source: rtems/c/src/lib/libbsp/arm/atsam/libraries/libchip/source/spi_dma.c @ 71f29c12

5
Last change on this file since 71f29c12 was 71f29c12, checked in by Sebastian Huber <sebastian.huber@…>, on 12/20/16 at 07:49:32

bsp/atsam: Use standard XDMA throughout

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