/* ---------------------------------------------------------------------------- * 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)