/* ---------------------------------------------------------------------------- * ATMEL Microcontroller Software Support * ---------------------------------------------------------------------------- * Copyright (c) 2009, Atmel Corporation * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the disclaimer below. * * Atmel's name may not be used to endorse or promote products derived from * this software without specific prior written permission. * * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ---------------------------------------------------------------------------- */ /** \addtogroup spi_pdc_module SPI PDC driver * The Spi driver is a low level spi driver which performs SPI device Initializes, * spi transfer and receive. It can be used by upper SPI driver such as AT45 * driver and AT26 driver. * * \usage * * Related files :\n * \ref spi_pdc.c\n * \ref spi_pdc.h.\n */ /*@{*/ /*@}*/ /** * \file * * Implementation of SPI PDC driver. * */ /*---------------------------------------------------------------------------- * Headers *----------------------------------------------------------------------------*/ #include "spi_pdc.h" #include /*---------------------------------------------------------------------------- * Macros *----------------------------------------------------------------------------*/ /** Write PMC register*/ #define WRITE_PMC(pPmc, regName, value) pPmc->regName = (value) /** Write SPI register*/ #define WRITE_SPI(pSpi, regName, value) pSpi->regName = (value) /** Read SPI registers */ #define READ_SPI(pSpi, regName) (pSpi->regName) /*---------------------------------------------------------------------------- * Exported functions *----------------------------------------------------------------------------*/ /** * \brief Initializes the Spid structure and the corresponding SPI hardware. * * \param pSpid Pointer to a Spid instance. * \param pSpiHw Associated SPI peripheral. * \param spiId SPI peripheral identifier. * \return 0. */ uint8_t SPID_Configure(Spid *pSpid, Spi *pSpiHw, uint8_t spiId) { /* Initialize the SPI structure*/ pSpid->pSpiHw = pSpiHw; pSpid->spiId = spiId; pSpid->semaphore = 1; pSpid->pCurrentCommand = 0; /* Enable the SPI clock*/ WRITE_PMC(PMC, PMC_PCER0, (1 << pSpid->spiId)); /* Execute a software reset of the SPI twice */ WRITE_SPI(pSpiHw, SPI_CR, SPI_CR_SWRST); WRITE_SPI(pSpiHw, SPI_CR, SPI_CR_SWRST); /* Configure SPI in Master Mode with No CS selected !!! */ WRITE_SPI(pSpiHw, SPI_MR, SPI_MR_MSTR | SPI_MR_MODFDIS | SPI_MR_PCS); /* Enable the SPI*/ WRITE_SPI(pSpiHw, SPI_CR, SPI_CR_SPIEN); /* Enable the SPI clock*/ WRITE_PMC(PMC, PMC_PCDR0, (1 << pSpid->spiId)); return 0; } /** * \brief Configures the parameters for the device corresponding to the cs. * * \param pSpid Pointer to a Spid instance. * \param cs number corresponding to the SPI chip select. * \param csr SPI_CSR value to setup. */ void SPID_ConfigureCS(Spid *pSpid, uint8_t cs, uint32_t csr) { Spi *pSpiHw = pSpid->pSpiHw; WRITE_SPI(pSpiHw, SPI_CSR[cs], csr); } /** * \brief Starts a SPI master transfer. This is a non blocking function. It will * return as soon as the transfer is started. * * \param pSpid Pointer to a Spid instance. * \param pCommand Pointer to the SPI command to execute. * \return 0 if the transfer has been started successfully; otherwise returns * SPID_ERROR_LOCK is the driver is in use, or SPID_ERROR if the command is not * valid. */ uint8_t SPID_SendCommand(Spid *pSpid, SpidCmd *pCommand) { Spi *pSpiHw = pSpid->pSpiHw; uint32_t spiMr; /* Try to get the dataflash semaphore */ if (pSpid->semaphore == 0) { return SPID_ERROR_LOCK; } pSpid->semaphore--; /* Enable the SPI clock */ WRITE_PMC(PMC, PMC_PCER0, (1 << pSpid->spiId)); /* Disable transmitter and receiver*/ WRITE_SPI(pSpiHw, SPI_PTCR, SPI_PTCR_RXTDIS | SPI_PTCR_TXTDIS); /* Write to the MR register*/ spiMr = READ_SPI(pSpiHw, SPI_MR); spiMr |= SPI_MR_PCS; spiMr &= ~((1 << pCommand->spiCs) << 16); WRITE_SPI(pSpiHw, SPI_MR, spiMr); /* Initialize the two SPI PDC buffer*/ WRITE_SPI(pSpiHw, SPI_RPR, (int) pCommand->pCmd); WRITE_SPI(pSpiHw, SPI_RCR, pCommand->cmdSize); WRITE_SPI(pSpiHw, SPI_TPR, (int) pCommand->pCmd); WRITE_SPI(pSpiHw, SPI_TCR, pCommand->cmdSize); WRITE_SPI(pSpiHw, SPI_RNPR, (int) pCommand->pData); WRITE_SPI(pSpiHw, SPI_RNCR, pCommand->dataSize); WRITE_SPI(pSpiHw, SPI_TNPR, (int) pCommand->pData); WRITE_SPI(pSpiHw, SPI_TNCR, pCommand->dataSize); /* Initialize the callback*/ pSpid->pCurrentCommand = pCommand; /* Enable transmitter and receiver*/ WRITE_SPI(pSpiHw, SPI_PTCR, SPI_PTCR_RXTEN | SPI_PTCR_TXTEN); /* Enable buffer complete interrupt*/ WRITE_SPI(pSpiHw, SPI_IER, SPI_IER_RXBUFF); return 0; } /** * \brief The SPI_Handler must be called by the SPI Interrupt Service Routine with the * corresponding Spi instance. * * \note The SPI_Handler will unlock the Spi semaphore and invoke the upper application * callback. * \param pSpid Pointer to a Spid instance. */ void SPID_Handler(Spid *pSpid) { SpidCmd *pSpidCmd = pSpid->pCurrentCommand; Spi *pSpiHw = pSpid->pSpiHw; volatile uint32_t spiSr; /* Read the status register*/ spiSr = READ_SPI(pSpiHw, SPI_SR); if (spiSr & SPI_SR_RXBUFF) { /* Disable transmitter and receiver */ WRITE_SPI(pSpiHw, SPI_PTCR, SPI_PTCR_RXTDIS | SPI_PTCR_TXTDIS); /* Disable the SPI clock*/ WRITE_PMC(PMC, PMC_PCDR0, (1 << pSpid->spiId)); /* Disable buffer complete interrupt */ WRITE_SPI(pSpiHw, SPI_IDR, SPI_IDR_RXBUFF); /* Release the dataflash semaphore*/ pSpid->semaphore++; /* Invoke the callback associated with the current command*/ if (pSpidCmd && pSpidCmd->callback) { pSpidCmd->callback(0, pSpidCmd->pArgument); } /* Nothing must be done after. A new DF operation may have been started in the callback function.*/ } } /** * \brief Returns 1 if the SPI driver is currently busy executing a command; otherwise * returns 0. * \param pSpid Pointer to a Spid instance. */ uint8_t SPID_IsBusy(const Spid *pSpid) { if (pSpid->semaphore == 0) { return 1; } else { return 0; } }