+static int stm32x_unlock_option_reg(struct target *target)
+{
+ uint32_t ctrl;
+
+ int retval = target_read_u32(target, STM32_FLASH_OPTCR, &ctrl);
+ if (retval != ERROR_OK)
+ return retval;
+
+ if ((ctrl & OPTCR_LOCK) == 0)
+ return ERROR_OK;
+
+ /* unlock option registers */
+ retval = target_write_u32(target, STM32_FLASH_OPTKEYR, OPTKEY1);
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = target_write_u32(target, STM32_FLASH_OPTKEYR, OPTKEY2);
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = target_read_u32(target, STM32_FLASH_OPTCR, &ctrl);
+ if (retval != ERROR_OK)
+ return retval;
+
+ if (ctrl & OPTCR_LOCK) {
+ LOG_ERROR("options not unlocked STM32_FLASH_OPTCR: %" PRIx32, ctrl);
+ return ERROR_TARGET_FAILURE;
+ }
+
+ return ERROR_OK;
+}
+
+static int stm32x_read_options(struct flash_bank *bank)
+{
+ uint32_t optiondata;
+ struct stm32x_flash_bank *stm32x_info = NULL;
+ struct target *target = bank->target;
+
+ stm32x_info = bank->driver_priv;
+
+ /* read current option bytes */
+ int retval = target_read_u32(target, STM32_FLASH_OPTCR, &optiondata);
+ if (retval != ERROR_OK)
+ return retval;
+
+ /* 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) & (~(0xffff << stm32x_info->protection_bits) & 0xffff);
+
+ 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 << (stm32x_info->protection_bits - 12)) & 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;
+
+ /* 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->has_optcr2_pcrop) {
+ retval = target_read_u32(target, STM32_FLASH_OPTCR2, &optiondata);
+ if (retval != ERROR_OK)
+ return retval;
+
+ stm32x_info->option_bytes.optcr2_pcrop = optiondata;
+ if (stm32x_info->has_optcr2_pcrop &&
+ (stm32x_info->option_bytes.optcr2_pcrop & ~OPTCR2_PCROP_RDP)) {
+ LOG_INFO("PCROP Engaged");
+ }
+ } else {
+ stm32x_info->option_bytes.optcr2_pcrop = 0x0;
+ }
+
+ if (stm32x_info->option_bytes.RDP != 0xAA)
+ LOG_INFO("Device Security Bit Set");
+
+ return ERROR_OK;
+}
+
+static int stm32x_write_options(struct flash_bank *bank)
+{
+ struct stm32x_flash_bank *stm32x_info = NULL;
+ struct target *target = bank->target;
+ uint32_t optiondata, optiondata2;
+
+ stm32x_info = bank->driver_priv;
+
+ int retval = stm32x_unlock_option_reg(target);
+ if (retval != ERROR_OK)
+ return retval;
+
+ /* rebuild option data */
+ optiondata = stm32x_info->option_bytes.user_options & 0xfc;
+ optiondata |= stm32x_info->option_bytes.RDP << 8;
+ optiondata |= (stm32x_info->option_bytes.protection &
+ (~(0xffff << stm32x_info->protection_bits))) << 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 << (stm32x_info->protection_bits - 12)) & 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 extra pcrop register */
+ if (stm32x_info->has_optcr2_pcrop) {
+ retval = target_write_u32(target, STM32_FLASH_OPTCR2,
+ stm32x_info->option_bytes.optcr2_pcrop);
+ if (retval != ERROR_OK)
+ return retval;
+ }
+
+ /* program options */
+ retval = target_write_u32(target, STM32_FLASH_OPTCR, optiondata);
+ if (retval != ERROR_OK)
+ return retval;
+
+ /* start programming cycle */
+ retval = target_write_u32(target, STM32_FLASH_OPTCR, optiondata | OPTCR_START);
+ if (retval != ERROR_OK)
+ return retval;
+
+ /* wait for completion, this might trigger a security erase and take a while */
+ retval = stm32x_wait_status_busy(bank, FLASH_MASS_ERASE_TIMEOUT);
+ if (retval != ERROR_OK)
+ return retval;
+
+ /* relock registers */
+ retval = target_write_u32(target, STM32_FLASH_OPTCR, optiondata | OPTCR_LOCK);
+ if (retval != ERROR_OK)
+ return retval;
+
+ return ERROR_OK;
+}
+