From 044ad7c3987460ede48ff27afd6bdb0ca05a0432 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Mon, 4 Jul 2011 20:52:54 +0200 Subject: import at91lib from at91lib_20100901_softpack_1_9_v_1_0_svn_v15011 it's sad to see that atmel doesn't publish their svn repo or has a centralized location or even puts proper version/release info into the library itself --- memories/spi-flash/spid.c | 215 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 215 insertions(+) create mode 100644 memories/spi-flash/spid.c (limited to 'memories/spi-flash/spid.c') diff --git a/memories/spi-flash/spid.c b/memories/spi-flash/spid.c new file mode 100644 index 0000000..3dd0409 --- /dev/null +++ b/memories/spi-flash/spid.c @@ -0,0 +1,215 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, 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. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "spid.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 +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Initializes the Spid structure and the corresponding SPI hardware. +/// Always returns 0. +/// \param pSpid Pointer to a Spid instance. +/// \param pSpiHw Associated SPI peripheral. +/// \param spiId SPI peripheral identifier. +//------------------------------------------------------------------------------ +unsigned char SPID_Configure(Spid *pSpid, AT91S_SPI *pSpiHw, unsigned char spiId) +{ + // Initialize the SPI structure + pSpid->pSpiHw = pSpiHw; + pSpid->spiId = spiId; + pSpid->semaphore = 1; + pSpid->pCurrentCommand = 0; + + // Enable the SPI clock + WRITE_PMC(AT91C_BASE_PMC, PMC_PCER, (1 << pSpid->spiId)); + + // Execute a software reset of the SPI twice + WRITE_SPI(pSpiHw, SPI_CR, AT91C_SPI_SWRST); + WRITE_SPI(pSpiHw, SPI_CR, AT91C_SPI_SWRST); + + // Configure SPI in Master Mode with No CS selected !!! + WRITE_SPI(pSpiHw, SPI_MR, AT91C_SPI_MSTR | AT91C_SPI_MODFDIS | AT91C_SPI_PCS); + + // Disable the PDC transfer + WRITE_SPI(pSpiHw, SPI_PTCR, AT91C_PDC_RXTDIS | AT91C_PDC_TXTDIS); + + // Enable the SPI + WRITE_SPI(pSpiHw, SPI_CR, AT91C_SPI_SPIEN); + + // Enable the SPI clock + WRITE_PMC(AT91C_BASE_PMC, PMC_PCDR, (1 << pSpid->spiId)); + + return 0; +} + +//------------------------------------------------------------------------------ +/// 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, unsigned char cs, unsigned int csr) +{ + AT91S_SPI *pSpiHw = pSpid->pSpiHw; + WRITE_SPI(pSpiHw, SPI_CSR[cs], csr); +} + +//------------------------------------------------------------------------------ +/// Starts a SPI master transfer. This is a non blocking function. It will +/// return as soon as the transfer is started. +/// Returns 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. +/// \param pSpid Pointer to a Spid instance. +/// \param pCommand Pointer to the SPI command to execute. +//------------------------------------------------------------------------------ +unsigned char SPID_SendCommand(Spid *pSpid, SpidCmd *pCommand) +{ + AT91S_SPI *pSpiHw = pSpid->pSpiHw; + unsigned int spiMr; + + // Try to get the dataflash semaphore + if (pSpid->semaphore == 0) { + + return SPID_ERROR_LOCK; + } + pSpid->semaphore--; + + // Enable the SPI clock + WRITE_PMC(AT91C_BASE_PMC, PMC_PCER, (1 << pSpid->spiId)); + + // Disable transmitter and receiver + WRITE_SPI(pSpiHw, SPI_PTCR, AT91C_PDC_RXTDIS | AT91C_PDC_TXTDIS); + + // Write to the MR register + spiMr = READ_SPI(pSpiHw, SPI_MR); + spiMr |= AT91C_SPI_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, AT91C_PDC_RXTEN | AT91C_PDC_TXTEN); + + // Enable buffer complete interrupt + WRITE_SPI(pSpiHw, SPI_IER, AT91C_SPI_RXBUFF); + + return 0; +} + +//------------------------------------------------------------------------------ +/// The SPI_Handler must be called by the SPI Interrupt Service Routine with the +/// corresponding Spi instance. +/// 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; + AT91S_SPI *pSpiHw = pSpid->pSpiHw; + volatile unsigned int spiSr; + + // Read the status register + spiSr = READ_SPI(pSpiHw, SPI_SR); + if (spiSr & AT91C_SPI_RXBUFF) { + + // Disable transmitter and receiver + WRITE_SPI(pSpiHw, SPI_PTCR, AT91C_PDC_RXTDIS | AT91C_PDC_TXTDIS); + + // Disable the SPI clock + WRITE_PMC(AT91C_BASE_PMC, PMC_PCDR, (1 << pSpid->spiId)); + + // Disable buffer complete interrupt + WRITE_SPI(pSpiHw, SPI_IDR, AT91C_SPI_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. + } +} + +//------------------------------------------------------------------------------ +/// Returns 1 if the SPI driver is currently busy executing a command; otherwise +/// returns 0. +/// \param pSpid Pointer to a SPI driver instance. +//------------------------------------------------------------------------------ +unsigned char SPID_IsBusy(const Spid *pSpid) +{ + if (pSpid->semaphore == 0) { + + return 1; + } + else { + + return 0; + } +} + -- cgit v1.2.3