summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/st/iwdg/stm32_iwdg.c150
-rw-r--r--include/drivers/st/stm32_iwdg.h19
-rw-r--r--plat/st/common/include/stm32mp_common.h11
-rw-r--r--plat/st/stm32mp1/bl2_plat_setup.c12
-rw-r--r--plat/st/stm32mp1/include/stm32mp1_dbgmcu.h17
-rw-r--r--plat/st/stm32mp1/platform.mk2
-rw-r--r--plat/st/stm32mp1/sp_min/sp_min_setup.c5
-rw-r--r--plat/st/stm32mp1/stm32mp1_dbgmcu.c66
-rw-r--r--plat/st/stm32mp1/stm32mp1_def.h26
-rw-r--r--plat/st/stm32mp1/stm32mp1_private.c76
10 files changed, 382 insertions, 2 deletions
diff --git a/drivers/st/iwdg/stm32_iwdg.c b/drivers/st/iwdg/stm32_iwdg.c
new file mode 100644
index 00000000..ea6fbb2b
--- /dev/null
+++ b/drivers/st/iwdg/stm32_iwdg.c
@@ -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
index 00000000..bad25244
--- /dev/null
+++ b/include/drivers/st/stm32_iwdg.h
@@ -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 */
diff --git a/plat/st/common/include/stm32mp_common.h b/plat/st/common/include/stm32mp_common.h
index 4bbc4dba..f8b5e7ba 100644
--- a/plat/st/common/include/stm32mp_common.h
+++ b/plat/st/common/include/stm32mp_common.h
@@ -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
diff --git a/plat/st/stm32mp1/bl2_plat_setup.c b/plat/st/stm32mp1/bl2_plat_setup.c
index 27d298e8..7de264ba 100644
--- a/plat/st/stm32mp1/bl2_plat_setup.c
+++ b/plat/st/stm32mp1/bl2_plat_setup.c
@@ -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
index 00000000..a878308b
--- /dev/null
+++ b/plat/st/stm32mp1/include/stm32mp1_dbgmcu.h
@@ -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 */
diff --git a/plat/st/stm32mp1/platform.mk b/plat/st/stm32mp1/platform.mk
index 0ea7bbb8..83d97703 100644
--- a/plat/st/stm32mp1/platform.mk
+++ b/plat/st/stm32mp1/platform.mk
@@ -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
diff --git a/plat/st/stm32mp1/sp_min/sp_min_setup.c b/plat/st/stm32mp1/sp_min/sp_min_setup.c
index 329ff688..5ad21908 100644
--- a/plat/st/stm32mp1/sp_min/sp_min_setup.c
+++ b/plat/st/stm32mp1/sp_min/sp_min_setup.c
@@ -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
index 00000000..a614267d
--- /dev/null
+++ b/plat/st/stm32mp1/stm32mp1_dbgmcu.c
@@ -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;
+}
diff --git a/plat/st/stm32mp1/stm32mp1_def.h b/plat/st/stm32mp1/stm32mp1_def.h
index 37941aa7..34e6e3cc 100644
--- a/plat/st/stm32mp1/stm32mp1_def.h
+++ b/plat/st/stm32mp1/stm32mp1_def.h
@@ -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)
@@ -272,13 +278,29 @@ 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"
diff --git a/plat/st/stm32mp1/stm32mp1_private.c b/plat/st/stm32mp1/stm32mp1_private.c
index 340c7fba..886a8f31 100644
--- a/plat/st/stm32mp1/stm32mp1_private.c
+++ b/plat/st/stm32mp1/stm32mp1_private.c
@@ -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