flash/nor/stm32h7x: remove options cache and some driver enhancements
[fw/openocd] / src / flash / nor / stm32h7x.c
index 01e6f06dce2f6f39b180bfbf676ea0d99a6bf2da..aec836b19d310cd4fb779e4b69510c4264367be9 100644 (file)
 #define FLASH_SR        0x10
 #define FLASH_CCR       0x14
 #define FLASH_OPTCR     0x18
-#define FLASH_OPTCUR    0x1C
-#define FLASH_OPTPRG    0x20
+#define FLASH_OPTSR_CUR 0x1C
+#define FLASH_OPTSR_PRG 0x20
 #define FLASH_OPTCCR    0x24
-#define FLASH_WPSNCUR   0x38
-#define FLASH_WPSNPRG   0x3C
+#define FLASH_WPSN_CUR  0x38
+#define FLASH_WPSN_PRG  0x3C
 
 
 /* FLASH_CR register bits */
 #define FLASH_LOCK     (1 << 0)
 #define FLASH_PG       (1 << 1)
 #define FLASH_SER      (1 << 2)
-#define FLASH_BER_CMD  (1 << 3)
+#define FLASH_BER      (1 << 3)
 #define FLASH_PSIZE_8  (0 << 4)
 #define FLASH_PSIZE_16 (1 << 4)
 #define FLASH_PSIZE_32 (2 << 4)
 
 /* FLASH_SR register bits */
 #define FLASH_BSY      (1 << 0)  /* Operation in progress */
+#define FLASH_QW       (1 << 2)  /* Operation queue in progress */
 #define FLASH_WRPERR   (1 << 17) /* Write protection error */
 #define FLASH_PGSERR   (1 << 18) /* Programming sequence error */
 #define FLASH_STRBERR  (1 << 19) /* Strobe error */
-#define FLASH_INCERR   (1 << 21) /* Increment error */
+#define FLASH_INCERR   (1 << 21) /* Inconsistency error */
 #define FLASH_OPERR    (1 << 22) /* Operation error */
 #define FLASH_RDPERR   (1 << 23) /* Read Protection error */
 #define FLASH_RDSERR   (1 << 24) /* Secure Protection error */
 #define OPT_LOCK       (1 << 0)
 #define OPT_START      (1 << 1)
 
-/* FLASH_OPTCUR bit definitions (reading) */
-#define IWDG1_HW       (1 << 4)
+/* FLASH_OPTSR register bits */
+#define OPT_BSY        (1 << 0)
+#define OPT_RDP_POS    8
+#define OPT_RDP_MASK   (0xff << OPT_RDP_POS)
+
+/* FLASH_OPTCCR register bits */
+#define OPT_CLR_OPTCHANGEERR (1 << 30)
 
 /* register unlock keys */
 #define KEY1           0x45670123
@@ -102,23 +108,12 @@ struct stm32h7x_rev {
        const char *str;
 };
 
-struct stm32x_options {
-       uint8_t RDP;
-       uint32_t protection;  /* bank1 WRP */
-       uint32_t protection2; /* bank2 WRP */
-       uint8_t user_options;
-       uint8_t user2_options;
-       uint8_t user3_options;
-       uint8_t independent_watchdog_selection;
-};
-
 struct stm32h7x_part_info {
        uint16_t id;
        const char *device_str;
        const struct stm32h7x_rev *revs;
        size_t num_revs;
        unsigned int page_size;
-       unsigned int pages_per_sector;
        uint16_t max_flash_size_kb;
        uint8_t has_dual_bank;
        uint16_t first_bank_size_kb; /* Used when has_dual_bank is true */
@@ -131,12 +126,17 @@ struct stm32h7x_flash_bank {
        uint32_t idcode;
        uint32_t user_bank_size;
        uint32_t flash_base;    /* Address of flash reg controller */
-       struct stm32x_options option_bytes;
        const struct stm32h7x_part_info *part_info;
 };
 
+enum stm32h7x_opt_rdp {
+       OPT_RDP_L0 = 0xaa,
+       OPT_RDP_L1 = 0x00,
+       OPT_RDP_L2 = 0xcc
+};
+
 static const struct stm32h7x_rev stm32_450_revs[] = {
-       { 0x1000, "A" }, { 0x1001, "Z" }, { 0x1003, "Y" },
+       { 0x1000, "A" }, { 0x1001, "Z" }, { 0x1003, "Y" }, { 0x2001, "X"  },
 };
 
 static const struct stm32h7x_part_info stm32h7x_parts[] = {
@@ -144,7 +144,7 @@ static const struct stm32h7x_part_info stm32h7x_parts[] = {
        .id                                     = 0x450,
        .revs                           = stm32_450_revs,
        .num_revs                       = ARRAY_SIZE(stm32_450_revs),
-       .device_str                     = "STM32H7xx 2M",
+       .device_str                     = "STM32H74x/75x",
        .page_size                      = 128,  /* 128 KB */
        .max_flash_size_kb      = 2048,
        .first_bank_size_kb     = 1024,
@@ -154,10 +154,6 @@ static const struct stm32h7x_part_info stm32h7x_parts[] = {
        },
 };
 
-static int stm32x_unlock_reg(struct flash_bank *bank);
-static int stm32x_lock_reg(struct flash_bank *bank);
-static int stm32x_probe(struct flash_bank *bank);
-
 /* flash bank stm32x <base> <size> 0 0 <target#> */
 
 FLASH_BANK_COMMAND_HANDLER(stm32x_flash_bank_command)
@@ -176,52 +172,62 @@ FLASH_BANK_COMMAND_HANDLER(stm32x_flash_bank_command)
        return ERROR_OK;
 }
 
-static inline uint32_t stm32x_get_flash_reg(struct flash_bank *bank, uint32_t reg)
+static inline uint32_t stm32x_get_flash_reg(struct flash_bank *bank, uint32_t reg_offset)
 {
        struct stm32h7x_flash_bank *stm32x_info = bank->driver_priv;
-       return reg + stm32x_info->flash_base;
+       return reg_offset + stm32x_info->flash_base;
+}
+
+static inline int stm32x_read_flash_reg(struct flash_bank *bank, uint32_t reg_offset, uint32_t *value)
+{
+       return target_read_u32(bank->target, stm32x_get_flash_reg(bank, reg_offset), value);
+}
+
+static inline int stm32x_write_flash_reg(struct flash_bank *bank, uint32_t reg_offset, uint32_t value)
+{
+       return target_write_u32(bank->target, stm32x_get_flash_reg(bank, reg_offset), value);
 }
 
 static inline int stm32x_get_flash_status(struct flash_bank *bank, uint32_t *status)
 {
-       struct target *target = bank->target;
-       return target_read_u32(target, stm32x_get_flash_reg(bank, FLASH_SR), status);
+       return stm32x_read_flash_reg(bank, FLASH_SR, status);
 }
 
-static int stm32x_wait_status_busy(struct flash_bank *bank, int timeout)
+static int stm32x_wait_flash_op_queue(struct flash_bank *bank, int timeout)
 {
-       struct target *target = bank->target;
        struct stm32h7x_flash_bank *stm32x_info = bank->driver_priv;
        uint32_t status;
        int retval;
 
-       /* wait for busy to clear */
+       /* wait for flash operations completion */
        for (;;) {
                retval = stm32x_get_flash_status(bank, &status);
                if (retval != ERROR_OK) {
-                       LOG_INFO("wait_status_busy, target_read_u32 : error : remote address 0x%x", stm32x_info->flash_base);
+                       LOG_INFO("wait_flash_op_queue, target_read_u32 : error : remote address 0x%x", stm32x_info->flash_base);
                        return retval;
                }
 
-               if ((status & FLASH_BSY) == 0)
+               if ((status & FLASH_QW) == 0)
                        break;
 
                if (timeout-- <= 0) {
-                       LOG_INFO("wait_status_busy, time out expired, status: 0x%" PRIx32 "", status);
+                       LOG_INFO("wait_flash_op_queue, time out expired, status: 0x%" PRIx32 "", status);
                        return ERROR_FAIL;
                }
                alive_sleep(1);
        }
 
        if (status & FLASH_WRPERR) {
-               LOG_INFO("wait_status_busy, WRPERR : error : remote address 0x%x", stm32x_info->flash_base);
+               LOG_INFO("wait_flash_op_queue, WRPERR : error : remote address 0x%x", stm32x_info->flash_base);
                retval = ERROR_FAIL;
        }
 
        /* Clear error + EOP flags 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 */
-               target_write_u32(target, stm32x_get_flash_reg(bank, FLASH_CCR), status);
+               stm32x_write_flash_reg(bank, FLASH_CCR, status);
        }
        return retval;
 }
@@ -229,12 +235,11 @@ static int stm32x_wait_status_busy(struct flash_bank *bank, int timeout)
 static int stm32x_unlock_reg(struct flash_bank *bank)
 {
        uint32_t ctrl;
-       struct target *target = bank->target;
 
        /* first check if not already unlocked
         * otherwise writing on FLASH_KEYR will fail
         */
-       int retval = target_read_u32(target, stm32x_get_flash_reg(bank, FLASH_CR), &ctrl);
+       int retval = stm32x_read_flash_reg(bank, FLASH_CR, &ctrl);
        if (retval != ERROR_OK)
                return retval;
 
@@ -242,15 +247,15 @@ static int stm32x_unlock_reg(struct flash_bank *bank)
                return ERROR_OK;
 
        /* unlock flash registers for bank */
-       retval = target_write_u32(target, stm32x_get_flash_reg(bank, FLASH_KEYR), KEY1);
+       retval = stm32x_write_flash_reg(bank, FLASH_KEYR, KEY1);
        if (retval != ERROR_OK)
                return retval;
 
-       retval = target_write_u32(target, stm32x_get_flash_reg(bank, FLASH_KEYR), KEY2);
+       retval = stm32x_write_flash_reg(bank, FLASH_KEYR, KEY2);
        if (retval != ERROR_OK)
                return retval;
 
-       retval = target_read_u32(target, stm32x_get_flash_reg(bank, FLASH_CR), &ctrl);
+       retval = stm32x_read_flash_reg(bank, FLASH_CR, &ctrl);
        if (retval != ERROR_OK)
                return retval;
 
@@ -264,9 +269,8 @@ static int stm32x_unlock_reg(struct flash_bank *bank)
 static int stm32x_unlock_option_reg(struct flash_bank *bank)
 {
        uint32_t ctrl;
-       struct target *target = bank->target;
 
-       int retval = target_read_u32(target, FLASH_REG_BASE_B0 + FLASH_OPTCR, &ctrl);
+       int retval = stm32x_read_flash_reg(bank, FLASH_OPTCR, &ctrl);
        if (retval != ERROR_OK)
                return retval;
 
@@ -274,15 +278,15 @@ static int stm32x_unlock_option_reg(struct flash_bank *bank)
                return ERROR_OK;
 
        /* unlock option registers */
-       retval = target_write_u32(target, FLASH_REG_BASE_B0 + FLASH_OPTKEYR, OPTKEY1);
+       retval = stm32x_write_flash_reg(bank, FLASH_OPTKEYR, OPTKEY1);
        if (retval != ERROR_OK)
                return retval;
 
-       retval = target_write_u32(target, FLASH_REG_BASE_B0 + FLASH_OPTKEYR, OPTKEY2);
+       retval = stm32x_write_flash_reg(bank, FLASH_OPTKEYR, OPTKEY2);
        if (retval != ERROR_OK)
                return retval;
 
-       retval = target_read_u32(target, FLASH_REG_BASE_B0 + FLASH_OPTCR, &ctrl);
+       retval = stm32x_read_flash_reg(bank, FLASH_OPTCR, &ctrl);
        if (retval != ERROR_OK)
                return retval;
 
@@ -294,169 +298,101 @@ static int stm32x_unlock_option_reg(struct flash_bank *bank)
        return ERROR_OK;
 }
 
-static int stm32x_lock_reg(struct flash_bank *bank)
+static inline int stm32x_lock_reg(struct flash_bank *bank)
 {
-       struct target *target = bank->target;
-
-       /* Lock bank reg */
-       int retval = target_write_u32(target, stm32x_get_flash_reg(bank, FLASH_CR), FLASH_LOCK);
-       if (retval != ERROR_OK)
-               return retval;
-
-       return ERROR_OK;
+       return stm32x_write_flash_reg(bank, FLASH_CR, FLASH_LOCK);
 }
 
-static int stm32x_read_options(struct flash_bank *bank)
+static inline int stm32x_lock_option_reg(struct flash_bank *bank)
 {
-       uint32_t optiondata;
-       struct stm32h7x_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, FLASH_REG_BASE_B0 + FLASH_OPTCUR, &optiondata);
-       if (retval != ERROR_OK)
-               return retval;
-
-       /* decode option data */
-       stm32x_info->option_bytes.user_options = optiondata & 0xfc;
-       stm32x_info->option_bytes.RDP = (optiondata >> 8) & 0xff;
-       stm32x_info->option_bytes.user2_options = (optiondata >> 16) & 0xff;
-       stm32x_info->option_bytes.user3_options = (optiondata >> 24) & 0x83;
-
-       if (optiondata & IWDG1_HW)
-               stm32x_info->option_bytes.independent_watchdog_selection = 1;
-       else
-               stm32x_info->option_bytes.independent_watchdog_selection = 0;
-
-       if (stm32x_info->option_bytes.RDP != 0xAA)
-               LOG_INFO("Device Security Bit Set");
-
-       /* read current WPSN option bytes */
-       retval = target_read_u32(target, FLASH_REG_BASE_B0 + FLASH_WPSNCUR, &optiondata);
-       if (retval != ERROR_OK)
-               return retval;
-       stm32x_info->option_bytes.protection = optiondata & 0xff;
-
-       /* read current WPSN2 option bytes */
-       retval = target_read_u32(target, FLASH_REG_BASE_B1 + FLASH_WPSNCUR, &optiondata);
-       if (retval != ERROR_OK)
-               return retval;
-       stm32x_info->option_bytes.protection2 = optiondata & 0xff;
-
-       return ERROR_OK;
+       return stm32x_write_flash_reg(bank, FLASH_OPTCR, OPT_LOCK);
 }
 
-static int stm32x_write_options(struct flash_bank *bank)
+static int stm32x_write_option(struct flash_bank *bank, uint32_t reg_offset, uint32_t value)
 {
-       struct stm32h7x_flash_bank *stm32x_info = NULL;
-       struct target *target = bank->target;
-       uint32_t optiondata;
-
-       stm32x_info = bank->driver_priv;
+       int retval, retval2;
 
-       int retval = stm32x_unlock_option_reg(bank);
+       /* unlock option bytes for modification */
+       retval = stm32x_unlock_option_reg(bank);
        if (retval != ERROR_OK)
-               return retval;
-
-       /* rebuild option data */
-       optiondata = stm32x_info->option_bytes.user_options;
-       optiondata |= (stm32x_info->option_bytes.RDP << 8);
-       optiondata |= (stm32x_info->option_bytes.user2_options & 0xff) << 16;
-       optiondata |= (stm32x_info->option_bytes.user3_options & 0x83) << 24;
+               goto flash_options_lock;
 
-       if (stm32x_info->option_bytes.independent_watchdog_selection)
-               optiondata |= IWDG1_HW;
-       else
-               optiondata &= ~IWDG1_HW;
-
-       /* program options */
-       retval = target_write_u32(target, FLASH_REG_BASE_B0 + FLASH_OPTPRG, optiondata);
-               if (retval != ERROR_OK)
-                       return retval;
-
-       optiondata = stm32x_info->option_bytes.protection & 0xff;
-       /* Program protection WPSNPRG */
-       retval = target_write_u32(target, FLASH_REG_BASE_B0 + FLASH_WPSNPRG, optiondata);
-               if (retval != ERROR_OK)
-                       return retval;
-
-       optiondata = stm32x_info->option_bytes.protection2 & 0xff;
-       /* Program protection WPSNPRG2 */
-       retval = target_write_u32(target, FLASH_REG_BASE_B1 + FLASH_WPSNPRG, optiondata);
-               if (retval != ERROR_OK)
-                       return retval;
+       /* write option bytes */
+       retval = stm32x_write_flash_reg(bank, reg_offset, value);
+       if (retval != ERROR_OK)
+               goto flash_options_lock;
 
-       optiondata = 0x40000000;
        /* Remove OPT error flag before programming */
-       retval = target_write_u32(target, FLASH_REG_BASE_B0 + FLASH_OPTCCR, optiondata);
-               if (retval != ERROR_OK)
-                       return retval;
+       retval = stm32x_write_flash_reg(bank, FLASH_OPTCCR, OPT_CLR_OPTCHANGEERR);
+       if (retval != ERROR_OK)
+               goto flash_options_lock;
 
        /* start programming cycle */
-       retval = target_write_u32(target, FLASH_REG_BASE_B0 + FLASH_OPTCR, OPT_START);
+       retval = stm32x_write_flash_reg(bank, FLASH_OPTCR, OPT_START);
        if (retval != ERROR_OK)
-               return retval;
+               goto flash_options_lock;
 
        /* wait for completion */
        int timeout = FLASH_ERASE_TIMEOUT;
        for (;;) {
                uint32_t status;
-               retval = target_read_u32(target, FLASH_REG_BASE_B0 + FLASH_SR, &status);
+               retval = stm32x_read_flash_reg(bank, FLASH_OPTSR_CUR, &status);
                if (retval != ERROR_OK) {
-                       LOG_INFO("stm32x_write_options: wait_status_busy : error");
-                       return retval;
+                       LOG_INFO("stm32x_options_program: failed to read FLASH_OPTSR_CUR");
+                       goto flash_options_lock;
                }
-               if ((status & FLASH_BSY) == 0)
+               if ((status & OPT_BSY) == 0)
                        break;
 
                if (timeout-- <= 0) {
-                       LOG_INFO("wait_status_busy, time out expired, status: 0x%" PRIx32 "", status);
-                       return ERROR_FAIL;
+                       LOG_INFO("waiting for OBL launch, time out expired, OPTSR: 0x%" PRIx32 "", status);
+                       retval = ERROR_FAIL;
+                       goto flash_options_lock;
                }
                alive_sleep(1);
        }
 
-       /* relock option registers */
-       retval = target_write_u32(target, FLASH_REG_BASE_B0 + FLASH_OPTCR, OPT_LOCK);
+flash_options_lock:
+       retval2 = stm32x_lock_option_reg(bank);
+       if (retval2 != ERROR_OK)
+               LOG_ERROR("error during the lock of flash options");
+
+       return (retval == ERROR_OK) ? retval2 : retval;
+}
+
+static int stm32x_modify_option(struct flash_bank *bank, uint32_t reg_offset, uint32_t value, uint32_t mask)
+{
+       uint32_t data;
+
+       int retval = stm32x_read_flash_reg(bank, reg_offset, &data);
        if (retval != ERROR_OK)
                return retval;
 
-       return ERROR_OK;
+       data = (data & ~mask) | (value & mask);
+
+       return stm32x_write_option(bank, reg_offset, data);
 }
 
 static int stm32x_protect_check(struct flash_bank *bank)
 {
-       struct stm32h7x_flash_bank *stm32x_info = bank->driver_priv;
+       uint32_t protection;
 
        /* read 'write protection' settings */
-       int retval = stm32x_read_options(bank);
+       int retval = stm32x_read_flash_reg(bank, FLASH_WPSN_CUR, &protection);
        if (retval != ERROR_OK) {
-               LOG_DEBUG("unable to read option bytes");
+               LOG_DEBUG("unable to read WPSN_CUR register");
                return retval;
        }
 
        for (int i = 0; i < bank->num_sectors; i++) {
-               if (stm32x_info->flash_base == FLASH_REG_BASE_B0) {
-                       if (stm32x_info->option_bytes.protection & (1 << i))
-                               bank->sectors[i].is_protected = 0;
-                       else
-                               bank->sectors[i].is_protected = 1;
-               } else {
-                       if (stm32x_info->option_bytes.protection2 & (1 << i))
-                               bank->sectors[i].is_protected = 0;
-                       else
-                               bank->sectors[i].is_protected = 1;
-               }
+               bank->sectors[i].is_protected = protection & (1 << i) ? 0 : 1;
        }
        return ERROR_OK;
 }
 
 static int stm32x_erase(struct flash_bank *bank, int first, int last)
 {
-       struct target *target = bank->target;
-       int retval;
+       int retval, retval2;
 
        assert(first < bank->num_sectors);
        assert(last < bank->num_sectors);
@@ -466,88 +402,80 @@ static int stm32x_erase(struct flash_bank *bank, int first, int last)
 
        retval = stm32x_unlock_reg(bank);
        if (retval != ERROR_OK)
-               return retval;
+               goto flash_lock;
 
        /*
        Sector Erase
        To erase a sector, follow the procedure below:
-       1. Check that no Flash memory operation is ongoing by checking the BSY bit in the
+       1. Check that no Flash memory operation is ongoing by checking the QW bit in the
          FLASH_SR register
        2. Set the SER bit and select the sector
          you wish to erase (SNB) in the FLASH_CR register
        3. Set the STRT bit in the FLASH_CR register
-       4. Wait for the BSY bit to be cleared
+       4. Wait for flash operations completion
         */
        for (int i = first; i <= last; i++) {
                LOG_DEBUG("erase sector %d", i);
-               retval = target_write_u32(target, stm32x_get_flash_reg(bank, FLASH_CR),
+               retval = stm32x_write_flash_reg(bank, FLASH_CR,
                                FLASH_SER | FLASH_SNB(i) | FLASH_PSIZE_64);
                if (retval != ERROR_OK) {
                        LOG_ERROR("Error erase sector %d", i);
-                       return retval;
+                       goto flash_lock;
                }
-               retval = target_write_u32(target, stm32x_get_flash_reg(bank, FLASH_CR),
+               retval = stm32x_write_flash_reg(bank, FLASH_CR,
                                FLASH_SER | FLASH_SNB(i) | FLASH_PSIZE_64 | FLASH_START);
                if (retval != ERROR_OK) {
                        LOG_ERROR("Error erase sector %d", i);
-                       return retval;
+                       goto flash_lock;
                }
-               retval = stm32x_wait_status_busy(bank, FLASH_ERASE_TIMEOUT);
+               retval = stm32x_wait_flash_op_queue(bank, FLASH_ERASE_TIMEOUT);
 
                if (retval != ERROR_OK) {
-                       LOG_ERROR("erase time-out error sector %d", i);
-                       return retval;
+                       LOG_ERROR("erase time-out or operation error sector %d", i);
+                       goto flash_lock;
                }
                bank->sectors[i].is_erased = 1;
        }
 
-       retval = stm32x_lock_reg(bank);
-       if (retval != ERROR_OK) {
+flash_lock:
+       retval2 = stm32x_lock_reg(bank);
+       if (retval2 != ERROR_OK)
                LOG_ERROR("error during the lock of flash");
-               return retval;
-       }
 
-       return ERROR_OK;
+       return (retval == ERROR_OK) ? retval2 : retval;
 }
 
 static int stm32x_protect(struct flash_bank *bank, int set, int first, int last)
 {
        struct target *target = bank->target;
-       struct stm32h7x_flash_bank *stm32x_info = bank->driver_priv;
+       uint32_t protection;
 
        if (target->state != TARGET_HALTED) {
                LOG_ERROR("Target not halted");
                return ERROR_TARGET_NOT_HALTED;
        }
-       /* read protection settings */
-       int retval = stm32x_read_options(bank);
+
+       /* read 'write protection' settings */
+       int retval = stm32x_read_flash_reg(bank, FLASH_WPSN_CUR, &protection);
        if (retval != ERROR_OK) {
-               LOG_DEBUG("unable to read option bytes");
+               LOG_DEBUG("unable to read WPSN_CUR register");
                return retval;
        }
 
        for (int i = first; i <= last; i++) {
-               if (stm32x_info->flash_base == FLASH_REG_BASE_B0) {
-                       if (set)
-                               stm32x_info->option_bytes.protection &= ~(1 << i);
-                       else
-                               stm32x_info->option_bytes.protection |= (1 << i);
-               } else {
-                       if (set)
-                               stm32x_info->option_bytes.protection2 &= ~(1 << i);
-                       else
-                               stm32x_info->option_bytes.protection2 |= (1 << i);
-               }
+               if (set)
+                       protection &= ~(1 << i);
+               else
+                       protection |= (1 << i);
        }
 
-       LOG_INFO("stm32x_protect, option_bytes written WRP1 0x%x , WRP2 0x%x",
-         (stm32x_info->option_bytes.protection & 0xff), (stm32x_info->option_bytes.protection2 & 0xff));
+       /* apply WRPSN mask */
+       protection &= 0xff;
 
-       retval = stm32x_write_options(bank);
-       if (retval != ERROR_OK)
-               return retval;
+       LOG_DEBUG("stm32x_protect, option_bytes written WPSN 0x%x", protection);
 
-       return ERROR_OK;
+       /* apply new option value */
+       return stm32x_write_option(bank, FLASH_WPSN_PRG, protection);
 }
 
 static int stm32x_write_block(struct flash_bank *bank, const uint8_t *buffer,
@@ -568,51 +496,8 @@ static int stm32x_write_block(struct flash_bank *bank, const uint8_t *buffer,
        struct stm32h7x_flash_bank *stm32x_info = bank->driver_priv;
        int retval = ERROR_OK;
 
-       /* see contrib/loaders/flash/smt32h7x.S for src */
        static const uint8_t stm32x_flash_write_code[] = {
-                                                               /* <code>: */
-               0x45, 0x68,                             /*              ldr             r5, [r0, #4] */
-                                                               /* <wait_fifo>: */
-               0x06, 0x68,                             /*              ldr             r6, [r0, #0] */
-               0x26, 0xb3,                             /*              cbz             r6, <exit> */
-               0x76, 0x1b,                             /*              subs    r6, r6, r5 */
-               0x42, 0xbf,                             /*              ittt    mi */
-               0x76, 0x18,                             /*              addmi   r6, r6, r1 */
-               0x36, 0x1a,                             /*              submi   r6, r6, r0 */
-               0x08, 0x3e,                             /*              submi   r6, #8 */
-               0x20, 0x2e,                             /*              cmp             r6, #32 */
-               0xf6, 0xd3,                             /*              bcc.n   <wait_fifo> */
-               0x4f, 0xf0, 0x32, 0x06, /*              mov.w   r6, #STM32_PROG */
-               0xe6, 0x60,                             /*              str             r6, [r4, #STM32_FLASH_CR_OFFSET] */
-               0x4f, 0xf0, 0x08, 0x07, /*              mov.w   r7, #8 */
-                                                               /* <write_flash>: */
-               0x55, 0xf8, 0x04, 0x6b, /*              ldr.w   r6, [r5], #4 */
-               0x42, 0xf8, 0x04, 0x6b, /*              str.w   r6, [r2], #4 */
-               0xbf, 0xf3, 0x4f, 0x8f, /*              dsb             sy */
-               0x8d, 0x42,                             /*              cmp             r5, r1 */
-               0x28, 0xbf,                             /*              it              cs */
-               0x00, 0xf1, 0x08, 0x05, /*              addcs.w r5, r0, #8 */
-               0x01, 0x3f,                             /*              subs    r7, #1 */
-               0xf3, 0xd1,                             /*              bne.n   <write_flash> */
-                                                               /* <busy>: */
-               0x26, 0x69,                             /*              ldr             r6, [r4, #STM32_FLASH_SR_OFFSET] */
-               0x16, 0xf0, 0x01, 0x0f, /*              tst.w   r6, #STM32_SR_BUSY_MASK */
-               0xfb, 0xd1,                             /*              bne.n   <busy> */
-               0x05, 0x4f,                             /*              ldr             r7, [pc, #20] ; (<stm32_sr_error_mask>) */
-               0x3e, 0x42,                             /*              tst             r6, r7 */
-               0x03, 0xd1,                             /*              bne.n   <error> */
-               0x45, 0x60,                             /*              str             r5, [r0, #4] */
-               0x01, 0x3b,                             /*              subs    r3, #1 */
-               0xdb, 0xd1,                             /*              bne.n   <wait_fifo> */
-               0x01, 0xe0,                             /*              b.n             <exit> */
-                                                               /* <error>: */
-               0x00, 0x27,                             /*              movs    r7, #0 */
-               0x47, 0x60,                             /*              str             r7, [r0, #4] */
-                                                               /* <exit>: */
-               0x30, 0x46,                             /*              mov             r0, r6 */
-               0x00, 0xbe,                             /*              bkpt    0x0000 */
-                                                               /* <stm32_sr_error_mask>: */
-               0x00, 0x00, 0xee, 0x03  /*              .word   0x03ee0000 ; (STM32_SR_ERROR_MASK) */
+#include "../../../contrib/loaders/flash/stm32/stm32h7x.inc"
        };
 
        if (target_alloc_working_area(target, sizeof(stm32x_flash_write_code),
@@ -624,8 +509,10 @@ static int stm32x_write_block(struct flash_bank *bank, const uint8_t *buffer,
        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) {
@@ -679,7 +566,7 @@ static int stm32x_write_block(struct flash_bank *bank, const uint8_t *buffer,
                if ((flash_sr & FLASH_ERROR) != 0) {
                        LOG_ERROR("flash write failed, FLASH_SR = %08" PRIx32, flash_sr);
                        /* Clear error + EOP flags but report errors */
-                       target_write_u32(target, stm32x_get_flash_reg(bank, FLASH_CCR), flash_sr);
+                       stm32x_write_flash_reg(bank, FLASH_CCR, flash_sr);
                        retval = ERROR_FAIL;
                }
        }
@@ -714,7 +601,7 @@ static int stm32x_write(struct flash_bank *bank, const uint8_t *buffer,
 
        retval = stm32x_unlock_reg(bank);
        if (retval != ERROR_OK)
-               return retval;
+               goto flash_lock;
 
        uint32_t blocks_remaining = count / FLASH_BLOCK_SIZE;
        uint32_t bytes_remaining = count % FLASH_BLOCK_SIZE;
@@ -740,14 +627,14 @@ static int stm32x_write(struct flash_bank *bank, const uint8_t *buffer,
        /*
        Standard programming
        The Flash memory programming sequence is as follows:
-       1. Check that no main Flash memory operation is ongoing by checking the BSY bit in the
+       1. Check that no main Flash memory operation is ongoing by checking the QW bit in the
           FLASH_SR register.
        2. Set the PG bit in the FLASH_CR register
        3. 8 x Word access (or Force Write FW)
-       4. Wait for the BSY bit to be cleared
+       4. Wait for flash operations completion
        */
        while (blocks_remaining > 0) {
-               retval = target_write_u32(target, stm32x_get_flash_reg(bank, FLASH_CR), FLASH_PG | FLASH_PSIZE_64);
+               retval = stm32x_write_flash_reg(bank, FLASH_CR, FLASH_PG | FLASH_PSIZE_64);
                if (retval != ERROR_OK)
                        goto flash_lock;
 
@@ -755,7 +642,7 @@ static int stm32x_write(struct flash_bank *bank, const uint8_t *buffer,
                if (retval != ERROR_OK)
                        goto flash_lock;
 
-               retval = stm32x_wait_status_busy(bank, FLASH_WRITE_TIMEOUT);
+               retval = stm32x_wait_flash_op_queue(bank, FLASH_WRITE_TIMEOUT);
                if (retval != ERROR_OK)
                        goto flash_lock;
 
@@ -765,7 +652,7 @@ static int stm32x_write(struct flash_bank *bank, const uint8_t *buffer,
        }
 
        if (bytes_remaining) {
-               retval = target_write_u32(target, stm32x_get_flash_reg(bank, FLASH_CR), FLASH_PG | FLASH_PSIZE_64);
+               retval = stm32x_write_flash_reg(bank, FLASH_CR, FLASH_PG | FLASH_PSIZE_64);
                if (retval != ERROR_OK)
                        goto flash_lock;
 
@@ -774,11 +661,11 @@ static int stm32x_write(struct flash_bank *bank, const uint8_t *buffer,
                        goto flash_lock;
 
                /* Force Write buffer of FLASH_BLOCK_SIZE = 32 bytes */
-               retval = target_write_u32(target, stm32x_get_flash_reg(bank, FLASH_CR), FLASH_PG | FLASH_PSIZE_64 | FLASH_FW);
+               retval = stm32x_write_flash_reg(bank, FLASH_CR, FLASH_PG | FLASH_PSIZE_64 | FLASH_FW);
                if (retval != ERROR_OK)
                        goto flash_lock;
 
-               retval = stm32x_wait_status_busy(bank, FLASH_WRITE_TIMEOUT);
+               retval = stm32x_wait_flash_op_queue(bank, FLASH_WRITE_TIMEOUT);
                if (retval != ERROR_OK)
                        goto flash_lock;
        }
@@ -788,10 +675,7 @@ flash_lock:
        if (retval2 != ERROR_OK)
                LOG_ERROR("error during the lock of flash");
 
-       if (retval == ERROR_OK)
-               retval = retval2;
-
-       return retval;
+       return (retval == ERROR_OK) ? retval2 : retval;
 }
 
 static void setup_sector(struct flash_bank *bank, int start, int num, int size)
@@ -872,8 +756,8 @@ static int stm32x_probe(struct flash_bank *bank)
                        /* This is the first bank */
                        flash_size_in_kb = stm32x_info->part_info->first_bank_size_kb;
                } else {
-                       LOG_WARNING("STM32H flash bank base address config is incorrect."
-                                   " 0x%" PRIx32 " but should rather be 0x%" PRIx32 " or 0x%" PRIx32,
+                       LOG_WARNING("STM32H flash bank base address config is incorrect. "
+                                   TARGET_ADDR_FMT " but should rather be 0x%" PRIx32 " or 0x%" PRIx32,
                                        bank->base, base_address, second_bank_base);
                        return ERROR_FAIL;
                }
@@ -975,57 +859,52 @@ static int stm32x_get_info(struct flash_bank *bank, char *buf, int buf_size)
        return ERROR_OK;
 }
 
-COMMAND_HANDLER(stm32x_handle_lock_command)
+static int stm32x_set_rdp(struct flash_bank *bank, enum stm32h7x_opt_rdp new_rdp)
 {
-       struct target *target = NULL;
-       struct stm32h7x_flash_bank *stm32x_info = NULL;
-
-       if (CMD_ARGC < 1)
-               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;
-
-       stm32x_info = bank->driver_priv;
-       target = bank->target;
-
-       /* if we have a dual flash bank device then
-        * we need to perform option byte lock on bank0 only */
-       if (stm32x_info->flash_base != FLASH_REG_BASE_B0) {
-               LOG_ERROR("Option Byte Lock Operation must use bank0");
-               return ERROR_FLASH_OPERATION_FAILED;
-       }
+       struct target *target = bank->target;
+       uint32_t optsr, cur_rdp;
+       int retval;
 
        if (target->state != TARGET_HALTED) {
                LOG_ERROR("Target not halted");
                return ERROR_TARGET_NOT_HALTED;
        }
 
-       if (stm32x_read_options(bank) != ERROR_OK) {
-               command_print(CMD_CTX, "%s failed to read options",
-                             bank->driver->name);
-               return ERROR_OK;
+       retval = stm32x_read_flash_reg(bank, FLASH_OPTSR_PRG, &optsr);
+
+       if (retval != ERROR_OK) {
+               LOG_DEBUG("unable to read FLASH_OPTSR_PRG register");
+               return retval;
        }
-       /* set readout protection */
-       stm32x_info->option_bytes.RDP = 0;
 
-       if (stm32x_write_options(bank) != ERROR_OK) {
-               command_print(CMD_CTX, "%s failed to lock device",
-                             bank->driver->name);
+       /* get current RDP, and check if there is a change */
+       cur_rdp = (optsr & OPT_RDP_MASK) >> OPT_RDP_POS;
+       if (new_rdp == cur_rdp) {
+               LOG_INFO("the requested RDP value is already programmed");
                return ERROR_OK;
        }
-       command_print(CMD_CTX, "%s locked", bank->driver->name);
 
-       return ERROR_OK;
+       switch (new_rdp) {
+       case OPT_RDP_L0:
+               LOG_WARNING("unlocking the entire flash device");
+               break;
+       case OPT_RDP_L1:
+               LOG_WARNING("locking the entire flash device");
+               break;
+       case OPT_RDP_L2:
+               LOG_WARNING("locking the entire flash device, irreversible");
+               break;
+       }
+
+       /* apply new RDP */
+       optsr = (optsr & ~OPT_RDP_MASK) | (new_rdp << OPT_RDP_POS);
+
+       /* apply new option value */
+       return stm32x_write_option(bank, FLASH_OPTSR_PRG, optsr);
 }
 
-COMMAND_HANDLER(stm32x_handle_unlock_command)
+COMMAND_HANDLER(stm32x_handle_lock_command)
 {
-       struct target *target = NULL;
-       struct stm32h7x_flash_bank *stm32x_info = NULL;
-
        if (CMD_ARGC < 1)
                return ERROR_COMMAND_SYNTAX_ERROR;
 
@@ -1034,42 +913,39 @@ COMMAND_HANDLER(stm32x_handle_unlock_command)
        if (ERROR_OK != retval)
                return retval;
 
-       stm32x_info = bank->driver_priv;
-       target = bank->target;
+       retval = stm32x_set_rdp(bank, OPT_RDP_L1);
 
-       /* if we have a dual flash bank device then
-        * we need to perform option byte unlock on bank0 only */
-       if (stm32x_info->flash_base != FLASH_REG_BASE_B0) {
-               LOG_ERROR("Option Byte Unlock Operation must use bank0");
-               return ERROR_FLASH_OPERATION_FAILED;
-       }
+       if (retval != ERROR_OK)
+               command_print(CMD, "%s failed to lock device", bank->driver->name);
+       else
+               command_print(CMD, "%s locked", bank->driver->name);
 
-       if (target->state != TARGET_HALTED) {
-               LOG_ERROR("Target not halted");
-               return ERROR_TARGET_NOT_HALTED;
-       }
+       return retval;
+}
 
-       if (stm32x_read_options(bank) != ERROR_OK) {
-               command_print(CMD_CTX, "%s failed to read options", bank->driver->name);
-               return ERROR_OK;
-       }
+COMMAND_HANDLER(stm32x_handle_unlock_command)
+{
+       if (CMD_ARGC < 1)
+               return ERROR_COMMAND_SYNTAX_ERROR;
 
-       /* clear readout protection option byte
-        * this will also force a device unlock if set */
-       stm32x_info->option_bytes.RDP = 0xAA;
+       struct flash_bank *bank;
+       int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
+       if (ERROR_OK != retval)
+               return retval;
 
-       if (stm32x_write_options(bank) != ERROR_OK) {
-               command_print(CMD_CTX, "%s failed to unlock device", bank->driver->name);
-               return ERROR_OK;
-       }
-       command_print(CMD_CTX, "%s unlocked.\n", bank->driver->name);
+       retval = stm32x_set_rdp(bank, OPT_RDP_L0);
 
-       return ERROR_OK;
+       if (retval != ERROR_OK)
+               command_print(CMD, "%s failed to unlock device", bank->driver->name);
+       else
+               command_print(CMD, "%s unlocked", bank->driver->name);
+
+       return retval;
 }
 
 static int stm32x_mass_erase(struct flash_bank *bank)
 {
-       int retval;
+       int retval, retval2;
        struct target *target = bank->target;
 
        if (target->state != TARGET_HALTED) {
@@ -1079,28 +955,27 @@ static int stm32x_mass_erase(struct flash_bank *bank)
 
        retval = stm32x_unlock_reg(bank);
        if (retval != ERROR_OK)
-               return retval;
+               goto flash_lock;
 
        /* mass erase flash memory bank */
-       retval = target_write_u32(target, stm32x_get_flash_reg(bank, FLASH_CR), FLASH_BER_CMD | FLASH_PSIZE_64);
+       retval = stm32x_write_flash_reg(bank, FLASH_CR, FLASH_BER | FLASH_PSIZE_64);
        if (retval != ERROR_OK)
-               return retval;
+               goto flash_lock;
 
-       retval = target_write_u32(target, stm32x_get_flash_reg(bank, FLASH_CR),
-                                                         FLASH_BER_CMD | FLASH_PSIZE_64 | FLASH_START);
+       retval = stm32x_write_flash_reg(bank, FLASH_CR, FLASH_BER | FLASH_PSIZE_64 | FLASH_START);
        if (retval != ERROR_OK)
-               return retval;
+               goto flash_lock;
 
-       retval = stm32x_wait_status_busy(bank, 30000);
+       retval = stm32x_wait_flash_op_queue(bank, 30000);
        if (retval != ERROR_OK)
-               return retval;
+               goto flash_lock;
 
-       retval = stm32x_lock_reg(bank);
-       if (retval != ERROR_OK) {
+flash_lock:
+       retval2 = stm32x_lock_reg(bank);
+       if (retval2 != ERROR_OK)
                LOG_ERROR("error during the lock of flash");
-               return retval;
-       }
-       return ERROR_OK;
+
+       return (retval == ERROR_OK) ? retval2 : retval;
 }
 
 COMMAND_HANDLER(stm32x_handle_mass_erase_command)
@@ -1108,7 +983,7 @@ COMMAND_HANDLER(stm32x_handle_mass_erase_command)
        int i;
 
        if (CMD_ARGC < 1) {
-               command_print(CMD_CTX, "stm32h7x mass_erase <bank>");
+               command_print(CMD, "stm32h7x mass_erase <bank>");
                return ERROR_COMMAND_SYNTAX_ERROR;
        }
 
@@ -1123,14 +998,61 @@ COMMAND_HANDLER(stm32x_handle_mass_erase_command)
                for (i = 0; i < bank->num_sectors; i++)
                        bank->sectors[i].is_erased = 1;
 
-               command_print(CMD_CTX, "stm32h7x mass erase complete");
+               command_print(CMD, "stm32h7x mass erase complete");
        } else {
-               command_print(CMD_CTX, "stm32h7x mass erase failed");
+               command_print(CMD, "stm32h7x mass erase failed");
        }
 
        return retval;
 }
 
+COMMAND_HANDLER(stm32x_handle_option_read_command)
+{
+       if (CMD_ARGC < 2) {
+               command_print(CMD, "stm32h7x option_read <bank> <option_reg offset>");
+               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;
+
+       uint32_t reg_offset, value;
+
+       COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], reg_offset);
+       retval = stm32x_read_flash_reg(bank, reg_offset, &value);
+       if (ERROR_OK != retval)
+               return retval;
+
+       command_print(CMD, "Option Register: <0x%" PRIx32 "> = 0x%" PRIx32 "",
+                       stm32x_get_flash_reg(bank, reg_offset), value);
+
+       return retval;
+}
+
+COMMAND_HANDLER(stm32x_handle_option_write_command)
+{
+       if (CMD_ARGC < 3) {
+               command_print(CMD, "stm32h7x option_write <bank> <option_reg offset> <value> [mask]");
+               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;
+
+       uint32_t reg_offset, value, mask = 0xffffffff;
+
+       COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], reg_offset);
+       COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], value);
+       if (CMD_ARGC > 3)
+               COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], mask);
+
+       return stm32x_modify_option(bank, reg_offset, value, mask);
+}
+
 static const struct command_registration stm32x_exec_command_handlers[] = {
        {
                .name = "lock",
@@ -1153,6 +1075,20 @@ static const struct command_registration stm32x_exec_command_handlers[] = {
                .usage = "bank_id",
                .help = "Erase entire flash device.",
        },
+       {
+               .name = "option_read",
+               .handler = stm32x_handle_option_read_command,
+               .mode = COMMAND_EXEC,
+               .usage = "bank_id reg_offset",
+               .help = "Read and display device option bytes.",
+       },
+       {
+               .name = "option_write",
+               .handler = stm32x_handle_option_write_command,
+               .mode = COMMAND_EXEC,
+               .usage = "bank_id reg_offset value [mask]",
+               .help = "Write device option bit fields with provided value.",
+       },
        COMMAND_REGISTRATION_DONE
 };
 
@@ -1167,7 +1103,7 @@ static const struct command_registration stm32x_command_handlers[] = {
        COMMAND_REGISTRATION_DONE
 };
 
-struct flash_driver stm32h7x_flash = {
+const struct flash_driver stm32h7x_flash = {
        .name = "stm32h7x",
        .commands = stm32x_command_handlers,
        .flash_bank_command = stm32x_flash_bank_command,
@@ -1180,4 +1116,5 @@ struct flash_driver stm32h7x_flash = {
        .erase_check = default_flash_blank_check,
        .protect_check = stm32x_protect_check,
        .info = stm32x_get_info,
+       .free_driver_priv = default_flash_free_driver_priv,
 };