From bb44aa09e53960c0230a645144fe566e094a2a02 Mon Sep 17 00:00:00 2001 From: Eugen Hristev Date: Mon, 18 Nov 2019 08:50:36 +0000 Subject: watchdog: sama5d4_wdt: addition of sam9x60 compatible watchdog Add support for SAM9X60 WDT into sama5d4_wdt. This means that this driver gets a flag inside the data struct that represents the sam9x60 support. This flag differentiates between the two hardware blocks, and is set according to the compatible of the driver instantiation. Signed-off-by: Eugen Hristev Reviewed-by-off-by: Guenter Roeck Link: https://lore.kernel.org/r/1574067012-18559-3-git-send-email-eugen.hristev@microchip.com Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/at91sam9_wdt.h | 21 ++++++++ drivers/watchdog/sama5d4_wdt.c | 109 +++++++++++++++++++++++++++++++--------- 2 files changed, 105 insertions(+), 25 deletions(-) (limited to 'drivers/watchdog') diff --git a/drivers/watchdog/at91sam9_wdt.h b/drivers/watchdog/at91sam9_wdt.h index abfe34dd760a..298d545df1a1 100644 --- a/drivers/watchdog/at91sam9_wdt.h +++ b/drivers/watchdog/at91sam9_wdt.h @@ -24,7 +24,10 @@ #define AT91_WDT_MR 0x04 /* Watchdog Mode Register */ #define AT91_WDT_WDV (0xfffUL << 0) /* Counter Value */ #define AT91_WDT_SET_WDV(x) ((x) & AT91_WDT_WDV) +#define AT91_SAM9X60_PERIODRST BIT(4) /* Period Reset */ +#define AT91_SAM9X60_RPTHRST BIT(5) /* Minimum Restart Period */ #define AT91_WDT_WDFIEN BIT(12) /* Fault Interrupt Enable */ +#define AT91_SAM9X60_WDDIS BIT(12) /* Watchdog Disable */ #define AT91_WDT_WDRSTEN BIT(13) /* Reset Processor */ #define AT91_WDT_WDRPROC BIT(14) /* Timer Restart */ #define AT91_WDT_WDDIS BIT(15) /* Watchdog Disable */ @@ -37,4 +40,22 @@ #define AT91_WDT_WDUNF BIT(0) /* Watchdog Underflow */ #define AT91_WDT_WDERR BIT(1) /* Watchdog Error */ +/* Watchdog Timer Value Register */ +#define AT91_SAM9X60_VR 0x08 + +/* Watchdog Window Level Register */ +#define AT91_SAM9X60_WLR 0x0c +/* Watchdog Period Value */ +#define AT91_SAM9X60_COUNTER (0xfffUL << 0) +#define AT91_SAM9X60_SET_COUNTER(x) ((x) & AT91_SAM9X60_COUNTER) + +/* Interrupt Enable Register */ +#define AT91_SAM9X60_IER 0x14 +/* Period Interrupt Enable */ +#define AT91_SAM9X60_PERINT BIT(0) +/* Interrupt Disable Register */ +#define AT91_SAM9X60_IDR 0x18 +/* Interrupt Status Register */ +#define AT91_SAM9X60_ISR 0x1c + #endif diff --git a/drivers/watchdog/sama5d4_wdt.c b/drivers/watchdog/sama5d4_wdt.c index d193a60430b2..e5d11d6a2600 100644 --- a/drivers/watchdog/sama5d4_wdt.c +++ b/drivers/watchdog/sama5d4_wdt.c @@ -2,7 +2,7 @@ /* * Driver for Atmel SAMA5D4 Watchdog Timer * - * Copyright (C) 2015 Atmel Corporation + * Copyright (C) 2015-2019 Microchip Technology Inc. and its subsidiaries */ #include @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -29,7 +30,10 @@ struct sama5d4_wdt { struct watchdog_device wdd; void __iomem *reg_base; u32 mr; + u32 ir; unsigned long last_ping; + bool need_irq; + bool sam9x60_support; }; static int wdt_timeout; @@ -78,7 +82,12 @@ static int sama5d4_wdt_start(struct watchdog_device *wdd) { struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd); - wdt->mr &= ~AT91_WDT_WDDIS; + if (wdt->sam9x60_support) { + writel_relaxed(wdt->ir, wdt->reg_base + AT91_SAM9X60_IER); + wdt->mr &= ~AT91_SAM9X60_WDDIS; + } else { + wdt->mr &= ~AT91_WDT_WDDIS; + } wdt_write(wdt, AT91_WDT_MR, wdt->mr); return 0; @@ -88,7 +97,12 @@ static int sama5d4_wdt_stop(struct watchdog_device *wdd) { struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd); - wdt->mr |= AT91_WDT_WDDIS; + if (wdt->sam9x60_support) { + writel_relaxed(wdt->ir, wdt->reg_base + AT91_SAM9X60_IDR); + wdt->mr |= AT91_SAM9X60_WDDIS; + } else { + wdt->mr |= AT91_WDT_WDDIS; + } wdt_write(wdt, AT91_WDT_MR, wdt->mr); return 0; @@ -109,6 +123,14 @@ static int sama5d4_wdt_set_timeout(struct watchdog_device *wdd, struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd); u32 value = WDT_SEC2TICKS(timeout); + if (wdt->sam9x60_support) { + wdt_write(wdt, AT91_SAM9X60_WLR, + AT91_SAM9X60_SET_COUNTER(value)); + + wdd->timeout = timeout; + return 0; + } + wdt->mr &= ~AT91_WDT_WDV; wdt->mr |= AT91_WDT_SET_WDV(value); @@ -143,8 +165,14 @@ static const struct watchdog_ops sama5d4_wdt_ops = { static irqreturn_t sama5d4_wdt_irq_handler(int irq, void *dev_id) { struct sama5d4_wdt *wdt = platform_get_drvdata(dev_id); + u32 reg; - if (wdt_read(wdt, AT91_WDT_SR)) { + if (wdt->sam9x60_support) + reg = wdt_read(wdt, AT91_SAM9X60_ISR); + else + reg = wdt_read(wdt, AT91_WDT_SR); + + if (reg) { pr_crit("Atmel Watchdog Software Reset\n"); emergency_restart(); pr_crit("Reboot didn't succeed\n"); @@ -157,13 +185,14 @@ static int of_sama5d4_wdt_init(struct device_node *np, struct sama5d4_wdt *wdt) { const char *tmp; - wdt->mr = AT91_WDT_WDDIS; + if (wdt->sam9x60_support) + wdt->mr = AT91_SAM9X60_WDDIS; + else + wdt->mr = AT91_WDT_WDDIS; if (!of_property_read_string(np, "atmel,watchdog-type", &tmp) && !strcmp(tmp, "software")) - wdt->mr |= AT91_WDT_WDFIEN; - else - wdt->mr |= AT91_WDT_WDRSTEN; + wdt->need_irq = true; if (of_property_read_bool(np, "atmel,idle-halt")) wdt->mr |= AT91_WDT_WDIDLEHLT; @@ -176,21 +205,46 @@ static int of_sama5d4_wdt_init(struct device_node *np, struct sama5d4_wdt *wdt) static int sama5d4_wdt_init(struct sama5d4_wdt *wdt) { - u32 reg; + u32 reg, val; + + val = WDT_SEC2TICKS(WDT_DEFAULT_TIMEOUT); /* * When booting and resuming, the bootloader may have changed the * watchdog configuration. * If the watchdog is already running, we can safely update it. * Else, we have to disable it properly. */ - if (wdt_enabled) { - wdt_write_nosleep(wdt, AT91_WDT_MR, wdt->mr); - } else { + if (!wdt_enabled) { reg = wdt_read(wdt, AT91_WDT_MR); - if (!(reg & AT91_WDT_WDDIS)) + if (wdt->sam9x60_support && (!(reg & AT91_SAM9X60_WDDIS))) + wdt_write_nosleep(wdt, AT91_WDT_MR, + reg | AT91_SAM9X60_WDDIS); + else if (!wdt->sam9x60_support && + (!(reg & AT91_WDT_WDDIS))) wdt_write_nosleep(wdt, AT91_WDT_MR, reg | AT91_WDT_WDDIS); } + + if (wdt->sam9x60_support) { + if (wdt->need_irq) + wdt->ir = AT91_SAM9X60_PERINT; + else + wdt->mr |= AT91_SAM9X60_PERIODRST; + + wdt_write(wdt, AT91_SAM9X60_IER, wdt->ir); + wdt_write(wdt, AT91_SAM9X60_WLR, AT91_SAM9X60_SET_COUNTER(val)); + } else { + wdt->mr |= AT91_WDT_SET_WDD(WDT_SEC2TICKS(MAX_WDT_TIMEOUT)); + wdt->mr |= AT91_WDT_SET_WDV(val); + + if (wdt->need_irq) + wdt->mr |= AT91_WDT_WDFIEN; + else + wdt->mr |= AT91_WDT_WDRSTEN; + } + + wdt_write_nosleep(wdt, AT91_WDT_MR, wdt->mr); + return 0; } @@ -201,7 +255,6 @@ static int sama5d4_wdt_probe(struct platform_device *pdev) struct sama5d4_wdt *wdt; void __iomem *regs; u32 irq = 0; - u32 timeout; int ret; wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL); @@ -215,6 +268,8 @@ static int sama5d4_wdt_probe(struct platform_device *pdev) wdd->min_timeout = MIN_WDT_TIMEOUT; wdd->max_timeout = MAX_WDT_TIMEOUT; wdt->last_ping = jiffies; + wdt->sam9x60_support = of_device_is_compatible(dev->of_node, + "microchip,sam9x60-wdt"); watchdog_set_drvdata(wdd, wdt); @@ -224,15 +279,19 @@ static int sama5d4_wdt_probe(struct platform_device *pdev) wdt->reg_base = regs; - irq = irq_of_parse_and_map(dev->of_node, 0); - if (!irq) - dev_warn(dev, "failed to get IRQ from DT\n"); - ret = of_sama5d4_wdt_init(dev->of_node, wdt); if (ret) return ret; - if ((wdt->mr & AT91_WDT_WDFIEN) && irq) { + if (wdt->need_irq) { + irq = irq_of_parse_and_map(dev->of_node, 0); + if (!irq) { + dev_warn(dev, "failed to get IRQ from DT\n"); + wdt->need_irq = false; + } + } + + if (wdt->need_irq) { ret = devm_request_irq(dev, irq, sama5d4_wdt_irq_handler, IRQF_SHARED | IRQF_IRQPOLL | IRQF_NO_SUSPEND, pdev->name, pdev); @@ -244,11 +303,6 @@ static int sama5d4_wdt_probe(struct platform_device *pdev) watchdog_init_timeout(wdd, wdt_timeout, dev); - timeout = WDT_SEC2TICKS(wdd->timeout); - - wdt->mr |= AT91_WDT_SET_WDD(WDT_SEC2TICKS(MAX_WDT_TIMEOUT)); - wdt->mr |= AT91_WDT_SET_WDV(timeout); - ret = sama5d4_wdt_init(wdt); if (ret) return ret; @@ -269,7 +323,12 @@ static int sama5d4_wdt_probe(struct platform_device *pdev) } static const struct of_device_id sama5d4_wdt_of_match[] = { - { .compatible = "atmel,sama5d4-wdt", }, + { + .compatible = "atmel,sama5d4-wdt", + }, + { + .compatible = "microchip,sam9x60-wdt", + }, { } }; MODULE_DEVICE_TABLE(of, sama5d4_wdt_of_match); -- cgit From 85fdc63fe256b595f923a69848cd99972ff446d8 Mon Sep 17 00:00:00 2001 From: Christophe Roullier Date: Fri, 22 Nov 2019 14:22:46 +0100 Subject: drivers: watchdog: stm32_iwdg: set WDOG_HW_RUNNING at probe If the watchdog hardware is already enabled during the boot process, when the Linux watchdog driver loads, it should start/reset the watchdog and tell the watchdog framework. As a result, ping can be generated from the watchdog framework (if CONFIG_WATCHDOG_HANDLE_BOOT_ENABLED is set), until the userspace watchdog daemon takes over control Fixes:4332d113c66a ("watchdog: Add STM32 IWDG driver") Signed-off-by: Christophe Roullier Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20191122132246.8473-1-christophe.roullier@st.com Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/stm32_iwdg.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'drivers/watchdog') diff --git a/drivers/watchdog/stm32_iwdg.c b/drivers/watchdog/stm32_iwdg.c index a3a329011a06..25188d6bbe15 100644 --- a/drivers/watchdog/stm32_iwdg.c +++ b/drivers/watchdog/stm32_iwdg.c @@ -262,6 +262,24 @@ static int stm32_iwdg_probe(struct platform_device *pdev) watchdog_set_nowayout(wdd, WATCHDOG_NOWAYOUT); watchdog_init_timeout(wdd, 0, dev); + /* + * In case of CONFIG_WATCHDOG_HANDLE_BOOT_ENABLED is set + * (Means U-Boot/bootloaders leaves the watchdog running) + * When we get here we should make a decision to prevent + * any side effects before user space daemon will take care of it. + * The best option, taking into consideration that there is no + * way to read values back from hardware, is to enforce watchdog + * being run with deterministic values. + */ + if (IS_ENABLED(CONFIG_WATCHDOG_HANDLE_BOOT_ENABLED)) { + ret = stm32_iwdg_start(wdd); + if (ret) + return ret; + + /* Make sure the watchdog is serviced */ + set_bit(WDOG_HW_RUNNING, &wdd->status); + } + ret = devm_watchdog_register_device(dev, wdd); if (ret) return ret; -- cgit From d4ba76d79854178ffff55d66e1a72580484a741d Mon Sep 17 00:00:00 2001 From: "Wang, Peng 1. (NSB - CN/Hangzhou)" Date: Mon, 25 Nov 2019 02:04:13 +0000 Subject: watchdog: make DesignWare watchdog allow users to set bigger timeout value watchdog_dev.c provides means to allow users to set bigger timeout value than HW can support, make DesignWare watchdog align with this. Signed-off-by: Peng Wang Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/8fa54e92c6cd4544a7a3eb60a373ac43@nokia-sbell.com Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/dw_wdt.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'drivers/watchdog') diff --git a/drivers/watchdog/dw_wdt.c b/drivers/watchdog/dw_wdt.c index fef7c61f5555..738eee5c8751 100644 --- a/drivers/watchdog/dw_wdt.c +++ b/drivers/watchdog/dw_wdt.c @@ -114,7 +114,15 @@ static int dw_wdt_set_timeout(struct watchdog_device *wdd, unsigned int top_s) writel(top_val | top_val << WDOG_TIMEOUT_RANGE_TOPINIT_SHIFT, dw_wdt->regs + WDOG_TIMEOUT_RANGE_REG_OFFSET); - wdd->timeout = dw_wdt_top_in_seconds(dw_wdt, top_val); + /* + * In case users set bigger timeout value than HW can support, + * kernel(watchdog_dev.c) helps to feed watchdog before + * wdd->max_hw_heartbeat_ms + */ + if (top_s * 1000 <= wdd->max_hw_heartbeat_ms) + wdd->timeout = dw_wdt_top_in_seconds(dw_wdt, top_val); + else + wdd->timeout = top_s; return 0; } -- cgit From f6c98b08381c774a56b2f0f0067da646c23f1447 Mon Sep 17 00:00:00 2001 From: Marco Felsch Date: Thu, 28 Nov 2019 18:19:31 +0100 Subject: watchdog: da9062: add power management ops Disable the watchdog during suspend if it is enabled and re-enable it on resume. So we can sleep without the interruptions. Signed-off-by: Marco Felsch Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20191128171931.22563-1-m.felsch@pengutronix.de Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/da9062_wdt.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'drivers/watchdog') diff --git a/drivers/watchdog/da9062_wdt.c b/drivers/watchdog/da9062_wdt.c index e149e66a6ea9..2a1e7de25b71 100644 --- a/drivers/watchdog/da9062_wdt.c +++ b/drivers/watchdog/da9062_wdt.c @@ -212,6 +212,7 @@ static int da9062_wdt_probe(struct platform_device *pdev) watchdog_set_restart_priority(&wdt->wdtdev, 128); watchdog_set_drvdata(&wdt->wdtdev, wdt); + dev_set_drvdata(dev, &wdt->wdtdev); ret = devm_watchdog_register_device(dev, &wdt->wdtdev); if (ret < 0) @@ -220,10 +221,34 @@ static int da9062_wdt_probe(struct platform_device *pdev) return da9062_wdt_ping(&wdt->wdtdev); } +static int __maybe_unused da9062_wdt_suspend(struct device *dev) +{ + struct watchdog_device *wdd = dev_get_drvdata(dev); + + if (watchdog_active(wdd)) + return da9062_wdt_stop(wdd); + + return 0; +} + +static int __maybe_unused da9062_wdt_resume(struct device *dev) +{ + struct watchdog_device *wdd = dev_get_drvdata(dev); + + if (watchdog_active(wdd)) + return da9062_wdt_start(wdd); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(da9062_wdt_pm_ops, + da9062_wdt_suspend, da9062_wdt_resume); + static struct platform_driver da9062_wdt_driver = { .probe = da9062_wdt_probe, .driver = { .name = "da9062-watchdog", + .pm = &da9062_wdt_pm_ops, .of_match_table = da9062_compatible_id_table, }, }; -- cgit From e0b4f4e0cf7fa9d62628d4249c765ec18dffd143 Mon Sep 17 00:00:00 2001 From: Sai Prakash Ranjan Date: Fri, 13 Dec 2019 12:19:34 +0530 Subject: watchdog: qcom: Use platform_get_irq_optional() for bark irq platform_get_irq() prints an error message when the interrupt is not available. So on platforms where bark interrupt is not specified, following error message is observed on SDM845. [ 2.975888] qcom_wdt 17980000.watchdog: IRQ index 0 not found This is also seen on SC7180, SM8150 SoCs as well. Fix this by using platform_get_irq_optional() instead. Fixes: 36375491a4395654 ("watchdog: qcom: support pre-timeout when the bark irq is available") Signed-off-by: Sai Prakash Ranjan Reviewed-by: Bjorn Andersson Reviewed-by: Guenter Roeck Reviewed-by: Stephen Boyd Link: https://lore.kernel.org/r/20191213064934.4112-1-saiprakash.ranjan@codeaurora.org Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/qcom-wdt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/watchdog') diff --git a/drivers/watchdog/qcom-wdt.c b/drivers/watchdog/qcom-wdt.c index a494543d3ae1..eb47fe5ed280 100644 --- a/drivers/watchdog/qcom-wdt.c +++ b/drivers/watchdog/qcom-wdt.c @@ -246,7 +246,7 @@ static int qcom_wdt_probe(struct platform_device *pdev) } /* check if there is pretimeout support */ - irq = platform_get_irq(pdev, 0); + irq = platform_get_irq_optional(pdev, 0); if (irq > 0) { ret = devm_request_irq(dev, irq, qcom_wdt_isr, IRQF_TRIGGER_RISING, -- cgit From b1301b9022e9769f3228a353a83bb9623c0f6e41 Mon Sep 17 00:00:00 2001 From: Srinivas Neeli Date: Fri, 20 Dec 2019 12:28:16 +0530 Subject: watchdog: cadence: Skip printing pointer value "%p" is not printing the pointer value. In driver, printing pointer value is not useful so avoiding print. Signed-off-by: Srinivas Neeli Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/1576825096-26605-1-git-send-email-srinivas.neeli@xilinx.com Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/cadence_wdt.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/watchdog') diff --git a/drivers/watchdog/cadence_wdt.c b/drivers/watchdog/cadence_wdt.c index 06bd4e1a5923..672b184da875 100644 --- a/drivers/watchdog/cadence_wdt.c +++ b/drivers/watchdog/cadence_wdt.c @@ -369,9 +369,8 @@ static int cdns_wdt_probe(struct platform_device *pdev) return ret; platform_set_drvdata(pdev, wdt); - dev_info(dev, "Xilinx Watchdog Timer at %p with timeout %ds%s\n", - wdt->regs, cdns_wdt_device->timeout, - nowayout ? ", nowayout" : ""); + dev_info(dev, "Xilinx Watchdog Timer with timeout %ds%s\n", + cdns_wdt_device->timeout, nowayout ? ", nowayout" : ""); return 0; } -- cgit From 69503e585192fdd84b240f18a0873d20e18a2e0a Mon Sep 17 00:00:00 2001 From: Vladis Dronov Date: Wed, 8 Jan 2020 13:53:47 +0100 Subject: watchdog: fix UAF in reboot notifier handling in watchdog core code After the commit 44ea39420fc9 ("drivers/watchdog: make use of devm_register_reboot_notifier()") the struct notifier_block reboot_nb in the struct watchdog_device is removed from the reboot notifiers chain at the time watchdog's chardev is closed. But at least in i6300esb.c case reboot_nb is embedded in the struct esb_dev which can be freed on its device removal and before the chardev is closed, thus UAF at reboot: [ 7.728581] esb_probe: esb_dev.watchdog_device ffff91316f91ab28 ts# uname -r note the address ^^^ 5.5.0-rc5-ae6088-wdog ts# ./openwdog0 & [1] 696 ts# opened /dev/watchdog0, sleeping 10s... ts# echo 1 > /sys/devices/pci0000\:00/0000\:00\:09.0/remove [ 178.086079] devres:rel_nodes: dev ffff91317668a0b0 data ffff91316f91ab28 esb_dev.watchdog_device.reboot_nb memory is freed here ^^^ ts# ...woken up [ 181.459010] devres:rel_nodes: dev ffff913171781000 data ffff913174a1dae8 [ 181.460195] devm_unreg_reboot_notifier: res ffff913174a1dae8 nb ffff91316f91ab78 attempt to use memory already freed ^^^ [ 181.461063] devm_unreg_reboot_notifier: nb->call 6b6b6b6b6b6b6b6b [ 181.461243] devm_unreg_reboot_notifier: nb->next 6b6b6b6b6b6b6b6b freed memory is filled with a slub poison ^^^ [1]+ Done ./openwdog0 ts# reboot [ 229.921862] systemd-shutdown[1]: Rebooting. [ 229.939265] notifier_call_chain: nb ffffffff9c6c2f20 nb->next ffffffff9c6d50c0 [ 229.943080] notifier_call_chain: nb ffffffff9c6d50c0 nb->next 6b6b6b6b6b6b6b6b [ 229.946054] notifier_call_chain: nb 6b6b6b6b6b6b6b6b INVAL [ 229.957584] general protection fault: 0000 [#1] SMP [ 229.958770] CPU: 0 PID: 1 Comm: systemd-shutdow Not tainted 5.5.0-rc5-ae6088-wdog [ 229.960224] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), ... [ 229.963288] RIP: 0010:notifier_call_chain+0x66/0xd0 [ 229.969082] RSP: 0018:ffffb20dc0013d88 EFLAGS: 00010246 [ 229.970812] RAX: 000000000000002e RBX: 6b6b6b6b6b6b6b6b RCX: 00000000000008b3 [ 229.972929] RDX: 0000000000000000 RSI: 0000000000000096 RDI: ffffffff9ccc46ac [ 229.975028] RBP: 0000000000000001 R08: 0000000000000000 R09: 00000000000008b3 [ 229.977039] R10: 0000000000000001 R11: ffffffff9c26c740 R12: 0000000000000000 [ 229.979155] R13: 6b6b6b6b6b6b6b6b R14: 0000000000000000 R15: 00000000fffffffa ... slub_debug=FZP poison ^^^ [ 229.989089] Call Trace: [ 229.990157] blocking_notifier_call_chain+0x43/0x59 [ 229.991401] kernel_restart_prepare+0x14/0x30 [ 229.992607] kernel_restart+0x9/0x30 [ 229.993800] __do_sys_reboot+0x1d2/0x210 [ 230.000149] do_syscall_64+0x3d/0x130 [ 230.001277] entry_SYSCALL_64_after_hwframe+0x44/0xa9 [ 230.002639] RIP: 0033:0x7f5461bdd177 [ 230.016402] Modules linked in: i6300esb [ 230.050261] Kernel panic - not syncing: Attempted to kill init! exitcode=0x0000000b Fix the crash by reverting 44ea39420fc9 so unregister_reboot_notifier() is called when watchdog device is removed. This also makes handling of the reboot notifier unified with the handling of the restart handler, which is freed with unregister_restart_handler() in the same place. Fixes: 44ea39420fc9 ("drivers/watchdog: make use of devm_register_reboot_notifier()") Cc: stable@vger.kernel.org # v4.15+ Signed-off-by: Vladis Dronov Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20200108125347.6067-1-vdronov@redhat.com Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/watchdog_core.c | 35 +++++++++++++++++++++++++++++++++++ drivers/watchdog/watchdog_dev.c | 36 +----------------------------------- 2 files changed, 36 insertions(+), 35 deletions(-) (limited to 'drivers/watchdog') diff --git a/drivers/watchdog/watchdog_core.c b/drivers/watchdog/watchdog_core.c index 21e8085b848b..861daf4f37b2 100644 --- a/drivers/watchdog/watchdog_core.c +++ b/drivers/watchdog/watchdog_core.c @@ -147,6 +147,25 @@ int watchdog_init_timeout(struct watchdog_device *wdd, } EXPORT_SYMBOL_GPL(watchdog_init_timeout); +static int watchdog_reboot_notifier(struct notifier_block *nb, + unsigned long code, void *data) +{ + struct watchdog_device *wdd; + + wdd = container_of(nb, struct watchdog_device, reboot_nb); + if (code == SYS_DOWN || code == SYS_HALT) { + if (watchdog_active(wdd)) { + int ret; + + ret = wdd->ops->stop(wdd); + if (ret) + return NOTIFY_BAD; + } + } + + return NOTIFY_DONE; +} + static int watchdog_restart_notifier(struct notifier_block *nb, unsigned long action, void *data) { @@ -235,6 +254,19 @@ static int __watchdog_register_device(struct watchdog_device *wdd) } } + if (test_bit(WDOG_STOP_ON_REBOOT, &wdd->status)) { + wdd->reboot_nb.notifier_call = watchdog_reboot_notifier; + + ret = register_reboot_notifier(&wdd->reboot_nb); + if (ret) { + pr_err("watchdog%d: Cannot register reboot notifier (%d)\n", + wdd->id, ret); + watchdog_dev_unregister(wdd); + ida_simple_remove(&watchdog_ida, id); + return ret; + } + } + if (wdd->ops->restart) { wdd->restart_nb.notifier_call = watchdog_restart_notifier; @@ -289,6 +321,9 @@ static void __watchdog_unregister_device(struct watchdog_device *wdd) if (wdd->ops->restart) unregister_restart_handler(&wdd->restart_nb); + if (test_bit(WDOG_STOP_ON_REBOOT, &wdd->status)) + unregister_reboot_notifier(&wdd->reboot_nb); + watchdog_dev_unregister(wdd); ida_simple_remove(&watchdog_ida, wdd->id); } diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c index 4b2a85438478..8b5c742f24e8 100644 --- a/drivers/watchdog/watchdog_dev.c +++ b/drivers/watchdog/watchdog_dev.c @@ -38,7 +38,6 @@ #include /* For handling misc devices */ #include /* For module stuff/... */ #include /* For mutexes */ -#include /* For reboot notifier */ #include /* For memory functions */ #include /* For standard types (like size_t) */ #include /* For watchdog specific items */ @@ -1097,25 +1096,6 @@ static void watchdog_cdev_unregister(struct watchdog_device *wdd) put_device(&wd_data->dev); } -static int watchdog_reboot_notifier(struct notifier_block *nb, - unsigned long code, void *data) -{ - struct watchdog_device *wdd; - - wdd = container_of(nb, struct watchdog_device, reboot_nb); - if (code == SYS_DOWN || code == SYS_HALT) { - if (watchdog_active(wdd)) { - int ret; - - ret = wdd->ops->stop(wdd); - if (ret) - return NOTIFY_BAD; - } - } - - return NOTIFY_DONE; -} - /* * watchdog_dev_register: register a watchdog device * @wdd: watchdog device @@ -1134,22 +1114,8 @@ int watchdog_dev_register(struct watchdog_device *wdd) return ret; ret = watchdog_register_pretimeout(wdd); - if (ret) { + if (ret) watchdog_cdev_unregister(wdd); - return ret; - } - - if (test_bit(WDOG_STOP_ON_REBOOT, &wdd->status)) { - wdd->reboot_nb.notifier_call = watchdog_reboot_notifier; - - ret = devm_register_reboot_notifier(&wdd->wd_data->dev, - &wdd->reboot_nb); - if (ret) { - pr_err("watchdog%d: Cannot register reboot notifier (%d)\n", - wdd->id, ret); - watchdog_dev_unregister(wdd); - } - } return ret; } -- cgit From e7046df873548bfc87c0c594ca473226c5d3317b Mon Sep 17 00:00:00 2001 From: Jack Mitchell Date: Tue, 7 Jan 2020 15:51:55 +0000 Subject: watchdog: dw_wdt: ping watchdog to reset countdown before start Currently on an rk3288 SoC when trying to use the watchdog the SoC will instantly reset. This is due to the watchdog countdown counter being set to its initial value of 0x0. Reset the watchdog counter before start in order to correctly start the countdown timer from the right position. Signed-off-by: Jack Mitchell Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20200107155155.278521-1-ml@embed.me.uk Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/dw_wdt.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/watchdog') diff --git a/drivers/watchdog/dw_wdt.c b/drivers/watchdog/dw_wdt.c index 738eee5c8751..fba21de2bbad 100644 --- a/drivers/watchdog/dw_wdt.c +++ b/drivers/watchdog/dw_wdt.c @@ -143,6 +143,7 @@ static int dw_wdt_start(struct watchdog_device *wdd) struct dw_wdt *dw_wdt = to_dw_wdt(wdd); dw_wdt_set_timeout(wdd, wdd->timeout); + dw_wdt_ping(&dw_wdt->wdd); dw_wdt_arm_system_reset(dw_wdt); return 0; -- cgit From 6ae58eecad31362f5caa0bd44ff7e78fbac391dd Mon Sep 17 00:00:00 2001 From: Vincent Prince Date: Thu, 23 Jan 2020 15:05:44 +0100 Subject: watchdog: it87_wdt: add IT8786 ID IT8786 watchdog works as in IT872x Tested on VECOW ECS-9000 board. Signed-off-by: Vincent Prince Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20200123140544.25937-1-vincent.prince.fr@gmail.com Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/it87_wdt.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/watchdog') diff --git a/drivers/watchdog/it87_wdt.c b/drivers/watchdog/it87_wdt.c index a4b71ebc8cab..f3bf3ea50e39 100644 --- a/drivers/watchdog/it87_wdt.c +++ b/drivers/watchdog/it87_wdt.c @@ -67,6 +67,7 @@ #define IT8726_ID 0x8726 /* the data sheet suggest wrongly 0x8716 */ #define IT8728_ID 0x8728 #define IT8783_ID 0x8783 +#define IT8786_ID 0x8786 /* GPIO Configuration Registers LDN=0x07 */ #define WDTCTRL 0x71 @@ -294,6 +295,7 @@ static int __init it87_wdt_init(void) case IT8721_ID: case IT8728_ID: case IT8783_ID: + case IT8786_ID: max_units = 65535; break; case IT8705_ID: -- cgit From c254e103082b74e4f0987c364e5e3b138dbef1cc Mon Sep 17 00:00:00 2001 From: "yong.liang" Date: Wed, 15 Jan 2020 16:58:27 +0800 Subject: watchdog: mtk_wdt: mt8183: Add reset controller Add reset controller API in watchdog driver. Besides watchdog, MTK toprgu module alsa provide sub-system (eg, audio, camera, codec and connectivity) software reset functionality. Signed-off-by: yong.liang Signed-off-by: Jiaxin Yu Reviewed-by: Yingjoe Chen Reviewed-by: Philipp Zabel Reviewed-by: Guenter Roeck Acked-by: Matthias Brugger Link: https://lore.kernel.org/r/20200115085828.27791-4-yong.liang@mediatek.com Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/mtk_wdt.c | 99 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 98 insertions(+), 1 deletion(-) (limited to 'drivers/watchdog') diff --git a/drivers/watchdog/mtk_wdt.c b/drivers/watchdog/mtk_wdt.c index 9c3d0033260d..e88aacb0404d 100644 --- a/drivers/watchdog/mtk_wdt.c +++ b/drivers/watchdog/mtk_wdt.c @@ -9,6 +9,8 @@ * Based on sunxi_wdt.c */ +#include +#include #include #include #include @@ -16,10 +18,11 @@ #include #include #include +#include #include +#include #include #include -#include #define WDT_MAX_TIMEOUT 31 #define WDT_MIN_TIMEOUT 1 @@ -44,6 +47,9 @@ #define WDT_SWRST 0x14 #define WDT_SWRST_KEY 0x1209 +#define WDT_SWSYSRST 0x18U +#define WDT_SWSYS_RST_KEY 0x88000000 + #define DRV_NAME "mtk-wdt" #define DRV_VERSION "1.0" @@ -53,8 +59,90 @@ static unsigned int timeout; struct mtk_wdt_dev { struct watchdog_device wdt_dev; void __iomem *wdt_base; + spinlock_t lock; /* protects WDT_SWSYSRST reg */ + struct reset_controller_dev rcdev; +}; + +struct mtk_wdt_data { + int toprgu_sw_rst_num; }; +static const struct mtk_wdt_data mt8183_data = { + .toprgu_sw_rst_num = MT8183_TOPRGU_SW_RST_NUM, +}; + +static int toprgu_reset_update(struct reset_controller_dev *rcdev, + unsigned long id, bool assert) +{ + unsigned int tmp; + unsigned long flags; + struct mtk_wdt_dev *data = + container_of(rcdev, struct mtk_wdt_dev, rcdev); + + spin_lock_irqsave(&data->lock, flags); + + tmp = readl(data->wdt_base + WDT_SWSYSRST); + if (assert) + tmp |= BIT(id); + else + tmp &= ~BIT(id); + tmp |= WDT_SWSYS_RST_KEY; + writel(tmp, data->wdt_base + WDT_SWSYSRST); + + spin_unlock_irqrestore(&data->lock, flags); + + return 0; +} + +static int toprgu_reset_assert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + return toprgu_reset_update(rcdev, id, true); +} + +static int toprgu_reset_deassert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + return toprgu_reset_update(rcdev, id, false); +} + +static int toprgu_reset(struct reset_controller_dev *rcdev, + unsigned long id) +{ + int ret; + + ret = toprgu_reset_assert(rcdev, id); + if (ret) + return ret; + + return toprgu_reset_deassert(rcdev, id); +} + +static const struct reset_control_ops toprgu_reset_ops = { + .assert = toprgu_reset_assert, + .deassert = toprgu_reset_deassert, + .reset = toprgu_reset, +}; + +static int toprgu_register_reset_controller(struct platform_device *pdev, + int rst_num) +{ + int ret; + struct mtk_wdt_dev *mtk_wdt = platform_get_drvdata(pdev); + + spin_lock_init(&mtk_wdt->lock); + + mtk_wdt->rcdev.owner = THIS_MODULE; + mtk_wdt->rcdev.nr_resets = rst_num; + mtk_wdt->rcdev.ops = &toprgu_reset_ops; + mtk_wdt->rcdev.of_node = pdev->dev.of_node; + ret = devm_reset_controller_register(&pdev->dev, &mtk_wdt->rcdev); + if (ret != 0) + dev_err(&pdev->dev, + "couldn't register wdt reset controller: %d\n", ret); + return ret; +} + static int mtk_wdt_restart(struct watchdog_device *wdt_dev, unsigned long action, void *data) { @@ -155,6 +243,7 @@ static int mtk_wdt_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct mtk_wdt_dev *mtk_wdt; + const struct mtk_wdt_data *wdt_data; int err; mtk_wdt = devm_kzalloc(dev, sizeof(*mtk_wdt), GFP_KERNEL); @@ -190,6 +279,13 @@ static int mtk_wdt_probe(struct platform_device *pdev) dev_info(dev, "Watchdog enabled (timeout=%d sec, nowayout=%d)\n", mtk_wdt->wdt_dev.timeout, nowayout); + wdt_data = of_device_get_match_data(dev); + if (wdt_data) { + err = toprgu_register_reset_controller(pdev, + wdt_data->toprgu_sw_rst_num); + if (err) + return err; + } return 0; } @@ -219,6 +315,7 @@ static int mtk_wdt_resume(struct device *dev) static const struct of_device_id mtk_wdt_dt_ids[] = { { .compatible = "mediatek,mt6589-wdt" }, + { .compatible = "mediatek,mt8183-wdt", .data = &mt8183_data }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, mtk_wdt_dt_ids); -- cgit From 9e5236e7cec110610f3bc824a4d535c1271e4bb5 Mon Sep 17 00:00:00 2001 From: "yong.liang" Date: Wed, 15 Jan 2020 16:58:28 +0800 Subject: watchdog: mtk_wdt: mt2712: Add reset controller Add reset controller for 2712. Besides watchdog, MTK toprgu module alsa provide sub-system (eg, audio, camera, codec and connectivity) software reset functionality. Signed-off-by: yong.liang Signed-off-by: Jiaxin Yu Reviewed-by: Yingjoe Chen Reviewed-by: Philipp Zabel Acked-by: Matthias Brugger Link: https://lore.kernel.org/r/20200115085828.27791-5-yong.liang@mediatek.com Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/mtk_wdt.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/watchdog') diff --git a/drivers/watchdog/mtk_wdt.c b/drivers/watchdog/mtk_wdt.c index e88aacb0404d..d6a6393f609d 100644 --- a/drivers/watchdog/mtk_wdt.c +++ b/drivers/watchdog/mtk_wdt.c @@ -9,6 +9,7 @@ * Based on sunxi_wdt.c */ +#include #include #include #include @@ -67,6 +68,10 @@ struct mtk_wdt_data { int toprgu_sw_rst_num; }; +static const struct mtk_wdt_data mt2712_data = { + .toprgu_sw_rst_num = MT2712_TOPRGU_SW_RST_NUM, +}; + static const struct mtk_wdt_data mt8183_data = { .toprgu_sw_rst_num = MT8183_TOPRGU_SW_RST_NUM, }; @@ -314,6 +319,7 @@ static int mtk_wdt_resume(struct device *dev) #endif static const struct of_device_id mtk_wdt_dt_ids[] = { + { .compatible = "mediatek,mt2712-wdt", .data = &mt2712_data }, { .compatible = "mediatek,mt6589-wdt" }, { .compatible = "mediatek,mt8183-wdt", .data = &mt8183_data }, { /* sentinel */ } -- cgit From 057b52b4b3d58f4ee5944171da50f77b00a1bb0d Mon Sep 17 00:00:00 2001 From: Marco Felsch Date: Wed, 15 Jan 2020 17:23:07 +0100 Subject: watchdog: da9062: make restart handler atomic safe The restart handler is executed during the shutdown phase which is atomic/irq-less. The i2c framework supports atomic transfers since commit 63b96983a5dd ("i2c: core: introduce callbacks for atomic transfers") to address this use case. Using regmap within an atomic context is allowed only if the regmap type is MMIO and the cache type 'flat' or no cache is used. Using the i2c_smbus_write_byte_data() function can be done without additional tests because: 1) the DA9062 is an i2c-only device and 2) the i2c framework emulates the smbus protocol if the host adapter does not support smbus_xfer by using the master_xfer. Signed-off-by: Marco Felsch Reviewed-by: Guenter Roeck Reviewed-by: Stefan Lengfeld Tested-by: Stefan Lengfeld Reviewed-by: Adam Thomson Link: https://lore.kernel.org/r/20200115162307.7336-1-m.felsch@pengutronix.de Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/da9062_wdt.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers/watchdog') diff --git a/drivers/watchdog/da9062_wdt.c b/drivers/watchdog/da9062_wdt.c index 2a1e7de25b71..47eefe072b40 100644 --- a/drivers/watchdog/da9062_wdt.c +++ b/drivers/watchdog/da9062_wdt.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -147,12 +148,13 @@ static int da9062_wdt_restart(struct watchdog_device *wdd, unsigned long action, void *data) { struct da9062_watchdog *wdt = watchdog_get_drvdata(wdd); + struct i2c_client *client = to_i2c_client(wdt->hw->dev); int ret; - ret = regmap_write(wdt->hw->regmap, - DA9062AA_CONTROL_F, - DA9062AA_SHUTDOWN_MASK); - if (ret) + /* Don't use regmap because it is not atomic safe */ + ret = i2c_smbus_write_byte_data(client, DA9062AA_CONTROL_F, + DA9062AA_SHUTDOWN_MASK); + if (ret < 0) dev_alert(wdt->hw->dev, "Failed to shutdown (err = %d)\n", ret); -- cgit