From 4d9186d01e7a6af1c02fcb639632fb97a8e140a6 Mon Sep 17 00:00:00 2001 From: Jerry Hoemann Date: Wed, 8 Aug 2018 13:13:23 -0600 Subject: watchdog: hpwdt: Initialize pretimeout from module parameter. When the pretimeout is specified as a module parameter, the value should be reflected in hpwdt_dev.pretimeout. The default (on) case is correct. But, when disabling pretimeout, the value should be set to zero in hpwdt_dev. When compiling w/o CONFIG_HPWDT_NMI_DECODING defined, the pretimeout module parameter is ignored and the value internally will be 0. Signed-off-by: Jerry Hoemann Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/hpwdt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/hpwdt.c b/drivers/watchdog/hpwdt.c index 9dc62a461451..fae93643beab 100644 --- a/drivers/watchdog/hpwdt.c +++ b/drivers/watchdog/hpwdt.c @@ -205,9 +205,7 @@ static struct watchdog_device hpwdt_dev = { .min_timeout = 1, .max_timeout = HPWDT_MAX_TIMER, .timeout = DEFAULT_MARGIN, -#ifdef CONFIG_HPWDT_NMI_DECODING .pretimeout = PRETIMEOUT_SEC, -#endif }; @@ -313,6 +311,8 @@ static int hpwdt_init_one(struct pci_dev *dev, if (watchdog_init_timeout(&hpwdt_dev, soft_margin, NULL)) dev_warn(&dev->dev, "Invalid soft_margin: %d.\n", soft_margin); + hpwdt_dev.pretimeout = pretimeout ? PRETIMEOUT_SEC : 0; + hpwdt_dev.parent = &dev->dev; retval = watchdog_register_device(&hpwdt_dev); if (retval < 0) { -- cgit v1.2.3 From 093d43858d1dec5114482daf02cc4901d46eb4d4 Mon Sep 17 00:00:00 2001 From: Jerry Hoemann Date: Wed, 8 Aug 2018 13:13:24 -0600 Subject: watchdog: hpwdt: Claim NMI from iLO The hwpdt driver is overloaded for handling both the iLO watchdog and the explicit "Generate NMI to System" virutal button. Hence NMI handler needs to claim NMI resulting from the virutal button. Claim if iLO generated accommodating firmware that might set wrong bit. Signed-off-by: Jerry Hoemann Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/hpwdt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/watchdog/hpwdt.c b/drivers/watchdog/hpwdt.c index fae93643beab..bb4171447a13 100644 --- a/drivers/watchdog/hpwdt.c +++ b/drivers/watchdog/hpwdt.c @@ -162,7 +162,7 @@ static int hpwdt_pretimeout(unsigned int ulReason, struct pt_regs *regs) if (ilo5 && ulReason == NMI_UNKNOWN && !mynmi) return NMI_DONE; - if (ilo5 && !pretimeout) + if (ilo5 && !pretimeout && !mynmi) return NMI_DONE; hpwdt_stop(); -- cgit v1.2.3 From 923014619e3d893c66bb398fd2ef19db7dc383ff Mon Sep 17 00:00:00 2001 From: Jerry Hoemann Date: Wed, 8 Aug 2018 13:13:25 -0600 Subject: watchdog: hpwdt: Display module parameters. Print module parameters when the driver is loaded. Signed-off-by: Jerry Hoemann Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/hpwdt.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/hpwdt.c b/drivers/watchdog/hpwdt.c index bb4171447a13..69a88b192dd8 100644 --- a/drivers/watchdog/hpwdt.c +++ b/drivers/watchdog/hpwdt.c @@ -320,9 +320,12 @@ static int hpwdt_init_one(struct pci_dev *dev, goto error_wd_register; } - dev_info(&dev->dev, "HPE Watchdog Timer Driver: %s" - ", timer margin: %d seconds (nowayout=%d).\n", - HPWDT_VERSION, hpwdt_dev.timeout, nowayout); + dev_info(&dev->dev, "HPE Watchdog Timer Driver: Version: %s\n", + HPWDT_VERSION); + dev_info(&dev->dev, "timeout: %d seconds (nowayout=%d)\n", + hpwdt_dev.timeout, nowayout); + dev_info(&dev->dev, "pretimeout: %s.\n", + pretimeout ? "on" : "off"); if (dev->subsystem_vendor == PCI_VENDOR_ID_HP_3PAR) ilo5 = true; -- cgit v1.2.3 From 397a35d418a20453952d707d4b97bae6ac3053ca Mon Sep 17 00:00:00 2001 From: Jerry Hoemann Date: Wed, 8 Aug 2018 13:13:26 -0600 Subject: watchdog: hpwdt: Module paramerter alias. Add module parameter "timeout" as an alias to "soft_margin." This aligns hpwdt usage more closely with other WDT while retaining backwards compatibility. Signed-off-by: Jerry Hoemann Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/hpwdt.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/watchdog/hpwdt.c b/drivers/watchdog/hpwdt.c index 69a88b192dd8..eb947bc25915 100644 --- a/drivers/watchdog/hpwdt.c +++ b/drivers/watchdog/hpwdt.c @@ -367,6 +367,9 @@ MODULE_VERSION(HPWDT_VERSION); module_param(soft_margin, int, 0); MODULE_PARM_DESC(soft_margin, "Watchdog timeout in seconds"); +module_param_named(timeout, soft_margin, int, 0); +MODULE_PARM_DESC(timeout, "Alias of soft_margin"); + module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); -- cgit v1.2.3 From e1c7f79ea54cac01d88e45f05a4c411cdb33e862 Mon Sep 17 00:00:00 2001 From: Jerry Hoemann Date: Wed, 8 Aug 2018 13:13:27 -0600 Subject: watchdog: hpwdt: Update version number. Bump version number to reflect recent bug fixes. Signed-off-by: Jerry Hoemann Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/hpwdt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/watchdog/hpwdt.c b/drivers/watchdog/hpwdt.c index eb947bc25915..7af358b3e278 100644 --- a/drivers/watchdog/hpwdt.c +++ b/drivers/watchdog/hpwdt.c @@ -26,7 +26,7 @@ #include #include -#define HPWDT_VERSION "2.0.0" +#define HPWDT_VERSION "2.0.1" #define SECS_TO_TICKS(secs) ((secs) * 1000 / 128) #define TICKS_TO_SECS(ticks) ((ticks) * 128 / 1000) #define HPWDT_MAX_TIMER TICKS_TO_SECS(65535) -- cgit v1.2.3 From 3bed02a2966146b5876b366f4489e30af0547c66 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 22 Aug 2018 00:02:24 +0200 Subject: watchdog: renesas_wdt: use SPDX identifier for Renesas drivers Signed-off-by: Wolfram Sang Reviewed-by: Simon Horman Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/renesas_wdt.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/renesas_wdt.c b/drivers/watchdog/renesas_wdt.c index 88d81feba4e6..84bb9d328180 100644 --- a/drivers/watchdog/renesas_wdt.c +++ b/drivers/watchdog/renesas_wdt.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Watchdog driver for Renesas WDT watchdog * * Copyright (C) 2015-17 Wolfram Sang, Sang Engineering * Copyright (C) 2015-17 Renesas Electronics Corporation - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. */ #include #include -- cgit v1.2.3 From 953b9dd7725bad55a922a35e75bff7bebf7b9978 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 28 Aug 2018 12:13:47 +0200 Subject: watchdog: core: fix null pointer dereference when releasing cdev watchdog_stop() calls watchdog_update_worker() which needs a valid wdd->wd_data pointer. So, when unregistering the cdev, clear the pointers after we call watchdog_stop(), not before. Fixes: bb292ac1c602 ("watchdog: Introduce watchdog_stop_on_unregister helper") Signed-off-by: Wolfram Sang Reviewed-by: Fabrizio Castro Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/watchdog_dev.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c index ffbdc4642ea5..f6c24b22b37c 100644 --- a/drivers/watchdog/watchdog_dev.c +++ b/drivers/watchdog/watchdog_dev.c @@ -1019,16 +1019,16 @@ static void watchdog_cdev_unregister(struct watchdog_device *wdd) old_wd_data = NULL; } - mutex_lock(&wd_data->lock); - wd_data->wdd = NULL; - wdd->wd_data = NULL; - mutex_unlock(&wd_data->lock); - if (watchdog_active(wdd) && test_bit(WDOG_STOP_ON_UNREGISTER, &wdd->status)) { watchdog_stop(wdd); } + mutex_lock(&wd_data->lock); + wd_data->wdd = NULL; + wdd->wd_data = NULL; + mutex_unlock(&wd_data->lock); + hrtimer_cancel(&wd_data->timer); kthread_cancel_work_sync(&wd_data->work); -- cgit v1.2.3 From 14de99b44b34dbb9d0f64845b1cbb675e047767e Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 28 Aug 2018 12:13:48 +0200 Subject: watchdog: renesas_wdt: stop when unregistering We want to go into a sane state when unregistering. Currently, it happens that the watchdog stops when unbinding because of RuntimePM stopping the core clock. When rebinding, the core clock gets reactivated and the watchdog fires even though it hasn't been opened by userspace yet. Strange scenario, yes, but sane state is much preferred anyhow. Signed-off-by: Wolfram Sang Reviewed-by: Fabrizio Castro Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/renesas_wdt.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/watchdog/renesas_wdt.c b/drivers/watchdog/renesas_wdt.c index 84bb9d328180..0d74c3e48979 100644 --- a/drivers/watchdog/renesas_wdt.c +++ b/drivers/watchdog/renesas_wdt.c @@ -231,6 +231,7 @@ static int rwdt_probe(struct platform_device *pdev) watchdog_set_drvdata(&priv->wdev, priv); watchdog_set_nowayout(&priv->wdev, nowayout); watchdog_set_restart_priority(&priv->wdev, 0); + watchdog_stop_on_unregister(&priv->wdev); /* This overrides the default timeout only if DT configuration was found */ ret = watchdog_init_timeout(&priv->wdev, 0, &pdev->dev); -- cgit v1.2.3 From 2c05318ad5c390e65d6c819debeff3dfa262223e Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 31 Aug 2018 15:06:10 +0200 Subject: watchdog: iTCO_wdt: Drop option vendorsupport=2 iTCO_vendor_support's option vendorsupport=2 is a gross hack. It claims to extend the existing iTCO_wdt driver, but in fact programs a watchdog on a completely different device (Super-I/O) without requesting its I/O ports and without checking the device ID. This is an utterly dangerous thing to do. It also turns out to be unnecessary, because as far as I can tell, the code in question is basically duplicating what the clean w83627hf_wdt driver is doing. My guess is that on these SuperMicro boards which sparkled the implementation of vendorsupport=2, the watchdog functionality of the Intel south bridge is not used, and the watchdog feature of some W83627HF-like Super-I/O chip is used instead. So we should point the users to the w83627hf_wdt driver. Signed-off-by: Jean Delvare Cc: Martin Wilck Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/iTCO_vendor_support.c | 158 ++------------------------------- 1 file changed, 8 insertions(+), 150 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/iTCO_vendor_support.c b/drivers/watchdog/iTCO_vendor_support.c index b6b2f90b5d44..d714a06de57c 100644 --- a/drivers/watchdog/iTCO_vendor_support.c +++ b/drivers/watchdog/iTCO_vendor_support.c @@ -38,7 +38,7 @@ /* List of vendor support modes */ /* SuperMicro Pentium 3 Era 370SSE+-OEM1/P3TSSE */ #define SUPERMICRO_OLD_BOARD 1 -/* SuperMicro Pentium 4 / Xeon 4 / EMT64T Era Systems */ +/* SuperMicro Pentium 4 / Xeon 4 / EMT64T Era Systems - no longer supported */ #define SUPERMICRO_NEW_BOARD 2 /* Broken BIOS */ #define BROKEN_BIOS 911 @@ -46,8 +46,7 @@ static int vendorsupport; module_param(vendorsupport, int, 0); MODULE_PARM_DESC(vendorsupport, "iTCO vendor specific support mode, default=" - "0 (none), 1=SuperMicro Pent3, 2=SuperMicro Pent4+, " - "911=Broken SMI BIOS"); + "0 (none), 1=SuperMicro Pent3, 911=Broken SMI BIOS"); /* * Vendor Specific Support @@ -97,143 +96,6 @@ static void supermicro_old_pre_stop(struct resource *smires) outl(val32, smires->start); /* Needed to deactivate watchdog */ } -/* - * Vendor Support: 2 - * Board: Super Micro Computer Inc. P4SBx, P4DPx - * iTCO chipset: ICH4 - * - * Code contributed by: R. Seretny - * Documentation obtained by R. Seretny from SuperMicro Technical Support - * - * To enable Watchdog function: - * 1. BIOS - * For P4SBx: - * BIOS setup -> Advanced -> Integrated Peripherals -> Watch Dog Feature - * For P4DPx: - * BIOS setup -> Advanced -> I/O Device Configuration -> Watch Dog - * This setting enables or disables Watchdog function. When enabled, the - * default watchdog timer is set to be 5 minutes (about 4m35s). It is - * enough to load and run the OS. The application (service or driver) has - * to take over the control once OS is running up and before watchdog - * expires. - * - * 2. JUMPER - * For P4SBx: JP39 - * For P4DPx: JP37 - * This jumper is used for safety. Closed is enabled. This jumper - * prevents user enables watchdog in BIOS by accident. - * - * To enable Watch Dog function, both BIOS and JUMPER must be enabled. - * - * The documentation lists motherboards P4SBx and P4DPx series as of - * 20-March-2002. However, this code works flawlessly with much newer - * motherboards, such as my X6DHR-8G2 (SuperServer 6014H-82). - * - * The original iTCO driver as written does not actually reset the - * watchdog timer on these machines, as a result they reboot after five - * minutes. - * - * NOTE: You may leave the Watchdog function disabled in the SuperMicro - * BIOS to avoid a "boot-race"... This driver will enable watchdog - * functionality even if it's disabled in the BIOS once the /dev/watchdog - * file is opened. - */ - -/* I/O Port's */ -#define SM_REGINDEX 0x2e /* SuperMicro ICH4+ Register Index */ -#define SM_DATAIO 0x2f /* SuperMicro ICH4+ Register Data I/O */ - -/* Control Register's */ -#define SM_CTLPAGESW 0x07 /* SuperMicro ICH4+ Control Page Switch */ -#define SM_CTLPAGE 0x08 /* SuperMicro ICH4+ Control Page Num */ - -#define SM_WATCHENABLE 0x30 /* Watchdog enable: Bit 0: 0=off, 1=on */ - -#define SM_WATCHPAGE 0x87 /* Watchdog unlock control page */ - -#define SM_ENDWATCH 0xAA /* Watchdog lock control page */ - -#define SM_COUNTMODE 0xf5 /* Watchdog count mode select */ - /* (Bit 3: 0 = seconds, 1 = minutes */ - -#define SM_WATCHTIMER 0xf6 /* 8-bits, Watchdog timer counter (RW) */ - -#define SM_RESETCONTROL 0xf7 /* Watchdog reset control */ - /* Bit 6: timer is reset by kbd interrupt */ - /* Bit 7: timer is reset by mouse interrupt */ - -static void supermicro_new_unlock_watchdog(void) -{ - /* Write 0x87 to port 0x2e twice */ - outb(SM_WATCHPAGE, SM_REGINDEX); - outb(SM_WATCHPAGE, SM_REGINDEX); - /* Switch to watchdog control page */ - outb(SM_CTLPAGESW, SM_REGINDEX); - outb(SM_CTLPAGE, SM_DATAIO); -} - -static void supermicro_new_lock_watchdog(void) -{ - outb(SM_ENDWATCH, SM_REGINDEX); -} - -static void supermicro_new_pre_start(unsigned int heartbeat) -{ - unsigned int val; - - supermicro_new_unlock_watchdog(); - - /* Watchdog timer setting needs to be in seconds*/ - outb(SM_COUNTMODE, SM_REGINDEX); - val = inb(SM_DATAIO); - val &= 0xF7; - outb(val, SM_DATAIO); - - /* Write heartbeat interval to WDOG */ - outb(SM_WATCHTIMER, SM_REGINDEX); - outb((heartbeat & 255), SM_DATAIO); - - /* Make sure keyboard/mouse interrupts don't interfere */ - outb(SM_RESETCONTROL, SM_REGINDEX); - val = inb(SM_DATAIO); - val &= 0x3f; - outb(val, SM_DATAIO); - - /* enable watchdog by setting bit 0 of Watchdog Enable to 1 */ - outb(SM_WATCHENABLE, SM_REGINDEX); - val = inb(SM_DATAIO); - val |= 0x01; - outb(val, SM_DATAIO); - - supermicro_new_lock_watchdog(); -} - -static void supermicro_new_pre_stop(void) -{ - unsigned int val; - - supermicro_new_unlock_watchdog(); - - /* disable watchdog by setting bit 0 of Watchdog Enable to 0 */ - outb(SM_WATCHENABLE, SM_REGINDEX); - val = inb(SM_DATAIO); - val &= 0xFE; - outb(val, SM_DATAIO); - - supermicro_new_lock_watchdog(); -} - -static void supermicro_new_pre_set_heartbeat(unsigned int heartbeat) -{ - supermicro_new_unlock_watchdog(); - - /* reset watchdog timeout to heartveat value */ - outb(SM_WATCHTIMER, SM_REGINDEX); - outb((heartbeat & 255), SM_DATAIO); - - supermicro_new_lock_watchdog(); -} - /* * Vendor Support: 911 * Board: Some Intel ICHx based motherboards @@ -298,9 +160,6 @@ void iTCO_vendor_pre_start(struct resource *smires, case SUPERMICRO_OLD_BOARD: supermicro_old_pre_start(smires); break; - case SUPERMICRO_NEW_BOARD: - supermicro_new_pre_start(heartbeat); - break; case BROKEN_BIOS: broken_bios_start(smires); break; @@ -314,9 +173,6 @@ void iTCO_vendor_pre_stop(struct resource *smires) case SUPERMICRO_OLD_BOARD: supermicro_old_pre_stop(smires); break; - case SUPERMICRO_NEW_BOARD: - supermicro_new_pre_stop(); - break; case BROKEN_BIOS: broken_bios_stop(smires); break; @@ -326,15 +182,11 @@ EXPORT_SYMBOL(iTCO_vendor_pre_stop); void iTCO_vendor_pre_keepalive(struct resource *smires, unsigned int heartbeat) { - if (vendorsupport == SUPERMICRO_NEW_BOARD) - supermicro_new_pre_set_heartbeat(heartbeat); } EXPORT_SYMBOL(iTCO_vendor_pre_keepalive); void iTCO_vendor_pre_set_heartbeat(unsigned int heartbeat) { - if (vendorsupport == SUPERMICRO_NEW_BOARD) - supermicro_new_pre_set_heartbeat(heartbeat); } EXPORT_SYMBOL(iTCO_vendor_pre_set_heartbeat); @@ -351,6 +203,12 @@ EXPORT_SYMBOL(iTCO_vendor_check_noreboot_on); static int __init iTCO_vendor_init_module(void) { + if (vendorsupport == SUPERMICRO_NEW_BOARD) { + pr_warn("Option vendorsupport=%d is no longer supported, " + "please use the w83627hf_wdt driver instead\n", + SUPERMICRO_NEW_BOARD); + return -EINVAL; + } pr_info("vendor-support=%d\n", vendorsupport); return 0; } -- cgit v1.2.3 From 5a623ce0a55ed32fd999a80d0423cfae782893dc Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 31 Aug 2018 15:07:34 +0200 Subject: watchdog: iTCO_wdt: Remove unused hooks As the only user of iTCO_vendor_pre_keepalive and iTCO_vendor_pre_set_heartbeat has just been removed, we can delete these 2 hooks. Signed-off-by: Jean Delvare Cc: Martin Wilck Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/iTCO_vendor.h | 4 ---- drivers/watchdog/iTCO_vendor_support.c | 10 ---------- drivers/watchdog/iTCO_wdt.c | 4 ---- 3 files changed, 18 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/iTCO_vendor.h b/drivers/watchdog/iTCO_vendor.h index 7b82a7c6e7c3..0f7373ba10d5 100644 --- a/drivers/watchdog/iTCO_vendor.h +++ b/drivers/watchdog/iTCO_vendor.h @@ -3,14 +3,10 @@ #ifdef CONFIG_ITCO_VENDOR_SUPPORT extern void iTCO_vendor_pre_start(struct resource *, unsigned int); extern void iTCO_vendor_pre_stop(struct resource *); -extern void iTCO_vendor_pre_keepalive(struct resource *, unsigned int); -extern void iTCO_vendor_pre_set_heartbeat(unsigned int); extern int iTCO_vendor_check_noreboot_on(void); #else #define iTCO_vendor_pre_start(acpibase, heartbeat) {} #define iTCO_vendor_pre_stop(acpibase) {} -#define iTCO_vendor_pre_keepalive(acpibase, heartbeat) {} -#define iTCO_vendor_pre_set_heartbeat(heartbeat) {} #define iTCO_vendor_check_noreboot_on() 1 /* 1=check noreboot; 0=don't check */ #endif diff --git a/drivers/watchdog/iTCO_vendor_support.c b/drivers/watchdog/iTCO_vendor_support.c index d714a06de57c..68a9d9cc2eb8 100644 --- a/drivers/watchdog/iTCO_vendor_support.c +++ b/drivers/watchdog/iTCO_vendor_support.c @@ -180,16 +180,6 @@ void iTCO_vendor_pre_stop(struct resource *smires) } EXPORT_SYMBOL(iTCO_vendor_pre_stop); -void iTCO_vendor_pre_keepalive(struct resource *smires, unsigned int heartbeat) -{ -} -EXPORT_SYMBOL(iTCO_vendor_pre_keepalive); - -void iTCO_vendor_pre_set_heartbeat(unsigned int heartbeat) -{ -} -EXPORT_SYMBOL(iTCO_vendor_pre_set_heartbeat); - int iTCO_vendor_check_noreboot_on(void) { switch (vendorsupport) { diff --git a/drivers/watchdog/iTCO_wdt.c b/drivers/watchdog/iTCO_wdt.c index 347f0389b089..0a5318b7865e 100644 --- a/drivers/watchdog/iTCO_wdt.c +++ b/drivers/watchdog/iTCO_wdt.c @@ -304,8 +304,6 @@ static int iTCO_wdt_ping(struct watchdog_device *wd_dev) spin_lock(&p->io_lock); - iTCO_vendor_pre_keepalive(p->smi_res, wd_dev->timeout); - /* Reload the timer by writing to the TCO Timer Counter register */ if (p->iTCO_version >= 2) { outw(0x01, TCO_RLD(p)); @@ -342,8 +340,6 @@ static int iTCO_wdt_set_timeout(struct watchdog_device *wd_dev, unsigned int t) (p->iTCO_version == 1 && tmrval > 0x03f)) return -EINVAL; - iTCO_vendor_pre_set_heartbeat(tmrval); - /* Write new heartbeat to watchdog */ if (p->iTCO_version >= 2) { spin_lock(&p->io_lock); -- cgit v1.2.3 From d674ee232b2d9480033df9107a04b4b5436040e6 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Fri, 7 Sep 2018 02:11:17 +0000 Subject: watchdog: rza_wdt: convert to SPDX identifiers This patch updates license to use SPDX-License-Identifier instead of verbose license text. Signed-off-by: Kuninori Morimoto Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/rza_wdt.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/rza_wdt.c b/drivers/watchdog/rza_wdt.c index e618218d2374..c63ef03e24f6 100644 --- a/drivers/watchdog/rza_wdt.c +++ b/drivers/watchdog/rza_wdt.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Renesas RZ/A Series WDT Driver * * Copyright (C) 2017 Renesas Electronics America, Inc. * Copyright (C) 2017 Chris Brandt - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. */ #include -- cgit v1.2.3 From d08ec7bea2795be008736a852e67949c162669ed Mon Sep 17 00:00:00 2001 From: "Robert P. J. Day" Date: Sat, 8 Sep 2018 06:20:05 -0400 Subject: watchdog: fix a small number of "watchog" typos in comments All typos in comments, should not affect functionality. Signed-off-by: Robert P. J. Day Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/booke_wdt.c | 2 +- drivers/watchdog/via_wdt.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/booke_wdt.c b/drivers/watchdog/booke_wdt.c index 3ad1e44bef44..6e78a34c2370 100644 --- a/drivers/watchdog/booke_wdt.c +++ b/drivers/watchdog/booke_wdt.c @@ -25,7 +25,7 @@ /* If the kernel parameter wdt=1, the watchdog will be enabled at boot. * Also, the wdt_period sets the watchdog timer period timeout. * For E500 cpus the wdt_period sets which bit changing from 0->1 will - * trigger a watchog timeout. This watchdog timeout will occur 3 times, the + * trigger a watchdog timeout. This watchdog timeout will occur 3 times, the * first time nothing will happen, the second time a watchdog exception will * occur, and the final time the board will reset. */ diff --git a/drivers/watchdog/via_wdt.c b/drivers/watchdog/via_wdt.c index b085ef1084ec..d56d0a927a1e 100644 --- a/drivers/watchdog/via_wdt.c +++ b/drivers/watchdog/via_wdt.c @@ -30,7 +30,7 @@ #define VIA_WDT_CONF_MMIO 0x02 /* 1: enable watchdog MMIO */ /* - * The MMIO region contains the watchog control register and the + * The MMIO region contains the watchdog control register and the * hardware timer counter. */ #define VIA_WDT_MMIO_LEN 8 /* MMIO region length in bytes */ @@ -82,7 +82,7 @@ static inline void wdt_reset(void) /* * Timer tick: the timer will make sure that the watchdog timer hardware * is being reset in time. The conditions to do this are: - * 1) the watchog timer has been started and /dev/watchdog is open + * 1) the watchdog timer has been started and /dev/watchdog is open * and there is still time left before userspace should send the * next heartbeat/ping. (note: the internal heartbeat is much smaller * then the external/userspace heartbeat). -- cgit v1.2.3 From 2e0432f8f8ad11b4bd208445360220efa5b37d82 Mon Sep 17 00:00:00 2001 From: Romain Izard Date: Fri, 14 Sep 2018 12:13:38 +0200 Subject: watchdog: sama5d4: fix timeout-sec usage When using watchdog_init_timeout to update the default timeout value, an error means that there is no "timeout-sec" in the relevant device tree node. This should not prevent binding of the driver to the device. Fixes: 976932e40036 ("watchdog: sama5d4: make use of timeout-secs provided in devicetree") Signed-off-by: Romain Izard Reviewed-by: Marcus Folkesson Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/sama5d4_wdt.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/sama5d4_wdt.c b/drivers/watchdog/sama5d4_wdt.c index 255169916dbb..1e93c1b0e3cf 100644 --- a/drivers/watchdog/sama5d4_wdt.c +++ b/drivers/watchdog/sama5d4_wdt.c @@ -247,11 +247,7 @@ static int sama5d4_wdt_probe(struct platform_device *pdev) } } - ret = watchdog_init_timeout(wdd, wdt_timeout, &pdev->dev); - if (ret) { - dev_err(&pdev->dev, "unable to set timeout value\n"); - return ret; - } + watchdog_init_timeout(wdd, wdt_timeout, &pdev->dev); timeout = WDT_SEC2TICKS(wdd->timeout); -- cgit v1.2.3 From 1f59f8aff98f200af7a6882184add7b85f5da741 Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Thu, 13 Sep 2018 23:32:09 +0200 Subject: watchdog: lantiq: update register names to better match spec Some of the names of the bits were confusing to me. Now the bits share the same prefix as the register they are set on. The LTQ_WDT_CR_PWL register (bits 26:25) is the pre warning limit and it does not turn anything on. It has 4 possible divers 1/2, 1/4, 1/8 and 1/16, this drivers only uses 1/16. The LTQ_WDT_CR_CLKDIV register bits(25:24) is only configuring a clock divers and do not turn any thing on too, all possible values are valid dividers. Using the LTQ_WDT_SR prefix is also wrong these bits are used in the LTQ_WDT_CR registers, SR is the status register which is read only. This uses GENMASK where it is a mask and it uses shifts when a value is written to some bits. Signed-off-by: Hauke Mehrtens Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/lantiq_wdt.c | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/lantiq_wdt.c b/drivers/watchdog/lantiq_wdt.c index 7f43cefa0eae..a086005fbaac 100644 --- a/drivers/watchdog/lantiq_wdt.c +++ b/drivers/watchdog/lantiq_wdt.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -40,18 +41,19 @@ * essentially the following two magic passwords need to be written to allow * IO access to the WDT core */ -#define LTQ_WDT_PW1 0x00BE0000 -#define LTQ_WDT_PW2 0x00DC0000 +#define LTQ_WDT_CR_PW1 0x00BE0000 +#define LTQ_WDT_CR_PW2 0x00DC0000 + +#define LTQ_WDT_CR 0x0 /* watchdog control register */ +#define LTQ_WDT_CR_GEN BIT(31) /* enable bit */ +/* Pre-warning limit set to 1/16 of max WDT period */ +#define LTQ_WDT_CR_PWL (0x3 << 26) +/* set clock divider to 0x40000 */ +#define LTQ_WDT_CR_CLKDIV (0x3 << 24) +#define LTQ_WDT_CR_PW_MASK GENMASK(23, 16) /* Password field */ +#define LTQ_WDT_CR_MAX_TIMEOUT ((1 << 16) - 1) /* The reload field is 16 bit */ -#define LTQ_WDT_CR 0x0 /* watchdog control register */ -#define LTQ_WDT_SR 0x8 /* watchdog status register */ - -#define LTQ_WDT_SR_EN (0x1 << 31) /* enable bit */ -#define LTQ_WDT_SR_PWD (0x3 << 26) /* turn on power */ -#define LTQ_WDT_SR_CLKDIV (0x3 << 24) /* turn on clock and set */ - /* divider to 0x40000 */ #define LTQ_WDT_DIVIDER 0x40000 -#define LTQ_MAX_TIMEOUT ((1 << 16) - 1) /* the reload field is 16 bit */ static bool nowayout = WATCHDOG_NOWAYOUT; @@ -68,26 +70,26 @@ ltq_wdt_enable(void) { unsigned long int timeout = ltq_wdt_timeout * (ltq_io_region_clk_rate / LTQ_WDT_DIVIDER) + 0x1000; - if (timeout > LTQ_MAX_TIMEOUT) - timeout = LTQ_MAX_TIMEOUT; + if (timeout > LTQ_WDT_CR_MAX_TIMEOUT) + timeout = LTQ_WDT_CR_MAX_TIMEOUT; /* write the first password magic */ - ltq_w32(LTQ_WDT_PW1, ltq_wdt_membase + LTQ_WDT_CR); + ltq_w32(LTQ_WDT_CR_PW1, ltq_wdt_membase + LTQ_WDT_CR); /* write the second magic plus the configuration and new timeout */ - ltq_w32(LTQ_WDT_SR_EN | LTQ_WDT_SR_PWD | LTQ_WDT_SR_CLKDIV | - LTQ_WDT_PW2 | timeout, ltq_wdt_membase + LTQ_WDT_CR); + ltq_w32(LTQ_WDT_CR_GEN | LTQ_WDT_CR_PWL | LTQ_WDT_CR_CLKDIV | + LTQ_WDT_CR_PW2 | timeout, ltq_wdt_membase + LTQ_WDT_CR); } static void ltq_wdt_disable(void) { /* write the first password magic */ - ltq_w32(LTQ_WDT_PW1, ltq_wdt_membase + LTQ_WDT_CR); + ltq_w32(LTQ_WDT_CR_PW1, ltq_wdt_membase + LTQ_WDT_CR); /* * write the second password magic with no config * this turns the watchdog off */ - ltq_w32(LTQ_WDT_PW2, ltq_wdt_membase + LTQ_WDT_CR); + ltq_w32(LTQ_WDT_CR_PW2, ltq_wdt_membase + LTQ_WDT_CR); } static ssize_t -- cgit v1.2.3 From dcd7e04e8e2f5f887dd9692f5ff3e0d0c26c8e3f Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Thu, 13 Sep 2018 23:32:10 +0200 Subject: watchdog: lantiq: Convert to watchdog_device Instead of doing the ioctl handling manually just use register a watchdog_device and let the watchdog framework do the ioctl handling. This also removes the ltq_wdt_bootstatus_set typedef and replaces it with a structure providing the chip specific functions pointer. The watchdog_init_timeout() function is now used and the initial timeout can be provided in device tree. If the watchdog was already activated it will not be stopped any more, but the settings from the driver will be used and the watchdog subsystem will take care. Signed-off-by: Hauke Mehrtens Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/Kconfig | 1 + drivers/watchdog/lantiq_wdt.c | 278 +++++++++++++++++++----------------------- 2 files changed, 125 insertions(+), 154 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 5ea8909a41f9..5637d8be31e0 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -1621,6 +1621,7 @@ config IMGPDC_WDT config LANTIQ_WDT tristate "Lantiq SoC watchdog" depends on LANTIQ + select WATCHDOG_CORE help Hardware driver for the Lantiq SoC Watchdog Timer. diff --git a/drivers/watchdog/lantiq_wdt.c b/drivers/watchdog/lantiq_wdt.c index a086005fbaac..c2f14d2bd695 100644 --- a/drivers/watchdog/lantiq_wdt.c +++ b/drivers/watchdog/lantiq_wdt.c @@ -8,11 +8,7 @@ * Based on EP93xx wdt driver */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - #include -#include -#include #include #include #include @@ -52,159 +48,106 @@ #define LTQ_WDT_CR_CLKDIV (0x3 << 24) #define LTQ_WDT_CR_PW_MASK GENMASK(23, 16) /* Password field */ #define LTQ_WDT_CR_MAX_TIMEOUT ((1 << 16) - 1) /* The reload field is 16 bit */ +#define LTQ_WDT_SR 0x8 /* watchdog status register */ +#define LTQ_WDT_SR_EN BIT(31) /* Enable */ #define LTQ_WDT_DIVIDER 0x40000 static bool nowayout = WATCHDOG_NOWAYOUT; -static void __iomem *ltq_wdt_membase; -static unsigned long ltq_io_region_clk_rate; +struct ltq_wdt_hw { + int (*bootstatus_get)(struct device *dev); +}; -static unsigned long ltq_wdt_bootstatus; -static unsigned long ltq_wdt_in_use; -static int ltq_wdt_timeout = 30; -static int ltq_wdt_ok_to_close; +struct ltq_wdt_priv { + struct watchdog_device wdt; + void __iomem *membase; + unsigned long clk_rate; +}; -static void -ltq_wdt_enable(void) +static u32 ltq_wdt_r32(struct ltq_wdt_priv *priv, u32 offset) { - unsigned long int timeout = ltq_wdt_timeout * - (ltq_io_region_clk_rate / LTQ_WDT_DIVIDER) + 0x1000; - if (timeout > LTQ_WDT_CR_MAX_TIMEOUT) - timeout = LTQ_WDT_CR_MAX_TIMEOUT; - - /* write the first password magic */ - ltq_w32(LTQ_WDT_CR_PW1, ltq_wdt_membase + LTQ_WDT_CR); - /* write the second magic plus the configuration and new timeout */ - ltq_w32(LTQ_WDT_CR_GEN | LTQ_WDT_CR_PWL | LTQ_WDT_CR_CLKDIV | - LTQ_WDT_CR_PW2 | timeout, ltq_wdt_membase + LTQ_WDT_CR); + return __raw_readl(priv->membase + offset); } -static void -ltq_wdt_disable(void) +static void ltq_wdt_w32(struct ltq_wdt_priv *priv, u32 val, u32 offset) { - /* write the first password magic */ - ltq_w32(LTQ_WDT_CR_PW1, ltq_wdt_membase + LTQ_WDT_CR); - /* - * write the second password magic with no config - * this turns the watchdog off - */ - ltq_w32(LTQ_WDT_CR_PW2, ltq_wdt_membase + LTQ_WDT_CR); + __raw_writel(val, priv->membase + offset); } -static ssize_t -ltq_wdt_write(struct file *file, const char __user *data, - size_t len, loff_t *ppos) +static void ltq_wdt_mask(struct ltq_wdt_priv *priv, u32 clear, u32 set, + u32 offset) { - if (len) { - if (!nowayout) { - size_t i; - - ltq_wdt_ok_to_close = 0; - for (i = 0; i != len; i++) { - char c; - - if (get_user(c, data + i)) - return -EFAULT; - if (c == 'V') - ltq_wdt_ok_to_close = 1; - else - ltq_wdt_ok_to_close = 0; - } - } - ltq_wdt_enable(); - } + u32 val = ltq_wdt_r32(priv, offset); + + val &= ~(clear); + val |= set; + ltq_wdt_w32(priv, val, offset); +} - return len; +static struct ltq_wdt_priv *ltq_wdt_get_priv(struct watchdog_device *wdt) +{ + return container_of(wdt, struct ltq_wdt_priv, wdt); } -static struct watchdog_info ident = { +static struct watchdog_info ltq_wdt_info = { .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | - WDIOF_CARDRESET, + WDIOF_CARDRESET, .identity = "ltq_wdt", }; -static long -ltq_wdt_ioctl(struct file *file, - unsigned int cmd, unsigned long arg) +static int ltq_wdt_start(struct watchdog_device *wdt) { - int ret = -ENOTTY; - - switch (cmd) { - case WDIOC_GETSUPPORT: - ret = copy_to_user((struct watchdog_info __user *)arg, &ident, - sizeof(ident)) ? -EFAULT : 0; - break; - - case WDIOC_GETBOOTSTATUS: - ret = put_user(ltq_wdt_bootstatus, (int __user *)arg); - break; - - case WDIOC_GETSTATUS: - ret = put_user(0, (int __user *)arg); - break; - - case WDIOC_SETTIMEOUT: - ret = get_user(ltq_wdt_timeout, (int __user *)arg); - if (!ret) - ltq_wdt_enable(); - /* intentional drop through */ - case WDIOC_GETTIMEOUT: - ret = put_user(ltq_wdt_timeout, (int __user *)arg); - break; - - case WDIOC_KEEPALIVE: - ltq_wdt_enable(); - ret = 0; - break; - } - return ret; + struct ltq_wdt_priv *priv = ltq_wdt_get_priv(wdt); + u32 timeout; + + timeout = wdt->timeout * priv->clk_rate; + + ltq_wdt_mask(priv, LTQ_WDT_CR_PW_MASK, LTQ_WDT_CR_PW1, LTQ_WDT_CR); + /* write the second magic plus the configuration and new timeout */ + ltq_wdt_mask(priv, LTQ_WDT_CR_PW_MASK | LTQ_WDT_CR_MAX_TIMEOUT, + LTQ_WDT_CR_GEN | LTQ_WDT_CR_PWL | LTQ_WDT_CR_CLKDIV | + LTQ_WDT_CR_PW2 | timeout, + LTQ_WDT_CR); + + return 0; } -static int -ltq_wdt_open(struct inode *inode, struct file *file) +static int ltq_wdt_stop(struct watchdog_device *wdt) { - if (test_and_set_bit(0, <q_wdt_in_use)) - return -EBUSY; - ltq_wdt_in_use = 1; - ltq_wdt_enable(); + struct ltq_wdt_priv *priv = ltq_wdt_get_priv(wdt); + + ltq_wdt_mask(priv, LTQ_WDT_CR_PW_MASK, LTQ_WDT_CR_PW1, LTQ_WDT_CR); + ltq_wdt_mask(priv, LTQ_WDT_CR_GEN | LTQ_WDT_CR_PW_MASK, + LTQ_WDT_CR_PW2, LTQ_WDT_CR); - return nonseekable_open(inode, file); + return 0; } -static int -ltq_wdt_release(struct inode *inode, struct file *file) +static int ltq_wdt_ping(struct watchdog_device *wdt) { - if (ltq_wdt_ok_to_close) - ltq_wdt_disable(); - else - pr_err("watchdog closed without warning\n"); - ltq_wdt_ok_to_close = 0; - clear_bit(0, <q_wdt_in_use); + struct ltq_wdt_priv *priv = ltq_wdt_get_priv(wdt); + u32 timeout; + + timeout = wdt->timeout * priv->clk_rate; + + ltq_wdt_mask(priv, LTQ_WDT_CR_PW_MASK, LTQ_WDT_CR_PW1, LTQ_WDT_CR); + /* write the second magic plus the configuration and new timeout */ + ltq_wdt_mask(priv, LTQ_WDT_CR_PW_MASK | LTQ_WDT_CR_MAX_TIMEOUT, + LTQ_WDT_CR_PW2 | timeout, LTQ_WDT_CR); return 0; } -static const struct file_operations ltq_wdt_fops = { +static const struct watchdog_ops ltq_wdt_ops = { .owner = THIS_MODULE, - .write = ltq_wdt_write, - .unlocked_ioctl = ltq_wdt_ioctl, - .open = ltq_wdt_open, - .release = ltq_wdt_release, - .llseek = no_llseek, + .start = ltq_wdt_start, + .stop = ltq_wdt_stop, + .ping = ltq_wdt_ping, }; -static struct miscdevice ltq_wdt_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = <q_wdt_fops, -}; - -typedef int (*ltq_wdt_bootstatus_set)(struct platform_device *pdev); - -static int ltq_wdt_bootstatus_xrx(struct platform_device *pdev) +static int ltq_wdt_xrx_bootstatus_get(struct device *dev) { - struct device *dev = &pdev->dev; struct regmap *rcu_regmap; u32 val; int err; @@ -218,14 +161,13 @@ static int ltq_wdt_bootstatus_xrx(struct platform_device *pdev) return err; if (val & LTQ_XRX_RCU_RST_STAT_WDT) - ltq_wdt_bootstatus = WDIOF_CARDRESET; + return WDIOF_CARDRESET; return 0; } -static int ltq_wdt_bootstatus_falcon(struct platform_device *pdev) +static int ltq_wdt_falcon_bootstatus_get(struct device *dev) { - struct device *dev = &pdev->dev; struct regmap *rcu_regmap; u32 val; int err; @@ -240,62 +182,90 @@ static int ltq_wdt_bootstatus_falcon(struct platform_device *pdev) return err; if ((val & LTQ_FALCON_SYS1_CPU0RS_MASK) == LTQ_FALCON_SYS1_CPU0RS_WDT) - ltq_wdt_bootstatus = WDIOF_CARDRESET; + return WDIOF_CARDRESET; return 0; } -static int -ltq_wdt_probe(struct platform_device *pdev) +static int ltq_wdt_probe(struct platform_device *pdev) { - struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + struct device *dev = &pdev->dev; + struct ltq_wdt_priv *priv; + struct watchdog_device *wdt; + struct resource *res; struct clk *clk; - ltq_wdt_bootstatus_set ltq_wdt_bootstatus_set; + const struct ltq_wdt_hw *ltq_wdt_hw; int ret; + u32 status; - ltq_wdt_membase = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(ltq_wdt_membase)) - return PTR_ERR(ltq_wdt_membase); + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; - ltq_wdt_bootstatus_set = of_device_get_match_data(&pdev->dev); - if (ltq_wdt_bootstatus_set) { - ret = ltq_wdt_bootstatus_set(pdev); - if (ret) - return ret; - } + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->membase = devm_ioremap_resource(dev, res); + if (IS_ERR(priv->membase)) + return PTR_ERR(priv->membase); /* we do not need to enable the clock as it is always running */ clk = clk_get_io(); - if (IS_ERR(clk)) { - dev_err(&pdev->dev, "Failed to get clock\n"); - return -ENOENT; + priv->clk_rate = clk_get_rate(clk) / LTQ_WDT_DIVIDER; + if (!priv->clk_rate) { + dev_err(dev, "clock rate less than divider %i\n", + LTQ_WDT_DIVIDER); + return -EINVAL; } - ltq_io_region_clk_rate = clk_get_rate(clk); - clk_put(clk); - dev_info(&pdev->dev, "Init done\n"); - return misc_register(<q_wdt_miscdev); -} + wdt = &priv->wdt; + wdt->info = <q_wdt_info; + wdt->ops = <q_wdt_ops; + wdt->min_timeout = 1; + wdt->max_timeout = LTQ_WDT_CR_MAX_TIMEOUT / priv->clk_rate; + wdt->timeout = wdt->max_timeout; + wdt->parent = dev; + + ltq_wdt_hw = of_device_get_match_data(dev); + if (ltq_wdt_hw && ltq_wdt_hw->bootstatus_get) { + ret = ltq_wdt_hw->bootstatus_get(dev); + if (ret >= 0) + wdt->bootstatus = ret; + } -static int -ltq_wdt_remove(struct platform_device *pdev) -{ - misc_deregister(<q_wdt_miscdev); + watchdog_set_nowayout(wdt, nowayout); + watchdog_init_timeout(wdt, 0, dev); + + status = ltq_wdt_r32(priv, LTQ_WDT_SR); + if (status & LTQ_WDT_SR_EN) { + /* + * If the watchdog is already running overwrite it with our + * new settings. Stop is not needed as the start call will + * replace all settings anyway. + */ + ltq_wdt_start(wdt); + set_bit(WDOG_HW_RUNNING, &wdt->status); + } - return 0; + return devm_watchdog_register_device(dev, wdt); } +static const struct ltq_wdt_hw ltq_wdt_xrx100 = { + .bootstatus_get = ltq_wdt_xrx_bootstatus_get, +}; + +static const struct ltq_wdt_hw ltq_wdt_falcon = { + .bootstatus_get = ltq_wdt_falcon_bootstatus_get, +}; + static const struct of_device_id ltq_wdt_match[] = { - { .compatible = "lantiq,wdt", .data = NULL}, - { .compatible = "lantiq,xrx100-wdt", .data = ltq_wdt_bootstatus_xrx }, - { .compatible = "lantiq,falcon-wdt", .data = ltq_wdt_bootstatus_falcon }, + { .compatible = "lantiq,wdt", .data = NULL }, + { .compatible = "lantiq,xrx100-wdt", .data = <q_wdt_xrx100 }, + { .compatible = "lantiq,falcon-wdt", .data = <q_wdt_falcon }, {}, }; MODULE_DEVICE_TABLE(of, ltq_wdt_match); static struct platform_driver ltq_wdt_driver = { .probe = ltq_wdt_probe, - .remove = ltq_wdt_remove, .driver = { .name = "wdt", .of_match_table = ltq_wdt_match, -- cgit v1.2.3 From c99d9df1d3c312759c43a3da0d2f17b4aecc05c8 Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Thu, 13 Sep 2018 23:32:11 +0200 Subject: watchdog: lantiq: add get_timeleft callback This callback will provide the current time left. Signed-off-by: Hauke Mehrtens Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/lantiq_wdt.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers') diff --git a/drivers/watchdog/lantiq_wdt.c b/drivers/watchdog/lantiq_wdt.c index c2f14d2bd695..83da84d6074b 100644 --- a/drivers/watchdog/lantiq_wdt.c +++ b/drivers/watchdog/lantiq_wdt.c @@ -50,6 +50,7 @@ #define LTQ_WDT_CR_MAX_TIMEOUT ((1 << 16) - 1) /* The reload field is 16 bit */ #define LTQ_WDT_SR 0x8 /* watchdog status register */ #define LTQ_WDT_SR_EN BIT(31) /* Enable */ +#define LTQ_WDT_SR_VALUE_MASK GENMASK(15, 0) /* Timer value */ #define LTQ_WDT_DIVIDER 0x40000 @@ -139,11 +140,21 @@ static int ltq_wdt_ping(struct watchdog_device *wdt) return 0; } +static unsigned int ltq_wdt_get_timeleft(struct watchdog_device *wdt) +{ + struct ltq_wdt_priv *priv = ltq_wdt_get_priv(wdt); + u64 timeout; + + timeout = ltq_wdt_r32(priv, LTQ_WDT_SR) & LTQ_WDT_SR_VALUE_MASK; + return do_div(timeout, priv->clk_rate); +} + static const struct watchdog_ops ltq_wdt_ops = { .owner = THIS_MODULE, .start = ltq_wdt_start, .stop = ltq_wdt_stop, .ping = ltq_wdt_ping, + .get_timeleft = ltq_wdt_get_timeleft, }; static int ltq_wdt_xrx_bootstatus_get(struct device *dev) -- cgit v1.2.3 From 79b10e09bb52c3cf99be771953374249b1198352 Mon Sep 17 00:00:00 2001 From: Christophe Leroy Date: Mon, 17 Sep 2018 06:22:47 +0000 Subject: watchdog: mpc8xxx: use dev_xxxx() instead of pr_xxxx() mpc8xxx watchdog driver is a platform device drivers, it is therefore possible to use dev_xxx() messaging rather than pr_xxx() Signed-off-by: Christophe Leroy Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/mpc8xxx_wdt.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/mpc8xxx_wdt.c b/drivers/watchdog/mpc8xxx_wdt.c index aca2d6323f8a..1dcf5f10cdd9 100644 --- a/drivers/watchdog/mpc8xxx_wdt.c +++ b/drivers/watchdog/mpc8xxx_wdt.c @@ -17,8 +17,6 @@ * option) any later version. */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - #include #include #include @@ -137,26 +135,27 @@ static int mpc8xxx_wdt_probe(struct platform_device *ofdev) struct mpc8xxx_wdt_ddata *ddata; u32 freq = fsl_get_sys_freq(); bool enabled; + struct device *dev = &ofdev->dev; - wdt_type = of_device_get_match_data(&ofdev->dev); + wdt_type = of_device_get_match_data(dev); if (!wdt_type) return -EINVAL; if (!freq || freq == -1) return -EINVAL; - ddata = devm_kzalloc(&ofdev->dev, sizeof(*ddata), GFP_KERNEL); + ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL); if (!ddata) return -ENOMEM; res = platform_get_resource(ofdev, IORESOURCE_MEM, 0); - ddata->base = devm_ioremap_resource(&ofdev->dev, res); + ddata->base = devm_ioremap_resource(dev, res); if (IS_ERR(ddata->base)) return PTR_ERR(ddata->base); enabled = in_be32(&ddata->base->swcrr) & SWCRR_SWEN; if (!enabled && wdt_type->hw_enabled) { - pr_info("could not be enabled in software\n"); + dev_info(dev, "could not be enabled in software\n"); return -ENODEV; } @@ -166,7 +165,7 @@ static int mpc8xxx_wdt_probe(struct platform_device *ofdev) ddata->wdd.ops = &mpc8xxx_wdt_ops, ddata->wdd.timeout = WATCHDOG_TIMEOUT; - watchdog_init_timeout(&ddata->wdd, timeout, &ofdev->dev); + watchdog_init_timeout(&ddata->wdd, timeout, dev); watchdog_set_nowayout(&ddata->wdd, nowayout); @@ -189,12 +188,13 @@ static int mpc8xxx_wdt_probe(struct platform_device *ofdev) ret = watchdog_register_device(&ddata->wdd); if (ret) { - pr_err("cannot register watchdog device (err=%d)\n", ret); + dev_err(dev, "cannot register watchdog device (err=%d)\n", ret); return ret; } - pr_info("WDT driver for MPC8xxx initialized. mode:%s timeout=%d sec\n", - reset ? "reset" : "interrupt", ddata->wdd.timeout); + dev_info(dev, + "WDT driver for MPC8xxx initialized. mode:%s timeout=%d sec\n", + reset ? "reset" : "interrupt", ddata->wdd.timeout); platform_set_drvdata(ofdev, ddata); return 0; @@ -204,8 +204,8 @@ static int mpc8xxx_wdt_remove(struct platform_device *ofdev) { struct mpc8xxx_wdt_ddata *ddata = platform_get_drvdata(ofdev); - pr_crit("Watchdog removed, expect the %s soon!\n", - reset ? "reset" : "machine check exception"); + dev_crit(&ofdev->dev, "Watchdog removed, expect the %s soon!\n", + reset ? "reset" : "machine check exception"); watchdog_unregister_device(&ddata->wdd); return 0; -- cgit v1.2.3 From 57cbf0e3a0fd48e5ad8f3884562e8dde4827c1c8 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 23 Sep 2018 06:54:11 -0700 Subject: watchdog: w83627hf_wdt: Support NCT6796D, NCT6797D, NCT6798D The watchdog controller on NCT6796D, NCT6797D, and NCT6798D is compatible with the wtachdog controller on other Nuvoton chips. Signed-off-by: Guenter Roeck Reviewed-by: Wim Van Sebroeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/w83627hf_wdt.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/watchdog/w83627hf_wdt.c b/drivers/watchdog/w83627hf_wdt.c index 7817836bff55..4b9365d4de7a 100644 --- a/drivers/watchdog/w83627hf_wdt.c +++ b/drivers/watchdog/w83627hf_wdt.c @@ -50,7 +50,7 @@ static int cr_wdt_csr; /* WDT control & status register */ enum chips { w83627hf, w83627s, w83697hf, w83697ug, w83637hf, w83627thf, w83687thf, w83627ehf, w83627dhg, w83627uhg, w83667hg, w83627dhg_p, w83667hg_b, nct6775, nct6776, nct6779, nct6791, nct6792, nct6793, - nct6795, nct6102 }; + nct6795, nct6796, nct6102 }; static int timeout; /* in seconds */ module_param(timeout, int, 0); @@ -100,6 +100,7 @@ MODULE_PARM_DESC(early_disable, "Disable watchdog at boot time (default=0)"); #define NCT6792_ID 0xc9 #define NCT6793_ID 0xd1 #define NCT6795_ID 0xd3 +#define NCT6796_ID 0xd4 /* also NCT9697D, NCT9698D */ #define W83627HF_WDT_TIMEOUT 0xf6 #define W83697HF_WDT_TIMEOUT 0xf4 @@ -209,6 +210,7 @@ static int w83627hf_init(struct watchdog_device *wdog, enum chips chip) case nct6792: case nct6793: case nct6795: + case nct6796: case nct6102: /* * These chips have a fixed WDTO# output pin (W83627UHG), @@ -407,6 +409,9 @@ static int wdt_find(int addr) case NCT6795_ID: ret = nct6795; break; + case NCT6796_ID: + ret = nct6796; + break; case NCT6102_ID: ret = nct6102; cr_wdt_timeout = NCT6102D_WDT_TIMEOUT; @@ -450,6 +455,7 @@ static int __init wdt_init(void) "NCT6792", "NCT6793", "NCT6795", + "NCT6796", "NCT6102", }; -- cgit v1.2.3 From 10d790d1fa2e9487053f9a3a289ff198736fa964 Mon Sep 17 00:00:00 2001 From: Jerry Hoemann Date: Fri, 21 Sep 2018 14:50:39 -0600 Subject: watchdog: hpwdt: Disable PreTimeout when Timeout is smaller During module install, disable pretimeout if the requested timeout value is not greater than the minimal pretimeout value that is supported by hardware. This makes the module load handling of pretimeout consistent with the ioctl handling of pretimeout. Signed-off-by: Jerry Hoemann Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/hpwdt.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/watchdog/hpwdt.c b/drivers/watchdog/hpwdt.c index 7af358b3e278..93562304f7aa 100644 --- a/drivers/watchdog/hpwdt.c +++ b/drivers/watchdog/hpwdt.c @@ -311,6 +311,10 @@ static int hpwdt_init_one(struct pci_dev *dev, if (watchdog_init_timeout(&hpwdt_dev, soft_margin, NULL)) dev_warn(&dev->dev, "Invalid soft_margin: %d.\n", soft_margin); + if (pretimeout && hpwdt_dev.timeout <= PRETIMEOUT_SEC) { + dev_warn(&dev->dev, "timeout <= pretimeout. Setting pretimeout to zero\n"); + pretimeout = 0; + } hpwdt_dev.pretimeout = pretimeout ? PRETIMEOUT_SEC : 0; hpwdt_dev.parent = &dev->dev; -- cgit v1.2.3 From 8e89130632c25f61137baa3d8e3ad861dcad179c Mon Sep 17 00:00:00 2001 From: Chris Brandt Date: Mon, 24 Sep 2018 08:58:15 -0500 Subject: watchdog: rza_wdt: Support longer timeouts The RZ/A2 watchdog timer extends the clock source options in order to allow for longer timeouts. Signed-off-by: Chris Brandt Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/rza_wdt.c | 88 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 70 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/rza_wdt.c b/drivers/watchdog/rza_wdt.c index c63ef03e24f6..781bb572e6af 100644 --- a/drivers/watchdog/rza_wdt.c +++ b/drivers/watchdog/rza_wdt.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -31,12 +32,45 @@ #define WRCSR_RSTE BIT(6) #define WRCSR_CLEAR_WOVF 0xA500 /* special value */ +/* The maximum CKS register setting value to get the longest timeout */ +#define CKS_3BIT 0x7 +#define CKS_4BIT 0xF + +#define DIVIDER_3BIT 16384 /* Clock divider when CKS = 0x7 */ +#define DIVIDER_4BIT 4194304 /* Clock divider when CKS = 0xF */ + struct rza_wdt { struct watchdog_device wdev; void __iomem *base; struct clk *clk; + u8 count; + u8 cks; }; +static void rza_wdt_calc_timeout(struct rza_wdt *priv, int timeout) +{ + unsigned long rate = clk_get_rate(priv->clk); + unsigned int ticks; + + if (priv->cks == CKS_4BIT) { + ticks = DIV_ROUND_UP(timeout * rate, DIVIDER_4BIT); + + /* + * Since max_timeout was set in probe, we know that the timeout + * value passed will never calculate to a tick value greater + * than 256. + */ + priv->count = 256 - ticks; + + } else { + /* Start timer with longest timeout */ + priv->count = 0; + } + + pr_debug("%s: timeout set to %u (WTCNT=%d)\n", __func__, + timeout, priv->count); +} + static int rza_wdt_start(struct watchdog_device *wdev) { struct rza_wdt *priv = watchdog_get_drvdata(wdev); @@ -48,13 +82,12 @@ static int rza_wdt_start(struct watchdog_device *wdev) readb(priv->base + WRCSR); writew(WRCSR_CLEAR_WOVF, priv->base + WRCSR); - /* - * Start timer with slowest clock source and reset option enabled. - */ + rza_wdt_calc_timeout(priv, wdev->timeout); + writew(WRCSR_MAGIC | WRCSR_RSTE, priv->base + WRCSR); - writew(WTCNT_MAGIC | 0, priv->base + WTCNT); - writew(WTCSR_MAGIC | WTSCR_WT | WTSCR_TME | WTSCR_CKS(7), - priv->base + WTCSR); + writew(WTCNT_MAGIC | priv->count, priv->base + WTCNT); + writew(WTCSR_MAGIC | WTSCR_WT | WTSCR_TME | + WTSCR_CKS(priv->cks), priv->base + WTCSR); return 0; } @@ -72,8 +105,17 @@ static int rza_wdt_ping(struct watchdog_device *wdev) { struct rza_wdt *priv = watchdog_get_drvdata(wdev); - writew(WTCNT_MAGIC | 0, priv->base + WTCNT); + writew(WTCNT_MAGIC | priv->count, priv->base + WTCNT); + pr_debug("%s: timeout = %u\n", __func__, wdev->timeout); + + return 0; +} + +static int rza_set_timeout(struct watchdog_device *wdev, unsigned int timeout) +{ + wdev->timeout = timeout; + rza_wdt_start(wdev); return 0; } @@ -118,6 +160,7 @@ static const struct watchdog_ops rza_wdt_ops = { .start = rza_wdt_start, .stop = rza_wdt_stop, .ping = rza_wdt_ping, + .set_timeout = rza_set_timeout, .restart = rza_wdt_restart, }; @@ -147,20 +190,28 @@ static int rza_wdt_probe(struct platform_device *pdev) return -ENOENT; } - /* Assume slowest clock rate possible (CKS=7) */ - rate /= 16384; - priv->wdev.info = &rza_wdt_ident, priv->wdev.ops = &rza_wdt_ops, priv->wdev.parent = &pdev->dev; - /* - * Since the max possible timeout of our 8-bit count register is less - * than a second, we must use max_hw_heartbeat_ms. - */ - priv->wdev.max_hw_heartbeat_ms = (1000 * U8_MAX) / rate; - dev_dbg(&pdev->dev, "max hw timeout of %dms\n", - priv->wdev.max_hw_heartbeat_ms); + priv->cks = (u8)(uintptr_t)of_device_get_match_data(&pdev->dev); + if (priv->cks == CKS_4BIT) { + /* Assume slowest clock rate possible (CKS=0xF) */ + priv->wdev.max_timeout = (DIVIDER_4BIT * U8_MAX) / rate; + + } else if (priv->cks == CKS_3BIT) { + /* Assume slowest clock rate possible (CKS=7) */ + rate /= DIVIDER_3BIT; + + /* + * Since the max possible timeout of our 8-bit count + * register is less than a second, we must use + * max_hw_heartbeat_ms. + */ + priv->wdev.max_hw_heartbeat_ms = (1000 * U8_MAX) / rate; + dev_dbg(&pdev->dev, "max hw timeout of %dms\n", + priv->wdev.max_hw_heartbeat_ms); + } priv->wdev.min_timeout = 1; priv->wdev.timeout = DEFAULT_TIMEOUT; @@ -176,7 +227,8 @@ static int rza_wdt_probe(struct platform_device *pdev) } static const struct of_device_id rza_wdt_of_match[] = { - { .compatible = "renesas,rza-wdt", }, + { .compatible = "renesas,r7s9210-wdt", .data = (void *)CKS_4BIT, }, + { .compatible = "renesas,rza-wdt", .data = (void *)CKS_3BIT, }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, rza_wdt_of_match); -- cgit v1.2.3 From 38e48b718934d32d45bb9f917b3430822fca76bb Mon Sep 17 00:00:00 2001 From: Christophe Leroy Date: Mon, 17 Sep 2018 06:22:50 +0000 Subject: watchdog: mpc8xxx: provide boot status mpc8xxx watchdog driver supports the following platforms: - mpc8xx - mpc83xx - mpc86xx Those three platforms have a 32 bits register which provides the reason of the last boot, including whether it was caused by the watchdog. mpc8xx: Register RSR, bit SWRS (bit 3) mpc83xx: Register RSR, bit SWRS (bit 28) mpc86xx: Register RSTRSCR, bit WDT_RR (bit 11) This patch maps the register as defined in the device tree and updates wdt.bootstatus based on the value of the watchdog related bit. Then the information can be retrieved via the WDIOC_GETBOOTSTATUS ioctl. Hereunder is an example of devicetree for mpc8xx, the Reset Status Register being at offset 0x288: WDT: watchdog@0 { compatible = "fsl,mpc823-wdt"; reg = <0x0 0x10 0x288 0x4>; }; On the mpc83xx, RSR is at offset 0x910 On the mpc86xx, RSTRSCR is at offset 0xe0094 Suggested-by: Radu Rendec Tested-by: Christophe Leroy # On mpc885 Signed-off-by: Christophe Leroy Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/mpc8xxx_wdt.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'drivers') diff --git a/drivers/watchdog/mpc8xxx_wdt.c b/drivers/watchdog/mpc8xxx_wdt.c index 1dcf5f10cdd9..069072e6747d 100644 --- a/drivers/watchdog/mpc8xxx_wdt.c +++ b/drivers/watchdog/mpc8xxx_wdt.c @@ -47,6 +47,7 @@ struct mpc8xxx_wdt { struct mpc8xxx_wdt_type { int prescaler; bool hw_enabled; + u32 rsr_mask; }; struct mpc8xxx_wdt_ddata { @@ -159,6 +160,24 @@ static int mpc8xxx_wdt_probe(struct platform_device *ofdev) return -ENODEV; } + res = platform_get_resource(ofdev, IORESOURCE_MEM, 1); + if (res) { + bool status; + u32 __iomem *rsr = ioremap(res->start, resource_size(res)); + + if (!rsr) + return -ENOMEM; + + status = in_be32(rsr) & wdt_type->rsr_mask; + ddata->wdd.bootstatus = status ? WDIOF_CARDRESET : 0; + /* clear reset status bits related to watchdog timer */ + out_be32(rsr, wdt_type->rsr_mask); + iounmap(rsr); + + dev_info(dev, "Last boot was %scaused by watchdog\n", + status ? "" : "not "); + } + spin_lock_init(&ddata->lock); ddata->wdd.info = &mpc8xxx_wdt_info, @@ -216,6 +235,7 @@ static const struct of_device_id mpc8xxx_wdt_match[] = { .compatible = "mpc83xx_wdt", .data = &(struct mpc8xxx_wdt_type) { .prescaler = 0x10000, + .rsr_mask = BIT(3), /* RSR Bit SWRS */ }, }, { @@ -223,6 +243,7 @@ static const struct of_device_id mpc8xxx_wdt_match[] = { .data = &(struct mpc8xxx_wdt_type) { .prescaler = 0x10000, .hw_enabled = true, + .rsr_mask = BIT(20), /* RSTRSCR Bit WDT_RR */ }, }, { @@ -230,6 +251,7 @@ static const struct of_device_id mpc8xxx_wdt_match[] = { .data = &(struct mpc8xxx_wdt_type) { .prescaler = 0x800, .hw_enabled = true, + .rsr_mask = BIT(28), /* RSR Bit SWRS */ }, }, {}, -- cgit v1.2.3 From 54e3d9b518c8a2a7a3acb14a5912fafd8ef38f40 Mon Sep 17 00:00:00 2001 From: Marek Behún Date: Mon, 24 Sep 2018 13:06:51 +0200 Subject: watchdog: Add support for Armada 37xx CPU watchdog MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds support for the CPU watchdog found on Marvell Armada 37xx SoCs. There are 4 counters which can be set as CPU watchdog counters. This driver uses the second counter (ID 1, counting from 0) as watchdog counter, and first counter (ID 0) to implement pinging on the second counter without the need to disable it. Since counters IDs 2 and 3 are enabled already before even U-Boot starts, this driver does not use them at all, for example by adding a device tree property for counter selection. Signed-off-by: Marek Behún Reviewed-by: Guenter Roeck Tested-by: Miquel Raynal Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/Kconfig | 11 ++ drivers/watchdog/Makefile | 1 + drivers/watchdog/armada_37xx_wdt.c | 387 +++++++++++++++++++++++++++++++++++++ 3 files changed, 399 insertions(+) create mode 100644 drivers/watchdog/armada_37xx_wdt.c (limited to 'drivers') diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 5637d8be31e0..2d64333f4782 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -273,6 +273,17 @@ config ARM_SBSA_WATCHDOG To compile this driver as module, choose M here: The module will be called sbsa_gwdt. +config ARMADA_37XX_WATCHDOG + tristate "Armada 37xx watchdog" + depends on ARCH_MVEBU || COMPILE_TEST + select MFD_SYSCON + select WATCHDOG_CORE + help + Say Y here to include support for the watchdog timer found on + Marvell Armada 37xx SoCs. + To compile this driver as a module, choose M here: the + module will be called armada_37xx_wdt. + config ASM9260_WATCHDOG tristate "Alphascale ASM9260 watchdog" depends on MACH_ASM9260 || COMPILE_TEST diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index bf92e7bf9ce0..f69cdff5ad7f 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -38,6 +38,7 @@ obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb.o # ARM Architecture obj-$(CONFIG_ARM_SP805_WATCHDOG) += sp805_wdt.o obj-$(CONFIG_ARM_SBSA_WATCHDOG) += sbsa_gwdt.o +obj-$(CONFIG_ARMADA_37XX_WATCHDOG) += armada_37xx_wdt.o obj-$(CONFIG_ASM9260_WATCHDOG) += asm9260_wdt.o obj-$(CONFIG_AT91RM9200_WATCHDOG) += at91rm9200_wdt.o obj-$(CONFIG_AT91SAM9X_WATCHDOG) += at91sam9_wdt.o diff --git a/drivers/watchdog/armada_37xx_wdt.c b/drivers/watchdog/armada_37xx_wdt.c new file mode 100644 index 000000000000..c43c313ba63d --- /dev/null +++ b/drivers/watchdog/armada_37xx_wdt.c @@ -0,0 +1,387 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Watchdog driver for Marvell Armada 37xx SoCs + * + * Author: Marek Behun + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * There are four counters that can be used for watchdog on Armada 37xx. + * The addresses for counter control registers are register base plus ID*0x10, + * where ID is 0, 1, 2 or 3. + * + * In this driver we use IDs 0 and 1. Counter ID 1 is used as watchdog counter, + * while counter ID 0 is used to implement pinging the watchdog: counter ID 1 is + * set to restart counting from initial value on counter ID 0 end count event. + * Pinging is done by forcing immediate end count event on counter ID 0. + * If only one counter was used, pinging would have to be implemented by + * disabling and enabling the counter, leaving the system in a vulnerable state + * for a (really) short period of time. + * + * Counters ID 2 and 3 are enabled by default even before U-Boot loads, + * therefore this driver does not provide a way to use them, eg. by setting a + * property in device tree. + */ + +#define CNTR_ID_RETRIGGER 0 +#define CNTR_ID_WDOG 1 + +/* relative to cpu_misc */ +#define WDT_TIMER_SELECT 0x64 +#define WDT_TIMER_SELECT_MASK 0xf +#define WDT_TIMER_SELECT_VAL BIT(CNTR_ID_WDOG) + +/* relative to reg */ +#define CNTR_CTRL(id) ((id) * 0x10) +#define CNTR_CTRL_ENABLE 0x0001 +#define CNTR_CTRL_ACTIVE 0x0002 +#define CNTR_CTRL_MODE_MASK 0x000c +#define CNTR_CTRL_MODE_ONESHOT 0x0000 +#define CNTR_CTRL_MODE_HWSIG 0x000c +#define CNTR_CTRL_TRIG_SRC_MASK 0x00f0 +#define CNTR_CTRL_TRIG_SRC_PREV_CNTR 0x0050 +#define CNTR_CTRL_PRESCALE_MASK 0xff00 +#define CNTR_CTRL_PRESCALE_MIN 2 +#define CNTR_CTRL_PRESCALE_SHIFT 8 + +#define CNTR_COUNT_LOW(id) (CNTR_CTRL(id) + 0x4) +#define CNTR_COUNT_HIGH(id) (CNTR_CTRL(id) + 0x8) + +#define WATCHDOG_TIMEOUT 120 + +static unsigned int timeout; +module_param(timeout, int, 0); +MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds"); + +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 armada_37xx_watchdog { + struct watchdog_device wdt; + struct regmap *cpu_misc; + void __iomem *reg; + u64 timeout; /* in clock ticks */ + unsigned long clk_rate; + struct clk *clk; +}; + +static u64 get_counter_value(struct armada_37xx_watchdog *dev, int id) +{ + u64 val; + + /* + * when low is read, high is latched into flip-flops so that it can be + * read consistently without using software debouncing + */ + val = readl(dev->reg + CNTR_COUNT_LOW(id)); + val |= ((u64)readl(dev->reg + CNTR_COUNT_HIGH(id))) << 32; + + return val; +} + +static void set_counter_value(struct armada_37xx_watchdog *dev, int id, u64 val) +{ + writel(val & 0xffffffff, dev->reg + CNTR_COUNT_LOW(id)); + writel(val >> 32, dev->reg + CNTR_COUNT_HIGH(id)); +} + +static void counter_enable(struct armada_37xx_watchdog *dev, int id) +{ + u32 reg; + + reg = readl(dev->reg + CNTR_CTRL(id)); + reg |= CNTR_CTRL_ENABLE; + writel(reg, dev->reg + CNTR_CTRL(id)); +} + +static void counter_disable(struct armada_37xx_watchdog *dev, int id) +{ + u32 reg; + + reg = readl(dev->reg + CNTR_CTRL(id)); + reg &= ~CNTR_CTRL_ENABLE; + writel(reg, dev->reg + CNTR_CTRL(id)); +} + +static void init_counter(struct armada_37xx_watchdog *dev, int id, u32 mode, + u32 trig_src) +{ + u32 reg; + + reg = readl(dev->reg + CNTR_CTRL(id)); + + reg &= ~(CNTR_CTRL_MODE_MASK | CNTR_CTRL_PRESCALE_MASK | + CNTR_CTRL_TRIG_SRC_MASK); + + /* set mode */ + reg |= mode & CNTR_CTRL_MODE_MASK; + + /* set prescaler to the min value */ + reg |= CNTR_CTRL_PRESCALE_MIN << CNTR_CTRL_PRESCALE_SHIFT; + + /* set trigger source */ + reg |= trig_src & CNTR_CTRL_TRIG_SRC_MASK; + + writel(reg, dev->reg + CNTR_CTRL(id)); +} + +static int armada_37xx_wdt_ping(struct watchdog_device *wdt) +{ + struct armada_37xx_watchdog *dev = watchdog_get_drvdata(wdt); + + /* counter 1 is retriggered by forcing end count on counter 0 */ + counter_disable(dev, CNTR_ID_RETRIGGER); + counter_enable(dev, CNTR_ID_RETRIGGER); + + return 0; +} + +static unsigned int armada_37xx_wdt_get_timeleft(struct watchdog_device *wdt) +{ + struct armada_37xx_watchdog *dev = watchdog_get_drvdata(wdt); + unsigned int res; + + res = get_counter_value(dev, CNTR_ID_WDOG) * + CNTR_CTRL_PRESCALE_MIN / dev->clk_rate; + + return res; +} + +static int armada_37xx_wdt_set_timeout(struct watchdog_device *wdt, + unsigned int timeout) +{ + struct armada_37xx_watchdog *dev = watchdog_get_drvdata(wdt); + + wdt->timeout = timeout; + + /* + * Compute the timeout in clock rate. We use smallest possible + * prescaler, which divides the clock rate by 2 + * (CNTR_CTRL_PRESCALE_MIN). + */ + dev->timeout = (u64)dev->clk_rate * timeout / CNTR_CTRL_PRESCALE_MIN; + + return 0; +} + +static bool armada_37xx_wdt_is_running(struct armada_37xx_watchdog *dev) +{ + u32 reg; + + regmap_read(dev->cpu_misc, WDT_TIMER_SELECT, ®); + if ((reg & WDT_TIMER_SELECT_MASK) != WDT_TIMER_SELECT_VAL) + return false; + + reg = readl(dev->reg + CNTR_CTRL(CNTR_ID_WDOG)); + return !!(reg & CNTR_CTRL_ACTIVE); +} + +static int armada_37xx_wdt_start(struct watchdog_device *wdt) +{ + struct armada_37xx_watchdog *dev = watchdog_get_drvdata(wdt); + + /* select counter 1 as watchdog counter */ + regmap_write(dev->cpu_misc, WDT_TIMER_SELECT, WDT_TIMER_SELECT_VAL); + + /* init counter 0 as retrigger counter for counter 1 */ + init_counter(dev, CNTR_ID_RETRIGGER, CNTR_CTRL_MODE_ONESHOT, 0); + set_counter_value(dev, CNTR_ID_RETRIGGER, 0); + + /* init counter 1 to be retriggerable by counter 0 end count */ + init_counter(dev, CNTR_ID_WDOG, CNTR_CTRL_MODE_HWSIG, + CNTR_CTRL_TRIG_SRC_PREV_CNTR); + set_counter_value(dev, CNTR_ID_WDOG, dev->timeout); + + /* enable counter 1 */ + counter_enable(dev, CNTR_ID_WDOG); + + /* start counter 1 by forcing immediate end count on counter 0 */ + counter_enable(dev, CNTR_ID_RETRIGGER); + + return 0; +} + +static int armada_37xx_wdt_stop(struct watchdog_device *wdt) +{ + struct armada_37xx_watchdog *dev = watchdog_get_drvdata(wdt); + + counter_disable(dev, CNTR_ID_WDOG); + counter_disable(dev, CNTR_ID_RETRIGGER); + regmap_write(dev->cpu_misc, WDT_TIMER_SELECT, 0); + + return 0; +} + +static const struct watchdog_info armada_37xx_wdt_info = { + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, + .identity = "Armada 37xx Watchdog", +}; + +static const struct watchdog_ops armada_37xx_wdt_ops = { + .owner = THIS_MODULE, + .start = armada_37xx_wdt_start, + .stop = armada_37xx_wdt_stop, + .ping = armada_37xx_wdt_ping, + .set_timeout = armada_37xx_wdt_set_timeout, + .get_timeleft = armada_37xx_wdt_get_timeleft, +}; + +static int armada_37xx_wdt_probe(struct platform_device *pdev) +{ + struct armada_37xx_watchdog *dev; + struct resource *res; + struct regmap *regmap; + int ret; + + dev = devm_kzalloc(&pdev->dev, sizeof(struct armada_37xx_watchdog), + GFP_KERNEL); + if (!dev) + return -ENOMEM; + + dev->wdt.info = &armada_37xx_wdt_info; + dev->wdt.ops = &armada_37xx_wdt_ops; + + regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, + "marvell,system-controller"); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + dev->cpu_misc = regmap; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + dev->reg = devm_ioremap(&pdev->dev, res->start, resource_size(res)); + + /* init clock */ + dev->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(dev->clk)) + return PTR_ERR(dev->clk); + + ret = clk_prepare_enable(dev->clk); + if (ret) + return ret; + + dev->clk_rate = clk_get_rate(dev->clk); + if (!dev->clk_rate) { + ret = -EINVAL; + goto disable_clk; + } + + /* + * Since the timeout in seconds is given as 32 bit unsigned int, and + * the counters hold 64 bit values, even after multiplication by clock + * rate the counter can hold timeout of UINT_MAX seconds. + */ + dev->wdt.min_timeout = 1; + dev->wdt.max_timeout = UINT_MAX; + dev->wdt.parent = &pdev->dev; + + /* default value, possibly override by module parameter or dtb */ + dev->wdt.timeout = WATCHDOG_TIMEOUT; + watchdog_init_timeout(&dev->wdt, timeout, &pdev->dev); + + platform_set_drvdata(pdev, &dev->wdt); + watchdog_set_drvdata(&dev->wdt, dev); + + armada_37xx_wdt_set_timeout(&dev->wdt, dev->wdt.timeout); + + if (armada_37xx_wdt_is_running(dev)) + set_bit(WDOG_HW_RUNNING, &dev->wdt.status); + + watchdog_set_nowayout(&dev->wdt, nowayout); + ret = watchdog_register_device(&dev->wdt); + if (ret) + goto disable_clk; + + dev_info(&pdev->dev, "Initial timeout %d sec%s\n", + dev->wdt.timeout, nowayout ? ", nowayout" : ""); + + return 0; + +disable_clk: + clk_disable_unprepare(dev->clk); + return ret; +} + +static int armada_37xx_wdt_remove(struct platform_device *pdev) +{ + struct watchdog_device *wdt = platform_get_drvdata(pdev); + struct armada_37xx_watchdog *dev = watchdog_get_drvdata(wdt); + + watchdog_unregister_device(wdt); + clk_disable_unprepare(dev->clk); + return 0; +} + +static void armada_37xx_wdt_shutdown(struct platform_device *pdev) +{ + struct watchdog_device *wdt = platform_get_drvdata(pdev); + + armada_37xx_wdt_stop(wdt); +} + +static int __maybe_unused armada_37xx_wdt_suspend(struct device *dev) +{ + struct watchdog_device *wdt = dev_get_drvdata(dev); + + return armada_37xx_wdt_stop(wdt); +} + +static int __maybe_unused armada_37xx_wdt_resume(struct device *dev) +{ + struct watchdog_device *wdt = dev_get_drvdata(dev); + + if (watchdog_active(wdt)) + return armada_37xx_wdt_start(wdt); + + return 0; +} + +static const struct dev_pm_ops armada_37xx_wdt_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(armada_37xx_wdt_suspend, + armada_37xx_wdt_resume) +}; + +#ifdef CONFIG_OF +static const struct of_device_id armada_37xx_wdt_match[] = { + { .compatible = "marvell,armada-3700-wdt", }, + {}, +}; +MODULE_DEVICE_TABLE(of, armada_37xx_wdt_match); +#endif + +static struct platform_driver armada_37xx_wdt_driver = { + .probe = armada_37xx_wdt_probe, + .remove = armada_37xx_wdt_remove, + .shutdown = armada_37xx_wdt_shutdown, + .driver = { + .name = "armada_37xx_wdt", + .of_match_table = of_match_ptr(armada_37xx_wdt_match), + .pm = &armada_37xx_wdt_dev_pm_ops, + }, +}; + +module_platform_driver(armada_37xx_wdt_driver); + +MODULE_AUTHOR("Marek Behun "); +MODULE_DESCRIPTION("Armada 37xx CPU Watchdog"); + +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:armada_37xx_wdt"); -- cgit v1.2.3 From c8ca6e70fb74cd3bb6fc0738aed40991b9de0b87 Mon Sep 17 00:00:00 2001 From: Marek Behún Date: Wed, 10 Oct 2018 17:17:27 +0200 Subject: watchdog: armada_37xx_wdt: use do_div for u64 division MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the driver is built on 32 bit architectures during compile test, the linker complains about "__udivdi3" being undefined. We have to use do_div macro instead of the division operator when dividing u64 value. Signed-off-by: Marek Behún Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/armada_37xx_wdt.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/armada_37xx_wdt.c b/drivers/watchdog/armada_37xx_wdt.c index c43c313ba63d..4b4054f54df9 100644 --- a/drivers/watchdog/armada_37xx_wdt.c +++ b/drivers/watchdog/armada_37xx_wdt.c @@ -156,10 +156,10 @@ static int armada_37xx_wdt_ping(struct watchdog_device *wdt) static unsigned int armada_37xx_wdt_get_timeleft(struct watchdog_device *wdt) { struct armada_37xx_watchdog *dev = watchdog_get_drvdata(wdt); - unsigned int res; + u64 res; - res = get_counter_value(dev, CNTR_ID_WDOG) * - CNTR_CTRL_PRESCALE_MIN / dev->clk_rate; + res = get_counter_value(dev, CNTR_ID_WDOG) * CNTR_CTRL_PRESCALE_MIN; + do_div(res, dev->clk_rate); return res; } @@ -176,7 +176,8 @@ static int armada_37xx_wdt_set_timeout(struct watchdog_device *wdt, * prescaler, which divides the clock rate by 2 * (CNTR_CTRL_PRESCALE_MIN). */ - dev->timeout = (u64)dev->clk_rate * timeout / CNTR_CTRL_PRESCALE_MIN; + dev->timeout = (u64)dev->clk_rate * timeout; + do_div(dev->timeout, CNTR_CTRL_PRESCALE_MIN); return 0; } -- cgit v1.2.3 From cd6ba41c192deca20d9add5554bc8c51d0f07de2 Mon Sep 17 00:00:00 2001 From: Alexey Khoroshilov Date: Sat, 13 Oct 2018 23:51:03 +0300 Subject: watchdog: ts4800: release syscon device node in ts4800_wdt_probe() Put syscon device node when it is not needed anymore. Found by Linux Driver Verification project (linuxtesting.org). Signed-off-by: Alexey Khoroshilov Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/ts4800_wdt.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/watchdog/ts4800_wdt.c b/drivers/watchdog/ts4800_wdt.c index 2b8de8602b67..89843b16b04a 100644 --- a/drivers/watchdog/ts4800_wdt.c +++ b/drivers/watchdog/ts4800_wdt.c @@ -135,6 +135,7 @@ static int ts4800_wdt_probe(struct platform_device *pdev) /* set regmap and offset to know where to write */ wdt->feed_offset = reg; wdt->regmap = syscon_node_to_regmap(syscon_np); + of_node_put(syscon_np); if (IS_ERR(wdt->regmap)) { dev_err(&pdev->dev, "cannot get parent's regmap\n"); return PTR_ERR(wdt->regmap); -- cgit v1.2.3