Commit a9a01e12 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'linux-watchdog-6.3-rc1' of git://www.linux-watchdog.org/linux-watchdog

Pull watchdog updates from Wim Van Sebroeck:

 - qcom-wdt dt-bindings improvements and additions (like MSM8994 and
   MDM9615)

 - mtk_wdt: Add reset_by_toprgu support

 - devm_clk_get_enabled() helper changes

 - Fix kmemleak in watchdog_cdev_register

 - watchdog sysfs improvements

 - Other fixes and small improvements

* tag 'linux-watchdog-6.3-rc1' of git://www.linux-watchdog.org/linux-watchdog: (52 commits)
  watchdog: at91rm9200: Only warn once about problems in .remove()
  watchdog: mt7621-wdt: avoid ralink architecture dependent code
  watchdog: mt7621-wdt: avoid static global declarations
  dt-bindings: watchdog: mt7621-wdt: add phandle to access system controller registers
  watchdog: sbsa_wdog: Make sure the timeout programming is within the limits
  dt-bindings: watchdog: qcom-wdt: add qcom,apss-wdt-sa8775p compatible
  watchdog: report options in sysfs
  watchdog: report fw_version in sysfs
  dt-bindings: watchdog: fsl-imx: document suspend in wait mode
  watchdog: imx2_wdg: suspend watchdog in WAIT mode
  watchdog: pcwd_usb: Fix attempting to access uninitialized memory
  dt-bindings: watchdog: qcom-wdt: merge MSM timer
  dt-bindings: watchdog: qcom-wdt: allow interrupts
  dt-bindings: watchdog: qcom-wdt: add qcom,kpss-wdt-mdm9615
  dt-bindings: watchdog: qcom-wdt: fix list of MSM timer compatibles
  dt-bindings: watchdog: qcom-wdt: do not allow fallback alone
  dt-bindings: watchdog: qcom-wdt: require fallback for IPQ4019
  watchdog: Fix kmemleak in watchdog_cdev_register
  watchdog: Include <linux/kstrtox.h> when appropriate
  watchdog: at91sam9_wdt: use devm_request_irq to avoid missing free_irq() in error path
  ...
parents c3f9b9fa cf3be7e8
...@@ -6,6 +6,19 @@ Description: ...@@ -6,6 +6,19 @@ Description:
device at boot. It is equivalent to WDIOC_GETBOOTSTATUS of device at boot. It is equivalent to WDIOC_GETBOOTSTATUS of
ioctl interface. 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 What: /sys/class/watchdog/watchdogn/identity
Date: August 2015 Date: August 2015
Contact: Wim Van Sebroeck <wim@iguana.be> Contact: Wim Van Sebroeck <wim@iguana.be>
......
* 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>;
};
# 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>;
};
...@@ -9,9 +9,6 @@ title: Freescale i.MX Watchdog Timer (WDT) Controller ...@@ -9,9 +9,6 @@ title: Freescale i.MX Watchdog Timer (WDT) Controller
maintainers: maintainers:
- Anson Huang <Anson.Huang@nxp.com> - Anson Huang <Anson.Huang@nxp.com>
allOf:
- $ref: "watchdog.yaml#"
properties: properties:
compatible: compatible:
oneOf: oneOf:
...@@ -55,11 +52,45 @@ properties: ...@@ -55,11 +52,45 @@ properties:
If present, the watchdog device is configured to assert its If present, the watchdog device is configured to assert its
external reset (WDOG_B) instead of issuing a software reset. 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: required:
- compatible - compatible
- interrupts - interrupts
- reg - 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 unevaluatedProperties: false
examples: examples:
......
# 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
...@@ -19,6 +19,12 @@ properties: ...@@ -19,6 +19,12 @@ properties:
reg: reg:
maxItems: 1 maxItems: 1
mediatek,sysctl:
$ref: /schemas/types.yaml#/definitions/phandle
description:
phandle to system controller 'sysc' syscon node which
controls system registers
required: required:
- compatible - compatible
- reg - reg
...@@ -30,4 +36,5 @@ examples: ...@@ -30,4 +36,5 @@ examples:
watchdog@100 { watchdog@100 {
compatible = "mediatek,mt7621-wdt"; compatible = "mediatek,mt7621-wdt";
reg = <0x100 0x100>; reg = <0x100 0x100>;
mediatek,sysctl = <&sysc>;
}; };
...@@ -52,6 +52,12 @@ properties: ...@@ -52,6 +52,12 @@ properties:
description: Disable sending output reset signal description: Disable sending output reset signal
type: boolean 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': '#reset-cells':
const: 1 const: 1
......
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>;
};
...@@ -9,15 +9,18 @@ title: Qualcomm Krait Processor Sub-system (KPSS) Watchdog timer ...@@ -9,15 +9,18 @@ title: Qualcomm Krait Processor Sub-system (KPSS) Watchdog timer
maintainers: maintainers:
- Sai Prakash Ranjan <saiprakash.ranjan@codeaurora.org> - Sai Prakash Ranjan <saiprakash.ranjan@codeaurora.org>
allOf:
- $ref: watchdog.yaml#
properties: properties:
$nodename:
pattern: "^(watchdog|timer)@[0-9a-f]+$"
compatible: compatible:
oneOf: oneOf:
- items: - items:
- enum: - enum:
- qcom,kpss-wdt-ipq4019
- qcom,apss-wdt-msm8994
- qcom,apss-wdt-qcs404 - qcom,apss-wdt-qcs404
- qcom,apss-wdt-sa8775p
- qcom,apss-wdt-sc7180 - qcom,apss-wdt-sc7180
- qcom,apss-wdt-sc7280 - qcom,apss-wdt-sc7280
- qcom,apss-wdt-sc8180x - qcom,apss-wdt-sc8180x
...@@ -29,15 +32,19 @@ properties: ...@@ -29,15 +32,19 @@ properties:
- qcom,apss-wdt-sm8150 - qcom,apss-wdt-sm8150
- qcom,apss-wdt-sm8250 - qcom,apss-wdt-sm8250
- const: qcom,kpss-wdt - const: qcom,kpss-wdt
- const: qcom,kpss-wdt
deprecated: true
- items:
- const: qcom,scss-timer
- const: qcom,msm-timer
- items: - items:
- enum: - enum:
- qcom,kpss-wdt
- qcom,kpss-timer
- qcom,kpss-wdt-apq8064 - qcom,kpss-wdt-apq8064
- qcom,kpss-wdt-ipq4019
- qcom,kpss-wdt-ipq8064 - qcom,kpss-wdt-ipq8064
- qcom,kpss-wdt-mdm9615
- qcom,kpss-wdt-msm8960 - qcom,kpss-wdt-msm8960
- qcom,scss-timer - const: qcom,kpss-timer
- const: qcom,msm-timer
reg: reg:
maxItems: 1 maxItems: 1
...@@ -45,18 +52,87 @@ properties: ...@@ -45,18 +52,87 @@ properties:
clocks: clocks:
maxItems: 1 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: required:
- compatible - compatible
- reg - reg
- clocks - 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 unevaluatedProperties: false
examples: examples:
- | - |
watchdog@208a038 { #include <dt-bindings/interrupt-controller/arm-gic.h>
compatible = "qcom,kpss-wdt-ipq8064";
reg = <0x0208a038 0x40>; watchdog@17c10000 {
compatible = "qcom,apss-wdt-sm8150", "qcom,kpss-wdt";
reg = <0x17c10000 0x1000>;
clocks = <&sleep_clk>; clocks = <&sleep_clk>;
interrupts = <GIC_SPI 0 IRQ_TYPE_LEVEL_HIGH>;
timeout-sec = <10>; 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>;
};
...@@ -26,7 +26,7 @@ properties: ...@@ -26,7 +26,7 @@ properties:
- items: - items:
- enum: - enum:
- renesas,r9a07g043-wdt # RZ/G2UL - renesas,r9a07g043-wdt # RZ/G2UL and RZ/Five
- renesas,r9a07g044-wdt # RZ/G2{L,LC} - renesas,r9a07g044-wdt # RZ/G2{L,LC}
- renesas,r9a07g054-wdt # RZ/V2L - renesas,r9a07g054-wdt # RZ/V2L
- const: renesas,rzg2l-wdt - const: renesas,rzg2l-wdt
......
...@@ -14,9 +14,14 @@ description: | ...@@ -14,9 +14,14 @@ description: |
This document describes generic bindings which can be used to This document describes generic bindings which can be used to
describe watchdog devices in a device tree. describe watchdog devices in a device tree.
select:
properties:
$nodename:
pattern: "^watchdog(@.*|-[0-9a-f])?$"
properties: properties:
$nodename: $nodename:
pattern: "^watchdog(@.*|-[0-9a-f])?$" pattern: "^(timer|watchdog)(@.*|-[0-9a-f])?$"
timeout-sec: timeout-sec:
description: description:
......
...@@ -1871,7 +1871,9 @@ config GXP_WATCHDOG ...@@ -1871,7 +1871,9 @@ config GXP_WATCHDOG
config MT7621_WDT config MT7621_WDT
tristate "Mediatek SoC watchdog" tristate "Mediatek SoC watchdog"
select WATCHDOG_CORE select WATCHDOG_CORE
depends on SOC_MT7620 || SOC_MT7621 select REGMAP_MMIO
select MFD_SYSCON
depends on SOC_MT7620 || SOC_MT7621 || COMPILE_TEST
help help
Hardware driver for the Mediatek/Ralink MT7621/8 SoC Watchdog Timer. Hardware driver for the Mediatek/Ralink MT7621/8 SoC Watchdog Timer.
......
...@@ -136,11 +136,6 @@ static int apple_wdt_restart(struct watchdog_device *wdd, unsigned long mode, ...@@ -136,11 +136,6 @@ static int apple_wdt_restart(struct watchdog_device *wdd, unsigned long mode,
return 0; return 0;
} }
static void apple_wdt_clk_disable_unprepare(void *data)
{
clk_disable_unprepare(data);
}
static struct watchdog_ops apple_wdt_ops = { static struct watchdog_ops apple_wdt_ops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.start = apple_wdt_start, .start = apple_wdt_start,
...@@ -162,7 +157,6 @@ static int apple_wdt_probe(struct platform_device *pdev) ...@@ -162,7 +157,6 @@ static int apple_wdt_probe(struct platform_device *pdev)
struct apple_wdt *wdt; struct apple_wdt *wdt;
struct clk *clk; struct clk *clk;
u32 wdt_ctrl; u32 wdt_ctrl;
int ret;
wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL); wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
if (!wdt) if (!wdt)
...@@ -172,19 +166,9 @@ static int apple_wdt_probe(struct platform_device *pdev) ...@@ -172,19 +166,9 @@ static int apple_wdt_probe(struct platform_device *pdev)
if (IS_ERR(wdt->regs)) if (IS_ERR(wdt->regs))
return PTR_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)) if (IS_ERR(clk))
return PTR_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); wdt->clk_rate = clk_get_rate(clk);
if (!wdt->clk_rate) if (!wdt->clk_rate)
return -EINVAL; return -EINVAL;
......
...@@ -246,11 +246,6 @@ static const struct watchdog_ops armada_37xx_wdt_ops = { ...@@ -246,11 +246,6 @@ static const struct watchdog_ops armada_37xx_wdt_ops = {
.get_timeleft = armada_37xx_wdt_get_timeleft, .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) static int armada_37xx_wdt_probe(struct platform_device *pdev)
{ {
struct armada_37xx_watchdog *dev; struct armada_37xx_watchdog *dev;
...@@ -280,18 +275,10 @@ static int armada_37xx_wdt_probe(struct platform_device *pdev) ...@@ -280,18 +275,10 @@ static int armada_37xx_wdt_probe(struct platform_device *pdev)
return -ENOMEM; return -ENOMEM;
/* init clock */ /* init clock */
dev->clk = devm_clk_get(&pdev->dev, NULL); dev->clk = devm_clk_get_enabled(&pdev->dev, NULL);
if (IS_ERR(dev->clk)) if (IS_ERR(dev->clk))
return PTR_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); dev->clk_rate = clk_get_rate(dev->clk);
if (!dev->clk_rate) if (!dev->clk_rate)
return -EINVAL; return -EINVAL;
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/kstrtox.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_irq.h> #include <linux/of_irq.h>
......
...@@ -270,7 +270,7 @@ static int at91wdt_remove(struct platform_device *pdev) ...@@ -270,7 +270,7 @@ static int at91wdt_remove(struct platform_device *pdev)
misc_deregister(&at91wdt_miscdev); misc_deregister(&at91wdt_miscdev);
at91wdt_miscdev.parent = NULL; at91wdt_miscdev.parent = NULL;
return res; return 0;
} }
static void at91wdt_shutdown(struct platform_device *pdev) static void at91wdt_shutdown(struct platform_device *pdev)
......
...@@ -206,10 +206,9 @@ static int at91_wdt_init(struct platform_device *pdev, struct at91wdt *wdt) ...@@ -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"); "min heartbeat and max heartbeat might be too close for the system to handle it correctly\n");
if ((tmp & AT91_WDT_WDFIEN) && wdt->irq) { if ((tmp & AT91_WDT_WDFIEN) && wdt->irq) {
err = request_irq(wdt->irq, wdt_interrupt, err = devm_request_irq(dev, wdt->irq, wdt_interrupt,
IRQF_SHARED | IRQF_IRQPOLL | IRQF_SHARED | IRQF_IRQPOLL | IRQF_NO_SUSPEND,
IRQF_NO_SUSPEND, pdev->name, wdt);
pdev->name, wdt);
if (err) if (err)
return err; return err;
} }
......
...@@ -127,11 +127,6 @@ static const struct watchdog_ops bcm7038_wdt_ops = { ...@@ -127,11 +127,6 @@ static const struct watchdog_ops bcm7038_wdt_ops = {
.get_timeleft = bcm7038_wdt_get_timeleft, .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) static int bcm7038_wdt_probe(struct platform_device *pdev)
{ {
struct bcm7038_wdt_platform_data *pdata = pdev->dev.platform_data; struct bcm7038_wdt_platform_data *pdata = pdev->dev.platform_data;
...@@ -153,17 +148,9 @@ static int bcm7038_wdt_probe(struct platform_device *pdev) ...@@ -153,17 +148,9 @@ static int bcm7038_wdt_probe(struct platform_device *pdev)
if (pdata && pdata->clk_name) if (pdata && pdata->clk_name)
clk_name = 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 unable to get clock, use default frequency */
if (!IS_ERR(wdt->clk)) { 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); wdt->rate = clk_get_rate(wdt->clk);
/* Prevent divide-by-zero exception */ /* Prevent divide-by-zero exception */
if (!wdt->rate) if (!wdt->rate)
......
...@@ -274,11 +274,6 @@ static const struct watchdog_ops cdns_wdt_ops = { ...@@ -274,11 +274,6 @@ static const struct watchdog_ops cdns_wdt_ops = {
.set_timeout = cdns_wdt_settimeout, .set_timeout = cdns_wdt_settimeout,
}; };
static void cdns_clk_disable_unprepare(void *data)
{
clk_disable_unprepare(data);
}
/************************Platform Operations*****************************/ /************************Platform Operations*****************************/
/** /**
* cdns_wdt_probe - Probe call for the device. * cdns_wdt_probe - Probe call for the device.
...@@ -333,21 +328,11 @@ static int cdns_wdt_probe(struct platform_device *pdev) ...@@ -333,21 +328,11 @@ static int cdns_wdt_probe(struct platform_device *pdev)
watchdog_stop_on_reboot(cdns_wdt_device); watchdog_stop_on_reboot(cdns_wdt_device);
watchdog_set_drvdata(cdns_wdt_device, wdt); 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)) if (IS_ERR(wdt->clk))
return dev_err_probe(dev, PTR_ERR(wdt->clk), return dev_err_probe(dev, PTR_ERR(wdt->clk),
"input clock not found\n"); "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); clock_f = clk_get_rate(wdt->clk);
if (clock_f <= CDNS_WDT_CLK_75MHZ) { if (clock_f <= CDNS_WDT_CLK_75MHZ) {
wdt->prescaler = CDNS_WDT_PRESCALE_512; wdt->prescaler = CDNS_WDT_PRESCALE_512;
......
...@@ -155,11 +155,20 @@ static int da9062_wdt_restart(struct watchdog_device *wdd, unsigned long action, ...@@ -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 da9062_watchdog *wdt = watchdog_get_drvdata(wdd);
struct i2c_client *client = to_i2c_client(wdt->hw->dev); struct i2c_client *client = to_i2c_client(wdt->hw->dev);
union i2c_smbus_data msg;
int ret; int ret;
/* Don't use regmap because it is not atomic safe */ /*
ret = i2c_smbus_write_byte_data(client, DA9062AA_CONTROL_F, * Don't use regmap because it is not atomic safe. Additionally, use
DA9062AA_SHUTDOWN_MASK); * 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) if (ret < 0)
dev_alert(wdt->hw->dev, "Failed to shutdown (err = %d)\n", dev_alert(wdt->hw->dev, "Failed to shutdown (err = %d)\n",
ret); ret);
......
...@@ -174,11 +174,20 @@ static int da9063_wdt_restart(struct watchdog_device *wdd, unsigned long action, ...@@ -174,11 +174,20 @@ static int da9063_wdt_restart(struct watchdog_device *wdd, unsigned long action,
{ {
struct da9063 *da9063 = watchdog_get_drvdata(wdd); struct da9063 *da9063 = watchdog_get_drvdata(wdd);
struct i2c_client *client = to_i2c_client(da9063->dev); struct i2c_client *client = to_i2c_client(da9063->dev);
union i2c_smbus_data msg;
int ret; int ret;
/* Don't use regmap because it is not atomic safe */ /*
ret = i2c_smbus_write_byte_data(client, DA9063_REG_CONTROL_F, * Don't use regmap because it is not atomic safe. Additionally, use
DA9063_SHUTDOWN); * 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) if (ret < 0)
dev_alert(da9063->dev, "Failed to shutdown (err = %d)\n", dev_alert(da9063->dev, "Failed to shutdown (err = %d)\n",
ret); ret);
......
...@@ -189,14 +189,8 @@ static const struct watchdog_ops davinci_wdt_ops = { ...@@ -189,14 +189,8 @@ static const struct watchdog_ops davinci_wdt_ops = {
.restart = davinci_wdt_restart, .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) static int davinci_wdt_probe(struct platform_device *pdev)
{ {
int ret = 0;
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct watchdog_device *wdd; struct watchdog_device *wdd;
struct davinci_wdt_device *davinci_wdt; struct davinci_wdt_device *davinci_wdt;
...@@ -205,21 +199,11 @@ static int davinci_wdt_probe(struct platform_device *pdev) ...@@ -205,21 +199,11 @@ static int davinci_wdt_probe(struct platform_device *pdev)
if (!davinci_wdt) if (!davinci_wdt)
return -ENOMEM; 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)) if (IS_ERR(davinci_wdt->clk))
return dev_err_probe(dev, PTR_ERR(davinci_wdt->clk), return dev_err_probe(dev, PTR_ERR(davinci_wdt->clk),
"failed to get clock node\n"); "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); platform_set_drvdata(pdev, davinci_wdt);
wdd = &davinci_wdt->wdd; wdd = &davinci_wdt->wdd;
......
...@@ -663,6 +663,7 @@ static int dw_wdt_drv_probe(struct platform_device *pdev) ...@@ -663,6 +663,7 @@ static int dw_wdt_drv_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, dw_wdt); platform_set_drvdata(pdev, dw_wdt);
watchdog_set_restart_priority(wdd, 128); watchdog_set_restart_priority(wdd, 128);
watchdog_stop_on_reboot(wdd);
ret = watchdog_register_device(wdd); ret = watchdog_register_device(wdd);
if (ret) if (ret)
......
...@@ -441,11 +441,10 @@ static bool iTCO_wdt_set_running(struct iTCO_wdt_private *p) ...@@ -441,11 +441,10 @@ static bool iTCO_wdt_set_running(struct iTCO_wdt_private *p)
* Kernel Interfaces * Kernel Interfaces
*/ */
static const struct watchdog_info ident = { static struct watchdog_info ident = {
.options = WDIOF_SETTIMEOUT | .options = WDIOF_SETTIMEOUT |
WDIOF_KEEPALIVEPING | WDIOF_KEEPALIVEPING |
WDIOF_MAGICCLOSE, WDIOF_MAGICCLOSE,
.firmware_version = 0,
.identity = DRV_NAME, .identity = DRV_NAME,
}; };
...@@ -563,6 +562,7 @@ static int iTCO_wdt_probe(struct platform_device *pdev) ...@@ -563,6 +562,7 @@ static int iTCO_wdt_probe(struct platform_device *pdev)
break; break;
} }
ident.firmware_version = p->iTCO_version;
p->wddev.info = &ident, p->wddev.info = &ident,
p->wddev.ops = &iTCO_wdt_ops, p->wddev.ops = &iTCO_wdt_ops,
p->wddev.bootstatus = 0; p->wddev.bootstatus = 0;
......
...@@ -175,16 +175,11 @@ static const struct watchdog_ops pdc_wdt_ops = { ...@@ -175,16 +175,11 @@ static const struct watchdog_ops pdc_wdt_ops = {
.restart = pdc_wdt_restart, .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) static int pdc_wdt_probe(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
u64 div; u64 div;
int ret, val; int val;
unsigned long clk_rate; unsigned long clk_rate;
struct pdc_wdt_dev *pdc_wdt; struct pdc_wdt_dev *pdc_wdt;
...@@ -196,38 +191,18 @@ static int pdc_wdt_probe(struct platform_device *pdev) ...@@ -196,38 +191,18 @@ static int pdc_wdt_probe(struct platform_device *pdev)
if (IS_ERR(pdc_wdt->base)) if (IS_ERR(pdc_wdt->base))
return PTR_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)) { if (IS_ERR(pdc_wdt->sys_clk)) {
dev_err(dev, "failed to get the sys clock\n"); dev_err(dev, "failed to get the sys clock\n");
return PTR_ERR(pdc_wdt->sys_clk); 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)) { if (IS_ERR(pdc_wdt->wdt_clk)) {
dev_err(dev, "failed to get the wdt clock\n"); dev_err(dev, "failed to get the wdt clock\n");
return PTR_ERR(pdc_wdt->wdt_clk); 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 */ /* We use the clock rate to calculate the max timeout */
clk_rate = clk_get_rate(pdc_wdt->wdt_clk); clk_rate = clk_get_rate(pdc_wdt->wdt_clk);
if (clk_rate == 0) { if (clk_rate == 0) {
......
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/moduleparam.h> #include <linux/moduleparam.h>
#include <linux/of_address.h> #include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/regmap.h> #include <linux/regmap.h>
#include <linux/watchdog.h> #include <linux/watchdog.h>
...@@ -35,6 +36,7 @@ ...@@ -35,6 +36,7 @@
#define IMX2_WDT_WCR 0x00 /* Control Register */ #define IMX2_WDT_WCR 0x00 /* Control Register */
#define IMX2_WDT_WCR_WT (0xFF << 8) /* -> Watchdog Timeout Field */ #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_WDA BIT(5) /* -> External Reset WDOG_B */
#define IMX2_WDT_WCR_SRS BIT(4) /* -> Software Reset Signal */ #define IMX2_WDT_WCR_SRS BIT(4) /* -> Software Reset Signal */
#define IMX2_WDT_WCR_WRE BIT(3) /* -> WDOG Reset Enable */ #define IMX2_WDT_WCR_WRE BIT(3) /* -> WDOG Reset Enable */
...@@ -60,13 +62,19 @@ ...@@ -60,13 +62,19 @@
#define WDOG_SEC_TO_COUNT(s) ((s * 2 - 1) << 8) #define WDOG_SEC_TO_COUNT(s) ((s * 2 - 1) << 8)
struct imx2_wdt_data {
bool wdw_supported;
};
struct imx2_wdt_device { struct imx2_wdt_device {
struct clk *clk; struct clk *clk;
struct regmap *regmap; struct regmap *regmap;
struct watchdog_device wdog; struct watchdog_device wdog;
const struct imx2_wdt_data *data;
bool ext_reset; bool ext_reset;
bool clk_is_on; bool clk_is_on;
bool no_ping; bool no_ping;
bool sleep_wait;
}; };
static bool nowayout = WATCHDOG_NOWAYOUT; static bool nowayout = WATCHDOG_NOWAYOUT;
...@@ -129,6 +137,9 @@ static inline void imx2_wdt_setup(struct watchdog_device *wdog) ...@@ -129,6 +137,9 @@ static inline void imx2_wdt_setup(struct watchdog_device *wdog)
/* Suspend timer in low power mode, write once-only */ /* Suspend timer in low power mode, write once-only */
val |= IMX2_WDT_WCR_WDZST; 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 */ /* Strip the old watchdog Time-Out value */
val &= ~IMX2_WDT_WCR_WT; val &= ~IMX2_WDT_WCR_WT;
/* Generate internal chip-level reset if WDOG times out */ /* Generate internal chip-level reset if WDOG times out */
...@@ -292,6 +303,8 @@ static int __init imx2_wdt_probe(struct platform_device *pdev) ...@@ -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->max_hw_heartbeat_ms = IMX2_WDT_MAX_TIME * 1000;
wdog->parent = dev; wdog->parent = dev;
wdev->data = of_device_get_match_data(dev);
ret = platform_get_irq(pdev, 0); ret = platform_get_irq(pdev, 0);
if (ret > 0) if (ret > 0)
if (!devm_request_irq(dev, ret, imx2_wdt_isr, 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) ...@@ -313,9 +326,18 @@ static int __init imx2_wdt_probe(struct platform_device *pdev)
wdev->ext_reset = of_property_read_bool(dev->of_node, wdev->ext_reset = of_property_read_bool(dev->of_node,
"fsl,ext-reset-output"); "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 * 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"); wdev->no_ping = !of_device_is_compatible(dev->of_node, "fsl,imx7d-wdt");
platform_set_drvdata(pdev, wdog); platform_set_drvdata(pdev, wdog);
...@@ -417,9 +439,36 @@ static int __maybe_unused imx2_wdt_resume(struct device *dev) ...@@ -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, static SIMPLE_DEV_PM_OPS(imx2_wdt_pm_ops, imx2_wdt_suspend,
imx2_wdt_resume); 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[] = { static const struct of_device_id imx2_wdt_dt_ids[] = {
{ .compatible = "fsl,imx21-wdt", }, { .compatible = "fsl,imx21-wdt", .data = &imx_wdt_legacy },
{ .compatible = "fsl,imx7d-wdt", }, { .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 */ } { /* sentinel */ }
}; };
MODULE_DEVICE_TABLE(of, imx2_wdt_dt_ids); MODULE_DEVICE_TABLE(of, imx2_wdt_dt_ids);
......
...@@ -299,11 +299,6 @@ static int imx7ulp_wdt_init(struct imx7ulp_wdt_device *wdt, unsigned int timeout ...@@ -299,11 +299,6 @@ static int imx7ulp_wdt_init(struct imx7ulp_wdt_device *wdt, unsigned int timeout
return ret; return ret;
} }
static void imx7ulp_wdt_action(void *data)
{
clk_disable_unprepare(data);
}
static int imx7ulp_wdt_probe(struct platform_device *pdev) static int imx7ulp_wdt_probe(struct platform_device *pdev)
{ {
struct imx7ulp_wdt_device *imx7ulp_wdt; struct imx7ulp_wdt_device *imx7ulp_wdt;
...@@ -321,7 +316,7 @@ static int imx7ulp_wdt_probe(struct platform_device *pdev) ...@@ -321,7 +316,7 @@ static int imx7ulp_wdt_probe(struct platform_device *pdev)
if (IS_ERR(imx7ulp_wdt->base)) if (IS_ERR(imx7ulp_wdt->base))
return PTR_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)) { if (IS_ERR(imx7ulp_wdt->clk)) {
dev_err(dev, "Failed to get watchdog clock\n"); dev_err(dev, "Failed to get watchdog clock\n");
return PTR_ERR(imx7ulp_wdt->clk); return PTR_ERR(imx7ulp_wdt->clk);
...@@ -336,14 +331,6 @@ static int imx7ulp_wdt_probe(struct platform_device *pdev) ...@@ -336,14 +331,6 @@ static int imx7ulp_wdt_probe(struct platform_device *pdev)
dev_info(dev, "imx7ulp wdt probe\n"); 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 = &imx7ulp_wdt->wdd;
wdog->info = &imx7ulp_wdt_info; wdog->info = &imx7ulp_wdt_info;
wdog->ops = &imx7ulp_wdt_ops; wdog->ops = &imx7ulp_wdt_ops;
......
...@@ -197,16 +197,10 @@ static const struct watchdog_ops lpc18xx_wdt_ops = { ...@@ -197,16 +197,10 @@ static const struct watchdog_ops lpc18xx_wdt_ops = {
.restart = lpc18xx_wdt_restart, .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) static int lpc18xx_wdt_probe(struct platform_device *pdev)
{ {
struct lpc18xx_wdt_dev *lpc18xx_wdt; struct lpc18xx_wdt_dev *lpc18xx_wdt;
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
int ret;
lpc18xx_wdt = devm_kzalloc(dev, sizeof(*lpc18xx_wdt), GFP_KERNEL); lpc18xx_wdt = devm_kzalloc(dev, sizeof(*lpc18xx_wdt), GFP_KERNEL);
if (!lpc18xx_wdt) if (!lpc18xx_wdt)
...@@ -216,38 +210,18 @@ static int lpc18xx_wdt_probe(struct platform_device *pdev) ...@@ -216,38 +210,18 @@ static int lpc18xx_wdt_probe(struct platform_device *pdev)
if (IS_ERR(lpc18xx_wdt->base)) if (IS_ERR(lpc18xx_wdt->base))
return PTR_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)) { if (IS_ERR(lpc18xx_wdt->reg_clk)) {
dev_err(dev, "failed to get the reg clock\n"); dev_err(dev, "failed to get the reg clock\n");
return PTR_ERR(lpc18xx_wdt->reg_clk); 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)) { if (IS_ERR(lpc18xx_wdt->wdt_clk)) {
dev_err(dev, "failed to get the wdt clock\n"); dev_err(dev, "failed to get the wdt clock\n");
return PTR_ERR(lpc18xx_wdt->wdt_clk); 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 */ /* We use the clock rate to calculate timeouts */
lpc18xx_wdt->clk_rate = clk_get_rate(lpc18xx_wdt->wdt_clk); lpc18xx_wdt->clk_rate = clk_get_rate(lpc18xx_wdt->wdt_clk);
if (lpc18xx_wdt->clk_rate == 0) { if (lpc18xx_wdt->clk_rate == 0) {
......
...@@ -146,16 +146,10 @@ static const struct of_device_id meson_gxbb_wdt_dt_ids[] = { ...@@ -146,16 +146,10 @@ static const struct of_device_id meson_gxbb_wdt_dt_ids[] = {
}; };
MODULE_DEVICE_TABLE(of, 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) static int meson_gxbb_wdt_probe(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct meson_gxbb_wdt *data; struct meson_gxbb_wdt *data;
int ret;
u32 ctrl_reg; u32 ctrl_reg;
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
...@@ -166,18 +160,10 @@ static int meson_gxbb_wdt_probe(struct platform_device *pdev) ...@@ -166,18 +160,10 @@ static int meson_gxbb_wdt_probe(struct platform_device *pdev)
if (IS_ERR(data->reg_base)) if (IS_ERR(data->reg_base))
return PTR_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)) if (IS_ERR(data->clk))
return PTR_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); platform_set_drvdata(pdev, data);
data->wdt_dev.parent = dev; data->wdt_dev.parent = dev;
......
...@@ -15,8 +15,8 @@ ...@@ -15,8 +15,8 @@
#include <linux/moduleparam.h> #include <linux/moduleparam.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/mod_devicetable.h> #include <linux/mod_devicetable.h>
#include <linux/mfd/syscon.h>
#include <asm/mach-ralink/ralink_regs.h> #include <linux/regmap.h>
#define SYSC_RSTSTAT 0x38 #define SYSC_RSTSTAT 0x38
#define WDT_RST_CAUSE BIT(1) #define WDT_RST_CAUSE BIT(1)
...@@ -31,8 +31,12 @@ ...@@ -31,8 +31,12 @@
#define TMR1CTL_RESTART BIT(9) #define TMR1CTL_RESTART BIT(9)
#define TMR1CTL_PRESCALE_SHIFT 16 #define TMR1CTL_PRESCALE_SHIFT 16
static void __iomem *mt7621_wdt_base; struct mt7621_wdt_data {
static struct reset_control *mt7621_wdt_reset; void __iomem *base;
struct reset_control *rst;
struct regmap *sysc;
struct watchdog_device wdt;
};
static bool nowayout = WATCHDOG_NOWAYOUT; static bool nowayout = WATCHDOG_NOWAYOUT;
module_param(nowayout, bool, 0); module_param(nowayout, bool, 0);
...@@ -40,27 +44,31 @@ MODULE_PARM_DESC(nowayout, ...@@ -40,27 +44,31 @@ MODULE_PARM_DESC(nowayout,
"Watchdog cannot be stopped once started (default=" "Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); __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) 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; return 0;
} }
static int mt7621_wdt_set_timeout(struct watchdog_device *w, unsigned int t) 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; 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); mt7621_wdt_ping(w);
return 0; return 0;
...@@ -68,36 +76,41 @@ static int mt7621_wdt_set_timeout(struct watchdog_device *w, unsigned int t) ...@@ -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) static int mt7621_wdt_start(struct watchdog_device *w)
{ {
struct mt7621_wdt_data *drvdata = watchdog_get_drvdata(w);
u32 t; u32 t;
/* set the prescaler to 1ms == 1000us */ /* 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); 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; t |= TMR1CTL_ENABLE;
rt_wdt_w32(TIMER_REG_TMR1CTL, t); rt_wdt_w32(drvdata->base, TIMER_REG_TMR1CTL, t);
return 0; return 0;
} }
static int mt7621_wdt_stop(struct watchdog_device *w) static int mt7621_wdt_stop(struct watchdog_device *w)
{ {
struct mt7621_wdt_data *drvdata = watchdog_get_drvdata(w);
u32 t; u32 t;
mt7621_wdt_ping(w); mt7621_wdt_ping(w);
t = rt_wdt_r32(TIMER_REG_TMR1CTL); t = rt_wdt_r32(drvdata->base, TIMER_REG_TMR1CTL);
t &= ~TMR1CTL_ENABLE; t &= ~TMR1CTL_ENABLE;
rt_wdt_w32(TIMER_REG_TMR1CTL, t); rt_wdt_w32(drvdata->base, TIMER_REG_TMR1CTL, t);
return 0; 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 WDIOF_CARDRESET;
return 0; return 0;
...@@ -105,7 +118,9 @@ static int mt7621_wdt_bootcause(void) ...@@ -105,7 +118,9 @@ static int mt7621_wdt_bootcause(void)
static int mt7621_wdt_is_running(struct watchdog_device *w) 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 = { static const struct watchdog_info mt7621_wdt_info = {
...@@ -121,30 +136,47 @@ static const struct watchdog_ops mt7621_wdt_ops = { ...@@ -121,30 +136,47 @@ static const struct watchdog_ops mt7621_wdt_ops = {
.set_timeout = mt7621_wdt_set_timeout, .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) static int mt7621_wdt_probe(struct platform_device *pdev)
{ {
struct device_node *np = pdev->dev.of_node;
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
mt7621_wdt_base = devm_platform_ioremap_resource(pdev, 0); struct watchdog_device *mt7621_wdt;
if (IS_ERR(mt7621_wdt_base)) struct mt7621_wdt_data *drvdata;
return PTR_ERR(mt7621_wdt_base); 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); mt7621_wdt->bootstatus = mt7621_wdt_bootcause(drvdata);
if (!IS_ERR(mt7621_wdt_reset))
reset_control_deassert(mt7621_wdt_reset);
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, if (mt7621_wdt_is_running(mt7621_wdt)) {
dev);
watchdog_set_nowayout(&mt7621_wdt_dev, nowayout);
if (mt7621_wdt_is_running(&mt7621_wdt_dev)) {
/* /*
* Make sure to apply timeout from watchdog core, taking * Make sure to apply timeout from watchdog core, taking
* the prescaler of this driver here into account (the * the prescaler of this driver here into account (the
...@@ -154,17 +186,25 @@ static int mt7621_wdt_probe(struct platform_device *pdev) ...@@ -154,17 +186,25 @@ static int mt7621_wdt_probe(struct platform_device *pdev)
* we first disable the watchdog, set the new prescaler * we first disable the watchdog, set the new prescaler
* and timeout, and then re-enable the watchdog. * and timeout, and then re-enable the watchdog.
*/ */
mt7621_wdt_stop(&mt7621_wdt_dev); mt7621_wdt_stop(mt7621_wdt);
mt7621_wdt_start(&mt7621_wdt_dev); mt7621_wdt_start(mt7621_wdt);
set_bit(WDOG_HW_RUNNING, &mt7621_wdt_dev.status); 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) 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[] = { static const struct of_device_id mt7621_wdt_match[] = {
......
...@@ -50,6 +50,7 @@ ...@@ -50,6 +50,7 @@
#define WDT_MODE_IRQ_EN (1 << 3) #define WDT_MODE_IRQ_EN (1 << 3)
#define WDT_MODE_AUTO_START (1 << 4) #define WDT_MODE_AUTO_START (1 << 4)
#define WDT_MODE_DUAL_EN (1 << 6) #define WDT_MODE_DUAL_EN (1 << 6)
#define WDT_MODE_CNT_SEL (1 << 8)
#define WDT_MODE_KEY 0x22000000 #define WDT_MODE_KEY 0x22000000
#define WDT_SWRST 0x14 #define WDT_SWRST 0x14
...@@ -70,6 +71,7 @@ struct mtk_wdt_dev { ...@@ -70,6 +71,7 @@ struct mtk_wdt_dev {
spinlock_t lock; /* protects WDT_SWSYSRST reg */ spinlock_t lock; /* protects WDT_SWSYSRST reg */
struct reset_controller_dev rcdev; struct reset_controller_dev rcdev;
bool disable_wdt_extrst; bool disable_wdt_extrst;
bool reset_by_toprgu;
}; };
struct mtk_wdt_data { struct mtk_wdt_data {
...@@ -279,6 +281,8 @@ static int mtk_wdt_start(struct watchdog_device *wdt_dev) ...@@ -279,6 +281,8 @@ static int mtk_wdt_start(struct watchdog_device *wdt_dev)
reg &= ~(WDT_MODE_IRQ_EN | WDT_MODE_DUAL_EN); reg &= ~(WDT_MODE_IRQ_EN | WDT_MODE_DUAL_EN);
if (mtk_wdt->disable_wdt_extrst) if (mtk_wdt->disable_wdt_extrst)
reg &= ~WDT_MODE_EXRST_EN; reg &= ~WDT_MODE_EXRST_EN;
if (mtk_wdt->reset_by_toprgu)
reg |= WDT_MODE_CNT_SEL;
reg |= (WDT_MODE_EN | WDT_MODE_KEY); reg |= (WDT_MODE_EN | WDT_MODE_KEY);
iowrite32(reg, wdt_base + WDT_MODE); iowrite32(reg, wdt_base + WDT_MODE);
...@@ -408,6 +412,9 @@ static int mtk_wdt_probe(struct platform_device *pdev) ...@@ -408,6 +412,9 @@ static int mtk_wdt_probe(struct platform_device *pdev)
mtk_wdt->disable_wdt_extrst = mtk_wdt->disable_wdt_extrst =
of_property_read_bool(dev->of_node, "mediatek,disable-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; return 0;
} }
......
...@@ -154,11 +154,6 @@ static u32 xwdt_selftest(struct xwdt_device *xdev) ...@@ -154,11 +154,6 @@ static u32 xwdt_selftest(struct xwdt_device *xdev)
return XWT_TIMER_FAILED; return XWT_TIMER_FAILED;
} }
static void xwdt_clk_disable_unprepare(void *data)
{
clk_disable_unprepare(data);
}
static int xwdt_probe(struct platform_device *pdev) static int xwdt_probe(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
...@@ -193,7 +188,7 @@ static int xwdt_probe(struct platform_device *pdev) ...@@ -193,7 +188,7 @@ static int xwdt_probe(struct platform_device *pdev)
watchdog_set_nowayout(xilinx_wdt_wdd, enable_once); 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 (IS_ERR(xdev->clk)) {
if (PTR_ERR(xdev->clk) != -ENOENT) if (PTR_ERR(xdev->clk) != -ENOENT)
return PTR_ERR(xdev->clk); return PTR_ERR(xdev->clk);
...@@ -211,15 +206,6 @@ static int xwdt_probe(struct platform_device *pdev) ...@@ -211,15 +206,6 @@ static int xwdt_probe(struct platform_device *pdev)
"The watchdog clock freq cannot be obtained\n"); "The watchdog clock freq cannot be obtained\n");
} else { } else {
pfreq = clk_get_rate(xdev->clk); 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;
} }
/* /*
......
...@@ -325,7 +325,8 @@ static int usb_pcwd_set_heartbeat(struct usb_pcwd_private *usb_pcwd, int t) ...@@ -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, static int usb_pcwd_get_temperature(struct usb_pcwd_private *usb_pcwd,
int *temperature) 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); 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, ...@@ -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, static int usb_pcwd_get_timeleft(struct usb_pcwd_private *usb_pcwd,
int *time_left) int *time_left)
{ {
unsigned char msb, lsb; unsigned char msb = 0x00;
unsigned char lsb = 0x00;
/* Read the time that's left before rebooting */ /* Read the time that's left before rebooting */
/* Note: if the board is not yet armed then we will read 0xFFFF */ /* Note: if the board is not yet armed then we will read 0xFFFF */
......
...@@ -164,11 +164,6 @@ static struct watchdog_device pic32_dmt_wdd = { ...@@ -164,11 +164,6 @@ static struct watchdog_device pic32_dmt_wdd = {
.ops = &pic32_dmt_fops, .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) static int pic32_dmt_probe(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
...@@ -184,20 +179,12 @@ static int pic32_dmt_probe(struct platform_device *pdev) ...@@ -184,20 +179,12 @@ static int pic32_dmt_probe(struct platform_device *pdev)
if (IS_ERR(dmt->regs)) if (IS_ERR(dmt->regs))
return PTR_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)) { if (IS_ERR(dmt->clk)) {
dev_err(dev, "clk not found\n"); dev_err(dev, "clk not found\n");
return PTR_ERR(dmt->clk); 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); wdd->timeout = pic32_dmt_get_timeout_secs(dmt);
if (!wdd->timeout) { if (!wdd->timeout) {
dev_err(dev, "failed to read watchdog register timeout\n"); dev_err(dev, "failed to read watchdog register timeout\n");
......
...@@ -162,11 +162,6 @@ static const struct of_device_id pic32_wdt_dt_ids[] = { ...@@ -162,11 +162,6 @@ static const struct of_device_id pic32_wdt_dt_ids[] = {
}; };
MODULE_DEVICE_TABLE(of, 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) static int pic32_wdt_drv_probe(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
...@@ -186,22 +181,12 @@ static int pic32_wdt_drv_probe(struct platform_device *pdev) ...@@ -186,22 +181,12 @@ static int pic32_wdt_drv_probe(struct platform_device *pdev)
if (!wdt->rst_base) if (!wdt->rst_base)
return -ENOMEM; return -ENOMEM;
wdt->clk = devm_clk_get(dev, NULL); wdt->clk = devm_clk_get_enabled(dev, NULL);
if (IS_ERR(wdt->clk)) { if (IS_ERR(wdt->clk)) {
dev_err(dev, "clk not found\n"); dev_err(dev, "clk not found\n");
return PTR_ERR(wdt->clk); 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)) { if (pic32_wdt_is_win_enabled(wdt)) {
dev_err(dev, "windowed-clear mode is not supported.\n"); dev_err(dev, "windowed-clear mode is not supported.\n");
return -ENODEV; return -ENODEV;
......
...@@ -179,11 +179,6 @@ static struct watchdog_device pnx4008_wdd = { ...@@ -179,11 +179,6 @@ static struct watchdog_device pnx4008_wdd = {
.max_timeout = MAX_HEARTBEAT, .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) static int pnx4008_wdt_probe(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
...@@ -195,18 +190,10 @@ static int pnx4008_wdt_probe(struct platform_device *pdev) ...@@ -195,18 +190,10 @@ static int pnx4008_wdt_probe(struct platform_device *pdev)
if (IS_ERR(wdt_base)) if (IS_ERR(wdt_base))
return PTR_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)) if (IS_ERR(wdt_clk))
return PTR_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) ? pnx4008_wdd.bootstatus = (readl(WDTIM_RES(wdt_base)) & WDOG_RESET) ?
WDIOF_CARDRESET : 0; WDIOF_CARDRESET : 0;
pnx4008_wdd.parent = dev; pnx4008_wdd.parent = dev;
......
...@@ -175,11 +175,6 @@ static const struct watchdog_info qcom_wdt_pt_info = { ...@@ -175,11 +175,6 @@ static const struct watchdog_info qcom_wdt_pt_info = {
.identity = KBUILD_MODNAME, .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 = { static const struct qcom_wdt_match_data match_data_apcs_tmr = {
.offset = reg_offset_data_apcs_tmr, .offset = reg_offset_data_apcs_tmr,
.pretimeout = false, .pretimeout = false,
...@@ -226,21 +221,12 @@ static int qcom_wdt_probe(struct platform_device *pdev) ...@@ -226,21 +221,12 @@ static int qcom_wdt_probe(struct platform_device *pdev)
if (IS_ERR(wdt->base)) if (IS_ERR(wdt->base))
return PTR_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)) { if (IS_ERR(clk)) {
dev_err(dev, "failed to get input clock\n"); dev_err(dev, "failed to get input clock\n");
return PTR_ERR(clk); 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 * We use the clock rate to calculate the max timeout, so ensure it's
* not zero to avoid a divide-by-zero exception. * not zero to avoid a divide-by-zero exception.
......
...@@ -235,27 +235,14 @@ static const struct watchdog_info otto_wdt_info = { ...@@ -235,27 +235,14 @@ static const struct watchdog_info otto_wdt_info = {
WDIOF_PRETIMEOUT, 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) static int otto_wdt_probe_clk(struct otto_wdt_ctrl *ctrl)
{ {
struct clk *clk = devm_clk_get(ctrl->dev, NULL); struct clk *clk;
int ret;
clk = devm_clk_get_enabled(ctrl->dev, NULL);
if (IS_ERR(clk)) if (IS_ERR(clk))
return dev_err_probe(ctrl->dev, PTR_ERR(clk), "Failed to get clock\n"); 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; ctrl->clk_rate_khz = clk_get_rate(clk) / 1000;
if (ctrl->clk_rate_khz == 0) if (ctrl->clk_rate_khz == 0)
return dev_err_probe(ctrl->dev, -ENXIO, "Failed to get clock rate\n"); return dev_err_probe(ctrl->dev, -ENXIO, "Failed to get clock rate\n");
......
...@@ -94,16 +94,10 @@ static const struct of_device_id rtd119x_wdt_dt_ids[] = { ...@@ -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) static int rtd119x_wdt_probe(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct rtd119x_watchdog_device *data; struct rtd119x_watchdog_device *data;
int ret;
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data) if (!data)
...@@ -113,18 +107,10 @@ static int rtd119x_wdt_probe(struct platform_device *pdev) ...@@ -113,18 +107,10 @@ static int rtd119x_wdt_probe(struct platform_device *pdev)
if (IS_ERR(data->base)) if (IS_ERR(data->base))
return PTR_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)) if (IS_ERR(data->clk))
return PTR_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.info = &rtd119x_wdt_info;
data->wdt_dev.ops = &rtd119x_wdt_ops; data->wdt_dev.ops = &rtd119x_wdt_ops;
data->wdt_dev.timeout = 120; data->wdt_dev.timeout = 120;
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of_device.h> #include <linux/of_device.h>
...@@ -35,6 +36,8 @@ ...@@ -35,6 +36,8 @@
#define F2CYCLE_NSEC(f) (1000000000 / (f)) #define F2CYCLE_NSEC(f) (1000000000 / (f))
#define RZV2M_A_NSEC 730
static bool nowayout = WATCHDOG_NOWAYOUT; static bool nowayout = WATCHDOG_NOWAYOUT;
module_param(nowayout, bool, 0); module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
...@@ -51,11 +54,35 @@ struct rzg2l_wdt_priv { ...@@ -51,11 +54,35 @@ struct rzg2l_wdt_priv {
struct reset_control *rstc; struct reset_control *rstc;
unsigned long osc_clk_rate; unsigned long osc_clk_rate;
unsigned long delay; unsigned long delay;
unsigned long minimum_assertion_period;
struct clk *pclk; struct clk *pclk;
struct clk *osc_clk; struct clk *osc_clk;
enum rz_wdt_type devtype; 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) static void rzg2l_wdt_wait_delay(struct rzg2l_wdt_priv *priv)
{ {
/* delay timer when change the setting register */ /* delay timer when change the setting register */
...@@ -115,25 +142,23 @@ static int rzg2l_wdt_stop(struct watchdog_device *wdev) ...@@ -115,25 +142,23 @@ static int rzg2l_wdt_stop(struct watchdog_device *wdev)
{ {
struct rzg2l_wdt_priv *priv = watchdog_get_drvdata(wdev); struct rzg2l_wdt_priv *priv = watchdog_get_drvdata(wdev);
rzg2l_wdt_reset(priv);
pm_runtime_put(wdev->parent); pm_runtime_put(wdev->parent);
reset_control_reset(priv->rstc);
return 0; return 0;
} }
static int rzg2l_wdt_set_timeout(struct watchdog_device *wdev, unsigned int timeout) 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; wdev->timeout = timeout;
/* /*
* If the watchdog is active, reset the module for updating the WDTSET * 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)) { if (watchdog_active(wdev)) {
pm_runtime_put(wdev->parent); rzg2l_wdt_stop(wdev);
reset_control_reset(priv->rstc);
rzg2l_wdt_start(wdev); rzg2l_wdt_start(wdev);
} }
...@@ -156,6 +181,7 @@ static int rzg2l_wdt_restart(struct watchdog_device *wdev, ...@@ -156,6 +181,7 @@ static int rzg2l_wdt_restart(struct watchdog_device *wdev,
rzg2l_wdt_write(priv, PEEN_FORCE, PEEN); rzg2l_wdt_write(priv, PEEN_FORCE, PEEN);
} else { } else {
/* RZ/V2M doesn't have parity error registers */ /* RZ/V2M doesn't have parity error registers */
rzg2l_wdt_reset(priv);
wdev->timeout = 0; wdev->timeout = 0;
...@@ -253,6 +279,13 @@ static int rzg2l_wdt_probe(struct platform_device *pdev) ...@@ -253,6 +279,13 @@ static int rzg2l_wdt_probe(struct platform_device *pdev)
priv->devtype = (uintptr_t)of_device_get_match_data(dev); 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); pm_runtime_enable(&pdev->dev);
priv->wdev.info = &rzg2l_wdt_ident; priv->wdev.info = &rzg2l_wdt_ident;
......
...@@ -98,11 +98,6 @@ static const struct watchdog_ops rzn1_wdt_ops = { ...@@ -98,11 +98,6 @@ static const struct watchdog_ops rzn1_wdt_ops = {
.ping = rzn1_wdt_ping, .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) static int rzn1_wdt_probe(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
...@@ -132,23 +127,12 @@ static int rzn1_wdt_probe(struct platform_device *pdev) ...@@ -132,23 +127,12 @@ static int rzn1_wdt_probe(struct platform_device *pdev)
return ret; return ret;
} }
clk = devm_clk_get(dev, NULL); clk = devm_clk_get_enabled(dev, NULL);
if (IS_ERR(clk)) { if (IS_ERR(clk)) {
dev_err(dev, "failed to get the clock\n"); dev_err(dev, "failed to get the clock\n");
return PTR_ERR(clk); 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); clk_rate = clk_get_rate(clk);
if (!clk_rate) { if (!clk_rate) {
dev_err(dev, "failed to get the clock rate\n"); dev_err(dev, "failed to get the clock rate\n");
......
...@@ -150,6 +150,7 @@ static int sbsa_gwdt_set_timeout(struct watchdog_device *wdd, ...@@ -150,6 +150,7 @@ static int sbsa_gwdt_set_timeout(struct watchdog_device *wdd,
struct sbsa_gwdt *gwdt = watchdog_get_drvdata(wdd); struct sbsa_gwdt *gwdt = watchdog_get_drvdata(wdd);
wdd->timeout = timeout; wdd->timeout = timeout;
timeout = clamp_t(unsigned int, timeout, 1, wdd->max_hw_heartbeat_ms / 1000);
if (action) if (action)
sbsa_gwdt_reg_write(gwdt->clk * timeout, gwdt); sbsa_gwdt_reg_write(gwdt->clk * timeout, gwdt);
......
...@@ -112,11 +112,6 @@ static const struct watchdog_ops visconti_wdt_ops = { ...@@ -112,11 +112,6 @@ static const struct watchdog_ops visconti_wdt_ops = {
.set_timeout = visconti_wdt_set_timeout, .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) static int visconti_wdt_probe(struct platform_device *pdev)
{ {
struct watchdog_device *wdev; struct watchdog_device *wdev;
...@@ -134,20 +129,10 @@ static int visconti_wdt_probe(struct platform_device *pdev) ...@@ -134,20 +129,10 @@ static int visconti_wdt_probe(struct platform_device *pdev)
if (IS_ERR(priv->base)) if (IS_ERR(priv->base))
return PTR_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)) if (IS_ERR(clk))
return dev_err_probe(dev, PTR_ERR(clk), "Could not get clock\n"); 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); clk_freq = clk_get_rate(clk);
if (!clk_freq) if (!clk_freq)
return -EINVAL; return -EINVAL;
......
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
#include <linux/init.h> /* For __init/__exit/... */ #include <linux/init.h> /* For __init/__exit/... */
#include <linux/hrtimer.h> /* For hrtimers */ #include <linux/hrtimer.h> /* For hrtimers */
#include <linux/kernel.h> /* For printk/panic/... */ #include <linux/kernel.h> /* For printk/panic/... */
#include <linux/kstrtox.h> /* For kstrto* */
#include <linux/kthread.h> /* For kthread_work */ #include <linux/kthread.h> /* For kthread_work */
#include <linux/miscdevice.h> /* For handling misc devices */ #include <linux/miscdevice.h> /* For handling misc devices */
#include <linux/module.h> /* For module stuff/... */ #include <linux/module.h> /* For module stuff/... */
...@@ -546,6 +547,24 @@ static ssize_t pretimeout_show(struct device *dev, ...@@ -546,6 +547,24 @@ static ssize_t pretimeout_show(struct device *dev,
} }
static DEVICE_ATTR_RO(pretimeout); 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, static ssize_t identity_show(struct device *dev, struct device_attribute *attr,
char *buf) char *buf)
{ {
...@@ -617,6 +636,8 @@ static umode_t wdt_is_visible(struct kobject *kobj, struct attribute *attr, ...@@ -617,6 +636,8 @@ static umode_t wdt_is_visible(struct kobject *kobj, struct attribute *attr,
} }
static struct attribute *wdt_attrs[] = { static struct attribute *wdt_attrs[] = {
&dev_attr_state.attr, &dev_attr_state.attr,
&dev_attr_options.attr,
&dev_attr_fw_version.attr,
&dev_attr_identity.attr, &dev_attr_identity.attr,
&dev_attr_timeout.attr, &dev_attr_timeout.attr,
&dev_attr_min_timeout.attr, &dev_attr_min_timeout.attr,
...@@ -1061,8 +1082,8 @@ static int watchdog_cdev_register(struct watchdog_device *wdd) ...@@ -1061,8 +1082,8 @@ static int watchdog_cdev_register(struct watchdog_device *wdd)
if (wdd->id == 0) { if (wdd->id == 0) {
misc_deregister(&watchdog_miscdev); misc_deregister(&watchdog_miscdev);
old_wd_data = NULL; old_wd_data = NULL;
put_device(&wd_data->dev);
} }
put_device(&wd_data->dev);
return err; return err;
} }
......
...@@ -301,13 +301,12 @@ static const struct watchdog_info wdat_wdt_info = { ...@@ -301,13 +301,12 @@ static const struct watchdog_info wdat_wdt_info = {
.identity = "wdat_wdt", .identity = "wdat_wdt",
}; };
static const struct watchdog_ops wdat_wdt_ops = { static struct watchdog_ops wdat_wdt_ops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.start = wdat_wdt_start, .start = wdat_wdt_start,
.stop = wdat_wdt_stop, .stop = wdat_wdt_stop,
.ping = wdat_wdt_ping, .ping = wdat_wdt_ping,
.set_timeout = wdat_wdt_set_timeout, .set_timeout = wdat_wdt_set_timeout,
.get_timeleft = wdat_wdt_get_timeleft,
}; };
static int wdat_wdt_probe(struct platform_device *pdev) static int wdat_wdt_probe(struct platform_device *pdev)
...@@ -436,6 +435,9 @@ 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); 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_boot_status(wdat);
wdat_wdt_set_running(wdat); wdat_wdt_set_running(wdat);
......
...@@ -593,8 +593,7 @@ static int ziirave_wdt_init_duration(struct i2c_client *client) ...@@ -593,8 +593,7 @@ static int ziirave_wdt_init_duration(struct i2c_client *client)
reset_duration); reset_duration);
} }
static int ziirave_wdt_probe(struct i2c_client *client, static int ziirave_wdt_probe(struct i2c_client *client)
const struct i2c_device_id *id)
{ {
int ret; int ret;
struct ziirave_wdt_data *w_priv; struct ziirave_wdt_data *w_priv;
...@@ -732,7 +731,7 @@ static struct i2c_driver ziirave_wdt_driver = { ...@@ -732,7 +731,7 @@ static struct i2c_driver ziirave_wdt_driver = {
.name = "ziirave_wdt", .name = "ziirave_wdt",
.of_match_table = zrv_wdt_of_match, .of_match_table = zrv_wdt_of_match,
}, },
.probe = ziirave_wdt_probe, .probe_new = ziirave_wdt_probe,
.remove = ziirave_wdt_remove, .remove = ziirave_wdt_remove,
.id_table = ziirave_wdt_id, .id_table = ziirave_wdt_id,
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment