Commit 4a08fe57 authored by Linus Torvalds's avatar Linus Torvalds

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

Pull watchdog updates from Wim Van Sebroeck:

 - support for NCT6116D

 - several small fixes and improvements

* tag 'linux-watchdog-5.5-rc1' of git://www.linux-watchdog.org/linux-watchdog: (24 commits)
  watchdog: jz4740: Drop dependency on MACH_JZ47xx
  watchdog: jz4740: Use regmap provided by TCU driver
  watchdog: jz4740: Use WDT clock provided by TCU driver
  dt-bindings: watchdog: sama5d4_wdt: add microchip,sam9x60-wdt compatible
  watchdog: sama5d4_wdt: cleanup the bit definitions
  watchdog: sprd: Fix the incorrect pointer getting from driver data
  watchdog: aspeed: Fix clock behaviour for ast2600
  watchdog: imx7ulp: Fix reboot hang
  watchdog: make nowayout sysfs file writable
  watchdog: prevent deferral of watchdogd wakeup on RT
  watchdog: imx7ulp: Use definitions instead of magic values
  watchdog: imx7ulp: Remove inline annotations
  watchdog: imx7ulp: Remove unused structure member
  watchdog: imx7ulp: Pass the wdog instance inimx7ulp_wdt_enable()
  watchdog: wdat_wdt: Spelling s/configrable/configurable/
  watchdog: bd70528: Trivial function documentation fix
  watchdog: cadence: Do not show error in case of deferred probe
  watchdog: Fix the race between the release of watchdog_core_data and cdev
  watchdog: sbc7240_wdt: Fix yet another -Wimplicit-fallthrough warning
  watchdog: intel-mid_wdt: Add WATCHDOG_NOWAYOUT support
  ...
parents 99a0d9f5 33c26ab4
...@@ -17,8 +17,13 @@ What: /sys/class/watchdog/watchdogn/nowayout ...@@ -17,8 +17,13 @@ What: /sys/class/watchdog/watchdogn/nowayout
Date: August 2015 Date: August 2015
Contact: Wim Van Sebroeck <wim@iguana.be> Contact: Wim Van Sebroeck <wim@iguana.be>
Description: Description:
It is a read only file. While reading, it gives '1' if that It is a read/write file. While reading, it gives '1'
device supports nowayout feature else, it gives '0'. if the device has the nowayout feature set, otherwise
it gives '0'. Writing a '1' to the file enables the
nowayout feature. Once set, the nowayout feature
cannot be disabled, so writing a '0' either has no
effect (if the feature was already disabled) or
results in a permission error.
What: /sys/class/watchdog/watchdogn/state What: /sys/class/watchdog/watchdogn/state
Date: August 2015 Date: August 2015
......
* Atmel SAMA5D4 Watchdog Timer (WDT) Controller * Atmel SAMA5D4 Watchdog Timer (WDT) Controller
Required properties: Required properties:
- compatible: "atmel,sama5d4-wdt" - compatible: "atmel,sama5d4-wdt" or "microchip,sam9x60-wdt"
- reg: base physical address and length of memory mapped region. - reg: base physical address and length of memory mapped region.
Optional properties: Optional properties:
......
...@@ -1485,6 +1485,7 @@ config W83627HF_WDT ...@@ -1485,6 +1485,7 @@ config W83627HF_WDT
NCT6791 NCT6791
NCT6792 NCT6792
NCT6102D/04D/06D NCT6102D/04D/06D
NCT6116D
This watchdog simply watches your kernel to make sure it doesn't This watchdog simply watches your kernel to make sure it doesn't
freeze, and if it does, it reboots your computer after a certain freeze, and if it does, it reboots your computer after a certain
...@@ -1641,8 +1642,10 @@ config INDYDOG ...@@ -1641,8 +1642,10 @@ config INDYDOG
config JZ4740_WDT config JZ4740_WDT
tristate "Ingenic jz4740 SoC hardware watchdog" tristate "Ingenic jz4740 SoC hardware watchdog"
depends on MACH_JZ4740 || MACH_JZ4780 depends on MIPS
depends on COMMON_CLK
select WATCHDOG_CORE select WATCHDOG_CORE
select MFD_SYSCON
help help
Hardware driver for the built-in watchdog timer on Ingenic jz4740 SoCs. Hardware driver for the built-in watchdog timer on Ingenic jz4740 SoCs.
......
...@@ -258,11 +258,6 @@ static int aspeed_wdt_probe(struct platform_device *pdev) ...@@ -258,11 +258,6 @@ static int aspeed_wdt_probe(struct platform_device *pdev)
if (IS_ERR(wdt->base)) if (IS_ERR(wdt->base))
return PTR_ERR(wdt->base); return PTR_ERR(wdt->base);
/*
* The ast2400 wdt can run at PCLK, or 1MHz. The ast2500 only
* runs at 1MHz. We chose to always run at 1MHz, as there's no
* good reason to have a faster watchdog counter.
*/
wdt->wdd.info = &aspeed_wdt_info; wdt->wdd.info = &aspeed_wdt_info;
wdt->wdd.ops = &aspeed_wdt_ops; wdt->wdd.ops = &aspeed_wdt_ops;
wdt->wdd.max_hw_heartbeat_ms = WDT_MAX_TIMEOUT_MS; wdt->wdd.max_hw_heartbeat_ms = WDT_MAX_TIMEOUT_MS;
...@@ -278,7 +273,16 @@ static int aspeed_wdt_probe(struct platform_device *pdev) ...@@ -278,7 +273,16 @@ static int aspeed_wdt_probe(struct platform_device *pdev)
return -EINVAL; return -EINVAL;
config = ofdid->data; config = ofdid->data;
wdt->ctrl = WDT_CTRL_1MHZ_CLK; /*
* On clock rates:
* - ast2400 wdt can run at PCLK, or 1MHz
* - ast2500 only runs at 1MHz, hard coding bit 4 to 1
* - ast2600 always runs at 1MHz
*
* Set the ast2400 to run at 1MHz as it simplifies the driver.
*/
if (of_device_is_compatible(np, "aspeed,ast2400-wdt"))
wdt->ctrl = WDT_CTRL_1MHZ_CLK;
/* /*
* Control reset on a per-device basis to ensure the * Control reset on a per-device basis to ensure the
......
...@@ -4,33 +4,37 @@ ...@@ -4,33 +4,37 @@
* *
* Copyright (C) 2007 Andrew Victor * Copyright (C) 2007 Andrew Victor
* Copyright (C) 2007 Atmel Corporation. * Copyright (C) 2007 Atmel Corporation.
* Copyright (C) 2019 Microchip Technology Inc. and its subsidiaries
* *
* Watchdog Timer (WDT) - System peripherals regsters. * Watchdog Timer (WDT) - System peripherals regsters.
* Based on AT91SAM9261 datasheet revision D. * Based on AT91SAM9261 datasheet revision D.
* Based on SAM9X60 datasheet.
* *
*/ */
#ifndef AT91_WDT_H #ifndef AT91_WDT_H
#define AT91_WDT_H #define AT91_WDT_H
#include <linux/bits.h>
#define AT91_WDT_CR 0x00 /* Watchdog Control Register */ #define AT91_WDT_CR 0x00 /* Watchdog Control Register */
#define AT91_WDT_WDRSTT (1 << 0) /* Restart */ #define AT91_WDT_WDRSTT BIT(0) /* Restart */
#define AT91_WDT_KEY (0xa5 << 24) /* KEY Password */ #define AT91_WDT_KEY (0xa5UL << 24) /* KEY Password */
#define AT91_WDT_MR 0x04 /* Watchdog Mode Register */ #define AT91_WDT_MR 0x04 /* Watchdog Mode Register */
#define AT91_WDT_WDV (0xfff << 0) /* Counter Value */ #define AT91_WDT_WDV (0xfffUL << 0) /* Counter Value */
#define AT91_WDT_SET_WDV(x) ((x) & AT91_WDT_WDV) #define AT91_WDT_SET_WDV(x) ((x) & AT91_WDT_WDV)
#define AT91_WDT_WDFIEN (1 << 12) /* Fault Interrupt Enable */ #define AT91_WDT_WDFIEN BIT(12) /* Fault Interrupt Enable */
#define AT91_WDT_WDRSTEN (1 << 13) /* Reset Processor */ #define AT91_WDT_WDRSTEN BIT(13) /* Reset Processor */
#define AT91_WDT_WDRPROC (1 << 14) /* Timer Restart */ #define AT91_WDT_WDRPROC BIT(14) /* Timer Restart */
#define AT91_WDT_WDDIS (1 << 15) /* Watchdog Disable */ #define AT91_WDT_WDDIS BIT(15) /* Watchdog Disable */
#define AT91_WDT_WDD (0xfff << 16) /* Delta Value */ #define AT91_WDT_WDD (0xfffUL << 16) /* Delta Value */
#define AT91_WDT_SET_WDD(x) (((x) << 16) & AT91_WDT_WDD) #define AT91_WDT_SET_WDD(x) (((x) << 16) & AT91_WDT_WDD)
#define AT91_WDT_WDDBGHLT (1 << 28) /* Debug Halt */ #define AT91_WDT_WDDBGHLT BIT(28) /* Debug Halt */
#define AT91_WDT_WDIDLEHLT (1 << 29) /* Idle Halt */ #define AT91_WDT_WDIDLEHLT BIT(29) /* Idle Halt */
#define AT91_WDT_SR 0x08 /* Watchdog Status Register */ #define AT91_WDT_SR 0x08 /* Watchdog Status Register */
#define AT91_WDT_WDUNF (1 << 0) /* Watchdog Underflow */ #define AT91_WDT_WDUNF BIT(0) /* Watchdog Underflow */
#define AT91_WDT_WDERR (1 << 1) /* Watchdog Error */ #define AT91_WDT_WDERR BIT(1) /* Watchdog Error */
#endif #endif
...@@ -97,7 +97,7 @@ EXPORT_SYMBOL(bd70528_wdt_set); ...@@ -97,7 +97,7 @@ EXPORT_SYMBOL(bd70528_wdt_set);
/** /**
* bd70528_wdt_lock - take WDT lock * bd70528_wdt_lock - take WDT lock
* *
* @bd70528: device data for the PMIC instance we want to operate on * @data: device data for the PMIC instance we want to operate on
* *
* Lock WDT for arming/disarming in order to avoid race condition caused * Lock WDT for arming/disarming in order to avoid race condition caused
* by WDT state changes initiated by WDT and RTC drivers. * by WDT state changes initiated by WDT and RTC drivers.
...@@ -114,7 +114,7 @@ EXPORT_SYMBOL(bd70528_wdt_lock); ...@@ -114,7 +114,7 @@ EXPORT_SYMBOL(bd70528_wdt_lock);
/** /**
* bd70528_wdt_unlock - unlock WDT lock * bd70528_wdt_unlock - unlock WDT lock
* *
* @bd70528: device data for the PMIC instance we want to operate on * @data: device data for the PMIC instance we want to operate on
* *
* Unlock WDT lock which has previously been taken by call to * Unlock WDT lock which has previously been taken by call to
* bd70528_wdt_lock. * bd70528_wdt_lock.
......
...@@ -335,8 +335,10 @@ static int cdns_wdt_probe(struct platform_device *pdev) ...@@ -335,8 +335,10 @@ static int cdns_wdt_probe(struct platform_device *pdev)
wdt->clk = devm_clk_get(dev, NULL); wdt->clk = devm_clk_get(dev, NULL);
if (IS_ERR(wdt->clk)) { if (IS_ERR(wdt->clk)) {
dev_err(dev, "input clock not found\n"); ret = PTR_ERR(wdt->clk);
return PTR_ERR(wdt->clk); if (ret != -EPROBE_DEFER)
dev_err(dev, "input clock not found\n");
return ret;
} }
ret = clk_prepare_enable(wdt->clk); ret = clk_prepare_enable(wdt->clk);
......
...@@ -72,7 +72,6 @@ module_param(nowayout, bool, 0); ...@@ -72,7 +72,6 @@ module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
static unsigned timeout; static unsigned timeout;
module_param(timeout, uint, 0); module_param(timeout, uint, 0);
MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds (default=" MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds (default="
...@@ -247,13 +246,14 @@ static const struct regmap_config imx2_wdt_regmap_config = { ...@@ -247,13 +246,14 @@ static const struct regmap_config imx2_wdt_regmap_config = {
static int __init imx2_wdt_probe(struct platform_device *pdev) static int __init imx2_wdt_probe(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev;
struct imx2_wdt_device *wdev; struct imx2_wdt_device *wdev;
struct watchdog_device *wdog; struct watchdog_device *wdog;
void __iomem *base; void __iomem *base;
int ret; int ret;
u32 val; u32 val;
wdev = devm_kzalloc(&pdev->dev, sizeof(*wdev), GFP_KERNEL); wdev = devm_kzalloc(dev, sizeof(*wdev), GFP_KERNEL);
if (!wdev) if (!wdev)
return -ENOMEM; return -ENOMEM;
...@@ -261,16 +261,16 @@ static int __init imx2_wdt_probe(struct platform_device *pdev) ...@@ -261,16 +261,16 @@ static int __init imx2_wdt_probe(struct platform_device *pdev)
if (IS_ERR(base)) if (IS_ERR(base))
return PTR_ERR(base); return PTR_ERR(base);
wdev->regmap = devm_regmap_init_mmio_clk(&pdev->dev, NULL, base, wdev->regmap = devm_regmap_init_mmio_clk(dev, NULL, base,
&imx2_wdt_regmap_config); &imx2_wdt_regmap_config);
if (IS_ERR(wdev->regmap)) { if (IS_ERR(wdev->regmap)) {
dev_err(&pdev->dev, "regmap init failed\n"); dev_err(dev, "regmap init failed\n");
return PTR_ERR(wdev->regmap); return PTR_ERR(wdev->regmap);
} }
wdev->clk = devm_clk_get(&pdev->dev, NULL); wdev->clk = devm_clk_get(dev, NULL);
if (IS_ERR(wdev->clk)) { if (IS_ERR(wdev->clk)) {
dev_err(&pdev->dev, "can't get Watchdog clock\n"); dev_err(dev, "can't get Watchdog clock\n");
return PTR_ERR(wdev->clk); return PTR_ERR(wdev->clk);
} }
...@@ -280,12 +280,12 @@ static int __init imx2_wdt_probe(struct platform_device *pdev) ...@@ -280,12 +280,12 @@ static int __init imx2_wdt_probe(struct platform_device *pdev)
wdog->min_timeout = 1; wdog->min_timeout = 1;
wdog->timeout = IMX2_WDT_DEFAULT_TIME; wdog->timeout = IMX2_WDT_DEFAULT_TIME;
wdog->max_hw_heartbeat_ms = IMX2_WDT_MAX_TIME * 1000; wdog->max_hw_heartbeat_ms = IMX2_WDT_MAX_TIME * 1000;
wdog->parent = &pdev->dev; wdog->parent = dev;
ret = platform_get_irq(pdev, 0); ret = platform_get_irq(pdev, 0);
if (ret > 0) if (ret > 0)
if (!devm_request_irq(&pdev->dev, ret, imx2_wdt_isr, 0, if (!devm_request_irq(dev, ret, imx2_wdt_isr, 0,
dev_name(&pdev->dev), wdog)) dev_name(dev), wdog))
wdog->info = &imx2_wdt_pretimeout_info; wdog->info = &imx2_wdt_pretimeout_info;
ret = clk_prepare_enable(wdev->clk); ret = clk_prepare_enable(wdev->clk);
...@@ -295,13 +295,13 @@ static int __init imx2_wdt_probe(struct platform_device *pdev) ...@@ -295,13 +295,13 @@ static int __init imx2_wdt_probe(struct platform_device *pdev)
regmap_read(wdev->regmap, IMX2_WDT_WRSR, &val); regmap_read(wdev->regmap, IMX2_WDT_WRSR, &val);
wdog->bootstatus = val & IMX2_WDT_WRSR_TOUT ? WDIOF_CARDRESET : 0; wdog->bootstatus = val & IMX2_WDT_WRSR_TOUT ? WDIOF_CARDRESET : 0;
wdev->ext_reset = of_property_read_bool(pdev->dev.of_node, wdev->ext_reset = of_property_read_bool(dev->of_node,
"fsl,ext-reset-output"); "fsl,ext-reset-output");
platform_set_drvdata(pdev, wdog); platform_set_drvdata(pdev, wdog);
watchdog_set_drvdata(wdog, wdev); watchdog_set_drvdata(wdog, wdev);
watchdog_set_nowayout(wdog, nowayout); watchdog_set_nowayout(wdog, nowayout);
watchdog_set_restart_priority(wdog, 128); watchdog_set_restart_priority(wdog, 128);
watchdog_init_timeout(wdog, timeout, &pdev->dev); watchdog_init_timeout(wdog, timeout, dev);
if (imx2_wdt_is_running(wdev)) { if (imx2_wdt_is_running(wdev)) {
imx2_wdt_set_timeout(wdog, wdog->timeout); imx2_wdt_set_timeout(wdog, wdog->timeout);
...@@ -319,7 +319,7 @@ static int __init imx2_wdt_probe(struct platform_device *pdev) ...@@ -319,7 +319,7 @@ static int __init imx2_wdt_probe(struct platform_device *pdev)
if (ret) if (ret)
goto disable_clk; goto disable_clk;
dev_info(&pdev->dev, "timeout %d sec (nowayout=%d)\n", dev_info(dev, "timeout %d sec (nowayout=%d)\n",
wdog->timeout, nowayout); wdog->timeout, nowayout);
return 0; return 0;
...@@ -359,9 +359,8 @@ static void imx2_wdt_shutdown(struct platform_device *pdev) ...@@ -359,9 +359,8 @@ static void imx2_wdt_shutdown(struct platform_device *pdev)
} }
} }
#ifdef CONFIG_PM_SLEEP
/* Disable watchdog if it is active or non-active but still running */ /* Disable watchdog if it is active or non-active but still running */
static int imx2_wdt_suspend(struct device *dev) static int __maybe_unused imx2_wdt_suspend(struct device *dev)
{ {
struct watchdog_device *wdog = dev_get_drvdata(dev); struct watchdog_device *wdog = dev_get_drvdata(dev);
struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog); struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog);
...@@ -382,7 +381,7 @@ static int imx2_wdt_suspend(struct device *dev) ...@@ -382,7 +381,7 @@ static int imx2_wdt_suspend(struct device *dev)
} }
/* Enable watchdog and configure it if necessary */ /* Enable watchdog and configure it if necessary */
static int imx2_wdt_resume(struct device *dev) static int __maybe_unused imx2_wdt_resume(struct device *dev)
{ {
struct watchdog_device *wdog = dev_get_drvdata(dev); struct watchdog_device *wdog = dev_get_drvdata(dev);
struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog); struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog);
...@@ -407,7 +406,6 @@ static int imx2_wdt_resume(struct device *dev) ...@@ -407,7 +406,6 @@ static int imx2_wdt_resume(struct device *dev)
return 0; return 0;
} }
#endif
static SIMPLE_DEV_PM_OPS(imx2_wdt_pm_ops, imx2_wdt_suspend, static SIMPLE_DEV_PM_OPS(imx2_wdt_pm_ops, imx2_wdt_suspend,
imx2_wdt_resume); imx2_wdt_resume);
......
...@@ -17,6 +17,9 @@ ...@@ -17,6 +17,9 @@
#define WDOG_CS_CMD32EN BIT(13) #define WDOG_CS_CMD32EN BIT(13)
#define WDOG_CS_ULK BIT(11) #define WDOG_CS_ULK BIT(11)
#define WDOG_CS_RCS BIT(10) #define WDOG_CS_RCS BIT(10)
#define LPO_CLK 0x1
#define LPO_CLK_SHIFT 8
#define WDOG_CS_CLK (LPO_CLK << LPO_CLK_SHIFT)
#define WDOG_CS_EN BIT(7) #define WDOG_CS_EN BIT(7)
#define WDOG_CS_UPDATE BIT(5) #define WDOG_CS_UPDATE BIT(5)
...@@ -41,24 +44,25 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" ...@@ -41,24 +44,25 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
struct imx7ulp_wdt_device { struct imx7ulp_wdt_device {
struct notifier_block restart_handler;
struct watchdog_device wdd; struct watchdog_device wdd;
void __iomem *base; void __iomem *base;
struct clk *clk; struct clk *clk;
}; };
static inline void imx7ulp_wdt_enable(void __iomem *base, bool enable) static void imx7ulp_wdt_enable(struct watchdog_device *wdog, bool enable)
{ {
u32 val = readl(base + WDOG_CS); struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog);
writel(UNLOCK, base + WDOG_CNT); u32 val = readl(wdt->base + WDOG_CS);
writel(UNLOCK, wdt->base + WDOG_CNT);
if (enable) if (enable)
writel(val | WDOG_CS_EN, base + WDOG_CS); writel(val | WDOG_CS_EN, wdt->base + WDOG_CS);
else else
writel(val & ~WDOG_CS_EN, base + WDOG_CS); writel(val & ~WDOG_CS_EN, wdt->base + WDOG_CS);
} }
static inline bool imx7ulp_wdt_is_enabled(void __iomem *base) static bool imx7ulp_wdt_is_enabled(void __iomem *base)
{ {
u32 val = readl(base + WDOG_CS); u32 val = readl(base + WDOG_CS);
...@@ -76,18 +80,15 @@ static int imx7ulp_wdt_ping(struct watchdog_device *wdog) ...@@ -76,18 +80,15 @@ static int imx7ulp_wdt_ping(struct watchdog_device *wdog)
static int imx7ulp_wdt_start(struct watchdog_device *wdog) static int imx7ulp_wdt_start(struct watchdog_device *wdog)
{ {
struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog);
imx7ulp_wdt_enable(wdt->base, true); imx7ulp_wdt_enable(wdog, true);
return 0; return 0;
} }
static int imx7ulp_wdt_stop(struct watchdog_device *wdog) static int imx7ulp_wdt_stop(struct watchdog_device *wdog)
{ {
struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog); imx7ulp_wdt_enable(wdog, false);
imx7ulp_wdt_enable(wdt->base, false);
return 0; return 0;
} }
...@@ -106,12 +107,28 @@ static int imx7ulp_wdt_set_timeout(struct watchdog_device *wdog, ...@@ -106,12 +107,28 @@ static int imx7ulp_wdt_set_timeout(struct watchdog_device *wdog,
return 0; return 0;
} }
static int imx7ulp_wdt_restart(struct watchdog_device *wdog,
unsigned long action, void *data)
{
struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog);
imx7ulp_wdt_enable(wdt->base, true);
imx7ulp_wdt_set_timeout(&wdt->wdd, 1);
/* wait for wdog to fire */
while (true)
;
return NOTIFY_DONE;
}
static const struct watchdog_ops imx7ulp_wdt_ops = { static const struct watchdog_ops imx7ulp_wdt_ops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.start = imx7ulp_wdt_start, .start = imx7ulp_wdt_start,
.stop = imx7ulp_wdt_stop, .stop = imx7ulp_wdt_stop,
.ping = imx7ulp_wdt_ping, .ping = imx7ulp_wdt_ping,
.set_timeout = imx7ulp_wdt_set_timeout, .set_timeout = imx7ulp_wdt_set_timeout,
.restart = imx7ulp_wdt_restart,
}; };
static const struct watchdog_info imx7ulp_wdt_info = { static const struct watchdog_info imx7ulp_wdt_info = {
...@@ -120,7 +137,7 @@ static const struct watchdog_info imx7ulp_wdt_info = { ...@@ -120,7 +137,7 @@ static const struct watchdog_info imx7ulp_wdt_info = {
WDIOF_MAGICCLOSE, WDIOF_MAGICCLOSE,
}; };
static inline void imx7ulp_wdt_init(void __iomem *base, unsigned int timeout) static void imx7ulp_wdt_init(void __iomem *base, unsigned int timeout)
{ {
u32 val; u32 val;
...@@ -131,7 +148,7 @@ static inline void imx7ulp_wdt_init(void __iomem *base, unsigned int timeout) ...@@ -131,7 +148,7 @@ static inline void imx7ulp_wdt_init(void __iomem *base, unsigned int timeout)
/* set an initial timeout value in TOVAL */ /* set an initial timeout value in TOVAL */
writel(timeout, base + WDOG_TOVAL); writel(timeout, base + WDOG_TOVAL);
/* enable 32bit command sequence and reconfigure */ /* enable 32bit command sequence and reconfigure */
val = BIT(13) | BIT(8) | BIT(5); val = WDOG_CS_CMD32EN | WDOG_CS_CLK | WDOG_CS_UPDATE;
writel(val, base + WDOG_CS); writel(val, base + WDOG_CS);
} }
......
...@@ -134,6 +134,7 @@ static int mid_wdt_probe(struct platform_device *pdev) ...@@ -134,6 +134,7 @@ static int mid_wdt_probe(struct platform_device *pdev)
wdt_dev->timeout = MID_WDT_DEFAULT_TIMEOUT; wdt_dev->timeout = MID_WDT_DEFAULT_TIMEOUT;
wdt_dev->parent = dev; wdt_dev->parent = dev;
watchdog_set_nowayout(wdt_dev, WATCHDOG_NOWAYOUT);
watchdog_set_drvdata(wdt_dev, dev); watchdog_set_drvdata(wdt_dev, dev);
ret = devm_request_irq(dev, pdata->irq, mid_wdt_irq, ret = devm_request_irq(dev, pdata->irq, mid_wdt_irq,
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
*/ */
#include <linux/mfd/ingenic-tcu.h> #include <linux/mfd/ingenic-tcu.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/moduleparam.h> #include <linux/moduleparam.h>
#include <linux/types.h> #include <linux/types.h>
...@@ -17,19 +18,7 @@ ...@@ -17,19 +18,7 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/regmap.h>
#include <asm/mach-jz4740/timer.h>
#define JZ_WDT_CLOCK_PCLK 0x1
#define JZ_WDT_CLOCK_RTC 0x2
#define JZ_WDT_CLOCK_EXT 0x4
#define JZ_WDT_CLOCK_DIV_1 (0 << TCU_TCSR_PRESCALE_LSB)
#define JZ_WDT_CLOCK_DIV_4 (1 << TCU_TCSR_PRESCALE_LSB)
#define JZ_WDT_CLOCK_DIV_16 (2 << TCU_TCSR_PRESCALE_LSB)
#define JZ_WDT_CLOCK_DIV_64 (3 << TCU_TCSR_PRESCALE_LSB)
#define JZ_WDT_CLOCK_DIV_256 (4 << TCU_TCSR_PRESCALE_LSB)
#define JZ_WDT_CLOCK_DIV_1024 (5 << TCU_TCSR_PRESCALE_LSB)
#define DEFAULT_HEARTBEAT 5 #define DEFAULT_HEARTBEAT 5
#define MAX_HEARTBEAT 2048 #define MAX_HEARTBEAT 2048
...@@ -49,15 +38,17 @@ MODULE_PARM_DESC(heartbeat, ...@@ -49,15 +38,17 @@ MODULE_PARM_DESC(heartbeat,
struct jz4740_wdt_drvdata { struct jz4740_wdt_drvdata {
struct watchdog_device wdt; struct watchdog_device wdt;
void __iomem *base; struct regmap *map;
struct clk *rtc_clk; struct clk *clk;
unsigned long clk_rate;
}; };
static int jz4740_wdt_ping(struct watchdog_device *wdt_dev) static int jz4740_wdt_ping(struct watchdog_device *wdt_dev)
{ {
struct jz4740_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev); struct jz4740_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev);
writew(0x0, drvdata->base + TCU_REG_WDT_TCNT); regmap_write(drvdata->map, TCU_REG_WDT_TCNT, 0);
return 0; return 0;
} }
...@@ -65,35 +56,17 @@ static int jz4740_wdt_set_timeout(struct watchdog_device *wdt_dev, ...@@ -65,35 +56,17 @@ static int jz4740_wdt_set_timeout(struct watchdog_device *wdt_dev,
unsigned int new_timeout) unsigned int new_timeout)
{ {
struct jz4740_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev); struct jz4740_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev);
unsigned int rtc_clk_rate; u16 timeout_value = (u16)(drvdata->clk_rate * new_timeout);
unsigned int timeout_value; unsigned int tcer;
unsigned short clock_div = JZ_WDT_CLOCK_DIV_1;
u8 tcer;
rtc_clk_rate = clk_get_rate(drvdata->rtc_clk);
timeout_value = rtc_clk_rate * new_timeout;
while (timeout_value > 0xffff) {
if (clock_div == JZ_WDT_CLOCK_DIV_1024) {
/* Requested timeout too high;
* use highest possible value. */
timeout_value = 0xffff;
break;
}
timeout_value >>= 2;
clock_div += (1 << TCU_TCSR_PRESCALE_LSB);
}
tcer = readb(drvdata->base + TCU_REG_WDT_TCER); regmap_read(drvdata->map, TCU_REG_WDT_TCER, &tcer);
writeb(0x0, drvdata->base + TCU_REG_WDT_TCER); regmap_write(drvdata->map, TCU_REG_WDT_TCER, 0);
writew(clock_div, drvdata->base + TCU_REG_WDT_TCSR);
writew((u16)timeout_value, drvdata->base + TCU_REG_WDT_TDR); regmap_write(drvdata->map, TCU_REG_WDT_TDR, timeout_value);
writew(0x0, drvdata->base + TCU_REG_WDT_TCNT); regmap_write(drvdata->map, TCU_REG_WDT_TCNT, 0);
writew(clock_div | JZ_WDT_CLOCK_RTC, drvdata->base + TCU_REG_WDT_TCSR);
if (tcer & TCU_WDT_TCER_TCEN) if (tcer & TCU_WDT_TCER_TCEN)
writeb(TCU_WDT_TCER_TCEN, drvdata->base + TCU_REG_WDT_TCER); regmap_write(drvdata->map, TCU_REG_WDT_TCER, TCU_WDT_TCER_TCEN);
wdt_dev->timeout = new_timeout; wdt_dev->timeout = new_timeout;
return 0; return 0;
...@@ -102,16 +75,20 @@ static int jz4740_wdt_set_timeout(struct watchdog_device *wdt_dev, ...@@ -102,16 +75,20 @@ static int jz4740_wdt_set_timeout(struct watchdog_device *wdt_dev,
static int jz4740_wdt_start(struct watchdog_device *wdt_dev) static int jz4740_wdt_start(struct watchdog_device *wdt_dev)
{ {
struct jz4740_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev); struct jz4740_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev);
u8 tcer; unsigned int tcer;
int ret;
tcer = readb(drvdata->base + TCU_REG_WDT_TCER); ret = clk_prepare_enable(drvdata->clk);
if (ret)
return ret;
regmap_read(drvdata->map, TCU_REG_WDT_TCER, &tcer);
jz4740_timer_enable_watchdog();
jz4740_wdt_set_timeout(wdt_dev, wdt_dev->timeout); jz4740_wdt_set_timeout(wdt_dev, wdt_dev->timeout);
/* Start watchdog if it wasn't started already */ /* Start watchdog if it wasn't started already */
if (!(tcer & TCU_WDT_TCER_TCEN)) if (!(tcer & TCU_WDT_TCER_TCEN))
writeb(TCU_WDT_TCER_TCEN, drvdata->base + TCU_REG_WDT_TCER); regmap_write(drvdata->map, TCU_REG_WDT_TCER, TCU_WDT_TCER_TCEN);
return 0; return 0;
} }
...@@ -120,8 +97,8 @@ static int jz4740_wdt_stop(struct watchdog_device *wdt_dev) ...@@ -120,8 +97,8 @@ static int jz4740_wdt_stop(struct watchdog_device *wdt_dev)
{ {
struct jz4740_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev); struct jz4740_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev);
writeb(0x0, drvdata->base + TCU_REG_WDT_TCER); regmap_write(drvdata->map, TCU_REG_WDT_TCER, 0);
jz4740_timer_disable_watchdog(); clk_disable_unprepare(drvdata->clk);
return 0; return 0;
} }
...@@ -162,33 +139,46 @@ static int jz4740_wdt_probe(struct platform_device *pdev) ...@@ -162,33 +139,46 @@ static int jz4740_wdt_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct jz4740_wdt_drvdata *drvdata; struct jz4740_wdt_drvdata *drvdata;
struct watchdog_device *jz4740_wdt; struct watchdog_device *jz4740_wdt;
long rate;
int ret;
drvdata = devm_kzalloc(dev, sizeof(struct jz4740_wdt_drvdata), drvdata = devm_kzalloc(dev, sizeof(struct jz4740_wdt_drvdata),
GFP_KERNEL); GFP_KERNEL);
if (!drvdata) if (!drvdata)
return -ENOMEM; return -ENOMEM;
if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT) drvdata->clk = devm_clk_get(&pdev->dev, "wdt");
heartbeat = DEFAULT_HEARTBEAT; if (IS_ERR(drvdata->clk)) {
dev_err(&pdev->dev, "cannot find WDT clock\n");
return PTR_ERR(drvdata->clk);
}
/* Set smallest clock possible */
rate = clk_round_rate(drvdata->clk, 1);
if (rate < 0)
return rate;
ret = clk_set_rate(drvdata->clk, rate);
if (ret)
return ret;
drvdata->clk_rate = rate;
jz4740_wdt = &drvdata->wdt; jz4740_wdt = &drvdata->wdt;
jz4740_wdt->info = &jz4740_wdt_info; jz4740_wdt->info = &jz4740_wdt_info;
jz4740_wdt->ops = &jz4740_wdt_ops; jz4740_wdt->ops = &jz4740_wdt_ops;
jz4740_wdt->timeout = heartbeat;
jz4740_wdt->min_timeout = 1; jz4740_wdt->min_timeout = 1;
jz4740_wdt->max_timeout = MAX_HEARTBEAT; jz4740_wdt->max_timeout = 0xffff / rate;
jz4740_wdt->timeout = clamp(heartbeat,
jz4740_wdt->min_timeout,
jz4740_wdt->max_timeout);
jz4740_wdt->parent = dev; jz4740_wdt->parent = dev;
watchdog_set_nowayout(jz4740_wdt, nowayout); watchdog_set_nowayout(jz4740_wdt, nowayout);
watchdog_set_drvdata(jz4740_wdt, drvdata); watchdog_set_drvdata(jz4740_wdt, drvdata);
drvdata->base = devm_platform_ioremap_resource(pdev, 0); drvdata->map = device_node_to_regmap(dev->parent->of_node);
if (IS_ERR(drvdata->base)) if (!drvdata->map) {
return PTR_ERR(drvdata->base); dev_err(dev, "regmap not found\n");
return -EINVAL;
drvdata->rtc_clk = devm_clk_get(dev, "rtc");
if (IS_ERR(drvdata->rtc_clk)) {
dev_err(dev, "cannot find RTC clock\n");
return PTR_ERR(drvdata->rtc_clk);
} }
return devm_watchdog_register_device(dev, &drvdata->wdt); return devm_watchdog_register_device(dev, &drvdata->wdt);
......
...@@ -194,9 +194,8 @@ static long fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ...@@ -194,9 +194,8 @@ static long fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
if (wdt_set_timeout(new_timeout)) if (wdt_set_timeout(new_timeout))
return -EINVAL; return -EINVAL;
/* Fall through */
} }
/* Fall through */
case WDIOC_GETTIMEOUT: case WDIOC_GETTIMEOUT:
return put_user(timeout, (int __user *)arg); return put_user(timeout, (int __user *)arg);
default: default:
......
...@@ -327,10 +327,9 @@ static int sprd_wdt_probe(struct platform_device *pdev) ...@@ -327,10 +327,9 @@ static int sprd_wdt_probe(struct platform_device *pdev)
static int __maybe_unused sprd_wdt_pm_suspend(struct device *dev) static int __maybe_unused sprd_wdt_pm_suspend(struct device *dev)
{ {
struct watchdog_device *wdd = dev_get_drvdata(dev);
struct sprd_wdt *wdt = dev_get_drvdata(dev); struct sprd_wdt *wdt = dev_get_drvdata(dev);
if (watchdog_active(wdd)) if (watchdog_active(&wdt->wdd))
sprd_wdt_stop(&wdt->wdd); sprd_wdt_stop(&wdt->wdd);
sprd_wdt_disable(wdt); sprd_wdt_disable(wdt);
...@@ -339,7 +338,6 @@ static int __maybe_unused sprd_wdt_pm_suspend(struct device *dev) ...@@ -339,7 +338,6 @@ static int __maybe_unused sprd_wdt_pm_suspend(struct device *dev)
static int __maybe_unused sprd_wdt_pm_resume(struct device *dev) static int __maybe_unused sprd_wdt_pm_resume(struct device *dev)
{ {
struct watchdog_device *wdd = dev_get_drvdata(dev);
struct sprd_wdt *wdt = dev_get_drvdata(dev); struct sprd_wdt *wdt = dev_get_drvdata(dev);
int ret; int ret;
...@@ -347,7 +345,7 @@ static int __maybe_unused sprd_wdt_pm_resume(struct device *dev) ...@@ -347,7 +345,7 @@ static int __maybe_unused sprd_wdt_pm_resume(struct device *dev)
if (ret) if (ret)
return ret; return ret;
if (watchdog_active(wdd)) { if (watchdog_active(&wdt->wdd)) {
ret = sprd_wdt_start(&wdt->wdd); ret = sprd_wdt_start(&wdt->wdd);
if (ret) { if (ret) {
sprd_wdt_disable(wdt); sprd_wdt_disable(wdt);
......
...@@ -49,7 +49,7 @@ static int wdt_cfg_leave = 0xAA;/* key to lock configuration space */ ...@@ -49,7 +49,7 @@ static int wdt_cfg_leave = 0xAA;/* key to lock configuration space */
enum chips { w83627hf, w83627s, w83697hf, w83697ug, w83637hf, w83627thf, enum chips { w83627hf, w83627s, w83697hf, w83697ug, w83637hf, w83627thf,
w83687thf, w83627ehf, w83627dhg, w83627uhg, w83667hg, w83627dhg_p, w83687thf, w83627ehf, w83627dhg, w83627uhg, w83667hg, w83627dhg_p,
w83667hg_b, nct6775, nct6776, nct6779, nct6791, nct6792, nct6793, w83667hg_b, nct6775, nct6776, nct6779, nct6791, nct6792, nct6793,
nct6795, nct6796, nct6102 }; nct6795, nct6796, nct6102, nct6116 };
static int timeout; /* in seconds */ static int timeout; /* in seconds */
module_param(timeout, int, 0); module_param(timeout, int, 0);
...@@ -94,6 +94,7 @@ MODULE_PARM_DESC(early_disable, "Disable watchdog at boot time (default=0)"); ...@@ -94,6 +94,7 @@ MODULE_PARM_DESC(early_disable, "Disable watchdog at boot time (default=0)");
#define NCT6775_ID 0xb4 #define NCT6775_ID 0xb4
#define NCT6776_ID 0xc3 #define NCT6776_ID 0xc3
#define NCT6102_ID 0xc4 #define NCT6102_ID 0xc4
#define NCT6116_ID 0xd2
#define NCT6779_ID 0xc5 #define NCT6779_ID 0xc5
#define NCT6791_ID 0xc8 #define NCT6791_ID 0xc8
#define NCT6792_ID 0xc9 #define NCT6792_ID 0xc9
...@@ -211,6 +212,7 @@ static int w83627hf_init(struct watchdog_device *wdog, enum chips chip) ...@@ -211,6 +212,7 @@ static int w83627hf_init(struct watchdog_device *wdog, enum chips chip)
case nct6795: case nct6795:
case nct6796: case nct6796:
case nct6102: case nct6102:
case nct6116:
/* /*
* These chips have a fixed WDTO# output pin (W83627UHG), * These chips have a fixed WDTO# output pin (W83627UHG),
* or support more than one WDTO# output pin. * or support more than one WDTO# output pin.
...@@ -417,6 +419,12 @@ static int wdt_find(int addr) ...@@ -417,6 +419,12 @@ static int wdt_find(int addr)
cr_wdt_control = NCT6102D_WDT_CONTROL; cr_wdt_control = NCT6102D_WDT_CONTROL;
cr_wdt_csr = NCT6102D_WDT_CSR; cr_wdt_csr = NCT6102D_WDT_CSR;
break; break;
case NCT6116_ID:
ret = nct6102;
cr_wdt_timeout = NCT6102D_WDT_TIMEOUT;
cr_wdt_control = NCT6102D_WDT_CONTROL;
cr_wdt_csr = NCT6102D_WDT_CSR;
break;
case 0xff: case 0xff:
ret = -ENODEV; ret = -ENODEV;
break; break;
...@@ -482,6 +490,7 @@ static int __init wdt_init(void) ...@@ -482,6 +490,7 @@ static int __init wdt_init(void)
"NCT6795", "NCT6795",
"NCT6796", "NCT6796",
"NCT6102", "NCT6102",
"NCT6116",
}; };
/* Apply system-specific quirks */ /* Apply system-specific quirks */
......
...@@ -34,7 +34,6 @@ ...@@ -34,7 +34,6 @@
#include <linux/init.h> /* For __init/__exit/... */ #include <linux/init.h> /* For __init/__exit/... */
#include <linux/hrtimer.h> /* For hrtimers */ #include <linux/hrtimer.h> /* For hrtimers */
#include <linux/kernel.h> /* For printk/panic/... */ #include <linux/kernel.h> /* For printk/panic/... */
#include <linux/kref.h> /* For data references */
#include <linux/kthread.h> /* For kthread_work */ #include <linux/kthread.h> /* For kthread_work */
#include <linux/miscdevice.h> /* For handling misc devices */ #include <linux/miscdevice.h> /* For handling misc devices */
#include <linux/module.h> /* For module stuff/... */ #include <linux/module.h> /* For module stuff/... */
...@@ -52,14 +51,14 @@ ...@@ -52,14 +51,14 @@
/* /*
* struct watchdog_core_data - watchdog core internal data * struct watchdog_core_data - watchdog core internal data
* @kref: Reference count. * @dev: The watchdog's internal device
* @cdev: The watchdog's Character device. * @cdev: The watchdog's Character device.
* @wdd: Pointer to watchdog device. * @wdd: Pointer to watchdog device.
* @lock: Lock for watchdog core. * @lock: Lock for watchdog core.
* @status: Watchdog core internal status bits. * @status: Watchdog core internal status bits.
*/ */
struct watchdog_core_data { struct watchdog_core_data {
struct kref kref; struct device dev;
struct cdev cdev; struct cdev cdev;
struct watchdog_device *wdd; struct watchdog_device *wdd;
struct mutex lock; struct mutex lock;
...@@ -158,7 +157,8 @@ static inline void watchdog_update_worker(struct watchdog_device *wdd) ...@@ -158,7 +157,8 @@ static inline void watchdog_update_worker(struct watchdog_device *wdd)
ktime_t t = watchdog_next_keepalive(wdd); ktime_t t = watchdog_next_keepalive(wdd);
if (t > 0) if (t > 0)
hrtimer_start(&wd_data->timer, t, HRTIMER_MODE_REL); hrtimer_start(&wd_data->timer, t,
HRTIMER_MODE_REL_HARD);
} else { } else {
hrtimer_cancel(&wd_data->timer); hrtimer_cancel(&wd_data->timer);
} }
...@@ -177,7 +177,7 @@ static int __watchdog_ping(struct watchdog_device *wdd) ...@@ -177,7 +177,7 @@ static int __watchdog_ping(struct watchdog_device *wdd)
if (ktime_after(earliest_keepalive, now)) { if (ktime_after(earliest_keepalive, now)) {
hrtimer_start(&wd_data->timer, hrtimer_start(&wd_data->timer,
ktime_sub(earliest_keepalive, now), ktime_sub(earliest_keepalive, now),
HRTIMER_MODE_REL); HRTIMER_MODE_REL_HARD);
return 0; return 0;
} }
...@@ -452,7 +452,26 @@ static ssize_t nowayout_show(struct device *dev, struct device_attribute *attr, ...@@ -452,7 +452,26 @@ static ssize_t nowayout_show(struct device *dev, struct device_attribute *attr,
return sprintf(buf, "%d\n", !!test_bit(WDOG_NO_WAY_OUT, &wdd->status)); return sprintf(buf, "%d\n", !!test_bit(WDOG_NO_WAY_OUT, &wdd->status));
} }
static DEVICE_ATTR_RO(nowayout);
static ssize_t nowayout_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t len)
{
struct watchdog_device *wdd = dev_get_drvdata(dev);
unsigned int value;
int ret;
ret = kstrtouint(buf, 0, &value);
if (ret)
return ret;
if (value > 1)
return -EINVAL;
/* nowayout cannot be disabled once set */
if (test_bit(WDOG_NO_WAY_OUT, &wdd->status) && !value)
return -EPERM;
watchdog_set_nowayout(wdd, value);
return len;
}
static DEVICE_ATTR_RW(nowayout);
static ssize_t status_show(struct device *dev, struct device_attribute *attr, static ssize_t status_show(struct device *dev, struct device_attribute *attr,
char *buf) char *buf)
...@@ -839,7 +858,7 @@ static int watchdog_open(struct inode *inode, struct file *file) ...@@ -839,7 +858,7 @@ static int watchdog_open(struct inode *inode, struct file *file)
file->private_data = wd_data; file->private_data = wd_data;
if (!hw_running) if (!hw_running)
kref_get(&wd_data->kref); get_device(&wd_data->dev);
/* /*
* open_timeout only applies for the first open from * open_timeout only applies for the first open from
...@@ -860,11 +879,11 @@ static int watchdog_open(struct inode *inode, struct file *file) ...@@ -860,11 +879,11 @@ static int watchdog_open(struct inode *inode, struct file *file)
return err; return err;
} }
static void watchdog_core_data_release(struct kref *kref) static void watchdog_core_data_release(struct device *dev)
{ {
struct watchdog_core_data *wd_data; struct watchdog_core_data *wd_data;
wd_data = container_of(kref, struct watchdog_core_data, kref); wd_data = container_of(dev, struct watchdog_core_data, dev);
kfree(wd_data); kfree(wd_data);
} }
...@@ -924,7 +943,7 @@ static int watchdog_release(struct inode *inode, struct file *file) ...@@ -924,7 +943,7 @@ static int watchdog_release(struct inode *inode, struct file *file)
*/ */
if (!running) { if (!running) {
module_put(wd_data->cdev.owner); module_put(wd_data->cdev.owner);
kref_put(&wd_data->kref, watchdog_core_data_release); put_device(&wd_data->dev);
} }
return 0; return 0;
} }
...@@ -944,17 +963,22 @@ static struct miscdevice watchdog_miscdev = { ...@@ -944,17 +963,22 @@ static struct miscdevice watchdog_miscdev = {
.fops = &watchdog_fops, .fops = &watchdog_fops,
}; };
static struct class watchdog_class = {
.name = "watchdog",
.owner = THIS_MODULE,
.dev_groups = wdt_groups,
};
/* /*
* watchdog_cdev_register: register watchdog character device * watchdog_cdev_register: register watchdog character device
* @wdd: watchdog device * @wdd: watchdog device
* @devno: character device number
* *
* Register a watchdog character device including handling the legacy * Register a watchdog character device including handling the legacy
* /dev/watchdog node. /dev/watchdog is actually a miscdevice and * /dev/watchdog node. /dev/watchdog is actually a miscdevice and
* thus we set it up like that. * thus we set it up like that.
*/ */
static int watchdog_cdev_register(struct watchdog_device *wdd, dev_t devno) static int watchdog_cdev_register(struct watchdog_device *wdd)
{ {
struct watchdog_core_data *wd_data; struct watchdog_core_data *wd_data;
int err; int err;
...@@ -962,7 +986,6 @@ static int watchdog_cdev_register(struct watchdog_device *wdd, dev_t devno) ...@@ -962,7 +986,6 @@ static int watchdog_cdev_register(struct watchdog_device *wdd, dev_t devno)
wd_data = kzalloc(sizeof(struct watchdog_core_data), GFP_KERNEL); wd_data = kzalloc(sizeof(struct watchdog_core_data), GFP_KERNEL);
if (!wd_data) if (!wd_data)
return -ENOMEM; return -ENOMEM;
kref_init(&wd_data->kref);
mutex_init(&wd_data->lock); mutex_init(&wd_data->lock);
wd_data->wdd = wdd; wd_data->wdd = wdd;
...@@ -972,7 +995,7 @@ static int watchdog_cdev_register(struct watchdog_device *wdd, dev_t devno) ...@@ -972,7 +995,7 @@ static int watchdog_cdev_register(struct watchdog_device *wdd, dev_t devno)
return -ENODEV; return -ENODEV;
kthread_init_work(&wd_data->work, watchdog_ping_work); kthread_init_work(&wd_data->work, watchdog_ping_work);
hrtimer_init(&wd_data->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); hrtimer_init(&wd_data->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_HARD);
wd_data->timer.function = watchdog_timer_expired; wd_data->timer.function = watchdog_timer_expired;
if (wdd->id == 0) { if (wdd->id == 0) {
...@@ -991,23 +1014,33 @@ static int watchdog_cdev_register(struct watchdog_device *wdd, dev_t devno) ...@@ -991,23 +1014,33 @@ static int watchdog_cdev_register(struct watchdog_device *wdd, dev_t devno)
} }
} }
device_initialize(&wd_data->dev);
wd_data->dev.devt = MKDEV(MAJOR(watchdog_devt), wdd->id);
wd_data->dev.class = &watchdog_class;
wd_data->dev.parent = wdd->parent;
wd_data->dev.groups = wdd->groups;
wd_data->dev.release = watchdog_core_data_release;
dev_set_drvdata(&wd_data->dev, wdd);
dev_set_name(&wd_data->dev, "watchdog%d", wdd->id);
/* Fill in the data structures */ /* Fill in the data structures */
cdev_init(&wd_data->cdev, &watchdog_fops); cdev_init(&wd_data->cdev, &watchdog_fops);
wd_data->cdev.owner = wdd->ops->owner;
/* Add the device */ /* Add the device */
err = cdev_add(&wd_data->cdev, devno, 1); err = cdev_device_add(&wd_data->cdev, &wd_data->dev);
if (err) { if (err) {
pr_err("watchdog%d unable to add device %d:%d\n", pr_err("watchdog%d unable to add device %d:%d\n",
wdd->id, MAJOR(watchdog_devt), wdd->id); wdd->id, MAJOR(watchdog_devt), wdd->id);
if (wdd->id == 0) { if (wdd->id == 0) {
misc_deregister(&watchdog_miscdev); misc_deregister(&watchdog_miscdev);
old_wd_data = NULL; old_wd_data = NULL;
kref_put(&wd_data->kref, watchdog_core_data_release); put_device(&wd_data->dev);
} }
return err; return err;
} }
wd_data->cdev.owner = wdd->ops->owner;
/* Record time of most recent heartbeat as 'just before now'. */ /* Record time of most recent heartbeat as 'just before now'. */
wd_data->last_hw_keepalive = ktime_sub(ktime_get(), 1); wd_data->last_hw_keepalive = ktime_sub(ktime_get(), 1);
watchdog_set_open_deadline(wd_data); watchdog_set_open_deadline(wd_data);
...@@ -1018,9 +1051,10 @@ static int watchdog_cdev_register(struct watchdog_device *wdd, dev_t devno) ...@@ -1018,9 +1051,10 @@ static int watchdog_cdev_register(struct watchdog_device *wdd, dev_t devno)
*/ */
if (watchdog_hw_running(wdd)) { if (watchdog_hw_running(wdd)) {
__module_get(wdd->ops->owner); __module_get(wdd->ops->owner);
kref_get(&wd_data->kref); get_device(&wd_data->dev);
if (handle_boot_enabled) if (handle_boot_enabled)
hrtimer_start(&wd_data->timer, 0, HRTIMER_MODE_REL); hrtimer_start(&wd_data->timer, 0,
HRTIMER_MODE_REL_HARD);
else else
pr_info("watchdog%d running and kernel based pre-userspace handler disabled\n", pr_info("watchdog%d running and kernel based pre-userspace handler disabled\n",
wdd->id); wdd->id);
...@@ -1041,7 +1075,7 @@ static void watchdog_cdev_unregister(struct watchdog_device *wdd) ...@@ -1041,7 +1075,7 @@ static void watchdog_cdev_unregister(struct watchdog_device *wdd)
{ {
struct watchdog_core_data *wd_data = wdd->wd_data; struct watchdog_core_data *wd_data = wdd->wd_data;
cdev_del(&wd_data->cdev); cdev_device_del(&wd_data->cdev, &wd_data->dev);
if (wdd->id == 0) { if (wdd->id == 0) {
misc_deregister(&watchdog_miscdev); misc_deregister(&watchdog_miscdev);
old_wd_data = NULL; old_wd_data = NULL;
...@@ -1060,15 +1094,9 @@ static void watchdog_cdev_unregister(struct watchdog_device *wdd) ...@@ -1060,15 +1094,9 @@ static void watchdog_cdev_unregister(struct watchdog_device *wdd)
hrtimer_cancel(&wd_data->timer); hrtimer_cancel(&wd_data->timer);
kthread_cancel_work_sync(&wd_data->work); kthread_cancel_work_sync(&wd_data->work);
kref_put(&wd_data->kref, watchdog_core_data_release); put_device(&wd_data->dev);
} }
static struct class watchdog_class = {
.name = "watchdog",
.owner = THIS_MODULE,
.dev_groups = wdt_groups,
};
static int watchdog_reboot_notifier(struct notifier_block *nb, static int watchdog_reboot_notifier(struct notifier_block *nb,
unsigned long code, void *data) unsigned long code, void *data)
{ {
...@@ -1099,27 +1127,14 @@ static int watchdog_reboot_notifier(struct notifier_block *nb, ...@@ -1099,27 +1127,14 @@ static int watchdog_reboot_notifier(struct notifier_block *nb,
int watchdog_dev_register(struct watchdog_device *wdd) int watchdog_dev_register(struct watchdog_device *wdd)
{ {
struct device *dev;
dev_t devno;
int ret; int ret;
devno = MKDEV(MAJOR(watchdog_devt), wdd->id); ret = watchdog_cdev_register(wdd);
ret = watchdog_cdev_register(wdd, devno);
if (ret) if (ret)
return ret; return ret;
dev = device_create_with_groups(&watchdog_class, wdd->parent,
devno, wdd, wdd->groups,
"watchdog%d", wdd->id);
if (IS_ERR(dev)) {
watchdog_cdev_unregister(wdd);
return PTR_ERR(dev);
}
ret = watchdog_register_pretimeout(wdd); ret = watchdog_register_pretimeout(wdd);
if (ret) { if (ret) {
device_destroy(&watchdog_class, devno);
watchdog_cdev_unregister(wdd); watchdog_cdev_unregister(wdd);
return ret; return ret;
} }
...@@ -1127,7 +1142,8 @@ int watchdog_dev_register(struct watchdog_device *wdd) ...@@ -1127,7 +1142,8 @@ int watchdog_dev_register(struct watchdog_device *wdd)
if (test_bit(WDOG_STOP_ON_REBOOT, &wdd->status)) { if (test_bit(WDOG_STOP_ON_REBOOT, &wdd->status)) {
wdd->reboot_nb.notifier_call = watchdog_reboot_notifier; wdd->reboot_nb.notifier_call = watchdog_reboot_notifier;
ret = devm_register_reboot_notifier(dev, &wdd->reboot_nb); ret = devm_register_reboot_notifier(&wdd->wd_data->dev,
&wdd->reboot_nb);
if (ret) { if (ret) {
pr_err("watchdog%d: Cannot register reboot notifier (%d)\n", pr_err("watchdog%d: Cannot register reboot notifier (%d)\n",
wdd->id, ret); wdd->id, ret);
...@@ -1149,7 +1165,6 @@ int watchdog_dev_register(struct watchdog_device *wdd) ...@@ -1149,7 +1165,6 @@ int watchdog_dev_register(struct watchdog_device *wdd)
void watchdog_dev_unregister(struct watchdog_device *wdd) void watchdog_dev_unregister(struct watchdog_device *wdd)
{ {
watchdog_unregister_pretimeout(wdd); watchdog_unregister_pretimeout(wdd);
device_destroy(&watchdog_class, wdd->wd_data->cdev.dev);
watchdog_cdev_unregister(wdd); watchdog_cdev_unregister(wdd);
} }
......
...@@ -202,7 +202,7 @@ static int wdat_wdt_enable_reboot(struct wdat_wdt *wdat) ...@@ -202,7 +202,7 @@ static int wdat_wdt_enable_reboot(struct wdat_wdt *wdat)
* WDAT specification says that the watchdog is required to reboot * WDAT specification says that the watchdog is required to reboot
* the system when it fires. However, it also states that it is * the system when it fires. However, it also states that it is
* recommeded to make it configurable through hardware register. We * recommeded to make it configurable through hardware register. We
* enable reboot now if it is configrable, just in case. * enable reboot now if it is configurable, just in case.
*/ */
ret = wdat_wdt_run_action(wdat, ACPI_WDAT_SET_REBOOT, 0, NULL); ret = wdat_wdt_run_action(wdat, ACPI_WDAT_SET_REBOOT, 0, NULL);
if (ret && ret != -EOPNOTSUPP) { if (ret && ret != -EOPNOTSUPP) {
......
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