source: rtems/bsps/arm/atsam/contrib/libraries/libchip/source/xdmad.c @ c344e58

5
Last change on this file since c344e58 was c344e58, checked in by Sebastian Huber <sebastian.huber@…>, on 02/02/20 at 10:00:54

Use RTEMS_SYSINIT_ORDER_LAST_BUT_5

Use RTEMS_SYSINIT_ORDER_LAST_BUT_5 instead of RTEMS_SYSINIT_ORDER_LAST
to allow applications and support functions to place system
initialization handlers behind the standard handlers.

Update #3838.

  • Property mode set to 100644
File size: 14.7 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 *----------------------------------------------------------------------------*/
76static void XDMAD_Handler(void *arg);
77/**
78 * \brief Try to allocate a DMA channel for on given controller.
79 * \param pDmad  Pointer to DMA driver instance.
80 * \param bSrcID Source peripheral ID, 0xFF for memory.
81 * \param bDstID Destination peripheral ID, 0xFF for memory.
82 * \return Channel number if allocation successful, return
83 * DMAD_ALLOC_FAILED if allocation failed.
84 */
85static uint32_t XDMAD_AllocateXdmacChannel(sXdmad *pXdmad,
86                uint8_t bSrcID,
87                uint8_t bDstID)
88{
89        uint32_t i;
90
91        /* Can't support peripheral to peripheral */
92        if (((bSrcID != XDMAD_TRANSFER_MEMORY)
93                 && (bDstID != XDMAD_TRANSFER_MEMORY)))
94                return XDMAD_ALLOC_FAILED;
95
96        /* dma transfer from peripheral to memory */
97        if (bDstID == XDMAD_TRANSFER_MEMORY) {
98                if ((!XDMAIF_IsValidatedPeripherOnDma(bSrcID))) {
99                        TRACE_ERROR("%s:: Allocation failed", __FUNCTION__);
100                        return XDMAD_ALLOC_FAILED;
101                }
102        }
103
104        /* dma transfer from memory to peripheral */
105        if (bSrcID == XDMAD_TRANSFER_MEMORY) {
106                if ((!XDMAIF_IsValidatedPeripherOnDma(bDstID))) {
107                        TRACE_ERROR("%s:: Allocation failed", __FUNCTION__);
108                        return XDMAD_ALLOC_FAILED;
109                }
110        }
111
112        for (i = 0; i < pXdmad->numChannels; i ++) {
113                if (pXdmad->XdmaChannels[i].state == XDMAD_STATE_FREE) {
114                        /* Allocate the channel */
115                        pXdmad->XdmaChannels[i].state = XDMAD_STATE_ALLOCATED;
116                        /* Get general informations */
117                        pXdmad->XdmaChannels[i].bSrcPeriphID = bSrcID;
118                        pXdmad->XdmaChannels[i].bDstPeriphID = bDstID;
119                        pXdmad->XdmaChannels[i].bSrcTxIfID =
120                                XDMAIF_Get_ChannelNumber(bSrcID, 0);
121                        pXdmad->XdmaChannels[i].bSrcRxIfID =
122                                XDMAIF_Get_ChannelNumber(bSrcID, 1);
123                        pXdmad->XdmaChannels[i].bDstTxIfID =
124                                XDMAIF_Get_ChannelNumber(bDstID, 0);
125                        pXdmad->XdmaChannels[i].bDstRxIfID =
126                                XDMAIF_Get_ChannelNumber(bDstID, 1);
127                        return  ((i) & 0xFF);
128                }
129        }
130
131        TRACE_ERROR("%s:: Allocation failed, all channels are occupied", __FUNCTION__);
132        return XDMAD_ALLOC_FAILED;
133}
134
135void XDMAD_DoNothingCallback(uint32_t Channel, void *pArg, uint32_t status)
136{
137        /* Do nothing */
138}
139
140/*----------------------------------------------------------------------------
141 *        Exported functions
142 *----------------------------------------------------------------------------*/
143
144sXdmad XDMAD_Instance;
145
146static void XDMAD_SysInitialize(void)
147{
148        sXdmad *pXdmad;
149        uint32_t j;
150        rtems_status_code sc;
151
152        pXdmad = &XDMAD_Instance;
153
154        pXdmad->pXdmacs = XDMAC;
155        pXdmad->numControllers = XDMAC_CONTROLLER_NUM;
156        pXdmad->numChannels    = (XDMAC_GTYPE_NB_CH(XDMAC_GetType(XDMAC)) + 1);
157
158        for (j = 0; j < pXdmad->numChannels; j ++) {
159                pXdmad->XdmaChannels[j].fCallback = XDMAD_DoNothingCallback;
160        }
161
162        sc = rtems_interrupt_handler_install(
163                ID_XDMAC,
164                "XDMA",
165                RTEMS_INTERRUPT_UNIQUE,
166                XDMAD_Handler,
167                pXdmad
168        );
169        if (sc != RTEMS_SUCCESSFUL) {
170                bsp_fatal(ATSAM_FATAL_XDMA_IRQ_INSTALL);
171        }
172}
173
174RTEMS_SYSINIT_ITEM(XDMAD_SysInitialize, RTEMS_SYSINIT_BSP_START,
175    RTEMS_SYSINIT_ORDER_LAST_BUT_5);
176
177
178/**
179 * \brief Allocate a XDMA channel for upper layer.
180 * \param pXdmad  Pointer to xDMA driver instance.
181 * \param bSrcID Source peripheral ID, 0xFF for memory.
182 * \param bDstID Destination peripheral ID, 0xFF for memory.
183 * \return Channel number if allocation successful, return
184 * XDMAD_ALLOC_FAILED if allocation failed.
185 */
186uint32_t XDMAD_AllocateChannel(sXdmad *pXdmad,
187                                                                uint8_t bSrcID,
188                                                                uint8_t bDstID)
189{
190        uint32_t dwChannel = XDMAD_ALLOC_FAILED;
191        uint32_t volatile timer = 0x7FF;
192
193        LockMutex(pXdmad->xdmaMutex, timer);
194        dwChannel = XDMAD_AllocateXdmacChannel(pXdmad,  bSrcID, bDstID);
195        ReleaseMutex(pXdmad->xdmaMutex);
196
197        return dwChannel;
198}
199
200/**
201 * \brief Free the specified xDMA channel.
202 * \param pXdmad     Pointer to xDMA driver instance.
203 * \param dwChannel ControllerNumber << 8 | ChannelNumber.
204 */
205eXdmadRC XDMAD_FreeChannel(sXdmad *pXdmad,
206                                                        uint32_t dwChannel)
207{
208        uint8_t iChannel    = (dwChannel) & 0xFF;
209        assert(pXdmad != NULL);
210
211        if (iChannel >= pXdmad->numChannels) return XDMAD_ERROR;
212
213        switch (pXdmad->XdmaChannels[iChannel].state) {
214        case XDMAD_STATE_ALLOCATED:
215        case XDMAD_STATE_START:
216        case XDMAD_STATE_IN_XFR:
217                return XDMAD_BUSY;
218
219        case XDMAD_STATE_DONE:
220        case XDMAD_STATE_HALTED:
221                pXdmad->XdmaChannels[iChannel].state = XDMAD_STATE_FREE;
222                break;
223        }
224
225        return XDMAD_OK;
226}
227
228/**
229 * \brief Set the callback function for xDMA channel transfer.
230 * \param pXdmad     Pointer to xDMA driver instance.
231 * \param dwChannel ControllerNumber << 8 | ChannelNumber.
232 * \param fCallback Pointer to callback function.
233 * \param pArg Pointer to optional argument for callback.
234 */
235eXdmadRC XDMAD_SetCallback(sXdmad *pXdmad,
236                                                        uint32_t dwChannel,
237                                                        XdmadTransferCallback fCallback,
238                                                        void *pArg)
239{
240
241        uint8_t iChannel    = (dwChannel) & 0xFF;
242        assert(pXdmad != NULL);
243
244        if (iChannel >= pXdmad->numChannels) return XDMAD_ERROR;
245
246        if (pXdmad->XdmaChannels[iChannel].state == XDMAD_STATE_FREE)
247                return XDMAD_ERROR;
248        else if (pXdmad->XdmaChannels[iChannel].state == XDMAD_STATE_START)
249                return XDMAD_BUSY;
250
251        pXdmad->XdmaChannels[iChannel].fCallback = fCallback;
252        pXdmad->XdmaChannels[iChannel].pArg = pArg;
253
254        return XDMAD_OK;
255}
256
257
258/**
259 * \brief Enable clock of the xDMA peripheral, Enable the dma peripheral,
260 * configure configuration register for xDMA transfer.
261 * \param pXdmad     Pointer to xDMA driver instance.
262 * \param dwChannel ControllerNumber << 8 | ChannelNumber.
263 * \param dwCfg     Configuration value.
264 */
265eXdmadRC XDMAD_PrepareChannel(sXdmad *pXdmad, uint32_t dwChannel)
266{
267
268        uint8_t iChannel    = (dwChannel) & 0xFF;
269        Xdmac *pXdmac = pXdmad->pXdmacs;
270
271        assert(pXdmad != NULL);
272
273        if (iChannel >= pXdmad->numChannels) return XDMAD_ERROR;
274
275        if (pXdmad->XdmaChannels[iChannel].state == XDMAD_STATE_FREE)
276                return XDMAD_ERROR;
277        else if ((pXdmad->XdmaChannels[iChannel].state == XDMAD_STATE_START)
278                          || (pXdmad->XdmaChannels[iChannel].state == XDMAD_STATE_IN_XFR))
279                return XDMAD_BUSY;
280
281
282        /* Enable clock of the DMA peripheral */
283        if (!PMC_IsPeriphEnabled(ID_XDMAC))
284                PMC_EnablePeripheral(ID_XDMAC);
285
286        /* Clear dummy status */
287        XDMAC_GetChannelIsr(pXdmac, iChannel);
288        /* Disables XDMAC interrupt for the given channel. */
289        XDMAC_DisableGIt (pXdmac, iChannel);
290        XDMAC_DisableChannelIt (pXdmac, iChannel, 0xFF);
291        /* Disable the given dma channel. */
292        XDMAC_DisableChannel(pXdmac, iChannel);
293        XDMAC_SetSourceAddr(pXdmac, iChannel, 0);
294        XDMAC_SetDestinationAddr(pXdmac, iChannel, 0);
295        XDMAC_SetBlockControl(pXdmac, iChannel, 0);
296        XDMAC_SetChannelConfig(pXdmac, iChannel, 0x20);
297        XDMAC_SetDescriptorAddr(pXdmac, iChannel, 0, 0);
298        XDMAC_SetDescriptorControl(pXdmac, iChannel, 0);
299        return XDMAD_OK;
300}
301
302/**
303 * \brief xDMA interrupt handler
304 * \param arg Pointer to DMA driver instance.
305 */
306static void XDMAD_Handler(void *arg)
307{
308        sXdmad *pDmad;
309        Xdmac *pXdmac;
310        uint32_t xdmaGlobaIntStatus;
311
312        pDmad = arg;
313        pXdmac = pDmad->pXdmacs;
314        xdmaGlobaIntStatus = XDMAC_GetGIsr(pXdmac) & 0xFFFFFF;
315
316        while (xdmaGlobaIntStatus != 0) {
317                uint8_t _iChannel;
318                uint32_t xdmaChannelIntStatus;
319                sXdmadChannel *pCh;
320
321                _iChannel = 31 - __builtin_clz(xdmaGlobaIntStatus);
322                xdmaChannelIntStatus = XDMAC_GetChannelIsr(pXdmac, _iChannel);
323                pCh = &pDmad->XdmaChannels[_iChannel];
324
325                xdmaGlobaIntStatus &= ~(UINT32_C(1) << _iChannel);
326
327                /* Execute callback */
328                pCh->fCallback(_iChannel, pCh->pArg, xdmaChannelIntStatus);
329        }
330}
331
332
333/**
334 * \brief Configure DMA for a single transfer.
335 * \param pXdmad     Pointer to xDMA driver instance.
336 * \param dwChannel ControllerNumber << 8 | ChannelNumber.
337 */
338eXdmadRC XDMAD_ConfigureTransfer(sXdmad *pXdmad,
339                                                                  uint32_t dwChannel,
340                                                                  sXdmadCfg *pXdmaParam,
341                                                                  uint32_t dwXdmaDescCfg,
342                                                                  uint32_t dwXdmaDescAddr,
343                                                                  uint32_t dwXdmaIntEn)
344{
345        uint8_t iChannel    = (dwChannel) & 0xFF;
346
347        assert(pXdmad != NULL);
348
349        if (iChannel >= pXdmad->numChannels)
350                return XDMAD_ERROR;
351
352        Xdmac *pXdmac = pXdmad->pXdmacs;
353        XDMAC_GetChannelIsr(pXdmac, iChannel);
354
355        if (pXdmad->XdmaChannels[iChannel].state == XDMAD_STATE_FREE)
356                return XDMAD_ERROR;
357
358        if (pXdmad->XdmaChannels[iChannel].state == XDMAD_STATE_START)
359                return XDMAD_BUSY;
360
361        /* Linked List is enabled */
362        if ((dwXdmaDescCfg & XDMAC_CNDC_NDE) == XDMAC_CNDC_NDE_DSCR_FETCH_EN) {
363                if ((dwXdmaDescCfg & XDMAC_CNDC_NDVIEW_Msk) == XDMAC_CNDC_NDVIEW_NDV0) {
364                        XDMAC_SetChannelConfig(pXdmac, iChannel, pXdmaParam->mbr_cfg);
365                        XDMAC_SetSourceAddr(pXdmac, iChannel, pXdmaParam->mbr_sa);
366                        XDMAC_SetDestinationAddr(pXdmac, iChannel, pXdmaParam->mbr_da);
367                }
368
369                if ((dwXdmaDescCfg & XDMAC_CNDC_NDVIEW_Msk) == XDMAC_CNDC_NDVIEW_NDV1)
370                        XDMAC_SetChannelConfig(pXdmac, iChannel, pXdmaParam->mbr_cfg);
371
372                XDMAC_SetDescriptorAddr(pXdmac, iChannel, dwXdmaDescAddr, 0);
373                XDMAC_SetDescriptorControl(pXdmac, iChannel, dwXdmaDescCfg);
374                XDMAC_DisableChannelIt (pXdmac, iChannel, 0xFF);
375                XDMAC_EnableChannelIt (pXdmac, iChannel, dwXdmaIntEn);
376        } else {
377                /* LLI is disabled. */
378                XDMAC_SetSourceAddr(pXdmac, iChannel, pXdmaParam->mbr_sa);
379                XDMAC_SetDestinationAddr(pXdmac, iChannel, pXdmaParam->mbr_da);
380                XDMAC_SetMicroblockControl(pXdmac, iChannel, pXdmaParam->mbr_ubc);
381                XDMAC_SetBlockControl(pXdmac, iChannel, pXdmaParam->mbr_bc);
382                XDMAC_SetDataStride_MemPattern(pXdmac, iChannel, pXdmaParam->mbr_ds);
383                XDMAC_SetSourceMicroBlockStride(pXdmac, iChannel, pXdmaParam->mbr_sus);
384                XDMAC_SetDestinationMicroBlockStride(pXdmac, iChannel, pXdmaParam->mbr_dus);
385                XDMAC_SetChannelConfig(pXdmac, iChannel, pXdmaParam->mbr_cfg);
386                XDMAC_SetDescriptorAddr(pXdmac, iChannel, 0, 0);
387                XDMAC_SetDescriptorControl(pXdmac, iChannel, 0);
388                XDMAC_EnableChannelIt (pXdmac, iChannel, dwXdmaIntEn);
389        }
390
391        return XDMAD_OK;
392}
393
394/**
395 * \brief Start xDMA transfer.
396 * \param pXdmad     Pointer to XDMA driver instance.
397 * \param dwChannel ControllerNumber << 8 | ChannelNumber.
398 */
399eXdmadRC XDMAD_StartTransfer(sXdmad *pXdmad, uint32_t dwChannel)
400{
401        uint8_t iChannel    = (dwChannel) & 0xFF;
402
403        assert(pXdmad != NULL);
404
405        if (iChannel >= pXdmad->numChannels) return XDMAD_ERROR;
406
407        Xdmac *pXdmac = pXdmad->pXdmacs;
408
409        if (pXdmad->XdmaChannels[iChannel].state == XDMAD_STATE_FREE) {
410                TRACE_ERROR("%s:: XDMAD_STATE_FREE \n\r", __FUNCTION__);
411                return XDMAD_ERROR;
412        } else if (pXdmad->XdmaChannels[iChannel].state == XDMAD_STATE_START) {
413                TRACE_ERROR("%s:: XDMAD_STATE_START \n\r", __FUNCTION__)
414                return XDMAD_BUSY;
415        }
416
417        /* Change state to transferring */
418        pXdmad->XdmaChannels[iChannel].state = XDMAD_STATE_START;
419        XDMAC_EnableChannel(pXdmac, iChannel);
420        XDMAC_EnableGIt(pXdmac, iChannel);
421
422        return XDMAD_OK;
423}
424
425
426/**
427 * \brief Stop DMA transfer.
428 * \param pDmad     Pointer to DMA driver instance.
429 * \param dwChannel ControllerNumber << 8 | ChannelNumber.
430 */
431eXdmadRC XDMAD_StopTransfer(sXdmad *pXdmad, uint32_t dwChannel)
432{
433        uint8_t _iChannel    = (dwChannel) & 0xFF;
434        assert(pXdmad != NULL);
435
436        if (_iChannel >= pXdmad->numChannels) return XDMAD_ERROR;
437
438        Xdmac *pXdmac = pXdmad->pXdmacs;
439
440        pXdmad->XdmaChannels[_iChannel].state = XDMAD_STATE_HALTED;
441        /* Disable channel */
442        XDMAC_DisableChannel(pXdmac, _iChannel);
443        /* Disable interrupts */
444        XDMAC_DisableChannelIt(pXdmac, _iChannel, 0xFF);
445        /* Clear pending status */
446        XDMAC_GetChannelIsr(pXdmac, _iChannel);
447        XDMAC_GetGlobalChStatus(pXdmac);
448        return XDMAD_OK;
449}
450
451/**@}*/
452
Note: See TracBrowser for help on using the repository browser.