366 lines
12 KiB
C
366 lines
12 KiB
C
/** @file hal/micro/cortexm3/flash.c
|
|
* @brief Implements the generic flash manipulation routines.
|
|
*
|
|
* The file 'flash-sw-spec.txt' should provide *all* the information needed
|
|
* to understand and work with the FLITF and flash.
|
|
*
|
|
*
|
|
* <!--(C) COPYRIGHT 2010 STMicroelectronics. All rights reserved. -->
|
|
*/
|
|
|
|
#include PLATFORM_HEADER
|
|
#include "error.h"
|
|
#include "hal/micro/cortexm3/bootloader/fib-bootloader.h"
|
|
#include "hal/micro/cortexm3/mpu.h"
|
|
#include "memmap.h"
|
|
#include "flash.h"
|
|
|
|
#ifdef FLASH_PROGRAMMING_WITH_EMPTY_FIB
|
|
#define ST_EMU_TEST
|
|
#endif
|
|
// A translation table used to convert FibStatus codes to corresponding
|
|
// StStatus values
|
|
static const StStatus fibToStStatus[] = {
|
|
ST_SUCCESS, // FIB_SUCCESS 0
|
|
ST_BAD_ARGUMENT, // FIB_ERR_UNALIGNED 1
|
|
ST_BAD_ARGUMENT, // FIB_ERR_INVALID_ADDRESS 2
|
|
ST_BAD_ARGUMENT, // FIB_ERR_INVALID_TYPE 3
|
|
ST_ERR_FLASH_PROG_FAIL, // FIB_ERR_WRITE_PROTECTED 4
|
|
ST_ERR_FLASH_PROG_FAIL, // FIB_ERR_WRITE_FAILED 5
|
|
ST_ERR_FLASH_WRITE_INHIBITED, // FIB_ERR_ERASE_REQUIRED 6
|
|
ST_ERR_FLASH_VERIFY_FAILED // FIB_ERR_VERIFY_FAILED 7
|
|
};
|
|
|
|
//The purpose of flashEraseIsActive and halFlashEraseIsActive() is so that
|
|
//interrupts can query the flash library to find out of Flash Erase is
|
|
//active when their ISR gets invoked. This is useful because Flash Erase
|
|
//causes the chip to go ATOMIC for 21ms and this delay will disrupt interrupt
|
|
//latency. By having a sinple API that an ISR can query for this state,
|
|
//the ISR can appriopriately adjust for a 21ms latency time.
|
|
boolean flashEraseIsActive = FALSE;
|
|
boolean halFlashEraseIsActive(void)
|
|
{
|
|
return flashEraseIsActive;
|
|
}
|
|
|
|
|
|
// Emulators do not have FIB bootloaders, so need to include a copy of
|
|
// these core flash routines.
|
|
|
|
#if defined(ST_EMU_TEST)
|
|
|
|
static void enableFlitf(void)
|
|
{
|
|
//First, unlock the FLITF by writing the two key values to the Flash
|
|
//Protection Unlock register
|
|
FPEC_KEY = FPEC_KEY1;
|
|
FPEC_KEY = FPEC_KEY2;
|
|
|
|
//Second, unlock the CIB by writing the two key values to the CIB
|
|
//Protection Unlock register
|
|
OPT_KEY = FPEC_KEY1;
|
|
OPT_KEY = FPEC_KEY2;
|
|
|
|
//Turn on the FPEC clock for flash manipulation operations
|
|
FPEC_CLKREQ = FPEC_CLKREQ_FIELD;
|
|
|
|
//make sure the FPEC clock is running before we proceed
|
|
while( (FPEC_CLKSTAT&FPEC_CLKACK) != FPEC_CLKACK) {}
|
|
|
|
//just in case, wait until the flash is no longer busy
|
|
while( (FLASH_STATUS&FLASH_STATUS_FLA_BSY) == FLASH_STATUS_FLA_BSY ) {}
|
|
}
|
|
|
|
static void disableFlitf(void)
|
|
{
|
|
//make sure the FPEC is completely idle before turning off the clock
|
|
while( (FPEC_CLKSTAT&FPEC_CLKBSY) == FPEC_CLKBSY) {}
|
|
|
|
//Turn off the FPEC clock now that we're done
|
|
FPEC_CLKREQ = FPEC_CLKREQ_RESET;
|
|
|
|
//Set LOCK and clear OPTWREN to lock both the FLITF and the CIB.
|
|
//NOTE: The PROG bit must also be cleared otherwise Flash can still
|
|
// be programmed even with the LOCK bit set. BugzID: 6267
|
|
FLASH_CTRL = FLASH_CTRL_LOCK; //lock the flash from further accesses
|
|
}
|
|
|
|
static FibStatus fibFlashWrite(int32u address, int8u *data, int32u length, int32u dummy)
|
|
{
|
|
int32u i;
|
|
int16u *ptr;
|
|
FibStatus status = FIB_SUCCESS;
|
|
// Address and length must be half-word aligned.
|
|
if ((address & 1) || (length & 1)) {
|
|
return FIB_ERR_UNALIGNED;
|
|
}
|
|
// Start and end address must be in MFB or CIB.
|
|
if (!((address >= MFB_BOTTOM && address + length <= MFB_TOP + 1)
|
|
|| (address >= CIB_BOTTOM && address + length <= CIB_TOP + 1))) {
|
|
return FIB_ERR_INVALID_ADDRESS;
|
|
}
|
|
enableFlitf();
|
|
ptr = (int16u *)address;
|
|
for (i = 0; i < length; i += 2) {
|
|
int16u currentData = *ptr;
|
|
int16u newData = HIGH_LOW_TO_INT(data[i + 1], data[i]);
|
|
// Only program the data if it makes sense to do so.
|
|
if (currentData == newData) {
|
|
// If the new data matches the flash, don't bother doing anything.
|
|
} else if (currentData == 0xFFFF || newData == 0x0000) {
|
|
// If the flash is 0xFFFF we're allowed to write anything.
|
|
// If the new data is 0x0000 it doesn't matter what the flash is.
|
|
// OPTWREN must stay set to keep CIB unlocked.
|
|
if ((CIB_OB_BOTTOM <= (int32u)ptr) && ((int32u)ptr <= CIB_OB_TOP)) {
|
|
FLASH_CTRL = (FLASH_CTRL_OPTWREN | FLASH_CTRL_OPTPROG);
|
|
} else {
|
|
FLASH_CTRL = (FLASH_CTRL_OPTWREN | FLASH_CTRL_PROG);
|
|
}
|
|
// Assigning data to the address performs the actual write.
|
|
(*ptr) = newData;
|
|
// Wait for the busy bit to clear, indicating operation is done.
|
|
while ((FLASH_STATUS & FLASH_STATUS_FLA_BSY) != 0) {}
|
|
// Reset the operation complete flag.
|
|
FLASH_STATUS = FLASH_STATUS_EOP;
|
|
// Check if any error bits have been tripped, and if so, exit.
|
|
// The bit PAGE_PROG_ERR is not relevant in this programming mode.
|
|
if (FLASH_STATUS & (FLASH_STATUS_WRP_ERR | FLASH_STATUS_PROG_ERR)) {
|
|
if (FLASH_STATUS & FLASH_STATUS_WRP_ERR) {
|
|
status = FIB_ERR_WRITE_PROTECTED;
|
|
} else {
|
|
status = FIB_ERR_WRITE_FAILED;
|
|
}
|
|
FLASH_STATUS = FLASH_STATUS_WRP_ERR;
|
|
FLASH_STATUS = FLASH_STATUS_PROG_ERR;
|
|
break;
|
|
}
|
|
} else {
|
|
status = FIB_ERR_ERASE_REQUIRED;
|
|
break;
|
|
}
|
|
ptr++;
|
|
}
|
|
disableFlitf();
|
|
return status;
|
|
}
|
|
|
|
static FibStatus fibFlashWriteVerify(int32u address, int8u *data, int32u length)
|
|
{
|
|
int32u i;
|
|
int8u *ptr = (int8u *)address;
|
|
for (i = 0; i < length; i++) {
|
|
if (*ptr != data[i]) {
|
|
return FIB_ERR_VERIFY_FAILED;
|
|
}
|
|
ptr++;
|
|
}
|
|
return FIB_SUCCESS;
|
|
}
|
|
|
|
static FibStatus fibFlashErase(FibEraseType eraseType, int32u address)
|
|
{
|
|
int32u eraseOp;
|
|
int32u *ptr;
|
|
int32u length;
|
|
FibStatus status = FIB_SUCCESS;
|
|
if (BYTE_0(eraseType) == MFB_MASS_ERASE) {
|
|
eraseOp = FLASH_CTRL_MASSERASE;
|
|
ptr = (int32u *)MFB_BOTTOM;
|
|
length = MFB_SIZE_W;
|
|
} else if (BYTE_0(eraseType) == MFB_PAGE_ERASE) {
|
|
if (address < MFB_BOTTOM || address > MFB_TOP) {
|
|
return FIB_ERR_INVALID_ADDRESS;
|
|
}
|
|
eraseOp = FLASH_CTRL_PAGEERASE;
|
|
ptr = (int32u *)(address & MFB_PAGE_MASK_B);
|
|
length = MFB_PAGE_SIZE_W;
|
|
} else if (BYTE_0(eraseType) == CIB_ERASE) {
|
|
eraseOp = FLASH_CTRL_OPTWREN | FLASH_CTRL_OPTERASE;
|
|
ptr = (int32u *)CIB_BOTTOM;
|
|
length = CIB_SIZE_W;
|
|
} else {
|
|
return FIB_ERR_INVALID_TYPE;
|
|
}
|
|
if ((eraseType & DO_ERASE) != 0) {
|
|
enableFlitf();
|
|
FLASH_CTRL = eraseOp;
|
|
if (BYTE_0(eraseType) == MFB_PAGE_ERASE) {
|
|
FLASH_ADDR = (address & MFB_PAGE_MASK_B);
|
|
}
|
|
eraseOp |= FLASH_CTRL_FLA_START;
|
|
// Perform the actual erase.
|
|
FLASH_CTRL = eraseOp;
|
|
// Wait for the busy bit to clear, indicating operation is done.
|
|
while ((FLASH_STATUS & FLASH_STATUS_FLA_BSY) != 0) {}
|
|
// Reset the operation complete flag.
|
|
FLASH_STATUS = FLASH_STATUS_EOP;
|
|
// Check for errors; the only relevant one for erasing is write protection.
|
|
if (FLASH_STATUS & FLASH_STATUS_WRP_ERR) {
|
|
FLASH_STATUS = FLASH_STATUS_WRP_ERR;
|
|
status = FIB_ERR_WRITE_PROTECTED;
|
|
}
|
|
disableFlitf();
|
|
}
|
|
if (status == FIB_SUCCESS
|
|
&& (eraseType & DO_VERIFY) != 0) {
|
|
int32u i;
|
|
for (i = 0; i < length; i++) {
|
|
if (*ptr != 0xFFFFFFFF) {
|
|
return FIB_ERR_VERIFY_FAILED;
|
|
}
|
|
ptr++;
|
|
}
|
|
}
|
|
return status;
|
|
}
|
|
#endif // ST_EMU_TEST
|
|
|
|
static boolean verifyFib(void)
|
|
{
|
|
// Ensure that a programmed FIB of a proper version is present
|
|
return ( (halFixedAddressTable.baseTable.type == FIXED_ADDRESS_TABLE_TYPE) &&
|
|
( ( (halFixedAddressTable.baseTable.version & FAT_MAJOR_VERSION_MASK)
|
|
== 0x0000 ) &&
|
|
(halFixedAddressTable.baseTable.version >= 0x0002)
|
|
)
|
|
);
|
|
}
|
|
|
|
//The parameter 'eraseType' chooses which erasure will be performed while
|
|
//the 'address' parameter chooses the page to be erased during MFB page erase.
|
|
StStatus halInternalFlashErase(int8u eraseType, int32u address)
|
|
{
|
|
FibStatus status;
|
|
|
|
ATOMIC(
|
|
BYPASS_MPU(
|
|
flashEraseIsActive = TRUE;
|
|
#if defined(ST_EMU_TEST)
|
|
// Always try to use the FIB bootloader if its present
|
|
if(verifyFib()) {
|
|
status = halFixedAddressTable.fibFlashErase(
|
|
(((int32u)eraseType) | DO_ERASE),
|
|
address);
|
|
} else {
|
|
status = fibFlashErase((((int32u)eraseType) | DO_ERASE), address);
|
|
}
|
|
#else
|
|
|
|
|
|
|
|
assert(verifyFib());
|
|
status = halFixedAddressTable.fibFlashErase(
|
|
(((int32u)eraseType) | DO_ERASE),
|
|
address);
|
|
#endif
|
|
)
|
|
)
|
|
//If there are any interrupts pending that could have been delayed for 21ms,
|
|
//they will be serviced here since we exit the ATOMIC block. These ISRs
|
|
//can query the flash library and find out that erasing is active. After
|
|
//this point, we're no longer ATOMIC/disrupting latency so our erase
|
|
//active flag should be cleared.
|
|
flashEraseIsActive = FALSE;
|
|
|
|
if(status!=FIB_SUCCESS) {
|
|
return fibToStStatus[status];
|
|
}
|
|
|
|
#if defined(ST_EMU_TEST)
|
|
// Always try to use the FIB bootloader if its present
|
|
if(verifyFib()) {
|
|
status = halFixedAddressTable.fibFlashErase(
|
|
(((int32u)eraseType) | DO_VERIFY),
|
|
address);
|
|
} else {
|
|
status = fibFlashErase((((int32u)eraseType) | DO_VERIFY), address);
|
|
}
|
|
#else
|
|
status = halFixedAddressTable.fibFlashErase(
|
|
(((int32u)eraseType) | DO_VERIFY),
|
|
address);
|
|
#endif
|
|
return fibToStStatus[status];
|
|
}
|
|
|
|
|
|
//The parameter 'address' defines the starting address of where the
|
|
//programming will occur - this parameter MUST be half-word aligned since all
|
|
//programming operations are HW. The parameter 'data' is a pointer to a buffer
|
|
//containin the 16bit half-words to be written. Length is the number of 16bit
|
|
//half-words contained in 'data' to be written to flash.
|
|
//NOTE: This function can NOT write the option bytes and will throw an error
|
|
//if that is attempted.
|
|
StStatus halInternalFlashWrite(int32u address, int16u * data, int32u length)
|
|
{
|
|
FibStatus status;
|
|
|
|
length = length * 2; // fib routines specify length in bytes
|
|
|
|
ATOMIC(
|
|
BYPASS_MPU(
|
|
#if defined(ST_EMU_TEST)
|
|
// Always try to use the FIB bootloader if its present
|
|
if(verifyFib()) {
|
|
status = halFixedAddressTable.fibFlashWrite(address,
|
|
(int8u *)data,
|
|
length,
|
|
0);
|
|
} else {
|
|
status = fibFlashWrite(address, (int8u *)data, length, 0);
|
|
}
|
|
#else
|
|
|
|
|
|
|
|
// Ensure that a programmed FIB of a proper version is present
|
|
assert(verifyFib());
|
|
status = halFixedAddressTable.fibFlashWrite(address,
|
|
(int8u *)data,
|
|
length,
|
|
0);
|
|
#endif
|
|
)
|
|
)
|
|
|
|
if(status!=FIB_SUCCESS) {
|
|
return fibToStStatus[status];
|
|
}
|
|
|
|
#if defined(ST_EMU_TEST)
|
|
// Always try to use the FIB bootloader if its present
|
|
if(verifyFib()) {
|
|
status = halFixedAddressTable.fibFlashWrite(address,
|
|
(int8u *)data,
|
|
0,
|
|
length);
|
|
} else {
|
|
status = fibFlashWriteVerify(address, (int8u *)data, length);
|
|
}
|
|
#else
|
|
status = halFixedAddressTable.fibFlashWrite(address,
|
|
(int8u *)data,
|
|
0,
|
|
length);
|
|
#endif
|
|
|
|
return fibToStStatus[status];
|
|
}
|
|
|
|
|
|
//The parameter 'byte' is the option byte number to be programmed. This
|
|
//parameter can have a value of 0 through 7. 'data' is the 8bit value to be
|
|
//programmed into the option byte since the hardware will calculate the
|
|
//compliment and program the full 16bit option byte.
|
|
StStatus halInternalCibOptionByteWrite(int8u byte, int8u data)
|
|
{
|
|
int16u dataAndInverse = HIGH_LOW_TO_INT(~data, data);
|
|
// There are only 8 option bytes, don't try to program more than that.
|
|
if (byte > 7) {
|
|
return ST_ERR_FLASH_PROG_FAIL;
|
|
}
|
|
return halInternalFlashWrite(CIB_OB_BOTTOM + (byte << 1), &dataAndInverse, 1);
|
|
}
|
|
|
|
|