1 /*******************************************************************************
\r
2 * (c) Copyright 2012 Microsemi SoC Products Group. All rights reserved.
\r
4 * SmartFusion2 COMBLK access functions.
\r
6 * SVN $Revision: 5615 $
\r
7 * SVN $Date: 2013-04-05 14:48:10 +0100 (Fri, 05 Apr 2013) $
\r
10 #include "mss_comblk.h"
\r
11 #include "../../CMSIS/mss_assert.h"
\r
13 /*==============================================================================
\r
16 /*------------------------------------------------------------------------------
\r
17 * Control register bit masks.
\r
19 #define CR_FLUSHOUT_MASK 0x01u
\r
20 #define CR_SIZETX_MASK 0x04u
\r
21 #define CR_ENABLE_MASK 0x10u
\r
22 #define CR_LOOPBACK_MASK 0x20u
\r
24 /*------------------------------------------------------------------------------
\r
25 * Status and interrupt enable registers bit masks.
\r
27 #define TXTOKAY_MASK 0x01u
\r
28 #define RCVOKAY_MASK 0x02u
\r
30 /*------------------------------------------------------------------------------
\r
31 * DATA8 register bit masks.
\r
33 #define DATA8_COMMAND_MASK 0x8000u
\r
35 /*------------------------------------------------------------------------------
\r
36 * COMBLK driver states.
\r
38 #define COMBLK_IDLE 0u
\r
39 #define COMBLK_TX_CMD 1u
\r
40 #define COMBLK_TX_DATA 2u
\r
41 #define COMBLK_WAIT_RESPONSE 3u
\r
42 #define COMBLK_RX_RESPONSE 4u
\r
43 #define COMBLK_TX_PAGED_DATA 5u
\r
45 /*==============================================================================
\r
46 * COMBLK interrupt servcie routine.
\r
48 void ComBlk_IRQHandler(void);
\r
50 /*==============================================================================
\r
53 static void abort_current_cmd(void);
\r
54 static void send_cmd_opcode(uint8_t opcode);
\r
55 static uint32_t fill_tx_fifo(const uint8_t * p_cmd, uint32_t cmd_size);
\r
56 static void handle_tx_okay_irq(void);
\r
57 static void handle_rx_okay_irq(void);
\r
58 static void complete_request(uint16_t response_length);
\r
59 static void process_sys_ctrl_command(uint8_t cmd_opcode);
\r
61 /*==============================================================================
\r
64 static volatile uint8_t g_comblk_cmd_opcode = 0u;
\r
65 static const uint8_t * g_comblk_p_cmd = 0u;
\r
66 static volatile uint16_t g_comblk_cmd_size = 0u;
\r
67 static const uint8_t * g_comblk_p_data = 0u;
\r
68 static volatile uint32_t g_comblk_data_size = 0u;
\r
69 static uint8_t * g_comblk_p_response = 0u;
\r
70 static uint16_t g_comblk_response_size = 0u;
\r
71 static volatile uint16_t g_comblk_response_idx = 0u;
\r
72 static comblk_completion_handler_t g_comblk_completion_handler = 0;
\r
74 /*typedef uint32_t (*comblk_page_handler_t)(uint8_t const ** pp_next_page);
\r
76 static uint32_t (*g_comblk_page_handler)(uint8_t const ** pp_next_page) = 0;
\r
78 static uint8_t g_request_in_progress = 0u;
\r
80 static uint8_t g_comblk_state = COMBLK_IDLE;
\r
82 static comblk_async_event_handler_t g_async_event_handler = 0;
\r
84 /*==============================================================================
\r
87 void MSS_COMBLK_init(comblk_async_event_handler_t async_event_handler)
\r
90 * Disable and clear previous interrupts.
\r
92 NVIC_DisableIRQ(ComBlk_IRQn);
\r
93 COMBLK->INT_ENABLE = 0u;
\r
94 NVIC_ClearPendingIRQ(ComBlk_IRQn);
\r
96 g_async_event_handler = async_event_handler;
\r
99 * Initialialize COMBLK driver state variables:
\r
101 g_request_in_progress = 0u;
\r
102 g_comblk_cmd_opcode = 0u;
\r
103 g_comblk_p_cmd = 0u;
\r
104 g_comblk_cmd_size = 0u;
\r
105 g_comblk_p_data = 0u;
\r
106 g_comblk_data_size = 0u;
\r
107 g_comblk_p_response = 0u;
\r
108 g_comblk_response_size = 0u;
\r
109 g_comblk_response_idx = 0u;
\r
110 g_comblk_completion_handler = 0;
\r
112 g_comblk_state = COMBLK_IDLE;
\r
114 COMBLK->CONTROL |= CR_ENABLE_MASK;
\r
115 COMBLK->CONTROL &= ~CR_LOOPBACK_MASK;
\r
117 /*--------------------------------------------------------------------------
\r
118 * Enable receive interrupt to receive asynchronous events from the system
\r
121 COMBLK->INT_ENABLE &= ~TXTOKAY_MASK;
\r
122 COMBLK->INT_ENABLE |= RCVOKAY_MASK;
\r
123 NVIC_EnableIRQ(ComBlk_IRQn);
\r
126 /*==============================================================================
\r
129 void MSS_COMBLK_send_cmd_with_ptr
\r
131 uint8_t cmd_opcode,
\r
132 uint32_t cmd_params_ptr,
\r
133 uint8_t * p_response,
\r
134 uint16_t response_size,
\r
135 comblk_completion_handler_t completion_handler
\r
140 /*--------------------------------------------------------------------------
\r
141 * Disable and clear previous interrupts.
\r
143 NVIC_DisableIRQ(ComBlk_IRQn);
\r
144 COMBLK->INT_ENABLE = 0u;
\r
145 NVIC_ClearPendingIRQ(ComBlk_IRQn);
\r
147 /*--------------------------------------------------------------------------
\r
148 * Abort current command if any.
\r
150 abort_current_cmd();
\r
152 /*--------------------------------------------------------------------------
\r
153 * Initialialize COMBLK driver state variables:
\r
155 g_request_in_progress = 1u;
\r
156 g_comblk_cmd_opcode = cmd_opcode;
\r
157 g_comblk_p_cmd = 0u;
\r
158 g_comblk_cmd_size = 0u;
\r
159 g_comblk_p_data = 0u;
\r
160 g_comblk_data_size = 0u;
\r
161 g_comblk_p_response = p_response;
\r
162 g_comblk_response_size = response_size;
\r
163 g_comblk_response_idx = 0u;
\r
164 g_comblk_page_handler = 0u;
\r
165 g_comblk_completion_handler = completion_handler;
\r
167 /*--------------------------------------------------------------------------
\r
168 * Send command opcode as a single byte write to the Tx FIFO.
\r
170 send_cmd_opcode(g_comblk_cmd_opcode);
\r
172 /*--------------------------------------------------------------------------
\r
173 * Send the command parameters pointer to the Tx FIFO as a single 4 bytes
\r
174 * write to the Tx FIFO.
\r
176 COMBLK->CONTROL |= CR_SIZETX_MASK;
\r
178 /* Wait for space to become available in Tx FIFO. */
\r
180 tx_okay = COMBLK->STATUS & TXTOKAY_MASK;
\r
181 } while(0u == tx_okay);
\r
183 /* Send command opcode. */
\r
184 COMBLK->DATA32 = cmd_params_ptr;
\r
186 COMBLK->CONTROL &= ~CR_SIZETX_MASK;
\r
188 g_comblk_state = COMBLK_WAIT_RESPONSE;
\r
190 /*--------------------------------------------------------------------------
\r
191 * Enable interrupt.
\r
193 COMBLK->INT_ENABLE |= RCVOKAY_MASK;
\r
194 NVIC_EnableIRQ(ComBlk_IRQn);
\r
197 /*==============================================================================
\r
200 void MSS_COMBLK_send_cmd
\r
202 const uint8_t * p_cmd,
\r
204 const uint8_t * p_data,
\r
205 uint32_t data_size,
\r
206 uint8_t * p_response,
\r
207 uint16_t response_size,
\r
208 comblk_completion_handler_t completion_handler
\r
211 uint32_t size_sent;
\r
213 ASSERT(cmd_size > 0);
\r
216 * Disable and clear previous interrupts.
\r
218 NVIC_DisableIRQ(ComBlk_IRQn);
\r
219 COMBLK->INT_ENABLE = 0u;
\r
220 NVIC_ClearPendingIRQ(ComBlk_IRQn);
\r
223 * Abort current command if any.
\r
225 abort_current_cmd();
\r
228 * Initialialize COMBLK driver state variables:
\r
230 g_request_in_progress = 1u;
\r
231 g_comblk_cmd_opcode = p_cmd[0];
\r
232 g_comblk_p_cmd = p_cmd;
\r
233 g_comblk_cmd_size = cmd_size;
\r
234 g_comblk_p_data = p_data;
\r
235 g_comblk_data_size = data_size;
\r
236 g_comblk_p_response = p_response;
\r
237 g_comblk_response_size = response_size;
\r
238 g_comblk_response_idx = 0u;
\r
239 g_comblk_page_handler = 0u;
\r
240 g_comblk_completion_handler = completion_handler;
\r
242 COMBLK->INT_ENABLE |= RCVOKAY_MASK;
\r
245 * Fill FIFO with command.
\r
247 send_cmd_opcode(g_comblk_cmd_opcode);
\r
248 size_sent = fill_tx_fifo(&p_cmd[1], cmd_size - 1u);
\r
249 ++size_sent; /* Adjust for opcode byte sent. */
\r
250 if(size_sent < cmd_size)
\r
252 g_comblk_cmd_size = g_comblk_cmd_size - (uint16_t)size_sent;
\r
253 g_comblk_p_cmd = &g_comblk_p_cmd[size_sent];
\r
255 g_comblk_state = COMBLK_TX_CMD;
\r
259 g_comblk_cmd_size = 0u;
\r
260 if(g_comblk_data_size > 0u)
\r
262 g_comblk_state = COMBLK_TX_DATA;
\r
266 g_comblk_state = COMBLK_WAIT_RESPONSE;
\r
271 * Enable interrupt.
\r
273 NVIC_EnableIRQ(ComBlk_IRQn);
\r
276 /*==============================================================================
\r
279 void MSS_COMBLK_send_paged_cmd
\r
281 const uint8_t * p_cmd,
\r
283 uint8_t * p_response,
\r
284 uint16_t response_size,
\r
285 uint32_t (*page_read_handler)(uint8_t const **),
\r
286 void (*completion_handler)(uint8_t *, uint16_t)
\r
289 uint32_t size_sent;
\r
290 uint8_t irq_enable = 0u;
\r
292 ASSERT(cmd_size > 0u);
\r
295 * Disable and clear previous interrupts.
\r
297 NVIC_DisableIRQ(ComBlk_IRQn);
\r
298 COMBLK->INT_ENABLE = 0u;
\r
299 NVIC_ClearPendingIRQ(ComBlk_IRQn);
\r
302 * Abort current command if any.
\r
304 abort_current_cmd();
\r
307 * Initialialize COMBLK driver state variables:
\r
309 g_request_in_progress = 1u;
\r
310 g_comblk_cmd_opcode = p_cmd[0];
\r
311 g_comblk_p_cmd = p_cmd;
\r
312 g_comblk_cmd_size = cmd_size;
\r
313 g_comblk_p_data = 0;
\r
314 g_comblk_data_size = 0u;
\r
315 g_comblk_p_response = p_response;
\r
316 g_comblk_response_size = response_size;
\r
317 g_comblk_response_idx = 0u;
\r
318 g_comblk_page_handler = page_read_handler;
\r
319 g_comblk_completion_handler = completion_handler;
\r
322 * Fill FIFO with command.
\r
324 send_cmd_opcode(g_comblk_cmd_opcode);
\r
325 size_sent = fill_tx_fifo(&p_cmd[1], cmd_size - 1u);
\r
326 ++size_sent; /* Adjust for opcode byte sent. */
\r
327 if(size_sent < cmd_size)
\r
329 g_comblk_cmd_size = g_comblk_cmd_size - (uint16_t)size_sent;
\r
330 g_comblk_p_cmd = &g_comblk_p_cmd[size_sent];
\r
332 g_comblk_state = COMBLK_TX_CMD;
\r
333 irq_enable = TXTOKAY_MASK | RCVOKAY_MASK;
\r
337 g_comblk_cmd_size = 0u;
\r
338 g_comblk_state = COMBLK_TX_PAGED_DATA;
\r
339 irq_enable = TXTOKAY_MASK | RCVOKAY_MASK;
\r
343 * Enable interrupt.
\r
345 COMBLK->INT_ENABLE |= irq_enable;
\r
346 NVIC_EnableIRQ(ComBlk_IRQn);
\r
349 /*==============================================================================
\r
350 * COMBLK interrupt handler.
\r
352 void ComBlk_IRQHandler(void)
\r
358 status = (uint8_t)COMBLK->STATUS;
\r
360 /* Mask off interrupt that are not enabled.*/
\r
361 status &= COMBLK->INT_ENABLE;
\r
363 rcv_okay = status & RCVOKAY_MASK;
\r
366 handle_rx_okay_irq();
\r
369 tx_okay = status & TXTOKAY_MASK;
\r
372 handle_tx_okay_irq();
\r
376 /*==============================================================================
\r
379 static void handle_tx_okay_irq(void)
\r
381 switch(g_comblk_state)
\r
383 /*----------------------------------------------------------------------
\r
384 * The TX_OKAY interrupt should only be enabled for states COMBLK_TX_CMD
\r
385 * and COMBLK_TX_DATA.
\r
387 case COMBLK_TX_CMD:
\r
388 if(g_comblk_cmd_size > 0u)
\r
390 uint32_t size_sent;
\r
391 size_sent = fill_tx_fifo(g_comblk_p_cmd, g_comblk_cmd_size);
\r
392 if(size_sent < g_comblk_cmd_size)
\r
394 g_comblk_cmd_size = g_comblk_cmd_size - (uint16_t)size_sent;
\r
395 g_comblk_p_cmd = &g_comblk_p_cmd[size_sent];
\r
399 g_comblk_cmd_size = 0u;
\r
400 if(g_comblk_data_size > 0u)
\r
402 g_comblk_state = COMBLK_TX_DATA;
\r
406 g_comblk_state = COMBLK_WAIT_RESPONSE;
\r
413 * This is an invalid situation indicating a bug in the driver
\r
414 * or corrupted memory.
\r
417 abort_current_cmd();
\r
421 case COMBLK_TX_DATA:
\r
422 if(g_comblk_data_size > 0u)
\r
424 uint32_t size_sent;
\r
425 size_sent = fill_tx_fifo(g_comblk_p_data, g_comblk_data_size);
\r
426 if(size_sent < g_comblk_data_size)
\r
428 g_comblk_data_size = g_comblk_data_size - size_sent;
\r
429 g_comblk_p_data = &g_comblk_p_data[size_sent];
\r
433 COMBLK->INT_ENABLE &= ~TXTOKAY_MASK;
\r
434 g_comblk_state = COMBLK_WAIT_RESPONSE;
\r
440 * This is an invalid situation indicating a bug in the driver
\r
441 * or corrupted memory.
\r
444 abort_current_cmd();
\r
448 case COMBLK_TX_PAGED_DATA:
\r
450 * Read a page of data if required.
\r
452 if(0u == g_comblk_data_size)
\r
454 if(g_comblk_page_handler != 0)
\r
456 g_comblk_data_size = g_comblk_page_handler(&g_comblk_p_data);
\r
457 if(0u == g_comblk_data_size)
\r
459 COMBLK->INT_ENABLE &= ~TXTOKAY_MASK;
\r
460 g_comblk_state = COMBLK_WAIT_RESPONSE;
\r
466 abort_current_cmd();
\r
471 * Transmit the page data or move to COMBLK_WAIT_RESPONSE state if
\r
472 * no further page data could be obtained by the call to the page
\r
475 if(0u == g_comblk_data_size)
\r
477 COMBLK->INT_ENABLE &= ~TXTOKAY_MASK;
\r
478 g_comblk_state = COMBLK_WAIT_RESPONSE;
\r
482 uint32_t size_sent;
\r
483 size_sent = fill_tx_fifo(g_comblk_p_data, g_comblk_data_size);
\r
484 g_comblk_data_size = g_comblk_data_size - size_sent;
\r
485 g_comblk_p_data = &g_comblk_p_data[size_sent];
\r
489 /*----------------------------------------------------------------------
\r
490 * The TX_OKAY interrupt should NOT be enabled for states COMBLK_IDLE,
\r
491 * COMBLK_WAIT_RESPONSE and COMBLK_RX_RESPONSE.
\r
495 case COMBLK_WAIT_RESPONSE:
\r
497 case COMBLK_RX_RESPONSE:
\r
500 COMBLK->INT_ENABLE &= ~TXTOKAY_MASK;
\r
501 complete_request(0u);
\r
502 g_comblk_state = COMBLK_IDLE;
\r
507 /*==============================================================================
\r
510 static void handle_rx_okay_irq(void)
\r
513 uint16_t is_command;
\r
515 data16 = (uint16_t)COMBLK->DATA8;
\r
516 is_command = data16 & DATA8_COMMAND_MASK;
\r
518 switch(g_comblk_state)
\r
520 /*----------------------------------------------------------------------
\r
521 * The RCV_OKAY interrupt should only be enabled for states
\r
522 * COMBLK_WAIT_RESPONSE and COMBLK_RX_RESPONSE.
\r
524 case COMBLK_WAIT_RESPONSE:
\r
527 uint8_t rxed_opcode;
\r
528 rxed_opcode = (uint8_t)data16;
\r
529 if(rxed_opcode == g_comblk_cmd_opcode)
\r
531 g_comblk_response_idx = 0u;
\r
532 g_comblk_p_response[g_comblk_response_idx] = rxed_opcode;
\r
533 ++g_comblk_response_idx;
\r
534 g_comblk_state = COMBLK_RX_RESPONSE;
\r
538 process_sys_ctrl_command(rxed_opcode);
\r
543 case COMBLK_RX_RESPONSE:
\r
546 uint8_t rxed_opcode;
\r
547 rxed_opcode = (uint8_t)data16;
\r
548 process_sys_ctrl_command(rxed_opcode);
\r
552 if(g_comblk_response_idx < g_comblk_response_size)
\r
556 rxed_data = (uint8_t)data16;
\r
557 g_comblk_p_response[g_comblk_response_idx] = rxed_data;
\r
558 ++g_comblk_response_idx;
\r
561 if(g_comblk_response_idx == g_comblk_response_size)
\r
563 complete_request(g_comblk_response_idx);
\r
564 g_comblk_state = COMBLK_IDLE;
\r
569 /*----------------------------------------------------------------------
\r
570 * The RCV_OKAY interrupt should NOT be enabled for states
\r
571 * COMBLK_IDLE, COMBLK_TX_CMD and COMBLK_TX_DATA.
\r
573 case COMBLK_TX_PAGED_DATA:
\r
574 /* This is needed because when there is an error, we need to terminate loading the data */
\r
577 g_comblk_p_response[1] = (uint8_t)data16;
\r
578 complete_request(2u);
\r
579 g_comblk_state = COMBLK_IDLE;
\r
584 case COMBLK_TX_CMD:
\r
586 case COMBLK_TX_DATA:
\r
590 uint8_t rxed_opcode;
\r
591 rxed_opcode = (uint8_t)data16;
\r
592 process_sys_ctrl_command(rxed_opcode);
\r
597 complete_request(0u);
\r
598 g_comblk_state = COMBLK_IDLE;
\r
603 /*==============================================================================
\r
606 static void complete_request
\r
608 uint16_t response_length
\r
611 if(g_comblk_completion_handler != 0)
\r
613 g_comblk_completion_handler(g_comblk_p_response, response_length);
\r
614 g_comblk_completion_handler = 0;
\r
615 g_request_in_progress = 0u;
\r
619 /*==============================================================================
\r
622 static void abort_current_cmd(void)
\r
624 if(g_request_in_progress)
\r
626 uint32_t flush_in_progress;
\r
629 * Call completion handler just in case we are in a multi threaded system
\r
630 * to avoid a task lockup.
\r
632 complete_request(g_comblk_response_idx);
\r
637 COMBLK->CONTROL |= CR_FLUSHOUT_MASK;
\r
639 flush_in_progress = COMBLK->CONTROL & CR_FLUSHOUT_MASK;
\r
640 } while(flush_in_progress);
\r
644 /*==============================================================================
\r
647 static void send_cmd_opcode
\r
654 /* Set transmit FIFO to transfer bytes. */
\r
655 COMBLK->CONTROL &= ~CR_SIZETX_MASK;
\r
657 /* Wait for space to become available in Tx FIFO. */
\r
659 tx_okay = COMBLK->STATUS & TXTOKAY_MASK;
\r
660 } while(0u == tx_okay);
\r
662 /* Send command opcode. */
\r
663 COMBLK->FRAME_START8 = opcode;
\r
666 /*==============================================================================
\r
669 static uint32_t fill_tx_fifo
\r
671 const uint8_t * p_cmd,
\r
675 volatile uint32_t tx_okay;
\r
676 uint32_t size_sent;
\r
678 /* Set transmit FIFO to transfer bytes. */
\r
679 COMBLK->CONTROL &= ~CR_SIZETX_MASK;
\r
682 tx_okay = COMBLK->STATUS & TXTOKAY_MASK;
\r
683 while((tx_okay != 0u) && (size_sent < cmd_size))
\r
685 COMBLK->DATA8 = p_cmd[size_sent];
\r
687 tx_okay = COMBLK->STATUS & TXTOKAY_MASK;
\r
693 /*==============================================================================
\r
696 static void process_sys_ctrl_command(uint8_t cmd_opcode)
\r
698 if(g_async_event_handler != 0)
\r
700 g_async_event_handler(cmd_opcode);
\r