1 /* ----------------------------------------------------------------------------
2  *         SAM Software Package License
3  * ----------------------------------------------------------------------------
4  * Copyright (c) 2011-2012, Atmel Corporation
5  *
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following condition is met:
10  *
11  * - Redistributions of source code must retain the above copyright notice,
12  * this list of conditions and the disclaimer below.
13  *
14  * Atmel's name may not be used to endorse or promote products derived from
15  * this software without specific prior written permission.
16  *
17  * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
20  * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
23  * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
26  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  * ----------------------------------------------------------------------------
28  */
29 
30 /** \addtogroup spi_module Working with SPI
31  * The SPI driver provides the interface to configure and use the SPI
32  * peripheral.
33  *
34  * The Serial Peripheral Interface (SPI) circuit is a synchronous serial
35  * data link that provides communication with external devices in Master
36  * or Slave Mode.
37  *
38  * To use the SPI, the user has to follow these few steps:
39  * -# Enable the SPI pins required by the application (see pio.h).
40  * -# Configure the SPI using the \ref SPI_Configure(). This enables the
41  *    peripheral clock. The mode register is loaded with the given value.
42  * -# Configure all the necessary chip selects with \ref SPI_ConfigureNPCS().
43  * -# Enable the SPI by calling \ref SPI_Enable().
44  * -# Send/receive data using \ref SPI_Write() and \ref SPI_Read(). Note that \ref SPI_Read()
45  *    must be called after \ref SPI_Write() to retrieve the last value read.
46  * -# Send/receive data using the PDC with the \ref SPI_WriteBuffer() and
47  *    \ref SPI_ReadBuffer() functions.
48  * -# Disable the SPI by calling \ref SPI_Disable().
49  *
50  * For more accurate information, please look at the SPI section of the
51  * Datasheet.
52  *
53  * Related files :\n
54  * \ref spi.c\n
55  * \ref spi.h.\n
56 */
57 /*@{*/
58 /*@}*/
59 
60 /**
61  * \file
62  *
63  * Implementation of Serial Peripheral Interface (SPI) controller.
64  *
65  */
66 
67 /*----------------------------------------------------------------------------
68  *        Headers
69  *----------------------------------------------------------------------------*/
70 
71 #include "chip.h"
72 
73 #include <stdint.h>
74 
75 /*----------------------------------------------------------------------------
76  *        Exported functions
77  *----------------------------------------------------------------------------*/
78 
79 /**
80  * \brief Enables a SPI peripheral.
81  *
82  * \param spi  Pointer to an Spi instance.
83  */
SPI_Enable(Spi * spi)84 extern void SPI_Enable( Spi* spi )
85 {
86     spi->SPI_CR = SPI_CR_SPIEN ;
87 }
88 
89 /**
90  * \brief Disables a SPI peripheral.
91  *
92  * \param spi  Pointer to an Spi instance.
93  */
SPI_Disable(Spi * spi)94 extern void SPI_Disable( Spi* spi )
95 {
96     spi->SPI_CR = SPI_CR_SPIDIS ;
97 }
98 
99 /**
100  * \brief Enables one or more interrupt sources of a SPI peripheral.
101  *
102  * \param spi  Pointer to an Spi instance.
103  * \param sources Bitwise OR of selected interrupt sources.
104  */
SPI_EnableIt(Spi * spi,uint32_t dwSources)105 extern void SPI_EnableIt( Spi* spi, uint32_t dwSources )
106 {
107     spi->SPI_IER = dwSources ;
108 }
109 
110 /**
111  * \brief Disables one or more interrupt sources of a SPI peripheral.
112  *
113  * \param spi  Pointer to an Spi instance.
114  * \param sources Bitwise OR of selected interrupt sources.
115  */
SPI_DisableIt(Spi * spi,uint32_t dwSources)116 extern void SPI_DisableIt( Spi* spi, uint32_t dwSources )
117 {
118     spi->SPI_IDR = dwSources ;
119 }
120 
121 /**
122  * \brief Configures a SPI peripheral as specified. The configuration can be computed
123  * using several macros (see \ref spi_configuration_macros).
124  *
125  * \param spi  Pointer to an Spi instance.
126  * \param id   Peripheral ID of the SPI.
127  * \param configuration  Value of the SPI configuration register.
128  */
SPI_Configure(Spi * spi,uint32_t dwId,uint32_t dwConfiguration)129 extern void SPI_Configure( Spi* spi, uint32_t dwId, uint32_t dwConfiguration )
130 {
131     pmc_enable_periph_clk( dwId ) ;
132     spi->SPI_CR = SPI_CR_SPIDIS ;
133 
134     /* Execute a software reset of the SPI twice */
135     spi->SPI_CR = SPI_CR_SWRST ;
136     spi->SPI_CR = SPI_CR_SWRST ;
137     spi->SPI_MR = dwConfiguration ;
138 }
139 
140 
141 /**
142  * \brief Configures a chip select of a SPI peripheral. The chip select configuration
143  * is computed using several macros (see \ref spi_configuration_macros).
144  *
145  * \param spi   Pointer to an Spi instance.
146  * \param npcs  Chip select to configure (0, 1, 2 or 3).
147  * \param configuration  Desired chip select configuration.
148  */
SPI_ConfigureNPCS(Spi * spi,uint32_t dwNpcs,uint32_t dwConfiguration)149 void SPI_ConfigureNPCS( Spi* spi, uint32_t dwNpcs, uint32_t dwConfiguration )
150 {
151     spi->SPI_CSR[dwNpcs] = dwConfiguration ;
152 }
153 
154 /**
155  * \brief Get the current status register of the given SPI peripheral.
156  * \note This resets the internal value of the status register, so further
157  * read may yield different values.
158  * \param spi   Pointer to a Spi instance.
159  * \return  SPI status register.
160  */
SPI_GetStatus(Spi * spi)161 extern uint32_t SPI_GetStatus( Spi* spi )
162 {
163     return spi->SPI_SR ;
164 }
165 
166 /**
167  * \brief Reads and returns the last word of data received by a SPI peripheral. This
168  * method must be called after a successful SPI_Write call.
169  *
170  * \param spi  Pointer to an Spi instance.
171  *
172  * \return readed data.
173  */
SPI_Read(Spi * spi)174 extern uint32_t SPI_Read( Spi* spi )
175 {
176     while ( (spi->SPI_SR & SPI_SR_RDRF) == 0 ) ;
177 
178     return spi->SPI_RDR & 0xFFFF ;
179 }
180 
181 /**
182  * \brief Sends data through a SPI peripheral. If the SPI is configured to use a fixed
183  * peripheral select, the npcs value is meaningless. Otherwise, it identifies
184  * the component which shall be addressed.
185  *
186  * \param spi   Pointer to an Spi instance.
187  * \param npcs  Chip select of the component to address (0, 1, 2 or 3).
188  * \param data  Word of data to send.
189  */
SPI_Write(Spi * spi,uint32_t dwNpcs,uint16_t wData)190 extern void SPI_Write( Spi* spi, uint32_t dwNpcs, uint16_t wData )
191 {
192     /* Send data */
193     while ( (spi->SPI_SR & SPI_SR_TXEMPTY) == 0 ) ;
194     spi->SPI_TDR = wData | SPI_PCS( dwNpcs ) ;
195     while ( (spi->SPI_SR & SPI_SR_TDRE) == 0 ) ;
196 }
197 
198 /**
199  * \brief Check if SPI transfer finish.
200  *
201  * \param spi  Pointer to an Spi instance.
202  *
203  * \return Returns 1 if there is no pending write operation on the SPI; otherwise
204  * returns 0.
205  */
SPI_IsFinished(Spi * spi)206 extern uint32_t SPI_IsFinished( Spi* spi )
207 {
208     return ((spi->SPI_SR & SPI_SR_TXEMPTY) != 0) ;
209 }
210 
211 #if (defined _SAM3S_) || (defined _SAM3S8_) || (defined _SAM3N_)
212 /**
213  * \brief Enable Spi PDC transmit
214  * \param spi  Pointer to an Spi instance.
215 */
SPI_PdcEnableTx(Spi * spi)216 extern void SPI_PdcEnableTx( Spi* spi )
217 {
218     spi->SPI_PTCR = SPI_PTCR_TXTEN ;
219 }
220 
221 /**
222  * \brief Disable Spi PDC transmit
223  * \param spi  Pointer to an Spi instance.
224 */
SPI_PdcDisableTx(Spi * spi)225 extern void SPI_PdcDisableTx( Spi* spi )
226 {
227     spi->SPI_PTCR = SPI_PTCR_TXTDIS ;
228 }
229 
230 /**
231  * \brief Enable Spi PDC receive
232  * \param spi  Pointer to an Spi instance.
233 */
SPI_PdcEnableRx(Spi * spi)234 extern void SPI_PdcEnableRx( Spi* spi )
235 {
236     spi->SPI_PTCR = SPI_PTCR_RXTEN ;
237 }
238 
239 /**
240  * \brief Disable Spi PDC receive
241  * \param spi  Pointer to an Spi instance.
242 */
SPI_PdcDisableRx(Spi * spi)243 extern void SPI_PdcDisableRx( Spi* spi )
244 {
245     spi->SPI_PTCR = SPI_PTCR_RXTDIS ;
246 }
247 
248 /**
249  * \brief Set PDC transmit and next transmit buffer address and size.
250  *
251  * \param spi    Pointer to an Spi instance.
252  * \param txBuf  PDC transmit buffer address.
253  * \param txCount  Length in bytes of the transmit buffer.
254  * \param txNextBuf  PDC next transmit buffer address.
255  * \param txNextCount  Length in bytes of the next transmit buffer.
256  */
SPI_PdcSetTx(Spi * spi,void * pvTxBuf,uint32_t dwTxCount,void * pvTxNextBuf,uint32_t dwTxNextCount)257 extern void SPI_PdcSetTx( Spi* spi, void* pvTxBuf, uint32_t dwTxCount, void* pvTxNextBuf, uint32_t dwTxNextCount )
258 {
259     spi->SPI_TPR = (uint32_t)pvTxBuf ;
260     spi->SPI_TCR = dwTxCount ;
261     spi->SPI_TNPR = (uint32_t)pvTxNextBuf ;
262     spi->SPI_TNCR = dwTxNextCount ;
263 }
264 
265 /**
266  * \brief Set PDC receive and next receive buffer address and size.
267  *
268  * \param spi    Pointer to an Spi instance.
269  * \param rxBuf  PDC receive buffer address.
270  * \param rxCount  Length in bytes of the receive buffer.
271  * \param rxNextBuf  PDC next receive buffer address.
272  * \param rxNextCount  Length in bytes of the next receive buffer.
273  */
SPI_PdcSetRx(Spi * spi,void * pvRxBuf,uint32_t dwRxCount,void * pvRxNextBuf,uint32_t dwRxNextCount)274 extern void SPI_PdcSetRx( Spi* spi, void* pvRxBuf, uint32_t dwRxCount, void* pvRxNextBuf, uint32_t dwRxNextCount )
275 {
276     spi->SPI_RPR = (uint32_t)pvRxBuf ;
277     spi->SPI_RCR = dwRxCount ;
278     spi->SPI_RNPR = (uint32_t)pvRxNextBuf ;
279     spi->SPI_RNCR = dwRxNextCount ;
280 }
281 
282 /**
283  * \brief Sends the contents of buffer through a SPI peripheral, using the PDC to
284  * take care of the transfer.
285  *
286  * \param spi     Pointer to an Spi instance.
287  * \param buffer  Data buffer to send.
288  * \param length  Length of the data buffer.
289  */
SPI_WriteBuffer(Spi * spi,void * pvBuffer,uint32_t dwLength)290 extern uint32_t SPI_WriteBuffer( Spi* spi, void* pvBuffer, uint32_t dwLength )
291 {
292     /* Check if first bank is free */
293     if ( spi->SPI_TCR == 0 )
294     {
295         spi->SPI_TPR = (uint32_t)pvBuffer ;
296         spi->SPI_TCR = dwLength ;
297         spi->SPI_PTCR = PERIPH_PTCR_TXTEN ;
298 
299         return 1 ;
300     }
301     /* Check if second bank is free */
302     else
303     {
304         if ( spi->SPI_TNCR == 0 )
305         {
306             spi->SPI_TNPR = (uint32_t)pvBuffer ;
307             spi->SPI_TNCR = dwLength ;
308 
309             return 1 ;
310         }
311     }
312 
313     /* No free banks */
314     return 0 ;
315 }
316 
317 /**
318  * \brief Reads data from a SPI peripheral until the provided buffer is filled. This
319  * method does NOT need to be called after SPI_Write or SPI_WriteBuffer.
320  *
321  * \param spi     Pointer to an Spi instance.
322  * \param buffer  Data buffer to store incoming bytes.
323  * \param length  Length in bytes of the data buffer.
324  */
SPI_ReadBuffer(Spi * spi,void * pvBuffer,uint32_t dwLength)325 extern uint32_t SPI_ReadBuffer( Spi* spi, void *pvBuffer, uint32_t dwLength )
326 {
327     /* Check if the first bank is free */
328     if ( spi->SPI_RCR == 0 )
329     {
330         spi->SPI_RPR = (uint32_t)pvBuffer ;
331         spi->SPI_RCR = dwLength ;
332         spi->SPI_PTCR = PERIPH_PTCR_RXTEN ;
333 
334         return 1 ;
335     }
336     /* Check if second bank is free */
337     else
338     {
339         if ( spi->SPI_RNCR == 0 )
340         {
341             spi->SPI_RNPR = (uint32_t)pvBuffer ;
342             spi->SPI_RNCR = dwLength ;
343             return 1 ;
344         }
345     }
346 
347     /* No free bank */
348     return 0 ;
349 }
350 
351 #endif /* (defined _SAM3S_) || (defined _SAM3S8_) || (defined _SAM3N_) */
352 
353