stm32mp1: add watchdog support
authorYann Gautier <yann.gautier@st.com>
Tue, 4 Jun 2019 16:06:34 +0000 (18:06 +0200)
committerYann Gautier <yann.gautier@st.com>
Mon, 2 Sep 2019 15:25:08 +0000 (17:25 +0200)
Introduce driver for STM32 IWDG peripheral (Independent Watchdog).
It is configured according to device tree content and should be enabled
from there.
The watchdog is not started by default. It can be started after an HW
reset if the dedicated OTP is fused.

The watchdog also needs to be frozen if a debugger is attached.
This is done by configuring the correct bits in DBGMCU.
This configuration is allowed by checking BSEC properties.

An increase of BL2 size is also required when adding this new code.

Change-Id: Ide7535d717885ce2f9c387cf17afd8b5607f3e7f
Signed-off-by: Yann Gautier <yann.gautier@st.com>
Signed-off-by: Lionel Debieve <lionel.debieve@st.com>
Signed-off-by: Nicolas Le Bayon <nicolas.le.bayon@st.com>
drivers/st/iwdg/stm32_iwdg.c [new file with mode: 0644]
include/drivers/st/stm32_iwdg.h [new file with mode: 0644]
plat/st/common/include/stm32mp_common.h
plat/st/stm32mp1/bl2_plat_setup.c
plat/st/stm32mp1/include/stm32mp1_dbgmcu.h [new file with mode: 0644]
plat/st/stm32mp1/platform.mk
plat/st/stm32mp1/sp_min/sp_min_setup.c
plat/st/stm32mp1/stm32mp1_dbgmcu.c [new file with mode: 0644]
plat/st/stm32mp1/stm32mp1_def.h
plat/st/stm32mp1/stm32mp1_private.c

diff --git a/drivers/st/iwdg/stm32_iwdg.c b/drivers/st/iwdg/stm32_iwdg.c
new file mode 100644 (file)
index 0000000..ea6fbb2
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 2017-2019, STMicroelectronics - All Rights Reserved
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <string.h>
+
+#include <libfdt.h>
+
+#include <platform_def.h>
+
+#include <arch_helpers.h>
+#include <common/debug.h>
+#include <drivers/arm/gicv2.h>
+#include <drivers/delay_timer.h>
+#include <drivers/st/stm32_iwdg.h>
+#include <drivers/st/stm32mp_clkfunc.h>
+#include <lib/mmio.h>
+#include <lib/utils.h>
+#include <plat/common/platform.h>
+
+/* IWDG registers offsets */
+#define IWDG_KR_OFFSET         0x00U
+
+/* Registers values */
+#define IWDG_KR_RELOAD_KEY     0xAAAA
+
+struct stm32_iwdg_instance {
+       uintptr_t base;
+       unsigned long clock;
+       uint8_t flags;
+       int num_irq;
+};
+
+static struct stm32_iwdg_instance stm32_iwdg[IWDG_MAX_INSTANCE];
+
+static int stm32_iwdg_get_dt_node(struct dt_node_info *info, int offset)
+{
+       int node;
+
+       node = dt_get_node(info, offset, DT_IWDG_COMPAT);
+       if (node < 0) {
+               if (offset == -1) {
+                       VERBOSE("%s: No IDWG found\n", __func__);
+               }
+               return -FDT_ERR_NOTFOUND;
+       }
+
+       return node;
+}
+
+void stm32_iwdg_refresh(void)
+{
+       uint8_t i;
+
+       for (i = 0U; i < IWDG_MAX_INSTANCE; i++) {
+               struct stm32_iwdg_instance *iwdg = &stm32_iwdg[i];
+
+               /* 0x00000000 is not a valid address for IWDG peripherals */
+               if (iwdg->base != 0U) {
+                       stm32mp_clk_enable(iwdg->clock);
+
+                       mmio_write_32(iwdg->base + IWDG_KR_OFFSET,
+                                     IWDG_KR_RELOAD_KEY);
+
+                       stm32mp_clk_disable(iwdg->clock);
+               }
+       }
+}
+
+int stm32_iwdg_init(void)
+{
+       int node = -1;
+       struct dt_node_info dt_info;
+       void *fdt;
+       uint32_t __unused count = 0;
+
+       if (fdt_get_address(&fdt) == 0) {
+               panic();
+       }
+
+       for (node = stm32_iwdg_get_dt_node(&dt_info, node);
+            node != -FDT_ERR_NOTFOUND;
+            node = stm32_iwdg_get_dt_node(&dt_info, node)) {
+               struct stm32_iwdg_instance *iwdg;
+               uint32_t hw_init;
+               uint32_t idx;
+
+               count++;
+
+               idx = stm32_iwdg_get_instance(dt_info.base);
+               iwdg = &stm32_iwdg[idx];
+               iwdg->base = dt_info.base;
+               iwdg->clock = (unsigned long)dt_info.clock;
+
+               /* DT can specify low power cases */
+               if (fdt_getprop(fdt, node, "stm32,enable-on-stop", NULL) ==
+                   NULL) {
+                       iwdg->flags |= IWDG_DISABLE_ON_STOP;
+               }
+
+               if (fdt_getprop(fdt, node, "stm32,enable-on-standby", NULL) ==
+                   NULL) {
+                       iwdg->flags |= IWDG_DISABLE_ON_STANDBY;
+               }
+
+               /* Explicit list of supported bit flags */
+               hw_init = stm32_iwdg_get_otp_config(idx);
+
+               if ((hw_init & IWDG_HW_ENABLED) != 0) {
+                       if (dt_info.status == DT_DISABLED) {
+                               ERROR("OTP enabled but iwdg%u DT-disabled\n",
+                                     idx + 1U);
+                               panic();
+                       }
+                       iwdg->flags |= IWDG_HW_ENABLED;
+               }
+
+               if (dt_info.status == DT_DISABLED) {
+                       zeromem((void *)iwdg,
+                               sizeof(struct stm32_iwdg_instance));
+                       continue;
+               }
+
+               if ((hw_init & IWDG_DISABLE_ON_STOP) != 0) {
+                       iwdg->flags |= IWDG_DISABLE_ON_STOP;
+               }
+
+               if ((hw_init & IWDG_DISABLE_ON_STANDBY) != 0) {
+                       iwdg->flags |= IWDG_DISABLE_ON_STANDBY;
+               }
+
+               VERBOSE("IWDG%u found, %ssecure\n", idx + 1U,
+                       ((dt_info.status & DT_NON_SECURE) != 0) ?
+                       "non-" : "");
+
+#if defined(IMAGE_BL2)
+               if (stm32_iwdg_shadow_update(idx, iwdg->flags) != BSEC_OK) {
+                       return -1;
+               }
+#endif
+       }
+
+       VERBOSE("%u IWDG instance%s found\n", count, (count > 1U) ? "s" : "");
+
+       return 0;
+}
diff --git a/include/drivers/st/stm32_iwdg.h b/include/drivers/st/stm32_iwdg.h
new file mode 100644 (file)
index 0000000..bad2524
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2018-2019, STMicroelectronics - All Rights Reserved
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef STM32_IWDG_H
+#define STM32_IWDG_H
+
+#include <stdint.h>
+
+#define IWDG_HW_ENABLED                        BIT(0)
+#define IWDG_DISABLE_ON_STOP           BIT(1)
+#define IWDG_DISABLE_ON_STANDBY                BIT(2)
+
+int stm32_iwdg_init(void);
+void stm32_iwdg_refresh(void);
+
+#endif /* STM32_IWDG_H */
index 4bbc4dba53a7879874da309c0cce53385f7b14a4..f8b5e7bad4e984f2919b36e8d62cc6803e9c8d6a 100644 (file)
@@ -28,6 +28,17 @@ uintptr_t stm32mp_pwr_base(void);
 /* Return the base address of the RCC peripheral */
 uintptr_t stm32mp_rcc_base(void);
 
+/* Get IWDG platform instance ID from peripheral IO memory base address */
+uint32_t stm32_iwdg_get_instance(uintptr_t base);
+
+/* Return bitflag mask for expected IWDG configuration from OTP content */
+uint32_t stm32_iwdg_get_otp_config(uint32_t iwdg_inst);
+
+#if defined(IMAGE_BL2)
+/* Update OTP shadow registers with IWDG configuration from device tree */
+uint32_t stm32_iwdg_shadow_update(uint32_t iwdg_inst, uint32_t flags);
+#endif
+
 /*
  * Platform util functions for the GPIO driver
  * @bank: Target GPIO bank ID as per DT bindings
index 27d298e8d5dfdd9054542cab18cfdfa93bfcc70a..7de264ba06fab9fd5e97e55967e4fcb715e58099 100644 (file)
@@ -17,6 +17,7 @@
 #include <drivers/generic_delay_timer.h>
 #include <drivers/st/bsec.h>
 #include <drivers/st/stm32_console.h>
+#include <drivers/st/stm32_iwdg.h>
 #include <drivers/st/stm32mp_pmic.h>
 #include <drivers/st/stm32mp_reset.h>
 #include <drivers/st/stm32mp1_clk.h>
@@ -28,6 +29,7 @@
 #include <plat/common/platform.h>
 
 #include <stm32mp1_context.h>
+#include <stm32mp1_dbgmcu.h>
 
 static struct console_stm32 console;
 
@@ -276,6 +278,16 @@ void bl2_el3_plat_arch_setup(void)
        }
 
 skip_console_init:
+       if (stm32_iwdg_init() < 0) {
+               panic();
+       }
+
+       stm32_iwdg_refresh();
+
+       result = stm32mp1_dbgmcu_freeze_iwdg2();
+       if (result != 0) {
+               INFO("IWDG2 freeze error : %i\n", result);
+       }
 
        if (stm32_save_boot_interface(boot_context->boot_interface_selected,
                                      boot_context->boot_interface_instance) !=
diff --git a/plat/st/stm32mp1/include/stm32mp1_dbgmcu.h b/plat/st/stm32mp1/include/stm32mp1_dbgmcu.h
new file mode 100644 (file)
index 0000000..a878308
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2015-2019, STMicroelectronics - All Rights Reserved
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef STM32MP1_DBGMCU_H
+#define STM32MP1_DBGMCU_H
+
+/*
+ * Freeze watchdog when a debugger is attached, if the security configuration
+ * allows it.
+ * Return 0 on success, a negative error value otherwise.
+ */
+int stm32mp1_dbgmcu_freeze_iwdg2(void);
+
+#endif /* STM32MP1_DBGMCU_H */
index 0ea7bbb8e6dba9a960bde3c81ce94f513c141454..83d9770396218e32aa8c152f2776f176bb5a2b23 100644 (file)
@@ -57,11 +57,13 @@ PLAT_BL_COMMON_SOURCES      +=      drivers/arm/tzc/tzc400.c                                \
                                drivers/st/ddr/stm32mp1_ddr_helpers.c                   \
                                drivers/st/gpio/stm32_gpio.c                            \
                                drivers/st/i2c/stm32_i2c.c                              \
+                               drivers/st/iwdg/stm32_iwdg.c                            \
                                drivers/st/pmic/stm32mp_pmic.c                          \
                                drivers/st/pmic/stpmic1.c                               \
                                drivers/st/reset/stm32mp1_reset.c                       \
                                plat/st/common/stm32mp_dt.c                             \
                                plat/st/stm32mp1/stm32mp1_context.c                     \
+                               plat/st/stm32mp1/stm32mp1_dbgmcu.c                      \
                                plat/st/stm32mp1/stm32mp1_helper.S                      \
                                plat/st/stm32mp1/stm32mp1_security.c                    \
                                plat/st/stm32mp1/stm32mp1_syscfg.c
index 329ff688a0d8ea9c305f990d675bb77a7201baaa..5ad219085bef90447b119d850e0de113fa3e2a7d 100644 (file)
@@ -19,6 +19,7 @@
 #include <drivers/st/bsec.h>
 #include <drivers/st/stm32_console.h>
 #include <drivers/st/stm32_gpio.h>
+#include <drivers/st/stm32_iwdg.h>
 #include <drivers/st/stm32mp1_clk.h>
 #include <dt-bindings/clock/stm32mp1-clks.h>
 #include <lib/el3_runtime/context_mgmt.h>
@@ -157,6 +158,10 @@ void sp_min_platform_setup(void)
        for (uint32_t pin = 0U; pin < STM32MP_GPIOZ_PIN_MAX_COUNT; pin++) {
                set_gpio_secure_cfg(GPIO_BANK_Z, pin, false);
        }
+
+       if (stm32_iwdg_init() < 0) {
+               panic();
+       }
 }
 
 void sp_min_plat_arch_setup(void)
diff --git a/plat/st/stm32mp1/stm32mp1_dbgmcu.c b/plat/st/stm32mp1/stm32mp1_dbgmcu.c
new file mode 100644 (file)
index 0000000..a614267
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2016-2019, STMicroelectronics - All Rights Reserved
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <errno.h>
+
+#include <platform_def.h>
+
+#include <common/debug.h>
+#include <drivers/st/bsec.h>
+#include <drivers/st/stm32mp1_rcc.h>
+#include <lib/mmio.h>
+#include <lib/utils_def.h>
+
+#include <stm32mp1_dbgmcu.h>
+
+#define DBGMCU_APB4FZ1         U(0x2C)
+#define DBGMCU_APB4FZ1_IWDG2   BIT(2)
+
+static uintptr_t get_rcc_base(void)
+{
+       /* This is called before stm32mp_rcc_base() is available */
+       return RCC_BASE;
+}
+
+static int stm32mp1_dbgmcu_init(void)
+{
+       uint32_t dbg_conf;
+       uintptr_t rcc_base = get_rcc_base();
+
+       dbg_conf = bsec_read_debug_conf();
+
+       if ((dbg_conf & BSEC_DBGSWGEN) == 0U) {
+               uint32_t result = bsec_write_debug_conf(dbg_conf |
+                                                       BSEC_DBGSWGEN);
+
+               if (result != BSEC_OK) {
+                       ERROR("Error enabling DBGSWGEN\n");
+                       return -1;
+               }
+       }
+
+       mmio_setbits_32(rcc_base + RCC_DBGCFGR, RCC_DBGCFGR_DBGCKEN);
+
+       return 0;
+}
+
+int stm32mp1_dbgmcu_freeze_iwdg2(void)
+{
+       uint32_t dbg_conf;
+
+       if (stm32mp1_dbgmcu_init() != 0) {
+               return -EPERM;
+       }
+
+       dbg_conf = bsec_read_debug_conf();
+
+       if ((dbg_conf & (BSEC_SPIDEN | BSEC_SPINDEN)) != 0U) {
+               mmio_setbits_32(DBGMCU_BASE + DBGMCU_APB4FZ1,
+                               DBGMCU_APB4FZ1_IWDG2);
+       }
+
+       return 0;
+}
index 37941aa74b556fc27d2fedf932af224aca5acc2e..34e6e3cc811d34e9ab3931038f77d65113c20221 100644 (file)
@@ -15,6 +15,7 @@
 #include <lib/xlat_tables/xlat_tables_defs.h>
 
 #ifndef __ASSEMBLER__
+#include <drivers/st/bsec.h>
 #include <drivers/st/stm32mp1_clk.h>
 
 #include <boot_api.h>
@@ -87,9 +88,9 @@ enum ddr_type {
 #endif
 #else
 #if STACK_PROTECTOR_ENABLED
-#define STM32MP_BL2_SIZE               U(0x00015000)   /* 84 Ko for BL2 */
+#define STM32MP_BL2_SIZE               U(0x00018000)   /* 96 Ko for BL2 */
 #else
-#define STM32MP_BL2_SIZE               U(0x00013000)   /* 76 Ko for BL2 */
+#define STM32MP_BL2_SIZE               U(0x00016000)   /* 88 Ko for BL2 */
 #endif
 #endif
 
@@ -245,6 +246,11 @@ enum ddr_type {
 /* DATA0 */
 #define DATA0_OTP_SECURED              BIT(6)
 
+/* IWDG OTP */
+#define HW2_OTP_IWDG_HW_POS            U(3)
+#define HW2_OTP_IWDG_FZ_STOP_POS       U(5)
+#define HW2_OTP_IWDG_FZ_STANDBY_POS    U(7)
+
 /* HW2 OTP */
 #define HW2_OTP_PRODUCT_BELOW_2V5      BIT(13)
 
@@ -271,14 +277,30 @@ static inline uint32_t tamp_bkpr(uint32_t idx)
  ******************************************************************************/
 #define DDRPHYC_BASE                   U(0x5A004000)
 
+/*******************************************************************************
+ * STM32MP1 IWDG
+ ******************************************************************************/
+#define IWDG_MAX_INSTANCE              U(2)
+#define IWDG1_INST                     U(0)
+#define IWDG2_INST                     U(1)
+
+#define IWDG1_BASE                     U(0x5C003000)
+#define IWDG2_BASE                     U(0x5A002000)
+
 /*******************************************************************************
  * STM32MP1 I2C4
  ******************************************************************************/
 #define I2C4_BASE                      U(0x5C002000)
 
+/*******************************************************************************
+ * STM32MP1 DBGMCU
+ ******************************************************************************/
+#define DBGMCU_BASE                    U(0x50081000)
+
 /*******************************************************************************
  * Device Tree defines
  ******************************************************************************/
+#define DT_IWDG_COMPAT                 "st,stm32mp1-iwdg"
 #define DT_PWR_COMPAT                  "st,stm32mp1-pwr"
 #define DT_RCC_CLK_COMPAT              "st,stm32mp1-rcc"
 #define DT_SYSCFG_COMPAT               "st,stm32mp157-syscfg"
index 340c7fba334f8296322fe9fa22d362f43c8ba1ef..886a8f3100b51d1e95f1727ad76f833a72f195a7 100644 (file)
@@ -8,6 +8,7 @@
 
 #include <platform_def.h>
 
+#include <drivers/st/stm32_iwdg.h>
 #include <lib/xlat_tables/xlat_tables_v2.h>
 
 #define MAP_SRAM       MAP_REGION_FLAT(STM32MP_SYSRAM_BASE, \
@@ -66,3 +67,78 @@ unsigned long stm32_get_gpio_bank_clock(unsigned int bank)
 
        return GPIOA + (bank - GPIO_BANK_A);
 }
+
+uint32_t stm32_iwdg_get_instance(uintptr_t base)
+{
+       switch (base) {
+       case IWDG1_BASE:
+               return IWDG1_INST;
+       case IWDG2_BASE:
+               return IWDG2_INST;
+       default:
+               panic();
+       }
+}
+
+uint32_t stm32_iwdg_get_otp_config(uint32_t iwdg_inst)
+{
+       uint32_t iwdg_cfg = 0U;
+       uint32_t otp_value;
+
+#if defined(IMAGE_BL2)
+       if (bsec_shadow_register(HW2_OTP) != BSEC_OK) {
+               panic();
+       }
+#endif
+
+       if (bsec_read_otp(&otp_value, HW2_OTP) != BSEC_OK) {
+               panic();
+       }
+
+       if ((otp_value & BIT(iwdg_inst + HW2_OTP_IWDG_HW_POS)) != 0U) {
+               iwdg_cfg |= IWDG_HW_ENABLED;
+       }
+
+       if ((otp_value & BIT(iwdg_inst + HW2_OTP_IWDG_FZ_STOP_POS)) != 0U) {
+               iwdg_cfg |= IWDG_DISABLE_ON_STOP;
+       }
+
+       if ((otp_value & BIT(iwdg_inst + HW2_OTP_IWDG_FZ_STANDBY_POS)) != 0U) {
+               iwdg_cfg |= IWDG_DISABLE_ON_STANDBY;
+       }
+
+       return iwdg_cfg;
+}
+
+#if defined(IMAGE_BL2)
+uint32_t stm32_iwdg_shadow_update(uint32_t iwdg_inst, uint32_t flags)
+{
+       uint32_t otp;
+       uint32_t result;
+
+       if (bsec_shadow_read_otp(&otp, HW2_OTP) != BSEC_OK) {
+               panic();
+       }
+
+       if ((flags & IWDG_DISABLE_ON_STOP) != 0U) {
+               otp |= BIT(iwdg_inst + HW2_OTP_IWDG_FZ_STOP_POS);
+       }
+
+       if ((flags & IWDG_DISABLE_ON_STANDBY) != 0U) {
+               otp |= BIT(iwdg_inst + HW2_OTP_IWDG_FZ_STANDBY_POS);
+       }
+
+       result = bsec_write_otp(otp, HW2_OTP);
+       if (result != BSEC_OK) {
+               return result;
+       }
+
+       /* Sticky lock OTP_IWDG (read and write) */
+       if (!bsec_write_sr_lock(HW2_OTP, 1U) ||
+           !bsec_write_sw_lock(HW2_OTP, 1U)) {
+               return BSEC_LOCK_FAIL;
+       }
+
+       return BSEC_OK;
+}
+#endif