]> git.sur5r.net Git - freertos/blob - FreeRTOS/Demo/CORTEX_M7_SAMV71_Xplained/libchip_samv7/source/spi_dma.c
Update version number ready for V8.2.1 release.
[freertos] / FreeRTOS / Demo / CORTEX_M7_SAMV71_Xplained / libchip_samv7 / source / spi_dma.c
1 /* ----------------------------------------------------------------------------\r
2  *         SAM Software Package License \r
3  * ----------------------------------------------------------------------------\r
4  * Copyright (c) 2013, Atmel Corporation\r
5  *\r
6  * All rights reserved.\r
7  *\r
8  * Redistribution and use in source and binary forms, with or without\r
9  * modification, are permitted provided that the following conditions are met:\r
10  *\r
11  * - Redistributions of source code must retain the above copyright notice,\r
12  * this list of conditions and the disclaimer below.\r
13  *\r
14  * Atmel's name may not be used to endorse or promote products derived from\r
15  * this software without specific prior written permission.\r
16  *\r
17  * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR\r
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\r
19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE\r
20  * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT,\r
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\r
22  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,\r
23  * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\r
24  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\r
25  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,\r
26  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
27  * ----------------------------------------------------------------------------\r
28  */\r
29 \r
30  /**\r
31  * \addtogroup spi_dma_module SPI xDMA driver\r
32  * \ingroup lib_spiflash\r
33  * \section Usage\r
34  *\r
35  * <ul>\r
36  * <li> SPID_Configure() initializes and configures the SPI peripheral and xDMA for data transfer.</li>\r
37  * <li> Configures the parameters for the device corresponding to the cs value by SPID_ConfigureCS(). </li>\r
38  * <li> Starts a SPI master transfer. This is a non blocking function SPID_SendCommand(). It will\r
39  * return as soon as the transfer is started..</li>\r
40  * </ul>\r
41  *\r
42  */\r
43 \r
44 /**\r
45  * \file\r
46  *\r
47  * Implementation for the SPI Flash with xDMA driver.\r
48  *\r
49  */\r
50 \r
51  \r
52 /*----------------------------------------------------------------------------\r
53  *        Headers\r
54  *----------------------------------------------------------------------------*/\r
55 \r
56 #include "chip.h"\r
57 \r
58 /*----------------------------------------------------------------------------\r
59  *        Definitions\r
60  *----------------------------------------------------------------------------*/\r
61 \r
62 /** xDMA support */\r
63 #define USE_SPI_DMA\r
64 \r
65 /** xDMA Link List size for spi transation*/\r
66 #define DMA_SPI_LLI     2\r
67 \r
68 /*----------------------------------------------------------------------------\r
69  *        Macros\r
70  *----------------------------------------------------------------------------*/\r
71 \r
72  /*----------------------------------------------------------------------------\r
73  *        Local Variables\r
74  *----------------------------------------------------------------------------*/\r
75 \r
76 \r
77 /*  DMA driver instance */\r
78 static uint32_t spiDmaTxChannel;\r
79 static uint32_t spiDmaRxChannel;\r
80 \r
81 /*----------------------------------------------------------------------------\r
82  *        Local functions\r
83  *----------------------------------------------------------------------------*/\r
84 \r
85 /**\r
86  * \brief SPI xDMA Rx callback\r
87  * Invoked on SPi DMA reception done.\r
88  * \param channel DMA channel.\r
89  * \param pArg Pointer to callback argument - Pointer to Spid instance.   \r
90  */ \r
91 static void SPID_Rx_Cb(uint32_t channel, Spid* pArg)\r
92 {\r
93     SpidCmd *pSpidCmd = pArg->pCurrentCommand;\r
94     Spi *pSpiHw = pArg->pSpiHw;\r
95     if (channel != spiDmaRxChannel)\r
96         return;\r
97     \r
98     /* Disable the SPI TX & RX */\r
99     SPI_Disable ( pSpiHw );\r
100  \r
101     /* Configure and enable interrupt on RC compare */    \r
102     NVIC_ClearPendingIRQ(XDMAC_IRQn);\r
103     NVIC_DisableIRQ(XDMAC_IRQn);\r
104     \r
105     /* Disable the SPI Peripheral */\r
106     PMC_DisablePeripheral ( pArg->spiId );\r
107     \r
108     /* Release CS */\r
109     SPI_ReleaseCS(pSpiHw);\r
110     \r
111     /* Release the DMA channels */\r
112     XDMAD_FreeChannel(pArg->pXdmad, spiDmaRxChannel);\r
113     XDMAD_FreeChannel(pArg->pXdmad, spiDmaTxChannel);\r
114 \r
115     /* Release the dataflash semaphore */\r
116     pArg->semaphore++;\r
117         \r
118     printf("\n\r%s\n\r",pArg->pCurrentCommand->pRxBuff);\r
119     \r
120     /* Invoke the callback associated with the current command */\r
121     if (pSpidCmd && pSpidCmd->callback) {\r
122         //printf("p %d", pArg->semaphore);\r
123         pSpidCmd->callback(0, pSpidCmd->pArgument);\r
124     }\r
125 }\r
126 \r
127 /**\r
128  * \brief Configure the DMA Channels: 0 RX, 1 TX.\r
129  * Channels are disabled after configure.\r
130  * \returns 0 if the dma channel configuration successfully; otherwise returns\r
131  * SPID_ERROR_XXX.\r
132  */\r
133 static uint8_t _spid_configureDmaChannels( Spid* pSpid )\r
134 {\r
135   \r
136     /* Driver initialize */\r
137     XDMAD_Initialize(  pSpid->pXdmad, 0 );\r
138     \r
139     XDMAD_FreeChannel( pSpid->pXdmad, spiDmaTxChannel);\r
140     XDMAD_FreeChannel( pSpid->pXdmad, spiDmaRxChannel);\r
141     \r
142     /* Allocate a DMA channel for SPI0/1 TX. */\r
143     spiDmaTxChannel = XDMAD_AllocateChannel( pSpid->pXdmad, XDMAD_TRANSFER_MEMORY, pSpid->spiId);\r
144     {\r
145         if ( spiDmaTxChannel == XDMAD_ALLOC_FAILED ) \r
146         {\r
147             return SPID_ERROR;\r
148         }\r
149     }\r
150     /* Allocate a DMA channel for SPI0/1 RX. */\r
151     spiDmaRxChannel = XDMAD_AllocateChannel( pSpid->pXdmad, pSpid->spiId, XDMAD_TRANSFER_MEMORY);\r
152     {\r
153         if ( spiDmaRxChannel == XDMAD_ALLOC_FAILED ) \r
154         {\r
155             return SPID_ERROR;\r
156         }\r
157     }\r
158 \r
159     /* Setup callbacks for SPI0/1 RX */\r
160     XDMAD_SetCallback(pSpid->pXdmad, spiDmaRxChannel, (XdmadTransferCallback)SPID_Rx_Cb, pSpid);\r
161     if (XDMAD_PrepareChannel( pSpid->pXdmad, spiDmaRxChannel ))\r
162         return SPID_ERROR;\r
163 \r
164     /* Setup callbacks for SPI0/1 TX (ignored) */\r
165     XDMAD_SetCallback(pSpid->pXdmad, spiDmaTxChannel, NULL, NULL);\r
166     if ( XDMAD_PrepareChannel( pSpid->pXdmad, spiDmaTxChannel ))\r
167         return SPID_ERROR;\r
168     \r
169     \r
170     return 0;\r
171 }\r
172 \r
173 /**\r
174  * \brief Configure the DMA source and destination with Linker List mode.\r
175  *\r
176  * \param pCommand Pointer to command\r
177   * \returns 0 if the dma multibuffer configuration successfully; otherwise returns\r
178  * SPID_ERROR_XXX.\r
179  */\r
180 static uint8_t _spid_configureLinkList(Spi *pSpiHw, void *pXdmad, SpidCmd *pCommand)\r
181 {\r
182     sXdmadCfg xdmadRxCfg,xdmadTxCfg;\r
183     uint32_t xdmaCndc;\r
184     uint32_t spiId;\r
185     if ((unsigned int)pSpiHw == (unsigned int)SPI0 ) spiId = ID_SPI0;\r
186     if ((unsigned int)pSpiHw == (unsigned int)SPI1 ) spiId = ID_SPI1;\r
187     \r
188     \r
189     \r
190     /* Setup TX  */ \r
191         \r
192     xdmadTxCfg.mbr_sa = (uint32_t)pCommand->pTxBuff;    \r
193     \r
194     xdmadTxCfg.mbr_da = (uint32_t)&pSpiHw->SPI_TDR;\r
195     \r
196     xdmadTxCfg.mbr_ubc =  XDMA_UBC_NVIEW_NDV0 |\r
197                            XDMA_UBC_NDE_FETCH_DIS|\r
198                            XDMA_UBC_NSEN_UPDATED | pCommand->TxSize;\r
199     \r
200     xdmadTxCfg.mbr_cfg = XDMAC_CC_TYPE_PER_TRAN |\r
201                          XDMAC_CC_MBSIZE_SINGLE |\r
202                          XDMAC_CC_DSYNC_MEM2PER |\r
203                          XDMAC_CC_CSIZE_CHK_1 |\r
204                          XDMAC_CC_DWIDTH_BYTE|\r
205                          XDMAC_CC_SIF_AHB_IF0 |\r
206                          XDMAC_CC_DIF_AHB_IF1 |\r
207                          XDMAC_CC_SAM_INCREMENTED_AM |\r
208                          XDMAC_CC_DAM_FIXED_AM |\r
209                          XDMAC_CC_PERID(XDMAIF_Get_ChannelNumber(  spiId, XDMAD_TRANSFER_TX ));\r
210         \r
211    \r
212     xdmadTxCfg.mbr_bc = 0;\r
213     xdmadTxCfg.mbr_sus = 0;\r
214     xdmadTxCfg.mbr_dus =0;\r
215     \r
216     /* Setup RX Link List */\r
217     \r
218     xdmadRxCfg.mbr_ubc = XDMA_UBC_NVIEW_NDV0 |\r
219                          XDMA_UBC_NDE_FETCH_DIS|\r
220                          XDMA_UBC_NDEN_UPDATED | pCommand->RxSize;\r
221     \r
222     xdmadRxCfg.mbr_da = (uint32_t)pCommand->pRxBuff;\r
223     \r
224     xdmadRxCfg.mbr_sa = (uint32_t)&pSpiHw->SPI_RDR;\r
225     xdmadRxCfg.mbr_cfg = XDMAC_CC_TYPE_PER_TRAN |\r
226                          XDMAC_CC_MBSIZE_SINGLE |\r
227                          XDMAC_CC_DSYNC_PER2MEM |\r
228                          XDMAC_CC_CSIZE_CHK_1 |\r
229                          XDMAC_CC_DWIDTH_BYTE|\r
230                          XDMAC_CC_SIF_AHB_IF1 |\r
231                          XDMAC_CC_DIF_AHB_IF0 |\r
232                          XDMAC_CC_SAM_FIXED_AM |\r
233                          XDMAC_CC_DAM_INCREMENTED_AM |\r
234                          XDMAC_CC_PERID(XDMAIF_Get_ChannelNumber(  spiId, XDMAD_TRANSFER_RX ));\r
235     \r
236 \r
237     xdmadRxCfg.mbr_bc = 0;\r
238     xdmadRxCfg.mbr_sus = 0;\r
239     xdmadRxCfg.mbr_dus =0;\r
240 \r
241     xdmaCndc = 0;\r
242     \r
243 \r
244     if (XDMAD_ConfigureTransfer( pXdmad, spiDmaRxChannel, &xdmadRxCfg, xdmaCndc, 0))\r
245        return SPID_ERROR;\r
246        \r
247     if (XDMAD_ConfigureTransfer( pXdmad, spiDmaTxChannel, &xdmadTxCfg, xdmaCndc, 0))\r
248         return SPID_ERROR;\r
249     return 0;\r
250 }\r
251 \r
252 \r
253 /*----------------------------------------------------------------------------\r
254  *        Exported functions\r
255  *----------------------------------------------------------------------------*/\r
256 /**\r
257  * \brief Initializes the Spid structure and the corresponding SPI & DMA hardware.\r
258  * select value.\r
259  * The driver will uses DMA channel 0 for RX and DMA channel 1 for TX.\r
260  * The DMA channels are freed automatically when no SPI command processing.\r
261  *\r
262  * \param pSpid  Pointer to a Spid instance.\r
263  * \param pSpiHw Associated SPI peripheral.\r
264  * \param spiId  SPI peripheral identifier.\r
265  * \param pDmad  Pointer to a Dmad instance. \r
266  */\r
267 uint32_t SPID_Configure( Spid *pSpid ,\r
268                          Spi *pSpiHw , \r
269                          uint8_t spiId,\r
270                          uint8_t SpiMode,\r
271                          sXdmad *pXdmad )\r
272 {\r
273     /* Initialize the SPI structure */\r
274     pSpid->pSpiHw = pSpiHw;\r
275     pSpid->spiId  = spiId;\r
276     pSpid->semaphore = 1;\r
277     pSpid->pCurrentCommand = 0;\r
278     pSpid->pXdmad = pXdmad;\r
279 \r
280     /* Enable the SPI Peripheral ,Execute a software reset of the SPI, Configure SPI in Master Mode*/\r
281     SPI_Configure ( pSpiHw, pSpid->spiId, SpiMode );\r
282     \r
283     return 0;\r
284 }\r
285 \r
286 /**\r
287  * \brief Configures the parameters for the device corresponding to the cs value.\r
288  *\r
289  * \param pSpid  Pointer to a Spid instance.\r
290  * \param cs number corresponding to the SPI chip select.\r
291  * \param csr SPI_CSR value to setup.\r
292  */\r
293 void SPID_ConfigureCS( Spid *pSpid, \r
294                        uint32_t dwCS, \r
295                        uint32_t dwCsr)\r
296 {\r
297     Spi *pSpiHw = pSpid->pSpiHw;\r
298     \r
299     /* Enable the SPI Peripheral */\r
300     PMC_EnablePeripheral (pSpid->spiId );\r
301     /* Configure SPI Chip Select Register */\r
302     SPI_ConfigureNPCS( pSpiHw, dwCS, dwCsr );\r
303     \r
304     /* Disable the SPI Peripheral */\r
305     PMC_DisablePeripheral (pSpid->spiId );\r
306     \r
307 }\r
308 \r
309 /**\r
310  * \brief Starts a SPI master transfer. This is a non blocking function. It will\r
311  *  return as soon as the transfer is started.\r
312  *\r
313  * \param pSpid  Pointer to a Spid instance.\r
314  * \param pCommand Pointer to the SPI command to execute.\r
315  * \returns 0 if the transfer has been started successfully; otherwise returns\r
316  * SPID_ERROR_LOCK is the driver is in use, or SPID_ERROR if the command is not\r
317  * valid.\r
318  */\r
319 uint32_t SPID_SendCommand( Spid *pSpid, SpidCmd *pCommand)\r
320 {\r
321     Spi *pSpiHw = pSpid->pSpiHw;\r
322          \r
323     /* Try to get the dataflash semaphore */\r
324     if (pSpid->semaphore == 0) {\r
325     \r
326          return SPID_ERROR_LOCK;\r
327     }\r
328     pSpid->semaphore--;\r
329     \r
330     /* Enable the SPI Peripheral */\r
331     PMC_EnablePeripheral (pSpid->spiId );\r
332     \r
333      /* SPI chip select */\r
334     SPI_ChipSelect (pSpiHw, 1 << pCommand->spiCs);\r
335     \r
336     // Initialize the callback\r
337     pSpid->pCurrentCommand = pCommand;\r
338     \r
339     /* Initialize DMA controller using channel 0 for RX, 1 for TX. */\r
340     if (_spid_configureDmaChannels(pSpid) )\r
341         return SPID_ERROR_LOCK;\r
342     \r
343     /* Configure and enable interrupt on RC compare */    \r
344     NVIC_ClearPendingIRQ(XDMAC_IRQn);\r
345     NVIC_SetPriority( XDMAC_IRQn ,1);\r
346     NVIC_EnableIRQ(XDMAC_IRQn);\r
347     \r
348     \r
349     if (_spid_configureLinkList(pSpiHw, pSpid->pXdmad, pCommand))\r
350         return SPID_ERROR_LOCK;\r
351     \r
352     /* Enables the SPI to transfer and receive data. */\r
353     SPI_Enable (pSpiHw );\r
354 \r
355     /* Start DMA 0(RX) && 1(TX) */\r
356     if (XDMAD_StartTransfer( pSpid->pXdmad, spiDmaRxChannel )) \r
357         return SPID_ERROR_LOCK;\r
358     if (XDMAD_StartTransfer( pSpid->pXdmad, spiDmaTxChannel )) \r
359         return SPID_ERROR_LOCK;\r
360 \r
361     return 0;\r
362 }\r
363 \r
364 /**\r
365  * \brief Check if the SPI driver is busy.\r
366  *\r
367  * \param pSpid  Pointer to a Spid instance.\r
368  * \returns 1 if the SPI driver is currently busy executing a command; otherwise\r
369  */\r
370 uint32_t SPID_IsBusy(const Spid *pSpid)\r
371 {\r
372     if (pSpid->semaphore == 0) {\r
373         return 1;\r
374     }\r
375     else {\r
376         return 0;\r
377     }\r
378 }\r