SAMV71 Xplained Ultra Software Package 1.5

spid_dma.c

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 //         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 
00157     // Only command
00158     if (pCommand->pData == 0) {
00159 
00160         dmaRxLinkList[0].descriptor    = 0;
00161         dmaTxLinkList[0].descriptor    = 0;
00162     }
00163     // Command & Data
00164     else {
00165 
00166         dmaRxLinkList[0].descriptor    = (unsigned int)&dmaRxLinkList[1];
00167         dmaRxLinkList[1].sourceAddress = (unsigned int)&pSpiHw->SPI_RDR;
00168         dmaRxLinkList[1].destAddress   = (unsigned int)pCommand->pData;
00169         dmaRxLinkList[1].controlA      = pCommand->dataSize
00170                                          | AT91C_HDMA_SRC_WIDTH_BYTE
00171                                          | AT91C_HDMA_DST_WIDTH_BYTE
00172                                         ;
00173         dmaRxLinkList[1].controlB      = AT91C_HDMA_SIF_0
00174                                          | AT91C_HDMA_DIF_0
00175                                          | AT91C_HDMA_SRC_DSCR_FETCH_DISABLE
00176                                          | AT91C_HDMA_DST_DSCR_FETCH_DISABLE
00177                                          | AT91C_HDMA_FC_PER2MEM
00178                                          | AT91C_HDMA_SRC_ADDRESS_MODE_FIXED
00179                                          | AT91C_HDMA_DST_ADDRESS_MODE_INCR
00180                                         ;
00181         dmaRxLinkList[1].descriptor    = 0;
00182 
00183         dmaTxLinkList[0].descriptor    = (unsigned int)&dmaTxLinkList[1];
00184         dmaTxLinkList[1].sourceAddress = (unsigned int)pCommand->pData;
00185         dmaTxLinkList[1].destAddress   = (unsigned int)&pSpiHw->SPI_TDR;
00186         dmaTxLinkList[1].controlA      = pCommand->dataSize
00187                                          | AT91C_HDMA_SRC_WIDTH_BYTE
00188                                          | AT91C_HDMA_DST_WIDTH_BYTE
00189                                         ;
00190         dmaTxLinkList[1].controlB      = AT91C_HDMA_SIF_0
00191                                          | AT91C_HDMA_DIF_0
00192                                          | AT91C_HDMA_SRC_DSCR_FETCH_DISABLE
00193                                          | AT91C_HDMA_DST_DSCR_FETCH_DISABLE
00194                                          | AT91C_HDMA_FC_MEM2PER
00195                                          | AT91C_HDMA_SRC_ADDRESS_MODE_INCR
00196                                          | AT91C_HDMA_DST_ADDRESS_MODE_FIXED
00197                                         ;
00198         dmaTxLinkList[1].descriptor    = 0;
00199     }
00200 
00201     // Setup registers
00202     DMA_SetDescriptorAddr(DMA_CHANNEL_0, (unsigned int)&dmaRxLinkList[0]);
00203     DMA_SetDescriptorAddr(DMA_CHANNEL_1, (unsigned int)&dmaTxLinkList[0]);
00204     AT91C_BASE_HDMA->HDMA_CH[DMA_CHANNEL_0].HDMA_CTRLB = 0
00205             | AT91C_HDMA_SRC_DSCR_FETCH_FROM_MEM
00206             | AT91C_HDMA_DST_DSCR_FETCH_FROM_MEM
00207             ;
00208     AT91C_BASE_HDMA->HDMA_CH[DMA_CHANNEL_1].HDMA_CTRLB = 0
00209             | AT91C_HDMA_SRC_DSCR_FETCH_FROM_MEM
00210             | AT91C_HDMA_DST_DSCR_FETCH_FROM_MEM
00211             ;
00212 }
00213 
00214 //------------------------------------------------------------------------------
00215 //         Exported functions
00216 //------------------------------------------------------------------------------
00217 
00218 //------------------------------------------------------------------------------
00219 /// Initializes the Spid structure and the corresponding SPI & DMA hardware.
00220 /// The driver will uses DMA channel 0 for RX and DMA channel 1 for TX.
00221 /// The DMA channels are freed automatically when no SPI command processing.
00222 /// \param pSpid  Pointer to a Spid instance.
00223 /// \param pSpiHw  Associated SPI peripheral.
00224 /// \param spiId  SPI peripheral identifier.
00225 /// \return Always 0.
00226 //------------------------------------------------------------------------------
00227 unsigned char SPID_Configure(Spid *pSpid, AT91S_SPI *pSpiHw,
00228                              unsigned char spiId)
00229 {
00230     // Initialize the SPI structure
00231     pSpid->pSpiHw = pSpiHw;
00232     pSpid->spiId  = spiId;
00233     pSpid->semaphore = 1;
00234     pSpid->pCurrentCommand = 0;
00235 
00236     // Enable the SPI Peripheral
00237     PERIPH_ENABLE(pSpid->spiId);
00238 
00239     // Execute a software reset of the SPI twice
00240     WRITE_SPI(pSpiHw, SPI_CR, AT91C_SPI_SWRST);
00241     WRITE_SPI(pSpiHw, SPI_CR, AT91C_SPI_SWRST);
00242 
00243     // Configure SPI in Master Mode with No CS selected !!!
00244     WRITE_SPI(pSpiHw, SPI_MR, AT91C_SPI_MSTR | AT91C_SPI_MODFDIS | AT91C_SPI_PCS);
00245 
00246     // Disable the PDC transfer
00247     WRITE_SPI(pSpiHw, SPI_PTCR, AT91C_PDC_RXTDIS | AT91C_PDC_TXTDIS);
00248 
00249     // Disable the SPI TX & RX
00250     WRITE_SPI(pSpiHw, SPI_CR, AT91C_SPI_SPIDIS);
00251 
00252     // Disable the SPI Peripheral
00253     PERIPH_DISABLE(pSpid->spiId);
00254 
00255     return 0;
00256 }
00257 
00258 //------------------------------------------------------------------------------
00259 /// Configures the parameters for the device corresponding to the cs.
00260 /// \param pSpid  Pointer to a Spid instance.
00261 /// \param cs  number corresponding to the SPI chip select.
00262 /// \param csr  SPI_CSR value to setup.
00263 //------------------------------------------------------------------------------
00264 void SPID_ConfigureCS(Spid *pSpid, unsigned char cs, unsigned int csr)
00265 {
00266     AT91S_SPI *pSpiHw = pSpid->pSpiHw;
00267 
00268     // Enable the SPI Peripheral
00269     PERIPH_ENABLE(pSpid->spiId);
00270 
00271     // Write CS
00272     WRITE_SPI(pSpiHw, SPI_CSR[cs], csr);
00273 
00274     // Disable the SPI Peripheral
00275     PERIPH_DISABLE(pSpid->spiId);
00276 }
00277 
00278 //------------------------------------------------------------------------------
00279 /// Starts a SPI master transfer. This is a non blocking function. It will
00280 /// return as soon as the transfer is started.
00281 /// Returns 0 if the transfer has been started successfully; otherwise returns
00282 /// SPID_ERROR_LOCK is the driver is in use, or SPID_ERROR if the command is not
00283 /// valid.
00284 /// \param pSpid  Pointer to a Spid instance.
00285 /// \param pCommand Pointer to the SPI command to execute.
00286 //------------------------------------------------------------------------------
00287 unsigned char SPID_SendCommand(Spid *pSpid, SpidCmd *pCommand)
00288 {
00289     AT91S_SPI *pSpiHw = pSpid->pSpiHw;
00290     unsigned int spiMr;
00291 
00292     // Try to get the dataflash semaphore
00293     if (pSpid->semaphore == 0)
00294 
00295         return SPID_ERROR_LOCK;
00296 
00297     pSpid->semaphore--;
00298 
00299     // Enable the SPI Peripheral
00300     PERIPH_ENABLE(pSpid->spiId);
00301 
00302     // Disable PDC transmitter and receiver
00303     WRITE_SPI(pSpiHw, SPI_PTCR, AT91C_PDC_RXTDIS | AT91C_PDC_TXTDIS);
00304 
00305     // Write to the MR register
00306     spiMr = READ_SPI(pSpiHw, SPI_MR);
00307     spiMr |= AT91C_SPI_PCS;
00308     spiMr &= ~((1 << pCommand->spiCs) << 16);
00309     WRITE_SPI(pSpiHw, SPI_MR, spiMr);
00310 
00311     // Initialize DMA controller using channel 0 for RX, 1 for TX.
00312     configureDmaChannels();
00313     configureLinkList(pSpiHw, pCommand);
00314 
00315     // Initialize the callback
00316     pSpid->pCurrentCommand = pCommand;
00317 
00318     // Enable the SPI TX & RX
00319     WRITE_SPI(pSpiHw, SPI_CR, AT91C_SPI_SPIEN);
00320 
00321     // Start DMA 0(RX) && 1(TX)
00322     DMA_EnableChannels((1 << DMA_CHANNEL_0) | (1 << DMA_CHANNEL_1));
00323 
00324     // Enable DMA Interrupts
00325     DMA_EnableIt((DMA_CBTC << DMA_CHANNEL_0)
00326                    | (DMA_CBTC << DMA_CHANNEL_1));
00327 
00328     return 0;
00329 }
00330 
00331 //------------------------------------------------------------------------------
00332 /// SPI DMA transfer ISR, Handle RX complete
00333 //------------------------------------------------------------------------------
00334 void SPID_Handler(Spid *pSpid)
00335 {
00336     unsigned int dmaStatus;
00337     SpidCmd *pSpidCmd = pSpid->pCurrentCommand;
00338     AT91S_SPI *pSpiHw = pSpid->pSpiHw;
00339 
00340     dmaStatus = DMA_GetStatus();
00341 
00342     if ((dmaStatus & AT91C_CBTC) == 0)
00343         return;
00344 
00345     if ((dmaStatus & (DMA_CBTC << DMA_CHANNEL_0)) == 0)
00346         return;
00347 
00348     // Disable the SPI TX & RX
00349     WRITE_SPI(pSpiHw, SPI_CR, AT91C_SPI_SPIDIS);
00350     // Disable the SPI Peripheral
00351     PERIPH_DISABLE(pSpid->spiId);
00352 
00353     // Disable DMA
00354     DMA_Disable();
00355     // Disable DMA Peripheral
00356     PERIPH_DISABLE(AT91C_ID_HDMA);
00357 
00358     // Release the dataflash semaphore
00359     pSpid->semaphore++;
00360 
00361     // Invoke the callback associated with the current command
00362     if (pSpidCmd && pSpidCmd->callback)
00363 
00364         pSpidCmd->callback(0, pSpidCmd->pArgument);
00365 
00366 }
00367 
00368 //------------------------------------------------------------------------------
00369 /// Returns 1 if the SPI driver is currently busy executing a command; otherwise
00370 /// returns 0.
00371 /// \param pSpid  Pointer to a SPI driver instance.
00372 //------------------------------------------------------------------------------
00373 unsigned char SPID_IsBusy(const Spid *pSpid)
00374 {
00375     if (pSpid->semaphore == 0)
00376         return 1;
00377     else
00378 
00379         return 0;
00380 }
00381 
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines