summaryrefslogtreecommitdiff
path: root/drivers/watchdog
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/watchdog')
-rw-r--r--drivers/watchdog/Kconfig20
-rw-r--r--drivers/watchdog/Makefile1
-rw-r--r--drivers/watchdog/booke_wdt.c106
-rw-r--r--drivers/watchdog/designware_wdt.c37
-rw-r--r--drivers/watchdog/imx_watchdog.c2
-rw-r--r--drivers/watchdog/ulp_wdog.c2
-rw-r--r--drivers/watchdog/wdt-uclass.c28
7 files changed, 177 insertions, 19 deletions
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 602ccbe41c0..f0ff2612a6b 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -9,6 +9,19 @@ config WATCHDOG
this option if you want to service enabled watchdog by U-Boot. Disable
this option if you want U-Boot to start watchdog but never service it.
+config WATCHDOG_AUTOSTART
+ bool "Automatically start watchdog timer"
+ depends on WDT
+ default y
+ help
+ Automatically start watchdog timer and start servicing it during
+ init phase. Enabled by default. Disable this option if you want
+ to compile U-Boot with CONFIG_WDT support but do not want to
+ activate watchdog, like when CONFIG_WDT option is disabled. You
+ would be able to start watchdog manually by 'wdt' command. Useful
+ when you want to have support for 'wdt' command but do not want
+ to have watchdog enabled by default.
+
config WATCHDOG_TIMEOUT_MSECS
int "Watchdog timeout in msec"
default 128000 if ARCH_MX25 || ARCH_MX31 || ARCH_MX5 || ARCH_MX6
@@ -111,6 +124,13 @@ config WDT_BCM6345
The watchdog timer is stopped when initialized.
It performs full SoC reset.
+config WDT_BOOKE
+ bool "PowerPC Book-E watchdog driver"
+ depends on WDT && MPC85xx
+ help
+ Watchdog driver for PowerPC Book-E chips, such as the Freescale
+ MPC85xx SOCs and the IBM PowerPC 440.
+
config WDT_CDNS
bool "Cadence watchdog timer support"
depends on WDT
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 6e70c7ae19c..5c7ef593fe5 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -21,6 +21,7 @@ obj-$(CONFIG_WDT_ARMADA_37XX) += armada-37xx-wdt.o
obj-$(CONFIG_WDT_ASPEED) += ast_wdt.o
obj-$(CONFIG_WDT_AST2600) += ast2600_wdt.o
obj-$(CONFIG_WDT_BCM6345) += bcm6345_wdt.o
+obj-$(CONFIG_WDT_BOOKE) += booke_wdt.o
obj-$(CONFIG_WDT_CORTINA) += cortina_wdt.o
obj-$(CONFIG_WDT_ORION) += orion_wdt.o
obj-$(CONFIG_WDT_CDNS) += cdns_wdt.o
diff --git a/drivers/watchdog/booke_wdt.c b/drivers/watchdog/booke_wdt.c
new file mode 100644
index 00000000000..50c091956e7
--- /dev/null
+++ b/drivers/watchdog/booke_wdt.c
@@ -0,0 +1,106 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Watchdog timer for PowerPC Book-E systems
+ */
+
+#include <div64.h>
+#include <dm.h>
+#include <wdt.h>
+#include <asm/processor.h>
+
+#define WDTP_MASK TCR_WP(0x3f)
+
+/* For the specified period, determine the number of seconds
+ * corresponding to the reset time. There will be a watchdog
+ * exception at approximately 3/5 of this time.
+ *
+ * The formula to calculate this is given by:
+ * 2.5 * (2^(63-period+1)) / timebase_freq
+ *
+ * In order to simplify things, we assume that period is
+ * at least 1. This will still result in a very long timeout.
+ */
+static unsigned long long period_to_sec(unsigned int period)
+{
+ unsigned long long tmp = 1ULL << (64 - period);
+ unsigned long tmp2 = get_tbclk();
+
+ /* tmp may be a very large number and we don't want to overflow,
+ * so divide the timebase freq instead of multiplying tmp
+ */
+ tmp2 = tmp2 / 5 * 2;
+
+ do_div(tmp, tmp2);
+ return tmp;
+}
+
+/*
+ * This procedure will find the highest period which will give a timeout
+ * greater than the one required. e.g. for a bus speed of 66666666 and
+ * and a parameter of 2 secs, then this procedure will return a value of 38.
+ */
+static unsigned int sec_to_period(unsigned int secs)
+{
+ unsigned int period;
+
+ for (period = 63; period > 0; period--) {
+ if (period_to_sec(period) >= secs)
+ return period;
+ }
+ return 0;
+}
+
+static int booke_wdt_reset(struct udevice *dev)
+{
+ mtspr(SPRN_TSR, TSR_ENW | TSR_WIS);
+
+ return 0;
+}
+
+static int booke_wdt_start(struct udevice *dev, u64 timeout_ms, ulong flags)
+{
+ u32 val;
+ unsigned int timeout = DIV_ROUND_UP(timeout_ms, 1000);
+
+ /* clear status before enabling watchdog */
+ booke_wdt_reset(dev);
+ val = mfspr(SPRN_TCR);
+ val &= ~WDTP_MASK;
+ val |= (TCR_WIE | TCR_WRC(WRC_CHIP) | TCR_WP(sec_to_period(timeout)));
+
+ mtspr(SPRN_TCR, val);
+
+ return 0;
+}
+
+static int booke_wdt_stop(struct udevice *dev)
+{
+ u32 val;
+
+ val = mfspr(SPRN_TCR);
+ val &= ~(TCR_WIE | WDTP_MASK);
+ mtspr(SPRN_TCR, val);
+
+ /* clear status to make sure nothing is pending */
+ booke_wdt_reset(dev);
+
+ return 0;
+}
+
+static const struct wdt_ops booke_wdt_ops = {
+ .start = booke_wdt_start,
+ .stop = booke_wdt_stop,
+ .reset = booke_wdt_reset,
+};
+
+static const struct udevice_id booke_wdt_ids[] = {
+ { .compatible = "fsl,booke-wdt" },
+ {}
+};
+
+U_BOOT_DRIVER(booke_wdt) = {
+ .name = "booke_wdt",
+ .id = UCLASS_WDT,
+ .of_match = booke_wdt_ids,
+ .ops = &booke_wdt_ops,
+};
diff --git a/drivers/watchdog/designware_wdt.c b/drivers/watchdog/designware_wdt.c
index c020324973e..9e5487168cd 100644
--- a/drivers/watchdog/designware_wdt.c
+++ b/drivers/watchdog/designware_wdt.c
@@ -9,7 +9,6 @@
#include <reset.h>
#include <wdt.h>
#include <asm/io.h>
-#include <asm/utils.h>
#include <linux/bitops.h>
#define DW_WDT_CR 0x00
@@ -35,7 +34,7 @@ static int designware_wdt_settimeout(void __iomem *base, unsigned int clk_khz,
signed int i;
/* calculate the timeout range value */
- i = log_2_n_round_up(timeout * clk_khz) - 16;
+ i = fls(timeout * clk_khz - 1) - 16;
i = clamp(i, 0, 15);
writel(i | (i << 4), base + DW_WDT_TORR);
@@ -130,27 +129,39 @@ static int designware_wdt_probe(struct udevice *dev)
if (ret)
return ret;
+ ret = clk_enable(&clk);
+ if (ret)
+ goto err;
+
priv->clk_khz = clk_get_rate(&clk) / 1000;
- if (!priv->clk_khz)
- return -EINVAL;
+ if (!priv->clk_khz) {
+ ret = -EINVAL;
+ goto err;
+ }
#else
priv->clk_khz = CONFIG_DW_WDT_CLOCK_KHZ;
#endif
-#if CONFIG_IS_ENABLED(DM_RESET)
- struct reset_ctl_bulk resets;
+ if (CONFIG_IS_ENABLED(DM_RESET)) {
+ struct reset_ctl_bulk resets;
- ret = reset_get_bulk(dev, &resets);
- if (ret)
- return ret;
+ ret = reset_get_bulk(dev, &resets);
+ if (ret)
+ goto err;
- ret = reset_deassert_bulk(&resets);
- if (ret)
- return ret;
-#endif
+ ret = reset_deassert_bulk(&resets);
+ if (ret)
+ goto err;
+ }
/* reset to disable the watchdog */
return designware_wdt_stop(dev);
+
+err:
+#if CONFIG_IS_ENABLED(CLK)
+ clk_free(&clk);
+#endif
+ return ret;
}
static const struct wdt_ops designware_wdt_ops = {
diff --git a/drivers/watchdog/imx_watchdog.c b/drivers/watchdog/imx_watchdog.c
index 5e0a096ce50..3586246fbfb 100644
--- a/drivers/watchdog/imx_watchdog.c
+++ b/drivers/watchdog/imx_watchdog.c
@@ -44,7 +44,7 @@ static void imx_watchdog_expire_now(struct watchdog_regs *wdog, bool ext_reset)
#if !defined(CONFIG_IMX_WATCHDOG) || \
(defined(CONFIG_IMX_WATCHDOG) && !CONFIG_IS_ENABLED(WDT))
-void __attribute__((weak)) reset_cpu(ulong addr)
+void __attribute__((weak)) reset_cpu(void)
{
struct watchdog_regs *wdog = (struct watchdog_regs *)WDOG1_BASE_ADDR;
diff --git a/drivers/watchdog/ulp_wdog.c b/drivers/watchdog/ulp_wdog.c
index 7533fc612c7..6f63b11b9ff 100644
--- a/drivers/watchdog/ulp_wdog.c
+++ b/drivers/watchdog/ulp_wdog.c
@@ -77,7 +77,7 @@ void hw_watchdog_init(void)
hw_watchdog_reset();
}
-void reset_cpu(ulong addr)
+void reset_cpu(void)
{
struct wdog_regs *wdog = (struct wdog_regs *)WDOG_BASE_ADDR;
diff --git a/drivers/watchdog/wdt-uclass.c b/drivers/watchdog/wdt-uclass.c
index 28f7918c467..0603ffbd36d 100644
--- a/drivers/watchdog/wdt-uclass.c
+++ b/drivers/watchdog/wdt-uclass.c
@@ -27,6 +27,7 @@ static ulong reset_period = 1000;
int initr_watchdog(void)
{
u32 timeout = WATCHDOG_TIMEOUT_SECS;
+ int ret;
/*
* Init watchdog: This will call the probe function of the
@@ -50,8 +51,17 @@ int initr_watchdog(void)
4 * reset_period) / 4;
}
- wdt_start(gd->watchdog_dev, timeout * 1000, 0);
- gd->flags |= GD_FLG_WDT_READY;
+ if (!CONFIG_IS_ENABLED(WATCHDOG_AUTOSTART)) {
+ printf("WDT: Not starting\n");
+ return 0;
+ }
+
+ ret = wdt_start(gd->watchdog_dev, timeout * 1000, 0);
+ if (ret != 0) {
+ printf("WDT: Failed to start\n");
+ return 0;
+ }
+
printf("WDT: Started with%s servicing (%ds timeout)\n",
IS_ENABLED(CONFIG_WATCHDOG) ? "" : "out", timeout);
@@ -61,21 +71,31 @@ int initr_watchdog(void)
int wdt_start(struct udevice *dev, u64 timeout_ms, ulong flags)
{
const struct wdt_ops *ops = device_get_ops(dev);
+ int ret;
if (!ops->start)
return -ENOSYS;
- return ops->start(dev, timeout_ms, flags);
+ ret = ops->start(dev, timeout_ms, flags);
+ if (ret == 0)
+ gd->flags |= GD_FLG_WDT_READY;
+
+ return ret;
}
int wdt_stop(struct udevice *dev)
{
const struct wdt_ops *ops = device_get_ops(dev);
+ int ret;
if (!ops->stop)
return -ENOSYS;
- return ops->stop(dev);
+ ret = ops->stop(dev);
+ if (ret == 0)
+ gd->flags &= ~GD_FLG_WDT_READY;
+
+ return ret;
}
int wdt_reset(struct udevice *dev)