From 9a5a7225737051e9523a775ebf8bba63f144259b Mon Sep 17 00:00:00 2001 From: Jack Rosenthal Date: Thu, 14 Mar 2019 13:14:15 -0600 Subject: ish: Add watchdog timer This adds support for the watchdog timer (WDT) available on Intel Sensor Hub (ISH). The ISH will reset after T1 expires; see the comments at the top of watchdog.c for further information on this design decision. Originally, we had planned to implement a counter that would disable the WDT after N failures. This was abandoned, since the register used to store the counter was not able to maintain a value across reset on a reliable basis (see b:128679825). BUG=b:127980538,b:128679825 BRANCH=none TEST=Used waitms command on arcada to verify WDT triggered a warning IRQ after T1 and reset the system. Change-Id: I4bd16c253110d60c57eb24cda2abc0facee20748 Signed-off-by: Jack Rosenthal Reviewed-on: https://chromium-review.googlesource.com/1526316 Commit-Ready: ChromeOS CL Exonerator Bot Reviewed-by: Jett Rink --- chip/ish/config_chip.h | 11 +++++++ chip/ish/registers.h | 18 ++++++++++- chip/ish/watchdog.c | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 chip/ish/watchdog.c (limited to 'chip') diff --git a/chip/ish/config_chip.h b/chip/ish/config_chip.h index c595e825b9..d10ee650d6 100644 --- a/chip/ish/config_chip.h +++ b/chip/ish/config_chip.h @@ -67,6 +67,17 @@ #include "config_flash_layout.h" +/*****************************************************************************/ +/* Watchdog Timer Configuration */ +/*****************************************************************************/ +#if defined(CHIP_FAMILY_ISH3) || defined(CHIP_FAMILY_ISH5) +#define WDT_CLOCK_HZ (120000000) /* 120 MHz */ +#elif defined(CHIP_FAMILY_ISH4) +#define WDT_CLOCK_HZ (100000000) /* 100 MHz */ +#else +#error "CHIP_FAMILY_ISH(3|4|5) must be defined" +#endif + /****************************************************************************/ /* Customize the build */ /* Optional features present on this chip */ diff --git a/chip/ish/registers.h b/chip/ish/registers.h index 9106463356..834dc63b72 100644 --- a/chip/ish/registers.h +++ b/chip/ish/registers.h @@ -40,6 +40,7 @@ enum ish_i2c_port { #define ISH_PMU_BASE 0x00800000 #define ISH_CCU_BASE 0x00900000 #define ISH_IPC_BASE 0x00B00000 +#define ISH_WDT_BASE 0xFDE00000 #define ISH_IOAPIC_BASE 0xFEC00000 #define ISH_HPET_BASE 0xFED00000 #define ISH_LAPIC_BASE 0xFEE00000 @@ -48,6 +49,7 @@ enum ish_i2c_port { #define ISH_I2C0_IRQ 0 #define ISH_I2C1_IRQ 1 #define ISH_I2C2_IRQ 40 +#define ISH_WDT_IRQ 6 #define ISH_GPIO_IRQ 7 #define ISH_HPET_TIMER0_IRQ 55 #define ISH_HPET_TIMER1_IRQ 8 @@ -90,6 +92,7 @@ enum ish_i2c_port { #define ISH_I2C0_VEC IRQ_TO_VEC(ISH_I2C0_IRQ) #define ISH_I2C1_VEC IRQ_TO_VEC(ISH_I2C1_IRQ) #define ISH_I2C2_VEC IRQ_TO_VEC(ISH_I2C2_IRQ) +#define ISH_WDT_VEC IRQ_TO_VEC(ISH_WDT_IRQ) #define ISH_GPIO_VEC IRQ_TO_VEC(ISH_GPIO_IRQ) #define ISH_HPET_TIMER0_VEC IRQ_TO_VEC(ISH_HPET_TIMER0_IRQ) #define ISH_HPET_TIMER1_VEC IRQ_TO_VEC(ISH_HPET_TIMER1_IRQ) @@ -135,10 +138,17 @@ enum ish_i2c_port { /* CCU Registers */ #define CCU_TCG_EN REG32(ISH_CCU_BASE + 0x0) #define CCU_BCG_EN REG32(ISH_CCU_BASE + 0x4) -#define CCU_RST_HST REG32(ISH_CCU_BASE + 0x34) +#define CCU_WDT_CD REG32(ISH_CCU_BASE + 0x8) +#define CCU_RST_HST REG32(ISH_CCU_BASE + 0x34) /* Reset history */ #define CCU_TCG_ENABLE REG32(ISH_CCU_BASE + 0x38) #define CCU_BCG_ENABLE REG32(ISH_CCU_BASE + 0x3c) +/* Bitmasks for CCU_RST_HST */ +#define CCU_SW_RST (1 << 0) /* Used to indicate SW reset */ +#define CCU_WDT_RST (1 << 1) /* Used to indicate WDT reset */ +#define CCU_MIASS_RST (1 << 2) /* Used to indicate UIA shutdown reset */ +#define CCU_SRECC_RST (1 << 3) /* Used to indicate SRAM ECC reset */ + /* CSME Registers */ #define ISH_RST_REG REG32(ISH_IPC_BASE + 0x44) @@ -158,6 +168,12 @@ enum ish_i2c_port { #define IOAPIC_REDTBL_TRIGGER_LEVEL 0x00008000 #define IOAPIC_REDTBL_MASK 0x00010000 +/* WDT (Watchdog Timer) Registers */ +#define WDT_CONTROL REG32(ISH_WDT_BASE + 0x0) +#define WDT_RELOAD REG32(ISH_WDT_BASE + 0x4) +#define WDT_VALUES REG32(ISH_WDT_BASE + 0x8) +#define WDT_CONTROL_ENABLE_BIT (1 << 17) + /* LAPIC registers */ #define LAPIC_EOI_REG 0xFEE000B0 #define LAPIC_ISR_REG 0xFEE00170 diff --git a/chip/ish/watchdog.c b/chip/ish/watchdog.c new file mode 100644 index 0000000000..fd0ebcf247 --- /dev/null +++ b/chip/ish/watchdog.c @@ -0,0 +1,84 @@ +/* Copyright 2019 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. + * + * Watchdog Timer + * + * In ISH, there is a watchdog timer available from the hardware. It is + * controlled by a few registers: + * + * - WDT_CONTROL (consists of enable bit, T1, and T2 values): When T1 + * reaches 0, a warning is fired. After T2 then reaches 0, the system + * will reset. + * - WDT_RELOAD: Pet the watchdog by setting to 1 + * - WDT_VALUES: Gives software access to T1 and T2 if needed + * + * For ISH implementation, we wish to reset only the ISH. Waiting until + * T2 expires will kill the whole system. The functionality of T2 is + * ignored, and we simply call system_reset when T1 expires. T2 will + * only be used if the system cannot reset when T1 expires. + */ + +#include "common.h" +#include "console.h" +#include "hooks.h" +#include "task.h" +#include "registers.h" +#include "system.h" +#include "watchdog.h" + +/* Units are hundreds of milliseconds */ +#define WDT_T1_PERIOD (100) /* 10 seconds */ +#define WDT_T2_PERIOD (10) /* 1 second */ + +int watchdog_init(void) +{ + /* Initialize WDT clock divider */ + CCU_WDT_CD = WDT_CLOCK_HZ / 10; /* 10 Hz => 100 ms period */ + + /* Enable the watchdog timer and set initial T1/T2 values */ + WDT_CONTROL = WDT_CONTROL_ENABLE_BIT + | (WDT_T2_PERIOD << 8) + | WDT_T1_PERIOD; + + task_enable_irq(ISH_WDT_IRQ); + + return EC_SUCCESS; +} + +/* Parameters are pushed by hardware, we only care about %EIP */ +__attribute__ ((noreturn)) +void watchdog_warning(uint32_t errorcode, + uint32_t eip, + uint32_t cs, + uint32_t eflags) +{ + ccprintf("\nWDT Expired. EIP was 0x%08X. Resetting...\n", eip); + cflush(); + + system_reset(SYSTEM_RESET_AP_WATCHDOG); + __builtin_unreachable(); +} + +__attribute__ ((noreturn)) +void watchdog_warning_irq(void) +{ + /* + * Parameters to watchdog_warning were pushed by hardware, use + * asm here to re-use these parameters in the call. + */ + __asm__ ("call watchdog_warning\n"); + __builtin_unreachable(); +} +DECLARE_IRQ(ISH_WDT_IRQ, watchdog_warning_irq); + +void watchdog_reload(void) +{ + /* + * ISH Supplemental Registers Info, 1.2.6.2: + * "When firmware writes a 1 to this bit, hardware reloads + * the values in WDT_T1 and WDT_T2..." + */ + WDT_RELOAD = 1; +} +DECLARE_HOOK(HOOK_TICK, watchdog_reload, HOOK_PRIO_DEFAULT); -- cgit v1.2.1