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/at26.c | 232 ++++++++++++++++++++++ memories/spi-flash/at26.h | 252 ++++++++++++++++++++++++ memories/spi-flash/at26d.c | 409 +++++++++++++++++++++++++++++++++++++++ memories/spi-flash/at26d.h | 79 ++++++++ memories/spi-flash/at45.c | 257 ++++++++++++++++++++++++ memories/spi-flash/at45.h | 255 ++++++++++++++++++++++++ memories/spi-flash/at45d.c | 223 +++++++++++++++++++++ memories/spi-flash/at45d.h | 86 ++++++++ memories/spi-flash/spi-flash.dir | 37 ++++ memories/spi-flash/spid.c | 215 ++++++++++++++++++++ memories/spi-flash/spid.h | 212 ++++++++++++++++++++ memories/spi-flash/spid_dma.c | 392 +++++++++++++++++++++++++++++++++++++ 12 files changed, 2649 insertions(+) create mode 100644 memories/spi-flash/at26.c create mode 100644 memories/spi-flash/at26.h create mode 100644 memories/spi-flash/at26d.c create mode 100644 memories/spi-flash/at26d.h create mode 100644 memories/spi-flash/at45.c create mode 100644 memories/spi-flash/at45.h create mode 100644 memories/spi-flash/at45d.c create mode 100644 memories/spi-flash/at45d.h create mode 100644 memories/spi-flash/spi-flash.dir create mode 100644 memories/spi-flash/spid.c create mode 100644 memories/spi-flash/spid.h create mode 100644 memories/spi-flash/spid_dma.c (limited to 'memories/spi-flash') diff --git a/memories/spi-flash/at26.c b/memories/spi-flash/at26.c new file mode 100644 index 0000000..4ac80c2 --- /dev/null +++ b/memories/spi-flash/at26.c @@ -0,0 +1,232 @@ +/* ---------------------------------------------------------------------------- + * 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. +#if defined (BOARD_AT26F004) +// SPI runs at a faster frequency for devices with smaller throughput +#define SPCK 10000000 +#else +#define SPCK 1000000 +#endif + +/// 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, 512 * 1024 , 256, 64 * 1024, AT26_BLOCK_ERASE_64K}, + {"AT25DF161" , 0x0002461F, 2 * 1024 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K}, + {"AT26F004" , 0x0000041F, 512 * 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, 4 * 1024 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K}, + {"AT25DF512B" , 0x0001651F, 64 * 1024, 256, 32 * 1024, AT26_BLOCK_ERASE_32K}, + {"AT25DF512B" , 0x0000651F, 64 * 1024, 256, 32 * 1024, AT26_BLOCK_ERASE_32K}, + {"AT25DF021" , 0x0000431F, 256 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K}, + {"AT26DF641" , 0x0000481F, 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/memories/spi-flash/at26.h b/memories/spi-flash/at26.h new file mode 100644 index 0000000..4e3aa6d --- /dev/null +++ b/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/memories/spi-flash/at26d.c b/memories/spi-flash/at26d.c new file mode 100644 index 0000000..123fde1 --- /dev/null +++ b/memories/spi-flash/at26d.c @@ -0,0 +1,409 @@ +/* ---------------------------------------------------------------------------- + * 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 "at26d.h" +#include "board.h" +#include +#include + +/*****/ +#include +/*****/ + +//------------------------------------------------------------------------------ +// Local functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Wait for transfer to finish calling the SPI driver ISR. (interrupts are disabled) +/// \param pAt26 Pointer to an AT26 driver instance. +//------------------------------------------------------------------------------ +static void AT26D_Wait(At26 *pAt26) +{ + // Wait for transfer to finish + while (AT26_IsBusy(pAt26)) + SPID_Handler(pAt26->pSpid); +} + +//------------------------------------------------------------------------------ +/// Reads and returns the status register of the serial flash. +/// \param pAt26 Pointer to an AT26 driver instance. +//------------------------------------------------------------------------------ +static unsigned char AT26D_ReadStatus(At26 *pAt26) +{ + unsigned char error, status; + + SANITY_CHECK(pAt26); + + // Issue a status read command + error = AT26_SendCommand(pAt26, AT26_READ_STATUS, 1, &status, 1, 0, 0, 0); + ASSERT(!error, "-F- AT26_GetStatus: Failed to issue command.\n\r"); + + // Wait for transfer to finish + AT26D_Wait(pAt26); + + return status; +} + +//------------------------------------------------------------------------------ +/// Writes the given value in the status register of the serial flash device. +/// \param pAt26 Pointer to an AT26 driver instance. +/// \param status Status to write. +//------------------------------------------------------------------------------ +static void AT26D_WriteStatus(At26 *pAt26, unsigned char status) +{ + unsigned char error; + + SANITY_CHECK(pAt26); + + // Issue a write status command + error = AT26_SendCommand(pAt26, AT26_WRITE_STATUS, 1, &status, 1, 0, 0, 0); + ASSERT(!error, "-F- AT26_WriteStatus: Failed to issue command.\n\r"); + // Wait for transfer to finish + AT26D_Wait(pAt26); +} + + +//------------------------------------------------------------------------------ +// Global functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Waits for the serial flash device to become ready to accept new commands. +/// \param pAt26 Pointer to an AT26 driver instance. +//------------------------------------------------------------------------------ +void AT26D_WaitReady(At26 *pAt26) +{ + unsigned char ready = 0; + + SANITY_CHECK(pAt26); + + // Read status register and check busy bit + while (!ready) { + + ready = ((AT26D_ReadStatus(pAt26) & AT26_STATUS_RDYBSY) == AT26_STATUS_RDYBSY_READY); + } +} + +//------------------------------------------------------------------------------ +/// Reads and returns the serial flash device ID. +/// \param pAt26 Pointer to an AT26 driver instance. +//------------------------------------------------------------------------------ +unsigned int AT26D_ReadJedecId(At26 *pAt26) +{ + unsigned char error; + unsigned int id = 0; + + SANITY_CHECK(pAt26); + + // Issue a read ID command + error = AT26_SendCommand(pAt26, AT26_READ_JEDEC_ID, 1, + (unsigned char *) &id, 3, 0, 0, 0); + ASSERT(!error, "-F- AT26_GetJedecId: Could not issue command.\n\r"); + + // Wait for transfer to finish + AT26D_Wait(pAt26); + + return id; +} + +//------------------------------------------------------------------------------ +/// Enables critical writes operation on a serial flash device, such as sector +/// protection, status register, etc. +/// \para pAt26 Pointer to an AT26 driver instance. +//------------------------------------------------------------------------------ +void AT26D_EnableWrite(At26 *pAt26) +{ + unsigned char error; + + SANITY_CHECK(pAt26); + + // Issue a write enable command + error = AT26_SendCommand(pAt26, AT26_WRITE_ENABLE, 1, 0, 0, 0, 0, 0); + ASSERT(!error, "-F- AT26_EnableWrite: Could not issue command.\n\r"); + + // Wait for transfer to finish + AT26D_Wait(pAt26); +} + +#if defined(BOARD_AT26F004) + +//------------------------------------------------------------------------------ +// Reads and returns the status register of the serial flash. +// \param pAt26 Pointer to an AT26 driver instance. +//------------------------------------------------------------------------------ +unsigned char AT26F004_Unprotect(At26 *pAt26) +{ + unsigned char error; + unsigned int sector, sectorAddress = 0; + + for (sector = 0 ; sector < 11 ; sector ++) + { + sectorAddress = 0x0; + + if (sector == 10) + sectorAddress = 0x7C000; + else if (sector == 9) + sectorAddress = 0x7A000; + else if (sector == 8) + sectorAddress = 0x78000; + else if (sector == 7) + sectorAddress = 0x70000; + else + sectorAddress = sectorAddress + (0x10000 * sector); + + // Write Enable + AT26D_EnableWrite(pAt26); + + error = AT26_SendCommand(pAt26, AT26_UNPROTECT_SECTOR, 4, 0, 0, sectorAddress, 0, 0); + ASSERT(!error, "-F- AT26_SectorUnprotect: Failed to issue command.\n\r"); + + // Wait for transfer to finish + AT26D_Wait(pAt26); + } + + return 0; +} +#endif + +//------------------------------------------------------------------------------ +/// Unprotects the contents of the serial flash device. +/// Returns 0 if the device has been unprotected; otherwise returns +/// SF_PROTECTED. +/// \param pAt26 Pointer to an AT26 driver instance. +//------------------------------------------------------------------------------ +unsigned char AT26D_Unprotect(At26 *pAt26) +{ + unsigned char status; + + SANITY_CHECK(pAt26); + + // Get the status register value to check the current protection + status = AT26D_ReadStatus(pAt26); + if ((status & AT26_STATUS_SWP) == AT26_STATUS_SWP_PROTNONE) { + + // Protection already disabled + return 0; + } + + // Check if sector protection registers are locked + if ((status & AT26_STATUS_SPRL) == AT26_STATUS_SPRL_LOCKED) { + + // Unprotect sector protection registers by writing the status reg. + AT26D_EnableWrite(pAt26); + AT26D_WriteStatus(pAt26, 0); + } + + // Perform a global unprotect command + AT26D_EnableWrite(pAt26); + + #if defined(BOARD_AT26F004) + AT26F004_Unprotect(pAt26); + #endif + + AT26D_WriteStatus(pAt26, 0); + + // Check the new status + status = AT26D_ReadStatus(pAt26); + if ((status & (AT26_STATUS_SPRL | AT26_STATUS_SWP)) != 0) { + + return AT26_ERROR_PROTECTED; + } + else { + + return 0; + } +} + +//------------------------------------------------------------------------------ +/// Erases all the content of the memory chip. +/// \param pAt26 Pointer to an AT26 driver instance. +//------------------------------------------------------------------------------ +unsigned char AT26D_EraseChip(At26 *pAt26) +{ + unsigned char status; + unsigned char error; + + SANITY_CHECK(pAt26); + + // Check that the flash is unprotected + status = AT26D_ReadStatus(pAt26); + if ((status & AT26_STATUS_SWP) != AT26_STATUS_SWP_PROTNONE) { + return AT26_ERROR_PROTECTED; + } + + // Enable critical write operation + AT26D_EnableWrite(pAt26); + + // Erase the chip + error = AT26_SendCommand(pAt26, AT26_CHIP_ERASE_2, 1, 0, 0, 0, 0, 0); + ASSERT(!error, "-F- AT26_ChipErase: Could not issue command.\n\r"); + // Wait for transfer to finish + AT26D_Wait(pAt26); + // Poll the Serial flash status register until the operation is achieved + AT26D_WaitReady(pAt26); + + return 0; +} + +//------------------------------------------------------------------------------ +/// Erases the specified 64KB block of the serial firmware dataflash. +/// Returns 0 if successful; otherwise returns AT26_ERROR_PROTECTED if the +/// device is protected or AT26_ERROR_BUSY if it is busy executing a command. +/// \param pAt26 Pointer to an AT26 driver instance. +/// \param address Address of the block to erase. +//------------------------------------------------------------------------------ +unsigned char AT26D_EraseBlock(At26 *pAt26, unsigned int address) +{ + unsigned char status; + unsigned char error; + + SANITY_CHECK(pAt26); + + // Check that the flash is ready and unprotected + status = AT26D_ReadStatus(pAt26); + if ((status & AT26_STATUS_RDYBSY) != AT26_STATUS_RDYBSY_READY) { + TRACE_ERROR("AT26D_EraseBlock : Flash busy\n\r"); + return AT26_ERROR_BUSY; + } + else if ((status & AT26_STATUS_SWP) != AT26_STATUS_SWP_PROTNONE) { + TRACE_ERROR("AT26D_EraseBlock : Flash protected\n\r"); + return AT26_ERROR_PROTECTED; + } + + // Enable critical write operation + AT26D_EnableWrite(pAt26); + + // Start the block erase command + error = AT26_SendCommand(pAt26, AT26_BlockEraseCmd(pAt26), 4, 0, 0, address, 0, 0); + ASSERT(!error, "-F- AT26_EraseBlock: Could not issue command.\n\r"); + // Wait for transfer to finish + AT26D_Wait(pAt26); + // Poll the Serial flash status register until the operation is achieved + AT26D_WaitReady(pAt26); + + return 0; +} + +//------------------------------------------------------------------------------ +/// Writes data at the specified address on the serial firmware dataflash. The +/// page(s) to program must have been erased prior to writing. This function +/// handles page boundary crossing automatically. +/// Returns 0 if successful; otherwise, returns AT26_ERROR_PROGRAM is there has +/// been an error during the data programming. +/// \param pAt26 Pointer to an AT26 driver instance. +/// \param pData Data buffer. +/// \param size Number of bytes in buffer. +/// \param address Write address. +//------------------------------------------------------------------------------ +unsigned char AT26D_Write( + At26 *pAt26, + unsigned char *pData, + unsigned int size, + unsigned int address) +{ + unsigned int pageSize; + unsigned int writeSize; + unsigned char error; + #if !defined(BOARD_AT26F004) + unsigned char status; + #endif + + SANITY_CHECK(pAt26); + SANITY_CHECK(pData); + + // Retrieve device page size + pageSize = AT26_PageSize(pAt26); + + // Program one page after the other + while (size > 0) { + // Compute number of bytes to program in page + #if defined(BOARD_AT26F004) + writeSize = 1; + #else + writeSize = min(size, pageSize - (address % pageSize)); + #endif + + // Enable critical write operation + AT26D_EnableWrite(pAt26); + + // Program page + error = AT26_SendCommand(pAt26, AT26_BYTE_PAGE_PROGRAM, 4, + pData, writeSize, address, 0, 0); + ASSERT(!error, "-F- AT26_WritePage: Failed to issue command.\n\r"); + // Wait for transfer to finish + AT26D_Wait(pAt26); + // Poll the Serial flash status register until the operation is achieved + AT26D_WaitReady(pAt26); + + #if !defined(AT26F004) + // Make sure that write was without error + status = AT26D_ReadStatus(pAt26); + if ((status & AT26_STATUS_EPE) == AT26_STATUS_EPE_ERROR) { + + return AT26_ERROR_PROGRAM; + } + #endif + + pData += writeSize; + size -= writeSize; + address += writeSize; + } + + return 0; +} + +//------------------------------------------------------------------------------ +/// Reads data from the specified address on the serial flash. +/// \param pAt26 Pointer to an AT26 driver instance. +/// \param pData Data buffer. +/// \param size Number of bytes to read. +/// \param address Read address. +//------------------------------------------------------------------------------ +unsigned char AT26D_Read( + At26 *pAt26, + unsigned char *pData, + unsigned int size, + unsigned int address) +{ + unsigned char error; + + // Start a read operation + error = AT26_SendCommand(pAt26, AT26_READ_ARRAY_LF, 4, pData, size, address, 0, 0); + ASSERT(!error, "-F- AT26_Read: Could not issue command.\n\r"); + // Wait for transfer to finish + AT26D_Wait(pAt26); + + return error; +} diff --git a/memories/spi-flash/at26d.h b/memories/spi-flash/at26d.h new file mode 100644 index 0000000..9bed206 --- /dev/null +++ b/memories/spi-flash/at26d.h @@ -0,0 +1,79 @@ +/* ---------------------------------------------------------------------------- + * 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 Serialflash driver. +/// +/// !Usage +//------------------------------------------------------------------------------ + +#ifndef AT26D_H +#define AT26D_H + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "at26.h" + + +//------------------------------------------------------------------------------ +// Macros +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ +extern void AT26D_WaitReady(At26 *pAt26); + +extern unsigned int AT26D_ReadJedecId(At26 *pAt26); + +extern unsigned char AT26D_Unprotect(At26 *pAt26); + +extern unsigned char AT26D_EraseChip(At26 *pAt26); + +extern unsigned char AT26D_EraseBlock(At26 *pAt26, unsigned int address); + +extern unsigned char AT26D_Write( + At26 *pAt26, + unsigned char *pData, + unsigned int size, + unsigned int address); + +extern unsigned char AT26D_Read( + At26 *pAt26, + unsigned char *pData, + unsigned int size, + unsigned int address); + +#endif // #ifndef AT26D_H + diff --git a/memories/spi-flash/at45.c b/memories/spi-flash/at45.c new file mode 100644 index 0000000..142e7ed --- /dev/null +++ b/memories/spi-flash/at45.c @@ -0,0 +1,257 @@ +/* ---------------------------------------------------------------------------- + * 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 "at45.h" +#include +#include + +#include + +//------------------------------------------------------------------------------ +// Internal definitions +//------------------------------------------------------------------------------ + +/// Number of dataflash which can be recognized. +#define NUMDATAFLASH (sizeof(at45Devices) / sizeof(At45Desc)) + +//------------------------------------------------------------------------------ +// Local variables +//------------------------------------------------------------------------------ + +/// indicate if the device is configured as binary page or not. +static unsigned char configuredBinaryPage; + +//------------------------------------------------------------------------------ +// Internal variables +//------------------------------------------------------------------------------ + +static const At45Desc at45Devices[] = { + { 512, 1, 264, 9, 0x0C, "AT45DB011D"}, + { 1024, 1, 264, 9, 0x14, "AT45DB021D"}, + { 2048, 1, 264, 9, 0x1C, "AT45DB041D"}, + { 4096, 1, 264, 9, 0x24, "AT45DB081D"}, + { 4096, 1, 528, 10, 0x2C, "AT45DB161D"}, + { 8192, 1, 528, 10, 0x34, "AT45DB321D"}, + { 8192, 1, 1056, 11, 0x3C, "AT45DB642D"}, + {16384, 1, 1056, 11, 0x10, "AT45DB1282"}, + {16384, 1, 2112, 12, 0x18, "AT45DB2562"}, + {32768, 1, 2112, 12, 0x20, "AT45DB5122"} +}; + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Initializes an AT45 instance and configures SPI chip select register. +/// Always returns 0. +/// \param pAt45 Pointer to the At45 instance to initialize. +/// \param pSpid Pointer to the underlying SPI driver. +/// \param spiCs Chip select value to connect to the At45. +//------------------------------------------------------------------------------ +unsigned char AT45_Configure(At45 *pAt45, Spid *pSpid, unsigned char spiCs) +{ + SpidCmd *pCommand; + + // Sanity checks + ASSERT(pSpid, "AT45_Configure: pSpid is 0.\n\r"); + ASSERT(pAt45, "AT45_Configure: pAt45 is 0.\n\r"); + + // Initialize the At45 instance + pAt45->pSpid = pSpid; + pAt45->pDesc = 0; + memset(pAt45->pCmdBuffer, 0, 8); + + // Initialize the spidCmd structure + pCommand = &(pAt45->command); + pCommand->pCmd = pAt45->pCmdBuffer; + pCommand->callback = 0; + pCommand->pArgument = 0; + pCommand->spiCs = spiCs; + + return 0; +} + +//------------------------------------------------------------------------------ +/// This function returns 1 if the At45 driver is not executing any command; +/// otherwise it returns 0. +/// \param pAt45 Pointer to an At45 instance. +//------------------------------------------------------------------------------ +unsigned char AT45_IsBusy(At45 *pAt45) +{ + return SPID_IsBusy(pAt45->pSpid); +} + +//------------------------------------------------------------------------------ +/// Sends a command to the dataflash through the SPI. The command is identified +/// by its command code and the number of bytes to transfer (1 + number of +/// address bytes + number of dummy bytes). If data needs to be received, then +/// a data buffer must be provided. +/// This function does not block; its optional callback will be invoked when +/// the transfer completes. +/// \param pAt45 Pointer to an At45 driver instance. +/// \param cmd Command code. +/// \param cmdSize Size of command code + address bytes + dummy bytes. +/// \param pData Data buffer. +/// \param dataSize Number of data bytes to send/receive. +/// \param address Address at which the command is performed if meaningful. +/// \param callback Optional callback to invoke at end of transfer. +/// \param pArgument Optional parameter to the callback function. +//------------------------------------------------------------------------------ +unsigned char AT45_SendCommand( + At45 *pAt45, + unsigned char cmd, + unsigned char cmdSize, + unsigned char *pData, + unsigned int dataSize, + unsigned int address, + SpidCallback callback, + void *pArgument) +{ + SpidCmd *pCommand; + const At45Desc *pDesc = pAt45->pDesc; + unsigned int dfAddress = 0; + + // Sanity checks + ASSERT(pAt45, "AT45_Command: pAt45 is 0.\n\r"); + ASSERT(pDesc || (cmd == AT45_STATUS_READ), + "AT45_Command: Device has no descriptor, only STATUS_READ command allowed\n\r"); + + // Check if the SPI driver is available + if (AT45_IsBusy(pAt45)) { + + return AT45_ERROR_LOCK; + } + + // Compute command pattern + pAt45->pCmdBuffer[0] = cmd; + + // Add address bytes if necessary + if (cmdSize > 1) { + + ASSERT(pDesc, "AT45_Command: No descriptor for dataflash.\n\r"); + if (!configuredBinaryPage) { + dfAddress = + ((address / (pDesc->pageSize)) << pDesc->pageOffset) + + (address % (pDesc->pageSize)); + } + else { + dfAddress = address; + } + // Write address bytes + if (pDesc->pageNumber >= 16384) { + + pAt45->pCmdBuffer[1] = ((dfAddress & 0x0F000000) >> 24); + pAt45->pCmdBuffer[2] = ((dfAddress & 0x00FF0000) >> 16); + pAt45->pCmdBuffer[3] = ((dfAddress & 0x0000FF00) >> 8); + pAt45->pCmdBuffer[4] = ((dfAddress & 0x000000FF) >> 0); + + if ((cmd != AT45_CONTINUOUS_READ) && (cmd != AT45_PAGE_READ)) { + + cmdSize++; + } + } + else { + + pAt45->pCmdBuffer[1] = ((dfAddress & 0x00FF0000) >> 16); + pAt45->pCmdBuffer[2] = ((dfAddress & 0x0000FF00) >> 8); + pAt45->pCmdBuffer[3] = ((dfAddress & 0x000000FF) >> 0); + } + } + + // Update the SPI Transfer descriptors + pCommand = &(pAt45->command); + pCommand->cmdSize = cmdSize; + pCommand->pData = pData; + pCommand->dataSize = dataSize; + pCommand->callback = callback; + pCommand->pArgument = pArgument; + + // Send Command and data through the SPI + if (SPID_SendCommand(pAt45->pSpid, pCommand)) { + + return AT45_ERROR_SPI; + } + + return 0; +} + +//------------------------------------------------------------------------------ +/// This function returns the At45Desc structure corresponding to the device +/// connected +/// It automatically initializes pAt45->pDesc field structure. +/// This function shall be called by the application before AT45_SendCommand. +/// Returns 0 if successful; Otherwise, returns AT45_ERROR_LOCK if the At45 +/// driver is in use or AT45_ERROR_SPI if there was an error with the SPI driver. +/// \param pAt45 Pointer to an AT45 driver instance. +/// \param status Device status register value. +//------------------------------------------------------------------------------ +const At45Desc * AT45_FindDevice(At45 *pAt45, unsigned char status) +{ + unsigned int i; + unsigned char id = AT45_STATUS_ID(status); + + // Check if status is all one; in which case, it is assumed that no device + // is connected + if (status == 0xFF) { + + return 0; + } + + // Look in device array + i = 0; + pAt45->pDesc = 0; + while ((i < NUMDATAFLASH) && !(pAt45->pDesc)) { + + if (at45Devices[i].id == id) { + + pAt45->pDesc = &(at45Devices[i]); + } + i++; + } + configuredBinaryPage = AT45_STATUS_BINARY(status); + return pAt45->pDesc; +} + +//------------------------------------------------------------------------------ +/// This function returns the pagesize corresponding to the device connected +/// \param pAt45 Pointer to an AT45 driver instance. +//------------------------------------------------------------------------------ +unsigned int AT45_PageSize(At45 *pAt45) +{ + unsigned int pagesize = pAt45->pDesc->pageSize; + if(((pAt45->pDesc->hasBinaryPage) == 0) || !configuredBinaryPage){ + return pagesize; + } + return ((pagesize >> 8) << 8); +} diff --git a/memories/spi-flash/at45.h b/memories/spi-flash/at45.h new file mode 100644 index 0000000..ef7ef26 --- /dev/null +++ b/memories/spi-flash/at45.h @@ -0,0 +1,255 @@ +/* ---------------------------------------------------------------------------- + * 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 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 AT45 instance and configures SPI chip select pin +/// using AT45_Configure(). +/// -# Detect DF and returns DF description corresponding to the device +/// connected using AT45_FindDevice().This function shall be called by +/// the application before AT45_SendCommand. +/// -# Sends a command to the DF through the SPI using AT45_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 +/// // Issue a page write through buffer 1 command +/// error = AT45_SendCommand(pAt45, AT45_PAGE_WRITE_BUF1, 4, +/// pBuffer, size, 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 +/// // Issue a continuous read array command +/// error = AT45_SendCommand(pAt45, AT45_CONTINUOUS_READ_LEG, 8, +/// pBuffer, size, address, 0, 0); +/// \endcode +/// -# This function does not block; its optional callback will +/// be invoked when the transfer completes. +/// -# Check the AT45 driver is ready or not by polling AT45_IsBusy(). +/// +//------------------------------------------------------------------------------ + +#ifndef AT45_H +#define AT45_H + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "spid.h" + +//------------------------------------------------------------------------------ +// Definitions +//------------------------------------------------------------------------------ + +/// The dataflash driver is currently in use. +#define AT45_ERROR_LOCK 1 +/// There was an error with the SPI driver. +#define AT45_ERROR_SPI 2 + +/// AT45 dataflash SPI CSR settings given MCK and SPCK. +#define AT45_CSR(mck, spck) \ + (AT91C_SPI_NCPHA | SPID_CSR_DLYBCT(mck, 250) \ + | SPID_CSR_DLYBS(mck, 250) | SPID_CSR_SCBR(mck, spck)) + +//------------------------------------------------------------------------------ +// Macros +//------------------------------------------------------------------------------ + +#define AT45_PageOffset(pAt45) ((pAt45)->pDesc->pageOffset) +#define AT45_PageNumber(pAt45) ((pAt45)->pDesc->pageNumber) + +/// Returns 1 if the device is ready; otherwise 0. +#define AT45_STATUS_READY(status) (status & 0x80) +/// Returns the device ID code. +#define AT45_STATUS_ID(status) (status & 0x3c) +/// Returns 1 if the device is configured in binary page mode; otherwise 0. +#define AT45_STATUS_BINARY(status) (status & 0x01) + +//------------------------------------------------------------------------------ +// Definitions +//------------------------------------------------------------------------------ + +/// Main memory page read command code. +#define AT45_PAGE_READ 0xD2 +/// Continous array read (legacy) command code. +#define AT45_CONTINUOUS_READ_LEG 0xE8 +/// Continous array read (low frequency) command code. +#define AT45_CONTINUOUS_READ_LF 0x03 +/// Continous array read command code. +#define AT45_CONTINUOUS_READ 0x0B +/// Buffer 1 read (low frequency) command code. +#define AT45_BUF1_READ_LF 0xD1 +/// Buffer 2 read (low frequency) command code. +#define AT45_BUF2_READ_LF 0xD3 +/// Buffer 1 read (serial) command code. +#define AT45_BUF1_READ_SER 0xD4 +/// Buffer 2 read (serial) command code. +#define AT45_BUF2_READ_SER 0xD6 +/// Buffer 1 read (8-bit) command code. +#define AT45_BUF1_READ_8B 0x54 +/// Buffer 2 read (8-bit) command code. +#define AT45_BUF2_READ_8B 0x56 + +/// Buffer 1 write command code. +#define AT45_BUF1_WRITE 0x84 +/// Buffer 2 write command code. +#define AT45_BUF2_WRITE 0x87 +/// Buffer 1 to main memory page program with erase command code. +#define AT45_BUF1_MEM_ERASE 0x83 +/// Buffer 2 to main memory page program with erase command code. +#define AT45_BUF2_MEM_ERASE 0x86 +/// Buffer 1 to main memory page program without erase command code. +#define AT45_BUF1_MEM_NOERASE 0x88 +/// Buffer 2 to main memory page program without erase command code. +#define AT45_BUF2_MEM_NOERASE 0x89 +/// Page erase command code. +#define AT45_PAGE_ERASE 0x81 +/// Block erase command code. +#define AT45_BLOCK_ERASE 0x50 +/// Sector erase command code. +#define AT45_SECTOR_ERASE 0x7C +/// Chip erase command code. +#define AT45_CHIP_ERASE 0xC7, 0x94, 0x80, 0x9A +/// Main memory page program through buffer 1 command code. +#define AT45_PAGE_WRITE_BUF1 0x82 +/// Main memory page program through buffer 2 command code. +#define AT45_PAGE_WRITE_BUF2 0x85 + +/// Main memory page to buffer 1 transfer command code. +#define AT45_PAGE_BUF1_TX 0x53 +/// Main memory page to buffer 2 transfer command code. +#define AT45_PAGE_BUF2_TX 0x55 +/// Main memory page to buffer 1 compare command code. +#define AT45_PAGE_BUF1_CMP 0x60 +/// Main memory page to buffer 2 compare command code. +#define AT45_PAGE_BUF2_CMP 0x61 +/// Auto page rewrite through buffer 1 command code. +#define AT45_AUTO_REWRITE_BUF1 0x58 +/// Auto page rewrite through buffer 2 command code. +#define AT45_AUTO_REWRITE_BUF2 0x59 +/// Deep power-down command code. +#define AT45_DEEP_PDOWN 0xB9 +/// Resume from deep power-down command code. +#define AT45_RES_DEEP_PDOWN 0xAB +/// Status register read command code. +#define AT45_STATUS_READ 0xD7 +/// Manufacturer and device ID read command code. +#define AT45_ID_READ 0x9F + +/// Power-of-2 binary page size configuration command code. +#define AT45_BINARY_PAGE_FIRST_OPCODE 0x3D +#define AT45_BINARY_PAGE 0x2A, 0x80, 0xA6 + +//------------------------------------------------------------------------------ +// Types +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Dataflash description. A constant array of DataflashDesc instance is defined +/// in at45.c. The DF_Scan() function returns the corresponding descriptor +/// according to the dataflash ID detected. +/// This description (page_size, page_offset) is used to compute the internal +/// dataflash address by the DF_Command() function. +//------------------------------------------------------------------------------ +typedef struct { + + /// dataflash page number. + unsigned int pageNumber; + // indicate if power-of-2 binary page supported. + unsigned int hasBinaryPage; + /// dataflash page size. + unsigned int pageSize; + /// page offset in command. + unsigned int pageOffset; + /// Dataflash ID. + unsigned char id; + /// Identifier. + const char *name; + +} At45Desc; + +//------------------------------------------------------------------------------ +/// Dataflash driver structure. It holds the current command being processed. +/// This structure is initialized by the DF_Init() command. +/// pDfDesc field can be initialized by the DF_Scan() function. +/// cmdBuffer is a private driver area used to compute the dataflash address to +/// be sent to the dataflash. +/// Beware the PDC master must have access to this area. +//------------------------------------------------------------------------------ +typedef struct _Dataflash { + + /// Pointer to Spi Structure (SPI low level driver). + Spid *pSpid; + /// Current SPI command sent to the SPI low level driver. + SpidCmd command; + /// Pointer to the dataflash description. + const At45Desc *pDesc; + /// Buffer to store the current command (opcode + dataflash address. + unsigned char pCmdBuffer[8]; + +} At45; + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +extern unsigned char AT45_Configure(At45 *pAt45, Spid *pSpid, unsigned char spiCs); + +extern unsigned char AT45_IsBusy(At45 *pAt45); + +extern unsigned char AT45_SendCommand( + At45 *pAt45, + unsigned char cmd, + unsigned char cmdSize, + unsigned char *pData, + unsigned int dataSize, + unsigned int address, + SpidCallback callback, + void *pArgument); + +extern const At45Desc * AT45_FindDevice(At45 *pAt45, unsigned char status); + +extern unsigned int AT45_PageSize(At45 *pAt45); +#endif // #ifndef AT45_H + diff --git a/memories/spi-flash/at45d.c b/memories/spi-flash/at45d.c new file mode 100644 index 0000000..228a1b6 --- /dev/null +++ b/memories/spi-flash/at45d.c @@ -0,0 +1,223 @@ +/* ---------------------------------------------------------------------------- + * 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 "at45.h" +#include "at45d.h" +#include +#include + +//------------------------------------------------------------------------------ +// Local functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Wait for transfer to finish calling the SPI driver ISR (interrupts are +/// disabled). +/// \param pAt45 Pointer to an AT45 driver instance. +//------------------------------------------------------------------------------ +static void AT45D_Wait(At45 *pAt45) +{ + SANITY_CHECK(pAt45); + + // Wait for transfer to finish + while (AT45_IsBusy(pAt45)) { + + SPID_Handler(pAt45->pSpid); + } +} + + +//------------------------------------------------------------------------------ +// Global functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Waits for the At45 to be ready to accept new commands. +/// \param pAt45 Pointer to a At45 driver instance. +//------------------------------------------------------------------------------ +void AT45D_WaitReady(At45 *pAt45) +{ + unsigned char ready = 0; + + SANITY_CHECK(pAt45); + + // Poll device until it is ready + while (!ready) { + + ready = AT45_STATUS_READY(AT45D_GetStatus(pAt45)); + } +} + +//------------------------------------------------------------------------------ +/// Retrieves and returns the At45 current status, or 0 if an error +/// happened. +/// \param pAt45 Pointer to a At45 driver instance. +//------------------------------------------------------------------------------ +unsigned char AT45D_GetStatus(At45 *pAt45) +{ + unsigned char error; + unsigned char status; + + SANITY_CHECK(pAt45); + + // Issue a status register read command + error = AT45_SendCommand(pAt45, AT45_STATUS_READ, 1, &status, 1, 0, 0, 0); + ASSERT(!error, "-F- AT45_GetStatus: Failed to issue command.\n\r"); + + // Wait for command to terminate + while (AT45_IsBusy(pAt45)) { + + AT45D_Wait(pAt45); + } + + return status; +} + +//------------------------------------------------------------------------------ +/// Reads data from the At45 inside the provided buffer. Since a continuous +/// read command is used, there is no restriction on the buffer size and read +/// address. +/// \param pAt45 Pointer to a At45 driver instance. +/// \param pBuffer Data buffer. +/// \param size Number of bytes to read. +/// \param address Address at which data shall be read. +//------------------------------------------------------------------------------ +void AT45D_Read( + At45 *pAt45, + unsigned char *pBuffer, + unsigned int size, + unsigned int address) +{ + unsigned char error; + + SANITY_CHECK(pAt45); + SANITY_CHECK(pBuffer); + + // Issue a continuous read array command + error = AT45_SendCommand(pAt45, AT45_CONTINUOUS_READ_LEG, 8, pBuffer, size, address, 0, 0); + ASSERT(!error, "-F- AT45_Read: Failed to issue command\n\r"); + + // Wait for the read command to execute + while (AT45_IsBusy(pAt45)) { + + AT45D_Wait(pAt45); + } +} + +//------------------------------------------------------------------------------ +/// Writes data on the At45 at the specified address. Only one page of +/// data is written that way; if the address is not at the beginning of the +/// page, the data is written starting from this address and wraps around to +/// the beginning of the page. +/// \param pAt45 Pointer to a At45 driver instance. +/// \param pBuffer Buffer containing the data to write. +/// \param size Number of bytes to write. +/// \param address Destination address on the At45. +//------------------------------------------------------------------------------ +void AT45D_Write( + At45 *pAt45, + unsigned char *pBuffer, + unsigned int size, + unsigned int address) +{ + unsigned char error; + + SANITY_CHECK(pAt45); + SANITY_CHECK(pBuffer); + SANITY_CHECK(size <= pAt45->pDesc->pageSize); + + // Issue a page write through buffer 1 command + error = AT45_SendCommand(pAt45, AT45_PAGE_WRITE_BUF1, 4, pBuffer, size, address, 0, 0); + ASSERT(!error, "-F- AT45_Write: Could not issue command.\n\r"); + + // Wait until the command is sent + while (AT45_IsBusy(pAt45)) { + + AT45D_Wait(pAt45); + } + + // Wait until the At45 becomes ready again + AT45D_WaitReady(pAt45); +} + +//------------------------------------------------------------------------------ +/// Erases a page of data at the given address in the At45. +/// \param pAt45 Pointer to a At45 driver instance. +/// \param address Address of page to erase. +//------------------------------------------------------------------------------ +void AT45D_Erase(At45 *pAt45, unsigned int address) +{ + unsigned char error; + + SANITY_CHECK(pAt45); + + // Issue a page erase command. + error = AT45_SendCommand(pAt45, AT45_PAGE_ERASE, 4, 0, 0, address, 0, 0); + ASSERT(!error, "-F- AT45_Erase: Could not issue command.\n\r"); + + // Wait for end of transfer + while (AT45_IsBusy(pAt45)) { + + AT45D_Wait(pAt45); + } + + // Poll until the At45 has completed the erase operation + AT45D_WaitReady(pAt45); +} + +//------------------------------------------------------------------------------ +/// Configure power-of-2 binary page size in the At45. +/// \param pAt45 Pointer to a At45 driver instance. +//------------------------------------------------------------------------------ + +void AT45D_BinaryPage(At45 *pAt45) +{ + unsigned char error; + unsigned char opcode[3]= {AT45_BINARY_PAGE}; + SANITY_CHECK(pAt45); + + // Issue a binary page command. + + error = AT45_SendCommand(pAt45, AT45_BINARY_PAGE_FIRST_OPCODE, 1, opcode, 3, 0, 0, 0); + + ASSERT(!error, "-F- AT45_Erase: Could not issue command.\n\r"); + + // Wait for end of transfer + while (AT45_IsBusy(pAt45)) { + + AT45D_Wait(pAt45); + } + + // Wait until the At45 becomes ready again + AT45D_WaitReady(pAt45); +} diff --git a/memories/spi-flash/at45d.h b/memories/spi-flash/at45d.h new file mode 100644 index 0000000..b512445 --- /dev/null +++ b/memories/spi-flash/at45d.h @@ -0,0 +1,86 @@ +/* ---------------------------------------------------------------------------- + * 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 AT45 Dataflash driver is based on the corresponding AT45 driver. +/// A AT45 instance has to be initialized using the Dataflash levle function +/// AT45_Configure(). AT45 Dataflash can be automatically detected using +/// the AT45_FindDevice() function. Then AT45 dataflash operations such as +/// read, write and erase DF can be launched using AT45_SendCommand function +/// with corresponding AT45 command set. +/// +/// !!!Usage +/// +/// -# Reads data from the At45 at the specified address using AT45D_Read(). +/// -# Writes data on the At45 at the specified address using AT45D_Write(). +/// -# Erases a page of data at the given address using AT45D_Erase(). +/// -# Poll until the At45 has completed of corresponding operations using +/// AT45D_WaitReady(). +/// -# Retrieves and returns the At45 current using AT45D_GetStatus(). +//------------------------------------------------------------------------------ + + +#ifndef AT45D_H +#define AT45D_H + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "at45.h" + +//------------------------------------------------------------------------------ +// Global functions +//------------------------------------------------------------------------------ + +extern void AT45D_WaitReady(At45 *pAt45); + +extern unsigned char AT45D_GetStatus(At45 *pAt45); + +extern void AT45D_Read( + At45 *pAt45, + unsigned char *pBuffer, + unsigned int size, + unsigned int address); + +extern void AT45D_Write( + At45 *pAt45, + unsigned char *pBuffer, + unsigned int size, + unsigned int address); + +extern void AT45D_Erase(At45 *pAt45, unsigned int address); + +extern void AT45D_BinaryPage(At45 *pAt45); + +#endif //#ifndef AT45D_H + diff --git a/memories/spi-flash/spi-flash.dir b/memories/spi-flash/spi-flash.dir new file mode 100644 index 0000000..c2f3c2f --- /dev/null +++ b/memories/spi-flash/spi-flash.dir @@ -0,0 +1,37 @@ +/* ---------------------------------------------------------------------------- + * 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. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +/// \dir +/// +/// !!!Purpose +/// +/// Contains the %spi-flash driver for the SPI compatible Dataflash. +//------------------------------------------------------------------------------ + 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; + } +} + diff --git a/memories/spi-flash/spid.h b/memories/spi-flash/spid.h new file mode 100644 index 0000000..654a27e --- /dev/null +++ b/memories/spi-flash/spid.h @@ -0,0 +1,212 @@ +/* ---------------------------------------------------------------------------- + * 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 may perform in 2 ways (depending on the implement): +/// -# 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. +/// -# The transfer is performed using the DMA channels 0 & 1. +/// -# Enable SPI peripheral. +/// -# Set the corresponding peripheral chip select. +/// -# Initialize the two DMA channels. +/// - Configure channel 0 as RX channel. +/// - Source DMA interface is SPI RX. +/// - Source is HW triggerred. +/// - Configure channel 1 as TX channel +/// - Destination DMA interface is SPI TX +/// - Destination is HW triggerred. +/// - Initialize DMA Link List of two items for RX. +/// - Source address pointer to SPI_RDR. +/// - Destination address pointer to command or data position. +/// - Transfer width always BYTE, size is command or data length. +/// - Uses Peripheral-to-Memory Flow Control. +/// - Set DMA channel 0 DMA_DSCR to RX link list. +/// - Set DMA channel 0 DMA_CTRLB to 0 to load link list. +/// - Initialize DMA Link List of two items for TX. +/// - Source address pointer to command or data position. +/// - Destination address pointer to SPI_TDR. +/// - Transfer width always BYTE, size is command or data length. +/// - Uses Memory-to-Peripheral Flow Control. +/// - Set DMA channel 1 DMA_DSCR to TX link list. +/// - Set DMA channel 1 DMA_CTRLB to 0 to load link list. +/// -# Initialize the callback function if specified. +/// -# Enable transmitter and receiver. +/// -# Enable two DMA channels at the same time. +/// +/// - 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 SPID_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 + diff --git a/memories/spi-flash/spid_dma.c b/memories/spi-flash/spid_dma.c new file mode 100644 index 0000000..dc8139d --- /dev/null +++ b/memories/spi-flash/spid_dma.c @@ -0,0 +1,392 @@ +/* ---------------------------------------------------------------------------- + * 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 +#include +#include +#include + +//------------------------------------------------------------------------------ +// Defines +//------------------------------------------------------------------------------ + +/// DMA Link List size +#define SIZE_LL 2 + +/// DMA Width BYTE +#define DMA_WIDTH 0 + +//------------------------------------------------------------------------------ +// 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) + +/// Enable Peripheral +#define PERIPH_ENABLE(id) \ + WRITE_PMC(AT91C_BASE_PMC, PMC_PCER, (1 << (id))) +/// Disable Peripheral +#define PERIPH_DISABLE(id) \ + WRITE_PMC(AT91C_BASE_PMC, PMC_PCDR, (1 << (id))) + + +//------------------------------------------------------------------------------ +// Local Variables +//------------------------------------------------------------------------------ + +/// Linked lists for multi transfer buffer chaining structure instance. +static DmaLinkList dmaTxLinkList[SIZE_LL]; +static DmaLinkList dmaRxLinkList[SIZE_LL]; + +//------------------------------------------------------------------------------ +// Local functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Configure the DMA Channels: 0 RX, 1 TX. +/// Channels are disabled after configure. +//------------------------------------------------------------------------------ +static void configureDmaChannels(void) +{ + // Enable DMA Peripheral + PERIPH_ENABLE(AT91C_ID_HDMA); + // Enable DMA + DMA_Enable(); + + // Free status + DMA_DisableIt(0xFFFFFFFF); + DMA_GetChannelStatus(); + DMA_GetStatus(); + DMA_DisableChannels((1 << DMA_CHANNEL_0) | (1 << DMA_CHANNEL_1)); + // RX channel 0 + DMA_SetConfiguration(DMA_CHANNEL_0, + AT91C_HDMA_SRC_PER_2 + | AT91C_HDMA_DST_PER_2 + | AT91C_HDMA_SRC_H2SEL_HW + | AT91C_HDMA_DST_H2SEL_SW + | AT91C_HDMA_SOD_ENABLE + | AT91C_HDMA_FIFOCFG_LARGESTBURST + ); + + // TX channel 1 + DMA_SetConfiguration(DMA_CHANNEL_1, + AT91C_HDMA_SRC_PER_1 + | AT91C_HDMA_DST_PER_1 + | AT91C_HDMA_SRC_H2SEL_SW + | AT91C_HDMA_DST_H2SEL_HW + | AT91C_HDMA_SOD_ENABLE + | AT91C_HDMA_FIFOCFG_LARGESTBURST + ); +} + +//------------------------------------------------------------------------------ +/// Configure the DMA source and destination with Linker List mode. +/// \param pCommand Pointer to command +//------------------------------------------------------------------------------ +static void configureLinkList(AT91S_SPI *pSpiHw, + SpidCmd *pCommand) +{ + // Setup RX Link List + dmaRxLinkList[0].sourceAddress = (unsigned int)&pSpiHw->SPI_RDR; + dmaRxLinkList[0].destAddress = (unsigned int)pCommand->pCmd; + dmaRxLinkList[0].controlA = pCommand->cmdSize + | AT91C_HDMA_SRC_WIDTH_BYTE + | AT91C_HDMA_DST_WIDTH_BYTE + ; + dmaRxLinkList[0].controlB = 0 + //| AT91C_HDMA_SIF_0 + //| AT91C_HDMA_DIF_0 + | AT91C_HDMA_SRC_DSCR_FETCH_FROM_MEM + | AT91C_HDMA_DST_DSCR_FETCH_FROM_MEM + | AT91C_HDMA_FC_PER2MEM + | AT91C_HDMA_SRC_ADDRESS_MODE_FIXED + | AT91C_HDMA_DST_ADDRESS_MODE_INCR + ; + dmaTxLinkList[0].sourceAddress = (unsigned int)pCommand->pCmd; + dmaTxLinkList[0].destAddress = (unsigned int)&pSpiHw->SPI_TDR; + dmaTxLinkList[0].controlA = pCommand->cmdSize + | AT91C_HDMA_SRC_WIDTH_BYTE + | AT91C_HDMA_DST_WIDTH_BYTE + ; + dmaTxLinkList[0].controlB = 0 + //| AT91C_HDMA_SIF_0 + //| AT91C_HDMA_DIF_0 + | AT91C_HDMA_SRC_DSCR_FETCH_FROM_MEM + | AT91C_HDMA_DST_DSCR_FETCH_FROM_MEM + | AT91C_HDMA_FC_MEM2PER + | AT91C_HDMA_SRC_ADDRESS_MODE_INCR + | AT91C_HDMA_DST_ADDRESS_MODE_FIXED + ; + // Only command + if (pCommand->pData == 0) { + + dmaRxLinkList[0].descriptor = 0; + dmaTxLinkList[0].descriptor = 0; + } + // Command & Data + else { + + dmaRxLinkList[0].descriptor = (unsigned int)&dmaRxLinkList[1]; + dmaRxLinkList[1].sourceAddress = (unsigned int)&pSpiHw->SPI_RDR; + dmaRxLinkList[1].destAddress = (unsigned int)pCommand->pData; + dmaRxLinkList[1].controlA = pCommand->dataSize + | AT91C_HDMA_SRC_WIDTH_BYTE + | AT91C_HDMA_DST_WIDTH_BYTE + ; + dmaRxLinkList[1].controlB = 0 + //| AT91C_HDMA_SIF_0 + //| AT91C_HDMA_DIF_0 + | AT91C_HDMA_SRC_DSCR_FETCH_DISABLE + | AT91C_HDMA_DST_DSCR_FETCH_DISABLE + | AT91C_HDMA_FC_PER2MEM + | AT91C_HDMA_SRC_ADDRESS_MODE_FIXED + | AT91C_HDMA_DST_ADDRESS_MODE_INCR + ; + dmaRxLinkList[1].descriptor = 0; + + dmaTxLinkList[0].descriptor = (unsigned int)&dmaTxLinkList[1]; + dmaTxLinkList[1].sourceAddress = (unsigned int)pCommand->pData; + dmaTxLinkList[1].destAddress = (unsigned int)&pSpiHw->SPI_TDR; + dmaTxLinkList[1].controlA = pCommand->dataSize + | AT91C_HDMA_SRC_WIDTH_BYTE + | AT91C_HDMA_DST_WIDTH_BYTE + ; + dmaTxLinkList[1].controlB = 0 + //| AT91C_HDMA_SIF_0 + //| AT91C_HDMA_DIF_0 + | AT91C_HDMA_SRC_DSCR_FETCH_DISABLE + | AT91C_HDMA_DST_DSCR_FETCH_DISABLE + | AT91C_HDMA_FC_MEM2PER + | AT91C_HDMA_SRC_ADDRESS_MODE_INCR + | AT91C_HDMA_DST_ADDRESS_MODE_FIXED + ; + dmaTxLinkList[1].descriptor = 0; + } + + + // Setup registers + DMA_SetDescriptorAddr(DMA_CHANNEL_0, (unsigned int)&dmaRxLinkList[0]); + DMA_SetDescriptorAddr(DMA_CHANNEL_1, (unsigned int)&dmaTxLinkList[0]); + AT91C_BASE_HDMA->HDMA_CH[DMA_CHANNEL_0].HDMA_CTRLB = 0 + | AT91C_HDMA_SRC_DSCR_FETCH_FROM_MEM + | AT91C_HDMA_DST_DSCR_FETCH_FROM_MEM + ; + AT91C_BASE_HDMA->HDMA_CH[DMA_CHANNEL_1].HDMA_CTRLB = 0 + | AT91C_HDMA_SRC_DSCR_FETCH_FROM_MEM + | AT91C_HDMA_DST_DSCR_FETCH_FROM_MEM + ; +} + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Initializes the Spid structure and the corresponding SPI & DMA hardware. +/// The driver will uses DMA channel 0 for RX and DMA channel 1 for TX. +/// The DMA channels are freed automatically when no SPI command processing. +/// \param pSpid Pointer to a Spid instance. +/// \param pSpiHw Associated SPI peripheral. +/// \param spiId SPI peripheral identifier. +/// \return Always 0. +//------------------------------------------------------------------------------ +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 Peripheral + PERIPH_ENABLE(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 + #if !defined(at91sam3u) + WRITE_SPI(pSpiHw, SPI_PTCR, AT91C_PDC_RXTDIS | AT91C_PDC_TXTDIS); + #endif + + // Disable the SPI TX & RX + WRITE_SPI(pSpiHw, SPI_CR, AT91C_SPI_SPIDIS); + + // Disable the SPI Peripheral + PERIPH_DISABLE(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; + + // Enable the SPI Peripheral + PERIPH_ENABLE(pSpid->spiId); + + // Write CS + WRITE_SPI(pSpiHw, SPI_CSR[cs], csr); + + // Disable the SPI Peripheral + PERIPH_DISABLE(pSpid->spiId); +} + +//------------------------------------------------------------------------------ +/// 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 Peripheral + PERIPH_ENABLE(pSpid->spiId); + + // Disable PDC transmitter and receiver + #if !defined(at91sam3u) + WRITE_SPI(pSpiHw, SPI_PTCR, AT91C_PDC_RXTDIS | AT91C_PDC_TXTDIS); + #endif + + // 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 DMA controller using channel 0 for RX, 1 for TX. + configureDmaChannels(); + configureLinkList(pSpiHw, pCommand); + + // Initialize the callback + pSpid->pCurrentCommand = pCommand; + + // Enable the SPI TX & RX + WRITE_SPI(pSpiHw, SPI_CR, AT91C_SPI_SPIEN); + + // Start DMA 0(RX) && 1(TX) + DMA_EnableChannels((1 << DMA_CHANNEL_0) | (1 << DMA_CHANNEL_1)); + + // Enable DMA Interrupts + DMA_EnableIt( (DMA_CBTC << DMA_CHANNEL_0) + | (DMA_CBTC << DMA_CHANNEL_1)); + + return 0; +} + +//------------------------------------------------------------------------------ +/// SPI DMA transfer ISR, Handle RX complete +//------------------------------------------------------------------------------ +void SPID_Handler(Spid *pSpid) +{ + unsigned int dmaStatus; + SpidCmd *pSpidCmd = pSpid->pCurrentCommand; + AT91S_SPI *pSpiHw = pSpid->pSpiHw; + + dmaStatus = DMA_GetStatus(); + + if ((dmaStatus & AT91C_CBTC) == 0) + return; + + if ((dmaStatus & (DMA_CBTC << DMA_CHANNEL_0)) == 0) + return; + + // Disable the SPI TX & RX + WRITE_SPI(pSpiHw, SPI_CR, AT91C_SPI_SPIDIS); + // Disable the SPI Peripheral + PERIPH_DISABLE(pSpid->spiId); + + // Disable DMA + DMA_Disable(); + // Disable DMA Peripheral + PERIPH_DISABLE(AT91C_ID_HDMA); + + // Release the dataflash semaphore + pSpid->semaphore++; + + // Invoke the callback associated with the current command + if (pSpidCmd && pSpidCmd->callback) { + + pSpidCmd->callback(0, pSpidCmd->pArgument); + } + +} + +//------------------------------------------------------------------------------ +/// 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