/* ---------------------------------------------------------------------------- * ATMEL Microcontroller Software Support * ---------------------------------------------------------------------------- * Copyright (c) 2008, Atmel Corporation * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the disclaimer below. * * Atmel's name may not be used to endorse or promote products derived from * this software without specific prior written permission. * * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ---------------------------------------------------------------------------- */ //------------------------------------------------------------------------------ // Headers //------------------------------------------------------------------------------ #include "at26.h" #include #include //------------------------------------------------------------------------------ // Internal definitions //------------------------------------------------------------------------------ /// SPI clock frequency used in Hz. #define SPCK 1000000 /// SPI chip select configuration value. #define CSR (AT91C_SPI_NCPHA | \ SPID_CSR_DLYBCT(BOARD_MCK, 100) | \ SPID_CSR_DLYBS(BOARD_MCK, 5) | \ SPID_CSR_SCBR(BOARD_MCK, SPCK)) /// Number of recognized dataflash. #define NUMDATAFLASH (sizeof(at26Devices) / sizeof(At26Desc)) //------------------------------------------------------------------------------ // Internal variables //------------------------------------------------------------------------------ /// Array of recognized serial firmware dataflash chips. static const At26Desc at26Devices[] = { // name, Jedec ID, size, page size, block size, block erase command {"AT25DF041A" , 0x0001441F, 1 * 1024 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K}, {"AT25DF161" , 0x0002461F, 2 * 1024 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K}, {"AT26DF081A" , 0x0001451F, 1 * 1024 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K}, {"AT26DF0161" , 0x0000461F, 2 * 1024 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K}, {"AT26DF161A" , 0x0001461F, 2 * 1024 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K}, {"AT26DF321 " , 0x0000471F, 8 * 1024 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K}, // Manufacturer: ST {"M25P05" , 0x00102020, 64 * 1024, 256, 32 * 1024, AT26_BLOCK_ERASE_64K}, {"M25P10" , 0x00112020, 128 * 1024, 256, 32 * 1024, AT26_BLOCK_ERASE_64K}, {"M25P20" , 0x00122020, 256 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K}, {"M25P40" , 0x00132020, 512 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K}, {"M25P80" , 0x00142020, 1 * 1024 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K}, {"M25P16" , 0x00152020, 2 * 1024 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K}, {"M25P32" , 0x00162020, 4 * 1024 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K}, {"M25P64" , 0x00172020, 8 * 1024 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K}, // Manufacturer: Windbond {"W25X10" , 0x001130EF, 128 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K}, {"W25X20" , 0x001230EF, 256 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K}, {"W25X40" , 0x001330EF, 512 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K}, {"W25X80" , 0x001430EF, 1 * 1024 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K}, // Manufacturer: Macronix {"MX25L512" , 0x001020C2, 64 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K}, {"MX25L3205" , 0x001620C2, 4 * 1024 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K}, {"MX25L6405" , 0x001720C2, 8 * 1024 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K}, // Other {"SST25VF512" , 0x000048BF, 64 * 1024, 256, 32 * 1024, AT26_BLOCK_ERASE_32K} }; //------------------------------------------------------------------------------ // Exported functions //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ /// Initializes an AT26 driver instance with the given SPI driver and chip /// select value. /// \param pAt26 Pointer to an AT26 driver instance. /// \param pSpid Pointer to an SPI driver instance. /// \param cs Chip select value to communicate with the serial flash. //------------------------------------------------------------------------------ void AT26_Configure(At26 *pAt26, Spid *pSpid, unsigned char cs) { SpidCmd *pCommand; SANITY_CHECK(pAt26); SANITY_CHECK(pSpid); SANITY_CHECK(cs < 4); // Configure the SPI chip select for the serial flash SPID_ConfigureCS(pSpid, cs, CSR); // Initialize the AT26 fields pAt26->pSpid = pSpid; pAt26->pDesc = 0; // Initialize the command structure pCommand = &(pAt26->command); pCommand->pCmd = (unsigned char *) pAt26->pCmdBuffer; pCommand->callback = 0; pCommand->pArgument = 0; pCommand->spiCs = cs; } //------------------------------------------------------------------------------ /// Returns 1 if the serial flash driver is currently busy executing a command; /// otherwise returns 0. /// \param pAt26 Pointer to an At26 driver instance. //------------------------------------------------------------------------------ unsigned char AT26_IsBusy(At26 *pAt26) { return SPID_IsBusy(pAt26->pSpid); } //------------------------------------------------------------------------------ /// Sends a command to the serial flash through the SPI. The command is made up /// of two parts: the first is used to transmit the command byte and optionally, /// address and dummy bytes. The second part is the data to send or receive. /// This function does not block: it returns as soon as the transfer has been /// started. An optional callback can be invoked to notify the end of transfer. /// Return 0 if successful; otherwise, returns AT26_ERROR_BUSY if the AT26 /// driver is currently executing a command, or AT26_ERROR_SPI if the command /// cannot be sent because of a SPI error. /// \param pAt26 Pointer to an At26 driver instance. /// \param cmd Command byte. /// \param cmdSize Size of command (command byte + address bytes + dummy bytes). /// \param pData Data buffer. /// \param dataSize Number of bytes to send/receive. /// \param address Address to transmit. /// \param callback Optional user-provided callback to invoke at end of transfer. /// \param pArgument Optional argument to the callback function. //------------------------------------------------------------------------------ unsigned char AT26_SendCommand( At26 *pAt26, unsigned char cmd, unsigned char cmdSize, unsigned char *pData, unsigned int dataSize, unsigned int address, SpidCallback callback, void *pArgument) { SpidCmd *pCommand; SANITY_CHECK(pAt26); // Check if the SPI driver is available if (AT26_IsBusy(pAt26)) { return AT26_ERROR_BUSY; } // Store command and address in command buffer pAt26->pCmdBuffer[0] = (cmd & 0x000000FF) | ((address & 0x0000FF) << 24) | ((address & 0x00FF00) << 8) | ((address & 0xFF0000) >> 8); // Update the SPI transfer descriptor pCommand = &(pAt26->command); pCommand->cmdSize = cmdSize; pCommand->pData = pData; pCommand->dataSize = dataSize; pCommand->callback = callback; pCommand->pArgument = pArgument; // Start the SPI transfer if (SPID_SendCommand(pAt26->pSpid, pCommand)) { return AT26_ERROR_SPI; } return 0; } //------------------------------------------------------------------------------ /// Tries to detect a serial firmware flash device given its JEDEC identifier. /// The JEDEC id can be retrieved by sending the correct command to the device. /// Returns the corresponding AT26 descriptor if found; otherwise returns 0. /// \param pAt26 Pointer to an AT26 driver instance. /// \param jedecId JEDEC identifier of device. //------------------------------------------------------------------------------ const At26Desc * AT26_FindDevice(At26 *pAt26, unsigned int jedecId) { unsigned int i = 0; SANITY_CHECK(pAt26); // Search if device is recognized pAt26->pDesc = 0; while ((i < NUMDATAFLASH) && !(pAt26->pDesc)) { if (jedecId == at26Devices[i].jedecId) { pAt26->pDesc = &(at26Devices[i]); } i++; } return pAt26->pDesc; }