SAMV71 Xplained Ultra Software Package 1.3

spi_dma.c

Go to the documentation of this file.
00001 /* ----------------------------------------------------------------------------
00002  *         SAM Software Package License 
00003  * ----------------------------------------------------------------------------
00004  * Copyright (c) 2013, Atmel Corporation
00005  *
00006  * All rights reserved.
00007  *
00008  * Redistribution and use in source and binary forms, with or without
00009  * modification, are permitted provided that the following conditions are met:
00010  *
00011  * - Redistributions of source code must retain the above copyright notice,
00012  * this list of conditions and the disclaimer below.
00013  *
00014  * Atmel's name may not be used to endorse or promote products derived from
00015  * this software without specific prior written permission.
00016  *
00017  * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
00018  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
00019  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
00020  * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT,
00021  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
00022  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
00023  * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
00024  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
00025  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
00026  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00027  * ----------------------------------------------------------------------------
00028  */
00029 
00030 /**
00031  * \addtogroup spi_dma_module SPI xDMA driver
00032  * \ingroup lib_spiflash
00033  * \section Usage
00034  *
00035  * <ul>
00036  * <li> SPID_Configure() initializes and configures the SPI peripheral and xDMA 
00037  * for data transfer.</li>
00038  * <li> Configures the parameters for the device corresponding to the cs value 
00039  * by SPID_ConfigureCS(). </li>
00040  * <li> Starts a SPI master transfer. This is a non blocking function 
00041  * SPID_SendCommand(). It will
00042  * return as soon as the transfer is started..</li>
00043  * </ul>
00044  *
00045  */
00046 
00047 /**
00048  * \file
00049  *
00050  * Implementation for the SPI Flash with xDMA driver.
00051  *
00052  */
00053 
00054 
00055 /*----------------------------------------------------------------------------
00056  *        Headers
00057  *----------------------------------------------------------------------------*/
00058 
00059 #include "chip.h"
00060 
00061 /*----------------------------------------------------------------------------
00062  *        Definitions
00063  *----------------------------------------------------------------------------*/
00064 
00065 /** xDMA support */
00066 #define USE_SPI_DMA
00067 
00068 /** xDMA Link List size for SPI transmission*/
00069 #define DMA_SPI_LLI     2
00070 
00071 /*----------------------------------------------------------------------------
00072  *        Macros
00073  *----------------------------------------------------------------------------*/
00074 
00075 /*----------------------------------------------------------------------------
00076  *        Local Variables
00077  *----------------------------------------------------------------------------*/
00078 
00079 
00080 /*  DMA driver instance */
00081 static uint32_t spiDmaTxChannel;
00082 static uint32_t spiDmaRxChannel;
00083 
00084 /*----------------------------------------------------------------------------
00085  *        Local functions
00086  *----------------------------------------------------------------------------*/
00087 
00088 /**
00089  * \brief SPI xDMA Rx callback
00090  * Invoked on SPi DMA reception done.
00091  * \param channel DMA channel.
00092  * \param pArg Pointer to callback argument - Pointer to Spid instance.   
00093  */ 
00094 static void SPID_Rx_Cb(uint32_t channel, Spid* pArg)
00095 {
00096     SpidCmd *pSpidCmd = pArg->pCurrentCommand;
00097     Spi *pSpiHw = pArg->pSpiHw;
00098     if (channel != spiDmaRxChannel)
00099         return;
00100 
00101     /* Disable the SPI TX & RX */
00102     SPI_Disable ( pSpiHw );
00103     TRACE_INFO("SPI Rx DMA Callback has been called %d bytes received\n\r",
00104             pArg->pCurrentCommand->RxSize);
00105     /* Configure and enable interrupt on RC compare */    
00106     NVIC_ClearPendingIRQ(XDMAC_IRQn);
00107     NVIC_DisableIRQ(XDMAC_IRQn);
00108 
00109     /* Disable the SPI Peripheral */
00110     PMC_DisablePeripheral ( pArg->spiId );
00111 
00112     /* Release CS */
00113     SPI_ReleaseCS(pSpiHw);
00114 
00115     /* Release the DMA channels */
00116     XDMAD_FreeChannel(pArg->pXdmad, spiDmaRxChannel);
00117     XDMAD_FreeChannel(pArg->pXdmad, spiDmaTxChannel);
00118     SCB_CleanInvalidateDCache();
00119     /* Release the dataflash semaphore */
00120     pArg->semaphore++;
00121     
00122     printf(" %s\n\r",pArg->pCurrentCommand->pRxBuff);
00123 
00124     /* Invoke the callback associated with the current command */
00125     if (pSpidCmd && pSpidCmd->callback) {
00126         //printf("p %d", pArg->semaphore);
00127         pSpidCmd->callback(0, pSpidCmd->pArgument);
00128     }
00129 }
00130 
00131 /**
00132  * \brief Configure the DMA Channels: 0 RX, 1 TX.
00133  * Channels are disabled after configure.
00134  * \returns 0 if the dma channel configuration successfully; otherwise returns
00135  * SPID_ERROR_XXX.
00136  */
00137 static uint8_t _spid_configureDmaChannels( Spid* pSpid )
00138 {
00139     /* Driver initialize */
00140     XDMAD_Initialize(  pSpid->pXdmad, 0 );
00141 
00142     XDMAD_FreeChannel( pSpid->pXdmad, spiDmaTxChannel);
00143     XDMAD_FreeChannel( pSpid->pXdmad, spiDmaRxChannel);
00144 
00145     /* Allocate a DMA channel for SPI0/1 TX. */
00146     spiDmaTxChannel = XDMAD_AllocateChannel( pSpid->pXdmad, 
00147             XDMAD_TRANSFER_MEMORY, pSpid->spiId);
00148     if ( spiDmaTxChannel == XDMAD_ALLOC_FAILED ) {
00149         return SPID_ERROR;
00150     }
00151     /* Allocate a DMA channel for SPI0/1 RX. */
00152     spiDmaRxChannel = 
00153         XDMAD_AllocateChannel( pSpid->pXdmad, pSpid->spiId, XDMAD_TRANSFER_MEMORY);
00154     if ( spiDmaRxChannel == XDMAD_ALLOC_FAILED ) {
00155         return SPID_ERROR;
00156     }
00157     
00158     /* Setup callbacks for SPI0/1 RX */
00159     XDMAD_SetCallback(pSpid->pXdmad, spiDmaRxChannel, 
00160         (XdmadTransferCallback)SPID_Rx_Cb, pSpid);
00161     if (XDMAD_PrepareChannel( pSpid->pXdmad, spiDmaRxChannel ))
00162         return SPID_ERROR;
00163 
00164     /* Setup callbacks for SPI0/1 TX (ignored) */
00165     XDMAD_SetCallback(pSpid->pXdmad, spiDmaTxChannel, NULL, NULL);
00166     if ( XDMAD_PrepareChannel( pSpid->pXdmad, spiDmaTxChannel ))
00167         return SPID_ERROR;
00168     return 0;
00169 }
00170 
00171 /**
00172  * \brief Configure the DMA source and destination with Linker List mode.
00173  *
00174  * \param pCommand Pointer to command
00175  * \returns 0 if the dma multibuffer configuration successfully; otherwise 
00176  * returns SPID_ERROR_XXX.
00177  */
00178 static uint8_t _spid_configureLinkList(Spi *pSpiHw, void *pXdmad, SpidCmd *pCommand)
00179 {
00180     sXdmadCfg xdmadRxCfg,xdmadTxCfg;
00181     uint32_t xdmaCndc, xdmaInt;
00182     uint32_t spiId;
00183     if ((unsigned int)pSpiHw == (unsigned int)SPI0 ) spiId = ID_SPI0;
00184     if ((unsigned int)pSpiHw == (unsigned int)SPI1 ) spiId = ID_SPI1;
00185 
00186     /* Setup TX  */ 
00187 
00188     xdmadTxCfg.mbr_sa = (uint32_t)pCommand->pTxBuff;    
00189 
00190     xdmadTxCfg.mbr_da = (uint32_t)&pSpiHw->SPI_TDR;
00191 
00192     xdmadTxCfg.mbr_ubc =  XDMA_UBC_NVIEW_NDV0 |
00193         XDMA_UBC_NDE_FETCH_DIS|
00194         XDMA_UBC_NSEN_UPDATED | pCommand->TxSize;
00195 
00196     xdmadTxCfg.mbr_cfg = XDMAC_CC_TYPE_PER_TRAN |
00197         XDMAC_CC_MBSIZE_SINGLE |
00198         XDMAC_CC_DSYNC_MEM2PER |
00199         XDMAC_CC_CSIZE_CHK_1 |
00200         XDMAC_CC_DWIDTH_BYTE|
00201         XDMAC_CC_SIF_AHB_IF0 |
00202         XDMAC_CC_DIF_AHB_IF1 |
00203         XDMAC_CC_SAM_INCREMENTED_AM |
00204         XDMAC_CC_DAM_FIXED_AM |
00205         XDMAC_CC_PERID(XDMAIF_Get_ChannelNumber(  spiId, XDMAD_TRANSFER_TX ));
00206 
00207 
00208     xdmadTxCfg.mbr_bc = 0;
00209     xdmadTxCfg.mbr_sus = 0;
00210     xdmadTxCfg.mbr_dus =0;
00211 
00212     /* Setup RX Link List */
00213 
00214     xdmadRxCfg.mbr_ubc = XDMA_UBC_NVIEW_NDV0 |
00215         XDMA_UBC_NDE_FETCH_DIS|
00216         XDMA_UBC_NDEN_UPDATED | pCommand->RxSize;
00217 
00218     xdmadRxCfg.mbr_da = (uint32_t)pCommand->pRxBuff;
00219 
00220     xdmadRxCfg.mbr_sa = (uint32_t)&pSpiHw->SPI_RDR;
00221     xdmadRxCfg.mbr_cfg = XDMAC_CC_TYPE_PER_TRAN |
00222         XDMAC_CC_MBSIZE_SINGLE |
00223         XDMAC_CC_DSYNC_PER2MEM |
00224         XDMAC_CC_CSIZE_CHK_1 |
00225         XDMAC_CC_DWIDTH_BYTE|
00226         XDMAC_CC_SIF_AHB_IF1 |
00227         XDMAC_CC_DIF_AHB_IF0 |
00228         XDMAC_CC_SAM_FIXED_AM |
00229         XDMAC_CC_DAM_INCREMENTED_AM |
00230         XDMAC_CC_PERID(XDMAIF_Get_ChannelNumber(  spiId, XDMAD_TRANSFER_RX ));
00231 
00232 
00233     xdmadRxCfg.mbr_bc = 0;
00234     xdmadRxCfg.mbr_sus = 0;
00235     xdmadRxCfg.mbr_dus =0;
00236 
00237     xdmaCndc = 0;
00238 
00239     /* Put all interrupts on for non LLI list setup of DMA */
00240       xdmaInt =  (XDMAC_CIE_BIE   |
00241                    XDMAC_CIE_DIE   |
00242                    XDMAC_CIE_FIE   |
00243                    XDMAC_CIE_RBIE  |
00244                    XDMAC_CIE_WBIE  |
00245                    XDMAC_CIE_ROIE);
00246       
00247     if (XDMAD_ConfigureTransfer( pXdmad, spiDmaRxChannel, &xdmadRxCfg, xdmaCndc, 0, xdmaInt))
00248         return SPID_ERROR;
00249 
00250     if (XDMAD_ConfigureTransfer( pXdmad, spiDmaTxChannel, &xdmadTxCfg, xdmaCndc, 0, xdmaInt))
00251         return SPID_ERROR;
00252     return 0;
00253 }
00254 
00255 
00256 /*----------------------------------------------------------------------------
00257  *        Exported functions
00258  *----------------------------------------------------------------------------*/
00259 /**
00260  * \brief Initializes the Spid structure and the corresponding SPI & DMA hardware.
00261  * select value.
00262  * The driver will uses DMA channel 0 for RX and DMA channel 1 for TX.
00263  * The DMA channels are freed automatically when no SPI command processing.
00264  *
00265  * \param pSpid  Pointer to a Spid instance.
00266  * \param pSpiHw Associated SPI peripheral.
00267  * \param spiId  SPI peripheral identifier.
00268  * \param pDmad  Pointer to a Dmad instance. 
00269  */
00270 uint32_t SPID_Configure( Spid *pSpid ,
00271         Spi *pSpiHw , 
00272         uint8_t spiId,
00273         uint32_t spiMode,
00274         sXdmad *pXdmad )
00275 {
00276     /* Initialize the SPI structure */
00277     pSpid->pSpiHw = pSpiHw;
00278     pSpid->spiId  = spiId;
00279     pSpid->semaphore = 1;
00280     pSpid->pCurrentCommand = 0;
00281     pSpid->pXdmad = pXdmad;
00282 
00283     /* Enable the SPI Peripheral ,Execute a software reset of the SPI, 
00284         Configure SPI in Master Mode*/
00285     SPI_Configure ( pSpiHw, pSpid->spiId, spiMode );
00286 
00287     return 0;
00288 }
00289 
00290 /**
00291  * \brief Configures the parameters for the device corresponding to the cs value.
00292  *
00293  * \param pSpid  Pointer to a Spid instance.
00294  * \param cs number corresponding to the SPI chip select.
00295  * \param csr SPI_CSR value to setup.
00296  */
00297 void SPID_ConfigureCS( Spid *pSpid, 
00298         uint32_t dwCS, 
00299         uint32_t dwCsr)
00300 {
00301     Spi *pSpiHw = pSpid->pSpiHw;
00302 
00303     /* Enable the SPI Peripheral */
00304     PMC_EnablePeripheral (pSpid->spiId );
00305     /* Configure SPI Chip Select Register */
00306     SPI_ConfigureNPCS( pSpiHw, dwCS, dwCsr );
00307 
00308     /* Disable the SPI Peripheral */
00309     PMC_DisablePeripheral (pSpid->spiId );
00310 
00311 }
00312 
00313 /**
00314  * \brief Starts a SPI master transfer. This is a non blocking function. It will
00315  *  return as soon as the transfer is started.
00316  *
00317  * \param pSpid  Pointer to a Spid instance.
00318  * \param pCommand Pointer to the SPI command to execute.
00319  * \returns 0 if the transfer has been started successfully; otherwise returns
00320  * SPID_ERROR_LOCK is the driver is in use, or SPID_ERROR if the command is not
00321  * valid.
00322  */
00323 uint32_t SPID_SendCommand( Spid *pSpid, SpidCmd *pCommand)
00324 {
00325     Spi *pSpiHw = pSpid->pSpiHw;
00326 
00327     /* Try to get the dataflash semaphore */
00328     if (pSpid->semaphore == 0) {
00329         return SPID_ERROR_LOCK;
00330     }
00331     pSpid->semaphore--;
00332 
00333     /* Enable the SPI Peripheral */
00334     PMC_EnablePeripheral (pSpid->spiId );
00335 
00336     /* SPI chip select */
00337     SPI_ChipSelect (pSpiHw, 1 << pCommand->spiCs);
00338 
00339     // Initialize the callback
00340     pSpid->pCurrentCommand = pCommand;
00341 
00342     /* Initialize DMA controller using channel 0 for RX, 1 for TX. */
00343     if (_spid_configureDmaChannels(pSpid) )
00344         return SPID_ERROR_LOCK;
00345 
00346     /* Configure and enable interrupt on RC compare */    
00347     NVIC_ClearPendingIRQ(XDMAC_IRQn);
00348     NVIC_SetPriority( XDMAC_IRQn ,1);
00349     NVIC_EnableIRQ(XDMAC_IRQn);
00350 
00351 
00352     if (_spid_configureLinkList(pSpiHw, pSpid->pXdmad, pCommand))
00353         return SPID_ERROR_LOCK;
00354 
00355     /* Enables the SPI to transfer and receive data. */
00356     SPI_Enable (pSpiHw );
00357     SCB_CleanInvalidateDCache();
00358     /* Start DMA 0(RX) && 1(TX) */
00359     if (XDMAD_StartTransfer( pSpid->pXdmad, spiDmaRxChannel )) 
00360         return SPID_ERROR_LOCK;
00361     if (XDMAD_StartTransfer( pSpid->pXdmad, spiDmaTxChannel )) 
00362         return SPID_ERROR_LOCK;
00363 
00364     return 0;
00365 }
00366 
00367 /**
00368  * \brief Check if the SPI driver is busy.
00369  *
00370  * \param pSpid  Pointer to a Spid instance.
00371  * \returns 1 if the SPI driver is currently busy executing a command; otherwise
00372  */
00373 uint32_t SPID_IsBusy(const Spid *pSpid)
00374 {
00375     if (pSpid->semaphore == 0) {
00376         return 1;
00377     } else {
00378         return 0;
00379     }
00380 }
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines