diff options
-rw-r--r-- | Documentation/devicetree/bindings/watchdog/nxp,pnx4008-wdt.yaml | 3 | ||||
-rw-r--r-- | drivers/watchdog/dw_wdt.c | 2 | ||||
-rw-r--r-- | drivers/watchdog/iTCO_wdt.c | 6 | ||||
-rw-r--r-- | drivers/watchdog/it87_wdt.c | 4 | ||||
-rw-r--r-- | drivers/watchdog/renesas_wdt.c | 8 | ||||
-rw-r--r-- | drivers/watchdog/rti_wdt.c | 14 | ||||
-rw-r--r-- | drivers/watchdog/sbsa_gwdt.c | 50 | ||||
-rw-r--r-- | drivers/watchdog/watchdog_core.h | 8 | ||||
-rw-r--r-- | drivers/watchdog/watchdog_pretimeout.c | 2 | ||||
-rw-r--r-- | drivers/watchdog/ziirave_wdt.c | 3 | ||||
-rw-r--r-- | include/linux/watchdog.h | 12 |
11 files changed, 87 insertions, 25 deletions
diff --git a/Documentation/devicetree/bindings/watchdog/nxp,pnx4008-wdt.yaml b/Documentation/devicetree/bindings/watchdog/nxp,pnx4008-wdt.yaml index 35ef940cbabe..8964c1c5d522 100644 --- a/Documentation/devicetree/bindings/watchdog/nxp,pnx4008-wdt.yaml +++ b/Documentation/devicetree/bindings/watchdog/nxp,pnx4008-wdt.yaml @@ -19,6 +19,9 @@ properties: reg: maxItems: 1 + clocks: + maxItems: 1 + required: - compatible - reg diff --git a/drivers/watchdog/dw_wdt.c b/drivers/watchdog/dw_wdt.c index 26efca9ae0e7..c3fbb6068c52 100644 --- a/drivers/watchdog/dw_wdt.c +++ b/drivers/watchdog/dw_wdt.c @@ -644,6 +644,8 @@ static int dw_wdt_drv_probe(struct platform_device *pdev) } else { wdd->timeout = DW_WDT_DEFAULT_SECONDS; watchdog_init_timeout(wdd, 0, dev); + /* Limit timeout value to hardware constraints. */ + dw_wdt_set_timeout(wdd, wdd->timeout); } platform_set_drvdata(pdev, dw_wdt); diff --git a/drivers/watchdog/iTCO_wdt.c b/drivers/watchdog/iTCO_wdt.c index 9ab769aa0244..4ab3405ef8e6 100644 --- a/drivers/watchdog/iTCO_wdt.c +++ b/drivers/watchdog/iTCO_wdt.c @@ -577,7 +577,11 @@ static int iTCO_wdt_probe(struct platform_device *pdev) /* Check that the heartbeat value is within it's range; if not reset to the default */ if (iTCO_wdt_set_timeout(&p->wddev, heartbeat)) { - iTCO_wdt_set_timeout(&p->wddev, WATCHDOG_TIMEOUT); + ret = iTCO_wdt_set_timeout(&p->wddev, WATCHDOG_TIMEOUT); + if (ret != 0) { + dev_err(dev, "Failed to set watchdog timeout (%d)\n", WATCHDOG_TIMEOUT); + return ret; + } dev_info(dev, "timeout value out of range, using %d\n", WATCHDOG_TIMEOUT); heartbeat = WATCHDOG_TIMEOUT; diff --git a/drivers/watchdog/it87_wdt.c b/drivers/watchdog/it87_wdt.c index a1e23dce8810..3b8488c86a2f 100644 --- a/drivers/watchdog/it87_wdt.c +++ b/drivers/watchdog/it87_wdt.c @@ -22,11 +22,13 @@ #include <linux/bits.h> #include <linux/dmi.h> +#include <linux/errno.h> #include <linux/init.h> #include <linux/io.h> -#include <linux/kernel.h> +#include <linux/ioport.h> #include <linux/module.h> #include <linux/moduleparam.h> +#include <linux/printk.h> #include <linux/types.h> #include <linux/watchdog.h> diff --git a/drivers/watchdog/renesas_wdt.c b/drivers/watchdog/renesas_wdt.c index c0b2a9c5250d..97bcd32bade5 100644 --- a/drivers/watchdog/renesas_wdt.c +++ b/drivers/watchdog/renesas_wdt.c @@ -300,7 +300,7 @@ static void rwdt_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); } -static int __maybe_unused rwdt_suspend(struct device *dev) +static int rwdt_suspend(struct device *dev) { struct rwdt_priv *priv = dev_get_drvdata(dev); @@ -310,7 +310,7 @@ static int __maybe_unused rwdt_suspend(struct device *dev) return 0; } -static int __maybe_unused rwdt_resume(struct device *dev) +static int rwdt_resume(struct device *dev) { struct rwdt_priv *priv = dev_get_drvdata(dev); @@ -320,7 +320,7 @@ static int __maybe_unused rwdt_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(rwdt_pm_ops, rwdt_suspend, rwdt_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(rwdt_pm_ops, rwdt_suspend, rwdt_resume); static const struct of_device_id rwdt_ids[] = { { .compatible = "renesas,rcar-gen2-wdt", }, @@ -334,7 +334,7 @@ static struct platform_driver rwdt_driver = { .driver = { .name = "renesas_wdt", .of_match_table = rwdt_ids, - .pm = &rwdt_pm_ops, + .pm = pm_sleep_ptr(&rwdt_pm_ops), }, .probe = rwdt_probe, .remove = rwdt_remove, diff --git a/drivers/watchdog/rti_wdt.c b/drivers/watchdog/rti_wdt.c index d1f9ce4100a8..be7d7db47591 100644 --- a/drivers/watchdog/rti_wdt.c +++ b/drivers/watchdog/rti_wdt.c @@ -15,7 +15,7 @@ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/of.h> -#include <linux/of_address.h> +#include <linux/of_reserved_mem.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/types.h> @@ -214,7 +214,6 @@ static int rti_wdt_probe(struct platform_device *pdev) struct rti_wdt_device *wdt; struct clk *clk; u32 last_ping = 0; - struct device_node *node; u32 reserved_mem_size; struct resource res; u32 *vaddr; @@ -299,15 +298,8 @@ static int rti_wdt_probe(struct platform_device *pdev) } } - node = of_parse_phandle(pdev->dev.of_node, "memory-region", 0); - if (node) { - ret = of_address_to_resource(node, 0, &res); - of_node_put(node); - if (ret) { - dev_err(dev, "No memory address assigned to the region.\n"); - goto err_iomap; - } - + ret = of_reserved_mem_region_to_resource(pdev->dev.of_node, 0, &res); + if (!ret) { /* * If reserved memory is defined for watchdog reset cause. * Readout the Power-on(PON) reason and pass to bootstatus. diff --git a/drivers/watchdog/sbsa_gwdt.c b/drivers/watchdog/sbsa_gwdt.c index 5f23913ce3b4..6ce1bfb39064 100644 --- a/drivers/watchdog/sbsa_gwdt.c +++ b/drivers/watchdog/sbsa_gwdt.c @@ -75,11 +75,17 @@ #define SBSA_GWDT_VERSION_MASK 0xF #define SBSA_GWDT_VERSION_SHIFT 16 +#define SBSA_GWDT_IMPL_MASK 0x7FF +#define SBSA_GWDT_IMPL_SHIFT 0 +#define SBSA_GWDT_IMPL_MEDIATEK 0x426 + /** * struct sbsa_gwdt - Internal representation of the SBSA GWDT * @wdd: kernel watchdog_device structure * @clk: store the System Counter clock frequency, in Hz. * @version: store the architecture version + * @need_ws0_race_workaround: + * indicate whether to adjust wdd->timeout to avoid a race with WS0 * @refresh_base: Virtual address of the watchdog refresh frame * @control_base: Virtual address of the watchdog control frame */ @@ -87,6 +93,7 @@ struct sbsa_gwdt { struct watchdog_device wdd; u32 clk; int version; + bool need_ws0_race_workaround; void __iomem *refresh_base; void __iomem *control_base; }; @@ -161,6 +168,31 @@ static int sbsa_gwdt_set_timeout(struct watchdog_device *wdd, */ sbsa_gwdt_reg_write(((u64)gwdt->clk / 2) * timeout, gwdt); + /* + * Some watchdog hardware has a race condition where it will ignore + * sbsa_gwdt_keepalive() if it is called at the exact moment that a + * timeout occurs and WS0 is being asserted. Unfortunately, the default + * behavior of the watchdog core is very likely to trigger this race + * when action=0 because it programs WOR to be half of the desired + * timeout, and watchdog_next_keepalive() chooses the exact same time to + * send keepalive pings. + * + * This triggers a race where sbsa_gwdt_keepalive() can be called right + * as WS0 is being asserted, and affected hardware will ignore that + * write and continue to assert WS0. After another (timeout / 2) + * seconds, the same race happens again. If the driver wins then the + * explicit refresh will reset WS0 to false but if the hardware wins, + * then WS1 is asserted and the system resets. + * + * Avoid the problem by scheduling keepalive heartbeats one second later + * than the WOR timeout. + * + * This workaround might not be needed in a future revision of the + * hardware. + */ + if (gwdt->need_ws0_race_workaround) + wdd->min_hw_heartbeat_ms = timeout * 500 + 1000; + return 0; } @@ -202,12 +234,15 @@ static int sbsa_gwdt_keepalive(struct watchdog_device *wdd) static void sbsa_gwdt_get_version(struct watchdog_device *wdd) { struct sbsa_gwdt *gwdt = watchdog_get_drvdata(wdd); - int ver; + int iidr, ver, impl; - ver = readl(gwdt->control_base + SBSA_GWDT_W_IIDR); - ver = (ver >> SBSA_GWDT_VERSION_SHIFT) & SBSA_GWDT_VERSION_MASK; + iidr = readl(gwdt->control_base + SBSA_GWDT_W_IIDR); + ver = (iidr >> SBSA_GWDT_VERSION_SHIFT) & SBSA_GWDT_VERSION_MASK; + impl = (iidr >> SBSA_GWDT_IMPL_SHIFT) & SBSA_GWDT_IMPL_MASK; gwdt->version = ver; + gwdt->need_ws0_race_workaround = + !action && (impl == SBSA_GWDT_IMPL_MEDIATEK); } static int sbsa_gwdt_start(struct watchdog_device *wdd) @@ -299,6 +334,15 @@ static int sbsa_gwdt_probe(struct platform_device *pdev) else wdd->max_hw_heartbeat_ms = GENMASK_ULL(47, 0) / gwdt->clk * 1000; + if (gwdt->need_ws0_race_workaround) { + /* + * A timeout of 3 seconds means that WOR will be set to 1.5 + * seconds and the heartbeat will be scheduled every 2.5 + * seconds. + */ + wdd->min_timeout = 3; + } + status = readl(cf_base + SBSA_GWDT_WCS); if (status & SBSA_GWDT_WCS_WS1) { dev_warn(dev, "System reset by WDT.\n"); diff --git a/drivers/watchdog/watchdog_core.h b/drivers/watchdog/watchdog_core.h index 5b35a8439e26..ab825d9f9248 100644 --- a/drivers/watchdog/watchdog_core.h +++ b/drivers/watchdog/watchdog_core.h @@ -24,8 +24,14 @@ * This material is provided "AS-IS" and at no charge. */ -#include <linux/hrtimer.h> +#include <linux/cdev.h> +#include <linux/device.h> +#include <linux/hrtimer_types.h> +#include <linux/init.h> #include <linux/kthread.h> +#include <linux/mutex_types.h> +#include <linux/types.h> +#include <linux/watchdog.h> #define MAX_DOGS 32 /* Maximum number of watchdog devices */ diff --git a/drivers/watchdog/watchdog_pretimeout.c b/drivers/watchdog/watchdog_pretimeout.c index e5295c990fa1..2526436dc74d 100644 --- a/drivers/watchdog/watchdog_pretimeout.c +++ b/drivers/watchdog/watchdog_pretimeout.c @@ -7,6 +7,8 @@ #include <linux/slab.h> #include <linux/spinlock.h> #include <linux/string.h> +#include <linux/sysfs.h> +#include <linux/types.h> #include <linux/watchdog.h> #include "watchdog_core.h" diff --git a/drivers/watchdog/ziirave_wdt.c b/drivers/watchdog/ziirave_wdt.c index fcc1ba02e75b..5c6e3fa001d8 100644 --- a/drivers/watchdog/ziirave_wdt.c +++ b/drivers/watchdog/ziirave_wdt.c @@ -302,6 +302,9 @@ static int ziirave_firm_verify(struct watchdog_device *wdd, const u16 len = be16_to_cpu(rec->len); const u32 addr = be32_to_cpu(rec->addr); + if (len > sizeof(data)) + return -EINVAL; + if (ziirave_firm_addr_readonly(addr)) continue; diff --git a/include/linux/watchdog.h b/include/linux/watchdog.h index 99660197a36c..8c60687a3e55 100644 --- a/include/linux/watchdog.h +++ b/include/linux/watchdog.h @@ -9,14 +9,18 @@ #ifndef _LINUX_WATCHDOG_H #define _LINUX_WATCHDOG_H - #include <linux/bitops.h> -#include <linux/cdev.h> -#include <linux/device.h> -#include <linux/kernel.h> +#include <linux/limits.h> #include <linux/notifier.h> +#include <linux/printk.h> +#include <linux/types.h> + #include <uapi/linux/watchdog.h> +struct attribute_group; +struct device; +struct module; + struct watchdog_ops; struct watchdog_device; struct watchdog_core_data; |