Commit 07d306c8 authored by Linus Torvalds's avatar Linus Torvalds

Merge git://www.linux-watchdog.org/linux-watchdog

Pull watchdog updates from Wim Van Sebroeck:

 - Add Renesas RZ/A WDT Watchdog driver

 - STM32 Independent WatchDoG (IWDG) support

 - UniPhier watchdog support

 - Add F71868 support

 - Add support for NCT6793D and NCT6795D

 - dw_wdt: add reset lines support

 - core: add option to avoid early handling of watchdog

 - core: introduce watchdog_worker_should_ping helper

 - Cleanups and improvements for sama5d4, intel-mid_wdt, s3c2410_wdt,
   orion_wdt, gpio_wdt, it87_wdt, meson_wdt, davinci_wdt, bcm47xx_wdt,
   zx2967_wdt, cadence_wdt

* git://www.linux-watchdog.org/linux-watchdog: (32 commits)
  watchdog: introduce watchdog_worker_should_ping helper
  watchdog: uniphier: add UniPhier watchdog driver
  dt-bindings: watchdog: add description for UniPhier WDT controller
  watchdog: cadence_wdt: make of_device_ids const.
  watchdog: zx2967: constify zx2967_wdt_ops.
  watchdog: bcm47xx_wdt: constify bcm47xx_wdt_hard_ops and bcm47xx_wdt_soft_ops
  watchdog: davinci: Add missing clk_disable_unprepare().
  watchdog: davinci: Handle return value of clk_prepare_enable
  watchdog: meson: Handle return value of clk_prepare_enable
  watchdog: it87: Add support for various Super-IO chips
  watchdog: it87: Use infrastructure to stop watchdog on reboot
  watchdog: it87: Drop support for resetting watchdog though CIR and Game port
  watchdog: it87: Convert to use watchdog core infrastructure
  watchdog: it87: Drop FSF mailing address
  watchdog: dw_wdt: get reset lines from dt
  watchdog: bindings: dw_wdt: add reset lines
  watchdog: w83627hf: Add support for NCT6793D and NCT6795D
  watchdog: core: add option to avoid early handling of watchdog
  watchdog: f71808e_wdt: Add F71868 support
  watchdog: Add STM32 IWDG driver
  ...
parents a3ddacba c013b65a
* Dialog Semiconductor DA9062/61 Watchdog Timer
Required properties:
- compatible: should be one of the following valid compatible string lines:
"dlg,da9061-watchdog", "dlg,da9062-watchdog"
"dlg,da9062-watchdog"
Example: DA9062
pmic0: da9062@58 {
watchdog {
compatible = "dlg,da9062-watchdog";
};
};
Example: DA9061 using a fall-back compatible for the DA9062 watchdog driver
pmic0: da9061@58 {
watchdog {
compatible = "dlg,da9061-watchdog", "dlg,da9062-watchdog";
};
};
...@@ -10,6 +10,8 @@ Required Properties: ...@@ -10,6 +10,8 @@ Required Properties:
Optional Properties: Optional Properties:
- interrupts : The interrupt used for the watchdog timeout warning. - interrupts : The interrupt used for the watchdog timeout warning.
- resets : phandle pointing to the system reset controller with
line index for the watchdog.
Example: Example:
...@@ -18,4 +20,5 @@ Example: ...@@ -18,4 +20,5 @@ Example:
reg = <0xffd02000 0x1000>; reg = <0xffd02000 0x1000>;
interrupts = <0 171 4>; interrupts = <0 171 4>;
clocks = <&per_base_clk>; clocks = <&per_base_clk>;
resets = <&rst WDT0_RESET>;
}; };
...@@ -2,10 +2,11 @@ Renesas Watchdog Timer (WDT) Controller ...@@ -2,10 +2,11 @@ Renesas Watchdog Timer (WDT) Controller
Required properties: Required properties:
- compatible : Should be "renesas,<soctype>-wdt", and - compatible : Should be "renesas,<soctype>-wdt", and
"renesas,rcar-gen3-wdt" as fallback. "renesas,rcar-gen3-wdt" or "renesas,rza-wdt" as fallback.
Examples with soctypes are: Examples with soctypes are:
- "renesas,r8a7795-wdt" (R-Car H3) - "renesas,r8a7795-wdt" (R-Car H3)
- "renesas,r8a7796-wdt" (R-Car M3-W) - "renesas,r8a7796-wdt" (R-Car M3-W)
- "renesas,r7s72100-wdt" (RZ/A1)
When compatible with the generic version, nodes must list the SoC-specific When compatible with the generic version, nodes must list the SoC-specific
version corresponding to the platform first, followed by the generic version corresponding to the platform first, followed by the generic
...@@ -17,6 +18,7 @@ Required properties: ...@@ -17,6 +18,7 @@ Required properties:
Optional properties: Optional properties:
- timeout-sec : Contains the watchdog timeout in seconds - timeout-sec : Contains the watchdog timeout in seconds
- power-domains : the power domain the WDT belongs to - power-domains : the power domain the WDT belongs to
- interrupts: Some WDTs have an interrupt when used in interval timer mode
Examples: Examples:
......
STM32 Independent WatchDoG (IWDG)
---------------------------------
Required properties:
- compatible: "st,stm32-iwdg"
- reg: physical base address and length of the registers set for the device
- clocks: must contain a single entry describing the clock input
Optional Properties:
- timeout-sec: Watchdog timeout value in seconds.
Example:
iwdg: watchdog@40003000 {
compatible = "st,stm32-iwdg";
reg = <0x40003000 0x400>;
clocks = <&clk_lsi>;
timeout-sec = <32>;
};
UniPhier watchdog timer controller
This UniPhier watchdog timer controller must be under sysctrl node.
Required properties:
- compatible: should be "socionext,uniphier-wdt"
Example:
sysctrl@61840000 {
compatible = "socionext,uniphier-ld11-sysctrl",
"simple-mfd", "syscon";
reg = <0x61840000 0x4000>;
watchdog {
compatible = "socionext,uniphier-wdt";
}
other nodes ...
};
...@@ -369,6 +369,12 @@ timeout: Watchdog timeout in seconds. (0<timeout<N, default=60) ...@@ -369,6 +369,12 @@ timeout: Watchdog timeout in seconds. (0<timeout<N, default=60)
nowayout: Watchdog cannot be stopped once started nowayout: Watchdog cannot be stopped once started
(default=kernel config parameter) (default=kernel config parameter)
------------------------------------------------- -------------------------------------------------
uniphier_wdt:
timeout: Watchdog timeout in power of two seconds.
(1 <= timeout <= 128, default=64)
nowayout: Watchdog cannot be stopped once started
(default=kernel config parameter)
-------------------------------------------------
w83627hf_wdt: w83627hf_wdt:
wdt_io: w83627hf/thf WDT io port (default 0x2E) wdt_io: w83627hf/thf WDT io port (default 0x2E)
timeout: Watchdog timeout in seconds. 1 <= timeout <= 255, default=60. timeout: Watchdog timeout in seconds. 1 <= timeout <= 255, default=60.
......
...@@ -46,6 +46,17 @@ config WATCHDOG_NOWAYOUT ...@@ -46,6 +46,17 @@ config WATCHDOG_NOWAYOUT
get killed. If you say Y here, the watchdog cannot be stopped once get killed. If you say Y here, the watchdog cannot be stopped once
it has been started. it has been started.
config WATCHDOG_HANDLE_BOOT_ENABLED
bool "Update boot-enabled watchdog until userspace takes over"
default y
help
The default watchdog behaviour (which you get if you say Y here) is
to ping watchdog devices that were enabled before the driver has
been loaded until control is taken over from userspace using the
/dev/watchdog file. If you say N here, the kernel will not update
the watchdog on its own. Thus if your userspace does not start fast
enough your device will reboot.
config WATCHDOG_SYSFS config WATCHDOG_SYSFS
bool "Read different watchdog information through sysfs" bool "Read different watchdog information through sysfs"
help help
...@@ -721,6 +732,14 @@ config RENESAS_WDT ...@@ -721,6 +732,14 @@ config RENESAS_WDT
This driver adds watchdog support for the integrated watchdogs in the This driver adds watchdog support for the integrated watchdogs in the
Renesas R-Car and other SH-Mobile SoCs (usually named RWDT or SWDT). Renesas R-Car and other SH-Mobile SoCs (usually named RWDT or SWDT).
config RENESAS_RZAWDT
tristate "Renesas RZ/A WDT Watchdog"
depends on ARCH_RENESAS || COMPILE_TEST
select WATCHDOG_CORE
help
This driver adds watchdog support for the integrated watchdogs in the
Renesas RZ/A SoCs. These watchdogs can be used to reset a system.
config ASPEED_WATCHDOG config ASPEED_WATCHDOG
tristate "Aspeed 2400 watchdog support" tristate "Aspeed 2400 watchdog support"
depends on ARCH_ASPEED || COMPILE_TEST depends on ARCH_ASPEED || COMPILE_TEST
...@@ -744,6 +763,30 @@ config ZX2967_WATCHDOG ...@@ -744,6 +763,30 @@ config ZX2967_WATCHDOG
To compile this driver as a module, choose M here: the To compile this driver as a module, choose M here: the
module will be called zx2967_wdt. module will be called zx2967_wdt.
config STM32_WATCHDOG
tristate "STM32 Independent WatchDoG (IWDG) support"
depends on ARCH_STM32
select WATCHDOG_CORE
default y
help
Say Y here to include support for the watchdog timer
in stm32 SoCs.
To compile this driver as a module, choose M here: the
module will be called stm32_iwdg.
config UNIPHIER_WATCHDOG
tristate "UniPhier watchdog support"
depends on ARCH_UNIPHIER || COMPILE_TEST
depends on OF && MFD_SYSCON
select WATCHDOG_CORE
help
Say Y here to include support watchdog timer embedded
into the UniPhier system.
To compile this driver as a module, choose M here: the
module will be called uniphier_wdt.
# AVR32 Architecture # AVR32 Architecture
config AT32AP700X_WDT config AT32AP700X_WDT
...@@ -829,11 +872,12 @@ config EBC_C384_WDT ...@@ -829,11 +872,12 @@ config EBC_C384_WDT
the timeout module parameter. the timeout module parameter.
config F71808E_WDT config F71808E_WDT
tristate "Fintek F71808E, F71862FG, F71869, F71882FG and F71889FG Watchdog" tristate "Fintek F718xx, F818xx Super I/O Watchdog"
depends on X86 depends on X86
help help
This is the driver for the hardware watchdog on the Fintek This is the driver for the hardware watchdog on the Fintek F71808E,
F71808E, F71862FG, F71869, F71882FG and F71889FG Super I/O controllers. F71862FG, F71868, F71869, F71882FG, F71889FG, F81865 and F81866
Super I/O controllers.
You can compile this driver directly into the kernel, or use You can compile this driver directly into the kernel, or use
it as a module. The module will be called f71808e_wdt. it as a module. The module will be called f71808e_wdt.
...@@ -1037,13 +1081,12 @@ config IT8712F_WDT ...@@ -1037,13 +1081,12 @@ config IT8712F_WDT
config IT87_WDT config IT87_WDT
tristate "IT87 Watchdog Timer" tristate "IT87 Watchdog Timer"
depends on X86 depends on X86
select WATCHDOG_CORE
---help--- ---help---
This is the driver for the hardware watchdog on the ITE IT8620, This is the driver for the hardware watchdog on the ITE IT8607,
IT8702, IT8712, IT8716, IT8718, IT8720, IT8721, IT8726 and IT8728 IT8620, IT8622, IT8625, IT8628, IT8655, IT8665, IT8686, IT8702,
Super I/O chips. IT8712, IT8716, IT8718, IT8720, IT8721, IT8726, IT8728, and
IT8783 Super I/O chips.
If the driver does not work, then make sure that the game port in
the BIOS is enabled.
This watchdog simply watches your kernel to make sure it doesn't This watchdog simply watches your kernel to make sure it doesn't
freeze, and if it does, it reboots your computer after a certain freeze, and if it does, it reboots your computer after a certain
......
...@@ -82,8 +82,11 @@ obj-$(CONFIG_LPC18XX_WATCHDOG) += lpc18xx_wdt.o ...@@ -82,8 +82,11 @@ obj-$(CONFIG_LPC18XX_WATCHDOG) += lpc18xx_wdt.o
obj-$(CONFIG_BCM7038_WDT) += bcm7038_wdt.o obj-$(CONFIG_BCM7038_WDT) += bcm7038_wdt.o
obj-$(CONFIG_ATLAS7_WATCHDOG) += atlas7_wdt.o obj-$(CONFIG_ATLAS7_WATCHDOG) += atlas7_wdt.o
obj-$(CONFIG_RENESAS_WDT) += renesas_wdt.o obj-$(CONFIG_RENESAS_WDT) += renesas_wdt.o
obj-$(CONFIG_RENESAS_RZAWDT) += rza_wdt.o
obj-$(CONFIG_ASPEED_WATCHDOG) += aspeed_wdt.o obj-$(CONFIG_ASPEED_WATCHDOG) += aspeed_wdt.o
obj-$(CONFIG_ZX2967_WATCHDOG) += zx2967_wdt.o obj-$(CONFIG_ZX2967_WATCHDOG) += zx2967_wdt.o
obj-$(CONFIG_STM32_WATCHDOG) += stm32_iwdg.o
obj-$(CONFIG_UNIPHIER_WATCHDOG) += uniphier_wdt.o
# AVR32 Architecture # AVR32 Architecture
obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o
......
...@@ -97,7 +97,7 @@ static int bcm47xx_wdt_restart(struct watchdog_device *wdd, ...@@ -97,7 +97,7 @@ static int bcm47xx_wdt_restart(struct watchdog_device *wdd,
return 0; return 0;
} }
static struct watchdog_ops bcm47xx_wdt_hard_ops = { static const struct watchdog_ops bcm47xx_wdt_hard_ops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.start = bcm47xx_wdt_hard_start, .start = bcm47xx_wdt_hard_start,
.stop = bcm47xx_wdt_hard_stop, .stop = bcm47xx_wdt_hard_stop,
...@@ -168,7 +168,7 @@ static const struct watchdog_info bcm47xx_wdt_info = { ...@@ -168,7 +168,7 @@ static const struct watchdog_info bcm47xx_wdt_info = {
WDIOF_MAGICCLOSE, WDIOF_MAGICCLOSE,
}; };
static struct watchdog_ops bcm47xx_wdt_soft_ops = { static const struct watchdog_ops bcm47xx_wdt_soft_ops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.start = bcm47xx_wdt_soft_start, .start = bcm47xx_wdt_soft_start,
.stop = bcm47xx_wdt_soft_stop, .stop = bcm47xx_wdt_soft_stop,
......
...@@ -458,7 +458,7 @@ static int __maybe_unused cdns_wdt_resume(struct device *dev) ...@@ -458,7 +458,7 @@ static int __maybe_unused cdns_wdt_resume(struct device *dev)
static SIMPLE_DEV_PM_OPS(cdns_wdt_pm_ops, cdns_wdt_suspend, cdns_wdt_resume); static SIMPLE_DEV_PM_OPS(cdns_wdt_pm_ops, cdns_wdt_suspend, cdns_wdt_resume);
static struct of_device_id cdns_wdt_of_match[] = { static const struct of_device_id cdns_wdt_of_match[] = {
{ .compatible = "cdns,wdt-r1p2", }, { .compatible = "cdns,wdt-r1p2", },
{ /* end of table */ } { /* end of table */ }
}; };
......
...@@ -173,7 +173,11 @@ static int davinci_wdt_probe(struct platform_device *pdev) ...@@ -173,7 +173,11 @@ static int davinci_wdt_probe(struct platform_device *pdev)
return PTR_ERR(davinci_wdt->clk); return PTR_ERR(davinci_wdt->clk);
} }
clk_prepare_enable(davinci_wdt->clk); ret = clk_prepare_enable(davinci_wdt->clk);
if (ret) {
dev_err(&pdev->dev, "failed to prepare clock\n");
return ret;
}
platform_set_drvdata(pdev, davinci_wdt); platform_set_drvdata(pdev, davinci_wdt);
...@@ -198,8 +202,10 @@ static int davinci_wdt_probe(struct platform_device *pdev) ...@@ -198,8 +202,10 @@ static int davinci_wdt_probe(struct platform_device *pdev)
return PTR_ERR(davinci_wdt->base); return PTR_ERR(davinci_wdt->base);
ret = watchdog_register_device(wdd); ret = watchdog_register_device(wdd);
if (ret < 0) if (ret < 0) {
clk_disable_unprepare(davinci_wdt->clk);
dev_err(dev, "cannot register watchdog device\n"); dev_err(dev, "cannot register watchdog device\n");
}
return ret; return ret;
} }
......
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#include <linux/of.h> #include <linux/of.h>
#include <linux/pm.h> #include <linux/pm.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/reset.h>
#include <linux/watchdog.h> #include <linux/watchdog.h>
#define WDOG_CONTROL_REG_OFFSET 0x00 #define WDOG_CONTROL_REG_OFFSET 0x00
...@@ -54,6 +55,7 @@ struct dw_wdt { ...@@ -54,6 +55,7 @@ struct dw_wdt {
struct clk *clk; struct clk *clk;
unsigned long rate; unsigned long rate;
struct watchdog_device wdd; struct watchdog_device wdd;
struct reset_control *rst;
}; };
#define to_dw_wdt(wdd) container_of(wdd, struct dw_wdt, wdd) #define to_dw_wdt(wdd) container_of(wdd, struct dw_wdt, wdd)
...@@ -234,6 +236,14 @@ static int dw_wdt_drv_probe(struct platform_device *pdev) ...@@ -234,6 +236,14 @@ static int dw_wdt_drv_probe(struct platform_device *pdev)
goto out_disable_clk; goto out_disable_clk;
} }
dw_wdt->rst = devm_reset_control_get_optional_shared(&pdev->dev, NULL);
if (IS_ERR(dw_wdt->rst)) {
ret = PTR_ERR(dw_wdt->rst);
goto out_disable_clk;
}
reset_control_deassert(dw_wdt->rst);
wdd = &dw_wdt->wdd; wdd = &dw_wdt->wdd;
wdd->info = &dw_wdt_ident; wdd->info = &dw_wdt_ident;
wdd->ops = &dw_wdt_ops; wdd->ops = &dw_wdt_ops;
...@@ -279,6 +289,7 @@ static int dw_wdt_drv_remove(struct platform_device *pdev) ...@@ -279,6 +289,7 @@ static int dw_wdt_drv_remove(struct platform_device *pdev)
struct dw_wdt *dw_wdt = platform_get_drvdata(pdev); struct dw_wdt *dw_wdt = platform_get_drvdata(pdev);
watchdog_unregister_device(&dw_wdt->wdd); watchdog_unregister_device(&dw_wdt->wdd);
reset_control_assert(dw_wdt->rst);
clk_disable_unprepare(dw_wdt->clk); clk_disable_unprepare(dw_wdt->clk);
return 0; return 0;
......
...@@ -57,6 +57,7 @@ ...@@ -57,6 +57,7 @@
#define SIO_F71808_ID 0x0901 /* Chipset ID */ #define SIO_F71808_ID 0x0901 /* Chipset ID */
#define SIO_F71858_ID 0x0507 /* Chipset ID */ #define SIO_F71858_ID 0x0507 /* Chipset ID */
#define SIO_F71862_ID 0x0601 /* Chipset ID */ #define SIO_F71862_ID 0x0601 /* Chipset ID */
#define SIO_F71868_ID 0x1106 /* Chipset ID */
#define SIO_F71869_ID 0x0814 /* Chipset ID */ #define SIO_F71869_ID 0x0814 /* Chipset ID */
#define SIO_F71869A_ID 0x1007 /* Chipset ID */ #define SIO_F71869A_ID 0x1007 /* Chipset ID */
#define SIO_F71882_ID 0x0541 /* Chipset ID */ #define SIO_F71882_ID 0x0541 /* Chipset ID */
...@@ -101,7 +102,7 @@ MODULE_PARM_DESC(timeout, ...@@ -101,7 +102,7 @@ MODULE_PARM_DESC(timeout,
static unsigned int pulse_width = WATCHDOG_PULSE_WIDTH; static unsigned int pulse_width = WATCHDOG_PULSE_WIDTH;
module_param(pulse_width, uint, 0); module_param(pulse_width, uint, 0);
MODULE_PARM_DESC(pulse_width, MODULE_PARM_DESC(pulse_width,
"Watchdog signal pulse width. 0(=level), 1 ms, 25 ms, 125 ms or 5000 ms" "Watchdog signal pulse width. 0(=level), 1, 25, 30, 125, 150, 5000 or 6000 ms"
" (default=" __MODULE_STRING(WATCHDOG_PULSE_WIDTH) ")"); " (default=" __MODULE_STRING(WATCHDOG_PULSE_WIDTH) ")");
static unsigned int f71862fg_pin = WATCHDOG_F71862FG_PIN; static unsigned int f71862fg_pin = WATCHDOG_F71862FG_PIN;
...@@ -119,13 +120,14 @@ module_param(start_withtimeout, uint, 0); ...@@ -119,13 +120,14 @@ module_param(start_withtimeout, uint, 0);
MODULE_PARM_DESC(start_withtimeout, "Start watchdog timer on module load with" MODULE_PARM_DESC(start_withtimeout, "Start watchdog timer on module load with"
" given initial timeout. Zero (default) disables this feature."); " given initial timeout. Zero (default) disables this feature.");
enum chips { f71808fg, f71858fg, f71862fg, f71869, f71882fg, f71889fg, f81865, enum chips { f71808fg, f71858fg, f71862fg, f71868, f71869, f71882fg, f71889fg,
f81866}; f81865, f81866};
static const char *f71808e_names[] = { static const char *f71808e_names[] = {
"f71808fg", "f71808fg",
"f71858fg", "f71858fg",
"f71862fg", "f71862fg",
"f71868",
"f71869", "f71869",
"f71882fg", "f71882fg",
"f71889fg", "f71889fg",
...@@ -252,16 +254,23 @@ static int watchdog_set_timeout(int timeout) ...@@ -252,16 +254,23 @@ static int watchdog_set_timeout(int timeout)
static int watchdog_set_pulse_width(unsigned int pw) static int watchdog_set_pulse_width(unsigned int pw)
{ {
int err = 0; int err = 0;
unsigned int t1 = 25, t2 = 125, t3 = 5000;
if (watchdog.type == f71868) {
t1 = 30;
t2 = 150;
t3 = 6000;
}
mutex_lock(&watchdog.lock); mutex_lock(&watchdog.lock);
if (pw <= 1) { if (pw <= 1) {
watchdog.pulse_val = 0; watchdog.pulse_val = 0;
} else if (pw <= 25) { } else if (pw <= t1) {
watchdog.pulse_val = 1; watchdog.pulse_val = 1;
} else if (pw <= 125) { } else if (pw <= t2) {
watchdog.pulse_val = 2; watchdog.pulse_val = 2;
} else if (pw <= 5000) { } else if (pw <= t3) {
watchdog.pulse_val = 3; watchdog.pulse_val = 3;
} else { } else {
pr_err("pulse width out of range\n"); pr_err("pulse width out of range\n");
...@@ -354,6 +363,7 @@ static int watchdog_start(void) ...@@ -354,6 +363,7 @@ static int watchdog_start(void)
goto exit_superio; goto exit_superio;
break; break;
case f71868:
case f71869: case f71869:
/* GPIO14 --> WDTRST# */ /* GPIO14 --> WDTRST# */
superio_clear_bit(watchdog.sioaddr, SIO_REG_MFUNCT1, 4); superio_clear_bit(watchdog.sioaddr, SIO_REG_MFUNCT1, 4);
...@@ -792,6 +802,9 @@ static int __init f71808e_find(int sioaddr) ...@@ -792,6 +802,9 @@ static int __init f71808e_find(int sioaddr)
watchdog.type = f71862fg; watchdog.type = f71862fg;
err = f71862fg_pin_configure(0); /* validate module parameter */ err = f71862fg_pin_configure(0); /* validate module parameter */
break; break;
case SIO_F71868_ID:
watchdog.type = f71868;
break;
case SIO_F71869_ID: case SIO_F71869_ID:
case SIO_F71869A_ID: case SIO_F71869A_ID:
watchdog.type = f71869; watchdog.type = f71869;
......
...@@ -18,7 +18,6 @@ ...@@ -18,7 +18,6 @@
#define SOFT_TIMEOUT_MIN 1 #define SOFT_TIMEOUT_MIN 1
#define SOFT_TIMEOUT_DEF 60 #define SOFT_TIMEOUT_DEF 60
#define SOFT_TIMEOUT_MAX 0xffff
enum { enum {
HW_ALGO_TOGGLE, HW_ALGO_TOGGLE,
...@@ -30,11 +29,7 @@ struct gpio_wdt_priv { ...@@ -30,11 +29,7 @@ struct gpio_wdt_priv {
bool active_low; bool active_low;
bool state; bool state;
bool always_running; bool always_running;
bool armed;
unsigned int hw_algo; unsigned int hw_algo;
unsigned int hw_margin;
unsigned long last_jiffies;
struct timer_list timer;
struct watchdog_device wdd; struct watchdog_device wdd;
}; };
...@@ -47,21 +42,10 @@ static void gpio_wdt_disable(struct gpio_wdt_priv *priv) ...@@ -47,21 +42,10 @@ static void gpio_wdt_disable(struct gpio_wdt_priv *priv)
gpio_direction_input(priv->gpio); gpio_direction_input(priv->gpio);
} }
static void gpio_wdt_hwping(unsigned long data) static int gpio_wdt_ping(struct watchdog_device *wdd)
{ {
struct watchdog_device *wdd = (struct watchdog_device *)data;
struct gpio_wdt_priv *priv = watchdog_get_drvdata(wdd); struct gpio_wdt_priv *priv = watchdog_get_drvdata(wdd);
if (priv->armed && time_after(jiffies, priv->last_jiffies +
msecs_to_jiffies(wdd->timeout * 1000))) {
dev_crit(wdd->parent,
"Timer expired. System will reboot soon!\n");
return;
}
/* Restart timer */
mod_timer(&priv->timer, jiffies + priv->hw_margin);
switch (priv->hw_algo) { switch (priv->hw_algo) {
case HW_ALGO_TOGGLE: case HW_ALGO_TOGGLE:
/* Toggle output pin */ /* Toggle output pin */
...@@ -75,55 +59,33 @@ static void gpio_wdt_hwping(unsigned long data) ...@@ -75,55 +59,33 @@ static void gpio_wdt_hwping(unsigned long data)
gpio_set_value_cansleep(priv->gpio, priv->active_low); gpio_set_value_cansleep(priv->gpio, priv->active_low);
break; break;
} }
} return 0;
static void gpio_wdt_start_impl(struct gpio_wdt_priv *priv)
{
priv->state = priv->active_low;
gpio_direction_output(priv->gpio, priv->state);
priv->last_jiffies = jiffies;
gpio_wdt_hwping((unsigned long)&priv->wdd);
} }
static int gpio_wdt_start(struct watchdog_device *wdd) static int gpio_wdt_start(struct watchdog_device *wdd)
{ {
struct gpio_wdt_priv *priv = watchdog_get_drvdata(wdd); struct gpio_wdt_priv *priv = watchdog_get_drvdata(wdd);
gpio_wdt_start_impl(priv); priv->state = priv->active_low;
priv->armed = true; gpio_direction_output(priv->gpio, priv->state);
return 0; set_bit(WDOG_HW_RUNNING, &wdd->status);
return gpio_wdt_ping(wdd);
} }
static int gpio_wdt_stop(struct watchdog_device *wdd) static int gpio_wdt_stop(struct watchdog_device *wdd)
{ {
struct gpio_wdt_priv *priv = watchdog_get_drvdata(wdd); struct gpio_wdt_priv *priv = watchdog_get_drvdata(wdd);
priv->armed = false;
if (!priv->always_running) { if (!priv->always_running) {
mod_timer(&priv->timer, 0);
gpio_wdt_disable(priv); gpio_wdt_disable(priv);
clear_bit(WDOG_HW_RUNNING, &wdd->status);
} }
return 0; return 0;
} }
static int gpio_wdt_ping(struct watchdog_device *wdd)
{
struct gpio_wdt_priv *priv = watchdog_get_drvdata(wdd);
priv->last_jiffies = jiffies;
return 0;
}
static int gpio_wdt_set_timeout(struct watchdog_device *wdd, unsigned int t)
{
wdd->timeout = t;
return gpio_wdt_ping(wdd);
}
static const struct watchdog_info gpio_wdt_ident = { static const struct watchdog_info gpio_wdt_ident = {
.options = WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING | .options = WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING |
WDIOF_SETTIMEOUT, WDIOF_SETTIMEOUT,
...@@ -135,7 +97,6 @@ static const struct watchdog_ops gpio_wdt_ops = { ...@@ -135,7 +97,6 @@ static const struct watchdog_ops gpio_wdt_ops = {
.start = gpio_wdt_start, .start = gpio_wdt_start,
.stop = gpio_wdt_stop, .stop = gpio_wdt_stop,
.ping = gpio_wdt_ping, .ping = gpio_wdt_ping,
.set_timeout = gpio_wdt_set_timeout,
}; };
static int gpio_wdt_probe(struct platform_device *pdev) static int gpio_wdt_probe(struct platform_device *pdev)
...@@ -185,9 +146,6 @@ static int gpio_wdt_probe(struct platform_device *pdev) ...@@ -185,9 +146,6 @@ static int gpio_wdt_probe(struct platform_device *pdev)
if (hw_margin < 2 || hw_margin > 65535) if (hw_margin < 2 || hw_margin > 65535)
return -EINVAL; return -EINVAL;
/* Use safe value (1/2 of real timeout) */
priv->hw_margin = msecs_to_jiffies(hw_margin / 2);
priv->always_running = of_property_read_bool(pdev->dev.of_node, priv->always_running = of_property_read_bool(pdev->dev.of_node,
"always-running"); "always-running");
...@@ -196,31 +154,26 @@ static int gpio_wdt_probe(struct platform_device *pdev) ...@@ -196,31 +154,26 @@ static int gpio_wdt_probe(struct platform_device *pdev)
priv->wdd.info = &gpio_wdt_ident; priv->wdd.info = &gpio_wdt_ident;
priv->wdd.ops = &gpio_wdt_ops; priv->wdd.ops = &gpio_wdt_ops;
priv->wdd.min_timeout = SOFT_TIMEOUT_MIN; priv->wdd.min_timeout = SOFT_TIMEOUT_MIN;
priv->wdd.max_timeout = SOFT_TIMEOUT_MAX; priv->wdd.max_hw_heartbeat_ms = hw_margin;
priv->wdd.parent = &pdev->dev; priv->wdd.parent = &pdev->dev;
if (watchdog_init_timeout(&priv->wdd, 0, &pdev->dev) < 0) if (watchdog_init_timeout(&priv->wdd, 0, &pdev->dev) < 0)
priv->wdd.timeout = SOFT_TIMEOUT_DEF; priv->wdd.timeout = SOFT_TIMEOUT_DEF;
setup_timer(&priv->timer, gpio_wdt_hwping, (unsigned long)&priv->wdd);
watchdog_stop_on_reboot(&priv->wdd); watchdog_stop_on_reboot(&priv->wdd);
ret = watchdog_register_device(&priv->wdd);
if (ret)
return ret;
if (priv->always_running) if (priv->always_running)
gpio_wdt_start_impl(priv); gpio_wdt_start(&priv->wdd);
return 0; ret = watchdog_register_device(&priv->wdd);
return ret;
} }
static int gpio_wdt_remove(struct platform_device *pdev) static int gpio_wdt_remove(struct platform_device *pdev)
{ {
struct gpio_wdt_priv *priv = platform_get_drvdata(pdev); struct gpio_wdt_priv *priv = platform_get_drvdata(pdev);
del_timer_sync(&priv->timer);
watchdog_unregister_device(&priv->wdd); watchdog_unregister_device(&priv->wdd);
return 0; return 0;
......
...@@ -147,8 +147,21 @@ static int mid_wdt_probe(struct platform_device *pdev) ...@@ -147,8 +147,21 @@ static int mid_wdt_probe(struct platform_device *pdev)
return ret; return ret;
} }
/* Make sure the watchdog is not running */ /*
wdt_stop(wdt_dev); * The firmware followed by U-Boot leaves the watchdog running
* with the default threshold which may vary. When we get here
* we should make a decision to prevent any side effects before
* user space daemon will take care of it. The best option,
* taking into consideration that there is no way to read values
* back from hardware, is to enforce watchdog being run with
* deterministic values.
*/
ret = wdt_start(wdt_dev);
if (ret)
return ret;
/* Make sure the watchdog is serviced */
set_bit(WDOG_HW_RUNNING, &wdt_dev->status);
ret = devm_watchdog_register_device(&pdev->dev, wdt_dev); ret = devm_watchdog_register_device(&pdev->dev, wdt_dev);
if (ret) { if (ret) {
......
...@@ -12,8 +12,9 @@ ...@@ -12,8 +12,9 @@
* http://www.ite.com.tw/ * http://www.ite.com.tw/
* *
* Support of the watchdog timers, which are available on * Support of the watchdog timers, which are available on
* IT8620, IT8702, IT8712, IT8716, IT8718, IT8720, IT8721, IT8726, * IT8607, IT8620, IT8622, IT8625, IT8628, IT8655, IT8665, IT8686,
* IT8728 and IT8783. * IT8702, IT8712, IT8716, IT8718, IT8720, IT8721, IT8726, IT8728,
* and IT8783.
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
...@@ -24,38 +25,21 @@ ...@@ -24,38 +25,21 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/ */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/init.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/moduleparam.h> #include <linux/moduleparam.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/init.h>
#include <linux/ioport.h>
#include <linux/watchdog.h> #include <linux/watchdog.h>
#include <linux/notifier.h>
#include <linux/reboot.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#define WATCHDOG_VERSION "1.14"
#define WATCHDOG_NAME "IT87 WDT" #define WATCHDOG_NAME "IT87 WDT"
#define DRIVER_VERSION WATCHDOG_NAME " driver, v" WATCHDOG_VERSION "\n"
#define WD_MAGIC 'V'
/* Defaults for Module Parameter */ /* Defaults for Module Parameter */
#define DEFAULT_NOGAMEPORT 0
#define DEFAULT_NOCIR 0
#define DEFAULT_EXCLUSIVE 1
#define DEFAULT_TIMEOUT 60 #define DEFAULT_TIMEOUT 60
#define DEFAULT_TESTMODE 0 #define DEFAULT_TESTMODE 0
#define DEFAULT_NOWAYOUT WATCHDOG_NOWAYOUT #define DEFAULT_NOWAYOUT WATCHDOG_NOWAYOUT
...@@ -66,19 +50,22 @@ ...@@ -66,19 +50,22 @@
/* Logical device Numbers LDN */ /* Logical device Numbers LDN */
#define GPIO 0x07 #define GPIO 0x07
#define GAMEPORT 0x09
#define CIR 0x0a
/* Configuration Registers and Functions */ /* Configuration Registers and Functions */
#define LDNREG 0x07 #define LDNREG 0x07
#define CHIPID 0x20 #define CHIPID 0x20
#define CHIPREV 0x22 #define CHIPREV 0x22
#define ACTREG 0x30
#define BASEREG 0x60
/* Chip Id numbers */ /* Chip Id numbers */
#define NO_DEV_ID 0xffff #define NO_DEV_ID 0xffff
#define IT8607_ID 0x8607
#define IT8620_ID 0x8620 #define IT8620_ID 0x8620
#define IT8622_ID 0x8622
#define IT8625_ID 0x8625
#define IT8628_ID 0x8628
#define IT8655_ID 0x8655
#define IT8665_ID 0x8665
#define IT8686_ID 0x8686
#define IT8702_ID 0x8702 #define IT8702_ID 0x8702
#define IT8705_ID 0x8705 #define IT8705_ID 0x8705
#define IT8712_ID 0x8712 #define IT8712_ID 0x8712
...@@ -96,14 +83,6 @@ ...@@ -96,14 +83,6 @@
#define WDTVALLSB 0x73 #define WDTVALLSB 0x73
#define WDTVALMSB 0x74 #define WDTVALMSB 0x74
/* GPIO Bits WDTCTRL */
#define WDT_CIRINT 0x80
#define WDT_MOUSEINT 0x40
#define WDT_KYBINT 0x20
#define WDT_GAMEPORT 0x10 /* not in it8718, it8720, it8721, it8728 */
#define WDT_FORCE 0x02
#define WDT_ZERO 0x01
/* GPIO Bits WDTCFG */ /* GPIO Bits WDTCFG */
#define WDT_TOV1 0x80 #define WDT_TOV1 0x80
#define WDT_KRST 0x40 #define WDT_KRST 0x40
...@@ -111,55 +90,12 @@ ...@@ -111,55 +90,12 @@
#define WDT_PWROK 0x10 /* not in it8721 */ #define WDT_PWROK 0x10 /* not in it8721 */
#define WDT_INT_MASK 0x0f #define WDT_INT_MASK 0x0f
/* CIR Configuration Register LDN=0x0a */ static unsigned int max_units, chip_type;
#define CIR_ILS 0x70
static unsigned int timeout = DEFAULT_TIMEOUT;
/* The default Base address is not always available, we use this */ static int testmode = DEFAULT_TESTMODE;
#define CIR_BASE 0x0208 static bool nowayout = DEFAULT_NOWAYOUT;
/* CIR Controller */
#define CIR_DR(b) (b)
#define CIR_IER(b) (b + 1)
#define CIR_RCR(b) (b + 2)
#define CIR_TCR1(b) (b + 3)
#define CIR_TCR2(b) (b + 4)
#define CIR_TSR(b) (b + 5)
#define CIR_RSR(b) (b + 6)
#define CIR_BDLR(b) (b + 5)
#define CIR_BDHR(b) (b + 6)
#define CIR_IIR(b) (b + 7)
/* Default Base address of Game port */
#define GP_BASE_DEFAULT 0x0201
/* wdt_status */
#define WDTS_TIMER_RUN 0
#define WDTS_DEV_OPEN 1
#define WDTS_KEEPALIVE 2
#define WDTS_LOCKED 3
#define WDTS_USE_GP 4
#define WDTS_EXPECTED 5
#define WDTS_USE_CIR 6
static unsigned int base, gpact, ciract, max_units, chip_type;
static unsigned long wdt_status;
static int nogameport = DEFAULT_NOGAMEPORT;
static int nocir = DEFAULT_NOCIR;
static int exclusive = DEFAULT_EXCLUSIVE;
static int timeout = DEFAULT_TIMEOUT;
static int testmode = DEFAULT_TESTMODE;
static bool nowayout = DEFAULT_NOWAYOUT;
module_param(nogameport, int, 0);
MODULE_PARM_DESC(nogameport, "Forbid the activation of game port, default="
__MODULE_STRING(DEFAULT_NOGAMEPORT));
module_param(nocir, int, 0);
MODULE_PARM_DESC(nocir, "Forbid the use of Consumer IR interrupts to reset timer, default="
__MODULE_STRING(DEFAULT_NOCIR));
module_param(exclusive, int, 0);
MODULE_PARM_DESC(exclusive, "Watchdog exclusive device open, default="
__MODULE_STRING(DEFAULT_EXCLUSIVE));
module_param(timeout, int, 0); module_param(timeout, int, 0);
MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds, default=" MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds, default="
__MODULE_STRING(DEFAULT_TIMEOUT)); __MODULE_STRING(DEFAULT_TIMEOUT));
...@@ -231,88 +167,59 @@ static inline void superio_outw(int val, int reg) ...@@ -231,88 +167,59 @@ static inline void superio_outw(int val, int reg)
} }
/* Internal function, should be called after superio_select(GPIO) */ /* Internal function, should be called after superio_select(GPIO) */
static void wdt_update_timeout(void) static void _wdt_update_timeout(unsigned int t)
{ {
unsigned char cfg = WDT_KRST; unsigned char cfg = WDT_KRST;
int tm = timeout;
if (testmode) if (testmode)
cfg = 0; cfg = 0;
if (tm <= max_units) if (t <= max_units)
cfg |= WDT_TOV1; cfg |= WDT_TOV1;
else else
tm /= 60; t /= 60;
if (chip_type != IT8721_ID) if (chip_type != IT8721_ID)
cfg |= WDT_PWROK; cfg |= WDT_PWROK;
superio_outb(cfg, WDTCFG); superio_outb(cfg, WDTCFG);
superio_outb(tm, WDTVALLSB); superio_outb(t, WDTVALLSB);
if (max_units > 255) if (max_units > 255)
superio_outb(tm>>8, WDTVALMSB); superio_outb(t >> 8, WDTVALMSB);
} }
static int wdt_round_time(int t) static int wdt_update_timeout(unsigned int t)
{ {
t += 59; int ret;
t -= t % 60;
return t;
}
/* watchdog timer handling */ ret = superio_enter();
static void wdt_keepalive(void)
{
if (test_bit(WDTS_USE_GP, &wdt_status))
inb(base);
else if (test_bit(WDTS_USE_CIR, &wdt_status))
/* The timer reloads with around 5 msec delay */
outb(0x55, CIR_DR(base));
else {
if (superio_enter())
return;
superio_select(GPIO);
wdt_update_timeout();
superio_exit();
}
set_bit(WDTS_KEEPALIVE, &wdt_status);
}
static int wdt_start(void)
{
int ret = superio_enter();
if (ret) if (ret)
return ret; return ret;
superio_select(GPIO); superio_select(GPIO);
if (test_bit(WDTS_USE_GP, &wdt_status)) _wdt_update_timeout(t);
superio_outb(WDT_GAMEPORT, WDTCTRL);
else if (test_bit(WDTS_USE_CIR, &wdt_status))
superio_outb(WDT_CIRINT, WDTCTRL);
wdt_update_timeout();
superio_exit(); superio_exit();
return 0; return 0;
} }
static int wdt_stop(void) static int wdt_round_time(int t)
{ {
int ret = superio_enter(); t += 59;
if (ret) t -= t % 60;
return ret; return t;
}
superio_select(GPIO); /* watchdog timer handling */
superio_outb(0x00, WDTCTRL);
superio_outb(WDT_TOV1, WDTCFG);
superio_outb(0x00, WDTVALLSB);
if (max_units > 255)
superio_outb(0x00, WDTVALMSB);
superio_exit(); static int wdt_start(struct watchdog_device *wdd)
return 0; {
return wdt_update_timeout(wdd->timeout);
}
static int wdt_stop(struct watchdog_device *wdd)
{
return wdt_update_timeout(0);
} }
/** /**
...@@ -325,292 +232,44 @@ static int wdt_stop(void) ...@@ -325,292 +232,44 @@ static int wdt_stop(void)
* Used within WDIOC_SETTIMEOUT watchdog device ioctl. * Used within WDIOC_SETTIMEOUT watchdog device ioctl.
*/ */
static int wdt_set_timeout(int t) static int wdt_set_timeout(struct watchdog_device *wdd, unsigned int t)
{ {
if (t < 1 || t > max_units * 60) int ret = 0;
return -EINVAL;
if (t > max_units) if (t > max_units)
timeout = wdt_round_time(t); t = wdt_round_time(t);
else
timeout = t;
if (test_bit(WDTS_TIMER_RUN, &wdt_status)) {
int ret = superio_enter();
if (ret)
return ret;
superio_select(GPIO);
wdt_update_timeout();
superio_exit();
}
return 0;
}
/**
* wdt_get_status - determines the status supported by watchdog ioctl
* @status: status returned to user space
*
* The status bit of the device does not allow to distinguish
* between a regular system reset and a watchdog forced reset.
* But, in test mode it is useful, so it is supported through
* WDIOC_GETSTATUS watchdog ioctl. Additionally the driver
* reports the keepalive signal and the acception of the magic.
*
* Used within WDIOC_GETSTATUS watchdog device ioctl.
*/
static int wdt_get_status(int *status)
{
*status = 0;
if (testmode) {
int ret = superio_enter();
if (ret)
return ret;
superio_select(GPIO);
if (superio_inb(WDTCTRL) & WDT_ZERO) {
superio_outb(0x00, WDTCTRL);
clear_bit(WDTS_TIMER_RUN, &wdt_status);
*status |= WDIOF_CARDRESET;
}
superio_exit();
}
if (test_and_clear_bit(WDTS_KEEPALIVE, &wdt_status))
*status |= WDIOF_KEEPALIVEPING;
if (test_bit(WDTS_EXPECTED, &wdt_status))
*status |= WDIOF_MAGICCLOSE;
return 0;
}
/* /dev/watchdog handling */
/**
* wdt_open - watchdog file_operations .open
* @inode: inode of the device
* @file: file handle to the device
*
* The watchdog timer starts by opening the device.
*
* Used within the file operation of the watchdog device.
*/
static int wdt_open(struct inode *inode, struct file *file) wdd->timeout = t;
{
if (exclusive && test_and_set_bit(WDTS_DEV_OPEN, &wdt_status))
return -EBUSY;
if (!test_and_set_bit(WDTS_TIMER_RUN, &wdt_status)) {
int ret;
if (nowayout && !test_and_set_bit(WDTS_LOCKED, &wdt_status))
__module_get(THIS_MODULE);
ret = wdt_start();
if (ret) {
clear_bit(WDTS_LOCKED, &wdt_status);
clear_bit(WDTS_TIMER_RUN, &wdt_status);
clear_bit(WDTS_DEV_OPEN, &wdt_status);
return ret;
}
}
return nonseekable_open(inode, file);
}
/** if (watchdog_hw_running(wdd))
* wdt_release - watchdog file_operations .release ret = wdt_update_timeout(t);
* @inode: inode of the device
* @file: file handle to the device
*
* Closing the watchdog device either stops the watchdog timer
* or in the case, that nowayout is set or the magic character
* wasn't written, a critical warning about an running watchdog
* timer is given.
*
* Used within the file operation of the watchdog device.
*/
static int wdt_release(struct inode *inode, struct file *file) return ret;
{
if (test_bit(WDTS_TIMER_RUN, &wdt_status)) {
if (test_and_clear_bit(WDTS_EXPECTED, &wdt_status)) {
int ret = wdt_stop();
if (ret) {
/*
* Stop failed. Just keep the watchdog alive
* and hope nothing bad happens.
*/
set_bit(WDTS_EXPECTED, &wdt_status);
wdt_keepalive();
return ret;
}
clear_bit(WDTS_TIMER_RUN, &wdt_status);
} else {
wdt_keepalive();
pr_crit("unexpected close, not stopping watchdog!\n");
}
}
clear_bit(WDTS_DEV_OPEN, &wdt_status);
return 0;
}
/**
* wdt_write - watchdog file_operations .write
* @file: file handle to the watchdog
* @buf: buffer to write
* @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 don't define content meaning.
*
* Used within the file operation of the watchdog device.
*/
static ssize_t wdt_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
if (count) {
clear_bit(WDTS_EXPECTED, &wdt_status);
wdt_keepalive();
}
if (!nowayout) {
size_t ofs;
/* note: just in case someone wrote the magic character long ago */
for (ofs = 0; ofs != count; ofs++) {
char c;
if (get_user(c, buf + ofs))
return -EFAULT;
if (c == WD_MAGIC)
set_bit(WDTS_EXPECTED, &wdt_status);
}
}
return count;
} }
static const struct watchdog_info ident = { static const struct watchdog_info ident = {
.options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING, .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING,
.firmware_version = 1, .firmware_version = 1,
.identity = WATCHDOG_NAME, .identity = WATCHDOG_NAME,
}; };
/** static struct watchdog_ops wdt_ops = {
* wdt_ioctl - watchdog file_operations .unlocked_ioctl .owner = THIS_MODULE,
* @file: file handle to the device .start = wdt_start,
* @cmd: watchdog command .stop = wdt_stop,
* @arg: argument pointer .set_timeout = wdt_set_timeout,
*
* The watchdog API defines a common set of functions for all watchdogs
* according to their available features.
*
* Used within the file operation of the watchdog device.
*/
static long wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
int rc = 0, status, new_options, new_timeout;
union {
struct watchdog_info __user *ident;
int __user *i;
} uarg;
uarg.i = (int __user *)arg;
switch (cmd) {
case WDIOC_GETSUPPORT:
return copy_to_user(uarg.ident,
&ident, sizeof(ident)) ? -EFAULT : 0;
case WDIOC_GETSTATUS:
rc = wdt_get_status(&status);
if (rc)
return rc;
return put_user(status, uarg.i);
case WDIOC_GETBOOTSTATUS:
return put_user(0, uarg.i);
case WDIOC_KEEPALIVE:
wdt_keepalive();
return 0;
case WDIOC_SETOPTIONS:
if (get_user(new_options, uarg.i))
return -EFAULT;
switch (new_options) {
case WDIOS_DISABLECARD:
if (test_bit(WDTS_TIMER_RUN, &wdt_status)) {
rc = wdt_stop();
if (rc)
return rc;
}
clear_bit(WDTS_TIMER_RUN, &wdt_status);
return 0;
case WDIOS_ENABLECARD:
if (!test_and_set_bit(WDTS_TIMER_RUN, &wdt_status)) {
rc = wdt_start();
if (rc) {
clear_bit(WDTS_TIMER_RUN, &wdt_status);
return rc;
}
}
return 0;
default:
return -EFAULT;
}
case WDIOC_SETTIMEOUT:
if (get_user(new_timeout, uarg.i))
return -EFAULT;
rc = wdt_set_timeout(new_timeout);
case WDIOC_GETTIMEOUT:
if (put_user(timeout, uarg.i))
return -EFAULT;
return rc;
default:
return -ENOTTY;
}
}
static int wdt_notify_sys(struct notifier_block *this, unsigned long code,
void *unused)
{
if (code == SYS_DOWN || code == SYS_HALT)
wdt_stop();
return NOTIFY_DONE;
}
static const struct file_operations wdt_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.write = wdt_write,
.unlocked_ioctl = wdt_ioctl,
.open = wdt_open,
.release = wdt_release,
}; };
static struct miscdevice wdt_miscdev = { static struct watchdog_device wdt_dev = {
.minor = WATCHDOG_MINOR, .info = &ident,
.name = "watchdog", .ops = &wdt_ops,
.fops = &wdt_fops, .min_timeout = 1,
};
static struct notifier_block wdt_notifier = {
.notifier_call = wdt_notify_sys,
}; };
static int __init it87_wdt_init(void) static int __init it87_wdt_init(void)
{ {
int rc = 0;
int try_gameport = !nogameport;
u8 chip_rev; u8 chip_rev;
int gp_rreq_fail = 0; int rc;
wdt_status = 0;
rc = superio_enter(); rc = superio_enter();
if (rc) if (rc)
...@@ -631,14 +290,20 @@ static int __init it87_wdt_init(void) ...@@ -631,14 +290,20 @@ static int __init it87_wdt_init(void)
case IT8726_ID: case IT8726_ID:
max_units = 65535; max_units = 65535;
break; break;
case IT8607_ID:
case IT8620_ID: case IT8620_ID:
case IT8622_ID:
case IT8625_ID:
case IT8628_ID:
case IT8655_ID:
case IT8665_ID:
case IT8686_ID:
case IT8718_ID: case IT8718_ID:
case IT8720_ID: case IT8720_ID:
case IT8721_ID: case IT8721_ID:
case IT8728_ID: case IT8728_ID:
case IT8783_ID: case IT8783_ID:
max_units = 65535; max_units = 65535;
try_gameport = 0;
break; break;
case IT8705_ID: case IT8705_ID:
pr_err("Unsupported Chip found, Chip %04x Revision %02x\n", pr_err("Unsupported Chip found, Chip %04x Revision %02x\n",
...@@ -660,48 +325,7 @@ static int __init it87_wdt_init(void) ...@@ -660,48 +325,7 @@ static int __init it87_wdt_init(void)
superio_select(GPIO); superio_select(GPIO);
superio_outb(WDT_TOV1, WDTCFG); superio_outb(WDT_TOV1, WDTCFG);
superio_outb(0x00, WDTCTRL); superio_outb(0x00, WDTCTRL);
superio_exit();
/* First try to get Gameport support */
if (try_gameport) {
superio_select(GAMEPORT);
base = superio_inw(BASEREG);
if (!base) {
base = GP_BASE_DEFAULT;
superio_outw(base, BASEREG);
}
gpact = superio_inb(ACTREG);
superio_outb(0x01, ACTREG);
if (request_region(base, 1, WATCHDOG_NAME))
set_bit(WDTS_USE_GP, &wdt_status);
else
gp_rreq_fail = 1;
}
/* If we haven't Gameport support, try to get CIR support */
if (!nocir && !test_bit(WDTS_USE_GP, &wdt_status)) {
if (!request_region(CIR_BASE, 8, WATCHDOG_NAME)) {
if (gp_rreq_fail)
pr_err("I/O Address 0x%04x and 0x%04x already in use\n",
base, CIR_BASE);
else
pr_err("I/O Address 0x%04x already in use\n",
CIR_BASE);
rc = -EIO;
goto err_out;
}
base = CIR_BASE;
superio_select(CIR);
superio_outw(base, BASEREG);
superio_outb(0x00, CIR_ILS);
ciract = superio_inb(ACTREG);
superio_outb(0x01, ACTREG);
if (gp_rreq_fail) {
superio_select(GAMEPORT);
superio_outb(gpact, ACTREG);
}
set_bit(WDTS_USE_CIR, &wdt_status);
}
if (timeout < 1 || timeout > max_units * 60) { if (timeout < 1 || timeout > max_units * 60) {
timeout = DEFAULT_TIMEOUT; timeout = DEFAULT_TIMEOUT;
...@@ -712,83 +336,25 @@ static int __init it87_wdt_init(void) ...@@ -712,83 +336,25 @@ static int __init it87_wdt_init(void)
if (timeout > max_units) if (timeout > max_units)
timeout = wdt_round_time(timeout); timeout = wdt_round_time(timeout);
rc = register_reboot_notifier(&wdt_notifier); wdt_dev.timeout = timeout;
if (rc) { wdt_dev.max_timeout = max_units * 60;
pr_err("Cannot register reboot notifier (err=%d)\n", rc);
goto err_out_region;
}
rc = misc_register(&wdt_miscdev); watchdog_stop_on_reboot(&wdt_dev);
rc = watchdog_register_device(&wdt_dev);
if (rc) { if (rc) {
pr_err("Cannot register miscdev on minor=%d (err=%d)\n", pr_err("Cannot register watchdog device (err=%d)\n", rc);
wdt_miscdev.minor, rc); return rc;
goto err_out_reboot;
}
/* Initialize CIR to use it as keepalive source */
if (test_bit(WDTS_USE_CIR, &wdt_status)) {
outb(0x00, CIR_RCR(base));
outb(0xc0, CIR_TCR1(base));
outb(0x5c, CIR_TCR2(base));
outb(0x10, CIR_IER(base));
outb(0x00, CIR_BDHR(base));
outb(0x01, CIR_BDLR(base));
outb(0x09, CIR_IER(base));
} }
pr_info("Chip IT%04x revision %d initialized. timeout=%d sec (nowayout=%d testmode=%d exclusive=%d nogameport=%d nocir=%d)\n", pr_info("Chip IT%04x revision %d initialized. timeout=%d sec (nowayout=%d testmode=%d)\n",
chip_type, chip_rev, timeout, chip_type, chip_rev, timeout, nowayout, testmode);
nowayout, testmode, exclusive, nogameport, nocir);
superio_exit();
return 0; return 0;
err_out_reboot:
unregister_reboot_notifier(&wdt_notifier);
err_out_region:
if (test_bit(WDTS_USE_GP, &wdt_status))
release_region(base, 1);
else if (test_bit(WDTS_USE_CIR, &wdt_status)) {
release_region(base, 8);
superio_select(CIR);
superio_outb(ciract, ACTREG);
}
err_out:
if (try_gameport) {
superio_select(GAMEPORT);
superio_outb(gpact, ACTREG);
}
superio_exit();
return rc;
} }
static void __exit it87_wdt_exit(void) static void __exit it87_wdt_exit(void)
{ {
if (superio_enter() == 0) { watchdog_unregister_device(&wdt_dev);
superio_select(GPIO);
superio_outb(0x00, WDTCTRL);
superio_outb(0x00, WDTCFG);
superio_outb(0x00, WDTVALLSB);
if (max_units > 255)
superio_outb(0x00, WDTVALMSB);
if (test_bit(WDTS_USE_GP, &wdt_status)) {
superio_select(GAMEPORT);
superio_outb(gpact, ACTREG);
} else if (test_bit(WDTS_USE_CIR, &wdt_status)) {
superio_select(CIR);
superio_outb(ciract, ACTREG);
}
superio_exit();
}
misc_deregister(&wdt_miscdev);
unregister_reboot_notifier(&wdt_notifier);
if (test_bit(WDTS_USE_GP, &wdt_status))
release_region(base, 1);
else if (test_bit(WDTS_USE_CIR, &wdt_status))
release_region(base, 8);
} }
module_init(it87_wdt_init); module_init(it87_wdt_init);
......
...@@ -203,7 +203,9 @@ static int meson_gxbb_wdt_probe(struct platform_device *pdev) ...@@ -203,7 +203,9 @@ static int meson_gxbb_wdt_probe(struct platform_device *pdev)
if (IS_ERR(data->clk)) if (IS_ERR(data->clk))
return PTR_ERR(data->clk); return PTR_ERR(data->clk);
clk_prepare_enable(data->clk); ret = clk_prepare_enable(data->clk);
if (ret)
return ret;
platform_set_drvdata(pdev, data); platform_set_drvdata(pdev, data);
......
...@@ -651,5 +651,5 @@ module_param(nowayout, bool, 0); ...@@ -651,5 +651,5 @@ module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:orion_wdt"); MODULE_ALIAS("platform:orion_wdt");
/*
* 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>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/watchdog.h>
#define DEFAULT_TIMEOUT 30
/* Watchdog Timer Registers */
#define WTCSR 0
#define WTCSR_MAGIC 0xA500
#define WTSCR_WT BIT(6)
#define WTSCR_TME BIT(5)
#define WTSCR_CKS(i) (i)
#define WTCNT 2
#define WTCNT_MAGIC 0x5A00
#define WRCSR 4
#define WRCSR_MAGIC 0x5A00
#define WRCSR_RSTE BIT(6)
#define WRCSR_CLEAR_WOVF 0xA500 /* special value */
struct rza_wdt {
struct watchdog_device wdev;
void __iomem *base;
struct clk *clk;
};
static int rza_wdt_start(struct watchdog_device *wdev)
{
struct rza_wdt *priv = watchdog_get_drvdata(wdev);
/* Stop timer */
writew(WTCSR_MAGIC | 0, priv->base + WTCSR);
/* Must dummy read WRCSR:WOVF at least once before clearing */
readb(priv->base + WRCSR);
writew(WRCSR_CLEAR_WOVF, priv->base + WRCSR);
/*
* Start timer with slowest clock source and reset option enabled.
*/
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);
return 0;
}
static int rza_wdt_stop(struct watchdog_device *wdev)
{
struct rza_wdt *priv = watchdog_get_drvdata(wdev);
writew(WTCSR_MAGIC | 0, priv->base + WTCSR);
return 0;
}
static int rza_wdt_ping(struct watchdog_device *wdev)
{
struct rza_wdt *priv = watchdog_get_drvdata(wdev);
writew(WTCNT_MAGIC | 0, priv->base + WTCNT);
return 0;
}
static int rza_wdt_restart(struct watchdog_device *wdev, unsigned long action,
void *data)
{
struct rza_wdt *priv = watchdog_get_drvdata(wdev);
/* Stop timer */
writew(WTCSR_MAGIC | 0, priv->base + WTCSR);
/* Must dummy read WRCSR:WOVF at least once before clearing */
readb(priv->base + WRCSR);
writew(WRCSR_CLEAR_WOVF, priv->base + WRCSR);
/*
* Start timer with fastest clock source and only 1 clock left before
* overflow with reset option enabled.
*/
writew(WRCSR_MAGIC | WRCSR_RSTE, priv->base + WRCSR);
writew(WTCNT_MAGIC | 255, priv->base + WTCNT);
writew(WTCSR_MAGIC | WTSCR_WT | WTSCR_TME, priv->base + WTCSR);
/*
* Actually make sure the above sequence hits hardware before sleeping.
*/
wmb();
/* Wait for WDT overflow (reset) */
udelay(20);
return 0;
}
static const struct watchdog_info rza_wdt_ident = {
.options = WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT,
.identity = "Renesas RZ/A WDT Watchdog",
};
static const struct watchdog_ops rza_wdt_ops = {
.owner = THIS_MODULE,
.start = rza_wdt_start,
.stop = rza_wdt_stop,
.ping = rza_wdt_ping,
.restart = rza_wdt_restart,
};
static int rza_wdt_probe(struct platform_device *pdev)
{
struct rza_wdt *priv;
struct resource *res;
unsigned long rate;
int ret;
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
priv->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(priv->base))
return PTR_ERR(priv->base);
priv->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(priv->clk))
return PTR_ERR(priv->clk);
rate = clk_get_rate(priv->clk);
if (rate < 16384) {
dev_err(&pdev->dev, "invalid clock rate (%ld)\n", rate);
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->wdev.min_timeout = 1;
priv->wdev.timeout = DEFAULT_TIMEOUT;
watchdog_init_timeout(&priv->wdev, 0, &pdev->dev);
watchdog_set_drvdata(&priv->wdev, priv);
ret = devm_watchdog_register_device(&pdev->dev, &priv->wdev);
if (ret)
dev_err(&pdev->dev, "Cannot register watchdog device\n");
return ret;
}
static const struct of_device_id rza_wdt_of_match[] = {
{ .compatible = "renesas,rza-wdt", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, rza_wdt_of_match);
static struct platform_driver rza_wdt_driver = {
.probe = rza_wdt_probe,
.driver = {
.name = "rza_wdt",
.of_match_table = rza_wdt_of_match,
},
};
module_platform_driver(rza_wdt_driver);
MODULE_DESCRIPTION("Renesas RZ/A WDT Driver");
MODULE_AUTHOR("Chris Brandt <chris.brandt@renesas.com>");
MODULE_LICENSE("GPL v2");
/* linux/drivers/char/watchdog/s3c2410_wdt.c /*
*
* Copyright (c) 2004 Simtec Electronics * Copyright (c) 2004 Simtec Electronics
* Ben Dooks <ben@simtec.co.uk> * Ben Dooks <ben@simtec.co.uk>
* *
...@@ -17,11 +16,7 @@ ...@@ -17,11 +16,7 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* */
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/module.h> #include <linux/module.h>
#include <linux/moduleparam.h> #include <linux/moduleparam.h>
...@@ -37,6 +32,7 @@ ...@@ -37,6 +32,7 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_device.h>
#include <linux/mfd/syscon.h> #include <linux/mfd/syscon.h>
#include <linux/regmap.h> #include <linux/regmap.h>
#include <linux/delay.h> #include <linux/delay.h>
...@@ -94,8 +90,7 @@ MODULE_PARM_DESC(tmr_atboot, ...@@ -94,8 +90,7 @@ MODULE_PARM_DESC(tmr_atboot,
__MODULE_STRING(S3C2410_WATCHDOG_ATBOOT)); __MODULE_STRING(S3C2410_WATCHDOG_ATBOOT));
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
MODULE_PARM_DESC(soft_noboot, "Watchdog action, set to 1 to ignore reboots, " MODULE_PARM_DESC(soft_noboot, "Watchdog action, set to 1 to ignore reboots, 0 to reboot (default 0)");
"0 to reboot (default 0)");
/** /**
* struct s3c2410_wdt_variant - Per-variant config data * struct s3c2410_wdt_variant - Per-variant config data
...@@ -131,7 +126,7 @@ struct s3c2410_wdt { ...@@ -131,7 +126,7 @@ struct s3c2410_wdt {
unsigned long wtdat_save; unsigned long wtdat_save;
struct watchdog_device wdt_device; struct watchdog_device wdt_device;
struct notifier_block freq_transition; struct notifier_block freq_transition;
struct s3c2410_wdt_variant *drv_data; const struct s3c2410_wdt_variant *drv_data;
struct regmap *pmureg; struct regmap *pmureg;
}; };
...@@ -310,7 +305,8 @@ static inline int s3c2410wdt_is_running(struct s3c2410_wdt *wdt) ...@@ -310,7 +305,8 @@ static inline int s3c2410wdt_is_running(struct s3c2410_wdt *wdt)
return readl(wdt->reg_base + S3C2410_WTCON) & S3C2410_WTCON_ENABLE; return readl(wdt->reg_base + S3C2410_WTCON) & S3C2410_WTCON_ENABLE;
} }
static int s3c2410wdt_set_heartbeat(struct watchdog_device *wdd, unsigned timeout) static int s3c2410wdt_set_heartbeat(struct watchdog_device *wdd,
unsigned int timeout)
{ {
struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd); struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd);
unsigned long freq = clk_get_rate(wdt->clock); unsigned long freq = clk_get_rate(wdt->clock);
...@@ -401,7 +397,7 @@ static const struct watchdog_ops s3c2410wdt_ops = { ...@@ -401,7 +397,7 @@ static const struct watchdog_ops s3c2410wdt_ops = {
.restart = s3c2410wdt_restart, .restart = s3c2410wdt_restart,
}; };
static struct watchdog_device s3c2410_wdd = { static const struct watchdog_device s3c2410_wdd = {
.info = &s3c2410_wdt_ident, .info = &s3c2410_wdt_ident,
.ops = &s3c2410wdt_ops, .ops = &s3c2410wdt_ops,
.timeout = S3C2410_WATCHDOG_DEFAULT_TIME, .timeout = S3C2410_WATCHDOG_DEFAULT_TIME,
...@@ -507,22 +503,24 @@ static inline unsigned int s3c2410wdt_get_bootstatus(struct s3c2410_wdt *wdt) ...@@ -507,22 +503,24 @@ static inline unsigned int s3c2410wdt_get_bootstatus(struct s3c2410_wdt *wdt)
return 0; return 0;
} }
static inline struct s3c2410_wdt_variant * static inline const struct s3c2410_wdt_variant *
s3c2410_get_wdt_drv_data(struct platform_device *pdev) s3c2410_get_wdt_drv_data(struct platform_device *pdev)
{ {
if (pdev->dev.of_node) { const struct s3c2410_wdt_variant *variant;
const struct of_device_id *match;
match = of_match_node(s3c2410_wdt_match, pdev->dev.of_node); variant = of_device_get_match_data(&pdev->dev);
return (struct s3c2410_wdt_variant *)match->data; if (!variant) {
} else { /* Device matched by platform_device_id */
return (struct s3c2410_wdt_variant *) variant = (struct s3c2410_wdt_variant *)
platform_get_device_id(pdev)->driver_data; platform_get_device_id(pdev)->driver_data;
} }
return variant;
} }
static int s3c2410wdt_probe(struct platform_device *pdev) static int s3c2410wdt_probe(struct platform_device *pdev)
{ {
struct device *dev; struct device *dev = &pdev->dev;
struct s3c2410_wdt *wdt; struct s3c2410_wdt *wdt;
struct resource *wdt_mem; struct resource *wdt_mem;
struct resource *wdt_irq; struct resource *wdt_irq;
...@@ -530,13 +528,11 @@ static int s3c2410wdt_probe(struct platform_device *pdev) ...@@ -530,13 +528,11 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
int started = 0; int started = 0;
int ret; int ret;
dev = &pdev->dev;
wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL); wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
if (!wdt) if (!wdt)
return -ENOMEM; return -ENOMEM;
wdt->dev = &pdev->dev; wdt->dev = dev;
spin_lock_init(&wdt->lock); spin_lock_init(&wdt->lock);
wdt->wdt_device = s3c2410_wdd; wdt->wdt_device = s3c2410_wdd;
...@@ -592,7 +588,7 @@ static int s3c2410wdt_probe(struct platform_device *pdev) ...@@ -592,7 +588,7 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
/* see if we can actually set the requested timer margin, and if /* see if we can actually set the requested timer margin, and if
* not, try the default value */ * not, try the default value */
watchdog_init_timeout(&wdt->wdt_device, tmr_margin, &pdev->dev); watchdog_init_timeout(&wdt->wdt_device, tmr_margin, dev);
ret = s3c2410wdt_set_heartbeat(&wdt->wdt_device, ret = s3c2410wdt_set_heartbeat(&wdt->wdt_device,
wdt->wdt_device.timeout); wdt->wdt_device.timeout);
if (ret) { if (ret) {
...@@ -601,11 +597,10 @@ static int s3c2410wdt_probe(struct platform_device *pdev) ...@@ -601,11 +597,10 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
if (started == 0) if (started == 0)
dev_info(dev, dev_info(dev,
"tmr_margin value out of range, default %d used\n", "tmr_margin value out of range, default %d used\n",
S3C2410_WATCHDOG_DEFAULT_TIME); S3C2410_WATCHDOG_DEFAULT_TIME);
else else
dev_info(dev, "default timer value is out of range, " dev_info(dev, "default timer value is out of range, cannot start\n");
"cannot start\n");
} }
ret = devm_request_irq(dev, wdt_irq->start, s3c2410wdt_irq, 0, ret = devm_request_irq(dev, wdt_irq->start, s3c2410wdt_irq, 0,
...@@ -619,7 +614,7 @@ static int s3c2410wdt_probe(struct platform_device *pdev) ...@@ -619,7 +614,7 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
watchdog_set_restart_priority(&wdt->wdt_device, 128); watchdog_set_restart_priority(&wdt->wdt_device, 128);
wdt->wdt_device.bootstatus = s3c2410wdt_get_bootstatus(wdt); wdt->wdt_device.bootstatus = s3c2410wdt_get_bootstatus(wdt);
wdt->wdt_device.parent = &pdev->dev; wdt->wdt_device.parent = dev;
ret = watchdog_register_device(&wdt->wdt_device); ret = watchdog_register_device(&wdt->wdt_device);
if (ret) { if (ret) {
...@@ -754,7 +749,6 @@ static struct platform_driver s3c2410wdt_driver = { ...@@ -754,7 +749,6 @@ static struct platform_driver s3c2410wdt_driver = {
module_platform_driver(s3c2410wdt_driver); module_platform_driver(s3c2410wdt_driver);
MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>, " MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>, Dimitry Andric <dimitry.andric@tomtom.com>");
"Dimitry Andric <dimitry.andric@tomtom.com>");
MODULE_DESCRIPTION("S3C2410 Watchdog Device Driver"); MODULE_DESCRIPTION("S3C2410 Watchdog Device Driver");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
...@@ -228,15 +228,13 @@ static int sama5d4_wdt_probe(struct platform_device *pdev) ...@@ -228,15 +228,13 @@ static int sama5d4_wdt_probe(struct platform_device *pdev)
wdt->reg_base = regs; wdt->reg_base = regs;
if (pdev->dev.of_node) { irq = irq_of_parse_and_map(pdev->dev.of_node, 0);
irq = irq_of_parse_and_map(pdev->dev.of_node, 0); if (!irq)
if (!irq) dev_warn(&pdev->dev, "failed to get IRQ from DT\n");
dev_warn(&pdev->dev, "failed to get IRQ from DT\n");
ret = of_sama5d4_wdt_init(pdev->dev.of_node, wdt); ret = of_sama5d4_wdt_init(pdev->dev.of_node, wdt);
if (ret) if (ret)
return ret; return ret;
}
if ((wdt->mr & AT91_WDT_WDFIEN) && irq) { if ((wdt->mr & AT91_WDT_WDFIEN) && irq) {
ret = devm_request_irq(&pdev->dev, irq, sama5d4_wdt_irq_handler, ret = devm_request_irq(&pdev->dev, irq, sama5d4_wdt_irq_handler,
...@@ -302,6 +300,11 @@ static int sama5d4_wdt_resume(struct device *dev) ...@@ -302,6 +300,11 @@ static int sama5d4_wdt_resume(struct device *dev)
{ {
struct sama5d4_wdt *wdt = dev_get_drvdata(dev); struct sama5d4_wdt *wdt = dev_get_drvdata(dev);
/*
* FIXME: writing MR also pings the watchdog which may not be desired.
* This should only be done when the registers are lost on suspend but
* there is no way to get this information right now.
*/
sama5d4_wdt_init(wdt); sama5d4_wdt_init(wdt);
return 0; return 0;
......
/*
* Driver for STM32 Independent Watchdog
*
* Copyright (C) Yannick Fertre 2017
* Author: Yannick Fertre <yannick.fertre@st.com>
*
* This driver is based on tegra_wdt.c
*
* License terms: GNU General Public License (GPL), version 2
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/watchdog.h>
/* IWDG registers */
#define IWDG_KR 0x00 /* Key register */
#define IWDG_PR 0x04 /* Prescaler Register */
#define IWDG_RLR 0x08 /* ReLoad Register */
#define IWDG_SR 0x0C /* Status Register */
#define IWDG_WINR 0x10 /* Windows Register */
/* IWDG_KR register bit mask */
#define KR_KEY_RELOAD 0xAAAA /* reload counter enable */
#define KR_KEY_ENABLE 0xCCCC /* peripheral enable */
#define KR_KEY_EWA 0x5555 /* write access enable */
#define KR_KEY_DWA 0x0000 /* write access disable */
/* IWDG_PR register bit values */
#define PR_4 0x00 /* prescaler set to 4 */
#define PR_8 0x01 /* prescaler set to 8 */
#define PR_16 0x02 /* prescaler set to 16 */
#define PR_32 0x03 /* prescaler set to 32 */
#define PR_64 0x04 /* prescaler set to 64 */
#define PR_128 0x05 /* prescaler set to 128 */
#define PR_256 0x06 /* prescaler set to 256 */
/* IWDG_RLR register values */
#define RLR_MIN 0x07C /* min value supported by reload register */
#define RLR_MAX 0xFFF /* max value supported by reload register */
/* IWDG_SR register bit mask */
#define FLAG_PVU BIT(0) /* Watchdog prescaler value update */
#define FLAG_RVU BIT(1) /* Watchdog counter reload value update */
/* set timeout to 100000 us */
#define TIMEOUT_US 100000
#define SLEEP_US 1000
struct stm32_iwdg {
struct watchdog_device wdd;
void __iomem *regs;
struct clk *clk;
unsigned int rate;
};
static inline u32 reg_read(void __iomem *base, u32 reg)
{
return readl_relaxed(base + reg);
}
static inline void reg_write(void __iomem *base, u32 reg, u32 val)
{
writel_relaxed(val, base + reg);
}
static int stm32_iwdg_start(struct watchdog_device *wdd)
{
struct stm32_iwdg *wdt = watchdog_get_drvdata(wdd);
u32 val = FLAG_PVU | FLAG_RVU;
u32 reload;
int ret;
dev_dbg(wdd->parent, "%s\n", __func__);
/* prescaler fixed to 256 */
reload = clamp_t(unsigned int, ((wdd->timeout * wdt->rate) / 256) - 1,
RLR_MIN, RLR_MAX);
/* enable write access */
reg_write(wdt->regs, IWDG_KR, KR_KEY_EWA);
/* set prescaler & reload registers */
reg_write(wdt->regs, IWDG_PR, PR_256); /* prescaler fix to 256 */
reg_write(wdt->regs, IWDG_RLR, reload);
reg_write(wdt->regs, IWDG_KR, KR_KEY_ENABLE);
/* wait for the registers to be updated (max 100ms) */
ret = readl_relaxed_poll_timeout(wdt->regs + IWDG_SR, val,
!(val & (FLAG_PVU | FLAG_RVU)),
SLEEP_US, TIMEOUT_US);
if (ret) {
dev_err(wdd->parent,
"Fail to set prescaler or reload registers\n");
return ret;
}
/* reload watchdog */
reg_write(wdt->regs, IWDG_KR, KR_KEY_RELOAD);
return 0;
}
static int stm32_iwdg_ping(struct watchdog_device *wdd)
{
struct stm32_iwdg *wdt = watchdog_get_drvdata(wdd);
dev_dbg(wdd->parent, "%s\n", __func__);
/* reload watchdog */
reg_write(wdt->regs, IWDG_KR, KR_KEY_RELOAD);
return 0;
}
static int stm32_iwdg_set_timeout(struct watchdog_device *wdd,
unsigned int timeout)
{
dev_dbg(wdd->parent, "%s timeout: %d sec\n", __func__, timeout);
wdd->timeout = timeout;
if (watchdog_active(wdd))
return stm32_iwdg_start(wdd);
return 0;
}
static const struct watchdog_info stm32_iwdg_info = {
.options = WDIOF_SETTIMEOUT |
WDIOF_MAGICCLOSE |
WDIOF_KEEPALIVEPING,
.identity = "STM32 Independent Watchdog",
};
static struct watchdog_ops stm32_iwdg_ops = {
.owner = THIS_MODULE,
.start = stm32_iwdg_start,
.ping = stm32_iwdg_ping,
.set_timeout = stm32_iwdg_set_timeout,
};
static int stm32_iwdg_probe(struct platform_device *pdev)
{
struct watchdog_device *wdd;
struct stm32_iwdg *wdt;
struct resource *res;
void __iomem *regs;
struct clk *clk;
int ret;
/* This is the timer base. */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
regs = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(regs)) {
dev_err(&pdev->dev, "Could not get resource\n");
return PTR_ERR(regs);
}
clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(clk)) {
dev_err(&pdev->dev, "Unable to get clock\n");
return PTR_ERR(clk);
}
ret = clk_prepare_enable(clk);
if (ret) {
dev_err(&pdev->dev, "Unable to prepare clock %p\n", clk);
return ret;
}
/*
* Allocate our watchdog driver data, which has the
* struct watchdog_device nested within it.
*/
wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL);
if (!wdt) {
ret = -ENOMEM;
goto err;
}
/* Initialize struct stm32_iwdg. */
wdt->regs = regs;
wdt->clk = clk;
wdt->rate = clk_get_rate(clk);
/* Initialize struct watchdog_device. */
wdd = &wdt->wdd;
wdd->info = &stm32_iwdg_info;
wdd->ops = &stm32_iwdg_ops;
wdd->min_timeout = ((RLR_MIN + 1) * 256) / wdt->rate;
wdd->max_hw_heartbeat_ms = ((RLR_MAX + 1) * 256 * 1000) / wdt->rate;
wdd->parent = &pdev->dev;
watchdog_set_drvdata(wdd, wdt);
watchdog_set_nowayout(wdd, WATCHDOG_NOWAYOUT);
ret = watchdog_init_timeout(wdd, 0, &pdev->dev);
if (ret)
dev_warn(&pdev->dev,
"unable to set timeout value, using default\n");
ret = watchdog_register_device(wdd);
if (ret) {
dev_err(&pdev->dev, "failed to register watchdog device\n");
goto err;
}
platform_set_drvdata(pdev, wdt);
return 0;
err:
clk_disable_unprepare(clk);
return ret;
}
static int stm32_iwdg_remove(struct platform_device *pdev)
{
struct stm32_iwdg *wdt = platform_get_drvdata(pdev);
watchdog_unregister_device(&wdt->wdd);
clk_disable_unprepare(wdt->clk);
return 0;
}
static const struct of_device_id stm32_iwdg_of_match[] = {
{ .compatible = "st,stm32-iwdg" },
{ /* end node */ }
};
MODULE_DEVICE_TABLE(of, stm32_iwdg_of_match);
static struct platform_driver stm32_iwdg_driver = {
.probe = stm32_iwdg_probe,
.remove = stm32_iwdg_remove,
.driver = {
.name = "iwdg",
.of_match_table = stm32_iwdg_of_match,
},
};
module_platform_driver(stm32_iwdg_driver);
MODULE_AUTHOR("Yannick Fertre <yannick.fertre@st.com>");
MODULE_DESCRIPTION("STMicroelectronics STM32 Independent Watchdog Driver");
MODULE_LICENSE("GPL v2");
/*
* Watchdog driver for the UniPhier watchdog timer
*
* (c) Copyright 2014 Panasonic Corporation
* (c) Copyright 2016 Socionext Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/bitops.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/watchdog.h>
/* WDT timer setting register */
#define WDTTIMSET 0x3004
#define WDTTIMSET_PERIOD_MASK (0xf << 0)
#define WDTTIMSET_PERIOD_1_SEC (0x3 << 0)
/* WDT reset selection register */
#define WDTRSTSEL 0x3008
#define WDTRSTSEL_RSTSEL_MASK (0x3 << 0)
#define WDTRSTSEL_RSTSEL_BOTH (0x0 << 0)
#define WDTRSTSEL_RSTSEL_IRQ_ONLY (0x2 << 0)
/* WDT control register */
#define WDTCTRL 0x300c
#define WDTCTRL_STATUS BIT(8)
#define WDTCTRL_CLEAR BIT(1)
#define WDTCTRL_ENABLE BIT(0)
#define SEC_TO_WDTTIMSET_PRD(sec) \
(ilog2(sec) + WDTTIMSET_PERIOD_1_SEC)
#define WDTST_TIMEOUT 1000 /* usec */
#define WDT_DEFAULT_TIMEOUT 64 /* Default is 64 seconds */
#define WDT_PERIOD_MIN 1
#define WDT_PERIOD_MAX 128
static unsigned int timeout = 0;
static bool nowayout = WATCHDOG_NOWAYOUT;
struct uniphier_wdt_dev {
struct watchdog_device wdt_dev;
struct regmap *regmap;
};
/*
* UniPhier Watchdog operations
*/
static int uniphier_watchdog_ping(struct watchdog_device *w)
{
struct uniphier_wdt_dev *wdev = watchdog_get_drvdata(w);
unsigned int val;
int ret;
/* Clear counter */
ret = regmap_write_bits(wdev->regmap, WDTCTRL,
WDTCTRL_CLEAR, WDTCTRL_CLEAR);
if (!ret)
/*
* As SoC specification, after clear counter,
* it needs to wait until counter status is 1.
*/
ret = regmap_read_poll_timeout(wdev->regmap, WDTCTRL, val,
(val & WDTCTRL_STATUS),
0, WDTST_TIMEOUT);
return ret;
}
static int __uniphier_watchdog_start(struct regmap *regmap, unsigned int sec)
{
unsigned int val;
int ret;
ret = regmap_read_poll_timeout(regmap, WDTCTRL, val,
!(val & WDTCTRL_STATUS),
0, WDTST_TIMEOUT);
if (ret)
return ret;
/* Setup period */
ret = regmap_write(regmap, WDTTIMSET,
SEC_TO_WDTTIMSET_PRD(sec));
if (ret)
return ret;
/* Enable and clear watchdog */
ret = regmap_write(regmap, WDTCTRL, WDTCTRL_ENABLE | WDTCTRL_CLEAR);
if (!ret)
/*
* As SoC specification, after clear counter,
* it needs to wait until counter status is 1.
*/
ret = regmap_read_poll_timeout(regmap, WDTCTRL, val,
(val & WDTCTRL_STATUS),
0, WDTST_TIMEOUT);
return ret;
}
static int __uniphier_watchdog_stop(struct regmap *regmap)
{
/* Disable and stop watchdog */
return regmap_write_bits(regmap, WDTCTRL, WDTCTRL_ENABLE, 0);
}
static int __uniphier_watchdog_restart(struct regmap *regmap, unsigned int sec)
{
int ret;
ret = __uniphier_watchdog_stop(regmap);
if (ret)
return ret;
return __uniphier_watchdog_start(regmap, sec);
}
static int uniphier_watchdog_start(struct watchdog_device *w)
{
struct uniphier_wdt_dev *wdev = watchdog_get_drvdata(w);
unsigned int tmp_timeout;
tmp_timeout = roundup_pow_of_two(w->timeout);
return __uniphier_watchdog_start(wdev->regmap, tmp_timeout);
}
static int uniphier_watchdog_stop(struct watchdog_device *w)
{
struct uniphier_wdt_dev *wdev = watchdog_get_drvdata(w);
return __uniphier_watchdog_stop(wdev->regmap);
}
static int uniphier_watchdog_set_timeout(struct watchdog_device *w,
unsigned int t)
{
struct uniphier_wdt_dev *wdev = watchdog_get_drvdata(w);
unsigned int tmp_timeout;
int ret;
tmp_timeout = roundup_pow_of_two(t);
if (tmp_timeout == w->timeout)
return 0;
if (watchdog_active(w)) {
ret = __uniphier_watchdog_restart(wdev->regmap, tmp_timeout);
if (ret)
return ret;
}
w->timeout = tmp_timeout;
return 0;
}
/*
* Kernel Interfaces
*/
static const struct watchdog_info uniphier_wdt_info = {
.identity = "uniphier-wdt",
.options = WDIOF_SETTIMEOUT |
WDIOF_KEEPALIVEPING |
WDIOF_MAGICCLOSE |
WDIOF_OVERHEAT,
};
static const struct watchdog_ops uniphier_wdt_ops = {
.owner = THIS_MODULE,
.start = uniphier_watchdog_start,
.stop = uniphier_watchdog_stop,
.ping = uniphier_watchdog_ping,
.set_timeout = uniphier_watchdog_set_timeout,
};
static int uniphier_wdt_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct uniphier_wdt_dev *wdev;
struct regmap *regmap;
struct device_node *parent;
int ret;
wdev = devm_kzalloc(dev, sizeof(*wdev), GFP_KERNEL);
if (!wdev)
return -ENOMEM;
platform_set_drvdata(pdev, wdev);
parent = of_get_parent(dev->of_node); /* parent should be syscon node */
regmap = syscon_node_to_regmap(parent);
of_node_put(parent);
if (IS_ERR(regmap))
return PTR_ERR(regmap);
wdev->regmap = regmap;
wdev->wdt_dev.info = &uniphier_wdt_info;
wdev->wdt_dev.ops = &uniphier_wdt_ops;
wdev->wdt_dev.max_timeout = WDT_PERIOD_MAX;
wdev->wdt_dev.min_timeout = WDT_PERIOD_MIN;
wdev->wdt_dev.parent = dev;
if (watchdog_init_timeout(&wdev->wdt_dev, timeout, dev) < 0) {
wdev->wdt_dev.timeout = WDT_DEFAULT_TIMEOUT;
}
watchdog_set_nowayout(&wdev->wdt_dev, nowayout);
watchdog_stop_on_reboot(&wdev->wdt_dev);
watchdog_set_drvdata(&wdev->wdt_dev, wdev);
uniphier_watchdog_stop(&wdev->wdt_dev);
ret = regmap_write(wdev->regmap, WDTRSTSEL, WDTRSTSEL_RSTSEL_BOTH);
if (ret)
return ret;
ret = devm_watchdog_register_device(dev, &wdev->wdt_dev);
if (ret)
return ret;
dev_info(dev, "watchdog driver (timeout=%d sec, nowayout=%d)\n",
wdev->wdt_dev.timeout, nowayout);
return 0;
}
static const struct of_device_id uniphier_wdt_dt_ids[] = {
{ .compatible = "socionext,uniphier-wdt" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, uniphier_wdt_dt_ids);
static struct platform_driver uniphier_wdt_driver = {
.probe = uniphier_wdt_probe,
.driver = {
.name = "uniphier-wdt",
.of_match_table = uniphier_wdt_dt_ids,
},
};
module_platform_driver(uniphier_wdt_driver);
module_param(timeout, uint, 0000);
MODULE_PARM_DESC(timeout,
"Watchdog timeout seconds in power of 2. (0 < timeout < 128, default="
__MODULE_STRING(WDT_DEFAULT_TIMEOUT) ")");
module_param(nowayout, bool, 0000);
MODULE_PARM_DESC(nowayout,
"Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
MODULE_AUTHOR("Keiji Hayashibara <hayashibara.keiji@socionext.com>");
MODULE_DESCRIPTION("UniPhier Watchdog Device Driver");
MODULE_LICENSE("GPL v2");
...@@ -49,7 +49,8 @@ static int cr_wdt_csr; /* WDT control & status register */ ...@@ -49,7 +49,8 @@ static int cr_wdt_csr; /* WDT control & status register */
enum chips { w83627hf, w83627s, w83697hf, w83697ug, w83637hf, w83627thf, enum chips { w83627hf, w83627s, w83697hf, w83697ug, w83637hf, w83627thf,
w83687thf, w83627ehf, w83627dhg, w83627uhg, w83667hg, w83627dhg_p, w83687thf, w83627ehf, w83627dhg, w83627uhg, w83667hg, w83627dhg_p,
w83667hg_b, nct6775, nct6776, nct6779, nct6791, nct6792, nct6102 }; w83667hg_b, nct6775, nct6776, nct6779, nct6791, nct6792, nct6793,
nct6795, nct6102 };
static int timeout; /* in seconds */ static int timeout; /* in seconds */
module_param(timeout, int, 0); module_param(timeout, int, 0);
...@@ -97,6 +98,8 @@ MODULE_PARM_DESC(early_disable, "Disable watchdog at boot time (default=0)"); ...@@ -97,6 +98,8 @@ MODULE_PARM_DESC(early_disable, "Disable watchdog at boot time (default=0)");
#define NCT6779_ID 0xc5 #define NCT6779_ID 0xc5
#define NCT6791_ID 0xc8 #define NCT6791_ID 0xc8
#define NCT6792_ID 0xc9 #define NCT6792_ID 0xc9
#define NCT6793_ID 0xd1
#define NCT6795_ID 0xd3
#define W83627HF_WDT_TIMEOUT 0xf6 #define W83627HF_WDT_TIMEOUT 0xf6
#define W83697HF_WDT_TIMEOUT 0xf4 #define W83697HF_WDT_TIMEOUT 0xf4
...@@ -204,6 +207,8 @@ static int w83627hf_init(struct watchdog_device *wdog, enum chips chip) ...@@ -204,6 +207,8 @@ static int w83627hf_init(struct watchdog_device *wdog, enum chips chip)
case nct6779: case nct6779:
case nct6791: case nct6791:
case nct6792: case nct6792:
case nct6793:
case nct6795:
case nct6102: case nct6102:
/* /*
* These chips have a fixed WDTO# output pin (W83627UHG), * These chips have a fixed WDTO# output pin (W83627UHG),
...@@ -396,6 +401,12 @@ static int wdt_find(int addr) ...@@ -396,6 +401,12 @@ static int wdt_find(int addr)
case NCT6792_ID: case NCT6792_ID:
ret = nct6792; ret = nct6792;
break; break;
case NCT6793_ID:
ret = nct6793;
break;
case NCT6795_ID:
ret = nct6795;
break;
case NCT6102_ID: case NCT6102_ID:
ret = nct6102; ret = nct6102;
cr_wdt_timeout = NCT6102D_WDT_TIMEOUT; cr_wdt_timeout = NCT6102D_WDT_TIMEOUT;
...@@ -437,6 +448,8 @@ static int __init wdt_init(void) ...@@ -437,6 +448,8 @@ static int __init wdt_init(void)
"NCT6779", "NCT6779",
"NCT6791", "NCT6791",
"NCT6792", "NCT6792",
"NCT6793",
"NCT6795",
"NCT6102", "NCT6102",
}; };
......
...@@ -80,6 +80,9 @@ static struct watchdog_core_data *old_wd_data; ...@@ -80,6 +80,9 @@ static struct watchdog_core_data *old_wd_data;
static struct workqueue_struct *watchdog_wq; static struct workqueue_struct *watchdog_wq;
static bool handle_boot_enabled =
IS_ENABLED(CONFIG_WATCHDOG_HANDLE_BOOT_ENABLED);
static inline bool watchdog_need_worker(struct watchdog_device *wdd) static inline bool watchdog_need_worker(struct watchdog_device *wdd)
{ {
/* All variables in milli-seconds */ /* All variables in milli-seconds */
...@@ -192,18 +195,23 @@ static int watchdog_ping(struct watchdog_device *wdd) ...@@ -192,18 +195,23 @@ static int watchdog_ping(struct watchdog_device *wdd)
return __watchdog_ping(wdd); return __watchdog_ping(wdd);
} }
static bool watchdog_worker_should_ping(struct watchdog_core_data *wd_data)
{
struct watchdog_device *wdd = wd_data->wdd;
return wdd && (watchdog_active(wdd) || watchdog_hw_running(wdd));
}
static void watchdog_ping_work(struct work_struct *work) static void watchdog_ping_work(struct work_struct *work)
{ {
struct watchdog_core_data *wd_data; struct watchdog_core_data *wd_data;
struct watchdog_device *wdd;
wd_data = container_of(to_delayed_work(work), struct watchdog_core_data, wd_data = container_of(to_delayed_work(work), struct watchdog_core_data,
work); work);
mutex_lock(&wd_data->lock); mutex_lock(&wd_data->lock);
wdd = wd_data->wdd; if (watchdog_worker_should_ping(wd_data))
if (wdd && (watchdog_active(wdd) || watchdog_hw_running(wdd))) __watchdog_ping(wd_data->wdd);
__watchdog_ping(wdd);
mutex_unlock(&wd_data->lock); mutex_unlock(&wd_data->lock);
} }
...@@ -956,9 +964,14 @@ static int watchdog_cdev_register(struct watchdog_device *wdd, dev_t devno) ...@@ -956,9 +964,14 @@ static int watchdog_cdev_register(struct watchdog_device *wdd, dev_t devno)
* and schedule an immediate ping. * and schedule an immediate ping.
*/ */
if (watchdog_hw_running(wdd)) { if (watchdog_hw_running(wdd)) {
__module_get(wdd->ops->owner); if (handle_boot_enabled) {
kref_get(&wd_data->kref); __module_get(wdd->ops->owner);
queue_delayed_work(watchdog_wq, &wd_data->work, 0); kref_get(&wd_data->kref);
queue_delayed_work(watchdog_wq, &wd_data->work, 0);
} else {
pr_info("watchdog%d running and kernel based pre-userspace handler disabled\n",
wdd->id);
}
} }
return 0; return 0;
...@@ -1106,3 +1119,8 @@ void __exit watchdog_dev_exit(void) ...@@ -1106,3 +1119,8 @@ void __exit watchdog_dev_exit(void)
class_unregister(&watchdog_class); class_unregister(&watchdog_class);
destroy_workqueue(watchdog_wq); destroy_workqueue(watchdog_wq);
} }
module_param(handle_boot_enabled, bool, 0444);
MODULE_PARM_DESC(handle_boot_enabled,
"Watchdog core auto-updates boot enabled watchdogs before userspace takes over (default="
__MODULE_STRING(IS_ENABLED(CONFIG_WATCHDOG_HANDLE_BOOT_ENABLED)) ")");
...@@ -154,7 +154,7 @@ static const struct watchdog_info zx2967_wdt_ident = { ...@@ -154,7 +154,7 @@ static const struct watchdog_info zx2967_wdt_ident = {
.identity = "zx2967 watchdog", .identity = "zx2967 watchdog",
}; };
static struct watchdog_ops zx2967_wdt_ops = { static const struct watchdog_ops zx2967_wdt_ops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.start = zx2967_wdt_start, .start = zx2967_wdt_start,
.stop = zx2967_wdt_stop, .stop = zx2967_wdt_stop,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment