/* 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
#define FLASH_PSIZE_16 (1 << 8)
#define FLASH_PSIZE_32 (2 << 8)
#define FLASH_PSIZE_64 (3 << 8)
-/* The sector number encoding is not straight binary for dual bank flash.
- * Warning: evaluates the argument multiple times */
-#define FLASH_SNB(a) ((((a) >= 12) ? 0x10 | ((a) - 12) : (a)) << 3)
+/* The sector number encoding is not straight binary for dual bank flash. */
+#define FLASH_SNB(a) ((a) << 3)
#define FLASH_LOCK (1 << 31)
/* FLASH_SR register bits */
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;
/* Clear but report errors */
if (status & FLASH_ERROR) {
+ if (retval == ERROR_OK)
+ retval = ERROR_FAIL;
/* If this operation fails, we ignore it and report the original
* retval
*/
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;
static int stm32x_erase(struct flash_bank *bank, int first, int last)
{
+ struct stm32x_flash_bank *stm32x_info = bank->driver_priv;
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) {
*/
for (i = first; i <= last; i++) {
+ int snb;
+ if (stm32x_info->has_large_mem && i >= 12)
+ snb = (i - 12) | 0x10;
+ else
+ snb = i;
+
retval = target_write_u32(target,
- stm32x_get_flash_reg(bank, STM32_FLASH_CR), FLASH_SER | FLASH_SNB(i) | FLASH_STRT);
+ stm32x_get_flash_reg(bank, STM32_FLASH_CR), FLASH_SER | FLASH_SNB(snb) | FLASH_STRT);
if (retval != ERROR_OK)
return retval;
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) {
struct armv7m_algorithm armv7m_info;
int retval = ERROR_OK;
- /* see contrib/loaders/flash/stm32f2x.S for src */
-
static const uint8_t stm32x_flash_write_code[] = {
- /* wait_fifo: */
- 0xD0, 0xF8, 0x00, 0x80, /* ldr r8, [r0, #0] */
- 0xB8, 0xF1, 0x00, 0x0F, /* cmp r8, #0 */
- 0x1A, 0xD0, /* beq exit */
- 0x47, 0x68, /* ldr r7, [r0, #4] */
- 0x47, 0x45, /* cmp r7, r8 */
- 0xF7, 0xD0, /* beq wait_fifo */
-
- 0xDF, 0xF8, 0x34, 0x60, /* ldr r6, STM32_PROG16 */
- 0x26, 0x61, /* str r6, [r4, #STM32_FLASH_CR_OFFSET] */
- 0x37, 0xF8, 0x02, 0x6B, /* ldrh r6, [r7], #0x02 */
- 0x22, 0xF8, 0x02, 0x6B, /* strh r6, [r2], #0x02 */
- 0xBF, 0xF3, 0x4F, 0x8F, /* dsb sy */
- /* busy: */
- 0xE6, 0x68, /* ldr r6, [r4, #STM32_FLASH_SR_OFFSET] */
- 0x16, 0xF4, 0x80, 0x3F, /* tst r6, #0x10000 */
- 0xFB, 0xD1, /* bne busy */
- 0x16, 0xF0, 0xF0, 0x0F, /* tst r6, #0xf0 */
- 0x07, 0xD1, /* bne error */
-
- 0x8F, 0x42, /* cmp r7, r1 */
- 0x28, 0xBF, /* it cs */
- 0x00, 0xF1, 0x08, 0x07, /* addcs r7, r0, #8 */
- 0x47, 0x60, /* str r7, [r0, #4] */
- 0x01, 0x3B, /* subs r3, r3, #1 */
- 0x13, 0xB1, /* cbz r3, exit */
- 0xDF, 0xE7, /* b wait_fifo */
- /* error: */
- 0x00, 0x21, /* movs r1, #0 */
- 0x41, 0x60, /* str r1, [r0, #4] */
- /* exit: */
- 0x30, 0x46, /* mov r0, r6 */
- 0x00, 0xBE, /* bkpt #0x00 */
-
- /* <STM32_PROG16>: */
- 0x01, 0x01, 0x00, 0x00, /* .word 0x00000101 */
+#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");
retval = target_write_buffer(target, write_algorithm->address,
sizeof(stm32x_flash_write_code),
stm32x_flash_write_code);
- if (retval != ERROR_OK)
+ if (retval != ERROR_OK) {
+ target_free_working_area(target, write_algorithm);
return retval;
+ }
/* memory buffer */
while (target_alloc_working_area_try(target, buffer_size, &source) != ERROR_OK) {
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;
}
case 0x1000:
rev_str = "A";
break;
+ case 0x1001:
+ rev_str = "Z";
+ break;
}
break;
/* 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
};
.erase_check = default_flash_blank_check,
.protect_check = stm32x_protect_check,
.info = get_stm32x_info,
+ .free_driver_priv = default_flash_free_driver_priv,
};