From 4bcd615fad6adddc68b058d498b30a9e0e0db77a Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 25 Sep 2017 09:17:01 -0700 Subject: watchdog: Fix potential kref imbalance when opening watchdog If a watchdog driver's open function sets WDOG_HW_RUNNING with the expectation that the watchdog can not be stopped, but then stops the watchdog anyway in its stop function, kref_get() wil not be called in watchdog_open(). If the watchdog then stops on close, WDOG_HW_RUNNING will be cleared and kref_put() will be called, causing a kref imbalance. As result the character device data structure will be released, which in turn will cause the system to crash on the next call to watchdog_open(). Fixes: ee142889e32f5 ("watchdog: Introduce WDOG_HW_RUNNING flag") Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/watchdog_dev.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c index 1e971a50d7fb..12fe47d8237b 100644 --- a/drivers/watchdog/watchdog_dev.c +++ b/drivers/watchdog/watchdog_dev.c @@ -769,6 +769,7 @@ static int watchdog_open(struct inode *inode, struct file *file) { struct watchdog_core_data *wd_data; struct watchdog_device *wdd; + bool hw_running; int err; /* Get the corresponding watchdog device */ @@ -788,7 +789,8 @@ static int watchdog_open(struct inode *inode, struct file *file) * If the /dev/watchdog device is open, we don't want the module * to be unloaded. */ - if (!watchdog_hw_running(wdd) && !try_module_get(wdd->ops->owner)) { + hw_running = watchdog_hw_running(wdd); + if (!hw_running && !try_module_get(wdd->ops->owner)) { err = -EBUSY; goto out_clear; } @@ -799,7 +801,7 @@ static int watchdog_open(struct inode *inode, struct file *file) file->private_data = wd_data; - if (!watchdog_hw_running(wdd)) + if (!hw_running) kref_get(&wd_data->kref); /* dev/watchdog is a virtual (and thus non-seekable) filesystem */ -- cgit v1.2.3 From 914d65f3f013ba2556c7beec5d3baac7b3292504 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 25 Sep 2017 09:17:02 -0700 Subject: watchdog: Fix kref imbalance seen if handle_boot_enabled=0 If handle_boot_enabled is set to 0, the watchdog driver module use counter will not be increased and kref_get() will not be called when registering the watchdog. Subsequently, on open, this does not happen either because the code believes that it was already done because the hardware watchdog is marked as running. We could introduce a state variable to indicate this state, but let's just increase the module use counter and call kref_get() unconditionally if the hardware watchdog is running when a driver is registering itself to keep the code simple. Fixes: 2501b015313fe ("watchdog: core: add option to avoid early ...") Cc: Sebastian Reichel Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/watchdog_dev.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c index 12fe47d8237b..95b96f3cb36f 100644 --- a/drivers/watchdog/watchdog_dev.c +++ b/drivers/watchdog/watchdog_dev.c @@ -967,14 +967,13 @@ static int watchdog_cdev_register(struct watchdog_device *wdd, dev_t devno) * and schedule an immediate ping. */ if (watchdog_hw_running(wdd)) { - if (handle_boot_enabled) { - __module_get(wdd->ops->owner); - kref_get(&wd_data->kref); + __module_get(wdd->ops->owner); + kref_get(&wd_data->kref); + if (handle_boot_enabled) queue_delayed_work(watchdog_wq, &wd_data->work, 0); - } else { + else pr_info("watchdog%d running and kernel based pre-userspace handler disabled\n", - wdd->id); - } + wdd->id); } return 0; -- cgit v1.2.3 From 24f8d233074badd4c18e4dafd2fb97d65838afed Mon Sep 17 00:00:00 2001 From: Matt Redfearn Date: Tue, 14 Nov 2017 10:52:54 +0000 Subject: watchdog: indydog: Add dependency on SGI_HAS_INDYDOG Commit da2a68b3eb47 ("watchdog: Enable COMPILE_TEST where possible") enabled building the Indy watchdog driver when COMPILE_TEST is enabled. However, the driver makes reference to symbols that are only defined for certain platforms are selected in the config. These platforms select SGI_HAS_INDYDOG. Without this, link time errors result, for example when building a MIPS allyesconfig. drivers/watchdog/indydog.o: In function `indydog_write': indydog.c:(.text+0x18): undefined reference to `sgimc' indydog.c:(.text+0x1c): undefined reference to `sgimc' drivers/watchdog/indydog.o: In function `indydog_start': indydog.c:(.text+0x54): undefined reference to `sgimc' indydog.c:(.text+0x58): undefined reference to `sgimc' drivers/watchdog/indydog.o: In function `indydog_stop': indydog.c:(.text+0xa4): undefined reference to `sgimc' drivers/watchdog/indydog.o:indydog.c:(.text+0xa8): more undefined references to `sgimc' follow make: *** [Makefile:1005: vmlinux] Error 1 Fix this by ensuring that CONFIG_INDIDOG can only be selected when the necessary dependent platform symbols are built in. Fixes: da2a68b3eb47 ("watchdog: Enable COMPILE_TEST where possible") Signed-off-by: Matt Redfearn Cc: # 4.11 + Signed-off-by: Ralf Baechle Suggested-by: James Hogan Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index ca200d1f310a..5a606a4aee6a 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -1451,7 +1451,7 @@ config RC32434_WDT config INDYDOG tristate "Indy/I2 Hardware Watchdog" - depends on SGI_HAS_INDYDOG || (MIPS && COMPILE_TEST) + depends on SGI_HAS_INDYDOG help Hardware driver for the Indy's/I2's watchdog. This is a watchdog timer that will reboot the machine after a 60 second -- cgit v1.2.3 From c42cbe41727a138905a28f8e0b00c147be77ee93 Mon Sep 17 00:00:00 2001 From: Jerry Hoemann Date: Mon, 23 Oct 2017 16:46:16 -0600 Subject: watchdog: hpwdt: SMBIOS check This corrects: commit cce78da76601 ("watchdog: hpwdt: Add check for UEFI bits") The test on HPE SMBIOS extension type 219 record "Misc Features" bits for UEFI support is incorrect. The definition of the Misc Features bits in the HPE SMBIOS OEM Extensions specification (and related firmware) was changed to use a different pair of bits to represent UEFI supported. Howerver, a corresponding change to Linux was missed. Current code/platform work because the iCRU test is working. But purpose of cce78da766 is to ensure correct functionality on future systems where iCRU isn't supported. Signed-off-by: Jerry Hoemann Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/hpwdt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/watchdog/hpwdt.c b/drivers/watchdog/hpwdt.c index 67fbe35ce7cf..9fd869fbd8a9 100644 --- a/drivers/watchdog/hpwdt.c +++ b/drivers/watchdog/hpwdt.c @@ -700,7 +700,7 @@ static void dmi_find_icru(const struct dmi_header *dm, void *dummy) smbios_proliant_ptr = (struct smbios_proliant_info *) dm; if (smbios_proliant_ptr->misc_features & 0x01) is_icru = 1; - if (smbios_proliant_ptr->misc_features & 0x408) + if (smbios_proliant_ptr->misc_features & 0x1400) is_uefi = 1; } } -- cgit v1.2.3 From 2bdf6acbfead7e9aa69f36ee5682d1e5c8f70367 Mon Sep 17 00:00:00 2001 From: Andreas Färber Date: Tue, 5 Sep 2017 01:16:01 +0200 Subject: watchdog: Add Realtek RTD1295 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a watchdog driver for the Realtek RTD1295 SoC. Based on QNAP's arch/arm/mach-rtk119x/driver/rtk_watchdog.c code and mach-rtk119x/driver/dc2vo/fpga/include/iso_reg.h register defines. Signed-off-by: Andreas Färber Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/Kconfig | 10 +++ drivers/watchdog/Makefile | 1 + drivers/watchdog/rtd119x_wdt.c | 168 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 179 insertions(+) create mode 100644 drivers/watchdog/rtd119x_wdt.c (limited to 'drivers') diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 5a606a4aee6a..6fdf3f95d146 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -787,6 +787,16 @@ config UNIPHIER_WATCHDOG To compile this driver as a module, choose M here: the module will be called uniphier_wdt. +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. + # AVR32 Architecture config AT32AP700X_WDT diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 715a21078e0c..cb5fe3d414bb 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -88,6 +88,7 @@ obj-$(CONFIG_ASPEED_WATCHDOG) += aspeed_wdt.o obj-$(CONFIG_ZX2967_WATCHDOG) += zx2967_wdt.o obj-$(CONFIG_STM32_WATCHDOG) += stm32_iwdg.o obj-$(CONFIG_UNIPHIER_WATCHDOG) += uniphier_wdt.o +obj-$(CONFIG_RTD119X_WATCHDOG) += rtd119x_wdt.o # AVR32 Architecture obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o diff --git a/drivers/watchdog/rtd119x_wdt.c b/drivers/watchdog/rtd119x_wdt.c new file mode 100644 index 000000000000..d001c17ddfde --- /dev/null +++ b/drivers/watchdog/rtd119x_wdt.c @@ -0,0 +1,168 @@ +/* + * Realtek RTD129x watchdog + * + * Copyright (c) 2017 Andreas Färber + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#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); -- cgit v1.2.3 From 71246c3528780edc45990d189b856447879d1318 Mon Sep 17 00:00:00 2001 From: Mathieu Malaterre Date: Fri, 15 Sep 2017 21:20:19 +0200 Subject: watchdog: jz4740: Add support for the watchdog in jz4780 SoC The watchdog unit present in the JZ4780 works the same as the one in the JZ4740. Signed-off-by: Mathieu Malaterre Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/jz4740_wdt.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/watchdog/jz4740_wdt.c b/drivers/watchdog/jz4740_wdt.c index 20627f22baf6..aafbeb96561b 100644 --- a/drivers/watchdog/jz4740_wdt.c +++ b/drivers/watchdog/jz4740_wdt.c @@ -146,6 +146,7 @@ static const struct watchdog_ops jz4740_wdt_ops = { #ifdef CONFIG_OF static const struct of_device_id jz4740_wdt_of_matches[] = { { .compatible = "ingenic,jz4740-watchdog", }, + { .compatible = "ingenic,jz4780-watchdog", }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, jz4740_wdt_of_matches); -- cgit v1.2.3 From 9f3e13c74e1b4f370c3de06cb504c003091c9673 Mon Sep 17 00:00:00 2001 From: Andrew Jeffery Date: Wed, 20 Sep 2017 15:00:17 +0930 Subject: watchdog: aspeed: Retain watchdog enabled state An unintended post-condition of probe() is that the watchdog is disabled. This behaviour was introduced by an unnecessary write to the control register to configure the hardware based on the devicetree. The write is unnecessary because the cached control value that is manipulated by the code parsing the devicetree is eventually written by aspeed_wdt_enable(), which is when we care how the control register should be configured. Remove the write to restore expected behaviour. Fixes: b7f0b8ad25f3 ("drivers/watchdog: ASPEED reference dev tree properties for config") Signed-off-by: Andrew Jeffery Reviewed-by: Joel Stanley Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/aspeed_wdt.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/aspeed_wdt.c b/drivers/watchdog/aspeed_wdt.c index 79cc766cd30f..6c6dd3f4c48d 100644 --- a/drivers/watchdog/aspeed_wdt.c +++ b/drivers/watchdog/aspeed_wdt.c @@ -243,9 +243,13 @@ static int aspeed_wdt_probe(struct platform_device *pdev) if (of_property_read_bool(np, "aspeed,external-signal")) wdt->ctrl |= WDT_CTRL_WDT_EXT; - writel(wdt->ctrl, wdt->base + WDT_CTRL); - 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); set_bit(WDOG_HW_RUNNING, &wdt->wdd.status); } -- cgit v1.2.3 From ffff023a72e28e3ab4e7e43d8d41cb74f4b85e97 Mon Sep 17 00:00:00 2001 From: Andrew Jeffery Date: Wed, 20 Sep 2017 15:00:18 +0930 Subject: watchdog: aspeed: Fix 'Apseed' typo in Kconfig Apseed sounds like a good name for a web/mobile start-up incubator, but isn't a reflection of Aspeed themselves. Signed-off-by: Andrew Jeffery Reviewed-by: Joel Stanley Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 6fdf3f95d146..60ab8d90da08 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -746,7 +746,7 @@ config ASPEED_WATCHDOG select WATCHDOG_CORE help 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. -- cgit v1.2.3 From 2050dd0610237488df12fce45966db7debbfa4f0 Mon Sep 17 00:00:00 2001 From: Andrew Jeffery Date: Wed, 20 Sep 2017 15:00:19 +0930 Subject: watchdog: aspeed: Remove specific reference to AST2400 in Kconfig The driver also supports the watchdog in the AST25xx series, and may work on earlier SoCs as well. Signed-off-by: Andrew Jeffery Reviewed-by: Joel Stanley Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 60ab8d90da08..ce8013cbdcf2 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -741,7 +741,7 @@ config RENESAS_RZAWDT Renesas RZ/A SoCs. These watchdogs can be used to reset a system. config ASPEED_WATCHDOG - tristate "Aspeed 2400 watchdog support" + tristate "Aspeed BMC watchdog support" depends on ARCH_ASPEED || COMPILE_TEST select WATCHDOG_CORE help -- cgit v1.2.3 From d4238aa458b8c3e64d6f124aafa5c230cae31d6a Mon Sep 17 00:00:00 2001 From: Andrew Jeffery Date: Wed, 20 Sep 2017 15:00:20 +0930 Subject: watchdog: aspeed: Move init to arch_initcall Probing at device_initcall time lead to perverse cases where the watchdog was probed after, say, I2C devices, which then leaves a potentially running watchdog at the mercy of I2C device behaviour and bus conditions. Load the watchdog driver early to ensure that the kernel is patting it well before initialising peripherals. Signed-off-by: Andrew Jeffery Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/aspeed_wdt.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/watchdog/aspeed_wdt.c b/drivers/watchdog/aspeed_wdt.c index 6c6dd3f4c48d..ca5b91e2eb92 100644 --- a/drivers/watchdog/aspeed_wdt.c +++ b/drivers/watchdog/aspeed_wdt.c @@ -316,7 +316,18 @@ static struct platform_driver aspeed_watchdog_driver = { .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_LICENSE("GPL"); -- cgit v1.2.3 From 1bfe8889380890efe4943d125124f5a7b48571b0 Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Tue, 26 Sep 2017 08:11:22 +0200 Subject: watchdog: dw_wdt: add stop watchdog operation The only way of stopping the watchdog is by resetting it. Add the watchdog op for stopping the device and reset if a reset line is provided. At same time WDOG_HW_RUNNING should be remove from dw_wdt_start. As commented by Guenter Roeck: dw_wdt sets WDOG_HW_RUNNING in its open function. Result is that the kref_get() in watchdog_open() won't be executed. But then kref_put() in close will be called since the watchdog now does stop. This causes the imbalance. Signed-off-by: Oleksij Rempel Cc: Wim Van Sebroeck Cc: Guenter Roeck Cc: linux-watchdog@vger.kernel.org Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/dw_wdt.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/dw_wdt.c b/drivers/watchdog/dw_wdt.c index 36be987ff9ef..c2f4ff516230 100644 --- a/drivers/watchdog/dw_wdt.c +++ b/drivers/watchdog/dw_wdt.c @@ -127,14 +127,27 @@ static int dw_wdt_start(struct watchdog_device *wdd) dw_wdt_set_timeout(wdd, wdd->timeout); - set_bit(WDOG_HW_RUNNING, &wdd->status); - writel(WDOG_CONTROL_REG_WDT_EN_MASK, dw_wdt->regs + WDOG_CONTROL_REG_OFFSET); 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, unsigned long action, void *data) { @@ -173,6 +186,7 @@ static const struct watchdog_info dw_wdt_ident = { static const struct watchdog_ops dw_wdt_ops = { .owner = THIS_MODULE, .start = dw_wdt_start, + .stop = dw_wdt_stop, .ping = dw_wdt_ping, .set_timeout = dw_wdt_set_timeout, .get_timeleft = dw_wdt_get_timeleft, -- cgit v1.2.3 From d0d0677e8fdfba18582bb2dc4bd07189da598e1d Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 9 Oct 2017 01:28:46 +0200 Subject: watchdog: gpio: Add some local helper variables This add "dev" and "np" variables to make the probe() function a bit easier to read. Signed-off-by: Linus Walleij Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/gpio_wdt.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/gpio_wdt.c b/drivers/watchdog/gpio_wdt.c index cb66c2f99ff1..448e6c3ba73c 100644 --- a/drivers/watchdog/gpio_wdt.c +++ b/drivers/watchdog/gpio_wdt.c @@ -101,6 +101,8 @@ static const struct watchdog_ops gpio_wdt_ops = { 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; enum of_gpio_flags flags; unsigned int hw_margin; @@ -108,19 +110,19 @@ static int gpio_wdt_probe(struct platform_device *pdev) const char *algo; int ret; - priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; platform_set_drvdata(pdev, priv); - priv->gpio = of_get_gpio_flags(pdev->dev.of_node, 0, &flags); + priv->gpio = of_get_gpio_flags(np, 0, &flags); 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); + ret = of_property_read_string(np, "hw_algo", &algo); if (ret) return ret; if (!strcmp(algo, "toggle")) { @@ -133,12 +135,12 @@ static int gpio_wdt_probe(struct platform_device *pdev) return -EINVAL; } - ret = devm_gpio_request_one(&pdev->dev, priv->gpio, f, - dev_name(&pdev->dev)); + ret = devm_gpio_request_one(dev, priv->gpio, f, + dev_name(dev)); if (ret) return ret; - ret = of_property_read_u32(pdev->dev.of_node, + ret = of_property_read_u32(np, "hw_margin_ms", &hw_margin); if (ret) return ret; @@ -146,7 +148,7 @@ static int gpio_wdt_probe(struct platform_device *pdev) if (hw_margin < 2 || hw_margin > 65535) return -EINVAL; - priv->always_running = of_property_read_bool(pdev->dev.of_node, + priv->always_running = of_property_read_bool(np, "always-running"); watchdog_set_drvdata(&priv->wdd, priv); @@ -155,9 +157,9 @@ static int gpio_wdt_probe(struct platform_device *pdev) priv->wdd.ops = &gpio_wdt_ops; priv->wdd.min_timeout = SOFT_TIMEOUT_MIN; 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; watchdog_stop_on_reboot(&priv->wdd); -- cgit v1.2.3 From a2363f9d2f34bbfd9899b3a3b3cc7022e0f85724 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 9 Oct 2017 01:28:47 +0200 Subject: watchdog: gpio: Convert to use GPIO descriptors This converts the GPIO watchdog driver to use GPIO descriptors instead of relying on the old method to read out GPIO numbers from the device tree and then using those with the old GPIO API. The descriptor API keeps track of whether the line is active low so we can remove all active low handling and rely on the GPIO descriptor to deal with this for us. Signed-off-by: Linus Walleij Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/gpio_wdt.c | 41 +++++++++++++++++------------------------ 1 file changed, 17 insertions(+), 24 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/gpio_wdt.c b/drivers/watchdog/gpio_wdt.c index 448e6c3ba73c..b077c88a5ceb 100644 --- a/drivers/watchdog/gpio_wdt.c +++ b/drivers/watchdog/gpio_wdt.c @@ -12,7 +12,8 @@ #include #include #include -#include +#include +#include #include #include @@ -25,8 +26,7 @@ enum { }; struct gpio_wdt_priv { - int gpio; - bool active_low; + struct gpio_desc *gpiod; bool state; bool always_running; unsigned int hw_algo; @@ -35,11 +35,12 @@ struct gpio_wdt_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 */ 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) @@ -50,13 +51,13 @@ static int gpio_wdt_ping(struct watchdog_device *wdd) case HW_ALGO_TOGGLE: /* Toggle output pin */ priv->state = !priv->state; - gpio_set_value_cansleep(priv->gpio, priv->state); + gpiod_set_value_cansleep(priv->gpiod, priv->state); break; case HW_ALGO_LEVEL: /* Pulse */ - gpio_set_value_cansleep(priv->gpio, !priv->active_low); + gpiod_set_value_cansleep(priv->gpiod, 1); udelay(1); - gpio_set_value_cansleep(priv->gpio, priv->active_low); + gpiod_set_value_cansleep(priv->gpiod, 0); break; } return 0; @@ -66,8 +67,8 @@ static int gpio_wdt_start(struct watchdog_device *wdd) { struct gpio_wdt_priv *priv = watchdog_get_drvdata(wdd); - priv->state = priv->active_low; - gpio_direction_output(priv->gpio, priv->state); + priv->state = 0; + gpiod_direction_output(priv->gpiod, priv->state); set_bit(WDOG_HW_RUNNING, &wdd->status); @@ -104,9 +105,8 @@ 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; - enum of_gpio_flags flags; + enum gpiod_flags gflags; unsigned int hw_margin; - unsigned long f = 0; const char *algo; int ret; @@ -116,29 +116,22 @@ static int gpio_wdt_probe(struct platform_device *pdev) platform_set_drvdata(pdev, priv); - priv->gpio = of_get_gpio_flags(np, 0, &flags); - if (!gpio_is_valid(priv->gpio)) - return priv->gpio; - - priv->active_low = flags & OF_GPIO_ACTIVE_LOW; - ret = of_property_read_string(np, "hw_algo", &algo); if (ret) return ret; if (!strcmp(algo, "toggle")) { priv->hw_algo = HW_ALGO_TOGGLE; - f = GPIOF_IN; + gflags = GPIOD_IN; } else if (!strcmp(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 { return -EINVAL; } - ret = devm_gpio_request_one(dev, priv->gpio, f, - dev_name(dev)); - if (ret) - return ret; + priv->gpiod = devm_gpiod_get(dev, NULL, gflags); + if (IS_ERR(priv->gpiod)) + return PTR_ERR(priv->gpiod); ret = of_property_read_u32(np, "hw_margin_ms", &hw_margin); -- cgit v1.2.3 From 844ecd970f2c7932878dc363da0a07012a221437 Mon Sep 17 00:00:00 2001 From: Chris Packham Date: Wed, 11 Oct 2017 15:29:56 +1300 Subject: watchdog: orion: fix typo Correct typo in comment "insterted" -> "inserted". Signed-off-by: Chris Packham Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/orion_wdt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/watchdog/orion_wdt.c b/drivers/watchdog/orion_wdt.c index 83af7d6cc37c..ea676d233e1e 100644 --- a/drivers/watchdog/orion_wdt.c +++ b/drivers/watchdog/orion_wdt.c @@ -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 * 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. */ if (!orion_wdt_enabled(&dev->wdt)) -- cgit v1.2.3 From 766a2aad645e6c4c4dec0c02be34318d5538e75e Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 16 Oct 2017 22:54:24 +0200 Subject: watchdog: gemini/ftwdt010: rename driver and symbols This renames all the driver files and symbols for the Gemini watchdog to FTWDT010 as it has been revealed that this IP block is a generic watchdog timer from Faraday Technology used in several SoC designs. Select this driver by default for the Gemini, it is a sensible driver to always have enabled. Signed-off-by: Linus Walleij Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/Kconfig | 14 +-- drivers/watchdog/Makefile | 2 +- drivers/watchdog/ftwdt010_wdt.c | 230 ++++++++++++++++++++++++++++++++++++++++ drivers/watchdog/gemini_wdt.c | 229 --------------------------------------- 4 files changed, 239 insertions(+), 236 deletions(-) create mode 100644 drivers/watchdog/ftwdt010_wdt.c delete mode 100644 drivers/watchdog/gemini_wdt.c (limited to 'drivers') diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index ce8013cbdcf2..be37e102dd30 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -321,16 +321,18 @@ config 977_WATCHDOG Not sure? It's safe to say N. -config GEMINI_WATCHDOG - tristate "Gemini watchdog" - depends on ARCH_GEMINI +config FTWDT010_WATCHDOG + tristate "Faraday Technology FTWDT010 watchdog" + depends on ARM || COMPILE_TEST select WATCHDOG_CORE + default ARCH_GEMINI help - Say Y here if to include support for the watchdog timer - embedded in the Cortina Systems Gemini family of devices. + Say Y here if to include support for the Faraday Technology + FTWDT010 watchdog timer embedded in the Cortina Systems Gemini + family of devices. 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 tristate "IXP4xx Watchdog" diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index cb5fe3d414bb..b6cf527b31fb 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -46,7 +46,7 @@ obj-$(CONFIG_OMAP_WATCHDOG) += omap_wdt.o obj-$(CONFIG_TWL4030_WATCHDOG) += twl4030_wdt.o obj-$(CONFIG_21285_WATCHDOG) += wdt285.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_KS8695_WATCHDOG) += ks8695_wdt.o obj-$(CONFIG_S3C2410_WATCHDOG) += s3c2410_wdt.o diff --git a/drivers/watchdog/ftwdt010_wdt.c b/drivers/watchdog/ftwdt010_wdt.c new file mode 100644 index 000000000000..637ffd812f0b --- /dev/null +++ b/drivers/watchdog/ftwdt010_wdt.c @@ -0,0 +1,230 @@ +/* + * Watchdog driver for Faraday Technology FTWDT010 + * + * Copyright (C) 2017 Linus Walleij + * + * Inspired by the out-of-tree drivers from OpenWRT: + * Copyright (C) 2009 Paulius Zaleckas + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define FTWDT010_WDCOUNTER 0x0 +#define FTWDT010_WDLOAD 0x4 +#define FTWDT010_WDRESTART 0x8 +#define FTWDT010_WDCR 0xC + +#define WDRESTART_MAGIC 0x5AB9 + +#define WDCR_CLOCK_5MHZ BIT(4) +#define WDCR_SYS_RST BIT(1) +#define WDCR_ENABLE BIT(0) + +#define WDT_CLOCK 5000000 /* 5 MHz */ + +struct ftwdt010_wdt { + struct watchdog_device wdd; + struct device *dev; + void __iomem *base; +}; + +static inline +struct ftwdt010_wdt *to_ftwdt010_wdt(struct watchdog_device *wdd) +{ + return container_of(wdd, struct ftwdt010_wdt, wdd); +} + +static int ftwdt010_wdt_start(struct watchdog_device *wdd) +{ + struct ftwdt010_wdt *gwdt = to_ftwdt010_wdt(wdd); + + writel(wdd->timeout * WDT_CLOCK, gwdt->base + FTWDT010_WDLOAD); + writel(WDRESTART_MAGIC, gwdt->base + FTWDT010_WDRESTART); + /* set clock before enabling */ + writel(WDCR_CLOCK_5MHZ | WDCR_SYS_RST, + gwdt->base + FTWDT010_WDCR); + writel(WDCR_CLOCK_5MHZ | WDCR_SYS_RST | WDCR_ENABLE, + gwdt->base + FTWDT010_WDCR); + + return 0; +} + +static int ftwdt010_wdt_stop(struct watchdog_device *wdd) +{ + struct ftwdt010_wdt *gwdt = to_ftwdt010_wdt(wdd); + + writel(0, gwdt->base + FTWDT010_WDCR); + + return 0; +} + +static int ftwdt010_wdt_ping(struct watchdog_device *wdd) +{ + struct ftwdt010_wdt *gwdt = to_ftwdt010_wdt(wdd); + + writel(WDRESTART_MAGIC, gwdt->base + FTWDT010_WDRESTART); + + return 0; +} + +static int ftwdt010_wdt_set_timeout(struct watchdog_device *wdd, + unsigned int timeout) +{ + wdd->timeout = timeout; + if (watchdog_active(wdd)) + ftwdt010_wdt_start(wdd); + + return 0; +} + +static irqreturn_t ftwdt010_wdt_interrupt(int irq, void *data) +{ + struct ftwdt010_wdt *gwdt = data; + + watchdog_notify_pretimeout(&gwdt->wdd); + + return IRQ_HANDLED; +} + +static const struct watchdog_ops ftwdt010_wdt_ops = { + .start = ftwdt010_wdt_start, + .stop = ftwdt010_wdt_stop, + .ping = ftwdt010_wdt_ping, + .set_timeout = ftwdt010_wdt_set_timeout, + .owner = THIS_MODULE, +}; + +static const struct watchdog_info ftwdt010_wdt_info = { + .options = WDIOF_KEEPALIVEPING + | WDIOF_MAGICCLOSE + | WDIOF_SETTIMEOUT, + .identity = KBUILD_MODNAME, +}; + + +static int ftwdt010_wdt_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct resource *res; + struct ftwdt010_wdt *gwdt; + unsigned int reg; + int irq; + int ret; + + gwdt = devm_kzalloc(dev, sizeof(*gwdt), GFP_KERNEL); + if (!gwdt) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + gwdt->base = devm_ioremap_resource(dev, res); + if (IS_ERR(gwdt->base)) + return PTR_ERR(gwdt->base); + + irq = platform_get_irq(pdev, 0); + if (!irq) + return -EINVAL; + + gwdt->dev = dev; + gwdt->wdd.info = &ftwdt010_wdt_info; + gwdt->wdd.ops = &ftwdt010_wdt_ops; + gwdt->wdd.min_timeout = 1; + gwdt->wdd.max_timeout = 0xFFFFFFFF / WDT_CLOCK; + gwdt->wdd.parent = dev; + + /* + * If 'timeout-sec' unspecified in devicetree, assume a 13 second + * default. + */ + gwdt->wdd.timeout = 13U; + watchdog_init_timeout(&gwdt->wdd, 0, dev); + + reg = readw(gwdt->base + FTWDT010_WDCR); + if (reg & WDCR_ENABLE) { + /* Watchdog was enabled by the bootloader, disable it. */ + reg &= ~WDCR_ENABLE; + writel(reg, gwdt->base + FTWDT010_WDCR); + } + + ret = devm_request_irq(dev, irq, ftwdt010_wdt_interrupt, 0, + "watchdog bark", gwdt); + if (ret) + return ret; + + ret = devm_watchdog_register_device(dev, &gwdt->wdd); + if (ret) { + dev_err(&pdev->dev, "failed to register watchdog\n"); + return ret; + } + + /* Set up platform driver data */ + platform_set_drvdata(pdev, gwdt); + dev_info(dev, "FTWDT010 watchdog driver enabled\n"); + + return 0; +} + +static int __maybe_unused ftwdt010_wdt_suspend(struct device *dev) +{ + struct ftwdt010_wdt *gwdt = dev_get_drvdata(dev); + unsigned int reg; + + reg = readw(gwdt->base + FTWDT010_WDCR); + reg &= ~WDCR_ENABLE; + writel(reg, gwdt->base + FTWDT010_WDCR); + + return 0; +} + +static int __maybe_unused ftwdt010_wdt_resume(struct device *dev) +{ + struct ftwdt010_wdt *gwdt = dev_get_drvdata(dev); + unsigned int reg; + + if (watchdog_active(&gwdt->wdd)) { + reg = readw(gwdt->base + FTWDT010_WDCR); + reg |= WDCR_ENABLE; + writel(reg, gwdt->base + FTWDT010_WDCR); + } + + return 0; +} + +static const struct dev_pm_ops ftwdt010_wdt_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(ftwdt010_wdt_suspend, + ftwdt010_wdt_resume) +}; + +#ifdef CONFIG_OF +static const struct of_device_id ftwdt010_wdt_match[] = { + { .compatible = "faraday,ftwdt010" }, + { .compatible = "cortina,gemini-watchdog" }, + {}, +}; +MODULE_DEVICE_TABLE(of, ftwdt010_wdt_match); +#endif + +static struct platform_driver ftwdt010_wdt_driver = { + .probe = ftwdt010_wdt_probe, + .driver = { + .name = "ftwdt010-wdt", + .of_match_table = of_match_ptr(ftwdt010_wdt_match), + .pm = &ftwdt010_wdt_dev_pm_ops, + }, +}; +module_platform_driver(ftwdt010_wdt_driver); +MODULE_AUTHOR("Linus Walleij"); +MODULE_DESCRIPTION("Watchdog driver for Faraday Technology FTWDT010"); +MODULE_LICENSE("GPL"); diff --git a/drivers/watchdog/gemini_wdt.c b/drivers/watchdog/gemini_wdt.c deleted file mode 100644 index 8155aa619e4c..000000000000 --- a/drivers/watchdog/gemini_wdt.c +++ /dev/null @@ -1,229 +0,0 @@ -/* - * Watchdog driver for Cortina Systems Gemini SoC - * - * Copyright (C) 2017 Linus Walleij - * - * Inspired by the out-of-tree drivers from OpenWRT: - * Copyright (C) 2009 Paulius Zaleckas - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define GEMINI_WDCOUNTER 0x0 -#define GEMINI_WDLOAD 0x4 -#define GEMINI_WDRESTART 0x8 -#define GEMINI_WDCR 0xC - -#define WDRESTART_MAGIC 0x5AB9 - -#define WDCR_CLOCK_5MHZ BIT(4) -#define WDCR_SYS_RST BIT(1) -#define WDCR_ENABLE BIT(0) - -#define WDT_CLOCK 5000000 /* 5 MHz */ - -struct gemini_wdt { - struct watchdog_device wdd; - struct device *dev; - void __iomem *base; -}; - -static inline -struct gemini_wdt *to_gemini_wdt(struct watchdog_device *wdd) -{ - return container_of(wdd, struct gemini_wdt, wdd); -} - -static int gemini_wdt_start(struct watchdog_device *wdd) -{ - struct gemini_wdt *gwdt = to_gemini_wdt(wdd); - - writel(wdd->timeout * WDT_CLOCK, gwdt->base + GEMINI_WDLOAD); - writel(WDRESTART_MAGIC, gwdt->base + GEMINI_WDRESTART); - /* set clock before enabling */ - writel(WDCR_CLOCK_5MHZ | WDCR_SYS_RST, - gwdt->base + GEMINI_WDCR); - writel(WDCR_CLOCK_5MHZ | WDCR_SYS_RST | WDCR_ENABLE, - gwdt->base + GEMINI_WDCR); - - return 0; -} - -static int gemini_wdt_stop(struct watchdog_device *wdd) -{ - struct gemini_wdt *gwdt = to_gemini_wdt(wdd); - - writel(0, gwdt->base + GEMINI_WDCR); - - return 0; -} - -static int gemini_wdt_ping(struct watchdog_device *wdd) -{ - struct gemini_wdt *gwdt = to_gemini_wdt(wdd); - - writel(WDRESTART_MAGIC, gwdt->base + GEMINI_WDRESTART); - - return 0; -} - -static int gemini_wdt_set_timeout(struct watchdog_device *wdd, - unsigned int timeout) -{ - wdd->timeout = timeout; - if (watchdog_active(wdd)) - gemini_wdt_start(wdd); - - return 0; -} - -static irqreturn_t gemini_wdt_interrupt(int irq, void *data) -{ - struct gemini_wdt *gwdt = data; - - watchdog_notify_pretimeout(&gwdt->wdd); - - return IRQ_HANDLED; -} - -static const struct watchdog_ops gemini_wdt_ops = { - .start = gemini_wdt_start, - .stop = gemini_wdt_stop, - .ping = gemini_wdt_ping, - .set_timeout = gemini_wdt_set_timeout, - .owner = THIS_MODULE, -}; - -static const struct watchdog_info gemini_wdt_info = { - .options = WDIOF_KEEPALIVEPING - | WDIOF_MAGICCLOSE - | WDIOF_SETTIMEOUT, - .identity = KBUILD_MODNAME, -}; - - -static int gemini_wdt_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct resource *res; - struct gemini_wdt *gwdt; - unsigned int reg; - int irq; - int ret; - - gwdt = devm_kzalloc(dev, sizeof(*gwdt), GFP_KERNEL); - if (!gwdt) - return -ENOMEM; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - gwdt->base = devm_ioremap_resource(dev, res); - if (IS_ERR(gwdt->base)) - return PTR_ERR(gwdt->base); - - irq = platform_get_irq(pdev, 0); - if (!irq) - return -EINVAL; - - gwdt->dev = dev; - gwdt->wdd.info = &gemini_wdt_info; - gwdt->wdd.ops = &gemini_wdt_ops; - gwdt->wdd.min_timeout = 1; - gwdt->wdd.max_timeout = 0xFFFFFFFF / WDT_CLOCK; - gwdt->wdd.parent = dev; - - /* - * If 'timeout-sec' unspecified in devicetree, assume a 13 second - * default. - */ - gwdt->wdd.timeout = 13U; - watchdog_init_timeout(&gwdt->wdd, 0, dev); - - reg = readw(gwdt->base + GEMINI_WDCR); - if (reg & WDCR_ENABLE) { - /* Watchdog was enabled by the bootloader, disable it. */ - reg &= ~WDCR_ENABLE; - writel(reg, gwdt->base + GEMINI_WDCR); - } - - ret = devm_request_irq(dev, irq, gemini_wdt_interrupt, 0, - "watchdog bark", gwdt); - if (ret) - return ret; - - ret = devm_watchdog_register_device(dev, &gwdt->wdd); - if (ret) { - dev_err(&pdev->dev, "failed to register watchdog\n"); - return ret; - } - - /* Set up platform driver data */ - platform_set_drvdata(pdev, gwdt); - dev_info(dev, "Gemini watchdog driver enabled\n"); - - return 0; -} - -static int __maybe_unused gemini_wdt_suspend(struct device *dev) -{ - struct gemini_wdt *gwdt = dev_get_drvdata(dev); - unsigned int reg; - - reg = readw(gwdt->base + GEMINI_WDCR); - reg &= ~WDCR_ENABLE; - writel(reg, gwdt->base + GEMINI_WDCR); - - return 0; -} - -static int __maybe_unused gemini_wdt_resume(struct device *dev) -{ - struct gemini_wdt *gwdt = dev_get_drvdata(dev); - unsigned int reg; - - if (watchdog_active(&gwdt->wdd)) { - reg = readw(gwdt->base + GEMINI_WDCR); - reg |= WDCR_ENABLE; - writel(reg, gwdt->base + GEMINI_WDCR); - } - - return 0; -} - -static const struct dev_pm_ops gemini_wdt_dev_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(gemini_wdt_suspend, - gemini_wdt_resume) -}; - -#ifdef CONFIG_OF -static const struct of_device_id gemini_wdt_match[] = { - { .compatible = "cortina,gemini-watchdog" }, - {}, -}; -MODULE_DEVICE_TABLE(of, gemini_wdt_match); -#endif - -static struct platform_driver gemini_wdt_driver = { - .probe = gemini_wdt_probe, - .driver = { - .name = "gemini-wdt", - .of_match_table = of_match_ptr(gemini_wdt_match), - .pm = &gemini_wdt_dev_pm_ops, - }, -}; -module_platform_driver(gemini_wdt_driver); -MODULE_AUTHOR("Linus Walleij"); -MODULE_DESCRIPTION("Watchdog driver for Gemini"); -MODULE_LICENSE("GPL"); -- cgit v1.2.3 From d5433fd60df0d77b4751715e22a2dd715d26d926 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 16 Oct 2017 22:54:25 +0200 Subject: watchdog: ftwdt010: Make interrupt optional The Moxart does not appear to be using the interrupt from the watchdog timer, maybe it's not even routed, so as to support more architectures with this driver, make the interrupt optional. While we are at it: actually enable the use of the interrupt if present by setting the right bit in the control register and define the missing control register bits. Signed-off-by: Linus Walleij Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/ftwdt010_wdt.c | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/ftwdt010_wdt.c b/drivers/watchdog/ftwdt010_wdt.c index 637ffd812f0b..a9c2912ee280 100644 --- a/drivers/watchdog/ftwdt010_wdt.c +++ b/drivers/watchdog/ftwdt010_wdt.c @@ -30,6 +30,8 @@ #define WDRESTART_MAGIC 0x5AB9 #define WDCR_CLOCK_5MHZ BIT(4) +#define WDCR_WDEXT BIT(3) +#define WDCR_WDINTR BIT(2) #define WDCR_SYS_RST BIT(1) #define WDCR_ENABLE BIT(0) @@ -39,6 +41,7 @@ struct ftwdt010_wdt { struct watchdog_device wdd; struct device *dev; void __iomem *base; + bool has_irq; }; static inline @@ -50,14 +53,17 @@ struct ftwdt010_wdt *to_ftwdt010_wdt(struct watchdog_device *wdd) static int ftwdt010_wdt_start(struct watchdog_device *wdd) { struct ftwdt010_wdt *gwdt = to_ftwdt010_wdt(wdd); + u32 enable; writel(wdd->timeout * WDT_CLOCK, gwdt->base + FTWDT010_WDLOAD); writel(WDRESTART_MAGIC, gwdt->base + FTWDT010_WDRESTART); /* set clock before enabling */ - writel(WDCR_CLOCK_5MHZ | WDCR_SYS_RST, - gwdt->base + FTWDT010_WDCR); - writel(WDCR_CLOCK_5MHZ | WDCR_SYS_RST | WDCR_ENABLE, - gwdt->base + FTWDT010_WDCR); + enable = WDCR_CLOCK_5MHZ | WDCR_SYS_RST; + writel(enable, gwdt->base + FTWDT010_WDCR); + if (gwdt->has_irq) + enable |= WDCR_WDINTR; + enable |= WDCR_ENABLE; + writel(enable, gwdt->base + FTWDT010_WDCR); return 0; } @@ -133,10 +139,6 @@ static int ftwdt010_wdt_probe(struct platform_device *pdev) if (IS_ERR(gwdt->base)) return PTR_ERR(gwdt->base); - irq = platform_get_irq(pdev, 0); - if (!irq) - return -EINVAL; - gwdt->dev = dev; gwdt->wdd.info = &ftwdt010_wdt_info; gwdt->wdd.ops = &ftwdt010_wdt_ops; @@ -158,10 +160,14 @@ static int ftwdt010_wdt_probe(struct platform_device *pdev) writel(reg, gwdt->base + FTWDT010_WDCR); } - ret = devm_request_irq(dev, irq, ftwdt010_wdt_interrupt, 0, - "watchdog bark", gwdt); - if (ret) - return ret; + irq = platform_get_irq(pdev, 0); + if (irq) { + ret = devm_request_irq(dev, irq, ftwdt010_wdt_interrupt, 0, + "watchdog bark", gwdt); + if (ret) + return ret; + gwdt->has_irq = true; + } ret = devm_watchdog_register_device(dev, &gwdt->wdd); if (ret) { -- cgit v1.2.3 From 540f635192bb222f252724f70b9240d17742f1b1 Mon Sep 17 00:00:00 2001 From: Michael Grzeschik Date: Tue, 17 Oct 2017 17:30:22 +0200 Subject: watchdog: da9062: Add restart handler support Register a restart handler for the da9062 watchdog. System restart is triggered by sending the shutdown command to the PMIC. As more-suitable restart handlers may exist, the priority of the watchdog restart handler is set to 128. Signed-off-by: Michael Grzeschik Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/da9062_wdt.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'drivers') diff --git a/drivers/watchdog/da9062_wdt.c b/drivers/watchdog/da9062_wdt.c index 9083d3d922b0..4349a0221548 100644 --- a/drivers/watchdog/da9062_wdt.c +++ b/drivers/watchdog/da9062_wdt.c @@ -175,6 +175,25 @@ static int da9062_wdt_set_timeout(struct watchdog_device *wdd, 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 = { .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, .identity = "DA9062 WDT", @@ -186,6 +205,7 @@ static const struct watchdog_ops da9062_watchdog_ops = { .stop = da9062_wdt_stop, .ping = da9062_wdt_ping, .set_timeout = da9062_wdt_set_timeout, + .restart = da9062_wdt_restart, }; static const struct of_device_id da9062_compatible_id_table[] = { @@ -219,6 +239,8 @@ static int da9062_wdt_probe(struct platform_device *pdev) wdt->wdtdev.status = WATCHDOG_NOWAYOUT_INIT_STATUS; wdt->wdtdev.parent = &pdev->dev; + watchdog_set_restart_priority(&wdt->wdtdev, 128); + watchdog_set_drvdata(&wdt->wdtdev, wdt); ret = devm_watchdog_register_device(&pdev->dev, &wdt->wdtdev); -- cgit v1.2.3 From f31b2a9bdeaea38957f4675ee0d1b8421595dc67 Mon Sep 17 00:00:00 2001 From: Michael Grzeschik Date: Tue, 17 Oct 2017 17:30:25 +0200 Subject: watchdog: da9062: Disable and wait before changing timeout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The DA9062 watchdog occasionally enters error condition and resets the system if the timeout is changed quickly after the timer was enabled. The method of disabling and waiting for > 150 µs before setting the new timeout is taken from the DA9052 driver. Signed-off-by: Michael Grzeschik Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/da9062_wdt.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers') diff --git a/drivers/watchdog/da9062_wdt.c b/drivers/watchdog/da9062_wdt.c index 4349a0221548..dbb970e87d3d 100644 --- a/drivers/watchdog/da9062_wdt.c +++ b/drivers/watchdog/da9062_wdt.c @@ -100,6 +100,13 @@ static int da9062_wdt_update_timeout_register(struct da9062_watchdog *wdt, if (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, DA9062AA_CONTROL_D, DA9062AA_TWDSCALE_MASK, -- cgit v1.2.3 From fb484262f9ef0d73e0116b942c114bc48c6c7b3b Mon Sep 17 00:00:00 2001 From: Michael Grzeschik Date: Tue, 17 Oct 2017 17:30:26 +0200 Subject: watchdog: da9062: use protection delay mechanism from core This patch removes the windows protection routine that got now covered by the wdt core. Signed-off-by: Michael Grzeschik Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/da9062_wdt.c | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/da9062_wdt.c b/drivers/watchdog/da9062_wdt.c index dbb970e87d3d..814dff6045a4 100644 --- a/drivers/watchdog/da9062_wdt.c +++ b/drivers/watchdog/da9062_wdt.c @@ -46,22 +46,6 @@ static void da9062_set_window_start(struct da9062_watchdog *wdt) 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) { unsigned int i; @@ -78,8 +62,6 @@ static int da9062_reset_watchdog_timer(struct da9062_watchdog *wdt) { int ret; - da9062_apply_window_protection(wdt); - ret = regmap_update_bits(wdt->hw->regmap, DA9062AA_CONTROL_F, DA9062AA_WATCHDOG_MASK, @@ -242,6 +224,7 @@ static int da9062_wdt_probe(struct platform_device *pdev) wdt->wdtdev.ops = &da9062_watchdog_ops; wdt->wdtdev.min_timeout = DA9062_WDT_MIN_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.status = WATCHDOG_NOWAYOUT_INIT_STATUS; wdt->wdtdev.parent = &pdev->dev; -- cgit v1.2.3 From b6c84c9ff3f48a476451eceb5e8478b8aadda8dc Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 19 Oct 2017 17:05:48 +0200 Subject: watchdog: xen: use time64_t for timeouts The Xen watchdog driver uses __kernel_time_t and ktime_to_timespec() internally for managing its timeouts. Both are deprecated because of y2038 problems. The driver itself is fine, since it only uses monotonic times, but converting it to use ktime_get_seconds() avoids the deprecated interfaces and is slightly simpler. Signed-off-by: Arnd Bergmann Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/xen_wdt.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/xen_wdt.c b/drivers/watchdog/xen_wdt.c index cf0e650c2015..5dd5c3494d55 100644 --- a/drivers/watchdog/xen_wdt.c +++ b/drivers/watchdog/xen_wdt.c @@ -35,7 +35,7 @@ static struct platform_device *platform_device; static DEFINE_SPINLOCK(wdt_lock); 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 */ @@ -49,15 +49,15 @@ module_param(nowayout, bool, S_IRUGO); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started " "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); -static inline __kernel_time_t set_timeout(void) +static inline time64_t set_timeout(void) { wdt.timeout = timeout; - return ktime_to_timespec(ktime_get()).tv_sec + timeout; + return ktime_get_seconds() + timeout; } static int xen_wdt_start(void) { - __kernel_time_t expires; + time64_t expires; int err; spin_lock(&wdt_lock); @@ -98,7 +98,7 @@ static int xen_wdt_stop(void) static int xen_wdt_kick(void) { - __kernel_time_t expires; + time64_t expires; int err; spin_lock(&wdt_lock); @@ -222,7 +222,7 @@ static long xen_wdt_ioctl(struct file *file, unsigned int cmd, return put_user(timeout, argp); case WDIOC_GETTIMELEFT: - retval = wdt_expires - ktime_to_timespec(ktime_get()).tv_sec; + retval = wdt_expires - ktime_get_seconds(); return put_user(retval, argp); } -- cgit v1.2.3 From e531037145df630ff9d7be3858497388cc700f79 Mon Sep 17 00:00:00 2001 From: Corentin Labbe Date: Sat, 21 Oct 2017 19:45:13 +0200 Subject: watchdog: sunxi_wdt: use of_device_get_match_data The usage of of_device_get_match_data reduce the code size a bit. Furthermore, it prevents an improbable dereference when of_match_device() return NULL. Signed-off-by: Corentin Labbe Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/sunxi_wdt.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/sunxi_wdt.c b/drivers/watchdog/sunxi_wdt.c index 9728fa32c357..802e31b1416d 100644 --- a/drivers/watchdog/sunxi_wdt.c +++ b/drivers/watchdog/sunxi_wdt.c @@ -234,7 +234,6 @@ MODULE_DEVICE_TABLE(of, sunxi_wdt_dt_ids); static int sunxi_wdt_probe(struct platform_device *pdev) { struct sunxi_wdt_dev *sunxi_wdt; - const struct of_device_id *device; struct resource *res; int err; @@ -242,12 +241,10 @@ static int sunxi_wdt_probe(struct platform_device *pdev) if (!sunxi_wdt) return -EINVAL; - device = of_match_device(sunxi_wdt_dt_ids, &pdev->dev); - if (!device) + sunxi_wdt->wdt_regs = of_device_get_match_data(&pdev->dev); + if (!sunxi_wdt->wdt_regs) return -ENODEV; - sunxi_wdt->wdt_regs = device->data; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); sunxi_wdt->wdt_base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(sunxi_wdt->wdt_base)) -- cgit v1.2.3 From 838534e50e2e5c1e644e30ab6cb28da88eb31368 Mon Sep 17 00:00:00 2001 From: Jerry Hoemann Date: Mon, 23 Oct 2017 16:46:17 -0600 Subject: watchdog: hpwdt: Check source of NMI Do not claim the NMI (i.e. return NMI_DONE) if the source of the NMI isn't the iLO watchdog or debug. Signed-off-by: Jerry Hoemann Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/hpwdt.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers') diff --git a/drivers/watchdog/hpwdt.c b/drivers/watchdog/hpwdt.c index 9fd869fbd8a9..e61658310381 100644 --- a/drivers/watchdog/hpwdt.c +++ b/drivers/watchdog/hpwdt.c @@ -52,6 +52,7 @@ static char expect_release; static unsigned long hpwdt_is_open; 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_con; @@ -474,6 +475,11 @@ static int hpwdt_time_left(void) return TICKS_TO_SECS(ioread16(hpwdt_timer_reg)); } +static int hpwdt_my_nmi(void) +{ + return ioread8(hpwdt_nmistat) & 0x6; +} + #ifdef CONFIG_HPWDT_NMI_DECODING /* * NMI Handler @@ -486,6 +492,9 @@ static int hpwdt_pretimeout(unsigned int ulReason, struct pt_regs *regs) if (!hpwdt_nmi_decoding) return NMI_DONE; + if ((ulReason == NMI_UNKNOWN) && !hpwdt_my_nmi()) + return NMI_DONE; + spin_lock_irqsave(&rom_lock, rom_pl); if (!die_nmi_called && !is_icru && !is_uefi) asminline_call(&cmn_regs, cru_rom_addr); @@ -842,6 +851,7 @@ static int hpwdt_init_one(struct pci_dev *dev, retval = -ENOMEM; goto error_pci_iomap; } + hpwdt_nmistat = pci_mem_addr + 0x6e; hpwdt_timer_reg = pci_mem_addr + 0x70; hpwdt_timer_con = pci_mem_addr + 0x72; -- cgit v1.2.3 From 7af4ac8772a8f9af40f998671e6d09f5de137854 Mon Sep 17 00:00:00 2001 From: Radu Rendec Date: Thu, 26 Oct 2017 17:10:13 +0100 Subject: watchdog: i6300esb: use the watchdog subsystem Change the i6300esb driver to use the watchdog subsystem instead of the legacy watchdog API. This is mainly just getting rid of the "write" and "ioctl" methods and part of the watchdog control logic (which are all implemented by the watchdog subsystem). Even though the watchdog subsystem supports registering and handling multiple watchdog devices at the same time, the i6300esb driver still has a limitation of only one i6300esb device due to some global variable usage that comes from the original design. However, the driver can now coexist with other watchdog devices (supported by different drivers). Signed-off-by: Radu Rendec Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/i6300esb.c | 232 +++++++++----------------------------------- 1 file changed, 47 insertions(+), 185 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/i6300esb.c b/drivers/watchdog/i6300esb.c index d7befd58b391..4f4287d83c21 100644 --- a/drivers/watchdog/i6300esb.c +++ b/drivers/watchdog/i6300esb.c @@ -21,6 +21,8 @@ * Version 0.02 * 20050210 David Härdeman * Ported driver to kernel 2.6 + * 20171016 Radu Rendec + * Change driver to use the watchdog subsystem */ /* @@ -44,7 +46,6 @@ /* Module and version information */ #define ESB_VERSION "0.05" #define ESB_MODULE_NAME "i6300ESB timer" -#define ESB_DRIVER_NAME ESB_MODULE_NAME ", v" ESB_VERSION /* PCI configuration registers */ #define ESB_CONFIG_REG 0x60 /* Config register */ @@ -76,11 +77,7 @@ /* internal variables */ static void __iomem *BASEADDR; -static DEFINE_SPINLOCK(esb_lock); /* Guards the hardware */ -static unsigned long timer_alive; static struct pci_dev *esb_pci; -static unsigned short triggered; /* The status of the watchdog upon boot */ -static char esb_expect_close; /* We can only use 1 card due to the /dev/watchdog restriction */ static int cards_found; @@ -88,7 +85,7 @@ static int cards_found; /* module parameters */ /* 30 sec default heartbeat (1 < heartbeat < 2*1023) */ #define WATCHDOG_HEARTBEAT 30 -static int heartbeat = WATCHDOG_HEARTBEAT; /* in seconds */ +static int heartbeat; /* in seconds */ module_param(heartbeat, int, 0); MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. (1dev, "failed to enable device\n"); goto err_devput; } if (pci_request_region(pdev, 0, ESB_MODULE_NAME)) { - pr_err("failed to request region\n"); + dev_err(&pdev->dev, "failed to request region\n"); goto err_disable; } BASEADDR = pci_ioremap_bar(pdev, 0); if (BASEADDR == NULL) { /* Something's wrong here, BASEADDR has to be set */ - pr_err("failed to get BASEADDR\n"); + dev_err(&pdev->dev, "failed to get BASEADDR\n"); goto err_release; } @@ -396,7 +270,7 @@ static void esb_initdevice(void) /* Check that the WDT isn't already locked */ pci_read_config_byte(esb_pci, ESB_LOCK_REG, &val1); if (val1 & ESB_WDT_LOCK) - pr_warn("nowayout already set\n"); + dev_warn(&esb_pci->dev, "nowayout already set\n"); /* Set the timer to watchdog mode and disable it for now */ pci_write_config_byte(esb_pci, ESB_LOCK_REG, 0x00); @@ -405,14 +279,14 @@ static void esb_initdevice(void) esb_unlock_registers(); val2 = readw(ESB_RELOAD_REG); if (val2 & ESB_WDT_TIMEOUT) - triggered = WDIOF_CARDRESET; + esb_dev.bootstatus = WDIOF_CARDRESET; /* Reset WDT_TIMEOUT flag and timers */ esb_unlock_registers(); writew((ESB_WDT_TIMEOUT | ESB_WDT_RELOAD), ESB_RELOAD_REG); /* And set the correct timeout value */ - esb_timer_set_heartbeat(heartbeat); + esb_timer_set_heartbeat(&esb_dev, esb_dev.timeout); } static int esb_probe(struct pci_dev *pdev, @@ -434,26 +308,25 @@ static int esb_probe(struct pci_dev *pdev, if (!esb_getdevice(pdev) || esb_pci == NULL) return -ENODEV; - /* Check that the heartbeat value is within it's range; - if not reset to the default */ - if (heartbeat < 0x1 || heartbeat > 2 * 0x03ff) { - heartbeat = WATCHDOG_HEARTBEAT; - pr_info("heartbeat value must be 1dev, + "cannot register watchdog device (err=%d)\n", ret); goto err_unmap; } - pr_info("initialized (0x%p). heartbeat=%d sec (nowayout=%d)\n", - BASEADDR, heartbeat, nowayout); + dev_info(&pdev->dev, + "initialized (0x%p). heartbeat=%d sec (nowayout=%d)\n", + BASEADDR, esb_dev.timeout, nowayout); return 0; err_unmap: @@ -466,29 +339,18 @@ err_unmap: static void esb_remove(struct pci_dev *pdev) { - /* Stop the timer before we leave */ - if (!nowayout) - esb_timer_stop(); - - /* Deregister */ - misc_deregister(&esb_miscdev); + watchdog_unregister_device(&esb_dev); iounmap(BASEADDR); pci_release_region(esb_pci, 0); pci_disable_device(esb_pci); esb_pci = NULL; } -static void esb_shutdown(struct pci_dev *pdev) -{ - esb_timer_stop(); -} - static struct pci_driver esb_driver = { .name = ESB_MODULE_NAME, .id_table = esb_pci_tbl, .probe = esb_probe, .remove = esb_remove, - .shutdown = esb_shutdown, }; module_pci_driver(esb_driver); -- cgit v1.2.3 From cf73120ba43172e181905133cfcc5711485fd4b3 Mon Sep 17 00:00:00 2001 From: Radu Rendec Date: Thu, 26 Oct 2017 17:10:14 +0100 Subject: watchdog: i6300esb: support multiple devices Support multiple i6300esb devices simultaneously, by removing the single device restriction in the original design and leveraging the multiple device support of the watchdog subsystem. This patch replaces the global definitions of watchdog device data with a dynamically allocated structure. This structure is allocated during device probe, so multiple independent structures can be allocated if multiple devices are probed. Signed-off-by: Radu Rendec Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/i6300esb.c | 170 +++++++++++++++++++++++--------------------- 1 file changed, 90 insertions(+), 80 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/i6300esb.c b/drivers/watchdog/i6300esb.c index 4f4287d83c21..147462f431de 100644 --- a/drivers/watchdog/i6300esb.c +++ b/drivers/watchdog/i6300esb.c @@ -23,6 +23,7 @@ * Ported driver to kernel 2.6 * 20171016 Radu Rendec * Change driver to use the watchdog subsystem + * Add support for multiple 6300ESB devices */ /* @@ -52,10 +53,10 @@ #define ESB_LOCK_REG 0x68 /* WDT lock register */ /* Memory mapped registers */ -#define ESB_TIMER1_REG (BASEADDR + 0x00)/* Timer1 value after each reset */ -#define ESB_TIMER2_REG (BASEADDR + 0x04)/* Timer2 value after each reset */ -#define ESB_GINTSR_REG (BASEADDR + 0x08)/* General Interrupt Status Register */ -#define ESB_RELOAD_REG (BASEADDR + 0x0c)/* Reload register */ +#define ESB_TIMER1_REG(w) ((w)->base + 0x00)/* Timer1 value after each reset */ +#define ESB_TIMER2_REG(w) ((w)->base + 0x04)/* Timer2 value after each reset */ +#define ESB_GINTSR_REG(w) ((w)->base + 0x08)/* General Interrupt Status Reg */ +#define ESB_RELOAD_REG(w) ((w)->base + 0x0c)/* Reload register */ /* Lock register bits */ #define ESB_WDT_FUNC (0x01 << 2) /* Watchdog functionality */ @@ -75,11 +76,7 @@ #define ESB_UNLOCK1 0x80 /* Step 1 to unlock reset registers */ #define ESB_UNLOCK2 0x86 /* Step 2 to unlock reset registers */ -/* internal variables */ -static void __iomem *BASEADDR; -static struct pci_dev *esb_pci; - -/* We can only use 1 card due to the /dev/watchdog restriction */ +/* Probed devices counter; used only for printing the initial info message */ static int cards_found; /* module parameters */ @@ -97,6 +94,15 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); +/* internal variables */ +struct esb_dev { + struct watchdog_device wdd; + void __iomem *base; + struct pci_dev *pdev; +}; + +#define to_esb_dev(wptr) container_of(wptr, struct esb_dev, wdd) + /* * Some i6300ESB specific functions */ @@ -107,35 +113,37 @@ MODULE_PARM_DESC(nowayout, * reload register. After this the appropriate registers can be written * to once before they need to be unlocked again. */ -static inline void esb_unlock_registers(void) +static inline void esb_unlock_registers(struct esb_dev *edev) { - writew(ESB_UNLOCK1, ESB_RELOAD_REG); - writew(ESB_UNLOCK2, ESB_RELOAD_REG); + writew(ESB_UNLOCK1, ESB_RELOAD_REG(edev)); + writew(ESB_UNLOCK2, ESB_RELOAD_REG(edev)); } static int esb_timer_start(struct watchdog_device *wdd) { + struct esb_dev *edev = to_esb_dev(wdd); int _wdd_nowayout = test_bit(WDOG_NO_WAY_OUT, &wdd->status); u8 val; - esb_unlock_registers(); - writew(ESB_WDT_RELOAD, ESB_RELOAD_REG); + esb_unlock_registers(edev); + writew(ESB_WDT_RELOAD, ESB_RELOAD_REG(edev)); /* Enable or Enable + Lock? */ val = ESB_WDT_ENABLE | (_wdd_nowayout ? ESB_WDT_LOCK : 0x00); - pci_write_config_byte(esb_pci, ESB_LOCK_REG, val); + pci_write_config_byte(edev->pdev, ESB_LOCK_REG, val); return 0; } static int esb_timer_stop(struct watchdog_device *wdd) { + struct esb_dev *edev = to_esb_dev(wdd); u8 val; /* First, reset timers as suggested by the docs */ - esb_unlock_registers(); - writew(ESB_WDT_RELOAD, ESB_RELOAD_REG); + esb_unlock_registers(edev); + writew(ESB_WDT_RELOAD, ESB_RELOAD_REG(edev)); /* Then disable the WDT */ - pci_write_config_byte(esb_pci, ESB_LOCK_REG, 0x0); - pci_read_config_byte(esb_pci, ESB_LOCK_REG, &val); + pci_write_config_byte(edev->pdev, ESB_LOCK_REG, 0x0); + pci_read_config_byte(edev->pdev, ESB_LOCK_REG, &val); /* Returns 0 if the timer was disabled, non-zero otherwise */ return val & ESB_WDT_ENABLE; @@ -143,8 +151,10 @@ static int esb_timer_stop(struct watchdog_device *wdd) static int esb_timer_keepalive(struct watchdog_device *wdd) { - esb_unlock_registers(); - writew(ESB_WDT_RELOAD, ESB_RELOAD_REG); + struct esb_dev *edev = to_esb_dev(wdd); + + esb_unlock_registers(edev); + writew(ESB_WDT_RELOAD, ESB_RELOAD_REG(edev)); /* FIXME: Do we need to flush anything here? */ return 0; } @@ -152,6 +162,7 @@ static int esb_timer_keepalive(struct watchdog_device *wdd) static int esb_timer_set_heartbeat(struct watchdog_device *wdd, unsigned int time) { + struct esb_dev *edev = to_esb_dev(wdd); u32 val; /* We shift by 9, so if we are passed a value of 1 sec, @@ -161,16 +172,16 @@ static int esb_timer_set_heartbeat(struct watchdog_device *wdd, val = time << 9; /* Write timer 1 */ - esb_unlock_registers(); - writel(val, ESB_TIMER1_REG); + esb_unlock_registers(edev); + writel(val, ESB_TIMER1_REG(edev)); /* Write timer 2 */ - esb_unlock_registers(); - writel(val, ESB_TIMER2_REG); + esb_unlock_registers(edev); + writel(val, ESB_TIMER2_REG(edev)); /* Reload */ - esb_unlock_registers(); - writew(ESB_WDT_RELOAD, ESB_RELOAD_REG); + esb_unlock_registers(edev); + writew(ESB_WDT_RELOAD, ESB_RELOAD_REG(edev)); /* FIXME: Do we need to flush everything out? */ @@ -196,14 +207,6 @@ static const struct watchdog_ops esb_ops = { .ping = esb_timer_keepalive, }; -static struct watchdog_device esb_dev = { - .info = &esb_info, - .ops = &esb_ops, - .min_timeout = 1, - .max_timeout = 2 * 0x03ff, - .timeout = WATCHDOG_HEARTBEAT, -}; - /* * Data for PCI driver interface */ @@ -217,38 +220,38 @@ MODULE_DEVICE_TABLE(pci, esb_pci_tbl); * Init & exit routines */ -static unsigned char esb_getdevice(struct pci_dev *pdev) +static unsigned char esb_getdevice(struct esb_dev *edev) { - if (pci_enable_device(pdev)) { - dev_err(&pdev->dev, "failed to enable device\n"); + if (pci_enable_device(edev->pdev)) { + dev_err(&edev->pdev->dev, "failed to enable device\n"); goto err_devput; } - if (pci_request_region(pdev, 0, ESB_MODULE_NAME)) { - dev_err(&pdev->dev, "failed to request region\n"); + if (pci_request_region(edev->pdev, 0, ESB_MODULE_NAME)) { + dev_err(&edev->pdev->dev, "failed to request region\n"); goto err_disable; } - BASEADDR = pci_ioremap_bar(pdev, 0); - if (BASEADDR == NULL) { + edev->base = pci_ioremap_bar(edev->pdev, 0); + if (edev->base == NULL) { /* Something's wrong here, BASEADDR has to be set */ - dev_err(&pdev->dev, "failed to get BASEADDR\n"); + dev_err(&edev->pdev->dev, "failed to get BASEADDR\n"); goto err_release; } /* Done */ - esb_pci = pdev; + dev_set_drvdata(&edev->pdev->dev, edev); return 1; err_release: - pci_release_region(pdev, 0); + pci_release_region(edev->pdev, 0); err_disable: - pci_disable_device(pdev); + pci_disable_device(edev->pdev); err_devput: return 0; } -static void esb_initdevice(void) +static void esb_initdevice(struct esb_dev *edev) { u8 val1; u16 val2; @@ -265,33 +268,34 @@ static void esb_initdevice(void) * any interrupts as there is not much we can do with it * right now. */ - pci_write_config_word(esb_pci, ESB_CONFIG_REG, 0x0003); + pci_write_config_word(edev->pdev, ESB_CONFIG_REG, 0x0003); /* Check that the WDT isn't already locked */ - pci_read_config_byte(esb_pci, ESB_LOCK_REG, &val1); + pci_read_config_byte(edev->pdev, ESB_LOCK_REG, &val1); if (val1 & ESB_WDT_LOCK) - dev_warn(&esb_pci->dev, "nowayout already set\n"); + dev_warn(&edev->pdev->dev, "nowayout already set\n"); /* Set the timer to watchdog mode and disable it for now */ - pci_write_config_byte(esb_pci, ESB_LOCK_REG, 0x00); + pci_write_config_byte(edev->pdev, ESB_LOCK_REG, 0x00); /* Check if the watchdog was previously triggered */ - esb_unlock_registers(); - val2 = readw(ESB_RELOAD_REG); + esb_unlock_registers(edev); + val2 = readw(ESB_RELOAD_REG(edev)); if (val2 & ESB_WDT_TIMEOUT) - esb_dev.bootstatus = WDIOF_CARDRESET; + edev->wdd.bootstatus = WDIOF_CARDRESET; /* Reset WDT_TIMEOUT flag and timers */ - esb_unlock_registers(); - writew((ESB_WDT_TIMEOUT | ESB_WDT_RELOAD), ESB_RELOAD_REG); + esb_unlock_registers(edev); + writew((ESB_WDT_TIMEOUT | ESB_WDT_RELOAD), ESB_RELOAD_REG(edev)); /* And set the correct timeout value */ - esb_timer_set_heartbeat(&esb_dev, esb_dev.timeout); + esb_timer_set_heartbeat(&edev->wdd, edev->wdd.timeout); } static int esb_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { + struct esb_dev *edev; int ret; cards_found++; @@ -299,26 +303,32 @@ static int esb_probe(struct pci_dev *pdev, pr_info("Intel 6300ESB WatchDog Timer Driver v%s\n", ESB_VERSION); - if (cards_found > 1) { - pr_err("This driver only supports 1 device\n"); - return -ENODEV; - } + edev = devm_kzalloc(&pdev->dev, sizeof(*edev), GFP_KERNEL); + if (!edev) + return -ENOMEM; /* Check whether or not the hardware watchdog is there */ - if (!esb_getdevice(pdev) || esb_pci == NULL) + edev->pdev = pdev; + if (!esb_getdevice(edev)) return -ENODEV; /* Initialize the watchdog and make sure it does not run */ - if (watchdog_init_timeout(&esb_dev, heartbeat, NULL)) - pr_info("heartbeat value must be 1wdd.info = &esb_info; + edev->wdd.ops = &esb_ops; + edev->wdd.min_timeout = 1; + edev->wdd.max_timeout = 2 * 0x03ff; + edev->wdd.timeout = WATCHDOG_HEARTBEAT; + if (watchdog_init_timeout(&edev->wdd, heartbeat, NULL)) + dev_info(&pdev->dev, + "heartbeat value must be 1wdd.timeout); + watchdog_set_nowayout(&edev->wdd, nowayout); + watchdog_stop_on_reboot(&edev->wdd); + watchdog_stop_on_unregister(&edev->wdd); + esb_initdevice(edev); /* Register the watchdog so that userspace has access to it */ - ret = watchdog_register_device(&esb_dev); + ret = watchdog_register_device(&edev->wdd); if (ret != 0) { dev_err(&pdev->dev, "cannot register watchdog device (err=%d)\n", ret); @@ -326,24 +336,24 @@ static int esb_probe(struct pci_dev *pdev, } dev_info(&pdev->dev, "initialized (0x%p). heartbeat=%d sec (nowayout=%d)\n", - BASEADDR, esb_dev.timeout, nowayout); + edev->base, edev->wdd.timeout, nowayout); return 0; err_unmap: - iounmap(BASEADDR); - pci_release_region(esb_pci, 0); - pci_disable_device(esb_pci); - esb_pci = NULL; + iounmap(edev->base); + pci_release_region(edev->pdev, 0); + pci_disable_device(edev->pdev); return ret; } static void esb_remove(struct pci_dev *pdev) { - watchdog_unregister_device(&esb_dev); - iounmap(BASEADDR); - pci_release_region(esb_pci, 0); - pci_disable_device(esb_pci); - esb_pci = NULL; + struct esb_dev *edev = dev_get_drvdata(&pdev->dev); + + watchdog_unregister_device(&edev->wdd); + iounmap(edev->base); + pci_release_region(edev->pdev, 0); + pci_disable_device(edev->pdev); } static struct pci_driver esb_driver = { -- cgit v1.2.3 From 568d6015dbdef2c437da2fb6e697c43df6a52fea Mon Sep 17 00:00:00 2001 From: Radu Rendec Date: Thu, 26 Oct 2017 17:10:15 +0100 Subject: watchdog: i6300esb: do not hardcode heartbeat limits The minimum, maximum and default values for the watchdog heartbeat (timeout) were hardcoded in several places (including module parameter description and warning message for invalid module parameter value). This patch adds macros for the aforementioned values and replaces all occurences of hardcoded values by these macros. Signed-off-by: Radu Rendec Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/i6300esb.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/i6300esb.c b/drivers/watchdog/i6300esb.c index 147462f431de..4d92925cdc1e 100644 --- a/drivers/watchdog/i6300esb.c +++ b/drivers/watchdog/i6300esb.c @@ -81,12 +81,16 @@ static int cards_found; /* module parameters */ /* 30 sec default heartbeat (1 < heartbeat < 2*1023) */ -#define WATCHDOG_HEARTBEAT 30 +#define ESB_HEARTBEAT_MIN 1 +#define ESB_HEARTBEAT_MAX 2046 +#define ESB_HEARTBEAT_DEFAULT 30 +#define ESB_HEARTBEAT_RANGE __MODULE_STRING(ESB_HEARTBEAT_MIN) \ + "wdd.info = &esb_info; edev->wdd.ops = &esb_ops; - edev->wdd.min_timeout = 1; - edev->wdd.max_timeout = 2 * 0x03ff; - edev->wdd.timeout = WATCHDOG_HEARTBEAT; + edev->wdd.min_timeout = ESB_HEARTBEAT_MIN; + edev->wdd.max_timeout = ESB_HEARTBEAT_MAX; + edev->wdd.timeout = ESB_HEARTBEAT_DEFAULT; if (watchdog_init_timeout(&edev->wdd, heartbeat, NULL)) dev_info(&pdev->dev, - "heartbeat value must be 1wdd.timeout); + "heartbeat value must be " ESB_HEARTBEAT_RANGE + ", using %u\n", edev->wdd.timeout); watchdog_set_nowayout(&edev->wdd, nowayout); watchdog_stop_on_reboot(&edev->wdd); watchdog_stop_on_unregister(&edev->wdd); -- cgit v1.2.3 From 788c2fdb4f6f1d781d9db584f0c9bca54043dc84 Mon Sep 17 00:00:00 2001 From: Radu Rendec Date: Thu, 26 Oct 2017 17:10:16 +0100 Subject: watchdog: i6300esb: remove info message and version number The initial info message (early in the esb_probe() function) is not very useful and we already have a message on successful probe, which includes device identification. If the probe fails (e.g. PCI related errors), additional messages are printed anyway. The version number was only used in the initial info message. Other than that, it serves no useful purpose and it ran out of favor upstream anyway. Signed-off-by: Radu Rendec Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/i6300esb.c | 11 ----------- 1 file changed, 11 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/i6300esb.c b/drivers/watchdog/i6300esb.c index 4d92925cdc1e..950c71a8bb22 100644 --- a/drivers/watchdog/i6300esb.c +++ b/drivers/watchdog/i6300esb.c @@ -30,8 +30,6 @@ * Includes, defines, variables, module parameters, ... */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - #include #include #include @@ -45,7 +43,6 @@ #include /* Module and version information */ -#define ESB_VERSION "0.05" #define ESB_MODULE_NAME "i6300ESB timer" /* PCI configuration registers */ @@ -76,9 +73,6 @@ #define ESB_UNLOCK1 0x80 /* Step 1 to unlock reset registers */ #define ESB_UNLOCK2 0x86 /* Step 2 to unlock reset registers */ -/* Probed devices counter; used only for printing the initial info message */ -static int cards_found; - /* module parameters */ /* 30 sec default heartbeat (1 < heartbeat < 2*1023) */ #define ESB_HEARTBEAT_MIN 1 @@ -302,11 +296,6 @@ static int esb_probe(struct pci_dev *pdev, struct esb_dev *edev; int ret; - cards_found++; - if (cards_found == 1) - pr_info("Intel 6300ESB WatchDog Timer Driver v%s\n", - ESB_VERSION); - edev = devm_kzalloc(&pdev->dev, sizeof(*edev), GFP_KERNEL); if (!edev) return -ENOMEM; -- cgit v1.2.3 From 7692a982698504465640b90f6d64ce62ca0a5bb9 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Sun, 29 Oct 2017 09:51:19 +0200 Subject: watchdog: mei_wdt: don't use of variable length array Fixes sparse warning: drivers/watchdog/mei_wdt.c:530:18: warning: Variable length array is used Signed-off-by: Tomas Winkler Reviewed-by: Alexander Usyskin Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/mei_wdt.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/mei_wdt.c b/drivers/watchdog/mei_wdt.c index ea60b29494fb..b8194b02abe0 100644 --- a/drivers/watchdog/mei_wdt.c +++ b/drivers/watchdog/mei_wdt.c @@ -526,12 +526,11 @@ static ssize_t mei_dbgfs_read_state(struct file *file, char __user *ubuf, size_t cnt, loff_t *ppos) { struct mei_wdt *wdt = file->private_data; - const size_t bufsz = 32; - char buf[bufsz]; + char buf[32]; ssize_t pos; - pos = scnprintf(buf, bufsz, "state: %s\n", - mei_wdt_state_str(wdt->state)); + pos = scnprintf(buf, sizeof(buf), "state: %s\n", + mei_wdt_state_str(wdt->state)); return simple_read_from_buffer(ubuf, cnt, ppos, buf, pos); } -- cgit v1.2.3 From 083ccebb564245d75e956eaa934bcfd0f0a21dea Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Thu, 2 Nov 2017 14:21:36 -0500 Subject: watchdog: advantechwdt: mark expected switch fall-through In preparation to enabling -Wimplicit-fallthrough, mark switch cases where we are expecting to fall through. Notice that in this particular case I replaced "Fall" with a proper "fall through" comment, which is what GCC is expecting to find. Signed-off-by: Gustavo A. R. Silva Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/advantechwdt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/watchdog/advantechwdt.c b/drivers/watchdog/advantechwdt.c index 7d7db0c5a64e..f61944369c1a 100644 --- a/drivers/watchdog/advantechwdt.c +++ b/drivers/watchdog/advantechwdt.c @@ -181,7 +181,7 @@ static long advwdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg) if (advwdt_set_heartbeat(new_timeout)) return -EINVAL; advwdt_ping(); - /* Fall */ + /* fall through */ case WDIOC_GETTIMEOUT: return put_user(timeout, p); default: -- cgit v1.2.3 From a9868bcc4f7c4eea9e1cf3431d46f29999227b35 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Thu, 2 Nov 2017 14:25:09 -0500 Subject: watchdog: alim1535_wdt: mark expected switch fall-through In preparation to enabling -Wimplicit-fallthrough, mark switch cases where we are expecting to fall through. Notice that in this particular case I replaced "Fall" with a proper "fall through" comment, which is what GCC is expecting to find. Signed-off-by: Gustavo A. R. Silva Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/alim1535_wdt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/watchdog/alim1535_wdt.c b/drivers/watchdog/alim1535_wdt.c index 3a17fbd39f8a..60f0c2eb8531 100644 --- a/drivers/watchdog/alim1535_wdt.c +++ b/drivers/watchdog/alim1535_wdt.c @@ -223,8 +223,8 @@ static long ali_ioctl(struct file *file, unsigned int cmd, unsigned long arg) if (ali_settimer(new_timeout)) return -EINVAL; ali_keepalive(); - /* Fall */ } + /* fall through */ case WDIOC_GETTIMEOUT: return put_user(timeout, p); default: -- cgit v1.2.3 From fd846b57a6b0b551cf7a31c301bd9705759ce825 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Thu, 2 Nov 2017 14:28:17 -0500 Subject: watchdog: f71808e_wdt: mark expected switch fall-throughs In preparation to enabling -Wimplicit-fallthrough, mark switch cases where we are expecting to fall through. Notice that in this particular case I replaced "Fall" with a proper "fall through" comment, which is what GCC is expecting to find. Signed-off-by: Gustavo A. R. Silva Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/f71808e_wdt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/f71808e_wdt.c b/drivers/watchdog/f71808e_wdt.c index 8658dba21768..e0678c14480f 100644 --- a/drivers/watchdog/f71808e_wdt.c +++ b/drivers/watchdog/f71808e_wdt.c @@ -627,7 +627,7 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd, if (new_options & WDIOS_ENABLECARD) return watchdog_start(); - + /* fall through */ case WDIOC_KEEPALIVE: watchdog_keepalive(); @@ -641,7 +641,7 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd, return -EINVAL; watchdog_keepalive(); - /* Fall */ + /* fall through */ case WDIOC_GETTIMEOUT: return put_user(watchdog.timeout, uarg.i); -- cgit v1.2.3 From 3d002ea611ad964c28d9a97ab500a0272deec776 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Fri, 3 Nov 2017 09:53:41 -0500 Subject: watchdog: pcwd_usb: mark expected switch fall-through In preparation to enabling -Wimplicit-fallthrough, mark switch cases where we are expecting to fall through. Notice that in this particular case I replaced "Fall" with a proper "fall through" comment, which is what GCC is expecting to find. Signed-off-by: Gustavo A. R. Silva Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/pcwd_usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/watchdog/pcwd_usb.c b/drivers/watchdog/pcwd_usb.c index b9e376c8e2e3..47fe4c5449c9 100644 --- a/drivers/watchdog/pcwd_usb.c +++ b/drivers/watchdog/pcwd_usb.c @@ -456,8 +456,8 @@ static long usb_pcwd_ioctl(struct file *file, unsigned int cmd, return -EINVAL; usb_pcwd_keepalive(usb_pcwd_device); - /* Fall */ } + /* fall through */ case WDIOC_GETTIMEOUT: return put_user(heartbeat, p); -- cgit v1.2.3 From 4d379727afd07fb901003e52673c3d705aa342f6 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Fri, 3 Nov 2017 11:02:30 -0500 Subject: watchdog: pcwd_pci: mark expected switch fall-through In preparation to enabling -Wimplicit-fallthrough, mark switch cases where we are expecting to fall through. Notice that in this particular case I replaced "Fall" with a proper "fall through" comment, which is what GCC is expecting to find. Signed-off-by: Gustavo A. R. Silva Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/pcwd_pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/watchdog/pcwd_pci.c b/drivers/watchdog/pcwd_pci.c index c0d07eef2640..1f78f0908621 100644 --- a/drivers/watchdog/pcwd_pci.c +++ b/drivers/watchdog/pcwd_pci.c @@ -545,8 +545,8 @@ static long pcipcwd_ioctl(struct file *file, unsigned int cmd, return -EINVAL; pcipcwd_keepalive(); - /* Fall */ } + /* fall through */ case WDIOC_GETTIMEOUT: return put_user(heartbeat, p); -- cgit v1.2.3 From e2af3092d05a814d670a045df983fcefb18e7c14 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Fri, 3 Nov 2017 18:03:27 -0500 Subject: watchdog: watchdog_dev: mark expected switch fall-through In preparation to enabling -Wimplicit-fallthrough, mark switch cases where we are expecting to fall through. Notice that in this particular case I replaced "Fall" with a proper "fall through" comment, which is what GCC is expecting to find. Signed-off-by: Gustavo A. R. Silva Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/watchdog_dev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c index 95b96f3cb36f..eb3570096298 100644 --- a/drivers/watchdog/watchdog_dev.c +++ b/drivers/watchdog/watchdog_dev.c @@ -720,7 +720,7 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd, err = watchdog_ping(wdd); if (err < 0) break; - /* Fall */ + /* fall through */ case WDIOC_GETTIMEOUT: /* timeout == 0 means that we don't know the timeout */ if (wdd->timeout == 0) { -- cgit v1.2.3 From 57b6d481f7a59c091b53a18c843d8631fa04f7eb Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Fri, 3 Nov 2017 18:07:23 -0500 Subject: watchdog: wdt_pci: mark expected switch fall-through In preparation to enabling -Wimplicit-fallthrough, mark switch cases where we are expecting to fall through. Notice that in this particular case I replaced "Fall" with a proper "fall through" comment, which is what GCC is expecting to find. Signed-off-by: Gustavo A. R. Silva Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/wdt_pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/watchdog/wdt_pci.c b/drivers/watchdog/wdt_pci.c index bc7addc2dc06..10e2cda0ee5a 100644 --- a/drivers/watchdog/wdt_pci.c +++ b/drivers/watchdog/wdt_pci.c @@ -430,7 +430,7 @@ static long wdtpci_ioctl(struct file *file, unsigned int cmd, if (wdtpci_set_heartbeat(new_heartbeat)) return -EINVAL; wdtpci_ping(); - /* Fall */ + /* fall through */ case WDIOC_GETTIMEOUT: return put_user(heartbeat, p); default: -- cgit v1.2.3 From bc137dfdbec27c0ec5731a89002daded4a4aa1ea Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Thu, 9 Nov 2017 14:39:55 +0100 Subject: watchdog: gpio_wdt: set WDOG_HW_RUNNING in gpio_wdt_stop The first patch above (https://patchwork.kernel.org/patch/9970181/) makes the oops go away, but it just papers over the problem. The real problem is that the watchdog core clears WDOG_HW_RUNNING in watchdog_stop, and the gpio driver fails to set it in its stop function when it doesn't actually stop it. This means that the core doesn't know that it now has responsibility for petting the device, in turn causing the device to reset the system (I hadn't noticed this because the board I'm working on has that reset logic disabled). How about this (other drivers may of course have the same problem, I haven't checked). One might say that ->stop should return an error when the device can't be stopped, but OTOH this brings parity between a device without a ->stop method and a GPIO wd that has always-running set. IOW, I think ->stop should only return an error when an actual attempt to stop the hardware failed. From: Rasmus Villemoes The watchdog framework clears WDOG_HW_RUNNING before calling ->stop. If the driver is unable to stop the device, it is supposed to set that bit again so that the watchdog core takes care of sending heart-beats while the device is not open from user-space. Update the gpio_wdt driver to honour that contract (and get rid of the redundant clearing of WDOG_HW_RUNNING). Fixes: 3c10bbde10 ("watchdog: core: Clear WDOG_HW_RUNNING before calling the stop function") Signed-off-by: Rasmus Villemoes Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/gpio_wdt.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/watchdog/gpio_wdt.c b/drivers/watchdog/gpio_wdt.c index b077c88a5ceb..3ade28190341 100644 --- a/drivers/watchdog/gpio_wdt.c +++ b/drivers/watchdog/gpio_wdt.c @@ -81,7 +81,8 @@ static int gpio_wdt_stop(struct watchdog_device *wdd) if (!priv->always_running) { gpio_wdt_disable(priv); - clear_bit(WDOG_HW_RUNNING, &wdd->status); + } else { + set_bit(WDOG_HW_RUNNING, &wdd->status); } return 0; -- cgit v1.2.3 From 19ce9490aa840164a88aad033200ac8c2117c7da Mon Sep 17 00:00:00 2001 From: Christophe Leroy Date: Wed, 8 Nov 2017 15:39:44 +0100 Subject: watchdog: mpc8xxx: use the core worker function The watchdog core includes a worker function which pings the watchdog until user app starts pinging it and which also pings it if the HW require more frequent pings. Use that function instead of the dedicated timer. In the mean time, we can allow the user to change the timeout. Then change the timeout module parameter to use seconds and use the watchdog_init_timeout() core function. On some HW (eg: the 8xx), SWCRR contains bits unrelated to the watchdog which have to be preserved upon write. This driver has nothing preventing the use of the magic close, so enable it. Signed-off-by: Christophe Leroy Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/mpc8xxx_wdt.c | 83 +++++++++++++++++++----------------------- 1 file changed, 38 insertions(+), 45 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/mpc8xxx_wdt.c b/drivers/watchdog/mpc8xxx_wdt.c index 6610e9217dbc..aca2d6323f8a 100644 --- a/drivers/watchdog/mpc8xxx_wdt.c +++ b/drivers/watchdog/mpc8xxx_wdt.c @@ -22,7 +22,6 @@ #include #include #include -#include #include #include #include @@ -31,10 +30,13 @@ #include #include +#define WATCHDOG_TIMEOUT 10 + struct mpc8xxx_wdt { __be32 res0; __be32 swcrr; /* System watchdog control register */ #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_SWRI 0x00000002 /* Software Watchdog Reset/Interrupt Select bit.*/ #define SWCRR_SWPR 0x00000001 /* Software Watchdog Counter Prescale bit. */ @@ -52,14 +54,15 @@ struct mpc8xxx_wdt_type { struct mpc8xxx_wdt_ddata { struct mpc8xxx_wdt __iomem *base; struct watchdog_device wdd; - struct timer_list timer; spinlock_t lock; + u16 swtc; }; -static u16 timeout = 0xffff; +static u16 timeout; module_param(timeout, ushort, 0); MODULE_PARM_DESC(timeout, - "Watchdog timeout in ticks. (0lock); } -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) { struct mpc8xxx_wdt_ddata *ddata = container_of(w, struct mpc8xxx_wdt_ddata, wdd); - - u32 tmp = SWCRR_SWEN | SWCRR_SWPR; + u32 tmp = in_be32(&ddata->base->swcrr); /* 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) tmp |= SWCRR_SWRI; - tmp |= timeout << 16; - 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; } @@ -118,17 +117,8 @@ static int mpc8xxx_wdt_ping(struct watchdog_device *w) 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 = { - .options = WDIOF_KEEPALIVEPING, + .options = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT, .firmware_version = 1, .identity = "MPC8xxx", }; @@ -137,7 +127,6 @@ static struct watchdog_ops mpc8xxx_wdt_ops = { .owner = THIS_MODULE, .start = mpc8xxx_wdt_start, .ping = mpc8xxx_wdt_ping, - .stop = mpc8xxx_wdt_stop, }; 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; u32 freq = fsl_get_sys_freq(); bool enabled; - unsigned int timeout_sec; wdt_type = of_device_get_match_data(&ofdev->dev); if (!wdt_type) @@ -173,26 +161,17 @@ static int mpc8xxx_wdt_probe(struct platform_device *ofdev) } spin_lock_init(&ddata->lock); - timer_setup(&ddata->timer, mpc8xxx_wdt_timer_ping, 0); ddata->wdd.info = &mpc8xxx_wdt_info, ddata->wdd.ops = &mpc8xxx_wdt_ops, - /* Calculate the timeout in seconds */ - timeout_sec = (timeout * wdt_type->prescaler) / freq; - - ddata->wdd.timeout = timeout_sec; + ddata->wdd.timeout = WATCHDOG_TIMEOUT; + watchdog_init_timeout(&ddata->wdd, timeout, &ofdev->dev); watchdog_set_nowayout(&ddata->wdd, nowayout); - 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 (%d seconds)\n", - reset ? "reset" : "interrupt", timeout, timeout_sec); + ddata->swtc = min(ddata->wdd.timeout * freq / wdt_type->prescaler, + 0xffffU); /* * If the watchdog was previously enabled or we're running on @@ -200,7 +179,22 @@ static int mpc8xxx_wdt_probe(struct platform_device *ofdev) * userspace handles it. */ 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); return 0; @@ -212,7 +206,6 @@ static int mpc8xxx_wdt_remove(struct platform_device *ofdev) pr_crit("Watchdog removed, expect the %s soon!\n", reset ? "reset" : "machine check exception"); - del_timer_sync(&ddata->timer); watchdog_unregister_device(&ddata->wdd); return 0; -- cgit v1.2.3 From 477603467009673d0965ca7ec165a09bd0fcb87e Mon Sep 17 00:00:00 2001 From: Eric Long Date: Mon, 6 Nov 2017 10:46:28 +0800 Subject: watchdog: Add Spreadtrum watchdog driver This patch adds the watchdog driver for Spreadtrum SC9860 platform. Signed-off-by: Eric Long Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/Kconfig | 8 + drivers/watchdog/Makefile | 1 + drivers/watchdog/sprd_wdt.c | 399 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 408 insertions(+) create mode 100644 drivers/watchdog/sprd_wdt.c (limited to 'drivers') diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index be37e102dd30..633173260f5a 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -799,6 +799,14 @@ config RTD119X_WATCHDOG Say Y here to include support for the watchdog timer in Realtek RTD1295 SoCs. +config SPRD_WATCHDOG + tristate "Spreadtrum watchdog support" + depends on ARCH_SPRD || COMPILE_TEST + select WATCHDOG_CORE + help + Say Y here to include watchdog timer supported + by Spreadtrum system. + # AVR32 Architecture config AT32AP700X_WDT diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index b6cf527b31fb..4b374b9e55e8 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -89,6 +89,7 @@ obj-$(CONFIG_ZX2967_WATCHDOG) += zx2967_wdt.o obj-$(CONFIG_STM32_WATCHDOG) += stm32_iwdg.o obj-$(CONFIG_UNIPHIER_WATCHDOG) += uniphier_wdt.o obj-$(CONFIG_RTD119X_WATCHDOG) += rtd119x_wdt.o +obj-$(CONFIG_SPRD_WATCHDOG) += sprd_wdt.o # AVR32 Architecture obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o diff --git a/drivers/watchdog/sprd_wdt.c b/drivers/watchdog/sprd_wdt.c new file mode 100644 index 000000000000..a8b280ff33e0 --- /dev/null +++ b/drivers/watchdog/sprd_wdt.c @@ -0,0 +1,399 @@ +/* + * Spreadtrum watchdog driver + * Copyright (C) 2017 Spreadtrum - http://www.spreadtrum.com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SPRD_WDT_LOAD_LOW 0x0 +#define SPRD_WDT_LOAD_HIGH 0x4 +#define SPRD_WDT_CTRL 0x8 +#define SPRD_WDT_INT_CLR 0xc +#define SPRD_WDT_INT_RAW 0x10 +#define SPRD_WDT_INT_MSK 0x14 +#define SPRD_WDT_CNT_LOW 0x18 +#define SPRD_WDT_CNT_HIGH 0x1c +#define SPRD_WDT_LOCK 0x20 +#define SPRD_WDT_IRQ_LOAD_LOW 0x2c +#define SPRD_WDT_IRQ_LOAD_HIGH 0x30 + +/* WDT_CTRL */ +#define SPRD_WDT_INT_EN_BIT BIT(0) +#define SPRD_WDT_CNT_EN_BIT BIT(1) +#define SPRD_WDT_NEW_VER_EN BIT(2) +#define SPRD_WDT_RST_EN_BIT BIT(3) + +/* WDT_INT_CLR */ +#define SPRD_WDT_INT_CLEAR_BIT BIT(0) +#define SPRD_WDT_RST_CLEAR_BIT BIT(3) + +/* WDT_INT_RAW */ +#define SPRD_WDT_INT_RAW_BIT BIT(0) +#define SPRD_WDT_RST_RAW_BIT BIT(3) +#define SPRD_WDT_LD_BUSY_BIT BIT(4) + +/* 1s equal to 32768 counter steps */ +#define SPRD_WDT_CNT_STEP 32768 + +#define SPRD_WDT_UNLOCK_KEY 0xe551 +#define SPRD_WDT_MIN_TIMEOUT 3 +#define SPRD_WDT_MAX_TIMEOUT 60 + +#define SPRD_WDT_CNT_HIGH_SHIFT 16 +#define SPRD_WDT_LOW_VALUE_MASK GENMASK(15, 0) +#define SPRD_WDT_LOAD_TIMEOUT 1000 + +struct sprd_wdt { + void __iomem *base; + struct watchdog_device wdd; + struct clk *enable; + struct clk *rtc_enable; + int irq; +}; + +static inline struct sprd_wdt *to_sprd_wdt(struct watchdog_device *wdd) +{ + return container_of(wdd, struct sprd_wdt, wdd); +} + +static inline void sprd_wdt_lock(void __iomem *addr) +{ + writel_relaxed(0x0, addr + SPRD_WDT_LOCK); +} + +static inline void sprd_wdt_unlock(void __iomem *addr) +{ + writel_relaxed(SPRD_WDT_UNLOCK_KEY, addr + SPRD_WDT_LOCK); +} + +static irqreturn_t sprd_wdt_isr(int irq, void *dev_id) +{ + struct sprd_wdt *wdt = (struct sprd_wdt *)dev_id; + + sprd_wdt_unlock(wdt->base); + writel_relaxed(SPRD_WDT_INT_CLEAR_BIT, wdt->base + SPRD_WDT_INT_CLR); + sprd_wdt_lock(wdt->base); + watchdog_notify_pretimeout(&wdt->wdd); + return IRQ_HANDLED; +} + +static u32 sprd_wdt_get_cnt_value(struct sprd_wdt *wdt) +{ + u32 val; + + val = readl_relaxed(wdt->base + SPRD_WDT_CNT_HIGH) << + SPRD_WDT_CNT_HIGH_SHIFT; + val |= readl_relaxed(wdt->base + SPRD_WDT_CNT_LOW) & + SPRD_WDT_LOW_VALUE_MASK; + + return val; +} + +static int sprd_wdt_load_value(struct sprd_wdt *wdt, u32 timeout, + u32 pretimeout) +{ + u32 val, delay_cnt = 0; + u32 tmr_step = timeout * SPRD_WDT_CNT_STEP; + u32 prtmr_step = pretimeout * SPRD_WDT_CNT_STEP; + + sprd_wdt_unlock(wdt->base); + writel_relaxed((tmr_step >> SPRD_WDT_CNT_HIGH_SHIFT) & + SPRD_WDT_LOW_VALUE_MASK, wdt->base + SPRD_WDT_LOAD_HIGH); + writel_relaxed((tmr_step & SPRD_WDT_LOW_VALUE_MASK), + wdt->base + SPRD_WDT_LOAD_LOW); + writel_relaxed((prtmr_step >> SPRD_WDT_CNT_HIGH_SHIFT) & + SPRD_WDT_LOW_VALUE_MASK, + wdt->base + SPRD_WDT_IRQ_LOAD_HIGH); + writel_relaxed(prtmr_step & SPRD_WDT_LOW_VALUE_MASK, + wdt->base + SPRD_WDT_IRQ_LOAD_LOW); + sprd_wdt_lock(wdt->base); + + /* + * Waiting the load value operation done, + * it needs two or three RTC clock cycles. + */ + do { + val = readl_relaxed(wdt->base + SPRD_WDT_INT_RAW); + if (!(val & SPRD_WDT_LD_BUSY_BIT)) + break; + + cpu_relax(); + } while (delay_cnt++ < SPRD_WDT_LOAD_TIMEOUT); + + if (delay_cnt >= SPRD_WDT_LOAD_TIMEOUT) + return -EBUSY; + return 0; +} + +static int sprd_wdt_enable(struct sprd_wdt *wdt) +{ + u32 val; + int ret; + + ret = clk_prepare_enable(wdt->enable); + if (ret) + return ret; + ret = clk_prepare_enable(wdt->rtc_enable); + if (ret) + return ret; + + sprd_wdt_unlock(wdt->base); + val = readl_relaxed(wdt->base + SPRD_WDT_CTRL); + val |= SPRD_WDT_NEW_VER_EN; + writel_relaxed(val, wdt->base + SPRD_WDT_CTRL); + sprd_wdt_lock(wdt->base); + return 0; +} + +static void sprd_wdt_disable(void *_data) +{ + struct sprd_wdt *wdt = _data; + + sprd_wdt_unlock(wdt->base); + writel_relaxed(0x0, wdt->base + SPRD_WDT_CTRL); + sprd_wdt_lock(wdt->base); + + clk_disable_unprepare(wdt->rtc_enable); + clk_disable_unprepare(wdt->enable); +} + +static int sprd_wdt_start(struct watchdog_device *wdd) +{ + struct sprd_wdt *wdt = to_sprd_wdt(wdd); + u32 val; + int ret; + + ret = sprd_wdt_load_value(wdt, wdd->timeout, wdd->pretimeout); + if (ret) + return ret; + + sprd_wdt_unlock(wdt->base); + val = readl_relaxed(wdt->base + SPRD_WDT_CTRL); + val |= SPRD_WDT_CNT_EN_BIT | SPRD_WDT_INT_EN_BIT | SPRD_WDT_RST_EN_BIT; + writel_relaxed(val, wdt->base + SPRD_WDT_CTRL); + sprd_wdt_lock(wdt->base); + set_bit(WDOG_HW_RUNNING, &wdd->status); + + return 0; +} + +static int sprd_wdt_stop(struct watchdog_device *wdd) +{ + struct sprd_wdt *wdt = to_sprd_wdt(wdd); + u32 val; + + sprd_wdt_unlock(wdt->base); + val = readl_relaxed(wdt->base + SPRD_WDT_CTRL); + val &= ~(SPRD_WDT_CNT_EN_BIT | SPRD_WDT_RST_EN_BIT | + SPRD_WDT_INT_EN_BIT); + writel_relaxed(val, wdt->base + SPRD_WDT_CTRL); + sprd_wdt_lock(wdt->base); + return 0; +} + +static int sprd_wdt_set_timeout(struct watchdog_device *wdd, + u32 timeout) +{ + struct sprd_wdt *wdt = to_sprd_wdt(wdd); + + if (timeout == wdd->timeout) + return 0; + + wdd->timeout = timeout; + + return sprd_wdt_load_value(wdt, timeout, wdd->pretimeout); +} + +static int sprd_wdt_set_pretimeout(struct watchdog_device *wdd, + u32 new_pretimeout) +{ + struct sprd_wdt *wdt = to_sprd_wdt(wdd); + + if (new_pretimeout < wdd->min_timeout) + return -EINVAL; + + wdd->pretimeout = new_pretimeout; + + return sprd_wdt_load_value(wdt, wdd->timeout, new_pretimeout); +} + +static u32 sprd_wdt_get_timeleft(struct watchdog_device *wdd) +{ + struct sprd_wdt *wdt = to_sprd_wdt(wdd); + u32 val; + + val = sprd_wdt_get_cnt_value(wdt); + val = val / SPRD_WDT_CNT_STEP; + + return val; +} + +static const struct watchdog_ops sprd_wdt_ops = { + .owner = THIS_MODULE, + .start = sprd_wdt_start, + .stop = sprd_wdt_stop, + .set_timeout = sprd_wdt_set_timeout, + .set_pretimeout = sprd_wdt_set_pretimeout, + .get_timeleft = sprd_wdt_get_timeleft, +}; + +static const struct watchdog_info sprd_wdt_info = { + .options = WDIOF_SETTIMEOUT | + WDIOF_PRETIMEOUT | + WDIOF_MAGICCLOSE | + WDIOF_KEEPALIVEPING, + .identity = "Spreadtrum Watchdog Timer", +}; + +static int sprd_wdt_probe(struct platform_device *pdev) +{ + struct resource *wdt_res; + struct sprd_wdt *wdt; + int ret; + + wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); + if (!wdt) + return -ENOMEM; + + wdt_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + wdt->base = devm_ioremap_resource(&pdev->dev, wdt_res); + if (IS_ERR(wdt->base)) { + dev_err(&pdev->dev, "failed to map memory resource\n"); + return PTR_ERR(wdt->base); + } + + wdt->enable = devm_clk_get(&pdev->dev, "enable"); + if (IS_ERR(wdt->enable)) { + dev_err(&pdev->dev, "can't get the enable clock\n"); + return PTR_ERR(wdt->enable); + } + + wdt->rtc_enable = devm_clk_get(&pdev->dev, "rtc_enable"); + if (IS_ERR(wdt->rtc_enable)) { + dev_err(&pdev->dev, "can't get the rtc enable clock\n"); + return PTR_ERR(wdt->rtc_enable); + } + + wdt->irq = platform_get_irq(pdev, 0); + if (wdt->irq < 0) { + dev_err(&pdev->dev, "failed to get IRQ resource\n"); + return wdt->irq; + } + + ret = devm_request_irq(&pdev->dev, wdt->irq, sprd_wdt_isr, + IRQF_NO_SUSPEND, "sprd-wdt", (void *)wdt); + if (ret) { + dev_err(&pdev->dev, "failed to register irq\n"); + return ret; + } + + wdt->wdd.info = &sprd_wdt_info; + wdt->wdd.ops = &sprd_wdt_ops; + wdt->wdd.parent = &pdev->dev; + wdt->wdd.min_timeout = SPRD_WDT_MIN_TIMEOUT; + wdt->wdd.max_timeout = SPRD_WDT_MAX_TIMEOUT; + wdt->wdd.timeout = SPRD_WDT_MAX_TIMEOUT; + + ret = sprd_wdt_enable(wdt); + if (ret) { + dev_err(&pdev->dev, "failed to enable wdt\n"); + return ret; + } + ret = devm_add_action(&pdev->dev, sprd_wdt_disable, wdt); + if (ret) { + sprd_wdt_disable(wdt); + dev_err(&pdev->dev, "Failed to add wdt disable action\n"); + return ret; + } + + watchdog_set_nowayout(&wdt->wdd, WATCHDOG_NOWAYOUT); + watchdog_init_timeout(&wdt->wdd, 0, &pdev->dev); + + ret = devm_watchdog_register_device(&pdev->dev, &wdt->wdd); + if (ret) { + sprd_wdt_disable(wdt); + dev_err(&pdev->dev, "failed to register watchdog\n"); + return ret; + } + platform_set_drvdata(pdev, wdt); + + return 0; +} + +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); + + if (watchdog_active(wdd)) + sprd_wdt_stop(&wdt->wdd); + sprd_wdt_disable(wdt); + + return 0; +} + +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); + int ret; + + ret = sprd_wdt_enable(wdt); + if (ret) + return ret; + + if (watchdog_active(wdd)) { + ret = sprd_wdt_start(&wdt->wdd); + if (ret) { + sprd_wdt_disable(wdt); + return ret; + } + } + + return 0; +} + +static const struct dev_pm_ops sprd_wdt_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(sprd_wdt_pm_suspend, + sprd_wdt_pm_resume) +}; + +static const struct of_device_id sprd_wdt_match_table[] = { + { .compatible = "sprd,sp9860-wdt", }, + {}, +}; +MODULE_DEVICE_TABLE(of, sprd_wdt_match_table); + +static struct platform_driver sprd_watchdog_driver = { + .probe = sprd_wdt_probe, + .driver = { + .name = "sprd-wdt", + .of_match_table = sprd_wdt_match_table, + .pm = &sprd_wdt_pm_ops, + }, +}; +module_platform_driver(sprd_watchdog_driver); + +MODULE_AUTHOR("Eric Long "); +MODULE_DESCRIPTION("Spreadtrum Watchdog Timer Controller Driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 18cffd68e0c44fc09817ea2fb66bebe8a9188093 Mon Sep 17 00:00:00 2001 From: Radu Rendec Date: Wed, 15 Nov 2017 19:34:41 +0000 Subject: watchdog: xen_wdt: use the watchdog subsystem Change the xen_wdt driver to use the watchdog subsystem instead of registering and manipulating the char device directly through the misc API. This is mainly getting rid of the "write" and "ioctl" methods and part of the watchdog control logic (which are all implemented by the watchdog subsystem). Even though the watchdog subsystem supports registering and handling multiple watchdog devices at the same time, the xen_wdt driver has an inherent limitation of only one device due to the way the Xen hypervisor exposes watchdog functionality. However, the driver can now coexist with other watchdog devices (supported by different drivers). Signed-off-by: Radu Rendec Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/xen_wdt.c | 245 ++++++++++----------------------------------- 1 file changed, 51 insertions(+), 194 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/xen_wdt.c b/drivers/watchdog/xen_wdt.c index 5dd5c3494d55..51576e15a086 100644 --- a/drivers/watchdog/xen_wdt.c +++ b/drivers/watchdog/xen_wdt.c @@ -9,9 +9,7 @@ * 2 of the License, or (at your option) any later version. */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#define DRV_NAME "wdt" +#define DRV_NAME "xen_wdt" #define DRV_VERSION "0.01" #include @@ -21,25 +19,20 @@ #include #include #include -#include #include #include #include -#include -#include #include #include #include #include static struct platform_device *platform_device; -static DEFINE_SPINLOCK(wdt_lock); static struct sched_watchdog wdt; static time64_t wdt_expires; -static bool is_active, expect_release; #define WATCHDOG_TIMEOUT 60 /* in seconds */ -static unsigned int timeout = WATCHDOG_TIMEOUT; +static unsigned int timeout; module_param(timeout, uint, S_IRUGO); MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds " "(default=" __MODULE_STRING(WATCHDOG_TIMEOUT) ")"); @@ -49,20 +42,18 @@ module_param(nowayout, bool, S_IRUGO); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started " "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); -static inline time64_t set_timeout(void) +static inline time64_t set_timeout(struct watchdog_device *wdd) { - wdt.timeout = timeout; - return ktime_get_seconds() + timeout; + wdt.timeout = wdd->timeout; + return ktime_get_seconds() + wdd->timeout; } -static int xen_wdt_start(void) +static int xen_wdt_start(struct watchdog_device *wdd) { time64_t expires; int err; - spin_lock(&wdt_lock); - - expires = set_timeout(); + expires = set_timeout(wdd); if (!wdt.id) err = HYPERVISOR_sched_op(SCHEDOP_watchdog, &wdt); else @@ -74,36 +65,28 @@ static int xen_wdt_start(void) } else BUG_ON(!err); - spin_unlock(&wdt_lock); - return err; } -static int xen_wdt_stop(void) +static int xen_wdt_stop(struct watchdog_device *wdd) { int err = 0; - spin_lock(&wdt_lock); - wdt.timeout = 0; if (wdt.id) err = HYPERVISOR_sched_op(SCHEDOP_watchdog, &wdt); if (!err) wdt.id = 0; - spin_unlock(&wdt_lock); - return err; } -static int xen_wdt_kick(void) +static int xen_wdt_kick(struct watchdog_device *wdd) { time64_t expires; int err; - spin_lock(&wdt_lock); - - expires = set_timeout(); + expires = set_timeout(wdd); if (wdt.id) err = HYPERVISOR_sched_op(SCHEDOP_watchdog, &wdt); else @@ -111,195 +94,72 @@ static int xen_wdt_kick(void) if (!err) wdt_expires = expires; - spin_unlock(&wdt_lock); - return err; } -static int xen_wdt_open(struct inode *inode, struct file *file) +static unsigned int xen_wdt_get_timeleft(struct watchdog_device *wdd) { - 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); + return wdt_expires - ktime_get_seconds(); } -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; -} - -static ssize_t xen_wdt_write(struct file *file, const char __user *data, - size_t len, loff_t *ppos) -{ - /* See if we got the magic character 'V' and reload the timer */ - 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, - 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, - }; - - 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_get_seconds(); - return put_user(retval, argp); - } - - return -ENOTTY; -} +static struct watchdog_info xen_wdt_info = { + .identity = DRV_NAME, + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, +}; -static const struct file_operations xen_wdt_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .write = xen_wdt_write, - .unlocked_ioctl = xen_wdt_ioctl, - .open = xen_wdt_open, - .release = xen_wdt_release, +static const struct watchdog_ops xen_wdt_ops = { + .owner = THIS_MODULE, + .start = xen_wdt_start, + .stop = xen_wdt_stop, + .ping = xen_wdt_kick, + .get_timeleft = xen_wdt_get_timeleft, }; -static struct miscdevice xen_wdt_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &xen_wdt_fops, +static struct watchdog_device xen_wdt_dev = { + .info = &xen_wdt_info, + .ops = &xen_wdt_ops, + .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 }; int ret = HYPERVISOR_sched_op(SCHEDOP_watchdog, &wd); - switch (ret) { - case -EINVAL: - if (!timeout) { - timeout = WATCHDOG_TIMEOUT; - pr_info("timeout value invalid, using %d\n", timeout); - } - - ret = misc_register(&xen_wdt_miscdev); - if (ret) { - pr_err("cannot register miscdev on minor=%d (%d)\n", - WATCHDOG_MINOR, ret); - break; - } - - pr_info("initialized (timeout=%ds, nowayout=%d)\n", - timeout, nowayout); - break; - - case -ENOSYS: - pr_info("not supported\n"); - ret = -ENODEV; - break; - - default: - pr_info("bogus return value %d\n", ret); - break; + if (ret == -ENOSYS) { + dev_err(&pdev->dev, "watchdog not supported by hypervisor\n"); + return -ENODEV; } - return ret; -} + if (ret != -EINVAL) { + dev_err(&pdev->dev, "unexpected hypervisor error (%d)\n", ret); + return -ENODEV; + } -static int xen_wdt_remove(struct platform_device *dev) -{ - /* Stop the timer before we leave */ - if (!nowayout) - xen_wdt_stop(); + if (watchdog_init_timeout(&xen_wdt_dev, timeout, NULL)) + dev_info(&pdev->dev, "timeout value invalid, using %d\n", + xen_wdt_dev.timeout); + watchdog_set_nowayout(&xen_wdt_dev, nowayout); + watchdog_stop_on_reboot(&xen_wdt_dev); + watchdog_stop_on_unregister(&xen_wdt_dev); + + 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; + } - misc_deregister(&xen_wdt_miscdev); + dev_info(&pdev->dev, "initialized (timeout=%ds, nowayout=%d)\n", + xen_wdt_dev.timeout, nowayout); 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) { typeof(wdt.id) id = wdt.id; - int rc = xen_wdt_stop(); + int rc = xen_wdt_stop(&xen_wdt_dev); wdt.id = id; return rc; @@ -310,13 +170,11 @@ static int xen_wdt_resume(struct platform_device *dev) if (!wdt.id) return 0; wdt.id = 0; - return xen_wdt_start(); + return xen_wdt_start(&xen_wdt_dev); } static struct platform_driver xen_wdt_driver = { .probe = xen_wdt_probe, - .remove = xen_wdt_remove, - .shutdown = xen_wdt_shutdown, .suspend = xen_wdt_suspend, .resume = xen_wdt_resume, .driver = { @@ -351,7 +209,6 @@ static void __exit xen_wdt_cleanup_module(void) { platform_device_unregister(platform_device); platform_driver_unregister(&xen_wdt_driver); - pr_info("module unloaded\n"); } module_init(xen_wdt_init_module); -- cgit v1.2.3 From c41fe7cdbd548121a59323a26d45e20ff08cc2eb Mon Sep 17 00:00:00 2001 From: Radu Rendec Date: Wed, 15 Nov 2017 19:34:42 +0000 Subject: watchdog: xen_wdt: remove info message and version number The initial info message (early in the xen_wdt_init_module() function) is not very useful and we already have a message on successful probe. If the probe fails, additional messages are printed anyway. The version number serves no useful purpose and it ran out of favor upstream anyway. Signed-off-by: Radu Rendec Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/xen_wdt.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/xen_wdt.c b/drivers/watchdog/xen_wdt.c index 51576e15a086..f1c016d015b3 100644 --- a/drivers/watchdog/xen_wdt.c +++ b/drivers/watchdog/xen_wdt.c @@ -10,7 +10,6 @@ */ #define DRV_NAME "xen_wdt" -#define DRV_VERSION "0.01" #include #include @@ -189,8 +188,6 @@ static int __init xen_wdt_init_module(void) if (!xen_domain()) return -ENODEV; - pr_info("Xen WatchDog Timer Driver v%s\n", DRV_VERSION); - err = platform_driver_register(&xen_wdt_driver); if (err) return err; @@ -216,5 +213,4 @@ module_exit(xen_wdt_cleanup_module); MODULE_AUTHOR("Jan Beulich "); MODULE_DESCRIPTION("Xen WatchDog Timer Driver"); -MODULE_VERSION(DRV_VERSION); MODULE_LICENSE("GPL"); -- cgit v1.2.3 From bf0209747e97fa5e75da8ab52ccb99faad96659c Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 17 Nov 2017 15:22:03 +0100 Subject: watchdog: pcwd_usb: remove unneeded DRIVER_LICENSE #define There is no need to #define the license of the driver, just put it in the MODULE_LICENSE() line directly as a text string. This allows tools that check that the module license matches the source code license to work properly, as there is no need to unwind the unneeded dereference, especially when it is defined just a few lines above from where it is used. Cc: Wim Van Sebroeck Cc: Guenter Roeck Reported-by: Philippe Ombredanne Signed-off-by: Greg Kroah-Hartman Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/pcwd_usb.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/pcwd_usb.c b/drivers/watchdog/pcwd_usb.c index 47fe4c5449c9..4d02f26156f9 100644 --- a/drivers/watchdog/pcwd_usb.c +++ b/drivers/watchdog/pcwd_usb.c @@ -49,12 +49,11 @@ #define DRIVER_VERSION "1.02" #define DRIVER_AUTHOR "Wim Van Sebroeck " #define DRIVER_DESC "Berkshire USB-PC Watchdog driver" -#define DRIVER_LICENSE "GPL" #define DRIVER_NAME "pcwd_usb" MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_LICENSE(DRIVER_LICENSE); +MODULE_LICENSE("GPL"); #define WATCHDOG_HEARTBEAT 0 /* default heartbeat = delay-time from dip-switches */ -- cgit v1.2.3 From 49620a283eed91cd2fcd87af4f5381e8f03285aa Mon Sep 17 00:00:00 2001 From: Benjamin Gaignard Date: Thu, 30 Nov 2017 09:48:04 +0100 Subject: watchdog: stm32: Fix copyright Uniformize STMicroelectronics copyrights header Add SPDX identifier Signed-off-by: Benjamin Gaignard Acked-by: Alexandre TORGUE CC: Yannick Fertre Acked-by: Yannick Fertre Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/stm32_iwdg.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/stm32_iwdg.c b/drivers/watchdog/stm32_iwdg.c index be64a8699de3..c97ad5619cb0 100644 --- a/drivers/watchdog/stm32_iwdg.c +++ b/drivers/watchdog/stm32_iwdg.c @@ -1,12 +1,12 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Driver for STM32 Independent Watchdog * - * Copyright (C) Yannick Fertre 2017 - * Author: Yannick Fertre + * Copyright (C) STMicroelectronics 2017 + * Author: Yannick Fertre for STMicroelectronics. * * This driver is based on tegra_wdt.c * - * License terms: GNU General Public License (GPL), version 2 */ #include -- cgit v1.2.3 From 82c8b5c9774e64f8e1b9abec876fcfa8d8e3d791 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Mon, 20 Nov 2017 11:06:45 -0600 Subject: watchdog: eurotechwdt: mark expected switch fall-through In preparation to enabling -Wimplicit-fallthrough, mark switch cases where we are expecting to fall through. Notice that in this particular case I replaced "Fall" with a proper "fall through" comment, which is what GCC is expecting to find. Signed-off-by: Gustavo A. R. Silva Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/eurotechwdt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/watchdog/eurotechwdt.c b/drivers/watchdog/eurotechwdt.c index 38e96712264f..47f77a6fdfd6 100644 --- a/drivers/watchdog/eurotechwdt.c +++ b/drivers/watchdog/eurotechwdt.c @@ -290,7 +290,7 @@ static long eurwdt_ioctl(struct file *file, eurwdt_timeout = time; eurwdt_set_timeout(time); spin_unlock(&eurwdt_lock); - /* Fall */ + /* fall through */ case WDIOC_GETTIMEOUT: return put_user(eurwdt_timeout, p); -- cgit v1.2.3 From 13983959e804691b09322c2c8d71cd94f365655f Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Mon, 20 Nov 2017 11:22:21 -0600 Subject: watchdog: ib700wdt: mark expected switch fall-through In preparation to enabling -Wimplicit-fallthrough, mark switch cases where we are expecting to fall through. Notice that in this particular case I replaced "Fall" with a proper "fall through" comment, which is what GCC is expecting to find. Signed-off-by: Gustavo A. R. Silva Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/ib700wdt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/watchdog/ib700wdt.c b/drivers/watchdog/ib700wdt.c index f2e4e1eeb8dd..cc262284a6aa 100644 --- a/drivers/watchdog/ib700wdt.c +++ b/drivers/watchdog/ib700wdt.c @@ -218,7 +218,7 @@ static long ibwdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg) if (ibwdt_set_heartbeat(new_margin)) return -EINVAL; ibwdt_ping(); - /* Fall */ + /* fall through */ case WDIOC_GETTIMEOUT: return put_user(timeout, p); -- cgit v1.2.3 From aeebc6ba88ba3758ad95467ff6191fabf2074c13 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 6 Dec 2017 22:02:37 +0100 Subject: watchdog: hpwdt: fix unused variable warning The new hpwdt_my_nmi() function is used conditionally, which produces a harmless warning in some configurations: drivers/watchdog/hpwdt.c:478:12: error: 'hpwdt_my_nmi' defined but not used [-Werror=unused-function] This moves it inside of the #ifdef that protects its caller, to silence the warning. Fixes: 621174a92851 ("watchdog: hpwdt: Check source of NMI") Signed-off-by: Arnd Bergmann Reviewed-by: Jerry Hoemann Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/hpwdt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/watchdog/hpwdt.c b/drivers/watchdog/hpwdt.c index e61658310381..f1f00dfc0e68 100644 --- a/drivers/watchdog/hpwdt.c +++ b/drivers/watchdog/hpwdt.c @@ -475,12 +475,12 @@ static int hpwdt_time_left(void) return TICKS_TO_SECS(ioread16(hpwdt_timer_reg)); } +#ifdef CONFIG_HPWDT_NMI_DECODING static int hpwdt_my_nmi(void) { return ioread8(hpwdt_nmistat) & 0x6; } -#ifdef CONFIG_HPWDT_NMI_DECODING /* * NMI Handler */ -- cgit v1.2.3 From 8bc86475a0ded71bb0cfa95d88b1708292741356 Mon Sep 17 00:00:00 2001 From: Corentin Labbe Date: Wed, 13 Dec 2017 20:41:40 +0100 Subject: watchdog: document watchdog_init_timeout() wdd parameter All parameters of watchdog_init_timeout() are documented with exception of wdd, thus generating a build warning. This patch document it and so remove the following build warning: drivers/watchdog/watchdog_core.c:113: warning: No description found for parameter 'wdd' Signed-off-by: Corentin Labbe Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/watchdog_core.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/watchdog/watchdog_core.c b/drivers/watchdog/watchdog_core.c index 8a8d952f8df9..eb8fa25f8eb2 100644 --- a/drivers/watchdog/watchdog_core.c +++ b/drivers/watchdog/watchdog_core.c @@ -97,6 +97,7 @@ static void watchdog_check_min_max_timeout(struct watchdog_device *wdd) /** * watchdog_init_timeout() - initialize the timeout field + * @wdd: watchdog device * @timeout_parm: timeout module parameter * @dev: Device that stores the timeout-sec property * -- cgit v1.2.3 From 71d1f058844de6052835549f06c113f5014a8290 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Mon, 11 Dec 2017 11:21:08 -0600 Subject: watchdog: davinci_wdt: add restart function This adds a restart function to the davinci watchdog timer driver. This is copied from arch/arm/mach-davinci/time.c and will allow us to remove the code from there. Signed-off-by: David Lechner Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/davinci_wdt.c | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) (limited to 'drivers') diff --git a/drivers/watchdog/davinci_wdt.c b/drivers/watchdog/davinci_wdt.c index 2f46487af86d..3e4c592c239f 100644 --- a/drivers/watchdog/davinci_wdt.c +++ b/drivers/watchdog/davinci_wdt.c @@ -140,6 +140,42 @@ static unsigned int davinci_wdt_get_timeleft(struct watchdog_device *wdd) 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 = { .options = WDIOF_KEEPALIVEPING, .identity = "DaVinci/Keystone Watchdog", @@ -151,6 +187,7 @@ static const struct watchdog_ops davinci_wdt_ops = { .stop = davinci_wdt_ping, .ping = davinci_wdt_ping, .get_timeleft = davinci_wdt_get_timeleft, + .restart = davinci_wdt_restart, }; 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_nowayout(wdd, 1); + watchdog_set_restart_priority(wdd, 128); wdt_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); davinci_wdt->base = devm_ioremap_resource(dev, wdt_mem); -- cgit v1.2.3 From 38a1222ae4f364d5bd5221fe305dbb0889f45d15 Mon Sep 17 00:00:00 2001 From: Christophe Leroy Date: Fri, 8 Dec 2017 11:18:35 +0100 Subject: watchdog: core: make sure the watchdog worker always works When running a command like 'chrt -f 50 dd if=/dev/zero of=/dev/null', the watchdog_worker fails to service the HW watchdog and the HW watchdog fires long before the watchdog soft timeout. At the moment, the watchdog_worker is invoked as a delayed work. Delayed works are handled by non realtime kernel threads. The WQ_HIGHPRI flag only increases the niceness of that threads. This patch replaces the delayed work logic by kthread delayed work, and sets the associated kernel task to SCHED_FIFO with the highest priority, in order to ensure that the watchdog worker will run as soon as possible. Signed-off-by: Christophe Leroy Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/watchdog_dev.c | 48 +++++++++++++++++++++++------------------ 1 file changed, 27 insertions(+), 21 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c index eb3570096298..68bc29e6e79e 100644 --- a/drivers/watchdog/watchdog_dev.c +++ b/drivers/watchdog/watchdog_dev.c @@ -39,6 +39,7 @@ #include /* For timeout functions */ #include /* For printk/panic/... */ #include /* For data references */ +#include /* For kthread_delayed_work */ #include /* For handling misc devices */ #include /* For module stuff/... */ #include /* For mutexes */ @@ -46,9 +47,10 @@ #include /* For memory functions */ #include /* For standard types (like size_t) */ #include /* For watchdog specific items */ -#include /* For workqueue */ #include /* For copy_to_user/put_user/... */ +#include /* For struct sched_param */ + #include "watchdog_core.h" #include "watchdog_pretimeout.h" @@ -67,7 +69,7 @@ struct watchdog_core_data { struct mutex lock; unsigned long last_keepalive; unsigned long last_hw_keepalive; - struct delayed_work work; + struct kthread_delayed_work work; unsigned long status; /* Internal status bits */ #define _WDOG_DEV_OPEN 0 /* Opened ? */ #define _WDOG_ALLOW_RELEASE 1 /* Did we receive the magic char ? */ @@ -79,7 +81,7 @@ static dev_t watchdog_devt; /* Reference to watchdog device behind /dev/watchdog */ static struct watchdog_core_data *old_wd_data; -static struct workqueue_struct *watchdog_wq; +static struct kthread_worker *watchdog_kworker; static bool handle_boot_enabled = IS_ENABLED(CONFIG_WATCHDOG_HANDLE_BOOT_ENABLED); @@ -140,9 +142,10 @@ static inline void watchdog_update_worker(struct watchdog_device *wdd) long t = watchdog_next_keepalive(wdd); if (t > 0) - mod_delayed_work(watchdog_wq, &wd_data->work, t); + kthread_mod_delayed_work(watchdog_kworker, + &wd_data->work, t); } else { - cancel_delayed_work(&wd_data->work); + kthread_cancel_delayed_work_sync(&wd_data->work); } } @@ -154,8 +157,8 @@ static int __watchdog_ping(struct watchdog_device *wdd) int err; if (time_is_after_jiffies(earliest_keepalive)) { - mod_delayed_work(watchdog_wq, &wd_data->work, - earliest_keepalive - jiffies); + kthread_mod_delayed_work(watchdog_kworker, &wd_data->work, + earliest_keepalive - jiffies); return 0; } @@ -203,12 +206,13 @@ static bool watchdog_worker_should_ping(struct watchdog_core_data *wd_data) return wdd && (watchdog_active(wdd) || watchdog_hw_running(wdd)); } -static void watchdog_ping_work(struct work_struct *work) +static void watchdog_ping_work(struct kthread_work *work) { struct watchdog_core_data *wd_data; - wd_data = container_of(to_delayed_work(work), struct watchdog_core_data, - work); + wd_data = container_of(container_of(work, struct kthread_delayed_work, + work), + struct watchdog_core_data, work); mutex_lock(&wd_data->lock); if (watchdog_worker_should_ping(wd_data)) @@ -921,10 +925,10 @@ static int watchdog_cdev_register(struct watchdog_device *wdd, dev_t devno) wd_data->wdd = wdd; wdd->wd_data = wd_data; - if (!watchdog_wq) + if (IS_ERR_OR_NULL(watchdog_kworker)) return -ENODEV; - INIT_DELAYED_WORK(&wd_data->work, watchdog_ping_work); + kthread_init_delayed_work(&wd_data->work, watchdog_ping_work); if (wdd->id == 0) { old_wd_data = wd_data; @@ -970,7 +974,8 @@ static int watchdog_cdev_register(struct watchdog_device *wdd, dev_t devno) __module_get(wdd->ops->owner); kref_get(&wd_data->kref); if (handle_boot_enabled) - queue_delayed_work(watchdog_wq, &wd_data->work, 0); + kthread_queue_delayed_work(watchdog_kworker, + &wd_data->work, 0); else pr_info("watchdog%d running and kernel based pre-userspace handler disabled\n", wdd->id); @@ -1007,7 +1012,7 @@ static void watchdog_cdev_unregister(struct watchdog_device *wdd) watchdog_stop(wdd); } - cancel_delayed_work_sync(&wd_data->work); + kthread_cancel_delayed_work_sync(&wd_data->work); kref_put(&wd_data->kref, watchdog_core_data_release); } @@ -1111,13 +1116,14 @@ void watchdog_dev_unregister(struct watchdog_device *wdd) int __init watchdog_dev_init(void) { int err; + struct sched_param param = {.sched_priority = MAX_RT_PRIO - 1,}; - watchdog_wq = alloc_workqueue("watchdogd", - WQ_HIGHPRI | WQ_MEM_RECLAIM, 0); - if (!watchdog_wq) { - pr_err("Failed to create watchdog workqueue\n"); - return -ENOMEM; + watchdog_kworker = kthread_create_worker(0, "watchdogd"); + if (IS_ERR(watchdog_kworker)) { + pr_err("Failed to create watchdog kworker\n"); + return PTR_ERR(watchdog_kworker); } + sched_setscheduler(watchdog_kworker->task, SCHED_FIFO, ¶m); err = class_register(&watchdog_class); if (err < 0) { @@ -1136,7 +1142,7 @@ int __init watchdog_dev_init(void) err_alloc: class_unregister(&watchdog_class); err_register: - destroy_workqueue(watchdog_wq); + kthread_destroy_worker(watchdog_kworker); return err; } @@ -1150,7 +1156,7 @@ void __exit watchdog_dev_exit(void) { unregister_chrdev_region(watchdog_devt, MAX_DOGS); class_unregister(&watchdog_class); - destroy_workqueue(watchdog_wq); + kthread_destroy_worker(watchdog_kworker); } module_param(handle_boot_enabled, bool, 0444); -- cgit v1.2.3 From 0be267255cef64e1c58475baa7b25568355a3816 Mon Sep 17 00:00:00 2001 From: Martin Kaiser Date: Mon, 1 Jan 2018 18:26:47 +0100 Subject: watchdog: imx2_wdt: restore previous timeout after suspend+resume When the watchdog device is suspended, its timeout is set to the maximum value. During resume, the previously set timeout should be restored. This does not work at the moment. The suspend function calls imx2_wdt_set_timeout(wdog, IMX2_WDT_MAX_TIME); and resume reverts this by calling imx2_wdt_set_timeout(wdog, wdog->timeout); However, imx2_wdt_set_timeout() updates wdog->timeout. Therefore, wdog->timeout is set to IMX2_WDT_MAX_TIME when we enter the resume function. Fix this by adding a new function __imx2_wdt_set_timeout() which only updates the hardware settings. imx2_wdt_set_timeout() now calls __imx2_wdt_set_timeout() and then saves the new timeout to wdog->timeout. During suspend, we call __imx2_wdt_set_timeout() directly so that wdog->timeout won't be updated and we can restore the previous value during resume. This approach makes wdog->timeout different from the actual setting in the hardware which is usually not a good thing. However, the two differ only while we're suspended and no kernel code is running, so it should be ok in this case. Signed-off-by: Martin Kaiser Cc: stable@vger.kernel.org Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/imx2_wdt.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/imx2_wdt.c b/drivers/watchdog/imx2_wdt.c index 4874b0f18650..518dfa1047cb 100644 --- a/drivers/watchdog/imx2_wdt.c +++ b/drivers/watchdog/imx2_wdt.c @@ -169,15 +169,21 @@ static int imx2_wdt_ping(struct watchdog_device *wdog) return 0; } -static int imx2_wdt_set_timeout(struct watchdog_device *wdog, - unsigned int new_timeout) +static void __imx2_wdt_set_timeout(struct watchdog_device *wdog, + unsigned int new_timeout) { 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, 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; } @@ -371,7 +377,11 @@ static int imx2_wdt_suspend(struct device *dev) /* The watchdog IP block is running */ 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); } -- cgit v1.2.3 From 392d39a863583eb6439e2ec543818250b57ef99d Mon Sep 17 00:00:00 2001 From: André Draszik Date: Fri, 12 Jan 2018 09:44:53 +0000 Subject: watchdog: mt7621: set WDOG_HW_RUNNING bit when appropriate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the watchdog hardware is enabled/running during boot, e.g. due to a boot loader configuring it, we must tell the watchdog framework about this fact so that it can ping the watchdog until userspace opens the device and takes over control. Do so using the WDOG_HW_RUNNING flag that exists for exactly that use-case. Given the watchdog driver core doesn't know what timeout was originally set by whoever started the watchdog (boot loader), we make sure to update the timeout in the hardware according to what the watchdog core thinks it is. Signed-off-by: André Draszik Cc: linux-watchdog@vger.kernel.org Cc: John Crispin Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/mt7621_wdt.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'drivers') diff --git a/drivers/watchdog/mt7621_wdt.c b/drivers/watchdog/mt7621_wdt.c index db38f8017218..eec57e5e1eae 100644 --- a/drivers/watchdog/mt7621_wdt.c +++ b/drivers/watchdog/mt7621_wdt.c @@ -105,6 +105,11 @@ static int mt7621_wdt_bootcause(void) 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 = { .identity = "Mediatek Watchdog", .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, @@ -144,6 +149,20 @@ static int mt7621_wdt_probe(struct platform_device *pdev) watchdog_init_timeout(&mt7621_wdt_dev, mt7621_wdt_dev.max_timeout, &pdev->dev); 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); -- cgit v1.2.3 From 1d2e5eb521a7edeac9e9decd3d526b04e307bb41 Mon Sep 17 00:00:00 2001 From: André Draszik Date: Fri, 12 Jan 2018 09:44:54 +0000 Subject: watchdog: mt7621: switch to using managed devm_watchdog_register_device() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This does the necessary cleanup on driver unload automatically. Signed-off-by: André Draszik Cc: linux-watchdog@vger.kernel.org Cc: John Crispin Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/mt7621_wdt.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/mt7621_wdt.c b/drivers/watchdog/mt7621_wdt.c index eec57e5e1eae..5c4a764717c4 100644 --- a/drivers/watchdog/mt7621_wdt.c +++ b/drivers/watchdog/mt7621_wdt.c @@ -133,7 +133,6 @@ static struct watchdog_device mt7621_wdt_dev = { static int mt7621_wdt_probe(struct platform_device *pdev) { struct resource *res; - int ret; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); mt7621_wdt_base = devm_ioremap_resource(&pdev->dev, res); @@ -164,16 +163,7 @@ static int mt7621_wdt_probe(struct platform_device *pdev) set_bit(WDOG_HW_RUNNING, &mt7621_wdt_dev.status); } - ret = watchdog_register_device(&mt7621_wdt_dev); - - return 0; -} - -static int mt7621_wdt_remove(struct platform_device *pdev) -{ - watchdog_unregister_device(&mt7621_wdt_dev); - - return 0; + return devm_watchdog_register_device(&pdev->dev, &mt7621_wdt_dev); } static void mt7621_wdt_shutdown(struct platform_device *pdev) @@ -189,7 +179,6 @@ MODULE_DEVICE_TABLE(of, mt7621_wdt_match); static struct platform_driver mt7621_wdt_driver = { .probe = mt7621_wdt_probe, - .remove = mt7621_wdt_remove, .shutdown = mt7621_wdt_shutdown, .driver = { .name = KBUILD_MODNAME, -- cgit v1.2.3 From 1ff688209e2ed23f699269b9733993e2ce123fd2 Mon Sep 17 00:00:00 2001 From: Christophe Leroy Date: Thu, 18 Jan 2018 12:11:21 +0100 Subject: watchdog: core: make sure the watchdog_worker is not deferred commit 4cd13c21b207e ("softirq: Let ksoftirqd do its job") has the effect of deferring timer handling in case of high CPU load, hence delaying the delayed work allthought the worker is running which high realtime priority. As hrtimers are not managed by softirqs, this patch replaces the delayed work by a plain work and uses an hrtimer to schedule that work. Signed-off-by: Christophe Leroy Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/watchdog_dev.c | 86 +++++++++++++++++++++++++---------------- 1 file changed, 52 insertions(+), 34 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c index 68bc29e6e79e..ffbdc4642ea5 100644 --- a/drivers/watchdog/watchdog_dev.c +++ b/drivers/watchdog/watchdog_dev.c @@ -36,10 +36,10 @@ #include /* For the -ENODEV/... values */ #include /* For file operations */ #include /* For __init/__exit/... */ -#include /* For timeout functions */ +#include /* For hrtimers */ #include /* For printk/panic/... */ #include /* For data references */ -#include /* For kthread_delayed_work */ +#include /* For kthread_work */ #include /* For handling misc devices */ #include /* For module stuff/... */ #include /* For mutexes */ @@ -67,9 +67,10 @@ struct watchdog_core_data { struct cdev cdev; struct watchdog_device *wdd; struct mutex lock; - unsigned long last_keepalive; - unsigned long last_hw_keepalive; - struct kthread_delayed_work work; + ktime_t last_keepalive; + ktime_t last_hw_keepalive; + struct hrtimer timer; + struct kthread_work work; unsigned long status; /* Internal status bits */ #define _WDOG_DEV_OPEN 0 /* Opened ? */ #define _WDOG_ALLOW_RELEASE 1 /* Did we receive the magic char ? */ @@ -109,18 +110,19 @@ static inline bool watchdog_need_worker(struct watchdog_device *wdd) (t && !watchdog_active(wdd) && watchdog_hw_running(wdd)); } -static long watchdog_next_keepalive(struct watchdog_device *wdd) +static ktime_t watchdog_next_keepalive(struct watchdog_device *wdd) { struct watchdog_core_data *wd_data = wdd->wd_data; unsigned int timeout_ms = wdd->timeout * 1000; - unsigned long keepalive_interval; - unsigned long last_heartbeat; - unsigned long virt_timeout; + ktime_t keepalive_interval; + ktime_t last_heartbeat, latest_heartbeat; + ktime_t virt_timeout; unsigned int hw_heartbeat_ms; - virt_timeout = wd_data->last_keepalive + msecs_to_jiffies(timeout_ms); + virt_timeout = ktime_add(wd_data->last_keepalive, + ms_to_ktime(timeout_ms)); hw_heartbeat_ms = min_not_zero(timeout_ms, wdd->max_hw_heartbeat_ms); - keepalive_interval = msecs_to_jiffies(hw_heartbeat_ms / 2); + keepalive_interval = ms_to_ktime(hw_heartbeat_ms / 2); if (!watchdog_active(wdd)) return keepalive_interval; @@ -130,8 +132,11 @@ static long watchdog_next_keepalive(struct watchdog_device *wdd) * after the most recent ping from userspace, the last * worker ping has to come in hw_heartbeat_ms before this timeout. */ - last_heartbeat = virt_timeout - msecs_to_jiffies(hw_heartbeat_ms); - return min_t(long, last_heartbeat - jiffies, keepalive_interval); + last_heartbeat = ktime_sub(virt_timeout, ms_to_ktime(hw_heartbeat_ms)); + latest_heartbeat = ktime_sub(last_heartbeat, ktime_get()); + if (ktime_before(latest_heartbeat, keepalive_interval)) + return latest_heartbeat; + return keepalive_interval; } static inline void watchdog_update_worker(struct watchdog_device *wdd) @@ -139,30 +144,33 @@ static inline void watchdog_update_worker(struct watchdog_device *wdd) struct watchdog_core_data *wd_data = wdd->wd_data; if (watchdog_need_worker(wdd)) { - long t = watchdog_next_keepalive(wdd); + ktime_t t = watchdog_next_keepalive(wdd); if (t > 0) - kthread_mod_delayed_work(watchdog_kworker, - &wd_data->work, t); + hrtimer_start(&wd_data->timer, t, HRTIMER_MODE_REL); } else { - kthread_cancel_delayed_work_sync(&wd_data->work); + hrtimer_cancel(&wd_data->timer); } } static int __watchdog_ping(struct watchdog_device *wdd) { struct watchdog_core_data *wd_data = wdd->wd_data; - unsigned long earliest_keepalive = wd_data->last_hw_keepalive + - msecs_to_jiffies(wdd->min_hw_heartbeat_ms); + ktime_t earliest_keepalive, now; int err; - if (time_is_after_jiffies(earliest_keepalive)) { - kthread_mod_delayed_work(watchdog_kworker, &wd_data->work, - earliest_keepalive - jiffies); + earliest_keepalive = ktime_add(wd_data->last_hw_keepalive, + ms_to_ktime(wdd->min_hw_heartbeat_ms)); + now = ktime_get(); + + if (ktime_after(earliest_keepalive, now)) { + hrtimer_start(&wd_data->timer, + ktime_sub(earliest_keepalive, now), + HRTIMER_MODE_REL); return 0; } - wd_data->last_hw_keepalive = jiffies; + wd_data->last_hw_keepalive = now; if (wdd->ops->ping) err = wdd->ops->ping(wdd); /* ping the watchdog */ @@ -195,7 +203,7 @@ static int watchdog_ping(struct watchdog_device *wdd) set_bit(_WDOG_KEEPALIVE, &wd_data->status); - wd_data->last_keepalive = jiffies; + wd_data->last_keepalive = ktime_get(); return __watchdog_ping(wdd); } @@ -210,9 +218,7 @@ static void watchdog_ping_work(struct kthread_work *work) { struct watchdog_core_data *wd_data; - wd_data = container_of(container_of(work, struct kthread_delayed_work, - work), - struct watchdog_core_data, work); + wd_data = container_of(work, struct watchdog_core_data, work); mutex_lock(&wd_data->lock); if (watchdog_worker_should_ping(wd_data)) @@ -220,6 +226,16 @@ static void watchdog_ping_work(struct kthread_work *work) mutex_unlock(&wd_data->lock); } +static enum hrtimer_restart watchdog_timer_expired(struct hrtimer *timer) +{ + struct watchdog_core_data *wd_data; + + wd_data = container_of(timer, struct watchdog_core_data, timer); + + kthread_queue_work(watchdog_kworker, &wd_data->work); + return HRTIMER_NORESTART; +} + /* * watchdog_start: wrapper to start the watchdog. * @wdd: the watchdog device to start @@ -234,7 +250,7 @@ static void watchdog_ping_work(struct kthread_work *work) static int watchdog_start(struct watchdog_device *wdd) { struct watchdog_core_data *wd_data = wdd->wd_data; - unsigned long started_at; + ktime_t started_at; int err; if (watchdog_active(wdd)) @@ -242,7 +258,7 @@ static int watchdog_start(struct watchdog_device *wdd) set_bit(_WDOG_KEEPALIVE, &wd_data->status); - started_at = jiffies; + started_at = ktime_get(); if (watchdog_hw_running(wdd) && wdd->ops->ping) err = wdd->ops->ping(wdd); else @@ -928,7 +944,9 @@ static int watchdog_cdev_register(struct watchdog_device *wdd, dev_t devno) if (IS_ERR_OR_NULL(watchdog_kworker)) return -ENODEV; - kthread_init_delayed_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); + wd_data->timer.function = watchdog_timer_expired; if (wdd->id == 0) { old_wd_data = wd_data; @@ -964,7 +982,7 @@ static int watchdog_cdev_register(struct watchdog_device *wdd, dev_t devno) } /* Record time of most recent heartbeat as 'just before now'. */ - wd_data->last_hw_keepalive = jiffies - 1; + wd_data->last_hw_keepalive = ktime_sub(ktime_get(), 1); /* * If the watchdog is running, prevent its driver from being unloaded, @@ -974,8 +992,7 @@ static int watchdog_cdev_register(struct watchdog_device *wdd, dev_t devno) __module_get(wdd->ops->owner); kref_get(&wd_data->kref); if (handle_boot_enabled) - kthread_queue_delayed_work(watchdog_kworker, - &wd_data->work, 0); + hrtimer_start(&wd_data->timer, 0, HRTIMER_MODE_REL); else pr_info("watchdog%d running and kernel based pre-userspace handler disabled\n", wdd->id); @@ -1012,7 +1029,8 @@ static void watchdog_cdev_unregister(struct watchdog_device *wdd) watchdog_stop(wdd); } - kthread_cancel_delayed_work_sync(&wd_data->work); + hrtimer_cancel(&wd_data->timer); + kthread_cancel_work_sync(&wd_data->work); kref_put(&wd_data->kref, watchdog_core_data_release); } -- cgit v1.2.3 From 2b750cffe1ed05c9001d9524b5815e1f50461a44 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 24 Dec 2017 13:04:06 -0800 Subject: watchdog: sp5100_tco: Always use SP5100_IO_PM_{INDEX_REG,DATA_REG} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SP5100_IO_PM_INDEX_REG and SB800_IO_PM_INDEX_REG are used inconsistently and define the same value. Just use SP5100_IO_PM_INDEX_REG throughout. Do the same for SP5100_IO_PM_DATA_REG and SB800_IO_PM_DATA_REG. Use helper functions to access the indexed registers. Cc: Zoltán Böszörményi Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/sp5100_tco.c | 94 ++++++++++++++++++++++--------------------- drivers/watchdog/sp5100_tco.h | 7 +--- 2 files changed, 51 insertions(+), 50 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/sp5100_tco.c b/drivers/watchdog/sp5100_tco.c index 028618c5eeba..05f9d27a306a 100644 --- a/drivers/watchdog/sp5100_tco.c +++ b/drivers/watchdog/sp5100_tco.c @@ -48,7 +48,6 @@ static u32 tcobase_phys; static u32 tco_wdt_fired; static void __iomem *tcobase; -static unsigned int pm_iobase; static DEFINE_SPINLOCK(tco_lock); /* Guards the hardware */ static unsigned long timer_alive; static char tco_expect_close; @@ -132,25 +131,38 @@ static int tco_timer_set_heartbeat(int t) return 0; } -static void tco_timer_enable(void) +static u8 sp5100_tco_read_pm_reg8(u8 index) +{ + outb(index, SP5100_IO_PM_INDEX_REG); + return inb(SP5100_IO_PM_DATA_REG); +} + +static void sp5100_tco_update_pm_reg8(u8 index, u8 reset, u8 set) { - int val; + u8 val; + outb(index, SP5100_IO_PM_INDEX_REG); + val = inb(SP5100_IO_PM_DATA_REG); + val &= reset; + val |= set; + outb(val, SP5100_IO_PM_DATA_REG); +} + +static void tco_timer_enable(void) +{ if (!tco_has_sp5100_reg_layout(sp5100_tco_pci)) { /* For SB800 or later */ /* Set the Watchdog timer resolution to 1 sec */ - outb(SB800_PM_WATCHDOG_CONFIG, SB800_IO_PM_INDEX_REG); - val = inb(SB800_IO_PM_DATA_REG); - val |= SB800_PM_WATCHDOG_SECOND_RES; - outb(val, SB800_IO_PM_DATA_REG); + sp5100_tco_update_pm_reg8(SB800_PM_WATCHDOG_CONFIG, + 0xff, SB800_PM_WATCHDOG_SECOND_RES); /* Enable watchdog decode bit and watchdog timer */ - outb(SB800_PM_WATCHDOG_CONTROL, SB800_IO_PM_INDEX_REG); - val = inb(SB800_IO_PM_DATA_REG); - val |= SB800_PCI_WATCHDOG_DECODE_EN; - val &= ~SB800_PM_WATCHDOG_DISABLE; - outb(val, SB800_IO_PM_DATA_REG); + sp5100_tco_update_pm_reg8(SB800_PM_WATCHDOG_CONTROL, + ~SB800_PM_WATCHDOG_DISABLE, + SB800_PCI_WATCHDOG_DECODE_EN); } else { + u32 val; + /* For SP5100 or SB7x0 */ /* Enable watchdog decode bit */ pci_read_config_dword(sp5100_tco_pci, @@ -164,11 +176,9 @@ static void tco_timer_enable(void) val); /* Enable Watchdog timer and set the resolution to 1 sec */ - outb(SP5100_PM_WATCHDOG_CONTROL, SP5100_IO_PM_INDEX_REG); - val = inb(SP5100_IO_PM_DATA_REG); - val |= SP5100_PM_WATCHDOG_SECOND_RES; - val &= ~SP5100_PM_WATCHDOG_DISABLE; - outb(val, SP5100_IO_PM_DATA_REG); + sp5100_tco_update_pm_reg8(SP5100_PM_WATCHDOG_CONTROL, + ~SP5100_PM_WATCHDOG_DISABLE, + SP5100_PM_WATCHDOG_SECOND_RES); } } @@ -321,6 +331,17 @@ static const struct pci_device_id sp5100_tco_pci_tbl[] = { }; MODULE_DEVICE_TABLE(pci, sp5100_tco_pci_tbl); +static u8 sp5100_tco_read_pm_reg32(u8 index) +{ + u32 val = 0; + int i; + + for (i = 3; i >= 0; i--) + val = (val << 8) + sp5100_tco_read_pm_reg8(index + i); + + return val; +} + /* * Init & exit routines */ @@ -329,7 +350,7 @@ static unsigned char sp5100_tco_setupdevice(void) struct pci_dev *dev = NULL; const char *dev_name = NULL; u32 val; - u32 index_reg, data_reg, base_addr; + u8 base_addr; /* Match the PCI device */ for_each_pci_dev(dev) { @@ -351,35 +372,25 @@ static unsigned char sp5100_tco_setupdevice(void) */ if (tco_has_sp5100_reg_layout(sp5100_tco_pci)) { dev_name = SP5100_DEVNAME; - index_reg = SP5100_IO_PM_INDEX_REG; - data_reg = SP5100_IO_PM_DATA_REG; base_addr = SP5100_PM_WATCHDOG_BASE; } else { dev_name = SB800_DEVNAME; - index_reg = SB800_IO_PM_INDEX_REG; - data_reg = SB800_IO_PM_DATA_REG; base_addr = SB800_PM_WATCHDOG_BASE; } /* Request the IO ports used by this driver */ - pm_iobase = SP5100_IO_PM_INDEX_REG; - if (!request_region(pm_iobase, SP5100_PM_IOPORTS_SIZE, dev_name)) { - pr_err("I/O address 0x%04x already in use\n", pm_iobase); + if (!request_region(SP5100_IO_PM_INDEX_REG, SP5100_PM_IOPORTS_SIZE, + dev_name)) { + pr_err("I/O address 0x%04x already in use\n", + SP5100_IO_PM_INDEX_REG); goto exit; } /* * First, Find the watchdog timer MMIO address from indirect I/O. + * Low three bits of BASE are reserved. */ - outb(base_addr+3, index_reg); - val = inb(data_reg); - outb(base_addr+2, index_reg); - val = val << 8 | inb(data_reg); - outb(base_addr+1, index_reg); - val = val << 8 | inb(data_reg); - outb(base_addr+0, index_reg); - /* Low three bits of BASE are reserved */ - val = val << 8 | (inb(data_reg) & 0xf8); + val = sp5100_tco_read_pm_reg32(base_addr) & 0xfffffff8; pr_debug("Got 0x%04x from indirect I/O\n", val); @@ -400,14 +411,7 @@ static unsigned char sp5100_tco_setupdevice(void) SP5100_SB_RESOURCE_MMIO_BASE, &val); } else { /* Read SBResource_MMIO from AcpiMmioEn(PM_Reg: 24h) */ - outb(SB800_PM_ACPI_MMIO_EN+3, SB800_IO_PM_INDEX_REG); - val = inb(SB800_IO_PM_DATA_REG); - outb(SB800_PM_ACPI_MMIO_EN+2, SB800_IO_PM_INDEX_REG); - val = val << 8 | inb(SB800_IO_PM_DATA_REG); - outb(SB800_PM_ACPI_MMIO_EN+1, SB800_IO_PM_INDEX_REG); - val = val << 8 | inb(SB800_IO_PM_DATA_REG); - outb(SB800_PM_ACPI_MMIO_EN+0, SB800_IO_PM_INDEX_REG); - val = val << 8 | inb(SB800_IO_PM_DATA_REG); + val = sp5100_tco_read_pm_reg32(SB800_PM_ACPI_MMIO_EN); } /* The SBResource_MMIO is enabled and mapped memory space? */ @@ -470,7 +474,7 @@ setup_wdt: unreg_mem_region: release_mem_region(tcobase_phys, SP5100_WDT_MEM_MAP_SIZE); unreg_region: - release_region(pm_iobase, SP5100_PM_IOPORTS_SIZE); + release_region(SP5100_IO_PM_INDEX_REG, SP5100_PM_IOPORTS_SIZE); exit: return 0; } @@ -517,7 +521,7 @@ static int sp5100_tco_init(struct platform_device *dev) exit: iounmap(tcobase); release_mem_region(tcobase_phys, SP5100_WDT_MEM_MAP_SIZE); - release_region(pm_iobase, SP5100_PM_IOPORTS_SIZE); + release_region(SP5100_IO_PM_INDEX_REG, SP5100_PM_IOPORTS_SIZE); return ret; } @@ -531,7 +535,7 @@ static void sp5100_tco_cleanup(void) misc_deregister(&sp5100_tco_miscdev); iounmap(tcobase); release_mem_region(tcobase_phys, SP5100_WDT_MEM_MAP_SIZE); - release_region(pm_iobase, SP5100_PM_IOPORTS_SIZE); + release_region(SP5100_IO_PM_INDEX_REG, SP5100_PM_IOPORTS_SIZE); } static int sp5100_tco_remove(struct platform_device *dev) diff --git a/drivers/watchdog/sp5100_tco.h b/drivers/watchdog/sp5100_tco.h index 1af4dee71337..f495fe03887a 100644 --- a/drivers/watchdog/sp5100_tco.h +++ b/drivers/watchdog/sp5100_tco.h @@ -24,10 +24,11 @@ * 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_DATA_REG 0xCD7 +/* For SP5100/SB7x0 chipset */ #define SP5100_SB_RESOURCE_MMIO_BASE 0x9C #define SP5100_PM_WATCHDOG_CONTROL 0x69 @@ -44,11 +45,7 @@ #define SP5100_DEVNAME "SP5100 TCO" - /* 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_WATCHDOG_CONTROL 0x48 #define SB800_PM_WATCHDOG_BASE 0x48 -- cgit v1.2.3 From f541c09ebfc61697b586b38c9ebaf4b70defb278 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 24 Dec 2017 13:04:07 -0800 Subject: watchdog: sp5100_tco: Fix watchdog disable bit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to all published information, the watchdog disable bit for SB800 compatible controllers is bit 1 of PM register 0x48, not bit 2. For the most part that doesn't matter in practice, since the bit has to be cleared to enable watchdog address decoding, which is the default setting, but it still needs to be fixed. Cc: Zoltán Böszörményi Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/sp5100_tco.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/watchdog/sp5100_tco.h b/drivers/watchdog/sp5100_tco.h index f495fe03887a..2622cfe23dc1 100644 --- a/drivers/watchdog/sp5100_tco.h +++ b/drivers/watchdog/sp5100_tco.h @@ -52,7 +52,7 @@ #define SB800_PM_WATCHDOG_CONFIG 0x4C #define SB800_PCI_WATCHDOG_DECODE_EN (1 << 0) -#define SB800_PM_WATCHDOG_DISABLE (1 << 2) +#define SB800_PM_WATCHDOG_DISABLE (1 << 1) #define SB800_PM_WATCHDOG_SECOND_RES (3 << 0) #define SB800_ACPI_MMIO_DECODE_EN (1 << 0) #define SB800_ACPI_MMIO_SEL (1 << 1) -- cgit v1.2.3 From 16e7730bd7ec7f4ec19628e5ddd172df28cf996d Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 24 Dec 2017 13:04:08 -0800 Subject: watchdog: sp5100_tco: Use request_muxed_region where possible MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use request_muxed_region for multiplexed IO memory regions. Also, SP5100_IO_PM_INDEX_REG/SP5100_IO_PM_DATA_REG are only used during initialization; it is unnecessary to keep the address range reserved. Cc: Zoltán Böszörményi Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/sp5100_tco.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/sp5100_tco.c b/drivers/watchdog/sp5100_tco.c index 05f9d27a306a..11109ac959e2 100644 --- a/drivers/watchdog/sp5100_tco.c +++ b/drivers/watchdog/sp5100_tco.c @@ -379,8 +379,8 @@ static unsigned char sp5100_tco_setupdevice(void) } /* Request the IO ports used by this driver */ - if (!request_region(SP5100_IO_PM_INDEX_REG, SP5100_PM_IOPORTS_SIZE, - dev_name)) { + if (!request_muxed_region(SP5100_IO_PM_INDEX_REG, + SP5100_PM_IOPORTS_SIZE, dev_name)) { pr_err("I/O address 0x%04x already in use\n", SP5100_IO_PM_INDEX_REG); goto exit; @@ -468,6 +468,7 @@ setup_wdt: */ tco_timer_stop(); + release_region(SP5100_IO_PM_INDEX_REG, SP5100_PM_IOPORTS_SIZE); /* Done */ return 1; @@ -521,7 +522,6 @@ static int sp5100_tco_init(struct platform_device *dev) exit: iounmap(tcobase); release_mem_region(tcobase_phys, SP5100_WDT_MEM_MAP_SIZE); - release_region(SP5100_IO_PM_INDEX_REG, SP5100_PM_IOPORTS_SIZE); return ret; } @@ -535,7 +535,6 @@ static void sp5100_tco_cleanup(void) misc_deregister(&sp5100_tco_miscdev); iounmap(tcobase); release_mem_region(tcobase_phys, SP5100_WDT_MEM_MAP_SIZE); - release_region(SP5100_IO_PM_INDEX_REG, SP5100_PM_IOPORTS_SIZE); } static int sp5100_tco_remove(struct platform_device *dev) -- cgit v1.2.3 From 23dfe140057baa165daebadf8f4600e3603e0954 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 24 Dec 2017 13:04:09 -0800 Subject: watchdog: sp5100_tco: Use standard error codes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit By using standard error codes, we can identify and return more than one error condition. Cc: Zoltán Böszörményi Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/sp5100_tco.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/sp5100_tco.c b/drivers/watchdog/sp5100_tco.c index 11109ac959e2..0e816f2cdb07 100644 --- a/drivers/watchdog/sp5100_tco.c +++ b/drivers/watchdog/sp5100_tco.c @@ -345,12 +345,13 @@ static u8 sp5100_tco_read_pm_reg32(u8 index) /* * Init & exit routines */ -static unsigned char sp5100_tco_setupdevice(void) +static int sp5100_tco_setupdevice(void) { struct pci_dev *dev = NULL; const char *dev_name = NULL; u32 val; u8 base_addr; + int ret; /* Match the PCI device */ for_each_pci_dev(dev) { @@ -361,7 +362,7 @@ static unsigned char sp5100_tco_setupdevice(void) } if (!sp5100_tco_pci) - return 0; + return -ENODEV; pr_info("PCI Vendor ID: 0x%x, Device ID: 0x%x, Revision ID: 0x%x\n", sp5100_tco_pci->vendor, sp5100_tco_pci->device, @@ -383,7 +384,7 @@ static unsigned char sp5100_tco_setupdevice(void) SP5100_PM_IOPORTS_SIZE, dev_name)) { pr_err("I/O address 0x%04x already in use\n", SP5100_IO_PM_INDEX_REG); - goto exit; + return -EBUSY; } /* @@ -433,6 +434,7 @@ static unsigned char sp5100_tco_setupdevice(void) pr_debug("SBResource_MMIO is disabled(0x%04x)\n", val); pr_notice("failed to find MMIO address, giving up.\n"); + ret = -ENODEV; goto unreg_region; setup_wdt: @@ -441,6 +443,7 @@ setup_wdt: tcobase = ioremap(val, SP5100_WDT_MEM_MAP_SIZE); if (!tcobase) { pr_err("failed to get tcobase address\n"); + ret = -ENOMEM; goto unreg_mem_region; } @@ -470,14 +473,13 @@ setup_wdt: release_region(SP5100_IO_PM_INDEX_REG, SP5100_PM_IOPORTS_SIZE); /* Done */ - return 1; + return 0; unreg_mem_region: release_mem_region(tcobase_phys, SP5100_WDT_MEM_MAP_SIZE); unreg_region: release_region(SP5100_IO_PM_INDEX_REG, SP5100_PM_IOPORTS_SIZE); -exit: - return 0; + return ret; } static int sp5100_tco_init(struct platform_device *dev) @@ -488,8 +490,9 @@ static int sp5100_tco_init(struct platform_device *dev) * Check whether or not the hardware watchdog is there. If found, then * set it up. */ - if (!sp5100_tco_setupdevice()) - return -ENODEV; + ret = sp5100_tco_setupdevice(); + if (ret) + return ret; /* Check to see if last reboot was due to watchdog timeout */ pr_info("Last reboot was %striggered by watchdog.\n", -- cgit v1.2.3 From e189410cbef1374ec29d42b72df0d58d5c7e193c Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 24 Dec 2017 13:04:10 -0800 Subject: watchdog: sp5100_tco: Clean up sp5100_tco_setupdevice MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are too many unnecessary goto statements in sp5100_tco_setupdevice(). Rearrange the code and limit goto statements to error handling. Cc: Zoltán Böszörményi Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/sp5100_tco.c | 62 ++++++++++++++++++++----------------------- 1 file changed, 29 insertions(+), 33 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/sp5100_tco.c b/drivers/watchdog/sp5100_tco.c index 0e816f2cdb07..5a13ab483c50 100644 --- a/drivers/watchdog/sp5100_tco.c +++ b/drivers/watchdog/sp5100_tco.c @@ -396,48 +396,44 @@ static int sp5100_tco_setupdevice(void) pr_debug("Got 0x%04x from indirect I/O\n", val); /* Check MMIO address conflict */ - if (request_mem_region_exclusive(val, SP5100_WDT_MEM_MAP_SIZE, - dev_name)) - goto setup_wdt; - else + if (!request_mem_region_exclusive(val, SP5100_WDT_MEM_MAP_SIZE, + dev_name)) { pr_debug("MMIO address 0x%04x already in use\n", val); + /* + * Secondly, Find the watchdog timer MMIO address + * from SBResource_MMIO register. + */ + if (tco_has_sp5100_reg_layout(sp5100_tco_pci)) { + /* Read SBResource_MMIO from PCI config(PCI_Reg: 9Ch) */ + pci_read_config_dword(sp5100_tco_pci, + SP5100_SB_RESOURCE_MMIO_BASE, + &val); + } else { + /* Read SBResource_MMIO from AcpiMmioEn(PM_Reg: 24h) */ + val = sp5100_tco_read_pm_reg32(SB800_PM_ACPI_MMIO_EN); + } - /* - * Secondly, Find the watchdog timer MMIO address - * from SBResource_MMIO register. - */ - if (tco_has_sp5100_reg_layout(sp5100_tco_pci)) { - /* Read SBResource_MMIO from PCI config(PCI_Reg: 9Ch) */ - pci_read_config_dword(sp5100_tco_pci, - SP5100_SB_RESOURCE_MMIO_BASE, &val); - } else { - /* Read SBResource_MMIO from AcpiMmioEn(PM_Reg: 24h) */ - val = sp5100_tco_read_pm_reg32(SB800_PM_ACPI_MMIO_EN); - } - - /* The SBResource_MMIO is enabled and mapped memory space? */ - if ((val & (SB800_ACPI_MMIO_DECODE_EN | SB800_ACPI_MMIO_SEL)) == + /* The SBResource_MMIO is enabled and mapped memory space? */ + if ((val & (SB800_ACPI_MMIO_DECODE_EN | SB800_ACPI_MMIO_SEL)) != SB800_ACPI_MMIO_DECODE_EN) { + pr_notice("failed to find MMIO address, giving up.\n"); + ret = -ENODEV; + goto unreg_region; + } /* Clear unnecessary the low twelve bits */ val &= ~0xFFF; /* Add the Watchdog Timer offset to base address. */ val += SB800_PM_WDT_MMIO_OFFSET; /* Check MMIO address conflict */ - if (request_mem_region_exclusive(val, SP5100_WDT_MEM_MAP_SIZE, - dev_name)) { - pr_debug("Got 0x%04x from SBResource_MMIO register\n", - val); - goto setup_wdt; - } else + if (!request_mem_region_exclusive(val, SP5100_WDT_MEM_MAP_SIZE, + dev_name)) { pr_debug("MMIO address 0x%04x already in use\n", val); - } else - pr_debug("SBResource_MMIO is disabled(0x%04x)\n", val); - - pr_notice("failed to find MMIO address, giving up.\n"); - ret = -ENODEV; - goto unreg_region; + ret = -EBUSY; + goto unreg_region; + } + pr_debug("Got 0x%04x from SBResource_MMIO register\n", val); + } -setup_wdt: tcobase_phys = val; tcobase = ioremap(val, SP5100_WDT_MEM_MAP_SIZE); @@ -472,7 +468,7 @@ setup_wdt: tco_timer_stop(); release_region(SP5100_IO_PM_INDEX_REG, SP5100_PM_IOPORTS_SIZE); - /* Done */ + return 0; unreg_mem_region: -- cgit v1.2.3 From a34834435f46225d9371b77599f44e3ec2e43b08 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 24 Dec 2017 13:04:11 -0800 Subject: watchdog: sp5100_tco: Match PCI device early MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Match PCI device in module init function, not in the probe function. It is pointless trying to probe if we can determine early that the device is not supported. Cc: Zoltán Böszörményi Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/sp5100_tco.c | 66 ++++++++++++++++++++----------------------- 1 file changed, 31 insertions(+), 35 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/sp5100_tco.c b/drivers/watchdog/sp5100_tco.c index 5a13ab483c50..5868c6b6bf17 100644 --- a/drivers/watchdog/sp5100_tco.c +++ b/drivers/watchdog/sp5100_tco.c @@ -312,25 +312,6 @@ static struct miscdevice sp5100_tco_miscdev = { .fops = &sp5100_tco_fops, }; -/* - * Data for PCI driver interface - * - * This data only exists for exporting the supported - * PCI ids via MODULE_DEVICE_TABLE. We do not actually - * register a pci_driver, because someone else might - * want to register another driver on the same PCI id. - */ -static const struct pci_device_id sp5100_tco_pci_tbl[] = { - { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_SBX00_SMBUS, PCI_ANY_ID, - PCI_ANY_ID, }, - { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_HUDSON2_SMBUS, PCI_ANY_ID, - PCI_ANY_ID, }, - { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_KERNCZ_SMBUS, PCI_ANY_ID, - PCI_ANY_ID, }, - { 0, }, /* End of list */ -}; -MODULE_DEVICE_TABLE(pci, sp5100_tco_pci_tbl); - static u8 sp5100_tco_read_pm_reg32(u8 index) { u32 val = 0; @@ -347,27 +328,11 @@ static u8 sp5100_tco_read_pm_reg32(u8 index) */ static int sp5100_tco_setupdevice(void) { - struct pci_dev *dev = NULL; const char *dev_name = NULL; u32 val; u8 base_addr; int ret; - /* Match the PCI device */ - for_each_pci_dev(dev) { - if (pci_match_id(sp5100_tco_pci_tbl, dev) != NULL) { - sp5100_tco_pci = dev; - break; - } - } - - if (!sp5100_tco_pci) - return -ENODEV; - - pr_info("PCI Vendor ID: 0x%x, Device ID: 0x%x, Revision ID: 0x%x\n", - sp5100_tco_pci->vendor, sp5100_tco_pci->device, - sp5100_tco_pci->revision); - /* * Determine type of southbridge chipset. */ @@ -557,10 +522,41 @@ static struct platform_driver sp5100_tco_driver = { }, }; +/* + * Data for PCI driver interface + * + * This data only exists for exporting the supported + * PCI ids via MODULE_DEVICE_TABLE. We do not actually + * register a pci_driver, because someone else might + * want to register another driver on the same PCI id. + */ +static const struct pci_device_id sp5100_tco_pci_tbl[] = { + { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_SBX00_SMBUS, PCI_ANY_ID, + PCI_ANY_ID, }, + { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_HUDSON2_SMBUS, PCI_ANY_ID, + PCI_ANY_ID, }, + { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_KERNCZ_SMBUS, PCI_ANY_ID, + PCI_ANY_ID, }, + { 0, }, /* End of list */ +}; +MODULE_DEVICE_TABLE(pci, sp5100_tco_pci_tbl); + static int __init sp5100_tco_init_module(void) { + struct pci_dev *dev = NULL; int err; + /* Match the PCI device */ + for_each_pci_dev(dev) { + if (pci_match_id(sp5100_tco_pci_tbl, dev) != NULL) { + sp5100_tco_pci = dev; + break; + } + } + + if (!sp5100_tco_pci) + return -ENODEV; + pr_info("SP5100/SB800 TCO WatchDog Timer Driver v%s\n", TCO_VERSION); err = platform_driver_register(&sp5100_tco_driver); -- cgit v1.2.3 From fd8f9093a07306036cf63eda35e3357db3387d3a Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 24 Dec 2017 13:04:12 -0800 Subject: watchdog: sp5100_tco: Use dev_ print functions where possible MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use dev_ instead of pr_ functions where possible. Cc: Zoltán Böszörményi Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/sp5100_tco.c | 40 +++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/sp5100_tco.c b/drivers/watchdog/sp5100_tco.c index 5868c6b6bf17..ff240e5be833 100644 --- a/drivers/watchdog/sp5100_tco.c +++ b/drivers/watchdog/sp5100_tco.c @@ -326,7 +326,7 @@ static u8 sp5100_tco_read_pm_reg32(u8 index) /* * Init & exit routines */ -static int sp5100_tco_setupdevice(void) +static int sp5100_tco_setupdevice(struct device *dev) { const char *dev_name = NULL; u32 val; @@ -347,8 +347,8 @@ static int sp5100_tco_setupdevice(void) /* Request the IO ports used by this driver */ if (!request_muxed_region(SP5100_IO_PM_INDEX_REG, SP5100_PM_IOPORTS_SIZE, dev_name)) { - pr_err("I/O address 0x%04x already in use\n", - SP5100_IO_PM_INDEX_REG); + dev_err(dev, "I/O address 0x%04x already in use\n", + SP5100_IO_PM_INDEX_REG); return -EBUSY; } @@ -358,12 +358,12 @@ static int sp5100_tco_setupdevice(void) */ val = sp5100_tco_read_pm_reg32(base_addr) & 0xfffffff8; - pr_debug("Got 0x%04x from indirect I/O\n", val); + dev_dbg(dev, "Got 0x%04x from indirect I/O\n", val); /* Check MMIO address conflict */ if (!request_mem_region_exclusive(val, SP5100_WDT_MEM_MAP_SIZE, dev_name)) { - pr_debug("MMIO address 0x%04x already in use\n", val); + dev_dbg(dev, "MMIO address 0x%04x already in use\n", val); /* * Secondly, Find the watchdog timer MMIO address * from SBResource_MMIO register. @@ -381,7 +381,8 @@ static int sp5100_tco_setupdevice(void) /* The SBResource_MMIO is enabled and mapped memory space? */ if ((val & (SB800_ACPI_MMIO_DECODE_EN | SB800_ACPI_MMIO_SEL)) != SB800_ACPI_MMIO_DECODE_EN) { - pr_notice("failed to find MMIO address, giving up.\n"); + dev_notice(dev, + "failed to find MMIO address, giving up.\n"); ret = -ENODEV; goto unreg_region; } @@ -392,23 +393,24 @@ static int sp5100_tco_setupdevice(void) /* Check MMIO address conflict */ if (!request_mem_region_exclusive(val, SP5100_WDT_MEM_MAP_SIZE, dev_name)) { - pr_debug("MMIO address 0x%04x already in use\n", val); + dev_dbg(dev, "MMIO address 0x%04x already in use\n", + val); ret = -EBUSY; goto unreg_region; } - pr_debug("Got 0x%04x from SBResource_MMIO register\n", val); + dev_dbg(dev, "Got 0x%04x from SBResource_MMIO register\n", val); } tcobase_phys = val; tcobase = ioremap(val, SP5100_WDT_MEM_MAP_SIZE); if (!tcobase) { - pr_err("failed to get tcobase address\n"); + dev_err(dev, "failed to get tcobase address\n"); ret = -ENOMEM; goto unreg_mem_region; } - pr_info("Using 0x%04x for watchdog MMIO address\n", val); + dev_info(dev, "Using 0x%04x for watchdog MMIO address\n", val); /* Setup the watchdog timer */ tco_timer_enable(); @@ -443,21 +445,22 @@ unreg_region: return ret; } -static int sp5100_tco_init(struct platform_device *dev) +static int sp5100_tco_init(struct platform_device *pdev) { + struct device *dev = &pdev->dev; int ret; /* * Check whether or not the hardware watchdog is there. If found, then * set it up. */ - ret = sp5100_tco_setupdevice(); + ret = sp5100_tco_setupdevice(dev); if (ret) return ret; /* Check to see if last reboot was due to watchdog timeout */ - pr_info("Last reboot was %striggered by watchdog.\n", - tco_wdt_fired ? "" : "not "); + dev_info(dev, "Last reboot was %striggered by watchdog.\n", + tco_wdt_fired ? "" : "not "); /* * Check that the heartbeat value is within it's range. @@ -470,16 +473,16 @@ static int sp5100_tco_init(struct platform_device *dev) ret = misc_register(&sp5100_tco_miscdev); if (ret != 0) { - pr_err("cannot register miscdev on minor=%d (err=%d)\n", - WATCHDOG_MINOR, ret); + dev_err(dev, "cannot register miscdev on minor=%d (err=%d)\n", + WATCHDOG_MINOR, ret); goto exit; } clear_bit(0, &timer_alive); /* Show module parameters */ - pr_info("initialized (0x%p). heartbeat=%d sec (nowayout=%d)\n", - tcobase, heartbeat, nowayout); + dev_info(dev, "initialized (0x%p). heartbeat=%d sec (nowayout=%d)\n", + tcobase, heartbeat, nowayout); return 0; @@ -581,7 +584,6 @@ static void __exit sp5100_tco_cleanup_module(void) { platform_device_unregister(sp5100_tco_platform_device); platform_driver_unregister(&sp5100_tco_driver); - pr_info("SP5100/SB800 TCO Watchdog Module Unloaded\n"); } module_init(sp5100_tco_init_module); -- cgit v1.2.3 From 5bbecc5d3454d1069baf061a2cf0f04d0f0b9e7f Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 24 Dec 2017 13:04:13 -0800 Subject: watchdog: sp5100_tco: Clean up function and variable names MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use more common function and variable names. Use pdev instead of dev for platform device. Use sp5100_tco_probe() instead of sp5100_tco_init() for the probe function. Drop sp5100_tco_cleanup(); just move the code into sp5100_tco_remove(). Use sp5100_tco_init() instead of sp5100_tco_init_module() for the module initialization function. Use sp5100_tco_exit() instead of sp5100_tco_cleanup_module() for the module exit function. Use consistent defines for accessing the watchdog control register. Cc: Zoltán Böszörményi Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/sp5100_tco.c | 25 ++++++++++--------------- drivers/watchdog/sp5100_tco.h | 5 ++--- 2 files changed, 12 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/sp5100_tco.c b/drivers/watchdog/sp5100_tco.c index ff240e5be833..1123fad38fdf 100644 --- a/drivers/watchdog/sp5100_tco.c +++ b/drivers/watchdog/sp5100_tco.c @@ -421,8 +421,8 @@ static int sp5100_tco_setupdevice(struct device *dev) * Save WatchDogFired status, because WatchDogFired flag is * cleared here. */ - tco_wdt_fired = val & SP5100_PM_WATCHDOG_FIRED; - val &= ~SP5100_PM_WATCHDOG_ACTION_RESET; + tco_wdt_fired = val & SP5100_WDT_FIRED; + val &= ~SP5100_WDT_ACTION_RESET; writel(val, SP5100_WDT_CONTROL(tcobase)); /* Set a reasonable heartbeat before we stop the timer */ @@ -445,7 +445,7 @@ unreg_region: return ret; } -static int sp5100_tco_init(struct platform_device *pdev) +static int sp5100_tco_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; int ret; @@ -492,7 +492,7 @@ exit: return ret; } -static void sp5100_tco_cleanup(void) +static int sp5100_tco_remove(struct platform_device *pdev) { /* Stop the timer before we leave */ if (!nowayout) @@ -502,22 +502,17 @@ static void sp5100_tco_cleanup(void) misc_deregister(&sp5100_tco_miscdev); iounmap(tcobase); release_mem_region(tcobase_phys, SP5100_WDT_MEM_MAP_SIZE); -} -static int sp5100_tco_remove(struct platform_device *dev) -{ - if (tcobase) - sp5100_tco_cleanup(); return 0; } -static void sp5100_tco_shutdown(struct platform_device *dev) +static void sp5100_tco_shutdown(struct platform_device *pdev) { tco_timer_stop(); } static struct platform_driver sp5100_tco_driver = { - .probe = sp5100_tco_init, + .probe = sp5100_tco_probe, .remove = sp5100_tco_remove, .shutdown = sp5100_tco_shutdown, .driver = { @@ -544,7 +539,7 @@ static const struct pci_device_id sp5100_tco_pci_tbl[] = { }; MODULE_DEVICE_TABLE(pci, sp5100_tco_pci_tbl); -static int __init sp5100_tco_init_module(void) +static int __init sp5100_tco_init(void) { struct pci_dev *dev = NULL; int err; @@ -580,14 +575,14 @@ unreg_platform_driver: return err; } -static void __exit sp5100_tco_cleanup_module(void) +static void __exit sp5100_tco_exit(void) { platform_device_unregister(sp5100_tco_platform_device); platform_driver_unregister(&sp5100_tco_driver); } -module_init(sp5100_tco_init_module); -module_exit(sp5100_tco_cleanup_module); +module_init(sp5100_tco_init); +module_exit(sp5100_tco_exit); MODULE_AUTHOR("Priyanka Gupta"); MODULE_DESCRIPTION("TCO timer driver for SP5100/SB800 chipset"); diff --git a/drivers/watchdog/sp5100_tco.h b/drivers/watchdog/sp5100_tco.h index 2622cfe23dc1..cc00f1157220 100644 --- a/drivers/watchdog/sp5100_tco.h +++ b/drivers/watchdog/sp5100_tco.h @@ -15,6 +15,8 @@ #define SP5100_WDT_COUNT(base) ((base) + 0x04) /* Watchdog Count */ #define SP5100_WDT_START_STOP_BIT (1 << 0) +#define SP5100_WDT_FIRED (1 << 1) +#define SP5100_WDT_ACTION_RESET (1 << 2) #define SP5100_WDT_TRIGGER_BIT (1 << 7) #define SP5100_PM_IOPORTS_SIZE 0x02 @@ -34,9 +36,6 @@ #define SP5100_PM_WATCHDOG_CONTROL 0x69 #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_DECODE_EN (1 << 3) -- cgit v1.2.3 From 7cd9d5fff792026a908ccf3c229e1ec84668733c Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 24 Dec 2017 13:04:14 -0800 Subject: watchdog: sp5100_tco: Convert to use watchdog subsystem MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert to watchdog subsystem. As part of that rework, use devm functions where possible, and replace almost all static variables with a dynamically allocated data structure. Cc: Zoltán Böszörményi Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/sp5100_tco.c | 358 ++++++++++++------------------------------ 1 file changed, 102 insertions(+), 256 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/sp5100_tco.c b/drivers/watchdog/sp5100_tco.c index 1123fad38fdf..bb6c4608c1c0 100644 --- a/drivers/watchdog/sp5100_tco.c +++ b/drivers/watchdog/sp5100_tco.c @@ -24,37 +24,31 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include +#include +#include #include #include -#include -#include -#include -#include -#include #include -#include #include -#include -#include +#include +#include #include "sp5100_tco.h" -/* Module and version information */ -#define TCO_VERSION "0.05" -#define TCO_MODULE_NAME "SP5100 TCO timer" -#define TCO_DRIVER_NAME TCO_MODULE_NAME ", v" TCO_VERSION +#define TCO_DRIVER_NAME "sp5100-tco" /* internal variables */ -static u32 tcobase_phys; -static u32 tco_wdt_fired; -static void __iomem *tcobase; -static DEFINE_SPINLOCK(tco_lock); /* Guards the hardware */ -static unsigned long timer_alive; -static char tco_expect_close; -static struct pci_dev *sp5100_tco_pci; + +struct sp5100_tco { + struct watchdog_device wdd; + void __iomem *tcobase; +}; /* the watchdog platform device */ static struct platform_device *sp5100_tco_platform_device; +/* the associated PCI device */ +static struct pci_dev *sp5100_tco_pci; /* module parameters */ @@ -79,55 +73,52 @@ static bool tco_has_sp5100_reg_layout(struct pci_dev *dev) dev->revision < 0x40; } -static void tco_timer_start(void) +static int tco_timer_start(struct watchdog_device *wdd) { + struct sp5100_tco *tco = watchdog_get_drvdata(wdd); u32 val; - unsigned long flags; - spin_lock_irqsave(&tco_lock, flags); - val = readl(SP5100_WDT_CONTROL(tcobase)); + val = readl(SP5100_WDT_CONTROL(tco->tcobase)); val |= SP5100_WDT_START_STOP_BIT; - writel(val, SP5100_WDT_CONTROL(tcobase)); - spin_unlock_irqrestore(&tco_lock, flags); + writel(val, SP5100_WDT_CONTROL(tco->tcobase)); + + return 0; } -static void tco_timer_stop(void) +static int tco_timer_stop(struct watchdog_device *wdd) { + struct sp5100_tco *tco = watchdog_get_drvdata(wdd); u32 val; - unsigned long flags; - spin_lock_irqsave(&tco_lock, flags); - val = readl(SP5100_WDT_CONTROL(tcobase)); + val = readl(SP5100_WDT_CONTROL(tco->tcobase)); val &= ~SP5100_WDT_START_STOP_BIT; - writel(val, SP5100_WDT_CONTROL(tcobase)); - spin_unlock_irqrestore(&tco_lock, flags); + writel(val, SP5100_WDT_CONTROL(tco->tcobase)); + + return 0; } -static void tco_timer_keepalive(void) +static int tco_timer_ping(struct watchdog_device *wdd) { + struct sp5100_tco *tco = watchdog_get_drvdata(wdd); u32 val; - unsigned long flags; - spin_lock_irqsave(&tco_lock, flags); - val = readl(SP5100_WDT_CONTROL(tcobase)); + val = readl(SP5100_WDT_CONTROL(tco->tcobase)); val |= SP5100_WDT_TRIGGER_BIT; - writel(val, SP5100_WDT_CONTROL(tcobase)); - spin_unlock_irqrestore(&tco_lock, flags); + writel(val, SP5100_WDT_CONTROL(tco->tcobase)); + + return 0; } -static int tco_timer_set_heartbeat(int t) +static int tco_timer_set_timeout(struct watchdog_device *wdd, + unsigned int t) { - unsigned long flags; - - if (t < 0 || t > 0xffff) - return -EINVAL; + struct sp5100_tco *tco = watchdog_get_drvdata(wdd); /* Write new heartbeat to watchdog */ - spin_lock_irqsave(&tco_lock, flags); - writel(t, SP5100_WDT_COUNT(tcobase)); - spin_unlock_irqrestore(&tco_lock, flags); + writel(t, SP5100_WDT_COUNT(tco->tcobase)); + + wdd->timeout = t; - heartbeat = t; return 0; } @@ -182,137 +173,7 @@ static void tco_timer_enable(void) } } -/* - * /dev/watchdog handling - */ - -static int sp5100_tco_open(struct inode *inode, struct file *file) -{ - /* /dev/watchdog can only be opened once */ - if (test_and_set_bit(0, &timer_alive)) - return -EBUSY; - - /* Reload and activate timer */ - tco_timer_start(); - tco_timer_keepalive(); - return nonseekable_open(inode, file); -} - -static int sp5100_tco_release(struct inode *inode, struct file *file) -{ - /* Shut off the timer. */ - if (tco_expect_close == 42) { - tco_timer_stop(); - } else { - pr_crit("Unexpected close, not stopping watchdog!\n"); - tco_timer_keepalive(); - } - clear_bit(0, &timer_alive); - tco_expect_close = 0; - return 0; -} - -static ssize_t sp5100_tco_write(struct file *file, const char __user *data, - size_t len, loff_t *ppos) -{ - /* See if we got the magic character 'V' and reload the timer */ - if (len) { - if (!nowayout) { - size_t i; - - /* note: just in case someone wrote the magic character - * five months ago... */ - tco_expect_close = 0; - - /* 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') - tco_expect_close = 42; - } - } - - /* someone wrote to us, we should reload the timer */ - tco_timer_keepalive(); - } - return len; -} - -static long sp5100_tco_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) -{ - int new_options, retval = -EINVAL; - int new_heartbeat; - void __user *argp = (void __user *)arg; - int __user *p = argp; - static const struct watchdog_info ident = { - .options = WDIOF_SETTIMEOUT | - WDIOF_KEEPALIVEPING | - WDIOF_MAGICCLOSE, - .firmware_version = 0, - .identity = TCO_MODULE_NAME, - }; - - 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, p); - case WDIOC_SETOPTIONS: - if (get_user(new_options, p)) - return -EFAULT; - if (new_options & WDIOS_DISABLECARD) { - tco_timer_stop(); - retval = 0; - } - if (new_options & WDIOS_ENABLECARD) { - tco_timer_start(); - tco_timer_keepalive(); - retval = 0; - } - return retval; - case WDIOC_KEEPALIVE: - tco_timer_keepalive(); - return 0; - case WDIOC_SETTIMEOUT: - if (get_user(new_heartbeat, p)) - return -EFAULT; - if (tco_timer_set_heartbeat(new_heartbeat)) - return -EINVAL; - tco_timer_keepalive(); - /* Fall through */ - case WDIOC_GETTIMEOUT: - return put_user(heartbeat, p); - default: - return -ENOTTY; - } -} - -/* - * Kernel Interfaces - */ - -static const struct file_operations sp5100_tco_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .write = sp5100_tco_write, - .unlocked_ioctl = sp5100_tco_ioctl, - .open = sp5100_tco_open, - .release = sp5100_tco_release, -}; - -static struct miscdevice sp5100_tco_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &sp5100_tco_fops, -}; - -static u8 sp5100_tco_read_pm_reg32(u8 index) +static u32 sp5100_tco_read_pm_reg32(u8 index) { u32 val = 0; int i; @@ -323,14 +184,13 @@ static u8 sp5100_tco_read_pm_reg32(u8 index) return val; } -/* - * Init & exit routines - */ -static int sp5100_tco_setupdevice(struct device *dev) +static int sp5100_tco_setupdevice(struct device *dev, + struct watchdog_device *wdd) { - const char *dev_name = NULL; - u32 val; + struct sp5100_tco *tco = watchdog_get_drvdata(wdd); + const char *dev_name; u8 base_addr; + u32 val; int ret; /* @@ -361,8 +221,8 @@ static int sp5100_tco_setupdevice(struct device *dev) dev_dbg(dev, "Got 0x%04x from indirect I/O\n", val); /* Check MMIO address conflict */ - if (!request_mem_region_exclusive(val, SP5100_WDT_MEM_MAP_SIZE, - dev_name)) { + if (!devm_request_mem_region(dev, val, SP5100_WDT_MEM_MAP_SIZE, + dev_name)) { dev_dbg(dev, "MMIO address 0x%04x already in use\n", val); /* * Secondly, Find the watchdog timer MMIO address @@ -391,8 +251,8 @@ static int sp5100_tco_setupdevice(struct device *dev) /* Add the Watchdog Timer offset to base address. */ val += SB800_PM_WDT_MMIO_OFFSET; /* Check MMIO address conflict */ - if (!request_mem_region_exclusive(val, SP5100_WDT_MEM_MAP_SIZE, - dev_name)) { + if (!devm_request_mem_region(dev, val, SP5100_WDT_MEM_MAP_SIZE, + dev_name)) { dev_dbg(dev, "MMIO address 0x%04x already in use\n", val); ret = -EBUSY; @@ -401,13 +261,11 @@ static int sp5100_tco_setupdevice(struct device *dev) dev_dbg(dev, "Got 0x%04x from SBResource_MMIO register\n", val); } - tcobase_phys = val; - - tcobase = ioremap(val, SP5100_WDT_MEM_MAP_SIZE); - if (!tcobase) { + tco->tcobase = devm_ioremap(dev, val, SP5100_WDT_MEM_MAP_SIZE); + if (!tco->tcobase) { dev_err(dev, "failed to get tcobase address\n"); ret = -ENOMEM; - goto unreg_mem_region; + goto unreg_region; } dev_info(dev, "Using 0x%04x for watchdog MMIO address\n", val); @@ -416,107 +274,95 @@ static int sp5100_tco_setupdevice(struct device *dev) tco_timer_enable(); /* Check that the watchdog action is set to reset the system */ - val = readl(SP5100_WDT_CONTROL(tcobase)); + val = readl(SP5100_WDT_CONTROL(tco->tcobase)); /* * Save WatchDogFired status, because WatchDogFired flag is * cleared here. */ - tco_wdt_fired = val & SP5100_WDT_FIRED; + if (val & SP5100_WDT_FIRED) + wdd->bootstatus = WDIOF_CARDRESET; val &= ~SP5100_WDT_ACTION_RESET; - writel(val, SP5100_WDT_CONTROL(tcobase)); + writel(val, SP5100_WDT_CONTROL(tco->tcobase)); /* Set a reasonable heartbeat before we stop the timer */ - tco_timer_set_heartbeat(heartbeat); + tco_timer_set_timeout(wdd, wdd->timeout); /* * Stop the TCO before we change anything so we don't race with * a zeroed timer. */ - tco_timer_stop(); + tco_timer_stop(wdd); release_region(SP5100_IO_PM_INDEX_REG, SP5100_PM_IOPORTS_SIZE); return 0; -unreg_mem_region: - release_mem_region(tcobase_phys, SP5100_WDT_MEM_MAP_SIZE); unreg_region: release_region(SP5100_IO_PM_INDEX_REG, SP5100_PM_IOPORTS_SIZE); return ret; } +static struct watchdog_info sp5100_tco_wdt_info = { + .identity = "SP5100 TCO timer", + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, +}; + +static const struct watchdog_ops sp5100_tco_wdt_ops = { + .owner = THIS_MODULE, + .start = tco_timer_start, + .stop = tco_timer_stop, + .ping = tco_timer_ping, + .set_timeout = tco_timer_set_timeout, +}; + static int sp5100_tco_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; + struct watchdog_device *wdd; + struct sp5100_tco *tco; int ret; - /* - * Check whether or not the hardware watchdog is there. If found, then - * set it up. - */ - ret = sp5100_tco_setupdevice(dev); + tco = devm_kzalloc(dev, sizeof(*tco), GFP_KERNEL); + if (!tco) + return -ENOMEM; + + wdd = &tco->wdd; + wdd->parent = dev; + wdd->info = &sp5100_tco_wdt_info; + wdd->ops = &sp5100_tco_wdt_ops; + wdd->timeout = WATCHDOG_HEARTBEAT; + wdd->min_timeout = 1; + wdd->max_timeout = 0xffff; + + if (watchdog_init_timeout(wdd, heartbeat, NULL)) + dev_info(dev, "timeout value invalid, using %d\n", + wdd->timeout); + watchdog_set_nowayout(wdd, nowayout); + watchdog_stop_on_reboot(wdd); + watchdog_stop_on_unregister(wdd); + watchdog_set_drvdata(wdd, tco); + + ret = sp5100_tco_setupdevice(dev, wdd); if (ret) return ret; - /* Check to see if last reboot was due to watchdog timeout */ - dev_info(dev, "Last reboot was %striggered by watchdog.\n", - tco_wdt_fired ? "" : "not "); - - /* - * Check that the heartbeat value is within it's range. - * If not, reset to the default. - */ - if (tco_timer_set_heartbeat(heartbeat)) { - heartbeat = WATCHDOG_HEARTBEAT; - tco_timer_set_heartbeat(heartbeat); - } - - ret = misc_register(&sp5100_tco_miscdev); - if (ret != 0) { - dev_err(dev, "cannot register miscdev on minor=%d (err=%d)\n", - WATCHDOG_MINOR, ret); - goto exit; + ret = devm_watchdog_register_device(dev, wdd); + if (ret) { + dev_err(dev, "cannot register watchdog device (err=%d)\n", ret); + return ret; } - clear_bit(0, &timer_alive); - /* Show module parameters */ - dev_info(dev, "initialized (0x%p). heartbeat=%d sec (nowayout=%d)\n", - tcobase, heartbeat, nowayout); + dev_info(dev, "initialized. heartbeat=%d sec (nowayout=%d)\n", + wdd->timeout, nowayout); return 0; - -exit: - iounmap(tcobase); - release_mem_region(tcobase_phys, SP5100_WDT_MEM_MAP_SIZE); - return ret; -} - -static int sp5100_tco_remove(struct platform_device *pdev) -{ - /* Stop the timer before we leave */ - if (!nowayout) - tco_timer_stop(); - - /* Deregister */ - misc_deregister(&sp5100_tco_miscdev); - iounmap(tcobase); - release_mem_region(tcobase_phys, SP5100_WDT_MEM_MAP_SIZE); - - return 0; -} - -static void sp5100_tco_shutdown(struct platform_device *pdev) -{ - tco_timer_stop(); } static struct platform_driver sp5100_tco_driver = { .probe = sp5100_tco_probe, - .remove = sp5100_tco_remove, - .shutdown = sp5100_tco_shutdown, .driver = { - .name = TCO_MODULE_NAME, + .name = TCO_DRIVER_NAME, }, }; @@ -555,14 +401,14 @@ static int __init sp5100_tco_init(void) if (!sp5100_tco_pci) return -ENODEV; - pr_info("SP5100/SB800 TCO WatchDog Timer Driver v%s\n", TCO_VERSION); + pr_info("SP5100/SB800 TCO WatchDog Timer Driver\n"); err = platform_driver_register(&sp5100_tco_driver); if (err) return err; - sp5100_tco_platform_device = platform_device_register_simple( - TCO_MODULE_NAME, -1, NULL, 0); + sp5100_tco_platform_device = + platform_device_register_simple(TCO_DRIVER_NAME, -1, NULL, 0); if (IS_ERR(sp5100_tco_platform_device)) { err = PTR_ERR(sp5100_tco_platform_device); goto unreg_platform_driver; -- cgit v1.2.3 From 17b20833ff427e285e36e4ce6e5dc8b0046fa0a4 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 24 Dec 2017 13:04:15 -0800 Subject: watchdog: sp5100_tco: Use bit operations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Using bit operations makes it easier to improve the driver. Cc: Zoltán Böszörményi Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/sp5100_tco.h | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/sp5100_tco.h b/drivers/watchdog/sp5100_tco.h index cc00f1157220..ca0721c8d879 100644 --- a/drivers/watchdog/sp5100_tco.h +++ b/drivers/watchdog/sp5100_tco.h @@ -7,6 +7,8 @@ * TCO timer driver for sp5100 chipsets */ +#include + /* * Some address definitions for the Watchdog */ @@ -14,10 +16,10 @@ #define SP5100_WDT_CONTROL(base) ((base) + 0x00) /* Watchdog Control */ #define SP5100_WDT_COUNT(base) ((base) + 0x04) /* Watchdog Count */ -#define SP5100_WDT_START_STOP_BIT (1 << 0) -#define SP5100_WDT_FIRED (1 << 1) -#define SP5100_WDT_ACTION_RESET (1 << 2) -#define SP5100_WDT_TRIGGER_BIT (1 << 7) +#define SP5100_WDT_START_STOP_BIT BIT(0) +#define SP5100_WDT_FIRED BIT(1) +#define SP5100_WDT_ACTION_RESET BIT(2) +#define SP5100_WDT_TRIGGER_BIT BIT(7) #define SP5100_PM_IOPORTS_SIZE 0x02 @@ -37,10 +39,10 @@ #define SP5100_PM_WATCHDOG_BASE 0x6C #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_SECOND_RES (3 << 1) +#define SP5100_PM_WATCHDOG_DISABLE ((u8)BIT(0)) +#define SP5100_PM_WATCHDOG_SECOND_RES GENMASK(2, 1) #define SP5100_DEVNAME "SP5100 TCO" @@ -50,12 +52,11 @@ #define SB800_PM_WATCHDOG_BASE 0x48 #define SB800_PM_WATCHDOG_CONFIG 0x4C -#define SB800_PCI_WATCHDOG_DECODE_EN (1 << 0) -#define SB800_PM_WATCHDOG_DISABLE (1 << 1) -#define SB800_PM_WATCHDOG_SECOND_RES (3 << 0) -#define SB800_ACPI_MMIO_DECODE_EN (1 << 0) -#define SB800_ACPI_MMIO_SEL (1 << 1) - +#define SB800_PCI_WATCHDOG_DECODE_EN BIT(0) +#define SB800_PM_WATCHDOG_DISABLE ((u8)BIT(1)) +#define SB800_PM_WATCHDOG_SECOND_RES GENMASK(1, 0) +#define SB800_ACPI_MMIO_DECODE_EN BIT(0) +#define SB800_ACPI_MMIO_SEL BIT(1) #define SB800_PM_WDT_MMIO_OFFSET 0xB00 -- cgit v1.2.3 From f7781b067522aa269213e8025c80cbed1868d349 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 24 Dec 2017 13:04:16 -0800 Subject: watchdog: sp5100-tco: Abort if watchdog is disabled by hardware MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the watchdog control register indicates that the watchdog hardware is disabled even after we tried to enable it, there is no point to instantiate the driver. Cc: Zoltán Böszörményi Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/sp5100_tco.c | 8 +++++++- drivers/watchdog/sp5100_tco.h | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/watchdog/sp5100_tco.c b/drivers/watchdog/sp5100_tco.c index bb6c4608c1c0..23246cb40598 100644 --- a/drivers/watchdog/sp5100_tco.c +++ b/drivers/watchdog/sp5100_tco.c @@ -273,14 +273,20 @@ static int sp5100_tco_setupdevice(struct device *dev, /* Setup the watchdog timer */ tco_timer_enable(); - /* Check that the watchdog action is set to reset the system */ val = readl(SP5100_WDT_CONTROL(tco->tcobase)); + if (val & SP5100_WDT_DISABLED) { + dev_err(dev, "Watchdog hardware is disabled\n"); + ret = -ENODEV; + goto unreg_region; + } + /* * Save WatchDogFired status, because WatchDogFired flag is * cleared here. */ if (val & SP5100_WDT_FIRED) wdd->bootstatus = WDIOF_CARDRESET; + /* Set watchdog action to reset the system */ val &= ~SP5100_WDT_ACTION_RESET; writel(val, SP5100_WDT_CONTROL(tco->tcobase)); diff --git a/drivers/watchdog/sp5100_tco.h b/drivers/watchdog/sp5100_tco.h index ca0721c8d879..008b2094bd13 100644 --- a/drivers/watchdog/sp5100_tco.h +++ b/drivers/watchdog/sp5100_tco.h @@ -19,6 +19,7 @@ #define SP5100_WDT_START_STOP_BIT BIT(0) #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 -- cgit v1.2.3 From 887d2ec51e34b704837816a10f185f3b604170fd Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 24 Dec 2017 13:04:17 -0800 Subject: watchdog: sp5100_tco: Add support for recent FCH versions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Starting with Family 16h Models 30h-3Fh and Family 15h Models 60h-6Fh, watchdog address space decoding has changed. The cutover point is already identified in the i2c-piix2 driver, so use the same mechanism. Cc: Zoltán Böszörményi Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/sp5100_tco.c | 169 ++++++++++++++++++++++++++++-------------- drivers/watchdog/sp5100_tco.h | 21 ++++++ 2 files changed, 133 insertions(+), 57 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/sp5100_tco.c b/drivers/watchdog/sp5100_tco.c index 23246cb40598..41aaae2d5287 100644 --- a/drivers/watchdog/sp5100_tco.c +++ b/drivers/watchdog/sp5100_tco.c @@ -16,6 +16,11 @@ * See AMD Publication 43009 "AMD SB700/710/750 Register Reference Guide", * AMD Publication 45482 "AMD SB800-Series Southbridges Register * Reference Guide" + * AMD Publication 48751 "BIOS and Kernel Developer’s Guide (BKDG) + * for AMD Family 16h Models 00h-0Fh Processors" + * AMD Publication 51192 "AMD Bolton FCH Register Reference Guide" + * AMD Publication 52740 "BIOS and Kernel Developer’s Guide (BKDG) + * for AMD Family 16h Models 30h-3Fh Processors" */ /* @@ -40,9 +45,14 @@ /* internal variables */ +enum tco_reg_layout { + sp5100, sb800, efch +}; + struct sp5100_tco { struct watchdog_device wdd; void __iomem *tcobase; + enum tco_reg_layout tco_reg_layout; }; /* the watchdog platform device */ @@ -67,10 +77,20 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started." * Some TCO specific functions */ -static bool tco_has_sp5100_reg_layout(struct pci_dev *dev) +static enum tco_reg_layout tco_reg_layout(struct pci_dev *dev) { - return dev->device == PCI_DEVICE_ID_ATI_SBX00_SMBUS && - dev->revision < 0x40; + if (dev->vendor == PCI_VENDOR_ID_ATI && + dev->device == PCI_DEVICE_ID_ATI_SBX00_SMBUS && + dev->revision < 0x40) { + return sp5100; + } else if (dev->vendor == PCI_VENDOR_ID_AMD && + ((dev->device == PCI_DEVICE_ID_AMD_HUDSON2_SMBUS && + dev->revision >= 0x41) || + (dev->device == PCI_DEVICE_ID_AMD_KERNCZ_SMBUS && + dev->revision >= 0x49))) { + return efch; + } + return sb800; } static int tco_timer_start(struct watchdog_device *wdd) @@ -139,9 +159,12 @@ static void sp5100_tco_update_pm_reg8(u8 index, u8 reset, u8 set) outb(val, SP5100_IO_PM_DATA_REG); } -static void tco_timer_enable(void) +static void tco_timer_enable(struct sp5100_tco *tco) { - if (!tco_has_sp5100_reg_layout(sp5100_tco_pci)) { + u32 val; + + switch (tco->tco_reg_layout) { + case sb800: /* For SB800 or later */ /* Set the Watchdog timer resolution to 1 sec */ sp5100_tco_update_pm_reg8(SB800_PM_WATCHDOG_CONFIG, @@ -151,9 +174,8 @@ static void tco_timer_enable(void) sp5100_tco_update_pm_reg8(SB800_PM_WATCHDOG_CONTROL, ~SB800_PM_WATCHDOG_DISABLE, SB800_PCI_WATCHDOG_DECODE_EN); - } else { - u32 val; - + break; + case sp5100: /* For SP5100 or SB7x0 */ /* Enable watchdog decode bit */ pci_read_config_dword(sp5100_tco_pci, @@ -170,6 +192,13 @@ static void tco_timer_enable(void) sp5100_tco_update_pm_reg8(SP5100_PM_WATCHDOG_CONTROL, ~SP5100_PM_WATCHDOG_DISABLE, SP5100_PM_WATCHDOG_SECOND_RES); + break; + case efch: + /* Set the Watchdog timer resolution to 1 sec and enable */ + sp5100_tco_update_pm_reg8(EFCH_PM_DECODEEN3, + ~EFCH_PM_WATCHDOG_DISABLE, + EFCH_PM_DECODEEN_SECOND_RES); + break; } } @@ -189,89 +218,113 @@ static int sp5100_tco_setupdevice(struct device *dev, { struct sp5100_tco *tco = watchdog_get_drvdata(wdd); const char *dev_name; - u8 base_addr; - u32 val; + u32 mmio_addr = 0, val; int ret; - /* - * Determine type of southbridge chipset. - */ - if (tco_has_sp5100_reg_layout(sp5100_tco_pci)) { - dev_name = SP5100_DEVNAME; - base_addr = SP5100_PM_WATCHDOG_BASE; - } else { - dev_name = SB800_DEVNAME; - base_addr = SB800_PM_WATCHDOG_BASE; - } - /* Request the IO ports used by this driver */ if (!request_muxed_region(SP5100_IO_PM_INDEX_REG, - SP5100_PM_IOPORTS_SIZE, dev_name)) { + SP5100_PM_IOPORTS_SIZE, "sp5100_tco")) { dev_err(dev, "I/O address 0x%04x already in use\n", SP5100_IO_PM_INDEX_REG); return -EBUSY; } /* - * First, Find the watchdog timer MMIO address from indirect I/O. - * Low three bits of BASE are reserved. + * Determine type of southbridge chipset. */ - val = sp5100_tco_read_pm_reg32(base_addr) & 0xfffffff8; - - dev_dbg(dev, "Got 0x%04x from indirect I/O\n", val); + switch (tco->tco_reg_layout) { + case sp5100: + dev_name = SP5100_DEVNAME; + mmio_addr = sp5100_tco_read_pm_reg32(SP5100_PM_WATCHDOG_BASE) & + 0xfffffff8; + break; + case sb800: + dev_name = SB800_DEVNAME; + mmio_addr = sp5100_tco_read_pm_reg32(SB800_PM_WATCHDOG_BASE) & + 0xfffffff8; + break; + case efch: + dev_name = SB800_DEVNAME; + val = sp5100_tco_read_pm_reg8(EFCH_PM_DECODEEN); + if (val & EFCH_PM_DECODEEN_WDT_TMREN) + mmio_addr = EFCH_PM_WDT_ADDR; + break; + default: + return -ENODEV; + } /* Check MMIO address conflict */ - if (!devm_request_mem_region(dev, val, SP5100_WDT_MEM_MAP_SIZE, + if (!mmio_addr || + !devm_request_mem_region(dev, mmio_addr, SP5100_WDT_MEM_MAP_SIZE, dev_name)) { - dev_dbg(dev, "MMIO address 0x%04x already in use\n", val); - /* - * Secondly, Find the watchdog timer MMIO address - * from SBResource_MMIO register. - */ - if (tco_has_sp5100_reg_layout(sp5100_tco_pci)) { + if (mmio_addr) + dev_dbg(dev, "MMIO address 0x%08x already in use\n", + mmio_addr); + switch (tco->tco_reg_layout) { + case sp5100: + /* + * Secondly, Find the watchdog timer MMIO address + * from SBResource_MMIO register. + */ /* Read SBResource_MMIO from PCI config(PCI_Reg: 9Ch) */ pci_read_config_dword(sp5100_tco_pci, SP5100_SB_RESOURCE_MMIO_BASE, - &val); - } else { + &mmio_addr); + if ((mmio_addr & (SB800_ACPI_MMIO_DECODE_EN | + SB800_ACPI_MMIO_SEL)) != + SB800_ACPI_MMIO_DECODE_EN) { + ret = -ENODEV; + goto unreg_region; + } + mmio_addr &= ~0xFFF; + mmio_addr += SB800_PM_WDT_MMIO_OFFSET; + break; + case sb800: /* Read SBResource_MMIO from AcpiMmioEn(PM_Reg: 24h) */ - val = sp5100_tco_read_pm_reg32(SB800_PM_ACPI_MMIO_EN); - } - - /* The SBResource_MMIO is enabled and mapped memory space? */ - if ((val & (SB800_ACPI_MMIO_DECODE_EN | SB800_ACPI_MMIO_SEL)) != + mmio_addr = + sp5100_tco_read_pm_reg32(SB800_PM_ACPI_MMIO_EN); + if ((mmio_addr & (SB800_ACPI_MMIO_DECODE_EN | + SB800_ACPI_MMIO_SEL)) != SB800_ACPI_MMIO_DECODE_EN) { - dev_notice(dev, - "failed to find MMIO address, giving up.\n"); - ret = -ENODEV; - goto unreg_region; + ret = -ENODEV; + goto unreg_region; + } + mmio_addr &= ~0xFFF; + mmio_addr += SB800_PM_WDT_MMIO_OFFSET; + break; + case efch: + val = sp5100_tco_read_pm_reg8(EFCH_PM_ISACONTROL); + if (!(val & EFCH_PM_ISACONTROL_MMIOEN)) { + ret = -ENODEV; + goto unreg_region; + } + mmio_addr = EFCH_PM_ACPI_MMIO_ADDR + + EFCH_PM_ACPI_MMIO_WDT_OFFSET; + break; } - /* Clear unnecessary the low twelve bits */ - val &= ~0xFFF; - /* Add the Watchdog Timer offset to base address. */ - val += SB800_PM_WDT_MMIO_OFFSET; - /* Check MMIO address conflict */ - if (!devm_request_mem_region(dev, val, SP5100_WDT_MEM_MAP_SIZE, + dev_dbg(dev, "Got 0x%08x from SBResource_MMIO register\n", + mmio_addr); + if (!devm_request_mem_region(dev, mmio_addr, + SP5100_WDT_MEM_MAP_SIZE, dev_name)) { - dev_dbg(dev, "MMIO address 0x%04x already in use\n", - val); + dev_dbg(dev, "MMIO address 0x%08x already in use\n", + mmio_addr); ret = -EBUSY; goto unreg_region; } - dev_dbg(dev, "Got 0x%04x from SBResource_MMIO register\n", val); } - tco->tcobase = devm_ioremap(dev, val, SP5100_WDT_MEM_MAP_SIZE); + tco->tcobase = devm_ioremap(dev, mmio_addr, SP5100_WDT_MEM_MAP_SIZE); if (!tco->tcobase) { dev_err(dev, "failed to get tcobase address\n"); ret = -ENOMEM; goto unreg_region; } - dev_info(dev, "Using 0x%04x for watchdog MMIO address\n", val); + dev_info(dev, "Using 0x%08x for watchdog MMIO address\n", mmio_addr); /* Setup the watchdog timer */ - tco_timer_enable(); + tco_timer_enable(tco); val = readl(SP5100_WDT_CONTROL(tco->tcobase)); if (val & SP5100_WDT_DISABLED) { @@ -332,6 +385,8 @@ static int sp5100_tco_probe(struct platform_device *pdev) if (!tco) return -ENOMEM; + tco->tco_reg_layout = tco_reg_layout(sp5100_tco_pci); + wdd = &tco->wdd; wdd->parent = dev; wdd->info = &sp5100_tco_wdt_info; diff --git a/drivers/watchdog/sp5100_tco.h b/drivers/watchdog/sp5100_tco.h index 008b2094bd13..87eaf357ae01 100644 --- a/drivers/watchdog/sp5100_tco.h +++ b/drivers/watchdog/sp5100_tco.h @@ -62,3 +62,24 @@ #define SB800_PM_WDT_MMIO_OFFSET 0xB00 #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 -- cgit v1.2.3 From d040a2ee0e6292b73a328e671eb92a7c9753d18f Mon Sep 17 00:00:00 2001 From: Corentin Labbe Date: Thu, 18 Jan 2018 20:52:56 +0100 Subject: watchdog: remove at32ap700x_wdt Since AVR32 is gone, this driver is useless. Signed-off-by: Corentin Labbe Acked-by: Hans-Christian Noren Egtvedt Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/Kconfig | 9 - drivers/watchdog/Makefile | 3 - drivers/watchdog/at32ap700x_wdt.c | 433 -------------------------------------- 3 files changed, 445 deletions(-) delete mode 100644 drivers/watchdog/at32ap700x_wdt.c (limited to 'drivers') diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 633173260f5a..6a602f70aaa4 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -807,15 +807,6 @@ config SPRD_WATCHDOG Say Y here to include watchdog timer supported by Spreadtrum system. -# AVR32 Architecture - -config AT32AP700X_WDT - tristate "AT32AP700x watchdog" - depends on CPU_AT32AP700X || COMPILE_TEST - help - Watchdog timer embedded into AT32AP700x devices. This will reboot - your system when the timeout is reached. - # BLACKFIN Architecture config BFIN_WDT diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 4b374b9e55e8..b7123bab9f8e 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -91,9 +91,6 @@ obj-$(CONFIG_UNIPHIER_WATCHDOG) += uniphier_wdt.o obj-$(CONFIG_RTD119X_WATCHDOG) += rtd119x_wdt.o obj-$(CONFIG_SPRD_WATCHDOG) += sprd_wdt.o -# AVR32 Architecture -obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o - # BLACKFIN Architecture obj-$(CONFIG_BFIN_WDT) += bfin_wdt.o diff --git a/drivers/watchdog/at32ap700x_wdt.c b/drivers/watchdog/at32ap700x_wdt.c deleted file mode 100644 index 81ba8920f127..000000000000 --- a/drivers/watchdog/at32ap700x_wdt.c +++ /dev/null @@ -1,433 +0,0 @@ -/* - * Watchdog driver for Atmel AT32AP700X devices - * - * Copyright (C) 2005-2006 Atmel Corporation - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * - * Errata: WDT Clear is blocked after WDT Reset - * - * A watchdog timer event will, after reset, block writes to the WDT_CLEAR - * register, preventing the program to clear the next Watchdog Timer Reset. - * - * If you still want to use the WDT after a WDT reset a small code can be - * insterted at the startup checking the AVR32_PM.rcause register for WDT reset - * and use a GPIO pin to reset the system. This method requires that one of the - * GPIO pins are available and connected externally to the RESET_N pin. After - * the GPIO pin has pulled down the reset line the GPIO will be reset and leave - * the pin tristated with pullup. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define TIMEOUT_MIN 1 -#define TIMEOUT_MAX 2 -#define TIMEOUT_DEFAULT TIMEOUT_MAX - -/* module parameters */ -static int timeout = TIMEOUT_DEFAULT; -module_param(timeout, int, 0); -MODULE_PARM_DESC(timeout, - "Timeout value. Limited to be 1 or 2 seconds. (default=" - __MODULE_STRING(TIMEOUT_DEFAULT) ")"); - -static bool nowayout = WATCHDOG_NOWAYOUT; -module_param(nowayout, bool, 0); -MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" - __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); - -/* Watchdog registers and write/read macro */ -#define WDT_CTRL 0x00 -#define WDT_CTRL_EN 0 -#define WDT_CTRL_PSEL 8 -#define WDT_CTRL_KEY 24 - -#define WDT_CLR 0x04 - -#define WDT_RCAUSE 0x10 -#define WDT_RCAUSE_POR 0 -#define WDT_RCAUSE_EXT 2 -#define WDT_RCAUSE_WDT 3 -#define WDT_RCAUSE_JTAG 4 -#define WDT_RCAUSE_SERP 5 - -#define WDT_BIT(name) (1 << WDT_##name) -#define WDT_BF(name, value) ((value) << WDT_##name) - -#define wdt_readl(dev, reg) \ - __raw_readl((dev)->regs + WDT_##reg) -#define wdt_writel(dev, reg, value) \ - __raw_writel((value), (dev)->regs + WDT_##reg) - -struct wdt_at32ap700x { - void __iomem *regs; - spinlock_t io_lock; - int timeout; - int boot_status; - unsigned long users; - struct miscdevice miscdev; -}; - -static struct wdt_at32ap700x *wdt; -static char expect_release; - -/* - * Disable the watchdog. - */ -static inline void at32_wdt_stop(void) -{ - unsigned long psel; - - spin_lock(&wdt->io_lock); - psel = wdt_readl(wdt, CTRL) & WDT_BF(CTRL_PSEL, 0x0f); - wdt_writel(wdt, CTRL, psel | WDT_BF(CTRL_KEY, 0x55)); - wdt_writel(wdt, CTRL, psel | WDT_BF(CTRL_KEY, 0xaa)); - spin_unlock(&wdt->io_lock); -} - -/* - * Enable and reset the watchdog. - */ -static inline void at32_wdt_start(void) -{ - /* 0xf is 2^16 divider = 2 sec, 0xe is 2^15 divider = 1 sec */ - unsigned long psel = (wdt->timeout > 1) ? 0xf : 0xe; - - spin_lock(&wdt->io_lock); - wdt_writel(wdt, CTRL, WDT_BIT(CTRL_EN) - | WDT_BF(CTRL_PSEL, psel) - | WDT_BF(CTRL_KEY, 0x55)); - wdt_writel(wdt, CTRL, WDT_BIT(CTRL_EN) - | WDT_BF(CTRL_PSEL, psel) - | WDT_BF(CTRL_KEY, 0xaa)); - spin_unlock(&wdt->io_lock); -} - -/* - * Pat the watchdog timer. - */ -static inline void at32_wdt_pat(void) -{ - spin_lock(&wdt->io_lock); - wdt_writel(wdt, CLR, 0x42); - spin_unlock(&wdt->io_lock); -} - -/* - * Watchdog device is opened, and watchdog starts running. - */ -static int at32_wdt_open(struct inode *inode, struct file *file) -{ - if (test_and_set_bit(1, &wdt->users)) - return -EBUSY; - - at32_wdt_start(); - return nonseekable_open(inode, file); -} - -/* - * Close the watchdog device. - */ -static int at32_wdt_close(struct inode *inode, struct file *file) -{ - if (expect_release == 42) { - at32_wdt_stop(); - } else { - dev_dbg(wdt->miscdev.parent, - "unexpected close, not stopping watchdog!\n"); - at32_wdt_pat(); - } - clear_bit(1, &wdt->users); - expect_release = 0; - return 0; -} - -/* - * Change the watchdog time interval. - */ -static int at32_wdt_settimeout(int time) -{ - /* - * All counting occurs at 1 / SLOW_CLOCK (32 kHz) and max prescaler is - * 2 ^ 16 allowing up to 2 seconds timeout. - */ - if ((time < TIMEOUT_MIN) || (time > TIMEOUT_MAX)) - return -EINVAL; - - /* - * Set new watchdog time. It will be used when at32_wdt_start() is - * called. - */ - wdt->timeout = time; - return 0; -} - -/* - * Get the watchdog status. - */ -static int at32_wdt_get_status(void) -{ - int rcause; - int status = 0; - - rcause = wdt_readl(wdt, RCAUSE); - - switch (rcause) { - case WDT_BIT(RCAUSE_EXT): - status = WDIOF_EXTERN1; - break; - case WDT_BIT(RCAUSE_WDT): - status = WDIOF_CARDRESET; - break; - case WDT_BIT(RCAUSE_POR): /* fall through */ - case WDT_BIT(RCAUSE_JTAG): /* fall through */ - case WDT_BIT(RCAUSE_SERP): /* fall through */ - default: - break; - } - - return status; -} - -static const struct watchdog_info at32_wdt_info = { - .identity = "at32ap700x watchdog", - .options = WDIOF_SETTIMEOUT | - WDIOF_KEEPALIVEPING | - WDIOF_MAGICCLOSE, -}; - -/* - * Handle commands from user-space. - */ -static long at32_wdt_ioctl(struct file *file, - unsigned int cmd, unsigned long arg) -{ - int ret = -ENOTTY; - int time; - void __user *argp = (void __user *)arg; - int __user *p = argp; - - switch (cmd) { - case WDIOC_GETSUPPORT: - ret = copy_to_user(argp, &at32_wdt_info, - sizeof(at32_wdt_info)) ? -EFAULT : 0; - break; - case WDIOC_GETSTATUS: - ret = put_user(0, p); - break; - case WDIOC_GETBOOTSTATUS: - ret = put_user(wdt->boot_status, p); - break; - case WDIOC_SETOPTIONS: - ret = get_user(time, p); - if (ret) - break; - if (time & WDIOS_DISABLECARD) - at32_wdt_stop(); - if (time & WDIOS_ENABLECARD) - at32_wdt_start(); - ret = 0; - break; - case WDIOC_KEEPALIVE: - at32_wdt_pat(); - ret = 0; - break; - case WDIOC_SETTIMEOUT: - ret = get_user(time, p); - if (ret) - break; - ret = at32_wdt_settimeout(time); - if (ret) - break; - /* Enable new time value */ - at32_wdt_start(); - /* fall through */ - case WDIOC_GETTIMEOUT: - ret = put_user(wdt->timeout, p); - break; - } - - return ret; -} - -static ssize_t at32_wdt_write(struct file *file, const char __user *data, - size_t len, loff_t *ppos) -{ - /* See if we got the magic character 'V' and reload the timer */ - if (len) { - if (!nowayout) { - size_t i; - - /* - * note: just in case someone wrote the magic - * character five months ago... - */ - expect_release = 0; - - /* - * 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 = 42; - } - } - /* someone wrote to us, we should pat the watchdog */ - at32_wdt_pat(); - } - return len; -} - -static const struct file_operations at32_wdt_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .unlocked_ioctl = at32_wdt_ioctl, - .open = at32_wdt_open, - .release = at32_wdt_close, - .write = at32_wdt_write, -}; - -static int __init at32_wdt_probe(struct platform_device *pdev) -{ - struct resource *regs; - int ret; - - if (wdt) { - dev_dbg(&pdev->dev, "only 1 wdt instance supported.\n"); - return -EBUSY; - } - - regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!regs) { - dev_dbg(&pdev->dev, "missing mmio resource\n"); - return -ENXIO; - } - - wdt = devm_kzalloc(&pdev->dev, sizeof(struct wdt_at32ap700x), - GFP_KERNEL); - if (!wdt) - return -ENOMEM; - - wdt->regs = devm_ioremap(&pdev->dev, regs->start, resource_size(regs)); - if (!wdt->regs) { - ret = -ENOMEM; - dev_dbg(&pdev->dev, "could not map I/O memory\n"); - goto err_free; - } - - spin_lock_init(&wdt->io_lock); - wdt->boot_status = at32_wdt_get_status(); - - /* Work-around for watchdog silicon errata. */ - if (wdt->boot_status & WDIOF_CARDRESET) { - dev_info(&pdev->dev, "CPU must be reset with external " - "reset or POR due to silicon errata.\n"); - ret = -EIO; - goto err_free; - } else { - wdt->users = 0; - } - - wdt->miscdev.minor = WATCHDOG_MINOR; - wdt->miscdev.name = "watchdog"; - wdt->miscdev.fops = &at32_wdt_fops; - wdt->miscdev.parent = &pdev->dev; - - platform_set_drvdata(pdev, wdt); - - if (at32_wdt_settimeout(timeout)) { - at32_wdt_settimeout(TIMEOUT_DEFAULT); - dev_dbg(&pdev->dev, - "default timeout invalid, set to %d sec.\n", - TIMEOUT_DEFAULT); - } - - ret = misc_register(&wdt->miscdev); - if (ret) { - dev_dbg(&pdev->dev, "failed to register wdt miscdev\n"); - goto err_free; - } - - dev_info(&pdev->dev, - "AT32AP700X WDT at 0x%p, timeout %d sec (nowayout=%d)\n", - wdt->regs, wdt->timeout, nowayout); - - return 0; - -err_free: - wdt = NULL; - return ret; -} - -static int __exit at32_wdt_remove(struct platform_device *pdev) -{ - if (wdt && platform_get_drvdata(pdev) == wdt) { - /* Stop the timer before we leave */ - if (!nowayout) - at32_wdt_stop(); - - misc_deregister(&wdt->miscdev); - wdt = NULL; - } - return 0; -} - -static void at32_wdt_shutdown(struct platform_device *pdev) -{ - at32_wdt_stop(); -} - -#ifdef CONFIG_PM -static int at32_wdt_suspend(struct platform_device *pdev, pm_message_t message) -{ - at32_wdt_stop(); - return 0; -} - -static int at32_wdt_resume(struct platform_device *pdev) -{ - if (wdt->users) - at32_wdt_start(); - return 0; -} -#else -#define at32_wdt_suspend NULL -#define at32_wdt_resume NULL -#endif - -/* work with hotplug and coldplug */ -MODULE_ALIAS("platform:at32_wdt"); - -static struct platform_driver at32_wdt_driver = { - .remove = __exit_p(at32_wdt_remove), - .suspend = at32_wdt_suspend, - .resume = at32_wdt_resume, - .driver = { - .name = "at32_wdt", - }, - .shutdown = at32_wdt_shutdown, -}; - -module_platform_driver_probe(at32_wdt_driver, at32_wdt_probe); - -MODULE_AUTHOR("Hans-Christian Egtvedt "); -MODULE_DESCRIPTION("Watchdog driver for Atmel AT32AP700X"); -MODULE_LICENSE("GPL"); -- cgit v1.2.3