mirror of
https://github.com/torvalds/linux.git
synced 2024-12-28 22:02:28 +00:00
linux-watchdog 6.1-rc1 tag
-----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.14 (GNU/Linux) iEYEABECAAYFAmNFSLMACgkQ+iyteGJfRsqCEwCgiKmFUmOQcw1x0PIGYZ/onzh0 0GYAn3ejZjwEgJPTlTZT0P7tXM5n4qsU =Zgdj -----END PGP SIGNATURE----- Merge tag 'linux-watchdog-6.1-rc1' of git://www.linux-watchdog.org/linux-watchdog Pull watchdog updates from Wim Van Sebroeck: - new driver for Exar/MaxLinear XR28V38x - support for exynosautov9 SoC - support for Renesas R-Car V5H (R8A779G0) and RZ/V2M (r9a09g011) SoC - support for imx93 - several other fixes and improvements * tag 'linux-watchdog-6.1-rc1' of git://www.linux-watchdog.org/linux-watchdog: (36 commits) watchdog: twl4030_wdt: add missing mod_devicetable.h include dt-bindings: watchdog: migrate mt7621 text bindings to YAML watchdog: sp5100_tco: Add "action" module parameter watchdog: imx93: add watchdog timer on imx93 watchdog: imx7ulp_wdt: init wdog when it was active watchdog: imx7ulp_wdt: Handle wdog reconfigure failure watchdog: imx7ulp_wdt: Fix RCS timeout issue watchdog: imx7ulp_wdt: Check CMD32EN in wdog init watchdog: imx7ulp: Add explict memory barrier for unlock sequence watchdog: imx7ulp: Move suspend/resume to noirq phase watchdog: rti-wdt:using the pm_runtime_resume_and_get to simplify the code dt-bindings: watchdog: rockchip: add rockchip,rk3128-wdt watchdog: s3c2410_wdt: support exynosautov9 watchdog dt-bindings: watchdog: add exynosautov9 compatible watchdog: npcm: Enable clock if provided watchdog: meson: keep running if already active watchdog: dt-bindings: atmel,at91sam9-wdt: convert to json-schema watchdog: armada_37xx_wdt: Fix .set_timeout callback watchdog: sa1100: make variable sa1100dog_driver static watchdog: w83977f_wdt: Fix comment typo ...
This commit is contained in:
commit
3d33e6dd5c
@ -0,0 +1,127 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
# Copyright (C) 2022 Microchip Technology, Inc. and its subsidiaries
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/watchdog/atmel,at91sam9-wdt.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Atmel Watchdog Timers
|
||||
|
||||
maintainers:
|
||||
- Eugen Hristev <eugen.hristev@microchip.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: atmel,at91sam9260-wdt
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
atmel,max-heartbeat-sec:
|
||||
description:
|
||||
Should contain the maximum heartbeat value in seconds. This value
|
||||
should be less or equal to 16. It is used to compute the WDV field.
|
||||
maximum: 16
|
||||
|
||||
atmel,min-heartbeat-sec:
|
||||
description:
|
||||
Should contain the minimum heartbeat value in seconds. This value
|
||||
must be smaller than the max-heartbeat-sec value. It is used to
|
||||
compute the WDD field.
|
||||
maximum: 16
|
||||
|
||||
atmel,watchdog-type:
|
||||
$ref: /schemas/types.yaml#/definitions/string
|
||||
description: |
|
||||
Should be hardware or software.
|
||||
oneOf:
|
||||
- description:
|
||||
Hardware watchdog uses the at91 watchdog reset.
|
||||
const: hardware
|
||||
- description: |
|
||||
Software watchdog uses the watchdog interrupt
|
||||
to trigger a software reset.
|
||||
const: software
|
||||
default: hardware
|
||||
|
||||
atmel,reset-type:
|
||||
$ref: /schemas/types.yaml#/definitions/string
|
||||
description: |
|
||||
Should be proc or all. This is valid only when using hardware watchdog.
|
||||
oneOf:
|
||||
- description:
|
||||
Assert peripherals and processor reset signals.
|
||||
const: all
|
||||
- description:
|
||||
Assert the processor reset signal.
|
||||
const: proc
|
||||
default: all
|
||||
|
||||
atmel,disable:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
description:
|
||||
Should be present if you want to stop the watchdog.
|
||||
|
||||
atmel,idle-halt:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
description: |
|
||||
Should be present if you want to stop the watchdog when
|
||||
entering idle state.
|
||||
CAUTION: This property should be used with care, it actually makes the
|
||||
watchdog not counting when the CPU is in idle state, therefore the
|
||||
watchdog reset time depends on mean CPU usage and will not reset at all
|
||||
if the CPU stops working while it is in idle state, which is probably
|
||||
not what you want.
|
||||
|
||||
atmel,dbg-halt:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
description: |
|
||||
Should be present if you want to stop the watchdog when
|
||||
entering debug state.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- clocks
|
||||
|
||||
allOf:
|
||||
- $ref: watchdog.yaml#
|
||||
- if:
|
||||
properties:
|
||||
atmel,reset-type:
|
||||
enum:
|
||||
- all
|
||||
- proc
|
||||
then:
|
||||
properties:
|
||||
atmel,watchdog-type:
|
||||
const: hardware
|
||||
|
||||
dependencies:
|
||||
atmel,reset-type: ['atmel,watchdog-type']
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
watchdog@fffffd40 {
|
||||
compatible = "atmel,at91sam9260-wdt";
|
||||
reg = <0xfffffd40 0x10>;
|
||||
interrupts = <1 IRQ_TYPE_LEVEL_HIGH 7>;
|
||||
clocks = <&clk32k>;
|
||||
timeout-sec = <15>;
|
||||
atmel,watchdog-type = "hardware";
|
||||
atmel,reset-type = "all";
|
||||
atmel,dbg-halt;
|
||||
atmel,idle-halt;
|
||||
atmel,max-heartbeat-sec = <16>;
|
||||
atmel,min-heartbeat-sec = <0>;
|
||||
};
|
@ -1,51 +0,0 @@
|
||||
* Atmel Watchdog Timers
|
||||
|
||||
** at91sam9-wdt
|
||||
|
||||
Required properties:
|
||||
- compatible: must be "atmel,at91sam9260-wdt".
|
||||
- reg: physical base address of the controller and length of memory mapped
|
||||
region.
|
||||
- clocks: phandle to input clock.
|
||||
|
||||
Optional properties:
|
||||
- timeout-sec: contains the watchdog timeout in seconds.
|
||||
- interrupts : Should contain WDT interrupt.
|
||||
- atmel,max-heartbeat-sec : Should contain the maximum heartbeat value in
|
||||
seconds. This value should be less or equal to 16. It is used to
|
||||
compute the WDV field.
|
||||
- atmel,min-heartbeat-sec : Should contain the minimum heartbeat value in
|
||||
seconds. This value must be smaller than the max-heartbeat-sec value.
|
||||
It is used to compute the WDD field.
|
||||
- atmel,watchdog-type : Should be "hardware" or "software". Hardware watchdog
|
||||
use the at91 watchdog reset. Software watchdog use the watchdog
|
||||
interrupt to trigger a software reset.
|
||||
- atmel,reset-type : Should be "proc" or "all".
|
||||
"all" : assert peripherals and processor reset signals
|
||||
"proc" : assert the processor reset signal
|
||||
This is valid only when using "hardware" watchdog.
|
||||
- atmel,disable : Should be present if you want to disable the watchdog.
|
||||
- atmel,idle-halt : Should be present if you want to stop the watchdog when
|
||||
entering idle state.
|
||||
CAUTION: This property should be used with care, it actually makes the
|
||||
watchdog not counting when the CPU is in idle state, therefore the
|
||||
watchdog reset time depends on mean CPU usage and will not reset at all
|
||||
if the CPU stop working while it is in idle state, which is probably
|
||||
not what you want.
|
||||
- atmel,dbg-halt : Should be present if you want to stop the watchdog when
|
||||
entering debug state.
|
||||
|
||||
Example:
|
||||
watchdog@fffffd40 {
|
||||
compatible = "atmel,at91sam9260-wdt";
|
||||
reg = <0xfffffd40 0x10>;
|
||||
interrupts = <1 IRQ_TYPE_LEVEL_HIGH 7>;
|
||||
clocks = <&clk32k>;
|
||||
timeout-sec = <15>;
|
||||
atmel,watchdog-type = "hardware";
|
||||
atmel,reset-type = "all";
|
||||
atmel,dbg-halt;
|
||||
atmel,idle-halt;
|
||||
atmel,max-heartbeat-sec = <16>;
|
||||
atmel,min-heartbeat-sec = <0>;
|
||||
};
|
@ -0,0 +1,33 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/watchdog/mediatek,mt7621-wdt.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Ralink Watchdog Timers
|
||||
|
||||
maintainers:
|
||||
- Sergio Paracuellos <sergio.paracuellos@gmail.com>
|
||||
|
||||
allOf:
|
||||
- $ref: watchdog.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: mediatek,mt7621-wdt
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
watchdog@100 {
|
||||
compatible = "mediatek,mt7621-wdt";
|
||||
reg = <0x100 0x100>;
|
||||
};
|
@ -1,12 +0,0 @@
|
||||
Ralink Watchdog Timers
|
||||
|
||||
Required properties:
|
||||
- compatible: must be "mediatek,mt7621-wdt"
|
||||
- reg: physical base address of the controller and length of the register range
|
||||
|
||||
Example:
|
||||
|
||||
watchdog@100 {
|
||||
compatible = "mediatek,mt7621-wdt";
|
||||
reg = <0x100 0x10>;
|
||||
};
|
@ -1,26 +0,0 @@
|
||||
Xilinx AXI/PLB soft-core watchdog Device Tree Bindings
|
||||
---------------------------------------------------------
|
||||
|
||||
Required properties:
|
||||
- compatible : Should be "xlnx,xps-timebase-wdt-1.00.a" or
|
||||
"xlnx,xps-timebase-wdt-1.01.a".
|
||||
- reg : Physical base address and size
|
||||
|
||||
Optional properties:
|
||||
- clocks : Input clock specifier. Refer to common clock
|
||||
bindings.
|
||||
- clock-frequency : Frequency of clock in Hz
|
||||
- xlnx,wdt-enable-once : 0 - Watchdog can be restarted
|
||||
1 - Watchdog can be enabled just once
|
||||
- xlnx,wdt-interval : Watchdog timeout interval in 2^<val> clock cycles,
|
||||
<val> is integer from 8 to 31.
|
||||
|
||||
Example:
|
||||
axi-timebase-wdt@40100000 {
|
||||
clock-frequency = <50000000>;
|
||||
compatible = "xlnx,xps-timebase-wdt-1.00.a";
|
||||
clocks = <&clkc 15>;
|
||||
reg = <0x40100000 0x10000>;
|
||||
xlnx,wdt-enable-once = <0x0>;
|
||||
xlnx,wdt-interval = <0x1b>;
|
||||
} ;
|
@ -31,6 +31,11 @@ properties:
|
||||
- renesas,r9a07g054-wdt # RZ/V2L
|
||||
- const: renesas,rzg2l-wdt
|
||||
|
||||
- items:
|
||||
- enum:
|
||||
- renesas,r9a09g011-wdt # RZ/V2M
|
||||
- const: renesas,rzv2m-wdt # RZ/V2M
|
||||
|
||||
- items:
|
||||
- enum:
|
||||
- renesas,r8a7742-wdt # RZ/G1H
|
||||
@ -65,18 +70,35 @@ properties:
|
||||
- enum:
|
||||
- renesas,r8a779a0-wdt # R-Car V3U
|
||||
- renesas,r8a779f0-wdt # R-Car S4-8
|
||||
- renesas,r8a779g0-wdt # R-Car V4H
|
||||
- const: renesas,rcar-gen4-wdt # R-Car Gen4
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts: true
|
||||
interrupts:
|
||||
minItems: 1
|
||||
items:
|
||||
- description: Timeout
|
||||
- description: Parity error
|
||||
|
||||
interrupt-names: true
|
||||
interrupt-names:
|
||||
minItems: 1
|
||||
items:
|
||||
- const: wdt
|
||||
- const: perrout
|
||||
|
||||
clocks: true
|
||||
clocks:
|
||||
minItems: 1
|
||||
items:
|
||||
- description: Register access clock
|
||||
- description: Main clock
|
||||
|
||||
clock-names: true
|
||||
clock-names:
|
||||
minItems: 1
|
||||
items:
|
||||
- const: pclk
|
||||
- const: oscclk
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
@ -89,6 +111,7 @@ properties:
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
|
||||
allOf:
|
||||
@ -107,6 +130,26 @@ allOf:
|
||||
- power-domains
|
||||
- resets
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- renesas,rzg2l-wdt
|
||||
- renesas,rzv2m-wdt
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
minItems: 2
|
||||
clock-names:
|
||||
minItems: 2
|
||||
required:
|
||||
- clock-names
|
||||
else:
|
||||
properties:
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
@ -116,28 +159,15 @@ allOf:
|
||||
then:
|
||||
properties:
|
||||
interrupts:
|
||||
maxItems: 2
|
||||
minItems: 2
|
||||
interrupt-names:
|
||||
items:
|
||||
- const: wdt
|
||||
- const: perrout
|
||||
clocks:
|
||||
items:
|
||||
- description: Register access clock
|
||||
- description: Main clock
|
||||
clock-names:
|
||||
items:
|
||||
- const: pclk
|
||||
- const: oscclk
|
||||
minItems: 2
|
||||
required:
|
||||
- clock-names
|
||||
- interrupt-names
|
||||
else:
|
||||
properties:
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
@ -145,9 +175,11 @@ examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/r8a7795-cpg-mssr.h>
|
||||
#include <dt-bindings/power/r8a7795-sysc.h>
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
wdt0: watchdog@e6020000 {
|
||||
compatible = "renesas,r8a7795-wdt", "renesas,rcar-gen3-wdt";
|
||||
reg = <0xe6020000 0x0c>;
|
||||
interrupts = <GIC_SPI 140 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&cpg CPG_MOD 402>;
|
||||
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
|
||||
resets = <&cpg 402>;
|
||||
|
@ -23,6 +23,7 @@ properties:
|
||||
- samsung,exynos5420-wdt # for Exynos5420
|
||||
- samsung,exynos7-wdt # for Exynos7
|
||||
- samsung,exynos850-wdt # for Exynos850
|
||||
- samsung,exynosautov9-wdt # for Exynosautov9
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
@ -67,6 +68,7 @@ allOf:
|
||||
- samsung,exynos5420-wdt
|
||||
- samsung,exynos7-wdt
|
||||
- samsung,exynos850-wdt
|
||||
- samsung,exynosautov9-wdt
|
||||
then:
|
||||
required:
|
||||
- samsung,syscon-phandle
|
||||
@ -76,6 +78,7 @@ allOf:
|
||||
contains:
|
||||
enum:
|
||||
- samsung,exynos850-wdt
|
||||
- samsung,exynosautov9-wdt
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
|
@ -20,6 +20,7 @@ properties:
|
||||
- enum:
|
||||
- rockchip,px30-wdt
|
||||
- rockchip,rk3066-wdt
|
||||
- rockchip,rk3128-wdt
|
||||
- rockchip,rk3188-wdt
|
||||
- rockchip,rk3228-wdt
|
||||
- rockchip,rk3288-wdt
|
||||
|
@ -35,20 +35,16 @@ additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/toshiba,tmpv770x.h>
|
||||
|
||||
soc {
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
|
||||
wdt_clk: wdt-clk {
|
||||
compatible = "fixed-clock";
|
||||
clock-frequency = <150000000>;
|
||||
#clock-cells = <0>;
|
||||
};
|
||||
|
||||
watchdog@28330000 {
|
||||
wdt: watchdog@28330000 {
|
||||
compatible = "toshiba,visconti-wdt";
|
||||
reg = <0 0x28330000 0 0x1000>;
|
||||
clocks = <&wdt_clk>;
|
||||
timeout-sec = <20>;
|
||||
clocks = <&pismu TMPV770X_CLK_WDTCLK>;
|
||||
};
|
||||
};
|
||||
|
@ -0,0 +1,68 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/watchdog/xlnx,xps-timebase-wdt.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Xilinx AXI/PLB softcore and window Watchdog Timer
|
||||
|
||||
maintainers:
|
||||
- Shubhrajyoti Datta <shubhrajyoti.datta@xilinx.com>
|
||||
- Srinivas Neeli <srinivas.neeli@xilinx.com>
|
||||
|
||||
description:
|
||||
The Timebase watchdog timer(WDT) is a free-running 32 bit counter.
|
||||
WDT uses a dual-expiration architecture. After one expiration of
|
||||
the timeout interval, an interrupt is generated and the WDT state
|
||||
bit is set to one in the status register. If the state bit is not
|
||||
cleared (by writing a one to the state bit) before the next
|
||||
expiration of the timeout interval, a WDT reset is generated.
|
||||
|
||||
allOf:
|
||||
- $ref: watchdog.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- xlnx,xps-timebase-wdt-1.01.a
|
||||
- xlnx,xps-timebase-wdt-1.00.a
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
clock-frequency:
|
||||
description: Frequency of clock in Hz
|
||||
|
||||
xlnx,wdt-interval:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description: Watchdog timeout interval
|
||||
minimum: 8
|
||||
maximum: 32
|
||||
|
||||
xlnx,wdt-enable-once:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
enum: [0, 1]
|
||||
description: If watchdog is configured as enable once,
|
||||
then the watchdog cannot be disabled after
|
||||
it has been enabled.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
watchdog@40100000 {
|
||||
compatible = "xlnx,xps-timebase-wdt-1.00.a";
|
||||
reg = <0x40100000 0x1000>;
|
||||
clock-frequency = <50000000>;
|
||||
clocks = <&clkc 15>;
|
||||
xlnx,wdt-enable-once = <0x0>;
|
||||
xlnx,wdt-interval = <0x1b>;
|
||||
};
|
||||
...
|
@ -1089,6 +1089,17 @@ config EBC_C384_WDT
|
||||
WinSystems EBC-C384 motherboard. The timeout may be configured via
|
||||
the timeout module parameter.
|
||||
|
||||
config EXAR_WDT
|
||||
tristate "Exar Watchdog Timer"
|
||||
depends on X86
|
||||
select WATCHDOG_CORE
|
||||
help
|
||||
Enables watchdog timer support for the watchdog timer present
|
||||
in some Exar/MaxLinear UART chips like the XR28V38x.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called exar_wdt.
|
||||
|
||||
config F71808E_WDT
|
||||
tristate "Fintek F718xx, F818xx Super I/O Watchdog"
|
||||
depends on X86
|
||||
@ -1315,7 +1326,7 @@ config IT87_WDT
|
||||
config HP_WATCHDOG
|
||||
tristate "HP ProLiant iLO2+ Hardware Watchdog Timer"
|
||||
select WATCHDOG_CORE
|
||||
depends on X86 && PCI
|
||||
depends on (ARM64 || X86) && PCI
|
||||
help
|
||||
A software monitoring watchdog and NMI handling driver. This driver
|
||||
will detect lockups and provide a stack trace. This is a driver that
|
||||
@ -1325,7 +1336,7 @@ config HP_WATCHDOG
|
||||
|
||||
config HPWDT_NMI_DECODING
|
||||
bool "NMI support for the HP ProLiant iLO2+ Hardware Watchdog Timer"
|
||||
depends on HP_WATCHDOG
|
||||
depends on X86 && HP_WATCHDOG
|
||||
default y
|
||||
help
|
||||
Enables the NMI handler for the watchdog pretimeout NMI and the iLO
|
||||
|
@ -105,6 +105,7 @@ obj-$(CONFIG_ADVANTECH_WDT) += advantechwdt.o
|
||||
obj-$(CONFIG_ALIM1535_WDT) += alim1535_wdt.o
|
||||
obj-$(CONFIG_ALIM7101_WDT) += alim7101_wdt.o
|
||||
obj-$(CONFIG_EBC_C384_WDT) += ebc-c384_wdt.o
|
||||
obj-$(CONFIG_EXAR_WDT) += exar_wdt.o
|
||||
obj-$(CONFIG_F71808E_WDT) += f71808e_wdt.o
|
||||
obj-$(CONFIG_SP5100_TCO) += sp5100_tco.o
|
||||
obj-$(CONFIG_GEODE_WDT) += geodewdt.o
|
||||
|
@ -179,6 +179,8 @@ static int armada_37xx_wdt_set_timeout(struct watchdog_device *wdt,
|
||||
dev->timeout = (u64)dev->clk_rate * timeout;
|
||||
do_div(dev->timeout, CNTR_CTRL_PRESCALE_MIN);
|
||||
|
||||
set_counter_value(dev, CNTR_ID_WDOG, dev->timeout);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -331,14 +331,6 @@ static int aspeed_wdt_probe(struct platform_device *pdev)
|
||||
(of_device_is_compatible(np, "aspeed,ast2600-wdt"))) {
|
||||
u32 reg = readl(wdt->base + WDT_RESET_WIDTH);
|
||||
|
||||
reg &= config->ext_pulse_width_mask;
|
||||
if (of_property_read_bool(np, "aspeed,ext-push-pull"))
|
||||
reg |= WDT_PUSH_PULL_MAGIC;
|
||||
else
|
||||
reg |= WDT_OPEN_DRAIN_MAGIC;
|
||||
|
||||
writel(reg, wdt->base + WDT_RESET_WIDTH);
|
||||
|
||||
reg &= config->ext_pulse_width_mask;
|
||||
if (of_property_read_bool(np, "aspeed,ext-active-high"))
|
||||
reg |= WDT_ACTIVE_HIGH_MAGIC;
|
||||
@ -346,6 +338,14 @@ static int aspeed_wdt_probe(struct platform_device *pdev)
|
||||
reg |= WDT_ACTIVE_LOW_MAGIC;
|
||||
|
||||
writel(reg, wdt->base + WDT_RESET_WIDTH);
|
||||
|
||||
reg &= config->ext_pulse_width_mask;
|
||||
if (of_property_read_bool(np, "aspeed,ext-push-pull"))
|
||||
reg |= WDT_PUSH_PULL_MAGIC;
|
||||
else
|
||||
reg |= WDT_OPEN_DRAIN_MAGIC;
|
||||
|
||||
writel(reg, wdt->base + WDT_RESET_WIDTH);
|
||||
}
|
||||
|
||||
if (!of_property_read_u32(np, "aspeed,ext-pulse-duration", &duration)) {
|
||||
|
@ -9,8 +9,8 @@
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/mfd/rohm-bd957x.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/watchdog.h>
|
||||
|
||||
@ -202,10 +202,10 @@ static int bd957x_set_wdt_mode(struct bd9576_wdt_priv *priv, int hw_margin,
|
||||
static int bd9576_wdt_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->parent->of_node;
|
||||
struct bd9576_wdt_priv *priv;
|
||||
u32 hw_margin[2];
|
||||
u32 hw_margin_max = BD957X_WDT_DEFAULT_MARGIN, hw_margin_min = 0;
|
||||
int count;
|
||||
int ret;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
@ -221,40 +221,51 @@ static int bd9576_wdt_probe(struct platform_device *pdev)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
priv->gpiod_en = devm_gpiod_get_from_of_node(dev, dev->parent->of_node,
|
||||
"rohm,watchdog-enable-gpios",
|
||||
0, GPIOD_OUT_LOW,
|
||||
"watchdog-enable");
|
||||
priv->gpiod_en = devm_fwnode_gpiod_get(dev, dev_fwnode(dev->parent),
|
||||
"rohm,watchdog-enable",
|
||||
GPIOD_OUT_LOW,
|
||||
"watchdog-enable");
|
||||
if (IS_ERR(priv->gpiod_en))
|
||||
return dev_err_probe(dev, PTR_ERR(priv->gpiod_en),
|
||||
"getting watchdog-enable GPIO failed\n");
|
||||
|
||||
priv->gpiod_ping = devm_gpiod_get_from_of_node(dev, dev->parent->of_node,
|
||||
"rohm,watchdog-ping-gpios",
|
||||
0, GPIOD_OUT_LOW,
|
||||
"watchdog-ping");
|
||||
priv->gpiod_ping = devm_fwnode_gpiod_get(dev, dev_fwnode(dev->parent),
|
||||
"rohm,watchdog-ping",
|
||||
GPIOD_OUT_LOW,
|
||||
"watchdog-ping");
|
||||
if (IS_ERR(priv->gpiod_ping))
|
||||
return dev_err_probe(dev, PTR_ERR(priv->gpiod_ping),
|
||||
"getting watchdog-ping GPIO failed\n");
|
||||
|
||||
ret = of_property_read_variable_u32_array(np, "rohm,hw-timeout-ms",
|
||||
&hw_margin[0], 1, 2);
|
||||
if (ret < 0 && ret != -EINVAL)
|
||||
return ret;
|
||||
count = device_property_count_u32(dev->parent, "rohm,hw-timeout-ms");
|
||||
if (count < 0 && count != -EINVAL)
|
||||
return count;
|
||||
|
||||
if (ret == 1)
|
||||
hw_margin_max = hw_margin[0];
|
||||
if (count > 0) {
|
||||
if (count > ARRAY_SIZE(hw_margin))
|
||||
return -EINVAL;
|
||||
|
||||
if (ret == 2) {
|
||||
hw_margin_max = hw_margin[1];
|
||||
hw_margin_min = hw_margin[0];
|
||||
ret = device_property_read_u32_array(dev->parent,
|
||||
"rohm,hw-timeout-ms",
|
||||
hw_margin, count);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (count == 1)
|
||||
hw_margin_max = hw_margin[0];
|
||||
|
||||
if (count == 2) {
|
||||
hw_margin_max = hw_margin[1];
|
||||
hw_margin_min = hw_margin[0];
|
||||
}
|
||||
}
|
||||
|
||||
ret = bd957x_set_wdt_mode(priv, hw_margin_max, hw_margin_min);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
priv->always_running = of_property_read_bool(np, "always-running");
|
||||
priv->always_running = device_property_read_bool(dev->parent,
|
||||
"always-running");
|
||||
|
||||
watchdog_set_drvdata(&priv->wdd, priv);
|
||||
|
||||
|
@ -192,7 +192,7 @@ static void eurwdt_ping(void)
|
||||
* @ppos: pointer to the position to write. No seeks allowed
|
||||
*
|
||||
* A write to a watchdog device is defined as a keepalive signal. Any
|
||||
* write of data will do, as we we don't define content meaning.
|
||||
* write of data will do, as we don't define content meaning.
|
||||
*/
|
||||
|
||||
static ssize_t eurwdt_write(struct file *file, const char __user *buf,
|
||||
|
427
drivers/watchdog/exar_wdt.c
Normal file
427
drivers/watchdog/exar_wdt.c
Normal file
@ -0,0 +1,427 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* exar_wdt.c - Driver for the watchdog present in some
|
||||
* Exar/MaxLinear UART chips like the XR28V38x.
|
||||
*
|
||||
* (c) Copyright 2022 D. Müller <d.mueller@elsoft.ch>.
|
||||
*
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/io.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/watchdog.h>
|
||||
|
||||
#define DRV_NAME "exar_wdt"
|
||||
|
||||
static const unsigned short sio_config_ports[] = { 0x2e, 0x4e };
|
||||
static const unsigned char sio_enter_keys[] = { 0x67, 0x77, 0x87, 0xA0 };
|
||||
#define EXAR_EXIT_KEY 0xAA
|
||||
|
||||
#define EXAR_LDN 0x07
|
||||
#define EXAR_DID 0x20
|
||||
#define EXAR_VID 0x23
|
||||
#define EXAR_WDT 0x26
|
||||
#define EXAR_ACT 0x30
|
||||
#define EXAR_RTBASE 0x60
|
||||
|
||||
#define EXAR_WDT_LDEV 0x08
|
||||
|
||||
#define EXAR_VEN_ID 0x13A8
|
||||
#define EXAR_DEV_382 0x0382
|
||||
#define EXAR_DEV_384 0x0384
|
||||
|
||||
/* WDT runtime registers */
|
||||
#define WDT_CTRL 0x00
|
||||
#define WDT_VAL 0x01
|
||||
|
||||
#define WDT_UNITS_10MS 0x0 /* the 10 millisec unit of the HW is not used */
|
||||
#define WDT_UNITS_SEC 0x2
|
||||
#define WDT_UNITS_MIN 0x4
|
||||
|
||||
/* default WDT control for WDTOUT signal activ / rearm by read */
|
||||
#define EXAR_WDT_DEF_CONF 0
|
||||
|
||||
struct wdt_pdev_node {
|
||||
struct list_head list;
|
||||
struct platform_device *pdev;
|
||||
const char name[16];
|
||||
};
|
||||
|
||||
struct wdt_priv {
|
||||
/* the lock for WDT io operations */
|
||||
spinlock_t io_lock;
|
||||
struct resource wdt_res;
|
||||
struct watchdog_device wdt_dev;
|
||||
unsigned short did;
|
||||
unsigned short config_port;
|
||||
unsigned char enter_key;
|
||||
unsigned char unit;
|
||||
unsigned char timeout;
|
||||
};
|
||||
|
||||
#define WATCHDOG_TIMEOUT 60
|
||||
|
||||
static int timeout = WATCHDOG_TIMEOUT;
|
||||
module_param(timeout, int, 0);
|
||||
MODULE_PARM_DESC(timeout,
|
||||
"Watchdog timeout in seconds. 1<=timeout<=15300, default="
|
||||
__MODULE_STRING(WATCHDOG_TIMEOUT) ".");
|
||||
|
||||
static bool nowayout = WATCHDOG_NOWAYOUT;
|
||||
module_param(nowayout, bool, 0);
|
||||
MODULE_PARM_DESC(nowayout,
|
||||
"Watchdog cannot be stopped once started (default="
|
||||
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
|
||||
|
||||
static int exar_sio_enter(const unsigned short config_port,
|
||||
const unsigned char key)
|
||||
{
|
||||
if (!request_muxed_region(config_port, 2, DRV_NAME))
|
||||
return -EBUSY;
|
||||
|
||||
/* write the ENTER-KEY twice */
|
||||
outb(key, config_port);
|
||||
outb(key, config_port);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void exar_sio_exit(const unsigned short config_port)
|
||||
{
|
||||
outb(EXAR_EXIT_KEY, config_port);
|
||||
release_region(config_port, 2);
|
||||
}
|
||||
|
||||
static unsigned char exar_sio_read(const unsigned short config_port,
|
||||
const unsigned char reg)
|
||||
{
|
||||
outb(reg, config_port);
|
||||
return inb(config_port + 1);
|
||||
}
|
||||
|
||||
static void exar_sio_write(const unsigned short config_port,
|
||||
const unsigned char reg, const unsigned char val)
|
||||
{
|
||||
outb(reg, config_port);
|
||||
outb(val, config_port + 1);
|
||||
}
|
||||
|
||||
static unsigned short exar_sio_read16(const unsigned short config_port,
|
||||
const unsigned char reg)
|
||||
{
|
||||
unsigned char msb, lsb;
|
||||
|
||||
msb = exar_sio_read(config_port, reg);
|
||||
lsb = exar_sio_read(config_port, reg + 1);
|
||||
|
||||
return (msb << 8) | lsb;
|
||||
}
|
||||
|
||||
static void exar_sio_select_wdt(const unsigned short config_port)
|
||||
{
|
||||
exar_sio_write(config_port, EXAR_LDN, EXAR_WDT_LDEV);
|
||||
}
|
||||
|
||||
static void exar_wdt_arm(const struct wdt_priv *priv)
|
||||
{
|
||||
unsigned short rt_base = priv->wdt_res.start;
|
||||
|
||||
/* write timeout value twice to arm watchdog */
|
||||
outb(priv->timeout, rt_base + WDT_VAL);
|
||||
outb(priv->timeout, rt_base + WDT_VAL);
|
||||
}
|
||||
|
||||
static void exar_wdt_disarm(const struct wdt_priv *priv)
|
||||
{
|
||||
unsigned short rt_base = priv->wdt_res.start;
|
||||
|
||||
/*
|
||||
* use two accesses with different values to make sure
|
||||
* that a combination of a previous single access and
|
||||
* the ones below with the same value are not falsely
|
||||
* interpreted as "arm watchdog"
|
||||
*/
|
||||
outb(0xFF, rt_base + WDT_VAL);
|
||||
outb(0, rt_base + WDT_VAL);
|
||||
}
|
||||
|
||||
static int exar_wdt_start(struct watchdog_device *wdog)
|
||||
{
|
||||
struct wdt_priv *priv = watchdog_get_drvdata(wdog);
|
||||
unsigned short rt_base = priv->wdt_res.start;
|
||||
|
||||
spin_lock(&priv->io_lock);
|
||||
|
||||
exar_wdt_disarm(priv);
|
||||
outb(priv->unit, rt_base + WDT_CTRL);
|
||||
exar_wdt_arm(priv);
|
||||
|
||||
spin_unlock(&priv->io_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int exar_wdt_stop(struct watchdog_device *wdog)
|
||||
{
|
||||
struct wdt_priv *priv = watchdog_get_drvdata(wdog);
|
||||
|
||||
spin_lock(&priv->io_lock);
|
||||
|
||||
exar_wdt_disarm(priv);
|
||||
|
||||
spin_unlock(&priv->io_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int exar_wdt_keepalive(struct watchdog_device *wdog)
|
||||
{
|
||||
struct wdt_priv *priv = watchdog_get_drvdata(wdog);
|
||||
unsigned short rt_base = priv->wdt_res.start;
|
||||
|
||||
spin_lock(&priv->io_lock);
|
||||
|
||||
/* reading the WDT_VAL reg will feed the watchdog */
|
||||
inb(rt_base + WDT_VAL);
|
||||
|
||||
spin_unlock(&priv->io_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int exar_wdt_set_timeout(struct watchdog_device *wdog, unsigned int t)
|
||||
{
|
||||
struct wdt_priv *priv = watchdog_get_drvdata(wdog);
|
||||
bool unit_min = false;
|
||||
|
||||
/*
|
||||
* if new timeout is bigger then 255 seconds, change the
|
||||
* unit to minutes and round the timeout up to the next whole minute
|
||||
*/
|
||||
if (t > 255) {
|
||||
unit_min = true;
|
||||
t = DIV_ROUND_UP(t, 60);
|
||||
}
|
||||
|
||||
/* save for later use in exar_wdt_start() */
|
||||
priv->unit = unit_min ? WDT_UNITS_MIN : WDT_UNITS_SEC;
|
||||
priv->timeout = t;
|
||||
|
||||
wdog->timeout = unit_min ? t * 60 : t;
|
||||
|
||||
if (watchdog_hw_running(wdog))
|
||||
exar_wdt_start(wdog);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct watchdog_info exar_wdt_info = {
|
||||
.options = WDIOF_KEEPALIVEPING |
|
||||
WDIOF_SETTIMEOUT |
|
||||
WDIOF_MAGICCLOSE,
|
||||
.identity = "Exar/MaxLinear XR28V38x Watchdog",
|
||||
};
|
||||
|
||||
static const struct watchdog_ops exar_wdt_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.start = exar_wdt_start,
|
||||
.stop = exar_wdt_stop,
|
||||
.ping = exar_wdt_keepalive,
|
||||
.set_timeout = exar_wdt_set_timeout,
|
||||
};
|
||||
|
||||
static int exar_wdt_config(struct watchdog_device *wdog,
|
||||
const unsigned char conf)
|
||||
{
|
||||
struct wdt_priv *priv = watchdog_get_drvdata(wdog);
|
||||
int ret;
|
||||
|
||||
ret = exar_sio_enter(priv->config_port, priv->enter_key);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
exar_sio_select_wdt(priv->config_port);
|
||||
exar_sio_write(priv->config_port, EXAR_WDT, conf);
|
||||
|
||||
exar_sio_exit(priv->config_port);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init exar_wdt_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct wdt_priv *priv = dev->platform_data;
|
||||
struct watchdog_device *wdt_dev = &priv->wdt_dev;
|
||||
struct resource *res;
|
||||
int ret;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
|
||||
if (!res)
|
||||
return -ENXIO;
|
||||
|
||||
spin_lock_init(&priv->io_lock);
|
||||
|
||||
wdt_dev->info = &exar_wdt_info;
|
||||
wdt_dev->ops = &exar_wdt_ops;
|
||||
wdt_dev->min_timeout = 1;
|
||||
wdt_dev->max_timeout = 255 * 60;
|
||||
|
||||
watchdog_init_timeout(wdt_dev, timeout, NULL);
|
||||
watchdog_set_nowayout(wdt_dev, nowayout);
|
||||
watchdog_stop_on_reboot(wdt_dev);
|
||||
watchdog_stop_on_unregister(wdt_dev);
|
||||
watchdog_set_drvdata(wdt_dev, priv);
|
||||
|
||||
ret = exar_wdt_config(wdt_dev, EXAR_WDT_DEF_CONF);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
exar_wdt_set_timeout(wdt_dev, timeout);
|
||||
/* Make sure that the watchdog is not running */
|
||||
exar_wdt_stop(wdt_dev);
|
||||
|
||||
ret = devm_watchdog_register_device(dev, wdt_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev_info(dev, "XR28V%X WDT initialized. timeout=%d sec (nowayout=%d)\n",
|
||||
priv->did, timeout, nowayout);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned short __init exar_detect(const unsigned short config_port,
|
||||
const unsigned char key,
|
||||
unsigned short *rt_base)
|
||||
{
|
||||
int ret;
|
||||
unsigned short base = 0;
|
||||
unsigned short vid, did;
|
||||
|
||||
ret = exar_sio_enter(config_port, key);
|
||||
if (ret)
|
||||
return 0;
|
||||
|
||||
vid = exar_sio_read16(config_port, EXAR_VID);
|
||||
did = exar_sio_read16(config_port, EXAR_DID);
|
||||
|
||||
/* check for the vendor and device IDs we currently know about */
|
||||
if (vid == EXAR_VEN_ID &&
|
||||
(did == EXAR_DEV_382 ||
|
||||
did == EXAR_DEV_384)) {
|
||||
exar_sio_select_wdt(config_port);
|
||||
/* is device active? */
|
||||
if (exar_sio_read(config_port, EXAR_ACT) == 0x01)
|
||||
base = exar_sio_read16(config_port, EXAR_RTBASE);
|
||||
}
|
||||
|
||||
exar_sio_exit(config_port);
|
||||
|
||||
if (base) {
|
||||
pr_debug("Found a XR28V%X WDT (conf: 0x%x / rt: 0x%04x)\n",
|
||||
did, config_port, base);
|
||||
*rt_base = base;
|
||||
return did;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver exar_wdt_driver = {
|
||||
.driver = {
|
||||
.name = DRV_NAME,
|
||||
},
|
||||
};
|
||||
|
||||
static LIST_HEAD(pdev_list);
|
||||
|
||||
static int __init exar_wdt_register(struct wdt_priv *priv, const int idx)
|
||||
{
|
||||
struct wdt_pdev_node *n;
|
||||
|
||||
n = kzalloc(sizeof(*n), GFP_KERNEL);
|
||||
if (!n)
|
||||
return -ENOMEM;
|
||||
|
||||
INIT_LIST_HEAD(&n->list);
|
||||
|
||||
scnprintf((char *)n->name, sizeof(n->name), DRV_NAME ".%d", idx);
|
||||
priv->wdt_res.name = n->name;
|
||||
|
||||
n->pdev = platform_device_register_resndata(NULL, DRV_NAME, idx,
|
||||
&priv->wdt_res, 1,
|
||||
priv, sizeof(*priv));
|
||||
if (IS_ERR(n->pdev)) {
|
||||
kfree(n);
|
||||
return PTR_ERR(n->pdev);
|
||||
}
|
||||
|
||||
list_add_tail(&n->list, &pdev_list);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void exar_wdt_unregister(void)
|
||||
{
|
||||
struct wdt_pdev_node *n, *t;
|
||||
|
||||
list_for_each_entry_safe(n, t, &pdev_list, list) {
|
||||
platform_device_unregister(n->pdev);
|
||||
list_del(&n->list);
|
||||
kfree(n);
|
||||
}
|
||||
}
|
||||
|
||||
static int __init exar_wdt_init(void)
|
||||
{
|
||||
int ret, i, j, idx = 0;
|
||||
|
||||
/* search for active Exar watchdogs on all possible locations */
|
||||
for (i = 0; i < ARRAY_SIZE(sio_config_ports); i++) {
|
||||
for (j = 0; j < ARRAY_SIZE(sio_enter_keys); j++) {
|
||||
unsigned short did, rt_base = 0;
|
||||
|
||||
did = exar_detect(sio_config_ports[i],
|
||||
sio_enter_keys[j],
|
||||
&rt_base);
|
||||
|
||||
if (did) {
|
||||
struct wdt_priv priv = {
|
||||
.wdt_res = DEFINE_RES_IO(rt_base, 2),
|
||||
.did = did,
|
||||
.config_port = sio_config_ports[i],
|
||||
.enter_key = sio_enter_keys[j],
|
||||
};
|
||||
|
||||
ret = exar_wdt_register(&priv, idx);
|
||||
if (!ret)
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!idx)
|
||||
return -ENODEV;
|
||||
|
||||
ret = platform_driver_probe(&exar_wdt_driver, exar_wdt_probe);
|
||||
if (ret)
|
||||
exar_wdt_unregister();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit exar_wdt_exit(void)
|
||||
{
|
||||
exar_wdt_unregister();
|
||||
platform_driver_unregister(&exar_wdt_driver);
|
||||
}
|
||||
|
||||
module_init(exar_wdt_init);
|
||||
module_exit(exar_wdt_exit);
|
||||
|
||||
MODULE_AUTHOR("David Müller <d.mueller@elsoft.ch>");
|
||||
MODULE_DESCRIPTION("Exar/MaxLinear Watchdog Driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -47,21 +47,28 @@ struct ftwdt010_wdt *to_ftwdt010_wdt(struct watchdog_device *wdd)
|
||||
return container_of(wdd, struct ftwdt010_wdt, wdd);
|
||||
}
|
||||
|
||||
static int ftwdt010_wdt_start(struct watchdog_device *wdd)
|
||||
static void ftwdt010_enable(struct ftwdt010_wdt *gwdt,
|
||||
unsigned int timeout,
|
||||
bool need_irq)
|
||||
{
|
||||
struct ftwdt010_wdt *gwdt = to_ftwdt010_wdt(wdd);
|
||||
u32 enable;
|
||||
|
||||
writel(wdd->timeout * WDT_CLOCK, gwdt->base + FTWDT010_WDLOAD);
|
||||
writel(timeout * WDT_CLOCK, gwdt->base + FTWDT010_WDLOAD);
|
||||
writel(WDRESTART_MAGIC, gwdt->base + FTWDT010_WDRESTART);
|
||||
/* set clock before enabling */
|
||||
enable = WDCR_CLOCK_5MHZ | WDCR_SYS_RST;
|
||||
writel(enable, gwdt->base + FTWDT010_WDCR);
|
||||
if (gwdt->has_irq)
|
||||
if (need_irq)
|
||||
enable |= WDCR_WDINTR;
|
||||
enable |= WDCR_ENABLE;
|
||||
writel(enable, gwdt->base + FTWDT010_WDCR);
|
||||
}
|
||||
|
||||
static int ftwdt010_wdt_start(struct watchdog_device *wdd)
|
||||
{
|
||||
struct ftwdt010_wdt *gwdt = to_ftwdt010_wdt(wdd);
|
||||
|
||||
ftwdt010_enable(gwdt, wdd->timeout, gwdt->has_irq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -93,6 +100,13 @@ static int ftwdt010_wdt_set_timeout(struct watchdog_device *wdd,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ftwdt010_wdt_restart(struct watchdog_device *wdd,
|
||||
unsigned long action, void *data)
|
||||
{
|
||||
ftwdt010_enable(to_ftwdt010_wdt(wdd), 0, false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t ftwdt010_wdt_interrupt(int irq, void *data)
|
||||
{
|
||||
struct ftwdt010_wdt *gwdt = data;
|
||||
@ -107,6 +121,7 @@ static const struct watchdog_ops ftwdt010_wdt_ops = {
|
||||
.stop = ftwdt010_wdt_stop,
|
||||
.ping = ftwdt010_wdt_ping,
|
||||
.set_timeout = ftwdt010_wdt_set_timeout,
|
||||
.restart = ftwdt010_wdt_restart,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
@ -156,7 +171,7 @@ static int ftwdt010_wdt_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq) {
|
||||
if (irq > 0) {
|
||||
ret = devm_request_irq(dev, irq, ftwdt010_wdt_interrupt, 0,
|
||||
"watchdog bark", gwdt);
|
||||
if (ret)
|
||||
|
@ -20,7 +20,9 @@
|
||||
#include <linux/pci_ids.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/watchdog.h>
|
||||
#ifdef CONFIG_HPWDT_NMI_DECODING
|
||||
#include <asm/nmi.h>
|
||||
#endif
|
||||
#include <linux/crash_dump.h>
|
||||
|
||||
#define HPWDT_VERSION "2.0.4"
|
||||
|
@ -9,12 +9,15 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/watchdog.h>
|
||||
|
||||
#define WDOG_CS 0x0
|
||||
#define WDOG_CS_FLG BIT(14)
|
||||
#define WDOG_CS_CMD32EN BIT(13)
|
||||
#define WDOG_CS_PRES BIT(12)
|
||||
#define WDOG_CS_ULK BIT(11)
|
||||
#define WDOG_CS_RCS BIT(10)
|
||||
#define LPO_CLK 0x1
|
||||
@ -39,60 +42,105 @@
|
||||
#define DEFAULT_TIMEOUT 60
|
||||
#define MAX_TIMEOUT 128
|
||||
#define WDOG_CLOCK_RATE 1000
|
||||
#define WDOG_WAIT_TIMEOUT 20
|
||||
#define WDOG_ULK_WAIT_TIMEOUT 1000
|
||||
#define WDOG_RCS_WAIT_TIMEOUT 10000
|
||||
#define WDOG_RCS_POST_WAIT 3000
|
||||
|
||||
#define RETRY_MAX 5
|
||||
|
||||
static bool nowayout = WATCHDOG_NOWAYOUT;
|
||||
module_param(nowayout, bool, 0000);
|
||||
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
|
||||
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
|
||||
|
||||
struct imx_wdt_hw_feature {
|
||||
bool prescaler_enable;
|
||||
u32 wdog_clock_rate;
|
||||
};
|
||||
|
||||
struct imx7ulp_wdt_device {
|
||||
struct watchdog_device wdd;
|
||||
void __iomem *base;
|
||||
struct clk *clk;
|
||||
bool post_rcs_wait;
|
||||
const struct imx_wdt_hw_feature *hw;
|
||||
};
|
||||
|
||||
static int imx7ulp_wdt_wait(void __iomem *base, u32 mask)
|
||||
static int imx7ulp_wdt_wait_ulk(void __iomem *base)
|
||||
{
|
||||
u32 val = readl(base + WDOG_CS);
|
||||
|
||||
if (!(val & mask) && readl_poll_timeout_atomic(base + WDOG_CS, val,
|
||||
val & mask, 0,
|
||||
WDOG_WAIT_TIMEOUT))
|
||||
if (!(val & WDOG_CS_ULK) &&
|
||||
readl_poll_timeout_atomic(base + WDOG_CS, val,
|
||||
val & WDOG_CS_ULK, 0,
|
||||
WDOG_ULK_WAIT_TIMEOUT))
|
||||
return -ETIMEDOUT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx7ulp_wdt_enable(struct watchdog_device *wdog, bool enable)
|
||||
static int imx7ulp_wdt_wait_rcs(struct imx7ulp_wdt_device *wdt)
|
||||
{
|
||||
struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog);
|
||||
int ret = 0;
|
||||
u32 val = readl(wdt->base + WDOG_CS);
|
||||
u64 timeout = (val & WDOG_CS_PRES) ?
|
||||
WDOG_RCS_WAIT_TIMEOUT * 256 : WDOG_RCS_WAIT_TIMEOUT;
|
||||
unsigned long wait_min = (val & WDOG_CS_PRES) ?
|
||||
WDOG_RCS_POST_WAIT * 256 : WDOG_RCS_POST_WAIT;
|
||||
|
||||
if (!(val & WDOG_CS_RCS) &&
|
||||
readl_poll_timeout(wdt->base + WDOG_CS, val, val & WDOG_CS_RCS, 100,
|
||||
timeout))
|
||||
ret = -ETIMEDOUT;
|
||||
|
||||
/* Wait 2.5 clocks after RCS done */
|
||||
if (wdt->post_rcs_wait)
|
||||
usleep_range(wait_min, wait_min + 2000);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int _imx7ulp_wdt_enable(struct imx7ulp_wdt_device *wdt, bool enable)
|
||||
{
|
||||
u32 val = readl(wdt->base + WDOG_CS);
|
||||
int ret;
|
||||
|
||||
local_irq_disable();
|
||||
writel(UNLOCK, wdt->base + WDOG_CNT);
|
||||
ret = imx7ulp_wdt_wait(wdt->base, WDOG_CS_ULK);
|
||||
ret = imx7ulp_wdt_wait_ulk(wdt->base);
|
||||
if (ret)
|
||||
goto enable_out;
|
||||
if (enable)
|
||||
writel(val | WDOG_CS_EN, wdt->base + WDOG_CS);
|
||||
else
|
||||
writel(val & ~WDOG_CS_EN, wdt->base + WDOG_CS);
|
||||
imx7ulp_wdt_wait(wdt->base, WDOG_CS_RCS);
|
||||
|
||||
local_irq_enable();
|
||||
ret = imx7ulp_wdt_wait_rcs(wdt);
|
||||
|
||||
return ret;
|
||||
|
||||
enable_out:
|
||||
local_irq_enable();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool imx7ulp_wdt_is_enabled(void __iomem *base)
|
||||
static int imx7ulp_wdt_enable(struct watchdog_device *wdog, bool enable)
|
||||
{
|
||||
u32 val = readl(base + WDOG_CS);
|
||||
struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog);
|
||||
int ret;
|
||||
u32 val;
|
||||
u32 loop = RETRY_MAX;
|
||||
|
||||
return val & WDOG_CS_EN;
|
||||
do {
|
||||
ret = _imx7ulp_wdt_enable(wdt, enable);
|
||||
val = readl(wdt->base + WDOG_CS);
|
||||
} while (--loop > 0 && ((!!(val & WDOG_CS_EN)) != enable || ret));
|
||||
|
||||
if (loop == 0)
|
||||
return -EBUSY;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int imx7ulp_wdt_ping(struct watchdog_device *wdog)
|
||||
@ -114,26 +162,44 @@ static int imx7ulp_wdt_stop(struct watchdog_device *wdog)
|
||||
return imx7ulp_wdt_enable(wdog, false);
|
||||
}
|
||||
|
||||
static int imx7ulp_wdt_set_timeout(struct watchdog_device *wdog,
|
||||
unsigned int timeout)
|
||||
static int _imx7ulp_wdt_set_timeout(struct imx7ulp_wdt_device *wdt,
|
||||
unsigned int toval)
|
||||
{
|
||||
struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog);
|
||||
u32 val = WDOG_CLOCK_RATE * timeout;
|
||||
int ret;
|
||||
|
||||
local_irq_disable();
|
||||
writel(UNLOCK, wdt->base + WDOG_CNT);
|
||||
ret = imx7ulp_wdt_wait(wdt->base, WDOG_CS_ULK);
|
||||
ret = imx7ulp_wdt_wait_ulk(wdt->base);
|
||||
if (ret)
|
||||
goto timeout_out;
|
||||
writel(val, wdt->base + WDOG_TOVAL);
|
||||
imx7ulp_wdt_wait(wdt->base, WDOG_CS_RCS);
|
||||
|
||||
wdog->timeout = timeout;
|
||||
writel(toval, wdt->base + WDOG_TOVAL);
|
||||
local_irq_enable();
|
||||
ret = imx7ulp_wdt_wait_rcs(wdt);
|
||||
return ret;
|
||||
|
||||
timeout_out:
|
||||
local_irq_enable();
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int imx7ulp_wdt_set_timeout(struct watchdog_device *wdog,
|
||||
unsigned int timeout)
|
||||
{
|
||||
struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog);
|
||||
u32 toval = wdt->hw->wdog_clock_rate * timeout;
|
||||
u32 val;
|
||||
int ret;
|
||||
u32 loop = RETRY_MAX;
|
||||
|
||||
do {
|
||||
ret = _imx7ulp_wdt_set_timeout(wdt, toval);
|
||||
val = readl(wdt->base + WDOG_TOVAL);
|
||||
} while (--loop > 0 && (val != toval || ret));
|
||||
|
||||
if (loop == 0)
|
||||
return -EBUSY;
|
||||
|
||||
wdog->timeout = timeout;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -173,29 +239,62 @@ static const struct watchdog_info imx7ulp_wdt_info = {
|
||||
WDIOF_MAGICCLOSE,
|
||||
};
|
||||
|
||||
static int imx7ulp_wdt_init(void __iomem *base, unsigned int timeout)
|
||||
static int _imx7ulp_wdt_init(struct imx7ulp_wdt_device *wdt, unsigned int timeout, unsigned int cs)
|
||||
{
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
local_irq_disable();
|
||||
/* unlock the wdog for reconfiguration */
|
||||
writel_relaxed(UNLOCK_SEQ0, base + WDOG_CNT);
|
||||
writel_relaxed(UNLOCK_SEQ1, base + WDOG_CNT);
|
||||
ret = imx7ulp_wdt_wait(base, WDOG_CS_ULK);
|
||||
|
||||
val = readl(wdt->base + WDOG_CS);
|
||||
if (val & WDOG_CS_CMD32EN) {
|
||||
writel(UNLOCK, wdt->base + WDOG_CNT);
|
||||
} else {
|
||||
mb();
|
||||
/* unlock the wdog for reconfiguration */
|
||||
writel_relaxed(UNLOCK_SEQ0, wdt->base + WDOG_CNT);
|
||||
writel_relaxed(UNLOCK_SEQ1, wdt->base + WDOG_CNT);
|
||||
mb();
|
||||
}
|
||||
|
||||
ret = imx7ulp_wdt_wait_ulk(wdt->base);
|
||||
if (ret)
|
||||
goto init_out;
|
||||
|
||||
/* set an initial timeout value in TOVAL */
|
||||
writel(timeout, base + WDOG_TOVAL);
|
||||
/* enable 32bit command sequence and reconfigure */
|
||||
val = WDOG_CS_CMD32EN | WDOG_CS_CLK | WDOG_CS_UPDATE |
|
||||
WDOG_CS_WAIT | WDOG_CS_STOP;
|
||||
writel(val, base + WDOG_CS);
|
||||
imx7ulp_wdt_wait(base, WDOG_CS_RCS);
|
||||
writel(timeout, wdt->base + WDOG_TOVAL);
|
||||
writel(cs, wdt->base + WDOG_CS);
|
||||
local_irq_enable();
|
||||
ret = imx7ulp_wdt_wait_rcs(wdt);
|
||||
|
||||
return ret;
|
||||
|
||||
init_out:
|
||||
local_irq_enable();
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int imx7ulp_wdt_init(struct imx7ulp_wdt_device *wdt, unsigned int timeout)
|
||||
{
|
||||
/* enable 32bit command sequence and reconfigure */
|
||||
u32 val = WDOG_CS_CMD32EN | WDOG_CS_CLK | WDOG_CS_UPDATE |
|
||||
WDOG_CS_WAIT | WDOG_CS_STOP;
|
||||
u32 cs, toval;
|
||||
int ret;
|
||||
u32 loop = RETRY_MAX;
|
||||
|
||||
if (wdt->hw->prescaler_enable)
|
||||
val |= WDOG_CS_PRES;
|
||||
|
||||
do {
|
||||
ret = _imx7ulp_wdt_init(wdt, timeout, val);
|
||||
toval = readl(wdt->base + WDOG_TOVAL);
|
||||
cs = readl(wdt->base + WDOG_CS);
|
||||
cs &= ~(WDOG_CS_FLG | WDOG_CS_ULK | WDOG_CS_RCS);
|
||||
} while (--loop > 0 && (cs != val || toval != timeout || ret));
|
||||
|
||||
if (loop == 0)
|
||||
return -EBUSY;
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -228,6 +327,15 @@ static int imx7ulp_wdt_probe(struct platform_device *pdev)
|
||||
return PTR_ERR(imx7ulp_wdt->clk);
|
||||
}
|
||||
|
||||
imx7ulp_wdt->post_rcs_wait = true;
|
||||
if (of_device_is_compatible(dev->of_node,
|
||||
"fsl,imx8ulp-wdt")) {
|
||||
dev_info(dev, "imx8ulp wdt probe\n");
|
||||
imx7ulp_wdt->post_rcs_wait = false;
|
||||
} else {
|
||||
dev_info(dev, "imx7ulp wdt probe\n");
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(imx7ulp_wdt->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
@ -248,14 +356,16 @@ static int imx7ulp_wdt_probe(struct platform_device *pdev)
|
||||
watchdog_stop_on_reboot(wdog);
|
||||
watchdog_stop_on_unregister(wdog);
|
||||
watchdog_set_drvdata(wdog, imx7ulp_wdt);
|
||||
ret = imx7ulp_wdt_init(imx7ulp_wdt->base, wdog->timeout * WDOG_CLOCK_RATE);
|
||||
|
||||
imx7ulp_wdt->hw = of_device_get_match_data(dev);
|
||||
ret = imx7ulp_wdt_init(imx7ulp_wdt, wdog->timeout * imx7ulp_wdt->hw->wdog_clock_rate);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return devm_watchdog_register_device(dev, wdog);
|
||||
}
|
||||
|
||||
static int __maybe_unused imx7ulp_wdt_suspend(struct device *dev)
|
||||
static int __maybe_unused imx7ulp_wdt_suspend_noirq(struct device *dev)
|
||||
{
|
||||
struct imx7ulp_wdt_device *imx7ulp_wdt = dev_get_drvdata(dev);
|
||||
|
||||
@ -267,30 +377,44 @@ static int __maybe_unused imx7ulp_wdt_suspend(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused imx7ulp_wdt_resume(struct device *dev)
|
||||
static int __maybe_unused imx7ulp_wdt_resume_noirq(struct device *dev)
|
||||
{
|
||||
struct imx7ulp_wdt_device *imx7ulp_wdt = dev_get_drvdata(dev);
|
||||
u32 timeout = imx7ulp_wdt->wdd.timeout * WDOG_CLOCK_RATE;
|
||||
u32 timeout = imx7ulp_wdt->wdd.timeout * imx7ulp_wdt->hw->wdog_clock_rate;
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(imx7ulp_wdt->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (imx7ulp_wdt_is_enabled(imx7ulp_wdt->base))
|
||||
imx7ulp_wdt_init(imx7ulp_wdt->base, timeout);
|
||||
|
||||
if (watchdog_active(&imx7ulp_wdt->wdd))
|
||||
if (watchdog_active(&imx7ulp_wdt->wdd)) {
|
||||
imx7ulp_wdt_init(imx7ulp_wdt, timeout);
|
||||
imx7ulp_wdt_start(&imx7ulp_wdt->wdd);
|
||||
imx7ulp_wdt_ping(&imx7ulp_wdt->wdd);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(imx7ulp_wdt_pm_ops, imx7ulp_wdt_suspend,
|
||||
imx7ulp_wdt_resume);
|
||||
static const struct dev_pm_ops imx7ulp_wdt_pm_ops = {
|
||||
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(imx7ulp_wdt_suspend_noirq,
|
||||
imx7ulp_wdt_resume_noirq)
|
||||
};
|
||||
|
||||
static const struct imx_wdt_hw_feature imx7ulp_wdt_hw = {
|
||||
.prescaler_enable = false,
|
||||
.wdog_clock_rate = 1000,
|
||||
};
|
||||
|
||||
static const struct imx_wdt_hw_feature imx93_wdt_hw = {
|
||||
.prescaler_enable = true,
|
||||
.wdog_clock_rate = 125,
|
||||
};
|
||||
|
||||
static const struct of_device_id imx7ulp_wdt_dt_ids[] = {
|
||||
{ .compatible = "fsl,imx7ulp-wdt", },
|
||||
{ .compatible = "fsl,imx8ulp-wdt", .data = &imx7ulp_wdt_hw, },
|
||||
{ .compatible = "fsl,imx7ulp-wdt", .data = &imx7ulp_wdt_hw, },
|
||||
{ .compatible = "fsl,imx93-wdt", .data = &imx93_wdt_hw, },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, imx7ulp_wdt_dt_ids);
|
||||
|
@ -156,6 +156,7 @@ 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);
|
||||
if (!data)
|
||||
@ -189,13 +190,26 @@ static int meson_gxbb_wdt_probe(struct platform_device *pdev)
|
||||
watchdog_set_nowayout(&data->wdt_dev, nowayout);
|
||||
watchdog_set_drvdata(&data->wdt_dev, data);
|
||||
|
||||
/* Setup with 1ms timebase */
|
||||
writel(((clk_get_rate(data->clk) / 1000) & GXBB_WDT_CTRL_DIV_MASK) |
|
||||
GXBB_WDT_CTRL_EE_RESET |
|
||||
GXBB_WDT_CTRL_CLK_EN |
|
||||
GXBB_WDT_CTRL_CLKDIV_EN,
|
||||
data->reg_base + GXBB_WDT_CTRL_REG);
|
||||
ctrl_reg = readl(data->reg_base + GXBB_WDT_CTRL_REG) &
|
||||
GXBB_WDT_CTRL_EN;
|
||||
|
||||
if (ctrl_reg) {
|
||||
/* Watchdog is running - keep it running but extend timeout
|
||||
* to the maximum while setting the timebase
|
||||
*/
|
||||
set_bit(WDOG_HW_RUNNING, &data->wdt_dev.status);
|
||||
meson_gxbb_wdt_set_timeout(&data->wdt_dev,
|
||||
GXBB_WDT_TCNT_SETUP_MASK / 1000);
|
||||
}
|
||||
|
||||
/* Setup with 1ms timebase */
|
||||
ctrl_reg |= ((clk_get_rate(data->clk) / 1000) &
|
||||
GXBB_WDT_CTRL_DIV_MASK) |
|
||||
GXBB_WDT_CTRL_EE_RESET |
|
||||
GXBB_WDT_CTRL_CLK_EN |
|
||||
GXBB_WDT_CTRL_CLKDIV_EN;
|
||||
|
||||
writel(ctrl_reg, data->reg_base + GXBB_WDT_CTRL_REG);
|
||||
meson_gxbb_wdt_set_timeout(&data->wdt_dev, data->wdt_dev.timeout);
|
||||
|
||||
return devm_watchdog_register_device(dev, &data->wdt_dev);
|
||||
|
@ -3,6 +3,7 @@
|
||||
// Copyright (c) 2018 IBM Corp.
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
@ -43,6 +44,7 @@
|
||||
struct npcm_wdt {
|
||||
struct watchdog_device wdd;
|
||||
void __iomem *reg;
|
||||
struct clk *clk;
|
||||
};
|
||||
|
||||
static inline struct npcm_wdt *to_npcm_wdt(struct watchdog_device *wdd)
|
||||
@ -66,6 +68,9 @@ static int npcm_wdt_start(struct watchdog_device *wdd)
|
||||
struct npcm_wdt *wdt = to_npcm_wdt(wdd);
|
||||
u32 val;
|
||||
|
||||
if (wdt->clk)
|
||||
clk_prepare_enable(wdt->clk);
|
||||
|
||||
if (wdd->timeout < 2)
|
||||
val = 0x800;
|
||||
else if (wdd->timeout < 3)
|
||||
@ -100,6 +105,9 @@ static int npcm_wdt_stop(struct watchdog_device *wdd)
|
||||
|
||||
writel(0, wdt->reg);
|
||||
|
||||
if (wdt->clk)
|
||||
clk_disable_unprepare(wdt->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -147,6 +155,10 @@ static int npcm_wdt_restart(struct watchdog_device *wdd,
|
||||
{
|
||||
struct npcm_wdt *wdt = to_npcm_wdt(wdd);
|
||||
|
||||
/* For reset, we start the WDT clock and leave it running. */
|
||||
if (wdt->clk)
|
||||
clk_prepare_enable(wdt->clk);
|
||||
|
||||
writel(NPCM_WTR | NPCM_WTRE | NPCM_WTE, wdt->reg);
|
||||
udelay(1000);
|
||||
|
||||
@ -191,6 +203,10 @@ static int npcm_wdt_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(wdt->reg))
|
||||
return PTR_ERR(wdt->reg);
|
||||
|
||||
wdt->clk = devm_clk_get_optional(&pdev->dev, NULL);
|
||||
if (IS_ERR(wdt->clk))
|
||||
return PTR_ERR(wdt->clk);
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
@ -225,9 +225,8 @@ static int rti_wdt_probe(struct platform_device *pdev)
|
||||
wdt->freq = wdt->freq * 9 / 10;
|
||||
|
||||
pm_runtime_enable(dev);
|
||||
ret = pm_runtime_get_sync(dev);
|
||||
ret = pm_runtime_resume_and_get(dev);
|
||||
if (ret < 0) {
|
||||
pm_runtime_put_noidle(dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
return dev_err_probe(dev, ret, "runtime pm failed\n");
|
||||
}
|
||||
|
@ -10,7 +10,7 @@
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/reset.h>
|
||||
@ -40,6 +40,11 @@ module_param(nowayout, bool, 0);
|
||||
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
|
||||
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
|
||||
|
||||
enum rz_wdt_type {
|
||||
WDT_RZG2L,
|
||||
WDT_RZV2M,
|
||||
};
|
||||
|
||||
struct rzg2l_wdt_priv {
|
||||
void __iomem *base;
|
||||
struct watchdog_device wdev;
|
||||
@ -48,6 +53,7 @@ struct rzg2l_wdt_priv {
|
||||
unsigned long delay;
|
||||
struct clk *pclk;
|
||||
struct clk *osc_clk;
|
||||
enum rz_wdt_type devtype;
|
||||
};
|
||||
|
||||
static void rzg2l_wdt_wait_delay(struct rzg2l_wdt_priv *priv)
|
||||
@ -142,11 +148,29 @@ static int rzg2l_wdt_restart(struct watchdog_device *wdev,
|
||||
clk_prepare_enable(priv->pclk);
|
||||
clk_prepare_enable(priv->osc_clk);
|
||||
|
||||
/* Generate Reset (WDTRSTB) Signal on parity error */
|
||||
rzg2l_wdt_write(priv, 0, PECR);
|
||||
if (priv->devtype == WDT_RZG2L) {
|
||||
/* Generate Reset (WDTRSTB) Signal on parity error */
|
||||
rzg2l_wdt_write(priv, 0, PECR);
|
||||
|
||||
/* Force parity error */
|
||||
rzg2l_wdt_write(priv, PEEN_FORCE, PEEN);
|
||||
/* Force parity error */
|
||||
rzg2l_wdt_write(priv, PEEN_FORCE, PEEN);
|
||||
} else {
|
||||
/* RZ/V2M doesn't have parity error registers */
|
||||
|
||||
wdev->timeout = 0;
|
||||
|
||||
/* Initialize time out */
|
||||
rzg2l_wdt_init_timeout(wdev);
|
||||
|
||||
/* Initialize watchdog counter register */
|
||||
rzg2l_wdt_write(priv, 0, WDTTIM);
|
||||
|
||||
/* Enable watchdog timer*/
|
||||
rzg2l_wdt_write(priv, WDTCNT_WDTEN, WDTCNT);
|
||||
|
||||
/* Wait 2 consecutive overflow cycles for reset */
|
||||
mdelay(DIV_ROUND_UP(2 * 0xFFFFF * 1000, priv->osc_clk_rate));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -227,6 +251,8 @@ static int rzg2l_wdt_probe(struct platform_device *pdev)
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "failed to deassert");
|
||||
|
||||
priv->devtype = (uintptr_t)of_device_get_match_data(dev);
|
||||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
||||
priv->wdev.info = &rzg2l_wdt_ident;
|
||||
@ -255,7 +281,8 @@ static int rzg2l_wdt_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
static const struct of_device_id rzg2l_wdt_ids[] = {
|
||||
{ .compatible = "renesas,rzg2l-wdt", },
|
||||
{ .compatible = "renesas,rzg2l-wdt", .data = (void *)WDT_RZG2L },
|
||||
{ .compatible = "renesas,rzv2m-wdt", .data = (void *)WDT_RZV2M },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rzg2l_wdt_ids);
|
||||
|
@ -60,9 +60,13 @@
|
||||
#define EXYNOS850_CLUSTER0_NONCPU_INT_EN 0x1244
|
||||
#define EXYNOS850_CLUSTER1_NONCPU_OUT 0x1620
|
||||
#define EXYNOS850_CLUSTER1_NONCPU_INT_EN 0x1644
|
||||
#define EXYNOSAUTOV9_CLUSTER1_NONCPU_OUT 0x1520
|
||||
#define EXYNOSAUTOV9_CLUSTER1_NONCPU_INT_EN 0x1544
|
||||
|
||||
#define EXYNOS850_CLUSTER0_WDTRESET_BIT 24
|
||||
#define EXYNOS850_CLUSTER1_WDTRESET_BIT 23
|
||||
#define EXYNOSAUTOV9_CLUSTER0_WDTRESET_BIT 25
|
||||
#define EXYNOSAUTOV9_CLUSTER1_WDTRESET_BIT 24
|
||||
|
||||
/**
|
||||
* DOC: Quirk flags for different Samsung watchdog IP-cores
|
||||
@ -236,6 +240,30 @@ static const struct s3c2410_wdt_variant drv_data_exynos850_cl1 = {
|
||||
QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_CNT_EN,
|
||||
};
|
||||
|
||||
static const struct s3c2410_wdt_variant drv_data_exynosautov9_cl0 = {
|
||||
.mask_reset_reg = EXYNOS850_CLUSTER0_NONCPU_INT_EN,
|
||||
.mask_bit = 2,
|
||||
.mask_reset_inv = true,
|
||||
.rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET,
|
||||
.rst_stat_bit = EXYNOSAUTOV9_CLUSTER0_WDTRESET_BIT,
|
||||
.cnt_en_reg = EXYNOS850_CLUSTER0_NONCPU_OUT,
|
||||
.cnt_en_bit = 7,
|
||||
.quirks = QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_MASK_RESET |
|
||||
QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_CNT_EN,
|
||||
};
|
||||
|
||||
static const struct s3c2410_wdt_variant drv_data_exynosautov9_cl1 = {
|
||||
.mask_reset_reg = EXYNOSAUTOV9_CLUSTER1_NONCPU_INT_EN,
|
||||
.mask_bit = 2,
|
||||
.mask_reset_inv = true,
|
||||
.rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET,
|
||||
.rst_stat_bit = EXYNOSAUTOV9_CLUSTER1_WDTRESET_BIT,
|
||||
.cnt_en_reg = EXYNOSAUTOV9_CLUSTER1_NONCPU_OUT,
|
||||
.cnt_en_bit = 7,
|
||||
.quirks = QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_MASK_RESET |
|
||||
QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_CNT_EN,
|
||||
};
|
||||
|
||||
static const struct of_device_id s3c2410_wdt_match[] = {
|
||||
{ .compatible = "samsung,s3c2410-wdt",
|
||||
.data = &drv_data_s3c2410 },
|
||||
@ -249,6 +277,8 @@ static const struct of_device_id s3c2410_wdt_match[] = {
|
||||
.data = &drv_data_exynos7 },
|
||||
{ .compatible = "samsung,exynos850-wdt",
|
||||
.data = &drv_data_exynos850_cl0 },
|
||||
{ .compatible = "samsung,exynosautov9-wdt",
|
||||
.data = &drv_data_exynosautov9_cl0 },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, s3c2410_wdt_match);
|
||||
@ -630,8 +660,9 @@ s3c2410_get_wdt_drv_data(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
/* Choose Exynos850 driver data w.r.t. cluster index */
|
||||
if (variant == &drv_data_exynos850_cl0) {
|
||||
/* Choose Exynos850/ExynosAutov9 driver data w.r.t. cluster index */
|
||||
if (variant == &drv_data_exynos850_cl0 ||
|
||||
variant == &drv_data_exynosautov9_cl0) {
|
||||
u32 index;
|
||||
int err;
|
||||
|
||||
@ -644,9 +675,11 @@ s3c2410_get_wdt_drv_data(struct platform_device *pdev)
|
||||
|
||||
switch (index) {
|
||||
case 0:
|
||||
return &drv_data_exynos850_cl0;
|
||||
return variant;
|
||||
case 1:
|
||||
return &drv_data_exynos850_cl1;
|
||||
return (variant == &drv_data_exynos850_cl0) ?
|
||||
&drv_data_exynos850_cl1 :
|
||||
&drv_data_exynosautov9_cl1;
|
||||
default:
|
||||
dev_err(dev, "wrong cluster index: %u\n", index);
|
||||
return NULL;
|
||||
|
@ -238,7 +238,7 @@ static int sa1100dog_remove(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct platform_driver sa1100dog_driver = {
|
||||
static struct platform_driver sa1100dog_driver = {
|
||||
.driver.name = "sa1100_wdt",
|
||||
.probe = sa1100dog_probe,
|
||||
.remove = sa1100dog_remove,
|
||||
|
@ -65,6 +65,12 @@ static struct pci_dev *sp5100_tco_pci;
|
||||
|
||||
/* module parameters */
|
||||
|
||||
#define WATCHDOG_ACTION 0
|
||||
static bool action = WATCHDOG_ACTION;
|
||||
module_param(action, bool, 0);
|
||||
MODULE_PARM_DESC(action, "Action taken when watchdog expires, 0 to reset, 1 to poweroff (default="
|
||||
__MODULE_STRING(WATCHDOG_ACTION) ")");
|
||||
|
||||
#define WATCHDOG_HEARTBEAT 60 /* 60 sec default heartbeat. */
|
||||
static int heartbeat = WATCHDOG_HEARTBEAT; /* in seconds */
|
||||
module_param(heartbeat, int, 0);
|
||||
@ -297,8 +303,11 @@ static int sp5100_tco_timer_init(struct sp5100_tco *tco)
|
||||
if (val & SP5100_WDT_FIRED)
|
||||
wdd->bootstatus = WDIOF_CARDRESET;
|
||||
|
||||
/* Set watchdog action to reset the system */
|
||||
val &= ~SP5100_WDT_ACTION_RESET;
|
||||
/* Set watchdog action */
|
||||
if (action)
|
||||
val |= SP5100_WDT_ACTION_RESET;
|
||||
else
|
||||
val &= ~SP5100_WDT_ACTION_RESET;
|
||||
writel(val, SP5100_WDT_CONTROL(tco->tcobase));
|
||||
|
||||
/* Set a reasonable heartbeat before we stop the timer */
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include <linux/types.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/watchdog.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mfd/twl.h>
|
||||
|
@ -113,6 +113,10 @@ MODULE_PARM_DESC(early_disable, "Disable watchdog at boot time (default=0)");
|
||||
#define W836X7HF_WDT_CSR 0xf7
|
||||
#define NCT6102D_WDT_CSR 0xf2
|
||||
|
||||
#define WDT_CSR_STATUS 0x10
|
||||
#define WDT_CSR_KBD 0x40
|
||||
#define WDT_CSR_MOUSE 0x80
|
||||
|
||||
static void superio_outb(int reg, int val)
|
||||
{
|
||||
outb(reg, WDT_EFER);
|
||||
@ -244,8 +248,12 @@ static int w83627hf_init(struct watchdog_device *wdog, enum chips chip)
|
||||
t = superio_inb(cr_wdt_control) & ~0x0C;
|
||||
superio_outb(cr_wdt_control, t);
|
||||
|
||||
/* reset trigger, disable keyboard & mouse turning off watchdog */
|
||||
t = superio_inb(cr_wdt_csr) & ~0xD0;
|
||||
t = superio_inb(cr_wdt_csr);
|
||||
if (t & WDT_CSR_STATUS)
|
||||
wdog->bootstatus |= WDIOF_CARDRESET;
|
||||
|
||||
/* reset status, disable keyboard & mouse turning off watchdog */
|
||||
t &= ~(WDT_CSR_STATUS | WDT_CSR_KBD | WDT_CSR_MOUSE);
|
||||
superio_outb(cr_wdt_csr, t);
|
||||
|
||||
superio_exit();
|
||||
|
@ -321,7 +321,7 @@ static int wdt_release(struct inode *inode, struct file *file)
|
||||
* @ppos: pointer to the position to write. No seeks allowed
|
||||
*
|
||||
* A write to a watchdog device is defined as a keepalive signal. Any
|
||||
* write of data will do, as we we don't define content meaning.
|
||||
* write of data will do, as we don't define content meaning.
|
||||
*/
|
||||
|
||||
static ssize_t wdt_write(struct file *file, const char __user *buf,
|
||||
|
@ -1015,7 +1015,11 @@ static int watchdog_cdev_register(struct watchdog_device *wdd)
|
||||
wd_data->dev.groups = wdd->groups;
|
||||
wd_data->dev.release = watchdog_core_data_release;
|
||||
dev_set_drvdata(&wd_data->dev, wdd);
|
||||
dev_set_name(&wd_data->dev, "watchdog%d", wdd->id);
|
||||
err = dev_set_name(&wd_data->dev, "watchdog%d", wdd->id);
|
||||
if (err) {
|
||||
put_device(&wd_data->dev);
|
||||
return err;
|
||||
}
|
||||
|
||||
kthread_init_work(&wd_data->work, watchdog_ping_work);
|
||||
hrtimer_init(&wd_data->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_HARD);
|
||||
|
@ -342,9 +342,8 @@ static int wdat_wdt_probe(struct platform_device *pdev)
|
||||
return -EINVAL;
|
||||
|
||||
wdat->period = tbl->timer_period;
|
||||
wdat->wdd.min_hw_heartbeat_ms = wdat->period * tbl->min_count;
|
||||
wdat->wdd.max_hw_heartbeat_ms = wdat->period * tbl->max_count;
|
||||
wdat->wdd.min_timeout = 1;
|
||||
wdat->wdd.min_timeout = DIV_ROUND_UP(wdat->period * tbl->min_count, 1000);
|
||||
wdat->wdd.max_timeout = wdat->period * tbl->max_count / 1000;
|
||||
wdat->stopped_in_sleep = tbl->flags & ACPI_WDAT_STOPPED;
|
||||
wdat->wdd.info = &wdat_wdt_info;
|
||||
wdat->wdd.ops = &wdat_wdt_ops;
|
||||
|
Loading…
Reference in New Issue
Block a user