source: rtems/c/src/lib/libbsp/arm/atsam/libraries/libchip/source/xdmad.c @ 8eb5fbb6

5
Last change on this file since 8eb5fbb6 was 8eb5fbb6, checked in by Sebastian Huber <sebastian.huber@…>, on 12/15/16 at 12:27:17

bsp/atsamv: Move XDMAD instance out of SPI driver

Use system initialization for XDMAD. Remove support for polling.

  • Property mode set to 100644
File size: 15.8 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/** \addtogroup xdmad_module
31 *
32 * \section Xdma xDma Configuration Usage
33 *
34 * To configure a XDMA channel, the user has to follow these few steps :
35 * <ul>
36 * <li> Initialize a XDMA driver instance by XDMAD_Initialize().</li>
37 * <li> choose an available (disabled) channel using XDMAD_AllocateChannel().</li>
38 * <li> After the XDMAC selected channel has been programmed,
39 * XDMAD_PrepareChannel() is to enable clock and dma peripheral of the DMA, and
40 * set Configuration register to set up the transfer type (memory or non-memory
41 * peripheral for source and destination) and flow control device.</li>
42 * <li> Invoke XDMAD_StartTransfer() to start DMA transfer  or
43 * XDMAD_StopTransfer() to force stop DMA transfer.</li>
44 * <li> Once the buffer of data is transferred, XDMAD_IsTransferDone()
45 * checks if DMA transfer is finished.</li>
46 * <li> XDMAD_Handler() handles XDMA interrupt, and invoking XDMAD_SetCallback()
47 * if provided.</li>
48 * </ul>
49 *
50 * Related files:\n
51 * \ref xdmad.h\n
52 * \ref xdmad.c.\n
53 */
54
55/** \file */
56
57/** \addtogroup dmad_functions
58  @{*/
59
60/*----------------------------------------------------------------------------
61 *        Includes
62 *----------------------------------------------------------------------------*/
63
64#include "chip.h"
65#ifdef __rtems__
66#include "../../../utils/utility.h"
67#include <rtems/irq-extension.h>
68#include <rtems/sysinit.h>
69#include <bsp/fatal.h>
70#endif /* __rtems__ */
71#include <assert.h>
72
73/*----------------------------------------------------------------------------
74 *        Local functions
75 *----------------------------------------------------------------------------*/
76/**
77 * \brief Try to allocate a DMA channel for on given controller.
78 * \param pDmad  Pointer to DMA driver instance.
79 * \param bSrcID Source peripheral ID, 0xFF for memory.
80 * \param bDstID Destination peripheral ID, 0xFF for memory.
81 * \return Channel number if allocation successful, return
82 * DMAD_ALLOC_FAILED if allocation failed.
83 */
84static uint32_t XDMAD_AllocateXdmacChannel(sXdmad *pXdmad,
85                uint8_t bSrcID,
86                uint8_t bDstID)
87{
88        uint32_t i;
89
90        /* Can't support peripheral to peripheral */
91        if (((bSrcID != XDMAD_TRANSFER_MEMORY)
92                 && (bDstID != XDMAD_TRANSFER_MEMORY)))
93                return XDMAD_ALLOC_FAILED;
94
95        /* dma transfer from peripheral to memory */
96        if (bDstID == XDMAD_TRANSFER_MEMORY) {
97                if ((!XDMAIF_IsValidatedPeripherOnDma(bSrcID))) {
98                        TRACE_ERROR("%s:: Allocation failed", __FUNCTION__);
99                        return XDMAD_ALLOC_FAILED;
100                }
101        }
102
103        /* dma transfer from memory to peripheral */
104        if (bSrcID == XDMAD_TRANSFER_MEMORY) {
105                if ((!XDMAIF_IsValidatedPeripherOnDma(bDstID))) {
106                        TRACE_ERROR("%s:: Allocation failed", __FUNCTION__);
107                        return XDMAD_ALLOC_FAILED;
108                }
109        }
110
111        for (i = 0; i < pXdmad->numChannels; i ++) {
112                if (pXdmad->XdmaChannels[i].state == XDMAD_STATE_FREE) {
113                        /* Allocate the channel */
114                        pXdmad->XdmaChannels[i].state = XDMAD_STATE_ALLOCATED;
115                        /* Get general informations */
116                        pXdmad->XdmaChannels[i].bSrcPeriphID = bSrcID;
117                        pXdmad->XdmaChannels[i].bDstPeriphID = bDstID;
118                        pXdmad->XdmaChannels[i].bSrcTxIfID =
119                                XDMAIF_Get_ChannelNumber(bSrcID, 0);
120                        pXdmad->XdmaChannels[i].bSrcRxIfID =
121                                XDMAIF_Get_ChannelNumber(bSrcID, 1);
122                        pXdmad->XdmaChannels[i].bDstTxIfID =
123                                XDMAIF_Get_ChannelNumber(bDstID, 0);
124                        pXdmad->XdmaChannels[i].bDstRxIfID =
125                                XDMAIF_Get_ChannelNumber(bDstID, 1);
126                        return  ((i) & 0xFF);
127                }
128        }
129
130        TRACE_ERROR("%s:: Allocation failed, all channels are occupied", __FUNCTION__);
131        return XDMAD_ALLOC_FAILED;
132}
133
134void XDMAD_DoNothingCallback(uint32_t Channel, void *pArg)
135{
136        /* Do nothing */
137}
138
139/*----------------------------------------------------------------------------
140 *        Exported functions
141 *----------------------------------------------------------------------------*/
142
143sXdmad XDMAD_Instance;
144
145static void XDMAD_SysInitialize(void)
146{
147        sXdmad *pXdmad;
148        uint32_t j;
149        rtems_status_code sc;
150
151        pXdmad = &XDMAD_Instance;
152
153        pXdmad->pXdmacs = XDMAC;
154        pXdmad->numControllers = XDMAC_CONTROLLER_NUM;
155        pXdmad->numChannels    = (XDMAC_GTYPE_NB_CH(XDMAC_GetType(XDMAC)) + 1);
156
157        for (j = 0; j < pXdmad->numChannels; j ++) {
158                pXdmad->XdmaChannels[j].fCallback = XDMAD_DoNothingCallback;
159        }
160
161        sc = rtems_interrupt_handler_install(
162                ID_XDMAC,
163                "XDMA",
164                RTEMS_INTERRUPT_UNIQUE,
165                XDMAD_Handler,
166                pXdmad
167        );
168        if (sc != RTEMS_SUCCESSFUL) {
169                bsp_fatal(ATSAM_FATAL_XDMA_IRQ_INSTALL);
170        }
171}
172
173RTEMS_SYSINIT_ITEM(XDMAD_SysInitialize, RTEMS_SYSINIT_BSP_START,
174    RTEMS_SYSINIT_ORDER_LAST);
175
176
177/**
178 * \brief Allocate a XDMA channel for upper layer.
179 * \param pXdmad  Pointer to xDMA driver instance.
180 * \param bSrcID Source peripheral ID, 0xFF for memory.
181 * \param bDstID Destination peripheral ID, 0xFF for memory.
182 * \return Channel number if allocation successful, return
183 * XDMAD_ALLOC_FAILED if allocation failed.
184 */
185uint32_t XDMAD_AllocateChannel(sXdmad *pXdmad,
186                                                                uint8_t bSrcID,
187                                                                uint8_t bDstID)
188{
189        uint32_t dwChannel = XDMAD_ALLOC_FAILED;
190        uint32_t volatile timer = 0x7FF;
191
192        LockMutex(pXdmad->xdmaMutex, timer);
193        dwChannel = XDMAD_AllocateXdmacChannel(pXdmad,  bSrcID, bDstID);
194        ReleaseMutex(pXdmad->xdmaMutex);
195
196        return dwChannel;
197}
198
199/**
200 * \brief Free the specified xDMA channel.
201 * \param pXdmad     Pointer to xDMA driver instance.
202 * \param dwChannel ControllerNumber << 8 | ChannelNumber.
203 */
204eXdmadRC XDMAD_FreeChannel(sXdmad *pXdmad,
205                                                        uint32_t dwChannel)
206{
207        uint8_t iChannel    = (dwChannel) & 0xFF;
208        assert(pXdmad != NULL);
209
210        if (iChannel >= pXdmad->numChannels) return XDMAD_ERROR;
211
212        switch (pXdmad->XdmaChannels[iChannel].state) {
213        case XDMAD_STATE_ALLOCATED:
214        case XDMAD_STATE_START:
215        case XDMAD_STATE_IN_XFR:
216                return XDMAD_BUSY;
217
218        case XDMAD_STATE_DONE:
219        case XDMAD_STATE_HALTED:
220                pXdmad->XdmaChannels[iChannel].state = XDMAD_STATE_FREE;
221                break;
222        }
223
224        return XDMAD_OK;
225}
226
227/**
228 * \brief Set the callback function for xDMA channel transfer.
229 * \param pXdmad     Pointer to xDMA driver instance.
230 * \param dwChannel ControllerNumber << 8 | ChannelNumber.
231 * \param fCallback Pointer to callback function.
232 * \param pArg Pointer to optional argument for callback.
233 */
234eXdmadRC XDMAD_SetCallback(sXdmad *pXdmad,
235                                                        uint32_t dwChannel,
236                                                        XdmadTransferCallback fCallback,
237                                                        void *pArg)
238{
239
240        uint8_t iChannel    = (dwChannel) & 0xFF;
241        assert(pXdmad != NULL);
242
243        if (iChannel >= pXdmad->numChannels) return XDMAD_ERROR;
244
245        if (pXdmad->XdmaChannels[iChannel].state == XDMAD_STATE_FREE)
246                return XDMAD_ERROR;
247        else if (pXdmad->XdmaChannels[iChannel].state == XDMAD_STATE_START)
248                return XDMAD_BUSY;
249
250        pXdmad->XdmaChannels[iChannel].fCallback = fCallback;
251        pXdmad->XdmaChannels[iChannel].pArg = pArg;
252
253        return XDMAD_OK;
254}
255
256
257/**
258 * \brief Enable clock of the xDMA peripheral, Enable the dma peripheral,
259 * configure configuration register for xDMA transfer.
260 * \param pXdmad     Pointer to xDMA driver instance.
261 * \param dwChannel ControllerNumber << 8 | ChannelNumber.
262 * \param dwCfg     Configuration value.
263 */
264eXdmadRC XDMAD_PrepareChannel(sXdmad *pXdmad, uint32_t dwChannel)
265{
266
267        uint8_t iChannel    = (dwChannel) & 0xFF;
268        Xdmac *pXdmac = pXdmad->pXdmacs;
269
270        assert(pXdmad != NULL);
271
272        if (iChannel >= pXdmad->numChannels) return XDMAD_ERROR;
273
274        if (pXdmad->XdmaChannels[iChannel].state == XDMAD_STATE_FREE)
275                return XDMAD_ERROR;
276        else if ((pXdmad->XdmaChannels[iChannel].state == XDMAD_STATE_START)
277                          || (pXdmad->XdmaChannels[iChannel].state == XDMAD_STATE_IN_XFR))
278                return XDMAD_BUSY;
279
280
281        /* Enable clock of the DMA peripheral */
282        if (!PMC_IsPeriphEnabled(ID_XDMAC))
283                PMC_EnablePeripheral(ID_XDMAC);
284
285        /* Clear dummy status */
286        XDMAC_GetChannelIsr(pXdmac, iChannel);
287        /* Disables XDMAC interrupt for the given channel. */
288        XDMAC_DisableGIt (pXdmac, iChannel);
289        XDMAC_DisableChannelIt (pXdmac, iChannel, 0xFF);
290        /* Disable the given dma channel. */
291        XDMAC_DisableChannel(pXdmac, iChannel);
292        XDMAC_SetSourceAddr(pXdmac, iChannel, 0);
293        XDMAC_SetDestinationAddr(pXdmac, iChannel, 0);
294        XDMAC_SetBlockControl(pXdmac, iChannel, 0);
295        XDMAC_SetChannelConfig(pXdmac, iChannel, 0x20);
296        XDMAC_SetDescriptorAddr(pXdmac, iChannel, 0, 0);
297        XDMAC_SetDescriptorControl(pXdmac, iChannel, 0);
298        return XDMAD_OK;
299}
300
301/**
302 * \brief xDMA interrupt handler
303 * \param arg Pointer to DMA driver instance.
304 */
305void XDMAD_Handler(void *arg)
306{
307        sXdmad *pDmad;
308        Xdmac *pXdmac;
309        sXdmadChannel *pCh;
310        uint32_t xdmaChannelIntStatus, xdmaGlobaIntStatus, xdmaGlobalChStatus;
311        uint8_t bExec;
312        uint8_t _iChannel;
313
314        pDmad = arg;
315        pXdmac = pDmad->pXdmacs;
316        xdmaGlobaIntStatus = XDMAC_GetGIsr(pXdmac) & 0xFFFFFF;
317        xdmaGlobalChStatus = XDMAC_GetGlobalChStatus(pXdmac);
318
319        while (xdmaGlobaIntStatus != 0) {
320                _iChannel = 31 - __builtin_clz(xdmaGlobaIntStatus);
321                xdmaGlobaIntStatus &= ~(UINT32_C(1) << _iChannel);
322
323                pCh = &pDmad->XdmaChannels[_iChannel];
324                bExec = 0;
325
326                if ((xdmaGlobalChStatus & (XDMAC_GS_ST0 << _iChannel)) == 0) {
327                        xdmaChannelIntStatus = XDMAC_GetMaskChannelIsr(pXdmac, _iChannel);
328
329                        if (xdmaChannelIntStatus & XDMAC_CIS_BIS) {
330                                if ((XDMAC_GetChannelItMask(pXdmac, _iChannel) & XDMAC_CIM_LIM) == 0) {
331                                        pCh->state = XDMAD_STATE_DONE;
332                                        bExec = 1;
333                                }
334
335                                TRACE_DEBUG("XDMAC_CIS_BIS\n\r");
336                        }
337
338                        if (xdmaChannelIntStatus & XDMAC_CIS_FIS)
339                                TRACE_DEBUG("XDMAC_CIS_FIS\n\r");
340
341                        if (xdmaChannelIntStatus & XDMAC_CIS_RBEIS)
342                                TRACE_DEBUG("XDMAC_CIS_RBEIS\n\r");
343
344                        if (xdmaChannelIntStatus & XDMAC_CIS_WBEIS)
345                                TRACE_DEBUG("XDMAC_CIS_WBEIS\n\r");
346
347                        if (xdmaChannelIntStatus & XDMAC_CIS_ROIS)
348                                TRACE_DEBUG("XDMAC_CIS_ROIS\n\r");
349
350                        if (xdmaChannelIntStatus & XDMAC_CIS_LIS) {
351                                TRACE_DEBUG("XDMAC_CIS_LIS\n\r");
352                                pCh->state = XDMAD_STATE_DONE;
353                                bExec = 1;
354                        }
355
356                        if (xdmaChannelIntStatus & XDMAC_CIS_DIS) {
357                                pCh->state = XDMAD_STATE_DONE;
358                                bExec = 1;
359                        }
360
361                } else {
362                        /* Block end interrupt for LLI dma mode */
363                        if (XDMAC_GetChannelIsr(pXdmac, _iChannel) & XDMAC_CIS_BIS) {
364                                bExec = 1;
365                        }
366                }
367
368                /* Execute callback */
369                if (bExec)
370                        pCh->fCallback(_iChannel, pCh->pArg);
371        }
372}
373
374
375/**
376 * \brief Configure DMA for a single transfer.
377 * \param pXdmad     Pointer to xDMA driver instance.
378 * \param dwChannel ControllerNumber << 8 | ChannelNumber.
379 */
380eXdmadRC XDMAD_ConfigureTransfer(sXdmad *pXdmad,
381                                                                  uint32_t dwChannel,
382                                                                  sXdmadCfg *pXdmaParam,
383                                                                  uint32_t dwXdmaDescCfg,
384                                                                  uint32_t dwXdmaDescAddr,
385                                                                  uint32_t dwXdmaIntEn)
386{
387        uint8_t iChannel    = (dwChannel) & 0xFF;
388
389        assert(pXdmad != NULL);
390
391        if (iChannel >= pXdmad->numChannels)
392                return XDMAD_ERROR;
393
394        Xdmac *pXdmac = pXdmad->pXdmacs;
395        XDMAC_GetChannelIsr(pXdmac, iChannel);
396
397        if (pXdmad->XdmaChannels[iChannel].state == XDMAD_STATE_FREE)
398                return XDMAD_ERROR;
399
400        if (pXdmad->XdmaChannels[iChannel].state == XDMAD_STATE_START)
401                return XDMAD_BUSY;
402
403        /* Linked List is enabled */
404        if ((dwXdmaDescCfg & XDMAC_CNDC_NDE) == XDMAC_CNDC_NDE_DSCR_FETCH_EN) {
405                if ((dwXdmaDescCfg & XDMAC_CNDC_NDVIEW_Msk) == XDMAC_CNDC_NDVIEW_NDV0) {
406                        XDMAC_SetChannelConfig(pXdmac, iChannel, pXdmaParam->mbr_cfg);
407                        XDMAC_SetSourceAddr(pXdmac, iChannel, pXdmaParam->mbr_sa);
408                        XDMAC_SetDestinationAddr(pXdmac, iChannel, pXdmaParam->mbr_da);
409                }
410
411                if ((dwXdmaDescCfg & XDMAC_CNDC_NDVIEW_Msk) == XDMAC_CNDC_NDVIEW_NDV1)
412                        XDMAC_SetChannelConfig(pXdmac, iChannel, pXdmaParam->mbr_cfg);
413
414                XDMAC_SetDescriptorAddr(pXdmac, iChannel, dwXdmaDescAddr, 0);
415                XDMAC_SetDescriptorControl(pXdmac, iChannel, dwXdmaDescCfg);
416                XDMAC_DisableChannelIt (pXdmac, iChannel, 0xFF);
417                XDMAC_EnableChannelIt (pXdmac, iChannel, dwXdmaIntEn);
418        } else {
419                /* LLI is disabled. */
420                XDMAC_SetSourceAddr(pXdmac, iChannel, pXdmaParam->mbr_sa);
421                XDMAC_SetDestinationAddr(pXdmac, iChannel, pXdmaParam->mbr_da);
422                XDMAC_SetMicroblockControl(pXdmac, iChannel, pXdmaParam->mbr_ubc);
423                XDMAC_SetBlockControl(pXdmac, iChannel, pXdmaParam->mbr_bc);
424                XDMAC_SetDataStride_MemPattern(pXdmac, iChannel, pXdmaParam->mbr_ds);
425                XDMAC_SetSourceMicroBlockStride(pXdmac, iChannel, pXdmaParam->mbr_sus);
426                XDMAC_SetDestinationMicroBlockStride(pXdmac, iChannel, pXdmaParam->mbr_dus);
427                XDMAC_SetChannelConfig(pXdmac, iChannel, pXdmaParam->mbr_cfg);
428                XDMAC_SetDescriptorAddr(pXdmac, iChannel, 0, 0);
429                XDMAC_SetDescriptorControl(pXdmac, iChannel, 0);
430                XDMAC_EnableChannelIt (pXdmac, iChannel, dwXdmaIntEn);
431        }
432
433        return XDMAD_OK;
434}
435
436/**
437 * \brief Start xDMA transfer.
438 * \param pXdmad     Pointer to XDMA driver instance.
439 * \param dwChannel ControllerNumber << 8 | ChannelNumber.
440 */
441eXdmadRC XDMAD_StartTransfer(sXdmad *pXdmad, uint32_t dwChannel)
442{
443        uint8_t iChannel    = (dwChannel) & 0xFF;
444
445        assert(pXdmad != NULL);
446
447        if (iChannel >= pXdmad->numChannels) return XDMAD_ERROR;
448
449        Xdmac *pXdmac = pXdmad->pXdmacs;
450
451        if (pXdmad->XdmaChannels[iChannel].state == XDMAD_STATE_FREE) {
452                TRACE_ERROR("%s:: XDMAD_STATE_FREE \n\r", __FUNCTION__);
453                return XDMAD_ERROR;
454        } else if (pXdmad->XdmaChannels[iChannel].state == XDMAD_STATE_START) {
455                TRACE_ERROR("%s:: XDMAD_STATE_START \n\r", __FUNCTION__)
456                return XDMAD_BUSY;
457        }
458
459        /* Change state to transferring */
460        pXdmad->XdmaChannels[iChannel].state = XDMAD_STATE_START;
461        XDMAC_EnableChannel(pXdmac, iChannel);
462        XDMAC_EnableGIt(pXdmac, iChannel);
463
464        return XDMAD_OK;
465}
466
467
468/**
469 * \brief Stop DMA transfer.
470 * \param pDmad     Pointer to DMA driver instance.
471 * \param dwChannel ControllerNumber << 8 | ChannelNumber.
472 */
473eXdmadRC XDMAD_StopTransfer(sXdmad *pXdmad, uint32_t dwChannel)
474{
475        uint8_t _iChannel    = (dwChannel) & 0xFF;
476        assert(pXdmad != NULL);
477
478        if (_iChannel >= pXdmad->numChannels) return XDMAD_ERROR;
479
480        Xdmac *pXdmac = pXdmad->pXdmacs;
481
482        pXdmad->XdmaChannels[_iChannel].state = XDMAD_STATE_HALTED;
483        /* Disable channel */
484        XDMAC_DisableChannel(pXdmac, _iChannel);
485        /* Disable interrupts */
486        XDMAC_DisableChannelIt(pXdmac, _iChannel, 0xFF);
487        /* Clear pending status */
488        XDMAC_GetChannelIsr(pXdmac, _iChannel);
489        XDMAC_GetGlobalChStatus(pXdmac);
490        return XDMAD_OK;
491}
492
493/**@}*/
494
Note: See TracBrowser for help on using the repository browser.