From 10c3befc682d8be2f5200db31333d88b4ac16d8c Mon Sep 17 00:00:00 2001 From: =?utf8?q?Pali=20Roh=C3=A1r?= Date: Tue, 9 Nov 2021 17:14:02 +0100 Subject: [PATCH] arm: mvebu: turris_omnia: enable A385 watchdog before disabling MCU watchdog MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Commit aeb0ca64dbb5 ("arm: mvebu: turris_omnia: disable MCU watchdog in SPL when booting over UART") disabled MCU watchdog when booting over UART to ensure that watchdog does not reboot the board before UART transfer finishes. But if UART transfer fails for some reason, or if U-Boot binary crashes, then board hangs forever as there is no watchdog running which could reset it. To fix this issue, enable A385 watchdog with very high timeout before disabling MCU watchdog to ensure that even slow transfer can finish successfully before watchdog timer expires and also to ensure that if board hangs for some reason, watchdog will reset it. Omnia's MCU watchdog has fixed 120 seconds timer and it cannot be changed (without updating MCU firmware). A385 watchdog by default uses 25 MHz input clock and so the largest timeout value (2^32-1) can be just 171 seconds. But A385 watchdog can be switched to use NBCLK (L2) as input clock (on Turris Omnia it is 800 MHz clock) and in this case final watchdog clock frequency is calculated as: freq = NBCLK / 2 / (2 ^ R) So A385 watchdog on Turris Omnia can be configured to at most 1374 seconds (about 22 minutes). We set it to 10 minutes, which should be enough even for bigger U-Boot binaries or slower UART transfers. Both U-Boot and Linux kernel, when initializing A385 watchdog, switch watchdog timer to 25 MHz input clock, so usage of NBCLK input clock in U-Boot SPL does not cause any issues. Fixes: aeb0ca64dbb5 ("arm: mvebu: turris_omnia: disable MCU watchdog in SPL when booting over UART") Signed-off-by: Pali Rohár Signed-off-by: Marek Behún --- board/CZ.NIC/turris_omnia/turris_omnia.c | 65 +++++++++++++++++++++++- 1 file changed, 63 insertions(+), 2 deletions(-) diff --git a/board/CZ.NIC/turris_omnia/turris_omnia.c b/board/CZ.NIC/turris_omnia/turris_omnia.c index 36c596efc5..ae24d14b76 100644 --- a/board/CZ.NIC/turris_omnia/turris_omnia.c +++ b/board/CZ.NIC/turris_omnia/turris_omnia.c @@ -43,6 +43,23 @@ DECLARE_GLOBAL_DATA_PTR; #define OMNIA_I2C_EEPROM_CHIP_LEN 2 #define OMNIA_I2C_EEPROM_MAGIC 0x0341a034 +#define SYS_RSTOUT_MASK MVEBU_REGISTER(0x18260) +#define SYS_RSTOUT_MASK_WD BIT(10) + +#define A385_WDT_GLOBAL_CTRL MVEBU_REGISTER(0x20300) +#define A385_WDT_GLOBAL_RATIO_MASK GENMASK(18, 16) +#define A385_WDT_GLOBAL_RATIO_SHIFT 16 +#define A385_WDT_GLOBAL_25MHZ BIT(10) +#define A385_WDT_GLOBAL_ENABLE BIT(8) + +#define A385_WDT_GLOBAL_STATUS MVEBU_REGISTER(0x20304) +#define A385_WDT_GLOBAL_EXPIRED BIT(31) + +#define A385_WDT_DURATION MVEBU_REGISTER(0x20334) + +#define A385_WD_RSTOUT_UNMASK MVEBU_REGISTER(0x20704) +#define A385_WD_RSTOUT_UNMASK_GLOBAL BIT(8) + enum mcu_commands { CMD_GET_STATUS_WORD = 0x01, CMD_GET_RESET = 0x09, @@ -141,6 +158,47 @@ static int omnia_mcu_write(u8 cmd, const void *buf, int len) return dm_i2c_write(chip, cmd, buf, len); } +static void enable_a385_watchdog(unsigned int timeout_minutes) +{ + struct sar_freq_modes sar_freq; + u32 watchdog_freq; + + printf("Enabling A385 watchdog with %u minutes timeout...\n", + timeout_minutes); + + /* + * Use NBCLK clock (a.k.a. L2 clock) as watchdog input clock with + * its maximal ratio 7 instead of default fixed 25 MHz clock. + * It allows to set watchdog duration up to the 22 minutes. + */ + clrsetbits_32(A385_WDT_GLOBAL_CTRL, + A385_WDT_GLOBAL_25MHZ | A385_WDT_GLOBAL_RATIO_MASK, + 7 << A385_WDT_GLOBAL_RATIO_SHIFT); + + /* + * Calculate watchdog clock frequency. It is defined by formula: + * freq = NBCLK / 2 / (2 ^ ratio) + * We set ratio to the maximal possible value 7. + */ + get_sar_freq(&sar_freq); + watchdog_freq = sar_freq.nb_clk * 1000000 / 2 / (1 << 7); + + /* Set watchdog duration */ + writel(timeout_minutes * 60 * watchdog_freq, A385_WDT_DURATION); + + /* Clear the watchdog expiration bit */ + clrbits_32(A385_WDT_GLOBAL_STATUS, A385_WDT_GLOBAL_EXPIRED); + + /* Enable watchdog timer */ + setbits_32(A385_WDT_GLOBAL_CTRL, A385_WDT_GLOBAL_ENABLE); + + /* Enable reset on watchdog */ + setbits_32(A385_WD_RSTOUT_UNMASK, A385_WD_RSTOUT_UNMASK_GLOBAL); + + /* Unmask reset for watchdog */ + clrbits_32(SYS_RSTOUT_MASK, SYS_RSTOUT_MASK_WD); +} + static bool disable_mcu_watchdog(void) { int ret; @@ -423,10 +481,13 @@ void spl_board_init(void) { /* * If booting from UART, disable MCU watchdog in SPL, since uploading - * U-Boot proper can take too much time and trigger it. + * U-Boot proper can take too much time and trigger it. Instead enable + * A385 watchdog with very high timeout (10 minutes) to prevent hangup. */ - if (get_boot_device() == BOOT_DEVICE_UART) + if (get_boot_device() == BOOT_DEVICE_UART) { + enable_a385_watchdog(10); disable_mcu_watchdog(); + } } int board_init(void) -- 2.39.5