X-Git-Url: https://review.openocd.org/gitweb?a=blobdiff_plain;f=src%2Fflash%2Fnor%2Fstm32lx.c;h=7b0b0cc1daff595c708a15390e14b4f79c167848;hb=cc50a428829d6feaf9b6eb2a796c4a834bd6368e;hp=f7074c3eea18ffe58597cff912b1ebc0af4a7f1e;hpb=3751214b9cecf69d729985c05361b5bd5441d24a;p=openocd.git diff --git a/src/flash/nor/stm32lx.c b/src/flash/nor/stm32lx.c index f7074c3eea..7b0b0cc1da 100644 --- a/src/flash/nor/stm32lx.c +++ b/src/flash/nor/stm32lx.c @@ -92,11 +92,19 @@ #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; @@ -128,25 +136,30 @@ struct stm32lx_flash_bank { }; 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, @@ -170,7 +183,7 @@ static const struct stm32lx_part_info stm32lx_parts[] = { .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, @@ -179,11 +192,23 @@ static const struct stm32lx_part_info stm32lx_parts[] = { .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, @@ -194,7 +219,9 @@ static const struct stm32lx_part_info stm32lx_parts[] = { }, { .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, @@ -214,7 +241,7 @@ FLASH_BANK_COMMAND_HANDLER(stm32lx_flash_bank_command) return ERROR_COMMAND_SYNTAX_ERROR; /* Create the bank structure */ - stm32lx_info = malloc(sizeof(struct stm32lx_flash_bank)); + stm32lx_info = calloc(1, sizeof(*stm32lx_info)); /* Check allocation */ if (stm32lx_info == NULL) { @@ -233,6 +260,32 @@ FLASH_BANK_COMMAND_HANDLER(stm32lx_flash_bank_command) 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; @@ -325,7 +378,6 @@ static int stm32lx_write_half_pages(struct flash_bank *bank, const uint8_t *buff 0x93, 0x42, /* cmp r3, r2 */ 0xf8, 0xd3, /* bcc write_word */ 0x00, 0xbe, /* bkpt 0 */ - }; /* Make sure we're performing a half-page aligned write. */ @@ -624,6 +676,7 @@ static int stm32lx_probe(struct flash_bank *bank) uint32_t second_bank_base; stm32lx_info->probed = 0; + stm32lx_info->part_info = NULL; int retval = stm32lx_read_id_code(bank->target, &device_id); if (retval != ERROR_OK) @@ -678,12 +731,12 @@ static int stm32lx_probe(struct flash_bank *bank) */ 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 { @@ -808,7 +861,6 @@ static int stm32lx_get_info(struct flash_bank *bank, char *buf, int buf_size) } } - const struct stm32lx_part_info *info = stm32lx_info->part_info; if (info) { @@ -838,6 +890,13 @@ static int stm32lx_get_info(struct flash_bank *bank, char *buf, int buf_size) } 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 }; @@ -1052,23 +1111,79 @@ static int stm32lx_erase_sector(struct flash_bank *bank, int sector) 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; @@ -1086,5 +1201,87 @@ static int stm32lx_wait_until_bsy_clear(struct flash_bank *bank) 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; +}