]> git.sur5r.net Git - freertos/blob - FreeRTOS/Demo/CORTEX_M7_SAMV71_Xplained_IAR_Keil/libchip_samv7/source/qspi_dma.c
Final V8.2.1 release ready for tagging:
[freertos] / FreeRTOS / Demo / CORTEX_M7_SAMV71_Xplained_IAR_Keil / libchip_samv7 / source / qspi_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> QSPID_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 QSPID_ConfigureCS(). </li>\r
38  * <li> Starts a SPI master transfer. This is a non blocking function QSPID_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_QSPI_DMA\r
64 \r
65 #define TX_MICROBLOCK_LEN       256\r
66 #define RX_MICROBLOCK_LEN       256\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 qspiDmaTxChannel;\r
79 static uint32_t qspiDmaRxChannel;\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 Qspid instance.   \r
90  */ \r
91 static void QSPID_Rx_Cb(uint32_t channel, Qspid* pArg)\r
92 {\r
93     QspidCmd *pQspidCmd = pArg->pCurrentCommand;\r
94     Qspi *pQspiHw = pArg->pQspiHw;\r
95     if (channel != qspiDmaRxChannel)\r
96         return;\r
97 \r
98     /* Disable the SPI TX & RX */\r
99     QSPI_Disable ( pQspiHw );\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     QSPI_ReleaseCS(pQspiHw);\r
110 \r
111     /* Release the DMA channels */\r
112     XDMAD_FreeChannel(pArg->pXdmad, qspiDmaRxChannel);\r
113     XDMAD_FreeChannel(pArg->pXdmad, qspiDmaTxChannel);\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 (pQspidCmd && pQspidCmd->callback) {\r
122         //printf("p %d", pArg->semaphore);\r
123         pQspidCmd->callback(0, pQspidCmd->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  * QSPID_ERROR_XXX.\r
132  */\r
133 static uint8_t _qspid_configureDmaChannels( Qspid* pQspid )\r
134 {\r
135 \r
136     /* Driver initialize */\r
137     XDMAD_Initialize(  pQspid->pXdmad, 0 );\r
138 \r
139     XDMAD_FreeChannel( pQspid->pXdmad, qspiDmaTxChannel);\r
140     XDMAD_FreeChannel( pQspid->pXdmad, qspiDmaRxChannel);\r
141 \r
142     /* Allocate a DMA channel for SPI0/1 TX. */\r
143     qspiDmaTxChannel = XDMAD_AllocateChannel( pQspid->pXdmad, XDMAD_TRANSFER_MEMORY, pQspid->spiId);\r
144     {\r
145         if ( qspiDmaTxChannel == XDMAD_ALLOC_FAILED ) \r
146         {\r
147             return QSPID_ERROR;\r
148         }\r
149     }\r
150     /* Allocate a DMA channel for SPI0/1 RX. */\r
151     qspiDmaRxChannel = XDMAD_AllocateChannel( pQspid->pXdmad, pQspid->spiId, XDMAD_TRANSFER_MEMORY);\r
152     {\r
153         if ( qspiDmaRxChannel == XDMAD_ALLOC_FAILED ) \r
154         {\r
155             return QSPID_ERROR;\r
156         }\r
157     }\r
158 \r
159     /* Setup callbacks for SPI0/1 RX */\r
160     XDMAD_SetCallback(pQspid->pXdmad, qspiDmaRxChannel, (XdmadTransferCallback)QSPID_Rx_Cb, pQspid);\r
161     if (XDMAD_PrepareChannel( pQspid->pXdmad, qspiDmaRxChannel ))\r
162         return QSPID_ERROR;\r
163 \r
164     /* Setup callbacks for SPI0/1 TX (ignored) */\r
165     XDMAD_SetCallback(pQspid->pXdmad, qspiDmaTxChannel, NULL, NULL);\r
166     if ( XDMAD_PrepareChannel( pQspid->pXdmad, qspiDmaTxChannel ))\r
167         return QSPID_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  * QSPID_ERROR_XXX.\r
179  */\r
180 static uint8_t _spid_configureLinkList(Qspi *pQspiHw, void *pXdmad, QspidCmd *pCommand)\r
181 {\r
182     sXdmadCfg xdmadRxCfg,xdmadTxCfg;\r
183     uint32_t xdmaCndc;\r
184     uint32_t qspiId;\r
185     if ((unsigned int)pQspiHw == (unsigned int)QSPI ) qspiId = ID_QSPI;\r
186 \r
187 \r
188 \r
189     /* Setup TX  */ \r
190 \r
191     xdmadTxCfg.mbr_ubc = TX_MICROBLOCK_LEN;\r
192     xdmadTxCfg.mbr_sa = (uint32_t)pCommand->pTxBuff;\r
193     xdmadTxCfg.mbr_da = (uint32_t)QSPIMEM_ADDR;\r
194     xdmadTxCfg.mbr_cfg = XDMAC_CC_TYPE_MEM_TRAN |\r
195         XDMAC_CC_MBSIZE_SINGLE |\r
196         XDMAC_CC_MEMSET_NORMAL_MODE |\r
197         XDMAC_CC_CSIZE_CHK_1 |\r
198         XDMAC_CC_DWIDTH_BYTE|\r
199         XDMAC_CC_SIF_AHB_IF0 |\r
200         XDMAC_CC_DIF_AHB_IF0 |\r
201         XDMAC_CC_SAM_INCREMENTED_AM |\r
202         XDMAC_CC_DAM_INCREMENTED_AM |\r
203         XDMAC_CC_PERID(XDMAIF_Get_ChannelNumber(  qspiId, XDMAD_TRANSFER_TX ));\r
204 \r
205     xdmadTxCfg.mbr_bc = 0;\r
206     xdmadTxCfg.mbr_ds =  0;\r
207     xdmadTxCfg.mbr_sus = 0;\r
208     xdmadTxCfg.mbr_dus = 0; \r
209 \r
210 \r
211     /* Setup RX Link List */\r
212 \r
213     xdmadRxCfg.mbr_ubc = RX_MICROBLOCK_LEN;\r
214     xdmadRxCfg.mbr_sa = (uint32_t)QSPIMEM_ADDR;\r
215     xdmadRxCfg.mbr_da = (uint32_t)pCommand->pRxBuff;\r
216     xdmadRxCfg.mbr_cfg = XDMAC_CC_TYPE_MEM_TRAN |\r
217         XDMAC_CC_MBSIZE_SINGLE |\r
218         XDMAC_CC_MEMSET_NORMAL_MODE |\r
219         XDMAC_CC_CSIZE_CHK_1 |\r
220         XDMAC_CC_DWIDTH_BYTE|\r
221         XDMAC_CC_SIF_AHB_IF0 |\r
222         XDMAC_CC_DIF_AHB_IF0 |\r
223         XDMAC_CC_SAM_INCREMENTED_AM |\r
224         XDMAC_CC_DAM_INCREMENTED_AM |\r
225         XDMAC_CC_PERID(XDMAIF_Get_ChannelNumber(  qspiId, XDMAD_TRANSFER_RX ));\r
226 \r
227     xdmadRxCfg.mbr_bc = 0;\r
228     xdmadRxCfg.mbr_ds =  0;\r
229     xdmadRxCfg.mbr_sus = 0;\r
230     xdmadRxCfg.mbr_dus = 0; \r
231 \r
232 \r
233     if (XDMAD_ConfigureTransfer( pXdmad, qspiDmaRxChannel, &xdmadRxCfg, 0, 0))\r
234         return QSPID_ERROR;\r
235 \r
236     if (XDMAD_ConfigureTransfer( pXdmad, qspiDmaTxChannel, &xdmadTxCfg, 0, 0))\r
237         return QSPID_ERROR;\r
238 \r
239 \r
240     return 0;\r
241 }\r
242 \r
243 \r
244 /*----------------------------------------------------------------------------\r
245  *        Exported functions\r
246  *----------------------------------------------------------------------------*/\r
247 /**\r
248  * \brief Initializes the Qspid structure and the corresponding SPI & DMA hardware.\r
249  * select value.\r
250  * The driver will uses DMA channel 0 for RX and DMA channel 1 for TX.\r
251  * The DMA channels are freed automatically when no SPI command processing.\r
252  *\r
253  * \param pQspid  Pointer to a Qspid instance.\r
254  * \param pQspiHw Associated SPI peripheral.\r
255  * \param spiId  SPI peripheral identifier.\r
256  * \param pDmad  Pointer to a Dmad instance. \r
257  */\r
258 uint32_t QSPID_Configure( Qspid *pQspid ,\r
259         Qspi *pQspiHw , \r
260         uint8_t spiId,\r
261         uint8_t QspiMode,\r
262         sXdmad *pXdmad )\r
263 {\r
264     /* Initialize the SPI structure */\r
265     pQspid->pQspiHw = pQspiHw;\r
266     pQspid->spiId  = spiId;\r
267     pQspid->semaphore = 1;\r
268     pQspid->pCurrentCommand = 0;\r
269     pQspid->pXdmad = pXdmad;\r
270 \r
271     /* Enable the SPI Peripheral ,Execute a software reset of the SPI, Configure SPI in Master Mode*/\r
272     QSPI_Configure ( pQspiHw, pQspid->spiId, QspiMode );\r
273 \r
274     return 0;\r
275 }\r
276 \r
277 \r
278 \r
279 /**\r
280  * \brief Starts a SPI master transfer. This is a non blocking function. It will\r
281  *  return as soon as the transfer is started.\r
282  *\r
283  * \param pQspid  Pointer to a Qspid instance.\r
284  * \param pCommand Pointer to the SPI command to execute.\r
285  * \returns 0 if the transfer has been started successfully; otherwise returns\r
286  * QQSPID_ERROR_LOCK is the driver is in use, or QQSPID_ERROR if the command is not\r
287  * valid.\r
288  */\r
289 uint32_t QSPID_SendCommand( Qspid *pQspid, QspidCmd *pCommand)\r
290 {\r
291     Qspi *pQspiHw = pQspid->pQspiHw;\r
292 \r
293     /* Try to get the dataflash semaphore */\r
294     if (pQspid->semaphore == 0) {\r
295 \r
296         return QQSPID_ERROR_LOCK;\r
297     }\r
298     pQspid->semaphore--;\r
299 \r
300     /* Enable the SPI Peripheral */\r
301     PMC_EnablePeripheral (pQspid->spiId );\r
302 \r
303     /* SPI chip select */\r
304     SPI_ChipSelect (pQspiHw, 1 << pCommand->spiCs);\r
305 \r
306     // Initialize the callback\r
307     pQspid->pCurrentCommand = pCommand;\r
308 \r
309     /* Initialize DMA controller using channel 0 for RX, 1 for TX. */\r
310     if (_spid_configureDmaChannels(pQspid) )\r
311         return QQSPID_ERROR_LOCK;\r
312 \r
313     /* Configure and enable interrupt on RC compare */    \r
314     NVIC_ClearPendingIRQ(XDMAC_IRQn);\r
315     NVIC_SetPriority( XDMAC_IRQn ,1);\r
316     NVIC_EnableIRQ(XDMAC_IRQn);\r
317 \r
318 \r
319     if (_spid_configureLinkList(pQspiHw, pQspid->pXdmad, pCommand))\r
320         return QSPID_ERROR_LOCK;\r
321 \r
322     /* Enables the SPI to transfer and receive data. */\r
323     SPI_Enable (pQspiHw );\r
324 \r
325     /* Start DMA 0(RX) && 1(TX) */\r
326     if (XDMAD_StartTransfer( pQspid->pXdmad, qspiDmaRxChannel )) \r
327         return QSPID_ERROR_LOCK;\r
328     if (XDMAD_StartTransfer( pQspid->pXdmad, qspiDmaTxChannel )) \r
329         return QSPID_ERROR_LOCK;\r
330 \r
331     return 0;\r
332 }\r
333 \r
334 /**\r
335  * \brief Check if the SPI driver is busy.\r
336  *\r
337  * \param pQspid  Pointer to a Qspid instance.\r
338  * \returns 1 if the SPI driver is currently busy executing a command; otherwise\r
339  */\r
340 uint32_t QSPID_IsBusy(const Qspid *pQspid)\r
341 {\r
342     if (pQspid->semaphore == 0) {\r
343         return 1;\r
344     }\r
345     else {\r
346         return 0;\r
347     }\r
348 }\r