From 7268260e100caf8efb12193b12442574c5d4b193 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Sun, 24 Jul 2011 10:59:32 +0200 Subject: import 'memories' part of at91lib from serialflash-example --- at91lib/memories/spi-flash/at26.c | 222 +++++++++++++++++++++++++++++++++ at91lib/memories/spi-flash/at26.h | 252 ++++++++++++++++++++++++++++++++++++++ at91lib/memories/spi-flash/spid.c | 215 ++++++++++++++++++++++++++++++++ at91lib/memories/spi-flash/spid.h | 183 +++++++++++++++++++++++++++ 4 files changed, 872 insertions(+) create mode 100644 at91lib/memories/spi-flash/at26.c create mode 100644 at91lib/memories/spi-flash/at26.h create mode 100644 at91lib/memories/spi-flash/spid.c create mode 100644 at91lib/memories/spi-flash/spid.h (limited to 'at91lib') diff --git a/at91lib/memories/spi-flash/at26.c b/at91lib/memories/spi-flash/at26.c new file mode 100644 index 0000000..d7934c9 --- /dev/null +++ b/at91lib/memories/spi-flash/at26.c @@ -0,0 +1,222 @@ +/* ---------------------------------------------------------------------------- + * 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 "at26.h" +#include +#include + +//------------------------------------------------------------------------------ +// Internal definitions +//------------------------------------------------------------------------------ + +/// SPI clock frequency used in Hz. +#define SPCK 1000000 + +/// SPI chip select configuration value. +#define CSR (AT91C_SPI_NCPHA | \ + SPID_CSR_DLYBCT(BOARD_MCK, 100) | \ + SPID_CSR_DLYBS(BOARD_MCK, 5) | \ + SPID_CSR_SCBR(BOARD_MCK, SPCK)) + +/// Number of recognized dataflash. +#define NUMDATAFLASH (sizeof(at26Devices) / sizeof(At26Desc)) + +//------------------------------------------------------------------------------ +// Internal variables +//------------------------------------------------------------------------------ + +/// Array of recognized serial firmware dataflash chips. +static const At26Desc at26Devices[] = { + // name, Jedec ID, size, page size, block size, block erase command + {"AT25DF041A" , 0x0001441F, 1 * 1024 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K}, + {"AT25DF161" , 0x0002461F, 2 * 1024 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K}, + {"AT26DF081A" , 0x0001451F, 1 * 1024 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K}, + {"AT26DF0161" , 0x0000461F, 2 * 1024 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K}, + {"AT26DF161A" , 0x0001461F, 2 * 1024 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K}, + {"AT26DF321 " , 0x0000471F, 8 * 1024 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K}, + // Manufacturer: ST + {"M25P05" , 0x00102020, 64 * 1024, 256, 32 * 1024, AT26_BLOCK_ERASE_64K}, + {"M25P10" , 0x00112020, 128 * 1024, 256, 32 * 1024, AT26_BLOCK_ERASE_64K}, + {"M25P20" , 0x00122020, 256 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K}, + {"M25P40" , 0x00132020, 512 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K}, + {"M25P80" , 0x00142020, 1 * 1024 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K}, + {"M25P16" , 0x00152020, 2 * 1024 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K}, + {"M25P32" , 0x00162020, 4 * 1024 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K}, + {"M25P64" , 0x00172020, 8 * 1024 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K}, + // Manufacturer: Windbond + {"W25X10" , 0x001130EF, 128 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K}, + {"W25X20" , 0x001230EF, 256 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K}, + {"W25X40" , 0x001330EF, 512 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K}, + {"W25X80" , 0x001430EF, 1 * 1024 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K}, + // Manufacturer: Macronix + {"MX25L512" , 0x001020C2, 64 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K}, + {"MX25L3205" , 0x001620C2, 4 * 1024 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K}, + {"MX25L6405" , 0x001720C2, 8 * 1024 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K}, + // Other + {"SST25VF512" , 0x000048BF, 64 * 1024, 256, 32 * 1024, AT26_BLOCK_ERASE_32K} +}; + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Initializes an AT26 driver instance with the given SPI driver and chip +/// select value. +/// \param pAt26 Pointer to an AT26 driver instance. +/// \param pSpid Pointer to an SPI driver instance. +/// \param cs Chip select value to communicate with the serial flash. +//------------------------------------------------------------------------------ +void AT26_Configure(At26 *pAt26, Spid *pSpid, unsigned char cs) +{ + SpidCmd *pCommand; + + SANITY_CHECK(pAt26); + SANITY_CHECK(pSpid); + SANITY_CHECK(cs < 4); + + // Configure the SPI chip select for the serial flash + SPID_ConfigureCS(pSpid, cs, CSR); + + // Initialize the AT26 fields + pAt26->pSpid = pSpid; + pAt26->pDesc = 0; + + // Initialize the command structure + pCommand = &(pAt26->command); + pCommand->pCmd = (unsigned char *) pAt26->pCmdBuffer; + pCommand->callback = 0; + pCommand->pArgument = 0; + pCommand->spiCs = cs; +} + +//------------------------------------------------------------------------------ +/// Returns 1 if the serial flash driver is currently busy executing a command; +/// otherwise returns 0. +/// \param pAt26 Pointer to an At26 driver instance. +//------------------------------------------------------------------------------ +unsigned char AT26_IsBusy(At26 *pAt26) +{ + return SPID_IsBusy(pAt26->pSpid); +} + +//------------------------------------------------------------------------------ +/// Sends a command to the serial flash through the SPI. The command is made up +/// of two parts: the first is used to transmit the command byte and optionally, +/// address and dummy bytes. The second part is the data to send or receive. +/// This function does not block: it returns as soon as the transfer has been +/// started. An optional callback can be invoked to notify the end of transfer. +/// Return 0 if successful; otherwise, returns AT26_ERROR_BUSY if the AT26 +/// driver is currently executing a command, or AT26_ERROR_SPI if the command +/// cannot be sent because of a SPI error. +/// \param pAt26 Pointer to an At26 driver instance. +/// \param cmd Command byte. +/// \param cmdSize Size of command (command byte + address bytes + dummy bytes). +/// \param pData Data buffer. +/// \param dataSize Number of bytes to send/receive. +/// \param address Address to transmit. +/// \param callback Optional user-provided callback to invoke at end of transfer. +/// \param pArgument Optional argument to the callback function. +//------------------------------------------------------------------------------ +unsigned char AT26_SendCommand( + At26 *pAt26, + unsigned char cmd, + unsigned char cmdSize, + unsigned char *pData, + unsigned int dataSize, + unsigned int address, + SpidCallback callback, + void *pArgument) + +{ + SpidCmd *pCommand; + + SANITY_CHECK(pAt26); + + // Check if the SPI driver is available + if (AT26_IsBusy(pAt26)) { + + return AT26_ERROR_BUSY; + } + + // Store command and address in command buffer + pAt26->pCmdBuffer[0] = (cmd & 0x000000FF) + | ((address & 0x0000FF) << 24) + | ((address & 0x00FF00) << 8) + | ((address & 0xFF0000) >> 8); + + // Update the SPI transfer descriptor + pCommand = &(pAt26->command); + pCommand->cmdSize = cmdSize; + pCommand->pData = pData; + pCommand->dataSize = dataSize; + pCommand->callback = callback; + pCommand->pArgument = pArgument; + + // Start the SPI transfer + if (SPID_SendCommand(pAt26->pSpid, pCommand)) { + + return AT26_ERROR_SPI; + } + + return 0; +} + +//------------------------------------------------------------------------------ +/// Tries to detect a serial firmware flash device given its JEDEC identifier. +/// The JEDEC id can be retrieved by sending the correct command to the device. +/// Returns the corresponding AT26 descriptor if found; otherwise returns 0. +/// \param pAt26 Pointer to an AT26 driver instance. +/// \param jedecId JEDEC identifier of device. +//------------------------------------------------------------------------------ +const At26Desc * AT26_FindDevice(At26 *pAt26, unsigned int jedecId) +{ + unsigned int i = 0; + + SANITY_CHECK(pAt26); + + // Search if device is recognized + pAt26->pDesc = 0; + while ((i < NUMDATAFLASH) && !(pAt26->pDesc)) { + + if (jedecId == at26Devices[i].jedecId) { + + pAt26->pDesc = &(at26Devices[i]); + } + + i++; + } + + return pAt26->pDesc; +} + diff --git a/at91lib/memories/spi-flash/at26.h b/at91lib/memories/spi-flash/at26.h new file mode 100644 index 0000000..4e3aa6d --- /dev/null +++ b/at91lib/memories/spi-flash/at26.h @@ -0,0 +1,252 @@ +/* ---------------------------------------------------------------------------- + * 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. + * ---------------------------------------------------------------------------- + */ +//------------------------------------------------------------------------------ +/// \unit +/// +/// !Purpose +/// +/// The AT26 serial firmware Dataflash driver is based on top of the +/// corresponding Spi driver. A Dataflash structure instance has to be +/// initialized using the DF_Init function. Then basic dataflash +/// operations can be launched using macros such as DF_continuous_read. +/// These macros invoke the DF_Command() function which invokes the +/// DPI low driver using the SPI_SendCommand() function. +/// Beware to compute the dataflash internal address, the dataflash sector +/// description must be known (DataflashDesc). Dataflash can be automatically +/// detected using the DF_Scan() function. +/// +/// !Usage +/// +/// -# Initializes an AT26 instance and configures SPI chip select pin +/// using AT26_Configure(). +/// -# Detect DF and returns DF description corresponding to the device +/// connected using AT26_FindDevice().This function shall be called by +/// the application before AT26_SendCommand(). +/// -# Sends a command to the DF through the SPI using AT26_SendCommand(). +/// The command is identified by its command code and the number of +/// bytes to transfer. +/// -# Example code for sending command to write a page to DF. +/// \code +/// // Program page +/// error = AT26_SendCommand(pAt26, AT26_BYTE_PAGE_PROGRAM, 4, +/// pData, writeSize, address, 0, 0); +/// \endcode +/// -# Example code for sending command to read a page from DF. +/// If data needs to be received, then a data buffer must be +/// provided. +/// \code +/// // Start a read operation +/// error = AT26_SendCommand(pAt26, AT26_READ_ARRAY_LF, +/// 4, pData, size, address, 0, 0); +/// \endcode +/// -# This function does not block; its optional callback will +/// be invoked when the transfer completes. +/// -# Check the AT26 driver is ready or not by polling AT26_IsBusy(). +/// +//------------------------------------------------------------------------------ +#ifndef AT26_H +#define AT26_H + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "spid.h" + +//------------------------------------------------------------------------------ +// Macros +//------------------------------------------------------------------------------ + +#define AT26_Size(pAt26) ((pAt26)->pDesc->size) +#define AT26_PageSize(pAt26) ((pAt26)->pDesc->pageSize) +#define AT26_BlockSize(pAt26) ((pAt26)->pDesc->blockSize) +#define AT26_Name(pAt26) ((pAt26)->pDesc->name) +#define AT26_PageNumber(pAt26) (AT26_Size(pAt26) / AT26_PageSize(pAt26)) +#define AT26_BlockNumber(pAt26) (AT26_Size(pAt26) / AT26_BlockSize(pAt26)) +#define AT26_PagePerBlock(pAt26) (AT26_BlockSize(pAt26) / AT26_PageSize(pAt26)) +#define AT26_BlockEraseCmd(pAt26) ((pAt26)->pDesc->blockEraseCmd) + +//------------------------------------------------------------------------------ +// Definitions +//------------------------------------------------------------------------------ + +/// Device is protected, operation cannot be carried out. +#define AT26_ERROR_PROTECTED 1 +/// Device is busy executing a command. +#define AT26_ERROR_BUSY 2 +/// There was a problem while trying to program page data. +#define AT26_ERROR_PROGRAM 3 +/// There was an SPI communication error. +#define AT26_ERROR_SPI 4 + +/// Device ready/busy status bit. +#define AT26_STATUS_RDYBSY (1 << 0) +/// Device is ready. +#define AT26_STATUS_RDYBSY_READY (0 << 0) +/// Device is busy with internal operations. +#define AT26_STATUS_RDYBSY_BUSY (1 << 0) +/// Write enable latch status bit. +#define AT26_STATUS_WEL (1 << 1) +/// Device is not write enabled. +#define AT26_STATUS_WEL_DISABLED (0 << 1) +/// Device is write enabled. +#define AT26_STATUS_WEL_ENABLED (1 << 1) +/// Software protection status bitfield. +#define AT26_STATUS_SWP (3 << 2) +/// All sectors are software protected. +#define AT26_STATUS_SWP_PROTALL (3 << 2) +/// Some sectors are software protected. +#define AT26_STATUS_SWP_PROTSOME (1 << 2) +/// No sector is software protected. +#define AT26_STATUS_SWP_PROTNONE (0 << 2) +/// Write protect pin status bit. +#define AT26_STATUS_WPP (1 << 4) +/// Write protect signal is not asserted. +#define AT26_STATUS_WPP_NOTASSERTED (0 << 4) +/// Write protect signal is asserted. +#define AT26_STATUS_WPP_ASSERTED (1 << 4) +/// Erase/program error bit. +#define AT26_STATUS_EPE (1 << 5) +/// Erase or program operation was successful. +#define AT26_STATUS_EPE_SUCCESS (0 << 5) +/// Erase or program error detected. +#define AT26_STATUS_EPE_ERROR (1 << 5) +/// Sector protection registers locked bit. +#define AT26_STATUS_SPRL (1 << 7) +/// Sector protection registers are unlocked. +#define AT26_STATUS_SPRL_UNLOCKED (0 << 7) +/// Sector protection registers are locked. +#define AT26_STATUS_SPRL_LOCKED (1 << 7) + +/// Read array command code. +#define AT26_READ_ARRAY 0x0B +/// Read array (low frequency) command code. +#define AT26_READ_ARRAY_LF 0x03 +/// Block erase command code (4K block). +#define AT26_BLOCK_ERASE_4K 0x20 +/// Block erase command code (32K block). +#define AT26_BLOCK_ERASE_32K 0x52 +/// Block erase command code (64K block). +#define AT26_BLOCK_ERASE_64K 0xD8 +/// Chip erase command code 1. +#define AT26_CHIP_ERASE_1 0x60 +/// Chip erase command code 2. +#define AT26_CHIP_ERASE_2 0xC7 +/// Byte/page program command code. +#define AT26_BYTE_PAGE_PROGRAM 0x02 +/// Sequential program mode command code 1. +#define AT26_SEQUENTIAL_PROGRAM_1 0xAD +/// Sequential program mode command code 2. +#define AT26_SEQUENTIAL_PROGRAM_2 0xAF +/// Write enable command code. +#define AT26_WRITE_ENABLE 0x06 +/// Write disable command code. +#define AT26_WRITE_DISABLE 0x04 +/// Protect sector command code. +#define AT26_PROTECT_SECTOR 0x36 +/// Unprotect sector command code. +#define AT26_UNPROTECT_SECTOR 0x39 +/// Read sector protection registers command code. +#define AT26_READ_SECTOR_PROT 0x3C +/// Read status register command code. +#define AT26_READ_STATUS 0x05 +/// Write status register command code. +#define AT26_WRITE_STATUS 0x01 +/// Read manufacturer and device ID command code. +#define AT26_READ_JEDEC_ID 0x9F +/// Deep power-down command code. +#define AT26_DEEP_PDOWN 0xB9 +/// Resume from deep power-down command code. +#define AT26_RES_DEEP_PDOWN 0xAB + +//------------------------------------------------------------------------------ +// Types +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Describes a serial firmware flash device parameters. +//------------------------------------------------------------------------------ +typedef struct _At26Desc { + + /// Device string name. + const char *name; + /// JEDEC ID of device. + unsigned int jedecId; + /// Size of device in bytes. + unsigned int size; + /// Size of one page in bytes. + unsigned int pageSize; + /// Block erase size in bytes. + unsigned int blockSize; + /// Block erase command. + unsigned int blockEraseCmd; + +} At26Desc; + +//------------------------------------------------------------------------------ +/// Serial flash driver structure. Holds the current state of the driver, +/// including the current command and the descriptor for the underlying device. +//------------------------------------------------------------------------------ +typedef struct _At26 { + + /// Pointer to the underlying SPI driver. + Spid *pSpid; + /// Current SPI command sent to the SPI driver. + SpidCmd command; + /// Pointer to a descriptor for the serial firmware flash device. + const At26Desc *pDesc; + /// Command buffer. + unsigned int pCmdBuffer[2]; + +} At26; + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +extern void AT26_Configure(At26 *pAt26, Spid *pSpid, unsigned char cs); + +extern unsigned char AT26_SendCommand( + At26 *pAt26, + unsigned char cmd, + unsigned char cmdSize, + unsigned char *pData, + unsigned int dataSize, + unsigned int address, + SpidCallback callback, + void *pArgument); + +extern unsigned char AT26_IsBusy(At26 *pAt26); + +extern const At26Desc * AT26_FindDevice( + At26 *pAt26, + unsigned int jedecId); + +#endif //#ifndef AT26_H + diff --git a/at91lib/memories/spi-flash/spid.c b/at91lib/memories/spi-flash/spid.c new file mode 100644 index 0000000..3dd0409 --- /dev/null +++ b/at91lib/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; + } +} + diff --git a/at91lib/memories/spi-flash/spid.h b/at91lib/memories/spi-flash/spid.h new file mode 100644 index 0000000..5852c1c --- /dev/null +++ b/at91lib/memories/spi-flash/spid.h @@ -0,0 +1,183 @@ +/* ---------------------------------------------------------------------------- + * 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. + * ---------------------------------------------------------------------------- + */ +//------------------------------------------------------------------------------ +/// \unit +/// +/// !!!Purpose +/// +/// 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 +/// +/// -# Initializes a SPI instance and the corresponding SPI hardware, +/// Configure SPI in Master Mode using SPID_Configure(). +/// -# Configures the SPI characteristics (such as Clock Polarity, Phase, +/// transfers delay and Baud Rate) for the device corresponding to the +/// chip select using SPID_ConfigureCS(). +/// -# Starts a SPI master transfer using SPID_SendCommand(). +/// The transfer is performed using the PDC channels. +/// -# It enable the SPI clock. +/// -# Set the corresponding peripheral chip select. +/// -# Initialize the two SPI PDC buffers. +/// - Initialize SPI_TPR and SPI_TCR with SPI command data and size +/// to send command data first. +/// - Initialize SPI_RPR and SPI_RCR with SPI command data and size +/// as dummy value. +/// - Initialize SPI_TNPR and SPI_TNCR with rest of the data to be +/// transfered.(if the data specified in cmd structure) +/// - Initialize SPI_RNPR and SPI_RNCR with rest of the data to be +/// received.(if the data specified in cmd structure) +/// -# Initialize the callback function if specified. +/// -# Enable transmitter and receiver. +/// -# Example for sending a command to the dataflash through the SPI. +/// \code +/// /// Build command to be sent. +/// ... +/// // Send Command and data through the SPI +/// if (SPID_SendCommand(pAt45->pSpid, pCommand)) { +/// return AT45_ERROR_SPI; +/// } +/// \endcode +/// -# The SPI_Handler() must be called by the SPI Interrupt Service Routine +/// with the corresponding Spi instance. It is invokes to check for pending +/// interrupts. +/// - Example for initializing SPI interrupt handler in upper application. +/// \code +/// AIC_ConfigureIT(AT91C_ID_SPI, 0, SPI_Handler); +/// \endcode +//------------------------------------------------------------------------------ + +#ifndef SPID_H +#define SPID_H + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include + +//------------------------------------------------------------------------------ +// Definitions +//------------------------------------------------------------------------------ + +/// An unspecified error has occured. +#define SPID_ERROR 1 + +/// SPI driver is currently in use. +#define SPID_ERROR_LOCK 2 + +//------------------------------------------------------------------------------ +// Macros +//------------------------------------------------------------------------------ + +/// Calculates the value of the SCBR field of the Chip Select Register given +/// MCK and SPCK. +#define SPID_CSR_SCBR(mck, spck) ((((mck) / (spck)) << 8) & AT91C_SPI_SCBR) + +/// Calculates the value of the DLYBS field of the Chip Select Register given +/// the delay in ns and MCK. +#define SPID_CSR_DLYBS(mck, delay) \ + ((((((delay) * ((mck) / 1000000)) / 1000) + 1) << 16) & AT91C_SPI_DLYBS) + +/// Calculates the value of the DLYBCT field of the Chip Select Register given +/// the delay in ns and MCK. +#define SPID_CSR_DLYBCT(mck, delay) \ + ((((((delay) / 32 * ((mck) / 1000000)) / 1000) + 1) << 24) & AT91C_SPI_DLYBCT) + +//------------------------------------------------------------------------------ +// Types +//------------------------------------------------------------------------------ + +/// SPI transfer complete callback. +typedef void (*SpidCallback )(unsigned char, void *); + +//------------------------------------------------------------------------------ +/// Spi Transfer Request prepared by the application upper layer. This structure +/// is sent to the SPI_SendCommand function to start the transfer. At the end of +/// the transfer, the callback is invoked by the interrupt handler. +//------------------------------------------------------------------------------ +typedef struct _SpidCmd { + + /// Pointer to the command data. + unsigned char *pCmd; + /// Command size in bytes. + unsigned char cmdSize; + /// Pointer to the data to be sent. + unsigned char *pData; + /// Data size in bytes. + unsigned short dataSize; + /// SPI chip select. + unsigned char spiCs; + /// Callback function invoked at the end of transfer. + SpidCallback callback; + /// Callback arguments. + void *pArgument; + +} SpidCmd; + +//------------------------------------------------------------------------------ +/// Constant structure associated with SPI port. This structure prevents +/// client applications to have access in the same time. +//------------------------------------------------------------------------------ +typedef struct { + + /// Pointer to SPI Hardware registers + AT91S_SPI *pSpiHw; + /// SPI Id as defined in the product datasheet + char spiId; + /// Current SpiCommand being processed + SpidCmd *pCurrentCommand; + /// Mutual exclusion semaphore. + volatile char semaphore; + +} Spid; + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +extern unsigned char SPID_Configure( + Spid *pSpid, + AT91S_SPI *pSpiHw, + unsigned char spiId); + +extern void SPID_ConfigureCS(Spid *pSpid, unsigned char cs, unsigned int csr); + +extern unsigned char SPID_SendCommand( + Spid *pSpid, + SpidCmd *pCommand); + +extern void SPID_Handler(Spid *pSpid); + +extern unsigned char SPID_IsBusy(const Spid *pSpid); + +#endif // #ifndef SPID_H + -- cgit v1.2.3