summaryrefslogtreecommitdiff
path: root/drivers/watchdog
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/watchdog')
-rw-r--r--drivers/watchdog/Kconfig52
-rw-r--r--drivers/watchdog/Makefile3
-rw-r--r--drivers/watchdog/at91rm9200_wdt.c61
-rw-r--r--drivers/watchdog/at91sam9_wdt.c4
-rw-r--r--drivers/watchdog/bcm2835_wdt.c62
-rw-r--r--drivers/watchdog/bcm_kona_wdt.c27
-rw-r--r--drivers/watchdog/da9062_wdt.c253
-rw-r--r--drivers/watchdog/diag288_wdt.c20
-rw-r--r--drivers/watchdog/digicolor_wdt.c205
-rw-r--r--drivers/watchdog/dw_wdt.c8
-rw-r--r--drivers/watchdog/gpio_wdt.c9
-rw-r--r--drivers/watchdog/hpwdt.c16
-rw-r--r--drivers/watchdog/iTCO_wdt.c51
-rw-r--r--drivers/watchdog/imgpdc_wdt.c84
-rw-r--r--drivers/watchdog/imx2_wdt.c18
-rw-r--r--drivers/watchdog/max63xx_wdt.c172
-rw-r--r--drivers/watchdog/mena21_wdt.c5
-rw-r--r--drivers/watchdog/octeon-wdt-main.c201
-rw-r--r--drivers/watchdog/omap_wdt.c92
-rw-r--r--drivers/watchdog/omap_wdt.h1
-rw-r--r--drivers/watchdog/pnx4008_wdt.c2
-rw-r--r--drivers/watchdog/qcom-wdt.c21
-rw-r--r--drivers/watchdog/sp805_wdt.c4
-rw-r--r--drivers/watchdog/st_lpc_wdt.c344
-rw-r--r--drivers/watchdog/stmp3xxx_rtc_wdt.c4
-rw-r--r--drivers/watchdog/watchdog_core.c118
26 files changed, 1484 insertions, 353 deletions
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 16f202350997..241fafde42cb 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -1,3 +1,4 @@
+
#
# Watchdog device configuration
#
@@ -96,6 +97,15 @@ config DA9063_WATCHDOG
This driver can be built as a module. The module name is da9063_wdt.
+config DA9062_WATCHDOG
+ tristate "Dialog DA9062 Watchdog"
+ depends on MFD_DA9062
+ select WATCHDOG_CORE
+ help
+ Support for the watchdog in the DA9062 PMIC.
+
+ This driver can be built as a module. The module name is da9062_wdt.
+
config GPIO_WATCHDOG
tristate "Watchdog device controlled through GPIO-line"
depends on OF_GPIO
@@ -104,6 +114,17 @@ config GPIO_WATCHDOG
If you say yes here you get support for watchdog device
controlled through GPIO-line.
+config GPIO_WATCHDOG_ARCH_INITCALL
+ bool "Register the watchdog as early as possible"
+ depends on GPIO_WATCHDOG=y
+ help
+ In some situations, the default initcall level (module_init)
+ in not early enough in the boot process to avoid the watchdog
+ to be triggered.
+ If you say yes here, the initcall level would be raised to
+ arch_initcall.
+ If in doubt, say N.
+
config MENF21BMC_WATCHDOG
tristate "MEN 14F021P00 BMC Watchdog"
depends on MFD_MENF21BMC
@@ -154,7 +175,7 @@ config ARM_SP805_WATCHDOG
config AT91RM9200_WATCHDOG
tristate "AT91RM9200 watchdog"
- depends on SOC_AT91RM9200
+ depends on SOC_AT91RM9200 && MFD_SYSCON
help
Watchdog timer embedded into AT91RM9200 chips. This will reboot your
system when the timeout is reached.
@@ -169,7 +190,7 @@ config AT91SAM9X_WATCHDOG
config CADENCE_WATCHDOG
tristate "Cadence Watchdog Timer"
- depends on ARM
+ depends on HAS_IOMEM
select WATCHDOG_CORE
help
Say Y here if you want to include support for the watchdog
@@ -409,7 +430,7 @@ config TS72XX_WATCHDOG
config MAX63XX_WATCHDOG
tristate "Max63xx watchdog"
- depends on ARM && HAS_IOMEM
+ depends on HAS_IOMEM
select WATCHDOG_CORE
help
Support for memory mapped max63{69,70,71,72,73,74} watchdog timer.
@@ -471,6 +492,18 @@ config SIRFSOC_WATCHDOG
Support for CSR SiRFprimaII and SiRFatlasVI watchdog. When
the watchdog triggers the system will be reset.
+config ST_LPC_WATCHDOG
+ tristate "STMicroelectronics LPC Watchdog"
+ depends on ARCH_STI
+ depends on OF
+ select WATCHDOG_CORE
+ help
+ Say Y here to include STMicroelectronics Low Power Controller
+ (LPC) based Watchdog timer support.
+
+ To compile this driver as a module, choose M here: the
+ module will be called st_lpc_wdt.
+
config TEGRA_WATCHDOG
tristate "Tegra watchdog"
depends on (ARCH_TEGRA || COMPILE_TEST) && HAS_IOMEM
@@ -515,6 +548,16 @@ config MEDIATEK_WATCHDOG
To compile this driver as a module, choose M here: the
module will be called mtk_wdt.
+config DIGICOLOR_WATCHDOG
+ tristate "Conexant Digicolor SoCs watchdog support"
+ depends on ARCH_DIGICOLOR
+ select WATCHDOG_CORE
+ help
+ Say Y here to include support for the watchdog timer
+ in Conexant Digicolor SoCs.
+ To compile this driver as a module, choose M here: the
+ module will be called digicolor_wdt.
+
# AVR32 Architecture
config AT32AP700X_WDT
@@ -1190,6 +1233,7 @@ config OCTEON_WDT
tristate "Cavium OCTEON SOC family Watchdog Timer"
depends on CAVIUM_OCTEON_SOC
default y
+ select WATCHDOG_CORE
select EXPORT_UASM if OCTEON_WDT = m
help
Hardware driver for OCTEON's on chip watchdog timer.
@@ -1343,7 +1387,7 @@ config BOOKE_WDT_DEFAULT_TIMEOUT
config MEN_A21_WDT
tristate "MEN A21 VME CPU Carrier Board Watchdog Timer"
select WATCHDOG_CORE
- depends on GPIOLIB
+ depends on GPIOLIB || COMPILE_TEST
help
Watchdog driver for MEN A21 VMEbus CPU Carrier Boards.
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 5c19294d1c30..59ea9a1b8e76 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -59,11 +59,13 @@ obj-$(CONFIG_RETU_WATCHDOG) += retu_wdt.o
obj-$(CONFIG_BCM2835_WDT) += bcm2835_wdt.o
obj-$(CONFIG_MOXART_WDT) += moxart_wdt.o
obj-$(CONFIG_SIRFSOC_WATCHDOG) += sirfsoc_wdt.o
+obj-$(CONFIG_ST_LPC_WATCHDOG) += st_lpc_wdt.o
obj-$(CONFIG_QCOM_WDT) += qcom-wdt.o
obj-$(CONFIG_BCM_KONA_WDT) += bcm_kona_wdt.o
obj-$(CONFIG_TEGRA_WATCHDOG) += tegra_wdt.o
obj-$(CONFIG_MESON_WATCHDOG) += meson_wdt.o
obj-$(CONFIG_MEDIATEK_WATCHDOG) += mtk_wdt.o
+obj-$(CONFIG_DIGICOLOR_WATCHDOG) += digicolor_wdt.o
# AVR32 Architecture
obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o
@@ -179,6 +181,7 @@ obj-$(CONFIG_XEN_WDT) += xen_wdt.o
# Architecture Independent
obj-$(CONFIG_DA9052_WATCHDOG) += da9052_wdt.o
obj-$(CONFIG_DA9055_WATCHDOG) += da9055_wdt.o
+obj-$(CONFIG_DA9062_WATCHDOG) += da9062_wdt.o
obj-$(CONFIG_DA9063_WATCHDOG) += da9063_wdt.o
obj-$(CONFIG_GPIO_WATCHDOG) += gpio_wdt.o
obj-$(CONFIG_WM831X_WATCHDOG) += wm831x_wdt.o
diff --git a/drivers/watchdog/at91rm9200_wdt.c b/drivers/watchdog/at91rm9200_wdt.c
index d244112d5b6f..41cecb55766c 100644
--- a/drivers/watchdog/at91rm9200_wdt.c
+++ b/drivers/watchdog/at91rm9200_wdt.c
@@ -12,27 +12,32 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/bitops.h>
+#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/mfd/syscon/atmel-st.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/platform_device.h>
+#include <linux/reboot.h>
+#include <linux/regmap.h>
#include <linux/types.h>
#include <linux/watchdog.h>
#include <linux/uaccess.h>
#include <linux/of.h>
#include <linux/of_device.h>
-#include <mach/at91_st.h>
#define WDT_DEFAULT_TIME 5 /* seconds */
#define WDT_MAX_TIME 256 /* seconds */
static int wdt_time = WDT_DEFAULT_TIME;
static bool nowayout = WATCHDOG_NOWAYOUT;
+static struct regmap *regmap_st;
module_param(wdt_time, int, 0);
MODULE_PARM_DESC(wdt_time, "Watchdog time in seconds. (default="
@@ -50,12 +55,33 @@ static unsigned long at91wdt_busy;
/* ......................................................................... */
+static int at91rm9200_restart(struct notifier_block *this,
+ unsigned long mode, void *cmd)
+{
+ /*
+ * Perform a hardware reset with the use of the Watchdog timer.
+ */
+ regmap_write(regmap_st, AT91_ST_WDMR,
+ AT91_ST_RSTEN | AT91_ST_EXTEN | 1);
+ regmap_write(regmap_st, AT91_ST_CR, AT91_ST_WDRST);
+
+ mdelay(2000);
+
+ pr_emerg("Unable to restart system\n");
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block at91rm9200_restart_nb = {
+ .notifier_call = at91rm9200_restart,
+ .priority = 192,
+};
+
/*
* Disable the watchdog.
*/
static inline void at91_wdt_stop(void)
{
- at91_st_write(AT91_ST_WDMR, AT91_ST_EXTEN);
+ regmap_write(regmap_st, AT91_ST_WDMR, AT91_ST_EXTEN);
}
/*
@@ -63,9 +89,9 @@ static inline void at91_wdt_stop(void)
*/
static inline void at91_wdt_start(void)
{
- at91_st_write(AT91_ST_WDMR, AT91_ST_EXTEN | AT91_ST_RSTEN |
+ regmap_write(regmap_st, AT91_ST_WDMR, AT91_ST_EXTEN | AT91_ST_RSTEN |
(((65536 * wdt_time) >> 8) & AT91_ST_WDV));
- at91_st_write(AT91_ST_CR, AT91_ST_WDRST);
+ regmap_write(regmap_st, AT91_ST_CR, AT91_ST_WDRST);
}
/*
@@ -73,7 +99,7 @@ static inline void at91_wdt_start(void)
*/
static inline void at91_wdt_reload(void)
{
- at91_st_write(AT91_ST_CR, AT91_ST_WDRST);
+ regmap_write(regmap_st, AT91_ST_CR, AT91_ST_WDRST);
}
/* ......................................................................... */
@@ -203,16 +229,32 @@ static struct miscdevice at91wdt_miscdev = {
static int at91wdt_probe(struct platform_device *pdev)
{
+ struct device *dev = &pdev->dev;
+ struct device *parent;
int res;
if (at91wdt_miscdev.parent)
return -EBUSY;
at91wdt_miscdev.parent = &pdev->dev;
+ parent = dev->parent;
+ if (!parent) {
+ dev_err(dev, "no parent\n");
+ return -ENODEV;
+ }
+
+ regmap_st = syscon_node_to_regmap(parent->of_node);
+ if (!regmap_st)
+ return -ENODEV;
+
res = misc_register(&at91wdt_miscdev);
if (res)
return res;
+ res = register_restart_handler(&at91rm9200_restart_nb);
+ if (res)
+ dev_warn(dev, "failed to register restart handler\n");
+
pr_info("AT91 Watchdog Timer enabled (%d seconds%s)\n",
wdt_time, nowayout ? ", nowayout" : "");
return 0;
@@ -220,8 +262,13 @@ static int at91wdt_probe(struct platform_device *pdev)
static int at91wdt_remove(struct platform_device *pdev)
{
+ struct device *dev = &pdev->dev;
int res;
+ res = unregister_restart_handler(&at91rm9200_restart_nb);
+ if (res)
+ dev_warn(dev, "failed to unregister restart handler\n");
+
res = misc_deregister(&at91wdt_miscdev);
if (!res)
at91wdt_miscdev.parent = NULL;
@@ -267,7 +314,7 @@ static struct platform_driver at91wdt_driver = {
.suspend = at91wdt_suspend,
.resume = at91wdt_resume,
.driver = {
- .name = "at91_wdt",
+ .name = "atmel_st_watchdog",
.of_match_table = at91_wdt_dt_ids,
},
};
@@ -296,4 +343,4 @@ module_exit(at91_wdt_exit);
MODULE_AUTHOR("Andrew Victor");
MODULE_DESCRIPTION("Watchdog driver for Atmel AT91RM9200");
MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:at91_wdt");
+MODULE_ALIAS("platform:atmel_st_watchdog");
diff --git a/drivers/watchdog/at91sam9_wdt.c b/drivers/watchdog/at91sam9_wdt.c
index 1443b3c391de..e4698f7c5f93 100644
--- a/drivers/watchdog/at91sam9_wdt.c
+++ b/drivers/watchdog/at91sam9_wdt.c
@@ -40,9 +40,9 @@
#define DRV_NAME "AT91SAM9 Watchdog"
#define wdt_read(wdt, field) \
- __raw_readl((wdt)->base + (field))
+ readl_relaxed((wdt)->base + (field))
#define wdt_write(wtd, field, val) \
- __raw_writel((val), (wdt)->base + (field))
+ writel_relaxed((val), (wdt)->base + (field))
/* AT91SAM9 watchdog runs a 12bit counter @ 256Hz,
* use this to convert a watchdog
diff --git a/drivers/watchdog/bcm2835_wdt.c b/drivers/watchdog/bcm2835_wdt.c
index 2b5a9bbf80b7..7116968dee12 100644
--- a/drivers/watchdog/bcm2835_wdt.c
+++ b/drivers/watchdog/bcm2835_wdt.c
@@ -13,20 +13,25 @@
* option) any later version.
*/
+#include <linux/delay.h>
+#include <linux/reboot.h>
#include <linux/types.h>
#include <linux/module.h>
#include <linux/io.h>
#include <linux/watchdog.h>
#include <linux/platform_device.h>
#include <linux/of_address.h>
+#include <linux/of_platform.h>
#define PM_RSTC 0x1c
+#define PM_RSTS 0x20
#define PM_WDOG 0x24
#define PM_PASSWORD 0x5a000000
#define PM_WDOG_TIME_SET 0x000fffff
#define PM_RSTC_WRCFG_CLR 0xffffffcf
+#define PM_RSTS_HADWRH_SET 0x00000040
#define PM_RSTC_WRCFG_SET 0x00000030
#define PM_RSTC_WRCFG_FULL_RESET 0x00000020
#define PM_RSTC_RESET 0x00000102
@@ -37,6 +42,7 @@
struct bcm2835_wdt {
void __iomem *base;
spinlock_t lock;
+ struct notifier_block restart_handler;
};
static unsigned int heartbeat;
@@ -106,6 +112,53 @@ static struct watchdog_device bcm2835_wdt_wdd = {
.timeout = WDOG_TICKS_TO_SECS(PM_WDOG_TIME_SET),
};
+static int
+bcm2835_restart(struct notifier_block *this, unsigned long mode, void *cmd)
+{
+ struct bcm2835_wdt *wdt = container_of(this, struct bcm2835_wdt,
+ restart_handler);
+ u32 val;
+
+ /* use a timeout of 10 ticks (~150us) */
+ writel_relaxed(10 | PM_PASSWORD, wdt->base + PM_WDOG);
+ val = readl_relaxed(wdt->base + PM_RSTC);
+ val &= PM_RSTC_WRCFG_CLR;
+ val |= PM_PASSWORD | PM_RSTC_WRCFG_FULL_RESET;
+ writel_relaxed(val, wdt->base + PM_RSTC);
+
+ /* No sleeping, possibly atomic. */
+ mdelay(1);
+
+ return 0;
+}
+
+/*
+ * We can't really power off, but if we do the normal reset scheme, and
+ * indicate to bootcode.bin not to reboot, then most of the chip will be
+ * powered off.
+ */
+static void bcm2835_power_off(void)
+{
+ struct device_node *np =
+ of_find_compatible_node(NULL, NULL, "brcm,bcm2835-pm-wdt");
+ struct platform_device *pdev = of_find_device_by_node(np);
+ struct bcm2835_wdt *wdt = platform_get_drvdata(pdev);
+ u32 val;
+
+ /*
+ * We set the watchdog hard reset bit here to distinguish this reset
+ * from the normal (full) reset. bootcode.bin will not reboot after a
+ * hard reset.
+ */
+ val = readl_relaxed(wdt->base + PM_RSTS);
+ val &= PM_RSTC_WRCFG_CLR;
+ val |= PM_PASSWORD | PM_RSTS_HADWRH_SET;
+ writel_relaxed(val, wdt->base + PM_RSTS);
+
+ /* Continue with normal reset mechanism */
+ bcm2835_restart(&wdt->restart_handler, REBOOT_HARD, NULL);
+}
+
static int bcm2835_wdt_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -136,6 +189,12 @@ static int bcm2835_wdt_probe(struct platform_device *pdev)
return err;
}
+ wdt->restart_handler.notifier_call = bcm2835_restart;
+ wdt->restart_handler.priority = 128;
+ register_restart_handler(&wdt->restart_handler);
+ if (pm_power_off == NULL)
+ pm_power_off = bcm2835_power_off;
+
dev_info(dev, "Broadcom BCM2835 watchdog timer");
return 0;
}
@@ -144,6 +203,9 @@ static int bcm2835_wdt_remove(struct platform_device *pdev)
{
struct bcm2835_wdt *wdt = platform_get_drvdata(pdev);
+ unregister_restart_handler(&wdt->restart_handler);
+ if (pm_power_off == bcm2835_power_off)
+ pm_power_off = NULL;
watchdog_unregister_device(&bcm2835_wdt_wdd);
iounmap(wdt->base);
diff --git a/drivers/watchdog/bcm_kona_wdt.c b/drivers/watchdog/bcm_kona_wdt.c
index 4e37db3539a4..22d8ae65772a 100644
--- a/drivers/watchdog/bcm_kona_wdt.c
+++ b/drivers/watchdog/bcm_kona_wdt.c
@@ -99,12 +99,14 @@ static int secure_register_read(struct bcm_kona_wdt *wdt, uint32_t offset)
static int bcm_kona_wdt_dbg_show(struct seq_file *s, void *data)
{
- int ctl_val, cur_val, ret;
+ int ctl_val, cur_val;
unsigned long flags;
struct bcm_kona_wdt *wdt = s->private;
- if (!wdt)
- return seq_puts(s, "No device pointer\n");
+ if (!wdt) {
+ seq_puts(s, "No device pointer\n");
+ return 0;
+ }
spin_lock_irqsave(&wdt->lock, flags);
ctl_val = secure_register_read(wdt, SECWDOG_CTRL_REG);
@@ -112,7 +114,7 @@ static int bcm_kona_wdt_dbg_show(struct seq_file *s, void *data)
spin_unlock_irqrestore(&wdt->lock, flags);
if (ctl_val < 0 || cur_val < 0) {
- ret = seq_puts(s, "Error accessing hardware\n");
+ seq_puts(s, "Error accessing hardware\n");
} else {
int ctl, cur, ctl_sec, cur_sec, res;
@@ -121,15 +123,18 @@ static int bcm_kona_wdt_dbg_show(struct seq_file *s, void *data)
cur = cur_val & SECWDOG_COUNT_MASK;
ctl_sec = TICKS_TO_SECS(ctl, wdt);
cur_sec = TICKS_TO_SECS(cur, wdt);
- ret = seq_printf(s, "Resolution: %d / %d\n"
- "Control: %d s / %d (%#x) ticks\n"
- "Current: %d s / %d (%#x) ticks\n"
- "Busy count: %lu\n", res,
- wdt->resolution, ctl_sec, ctl, ctl, cur_sec,
- cur, cur, wdt->busy_count);
+ seq_printf(s,
+ "Resolution: %d / %d\n"
+ "Control: %d s / %d (%#x) ticks\n"
+ "Current: %d s / %d (%#x) ticks\n"
+ "Busy count: %lu\n",
+ res, wdt->resolution,
+ ctl_sec, ctl, ctl,
+ cur_sec, cur, cur,
+ wdt->busy_count);
}
- return ret;
+ return 0;
}
static int bcm_kona_dbg_open(struct inode *inode, struct file *file)
diff --git a/drivers/watchdog/da9062_wdt.c b/drivers/watchdog/da9062_wdt.c
new file mode 100644
index 000000000000..b3a870ce85be
--- /dev/null
+++ b/drivers/watchdog/da9062_wdt.c
@@ -0,0 +1,253 @@
+/*
+ * da9062_wdt.c - WDT device driver for DA9062
+ * Copyright (C) 2015 Dialog Semiconductor Ltd.
+ *
+ * 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 <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/watchdog.h>
+#include <linux/platform_device.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/jiffies.h>
+#include <linux/mfd/da9062/registers.h>
+#include <linux/mfd/da9062/core.h>
+#include <linux/regmap.h>
+#include <linux/of.h>
+
+static const unsigned int wdt_timeout[] = { 0, 2, 4, 8, 16, 32, 65, 131 };
+#define DA9062_TWDSCALE_DISABLE 0
+#define DA9062_TWDSCALE_MIN 1
+#define DA9062_TWDSCALE_MAX (ARRAY_SIZE(wdt_timeout) - 1)
+#define DA9062_WDT_MIN_TIMEOUT wdt_timeout[DA9062_TWDSCALE_MIN]
+#define DA9062_WDT_MAX_TIMEOUT wdt_timeout[DA9062_TWDSCALE_MAX]
+#define DA9062_WDG_DEFAULT_TIMEOUT wdt_timeout[DA9062_TWDSCALE_MAX-1]
+#define DA9062_RESET_PROTECTION_MS 300
+
+struct da9062_watchdog {
+ struct da9062 *hw;
+ struct watchdog_device wdtdev;
+ unsigned long j_time_stamp;
+};
+
+static void da9062_set_window_start(struct da9062_watchdog *wdt)
+{
+ wdt->j_time_stamp = jiffies;
+}
+
+static void da9062_apply_window_protection(struct da9062_watchdog *wdt)
+{
+ unsigned long delay = msecs_to_jiffies(DA9062_RESET_PROTECTION_MS);
+ unsigned long timeout = wdt->j_time_stamp + delay;
+ unsigned long now = jiffies;
+ unsigned int diff_ms;
+
+ /* if time-limit has not elapsed then wait for remainder */
+ if (time_before(now, timeout)) {
+ diff_ms = jiffies_to_msecs(timeout-now);
+ dev_dbg(wdt->hw->dev,
+ "Kicked too quickly. Delaying %u msecs\n", diff_ms);
+ msleep(diff_ms);
+ }
+}
+
+static unsigned int da9062_wdt_timeout_to_sel(unsigned int secs)
+{
+ unsigned int i;
+
+ for (i = DA9062_TWDSCALE_MIN; i <= DA9062_TWDSCALE_MAX; i++) {
+ if (wdt_timeout[i] >= secs)
+ return i;
+ }
+
+ return DA9062_TWDSCALE_MAX;
+}
+
+static int da9062_reset_watchdog_timer(struct da9062_watchdog *wdt)
+{
+ int ret;
+
+ da9062_apply_window_protection(wdt);
+
+ ret = regmap_update_bits(wdt->hw->regmap,
+ DA9062AA_CONTROL_F,
+ DA9062AA_WATCHDOG_MASK,
+ DA9062AA_WATCHDOG_MASK);
+
+ da9062_set_window_start(wdt);
+
+ return ret;
+}
+
+static int da9062_wdt_update_timeout_register(struct da9062_watchdog *wdt,
+ unsigned int regval)
+{
+ struct da9062 *chip = wdt->hw;
+ int ret;
+
+ ret = da9062_reset_watchdog_timer(wdt);
+ if (ret)
+ return ret;
+
+ return regmap_update_bits(chip->regmap,
+ DA9062AA_CONTROL_D,
+ DA9062AA_TWDSCALE_MASK,
+ regval);
+}
+
+static int da9062_wdt_start(struct watchdog_device *wdd)
+{
+ struct da9062_watchdog *wdt = watchdog_get_drvdata(wdd);
+ unsigned int selector;
+ int ret;
+
+ selector = da9062_wdt_timeout_to_sel(wdt->wdtdev.timeout);
+ ret = da9062_wdt_update_timeout_register(wdt, selector);
+ if (ret)
+ dev_err(wdt->hw->dev, "Watchdog failed to start (err = %d)\n",
+ ret);
+
+ return ret;
+}
+
+static int da9062_wdt_stop(struct watchdog_device *wdd)
+{
+ struct da9062_watchdog *wdt = watchdog_get_drvdata(wdd);
+ int ret;
+
+ ret = da9062_reset_watchdog_timer(wdt);
+ if (ret) {
+ dev_err(wdt->hw->dev, "Failed to ping the watchdog (err = %d)\n",
+ ret);
+ return ret;
+ }
+
+ ret = regmap_update_bits(wdt->hw->regmap,
+ DA9062AA_CONTROL_D,
+ DA9062AA_TWDSCALE_MASK,
+ DA9062_TWDSCALE_DISABLE);
+ if (ret)
+ dev_err(wdt->hw->dev, "Watchdog failed to stop (err = %d)\n",
+ ret);
+
+ return ret;
+}
+
+static int da9062_wdt_ping(struct watchdog_device *wdd)
+{
+ struct da9062_watchdog *wdt = watchdog_get_drvdata(wdd);
+ int ret;
+
+ ret = da9062_reset_watchdog_timer(wdt);
+ if (ret)
+ dev_err(wdt->hw->dev, "Failed to ping the watchdog (err = %d)\n",
+ ret);
+
+ return ret;
+}
+
+static int da9062_wdt_set_timeout(struct watchdog_device *wdd,
+ unsigned int timeout)
+{
+ struct da9062_watchdog *wdt = watchdog_get_drvdata(wdd);
+ unsigned int selector;
+ int ret;
+
+ selector = da9062_wdt_timeout_to_sel(timeout);
+ ret = da9062_wdt_update_timeout_register(wdt, selector);
+ if (ret)
+ dev_err(wdt->hw->dev, "Failed to set watchdog timeout (err = %d)\n",
+ ret);
+ else
+ wdd->timeout = wdt_timeout[selector];
+
+ return ret;
+}
+
+static const struct watchdog_info da9062_watchdog_info = {
+ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
+ .identity = "DA9062 WDT",
+};
+
+static const struct watchdog_ops da9062_watchdog_ops = {
+ .owner = THIS_MODULE,
+ .start = da9062_wdt_start,
+ .stop = da9062_wdt_stop,
+ .ping = da9062_wdt_ping,
+ .set_timeout = da9062_wdt_set_timeout,
+};
+
+static int da9062_wdt_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct da9062 *chip;
+ struct da9062_watchdog *wdt;
+
+ chip = dev_get_drvdata(pdev->dev.parent);
+ if (!chip)
+ return -EINVAL;
+
+ wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL);
+ if (!wdt)
+ return -ENOMEM;
+
+ wdt->hw = chip;
+
+ wdt->wdtdev.info = &da9062_watchdog_info;
+ wdt->wdtdev.ops = &da9062_watchdog_ops;
+ wdt->wdtdev.min_timeout = DA9062_WDT_MIN_TIMEOUT;
+ wdt->wdtdev.max_timeout = DA9062_WDT_MAX_TIMEOUT;
+ wdt->wdtdev.timeout = DA9062_WDG_DEFAULT_TIMEOUT;
+ wdt->wdtdev.status = WATCHDOG_NOWAYOUT_INIT_STATUS;
+
+ watchdog_set_drvdata(&wdt->wdtdev, wdt);
+ dev_set_drvdata(&pdev->dev, wdt);
+
+ ret = watchdog_register_device(&wdt->wdtdev);
+ if (ret < 0) {
+ dev_err(wdt->hw->dev,
+ "watchdog registration failed (%d)\n", ret);
+ return ret;
+ }
+
+ da9062_set_window_start(wdt);
+
+ ret = da9062_wdt_ping(&wdt->wdtdev);
+ if (ret < 0)
+ watchdog_unregister_device(&wdt->wdtdev);
+
+ return ret;
+}
+
+static int da9062_wdt_remove(struct platform_device *pdev)
+{
+ struct da9062_watchdog *wdt = dev_get_drvdata(&pdev->dev);
+
+ watchdog_unregister_device(&wdt->wdtdev);
+ return 0;
+}
+
+static struct platform_driver da9062_wdt_driver = {
+ .probe = da9062_wdt_probe,
+ .remove = da9062_wdt_remove,
+ .driver = {
+ .name = "da9062-watchdog",
+ },
+};
+module_platform_driver(da9062_wdt_driver);
+
+MODULE_AUTHOR("S Twiss <stwiss.opensource@diasemi.com>");
+MODULE_DESCRIPTION("WDT device driver for Dialog DA9062");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:da9062-watchdog");
diff --git a/drivers/watchdog/diag288_wdt.c b/drivers/watchdog/diag288_wdt.c
index 429494b6c822..a9a5210143ae 100644
--- a/drivers/watchdog/diag288_wdt.c
+++ b/drivers/watchdog/diag288_wdt.c
@@ -125,9 +125,7 @@ static int wdt_start(struct watchdog_device *dev)
ret = __diag288_vm(func, dev->timeout, ebc_cmd, len);
WARN_ON(ret != 0);
kfree(ebc_cmd);
- }
-
- if (MACHINE_IS_LPAR) {
+ } else {
ret = __diag288_lpar(WDT_FUNC_INIT,
dev->timeout, LPARWDT_RESTART);
}
@@ -136,7 +134,6 @@ static int wdt_start(struct watchdog_device *dev)
pr_err("The watchdog cannot be activated\n");
return ret;
}
- pr_info("The watchdog was activated\n");
return 0;
}
@@ -145,7 +142,6 @@ static int wdt_stop(struct watchdog_device *dev)
int ret;
ret = __diag288(WDT_FUNC_CANCEL, 0, 0, 0);
- pr_info("The watchdog was deactivated\n");
return ret;
}
@@ -177,10 +173,9 @@ static int wdt_ping(struct watchdog_device *dev)
ret = __diag288_vm(func, dev->timeout, ebc_cmd, len);
WARN_ON(ret != 0);
kfree(ebc_cmd);
- }
-
- if (MACHINE_IS_LPAR)
+ } else {
ret = __diag288_lpar(WDT_FUNC_CHANGE, dev->timeout, 0);
+ }
if (ret)
pr_err("The watchdog timer cannot be started or reset\n");
@@ -202,7 +197,7 @@ static struct watchdog_ops wdt_ops = {
};
static struct watchdog_info wdt_info = {
- .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE,
+ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
.firmware_version = 0,
.identity = "z Watchdog",
};
@@ -273,21 +268,16 @@ static int __init diag288_init(void)
watchdog_set_nowayout(&wdt_dev, nowayout_info);
if (MACHINE_IS_VM) {
- pr_info("The watchdog device driver detected a z/VM environment\n");
if (__diag288_vm(WDT_FUNC_INIT, 15,
ebc_begin, sizeof(ebc_begin)) != 0) {
pr_err("The watchdog cannot be initialized\n");
return -EINVAL;
}
- } else if (MACHINE_IS_LPAR) {
- pr_info("The watchdog device driver detected an LPAR environment\n");
+ } else {
if (__diag288_lpar(WDT_FUNC_INIT, 30, LPARWDT_RESTART)) {
pr_err("The watchdog cannot be initialized\n");
return -EINVAL;
}
- } else {
- pr_err("Linux runs in an environment that does not support the diag288 watchdog\n");
- return -ENODEV;
}
if (__diag288_lpar(WDT_FUNC_CANCEL, 0, 0)) {
diff --git a/drivers/watchdog/digicolor_wdt.c b/drivers/watchdog/digicolor_wdt.c
new file mode 100644
index 000000000000..31d8e4936611
--- /dev/null
+++ b/drivers/watchdog/digicolor_wdt.c
@@ -0,0 +1,205 @@
+/*
+ * Watchdog driver for Conexant Digicolor
+ *
+ * Copyright (C) 2015 Paradox Innovation Ltd.
+ *
+ * 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 <linux/types.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/watchdog.h>
+#include <linux/reboot.h>
+#include <linux/platform_device.h>
+#include <linux/of_address.h>
+
+#define TIMER_A_CONTROL 0
+#define TIMER_A_COUNT 4
+
+#define TIMER_A_ENABLE_COUNT BIT(0)
+#define TIMER_A_ENABLE_WATCHDOG BIT(1)
+
+struct dc_wdt {
+ void __iomem *base;
+ struct clk *clk;
+ struct notifier_block restart_handler;
+ spinlock_t lock;
+};
+
+static unsigned timeout;
+module_param(timeout, uint, 0);
+MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds");
+
+static void dc_wdt_set(struct dc_wdt *wdt, u32 ticks)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&wdt->lock, flags);
+
+ writel_relaxed(0, wdt->base + TIMER_A_CONTROL);
+ writel_relaxed(ticks, wdt->base + TIMER_A_COUNT);
+ writel_relaxed(TIMER_A_ENABLE_COUNT | TIMER_A_ENABLE_WATCHDOG,
+ wdt->base + TIMER_A_CONTROL);
+
+ spin_unlock_irqrestore(&wdt->lock, flags);
+}
+
+static int dc_restart_handler(struct notifier_block *this, unsigned long mode,
+ void *cmd)
+{
+ struct dc_wdt *wdt = container_of(this, struct dc_wdt, restart_handler);
+
+ dc_wdt_set(wdt, 1);
+ /* wait for reset to assert... */
+ mdelay(500);
+
+ return NOTIFY_DONE;
+}
+
+static int dc_wdt_start(struct watchdog_device *wdog)
+{
+ struct dc_wdt *wdt = watchdog_get_drvdata(wdog);
+
+ dc_wdt_set(wdt, wdog->timeout * clk_get_rate(wdt->clk));
+
+ return 0;
+}
+
+static int dc_wdt_stop(struct watchdog_device *wdog)
+{
+ struct dc_wdt *wdt = watchdog_get_drvdata(wdog);
+
+ writel_relaxed(0, wdt->base + TIMER_A_CONTROL);
+
+ return 0;
+}
+
+static int dc_wdt_set_timeout(struct watchdog_device *wdog, unsigned int t)
+{
+ struct dc_wdt *wdt = watchdog_get_drvdata(wdog);
+
+ dc_wdt_set(wdt, t * clk_get_rate(wdt->clk));
+ wdog->timeout = t;
+
+ return 0;
+}
+
+static unsigned int dc_wdt_get_timeleft(struct watchdog_device *wdog)
+{
+ struct dc_wdt *wdt = watchdog_get_drvdata(wdog);
+ uint32_t count = readl_relaxed(wdt->base + TIMER_A_COUNT);
+
+ return count / clk_get_rate(wdt->clk);
+}
+
+static struct watchdog_ops dc_wdt_ops = {
+ .owner = THIS_MODULE,
+ .start = dc_wdt_start,
+ .stop = dc_wdt_stop,
+ .set_timeout = dc_wdt_set_timeout,
+ .get_timeleft = dc_wdt_get_timeleft,
+};
+
+static struct watchdog_info dc_wdt_info = {
+ .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE
+ | WDIOF_KEEPALIVEPING,
+ .identity = "Conexant Digicolor Watchdog",
+};
+
+static struct watchdog_device dc_wdt_wdd = {
+ .info = &dc_wdt_info,
+ .ops = &dc_wdt_ops,
+ .min_timeout = 1,
+};
+
+static int dc_wdt_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct dc_wdt *wdt;
+ int ret;
+
+ wdt = devm_kzalloc(dev, sizeof(struct dc_wdt), GFP_KERNEL);
+ if (!wdt)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, wdt);
+
+ wdt->base = of_iomap(np, 0);
+ if (!wdt->base) {
+ dev_err(dev, "Failed to remap watchdog regs");
+ return -ENODEV;
+ }
+
+ wdt->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(wdt->clk)) {
+ ret = PTR_ERR(wdt->clk);
+ goto err_iounmap;
+ }
+ dc_wdt_wdd.max_timeout = U32_MAX / clk_get_rate(wdt->clk);
+ dc_wdt_wdd.timeout = dc_wdt_wdd.max_timeout;
+
+ spin_lock_init(&wdt->lock);
+
+ watchdog_set_drvdata(&dc_wdt_wdd, wdt);
+ watchdog_init_timeout(&dc_wdt_wdd, timeout, dev);
+ ret = watchdog_register_device(&dc_wdt_wdd);
+ if (ret) {
+ dev_err(dev, "Failed to register watchdog device");
+ goto err_iounmap;
+ }
+
+ wdt->restart_handler.notifier_call = dc_restart_handler;
+ wdt->restart_handler.priority = 128;
+ ret = register_restart_handler(&wdt->restart_handler);
+ if (ret)
+ dev_warn(&pdev->dev, "cannot register restart handler\n");
+
+ return 0;
+
+err_iounmap:
+ iounmap(wdt->base);
+ return ret;
+}
+
+static int dc_wdt_remove(struct platform_device *pdev)
+{
+ struct dc_wdt *wdt = platform_get_drvdata(pdev);
+
+ unregister_restart_handler(&wdt->restart_handler);
+ watchdog_unregister_device(&dc_wdt_wdd);
+ iounmap(wdt->base);
+
+ return 0;
+}
+
+static void dc_wdt_shutdown(struct platform_device *pdev)
+{
+ dc_wdt_stop(&dc_wdt_wdd);
+}
+
+static const struct of_device_id dc_wdt_of_match[] = {
+ { .compatible = "cnxt,cx92755-wdt", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, dc_wdt_of_match);
+
+static struct platform_driver dc_wdt_driver = {
+ .probe = dc_wdt_probe,
+ .remove = dc_wdt_remove,
+ .shutdown = dc_wdt_shutdown,
+ .driver = {
+ .name = "digicolor-wdt",
+ .of_match_table = dc_wdt_of_match,
+ },
+};
+module_platform_driver(dc_wdt_driver);
+
+MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>");
+MODULE_DESCRIPTION("Driver for Conexant Digicolor watchdog timer");
+MODULE_LICENSE("GPL");
diff --git a/drivers/watchdog/dw_wdt.c b/drivers/watchdog/dw_wdt.c
index d0bb9499d12c..6ea0634345e9 100644
--- a/drivers/watchdog/dw_wdt.c
+++ b/drivers/watchdog/dw_wdt.c
@@ -35,7 +35,6 @@
#include <linux/pm.h>
#include <linux/platform_device.h>
#include <linux/reboot.h>
-#include <linux/spinlock.h>
#include <linux/timer.h>
#include <linux/uaccess.h>
#include <linux/watchdog.h>
@@ -61,7 +60,6 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
#define WDT_TIMEOUT (HZ / 2)
static struct {
- spinlock_t lock;
void __iomem *regs;
struct clk *clk;
unsigned long in_use;
@@ -177,7 +175,6 @@ static int dw_wdt_open(struct inode *inode, struct file *filp)
/* Make sure we don't get unloaded. */
__module_get(THIS_MODULE);
- spin_lock(&dw_wdt.lock);
if (!dw_wdt_is_enabled()) {
/*
* The watchdog is not currently enabled. Set the timeout to
@@ -190,8 +187,6 @@ static int dw_wdt_open(struct inode *inode, struct file *filp)
dw_wdt_set_next_heartbeat();
- spin_unlock(&dw_wdt.lock);
-
return nonseekable_open(inode, filp);
}
@@ -220,6 +215,7 @@ static ssize_t dw_wdt_write(struct file *filp, const char __user *buf,
}
dw_wdt_set_next_heartbeat();
+ dw_wdt_keepalive();
mod_timer(&dw_wdt.timer, jiffies + WDT_TIMEOUT);
return len;
@@ -348,8 +344,6 @@ static int dw_wdt_drv_probe(struct platform_device *pdev)
if (ret)
return ret;
- spin_lock_init(&dw_wdt.lock);
-
ret = misc_register(&dw_wdt_miscdev);
if (ret)
goto out_disable_clk;
diff --git a/drivers/watchdog/gpio_wdt.c b/drivers/watchdog/gpio_wdt.c
index cbc313d37c59..1687cc2d7122 100644
--- a/drivers/watchdog/gpio_wdt.c
+++ b/drivers/watchdog/gpio_wdt.c
@@ -267,7 +267,16 @@ static struct platform_driver gpio_wdt_driver = {
.probe = gpio_wdt_probe,
.remove = gpio_wdt_remove,
};
+
+#ifdef CONFIG_GPIO_WATCHDOG_ARCH_INITCALL
+static int __init gpio_wdt_init(void)
+{
+ return platform_driver_register(&gpio_wdt_driver);
+}
+arch_initcall(gpio_wdt_init);
+#else
module_platform_driver(gpio_wdt_driver);
+#endif
MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>");
MODULE_DESCRIPTION("GPIO Watchdog");
diff --git a/drivers/watchdog/hpwdt.c b/drivers/watchdog/hpwdt.c
index ada3e44f9932..286369d4f0f5 100644
--- a/drivers/watchdog/hpwdt.c
+++ b/drivers/watchdog/hpwdt.c
@@ -588,7 +588,7 @@ static long hpwdt_ioctl(struct file *file, unsigned int cmd,
{
void __user *argp = (void __user *)arg;
int __user *p = argp;
- int new_margin;
+ int new_margin, options;
int ret = -ENOTTY;
switch (cmd) {
@@ -608,6 +608,20 @@ static long hpwdt_ioctl(struct file *file, unsigned int cmd,
ret = 0;
break;
+ case WDIOC_SETOPTIONS:
+ ret = get_user(options, p);
+ if (ret)
+ break;
+
+ if (options & WDIOS_DISABLECARD)
+ hpwdt_stop();
+
+ if (options & WDIOS_ENABLECARD) {
+ hpwdt_start();
+ hpwdt_ping();
+ }
+ break;
+
case WDIOC_SETTIMEOUT:
ret = get_user(new_margin, p);
if (ret)
diff --git a/drivers/watchdog/iTCO_wdt.c b/drivers/watchdog/iTCO_wdt.c
index 05ee0bf88ce9..3c3fd417ddeb 100644
--- a/drivers/watchdog/iTCO_wdt.c
+++ b/drivers/watchdog/iTCO_wdt.c
@@ -51,6 +51,7 @@
#define DRV_VERSION "1.11"
/* Includes */
+#include <linux/acpi.h> /* For ACPI support */
#include <linux/module.h> /* For module specific items */
#include <linux/moduleparam.h> /* For new moduleparam's */
#include <linux/types.h> /* For standard types (like size_t) */
@@ -103,6 +104,8 @@ static struct { /* this is private data for the iTCO_wdt device */
struct platform_device *dev;
/* the PCI-device */
struct pci_dev *pdev;
+ /* whether or not the watchdog has been suspended */
+ bool suspended;
} iTCO_wdt_private;
/* module parameters */
@@ -571,12 +574,60 @@ static void iTCO_wdt_shutdown(struct platform_device *dev)
iTCO_wdt_stop(NULL);
}
+#ifdef CONFIG_PM_SLEEP
+/*
+ * Suspend-to-idle requires this, because it stops the ticks and timekeeping, so
+ * the watchdog cannot be pinged while in that state. In ACPI sleep states the
+ * watchdog is stopped by the platform firmware.
+ */
+
+#ifdef CONFIG_ACPI
+static inline bool need_suspend(void)
+{
+ return acpi_target_system_state() == ACPI_STATE_S0;
+}
+#else
+static inline bool need_suspend(void) { return true; }
+#endif
+
+static int iTCO_wdt_suspend_noirq(struct device *dev)
+{
+ int ret = 0;
+
+ iTCO_wdt_private.suspended = false;
+ if (watchdog_active(&iTCO_wdt_watchdog_dev) && need_suspend()) {
+ ret = iTCO_wdt_stop(&iTCO_wdt_watchdog_dev);
+ if (!ret)
+ iTCO_wdt_private.suspended = true;
+ }
+ return ret;
+}
+
+static int iTCO_wdt_resume_noirq(struct device *dev)
+{
+ if (iTCO_wdt_private.suspended)
+ iTCO_wdt_start(&iTCO_wdt_watchdog_dev);
+
+ return 0;
+}
+
+static struct dev_pm_ops iTCO_wdt_pm = {
+ .suspend_noirq = iTCO_wdt_suspend_noirq,
+ .resume_noirq = iTCO_wdt_resume_noirq,
+};
+
+#define ITCO_WDT_PM_OPS (&iTCO_wdt_pm)
+#else
+#define ITCO_WDT_PM_OPS NULL
+#endif /* CONFIG_PM_SLEEP */
+
static struct platform_driver iTCO_wdt_driver = {
.probe = iTCO_wdt_probe,
.remove = iTCO_wdt_remove,
.shutdown = iTCO_wdt_shutdown,
.driver = {
.name = DRV_NAME,
+ .pm = ITCO_WDT_PM_OPS,
},
};
diff --git a/drivers/watchdog/imgpdc_wdt.c b/drivers/watchdog/imgpdc_wdt.c
index 0deaa4f971f5..0f73621827ab 100644
--- a/drivers/watchdog/imgpdc_wdt.c
+++ b/drivers/watchdog/imgpdc_wdt.c
@@ -9,6 +9,35 @@
*
* Based on drivers/watchdog/sunxi_wdt.c Copyright (c) 2013 Carlo Caione
* 2012 Henrik Nordstrom
+ *
+ * Notes
+ * -----
+ * The timeout value is rounded to the next power of two clock cycles.
+ * This is configured using the PDC_WDT_CONFIG register, according to this
+ * formula:
+ *
+ * timeout = 2^(delay + 1) clock cycles
+ *
+ * Where 'delay' is the value written in PDC_WDT_CONFIG register.
+ *
+ * Therefore, the hardware only allows to program watchdog timeouts, expressed
+ * as a power of two number of watchdog clock cycles. The current implementation
+ * guarantees that the actual watchdog timeout will be _at least_ the value
+ * programmed in the imgpdg_wdt driver.
+ *
+ * The following table shows how the user-configured timeout relates
+ * to the actual hardware timeout (watchdog clock @ 40000 Hz):
+ *
+ * input timeout | WD_DELAY | actual timeout
+ * -----------------------------------
+ * 10 | 18 | 13 seconds
+ * 20 | 19 | 26 seconds
+ * 30 | 20 | 52 seconds
+ * 60 | 21 | 104 seconds
+ *
+ * Albeit coarse, this granularity would suffice most watchdog uses.
+ * If the platform allows it, the user should be able to change the watchdog
+ * clock rate and achieve a finer timeout granularity.
*/
#include <linux/clk.h>
@@ -16,6 +45,7 @@
#include <linux/log2.h>
#include <linux/module.h>
#include <linux/platform_device.h>
+#include <linux/reboot.h>
#include <linux/slab.h>
#include <linux/watchdog.h>
@@ -42,7 +72,7 @@
#define PDC_WDT_MIN_TIMEOUT 1
#define PDC_WDT_DEF_TIMEOUT 64
-static int heartbeat = PDC_WDT_DEF_TIMEOUT;
+static int heartbeat;
module_param(heartbeat, int, 0);
MODULE_PARM_DESC(heartbeat, "Watchdog heartbeats in seconds "
"(default=" __MODULE_STRING(PDC_WDT_DEF_TIMEOUT) ")");
@@ -57,6 +87,7 @@ struct pdc_wdt_dev {
struct clk *wdt_clk;
struct clk *sys_clk;
void __iomem *base;
+ struct notifier_block restart_handler;
};
static int pdc_wdt_keepalive(struct watchdog_device *wdt_dev)
@@ -84,18 +115,24 @@ static int pdc_wdt_stop(struct watchdog_device *wdt_dev)
return 0;
}
+static void __pdc_wdt_set_timeout(struct pdc_wdt_dev *wdt)
+{
+ unsigned long clk_rate = clk_get_rate(wdt->wdt_clk);
+ unsigned int val;
+
+ val = readl(wdt->base + PDC_WDT_CONFIG) & ~PDC_WDT_CONFIG_DELAY_MASK;
+ val |= order_base_2(wdt->wdt_dev.timeout * clk_rate) - 1;
+ writel(val, wdt->base + PDC_WDT_CONFIG);
+}
+
static int pdc_wdt_set_timeout(struct watchdog_device *wdt_dev,
unsigned int new_timeout)
{
- unsigned int val;
struct pdc_wdt_dev *wdt = watchdog_get_drvdata(wdt_dev);
- unsigned long clk_rate = clk_get_rate(wdt->wdt_clk);
wdt->wdt_dev.timeout = new_timeout;
- val = readl(wdt->base + PDC_WDT_CONFIG) & ~PDC_WDT_CONFIG_DELAY_MASK;
- val |= order_base_2(new_timeout * clk_rate) - 1;
- writel(val, wdt->base + PDC_WDT_CONFIG);
+ __pdc_wdt_set_timeout(wdt);
return 0;
}
@@ -106,6 +143,8 @@ static int pdc_wdt_start(struct watchdog_device *wdt_dev)
unsigned int val;
struct pdc_wdt_dev *wdt = watchdog_get_drvdata(wdt_dev);
+ __pdc_wdt_set_timeout(wdt);
+
val = readl(wdt->base + PDC_WDT_CONFIG);
val |= PDC_WDT_CONFIG_ENABLE;
writel(val, wdt->base + PDC_WDT_CONFIG);
@@ -128,8 +167,21 @@ static const struct watchdog_ops pdc_wdt_ops = {
.set_timeout = pdc_wdt_set_timeout,
};
+static int pdc_wdt_restart(struct notifier_block *this, unsigned long mode,
+ void *cmd)
+{
+ struct pdc_wdt_dev *wdt = container_of(this, struct pdc_wdt_dev,
+ restart_handler);
+
+ /* Assert SOFT_RESET */
+ writel(0x1, wdt->base + PDC_WDT_SOFT_RESET);
+
+ return NOTIFY_OK;
+}
+
static int pdc_wdt_probe(struct platform_device *pdev)
{
+ u64 div;
int ret, val;
unsigned long clk_rate;
struct resource *res;
@@ -189,16 +241,15 @@ static int pdc_wdt_probe(struct platform_device *pdev)
pdc_wdt->wdt_dev.info = &pdc_wdt_info;
pdc_wdt->wdt_dev.ops = &pdc_wdt_ops;
- pdc_wdt->wdt_dev.max_timeout = 1 << PDC_WDT_CONFIG_DELAY_MASK;
+
+ div = 1ULL << (PDC_WDT_CONFIG_DELAY_MASK + 1);
+ do_div(div, clk_rate);
+ pdc_wdt->wdt_dev.max_timeout = div;
+ pdc_wdt->wdt_dev.timeout = PDC_WDT_DEF_TIMEOUT;
pdc_wdt->wdt_dev.parent = &pdev->dev;
watchdog_set_drvdata(&pdc_wdt->wdt_dev, pdc_wdt);
- ret = watchdog_init_timeout(&pdc_wdt->wdt_dev, heartbeat, &pdev->dev);
- if (ret < 0) {
- pdc_wdt->wdt_dev.timeout = pdc_wdt->wdt_dev.max_timeout;
- dev_warn(&pdev->dev,
- "Initial timeout out of range! setting max timeout\n");
- }
+ watchdog_init_timeout(&pdc_wdt->wdt_dev, heartbeat, &pdev->dev);
pdc_wdt_stop(&pdc_wdt->wdt_dev);
@@ -238,6 +289,13 @@ static int pdc_wdt_probe(struct platform_device *pdev)
if (ret)
goto disable_wdt_clk;
+ pdc_wdt->restart_handler.notifier_call = pdc_wdt_restart;
+ pdc_wdt->restart_handler.priority = 128;
+ ret = register_restart_handler(&pdc_wdt->restart_handler);
+ if (ret)
+ dev_warn(&pdev->dev, "failed to register restart handler: %d\n",
+ ret);
+
return 0;
disable_wdt_clk:
diff --git a/drivers/watchdog/imx2_wdt.c b/drivers/watchdog/imx2_wdt.c
index 5e6d808d358a..0bb1a1d1b170 100644
--- a/drivers/watchdog/imx2_wdt.c
+++ b/drivers/watchdog/imx2_wdt.c
@@ -166,6 +166,8 @@ static int imx2_wdt_set_timeout(struct watchdog_device *wdog,
{
struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog);
+ wdog->timeout = new_timeout;
+
regmap_update_bits(wdev->regmap, IMX2_WDT_WCR, IMX2_WDT_WCR_WT,
WDOG_SEC_TO_COUNT(new_timeout));
return 0;
@@ -256,8 +258,11 @@ static int __init imx2_wdt_probe(struct platform_device *pdev)
wdog->ops = &imx2_wdt_ops;
wdog->min_timeout = 1;
wdog->max_timeout = IMX2_WDT_MAX_TIME;
+ wdog->parent = &pdev->dev;
- clk_prepare_enable(wdev->clk);
+ ret = clk_prepare_enable(wdev->clk);
+ if (ret)
+ return ret;
regmap_read(wdev->regmap, IMX2_WDT_WRSR, &val);
wdog->bootstatus = val & IMX2_WDT_WRSR_TOUT ? WDIOF_CARDRESET : 0;
@@ -286,7 +291,7 @@ static int __init imx2_wdt_probe(struct platform_device *pdev)
ret = watchdog_register_device(wdog);
if (ret) {
dev_err(&pdev->dev, "cannot register watchdog device\n");
- return ret;
+ goto disable_clk;
}
wdev->restart_handler.notifier_call = imx2_restart_handler;
@@ -299,6 +304,10 @@ static int __init imx2_wdt_probe(struct platform_device *pdev)
wdog->timeout, nowayout);
return 0;
+
+disable_clk:
+ clk_disable_unprepare(wdev->clk);
+ return ret;
}
static int __exit imx2_wdt_remove(struct platform_device *pdev)
@@ -362,8 +371,11 @@ static int imx2_wdt_resume(struct device *dev)
{
struct watchdog_device *wdog = dev_get_drvdata(dev);
struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog);
+ int ret;
- clk_prepare_enable(wdev->clk);
+ ret = clk_prepare_enable(wdev->clk);
+ if (ret)
+ return ret;
if (watchdog_active(wdog) && !imx2_wdt_is_running(wdev)) {
/*
diff --git a/drivers/watchdog/max63xx_wdt.c b/drivers/watchdog/max63xx_wdt.c
index 08da3114accb..f36ca4be0720 100644
--- a/drivers/watchdog/max63xx_wdt.c
+++ b/drivers/watchdog/max63xx_wdt.c
@@ -39,10 +39,22 @@ static bool nowayout = WATCHDOG_NOWAYOUT;
#define MAX6369_WDSET (7 << 0)
#define MAX6369_WDI (1 << 3)
-static DEFINE_SPINLOCK(io_lock);
+#define MAX6369_WDSET_DISABLED 3
static int nodelay;
-static void __iomem *wdt_base;
+
+struct max63xx_wdt {
+ struct watchdog_device wdd;
+ const struct max63xx_timeout *timeout;
+
+ /* memory mapping */
+ void __iomem *base;
+ spinlock_t lock;
+
+ /* WDI and WSET bits write access routines */
+ void (*ping)(struct max63xx_wdt *wdt);
+ void (*set)(struct max63xx_wdt *wdt, u8 set);
+};
/*
* The timeout values used are actually the absolute minimum the chip
@@ -59,25 +71,25 @@ static void __iomem *wdt_base;
/* Timeouts in second */
struct max63xx_timeout {
- u8 wdset;
- u8 tdelay;
- u8 twd;
+ const u8 wdset;
+ const u8 tdelay;
+ const u8 twd;
};
-static struct max63xx_timeout max6369_table[] = {
+static const struct max63xx_timeout max6369_table[] = {
{ 5, 1, 1 },
{ 6, 10, 10 },
{ 7, 60, 60 },
{ },
};
-static struct max63xx_timeout max6371_table[] = {
+static const struct max63xx_timeout max6371_table[] = {
{ 6, 60, 3 },
{ 7, 60, 60 },
{ },
};
-static struct max63xx_timeout max6373_table[] = {
+static const struct max63xx_timeout max6373_table[] = {
{ 2, 60, 1 },
{ 5, 0, 1 },
{ 1, 3, 3 },
@@ -86,8 +98,6 @@ static struct max63xx_timeout max6373_table[] = {
{ },
};
-static struct max63xx_timeout *current_timeout;
-
static struct max63xx_timeout *
max63xx_select_timeout(struct max63xx_timeout *table, int value)
{
@@ -108,59 +118,32 @@ max63xx_select_timeout(struct max63xx_timeout *table, int value)
static int max63xx_wdt_ping(struct watchdog_device *wdd)
{
- u8 val;
-
- spin_lock(&io_lock);
+ struct max63xx_wdt *wdt = watchdog_get_drvdata(wdd);
- val = __raw_readb(wdt_base);
-
- __raw_writeb(val | MAX6369_WDI, wdt_base);
- __raw_writeb(val & ~MAX6369_WDI, wdt_base);
-
- spin_unlock(&io_lock);
+ wdt->ping(wdt);
return 0;
}
static int max63xx_wdt_start(struct watchdog_device *wdd)
{
- struct max63xx_timeout *entry = watchdog_get_drvdata(wdd);
- u8 val;
+ struct max63xx_wdt *wdt = watchdog_get_drvdata(wdd);
- spin_lock(&io_lock);
-
- val = __raw_readb(wdt_base);
- val &= ~MAX6369_WDSET;
- val |= entry->wdset;
- __raw_writeb(val, wdt_base);
-
- spin_unlock(&io_lock);
+ wdt->set(wdt, wdt->timeout->wdset);
/* check for a edge triggered startup */
- if (entry->tdelay == 0)
- max63xx_wdt_ping(wdd);
+ if (wdt->timeout->tdelay == 0)
+ wdt->ping(wdt);
return 0;
}
static int max63xx_wdt_stop(struct watchdog_device *wdd)
{
- u8 val;
+ struct max63xx_wdt *wdt = watchdog_get_drvdata(wdd);
- spin_lock(&io_lock);
-
- val = __raw_readb(wdt_base);
- val &= ~MAX6369_WDSET;
- val |= 3;
- __raw_writeb(val, wdt_base);
-
- spin_unlock(&io_lock);
+ wdt->set(wdt, MAX6369_WDSET_DISABLED);
return 0;
}
-static const struct watchdog_info max63xx_wdt_info = {
- .options = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
- .identity = "max63xx Watchdog",
-};
-
static const struct watchdog_ops max63xx_wdt_ops = {
.owner = THIS_MODULE,
.start = max63xx_wdt_start,
@@ -168,53 +151,108 @@ static const struct watchdog_ops max63xx_wdt_ops = {
.ping = max63xx_wdt_ping,
};
-static struct watchdog_device max63xx_wdt_dev = {
- .info = &max63xx_wdt_info,
- .ops = &max63xx_wdt_ops,
+static const struct watchdog_info max63xx_wdt_info = {
+ .options = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
+ .identity = "max63xx Watchdog",
};
+static void max63xx_mmap_ping(struct max63xx_wdt *wdt)
+{
+ u8 val;
+
+ spin_lock(&wdt->lock);
+
+ val = __raw_readb(wdt->base);
+
+ __raw_writeb(val | MAX6369_WDI, wdt->base);
+ __raw_writeb(val & ~MAX6369_WDI, wdt->base);
+
+ spin_unlock(&wdt->lock);
+}
+
+static void max63xx_mmap_set(struct max63xx_wdt *wdt, u8 set)
+{
+ u8 val;
+
+ spin_lock(&wdt->lock);
+
+ val = __raw_readb(wdt->base);
+ val &= ~MAX6369_WDSET;
+ val |= set & MAX6369_WDSET;
+ __raw_writeb(val, wdt->base);
+
+ spin_unlock(&wdt->lock);
+}
+
+static int max63xx_mmap_init(struct platform_device *p, struct max63xx_wdt *wdt)
+{
+ struct resource *mem = platform_get_resource(p, IORESOURCE_MEM, 0);
+
+ wdt->base = devm_ioremap_resource(&p->dev, mem);
+ if (IS_ERR(wdt->base))
+ return PTR_ERR(wdt->base);
+
+ spin_lock_init(&wdt->lock);
+
+ wdt->ping = max63xx_mmap_ping;
+ wdt->set = max63xx_mmap_set;
+ return 0;
+}
+
static int max63xx_wdt_probe(struct platform_device *pdev)
{
- struct resource *wdt_mem;
+ struct max63xx_wdt *wdt;
struct max63xx_timeout *table;
+ int err;
+
+ wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL);
+ if (!wdt)
+ return -ENOMEM;
table = (struct max63xx_timeout *)pdev->id_entry->driver_data;
if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT)
heartbeat = DEFAULT_HEARTBEAT;
- dev_info(&pdev->dev, "requesting %ds heartbeat\n", heartbeat);
- current_timeout = max63xx_select_timeout(table, heartbeat);
-
- if (!current_timeout) {
- dev_err(&pdev->dev, "unable to satisfy heartbeat request\n");
+ wdt->timeout = max63xx_select_timeout(table, heartbeat);
+ if (!wdt->timeout) {
+ dev_err(&pdev->dev, "unable to satisfy %ds heartbeat request\n",
+ heartbeat);
return -EINVAL;
}
- dev_info(&pdev->dev, "using %ds heartbeat with %ds initial delay\n",
- current_timeout->twd, current_timeout->tdelay);
+ err = max63xx_mmap_init(pdev, wdt);
+ if (err)
+ return err;
+
+ platform_set_drvdata(pdev, &wdt->wdd);
+ watchdog_set_drvdata(&wdt->wdd, wdt);
- heartbeat = current_timeout->twd;
+ wdt->wdd.parent = &pdev->dev;
+ wdt->wdd.timeout = wdt->timeout->twd;
+ wdt->wdd.info = &max63xx_wdt_info;
+ wdt->wdd.ops = &max63xx_wdt_ops;
- wdt_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- wdt_base = devm_ioremap_resource(&pdev->dev, wdt_mem);
- if (IS_ERR(wdt_base))
- return PTR_ERR(wdt_base);
+ watchdog_set_nowayout(&wdt->wdd, nowayout);
- max63xx_wdt_dev.timeout = heartbeat;
- watchdog_set_nowayout(&max63xx_wdt_dev, nowayout);
- watchdog_set_drvdata(&max63xx_wdt_dev, current_timeout);
+ err = watchdog_register_device(&wdt->wdd);
+ if (err)
+ return err;
- return watchdog_register_device(&max63xx_wdt_dev);
+ dev_info(&pdev->dev, "using %ds heartbeat with %ds initial delay\n",
+ wdt->timeout->twd, wdt->timeout->tdelay);
+ return 0;
}
static int max63xx_wdt_remove(struct platform_device *pdev)
{
- watchdog_unregister_device(&max63xx_wdt_dev);
+ struct watchdog_device *wdd = platform_get_drvdata(pdev);
+
+ watchdog_unregister_device(wdd);
return 0;
}
-static struct platform_device_id max63xx_id_table[] = {
+static const struct platform_device_id max63xx_id_table[] = {
{ "max6369_wdt", (kernel_ulong_t)max6369_table, },
{ "max6370_wdt", (kernel_ulong_t)max6369_table, },
{ "max6371_wdt", (kernel_ulong_t)max6371_table, },
diff --git a/drivers/watchdog/mena21_wdt.c b/drivers/watchdog/mena21_wdt.c
index 96dbba980579..d193a5e79c38 100644
--- a/drivers/watchdog/mena21_wdt.c
+++ b/drivers/watchdog/mena21_wdt.c
@@ -208,14 +208,15 @@ static int a21_wdt_probe(struct platform_device *pdev)
else if (reset == 7)
a21_wdt.bootstatus |= WDIOF_EXTERN2;
+ drv->wdt = a21_wdt;
+ dev_set_drvdata(&pdev->dev, drv);
+
ret = watchdog_register_device(&a21_wdt);
if (ret) {
dev_err(&pdev->dev, "Cannot register watchdog device\n");
goto err_register_wd;
}
- dev_set_drvdata(&pdev->dev, drv);
-
dev_info(&pdev->dev, "MEN A21 watchdog timer driver enabled\n");
return 0;
diff --git a/drivers/watchdog/octeon-wdt-main.c b/drivers/watchdog/octeon-wdt-main.c
index 8453531545df..14521c8b3d5a 100644
--- a/drivers/watchdog/octeon-wdt-main.c
+++ b/drivers/watchdog/octeon-wdt-main.c
@@ -3,6 +3,8 @@
*
* Copyright (C) 2007, 2008, 2009, 2010 Cavium Networks
*
+ * Converted to use WATCHDOG_CORE by Aaro Koskinen <aaro.koskinen@iki.fi>.
+ *
* Some parts derived from wdt.c
*
* (c) Copyright 1996-1997 Alan Cox <alan@lxorguk.ukuu.org.uk>,
@@ -103,13 +105,10 @@ MODULE_PARM_DESC(nowayout,
"Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
-static unsigned long octeon_wdt_is_open;
-static char expect_close;
-
-static u32 __initdata nmi_stage1_insns[64];
+static u32 nmi_stage1_insns[64] __initdata;
/* We need one branch and therefore one relocation per target label. */
-static struct uasm_label __initdata labels[5];
-static struct uasm_reloc __initdata relocs[5];
+static struct uasm_label labels[5] __initdata;
+static struct uasm_reloc relocs[5] __initdata;
enum lable_id {
label_enter_bootloader = 1
@@ -218,7 +217,8 @@ static void __init octeon_wdt_build_stage1(void)
pr_debug("\t.set pop\n");
if (len > 32)
- panic("NMI stage 1 handler exceeds 32 instructions, was %d\n", len);
+ panic("NMI stage 1 handler exceeds 32 instructions, was %d\n",
+ len);
}
static int cpu2core(int cpu)
@@ -294,6 +294,7 @@ static void octeon_wdt_write_hex(u64 value, int digits)
{
int d;
int v;
+
for (d = 0; d < digits; d++) {
v = (value >> ((digits - d - 1) * 4)) & 0xf;
if (v >= 10)
@@ -303,7 +304,7 @@ static void octeon_wdt_write_hex(u64 value, int digits)
}
}
-const char *reg_name[] = {
+static const char reg_name[][3] = {
"$0", "at", "v0", "v1", "a0", "a1", "a2", "a3",
"a4", "a5", "a6", "a7", "t0", "t1", "t2", "t3",
"s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7",
@@ -444,7 +445,7 @@ static int octeon_wdt_cpu_callback(struct notifier_block *nfb,
return NOTIFY_OK;
}
-static void octeon_wdt_ping(void)
+static int octeon_wdt_ping(struct watchdog_device __always_unused *wdog)
{
int cpu;
int coreid;
@@ -457,10 +458,12 @@ static void octeon_wdt_ping(void)
!cpumask_test_cpu(cpu, &irq_enabled_cpus)) {
/* We have to enable the irq */
int irq = OCTEON_IRQ_WDOG0 + coreid;
+
enable_irq(irq);
cpumask_set_cpu(cpu, &irq_enabled_cpus);
}
}
+ return 0;
}
static void octeon_wdt_calc_parameters(int t)
@@ -489,7 +492,8 @@ static void octeon_wdt_calc_parameters(int t)
timeout_cnt = ((octeon_get_io_clock_rate() >> 8) * timeout_sec) >> 8;
}
-static int octeon_wdt_set_heartbeat(int t)
+static int octeon_wdt_set_timeout(struct watchdog_device *wdog,
+ unsigned int t)
{
int cpu;
int coreid;
@@ -509,158 +513,45 @@ static int octeon_wdt_set_heartbeat(int t)
cvmx_write_csr(CVMX_CIU_WDOGX(coreid), ciu_wdog.u64);
cvmx_write_csr(CVMX_CIU_PP_POKEX(coreid), 1);
}
- octeon_wdt_ping(); /* Get the irqs back on. */
+ octeon_wdt_ping(wdog); /* Get the irqs back on. */
return 0;
}
-/**
- * octeon_wdt_write:
- * @file: file handle to the watchdog
- * @buf: buffer to write (unused as data does not matter here
- * @count: count of bytes
- * @ppos: pointer to the position to write. No seeks allowed
- *
- * A write to a watchdog device is defined as a keepalive signal. Any
- * write of data will do, as we we don't define content meaning.
- */
-
-static ssize_t octeon_wdt_write(struct file *file, const char __user *buf,
- size_t count, loff_t *ppos)
-{
- if (count) {
- if (!nowayout) {
- size_t i;
-
- /* In case it was set long ago */
- expect_close = 0;
-
- for (i = 0; i != count; i++) {
- char c;
- if (get_user(c, buf + i))
- return -EFAULT;
- if (c == 'V')
- expect_close = 1;
- }
- }
- octeon_wdt_ping();
- }
- return count;
-}
-
-/**
- * octeon_wdt_ioctl:
- * @file: file handle to the device
- * @cmd: watchdog command
- * @arg: argument pointer
- *
- * The watchdog API defines a common set of functions for all
- * watchdogs according to their available features. We only
- * actually usefully support querying capabilities and setting
- * the timeout.
- */
-
-static long octeon_wdt_ioctl(struct file *file, unsigned int cmd,
- unsigned long arg)
-{
- void __user *argp = (void __user *)arg;
- int __user *p = argp;
- int new_heartbeat;
-
- static struct watchdog_info ident = {
- .options = WDIOF_SETTIMEOUT|
- WDIOF_MAGICCLOSE|
- WDIOF_KEEPALIVEPING,
- .firmware_version = 1,
- .identity = "OCTEON",
- };
-
- switch (cmd) {
- case WDIOC_GETSUPPORT:
- return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
- case WDIOC_GETSTATUS:
- case WDIOC_GETBOOTSTATUS:
- return put_user(0, p);
- case WDIOC_KEEPALIVE:
- octeon_wdt_ping();
- return 0;
- case WDIOC_SETTIMEOUT:
- if (get_user(new_heartbeat, p))
- return -EFAULT;
- if (octeon_wdt_set_heartbeat(new_heartbeat))
- return -EINVAL;
- /* Fall through. */
- case WDIOC_GETTIMEOUT:
- return put_user(heartbeat, p);
- default:
- return -ENOTTY;
- }
-}
-
-/**
- * octeon_wdt_open:
- * @inode: inode of device
- * @file: file handle to device
- *
- * The watchdog device has been opened. The watchdog device is single
- * open and on opening we do a ping to reset the counters.
- */
-
-static int octeon_wdt_open(struct inode *inode, struct file *file)
+static int octeon_wdt_start(struct watchdog_device *wdog)
{
- if (test_and_set_bit(0, &octeon_wdt_is_open))
- return -EBUSY;
- /*
- * Activate
- */
- octeon_wdt_ping();
+ octeon_wdt_ping(wdog);
do_coundown = 1;
- return nonseekable_open(inode, file);
+ return 0;
}
-/**
- * octeon_wdt_release:
- * @inode: inode to board
- * @file: file handle to board
- *
- * The watchdog has a configurable API. There is a religious dispute
- * between people who want their watchdog to be able to shut down and
- * those who want to be sure if the watchdog manager dies the machine
- * reboots. In the former case we disable the counters, in the latter
- * case you have to open it again very soon.
- */
-
-static int octeon_wdt_release(struct inode *inode, struct file *file)
+static int octeon_wdt_stop(struct watchdog_device *wdog)
{
- if (expect_close) {
- do_coundown = 0;
- octeon_wdt_ping();
- } else {
- pr_crit("WDT device closed unexpectedly. WDT will not stop!\n");
- }
- clear_bit(0, &octeon_wdt_is_open);
- expect_close = 0;
+ do_coundown = 0;
+ octeon_wdt_ping(wdog);
return 0;
}
-static const struct file_operations octeon_wdt_fops = {
- .owner = THIS_MODULE,
- .llseek = no_llseek,
- .write = octeon_wdt_write,
- .unlocked_ioctl = octeon_wdt_ioctl,
- .open = octeon_wdt_open,
- .release = octeon_wdt_release,
+static struct notifier_block octeon_wdt_cpu_notifier = {
+ .notifier_call = octeon_wdt_cpu_callback,
};
-static struct miscdevice octeon_wdt_miscdev = {
- .minor = WATCHDOG_MINOR,
- .name = "watchdog",
- .fops = &octeon_wdt_fops,
+static const struct watchdog_info octeon_wdt_info = {
+ .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING,
+ .identity = "OCTEON",
};
-static struct notifier_block octeon_wdt_cpu_notifier = {
- .notifier_call = octeon_wdt_cpu_callback,
+static const struct watchdog_ops octeon_wdt_ops = {
+ .owner = THIS_MODULE,
+ .start = octeon_wdt_start,
+ .stop = octeon_wdt_stop,
+ .ping = octeon_wdt_ping,
+ .set_timeout = octeon_wdt_set_timeout,
};
+static struct watchdog_device octeon_wdt = {
+ .info = &octeon_wdt_info,
+ .ops = &octeon_wdt_ops,
+};
/**
* Module/ driver initialization.
@@ -685,7 +576,8 @@ static int __init octeon_wdt_init(void)
max_timeout_sec = 6;
do {
max_timeout_sec--;
- timeout_cnt = ((octeon_get_io_clock_rate() >> 8) * max_timeout_sec) >> 8;
+ timeout_cnt = ((octeon_get_io_clock_rate() >> 8) *
+ max_timeout_sec) >> 8;
} while (timeout_cnt > 65535);
BUG_ON(timeout_cnt == 0);
@@ -694,11 +586,15 @@ static int __init octeon_wdt_init(void)
pr_info("Initial granularity %d Sec\n", timeout_sec);
- ret = misc_register(&octeon_wdt_miscdev);
+ octeon_wdt.timeout = timeout_sec;
+ octeon_wdt.max_timeout = UINT_MAX;
+
+ watchdog_set_nowayout(&octeon_wdt, nowayout);
+
+ ret = watchdog_register_device(&octeon_wdt);
if (ret) {
- pr_err("cannot register miscdev on minor=%d (err=%d)\n",
- WATCHDOG_MINOR, ret);
- goto out;
+ pr_err("watchdog_register_device() failed: %d\n", ret);
+ return ret;
}
/* Build the NMI handler ... */
@@ -721,8 +617,7 @@ static int __init octeon_wdt_init(void)
__register_hotcpu_notifier(&octeon_wdt_cpu_notifier);
cpu_notifier_register_done();
-out:
- return ret;
+ return 0;
}
/**
@@ -732,7 +627,7 @@ static void __exit octeon_wdt_cleanup(void)
{
int cpu;
- misc_deregister(&octeon_wdt_miscdev);
+ watchdog_unregister_device(&octeon_wdt);
cpu_notifier_register_begin();
__unregister_hotcpu_notifier(&octeon_wdt_cpu_notifier);
diff --git a/drivers/watchdog/omap_wdt.c b/drivers/watchdog/omap_wdt.c
index 1e6be9e40577..de911c7e477c 100644
--- a/drivers/watchdog/omap_wdt.c
+++ b/drivers/watchdog/omap_wdt.c
@@ -53,7 +53,15 @@ static unsigned timer_margin;
module_param(timer_margin, uint, 0);
MODULE_PARM_DESC(timer_margin, "initial watchdog timeout (in seconds)");
+#define to_omap_wdt_dev(_wdog) container_of(_wdog, struct omap_wdt_dev, wdog)
+
+static bool early_enable;
+module_param(early_enable, bool, 0);
+MODULE_PARM_DESC(early_enable,
+ "Watchdog is started on module insertion (default=0)");
+
struct omap_wdt_dev {
+ struct watchdog_device wdog;
void __iomem *base; /* physical */
struct device *dev;
bool omap_wdt_users;
@@ -123,7 +131,7 @@ static void omap_wdt_set_timer(struct omap_wdt_dev *wdev,
static int omap_wdt_start(struct watchdog_device *wdog)
{
- struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog);
+ struct omap_wdt_dev *wdev = to_omap_wdt_dev(wdog);
void __iomem *base = wdev->base;
mutex_lock(&wdev->lock);
@@ -132,6 +140,13 @@ static int omap_wdt_start(struct watchdog_device *wdog)
pm_runtime_get_sync(wdev->dev);
+ /*
+ * Make sure the watchdog is disabled. This is unfortunately required
+ * because writing to various registers with the watchdog running has no
+ * effect.
+ */
+ omap_wdt_disable(wdev);
+
/* initialize prescaler */
while (readl_relaxed(base + OMAP_WATCHDOG_WPS) & 0x01)
cpu_relax();
@@ -151,7 +166,7 @@ static int omap_wdt_start(struct watchdog_device *wdog)
static int omap_wdt_stop(struct watchdog_device *wdog)
{
- struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog);
+ struct omap_wdt_dev *wdev = to_omap_wdt_dev(wdog);
mutex_lock(&wdev->lock);
omap_wdt_disable(wdev);
@@ -163,7 +178,7 @@ static int omap_wdt_stop(struct watchdog_device *wdog)
static int omap_wdt_ping(struct watchdog_device *wdog)
{
- struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog);
+ struct omap_wdt_dev *wdev = to_omap_wdt_dev(wdog);
mutex_lock(&wdev->lock);
omap_wdt_reload(wdev);
@@ -175,7 +190,7 @@ static int omap_wdt_ping(struct watchdog_device *wdog)
static int omap_wdt_set_timeout(struct watchdog_device *wdog,
unsigned int timeout)
{
- struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog);
+ struct omap_wdt_dev *wdev = to_omap_wdt_dev(wdog);
mutex_lock(&wdev->lock);
omap_wdt_disable(wdev);
@@ -188,6 +203,16 @@ static int omap_wdt_set_timeout(struct watchdog_device *wdog,
return 0;
}
+static unsigned int omap_wdt_get_timeleft(struct watchdog_device *wdog)
+{
+ struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog);
+ void __iomem *base = wdev->base;
+ u32 value;
+
+ value = readl_relaxed(base + OMAP_WATCHDOG_CRR);
+ return GET_WCCR_SECS(value);
+}
+
static const struct watchdog_info omap_wdt_info = {
.options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING,
.identity = "OMAP Watchdog",
@@ -199,21 +224,16 @@ static const struct watchdog_ops omap_wdt_ops = {
.stop = omap_wdt_stop,
.ping = omap_wdt_ping,
.set_timeout = omap_wdt_set_timeout,
+ .get_timeleft = omap_wdt_get_timeleft,
};
static int omap_wdt_probe(struct platform_device *pdev)
{
struct omap_wd_timer_platform_data *pdata = dev_get_platdata(&pdev->dev);
- struct watchdog_device *omap_wdt;
struct resource *res;
struct omap_wdt_dev *wdev;
- u32 rs;
int ret;
- omap_wdt = devm_kzalloc(&pdev->dev, sizeof(*omap_wdt), GFP_KERNEL);
- if (!omap_wdt)
- return -ENOMEM;
-
wdev = devm_kzalloc(&pdev->dev, sizeof(*wdev), GFP_KERNEL);
if (!wdev)
return -ENOMEM;
@@ -229,35 +249,30 @@ static int omap_wdt_probe(struct platform_device *pdev)
if (IS_ERR(wdev->base))
return PTR_ERR(wdev->base);
- omap_wdt->info = &omap_wdt_info;
- omap_wdt->ops = &omap_wdt_ops;
- omap_wdt->min_timeout = TIMER_MARGIN_MIN;
- omap_wdt->max_timeout = TIMER_MARGIN_MAX;
+ wdev->wdog.info = &omap_wdt_info;
+ wdev->wdog.ops = &omap_wdt_ops;
+ wdev->wdog.min_timeout = TIMER_MARGIN_MIN;
+ wdev->wdog.max_timeout = TIMER_MARGIN_MAX;
- if (timer_margin >= TIMER_MARGIN_MIN &&
- timer_margin <= TIMER_MARGIN_MAX)
- omap_wdt->timeout = timer_margin;
- else
- omap_wdt->timeout = TIMER_MARGIN_DEFAULT;
+ if (watchdog_init_timeout(&wdev->wdog, timer_margin, &pdev->dev) < 0)
+ wdev->wdog.timeout = TIMER_MARGIN_DEFAULT;
- watchdog_set_drvdata(omap_wdt, wdev);
- watchdog_set_nowayout(omap_wdt, nowayout);
+ watchdog_set_nowayout(&wdev->wdog, nowayout);
- platform_set_drvdata(pdev, omap_wdt);
+ platform_set_drvdata(pdev, wdev);
pm_runtime_enable(wdev->dev);
pm_runtime_get_sync(wdev->dev);
- if (pdata && pdata->read_reset_sources)
- rs = pdata->read_reset_sources();
- else
- rs = 0;
- omap_wdt->bootstatus = (rs & (1 << OMAP_MPU_WD_RST_SRC_ID_SHIFT)) ?
- WDIOF_CARDRESET : 0;
+ if (pdata && pdata->read_reset_sources) {
+ u32 rs = pdata->read_reset_sources();
+ if (rs & (1 << OMAP_MPU_WD_RST_SRC_ID_SHIFT))
+ wdev->wdog.bootstatus = WDIOF_CARDRESET;
+ }
omap_wdt_disable(wdev);
- ret = watchdog_register_device(omap_wdt);
+ ret = watchdog_register_device(&wdev->wdog);
if (ret) {
pm_runtime_disable(wdev->dev);
return ret;
@@ -265,17 +280,19 @@ static int omap_wdt_probe(struct platform_device *pdev)
pr_info("OMAP Watchdog Timer Rev 0x%02x: initial timeout %d sec\n",
readl_relaxed(wdev->base + OMAP_WATCHDOG_REV) & 0xFF,
- omap_wdt->timeout);
+ wdev->wdog.timeout);
pm_runtime_put_sync(wdev->dev);
+ if (early_enable)
+ omap_wdt_start(&wdev->wdog);
+
return 0;
}
static void omap_wdt_shutdown(struct platform_device *pdev)
{
- struct watchdog_device *wdog = platform_get_drvdata(pdev);
- struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog);
+ struct omap_wdt_dev *wdev = platform_get_drvdata(pdev);
mutex_lock(&wdev->lock);
if (wdev->omap_wdt_users) {
@@ -287,11 +304,10 @@ static void omap_wdt_shutdown(struct platform_device *pdev)
static int omap_wdt_remove(struct platform_device *pdev)
{
- struct watchdog_device *wdog = platform_get_drvdata(pdev);
- struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog);
+ struct omap_wdt_dev *wdev = platform_get_drvdata(pdev);
pm_runtime_disable(wdev->dev);
- watchdog_unregister_device(wdog);
+ watchdog_unregister_device(&wdev->wdog);
return 0;
}
@@ -306,8 +322,7 @@ static int omap_wdt_remove(struct platform_device *pdev)
static int omap_wdt_suspend(struct platform_device *pdev, pm_message_t state)
{
- struct watchdog_device *wdog = platform_get_drvdata(pdev);
- struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog);
+ struct omap_wdt_dev *wdev = platform_get_drvdata(pdev);
mutex_lock(&wdev->lock);
if (wdev->omap_wdt_users) {
@@ -321,8 +336,7 @@ static int omap_wdt_suspend(struct platform_device *pdev, pm_message_t state)
static int omap_wdt_resume(struct platform_device *pdev)
{
- struct watchdog_device *wdog = platform_get_drvdata(pdev);
- struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog);
+ struct omap_wdt_dev *wdev = platform_get_drvdata(pdev);
mutex_lock(&wdev->lock);
if (wdev->omap_wdt_users) {
diff --git a/drivers/watchdog/omap_wdt.h b/drivers/watchdog/omap_wdt.h
index 09b774cf75b9..42f31ec5e90d 100644
--- a/drivers/watchdog/omap_wdt.h
+++ b/drivers/watchdog/omap_wdt.h
@@ -50,5 +50,6 @@
#define PTV 0 /* prescale */
#define GET_WLDR_VAL(secs) (0xffffffff - ((secs) * (32768/(1<<PTV))) + 1)
+#define GET_WCCR_SECS(val) ((0xffffffff - (val) + 1) / (32768/(1<<PTV)))
#endif /* _OMAP_WATCHDOG_H */
diff --git a/drivers/watchdog/pnx4008_wdt.c b/drivers/watchdog/pnx4008_wdt.c
index 55e220150103..b9c6049c3e78 100644
--- a/drivers/watchdog/pnx4008_wdt.c
+++ b/drivers/watchdog/pnx4008_wdt.c
@@ -216,7 +216,7 @@ static struct platform_driver platform_wdt_driver = {
module_platform_driver(platform_wdt_driver);
MODULE_AUTHOR("MontaVista Software, Inc. <source@mvista.com>");
-MODULE_AUTHOR("Wolfram Sang <w.sang@pengutronix.de>");
+MODULE_AUTHOR("Wolfram Sang <kernel@pengutronix.de>");
MODULE_DESCRIPTION("PNX4008 Watchdog Driver");
module_param(heartbeat, uint, 0);
diff --git a/drivers/watchdog/qcom-wdt.c b/drivers/watchdog/qcom-wdt.c
index aa85618c4d03..aa03ca8f2d9b 100644
--- a/drivers/watchdog/qcom-wdt.c
+++ b/drivers/watchdog/qcom-wdt.c
@@ -20,9 +20,9 @@
#include <linux/reboot.h>
#include <linux/watchdog.h>
-#define WDT_RST 0x0
-#define WDT_EN 0x8
-#define WDT_BITE_TIME 0x24
+#define WDT_RST 0x38
+#define WDT_EN 0x40
+#define WDT_BITE_TIME 0x5C
struct qcom_wdt {
struct watchdog_device wdd;
@@ -117,6 +117,8 @@ static int qcom_wdt_probe(struct platform_device *pdev)
{
struct qcom_wdt *wdt;
struct resource *res;
+ struct device_node *np = pdev->dev.of_node;
+ u32 percpu_offset;
int ret;
wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL);
@@ -124,6 +126,14 @@ static int qcom_wdt_probe(struct platform_device *pdev)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ /* We use CPU0's DGT for the watchdog */
+ if (of_property_read_u32(np, "cpu-offset", &percpu_offset))
+ percpu_offset = 0;
+
+ res->start += percpu_offset;
+ res->end += percpu_offset;
+
wdt->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(wdt->base))
return PTR_ERR(wdt->base);
@@ -203,9 +213,8 @@ static int qcom_wdt_remove(struct platform_device *pdev)
}
static const struct of_device_id qcom_wdt_of_table[] = {
- { .compatible = "qcom,kpss-wdt-msm8960", },
- { .compatible = "qcom,kpss-wdt-apq8064", },
- { .compatible = "qcom,kpss-wdt-ipq8064", },
+ { .compatible = "qcom,kpss-timer" },
+ { .compatible = "qcom,scss-timer" },
{ },
};
MODULE_DEVICE_TABLE(of, qcom_wdt_of_table);
diff --git a/drivers/watchdog/sp805_wdt.c b/drivers/watchdog/sp805_wdt.c
index c1b03f4235b9..4e7fec36f5c3 100644
--- a/drivers/watchdog/sp805_wdt.c
+++ b/drivers/watchdog/sp805_wdt.c
@@ -4,7 +4,7 @@
* Watchdog driver for ARM SP805 watchdog module
*
* Copyright (C) 2010 ST Microelectronics
- * Viresh Kumar <viresh.linux@gmail.com>
+ * Viresh Kumar <vireshk@kernel.org>
*
* This file is licensed under the terms of the GNU General Public
* License version 2 or later. This program is licensed "as is" without any
@@ -303,6 +303,6 @@ static struct amba_driver sp805_wdt_driver = {
module_amba_driver(sp805_wdt_driver);
-MODULE_AUTHOR("Viresh Kumar <viresh.linux@gmail.com>");
+MODULE_AUTHOR("Viresh Kumar <vireshk@kernel.org>");
MODULE_DESCRIPTION("ARM SP805 Watchdog Driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/watchdog/st_lpc_wdt.c b/drivers/watchdog/st_lpc_wdt.c
new file mode 100644
index 000000000000..6785afdc0fca
--- /dev/null
+++ b/drivers/watchdog/st_lpc_wdt.c
@@ -0,0 +1,344 @@
+/*
+ * ST's LPC Watchdog
+ *
+ * Copyright (C) 2014 STMicroelectronics -- All Rights Reserved
+ *
+ * Author: David Paris <david.paris@st.com> for STMicroelectronics
+ * Lee Jones <lee.jones@linaro.org> for STMicroelectronics
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/watchdog.h>
+
+#include <dt-bindings/mfd/st-lpc.h>
+
+/* Low Power Alarm */
+#define LPC_LPA_LSB_OFF 0x410
+#define LPC_LPA_START_OFF 0x418
+
+/* LPC as WDT */
+#define LPC_WDT_OFF 0x510
+
+static struct watchdog_device st_wdog_dev;
+
+struct st_wdog_syscfg {
+ unsigned int reset_type_reg;
+ unsigned int reset_type_mask;
+ unsigned int enable_reg;
+ unsigned int enable_mask;
+};
+
+struct st_wdog {
+ void __iomem *base;
+ struct device *dev;
+ struct regmap *regmap;
+ struct st_wdog_syscfg *syscfg;
+ struct clk *clk;
+ unsigned long clkrate;
+ bool warm_reset;
+};
+
+static struct st_wdog_syscfg stid127_syscfg = {
+ .reset_type_reg = 0x004,
+ .reset_type_mask = BIT(2),
+ .enable_reg = 0x000,
+ .enable_mask = BIT(2),
+};
+
+static struct st_wdog_syscfg stih415_syscfg = {
+ .reset_type_reg = 0x0B8,
+ .reset_type_mask = BIT(6),
+ .enable_reg = 0x0B4,
+ .enable_mask = BIT(7),
+};
+
+static struct st_wdog_syscfg stih416_syscfg = {
+ .reset_type_reg = 0x88C,
+ .reset_type_mask = BIT(6),
+ .enable_reg = 0x888,
+ .enable_mask = BIT(7),
+};
+
+static struct st_wdog_syscfg stih407_syscfg = {
+ .enable_reg = 0x204,
+ .enable_mask = BIT(19),
+};
+
+static const struct of_device_id st_wdog_match[] = {
+ {
+ .compatible = "st,stih407-lpc",
+ .data = &stih407_syscfg,
+ },
+ {
+ .compatible = "st,stih416-lpc",
+ .data = &stih416_syscfg,
+ },
+ {
+ .compatible = "st,stih415-lpc",
+ .data = &stih415_syscfg,
+ },
+ {
+ .compatible = "st,stid127-lpc",
+ .data = &stid127_syscfg,
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, st_wdog_match);
+
+static void st_wdog_setup(struct st_wdog *st_wdog, bool enable)
+{
+ /* Type of watchdog reset - 0: Cold 1: Warm */
+ if (st_wdog->syscfg->reset_type_reg)
+ regmap_update_bits(st_wdog->regmap,
+ st_wdog->syscfg->reset_type_reg,
+ st_wdog->syscfg->reset_type_mask,
+ st_wdog->warm_reset);
+
+ /* Mask/unmask watchdog reset */
+ regmap_update_bits(st_wdog->regmap,
+ st_wdog->syscfg->enable_reg,
+ st_wdog->syscfg->enable_mask,
+ enable ? 0 : st_wdog->syscfg->enable_mask);
+}
+
+static void st_wdog_load_timer(struct st_wdog *st_wdog, unsigned int timeout)
+{
+ unsigned long clkrate = st_wdog->clkrate;
+
+ writel_relaxed(timeout * clkrate, st_wdog->base + LPC_LPA_LSB_OFF);
+ writel_relaxed(1, st_wdog->base + LPC_LPA_START_OFF);
+}
+
+static int st_wdog_start(struct watchdog_device *wdd)
+{
+ struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
+
+ writel_relaxed(1, st_wdog->base + LPC_WDT_OFF);
+
+ return 0;
+}
+
+static int st_wdog_stop(struct watchdog_device *wdd)
+{
+ struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
+
+ writel_relaxed(0, st_wdog->base + LPC_WDT_OFF);
+
+ return 0;
+}
+
+static int st_wdog_set_timeout(struct watchdog_device *wdd,
+ unsigned int timeout)
+{
+ struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
+
+ wdd->timeout = timeout;
+ st_wdog_load_timer(st_wdog, timeout);
+
+ return 0;
+}
+
+static int st_wdog_keepalive(struct watchdog_device *wdd)
+{
+ struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
+
+ st_wdog_load_timer(st_wdog, wdd->timeout);
+
+ return 0;
+}
+
+static const struct watchdog_info st_wdog_info = {
+ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
+ .identity = "ST LPC WDT",
+};
+
+static const struct watchdog_ops st_wdog_ops = {
+ .owner = THIS_MODULE,
+ .start = st_wdog_start,
+ .stop = st_wdog_stop,
+ .ping = st_wdog_keepalive,
+ .set_timeout = st_wdog_set_timeout,
+};
+
+static struct watchdog_device st_wdog_dev = {
+ .info = &st_wdog_info,
+ .ops = &st_wdog_ops,
+};
+
+static int st_wdog_probe(struct platform_device *pdev)
+{
+ const struct of_device_id *match;
+ struct device_node *np = pdev->dev.of_node;
+ struct st_wdog *st_wdog;
+ struct regmap *regmap;
+ struct resource *res;
+ struct clk *clk;
+ void __iomem *base;
+ uint32_t mode;
+ int ret;
+
+ ret = of_property_read_u32(np, "st,lpc-mode", &mode);
+ if (ret) {
+ dev_err(&pdev->dev, "An LPC mode must be provided\n");
+ return -EINVAL;
+ }
+
+ /* LPC can either run as a Clocksource or in RTC or WDT mode */
+ if (mode != ST_LPC_MODE_WDT)
+ return -ENODEV;
+
+ st_wdog = devm_kzalloc(&pdev->dev, sizeof(*st_wdog), GFP_KERNEL);
+ if (!st_wdog)
+ return -ENOMEM;
+
+ match = of_match_device(st_wdog_match, &pdev->dev);
+ if (!match) {
+ dev_err(&pdev->dev, "Couldn't match device\n");
+ return -ENODEV;
+ }
+ st_wdog->syscfg = (struct st_wdog_syscfg *)match->data;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg");
+ if (IS_ERR(regmap)) {
+ dev_err(&pdev->dev, "No syscfg phandle specified\n");
+ return PTR_ERR(regmap);
+ }
+
+ clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(clk)) {
+ dev_err(&pdev->dev, "Unable to request clock\n");
+ return PTR_ERR(clk);
+ }
+
+ st_wdog->dev = &pdev->dev;
+ st_wdog->base = base;
+ st_wdog->clk = clk;
+ st_wdog->regmap = regmap;
+ st_wdog->warm_reset = of_property_read_bool(np, "st,warm_reset");
+ st_wdog->clkrate = clk_get_rate(st_wdog->clk);
+
+ if (!st_wdog->clkrate) {
+ dev_err(&pdev->dev, "Unable to fetch clock rate\n");
+ return -EINVAL;
+ }
+ st_wdog_dev.max_timeout = 0xFFFFFFFF / st_wdog->clkrate;
+
+ ret = clk_prepare_enable(clk);
+ if (ret) {
+ dev_err(&pdev->dev, "Unable to enable clock\n");
+ return ret;
+ }
+
+ watchdog_set_drvdata(&st_wdog_dev, st_wdog);
+ watchdog_set_nowayout(&st_wdog_dev, WATCHDOG_NOWAYOUT);
+
+ /* Init Watchdog timeout with value in DT */
+ ret = watchdog_init_timeout(&st_wdog_dev, 0, &pdev->dev);
+ if (ret) {
+ dev_err(&pdev->dev, "Unable to initialise watchdog timeout\n");
+ clk_disable_unprepare(clk);
+ return ret;
+ }
+
+ ret = watchdog_register_device(&st_wdog_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "Unable to register watchdog\n");
+ clk_disable_unprepare(clk);
+ return ret;
+ }
+
+ st_wdog_setup(st_wdog, true);
+
+ dev_info(&pdev->dev, "LPC Watchdog driver registered, reset type is %s",
+ st_wdog->warm_reset ? "warm" : "cold");
+
+ return ret;
+}
+
+static int st_wdog_remove(struct platform_device *pdev)
+{
+ struct st_wdog *st_wdog = watchdog_get_drvdata(&st_wdog_dev);
+
+ st_wdog_setup(st_wdog, false);
+ watchdog_unregister_device(&st_wdog_dev);
+ clk_disable_unprepare(st_wdog->clk);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int st_wdog_suspend(struct device *dev)
+{
+ struct st_wdog *st_wdog = watchdog_get_drvdata(&st_wdog_dev);
+
+ if (watchdog_active(&st_wdog_dev))
+ st_wdog_stop(&st_wdog_dev);
+
+ st_wdog_setup(st_wdog, false);
+
+ clk_disable(st_wdog->clk);
+
+ return 0;
+}
+
+static int st_wdog_resume(struct device *dev)
+{
+ struct st_wdog *st_wdog = watchdog_get_drvdata(&st_wdog_dev);
+ int ret;
+
+ ret = clk_enable(st_wdog->clk);
+ if (ret) {
+ dev_err(dev, "Unable to re-enable clock\n");
+ watchdog_unregister_device(&st_wdog_dev);
+ clk_unprepare(st_wdog->clk);
+ return ret;
+ }
+
+ st_wdog_setup(st_wdog, true);
+
+ if (watchdog_active(&st_wdog_dev)) {
+ st_wdog_load_timer(st_wdog, st_wdog_dev.timeout);
+ st_wdog_start(&st_wdog_dev);
+ }
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(st_wdog_pm_ops,
+ st_wdog_suspend,
+ st_wdog_resume);
+
+static struct platform_driver st_wdog_driver = {
+ .driver = {
+ .name = "st-lpc-wdt",
+ .pm = &st_wdog_pm_ops,
+ .of_match_table = st_wdog_match,
+ },
+ .probe = st_wdog_probe,
+ .remove = st_wdog_remove,
+};
+module_platform_driver(st_wdog_driver);
+
+MODULE_AUTHOR("David Paris <david.paris@st.com>");
+MODULE_DESCRIPTION("ST LPC Watchdog Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/watchdog/stmp3xxx_rtc_wdt.c b/drivers/watchdog/stmp3xxx_rtc_wdt.c
index a62b1b6decf4..e7f0d5b60d3d 100644
--- a/drivers/watchdog/stmp3xxx_rtc_wdt.c
+++ b/drivers/watchdog/stmp3xxx_rtc_wdt.c
@@ -1,7 +1,7 @@
/*
* Watchdog driver for the RTC based watchdog in STMP3xxx and i.MX23/28
*
- * Author: Wolfram Sang <w.sang@pengutronix.de>
+ * Author: Wolfram Sang <kernel@pengutronix.de>
*
* Copyright (C) 2011-12 Wolfram Sang, Pengutronix
*
@@ -129,4 +129,4 @@ module_platform_driver(stmp3xxx_wdt_driver);
MODULE_DESCRIPTION("STMP3XXX RTC Watchdog Driver");
MODULE_LICENSE("GPL v2");
-MODULE_AUTHOR("Wolfram Sang <w.sang@pengutronix.de>");
+MODULE_AUTHOR("Wolfram Sang <kernel@pengutronix.de>");
diff --git a/drivers/watchdog/watchdog_core.c b/drivers/watchdog/watchdog_core.c
index cec9b559647d..1a8059455413 100644
--- a/drivers/watchdog/watchdog_core.c
+++ b/drivers/watchdog/watchdog_core.c
@@ -43,6 +43,45 @@
static DEFINE_IDA(watchdog_ida);
static struct class *watchdog_class;
+/*
+ * Deferred Registration infrastructure.
+ *
+ * Sometimes watchdog drivers needs to be loaded as soon as possible,
+ * for example when it's impossible to disable it. To do so,
+ * raising the initcall level of the watchdog driver is a solution.
+ * But in such case, the miscdev is maybe not ready (subsys_initcall), and
+ * watchdog_core need miscdev to register the watchdog as a char device.
+ *
+ * The deferred registration infrastructure offer a way for the watchdog
+ * subsystem to register a watchdog properly, even before miscdev is ready.
+ */
+
+static DEFINE_MUTEX(wtd_deferred_reg_mutex);
+static LIST_HEAD(wtd_deferred_reg_list);
+static bool wtd_deferred_reg_done;
+
+static int watchdog_deferred_registration_add(struct watchdog_device *wdd)
+{
+ list_add_tail(&wdd->deferred,
+ &wtd_deferred_reg_list);
+ return 0;
+}
+
+static void watchdog_deferred_registration_del(struct watchdog_device *wdd)
+{
+ struct list_head *p, *n;
+ struct watchdog_device *wdd_tmp;
+
+ list_for_each_safe(p, n, &wtd_deferred_reg_list) {
+ wdd_tmp = list_entry(p, struct watchdog_device,
+ deferred);
+ if (wdd_tmp == wdd) {
+ list_del(&wdd_tmp->deferred);
+ break;
+ }
+ }
+}
+
static void watchdog_check_min_max_timeout(struct watchdog_device *wdd)
{
/*
@@ -98,17 +137,7 @@ int watchdog_init_timeout(struct watchdog_device *wdd,
}
EXPORT_SYMBOL_GPL(watchdog_init_timeout);
-/**
- * watchdog_register_device() - register a watchdog device
- * @wdd: watchdog device
- *
- * Register a watchdog device with the kernel so that the
- * watchdog timer can be accessed from userspace.
- *
- * A zero is returned on success and a negative errno code for
- * failure.
- */
-int watchdog_register_device(struct watchdog_device *wdd)
+static int __watchdog_register_device(struct watchdog_device *wdd)
{
int ret, id, devno;
@@ -164,16 +193,33 @@ int watchdog_register_device(struct watchdog_device *wdd)
return 0;
}
-EXPORT_SYMBOL_GPL(watchdog_register_device);
/**
- * watchdog_unregister_device() - unregister a watchdog device
- * @wdd: watchdog device to unregister
+ * watchdog_register_device() - register a watchdog device
+ * @wdd: watchdog device
*
- * Unregister a watchdog device that was previously successfully
- * registered with watchdog_register_device().
+ * Register a watchdog device with the kernel so that the
+ * watchdog timer can be accessed from userspace.
+ *
+ * A zero is returned on success and a negative errno code for
+ * failure.
*/
-void watchdog_unregister_device(struct watchdog_device *wdd)
+
+int watchdog_register_device(struct watchdog_device *wdd)
+{
+ int ret;
+
+ mutex_lock(&wtd_deferred_reg_mutex);
+ if (wtd_deferred_reg_done)
+ ret = __watchdog_register_device(wdd);
+ else
+ ret = watchdog_deferred_registration_add(wdd);
+ mutex_unlock(&wtd_deferred_reg_mutex);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(watchdog_register_device);
+
+static void __watchdog_unregister_device(struct watchdog_device *wdd)
{
int ret;
int devno;
@@ -189,8 +235,43 @@ void watchdog_unregister_device(struct watchdog_device *wdd)
ida_simple_remove(&watchdog_ida, wdd->id);
wdd->dev = NULL;
}
+
+/**
+ * watchdog_unregister_device() - unregister a watchdog device
+ * @wdd: watchdog device to unregister
+ *
+ * Unregister a watchdog device that was previously successfully
+ * registered with watchdog_register_device().
+ */
+
+void watchdog_unregister_device(struct watchdog_device *wdd)
+{
+ mutex_lock(&wtd_deferred_reg_mutex);
+ if (wtd_deferred_reg_done)
+ __watchdog_unregister_device(wdd);
+ else
+ watchdog_deferred_registration_del(wdd);
+ mutex_unlock(&wtd_deferred_reg_mutex);
+}
+
EXPORT_SYMBOL_GPL(watchdog_unregister_device);
+static int __init watchdog_deferred_registration(void)
+{
+ mutex_lock(&wtd_deferred_reg_mutex);
+ wtd_deferred_reg_done = true;
+ while (!list_empty(&wtd_deferred_reg_list)) {
+ struct watchdog_device *wdd;
+
+ wdd = list_first_entry(&wtd_deferred_reg_list,
+ struct watchdog_device, deferred);
+ list_del(&wdd->deferred);
+ __watchdog_register_device(wdd);
+ }
+ mutex_unlock(&wtd_deferred_reg_mutex);
+ return 0;
+}
+
static int __init watchdog_init(void)
{
int err;
@@ -207,6 +288,7 @@ static int __init watchdog_init(void)
return err;
}
+ watchdog_deferred_registration();
return 0;
}
@@ -217,7 +299,7 @@ static void __exit watchdog_exit(void)
ida_destroy(&watchdog_ida);
}
-subsys_initcall(watchdog_init);
+subsys_initcall_sync(watchdog_init);
module_exit(watchdog_exit);
MODULE_AUTHOR("Alan Cox <alan@lxorguk.ukuu.org.uk>");