From f606c9a8959728f7df539e182fb799d3ccc92cc6 Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Mon, 15 Nov 2021 23:45:43 +0100 Subject: [PATCH] watchdog: add sl28cpld watchdog driver The watchdog timer is part of the sl28cpld management controller. The watchdog timer usually supervises the bootloader boot-up and if it bites the failsafe bootloader will be activated. Apart from that it supports the usual board level reset and one SMARC speciality: driving the WDT_TIMEOUT# signal. Signed-off-by: Michael Walle Reviewed-by: Priyanka Jain --- MAINTAINERS | 1 + doc/board/kontron/sl28.rst | 53 +++++++++++----- drivers/watchdog/Kconfig | 7 ++ drivers/watchdog/Makefile | 1 + drivers/watchdog/sl28cpld-wdt.c | 109 ++++++++++++++++++++++++++++++++ 5 files changed, 154 insertions(+), 17 deletions(-) create mode 100644 drivers/watchdog/sl28cpld-wdt.c diff --git a/MAINTAINERS b/MAINTAINERS index 8b3870ff96..989ea41e2c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1165,6 +1165,7 @@ SL28CLPD M: Michael Walle S: Maintained F: drivers/misc/sl28cpld.c +F: drivers/watchdog/sl28cpld-wdt.c SPI M: Jagan Teki diff --git a/doc/board/kontron/sl28.rst b/doc/board/kontron/sl28.rst index c7b18bed10..c2cdc5e424 100644 --- a/doc/board/kontron/sl28.rst +++ b/doc/board/kontron/sl28.rst @@ -35,23 +35,6 @@ The board is fully failsafe, you can't break anything. But because you've disabled the builtin watchdog you might have to manually enter failsafe mode by asserting the ``FORCE_RECOV#`` line during board reset. -Disable the builtin watchdog -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -- boot into the failsafe bootloader, either by asserting the - ``FORCE_RECOV#`` line or if you still have the original bootloader - installed you can use the command:: - - > wdt dev cpld_watchdog@4a; wdt expire 1 - -- in the failsafe bootloader use the "sl28 nvm" command to disable - the automatic start of the builtin watchdog:: - - > sl28 nvm 0008 - -- power-cycle the board - - Update image ------------ @@ -82,6 +65,42 @@ u-boot (yet). But you can use the i2c command to access it. > i2c md 4a 3.1 1 +Builtin watchdog +---------------- + +The builtin watchdog will supervise the bootloader startup. If anything +goes wrong it will reset the board and boot into the failsafe bootloader. + +Once the bootloader is started successfully, it will disable the watchdog +timer. + +wdt command flags +^^^^^^^^^^^^^^^^^ + +The `wdt start` as well as the `wdt expire` command take a flags argument. +The supported bitmask is as follows. + +| Bit | Description | +| --- | ----------------------------- | +| 0 | Enable failsafe mode | +| 1 | Lock the control register | +| 2 | Disable board reset | +| 3 | Enable WDT_TIME_OUT# line | + +For example, you can use `wdt expire 1` to issue a reset and boot into the +failsafe bootloader. + +Disable the builtin watchdog +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If for some reason, this isn't a desired behavior, the watchdog can also +be configured to not be enabled on board reset. It's configuration is saved +in the non-volatile board configuration bits. To change these you can use +the `sl28 nvm` command. + +For more information on the non-volatile board configuration bits, see the +following section. + Non-volatile Board Configuration Bits ------------------------------------- diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index cabac29053..f90f0ca02b 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -266,6 +266,13 @@ config WDT_SBSA In the single stage mode, when the timeout is reached, your system will be reset by WS1. The first signal (WS0) is ignored. +config WDT_SL28CPLD + bool "sl28cpld watchdog timer support" + depends on WDT && SL28CPLD + help + Enable support for the watchdog timer in the Kontron sl28cpld + management controller. + config WDT_SP805 bool "SP805 watchdog timer support" depends on WDT diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 6d2b3822c0..a35bd559f5 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -35,6 +35,7 @@ obj-$(CONFIG_WDT_OCTEONTX) += octeontx_wdt.o obj-$(CONFIG_WDT_OMAP3) += omap_wdt.o obj-$(CONFIG_WDT_SBSA) += sbsa_gwdt.o obj-$(CONFIG_WDT_K3_RTI) += rti_wdt.o +obj-$(CONFIG_WDT_SL28CPLD) += sl28cpld-wdt.o obj-$(CONFIG_WDT_SP805) += sp805_wdt.o obj-$(CONFIG_WDT_STM32MP) += stm32mp_wdt.o obj-$(CONFIG_WDT_SUNXI) += sunxi_wdt.o diff --git a/drivers/watchdog/sl28cpld-wdt.c b/drivers/watchdog/sl28cpld-wdt.c new file mode 100644 index 0000000000..af5a6b1a28 --- /dev/null +++ b/drivers/watchdog/sl28cpld-wdt.c @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Watchdog driver for the sl28cpld + * + * Copyright (c) 2021 Michael Walle + */ + +#include +#include +#include +#include +#include + +#define SL28CPLD_WDT_CTRL 0x00 +#define WDT_CTRL_EN0 BIT(0) +#define WDT_CTRL_EN1 BIT(1) +#define WDT_CTRL_EN_MASK GENMASK(1, 0) +#define WDT_CTRL_LOCK BIT(2) +#define WDT_CTRL_ASSERT_SYS_RESET BIT(6) +#define WDT_CTRL_ASSERT_WDT_TIMEOUT BIT(7) +#define SL28CPLD_WDT_TIMEOUT 0x01 +#define SL28CPLD_WDT_KICK 0x02 +#define WDT_KICK_VALUE 0x6b + +static int sl28cpld_wdt_reset(struct udevice *dev) +{ + return sl28cpld_write(dev, SL28CPLD_WDT_KICK, WDT_KICK_VALUE); +} + +static int sl28cpld_wdt_start(struct udevice *dev, u64 timeout, ulong flags) +{ + int ret, val; + + val = sl28cpld_read(dev, SL28CPLD_WDT_CTRL); + if (val < 0) + return val; + + /* (1) disable watchdog */ + val &= ~WDT_CTRL_EN_MASK; + ret = sl28cpld_write(dev, SL28CPLD_WDT_CTRL, val); + if (ret) + return ret; + + /* (2) set timeout */ + ret = sl28cpld_write(dev, SL28CPLD_WDT_TIMEOUT, lldiv(timeout, 1000)); + if (ret) + return ret; + + /* (3) kick it, will reset timer to the timeout value */ + ret = sl28cpld_wdt_reset(dev); + if (ret) + return ret; + + /* (4) enable either recovery or normal one */ + if (flags & BIT(0)) + val |= WDT_CTRL_EN1; + else + val |= WDT_CTRL_EN0; + + if (flags & BIT(1)) + val |= WDT_CTRL_LOCK; + + if (flags & BIT(2)) + val &= ~WDT_CTRL_ASSERT_SYS_RESET; + else + val |= WDT_CTRL_ASSERT_SYS_RESET; + + if (flags & BIT(3)) + val |= WDT_CTRL_ASSERT_WDT_TIMEOUT; + else + val &= ~WDT_CTRL_ASSERT_WDT_TIMEOUT; + + return sl28cpld_write(dev, SL28CPLD_WDT_CTRL, val); +} + +static int sl28cpld_wdt_stop(struct udevice *dev) +{ + int val; + + val = sl28cpld_read(dev, SL28CPLD_WDT_CTRL); + if (val < 0) + return val; + + return sl28cpld_write(dev, SL28CPLD_WDT_CTRL, val & ~WDT_CTRL_EN_MASK); +} + +static int sl28cpld_wdt_expire_now(struct udevice *dev, ulong flags) +{ + return sl28cpld_wdt_start(dev, 0, flags); +} + +static const struct wdt_ops sl28cpld_wdt_ops = { + .start = sl28cpld_wdt_start, + .reset = sl28cpld_wdt_reset, + .stop = sl28cpld_wdt_stop, + .expire_now = sl28cpld_wdt_expire_now, +}; + +static const struct udevice_id sl28cpld_wdt_ids[] = { + { .compatible = "kontron,sl28cpld-wdt", }, + {} +}; + +U_BOOT_DRIVER(sl28cpld_wdt) = { + .name = "sl28cpld-wdt", + .id = UCLASS_WDT, + .of_match = sl28cpld_wdt_ids, + .ops = &sl28cpld_wdt_ops, +};