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 <michael@walle.cc>
Reviewed-by: Priyanka Jain <priyanka.jain@nxp.com>
This commit is contained in:
Michael Walle 2021-11-15 23:45:43 +01:00 committed by Priyanka Jain
parent 42595eb706
commit f606c9a895
5 changed files with 154 additions and 17 deletions

View File

@ -1165,6 +1165,7 @@ SL28CLPD
M: Michael Walle <michael@walle.cc>
S: Maintained
F: drivers/misc/sl28cpld.c
F: drivers/watchdog/sl28cpld-wdt.c
SPI
M: Jagan Teki <jagan@amarulasolutions.com>

View File

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

View File

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

View File

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

View File

@ -0,0 +1,109 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Watchdog driver for the sl28cpld
*
* Copyright (c) 2021 Michael Walle <michael@walle.cc>
*/
#include <common.h>
#include <dm.h>
#include <wdt.h>
#include <sl28cpld.h>
#include <div64.h>
#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,
};