/* ---------------------------------------------------------------------------- * 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; }