1 /* ----------------------------------------------------------------------------
\r
2 * SAM Software Package License
\r
3 * ----------------------------------------------------------------------------
\r
4 * Copyright (c) 2014, Atmel Corporation
\r
6 * All rights reserved.
\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
11 * - Redistributions of source code must retain the above copyright notice,
\r
12 * this list of conditions and the disclaimer below.
\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
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
30 /** \addtogroup xdmad_module
\r
32 * \section Xdma xDma Configuration Usage
\r
34 * To configure a XDMA channel, the user has to follow these few steps :
\r
36 * <li> Initialize a XDMA driver instance by XDMAD_Initialize().</li>
\r
37 * <li> choose an available (disabled) channel using XDMAD_AllocateChannel().</li>
\r
38 * <li> After the XDMAC selected channel has been programmed, XDMAD_PrepareChannel() is to enable
\r
39 * clock and dma peripheral of the DMA, and set Configuration register to set up the transfer type
\r
40 * (memory or non-memory peripheral for source and destination) and flow control device.</li>
\r
41 * <li> Invoke XDMAD_StartTransfer() to start DMA transfer or XDMAD_StopTransfer() to force stop DMA transfer.</li>
\r
42 * <li> Once the buffer of data is transferred, XDMAD_IsTransferDone() checks if DMA transfer is finished.</li>
\r
43 * <li> XDMAD_Handler() handles XDMA interrupt, and invoking XDMAD_SetCallback() if provided.</li>
\r
53 /** \addtogroup dmad_functions
\r
56 /*----------------------------------------------------------------------------
\r
58 *----------------------------------------------------------------------------*/
\r
62 static uint8_t xDmad_Initialized = 0;
\r
64 /*----------------------------------------------------------------------------
\r
66 *----------------------------------------------------------------------------*/
\r
68 * \brief Try to allocate a DMA channel for on given controller.
\r
69 * \param pDmad Pointer to DMA driver instance.
\r
70 * \param bSrcID Source peripheral ID, 0xFF for memory.
\r
71 * \param bDstID Destination peripheral ID, 0xFF for memory.
\r
72 * \return Channel number if allocation sucessful, return
\r
73 * DMAD_ALLOC_FAILED if allocation failed.
\r
75 static uint32_t XDMAD_AllocateXdmacChannel( sXdmad *pXdmad,
\r
80 /* Can't support peripheral to peripheral */
\r
81 if ((( bSrcID != XDMAD_TRANSFER_MEMORY ) && ( bDstID != XDMAD_TRANSFER_MEMORY )))
\r
83 return XDMAD_ALLOC_FAILED;
\r
85 /* dma transfer from peripheral to memory */
\r
86 if ( bDstID == XDMAD_TRANSFER_MEMORY)
\r
88 if( (!XDMAIF_IsValidatedPeripherOnDma(bSrcID)) )
\r
90 return XDMAD_ALLOC_FAILED;
\r
93 /* dma transfer from memory to peripheral */
\r
94 if ( bSrcID == XDMAD_TRANSFER_MEMORY )
\r
96 if( (!XDMAIF_IsValidatedPeripherOnDma(bDstID)) )
\r
98 return XDMAD_ALLOC_FAILED;
\r
102 for (i = 0; i < pXdmad->numChannels; i ++)
\r
104 if (( pXdmad->XdmaChannels[i].state == XDMAD_STATE_FREE ) || ( pXdmad->XdmaChannels[i].state == XDMAD_STATE_DONE ))
\r
106 /* Allocate the channel */
\r
107 pXdmad->XdmaChannels[i].state = XDMAD_STATE_ALLOCATED;
\r
108 /* Get general informations */
\r
109 pXdmad->XdmaChannels[i].bSrcPeriphID = bSrcID;
\r
110 pXdmad->XdmaChannels[i].bDstPeriphID = bDstID;
\r
111 pXdmad->XdmaChannels[i].bSrcTxIfID =
\r
112 XDMAIF_Get_ChannelNumber(bSrcID, 0);
\r
113 pXdmad->XdmaChannels[i].bSrcRxIfID =
\r
114 XDMAIF_Get_ChannelNumber(bSrcID, 1);
\r
115 pXdmad->XdmaChannels[i].bDstTxIfID =
\r
116 XDMAIF_Get_ChannelNumber(bDstID, 0);
\r
117 pXdmad->XdmaChannels[i].bDstRxIfID =
\r
118 XDMAIF_Get_ChannelNumber(bDstID, 1);
\r
119 return ((i) & 0xFF);
\r
122 return XDMAD_ALLOC_FAILED;
\r
125 /*----------------------------------------------------------------------------
\r
126 * Exported functions
\r
127 *----------------------------------------------------------------------------*/
\r
130 * \brief Initialize xDMA driver instance.
\r
131 * \param pXdmad Pointer to xDMA driver instance.
\r
132 * \param bPollingMode Polling DMA transfer:
\r
133 * 1. Via XDMAD_IsTransferDone(); or
\r
134 * 2. Via XDMAD_Handler().
\r
136 void XDMAD_Initialize( sXdmad *pXdmad, uint8_t bPollingMode )
\r
140 assert( pXdmad != NULL ) ;
\r
141 if (xDmad_Initialized) return;
\r
142 pXdmad->pXdmacs = XDMAC;
\r
143 pXdmad->pollingMode = bPollingMode;
\r
144 pXdmad->numControllers = XDMAC_CONTROLLER_NUM;
\r
145 pXdmad->numChannels = (XDMAC_GTYPE_NB_CH( XDMAC_GetType(XDMAC) ) + 1);
\r
147 for (j = 0; j < pXdmad->numChannels; j ++)
\r
149 pXdmad->XdmaChannels[j].fCallback = 0;
\r
150 pXdmad->XdmaChannels[j].pArg = 0;
\r
151 pXdmad->XdmaChannels[j].bIrqOwner = 0;
\r
152 pXdmad->XdmaChannels[j].bSrcPeriphID = 0;
\r
153 pXdmad->XdmaChannels[j].bDstPeriphID = 0;
\r
154 pXdmad->XdmaChannels[j].bSrcTxIfID = 0;
\r
155 pXdmad->XdmaChannels[j].bSrcRxIfID = 0;
\r
156 pXdmad->XdmaChannels[j].bDstTxIfID = 0;
\r
157 pXdmad->XdmaChannels[j].bDstRxIfID = 0;
\r
158 pXdmad->XdmaChannels[j].state = XDMAD_STATE_FREE;
\r
160 xDmad_Initialized = 1;
\r
165 * \brief Allocate a XDMA channel for upper layer.
\r
166 * \param pXdmad Pointer to xDMA driver instance.
\r
167 * \param bSrcID Source peripheral ID, 0xFF for memory.
\r
168 * \param bDstID Destination peripheral ID, 0xFF for memory.
\r
169 * \return Channel number if allocation sucessful, return
\r
170 * XDMAD_ALLOC_FAILED if allocation failed.
\r
172 uint32_t XDMAD_AllocateChannel( sXdmad *pXdmad,
\r
176 uint32_t dwChannel = XDMAD_ALLOC_FAILED;
\r
178 dwChannel = XDMAD_AllocateXdmacChannel( pXdmad, bSrcID, bDstID );
\r
184 * \brief Free the specified xDMA channel.
\r
185 * \param pXdmad Pointer to xDMA driver instance.
\r
186 * \param dwChannel ControllerNumber << 8 | ChannelNumber.
\r
188 eXdmadRC XDMAD_FreeChannel( sXdmad *pXdmad,
\r
189 uint32_t dwChannel )
\r
192 uint8_t iChannel = (dwChannel) & 0xFF;
\r
194 assert( pXdmad != NULL ) ;
\r
195 switch ( pXdmad->XdmaChannels[iChannel].state )
\r
197 case XDMAD_STATE_START:
\r
198 case XDMAD_STATE_ALLOCATED:
\r
200 case XDMAD_STATE_DONE:
\r
201 pXdmad->XdmaChannels[iChannel].state = XDMAD_STATE_FREE;
\r
209 * \brief Set the callback function for xDMA channel transfer.
\r
210 * \param pXdmad Pointer to xDMA driver instance.
\r
211 * \param dwChannel ControllerNumber << 8 | ChannelNumber.
\r
212 * \param fCallback Pointer to callback function.
\r
213 * \param pArg Pointer to optional argument for callback.
\r
215 eXdmadRC XDMAD_SetCallback( sXdmad *pXdmad,
\r
216 uint32_t dwChannel,
\r
217 XdmadTransferCallback fCallback,
\r
221 uint8_t iChannel = (dwChannel) & 0xFF;
\r
222 assert( pXdmad != NULL ) ;
\r
223 if ( pXdmad->XdmaChannels[iChannel].state == XDMAD_STATE_FREE )
\r
224 return XDMAD_ERROR;
\r
225 else if ( pXdmad->XdmaChannels[iChannel].state == XDMAD_STATE_START )
\r
228 pXdmad->XdmaChannels[iChannel].fCallback = fCallback;
\r
229 pXdmad->XdmaChannels[iChannel].pArg = pArg;
\r
236 * \brief Enable clock of the xDMA peripheral, Enable the dma peripheral,
\r
237 * configure configuration register for xDMA transfer.
\r
238 * \param pXdmad Pointer to xDMA driver instance.
\r
239 * \param dwChannel ControllerNumber << 8 | ChannelNumber.
\r
240 * \param dwCfg Configuration value.
\r
242 eXdmadRC XDMAD_PrepareChannel( sXdmad *pXdmad, uint32_t dwChannel)
\r
245 uint8_t iChannel = (dwChannel) & 0xFF;
\r
248 assert( pXdmad != NULL ) ;
\r
249 Xdmac *pXdmac = pXdmad->pXdmacs;
\r
251 if ( pXdmad->XdmaChannels[iChannel].state == XDMAD_STATE_FREE )
\r
252 return XDMAD_ERROR;
\r
253 else if ( pXdmad->XdmaChannels[iChannel].state == XDMAD_STATE_START )
\r
255 /* Clear dummy status */
\r
256 XDMAC_GetGlobalChStatus( pXdmac );
\r
257 XDMAC_GetGIsr (pXdmac);
\r
259 /* Enable clock of the DMA peripheral */
\r
260 if (!PMC_IsPeriphEnabled( ID_XDMAC ))
\r
262 PMC_EnablePeripheral( ID_XDMAC );
\r
265 /* Clear dummy status */
\r
266 XDMAC_GetChannelIsr( pXdmac,iChannel );
\r
267 /* Disables XDMAC interrupt for the given channel. */
\r
268 XDMAC_DisableGIt (pXdmac, (uint32_t)-1);
\r
269 XDMAC_DisableChannelIt (pXdmac, iChannel, (uint32_t)-1);
\r
270 /* Disable the given dma channel. */
\r
271 XDMAC_DisableChannel( pXdmac, iChannel );
\r
272 XDMAC_SetSourceAddr(pXdmac, iChannel, 0);
\r
273 XDMAC_SetDestinationAddr(pXdmac, iChannel, 0);
\r
274 XDMAC_SetBlockControl(pXdmac, iChannel, 0);
\r
275 XDMAC_SetChannelConfig( pXdmac, iChannel, 0x20);
\r
276 XDMAC_SetDescriptorAddr(pXdmac, iChannel, 0, 0);
\r
277 XDMAC_SetDescriptorControl(pXdmac, iChannel, 0);
\r
282 * \brief xDMA interrupt handler
\r
283 * \param pxDmad Pointer to DMA driver instance.
\r
285 void XDMAD_Handler( sXdmad *pDmad)
\r
288 sXdmadChannel *pCh;
\r
289 uint32_t xdmaChannelIntStatus, xdmaGlobaIntStatus,xdmaGlobalChStatus;
\r
292 assert( pDmad != NULL ) ;
\r
295 pXdmac = pDmad->pXdmacs;
\r
296 xdmaGlobaIntStatus = XDMAC_GetGIsr(pXdmac);
\r
297 if ((xdmaGlobaIntStatus & 0xFFFFFF) != 0)
\r
299 xdmaGlobalChStatus = XDMAC_GetGlobalChStatus(pXdmac);
\r
300 for (_iChannel = 0; _iChannel < pDmad->numChannels; _iChannel ++)
\r
302 if (!(xdmaGlobaIntStatus & (1<<_iChannel))) continue;
\r
303 pCh = &pDmad->XdmaChannels[_iChannel];
\r
304 if ( pCh->state == XDMAD_STATE_FREE) return ;
\r
305 if ((xdmaGlobalChStatus & ( XDMAC_GS_ST0 << _iChannel)) == 0)
\r
308 xdmaChannelIntStatus = XDMAC_GetChannelIsr( pXdmac, _iChannel);
\r
309 if (xdmaChannelIntStatus & XDMAC_CIS_BIS) {
\r
310 if((XDMAC_GetChannelItMask(pXdmac, _iChannel) & XDMAC_CIM_LIM) == 0 ) {
\r
311 pCh->state = XDMAD_STATE_DONE ;
\r
314 //printf("XDMAC_CIS_BIS\n\r");
\r
316 if (xdmaChannelIntStatus & XDMAC_CIS_FIS) {
\r
317 // printf("XDMAC_CIS_FIS\n\r");
\r
319 if (xdmaChannelIntStatus & XDMAC_CIS_RBEIS) {
\r
320 //printf("XDMAC_CIS_RBEIS\n\r");
\r
322 if (xdmaChannelIntStatus & XDMAC_CIS_WBEIS) {
\r
323 // printf("XDMAC_CIS_WBEIS\n\r");
\r
325 if (xdmaChannelIntStatus & XDMAC_CIS_ROIS) {
\r
326 // printf("XDMAC_CIS_ROIS\n\r");
\r
328 if (xdmaChannelIntStatus & XDMAC_CIS_LIS) {
\r
329 //printf("XDMAC_CIS_LIS\n\r");
\r
331 pCh->state = XDMAD_STATE_DONE ;
\r
334 if (xdmaChannelIntStatus & XDMAC_CIS_DIS )
\r
336 pCh->state = XDMAD_STATE_DONE ;
\r
340 /* Execute callback */
\r
341 if (bExec && pCh->fCallback) {
\r
342 //XDMAC_DisableGIt( pXdmac,1 << _iChannel);
\r
343 pCh->fCallback(_iChannel, pCh->pArg);
\r
350 * \brief Check if DMA transfer is finished.
\r
351 * In polling mode XDMAD_Handler() is polled.
\r
352 * \param pDmad Pointer to DMA driver instance.
\r
353 * \param dwChannel ControllerNumber << 8 | ChannelNumber.
\r
355 eXdmadRC XDMAD_IsTransferDone( sXdmad *pXdmad, uint32_t dwChannel )
\r
357 uint8_t iChannel = (dwChannel) & 0xFF;
\r
359 state = pXdmad->XdmaChannels[iChannel].state;
\r
360 assert( pXdmad != NULL ) ;
\r
361 if ( state == XDMAD_STATE_ALLOCATED ) return XDMAD_OK;
\r
362 if ( state == XDMAD_STATE_FREE )
\r
363 return XDMAD_ERROR;
\r
364 else if ( state != XDMAD_STATE_DONE )
\r
366 if(pXdmad->pollingMode) XDMAD_Handler( pXdmad);
\r
374 * \brief Configure DMA for a single transfer.
\r
375 * \param pXdmad Pointer to xDMA driver instance.
\r
376 * \param dwChannel ControllerNumber << 8 | ChannelNumber.
\r
378 eXdmadRC XDMAD_ConfigureTransfer( sXdmad *pXdmad,
\r
379 uint32_t dwChannel,
\r
380 sXdmadCfg *pXdmaParam,
\r
381 uint32_t dwXdmaDescCfg,
\r
382 uint32_t dwXdmaDescAddr)
\r
385 uint8_t iChannel = (dwChannel) & 0xFF;
\r
386 Xdmac *pXdmac = pXdmad->pXdmacs;
\r
387 XDMAC_GetGIsr(pXdmac);
\r
388 XDMAC_GetChannelIsr( pXdmac, iChannel);
\r
389 if ( pXdmad->XdmaChannels[iChannel].state == XDMAD_STATE_FREE )
\r
390 return XDMAD_ERROR;
\r
391 if ( pXdmad->XdmaChannels[iChannel].state == XDMAD_STATE_START )
\r
393 /* Linked List is enabled */
\r
394 if ((dwXdmaDescCfg & XDMAC_CNDC_NDE) == XDMAC_CNDC_NDE_DSCR_FETCH_EN)
\r
396 if ((dwXdmaDescCfg & XDMAC_CNDC_NDVIEW_Msk) == XDMAC_CNDC_NDVIEW_NDV0) {
\r
397 XDMAC_SetChannelConfig( pXdmac, iChannel, pXdmaParam->mbr_cfg );
\r
398 XDMAC_SetSourceAddr(pXdmac, iChannel, pXdmaParam->mbr_sa);
\r
399 XDMAC_SetDestinationAddr(pXdmac, iChannel, pXdmaParam->mbr_da);
\r
401 if ((dwXdmaDescCfg & XDMAC_CNDC_NDVIEW_Msk) == XDMAC_CNDC_NDVIEW_NDV1) {
\r
402 XDMAC_SetChannelConfig( pXdmac, iChannel, pXdmaParam->mbr_cfg );
\r
404 XDMAC_SetDescriptorAddr(pXdmac, iChannel, dwXdmaDescAddr, 0);
\r
405 XDMAC_SetDescriptorControl(pXdmac, iChannel, dwXdmaDescCfg);
\r
406 XDMAC_DisableChannelIt (pXdmac, iChannel, (uint32_t)-1);
\r
407 XDMAC_EnableChannelIt (pXdmac,iChannel, XDMAC_CIE_LIE );
\r
409 /* LLI is disabled. */
\r
412 XDMAC_SetSourceAddr(pXdmac, iChannel, pXdmaParam->mbr_sa);
\r
413 XDMAC_SetDestinationAddr(pXdmac, iChannel, pXdmaParam->mbr_da);
\r
414 XDMAC_SetMicroblockControl(pXdmac, iChannel, pXdmaParam->mbr_ubc);
\r
415 XDMAC_SetBlockControl(pXdmac, iChannel, pXdmaParam->mbr_bc);
\r
416 XDMAC_SetDataStride_MemPattern(pXdmac, iChannel, pXdmaParam->mbr_ds);
\r
417 XDMAC_SetSourceMicroBlockStride(pXdmac, iChannel, pXdmaParam->mbr_sus);
\r
418 XDMAC_SetDestinationMicroBlockStride(pXdmac, iChannel, pXdmaParam->mbr_dus);
\r
419 XDMAC_SetChannelConfig( pXdmac, iChannel, pXdmaParam->mbr_cfg );
\r
420 XDMAC_SetDescriptorAddr(pXdmac, iChannel, 0, 0);
\r
421 XDMAC_SetDescriptorControl(pXdmac, iChannel, 0);
\r
422 XDMAC_EnableChannelIt (pXdmac,
\r
435 * \brief Start xDMA transfer.
\r
436 * \param pXdmad Pointer to XDMA driver instance.
\r
437 * \param dwChannel ControllerNumber << 8 | ChannelNumber.
\r
439 eXdmadRC XDMAD_StartTransfer( sXdmad *pXdmad, uint32_t dwChannel )
\r
442 uint8_t iChannel = (dwChannel) & 0xFF;
\r
443 Xdmac *pXdmac = pXdmad->pXdmacs;
\r
444 if ( pXdmad->XdmaChannels[iChannel].state == XDMAD_STATE_FREE )
\r
446 printf("-E- XDMAD_STATE_FREE \n\r");
\r
447 return XDMAD_ERROR;
\r
449 else if ( pXdmad->XdmaChannels[iChannel].state == XDMAD_STATE_START )
\r
451 printf("-E- XDMAD_STATE_START \n\r");
\r
454 /* Change state to transferring */
\r
455 pXdmad->XdmaChannels[iChannel].state = XDMAD_STATE_START;
\r
456 XDMAC_EnableChannel(pXdmac, iChannel);
\r
457 if ( pXdmad->pollingMode == 0 )
\r
459 XDMAC_EnableGIt( pXdmac,1 << iChannel);
\r
466 * \brief Stop DMA transfer.
\r
467 * \param pDmad Pointer to DMA driver instance.
\r
468 * \param dwChannel ControllerNumber << 8 | ChannelNumber.
\r
470 eXdmadRC XDMAD_StopTransfer( sXdmad *pXdmad, uint32_t dwChannel )
\r
472 uint8_t _iChannel = (dwChannel) & 0xFF;
\r
473 Xdmac *pXdmac = pXdmad->pXdmacs;
\r
475 pXdmad->XdmaChannels[_iChannel].state = XDMAD_STATE_ALLOCATED;
\r
476 /* Disable channel */
\r
477 XDMAC_DisableChannel(pXdmac, _iChannel);
\r
478 /* Disable interrupts */
\r
479 XDMAC_DisableChannelIt(pXdmac, _iChannel, (uint32_t)-1);
\r
480 /* Clear pending status */
\r
481 XDMAC_GetChannelIsr( pXdmac, _iChannel);
\r
482 XDMAC_GetGlobalChStatus(pXdmac);
\r