1 /*******************************************************************************
\r
2 * (c) Copyright 2008 Actel Corporation. All rights reserved.
\r
4 * SmartFusion microcontroller subsystem SPI bare metal software driver
\r
7 * SVN $Revision: 2176 $
\r
8 * SVN $Date: 2010-02-15 21:04:22 +0000 (Mon, 15 Feb 2010) $
\r
10 #include "mss_spi.h"
\r
11 #include "../../CMSIS/mss_assert.h"
\r
17 /***************************************************************************//**
\r
18 MSS SPI can operate as master or slave.
\r
20 #define MSS_SPI_MODE_SLAVE (uint32_t)0
\r
21 #define MSS_SPI_MODE_MASTER (uint32_t)1
\r
23 /***************************************************************************//**
\r
24 * Mask of transfer protocol and SPO, SPH bits within control register.
\r
26 #define PROTOCOL_MODE_MASK (uint32_t)0x030000C0
\r
28 /***************************************************************************//**
\r
29 * Mask of theframe count bits within the SPI control register.
\r
31 #define TXRXDFCOUNT_MASK (uint32_t)0x00FFFF00
\r
32 #define TXRXDFCOUNT_SHIFT (uint32_t)8
\r
34 /***************************************************************************//**
\r
35 * SPI hardware FIFO depth.
\r
37 #define RX_FIFO_SIZE 4u
\r
39 /***************************************************************************//**
\r
40 Marker used to detect that the configuration has not been selected for a
\r
41 specific slave when operating as a master.
\r
43 #define NOT_CONFIGURED 0xFFFFFFFF
\r
45 /***************************************************************************//**
\r
46 * SPI instance data structures for SPI0 and SPI1. A pointer to these data
\r
47 * structures must be used as first parameter to any of the SPI driver functions
\r
48 * to identify the SPI hardware block that will perform the requested operation.
\r
50 mss_spi_instance_t g_mss_spi0;
\r
51 mss_spi_instance_t g_mss_spi1;
\r
53 /***************************************************************************//**
\r
54 SPI0 interrupt service routine
\r
56 #if defined(__GNUC__)
\r
57 __attribute__((__interrupt__)) void SPI0_IRQHandler( void );
\r
59 void SPI0_IRQHandler( void );
\r
62 /***************************************************************************//**
\r
63 SPI1 interrupt service routine
\r
65 #if defined(__GNUC__)
\r
66 __attribute__((__interrupt__)) void SPI1_IRQHandler( void );
\r
68 void SPI1_IRQHandler( void );
\r
71 /***************************************************************************//**
\r
73 * See "mss_spi.h" for details of how to use this function.
\r
77 mss_spi_instance_t * this_spi
\r
82 ASSERT( (this_spi == &g_mss_spi0) || (this_spi == &g_mss_spi1) );
\r
84 if (this_spi == &g_mss_spi0)
\r
86 this_spi->hw_reg = SPI0;
\r
87 this_spi->hw_reg_bit = SPI0_BITBAND;
\r
88 this_spi->irqn = SPI0_IRQn;
\r
91 SYSREG->SOFT_RST_CR |= SYSREG_SPI0_SOFTRESET_MASK;
\r
92 /* Clear any previously pended SPI0 interrupt */
\r
93 NVIC_ClearPendingIRQ( SPI0_IRQn );
\r
94 /* Take SPI0 out of reset. */
\r
95 SYSREG->SOFT_RST_CR &= ~SYSREG_SPI0_SOFTRESET_MASK;
\r
99 this_spi->hw_reg = SPI1;
\r
100 this_spi->hw_reg_bit = SPI1_BITBAND;
\r
101 this_spi->irqn = SPI1_IRQn;
\r
104 SYSREG->SOFT_RST_CR |= SYSREG_SPI1_SOFTRESET_MASK;
\r
105 /* Clear any previously pended SPI1 interrupt */
\r
106 NVIC_ClearPendingIRQ( SPI1_IRQn );
\r
107 /* Take SPI1 out of reset. */
\r
108 SYSREG->SOFT_RST_CR &= ~SYSREG_SPI1_SOFTRESET_MASK;
\r
111 this_spi->frame_rx_handler = 0U;
\r
112 this_spi->slave_tx_frame = 0U;
\r
114 this_spi->block_rx_handler = 0U;
\r
116 this_spi->slave_tx_buffer = 0U;
\r
117 this_spi->slave_tx_size = 0U;
\r
118 this_spi->slave_tx_idx = 0U;
\r
120 for ( i = 0u; i < (uint16_t)MSS_SPI_MAX_NB_OF_SLAVES; ++i )
\r
122 this_spi->slaves_cfg[i].ctrl_reg = NOT_CONFIGURED;
\r
126 /***************************************************************************//**
\r
127 * MSS_SPI_configure_slave_mode()
\r
128 * See "mss_spi.h" for details of how to use this function.
\r
130 void MSS_SPI_configure_slave_mode
\r
132 mss_spi_instance_t * this_spi,
\r
133 mss_spi_protocol_mode_t protocol_mode,
\r
134 mss_spi_pclk_div_t clk_rate,
\r
135 uint8_t frame_bit_length
\r
138 ASSERT( (this_spi == &g_mss_spi0) || (this_spi == &g_mss_spi1) );
\r
139 ASSERT( frame_bit_length <= 32 );
\r
141 /* Set the mode. */
\r
142 this_spi->hw_reg_bit->CTRL_MASTER = MSS_SPI_MODE_SLAVE;
\r
144 /* Set the clock rate. */
\r
145 this_spi->hw_reg_bit->CTRL_ENABLE = 0U;
\r
146 this_spi->hw_reg->CONTROL = (this_spi->hw_reg->CONTROL & ~PROTOCOL_MODE_MASK) | (uint32_t)protocol_mode;
\r
147 this_spi->hw_reg->CLK_GEN = (uint32_t)clk_rate;
\r
149 /* Set default frame size to byte size and number of data frames to 1. */
\r
150 this_spi->hw_reg->CONTROL = (this_spi->hw_reg->CONTROL & ~TXRXDFCOUNT_MASK) | ((uint32_t)1 << TXRXDFCOUNT_SHIFT);
\r
151 this_spi->hw_reg->TXRXDF_SIZE = frame_bit_length;
\r
152 this_spi->hw_reg_bit->CTRL_ENABLE = 1U;
\r
155 /***************************************************************************//**
\r
156 * MSS_SPI_configure_master_mode()
\r
157 * See "mss_spi.h" for details of how to use this function.
\r
159 void MSS_SPI_configure_master_mode
\r
161 mss_spi_instance_t * this_spi,
\r
162 mss_spi_slave_t slave,
\r
163 mss_spi_protocol_mode_t protocol_mode,
\r
164 mss_spi_pclk_div_t clk_rate,
\r
165 uint8_t frame_bit_length
\r
168 ASSERT( (this_spi == &g_mss_spi0) || (this_spi == &g_mss_spi1) );
\r
169 ASSERT( slave < MSS_SPI_MAX_NB_OF_SLAVES );
\r
170 ASSERT( frame_bit_length <= 32 );
\r
172 /* Set the mode. */
\r
173 this_spi->hw_reg_bit->CTRL_ENABLE = 0U;
\r
174 this_spi->hw_reg_bit->CTRL_MASTER = MSS_SPI_MODE_MASTER;
\r
175 this_spi->hw_reg_bit->CTRL_ENABLE = 1U;
\r
178 * Keep track of the required register configuration for this slave. These
\r
179 * values will be used by the MSS_SPI_set_slave_select() function to configure
\r
180 * the master to match the slave being selected.
\r
182 if ( slave < MSS_SPI_MAX_NB_OF_SLAVES )
\r
184 this_spi->slaves_cfg[slave].ctrl_reg = 0x00000002uL | (uint32_t)protocol_mode | ((uint32_t)1 << TXRXDFCOUNT_SHIFT);
\r
185 this_spi->slaves_cfg[slave].txrxdf_size_reg = frame_bit_length;
\r
186 this_spi->slaves_cfg[slave].clk_gen = (uint8_t)clk_rate;
\r
190 /***************************************************************************//**
\r
191 * MSS_SPI_set_slave_select()
\r
192 * See "mss_spi.h" for details of how to use this function.
\r
194 void MSS_SPI_set_slave_select
\r
196 mss_spi_instance_t * this_spi,
\r
197 mss_spi_slave_t slave
\r
200 ASSERT( (this_spi == &g_mss_spi0) || (this_spi == &g_mss_spi1) );
\r
202 /* This function is only intended to be used with an SPI master. */
\r
203 ASSERT( this_spi->hw_reg_bit->CTRL_MASTER == MSS_SPI_MODE_MASTER );
\r
204 ASSERT( this_spi->slaves_cfg[slave].ctrl_reg != NOT_CONFIGURED );
\r
206 /* Set the clock rate. */
\r
207 this_spi->hw_reg_bit->CTRL_ENABLE = 0U;
\r
208 this_spi->hw_reg->CONTROL = this_spi->slaves_cfg[slave].ctrl_reg;
\r
209 this_spi->hw_reg->CLK_GEN = this_spi->slaves_cfg[slave].clk_gen;
\r
210 this_spi->hw_reg->TXRXDF_SIZE = this_spi->slaves_cfg[slave].txrxdf_size_reg;
\r
211 this_spi->hw_reg_bit->CTRL_ENABLE = 1U;
\r
213 /* Set slave select */
\r
214 this_spi->hw_reg->SLAVE_SELECT |= ((uint32_t)1 << (uint32_t)slave);
\r
217 /***************************************************************************//**
\r
218 * MSS_SPI_clear_slave_select()
\r
219 * See "mss_spi.h" for details of how to use this function.
\r
221 void MSS_SPI_clear_slave_select
\r
223 mss_spi_instance_t * this_spi,
\r
224 mss_spi_slave_t slave
\r
227 ASSERT( (this_spi == &g_mss_spi0) || (this_spi == &g_mss_spi1) );
\r
229 /* This function is only intended to be used with an SPI master. */
\r
230 ASSERT( this_spi->hw_reg_bit->CTRL_MASTER == MSS_SPI_MODE_MASTER );
\r
232 this_spi->hw_reg->SLAVE_SELECT &= ~((uint32_t)1 << (uint32_t)slave);
\r
235 /***************************************************************************//**
\r
236 * MSS_SPI_transfer_frame()
\r
237 * See "mss_spi.h" for details of how to use this function.
\r
239 uint32_t MSS_SPI_transfer_frame
\r
241 mss_spi_instance_t * this_spi,
\r
245 volatile uint32_t dummy;
\r
247 ASSERT( (this_spi == &g_mss_spi0) || (this_spi == &g_mss_spi1) );
\r
249 /* This function is only intended to be used with an SPI master. */
\r
250 ASSERT( this_spi->hw_reg_bit->CTRL_MASTER == MSS_SPI_MODE_MASTER );
\r
252 /* Flush Rx FIFO. */
\r
253 while ( this_spi->hw_reg_bit->STATUS_RX_RDY == 1U )
\r
255 dummy = this_spi->hw_reg->RX_DATA;
\r
256 dummy = dummy; /* Prevent Lint warning. */
\r
260 this_spi->hw_reg->TX_DATA = tx_bits;
\r
262 /* Wait for frame Tx to complete. */
\r
263 while ( this_spi->hw_reg_bit->STATUS_TX_DONE == 0U )
\r
268 /* Read received frame. */
\r
269 /* Wait for Rx complete. */
\r
270 while ( this_spi->hw_reg_bit->STATUS_RX_RDY == 0U )
\r
274 /* Return Rx data. */
\r
275 return( this_spi->hw_reg->RX_DATA );
\r
279 /***************************************************************************//**
\r
280 * MSS_SPI_transfer_block()
\r
281 * See "mss_spi.h" for details of how to use this function.
\r
283 void MSS_SPI_transfer_block
\r
285 mss_spi_instance_t * this_spi,
\r
286 const uint8_t * cmd_buffer,
\r
287 uint16_t cmd_byte_size,
\r
288 uint8_t * rd_buffer,
\r
289 uint16_t rd_byte_size
\r
292 uint16_t transfer_idx = 0U;
\r
295 uint32_t frame_count;
\r
296 volatile uint32_t rx_raw;
\r
297 uint16_t transit = 0U;
\r
299 uint16_t transfer_size; /* Total number of bytes transfered. */
\r
301 ASSERT( (this_spi == &g_mss_spi0) || (this_spi == &g_mss_spi1) );
\r
303 /* This function is only intended to be used with an SPI master. */
\r
304 ASSERT( this_spi->hw_reg_bit->CTRL_MASTER == MSS_SPI_MODE_MASTER );
\r
306 /* Compute number of bytes to transfer. */
\r
307 transfer_size = cmd_byte_size + rd_byte_size;
\r
309 /* Adjust to 1 byte transfer to cater for DMA transfers. */
\r
310 if ( transfer_size == 0U )
\r
316 frame_count = transfer_size;
\r
319 /* Set frame size to 8 bits and the frame count to the tansfer size. */
\r
320 this_spi->hw_reg_bit->CTRL_ENABLE = 0U;
\r
321 this_spi->hw_reg->CONTROL = (this_spi->hw_reg->CONTROL & ~TXRXDFCOUNT_MASK) | ( (frame_count << TXRXDFCOUNT_SHIFT) & TXRXDFCOUNT_MASK);
\r
322 this_spi->hw_reg->TXRXDF_SIZE = 8U;
\r
323 this_spi->hw_reg_bit->CTRL_ENABLE = 1U;
\r
325 /* Flush the receive FIFO. */
\r
326 while ( !this_spi->hw_reg_bit->STATUS_RX_FIFO_EMPTY )
\r
328 rx_raw = this_spi->hw_reg->RX_DATA;
\r
333 if ( tx_idx < cmd_byte_size )
\r
335 this_spi->hw_reg->TX_DATA = cmd_buffer[tx_idx];
\r
341 if ( tx_idx < transfer_size )
\r
343 this_spi->hw_reg->TX_DATA = 0x00U;
\r
348 /* Perform the remainder of the transfer by sending a byte every time a byte
\r
349 * has been received. This should ensure that no Rx overflow can happen in
\r
350 * case of an interrupt occurs during this function. */
\r
351 while ( transfer_idx < transfer_size )
\r
353 if ( !this_spi->hw_reg_bit->STATUS_RX_FIFO_EMPTY )
\r
355 /* Process received byte. */
\r
356 rx_raw = this_spi->hw_reg->RX_DATA;
\r
357 if ( transfer_idx >= cmd_byte_size )
\r
359 if ( rx_idx < rd_byte_size )
\r
361 rd_buffer[rx_idx] = (uint8_t)rx_raw;
\r
369 if ( !this_spi->hw_reg_bit->STATUS_TX_FIFO_FULL )
\r
371 if (transit < RX_FIFO_SIZE)
\r
373 /* Send another byte. */
\r
374 if ( tx_idx < cmd_byte_size )
\r
376 this_spi->hw_reg->TX_DATA = cmd_buffer[tx_idx];
\r
382 if ( tx_idx < transfer_size )
\r
384 this_spi->hw_reg->TX_DATA = 0x00U;
\r
394 /***************************************************************************//**
\r
395 * MSS_SPI_set_frame_rx_handler()
\r
396 * See "mss_spi.h" for details of how to use this function.
\r
398 void MSS_SPI_set_frame_rx_handler
\r
400 mss_spi_instance_t * this_spi,
\r
401 mss_spi_frame_rx_handler_t rx_handler
\r
404 ASSERT( (this_spi == &g_mss_spi0) || (this_spi == &g_mss_spi1) );
\r
406 /* This function is only intended to be used with an SPI slave. */
\r
407 ASSERT( this_spi->hw_reg_bit->CTRL_MASTER == MSS_SPI_MODE_SLAVE );
\r
409 /* Disable block Rx handler as they are mutually exclusive. */
\r
410 this_spi->block_rx_handler = 0U;
\r
412 /* Keep a copy of the pointer to the rx hnadler function. */
\r
413 this_spi->frame_rx_handler = rx_handler;
\r
415 /* Enable Rx interrupt. */
\r
416 this_spi->hw_reg_bit->CTRL_RX_INT_EN = 1U;
\r
417 NVIC_EnableIRQ( this_spi->irqn );
\r
420 /***************************************************************************//**
\r
421 * MSS_SPI_set_slave_tx_frame()
\r
422 * See "mss_spi.h" for details of how to use this function.
\r
424 void MSS_SPI_set_slave_tx_frame
\r
426 mss_spi_instance_t * this_spi,
\r
427 uint32_t frame_value
\r
430 ASSERT( (this_spi == &g_mss_spi0) || (this_spi == &g_mss_spi1) );
\r
432 /* This function is only intended to be used with an SPI slave. */
\r
433 ASSERT( this_spi->hw_reg_bit->CTRL_MASTER == MSS_SPI_MODE_SLAVE );
\r
435 /* Disable slave block tx buffer as it is mutually exclusive with frame
\r
436 * level handling. */
\r
437 this_spi->slave_tx_buffer = 0U;
\r
438 this_spi->slave_tx_size = 0U;
\r
439 this_spi->slave_tx_idx = 0U;
\r
441 /* Keep a copy of the slave tx frame value. */
\r
442 this_spi->slave_tx_frame = frame_value;
\r
444 /* Load frame into Tx data register. */
\r
445 this_spi->hw_reg->TX_DATA = this_spi->slave_tx_frame;
\r
447 /* Enable Tx Done interrupt in order to reload the slave Tx frame after each
\r
448 * time it has been sent. */
\r
449 this_spi->hw_reg_bit->CTRL_TX_INT_EN = 1U;
\r
450 NVIC_EnableIRQ( this_spi->irqn );
\r
453 /***************************************************************************//**
\r
454 * MSS_SPI_set_slave_block_buffers()
\r
455 * See "mss_spi.h" for details of how to use this function.
\r
457 void MSS_SPI_set_slave_block_buffers
\r
459 mss_spi_instance_t * this_spi,
\r
460 const uint8_t * tx_buffer,
\r
461 uint32_t tx_buff_size,
\r
462 uint8_t * rx_buffer,
\r
463 uint32_t rx_buff_size,
\r
464 mss_spi_block_rx_handler_t block_rx_handler
\r
467 uint32_t frame_count;
\r
469 ASSERT( (this_spi == &g_mss_spi0) || (this_spi == &g_mss_spi1) );
\r
471 /* This function is only intended to be used with an SPI slave. */
\r
472 ASSERT( this_spi->hw_reg_bit->CTRL_MASTER == MSS_SPI_MODE_SLAVE );
\r
474 /* Disable Rx frame handler as it is mutually exclusive with block rx handler. */
\r
475 this_spi->frame_rx_handler = 0U;
\r
477 /* Keep a copy of the pointer to the block rx handler function. */
\r
478 this_spi->block_rx_handler = block_rx_handler;
\r
480 this_spi->slave_rx_buffer = rx_buffer;
\r
481 this_spi->slave_rx_size = rx_buff_size;
\r
482 this_spi->slave_rx_idx = 0U;
\r
485 this_spi->slave_tx_buffer = tx_buffer;
\r
486 this_spi->slave_tx_size = tx_buff_size;
\r
487 this_spi->slave_tx_idx = 0U;
\r
489 frame_count = rx_buff_size;
\r
492 this_spi->hw_reg_bit->CTRL_ENABLE = 0U;
\r
493 this_spi->hw_reg->CONTROL = (this_spi->hw_reg->CONTROL & ~TXRXDFCOUNT_MASK) | (frame_count << TXRXDFCOUNT_SHIFT);
\r
494 this_spi->hw_reg->TXRXDF_SIZE = 8U;
\r
495 this_spi->hw_reg_bit->CTRL_ENABLE = 1U;
\r
497 /* Load the transmit FIFO. */
\r
498 while ( !(this_spi->hw_reg_bit->STATUS_TX_FIFO_FULL) && ( this_spi->slave_tx_idx < this_spi->slave_tx_size ) )
\r
500 this_spi->hw_reg->TX_DATA = this_spi->slave_tx_buffer[this_spi->slave_tx_idx];
\r
501 ++this_spi->slave_tx_idx;
\r
504 /* Enable Rx interrupt. */
\r
505 this_spi->hw_reg_bit->CTRL_RX_INT_EN = 1U;
\r
506 NVIC_EnableIRQ( this_spi->irqn );
\r
509 /***************************************************************************//**
\r
510 * SPI interrupt service routine.
\r
512 static void mss_spi_isr
\r
514 mss_spi_instance_t * this_spi
\r
519 ASSERT( (this_spi == &g_mss_spi0) || (this_spi == &g_mss_spi1) );
\r
521 if ( this_spi->hw_reg_bit->MIS_RX_RDY )
\r
523 while( !this_spi->hw_reg_bit->STATUS_RX_FIFO_EMPTY )
\r
525 rx_frame = this_spi->hw_reg->RX_DATA;
\r
526 if ( this_spi->frame_rx_handler != 0U )
\r
528 /* Single frame handling mode. */
\r
529 this_spi->frame_rx_handler( rx_frame );
\r
533 if ( this_spi->block_rx_handler != 0U )
\r
535 /* Block handling mode. */
\r
536 if ( this_spi->slave_rx_idx < this_spi->slave_rx_size )
\r
538 this_spi->slave_rx_buffer[this_spi->slave_rx_idx] = (uint8_t)rx_frame;
\r
539 ++this_spi->slave_rx_idx;
\r
540 if ( this_spi->slave_rx_idx == this_spi->slave_rx_size )
\r
542 (*this_spi->block_rx_handler)( this_spi->slave_rx_buffer, this_spi->slave_rx_size );
\r
548 /* Feed transmit FIFO. */
\r
549 if ( !(this_spi->hw_reg_bit->STATUS_TX_FIFO_FULL) && ( this_spi->slave_tx_idx < this_spi->slave_tx_size ) )
\r
551 this_spi->hw_reg->TX_DATA = this_spi->slave_tx_buffer[this_spi->slave_tx_idx];
\r
552 ++this_spi->slave_tx_idx;
\r
555 this_spi->hw_reg_bit->INT_CLEAR_RX_RDY = 1U;
\r
558 if ( this_spi->hw_reg_bit->MIS_TX_DONE )
\r
560 if ( this_spi->slave_tx_buffer != 0U )
\r
562 this_spi->hw_reg->TX_DATA = this_spi->slave_tx_buffer[this_spi->slave_tx_idx];
\r
563 ++this_spi->slave_tx_idx;
\r
564 if ( this_spi->slave_tx_idx >= this_spi->slave_tx_size )
\r
566 this_spi->slave_tx_idx = 0U;
\r
571 /* Reload slave tx frame into Tx data register. */
\r
572 this_spi->hw_reg->TX_DATA = this_spi->slave_tx_frame;
\r
577 /***************************************************************************//**
\r
578 * SPIO interrupt service routine.
\r
579 * Please note that the name of this ISR is defined as part of the SmartFusion
\r
580 * CMSIS startup code.
\r
582 #if defined(__GNUC__)
\r
583 __attribute__((__interrupt__)) void SPI0_IRQHandler( void )
\r
585 void SPI0_IRQHandler( void )
\r
588 mss_spi_isr( &g_mss_spi0 );
\r
589 NVIC_ClearPendingIRQ( SPI0_IRQn );
\r
592 /***************************************************************************//**
\r
593 * SPI1 interrupt service routine.
\r
594 * Please note that the name of this ISR is defined as part of the SmartFusion
\r
595 * CMSIS startup code.
\r
597 #if defined(__GNUC__)
\r
598 __attribute__((__interrupt__)) void SPI1_IRQHandler( void )
\r
600 void SPI1_IRQHandler( void )
\r
603 mss_spi_isr( &g_mss_spi1 );
\r
604 NVIC_ClearPendingIRQ( SPI1_IRQn );
\r