flash/stm32l4x: introduce 'stm32l4x trustzone [enable|disable]' command
[fw/openocd] / src / flash / nor / stm32l4x.c
index 6816381619b7dc35432cdfcd527e40216ed73e85..6370d17becca58f0d19c26feaabe96ea01628a31 100644 (file)
@@ -24,6 +24,7 @@
 #endif
 
 #include "imp.h"
+#include <helper/align.h>
 #include <helper/binarybuffer.h>
 #include <target/algorithm.h>
 #include <target/armv7m.h>
 
 #define FLASH_ERASE_TIMEOUT 250
 
+
+/* relevant STM32L4 flags ****************************************************/
+#define F_NONE              0
+/* this flag indicates if the device flash is with dual bank architecture */
+#define F_HAS_DUAL_BANK     BIT(0)
+/* this flags is used for dual bank devices only, it indicates if the
+ * 4 WRPxx are usable if the device is configured in single-bank mode */
+#define F_USE_ALL_WRPXX     BIT(1)
+/* this flag indicates if the device embeds a TrustZone security feature */
+#define F_HAS_TZ            BIT(2)
+/* end of STM32L4 flags ******************************************************/
+
+
 enum stm32l4_flash_reg_index {
        STM32_FLASH_ACR_INDEX,
        STM32_FLASH_KEYR_INDEX,
@@ -130,6 +144,13 @@ enum stm32l4_flash_reg_index {
        STM32_FLASH_REG_INDEX_NUM,
 };
 
+enum stm32l4_rdp {
+       RDP_LEVEL_0   = 0xAA,
+       RDP_LEVEL_0_5 = 0x55, /* for devices with TrustZone enabled */
+       RDP_LEVEL_1   = 0x00,
+       RDP_LEVEL_2   = 0xCC
+};
+
 static const uint32_t stm32l4_flash_regs[STM32_FLASH_REG_INDEX_NUM] = {
        [STM32_FLASH_ACR_INDEX]      = 0x000,
        [STM32_FLASH_KEYR_INDEX]     = 0x008,
@@ -167,10 +188,7 @@ struct stm32l4_part_info {
        const struct stm32l4_rev *revs;
        const size_t num_revs;
        const uint16_t max_flash_size_kb;
-       const bool has_dual_bank;
-       /* this field is used for dual bank devices only, it indicates if the
-        * 4 WRPxx are usable if the device is configured in single-bank mode */
-       const bool use_all_wrpxx;
+       const uint32_t flags; /* one bit per feature, see STM32L4 flags: macros F_XXX */
        const uint32_t flash_regs_base;
        const uint32_t *default_flash_regs;
        const uint32_t fsize_addr;
@@ -189,6 +207,8 @@ struct stm32l4_flash_bank {
        const struct stm32l4_part_info *part_info;
        const uint32_t *flash_regs;
        bool otp_enabled;
+       enum stm32l4_rdp rdp;
+       bool tzen;
 };
 
 enum stm32_bank_id {
@@ -280,8 +300,7 @@ static const struct stm32l4_part_info stm32l4_parts[] = {
          .num_revs              = ARRAY_SIZE(stm32_415_revs),
          .device_str            = "STM32L47/L48xx",
          .max_flash_size_kb     = 1024,
-         .has_dual_bank         = true,
-         .use_all_wrpxx         = false,
+         .flags                 = F_HAS_DUAL_BANK,
          .flash_regs_base       = 0x40022000,
          .default_flash_regs    = stm32l4_flash_regs,
          .fsize_addr            = 0x1FFF75E0,
@@ -294,8 +313,7 @@ static const struct stm32l4_part_info stm32l4_parts[] = {
          .num_revs              = ARRAY_SIZE(stm32_435_revs),
          .device_str            = "STM32L43/L44xx",
          .max_flash_size_kb     = 256,
-         .has_dual_bank         = false,
-         .use_all_wrpxx         = false,
+         .flags                 = F_NONE,
          .flash_regs_base       = 0x40022000,
          .default_flash_regs    = stm32l4_flash_regs,
          .fsize_addr            = 0x1FFF75E0,
@@ -308,8 +326,7 @@ static const struct stm32l4_part_info stm32l4_parts[] = {
          .num_revs              = ARRAY_SIZE(stm32_460_revs),
          .device_str            = "STM32G07/G08xx",
          .max_flash_size_kb     = 128,
-         .has_dual_bank         = false,
-         .use_all_wrpxx         = false,
+         .flags                 = F_NONE,
          .flash_regs_base       = 0x40022000,
          .default_flash_regs    = stm32l4_flash_regs,
          .fsize_addr            = 0x1FFF75E0,
@@ -322,8 +339,7 @@ static const struct stm32l4_part_info stm32l4_parts[] = {
          .num_revs              = ARRAY_SIZE(stm32_461_revs),
          .device_str            = "STM32L49/L4Axx",
          .max_flash_size_kb     = 1024,
-         .has_dual_bank         = true,
-         .use_all_wrpxx         = false,
+         .flags                 = F_HAS_DUAL_BANK,
          .flash_regs_base       = 0x40022000,
          .default_flash_regs    = stm32l4_flash_regs,
          .fsize_addr            = 0x1FFF75E0,
@@ -336,8 +352,7 @@ static const struct stm32l4_part_info stm32l4_parts[] = {
          .num_revs              = ARRAY_SIZE(stm32_462_revs),
          .device_str            = "STM32L45/L46xx",
          .max_flash_size_kb     = 512,
-         .has_dual_bank         = false,
-         .use_all_wrpxx         = false,
+         .flags                 = F_NONE,
          .flash_regs_base       = 0x40022000,
          .default_flash_regs    = stm32l4_flash_regs,
          .fsize_addr            = 0x1FFF75E0,
@@ -350,8 +365,7 @@ static const struct stm32l4_part_info stm32l4_parts[] = {
          .num_revs              = ARRAY_SIZE(stm32_464_revs),
          .device_str            = "STM32L41/L42xx",
          .max_flash_size_kb     = 128,
-         .has_dual_bank         = false,
-         .use_all_wrpxx         = false,
+         .flags                 = F_NONE,
          .flash_regs_base       = 0x40022000,
          .default_flash_regs    = stm32l4_flash_regs,
          .fsize_addr            = 0x1FFF75E0,
@@ -364,8 +378,7 @@ static const struct stm32l4_part_info stm32l4_parts[] = {
          .num_revs              = ARRAY_SIZE(stm32_466_revs),
          .device_str            = "STM32G03/G04xx",
          .max_flash_size_kb     = 64,
-         .has_dual_bank         = false,
-         .use_all_wrpxx         = false,
+         .flags                 = F_NONE,
          .flash_regs_base       = 0x40022000,
          .default_flash_regs    = stm32l4_flash_regs,
          .fsize_addr            = 0x1FFF75E0,
@@ -378,8 +391,7 @@ static const struct stm32l4_part_info stm32l4_parts[] = {
          .num_revs              = ARRAY_SIZE(stm32_468_revs),
          .device_str            = "STM32G43/G44xx",
          .max_flash_size_kb     = 128,
-         .has_dual_bank         = false,
-         .use_all_wrpxx         = false,
+         .flags                 = F_NONE,
          .flash_regs_base       = 0x40022000,
          .default_flash_regs    = stm32l4_flash_regs,
          .fsize_addr            = 0x1FFF75E0,
@@ -392,8 +404,7 @@ static const struct stm32l4_part_info stm32l4_parts[] = {
          .num_revs              = ARRAY_SIZE(stm32_469_revs),
          .device_str            = "STM32G47/G48xx",
          .max_flash_size_kb     = 512,
-         .has_dual_bank         = true,
-         .use_all_wrpxx         = true,
+         .flags                 = F_HAS_DUAL_BANK | F_USE_ALL_WRPXX,
          .flash_regs_base       = 0x40022000,
          .default_flash_regs    = stm32l4_flash_regs,
          .fsize_addr            = 0x1FFF75E0,
@@ -406,8 +417,7 @@ static const struct stm32l4_part_info stm32l4_parts[] = {
          .num_revs              = ARRAY_SIZE(stm32_470_revs),
          .device_str            = "STM32L4R/L4Sxx",
          .max_flash_size_kb     = 2048,
-         .has_dual_bank         = true,
-         .use_all_wrpxx         = true,
+         .flags                 = F_HAS_DUAL_BANK | F_USE_ALL_WRPXX,
          .flash_regs_base       = 0x40022000,
          .default_flash_regs    = stm32l4_flash_regs,
          .fsize_addr            = 0x1FFF75E0,
@@ -420,8 +430,7 @@ static const struct stm32l4_part_info stm32l4_parts[] = {
          .num_revs              = ARRAY_SIZE(stm32_471_revs),
          .device_str            = "STM32L4P5/L4Q5x",
          .max_flash_size_kb     = 1024,
-         .has_dual_bank         = true,
-         .use_all_wrpxx         = true,
+         .flags                 = F_HAS_DUAL_BANK | F_USE_ALL_WRPXX,
          .flash_regs_base       = 0x40022000,
          .default_flash_regs    = stm32l4_flash_regs,
          .fsize_addr            = 0x1FFF75E0,
@@ -434,8 +443,7 @@ static const struct stm32l4_part_info stm32l4_parts[] = {
          .num_revs              = ARRAY_SIZE(stm32_472_revs),
          .device_str            = "STM32L55/L56xx",
          .max_flash_size_kb     = 512,
-         .has_dual_bank         = true,
-         .use_all_wrpxx         = true,
+         .flags                 = F_HAS_DUAL_BANK | F_USE_ALL_WRPXX | F_HAS_TZ,
          .flash_regs_base       = 0x40022000,
          .default_flash_regs    = stm32l5_ns_flash_regs,
          .fsize_addr            = 0x0BFA05E0,
@@ -448,8 +456,7 @@ static const struct stm32l4_part_info stm32l4_parts[] = {
          .num_revs              = ARRAY_SIZE(stm32_479_revs),
          .device_str            = "STM32G49/G4Axx",
          .max_flash_size_kb     = 512,
-         .has_dual_bank         = false,
-         .use_all_wrpxx         = false,
+         .flags                 = F_NONE,
          .flash_regs_base       = 0x40022000,
          .default_flash_regs    = stm32l4_flash_regs,
          .fsize_addr            = 0x1FFF75E0,
@@ -462,8 +469,7 @@ static const struct stm32l4_part_info stm32l4_parts[] = {
          .num_revs              = ARRAY_SIZE(stm32_495_revs),
          .device_str            = "STM32WB5x",
          .max_flash_size_kb     = 1024,
-         .has_dual_bank         = false,
-         .use_all_wrpxx         = false,
+         .flags                 = F_NONE,
          .flash_regs_base       = 0x58004000,
          .default_flash_regs    = stm32l4_flash_regs,
          .fsize_addr            = 0x1FFF75E0,
@@ -476,8 +482,7 @@ static const struct stm32l4_part_info stm32l4_parts[] = {
          .num_revs              = ARRAY_SIZE(stm32_496_revs),
          .device_str            = "STM32WB3x",
          .max_flash_size_kb     = 512,
-         .has_dual_bank         = false,
-         .use_all_wrpxx         = false,
+         .flags                 = F_NONE,
          .flash_regs_base       = 0x58004000,
          .default_flash_regs    = stm32l4_flash_regs,
          .fsize_addr            = 0x1FFF75E0,
@@ -490,8 +495,7 @@ static const struct stm32l4_part_info stm32l4_parts[] = {
          .num_revs              = ARRAY_SIZE(stm32_497_revs),
          .device_str            = "STM32WLEx",
          .max_flash_size_kb     = 256,
-         .has_dual_bank         = false,
-         .use_all_wrpxx         = false,
+         .flags                 = F_NONE,
          .flash_regs_base       = 0x58004000,
          .default_flash_regs    = stm32l4_flash_regs,
          .fsize_addr            = 0x1FFF75E0,
@@ -616,6 +620,35 @@ static inline bool stm32l4_otp_is_enabled(struct flash_bank *bank)
        return stm32l4_info->otp_enabled;
 }
 
+static void stm32l4_sync_rdp_tzen(struct flash_bank *bank, uint32_t optr_value)
+{
+       struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv;
+
+       bool tzen = false;
+
+       if (stm32l4_info->part_info->flags & F_HAS_TZ)
+               tzen = (optr_value & FLASH_TZEN) != 0;
+
+       uint32_t rdp = optr_value & FLASH_RDP_MASK;
+
+       /* for devices without TrustZone:
+        *   RDP level 0 and 2 values are to 0xAA and 0xCC
+        *   Any other value corresponds to RDP level 1
+        * for devices with TrusZone:
+        *   RDP level 0 and 2 values are 0xAA and 0xCC
+        *   RDP level 0.5 value is 0x55 only if TZEN = 1
+        *   Any other value corresponds to RDP level 1, including 0x55 if TZEN = 0
+        */
+
+       if (rdp != RDP_LEVEL_0 && rdp != RDP_LEVEL_2) {
+               if (!tzen || (tzen && rdp != RDP_LEVEL_0_5))
+                       rdp = RDP_LEVEL_1;
+       }
+
+       stm32l4_info->tzen = tzen;
+       stm32l4_info->rdp = rdp;
+}
+
 static inline uint32_t stm32l4_get_flash_reg(struct flash_bank *bank, uint32_t reg_offset)
 {
        struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv;
@@ -758,6 +791,42 @@ static int stm32l4_unlock_option_reg(struct flash_bank *bank)
        return ERROR_OK;
 }
 
+static int stm32l4_perform_obl_launch(struct flash_bank *bank)
+{
+       int retval, retval2;
+
+       retval = stm32l4_unlock_reg(bank);
+       if (retval != ERROR_OK)
+               goto err_lock;
+
+       retval = stm32l4_unlock_option_reg(bank);
+       if (retval != ERROR_OK)
+               goto err_lock;
+
+       /* Set OBL_LAUNCH bit in CR -> system reset and option bytes reload,
+        * but the RMs explicitly do *NOT* list this as power-on reset cause, and:
+        * "Note: If the read protection is set while the debugger is still
+        * connected through JTAG/SWD, apply a POR (power-on reset) instead of a system reset."
+        */
+
+       /* "Setting OBL_LAUNCH generates a reset so the option byte loading is performed under system reset" */
+       /* Due to this reset ST-Link reports an SWD_DP_ERROR, despite the write was successful,
+        * then just ignore the returned value */
+       stm32l4_write_flash_reg_by_index(bank, STM32_FLASH_CR_INDEX, FLASH_OBL_LAUNCH);
+
+       /* Need to re-probe after change */
+       struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv;
+       stm32l4_info->probed = false;
+
+err_lock:
+       retval2 = stm32l4_write_flash_reg_by_index(bank, STM32_FLASH_CR_INDEX, FLASH_LOCK | FLASH_OPTLOCK);
+
+       if (retval != ERROR_OK)
+               return retval;
+
+       return retval2;
+}
+
 static int stm32l4_write_option(struct flash_bank *bank, uint32_t reg_offset,
        uint32_t value, uint32_t mask)
 {
@@ -847,7 +916,7 @@ static int stm32l4_get_all_wrpxy(struct flash_bank *bank, enum stm32_bank_id dev
                        return ret;
 
                /* for some devices (like STM32L4R/S) in single-bank mode, the 4 WRPxx are usable */
-               if (stm32l4_info->part_info->use_all_wrpxx && !stm32l4_info->dual_bank_mode)
+               if ((stm32l4_info->part_info->flags & F_USE_ALL_WRPXX) && !stm32l4_info->dual_bank_mode)
                        wrp2y_sectors_offset = 0;
        }
 
@@ -969,8 +1038,6 @@ static int stm32l4_erase(struct flash_bank *bank, unsigned int first,
                retval = stm32l4_wait_status_busy(bank, FLASH_ERASE_TIMEOUT);
                if (retval != ERROR_OK)
                        break;
-
-               bank->sectors[i].is_erased = 1;
        }
 
 err_lock:
@@ -1288,10 +1355,10 @@ static int stm32l4_read_idcode(struct flash_bank *bank, uint32_t *id)
        int retval;
 
        /* try reading possible IDCODE registers, in the following order */
-       uint32_t DBGMCU_IDCODE[] = {DBGMCU_IDCODE_L4_G4, DBGMCU_IDCODE_G0, DBGMCU_IDCODE_L5};
+       uint32_t dbgmcu_idcode[] = {DBGMCU_IDCODE_L4_G4, DBGMCU_IDCODE_G0, DBGMCU_IDCODE_L5};
 
-       for (unsigned int i = 0; i < ARRAY_SIZE(DBGMCU_IDCODE); i++) {
-               retval = target_read_u32(bank->target, DBGMCU_IDCODE[i], id);
+       for (unsigned int i = 0; i < ARRAY_SIZE(dbgmcu_idcode); i++) {
+               retval = target_read_u32(bank->target, dbgmcu_idcode[i], id);
                if ((retval == ERROR_OK) && ((*id & 0xfff) != 0) && ((*id & 0xfff) != 0xfff))
                        return ERROR_OK;
        }
@@ -1300,13 +1367,35 @@ static int stm32l4_read_idcode(struct flash_bank *bank, uint32_t *id)
        return (retval == ERROR_OK) ? ERROR_FAIL : retval;
 }
 
+static const char *get_stm32l4_rev_str(struct flash_bank *bank)
+{
+       struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv;
+       const struct stm32l4_part_info *part_info = stm32l4_info->part_info;
+       assert(part_info);
+
+       const uint16_t rev_id = stm32l4_info->idcode >> 16;
+       for (unsigned int i = 0; i < part_info->num_revs; i++) {
+               if (rev_id == part_info->revs[i].rev)
+                       return part_info->revs[i].str;
+       }
+       return "'unknown'";
+}
+
+static const char *get_stm32l4_bank_type_str(struct flash_bank *bank)
+{
+       struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv;
+       assert(stm32l4_info->part_info);
+       return stm32l4_is_otp(bank) ? "OTP" :
+                       stm32l4_info->dual_bank_mode ? "Flash dual" :
+                       "Flash single";
+}
+
 static int stm32l4_probe(struct flash_bank *bank)
 {
        struct target *target = bank->target;
        struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv;
        const struct stm32l4_part_info *part_info;
        uint16_t flash_size_kb = 0xffff;
-       uint32_t device_id;
        uint32_t options;
 
        stm32l4_info->probed = false;
@@ -1316,11 +1405,13 @@ static int stm32l4_probe(struct flash_bank *bank)
        if (retval != ERROR_OK)
                return retval;
 
-       device_id = stm32l4_info->idcode & 0xFFF;
+       const uint32_t device_id = stm32l4_info->idcode & 0xFFF;
 
        for (unsigned int n = 0; n < ARRAY_SIZE(stm32l4_parts); n++) {
-               if (device_id == stm32l4_parts[n].id)
+               if (device_id == stm32l4_parts[n].id) {
                        stm32l4_info->part_info = &stm32l4_parts[n];
+                       break;
+               }
        }
 
        if (!stm32l4_info->part_info) {
@@ -1329,14 +1420,29 @@ static int stm32l4_probe(struct flash_bank *bank)
        }
 
        part_info = stm32l4_info->part_info;
+       const char *rev_str = get_stm32l4_rev_str(bank);
+       const uint16_t rev_id = stm32l4_info->idcode >> 16;
+
+       LOG_INFO("device idcode = 0x%08" PRIx32 " (%s - Rev %s : 0x%04x)",
+                       stm32l4_info->idcode, part_info->device_str, rev_str, rev_id);
+
        stm32l4_info->flash_regs = stm32l4_info->part_info->default_flash_regs;
 
-       char device_info[1024];
-       retval = bank->driver->info(bank, device_info, sizeof(device_info));
+       /* read flash option register */
+       retval = stm32l4_read_flash_reg_by_index(bank, STM32_FLASH_OPTR_INDEX, &options);
        if (retval != ERROR_OK)
                return retval;
 
-       LOG_INFO("device idcode = 0x%08" PRIx32 " (%s)", stm32l4_info->idcode, device_info);
+       stm32l4_sync_rdp_tzen(bank, options);
+
+       if (part_info->flags & F_HAS_TZ)
+               LOG_INFO("TZEN = %d : TrustZone %s by option bytes",
+                               stm32l4_info->tzen,
+                               stm32l4_info->tzen ? "enabled" : "disabled");
+
+       LOG_INFO("RDP level %s (0x%02X)",
+                       stm32l4_info->rdp == RDP_LEVEL_0 ? "0" : stm32l4_info->rdp == RDP_LEVEL_0_5 ? "0.5" : "1",
+                       stm32l4_info->rdp);
 
        if (stm32l4_is_otp(bank)) {
                bank->size = part_info->otp_size;
@@ -1353,7 +1459,6 @@ static int stm32l4_probe(struct flash_bank *bank)
                        return ERROR_FAIL;
                }
 
-
                stm32l4_info->probed = true;
                return ERROR_OK;
        } else if (bank->base != STM32_FLASH_BANK_BASE) {
@@ -1385,11 +1490,6 @@ static int stm32l4_probe(struct flash_bank *bank)
        /* did we assign a flash size? */
        assert((flash_size_kb != 0xffff) && flash_size_kb);
 
-       /* read flash option register */
-       retval = stm32l4_read_flash_reg_by_index(bank, STM32_FLASH_OPTR_INDEX, &options);
-       if (retval != ERROR_OK)
-               return retval;
-
        stm32l4_info->bank1_sectors = 0;
        stm32l4_info->hole_sectors = 0;
 
@@ -1525,7 +1625,7 @@ static int stm32l4_probe(struct flash_bank *bank)
         * max_flash_size is always power of two, so max_pages too
         */
        uint32_t max_pages = stm32l4_info->part_info->max_flash_size_kb / page_size_kb;
-       assert((max_pages & (max_pages - 1)) == 0);
+       assert(IS_PWR_OF_2(max_pages));
 
        /* in dual bank mode number of pages is doubled, but extra bit is bank selection */
        stm32l4_info->wrpxxr_mask = ((max_pages >> (stm32l4_info->dual_bank_mode ? 1 : 0)) - 1);
@@ -1537,7 +1637,7 @@ static int stm32l4_probe(struct flash_bank *bank)
        bank->size = (flash_size_kb + gap_size_kb) * 1024;
        bank->num_sectors = num_pages;
        bank->sectors = malloc(sizeof(struct flash_sector) * bank->num_sectors);
-       if (bank->sectors == NULL) {
+       if (!bank->sectors) {
                LOG_ERROR("failed to allocate bank sectors");
                return ERROR_FAIL;
        }
@@ -1566,33 +1666,19 @@ static int stm32l4_auto_probe(struct flash_bank *bank)
        return stm32l4_probe(bank);
 }
 
-static int get_stm32l4_info(struct flash_bank *bank, char *buf, int buf_size)
+static int get_stm32l4_info(struct flash_bank *bank, struct command_invocation *cmd)
 {
        struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv;
        const struct stm32l4_part_info *part_info = stm32l4_info->part_info;
 
        if (part_info) {
-               const char *rev_str = NULL;
-               uint16_t rev_id = stm32l4_info->idcode >> 16;
-               for (unsigned int i = 0; i < part_info->num_revs; i++) {
-                       if (rev_id == part_info->revs[i].rev) {
-                               rev_str = part_info->revs[i].str;
-                               break;
-                       }
-               }
-
-               int buf_len = snprintf(buf, buf_size, "%s - Rev %s : 0x%04x",
-                               part_info->device_str, rev_str ? rev_str : "'unknown'", rev_id);
-
+               const uint16_t rev_id = stm32l4_info->idcode >> 16;
+               command_print_sameline(cmd, "%s - Rev %s : 0x%04x", part_info->device_str,
+                               get_stm32l4_rev_str(bank), rev_id);
                if (stm32l4_info->probed)
-                       snprintf(buf + buf_len, buf_size - buf_len, " - %s-bank",
-                                       stm32l4_is_otp(bank) ? "OTP" :
-                                       stm32l4_info->dual_bank_mode ? "Flash dual" : "Flash single");
-
-               return ERROR_OK;
+                       command_print_sameline(cmd, " - %s-bank", get_stm32l4_bank_type_str(bank));
        } else {
-               snprintf(buf, buf_size, "Cannot identify target as an %s device", device_families);
-               return ERROR_FAIL;
+               command_print_sameline(cmd, "Cannot identify target as an %s device", device_families);
        }
 
        return ERROR_OK;
@@ -1611,7 +1697,7 @@ static int stm32l4_mass_erase(struct flash_bank *bank)
 
        uint32_t action = FLASH_MER1;
 
-       if (stm32l4_info->part_info->has_dual_bank)
+       if (stm32l4_info->part_info->flags & F_HAS_DUAL_BANK)
                action |= FLASH_MER2;
 
        if (target->state != TARGET_HALTED) {
@@ -1656,19 +1742,14 @@ COMMAND_HANDLER(stm32l4_handle_mass_erase_command)
 
        struct flash_bank *bank;
        int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
-       if (ERROR_OK != retval)
+       if (retval != ERROR_OK)
                return retval;
 
        retval = stm32l4_mass_erase(bank);
-       if (retval == ERROR_OK) {
-               /* set all sectors as erased */
-               for (unsigned int i = 0; i < bank->num_sectors; i++)
-                       bank->sectors[i].is_erased = 1;
-
+       if (retval == ERROR_OK)
                command_print(CMD, "stm32l4x mass erase complete");
-       } else {
+       else
                command_print(CMD, "stm32l4x mass erase failed");
-       }
 
        return retval;
 }
@@ -1682,17 +1763,17 @@ COMMAND_HANDLER(stm32l4_handle_option_read_command)
 
        struct flash_bank *bank;
        int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
-       if (ERROR_OK != retval)
+       if (retval != ERROR_OK)
                return retval;
 
        uint32_t reg_offset, reg_addr;
        uint32_t value = 0;
 
-       reg_offset = strtoul(CMD_ARGV[1], NULL, 16);
+       COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], reg_offset);
        reg_addr = stm32l4_get_flash_reg(bank, reg_offset);
 
        retval = stm32l4_read_flash_reg(bank, reg_offset, &value);
-       if (ERROR_OK != retval)
+       if (retval != ERROR_OK)
                return retval;
 
        command_print(CMD, "Option Register: <0x%" PRIx32 "> = 0x%" PRIx32 "", reg_addr, value);
@@ -1709,17 +1790,18 @@ COMMAND_HANDLER(stm32l4_handle_option_write_command)
 
        struct flash_bank *bank;
        int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
-       if (ERROR_OK != retval)
+       if (retval != ERROR_OK)
                return retval;
 
        uint32_t reg_offset;
        uint32_t value = 0;
        uint32_t mask = 0xFFFFFFFF;
 
-       reg_offset = strtoul(CMD_ARGV[1], NULL, 16);
-       value = strtoul(CMD_ARGV[2], NULL, 16);
+       COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], reg_offset);
+       COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], value);
+
        if (CMD_ARGC > 3)
-               mask = strtoul(CMD_ARGV[3], NULL, 16);
+               COMMAND_PARSE_NUMBER(u32, CMD_ARGV[3], mask);
 
        command_print(CMD, "%s Option written.\n"
                                "INFO: a reset or power cycle is required "
@@ -1729,38 +1811,88 @@ COMMAND_HANDLER(stm32l4_handle_option_write_command)
        return retval;
 }
 
-COMMAND_HANDLER(stm32l4_handle_option_load_command)
+COMMAND_HANDLER(stm32l4_handle_trustzone_command)
 {
-       if (CMD_ARGC != 1)
+       if (CMD_ARGC < 1 || CMD_ARGC > 2)
                return ERROR_COMMAND_SYNTAX_ERROR;
 
        struct flash_bank *bank;
        int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
-       if (ERROR_OK != retval)
+       if (retval != ERROR_OK)
                return retval;
 
-       retval = stm32l4_unlock_reg(bank);
-       if (ERROR_OK != retval)
+       struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv;
+       if (!(stm32l4_info->part_info->flags & F_HAS_TZ)) {
+               LOG_ERROR("This device does not have a TrustZone");
+               return ERROR_FAIL;
+       }
+
+       uint32_t optr;
+       retval = stm32l4_read_flash_reg_by_index(bank, STM32_FLASH_OPTR_INDEX, &optr);
+       if (retval != ERROR_OK)
                return retval;
 
-       retval = stm32l4_unlock_option_reg(bank);
-       if (ERROR_OK != retval)
+       stm32l4_sync_rdp_tzen(bank, optr);
+
+       if (CMD_ARGC == 1) {
+               /* only display the TZEN value */
+               LOG_INFO("Global TrustZone Security is %s", stm32l4_info->tzen ? "enabled" : "disabled");
+               return ERROR_OK;
+       }
+
+       bool new_tzen;
+       COMMAND_PARSE_ENABLE(CMD_ARGV[1], new_tzen);
+
+       if (new_tzen == stm32l4_info->tzen) {
+               LOG_INFO("The requested TZEN is already programmed");
+               return ERROR_OK;
+       }
+
+       if (new_tzen) {
+               if (stm32l4_info->rdp != RDP_LEVEL_0) {
+                       LOG_ERROR("TZEN can be set only when RDP level is 0");
+                       return ERROR_FAIL;
+               }
+               retval = stm32l4_write_option(bank, stm32l4_info->flash_regs[STM32_FLASH_OPTR_INDEX],
+                               FLASH_TZEN, FLASH_TZEN);
+       } else {
+               /* Deactivation of TZEN (from 1 to 0) is only possible when the RDP is
+                * changing to level 0 (from level 1 to level 0 or from level 0.5 to level 0). */
+               if (stm32l4_info->rdp != RDP_LEVEL_1 && stm32l4_info->rdp != RDP_LEVEL_0_5) {
+                       LOG_ERROR("Deactivation of TZEN is only possible when the RDP is changing to level 0");
+                       return ERROR_FAIL;
+               }
+
+               retval = stm32l4_write_option(bank, stm32l4_info->flash_regs[STM32_FLASH_OPTR_INDEX],
+                               RDP_LEVEL_0, FLASH_RDP_MASK | FLASH_TZEN);
+       }
+
+       if (retval != ERROR_OK)
                return retval;
 
-       /* Set OBL_LAUNCH bit in CR -> system reset and option bytes reload,
-        * but the RMs explicitly do *NOT* list this as power-on reset cause, and:
-        * "Note: If the read protection is set while the debugger is still
-        * connected through JTAG/SWD, apply a POR (power-on reset) instead of a system reset."
-        */
-       retval = stm32l4_write_flash_reg_by_index(bank, STM32_FLASH_CR_INDEX, FLASH_OBL_LAUNCH);
+       return stm32l4_perform_obl_launch(bank);
+}
 
-       command_print(CMD, "stm32l4x option load completed. Power-on reset might be required");
+COMMAND_HANDLER(stm32l4_handle_option_load_command)
+{
+       if (CMD_ARGC != 1)
+               return ERROR_COMMAND_SYNTAX_ERROR;
 
-       /* Need to re-probe after change */
-       struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv;
-       stm32l4_info->probed = false;
+       struct flash_bank *bank;
+       int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
+       if (retval != ERROR_OK)
+               return retval;
 
-       return retval;
+       retval = stm32l4_perform_obl_launch(bank);
+       if (retval != ERROR_OK) {
+               command_print(CMD, "stm32l4x option load failed");
+               return retval;
+       }
+
+
+       command_print(CMD, "stm32l4x option load completed. Power-on reset might be required");
+
+       return ERROR_OK;
 }
 
 COMMAND_HANDLER(stm32l4_handle_lock_command)
@@ -1772,7 +1904,7 @@ COMMAND_HANDLER(stm32l4_handle_lock_command)
 
        struct flash_bank *bank;
        int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
-       if (ERROR_OK != retval)
+       if (retval != ERROR_OK)
                return retval;
 
        if (stm32l4_is_otp(bank)) {
@@ -1789,7 +1921,8 @@ COMMAND_HANDLER(stm32l4_handle_lock_command)
 
        /* set readout protection level 1 by erasing the RDP option byte */
        struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv;
-       if (stm32l4_write_option(bank, stm32l4_info->flash_regs[STM32_FLASH_OPTR_INDEX], 0, 0x000000FF) != ERROR_OK) {
+       if (stm32l4_write_option(bank, stm32l4_info->flash_regs[STM32_FLASH_OPTR_INDEX],
+                       RDP_LEVEL_1, FLASH_RDP_MASK) != ERROR_OK) {
                command_print(CMD, "%s failed to lock device", bank->driver->name);
                return ERROR_OK;
        }
@@ -1806,7 +1939,7 @@ COMMAND_HANDLER(stm32l4_handle_unlock_command)
 
        struct flash_bank *bank;
        int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
-       if (ERROR_OK != retval)
+       if (retval != ERROR_OK)
                return retval;
 
        if (stm32l4_is_otp(bank)) {
@@ -1823,7 +1956,7 @@ COMMAND_HANDLER(stm32l4_handle_unlock_command)
 
        struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv;
        if (stm32l4_write_option(bank, stm32l4_info->flash_regs[STM32_FLASH_OPTR_INDEX],
-                       RDP_LEVEL_0, 0x000000FF) != ERROR_OK) {
+                       RDP_LEVEL_0, FLASH_RDP_MASK) != ERROR_OK) {
                command_print(CMD, "%s failed to unlock device", bank->driver->name);
                return ERROR_OK;
        }
@@ -1838,7 +1971,7 @@ COMMAND_HANDLER(stm32l4_handle_wrp_info_command)
 
        struct flash_bank *bank;
        int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
-       if (ERROR_OK != retval)
+       if (retval != ERROR_OK)
                return retval;
 
        if (stm32l4_is_otp(bank)) {
@@ -1858,7 +1991,7 @@ COMMAND_HANDLER(stm32l4_handle_wrp_info_command)
        }
 
        if (dev_bank_id == STM32_BANK2) {
-               if (!stm32l4_info->part_info->has_dual_bank) {
+               if (!(stm32l4_info->part_info->flags & F_HAS_DUAL_BANK)) {
                        LOG_ERROR("this device has no second bank");
                        return ERROR_FAIL;
                } else if (!stm32l4_info->dual_bank_mode) {
@@ -1910,7 +2043,7 @@ COMMAND_HANDLER(stm32l4_handle_otp_command)
 
        struct flash_bank *bank;
        int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
-       if (ERROR_OK != retval)
+       if (retval != ERROR_OK)
                return retval;
 
        if (!stm32l4_is_otp(bank)) {
@@ -1966,6 +2099,13 @@ static const struct command_registration stm32l4_exec_command_handlers[] = {
                .usage = "bank_id reg_offset value mask",
                .help = "Write device option bit fields with provided value.",
        },
+       {
+               .name = "trustzone",
+               .handler = stm32l4_handle_trustzone_command,
+               .mode = COMMAND_EXEC,
+               .usage = "<bank_id> [enable|disable]",
+               .help = "Configure TrustZone security",
+       },
        {
                .name = "wrp_info",
                .handler = stm32l4_handle_wrp_info_command,