osd-contiki/cpu/stm32w108/hal/micro/cortexm3/flash.c

365 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);
}