From 4e9ee81f0c2c4b86ce006206ae9b65e89210e784 Mon Sep 17 00:00:00 2001 From: Andreas Bolsch Date: Mon, 20 Jun 2016 21:02:29 +0200 Subject: [PATCH] Flash handling for STM32F76x/77x and F446 added - added ids for various parts - rewrite of sector allocation to deal with dual-bank F76x/77x - single- / dual-bank mode for F76x/77x - sector protection adapted for F76x/77x in dual-bank mode - handling of additional option bits (28-31) in FLASH_OPTCR in options_read and options_write for F42x/43x/469/479/7xx, options bits 0-1 masked out - check for sensible value of user_options in options_write - some #defines clarified, non-needed ones removed - docs updated (options read, options write) Change-Id: Ie4db80e60baa7d2663e024ab1f278640b1ce901b Signed-off-by: Andreas Bolsch Reviewed-on: http://openocd.zylin.com/3526 Tested-by: jenkins Reviewed-by: Andreas Fritiofson --- doc/openocd.texi | 17 +- src/flash/nor/stm32f2x.c | 414 ++++++++++++++++++++++++++++++--------- 2 files changed, 331 insertions(+), 100 deletions(-) diff --git a/doc/openocd.texi b/doc/openocd.texi index 983ce3ca9..dcb843acb 100644 --- a/doc/openocd.texi +++ b/doc/openocd.texi @@ -5827,8 +5827,8 @@ The @var{num} parameter is a value shown by @command{flash banks}. @end deffn @deffn {Flash Driver} stm32f2x -All members of the STM32F2 and STM32F4 microcontroller families from ST Microelectronics -include internal flash and use ARM Cortex-M3/M4 cores. +All members of the STM32F2, STM32F4 and STM32F7 microcontroller families from ST Microelectronics +include internal flash and use ARM Cortex-M3/M4/M7 cores. The driver automatically recognizes a number of these chips using the chip identification register, and autoconfigures itself. @@ -5851,6 +5851,19 @@ The @var{num} parameter is a value shown by @command{flash banks}. Unlocks the entire stm32 device. The @var{num} parameter is a value shown by @command{flash banks}. @end deffn + +@deffn Command {stm32f2x options_read} num +Reads and displays user options and (where implemented) boot_addr0 and boot_addr1. +The @var{num} parameter is a value shown by @command{flash banks}. +@end deffn + +@deffn Command {stm32f2x options_write} num user_options boot_addr0 boot_addr1 +Writes user options and (where implemented) boot_addr0 and boot_addr1 in raw format. +Warning: The meaning of the various bits depends on the device, always check datasheet! +The @var{num} parameter is a value shown by @command{flash banks}, user_options a +12 bit value, consisting of bits 31-28 and 7-0 of FLASH_OPTCR, boot_addr0 and boot_addr1 +two halfwords (of FLASH_OPTCR1). +@end deffn @end deffn @deffn {Flash Driver} stm32lx diff --git a/src/flash/nor/stm32f2x.c b/src/flash/nor/stm32f2x.c index 4269c4461..1acab8222 100644 --- a/src/flash/nor/stm32f2x.c +++ b/src/flash/nor/stm32f2x.c @@ -63,9 +63,15 @@ * 1 MiByte STM32F42x/43x part with DB1M Option set: * 4 x 16, 1 x 64, 3 x 128, 4 x 16, 1 x 64, 3 x 128. * - * STM32F7 + * STM32F7[4|5] * 1 MiByte part with 4 x 32, 1 x 128, 3 x 256. * + * STM32F7[6|7] + * 1 MiByte part in single bank mode with 4 x 32, 1 x 128, 3 x 256. + * 1 MiByte part in dual-bank mode two banks with 4 x 16, 1 x 64, 3 x 128 each. + * 2 MiByte part in single-bank mode with 4 x 32, 1 x 128, 7 x 256. + * 2 MiByte part in dual-bank mode two banks with 4 x 16, 1 x 64, 7 x 128 each. + * * Protection size is sector size. * * Tested with STM3220F-EVAL board. @@ -84,6 +90,9 @@ * RM0385 * http://www.st.com/web/en/resource/technical/document/reference_manual/DM00124865.pdf * + * RM0410 + * http://www.st.com/resource/en/reference_manual/dm00224583.pdf + * * STM32F1x series - notice that this code was copy, pasted and knocked * into a stm32f2x driver, so in case something has been converted or * bugs haven't been fixed, here are the original manuals: @@ -111,11 +120,10 @@ #define STM32_FLASH_OPTCR1 0x40023c18 /* FLASH_CR register bits */ - #define FLASH_PG (1 << 0) #define FLASH_SER (1 << 1) -#define FLASH_MER (1 << 2) -#define FLASH_MER1 (1 << 15) +#define FLASH_MER (1 << 2) /* MER/MER1 for f76x/77x */ +#define FLASH_MER1 (1 << 15) /* MER2 for f76x/77x, confusing ... */ #define FLASH_STRT (1 << 16) #define FLASH_PSIZE_8 (0 << 8) #define FLASH_PSIZE_16 (1 << 8) @@ -127,7 +135,6 @@ #define FLASH_LOCK (1 << 31) /* FLASH_SR register bits */ - #define FLASH_BSY (1 << 16) #define FLASH_PGSERR (1 << 7) /* Programming sequence error */ #define FLASH_PGPERR (1 << 6) /* Programming parallelism error */ @@ -138,22 +145,12 @@ #define FLASH_ERROR (FLASH_PGSERR | FLASH_PGPERR | FLASH_PGAERR | FLASH_WRPERR | FLASH_OPERR) /* STM32_FLASH_OPTCR register bits */ - -#define OPT_LOCK (1 << 0) -#define OPT_START (1 << 1) - -/* STM32_FLASH_OBR bit definitions (reading) */ - -#define OPT_ERROR 0 -#define OPT_READOUT 1 -#define OPT_RDWDGSW 2 -#define OPT_RDRSTSTOP 3 -#define OPT_RDRSTSTDBY 4 -#define OPT_BFB2 5 /* dual flash bank only */ -#define OPT_DB1M 14 /* 1 MiB devices dual flash bank option */ +#define OPTCR_LOCK (1 << 0) +#define OPTCR_START (1 << 1) +#define OPTCR_NDBANK (1 << 29) /* not dual bank mode */ +#define OPTCR_DB1M (1 << 30) /* 1 MiB devices dual flash bank option */ /* register unlock keys */ - #define KEY1 0x45670123 #define KEY2 0xCDEF89AB @@ -163,14 +160,17 @@ struct stm32x_options { uint8_t RDP; - uint8_t user_options; + uint16_t user_options; /* bit 0-7 usual options, bit 8-11 extra options */ uint32_t protection; + uint32_t boot_addr; }; struct stm32x_flash_bank { struct stm32x_options option_bytes; int probed; - bool has_large_mem; /* stm32f42x/stm32f43x family */ + bool has_large_mem; /* F42x/43x/469/479/7xx in dual bank mode */ + bool has_boot_addr; /* F7xx */ + bool has_extra_options; /* F42x/43x/469/479/7xx */ uint32_t user_bank_size; }; @@ -284,7 +284,7 @@ static int stm32x_unlock_option_reg(struct target *target) if (retval != ERROR_OK) return retval; - if ((ctrl & OPT_LOCK) == 0) + if ((ctrl & OPTCR_LOCK) == 0) return ERROR_OK; /* unlock option registers */ @@ -300,7 +300,7 @@ static int stm32x_unlock_option_reg(struct target *target) if (retval != ERROR_OK) return retval; - if (ctrl & OPT_LOCK) { + if (ctrl & OPTCR_LOCK) { LOG_ERROR("options not unlocked STM32_FLASH_OPTCR: %" PRIx32, ctrl); return ERROR_TARGET_FAILURE; } @@ -321,18 +321,30 @@ static int stm32x_read_options(struct flash_bank *bank) if (retval != ERROR_OK) return retval; - stm32x_info->option_bytes.user_options = optiondata & 0xec; + /* caution: F2 implements 5 bits (WDG_SW only) + * whereas F7 6 bits (IWDG_SW and WWDG_SW) in user_options */ + stm32x_info->option_bytes.user_options = optiondata & 0xfc; stm32x_info->option_bytes.RDP = (optiondata >> 8) & 0xff; stm32x_info->option_bytes.protection = (optiondata >> 16) & 0xfff; - if (stm32x_info->has_large_mem) { + if (stm32x_info->has_extra_options) { + /* F42x/43x/469/479 and 7xx have up to 4 bits of extra options */ + stm32x_info->option_bytes.user_options |= (optiondata >> 20) & 0xf00; + } + if (stm32x_info->has_large_mem || stm32x_info->has_boot_addr) { retval = target_read_u32(target, STM32_FLASH_OPTCR1, &optiondata); if (retval != ERROR_OK) return retval; - /* append protection bits */ - stm32x_info->option_bytes.protection |= (optiondata >> 4) & 0x00fff000; + /* FLASH_OPTCR1 has quite diffent meanings ... */ + if (stm32x_info->has_boot_addr) { + /* for F7xx it contains boot0 and boot1 */ + stm32x_info->option_bytes.boot_addr = optiondata; + } else { + /* for F42x/43x/469/479 it contains 12 additional protection bits */ + stm32x_info->option_bytes.protection |= (optiondata >> 4) & 0x00fff000; + } } if (stm32x_info->option_bytes.RDP != 0xAA) @@ -345,7 +357,7 @@ static int stm32x_write_options(struct flash_bank *bank) { struct stm32x_flash_bank *stm32x_info = NULL; struct target *target = bank->target; - uint32_t optiondata; + uint32_t optiondata, optiondata2; stm32x_info = bank->driver_priv; @@ -354,26 +366,36 @@ static int stm32x_write_options(struct flash_bank *bank) return retval; /* rebuild option data */ - optiondata = stm32x_info->option_bytes.user_options; + optiondata = stm32x_info->option_bytes.user_options & 0xfc; optiondata |= stm32x_info->option_bytes.RDP << 8; optiondata |= (stm32x_info->option_bytes.protection & 0x0fff) << 16; + if (stm32x_info->has_extra_options) { + /* F42x/43x/469/479 and 7xx have up to 4 bits of extra options */ + optiondata |= (stm32x_info->option_bytes.user_options & 0xf00) << 20; + } + + if (stm32x_info->has_large_mem || stm32x_info->has_boot_addr) { + if (stm32x_info->has_boot_addr) { + /* F7xx uses FLASH_OPTCR1 for boot0 and boot1 ... */ + optiondata2 = stm32x_info->option_bytes.boot_addr; + } else { + /* F42x/43x/469/479 uses FLASH_OPTCR1 for additional protection bits */ + optiondata2 = (stm32x_info->option_bytes.protection & 0x00fff000) << 4; + } + + retval = target_write_u32(target, STM32_FLASH_OPTCR1, optiondata2); + if (retval != ERROR_OK) + return retval; + } + /* program options */ retval = target_write_u32(target, STM32_FLASH_OPTCR, optiondata); if (retval != ERROR_OK) return retval; - if (stm32x_info->has_large_mem) { - - uint32_t optiondata2 = 0; - optiondata2 |= (stm32x_info->option_bytes.protection & 0x00fff000) << 4; - retval = target_write_u32(target, STM32_FLASH_OPTCR1, optiondata2); - if (retval != ERROR_OK) - return retval; - } - /* start programming cycle */ - retval = target_write_u32(target, STM32_FLASH_OPTCR, optiondata | OPT_START); + retval = target_write_u32(target, STM32_FLASH_OPTCR, optiondata | OPTCR_START); if (retval != ERROR_OK) return retval; @@ -383,7 +405,7 @@ static int stm32x_write_options(struct flash_bank *bank) return retval; /* relock registers */ - retval = target_write_u32(target, STM32_FLASH_OPTCR, optiondata | OPT_LOCK); + retval = target_write_u32(target, STM32_FLASH_OPTCR, optiondata | OPTCR_LOCK); if (retval != ERROR_OK) return retval; @@ -401,11 +423,25 @@ static int stm32x_protect_check(struct flash_bank *bank) return retval; } - for (int i = 0; i < bank->num_sectors; i++) { - if (stm32x_info->option_bytes.protection & (1 << i)) - bank->sectors[i].is_protected = 0; - else - bank->sectors[i].is_protected = 1; + if (stm32x_info->has_boot_addr && stm32x_info->has_large_mem) { + /* F76x/77x: bit k protects sectors 2*k and 2*k+1 */ + for (int i = 0; i < (bank->num_sectors >> 1); i++) { + if (stm32x_info->option_bytes.protection & (1 << i)) { + bank->sectors[i << 1].is_protected = 0; + bank->sectors[(i << 1) + 1].is_protected = 0; + } else { + bank->sectors[i << 1].is_protected = 1; + bank->sectors[(i << 1) + 1].is_protected = 1; + } + } + } else { + /* one protection bit per sector */ + for (int i = 0; i < bank->num_sectors; i++) { + if (stm32x_info->option_bytes.protection & (1 << i)) + bank->sectors[i].is_protected = 0; + else + bank->sectors[i].is_protected = 1; + } } return ERROR_OK; @@ -416,8 +452,7 @@ static int stm32x_erase(struct flash_bank *bank, int first, int last) struct target *target = bank->target; int i; - assert(first < bank->num_sectors); - assert(last < bank->num_sectors); + assert((0 <= first) && (first <= last) && (last < bank->num_sectors)); if (bank->target->state != TARGET_HALTED) { LOG_ERROR("Target not halted"); @@ -477,8 +512,18 @@ static int stm32x_protect(struct flash_bank *bank, int set, int first, int last) return retval; } - for (int i = first; i <= last; i++) { + if (stm32x_info->has_boot_addr && stm32x_info->has_large_mem) { + /* F76x/77x: bit k protects sectors 2*k and 2*k+1 */ + if ((first & 1) != 0 || (last & 1) != 1) { + LOG_ERROR("sector protection must be double sector aligned"); + return ERROR_FAIL; + } else { + first >>= 1; + last >>= 1; + } + } + for (int i = first; i <= last; i++) { if (set) stm32x_info->option_bytes.protection &= ~(1 << i); else @@ -719,14 +764,31 @@ static int stm32x_write(struct flash_bank *bank, const uint8_t *buffer, return target_write_u32(target, STM32_FLASH_CR, FLASH_LOCK); } -static void setup_sector(struct flash_bank *bank, int start, int num, int size) +static int setup_sector(struct flash_bank *bank, int start, int num, int size) { + for (int i = start; i < (start + num) ; i++) { assert(i < bank->num_sectors); 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); } + + return start + num; +} + +static void setup_bank(struct flash_bank *bank, int start, + uint16_t flash_size_in_kb, uint16_t max_sector_size_in_kb) +{ + int remain; + + start = setup_sector(bank, start, 4, (max_sector_size_in_kb / 8) * 1024); + start = setup_sector(bank, start, 1, (max_sector_size_in_kb / 2) * 1024); + + /* remaining sectors all of size max_sector_size_in_kb */ + remain = (flash_size_in_kb / max_sector_size_in_kb) - 1; + start = setup_sector(bank, start, remain, max_sector_size_in_kb * 1024); } static int stm32x_get_device_id(struct flash_bank *bank, uint32_t *device_id) @@ -774,6 +836,8 @@ static int stm32x_probe(struct flash_bank *bank) stm32x_info->probed = 0; stm32x_info->has_large_mem = false; + stm32x_info->has_boot_addr = false; + stm32x_info->has_extra_options = false; /* read stm32 device id register */ int retval = stm32x_get_device_id(bank, &device_id); @@ -781,33 +845,50 @@ static int stm32x_probe(struct flash_bank *bank) return retval; LOG_INFO("device id = 0x%08" PRIx32 "", device_id); - /* set max flash size depending on family */ + /* set max flash size depending on family, id taken from AN2606 */ switch (device_id & 0xfff) { - case 0x411: - case 0x413: - case 0x441: + case 0x411: /* F20x/21x */ + case 0x413: /* F40x/41x */ max_flash_size_in_kb = 1024; break; - case 0x419: - case 0x434: + + case 0x419: /* F42x/43x */ + case 0x434: /* F469/479 */ + stm32x_info->has_extra_options = true; max_flash_size_in_kb = 2048; break; - case 0x423: + + case 0x423: /* F401xB/C */ max_flash_size_in_kb = 256; break; - case 0x431: - case 0x433: - case 0x421: + + case 0x421: /* F446 */ + case 0x431: /* F411 */ + case 0x433: /* F401xD/E */ + case 0x441: /* F412 */ max_flash_size_in_kb = 512; break; - case 0x458: + + case 0x458: /* F410 */ max_flash_size_in_kb = 128; break; - case 0x449: + + case 0x449: /* F74x/75x */ max_flash_size_in_kb = 1024; max_sector_size_in_kb = 256; flash_size_reg = 0x1FF0F442; + stm32x_info->has_extra_options = true; + stm32x_info->has_boot_addr = true; break; + + case 0x451: /* F76x/77x */ + max_flash_size_in_kb = 2048; + max_sector_size_in_kb = 256; + flash_size_reg = 0x1FF0F442; + stm32x_info->has_extra_options = true; + stm32x_info->has_boot_addr = true; + break; + default: LOG_WARNING("Cannot identify target as a STM32 family."); return ERROR_FAIL; @@ -836,33 +917,48 @@ static int stm32x_probe(struct flash_bank *bank) /* did we assign flash size? */ assert(flash_size_in_kb != 0xffff); - /* calculate numbers of pages */ - int num_pages = (flash_size_in_kb / max_sector_size_in_kb) + 4; - /* Devices with > 1024 kiByte always are dual-banked */ if (flash_size_in_kb > 1024) stm32x_info->has_large_mem = true; - /* F42x/43x 1024 kiByte devices have a dual bank option */ - if ((device_id & 0xfff) == 0x419 && (flash_size_in_kb == 1024)) { + /* F42x/43x/469/479 1024 kiByte devices have a dual bank option */ + if ((device_id & 0xfff) == 0x419 || (device_id & 0xfff) == 0x434) { uint32_t optiondata; retval = target_read_u32(target, STM32_FLASH_OPTCR, &optiondata); if (retval != ERROR_OK) { LOG_DEBUG("unable to read option bytes"); return retval; } - if (optiondata & (1 << OPT_DB1M)) { + if ((flash_size_in_kb > 1024) || (optiondata & OPTCR_DB1M)) { stm32x_info->has_large_mem = true; - LOG_INFO("Dual Bank 1024 kiB STM32F42x/43x found"); + LOG_INFO("Dual Bank %d kiB STM32F42x/43x/469/479 found", flash_size_in_kb); + } else { + stm32x_info->has_large_mem = false; + LOG_INFO("Single Bank %d kiB STM32F42x/43x/469/479 found", flash_size_in_kb); } } - /* check for dual-banked devices */ - if (stm32x_info->has_large_mem) - num_pages += 4; + /* F76x/77x devices have a dual bank option */ + if ((device_id & 0xfff) == 0x451) { + uint32_t optiondata; + retval = target_read_u32(target, STM32_FLASH_OPTCR, &optiondata); + if (retval != ERROR_OK) { + LOG_DEBUG("unable to read option bytes"); + return retval; + } + if (optiondata & OPTCR_NDBANK) { + stm32x_info->has_large_mem = false; + LOG_INFO("Single Bank %d kiB STM32F76x/77x found", flash_size_in_kb); + } else { + stm32x_info->has_large_mem = true; + max_sector_size_in_kb >>= 1; /* sector size divided by 2 in dual-bank mode */ + LOG_INFO("Dual Bank %d kiB STM32F76x/77x found", flash_size_in_kb); + } + } - /* check that calculation result makes sense */ - assert(num_pages > 0); + /* calculate numbers of pages */ + int num_pages = flash_size_in_kb / max_sector_size_in_kb + + (stm32x_info->has_large_mem ? 8 : 4); if (bank->sectors) { free(bank->sectors); @@ -872,35 +968,25 @@ static int stm32x_probe(struct flash_bank *bank) bank->base = base_address; bank->num_sectors = num_pages; bank->sectors = malloc(sizeof(struct flash_sector) * num_pages); - bank->size = 0; - - /* fixed memory */ - setup_sector(bank, 0, 4, (max_sector_size_in_kb / 8) * 1024); - setup_sector(bank, 4, 1, (max_sector_size_in_kb / 2) * 1024); - - if (stm32x_info->has_large_mem) { - if (flash_size_in_kb == 1024) { - setup_sector(bank, 5, 3, 128 * 1024); - setup_sector(bank, 12, 4, 16 * 1024); - setup_sector(bank, 16, 1, 64 * 1024); - setup_sector(bank, 17, 3, 128 * 1024); - } else { - setup_sector(bank, 5, 7, 128 * 1024); - setup_sector(bank, 12, 4, 16 * 1024); - setup_sector(bank, 16, 1, 64 * 1024); - setup_sector(bank, 17, 7, 128 * 1024); - } - } else { - setup_sector(bank, 4 + 1, MIN(12, num_pages) - 5, - max_sector_size_in_kb * 1024); - } for (i = 0; i < num_pages; i++) { bank->sectors[i].is_erased = -1; bank->sectors[i].is_protected = 0; } + bank->size = 0; + LOG_DEBUG("allocated %d sectors", num_pages); + + if (stm32x_info->has_large_mem) { + /* dual-bank */ + setup_bank(bank, 0, flash_size_in_kb >> 1, max_sector_size_in_kb); + setup_bank(bank, num_pages >> 1, flash_size_in_kb >> 1, + max_sector_size_in_kb); + } else { + /* single-bank */ + setup_bank(bank, 0, flash_size_in_kb, max_sector_size_in_kb); + } + assert((bank->size >> 10) == flash_size_in_kb); stm32x_info->probed = 1; - return ERROR_OK; } @@ -950,11 +1036,24 @@ static int get_stm32x_info(struct flash_bank *bank, char *buf, int buf_size) case 0x2003: rev_str = "X"; break; + + case 0x2007: + rev_str = "1"; + break; + + case 0x200F: + rev_str = "V"; + break; + + case 0x201F: + rev_str = "2"; + break; } break; case 0x413: case 0x419: + case 0x434: device_str = "STM32F4xx"; switch (rev_id) { @@ -979,6 +1078,7 @@ static int get_stm32x_info(struct flash_bank *bank, char *buf, int buf_size) break; } break; + case 0x421: device_str = "STM32F446"; @@ -988,6 +1088,7 @@ static int get_stm32x_info(struct flash_bank *bank, char *buf, int buf_size) break; } break; + case 0x423: case 0x431: case 0x433: @@ -1019,8 +1120,9 @@ static int get_stm32x_info(struct flash_bank *bank, char *buf, int buf_size) break; } break; - case 0x434: - device_str = "STM32F46x/F47x"; + + case 0x451: + device_str = "STM32F7[6|7]x"; switch (rev_id) { case 0x1000: @@ -1146,6 +1248,7 @@ static int stm32x_mass_erase(struct flash_bank *bank) flash_mer = FLASH_MER | FLASH_MER1; else flash_mer = FLASH_MER; + retval = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_CR), flash_mer); if (retval != ERROR_OK) return retval; @@ -1193,6 +1296,107 @@ COMMAND_HANDLER(stm32x_handle_mass_erase_command) return retval; } +COMMAND_HANDLER(stm32f2x_handle_options_read_command) +{ + int retval; + struct flash_bank *bank; + struct stm32x_flash_bank *stm32x_info = NULL; + + if (CMD_ARGC != 1) { + command_print(CMD_CTX, "stm32f2x options_read "); + return ERROR_COMMAND_SYNTAX_ERROR; + } + + retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (ERROR_OK != retval) + return retval; + + retval = stm32x_read_options(bank); + if (ERROR_OK != retval) + return retval; + + stm32x_info = bank->driver_priv; + if (stm32x_info->has_extra_options) { + if (stm32x_info->has_boot_addr) { + uint32_t boot_addr = stm32x_info->option_bytes.boot_addr; + + command_print(CMD_CTX, "stm32f2x user_options 0x%03X," + " boot_add0 0x%04X, boot_add1 0x%04X", + stm32x_info->option_bytes.user_options, + boot_addr & 0xffff, (boot_addr & 0xffff0000) >> 16); + } else { + command_print(CMD_CTX, "stm32f2x user_options 0x%03X,", + stm32x_info->option_bytes.user_options); + } + } else { + command_print(CMD_CTX, "stm32f2x user_options 0x%02X", + stm32x_info->option_bytes.user_options); + + } + + return retval; +} + +COMMAND_HANDLER(stm32f2x_handle_options_write_command) +{ + int retval; + struct flash_bank *bank; + struct stm32x_flash_bank *stm32x_info = NULL; + uint16_t user_options, boot_addr0, boot_addr1; + + if (CMD_ARGC < 1) { + command_print(CMD_CTX, "stm32f2x options_write ..."); + return ERROR_COMMAND_SYNTAX_ERROR; + } + + retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (ERROR_OK != retval) + return retval; + + retval = stm32x_read_options(bank); + if (ERROR_OK != retval) + return retval; + + stm32x_info = bank->driver_priv; + if (stm32x_info->has_boot_addr) { + if (CMD_ARGC != 4) { + command_print(CMD_CTX, "stm32f2x options_write " + " "); + return ERROR_COMMAND_SYNTAX_ERROR; + } + COMMAND_PARSE_NUMBER(u16, CMD_ARGV[2], boot_addr0); + COMMAND_PARSE_NUMBER(u16, CMD_ARGV[3], boot_addr1); + stm32x_info->option_bytes.boot_addr = boot_addr0 | (((uint32_t) boot_addr1) << 16); + } else { + if (CMD_ARGC != 2) { + command_print(CMD_CTX, "stm32f2x options_write "); + return ERROR_COMMAND_SYNTAX_ERROR; + } + } + + COMMAND_PARSE_NUMBER(u16, CMD_ARGV[1], user_options); + if (user_options & (stm32x_info->has_extra_options ? ~0xffc : ~0xfc)) { + command_print(CMD_CTX, "stm32f2x invalid user_options"); + return ERROR_COMMAND_SYNTAX_ERROR; + } + + stm32x_info->option_bytes.user_options = user_options; + + if (stm32x_write_options(bank) != ERROR_OK) { + command_print(CMD_CTX, "stm32f2x failed to write options"); + return ERROR_OK; + } + + /* switching between single- and dual-bank modes requires re-probe */ + /* ... and reprogramming of whole flash */ + stm32x_info->probed = 0; + + command_print(CMD_CTX, "stm32f2x write options complete.\n" + "INFO: a reset or power cycle is required " + "for the new settings to take effect."); + return retval; +} + static const struct command_registration stm32x_exec_command_handlers[] = { { .name = "lock", @@ -1215,6 +1419,20 @@ static const struct command_registration stm32x_exec_command_handlers[] = { .usage = "bank_id", .help = "Erase entire flash device.", }, + { + .name = "options_read", + .handler = stm32f2x_handle_options_read_command, + .mode = COMMAND_EXEC, + .usage = "bank_id", + .help = "Read and display device option bytes.", + }, + { + .name = "options_write", + .handler = stm32f2x_handle_options_write_command, + .mode = COMMAND_EXEC, + .usage = "bank_id user_options [ boot_add0 boot_add1]", + .help = "Write option bytes", + }, COMMAND_REGISTRATION_DONE };