From b8fd9aecb12a5d930cf3e3e09cede4cd6787e847 Mon Sep 17 00:00:00 2001 From: Tarek BOCHKATI Date: Sun, 22 Mar 2020 20:39:44 +0100 Subject: [PATCH] stm32l4x: add OTP support for STM32 G0/G4/L4/L4+/L5/WB/WL devices this is a rework of #5320 started by Andreas then abandoned. same syntax as in stm32f2x driver: enable OTP for writing > stm32l4x otp 1 enable write to OTP > flash write_bank 1 foo.bin 0 > flash filld 0x1FFF7000 0xDeadBeafBaadF00d 1 read OTP > mdw 0x1FFF7000 4 disable OTP > stm32l4x otp 1 disable Change-Id: Id7d7c163b35d7a3f406dc200d7e2fc293b0675c2 Signed-off-by: Andreas Bolsch Signed-off-by: Tarek BOCHKATI Reviewed-on: http://openocd.zylin.com/5537 Tested-by: jenkins Reviewed-by: Tomas Vanek --- doc/openocd.texi | 11 +++ src/flash/nor/stm32l4x.c | 160 ++++++++++++++++++++++++++++++++++++++- tcl/target/stm32g0x.cfg | 4 +- tcl/target/stm32g4x.cfg | 4 +- tcl/target/stm32l4x.cfg | 4 +- tcl/target/stm32l5x.cfg | 3 +- tcl/target/stm32wbx.cfg | 4 +- tcl/target/stm32wlx.cfg | 4 +- 8 files changed, 182 insertions(+), 12 deletions(-) diff --git a/doc/openocd.texi b/doc/openocd.texi index b3fe854043..6cce952d43 100644 --- a/doc/openocd.texi +++ b/doc/openocd.texi @@ -7179,6 +7179,17 @@ the chip identification register, and autoconfigures itself. flash bank $_FLASHNAME stm32l4x 0 0 0 0 $_TARGETNAME @end example +If you use OTP (One-Time Programmable) memory define it as a second bank +as per the following example. +@example +flash bank $_FLASHNAME stm32l4x 0x1FFF7000 0 0 0 $_TARGETNAME +@end example + +@deffn Command {stm32l4x otp} num (@option{enable}|@option{disable}|@option{show}) +Enables or disables OTP write commands for bank @var{num}. +The @var{num} parameter is a value shown by @command{flash banks}. +@end deffn + Note that some devices have been found that have a flash size register that contains an invalid value, to workaround this issue you can override the probed value used by the flash driver. However, specifying a wrong value might lead to a completely diff --git a/src/flash/nor/stm32l4x.c b/src/flash/nor/stm32l4x.c index 89c5ac3fe2..492d0dde13 100644 --- a/src/flash/nor/stm32l4x.c +++ b/src/flash/nor/stm32l4x.c @@ -171,6 +171,8 @@ struct stm32l4_part_info { const uint32_t flash_regs_base; const uint32_t *default_flash_regs; const uint32_t fsize_addr; + const uint32_t otp_base; + const uint32_t otp_size; }; struct stm32l4_flash_bank { @@ -183,6 +185,7 @@ struct stm32l4_flash_bank { uint32_t wrpxxr_mask; const struct stm32l4_part_info *part_info; const uint32_t *flash_regs; + bool otp_enabled; }; /* human readable list of families this drivers supports (sorted alphabetically) */ @@ -263,6 +266,8 @@ static const struct stm32l4_part_info stm32l4_parts[] = { .flash_regs_base = 0x40022000, .default_flash_regs = stm32l4_flash_regs, .fsize_addr = 0x1FFF75E0, + .otp_base = 0x1FFF7000, + .otp_size = 1024, }, { .id = 0x435, @@ -274,6 +279,8 @@ static const struct stm32l4_part_info stm32l4_parts[] = { .flash_regs_base = 0x40022000, .default_flash_regs = stm32l4_flash_regs, .fsize_addr = 0x1FFF75E0, + .otp_base = 0x1FFF7000, + .otp_size = 1024, }, { .id = 0x460, @@ -285,6 +292,8 @@ static const struct stm32l4_part_info stm32l4_parts[] = { .flash_regs_base = 0x40022000, .default_flash_regs = stm32l4_flash_regs, .fsize_addr = 0x1FFF75E0, + .otp_base = 0x1FFF7000, + .otp_size = 1024, }, { .id = 0x461, @@ -296,6 +305,8 @@ static const struct stm32l4_part_info stm32l4_parts[] = { .flash_regs_base = 0x40022000, .default_flash_regs = stm32l4_flash_regs, .fsize_addr = 0x1FFF75E0, + .otp_base = 0x1FFF7000, + .otp_size = 1024, }, { .id = 0x462, @@ -307,6 +318,8 @@ static const struct stm32l4_part_info stm32l4_parts[] = { .flash_regs_base = 0x40022000, .default_flash_regs = stm32l4_flash_regs, .fsize_addr = 0x1FFF75E0, + .otp_base = 0x1FFF7000, + .otp_size = 1024, }, { .id = 0x464, @@ -318,6 +331,8 @@ static const struct stm32l4_part_info stm32l4_parts[] = { .flash_regs_base = 0x40022000, .default_flash_regs = stm32l4_flash_regs, .fsize_addr = 0x1FFF75E0, + .otp_base = 0x1FFF7000, + .otp_size = 1024, }, { .id = 0x466, @@ -329,6 +344,8 @@ static const struct stm32l4_part_info stm32l4_parts[] = { .flash_regs_base = 0x40022000, .default_flash_regs = stm32l4_flash_regs, .fsize_addr = 0x1FFF75E0, + .otp_base = 0x1FFF7000, + .otp_size = 1024, }, { .id = 0x468, @@ -340,6 +357,8 @@ static const struct stm32l4_part_info stm32l4_parts[] = { .flash_regs_base = 0x40022000, .default_flash_regs = stm32l4_flash_regs, .fsize_addr = 0x1FFF75E0, + .otp_base = 0x1FFF7000, + .otp_size = 1024, }, { .id = 0x469, @@ -351,6 +370,8 @@ static const struct stm32l4_part_info stm32l4_parts[] = { .flash_regs_base = 0x40022000, .default_flash_regs = stm32l4_flash_regs, .fsize_addr = 0x1FFF75E0, + .otp_base = 0x1FFF7000, + .otp_size = 1024, }, { .id = 0x470, @@ -362,6 +383,8 @@ static const struct stm32l4_part_info stm32l4_parts[] = { .flash_regs_base = 0x40022000, .default_flash_regs = stm32l4_flash_regs, .fsize_addr = 0x1FFF75E0, + .otp_base = 0x1FFF7000, + .otp_size = 1024, }, { .id = 0x471, @@ -373,6 +396,8 @@ static const struct stm32l4_part_info stm32l4_parts[] = { .flash_regs_base = 0x40022000, .default_flash_regs = stm32l4_flash_regs, .fsize_addr = 0x1FFF75E0, + .otp_base = 0x1FFF7000, + .otp_size = 1024, }, { .id = 0x472, @@ -384,6 +409,8 @@ static const struct stm32l4_part_info stm32l4_parts[] = { .flash_regs_base = 0x40022000, .default_flash_regs = stm32l5_ns_flash_regs, .fsize_addr = 0x0BFA05E0, + .otp_base = 0x0BFA0000, + .otp_size = 512, }, { .id = 0x479, @@ -395,6 +422,8 @@ static const struct stm32l4_part_info stm32l4_parts[] = { .flash_regs_base = 0x40022000, .default_flash_regs = stm32l4_flash_regs, .fsize_addr = 0x1FFF75E0, + .otp_base = 0x1FFF7000, + .otp_size = 1024, }, { .id = 0x495, @@ -406,6 +435,8 @@ static const struct stm32l4_part_info stm32l4_parts[] = { .flash_regs_base = 0x58004000, .default_flash_regs = stm32l4_flash_regs, .fsize_addr = 0x1FFF75E0, + .otp_base = 0x1FFF7000, + .otp_size = 1024, }, { .id = 0x496, @@ -417,6 +448,8 @@ static const struct stm32l4_part_info stm32l4_parts[] = { .flash_regs_base = 0x58004000, .default_flash_regs = stm32l4_flash_regs, .fsize_addr = 0x1FFF75E0, + .otp_base = 0x1FFF7000, + .otp_size = 1024, }, { .id = 0x497, @@ -428,6 +461,8 @@ static const struct stm32l4_part_info stm32l4_parts[] = { .flash_regs_base = 0x58004000, .default_flash_regs = stm32l4_flash_regs, .fsize_addr = 0x1FFF75E0, + .otp_base = 0x1FFF7000, + .otp_size = 1024, }, }; @@ -439,6 +474,10 @@ FLASH_BANK_COMMAND_HANDLER(stm32l4_flash_bank_command) if (CMD_ARGC < 6) return ERROR_COMMAND_SYNTAX_ERROR; + /* fix-up bank base address: 0 is used for normal flash memory */ + if (bank->base == 0) + bank->base = STM32_FLASH_BANK_BASE; + stm32l4_info = calloc(1, sizeof(struct stm32l4_flash_bank)); if (!stm32l4_info) return ERROR_FAIL; /* Checkme: What better error to use?*/ @@ -449,11 +488,43 @@ FLASH_BANK_COMMAND_HANDLER(stm32l4_flash_bank_command) bank->write_start_alignment = bank->write_end_alignment = 8; stm32l4_info->probed = false; + stm32l4_info->otp_enabled = false; stm32l4_info->user_bank_size = bank->size; return ERROR_OK; } +static inline bool stm32l4_is_otp(struct flash_bank *bank) +{ + struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv; + return bank->base == stm32l4_info->part_info->otp_base; +} + +static int stm32l4_otp_enable(struct flash_bank *bank, bool enable) +{ + struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv; + + if (!stm32l4_is_otp(bank)) + return ERROR_FAIL; + + char *op_str = enable ? "enabled" : "disabled"; + + LOG_INFO("OTP memory (bank #%d) is %s%s for write commands", + bank->bank_number, + stm32l4_info->otp_enabled == enable ? "already " : "", + op_str); + + stm32l4_info->otp_enabled = enable; + + return ERROR_OK; +} + +static inline bool stm32l4_otp_is_enabled(struct flash_bank *bank) +{ + struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv; + return stm32l4_info->otp_enabled; +} + static inline uint32_t stm32l4_get_flash_reg(struct flash_bank *bank, uint32_t reg_offset) { struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv; @@ -693,6 +764,11 @@ static int stm32l4_erase(struct flash_bank *bank, unsigned int first, assert((first <= last) && (last < bank->num_sectors)); + if (stm32l4_is_otp(bank)) { + LOG_ERROR("cannot erase OTP memory"); + return ERROR_FLASH_OPER_UNSUPPORTED; + } + if (bank->target->state != TARGET_HALTED) { LOG_ERROR("Target not halted"); return ERROR_TARGET_NOT_HALTED; @@ -749,6 +825,11 @@ static int stm32l4_protect(struct flash_bank *bank, int set, unsigned int first, struct target *target = bank->target; struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv; + if (stm32l4_is_otp(bank)) { + LOG_ERROR("cannot protect/unprotect OTP memory"); + return ERROR_FLASH_OPER_UNSUPPORTED; + } + if (target->state != TARGET_HALTED) { LOG_ERROR("Target not halted"); return ERROR_TARGET_NOT_HALTED; @@ -883,6 +964,11 @@ static int stm32l4_write(struct flash_bank *bank, const uint8_t *buffer, { int retval = ERROR_OK, retval2; + if (stm32l4_is_otp(bank) && !stm32l4_otp_is_enabled(bank)) { + LOG_ERROR("OTP memory is disabled for write commands"); + return ERROR_FAIL; + } + if (bank->target->state != TARGET_HALTED) { LOG_ERROR("Target not halted"); return ERROR_TARGET_NOT_HALTED; @@ -1001,6 +1087,29 @@ static int stm32l4_probe(struct flash_bank *bank) LOG_INFO("device idcode = 0x%08" PRIx32 " (%s)", stm32l4_info->idcode, device_info); + if (stm32l4_is_otp(bank)) { + bank->size = part_info->otp_size; + + LOG_INFO("OTP size is %d bytes, base address is " TARGET_ADDR_FMT, bank->size, bank->base); + + /* OTP memory is considered as one sector */ + free(bank->sectors); + bank->num_sectors = 1; + bank->sectors = alloc_block_array(0, part_info->otp_size, 1); + + if (!bank->sectors) { + LOG_ERROR("failed to allocate bank sectors"); + return ERROR_FAIL; + } + + + stm32l4_info->probed = true; + return ERROR_OK; + } else if (bank->base != STM32_FLASH_BANK_BASE) { + LOG_ERROR("invalid bank base address"); + return ERROR_FAIL; + } + /* get flash size from target. */ retval = target_read_u16(target, part_info->fsize_addr, &flash_size_kb); @@ -1175,7 +1284,6 @@ static int stm32l4_probe(struct flash_bank *bank) free(bank->sectors); bank->size = (flash_size_kb + gap_size_kb) * 1024; - bank->base = STM32_FLASH_BANK_BASE; bank->num_sectors = num_pages; bank->sectors = malloc(sizeof(struct flash_sector) * bank->num_sectors); if (bank->sectors == NULL) { @@ -1227,6 +1335,7 @@ static int get_stm32l4_info(struct flash_bank *bank, char *buf, int buf_size) if (stm32l4_info->probed) snprintf(buf + buf_len, buf_size - buf_len, " - %s-bank", + stm32l4_is_otp(bank) ? "OTP" : stm32l4_info->dual_bank_mode ? "Flash dual" : "Flash single"); return ERROR_OK; @@ -1244,6 +1353,11 @@ static int stm32l4_mass_erase(struct flash_bank *bank) struct target *target = bank->target; struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv; + if (stm32l4_is_otp(bank)) { + LOG_ERROR("cannot erase OTP memory"); + return ERROR_FLASH_OPER_UNSUPPORTED; + } + uint32_t action = FLASH_MER1; if (stm32l4_info->part_info->has_dual_bank) @@ -1410,6 +1524,11 @@ COMMAND_HANDLER(stm32l4_handle_lock_command) if (ERROR_OK != retval) return retval; + if (stm32l4_is_otp(bank)) { + LOG_ERROR("cannot lock/unlock OTP memory"); + return ERROR_FLASH_OPER_UNSUPPORTED; + } + target = bank->target; if (target->state != TARGET_HALTED) { @@ -1439,6 +1558,11 @@ COMMAND_HANDLER(stm32l4_handle_unlock_command) if (ERROR_OK != retval) return retval; + if (stm32l4_is_otp(bank)) { + LOG_ERROR("cannot lock/unlock OTP memory"); + return ERROR_FLASH_OPER_UNSUPPORTED; + } + target = bank->target; if (target->state != TARGET_HALTED) { @@ -1456,6 +1580,33 @@ COMMAND_HANDLER(stm32l4_handle_unlock_command) return ERROR_OK; } +COMMAND_HANDLER(stm32l4_handle_otp_command) +{ + if (CMD_ARGC < 2) + 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; + + if (!stm32l4_is_otp(bank)) { + command_print(CMD, "the specified bank is not an OTP memory"); + return ERROR_FAIL; + } + if (strcmp(CMD_ARGV[1], "enable") == 0) + stm32l4_otp_enable(bank, true); + else if (strcmp(CMD_ARGV[1], "disable") == 0) + stm32l4_otp_enable(bank, false); + else if (strcmp(CMD_ARGV[1], "show") == 0) + command_print(CMD, "OTP memory bank #%d is %s for write commands.", + bank->bank_number, stm32l4_otp_is_enabled(bank) ? "enabled" : "disabled"); + else + return ERROR_COMMAND_SYNTAX_ERROR; + + return ERROR_OK; +} + static const struct command_registration stm32l4_exec_command_handlers[] = { { .name = "lock", @@ -1499,6 +1650,13 @@ static const struct command_registration stm32l4_exec_command_handlers[] = { .usage = "bank_id", .help = "Force re-load of device options (will cause device reset).", }, + { + .name = "otp", + .handler = stm32l4_handle_otp_command, + .mode = COMMAND_EXEC, + .usage = " ", + .help = "OTP (One Time Programmable) memory write enable/disable", + }, COMMAND_REGISTRATION_DONE }; diff --git a/tcl/target/stm32g0x.cfg b/tcl/target/stm32g0x.cfg index 50836ea826..7df5306559 100644 --- a/tcl/target/stm32g0x.cfg +++ b/tcl/target/stm32g0x.cfg @@ -38,8 +38,8 @@ target create $_TARGETNAME cortex_m -endian $_ENDIAN -dap $_CHIPNAME.dap $_TARGETNAME configure -work-area-phys 0x20000000 -work-area-size $_WORKAREASIZE -work-area-backup 0 -set _FLASHNAME $_CHIPNAME.flash -flash bank $_FLASHNAME stm32l4x 0 0 0 0 $_TARGETNAME +flash bank $_CHIPNAME.flash stm32l4x 0x08000000 0 0 0 $_TARGETNAME +flash bank $_CHIPNAME.otp stm32l4x 0x1fff7000 0 0 0 $_TARGETNAME # reasonable default adapter speed 2000 diff --git a/tcl/target/stm32g4x.cfg b/tcl/target/stm32g4x.cfg index 9f144a07ef..360447bd5e 100644 --- a/tcl/target/stm32g4x.cfg +++ b/tcl/target/stm32g4x.cfg @@ -47,8 +47,8 @@ target create $_TARGETNAME cortex_m -endian $_ENDIAN -dap $_CHIPNAME.dap $_TARGETNAME configure -work-area-phys 0x20000000 -work-area-size $_WORKAREASIZE -work-area-backup 0 -set _FLASHNAME $_CHIPNAME.flash -flash bank $_FLASHNAME stm32l4x 0 0 0 0 $_TARGETNAME +flash bank $_CHIPNAME.flash stm32l4x 0x08000000 0 0 0 $_TARGETNAME +flash bank $_CHIPNAME.otp stm32l4x 0x1fff7000 0 0 0 $_TARGETNAME if { [info exists QUADSPI] && $QUADSPI } { set a [llength [flash list]] diff --git a/tcl/target/stm32l4x.cfg b/tcl/target/stm32l4x.cfg index 7f08f3c4ba..5899791938 100644 --- a/tcl/target/stm32l4x.cfg +++ b/tcl/target/stm32l4x.cfg @@ -47,8 +47,8 @@ target create $_TARGETNAME cortex_m -endian $_ENDIAN -dap $_CHIPNAME.dap $_TARGETNAME configure -work-area-phys 0x20000000 -work-area-size $_WORKAREASIZE -work-area-backup 0 -set _FLASHNAME $_CHIPNAME.flash -flash bank $_FLASHNAME stm32l4x 0 0 0 0 $_TARGETNAME +flash bank $_CHIPNAME.flash stm32l4x 0x08000000 0 0 0 $_TARGETNAME +flash bank $_CHIPNAME.otp stm32l4x 0x1fff7000 0 0 0 $_TARGETNAME if { [info exists QUADSPI] && $QUADSPI } { set a [llength [flash list]] diff --git a/tcl/target/stm32l5x.cfg b/tcl/target/stm32l5x.cfg index bf5636094f..92083b9e05 100644 --- a/tcl/target/stm32l5x.cfg +++ b/tcl/target/stm32l5x.cfg @@ -53,7 +53,8 @@ target create $_TARGETNAME cortex_m -endian $_ENDIAN -dap $_CHIPNAME.dap $_TARGETNAME configure -work-area-phys 0x20000000 -work-area-size $_WORKAREASIZE -work-area-backup 0 # declare non-secure flash -flash bank $_CHIPNAME.flash_ns stm32l4x 0 0 0 0 $_TARGETNAME +flash bank $_CHIPNAME.flash_ns stm32l4x 0x08000000 0 0 0 $_TARGETNAME +flash bank $_CHIPNAME.otp stm32l4x 0x0BFA0000 0 0 0 $_TARGETNAME # Common knowledges tells JTAG speed should be <= F_CPU/6. # F_CPU after reset is MSI 4MHz, so use F_JTAG = 500 kHz to stay on diff --git a/tcl/target/stm32wbx.cfg b/tcl/target/stm32wbx.cfg index 90f53bb967..6467667de0 100644 --- a/tcl/target/stm32wbx.cfg +++ b/tcl/target/stm32wbx.cfg @@ -46,8 +46,8 @@ target create $_TARGETNAME cortex_m -endian $_ENDIAN -dap $_CHIPNAME.dap $_TARGETNAME configure -work-area-phys 0x20000000 -work-area-size $_WORKAREASIZE -work-area-backup 0 -set _FLASHNAME $_CHIPNAME.flash -flash bank $_FLASHNAME stm32l4x 0 0 0 0 $_TARGETNAME +flash bank $_CHIPNAME.flash stm32l4x 0x08000000 0 0 0 $_TARGETNAME +flash bank $_CHIPNAME.otp stm32l4x 0x1fff7000 0 0 0 $_TARGETNAME # Common knowledges tells JTAG speed should be <= F_CPU/6. # F_CPU after reset is MSI 4MHz, so use F_JTAG = 500 kHz to stay on diff --git a/tcl/target/stm32wlx.cfg b/tcl/target/stm32wlx.cfg index ba16fab6b2..961850ad18 100644 --- a/tcl/target/stm32wlx.cfg +++ b/tcl/target/stm32wlx.cfg @@ -46,8 +46,8 @@ target create $_TARGETNAME cortex_m -endian $_ENDIAN -dap $_CHIPNAME.dap $_TARGETNAME configure -work-area-phys 0x20000000 -work-area-size $_WORKAREASIZE -work-area-backup 0 -set _FLASHNAME $_CHIPNAME.flash -flash bank $_FLASHNAME stm32l4x 0 0 0 0 $_TARGETNAME +flash bank $_CHIPNAME.flash stm32l4x 0x08000000 0 0 0 $_TARGETNAME +flash bank $_CHIPNAME.otp stm32l4x 0x1fff7000 0 0 0 $_TARGETNAME # Common knowledges tells JTAG speed should be <= F_CPU/6. # F_CPU after reset is MSI 4MHz, so use F_JTAG = 500 kHz to stay on -- 2.30.2