From 14b2d18e81f266bfa7657746507b71c6641ba4f1 Mon Sep 17 00:00:00 2001 From: Lukas Bulwahn Date: Tue, 17 Aug 2021 12:08:45 +0200 Subject: [PATCH 01/30] watchdog: remove dead iop watchdog timer driver Commit 59d3ae9a5bf6 ("ARM: remove Intel iop33x and iop13xx support") removes the config ARCH_IOP13XX in ./arch/arm/Kconfig. Hence, since then, the corresponding iop watchdog timer driver is dead code. Remove this dead driver. Signed-off-by: Lukas Bulwahn Acked-by: Arnd Bergmann Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/Kconfig | 16 --- drivers/watchdog/Makefile | 1 - drivers/watchdog/iop_wdt.c | 250 ------------------------------------- 3 files changed, 267 deletions(-) delete mode 100644 drivers/watchdog/iop_wdt.c diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index bf59faeb3de1..62cd1096d659 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -561,22 +561,6 @@ config PNX4008_WATCHDOG Say N if you are unsure. -config IOP_WATCHDOG - tristate "IOP Watchdog" - depends on ARCH_IOP13XX - select WATCHDOG_NOWAYOUT if (ARCH_IOP32X || ARCH_IOP33X) - help - Say Y here if to include support for the watchdog timer - in the Intel IOP3XX & IOP13XX I/O Processors. This driver can - be built as a module by choosing M. The module will - be called iop_wdt. - - Note: The IOP13XX watchdog does an Internal Bus Reset which will - affect both cores and the peripherals of the IOP. The ATU-X - and/or ATUe configuration registers will remain intact, but if - operating as an Root Complex and/or Central Resource, the PCI-X - and/or PCIe busses will also be reset. THIS IS A VERY BIG HAMMER. - config DAVINCI_WATCHDOG tristate "DaVinci watchdog" depends on ARCH_DAVINCI || ARCH_KEYSTONE || COMPILE_TEST diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 1bd2d6f37c53..74127ced3c23 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -56,7 +56,6 @@ obj-$(CONFIG_SAMA5D4_WATCHDOG) += sama5d4_wdt.o obj-$(CONFIG_DW_WATCHDOG) += dw_wdt.o obj-$(CONFIG_EP93XX_WATCHDOG) += ep93xx_wdt.o obj-$(CONFIG_PNX4008_WATCHDOG) += pnx4008_wdt.o -obj-$(CONFIG_IOP_WATCHDOG) += iop_wdt.o obj-$(CONFIG_DAVINCI_WATCHDOG) += davinci_wdt.o obj-$(CONFIG_K3_RTI_WATCHDOG) += rti_wdt.o obj-$(CONFIG_ORION_WATCHDOG) += orion_wdt.o diff --git a/drivers/watchdog/iop_wdt.c b/drivers/watchdog/iop_wdt.c deleted file mode 100644 index 6bf68d4750de..000000000000 --- a/drivers/watchdog/iop_wdt.c +++ /dev/null @@ -1,250 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * drivers/char/watchdog/iop_wdt.c - * - * WDT driver for Intel I/O Processors - * Copyright (C) 2005, Intel Corporation. - * - * Based on ixp4xx driver, Copyright 2004 (c) MontaVista, Software, Inc. - * - * Curt E Bruns - * Peter Milne - * Dan Williams - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static bool nowayout = WATCHDOG_NOWAYOUT; -static unsigned long wdt_status; -static unsigned long boot_status; -static DEFINE_SPINLOCK(wdt_lock); - -#define WDT_IN_USE 0 -#define WDT_OK_TO_CLOSE 1 -#define WDT_ENABLED 2 - -static unsigned long iop_watchdog_timeout(void) -{ - return (0xffffffffUL / get_iop_tick_rate()); -} - -/** - * wdt_supports_disable - determine if we are accessing a iop13xx watchdog - * or iop3xx by whether it has a disable command - */ -static int wdt_supports_disable(void) -{ - int can_disable; - - if (IOP_WDTCR_EN_ARM != IOP_WDTCR_DIS_ARM) - can_disable = 1; - else - can_disable = 0; - - return can_disable; -} - -static void wdt_enable(void) -{ - /* Arm and enable the Timer to starting counting down from 0xFFFF.FFFF - * Takes approx. 10.7s to timeout - */ - spin_lock(&wdt_lock); - write_wdtcr(IOP_WDTCR_EN_ARM); - write_wdtcr(IOP_WDTCR_EN); - spin_unlock(&wdt_lock); -} - -/* returns 0 if the timer was successfully disabled */ -static int wdt_disable(void) -{ - /* Stop Counting */ - if (wdt_supports_disable()) { - spin_lock(&wdt_lock); - write_wdtcr(IOP_WDTCR_DIS_ARM); - write_wdtcr(IOP_WDTCR_DIS); - clear_bit(WDT_ENABLED, &wdt_status); - spin_unlock(&wdt_lock); - pr_info("Disabled\n"); - return 0; - } else - return 1; -} - -static int iop_wdt_open(struct inode *inode, struct file *file) -{ - if (test_and_set_bit(WDT_IN_USE, &wdt_status)) - return -EBUSY; - - clear_bit(WDT_OK_TO_CLOSE, &wdt_status); - wdt_enable(); - set_bit(WDT_ENABLED, &wdt_status); - return stream_open(inode, file); -} - -static ssize_t iop_wdt_write(struct file *file, const char *data, size_t len, - loff_t *ppos) -{ - if (len) { - if (!nowayout) { - size_t i; - - clear_bit(WDT_OK_TO_CLOSE, &wdt_status); - - for (i = 0; i != len; i++) { - char c; - - if (get_user(c, data + i)) - return -EFAULT; - if (c == 'V') - set_bit(WDT_OK_TO_CLOSE, &wdt_status); - } - } - wdt_enable(); - } - return len; -} - -static const struct watchdog_info ident = { - .options = WDIOF_CARDRESET | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING, - .identity = "iop watchdog", -}; - -static long iop_wdt_ioctl(struct file *file, - unsigned int cmd, unsigned long arg) -{ - int options; - int ret = -ENOTTY; - int __user *argp = (int __user *)arg; - - switch (cmd) { - case WDIOC_GETSUPPORT: - if (copy_to_user(argp, &ident, sizeof(ident))) - ret = -EFAULT; - else - ret = 0; - break; - - case WDIOC_GETSTATUS: - ret = put_user(0, argp); - break; - - case WDIOC_GETBOOTSTATUS: - ret = put_user(boot_status, argp); - break; - - case WDIOC_SETOPTIONS: - if (get_user(options, (int *)arg)) - return -EFAULT; - - if (options & WDIOS_DISABLECARD) { - if (!nowayout) { - if (wdt_disable() == 0) { - set_bit(WDT_OK_TO_CLOSE, &wdt_status); - ret = 0; - } else - ret = -ENXIO; - } else - ret = 0; - } - if (options & WDIOS_ENABLECARD) { - wdt_enable(); - ret = 0; - } - break; - - case WDIOC_KEEPALIVE: - wdt_enable(); - ret = 0; - break; - - case WDIOC_GETTIMEOUT: - ret = put_user(iop_watchdog_timeout(), argp); - break; - } - return ret; -} - -static int iop_wdt_release(struct inode *inode, struct file *file) -{ - int state = 1; - if (test_bit(WDT_OK_TO_CLOSE, &wdt_status)) - if (test_bit(WDT_ENABLED, &wdt_status)) - state = wdt_disable(); - - /* if the timer is not disabled reload and notify that we are still - * going down - */ - if (state != 0) { - wdt_enable(); - pr_crit("Device closed unexpectedly - reset in %lu seconds\n", - iop_watchdog_timeout()); - } - - clear_bit(WDT_IN_USE, &wdt_status); - clear_bit(WDT_OK_TO_CLOSE, &wdt_status); - - return 0; -} - -static const struct file_operations iop_wdt_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .write = iop_wdt_write, - .unlocked_ioctl = iop_wdt_ioctl, - .compat_ioctl = compat_ptr_ioctl, - .open = iop_wdt_open, - .release = iop_wdt_release, -}; - -static struct miscdevice iop_wdt_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &iop_wdt_fops, -}; - -static int __init iop_wdt_init(void) -{ - int ret; - - /* check if the reset was caused by the watchdog timer */ - boot_status = (read_rcsr() & IOP_RCSR_WDT) ? WDIOF_CARDRESET : 0; - - /* Configure Watchdog Timeout to cause an Internal Bus (IB) Reset - * NOTE: An IB Reset will Reset both cores in the IOP342 - */ - write_wdtsr(IOP13XX_WDTCR_IB_RESET); - - /* Register after we have the device set up so we cannot race - with an open */ - ret = misc_register(&iop_wdt_miscdev); - if (ret == 0) - pr_info("timeout %lu sec\n", iop_watchdog_timeout()); - - return ret; -} - -static void __exit iop_wdt_exit(void) -{ - misc_deregister(&iop_wdt_miscdev); -} - -module_init(iop_wdt_init); -module_exit(iop_wdt_exit); - -module_param(nowayout, bool, 0); -MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started"); - -MODULE_AUTHOR("Curt E Bruns "); -MODULE_DESCRIPTION("iop watchdog timer driver"); -MODULE_LICENSE("GPL"); From 004920dfc3305cbc95cfb8449466c97a97560e25 Mon Sep 17 00:00:00 2001 From: Tang Bin Date: Sat, 14 Aug 2021 22:27:41 +0800 Subject: [PATCH 02/30] watchdog: stm32_iwdg: drop superfluous error message In the function stm32_iwdg_probe(), devm_platform_ioremap_resource has already contained error message, so drop the redundant one. Co-developed-by: Zhang Shengju Signed-off-by: Zhang Shengju Signed-off-by: Tang Bin Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20210814142741.7396-1-tangbin@cmss.chinamobile.com Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/stm32_iwdg.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/watchdog/stm32_iwdg.c b/drivers/watchdog/stm32_iwdg.c index a3436c296c97..570a71509d2a 100644 --- a/drivers/watchdog/stm32_iwdg.c +++ b/drivers/watchdog/stm32_iwdg.c @@ -237,10 +237,8 @@ static int stm32_iwdg_probe(struct platform_device *pdev) /* This is the timer base. */ wdt->regs = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(wdt->regs)) { - dev_err(dev, "Could not get resource\n"); + if (IS_ERR(wdt->regs)) return PTR_ERR(wdt->regs); - } ret = stm32_iwdg_clk_init(pdev, wdt); if (ret) From 164483c735190775f29d0dcbac0363adc51a068d Mon Sep 17 00:00:00 2001 From: Ahmad Fatoum Date: Mon, 9 Aug 2021 18:20:31 +0200 Subject: [PATCH 03/30] watchdog: f71808e_wdt: fix inaccurate report in WDIOC_GETTIMEOUT The fintek watchdog timer can configure timeouts of second granularity only up to 255 seconds. Beyond that, the timeout needs to be configured with minute granularity. WDIOC_GETTIMEOUT should report the actual timeout configured, not just echo back the timeout configured by the user. Do so. Fixes: 96cb4eb019ce ("watchdog: f71808e_wdt: new watchdog driver for Fintek F71808E and F71882FG") Suggested-by: Guenter Roeck Reviewed-by: Guenter Roeck Signed-off-by: Ahmad Fatoum Link: https://lore.kernel.org/r/5e17960fe8cc0e3cb2ba53de4730b75d9a0f33d5.1628525954.git-series.a.fatoum@pengutronix.de Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/f71808e_wdt.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/watchdog/f71808e_wdt.c b/drivers/watchdog/f71808e_wdt.c index f60beec1bbae..f7d82d261913 100644 --- a/drivers/watchdog/f71808e_wdt.c +++ b/drivers/watchdog/f71808e_wdt.c @@ -228,15 +228,17 @@ static int watchdog_set_timeout(int timeout) mutex_lock(&watchdog.lock); - watchdog.timeout = timeout; if (timeout > 0xff) { watchdog.timer_val = DIV_ROUND_UP(timeout, 60); watchdog.minutes_mode = true; + timeout = watchdog.timer_val * 60; } else { watchdog.timer_val = timeout; watchdog.minutes_mode = false; } + watchdog.timeout = timeout; + mutex_unlock(&watchdog.lock); return 0; From bba6c477d52ea1da0c2cd13a57ca8f00508d2625 Mon Sep 17 00:00:00 2001 From: Ahmad Fatoum Date: Mon, 9 Aug 2021 18:20:32 +0200 Subject: [PATCH 04/30] watchdog: f71808e_wdt: remove superfluous global max_timeout never served any purpose over WATCHDOG_MAX_TIMEOUT, which it was initialized with. Drop it. Suggested-by: Guenter Roeck Signed-off-by: Ahmad Fatoum Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/d1f8cda90283855633537adee0af2c6b00a9ec25.1628525954.git-series.a.fatoum@pengutronix.de Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/f71808e_wdt.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/watchdog/f71808e_wdt.c b/drivers/watchdog/f71808e_wdt.c index f7d82d261913..3a0b29cb5854 100644 --- a/drivers/watchdog/f71808e_wdt.c +++ b/drivers/watchdog/f71808e_wdt.c @@ -81,7 +81,6 @@ static unsigned short force_id; module_param(force_id, ushort, 0); MODULE_PARM_DESC(force_id, "Override the detected device ID"); -static const int max_timeout = WATCHDOG_MAX_TIMEOUT; static int timeout = WATCHDOG_TIMEOUT; /* default timeout in seconds */ module_param(timeout, int, 0); MODULE_PARM_DESC(timeout, @@ -221,7 +220,7 @@ static inline void superio_exit(int base) static int watchdog_set_timeout(int timeout) { if (timeout <= 0 - || timeout > max_timeout) { + || timeout > WATCHDOG_MAX_TIMEOUT) { pr_err("watchdog timeout out of range\n"); return -EINVAL; } @@ -720,7 +719,7 @@ static int __init watchdog_init(int sioaddr) if (start_withtimeout) { if (start_withtimeout <= 0 - || start_withtimeout > max_timeout) { + || start_withtimeout > WATCHDOG_MAX_TIMEOUT) { pr_err("starting timeout out of range\n"); err = -EINVAL; goto exit_miscdev; From c3a291e18dfe8b82b67fd0de2ff7f3cdb17dea62 Mon Sep 17 00:00:00 2001 From: Ahmad Fatoum Date: Mon, 9 Aug 2021 18:20:33 +0200 Subject: [PATCH 05/30] watchdog: f71808e_wdt: constify static array The fintek_wdt_names is meant for read-only use and is currently not modified. Mark it const to catch future writes. Suggested-by: Guenter Roeck Signed-off-by: Ahmad Fatoum Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/9720c5a1efcef861da68b693453bb3eb3c21af37.1628525954.git-series.a.fatoum@pengutronix.de Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/f71808e_wdt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/watchdog/f71808e_wdt.c b/drivers/watchdog/f71808e_wdt.c index 3a0b29cb5854..8913747517fa 100644 --- a/drivers/watchdog/f71808e_wdt.c +++ b/drivers/watchdog/f71808e_wdt.c @@ -112,7 +112,7 @@ MODULE_PARM_DESC(start_withtimeout, "Start watchdog timer on module load with" enum chips { f71808fg, f71858fg, f71862fg, f71868, f71869, f71882fg, f71889fg, f81803, f81865, f81866}; -static const char *f71808e_names[] = { +static const char * const f71808e_names[] = { "f71808fg", "f71858fg", "f71862fg", From 3a2c489513e9bdd12d4aaf484c9974d216526874 Mon Sep 17 00:00:00 2001 From: Ahmad Fatoum Date: Mon, 9 Aug 2021 18:20:34 +0200 Subject: [PATCH 06/30] watchdog: f71808e_wdt: rename variant-independent identifiers appropriately Code for the common parts of the driver either uses watchdog_ as prefix for the watchdog API or f71808e_ for everything else. The driver now supports 9 more variants besides the f71808e, so let's rename the common parts to start with fintek_wdt_ instead. This makes code browsing easier, because it's readily apparent that functions are not variant-specific. Some watchdog_-prefixed functions remain, but these will be dropped altogether with the move to the kernel watchdog API in a later commit. Signed-off-by: Ahmad Fatoum Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/31805c6aeb8d161f852ddad7c32b91319f924988.1628525954.git-series.a.fatoum@pengutronix.de Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/f71808e_wdt.c | 66 +++++++++++++++++----------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/drivers/watchdog/f71808e_wdt.c b/drivers/watchdog/f71808e_wdt.c index 8913747517fa..82da9bad66ec 100644 --- a/drivers/watchdog/f71808e_wdt.c +++ b/drivers/watchdog/f71808e_wdt.c @@ -112,7 +112,7 @@ MODULE_PARM_DESC(start_withtimeout, "Start watchdog timer on module load with" enum chips { f71808fg, f71858fg, f71862fg, f71868, f71869, f71882fg, f71889fg, f81803, f81865, f81866}; -static const char * const f71808e_names[] = { +static const char * const fintek_wdt_names[] = { "f71808fg", "f71858fg", "f71862fg", @@ -135,7 +135,7 @@ static inline int superio_enter(int base); static inline void superio_select(int base, int ld); static inline void superio_exit(int base); -struct watchdog_data { +struct fintek_wdt { unsigned short sioaddr; enum chips type; unsigned long opened; @@ -151,7 +151,7 @@ struct watchdog_data { char caused_reboot; /* last reboot was by the watchdog */ }; -static struct watchdog_data watchdog = { +static struct fintek_wdt watchdog = { .lock = __MUTEX_INITIALIZER(watchdog.lock), }; @@ -217,7 +217,7 @@ static inline void superio_exit(int base) release_region(base, 2); } -static int watchdog_set_timeout(int timeout) +static int fintek_wdt_set_timeout(int timeout) { if (timeout <= 0 || timeout > WATCHDOG_MAX_TIMEOUT) { @@ -243,7 +243,7 @@ static int watchdog_set_timeout(int timeout) return 0; } -static int watchdog_set_pulse_width(unsigned int pw) +static int fintek_wdt_set_pulse_width(unsigned int pw) { int err = 0; unsigned int t1 = 25, t2 = 125, t3 = 5000; @@ -277,7 +277,7 @@ exit_unlock: return err; } -static int watchdog_keepalive(void) +static int fintek_wdt_keepalive(void) { int err = 0; @@ -307,13 +307,13 @@ exit_unlock: return err; } -static int watchdog_start(void) +static int fintek_wdt_start(void) { int err; u8 tmp; /* Make sure we don't die as soon as the watchdog is enabled below */ - err = watchdog_keepalive(); + err = fintek_wdt_keepalive(); if (err) return err; @@ -434,7 +434,7 @@ exit_unlock: return err; } -static int watchdog_stop(void) +static int fintek_wdt_stop(void) { int err = 0; @@ -466,7 +466,7 @@ static int watchdog_get_status(void) return status; } -static bool watchdog_is_running(void) +static bool fintek_wdt_is_running(void) { /* * if we fail to determine the watchdog's status assume it to be @@ -500,7 +500,7 @@ static int watchdog_open(struct inode *inode, struct file *file) if (test_and_set_bit(0, &watchdog.opened)) return -EBUSY; - err = watchdog_start(); + err = fintek_wdt_start(); if (err) { clear_bit(0, &watchdog.opened); return err; @@ -518,10 +518,10 @@ static int watchdog_release(struct inode *inode, struct file *file) clear_bit(0, &watchdog.opened); if (!watchdog.expect_close) { - watchdog_keepalive(); + fintek_wdt_keepalive(); pr_crit("Unexpected close, not stopping watchdog!\n"); } else if (!nowayout) { - watchdog_stop(); + fintek_wdt_stop(); } return 0; } @@ -562,7 +562,7 @@ static ssize_t watchdog_write(struct file *file, const char __user *buf, } /* someone wrote to us, we should restart timer */ - watchdog_keepalive(); + fintek_wdt_keepalive(); } return count; } @@ -609,24 +609,24 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd, return -EFAULT; if (new_options & WDIOS_DISABLECARD) - watchdog_stop(); + fintek_wdt_stop(); if (new_options & WDIOS_ENABLECARD) - return watchdog_start(); + return fintek_wdt_start(); fallthrough; case WDIOC_KEEPALIVE: - watchdog_keepalive(); + fintek_wdt_keepalive(); return 0; case WDIOC_SETTIMEOUT: if (get_user(new_timeout, uarg.i)) return -EFAULT; - if (watchdog_set_timeout(new_timeout)) + if (fintek_wdt_set_timeout(new_timeout)) return -EINVAL; - watchdog_keepalive(); + fintek_wdt_keepalive(); fallthrough; case WDIOC_GETTIMEOUT: @@ -642,7 +642,7 @@ static int watchdog_notify_sys(struct notifier_block *this, unsigned long code, void *unused) { if (code == SYS_DOWN || code == SYS_HALT) - watchdog_stop(); + fintek_wdt_stop(); return NOTIFY_DONE; } @@ -680,7 +680,7 @@ static int __init watchdog_init(int sioaddr) snprintf(watchdog.ident.identity, sizeof(watchdog.ident.identity), "%s watchdog", - f71808e_names[watchdog.type]); + fintek_wdt_names[watchdog.type]); err = superio_enter(sioaddr); if (err) @@ -699,10 +699,10 @@ static int __init watchdog_init(int sioaddr) superio_exit(sioaddr); - err = watchdog_set_timeout(timeout); + err = fintek_wdt_set_timeout(timeout); if (err) return err; - err = watchdog_set_pulse_width(pulse_width); + err = fintek_wdt_set_pulse_width(pulse_width); if (err) return err; @@ -725,7 +725,7 @@ static int __init watchdog_init(int sioaddr) goto exit_miscdev; } - err = watchdog_start(); + err = fintek_wdt_start(); if (err) { pr_err("cannot start watchdog timer\n"); goto exit_miscdev; @@ -773,7 +773,7 @@ exit_reboot: return err; } -static int __init f71808e_find(int sioaddr) +static int __init fintek_wdt_find(int sioaddr) { u16 devid; int err = superio_enter(sioaddr); @@ -829,14 +829,14 @@ static int __init f71808e_find(int sioaddr) } pr_info("Found %s watchdog chip, revision %d\n", - f71808e_names[watchdog.type], + fintek_wdt_names[watchdog.type], (int)superio_inb(sioaddr, SIO_REG_DEVREV)); exit: superio_exit(sioaddr); return err; } -static int __init f71808e_init(void) +static int __init fintek_wdt_init(void) { static const unsigned short addrs[] = { 0x2e, 0x4e }; int err = -ENODEV; @@ -848,7 +848,7 @@ static int __init f71808e_init(void) } for (i = 0; i < ARRAY_SIZE(addrs); i++) { - err = f71808e_find(addrs[i]); + err = fintek_wdt_find(addrs[i]); if (err == 0) break; } @@ -858,11 +858,11 @@ static int __init f71808e_init(void) return watchdog_init(addrs[i]); } -static void __exit f71808e_exit(void) +static void __exit fintek_wdt_exit(void) { - if (watchdog_is_running()) { + if (fintek_wdt_is_running()) { pr_warn("Watchdog timer still running, stopping it\n"); - watchdog_stop(); + fintek_wdt_stop(); } misc_deregister(&watchdog_miscdev); unregister_reboot_notifier(&watchdog_notifier); @@ -872,5 +872,5 @@ MODULE_DESCRIPTION("F71808E Watchdog Driver"); MODULE_AUTHOR("Giel van Schijndel "); MODULE_LICENSE("GPL"); -module_init(f71808e_init); -module_exit(f71808e_exit); +module_init(fintek_wdt_init); +module_exit(fintek_wdt_exit); From 8bea27edc393bdb66b1b586447e97146dce37fbc Mon Sep 17 00:00:00 2001 From: Ahmad Fatoum Date: Mon, 9 Aug 2021 18:20:35 +0200 Subject: [PATCH 07/30] watchdog: f71808e_wdt: migrate to new kernel watchdog API Migrating the driver lets us drop the watchdog misc device boilerplate and reduces size by 285 lines. It also brings us support for new functionality like CONFIG_WATCHDOG_HANDLE_BOOT_ENABLED. This incurs a slight backwards-compatibility break, because the new kernel watchdog API doesn't support unloading modules for drivers whose watchdog hardware is reported to be running. This means following scenario will be no longer supported: - BIOS has enabled watchdog - Module is loaded and unloaded without opening watchdog - module_exit is expected to succeed and disable watchdog HW Signed-off-by: Ahmad Fatoum Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/35d9dbf57b58c5f003cef31dc256ec2fec044524.1628525954.git-series.a.fatoum@pengutronix.de Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/Kconfig | 1 + drivers/watchdog/f71808e_wdt.c | 387 +++++---------------------------- 2 files changed, 57 insertions(+), 331 deletions(-) diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 62cd1096d659..d0645c7be4e6 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -1036,6 +1036,7 @@ config EBC_C384_WDT config F71808E_WDT tristate "Fintek F718xx, F818xx Super I/O Watchdog" depends on X86 + select WATCHDOG_CORE help This is the driver for the hardware watchdog on the Fintek F71808E, F71862FG, F71868, F71869, F71882FG, F71889FG, F81803, F81865, and diff --git a/drivers/watchdog/f71808e_wdt.c b/drivers/watchdog/f71808e_wdt.c index 82da9bad66ec..67e344627586 100644 --- a/drivers/watchdog/f71808e_wdt.c +++ b/drivers/watchdog/f71808e_wdt.c @@ -9,16 +9,10 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include -#include #include #include #include -#include #include -#include -#include -#include -#include #include #define DRVNAME "f71808e_wdt" @@ -136,24 +130,18 @@ static inline void superio_select(int base, int ld); static inline void superio_exit(int base); struct fintek_wdt { + struct watchdog_device wdd; unsigned short sioaddr; enum chips type; - unsigned long opened; - struct mutex lock; - char expect_close; struct watchdog_info ident; - unsigned short timeout; u8 timer_val; /* content for the wd_time register */ char minutes_mode; u8 pulse_val; /* pulse width flag */ char pulse_mode; /* enable pulse output mode? */ - char caused_reboot; /* last reboot was by the watchdog */ }; -static struct fintek_wdt watchdog = { - .lock = __MUTEX_INITIALIZER(watchdog.lock), -}; +static struct fintek_wdt watchdog; /* Super I/O functions */ static inline int superio_inb(int base, int reg) @@ -217,16 +205,8 @@ static inline void superio_exit(int base) release_region(base, 2); } -static int fintek_wdt_set_timeout(int timeout) +static int fintek_wdt_set_timeout(struct watchdog_device *wdd, unsigned int timeout) { - if (timeout <= 0 - || timeout > WATCHDOG_MAX_TIMEOUT) { - pr_err("watchdog timeout out of range\n"); - return -EINVAL; - } - - mutex_lock(&watchdog.lock); - if (timeout > 0xff) { watchdog.timer_val = DIV_ROUND_UP(timeout, 60); watchdog.minutes_mode = true; @@ -236,16 +216,13 @@ static int fintek_wdt_set_timeout(int timeout) watchdog.minutes_mode = false; } - watchdog.timeout = timeout; - - mutex_unlock(&watchdog.lock); + wdd->timeout = timeout; return 0; } static int fintek_wdt_set_pulse_width(unsigned int pw) { - int err = 0; unsigned int t1 = 25, t2 = 125, t3 = 5000; if (watchdog.type == f71868) { @@ -254,8 +231,6 @@ static int fintek_wdt_set_pulse_width(unsigned int pw) t3 = 6000; } - mutex_lock(&watchdog.lock); - if (pw <= 1) { watchdog.pulse_val = 0; } else if (pw <= t1) { @@ -266,25 +241,21 @@ static int fintek_wdt_set_pulse_width(unsigned int pw) watchdog.pulse_val = 3; } else { pr_err("pulse width out of range\n"); - err = -EINVAL; - goto exit_unlock; + return -EINVAL; } watchdog.pulse_mode = pw; -exit_unlock: - mutex_unlock(&watchdog.lock); - return err; + return 0; } -static int fintek_wdt_keepalive(void) +static int fintek_wdt_keepalive(struct watchdog_device *wdd) { - int err = 0; + int err; - mutex_lock(&watchdog.lock); err = superio_enter(watchdog.sioaddr); if (err) - goto exit_unlock; + return err; superio_select(watchdog.sioaddr, SIO_F71808FG_LD_WDT); if (watchdog.minutes_mode) @@ -302,25 +273,22 @@ static int fintek_wdt_keepalive(void) superio_exit(watchdog.sioaddr); -exit_unlock: - mutex_unlock(&watchdog.lock); - return err; + return 0; } -static int fintek_wdt_start(void) +static int fintek_wdt_start(struct watchdog_device *wdd) { int err; u8 tmp; /* Make sure we don't die as soon as the watchdog is enabled below */ - err = fintek_wdt_keepalive(); + err = fintek_wdt_keepalive(wdd); if (err) return err; - mutex_lock(&watchdog.lock); err = superio_enter(watchdog.sioaddr); if (err) - goto exit_unlock; + return err; superio_select(watchdog.sioaddr, SIO_F71808FG_LD_WDT); /* Watchdog pin configuration */ @@ -428,20 +396,17 @@ static int fintek_wdt_start(void) exit_superio: superio_exit(watchdog.sioaddr); -exit_unlock: - mutex_unlock(&watchdog.lock); return err; } -static int fintek_wdt_stop(void) +static int fintek_wdt_stop(struct watchdog_device *wdd) { - int err = 0; + int err; - mutex_lock(&watchdog.lock); err = superio_enter(watchdog.sioaddr); if (err) - goto exit_unlock; + return err; superio_select(watchdog.sioaddr, SIO_F71808FG_LD_WDT); superio_clear_bit(watchdog.sioaddr, F71808FG_REG_WDT_CONF, @@ -449,232 +414,31 @@ static int fintek_wdt_stop(void) superio_exit(watchdog.sioaddr); -exit_unlock: - mutex_unlock(&watchdog.lock); - - return err; -} - -static int watchdog_get_status(void) -{ - int status = 0; - - mutex_lock(&watchdog.lock); - status = (watchdog.caused_reboot) ? WDIOF_CARDRESET : 0; - mutex_unlock(&watchdog.lock); - - return status; -} - -static bool fintek_wdt_is_running(void) -{ - /* - * if we fail to determine the watchdog's status assume it to be - * running to be on the safe side - */ - bool is_running = true; - - mutex_lock(&watchdog.lock); - if (superio_enter(watchdog.sioaddr)) - goto exit_unlock; - superio_select(watchdog.sioaddr, SIO_F71808FG_LD_WDT); - - is_running = (superio_inb(watchdog.sioaddr, SIO_REG_ENABLE) & BIT(0)) - && (superio_inb(watchdog.sioaddr, F71808FG_REG_WDT_CONF) - & BIT(F71808FG_FLAG_WD_EN)); - - superio_exit(watchdog.sioaddr); - -exit_unlock: - mutex_unlock(&watchdog.lock); - return is_running; -} - -/* /dev/watchdog api */ - -static int watchdog_open(struct inode *inode, struct file *file) -{ - int err; - - /* If the watchdog is alive we don't need to start it again */ - if (test_and_set_bit(0, &watchdog.opened)) - return -EBUSY; - - err = fintek_wdt_start(); - if (err) { - clear_bit(0, &watchdog.opened); - return err; - } - - if (nowayout) - __module_get(THIS_MODULE); - - watchdog.expect_close = 0; - return stream_open(inode, file); -} - -static int watchdog_release(struct inode *inode, struct file *file) -{ - clear_bit(0, &watchdog.opened); - - if (!watchdog.expect_close) { - fintek_wdt_keepalive(); - pr_crit("Unexpected close, not stopping watchdog!\n"); - } else if (!nowayout) { - fintek_wdt_stop(); - } return 0; } -/* - * watchdog_write: - * @file: file handle to the watchdog - * @buf: buffer to write - * @count: count of bytes - * @ppos: pointer to the position to write. No seeks allowed - * - * A write to a watchdog device is defined as a keepalive signal. Any - * write of data will do, as we we don't define content meaning. - */ - -static ssize_t watchdog_write(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) +static bool fintek_wdt_is_running(u8 wdt_conf) { - if (count) { - if (!nowayout) { - size_t i; - - /* In case it was set long ago */ - bool expect_close = false; - - for (i = 0; i != count; i++) { - char c; - if (get_user(c, buf + i)) - return -EFAULT; - if (c == 'V') - expect_close = true; - } - - /* Properly order writes across fork()ed processes */ - mutex_lock(&watchdog.lock); - watchdog.expect_close = expect_close; - mutex_unlock(&watchdog.lock); - } - - /* someone wrote to us, we should restart timer */ - fintek_wdt_keepalive(); - } - return count; + return (superio_inb(watchdog.sioaddr, SIO_REG_ENABLE) & BIT(0)) + && (wdt_conf & BIT(F71808FG_FLAG_WD_EN)); } -/* - * watchdog_ioctl: - * @inode: inode of the device - * @file: file handle to the device - * @cmd: watchdog command - * @arg: argument pointer - * - * The watchdog API defines a common set of functions for all watchdogs - * according to their available features. - */ -static long watchdog_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) -{ - int status; - int new_options; - int new_timeout; - union { - struct watchdog_info __user *ident; - int __user *i; - } uarg; - - uarg.i = (int __user *)arg; - - switch (cmd) { - case WDIOC_GETSUPPORT: - return copy_to_user(uarg.ident, &watchdog.ident, - sizeof(watchdog.ident)) ? -EFAULT : 0; - - case WDIOC_GETSTATUS: - status = watchdog_get_status(); - if (status < 0) - return status; - return put_user(status, uarg.i); - - case WDIOC_GETBOOTSTATUS: - return put_user(0, uarg.i); - - case WDIOC_SETOPTIONS: - if (get_user(new_options, uarg.i)) - return -EFAULT; - - if (new_options & WDIOS_DISABLECARD) - fintek_wdt_stop(); - - if (new_options & WDIOS_ENABLECARD) - return fintek_wdt_start(); - fallthrough; - - case WDIOC_KEEPALIVE: - fintek_wdt_keepalive(); - return 0; - - case WDIOC_SETTIMEOUT: - if (get_user(new_timeout, uarg.i)) - return -EFAULT; - - if (fintek_wdt_set_timeout(new_timeout)) - return -EINVAL; - - fintek_wdt_keepalive(); - fallthrough; - - case WDIOC_GETTIMEOUT: - return put_user(watchdog.timeout, uarg.i); - - default: - return -ENOTTY; - - } -} - -static int watchdog_notify_sys(struct notifier_block *this, unsigned long code, - void *unused) -{ - if (code == SYS_DOWN || code == SYS_HALT) - fintek_wdt_stop(); - return NOTIFY_DONE; -} - -static const struct file_operations watchdog_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .open = watchdog_open, - .release = watchdog_release, - .write = watchdog_write, - .unlocked_ioctl = watchdog_ioctl, - .compat_ioctl = compat_ptr_ioctl, -}; - -static struct miscdevice watchdog_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &watchdog_fops, -}; - -static struct notifier_block watchdog_notifier = { - .notifier_call = watchdog_notify_sys, +static const struct watchdog_ops fintek_wdt_ops = { + .owner = THIS_MODULE, + .start = fintek_wdt_start, + .stop = fintek_wdt_stop, + .ping = fintek_wdt_keepalive, + .set_timeout = fintek_wdt_set_timeout, }; static int __init watchdog_init(int sioaddr) { + struct watchdog_device *wdd; int wdt_conf, err = 0; - /* No need to lock watchdog.lock here because no entry points - * into the module have been registered yet. - */ watchdog.sioaddr = sioaddr; - watchdog.ident.options = WDIOF_MAGICCLOSE + watchdog.ident.options = WDIOF_SETTIMEOUT + | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING | WDIOF_CARDRESET; @@ -688,7 +452,6 @@ static int __init watchdog_init(int sioaddr) superio_select(watchdog.sioaddr, SIO_F71808FG_LD_WDT); wdt_conf = superio_inb(sioaddr, F71808FG_REG_WDT_CONF); - watchdog.caused_reboot = wdt_conf & BIT(F71808FG_FLAG_WDTMOUT_STS); /* * We don't want WDTMOUT_STS to stick around till regular reboot. @@ -697,80 +460,47 @@ static int __init watchdog_init(int sioaddr) superio_outb(sioaddr, F71808FG_REG_WDT_CONF, wdt_conf | BIT(F71808FG_FLAG_WDTMOUT_STS)); + wdd = &watchdog.wdd; + + if (fintek_wdt_is_running(wdt_conf)) + set_bit(WDOG_HW_RUNNING, &wdd->status); + superio_exit(sioaddr); - err = fintek_wdt_set_timeout(timeout); - if (err) - return err; - err = fintek_wdt_set_pulse_width(pulse_width); - if (err) - return err; + wdd->info = &watchdog.ident; + wdd->ops = &fintek_wdt_ops; + wdd->min_timeout = 1; + wdd->max_timeout = WATCHDOG_MAX_TIMEOUT; - err = register_reboot_notifier(&watchdog_notifier); - if (err) - return err; + watchdog_set_nowayout(wdd, nowayout); + watchdog_stop_on_unregister(wdd); + watchdog_stop_on_reboot(wdd); + watchdog_init_timeout(wdd, start_withtimeout ?: timeout, NULL); - err = misc_register(&watchdog_miscdev); - if (err) { - pr_err("cannot register miscdev on minor=%d\n", - watchdog_miscdev.minor); - goto exit_reboot; - } + if (wdt_conf & BIT(F71808FG_FLAG_WDTMOUT_STS)) + wdd->bootstatus = WDIOF_CARDRESET; + + /* + * WATCHDOG_HANDLE_BOOT_ENABLED can result in keepalive being directly + * called without a set_timeout before, so it needs to be done here + * unconditionally. + */ + fintek_wdt_set_timeout(wdd, wdd->timeout); + fintek_wdt_set_pulse_width(pulse_width); if (start_withtimeout) { - if (start_withtimeout <= 0 - || start_withtimeout > WATCHDOG_MAX_TIMEOUT) { - pr_err("starting timeout out of range\n"); - err = -EINVAL; - goto exit_miscdev; - } - - err = fintek_wdt_start(); + err = fintek_wdt_start(wdd); if (err) { pr_err("cannot start watchdog timer\n"); - goto exit_miscdev; + return err; } - mutex_lock(&watchdog.lock); - err = superio_enter(sioaddr); - if (err) - goto exit_unlock; - superio_select(watchdog.sioaddr, SIO_F71808FG_LD_WDT); - - if (start_withtimeout > 0xff) { - /* select minutes for timer units */ - superio_set_bit(sioaddr, F71808FG_REG_WDT_CONF, - F71808FG_FLAG_WD_UNIT); - superio_outb(sioaddr, F71808FG_REG_WD_TIME, - DIV_ROUND_UP(start_withtimeout, 60)); - } else { - /* select seconds for timer units */ - superio_clear_bit(sioaddr, F71808FG_REG_WDT_CONF, - F71808FG_FLAG_WD_UNIT); - superio_outb(sioaddr, F71808FG_REG_WD_TIME, - start_withtimeout); - } - - superio_exit(sioaddr); - mutex_unlock(&watchdog.lock); - - if (nowayout) - __module_get(THIS_MODULE); - + set_bit(WDOG_HW_RUNNING, &wdd->status); pr_info("watchdog started with initial timeout of %u sec\n", start_withtimeout); } - return 0; - -exit_unlock: - mutex_unlock(&watchdog.lock); -exit_miscdev: - misc_deregister(&watchdog_miscdev); -exit_reboot: - unregister_reboot_notifier(&watchdog_notifier); - - return err; + return watchdog_register_device(wdd); } static int __init fintek_wdt_find(int sioaddr) @@ -860,12 +590,7 @@ static int __init fintek_wdt_init(void) static void __exit fintek_wdt_exit(void) { - if (fintek_wdt_is_running()) { - pr_warn("Watchdog timer still running, stopping it\n"); - fintek_wdt_stop(); - } - misc_deregister(&watchdog_miscdev); - unregister_reboot_notifier(&watchdog_notifier); + watchdog_unregister_device(&watchdog.wdd); } MODULE_DESCRIPTION("F71808E Watchdog Driver"); From 27e0fe00a5c618571ab1533aec5c48b7200efb09 Mon Sep 17 00:00:00 2001 From: Ahmad Fatoum Date: Mon, 9 Aug 2021 18:20:36 +0200 Subject: [PATCH 08/30] watchdog: f71808e_wdt: refactor to platform device/driver pair Driver so far wasn't ported to the driver model and registered the watchdog device out of the init after probing the I/O ports for a watchdog with correct vendor and device revision. Keep the device detection part at init time, but move watchdog registration to a platform driver probe function. Suggested-by: Guenter Roeck Signed-off-by: Ahmad Fatoum Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/9e1088839662e5c357286cab0b9de0bb0602e4fd.1628525954.git-series.a.fatoum@pengutronix.de Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/f71808e_wdt.c | 49 +++++++++++++++++++++++++++++----- 1 file changed, 42 insertions(+), 7 deletions(-) diff --git a/drivers/watchdog/f71808e_wdt.c b/drivers/watchdog/f71808e_wdt.c index 67e344627586..a23f68be137f 100644 --- a/drivers/watchdog/f71808e_wdt.c +++ b/drivers/watchdog/f71808e_wdt.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #define DRVNAME "f71808e_wdt" @@ -431,10 +432,19 @@ static const struct watchdog_ops fintek_wdt_ops = { .set_timeout = fintek_wdt_set_timeout, }; -static int __init watchdog_init(int sioaddr) +static int fintek_wdt_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; struct watchdog_device *wdd; int wdt_conf, err = 0; + struct resource *res; + int sioaddr; + + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (!res) + return -ENXIO; + + sioaddr = res->start; watchdog.sioaddr = sioaddr; watchdog.ident.options = WDIOF_SETTIMEOUT @@ -467,6 +477,7 @@ static int __init watchdog_init(int sioaddr) superio_exit(sioaddr); + wdd->parent = dev; wdd->info = &watchdog.ident; wdd->ops = &fintek_wdt_ops; wdd->min_timeout = 1; @@ -491,16 +502,16 @@ static int __init watchdog_init(int sioaddr) if (start_withtimeout) { err = fintek_wdt_start(wdd); if (err) { - pr_err("cannot start watchdog timer\n"); + dev_err(dev, "cannot start watchdog timer\n"); return err; } set_bit(WDOG_HW_RUNNING, &wdd->status); - pr_info("watchdog started with initial timeout of %u sec\n", - start_withtimeout); + dev_info(dev, "watchdog started with initial timeout of %u sec\n", + start_withtimeout); } - return watchdog_register_device(wdd); + return devm_watchdog_register_device(dev, wdd); } static int __init fintek_wdt_find(int sioaddr) @@ -566,9 +577,19 @@ exit: return err; } +static struct platform_driver fintek_wdt_driver = { + .probe = fintek_wdt_probe, + .driver = { + .name = DRVNAME, + }, +}; + +static struct platform_device *fintek_wdt_pdev; + static int __init fintek_wdt_init(void) { static const unsigned short addrs[] = { 0x2e, 0x4e }; + struct resource wdt_res = {}; int err = -ENODEV; int i; @@ -585,12 +606,26 @@ static int __init fintek_wdt_init(void) if (i == ARRAY_SIZE(addrs)) return err; - return watchdog_init(addrs[i]); + platform_driver_register(&fintek_wdt_driver); + + wdt_res.name = "superio port"; + wdt_res.flags = IORESOURCE_IO; + wdt_res.start = addrs[i]; + wdt_res.end = addrs[i] + 1; + + fintek_wdt_pdev = platform_device_register_simple(DRVNAME, -1, &wdt_res, 1); + if (IS_ERR(fintek_wdt_pdev)) { + platform_driver_unregister(&fintek_wdt_driver); + return PTR_ERR(fintek_wdt_pdev); + } + + return 0; } static void __exit fintek_wdt_exit(void) { - watchdog_unregister_device(&watchdog.wdd); + platform_device_unregister(fintek_wdt_pdev); + platform_driver_unregister(&fintek_wdt_driver); } MODULE_DESCRIPTION("F71808E Watchdog Driver"); From a7876735f24f7256043967683271e4c631d1e7e7 Mon Sep 17 00:00:00 2001 From: Ahmad Fatoum Date: Mon, 9 Aug 2021 18:20:37 +0200 Subject: [PATCH 09/30] watchdog: f71808e_wdt: dynamically allocate watchdog driver data While the driver will only match against a single device, convention is to dynamically allocate the driver data. Suggested-by: Guenter Roeck Signed-off-by: Ahmad Fatoum Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/1baffdc410d6476cccffe9712cec56350335812a.1628525954.git-series.a.fatoum@pengutronix.de Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/f71808e_wdt.c | 198 ++++++++++++++++++--------------- 1 file changed, 111 insertions(+), 87 deletions(-) diff --git a/drivers/watchdog/f71808e_wdt.c b/drivers/watchdog/f71808e_wdt.c index a23f68be137f..ee90c5f943f9 100644 --- a/drivers/watchdog/f71808e_wdt.c +++ b/drivers/watchdog/f71808e_wdt.c @@ -142,7 +142,9 @@ struct fintek_wdt { char pulse_mode; /* enable pulse output mode? */ }; -static struct fintek_wdt watchdog; +struct fintek_wdt_pdata { + enum chips type; +}; /* Super I/O functions */ static inline int superio_inb(int base, int reg) @@ -208,13 +210,15 @@ static inline void superio_exit(int base) static int fintek_wdt_set_timeout(struct watchdog_device *wdd, unsigned int timeout) { + struct fintek_wdt *wd = watchdog_get_drvdata(wdd); + if (timeout > 0xff) { - watchdog.timer_val = DIV_ROUND_UP(timeout, 60); - watchdog.minutes_mode = true; - timeout = watchdog.timer_val * 60; + wd->timer_val = DIV_ROUND_UP(timeout, 60); + wd->minutes_mode = true; + timeout = wd->timer_val * 60; } else { - watchdog.timer_val = timeout; - watchdog.minutes_mode = false; + wd->timer_val = timeout; + wd->minutes_mode = false; } wdd->timeout = timeout; @@ -222,63 +226,65 @@ static int fintek_wdt_set_timeout(struct watchdog_device *wdd, unsigned int time return 0; } -static int fintek_wdt_set_pulse_width(unsigned int pw) +static int fintek_wdt_set_pulse_width(struct fintek_wdt *wd, unsigned int pw) { unsigned int t1 = 25, t2 = 125, t3 = 5000; - if (watchdog.type == f71868) { + if (wd->type == f71868) { t1 = 30; t2 = 150; t3 = 6000; } if (pw <= 1) { - watchdog.pulse_val = 0; + wd->pulse_val = 0; } else if (pw <= t1) { - watchdog.pulse_val = 1; + wd->pulse_val = 1; } else if (pw <= t2) { - watchdog.pulse_val = 2; + wd->pulse_val = 2; } else if (pw <= t3) { - watchdog.pulse_val = 3; + wd->pulse_val = 3; } else { pr_err("pulse width out of range\n"); return -EINVAL; } - watchdog.pulse_mode = pw; + wd->pulse_mode = pw; return 0; } static int fintek_wdt_keepalive(struct watchdog_device *wdd) { + struct fintek_wdt *wd = watchdog_get_drvdata(wdd); int err; - err = superio_enter(watchdog.sioaddr); + err = superio_enter(wd->sioaddr); if (err) return err; - superio_select(watchdog.sioaddr, SIO_F71808FG_LD_WDT); + superio_select(wd->sioaddr, SIO_F71808FG_LD_WDT); - if (watchdog.minutes_mode) + if (wd->minutes_mode) /* select minutes for timer units */ - superio_set_bit(watchdog.sioaddr, F71808FG_REG_WDT_CONF, + superio_set_bit(wd->sioaddr, F71808FG_REG_WDT_CONF, F71808FG_FLAG_WD_UNIT); else /* select seconds for timer units */ - superio_clear_bit(watchdog.sioaddr, F71808FG_REG_WDT_CONF, + superio_clear_bit(wd->sioaddr, F71808FG_REG_WDT_CONF, F71808FG_FLAG_WD_UNIT); /* Set timer value */ - superio_outb(watchdog.sioaddr, F71808FG_REG_WD_TIME, - watchdog.timer_val); + superio_outb(wd->sioaddr, F71808FG_REG_WD_TIME, + wd->timer_val); - superio_exit(watchdog.sioaddr); + superio_exit(wd->sioaddr); return 0; } static int fintek_wdt_start(struct watchdog_device *wdd) { + struct fintek_wdt *wd = watchdog_get_drvdata(wdd); int err; u8 tmp; @@ -287,57 +293,57 @@ static int fintek_wdt_start(struct watchdog_device *wdd) if (err) return err; - err = superio_enter(watchdog.sioaddr); + err = superio_enter(wd->sioaddr); if (err) return err; - superio_select(watchdog.sioaddr, SIO_F71808FG_LD_WDT); + superio_select(wd->sioaddr, SIO_F71808FG_LD_WDT); /* Watchdog pin configuration */ - switch (watchdog.type) { + switch (wd->type) { case f71808fg: /* Set pin 21 to GPIO23/WDTRST#, then to WDTRST# */ - superio_clear_bit(watchdog.sioaddr, SIO_REG_MFUNCT2, 3); - superio_clear_bit(watchdog.sioaddr, SIO_REG_MFUNCT3, 3); + superio_clear_bit(wd->sioaddr, SIO_REG_MFUNCT2, 3); + superio_clear_bit(wd->sioaddr, SIO_REG_MFUNCT3, 3); break; case f71862fg: if (f71862fg_pin == 63) { /* SPI must be disabled first to use this pin! */ - superio_clear_bit(watchdog.sioaddr, SIO_REG_ROM_ADDR_SEL, 6); - superio_set_bit(watchdog.sioaddr, SIO_REG_MFUNCT3, 4); + superio_clear_bit(wd->sioaddr, SIO_REG_ROM_ADDR_SEL, 6); + superio_set_bit(wd->sioaddr, SIO_REG_MFUNCT3, 4); } else if (f71862fg_pin == 56) { - superio_set_bit(watchdog.sioaddr, SIO_REG_MFUNCT1, 1); + superio_set_bit(wd->sioaddr, SIO_REG_MFUNCT1, 1); } break; case f71868: case f71869: /* GPIO14 --> WDTRST# */ - superio_clear_bit(watchdog.sioaddr, SIO_REG_MFUNCT1, 4); + superio_clear_bit(wd->sioaddr, SIO_REG_MFUNCT1, 4); break; case f71882fg: /* Set pin 56 to WDTRST# */ - superio_set_bit(watchdog.sioaddr, SIO_REG_MFUNCT1, 1); + superio_set_bit(wd->sioaddr, SIO_REG_MFUNCT1, 1); break; case f71889fg: /* set pin 40 to WDTRST# */ - superio_outb(watchdog.sioaddr, SIO_REG_MFUNCT3, - superio_inb(watchdog.sioaddr, SIO_REG_MFUNCT3) & 0xcf); + superio_outb(wd->sioaddr, SIO_REG_MFUNCT3, + superio_inb(wd->sioaddr, SIO_REG_MFUNCT3) & 0xcf); break; case f81803: /* Enable TSI Level register bank */ - superio_clear_bit(watchdog.sioaddr, SIO_REG_CLOCK_SEL, 3); + superio_clear_bit(wd->sioaddr, SIO_REG_CLOCK_SEL, 3); /* Set pin 27 to WDTRST# */ - superio_outb(watchdog.sioaddr, SIO_REG_TSI_LEVEL_SEL, 0x5f & - superio_inb(watchdog.sioaddr, SIO_REG_TSI_LEVEL_SEL)); + superio_outb(wd->sioaddr, SIO_REG_TSI_LEVEL_SEL, 0x5f & + superio_inb(wd->sioaddr, SIO_REG_TSI_LEVEL_SEL)); break; case f81865: /* Set pin 70 to WDTRST# */ - superio_clear_bit(watchdog.sioaddr, SIO_REG_MFUNCT3, 5); + superio_clear_bit(wd->sioaddr, SIO_REG_MFUNCT3, 5); break; case f81866: @@ -347,12 +353,12 @@ static int fintek_wdt_start(struct watchdog_device *wdd) * BIT5: 0 -> WDTRST# * 1 -> GPIO15 */ - tmp = superio_inb(watchdog.sioaddr, SIO_F81866_REG_PORT_SEL); + tmp = superio_inb(wd->sioaddr, SIO_F81866_REG_PORT_SEL); tmp &= ~(BIT(3) | BIT(0)); tmp |= BIT(2); - superio_outb(watchdog.sioaddr, SIO_F81866_REG_PORT_SEL, tmp); + superio_outb(wd->sioaddr, SIO_F81866_REG_PORT_SEL, tmp); - superio_clear_bit(watchdog.sioaddr, SIO_F81866_REG_GPIO1, 5); + superio_clear_bit(wd->sioaddr, SIO_F81866_REG_GPIO1, 5); break; default: @@ -364,63 +370,64 @@ static int fintek_wdt_start(struct watchdog_device *wdd) goto exit_superio; } - superio_select(watchdog.sioaddr, SIO_F71808FG_LD_WDT); - superio_set_bit(watchdog.sioaddr, SIO_REG_ENABLE, 0); + superio_select(wd->sioaddr, SIO_F71808FG_LD_WDT); + superio_set_bit(wd->sioaddr, SIO_REG_ENABLE, 0); - if (watchdog.type == f81865 || watchdog.type == f81866) - superio_set_bit(watchdog.sioaddr, F81865_REG_WDO_CONF, + if (wd->type == f81865 || wd->type == f81866) + superio_set_bit(wd->sioaddr, F81865_REG_WDO_CONF, F81865_FLAG_WDOUT_EN); else - superio_set_bit(watchdog.sioaddr, F71808FG_REG_WDO_CONF, + superio_set_bit(wd->sioaddr, F71808FG_REG_WDO_CONF, F71808FG_FLAG_WDOUT_EN); - superio_set_bit(watchdog.sioaddr, F71808FG_REG_WDT_CONF, + superio_set_bit(wd->sioaddr, F71808FG_REG_WDT_CONF, F71808FG_FLAG_WD_EN); - if (watchdog.pulse_mode) { + if (wd->pulse_mode) { /* Select "pulse" output mode with given duration */ - u8 wdt_conf = superio_inb(watchdog.sioaddr, + u8 wdt_conf = superio_inb(wd->sioaddr, F71808FG_REG_WDT_CONF); /* Set WD_PSWIDTH bits (1:0) */ - wdt_conf = (wdt_conf & 0xfc) | (watchdog.pulse_val & 0x03); + wdt_conf = (wdt_conf & 0xfc) | (wd->pulse_val & 0x03); /* Set WD_PULSE to "pulse" mode */ wdt_conf |= BIT(F71808FG_FLAG_WD_PULSE); - superio_outb(watchdog.sioaddr, F71808FG_REG_WDT_CONF, + superio_outb(wd->sioaddr, F71808FG_REG_WDT_CONF, wdt_conf); } else { /* Select "level" output mode */ - superio_clear_bit(watchdog.sioaddr, F71808FG_REG_WDT_CONF, + superio_clear_bit(wd->sioaddr, F71808FG_REG_WDT_CONF, F71808FG_FLAG_WD_PULSE); } exit_superio: - superio_exit(watchdog.sioaddr); + superio_exit(wd->sioaddr); return err; } static int fintek_wdt_stop(struct watchdog_device *wdd) { + struct fintek_wdt *wd = watchdog_get_drvdata(wdd); int err; - err = superio_enter(watchdog.sioaddr); + err = superio_enter(wd->sioaddr); if (err) return err; - superio_select(watchdog.sioaddr, SIO_F71808FG_LD_WDT); + superio_select(wd->sioaddr, SIO_F71808FG_LD_WDT); - superio_clear_bit(watchdog.sioaddr, F71808FG_REG_WDT_CONF, + superio_clear_bit(wd->sioaddr, F71808FG_REG_WDT_CONF, F71808FG_FLAG_WD_EN); - superio_exit(watchdog.sioaddr); + superio_exit(wd->sioaddr); return 0; } -static bool fintek_wdt_is_running(u8 wdt_conf) +static bool fintek_wdt_is_running(struct fintek_wdt *wd, u8 wdt_conf) { - return (superio_inb(watchdog.sioaddr, SIO_REG_ENABLE) & BIT(0)) + return (superio_inb(wd->sioaddr, SIO_REG_ENABLE) & BIT(0)) && (wdt_conf & BIT(F71808FG_FLAG_WD_EN)); } @@ -435,7 +442,9 @@ static const struct watchdog_ops fintek_wdt_ops = { static int fintek_wdt_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; + struct fintek_wdt_pdata *pdata; struct watchdog_device *wdd; + struct fintek_wdt *wd; int wdt_conf, err = 0; struct resource *res; int sioaddr; @@ -446,20 +455,27 @@ static int fintek_wdt_probe(struct platform_device *pdev) sioaddr = res->start; - watchdog.sioaddr = sioaddr; - watchdog.ident.options = WDIOF_SETTIMEOUT - | WDIOF_MAGICCLOSE - | WDIOF_KEEPALIVEPING - | WDIOF_CARDRESET; + wd = devm_kzalloc(dev, sizeof(*wd), GFP_KERNEL); + if (!wd) + return -ENOMEM; - snprintf(watchdog.ident.identity, - sizeof(watchdog.ident.identity), "%s watchdog", - fintek_wdt_names[watchdog.type]); + pdata = dev->platform_data; + + wd->type = pdata->type; + wd->sioaddr = sioaddr; + wd->ident.options = WDIOF_SETTIMEOUT + | WDIOF_MAGICCLOSE + | WDIOF_KEEPALIVEPING + | WDIOF_CARDRESET; + + snprintf(wd->ident.identity, + sizeof(wd->ident.identity), "%s watchdog", + fintek_wdt_names[wd->type]); err = superio_enter(sioaddr); if (err) return err; - superio_select(watchdog.sioaddr, SIO_F71808FG_LD_WDT); + superio_select(wd->sioaddr, SIO_F71808FG_LD_WDT); wdt_conf = superio_inb(sioaddr, F71808FG_REG_WDT_CONF); @@ -470,19 +486,20 @@ static int fintek_wdt_probe(struct platform_device *pdev) superio_outb(sioaddr, F71808FG_REG_WDT_CONF, wdt_conf | BIT(F71808FG_FLAG_WDTMOUT_STS)); - wdd = &watchdog.wdd; + wdd = &wd->wdd; - if (fintek_wdt_is_running(wdt_conf)) + if (fintek_wdt_is_running(wd, wdt_conf)) set_bit(WDOG_HW_RUNNING, &wdd->status); superio_exit(sioaddr); wdd->parent = dev; - wdd->info = &watchdog.ident; + wdd->info = &wd->ident; wdd->ops = &fintek_wdt_ops; wdd->min_timeout = 1; wdd->max_timeout = WATCHDOG_MAX_TIMEOUT; + watchdog_set_drvdata(wdd, wd); watchdog_set_nowayout(wdd, nowayout); watchdog_stop_on_unregister(wdd); watchdog_stop_on_reboot(wdd); @@ -497,7 +514,7 @@ static int fintek_wdt_probe(struct platform_device *pdev) * unconditionally. */ fintek_wdt_set_timeout(wdd, wdd->timeout); - fintek_wdt_set_pulse_width(pulse_width); + fintek_wdt_set_pulse_width(wd, pulse_width); if (start_withtimeout) { err = fintek_wdt_start(wdd); @@ -516,6 +533,7 @@ static int fintek_wdt_probe(struct platform_device *pdev) static int __init fintek_wdt_find(int sioaddr) { + enum chips type; u16 devid; int err = superio_enter(sioaddr); if (err) @@ -531,36 +549,36 @@ static int __init fintek_wdt_find(int sioaddr) devid = force_id ? force_id : superio_inw(sioaddr, SIO_REG_DEVID); switch (devid) { case SIO_F71808_ID: - watchdog.type = f71808fg; + type = f71808fg; break; case SIO_F71862_ID: - watchdog.type = f71862fg; + type = f71862fg; break; case SIO_F71868_ID: - watchdog.type = f71868; + type = f71868; break; case SIO_F71869_ID: case SIO_F71869A_ID: - watchdog.type = f71869; + type = f71869; break; case SIO_F71882_ID: - watchdog.type = f71882fg; + type = f71882fg; break; case SIO_F71889_ID: - watchdog.type = f71889fg; + type = f71889fg; break; case SIO_F71858_ID: /* Confirmed (by datasheet) not to have a watchdog. */ err = -ENODEV; goto exit; case SIO_F81803_ID: - watchdog.type = f81803; + type = f81803; break; case SIO_F81865_ID: - watchdog.type = f81865; + type = f81865; break; case SIO_F81866_ID: - watchdog.type = f81866; + type = f81866; break; default: pr_info("Unrecognized Fintek device: %04x\n", @@ -570,11 +588,12 @@ static int __init fintek_wdt_find(int sioaddr) } pr_info("Found %s watchdog chip, revision %d\n", - fintek_wdt_names[watchdog.type], + fintek_wdt_names[type], (int)superio_inb(sioaddr, SIO_REG_DEVREV)); + exit: superio_exit(sioaddr); - return err; + return err ? err : type; } static struct platform_driver fintek_wdt_driver = { @@ -589,8 +608,9 @@ static struct platform_device *fintek_wdt_pdev; static int __init fintek_wdt_init(void) { static const unsigned short addrs[] = { 0x2e, 0x4e }; + struct fintek_wdt_pdata pdata; struct resource wdt_res = {}; - int err = -ENODEV; + int ret; int i; if (f71862fg_pin != 63 && f71862fg_pin != 56) { @@ -599,12 +619,14 @@ static int __init fintek_wdt_init(void) } for (i = 0; i < ARRAY_SIZE(addrs); i++) { - err = fintek_wdt_find(addrs[i]); - if (err == 0) + ret = fintek_wdt_find(addrs[i]); + if (ret >= 0) break; } if (i == ARRAY_SIZE(addrs)) - return err; + return ret; + + pdata.type = ret; platform_driver_register(&fintek_wdt_driver); @@ -613,7 +635,9 @@ static int __init fintek_wdt_init(void) wdt_res.start = addrs[i]; wdt_res.end = addrs[i] + 1; - fintek_wdt_pdev = platform_device_register_simple(DRVNAME, -1, &wdt_res, 1); + fintek_wdt_pdev = platform_device_register_resndata(NULL, DRVNAME, -1, + &wdt_res, 1, + &pdata, sizeof(pdata)); if (IS_ERR(fintek_wdt_pdev)) { platform_driver_unregister(&fintek_wdt_driver); return PTR_ERR(fintek_wdt_pdev); From 2f61b3a746995ccf595781e34786bb69c63ab7fe Mon Sep 17 00:00:00 2001 From: Primoz Fiser Date: Thu, 8 Jul 2021 10:21:28 +0200 Subject: [PATCH 10/30] watchdog: da9062: da9063: prevent pings ahead of machine reset Proper machine resets via da9062/da9063 PMICs are very tricky as they require special i2c atomic transfers when interrupts are not available anymore. This is also a reason why both PMIC's restart handlers do not use regmap but instead opt for i2c_smbus_write_byte_data() which does i2c transfer in atomic manner. Under the hood, this function tries to obtain i2c bus lock with call to i2c_adapter_trylock_bus() which will return -EAGAIN (-11) if lock is not available. Since commit 982bb70517aef ("watchdog: reset last_hw_keepalive time at start") occasional restart handler failures with "Failed to shutdown (err = -11)" error messages were observed, indicating that some process is holding the i2c bus lock. Investigation into the matter uncovered that sometimes during reboot sequence watchdog ping is issued late into poweroff/reboot phase which did not happen before mentioned commit (usually the watchdog ping happened immediately as commit message suggests). As of now, when watchdog ping usually happens late into poweroff/reboot stage when interrupts are not available anymore, i2c bus lock cannot be released anymore and pending restart handler in turn fails. Thus, to prevent such late watchdog pings from happening ahead of pending machine restart and consequently locking up the i2c bus, check for system_state in watchdog ping handler and consequently do not send pings anymore in case system_state > SYSTEM_RUNNING. Signed-off-by: Primoz Fiser Reviewed-by: Guenter Roeck Reviewed-by: Adam Thomson Link: https://lore.kernel.org/r/20210708082128.2832904-1-primoz.fiser@norik.com Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/da9062_wdt.c | 7 +++++++ drivers/watchdog/da9063_wdt.c | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/drivers/watchdog/da9062_wdt.c b/drivers/watchdog/da9062_wdt.c index 706fb09c2f24..f02cbd530538 100644 --- a/drivers/watchdog/da9062_wdt.c +++ b/drivers/watchdog/da9062_wdt.c @@ -117,6 +117,13 @@ static int da9062_wdt_ping(struct watchdog_device *wdd) struct da9062_watchdog *wdt = watchdog_get_drvdata(wdd); int ret; + /* + * Prevent pings from occurring late in system poweroff/reboot sequence + * and possibly locking out restart handler from accessing i2c bus. + */ + if (system_state > SYSTEM_RUNNING) + return 0; + ret = da9062_reset_watchdog_timer(wdt); if (ret) dev_err(wdt->hw->dev, "Failed to ping the watchdog (err = %d)\n", diff --git a/drivers/watchdog/da9063_wdt.c b/drivers/watchdog/da9063_wdt.c index 423584252606..d79ce64e26a9 100644 --- a/drivers/watchdog/da9063_wdt.c +++ b/drivers/watchdog/da9063_wdt.c @@ -121,6 +121,13 @@ static int da9063_wdt_ping(struct watchdog_device *wdd) struct da9063 *da9063 = watchdog_get_drvdata(wdd); int ret; + /* + * Prevent pings from occurring late in system poweroff/reboot sequence + * and possibly locking out restart handler from accessing i2c bus. + */ + if (system_state > SYSTEM_RUNNING) + return 0; + ret = regmap_write(da9063->regmap, DA9063_REG_CONTROL_F, DA9063_WATCHDOG); if (ret) From bb6d7721ac3af6820edc4b17deccf1c91261da81 Mon Sep 17 00:00:00 2001 From: Artem Lapkin Date: Fri, 30 Jul 2021 12:13:53 +0800 Subject: [PATCH 11/30] watchdog: meson_gxbb_wdt: add nowayout parameter Add nowayout module parameter Signed-off-by: Artem Lapkin Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20210730041355.2810397-2-art@khadas.com Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/meson_gxbb_wdt.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/watchdog/meson_gxbb_wdt.c b/drivers/watchdog/meson_gxbb_wdt.c index 5a9ca10fbcfa..5aebc3a09652 100644 --- a/drivers/watchdog/meson_gxbb_wdt.c +++ b/drivers/watchdog/meson_gxbb_wdt.c @@ -29,6 +29,11 @@ #define GXBB_WDT_TCNT_SETUP_MASK (BIT(16) - 1) #define GXBB_WDT_TCNT_CNT_SHIFT 16 +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) ")"); + struct meson_gxbb_wdt { void __iomem *reg_base; struct watchdog_device wdt_dev; @@ -175,6 +180,7 @@ static int meson_gxbb_wdt_probe(struct platform_device *pdev) data->wdt_dev.max_hw_heartbeat_ms = GXBB_WDT_TCNT_SETUP_MASK; data->wdt_dev.min_timeout = 1; data->wdt_dev.timeout = DEFAULT_TIMEOUT; + watchdog_set_nowayout(&data->wdt_dev, nowayout); watchdog_set_drvdata(&data->wdt_dev, data); /* Setup with 1ms timebase */ From f01f0717928a7d38615c8ef5a72217bd63677e5b Mon Sep 17 00:00:00 2001 From: Artem Lapkin Date: Fri, 30 Jul 2021 12:13:54 +0800 Subject: [PATCH 12/30] watchdog: meson_gxbb_wdt: add timeout parameter Add timeout module parameter Signed-off-by: Artem Lapkin Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20210730041355.2810397-3-art@khadas.com Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/meson_gxbb_wdt.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/watchdog/meson_gxbb_wdt.c b/drivers/watchdog/meson_gxbb_wdt.c index 5aebc3a09652..945f5e65db57 100644 --- a/drivers/watchdog/meson_gxbb_wdt.c +++ b/drivers/watchdog/meson_gxbb_wdt.c @@ -34,6 +34,11 @@ module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); +static unsigned int timeout; +module_param(timeout, uint, 0); +MODULE_PARM_DESC(timeout, "Watchdog heartbeat in seconds=" + __MODULE_STRING(DEFAULT_TIMEOUT) ")"); + struct meson_gxbb_wdt { void __iomem *reg_base; struct watchdog_device wdt_dev; @@ -180,6 +185,7 @@ static int meson_gxbb_wdt_probe(struct platform_device *pdev) data->wdt_dev.max_hw_heartbeat_ms = GXBB_WDT_TCNT_SETUP_MASK; data->wdt_dev.min_timeout = 1; data->wdt_dev.timeout = DEFAULT_TIMEOUT; + watchdog_init_timeout(&data->wdt_dev, timeout, dev); watchdog_set_nowayout(&data->wdt_dev, nowayout); watchdog_set_drvdata(&data->wdt_dev, data); From 55f36df9ec4f6c53b6a06acfc4195e99bd9355fa Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Thu, 2 Sep 2021 17:57:48 -0500 Subject: [PATCH 13/30] dt-bindings: watchdog: sunxi: Add compatibles for R329 On existing SoCs, the watchdog has a single clock input: HOSC (OSC24M) divided by 750. However, starting with R329, LOSC (OSC32k) is added as an alternative clock source, with a bit to switch between them. Since 24 MHz / 750 == 32 kHz, not 32.768 kHz, the hardware adjusts the cycle counts to keep the timeouts independent of the clock source. This keeps the programming interface backward-compatible. Furthermore, the R329 has two watchdogs: one for use by the ARM CPUs at 0x20000a0, and a second one for use by the DSPs at 0x7020400. The first of these adds two more new registers, to allow software to immediately assert the SoC reset signal. Add an additional "-reset" suffix to signify the presence of this feature. Signed-off-by: Samuel Holland Acked-by: Maxime Ripard Reviewed-by: Rob Herring Acked-by: Guenter Roeck Link: https://lore.kernel.org/r/20210902225750.29313-2-samuel@sholland.org Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- .../watchdog/allwinner,sun4i-a10-wdt.yaml | 42 ++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/watchdog/allwinner,sun4i-a10-wdt.yaml b/Documentation/devicetree/bindings/watchdog/allwinner,sun4i-a10-wdt.yaml index 9aa3c313c49f..877f47759814 100644 --- a/Documentation/devicetree/bindings/watchdog/allwinner,sun4i-a10-wdt.yaml +++ b/Documentation/devicetree/bindings/watchdog/allwinner,sun4i-a10-wdt.yaml @@ -24,6 +24,8 @@ properties: - allwinner,sun50i-a100-wdt - allwinner,sun50i-h6-wdt - allwinner,sun50i-h616-wdt + - allwinner,sun50i-r329-wdt + - allwinner,sun50i-r329-wdt-reset - const: allwinner,sun6i-a31-wdt - items: - const: allwinner,suniv-f1c100s-wdt @@ -33,7 +35,18 @@ properties: maxItems: 1 clocks: - maxItems: 1 + minItems: 1 + maxItems: 2 + items: + - description: High-frequency oscillator input, divided internally + - description: Low-frequency oscillator input, only found on some variants + + clock-names: + minItems: 1 + maxItems: 2 + items: + - const: hosc + - const: losc interrupts: maxItems: 1 @@ -44,6 +57,33 @@ required: - clocks - interrupts +if: + properties: + compatible: + contains: + enum: + - allwinner,sun50i-r329-wdt + - allwinner,sun50i-r329-wdt-reset + +then: + properties: + clocks: + minItems: 2 + + clock-names: + minItems: 2 + + required: + - clock-names + +else: + properties: + clocks: + maxItems: 1 + + clock-names: + maxItems: 1 + unevaluatedProperties: false examples: From 28b7ee33a2122569ac065cad578bf23f50cc65c3 Mon Sep 17 00:00:00 2001 From: Jackie Liu Date: Tue, 7 Sep 2021 10:49:04 +0800 Subject: [PATCH 14/30] ar7: fix kernel builds for compiler test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TI AR7 Watchdog Timer is only build for 32bit. Avoid error like: In file included from drivers/watchdog/ar7_wdt.c:29: ./arch/mips/include/asm/mach-ar7/ar7.h: In function ‘ar7_is_titan’: ./arch/mips/include/asm/mach-ar7/ar7.h:111:24: error: implicit declaration of function ‘KSEG1ADDR’; did you mean ‘CKSEG1ADDR’? [-Werror=implicit-function-declaration] 111 | return (readl((void *)KSEG1ADDR(AR7_REGS_GPIO + 0x24)) & 0xffff) == | ^~~~~~~~~ | CKSEG1ADDR Fixes: da2a68b3eb47 ("watchdog: Enable COMPILE_TEST where possible") Signed-off-by: Jackie Liu Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20210907024904.4127611-1-liu.yun@linux.dev Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index d0645c7be4e6..f28fdae3c5b4 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -1664,7 +1664,7 @@ config SIBYTE_WDOG config AR7_WDT tristate "TI AR7 Watchdog Timer" - depends on AR7 || (MIPS && COMPILE_TEST) + depends on AR7 || (MIPS && 32BIT && COMPILE_TEST) help Hardware driver for the TI AR7 Watchdog Timer. From 601db217916de339ddebeb3b6f024f09a275562a Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Thu, 2 Sep 2021 17:57:49 -0500 Subject: [PATCH 15/30] dt-bindings: watchdog: sunxi: Add compatibles for D1 D1 keeps the same register layout and clock sources as the R329, but it adds a key field which must be set to update the watchdog's "CFG" and "MODE" registers. Therefore it is not backward-compatible. Similarly to the R329, the D1 has three watchdog instances, and only one of them has the "soft reset" registers. So that instance needs an extra compatible string. Signed-off-by: Samuel Holland Acked-by: Maxime Ripard Acked-by: Guenter Roeck Acked-by: Rob Herring Link: https://lore.kernel.org/r/20210902225750.29313-3-samuel@sholland.org Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- .../bindings/watchdog/allwinner,sun4i-a10-wdt.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Documentation/devicetree/bindings/watchdog/allwinner,sun4i-a10-wdt.yaml b/Documentation/devicetree/bindings/watchdog/allwinner,sun4i-a10-wdt.yaml index 877f47759814..44cad9427ae6 100644 --- a/Documentation/devicetree/bindings/watchdog/allwinner,sun4i-a10-wdt.yaml +++ b/Documentation/devicetree/bindings/watchdog/allwinner,sun4i-a10-wdt.yaml @@ -30,6 +30,10 @@ properties: - items: - const: allwinner,suniv-f1c100s-wdt - const: allwinner,sun4i-a10-wdt + - const: allwinner,sun20i-d1-wdt + - items: + - const: allwinner,sun20i-d1-wdt-reset + - const: allwinner,sun20i-d1-wdt reg: maxItems: 1 @@ -62,6 +66,8 @@ if: compatible: contains: enum: + - allwinner,sun20i-d1-wdt + - allwinner,sun20i-d1-wdt-reset - allwinner,sun50i-r329-wdt - allwinner,sun50i-r329-wdt-reset From 94213a39c3d8430297f52245d777b5c1ed627717 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Thu, 2 Sep 2021 17:57:50 -0500 Subject: [PATCH 16/30] watchdog: sunxi_wdt: Add support for D1 D1 adds a key field to the "CFG" and "MODE" registers, that must be set to change the other bits. Add logic to set the key when updating those registers. Signed-off-by: Samuel Holland Acked-by: Maxime Ripard Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20210902225750.29313-4-samuel@sholland.org Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/sunxi_wdt.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/drivers/watchdog/sunxi_wdt.c b/drivers/watchdog/sunxi_wdt.c index b50757882a98..6cf82922d3fb 100644 --- a/drivers/watchdog/sunxi_wdt.c +++ b/drivers/watchdog/sunxi_wdt.c @@ -48,6 +48,7 @@ struct sunxi_wdt_reg { u8 wdt_timeout_shift; u8 wdt_reset_mask; u8 wdt_reset_val; + u32 wdt_key_val; }; struct sunxi_wdt_dev { @@ -91,12 +92,14 @@ static int sunxi_wdt_restart(struct watchdog_device *wdt_dev, val = readl(wdt_base + regs->wdt_cfg); val &= ~(regs->wdt_reset_mask); val |= regs->wdt_reset_val; + val |= regs->wdt_key_val; writel(val, wdt_base + regs->wdt_cfg); /* Set lowest timeout and enable watchdog */ val = readl(wdt_base + regs->wdt_mode); val &= ~(WDT_TIMEOUT_MASK << regs->wdt_timeout_shift); val |= WDT_MODE_EN; + val |= regs->wdt_key_val; writel(val, wdt_base + regs->wdt_mode); /* @@ -109,6 +112,7 @@ static int sunxi_wdt_restart(struct watchdog_device *wdt_dev, mdelay(5); val = readl(wdt_base + regs->wdt_mode); val |= WDT_MODE_EN; + val |= regs->wdt_key_val; writel(val, wdt_base + regs->wdt_mode); } return 0; @@ -141,6 +145,7 @@ static int sunxi_wdt_set_timeout(struct watchdog_device *wdt_dev, reg = readl(wdt_base + regs->wdt_mode); reg &= ~(WDT_TIMEOUT_MASK << regs->wdt_timeout_shift); reg |= wdt_timeout_map[timeout] << regs->wdt_timeout_shift; + reg |= regs->wdt_key_val; writel(reg, wdt_base + regs->wdt_mode); sunxi_wdt_ping(wdt_dev); @@ -154,7 +159,7 @@ static int sunxi_wdt_stop(struct watchdog_device *wdt_dev) void __iomem *wdt_base = sunxi_wdt->wdt_base; const struct sunxi_wdt_reg *regs = sunxi_wdt->wdt_regs; - writel(0, wdt_base + regs->wdt_mode); + writel(regs->wdt_key_val, wdt_base + regs->wdt_mode); return 0; } @@ -176,11 +181,13 @@ static int sunxi_wdt_start(struct watchdog_device *wdt_dev) reg = readl(wdt_base + regs->wdt_cfg); reg &= ~(regs->wdt_reset_mask); reg |= regs->wdt_reset_val; + reg |= regs->wdt_key_val; writel(reg, wdt_base + regs->wdt_cfg); /* Enable watchdog */ reg = readl(wdt_base + regs->wdt_mode); reg |= WDT_MODE_EN; + reg |= regs->wdt_key_val; writel(reg, wdt_base + regs->wdt_mode); return 0; @@ -220,9 +227,20 @@ static const struct sunxi_wdt_reg sun6i_wdt_reg = { .wdt_reset_val = 0x01, }; +static const struct sunxi_wdt_reg sun20i_wdt_reg = { + .wdt_ctrl = 0x10, + .wdt_cfg = 0x14, + .wdt_mode = 0x18, + .wdt_timeout_shift = 4, + .wdt_reset_mask = 0x03, + .wdt_reset_val = 0x01, + .wdt_key_val = 0x16aa0000, +}; + static const struct of_device_id sunxi_wdt_dt_ids[] = { { .compatible = "allwinner,sun4i-a10-wdt", .data = &sun4i_wdt_reg }, { .compatible = "allwinner,sun6i-a31-wdt", .data = &sun6i_wdt_reg }, + { .compatible = "allwinner,sun20i-d1-wdt", .data = &sun20i_wdt_reg }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, sunxi_wdt_dt_ids); From 54ccba2f6a00026dbe6b5576990907827addab2b Mon Sep 17 00:00:00 2001 From: Cai Huoqing Date: Tue, 7 Sep 2021 15:42:22 +0800 Subject: [PATCH 17/30] watchdog: ar7_wdt: Make use of the helper function devm_platform_ioremap_resource_byname() Use the devm_platform_ioremap_resource_byname() helper instead of calling platform_get_resource_byname() and devm_ioremap_resource() separately Signed-off-by: Cai Huoqing Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20210907074223.2706-1-caihuoqing@baidu.com Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/ar7_wdt.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/watchdog/ar7_wdt.c b/drivers/watchdog/ar7_wdt.c index ff37dc91057d..743e171d97a3 100644 --- a/drivers/watchdog/ar7_wdt.c +++ b/drivers/watchdog/ar7_wdt.c @@ -63,8 +63,6 @@ static DEFINE_SPINLOCK(wdt_lock); /* XXX currently fixed, allows max margin ~68.72 secs */ #define prescale_value 0xffff -/* Resource of the WDT registers */ -static struct resource *ar7_regs_wdt; /* Pointer to the remapped WDT IO space */ static struct ar7_wdt *ar7_wdt; @@ -265,9 +263,7 @@ static int ar7_wdt_probe(struct platform_device *pdev) { int rc; - ar7_regs_wdt = - platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs"); - ar7_wdt = devm_ioremap_resource(&pdev->dev, ar7_regs_wdt); + ar7_wdt = devm_platform_ioremap_resource_byname(pdev, "regs"); if (IS_ERR(ar7_wdt)) return PTR_ERR(ar7_wdt); From 79cc4d22aa45c8933f7ea8682755f8beecae995a Mon Sep 17 00:00:00 2001 From: Cai Huoqing Date: Tue, 7 Sep 2021 15:42:29 +0800 Subject: [PATCH 18/30] watchdog: iTCO_wdt: Make use of the helper function devm_platform_ioremap_resource() Use the devm_platform_ioremap_resource() helper instead of calling platform_get_resource() and devm_ioremap_resource() separately Signed-off-by: Cai Huoqing Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20210907074230.2757-1-caihuoqing@baidu.com Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/iTCO_wdt.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/watchdog/iTCO_wdt.c b/drivers/watchdog/iTCO_wdt.c index ced2fc0deb8c..16ad2c9842c2 100644 --- a/drivers/watchdog/iTCO_wdt.c +++ b/drivers/watchdog/iTCO_wdt.c @@ -94,7 +94,6 @@ struct iTCO_wdt_private { * NO_REBOOT flag is Memory-Mapped GCS register bit 5 (TCO version 2), * or memory-mapped PMC register bit 4 (TCO version 3). */ - struct resource *gcs_pmc_res; unsigned long __iomem *gcs_pmc; /* the lock for io operations */ spinlock_t io_lock; @@ -497,10 +496,7 @@ static int iTCO_wdt_probe(struct platform_device *pdev) */ if (p->iTCO_version >= 2 && p->iTCO_version < 6 && !pdata->no_reboot_use_pmc) { - p->gcs_pmc_res = platform_get_resource(pdev, - IORESOURCE_MEM, - ICH_RES_MEM_GCS_PMC); - p->gcs_pmc = devm_ioremap_resource(dev, p->gcs_pmc_res); + p->gcs_pmc = devm_platform_ioremap_resource(pdev, ICH_RES_MEM_GCS_PMC); if (IS_ERR(p->gcs_pmc)) return PTR_ERR(p->gcs_pmc); } From b3220bde5e85db4552171419a2b421a964b40d32 Mon Sep 17 00:00:00 2001 From: Cai Huoqing Date: Tue, 7 Sep 2021 15:42:36 +0800 Subject: [PATCH 19/30] watchdog: rti-wdt: Make use of the helper function devm_platform_ioremap_resource() Use the devm_platform_ioremap_resource() helper instead of calling platform_get_resource() and devm_ioremap_resource() separately Signed-off-by: Cai Huoqing Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20210907074237.2808-1-caihuoqing@baidu.com Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/rti_wdt.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/watchdog/rti_wdt.c b/drivers/watchdog/rti_wdt.c index 359302f71f7e..117bc2a8eb0a 100644 --- a/drivers/watchdog/rti_wdt.c +++ b/drivers/watchdog/rti_wdt.c @@ -194,7 +194,6 @@ static int rti_wdt_probe(struct platform_device *pdev) { int ret = 0; struct device *dev = &pdev->dev; - struct resource *wdt_mem; struct watchdog_device *wdd; struct rti_wdt_device *wdt; struct clk *clk; @@ -246,8 +245,7 @@ static int rti_wdt_probe(struct platform_device *pdev) watchdog_set_nowayout(wdd, 1); watchdog_set_restart_priority(wdd, 128); - wdt_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - wdt->base = devm_ioremap_resource(dev, wdt_mem); + wdt->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(wdt->base)) { ret = PTR_ERR(wdt->base); goto err_iomap; From dd29cb4b88bc283ea4f01f8e0f38086f091dd348 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Tue, 7 Sep 2021 11:27:32 +0200 Subject: [PATCH 20/30] watchdog: mlx-wdt: Use regmap_write_bits() Use the regmap_write_bits() macro instead of regmap_update_bits_base(). No functional change. Signed-off-by: Philipp Zabel Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20210907092732.31815-1-p.zabel@pengutronix.de Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/mlx_wdt.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/watchdog/mlx_wdt.c b/drivers/watchdog/mlx_wdt.c index 54193369e85c..9c5b6616fc87 100644 --- a/drivers/watchdog/mlx_wdt.c +++ b/drivers/watchdog/mlx_wdt.c @@ -100,9 +100,8 @@ static int mlxreg_wdt_ping(struct watchdog_device *wdd) struct mlxreg_wdt *wdt = watchdog_get_drvdata(wdd); struct mlxreg_core_data *reg_data = &wdt->pdata->data[wdt->ping_idx]; - return regmap_update_bits_base(wdt->regmap, reg_data->reg, - ~reg_data->mask, BIT(reg_data->bit), - NULL, false, true); + return regmap_write_bits(wdt->regmap, reg_data->reg, ~reg_data->mask, + BIT(reg_data->bit)); } static int mlxreg_wdt_set_timeout(struct watchdog_device *wdd, From 414a9bf8285b8da90a7c2cb36a028a3279b20ec7 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 15 Sep 2021 11:48:23 +0200 Subject: [PATCH 21/30] watchdog: rza_wdt: Use semicolons instead of commas This code works, but it is cleaner to use semicolons at the end of statements instead of commas. Extracted from a big anonymous patch by Julia Lawall . Signed-off-by: Geert Uytterhoeven Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/fa4451efd21e287f8fdf2f7f8495b070544209c0.1631699262.git.geert+renesas@glider.be Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/rza_wdt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/watchdog/rza_wdt.c b/drivers/watchdog/rza_wdt.c index 7b6c365f7cd3..fe6c2ed35e04 100644 --- a/drivers/watchdog/rza_wdt.c +++ b/drivers/watchdog/rza_wdt.c @@ -189,8 +189,8 @@ static int rza_wdt_probe(struct platform_device *pdev) return -ENOENT; } - priv->wdev.info = &rza_wdt_ident, - priv->wdev.ops = &rza_wdt_ops, + priv->wdev.info = &rza_wdt_ident; + priv->wdev.ops = &rza_wdt_ops; priv->wdev.parent = dev; priv->cks = (u8)(uintptr_t) of_device_get_match_data(dev); From eed09878923ef2cd69b832b8ac84f8dfa674f353 Mon Sep 17 00:00:00 2001 From: Fengquan Chen Date: Tue, 14 Sep 2021 20:34:53 +0800 Subject: [PATCH 22/30] dt-bindings: watchdog: mtk-wdt: add disable_wdt_extrst support This patch add a description and example of disable_wdt_extrst element for watchdog on MTK Socs Signed-off-by: Fengquan Chen Acked-by: Rob Herring Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20210914123454.32603-2-Fengquan.Chen@mediatek.com Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- Documentation/devicetree/bindings/watchdog/mtk-wdt.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/watchdog/mtk-wdt.txt b/Documentation/devicetree/bindings/watchdog/mtk-wdt.txt index a4e31ce96e0e..0114871f887a 100644 --- a/Documentation/devicetree/bindings/watchdog/mtk-wdt.txt +++ b/Documentation/devicetree/bindings/watchdog/mtk-wdt.txt @@ -22,6 +22,7 @@ Required properties: - reg : Specifies base physical address and size of the registers. Optional properties: +- mediatek,disable-extrst: disable send output reset signal - interrupts: Watchdog pre-timeout (bark) interrupt. - timeout-sec: contains the watchdog timeout in seconds. - #reset-cells: Should be 1. @@ -31,6 +32,7 @@ Example: watchdog: watchdog@10007000 { compatible = "mediatek,mt8183-wdt", "mediatek,mt6589-wdt"; + mediatek,disable-extrst; reg = <0 0x10007000 0 0x100>; interrupts = ; timeout-sec = <10>; From 59b0f51335644ee603260faaa4298c0115fb7187 Mon Sep 17 00:00:00 2001 From: Fengquan Chen Date: Tue, 14 Sep 2021 20:34:54 +0800 Subject: [PATCH 23/30] watchdog: mtk: add disable_wdt_extrst support In some cases, we may need watchdog just to trigger an internal soc reset without sending any output signal. Provide a disable_wdt_extrst parameter for configuration. We can disable or enable it just by configuring dts. Signed-off-by: Fengquan Chen Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20210914123454.32603-3-Fengquan.Chen@mediatek.com Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/mtk_wdt.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/watchdog/mtk_wdt.c b/drivers/watchdog/mtk_wdt.c index 796fbb048cbe..ceb57ea627cd 100644 --- a/drivers/watchdog/mtk_wdt.c +++ b/drivers/watchdog/mtk_wdt.c @@ -65,6 +65,7 @@ struct mtk_wdt_dev { void __iomem *wdt_base; spinlock_t lock; /* protects WDT_SWSYSRST reg */ struct reset_controller_dev rcdev; + bool disable_wdt_extrst; }; struct mtk_wdt_data { @@ -256,6 +257,8 @@ static int mtk_wdt_start(struct watchdog_device *wdt_dev) reg |= (WDT_MODE_IRQ_EN | WDT_MODE_DUAL_EN); else reg &= ~(WDT_MODE_IRQ_EN | WDT_MODE_DUAL_EN); + if (mtk_wdt->disable_wdt_extrst) + reg &= ~WDT_MODE_EXRST_EN; reg |= (WDT_MODE_EN | WDT_MODE_KEY); iowrite32(reg, wdt_base + WDT_MODE); @@ -381,6 +384,10 @@ static int mtk_wdt_probe(struct platform_device *pdev) if (err) return err; } + + mtk_wdt->disable_wdt_extrst = + of_property_read_bool(dev->of_node, "mediatek,disable-extrst"); + return 0; } From 4d3d50f607b20b309f54b988c4b67cd6dd27b6d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Tue, 28 Sep 2021 08:57:35 +0200 Subject: [PATCH 24/30] watchdog: sp5100_tco: Add support for get_timeleft MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tested on a Gigabyte X570 I AORUS PRO WIFI. Signed-off-by: Thomas Weißschuh Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20210928065735.548966-1-linux@weissschuh.net Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/sp5100_tco.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/watchdog/sp5100_tco.c b/drivers/watchdog/sp5100_tco.c index a730ecbf78cd..dd9a744f82f8 100644 --- a/drivers/watchdog/sp5100_tco.c +++ b/drivers/watchdog/sp5100_tco.c @@ -10,6 +10,7 @@ * https://www.kernelconcepts.de * * See AMD Publication 43009 "AMD SB700/710/750 Register Reference Guide", + * AMD Publication 44413 "AMD SP5100 Register Reference Guide" * AMD Publication 45482 "AMD SB800-Series Southbridges Register * Reference Guide" * AMD Publication 48751 "BIOS and Kernel Developer’s Guide (BKDG) @@ -144,6 +145,13 @@ static int tco_timer_set_timeout(struct watchdog_device *wdd, return 0; } +static unsigned int tco_timer_get_timeleft(struct watchdog_device *wdd) +{ + struct sp5100_tco *tco = watchdog_get_drvdata(wdd); + + return readl(SP5100_WDT_COUNT(tco->tcobase)); +} + static u8 sp5100_tco_read_pm_reg8(u8 index) { outb(index, SP5100_IO_PM_INDEX_REG); @@ -386,6 +394,7 @@ static const struct watchdog_ops sp5100_tco_wdt_ops = { .stop = tco_timer_stop, .ping = tco_timer_ping, .set_timeout = tco_timer_set_timeout, + .get_timeleft = tco_timer_get_timeleft, }; static int sp5100_tco_probe(struct platform_device *pdev) From 981785da79f0bfecdd328f49e6473c232e3a6bdb Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 24 Sep 2021 15:29:30 +0200 Subject: [PATCH 25/30] watchdog: s3c2410: describe driver in KConfig Describe better which driver applies to which SoC, to make configuring kernel for Samsung SoC easier. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20210924132930.111443-1-krzysztof.kozlowski@canonical.com Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/Kconfig | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index f28fdae3c5b4..0fab8230b663 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -496,16 +496,18 @@ config S3C2410_WATCHDOG select WATCHDOG_CORE select MFD_SYSCON if ARCH_EXYNOS help - Watchdog timer block in the Samsung SoCs. This will reboot - the system when the timer expires with the watchdog enabled. + Watchdog timer block in the Samsung S3C24xx, S3C64xx, S5Pv210 and + Exynos SoCs. This will reboot the system when the timer expires with + the watchdog enabled. The driver is limited by the speed of the system's PCLK signal, so with reasonably fast systems (PCLK around 50-66MHz) then watchdog intervals of over approximately 20seconds are unavailable. + Choose Y/M here only if you build for such Samsung SoC. The driver can be built as a module by choosing M, and will - be called s3c2410_wdt + be called s3c2410_wdt. config SA1100_WATCHDOG tristate "SA1100/PXA2xx watchdog" From 1ae3e78c08209ac657c59f6f7ea21bbbd7f6a1d4 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Tue, 21 Sep 2021 13:29:00 +0300 Subject: [PATCH 26/30] watchdog: iTCO_wdt: No need to stop the timer in probe The watchdog core can handle pinging of the watchdog before userspace opens the device. For this reason instead of stopping the timer, just mark it as running and let the watchdog core take care of it. Cc: Malin Jonsson Signed-off-by: Mika Westerberg Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20210921102900.61586-1-mika.westerberg@linux.intel.com Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/iTCO_wdt.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/watchdog/iTCO_wdt.c b/drivers/watchdog/iTCO_wdt.c index 16ad2c9842c2..3f2f4343644f 100644 --- a/drivers/watchdog/iTCO_wdt.c +++ b/drivers/watchdog/iTCO_wdt.c @@ -423,6 +423,16 @@ static unsigned int iTCO_wdt_get_timeleft(struct watchdog_device *wd_dev) return time_left; } +static void iTCO_wdt_set_running(struct iTCO_wdt_private *p) +{ + u16 val; + + /* Bit 11: TCO Timer Halt -> 0 = The TCO timer is * enabled */ + val = inw(TCO1_CNT(p)); + if (!(val & BIT(11))) + set_bit(WDOG_HW_RUNNING, &p->wddev.status); +} + /* * Kernel Interfaces */ @@ -562,8 +572,7 @@ static int iTCO_wdt_probe(struct platform_device *pdev) watchdog_set_drvdata(&p->wddev, p); platform_set_drvdata(pdev, p); - /* Make sure the watchdog is not running */ - iTCO_wdt_stop(&p->wddev); + iTCO_wdt_set_running(p); /* Check that the heartbeat value is within it's range; if not reset to the default */ From ee1a0696934a8b77a6a2098f92832c46d34ec5da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Wed, 27 Oct 2021 14:31:35 +0200 Subject: [PATCH 27/30] watchdog: bcm63xx_wdt: fix fallthrough warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes: drivers/watchdog/bcm63xx_wdt.c: In function 'bcm63xx_wdt_ioctl': drivers/watchdog/bcm63xx_wdt.c:208:17: warning: this statement may fall through [-Wimplicit-fallthrough=] Signed-off-by: Rafał Miłecki Reviewed-by: Florian Fainelli Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20211027123135.27458-1-zajec5@gmail.com Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/bcm63xx_wdt.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/watchdog/bcm63xx_wdt.c b/drivers/watchdog/bcm63xx_wdt.c index 7cdb25363ea0..56cc262571a5 100644 --- a/drivers/watchdog/bcm63xx_wdt.c +++ b/drivers/watchdog/bcm63xx_wdt.c @@ -207,6 +207,8 @@ static long bcm63xx_wdt_ioctl(struct file *file, unsigned int cmd, bcm63xx_wdt_pet(); + fallthrough; + case WDIOC_GETTIMEOUT: return put_user(wdt_time, p); From 74128d801b5165650ae6222e8cf23780fbc55e67 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 23 Sep 2021 01:09:45 +0200 Subject: [PATCH 28/30] watchdog: ux500_wdt: Drop platform data Drop the platform data passing from the PRCMU driver. This platform data was part of the ambition to support more SoCs, which in turn were never mass produced. Only a name remains of the MFD cell so switch to MFD_CELL_NAME(). Cc: Lee Jones Signed-off-by: Linus Walleij Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20210922230947.1864357-1-linus.walleij@linaro.org Acked-by: Lee Jones Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/mfd/db8500-prcmu.c | 13 +------------ drivers/watchdog/ux500_wdt.c | 13 ++----------- include/linux/platform_data/ux500_wdt.h | 18 ------------------ 3 files changed, 3 insertions(+), 41 deletions(-) delete mode 100644 include/linux/platform_data/ux500_wdt.h diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index c1d3e7c116cf..ccf6be922b39 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -36,7 +36,6 @@ #include #include #include -#include #include "db8500-prcmu-regs.h" /* Index of different voltages to be used when accessing AVSData */ @@ -2939,18 +2938,8 @@ static struct regulator_init_data db8500_regulators[DB8500_NUM_REGULATORS] = { }, }; -static struct ux500_wdt_data db8500_wdt_pdata = { - .timeout = 600, /* 10 minutes */ - .has_28_bits_resolution = true, -}; - static const struct mfd_cell common_prcmu_devs[] = { - { - .name = "ux500_wdt", - .platform_data = &db8500_wdt_pdata, - .pdata_size = sizeof(db8500_wdt_pdata), - .id = -1, - }, + MFD_CELL_NAME("ux500_wdt"), MFD_CELL_NAME("db8500-cpuidle"), }; diff --git a/drivers/watchdog/ux500_wdt.c b/drivers/watchdog/ux500_wdt.c index 072758106865..40f8cf1cb234 100644 --- a/drivers/watchdog/ux500_wdt.c +++ b/drivers/watchdog/ux500_wdt.c @@ -15,7 +15,6 @@ #include #include #include -#include #include @@ -23,7 +22,6 @@ #define WATCHDOG_MIN 0 #define WATCHDOG_MAX28 268435 /* 28 bit resolution in ms == 268435.455 s */ -#define WATCHDOG_MAX32 4294967 /* 32 bit resolution in ms == 4294967.295 s */ static unsigned int timeout = WATCHDOG_TIMEOUT; module_param(timeout, uint, 0); @@ -80,22 +78,15 @@ static struct watchdog_device ux500_wdt = { .info = &ux500_wdt_info, .ops = &ux500_wdt_ops, .min_timeout = WATCHDOG_MIN, - .max_timeout = WATCHDOG_MAX32, + .max_timeout = WATCHDOG_MAX28, }; static int ux500_wdt_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; int ret; - struct ux500_wdt_data *pdata = dev_get_platdata(dev); - - if (pdata) { - if (pdata->timeout > 0) - timeout = pdata->timeout; - if (pdata->has_28_bits_resolution) - ux500_wdt.max_timeout = WATCHDOG_MAX28; - } + timeout = 600; /* Default to 10 minutes */ ux500_wdt.parent = dev; watchdog_set_nowayout(&ux500_wdt, nowayout); diff --git a/include/linux/platform_data/ux500_wdt.h b/include/linux/platform_data/ux500_wdt.h deleted file mode 100644 index de6a4ad41e76..000000000000 --- a/include/linux/platform_data/ux500_wdt.h +++ /dev/null @@ -1,18 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (C) ST Ericsson SA 2011 - * - * STE Ux500 Watchdog platform data - */ -#ifndef __UX500_WDT_H -#define __UX500_WDT_H - -/** - * struct ux500_wdt_data - */ -struct ux500_wdt_data { - unsigned int timeout; - bool has_28_bits_resolution; -}; - -#endif /* __UX500_WDT_H */ From d0305aac8e83caf24aaf44f1134335b405bbb075 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 23 Sep 2021 01:09:46 +0200 Subject: [PATCH 29/30] watchdog: db8500_wdt: Rename driver This driver is named after the ambition to support more SoCs than the DB8500. Those were never produced, so cut down the scope and rename the driver accordingly. Since the Kconfig for the watchdog defaults to y this will still be built by default. Signed-off-by: Linus Walleij Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20210922230947.1864357-2-linus.walleij@linaro.org Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/Kconfig | 8 ++++---- drivers/watchdog/Makefile | 2 +- drivers/watchdog/{ux500_wdt.c => db8500_wdt.c} | 0 3 files changed, 5 insertions(+), 5 deletions(-) rename drivers/watchdog/{ux500_wdt.c => db8500_wdt.c} (100%) diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 0fab8230b663..9d222ba17ec6 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -729,17 +729,17 @@ config IMX7ULP_WDT To compile this driver as a module, choose M here: the module will be called imx7ulp_wdt. -config UX500_WATCHDOG - tristate "ST-Ericsson Ux500 watchdog" +config DB500_WATCHDOG + tristate "ST-Ericsson DB800 watchdog" depends on MFD_DB8500_PRCMU select WATCHDOG_CORE default y help Say Y here to include Watchdog timer support for the watchdog - existing in the prcmu of ST-Ericsson Ux500 series platforms. + existing in the prcmu of ST-Ericsson DB8500 platform. To compile this driver as a module, choose M here: the - module will be called ux500_wdt. + module will be called db500_wdt. config RETU_WATCHDOG tristate "Retu watchdog" diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 74127ced3c23..2ee97064145b 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -68,7 +68,7 @@ obj-$(CONFIG_TS72XX_WATCHDOG) += ts72xx_wdt.o obj-$(CONFIG_IMX2_WDT) += imx2_wdt.o obj-$(CONFIG_IMX_SC_WDT) += imx_sc_wdt.o obj-$(CONFIG_IMX7ULP_WDT) += imx7ulp_wdt.o -obj-$(CONFIG_UX500_WATCHDOG) += ux500_wdt.o +obj-$(CONFIG_DB500_WATCHDOG) += db8500_wdt.o obj-$(CONFIG_RETU_WATCHDOG) += retu_wdt.o obj-$(CONFIG_BCM2835_WDT) += bcm2835_wdt.o obj-$(CONFIG_MOXART_WDT) += moxart_wdt.o diff --git a/drivers/watchdog/ux500_wdt.c b/drivers/watchdog/db8500_wdt.c similarity index 100% rename from drivers/watchdog/ux500_wdt.c rename to drivers/watchdog/db8500_wdt.c From c738888032ffafa1bbb971cd55b3d43b05b344cf Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 23 Sep 2021 01:09:47 +0200 Subject: [PATCH 30/30] watchdog: db8500_wdt: Rename symbols For conistency and clarity, rename all symbols and strings from ux500 to db8500 in the driver. Cc: Lee Jones Signed-off-by: Linus Walleij Reviewed-by: Guenter Roeck Acked-by: Lee Jones Link: https://lore.kernel.org/r/20210922230947.1864357-3-linus.walleij@linaro.org Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/mfd/db8500-prcmu.c | 2 +- drivers/watchdog/db8500_wdt.c | 76 +++++++++++++++++------------------ 2 files changed, 39 insertions(+), 39 deletions(-) diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index ccf6be922b39..56c61c99eb23 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -2939,7 +2939,7 @@ static struct regulator_init_data db8500_regulators[DB8500_NUM_REGULATORS] = { }; static const struct mfd_cell common_prcmu_devs[] = { - MFD_CELL_NAME("ux500_wdt"), + MFD_CELL_NAME("db8500_wdt"), MFD_CELL_NAME("db8500-cpuidle"), }; diff --git a/drivers/watchdog/db8500_wdt.c b/drivers/watchdog/db8500_wdt.c index 40f8cf1cb234..6ed8b63d310d 100644 --- a/drivers/watchdog/db8500_wdt.c +++ b/drivers/watchdog/db8500_wdt.c @@ -35,60 +35,60 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); -static int ux500_wdt_start(struct watchdog_device *wdd) +static int db8500_wdt_start(struct watchdog_device *wdd) { return prcmu_enable_a9wdog(PRCMU_WDOG_ALL); } -static int ux500_wdt_stop(struct watchdog_device *wdd) +static int db8500_wdt_stop(struct watchdog_device *wdd) { return prcmu_disable_a9wdog(PRCMU_WDOG_ALL); } -static int ux500_wdt_keepalive(struct watchdog_device *wdd) +static int db8500_wdt_keepalive(struct watchdog_device *wdd) { return prcmu_kick_a9wdog(PRCMU_WDOG_ALL); } -static int ux500_wdt_set_timeout(struct watchdog_device *wdd, +static int db8500_wdt_set_timeout(struct watchdog_device *wdd, unsigned int timeout) { - ux500_wdt_stop(wdd); + db8500_wdt_stop(wdd); prcmu_load_a9wdog(PRCMU_WDOG_ALL, timeout * 1000); - ux500_wdt_start(wdd); + db8500_wdt_start(wdd); return 0; } -static const struct watchdog_info ux500_wdt_info = { +static const struct watchdog_info db8500_wdt_info = { .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, - .identity = "Ux500 WDT", + .identity = "DB8500 WDT", .firmware_version = 1, }; -static const struct watchdog_ops ux500_wdt_ops = { +static const struct watchdog_ops db8500_wdt_ops = { .owner = THIS_MODULE, - .start = ux500_wdt_start, - .stop = ux500_wdt_stop, - .ping = ux500_wdt_keepalive, - .set_timeout = ux500_wdt_set_timeout, + .start = db8500_wdt_start, + .stop = db8500_wdt_stop, + .ping = db8500_wdt_keepalive, + .set_timeout = db8500_wdt_set_timeout, }; -static struct watchdog_device ux500_wdt = { - .info = &ux500_wdt_info, - .ops = &ux500_wdt_ops, +static struct watchdog_device db8500_wdt = { + .info = &db8500_wdt_info, + .ops = &db8500_wdt_ops, .min_timeout = WATCHDOG_MIN, .max_timeout = WATCHDOG_MAX28, }; -static int ux500_wdt_probe(struct platform_device *pdev) +static int db8500_wdt_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; int ret; timeout = 600; /* Default to 10 minutes */ - ux500_wdt.parent = dev; - watchdog_set_nowayout(&ux500_wdt, nowayout); + db8500_wdt.parent = dev; + watchdog_set_nowayout(&db8500_wdt, nowayout); /* disable auto off on sleep */ prcmu_config_a9wdog(PRCMU_WDOG_CPU1, false); @@ -96,7 +96,7 @@ static int ux500_wdt_probe(struct platform_device *pdev) /* set HW initial value */ prcmu_load_a9wdog(PRCMU_WDOG_ALL, timeout * 1000); - ret = devm_watchdog_register_device(dev, &ux500_wdt); + ret = devm_watchdog_register_device(dev, &db8500_wdt); if (ret) return ret; @@ -106,47 +106,47 @@ static int ux500_wdt_probe(struct platform_device *pdev) } #ifdef CONFIG_PM -static int ux500_wdt_suspend(struct platform_device *pdev, +static int db8500_wdt_suspend(struct platform_device *pdev, pm_message_t state) { - if (watchdog_active(&ux500_wdt)) { - ux500_wdt_stop(&ux500_wdt); + if (watchdog_active(&db8500_wdt)) { + db8500_wdt_stop(&db8500_wdt); prcmu_config_a9wdog(PRCMU_WDOG_CPU1, true); prcmu_load_a9wdog(PRCMU_WDOG_ALL, timeout * 1000); - ux500_wdt_start(&ux500_wdt); + db8500_wdt_start(&db8500_wdt); } return 0; } -static int ux500_wdt_resume(struct platform_device *pdev) +static int db8500_wdt_resume(struct platform_device *pdev) { - if (watchdog_active(&ux500_wdt)) { - ux500_wdt_stop(&ux500_wdt); + if (watchdog_active(&db8500_wdt)) { + db8500_wdt_stop(&db8500_wdt); prcmu_config_a9wdog(PRCMU_WDOG_CPU1, false); prcmu_load_a9wdog(PRCMU_WDOG_ALL, timeout * 1000); - ux500_wdt_start(&ux500_wdt); + db8500_wdt_start(&db8500_wdt); } return 0; } #else -#define ux500_wdt_suspend NULL -#define ux500_wdt_resume NULL +#define db8500_wdt_suspend NULL +#define db8500_wdt_resume NULL #endif -static struct platform_driver ux500_wdt_driver = { - .probe = ux500_wdt_probe, - .suspend = ux500_wdt_suspend, - .resume = ux500_wdt_resume, +static struct platform_driver db8500_wdt_driver = { + .probe = db8500_wdt_probe, + .suspend = db8500_wdt_suspend, + .resume = db8500_wdt_resume, .driver = { - .name = "ux500_wdt", + .name = "db8500_wdt", }, }; -module_platform_driver(ux500_wdt_driver); +module_platform_driver(db8500_wdt_driver); MODULE_AUTHOR("Jonas Aaberg "); -MODULE_DESCRIPTION("Ux500 Watchdog Driver"); +MODULE_DESCRIPTION("DB8500 Watchdog Driver"); MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:ux500_wdt"); +MODULE_ALIAS("platform:db8500_wdt");