/***************************************************************************//** * @file * @brief Flash controller (MSC) Peripheral API for EFM32 * @author Energy Micro AS * @version 2.3.2 ******************************************************************************* * @section License * (C) Copyright 2010 Energy Micro AS, http://www.energymicro.com ******************************************************************************* * * This source code is the property of Energy Micro AS. The source and compiled * code may only be used on Energy Micro "EFM32" microcontrollers. * * This copyright notice may not be removed from the source code nor changed. * * DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Energy Micro AS has no * obligation to support this Software. Energy Micro AS is providing the * Software "AS IS", with no express or implied warranties of any kind, * including, but not limited to, any implied warranties of merchantability * or fitness for any particular purpose or warranties against infringement * of any proprietary rights of a third party. * * Energy Micro AS will not be liable for any consequential, incidental, or * special damages, or any other relief, or for any claim by any third party, * arising from your use of this Software. * ******************************************************************************/ #include "efm32_msc.h" #if defined (_EFM32_TINY_FAMILY) || defined(_EFM32_GIANT_FAMILY) #include "efm32_cmu.h" #endif #include "efm32_assert.h" /***************************************************************************//** * @addtogroup EFM32_Library * @{ ******************************************************************************/ /***************************************************************************//** * @addtogroup MSC * @brief Flash controller (MSC) Peripheral API for EFM32 * @{ ******************************************************************************/ /******************************************************************************* ************************** GLOBAL FUNCTIONS ******************************* ******************************************************************************/ /***************************************************************************//** * @brief * Enables the flash controller for writing. * @note * IMPORTANT: This function must be called before flash operations when * AUXHFRCO clock has been changed from default 14MHz band. ******************************************************************************/ void MSC_Init(void) { #if defined (_EFM32_TINY_FAMILY) || defined(_EFM32_GIANT_FAMILY) uint32_t freq, cycles; #endif /* Enable writing to the MSC */ MSC->WRITECTRL |= MSC_WRITECTRL_WREN; /* Unlock the MSC */ MSC->LOCK = MSC_UNLOCK_CODE; /* Disable writing to the MSC */ MSC->WRITECTRL &= ~MSC_WRITECTRL_WREN; #if defined (_EFM32_TINY_FAMILY) || defined(_EFM32_GIANT_FAMILY) /* Configure MSC->TIMEBASE according to selected frequency */ freq = CMU_ClockFreqGet(cmuClock_AUX); if( freq > 7000000) { /* Calculate number of clock cycles for 1us as base period */ freq = (freq * 11) / 10; cycles = (freq / 1000000) + 1; /* Configure clock cycles for flash timing */ MSC->TIMEBASE = (MSC->TIMEBASE & ~(_MSC_TIMEBASE_BASE_MASK| _MSC_TIMEBASE_PERIOD_MASK))| MSC_TIMEBASE_PERIOD_1US| (cycles << _MSC_TIMEBASE_BASE_SHIFT); } else { /* Calculate number of clock cycles for 5us as base period */ freq = (freq * 5 * 11) / 10; cycles = (freq / 1000000) + 1; /* Configure clock cycles for flash timing */ MSC->TIMEBASE = (MSC->TIMEBASE & ~(_MSC_TIMEBASE_BASE_MASK| _MSC_TIMEBASE_PERIOD_MASK))| MSC_TIMEBASE_PERIOD_5US| (cycles << _MSC_TIMEBASE_BASE_SHIFT); } #endif } /***************************************************************************//** * @brief * Disables the flash controller for writing. ******************************************************************************/ void MSC_Deinit(void) { /* Enable writing to the MSC */ MSC->WRITECTRL |= MSC_WRITECTRL_WREN; /* Lock the MSC */ MSC->LOCK = 0; /* Disable writing to the MSC */ MSC->WRITECTRL &= ~MSC_WRITECTRL_WREN; } /***************************************************************************//** * @brief * Erases a page in flash memory. * @note * This function MUST be executed from RAM. Failure to execute this portion * of the code in RAM will result in a hardfault. For IAR, Rowley and * Codesourcery this will be achieved automatically. For Keil uVision 4 you * must define a section called "ram_code" and place this manually in your * project's scatter file. * @param[in] startAddress * Pointer to the flash page to erase. Must be aligned to beginning of page * boundary. * @return * Returns the status of erase operation, #msc_Return_TypeDef * @verbatim * flashReturnOk - Operation completed successfully. * flashReturnInvalidAddr - Operation tried to erase a non-flash area. * flashReturnLocked - Operation tried to erase a locked area of the flash. * flashReturnTimeOut - Operation timed out waiting for flash operation * to complete. * @endverbatim ******************************************************************************/ #ifdef __CC_ARM /* MDK-ARM compiler */ #pragma arm section code="ram_code" #endif /* __CC_ARM */ #if defined( __ICCARM__ ) /* Suppress warnings originating from use of EFM_ASSERT(): */ /* "Call to a non __ramfunc function from within a __ramfunc function" */ /* "Possible rom access from within a __ramfunc function" */ #pragma diag_suppress=Ta022 #pragma diag_suppress=Ta023 #endif msc_Return_TypeDef MSC_ErasePage(uint32_t *startAddress) { int timeOut = MSC_PROGRAM_TIMEOUT; /* Address must be aligned to pages */ EFM_ASSERT((((uint32_t)startAddress) & 0x1FF) == 0); /* Enable writing to the MSC */ MSC->WRITECTRL |= MSC_WRITECTRL_WREN; /* Load address */ MSC->ADDRB = (uint32_t)startAddress; MSC->WRITECMD = MSC_WRITECMD_LADDRIM; /* Check for invalid address */ if (MSC->STATUS & MSC_STATUS_INVADDR) { /* Disable writing to the MSC */ MSC->WRITECTRL &= ~MSC_WRITECTRL_WREN; return mscReturnInvalidAddr; } /* Check for write protected page */ if (MSC->STATUS & MSC_STATUS_LOCKED) { /* Disable writing to the MSC */ MSC->WRITECTRL &= ~MSC_WRITECTRL_WREN; return mscReturnLocked; } /* Send erase page command */ MSC->WRITECMD = MSC_WRITECMD_ERASEPAGE; /* Wait for the erase to complete */ while ((MSC->STATUS & MSC_STATUS_BUSY) && (timeOut != 0)) { timeOut--; } if (timeOut == 0) { /* Disable writing to the MSC */ MSC->WRITECTRL &= ~MSC_WRITECTRL_WREN; return mscReturnTimeOut; } /* Disable writing to the MSC */ MSC->WRITECTRL &= ~MSC_WRITECTRL_WREN; return mscReturnOk; } #if defined( __ICCARM__ ) #pragma diag_default=Ta022 #pragma diag_default=Ta023 #endif /***************************************************************************//** * @brief * Writes a single word to flash memory. Data to write must be aligned to * words and contain a number of bytes that is divisable by four. * @note * The flash must be erased prior to writing a new word. * This function must be run from RAM. Failure to execute this portion * of the code in RAM will result in a hardfault. For IAR, Rowley and * Codesourcery this will be achieved automatically. For Keil uVision 4 you * must define a section called "ram_code" and place this manually in your * project's scatter file. * * @param[in] address * Pointer to the flash word to write to. Must be aligned to words. * @param[in] data * Data to write to flash. * @param[in] numBytes * Number of bytes to write from flash. NB: Must be divisable by four. * @return * Returns the status of the write operation, #msc_Return_TypeDef * @verbatim * flashReturnOk - Operation completed successfully. * flashReturnInvalidAddr - Operation tried to erase a non-flash area. * flashReturnLocked - Operation tried to erase a locked area of the flash. * flashReturnTimeOut - Operation timed out waiting for flash operation * to complete. * @endverbatim ******************************************************************************/ #ifdef __CC_ARM /* MDK-ARM compiler */ #pragma arm section code="ram_code" #endif /* __CC_ARM */ #if defined( __ICCARM__ ) /* Suppress warnings originating from use of EFM_ASSERT(): */ /* "Call to a non __ramfunc function from within a __ramfunc function" */ /* "Possible rom access from within a __ramfunc function" */ #pragma diag_suppress=Ta022 #pragma diag_suppress=Ta023 #endif msc_Return_TypeDef MSC_WriteWord(uint32_t *address, void const *data, int numBytes) { int timeOut; int wordCount; int numWords; /* Check alignment (Must be aligned to words) */ EFM_ASSERT(((uint32_t) address & 0x3) == 0); /* Check number of bytes. Must be divisable by four */ EFM_ASSERT((numBytes & 0x3) == 0); /* Enable writing to the MSC */ MSC->WRITECTRL |= MSC_WRITECTRL_WREN; /* Convert bytes to words */ numWords = numBytes >> 2; for (wordCount = 0; wordCount < numWords; wordCount++) { /* Load address */ MSC->ADDRB = (uint32_t)(address + wordCount); MSC->WRITECMD = MSC_WRITECMD_LADDRIM; /* Check for invalid address */ if (MSC->STATUS & MSC_STATUS_INVADDR) { /* Disable writing to the MSC */ MSC->WRITECTRL &= ~MSC_WRITECTRL_WREN; return mscReturnInvalidAddr; } /* Check for write protected page */ if (MSC->STATUS & MSC_STATUS_LOCKED) { /* Disable writing to the MSC */ MSC->WRITECTRL &= ~MSC_WRITECTRL_WREN; return mscReturnLocked; } /* Wait for the MSC to be ready for a new data word */ /* Due to the timing of this function, the MSC should already by ready */ timeOut = MSC_PROGRAM_TIMEOUT; while (((MSC->STATUS & MSC_STATUS_WDATAREADY) == 0) && (timeOut != 0)) { timeOut--; } /* Check for timeout */ if (timeOut == 0) { /* Disable writing to the MSC */ MSC->WRITECTRL &= ~MSC_WRITECTRL_WREN; return mscReturnTimeOut; } /* Load data into write data register */ MSC->WDATA = *(((uint32_t *)data) + wordCount); /* Trigger write once */ MSC->WRITECMD = MSC_WRITECMD_WRITEONCE; /* Wait for the write to complete */ timeOut = MSC_PROGRAM_TIMEOUT; while ((MSC->STATUS & MSC_STATUS_BUSY) && (timeOut != 0)) { timeOut--; } /* Check for timeout */ if (timeOut == 0) { /* Disable writing to the MSC */ MSC->WRITECTRL &= ~MSC_WRITECTRL_WREN; return mscReturnTimeOut; } } /* Disable writing to the MSC */ MSC->WRITECTRL &= ~MSC_WRITECTRL_WREN; return mscReturnOk; } #if defined( __ICCARM__ ) #pragma diag_default=Ta022 #pragma diag_default=Ta023 #endif #if defined(_EFM32_GIANT_FAMILY) /***************************************************************************//** * @brief * Erase entire flash in one operation * @note * This command will erase the entire contents of the device. * Use with care, both a debug session and all contents of the flash will be * lost. The lock bit, MLW will prevent this operation from executing and * might prevent successful mass erase. ******************************************************************************/ #ifdef __CC_ARM /* MDK-ARM compiler */ #pragma arm section code="ram_code" #endif /* __CC_ARM */ msc_Return_TypeDef MSC_MassErase(void) { /* Enable writing to the MSC */ MSC->WRITECTRL |= MSC_WRITECTRL_WREN; /* Unlock device mass erase */ MSC->MASSLOCK = MSC_MASSLOCK_LOCKKEY_UNLOCK; /* Erase first 512K block */ MSC->WRITECMD = MSC_WRITECMD_ERASEMAIN0; /* Waiting for erase to complete */ while ((MSC->STATUS & MSC_STATUS_BUSY)){} #if FLASH_SIZE >= (512*1024) /* Erase second 512K block */ MSC->WRITECMD = MSC_WRITECMD_ERASEMAIN1; /* Waiting for erase to complete */ while ((MSC->STATUS & MSC_STATUS_BUSY)){} #endif /* Restore mass erase lock */ MSC->MASSLOCK = MSC_MASSLOCK_LOCKKEY_LOCK; /* This will only successfully return if calling function is also in SRAM */ return mscReturnOk; } #endif /** @} (end addtogroup MSC) */ /** @} (end addtogroup EFM32_Library) */