summaryrefslogtreecommitdiffstats
path: root/drivers/watchdog
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/watchdog')
-rw-r--r--drivers/watchdog/Kconfig33
-rw-r--r--drivers/watchdog/Makefile5
-rw-r--r--drivers/watchdog/ar9344_wdt.c14
-rw-r--r--drivers/watchdog/at91sam9_wdt.c9
-rw-r--r--drivers/watchdog/bcm2835_wdt.c42
-rw-r--r--drivers/watchdog/cadence_wdt.c278
-rw-r--r--drivers/watchdog/davinci_wdt.c11
-rw-r--r--drivers/watchdog/dw_wdt.c11
-rw-r--r--drivers/watchdog/efi_wdt.c8
-rw-r--r--drivers/watchdog/f71808e_wdt.c8
-rw-r--r--drivers/watchdog/gpio_wdt.c29
-rw-r--r--drivers/watchdog/im28wd.c20
-rw-r--r--drivers/watchdog/imxulp-wdt.c168
-rw-r--r--drivers/watchdog/imxwd.c75
-rw-r--r--drivers/watchdog/itco_wdt.c2
-rw-r--r--drivers/watchdog/jz4740.c12
-rw-r--r--drivers/watchdog/kvx_wdt.c9
-rw-r--r--drivers/watchdog/omap_wdt.c11
-rw-r--r--drivers/watchdog/orion_wdt.c15
-rw-r--r--drivers/watchdog/rave-sp-wdt.c9
-rw-r--r--drivers/watchdog/rn5t568_wdt.c147
-rw-r--r--drivers/watchdog/starfive_wdt.c5
-rw-r--r--drivers/watchdog/stm32_iwdg.c5
-rw-r--r--drivers/watchdog/stpmic1_wdt.c6
-rw-r--r--drivers/watchdog/wd_core.c27
-rw-r--r--drivers/watchdog/wdat_wdt.c496
26 files changed, 1267 insertions, 188 deletions
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 080bc91ff7..762e37c9c2 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
config WATCHDOG_IMX_RESET_SOURCE
bool
@@ -30,7 +31,7 @@ config WATCHDOG_AT91SAM9
config WATCHDOG_EFI
bool "Generic EFI Watchdog Driver"
- depends on EFI_BOOTUP
+ depends on EFI_PAYLOAD
help
Add support for the EFI watchdog.
@@ -58,6 +59,12 @@ config WATCHDOG_IMX
help
Add support for watchdog found on Freescale i.MX SoCs.
+config WATCHDOG_IMXULP
+ bool "i.MX ULP watchdog"
+ depends on ARCH_IMX || COMPILE_TEST
+ help
+ Add support for watchdog found on Freescale i.MX SoCs.
+
config WATCHDOG_JZ4740
bool "Ingenic jz4740 SoC hardware watchdog"
depends on MACH_MIPS_XBURST || COMPILE_TEST
@@ -96,7 +103,7 @@ config RAVE_SP_WATCHDOG
config STM32_IWDG_WATCHDOG
bool "STM32 IWDG"
- depends on ARCH_STM32MP || COMPILE_TEST
+ depends on ARCH_STM32 || COMPILE_TEST
select MFD_SYSCON
help
Enable to support configuration of the STM32's on-SoC IWDG watchdog.
@@ -108,6 +115,12 @@ config STPMIC1_WATCHDOG
help
Enable to support configuration of the stpmic1's built-in watchdog.
+config RN568_WATCHDOG
+ bool "Ricoh RN5t568 PMIC based Watchdog"
+ depends on MFD_RN568PMIC
+ help
+ Enable to support system control via the PMIC based watchdog.
+
config F71808E_WDT
bool "Fintek F718xx, F818xx Super I/O Watchdog"
depends on X86
@@ -148,4 +161,20 @@ config STARFIVE_WDT
If you say yes here you get support for the watchdog device
on StarFive SoCs.
+config WDAT_WDT
+ bool "ACPI Watchdog Action Table (WDAT)"
+ depends on X86
+ depends on ACPI
+ help
+ This driver adds support for systems with ACPI Watchdog Action
+ Table (WDAT) table. Servers typically have this but it can be
+ found on some desktop machines as well. This driver will take
+ over the native iTCO watchdog driver found on many Intel CPUs.
+
+config CADENCE_WATCHDOG
+ tristate "Cadence Watchdog Timer"
+ help
+ Say Y here if you want to include support for the watchdog
+ timer in the Xilinx Zynq.
+
endif
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 4e784b5aaa..2b0da7cea9 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_WATCHDOG) += wd_core.o
obj-$(CONFIG_WATCHDOG_AR9344) += ar9344_wdt.o
obj-$(CONFIG_WATCHDOG_AT91SAM9) += at91sam9_wdt.o
@@ -9,13 +10,17 @@ obj-$(CONFIG_WATCHDOG_DW) += dw_wdt.o
obj-$(CONFIG_WATCHDOG_JZ4740) += jz4740.o
obj-$(CONFIG_WATCHDOG_IMX_RESET_SOURCE) += imxwd.o
obj-$(CONFIG_WATCHDOG_IMX) += imxwd.o
+obj-$(CONFIG_WATCHDOG_IMXULP) += imxulp-wdt.o
obj-$(CONFIG_WATCHDOG_KVX) += kvx_wdt.o
obj-$(CONFIG_WATCHDOG_ORION) += orion_wdt.o
obj-$(CONFIG_ARCH_BCM283X) += bcm2835_wdt.o
obj-$(CONFIG_RAVE_SP_WATCHDOG) += rave-sp-wdt.o
obj-$(CONFIG_STM32_IWDG_WATCHDOG) += stm32_iwdg.o
obj-$(CONFIG_STPMIC1_WATCHDOG) += stpmic1_wdt.o
+obj-$(CONFIG_RN568_WATCHDOG) += rn5t568_wdt.o
obj-$(CONFIG_F71808E_WDT) += f71808e_wdt.o
obj-$(CONFIG_GPIO_WATCHDOG) += gpio_wdt.o
obj-$(CONFIG_ITCO_WDT) += itco_wdt.o
obj-$(CONFIG_STARFIVE_WDT) += starfive_wdt.o
+obj-$(CONFIG_WDAT_WDT) += wdat_wdt.o
+obj-$(CONFIG_CADENCE_WATCHDOG) += cadence_wdt.o
diff --git a/drivers/watchdog/ar9344_wdt.c b/drivers/watchdog/ar9344_wdt.c
index c7cd552dc7..50e83fa685 100644
--- a/drivers/watchdog/ar9344_wdt.c
+++ b/drivers/watchdog/ar9344_wdt.c
@@ -1,13 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* AR9344 Watchdog driver
*
* Copyright (C) 2017 Oleksij Rempel <linux@rempel-privat.de>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
*/
#include <common.h>
@@ -34,7 +29,7 @@
struct ar9344_wd {
struct watchdog wd;
void __iomem *base;
- struct device_d *dev;
+ struct device *dev;
unsigned int rate;
};
@@ -70,7 +65,7 @@ static void ar9344_watchdog_detect_reset_source(struct ar9344_wd *priv)
/* else keep the default 'unknown' state */
}
-static int ar9344_wdt_probe(struct device_d *dev)
+static int ar9344_wdt_probe(struct device *dev)
{
struct resource *iores;
struct ar9344_wd *priv;
@@ -129,8 +124,9 @@ static __maybe_unused struct of_device_id ar9344_wdt_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, ar9344_wdt_dt_ids);
-static struct driver_d ar9344_wdt_driver = {
+static struct driver ar9344_wdt_driver = {
.name = "ar9344-wdt",
.probe = ar9344_wdt_probe,
.of_compatible = DRV_OF_COMPAT(ar9344_wdt_dt_ids),
diff --git a/drivers/watchdog/at91sam9_wdt.c b/drivers/watchdog/at91sam9_wdt.c
index fe6f2e0408..46bb986229 100644
--- a/drivers/watchdog/at91sam9_wdt.c
+++ b/drivers/watchdog/at91sam9_wdt.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2019 Pengutronix, Ahmad Fatoum <a.fatoum@pengutronix.de>
*/
@@ -8,7 +8,7 @@
#include <io.h>
#include <watchdog.h>
#include <linux/clk.h>
-#include <mach/at91_wdt.h>
+#include <mach/at91/at91_wdt.h>
#define MIN_WDT_TIMEOUT 1
#define MAX_WDT_TIMEOUT 16
@@ -54,7 +54,7 @@ static inline bool at91sam9x_wdt_is_disabled(struct at91sam9x_wdt *wdt)
return readl(wdt->base + AT91_WDT_MR) & AT91_WDT_WDDIS;
}
-static int at91sam9x_wdt_probe(struct device_d *dev)
+static int at91sam9x_wdt_probe(struct device *dev)
{
struct at91sam9x_wdt *wdt;
struct resource *iores;
@@ -95,8 +95,9 @@ static const __maybe_unused struct of_device_id at91sam9x_wdt_dt_ids[] = {
{ .compatible = "atmel,sama5d4-wdt", },
{ /* sentinel */ },
};
+MODULE_DEVICE_TABLE(of, at91sam9x_wdt_dt_ids);
-static struct driver_d at91sam9x_wdt_driver = {
+static struct driver at91sam9x_wdt_driver = {
.name = "at91sam9x-wdt",
.of_compatible = DRV_OF_COMPAT(at91sam9x_wdt_dt_ids),
.probe = at91sam9x_wdt_probe,
diff --git a/drivers/watchdog/bcm2835_wdt.c b/drivers/watchdog/bcm2835_wdt.c
index 233eaa85c1..874315d502 100644
--- a/drivers/watchdog/bcm2835_wdt.c
+++ b/drivers/watchdog/bcm2835_wdt.c
@@ -1,18 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2017 Pengutronix, Lucas Stach <l.stach@pengutronix.de>
*
* Based on code from Carlo Caione <carlo@carlocaione.org>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
*/
#include <common.h>
#include <init.h>
@@ -20,28 +10,7 @@
#include <restart.h>
#include <watchdog.h>
-#define PM_RSTC 0x1c
-#define PM_RSTS 0x20
-#define PM_WDOG 0x24
-
-#define PM_WDOG_RESET 0000000000
-#define PM_PASSWORD 0x5a000000
-#define PM_WDOG_TIME_SET 0x000fffff
-#define PM_RSTC_WRCFG_CLR 0xffffffcf
-#define PM_RSTC_WRCFG_SET 0x00000030
-#define PM_RSTC_WRCFG_FULL_RESET 0x00000020
-#define PM_RSTC_RESET 0x00000102
-
-#define PM_RSTS_HADPOR_SET 0x00001000
-#define PM_RSTS_HADSRH_SET 0x00000400
-#define PM_RSTS_HADSRF_SET 0x00000200
-#define PM_RSTS_HADSRQ_SET 0x00000100
-#define PM_RSTS_HADWRH_SET 0x00000040
-#define PM_RSTS_HADWRF_SET 0x00000020
-#define PM_RSTS_HADWRQ_SET 0x00000010
-#define PM_RSTS_HADDRH_SET 0x00000004
-#define PM_RSTS_HADDRF_SET 0x00000002
-#define PM_RSTS_HADDRQ_SET 0x00000001
+#include <soc/bcm283x/wdt.h>
#define SECS_TO_WDOG_TICKS(x) ((x) << 16)
@@ -52,7 +21,7 @@
struct bcm2835_wd {
struct watchdog wd;
void __iomem *base;
- struct device_d *dev;
+ struct device *dev;
struct restart_handler restart;
};
@@ -91,7 +60,7 @@ static int bcm2835_wd_set_timeout(struct watchdog *wd, unsigned timeout)
return 0;
}
-static int bcm2835_wd_probe(struct device_d *dev)
+static int bcm2835_wd_probe(struct device *dev)
{
struct resource *iores;
struct bcm2835_wd *priv;
@@ -132,8 +101,9 @@ static __maybe_unused struct of_device_id bcm2835_wd_dt_ids[] = {
/* sentinel */
},
};
+MODULE_DEVICE_TABLE(of, bcm2835_wd_dt_ids);
-static struct driver_d bcm2835_wd_driver = {
+static struct driver bcm2835_wd_driver = {
.name = "bcm2835_wd",
.of_compatible = DRV_OF_COMPAT(bcm2835_wd_dt_ids),
.probe = bcm2835_wd_probe,
diff --git a/drivers/watchdog/cadence_wdt.c b/drivers/watchdog/cadence_wdt.c
new file mode 100644
index 0000000000..17655a188c
--- /dev/null
+++ b/drivers/watchdog/cadence_wdt.c
@@ -0,0 +1,278 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Cadence WDT driver - Used by Xilinx Zynq
+ *
+ * Copyright (C) 2010 - 2014 Xilinx, Inc.
+ *
+ */
+
+#include <common.h>
+#include <init.h>
+#include <io.h>
+#include <of.h>
+#include <restart.h>
+#include <watchdog.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/reset.h>
+
+/* Supports 1 - 516 sec */
+#define CDNS_WDT_MAX_TIMEOUT 516
+
+/* Restart key */
+#define CDNS_WDT_RESTART_KEY 0x00001999
+
+/* Counter register access key */
+#define CDNS_WDT_REGISTER_ACCESS_KEY 0x00920000
+
+/* Counter value divisor */
+#define CDNS_WDT_COUNTER_VALUE_DIVISOR 0x1000
+
+/* Clock prescaler value and selection */
+#define CDNS_WDT_PRESCALE_64 64
+#define CDNS_WDT_PRESCALE_512 512
+#define CDNS_WDT_PRESCALE_4096 4096
+#define CDNS_WDT_PRESCALE_SELECT_64 1
+#define CDNS_WDT_PRESCALE_SELECT_512 2
+#define CDNS_WDT_PRESCALE_SELECT_4096 3
+
+/* Input clock frequency */
+#define CDNS_WDT_CLK_10MHZ 10000000
+#define CDNS_WDT_CLK_75MHZ 75000000
+
+/* Counter maximum value */
+#define CDNS_WDT_COUNTER_MAX 0xFFF
+
+/**
+ * struct cdns_wdt - Watchdog device structure
+ * @regs: baseaddress of device
+ * @clk: struct clk * of a clock source
+ * @prescaler: for saving prescaler value
+ * @ctrl_clksel: counter clock prescaler selection
+ * @cdns_wdt_device: watchdog device structure
+ *
+ * Structure containing parameters specific to cadence watchdog.
+ */
+struct cdns_wdt {
+ void __iomem *regs;
+ struct clk *clk;
+ u32 prescaler;
+ u32 ctrl_clksel;
+ struct watchdog cdns_wdt_device;
+ unsigned timeout;
+};
+
+static inline struct cdns_wdt *to_cdns_wdt(struct watchdog *wdd)
+{
+ return container_of(wdd, struct cdns_wdt, cdns_wdt_device);
+}
+
+/* Write access to Registers */
+static inline void cdns_wdt_writereg(struct cdns_wdt *wdt, u32 offset, u32 val)
+{
+ writel_relaxed(val, wdt->regs + offset);
+}
+
+/*************************Register Map**************************************/
+
+/* Register Offsets for the WDT */
+#define CDNS_WDT_ZMR_OFFSET 0x0 /* Zero Mode Register */
+#define CDNS_WDT_CCR_OFFSET 0x4 /* Counter Control Register */
+#define CDNS_WDT_RESTART_OFFSET 0x8 /* Restart Register */
+#define CDNS_WDT_SR_OFFSET 0xC /* Status Register */
+
+/*
+ * Zero Mode Register - This register controls how the time out is indicated
+ * and also contains the access code to allow writes to the register (0xABC).
+ */
+#define CDNS_WDT_ZMR_WDEN_MASK 0x00000001 /* Enable the WDT */
+#define CDNS_WDT_ZMR_RSTEN_MASK 0x00000002 /* Enable the reset output */
+#define CDNS_WDT_ZMR_IRQEN_MASK 0x00000004 /* Enable IRQ output */
+#define CDNS_WDT_ZMR_RSTLEN_16 0x00000030 /* Reset pulse of 16 pclk cycles */
+#define CDNS_WDT_ZMR_ZKEY_VAL 0x00ABC000 /* Access key, 0xABC << 12 */
+/*
+ * Counter Control register - This register controls how fast the timer runs
+ * and the reset value and also contains the access code to allow writes to
+ * the register.
+ */
+#define CDNS_WDT_CCR_CRV_MASK 0x00003FFC /* Counter reset value */
+
+/**
+ * cdns_wdt_stop - Stop the watchdog.
+ *
+ * @wdt: cadence watchdog device
+ *
+ * Read the contents of the ZMR register, clear the WDEN bit
+ * in the register and set the access key for successful write.
+ */
+static void cdns_wdt_stop(struct cdns_wdt *wdt)
+{
+ cdns_wdt_writereg(wdt, CDNS_WDT_ZMR_OFFSET,
+ CDNS_WDT_ZMR_ZKEY_VAL & (~CDNS_WDT_ZMR_WDEN_MASK));
+}
+
+/**
+ * cdns_wdt_reload - Reload the watchdog timer (i.e. pat the watchdog).
+ *
+ * @wdt: cadence watchdog device
+ *
+ * Write the restart key value (0x00001999) to the restart register.
+ */
+static void cdns_wdt_reload(struct cdns_wdt *wdt)
+{
+ cdns_wdt_writereg(wdt, CDNS_WDT_RESTART_OFFSET,
+ CDNS_WDT_RESTART_KEY);
+}
+
+/**
+ * cdns_wdt_start - Enable and start the watchdog.
+ *
+ * @wdt: cadence watchdog device
+ * @timeout: new timeout
+ *
+ * The counter value is calculated according to the formula:
+ * calculated count = (timeout * clock) / prescaler + 1.
+ * The calculated count is divided by 0x1000 to obtain the field value
+ * to write to counter control register.
+ * Clears the contents of prescaler and counter reset value. Sets the
+ * prescaler to 4096 and the calculated count and access key
+ * to write to CCR Register.
+ * Sets the WDT (WDEN bit) and either the Reset signal(RSTEN bit)
+ * or Interrupt signal(IRQEN) with a specified cycles and the access
+ * key to write to ZMR Register.
+ */
+static void cdns_wdt_start(struct cdns_wdt *wdt, unsigned timeout)
+{
+ unsigned int data = 0;
+ unsigned short count;
+ unsigned long clock_f = clk_get_rate(wdt->clk);
+
+ /*
+ * Counter value divisor to obtain the value of
+ * counter reset to be written to control register.
+ */
+ count = (timeout * (clock_f / wdt->prescaler)) /
+ CDNS_WDT_COUNTER_VALUE_DIVISOR + 1;
+
+ if (count > CDNS_WDT_COUNTER_MAX)
+ count = CDNS_WDT_COUNTER_MAX;
+
+ cdns_wdt_writereg(wdt, CDNS_WDT_ZMR_OFFSET,
+ CDNS_WDT_ZMR_ZKEY_VAL);
+
+ count = (count << 2) & CDNS_WDT_CCR_CRV_MASK;
+
+ /* Write counter access key first to be able write to register */
+ data = count | CDNS_WDT_REGISTER_ACCESS_KEY | wdt->ctrl_clksel;
+ cdns_wdt_writereg(wdt, CDNS_WDT_CCR_OFFSET, data);
+ data = CDNS_WDT_ZMR_WDEN_MASK | CDNS_WDT_ZMR_RSTLEN_16 |
+ CDNS_WDT_ZMR_ZKEY_VAL;
+
+ /* Reset on timeout regardless of what's specified in device tree. */
+ data |= CDNS_WDT_ZMR_RSTEN_MASK;
+ data &= ~CDNS_WDT_ZMR_IRQEN_MASK;
+
+ cdns_wdt_writereg(wdt, CDNS_WDT_ZMR_OFFSET, data);
+ cdns_wdt_writereg(wdt, CDNS_WDT_RESTART_OFFSET,
+ CDNS_WDT_RESTART_KEY);
+}
+
+/**
+ * cdns_wdt_settimeout - Set a new timeout value for the watchdog device.
+ *
+ * @wdd: watchdog device
+ * @new_time: new timeout value that needs to be set
+ * Return: 0 on success
+ *
+ * Update the watchdog device timeout with new value which is used when
+ * cdns_wdt_start is called.
+ */
+static int cdns_wdt_settimeout(struct watchdog *wdd,
+ unsigned int new_time)
+{
+ struct cdns_wdt *wdt = to_cdns_wdt(wdd);
+
+ if (new_time > wdd->timeout_max)
+ return -EINVAL;
+
+ if (new_time == 0) {
+ cdns_wdt_stop(wdt);
+ } else if (wdt->timeout != new_time) {
+ cdns_wdt_start(wdt, new_time);
+ wdt->timeout = new_time;
+ } else {
+ cdns_wdt_reload(wdt);
+ }
+
+ return 0;
+}
+
+/************************Platform Operations*****************************/
+/**
+ * cdns_wdt_probe - Probe call for the device.
+ *
+ * @pdev: handle to the platform device structure.
+ * Return: 0 on success, negative error otherwise.
+ *
+ * It does all the memory allocation and registration for the device.
+ */
+static int cdns_wdt_probe(struct device *dev)
+{
+ unsigned long clock_f;
+ struct cdns_wdt *wdt;
+ struct resource *res;
+ struct watchdog *cdns_wdt_device;
+
+ wdt = xzalloc(sizeof(*wdt));
+
+ cdns_wdt_device = &wdt->cdns_wdt_device;
+ cdns_wdt_device->name = "cdns_wdt";
+ cdns_wdt_device->hwdev = dev;
+ cdns_wdt_device->set_timeout = cdns_wdt_settimeout;
+ cdns_wdt_device->timeout_max = CDNS_WDT_MAX_TIMEOUT;
+
+ res = dev_request_mem_resource(dev, 0);
+ if (IS_ERR(res))
+ return PTR_ERR(res);
+
+ wdt->regs = IOMEM(res->start);
+
+ /* We don't service interrupts in barebox, so a watchdog that doesn't
+ * reset the system isn't a useful thing to register
+ */
+ if (!of_property_read_bool(dev->of_node, "reset-on-timeout"))
+ dev_notice(dev, "proceeding as if reset-on-timeout was set\n");
+
+ wdt->clk = clk_get_enabled(dev, NULL);
+ if (IS_ERR(wdt->clk))
+ return dev_err_probe(dev, PTR_ERR(wdt->clk),
+ "input clock not found\n");
+
+ clock_f = clk_get_rate(wdt->clk);
+ if (clock_f <= CDNS_WDT_CLK_75MHZ) {
+ wdt->prescaler = CDNS_WDT_PRESCALE_512;
+ wdt->ctrl_clksel = CDNS_WDT_PRESCALE_SELECT_512;
+ } else {
+ wdt->prescaler = CDNS_WDT_PRESCALE_4096;
+ wdt->ctrl_clksel = CDNS_WDT_PRESCALE_SELECT_4096;
+ }
+
+ return watchdog_register(cdns_wdt_device);
+}
+
+static const struct of_device_id cdns_wdt_of_match[] = {
+ { .compatible = "cdns,wdt-r1p2", },
+ { /* end of table */ }
+};
+MODULE_DEVICE_TABLE(of, cdns_wdt_of_match);
+
+static struct driver cdns_wdt_driver = {
+ .name = "cdns-wdt",
+ .probe = cdns_wdt_probe,
+ .of_match_table = cdns_wdt_of_match,
+};
+device_platform_driver(cdns_wdt_driver);
+
+MODULE_AUTHOR("Xilinx, Inc.");
+MODULE_DESCRIPTION("Watchdog driver for Cadence WDT");
+MODULE_LICENSE("GPL");
diff --git a/drivers/watchdog/davinci_wdt.c b/drivers/watchdog/davinci_wdt.c
index 2ac5f8b38d..0b2df50c48 100644
--- a/drivers/watchdog/davinci_wdt.c
+++ b/drivers/watchdog/davinci_wdt.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* drivers/char/watchdog/davinci_wdt.c
*
@@ -6,10 +7,7 @@
* Copyright (C) 2006-2013 Texas Instruments.
* Copyright (C) 2015 Jan Luebbe <jluebbe@debian.org>
*
- * 2007 (c) MontaVista Software, Inc. This file is licensed under
- * the terms of the GNU General Public License version 2. This program
- * is licensed "as is" without any warranty of any kind, whether express
- * or implied.
+ * 2007 (c) MontaVista Software, Inc.
*/
#include <common.h>
@@ -129,7 +127,7 @@ static int davinci_wdt_set_timeout(struct watchdog *wd, unsigned timeout)
return 0;
}
-static int davinci_wdt_probe(struct device_d *dev)
+static int davinci_wdt_probe(struct device *dev)
{
struct resource *iores;
int ret = 0;
@@ -162,8 +160,9 @@ static __maybe_unused struct of_device_id davinci_wdt_of_match[] = {
{ .compatible = "ti,davinci-wdt", },
{},
};
+MODULE_DEVICE_TABLE(of, davinci_wdt_of_match);
-static struct driver_d platform_wdt_driver = {
+static struct driver platform_wdt_driver = {
.name = "davinci-wdt",
.of_compatible = DRV_OF_COMPAT(davinci_wdt_of_match),
.probe = davinci_wdt_probe,
diff --git a/drivers/watchdog/dw_wdt.c b/drivers/watchdog/dw_wdt.c
index 774d211a5f..178e0a29f1 100644
--- a/drivers/watchdog/dw_wdt.c
+++ b/drivers/watchdog/dw_wdt.c
@@ -1,12 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright 2010-2011 Picochip Ltd., Jamie Iles
* http://www.picochip.com
*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- *
* This file implements a driver for the Synopsys DesignWare watchdog device
* in the many subsystems. The watchdog has 16 different timeout periods
* and these are a function of the input clock frequency.
@@ -132,7 +128,7 @@ static void __noreturn dw_wdt_restart_handle(struct restart_handler *rst)
hang();
}
-static int dw_wdt_drv_probe(struct device_d *dev)
+static int dw_wdt_drv_probe(struct device *dev)
{
struct watchdog *wdd;
struct dw_wdt *dw_wdt;
@@ -200,8 +196,9 @@ static struct of_device_id dw_wdt_of_match[] = {
{ .compatible = "snps,dw-wdt", },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, dw_wdt_of_match);
-static struct driver_d dw_wdt_driver = {
+static struct driver dw_wdt_driver = {
.name = "dw-wdt",
.probe = dw_wdt_drv_probe,
.of_compatible = DRV_OF_COMPAT(dw_wdt_of_match),
diff --git a/drivers/watchdog/efi_wdt.c b/drivers/watchdog/efi_wdt.c
index 1512b1d99a..072240fcaf 100644
--- a/drivers/watchdog/efi_wdt.c
+++ b/drivers/watchdog/efi_wdt.c
@@ -7,12 +7,12 @@
#include <init.h>
#include <driver.h>
#include <efi.h>
-#include <efi/efi.h>
+#include <efi/efi-payload.h>
#include <watchdog.h>
struct efi_wdt_priv {
struct watchdog wd;
- struct device_d *dev;
+ struct device *dev;
};
#define to_efi_wdt(h) container_of(h, struct efi_wdt_priv, wd)
@@ -31,7 +31,7 @@ static int efi_wdt_set_timeout(struct watchdog *wd, unsigned timeout)
return 0;
}
-static int efi_wdt_probe(struct device_d *dev)
+static int efi_wdt_probe(struct device *dev)
{
struct efi_wdt_priv *priv;
int ret;
@@ -58,7 +58,7 @@ on_error:
return ret;
}
-static struct driver_d efi_wdt_driver = {
+static struct driver efi_wdt_driver = {
.name = "efi-wdt",
.probe = efi_wdt_probe,
};
diff --git a/drivers/watchdog/f71808e_wdt.c b/drivers/watchdog/f71808e_wdt.c
index 925c2f809d..5bee066366 100644
--- a/drivers/watchdog/f71808e_wdt.c
+++ b/drivers/watchdog/f71808e_wdt.c
@@ -13,7 +13,7 @@
#include <asm/io.h>
#include <driver.h>
#include <watchdog.h>
-#include <printk.h>
+#include <linux/printk.h>
#include <reset_source.h>
#include <superio.h>
#include <common.h>
@@ -239,7 +239,7 @@ static int f71808e_wdt_set_timeout(struct watchdog *wdd, unsigned int new_timeou
return 0;
}
-static int f71808e_wdt_init(struct f71808e_wdt *wd, struct device_d *dev)
+static int f71808e_wdt_init(struct f71808e_wdt *wd, struct device *dev)
{
struct watchdog *wdd = &wd->wdd;
const char * const *names = pulse_width_names;
@@ -376,7 +376,7 @@ static struct platform_device_id f71808e_wdt_ids[] = {
{ /* sentinel */ },
};
-static int f71808e_probe(struct device_d *dev)
+static int f71808e_probe(struct device *dev)
{
struct f71808e_wdt *wd;
struct resource *res;
@@ -396,7 +396,7 @@ static int f71808e_probe(struct device_d *dev)
return f71808e_wdt_init(wd, dev);
}
-static struct driver_d f71808e_wdt_driver = {
+static struct driver f71808e_wdt_driver = {
.probe = f71808e_probe,
.name = "f71808e_wdt",
.id_table = f71808e_wdt_ids,
diff --git a/drivers/watchdog/gpio_wdt.c b/drivers/watchdog/gpio_wdt.c
index 4de49dcee3..5fa98f87c6 100644
--- a/drivers/watchdog/gpio_wdt.c
+++ b/drivers/watchdog/gpio_wdt.c
@@ -9,7 +9,7 @@
#include <driver.h>
#include <watchdog.h>
#include <superio.h>
-#include <gpiod.h>
+#include <linux/gpio/consumer.h>
enum {
HW_ALGO_TOGGLE,
@@ -17,7 +17,7 @@ enum {
};
struct gpio_wdt_priv {
- int gpio;
+ struct gpio_desc *gpiod;
bool state;
bool started;
unsigned int hw_algo;
@@ -32,11 +32,11 @@ static inline struct gpio_wdt_priv *to_gpio_wdt_priv(struct watchdog *wdd)
static void gpio_wdt_disable(struct gpio_wdt_priv *priv)
{
/* Eternal ping */
- gpio_set_active(priv->gpio, 1);
+ gpiod_set_value(priv->gpiod, 1);
/* Put GPIO back to tristate */
if (priv->hw_algo == HW_ALGO_TOGGLE)
- gpio_direction_input(priv->gpio);
+ gpiod_direction_input(priv->gpiod);
priv->started = false;
}
@@ -47,13 +47,13 @@ static void gpio_wdt_ping(struct gpio_wdt_priv *priv)
case HW_ALGO_TOGGLE:
/* Toggle output pin */
priv->state = !priv->state;
- gpio_set_active(priv->gpio, priv->state);
+ gpiod_set_value(priv->gpiod, priv->state);
break;
case HW_ALGO_LEVEL:
/* Pulse */
- gpio_set_active(priv->gpio, true);
+ gpiod_set_value(priv->gpiod, true);
udelay(1);
- gpio_set_active(priv->gpio, false);
+ gpiod_set_value(priv->gpiod, false);
break;
}
}
@@ -61,7 +61,7 @@ static void gpio_wdt_ping(struct gpio_wdt_priv *priv)
static void gpio_wdt_start(struct gpio_wdt_priv *priv)
{
priv->state = false;
- gpio_direction_active(priv->gpio, priv->state);
+ gpiod_direction_output(priv->gpiod, priv->state);
priv->started = true;
}
@@ -81,9 +81,9 @@ static int gpio_wdt_set_timeout(struct watchdog *wdd, unsigned int new_timeout)
return 0;
}
-static int gpio_wdt_probe(struct device_d *dev)
+static int gpio_wdt_probe(struct device *dev)
{
- struct device_node *np = dev->device_node;
+ struct device_node *np = dev->of_node;
struct gpio_wdt_priv *priv;
enum gpiod_flags gflags;
unsigned int hw_margin;
@@ -113,9 +113,9 @@ static int gpio_wdt_probe(struct device_d *dev)
return -EINVAL;
}
- priv->gpio = gpiod_get(dev, NULL, gflags);
- if (priv->gpio < 0)
- return priv->gpio;
+ priv->gpiod = gpiod_get(dev, NULL, gflags);
+ if (IS_ERR(priv->gpiod))
+ return PTR_ERR(priv->gpiod);
priv->wdd.hwdev = dev;
priv->wdd.timeout_max = hw_margin / 1000;
@@ -129,8 +129,9 @@ static const struct of_device_id gpio_wdt_dt_ids[] = {
{ .compatible = "linux,wdt-gpio", },
{ }
};
+MODULE_DEVICE_TABLE(of, gpio_wdt_dt_ids);
-static struct driver_d gpio_wdt_driver = {
+static struct driver gpio_wdt_driver = {
.name = "gpio-wdt",
.of_compatible = gpio_wdt_dt_ids,
.probe = gpio_wdt_probe,
diff --git a/drivers/watchdog/im28wd.c b/drivers/watchdog/im28wd.c
index 6ebd97e5f5..b52e585175 100644
--- a/drivers/watchdog/im28wd.c
+++ b/drivers/watchdog/im28wd.c
@@ -1,16 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* (c) 2012 Juergen Beisert <kernel@pengutronix.de>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
* Note: this driver works for the i.MX28 SoC. It might work for the
* i.MX23 Soc as well, but is not tested yet.
*/
@@ -184,7 +175,7 @@ static void __maybe_unused imx28_detect_reset_source(const struct imx28_wd *p)
reset_source_set(RESET_RST);
}
-static int imx28_wd_probe(struct device_d *dev)
+static int imx28_wd_probe(struct device *dev)
{
struct resource *iores;
struct imx28_wd *priv;
@@ -196,7 +187,7 @@ static int imx28_wd_probe(struct device_d *dev)
return PTR_ERR(iores);
priv->regs = IOMEM(iores->start);
priv->wd.set_timeout = imx28_watchdog_set_timeout;
- priv->wd.timeout_max = ULONG_MAX / WDOG_TICK_RATE;
+ priv->wd.timeout_max = U32_MAX / WDOG_TICK_RATE;
priv->wd.hwdev = dev;
if (!(readl(priv->regs + MXS_RTC_STAT) & MXS_RTC_STAT_WD_PRESENT)) {
@@ -222,7 +213,7 @@ on_error:
return rc;
}
-static void imx28_wd_remove(struct device_d *dev)
+static void imx28_wd_remove(struct device *dev)
{
struct imx28_wd *priv= dev->priv;
watchdog_deregister(&priv->wd);
@@ -236,8 +227,9 @@ static __maybe_unused struct of_device_id imx28_wdt_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, imx28_wdt_dt_ids);
-static struct driver_d imx28_wd_driver = {
+static struct driver imx28_wd_driver = {
.name = "im28wd",
.probe = imx28_wd_probe,
.remove = imx28_wd_remove,
diff --git a/drivers/watchdog/imxulp-wdt.c b/drivers/watchdog/imxulp-wdt.c
new file mode 100644
index 0000000000..5a89876175
--- /dev/null
+++ b/drivers/watchdog/imxulp-wdt.c
@@ -0,0 +1,168 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2016 Freescale Semiconductor, Inc.
+ */
+
+#include <common.h>
+#include <init.h>
+#include <io.h>
+#include <of.h>
+#include <errno.h>
+#include <malloc.h>
+#include <restart.h>
+#include <watchdog.h>
+#include <reset_source.h>
+#include <linux/clk.h>
+#include <asm/system.h>
+
+struct imxulp_socdata {
+ bool prescaler_enable;
+ unsigned int rate;
+};
+
+struct imxulp_wd {
+ struct watchdog wd;
+ void __iomem *base;
+ bool prescaler_enable;
+ struct device *dev;
+ const struct imxulp_socdata *socdata;
+};
+
+#define REFRESH_WORD0 0xA602 /* 1st refresh word */
+#define REFRESH_WORD1 0xB480 /* 2nd refresh word */
+
+#define UNLOCK_WORD0 0xC520 /* 1st unlock word */
+#define UNLOCK_WORD1 0xD928 /* 2nd unlock word */
+
+#define UNLOCK_WORD 0xD928C520 /* unlock word */
+#define REFRESH_WORD 0xB480A602 /* refresh word */
+
+#define WDOG_CS 0x0
+#define WDOG_CS_UPDATE BIT(5)
+#define WDOG_CS_EN BIT(7)
+#define WDOG_CS_RCS BIT(10)
+#define WDOG_CS_ULK BIT(11)
+#define WDOG_CS_PRES BIT(12)
+#define WDOG_CS_CMD32EN BIT(13)
+#define WDOG_CS_FLG BIT(14)
+#define WDOG_CS_INT BIT(6)
+#define WDOG_CS_LPO_CLK (0x1 << 8)
+
+#define WDOG_CNT 0x4
+#define WDOG_TOVAL 0x8
+
+#define CLK_RATE_1KHZ 1000
+#define CLK_RATE_32KHZ 128
+
+static int imxulp_watchdog_set_timeout(struct watchdog *wd, unsigned int timeout)
+{
+ struct imxulp_wd *imxwd = container_of(wd, struct imxulp_wd, wd);
+ u32 cmd32 = 0;
+
+ if (timeout == 0)
+ return -ENOSYS;
+
+ if (readl(imxwd->base + WDOG_CS) & WDOG_CS_CMD32EN) {
+ writel(UNLOCK_WORD, imxwd->base + WDOG_CNT);
+ cmd32 = WDOG_CS_CMD32EN;
+ } else {
+ writel(UNLOCK_WORD0, imxwd->base + WDOG_CNT);
+ writel(UNLOCK_WORD1, imxwd->base + WDOG_CNT);
+ }
+
+ /* Wait WDOG Unlock */
+ while (!(readl(imxwd->base + WDOG_CS) & WDOG_CS_ULK))
+ ;
+
+ writel(timeout * imxwd->socdata->rate, imxwd->base + WDOG_TOVAL);
+
+ if (imxwd->socdata->prescaler_enable)
+ cmd32 |= WDOG_CS_PRES;
+
+ writel(cmd32 | WDOG_CS_EN | WDOG_CS_UPDATE | WDOG_CS_LPO_CLK |
+ WDOG_CS_FLG | WDOG_CS_INT, imxwd->base + WDOG_CS);
+
+ /* Wait WDOG reconfiguration */
+ while (!(readl(imxwd->base + WDOG_CS) & WDOG_CS_RCS))
+ ;
+
+ if (readl(imxwd->base + WDOG_CS) & WDOG_CS_CMD32EN) {
+ writel(REFRESH_WORD, imxwd->base + WDOG_CNT);
+ } else {
+ writel(REFRESH_WORD0, imxwd->base + WDOG_CNT);
+ writel(REFRESH_WORD1, imxwd->base + WDOG_CNT);
+ }
+
+ return 0;
+}
+
+static enum wdog_hw_runnning imxulp_wd_running(struct imxulp_wd *imxwd)
+{
+ if (readl(imxwd->base + WDOG_CS) & WDOG_CS_EN)
+ return WDOG_HW_RUNNING;
+ else
+ return WDOG_HW_NOT_RUNNING;
+}
+
+static int imxulp_wd_probe(struct device *dev)
+{
+ struct imxulp_wd *imxwd;
+ struct resource *iores;
+ struct imxulp_socdata *socdata;
+ int ret;
+
+ ret = dev_get_drvdata(dev, (const void **)&socdata);
+ if (ret)
+ return ret;
+
+ imxwd = xzalloc(sizeof(*imxwd));
+ iores = dev_request_mem_resource(dev, 0);
+ if (IS_ERR(iores))
+ return dev_err_probe(dev, PTR_ERR(iores), "could not get memory region\n");
+
+ imxwd->socdata = socdata;
+ imxwd->base = IOMEM(iores->start);
+ imxwd->wd.set_timeout = imxulp_watchdog_set_timeout;
+ imxwd->wd.timeout_max = 0xffff / imxwd->socdata->rate;
+ imxwd->wd.hwdev = dev;
+ imxwd->wd.running = imxulp_wd_running(imxwd);
+
+ ret = watchdog_register(&imxwd->wd);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to register watchdog device\n");
+
+ return 0;
+}
+
+static struct imxulp_socdata imx7ulp_wd_socdata = {
+ .prescaler_enable = false,
+ .rate = CLK_RATE_1KHZ,
+};
+
+static struct imxulp_socdata imx93_wd_socdata = {
+ .prescaler_enable = true,
+ .rate = CLK_RATE_32KHZ,
+};
+
+static __maybe_unused struct of_device_id imxulp_wdt_dt_ids[] = {
+ {
+ .compatible = "fsl,imx7ulp-wdt",
+ .data = &imx7ulp_wd_socdata,
+ }, {
+ .compatible = "fsl,imx8ulp-wdt",
+ .data = &imx7ulp_wd_socdata,
+ }, {
+ .compatible = "fsl,imx93-wdt",
+ .data = &imx93_wd_socdata,
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(of, imx_wdt_dt_ids);
+
+static struct driver imxulp_wd_driver = {
+ .name = "imxulp-watchdog",
+ .probe = imxulp_wd_probe,
+ .of_compatible = DRV_OF_COMPAT(imxulp_wdt_dt_ids),
+};
+device_platform_driver(imxulp_wd_driver);
diff --git a/drivers/watchdog/imxwd.c b/drivers/watchdog/imxwd.c
index a109f6fee7..8f4de5a994 100644
--- a/drivers/watchdog/imxwd.c
+++ b/drivers/watchdog/imxwd.c
@@ -1,15 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* (c) 2012 Sascha Hauer <s.hauer@pengutronix.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <common.h>
@@ -36,7 +27,7 @@ struct imx_wd_ops {
struct imx_wd {
struct watchdog wd;
void __iomem *base;
- struct device_d *dev;
+ struct device *dev;
const struct imx_wd_ops *ops;
struct restart_handler restart;
struct restart_handler restart_warm;
@@ -56,6 +47,7 @@ struct imx_wd {
#define IMX21_WDOG_WSR 0x02 /* Watchdog Service Register */
#define IMX21_WDOG_WSTR 0x04 /* Watchdog Status Register */
#define IMX21_WDOG_WMCR 0x08 /* Misc Register */
+#define IMX21_WDOG_WCR_WDZST (1 << 0)
#define IMX21_WDOG_WCR_WDE (1 << 2)
#define IMX21_WDOG_WCR_WDT (1 << 3)
#define IMX21_WDOG_WCR_SRS (1 << 4)
@@ -134,6 +126,9 @@ static int imx21_watchdog_set_timeout(struct imx_wd *priv, unsigned timeout)
if (priv->ext_reset)
val |= IMX21_WDOG_WCR_WDT;
+ /* Suspend timer in low power mode */
+ val |= IMX21_WDOG_WCR_WDZST;
+
/*
* set time and some write once bits first prior enabling the
* watchdog according to the datasheet
@@ -218,7 +213,7 @@ static void imx_watchdog_detect_reset_source(struct imx_wd *priv)
/* else keep the default 'unknown' state */
}
-static int imx21_wd_init(struct imx_wd *priv)
+static int imx21_wd_init_no_warm_reset(struct imx_wd *priv)
{
imx_watchdog_detect_reset_source(priv);
@@ -230,7 +225,19 @@ static int imx21_wd_init(struct imx_wd *priv)
return 0;
}
-static int imx_wd_probe(struct device_d *dev)
+static int imx21_wd_init(struct imx_wd *priv)
+{
+ priv->restart_warm.name = "imxwd-warm";
+ priv->restart_warm.restart = imxwd_force_soc_reset_internal;
+ priv->restart_warm.priority = RESTART_DEFAULT_PRIORITY - 10;
+ priv->restart_warm.flags = RESTART_FLAG_WARM_BOOTROM;
+
+ restart_handler_register(&priv->restart_warm);
+
+ return imx21_wd_init_no_warm_reset(priv);
+}
+
+static int imx_wd_probe(struct device *dev)
{
struct resource *iores;
struct imx_wd *priv;
@@ -244,18 +251,17 @@ static int imx_wd_probe(struct device_d *dev)
priv = xzalloc(sizeof(struct imx_wd));
iores = dev_request_mem_resource(dev, 0);
- if (IS_ERR(iores)) {
- dev_err(dev, "could not get memory region\n");
- return PTR_ERR(iores);
- }
+ if (IS_ERR(iores))
+ return dev_err_probe(dev, PTR_ERR(iores),
+ "could not get memory region\n");
clk = clk_get(dev, NULL);
if (IS_ERR(clk))
- return PTR_ERR(clk);
+ return dev_err_probe(dev, PTR_ERR(clk), "Failed to get clk\n");
ret = clk_enable(clk);
if (ret)
- return ret;
+ return dev_err_probe(dev, ret, "Failed to enable clk\n");
priv->base = IOMEM(iores->start);
priv->ops = ops;
@@ -263,9 +269,9 @@ static int imx_wd_probe(struct device_d *dev)
priv->wd.timeout_max = priv->ops->timeout_max;
priv->wd.hwdev = dev;
priv->dev = dev;
- priv->bigendian = of_device_is_big_endian(dev->device_node);
+ priv->bigendian = of_device_is_big_endian(dev->of_node);
- priv->ext_reset = of_property_read_bool(dev->device_node,
+ priv->ext_reset = of_property_read_bool(dev->of_node,
"fsl,ext-reset-output");
if (IS_ENABLED(CONFIG_WATCHDOG_IMX)) {
@@ -277,14 +283,17 @@ static int imx_wd_probe(struct device_d *dev)
}
ret = watchdog_register(&priv->wd);
- if (ret)
+ if (ret) {
+ dev_err_probe(dev, ret, "Failed to register watchdog device\n");
goto on_error;
+ }
}
if (priv->ops->init) {
ret = priv->ops->init(priv);
if (ret) {
- dev_err(dev, "Failed to init watchdog device %d\n", ret);
+ dev_err_probe(dev, ret,
+ "Failed to init watchdog device\n");
goto error_unregister;
}
}
@@ -297,12 +306,6 @@ static int imx_wd_probe(struct device_d *dev)
restart_handler_register(&priv->restart);
- priv->restart_warm.name = "imxwd-warm";
- priv->restart_warm.restart = imxwd_force_soc_reset_internal;
- priv->restart_warm.priority = RESTART_DEFAULT_PRIORITY - 10;
-
- restart_handler_register(&priv->restart_warm);
-
return 0;
error_unregister:
@@ -313,6 +316,14 @@ on_error:
return ret;
}
+static const struct imx_wd_ops imx7d_wd_ops = {
+ .set_timeout = imx21_watchdog_set_timeout,
+ .soc_reset = imx21_soc_reset,
+ .init = imx21_wd_init_no_warm_reset,
+ .is_running = imx21_watchdog_is_running,
+ .timeout_max = 128,
+};
+
static const struct imx_wd_ops imx21_wd_ops = {
.set_timeout = imx21_watchdog_set_timeout,
.soc_reset = imx21_soc_reset,
@@ -335,9 +346,13 @@ static __maybe_unused struct of_device_id imx_wdt_dt_ids[] = {
.compatible = "fsl,imx21-wdt",
.data = &imx21_wd_ops,
}, {
+ .compatible = "fsl,imx7d-wdt",
+ .data = &imx7d_wd_ops,
+ }, {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, imx_wdt_dt_ids);
static struct platform_device_id imx_wdt_ids[] = {
{
@@ -351,7 +366,7 @@ static struct platform_device_id imx_wdt_ids[] = {
},
};
-static struct driver_d imx_wd_driver = {
+static struct driver imx_wd_driver = {
.name = "imx-watchdog",
.probe = imx_wd_probe,
.of_compatible = DRV_OF_COMPAT(imx_wdt_dt_ids),
diff --git a/drivers/watchdog/itco_wdt.c b/drivers/watchdog/itco_wdt.c
index e7bd0fc99b..ca012c92dc 100644
--- a/drivers/watchdog/itco_wdt.c
+++ b/drivers/watchdog/itco_wdt.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+// SPDX-License-Identifier: GPL-2.0-only
/*
* EFI Boot Guard, iTCO support (Version 3 and later)
*
diff --git a/drivers/watchdog/jz4740.c b/drivers/watchdog/jz4740.c
index f28bb9177a..8b4b985cd4 100644
--- a/drivers/watchdog/jz4740.c
+++ b/drivers/watchdog/jz4740.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* JZ4740 Watchdog driver
*
@@ -6,12 +7,6 @@
* Based on jz4740_wdt.c from linux-3.15.
*
* Copyright (C) 2010, Paul Cercueil <paul@crapouillou.net>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
*/
#include <common.h>
@@ -65,7 +60,7 @@ static void __noreturn jz4740_reset_soc(struct restart_handler *rst)
hang();
}
-static int jz4740_wdt_probe(struct device_d *dev)
+static int jz4740_wdt_probe(struct device *dev)
{
struct resource *iores;
struct jz4740_wdt_drvdata *priv;
@@ -94,8 +89,9 @@ static __maybe_unused struct of_device_id jz4740_wdt_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, jz4740_wdt_dt_ids);
-static struct driver_d jz4740_wdt_driver = {
+static struct driver jz4740_wdt_driver = {
.name = "jz4740-wdt",
.probe = jz4740_wdt_probe,
.of_compatible = DRV_OF_COMPAT(jz4740_wdt_dt_ids),
diff --git a/drivers/watchdog/kvx_wdt.c b/drivers/watchdog/kvx_wdt.c
index da19136fda..be6b08b71c 100644
--- a/drivers/watchdog/kvx_wdt.c
+++ b/drivers/watchdog/kvx_wdt.c
@@ -48,7 +48,7 @@ static int kvx_wdt_set_timeout(struct watchdog *wdd, unsigned int timeout)
static int count;
-static int kvx_wdt_drv_probe(struct device_d *dev)
+static int kvx_wdt_drv_probe(struct device *dev)
{
struct watchdog *wdd;
struct clk *clk;
@@ -73,11 +73,9 @@ static int kvx_wdt_drv_probe(struct device_d *dev)
wdd->hwdev = dev;
wdd->set_timeout = kvx_wdt_set_timeout;
- /* Be sure that interrupt are disable */
+ /* Be sure that interrupt are disabled */
kvx_sfr_set_field(TCR, WIE, 0);
- kvx_watchdog_disable();
-
return watchdog_register(wdd);
}
@@ -85,8 +83,9 @@ static struct of_device_id kvx_wdt_of_match[] = {
{ .compatible = "kalray,kvx-core-watchdog", },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, kvx_wdt_of_match);
-static struct driver_d kvx_wdt_driver = {
+static struct driver kvx_wdt_driver = {
.name = "kvx-wdt",
.probe = kvx_wdt_drv_probe,
.of_compatible = DRV_OF_COMPAT(kvx_wdt_of_match),
diff --git a/drivers/watchdog/omap_wdt.c b/drivers/watchdog/omap_wdt.c
index c8a7ccf607..0ebc1172aa 100644
--- a/drivers/watchdog/omap_wdt.c
+++ b/drivers/watchdog/omap_wdt.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* omap_wdt.c
*
@@ -11,10 +12,7 @@
* Author: MontaVista Software, Inc.
* <gdavis@mvista.com> or <source@mvista.com>
*
- * 2003 (c) MontaVista Software, Inc. This file is licensed under the
- * terms of the GNU General Public License version 2. This program is
- * licensed "as is" without any warranty of any kind, whether express
- * or implied.
+ * 2003 (c) MontaVista Software, Inc.
*
* History:
*
@@ -152,7 +150,7 @@ static int omap_wdt_set_timeout(struct watchdog *wdog,
return 0;
}
-static int omap_wdt_probe(struct device_d *dev)
+static int omap_wdt_probe(struct device *dev)
{
struct resource *iores;
struct omap_wdt_dev *wdev;
@@ -193,8 +191,9 @@ static const struct of_device_id omap_wdt_of_match[] = {
{ .compatible = "ti,omap3-wdt", },
{},
};
+MODULE_DEVICE_TABLE(of, omap_wdt_of_match);
-static struct driver_d omap_wdt_driver = {
+static struct driver omap_wdt_driver = {
.probe = omap_wdt_probe,
.name = "omap_wdt",
.of_compatible = DRV_OF_COMPAT(omap_wdt_of_match),
diff --git a/drivers/watchdog/orion_wdt.c b/drivers/watchdog/orion_wdt.c
index dd1fa3a04d..227f8b7bb1 100644
--- a/drivers/watchdog/orion_wdt.c
+++ b/drivers/watchdog/orion_wdt.c
@@ -1,16 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Watchdog driver for Marvell Armada XP.
*
* Copyright (C) 2017 Pengutronix, Uwe Kleine-König <kernel@pengutronix.de>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <common.h>
@@ -76,7 +68,7 @@ static int armada_xp_set_timeout(struct watchdog *wd, unsigned timeout)
return 0;
}
-static int orion_wdt_probe(struct device_d *dev)
+static int orion_wdt_probe(struct device *dev)
{
struct orion_wdt_ddata* ddata;
struct resource *res_timer, *res_rstout;
@@ -112,8 +104,9 @@ static const struct of_device_id orion_wdt_of_match[] = {
.compatible = "marvell,armada-xp-wdt",
}, { /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, orion_wdt_of_match);
-static struct driver_d orion_wdt_driver = {
+static struct driver orion_wdt_driver = {
.probe = orion_wdt_probe,
.name = "orion_wdt",
.of_compatible = DRV_OF_COMPAT(orion_wdt_of_match),
diff --git a/drivers/watchdog/rave-sp-wdt.c b/drivers/watchdog/rave-sp-wdt.c
index cad63e22f9..b4fc18cb8b 100644
--- a/drivers/watchdog/rave-sp-wdt.c
+++ b/drivers/watchdog/rave-sp-wdt.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for watchdog aspect of for Zodiac Inflight Innovations RAVE
@@ -252,6 +252,7 @@ static const struct of_device_id rave_sp_wdt_of_match[] = {
},
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, rave_sp_wdt_of_match);
static int rave_sp_wdt_set_boot_source(struct param_d *param, void *priv)
{
@@ -286,7 +287,7 @@ static int rave_sp_wdt_get_boot_source(struct param_d *param, void *priv)
static int rave_sp_wdt_add_params(struct rave_sp_wdt *sp_wd)
{
struct watchdog *wdd = &sp_wd->wdd;
- struct device_node *np = wdd->hwdev->device_node;
+ struct device_node *np = wdd->hwdev->of_node;
struct param_d *p;
sp_wd->boot_source_cell = of_nvmem_cell_get(np, "boot-source");
@@ -302,7 +303,7 @@ static int rave_sp_wdt_add_params(struct rave_sp_wdt *sp_wd)
return PTR_ERR_OR_ZERO(p);
}
-static int rave_sp_wdt_probe(struct device_d *dev)
+static int rave_sp_wdt_probe(struct device *dev)
{
struct rave_sp_wdt *sp_wd;
const char *reset_reason;
@@ -415,7 +416,7 @@ static int rave_sp_wdt_probe(struct device_d *dev)
return 0;
}
-static struct driver_d rave_sp_wdt_driver = {
+static struct driver rave_sp_wdt_driver = {
.name = "rave-sp-wdt",
.probe = rave_sp_wdt_probe,
.of_compatible = DRV_OF_COMPAT(rave_sp_wdt_of_match),
diff --git a/drivers/watchdog/rn5t568_wdt.c b/drivers/watchdog/rn5t568_wdt.c
new file mode 100644
index 0000000000..2011e3e01c
--- /dev/null
+++ b/drivers/watchdog/rn5t568_wdt.c
@@ -0,0 +1,147 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Watchdog driver for Ricoh RN5T618 PMIC
+ *
+ * Copyright (C) 2014 Beniamino Galvani <b.galvani@gmail.com>
+ */
+
+#include <common.h>
+#include <init.h>
+#include <watchdog.h>
+#include <linux/regmap.h>
+#include <of.h>
+
+#define RN5T568_WATCHDOG 0x0b
+# define RN5T568_WATCHDOG_WDPWROFFEN BIT(2)
+# define RN5T568_WATCHDOG_WDOGTIM_M (BIT(0) | BIT(1))
+#define RN5T568_PWRIREN 0x12
+# define RN5T568_PWRIREN_EN_WDOG BIT(6)
+#define RN5T568_PWRIRQ 0x13
+# define RN5T568_PWRIRQ_IR_WDOG BIT(6)
+
+struct rn5t568_wdt {
+ struct watchdog wdd;
+ struct regmap *regmap;
+ unsigned int timeout;
+};
+
+struct rn5t568_wdt_tim {
+ u8 reg_val;
+ u8 time;
+};
+
+static const struct rn5t568_wdt_tim rn5t568_wdt_timeout[] = {
+ { .reg_val = 0, .time = 1, },
+ { .reg_val = 1, .time = 8, },
+ { .reg_val = 2, .time = 32, },
+ { .reg_val = 3, .time = 128, },
+};
+
+#define PMIC_WDT_MAX_TIMEOUT 128
+
+static int rn5t568_wdt_start(struct regmap *regmap, int idx)
+{
+ int ret;
+
+ ret = regmap_update_bits(regmap, RN5T568_WATCHDOG, RN5T568_WATCHDOG_WDOGTIM_M,
+ rn5t568_wdt_timeout[idx].reg_val);
+ if (ret)
+ return ret;
+
+ regmap_clear_bits(regmap, RN5T568_PWRIRQ, RN5T568_PWRIRQ_IR_WDOG);
+ regmap_set_bits(regmap, RN5T568_PWRIREN, RN5T568_PWRIREN_EN_WDOG);
+
+ pr_debug("RN5t: Starting the watchdog with %u seconds\n", rn5t568_wdt_timeout[idx].time);
+
+ return regmap_set_bits(regmap, RN5T568_WATCHDOG, RN5T568_WATCHDOG_WDPWROFFEN);
+}
+
+static int rn5t568_wdt_stop(struct regmap *regmap)
+{
+ int ret;
+
+ ret = regmap_clear_bits(regmap, RN5T568_PWRIREN, RN5T568_PWRIREN_EN_WDOG);
+ if (ret)
+ return ret;
+
+ return regmap_clear_bits(regmap, RN5T568_WATCHDOG, RN5T568_WATCHDOG_WDPWROFFEN);
+}
+
+static int rn5t568_wdt_ping(struct regmap *regmap)
+{
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(regmap, RN5T568_WATCHDOG, &val);
+ if (ret)
+ return ret;
+
+ return regmap_write(regmap, RN5T568_WATCHDOG, val);
+}
+
+static int rn5t568_wdt_set_timeout(struct watchdog *wdd, unsigned int timeout)
+{
+ struct rn5t568_wdt *wdt = container_of(wdd, struct rn5t568_wdt, wdd);
+ int ret, i;
+
+ if (!timeout)
+ return rn5t568_wdt_stop(wdt->regmap);
+
+ for (i = 0; i < ARRAY_SIZE(rn5t568_wdt_timeout); i++) {
+ if (timeout < rn5t568_wdt_timeout[i].time)
+ break;
+ }
+
+ if (i == ARRAY_SIZE(rn5t568_wdt_timeout))
+ return -EINVAL;
+
+ if (wdt->timeout == timeout)
+ return rn5t568_wdt_ping(wdt->regmap);
+
+ ret = rn5t568_wdt_start(wdt->regmap, i);
+ if (ret)
+ return ret;
+
+ wdt->timeout = rn5t568_wdt_timeout[i].time;
+
+ return ret;
+}
+
+static int rn5t568_wdt_probe(struct device *dev)
+{
+ struct rn5t568_wdt *wdt;
+ struct watchdog *wdd;
+ unsigned int val;
+ int ret;
+
+ wdt = xzalloc(sizeof(*wdt));
+
+ wdt->regmap = dev_get_regmap(dev->parent, NULL);
+ if (IS_ERR(wdt->regmap))
+ return PTR_ERR(wdt->regmap);
+
+ wdd = &wdt->wdd;
+ wdd->hwdev = dev;
+ wdd->set_timeout = rn5t568_wdt_set_timeout;
+ wdd->timeout_max = PMIC_WDT_MAX_TIMEOUT;
+
+ ret = regmap_read(wdt->regmap, RN5T568_WATCHDOG, &val);
+ if (ret == 0)
+ wdd->running = val & RN5T568_WATCHDOG_WDPWROFFEN ?
+ WDOG_HW_RUNNING : WDOG_HW_NOT_RUNNING;
+
+ return watchdog_register(wdd);
+}
+
+static __maybe_unused const struct of_device_id rn5t568_wdt_of_match[] = {
+ { .compatible = "ricoh,rn5t568-wdt" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, rn5t568_wdt_of_match);
+
+static struct driver rn5t568_wdt_driver = {
+ .name = "rn5t568-wdt",
+ .probe = rn5t568_wdt_probe,
+ .of_compatible = DRV_OF_COMPAT(rn5t568_wdt_of_match),
+};
+device_platform_driver(rn5t568_wdt_driver);
diff --git a/drivers/watchdog/starfive_wdt.c b/drivers/watchdog/starfive_wdt.c
index 6779566fd6..90f1e0ae6b 100644
--- a/drivers/watchdog/starfive_wdt.c
+++ b/drivers/watchdog/starfive_wdt.c
@@ -44,7 +44,7 @@ static int starfive_wdt_set_timeout(struct watchdog *wdd, unsigned int timeout)
return 0;
}
-static int starfive_wdt_drv_probe(struct device_d *dev)
+static int starfive_wdt_drv_probe(struct device *dev)
{
struct starfive_wdt *wd;
struct resource *iores;
@@ -97,8 +97,9 @@ static struct of_device_id starfive_wdt_of_match[] = {
{ .compatible = "starfive,wdt", },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, starfive_wdt_of_match);
-static struct driver_d starfive_wdt_driver = {
+static struct driver starfive_wdt_driver = {
.name = "starfive-wdt",
.probe = starfive_wdt_drv_probe,
.of_compatible = starfive_wdt_of_match,
diff --git a/drivers/watchdog/stm32_iwdg.c b/drivers/watchdog/stm32_iwdg.c
index 4d7a263b7e..6ac9e7d56e 100644
--- a/drivers/watchdog/stm32_iwdg.c
+++ b/drivers/watchdog/stm32_iwdg.c
@@ -125,8 +125,9 @@ static const struct of_device_id stm32_iwdg_of_match[] = {
{ .compatible = "st,stm32mp1-iwdg", .data = &stm32mp1_iwdg_data },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, stm32_iwdg_of_match);
-static int stm32_iwdg_probe(struct device_d *dev)
+static int stm32_iwdg_probe(struct device *dev)
{
struct stm32_iwdg_data *data;
struct stm32_iwdg *wd;
@@ -187,7 +188,7 @@ static int stm32_iwdg_probe(struct device_d *dev)
return 0;
}
-static struct driver_d stm32_iwdg_driver = {
+static struct driver stm32_iwdg_driver = {
.name = "stm32-iwdg",
.probe = stm32_iwdg_probe,
.of_compatible = DRV_OF_COMPAT(stm32_iwdg_of_match),
diff --git a/drivers/watchdog/stpmic1_wdt.c b/drivers/watchdog/stpmic1_wdt.c
index 105ba39fb7..4a0519690a 100644
--- a/drivers/watchdog/stpmic1_wdt.c
+++ b/drivers/watchdog/stpmic1_wdt.c
@@ -11,6 +11,7 @@
#include <linux/iopoll.h>
#include <poweroff.h>
#include <mfd/syscon.h>
+#include <linux/regmap.h>
#include <restart.h>
#include <reset_source.h>
@@ -157,7 +158,7 @@ static int stpmic1_set_reset_reason(struct regmap *map)
return 0;
}
-static int stpmic1_wdt_probe(struct device_d *dev)
+static int stpmic1_wdt_probe(struct device *dev)
{
struct stpmic1_wdt *wdt;
struct watchdog *wdd;
@@ -208,8 +209,9 @@ static __maybe_unused const struct of_device_id stpmic1_wdt_of_match[] = {
{ .compatible = "st,stpmic1-wdt" },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, stpmic1_wdt_of_match);
-static struct driver_d stpmic1_wdt_driver = {
+static struct driver stpmic1_wdt_driver = {
.name = "stpmic1-wdt",
.probe = stpmic1_wdt_probe,
.of_compatible = DRV_OF_COMPAT(stpmic1_wdt_of_match),
diff --git a/drivers/watchdog/wd_core.c b/drivers/watchdog/wd_core.c
index 2e2814a8f2..f39a8f4522 100644
--- a/drivers/watchdog/wd_core.c
+++ b/drivers/watchdog/wd_core.c
@@ -1,15 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* (c) 2012 Juergen Beisert <kernel@pengutronix.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#define pr_fmt(fmt) "watchdog: " fmt
@@ -148,12 +139,12 @@ static int watchdog_register_dev(struct watchdog *wd, const char *name, int id)
*
* return: The priority
*/
-static unsigned int dev_get_watchdog_priority(struct device_d *dev)
+static unsigned int dev_get_watchdog_priority(struct device *dev)
{
unsigned int priority = WATCHDOG_DEFAULT_PRIORITY;
if (dev)
- of_property_read_u32(dev->device_node, "watchdog-priority",
+ of_property_read_u32(dev->of_node, "watchdog-priority",
&priority);
return priority;
@@ -188,7 +179,9 @@ static void __noreturn watchdog_restart_handle(struct restart_handler *this)
BUG_ON(ret);
mdelay(2000);
- __builtin_unreachable();
+
+ pr_emerg("Watchdog failed to reset the machine\n");
+ hang();
}
static struct restart_handler restart_handler = {
@@ -203,7 +196,7 @@ int watchdog_register(struct watchdog *wd)
int ret = 0;
if (wd->hwdev)
- alias = of_alias_get(wd->hwdev->device_node);
+ alias = of_alias_get(wd->hwdev->of_node);
if (alias)
ret = watchdog_register_dev(wd, alias, DEVICE_ID_SINGLE);
@@ -318,7 +311,7 @@ EXPORT_SYMBOL(watchdog_get_default);
int watchdog_get_alias_id_from(struct watchdog *wd, struct device_node *root)
{
- struct device_node *dstnp, *srcnp = wd->hwdev ? wd->hwdev->device_node : NULL;
+ struct device_node *dstnp, *srcnp = wd->hwdev ? wd->hwdev->of_node : NULL;
char *name;
if (!srcnp)
@@ -331,14 +324,14 @@ int watchdog_get_alias_id_from(struct watchdog *wd, struct device_node *root)
if (!dstnp)
return -ENODEV;
- return of_alias_get_id_from(root, wd->hwdev->device_node, "watchdog");
+ return of_alias_get_id_from(root, wd->hwdev->of_node, "watchdog");
}
EXPORT_SYMBOL(watchdog_get_alias_id_from);
struct watchdog *watchdog_get_by_name(const char *name)
{
struct watchdog *tmp;
- struct device_d *dev = get_device_by_name(name);
+ struct device *dev = get_device_by_name(name);
if (!dev)
return NULL;
diff --git a/drivers/watchdog/wdat_wdt.c b/drivers/watchdog/wdat_wdt.c
new file mode 100644
index 0000000000..522f1c25f1
--- /dev/null
+++ b/drivers/watchdog/wdat_wdt.c
@@ -0,0 +1,496 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ACPI Hardware Watchdog (WDAT) driver.
+ *
+ * Copyright (C) 2016, Intel Corporation
+ * Author: Mika Westerberg <mika.westerberg@linux.intel.com>
+ */
+#include <common.h>
+#include <acpi.h>
+#include <errno.h>
+#include <init.h>
+#include <io.h>
+#include <malloc.h>
+#include <of.h>
+#include <watchdog.h>
+
+enum acpi_wdat_actions {
+ ACPI_WDAT_RESET = 1,
+ ACPI_WDAT_GET_CURRENT_COUNTDOWN = 4,
+ ACPI_WDAT_GET_COUNTDOWN = 5,
+ ACPI_WDAT_SET_COUNTDOWN = 6,
+ ACPI_WDAT_GET_RUNNING_STATE = 8,
+ ACPI_WDAT_SET_RUNNING_STATE = 9,
+ ACPI_WDAT_GET_STOPPED_STATE = 10,
+ ACPI_WDAT_SET_STOPPED_STATE = 11,
+ ACPI_WDAT_GET_REBOOT = 16,
+ ACPI_WDAT_SET_REBOOT = 17,
+ ACPI_WDAT_GET_SHUTDOWN = 18,
+ ACPI_WDAT_SET_SHUTDOWN = 19,
+ ACPI_WDAT_GET_STATUS = 32,
+ ACPI_WDAT_SET_STATUS = 33,
+ ACPI_WDAT_ACTION_RESERVED = 34 /* 34 and greater are reserved */
+};
+
+enum acpi_wdat_instructions {
+ ACPI_WDAT_READ_VALUE = 0,
+ ACPI_WDAT_READ_COUNTDOWN = 1,
+ ACPI_WDAT_WRITE_VALUE = 2,
+ ACPI_WDAT_WRITE_COUNTDOWN = 3,
+ ACPI_WDAT_INSTRUCTION_RESERVED = 4, /* 4 and greater are reserved */
+ ACPI_WDAT_PRESERVE_REGISTER = 0x80 /* Except for this value */
+};
+
+#define MAX_WDAT_ACTIONS ACPI_WDAT_ACTION_RESERVED
+
+#define WDAT_DEFAULT_TIMEOUT 30
+
+/* WDAT Instruction Entries (actions) */
+
+struct __packed acpi_wdat_entry {
+ u8 action;
+ u8 instruction;
+ u16 reserved;
+ struct acpi_generic_address register_region;
+ u32 value; /* Value used with Read/Write register */
+ u32 mask; /* Bitmask required for this register instruction */
+};
+
+/**
+ * struct wdat_instruction - Single ACPI WDAT instruction
+ * @entry: Copy of the ACPI table instruction
+ * @reg: Register the instruction is accessing
+ * @node: Next instruction in action sequence
+ */
+struct wdat_instruction {
+ struct acpi_wdat_entry entry;
+ void __iomem *reg;
+ struct list_head node;
+};
+
+/**
+ * struct wdat_wdt - ACPI WDAT watchdog device
+ * @dev: Parent platform device
+ * @wdd: Watchdog core device
+ * @period: How long is one watchdog period in ms
+ * @stopped_in_sleep: Is this watchdog stopped by the firmware in S1-S5
+ * @stopped: Was the watchdog stopped by the driver in suspend
+ * @instructions: An array of instruction lists indexed by an action number from
+ * the WDAT table. There can be %NULL entries for not implemented
+ * actions.
+ */
+struct wdat_wdt {
+ struct watchdog wdd;
+ unsigned int period;
+ bool stopped_in_sleep;
+ bool stopped;
+ struct list_head *instructions[MAX_WDAT_ACTIONS];
+};
+
+struct __packed acpi_table_wdat {
+ struct acpi_table_header header; /* Common ACPI table header */
+ u32 header_length; /* Watchdog Header Length */
+ u16 pci_segment; /* PCI Segment number */
+ u8 pci_bus; /* PCI Bus number */
+ u8 pci_device; /* PCI Device number */
+ u8 pci_function; /* PCI Function number */
+ u8 reserved[3];
+ u32 timer_period; /* Period of one timer count (msec) */
+ u32 max_count; /* Maximum counter value supported */
+ u32 min_count; /* Minimum counter value */
+ u8 flags;
+ u8 reserved2[3];
+ u32 nr_entries; /* Number of watchdog entries that follow */
+ struct acpi_wdat_entry entries[];
+};
+
+#define ACPI_WDAT_ENABLED (1)
+#define ACPI_WDAT_STOPPED 0x80
+
+#define IO_COND(instr, is_pio, is_mmio) do { \
+ const struct acpi_generic_address *gas = &instr->entry.register_region; \
+ unsigned long port = (unsigned long __force)instr->reg; \
+ if (gas->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) { \
+ is_mmio; \
+ } else if (gas->space_id == ACPI_ADR_SPACE_SYSTEM_IO) { \
+ is_pio; \
+ } \
+} while (0)
+
+static unsigned int read8(const struct wdat_instruction *instr)
+{
+ IO_COND(instr, return inb(port), return readb(instr->reg));
+ return 0xff;
+}
+
+static unsigned int read16(const struct wdat_instruction *instr)
+{
+ IO_COND(instr, return inw(port), return readw(instr->reg));
+ return 0xffff;
+}
+
+static unsigned int read32(const struct wdat_instruction *instr)
+{
+ IO_COND(instr, return inl(port), return readl(instr->reg));
+ return 0xffffffff;
+}
+
+static void write8(u8 val, const struct wdat_instruction *instr)
+{
+ IO_COND(instr, outb(val,port), writeb(val, instr->reg));
+}
+
+static void write16(u16 val, const struct wdat_instruction *instr)
+{
+ IO_COND(instr, outw(val,port), writew(val, instr->reg));
+}
+
+static void write32(u32 val, const struct wdat_instruction *instr)
+{
+ IO_COND(instr, outl(val,port), writel(val, instr->reg));
+}
+
+static int wdat_wdt_read(struct wdat_wdt *wdat,
+ const struct wdat_instruction *instr, u32 *value)
+{
+ const struct acpi_generic_address *gas = &instr->entry.register_region;
+
+ switch (gas->access_width) {
+ case 1:
+ *value = read8(instr);
+ break;
+ case 2:
+ *value = read16(instr);
+ break;
+ case 3:
+ *value = read32(instr);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ dev_dbg(wdat->wdd.hwdev, "Read %#x from 0x%08llx\n", *value,
+ gas->address);
+
+ return 0;
+}
+
+static int wdat_wdt_write(struct wdat_wdt *wdat,
+ const struct wdat_instruction *instr, u32 value)
+{
+ const struct acpi_generic_address *gas = &instr->entry.register_region;
+
+ switch (gas->access_width) {
+ case 1:
+ write8((u8)value, instr);
+ break;
+ case 2:
+ write16((u16)value, instr);
+ break;
+ case 3:
+ write32(value, instr);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ dev_dbg(wdat->wdd.hwdev, "Wrote %#x to 0x%08llx\n", value,
+ gas->address);
+
+ return 0;
+}
+
+static int wdat_wdt_run_action(struct wdat_wdt *wdat, unsigned int action,
+ u32 param, u32 *retval)
+{
+ struct wdat_instruction *instr;
+
+ if (action >= ARRAY_SIZE(wdat->instructions)) {
+ dev_dbg(wdat->wdd.hwdev, "Invalid action %#x\n", action);
+ return -EINVAL;
+ }
+
+ if (!wdat->instructions[action]) {
+ dev_dbg(wdat->wdd.hwdev, "Unsupported action %#x\n", action);
+ return -EOPNOTSUPP;
+ }
+
+ dev_dbg(wdat->wdd.hwdev, "Running action %#x\n", action);
+
+ /* Run each instruction sequentially */
+ list_for_each_entry(instr, wdat->instructions[action], node) {
+ const struct acpi_wdat_entry *entry = &instr->entry;
+ const struct acpi_generic_address *gas;
+ u32 flags, value, mask, x, y;
+ bool preserve;
+ int ret;
+
+ gas = &entry->register_region;
+
+ preserve = entry->instruction & ACPI_WDAT_PRESERVE_REGISTER;
+ flags = entry->instruction & ~ACPI_WDAT_PRESERVE_REGISTER;
+ value = entry->value;
+ mask = entry->mask;
+
+ switch (flags) {
+ case ACPI_WDAT_READ_VALUE:
+ ret = wdat_wdt_read(wdat, instr, &x);
+ if (ret)
+ return ret;
+ x >>= gas->bit_offset;
+ x &= mask;
+ if (retval)
+ *retval = x == value;
+ break;
+
+ case ACPI_WDAT_READ_COUNTDOWN:
+ ret = wdat_wdt_read(wdat, instr, &x);
+ if (ret)
+ return ret;
+ x >>= gas->bit_offset;
+ x &= mask;
+ if (retval)
+ *retval = x;
+ break;
+
+ case ACPI_WDAT_WRITE_VALUE:
+ x = value & mask;
+ x <<= gas->bit_offset;
+ if (preserve) {
+ ret = wdat_wdt_read(wdat, instr, &y);
+ if (ret)
+ return ret;
+ y = y & ~(mask << gas->bit_offset);
+ x |= y;
+ }
+ ret = wdat_wdt_write(wdat, instr, x);
+ if (ret)
+ return ret;
+ break;
+
+ case ACPI_WDAT_WRITE_COUNTDOWN:
+ x = param;
+ x &= mask;
+ x <<= gas->bit_offset;
+ if (preserve) {
+ ret = wdat_wdt_read(wdat, instr, &y);
+ if (ret)
+ return ret;
+ y = y & ~(mask << gas->bit_offset);
+ x |= y;
+ }
+ ret = wdat_wdt_write(wdat, instr, x);
+ if (ret)
+ return ret;
+ break;
+
+ default:
+ dev_err(wdat->wdd.hwdev, "Unknown instruction: %u\n",
+ flags);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static void wdat_wdt_boot_status(struct wdat_wdt *wdat)
+{
+ u32 boot_status = 0;
+ int ret;
+
+ ret = wdat_wdt_run_action(wdat, ACPI_WDAT_GET_STATUS, 0, &boot_status);
+ if (ret && ret != -EOPNOTSUPP) {
+ dev_err(wdat->wdd.hwdev, "Failed to read boot status\n");
+ return;
+ }
+
+ /* Clear the boot status in case BIOS did not do it */
+ ret = wdat_wdt_run_action(wdat, ACPI_WDAT_SET_STATUS, 0, NULL);
+ if (ret && ret != -EOPNOTSUPP)
+ dev_err(wdat->wdd.hwdev, "Failed to clear boot status\n");
+}
+
+static int wdat_wdt_start(struct watchdog *wdd)
+{
+ struct wdat_wdt *wdat = container_of(wdd, struct wdat_wdt, wdd);
+
+ return wdat_wdt_run_action(wdat, ACPI_WDAT_SET_RUNNING_STATE, 0, NULL);
+}
+
+static int wdat_wdt_stop(struct watchdog *wdd)
+{
+ struct wdat_wdt *wdat = container_of(wdd, struct wdat_wdt, wdd);
+
+ return wdat_wdt_run_action(wdat, ACPI_WDAT_SET_STOPPED_STATE, 0, NULL);
+}
+
+static void wdat_wdt_set_running(struct wdat_wdt *wdat)
+{
+ u32 running = 0;
+ int ret;
+
+ ret = wdat_wdt_run_action(wdat, ACPI_WDAT_GET_RUNNING_STATE, 0,
+ &running);
+ if (ret && ret != -EOPNOTSUPP)
+ dev_err(wdat->wdd.hwdev, "Failed to read running state\n");
+
+ dev_dbg(wdat->wdd.hwdev, "Running state: %d\n", running);
+
+ wdat->wdd.running = running ? WDOG_HW_RUNNING : WDOG_HW_NOT_RUNNING;
+}
+
+static int wdat_wdt_set_timeout(struct watchdog *wdd, unsigned int timeout)
+{
+ struct wdat_wdt *wdat = container_of(wdd, struct wdat_wdt, wdd);
+ unsigned int periods;
+ int ret;
+
+ if (timeout == 0)
+ return wdat_wdt_stop(wdd);
+
+ periods = timeout * 1000 / wdat->period;
+ ret = wdat_wdt_run_action(wdat, ACPI_WDAT_SET_COUNTDOWN, periods, NULL);
+ if (ret)
+ return ret;
+
+ if (wdat->wdd.running == WDOG_HW_NOT_RUNNING) {
+ wdat_wdt_start(wdd);
+ wdat->wdd.running = WDOG_HW_RUNNING;
+ }
+
+ return wdat_wdt_run_action(wdat, ACPI_WDAT_RESET, 0, NULL);
+}
+
+static int wdat_wdt_enable_reboot(struct wdat_wdt *wdat)
+{
+ int ret;
+
+ /*
+ * WDAT specification says that the watchdog is required to reboot
+ * the system when it fires. However, it also states that it is
+ * recommeded to make it configurable through hardware register. We
+ * enable reboot now if it is configrable, just in case.
+ */
+ ret = wdat_wdt_run_action(wdat, ACPI_WDAT_SET_REBOOT, 0, NULL);
+ if (ret && ret != -EOPNOTSUPP) {
+ dev_err(wdat->wdd.hwdev,
+ "Failed to enable reboot when watchdog triggers\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int wdat_wdt_probe(struct device *const dev)
+{
+ const struct acpi_wdat_entry *entries;
+ struct acpi_table_wdat *tbl;
+ struct wdat_wdt *wdat;
+ int i, ret;
+
+ dev_dbg(dev, "driver initializing...\n");
+
+ tbl = (struct acpi_table_wdat __force *)dev_request_mem_region_by_name(dev, "SDT");
+ if (IS_ERR(tbl)) {
+ dev_err(dev, "no SDT resource available: %pe\n", tbl);
+ return PTR_ERR(tbl);
+ }
+
+ dev_dbg(dev, "SDT is at 0x%p\n", tbl);
+
+ wdat = xzalloc(sizeof(*wdat));
+
+ /* WDAT specification wants to have >= 1ms period */
+ if (tbl->timer_period < 1) {
+ dev_dbg(dev, "timer_period is less than 1: %d\n", tbl->timer_period);
+ return -EINVAL;
+ }
+ if (tbl->min_count > tbl->max_count) {
+ dev_dbg(dev, "min_count must be greater than max_count\n");
+ return -EINVAL;
+ }
+
+ wdat->period = tbl->timer_period;
+ wdat->stopped_in_sleep = tbl->flags & ACPI_WDAT_STOPPED;
+ wdat->wdd.set_timeout = wdat_wdt_set_timeout;
+ wdat->wdd.hwdev = dev;
+ wdat->wdd.timeout_max = U32_MAX;
+
+ entries = tbl->entries;
+
+ for (i = 0; i < tbl->nr_entries; i++) {
+ const struct acpi_generic_address *gas;
+ struct wdat_instruction *instr;
+ struct list_head *instructions;
+ struct resource *res;
+ unsigned int action;
+ struct resource r;
+
+ action = entries[i].action;
+ if (action >= MAX_WDAT_ACTIONS) {
+ dev_dbg(dev, "Skipping unknown action: %u\n", action);
+ continue;
+ }
+
+ instr = xzalloc(sizeof(*instr));
+
+ INIT_LIST_HEAD(&instr->node);
+ instr->entry = entries[i];
+
+ gas = &entries[i].register_region;
+
+ memset(&r, 0, sizeof(r));
+ r.start = gas->address;
+ r.end = r.start + ACPI_ACCESS_BYTE_WIDTH(gas->access_width) - 1;
+ if (gas->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) {
+ res = request_iomem_region(dev_name(dev), r.start, r.end);
+ } else if (gas->space_id == ACPI_ADR_SPACE_SYSTEM_IO) {
+ res = request_ioport_region(dev_name(dev), r.start, r.end);
+ } else {
+ dev_dbg(dev, "Unsupported address space: %d\n",
+ gas->space_id);
+ continue;
+ }
+
+ /*
+ * Some entries have the same gas->address.
+ * We want the action but can't request the region multiple times.
+ */
+ if (IS_ERR(res) && (PTR_ERR(res) != -EBUSY))
+ return PTR_ERR(res);
+
+ instr->reg = IOMEM(r.start);
+
+ instructions = wdat->instructions[action];
+ if (!instructions) {
+ instructions = xzalloc(sizeof(*instructions));
+ if (!instructions)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(instructions);
+ wdat->instructions[action] = instructions;
+ }
+
+ list_add_tail(&instr->node, instructions);
+ }
+
+ wdat_wdt_boot_status(wdat);
+ wdat_wdt_set_running(wdat);
+
+ ret = wdat_wdt_enable_reboot(wdat);
+ if (ret)
+ return ret;
+
+ return watchdog_register(&wdat->wdd);
+}
+
+
+static struct acpi_driver wdat_wdt_driver = {
+ .signature = "WDAT",
+ .driver = {
+ .name = "wdat-wdt",
+ .probe = wdat_wdt_probe,
+ }
+};
+device_acpi_driver(wdat_wdt_driver);