/* Mass erase time can be as high as 32 s in x8 mode. */
#define FLASH_MASS_ERASE_TIMEOUT 33000
+#define FLASH_BANK_BASE 0x80000000
+
+#define STM32F2_OTP_SIZE 512
+#define STM32F2_OTP_SECTOR_SIZE 32
+#define STM32F2_OTP_BANK_BASE 0x1fff7800
+#define STM32F2_OTP_LOCK_BASE ((STM32F2_OTP_BANK_BASE) + (STM32F2_OTP_SIZE))
+
+/* see RM0410 section 3.6 "One-time programmable bytes" */
+#define STM32F7_OTP_SECTOR_SIZE 64
+#define STM32F7_OTP_SIZE 1024
+#define STM32F7_OTP_BANK_BASE 0x1ff0f000
+#define STM32F7_OTP_LOCK_BASE ((STM32F7_OTP_BANK_BASE) + (STM32F7_OTP_SIZE))
+
#define STM32_FLASH_BASE 0x40023c00
#define STM32_FLASH_ACR 0x40023c00
#define STM32_FLASH_KEYR 0x40023c04
struct stm32x_flash_bank {
struct stm32x_options option_bytes;
- int probed;
+ bool probed;
+ bool otp_unlocked;
bool has_large_mem; /* F42x/43x/469/479/7xx in dual bank mode */
bool has_extra_options; /* F42x/43x/469/479/7xx */
bool has_boot_addr; /* F7xx */
uint32_t user_bank_size;
};
+static bool stm32x_is_otp(struct flash_bank *bank)
+{
+ return bank->base == STM32F2_OTP_BANK_BASE ||
+ bank->base == STM32F7_OTP_BANK_BASE;
+}
+
+static bool stm32x_otp_is_f7(struct flash_bank *bank)
+{
+ return bank->base == STM32F7_OTP_BANK_BASE;
+}
+
+static int stm32x_is_otp_unlocked(struct flash_bank *bank)
+{
+ struct stm32x_flash_bank *stm32x_info = bank->driver_priv;
+
+ return stm32x_info->otp_unlocked;
+}
+
+static int stm32x_otp_disable(struct flash_bank *bank)
+{
+ struct stm32x_flash_bank *stm32x_info = bank->driver_priv;
+
+ LOG_INFO("OTP memory bank #%d is disabled for write commands.",
+ bank->bank_number);
+ stm32x_info->otp_unlocked = false;
+ return ERROR_OK;
+}
+
+static int stm32x_otp_enable(struct flash_bank *bank)
+{
+ struct stm32x_flash_bank *stm32x_info = bank->driver_priv;
+
+ if (!stm32x_info->otp_unlocked) {
+ LOG_INFO("OTP memory bank #%d is is enabled for write commands.",
+ bank->bank_number);
+ stm32x_info->otp_unlocked = true;
+ } else {
+ LOG_WARNING("OTP memory bank #%d is is already enabled for write commands.",
+ bank->bank_number);
+ }
+ return ERROR_OK;
+}
+
/* flash bank stm32x <base> <size> 0 0 <target#>
*/
FLASH_BANK_COMMAND_HANDLER(stm32x_flash_bank_command)
stm32x_info = malloc(sizeof(struct stm32x_flash_bank));
bank->driver_priv = stm32x_info;
- stm32x_info->probed = 0;
+ stm32x_info->probed = false;
+ stm32x_info->otp_unlocked = false;
stm32x_info->user_bank_size = bank->size;
return ERROR_OK;
return ERROR_OK;
}
+static int stm32x_otp_read_protect(struct flash_bank *bank)
+{
+ struct target *target = bank->target;
+ uint32_t lock_base;
+ int i, retval;
+ uint8_t lock;
+
+ lock_base = stm32x_otp_is_f7(bank) ? STM32F7_OTP_LOCK_BASE
+ : STM32F2_OTP_LOCK_BASE;
+
+ for (i = 0; i < bank->num_sectors; i++) {
+ retval = target_read_u8(target, lock_base + i, &lock);
+ if (retval != ERROR_OK)
+ return retval;
+ bank->sectors[i].is_protected = !lock;
+ }
+
+ return ERROR_OK;
+}
+
+static int stm32x_otp_protect(struct flash_bank *bank, int first, int last)
+{
+ struct target *target = bank->target;
+ uint32_t lock_base;
+ int i, retval;
+ uint8_t lock;
+
+ assert((0 <= first) && (first <= last) && (last < bank->num_sectors));
+
+ lock_base = stm32x_otp_is_f7(bank) ? STM32F7_OTP_LOCK_BASE
+ : STM32F2_OTP_LOCK_BASE;
+
+ for (i = first; first <= last; i++) {
+ retval = target_read_u8(target, lock_base + i, &lock);
+ if (retval != ERROR_OK)
+ return retval;
+ if (lock)
+ continue;
+
+ lock = 0xff;
+ retval = target_write_u8(target, lock_base + i, lock);
+ if (retval != ERROR_OK)
+ return retval;
+ }
+
+ return ERROR_OK;
+}
+
static int stm32x_protect_check(struct flash_bank *bank)
{
struct stm32x_flash_bank *stm32x_info = bank->driver_priv;
struct flash_sector *prot_blocks;
int num_prot_blocks;
+ int retval;
+
+ /* if it's the OTP bank, look at the lock bits there */
+ if (stm32x_is_otp(bank))
+ return stm32x_otp_read_protect(bank);
/* read write protection settings */
- int retval = stm32x_read_options(bank);
+ retval = stm32x_read_options(bank);
if (retval != ERROR_OK) {
LOG_DEBUG("unable to read option bytes");
return retval;
struct target *target = bank->target;
int i;
+ if (stm32x_is_otp(bank)) {
+ LOG_ERROR("Cannot erase OTP memory");
+ return ERROR_FAIL;
+ }
+
assert((0 <= first) && (first <= last) && (last < bank->num_sectors));
if (bank->target->state != TARGET_HALTED) {
return ERROR_TARGET_NOT_HALTED;
}
+ if (stm32x_is_otp(bank)) {
+ if (!set)
+ return ERROR_COMMAND_ARGUMENT_INVALID;
+
+ return stm32x_otp_protect(bank, first, last);
+ }
+
/* read protection settings */
int retval = stm32x_read_options(bank);
if (retval != ERROR_OK) {
#include "../../../contrib/loaders/flash/stm32/stm32f2x.inc"
};
+ if (stm32x_is_otp(bank) && !stm32x_is_otp_unlocked(bank)) {
+ LOG_ERROR("OTP memory bank is disabled for write commands.");
+ return ERROR_FAIL;
+ }
+
if (target_alloc_working_area(target, sizeof(stm32x_flash_write_code),
&write_algorithm) != ERROR_OK) {
LOG_WARNING("no working area available, can't do block memory writes");
bank->sectors[i].offset = bank->size;
bank->sectors[i].size = size;
bank->size += bank->sectors[i].size;
- LOG_DEBUG("sector %d: %dkBytes", i, size >> 10);
+ LOG_DEBUG("sector %d: %d kBytes", i, size >> 10);
}
return start + num;
{
struct target *target = bank->target;
struct stm32x_flash_bank *stm32x_info = bank->driver_priv;
- int i, num_prot_blocks;
+ int i, num_prot_blocks, num_sectors;
uint16_t flash_size_in_kb;
+ uint16_t otp_size_in_b;
+ uint16_t otp_sector_size;
uint32_t flash_size_reg = 0x1FFF7A22;
uint16_t max_sector_size_in_kb = 128;
uint16_t max_flash_size_in_kb;
uint32_t device_id;
uint32_t base_address = 0x08000000;
- stm32x_info->probed = 0;
+ stm32x_info->probed = false;
stm32x_info->has_large_mem = false;
stm32x_info->has_boot_addr = false;
stm32x_info->has_extra_options = false;
bank->prot_blocks = NULL;
}
+ /* if explicitely called out as OTP bank, short circuit probe */
+ if (stm32x_is_otp(bank)) {
+ if (stm32x_otp_is_f7(bank)) {
+ otp_size_in_b = STM32F7_OTP_SIZE;
+ otp_sector_size = STM32F7_OTP_SECTOR_SIZE;
+ } else {
+ otp_size_in_b = STM32F2_OTP_SIZE;
+ otp_sector_size = STM32F2_OTP_SECTOR_SIZE;
+ }
+
+ num_sectors = otp_size_in_b / otp_sector_size;
+ LOG_INFO("flash size = %d bytes", otp_size_in_b);
+
+ assert(num_sectors > 0);
+
+ bank->num_sectors = num_sectors;
+ bank->sectors = calloc(sizeof(struct flash_sector), num_sectors);
+
+ if (stm32x_otp_is_f7(bank))
+ bank->size = STM32F7_OTP_SIZE;
+ else
+ bank->size = STM32F2_OTP_SIZE;
+
+ for (i = 0; i < num_sectors; i++) {
+ bank->sectors[i].offset = i * otp_sector_size;
+ bank->sectors[i].size = otp_sector_size;
+ bank->sectors[i].is_erased = 1;
+ bank->sectors[i].is_protected = 0;
+ }
+
+ stm32x_info->probed = true;
+ return ERROR_OK;
+ }
+
/* read stm32 device id register */
int retval = stm32x_get_device_id(bank, &device_id);
if (retval != ERROR_OK)
flash_size_in_kb = stm32x_info->user_bank_size / 1024;
}
- LOG_INFO("flash size = %dkbytes", flash_size_in_kb);
+ LOG_INFO("flash size = %d kbytes", flash_size_in_kb);
/* did we assign flash size? */
assert(flash_size_in_kb != 0xffff);
bank->num_prot_blocks = num_prot_blocks;
assert((bank->size >> 10) == flash_size_in_kb);
- stm32x_info->probed = 1;
+ stm32x_info->probed = true;
return ERROR_OK;
}
/* switching between single- and dual-bank modes requires re-probe */
/* ... and reprogramming of whole flash */
- stm32x_info->probed = 0;
+ stm32x_info->probed = false;
command_print(CMD_CTX, "stm32f2x write options complete.\n"
"INFO: a reset or power cycle is required "
return retval;
}
+COMMAND_HANDLER(stm32x_handle_otp_command)
+{
+ if (CMD_ARGC < 2) {
+ command_print(CMD_CTX, "stm32x otp <bank> (enable|disable|show)");
+ 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 (stm32x_is_otp(bank)) {
+ if (strcmp(CMD_ARGV[1], "enable") == 0) {
+ stm32x_otp_enable(bank);
+ } else if (strcmp(CMD_ARGV[1], "disable") == 0) {
+ stm32x_otp_disable(bank);
+ } else if (strcmp(CMD_ARGV[1], "show") == 0) {
+ command_print(CMD_CTX,
+ "OTP memory bank #%d is %s for write commands.",
+ bank->bank_number,
+ stm32x_is_otp_unlocked(bank) ? "enabled" : "disabled");
+ } else {
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+ } else {
+ command_print(CMD_CTX, "Failed: not an OTP bank.");
+ }
+
+ return retval;
+}
+
static const struct command_registration stm32x_exec_command_handlers[] = {
{
.name = "lock",
.usage = "bank_id optcr2",
.help = "Write optcr2 word",
},
-
+ {
+ .name = "otp",
+ .handler = stm32x_handle_otp_command,
+ .mode = COMMAND_EXEC,
+ .usage = "bank_id",
+ .help = "OTP (One Time Programmable) memory write enable/disable.",
+ },
COMMAND_REGISTRATION_DONE
};