Commit 7e6127c1 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'linux-watchdog-4.16-rc1' of git://www.linux-watchdog.org/linux-watchdog

Pull watchdog updates from Wim Van Sebroeck:

 - new watchdog device drivers for Realtek RTD1295 and Spreadtrum SC9860
   platform

 - add support for the following devices: jz4780 SoC, AST25xx series SoC
   and r8a77970 SoC

 - convert to watchdog framework: i6300esb_wdt, xen_wdt and sp5100_tco

 - several fixes for watchdog core

 - remove at32ap700x and obsolete documentation

 - gpio: Convert to use GPIO descriptors

 - rename gemini into FTWDT010 as this IP block is generc from Faraday
   Technology

 - various clean-ups and small bugfixes

 - add Guenter Roeck as co-maintainer

 - change maintainers e-mail address

* tag 'linux-watchdog-4.16-rc1' of git://www.linux-watchdog.org/linux-watchdog: (74 commits)
  documentation: watchdog: remove documentation of w83697hf_wdt/w83697ug_wdt
  documentation: watchdog: remove documentation for ixp2000
  documentation: watchdog: remove documentation of at32ap700x_wdt
  watchdog: remove at32ap700x_wdt
  watchdog: sp5100_tco: Add support for recent FCH versions
  watchdog: sp5100-tco: Abort if watchdog is disabled by hardware
  watchdog: sp5100_tco: Use bit operations
  watchdog: sp5100_tco: Convert to use watchdog subsystem
  watchdog: sp5100_tco: Clean up function and variable names
  watchdog: sp5100_tco: Use dev_ print functions where possible
  watchdog: sp5100_tco: Match PCI device early
  watchdog: sp5100_tco: Clean up sp5100_tco_setupdevice
  watchdog: sp5100_tco: Use standard error codes
  watchdog: sp5100_tco: Use request_muxed_region where possible
  watchdog: sp5100_tco: Fix watchdog disable bit
  watchdog: sp5100_tco: Always use SP5100_IO_PM_{INDEX_REG,DATA_REG}
  watchdog: core: make sure the watchdog_worker is not deferred
  watchdog: mt7621: switch to using managed devm_watchdog_register_device()
  watchdog: mt7621: set WDOG_HW_RUNNING bit when appropriate
  watchdog: imx2_wdt: restore previous timeout after suspend+resume
  ...
parents 413879a1 592a547a
Cortina Systems Gemini SoC Watchdog
Required properties:
- compatible : must be "cortina,gemini-watchdog"
- reg : shall contain base register location and length
- interrupts : shall contain the interrupt for the watchdog
Optional properties:
- timeout-sec : the default watchdog timeout in seconds.
Example:
watchdog@41000000 {
compatible = "cortina,gemini-watchdog";
reg = <0x41000000 0x1000>;
interrupts = <3 IRQ_TYPE_LEVEL_HIGH>;
};
Cortina Systems Gemini SoC Watchdog Faraday Technology FTWDT010 watchdog
This is an IP part from Faraday Technology found in the Gemini
SoCs and others.
Required properties: Required properties:
- compatible : must be "cortina,gemini-watchdog" - compatible : must be one of
"faraday,ftwdt010"
"cortina,gemini-watchdog", "faraday,ftwdt010"
- reg : shall contain base register location and length - reg : shall contain base register location and length
- interrupts : shall contain the interrupt for the watchdog - interrupts : shall contain the interrupt for the watchdog
...@@ -11,7 +16,7 @@ Optional properties: ...@@ -11,7 +16,7 @@ Optional properties:
Example: Example:
watchdog@41000000 { watchdog@41000000 {
compatible = "cortina,gemini-watchdog"; compatible = "faraday,ftwdt010";
reg = <0x41000000 0x1000>; reg = <0x41000000 0x1000>;
interrupts = <3 IRQ_TYPE_LEVEL_HIGH>; interrupts = <3 IRQ_TYPE_LEVEL_HIGH>;
}; };
Ingenic Watchdog Timer (WDT) Controller for JZ4740 Ingenic Watchdog Timer (WDT) Controller for JZ4740 & JZ4780
Required properties: Required properties:
compatible: "ingenic,jz4740-watchdog" compatible: "ingenic,jz4740-watchdog" or "ingenic,jz4780-watchdog"
reg: Register address and length for watchdog registers reg: Register address and length for watchdog registers
Example: Example:
......
Realtek RTD1295 Watchdog
========================
Required properties:
- compatible : Should be "realtek,rtd1295-watchdog"
- reg : Specifies the physical base address and size of registers
- clocks : Specifies one clock input
Example:
watchdog@98007680 {
compatible = "realtek,rtd1295-watchdog";
reg = <0x98007680 0x100>;
clocks = <&osc27M>;
};
...@@ -4,10 +4,11 @@ Required properties: ...@@ -4,10 +4,11 @@ Required properties:
- compatible : Should be "renesas,<soctype>-wdt", and - compatible : Should be "renesas,<soctype>-wdt", and
"renesas,rcar-gen3-wdt" or "renesas,rza-wdt" as fallback. "renesas,rcar-gen3-wdt" or "renesas,rza-wdt" as fallback.
Examples with soctypes are: Examples with soctypes are:
- "renesas,r7s72100-wdt" (RZ/A1)
- "renesas,r8a7795-wdt" (R-Car H3) - "renesas,r8a7795-wdt" (R-Car H3)
- "renesas,r8a7796-wdt" (R-Car M3-W) - "renesas,r8a7796-wdt" (R-Car M3-W)
- "renesas,r8a77970-wdt" (R-Car V3M)
- "renesas,r8a77995-wdt" (R-Car D3) - "renesas,r8a77995-wdt" (R-Car D3)
- "renesas,r7s72100-wdt" (RZ/A1)
When compatible with the generic version, nodes must list the SoC-specific When compatible with the generic version, nodes must list the SoC-specific
version corresponding to the platform first, followed by the generic version corresponding to the platform first, followed by the generic
......
Spreadtrum SoCs Watchdog timer
Required properties:
- compatible : Should be "sprd,sp9860-wdt".
- reg : Specifies base physical address and size of the registers.
- interrupts : Exactly one interrupt specifier.
- timeout-sec : Contain the default watchdog timeout in seconds.
- clock-names : Contain the input clock names.
- clocks : Phandles to input clocks.
Example:
watchdog: watchdog@40310000 {
compatible = "sprd,sp9860-wdt";
reg = <0 0x40310000 0 0x1000>;
interrupts = <GIC_SPI 61 IRQ_TYPE_LEVEL_HIGH>;
timeout-sec = <12>;
clock-names = "enable", "rtc_enable";
clocks = <&clk_aon_apb_gates1 8>, <&clk_aon_apb_rtc_gates 9>;
};
...@@ -40,11 +40,6 @@ margin: Watchdog margin in seconds (default=60) ...@@ -40,11 +40,6 @@ margin: Watchdog margin in seconds (default=60)
nowayout: Disable watchdog shutdown on close nowayout: Disable watchdog shutdown on close
(default=kernel config parameter) (default=kernel config parameter)
------------------------------------------------- -------------------------------------------------
at32ap700x_wdt:
timeout: Timeout value. Limited to be 1 or 2 seconds. (default=2)
nowayout: Watchdog cannot be stopped once started
(default=kernel config parameter)
-------------------------------------------------
at91rm9200_wdt: at91rm9200_wdt:
wdt_time: Watchdog time in seconds. (default=5) wdt_time: Watchdog time in seconds. (default=5)
nowayout: Watchdog cannot be stopped once started nowayout: Watchdog cannot be stopped once started
...@@ -162,11 +157,6 @@ testmode: Watchdog test mode (1 = no reboot), default=0 ...@@ -162,11 +157,6 @@ testmode: Watchdog test mode (1 = no reboot), default=0
nowayout: Watchdog cannot be stopped once started nowayout: Watchdog cannot be stopped once started
(default=kernel config parameter) (default=kernel config parameter)
------------------------------------------------- -------------------------------------------------
ixp2000_wdt:
heartbeat: Watchdog heartbeat in seconds (default 60s)
nowayout: Watchdog cannot be stopped once started
(default=kernel config parameter)
-------------------------------------------------
ixp4xx_wdt: ixp4xx_wdt:
heartbeat: Watchdog heartbeat in seconds (default 60s) heartbeat: Watchdog heartbeat in seconds (default 60s)
nowayout: Watchdog cannot be stopped once started nowayout: Watchdog cannot be stopped once started
...@@ -381,19 +371,6 @@ timeout: Watchdog timeout in seconds. 1 <= timeout <= 255, default=60. ...@@ -381,19 +371,6 @@ timeout: Watchdog timeout in seconds. 1 <= timeout <= 255, default=60.
nowayout: Watchdog cannot be stopped once started nowayout: Watchdog cannot be stopped once started
(default=kernel config parameter) (default=kernel config parameter)
------------------------------------------------- -------------------------------------------------
w83697hf_wdt:
wdt_io: w83697hf/hg WDT io port (default 0x2e, 0 = autodetect)
timeout: Watchdog timeout in seconds. 1<= timeout <=255 (default=60)
nowayout: Watchdog cannot be stopped once started
(default=kernel config parameter)
early_disable: Watchdog gets disabled at boot time (default=1)
-------------------------------------------------
w83697ug_wdt:
wdt_io: w83697ug/uf WDT io port (default 0x2e)
timeout: Watchdog timeout in seconds. 1<= timeout <=255 (default=60)
nowayout: Watchdog cannot be stopped once started
(default=kernel config parameter)
-------------------------------------------------
w83877f_wdt: w83877f_wdt:
timeout: Watchdog timeout in seconds. (1<=timeout<=3600, default=30) timeout: Watchdog timeout in seconds. (1<=timeout<=3600, default=30)
nowayout: Watchdog cannot be stopped once started nowayout: Watchdog cannot be stopped once started
......
...@@ -14991,8 +14991,8 @@ S: Maintained ...@@ -14991,8 +14991,8 @@ S: Maintained
F: drivers/input/tablet/wacom_serial4.c F: drivers/input/tablet/wacom_serial4.c
WATCHDOG DEVICE DRIVERS WATCHDOG DEVICE DRIVERS
M: Wim Van Sebroeck <wim@iguana.be> M: Wim Van Sebroeck <wim@linux-watchdog.org>
R: Guenter Roeck <linux@roeck-us.net> M: Guenter Roeck <linux@roeck-us.net>
L: linux-watchdog@vger.kernel.org L: linux-watchdog@vger.kernel.org
W: http://www.linux-watchdog.org/ W: http://www.linux-watchdog.org/
T: git git://www.linux-watchdog.org/linux-watchdog.git T: git git://www.linux-watchdog.org/linux-watchdog.git
......
...@@ -328,16 +328,18 @@ config 977_WATCHDOG ...@@ -328,16 +328,18 @@ config 977_WATCHDOG
Not sure? It's safe to say N. Not sure? It's safe to say N.
config GEMINI_WATCHDOG config FTWDT010_WATCHDOG
tristate "Gemini watchdog" tristate "Faraday Technology FTWDT010 watchdog"
depends on ARCH_GEMINI depends on ARM || COMPILE_TEST
select WATCHDOG_CORE select WATCHDOG_CORE
default ARCH_GEMINI
help help
Say Y here if to include support for the watchdog timer Say Y here if to include support for the Faraday Technology
embedded in the Cortina Systems Gemini family of devices. FTWDT010 watchdog timer embedded in the Cortina Systems Gemini
family of devices.
To compile this driver as a module, choose M here: the To compile this driver as a module, choose M here: the
module will be called gemini_wdt. module will be called ftwdt010_wdt.
config IXP4XX_WATCHDOG config IXP4XX_WATCHDOG
tristate "IXP4xx Watchdog" tristate "IXP4xx Watchdog"
...@@ -748,12 +750,12 @@ config RENESAS_RZAWDT ...@@ -748,12 +750,12 @@ config RENESAS_RZAWDT
Renesas RZ/A SoCs. These watchdogs can be used to reset a system. Renesas RZ/A SoCs. These watchdogs can be used to reset a system.
config ASPEED_WATCHDOG config ASPEED_WATCHDOG
tristate "Aspeed 2400 watchdog support" tristate "Aspeed BMC watchdog support"
depends on ARCH_ASPEED || COMPILE_TEST depends on ARCH_ASPEED || COMPILE_TEST
select WATCHDOG_CORE select WATCHDOG_CORE
help help
Say Y here to include support for the watchdog timer Say Y here to include support for the watchdog timer
in Apseed BMC SoCs. in Aspeed BMC SoCs.
This driver is required to reboot the SoC. This driver is required to reboot the SoC.
...@@ -794,14 +796,23 @@ config UNIPHIER_WATCHDOG ...@@ -794,14 +796,23 @@ config UNIPHIER_WATCHDOG
To compile this driver as a module, choose M here: the To compile this driver as a module, choose M here: the
module will be called uniphier_wdt. module will be called uniphier_wdt.
# AVR32 Architecture config RTD119X_WATCHDOG
bool "Realtek RTD119x/RTD129x watchdog support"
depends on ARCH_REALTEK || COMPILE_TEST
depends on OF
select WATCHDOG_CORE
default ARCH_REALTEK
help
Say Y here to include support for the watchdog timer in
Realtek RTD1295 SoCs.
config AT32AP700X_WDT config SPRD_WATCHDOG
tristate "AT32AP700x watchdog" tristate "Spreadtrum watchdog support"
depends on CPU_AT32AP700X || COMPILE_TEST depends on ARCH_SPRD || COMPILE_TEST
select WATCHDOG_CORE
help help
Watchdog timer embedded into AT32AP700x devices. This will reboot Say Y here to include watchdog timer supported
your system when the timeout is reached. by Spreadtrum system.
# BLACKFIN Architecture # BLACKFIN Architecture
...@@ -1458,7 +1469,7 @@ config RC32434_WDT ...@@ -1458,7 +1469,7 @@ config RC32434_WDT
config INDYDOG config INDYDOG
tristate "Indy/I2 Hardware Watchdog" tristate "Indy/I2 Hardware Watchdog"
depends on SGI_HAS_INDYDOG || (MIPS && COMPILE_TEST) depends on SGI_HAS_INDYDOG
help help
Hardware driver for the Indy's/I2's watchdog. This is a Hardware driver for the Indy's/I2's watchdog. This is a
watchdog timer that will reboot the machine after a 60 second watchdog timer that will reboot the machine after a 60 second
......
...@@ -46,7 +46,7 @@ obj-$(CONFIG_OMAP_WATCHDOG) += omap_wdt.o ...@@ -46,7 +46,7 @@ obj-$(CONFIG_OMAP_WATCHDOG) += omap_wdt.o
obj-$(CONFIG_TWL4030_WATCHDOG) += twl4030_wdt.o obj-$(CONFIG_TWL4030_WATCHDOG) += twl4030_wdt.o
obj-$(CONFIG_21285_WATCHDOG) += wdt285.o obj-$(CONFIG_21285_WATCHDOG) += wdt285.o
obj-$(CONFIG_977_WATCHDOG) += wdt977.o obj-$(CONFIG_977_WATCHDOG) += wdt977.o
obj-$(CONFIG_GEMINI_WATCHDOG) += gemini_wdt.o obj-$(CONFIG_FTWDT010_WATCHDOG) += ftwdt010_wdt.o
obj-$(CONFIG_IXP4XX_WATCHDOG) += ixp4xx_wdt.o obj-$(CONFIG_IXP4XX_WATCHDOG) += ixp4xx_wdt.o
obj-$(CONFIG_KS8695_WATCHDOG) += ks8695_wdt.o obj-$(CONFIG_KS8695_WATCHDOG) += ks8695_wdt.o
obj-$(CONFIG_S3C2410_WATCHDOG) += s3c2410_wdt.o obj-$(CONFIG_S3C2410_WATCHDOG) += s3c2410_wdt.o
...@@ -88,9 +88,8 @@ obj-$(CONFIG_ASPEED_WATCHDOG) += aspeed_wdt.o ...@@ -88,9 +88,8 @@ obj-$(CONFIG_ASPEED_WATCHDOG) += aspeed_wdt.o
obj-$(CONFIG_ZX2967_WATCHDOG) += zx2967_wdt.o obj-$(CONFIG_ZX2967_WATCHDOG) += zx2967_wdt.o
obj-$(CONFIG_STM32_WATCHDOG) += stm32_iwdg.o obj-$(CONFIG_STM32_WATCHDOG) += stm32_iwdg.o
obj-$(CONFIG_UNIPHIER_WATCHDOG) += uniphier_wdt.o obj-$(CONFIG_UNIPHIER_WATCHDOG) += uniphier_wdt.o
obj-$(CONFIG_RTD119X_WATCHDOG) += rtd119x_wdt.o
# AVR32 Architecture obj-$(CONFIG_SPRD_WATCHDOG) += sprd_wdt.o
obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o
# BLACKFIN Architecture # BLACKFIN Architecture
obj-$(CONFIG_BFIN_WDT) += bfin_wdt.o obj-$(CONFIG_BFIN_WDT) += bfin_wdt.o
......
...@@ -181,7 +181,7 @@ static long advwdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ...@@ -181,7 +181,7 @@ static long advwdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
if (advwdt_set_heartbeat(new_timeout)) if (advwdt_set_heartbeat(new_timeout))
return -EINVAL; return -EINVAL;
advwdt_ping(); advwdt_ping();
/* Fall */ /* fall through */
case WDIOC_GETTIMEOUT: case WDIOC_GETTIMEOUT:
return put_user(timeout, p); return put_user(timeout, p);
default: default:
......
...@@ -223,8 +223,8 @@ static long ali_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ...@@ -223,8 +223,8 @@ static long ali_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
if (ali_settimer(new_timeout)) if (ali_settimer(new_timeout))
return -EINVAL; return -EINVAL;
ali_keepalive(); ali_keepalive();
/* Fall */
} }
/* fall through */
case WDIOC_GETTIMEOUT: case WDIOC_GETTIMEOUT:
return put_user(timeout, p); return put_user(timeout, p);
default: default:
......
...@@ -243,9 +243,13 @@ static int aspeed_wdt_probe(struct platform_device *pdev) ...@@ -243,9 +243,13 @@ static int aspeed_wdt_probe(struct platform_device *pdev)
if (of_property_read_bool(np, "aspeed,external-signal")) if (of_property_read_bool(np, "aspeed,external-signal"))
wdt->ctrl |= WDT_CTRL_WDT_EXT; wdt->ctrl |= WDT_CTRL_WDT_EXT;
writel(wdt->ctrl, wdt->base + WDT_CTRL);
if (readl(wdt->base + WDT_CTRL) & WDT_CTRL_ENABLE) { if (readl(wdt->base + WDT_CTRL) & WDT_CTRL_ENABLE) {
/*
* The watchdog is running, but invoke aspeed_wdt_start() to
* write wdt->ctrl to WDT_CTRL to ensure the watchdog's
* configuration conforms to the driver's expectations.
* Primarily, ensure we're using the 1MHz clock source.
*/
aspeed_wdt_start(&wdt->wdd); aspeed_wdt_start(&wdt->wdd);
set_bit(WDOG_HW_RUNNING, &wdt->wdd.status); set_bit(WDOG_HW_RUNNING, &wdt->wdd.status);
} }
...@@ -312,7 +316,18 @@ static struct platform_driver aspeed_watchdog_driver = { ...@@ -312,7 +316,18 @@ static struct platform_driver aspeed_watchdog_driver = {
.of_match_table = of_match_ptr(aspeed_wdt_of_table), .of_match_table = of_match_ptr(aspeed_wdt_of_table),
}, },
}; };
module_platform_driver(aspeed_watchdog_driver);
static int __init aspeed_wdt_init(void)
{
return platform_driver_register(&aspeed_watchdog_driver);
}
arch_initcall(aspeed_wdt_init);
static void __exit aspeed_wdt_exit(void)
{
platform_driver_unregister(&aspeed_watchdog_driver);
}
module_exit(aspeed_wdt_exit);
MODULE_DESCRIPTION("Aspeed Watchdog Driver"); MODULE_DESCRIPTION("Aspeed Watchdog Driver");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
This diff is collapsed.
...@@ -46,22 +46,6 @@ static void da9062_set_window_start(struct da9062_watchdog *wdt) ...@@ -46,22 +46,6 @@ static void da9062_set_window_start(struct da9062_watchdog *wdt)
wdt->j_time_stamp = jiffies; wdt->j_time_stamp = jiffies;
} }
static void da9062_apply_window_protection(struct da9062_watchdog *wdt)
{
unsigned long delay = msecs_to_jiffies(DA9062_RESET_PROTECTION_MS);
unsigned long timeout = wdt->j_time_stamp + delay;
unsigned long now = jiffies;
unsigned int diff_ms;
/* if time-limit has not elapsed then wait for remainder */
if (time_before(now, timeout)) {
diff_ms = jiffies_to_msecs(timeout-now);
dev_dbg(wdt->hw->dev,
"Kicked too quickly. Delaying %u msecs\n", diff_ms);
msleep(diff_ms);
}
}
static unsigned int da9062_wdt_timeout_to_sel(unsigned int secs) static unsigned int da9062_wdt_timeout_to_sel(unsigned int secs)
{ {
unsigned int i; unsigned int i;
...@@ -78,8 +62,6 @@ static int da9062_reset_watchdog_timer(struct da9062_watchdog *wdt) ...@@ -78,8 +62,6 @@ static int da9062_reset_watchdog_timer(struct da9062_watchdog *wdt)
{ {
int ret; int ret;
da9062_apply_window_protection(wdt);
ret = regmap_update_bits(wdt->hw->regmap, ret = regmap_update_bits(wdt->hw->regmap,
DA9062AA_CONTROL_F, DA9062AA_CONTROL_F,
DA9062AA_WATCHDOG_MASK, DA9062AA_WATCHDOG_MASK,
...@@ -100,6 +82,13 @@ static int da9062_wdt_update_timeout_register(struct da9062_watchdog *wdt, ...@@ -100,6 +82,13 @@ static int da9062_wdt_update_timeout_register(struct da9062_watchdog *wdt,
if (ret) if (ret)
return ret; return ret;
regmap_update_bits(chip->regmap,
DA9062AA_CONTROL_D,
DA9062AA_TWDSCALE_MASK,
DA9062_TWDSCALE_DISABLE);
usleep_range(150, 300);
return regmap_update_bits(chip->regmap, return regmap_update_bits(chip->regmap,
DA9062AA_CONTROL_D, DA9062AA_CONTROL_D,
DA9062AA_TWDSCALE_MASK, DA9062AA_TWDSCALE_MASK,
...@@ -175,6 +164,25 @@ static int da9062_wdt_set_timeout(struct watchdog_device *wdd, ...@@ -175,6 +164,25 @@ static int da9062_wdt_set_timeout(struct watchdog_device *wdd,
return ret; return ret;
} }
static int da9062_wdt_restart(struct watchdog_device *wdd, unsigned long action,
void *data)
{
struct da9062_watchdog *wdt = watchdog_get_drvdata(wdd);
int ret;
ret = regmap_write(wdt->hw->regmap,
DA9062AA_CONTROL_F,
DA9062AA_SHUTDOWN_MASK);
if (ret)
dev_alert(wdt->hw->dev, "Failed to shutdown (err = %d)\n",
ret);
/* wait for reset to assert... */
mdelay(500);
return ret;
}
static const struct watchdog_info da9062_watchdog_info = { static const struct watchdog_info da9062_watchdog_info = {
.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
.identity = "DA9062 WDT", .identity = "DA9062 WDT",
...@@ -186,6 +194,7 @@ static const struct watchdog_ops da9062_watchdog_ops = { ...@@ -186,6 +194,7 @@ static const struct watchdog_ops da9062_watchdog_ops = {
.stop = da9062_wdt_stop, .stop = da9062_wdt_stop,
.ping = da9062_wdt_ping, .ping = da9062_wdt_ping,
.set_timeout = da9062_wdt_set_timeout, .set_timeout = da9062_wdt_set_timeout,
.restart = da9062_wdt_restart,
}; };
static const struct of_device_id da9062_compatible_id_table[] = { static const struct of_device_id da9062_compatible_id_table[] = {
...@@ -215,10 +224,13 @@ static int da9062_wdt_probe(struct platform_device *pdev) ...@@ -215,10 +224,13 @@ static int da9062_wdt_probe(struct platform_device *pdev)
wdt->wdtdev.ops = &da9062_watchdog_ops; wdt->wdtdev.ops = &da9062_watchdog_ops;
wdt->wdtdev.min_timeout = DA9062_WDT_MIN_TIMEOUT; wdt->wdtdev.min_timeout = DA9062_WDT_MIN_TIMEOUT;
wdt->wdtdev.max_timeout = DA9062_WDT_MAX_TIMEOUT; wdt->wdtdev.max_timeout = DA9062_WDT_MAX_TIMEOUT;
wdt->wdtdev.min_hw_heartbeat_ms = DA9062_RESET_PROTECTION_MS;
wdt->wdtdev.timeout = DA9062_WDG_DEFAULT_TIMEOUT; wdt->wdtdev.timeout = DA9062_WDG_DEFAULT_TIMEOUT;
wdt->wdtdev.status = WATCHDOG_NOWAYOUT_INIT_STATUS; wdt->wdtdev.status = WATCHDOG_NOWAYOUT_INIT_STATUS;
wdt->wdtdev.parent = &pdev->dev; wdt->wdtdev.parent = &pdev->dev;
watchdog_set_restart_priority(&wdt->wdtdev, 128);
watchdog_set_drvdata(&wdt->wdtdev, wdt); watchdog_set_drvdata(&wdt->wdtdev, wdt);
ret = devm_watchdog_register_device(&pdev->dev, &wdt->wdtdev); ret = devm_watchdog_register_device(&pdev->dev, &wdt->wdtdev);
......
...@@ -140,6 +140,42 @@ static unsigned int davinci_wdt_get_timeleft(struct watchdog_device *wdd) ...@@ -140,6 +140,42 @@ static unsigned int davinci_wdt_get_timeleft(struct watchdog_device *wdd)
return wdd->timeout - timer_counter; return wdd->timeout - timer_counter;
} }
static int davinci_wdt_restart(struct watchdog_device *wdd,
unsigned long action, void *data)
{
struct davinci_wdt_device *davinci_wdt = watchdog_get_drvdata(wdd);
u32 tgcr, wdtcr;
/* disable, internal clock source */
iowrite32(0, davinci_wdt->base + TCR);
/* reset timer, set mode to 64-bit watchdog, and unreset */
tgcr = 0;
iowrite32(tgcr, davinci_wdt->base + TGCR);
tgcr = TIMMODE_64BIT_WDOG | TIM12RS_UNRESET | TIM34RS_UNRESET;
iowrite32(tgcr, davinci_wdt->base + TGCR);
/* clear counter and period regs */
iowrite32(0, davinci_wdt->base + TIM12);
iowrite32(0, davinci_wdt->base + TIM34);
iowrite32(0, davinci_wdt->base + PRD12);
iowrite32(0, davinci_wdt->base + PRD34);
/* put watchdog in pre-active state */
wdtcr = WDKEY_SEQ0 | WDEN;
iowrite32(wdtcr, davinci_wdt->base + WDTCR);
/* put watchdog in active state */
wdtcr = WDKEY_SEQ1 | WDEN;
iowrite32(wdtcr, davinci_wdt->base + WDTCR);
/* write an invalid value to the WDKEY field to trigger a restart */
wdtcr = 0x00004000;
iowrite32(wdtcr, davinci_wdt->base + WDTCR);
return 0;
}
static const struct watchdog_info davinci_wdt_info = { static const struct watchdog_info davinci_wdt_info = {
.options = WDIOF_KEEPALIVEPING, .options = WDIOF_KEEPALIVEPING,
.identity = "DaVinci/Keystone Watchdog", .identity = "DaVinci/Keystone Watchdog",
...@@ -151,6 +187,7 @@ static const struct watchdog_ops davinci_wdt_ops = { ...@@ -151,6 +187,7 @@ static const struct watchdog_ops davinci_wdt_ops = {
.stop = davinci_wdt_ping, .stop = davinci_wdt_ping,
.ping = davinci_wdt_ping, .ping = davinci_wdt_ping,
.get_timeleft = davinci_wdt_get_timeleft, .get_timeleft = davinci_wdt_get_timeleft,
.restart = davinci_wdt_restart,
}; };
static int davinci_wdt_probe(struct platform_device *pdev) static int davinci_wdt_probe(struct platform_device *pdev)
...@@ -195,6 +232,7 @@ static int davinci_wdt_probe(struct platform_device *pdev) ...@@ -195,6 +232,7 @@ static int davinci_wdt_probe(struct platform_device *pdev)
watchdog_set_drvdata(wdd, davinci_wdt); watchdog_set_drvdata(wdd, davinci_wdt);
watchdog_set_nowayout(wdd, 1); watchdog_set_nowayout(wdd, 1);
watchdog_set_restart_priority(wdd, 128);
wdt_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); wdt_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
davinci_wdt->base = devm_ioremap_resource(dev, wdt_mem); davinci_wdt->base = devm_ioremap_resource(dev, wdt_mem);
......
...@@ -127,14 +127,27 @@ static int dw_wdt_start(struct watchdog_device *wdd) ...@@ -127,14 +127,27 @@ static int dw_wdt_start(struct watchdog_device *wdd)
dw_wdt_set_timeout(wdd, wdd->timeout); dw_wdt_set_timeout(wdd, wdd->timeout);
set_bit(WDOG_HW_RUNNING, &wdd->status);
writel(WDOG_CONTROL_REG_WDT_EN_MASK, writel(WDOG_CONTROL_REG_WDT_EN_MASK,
dw_wdt->regs + WDOG_CONTROL_REG_OFFSET); dw_wdt->regs + WDOG_CONTROL_REG_OFFSET);
return 0; return 0;
} }
static int dw_wdt_stop(struct watchdog_device *wdd)
{
struct dw_wdt *dw_wdt = to_dw_wdt(wdd);
if (!dw_wdt->rst) {
set_bit(WDOG_HW_RUNNING, &wdd->status);
return 0;
}
reset_control_assert(dw_wdt->rst);
reset_control_deassert(dw_wdt->rst);
return 0;
}
static int dw_wdt_restart(struct watchdog_device *wdd, static int dw_wdt_restart(struct watchdog_device *wdd,
unsigned long action, void *data) unsigned long action, void *data)
{ {
...@@ -173,6 +186,7 @@ static const struct watchdog_info dw_wdt_ident = { ...@@ -173,6 +186,7 @@ static const struct watchdog_info dw_wdt_ident = {
static const struct watchdog_ops dw_wdt_ops = { static const struct watchdog_ops dw_wdt_ops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.start = dw_wdt_start, .start = dw_wdt_start,
.stop = dw_wdt_stop,
.ping = dw_wdt_ping, .ping = dw_wdt_ping,
.set_timeout = dw_wdt_set_timeout, .set_timeout = dw_wdt_set_timeout,
.get_timeleft = dw_wdt_get_timeleft, .get_timeleft = dw_wdt_get_timeleft,
......
...@@ -290,7 +290,7 @@ static long eurwdt_ioctl(struct file *file, ...@@ -290,7 +290,7 @@ static long eurwdt_ioctl(struct file *file,
eurwdt_timeout = time; eurwdt_timeout = time;
eurwdt_set_timeout(time); eurwdt_set_timeout(time);
spin_unlock(&eurwdt_lock); spin_unlock(&eurwdt_lock);
/* Fall */ /* fall through */
case WDIOC_GETTIMEOUT: case WDIOC_GETTIMEOUT:
return put_user(eurwdt_timeout, p); return put_user(eurwdt_timeout, p);
......
...@@ -627,7 +627,7 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd, ...@@ -627,7 +627,7 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd,
if (new_options & WDIOS_ENABLECARD) if (new_options & WDIOS_ENABLECARD)
return watchdog_start(); return watchdog_start();
/* fall through */
case WDIOC_KEEPALIVE: case WDIOC_KEEPALIVE:
watchdog_keepalive(); watchdog_keepalive();
...@@ -641,7 +641,7 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd, ...@@ -641,7 +641,7 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd,
return -EINVAL; return -EINVAL;
watchdog_keepalive(); watchdog_keepalive();
/* Fall */ /* fall through */
case WDIOC_GETTIMEOUT: case WDIOC_GETTIMEOUT:
return put_user(watchdog.timeout, uarg.i); return put_user(watchdog.timeout, uarg.i);
......
/* /*
* Watchdog driver for Cortina Systems Gemini SoC * Watchdog driver for Faraday Technology FTWDT010
* *
* Copyright (C) 2017 Linus Walleij <linus.walleij@linaro.org> * Copyright (C) 2017 Linus Walleij <linus.walleij@linaro.org>
* *
...@@ -22,92 +22,98 @@ ...@@ -22,92 +22,98 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/watchdog.h> #include <linux/watchdog.h>
#define GEMINI_WDCOUNTER 0x0 #define FTWDT010_WDCOUNTER 0x0
#define GEMINI_WDLOAD 0x4 #define FTWDT010_WDLOAD 0x4
#define GEMINI_WDRESTART 0x8 #define FTWDT010_WDRESTART 0x8
#define GEMINI_WDCR 0xC #define FTWDT010_WDCR 0xC
#define WDRESTART_MAGIC 0x5AB9 #define WDRESTART_MAGIC 0x5AB9
#define WDCR_CLOCK_5MHZ BIT(4) #define WDCR_CLOCK_5MHZ BIT(4)
#define WDCR_WDEXT BIT(3)
#define WDCR_WDINTR BIT(2)
#define WDCR_SYS_RST BIT(1) #define WDCR_SYS_RST BIT(1)
#define WDCR_ENABLE BIT(0) #define WDCR_ENABLE BIT(0)
#define WDT_CLOCK 5000000 /* 5 MHz */ #define WDT_CLOCK 5000000 /* 5 MHz */
struct gemini_wdt { struct ftwdt010_wdt {
struct watchdog_device wdd; struct watchdog_device wdd;
struct device *dev; struct device *dev;
void __iomem *base; void __iomem *base;
bool has_irq;
}; };
static inline static inline
struct gemini_wdt *to_gemini_wdt(struct watchdog_device *wdd) struct ftwdt010_wdt *to_ftwdt010_wdt(struct watchdog_device *wdd)
{ {
return container_of(wdd, struct gemini_wdt, wdd); return container_of(wdd, struct ftwdt010_wdt, wdd);
} }
static int gemini_wdt_start(struct watchdog_device *wdd) static int ftwdt010_wdt_start(struct watchdog_device *wdd)
{ {
struct gemini_wdt *gwdt = to_gemini_wdt(wdd); struct ftwdt010_wdt *gwdt = to_ftwdt010_wdt(wdd);
u32 enable;
writel(wdd->timeout * WDT_CLOCK, gwdt->base + GEMINI_WDLOAD); writel(wdd->timeout * WDT_CLOCK, gwdt->base + FTWDT010_WDLOAD);
writel(WDRESTART_MAGIC, gwdt->base + GEMINI_WDRESTART); writel(WDRESTART_MAGIC, gwdt->base + FTWDT010_WDRESTART);
/* set clock before enabling */ /* set clock before enabling */
writel(WDCR_CLOCK_5MHZ | WDCR_SYS_RST, enable = WDCR_CLOCK_5MHZ | WDCR_SYS_RST;
gwdt->base + GEMINI_WDCR); writel(enable, gwdt->base + FTWDT010_WDCR);
writel(WDCR_CLOCK_5MHZ | WDCR_SYS_RST | WDCR_ENABLE, if (gwdt->has_irq)
gwdt->base + GEMINI_WDCR); enable |= WDCR_WDINTR;
enable |= WDCR_ENABLE;
writel(enable, gwdt->base + FTWDT010_WDCR);
return 0; return 0;
} }
static int gemini_wdt_stop(struct watchdog_device *wdd) static int ftwdt010_wdt_stop(struct watchdog_device *wdd)
{ {
struct gemini_wdt *gwdt = to_gemini_wdt(wdd); struct ftwdt010_wdt *gwdt = to_ftwdt010_wdt(wdd);
writel(0, gwdt->base + GEMINI_WDCR); writel(0, gwdt->base + FTWDT010_WDCR);
return 0; return 0;
} }
static int gemini_wdt_ping(struct watchdog_device *wdd) static int ftwdt010_wdt_ping(struct watchdog_device *wdd)
{ {
struct gemini_wdt *gwdt = to_gemini_wdt(wdd); struct ftwdt010_wdt *gwdt = to_ftwdt010_wdt(wdd);
writel(WDRESTART_MAGIC, gwdt->base + GEMINI_WDRESTART); writel(WDRESTART_MAGIC, gwdt->base + FTWDT010_WDRESTART);
return 0; return 0;
} }
static int gemini_wdt_set_timeout(struct watchdog_device *wdd, static int ftwdt010_wdt_set_timeout(struct watchdog_device *wdd,
unsigned int timeout) unsigned int timeout)
{ {
wdd->timeout = timeout; wdd->timeout = timeout;
if (watchdog_active(wdd)) if (watchdog_active(wdd))
gemini_wdt_start(wdd); ftwdt010_wdt_start(wdd);
return 0; return 0;
} }
static irqreturn_t gemini_wdt_interrupt(int irq, void *data) static irqreturn_t ftwdt010_wdt_interrupt(int irq, void *data)
{ {
struct gemini_wdt *gwdt = data; struct ftwdt010_wdt *gwdt = data;
watchdog_notify_pretimeout(&gwdt->wdd); watchdog_notify_pretimeout(&gwdt->wdd);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
static const struct watchdog_ops gemini_wdt_ops = { static const struct watchdog_ops ftwdt010_wdt_ops = {
.start = gemini_wdt_start, .start = ftwdt010_wdt_start,
.stop = gemini_wdt_stop, .stop = ftwdt010_wdt_stop,
.ping = gemini_wdt_ping, .ping = ftwdt010_wdt_ping,
.set_timeout = gemini_wdt_set_timeout, .set_timeout = ftwdt010_wdt_set_timeout,
.owner = THIS_MODULE, .owner = THIS_MODULE,
}; };
static const struct watchdog_info gemini_wdt_info = { static const struct watchdog_info ftwdt010_wdt_info = {
.options = WDIOF_KEEPALIVEPING .options = WDIOF_KEEPALIVEPING
| WDIOF_MAGICCLOSE | WDIOF_MAGICCLOSE
| WDIOF_SETTIMEOUT, | WDIOF_SETTIMEOUT,
...@@ -115,11 +121,11 @@ static const struct watchdog_info gemini_wdt_info = { ...@@ -115,11 +121,11 @@ static const struct watchdog_info gemini_wdt_info = {
}; };
static int gemini_wdt_probe(struct platform_device *pdev) static int ftwdt010_wdt_probe(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct resource *res; struct resource *res;
struct gemini_wdt *gwdt; struct ftwdt010_wdt *gwdt;
unsigned int reg; unsigned int reg;
int irq; int irq;
int ret; int ret;
...@@ -133,13 +139,9 @@ static int gemini_wdt_probe(struct platform_device *pdev) ...@@ -133,13 +139,9 @@ static int gemini_wdt_probe(struct platform_device *pdev)
if (IS_ERR(gwdt->base)) if (IS_ERR(gwdt->base))
return PTR_ERR(gwdt->base); return PTR_ERR(gwdt->base);
irq = platform_get_irq(pdev, 0);
if (!irq)
return -EINVAL;
gwdt->dev = dev; gwdt->dev = dev;
gwdt->wdd.info = &gemini_wdt_info; gwdt->wdd.info = &ftwdt010_wdt_info;
gwdt->wdd.ops = &gemini_wdt_ops; gwdt->wdd.ops = &ftwdt010_wdt_ops;
gwdt->wdd.min_timeout = 1; gwdt->wdd.min_timeout = 1;
gwdt->wdd.max_timeout = 0xFFFFFFFF / WDT_CLOCK; gwdt->wdd.max_timeout = 0xFFFFFFFF / WDT_CLOCK;
gwdt->wdd.parent = dev; gwdt->wdd.parent = dev;
...@@ -151,17 +153,21 @@ static int gemini_wdt_probe(struct platform_device *pdev) ...@@ -151,17 +153,21 @@ static int gemini_wdt_probe(struct platform_device *pdev)
gwdt->wdd.timeout = 13U; gwdt->wdd.timeout = 13U;
watchdog_init_timeout(&gwdt->wdd, 0, dev); watchdog_init_timeout(&gwdt->wdd, 0, dev);
reg = readw(gwdt->base + GEMINI_WDCR); reg = readw(gwdt->base + FTWDT010_WDCR);
if (reg & WDCR_ENABLE) { if (reg & WDCR_ENABLE) {
/* Watchdog was enabled by the bootloader, disable it. */ /* Watchdog was enabled by the bootloader, disable it. */
reg &= ~WDCR_ENABLE; reg &= ~WDCR_ENABLE;
writel(reg, gwdt->base + GEMINI_WDCR); writel(reg, gwdt->base + FTWDT010_WDCR);
} }
ret = devm_request_irq(dev, irq, gemini_wdt_interrupt, 0, irq = platform_get_irq(pdev, 0);
if (irq) {
ret = devm_request_irq(dev, irq, ftwdt010_wdt_interrupt, 0,
"watchdog bark", gwdt); "watchdog bark", gwdt);
if (ret) if (ret)
return ret; return ret;
gwdt->has_irq = true;
}
ret = devm_watchdog_register_device(dev, &gwdt->wdd); ret = devm_watchdog_register_device(dev, &gwdt->wdd);
if (ret) { if (ret) {
...@@ -171,59 +177,60 @@ static int gemini_wdt_probe(struct platform_device *pdev) ...@@ -171,59 +177,60 @@ static int gemini_wdt_probe(struct platform_device *pdev)
/* Set up platform driver data */ /* Set up platform driver data */
platform_set_drvdata(pdev, gwdt); platform_set_drvdata(pdev, gwdt);
dev_info(dev, "Gemini watchdog driver enabled\n"); dev_info(dev, "FTWDT010 watchdog driver enabled\n");
return 0; return 0;
} }
static int __maybe_unused gemini_wdt_suspend(struct device *dev) static int __maybe_unused ftwdt010_wdt_suspend(struct device *dev)
{ {
struct gemini_wdt *gwdt = dev_get_drvdata(dev); struct ftwdt010_wdt *gwdt = dev_get_drvdata(dev);
unsigned int reg; unsigned int reg;
reg = readw(gwdt->base + GEMINI_WDCR); reg = readw(gwdt->base + FTWDT010_WDCR);
reg &= ~WDCR_ENABLE; reg &= ~WDCR_ENABLE;
writel(reg, gwdt->base + GEMINI_WDCR); writel(reg, gwdt->base + FTWDT010_WDCR);
return 0; return 0;
} }
static int __maybe_unused gemini_wdt_resume(struct device *dev) static int __maybe_unused ftwdt010_wdt_resume(struct device *dev)
{ {
struct gemini_wdt *gwdt = dev_get_drvdata(dev); struct ftwdt010_wdt *gwdt = dev_get_drvdata(dev);
unsigned int reg; unsigned int reg;
if (watchdog_active(&gwdt->wdd)) { if (watchdog_active(&gwdt->wdd)) {
reg = readw(gwdt->base + GEMINI_WDCR); reg = readw(gwdt->base + FTWDT010_WDCR);
reg |= WDCR_ENABLE; reg |= WDCR_ENABLE;
writel(reg, gwdt->base + GEMINI_WDCR); writel(reg, gwdt->base + FTWDT010_WDCR);
} }
return 0; return 0;
} }
static const struct dev_pm_ops gemini_wdt_dev_pm_ops = { static const struct dev_pm_ops ftwdt010_wdt_dev_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(gemini_wdt_suspend, SET_SYSTEM_SLEEP_PM_OPS(ftwdt010_wdt_suspend,
gemini_wdt_resume) ftwdt010_wdt_resume)
}; };
#ifdef CONFIG_OF #ifdef CONFIG_OF
static const struct of_device_id gemini_wdt_match[] = { static const struct of_device_id ftwdt010_wdt_match[] = {
{ .compatible = "faraday,ftwdt010" },
{ .compatible = "cortina,gemini-watchdog" }, { .compatible = "cortina,gemini-watchdog" },
{}, {},
}; };
MODULE_DEVICE_TABLE(of, gemini_wdt_match); MODULE_DEVICE_TABLE(of, ftwdt010_wdt_match);
#endif #endif
static struct platform_driver gemini_wdt_driver = { static struct platform_driver ftwdt010_wdt_driver = {
.probe = gemini_wdt_probe, .probe = ftwdt010_wdt_probe,
.driver = { .driver = {
.name = "gemini-wdt", .name = "ftwdt010-wdt",
.of_match_table = of_match_ptr(gemini_wdt_match), .of_match_table = of_match_ptr(ftwdt010_wdt_match),
.pm = &gemini_wdt_dev_pm_ops, .pm = &ftwdt010_wdt_dev_pm_ops,
}, },
}; };
module_platform_driver(gemini_wdt_driver); module_platform_driver(ftwdt010_wdt_driver);
MODULE_AUTHOR("Linus Walleij"); MODULE_AUTHOR("Linus Walleij");
MODULE_DESCRIPTION("Watchdog driver for Gemini"); MODULE_DESCRIPTION("Watchdog driver for Faraday Technology FTWDT010");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
...@@ -12,7 +12,8 @@ ...@@ -12,7 +12,8 @@
#include <linux/err.h> #include <linux/err.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of_gpio.h> #include <linux/gpio/consumer.h>
#include <linux/of.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/watchdog.h> #include <linux/watchdog.h>
...@@ -25,8 +26,7 @@ enum { ...@@ -25,8 +26,7 @@ enum {
}; };
struct gpio_wdt_priv { struct gpio_wdt_priv {
int gpio; struct gpio_desc *gpiod;
bool active_low;
bool state; bool state;
bool always_running; bool always_running;
unsigned int hw_algo; unsigned int hw_algo;
...@@ -35,11 +35,12 @@ struct gpio_wdt_priv { ...@@ -35,11 +35,12 @@ struct gpio_wdt_priv {
static void gpio_wdt_disable(struct gpio_wdt_priv *priv) static void gpio_wdt_disable(struct gpio_wdt_priv *priv)
{ {
gpio_set_value_cansleep(priv->gpio, !priv->active_low); /* Eternal ping */
gpiod_set_value_cansleep(priv->gpiod, 1);
/* Put GPIO back to tristate */ /* Put GPIO back to tristate */
if (priv->hw_algo == HW_ALGO_TOGGLE) if (priv->hw_algo == HW_ALGO_TOGGLE)
gpio_direction_input(priv->gpio); gpiod_direction_input(priv->gpiod);
} }
static int gpio_wdt_ping(struct watchdog_device *wdd) static int gpio_wdt_ping(struct watchdog_device *wdd)
...@@ -50,13 +51,13 @@ static int gpio_wdt_ping(struct watchdog_device *wdd) ...@@ -50,13 +51,13 @@ static int gpio_wdt_ping(struct watchdog_device *wdd)
case HW_ALGO_TOGGLE: case HW_ALGO_TOGGLE:
/* Toggle output pin */ /* Toggle output pin */
priv->state = !priv->state; priv->state = !priv->state;
gpio_set_value_cansleep(priv->gpio, priv->state); gpiod_set_value_cansleep(priv->gpiod, priv->state);
break; break;
case HW_ALGO_LEVEL: case HW_ALGO_LEVEL:
/* Pulse */ /* Pulse */
gpio_set_value_cansleep(priv->gpio, !priv->active_low); gpiod_set_value_cansleep(priv->gpiod, 1);
udelay(1); udelay(1);
gpio_set_value_cansleep(priv->gpio, priv->active_low); gpiod_set_value_cansleep(priv->gpiod, 0);
break; break;
} }
return 0; return 0;
...@@ -66,8 +67,8 @@ static int gpio_wdt_start(struct watchdog_device *wdd) ...@@ -66,8 +67,8 @@ static int gpio_wdt_start(struct watchdog_device *wdd)
{ {
struct gpio_wdt_priv *priv = watchdog_get_drvdata(wdd); struct gpio_wdt_priv *priv = watchdog_get_drvdata(wdd);
priv->state = priv->active_low; priv->state = 0;
gpio_direction_output(priv->gpio, priv->state); gpiod_direction_output(priv->gpiod, priv->state);
set_bit(WDOG_HW_RUNNING, &wdd->status); set_bit(WDOG_HW_RUNNING, &wdd->status);
...@@ -80,7 +81,8 @@ static int gpio_wdt_stop(struct watchdog_device *wdd) ...@@ -80,7 +81,8 @@ static int gpio_wdt_stop(struct watchdog_device *wdd)
if (!priv->always_running) { if (!priv->always_running) {
gpio_wdt_disable(priv); gpio_wdt_disable(priv);
clear_bit(WDOG_HW_RUNNING, &wdd->status); } else {
set_bit(WDOG_HW_RUNNING, &wdd->status);
} }
return 0; return 0;
...@@ -101,44 +103,38 @@ static const struct watchdog_ops gpio_wdt_ops = { ...@@ -101,44 +103,38 @@ static const struct watchdog_ops gpio_wdt_ops = {
static int gpio_wdt_probe(struct platform_device *pdev) static int gpio_wdt_probe(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
struct gpio_wdt_priv *priv; struct gpio_wdt_priv *priv;
enum of_gpio_flags flags; enum gpiod_flags gflags;
unsigned int hw_margin; unsigned int hw_margin;
unsigned long f = 0;
const char *algo; const char *algo;
int ret; int ret;
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv) if (!priv)
return -ENOMEM; return -ENOMEM;
platform_set_drvdata(pdev, priv); platform_set_drvdata(pdev, priv);
priv->gpio = of_get_gpio_flags(pdev->dev.of_node, 0, &flags); ret = of_property_read_string(np, "hw_algo", &algo);
if (!gpio_is_valid(priv->gpio))
return priv->gpio;
priv->active_low = flags & OF_GPIO_ACTIVE_LOW;
ret = of_property_read_string(pdev->dev.of_node, "hw_algo", &algo);
if (ret) if (ret)
return ret; return ret;
if (!strcmp(algo, "toggle")) { if (!strcmp(algo, "toggle")) {
priv->hw_algo = HW_ALGO_TOGGLE; priv->hw_algo = HW_ALGO_TOGGLE;
f = GPIOF_IN; gflags = GPIOD_IN;
} else if (!strcmp(algo, "level")) { } else if (!strcmp(algo, "level")) {
priv->hw_algo = HW_ALGO_LEVEL; priv->hw_algo = HW_ALGO_LEVEL;
f = priv->active_low ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW; gflags = GPIOD_OUT_LOW;
} else { } else {
return -EINVAL; return -EINVAL;
} }
ret = devm_gpio_request_one(&pdev->dev, priv->gpio, f, priv->gpiod = devm_gpiod_get(dev, NULL, gflags);
dev_name(&pdev->dev)); if (IS_ERR(priv->gpiod))
if (ret) return PTR_ERR(priv->gpiod);
return ret;
ret = of_property_read_u32(pdev->dev.of_node, ret = of_property_read_u32(np,
"hw_margin_ms", &hw_margin); "hw_margin_ms", &hw_margin);
if (ret) if (ret)
return ret; return ret;
...@@ -146,7 +142,7 @@ static int gpio_wdt_probe(struct platform_device *pdev) ...@@ -146,7 +142,7 @@ static int gpio_wdt_probe(struct platform_device *pdev)
if (hw_margin < 2 || hw_margin > 65535) if (hw_margin < 2 || hw_margin > 65535)
return -EINVAL; return -EINVAL;
priv->always_running = of_property_read_bool(pdev->dev.of_node, priv->always_running = of_property_read_bool(np,
"always-running"); "always-running");
watchdog_set_drvdata(&priv->wdd, priv); watchdog_set_drvdata(&priv->wdd, priv);
...@@ -155,9 +151,9 @@ static int gpio_wdt_probe(struct platform_device *pdev) ...@@ -155,9 +151,9 @@ static int gpio_wdt_probe(struct platform_device *pdev)
priv->wdd.ops = &gpio_wdt_ops; priv->wdd.ops = &gpio_wdt_ops;
priv->wdd.min_timeout = SOFT_TIMEOUT_MIN; priv->wdd.min_timeout = SOFT_TIMEOUT_MIN;
priv->wdd.max_hw_heartbeat_ms = hw_margin; priv->wdd.max_hw_heartbeat_ms = hw_margin;
priv->wdd.parent = &pdev->dev; priv->wdd.parent = dev;
if (watchdog_init_timeout(&priv->wdd, 0, &pdev->dev) < 0) if (watchdog_init_timeout(&priv->wdd, 0, dev) < 0)
priv->wdd.timeout = SOFT_TIMEOUT_DEF; priv->wdd.timeout = SOFT_TIMEOUT_DEF;
watchdog_stop_on_reboot(&priv->wdd); watchdog_stop_on_reboot(&priv->wdd);
......
...@@ -52,6 +52,7 @@ static char expect_release; ...@@ -52,6 +52,7 @@ static char expect_release;
static unsigned long hpwdt_is_open; static unsigned long hpwdt_is_open;
static void __iomem *pci_mem_addr; /* the PCI-memory address */ static void __iomem *pci_mem_addr; /* the PCI-memory address */
static unsigned long __iomem *hpwdt_nmistat;
static unsigned long __iomem *hpwdt_timer_reg; static unsigned long __iomem *hpwdt_timer_reg;
static unsigned long __iomem *hpwdt_timer_con; static unsigned long __iomem *hpwdt_timer_con;
...@@ -475,6 +476,11 @@ static int hpwdt_time_left(void) ...@@ -475,6 +476,11 @@ static int hpwdt_time_left(void)
} }
#ifdef CONFIG_HPWDT_NMI_DECODING #ifdef CONFIG_HPWDT_NMI_DECODING
static int hpwdt_my_nmi(void)
{
return ioread8(hpwdt_nmistat) & 0x6;
}
/* /*
* NMI Handler * NMI Handler
*/ */
...@@ -486,6 +492,9 @@ static int hpwdt_pretimeout(unsigned int ulReason, struct pt_regs *regs) ...@@ -486,6 +492,9 @@ static int hpwdt_pretimeout(unsigned int ulReason, struct pt_regs *regs)
if (!hpwdt_nmi_decoding) if (!hpwdt_nmi_decoding)
return NMI_DONE; return NMI_DONE;
if ((ulReason == NMI_UNKNOWN) && !hpwdt_my_nmi())
return NMI_DONE;
spin_lock_irqsave(&rom_lock, rom_pl); spin_lock_irqsave(&rom_lock, rom_pl);
if (!die_nmi_called && !is_icru && !is_uefi) if (!die_nmi_called && !is_icru && !is_uefi)
asminline_call(&cmn_regs, cru_rom_addr); asminline_call(&cmn_regs, cru_rom_addr);
...@@ -700,7 +709,7 @@ static void dmi_find_icru(const struct dmi_header *dm, void *dummy) ...@@ -700,7 +709,7 @@ static void dmi_find_icru(const struct dmi_header *dm, void *dummy)
smbios_proliant_ptr = (struct smbios_proliant_info *) dm; smbios_proliant_ptr = (struct smbios_proliant_info *) dm;
if (smbios_proliant_ptr->misc_features & 0x01) if (smbios_proliant_ptr->misc_features & 0x01)
is_icru = 1; is_icru = 1;
if (smbios_proliant_ptr->misc_features & 0x408) if (smbios_proliant_ptr->misc_features & 0x1400)
is_uefi = 1; is_uefi = 1;
} }
} }
...@@ -842,6 +851,7 @@ static int hpwdt_init_one(struct pci_dev *dev, ...@@ -842,6 +851,7 @@ static int hpwdt_init_one(struct pci_dev *dev,
retval = -ENOMEM; retval = -ENOMEM;
goto error_pci_iomap; goto error_pci_iomap;
} }
hpwdt_nmistat = pci_mem_addr + 0x6e;
hpwdt_timer_reg = pci_mem_addr + 0x70; hpwdt_timer_reg = pci_mem_addr + 0x70;
hpwdt_timer_con = pci_mem_addr + 0x72; hpwdt_timer_con = pci_mem_addr + 0x72;
......
This diff is collapsed.
...@@ -218,7 +218,7 @@ static long ibwdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ...@@ -218,7 +218,7 @@ static long ibwdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
if (ibwdt_set_heartbeat(new_margin)) if (ibwdt_set_heartbeat(new_margin))
return -EINVAL; return -EINVAL;
ibwdt_ping(); ibwdt_ping();
/* Fall */ /* fall through */
case WDIOC_GETTIMEOUT: case WDIOC_GETTIMEOUT:
return put_user(timeout, p); return put_user(timeout, p);
......
...@@ -169,15 +169,21 @@ static int imx2_wdt_ping(struct watchdog_device *wdog) ...@@ -169,15 +169,21 @@ static int imx2_wdt_ping(struct watchdog_device *wdog)
return 0; return 0;
} }
static int imx2_wdt_set_timeout(struct watchdog_device *wdog, static void __imx2_wdt_set_timeout(struct watchdog_device *wdog,
unsigned int new_timeout) unsigned int new_timeout)
{ {
struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog); struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog);
wdog->timeout = new_timeout;
regmap_update_bits(wdev->regmap, IMX2_WDT_WCR, IMX2_WDT_WCR_WT, regmap_update_bits(wdev->regmap, IMX2_WDT_WCR, IMX2_WDT_WCR_WT,
WDOG_SEC_TO_COUNT(new_timeout)); WDOG_SEC_TO_COUNT(new_timeout));
}
static int imx2_wdt_set_timeout(struct watchdog_device *wdog,
unsigned int new_timeout)
{
__imx2_wdt_set_timeout(wdog, new_timeout);
wdog->timeout = new_timeout;
return 0; return 0;
} }
...@@ -371,7 +377,11 @@ static int imx2_wdt_suspend(struct device *dev) ...@@ -371,7 +377,11 @@ static int imx2_wdt_suspend(struct device *dev)
/* The watchdog IP block is running */ /* The watchdog IP block is running */
if (imx2_wdt_is_running(wdev)) { if (imx2_wdt_is_running(wdev)) {
imx2_wdt_set_timeout(wdog, IMX2_WDT_MAX_TIME); /*
* Don't update wdog->timeout, we'll restore the current value
* during resume.
*/
__imx2_wdt_set_timeout(wdog, IMX2_WDT_MAX_TIME);
imx2_wdt_ping(wdog); imx2_wdt_ping(wdog);
} }
......
...@@ -146,6 +146,7 @@ static const struct watchdog_ops jz4740_wdt_ops = { ...@@ -146,6 +146,7 @@ static const struct watchdog_ops jz4740_wdt_ops = {
#ifdef CONFIG_OF #ifdef CONFIG_OF
static const struct of_device_id jz4740_wdt_of_matches[] = { static const struct of_device_id jz4740_wdt_of_matches[] = {
{ .compatible = "ingenic,jz4740-watchdog", }, { .compatible = "ingenic,jz4740-watchdog", },
{ .compatible = "ingenic,jz4780-watchdog", },
{ /* sentinel */ } { /* sentinel */ }
}; };
MODULE_DEVICE_TABLE(of, jz4740_wdt_of_matches); MODULE_DEVICE_TABLE(of, jz4740_wdt_of_matches);
......
...@@ -526,11 +526,10 @@ static ssize_t mei_dbgfs_read_state(struct file *file, char __user *ubuf, ...@@ -526,11 +526,10 @@ static ssize_t mei_dbgfs_read_state(struct file *file, char __user *ubuf,
size_t cnt, loff_t *ppos) size_t cnt, loff_t *ppos)
{ {
struct mei_wdt *wdt = file->private_data; struct mei_wdt *wdt = file->private_data;
const size_t bufsz = 32; char buf[32];
char buf[bufsz];
ssize_t pos; ssize_t pos;
pos = scnprintf(buf, bufsz, "state: %s\n", pos = scnprintf(buf, sizeof(buf), "state: %s\n",
mei_wdt_state_str(wdt->state)); mei_wdt_state_str(wdt->state));
return simple_read_from_buffer(ubuf, cnt, ppos, buf, pos); return simple_read_from_buffer(ubuf, cnt, ppos, buf, pos);
......
...@@ -22,7 +22,6 @@ ...@@ -22,7 +22,6 @@
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/timer.h>
#include <linux/of_address.h> #include <linux/of_address.h>
#include <linux/of_platform.h> #include <linux/of_platform.h>
#include <linux/module.h> #include <linux/module.h>
...@@ -31,10 +30,13 @@ ...@@ -31,10 +30,13 @@
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <sysdev/fsl_soc.h> #include <sysdev/fsl_soc.h>
#define WATCHDOG_TIMEOUT 10
struct mpc8xxx_wdt { struct mpc8xxx_wdt {
__be32 res0; __be32 res0;
__be32 swcrr; /* System watchdog control register */ __be32 swcrr; /* System watchdog control register */
#define SWCRR_SWTC 0xFFFF0000 /* Software Watchdog Time Count. */ #define SWCRR_SWTC 0xFFFF0000 /* Software Watchdog Time Count. */
#define SWCRR_SWF 0x00000008 /* Software Watchdog Freeze (mpc8xx). */
#define SWCRR_SWEN 0x00000004 /* Watchdog Enable bit. */ #define SWCRR_SWEN 0x00000004 /* Watchdog Enable bit. */
#define SWCRR_SWRI 0x00000002 /* Software Watchdog Reset/Interrupt Select bit.*/ #define SWCRR_SWRI 0x00000002 /* Software Watchdog Reset/Interrupt Select bit.*/
#define SWCRR_SWPR 0x00000001 /* Software Watchdog Counter Prescale bit. */ #define SWCRR_SWPR 0x00000001 /* Software Watchdog Counter Prescale bit. */
...@@ -52,14 +54,15 @@ struct mpc8xxx_wdt_type { ...@@ -52,14 +54,15 @@ struct mpc8xxx_wdt_type {
struct mpc8xxx_wdt_ddata { struct mpc8xxx_wdt_ddata {
struct mpc8xxx_wdt __iomem *base; struct mpc8xxx_wdt __iomem *base;
struct watchdog_device wdd; struct watchdog_device wdd;
struct timer_list timer;
spinlock_t lock; spinlock_t lock;
u16 swtc;
}; };
static u16 timeout = 0xffff; static u16 timeout;
module_param(timeout, ushort, 0); module_param(timeout, ushort, 0);
MODULE_PARM_DESC(timeout, MODULE_PARM_DESC(timeout,
"Watchdog timeout in ticks. (0<timeout<65536, default=65535)"); "Watchdog timeout in seconds. (1<timeout<65535, default="
__MODULE_STRING(WATCHDOG_TIMEOUT) ")");
static bool reset = 1; static bool reset = 1;
module_param(reset, bool, 0); module_param(reset, bool, 0);
...@@ -80,31 +83,27 @@ static void mpc8xxx_wdt_keepalive(struct mpc8xxx_wdt_ddata *ddata) ...@@ -80,31 +83,27 @@ static void mpc8xxx_wdt_keepalive(struct mpc8xxx_wdt_ddata *ddata)
spin_unlock(&ddata->lock); spin_unlock(&ddata->lock);
} }
static void mpc8xxx_wdt_timer_ping(struct timer_list *t)
{
struct mpc8xxx_wdt_ddata *ddata = from_timer(ddata, t, timer);
mpc8xxx_wdt_keepalive(ddata);
/* We're pinging it twice faster than needed, just to be sure. */
mod_timer(&ddata->timer, jiffies + HZ * ddata->wdd.timeout / 2);
}
static int mpc8xxx_wdt_start(struct watchdog_device *w) static int mpc8xxx_wdt_start(struct watchdog_device *w)
{ {
struct mpc8xxx_wdt_ddata *ddata = struct mpc8xxx_wdt_ddata *ddata =
container_of(w, struct mpc8xxx_wdt_ddata, wdd); container_of(w, struct mpc8xxx_wdt_ddata, wdd);
u32 tmp = in_be32(&ddata->base->swcrr);
u32 tmp = SWCRR_SWEN | SWCRR_SWPR;
/* Good, fire up the show */ /* Good, fire up the show */
tmp &= ~(SWCRR_SWTC | SWCRR_SWF | SWCRR_SWEN | SWCRR_SWRI | SWCRR_SWPR);
tmp |= SWCRR_SWEN | SWCRR_SWPR | (ddata->swtc << 16);
if (reset) if (reset)
tmp |= SWCRR_SWRI; tmp |= SWCRR_SWRI;
tmp |= timeout << 16;
out_be32(&ddata->base->swcrr, tmp); out_be32(&ddata->base->swcrr, tmp);
del_timer_sync(&ddata->timer); tmp = in_be32(&ddata->base->swcrr);
if (!(tmp & SWCRR_SWEN))
return -EOPNOTSUPP;
ddata->swtc = tmp >> 16;
set_bit(WDOG_HW_RUNNING, &ddata->wdd.status);
return 0; return 0;
} }
...@@ -118,17 +117,8 @@ static int mpc8xxx_wdt_ping(struct watchdog_device *w) ...@@ -118,17 +117,8 @@ static int mpc8xxx_wdt_ping(struct watchdog_device *w)
return 0; return 0;
} }
static int mpc8xxx_wdt_stop(struct watchdog_device *w)
{
struct mpc8xxx_wdt_ddata *ddata =
container_of(w, struct mpc8xxx_wdt_ddata, wdd);
mod_timer(&ddata->timer, jiffies);
return 0;
}
static struct watchdog_info mpc8xxx_wdt_info = { static struct watchdog_info mpc8xxx_wdt_info = {
.options = WDIOF_KEEPALIVEPING, .options = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT,
.firmware_version = 1, .firmware_version = 1,
.identity = "MPC8xxx", .identity = "MPC8xxx",
}; };
...@@ -137,7 +127,6 @@ static struct watchdog_ops mpc8xxx_wdt_ops = { ...@@ -137,7 +127,6 @@ static struct watchdog_ops mpc8xxx_wdt_ops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.start = mpc8xxx_wdt_start, .start = mpc8xxx_wdt_start,
.ping = mpc8xxx_wdt_ping, .ping = mpc8xxx_wdt_ping,
.stop = mpc8xxx_wdt_stop,
}; };
static int mpc8xxx_wdt_probe(struct platform_device *ofdev) static int mpc8xxx_wdt_probe(struct platform_device *ofdev)
...@@ -148,7 +137,6 @@ static int mpc8xxx_wdt_probe(struct platform_device *ofdev) ...@@ -148,7 +137,6 @@ static int mpc8xxx_wdt_probe(struct platform_device *ofdev)
struct mpc8xxx_wdt_ddata *ddata; struct mpc8xxx_wdt_ddata *ddata;
u32 freq = fsl_get_sys_freq(); u32 freq = fsl_get_sys_freq();
bool enabled; bool enabled;
unsigned int timeout_sec;
wdt_type = of_device_get_match_data(&ofdev->dev); wdt_type = of_device_get_match_data(&ofdev->dev);
if (!wdt_type) if (!wdt_type)
...@@ -173,26 +161,17 @@ static int mpc8xxx_wdt_probe(struct platform_device *ofdev) ...@@ -173,26 +161,17 @@ static int mpc8xxx_wdt_probe(struct platform_device *ofdev)
} }
spin_lock_init(&ddata->lock); spin_lock_init(&ddata->lock);
timer_setup(&ddata->timer, mpc8xxx_wdt_timer_ping, 0);
ddata->wdd.info = &mpc8xxx_wdt_info, ddata->wdd.info = &mpc8xxx_wdt_info,
ddata->wdd.ops = &mpc8xxx_wdt_ops, ddata->wdd.ops = &mpc8xxx_wdt_ops,
/* Calculate the timeout in seconds */ ddata->wdd.timeout = WATCHDOG_TIMEOUT;
timeout_sec = (timeout * wdt_type->prescaler) / freq; watchdog_init_timeout(&ddata->wdd, timeout, &ofdev->dev);
ddata->wdd.timeout = timeout_sec;
watchdog_set_nowayout(&ddata->wdd, nowayout); watchdog_set_nowayout(&ddata->wdd, nowayout);
ret = watchdog_register_device(&ddata->wdd); ddata->swtc = min(ddata->wdd.timeout * freq / wdt_type->prescaler,
if (ret) { 0xffffU);
pr_err("cannot register watchdog device (err=%d)\n", ret);
return ret;
}
pr_info("WDT driver for MPC8xxx initialized. mode:%s timeout=%d (%d seconds)\n",
reset ? "reset" : "interrupt", timeout, timeout_sec);
/* /*
* If the watchdog was previously enabled or we're running on * If the watchdog was previously enabled or we're running on
...@@ -200,7 +179,22 @@ static int mpc8xxx_wdt_probe(struct platform_device *ofdev) ...@@ -200,7 +179,22 @@ static int mpc8xxx_wdt_probe(struct platform_device *ofdev)
* userspace handles it. * userspace handles it.
*/ */
if (enabled) if (enabled)
mod_timer(&ddata->timer, jiffies); mpc8xxx_wdt_start(&ddata->wdd);
ddata->wdd.max_hw_heartbeat_ms = (ddata->swtc * wdt_type->prescaler) /
(freq / 1000);
ddata->wdd.min_timeout = ddata->wdd.max_hw_heartbeat_ms / 1000;
if (ddata->wdd.timeout < ddata->wdd.min_timeout)
ddata->wdd.timeout = ddata->wdd.min_timeout;
ret = watchdog_register_device(&ddata->wdd);
if (ret) {
pr_err("cannot register watchdog device (err=%d)\n", ret);
return ret;
}
pr_info("WDT driver for MPC8xxx initialized. mode:%s timeout=%d sec\n",
reset ? "reset" : "interrupt", ddata->wdd.timeout);
platform_set_drvdata(ofdev, ddata); platform_set_drvdata(ofdev, ddata);
return 0; return 0;
...@@ -212,7 +206,6 @@ static int mpc8xxx_wdt_remove(struct platform_device *ofdev) ...@@ -212,7 +206,6 @@ static int mpc8xxx_wdt_remove(struct platform_device *ofdev)
pr_crit("Watchdog removed, expect the %s soon!\n", pr_crit("Watchdog removed, expect the %s soon!\n",
reset ? "reset" : "machine check exception"); reset ? "reset" : "machine check exception");
del_timer_sync(&ddata->timer);
watchdog_unregister_device(&ddata->wdd); watchdog_unregister_device(&ddata->wdd);
return 0; return 0;
......
...@@ -105,6 +105,11 @@ static int mt7621_wdt_bootcause(void) ...@@ -105,6 +105,11 @@ static int mt7621_wdt_bootcause(void)
return 0; return 0;
} }
static int mt7621_wdt_is_running(struct watchdog_device *w)
{
return !!(rt_wdt_r32(TIMER_REG_TMR1CTL) & TMR1CTL_ENABLE);
}
static const struct watchdog_info mt7621_wdt_info = { static const struct watchdog_info mt7621_wdt_info = {
.identity = "Mediatek Watchdog", .identity = "Mediatek Watchdog",
.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
...@@ -128,7 +133,6 @@ static struct watchdog_device mt7621_wdt_dev = { ...@@ -128,7 +133,6 @@ static struct watchdog_device mt7621_wdt_dev = {
static int mt7621_wdt_probe(struct platform_device *pdev) static int mt7621_wdt_probe(struct platform_device *pdev)
{ {
struct resource *res; struct resource *res;
int ret;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
mt7621_wdt_base = devm_ioremap_resource(&pdev->dev, res); mt7621_wdt_base = devm_ioremap_resource(&pdev->dev, res);
...@@ -144,17 +148,22 @@ static int mt7621_wdt_probe(struct platform_device *pdev) ...@@ -144,17 +148,22 @@ static int mt7621_wdt_probe(struct platform_device *pdev)
watchdog_init_timeout(&mt7621_wdt_dev, mt7621_wdt_dev.max_timeout, watchdog_init_timeout(&mt7621_wdt_dev, mt7621_wdt_dev.max_timeout,
&pdev->dev); &pdev->dev);
watchdog_set_nowayout(&mt7621_wdt_dev, nowayout); watchdog_set_nowayout(&mt7621_wdt_dev, nowayout);
if (mt7621_wdt_is_running(&mt7621_wdt_dev)) {
/*
* Make sure to apply timeout from watchdog core, taking
* the prescaler of this driver here into account (the
* boot loader might be using a different prescaler).
*
* To avoid spurious resets because of different scaling,
* we first disable the watchdog, set the new prescaler
* and timeout, and then re-enable the watchdog.
*/
mt7621_wdt_stop(&mt7621_wdt_dev);
mt7621_wdt_start(&mt7621_wdt_dev);
set_bit(WDOG_HW_RUNNING, &mt7621_wdt_dev.status);
}
ret = watchdog_register_device(&mt7621_wdt_dev); return devm_watchdog_register_device(&pdev->dev, &mt7621_wdt_dev);
return 0;
}
static int mt7621_wdt_remove(struct platform_device *pdev)
{
watchdog_unregister_device(&mt7621_wdt_dev);
return 0;
} }
static void mt7621_wdt_shutdown(struct platform_device *pdev) static void mt7621_wdt_shutdown(struct platform_device *pdev)
...@@ -170,7 +179,6 @@ MODULE_DEVICE_TABLE(of, mt7621_wdt_match); ...@@ -170,7 +179,6 @@ MODULE_DEVICE_TABLE(of, mt7621_wdt_match);
static struct platform_driver mt7621_wdt_driver = { static struct platform_driver mt7621_wdt_driver = {
.probe = mt7621_wdt_probe, .probe = mt7621_wdt_probe,
.remove = mt7621_wdt_remove,
.shutdown = mt7621_wdt_shutdown, .shutdown = mt7621_wdt_shutdown,
.driver = { .driver = {
.name = KBUILD_MODNAME, .name = KBUILD_MODNAME,
......
...@@ -576,7 +576,7 @@ static int orion_wdt_probe(struct platform_device *pdev) ...@@ -576,7 +576,7 @@ static int orion_wdt_probe(struct platform_device *pdev)
/* /*
* Let's make sure the watchdog is fully stopped, unless it's * Let's make sure the watchdog is fully stopped, unless it's
* explicitly enabled. This may be the case if the module was * explicitly enabled. This may be the case if the module was
* removed and re-insterted, or if the bootloader explicitly * removed and re-inserted, or if the bootloader explicitly
* set a running watchdog before booting the kernel. * set a running watchdog before booting the kernel.
*/ */
if (!orion_wdt_enabled(&dev->wdt)) if (!orion_wdt_enabled(&dev->wdt))
......
...@@ -545,8 +545,8 @@ static long pcipcwd_ioctl(struct file *file, unsigned int cmd, ...@@ -545,8 +545,8 @@ static long pcipcwd_ioctl(struct file *file, unsigned int cmd,
return -EINVAL; return -EINVAL;
pcipcwd_keepalive(); pcipcwd_keepalive();
/* Fall */
} }
/* fall through */
case WDIOC_GETTIMEOUT: case WDIOC_GETTIMEOUT:
return put_user(heartbeat, p); return put_user(heartbeat, p);
......
...@@ -49,12 +49,11 @@ ...@@ -49,12 +49,11 @@
#define DRIVER_VERSION "1.02" #define DRIVER_VERSION "1.02"
#define DRIVER_AUTHOR "Wim Van Sebroeck <wim@iguana.be>" #define DRIVER_AUTHOR "Wim Van Sebroeck <wim@iguana.be>"
#define DRIVER_DESC "Berkshire USB-PC Watchdog driver" #define DRIVER_DESC "Berkshire USB-PC Watchdog driver"
#define DRIVER_LICENSE "GPL"
#define DRIVER_NAME "pcwd_usb" #define DRIVER_NAME "pcwd_usb"
MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC); MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE(DRIVER_LICENSE); MODULE_LICENSE("GPL");
#define WATCHDOG_HEARTBEAT 0 /* default heartbeat = #define WATCHDOG_HEARTBEAT 0 /* default heartbeat =
delay-time from dip-switches */ delay-time from dip-switches */
...@@ -456,8 +455,8 @@ static long usb_pcwd_ioctl(struct file *file, unsigned int cmd, ...@@ -456,8 +455,8 @@ static long usb_pcwd_ioctl(struct file *file, unsigned int cmd,
return -EINVAL; return -EINVAL;
usb_pcwd_keepalive(usb_pcwd_device); usb_pcwd_keepalive(usb_pcwd_device);
/* Fall */
} }
/* fall through */
case WDIOC_GETTIMEOUT: case WDIOC_GETTIMEOUT:
return put_user(heartbeat, p); return put_user(heartbeat, p);
......
/*
* Realtek RTD129x watchdog
*
* Copyright (c) 2017 Andreas Färber
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <linux/bitops.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/watchdog.h>
#define RTD119X_TCWCR 0x0
#define RTD119X_TCWTR 0x4
#define RTD119X_TCWOV 0xc
#define RTD119X_TCWCR_WDEN_DISABLED 0xa5
#define RTD119X_TCWCR_WDEN_ENABLED 0xff
#define RTD119X_TCWCR_WDEN_MASK 0xff
#define RTD119X_TCWTR_WDCLR BIT(0)
struct rtd119x_watchdog_device {
struct watchdog_device wdt_dev;
void __iomem *base;
struct clk *clk;
};
static int rtd119x_wdt_start(struct watchdog_device *wdev)
{
struct rtd119x_watchdog_device *data = watchdog_get_drvdata(wdev);
u32 val;
val = readl_relaxed(data->base + RTD119X_TCWCR);
val &= ~RTD119X_TCWCR_WDEN_MASK;
val |= RTD119X_TCWCR_WDEN_ENABLED;
writel(val, data->base + RTD119X_TCWCR);
return 0;
}
static int rtd119x_wdt_stop(struct watchdog_device *wdev)
{
struct rtd119x_watchdog_device *data = watchdog_get_drvdata(wdev);
u32 val;
val = readl_relaxed(data->base + RTD119X_TCWCR);
val &= ~RTD119X_TCWCR_WDEN_MASK;
val |= RTD119X_TCWCR_WDEN_DISABLED;
writel(val, data->base + RTD119X_TCWCR);
return 0;
}
static int rtd119x_wdt_ping(struct watchdog_device *wdev)
{
struct rtd119x_watchdog_device *data = watchdog_get_drvdata(wdev);
writel_relaxed(RTD119X_TCWTR_WDCLR, data->base + RTD119X_TCWTR);
return rtd119x_wdt_start(wdev);
}
static int rtd119x_wdt_set_timeout(struct watchdog_device *wdev, unsigned int val)
{
struct rtd119x_watchdog_device *data = watchdog_get_drvdata(wdev);
writel(val * clk_get_rate(data->clk), data->base + RTD119X_TCWOV);
data->wdt_dev.timeout = val;
return 0;
}
static const struct watchdog_ops rtd119x_wdt_ops = {
.owner = THIS_MODULE,
.start = rtd119x_wdt_start,
.stop = rtd119x_wdt_stop,
.ping = rtd119x_wdt_ping,
.set_timeout = rtd119x_wdt_set_timeout,
};
static const struct watchdog_info rtd119x_wdt_info = {
.identity = "rtd119x-wdt",
.options = 0,
};
static const struct of_device_id rtd119x_wdt_dt_ids[] = {
{ .compatible = "realtek,rtd1295-watchdog" },
{ }
};
static int rtd119x_wdt_probe(struct platform_device *pdev)
{
struct rtd119x_watchdog_device *data;
struct resource *res;
int ret;
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
data->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(data->base))
return PTR_ERR(data->base);
data->clk = of_clk_get(pdev->dev.of_node, 0);
if (IS_ERR(data->clk))
return PTR_ERR(data->clk);
ret = clk_prepare_enable(data->clk);
if (ret) {
clk_put(data->clk);
return ret;
}
data->wdt_dev.info = &rtd119x_wdt_info;
data->wdt_dev.ops = &rtd119x_wdt_ops;
data->wdt_dev.timeout = 120;
data->wdt_dev.max_timeout = 0xffffffff / clk_get_rate(data->clk);
data->wdt_dev.min_timeout = 1;
data->wdt_dev.parent = &pdev->dev;
watchdog_stop_on_reboot(&data->wdt_dev);
watchdog_set_drvdata(&data->wdt_dev, data);
platform_set_drvdata(pdev, data);
writel_relaxed(RTD119X_TCWTR_WDCLR, data->base + RTD119X_TCWTR);
rtd119x_wdt_set_timeout(&data->wdt_dev, data->wdt_dev.timeout);
rtd119x_wdt_stop(&data->wdt_dev);
ret = devm_watchdog_register_device(&pdev->dev, &data->wdt_dev);
if (ret) {
clk_disable_unprepare(data->clk);
clk_put(data->clk);
return ret;
}
return 0;
}
static int rtd119x_wdt_remove(struct platform_device *pdev)
{
struct rtd119x_watchdog_device *data = platform_get_drvdata(pdev);
watchdog_unregister_device(&data->wdt_dev);
clk_disable_unprepare(data->clk);
clk_put(data->clk);
return 0;
}
static struct platform_driver rtd119x_wdt_driver = {
.probe = rtd119x_wdt_probe,
.remove = rtd119x_wdt_remove,
.driver = {
.name = "rtd1295-watchdog",
.of_match_table = rtd119x_wdt_dt_ids,
},
};
builtin_platform_driver(rtd119x_wdt_driver);
This diff is collapsed.
...@@ -7,6 +7,8 @@ ...@@ -7,6 +7,8 @@
* TCO timer driver for sp5100 chipsets * TCO timer driver for sp5100 chipsets
*/ */
#include <linux/bitops.h>
/* /*
* Some address definitions for the Watchdog * Some address definitions for the Watchdog
*/ */
...@@ -14,8 +16,11 @@ ...@@ -14,8 +16,11 @@
#define SP5100_WDT_CONTROL(base) ((base) + 0x00) /* Watchdog Control */ #define SP5100_WDT_CONTROL(base) ((base) + 0x00) /* Watchdog Control */
#define SP5100_WDT_COUNT(base) ((base) + 0x04) /* Watchdog Count */ #define SP5100_WDT_COUNT(base) ((base) + 0x04) /* Watchdog Count */
#define SP5100_WDT_START_STOP_BIT (1 << 0) #define SP5100_WDT_START_STOP_BIT BIT(0)
#define SP5100_WDT_TRIGGER_BIT (1 << 7) #define SP5100_WDT_FIRED BIT(1)
#define SP5100_WDT_ACTION_RESET BIT(2)
#define SP5100_WDT_DISABLED BIT(3)
#define SP5100_WDT_TRIGGER_BIT BIT(7)
#define SP5100_PM_IOPORTS_SIZE 0x02 #define SP5100_PM_IOPORTS_SIZE 0x02
...@@ -24,43 +29,57 @@ ...@@ -24,43 +29,57 @@
* read them from a register. * read them from a register.
*/ */
/* For SP5100/SB7x0 chipset */ /* For SP5100/SB7x0/SB8x0 chipset */
#define SP5100_IO_PM_INDEX_REG 0xCD6 #define SP5100_IO_PM_INDEX_REG 0xCD6
#define SP5100_IO_PM_DATA_REG 0xCD7 #define SP5100_IO_PM_DATA_REG 0xCD7
/* For SP5100/SB7x0 chipset */
#define SP5100_SB_RESOURCE_MMIO_BASE 0x9C #define SP5100_SB_RESOURCE_MMIO_BASE 0x9C
#define SP5100_PM_WATCHDOG_CONTROL 0x69 #define SP5100_PM_WATCHDOG_CONTROL 0x69
#define SP5100_PM_WATCHDOG_BASE 0x6C #define SP5100_PM_WATCHDOG_BASE 0x6C
#define SP5100_PM_WATCHDOG_FIRED (1 << 1)
#define SP5100_PM_WATCHDOG_ACTION_RESET (1 << 2)
#define SP5100_PCI_WATCHDOG_MISC_REG 0x41 #define SP5100_PCI_WATCHDOG_MISC_REG 0x41
#define SP5100_PCI_WATCHDOG_DECODE_EN (1 << 3) #define SP5100_PCI_WATCHDOG_DECODE_EN BIT(3)
#define SP5100_PM_WATCHDOG_DISABLE (1 << 0) #define SP5100_PM_WATCHDOG_DISABLE ((u8)BIT(0))
#define SP5100_PM_WATCHDOG_SECOND_RES (3 << 1) #define SP5100_PM_WATCHDOG_SECOND_RES GENMASK(2, 1)
#define SP5100_DEVNAME "SP5100 TCO" #define SP5100_DEVNAME "SP5100 TCO"
/* For SB8x0(or later) chipset */ /* For SB8x0(or later) chipset */
#define SB800_IO_PM_INDEX_REG 0xCD6
#define SB800_IO_PM_DATA_REG 0xCD7
#define SB800_PM_ACPI_MMIO_EN 0x24 #define SB800_PM_ACPI_MMIO_EN 0x24
#define SB800_PM_WATCHDOG_CONTROL 0x48 #define SB800_PM_WATCHDOG_CONTROL 0x48
#define SB800_PM_WATCHDOG_BASE 0x48 #define SB800_PM_WATCHDOG_BASE 0x48
#define SB800_PM_WATCHDOG_CONFIG 0x4C #define SB800_PM_WATCHDOG_CONFIG 0x4C
#define SB800_PCI_WATCHDOG_DECODE_EN (1 << 0) #define SB800_PCI_WATCHDOG_DECODE_EN BIT(0)
#define SB800_PM_WATCHDOG_DISABLE (1 << 2) #define SB800_PM_WATCHDOG_DISABLE ((u8)BIT(1))
#define SB800_PM_WATCHDOG_SECOND_RES (3 << 0) #define SB800_PM_WATCHDOG_SECOND_RES GENMASK(1, 0)
#define SB800_ACPI_MMIO_DECODE_EN (1 << 0) #define SB800_ACPI_MMIO_DECODE_EN BIT(0)
#define SB800_ACPI_MMIO_SEL (1 << 1) #define SB800_ACPI_MMIO_SEL BIT(1)
#define SB800_PM_WDT_MMIO_OFFSET 0xB00 #define SB800_PM_WDT_MMIO_OFFSET 0xB00
#define SB800_DEVNAME "SB800 TCO" #define SB800_DEVNAME "SB800 TCO"
/* For recent chips with embedded FCH (rev 40+) */
#define EFCH_PM_DECODEEN 0x00
#define EFCH_PM_DECODEEN_WDT_TMREN BIT(7)
#define EFCH_PM_DECODEEN3 0x00
#define EFCH_PM_DECODEEN_SECOND_RES GENMASK(1, 0)
#define EFCH_PM_WATCHDOG_DISABLE ((u8)GENMASK(3, 2))
/* WDT MMIO if enabled with PM00_DECODEEN_WDT_TMREN */
#define EFCH_PM_WDT_ADDR 0xfeb00000
#define EFCH_PM_ISACONTROL 0x04
#define EFCH_PM_ISACONTROL_MMIOEN BIT(1)
#define EFCH_PM_ACPI_MMIO_ADDR 0xfed80000
#define EFCH_PM_ACPI_MMIO_WDT_OFFSET 0x00000b00
This diff is collapsed.
// SPDX-License-Identifier: GPL-2.0
/* /*
* Driver for STM32 Independent Watchdog * Driver for STM32 Independent Watchdog
* *
* Copyright (C) Yannick Fertre 2017 * Copyright (C) STMicroelectronics 2017
* Author: Yannick Fertre <yannick.fertre@st.com> * Author: Yannick Fertre <yannick.fertre@st.com> for STMicroelectronics.
* *
* This driver is based on tegra_wdt.c * This driver is based on tegra_wdt.c
* *
* License terms: GNU General Public License (GPL), version 2
*/ */
#include <linux/clk.h> #include <linux/clk.h>
......
...@@ -234,7 +234,6 @@ MODULE_DEVICE_TABLE(of, sunxi_wdt_dt_ids); ...@@ -234,7 +234,6 @@ MODULE_DEVICE_TABLE(of, sunxi_wdt_dt_ids);
static int sunxi_wdt_probe(struct platform_device *pdev) static int sunxi_wdt_probe(struct platform_device *pdev)
{ {
struct sunxi_wdt_dev *sunxi_wdt; struct sunxi_wdt_dev *sunxi_wdt;
const struct of_device_id *device;
struct resource *res; struct resource *res;
int err; int err;
...@@ -242,12 +241,10 @@ static int sunxi_wdt_probe(struct platform_device *pdev) ...@@ -242,12 +241,10 @@ static int sunxi_wdt_probe(struct platform_device *pdev)
if (!sunxi_wdt) if (!sunxi_wdt)
return -EINVAL; return -EINVAL;
device = of_match_device(sunxi_wdt_dt_ids, &pdev->dev); sunxi_wdt->wdt_regs = of_device_get_match_data(&pdev->dev);
if (!device) if (!sunxi_wdt->wdt_regs)
return -ENODEV; return -ENODEV;
sunxi_wdt->wdt_regs = device->data;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
sunxi_wdt->wdt_base = devm_ioremap_resource(&pdev->dev, res); sunxi_wdt->wdt_base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(sunxi_wdt->wdt_base)) if (IS_ERR(sunxi_wdt->wdt_base))
......
...@@ -97,6 +97,7 @@ static void watchdog_check_min_max_timeout(struct watchdog_device *wdd) ...@@ -97,6 +97,7 @@ static void watchdog_check_min_max_timeout(struct watchdog_device *wdd)
/** /**
* watchdog_init_timeout() - initialize the timeout field * watchdog_init_timeout() - initialize the timeout field
* @wdd: watchdog device
* @timeout_parm: timeout module parameter * @timeout_parm: timeout module parameter
* @dev: Device that stores the timeout-sec property * @dev: Device that stores the timeout-sec property
* *
......
This diff is collapsed.
...@@ -430,7 +430,7 @@ static long wdtpci_ioctl(struct file *file, unsigned int cmd, ...@@ -430,7 +430,7 @@ static long wdtpci_ioctl(struct file *file, unsigned int cmd,
if (wdtpci_set_heartbeat(new_heartbeat)) if (wdtpci_set_heartbeat(new_heartbeat))
return -EINVAL; return -EINVAL;
wdtpci_ping(); wdtpci_ping();
/* Fall */ /* fall through */
case WDIOC_GETTIMEOUT: case WDIOC_GETTIMEOUT:
return put_user(heartbeat, p); return put_user(heartbeat, p);
default: default:
......
...@@ -9,10 +9,7 @@ ...@@ -9,10 +9,7 @@
* 2 of the License, or (at your option) any later version. * 2 of the License, or (at your option) any later version.
*/ */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #define DRV_NAME "xen_wdt"
#define DRV_NAME "wdt"
#define DRV_VERSION "0.01"
#include <linux/bug.h> #include <linux/bug.h>
#include <linux/errno.h> #include <linux/errno.h>
...@@ -21,25 +18,20 @@ ...@@ -21,25 +18,20 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/ktime.h> #include <linux/ktime.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/moduleparam.h> #include <linux/moduleparam.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/spinlock.h>
#include <linux/uaccess.h>
#include <linux/watchdog.h> #include <linux/watchdog.h>
#include <xen/xen.h> #include <xen/xen.h>
#include <asm/xen/hypercall.h> #include <asm/xen/hypercall.h>
#include <xen/interface/sched.h> #include <xen/interface/sched.h>
static struct platform_device *platform_device; static struct platform_device *platform_device;
static DEFINE_SPINLOCK(wdt_lock);
static struct sched_watchdog wdt; static struct sched_watchdog wdt;
static __kernel_time_t wdt_expires; static time64_t wdt_expires;
static bool is_active, expect_release;
#define WATCHDOG_TIMEOUT 60 /* in seconds */ #define WATCHDOG_TIMEOUT 60 /* in seconds */
static unsigned int timeout = WATCHDOG_TIMEOUT; static unsigned int timeout;
module_param(timeout, uint, S_IRUGO); module_param(timeout, uint, S_IRUGO);
MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds " MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds "
"(default=" __MODULE_STRING(WATCHDOG_TIMEOUT) ")"); "(default=" __MODULE_STRING(WATCHDOG_TIMEOUT) ")");
...@@ -49,20 +41,18 @@ module_param(nowayout, bool, S_IRUGO); ...@@ -49,20 +41,18 @@ module_param(nowayout, bool, S_IRUGO);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started " MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
"(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
static inline __kernel_time_t set_timeout(void) static inline time64_t set_timeout(struct watchdog_device *wdd)
{ {
wdt.timeout = timeout; wdt.timeout = wdd->timeout;
return ktime_to_timespec(ktime_get()).tv_sec + timeout; return ktime_get_seconds() + wdd->timeout;
} }
static int xen_wdt_start(void) static int xen_wdt_start(struct watchdog_device *wdd)
{ {
__kernel_time_t expires; time64_t expires;
int err; int err;
spin_lock(&wdt_lock); expires = set_timeout(wdd);
expires = set_timeout();
if (!wdt.id) if (!wdt.id)
err = HYPERVISOR_sched_op(SCHEDOP_watchdog, &wdt); err = HYPERVISOR_sched_op(SCHEDOP_watchdog, &wdt);
else else
...@@ -74,36 +64,28 @@ static int xen_wdt_start(void) ...@@ -74,36 +64,28 @@ static int xen_wdt_start(void)
} else } else
BUG_ON(!err); BUG_ON(!err);
spin_unlock(&wdt_lock);
return err; return err;
} }
static int xen_wdt_stop(void) static int xen_wdt_stop(struct watchdog_device *wdd)
{ {
int err = 0; int err = 0;
spin_lock(&wdt_lock);
wdt.timeout = 0; wdt.timeout = 0;
if (wdt.id) if (wdt.id)
err = HYPERVISOR_sched_op(SCHEDOP_watchdog, &wdt); err = HYPERVISOR_sched_op(SCHEDOP_watchdog, &wdt);
if (!err) if (!err)
wdt.id = 0; wdt.id = 0;
spin_unlock(&wdt_lock);
return err; return err;
} }
static int xen_wdt_kick(void) static int xen_wdt_kick(struct watchdog_device *wdd)
{ {
__kernel_time_t expires; time64_t expires;
int err; int err;
spin_lock(&wdt_lock); expires = set_timeout(wdd);
expires = set_timeout();
if (wdt.id) if (wdt.id)
err = HYPERVISOR_sched_op(SCHEDOP_watchdog, &wdt); err = HYPERVISOR_sched_op(SCHEDOP_watchdog, &wdt);
else else
...@@ -111,195 +93,72 @@ static int xen_wdt_kick(void) ...@@ -111,195 +93,72 @@ static int xen_wdt_kick(void)
if (!err) if (!err)
wdt_expires = expires; wdt_expires = expires;
spin_unlock(&wdt_lock);
return err;
}
static int xen_wdt_open(struct inode *inode, struct file *file)
{
int err;
/* /dev/watchdog can only be opened once */
if (xchg(&is_active, true))
return -EBUSY;
err = xen_wdt_start();
if (err == -EBUSY)
err = xen_wdt_kick();
return err ?: nonseekable_open(inode, file);
}
static int xen_wdt_release(struct inode *inode, struct file *file)
{
int err = 0;
if (expect_release)
err = xen_wdt_stop();
else {
pr_crit("unexpected close, not stopping watchdog!\n");
xen_wdt_kick();
}
is_active = err;
expect_release = false;
return err; return err;
} }
static ssize_t xen_wdt_write(struct file *file, const char __user *data, static unsigned int xen_wdt_get_timeleft(struct watchdog_device *wdd)
size_t len, loff_t *ppos)
{ {
/* See if we got the magic character 'V' and reload the timer */ return wdt_expires - ktime_get_seconds();
if (len) {
if (!nowayout) {
size_t i;
/* in case it was set long ago */
expect_release = false;
/* scan to see whether or not we got the magic
character */
for (i = 0; i != len; i++) {
char c;
if (get_user(c, data + i))
return -EFAULT;
if (c == 'V')
expect_release = true;
}
}
/* someone wrote to us, we should reload the timer */
xen_wdt_kick();
}
return len;
} }
static long xen_wdt_ioctl(struct file *file, unsigned int cmd, static struct watchdog_info xen_wdt_info = {
unsigned long arg)
{
int new_options, retval = -EINVAL;
int new_timeout;
int __user *argp = (void __user *)arg;
static const struct watchdog_info ident = {
.options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE,
.firmware_version = 0,
.identity = DRV_NAME, .identity = DRV_NAME,
}; .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
};
switch (cmd) {
case WDIOC_GETSUPPORT:
return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
case WDIOC_GETSTATUS:
case WDIOC_GETBOOTSTATUS:
return put_user(0, argp);
case WDIOC_SETOPTIONS:
if (get_user(new_options, argp))
return -EFAULT;
if (new_options & WDIOS_DISABLECARD)
retval = xen_wdt_stop();
if (new_options & WDIOS_ENABLECARD) {
retval = xen_wdt_start();
if (retval == -EBUSY)
retval = xen_wdt_kick();
}
return retval;
case WDIOC_KEEPALIVE:
xen_wdt_kick();
return 0;
case WDIOC_SETTIMEOUT:
if (get_user(new_timeout, argp))
return -EFAULT;
if (!new_timeout)
return -EINVAL;
timeout = new_timeout;
xen_wdt_kick();
/* fall through */
case WDIOC_GETTIMEOUT:
return put_user(timeout, argp);
case WDIOC_GETTIMELEFT:
retval = wdt_expires - ktime_to_timespec(ktime_get()).tv_sec;
return put_user(retval, argp);
}
return -ENOTTY;
}
static const struct file_operations xen_wdt_fops = { static const struct watchdog_ops xen_wdt_ops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.llseek = no_llseek, .start = xen_wdt_start,
.write = xen_wdt_write, .stop = xen_wdt_stop,
.unlocked_ioctl = xen_wdt_ioctl, .ping = xen_wdt_kick,
.open = xen_wdt_open, .get_timeleft = xen_wdt_get_timeleft,
.release = xen_wdt_release,
}; };
static struct miscdevice xen_wdt_miscdev = { static struct watchdog_device xen_wdt_dev = {
.minor = WATCHDOG_MINOR, .info = &xen_wdt_info,
.name = "watchdog", .ops = &xen_wdt_ops,
.fops = &xen_wdt_fops, .timeout = WATCHDOG_TIMEOUT,
}; };
static int xen_wdt_probe(struct platform_device *dev) static int xen_wdt_probe(struct platform_device *pdev)
{ {
struct sched_watchdog wd = { .id = ~0 }; struct sched_watchdog wd = { .id = ~0 };
int ret = HYPERVISOR_sched_op(SCHEDOP_watchdog, &wd); int ret = HYPERVISOR_sched_op(SCHEDOP_watchdog, &wd);
switch (ret) { if (ret == -ENOSYS) {
case -EINVAL: dev_err(&pdev->dev, "watchdog not supported by hypervisor\n");
if (!timeout) { return -ENODEV;
timeout = WATCHDOG_TIMEOUT;
pr_info("timeout value invalid, using %d\n", timeout);
} }
ret = misc_register(&xen_wdt_miscdev); if (ret != -EINVAL) {
if (ret) { dev_err(&pdev->dev, "unexpected hypervisor error (%d)\n", ret);
pr_err("cannot register miscdev on minor=%d (%d)\n", return -ENODEV;
WATCHDOG_MINOR, ret);
break;
} }
pr_info("initialized (timeout=%ds, nowayout=%d)\n", if (watchdog_init_timeout(&xen_wdt_dev, timeout, NULL))
timeout, nowayout); dev_info(&pdev->dev, "timeout value invalid, using %d\n",
break; xen_wdt_dev.timeout);
watchdog_set_nowayout(&xen_wdt_dev, nowayout);
case -ENOSYS: watchdog_stop_on_reboot(&xen_wdt_dev);
pr_info("not supported\n"); watchdog_stop_on_unregister(&xen_wdt_dev);
ret = -ENODEV;
break;
default:
pr_info("bogus return value %d\n", ret);
break;
}
ret = devm_watchdog_register_device(&pdev->dev, &xen_wdt_dev);
if (ret) {
dev_err(&pdev->dev, "cannot register watchdog device (%d)\n",
ret);
return ret; return ret;
} }
static int xen_wdt_remove(struct platform_device *dev)
{
/* Stop the timer before we leave */
if (!nowayout)
xen_wdt_stop();
misc_deregister(&xen_wdt_miscdev); dev_info(&pdev->dev, "initialized (timeout=%ds, nowayout=%d)\n",
xen_wdt_dev.timeout, nowayout);
return 0; return 0;
} }
static void xen_wdt_shutdown(struct platform_device *dev)
{
xen_wdt_stop();
}
static int xen_wdt_suspend(struct platform_device *dev, pm_message_t state) static int xen_wdt_suspend(struct platform_device *dev, pm_message_t state)
{ {
typeof(wdt.id) id = wdt.id; typeof(wdt.id) id = wdt.id;
int rc = xen_wdt_stop(); int rc = xen_wdt_stop(&xen_wdt_dev);
wdt.id = id; wdt.id = id;
return rc; return rc;
...@@ -310,13 +169,11 @@ static int xen_wdt_resume(struct platform_device *dev) ...@@ -310,13 +169,11 @@ static int xen_wdt_resume(struct platform_device *dev)
if (!wdt.id) if (!wdt.id)
return 0; return 0;
wdt.id = 0; wdt.id = 0;
return xen_wdt_start(); return xen_wdt_start(&xen_wdt_dev);
} }
static struct platform_driver xen_wdt_driver = { static struct platform_driver xen_wdt_driver = {
.probe = xen_wdt_probe, .probe = xen_wdt_probe,
.remove = xen_wdt_remove,
.shutdown = xen_wdt_shutdown,
.suspend = xen_wdt_suspend, .suspend = xen_wdt_suspend,
.resume = xen_wdt_resume, .resume = xen_wdt_resume,
.driver = { .driver = {
...@@ -331,8 +188,6 @@ static int __init xen_wdt_init_module(void) ...@@ -331,8 +188,6 @@ static int __init xen_wdt_init_module(void)
if (!xen_domain()) if (!xen_domain())
return -ENODEV; return -ENODEV;
pr_info("Xen WatchDog Timer Driver v%s\n", DRV_VERSION);
err = platform_driver_register(&xen_wdt_driver); err = platform_driver_register(&xen_wdt_driver);
if (err) if (err)
return err; return err;
...@@ -351,7 +206,6 @@ static void __exit xen_wdt_cleanup_module(void) ...@@ -351,7 +206,6 @@ static void __exit xen_wdt_cleanup_module(void)
{ {
platform_device_unregister(platform_device); platform_device_unregister(platform_device);
platform_driver_unregister(&xen_wdt_driver); platform_driver_unregister(&xen_wdt_driver);
pr_info("module unloaded\n");
} }
module_init(xen_wdt_init_module); module_init(xen_wdt_init_module);
...@@ -359,5 +213,4 @@ module_exit(xen_wdt_cleanup_module); ...@@ -359,5 +213,4 @@ module_exit(xen_wdt_cleanup_module);
MODULE_AUTHOR("Jan Beulich <jbeulich@novell.com>"); MODULE_AUTHOR("Jan Beulich <jbeulich@novell.com>");
MODULE_DESCRIPTION("Xen WatchDog Timer Driver"); MODULE_DESCRIPTION("Xen WatchDog Timer Driver");
MODULE_VERSION(DRV_VERSION);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
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