diff --git a/drivers/st/iwdg/stm32_iwdg.c b/drivers/st/iwdg/stm32_iwdg.c new file mode 100644 index 0000000..ea6fbb2 --- /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 +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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 0000000..bad2524 --- /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 + +#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 4bbc4db..f8b5e7b 100644 --- a/plat/st/common/include/stm32mp_common.h +++ b/plat/st/common/include/stm32mp_common.h @@ -28,6 +28,17 @@ /* 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 27d298e..7de264b 100644 --- a/plat/st/stm32mp1/bl2_plat_setup.c +++ b/plat/st/stm32mp1/bl2_plat_setup.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -28,6 +29,7 @@ #include #include +#include static struct console_stm32 console; @@ -276,6 +278,16 @@ } 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 0000000..a878308 --- /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 0ea7bbb..83d9770 100644 --- a/plat/st/stm32mp1/platform.mk +++ b/plat/st/stm32mp1/platform.mk @@ -57,11 +57,13 @@ 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 329ff68..5ad2190 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 #include #include +#include #include #include #include @@ -157,6 +158,10 @@ 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 0000000..a614267 --- /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 + +#include + +#include +#include +#include +#include +#include + +#include + +#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 37941aa..34e6e3c 100644 --- a/plat/st/stm32mp1/stm32mp1_def.h +++ b/plat/st/stm32mp1/stm32mp1_def.h @@ -15,6 +15,7 @@ #include #ifndef __ASSEMBLER__ +#include #include #include @@ -87,9 +88,9 @@ #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 @@ /* 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 @@ #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 340c7fb..886a8f3 100644 --- a/plat/st/stm32mp1/stm32mp1_private.c +++ b/plat/st/stm32mp1/stm32mp1_private.c @@ -8,6 +8,7 @@ #include +#include #include #define MAP_SRAM MAP_REGION_FLAT(STM32MP_SYSRAM_BASE, \ @@ -66,3 +67,78 @@ 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