SAMV71 Xplained Ultra Software Package 1.4

spid_dma.c

00001 /* ----------------------------------------------------------------------------
00002  *         SAM Software Package License
00003  * ----------------------------------------------------------------------------
00004  * Copyright (c) 2014, 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 //         Headers
00032 //------------------------------------------------------------------------------
00033 
00034 #include "spid.h"
00035 #include "board.h"
00036 #include <dma/dma.h>
00037 #include <drivers/dmad/dmad.h>
00038 #include <irq/irq.h>
00039 
00040 //------------------------------------------------------------------------------
00041 //         Defines
00042 //------------------------------------------------------------------------------
00043 
00044 /// DMA Link List size
00045 #define SIZE_LL     2
00046 
00047 /// DMA Width BYTE
00048 #define DMA_WIDTH   0
00049 
00050 //------------------------------------------------------------------------------
00051 //         Macros
00052 //------------------------------------------------------------------------------
00053 
00054 /// Write PMC register
00055 #define WRITE_PMC(pPmc, regName, value) pPmc->regName = (value)
00056 
00057 /// Write SPI register
00058 #define WRITE_SPI(pSpi, regName, value) pSpi->regName = (value)
00059 
00060 /// Read SPI registers
00061 #define READ_SPI(pSpi, regName) (pSpi->regName)
00062 
00063 /// Enable Peripheral
00064 #define PERIPH_ENABLE(id) \
00065     WRITE_PMC(AT91C_BASE_PMC, PMC_PCER, (1 << (id)))
00066 /// Disable Peripheral
00067 #define PERIPH_DISABLE(id) \
00068     WRITE_PMC(AT91C_BASE_PMC, PMC_PCDR, (1 << (id)))
00069 
00070 
00071 //------------------------------------------------------------------------------
00072 //         Local Variables
00073 //------------------------------------------------------------------------------
00074 
00075 /// Linked lists for multi transfer buffer chaining structure instance.
00076 static DmaLinkList dmaTxLinkList[SIZE_LL];
00077 static DmaLinkList dmaRxLinkList[SIZE_LL];
00078 
00079 //------------------------------------------------------------------------------
00080 //         Local functions
00081 //------------------------------------------------------------------------------
00082 
00083 //------------------------------------------------------------------------------
00084 /// Configure the DMA Channels: 0 RX, 1 TX.
00085 /// Channels are disabled after configure.
00086 //------------------------------------------------------------------------------
00087 static void configureDmaChannels(void)
00088 {
00089     // Enable DMA Peripheral
00090     PERIPH_ENABLE(AT91C_ID_HDMA);
00091     // Enable DMA
00092     DMA_Enable();
00093 
00094     // Free status
00095     DMA_DisableIt(0xFFFFFFFF);
00096     DMA_GetChannelStatus();
00097     DMA_GetStatus();
00098     DMA_DisableChannels((1 << DMA_CHANNEL_0) | (1 << DMA_CHANNEL_1));
00099     // RX channel 0
00100     DMA_SetConfiguration(DMA_CHANNEL_0,
00101                           AT91C_HDMA_SRC_PER_2
00102                         | AT91C_HDMA_DST_PER_2
00103                         | AT91C_HDMA_SRC_H2SEL_HW
00104                         | AT91C_HDMA_DST_H2SEL_SW
00105                         | AT91C_HDMA_SOD_ENABLE
00106                         | AT91C_HDMA_FIFOCFG_LARGESTBURST
00107                         );
00108 
00109     // TX channel 1
00110     DMA_SetConfiguration(DMA_CHANNEL_1,
00111                           AT91C_HDMA_SRC_PER_1
00112                         | AT91C_HDMA_DST_PER_1
00113                         | AT91C_HDMA_SRC_H2SEL_SW
00114                         | AT91C_HDMA_DST_H2SEL_HW
00115                         | AT91C_HDMA_SOD_ENABLE
00116                         | AT91C_HDMA_FIFOCFG_LARGESTBURST
00117                         );
00118 }
00119 
00120 //------------------------------------------------------------------------------
00121 /// Configure the DMA source and destination with Linker List mode.
00122 /// \param pCommand Pointer to command
00123 //------------------------------------------------------------------------------
00124 static void configureLinkList(AT91S_SPI *pSpiHw,
00125                               SpidCmd *pCommand)
00126 {
00127     // Setup RX Link List
00128     dmaRxLinkList[0].sourceAddress = (unsigned int)&pSpiHw->SPI_RDR;
00129     dmaRxLinkList[0].destAddress   = (unsigned int)pCommand->pCmd;
00130     dmaRxLinkList[0].controlA      = pCommand->cmdSize
00131                                    | AT91C_HDMA_SRC_WIDTH_BYTE
00132                                    | AT91C_HDMA_DST_WIDTH_BYTE
00133                                    ;
00134     dmaRxLinkList[0].controlB      = AT91C_HDMA_SIF_0
00135                                    | AT91C_HDMA_DIF_0
00136                                    | AT91C_HDMA_SRC_DSCR_FETCH_FROM_MEM
00137                                    | AT91C_HDMA_DST_DSCR_FETCH_FROM_MEM
00138                                    | AT91C_HDMA_FC_PER2MEM
00139                                    | AT91C_HDMA_SRC_ADDRESS_MODE_FIXED
00140                                    | AT91C_HDMA_DST_ADDRESS_MODE_INCR
00141                                    ;
00142     dmaTxLinkList[0].sourceAddress = (unsigned int)pCommand->pCmd;
00143     dmaTxLinkList[0].destAddress   = (unsigned int)&pSpiHw->SPI_TDR;
00144     dmaTxLinkList[0].controlA      = pCommand->cmdSize
00145                                    | AT91C_HDMA_SRC_WIDTH_BYTE
00146                                    | AT91C_HDMA_DST_WIDTH_BYTE
00147                                    ;
00148     dmaTxLinkList[0].controlB      = AT91C_HDMA_SIF_0
00149                                    | AT91C_HDMA_DIF_0
00150                                    | AT91C_HDMA_SRC_DSCR_FETCH_FROM_MEM
00151                                    | AT91C_HDMA_DST_DSCR_FETCH_FROM_MEM
00152                                    | AT91C_HDMA_FC_MEM2PER
00153                                    | AT91C_HDMA_SRC_ADDRESS_MODE_INCR
00154                                    | AT91C_HDMA_DST_ADDRESS_MODE_FIXED
00155                                    ;
00156     // Only command
00157     if (pCommand->pData == 0) {
00158 
00159         dmaRxLinkList[0].descriptor    = 0;
00160         dmaTxLinkList[0].descriptor    = 0;
00161     }
00162     // Command & Data
00163     else {
00164 
00165         dmaRxLinkList[0].descriptor    = (unsigned int)&dmaRxLinkList[1];
00166         dmaRxLinkList[1].sourceAddress = (unsigned int)&pSpiHw->SPI_RDR;
00167         dmaRxLinkList[1].destAddress   = (unsigned int)pCommand->pData;
00168         dmaRxLinkList[1].controlA      = pCommand->dataSize
00169                                        | AT91C_HDMA_SRC_WIDTH_BYTE
00170                                        | AT91C_HDMA_DST_WIDTH_BYTE
00171                                        ;
00172         dmaRxLinkList[1].controlB      = AT91C_HDMA_SIF_0
00173                                        | AT91C_HDMA_DIF_0
00174                                        | AT91C_HDMA_SRC_DSCR_FETCH_DISABLE
00175                                        | AT91C_HDMA_DST_DSCR_FETCH_DISABLE
00176                                        | AT91C_HDMA_FC_PER2MEM
00177                                        | AT91C_HDMA_SRC_ADDRESS_MODE_FIXED
00178                                        | AT91C_HDMA_DST_ADDRESS_MODE_INCR
00179                                        ;
00180         dmaRxLinkList[1].descriptor    = 0;
00181         
00182         dmaTxLinkList[0].descriptor    = (unsigned int)&dmaTxLinkList[1];
00183         dmaTxLinkList[1].sourceAddress = (unsigned int)pCommand->pData;
00184         dmaTxLinkList[1].destAddress   = (unsigned int)&pSpiHw->SPI_TDR;
00185         dmaTxLinkList[1].controlA      = pCommand->dataSize
00186                                        | AT91C_HDMA_SRC_WIDTH_BYTE
00187                                        | AT91C_HDMA_DST_WIDTH_BYTE
00188                                        ;
00189         dmaTxLinkList[1].controlB      = AT91C_HDMA_SIF_0
00190                                        | AT91C_HDMA_DIF_0
00191                                        | AT91C_HDMA_SRC_DSCR_FETCH_DISABLE
00192                                        | AT91C_HDMA_DST_DSCR_FETCH_DISABLE
00193                                        | AT91C_HDMA_FC_MEM2PER
00194                                        | AT91C_HDMA_SRC_ADDRESS_MODE_INCR
00195                                        | AT91C_HDMA_DST_ADDRESS_MODE_FIXED
00196                                        ;
00197         dmaTxLinkList[1].descriptor    = 0;
00198     }
00199     // Setup registers
00200     DMA_SetDescriptorAddr(DMA_CHANNEL_0, (unsigned int)&dmaRxLinkList[0]);
00201     DMA_SetDescriptorAddr(DMA_CHANNEL_1, (unsigned int)&dmaTxLinkList[0]);
00202     AT91C_BASE_HDMA->HDMA_CH[DMA_CHANNEL_0].HDMA_CTRLB = 0
00203                       | AT91C_HDMA_SRC_DSCR_FETCH_FROM_MEM
00204                       | AT91C_HDMA_DST_DSCR_FETCH_FROM_MEM
00205                       ;
00206     AT91C_BASE_HDMA->HDMA_CH[DMA_CHANNEL_1].HDMA_CTRLB = 0
00207                       | AT91C_HDMA_SRC_DSCR_FETCH_FROM_MEM
00208                       | AT91C_HDMA_DST_DSCR_FETCH_FROM_MEM
00209                       ;
00210 }
00211 
00212 //------------------------------------------------------------------------------
00213 //         Exported functions
00214 //------------------------------------------------------------------------------
00215 
00216 //------------------------------------------------------------------------------
00217 /// Initializes the Spid structure and the corresponding SPI & DMA hardware.
00218 /// The driver will uses DMA channel 0 for RX and DMA channel 1 for TX.
00219 /// The DMA channels are freed automatically when no SPI command processing.
00220 /// \param pSpid  Pointer to a Spid instance.
00221 /// \param pSpiHw  Associated SPI peripheral.
00222 /// \param spiId  SPI peripheral identifier.
00223 /// \return Always 0.
00224 //------------------------------------------------------------------------------
00225 unsigned char SPID_Configure(Spid *pSpid, AT91S_SPI *pSpiHw, unsigned char spiId)
00226 {
00227     // Initialize the SPI structure
00228     pSpid->pSpiHw = pSpiHw;
00229     pSpid->spiId  = spiId;
00230     pSpid->semaphore = 1;
00231     pSpid->pCurrentCommand = 0;
00232 
00233     // Enable the SPI Peripheral
00234     PERIPH_ENABLE(pSpid->spiId);
00235     
00236     // Execute a software reset of the SPI twice
00237     WRITE_SPI(pSpiHw, SPI_CR, AT91C_SPI_SWRST);
00238     WRITE_SPI(pSpiHw, SPI_CR, AT91C_SPI_SWRST);
00239 
00240     // Configure SPI in Master Mode with No CS selected !!!
00241     WRITE_SPI(pSpiHw, SPI_MR, AT91C_SPI_MSTR | AT91C_SPI_MODFDIS | AT91C_SPI_PCS);
00242      
00243     // Disable the PDC transfer    
00244     WRITE_SPI(pSpiHw, SPI_PTCR, AT91C_PDC_RXTDIS | AT91C_PDC_TXTDIS);
00245 
00246     // Disable the SPI TX & RX
00247     WRITE_SPI(pSpiHw, SPI_CR, AT91C_SPI_SPIDIS);
00248 
00249     // Disable the SPI Peripheral
00250     PERIPH_DISABLE(pSpid->spiId);
00251 
00252     return 0;
00253 }
00254 
00255 //------------------------------------------------------------------------------
00256 /// Configures the parameters for the device corresponding to the cs.
00257 /// \param pSpid  Pointer to a Spid instance.
00258 /// \param cs  number corresponding to the SPI chip select.
00259 /// \param csr  SPI_CSR value to setup.
00260 //------------------------------------------------------------------------------
00261 void SPID_ConfigureCS(Spid *pSpid, unsigned char cs, unsigned int csr)
00262 {
00263     AT91S_SPI *pSpiHw = pSpid->pSpiHw;
00264 
00265     // Enable the SPI Peripheral
00266     PERIPH_ENABLE(pSpid->spiId);
00267 
00268     // Write CS
00269     WRITE_SPI(pSpiHw, SPI_CSR[cs], csr);
00270 
00271     // Disable the SPI Peripheral
00272     PERIPH_DISABLE(pSpid->spiId);
00273 }
00274 
00275 //------------------------------------------------------------------------------
00276 /// Starts a SPI master transfer. This is a non blocking function. It will
00277 /// return as soon as the transfer is started.
00278 /// Returns 0 if the transfer has been started successfully; otherwise returns
00279 /// SPID_ERROR_LOCK is the driver is in use, or SPID_ERROR if the command is not
00280 /// valid.
00281 /// \param pSpid  Pointer to a Spid instance.
00282 /// \param pCommand Pointer to the SPI command to execute.
00283 //------------------------------------------------------------------------------
00284 unsigned char SPID_SendCommand(Spid *pSpid, SpidCmd *pCommand)
00285 {
00286     AT91S_SPI *pSpiHw = pSpid->pSpiHw;
00287     unsigned int spiMr;
00288          
00289     // Try to get the dataflash semaphore
00290     if (pSpid->semaphore == 0) {
00291     
00292          return SPID_ERROR_LOCK;
00293     }
00294      pSpid->semaphore--;
00295 
00296     // Enable the SPI Peripheral
00297     PERIPH_ENABLE(pSpid->spiId);
00298     
00299     // Disable PDC transmitter and receiver
00300     WRITE_SPI(pSpiHw, SPI_PTCR, AT91C_PDC_RXTDIS | AT91C_PDC_TXTDIS);
00301 
00302     // Write to the MR register
00303     spiMr = READ_SPI(pSpiHw, SPI_MR);
00304     spiMr |= AT91C_SPI_PCS;
00305     spiMr &= ~((1 << pCommand->spiCs) << 16);
00306     WRITE_SPI(pSpiHw, SPI_MR, spiMr);
00307 
00308     // Initialize DMA controller using channel 0 for RX, 1 for TX.
00309     configureDmaChannels();
00310     configureLinkList(pSpiHw, pCommand);
00311 
00312     // Initialize the callback
00313     pSpid->pCurrentCommand = pCommand;
00314     
00315     // Enable the SPI TX & RX
00316     WRITE_SPI(pSpiHw, SPI_CR, AT91C_SPI_SPIEN);
00317 
00318     // Start DMA 0(RX) && 1(TX)
00319     DMA_EnableChannels((1 << DMA_CHANNEL_0) | (1 << DMA_CHANNEL_1));
00320 
00321     // Enable DMA Interrupts
00322     DMA_EnableIt(  (DMA_CBTC << DMA_CHANNEL_0)
00323                  | (DMA_CBTC << DMA_CHANNEL_1));
00324 
00325     return 0;
00326 }
00327 
00328 //------------------------------------------------------------------------------
00329 /// SPI DMA transfer ISR, Handle RX complete
00330 //------------------------------------------------------------------------------
00331 void SPID_Handler(Spid *pSpid)
00332 {
00333     unsigned int dmaStatus;
00334     SpidCmd *pSpidCmd = pSpid->pCurrentCommand;
00335     AT91S_SPI *pSpiHw = pSpid->pSpiHw;
00336 
00337     dmaStatus = DMA_GetStatus();
00338 
00339     if ((dmaStatus & AT91C_CBTC) == 0)
00340         return;
00341 
00342     if ((dmaStatus & (DMA_CBTC << DMA_CHANNEL_0)) == 0)
00343         return;
00344 
00345     // Disable the SPI TX & RX
00346     WRITE_SPI(pSpiHw, SPI_CR, AT91C_SPI_SPIDIS);
00347     // Disable the SPI Peripheral
00348     PERIPH_DISABLE(pSpid->spiId);
00349 
00350     // Disable DMA
00351     DMA_Disable();
00352     // Disable DMA Peripheral
00353     PERIPH_DISABLE(AT91C_ID_HDMA);
00354     
00355     // Release the dataflash semaphore
00356     pSpid->semaphore++;
00357         
00358     // Invoke the callback associated with the current command
00359     if (pSpidCmd && pSpidCmd->callback) {
00360     
00361         pSpidCmd->callback(0, pSpidCmd->pArgument);
00362     }
00363         
00364 }
00365 
00366 //------------------------------------------------------------------------------
00367 /// Returns 1 if the SPI driver is currently busy executing a command; otherwise
00368 /// returns 0.
00369 /// \param pSpid  Pointer to a SPI driver instance.
00370 //------------------------------------------------------------------------------
00371 unsigned char SPID_IsBusy(const Spid *pSpid)
00372 {
00373     if (pSpid->semaphore == 0) {
00374         return 1;
00375     } else {
00376 
00377         return 0;
00378     }
00379 }
00380 
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines