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 <hyphen0break@gmail.com> Signed-off-by: Tarek BOCHKATI <tarek.bouchkati@gmail.com> Reviewed-on: http://openocd.zylin.com/5537 Tested-by: jenkins Reviewed-by: Tomas Vanek <vanekt@fbl.cz>
This commit is contained in:
parent
f13bb10e26
commit
b8fd9aecb1
|
@ -7179,6 +7179,17 @@ the chip identification register, and autoconfigures itself.
|
||||||
flash bank $_FLASHNAME stm32l4x 0 0 0 0 $_TARGETNAME
|
flash bank $_FLASHNAME stm32l4x 0 0 0 0 $_TARGETNAME
|
||||||
@end example
|
@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
|
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
|
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
|
the flash driver. However, specifying a wrong value might lead to a completely
|
||||||
|
|
|
@ -171,6 +171,8 @@ struct stm32l4_part_info {
|
||||||
const uint32_t flash_regs_base;
|
const uint32_t flash_regs_base;
|
||||||
const uint32_t *default_flash_regs;
|
const uint32_t *default_flash_regs;
|
||||||
const uint32_t fsize_addr;
|
const uint32_t fsize_addr;
|
||||||
|
const uint32_t otp_base;
|
||||||
|
const uint32_t otp_size;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct stm32l4_flash_bank {
|
struct stm32l4_flash_bank {
|
||||||
|
@ -183,6 +185,7 @@ struct stm32l4_flash_bank {
|
||||||
uint32_t wrpxxr_mask;
|
uint32_t wrpxxr_mask;
|
||||||
const struct stm32l4_part_info *part_info;
|
const struct stm32l4_part_info *part_info;
|
||||||
const uint32_t *flash_regs;
|
const uint32_t *flash_regs;
|
||||||
|
bool otp_enabled;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* human readable list of families this drivers supports (sorted alphabetically) */
|
/* 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,
|
.flash_regs_base = 0x40022000,
|
||||||
.default_flash_regs = stm32l4_flash_regs,
|
.default_flash_regs = stm32l4_flash_regs,
|
||||||
.fsize_addr = 0x1FFF75E0,
|
.fsize_addr = 0x1FFF75E0,
|
||||||
|
.otp_base = 0x1FFF7000,
|
||||||
|
.otp_size = 1024,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.id = 0x435,
|
.id = 0x435,
|
||||||
|
@ -274,6 +279,8 @@ static const struct stm32l4_part_info stm32l4_parts[] = {
|
||||||
.flash_regs_base = 0x40022000,
|
.flash_regs_base = 0x40022000,
|
||||||
.default_flash_regs = stm32l4_flash_regs,
|
.default_flash_regs = stm32l4_flash_regs,
|
||||||
.fsize_addr = 0x1FFF75E0,
|
.fsize_addr = 0x1FFF75E0,
|
||||||
|
.otp_base = 0x1FFF7000,
|
||||||
|
.otp_size = 1024,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.id = 0x460,
|
.id = 0x460,
|
||||||
|
@ -285,6 +292,8 @@ static const struct stm32l4_part_info stm32l4_parts[] = {
|
||||||
.flash_regs_base = 0x40022000,
|
.flash_regs_base = 0x40022000,
|
||||||
.default_flash_regs = stm32l4_flash_regs,
|
.default_flash_regs = stm32l4_flash_regs,
|
||||||
.fsize_addr = 0x1FFF75E0,
|
.fsize_addr = 0x1FFF75E0,
|
||||||
|
.otp_base = 0x1FFF7000,
|
||||||
|
.otp_size = 1024,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.id = 0x461,
|
.id = 0x461,
|
||||||
|
@ -296,6 +305,8 @@ static const struct stm32l4_part_info stm32l4_parts[] = {
|
||||||
.flash_regs_base = 0x40022000,
|
.flash_regs_base = 0x40022000,
|
||||||
.default_flash_regs = stm32l4_flash_regs,
|
.default_flash_regs = stm32l4_flash_regs,
|
||||||
.fsize_addr = 0x1FFF75E0,
|
.fsize_addr = 0x1FFF75E0,
|
||||||
|
.otp_base = 0x1FFF7000,
|
||||||
|
.otp_size = 1024,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.id = 0x462,
|
.id = 0x462,
|
||||||
|
@ -307,6 +318,8 @@ static const struct stm32l4_part_info stm32l4_parts[] = {
|
||||||
.flash_regs_base = 0x40022000,
|
.flash_regs_base = 0x40022000,
|
||||||
.default_flash_regs = stm32l4_flash_regs,
|
.default_flash_regs = stm32l4_flash_regs,
|
||||||
.fsize_addr = 0x1FFF75E0,
|
.fsize_addr = 0x1FFF75E0,
|
||||||
|
.otp_base = 0x1FFF7000,
|
||||||
|
.otp_size = 1024,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.id = 0x464,
|
.id = 0x464,
|
||||||
|
@ -318,6 +331,8 @@ static const struct stm32l4_part_info stm32l4_parts[] = {
|
||||||
.flash_regs_base = 0x40022000,
|
.flash_regs_base = 0x40022000,
|
||||||
.default_flash_regs = stm32l4_flash_regs,
|
.default_flash_regs = stm32l4_flash_regs,
|
||||||
.fsize_addr = 0x1FFF75E0,
|
.fsize_addr = 0x1FFF75E0,
|
||||||
|
.otp_base = 0x1FFF7000,
|
||||||
|
.otp_size = 1024,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.id = 0x466,
|
.id = 0x466,
|
||||||
|
@ -329,6 +344,8 @@ static const struct stm32l4_part_info stm32l4_parts[] = {
|
||||||
.flash_regs_base = 0x40022000,
|
.flash_regs_base = 0x40022000,
|
||||||
.default_flash_regs = stm32l4_flash_regs,
|
.default_flash_regs = stm32l4_flash_regs,
|
||||||
.fsize_addr = 0x1FFF75E0,
|
.fsize_addr = 0x1FFF75E0,
|
||||||
|
.otp_base = 0x1FFF7000,
|
||||||
|
.otp_size = 1024,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.id = 0x468,
|
.id = 0x468,
|
||||||
|
@ -340,6 +357,8 @@ static const struct stm32l4_part_info stm32l4_parts[] = {
|
||||||
.flash_regs_base = 0x40022000,
|
.flash_regs_base = 0x40022000,
|
||||||
.default_flash_regs = stm32l4_flash_regs,
|
.default_flash_regs = stm32l4_flash_regs,
|
||||||
.fsize_addr = 0x1FFF75E0,
|
.fsize_addr = 0x1FFF75E0,
|
||||||
|
.otp_base = 0x1FFF7000,
|
||||||
|
.otp_size = 1024,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.id = 0x469,
|
.id = 0x469,
|
||||||
|
@ -351,6 +370,8 @@ static const struct stm32l4_part_info stm32l4_parts[] = {
|
||||||
.flash_regs_base = 0x40022000,
|
.flash_regs_base = 0x40022000,
|
||||||
.default_flash_regs = stm32l4_flash_regs,
|
.default_flash_regs = stm32l4_flash_regs,
|
||||||
.fsize_addr = 0x1FFF75E0,
|
.fsize_addr = 0x1FFF75E0,
|
||||||
|
.otp_base = 0x1FFF7000,
|
||||||
|
.otp_size = 1024,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.id = 0x470,
|
.id = 0x470,
|
||||||
|
@ -362,6 +383,8 @@ static const struct stm32l4_part_info stm32l4_parts[] = {
|
||||||
.flash_regs_base = 0x40022000,
|
.flash_regs_base = 0x40022000,
|
||||||
.default_flash_regs = stm32l4_flash_regs,
|
.default_flash_regs = stm32l4_flash_regs,
|
||||||
.fsize_addr = 0x1FFF75E0,
|
.fsize_addr = 0x1FFF75E0,
|
||||||
|
.otp_base = 0x1FFF7000,
|
||||||
|
.otp_size = 1024,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.id = 0x471,
|
.id = 0x471,
|
||||||
|
@ -373,6 +396,8 @@ static const struct stm32l4_part_info stm32l4_parts[] = {
|
||||||
.flash_regs_base = 0x40022000,
|
.flash_regs_base = 0x40022000,
|
||||||
.default_flash_regs = stm32l4_flash_regs,
|
.default_flash_regs = stm32l4_flash_regs,
|
||||||
.fsize_addr = 0x1FFF75E0,
|
.fsize_addr = 0x1FFF75E0,
|
||||||
|
.otp_base = 0x1FFF7000,
|
||||||
|
.otp_size = 1024,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.id = 0x472,
|
.id = 0x472,
|
||||||
|
@ -384,6 +409,8 @@ static const struct stm32l4_part_info stm32l4_parts[] = {
|
||||||
.flash_regs_base = 0x40022000,
|
.flash_regs_base = 0x40022000,
|
||||||
.default_flash_regs = stm32l5_ns_flash_regs,
|
.default_flash_regs = stm32l5_ns_flash_regs,
|
||||||
.fsize_addr = 0x0BFA05E0,
|
.fsize_addr = 0x0BFA05E0,
|
||||||
|
.otp_base = 0x0BFA0000,
|
||||||
|
.otp_size = 512,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.id = 0x479,
|
.id = 0x479,
|
||||||
|
@ -395,6 +422,8 @@ static const struct stm32l4_part_info stm32l4_parts[] = {
|
||||||
.flash_regs_base = 0x40022000,
|
.flash_regs_base = 0x40022000,
|
||||||
.default_flash_regs = stm32l4_flash_regs,
|
.default_flash_regs = stm32l4_flash_regs,
|
||||||
.fsize_addr = 0x1FFF75E0,
|
.fsize_addr = 0x1FFF75E0,
|
||||||
|
.otp_base = 0x1FFF7000,
|
||||||
|
.otp_size = 1024,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.id = 0x495,
|
.id = 0x495,
|
||||||
|
@ -406,6 +435,8 @@ static const struct stm32l4_part_info stm32l4_parts[] = {
|
||||||
.flash_regs_base = 0x58004000,
|
.flash_regs_base = 0x58004000,
|
||||||
.default_flash_regs = stm32l4_flash_regs,
|
.default_flash_regs = stm32l4_flash_regs,
|
||||||
.fsize_addr = 0x1FFF75E0,
|
.fsize_addr = 0x1FFF75E0,
|
||||||
|
.otp_base = 0x1FFF7000,
|
||||||
|
.otp_size = 1024,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.id = 0x496,
|
.id = 0x496,
|
||||||
|
@ -417,6 +448,8 @@ static const struct stm32l4_part_info stm32l4_parts[] = {
|
||||||
.flash_regs_base = 0x58004000,
|
.flash_regs_base = 0x58004000,
|
||||||
.default_flash_regs = stm32l4_flash_regs,
|
.default_flash_regs = stm32l4_flash_regs,
|
||||||
.fsize_addr = 0x1FFF75E0,
|
.fsize_addr = 0x1FFF75E0,
|
||||||
|
.otp_base = 0x1FFF7000,
|
||||||
|
.otp_size = 1024,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.id = 0x497,
|
.id = 0x497,
|
||||||
|
@ -428,6 +461,8 @@ static const struct stm32l4_part_info stm32l4_parts[] = {
|
||||||
.flash_regs_base = 0x58004000,
|
.flash_regs_base = 0x58004000,
|
||||||
.default_flash_regs = stm32l4_flash_regs,
|
.default_flash_regs = stm32l4_flash_regs,
|
||||||
.fsize_addr = 0x1FFF75E0,
|
.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)
|
if (CMD_ARGC < 6)
|
||||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
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));
|
stm32l4_info = calloc(1, sizeof(struct stm32l4_flash_bank));
|
||||||
if (!stm32l4_info)
|
if (!stm32l4_info)
|
||||||
return ERROR_FAIL; /* Checkme: What better error to use?*/
|
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;
|
bank->write_start_alignment = bank->write_end_alignment = 8;
|
||||||
|
|
||||||
stm32l4_info->probed = false;
|
stm32l4_info->probed = false;
|
||||||
|
stm32l4_info->otp_enabled = false;
|
||||||
stm32l4_info->user_bank_size = bank->size;
|
stm32l4_info->user_bank_size = bank->size;
|
||||||
|
|
||||||
return ERROR_OK;
|
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)
|
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;
|
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));
|
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) {
|
if (bank->target->state != TARGET_HALTED) {
|
||||||
LOG_ERROR("Target not halted");
|
LOG_ERROR("Target not halted");
|
||||||
return 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 target *target = bank->target;
|
||||||
struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv;
|
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) {
|
if (target->state != TARGET_HALTED) {
|
||||||
LOG_ERROR("Target not halted");
|
LOG_ERROR("Target not halted");
|
||||||
return 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;
|
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) {
|
if (bank->target->state != TARGET_HALTED) {
|
||||||
LOG_ERROR("Target not halted");
|
LOG_ERROR("Target not halted");
|
||||||
return 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);
|
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. */
|
/* get flash size from target. */
|
||||||
retval = target_read_u16(target, part_info->fsize_addr, &flash_size_kb);
|
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);
|
free(bank->sectors);
|
||||||
|
|
||||||
bank->size = (flash_size_kb + gap_size_kb) * 1024;
|
bank->size = (flash_size_kb + gap_size_kb) * 1024;
|
||||||
bank->base = STM32_FLASH_BANK_BASE;
|
|
||||||
bank->num_sectors = num_pages;
|
bank->num_sectors = num_pages;
|
||||||
bank->sectors = malloc(sizeof(struct flash_sector) * bank->num_sectors);
|
bank->sectors = malloc(sizeof(struct flash_sector) * bank->num_sectors);
|
||||||
if (bank->sectors == NULL) {
|
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)
|
if (stm32l4_info->probed)
|
||||||
snprintf(buf + buf_len, buf_size - buf_len, " - %s-bank",
|
snprintf(buf + buf_len, buf_size - buf_len, " - %s-bank",
|
||||||
|
stm32l4_is_otp(bank) ? "OTP" :
|
||||||
stm32l4_info->dual_bank_mode ? "Flash dual" : "Flash single");
|
stm32l4_info->dual_bank_mode ? "Flash dual" : "Flash single");
|
||||||
|
|
||||||
return ERROR_OK;
|
return ERROR_OK;
|
||||||
|
@ -1244,6 +1353,11 @@ static int stm32l4_mass_erase(struct flash_bank *bank)
|
||||||
struct target *target = bank->target;
|
struct target *target = bank->target;
|
||||||
struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv;
|
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;
|
uint32_t action = FLASH_MER1;
|
||||||
|
|
||||||
if (stm32l4_info->part_info->has_dual_bank)
|
if (stm32l4_info->part_info->has_dual_bank)
|
||||||
|
@ -1410,6 +1524,11 @@ COMMAND_HANDLER(stm32l4_handle_lock_command)
|
||||||
if (ERROR_OK != retval)
|
if (ERROR_OK != retval)
|
||||||
return retval;
|
return retval;
|
||||||
|
|
||||||
|
if (stm32l4_is_otp(bank)) {
|
||||||
|
LOG_ERROR("cannot lock/unlock OTP memory");
|
||||||
|
return ERROR_FLASH_OPER_UNSUPPORTED;
|
||||||
|
}
|
||||||
|
|
||||||
target = bank->target;
|
target = bank->target;
|
||||||
|
|
||||||
if (target->state != TARGET_HALTED) {
|
if (target->state != TARGET_HALTED) {
|
||||||
|
@ -1439,6 +1558,11 @@ COMMAND_HANDLER(stm32l4_handle_unlock_command)
|
||||||
if (ERROR_OK != retval)
|
if (ERROR_OK != retval)
|
||||||
return retval;
|
return retval;
|
||||||
|
|
||||||
|
if (stm32l4_is_otp(bank)) {
|
||||||
|
LOG_ERROR("cannot lock/unlock OTP memory");
|
||||||
|
return ERROR_FLASH_OPER_UNSUPPORTED;
|
||||||
|
}
|
||||||
|
|
||||||
target = bank->target;
|
target = bank->target;
|
||||||
|
|
||||||
if (target->state != TARGET_HALTED) {
|
if (target->state != TARGET_HALTED) {
|
||||||
|
@ -1456,6 +1580,33 @@ COMMAND_HANDLER(stm32l4_handle_unlock_command)
|
||||||
return ERROR_OK;
|
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[] = {
|
static const struct command_registration stm32l4_exec_command_handlers[] = {
|
||||||
{
|
{
|
||||||
.name = "lock",
|
.name = "lock",
|
||||||
|
@ -1499,6 +1650,13 @@ static const struct command_registration stm32l4_exec_command_handlers[] = {
|
||||||
.usage = "bank_id",
|
.usage = "bank_id",
|
||||||
.help = "Force re-load of device options (will cause device reset).",
|
.help = "Force re-load of device options (will cause device reset).",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.name = "otp",
|
||||||
|
.handler = stm32l4_handle_otp_command,
|
||||||
|
.mode = COMMAND_EXEC,
|
||||||
|
.usage = "<bank_id> <enable|disable|show>",
|
||||||
|
.help = "OTP (One Time Programmable) memory write enable/disable",
|
||||||
|
},
|
||||||
COMMAND_REGISTRATION_DONE
|
COMMAND_REGISTRATION_DONE
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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
|
$_TARGETNAME configure -work-area-phys 0x20000000 -work-area-size $_WORKAREASIZE -work-area-backup 0
|
||||||
|
|
||||||
set _FLASHNAME $_CHIPNAME.flash
|
flash bank $_CHIPNAME.flash stm32l4x 0x08000000 0 0 0 $_TARGETNAME
|
||||||
flash bank $_FLASHNAME stm32l4x 0 0 0 0 $_TARGETNAME
|
flash bank $_CHIPNAME.otp stm32l4x 0x1fff7000 0 0 0 $_TARGETNAME
|
||||||
|
|
||||||
# reasonable default
|
# reasonable default
|
||||||
adapter speed 2000
|
adapter speed 2000
|
||||||
|
|
|
@ -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
|
$_TARGETNAME configure -work-area-phys 0x20000000 -work-area-size $_WORKAREASIZE -work-area-backup 0
|
||||||
|
|
||||||
set _FLASHNAME $_CHIPNAME.flash
|
flash bank $_CHIPNAME.flash stm32l4x 0x08000000 0 0 0 $_TARGETNAME
|
||||||
flash bank $_FLASHNAME stm32l4x 0 0 0 0 $_TARGETNAME
|
flash bank $_CHIPNAME.otp stm32l4x 0x1fff7000 0 0 0 $_TARGETNAME
|
||||||
|
|
||||||
if { [info exists QUADSPI] && $QUADSPI } {
|
if { [info exists QUADSPI] && $QUADSPI } {
|
||||||
set a [llength [flash list]]
|
set a [llength [flash list]]
|
||||||
|
|
|
@ -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
|
$_TARGETNAME configure -work-area-phys 0x20000000 -work-area-size $_WORKAREASIZE -work-area-backup 0
|
||||||
|
|
||||||
set _FLASHNAME $_CHIPNAME.flash
|
flash bank $_CHIPNAME.flash stm32l4x 0x08000000 0 0 0 $_TARGETNAME
|
||||||
flash bank $_FLASHNAME stm32l4x 0 0 0 0 $_TARGETNAME
|
flash bank $_CHIPNAME.otp stm32l4x 0x1fff7000 0 0 0 $_TARGETNAME
|
||||||
|
|
||||||
if { [info exists QUADSPI] && $QUADSPI } {
|
if { [info exists QUADSPI] && $QUADSPI } {
|
||||||
set a [llength [flash list]]
|
set a [llength [flash list]]
|
||||||
|
|
|
@ -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
|
$_TARGETNAME configure -work-area-phys 0x20000000 -work-area-size $_WORKAREASIZE -work-area-backup 0
|
||||||
|
|
||||||
# declare non-secure flash
|
# 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.
|
# 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
|
# F_CPU after reset is MSI 4MHz, so use F_JTAG = 500 kHz to stay on
|
||||||
|
|
|
@ -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
|
$_TARGETNAME configure -work-area-phys 0x20000000 -work-area-size $_WORKAREASIZE -work-area-backup 0
|
||||||
|
|
||||||
set _FLASHNAME $_CHIPNAME.flash
|
flash bank $_CHIPNAME.flash stm32l4x 0x08000000 0 0 0 $_TARGETNAME
|
||||||
flash bank $_FLASHNAME stm32l4x 0 0 0 0 $_TARGETNAME
|
flash bank $_CHIPNAME.otp stm32l4x 0x1fff7000 0 0 0 $_TARGETNAME
|
||||||
|
|
||||||
# Common knowledges tells JTAG speed should be <= F_CPU/6.
|
# 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
|
# F_CPU after reset is MSI 4MHz, so use F_JTAG = 500 kHz to stay on
|
||||||
|
|
|
@ -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
|
$_TARGETNAME configure -work-area-phys 0x20000000 -work-area-size $_WORKAREASIZE -work-area-backup 0
|
||||||
|
|
||||||
set _FLASHNAME $_CHIPNAME.flash
|
flash bank $_CHIPNAME.flash stm32l4x 0x08000000 0 0 0 $_TARGETNAME
|
||||||
flash bank $_FLASHNAME stm32l4x 0 0 0 0 $_TARGETNAME
|
flash bank $_CHIPNAME.otp stm32l4x 0x1fff7000 0 0 0 $_TARGETNAME
|
||||||
|
|
||||||
# Common knowledges tells JTAG speed should be <= F_CPU/6.
|
# 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
|
# F_CPU after reset is MSI 4MHz, so use F_JTAG = 500 kHz to stay on
|
||||||
|
|
Loading…
Reference in New Issue