/* Erase time can be as high as 25ms, 10x this and assume it's toast... */
#define FLASH_ERASE_TIMEOUT 250
+#define FLASH_WRITE_TIMEOUT 50
/* relevant STM32L4 flags ****************************************************/
const uint16_t max_flash_size_kb;
const uint32_t flags; /* one bit per feature, see STM32L4 flags: macros F_XXX */
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;
bool dual_bank_mode;
int hole_sectors;
uint32_t user_bank_size;
+ uint32_t cr_bker_mask;
+ uint32_t sr_bsy_mask;
uint32_t wrpxxr_mask;
const struct stm32l4_part_info *part_info;
uint32_t flash_regs_base;
const uint32_t *flash_regs;
bool otp_enabled;
+ bool use_flashloader;
enum stm32l4_rdp rdp;
bool tzen;
uint32_t optr;
{ 0x1000, "A" }, { 0x1001, "Z" }, { 0x2000, "B" },
};
+static const struct stm32l4_rev stm32_467_revs[] = {
+ { 0x1000, "A" },
+};
+
static const struct stm32l4_rev stm32_468_revs[] = {
{ 0x1000, "A" }, { 0x2000, "B" }, { 0x2001, "Z" },
};
.max_flash_size_kb = 1024,
.flags = F_HAS_DUAL_BANK,
.flash_regs_base = 0x40022000,
- .default_flash_regs = stm32l4_flash_regs,
.fsize_addr = 0x1FFF75E0,
.otp_base = 0x1FFF7000,
.otp_size = 1024,
.max_flash_size_kb = 256,
.flags = F_NONE,
.flash_regs_base = 0x40022000,
- .default_flash_regs = stm32l4_flash_regs,
.fsize_addr = 0x1FFF75E0,
.otp_base = 0x1FFF7000,
.otp_size = 1024,
.max_flash_size_kb = 128,
.flags = F_NONE,
.flash_regs_base = 0x40022000,
- .default_flash_regs = stm32l4_flash_regs,
.fsize_addr = 0x1FFF75E0,
.otp_base = 0x1FFF7000,
.otp_size = 1024,
.max_flash_size_kb = 1024,
.flags = F_HAS_DUAL_BANK,
.flash_regs_base = 0x40022000,
- .default_flash_regs = stm32l4_flash_regs,
.fsize_addr = 0x1FFF75E0,
.otp_base = 0x1FFF7000,
.otp_size = 1024,
.max_flash_size_kb = 512,
.flags = F_NONE,
.flash_regs_base = 0x40022000,
- .default_flash_regs = stm32l4_flash_regs,
.fsize_addr = 0x1FFF75E0,
.otp_base = 0x1FFF7000,
.otp_size = 1024,
.max_flash_size_kb = 128,
.flags = F_NONE,
.flash_regs_base = 0x40022000,
- .default_flash_regs = stm32l4_flash_regs,
.fsize_addr = 0x1FFF75E0,
.otp_base = 0x1FFF7000,
.otp_size = 1024,
.max_flash_size_kb = 64,
.flags = F_NONE,
.flash_regs_base = 0x40022000,
- .default_flash_regs = stm32l4_flash_regs,
+ .fsize_addr = 0x1FFF75E0,
+ .otp_base = 0x1FFF7000,
+ .otp_size = 1024,
+ },
+ {
+ .id = 0x467,
+ .revs = stm32_467_revs,
+ .num_revs = ARRAY_SIZE(stm32_467_revs),
+ .device_str = "STM32G0Bx/G0Cx",
+ .max_flash_size_kb = 512,
+ .flags = F_HAS_DUAL_BANK,
+ .flash_regs_base = 0x40022000,
.fsize_addr = 0x1FFF75E0,
.otp_base = 0x1FFF7000,
.otp_size = 1024,
.max_flash_size_kb = 128,
.flags = F_NONE,
.flash_regs_base = 0x40022000,
- .default_flash_regs = stm32l4_flash_regs,
.fsize_addr = 0x1FFF75E0,
.otp_base = 0x1FFF7000,
.otp_size = 1024,
.max_flash_size_kb = 512,
.flags = F_HAS_DUAL_BANK | F_USE_ALL_WRPXX,
.flash_regs_base = 0x40022000,
- .default_flash_regs = stm32l4_flash_regs,
.fsize_addr = 0x1FFF75E0,
.otp_base = 0x1FFF7000,
.otp_size = 1024,
.max_flash_size_kb = 2048,
.flags = F_HAS_DUAL_BANK | F_USE_ALL_WRPXX,
.flash_regs_base = 0x40022000,
- .default_flash_regs = stm32l4_flash_regs,
.fsize_addr = 0x1FFF75E0,
.otp_base = 0x1FFF7000,
.otp_size = 1024,
.max_flash_size_kb = 1024,
.flags = F_HAS_DUAL_BANK | F_USE_ALL_WRPXX,
.flash_regs_base = 0x40022000,
- .default_flash_regs = stm32l4_flash_regs,
.fsize_addr = 0x1FFF75E0,
.otp_base = 0x1FFF7000,
.otp_size = 1024,
.max_flash_size_kb = 512,
.flags = F_HAS_DUAL_BANK | F_USE_ALL_WRPXX | F_HAS_TZ | F_HAS_L5_FLASH_REGS,
.flash_regs_base = 0x40022000,
- .default_flash_regs = stm32l5_ns_flash_regs,
.fsize_addr = 0x0BFA05E0,
.otp_base = 0x0BFA0000,
.otp_size = 512,
.max_flash_size_kb = 512,
.flags = F_NONE,
.flash_regs_base = 0x40022000,
- .default_flash_regs = stm32l4_flash_regs,
.fsize_addr = 0x1FFF75E0,
.otp_base = 0x1FFF7000,
.otp_size = 1024,
.max_flash_size_kb = 1024,
.flags = F_NONE,
.flash_regs_base = 0x58004000,
- .default_flash_regs = stm32l4_flash_regs,
.fsize_addr = 0x1FFF75E0,
.otp_base = 0x1FFF7000,
.otp_size = 1024,
.max_flash_size_kb = 512,
.flags = F_NONE,
.flash_regs_base = 0x58004000,
- .default_flash_regs = stm32l4_flash_regs,
.fsize_addr = 0x1FFF75E0,
.otp_base = 0x1FFF7000,
.otp_size = 1024,
.max_flash_size_kb = 256,
.flags = F_NONE,
.flash_regs_base = 0x58004000,
- .default_flash_regs = stm32l4_flash_regs,
.fsize_addr = 0x1FFF75E0,
.otp_base = 0x1FFF7000,
.otp_size = 1024,
stm32l4_info->probed = false;
stm32l4_info->otp_enabled = false;
stm32l4_info->user_bank_size = bank->size;
+ stm32l4_info->use_flashloader = true;
return ERROR_OK;
}
static int stm32l4_wait_status_busy(struct flash_bank *bank, int timeout)
{
+ struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv;
uint32_t status;
int retval = ERROR_OK;
if (retval != ERROR_OK)
return retval;
LOG_DEBUG("status: 0x%" PRIx32 "", status);
- if ((status & FLASH_BSY) == 0)
+ if ((status & stm32l4_info->sr_bsy_mask) == 0)
break;
if (timeout-- <= 0) {
LOG_ERROR("timed out waiting for flash");
if (i >= stm32l4_info->bank1_sectors) {
uint8_t snb;
snb = i - stm32l4_info->bank1_sectors;
- erase_flags |= snb << FLASH_PAGE_SHIFT | FLASH_CR_BKER;
+ erase_flags |= snb << FLASH_PAGE_SHIFT | stm32l4_info->cr_bker_mask;
} else
erase_flags |= i << FLASH_PAGE_SHIFT;
retval = stm32l4_write_flash_reg_by_index(bank, STM32_FLASH_CR_INDEX, erase_flags);
return retval;
}
+/* Count is in double-words */
+static int stm32l4_write_block_without_loader(struct flash_bank *bank, const uint8_t *buffer,
+ uint32_t offset, uint32_t count)
+{
+ struct target *target = bank->target;
+ uint32_t address = bank->base + offset;
+ int retval = ERROR_OK;
+
+ /* wait for BSY bit */
+ retval = stm32l4_wait_status_busy(bank, FLASH_WRITE_TIMEOUT);
+ if (retval != ERROR_OK)
+ return retval;
+
+ /* set PG in FLASH_CR */
+ retval = stm32l4_write_flash_reg_by_index(bank, STM32_FLASH_CR_INDEX, FLASH_PG);
+ if (retval != ERROR_OK)
+ return retval;
+
+
+ /* write directly to flash memory */
+ const uint8_t *src = buffer;
+ while (count--) {
+ retval = target_write_memory(target, address, 4, 2, src);
+ if (retval != ERROR_OK)
+ return retval;
+
+ /* wait for BSY bit */
+ retval = stm32l4_wait_status_busy(bank, FLASH_WRITE_TIMEOUT);
+ if (retval != ERROR_OK)
+ return retval;
+
+ src += 8;
+ address += 8;
+ }
+
+ /* reset PG in FLASH_CR */
+ retval = stm32l4_write_flash_reg_by_index(bank, STM32_FLASH_CR_INDEX, 0);
+ if (retval != ERROR_OK)
+ return retval;
+
+ return retval;
+}
+
static int stm32l4_write(struct flash_bank *bank, const uint8_t *buffer,
uint32_t offset, uint32_t count)
{
if (retval != ERROR_OK)
goto err_lock;
- /* For TrustZone enabled devices, when TZEN is set and RDP level is 0.5,
- * the debug is possible only in non-secure state.
- * Thus means the flashloader will run in non-secure mode,
- * and the workarea need to be in non-secure RAM */
- if (stm32l4_info->tzen && (stm32l4_info->rdp == RDP_LEVEL_0_5))
- LOG_INFO("RDP level is 0.5, the work-area should reside in non-secure RAM");
+ /**
+ * FIXME update the flash loader to use a custom FLASH_SR_BSY mask
+ * Workaround for STM32G0Bx/G0Cx devices in dual bank mode,
+ * as the flash loader does not use the SR_BSY2
+ */
+ bool use_flashloader = stm32l4_info->use_flashloader;
+ if ((stm32l4_info->part_info->id == 0x467) && stm32l4_info->dual_bank_mode) {
+ LOG_INFO("Couldn't use the flash loader in dual-bank mode");
+ use_flashloader = false;
+ }
+
+ if (use_flashloader) {
+ /* For TrustZone enabled devices, when TZEN is set and RDP level is 0.5,
+ * the debug is possible only in non-secure state.
+ * Thus means the flashloader will run in non-secure mode,
+ * and the workarea need to be in non-secure RAM */
+ if (stm32l4_info->tzen && (stm32l4_info->rdp == RDP_LEVEL_0_5))
+ LOG_INFO("RDP level is 0.5, the work-area should reside in non-secure RAM");
+
+ retval = stm32l4_write_block(bank, buffer, offset, count / 8);
+ }
+
+ if (!use_flashloader || retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE) {
+ LOG_INFO("falling back to single memory accesses");
+ retval = stm32l4_write_block_without_loader(bank, buffer, offset, count / 8);
+ }
- retval = stm32l4_write_block(bank, buffer, offset, count / 8);
err_lock:
retval2 = stm32l4_write_flash_reg_by_index(bank, STM32_FLASH_CR_INDEX, FLASH_LOCK);
stm32l4_info->idcode, part_info->device_str, rev_str, rev_id);
stm32l4_info->flash_regs_base = stm32l4_info->part_info->flash_regs_base;
- stm32l4_info->flash_regs = stm32l4_info->part_info->default_flash_regs;
+ stm32l4_info->cr_bker_mask = FLASH_BKER;
+ stm32l4_info->sr_bsy_mask = FLASH_BSY;
+
+ /* initialise the flash registers layout */
+ if (part_info->flags & F_HAS_L5_FLASH_REGS)
+ stm32l4_info->flash_regs = stm32l5_ns_flash_regs;
+ else
+ stm32l4_info->flash_regs = stm32l4_flash_regs;
/* read flash option register */
retval = stm32l4_read_flash_reg_by_index(bank, STM32_FLASH_OPTR_INDEX, &stm32l4_info->optr);
stm32l4_sync_rdp_tzen(bank);
+ /* for devices with trustzone, use flash secure registers when TZEN=1 and RDP is LEVEL_0 */
+ if (stm32l4_info->tzen && (stm32l4_info->rdp == RDP_LEVEL_0)) {
+ if (part_info->flags & F_HAS_L5_FLASH_REGS) {
+ stm32l4_info->flash_regs_base |= STM32L5_REGS_SEC_OFFSET;
+ stm32l4_info->flash_regs = stm32l5_s_flash_regs;
+ } else {
+ LOG_ERROR("BUG: device supported incomplete");
+ return ERROR_NOT_IMPLEMENTED;
+ }
+ }
+
if (part_info->flags & F_HAS_TZ)
LOG_INFO("TZEN = %d : TrustZone %s by option bytes",
stm32l4_info->tzen,
num_pages = flash_size_kb / page_size_kb;
stm32l4_info->bank1_sectors = num_pages;
break;
+ case 0x467: /* STM32G0B/G0Cxx */
+ /* single/dual bank depending on bit(21) */
+ page_size_kb = 2;
+ num_pages = flash_size_kb / page_size_kb;
+ stm32l4_info->bank1_sectors = num_pages;
+ stm32l4_info->cr_bker_mask = FLASH_BKER_G0;
+
+ /* check DUAL_BANK bit */
+ if (stm32l4_info->optr & BIT(21)) {
+ stm32l4_info->sr_bsy_mask = FLASH_BSY | FLASH_BSY2;
+ stm32l4_info->dual_bank_mode = true;
+ stm32l4_info->bank1_sectors = num_pages / 2;
+ }
+ break;
case 0x469: /* STM32G47/G48xx */
/* STM32G47/8 can be single/dual bank:
* if DUAL_BANK = 0 -> single bank
num_pages = flash_size_kb / page_size_kb;
stm32l4_info->bank1_sectors = num_pages / 2;
}
-
- /**
- * by default use the non-secure registers,
- * switch secure registers if TZ is enabled and RDP is LEVEL_0
- */
- if (stm32l4_info->tzen && (stm32l4_info->rdp == RDP_LEVEL_0)) {
- stm32l4_info->flash_regs_base |= 0x10000000;
- stm32l4_info->flash_regs = stm32l5_s_flash_regs;
- }
break;
case 0x495: /* STM32WB5x */
case 0x496: /* STM32WB3x */
return stm32l4_perform_obl_launch(bank);
}
+COMMAND_HANDLER(stm32l4_handle_flashloader_command)
+{
+ if (CMD_ARGC < 1 || CMD_ARGC > 2)
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ struct flash_bank *bank;
+ int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
+ if (retval != ERROR_OK)
+ return retval;
+
+ struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv;
+
+ if (CMD_ARGC == 2)
+ COMMAND_PARSE_ENABLE(CMD_ARGV[1], stm32l4_info->use_flashloader);
+
+ command_print(CMD, "FlashLoader usage is %s", stm32l4_info->use_flashloader ? "enabled" : "disabled");
+
+ return ERROR_OK;
+}
+
COMMAND_HANDLER(stm32l4_handle_option_load_command)
{
if (CMD_ARGC != 1)
.usage = "bank_id",
.help = "Unlock entire protected flash device.",
},
+ {
+ .name = "flashloader",
+ .handler = stm32l4_handle_flashloader_command,
+ .mode = COMMAND_EXEC,
+ .usage = "<bank_id> [enable|disable]",
+ .help = "Configure the flashloader usage",
+ },
{
.name = "mass_erase",
.handler = stm32l4_handle_mass_erase_command,