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 ...
This commit is contained in:
commit
07d306c838
|
@ -0,0 +1,23 @@
|
|||
* 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:
|
|||
Optional Properties:
|
||||
|
||||
- interrupts : The interrupt used for the watchdog timeout warning.
|
||||
- resets : phandle pointing to the system reset controller with
|
||||
line index for the watchdog.
|
||||
|
||||
Example:
|
||||
|
||||
|
@ -18,4 +20,5 @@ Example:
|
|||
reg = <0xffd02000 0x1000>;
|
||||
interrupts = <0 171 4>;
|
||||
clocks = <&per_base_clk>;
|
||||
resets = <&rst WDT0_RESET>;
|
||||
};
|
||||
|
|
|
@ -2,10 +2,11 @@ Renesas Watchdog Timer (WDT) Controller
|
|||
|
||||
Required properties:
|
||||
- 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:
|
||||
- "renesas,r8a7795-wdt" (R-Car H3)
|
||||
- "renesas,r8a7796-wdt" (R-Car M3-W)
|
||||
- "renesas,r7s72100-wdt" (RZ/A1)
|
||||
|
||||
When compatible with the generic version, nodes must list the SoC-specific
|
||||
version corresponding to the platform first, followed by the generic
|
||||
|
@ -17,6 +18,7 @@ Required properties:
|
|||
Optional properties:
|
||||
- timeout-sec : Contains the watchdog timeout in seconds
|
||||
- power-domains : the power domain the WDT belongs to
|
||||
- interrupts: Some WDTs have an interrupt when used in interval timer mode
|
||||
|
||||
Examples:
|
||||
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
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>;
|
||||
};
|
|
@ -0,0 +1,20 @@
|
|||
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)
|
|||
nowayout: Watchdog cannot be stopped once started
|
||||
(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:
|
||||
wdt_io: w83627hf/thf WDT io port (default 0x2E)
|
||||
timeout: Watchdog timeout in seconds. 1 <= timeout <= 255, default=60.
|
||||
|
|
|
@ -46,6 +46,17 @@ config WATCHDOG_NOWAYOUT
|
|||
get killed. If you say Y here, the watchdog cannot be stopped once
|
||||
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
|
||||
bool "Read different watchdog information through sysfs"
|
||||
help
|
||||
|
@ -721,6 +732,14 @@ config RENESAS_WDT
|
|||
This driver adds watchdog support for the integrated watchdogs in the
|
||||
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
|
||||
tristate "Aspeed 2400 watchdog support"
|
||||
depends on ARCH_ASPEED || COMPILE_TEST
|
||||
|
@ -744,6 +763,30 @@ config ZX2967_WATCHDOG
|
|||
To compile this driver as a module, choose M here: the
|
||||
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
|
||||
|
||||
config AT32AP700X_WDT
|
||||
|
@ -829,11 +872,12 @@ config EBC_C384_WDT
|
|||
the timeout module parameter.
|
||||
|
||||
config F71808E_WDT
|
||||
tristate "Fintek F71808E, F71862FG, F71869, F71882FG and F71889FG Watchdog"
|
||||
tristate "Fintek F718xx, F818xx Super I/O Watchdog"
|
||||
depends on X86
|
||||
help
|
||||
This is the driver for the hardware watchdog on the Fintek
|
||||
F71808E, F71862FG, F71869, F71882FG and F71889FG Super I/O controllers.
|
||||
This is the driver for the hardware watchdog on the Fintek F71808E,
|
||||
F71862FG, F71868, F71869, F71882FG, F71889FG, F81865 and F81866
|
||||
Super I/O controllers.
|
||||
|
||||
You can compile this driver directly into the kernel, or use
|
||||
it as a module. The module will be called f71808e_wdt.
|
||||
|
@ -1037,13 +1081,12 @@ config IT8712F_WDT
|
|||
config IT87_WDT
|
||||
tristate "IT87 Watchdog Timer"
|
||||
depends on X86
|
||||
select WATCHDOG_CORE
|
||||
---help---
|
||||
This is the driver for the hardware watchdog on the ITE IT8620,
|
||||
IT8702, IT8712, IT8716, IT8718, IT8720, IT8721, IT8726 and IT8728
|
||||
Super I/O chips.
|
||||
|
||||
If the driver does not work, then make sure that the game port in
|
||||
the BIOS is enabled.
|
||||
This is the driver for the hardware watchdog on the ITE IT8607,
|
||||
IT8620, IT8622, IT8625, IT8628, IT8655, IT8665, IT8686, IT8702,
|
||||
IT8712, IT8716, IT8718, IT8720, IT8721, IT8726, IT8728, and
|
||||
IT8783 Super I/O chips.
|
||||
|
||||
This watchdog simply watches your kernel to make sure it doesn't
|
||||
freeze, and if it does, it reboots your computer after a certain
|
||||
|
|
|
@ -82,8 +82,11 @@ obj-$(CONFIG_LPC18XX_WATCHDOG) += lpc18xx_wdt.o
|
|||
obj-$(CONFIG_BCM7038_WDT) += bcm7038_wdt.o
|
||||
obj-$(CONFIG_ATLAS7_WATCHDOG) += atlas7_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_ZX2967_WATCHDOG) += zx2967_wdt.o
|
||||
obj-$(CONFIG_STM32_WATCHDOG) += stm32_iwdg.o
|
||||
obj-$(CONFIG_UNIPHIER_WATCHDOG) += uniphier_wdt.o
|
||||
|
||||
# AVR32 Architecture
|
||||
obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o
|
||||
|
|
|
@ -97,7 +97,7 @@ static int bcm47xx_wdt_restart(struct watchdog_device *wdd,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct watchdog_ops bcm47xx_wdt_hard_ops = {
|
||||
static const struct watchdog_ops bcm47xx_wdt_hard_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.start = bcm47xx_wdt_hard_start,
|
||||
.stop = bcm47xx_wdt_hard_stop,
|
||||
|
@ -168,7 +168,7 @@ static const struct watchdog_info bcm47xx_wdt_info = {
|
|||
WDIOF_MAGICCLOSE,
|
||||
};
|
||||
|
||||
static struct watchdog_ops bcm47xx_wdt_soft_ops = {
|
||||
static const struct watchdog_ops bcm47xx_wdt_soft_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.start = bcm47xx_wdt_soft_start,
|
||||
.stop = bcm47xx_wdt_soft_stop,
|
||||
|
|
|
@ -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 struct of_device_id cdns_wdt_of_match[] = {
|
||||
static const struct of_device_id cdns_wdt_of_match[] = {
|
||||
{ .compatible = "cdns,wdt-r1p2", },
|
||||
{ /* end of table */ }
|
||||
};
|
||||
|
|
|
@ -173,7 +173,11 @@ static int davinci_wdt_probe(struct platform_device *pdev)
|
|||
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);
|
||||
|
||||
|
@ -198,8 +202,10 @@ static int davinci_wdt_probe(struct platform_device *pdev)
|
|||
return PTR_ERR(davinci_wdt->base);
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include <linux/of.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/watchdog.h>
|
||||
|
||||
#define WDOG_CONTROL_REG_OFFSET 0x00
|
||||
|
@ -54,6 +55,7 @@ struct dw_wdt {
|
|||
struct clk *clk;
|
||||
unsigned long rate;
|
||||
struct watchdog_device wdd;
|
||||
struct reset_control *rst;
|
||||
};
|
||||
|
||||
#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)
|
|||
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->info = &dw_wdt_ident;
|
||||
wdd->ops = &dw_wdt_ops;
|
||||
|
@ -279,6 +289,7 @@ static int dw_wdt_drv_remove(struct platform_device *pdev)
|
|||
struct dw_wdt *dw_wdt = platform_get_drvdata(pdev);
|
||||
|
||||
watchdog_unregister_device(&dw_wdt->wdd);
|
||||
reset_control_assert(dw_wdt->rst);
|
||||
clk_disable_unprepare(dw_wdt->clk);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -57,6 +57,7 @@
|
|||
#define SIO_F71808_ID 0x0901 /* Chipset ID */
|
||||
#define SIO_F71858_ID 0x0507 /* 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_F71869A_ID 0x1007 /* Chipset ID */
|
||||
#define SIO_F71882_ID 0x0541 /* Chipset ID */
|
||||
|
@ -101,7 +102,7 @@ MODULE_PARM_DESC(timeout,
|
|||
static unsigned int pulse_width = WATCHDOG_PULSE_WIDTH;
|
||||
module_param(pulse_width, uint, 0);
|
||||
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) ")");
|
||||
|
||||
static unsigned int f71862fg_pin = WATCHDOG_F71862FG_PIN;
|
||||
|
@ -119,13 +120,14 @@ module_param(start_withtimeout, uint, 0);
|
|||
MODULE_PARM_DESC(start_withtimeout, "Start watchdog timer on module load with"
|
||||
" given initial timeout. Zero (default) disables this feature.");
|
||||
|
||||
enum chips { f71808fg, f71858fg, f71862fg, f71869, f71882fg, f71889fg, f81865,
|
||||
f81866};
|
||||
enum chips { f71808fg, f71858fg, f71862fg, f71868, f71869, f71882fg, f71889fg,
|
||||
f81865, f81866};
|
||||
|
||||
static const char *f71808e_names[] = {
|
||||
"f71808fg",
|
||||
"f71858fg",
|
||||
"f71862fg",
|
||||
"f71868",
|
||||
"f71869",
|
||||
"f71882fg",
|
||||
"f71889fg",
|
||||
|
@ -252,16 +254,23 @@ static int watchdog_set_timeout(int timeout)
|
|||
static int watchdog_set_pulse_width(unsigned int pw)
|
||||
{
|
||||
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);
|
||||
|
||||
if (pw <= 1) {
|
||||
if (pw <= 1) {
|
||||
watchdog.pulse_val = 0;
|
||||
} else if (pw <= 25) {
|
||||
} else if (pw <= t1) {
|
||||
watchdog.pulse_val = 1;
|
||||
} else if (pw <= 125) {
|
||||
} else if (pw <= t2) {
|
||||
watchdog.pulse_val = 2;
|
||||
} else if (pw <= 5000) {
|
||||
} else if (pw <= t3) {
|
||||
watchdog.pulse_val = 3;
|
||||
} else {
|
||||
pr_err("pulse width out of range\n");
|
||||
|
@ -354,6 +363,7 @@ static int watchdog_start(void)
|
|||
goto exit_superio;
|
||||
break;
|
||||
|
||||
case f71868:
|
||||
case f71869:
|
||||
/* GPIO14 --> WDTRST# */
|
||||
superio_clear_bit(watchdog.sioaddr, SIO_REG_MFUNCT1, 4);
|
||||
|
@ -792,6 +802,9 @@ static int __init f71808e_find(int sioaddr)
|
|||
watchdog.type = f71862fg;
|
||||
err = f71862fg_pin_configure(0); /* validate module parameter */
|
||||
break;
|
||||
case SIO_F71868_ID:
|
||||
watchdog.type = f71868;
|
||||
break;
|
||||
case SIO_F71869_ID:
|
||||
case SIO_F71869A_ID:
|
||||
watchdog.type = f71869;
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
|
||||
#define SOFT_TIMEOUT_MIN 1
|
||||
#define SOFT_TIMEOUT_DEF 60
|
||||
#define SOFT_TIMEOUT_MAX 0xffff
|
||||
|
||||
enum {
|
||||
HW_ALGO_TOGGLE,
|
||||
|
@ -30,11 +29,7 @@ struct gpio_wdt_priv {
|
|||
bool active_low;
|
||||
bool state;
|
||||
bool always_running;
|
||||
bool armed;
|
||||
unsigned int hw_algo;
|
||||
unsigned int hw_margin;
|
||||
unsigned long last_jiffies;
|
||||
struct timer_list timer;
|
||||
struct watchdog_device wdd;
|
||||
};
|
||||
|
||||
|
@ -47,21 +42,10 @@ static void gpio_wdt_disable(struct gpio_wdt_priv *priv)
|
|||
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);
|
||||
|
||||
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) {
|
||||
case HW_ALGO_TOGGLE:
|
||||
/* Toggle output pin */
|
||||
|
@ -75,55 +59,33 @@ static void gpio_wdt_hwping(unsigned long data)
|
|||
gpio_set_value_cansleep(priv->gpio, priv->active_low);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gpio_wdt_start(struct watchdog_device *wdd)
|
||||
{
|
||||
struct gpio_wdt_priv *priv = watchdog_get_drvdata(wdd);
|
||||
|
||||
gpio_wdt_start_impl(priv);
|
||||
priv->armed = true;
|
||||
priv->state = priv->active_low;
|
||||
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)
|
||||
{
|
||||
struct gpio_wdt_priv *priv = watchdog_get_drvdata(wdd);
|
||||
|
||||
priv->armed = false;
|
||||
if (!priv->always_running) {
|
||||
mod_timer(&priv->timer, 0);
|
||||
gpio_wdt_disable(priv);
|
||||
clear_bit(WDOG_HW_RUNNING, &wdd->status);
|
||||
}
|
||||
|
||||
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 = {
|
||||
.options = WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING |
|
||||
WDIOF_SETTIMEOUT,
|
||||
|
@ -135,7 +97,6 @@ static const struct watchdog_ops gpio_wdt_ops = {
|
|||
.start = gpio_wdt_start,
|
||||
.stop = gpio_wdt_stop,
|
||||
.ping = gpio_wdt_ping,
|
||||
.set_timeout = gpio_wdt_set_timeout,
|
||||
};
|
||||
|
||||
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)
|
||||
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,
|
||||
"always-running");
|
||||
|
||||
|
@ -196,31 +154,26 @@ static int gpio_wdt_probe(struct platform_device *pdev)
|
|||
priv->wdd.info = &gpio_wdt_ident;
|
||||
priv->wdd.ops = &gpio_wdt_ops;
|
||||
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;
|
||||
|
||||
if (watchdog_init_timeout(&priv->wdd, 0, &pdev->dev) < 0)
|
||||
priv->wdd.timeout = SOFT_TIMEOUT_DEF;
|
||||
|
||||
setup_timer(&priv->timer, gpio_wdt_hwping, (unsigned long)&priv->wdd);
|
||||
|
||||
watchdog_stop_on_reboot(&priv->wdd);
|
||||
|
||||
ret = watchdog_register_device(&priv->wdd);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
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)
|
||||
{
|
||||
struct gpio_wdt_priv *priv = platform_get_drvdata(pdev);
|
||||
|
||||
del_timer_sync(&priv->timer);
|
||||
watchdog_unregister_device(&priv->wdd);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -147,8 +147,21 @@ static int mid_wdt_probe(struct platform_device *pdev)
|
|||
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);
|
||||
if (ret) {
|
||||
|
|
|
@ -12,8 +12,9 @@
|
|||
* http://www.ite.com.tw/
|
||||
*
|
||||
* Support of the watchdog timers, which are available on
|
||||
* IT8620, IT8702, IT8712, IT8716, IT8718, IT8720, IT8721, IT8726,
|
||||
* IT8728 and IT8783.
|
||||
* IT8607, IT8620, IT8622, IT8625, IT8628, IT8655, IT8665, IT8686,
|
||||
* IT8702, IT8712, IT8716, IT8718, IT8720, IT8721, IT8726, IT8728,
|
||||
* and IT8783.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
|
@ -24,38 +25,21 @@
|
|||
* 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.
|
||||
*
|
||||
* 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
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.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/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 DRIVER_VERSION WATCHDOG_NAME " driver, v" WATCHDOG_VERSION "\n"
|
||||
#define WD_MAGIC 'V'
|
||||
|
||||
/* Defaults for Module Parameter */
|
||||
#define DEFAULT_NOGAMEPORT 0
|
||||
#define DEFAULT_NOCIR 0
|
||||
#define DEFAULT_EXCLUSIVE 1
|
||||
#define DEFAULT_TIMEOUT 60
|
||||
#define DEFAULT_TESTMODE 0
|
||||
#define DEFAULT_NOWAYOUT WATCHDOG_NOWAYOUT
|
||||
|
@ -66,19 +50,22 @@
|
|||
|
||||
/* Logical device Numbers LDN */
|
||||
#define GPIO 0x07
|
||||
#define GAMEPORT 0x09
|
||||
#define CIR 0x0a
|
||||
|
||||
/* Configuration Registers and Functions */
|
||||
#define LDNREG 0x07
|
||||
#define CHIPID 0x20
|
||||
#define CHIPREV 0x22
|
||||
#define ACTREG 0x30
|
||||
#define BASEREG 0x60
|
||||
|
||||
/* Chip Id numbers */
|
||||
#define NO_DEV_ID 0xffff
|
||||
#define IT8607_ID 0x8607
|
||||
#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 IT8705_ID 0x8705
|
||||
#define IT8712_ID 0x8712
|
||||
|
@ -96,14 +83,6 @@
|
|||
#define WDTVALLSB 0x73
|
||||
#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 */
|
||||
#define WDT_TOV1 0x80
|
||||
#define WDT_KRST 0x40
|
||||
|
@ -111,55 +90,12 @@
|
|||
#define WDT_PWROK 0x10 /* not in it8721 */
|
||||
#define WDT_INT_MASK 0x0f
|
||||
|
||||
/* CIR Configuration Register LDN=0x0a */
|
||||
#define CIR_ILS 0x70
|
||||
static unsigned int max_units, chip_type;
|
||||
|
||||
/* The default Base address is not always available, we use this */
|
||||
#define CIR_BASE 0x0208
|
||||
static unsigned int timeout = DEFAULT_TIMEOUT;
|
||||
static int testmode = DEFAULT_TESTMODE;
|
||||
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_PARM_DESC(timeout, "Watchdog timeout in seconds, default="
|
||||
__MODULE_STRING(DEFAULT_TIMEOUT));
|
||||
|
@ -231,26 +167,40 @@ static inline void superio_outw(int val, int reg)
|
|||
}
|
||||
|
||||
/* 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;
|
||||
int tm = timeout;
|
||||
|
||||
if (testmode)
|
||||
cfg = 0;
|
||||
|
||||
if (tm <= max_units)
|
||||
if (t <= max_units)
|
||||
cfg |= WDT_TOV1;
|
||||
else
|
||||
tm /= 60;
|
||||
t /= 60;
|
||||
|
||||
if (chip_type != IT8721_ID)
|
||||
cfg |= WDT_PWROK;
|
||||
|
||||
superio_outb(cfg, WDTCFG);
|
||||
superio_outb(tm, WDTVALLSB);
|
||||
superio_outb(t, WDTVALLSB);
|
||||
if (max_units > 255)
|
||||
superio_outb(tm>>8, WDTVALMSB);
|
||||
superio_outb(t >> 8, WDTVALMSB);
|
||||
}
|
||||
|
||||
static int wdt_update_timeout(unsigned int t)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = superio_enter();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
superio_select(GPIO);
|
||||
_wdt_update_timeout(t);
|
||||
superio_exit();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wdt_round_time(int t)
|
||||
|
@ -262,57 +212,14 @@ static int wdt_round_time(int t)
|
|||
|
||||
/* watchdog timer handling */
|
||||
|
||||
static void wdt_keepalive(void)
|
||||
static int wdt_start(struct watchdog_device *wdd)
|
||||
{
|
||||
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);
|
||||
return wdt_update_timeout(wdd->timeout);
|
||||
}
|
||||
|
||||
static int wdt_start(void)
|
||||
static int wdt_stop(struct watchdog_device *wdd)
|
||||
{
|
||||
int ret = superio_enter();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
superio_select(GPIO);
|
||||
if (test_bit(WDTS_USE_GP, &wdt_status))
|
||||
superio_outb(WDT_GAMEPORT, WDTCTRL);
|
||||
else if (test_bit(WDTS_USE_CIR, &wdt_status))
|
||||
superio_outb(WDT_CIRINT, WDTCTRL);
|
||||
wdt_update_timeout();
|
||||
|
||||
superio_exit();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wdt_stop(void)
|
||||
{
|
||||
int ret = superio_enter();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
superio_select(GPIO);
|
||||
superio_outb(0x00, WDTCTRL);
|
||||
superio_outb(WDT_TOV1, WDTCFG);
|
||||
superio_outb(0x00, WDTVALLSB);
|
||||
if (max_units > 255)
|
||||
superio_outb(0x00, WDTVALMSB);
|
||||
|
||||
superio_exit();
|
||||
return 0;
|
||||
return wdt_update_timeout(0);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -325,292 +232,44 @@ static int wdt_stop(void)
|
|||
* 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)
|
||||
return -EINVAL;
|
||||
int ret = 0;
|
||||
|
||||
if (t > max_units)
|
||||
timeout = wdt_round_time(t);
|
||||
else
|
||||
timeout = t;
|
||||
t = wdt_round_time(t);
|
||||
|
||||
if (test_bit(WDTS_TIMER_RUN, &wdt_status)) {
|
||||
int ret = superio_enter();
|
||||
if (ret)
|
||||
return ret;
|
||||
wdd->timeout = t;
|
||||
|
||||
superio_select(GPIO);
|
||||
wdt_update_timeout();
|
||||
superio_exit();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if (watchdog_hw_running(wdd))
|
||||
ret = wdt_update_timeout(t);
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* wdt_release - watchdog file_operations .release
|
||||
* @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)
|
||||
{
|
||||
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;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct watchdog_info ident = {
|
||||
.options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING,
|
||||
.firmware_version = 1,
|
||||
.firmware_version = 1,
|
||||
.identity = WATCHDOG_NAME,
|
||||
};
|
||||
|
||||
/**
|
||||
* wdt_ioctl - watchdog file_operations .unlocked_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.
|
||||
*
|
||||
* 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 watchdog_ops wdt_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.start = wdt_start,
|
||||
.stop = wdt_stop,
|
||||
.set_timeout = wdt_set_timeout,
|
||||
};
|
||||
|
||||
static struct miscdevice wdt_miscdev = {
|
||||
.minor = WATCHDOG_MINOR,
|
||||
.name = "watchdog",
|
||||
.fops = &wdt_fops,
|
||||
};
|
||||
|
||||
static struct notifier_block wdt_notifier = {
|
||||
.notifier_call = wdt_notify_sys,
|
||||
static struct watchdog_device wdt_dev = {
|
||||
.info = &ident,
|
||||
.ops = &wdt_ops,
|
||||
.min_timeout = 1,
|
||||
};
|
||||
|
||||
static int __init it87_wdt_init(void)
|
||||
{
|
||||
int rc = 0;
|
||||
int try_gameport = !nogameport;
|
||||
u8 chip_rev;
|
||||
int gp_rreq_fail = 0;
|
||||
|
||||
wdt_status = 0;
|
||||
int rc;
|
||||
|
||||
rc = superio_enter();
|
||||
if (rc)
|
||||
|
@ -631,14 +290,20 @@ static int __init it87_wdt_init(void)
|
|||
case IT8726_ID:
|
||||
max_units = 65535;
|
||||
break;
|
||||
case IT8607_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 IT8720_ID:
|
||||
case IT8721_ID:
|
||||
case IT8728_ID:
|
||||
case IT8783_ID:
|
||||
max_units = 65535;
|
||||
try_gameport = 0;
|
||||
break;
|
||||
case IT8705_ID:
|
||||
pr_err("Unsupported Chip found, Chip %04x Revision %02x\n",
|
||||
|
@ -660,48 +325,7 @@ static int __init it87_wdt_init(void)
|
|||
superio_select(GPIO);
|
||||
superio_outb(WDT_TOV1, WDTCFG);
|
||||
superio_outb(0x00, WDTCTRL);
|
||||
|
||||
/* 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);
|
||||
}
|
||||
superio_exit();
|
||||
|
||||
if (timeout < 1 || timeout > max_units * 60) {
|
||||
timeout = DEFAULT_TIMEOUT;
|
||||
|
@ -712,83 +336,25 @@ static int __init it87_wdt_init(void)
|
|||
if (timeout > max_units)
|
||||
timeout = wdt_round_time(timeout);
|
||||
|
||||
rc = register_reboot_notifier(&wdt_notifier);
|
||||
wdt_dev.timeout = timeout;
|
||||
wdt_dev.max_timeout = max_units * 60;
|
||||
|
||||
watchdog_stop_on_reboot(&wdt_dev);
|
||||
rc = watchdog_register_device(&wdt_dev);
|
||||
if (rc) {
|
||||
pr_err("Cannot register reboot notifier (err=%d)\n", rc);
|
||||
goto err_out_region;
|
||||
pr_err("Cannot register watchdog device (err=%d)\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = misc_register(&wdt_miscdev);
|
||||
if (rc) {
|
||||
pr_err("Cannot register miscdev on minor=%d (err=%d)\n",
|
||||
wdt_miscdev.minor, rc);
|
||||
goto err_out_reboot;
|
||||
}
|
||||
pr_info("Chip IT%04x revision %d initialized. timeout=%d sec (nowayout=%d testmode=%d)\n",
|
||||
chip_type, chip_rev, timeout, nowayout, testmode);
|
||||
|
||||
/* 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",
|
||||
chip_type, chip_rev, timeout,
|
||||
nowayout, testmode, exclusive, nogameport, nocir);
|
||||
|
||||
superio_exit();
|
||||
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)
|
||||
{
|
||||
if (superio_enter() == 0) {
|
||||
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);
|
||||
watchdog_unregister_device(&wdt_dev);
|
||||
}
|
||||
|
||||
module_init(it87_wdt_init);
|
||||
|
|
|
@ -203,7 +203,9 @@ static int meson_gxbb_wdt_probe(struct platform_device *pdev)
|
|||
if (IS_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);
|
||||
|
||||
|
|
|
@ -651,5 +651,5 @@ module_param(nowayout, bool, 0);
|
|||
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
|
||||
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:orion_wdt");
|
||||
|
|
|
@ -0,0 +1,199 @@
|
|||
/*
|
||||
* 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");
|
|
@ -1,5 +1,4 @@
|
|||
/* linux/drivers/char/watchdog/s3c2410_wdt.c
|
||||
*
|
||||
/*
|
||||
* Copyright (c) 2004 Simtec Electronics
|
||||
* Ben Dooks <ben@simtec.co.uk>
|
||||
*
|
||||
|
@ -17,11 +16,7 @@
|
|||
* 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.
|
||||
*
|
||||
* 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/moduleparam.h>
|
||||
|
@ -37,6 +32,7 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/delay.h>
|
||||
|
@ -94,8 +90,7 @@ MODULE_PARM_DESC(tmr_atboot,
|
|||
__MODULE_STRING(S3C2410_WATCHDOG_ATBOOT));
|
||||
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
|
||||
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
|
||||
MODULE_PARM_DESC(soft_noboot, "Watchdog action, set to 1 to ignore reboots, "
|
||||
"0 to reboot (default 0)");
|
||||
MODULE_PARM_DESC(soft_noboot, "Watchdog action, set to 1 to ignore reboots, 0 to reboot (default 0)");
|
||||
|
||||
/**
|
||||
* struct s3c2410_wdt_variant - Per-variant config data
|
||||
|
@ -131,7 +126,7 @@ struct s3c2410_wdt {
|
|||
unsigned long wtdat_save;
|
||||
struct watchdog_device wdt_device;
|
||||
struct notifier_block freq_transition;
|
||||
struct s3c2410_wdt_variant *drv_data;
|
||||
const struct s3c2410_wdt_variant *drv_data;
|
||||
struct regmap *pmureg;
|
||||
};
|
||||
|
||||
|
@ -310,7 +305,8 @@ static inline int s3c2410wdt_is_running(struct s3c2410_wdt *wdt)
|
|||
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);
|
||||
unsigned long freq = clk_get_rate(wdt->clock);
|
||||
|
@ -401,7 +397,7 @@ static const struct watchdog_ops s3c2410wdt_ops = {
|
|||
.restart = s3c2410wdt_restart,
|
||||
};
|
||||
|
||||
static struct watchdog_device s3c2410_wdd = {
|
||||
static const struct watchdog_device s3c2410_wdd = {
|
||||
.info = &s3c2410_wdt_ident,
|
||||
.ops = &s3c2410wdt_ops,
|
||||
.timeout = S3C2410_WATCHDOG_DEFAULT_TIME,
|
||||
|
@ -507,22 +503,24 @@ static inline unsigned int s3c2410wdt_get_bootstatus(struct s3c2410_wdt *wdt)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static inline struct s3c2410_wdt_variant *
|
||||
static inline const struct s3c2410_wdt_variant *
|
||||
s3c2410_get_wdt_drv_data(struct platform_device *pdev)
|
||||
{
|
||||
if (pdev->dev.of_node) {
|
||||
const struct of_device_id *match;
|
||||
match = of_match_node(s3c2410_wdt_match, pdev->dev.of_node);
|
||||
return (struct s3c2410_wdt_variant *)match->data;
|
||||
} else {
|
||||
return (struct s3c2410_wdt_variant *)
|
||||
platform_get_device_id(pdev)->driver_data;
|
||||
const struct s3c2410_wdt_variant *variant;
|
||||
|
||||
variant = of_device_get_match_data(&pdev->dev);
|
||||
if (!variant) {
|
||||
/* Device matched by platform_device_id */
|
||||
variant = (struct s3c2410_wdt_variant *)
|
||||
platform_get_device_id(pdev)->driver_data;
|
||||
}
|
||||
|
||||
return variant;
|
||||
}
|
||||
|
||||
static int s3c2410wdt_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct s3c2410_wdt *wdt;
|
||||
struct resource *wdt_mem;
|
||||
struct resource *wdt_irq;
|
||||
|
@ -530,13 +528,11 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
|
|||
int started = 0;
|
||||
int ret;
|
||||
|
||||
dev = &pdev->dev;
|
||||
|
||||
wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
|
||||
if (!wdt)
|
||||
return -ENOMEM;
|
||||
|
||||
wdt->dev = &pdev->dev;
|
||||
wdt->dev = dev;
|
||||
spin_lock_init(&wdt->lock);
|
||||
wdt->wdt_device = s3c2410_wdd;
|
||||
|
||||
|
@ -592,7 +588,7 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
|
|||
/* see if we can actually set the requested timer margin, and if
|
||||
* 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,
|
||||
wdt->wdt_device.timeout);
|
||||
if (ret) {
|
||||
|
@ -601,11 +597,10 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
|
|||
|
||||
if (started == 0)
|
||||
dev_info(dev,
|
||||
"tmr_margin value out of range, default %d used\n",
|
||||
S3C2410_WATCHDOG_DEFAULT_TIME);
|
||||
"tmr_margin value out of range, default %d used\n",
|
||||
S3C2410_WATCHDOG_DEFAULT_TIME);
|
||||
else
|
||||
dev_info(dev, "default timer value is out of range, "
|
||||
"cannot start\n");
|
||||
dev_info(dev, "default timer value is out of range, cannot start\n");
|
||||
}
|
||||
|
||||
ret = devm_request_irq(dev, wdt_irq->start, s3c2410wdt_irq, 0,
|
||||
|
@ -619,7 +614,7 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
|
|||
watchdog_set_restart_priority(&wdt->wdt_device, 128);
|
||||
|
||||
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);
|
||||
if (ret) {
|
||||
|
@ -754,7 +749,6 @@ static struct platform_driver s3c2410wdt_driver = {
|
|||
|
||||
module_platform_driver(s3c2410wdt_driver);
|
||||
|
||||
MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>, "
|
||||
"Dimitry Andric <dimitry.andric@tomtom.com>");
|
||||
MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>, Dimitry Andric <dimitry.andric@tomtom.com>");
|
||||
MODULE_DESCRIPTION("S3C2410 Watchdog Device Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
@ -228,15 +228,13 @@ static int sama5d4_wdt_probe(struct platform_device *pdev)
|
|||
|
||||
wdt->reg_base = regs;
|
||||
|
||||
if (pdev->dev.of_node) {
|
||||
irq = irq_of_parse_and_map(pdev->dev.of_node, 0);
|
||||
if (!irq)
|
||||
dev_warn(&pdev->dev, "failed to get IRQ from DT\n");
|
||||
irq = irq_of_parse_and_map(pdev->dev.of_node, 0);
|
||||
if (!irq)
|
||||
dev_warn(&pdev->dev, "failed to get IRQ from DT\n");
|
||||
|
||||
ret = of_sama5d4_wdt_init(pdev->dev.of_node, wdt);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
ret = of_sama5d4_wdt_init(pdev->dev.of_node, wdt);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if ((wdt->mr & AT91_WDT_WDFIEN) && irq) {
|
||||
ret = devm_request_irq(&pdev->dev, irq, sama5d4_wdt_irq_handler,
|
||||
|
@ -302,6 +300,11 @@ static int sama5d4_wdt_resume(struct device *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);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -0,0 +1,253 @@
|
|||
/*
|
||||
* 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");
|
|
@ -0,0 +1,268 @@
|
|||
/*
|
||||
* 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 */
|
|||
|
||||
enum chips { w83627hf, w83627s, w83697hf, w83697ug, w83637hf, w83627thf,
|
||||
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 */
|
||||
module_param(timeout, int, 0);
|
||||
|
@ -97,6 +98,8 @@ MODULE_PARM_DESC(early_disable, "Disable watchdog at boot time (default=0)");
|
|||
#define NCT6779_ID 0xc5
|
||||
#define NCT6791_ID 0xc8
|
||||
#define NCT6792_ID 0xc9
|
||||
#define NCT6793_ID 0xd1
|
||||
#define NCT6795_ID 0xd3
|
||||
|
||||
#define W83627HF_WDT_TIMEOUT 0xf6
|
||||
#define W83697HF_WDT_TIMEOUT 0xf4
|
||||
|
@ -204,6 +207,8 @@ static int w83627hf_init(struct watchdog_device *wdog, enum chips chip)
|
|||
case nct6779:
|
||||
case nct6791:
|
||||
case nct6792:
|
||||
case nct6793:
|
||||
case nct6795:
|
||||
case nct6102:
|
||||
/*
|
||||
* These chips have a fixed WDTO# output pin (W83627UHG),
|
||||
|
@ -396,6 +401,12 @@ static int wdt_find(int addr)
|
|||
case NCT6792_ID:
|
||||
ret = nct6792;
|
||||
break;
|
||||
case NCT6793_ID:
|
||||
ret = nct6793;
|
||||
break;
|
||||
case NCT6795_ID:
|
||||
ret = nct6795;
|
||||
break;
|
||||
case NCT6102_ID:
|
||||
ret = nct6102;
|
||||
cr_wdt_timeout = NCT6102D_WDT_TIMEOUT;
|
||||
|
@ -437,6 +448,8 @@ static int __init wdt_init(void)
|
|||
"NCT6779",
|
||||
"NCT6791",
|
||||
"NCT6792",
|
||||
"NCT6793",
|
||||
"NCT6795",
|
||||
"NCT6102",
|
||||
};
|
||||
|
||||
|
|
|
@ -80,6 +80,9 @@ static struct watchdog_core_data *old_wd_data;
|
|||
|
||||
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)
|
||||
{
|
||||
/* All variables in milli-seconds */
|
||||
|
@ -192,18 +195,23 @@ static int watchdog_ping(struct watchdog_device *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)
|
||||
{
|
||||
struct watchdog_core_data *wd_data;
|
||||
struct watchdog_device *wdd;
|
||||
|
||||
wd_data = container_of(to_delayed_work(work), struct watchdog_core_data,
|
||||
work);
|
||||
|
||||
mutex_lock(&wd_data->lock);
|
||||
wdd = wd_data->wdd;
|
||||
if (wdd && (watchdog_active(wdd) || watchdog_hw_running(wdd)))
|
||||
__watchdog_ping(wdd);
|
||||
if (watchdog_worker_should_ping(wd_data))
|
||||
__watchdog_ping(wd_data->wdd);
|
||||
mutex_unlock(&wd_data->lock);
|
||||
}
|
||||
|
||||
|
@ -956,9 +964,14 @@ static int watchdog_cdev_register(struct watchdog_device *wdd, dev_t devno)
|
|||
* and schedule an immediate ping.
|
||||
*/
|
||||
if (watchdog_hw_running(wdd)) {
|
||||
__module_get(wdd->ops->owner);
|
||||
kref_get(&wd_data->kref);
|
||||
queue_delayed_work(watchdog_wq, &wd_data->work, 0);
|
||||
if (handle_boot_enabled) {
|
||||
__module_get(wdd->ops->owner);
|
||||
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;
|
||||
|
@ -1106,3 +1119,8 @@ void __exit watchdog_dev_exit(void)
|
|||
class_unregister(&watchdog_class);
|
||||
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 = {
|
|||
.identity = "zx2967 watchdog",
|
||||
};
|
||||
|
||||
static struct watchdog_ops zx2967_wdt_ops = {
|
||||
static const struct watchdog_ops zx2967_wdt_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.start = zx2967_wdt_start,
|
||||
.stop = zx2967_wdt_stop,
|
||||
|
|
Loading…
Reference in New Issue