summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVincent Palatin <vpalatin@chromium.org>2017-03-21 15:29:11 +0100
committerchrome-bot <chrome-bot@chromium.org>2017-03-24 19:38:04 -0700
commitd8cbf0dc402619cc7e027534c196abd9059ab98a (patch)
tree1b3b29de134a0cb6970824a7664b4d385f480af8
parentd9afaba9b4043a9d953b0d4c4e846520b04a0597 (diff)
downloadchrome-ec-d8cbf0dc402619cc7e027534c196abd9059ab98a.tar.gz
stm32: add internal flash support for STM32L4 family
Add a flash driver for the STM32L4 family. For write and erase, the code is very similar to other variants excepted the 'normal' writes need to be perform 2 aligned 32-bit words at a time. Option bytes are a sligthly easier business since the hardware deals with the option bytes page preserving and erasing for us. For the write-protection, the STM32L4 is slightly different from the other variants. The write-protection granularity is still a 2-kB block (2kB here) but instead of having a 'bitmap' of the protected blocks, it defines 2 write-protection ranges (WRP1AR and WRP1BR). For the EC code base, we are using WRP1AR to protect the Read-Only regions and WRP1BR to protect the Rollback and RW regions (if they exist). Signed-off-by: Vincent Palatin <vpalatin@chromium.org> BRANCH=none BUG=b:35648258 TEST=On Eve, run 'flashrom -p ec:type=fp -w /tmp/ec.bin' and 'flashrom -p ec:type=fp --wp-enable --wp-range 0x0 0x20000' Change-Id: Iaa98c1b4d3b07de2923ac076624bd4601c31a600 Reviewed-on: https://chromium-review.googlesource.com/456711 Commit-Ready: Vincent Palatin <vpalatin@chromium.org> Tested-by: Vincent Palatin <vpalatin@chromium.org> Reviewed-by: Randall Spangler <rspangler@chromium.org> Reviewed-by: Nicolas Boichat <drinkcat@chromium.org>
-rw-r--r--chip/stm32/flash-stm32l4.c531
-rw-r--r--chip/stm32/registers.h23
-rw-r--r--chip/stm32/system.c6
3 files changed, 554 insertions, 6 deletions
diff --git a/chip/stm32/flash-stm32l4.c b/chip/stm32/flash-stm32l4.c
new file mode 100644
index 0000000000..821d277b7a
--- /dev/null
+++ b/chip/stm32/flash-stm32l4.c
@@ -0,0 +1,531 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+/* Flash memory module for STM32L4 family */
+
+#include "common.h"
+#include "clock.h"
+#include "flash.h"
+#include "hooks.h"
+#include "registers.h"
+#include "panic.h"
+#include "system.h"
+#include "task.h"
+#include "timer.h"
+#include "util.h"
+#include "watchdog.h"
+
+/*
+ * Approximate number of CPU cycles per iteration of the loop when polling
+ * the flash status
+ */
+#define CYCLE_PER_FLASH_LOOP 10
+
+/* Flash page programming timeout. This is 2x the datasheet max. */
+#define FLASH_TIMEOUT_US 48000
+
+static inline int calculate_flash_timeout(void)
+{
+ return (FLASH_TIMEOUT_US *
+ (clock_get_freq() / SECOND) / CYCLE_PER_FLASH_LOOP);
+}
+
+static int wait_while_busy(void)
+{
+ int timeout = calculate_flash_timeout();
+
+ while (STM32_FLASH_SR & FLASH_SR_BUSY && timeout-- > 0)
+ ;
+ return (timeout > 0) ? EC_SUCCESS : EC_ERROR_TIMEOUT;
+}
+
+static int unlock(int locks)
+{
+ /*
+ * We may have already locked the flash module and get a bus fault
+ * in the attempt to unlock. Need to disable bus fault handler now.
+ */
+ ignore_bus_fault(1);
+
+ /* unlock CR if needed */
+ if (STM32_FLASH_CR & FLASH_CR_LOCK) {
+ STM32_FLASH_KEYR = FLASH_KEYR_KEY1;
+ STM32_FLASH_KEYR = FLASH_KEYR_KEY2;
+ }
+ /* unlock option memory if required */
+ if ((locks & FLASH_CR_OPTLOCK) &&
+ (STM32_FLASH_CR & FLASH_CR_OPTLOCK)) {
+ STM32_FLASH_OPTKEYR = FLASH_OPTKEYR_KEY1;
+ STM32_FLASH_OPTKEYR = FLASH_OPTKEYR_KEY2;
+ }
+
+ /* Re-enable bus fault handler */
+ ignore_bus_fault(0);
+
+ return (STM32_FLASH_CR & (locks | FLASH_CR_LOCK)) ? EC_ERROR_UNKNOWN
+ : EC_SUCCESS;
+}
+
+static void lock(void)
+{
+ STM32_FLASH_CR = FLASH_CR_LOCK;
+}
+
+/*
+ * Option byte organization
+ *
+ * [63:56][55:48][47:40][39:32] [31:24][23:16][15: 8][ 7: 0]
+ * +--------------+-------------------+------+ +-------------------+------+
+ * | 0x1FFF7800 | nUSER | nRDP | | USER | RDP |
+ * +--------------+------------+------+------+ +------------+------+------+
+ * | 0x1FFF7808 | | nPCROP1_STRT| | | PCROP1_STRT |
+ * +--------------+------------+-------------+ +------------+-------------+
+ * | 0x1FFF7810 | | nPCROP1_END | | | PCROP1_END |
+ * +--------------+------------+-------------+ +------------+-------------+
+ * | 0x1FFF7818 | |nWRP1A| |nWRP1A| | | WRP1A| | WRP1A|
+ * | | |_END | |_STRT | | | _END | | _STRT|
+ * +--------------+------------+-------------+ +------------+-------------+
+ * | 0x1FFF7820 | |nWRP1B| |nWRP1B| | | WRP1B| | WRP1B|
+ * | | |_END | |_STRT | | | _END | | _STRT|
+ * +--------------+------------+-------------+ +------------+-------------+
+ *
+ * Note that the variable with n prefix means the complement.
+ */
+static int unlock_optb(void)
+{
+ int rv;
+
+ rv = wait_while_busy();
+ if (rv)
+ return rv;
+
+ rv = unlock(FLASH_CR_OPTLOCK);
+ if (rv)
+ return rv;
+
+ return EC_SUCCESS;
+}
+
+static int commit_optb(void)
+{
+ int rv;
+
+ STM32_FLASH_CR |= FLASH_CR_OPTSTRT;
+
+ rv = wait_while_busy();
+ if (rv)
+ return rv;
+ lock();
+
+ return EC_SUCCESS;
+}
+
+static void unprotect_all_blocks(void)
+{
+ unlock_optb();
+ STM32_FLASH_WRP1AR = FLASH_WRP_RANGE_DISABLED;
+ STM32_FLASH_WRP1BR = FLASH_WRP_RANGE_DISABLED;
+ commit_optb();
+}
+
+int flash_physical_protect_at_boot(uint32_t new_flags)
+{
+ uint32_t ro_range = FLASH_WRP_RANGE_DISABLED;
+ uint32_t rb_rw_range = FLASH_WRP_RANGE_DISABLED;
+ /*
+ * WRP1AR is storing the write-protection range for the RO region.
+ * WRP1BR is storing the write-protection range for the
+ * rollback and RW regions.
+ */
+ if (new_flags & (EC_FLASH_PROTECT_ALL_AT_BOOT |
+ EC_FLASH_PROTECT_RO_AT_BOOT))
+ ro_range = FLASH_WRP_RANGE(WP_BANK_OFFSET,
+ WP_BANK_OFFSET + WP_BANK_COUNT);
+
+ if (new_flags & EC_FLASH_PROTECT_ALL_AT_BOOT) {
+ rb_rw_range = FLASH_WRP_RANGE(WP_BANK_OFFSET + WP_BANK_COUNT,
+ PHYSICAL_BANKS);
+ } else {
+ uint8_t strt = WP_BANK_OFFSET + WP_BANK_COUNT;
+ uint8_t end = FLASH_WRP_END(FLASH_WRP_RANGE_DISABLED);
+#ifdef CONFIG_ROLLBACK
+ if (new_flags & EC_FLASH_PROTECT_ROLLBACK_AT_BOOT) {
+ strt = ROLLBACK_BANK_OFFSET;
+ end = ROLLBACK_BANK_OFFSET + ROLLBACK_BANK_COUNT;
+ } else {
+ strt = ROLLBACK_BANK_OFFSET + ROLLBACK_BANK_COUNT;
+ }
+#endif /* !CONFIG_ROLLBACK */
+#ifdef CONFIG_FLASH_PROTECT_RW
+ if (new_flags & EC_FLASH_PROTECT_RW_AT_BOOT)
+ end = PHYSICAL_BANKS;
+#endif /* CONFIG_FLASH_PROTECT_RW */
+
+ if (end != FLASH_WRP_END(FLASH_WRP_RANGE_DISABLED))
+ rb_rw_range = FLASH_WRP_RANGE(strt, end);
+ }
+
+ unlock_optb();
+#ifdef CONFIG_WP_ALWAYS
+ /*
+ * Set a permanent protection by increasing RDP to level 1,
+ * trying to unprotected the flash will trigger a full erase.
+ */
+ STM32_FLASH_OPTR = (STM32_FLASH_OPTR & ~0xff) | 0x11;
+#endif
+ STM32_FLASH_WRP1AR = ro_range;
+ STM32_FLASH_WRP1BR = rb_rw_range;
+ commit_optb();
+
+ return EC_SUCCESS;
+}
+
+/**
+ * Check if write protect register state is inconsistent with RO_AT_BOOT and
+ * ALL_AT_BOOT state.
+ *
+ * @return zero if consistent, non-zero if inconsistent.
+ */
+static int registers_need_reset(void)
+{
+ uint32_t flags = flash_get_protect();
+ int ro_at_boot = (flags & EC_FLASH_PROTECT_RO_AT_BOOT) ? 1 : 0;
+ /*
+ * The RO region is write-protected by the WRP1AR range,
+ * it starts at page WP_BANK_OFFSET for WP_BANK_COUNT pages.
+ */
+ uint32_t wrp1ar = STM32_OPTB_WRP1AR;
+ uint32_t ro_range = ro_at_boot ?
+ FLASH_WRP_RANGE(WP_BANK_OFFSET, WP_BANK_OFFSET + WP_BANK_COUNT)
+ : FLASH_WRP_RANGE_DISABLED;
+
+ return ro_range != (wrp1ar & FLASH_WRP_MASK);
+}
+
+/*****************************************************************************/
+/* Physical layer APIs */
+
+int flash_physical_write(int offset, int size, const char *data)
+{
+ uint32_t *address = (void *)(CONFIG_PROGRAM_MEMORY_BASE + offset);
+ int res = EC_SUCCESS;
+ int timeout = calculate_flash_timeout();
+ int i;
+ int unaligned = (uint32_t)data & (CONFIG_FLASH_WRITE_SIZE - 1);
+ uint32_t *data32 = (void *)data;
+
+ if (unlock(FLASH_CR_LOCK) != EC_SUCCESS)
+ return EC_ERROR_UNKNOWN;
+
+ /* Clear previous error status */
+ STM32_FLASH_SR = FLASH_SR_ERR_MASK;
+
+ /* set PG bit */
+ STM32_FLASH_CR |= FLASH_CR_PG;
+
+ for (; size > 0; size -= CONFIG_FLASH_WRITE_SIZE) {
+ /*
+ * Reload the watchdog timer to avoid watchdog reset when doing
+ * long writing.
+ */
+ watchdog_reload();
+
+ /* wait to be ready */
+ for (i = 0; (STM32_FLASH_SR & FLASH_SR_BUSY) && (i < timeout);
+ i++)
+ ;
+ if (STM32_FLASH_SR & FLASH_SR_BUSY) {
+ res = EC_ERROR_TIMEOUT;
+ goto exit_wr;
+ }
+
+ /* write the 2 words */
+ if (unaligned) {
+ *address++ = (uint32_t)data[0] | (data[1] << 8)
+ | (data[2] << 16) | (data[3] << 24);
+ *address++ = (uint32_t)data[4] | (data[5] << 8)
+ | (data[6] << 16) | (data[7] << 24);
+ data += CONFIG_FLASH_WRITE_SIZE;
+ } else {
+ *address++ = *data32++;
+ *address++ = *data32++;
+ }
+
+ /* Wait for writes to complete */
+ for (i = 0; (STM32_FLASH_SR & FLASH_SR_BUSY) && (i < timeout);
+ i++)
+ ;
+
+ if (STM32_FLASH_SR & FLASH_SR_BUSY) {
+ res = EC_ERROR_TIMEOUT;
+ goto exit_wr;
+ }
+
+ /*
+ * Check for error conditions - erase failed, voltage error,
+ * protection error.
+ */
+ if (STM32_FLASH_SR & FLASH_SR_ERR_MASK) {
+ res = EC_ERROR_UNKNOWN;
+ goto exit_wr;
+ }
+ }
+
+exit_wr:
+ /* Disable PG bit */
+ STM32_FLASH_CR &= ~FLASH_CR_PG;
+
+ lock();
+
+ return res;
+}
+
+int flash_physical_erase(int offset, int size)
+{
+ int res = EC_SUCCESS;
+ int pg;
+ int last;
+
+ if (unlock(FLASH_CR_LOCK) != EC_SUCCESS)
+ return EC_ERROR_UNKNOWN;
+
+ /* Clear previous error status */
+ STM32_FLASH_SR = FLASH_SR_ERR_MASK;
+
+ last = (offset + size) / CONFIG_FLASH_ERASE_SIZE;
+ for (pg = offset / CONFIG_FLASH_ERASE_SIZE; pg < last; pg++) {
+ timestamp_t deadline;
+
+ /* select page to erase and PER bit */
+ STM32_FLASH_CR = (STM32_FLASH_CR & ~FLASH_CR_PNB_MASK)
+ | FLASH_CR_PER | FLASH_CR_PNB(pg);
+
+ /* set STRT bit : start erase */
+ STM32_FLASH_CR |= FLASH_CR_STRT;
+
+ /*
+ * Reload the watchdog timer to avoid watchdog reset during a
+ * long erase operation.
+ */
+ watchdog_reload();
+
+ deadline.val = get_time().val + FLASH_TIMEOUT_US;
+ /* Wait for erase to complete */
+ while ((STM32_FLASH_SR & FLASH_SR_BUSY) &&
+ (get_time().val < deadline.val)) {
+ usleep(300);
+ }
+ if (STM32_FLASH_SR & FLASH_SR_BUSY) {
+ res = EC_ERROR_TIMEOUT;
+ goto exit_er;
+ }
+
+ /*
+ * Check for error conditions - erase failed, voltage error,
+ * protection error
+ */
+ if (STM32_FLASH_SR & FLASH_SR_ERR_MASK) {
+ res = EC_ERROR_UNKNOWN;
+ goto exit_er;
+ }
+ }
+
+exit_er:
+ /* reset PER bit */
+ STM32_FLASH_CR &= ~(FLASH_CR_PER | FLASH_CR_PNB_MASK);
+
+ lock();
+
+ return res;
+}
+
+int flash_physical_get_protect(int block)
+{
+ uint32_t wrp1ar = STM32_FLASH_WRP1AR;
+ uint32_t wrp1br = STM32_FLASH_WRP1BR;
+
+ return ((block >= FLASH_WRP_START(wrp1ar)) &&
+ (block < FLASH_WRP_END(wrp1ar))) ||
+ ((block >= FLASH_WRP_START(wrp1br)) &&
+ (block < FLASH_WRP_END(wrp1br)));
+}
+
+/*
+ * Note: This does not need to update _NOW flags, as get_protect_flags
+ * in common code already does so.
+ */
+uint32_t flash_physical_get_protect_flags(void)
+{
+ uint32_t flags = 0;
+ uint32_t wrp1ar = STM32_OPTB_WRP1AR;
+ uint32_t wrp1br = STM32_OPTB_WRP1BR;
+
+ /* RO region protection range is in WRP1AR range */
+ if (wrp1ar == FLASH_WRP_RANGE(WP_BANK_OFFSET,
+ WP_BANK_OFFSET + WP_BANK_COUNT))
+ flags |= EC_FLASH_PROTECT_RO_AT_BOOT;
+ /* Rollback and RW regions protection range is in WRP1BR range */
+ if (wrp1br != FLASH_WRP_RANGE_DISABLED) {
+ int end = FLASH_WRP_END(wrp1br);
+ int strt = FLASH_WRP_START(wrp1br);
+
+#ifdef CONFIG_ROLLBACK
+ if (strt <= ROLLBACK_BANK_OFFSET &&
+ end >= ROLLBACK_BANK_OFFSET + ROLLBACK_BANK_COUNT)
+ flags |= EC_FLASH_PROTECT_ROLLBACK_AT_BOOT;
+#endif /* CONFIG_ROLLBACK */
+#ifdef CONFIG_FLASH_PROTECT_RW
+ if (end == PHYSICAL_BANKS)
+ flags |= EC_FLASH_PROTECT_RW_AT_BOOT;
+#endif /* CONFIG_FLASH_PROTECT_RW */
+ if (end == PHYSICAL_BANKS &&
+ strt == WP_BANK_OFFSET + WP_BANK_COUNT &&
+ flags & EC_FLASH_PROTECT_RO_AT_BOOT)
+ flags |= EC_FLASH_PROTECT_ALL_AT_BOOT;
+ }
+
+ return flags;
+}
+
+int flash_physical_protect_now(int all)
+{
+ return EC_ERROR_INVAL;
+}
+
+uint32_t flash_physical_get_valid_flags(void)
+{
+ return EC_FLASH_PROTECT_RO_AT_BOOT |
+ EC_FLASH_PROTECT_RO_NOW |
+#ifdef CONFIG_FLASH_PROTECT_RW
+ EC_FLASH_PROTECT_RW_AT_BOOT |
+ EC_FLASH_PROTECT_RW_NOW |
+#endif
+#ifdef CONFIG_ROLLBACK
+ EC_FLASH_PROTECT_ROLLBACK_AT_BOOT |
+ EC_FLASH_PROTECT_ROLLBACK_NOW |
+#endif
+ EC_FLASH_PROTECT_ALL_AT_BOOT |
+ EC_FLASH_PROTECT_ALL_NOW;
+}
+
+uint32_t flash_physical_get_writable_flags(uint32_t cur_flags)
+{
+ uint32_t ret = 0;
+
+ /* If RO protection isn't enabled, its at-boot state can be changed. */
+ if (!(cur_flags & EC_FLASH_PROTECT_RO_NOW))
+ ret |= EC_FLASH_PROTECT_RO_AT_BOOT;
+
+ /*
+ * ALL/RW at-boot state can be set if WP GPIO is asserted and can always
+ * be cleared.
+ */
+ if (cur_flags & (EC_FLASH_PROTECT_ALL_AT_BOOT |
+ EC_FLASH_PROTECT_GPIO_ASSERTED))
+ ret |= EC_FLASH_PROTECT_ALL_AT_BOOT;
+
+#ifdef CONFIG_FLASH_PROTECT_RW
+ if (cur_flags & (EC_FLASH_PROTECT_RW_AT_BOOT |
+ EC_FLASH_PROTECT_GPIO_ASSERTED))
+ ret |= EC_FLASH_PROTECT_RW_AT_BOOT;
+#endif
+
+#ifdef CONFIG_ROLLBACK
+ if (cur_flags & (EC_FLASH_PROTECT_ROLLBACK_AT_BOOT |
+ EC_FLASH_PROTECT_GPIO_ASSERTED))
+ ret |= EC_FLASH_PROTECT_ROLLBACK_AT_BOOT;
+#endif
+
+ return ret;
+}
+
+int flash_pre_init(void)
+{
+ uint32_t reset_flags = system_get_reset_flags();
+ uint32_t prot_flags = flash_get_protect();
+ int need_reset = 0;
+
+ /*
+ * If we have already jumped between images, an earlier image could
+ * have applied write protection. Nothing additional needs to be done.
+ */
+ if (reset_flags & RESET_FLAG_SYSJUMP)
+ return EC_SUCCESS;
+
+ if (prot_flags & EC_FLASH_PROTECT_GPIO_ASSERTED) {
+ if ((prot_flags & EC_FLASH_PROTECT_RO_AT_BOOT) &&
+ !(prot_flags & EC_FLASH_PROTECT_RO_NOW)) {
+ /*
+ * Pstate wants RO protected at boot, but the write
+ * protect register wasn't set to protect it. Force an
+ * update to the write protect register and reboot so
+ * it takes effect.
+ */
+ flash_physical_protect_at_boot(
+ EC_FLASH_PROTECT_RO_AT_BOOT);
+ need_reset = 1;
+ }
+
+ if (registers_need_reset()) {
+ /*
+ * Write protect register was in an inconsistent state.
+ * Set it back to a good state and reboot.
+ *
+ * TODO(crosbug.com/p/23798): this seems really similar
+ * to the check above. One of them should be able to
+ * go away.
+ */
+ flash_protect_at_boot(
+ prot_flags & EC_FLASH_PROTECT_RO_AT_BOOT);
+ need_reset = 1;
+ }
+ } else {
+ if (prot_flags & EC_FLASH_PROTECT_RO_NOW) {
+ /*
+ * Write protect pin unasserted but some section is
+ * protected. Drop it and reboot.
+ */
+ unprotect_all_blocks();
+ need_reset = 1;
+ }
+ }
+
+ if ((flash_physical_get_valid_flags() & EC_FLASH_PROTECT_ALL_AT_BOOT) &&
+ (!!(prot_flags & EC_FLASH_PROTECT_ALL_AT_BOOT) !=
+ !!(prot_flags & EC_FLASH_PROTECT_ALL_NOW))) {
+ /*
+ * ALL_AT_BOOT and ALL_NOW should be both set or both unset
+ * at boot. If they are not, it must be that the chip requires
+ * OBL_LAUNCH to be set to reload option bytes. Let's reset
+ * the system with OBL_LAUNCH set.
+ * This assumes OBL_LAUNCH is used for hard reset in
+ * chip/stm32/system.c.
+ */
+ need_reset = 1;
+ }
+
+#ifdef CONFIG_FLASH_PROTECT_RW
+ if ((flash_physical_get_valid_flags() & EC_FLASH_PROTECT_RW_AT_BOOT) &&
+ (!!(prot_flags & EC_FLASH_PROTECT_RW_AT_BOOT) !=
+ !!(prot_flags & EC_FLASH_PROTECT_RW_NOW))) {
+ /* RW_AT_BOOT and RW_NOW do not match. */
+ need_reset = 1;
+ }
+#endif
+
+#ifdef CONFIG_ROLLBACK
+ if ((flash_physical_get_valid_flags() &
+ EC_FLASH_PROTECT_ROLLBACK_AT_BOOT) &&
+ (!!(prot_flags & EC_FLASH_PROTECT_ROLLBACK_AT_BOOT) !=
+ !!(prot_flags & EC_FLASH_PROTECT_ROLLBACK_NOW))) {
+ /* ROLLBACK_AT_BOOT and ROLLBACK_NOW do not match. */
+ need_reset = 1;
+ }
+#endif
+
+ if (need_reset)
+ system_reset(SYSTEM_RESET_HARD | SYSTEM_RESET_PRESERVE_FLAGS);
+
+ return EC_SUCCESS;
+}
diff --git a/chip/stm32/registers.h b/chip/stm32/registers.h
index a8104114cf..f0bd1cf861 100644
--- a/chip/stm32/registers.h
+++ b/chip/stm32/registers.h
@@ -1323,30 +1323,41 @@ typedef volatile struct stm32_spi_regs stm32_spi_regs_t;
#define STM32_FLASH_ACR_DCEN (1 << 10)
#define STM32_FLASH_PDKEYR REG32(STM32_FLASH_REGS_BASE + 0x04)
#define STM32_FLASH_KEYR REG32(STM32_FLASH_REGS_BASE + 0x08)
+#define FLASH_KEYR_KEY1 0x45670123
+#define FLASH_KEYR_KEY2 0xCDEF89AB
#define STM32_FLASH_OPTKEYR REG32(STM32_FLASH_REGS_BASE + 0x0c)
+#define FLASH_OPTKEYR_KEY1 0x08192A3B
+#define FLASH_OPTKEYR_KEY2 0x4C5D6E7F
#define STM32_FLASH_SR REG32(STM32_FLASH_REGS_BASE + 0x10)
#define FLASH_SR_BUSY (1 << 16)
-#define FLASH_SR_ERR_MASK (0xc3fb)
+#define FLASH_SR_ERR_MASK (0xc3fa)
#define STM32_FLASH_CR REG32(STM32_FLASH_REGS_BASE + 0x14)
#define FLASH_CR_PG (1 << 0)
#define FLASH_CR_PER (1 << 1)
#define FLASH_CR_STRT (1 << 16)
+#define FLASH_CR_OPTSTRT (1 << 17)
+#define FLASH_CR_OBL_LAUNCH (1 << 27)
+#define FLASH_CR_OPTLOCK (1 << 30)
#define FLASH_CR_LOCK (1 << 31)
#define FLASH_CR_PNB(sec) (((sec) & 0xff) << 3)
-#define FLASH_CR_PNB_MASK FLASH_CR_SNB(0xff)
+#define FLASH_CR_PNB_MASK FLASH_CR_PNB(0xff)
#define STM32_FLASH_ECCR REG32(STM32_FLASH_REGS_BASE + 0x18)
#define STM32_FLASH_OPTR REG32(STM32_FLASH_REGS_BASE + 0x20)
#define STM32_FLASH_PCROP1SR REG32(STM32_FLASH_REGS_BASE + 0x24)
#define STM32_FLASH_PCROP1ER REG32(STM32_FLASH_REGS_BASE + 0x28)
#define STM32_FLASH_WRP1AR REG32(STM32_FLASH_REGS_BASE + 0x2C)
#define STM32_FLASH_WRP1BR REG32(STM32_FLASH_REGS_BASE + 0x30)
+#define FLASH_WRP_START(val) ((val) & 0xff)
+#define FLASH_WRP_END(val) (((val) >> 16) & 0xff)
+#define FLASH_WRP_RANGE(strt, end) (((end) << 16) | (strt))
+#define FLASH_WRP_RANGE_DISABLED FLASH_WRP_RANGE(0xFF, 0x00)
+#define FLASH_WRP_MASK FLASH_WRP_RANGE(0xFF, 0xFF)
#define STM32_OPTB_BASE 0x1FFF7800
-#define STM32_OPTB_USER_RDP_OFF 0x00
-#define STM32_OPTB_WRP1A 0x18
-#define STM32_OPTB_WRP1B 0x20
-#define STM32_OPTB_COMPL_OFF 4
+#define STM32_OPTB_USER_RDP REG32(STM32_OPTB_BASE + 0x00)
+#define STM32_OPTB_WRP1AR REG32(STM32_OPTB_BASE + 0x18)
+#define STM32_OPTB_WRP1BR REG32(STM32_OPTB_BASE + 0x20)
#elif defined(CHIP_FAMILY_STM32F4)
#define STM32_FLASH_REGS_BASE 0x40023c00
diff --git a/chip/stm32/system.c b/chip/stm32/system.c
index 8acdc7a729..b81dc0055c 100644
--- a/chip/stm32/system.c
+++ b/chip/stm32/system.c
@@ -292,6 +292,12 @@ void system_reset(int flags)
* use this for hard reset.
*/
STM32_FLASH_CR |= STM32_FLASH_CR_OBL_LAUNCH;
+#elif defined(CHIP_FAMILY_STM32L4)
+ STM32_FLASH_KEYR = FLASH_KEYR_KEY1;
+ STM32_FLASH_KEYR = FLASH_KEYR_KEY2;
+ STM32_FLASH_OPTKEYR = FLASH_OPTKEYR_KEY1;
+ STM32_FLASH_OPTKEYR = FLASH_OPTKEYR_KEY2;
+ STM32_FLASH_CR |= FLASH_CR_OBL_LAUNCH;
#else
/* Ask the watchdog to trigger a hard reboot */
STM32_IWDG_KR = 0x5555;