aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/watchdog
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/watchdog')
-rw-r--r--drivers/watchdog/Kconfig12
-rw-r--r--drivers/watchdog/Makefile1
-rw-r--r--drivers/watchdog/armada_37xx_wdt.c388
-rw-r--r--drivers/watchdog/booke_wdt.c2
-rw-r--r--drivers/watchdog/hpwdt.c24
-rw-r--r--drivers/watchdog/iTCO_vendor.h4
-rw-r--r--drivers/watchdog/iTCO_vendor_support.c168
-rw-r--r--drivers/watchdog/iTCO_wdt.c4
-rw-r--r--drivers/watchdog/lantiq_wdt.c311
-rw-r--r--drivers/watchdog/mpc8xxx_wdt.c46
-rw-r--r--drivers/watchdog/renesas_wdt.c6
-rw-r--r--drivers/watchdog/rza_wdt.c93
-rw-r--r--drivers/watchdog/sama5d4_wdt.c6
-rw-r--r--drivers/watchdog/ts4800_wdt.c1
-rw-r--r--drivers/watchdog/via_wdt.c4
-rw-r--r--drivers/watchdog/w83627hf_wdt.c8
-rw-r--r--drivers/watchdog/watchdog_dev.c10
17 files changed, 697 insertions, 391 deletions
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 5ea8909a41f9..2d64333f4782 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -273,6 +273,17 @@ config ARM_SBSA_WATCHDOG
To compile this driver as module, choose M here: The module
will be called sbsa_gwdt.
+config ARMADA_37XX_WATCHDOG
+ tristate "Armada 37xx watchdog"
+ depends on ARCH_MVEBU || COMPILE_TEST
+ select MFD_SYSCON
+ select WATCHDOG_CORE
+ help
+ Say Y here to include support for the watchdog timer found on
+ Marvell Armada 37xx SoCs.
+ To compile this driver as a module, choose M here: the
+ module will be called armada_37xx_wdt.
+
config ASM9260_WATCHDOG
tristate "Alphascale ASM9260 watchdog"
depends on MACH_ASM9260 || COMPILE_TEST
@@ -1621,6 +1632,7 @@ config IMGPDC_WDT
config LANTIQ_WDT
tristate "Lantiq SoC watchdog"
depends on LANTIQ
+ select WATCHDOG_CORE
help
Hardware driver for the Lantiq SoC Watchdog Timer.
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index bf92e7bf9ce0..f69cdff5ad7f 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -38,6 +38,7 @@ obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb.o
# ARM Architecture
obj-$(CONFIG_ARM_SP805_WATCHDOG) += sp805_wdt.o
obj-$(CONFIG_ARM_SBSA_WATCHDOG) += sbsa_gwdt.o
+obj-$(CONFIG_ARMADA_37XX_WATCHDOG) += armada_37xx_wdt.o
obj-$(CONFIG_ASM9260_WATCHDOG) += asm9260_wdt.o
obj-$(CONFIG_AT91RM9200_WATCHDOG) += at91rm9200_wdt.o
obj-$(CONFIG_AT91SAM9X_WATCHDOG) += at91sam9_wdt.o
diff --git a/drivers/watchdog/armada_37xx_wdt.c b/drivers/watchdog/armada_37xx_wdt.c
new file mode 100644
index 000000000000..4b4054f54df9
--- /dev/null
+++ b/drivers/watchdog/armada_37xx_wdt.c
@@ -0,0 +1,388 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Watchdog driver for Marvell Armada 37xx SoCs
+ *
+ * Author: Marek Behun <marek.behun@nic.cz>
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+#include <linux/watchdog.h>
+
+/*
+ * There are four counters that can be used for watchdog on Armada 37xx.
+ * The addresses for counter control registers are register base plus ID*0x10,
+ * where ID is 0, 1, 2 or 3.
+ *
+ * In this driver we use IDs 0 and 1. Counter ID 1 is used as watchdog counter,
+ * while counter ID 0 is used to implement pinging the watchdog: counter ID 1 is
+ * set to restart counting from initial value on counter ID 0 end count event.
+ * Pinging is done by forcing immediate end count event on counter ID 0.
+ * If only one counter was used, pinging would have to be implemented by
+ * disabling and enabling the counter, leaving the system in a vulnerable state
+ * for a (really) short period of time.
+ *
+ * Counters ID 2 and 3 are enabled by default even before U-Boot loads,
+ * therefore this driver does not provide a way to use them, eg. by setting a
+ * property in device tree.
+ */
+
+#define CNTR_ID_RETRIGGER 0
+#define CNTR_ID_WDOG 1
+
+/* relative to cpu_misc */
+#define WDT_TIMER_SELECT 0x64
+#define WDT_TIMER_SELECT_MASK 0xf
+#define WDT_TIMER_SELECT_VAL BIT(CNTR_ID_WDOG)
+
+/* relative to reg */
+#define CNTR_CTRL(id) ((id) * 0x10)
+#define CNTR_CTRL_ENABLE 0x0001
+#define CNTR_CTRL_ACTIVE 0x0002
+#define CNTR_CTRL_MODE_MASK 0x000c
+#define CNTR_CTRL_MODE_ONESHOT 0x0000
+#define CNTR_CTRL_MODE_HWSIG 0x000c
+#define CNTR_CTRL_TRIG_SRC_MASK 0x00f0
+#define CNTR_CTRL_TRIG_SRC_PREV_CNTR 0x0050
+#define CNTR_CTRL_PRESCALE_MASK 0xff00
+#define CNTR_CTRL_PRESCALE_MIN 2
+#define CNTR_CTRL_PRESCALE_SHIFT 8
+
+#define CNTR_COUNT_LOW(id) (CNTR_CTRL(id) + 0x4)
+#define CNTR_COUNT_HIGH(id) (CNTR_CTRL(id) + 0x8)
+
+#define WATCHDOG_TIMEOUT 120
+
+static unsigned int timeout;
+module_param(timeout, int, 0);
+MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds");
+
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+struct armada_37xx_watchdog {
+ struct watchdog_device wdt;
+ struct regmap *cpu_misc;
+ void __iomem *reg;
+ u64 timeout; /* in clock ticks */
+ unsigned long clk_rate;
+ struct clk *clk;
+};
+
+static u64 get_counter_value(struct armada_37xx_watchdog *dev, int id)
+{
+ u64 val;
+
+ /*
+ * when low is read, high is latched into flip-flops so that it can be
+ * read consistently without using software debouncing
+ */
+ val = readl(dev->reg + CNTR_COUNT_LOW(id));
+ val |= ((u64)readl(dev->reg + CNTR_COUNT_HIGH(id))) << 32;
+
+ return val;
+}
+
+static void set_counter_value(struct armada_37xx_watchdog *dev, int id, u64 val)
+{
+ writel(val & 0xffffffff, dev->reg + CNTR_COUNT_LOW(id));
+ writel(val >> 32, dev->reg + CNTR_COUNT_HIGH(id));
+}
+
+static void counter_enable(struct armada_37xx_watchdog *dev, int id)
+{
+ u32 reg;
+
+ reg = readl(dev->reg + CNTR_CTRL(id));
+ reg |= CNTR_CTRL_ENABLE;
+ writel(reg, dev->reg + CNTR_CTRL(id));
+}
+
+static void counter_disable(struct armada_37xx_watchdog *dev, int id)
+{
+ u32 reg;
+
+ reg = readl(dev->reg + CNTR_CTRL(id));
+ reg &= ~CNTR_CTRL_ENABLE;
+ writel(reg, dev->reg + CNTR_CTRL(id));
+}
+
+static void init_counter(struct armada_37xx_watchdog *dev, int id, u32 mode,
+ u32 trig_src)
+{
+ u32 reg;
+
+ reg = readl(dev->reg + CNTR_CTRL(id));
+
+ reg &= ~(CNTR_CTRL_MODE_MASK | CNTR_CTRL_PRESCALE_MASK |
+ CNTR_CTRL_TRIG_SRC_MASK);
+
+ /* set mode */
+ reg |= mode & CNTR_CTRL_MODE_MASK;
+
+ /* set prescaler to the min value */
+ reg |= CNTR_CTRL_PRESCALE_MIN << CNTR_CTRL_PRESCALE_SHIFT;
+
+ /* set trigger source */
+ reg |= trig_src & CNTR_CTRL_TRIG_SRC_MASK;
+
+ writel(reg, dev->reg + CNTR_CTRL(id));
+}
+
+static int armada_37xx_wdt_ping(struct watchdog_device *wdt)
+{
+ struct armada_37xx_watchdog *dev = watchdog_get_drvdata(wdt);
+
+ /* counter 1 is retriggered by forcing end count on counter 0 */
+ counter_disable(dev, CNTR_ID_RETRIGGER);
+ counter_enable(dev, CNTR_ID_RETRIGGER);
+
+ return 0;
+}
+
+static unsigned int armada_37xx_wdt_get_timeleft(struct watchdog_device *wdt)
+{
+ struct armada_37xx_watchdog *dev = watchdog_get_drvdata(wdt);
+ u64 res;
+
+ res = get_counter_value(dev, CNTR_ID_WDOG) * CNTR_CTRL_PRESCALE_MIN;
+ do_div(res, dev->clk_rate);
+
+ return res;
+}
+
+static int armada_37xx_wdt_set_timeout(struct watchdog_device *wdt,
+ unsigned int timeout)
+{
+ struct armada_37xx_watchdog *dev = watchdog_get_drvdata(wdt);
+
+ wdt->timeout = timeout;
+
+ /*
+ * Compute the timeout in clock rate. We use smallest possible
+ * prescaler, which divides the clock rate by 2
+ * (CNTR_CTRL_PRESCALE_MIN).
+ */
+ dev->timeout = (u64)dev->clk_rate * timeout;
+ do_div(dev->timeout, CNTR_CTRL_PRESCALE_MIN);
+
+ return 0;
+}
+
+static bool armada_37xx_wdt_is_running(struct armada_37xx_watchdog *dev)
+{
+ u32 reg;
+
+ regmap_read(dev->cpu_misc, WDT_TIMER_SELECT, &reg);
+ if ((reg & WDT_TIMER_SELECT_MASK) != WDT_TIMER_SELECT_VAL)
+ return false;
+
+ reg = readl(dev->reg + CNTR_CTRL(CNTR_ID_WDOG));
+ return !!(reg & CNTR_CTRL_ACTIVE);
+}
+
+static int armada_37xx_wdt_start(struct watchdog_device *wdt)
+{
+ struct armada_37xx_watchdog *dev = watchdog_get_drvdata(wdt);
+
+ /* select counter 1 as watchdog counter */
+ regmap_write(dev->cpu_misc, WDT_TIMER_SELECT, WDT_TIMER_SELECT_VAL);
+
+ /* init counter 0 as retrigger counter for counter 1 */
+ init_counter(dev, CNTR_ID_RETRIGGER, CNTR_CTRL_MODE_ONESHOT, 0);
+ set_counter_value(dev, CNTR_ID_RETRIGGER, 0);
+
+ /* init counter 1 to be retriggerable by counter 0 end count */
+ init_counter(dev, CNTR_ID_WDOG, CNTR_CTRL_MODE_HWSIG,
+ CNTR_CTRL_TRIG_SRC_PREV_CNTR);
+ set_counter_value(dev, CNTR_ID_WDOG, dev->timeout);
+
+ /* enable counter 1 */
+ counter_enable(dev, CNTR_ID_WDOG);
+
+ /* start counter 1 by forcing immediate end count on counter 0 */
+ counter_enable(dev, CNTR_ID_RETRIGGER);
+
+ return 0;
+}
+
+static int armada_37xx_wdt_stop(struct watchdog_device *wdt)
+{
+ struct armada_37xx_watchdog *dev = watchdog_get_drvdata(wdt);
+
+ counter_disable(dev, CNTR_ID_WDOG);
+ counter_disable(dev, CNTR_ID_RETRIGGER);
+ regmap_write(dev->cpu_misc, WDT_TIMER_SELECT, 0);
+
+ return 0;
+}
+
+static const struct watchdog_info armada_37xx_wdt_info = {
+ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
+ .identity = "Armada 37xx Watchdog",
+};
+
+static const struct watchdog_ops armada_37xx_wdt_ops = {
+ .owner = THIS_MODULE,
+ .start = armada_37xx_wdt_start,
+ .stop = armada_37xx_wdt_stop,
+ .ping = armada_37xx_wdt_ping,
+ .set_timeout = armada_37xx_wdt_set_timeout,
+ .get_timeleft = armada_37xx_wdt_get_timeleft,
+};
+
+static int armada_37xx_wdt_probe(struct platform_device *pdev)
+{
+ struct armada_37xx_watchdog *dev;
+ struct resource *res;
+ struct regmap *regmap;
+ int ret;
+
+ dev = devm_kzalloc(&pdev->dev, sizeof(struct armada_37xx_watchdog),
+ GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ dev->wdt.info = &armada_37xx_wdt_info;
+ dev->wdt.ops = &armada_37xx_wdt_ops;
+
+ regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
+ "marvell,system-controller");
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+ dev->cpu_misc = regmap;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENODEV;
+ dev->reg = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+
+ /* init clock */
+ dev->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(dev->clk))
+ return PTR_ERR(dev->clk);
+
+ ret = clk_prepare_enable(dev->clk);
+ if (ret)
+ return ret;
+
+ dev->clk_rate = clk_get_rate(dev->clk);
+ if (!dev->clk_rate) {
+ ret = -EINVAL;
+ goto disable_clk;
+ }
+
+ /*
+ * Since the timeout in seconds is given as 32 bit unsigned int, and
+ * the counters hold 64 bit values, even after multiplication by clock
+ * rate the counter can hold timeout of UINT_MAX seconds.
+ */
+ dev->wdt.min_timeout = 1;
+ dev->wdt.max_timeout = UINT_MAX;
+ dev->wdt.parent = &pdev->dev;
+
+ /* default value, possibly override by module parameter or dtb */
+ dev->wdt.timeout = WATCHDOG_TIMEOUT;
+ watchdog_init_timeout(&dev->wdt, timeout, &pdev->dev);
+
+ platform_set_drvdata(pdev, &dev->wdt);
+ watchdog_set_drvdata(&dev->wdt, dev);
+
+ armada_37xx_wdt_set_timeout(&dev->wdt, dev->wdt.timeout);
+
+ if (armada_37xx_wdt_is_running(dev))
+ set_bit(WDOG_HW_RUNNING, &dev->wdt.status);
+
+ watchdog_set_nowayout(&dev->wdt, nowayout);
+ ret = watchdog_register_device(&dev->wdt);
+ if (ret)
+ goto disable_clk;
+
+ dev_info(&pdev->dev, "Initial timeout %d sec%s\n",
+ dev->wdt.timeout, nowayout ? ", nowayout" : "");
+
+ return 0;
+
+disable_clk:
+ clk_disable_unprepare(dev->clk);
+ return ret;
+}
+
+static int armada_37xx_wdt_remove(struct platform_device *pdev)
+{
+ struct watchdog_device *wdt = platform_get_drvdata(pdev);
+ struct armada_37xx_watchdog *dev = watchdog_get_drvdata(wdt);
+
+ watchdog_unregister_device(wdt);
+ clk_disable_unprepare(dev->clk);
+ return 0;
+}
+
+static void armada_37xx_wdt_shutdown(struct platform_device *pdev)
+{
+ struct watchdog_device *wdt = platform_get_drvdata(pdev);
+
+ armada_37xx_wdt_stop(wdt);
+}
+
+static int __maybe_unused armada_37xx_wdt_suspend(struct device *dev)
+{
+ struct watchdog_device *wdt = dev_get_drvdata(dev);
+
+ return armada_37xx_wdt_stop(wdt);
+}
+
+static int __maybe_unused armada_37xx_wdt_resume(struct device *dev)
+{
+ struct watchdog_device *wdt = dev_get_drvdata(dev);
+
+ if (watchdog_active(wdt))
+ return armada_37xx_wdt_start(wdt);
+
+ return 0;
+}
+
+static const struct dev_pm_ops armada_37xx_wdt_dev_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(armada_37xx_wdt_suspend,
+ armada_37xx_wdt_resume)
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id armada_37xx_wdt_match[] = {
+ { .compatible = "marvell,armada-3700-wdt", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, armada_37xx_wdt_match);
+#endif
+
+static struct platform_driver armada_37xx_wdt_driver = {
+ .probe = armada_37xx_wdt_probe,
+ .remove = armada_37xx_wdt_remove,
+ .shutdown = armada_37xx_wdt_shutdown,
+ .driver = {
+ .name = "armada_37xx_wdt",
+ .of_match_table = of_match_ptr(armada_37xx_wdt_match),
+ .pm = &armada_37xx_wdt_dev_pm_ops,
+ },
+};
+
+module_platform_driver(armada_37xx_wdt_driver);
+
+MODULE_AUTHOR("Marek Behun <marek.behun@nic.cz>");
+MODULE_DESCRIPTION("Armada 37xx CPU Watchdog");
+
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:armada_37xx_wdt");
diff --git a/drivers/watchdog/booke_wdt.c b/drivers/watchdog/booke_wdt.c
index 3ad1e44bef44..6e78a34c2370 100644
--- a/drivers/watchdog/booke_wdt.c
+++ b/drivers/watchdog/booke_wdt.c
@@ -25,7 +25,7 @@
/* If the kernel parameter wdt=1, the watchdog will be enabled at boot.
* Also, the wdt_period sets the watchdog timer period timeout.
* For E500 cpus the wdt_period sets which bit changing from 0->1 will
- * trigger a watchog timeout. This watchdog timeout will occur 3 times, the
+ * trigger a watchdog timeout. This watchdog timeout will occur 3 times, the
* first time nothing will happen, the second time a watchdog exception will
* occur, and the final time the board will reset.
*/
diff --git a/drivers/watchdog/hpwdt.c b/drivers/watchdog/hpwdt.c
index 9dc62a461451..93562304f7aa 100644
--- a/drivers/watchdog/hpwdt.c
+++ b/drivers/watchdog/hpwdt.c
@@ -26,7 +26,7 @@
#include <linux/watchdog.h>
#include <asm/nmi.h>
-#define HPWDT_VERSION "2.0.0"
+#define HPWDT_VERSION "2.0.1"
#define SECS_TO_TICKS(secs) ((secs) * 1000 / 128)
#define TICKS_TO_SECS(ticks) ((ticks) * 128 / 1000)
#define HPWDT_MAX_TIMER TICKS_TO_SECS(65535)
@@ -162,7 +162,7 @@ static int hpwdt_pretimeout(unsigned int ulReason, struct pt_regs *regs)
if (ilo5 && ulReason == NMI_UNKNOWN && !mynmi)
return NMI_DONE;
- if (ilo5 && !pretimeout)
+ if (ilo5 && !pretimeout && !mynmi)
return NMI_DONE;
hpwdt_stop();
@@ -205,9 +205,7 @@ static struct watchdog_device hpwdt_dev = {
.min_timeout = 1,
.max_timeout = HPWDT_MAX_TIMER,
.timeout = DEFAULT_MARGIN,
-#ifdef CONFIG_HPWDT_NMI_DECODING
.pretimeout = PRETIMEOUT_SEC,
-#endif
};
@@ -313,6 +311,12 @@ static int hpwdt_init_one(struct pci_dev *dev,
if (watchdog_init_timeout(&hpwdt_dev, soft_margin, NULL))
dev_warn(&dev->dev, "Invalid soft_margin: %d.\n", soft_margin);
+ if (pretimeout && hpwdt_dev.timeout <= PRETIMEOUT_SEC) {
+ dev_warn(&dev->dev, "timeout <= pretimeout. Setting pretimeout to zero\n");
+ pretimeout = 0;
+ }
+ hpwdt_dev.pretimeout = pretimeout ? PRETIMEOUT_SEC : 0;
+
hpwdt_dev.parent = &dev->dev;
retval = watchdog_register_device(&hpwdt_dev);
if (retval < 0) {
@@ -320,9 +324,12 @@ static int hpwdt_init_one(struct pci_dev *dev,
goto error_wd_register;
}
- dev_info(&dev->dev, "HPE Watchdog Timer Driver: %s"
- ", timer margin: %d seconds (nowayout=%d).\n",
- HPWDT_VERSION, hpwdt_dev.timeout, nowayout);
+ dev_info(&dev->dev, "HPE Watchdog Timer Driver: Version: %s\n",
+ HPWDT_VERSION);
+ dev_info(&dev->dev, "timeout: %d seconds (nowayout=%d)\n",
+ hpwdt_dev.timeout, nowayout);
+ dev_info(&dev->dev, "pretimeout: %s.\n",
+ pretimeout ? "on" : "off");
if (dev->subsystem_vendor == PCI_VENDOR_ID_HP_3PAR)
ilo5 = true;
@@ -364,6 +371,9 @@ MODULE_VERSION(HPWDT_VERSION);
module_param(soft_margin, int, 0);
MODULE_PARM_DESC(soft_margin, "Watchdog timeout in seconds");
+module_param_named(timeout, soft_margin, int, 0);
+MODULE_PARM_DESC(timeout, "Alias of soft_margin");
+
module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
diff --git a/drivers/watchdog/iTCO_vendor.h b/drivers/watchdog/iTCO_vendor.h
index 7b82a7c6e7c3..0f7373ba10d5 100644
--- a/drivers/watchdog/iTCO_vendor.h
+++ b/drivers/watchdog/iTCO_vendor.h
@@ -3,14 +3,10 @@
#ifdef CONFIG_ITCO_VENDOR_SUPPORT
extern void iTCO_vendor_pre_start(struct resource *, unsigned int);
extern void iTCO_vendor_pre_stop(struct resource *);
-extern void iTCO_vendor_pre_keepalive(struct resource *, unsigned int);
-extern void iTCO_vendor_pre_set_heartbeat(unsigned int);
extern int iTCO_vendor_check_noreboot_on(void);
#else
#define iTCO_vendor_pre_start(acpibase, heartbeat) {}
#define iTCO_vendor_pre_stop(acpibase) {}
-#define iTCO_vendor_pre_keepalive(acpibase, heartbeat) {}
-#define iTCO_vendor_pre_set_heartbeat(heartbeat) {}
#define iTCO_vendor_check_noreboot_on() 1
/* 1=check noreboot; 0=don't check */
#endif
diff --git a/drivers/watchdog/iTCO_vendor_support.c b/drivers/watchdog/iTCO_vendor_support.c
index b6b2f90b5d44..68a9d9cc2eb8 100644
--- a/drivers/watchdog/iTCO_vendor_support.c
+++ b/drivers/watchdog/iTCO_vendor_support.c
@@ -38,7 +38,7 @@
/* List of vendor support modes */
/* SuperMicro Pentium 3 Era 370SSE+-OEM1/P3TSSE */
#define SUPERMICRO_OLD_BOARD 1
-/* SuperMicro Pentium 4 / Xeon 4 / EMT64T Era Systems */
+/* SuperMicro Pentium 4 / Xeon 4 / EMT64T Era Systems - no longer supported */
#define SUPERMICRO_NEW_BOARD 2
/* Broken BIOS */
#define BROKEN_BIOS 911
@@ -46,8 +46,7 @@
static int vendorsupport;
module_param(vendorsupport, int, 0);
MODULE_PARM_DESC(vendorsupport, "iTCO vendor specific support mode, default="
- "0 (none), 1=SuperMicro Pent3, 2=SuperMicro Pent4+, "
- "911=Broken SMI BIOS");
+ "0 (none), 1=SuperMicro Pent3, 911=Broken SMI BIOS");
/*
* Vendor Specific Support
@@ -98,143 +97,6 @@ static void supermicro_old_pre_stop(struct resource *smires)
}
/*
- * Vendor Support: 2
- * Board: Super Micro Computer Inc. P4SBx, P4DPx
- * iTCO chipset: ICH4
- *
- * Code contributed by: R. Seretny <lkpatches@paypc.com>
- * Documentation obtained by R. Seretny from SuperMicro Technical Support
- *
- * To enable Watchdog function:
- * 1. BIOS
- * For P4SBx:
- * BIOS setup -> Advanced -> Integrated Peripherals -> Watch Dog Feature
- * For P4DPx:
- * BIOS setup -> Advanced -> I/O Device Configuration -> Watch Dog
- * This setting enables or disables Watchdog function. When enabled, the
- * default watchdog timer is set to be 5 minutes (about 4m35s). It is
- * enough to load and run the OS. The application (service or driver) has
- * to take over the control once OS is running up and before watchdog
- * expires.
- *
- * 2. JUMPER
- * For P4SBx: JP39
- * For P4DPx: JP37
- * This jumper is used for safety. Closed is enabled. This jumper
- * prevents user enables watchdog in BIOS by accident.
- *
- * To enable Watch Dog function, both BIOS and JUMPER must be enabled.
- *
- * The documentation lists motherboards P4SBx and P4DPx series as of
- * 20-March-2002. However, this code works flawlessly with much newer
- * motherboards, such as my X6DHR-8G2 (SuperServer 6014H-82).
- *
- * The original iTCO driver as written does not actually reset the
- * watchdog timer on these machines, as a result they reboot after five
- * minutes.
- *
- * NOTE: You may leave the Watchdog function disabled in the SuperMicro
- * BIOS to avoid a "boot-race"... This driver will enable watchdog
- * functionality even if it's disabled in the BIOS once the /dev/watchdog
- * file is opened.
- */
-
-/* I/O Port's */
-#define SM_REGINDEX 0x2e /* SuperMicro ICH4+ Register Index */
-#define SM_DATAIO 0x2f /* SuperMicro ICH4+ Register Data I/O */
-
-/* Control Register's */
-#define SM_CTLPAGESW 0x07 /* SuperMicro ICH4+ Control Page Switch */
-#define SM_CTLPAGE 0x08 /* SuperMicro ICH4+ Control Page Num */
-
-#define SM_WATCHENABLE 0x30 /* Watchdog enable: Bit 0: 0=off, 1=on */
-
-#define SM_WATCHPAGE 0x87 /* Watchdog unlock control page */
-
-#define SM_ENDWATCH 0xAA /* Watchdog lock control page */
-
-#define SM_COUNTMODE 0xf5 /* Watchdog count mode select */
- /* (Bit 3: 0 = seconds, 1 = minutes */
-
-#define SM_WATCHTIMER 0xf6 /* 8-bits, Watchdog timer counter (RW) */
-
-#define SM_RESETCONTROL 0xf7 /* Watchdog reset control */
- /* Bit 6: timer is reset by kbd interrupt */
- /* Bit 7: timer is reset by mouse interrupt */
-
-static void supermicro_new_unlock_watchdog(void)
-{
- /* Write 0x87 to port 0x2e twice */
- outb(SM_WATCHPAGE, SM_REGINDEX);
- outb(SM_WATCHPAGE, SM_REGINDEX);
- /* Switch to watchdog control page */
- outb(SM_CTLPAGESW, SM_REGINDEX);
- outb(SM_CTLPAGE, SM_DATAIO);
-}
-
-static void supermicro_new_lock_watchdog(void)
-{
- outb(SM_ENDWATCH, SM_REGINDEX);
-}
-
-static void supermicro_new_pre_start(unsigned int heartbeat)
-{
- unsigned int val;
-
- supermicro_new_unlock_watchdog();
-
- /* Watchdog timer setting needs to be in seconds*/
- outb(SM_COUNTMODE, SM_REGINDEX);
- val = inb(SM_DATAIO);
- val &= 0xF7;
- outb(val, SM_DATAIO);
-
- /* Write heartbeat interval to WDOG */
- outb(SM_WATCHTIMER, SM_REGINDEX);
- outb((heartbeat & 255), SM_DATAIO);
-
- /* Make sure keyboard/mouse interrupts don't interfere */
- outb(SM_RESETCONTROL, SM_REGINDEX);
- val = inb(SM_DATAIO);
- val &= 0x3f;
- outb(val, SM_DATAIO);
-
- /* enable watchdog by setting bit 0 of Watchdog Enable to 1 */
- outb(SM_WATCHENABLE, SM_REGINDEX);
- val = inb(SM_DATAIO);
- val |= 0x01;
- outb(val, SM_DATAIO);
-
- supermicro_new_lock_watchdog();
-}
-
-static void supermicro_new_pre_stop(void)
-{
- unsigned int val;
-
- supermicro_new_unlock_watchdog();
-
- /* disable watchdog by setting bit 0 of Watchdog Enable to 0 */
- outb(SM_WATCHENABLE, SM_REGINDEX);
- val = inb(SM_DATAIO);
- val &= 0xFE;
- outb(val, SM_DATAIO);
-
- supermicro_new_lock_watchdog();
-}
-
-static void supermicro_new_pre_set_heartbeat(unsigned int heartbeat)
-{
- supermicro_new_unlock_watchdog();
-
- /* reset watchdog timeout to heartveat value */
- outb(SM_WATCHTIMER, SM_REGINDEX);
- outb((heartbeat & 255), SM_DATAIO);
-
- supermicro_new_lock_watchdog();
-}
-
-/*
* Vendor Support: 911
* Board: Some Intel ICHx based motherboards
* iTCO chipset: ICH7+
@@ -298,9 +160,6 @@ void iTCO_vendor_pre_start(struct resource *smires,
case SUPERMICRO_OLD_BOARD:
supermicro_old_pre_start(smires);
break;
- case SUPERMICRO_NEW_BOARD:
- supermicro_new_pre_start(heartbeat);
- break;
case BROKEN_BIOS:
broken_bios_start(smires);
break;
@@ -314,9 +173,6 @@ void iTCO_vendor_pre_stop(struct resource *smires)
case SUPERMICRO_OLD_BOARD:
supermicro_old_pre_stop(smires);
break;
- case SUPERMICRO_NEW_BOARD:
- supermicro_new_pre_stop();
- break;
case BROKEN_BIOS:
broken_bios_stop(smires);
break;
@@ -324,20 +180,6 @@ void iTCO_vendor_pre_stop(struct resource *smires)
}
EXPORT_SYMBOL(iTCO_vendor_pre_stop);
-void iTCO_vendor_pre_keepalive(struct resource *smires, unsigned int heartbeat)
-{
- if (vendorsupport == SUPERMICRO_NEW_BOARD)
- supermicro_new_pre_set_heartbeat(heartbeat);
-}
-EXPORT_SYMBOL(iTCO_vendor_pre_keepalive);
-
-void iTCO_vendor_pre_set_heartbeat(unsigned int heartbeat)
-{
- if (vendorsupport == SUPERMICRO_NEW_BOARD)
- supermicro_new_pre_set_heartbeat(heartbeat);
-}
-EXPORT_SYMBOL(iTCO_vendor_pre_set_heartbeat);
-
int iTCO_vendor_check_noreboot_on(void)
{
switch (vendorsupport) {
@@ -351,6 +193,12 @@ EXPORT_SYMBOL(iTCO_vendor_check_noreboot_on);
static int __init iTCO_vendor_init_module(void)
{
+ if (vendorsupport == SUPERMICRO_NEW_BOARD) {
+ pr_warn("Option vendorsupport=%d is no longer supported, "
+ "please use the w83627hf_wdt driver instead\n",
+ SUPERMICRO_NEW_BOARD);
+ return -EINVAL;
+ }
pr_info("vendor-support=%d\n", vendorsupport);
return 0;
}
diff --git a/drivers/watchdog/iTCO_wdt.c b/drivers/watchdog/iTCO_wdt.c
index 347f0389b089..0a5318b7865e 100644
--- a/drivers/watchdog/iTCO_wdt.c
+++ b/drivers/watchdog/iTCO_wdt.c
@@ -304,8 +304,6 @@ static int iTCO_wdt_ping(struct watchdog_device *wd_dev)
spin_lock(&p->io_lock);
- iTCO_vendor_pre_keepalive(p->smi_res, wd_dev->timeout);
-
/* Reload the timer by writing to the TCO Timer Counter register */
if (p->iTCO_version >= 2) {
outw(0x01, TCO_RLD(p));
@@ -342,8 +340,6 @@ static int iTCO_wdt_set_timeout(struct watchdog_device *wd_dev, unsigned int t)
(p->iTCO_version == 1 && tmrval > 0x03f))
return -EINVAL;
- iTCO_vendor_pre_set_heartbeat(tmrval);
-
/* Write new heartbeat to watchdog */
if (p->iTCO_version >= 2) {
spin_lock(&p->io_lock);
diff --git a/drivers/watchdog/lantiq_wdt.c b/drivers/watchdog/lantiq_wdt.c
index 7f43cefa0eae..83da84d6074b 100644
--- a/drivers/watchdog/lantiq_wdt.c
+++ b/drivers/watchdog/lantiq_wdt.c
@@ -8,11 +8,8 @@
* Based on EP93xx wdt driver
*/
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
#include <linux/module.h>
-#include <linux/fs.h>
-#include <linux/miscdevice.h>
+#include <linux/bitops.h>
#include <linux/watchdog.h>
#include <linux/of_platform.h>
#include <linux/uaccess.h>
@@ -40,169 +37,128 @@
* essentially the following two magic passwords need to be written to allow
* IO access to the WDT core
*/
-#define LTQ_WDT_PW1 0x00BE0000
-#define LTQ_WDT_PW2 0x00DC0000
-
-#define LTQ_WDT_CR 0x0 /* watchdog control register */
-#define LTQ_WDT_SR 0x8 /* watchdog status register */
+#define LTQ_WDT_CR_PW1 0x00BE0000
+#define LTQ_WDT_CR_PW2 0x00DC0000
+
+#define LTQ_WDT_CR 0x0 /* watchdog control register */
+#define LTQ_WDT_CR_GEN BIT(31) /* enable bit */
+/* Pre-warning limit set to 1/16 of max WDT period */
+#define LTQ_WDT_CR_PWL (0x3 << 26)
+/* set clock divider to 0x40000 */
+#define LTQ_WDT_CR_CLKDIV (0x3 << 24)
+#define LTQ_WDT_CR_PW_MASK GENMASK(23, 16) /* Password field */
+#define LTQ_WDT_CR_MAX_TIMEOUT ((1 << 16) - 1) /* The reload field is 16 bit */
+#define LTQ_WDT_SR 0x8 /* watchdog status register */
+#define LTQ_WDT_SR_EN BIT(31) /* Enable */
+#define LTQ_WDT_SR_VALUE_MASK GENMASK(15, 0) /* Timer value */
-#define LTQ_WDT_SR_EN (0x1 << 31) /* enable bit */
-#define LTQ_WDT_SR_PWD (0x3 << 26) /* turn on power */
-#define LTQ_WDT_SR_CLKDIV (0x3 << 24) /* turn on clock and set */
- /* divider to 0x40000 */
#define LTQ_WDT_DIVIDER 0x40000
-#define LTQ_MAX_TIMEOUT ((1 << 16) - 1) /* the reload field is 16 bit */
static bool nowayout = WATCHDOG_NOWAYOUT;
-static void __iomem *ltq_wdt_membase;
-static unsigned long ltq_io_region_clk_rate;
+struct ltq_wdt_hw {
+ int (*bootstatus_get)(struct device *dev);
+};
-static unsigned long ltq_wdt_bootstatus;
-static unsigned long ltq_wdt_in_use;
-static int ltq_wdt_timeout = 30;
-static int ltq_wdt_ok_to_close;
+struct ltq_wdt_priv {
+ struct watchdog_device wdt;
+ void __iomem *membase;
+ unsigned long clk_rate;
+};
-static void
-ltq_wdt_enable(void)
+static u32 ltq_wdt_r32(struct ltq_wdt_priv *priv, u32 offset)
{
- unsigned long int timeout = ltq_wdt_timeout *
- (ltq_io_region_clk_rate / LTQ_WDT_DIVIDER) + 0x1000;
- if (timeout > LTQ_MAX_TIMEOUT)
- timeout = LTQ_MAX_TIMEOUT;
-
- /* write the first password magic */
- ltq_w32(LTQ_WDT_PW1, ltq_wdt_membase + LTQ_WDT_CR);
- /* write the second magic plus the configuration and new timeout */
- ltq_w32(LTQ_WDT_SR_EN | LTQ_WDT_SR_PWD | LTQ_WDT_SR_CLKDIV |
- LTQ_WDT_PW2 | timeout, ltq_wdt_membase + LTQ_WDT_CR);
+ return __raw_readl(priv->membase + offset);
}
-static void
-ltq_wdt_disable(void)
+static void ltq_wdt_w32(struct ltq_wdt_priv *priv, u32 val, u32 offset)
{
- /* write the first password magic */
- ltq_w32(LTQ_WDT_PW1, ltq_wdt_membase + LTQ_WDT_CR);
- /*
- * write the second password magic with no config
- * this turns the watchdog off
- */
- ltq_w32(LTQ_WDT_PW2, ltq_wdt_membase + LTQ_WDT_CR);
+ __raw_writel(val, priv->membase + offset);
}
-static ssize_t
-ltq_wdt_write(struct file *file, const char __user *data,
- size_t len, loff_t *ppos)
+static void ltq_wdt_mask(struct ltq_wdt_priv *priv, u32 clear, u32 set,
+ u32 offset)
{
- if (len) {
- if (!nowayout) {
- size_t i;
-
- ltq_wdt_ok_to_close = 0;
- for (i = 0; i != len; i++) {
- char c;
-
- if (get_user(c, data + i))
- return -EFAULT;
- if (c == 'V')
- ltq_wdt_ok_to_close = 1;
- else
- ltq_wdt_ok_to_close = 0;
- }
- }
- ltq_wdt_enable();
- }
+ u32 val = ltq_wdt_r32(priv, offset);
- return len;
+ val &= ~(clear);
+ val |= set;
+ ltq_wdt_w32(priv, val, offset);
}
-static struct watchdog_info ident = {
+static struct ltq_wdt_priv *ltq_wdt_get_priv(struct watchdog_device *wdt)
+{
+ return container_of(wdt, struct ltq_wdt_priv, wdt);
+}
+
+static struct watchdog_info ltq_wdt_info = {
.options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING |
- WDIOF_CARDRESET,
+ WDIOF_CARDRESET,
.identity = "ltq_wdt",
};
-static long
-ltq_wdt_ioctl(struct file *file,
- unsigned int cmd, unsigned long arg)
+static int ltq_wdt_start(struct watchdog_device *wdt)
{
- int ret = -ENOTTY;
-
- switch (cmd) {
- case WDIOC_GETSUPPORT:
- ret = copy_to_user((struct watchdog_info __user *)arg, &ident,
- sizeof(ident)) ? -EFAULT : 0;
- break;
-
- case WDIOC_GETBOOTSTATUS:
- ret = put_user(ltq_wdt_bootstatus, (int __user *)arg);
- break;
-
- case WDIOC_GETSTATUS:
- ret = put_user(0, (int __user *)arg);
- break;
-
- case WDIOC_SETTIMEOUT:
- ret = get_user(ltq_wdt_timeout, (int __user *)arg);
- if (!ret)
- ltq_wdt_enable();
- /* intentional drop through */
- case WDIOC_GETTIMEOUT:
- ret = put_user(ltq_wdt_timeout, (int __user *)arg);
- break;
-
- case WDIOC_KEEPALIVE:
- ltq_wdt_enable();
- ret = 0;
- break;
- }
- return ret;
+ struct ltq_wdt_priv *priv = ltq_wdt_get_priv(wdt);
+ u32 timeout;
+
+ timeout = wdt->timeout * priv->clk_rate;
+
+ ltq_wdt_mask(priv, LTQ_WDT_CR_PW_MASK, LTQ_WDT_CR_PW1, LTQ_WDT_CR);
+ /* write the second magic plus the configuration and new timeout */
+ ltq_wdt_mask(priv, LTQ_WDT_CR_PW_MASK | LTQ_WDT_CR_MAX_TIMEOUT,
+ LTQ_WDT_CR_GEN | LTQ_WDT_CR_PWL | LTQ_WDT_CR_CLKDIV |
+ LTQ_WDT_CR_PW2 | timeout,
+ LTQ_WDT_CR);
+
+ return 0;
}
-static int
-ltq_wdt_open(struct inode *inode, struct file *file)
+static int ltq_wdt_stop(struct watchdog_device *wdt)
{
- if (test_and_set_bit(0, &ltq_wdt_in_use))
- return -EBUSY;
- ltq_wdt_in_use = 1;
- ltq_wdt_enable();
+ struct ltq_wdt_priv *priv = ltq_wdt_get_priv(wdt);
- return nonseekable_open(inode, file);
+ ltq_wdt_mask(priv, LTQ_WDT_CR_PW_MASK, LTQ_WDT_CR_PW1, LTQ_WDT_CR);
+ ltq_wdt_mask(priv, LTQ_WDT_CR_GEN | LTQ_WDT_CR_PW_MASK,
+ LTQ_WDT_CR_PW2, LTQ_WDT_CR);
+
+ return 0;
}
-static int
-ltq_wdt_release(struct inode *inode, struct file *file)
+static int ltq_wdt_ping(struct watchdog_device *wdt)
{
- if (ltq_wdt_ok_to_close)
- ltq_wdt_disable();
- else
- pr_err("watchdog closed without warning\n");
- ltq_wdt_ok_to_close = 0;
- clear_bit(0, &ltq_wdt_in_use);
+ struct ltq_wdt_priv *priv = ltq_wdt_get_priv(wdt);
+ u32 timeout;
+
+ timeout = wdt->timeout * priv->clk_rate;
+
+ ltq_wdt_mask(priv, LTQ_WDT_CR_PW_MASK, LTQ_WDT_CR_PW1, LTQ_WDT_CR);
+ /* write the second magic plus the configuration and new timeout */
+ ltq_wdt_mask(priv, LTQ_WDT_CR_PW_MASK | LTQ_WDT_CR_MAX_TIMEOUT,
+ LTQ_WDT_CR_PW2 | timeout, LTQ_WDT_CR);
return 0;
}
-static const struct file_operations ltq_wdt_fops = {
- .owner = THIS_MODULE,
- .write = ltq_wdt_write,
- .unlocked_ioctl = ltq_wdt_ioctl,
- .open = ltq_wdt_open,
- .release = ltq_wdt_release,
- .llseek = no_llseek,
-};
+static unsigned int ltq_wdt_get_timeleft(struct watchdog_device *wdt)
+{
+ struct ltq_wdt_priv *priv = ltq_wdt_get_priv(wdt);
+ u64 timeout;
-static struct miscdevice ltq_wdt_miscdev = {
- .minor = WATCHDOG_MINOR,
- .name = "watchdog",
- .fops = &ltq_wdt_fops,
-};
+ timeout = ltq_wdt_r32(priv, LTQ_WDT_SR) & LTQ_WDT_SR_VALUE_MASK;
+ return do_div(timeout, priv->clk_rate);
+}
-typedef int (*ltq_wdt_bootstatus_set)(struct platform_device *pdev);
+static const struct watchdog_ops ltq_wdt_ops = {
+ .owner = THIS_MODULE,
+ .start = ltq_wdt_start,
+ .stop = ltq_wdt_stop,
+ .ping = ltq_wdt_ping,
+ .get_timeleft = ltq_wdt_get_timeleft,
+};
-static int ltq_wdt_bootstatus_xrx(struct platform_device *pdev)
+static int ltq_wdt_xrx_bootstatus_get(struct device *dev)
{
- struct device *dev = &pdev->dev;
struct regmap *rcu_regmap;
u32 val;
int err;
@@ -216,14 +172,13 @@ static int ltq_wdt_bootstatus_xrx(struct platform_device *pdev)
return err;
if (val & LTQ_XRX_RCU_RST_STAT_WDT)
- ltq_wdt_bootstatus = WDIOF_CARDRESET;
+ return WDIOF_CARDRESET;
return 0;
}
-static int ltq_wdt_bootstatus_falcon(struct platform_device *pdev)
+static int ltq_wdt_falcon_bootstatus_get(struct device *dev)
{
- struct device *dev = &pdev->dev;
struct regmap *rcu_regmap;
u32 val;
int err;
@@ -238,62 +193,90 @@ static int ltq_wdt_bootstatus_falcon(struct platform_device *pdev)
return err;
if ((val & LTQ_FALCON_SYS1_CPU0RS_MASK) == LTQ_FALCON_SYS1_CPU0RS_WDT)
- ltq_wdt_bootstatus = WDIOF_CARDRESET;
+ return WDIOF_CARDRESET;
return 0;
}
-static int
-ltq_wdt_probe(struct platform_device *pdev)
+static int ltq_wdt_probe(struct platform_device *pdev)
{
- struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ struct device *dev = &pdev->dev;
+ struct ltq_wdt_priv *priv;
+ struct watchdog_device *wdt;
+ struct resource *res;
struct clk *clk;
- ltq_wdt_bootstatus_set ltq_wdt_bootstatus_set;
+ const struct ltq_wdt_hw *ltq_wdt_hw;
int ret;
+ u32 status;
- ltq_wdt_membase = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(ltq_wdt_membase))
- return PTR_ERR(ltq_wdt_membase);
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
- ltq_wdt_bootstatus_set = of_device_get_match_data(&pdev->dev);
- if (ltq_wdt_bootstatus_set) {
- ret = ltq_wdt_bootstatus_set(pdev);
- if (ret)
- return ret;
- }
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ priv->membase = devm_ioremap_resource(dev, res);
+ if (IS_ERR(priv->membase))
+ return PTR_ERR(priv->membase);
/* we do not need to enable the clock as it is always running */
clk = clk_get_io();
- if (IS_ERR(clk)) {
- dev_err(&pdev->dev, "Failed to get clock\n");
- return -ENOENT;
+ priv->clk_rate = clk_get_rate(clk) / LTQ_WDT_DIVIDER;
+ if (!priv->clk_rate) {
+ dev_err(dev, "clock rate less than divider %i\n",
+ LTQ_WDT_DIVIDER);
+ return -EINVAL;
}
- ltq_io_region_clk_rate = clk_get_rate(clk);
- clk_put(clk);
- dev_info(&pdev->dev, "Init done\n");
- return misc_register(&ltq_wdt_miscdev);
-}
+ wdt = &priv->wdt;
+ wdt->info = &ltq_wdt_info;
+ wdt->ops = &ltq_wdt_ops;
+ wdt->min_timeout = 1;
+ wdt->max_timeout = LTQ_WDT_CR_MAX_TIMEOUT / priv->clk_rate;
+ wdt->timeout = wdt->max_timeout;
+ wdt->parent = dev;
+
+ ltq_wdt_hw = of_device_get_match_data(dev);
+ if (ltq_wdt_hw && ltq_wdt_hw->bootstatus_get) {
+ ret = ltq_wdt_hw->bootstatus_get(dev);
+ if (ret >= 0)
+ wdt->bootstatus = ret;
+ }
-static int
-ltq_wdt_remove(struct platform_device *pdev)
-{
- misc_deregister(&ltq_wdt_miscdev);
+ watchdog_set_nowayout(wdt, nowayout);
+ watchdog_init_timeout(wdt, 0, dev);
+
+ status = ltq_wdt_r32(priv, LTQ_WDT_SR);
+ if (status & LTQ_WDT_SR_EN) {
+ /*
+ * If the watchdog is already running overwrite it with our
+ * new settings. Stop is not needed as the start call will
+ * replace all settings anyway.
+ */
+ ltq_wdt_start(wdt);
+ set_bit(WDOG_HW_RUNNING, &wdt->status);
+ }
- return 0;
+ return devm_watchdog_register_device(dev, wdt);
}
+static const struct ltq_wdt_hw ltq_wdt_xrx100 = {
+ .bootstatus_get = ltq_wdt_xrx_bootstatus_get,
+};
+
+static const struct ltq_wdt_hw ltq_wdt_falcon = {
+ .bootstatus_get = ltq_wdt_falcon_bootstatus_get,
+};
+
static const struct of_device_id ltq_wdt_match[] = {
- { .compatible = "lantiq,wdt", .data = NULL},
- { .compatible = "lantiq,xrx100-wdt", .data = ltq_wdt_bootstatus_xrx },
- { .compatible = "lantiq,falcon-wdt", .data = ltq_wdt_bootstatus_falcon },
+ { .compatible = "lantiq,wdt", .data = NULL },
+ { .compatible = "lantiq,xrx100-wdt", .data = &ltq_wdt_xrx100 },
+ { .compatible = "lantiq,falcon-wdt", .data = &ltq_wdt_falcon },
{},
};
MODULE_DEVICE_TABLE(of, ltq_wdt_match);
static struct platform_driver ltq_wdt_driver = {
.probe = ltq_wdt_probe,
- .remove = ltq_wdt_remove,
.driver = {
.name = "wdt",
.of_match_table = ltq_wdt_match,
diff --git a/drivers/watchdog/mpc8xxx_wdt.c b/drivers/watchdog/mpc8xxx_wdt.c
index aca2d6323f8a..069072e6747d 100644
--- a/drivers/watchdog/mpc8xxx_wdt.c
+++ b/drivers/watchdog/mpc8xxx_wdt.c
@@ -17,8 +17,6 @@
* option) any later version.
*/
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/kernel.h>
@@ -49,6 +47,7 @@ struct mpc8xxx_wdt {
struct mpc8xxx_wdt_type {
int prescaler;
bool hw_enabled;
+ u32 rsr_mask;
};
struct mpc8xxx_wdt_ddata {
@@ -137,36 +136,55 @@ static int mpc8xxx_wdt_probe(struct platform_device *ofdev)
struct mpc8xxx_wdt_ddata *ddata;
u32 freq = fsl_get_sys_freq();
bool enabled;
+ struct device *dev = &ofdev->dev;
- wdt_type = of_device_get_match_data(&ofdev->dev);
+ wdt_type = of_device_get_match_data(dev);
if (!wdt_type)
return -EINVAL;
if (!freq || freq == -1)
return -EINVAL;
- ddata = devm_kzalloc(&ofdev->dev, sizeof(*ddata), GFP_KERNEL);
+ ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL);
if (!ddata)
return -ENOMEM;
res = platform_get_resource(ofdev, IORESOURCE_MEM, 0);
- ddata->base = devm_ioremap_resource(&ofdev->dev, res);
+ ddata->base = devm_ioremap_resource(dev, res);
if (IS_ERR(ddata->base))
return PTR_ERR(ddata->base);
enabled = in_be32(&ddata->base->swcrr) & SWCRR_SWEN;
if (!enabled && wdt_type->hw_enabled) {
- pr_info("could not be enabled in software\n");
+ dev_info(dev, "could not be enabled in software\n");
return -ENODEV;
}
+ res = platform_get_resource(ofdev, IORESOURCE_MEM, 1);
+ if (res) {
+ bool status;
+ u32 __iomem *rsr = ioremap(res->start, resource_size(res));
+
+ if (!rsr)
+ return -ENOMEM;
+
+ status = in_be32(rsr) & wdt_type->rsr_mask;
+ ddata->wdd.bootstatus = status ? WDIOF_CARDRESET : 0;
+ /* clear reset status bits related to watchdog timer */
+ out_be32(rsr, wdt_type->rsr_mask);
+ iounmap(rsr);
+
+ dev_info(dev, "Last boot was %scaused by watchdog\n",
+ status ? "" : "not ");
+ }
+
spin_lock_init(&ddata->lock);
ddata->wdd.info = &mpc8xxx_wdt_info,
ddata->wdd.ops = &mpc8xxx_wdt_ops,
ddata->wdd.timeout = WATCHDOG_TIMEOUT;
- watchdog_init_timeout(&ddata->wdd, timeout, &ofdev->dev);
+ watchdog_init_timeout(&ddata->wdd, timeout, dev);
watchdog_set_nowayout(&ddata->wdd, nowayout);
@@ -189,12 +207,13 @@ static int mpc8xxx_wdt_probe(struct platform_device *ofdev)
ret = watchdog_register_device(&ddata->wdd);
if (ret) {
- pr_err("cannot register watchdog device (err=%d)\n", ret);
+ dev_err(dev, "cannot register watchdog device (err=%d)\n", ret);
return ret;
}
- pr_info("WDT driver for MPC8xxx initialized. mode:%s timeout=%d sec\n",
- reset ? "reset" : "interrupt", ddata->wdd.timeout);
+ dev_info(dev,
+ "WDT driver for MPC8xxx initialized. mode:%s timeout=%d sec\n",
+ reset ? "reset" : "interrupt", ddata->wdd.timeout);
platform_set_drvdata(ofdev, ddata);
return 0;
@@ -204,8 +223,8 @@ static int mpc8xxx_wdt_remove(struct platform_device *ofdev)
{
struct mpc8xxx_wdt_ddata *ddata = platform_get_drvdata(ofdev);
- pr_crit("Watchdog removed, expect the %s soon!\n",
- reset ? "reset" : "machine check exception");
+ dev_crit(&ofdev->dev, "Watchdog removed, expect the %s soon!\n",
+ reset ? "reset" : "machine check exception");
watchdog_unregister_device(&ddata->wdd);
return 0;
@@ -216,6 +235,7 @@ static const struct of_device_id mpc8xxx_wdt_match[] = {
.compatible = "mpc83xx_wdt",
.data = &(struct mpc8xxx_wdt_type) {
.prescaler = 0x10000,
+ .rsr_mask = BIT(3), /* RSR Bit SWRS */
},
},
{
@@ -223,6 +243,7 @@ static const struct of_device_id mpc8xxx_wdt_match[] = {
.data = &(struct mpc8xxx_wdt_type) {
.prescaler = 0x10000,
.hw_enabled = true,
+ .rsr_mask = BIT(20), /* RSTRSCR Bit WDT_RR */
},
},
{
@@ -230,6 +251,7 @@ static const struct of_device_id mpc8xxx_wdt_match[] = {
.data = &(struct mpc8xxx_wdt_type) {
.prescaler = 0x800,
.hw_enabled = true,
+ .rsr_mask = BIT(28), /* RSR Bit SWRS */
},
},
{},
diff --git a/drivers/watchdog/renesas_wdt.c b/drivers/watchdog/renesas_wdt.c
index 88d81feba4e6..0d74c3e48979 100644
--- a/drivers/watchdog/renesas_wdt.c
+++ b/drivers/watchdog/renesas_wdt.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Watchdog driver for Renesas WDT watchdog
*
* Copyright (C) 2015-17 Wolfram Sang, Sang Engineering <wsa@sang-engineering.com>
* Copyright (C) 2015-17 Renesas Electronics Corporation
- *
- * 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.
*/
#include <linux/bitops.h>
#include <linux/clk.h>
@@ -234,6 +231,7 @@ static int rwdt_probe(struct platform_device *pdev)
watchdog_set_drvdata(&priv->wdev, priv);
watchdog_set_nowayout(&priv->wdev, nowayout);
watchdog_set_restart_priority(&priv->wdev, 0);
+ watchdog_stop_on_unregister(&priv->wdev);
/* This overrides the default timeout only if DT configuration was found */
ret = watchdog_init_timeout(&priv->wdev, 0, &pdev->dev);
diff --git a/drivers/watchdog/rza_wdt.c b/drivers/watchdog/rza_wdt.c
index e618218d2374..781bb572e6af 100644
--- a/drivers/watchdog/rza_wdt.c
+++ b/drivers/watchdog/rza_wdt.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Renesas RZ/A Series WDT Driver
*
* Copyright (C) 2017 Renesas Electronics America, Inc.
* Copyright (C) 2017 Chris Brandt
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file "COPYING" in the main directory of this archive
- * for more details.
*/
#include <linux/bitops.h>
@@ -14,6 +11,7 @@
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/of_address.h>
+#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/watchdog.h>
@@ -34,12 +32,45 @@
#define WRCSR_RSTE BIT(6)
#define WRCSR_CLEAR_WOVF 0xA500 /* special value */
+/* The maximum CKS register setting value to get the longest timeout */
+#define CKS_3BIT 0x7
+#define CKS_4BIT 0xF
+
+#define DIVIDER_3BIT 16384 /* Clock divider when CKS = 0x7 */
+#define DIVIDER_4BIT 4194304 /* Clock divider when CKS = 0xF */
+
struct rza_wdt {
struct watchdog_device wdev;
void __iomem *base;
struct clk *clk;
+ u8 count;
+ u8 cks;
};
+static void rza_wdt_calc_timeout(struct rza_wdt *priv, int timeout)
+{
+ unsigned long rate = clk_get_rate(priv->clk);
+ unsigned int ticks;
+
+ if (priv->cks == CKS_4BIT) {
+ ticks = DIV_ROUND_UP(timeout * rate, DIVIDER_4BIT);
+
+ /*
+ * Since max_timeout was set in probe, we know that the timeout
+ * value passed will never calculate to a tick value greater
+ * than 256.
+ */
+ priv->count = 256 - ticks;
+
+ } else {
+ /* Start timer with longest timeout */
+ priv->count = 0;
+ }
+
+ pr_debug("%s: timeout set to %u (WTCNT=%d)\n", __func__,
+ timeout, priv->count);
+}
+
static int rza_wdt_start(struct watchdog_device *wdev)
{
struct rza_wdt *priv = watchdog_get_drvdata(wdev);
@@ -51,13 +82,12 @@ static int rza_wdt_start(struct watchdog_device *wdev)
readb(priv->base + WRCSR);
writew(WRCSR_CLEAR_WOVF, priv->base + WRCSR);
- /*
- * Start timer with slowest clock source and reset option enabled.
- */
+ rza_wdt_calc_timeout(priv, wdev->timeout);
+
writew(WRCSR_MAGIC | WRCSR_RSTE, priv->base + WRCSR);
- writew(WTCNT_MAGIC | 0, priv->base + WTCNT);
- writew(WTCSR_MAGIC | WTSCR_WT | WTSCR_TME | WTSCR_CKS(7),
- priv->base + WTCSR);
+ writew(WTCNT_MAGIC | priv->count, priv->base + WTCNT);
+ writew(WTCSR_MAGIC | WTSCR_WT | WTSCR_TME |
+ WTSCR_CKS(priv->cks), priv->base + WTCSR);
return 0;
}
@@ -75,8 +105,17 @@ static int rza_wdt_ping(struct watchdog_device *wdev)
{
struct rza_wdt *priv = watchdog_get_drvdata(wdev);
- writew(WTCNT_MAGIC | 0, priv->base + WTCNT);
+ writew(WTCNT_MAGIC | priv->count, priv->base + WTCNT);
+
+ pr_debug("%s: timeout = %u\n", __func__, wdev->timeout);
+
+ return 0;
+}
+static int rza_set_timeout(struct watchdog_device *wdev, unsigned int timeout)
+{
+ wdev->timeout = timeout;
+ rza_wdt_start(wdev);
return 0;
}
@@ -121,6 +160,7 @@ static const struct watchdog_ops rza_wdt_ops = {
.start = rza_wdt_start,
.stop = rza_wdt_stop,
.ping = rza_wdt_ping,
+ .set_timeout = rza_set_timeout,
.restart = rza_wdt_restart,
};
@@ -150,20 +190,28 @@ static int rza_wdt_probe(struct platform_device *pdev)
return -ENOENT;
}
- /* Assume slowest clock rate possible (CKS=7) */
- rate /= 16384;
-
priv->wdev.info = &rza_wdt_ident,
priv->wdev.ops = &rza_wdt_ops,
priv->wdev.parent = &pdev->dev;
- /*
- * Since the max possible timeout of our 8-bit count register is less
- * than a second, we must use max_hw_heartbeat_ms.
- */
- priv->wdev.max_hw_heartbeat_ms = (1000 * U8_MAX) / rate;
- dev_dbg(&pdev->dev, "max hw timeout of %dms\n",
- priv->wdev.max_hw_heartbeat_ms);
+ priv->cks = (u8)(uintptr_t)of_device_get_match_data(&pdev->dev);
+ if (priv->cks == CKS_4BIT) {
+ /* Assume slowest clock rate possible (CKS=0xF) */
+ priv->wdev.max_timeout = (DIVIDER_4BIT * U8_MAX) / rate;
+
+ } else if (priv->cks == CKS_3BIT) {
+ /* Assume slowest clock rate possible (CKS=7) */
+ rate /= DIVIDER_3BIT;
+
+ /*
+ * Since the max possible timeout of our 8-bit count
+ * register is less than a second, we must use
+ * max_hw_heartbeat_ms.
+ */
+ priv->wdev.max_hw_heartbeat_ms = (1000 * U8_MAX) / rate;
+ dev_dbg(&pdev->dev, "max hw timeout of %dms\n",
+ priv->wdev.max_hw_heartbeat_ms);
+ }
priv->wdev.min_timeout = 1;
priv->wdev.timeout = DEFAULT_TIMEOUT;
@@ -179,7 +227,8 @@ static int rza_wdt_probe(struct platform_device *pdev)
}
static const struct of_device_id rza_wdt_of_match[] = {
- { .compatible = "renesas,rza-wdt", },
+ { .compatible = "renesas,r7s9210-wdt", .data = (void *)CKS_4BIT, },
+ { .compatible = "renesas,rza-wdt", .data = (void *)CKS_3BIT, },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, rza_wdt_of_match);
diff --git a/drivers/watchdog/sama5d4_wdt.c b/drivers/watchdog/sama5d4_wdt.c
index 255169916dbb..1e93c1b0e3cf 100644
--- a/drivers/watchdog/sama5d4_wdt.c
+++ b/drivers/watchdog/sama5d4_wdt.c
@@ -247,11 +247,7 @@ static int sama5d4_wdt_probe(struct platform_device *pdev)
}
}
- ret = watchdog_init_timeout(wdd, wdt_timeout, &pdev->dev);
- if (ret) {
- dev_err(&pdev->dev, "unable to set timeout value\n");
- return ret;
- }
+ watchdog_init_timeout(wdd, wdt_timeout, &pdev->dev);
timeout = WDT_SEC2TICKS(wdd->timeout);
diff --git a/drivers/watchdog/ts4800_wdt.c b/drivers/watchdog/ts4800_wdt.c
index 2b8de8602b67..89843b16b04a 100644
--- a/drivers/watchdog/ts4800_wdt.c
+++ b/drivers/watchdog/ts4800_wdt.c
@@ -135,6 +135,7 @@ static int ts4800_wdt_probe(struct platform_device *pdev)
/* set regmap and offset to know where to write */
wdt->feed_offset = reg;
wdt->regmap = syscon_node_to_regmap(syscon_np);
+ of_node_put(syscon_np);
if (IS_ERR(wdt->regmap)) {
dev_err(&pdev->dev, "cannot get parent's regmap\n");
return PTR_ERR(wdt->regmap);
diff --git a/drivers/watchdog/via_wdt.c b/drivers/watchdog/via_wdt.c
index b085ef1084ec..d56d0a927a1e 100644
--- a/drivers/watchdog/via_wdt.c
+++ b/drivers/watchdog/via_wdt.c
@@ -30,7 +30,7 @@
#define VIA_WDT_CONF_MMIO 0x02 /* 1: enable watchdog MMIO */
/*
- * The MMIO region contains the watchog control register and the
+ * The MMIO region contains the watchdog control register and the
* hardware timer counter.
*/
#define VIA_WDT_MMIO_LEN 8 /* MMIO region length in bytes */
@@ -82,7 +82,7 @@ static inline void wdt_reset(void)
/*
* Timer tick: the timer will make sure that the watchdog timer hardware
* is being reset in time. The conditions to do this are:
- * 1) the watchog timer has been started and /dev/watchdog is open
+ * 1) the watchdog timer has been started and /dev/watchdog is open
* and there is still time left before userspace should send the
* next heartbeat/ping. (note: the internal heartbeat is much smaller
* then the external/userspace heartbeat).
diff --git a/drivers/watchdog/w83627hf_wdt.c b/drivers/watchdog/w83627hf_wdt.c
index 7817836bff55..4b9365d4de7a 100644
--- a/drivers/watchdog/w83627hf_wdt.c
+++ b/drivers/watchdog/w83627hf_wdt.c
@@ -50,7 +50,7 @@ static int cr_wdt_csr; /* WDT control & status register */
enum chips { w83627hf, w83627s, w83697hf, w83697ug, w83637hf, w83627thf,
w83687thf, w83627ehf, w83627dhg, w83627uhg, w83667hg, w83627dhg_p,
w83667hg_b, nct6775, nct6776, nct6779, nct6791, nct6792, nct6793,
- nct6795, nct6102 };
+ nct6795, nct6796, nct6102 };
static int timeout; /* in seconds */
module_param(timeout, int, 0);
@@ -100,6 +100,7 @@ MODULE_PARM_DESC(early_disable, "Disable watchdog at boot time (default=0)");
#define NCT6792_ID 0xc9
#define NCT6793_ID 0xd1
#define NCT6795_ID 0xd3
+#define NCT6796_ID 0xd4 /* also NCT9697D, NCT9698D */
#define W83627HF_WDT_TIMEOUT 0xf6
#define W83697HF_WDT_TIMEOUT 0xf4
@@ -209,6 +210,7 @@ static int w83627hf_init(struct watchdog_device *wdog, enum chips chip)
case nct6792:
case nct6793:
case nct6795:
+ case nct6796:
case nct6102:
/*
* These chips have a fixed WDTO# output pin (W83627UHG),
@@ -407,6 +409,9 @@ static int wdt_find(int addr)
case NCT6795_ID:
ret = nct6795;
break;
+ case NCT6796_ID:
+ ret = nct6796;
+ break;
case NCT6102_ID:
ret = nct6102;
cr_wdt_timeout = NCT6102D_WDT_TIMEOUT;
@@ -450,6 +455,7 @@ static int __init wdt_init(void)
"NCT6792",
"NCT6793",
"NCT6795",
+ "NCT6796",
"NCT6102",
};
diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c
index ffbdc4642ea5..f6c24b22b37c 100644
--- a/drivers/watchdog/watchdog_dev.c
+++ b/drivers/watchdog/watchdog_dev.c
@@ -1019,16 +1019,16 @@ static void watchdog_cdev_unregister(struct watchdog_device *wdd)
old_wd_data = NULL;
}
- mutex_lock(&wd_data->lock);
- wd_data->wdd = NULL;
- wdd->wd_data = NULL;
- mutex_unlock(&wd_data->lock);
-
if (watchdog_active(wdd) &&
test_bit(WDOG_STOP_ON_UNREGISTER, &wdd->status)) {
watchdog_stop(wdd);
}
+ mutex_lock(&wd_data->lock);
+ wd_data->wdd = NULL;
+ wdd->wd_data = NULL;
+ mutex_unlock(&wd_data->lock);
+
hrtimer_cancel(&wd_data->timer);
kthread_cancel_work_sync(&wd_data->work);