+// SPDX-License-Identifier: GPL-2.0-or-later
+
/***************************************************************************
* Copyright (C) 2015 by Uwe Bonnes *
* bon@elektron.ikp.physik.tu-darmstadt.de *
* *
* Copyright (C) 2019 by Tarek Bochkati for STMicroelectronics *
* tarek.bouchkati@gmail.com *
- * *
- * This program is free software; you can redistribute it and/or modify *
- * it under the terms of the GNU General Public License as published by *
- * the Free Software Foundation; either version 2 of the License, or *
- * (at your option) any later version. *
- * *
- * This program is distributed in the hope that it will be useful, *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
- * GNU General Public License for more details. *
- * *
- * You should have received a copy of the GNU General Public License *
- * along with this program. If not, see <http://www.gnu.org/licenses/>. *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include "imp.h"
#include <helper/align.h>
#include <helper/binarybuffer.h>
+#include <helper/bits.h>
#include <target/algorithm.h>
+#include <target/arm_adi_v5.h>
#include <target/cortex_m.h>
-#include "bits.h"
#include "stm32l4x.h"
/* STM32L4xxx series for reference.
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" }, { 0x2001, "Y" },
};
-static const struct stm32l4_rev stm32l41_L42xx_revs[] = {
+static const struct stm32l4_rev stm32l41_l42xx_revs[] = {
{ 0x1000, "A" }, { 0x1001, "Z" }, { 0x2001, "Y" },
};
},
{
.id = DEVID_STM32L41_L42XX,
- .revs = stm32l41_L42xx_revs,
- .num_revs = ARRAY_SIZE(stm32l41_L42xx_revs),
+ .revs = stm32l41_l42xx_revs,
+ .num_revs = ARRAY_SIZE(stm32l41_l42xx_revs),
.device_str = "STM32L41/L42xx",
.max_flash_size_kb = 128,
.flags = F_NONE,
stm32l4_info->probed = false;
stm32l4_info->otp_enabled = false;
stm32l4_info->user_bank_size = bank->size;
- stm32l4_info->use_flashloader = true;
return ERROR_OK;
}
if (dev_bank_id != STM32_BANK1 && stm32l4_info->dual_bank_mode)
wrp2y_sectors_offset = stm32l4_info->bank1_sectors;
- if (wrp2y_sectors_offset > -1) {
+ if (wrp2y_sectors_offset >= 0) {
/* get WRP2AR */
ret = stm32l4_get_one_wrpxy(bank, &wrpxy[(*n_wrp)++], STM32_FLASH_WRP2AR_INDEX, wrp2y_sectors_offset);
if (ret != ERROR_OK)
return retval2;
}
-static int stm32l4_protect(struct flash_bank *bank, int set, unsigned int first, unsigned int last)
+static int stm32l4_protect_same_bank(struct flash_bank *bank, enum stm32_bank_id bank_id, int set,
+ unsigned int first, unsigned int last)
{
- struct target *target = bank->target;
- struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv;
- int ret = ERROR_OK;
unsigned int i;
- 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;
- }
-
- /* the requested sectors could be located into bank1 and/or bank2 */
- bool use_bank2 = false;
- if (last >= stm32l4_info->bank1_sectors) {
- if (first < stm32l4_info->bank1_sectors) {
- /* the requested sectors for (un)protection are shared between
- * bank 1 and 2, then split the operation */
-
- /* 1- deal with bank 1 sectors */
- LOG_DEBUG("The requested sectors for %s are shared between bank 1 and 2",
- set ? "protection" : "unprotection");
- ret = stm32l4_protect(bank, set, first, stm32l4_info->bank1_sectors - 1);
- if (ret != ERROR_OK)
- return ret;
-
- /* 2- then continue with bank 2 sectors */
- first = stm32l4_info->bank1_sectors;
- }
-
- use_bank2 = true;
- }
-
- /* refresh the sectors' protection */
- ret = stm32l4_protect_check(bank);
- if (ret != ERROR_OK)
- return ret;
-
/* check if the desired protection is already configured */
for (i = first; i <= last; i++) {
if (bank->sectors[i].is_protected != set)
unsigned int n_wrp;
struct stm32l4_wrp wrpxy[4];
- ret = stm32l4_get_all_wrpxy(bank, use_bank2 ? STM32_BANK2 : STM32_BANK1, wrpxy, &n_wrp);
+ int ret = stm32l4_get_all_wrpxy(bank, bank_id, wrpxy, &n_wrp);
if (ret != ERROR_OK)
return ret;
return stm32l4_write_all_wrpxy(bank, wrpxy, n_wrp);
}
+static int stm32l4_protect(struct flash_bank *bank, int set, unsigned int first, unsigned int last)
+{
+ 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;
+ }
+
+ /* refresh the sectors' protection */
+ int ret = stm32l4_protect_check(bank);
+ if (ret != ERROR_OK)
+ return ret;
+
+ /* the requested sectors could be located into bank1 and/or bank2 */
+ if (last < stm32l4_info->bank1_sectors) {
+ return stm32l4_protect_same_bank(bank, STM32_BANK1, set, first, last);
+ } else if (first >= stm32l4_info->bank1_sectors) {
+ return stm32l4_protect_same_bank(bank, STM32_BANK2, set, first, last);
+ } else {
+ ret = stm32l4_protect_same_bank(bank, STM32_BANK1, set, first, stm32l4_info->bank1_sectors - 1);
+ if (ret != ERROR_OK)
+ return ret;
+
+ return stm32l4_protect_same_bank(bank, STM32_BANK2, set, stm32l4_info->bank1_sectors, last);
+ }
+}
+
/* count is the size divided by stm32l4_info->data_width */
static int stm32l4_write_block(struct flash_bank *bank, const uint8_t *buffer,
uint32_t offset, uint32_t count)
armv7m_info.common_magic = ARMV7M_COMMON_MAGIC;
armv7m_info.core_mode = ARM_MODE_THREAD;
- init_reg_param(®_params[0], "r0", 32, PARAM_IN_OUT); /* buffer start, status (out) */
+ /* contrib/loaders/flash/stm32/stm32l4x.c:write() arguments */
+ init_reg_param(®_params[0], "r0", 32, PARAM_IN_OUT); /* stm32l4_work_area ptr , status (out) */
init_reg_param(®_params[1], "r1", 32, PARAM_OUT); /* buffer end */
init_reg_param(®_params[2], "r2", 32, PARAM_OUT); /* target address */
init_reg_param(®_params[3], "r3", 32, PARAM_OUT); /* count (of stm32l4_info->data_width) */
- init_reg_param(®_params[4], "sp", 32, PARAM_OUT); /* write algo stack pointer */
buf_set_u32(reg_params[0].value, 0, 32, source->address);
buf_set_u32(reg_params[1].value, 0, 32, source->address + source->size);
buf_set_u32(reg_params[2].value, 0, 32, address);
buf_set_u32(reg_params[3].value, 0, 32, count);
+
+ /* write algo stack pointer */
+ init_reg_param(®_params[4], "sp", 32, PARAM_OUT);
buf_set_u32(reg_params[4].value, 0, 32, source->address +
offsetof(struct stm32l4_work_area, stack) + LDR_STACK_SIZE);
if (retval != ERROR_OK)
goto err_lock;
- if (stm32l4_info->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 / stm32l4_info->data_width);
- }
+ /* 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_WARNING("RDP = 0x55, the work-area should be in non-secure RAM (check SAU partitioning)");
+
+ /* first try to write using the loader, for better performance */
+ retval = stm32l4_write_block(bank, buffer, offset,
+ count / stm32l4_info->data_width);
- if (!stm32l4_info->use_flashloader || retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE) {
- LOG_INFO("falling back to single memory accesses");
+ /* if resources are not available write without a loader */
+ if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE) {
+ LOG_WARNING("falling back to programming without a flash loader (slower)");
retval = stm32l4_write_block_without_loader(bank, buffer, offset,
count / stm32l4_info->data_width);
}
static int stm32l4_read_idcode(struct flash_bank *bank, uint32_t *id)
{
- int retval;
+ int retval = ERROR_OK;
+ struct target *target = bank->target;
/* try reading possible IDCODE registers, in the following order */
uint32_t dbgmcu_idcode[] = {DBGMCU_IDCODE_L4_G4, DBGMCU_IDCODE_G0, DBGMCU_IDCODE_L5};
for (unsigned int i = 0; i < ARRAY_SIZE(dbgmcu_idcode); i++) {
- retval = target_read_u32(bank->target, dbgmcu_idcode[i], id);
+ retval = target_read_u32(target, dbgmcu_idcode[i], id);
if ((retval == ERROR_OK) && ((*id & 0xfff) != 0) && ((*id & 0xfff) != 0xfff))
return ERROR_OK;
}
* DBGMCU_IDCODE cannot be read using CPU1 (Cortex-M0+) at AP1,
* to solve this read the UID64 (IEEE 64-bit unique device ID register) */
- struct cortex_m_common *cortex_m = target_to_cm(bank->target);
+ struct armv7m_common *armv7m = target_to_armv7m_safe(target);
+ if (!armv7m) {
+ LOG_ERROR("Flash requires Cortex-M target");
+ return ERROR_TARGET_INVALID;
+ }
- if (cortex_m->core_info->partno == CORTEX_M0P_PARTNO && cortex_m->armv7m.debug_ap->ap_num == 1) {
+ /* CPU2 (Cortex-M0+) is supported only with non-hla adapters because it is on AP1.
+ * Using HLA adapters armv7m.debug_ap is null, and checking ap_num triggers a segfault */
+ if (cortex_m_get_partno_safe(target) == CORTEX_M0P_PARTNO &&
+ armv7m->debug_ap && armv7m->debug_ap->ap_num == 1) {
uint32_t uid64_ids;
/* UID64 is contains
*
* read only the fixed values {STID,DEVID} from UID64_IDS to identify the device as STM32WLx
*/
- retval = target_read_u32(bank->target, UID64_IDS, &uid64_ids);
+ retval = target_read_u32(target, UID64_IDS, &uid64_ids);
if (retval == ERROR_OK && uid64_ids == UID64_IDS_STM32WL) {
/* force the DEV_ID to DEVID_STM32WLE_WL5XX and the REV_ID to unknown */
*id = DEVID_STM32WLE_WL5XX;
static int stm32l4_probe(struct flash_bank *bank)
{
struct target *target = bank->target;
- struct armv7m_common *armv7m = target_to_armv7m(target);
struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv;
const struct stm32l4_part_info *part_info;
uint16_t flash_size_kb = 0xffff;
+ if (!target_was_examined(target)) {
+ LOG_ERROR("Target not examined yet");
+ return ERROR_TARGET_NOT_EXAMINED;
+ }
+
+ struct armv7m_common *armv7m = target_to_armv7m_safe(target);
+ if (!armv7m) {
+ LOG_ERROR("Flash requires Cortex-M target");
+ return ERROR_TARGET_INVALID;
+ }
+
stm32l4_info->probed = false;
/* read stm32 device id registers */
* Ask the flash infrastructure to ensure required alignment */
bank->write_start_alignment = bank->write_end_alignment = stm32l4_info->data_width;
- /* initialise the flash registers layout */
+ /* Initialize the flash registers layout */
if (part_info->flags & F_HAS_L5_FLASH_REGS)
stm32l4_info->flash_regs = stm32l5_ns_flash_regs;
else
stm32l4_sync_rdp_tzen(bank);
- /* for devices with trustzone, use flash secure registers when TZEN=1 and RDP is LEVEL_0 */
+ /* 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;
flash_size_kb = stm32l4_info->user_bank_size / 1024;
}
- LOG_INFO("flash size = %dkbytes", flash_size_kb);
+ LOG_INFO("flash size = %d KiB", flash_size_kb);
/* did we assign a flash size? */
assert((flash_size_kb != 0xffff) && flash_size_kb);
+ const bool is_max_flash_size = flash_size_kb == stm32l4_info->part_info->max_flash_size_kb;
+
stm32l4_info->bank1_sectors = 0;
stm32l4_info->hole_sectors = 0;
int page_size_kb = 0;
stm32l4_info->dual_bank_mode = false;
- bool use_dbank_bit = false;
switch (device_id) {
case DEVID_STM32L47_L48XX:
num_pages = flash_size_kb / page_size_kb;
stm32l4_info->bank1_sectors = num_pages;
- /* check DUAL_BANK bit[21] if the flash is less than 1M */
- if (flash_size_kb == 1024 || (stm32l4_info->optr & BIT(21))) {
+ /* check DUAL_BANK option bit if the flash is less than 1M */
+ if (is_max_flash_size || (stm32l4_info->optr & FLASH_L4_DUAL_BANK)) {
stm32l4_info->dual_bank_mode = true;
stm32l4_info->bank1_sectors = num_pages / 2;
}
stm32l4_info->bank1_sectors = num_pages;
break;
case DEVID_STM32G0B_G0CXX:
- /* single/dual bank depending on bit(21) */
+ /* single/dual bank depending on DUAL_BANK option bit */
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)) {
+ if (stm32l4_info->optr & FLASH_G0_DUAL_BANK) {
stm32l4_info->sr_bsy_mask = FLASH_BSY | FLASH_BSY2;
stm32l4_info->dual_bank_mode = true;
stm32l4_info->bank1_sectors = num_pages / 2;
page_size_kb = 4;
num_pages = flash_size_kb / page_size_kb;
stm32l4_info->bank1_sectors = num_pages;
- if (stm32l4_info->optr & BIT(22)) {
+ if (stm32l4_info->optr & FLASH_G4_DUAL_BANK) {
stm32l4_info->dual_bank_mode = true;
page_size_kb = 2;
num_pages = flash_size_kb / page_size_kb;
case DEVID_STM32L4R_L4SXX:
case DEVID_STM32L4P_L4QXX:
/* STM32L4R/S can be single/dual bank:
- * if size = 2M check DBANK bit(22)
- * if size = 1M check DB1M bit(21)
+ * if size = 2M check DBANK bit
+ * if size = 1M check DB1M bit
* STM32L4P/Q can be single/dual bank
- * if size = 1M check DBANK bit(22)
- * if size = 512K check DB512K bit(21)
+ * if size = 1M check DBANK bit
+ * if size = 512K check DB512K bit (same as DB1M bit)
*/
page_size_kb = 8;
num_pages = flash_size_kb / page_size_kb;
stm32l4_info->bank1_sectors = num_pages;
- use_dbank_bit = flash_size_kb == part_info->max_flash_size_kb;
- if ((use_dbank_bit && (stm32l4_info->optr & BIT(22))) ||
- (!use_dbank_bit && (stm32l4_info->optr & BIT(21)))) {
+ if ((is_max_flash_size && (stm32l4_info->optr & FLASH_L4R_DBANK)) ||
+ (!is_max_flash_size && (stm32l4_info->optr & FLASH_LRR_DB1M))) {
stm32l4_info->dual_bank_mode = true;
page_size_kb = 4;
num_pages = flash_size_kb / page_size_kb;
break;
case DEVID_STM32L55_L56XX:
/* STM32L55/L56xx can be single/dual bank:
- * if size = 512K check DBANK bit(22)
- * if size = 256K check DB256K bit(21)
+ * if size = 512K check DBANK bit
+ * if size = 256K check DB256K bit
+ *
+ * default page size is 4kb, if DBANK = 1, the page size is 2kb.
*/
- page_size_kb = 4;
+
+ page_size_kb = (stm32l4_info->optr & FLASH_L5_DBANK) ? 2 : 4;
num_pages = flash_size_kb / page_size_kb;
stm32l4_info->bank1_sectors = num_pages;
- use_dbank_bit = flash_size_kb == part_info->max_flash_size_kb;
- if ((use_dbank_bit && (stm32l4_info->optr & BIT(22))) ||
- (!use_dbank_bit && (stm32l4_info->optr & BIT(21)))) {
+
+ if ((is_max_flash_size && (stm32l4_info->optr & FLASH_L5_DBANK)) ||
+ (!is_max_flash_size && (stm32l4_info->optr & FLASH_L5_DB256))) {
stm32l4_info->dual_bank_mode = true;
- page_size_kb = 2;
- num_pages = flash_size_kb / page_size_kb;
stm32l4_info->bank1_sectors = num_pages / 2;
}
break;
case DEVID_STM32U57_U58XX:
/* if flash size is max (2M) the device is always dual bank
- * otherwise check DUALBANK bit(21)
+ * otherwise check DUALBANK
*/
page_size_kb = 8;
num_pages = flash_size_kb / page_size_kb;
stm32l4_info->bank1_sectors = num_pages;
- if ((flash_size_kb == part_info->max_flash_size_kb) || (stm32l4_info->optr & BIT(21))) {
+ if (is_max_flash_size || (stm32l4_info->optr & FLASH_U5_DUALBANK)) {
stm32l4_info->dual_bank_mode = true;
stm32l4_info->bank1_sectors = num_pages / 2;
}
page_size_kb = 2;
num_pages = flash_size_kb / page_size_kb;
stm32l4_info->bank1_sectors = num_pages;
- if (armv7m->debug_ap->ap_num == 1)
+
+ /* CPU2 (Cortex-M0+) is supported only with non-hla adapters because it is on AP1.
+ * Using HLA adapters armv7m->debug_ap is null, and checking ap_num triggers a segfault */
+ if (armv7m->debug_ap && armv7m->debug_ap->ap_num == 1)
stm32l4_info->flash_regs = stm32wl_cpu2_flash_regs;
break;
default:
return ERROR_FAIL;
}
+ /* ensure that at least there is 1 flash sector / page */
+ if (num_pages == 0) {
+ if (stm32l4_info->user_bank_size)
+ LOG_ERROR("The specified flash size is less than page size");
+
+ LOG_ERROR("Flash pages count cannot be zero");
+ return ERROR_FAIL;
+ }
+
LOG_INFO("flash mode : %s-bank", stm32l4_info->dual_bank_mode ? "dual" : "single");
const int gap_size_kb = stm32l4_info->hole_sectors * page_size_kb;
if (stm32l4_info->probed) {
uint32_t optr_cur;
+ /* save flash_regs_base */
+ uint32_t saved_flash_regs_base = stm32l4_info->flash_regs_base;
+
+ /* for devices with TrustZone, use NS flash registers to read OPTR */
+ if (stm32l4_info->part_info->flags & F_HAS_L5_FLASH_REGS)
+ stm32l4_info->flash_regs_base &= ~STM32L5_REGS_SEC_OFFSET;
+
/* read flash option register and re-probe if optr value is changed */
int retval = stm32l4_read_flash_reg_by_index(bank, STM32_FLASH_OPTR_INDEX, &optr_cur);
+
+ /* restore saved flash_regs_base */
+ stm32l4_info->flash_regs_base = saved_flash_regs_base;
+
if (retval != ERROR_OK)
return retval;
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,