diff options
46 files changed, 527 insertions, 455 deletions
diff --git a/Documentation/ABI/testing/sysfs-class-watchdog b/Documentation/ABI/testing/sysfs-class-watchdog index 585caecda3a5..94fb74615951 100644 --- a/Documentation/ABI/testing/sysfs-class-watchdog +++ b/Documentation/ABI/testing/sysfs-class-watchdog @@ -6,6 +6,19 @@ Description: device at boot. It is equivalent to WDIOC_GETBOOTSTATUS of ioctl interface. +What: /sys/class/watchdog/watchdogn/options +Date: April 2023 +Contact: Thomas Weißschuh +Description: + It is a read only file. It contains options of watchdog device. + +What: /sys/class/watchdog/watchdogn/fw_version +Date: April 2023 +Contact: Thomas Weißschuh +Description: + It is a read only file. It contains firmware version of + watchdog device. + What: /sys/class/watchdog/watchdogn/identity Date: August 2015 Contact: Wim Van Sebroeck <wim@iguana.be> diff --git a/Documentation/devicetree/bindings/timer/qcom,msm-timer.txt b/Documentation/devicetree/bindings/timer/qcom,msm-timer.txt deleted file mode 100644 index 5e10c345548f..000000000000 --- a/Documentation/devicetree/bindings/timer/qcom,msm-timer.txt +++ /dev/null @@ -1,47 +0,0 @@ -* MSM Timer - -Properties: - -- compatible : Should at least contain "qcom,msm-timer". More specific - properties specify which subsystem the timers are paired with. - - "qcom,kpss-timer" - krait subsystem - "qcom,scss-timer" - scorpion subsystem - -- interrupts : Interrupts for the debug timer, the first general purpose - timer, and optionally a second general purpose timer, and - optionally as well, 2 watchdog interrupts, in that order. - -- reg : Specifies the base address of the timer registers. - -- clocks: Reference to the parent clocks, one per output clock. The parents - must appear in the same order as the clock names. - -- clock-names: The name of the clocks as free-form strings. They should be in - the same order as the clocks. - -- clock-frequency : The frequency of the debug timer and the general purpose - timer(s) in Hz in that order. - -Optional: - -- cpu-offset : per-cpu offset used when the timer is accessed without the - CPU remapping facilities. The offset is - cpu-offset + (0x10000 * cpu-nr). - -Example: - - timer@200a000 { - compatible = "qcom,scss-timer", "qcom,msm-timer"; - interrupts = <1 1 0x301>, - <1 2 0x301>, - <1 3 0x301>, - <1 4 0x301>, - <1 5 0x301>; - reg = <0x0200a000 0x100>; - clock-frequency = <19200000>, - <32768>; - clocks = <&sleep_clk>; - clock-names = "sleep"; - cpu-offset = <0x40000>; - }; diff --git a/Documentation/devicetree/bindings/watchdog/amlogic,meson6-wdt.yaml b/Documentation/devicetree/bindings/watchdog/amlogic,meson6-wdt.yaml new file mode 100644 index 000000000000..84732cb58ec4 --- /dev/null +++ b/Documentation/devicetree/bindings/watchdog/amlogic,meson6-wdt.yaml @@ -0,0 +1,50 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/watchdog/amlogic,meson6-wdt.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Amlogic Meson6 SoCs Watchdog timer + +maintainers: + - Neil Armstrong <neil.armstrong@linaro.org> + - Martin Blumenstingl <martin.blumenstingl@googlemail.com> + +allOf: + - $ref: watchdog.yaml# + +properties: + compatible: + oneOf: + - enum: + - amlogic,meson6-wdt + - amlogic,meson8-wdt + - amlogic,meson8b-wdt + - items: + - const: amlogic,meson8m2-wdt + - const: amlogic,meson8b-wdt + + interrupts: + maxItems: 1 + + reg: + maxItems: 1 + +required: + - compatible + - interrupts + - reg + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/irq.h> + #include <dt-bindings/interrupt-controller/arm-gic.h> + + wdt: watchdog@c1109900 { + compatible = "amlogic,meson6-wdt"; + reg = <0xc1109900 0x8>; + interrupts = <GIC_SPI 0 IRQ_TYPE_EDGE_RISING>; + timeout-sec = <10>; + }; diff --git a/Documentation/devicetree/bindings/watchdog/fsl-imx-wdt.yaml b/Documentation/devicetree/bindings/watchdog/fsl-imx-wdt.yaml index fb7695515be1..181f0cc5b5bd 100644 --- a/Documentation/devicetree/bindings/watchdog/fsl-imx-wdt.yaml +++ b/Documentation/devicetree/bindings/watchdog/fsl-imx-wdt.yaml @@ -9,9 +9,6 @@ title: Freescale i.MX Watchdog Timer (WDT) Controller maintainers: - Anson Huang <Anson.Huang@nxp.com> -allOf: - - $ref: "watchdog.yaml#" - properties: compatible: oneOf: @@ -55,11 +52,45 @@ properties: If present, the watchdog device is configured to assert its external reset (WDOG_B) instead of issuing a software reset. + fsl,suspend-in-wait: + $ref: /schemas/types.yaml#/definitions/flag + description: | + If present, the watchdog device is suspended in WAIT mode + (Suspend-to-Idle). Only supported on certain devices. + required: - compatible - interrupts - reg +allOf: + - $ref: watchdog.yaml# + - if: + not: + properties: + compatible: + contains: + enum: + - fsl,imx25-wdt + - fsl,imx35-wdt + - fsl,imx50-wdt + - fsl,imx51-wdt + - fsl,imx53-wdt + - fsl,imx6q-wdt + - fsl,imx6sl-wdt + - fsl,imx6sll-wdt + - fsl,imx6sx-wdt + - fsl,imx6ul-wdt + - fsl,imx7d-wdt + - fsl,imx8mm-wdt + - fsl,imx8mn-wdt + - fsl,imx8mp-wdt + - fsl,imx8mq-wdt + - fsl,vf610-wdt + then: + properties: + fsl,suspend-in-wait: false + unevaluatedProperties: false examples: diff --git a/Documentation/devicetree/bindings/watchdog/gpio-wdt.yaml b/Documentation/devicetree/bindings/watchdog/gpio-wdt.yaml new file mode 100644 index 000000000000..155dc7965e9b --- /dev/null +++ b/Documentation/devicetree/bindings/watchdog/gpio-wdt.yaml @@ -0,0 +1,55 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/watchdog/gpio-wdt.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: GPIO controlled watchdog + +maintainers: + - Robert Marko <robert.marko@sartura.hr> + +properties: + compatible: + const: linux,wdt-gpio + + gpios: + maxItems: 1 + description: GPIO connected to the WDT reset pin + + hw_algo: + $ref: /schemas/types.yaml#/definitions/string + description: Algorithm used by the driver + oneOf: + - description: + Either a high-to-low or a low-to-high transition clears the WDT counter. + The watchdog timer is disabled when GPIO is left floating or connected + to a three-state buffer. + const: toggle + - description: + Low or high level starts counting WDT timeout, the opposite level + disables the WDT. + Active level is determined by the GPIO flags. + const: level + + hw_margin_ms: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Maximum time to reset watchdog circuit (in milliseconds) + minimum: 2 + maximum: 65535 + + always-running: + type: boolean + description: + If the watchdog timer cannot be disabled, add this flag to have the driver + keep toggling the signal without a client. + It will only cease to toggle the signal when the device is open and the + timeout elapsed. + +required: + - compatible + - gpios + - hw_algo + - hw_margin_ms + +unevaluatedProperties: false diff --git a/Documentation/devicetree/bindings/watchdog/mediatek,mt7621-wdt.yaml b/Documentation/devicetree/bindings/watchdog/mediatek,mt7621-wdt.yaml index b2b17fdf4e39..a668d0c2f14b 100644 --- a/Documentation/devicetree/bindings/watchdog/mediatek,mt7621-wdt.yaml +++ b/Documentation/devicetree/bindings/watchdog/mediatek,mt7621-wdt.yaml @@ -19,6 +19,12 @@ properties: reg: maxItems: 1 + mediatek,sysctl: + $ref: /schemas/types.yaml#/definitions/phandle + description: + phandle to system controller 'sysc' syscon node which + controls system registers + required: - compatible - reg @@ -30,4 +36,5 @@ examples: watchdog@100 { compatible = "mediatek,mt7621-wdt"; reg = <0x100 0x100>; + mediatek,sysctl = <&sysc>; }; diff --git a/Documentation/devicetree/bindings/watchdog/mediatek,mtk-wdt.yaml b/Documentation/devicetree/bindings/watchdog/mediatek,mtk-wdt.yaml index b3605608410c..55b34461df1b 100644 --- a/Documentation/devicetree/bindings/watchdog/mediatek,mtk-wdt.yaml +++ b/Documentation/devicetree/bindings/watchdog/mediatek,mtk-wdt.yaml @@ -52,6 +52,12 @@ properties: description: Disable sending output reset signal type: boolean + mediatek,reset-by-toprgu: + description: The Top Reset Generation Unit (TOPRGU) generates reset signals + and distributes them to each IP. If present, the watchdog timer will be + reset by TOPRGU once system resets. + type: boolean + '#reset-cells': const: 1 diff --git a/Documentation/devicetree/bindings/watchdog/meson-wdt.txt b/Documentation/devicetree/bindings/watchdog/meson-wdt.txt deleted file mode 100644 index 7588cc3971bf..000000000000 --- a/Documentation/devicetree/bindings/watchdog/meson-wdt.txt +++ /dev/null @@ -1,21 +0,0 @@ -Meson SoCs Watchdog timer - -Required properties: - -- compatible : depending on the SoC this should be one of: - "amlogic,meson6-wdt" on Meson6 SoCs - "amlogic,meson8-wdt" and "amlogic,meson6-wdt" on Meson8 SoCs - "amlogic,meson8b-wdt" on Meson8b SoCs - "amlogic,meson8m2-wdt" and "amlogic,meson8b-wdt" on Meson8m2 SoCs -- reg : Specifies base physical address and size of the registers. - -Optional properties: -- timeout-sec: contains the watchdog timeout in seconds. - -Example: - -wdt: watchdog@c1109900 { - compatible = "amlogic,meson6-wdt"; - reg = <0xc1109900 0x8>; - timeout-sec = <10>; -}; diff --git a/Documentation/devicetree/bindings/watchdog/qcom-wdt.yaml b/Documentation/devicetree/bindings/watchdog/qcom-wdt.yaml index d8ac0be36e6c..6448b633c970 100644 --- a/Documentation/devicetree/bindings/watchdog/qcom-wdt.yaml +++ b/Documentation/devicetree/bindings/watchdog/qcom-wdt.yaml @@ -9,15 +9,18 @@ title: Qualcomm Krait Processor Sub-system (KPSS) Watchdog timer maintainers: - Sai Prakash Ranjan <saiprakash.ranjan@codeaurora.org> -allOf: - - $ref: watchdog.yaml# - properties: + $nodename: + pattern: "^(watchdog|timer)@[0-9a-f]+$" + compatible: oneOf: - items: - enum: + - qcom,kpss-wdt-ipq4019 + - qcom,apss-wdt-msm8994 - qcom,apss-wdt-qcs404 + - qcom,apss-wdt-sa8775p - qcom,apss-wdt-sc7180 - qcom,apss-wdt-sc7280 - qcom,apss-wdt-sc8180x @@ -29,15 +32,19 @@ properties: - qcom,apss-wdt-sm8150 - qcom,apss-wdt-sm8250 - const: qcom,kpss-wdt + - const: qcom,kpss-wdt + deprecated: true + - items: + - const: qcom,scss-timer + - const: qcom,msm-timer - items: - enum: - - qcom,kpss-wdt - - qcom,kpss-timer - qcom,kpss-wdt-apq8064 - - qcom,kpss-wdt-ipq4019 - qcom,kpss-wdt-ipq8064 + - qcom,kpss-wdt-mdm9615 - qcom,kpss-wdt-msm8960 - - qcom,scss-timer + - const: qcom,kpss-timer + - const: qcom,msm-timer reg: maxItems: 1 @@ -45,18 +52,87 @@ properties: clocks: maxItems: 1 + clock-names: + items: + - const: sleep + + clock-frequency: + description: + The frequency of the general purpose timer in Hz. + + cpu-offset: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + Per-CPU offset used when the timer is accessed without the CPU remapping + facilities. The offset is cpu-offset + (0x10000 * cpu-nr). + + interrupts: + minItems: 1 + maxItems: 5 + required: - compatible - reg - clocks +allOf: + - $ref: watchdog.yaml# + + - if: + properties: + compatible: + contains: + const: qcom,kpss-wdt + then: + properties: + clock-frequency: false + cpu-offset: false + interrupts: + minItems: 1 + items: + - description: Bark + - description: Bite + + else: + properties: + interrupts: + minItems: 3 + items: + - description: Debug + - description: First general purpose timer + - description: Second general purpose timer + - description: First watchdog + - description: Second watchdog + required: + - clock-frequency + unevaluatedProperties: false examples: - | - watchdog@208a038 { - compatible = "qcom,kpss-wdt-ipq8064"; - reg = <0x0208a038 0x40>; + #include <dt-bindings/interrupt-controller/arm-gic.h> + + watchdog@17c10000 { + compatible = "qcom,apss-wdt-sm8150", "qcom,kpss-wdt"; + reg = <0x17c10000 0x1000>; clocks = <&sleep_clk>; + interrupts = <GIC_SPI 0 IRQ_TYPE_LEVEL_HIGH>; timeout-sec = <10>; }; + + - | + #include <dt-bindings/interrupt-controller/arm-gic.h> + + watchdog@200a000 { + compatible = "qcom,kpss-wdt-ipq8064", "qcom,kpss-timer", "qcom,msm-timer"; + interrupts = <GIC_PPI 1 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_EDGE_RISING)>, + <GIC_PPI 2 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_EDGE_RISING)>, + <GIC_PPI 3 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_EDGE_RISING)>, + <GIC_PPI 4 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_EDGE_RISING)>, + <GIC_PPI 5 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_EDGE_RISING)>; + reg = <0x0200a000 0x100>; + clock-frequency = <25000000>; + clocks = <&sleep_clk>; + clock-names = "sleep"; + cpu-offset = <0x80000>; + }; diff --git a/Documentation/devicetree/bindings/watchdog/renesas,wdt.yaml b/Documentation/devicetree/bindings/watchdog/renesas,wdt.yaml index 26b1815a6753..e2c9bf1aec38 100644 --- a/Documentation/devicetree/bindings/watchdog/renesas,wdt.yaml +++ b/Documentation/devicetree/bindings/watchdog/renesas,wdt.yaml @@ -26,7 +26,7 @@ properties: - items: - enum: - - renesas,r9a07g043-wdt # RZ/G2UL + - renesas,r9a07g043-wdt # RZ/G2UL and RZ/Five - renesas,r9a07g044-wdt # RZ/G2{L,LC} - renesas,r9a07g054-wdt # RZ/V2L - const: renesas,rzg2l-wdt diff --git a/Documentation/devicetree/bindings/watchdog/watchdog.yaml b/Documentation/devicetree/bindings/watchdog/watchdog.yaml index fccae0d00110..519b48889eb1 100644 --- a/Documentation/devicetree/bindings/watchdog/watchdog.yaml +++ b/Documentation/devicetree/bindings/watchdog/watchdog.yaml @@ -14,9 +14,14 @@ description: | This document describes generic bindings which can be used to describe watchdog devices in a device tree. +select: + properties: + $nodename: + pattern: "^watchdog(@.*|-[0-9a-f])?$" + properties: $nodename: - pattern: "^watchdog(@.*|-[0-9a-f])?$" + pattern: "^(timer|watchdog)(@.*|-[0-9a-f])?$" timeout-sec: description: diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 5de74686f12b..f0872970daf9 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -1871,7 +1871,9 @@ config GXP_WATCHDOG config MT7621_WDT tristate "Mediatek SoC watchdog" select WATCHDOG_CORE - depends on SOC_MT7620 || SOC_MT7621 + select REGMAP_MMIO + select MFD_SYSCON + depends on SOC_MT7620 || SOC_MT7621 || COMPILE_TEST help Hardware driver for the Mediatek/Ralink MT7621/8 SoC Watchdog Timer. diff --git a/drivers/watchdog/apple_wdt.c b/drivers/watchdog/apple_wdt.c index 16aca21f13d6..eddeb0fede89 100644 --- a/drivers/watchdog/apple_wdt.c +++ b/drivers/watchdog/apple_wdt.c @@ -136,11 +136,6 @@ static int apple_wdt_restart(struct watchdog_device *wdd, unsigned long mode, return 0; } -static void apple_wdt_clk_disable_unprepare(void *data) -{ - clk_disable_unprepare(data); -} - static struct watchdog_ops apple_wdt_ops = { .owner = THIS_MODULE, .start = apple_wdt_start, @@ -162,7 +157,6 @@ static int apple_wdt_probe(struct platform_device *pdev) struct apple_wdt *wdt; struct clk *clk; u32 wdt_ctrl; - int ret; wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL); if (!wdt) @@ -172,19 +166,9 @@ static int apple_wdt_probe(struct platform_device *pdev) if (IS_ERR(wdt->regs)) return PTR_ERR(wdt->regs); - clk = devm_clk_get(dev, NULL); + clk = devm_clk_get_enabled(dev, NULL); if (IS_ERR(clk)) return PTR_ERR(clk); - - ret = clk_prepare_enable(clk); - if (ret) - return ret; - - ret = devm_add_action_or_reset(dev, apple_wdt_clk_disable_unprepare, - clk); - if (ret) - return ret; - wdt->clk_rate = clk_get_rate(clk); if (!wdt->clk_rate) return -EINVAL; diff --git a/drivers/watchdog/armada_37xx_wdt.c b/drivers/watchdog/armada_37xx_wdt.c index ac9fed1ef681..e58652939f8a 100644 --- a/drivers/watchdog/armada_37xx_wdt.c +++ b/drivers/watchdog/armada_37xx_wdt.c @@ -246,11 +246,6 @@ static const struct watchdog_ops armada_37xx_wdt_ops = { .get_timeleft = armada_37xx_wdt_get_timeleft, }; -static void armada_clk_disable_unprepare(void *data) -{ - clk_disable_unprepare(data); -} - static int armada_37xx_wdt_probe(struct platform_device *pdev) { struct armada_37xx_watchdog *dev; @@ -280,18 +275,10 @@ static int armada_37xx_wdt_probe(struct platform_device *pdev) return -ENOMEM; /* init clock */ - dev->clk = devm_clk_get(&pdev->dev, NULL); + dev->clk = devm_clk_get_enabled(&pdev->dev, NULL); if (IS_ERR(dev->clk)) return PTR_ERR(dev->clk); - ret = clk_prepare_enable(dev->clk); - if (ret) - return ret; - ret = devm_add_action_or_reset(&pdev->dev, - armada_clk_disable_unprepare, dev->clk); - if (ret) - return ret; - dev->clk_rate = clk_get_rate(dev->clk); if (!dev->clk_rate) return -EINVAL; diff --git a/drivers/watchdog/aspeed_wdt.c b/drivers/watchdog/aspeed_wdt.c index 86b5331bc491..c1e79874a2bb 100644 --- a/drivers/watchdog/aspeed_wdt.c +++ b/drivers/watchdog/aspeed_wdt.c @@ -10,6 +10,7 @@ #include <linux/interrupt.h> #include <linux/io.h> #include <linux/kernel.h> +#include <linux/kstrtox.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_irq.h> diff --git a/drivers/watchdog/at91rm9200_wdt.c b/drivers/watchdog/at91rm9200_wdt.c index 5126454bb861..d57409c1a4d1 100644 --- a/drivers/watchdog/at91rm9200_wdt.c +++ b/drivers/watchdog/at91rm9200_wdt.c @@ -270,7 +270,7 @@ static int at91wdt_remove(struct platform_device *pdev) misc_deregister(&at91wdt_miscdev); at91wdt_miscdev.parent = NULL; - return res; + return 0; } static void at91wdt_shutdown(struct platform_device *pdev) diff --git a/drivers/watchdog/at91sam9_wdt.c b/drivers/watchdog/at91sam9_wdt.c index 292b5a1ca831..fed7be246442 100644 --- a/drivers/watchdog/at91sam9_wdt.c +++ b/drivers/watchdog/at91sam9_wdt.c @@ -206,10 +206,9 @@ static int at91_wdt_init(struct platform_device *pdev, struct at91wdt *wdt) "min heartbeat and max heartbeat might be too close for the system to handle it correctly\n"); if ((tmp & AT91_WDT_WDFIEN) && wdt->irq) { - err = request_irq(wdt->irq, wdt_interrupt, - IRQF_SHARED | IRQF_IRQPOLL | - IRQF_NO_SUSPEND, - pdev->name, wdt); + err = devm_request_irq(dev, wdt->irq, wdt_interrupt, + IRQF_SHARED | IRQF_IRQPOLL | IRQF_NO_SUSPEND, + pdev->name, wdt); if (err) return err; } diff --git a/drivers/watchdog/bcm7038_wdt.c b/drivers/watchdog/bcm7038_wdt.c index 9388838899ac..e038dd66b819 100644 --- a/drivers/watchdog/bcm7038_wdt.c +++ b/drivers/watchdog/bcm7038_wdt.c @@ -127,11 +127,6 @@ static const struct watchdog_ops bcm7038_wdt_ops = { .get_timeleft = bcm7038_wdt_get_timeleft, }; -static void bcm7038_clk_disable_unprepare(void *data) -{ - clk_disable_unprepare(data); -} - static int bcm7038_wdt_probe(struct platform_device *pdev) { struct bcm7038_wdt_platform_data *pdata = pdev->dev.platform_data; @@ -153,17 +148,9 @@ static int bcm7038_wdt_probe(struct platform_device *pdev) if (pdata && pdata->clk_name) clk_name = pdata->clk_name; - wdt->clk = devm_clk_get(dev, clk_name); + wdt->clk = devm_clk_get_enabled(dev, clk_name); /* If unable to get clock, use default frequency */ if (!IS_ERR(wdt->clk)) { - err = clk_prepare_enable(wdt->clk); - if (err) - return err; - err = devm_add_action_or_reset(dev, - bcm7038_clk_disable_unprepare, - wdt->clk); - if (err) - return err; wdt->rate = clk_get_rate(wdt->clk); /* Prevent divide-by-zero exception */ if (!wdt->rate) diff --git a/drivers/watchdog/cadence_wdt.c b/drivers/watchdog/cadence_wdt.c index bc99e9164930..23d41043863f 100644 --- a/drivers/watchdog/cadence_wdt.c +++ b/drivers/watchdog/cadence_wdt.c @@ -274,11 +274,6 @@ static const struct watchdog_ops cdns_wdt_ops = { .set_timeout = cdns_wdt_settimeout, }; -static void cdns_clk_disable_unprepare(void *data) -{ - clk_disable_unprepare(data); -} - /************************Platform Operations*****************************/ /** * cdns_wdt_probe - Probe call for the device. @@ -333,21 +328,11 @@ static int cdns_wdt_probe(struct platform_device *pdev) watchdog_stop_on_reboot(cdns_wdt_device); watchdog_set_drvdata(cdns_wdt_device, wdt); - wdt->clk = devm_clk_get(dev, NULL); + wdt->clk = devm_clk_get_enabled(dev, NULL); if (IS_ERR(wdt->clk)) return dev_err_probe(dev, PTR_ERR(wdt->clk), "input clock not found\n"); - ret = clk_prepare_enable(wdt->clk); - if (ret) { - dev_err(dev, "unable to enable clock\n"); - return ret; - } - ret = devm_add_action_or_reset(dev, cdns_clk_disable_unprepare, - wdt->clk); - if (ret) - return ret; - clock_f = clk_get_rate(wdt->clk); if (clock_f <= CDNS_WDT_CLK_75MHZ) { wdt->prescaler = CDNS_WDT_PRESCALE_512; diff --git a/drivers/watchdog/da9062_wdt.c b/drivers/watchdog/da9062_wdt.c index f02cbd530538..426962547df1 100644 --- a/drivers/watchdog/da9062_wdt.c +++ b/drivers/watchdog/da9062_wdt.c @@ -155,11 +155,20 @@ static int da9062_wdt_restart(struct watchdog_device *wdd, unsigned long action, { struct da9062_watchdog *wdt = watchdog_get_drvdata(wdd); struct i2c_client *client = to_i2c_client(wdt->hw->dev); + union i2c_smbus_data msg; int ret; - /* Don't use regmap because it is not atomic safe */ - ret = i2c_smbus_write_byte_data(client, DA9062AA_CONTROL_F, - DA9062AA_SHUTDOWN_MASK); + /* + * Don't use regmap because it is not atomic safe. Additionally, use + * unlocked flavor of i2c_smbus_xfer to avoid scenario where i2c bus + * might be previously locked by some process unable to release the + * lock due to interrupts already being disabled at this late stage. + */ + msg.byte = DA9062AA_SHUTDOWN_MASK; + ret = __i2c_smbus_xfer(client->adapter, client->addr, client->flags, + I2C_SMBUS_WRITE, DA9062AA_CONTROL_F, + I2C_SMBUS_BYTE_DATA, &msg); + if (ret < 0) dev_alert(wdt->hw->dev, "Failed to shutdown (err = %d)\n", ret); diff --git a/drivers/watchdog/da9063_wdt.c b/drivers/watchdog/da9063_wdt.c index 09a4af4c58fc..684667469b10 100644 --- a/drivers/watchdog/da9063_wdt.c +++ b/drivers/watchdog/da9063_wdt.c @@ -174,11 +174,20 @@ static int da9063_wdt_restart(struct watchdog_device *wdd, unsigned long action, { struct da9063 *da9063 = watchdog_get_drvdata(wdd); struct i2c_client *client = to_i2c_client(da9063->dev); + union i2c_smbus_data msg; int ret; - /* Don't use regmap because it is not atomic safe */ - ret = i2c_smbus_write_byte_data(client, DA9063_REG_CONTROL_F, - DA9063_SHUTDOWN); + /* + * Don't use regmap because it is not atomic safe. Additionally, use + * unlocked flavor of i2c_smbus_xfer to avoid scenario where i2c bus + * might previously be locked by some process unable to release the + * lock due to interrupts already being disabled at this late stage. + */ + msg.byte = DA9063_SHUTDOWN; + ret = __i2c_smbus_xfer(client->adapter, client->addr, client->flags, + I2C_SMBUS_WRITE, DA9063_REG_CONTROL_F, + I2C_SMBUS_BYTE_DATA, &msg); + if (ret < 0) dev_alert(da9063->dev, "Failed to shutdown (err = %d)\n", ret); diff --git a/drivers/watchdog/davinci_wdt.c b/drivers/watchdog/davinci_wdt.c index 584a56893b81..5f2184bda7b2 100644 --- a/drivers/watchdog/davinci_wdt.c +++ b/drivers/watchdog/davinci_wdt.c @@ -189,14 +189,8 @@ static const struct watchdog_ops davinci_wdt_ops = { .restart = davinci_wdt_restart, }; -static void davinci_clk_disable_unprepare(void *data) -{ - clk_disable_unprepare(data); -} - static int davinci_wdt_probe(struct platform_device *pdev) { - int ret = 0; struct device *dev = &pdev->dev; struct watchdog_device *wdd; struct davinci_wdt_device *davinci_wdt; @@ -205,21 +199,11 @@ static int davinci_wdt_probe(struct platform_device *pdev) if (!davinci_wdt) return -ENOMEM; - davinci_wdt->clk = devm_clk_get(dev, NULL); + davinci_wdt->clk = devm_clk_get_enabled(dev, NULL); if (IS_ERR(davinci_wdt->clk)) return dev_err_probe(dev, PTR_ERR(davinci_wdt->clk), "failed to get clock node\n"); - ret = clk_prepare_enable(davinci_wdt->clk); - if (ret) { - dev_err(dev, "failed to prepare clock\n"); - return ret; - } - ret = devm_add_action_or_reset(dev, davinci_clk_disable_unprepare, - davinci_wdt->clk); - if (ret) - return ret; - platform_set_drvdata(pdev, davinci_wdt); wdd = &davinci_wdt->wdd; diff --git a/drivers/watchdog/dw_wdt.c b/drivers/watchdog/dw_wdt.c index 52962e8d11a6..462f15bd5ffa 100644 --- a/drivers/watchdog/dw_wdt.c +++ b/drivers/watchdog/dw_wdt.c @@ -663,6 +663,7 @@ static int dw_wdt_drv_probe(struct platform_device *pdev) platform_set_drvdata(pdev, dw_wdt); watchdog_set_restart_priority(wdd, 128); + watchdog_stop_on_reboot(wdd); ret = watchdog_register_device(wdd); if (ret) diff --git a/drivers/watchdog/iTCO_wdt.c b/drivers/watchdog/iTCO_wdt.c index e937b4dd28be..264857d314da 100644 --- a/drivers/watchdog/iTCO_wdt.c +++ b/drivers/watchdog/iTCO_wdt.c @@ -441,11 +441,10 @@ static bool iTCO_wdt_set_running(struct iTCO_wdt_private *p) * Kernel Interfaces */ -static const struct watchdog_info ident = { +static struct watchdog_info ident = { .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, - .firmware_version = 0, .identity = DRV_NAME, }; @@ -563,6 +562,7 @@ static int iTCO_wdt_probe(struct platform_device *pdev) break; } + ident.firmware_version = p->iTCO_version; p->wddev.info = &ident, p->wddev.ops = &iTCO_wdt_ops, p->wddev.bootstatus = 0; diff --git a/drivers/watchdog/imgpdc_wdt.c b/drivers/watchdog/imgpdc_wdt.c index b57ff3787052..a55f801895d4 100644 --- a/drivers/watchdog/imgpdc_wdt.c +++ b/drivers/watchdog/imgpdc_wdt.c @@ -175,16 +175,11 @@ static const struct watchdog_ops pdc_wdt_ops = { .restart = pdc_wdt_restart, }; -static void pdc_clk_disable_unprepare(void *data) -{ - clk_disable_unprepare(data); -} - static int pdc_wdt_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; u64 div; - int ret, val; + int val; unsigned long clk_rate; struct pdc_wdt_dev *pdc_wdt; @@ -196,38 +191,18 @@ static int pdc_wdt_probe(struct platform_device *pdev) if (IS_ERR(pdc_wdt->base)) return PTR_ERR(pdc_wdt->base); - pdc_wdt->sys_clk = devm_clk_get(dev, "sys"); + pdc_wdt->sys_clk = devm_clk_get_enabled(dev, "sys"); if (IS_ERR(pdc_wdt->sys_clk)) { dev_err(dev, "failed to get the sys clock\n"); return PTR_ERR(pdc_wdt->sys_clk); } - pdc_wdt->wdt_clk = devm_clk_get(dev, "wdt"); + pdc_wdt->wdt_clk = devm_clk_get_enabled(dev, "wdt"); if (IS_ERR(pdc_wdt->wdt_clk)) { dev_err(dev, "failed to get the wdt clock\n"); return PTR_ERR(pdc_wdt->wdt_clk); } - ret = clk_prepare_enable(pdc_wdt->sys_clk); - if (ret) { - dev_err(dev, "could not prepare or enable sys clock\n"); - return ret; - } - ret = devm_add_action_or_reset(dev, pdc_clk_disable_unprepare, - pdc_wdt->sys_clk); - if (ret) - return ret; - - ret = clk_prepare_enable(pdc_wdt->wdt_clk); - if (ret) { - dev_err(dev, "could not prepare or enable wdt clock\n"); - return ret; - } - ret = devm_add_action_or_reset(dev, pdc_clk_disable_unprepare, - pdc_wdt->wdt_clk); - if (ret) - return ret; - /* We use the clock rate to calculate the max timeout */ clk_rate = clk_get_rate(pdc_wdt->wdt_clk); if (clk_rate == 0) { diff --git a/drivers/watchdog/imx2_wdt.c b/drivers/watchdog/imx2_wdt.c index d0c5d47ddede..19ab7b3d286b 100644 --- a/drivers/watchdog/imx2_wdt.c +++ b/drivers/watchdog/imx2_wdt.c @@ -27,6 +27,7 @@ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/of_address.h> +#include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/regmap.h> #include <linux/watchdog.h> @@ -35,6 +36,7 @@ #define IMX2_WDT_WCR 0x00 /* Control Register */ #define IMX2_WDT_WCR_WT (0xFF << 8) /* -> Watchdog Timeout Field */ +#define IMX2_WDT_WCR_WDW BIT(7) /* -> Watchdog disable for WAIT */ #define IMX2_WDT_WCR_WDA BIT(5) /* -> External Reset WDOG_B */ #define IMX2_WDT_WCR_SRS BIT(4) /* -> Software Reset Signal */ #define IMX2_WDT_WCR_WRE BIT(3) /* -> WDOG Reset Enable */ @@ -60,13 +62,19 @@ #define WDOG_SEC_TO_COUNT(s) ((s * 2 - 1) << 8) +struct imx2_wdt_data { + bool wdw_supported; +}; + struct imx2_wdt_device { struct clk *clk; struct regmap *regmap; struct watchdog_device wdog; + const struct imx2_wdt_data *data; bool ext_reset; bool clk_is_on; bool no_ping; + bool sleep_wait; }; static bool nowayout = WATCHDOG_NOWAYOUT; @@ -129,6 +137,9 @@ static inline void imx2_wdt_setup(struct watchdog_device *wdog) /* Suspend timer in low power mode, write once-only */ val |= IMX2_WDT_WCR_WDZST; + /* Suspend timer in low power WAIT mode, write once-only */ + if (wdev->sleep_wait) + val |= IMX2_WDT_WCR_WDW; /* Strip the old watchdog Time-Out value */ val &= ~IMX2_WDT_WCR_WT; /* Generate internal chip-level reset if WDOG times out */ @@ -292,6 +303,8 @@ static int __init imx2_wdt_probe(struct platform_device *pdev) wdog->max_hw_heartbeat_ms = IMX2_WDT_MAX_TIME * 1000; wdog->parent = dev; + wdev->data = of_device_get_match_data(dev); + ret = platform_get_irq(pdev, 0); if (ret > 0) if (!devm_request_irq(dev, ret, imx2_wdt_isr, 0, @@ -313,9 +326,18 @@ static int __init imx2_wdt_probe(struct platform_device *pdev) wdev->ext_reset = of_property_read_bool(dev->of_node, "fsl,ext-reset-output"); + + if (of_property_read_bool(dev->of_node, "fsl,suspend-in-wait")) { + if (!wdev->data->wdw_supported) { + dev_err(dev, "suspend-in-wait not supported\n"); + return -EINVAL; + } + wdev->sleep_wait = true; + } + /* * The i.MX7D doesn't support low power mode, so we need to ping the watchdog - * during suspend. + * during suspend. Interaction with "fsl,suspend-in-wait" is unknown! */ wdev->no_ping = !of_device_is_compatible(dev->of_node, "fsl,imx7d-wdt"); platform_set_drvdata(pdev, wdog); @@ -417,9 +439,36 @@ static int __maybe_unused imx2_wdt_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(imx2_wdt_pm_ops, imx2_wdt_suspend, imx2_wdt_resume); +struct imx2_wdt_data imx_wdt = { + .wdw_supported = true, +}; + +struct imx2_wdt_data imx_wdt_legacy = { + .wdw_supported = false, +}; + static const struct of_device_id imx2_wdt_dt_ids[] = { - { .compatible = "fsl,imx21-wdt", }, - { .compatible = "fsl,imx7d-wdt", }, + { .compatible = "fsl,imx21-wdt", .data = &imx_wdt_legacy }, + { .compatible = "fsl,imx25-wdt", .data = &imx_wdt }, + { .compatible = "fsl,imx27-wdt", .data = &imx_wdt_legacy }, + { .compatible = "fsl,imx31-wdt", .data = &imx_wdt_legacy }, + { .compatible = "fsl,imx35-wdt", .data = &imx_wdt }, + { .compatible = "fsl,imx50-wdt", .data = &imx_wdt }, + { .compatible = "fsl,imx51-wdt", .data = &imx_wdt }, + { .compatible = "fsl,imx53-wdt", .data = &imx_wdt }, + { .compatible = "fsl,imx6q-wdt", .data = &imx_wdt }, + { .compatible = "fsl,imx6sl-wdt", .data = &imx_wdt }, + { .compatible = "fsl,imx6sll-wdt", .data = &imx_wdt }, + { .compatible = "fsl,imx6sx-wdt", .data = &imx_wdt }, + { .compatible = "fsl,imx6ul-wdt", .data = &imx_wdt }, + { .compatible = "fsl,imx7d-wdt", .data = &imx_wdt }, + { .compatible = "fsl,imx8mm-wdt", .data = &imx_wdt }, + { .compatible = "fsl,imx8mn-wdt", .data = &imx_wdt }, + { .compatible = "fsl,imx8mp-wdt", .data = &imx_wdt }, + { .compatible = "fsl,imx8mq-wdt", .data = &imx_wdt }, + { .compatible = "fsl,ls1012a-wdt", .data = &imx_wdt_legacy }, + { .compatible = "fsl,ls1043a-wdt", .data = &imx_wdt_legacy }, + { .compatible = "fsl,vf610-wdt", .data = &imx_wdt }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, imx2_wdt_dt_ids); diff --git a/drivers/watchdog/imx7ulp_wdt.c b/drivers/watchdog/imx7ulp_wdt.c index 2897902090b3..7ca486794ba7 100644 --- a/drivers/watchdog/imx7ulp_wdt.c +++ b/drivers/watchdog/imx7ulp_wdt.c @@ -299,11 +299,6 @@ static int imx7ulp_wdt_init(struct imx7ulp_wdt_device *wdt, unsigned int timeout return ret; } -static void imx7ulp_wdt_action(void *data) -{ - clk_disable_unprepare(data); -} - static int imx7ulp_wdt_probe(struct platform_device *pdev) { struct imx7ulp_wdt_device *imx7ulp_wdt; @@ -321,7 +316,7 @@ static int imx7ulp_wdt_probe(struct platform_device *pdev) if (IS_ERR(imx7ulp_wdt->base)) return PTR_ERR(imx7ulp_wdt->base); - imx7ulp_wdt->clk = devm_clk_get(dev, NULL); + imx7ulp_wdt->clk = devm_clk_get_enabled(dev, NULL); if (IS_ERR(imx7ulp_wdt->clk)) { dev_err(dev, "Failed to get watchdog clock\n"); return PTR_ERR(imx7ulp_wdt->clk); @@ -336,14 +331,6 @@ static int imx7ulp_wdt_probe(struct platform_device *pdev) dev_info(dev, "imx7ulp wdt probe\n"); } - ret = clk_prepare_enable(imx7ulp_wdt->clk); - if (ret) - return ret; - - ret = devm_add_action_or_reset(dev, imx7ulp_wdt_action, imx7ulp_wdt->clk); - if (ret) - return ret; - wdog = &imx7ulp_wdt->wdd; wdog->info = &imx7ulp_wdt_info; wdog->ops = &imx7ulp_wdt_ops; diff --git a/drivers/watchdog/lpc18xx_wdt.c b/drivers/watchdog/lpc18xx_wdt.c index 60b6d74f267d..1b9b5f21a0df 100644 --- a/drivers/watchdog/lpc18xx_wdt.c +++ b/drivers/watchdog/lpc18xx_wdt.c @@ -197,16 +197,10 @@ static const struct watchdog_ops lpc18xx_wdt_ops = { .restart = lpc18xx_wdt_restart, }; -static void lpc18xx_clk_disable_unprepare(void *data) -{ - clk_disable_unprepare(data); -} - static int lpc18xx_wdt_probe(struct platform_device *pdev) { struct lpc18xx_wdt_dev *lpc18xx_wdt; struct device *dev = &pdev->dev; - int ret; lpc18xx_wdt = devm_kzalloc(dev, sizeof(*lpc18xx_wdt), GFP_KERNEL); if (!lpc18xx_wdt) @@ -216,38 +210,18 @@ static int lpc18xx_wdt_probe(struct platform_device *pdev) if (IS_ERR(lpc18xx_wdt->base)) return PTR_ERR(lpc18xx_wdt->base); - lpc18xx_wdt->reg_clk = devm_clk_get(dev, "reg"); + lpc18xx_wdt->reg_clk = devm_clk_get_enabled(dev, "reg"); if (IS_ERR(lpc18xx_wdt->reg_clk)) { dev_err(dev, "failed to get the reg clock\n"); return PTR_ERR(lpc18xx_wdt->reg_clk); } - lpc18xx_wdt->wdt_clk = devm_clk_get(dev, "wdtclk"); + lpc18xx_wdt->wdt_clk = devm_clk_get_enabled(dev, "wdtclk"); if (IS_ERR(lpc18xx_wdt->wdt_clk)) { dev_err(dev, "failed to get the wdt clock\n"); return PTR_ERR(lpc18xx_wdt->wdt_clk); } - ret = clk_prepare_enable(lpc18xx_wdt->reg_clk); - if (ret) { - dev_err(dev, "could not prepare or enable sys clock\n"); - return ret; - } - ret = devm_add_action_or_reset(dev, lpc18xx_clk_disable_unprepare, - lpc18xx_wdt->reg_clk); - if (ret) - return ret; - - ret = clk_prepare_enable(lpc18xx_wdt->wdt_clk); - if (ret) { - dev_err(dev, "could not prepare or enable wdt clock\n"); - return ret; - } - ret = devm_add_action_or_reset(dev, lpc18xx_clk_disable_unprepare, - lpc18xx_wdt->wdt_clk); - if (ret) - return ret; - /* We use the clock rate to calculate timeouts */ lpc18xx_wdt->clk_rate = clk_get_rate(lpc18xx_wdt->wdt_clk); if (lpc18xx_wdt->clk_rate == 0) { diff --git a/drivers/watchdog/meson_gxbb_wdt.c b/drivers/watchdog/meson_gxbb_wdt.c index 981a2f7c3bec..35d80cb39856 100644 --- a/drivers/watchdog/meson_gxbb_wdt.c +++ b/drivers/watchdog/meson_gxbb_wdt.c @@ -146,16 +146,10 @@ static const struct of_device_id meson_gxbb_wdt_dt_ids[] = { }; MODULE_DEVICE_TABLE(of, meson_gxbb_wdt_dt_ids); -static void meson_clk_disable_unprepare(void *data) -{ - clk_disable_unprepare(data); -} - static int meson_gxbb_wdt_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct meson_gxbb_wdt *data; - int ret; u32 ctrl_reg; data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); @@ -166,18 +160,10 @@ static int meson_gxbb_wdt_probe(struct platform_device *pdev) if (IS_ERR(data->reg_base)) return PTR_ERR(data->reg_base); - data->clk = devm_clk_get(dev, NULL); + data->clk = devm_clk_get_enabled(dev, NULL); if (IS_ERR(data->clk)) return PTR_ERR(data->clk); - ret = clk_prepare_enable(data->clk); - if (ret) - return ret; - ret = devm_add_action_or_reset(dev, meson_clk_disable_unprepare, - data->clk); - if (ret) - return ret; - platform_set_drvdata(pdev, data); data->wdt_dev.parent = dev; diff --git a/drivers/watchdog/mt7621_wdt.c b/drivers/watchdog/mt7621_wdt.c index a8aa3522cfda..442731bba194 100644 --- a/drivers/watchdog/mt7621_wdt.c +++ b/drivers/watchdog/mt7621_wdt.c @@ -15,8 +15,8 @@ #include <linux/moduleparam.h> #include <linux/platform_device.h> #include <linux/mod_devicetable.h> - -#include <asm/mach-ralink/ralink_regs.h> +#include <linux/mfd/syscon.h> +#include <linux/regmap.h> #define SYSC_RSTSTAT 0x38 #define WDT_RST_CAUSE BIT(1) @@ -31,8 +31,12 @@ #define TMR1CTL_RESTART BIT(9) #define TMR1CTL_PRESCALE_SHIFT 16 -static void __iomem *mt7621_wdt_base; -static struct reset_control *mt7621_wdt_reset; +struct mt7621_wdt_data { + void __iomem *base; + struct reset_control *rst; + struct regmap *sysc; + struct watchdog_device wdt; +}; static bool nowayout = WATCHDOG_NOWAYOUT; module_param(nowayout, bool, 0); @@ -40,27 +44,31 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); -static inline void rt_wdt_w32(unsigned reg, u32 val) +static inline void rt_wdt_w32(void __iomem *base, unsigned int reg, u32 val) { - iowrite32(val, mt7621_wdt_base + reg); + iowrite32(val, base + reg); } -static inline u32 rt_wdt_r32(unsigned reg) +static inline u32 rt_wdt_r32(void __iomem *base, unsigned int reg) { - return ioread32(mt7621_wdt_base + reg); + return ioread32(base + reg); } static int mt7621_wdt_ping(struct watchdog_device *w) { - rt_wdt_w32(TIMER_REG_TMRSTAT, TMR1CTL_RESTART); + struct mt7621_wdt_data *drvdata = watchdog_get_drvdata(w); + + rt_wdt_w32(drvdata->base, TIMER_REG_TMRSTAT, TMR1CTL_RESTART); return 0; } static int mt7621_wdt_set_timeout(struct watchdog_device *w, unsigned int t) { + struct mt7621_wdt_data *drvdata = watchdog_get_drvdata(w); + w->timeout = t; - rt_wdt_w32(TIMER_REG_TMR1LOAD, t * 1000); + rt_wdt_w32(drvdata->base, TIMER_REG_TMR1LOAD, t * 1000); mt7621_wdt_ping(w); return 0; @@ -68,36 +76,41 @@ static int mt7621_wdt_set_timeout(struct watchdog_device *w, unsigned int t) static int mt7621_wdt_start(struct watchdog_device *w) { + struct mt7621_wdt_data *drvdata = watchdog_get_drvdata(w); u32 t; /* set the prescaler to 1ms == 1000us */ - rt_wdt_w32(TIMER_REG_TMR1CTL, 1000 << TMR1CTL_PRESCALE_SHIFT); + rt_wdt_w32(drvdata->base, TIMER_REG_TMR1CTL, 1000 << TMR1CTL_PRESCALE_SHIFT); mt7621_wdt_set_timeout(w, w->timeout); - t = rt_wdt_r32(TIMER_REG_TMR1CTL); + t = rt_wdt_r32(drvdata->base, TIMER_REG_TMR1CTL); t |= TMR1CTL_ENABLE; - rt_wdt_w32(TIMER_REG_TMR1CTL, t); + rt_wdt_w32(drvdata->base, TIMER_REG_TMR1CTL, t); return 0; } static int mt7621_wdt_stop(struct watchdog_device *w) { + struct mt7621_wdt_data *drvdata = watchdog_get_drvdata(w); u32 t; mt7621_wdt_ping(w); - t = rt_wdt_r32(TIMER_REG_TMR1CTL); + t = rt_wdt_r32(drvdata->base, TIMER_REG_TMR1CTL); t &= ~TMR1CTL_ENABLE; - rt_wdt_w32(TIMER_REG_TMR1CTL, t); + rt_wdt_w32(drvdata->base, TIMER_REG_TMR1CTL, t); return 0; } -static int mt7621_wdt_bootcause(void) +static int mt7621_wdt_bootcause(struct mt7621_wdt_data *d) { - if (rt_sysc_r32(SYSC_RSTSTAT) & WDT_RST_CAUSE) + u32 val; + + regmap_read(d->sysc, SYSC_RSTSTAT, &val); + if (val & WDT_RST_CAUSE) return WDIOF_CARDRESET; return 0; @@ -105,7 +118,9 @@ static int mt7621_wdt_bootcause(void) static int mt7621_wdt_is_running(struct watchdog_device *w) { - return !!(rt_wdt_r32(TIMER_REG_TMR1CTL) & TMR1CTL_ENABLE); + struct mt7621_wdt_data *drvdata = watchdog_get_drvdata(w); + + return !!(rt_wdt_r32(drvdata->base, TIMER_REG_TMR1CTL) & TMR1CTL_ENABLE); } static const struct watchdog_info mt7621_wdt_info = { @@ -121,30 +136,47 @@ static const struct watchdog_ops mt7621_wdt_ops = { .set_timeout = mt7621_wdt_set_timeout, }; -static struct watchdog_device mt7621_wdt_dev = { - .info = &mt7621_wdt_info, - .ops = &mt7621_wdt_ops, - .min_timeout = 1, - .max_timeout = 0xfffful / 1000, -}; - static int mt7621_wdt_probe(struct platform_device *pdev) { + struct device_node *np = pdev->dev.of_node; struct device *dev = &pdev->dev; - mt7621_wdt_base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(mt7621_wdt_base)) - return PTR_ERR(mt7621_wdt_base); + struct watchdog_device *mt7621_wdt; + struct mt7621_wdt_data *drvdata; + int err; + + drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); + if (!drvdata) + return -ENOMEM; + + drvdata->sysc = syscon_regmap_lookup_by_phandle(np, "mediatek,sysctl"); + if (IS_ERR(drvdata->sysc)) { + drvdata->sysc = syscon_regmap_lookup_by_compatible("mediatek,mt7621-sysc"); + if (IS_ERR(drvdata->sysc)) + return PTR_ERR(drvdata->sysc); + } + + drvdata->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(drvdata->base)) + return PTR_ERR(drvdata->base); + + drvdata->rst = devm_reset_control_get_exclusive(dev, NULL); + if (!IS_ERR(drvdata->rst)) + reset_control_deassert(drvdata->rst); + + mt7621_wdt = &drvdata->wdt; + mt7621_wdt->info = &mt7621_wdt_info; + mt7621_wdt->ops = &mt7621_wdt_ops; + mt7621_wdt->min_timeout = 1; + mt7621_wdt->max_timeout = 0xfffful / 1000; + mt7621_wdt->parent = dev; - mt7621_wdt_reset = devm_reset_control_get_exclusive(dev, NULL); - if (!IS_ERR(mt7621_wdt_reset)) - reset_control_deassert(mt7621_wdt_reset); + mt7621_wdt->bootstatus = mt7621_wdt_bootcause(drvdata); - mt7621_wdt_dev.bootstatus = mt7621_wdt_bootcause(); + watchdog_init_timeout(mt7621_wdt, mt7621_wdt->max_timeout, dev); + watchdog_set_nowayout(mt7621_wdt, nowayout); + watchdog_set_drvdata(mt7621_wdt, drvdata); - watchdog_init_timeout(&mt7621_wdt_dev, mt7621_wdt_dev.max_timeout, - dev); - watchdog_set_nowayout(&mt7621_wdt_dev, nowayout); - if (mt7621_wdt_is_running(&mt7621_wdt_dev)) { + if (mt7621_wdt_is_running(mt7621_wdt)) { /* * Make sure to apply timeout from watchdog core, taking * the prescaler of this driver here into account (the @@ -154,17 +186,25 @@ static int mt7621_wdt_probe(struct platform_device *pdev) * we first disable the watchdog, set the new prescaler * and timeout, and then re-enable the watchdog. */ - mt7621_wdt_stop(&mt7621_wdt_dev); - mt7621_wdt_start(&mt7621_wdt_dev); - set_bit(WDOG_HW_RUNNING, &mt7621_wdt_dev.status); + mt7621_wdt_stop(mt7621_wdt); + mt7621_wdt_start(mt7621_wdt); + set_bit(WDOG_HW_RUNNING, &mt7621_wdt->status); } - return devm_watchdog_register_device(dev, &mt7621_wdt_dev); + err = devm_watchdog_register_device(dev, &drvdata->wdt); + if (err) + return err; + + platform_set_drvdata(pdev, drvdata); + + return 0; } static void mt7621_wdt_shutdown(struct platform_device *pdev) { - mt7621_wdt_stop(&mt7621_wdt_dev); + struct mt7621_wdt_data *drvdata = platform_get_drvdata(pdev); + + mt7621_wdt_stop(&drvdata->wdt); } static const struct of_device_id mt7621_wdt_match[] = { diff --git a/drivers/watchdog/mtk_wdt.c b/drivers/watchdog/mtk_wdt.c index 3e6212591e69..a9c437598e7e 100644 --- a/drivers/watchdog/mtk_wdt.c +++ b/drivers/watchdog/mtk_wdt.c @@ -50,6 +50,7 @@ #define WDT_MODE_IRQ_EN (1 << 3) #define WDT_MODE_AUTO_START (1 << 4) #define WDT_MODE_DUAL_EN (1 << 6) +#define WDT_MODE_CNT_SEL (1 << 8) #define WDT_MODE_KEY 0x22000000 #define WDT_SWRST 0x14 @@ -70,6 +71,7 @@ struct mtk_wdt_dev { spinlock_t lock; /* protects WDT_SWSYSRST reg */ struct reset_controller_dev rcdev; bool disable_wdt_extrst; + bool reset_by_toprgu; }; struct mtk_wdt_data { @@ -279,6 +281,8 @@ static int mtk_wdt_start(struct watchdog_device *wdt_dev) reg &= ~(WDT_MODE_IRQ_EN | WDT_MODE_DUAL_EN); if (mtk_wdt->disable_wdt_extrst) reg &= ~WDT_MODE_EXRST_EN; + if (mtk_wdt->reset_by_toprgu) + reg |= WDT_MODE_CNT_SEL; reg |= (WDT_MODE_EN | WDT_MODE_KEY); iowrite32(reg, wdt_base + WDT_MODE); @@ -408,6 +412,9 @@ static int mtk_wdt_probe(struct platform_device *pdev) mtk_wdt->disable_wdt_extrst = of_property_read_bool(dev->of_node, "mediatek,disable-extrst"); + mtk_wdt->reset_by_toprgu = + of_property_read_bool(dev->of_node, "mediatek,reset-by-toprgu"); + return 0; } diff --git a/drivers/watchdog/of_xilinx_wdt.c b/drivers/watchdog/of_xilinx_wdt.c index 3318544366b8..2a079ca04aa3 100644 --- a/drivers/watchdog/of_xilinx_wdt.c +++ b/drivers/watchdog/of_xilinx_wdt.c @@ -154,11 +154,6 @@ static u32 xwdt_selftest(struct xwdt_device *xdev) return XWT_TIMER_FAILED; } -static void xwdt_clk_disable_unprepare(void *data) -{ - clk_disable_unprepare(data); -} - static int xwdt_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -193,7 +188,7 @@ static int xwdt_probe(struct platform_device *pdev) watchdog_set_nowayout(xilinx_wdt_wdd, enable_once); - xdev->clk = devm_clk_get(dev, NULL); + xdev->clk = devm_clk_get_enabled(dev, NULL); if (IS_ERR(xdev->clk)) { if (PTR_ERR(xdev->clk) != -ENOENT) return PTR_ERR(xdev->clk); @@ -211,15 +206,6 @@ static int xwdt_probe(struct platform_device *pdev) "The watchdog clock freq cannot be obtained\n"); } else { pfreq = clk_get_rate(xdev->clk); - rc = clk_prepare_enable(xdev->clk); - if (rc) { - dev_err(dev, "unable to enable clock\n"); - return rc; - } - rc = devm_add_action_or_reset(dev, xwdt_clk_disable_unprepare, - xdev->clk); - if (rc) - return rc; } /* diff --git a/drivers/watchdog/pcwd_usb.c b/drivers/watchdog/pcwd_usb.c index 1bdaf17c1d38..8202f0a6b093 100644 --- a/drivers/watchdog/pcwd_usb.c +++ b/drivers/watchdog/pcwd_usb.c @@ -325,7 +325,8 @@ static int usb_pcwd_set_heartbeat(struct usb_pcwd_private *usb_pcwd, int t) static int usb_pcwd_get_temperature(struct usb_pcwd_private *usb_pcwd, int *temperature) { - unsigned char msb, lsb; + unsigned char msb = 0x00; + unsigned char lsb = 0x00; usb_pcwd_send_command(usb_pcwd, CMD_READ_TEMP, &msb, &lsb); @@ -341,7 +342,8 @@ static int usb_pcwd_get_temperature(struct usb_pcwd_private *usb_pcwd, static int usb_pcwd_get_timeleft(struct usb_pcwd_private *usb_pcwd, int *time_left) { - unsigned char msb, lsb; + unsigned char msb = 0x00; + unsigned char lsb = 0x00; /* Read the time that's left before rebooting */ /* Note: if the board is not yet armed then we will read 0xFFFF */ diff --git a/drivers/watchdog/pic32-dmt.c b/drivers/watchdog/pic32-dmt.c index f43062b3c4c8..bc4ccddc75a3 100644 --- a/drivers/watchdog/pic32-dmt.c +++ b/drivers/watchdog/pic32-dmt.c @@ -164,11 +164,6 @@ static struct watchdog_device pic32_dmt_wdd = { .ops = &pic32_dmt_fops, }; -static void pic32_clk_disable_unprepare(void *data) -{ - clk_disable_unprepare(data); -} - static int pic32_dmt_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -184,20 +179,12 @@ static int pic32_dmt_probe(struct platform_device *pdev) if (IS_ERR(dmt->regs)) return PTR_ERR(dmt->regs); - dmt->clk = devm_clk_get(dev, NULL); + dmt->clk = devm_clk_get_enabled(dev, NULL); if (IS_ERR(dmt->clk)) { dev_err(dev, "clk not found\n"); return PTR_ERR(dmt->clk); } - ret = clk_prepare_enable(dmt->clk); - if (ret) - return ret; - ret = devm_add_action_or_reset(dev, pic32_clk_disable_unprepare, - dmt->clk); - if (ret) - return ret; - wdd->timeout = pic32_dmt_get_timeout_secs(dmt); if (!wdd->timeout) { dev_err(dev, "failed to read watchdog register timeout\n"); diff --git a/drivers/watchdog/pic32-wdt.c b/drivers/watchdog/pic32-wdt.c index 41715d68d9e9..6d1a00222991 100644 --- a/drivers/watchdog/pic32-wdt.c +++ b/drivers/watchdog/pic32-wdt.c @@ -162,11 +162,6 @@ static const struct of_device_id pic32_wdt_dt_ids[] = { }; MODULE_DEVICE_TABLE(of, pic32_wdt_dt_ids); -static void pic32_clk_disable_unprepare(void *data) -{ - clk_disable_unprepare(data); -} - static int pic32_wdt_drv_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -186,22 +181,12 @@ static int pic32_wdt_drv_probe(struct platform_device *pdev) if (!wdt->rst_base) return -ENOMEM; - wdt->clk = devm_clk_get(dev, NULL); + wdt->clk = devm_clk_get_enabled(dev, NULL); if (IS_ERR(wdt->clk)) { dev_err(dev, "clk not found\n"); return PTR_ERR(wdt->clk); } - ret = clk_prepare_enable(wdt->clk); - if (ret) { - dev_err(dev, "clk enable failed\n"); - return ret; - } - ret = devm_add_action_or_reset(dev, pic32_clk_disable_unprepare, - wdt->clk); - if (ret) - return ret; - if (pic32_wdt_is_win_enabled(wdt)) { dev_err(dev, "windowed-clear mode is not supported.\n"); return -ENODEV; diff --git a/drivers/watchdog/pnx4008_wdt.c b/drivers/watchdog/pnx4008_wdt.c index e0ea133c1690..87a44a5675a1 100644 --- a/drivers/watchdog/pnx4008_wdt.c +++ b/drivers/watchdog/pnx4008_wdt.c @@ -179,11 +179,6 @@ static struct watchdog_device pnx4008_wdd = { .max_timeout = MAX_HEARTBEAT, }; -static void pnx4008_clk_disable_unprepare(void *data) -{ - clk_disable_unprepare(data); -} - static int pnx4008_wdt_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -195,18 +190,10 @@ static int pnx4008_wdt_probe(struct platform_device *pdev) if (IS_ERR(wdt_base)) return PTR_ERR(wdt_base); - wdt_clk = devm_clk_get(dev, NULL); + wdt_clk = devm_clk_get_enabled(dev, NULL); if (IS_ERR(wdt_clk)) return PTR_ERR(wdt_clk); - ret = clk_prepare_enable(wdt_clk); - if (ret) - return ret; - ret = devm_add_action_or_reset(dev, pnx4008_clk_disable_unprepare, - wdt_clk); - if (ret) - return ret; - pnx4008_wdd.bootstatus = (readl(WDTIM_RES(wdt_base)) & WDOG_RESET) ? WDIOF_CARDRESET : 0; pnx4008_wdd.parent = dev; diff --git a/drivers/watchdog/qcom-wdt.c b/drivers/watchdog/qcom-wdt.c index 0d2209c5eaca..d776474dcdf3 100644 --- a/drivers/watchdog/qcom-wdt.c +++ b/drivers/watchdog/qcom-wdt.c @@ -175,11 +175,6 @@ static const struct watchdog_info qcom_wdt_pt_info = { .identity = KBUILD_MODNAME, }; -static void qcom_clk_disable_unprepare(void *data) -{ - clk_disable_unprepare(data); -} - static const struct qcom_wdt_match_data match_data_apcs_tmr = { .offset = reg_offset_data_apcs_tmr, .pretimeout = false, @@ -226,21 +221,12 @@ static int qcom_wdt_probe(struct platform_device *pdev) if (IS_ERR(wdt->base)) return PTR_ERR(wdt->base); - clk = devm_clk_get(dev, NULL); + clk = devm_clk_get_enabled(dev, NULL); if (IS_ERR(clk)) { dev_err(dev, "failed to get input clock\n"); return PTR_ERR(clk); } - ret = clk_prepare_enable(clk); - if (ret) { - dev_err(dev, "failed to setup clock\n"); - return ret; - } - ret = devm_add_action_or_reset(dev, qcom_clk_disable_unprepare, clk); - if (ret) - return ret; - /* * We use the clock rate to calculate the max timeout, so ensure it's * not zero to avoid a divide-by-zero exception. diff --git a/drivers/watchdog/realtek_otto_wdt.c b/drivers/watchdog/realtek_otto_wdt.c index 2a5298c5e8e4..2c30ddd574c5 100644 --- a/drivers/watchdog/realtek_otto_wdt.c +++ b/drivers/watchdog/realtek_otto_wdt.c @@ -235,27 +235,14 @@ static const struct watchdog_info otto_wdt_info = { WDIOF_PRETIMEOUT, }; -static void otto_wdt_clock_action(void *data) -{ - clk_disable_unprepare(data); -} - static int otto_wdt_probe_clk(struct otto_wdt_ctrl *ctrl) { - struct clk *clk = devm_clk_get(ctrl->dev, NULL); - int ret; + struct clk *clk; + clk = devm_clk_get_enabled(ctrl->dev, NULL); if (IS_ERR(clk)) return dev_err_probe(ctrl->dev, PTR_ERR(clk), "Failed to get clock\n"); - ret = clk_prepare_enable(clk); - if (ret) - return dev_err_probe(ctrl->dev, ret, "Failed to enable clock\n"); - - ret = devm_add_action_or_reset(ctrl->dev, otto_wdt_clock_action, clk); - if (ret) - return ret; - ctrl->clk_rate_khz = clk_get_rate(clk) / 1000; if (ctrl->clk_rate_khz == 0) return dev_err_probe(ctrl->dev, -ENXIO, "Failed to get clock rate\n"); diff --git a/drivers/watchdog/rtd119x_wdt.c b/drivers/watchdog/rtd119x_wdt.c index 834b94ff3f90..95c8d7abce42 100644 --- a/drivers/watchdog/rtd119x_wdt.c +++ b/drivers/watchdog/rtd119x_wdt.c @@ -94,16 +94,10 @@ static const struct of_device_id rtd119x_wdt_dt_ids[] = { { } }; -static void rtd119x_clk_disable_unprepare(void *data) -{ - clk_disable_unprepare(data); -} - static int rtd119x_wdt_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct rtd119x_watchdog_device *data; - int ret; data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) @@ -113,18 +107,10 @@ static int rtd119x_wdt_probe(struct platform_device *pdev) if (IS_ERR(data->base)) return PTR_ERR(data->base); - data->clk = devm_clk_get(dev, NULL); + data->clk = devm_clk_get_enabled(dev, NULL); if (IS_ERR(data->clk)) return PTR_ERR(data->clk); - ret = clk_prepare_enable(data->clk); - if (ret) - return ret; - ret = devm_add_action_or_reset(dev, rtd119x_clk_disable_unprepare, - data->clk); - if (ret) - return ret; - data->wdt_dev.info = &rtd119x_wdt_info; data->wdt_dev.ops = &rtd119x_wdt_ops; data->wdt_dev.timeout = 120; diff --git a/drivers/watchdog/rzg2l_wdt.c b/drivers/watchdog/rzg2l_wdt.c index 974a4194a8fd..d404953d0e0f 100644 --- a/drivers/watchdog/rzg2l_wdt.c +++ b/drivers/watchdog/rzg2l_wdt.c @@ -8,6 +8,7 @@ #include <linux/clk.h> #include <linux/delay.h> #include <linux/io.h> +#include <linux/iopoll.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/of_device.h> @@ -35,6 +36,8 @@ #define F2CYCLE_NSEC(f) (1000000000 / (f)) +#define RZV2M_A_NSEC 730 + static bool nowayout = WATCHDOG_NOWAYOUT; module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" @@ -51,11 +54,35 @@ struct rzg2l_wdt_priv { struct reset_control *rstc; unsigned long osc_clk_rate; unsigned long delay; + unsigned long minimum_assertion_period; struct clk *pclk; struct clk *osc_clk; enum rz_wdt_type devtype; }; +static int rzg2l_wdt_reset(struct rzg2l_wdt_priv *priv) +{ + int err, status; + + if (priv->devtype == WDT_RZV2M) { + /* WDT needs TYPE-B reset control */ + err = reset_control_assert(priv->rstc); + if (err) + return err; + ndelay(priv->minimum_assertion_period); + err = reset_control_deassert(priv->rstc); + if (err) + return err; + err = read_poll_timeout(reset_control_status, status, + status != 1, 0, 1000, false, + priv->rstc); + } else { + err = reset_control_reset(priv->rstc); + } + + return err; +} + static void rzg2l_wdt_wait_delay(struct rzg2l_wdt_priv *priv) { /* delay timer when change the setting register */ @@ -115,25 +142,23 @@ static int rzg2l_wdt_stop(struct watchdog_device *wdev) { struct rzg2l_wdt_priv *priv = watchdog_get_drvdata(wdev); + rzg2l_wdt_reset(priv); pm_runtime_put(wdev->parent); - reset_control_reset(priv->rstc); return 0; } static int rzg2l_wdt_set_timeout(struct watchdog_device *wdev, unsigned int timeout) { - struct rzg2l_wdt_priv *priv = watchdog_get_drvdata(wdev); - wdev->timeout = timeout; /* * If the watchdog is active, reset the module for updating the WDTSET - * register so that it is updated with new timeout values. + * register by calling rzg2l_wdt_stop() (which internally calls reset_control_reset() + * to reset the module) so that it is updated with new timeout values. */ if (watchdog_active(wdev)) { - pm_runtime_put(wdev->parent); - reset_control_reset(priv->rstc); + rzg2l_wdt_stop(wdev); rzg2l_wdt_start(wdev); } @@ -156,6 +181,7 @@ static int rzg2l_wdt_restart(struct watchdog_device *wdev, rzg2l_wdt_write(priv, PEEN_FORCE, PEEN); } else { /* RZ/V2M doesn't have parity error registers */ + rzg2l_wdt_reset(priv); wdev->timeout = 0; @@ -253,6 +279,13 @@ static int rzg2l_wdt_probe(struct platform_device *pdev) priv->devtype = (uintptr_t)of_device_get_match_data(dev); + if (priv->devtype == WDT_RZV2M) { + priv->minimum_assertion_period = RZV2M_A_NSEC + + 3 * F2CYCLE_NSEC(pclk_rate) + 5 * + max(F2CYCLE_NSEC(priv->osc_clk_rate), + F2CYCLE_NSEC(pclk_rate)); + } + pm_runtime_enable(&pdev->dev); priv->wdev.info = &rzg2l_wdt_ident; diff --git a/drivers/watchdog/rzn1_wdt.c b/drivers/watchdog/rzn1_wdt.c index 55ab384b9965..980c1717adb5 100644 --- a/drivers/watchdog/rzn1_wdt.c +++ b/drivers/watchdog/rzn1_wdt.c @@ -98,11 +98,6 @@ static const struct watchdog_ops rzn1_wdt_ops = { .ping = rzn1_wdt_ping, }; -static void rzn1_wdt_clk_disable_unprepare(void *data) -{ - clk_disable_unprepare(data); -} - static int rzn1_wdt_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -132,23 +127,12 @@ static int rzn1_wdt_probe(struct platform_device *pdev) return ret; } - clk = devm_clk_get(dev, NULL); + clk = devm_clk_get_enabled(dev, NULL); if (IS_ERR(clk)) { dev_err(dev, "failed to get the clock\n"); return PTR_ERR(clk); } - ret = clk_prepare_enable(clk); - if (ret) { - dev_err(dev, "failed to prepare/enable the clock\n"); - return ret; - } - - ret = devm_add_action_or_reset(dev, rzn1_wdt_clk_disable_unprepare, - clk); - if (ret) - return ret; - clk_rate = clk_get_rate(clk); if (!clk_rate) { dev_err(dev, "failed to get the clock rate\n"); diff --git a/drivers/watchdog/sbsa_gwdt.c b/drivers/watchdog/sbsa_gwdt.c index 9791c74aebd4..63862803421f 100644 --- a/drivers/watchdog/sbsa_gwdt.c +++ b/drivers/watchdog/sbsa_gwdt.c @@ -150,6 +150,7 @@ static int sbsa_gwdt_set_timeout(struct watchdog_device *wdd, struct sbsa_gwdt *gwdt = watchdog_get_drvdata(wdd); wdd->timeout = timeout; + timeout = clamp_t(unsigned int, timeout, 1, wdd->max_hw_heartbeat_ms / 1000); if (action) sbsa_gwdt_reg_write(gwdt->clk * timeout, gwdt); diff --git a/drivers/watchdog/visconti_wdt.c b/drivers/watchdog/visconti_wdt.c index 83ef55e66ca8..cef0794708e7 100644 --- a/drivers/watchdog/visconti_wdt.c +++ b/drivers/watchdog/visconti_wdt.c @@ -112,11 +112,6 @@ static const struct watchdog_ops visconti_wdt_ops = { .set_timeout = visconti_wdt_set_timeout, }; -static void visconti_clk_disable_unprepare(void *data) -{ - clk_disable_unprepare(data); -} - static int visconti_wdt_probe(struct platform_device *pdev) { struct watchdog_device *wdev; @@ -134,20 +129,10 @@ static int visconti_wdt_probe(struct platform_device *pdev) if (IS_ERR(priv->base)) return PTR_ERR(priv->base); - clk = devm_clk_get(dev, NULL); + clk = devm_clk_get_enabled(dev, NULL); if (IS_ERR(clk)) return dev_err_probe(dev, PTR_ERR(clk), "Could not get clock\n"); - ret = clk_prepare_enable(clk); - if (ret) { - dev_err(dev, "Could not enable clock\n"); - return ret; - } - - ret = devm_add_action_or_reset(dev, visconti_clk_disable_unprepare, clk); - if (ret) - return ret; - clk_freq = clk_get_rate(clk); if (!clk_freq) return -EINVAL; diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c index 55574ed42504..0122e8796879 100644 --- a/drivers/watchdog/watchdog_dev.c +++ b/drivers/watchdog/watchdog_dev.c @@ -35,6 +35,7 @@ #include <linux/init.h> /* For __init/__exit/... */ #include <linux/hrtimer.h> /* For hrtimers */ #include <linux/kernel.h> /* For printk/panic/... */ +#include <linux/kstrtox.h> /* For kstrto* */ #include <linux/kthread.h> /* For kthread_work */ #include <linux/miscdevice.h> /* For handling misc devices */ #include <linux/module.h> /* For module stuff/... */ @@ -546,6 +547,24 @@ static ssize_t pretimeout_show(struct device *dev, } static DEVICE_ATTR_RO(pretimeout); +static ssize_t options_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct watchdog_device *wdd = dev_get_drvdata(dev); + + return sysfs_emit(buf, "0x%x\n", wdd->info->options); +} +static DEVICE_ATTR_RO(options); + +static ssize_t fw_version_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct watchdog_device *wdd = dev_get_drvdata(dev); + + return sysfs_emit(buf, "%d\n", wdd->info->firmware_version); +} +static DEVICE_ATTR_RO(fw_version); + static ssize_t identity_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -617,6 +636,8 @@ static umode_t wdt_is_visible(struct kobject *kobj, struct attribute *attr, } static struct attribute *wdt_attrs[] = { &dev_attr_state.attr, + &dev_attr_options.attr, + &dev_attr_fw_version.attr, &dev_attr_identity.attr, &dev_attr_timeout.attr, &dev_attr_min_timeout.attr, @@ -1061,8 +1082,8 @@ static int watchdog_cdev_register(struct watchdog_device *wdd) if (wdd->id == 0) { misc_deregister(&watchdog_miscdev); old_wd_data = NULL; - put_device(&wd_data->dev); } + put_device(&wd_data->dev); return err; } diff --git a/drivers/watchdog/wdat_wdt.c b/drivers/watchdog/wdat_wdt.c index ce7a4a9e4b03..0ba99bed59fc 100644 --- a/drivers/watchdog/wdat_wdt.c +++ b/drivers/watchdog/wdat_wdt.c @@ -301,13 +301,12 @@ static const struct watchdog_info wdat_wdt_info = { .identity = "wdat_wdt", }; -static const struct watchdog_ops wdat_wdt_ops = { +static struct watchdog_ops wdat_wdt_ops = { .owner = THIS_MODULE, .start = wdat_wdt_start, .stop = wdat_wdt_stop, .ping = wdat_wdt_ping, .set_timeout = wdat_wdt_set_timeout, - .get_timeleft = wdat_wdt_get_timeleft, }; static int wdat_wdt_probe(struct platform_device *pdev) @@ -436,6 +435,9 @@ static int wdat_wdt_probe(struct platform_device *pdev) list_add_tail(&instr->node, instructions); } + if (wdat->instructions[ACPI_WDAT_GET_CURRENT_COUNTDOWN]) + wdat_wdt_ops.get_timeleft = wdat_wdt_get_timeleft; + wdat_wdt_boot_status(wdat); wdat_wdt_set_running(wdat); diff --git a/drivers/watchdog/ziirave_wdt.c b/drivers/watchdog/ziirave_wdt.c index d0e88875443a..21ca08a694ee 100644 --- a/drivers/watchdog/ziirave_wdt.c +++ b/drivers/watchdog/ziirave_wdt.c @@ -593,8 +593,7 @@ static int ziirave_wdt_init_duration(struct i2c_client *client) reset_duration); } -static int ziirave_wdt_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int ziirave_wdt_probe(struct i2c_client *client) { int ret; struct ziirave_wdt_data *w_priv; @@ -732,7 +731,7 @@ static struct i2c_driver ziirave_wdt_driver = { .name = "ziirave_wdt", .of_match_table = zrv_wdt_of_match, }, - .probe = ziirave_wdt_probe, + .probe_new = ziirave_wdt_probe, .remove = ziirave_wdt_remove, .id_table = ziirave_wdt_id, }; |