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:
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:
......
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)
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) {
......
This diff is collapsed.
......@@ -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");
/*
* 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
* 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;
......
/*
* 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 */
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,
......
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