arm: mvebu: turris_omnia: enable A385 watchdog before disabling MCU watchdog
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 <pali@kernel.org>
Signed-off-by: Marek Behún <marek.behun@nic.cz>
diff --git a/board/CZ.NIC/turris_omnia/turris_omnia.c b/board/CZ.NIC/turris_omnia/turris_omnia.c
index 36c596e..ae24d14 100644
--- a/board/CZ.NIC/turris_omnia/turris_omnia.c
+++ b/board/CZ.NIC/turris_omnia/turris_omnia.c
@@ -43,6 +43,23 @@
#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 @@
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 @@
{
/*
* 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)