SAMV71 Xplained Ultra Software Package 1.5

spi_dma.c

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