#define FLASH_SECTOR_SIZE 4096
#define FLASH_BANK0_ADDRESS 0x08000000
+/* option bytes */
+#define OPTION_BYTES_ADDRESS 0x1FF80000
+
+#define OPTION_BYTE_0_PR1 0xFFFF0000
+#define OPTION_BYTE_0_PR0 0xFF5500AA
+
static int stm32lx_unlock_program_memory(struct flash_bank *bank);
static int stm32lx_lock_program_memory(struct flash_bank *bank);
static int stm32lx_enable_write_half_page(struct flash_bank *bank);
static int stm32lx_erase_sector(struct flash_bank *bank, int sector);
static int stm32lx_wait_until_bsy_clear(struct flash_bank *bank);
+static int stm32lx_mass_erase(struct flash_bank *bank);
+static int stm32lx_wait_until_bsy_clear_timeout(struct flash_bank *bank, int timeout);
struct stm32lx_rev {
uint16_t rev;
};
static const struct stm32lx_rev stm32_416_revs[] = {
- { 0x1000, "A" }, { 0x1008, "Y" }, { 0x1018, "X" }, { 0x1038, "W" },
- { 0x1078, "V" },
+ { 0x1000, "A" }, { 0x1008, "Y" }, { 0x1038, "W" }, { 0x1078, "V" },
};
static const struct stm32lx_rev stm32_417_revs[] = {
{ 0x1000, "A" }, { 0x1008, "Z" },
};
static const struct stm32lx_rev stm32_427_revs[] = {
- { 0x1018, "A" },
+ { 0x1000, "A" }, { 0x1018, "Y" }, { 0x1038, "X" },
+};
+static const struct stm32lx_rev stm32_429_revs[] = {
+ { 0x1000, "A" }, { 0x1018, "Z" },
};
static const struct stm32lx_rev stm32_436_revs[] = {
{ 0x1000, "A" }, { 0x1008, "Z" }, { 0x1018, "Y" },
};
+static const struct stm32lx_rev stm32_437_revs[] = {
+ { 0x1000, "A" },
+};
static const struct stm32lx_part_info stm32lx_parts[] = {
{
.id = 0x416,
.revs = stm32_416_revs,
.num_revs = ARRAY_SIZE(stm32_416_revs),
- .device_str = "STM32L1xx (Low/Medium Density)",
+ .device_str = "STM32L1xx (Cat.1 - Low/Medium Density)",
.page_size = 256,
.pages_per_sector = 16,
.max_flash_size_kb = 128,
.id = 0x427,
.revs = stm32_427_revs,
.num_revs = ARRAY_SIZE(stm32_427_revs),
- .device_str = "STM32L1xx (Medium+ Density)",
+ .device_str = "STM32L1xx (Cat.3 - Medium+ Density)",
.page_size = 256,
.pages_per_sector = 16,
.max_flash_size_kb = 256,
.flash_base = 0x40023C00,
.fsize_base = 0x1FF800CC,
},
+ {
+ .id = 0x429,
+ .revs = stm32_429_revs,
+ .num_revs = ARRAY_SIZE(stm32_429_revs),
+ .device_str = "STM32L1xx (Cat.2)",
+ .page_size = 256,
+ .pages_per_sector = 16,
+ .max_flash_size_kb = 128,
+ .has_dual_banks = false,
+ .flash_base = 0x40023C00,
+ .fsize_base = 0x1FF8004C,
+ },
{
.id = 0x436,
.revs = stm32_436_revs,
.num_revs = ARRAY_SIZE(stm32_436_revs),
- .device_str = "STM32L1xx (Medium+/High Density)",
+ .device_str = "STM32L1xx (Cat.4/Cat.3 - Medium+/High Density)",
.page_size = 256,
.pages_per_sector = 16,
.max_flash_size_kb = 384,
},
{
.id = 0x437,
- .device_str = "STM32L1xx (Medium+/High Density)",
+ .revs = stm32_437_revs,
+ .num_revs = ARRAY_SIZE(stm32_437_revs),
+ .device_str = "STM32L1xx (Cat.5/Cat.6)",
.page_size = 256,
.pages_per_sector = 16,
.max_flash_size_kb = 512,
return ERROR_OK;
}
+COMMAND_HANDLER(stm32lx_handle_mass_erase_command)
+{
+ int i;
+
+ if (CMD_ARGC < 1)
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ struct flash_bank *bank;
+ int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
+ if (ERROR_OK != retval)
+ return retval;
+
+ retval = stm32lx_mass_erase(bank);
+ if (retval == ERROR_OK) {
+ /* set all sectors as erased */
+ for (i = 0; i < bank->num_sectors; i++)
+ bank->sectors[i].is_erased = 1;
+
+ command_print(CMD_CTX, "stm32lx mass erase complete");
+ } else {
+ command_print(CMD_CTX, "stm32lx mass erase failed");
+ }
+
+ return retval;
+}
+
static int stm32lx_protect_check(struct flash_bank *bank)
{
int retval;
0x93, 0x42, /* cmp r3, r2 */
0xf8, 0xd3, /* bcc write_word */
0x00, 0xbe, /* bkpt 0 */
-
};
/* Make sure we're performing a half-page aligned write. */
*/
second_bank_base = base_address +
stm32lx_info->part_info->first_bank_size_kb * 1024;
- if (bank->base == second_bank_base) {
+ if (bank->base == second_bank_base || !bank->base) {
/* This is the second bank */
base_address = second_bank_base;
flash_size_in_kb = flash_size_in_kb -
stm32lx_info->part_info->first_bank_size_kb;
- } else if (bank->base == 0 || bank->base == base_address) {
+ } else if (bank->base == base_address) {
/* This is the first bank */
flash_size_in_kb = stm32lx_info->part_info->first_bank_size_kb;
} else {
}
}
-
const struct stm32lx_part_info *info = stm32lx_info->part_info;
if (info) {
}
static const struct command_registration stm32lx_exec_command_handlers[] = {
+ {
+ .name = "mass_erase",
+ .handler = stm32lx_handle_mass_erase_command,
+ .mode = COMMAND_EXEC,
+ .usage = "bank_id",
+ .help = "Erase entire flash device. including available EEPROM",
+ },
COMMAND_REGISTRATION_DONE
};
return ERROR_OK;
}
+static inline int stm32lx_get_flash_status(struct flash_bank *bank, uint32_t *status)
+{
+ struct target *target = bank->target;
+ struct stm32lx_flash_bank *stm32lx_info = bank->driver_priv;
+
+ return target_read_u32(target, stm32lx_info->flash_base + FLASH_SR, status);
+}
+
static int stm32lx_wait_until_bsy_clear(struct flash_bank *bank)
+{
+ return stm32lx_wait_until_bsy_clear_timeout(bank, 100);
+}
+
+static int stm32lx_unlock_options_bytes(struct flash_bank *bank)
+{
+ struct target *target = bank->target;
+ struct stm32lx_flash_bank *stm32lx_info = bank->driver_priv;
+ int retval;
+ uint32_t reg32;
+
+ /*
+ * Unlocking the options bytes is done by unlocking the PECR,
+ * then by writing the 2 FLASH_PEKEYR to the FLASH_OPTKEYR register
+ */
+
+ /* check flash is not already unlocked */
+ retval = target_read_u32(target, stm32lx_info->flash_base + FLASH_PECR, ®32);
+ if (retval != ERROR_OK)
+ return retval;
+
+ if ((reg32 & FLASH_PECR__OPTLOCK) == 0)
+ return ERROR_OK;
+
+ if ((reg32 & FLASH_PECR__PELOCK) != 0) {
+
+ retval = target_write_u32(target, stm32lx_info->flash_base + FLASH_PEKEYR, PEKEY1);
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = target_write_u32(target, stm32lx_info->flash_base + FLASH_PEKEYR, PEKEY2);
+ if (retval != ERROR_OK)
+ return retval;
+ }
+
+ /* To unlock the PECR write the 2 OPTKEY to the FLASH_OPTKEYR register */
+ retval = target_write_u32(target, stm32lx_info->flash_base + FLASH_OPTKEYR, OPTKEY1);
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = target_write_u32(target, stm32lx_info->flash_base + FLASH_OPTKEYR, OPTKEY2);
+ if (retval != ERROR_OK)
+ return retval;
+
+ return ERROR_OK;
+}
+
+static int stm32lx_wait_until_bsy_clear_timeout(struct flash_bank *bank, int timeout)
{
struct target *target = bank->target;
struct stm32lx_flash_bank *stm32lx_info = bank->driver_priv;
uint32_t status;
int retval = ERROR_OK;
- int timeout = 100;
/* wait for busy to clear */
for (;;) {
- retval = target_read_u32(target, stm32lx_info->flash_base + FLASH_SR,
- &status);
+ retval = stm32lx_get_flash_status(bank, &status);
if (retval != ERROR_OK)
return retval;
+ LOG_DEBUG("status: 0x%" PRIx32 "", status);
if ((status & FLASH_SR__BSY) == 0)
break;
+
if (timeout-- <= 0) {
LOG_ERROR("timed out waiting for flash");
return ERROR_FAIL;
retval = ERROR_FAIL;
}
+ /* Clear but report errors */
+ if (status & FLASH_SR__OPTVERR) {
+ /* If this operation fails, we ignore it and report the original retval */
+ target_write_u32(target, stm32lx_info->flash_base + FLASH_SR, status & FLASH_SR__OPTVERR);
+ }
+
return retval;
}
+
+static int stm32lx_obl_launch(struct flash_bank *bank)
+{
+ struct target *target = bank->target;
+ struct stm32lx_flash_bank *stm32lx_info = bank->driver_priv;
+ int retval;
+
+ /* This will fail as the target gets immediately rebooted */
+ target_write_u32(target, stm32lx_info->flash_base + FLASH_PECR,
+ FLASH_PECR__OBL_LAUNCH);
+
+ size_t tries = 10;
+ do {
+ target_halt(target);
+ retval = target_poll(target);
+ } while (--tries > 0 &&
+ (retval != ERROR_OK || target->state != TARGET_HALTED));
+
+ return tries ? ERROR_OK : ERROR_FAIL;
+}
+
+static int stm32lx_mass_erase(struct flash_bank *bank)
+{
+ int retval;
+ struct target *target = bank->target;
+ struct stm32lx_flash_bank *stm32lx_info = NULL;
+ uint32_t reg32;
+
+ if (target->state != TARGET_HALTED) {
+ LOG_ERROR("Target not halted");
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ stm32lx_info = bank->driver_priv;
+
+ retval = stm32lx_unlock_options_bytes(bank);
+ if (retval != ERROR_OK)
+ return retval;
+
+ /* mass erase flash memory */
+ /* set the RDP protection level to 1 */
+ retval = target_write_u32(target, OPTION_BYTES_ADDRESS, OPTION_BYTE_0_PR1);
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = stm32lx_obl_launch(bank);
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = stm32lx_unlock_options_bytes(bank);
+ if (retval != ERROR_OK)
+ return retval;
+
+ /* set the RDP protection level to 0 */
+ retval = target_write_u32(target, OPTION_BYTES_ADDRESS, OPTION_BYTE_0_PR0);
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = stm32lx_wait_until_bsy_clear_timeout(bank, 30000);
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = stm32lx_obl_launch(bank);
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = target_read_u32(target, stm32lx_info->flash_base + FLASH_PECR, ®32);
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = target_write_u32(target, stm32lx_info->flash_base + FLASH_PECR, reg32 | FLASH_PECR__OPTLOCK);
+ if (retval != ERROR_OK)
+ return retval;
+
+ return ERROR_OK;
+}