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/flash/flashd_efc.c | 598 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 598 insertions(+) create mode 100644 memories/flash/flashd_efc.c (limited to 'memories/flash/flashd_efc.c') diff --git a/memories/flash/flashd_efc.c b/memories/flash/flashd_efc.c new file mode 100644 index 0000000..f3fb2ee --- /dev/null +++ b/memories/flash/flashd_efc.c @@ -0,0 +1,598 @@ +/* ---------------------------------------------------------------------------- + * 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 "flashd.h" +#include + +#if !defined(CHIP_FLASH_EFC) +#error efc not supported +#endif + +#include +#include +#include +#include + +#include + +//------------------------------------------------------------------------------ +// Local constants +//------------------------------------------------------------------------------ + +#if defined(AT91C_BASE_EFC) && !defined(AT91C_BASE_EFC0) + #define AT91C_BASE_EFC0 AT91C_BASE_EFC +#endif + +//------------------------------------------------------------------------------ +// Local functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Computes the lock range associated with the given address range. +/// \param start Start address of lock range. +/// \param end End address of lock range. +/// \param pActualStart Actual start address of lock range. +/// \param pActualEnd Actual end address of lock range. +//------------------------------------------------------------------------------ +static void ComputeLockRange( + unsigned int start, + unsigned int end, + unsigned int *pActualStart, + unsigned int *pActualEnd) +{ + AT91S_EFC *pStartEfc, *pEndEfc; + unsigned short startPage, endPage; + unsigned short numPagesInRegion; + unsigned short actualStartPage, actualEndPage; + + // Convert start and end address in page numbers + EFC_TranslateAddress(&pStartEfc, start, &startPage, 0); + EFC_TranslateAddress(&pEndEfc, end, &endPage, 0); + + // Find out the first page of the first region to lock + numPagesInRegion = AT91C_IFLASH_LOCK_REGION_SIZE / AT91C_IFLASH_PAGE_SIZE; + actualStartPage = startPage - (startPage % numPagesInRegion); + actualEndPage = endPage; + if ((endPage % numPagesInRegion) != 0) { + + actualEndPage += numPagesInRegion - (endPage % numPagesInRegion); + } + + // Store actual page numbers + EFC_ComputeAddress(pStartEfc, actualStartPage, 0, pActualStart); + EFC_ComputeAddress(pEndEfc, actualEndPage, 0, pActualEnd); + TRACE_DEBUG("Actual lock range is 0x%06X - 0x%06X\n\r", *pActualStart, *pActualEnd); +} + +//------------------------------------------------------------------------------ +// Global functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Initializes the flash driver. +/// \param mck Master clock frequency in Hz. +//------------------------------------------------------------------------------ +void FLASHD_Initialize(unsigned int mck) +{ + EFC_SetMasterClock(mck); + EFC_SetEraseBeforeProgramming(AT91C_BASE_EFC0, 1); + EFC_DisableIt(AT91C_BASE_EFC0, AT91C_MC_FRDY | AT91C_MC_LOCKE | AT91C_MC_PROGE); +#ifdef AT91C_BASE_EFC1 + EFC_SetEraseBeforeProgramming(AT91C_BASE_EFC1, 1); + EFC_DisableIt(AT91C_BASE_EFC1, AT91C_MC_FRDY | AT91C_MC_LOCKE | AT91C_MC_PROGE); +#endif +} + +//------------------------------------------------------------------------------ +/// Erases the entire flash. +/// Returns 0 if successful; otherwise returns an error code. +//------------------------------------------------------------------------------ +unsigned char FLASHD_Erase(unsigned int address) +{ + AT91S_EFC *pEfc; + unsigned short page; + unsigned short offset; + unsigned char error; + + SANITY_CHECK((address >=AT91C_IFLASH) || (address <= (AT91C_IFLASH + AT91C_IFLASH_SIZE))); + + // Translate write address + EFC_TranslateAddress(&pEfc, address, &page, &offset); + + error = EFC_PerformCommand(pEfc, AT91C_MC_FCMD_ERASE_ALL, 0); + + return error; +} + +static unsigned char pPageBuffer[AT91C_IFLASH_PAGE_SIZE]; + +//------------------------------------------------------------------------------ +/// Writes a data buffer in the internal flash. This function works in polling +/// mode, and thus only returns when the data has been effectively written. +/// Returns 0 if successful; otherwise returns an error code. +/// \param address Write address. +/// \param pBuffer Data buffer. +/// \param size Size of data buffer in bytes. +//------------------------------------------------------------------------------ +unsigned char FLASHD_Write( + unsigned int address, + const void *pBuffer, + unsigned int size) +{ + AT91S_EFC *pEfc; + unsigned short page; + unsigned short offset; + unsigned int writeSize; + unsigned int pageAddress; + unsigned short padding; + unsigned char error; + + unsigned int sizeTmp; + unsigned int *pAlignedDestination; + unsigned int *pAlignedSource; + + SANITY_CHECK(address >= AT91C_IFLASH); + SANITY_CHECK(pBuffer); + SANITY_CHECK((address + size) <= (AT91C_IFLASH + AT91C_IFLASH_SIZE)); + + // Translate write address + EFC_TranslateAddress(&pEfc, address, &page, &offset); + + // Write all pages + while (size > 0) { + + // Copy data in temporary buffer to avoid alignment problems + writeSize = min(AT91C_IFLASH_PAGE_SIZE - offset, size); + EFC_ComputeAddress(pEfc, page, 0, &pageAddress); + padding = AT91C_IFLASH_PAGE_SIZE - offset - writeSize; + + // Pre-buffer data (mask with 0xFF) + memcpy(pPageBuffer, (void *) pageAddress, offset); + + // Buffer data + memcpy(pPageBuffer + offset, pBuffer, writeSize); + + // Post-buffer data + memcpy(pPageBuffer + offset + writeSize, (void *) (pageAddress + offset + writeSize), padding); + + // Write page + // Writing 8-bit and 16-bit data is not allowed + // and may lead to unpredictable data corruption +#ifdef EFC_EVEN_ODD_PROG + // Write even words first with auto erase + pAlignedDestination = (unsigned int*)pageAddress; + pAlignedSource = (unsigned int*)pPageBuffer; + sizeTmp = AT91C_IFLASH_PAGE_SIZE; + while (sizeTmp >= 4) { + + *pAlignedDestination = *pAlignedSource; + pAlignedDestination += 2; + pAlignedSource += 2; + sizeTmp -= 8; + } + // Send writing command + error = EFC_PerformCommand(pEfc, AT91C_MC_FCMD_START_PROG, page); + if (error) { + + return error; + } + + // Then write odd words without auto erase + EFC_SetEraseBeforeProgramming(AT91C_BASE_EFC0, 0); +#ifdef AT91C_BASE_EFC1 + EFC_SetEraseBeforeProgramming(AT91C_BASE_EFC1, 0); +#endif + pAlignedDestination = (unsigned int*)pageAddress + 1; + pAlignedSource = (unsigned int*)pPageBuffer + 1; + sizeTmp = AT91C_IFLASH_PAGE_SIZE; + while (sizeTmp >= 4) { + + *pAlignedDestination = *pAlignedSource; + pAlignedDestination += 2; + pAlignedSource += 2; + sizeTmp -= 8; + } + + // Send writing command + error = EFC_PerformCommand(pEfc, AT91C_MC_FCMD_START_PROG, page); + if (error) { + + return error; + } + + EFC_SetEraseBeforeProgramming(AT91C_BASE_EFC0, 1); +#ifdef AT91C_BASE_EFC1 + EFC_SetEraseBeforeProgramming(AT91C_BASE_EFC1, 1); +#endif + +#else + pAlignedDestination = (unsigned int*)pageAddress; + pAlignedSource = (unsigned int*)pPageBuffer; + sizeTmp = AT91C_IFLASH_PAGE_SIZE; + while (sizeTmp >= 4) { + + *pAlignedDestination++ = *pAlignedSource++; + sizeTmp -= 4; + } + + // Send writing command + error = EFC_PerformCommand(pEfc, AT91C_MC_FCMD_START_PROG, page); + if (error) { + + return error; + } +#endif + // Progression + address += AT91C_IFLASH_PAGE_SIZE; + pBuffer = (void *) ((unsigned int) pBuffer + writeSize); + size -= writeSize; + page++; + offset = 0; + +#if defined(AT91C_BASE_EFC1) + // Handle EFC crossover + if ((pEfc == AT91C_BASE_EFC0) && (page >= (AT91C_IFLASH_NB_OF_PAGES / 2))) { + + pEfc = AT91C_BASE_EFC1; + page = 0; + } +#endif + } + + return 0; +} + +//------------------------------------------------------------------------------ +/// Locks all the regions in the given address range. The actual lock range is +/// reported through two output parameters. +/// Returns 0 if successful; otherwise returns an error code. +/// \param start Start address of lock range. +/// \param end End address of lock range. +/// \param pActualStart Start address of the actual lock range (optional). +/// \param pActualEnd End address of the actual lock range (optional). +//------------------------------------------------------------------------------ +unsigned char FLASHD_Lock( + unsigned int start, + unsigned int end, + unsigned int *pActualStart, + unsigned int *pActualEnd) +{ + AT91S_EFC *pStartEfc, *pEndEfc, *pEfc; + unsigned int actualStart, actualEnd; + unsigned short startPage, endPage; + unsigned char error; + unsigned short numPagesInRegion = AT91C_IFLASH_LOCK_REGION_SIZE / AT91C_IFLASH_PAGE_SIZE; + + // Compute actual lock range and store it + ComputeLockRange(start, end, &actualStart, &actualEnd); + if (pActualStart) { + + *pActualStart = actualStart; + } + if (pActualEnd) { + + *pActualEnd = actualEnd; + } + + // Compute page numbers + EFC_TranslateAddress(&pStartEfc, actualStart, &startPage, 0); + EFC_TranslateAddress(&pEndEfc, actualEnd, &endPage, 0); + + // Lock all pages + // If there is an EFC crossover, lock all pages from first EFC +#if defined(AT91C_BASE_EFC1) + if (pStartEfc != pEndEfc) { + + while (startPage < (AT91C_IFLASH_NB_OF_PAGES / 2)) { + + error = EFC_PerformCommand(pStartEfc, AT91C_MC_FCMD_LOCK, startPage); + if (error) { + + return error; + } + startPage += numPagesInRegion; + } + startPage = 0; + } +#endif + pEfc = pEndEfc; + + // Lock remaining pages + while (startPage < endPage) { + + error = EFC_PerformCommand(pEfc, AT91C_MC_FCMD_LOCK, startPage); + if (error) { + + return error; + } + startPage += numPagesInRegion; + } + + return 0; +} + +//------------------------------------------------------------------------------ +/// Unlocks all the regions in the given address range. The actual unlock range is +/// reported through two output parameters. +/// Returns 0 if successful; otherwise returns an error code. +/// \param start Start address of unlock range. +/// \param end End address of unlock range. +/// \param pActualStart Start address of the actual unlock range (optional). +/// \param pActualEnd End address of the actual unlock range (optional). +//------------------------------------------------------------------------------ +unsigned char FLASHD_Unlock( + unsigned int start, + unsigned int end, + unsigned int *pActualStart, + unsigned int *pActualEnd) +{ + AT91S_EFC *pStartEfc, *pEndEfc, *pEfc; + unsigned int actualStart, actualEnd; + unsigned short startPage, endPage; + unsigned char error; + unsigned short numPagesInRegion = AT91C_IFLASH_LOCK_REGION_SIZE / AT91C_IFLASH_PAGE_SIZE; + + // Compute actual unlock range and store it + ComputeLockRange(start, end, &actualStart, &actualEnd); + if (pActualStart) { + + *pActualStart = actualStart; + } + if (pActualEnd) { + + *pActualEnd = actualEnd; + } + + // Compute page numbers + EFC_TranslateAddress(&pStartEfc, actualStart, &startPage, 0); + EFC_TranslateAddress(&pEndEfc, actualEnd, &endPage, 0); + + // Unlock all pages + // If there is an EFC crossover, unlock all pages from first EFC +#if defined(AT91C_BASE_EFC1) + if (pStartEfc != pEndEfc) { + + while (startPage < (AT91C_IFLASH_NB_OF_PAGES / 2)) { + + error = EFC_PerformCommand(pStartEfc, AT91C_MC_FCMD_UNLOCK, startPage); + if (error) { + + return error; + } + startPage += numPagesInRegion; + } + startPage = 0; + } +#endif + pEfc = pEndEfc; + + // Unlock remaining pages + while (startPage < endPage) { + + error = EFC_PerformCommand(pEfc, AT91C_MC_FCMD_UNLOCK, startPage); + if (error) { + + return error; + } + startPage += numPagesInRegion; + } + + return 0; +} + +//------------------------------------------------------------------------------ +/// Returns the number of locked regions inside the given address range. +/// \param start Start address of range. +/// \param end End address of range. +//------------------------------------------------------------------------------ +unsigned char FLASHD_IsLocked(unsigned int start, unsigned int end) +{ + AT91S_EFC *pStartEfc, *pEndEfc, *pEfc; + unsigned short startPage, endPage; + unsigned char startRegion, endRegion; + unsigned int numPagesInRegion; + unsigned int numLockedRegions = 0; + unsigned int status; + + // Get EFC & page values + EFC_TranslateAddress(&pStartEfc, start, &startPage, 0); + EFC_TranslateAddress(&pEndEfc, end, &endPage, 0); + + // Compute region indexes + numPagesInRegion = AT91C_IFLASH_LOCK_REGION_SIZE / AT91C_IFLASH_PAGE_SIZE; + startRegion = startPage / numPagesInRegion; + endRegion = endPage / numPagesInRegion; + if ((endPage % numPagesInRegion) != 0) { + + endRegion++; + } + + // EFC cross-over, handle starting page -> end page of EFC0 +#if defined(AT91C_BASE_EFC1) + if (pStartEfc != pEndEfc) { + + status = EFC_GetStatus(pStartEfc); + while (startRegion < 16) { + + if ((status & (1 << startRegion << 16)) != 0) { + + numLockedRegions++; + } + startRegion++; + } + startRegion = 0; + } +#endif + pEfc = pEndEfc; + + // Remaining regions / no EFC cross-over + status = EFC_GetStatus(pEfc); + while (startRegion < endRegion) { + + if ((status & (1 << startRegion << 16)) != 0) { + + numLockedRegions++; + } + startRegion++; + } + + return numLockedRegions; +} + +#if (CHIP_EFC_NUM_GPNVMS > 0) +//------------------------------------------------------------------------------ +/// Returns 1 if the given GPNVM bit is currently set; otherwise returns 0. +/// \param gpnvm GPNVM bit index. +//------------------------------------------------------------------------------ +unsigned char FLASHD_IsGPNVMSet(unsigned char gpnvm) +{ + AT91S_EFC *pEfc = AT91C_BASE_EFC0; + unsigned int status; + + SANITY_CHECK(gpnvm < CHIP_EFC_NUM_GPNVMS); + +#ifdef AT91C_BASE_EFC1 + // GPNVM in EFC1 + if (gpnvm >= 8) { + + pEfc = AT91C_BASE_EFC1; + gpnvm -= 8; + } +#endif + + // Check if GPNVM is set + status = EFC_GetStatus(pEfc); + if ((status & (1 << gpnvm << 8)) != 0) { + + return 1; + } + else { + + return 0; + } +} + +//------------------------------------------------------------------------------ +/// Sets the selected GPNVM bit. +/// Returns 0 if successful; otherwise returns an error code. +/// \param gpnvm GPNVM index. +//------------------------------------------------------------------------------ +unsigned char FLASHD_SetGPNVM(unsigned char gpnvm) +{ + AT91S_EFC *pEfc = AT91C_BASE_EFC0; + + SANITY_CHECK(gpnvm < CHIP_EFC_NUM_GPNVMS); + + if (!FLASHD_IsGPNVMSet(gpnvm)) { + +#ifdef AT91C_BASE_EFC1 + // GPNVM in EFC1 + if (gpnvm >= 8) { + + pEfc = AT91C_BASE_EFC1; + gpnvm -= 8; + } +#endif + + return EFC_PerformCommand(pEfc, AT91C_MC_FCMD_SET_GP_NVM, gpnvm); + } + else { + + return 0; + } +} + +//------------------------------------------------------------------------------ +/// Clears the selected GPNVM bit. +/// Returns 0 if successful; otherwise returns an error code. +/// \param gpnvm GPNVM index. +//------------------------------------------------------------------------------ +unsigned char FLASHD_ClearGPNVM(unsigned char gpnvm) +{ + AT91S_EFC *pEfc = AT91C_BASE_EFC0; + + SANITY_CHECK(gpnvm < CHIP_EFC_NUM_GPNVMS); + + if (FLASHD_IsGPNVMSet(gpnvm)) { + +#ifdef AT91C_BASE_EFC1 + // GPNVM in EFC1 + if (gpnvm >= 8) { + + pEfc = AT91C_BASE_EFC1; + gpnvm -= 8; + } +#endif + + return EFC_PerformCommand(pEfc, AT91C_MC_FCMD_CLR_GP_NVM, gpnvm); + } + else { + + return 0; + } +} +#endif //#if (CHIP_EFC_NUM_GPNVMS > 0) + +#if !defined EFC_NO_SECURITY_BIT +//------------------------------------------------------------------------------ +/// Returns 1 if the Security bit is currently set; otherwise returns 0. +//------------------------------------------------------------------------------ +unsigned char FLASHD_IsSecurityBitSet(void) +{ + + AT91S_EFC *pEfc = AT91C_BASE_EFC0; + unsigned int status; + + status = EFC_GetStatus(pEfc); + return ( ((status & AT91C_MC_SECURITY) != 0)?1:0 ); + +} + +//------------------------------------------------------------------------------ +/// Set Security Bit Command (SSB). +/// Returns 0 if successful; otherwise returns an error code. +//------------------------------------------------------------------------------ +unsigned char FLASHD_SetSecurityBit(void) +{ + AT91S_EFC *pEfc = AT91C_BASE_EFC0; + + if( FLASHD_IsSecurityBitSet() == 0) { + return EFC_PerformCommand(pEfc, AT91C_MC_FCMD_SET_SECURITY, 0); + } + else { + return 0; + } +} + +#endif //#if (!defined EFC_NO_SECURITY_BIT) + -- cgit v1.2.3