From 52a5502507bca19644c4d23112130eea361e79f1 Mon Sep 17 00:00:00 2001 From: Matti Vaittinen Date: Tue, 25 May 2021 13:14:31 +0300 Subject: watchdog: bd70528 drop bd70528 support The only known BD70528 use-cases are such that the PMIC is controlled from separate MCU which is not running Linux. I am not aware of any Linux driver users. Furthermore, it seems there is no demand for this IC. Let's ease the maintenance burden and drop the driver. We can always add it back if there is sudden need for it. Signed-off-by: Matti Vaittinen Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/994d2e374262c3f59f4465c03ef23d3116120778.1621937490.git.matti.vaittinen@fi.rohmeurope.com Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/Kconfig | 12 -- drivers/watchdog/Makefile | 1 - drivers/watchdog/bd70528_wdt.c | 291 ----------------------------------------- 3 files changed, 304 deletions(-) delete mode 100644 drivers/watchdog/bd70528_wdt.c diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 546dfc1e2349..478544f94df0 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -168,18 +168,6 @@ config SOFT_WATCHDOG_PRETIMEOUT watchdog. Be aware that governors might affect the watchdog because it is purely software, e.g. the panic governor will stall it! -config BD70528_WATCHDOG - tristate "ROHM BD70528 PMIC Watchdog" - depends on MFD_ROHM_BD70528 - select WATCHDOG_CORE - help - Support for the watchdog in the ROHM BD70528 PMIC. Watchdog trigger - cause system reset. - - Say Y here to include support for the ROHM BD70528 watchdog. - Alternatively say M to compile the driver as a module, - which will be called bd70528_wdt. - config BD957XMUF_WATCHDOG tristate "ROHM BD9576MUF and BD9573MUF PMIC Watchdog" depends on MFD_ROHM_BD957XMUF diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index abaf2ebd814e..1bd2d6f37c53 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -204,7 +204,6 @@ obj-$(CONFIG_WATCHDOG_SUN4V) += sun4v_wdt.o obj-$(CONFIG_XEN_WDT) += xen_wdt.o # Architecture Independent -obj-$(CONFIG_BD70528_WATCHDOG) += bd70528_wdt.o obj-$(CONFIG_BD957XMUF_WATCHDOG) += bd9576_wdt.o obj-$(CONFIG_DA9052_WATCHDOG) += da9052_wdt.o obj-$(CONFIG_DA9055_WATCHDOG) += da9055_wdt.o diff --git a/drivers/watchdog/bd70528_wdt.c b/drivers/watchdog/bd70528_wdt.c deleted file mode 100644 index 0170b37e6674..000000000000 --- a/drivers/watchdog/bd70528_wdt.c +++ /dev/null @@ -1,291 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -// Copyright (C) 2018 ROHM Semiconductors -// ROHM BD70528MWV watchdog driver - -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * Max time we can set is 1 hour, 59 minutes and 59 seconds - * and Minimum time is 1 second - */ -#define WDT_MAX_MS ((2 * 60 * 60 - 1) * 1000) -#define WDT_MIN_MS 1000 -#define DEFAULT_TIMEOUT 60 - -#define WD_CTRL_MAGIC1 0x55 -#define WD_CTRL_MAGIC2 0xAA - -struct wdtbd70528 { - struct device *dev; - struct regmap *regmap; - struct rohm_regmap_dev *mfd; - struct watchdog_device wdt; -}; - -/** - * bd70528_wdt_set - arm or disarm watchdog timer - * - * @data: device data for the PMIC instance we want to operate on - * @enable: new state of WDT. zero to disable, non zero to enable - * @old_state: previous state of WDT will be filled here - * - * Arm or disarm WDT on BD70528 PMIC. Expected to be called only by - * BD70528 RTC and BD70528 WDT drivers. The rtc_timer_lock must be taken - * by calling bd70528_wdt_lock before calling bd70528_wdt_set. - */ -int bd70528_wdt_set(struct rohm_regmap_dev *data, int enable, int *old_state) -{ - int ret, i; - unsigned int tmp; - struct bd70528_data *bd70528 = container_of(data, struct bd70528_data, - chip); - u8 wd_ctrl_arr[3] = { WD_CTRL_MAGIC1, WD_CTRL_MAGIC2, 0 }; - u8 *wd_ctrl = &wd_ctrl_arr[2]; - - ret = regmap_read(bd70528->chip.regmap, BD70528_REG_WDT_CTRL, &tmp); - if (ret) - return ret; - - *wd_ctrl = (u8)tmp; - - if (old_state) { - if (*wd_ctrl & BD70528_MASK_WDT_EN) - *old_state |= BD70528_WDT_STATE_BIT; - else - *old_state &= ~BD70528_WDT_STATE_BIT; - if ((!enable) == (!(*old_state & BD70528_WDT_STATE_BIT))) - return 0; - } - - if (enable) { - if (*wd_ctrl & BD70528_MASK_WDT_EN) - return 0; - *wd_ctrl |= BD70528_MASK_WDT_EN; - } else { - if (*wd_ctrl & BD70528_MASK_WDT_EN) - *wd_ctrl &= ~BD70528_MASK_WDT_EN; - else - return 0; - } - - for (i = 0; i < 3; i++) { - ret = regmap_write(bd70528->chip.regmap, BD70528_REG_WDT_CTRL, - wd_ctrl_arr[i]); - if (ret) - return ret; - } - - ret = regmap_read(bd70528->chip.regmap, BD70528_REG_WDT_CTRL, &tmp); - if ((tmp & BD70528_MASK_WDT_EN) != (*wd_ctrl & BD70528_MASK_WDT_EN)) { - dev_err(bd70528->chip.dev, - "Watchdog ctrl mismatch (hw) 0x%x (set) 0x%x\n", - tmp, *wd_ctrl); - ret = -EIO; - } - - return ret; -} -EXPORT_SYMBOL(bd70528_wdt_set); - -/** - * bd70528_wdt_lock - take WDT lock - * - * @data: device data for the PMIC instance we want to operate on - * - * Lock WDT for arming/disarming in order to avoid race condition caused - * by WDT state changes initiated by WDT and RTC drivers. - */ -void bd70528_wdt_lock(struct rohm_regmap_dev *data) -{ - struct bd70528_data *bd70528 = container_of(data, struct bd70528_data, - chip); - - mutex_lock(&bd70528->rtc_timer_lock); -} -EXPORT_SYMBOL(bd70528_wdt_lock); - -/** - * bd70528_wdt_unlock - unlock WDT lock - * - * @data: device data for the PMIC instance we want to operate on - * - * Unlock WDT lock which has previously been taken by call to - * bd70528_wdt_lock. - */ -void bd70528_wdt_unlock(struct rohm_regmap_dev *data) -{ - struct bd70528_data *bd70528 = container_of(data, struct bd70528_data, - chip); - - mutex_unlock(&bd70528->rtc_timer_lock); -} -EXPORT_SYMBOL(bd70528_wdt_unlock); - -static int bd70528_wdt_set_locked(struct wdtbd70528 *w, int enable) -{ - return bd70528_wdt_set(w->mfd, enable, NULL); -} - -static int bd70528_wdt_change(struct wdtbd70528 *w, int enable) -{ - int ret; - - bd70528_wdt_lock(w->mfd); - ret = bd70528_wdt_set_locked(w, enable); - bd70528_wdt_unlock(w->mfd); - - return ret; -} - -static int bd70528_wdt_start(struct watchdog_device *wdt) -{ - struct wdtbd70528 *w = watchdog_get_drvdata(wdt); - - dev_dbg(w->dev, "WDT ping...\n"); - return bd70528_wdt_change(w, 1); -} - -static int bd70528_wdt_stop(struct watchdog_device *wdt) -{ - struct wdtbd70528 *w = watchdog_get_drvdata(wdt); - - dev_dbg(w->dev, "WDT stopping...\n"); - return bd70528_wdt_change(w, 0); -} - -static int bd70528_wdt_set_timeout(struct watchdog_device *wdt, - unsigned int timeout) -{ - unsigned int hours; - unsigned int minutes; - unsigned int seconds; - int ret; - struct wdtbd70528 *w = watchdog_get_drvdata(wdt); - - seconds = timeout; - hours = timeout / (60 * 60); - /* Maximum timeout is 1h 59m 59s => hours is 1 or 0 */ - if (hours) - seconds -= (60 * 60); - minutes = seconds / 60; - seconds = seconds % 60; - - bd70528_wdt_lock(w->mfd); - - ret = bd70528_wdt_set_locked(w, 0); - if (ret) - goto out_unlock; - - ret = regmap_update_bits(w->regmap, BD70528_REG_WDT_HOUR, - BD70528_MASK_WDT_HOUR, hours); - if (ret) { - dev_err(w->dev, "Failed to set WDT hours\n"); - goto out_en_unlock; - } - ret = regmap_update_bits(w->regmap, BD70528_REG_WDT_MINUTE, - BD70528_MASK_WDT_MINUTE, bin2bcd(minutes)); - if (ret) { - dev_err(w->dev, "Failed to set WDT minutes\n"); - goto out_en_unlock; - } - ret = regmap_update_bits(w->regmap, BD70528_REG_WDT_SEC, - BD70528_MASK_WDT_SEC, bin2bcd(seconds)); - if (ret) - dev_err(w->dev, "Failed to set WDT seconds\n"); - else - dev_dbg(w->dev, "WDT tmo set to %u\n", timeout); - -out_en_unlock: - ret = bd70528_wdt_set_locked(w, 1); -out_unlock: - bd70528_wdt_unlock(w->mfd); - - return ret; -} - -static const struct watchdog_info bd70528_wdt_info = { - .identity = "bd70528-wdt", - .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, -}; - -static const struct watchdog_ops bd70528_wdt_ops = { - .start = bd70528_wdt_start, - .stop = bd70528_wdt_stop, - .set_timeout = bd70528_wdt_set_timeout, -}; - -static int bd70528_wdt_probe(struct platform_device *pdev) -{ - struct rohm_regmap_dev *bd70528; - struct wdtbd70528 *w; - int ret; - unsigned int reg; - - bd70528 = dev_get_drvdata(pdev->dev.parent); - if (!bd70528) { - dev_err(&pdev->dev, "No MFD driver data\n"); - return -EINVAL; - } - w = devm_kzalloc(&pdev->dev, sizeof(*w), GFP_KERNEL); - if (!w) - return -ENOMEM; - - w->regmap = bd70528->regmap; - w->mfd = bd70528; - w->dev = &pdev->dev; - - w->wdt.info = &bd70528_wdt_info; - w->wdt.ops = &bd70528_wdt_ops; - w->wdt.min_hw_heartbeat_ms = WDT_MIN_MS; - w->wdt.max_hw_heartbeat_ms = WDT_MAX_MS; - w->wdt.parent = pdev->dev.parent; - w->wdt.timeout = DEFAULT_TIMEOUT; - watchdog_set_drvdata(&w->wdt, w); - watchdog_init_timeout(&w->wdt, 0, pdev->dev.parent); - - ret = bd70528_wdt_set_timeout(&w->wdt, w->wdt.timeout); - if (ret) { - dev_err(&pdev->dev, "Failed to set the watchdog timeout\n"); - return ret; - } - - bd70528_wdt_lock(w->mfd); - ret = regmap_read(w->regmap, BD70528_REG_WDT_CTRL, ®); - bd70528_wdt_unlock(w->mfd); - - if (ret) { - dev_err(&pdev->dev, "Failed to get the watchdog state\n"); - return ret; - } - if (reg & BD70528_MASK_WDT_EN) { - dev_dbg(&pdev->dev, "watchdog was running during probe\n"); - set_bit(WDOG_HW_RUNNING, &w->wdt.status); - } - - ret = devm_watchdog_register_device(&pdev->dev, &w->wdt); - if (ret < 0) - dev_err(&pdev->dev, "watchdog registration failed: %d\n", ret); - - return ret; -} - -static struct platform_driver bd70528_wdt = { - .driver = { - .name = "bd70528-wdt" - }, - .probe = bd70528_wdt_probe, -}; - -module_platform_driver(bd70528_wdt); - -MODULE_AUTHOR("Matti Vaittinen "); -MODULE_DESCRIPTION("BD70528 watchdog driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:bd70528-wdt"); -- cgit From cf6ea9542372a966544d947c17c94048d4883325 Mon Sep 17 00:00:00 2001 From: Curtis Klein Date: Sat, 26 Jun 2021 16:47:45 -0700 Subject: watchdog: only run driver set_pretimeout op if device supports it Some watchdog devices might conditionally support pretimeouts (e.g. if an interrupt is exposed for the device) but some watchdog drivers might still define the set_pretimeout operation (e.g. the mtk_wdt driver) and indicate support at runtime through the WDIOF_PRETIMEOUT flag. If the kernel is compiled with CONFIG_WATCHDOG_HRTIMER_PRETIMEOUT enabled, watchdog_set_pretimeout would run the driver specific set_pretimeout even if WDIOF_PRETIMEOUT is not set which might have unintended consequences. So this change checks that the device flags and only runs the driver operation if pretimeouts are supported. Signed-off-by: Curtis Klein Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/1624751265-24785-1-git-send-email-curtis.klein@hpe.com Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/watchdog_dev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c index 3bab32485273..5bf795c1b346 100644 --- a/drivers/watchdog/watchdog_dev.c +++ b/drivers/watchdog/watchdog_dev.c @@ -401,7 +401,7 @@ static int watchdog_set_pretimeout(struct watchdog_device *wdd, if (watchdog_pretimeout_invalid(wdd, timeout)) return -EINVAL; - if (wdd->ops->set_pretimeout) + if (wdd->ops->set_pretimeout && (wdd->info->options & WDIOF_PRETIMEOUT)) err = wdd->ops->set_pretimeout(wdd, timeout); else wdd->pretimeout = timeout; -- cgit From c7b178dae139f8857edc50888cfbf251cd974a38 Mon Sep 17 00:00:00 2001 From: Curtis Klein Date: Tue, 22 Jun 2021 23:26:23 -0700 Subject: watchdog: Fix NULL pointer dereference when releasing cdev watchdog_hrtimer_pretimeout_stop needs the watchdog device to have a valid pointer to the watchdog core data to stop the pretimeout hrtimer. Therefore it needs to be called before the pointers are cleared in watchdog_cdev_unregister. Fixes: 7b7d2fdc8c3e ("watchdog: Add hrtimer-based pretimeout feature") Reported-by: Colin Ian King Signed-off-by: Curtis Klein Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/1624429583-5720-1-git-send-email-curtis.klein@hpe.com Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/watchdog_dev.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c index 5bf795c1b346..f408967ff1a4 100644 --- a/drivers/watchdog/watchdog_dev.c +++ b/drivers/watchdog/watchdog_dev.c @@ -1096,6 +1096,8 @@ static void watchdog_cdev_unregister(struct watchdog_device *wdd) watchdog_stop(wdd); } + watchdog_hrtimer_pretimeout_stop(wdd); + mutex_lock(&wd_data->lock); wd_data->wdd = NULL; wdd->wd_data = NULL; @@ -1103,7 +1105,6 @@ static void watchdog_cdev_unregister(struct watchdog_device *wdd) hrtimer_cancel(&wd_data->timer); kthread_cancel_work_sync(&wd_data->work); - watchdog_hrtimer_pretimeout_stop(wdd); put_device(&wd_data->dev); } -- cgit From 60bcd91aafd22ef62cef9ae2037fa2e1d4da2fb3 Mon Sep 17 00:00:00 2001 From: Grzegorz Jaszczyk Date: Fri, 18 Jun 2021 21:50:32 +0200 Subject: watchdog: introduce watchdog_dev_suspend/resume The watchdog drivers often disable wdog clock during suspend and then enable it again during resume. Nevertheless the ping worker is still running and can issue low-level ping while the wdog clock is disabled causing the system hang. To prevent such condition register pm notifier in the watchdog core which will call watchdog_dev_suspend/resume and actually cancel ping worker during suspend and restore it back, if needed, during resume. Signed-off-by: Grzegorz Jaszczyk Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20210618195033.3209598-2-grzegorz.jaszczyk@linaro.org Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/watchdog_core.c | 37 +++++++++++++++++++++++++++++++ drivers/watchdog/watchdog_dev.c | 47 ++++++++++++++++++++++++++++++++++++++++ include/linux/watchdog.h | 10 +++++++++ 3 files changed, 94 insertions(+) diff --git a/drivers/watchdog/watchdog_core.c b/drivers/watchdog/watchdog_core.c index 5df0a22e2cb4..3fe8a7edc252 100644 --- a/drivers/watchdog/watchdog_core.c +++ b/drivers/watchdog/watchdog_core.c @@ -34,6 +34,7 @@ #include /* For ida_* macros */ #include /* For IS_ERR macros */ #include /* For of_get_timeout_sec */ +#include #include "watchdog_core.h" /* For watchdog_dev_register/... */ @@ -185,6 +186,33 @@ static int watchdog_restart_notifier(struct notifier_block *nb, return NOTIFY_DONE; } +static int watchdog_pm_notifier(struct notifier_block *nb, unsigned long mode, + void *data) +{ + struct watchdog_device *wdd; + int ret = 0; + + wdd = container_of(nb, struct watchdog_device, pm_nb); + + switch (mode) { + case PM_HIBERNATION_PREPARE: + case PM_RESTORE_PREPARE: + case PM_SUSPEND_PREPARE: + ret = watchdog_dev_suspend(wdd); + break; + case PM_POST_HIBERNATION: + case PM_POST_RESTORE: + case PM_POST_SUSPEND: + ret = watchdog_dev_resume(wdd); + break; + } + + if (ret) + return NOTIFY_BAD; + + return NOTIFY_DONE; +} + /** * watchdog_set_restart_priority - Change priority of restart handler * @wdd: watchdog device @@ -292,6 +320,15 @@ static int __watchdog_register_device(struct watchdog_device *wdd) wdd->id, ret); } + if (test_bit(WDOG_NO_PING_ON_SUSPEND, &wdd->status)) { + wdd->pm_nb.notifier_call = watchdog_pm_notifier; + + ret = register_pm_notifier(&wdd->pm_nb); + if (ret) + pr_warn("watchdog%d: Cannot register pm handler (%d)\n", + wdd->id, ret); + } + return 0; } diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c index f408967ff1a4..597cf16ea4ba 100644 --- a/drivers/watchdog/watchdog_dev.c +++ b/drivers/watchdog/watchdog_dev.c @@ -1228,6 +1228,53 @@ void __exit watchdog_dev_exit(void) kthread_destroy_worker(watchdog_kworker); } +int watchdog_dev_suspend(struct watchdog_device *wdd) +{ + struct watchdog_core_data *wd_data = wdd->wd_data; + int ret = 0; + + if (!wdd->wd_data) + return -ENODEV; + + /* ping for the last time before suspend */ + mutex_lock(&wd_data->lock); + if (watchdog_worker_should_ping(wd_data)) + ret = __watchdog_ping(wd_data->wdd); + mutex_unlock(&wd_data->lock); + + if (ret) + return ret; + + /* + * make sure that watchdog worker will not kick in when the wdog is + * suspended + */ + hrtimer_cancel(&wd_data->timer); + kthread_cancel_work_sync(&wd_data->work); + + return 0; +} + +int watchdog_dev_resume(struct watchdog_device *wdd) +{ + struct watchdog_core_data *wd_data = wdd->wd_data; + int ret = 0; + + if (!wdd->wd_data) + return -ENODEV; + + /* + * __watchdog_ping will also retrigger hrtimer and therefore restore the + * ping worker if needed. + */ + mutex_lock(&wd_data->lock); + if (watchdog_worker_should_ping(wd_data)) + ret = __watchdog_ping(wd_data->wdd); + mutex_unlock(&wd_data->lock); + + return ret; +} + module_param(handle_boot_enabled, bool, 0444); MODULE_PARM_DESC(handle_boot_enabled, "Watchdog core auto-updates boot enabled watchdogs before userspace takes over (default=" diff --git a/include/linux/watchdog.h b/include/linux/watchdog.h index 9b19e6bb68b5..99660197a36c 100644 --- a/include/linux/watchdog.h +++ b/include/linux/watchdog.h @@ -107,6 +107,7 @@ struct watchdog_device { unsigned int max_hw_heartbeat_ms; struct notifier_block reboot_nb; struct notifier_block restart_nb; + struct notifier_block pm_nb; void *driver_data; struct watchdog_core_data *wd_data; unsigned long status; @@ -116,6 +117,7 @@ struct watchdog_device { #define WDOG_STOP_ON_REBOOT 2 /* Should be stopped on reboot */ #define WDOG_HW_RUNNING 3 /* True if HW watchdog running */ #define WDOG_STOP_ON_UNREGISTER 4 /* Should be stopped on unregister */ +#define WDOG_NO_PING_ON_SUSPEND 5 /* Ping worker should be stopped on suspend */ struct list_head deferred; }; @@ -156,6 +158,12 @@ static inline void watchdog_stop_on_unregister(struct watchdog_device *wdd) set_bit(WDOG_STOP_ON_UNREGISTER, &wdd->status); } +/* Use the following function to stop the wdog ping worker when suspending */ +static inline void watchdog_stop_ping_on_suspend(struct watchdog_device *wdd) +{ + set_bit(WDOG_NO_PING_ON_SUSPEND, &wdd->status); +} + /* Use the following function to check if a timeout value is invalid */ static inline bool watchdog_timeout_invalid(struct watchdog_device *wdd, unsigned int t) { @@ -209,6 +217,8 @@ extern int watchdog_init_timeout(struct watchdog_device *wdd, unsigned int timeout_parm, struct device *dev); extern int watchdog_register_device(struct watchdog_device *); extern void watchdog_unregister_device(struct watchdog_device *); +int watchdog_dev_suspend(struct watchdog_device *wdd); +int watchdog_dev_resume(struct watchdog_device *wdd); int watchdog_set_last_hw_keepalive(struct watchdog_device *, unsigned int); -- cgit From 14244b7c04d6611388e9312059df11b9c39a4f6c Mon Sep 17 00:00:00 2001 From: Grzegorz Jaszczyk Date: Fri, 18 Jun 2021 21:50:33 +0200 Subject: watchdog: imx2_wdg: notify wdog core to stop ping worker on suspend Suspend routine disables wdog clk. Nevertheless, the watchdog subsystem is not aware of that and can still try to ping wdog through watchdog_ping_work. In order to prevent such condition and therefore prevent from system hang (caused by the wdog register access issued while the wdog clock is disabled) notify watchdog core that the ping worker should be canceled during watchdog core suspend and restored during resume. Signed-off-by: Michal Koziel Signed-off-by: Grzegorz Jaszczyk Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20210618195033.3209598-3-grzegorz.jaszczyk@linaro.org Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/imx2_wdt.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/watchdog/imx2_wdt.c b/drivers/watchdog/imx2_wdt.c index cc86018c5eb5..51bfb796898b 100644 --- a/drivers/watchdog/imx2_wdt.c +++ b/drivers/watchdog/imx2_wdt.c @@ -317,6 +317,7 @@ static int __init imx2_wdt_probe(struct platform_device *pdev) watchdog_set_nowayout(wdog, nowayout); watchdog_set_restart_priority(wdog, 128); watchdog_init_timeout(wdog, timeout, dev); + watchdog_stop_ping_on_suspend(wdog); if (imx2_wdt_is_running(wdev)) { imx2_wdt_set_timeout(wdog, wdog->timeout); -- cgit From a4f95810e3fbc15f077880aef8c787489ec4a8b7 Mon Sep 17 00:00:00 2001 From: Stefan Wahren Date: Sun, 6 Jun 2021 14:16:12 +0200 Subject: watchdog: bcm2835_wdt: consider system-power-controller property Until now all Raspberry Pi boards used the power off function of the SoC. But the Raspberry Pi 400 uses gpio-poweroff for the whole board which possibly cannot register the poweroff handler because the it's already registered by this watchdog driver. So consider the system-power-controller property for registering, which is already defined in soc/bcm/brcm,bcm2835-pm.txt . Signed-off-by: Stefan Wahren Reviewed-by: Nicolas Saenz Julienne Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/1622981777-5023-3-git-send-email-stefan.wahren@i2se.com Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/bcm2835_wdt.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/watchdog/bcm2835_wdt.c b/drivers/watchdog/bcm2835_wdt.c index dec6ca019bea..94907176a0e4 100644 --- a/drivers/watchdog/bcm2835_wdt.c +++ b/drivers/watchdog/bcm2835_wdt.c @@ -205,9 +205,13 @@ static int bcm2835_wdt_probe(struct platform_device *pdev) if (err) return err; - if (pm_power_off == NULL) { - pm_power_off = bcm2835_power_off; - bcm2835_power_off_wdt = wdt; + if (of_device_is_system_power_controller(pdev->dev.parent->of_node)) { + if (!pm_power_off) { + pm_power_off = bcm2835_power_off; + bcm2835_power_off_wdt = wdt; + } else { + dev_info(dev, "Poweroff handler already present!\n"); + } } dev_info(dev, "Broadcom BCM2835 watchdog timer"); -- cgit From aec42642d91fc86ddc03e97f0139c6c34ee6b6b1 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Mon, 26 Jul 2021 13:46:13 +0200 Subject: watchdog: iTCO_wdt: Fix detection of SMI-off case MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Obviously, the test needs to run against the register content, not its address. Fixes: cb011044e34c ("watchdog: iTCO_wdt: Account for rebooting on second timeout") Cc: stable@vger.kernel.org Reported-by: Mantas Mikulėnas Signed-off-by: Jan Kiszka Reviewed-by: Paolo Bonzini Reviewed-by: Guenter Roeck Tested-by: Mantas Mikulėnas Link: https://lore.kernel.org/r/d84f8e06-f646-8b43-d063-fb11f4827044@siemens.com Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/iTCO_wdt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/watchdog/iTCO_wdt.c b/drivers/watchdog/iTCO_wdt.c index b3f604669e2c..643c6c2d0b72 100644 --- a/drivers/watchdog/iTCO_wdt.c +++ b/drivers/watchdog/iTCO_wdt.c @@ -362,7 +362,7 @@ static int iTCO_wdt_set_timeout(struct watchdog_device *wd_dev, unsigned int t) * Otherwise, the BIOS generally reboots when the SMI triggers. */ if (p->smi_res && - (SMI_EN(p) & (TCO_EN | GBL_SMI_EN)) != (TCO_EN | GBL_SMI_EN)) + (inl(SMI_EN(p)) & (TCO_EN | GBL_SMI_EN)) != (TCO_EN | GBL_SMI_EN)) tmrval /= 2; /* from the specs: */ -- cgit From ade448c7e58e58b8fb86ebf31497b68b7c4f94d7 Mon Sep 17 00:00:00 2001 From: Rikard Falkeborn Date: Wed, 28 Jul 2021 00:30:40 +0200 Subject: watchdog: sl28cpld_wdt: Constify static struct watchdog_ops The struct sl28cpld_wdt_ops is only assigned to the ops pointer in the watchdog_device struct, which is a pointer to const struct watchdog_ops. Make it const to allow the compiler to put it in read-only memory. Signed-off-by: Rikard Falkeborn Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20210727223042.48150-2-rikard.falkeborn@gmail.com Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/sl28cpld_wdt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/watchdog/sl28cpld_wdt.c b/drivers/watchdog/sl28cpld_wdt.c index 2de93298475f..9ce456f09f73 100644 --- a/drivers/watchdog/sl28cpld_wdt.c +++ b/drivers/watchdog/sl28cpld_wdt.c @@ -108,7 +108,7 @@ static const struct watchdog_info sl28cpld_wdt_info = { .identity = "sl28cpld watchdog", }; -static struct watchdog_ops sl28cpld_wdt_ops = { +static const struct watchdog_ops sl28cpld_wdt_ops = { .owner = THIS_MODULE, .start = sl28cpld_wdt_start, .stop = sl28cpld_wdt_stop, -- cgit From 47b45c4a69febd423ecb011fce22559158cd08d9 Mon Sep 17 00:00:00 2001 From: Rikard Falkeborn Date: Wed, 28 Jul 2021 00:30:42 +0200 Subject: watchdog: mpc8xxx_wdt: Constify static struct watchdog_ops The struct mpc8xxx_wdt_ops is only assigned to the ops pointer in the watchdog_device struct, which is a pointer to const struct watchdog_ops. Make it const to allow the compiler to put it in read-only memory. Signed-off-by: Rikard Falkeborn Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20210727223042.48150-4-rikard.falkeborn@gmail.com Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/mpc8xxx_wdt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/watchdog/mpc8xxx_wdt.c b/drivers/watchdog/mpc8xxx_wdt.c index 2f7ded32e878..1c569be72ea2 100644 --- a/drivers/watchdog/mpc8xxx_wdt.c +++ b/drivers/watchdog/mpc8xxx_wdt.c @@ -118,7 +118,7 @@ static struct watchdog_info mpc8xxx_wdt_info = { .identity = "MPC8xxx", }; -static struct watchdog_ops mpc8xxx_wdt_ops = { +static const struct watchdog_ops mpc8xxx_wdt_ops = { .owner = THIS_MODULE, .start = mpc8xxx_wdt_start, .ping = mpc8xxx_wdt_ping, -- cgit From 625e407ce0e78c82ce19f32710d2996f9ea11449 Mon Sep 17 00:00:00 2001 From: Rikard Falkeborn Date: Wed, 28 Jul 2021 00:30:41 +0200 Subject: watchdog: tqmx86: Constify static struct watchdog_ops The struct tqmx86_wdt_ops is only assigned to the ops pointer in the watchdog_device struct, which is a pointer to const struct watchdog_ops. Make it const to allow the compiler to put it in read-only memory. Signed-off-by: Rikard Falkeborn Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20210727223042.48150-3-rikard.falkeborn@gmail.com Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/tqmx86_wdt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/watchdog/tqmx86_wdt.c b/drivers/watchdog/tqmx86_wdt.c index 72d0b0adde38..83860e94ce9d 100644 --- a/drivers/watchdog/tqmx86_wdt.c +++ b/drivers/watchdog/tqmx86_wdt.c @@ -62,7 +62,7 @@ static const struct watchdog_info tqmx86_wdt_info = { .identity = "TQMx86 Watchdog", }; -static struct watchdog_ops tqmx86_wdt_ops = { +static const struct watchdog_ops tqmx86_wdt_ops = { .owner = THIS_MODULE, .start = tqmx86_wdt_start, .set_timeout = tqmx86_wdt_set_timeout, -- cgit From 39c5b2f6f22595e0a0c207a1a8475cffb719f5bf Mon Sep 17 00:00:00 2001 From: Christine Zhu Date: Fri, 6 Aug 2021 10:36:05 +0800 Subject: dt-bindings: reset: mt8195: add toprgu reset-controller header file Add toprgu reset-controller header file for MT8195 platform. Signed-off-by: Christine Zhu Acked-by: Rob Herring Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20210726122901.12195-3-Christine.Zhu@mediatek.com Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- include/dt-bindings/reset/mt8195-resets.h | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 include/dt-bindings/reset/mt8195-resets.h diff --git a/include/dt-bindings/reset/mt8195-resets.h b/include/dt-bindings/reset/mt8195-resets.h new file mode 100644 index 000000000000..a26bccc8b957 --- /dev/null +++ b/include/dt-bindings/reset/mt8195-resets.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)*/ +/* + * Copyright (c) 2021 MediaTek Inc. + * Author: Christine Zhu + */ + +#ifndef _DT_BINDINGS_RESET_CONTROLLER_MT8195 +#define _DT_BINDINGS_RESET_CONTROLLER_MT8195 + +#define MT8195_TOPRGU_CONN_MCU_SW_RST 0 +#define MT8195_TOPRGU_INFRA_GRST_SW_RST 1 +#define MT8195_TOPRGU_APU_SW_RST 2 +#define MT8195_TOPRGU_INFRA_AO_GRST_SW_RST 6 +#define MT8195_TOPRGU_MMSYS_SW_RST 7 +#define MT8195_TOPRGU_MFG_SW_RST 8 +#define MT8195_TOPRGU_VENC_SW_RST 9 +#define MT8195_TOPRGU_VDEC_SW_RST 10 +#define MT8195_TOPRGU_IMG_SW_RST 11 +#define MT8195_TOPRGU_APMIXEDSYS_SW_RST 13 +#define MT8195_TOPRGU_AUDIO_SW_RST 14 +#define MT8195_TOPRGU_CAMSYS_SW_RST 15 +#define MT8195_TOPRGU_EDPTX_SW_RST 16 +#define MT8195_TOPRGU_ADSPSYS_SW_RST 21 +#define MT8195_TOPRGU_DPTX_SW_RST 22 +#define MT8195_TOPRGU_SPMI_MST_SW_RST 23 + +#define MT8195_TOPRGU_SW_RST_NUM 16 + +#endif /* _DT_BINDINGS_RESET_CONTROLLER_MT8195 */ -- cgit From 8c6b5ea6ac6851b29ee808c413b93fea952cab62 Mon Sep 17 00:00:00 2001 From: Christine Zhu Date: Fri, 6 Aug 2021 10:36:06 +0800 Subject: watchdog: mediatek: mt8195: add wdt support Support MT8195 watchdog device. Signed-off-by: Christine Zhu Reviewed-by: Guenter Roeck Reviewed-by: Tzung-Bi Shih Link: https://lore.kernel.org/r/20210726122901.12195-4-Christine.Zhu@mediatek.com Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/mtk_wdt.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/watchdog/mtk_wdt.c b/drivers/watchdog/mtk_wdt.c index 16b6aff324a7..796fbb048cbe 100644 --- a/drivers/watchdog/mtk_wdt.c +++ b/drivers/watchdog/mtk_wdt.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -82,6 +83,10 @@ static const struct mtk_wdt_data mt8192_data = { .toprgu_sw_rst_num = MT8192_TOPRGU_SW_RST_NUM, }; +static const struct mtk_wdt_data mt8195_data = { + .toprgu_sw_rst_num = MT8195_TOPRGU_SW_RST_NUM, +}; + static int toprgu_reset_update(struct reset_controller_dev *rcdev, unsigned long id, bool assert) { @@ -408,6 +413,7 @@ static const struct of_device_id mtk_wdt_dt_ids[] = { { .compatible = "mediatek,mt6589-wdt" }, { .compatible = "mediatek,mt8183-wdt", .data = &mt8183_data }, { .compatible = "mediatek,mt8192-wdt", .data = &mt8192_data }, + { .compatible = "mediatek,mt8195-wdt", .data = &mt8195_data }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, mtk_wdt_dt_ids); -- cgit From 11648fa1886606f42ca7b7d6eb1ab219de7071f2 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 14 Jul 2021 17:33:13 +0200 Subject: dt-bindings: watchdog: Add Maxim MAX63xx bindings This adds devicetree bindings for the Maxim MAX63xx watchdog timers. Cc: devicetree@vger.kernel.org Cc: Marc Zyngier Signed-off-by: Linus Walleij Reviewed-by: Rob Herring Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20210714153314.1004147-1-linus.walleij@linaro.org Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- .../bindings/watchdog/maxim,max63xx.yaml | 44 ++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 Documentation/devicetree/bindings/watchdog/maxim,max63xx.yaml diff --git a/Documentation/devicetree/bindings/watchdog/maxim,max63xx.yaml b/Documentation/devicetree/bindings/watchdog/maxim,max63xx.yaml new file mode 100644 index 000000000000..f2105eedac2c --- /dev/null +++ b/Documentation/devicetree/bindings/watchdog/maxim,max63xx.yaml @@ -0,0 +1,44 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/watchdog/maxim,max63xx.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Maxim 63xx Watchdog Timers + +allOf: + - $ref: "watchdog.yaml#" + +maintainers: + - Marc Zyngier + - Linus Walleij + +properties: + compatible: + oneOf: + - const: maxim,max6369 + - const: maxim,max6370 + - const: maxim,max6371 + - const: maxim,max6372 + - const: maxim,max6373 + - const: maxim,max6374 + + reg: + description: This is a 1-byte memory-mapped address + maxItems: 1 + +required: + - compatible + - reg + +unevaluatedProperties: false + +examples: + - | + wdt: watchdog@50000000 { + compatible = "maxim,max6369"; + reg = <0x50000000 0x1>; + timeout-sec = <10>; + }; + +... -- cgit From 585ba602b1ff76dd6e7fbd488a8c5921ae422305 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 14 Jul 2021 17:33:14 +0200 Subject: watchdog: max63xx_wdt: Add device tree probing This adds device tree probing to the MAX63xx driver so it can be instantiated from the device tree. We use the generic fwnode-based method to get to the match data and clean up by constifying the functions as the match is indeed a const. Cc: Marc Zyngier Signed-off-by: Linus Walleij Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20210714153314.1004147-2-linus.walleij@linaro.org Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/max63xx_wdt.c | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/drivers/watchdog/max63xx_wdt.c b/drivers/watchdog/max63xx_wdt.c index 3a899628a834..9e1541cfae0d 100644 --- a/drivers/watchdog/max63xx_wdt.c +++ b/drivers/watchdog/max63xx_wdt.c @@ -26,6 +26,7 @@ #include #include #include +#include #define DEFAULT_HEARTBEAT 60 #define MAX_HEARTBEAT 60 @@ -99,8 +100,8 @@ static const struct max63xx_timeout max6373_table[] = { { }, }; -static struct max63xx_timeout * -max63xx_select_timeout(struct max63xx_timeout *table, int value) +static const struct max63xx_timeout * +max63xx_select_timeout(const struct max63xx_timeout *table, int value) { while (table->twd) { if (value <= table->twd) { @@ -202,14 +203,17 @@ static int max63xx_wdt_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct max63xx_wdt *wdt; - struct max63xx_timeout *table; + const struct max63xx_timeout *table; int err; wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL); if (!wdt) return -ENOMEM; - table = (struct max63xx_timeout *)pdev->id_entry->driver_data; + /* Attempt to use fwnode first */ + table = device_get_match_data(dev); + if (!table) + table = (struct max63xx_timeout *)pdev->id_entry->driver_data; if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT) heartbeat = DEFAULT_HEARTBEAT; @@ -255,11 +259,23 @@ static const struct platform_device_id max63xx_id_table[] = { }; MODULE_DEVICE_TABLE(platform, max63xx_id_table); +static const struct of_device_id max63xx_dt_id_table[] = { + { .compatible = "maxim,max6369", .data = max6369_table, }, + { .compatible = "maxim,max6370", .data = max6369_table, }, + { .compatible = "maxim,max6371", .data = max6371_table, }, + { .compatible = "maxim,max6372", .data = max6371_table, }, + { .compatible = "maxim,max6373", .data = max6373_table, }, + { .compatible = "maxim,max6374", .data = max6373_table, }, + { } +}; +MODULE_DEVICE_TABLE(of, max63xx_dt_id_table); + static struct platform_driver max63xx_wdt_driver = { .probe = max63xx_wdt_probe, .id_table = max63xx_id_table, .driver = { .name = "max63xx_wdt", + .of_match_table = max63xx_dt_id_table, }, }; -- cgit From dbe80cf471f940db3063197b7adb1169f89be9ed Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Sun, 1 Aug 2021 09:56:25 +0200 Subject: watchdog: Start watchdog in watchdog_set_last_hw_keepalive only if appropriate We must not pet a running watchdog when handle_boot_enabled is off because this will kick off automatic triggering before userland is running, defeating the purpose of the handle_boot_enabled control. Furthermore, don't ping in case watchdog_set_last_hw_keepalive was called incorrectly when the hardware watchdog is actually not running. Fixed: cef9572e9af3 ("watchdog: add support for adjusting last known HW keepalive time") Signed-off-by: Jan Kiszka Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/93d56386-6e37-060b-55ce-84de8cde535f@web.de Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/watchdog_dev.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c index 597cf16ea4ba..3a3d8b5c7ad5 100644 --- a/drivers/watchdog/watchdog_dev.c +++ b/drivers/watchdog/watchdog_dev.c @@ -1173,7 +1173,10 @@ int watchdog_set_last_hw_keepalive(struct watchdog_device *wdd, wd_data->last_hw_keepalive = ktime_sub(now, ms_to_ktime(last_ping_ms)); - return __watchdog_ping(wdd); + if (watchdog_hw_running(wdd) && handle_boot_enabled) + return __watchdog_ping(wdd); + + return 0; } EXPORT_SYMBOL_GPL(watchdog_set_last_hw_keepalive); -- cgit From 580b8e2899770cf4768f52e37a1c8bddc46f13e7 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 26 Jul 2021 14:12:14 +0200 Subject: watchdog: ixp4xx: Rewrite driver to use core This rewrites the IXP4xx watchdog driver as follows: - Spawn the watchdog driver as a platform device from the timer driver. It's one device in the hardware, and the fact that Linux splits the handling into two different devices is a Linux pecularity, and thus it becomes a Linux pecularity to spawn a separate watchdog driver. - Spawn the watchdog driver from the timer driver at probe(). This is well after the timer driver as actually registered and started and we know the register base is available. - Instead of looping back callbacks to the timer drivers for all watchdog calls, pass the register base to the watchdog driver and manage the registers there. The two drivers aren't even interested in the same register so the spinlock is totally surplus, delete it. - Replace pretty much all of the content in the watchdog driver with a simple, modern watchdog driver utilizing the watchdog core instead of registering its own misc device and ioctl() handling. - Drop module parameters as the same already exist in the watchdog core. What remains is a slim elegant (IMO) watchdog driver using the watchdog core, spawning from device tree or boardfile alike. Cc: Daniel Lezcano Cc: Thomas Gleixner Signed-off-by: Linus Walleij Reviewed-by: Guenter Roeck Acked-by: Daniel Lezcano Link: https://lore.kernel.org/r/20210726121214.2572836-1-linus.walleij@linaro.org Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/clocksource/timer-ixp4xx.c | 48 +++++-- drivers/watchdog/Kconfig | 1 + drivers/watchdog/ixp4xx_wdt.c | 283 ++++++++++++++++--------------------- 3 files changed, 155 insertions(+), 177 deletions(-) diff --git a/drivers/clocksource/timer-ixp4xx.c b/drivers/clocksource/timer-ixp4xx.c index 9396745e1c17..cbb184953510 100644 --- a/drivers/clocksource/timer-ixp4xx.c +++ b/drivers/clocksource/timer-ixp4xx.c @@ -18,6 +18,7 @@ #include #include #include +#include /* Goes away with OF conversion */ #include @@ -29,9 +30,6 @@ #define IXP4XX_OSRT1_OFFSET 0x08 /* Timer 1 Reload */ #define IXP4XX_OST2_OFFSET 0x0C /* Timer 2 Timestamp */ #define IXP4XX_OSRT2_OFFSET 0x10 /* Timer 2 Reload */ -#define IXP4XX_OSWT_OFFSET 0x14 /* Watchdog Timer */ -#define IXP4XX_OSWE_OFFSET 0x18 /* Watchdog Enable */ -#define IXP4XX_OSWK_OFFSET 0x1C /* Watchdog Key */ #define IXP4XX_OSST_OFFSET 0x20 /* Timer Status */ /* @@ -45,17 +43,10 @@ #define IXP4XX_OSST_TIMER_1_PEND 0x00000001 #define IXP4XX_OSST_TIMER_2_PEND 0x00000002 #define IXP4XX_OSST_TIMER_TS_PEND 0x00000004 -#define IXP4XX_OSST_TIMER_WDOG_PEND 0x00000008 -#define IXP4XX_OSST_TIMER_WARM_RESET 0x00000010 - -#define IXP4XX_WDT_KEY 0x0000482E -#define IXP4XX_WDT_RESET_ENABLE 0x00000001 -#define IXP4XX_WDT_IRQ_ENABLE 0x00000002 -#define IXP4XX_WDT_COUNT_ENABLE 0x00000004 +/* Remaining registers are for the watchdog and defined in the watchdog driver */ struct ixp4xx_timer { void __iomem *base; - unsigned int tick_rate; u32 latch; struct clock_event_device clkevt; #ifdef CONFIG_ARM @@ -181,7 +172,6 @@ static __init int ixp4xx_timer_register(void __iomem *base, if (!tmr) return -ENOMEM; tmr->base = base; - tmr->tick_rate = timer_freq; /* * The timer register doesn't allow to specify the two least @@ -239,6 +229,40 @@ static __init int ixp4xx_timer_register(void __iomem *base, return 0; } +static struct platform_device ixp4xx_watchdog_device = { + .name = "ixp4xx-watchdog", + .id = -1, +}; + +/* + * This probe gets called after the timer is already up and running. The main + * function on this platform is to spawn the watchdog device as a child. + */ +static int ixp4xx_timer_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + + /* Pass the base address as platform data and nothing else */ + ixp4xx_watchdog_device.dev.platform_data = local_ixp4xx_timer->base; + ixp4xx_watchdog_device.dev.parent = dev; + return platform_device_register(&ixp4xx_watchdog_device); +} + +static const struct of_device_id ixp4xx_timer_dt_id[] = { + { .compatible = "intel,ixp4xx-timer", }, + { /* sentinel */ }, +}; + +static struct platform_driver ixp4xx_timer_driver = { + .probe = ixp4xx_timer_probe, + .driver = { + .name = "ixp4xx-timer", + .of_match_table = ixp4xx_timer_dt_id, + .suppress_bind_attrs = true, + }, +}; +builtin_platform_driver(ixp4xx_timer_driver); + /** * ixp4xx_timer_setup() - Timer setup function to be called from boardfiles * @timerbase: physical base of timer block diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 478544f94df0..b81fe4f7d434 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -475,6 +475,7 @@ config FTWDT010_WATCHDOG config IXP4XX_WATCHDOG tristate "IXP4xx Watchdog" depends on ARCH_IXP4XX + select WATCHDOG_CORE help Say Y here if to include support for the watchdog timer in the Intel IXP4xx network processors. This driver can diff --git a/drivers/watchdog/ixp4xx_wdt.c b/drivers/watchdog/ixp4xx_wdt.c index aae29dcfaf11..2693ffb24ac7 100644 --- a/drivers/watchdog/ixp4xx_wdt.c +++ b/drivers/watchdog/ixp4xx_wdt.c @@ -1,220 +1,173 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * drivers/char/watchdog/ixp4xx_wdt.c * * Watchdog driver for Intel IXP4xx network processors * * Author: Deepak Saxena + * Author: Linus Walleij * * Copyright 2004 (c) MontaVista, Software, Inc. * Based on sa1100 driver, Copyright (C) 2000 Oleg Drokin - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - #include -#include #include #include -#include -#include -#include #include -#include -#include -#include -#include +#include +#include +#include +#include + +struct ixp4xx_wdt { + struct watchdog_device wdd; + void __iomem *base; + unsigned long rate; +}; -static bool nowayout = WATCHDOG_NOWAYOUT; -static int heartbeat = 60; /* (secs) Default is 1 minute */ -static unsigned long wdt_status; -static unsigned long boot_status; -static DEFINE_SPINLOCK(wdt_lock); +/* Fallback if we do not have a clock for this */ +#define IXP4XX_TIMER_FREQ 66666000 -#define WDT_TICK_RATE (IXP4XX_PERIPHERAL_BUS_CLOCK * 1000000UL) +/* Registers after the timer registers */ +#define IXP4XX_OSWT_OFFSET 0x14 /* Watchdog Timer */ +#define IXP4XX_OSWE_OFFSET 0x18 /* Watchdog Enable */ +#define IXP4XX_OSWK_OFFSET 0x1C /* Watchdog Key */ +#define IXP4XX_OSST_OFFSET 0x20 /* Timer Status */ -#define WDT_IN_USE 0 -#define WDT_OK_TO_CLOSE 1 +#define IXP4XX_OSST_TIMER_WDOG_PEND 0x00000008 +#define IXP4XX_OSST_TIMER_WARM_RESET 0x00000010 +#define IXP4XX_WDT_KEY 0x0000482E +#define IXP4XX_WDT_RESET_ENABLE 0x00000001 +#define IXP4XX_WDT_IRQ_ENABLE 0x00000002 +#define IXP4XX_WDT_COUNT_ENABLE 0x00000004 -static void wdt_enable(void) +static inline +struct ixp4xx_wdt *to_ixp4xx_wdt(struct watchdog_device *wdd) { - spin_lock(&wdt_lock); - *IXP4XX_OSWK = IXP4XX_WDT_KEY; - *IXP4XX_OSWE = 0; - *IXP4XX_OSWT = WDT_TICK_RATE * heartbeat; - *IXP4XX_OSWE = IXP4XX_WDT_COUNT_ENABLE | IXP4XX_WDT_RESET_ENABLE; - *IXP4XX_OSWK = 0; - spin_unlock(&wdt_lock); + return container_of(wdd, struct ixp4xx_wdt, wdd); } -static void wdt_disable(void) +static int ixp4xx_wdt_start(struct watchdog_device *wdd) { - spin_lock(&wdt_lock); - *IXP4XX_OSWK = IXP4XX_WDT_KEY; - *IXP4XX_OSWE = 0; - *IXP4XX_OSWK = 0; - spin_unlock(&wdt_lock); -} + struct ixp4xx_wdt *iwdt = to_ixp4xx_wdt(wdd); -static int ixp4xx_wdt_open(struct inode *inode, struct file *file) -{ - if (test_and_set_bit(WDT_IN_USE, &wdt_status)) - return -EBUSY; + __raw_writel(IXP4XX_WDT_KEY, iwdt->base + IXP4XX_OSWK_OFFSET); + __raw_writel(0, iwdt->base + IXP4XX_OSWE_OFFSET); + __raw_writel(wdd->timeout * iwdt->rate, + iwdt->base + IXP4XX_OSWT_OFFSET); + __raw_writel(IXP4XX_WDT_COUNT_ENABLE | IXP4XX_WDT_RESET_ENABLE, + iwdt->base + IXP4XX_OSWE_OFFSET); + __raw_writel(0, iwdt->base + IXP4XX_OSWK_OFFSET); - clear_bit(WDT_OK_TO_CLOSE, &wdt_status); - wdt_enable(); - return stream_open(inode, file); + return 0; } -static ssize_t -ixp4xx_wdt_write(struct file *file, const char *data, size_t len, loff_t *ppos) +static int ixp4xx_wdt_stop(struct watchdog_device *wdd) { - 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; -} + struct ixp4xx_wdt *iwdt = to_ixp4xx_wdt(wdd); -static const struct watchdog_info ident = { - .options = WDIOF_CARDRESET | WDIOF_MAGICCLOSE | - WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, - .identity = "IXP4xx Watchdog", -}; + __raw_writel(IXP4XX_WDT_KEY, iwdt->base + IXP4XX_OSWK_OFFSET); + __raw_writel(0, iwdt->base + IXP4XX_OSWE_OFFSET); + __raw_writel(0, iwdt->base + IXP4XX_OSWK_OFFSET); - -static long ixp4xx_wdt_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) -{ - int ret = -ENOTTY; - int time; - - switch (cmd) { - case WDIOC_GETSUPPORT: - ret = copy_to_user((struct watchdog_info *)arg, &ident, - sizeof(ident)) ? -EFAULT : 0; - break; - - case WDIOC_GETSTATUS: - ret = put_user(0, (int *)arg); - break; - - case WDIOC_GETBOOTSTATUS: - ret = put_user(boot_status, (int *)arg); - break; - - case WDIOC_KEEPALIVE: - wdt_enable(); - ret = 0; - break; - - case WDIOC_SETTIMEOUT: - ret = get_user(time, (int *)arg); - if (ret) - break; - - if (time <= 0 || time > 60) { - ret = -EINVAL; - break; - } - - heartbeat = time; - wdt_enable(); - fallthrough; - - case WDIOC_GETTIMEOUT: - ret = put_user(heartbeat, (int *)arg); - break; - } - return ret; + return 0; } -static int ixp4xx_wdt_release(struct inode *inode, struct file *file) +static int ixp4xx_wdt_set_timeout(struct watchdog_device *wdd, + unsigned int timeout) { - if (test_bit(WDT_OK_TO_CLOSE, &wdt_status)) - wdt_disable(); - else - pr_crit("Device closed unexpectedly - timer will not stop\n"); - clear_bit(WDT_IN_USE, &wdt_status); - clear_bit(WDT_OK_TO_CLOSE, &wdt_status); + wdd->timeout = timeout; + if (watchdog_active(wdd)) + ixp4xx_wdt_start(wdd); return 0; } - -static const struct file_operations ixp4xx_wdt_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .write = ixp4xx_wdt_write, - .unlocked_ioctl = ixp4xx_wdt_ioctl, - .compat_ioctl = compat_ptr_ioctl, - .open = ixp4xx_wdt_open, - .release = ixp4xx_wdt_release, +static const struct watchdog_ops ixp4xx_wdt_ops = { + .start = ixp4xx_wdt_start, + .stop = ixp4xx_wdt_stop, + .set_timeout = ixp4xx_wdt_set_timeout, + .owner = THIS_MODULE, }; -static struct miscdevice ixp4xx_wdt_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &ixp4xx_wdt_fops, +static const struct watchdog_info ixp4xx_wdt_info = { + .options = WDIOF_KEEPALIVEPING + | WDIOF_MAGICCLOSE + | WDIOF_SETTIMEOUT, + .identity = KBUILD_MODNAME, }; -static int __init ixp4xx_wdt_init(void) +/* Devres-handled clock disablement */ +static void ixp4xx_clock_action(void *d) +{ + clk_disable_unprepare(d); +} + +static int ixp4xx_wdt_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; + struct ixp4xx_wdt *iwdt; + struct clk *clk; int ret; - /* - * FIXME: we bail out on device tree boot but this really needs - * to be fixed in a nicer way: this registers the MDIO bus before - * even matching the driver infrastructure, we should only probe - * detected hardware. - */ - if (of_have_populated_dt()) - return -ENODEV; if (!(read_cpuid_id() & 0xf) && !cpu_is_ixp46x()) { - pr_err("Rev. A0 IXP42x CPU detected - watchdog disabled\n"); - + dev_err(dev, "Rev. A0 IXP42x CPU detected - watchdog disabled\n"); return -ENODEV; } - boot_status = (*IXP4XX_OSST & IXP4XX_OSST_TIMER_WARM_RESET) ? - WDIOF_CARDRESET : 0; - ret = misc_register(&ixp4xx_wdt_miscdev); - if (ret == 0) - pr_info("timer heartbeat %d sec\n", heartbeat); - return ret; -} -static void __exit ixp4xx_wdt_exit(void) -{ - misc_deregister(&ixp4xx_wdt_miscdev); -} + iwdt = devm_kzalloc(dev, sizeof(*iwdt), GFP_KERNEL); + if (!iwdt) + return -ENOMEM; + iwdt->base = dev->platform_data; + /* + * Retrieve rate from a fixed clock from the device tree if + * the parent has that, else use the default clock rate. + */ + clk = devm_clk_get(dev->parent, NULL); + if (!IS_ERR(clk)) { + ret = clk_prepare_enable(clk); + if (ret) + return ret; + ret = devm_add_action_or_reset(dev, ixp4xx_clock_action, clk); + if (ret) + return ret; + iwdt->rate = clk_get_rate(clk); + } + if (!iwdt->rate) + iwdt->rate = IXP4XX_TIMER_FREQ; -module_init(ixp4xx_wdt_init); -module_exit(ixp4xx_wdt_exit); + iwdt->wdd.info = &ixp4xx_wdt_info; + iwdt->wdd.ops = &ixp4xx_wdt_ops; + iwdt->wdd.min_timeout = 1; + iwdt->wdd.max_timeout = U32_MAX / iwdt->rate; + iwdt->wdd.parent = dev; + /* Default to 60 seconds */ + iwdt->wdd.timeout = 60U; + watchdog_init_timeout(&iwdt->wdd, 0, dev); -MODULE_AUTHOR("Deepak Saxena "); -MODULE_DESCRIPTION("IXP4xx Network Processor Watchdog"); + if (__raw_readl(iwdt->base + IXP4XX_OSST_OFFSET) & + IXP4XX_OSST_TIMER_WARM_RESET) + iwdt->wdd.bootstatus = WDIOF_CARDRESET; + + ret = devm_watchdog_register_device(dev, &iwdt->wdd); + if (ret) + return ret; + + dev_info(dev, "IXP4xx watchdog available\n"); -module_param(heartbeat, int, 0); -MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds (default 60s)"); + return 0; +} -module_param(nowayout, bool, 0); -MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started"); +static struct platform_driver ixp4xx_wdt_driver = { + .probe = ixp4xx_wdt_probe, + .driver = { + .name = "ixp4xx-watchdog", + }, +}; +module_platform_driver(ixp4xx_wdt_driver); +MODULE_AUTHOR("Deepak Saxena "); +MODULE_DESCRIPTION("IXP4xx Network Processor Watchdog"); MODULE_LICENSE("GPL"); -- cgit From 41e73feb1024929e75eaf2f7cd93f35a3feb331b Mon Sep 17 00:00:00 2001 From: Sam Shih Date: Tue, 17 Aug 2021 15:45:55 +0800 Subject: dt-bindings: watchdog: Add compatible for Mediatek MT7986 This commit adds dt-binding documentation of watchdog for Mediatek MT7986 SoC Platform. Signed-off-by: Sam Shih Acked-by: Rob Herring Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20210726071439.14248-11-sam.shih@mediatek.com Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- Documentation/devicetree/bindings/watchdog/mtk-wdt.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/watchdog/mtk-wdt.txt b/Documentation/devicetree/bindings/watchdog/mtk-wdt.txt index 416d716403f6..a4e31ce96e0e 100644 --- a/Documentation/devicetree/bindings/watchdog/mtk-wdt.txt +++ b/Documentation/devicetree/bindings/watchdog/mtk-wdt.txt @@ -13,6 +13,7 @@ Required properties: "mediatek,mt7622-wdt", "mediatek,mt6589-wdt": for MT7622 "mediatek,mt7623-wdt", "mediatek,mt6589-wdt": for MT7623 "mediatek,mt7629-wdt", "mediatek,mt6589-wdt": for MT7629 + "mediatek,mt7986-wdt", "mediatek,mt6589-wdt": for MT7986 "mediatek,mt8183-wdt": for MT8183 "mediatek,mt8516-wdt", "mediatek,mt6589-wdt": for MT8516 "mediatek,mt8192-wdt": for MT8192 -- cgit