stm32mp1: add watchdog support

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>
This commit is contained in:
Yann Gautier 2019-06-04 18:06:34 +02:00
parent a5ac37e7a6
commit 73680c230f
10 changed files with 382 additions and 2 deletions

View File

@ -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;
}

View File

@ -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 */

View 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

View 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) !=

View File

@ -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 */

View 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

View 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)

View File

@ -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;
}

View 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"

View 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