mirror of
https://github.com/torvalds/linux.git
synced 2024-11-22 12:11:40 +00:00
linux-watchdog 5.17-rc1 tag
-----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.14 (GNU/Linux) iEYEABECAAYFAmHih/MACgkQ+iyteGJfRsqJBQCeN3KG3aVFcHwFM7EgNIRpqQCp swMAoK0LtVw4gTrQX8YxJ+Ml/ZVz3guA =NVYY -----END PGP SIGNATURE----- Merge tag 'linux-watchdog-5.17-rc1' of git://www.linux-watchdog.org/linux-watchdog Pull watchdog updates from Wim Van Sebroeck: - New device support: - Watchdog Timer driver for RZ/G2L - Realtek Otto watchdog timer - Apple SoC watchdog driver - Fintek F81966 - Remove BCM63XX_WDT after support for this SoC was added to BCM7038_WDT - Improvements of the BCM7038_WDT and s3c2410_wdt code - Several other fixes and improvements * tag 'linux-watchdog-5.17-rc1' of git://www.linux-watchdog.org/linux-watchdog: (38 commits) watchdog: msc313e: Check if the WDT was running at boot watchdog: Add Apple SoC watchdog driver dt-bindings: watchdog: Add SM6350 and SM8250 compatible watchdog: s3c2410: Fix getting the optional clock watchdog: s3c2410: Use platform_get_irq() to get the interrupt dt-bindings: watchdog: atmel: Add missing 'interrupts' property watchdog: mtk_wdt: use platform_get_irq_optional watchdog: Add Watchdog Timer driver for RZ/G2L dt-bindings: watchdog: renesas,wdt: Add support for RZ/G2L watchdog: da9063: Add hard dependency on I2C watchdog: Add Realtek Otto watchdog timer dt-bindings: watchdog: Realtek Otto WDT binding watchdog: s3c2410: Add Exynos850 support watchdog: da9063: use atomic safe i2c transfer in reset handler watchdog: davinci: Use div64_ul instead of do_div watchdog: Remove BCM63XX_WDT MIPS: BCM63XX: Provide platform data to watchdog device watchdog: bcm7038_wdt: Add platform device id for bcm63xx-wdt watchdog: Allow building BCM7038_WDT for BCM63XX watchdog: bcm7038_wdt: Support platform data configuration ...
This commit is contained in:
commit
2225acc322
@ -1,19 +0,0 @@
|
||||
BCM7038 Watchdog timer
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : should be "brcm,bcm7038-wdt"
|
||||
- reg : Specifies base physical address and size of the registers.
|
||||
|
||||
Optional properties:
|
||||
|
||||
- clocks: The clock running the watchdog. If no clock is found the
|
||||
driver will default to 27000000 Hz.
|
||||
|
||||
Example:
|
||||
|
||||
watchdog@f040a7e8 {
|
||||
compatible = "brcm,bcm7038-wdt";
|
||||
clocks = <&upg_fixed>;
|
||||
reg = <0xf040a7e8 0x16>;
|
||||
};
|
@ -0,0 +1,43 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/watchdog/brcm,bcm7038-wdt.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: BCM63xx and BCM7038 watchdog timer
|
||||
|
||||
allOf:
|
||||
- $ref: "watchdog.yaml#"
|
||||
|
||||
maintainers:
|
||||
- Florian Fainelli <f.fainelli@gmail.com>
|
||||
- Justin Chen <justinpopo6@gmail.com>
|
||||
- Rafał Miłecki <rafal@milecki.pl>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- brcm,bcm6345-wdt
|
||||
- brcm,bcm7038-wdt
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
description: >
|
||||
The clock running the watchdog. If no clock is found the driver will
|
||||
default to 27000000 Hz.
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
required:
|
||||
- reg
|
||||
|
||||
examples:
|
||||
- |
|
||||
watchdog@f040a7e8 {
|
||||
compatible = "brcm,bcm7038-wdt";
|
||||
reg = <0xf040a7e8 0x16>;
|
||||
clocks = <&upg_fixed>;
|
||||
};
|
@ -14,8 +14,11 @@ allOf:
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- fsl,imx7ulp-wdt
|
||||
oneOf:
|
||||
- const: fsl,imx7ulp-wdt
|
||||
- items:
|
||||
- const: fsl,imx8ulp-wdt
|
||||
- const: fsl,imx7ulp-wdt
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
@ -20,7 +20,9 @@ properties:
|
||||
- qcom,apss-wdt-sc7280
|
||||
- qcom,apss-wdt-sdm845
|
||||
- qcom,apss-wdt-sdx55
|
||||
- qcom,apss-wdt-sm6350
|
||||
- qcom,apss-wdt-sm8150
|
||||
- qcom,apss-wdt-sm8250
|
||||
- qcom,kpss-timer
|
||||
- qcom,kpss-wdt
|
||||
- qcom,kpss-wdt-apq8064
|
||||
|
@ -0,0 +1,91 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/watchdog/realtek,otto-wdt.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Realtek Otto watchdog timer
|
||||
|
||||
maintainers:
|
||||
- Sander Vanheule <sander@svanheule.net>
|
||||
|
||||
description: |
|
||||
The timer has two timeout phases. Both phases have a maximum duration of 32
|
||||
prescaled clock ticks, which is ca. 43s with a bus clock of 200MHz. The
|
||||
minimum duration of each phase is one tick. Each phase can trigger an
|
||||
interrupt, although the phase 2 interrupt will occur with the system reset.
|
||||
- Phase 1: During this phase, the WDT can be pinged to reset the timeout.
|
||||
- Phase 2: Starts after phase 1 has timed out, and only serves to give the
|
||||
system some time to clean up, or notify others that it's going to reset.
|
||||
During this phase, pinging the WDT has no effect, and a reset is
|
||||
unavoidable, unless the WDT is disabled.
|
||||
|
||||
allOf:
|
||||
- $ref: watchdog.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- realtek,rtl8380-wdt
|
||||
- realtek,rtl8390-wdt
|
||||
- realtek,rtl9300-wdt
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
items:
|
||||
- description: interrupt specifier for pretimeout
|
||||
- description: interrupt specifier for timeout
|
||||
|
||||
interrupt-names:
|
||||
items:
|
||||
- const: phase1
|
||||
- const: phase2
|
||||
|
||||
realtek,reset-mode:
|
||||
$ref: /schemas/types.yaml#/definitions/string
|
||||
description: |
|
||||
Specify how the system is reset after a timeout. Defaults to "cpu" if
|
||||
left unspecified.
|
||||
oneOf:
|
||||
- description: Reset the entire chip
|
||||
const: soc
|
||||
- description: |
|
||||
Reset the CPU and IPsec engine, but leave other peripherals untouched
|
||||
const: cpu
|
||||
- description: |
|
||||
Reset the execution pointer, but don't actually reset any hardware
|
||||
const: software
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- clocks
|
||||
- interrupts
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
dependencies:
|
||||
interrupts: [ interrupt-names ]
|
||||
|
||||
examples:
|
||||
- |
|
||||
watchdog: watchdog@3150 {
|
||||
compatible = "realtek,rtl8380-wdt";
|
||||
reg = <0x3150 0xc>;
|
||||
|
||||
realtek,reset-mode = "soc";
|
||||
|
||||
clocks = <&lxbus_clock>;
|
||||
timeout-sec = <20>;
|
||||
|
||||
interrupt-parent = <&rtlintc>;
|
||||
interrupt-names = "phase1", "phase2";
|
||||
interrupts = <19>, <18>;
|
||||
};
|
||||
|
||||
...
|
@ -10,9 +10,6 @@ maintainers:
|
||||
- Wolfram Sang <wsa+renesas@sang-engineering.com>
|
||||
- Geert Uytterhoeven <geert+renesas@glider.be>
|
||||
|
||||
allOf:
|
||||
- $ref: "watchdog.yaml#"
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
@ -22,6 +19,11 @@ properties:
|
||||
- renesas,r7s9210-wdt # RZ/A2
|
||||
- const: renesas,rza-wdt # RZ/A
|
||||
|
||||
- items:
|
||||
- enum:
|
||||
- renesas,r9a07g044-wdt # RZ/G2{L,LC}
|
||||
- const: renesas,rzg2l-wdt # RZ/G2L
|
||||
|
||||
- items:
|
||||
- enum:
|
||||
- renesas,r8a7742-wdt # RZ/G1H
|
||||
@ -56,11 +58,13 @@ properties:
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
interrupts: true
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
interrupt-names: true
|
||||
|
||||
clocks: true
|
||||
|
||||
clock-names: true
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
@ -75,17 +79,52 @@ required:
|
||||
- reg
|
||||
- clocks
|
||||
|
||||
if:
|
||||
not:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- renesas,rza-wdt
|
||||
then:
|
||||
required:
|
||||
- power-domains
|
||||
- resets
|
||||
allOf:
|
||||
- $ref: "watchdog.yaml#"
|
||||
|
||||
- if:
|
||||
not:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- renesas,rza-wdt
|
||||
then:
|
||||
required:
|
||||
- power-domains
|
||||
- resets
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- renesas,rzg2l-wdt
|
||||
then:
|
||||
properties:
|
||||
interrupts:
|
||||
maxItems: 2
|
||||
interrupt-names:
|
||||
items:
|
||||
- const: wdt
|
||||
- const: perrout
|
||||
clocks:
|
||||
items:
|
||||
- description: Register access clock
|
||||
- description: Main clock
|
||||
clock-names:
|
||||
items:
|
||||
- const: pclk
|
||||
- const: oscclk
|
||||
required:
|
||||
- clock-names
|
||||
- interrupt-names
|
||||
else:
|
||||
properties:
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
|
@ -22,25 +22,32 @@ properties:
|
||||
- samsung,exynos5250-wdt # for Exynos5250
|
||||
- samsung,exynos5420-wdt # for Exynos5420
|
||||
- samsung,exynos7-wdt # for Exynos7
|
||||
- samsung,exynos850-wdt # for Exynos850
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: watchdog
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
samsung,cluster-index:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description:
|
||||
Index of CPU cluster on which watchdog is running (in case of Exynos850)
|
||||
|
||||
samsung,syscon-phandle:
|
||||
$ref: /schemas/types.yaml#/definitions/phandle
|
||||
description:
|
||||
Phandle to the PMU system controller node (in case of Exynos5250
|
||||
and Exynos5420).
|
||||
Phandle to the PMU system controller node (in case of Exynos5250,
|
||||
Exynos5420, Exynos7 and Exynos850).
|
||||
|
||||
required:
|
||||
- compatible
|
||||
@ -58,9 +65,40 @@ allOf:
|
||||
enum:
|
||||
- samsung,exynos5250-wdt
|
||||
- samsung,exynos5420-wdt
|
||||
- samsung,exynos7-wdt
|
||||
- samsung,exynos850-wdt
|
||||
then:
|
||||
required:
|
||||
- samsung,syscon-phandle
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- samsung,exynos850-wdt
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
items:
|
||||
- description: Bus clock, used for register interface
|
||||
- description: Source clock (driving watchdog counter)
|
||||
clock-names:
|
||||
items:
|
||||
- const: watchdog
|
||||
- const: watchdog_src
|
||||
samsung,cluster-index:
|
||||
enum: [0, 1]
|
||||
required:
|
||||
- samsung,cluster-index
|
||||
else:
|
||||
properties:
|
||||
clocks:
|
||||
items:
|
||||
- description: Bus clock, which is also a source clock
|
||||
clock-names:
|
||||
items:
|
||||
- const: watchdog
|
||||
samsung,cluster-index: false
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
|
@ -16343,6 +16343,13 @@ S: Maintained
|
||||
F: include/sound/rt*.h
|
||||
F: sound/soc/codecs/rt*
|
||||
|
||||
REALTEK OTTO WATCHDOG
|
||||
M: Sander Vanheule <sander@svanheule.net>
|
||||
L: linux-watchdog@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/watchdog/realtek,otto-wdt.yaml
|
||||
F: drivers/watchdog/realtek_otto_wdt.c
|
||||
|
||||
REALTEK RTL83xx SMI DSA ROUTER CHIPS
|
||||
M: Linus Walleij <linus.walleij@linaro.org>
|
||||
S: Maintained
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/platform_data/bcm7038_wdt.h>
|
||||
#include <bcm63xx_cpu.h>
|
||||
|
||||
static struct resource wdt_resources[] = {
|
||||
@ -19,11 +20,18 @@ static struct resource wdt_resources[] = {
|
||||
},
|
||||
};
|
||||
|
||||
static struct bcm7038_wdt_platform_data bcm63xx_wdt_pdata = {
|
||||
.clk_name = "periph",
|
||||
};
|
||||
|
||||
static struct platform_device bcm63xx_wdt_device = {
|
||||
.name = "bcm63xx-wdt",
|
||||
.id = -1,
|
||||
.num_resources = ARRAY_SIZE(wdt_resources),
|
||||
.resource = wdt_resources,
|
||||
.dev = {
|
||||
.platform_data = &bcm63xx_wdt_pdata,
|
||||
},
|
||||
};
|
||||
|
||||
int __init bcm63xx_wdt_register(void)
|
||||
|
@ -207,6 +207,7 @@ config DA9055_WATCHDOG
|
||||
config DA9063_WATCHDOG
|
||||
tristate "Dialog DA9063 Watchdog"
|
||||
depends on MFD_DA9063 || COMPILE_TEST
|
||||
depends on I2C
|
||||
select WATCHDOG_CORE
|
||||
help
|
||||
Support for the watchdog in the DA9063 PMIC.
|
||||
@ -680,10 +681,10 @@ config MAX77620_WATCHDOG
|
||||
depends on MFD_MAX77620 || COMPILE_TEST
|
||||
select WATCHDOG_CORE
|
||||
help
|
||||
This is the driver for the Max77620 watchdog timer.
|
||||
Say 'Y' here to enable the watchdog timer support for
|
||||
MAX77620 chips. To compile this driver as a module,
|
||||
choose M here: the module will be called max77620_wdt.
|
||||
This is the driver for the Max77620 watchdog timer.
|
||||
Say 'Y' here to enable the watchdog timer support for
|
||||
MAX77620 chips. To compile this driver as a module,
|
||||
choose M here: the module will be called max77620_wdt.
|
||||
|
||||
config IMX2_WDT
|
||||
tristate "IMX2+ Watchdog"
|
||||
@ -822,6 +823,7 @@ config MESON_WATCHDOG
|
||||
config MEDIATEK_WATCHDOG
|
||||
tristate "Mediatek SoCs watchdog support"
|
||||
depends on ARCH_MEDIATEK || COMPILE_TEST
|
||||
default ARCH_MEDIATEK
|
||||
select WATCHDOG_CORE
|
||||
select RESET_CONTROLLER
|
||||
help
|
||||
@ -881,6 +883,14 @@ config RENESAS_RZAWDT
|
||||
This driver adds watchdog support for the integrated watchdogs in the
|
||||
Renesas RZ/A SoCs. These watchdogs can be used to reset a system.
|
||||
|
||||
config RENESAS_RZG2LWDT
|
||||
tristate "Renesas RZ/G2L WDT Watchdog"
|
||||
depends on ARCH_RENESAS || COMPILE_TEST
|
||||
select WATCHDOG_CORE
|
||||
help
|
||||
This driver adds watchdog support for the integrated watchdogs in the
|
||||
Renesas RZ/G2L SoCs. These watchdogs can be used to reset a system.
|
||||
|
||||
config ASPEED_WATCHDOG
|
||||
tristate "Aspeed BMC watchdog support"
|
||||
depends on ARCH_ASPEED || COMPILE_TEST
|
||||
@ -940,6 +950,19 @@ config RTD119X_WATCHDOG
|
||||
Say Y here to include support for the watchdog timer in
|
||||
Realtek RTD1295 SoCs.
|
||||
|
||||
config REALTEK_OTTO_WDT
|
||||
tristate "Realtek Otto MIPS watchdog support"
|
||||
depends on MACH_REALTEK_RTL || COMPILE_TEST
|
||||
depends on COMMON_CLK
|
||||
select WATCHDOG_CORE
|
||||
default MACH_REALTEK_RTL
|
||||
help
|
||||
Say Y here to include support for the watchdog timer on Realtek
|
||||
RTL838x, RTL839x, RTL930x SoCs. This watchdog has pretimeout
|
||||
notifications and system reset on timeout.
|
||||
|
||||
When built as a module this will be called realtek_otto_wdt.
|
||||
|
||||
config SPRD_WATCHDOG
|
||||
tristate "Spreadtrum watchdog support"
|
||||
depends on ARCH_SPRD || COMPILE_TEST
|
||||
@ -976,6 +999,18 @@ config MSC313E_WATCHDOG
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called msc313e_wdt.
|
||||
|
||||
config APPLE_WATCHDOG
|
||||
tristate "Apple SoC watchdog"
|
||||
depends on ARCH_APPLE || COMPILE_TEST
|
||||
select WATCHDOG_CORE
|
||||
help
|
||||
Say Y here to include support for the Watchdog found in Apple
|
||||
SoCs such as the M1. Next to the common watchdog features this
|
||||
driver is also required in order to reboot these SoCs.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called apple_wdt.
|
||||
|
||||
# X86 (i386 + ia64 + x86_64) Architecture
|
||||
|
||||
config ACQUIRE_WDT
|
||||
@ -1440,26 +1475,26 @@ config TQMX86_WDT
|
||||
depends on X86
|
||||
select WATCHDOG_CORE
|
||||
help
|
||||
This is the driver for the hardware watchdog timer in the TQMX86 IO
|
||||
controller found on some of their ComExpress Modules.
|
||||
This is the driver for the hardware watchdog timer in the TQMX86 IO
|
||||
controller found on some of their ComExpress Modules.
|
||||
|
||||
To compile this driver as a module, choose M here; the module
|
||||
will be called tqmx86_wdt.
|
||||
To compile this driver as a module, choose M here; the module
|
||||
will be called tqmx86_wdt.
|
||||
|
||||
Most people will say N.
|
||||
Most people will say N.
|
||||
|
||||
config VIA_WDT
|
||||
tristate "VIA Watchdog Timer"
|
||||
depends on X86 && PCI
|
||||
select WATCHDOG_CORE
|
||||
help
|
||||
This is the driver for the hardware watchdog timer on VIA
|
||||
southbridge chipset CX700, VX800/VX820 or VX855/VX875.
|
||||
This is the driver for the hardware watchdog timer on VIA
|
||||
southbridge chipset CX700, VX800/VX820 or VX855/VX875.
|
||||
|
||||
To compile this driver as a module, choose M here; the module
|
||||
will be called via_wdt.
|
||||
To compile this driver as a module, choose M here; the module
|
||||
will be called via_wdt.
|
||||
|
||||
Most people will say N.
|
||||
Most people will say N.
|
||||
|
||||
config W83627HF_WDT
|
||||
tristate "Watchdog timer for W83627HF/W83627DHG and compatibles"
|
||||
@ -1707,16 +1742,6 @@ config OCTEON_WDT
|
||||
from the first interrupt, it is then only poked when the
|
||||
device is written.
|
||||
|
||||
config BCM63XX_WDT
|
||||
tristate "Broadcom BCM63xx hardware watchdog"
|
||||
depends on BCM63XX
|
||||
help
|
||||
Watchdog driver for the built in watchdog hardware in Broadcom
|
||||
BCM63xx SoC.
|
||||
|
||||
To compile this driver as a loadable module, choose M here.
|
||||
The module will be called bcm63xx_wdt.
|
||||
|
||||
config BCM2835_WDT
|
||||
tristate "Broadcom BCM2835 hardware watchdog"
|
||||
depends on ARCH_BCM2835 || (OF && COMPILE_TEST)
|
||||
@ -1751,15 +1776,16 @@ config BCM_KONA_WDT_DEBUG
|
||||
If in doubt, say 'N'.
|
||||
|
||||
config BCM7038_WDT
|
||||
tristate "BCM7038 Watchdog"
|
||||
tristate "BCM63xx/BCM7038 Watchdog"
|
||||
select WATCHDOG_CORE
|
||||
depends on HAS_IOMEM
|
||||
depends on ARCH_BRCMSTB || BMIPS_GENERIC || COMPILE_TEST
|
||||
depends on ARCH_BRCMSTB || BMIPS_GENERIC || BCM63XX || COMPILE_TEST
|
||||
help
|
||||
Watchdog driver for the built-in hardware in Broadcom 7038 and
|
||||
later SoCs used in set-top boxes. BCM7038 was made public
|
||||
during the 2004 CES, and since then, many Broadcom chips use this
|
||||
watchdog block, including some cable modem chips.
|
||||
Watchdog driver for the built-in hardware in Broadcom 7038 and
|
||||
later SoCs used in set-top boxes. BCM7038 was made public
|
||||
during the 2004 CES, and since then, many Broadcom chips use this
|
||||
watchdog block, including some cable modem chips and DSL (63xx)
|
||||
chips.
|
||||
|
||||
config IMGPDC_WDT
|
||||
tristate "Imagination Technologies PDC Watchdog Timer"
|
||||
@ -2120,12 +2146,12 @@ config KEEMBAY_WATCHDOG
|
||||
depends on ARCH_KEEMBAY || (ARM64 && COMPILE_TEST)
|
||||
select WATCHDOG_CORE
|
||||
help
|
||||
This option enable support for an In-secure watchdog timer driver for
|
||||
Intel Keem Bay SoC. This WDT has a 32 bit timer and decrements in every
|
||||
count unit. An interrupt will be triggered, when the count crosses
|
||||
the threshold configured in the register.
|
||||
This option enable support for an In-secure watchdog timer driver for
|
||||
Intel Keem Bay SoC. This WDT has a 32 bit timer and decrements in every
|
||||
count unit. An interrupt will be triggered, when the count crosses
|
||||
the threshold configured in the register.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called keembay_wdt.
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called keembay_wdt.
|
||||
|
||||
endif # WATCHDOG
|
||||
|
@ -84,6 +84,7 @@ obj-$(CONFIG_LPC18XX_WATCHDOG) += lpc18xx_wdt.o
|
||||
obj-$(CONFIG_BCM7038_WDT) += bcm7038_wdt.o
|
||||
obj-$(CONFIG_RENESAS_WDT) += renesas_wdt.o
|
||||
obj-$(CONFIG_RENESAS_RZAWDT) += rza_wdt.o
|
||||
obj-$(CONFIG_RENESAS_RZG2LWDT) += rzg2l_wdt.o
|
||||
obj-$(CONFIG_ASPEED_WATCHDOG) += aspeed_wdt.o
|
||||
obj-$(CONFIG_STM32_WATCHDOG) += stm32_iwdg.o
|
||||
obj-$(CONFIG_UNIPHIER_WATCHDOG) += uniphier_wdt.o
|
||||
@ -93,6 +94,7 @@ obj-$(CONFIG_PM8916_WATCHDOG) += pm8916_wdt.o
|
||||
obj-$(CONFIG_ARM_SMC_WATCHDOG) += arm_smc_wdt.o
|
||||
obj-$(CONFIG_VISCONTI_WATCHDOG) += visconti_wdt.o
|
||||
obj-$(CONFIG_MSC313E_WATCHDOG) += msc313e_wdt.o
|
||||
obj-$(CONFIG_APPLE_WATCHDOG) += apple_wdt.o
|
||||
|
||||
# X86 (i386 + ia64 + x86_64) Architecture
|
||||
obj-$(CONFIG_ACQUIRE_WDT) += acquirewdt.o
|
||||
@ -154,7 +156,6 @@ obj-$(CONFIG_XILINX_WATCHDOG) += of_xilinx_wdt.o
|
||||
# MIPS Architecture
|
||||
obj-$(CONFIG_ATH79_WDT) += ath79_wdt.o
|
||||
obj-$(CONFIG_BCM47XX_WDT) += bcm47xx_wdt.o
|
||||
obj-$(CONFIG_BCM63XX_WDT) += bcm63xx_wdt.o
|
||||
obj-$(CONFIG_RC32434_WDT) += rc32434_wdt.o
|
||||
obj-$(CONFIG_INDYDOG) += indydog.o
|
||||
obj-$(CONFIG_JZ4740_WDT) += jz4740_wdt.o
|
||||
@ -171,6 +172,7 @@ obj-$(CONFIG_IMGPDC_WDT) += imgpdc_wdt.o
|
||||
obj-$(CONFIG_MT7621_WDT) += mt7621_wdt.o
|
||||
obj-$(CONFIG_PIC32_WDT) += pic32-wdt.o
|
||||
obj-$(CONFIG_PIC32_DMT) += pic32-dmt.o
|
||||
obj-$(CONFIG_REALTEK_OTTO_WDT) += realtek_otto_wdt.o
|
||||
|
||||
# PARISC Architecture
|
||||
|
||||
|
226
drivers/watchdog/apple_wdt.c
Normal file
226
drivers/watchdog/apple_wdt.c
Normal file
@ -0,0 +1,226 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR MIT
|
||||
/*
|
||||
* Apple SoC Watchdog driver
|
||||
*
|
||||
* Copyright (C) The Asahi Linux Contributors
|
||||
*/
|
||||
|
||||
#include <linux/bits.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/limits.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/watchdog.h>
|
||||
|
||||
/*
|
||||
* Apple Watchdog MMIO registers
|
||||
*
|
||||
* This HW block has three separate watchdogs. WD0 resets the machine
|
||||
* to recovery mode and is not very useful for us. WD1 and WD2 trigger a normal
|
||||
* machine reset. WD0 additionally supports a configurable interrupt.
|
||||
* This information can be used to implement pretimeout support at a later time.
|
||||
*
|
||||
* APPLE_WDT_WDx_CUR_TIME is a simple counter incremented for each tick of the
|
||||
* reference clock. It can also be overwritten to any value.
|
||||
* Whenever APPLE_WDT_CTRL_RESET_EN is set in APPLE_WDT_WDx_CTRL and
|
||||
* APPLE_WDT_WDx_CUR_TIME >= APPLE_WDT_WDx_BITE_TIME the entire machine is
|
||||
* reset.
|
||||
* Whenever APPLE_WDT_CTRL_IRQ_EN is set and APPLE_WDTx_WD1_CUR_TIME >=
|
||||
* APPLE_WDTx_WD1_BARK_TIME an interrupt is triggered and
|
||||
* APPLE_WDT_CTRL_IRQ_STATUS is set. The interrupt can be cleared by writing
|
||||
* 1 to APPLE_WDT_CTRL_IRQ_STATUS.
|
||||
*/
|
||||
#define APPLE_WDT_WD0_CUR_TIME 0x00
|
||||
#define APPLE_WDT_WD0_BITE_TIME 0x04
|
||||
#define APPLE_WDT_WD0_BARK_TIME 0x08
|
||||
#define APPLE_WDT_WD0_CTRL 0x0c
|
||||
|
||||
#define APPLE_WDT_WD1_CUR_TIME 0x10
|
||||
#define APPLE_WDT_WD1_BITE_TIME 0x14
|
||||
#define APPLE_WDT_WD1_CTRL 0x1c
|
||||
|
||||
#define APPLE_WDT_WD2_CUR_TIME 0x20
|
||||
#define APPLE_WDT_WD2_BITE_TIME 0x24
|
||||
#define APPLE_WDT_WD2_CTRL 0x2c
|
||||
|
||||
#define APPLE_WDT_CTRL_IRQ_EN BIT(0)
|
||||
#define APPLE_WDT_CTRL_IRQ_STATUS BIT(1)
|
||||
#define APPLE_WDT_CTRL_RESET_EN BIT(2)
|
||||
|
||||
#define APPLE_WDT_TIMEOUT_DEFAULT 30
|
||||
|
||||
struct apple_wdt {
|
||||
struct watchdog_device wdd;
|
||||
void __iomem *regs;
|
||||
unsigned long clk_rate;
|
||||
};
|
||||
|
||||
static struct apple_wdt *to_apple_wdt(struct watchdog_device *wdd)
|
||||
{
|
||||
return container_of(wdd, struct apple_wdt, wdd);
|
||||
}
|
||||
|
||||
static int apple_wdt_start(struct watchdog_device *wdd)
|
||||
{
|
||||
struct apple_wdt *wdt = to_apple_wdt(wdd);
|
||||
|
||||
writel_relaxed(0, wdt->regs + APPLE_WDT_WD1_CUR_TIME);
|
||||
writel_relaxed(APPLE_WDT_CTRL_RESET_EN, wdt->regs + APPLE_WDT_WD1_CTRL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int apple_wdt_stop(struct watchdog_device *wdd)
|
||||
{
|
||||
struct apple_wdt *wdt = to_apple_wdt(wdd);
|
||||
|
||||
writel_relaxed(0, wdt->regs + APPLE_WDT_WD1_CTRL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int apple_wdt_ping(struct watchdog_device *wdd)
|
||||
{
|
||||
struct apple_wdt *wdt = to_apple_wdt(wdd);
|
||||
|
||||
writel_relaxed(0, wdt->regs + APPLE_WDT_WD1_CUR_TIME);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int apple_wdt_set_timeout(struct watchdog_device *wdd, unsigned int s)
|
||||
{
|
||||
struct apple_wdt *wdt = to_apple_wdt(wdd);
|
||||
|
||||
writel_relaxed(0, wdt->regs + APPLE_WDT_WD1_CUR_TIME);
|
||||
writel_relaxed(wdt->clk_rate * s, wdt->regs + APPLE_WDT_WD1_BITE_TIME);
|
||||
|
||||
wdd->timeout = s;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int apple_wdt_get_timeleft(struct watchdog_device *wdd)
|
||||
{
|
||||
struct apple_wdt *wdt = to_apple_wdt(wdd);
|
||||
u32 cur_time, reset_time;
|
||||
|
||||
cur_time = readl_relaxed(wdt->regs + APPLE_WDT_WD1_CUR_TIME);
|
||||
reset_time = readl_relaxed(wdt->regs + APPLE_WDT_WD1_BITE_TIME);
|
||||
|
||||
return (reset_time - cur_time) / wdt->clk_rate;
|
||||
}
|
||||
|
||||
static int apple_wdt_restart(struct watchdog_device *wdd, unsigned long mode,
|
||||
void *cmd)
|
||||
{
|
||||
struct apple_wdt *wdt = to_apple_wdt(wdd);
|
||||
|
||||
writel_relaxed(APPLE_WDT_CTRL_RESET_EN, wdt->regs + APPLE_WDT_WD1_CTRL);
|
||||
writel_relaxed(0, wdt->regs + APPLE_WDT_WD1_BITE_TIME);
|
||||
writel_relaxed(0, wdt->regs + APPLE_WDT_WD1_CUR_TIME);
|
||||
|
||||
/*
|
||||
* Flush writes and then wait for the SoC to reset. Even though the
|
||||
* reset is queued almost immediately experiments have shown that it
|
||||
* can take up to ~20-25ms until the SoC is actually reset. Just wait
|
||||
* 50ms here to be safe.
|
||||
*/
|
||||
(void)readl_relaxed(wdt->regs + APPLE_WDT_WD1_CUR_TIME);
|
||||
mdelay(50);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void apple_wdt_clk_disable_unprepare(void *data)
|
||||
{
|
||||
clk_disable_unprepare(data);
|
||||
}
|
||||
|
||||
static struct watchdog_ops apple_wdt_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.start = apple_wdt_start,
|
||||
.stop = apple_wdt_stop,
|
||||
.ping = apple_wdt_ping,
|
||||
.set_timeout = apple_wdt_set_timeout,
|
||||
.get_timeleft = apple_wdt_get_timeleft,
|
||||
.restart = apple_wdt_restart,
|
||||
};
|
||||
|
||||
static struct watchdog_info apple_wdt_info = {
|
||||
.identity = "Apple SoC Watchdog",
|
||||
.options = WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT,
|
||||
};
|
||||
|
||||
static int apple_wdt_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct apple_wdt *wdt;
|
||||
struct clk *clk;
|
||||
u32 wdt_ctrl;
|
||||
int ret;
|
||||
|
||||
wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
|
||||
if (!wdt)
|
||||
return -ENOMEM;
|
||||
|
||||
wdt->regs = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(wdt->regs))
|
||||
return PTR_ERR(wdt->regs);
|
||||
|
||||
clk = devm_clk_get(dev, NULL);
|
||||
if (IS_ERR(clk))
|
||||
return PTR_ERR(clk);
|
||||
|
||||
ret = clk_prepare_enable(clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = devm_add_action_or_reset(dev, apple_wdt_clk_disable_unprepare,
|
||||
clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
wdt->clk_rate = clk_get_rate(clk);
|
||||
if (!wdt->clk_rate)
|
||||
return -EINVAL;
|
||||
|
||||
wdt->wdd.ops = &apple_wdt_ops;
|
||||
wdt->wdd.info = &apple_wdt_info;
|
||||
wdt->wdd.max_timeout = U32_MAX / wdt->clk_rate;
|
||||
wdt->wdd.timeout = APPLE_WDT_TIMEOUT_DEFAULT;
|
||||
|
||||
wdt_ctrl = readl_relaxed(wdt->regs + APPLE_WDT_WD1_CTRL);
|
||||
if (wdt_ctrl & APPLE_WDT_CTRL_RESET_EN)
|
||||
set_bit(WDOG_HW_RUNNING, &wdt->wdd.status);
|
||||
|
||||
watchdog_init_timeout(&wdt->wdd, 0, dev);
|
||||
apple_wdt_set_timeout(&wdt->wdd, wdt->wdd.timeout);
|
||||
watchdog_stop_on_unregister(&wdt->wdd);
|
||||
watchdog_set_restart_priority(&wdt->wdd, 128);
|
||||
|
||||
return devm_watchdog_register_device(dev, &wdt->wdd);
|
||||
}
|
||||
|
||||
static const struct of_device_id apple_wdt_of_match[] = {
|
||||
{ .compatible = "apple,wdt" },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, apple_wdt_of_match);
|
||||
|
||||
static struct platform_driver apple_wdt_driver = {
|
||||
.driver = {
|
||||
.name = "apple-watchdog",
|
||||
.of_match_table = apple_wdt_of_match,
|
||||
},
|
||||
.probe = apple_wdt_probe,
|
||||
};
|
||||
module_platform_driver(apple_wdt_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Apple SoC watchdog driver");
|
||||
MODULE_AUTHOR("Sven Peter <sven@svenpeter.dev>");
|
||||
MODULE_LICENSE("Dual MIT/GPL");
|
@ -1,317 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Broadcom BCM63xx SoC watchdog driver
|
||||
*
|
||||
* Copyright (C) 2007, Miguel Gaio <miguel.gaio@efixo.com>
|
||||
* Copyright (C) 2008, Florian Fainelli <florian@openwrt.org>
|
||||
*
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/watchdog.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/resource.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include <bcm63xx_cpu.h>
|
||||
#include <bcm63xx_io.h>
|
||||
#include <bcm63xx_regs.h>
|
||||
#include <bcm63xx_timer.h>
|
||||
|
||||
#define PFX KBUILD_MODNAME
|
||||
|
||||
#define WDT_HZ 50000000 /* Fclk */
|
||||
#define WDT_DEFAULT_TIME 30 /* seconds */
|
||||
#define WDT_MAX_TIME 256 /* seconds */
|
||||
|
||||
static struct {
|
||||
void __iomem *regs;
|
||||
struct timer_list timer;
|
||||
unsigned long inuse;
|
||||
atomic_t ticks;
|
||||
} bcm63xx_wdt_device;
|
||||
|
||||
static int expect_close;
|
||||
|
||||
static int wdt_time = WDT_DEFAULT_TIME;
|
||||
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) ")");
|
||||
|
||||
/* HW functions */
|
||||
static void bcm63xx_wdt_hw_start(void)
|
||||
{
|
||||
bcm_writel(0xfffffffe, bcm63xx_wdt_device.regs + WDT_DEFVAL_REG);
|
||||
bcm_writel(WDT_START_1, bcm63xx_wdt_device.regs + WDT_CTL_REG);
|
||||
bcm_writel(WDT_START_2, bcm63xx_wdt_device.regs + WDT_CTL_REG);
|
||||
}
|
||||
|
||||
static void bcm63xx_wdt_hw_stop(void)
|
||||
{
|
||||
bcm_writel(WDT_STOP_1, bcm63xx_wdt_device.regs + WDT_CTL_REG);
|
||||
bcm_writel(WDT_STOP_2, bcm63xx_wdt_device.regs + WDT_CTL_REG);
|
||||
}
|
||||
|
||||
static void bcm63xx_wdt_isr(void *data)
|
||||
{
|
||||
struct pt_regs *regs = get_irq_regs();
|
||||
|
||||
die(PFX " fire", regs);
|
||||
}
|
||||
|
||||
static void bcm63xx_timer_tick(struct timer_list *unused)
|
||||
{
|
||||
if (!atomic_dec_and_test(&bcm63xx_wdt_device.ticks)) {
|
||||
bcm63xx_wdt_hw_start();
|
||||
mod_timer(&bcm63xx_wdt_device.timer, jiffies + HZ);
|
||||
} else
|
||||
pr_crit("watchdog will restart system\n");
|
||||
}
|
||||
|
||||
static void bcm63xx_wdt_pet(void)
|
||||
{
|
||||
atomic_set(&bcm63xx_wdt_device.ticks, wdt_time);
|
||||
}
|
||||
|
||||
static void bcm63xx_wdt_start(void)
|
||||
{
|
||||
bcm63xx_wdt_pet();
|
||||
bcm63xx_timer_tick(0);
|
||||
}
|
||||
|
||||
static void bcm63xx_wdt_pause(void)
|
||||
{
|
||||
del_timer_sync(&bcm63xx_wdt_device.timer);
|
||||
bcm63xx_wdt_hw_stop();
|
||||
}
|
||||
|
||||
static int bcm63xx_wdt_settimeout(int new_time)
|
||||
{
|
||||
if ((new_time <= 0) || (new_time > WDT_MAX_TIME))
|
||||
return -EINVAL;
|
||||
|
||||
wdt_time = new_time;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bcm63xx_wdt_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
if (test_and_set_bit(0, &bcm63xx_wdt_device.inuse))
|
||||
return -EBUSY;
|
||||
|
||||
bcm63xx_wdt_start();
|
||||
return stream_open(inode, file);
|
||||
}
|
||||
|
||||
static int bcm63xx_wdt_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
if (expect_close == 42)
|
||||
bcm63xx_wdt_pause();
|
||||
else {
|
||||
pr_crit("Unexpected close, not stopping watchdog!\n");
|
||||
bcm63xx_wdt_start();
|
||||
}
|
||||
clear_bit(0, &bcm63xx_wdt_device.inuse);
|
||||
expect_close = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t bcm63xx_wdt_write(struct file *file, const char *data,
|
||||
size_t len, loff_t *ppos)
|
||||
{
|
||||
if (len) {
|
||||
if (!nowayout) {
|
||||
size_t i;
|
||||
|
||||
/* In case it was set long ago */
|
||||
expect_close = 0;
|
||||
|
||||
for (i = 0; i != len; i++) {
|
||||
char c;
|
||||
if (get_user(c, data + i))
|
||||
return -EFAULT;
|
||||
if (c == 'V')
|
||||
expect_close = 42;
|
||||
}
|
||||
}
|
||||
bcm63xx_wdt_pet();
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
static struct watchdog_info bcm63xx_wdt_info = {
|
||||
.identity = PFX,
|
||||
.options = WDIOF_SETTIMEOUT |
|
||||
WDIOF_KEEPALIVEPING |
|
||||
WDIOF_MAGICCLOSE,
|
||||
};
|
||||
|
||||
|
||||
static long bcm63xx_wdt_ioctl(struct file *file, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
void __user *argp = (void __user *)arg;
|
||||
int __user *p = argp;
|
||||
int new_value, retval = -EINVAL;
|
||||
|
||||
switch (cmd) {
|
||||
case WDIOC_GETSUPPORT:
|
||||
return copy_to_user(argp, &bcm63xx_wdt_info,
|
||||
sizeof(bcm63xx_wdt_info)) ? -EFAULT : 0;
|
||||
|
||||
case WDIOC_GETSTATUS:
|
||||
case WDIOC_GETBOOTSTATUS:
|
||||
return put_user(0, p);
|
||||
|
||||
case WDIOC_SETOPTIONS:
|
||||
if (get_user(new_value, p))
|
||||
return -EFAULT;
|
||||
|
||||
if (new_value & WDIOS_DISABLECARD) {
|
||||
bcm63xx_wdt_pause();
|
||||
retval = 0;
|
||||
}
|
||||
if (new_value & WDIOS_ENABLECARD) {
|
||||
bcm63xx_wdt_start();
|
||||
retval = 0;
|
||||
}
|
||||
|
||||
return retval;
|
||||
|
||||
case WDIOC_KEEPALIVE:
|
||||
bcm63xx_wdt_pet();
|
||||
return 0;
|
||||
|
||||
case WDIOC_SETTIMEOUT:
|
||||
if (get_user(new_value, p))
|
||||
return -EFAULT;
|
||||
|
||||
if (bcm63xx_wdt_settimeout(new_value))
|
||||
return -EINVAL;
|
||||
|
||||
bcm63xx_wdt_pet();
|
||||
|
||||
fallthrough;
|
||||
|
||||
case WDIOC_GETTIMEOUT:
|
||||
return put_user(wdt_time, p);
|
||||
|
||||
default:
|
||||
return -ENOTTY;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static const struct file_operations bcm63xx_wdt_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.llseek = no_llseek,
|
||||
.write = bcm63xx_wdt_write,
|
||||
.unlocked_ioctl = bcm63xx_wdt_ioctl,
|
||||
.compat_ioctl = compat_ptr_ioctl,
|
||||
.open = bcm63xx_wdt_open,
|
||||
.release = bcm63xx_wdt_release,
|
||||
};
|
||||
|
||||
static struct miscdevice bcm63xx_wdt_miscdev = {
|
||||
.minor = WATCHDOG_MINOR,
|
||||
.name = "watchdog",
|
||||
.fops = &bcm63xx_wdt_fops,
|
||||
};
|
||||
|
||||
|
||||
static int bcm63xx_wdt_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
struct resource *r;
|
||||
|
||||
timer_setup(&bcm63xx_wdt_device.timer, bcm63xx_timer_tick, 0);
|
||||
|
||||
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!r) {
|
||||
dev_err(&pdev->dev, "failed to get resources\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
bcm63xx_wdt_device.regs = devm_ioremap(&pdev->dev, r->start,
|
||||
resource_size(r));
|
||||
if (!bcm63xx_wdt_device.regs) {
|
||||
dev_err(&pdev->dev, "failed to remap I/O resources\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
ret = bcm63xx_timer_register(TIMER_WDT_ID, bcm63xx_wdt_isr, NULL);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed to register wdt timer isr\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (bcm63xx_wdt_settimeout(wdt_time)) {
|
||||
bcm63xx_wdt_settimeout(WDT_DEFAULT_TIME);
|
||||
dev_info(&pdev->dev,
|
||||
": wdt_time value must be 1 <= wdt_time <= 256, using %d\n",
|
||||
wdt_time);
|
||||
}
|
||||
|
||||
ret = misc_register(&bcm63xx_wdt_miscdev);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed to register watchdog device\n");
|
||||
goto unregister_timer;
|
||||
}
|
||||
|
||||
dev_info(&pdev->dev, " started, timer margin: %d sec\n",
|
||||
WDT_DEFAULT_TIME);
|
||||
|
||||
return 0;
|
||||
|
||||
unregister_timer:
|
||||
bcm63xx_timer_unregister(TIMER_WDT_ID);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bcm63xx_wdt_remove(struct platform_device *pdev)
|
||||
{
|
||||
if (!nowayout)
|
||||
bcm63xx_wdt_pause();
|
||||
|
||||
misc_deregister(&bcm63xx_wdt_miscdev);
|
||||
bcm63xx_timer_unregister(TIMER_WDT_ID);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void bcm63xx_wdt_shutdown(struct platform_device *pdev)
|
||||
{
|
||||
bcm63xx_wdt_pause();
|
||||
}
|
||||
|
||||
static struct platform_driver bcm63xx_wdt_driver = {
|
||||
.probe = bcm63xx_wdt_probe,
|
||||
.remove = bcm63xx_wdt_remove,
|
||||
.shutdown = bcm63xx_wdt_shutdown,
|
||||
.driver = {
|
||||
.name = "bcm63xx-wdt",
|
||||
}
|
||||
};
|
||||
|
||||
module_platform_driver(bcm63xx_wdt_driver);
|
||||
|
||||
MODULE_AUTHOR("Miguel Gaio <miguel.gaio@efixo.com>");
|
||||
MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>");
|
||||
MODULE_DESCRIPTION("Driver for the Broadcom BCM63xx SoC watchdog");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:bcm63xx-wdt");
|
@ -10,6 +10,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/platform_data/bcm7038_wdt.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/watchdog.h>
|
||||
|
||||
@ -133,8 +134,10 @@ static void bcm7038_clk_disable_unprepare(void *data)
|
||||
|
||||
static int bcm7038_wdt_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct bcm7038_wdt_platform_data *pdata = pdev->dev.platform_data;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct bcm7038_watchdog *wdt;
|
||||
const char *clk_name = NULL;
|
||||
int err;
|
||||
|
||||
wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
|
||||
@ -147,7 +150,10 @@ static int bcm7038_wdt_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(wdt->base))
|
||||
return PTR_ERR(wdt->base);
|
||||
|
||||
wdt->clk = devm_clk_get(dev, NULL);
|
||||
if (pdata && pdata->clk_name)
|
||||
clk_name = pdata->clk_name;
|
||||
|
||||
wdt->clk = devm_clk_get(dev, clk_name);
|
||||
/* If unable to get clock, use default frequency */
|
||||
if (!IS_ERR(wdt->clk)) {
|
||||
err = clk_prepare_enable(wdt->clk);
|
||||
@ -217,8 +223,15 @@ static const struct of_device_id bcm7038_wdt_match[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, bcm7038_wdt_match);
|
||||
|
||||
static const struct platform_device_id bcm7038_wdt_devtype[] = {
|
||||
{ .name = "bcm63xx-wdt" },
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, bcm7038_wdt_devtype);
|
||||
|
||||
static struct platform_driver bcm7038_wdt_driver = {
|
||||
.probe = bcm7038_wdt_probe,
|
||||
.id_table = bcm7038_wdt_devtype,
|
||||
.driver = {
|
||||
.name = "bcm7038-wdt",
|
||||
.of_match_table = bcm7038_wdt_match,
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/mfd/da9063/registers.h>
|
||||
#include <linux/mfd/da9063/core.h>
|
||||
@ -169,14 +170,19 @@ static int da9063_wdt_restart(struct watchdog_device *wdd, unsigned long action,
|
||||
void *data)
|
||||
{
|
||||
struct da9063 *da9063 = watchdog_get_drvdata(wdd);
|
||||
struct i2c_client *client = to_i2c_client(da9063->dev);
|
||||
int ret;
|
||||
|
||||
ret = regmap_write(da9063->regmap, DA9063_REG_CONTROL_F,
|
||||
DA9063_SHUTDOWN);
|
||||
if (ret)
|
||||
/* Don't use regmap because it is not atomic safe */
|
||||
ret = i2c_smbus_write_byte_data(client, DA9063_REG_CONTROL_F,
|
||||
DA9063_SHUTDOWN);
|
||||
if (ret < 0)
|
||||
dev_alert(da9063->dev, "Failed to shutdown (err = %d)\n",
|
||||
ret);
|
||||
|
||||
/* wait for reset to assert... */
|
||||
mdelay(500);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -134,7 +134,7 @@ static unsigned int davinci_wdt_get_timeleft(struct watchdog_device *wdd)
|
||||
timer_counter = ioread32(davinci_wdt->base + TIM12);
|
||||
timer_counter |= ((u64)ioread32(davinci_wdt->base + TIM34) << 32);
|
||||
|
||||
do_div(timer_counter, freq);
|
||||
timer_counter = div64_ul(timer_counter, freq);
|
||||
|
||||
return wdd->timeout - timer_counter;
|
||||
}
|
||||
|
@ -49,6 +49,7 @@
|
||||
#define SIO_F81803_ID 0x1210 /* Chipset ID */
|
||||
#define SIO_F81865_ID 0x0704 /* Chipset ID */
|
||||
#define SIO_F81866_ID 0x1010 /* Chipset ID */
|
||||
#define SIO_F81966_ID 0x1502 /* F81804 chipset ID, same for f81966 */
|
||||
|
||||
#define F71808FG_REG_WDO_CONF 0xf0
|
||||
#define F71808FG_REG_WDT_CONF 0xf5
|
||||
@ -105,7 +106,7 @@ MODULE_PARM_DESC(start_withtimeout, "Start watchdog timer on module load with"
|
||||
" given initial timeout. Zero (default) disables this feature.");
|
||||
|
||||
enum chips { f71808fg, f71858fg, f71862fg, f71868, f71869, f71882fg, f71889fg,
|
||||
f81803, f81865, f81866};
|
||||
f81803, f81865, f81866, f81966};
|
||||
|
||||
static const char * const fintek_wdt_names[] = {
|
||||
"f71808fg",
|
||||
@ -118,6 +119,7 @@ static const char * const fintek_wdt_names[] = {
|
||||
"f81803",
|
||||
"f81865",
|
||||
"f81866",
|
||||
"f81966"
|
||||
};
|
||||
|
||||
/* Super-I/O Function prototypes */
|
||||
@ -347,6 +349,7 @@ static int fintek_wdt_start(struct watchdog_device *wdd)
|
||||
break;
|
||||
|
||||
case f81866:
|
||||
case f81966:
|
||||
/*
|
||||
* GPIO1 Control Register when 27h BIT3:2 = 01 & BIT0 = 0.
|
||||
* The PIN 70(GPIO15/WDTRST) is controlled by 2Ch:
|
||||
@ -373,7 +376,7 @@ static int fintek_wdt_start(struct watchdog_device *wdd)
|
||||
superio_select(wd->sioaddr, SIO_F71808FG_LD_WDT);
|
||||
superio_set_bit(wd->sioaddr, SIO_REG_ENABLE, 0);
|
||||
|
||||
if (wd->type == f81865 || wd->type == f81866)
|
||||
if (wd->type == f81865 || wd->type == f81866 || wd->type == f81966)
|
||||
superio_set_bit(wd->sioaddr, F81865_REG_WDO_CONF,
|
||||
F81865_FLAG_WDOUT_EN);
|
||||
else
|
||||
@ -580,6 +583,9 @@ static int __init fintek_wdt_find(int sioaddr)
|
||||
case SIO_F81866_ID:
|
||||
type = f81866;
|
||||
break;
|
||||
case SIO_F81966_ID:
|
||||
type = f81966;
|
||||
break;
|
||||
default:
|
||||
pr_info("Unrecognized Fintek device: %04x\n",
|
||||
(unsigned int)devid);
|
||||
|
@ -198,7 +198,6 @@ static int meson_gxbb_wdt_probe(struct platform_device *pdev)
|
||||
|
||||
meson_gxbb_wdt_set_timeout(&data->wdt_dev, data->wdt_dev.timeout);
|
||||
|
||||
watchdog_stop_on_reboot(&data->wdt_dev);
|
||||
return devm_watchdog_register_device(dev, &data->wdt_dev);
|
||||
}
|
||||
|
||||
|
@ -120,6 +120,10 @@ static int msc313e_wdt_probe(struct platform_device *pdev)
|
||||
priv->wdev.max_timeout = U32_MAX / clk_get_rate(priv->clk);
|
||||
priv->wdev.timeout = MSC313E_WDT_DEFAULT_TIMEOUT;
|
||||
|
||||
/* If the period is non-zero the WDT is running */
|
||||
if (readw(priv->base + REG_WDT_MAX_PRD_L) | (readw(priv->base + REG_WDT_MAX_PRD_H) << 16))
|
||||
set_bit(WDOG_HW_RUNNING, &priv->wdev.status);
|
||||
|
||||
watchdog_set_drvdata(&priv->wdev, priv);
|
||||
|
||||
watchdog_init_timeout(&priv->wdev, timeout, dev);
|
||||
|
@ -339,7 +339,7 @@ static int mtk_wdt_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(mtk_wdt->wdt_base))
|
||||
return PTR_ERR(mtk_wdt->wdt_base);
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
irq = platform_get_irq_optional(pdev, 0);
|
||||
if (irq > 0) {
|
||||
err = devm_request_irq(&pdev->dev, irq, mtk_wdt_isr, 0, "wdt_bark",
|
||||
&mtk_wdt->wdt_dev);
|
||||
|
384
drivers/watchdog/realtek_otto_wdt.c
Normal file
384
drivers/watchdog/realtek_otto_wdt.c
Normal file
@ -0,0 +1,384 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
/*
|
||||
* Realtek Otto MIPS platform watchdog
|
||||
*
|
||||
* Watchdog timer that will reset the system after timeout, using the selected
|
||||
* reset mode.
|
||||
*
|
||||
* Counter scaling and timeouts:
|
||||
* - Base prescale of (2 << 25), providing tick duration T_0: 168ms @ 200MHz
|
||||
* - PRESCALE: logarithmic prescaler adding a factor of {1, 2, 4, 8}
|
||||
* - Phase 1: Times out after (PHASE1 + 1) × PRESCALE × T_0
|
||||
* Generates an interrupt, WDT cannot be stopped after phase 1
|
||||
* - Phase 2: starts after phase 1, times out after (PHASE2 + 1) × PRESCALE × T_0
|
||||
* Resets the system according to RST_MODE
|
||||
*/
|
||||
|
||||
#include <linux/bits.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/math.h>
|
||||
#include <linux/minmax.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/watchdog.h>
|
||||
|
||||
#define OTTO_WDT_REG_CNTR 0x0
|
||||
#define OTTO_WDT_CNTR_PING BIT(31)
|
||||
|
||||
#define OTTO_WDT_REG_INTR 0x4
|
||||
#define OTTO_WDT_INTR_PHASE_1 BIT(31)
|
||||
#define OTTO_WDT_INTR_PHASE_2 BIT(30)
|
||||
|
||||
#define OTTO_WDT_REG_CTRL 0x8
|
||||
#define OTTO_WDT_CTRL_ENABLE BIT(31)
|
||||
#define OTTO_WDT_CTRL_PRESCALE GENMASK(30, 29)
|
||||
#define OTTO_WDT_CTRL_PHASE1 GENMASK(26, 22)
|
||||
#define OTTO_WDT_CTRL_PHASE2 GENMASK(19, 15)
|
||||
#define OTTO_WDT_CTRL_RST_MODE GENMASK(1, 0)
|
||||
#define OTTO_WDT_MODE_SOC 0
|
||||
#define OTTO_WDT_MODE_CPU 1
|
||||
#define OTTO_WDT_MODE_SOFTWARE 2
|
||||
#define OTTO_WDT_CTRL_DEFAULT OTTO_WDT_MODE_CPU
|
||||
|
||||
#define OTTO_WDT_PRESCALE_MAX 3
|
||||
|
||||
/*
|
||||
* One higher than the max values contained in PHASE{1,2}, since a value of 0
|
||||
* corresponds to one tick.
|
||||
*/
|
||||
#define OTTO_WDT_PHASE_TICKS_MAX 32
|
||||
|
||||
/*
|
||||
* The maximum reset delay is actually 2×32 ticks, but that would require large
|
||||
* pretimeout values for timeouts longer than 32 ticks. Limit the maximum timeout
|
||||
* to 32 + 1 to ensure small pretimeout values can be configured as expected.
|
||||
*/
|
||||
#define OTTO_WDT_TIMEOUT_TICKS_MAX (OTTO_WDT_PHASE_TICKS_MAX + 1)
|
||||
|
||||
struct otto_wdt_ctrl {
|
||||
struct watchdog_device wdev;
|
||||
struct device *dev;
|
||||
void __iomem *base;
|
||||
unsigned int clk_rate_khz;
|
||||
int irq_phase1;
|
||||
};
|
||||
|
||||
static int otto_wdt_start(struct watchdog_device *wdev)
|
||||
{
|
||||
struct otto_wdt_ctrl *ctrl = watchdog_get_drvdata(wdev);
|
||||
u32 v;
|
||||
|
||||
v = ioread32(ctrl->base + OTTO_WDT_REG_CTRL);
|
||||
v |= OTTO_WDT_CTRL_ENABLE;
|
||||
iowrite32(v, ctrl->base + OTTO_WDT_REG_CTRL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int otto_wdt_stop(struct watchdog_device *wdev)
|
||||
{
|
||||
struct otto_wdt_ctrl *ctrl = watchdog_get_drvdata(wdev);
|
||||
u32 v;
|
||||
|
||||
v = ioread32(ctrl->base + OTTO_WDT_REG_CTRL);
|
||||
v &= ~OTTO_WDT_CTRL_ENABLE;
|
||||
iowrite32(v, ctrl->base + OTTO_WDT_REG_CTRL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int otto_wdt_ping(struct watchdog_device *wdev)
|
||||
{
|
||||
struct otto_wdt_ctrl *ctrl = watchdog_get_drvdata(wdev);
|
||||
|
||||
iowrite32(OTTO_WDT_CNTR_PING, ctrl->base + OTTO_WDT_REG_CNTR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int otto_wdt_tick_ms(struct otto_wdt_ctrl *ctrl, int prescale)
|
||||
{
|
||||
return DIV_ROUND_CLOSEST(1 << (25 + prescale), ctrl->clk_rate_khz);
|
||||
}
|
||||
|
||||
/*
|
||||
* The timer asserts the PHASE1/PHASE2 IRQs when the number of ticks exceeds
|
||||
* the value stored in those fields. This means each phase will run for at least
|
||||
* one tick, so small values need to be clamped to correctly reflect the timeout.
|
||||
*/
|
||||
static inline unsigned int div_round_ticks(unsigned int val, unsigned int tick_duration,
|
||||
unsigned int min_ticks)
|
||||
{
|
||||
return max(min_ticks, DIV_ROUND_UP(val, tick_duration));
|
||||
}
|
||||
|
||||
static int otto_wdt_determine_timeouts(struct watchdog_device *wdev, unsigned int timeout,
|
||||
unsigned int pretimeout)
|
||||
{
|
||||
struct otto_wdt_ctrl *ctrl = watchdog_get_drvdata(wdev);
|
||||
unsigned int pretimeout_ms = pretimeout * 1000;
|
||||
unsigned int timeout_ms = timeout * 1000;
|
||||
unsigned int prescale_next = 0;
|
||||
unsigned int phase1_ticks;
|
||||
unsigned int phase2_ticks;
|
||||
unsigned int total_ticks;
|
||||
unsigned int prescale;
|
||||
unsigned int tick_ms;
|
||||
u32 v;
|
||||
|
||||
do {
|
||||
prescale = prescale_next;
|
||||
if (prescale > OTTO_WDT_PRESCALE_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
tick_ms = otto_wdt_tick_ms(ctrl, prescale);
|
||||
total_ticks = div_round_ticks(timeout_ms, tick_ms, 2);
|
||||
phase1_ticks = div_round_ticks(timeout_ms - pretimeout_ms, tick_ms, 1);
|
||||
phase2_ticks = total_ticks - phase1_ticks;
|
||||
|
||||
prescale_next++;
|
||||
} while (phase1_ticks > OTTO_WDT_PHASE_TICKS_MAX
|
||||
|| phase2_ticks > OTTO_WDT_PHASE_TICKS_MAX);
|
||||
|
||||
v = ioread32(ctrl->base + OTTO_WDT_REG_CTRL);
|
||||
|
||||
v &= ~(OTTO_WDT_CTRL_PRESCALE | OTTO_WDT_CTRL_PHASE1 | OTTO_WDT_CTRL_PHASE2);
|
||||
v |= FIELD_PREP(OTTO_WDT_CTRL_PHASE1, phase1_ticks - 1);
|
||||
v |= FIELD_PREP(OTTO_WDT_CTRL_PHASE2, phase2_ticks - 1);
|
||||
v |= FIELD_PREP(OTTO_WDT_CTRL_PRESCALE, prescale);
|
||||
|
||||
iowrite32(v, ctrl->base + OTTO_WDT_REG_CTRL);
|
||||
|
||||
timeout_ms = total_ticks * tick_ms;
|
||||
ctrl->wdev.timeout = timeout_ms / 1000;
|
||||
|
||||
pretimeout_ms = phase2_ticks * tick_ms;
|
||||
ctrl->wdev.pretimeout = pretimeout_ms / 1000;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int otto_wdt_set_timeout(struct watchdog_device *wdev, unsigned int val)
|
||||
{
|
||||
return otto_wdt_determine_timeouts(wdev, val, min(wdev->pretimeout, val - 1));
|
||||
}
|
||||
|
||||
static int otto_wdt_set_pretimeout(struct watchdog_device *wdev, unsigned int val)
|
||||
{
|
||||
return otto_wdt_determine_timeouts(wdev, wdev->timeout, val);
|
||||
}
|
||||
|
||||
static int otto_wdt_restart(struct watchdog_device *wdev, unsigned long reboot_mode,
|
||||
void *data)
|
||||
{
|
||||
struct otto_wdt_ctrl *ctrl = watchdog_get_drvdata(wdev);
|
||||
u32 reset_mode;
|
||||
u32 v;
|
||||
|
||||
disable_irq(ctrl->irq_phase1);
|
||||
|
||||
switch (reboot_mode) {
|
||||
case REBOOT_SOFT:
|
||||
reset_mode = OTTO_WDT_MODE_SOFTWARE;
|
||||
break;
|
||||
case REBOOT_WARM:
|
||||
reset_mode = OTTO_WDT_MODE_CPU;
|
||||
break;
|
||||
default:
|
||||
reset_mode = OTTO_WDT_MODE_SOC;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Configure for shortest timeout and wait for reset to occur */
|
||||
v = FIELD_PREP(OTTO_WDT_CTRL_RST_MODE, reset_mode) | OTTO_WDT_CTRL_ENABLE;
|
||||
iowrite32(v, ctrl->base + OTTO_WDT_REG_CTRL);
|
||||
|
||||
mdelay(3 * otto_wdt_tick_ms(ctrl, 0));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t otto_wdt_phase1_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct otto_wdt_ctrl *ctrl = dev_id;
|
||||
|
||||
iowrite32(OTTO_WDT_INTR_PHASE_1, ctrl->base + OTTO_WDT_REG_INTR);
|
||||
dev_crit(ctrl->dev, "phase 1 timeout\n");
|
||||
watchdog_notify_pretimeout(&ctrl->wdev);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static const struct watchdog_ops otto_wdt_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.start = otto_wdt_start,
|
||||
.stop = otto_wdt_stop,
|
||||
.ping = otto_wdt_ping,
|
||||
.set_timeout = otto_wdt_set_timeout,
|
||||
.set_pretimeout = otto_wdt_set_pretimeout,
|
||||
.restart = otto_wdt_restart,
|
||||
};
|
||||
|
||||
static const struct watchdog_info otto_wdt_info = {
|
||||
.identity = "Realtek Otto watchdog timer",
|
||||
.options = WDIOF_KEEPALIVEPING |
|
||||
WDIOF_MAGICCLOSE |
|
||||
WDIOF_SETTIMEOUT |
|
||||
WDIOF_PRETIMEOUT,
|
||||
};
|
||||
|
||||
static void otto_wdt_clock_action(void *data)
|
||||
{
|
||||
clk_disable_unprepare(data);
|
||||
}
|
||||
|
||||
static int otto_wdt_probe_clk(struct otto_wdt_ctrl *ctrl)
|
||||
{
|
||||
struct clk *clk = devm_clk_get(ctrl->dev, NULL);
|
||||
int ret;
|
||||
|
||||
if (IS_ERR(clk))
|
||||
return dev_err_probe(ctrl->dev, PTR_ERR(clk), "Failed to get clock\n");
|
||||
|
||||
ret = clk_prepare_enable(clk);
|
||||
if (ret)
|
||||
return dev_err_probe(ctrl->dev, ret, "Failed to enable clock\n");
|
||||
|
||||
ret = devm_add_action_or_reset(ctrl->dev, otto_wdt_clock_action, clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ctrl->clk_rate_khz = clk_get_rate(clk) / 1000;
|
||||
if (ctrl->clk_rate_khz == 0)
|
||||
return dev_err_probe(ctrl->dev, -ENXIO, "Failed to get clock rate\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int otto_wdt_probe_reset_mode(struct otto_wdt_ctrl *ctrl)
|
||||
{
|
||||
static const char *mode_property = "realtek,reset-mode";
|
||||
const struct fwnode_handle *node = ctrl->dev->fwnode;
|
||||
int mode_count;
|
||||
u32 mode;
|
||||
u32 v;
|
||||
|
||||
if (!node)
|
||||
return -ENXIO;
|
||||
|
||||
mode_count = fwnode_property_string_array_count(node, mode_property);
|
||||
if (mode_count < 0)
|
||||
return mode_count;
|
||||
else if (mode_count == 0)
|
||||
return 0;
|
||||
else if (mode_count != 1)
|
||||
return -EINVAL;
|
||||
|
||||
if (fwnode_property_match_string(node, mode_property, "soc") == 0)
|
||||
mode = OTTO_WDT_MODE_SOC;
|
||||
else if (fwnode_property_match_string(node, mode_property, "cpu") == 0)
|
||||
mode = OTTO_WDT_MODE_CPU;
|
||||
else if (fwnode_property_match_string(node, mode_property, "software") == 0)
|
||||
mode = OTTO_WDT_MODE_SOFTWARE;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
v = ioread32(ctrl->base + OTTO_WDT_REG_CTRL);
|
||||
v &= ~OTTO_WDT_CTRL_RST_MODE;
|
||||
v |= FIELD_PREP(OTTO_WDT_CTRL_RST_MODE, mode);
|
||||
iowrite32(v, ctrl->base + OTTO_WDT_REG_CTRL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int otto_wdt_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct otto_wdt_ctrl *ctrl;
|
||||
unsigned int max_tick_ms;
|
||||
int ret;
|
||||
|
||||
ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL);
|
||||
if (!ctrl)
|
||||
return -ENOMEM;
|
||||
|
||||
ctrl->dev = dev;
|
||||
ctrl->base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(ctrl->base))
|
||||
return PTR_ERR(ctrl->base);
|
||||
|
||||
/* Clear any old interrupts and reset initial state */
|
||||
iowrite32(OTTO_WDT_INTR_PHASE_1 | OTTO_WDT_INTR_PHASE_2,
|
||||
ctrl->base + OTTO_WDT_REG_INTR);
|
||||
iowrite32(OTTO_WDT_CTRL_DEFAULT, ctrl->base + OTTO_WDT_REG_CTRL);
|
||||
|
||||
ret = otto_wdt_probe_clk(ctrl);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ctrl->irq_phase1 = platform_get_irq_byname(pdev, "phase1");
|
||||
if (ctrl->irq_phase1 < 0)
|
||||
return ctrl->irq_phase1;
|
||||
|
||||
ret = devm_request_irq(dev, ctrl->irq_phase1, otto_wdt_phase1_isr, 0,
|
||||
"realtek-otto-wdt", ctrl);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Failed to get IRQ for phase1\n");
|
||||
|
||||
ret = otto_wdt_probe_reset_mode(ctrl);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Invalid reset mode specified\n");
|
||||
|
||||
ctrl->wdev.parent = dev;
|
||||
ctrl->wdev.info = &otto_wdt_info;
|
||||
ctrl->wdev.ops = &otto_wdt_ops;
|
||||
|
||||
/*
|
||||
* Since pretimeout cannot be disabled, min. timeout is twice the
|
||||
* subsystem resolution. Max. timeout is ca. 43s at a bus clock of 200MHz.
|
||||
*/
|
||||
ctrl->wdev.min_timeout = 2;
|
||||
max_tick_ms = otto_wdt_tick_ms(ctrl, OTTO_WDT_PRESCALE_MAX);
|
||||
ctrl->wdev.max_hw_heartbeat_ms = max_tick_ms * OTTO_WDT_TIMEOUT_TICKS_MAX;
|
||||
ctrl->wdev.timeout = min(30U, ctrl->wdev.max_hw_heartbeat_ms / 1000);
|
||||
|
||||
watchdog_set_drvdata(&ctrl->wdev, ctrl);
|
||||
watchdog_init_timeout(&ctrl->wdev, 0, dev);
|
||||
watchdog_stop_on_reboot(&ctrl->wdev);
|
||||
watchdog_set_restart_priority(&ctrl->wdev, 128);
|
||||
|
||||
ret = otto_wdt_determine_timeouts(&ctrl->wdev, ctrl->wdev.timeout, 1);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Failed to set timeout\n");
|
||||
|
||||
return devm_watchdog_register_device(dev, &ctrl->wdev);
|
||||
}
|
||||
|
||||
static const struct of_device_id otto_wdt_ids[] = {
|
||||
{ .compatible = "realtek,rtl8380-wdt" },
|
||||
{ .compatible = "realtek,rtl8390-wdt" },
|
||||
{ .compatible = "realtek,rtl9300-wdt" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, otto_wdt_ids);
|
||||
|
||||
static struct platform_driver otto_wdt_driver = {
|
||||
.probe = otto_wdt_probe,
|
||||
.driver = {
|
||||
.name = "realtek-otto-watchdog",
|
||||
.of_match_table = otto_wdt_ids,
|
||||
},
|
||||
};
|
||||
module_platform_driver(otto_wdt_driver);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_AUTHOR("Sander Vanheule <sander@svanheule.net>");
|
||||
MODULE_DESCRIPTION("Realtek Otto watchdog timer driver");
|
263
drivers/watchdog/rzg2l_wdt.c
Normal file
263
drivers/watchdog/rzg2l_wdt.c
Normal file
@ -0,0 +1,263 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Renesas RZ/G2L WDT Watchdog Driver
|
||||
*
|
||||
* Copyright (C) 2021 Renesas Electronics Corporation
|
||||
*/
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/units.h>
|
||||
#include <linux/watchdog.h>
|
||||
|
||||
#define WDTCNT 0x00
|
||||
#define WDTSET 0x04
|
||||
#define WDTTIM 0x08
|
||||
#define WDTINT 0x0C
|
||||
#define WDTCNT_WDTEN BIT(0)
|
||||
#define WDTINT_INTDISP BIT(0)
|
||||
|
||||
#define WDT_DEFAULT_TIMEOUT 60U
|
||||
|
||||
/* Setting period time register only 12 bit set in WDTSET[31:20] */
|
||||
#define WDTSET_COUNTER_MASK (0xFFF00000)
|
||||
#define WDTSET_COUNTER_VAL(f) ((f) << 20)
|
||||
|
||||
#define F2CYCLE_NSEC(f) (1000000000 / (f))
|
||||
|
||||
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) ")");
|
||||
|
||||
struct rzg2l_wdt_priv {
|
||||
void __iomem *base;
|
||||
struct watchdog_device wdev;
|
||||
struct reset_control *rstc;
|
||||
unsigned long osc_clk_rate;
|
||||
unsigned long delay;
|
||||
};
|
||||
|
||||
static void rzg2l_wdt_wait_delay(struct rzg2l_wdt_priv *priv)
|
||||
{
|
||||
/* delay timer when change the setting register */
|
||||
ndelay(priv->delay);
|
||||
}
|
||||
|
||||
static u32 rzg2l_wdt_get_cycle_usec(unsigned long cycle, u32 wdttime)
|
||||
{
|
||||
u64 timer_cycle_us = 1024 * 1024 * (wdttime + 1) * MICRO;
|
||||
|
||||
return div64_ul(timer_cycle_us, cycle);
|
||||
}
|
||||
|
||||
static void rzg2l_wdt_write(struct rzg2l_wdt_priv *priv, u32 val, unsigned int reg)
|
||||
{
|
||||
if (reg == WDTSET)
|
||||
val &= WDTSET_COUNTER_MASK;
|
||||
|
||||
writel_relaxed(val, priv->base + reg);
|
||||
/* Registers other than the WDTINT is always synchronized with WDT_CLK */
|
||||
if (reg != WDTINT)
|
||||
rzg2l_wdt_wait_delay(priv);
|
||||
}
|
||||
|
||||
static void rzg2l_wdt_init_timeout(struct watchdog_device *wdev)
|
||||
{
|
||||
struct rzg2l_wdt_priv *priv = watchdog_get_drvdata(wdev);
|
||||
u32 time_out;
|
||||
|
||||
/* Clear Lapsed Time Register and clear Interrupt */
|
||||
rzg2l_wdt_write(priv, WDTINT_INTDISP, WDTINT);
|
||||
/* 2 consecutive overflow cycle needed to trigger reset */
|
||||
time_out = (wdev->timeout * (MICRO / 2)) /
|
||||
rzg2l_wdt_get_cycle_usec(priv->osc_clk_rate, 0);
|
||||
rzg2l_wdt_write(priv, WDTSET_COUNTER_VAL(time_out), WDTSET);
|
||||
}
|
||||
|
||||
static int rzg2l_wdt_start(struct watchdog_device *wdev)
|
||||
{
|
||||
struct rzg2l_wdt_priv *priv = watchdog_get_drvdata(wdev);
|
||||
|
||||
reset_control_deassert(priv->rstc);
|
||||
pm_runtime_get_sync(wdev->parent);
|
||||
|
||||
/* 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);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rzg2l_wdt_stop(struct watchdog_device *wdev)
|
||||
{
|
||||
struct rzg2l_wdt_priv *priv = watchdog_get_drvdata(wdev);
|
||||
|
||||
pm_runtime_put(wdev->parent);
|
||||
reset_control_assert(priv->rstc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rzg2l_wdt_restart(struct watchdog_device *wdev,
|
||||
unsigned long action, void *data)
|
||||
{
|
||||
struct rzg2l_wdt_priv *priv = watchdog_get_drvdata(wdev);
|
||||
|
||||
/* Reset the module before we modify any register */
|
||||
reset_control_reset(priv->rstc);
|
||||
pm_runtime_get_sync(wdev->parent);
|
||||
|
||||
/* smallest counter value to reboot soon */
|
||||
rzg2l_wdt_write(priv, WDTSET_COUNTER_VAL(1), WDTSET);
|
||||
|
||||
/* Enable watchdog timer*/
|
||||
rzg2l_wdt_write(priv, WDTCNT_WDTEN, WDTCNT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct watchdog_info rzg2l_wdt_ident = {
|
||||
.options = WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT,
|
||||
.identity = "Renesas RZ/G2L WDT Watchdog",
|
||||
};
|
||||
|
||||
static int rzg2l_wdt_ping(struct watchdog_device *wdev)
|
||||
{
|
||||
struct rzg2l_wdt_priv *priv = watchdog_get_drvdata(wdev);
|
||||
|
||||
rzg2l_wdt_write(priv, WDTINT_INTDISP, WDTINT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct watchdog_ops rzg2l_wdt_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.start = rzg2l_wdt_start,
|
||||
.stop = rzg2l_wdt_stop,
|
||||
.ping = rzg2l_wdt_ping,
|
||||
.restart = rzg2l_wdt_restart,
|
||||
};
|
||||
|
||||
static void rzg2l_wdt_reset_assert_pm_disable_put(void *data)
|
||||
{
|
||||
struct watchdog_device *wdev = data;
|
||||
struct rzg2l_wdt_priv *priv = watchdog_get_drvdata(wdev);
|
||||
|
||||
pm_runtime_put(wdev->parent);
|
||||
pm_runtime_disable(wdev->parent);
|
||||
reset_control_assert(priv->rstc);
|
||||
}
|
||||
|
||||
static int rzg2l_wdt_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct rzg2l_wdt_priv *priv;
|
||||
unsigned long pclk_rate;
|
||||
struct clk *wdt_clk;
|
||||
int ret;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(priv->base))
|
||||
return PTR_ERR(priv->base);
|
||||
|
||||
/* Get watchdog main clock */
|
||||
wdt_clk = clk_get(&pdev->dev, "oscclk");
|
||||
if (IS_ERR(wdt_clk))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(wdt_clk), "no oscclk");
|
||||
|
||||
priv->osc_clk_rate = clk_get_rate(wdt_clk);
|
||||
clk_put(wdt_clk);
|
||||
if (!priv->osc_clk_rate)
|
||||
return dev_err_probe(&pdev->dev, -EINVAL, "oscclk rate is 0");
|
||||
|
||||
/* Get Peripheral clock */
|
||||
wdt_clk = clk_get(&pdev->dev, "pclk");
|
||||
if (IS_ERR(wdt_clk))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(wdt_clk), "no pclk");
|
||||
|
||||
pclk_rate = clk_get_rate(wdt_clk);
|
||||
clk_put(wdt_clk);
|
||||
if (!pclk_rate)
|
||||
return dev_err_probe(&pdev->dev, -EINVAL, "pclk rate is 0");
|
||||
|
||||
priv->delay = F2CYCLE_NSEC(priv->osc_clk_rate) * 6 + F2CYCLE_NSEC(pclk_rate) * 9;
|
||||
|
||||
priv->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL);
|
||||
if (IS_ERR(priv->rstc))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(priv->rstc),
|
||||
"failed to get cpg reset");
|
||||
|
||||
reset_control_deassert(priv->rstc);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
ret = pm_runtime_resume_and_get(&pdev->dev);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "pm_runtime_resume_and_get failed ret=%pe", ERR_PTR(ret));
|
||||
goto out_pm_get;
|
||||
}
|
||||
|
||||
priv->wdev.info = &rzg2l_wdt_ident;
|
||||
priv->wdev.ops = &rzg2l_wdt_ops;
|
||||
priv->wdev.parent = dev;
|
||||
priv->wdev.min_timeout = 1;
|
||||
priv->wdev.max_timeout = rzg2l_wdt_get_cycle_usec(priv->osc_clk_rate, 0xfff) /
|
||||
USEC_PER_SEC;
|
||||
priv->wdev.timeout = WDT_DEFAULT_TIMEOUT;
|
||||
|
||||
watchdog_set_drvdata(&priv->wdev, priv);
|
||||
ret = devm_add_action_or_reset(&pdev->dev,
|
||||
rzg2l_wdt_reset_assert_pm_disable_put,
|
||||
&priv->wdev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
watchdog_set_nowayout(&priv->wdev, nowayout);
|
||||
watchdog_stop_on_unregister(&priv->wdev);
|
||||
|
||||
ret = watchdog_init_timeout(&priv->wdev, 0, dev);
|
||||
if (ret)
|
||||
dev_warn(dev, "Specified timeout invalid, using default");
|
||||
|
||||
return devm_watchdog_register_device(&pdev->dev, &priv->wdev);
|
||||
|
||||
out_pm_get:
|
||||
pm_runtime_disable(dev);
|
||||
reset_control_assert(priv->rstc);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct of_device_id rzg2l_wdt_ids[] = {
|
||||
{ .compatible = "renesas,rzg2l-wdt", },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rzg2l_wdt_ids);
|
||||
|
||||
static struct platform_driver rzg2l_wdt_driver = {
|
||||
.driver = {
|
||||
.name = "rzg2l_wdt",
|
||||
.of_match_table = rzg2l_wdt_ids,
|
||||
},
|
||||
.probe = rzg2l_wdt_probe,
|
||||
};
|
||||
module_platform_driver(rzg2l_wdt_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Renesas RZ/G2L WDT Watchdog Driver");
|
||||
MODULE_AUTHOR("Biju Das <biju.das.jz@bp.renesas.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -56,13 +56,58 @@
|
||||
#define EXYNOS5_RST_STAT_REG_OFFSET 0x0404
|
||||
#define EXYNOS5_WDT_DISABLE_REG_OFFSET 0x0408
|
||||
#define EXYNOS5_WDT_MASK_RESET_REG_OFFSET 0x040c
|
||||
#define QUIRK_HAS_PMU_CONFIG (1 << 0)
|
||||
#define QUIRK_HAS_RST_STAT (1 << 1)
|
||||
#define QUIRK_HAS_WTCLRINT_REG (1 << 2)
|
||||
#define EXYNOS850_CLUSTER0_NONCPU_OUT 0x1220
|
||||
#define EXYNOS850_CLUSTER0_NONCPU_INT_EN 0x1244
|
||||
#define EXYNOS850_CLUSTER1_NONCPU_OUT 0x1620
|
||||
#define EXYNOS850_CLUSTER1_NONCPU_INT_EN 0x1644
|
||||
|
||||
#define EXYNOS850_CLUSTER0_WDTRESET_BIT 24
|
||||
#define EXYNOS850_CLUSTER1_WDTRESET_BIT 23
|
||||
|
||||
/**
|
||||
* DOC: Quirk flags for different Samsung watchdog IP-cores
|
||||
*
|
||||
* This driver supports multiple Samsung SoCs, each of which might have
|
||||
* different set of registers and features supported. As watchdog block
|
||||
* sometimes requires modifying PMU registers for proper functioning, register
|
||||
* differences in both watchdog and PMU IP-cores should be accounted for. Quirk
|
||||
* flags described below serve the purpose of telling the driver about mentioned
|
||||
* SoC traits, and can be specified in driver data for each particular supported
|
||||
* device.
|
||||
*
|
||||
* %QUIRK_HAS_WTCLRINT_REG: Watchdog block has WTCLRINT register. It's used to
|
||||
* clear the interrupt once the interrupt service routine is complete. It's
|
||||
* write-only, writing any values to this register clears the interrupt, but
|
||||
* reading is not permitted.
|
||||
*
|
||||
* %QUIRK_HAS_PMU_MASK_RESET: PMU block has the register for disabling/enabling
|
||||
* WDT reset request. On old SoCs it's usually called MASK_WDT_RESET_REQUEST,
|
||||
* new SoCs have CLUSTERx_NONCPU_INT_EN register, which 'mask_bit' value is
|
||||
* inverted compared to the former one.
|
||||
*
|
||||
* %QUIRK_HAS_PMU_RST_STAT: PMU block has RST_STAT (reset status) register,
|
||||
* which contains bits indicating the reason for most recent CPU reset. If
|
||||
* present, driver will use this register to check if previous reboot was due to
|
||||
* watchdog timer reset.
|
||||
*
|
||||
* %QUIRK_HAS_PMU_AUTO_DISABLE: PMU block has AUTOMATIC_WDT_RESET_DISABLE
|
||||
* register. If 'mask_bit' bit is set, PMU will disable WDT reset when
|
||||
* corresponding processor is in reset state.
|
||||
*
|
||||
* %QUIRK_HAS_PMU_CNT_EN: PMU block has some register (e.g. CLUSTERx_NONCPU_OUT)
|
||||
* with "watchdog counter enable" bit. That bit should be set to make watchdog
|
||||
* counter running.
|
||||
*/
|
||||
#define QUIRK_HAS_WTCLRINT_REG (1 << 0)
|
||||
#define QUIRK_HAS_PMU_MASK_RESET (1 << 1)
|
||||
#define QUIRK_HAS_PMU_RST_STAT (1 << 2)
|
||||
#define QUIRK_HAS_PMU_AUTO_DISABLE (1 << 3)
|
||||
#define QUIRK_HAS_PMU_CNT_EN (1 << 4)
|
||||
|
||||
/* These quirks require that we have a PMU register map */
|
||||
#define QUIRKS_HAVE_PMUREG (QUIRK_HAS_PMU_CONFIG | \
|
||||
QUIRK_HAS_RST_STAT)
|
||||
#define QUIRKS_HAVE_PMUREG \
|
||||
(QUIRK_HAS_PMU_MASK_RESET | QUIRK_HAS_PMU_RST_STAT | \
|
||||
QUIRK_HAS_PMU_AUTO_DISABLE | QUIRK_HAS_PMU_CNT_EN)
|
||||
|
||||
static bool nowayout = WATCHDOG_NOWAYOUT;
|
||||
static int tmr_margin;
|
||||
@ -90,26 +135,33 @@ MODULE_PARM_DESC(soft_noboot, "Watchdog action, set to 1 to ignore reboots, 0 to
|
||||
* timer reset functionality.
|
||||
* @mask_reset_reg: Offset in pmureg for the register that masks the watchdog
|
||||
* timer reset functionality.
|
||||
* @mask_reset_inv: If set, mask_reset_reg value will have inverted meaning.
|
||||
* @mask_bit: Bit number for the watchdog timer in the disable register and the
|
||||
* mask reset register.
|
||||
* @rst_stat_reg: Offset in pmureg for the register that has the reset status.
|
||||
* @rst_stat_bit: Bit number in the rst_stat register indicating a watchdog
|
||||
* reset.
|
||||
* @cnt_en_reg: Offset in pmureg for the register that enables WDT counter.
|
||||
* @cnt_en_bit: Bit number for "watchdog counter enable" in cnt_en register.
|
||||
* @quirks: A bitfield of quirks.
|
||||
*/
|
||||
|
||||
struct s3c2410_wdt_variant {
|
||||
int disable_reg;
|
||||
int mask_reset_reg;
|
||||
bool mask_reset_inv;
|
||||
int mask_bit;
|
||||
int rst_stat_reg;
|
||||
int rst_stat_bit;
|
||||
int cnt_en_reg;
|
||||
int cnt_en_bit;
|
||||
u32 quirks;
|
||||
};
|
||||
|
||||
struct s3c2410_wdt {
|
||||
struct device *dev;
|
||||
struct clk *clock;
|
||||
struct clk *bus_clk; /* for register interface (PCLK) */
|
||||
struct clk *src_clk; /* for WDT counter */
|
||||
void __iomem *reg_base;
|
||||
unsigned int count;
|
||||
spinlock_t lock;
|
||||
@ -136,8 +188,8 @@ static const struct s3c2410_wdt_variant drv_data_exynos5250 = {
|
||||
.mask_bit = 20,
|
||||
.rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET,
|
||||
.rst_stat_bit = 20,
|
||||
.quirks = QUIRK_HAS_PMU_CONFIG | QUIRK_HAS_RST_STAT \
|
||||
| QUIRK_HAS_WTCLRINT_REG,
|
||||
.quirks = QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_MASK_RESET | \
|
||||
QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_AUTO_DISABLE,
|
||||
};
|
||||
|
||||
static const struct s3c2410_wdt_variant drv_data_exynos5420 = {
|
||||
@ -146,8 +198,8 @@ static const struct s3c2410_wdt_variant drv_data_exynos5420 = {
|
||||
.mask_bit = 0,
|
||||
.rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET,
|
||||
.rst_stat_bit = 9,
|
||||
.quirks = QUIRK_HAS_PMU_CONFIG | QUIRK_HAS_RST_STAT \
|
||||
| QUIRK_HAS_WTCLRINT_REG,
|
||||
.quirks = QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_MASK_RESET | \
|
||||
QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_AUTO_DISABLE,
|
||||
};
|
||||
|
||||
static const struct s3c2410_wdt_variant drv_data_exynos7 = {
|
||||
@ -156,8 +208,32 @@ static const struct s3c2410_wdt_variant drv_data_exynos7 = {
|
||||
.mask_bit = 23,
|
||||
.rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET,
|
||||
.rst_stat_bit = 23, /* A57 WDTRESET */
|
||||
.quirks = QUIRK_HAS_PMU_CONFIG | QUIRK_HAS_RST_STAT \
|
||||
| QUIRK_HAS_WTCLRINT_REG,
|
||||
.quirks = QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_MASK_RESET | \
|
||||
QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_AUTO_DISABLE,
|
||||
};
|
||||
|
||||
static const struct s3c2410_wdt_variant drv_data_exynos850_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 = EXYNOS850_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_exynos850_cl1 = {
|
||||
.mask_reset_reg = EXYNOS850_CLUSTER1_NONCPU_INT_EN,
|
||||
.mask_bit = 2,
|
||||
.mask_reset_inv = true,
|
||||
.rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET,
|
||||
.rst_stat_bit = EXYNOS850_CLUSTER1_WDTRESET_BIT,
|
||||
.cnt_en_reg = EXYNOS850_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[] = {
|
||||
@ -171,6 +247,8 @@ static const struct of_device_id s3c2410_wdt_match[] = {
|
||||
.data = &drv_data_exynos5420 },
|
||||
{ .compatible = "samsung,exynos7-wdt",
|
||||
.data = &drv_data_exynos7 },
|
||||
{ .compatible = "samsung,exynos850-wdt",
|
||||
.data = &drv_data_exynos850_cl0 },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, s3c2410_wdt_match);
|
||||
@ -187,9 +265,14 @@ MODULE_DEVICE_TABLE(platform, s3c2410_wdt_ids);
|
||||
|
||||
/* functions */
|
||||
|
||||
static inline unsigned int s3c2410wdt_max_timeout(struct clk *clock)
|
||||
static inline unsigned long s3c2410wdt_get_freq(struct s3c2410_wdt *wdt)
|
||||
{
|
||||
unsigned long freq = clk_get_rate(clock);
|
||||
return clk_get_rate(wdt->src_clk ? wdt->src_clk : wdt->bus_clk);
|
||||
}
|
||||
|
||||
static inline unsigned int s3c2410wdt_max_timeout(struct s3c2410_wdt *wdt)
|
||||
{
|
||||
const unsigned long freq = s3c2410wdt_get_freq(wdt);
|
||||
|
||||
return S3C2410_WTCNT_MAXCNT / (freq / (S3C2410_WTCON_PRESCALE_MAX + 1)
|
||||
/ S3C2410_WTCON_MAXDIV);
|
||||
@ -200,35 +283,74 @@ static inline struct s3c2410_wdt *freq_to_wdt(struct notifier_block *nb)
|
||||
return container_of(nb, struct s3c2410_wdt, freq_transition);
|
||||
}
|
||||
|
||||
static int s3c2410wdt_mask_and_disable_reset(struct s3c2410_wdt *wdt, bool mask)
|
||||
static int s3c2410wdt_disable_wdt_reset(struct s3c2410_wdt *wdt, bool mask)
|
||||
{
|
||||
const u32 mask_val = BIT(wdt->drv_data->mask_bit);
|
||||
const u32 val = mask ? mask_val : 0;
|
||||
int ret;
|
||||
u32 mask_val = 1 << wdt->drv_data->mask_bit;
|
||||
u32 val = 0;
|
||||
|
||||
/* No need to do anything if no PMU CONFIG needed */
|
||||
if (!(wdt->drv_data->quirks & QUIRK_HAS_PMU_CONFIG))
|
||||
return 0;
|
||||
|
||||
if (mask)
|
||||
val = mask_val;
|
||||
|
||||
ret = regmap_update_bits(wdt->pmureg,
|
||||
wdt->drv_data->disable_reg,
|
||||
mask_val, val);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
ret = regmap_update_bits(wdt->pmureg,
|
||||
wdt->drv_data->mask_reset_reg,
|
||||
mask_val, val);
|
||||
error:
|
||||
ret = regmap_update_bits(wdt->pmureg, wdt->drv_data->disable_reg,
|
||||
mask_val, val);
|
||||
if (ret < 0)
|
||||
dev_err(wdt->dev, "failed to update reg(%d)\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int s3c2410wdt_mask_wdt_reset(struct s3c2410_wdt *wdt, bool mask)
|
||||
{
|
||||
const u32 mask_val = BIT(wdt->drv_data->mask_bit);
|
||||
const bool val_inv = wdt->drv_data->mask_reset_inv;
|
||||
const u32 val = (mask ^ val_inv) ? mask_val : 0;
|
||||
int ret;
|
||||
|
||||
ret = regmap_update_bits(wdt->pmureg, wdt->drv_data->mask_reset_reg,
|
||||
mask_val, val);
|
||||
if (ret < 0)
|
||||
dev_err(wdt->dev, "failed to update reg(%d)\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int s3c2410wdt_enable_counter(struct s3c2410_wdt *wdt, bool en)
|
||||
{
|
||||
const u32 mask_val = BIT(wdt->drv_data->cnt_en_bit);
|
||||
const u32 val = en ? mask_val : 0;
|
||||
int ret;
|
||||
|
||||
ret = regmap_update_bits(wdt->pmureg, wdt->drv_data->cnt_en_reg,
|
||||
mask_val, val);
|
||||
if (ret < 0)
|
||||
dev_err(wdt->dev, "failed to update reg(%d)\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int s3c2410wdt_enable(struct s3c2410_wdt *wdt, bool en)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (wdt->drv_data->quirks & QUIRK_HAS_PMU_AUTO_DISABLE) {
|
||||
ret = s3c2410wdt_disable_wdt_reset(wdt, !en);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (wdt->drv_data->quirks & QUIRK_HAS_PMU_MASK_RESET) {
|
||||
ret = s3c2410wdt_mask_wdt_reset(wdt, !en);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (wdt->drv_data->quirks & QUIRK_HAS_PMU_CNT_EN) {
|
||||
ret = s3c2410wdt_enable_counter(wdt, en);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int s3c2410wdt_keepalive(struct watchdog_device *wdd)
|
||||
{
|
||||
struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd);
|
||||
@ -300,7 +422,7 @@ static int s3c2410wdt_set_heartbeat(struct watchdog_device *wdd,
|
||||
unsigned int timeout)
|
||||
{
|
||||
struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd);
|
||||
unsigned long freq = clk_get_rate(wdt->clock);
|
||||
unsigned long freq = s3c2410wdt_get_freq(wdt);
|
||||
unsigned int count;
|
||||
unsigned int divisor = 1;
|
||||
unsigned long wtcon;
|
||||
@ -482,7 +604,7 @@ static inline unsigned int s3c2410wdt_get_bootstatus(struct s3c2410_wdt *wdt)
|
||||
unsigned int rst_stat;
|
||||
int ret;
|
||||
|
||||
if (!(wdt->drv_data->quirks & QUIRK_HAS_RST_STAT))
|
||||
if (!(wdt->drv_data->quirks & QUIRK_HAS_PMU_RST_STAT))
|
||||
return 0;
|
||||
|
||||
ret = regmap_read(wdt->pmureg, wdt->drv_data->rst_stat_reg, &rst_stat);
|
||||
@ -498,14 +620,40 @@ static inline const struct s3c2410_wdt_variant *
|
||||
s3c2410_get_wdt_drv_data(struct platform_device *pdev)
|
||||
{
|
||||
const struct s3c2410_wdt_variant *variant;
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
variant = of_device_get_match_data(&pdev->dev);
|
||||
variant = of_device_get_match_data(dev);
|
||||
if (!variant) {
|
||||
/* Device matched by platform_device_id */
|
||||
variant = (struct s3c2410_wdt_variant *)
|
||||
platform_get_device_id(pdev)->driver_data;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
/* Choose Exynos850 driver data w.r.t. cluster index */
|
||||
if (variant == &drv_data_exynos850_cl0) {
|
||||
u32 index;
|
||||
int err;
|
||||
|
||||
err = of_property_read_u32(dev->of_node,
|
||||
"samsung,cluster-index", &index);
|
||||
if (err) {
|
||||
dev_err(dev, "failed to get cluster index\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
switch (index) {
|
||||
case 0:
|
||||
return &drv_data_exynos850_cl0;
|
||||
case 1:
|
||||
return &drv_data_exynos850_cl1;
|
||||
default:
|
||||
dev_err(dev, "wrong cluster index: %u\n", index);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return variant;
|
||||
}
|
||||
|
||||
@ -513,9 +661,8 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct s3c2410_wdt *wdt;
|
||||
struct resource *wdt_irq;
|
||||
unsigned int wtcon;
|
||||
int started = 0;
|
||||
int wdt_irq;
|
||||
int ret;
|
||||
|
||||
wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
|
||||
@ -527,6 +674,9 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
|
||||
wdt->wdt_device = s3c2410_wdd;
|
||||
|
||||
wdt->drv_data = s3c2410_get_wdt_drv_data(pdev);
|
||||
if (!wdt->drv_data)
|
||||
return -EINVAL;
|
||||
|
||||
if (wdt->drv_data->quirks & QUIRKS_HAVE_PMUREG) {
|
||||
wdt->pmureg = syscon_regmap_lookup_by_phandle(dev->of_node,
|
||||
"samsung,syscon-phandle");
|
||||
@ -536,40 +686,52 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
|
||||
}
|
||||
}
|
||||
|
||||
wdt_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
||||
if (wdt_irq == NULL) {
|
||||
dev_err(dev, "no irq resource specified\n");
|
||||
ret = -ENOENT;
|
||||
goto err;
|
||||
}
|
||||
wdt_irq = platform_get_irq(pdev, 0);
|
||||
if (wdt_irq < 0)
|
||||
return wdt_irq;
|
||||
|
||||
/* get the memory region for the watchdog timer */
|
||||
wdt->reg_base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(wdt->reg_base)) {
|
||||
ret = PTR_ERR(wdt->reg_base);
|
||||
goto err;
|
||||
if (IS_ERR(wdt->reg_base))
|
||||
return PTR_ERR(wdt->reg_base);
|
||||
|
||||
wdt->bus_clk = devm_clk_get(dev, "watchdog");
|
||||
if (IS_ERR(wdt->bus_clk)) {
|
||||
dev_err(dev, "failed to find bus clock\n");
|
||||
return PTR_ERR(wdt->bus_clk);
|
||||
}
|
||||
|
||||
wdt->clock = devm_clk_get(dev, "watchdog");
|
||||
if (IS_ERR(wdt->clock)) {
|
||||
dev_err(dev, "failed to find watchdog clock source\n");
|
||||
ret = PTR_ERR(wdt->clock);
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(wdt->clock);
|
||||
ret = clk_prepare_enable(wdt->bus_clk);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to enable clock\n");
|
||||
dev_err(dev, "failed to enable bus clock\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* "watchdog_src" clock is optional; if it's not present -- just skip it
|
||||
* and use "watchdog" clock as both bus and source clock.
|
||||
*/
|
||||
wdt->src_clk = devm_clk_get_optional(dev, "watchdog_src");
|
||||
if (IS_ERR(wdt->src_clk)) {
|
||||
dev_err_probe(dev, PTR_ERR(wdt->src_clk),
|
||||
"failed to get source clock\n");
|
||||
ret = PTR_ERR(wdt->src_clk);
|
||||
goto err_bus_clk;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(wdt->src_clk);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to enable source clock\n");
|
||||
goto err_bus_clk;
|
||||
}
|
||||
|
||||
wdt->wdt_device.min_timeout = 1;
|
||||
wdt->wdt_device.max_timeout = s3c2410wdt_max_timeout(wdt->clock);
|
||||
wdt->wdt_device.max_timeout = s3c2410wdt_max_timeout(wdt);
|
||||
|
||||
ret = s3c2410wdt_cpufreq_register(wdt);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to register cpufreq\n");
|
||||
goto err_clk;
|
||||
goto err_src_clk;
|
||||
}
|
||||
|
||||
watchdog_set_drvdata(&wdt->wdt_device, wdt);
|
||||
@ -581,19 +743,19 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
|
||||
ret = s3c2410wdt_set_heartbeat(&wdt->wdt_device,
|
||||
wdt->wdt_device.timeout);
|
||||
if (ret) {
|
||||
started = s3c2410wdt_set_heartbeat(&wdt->wdt_device,
|
||||
S3C2410_WATCHDOG_DEFAULT_TIME);
|
||||
|
||||
if (started == 0)
|
||||
dev_info(dev,
|
||||
"tmr_margin value out of range, default %d used\n",
|
||||
ret = s3c2410wdt_set_heartbeat(&wdt->wdt_device,
|
||||
S3C2410_WATCHDOG_DEFAULT_TIME);
|
||||
if (ret == 0) {
|
||||
dev_warn(dev, "tmr_margin value out of range, default %d used\n",
|
||||
S3C2410_WATCHDOG_DEFAULT_TIME);
|
||||
else
|
||||
dev_info(dev, "default timer value is out of range, cannot start\n");
|
||||
} else {
|
||||
dev_err(dev, "failed to use default timeout\n");
|
||||
goto err_cpufreq;
|
||||
}
|
||||
}
|
||||
|
||||
ret = devm_request_irq(dev, wdt_irq->start, s3c2410wdt_irq, 0,
|
||||
pdev->name, pdev);
|
||||
ret = devm_request_irq(dev, wdt_irq, s3c2410wdt_irq, 0,
|
||||
pdev->name, pdev);
|
||||
if (ret != 0) {
|
||||
dev_err(dev, "failed to install irq (%d)\n", ret);
|
||||
goto err_cpufreq;
|
||||
@ -605,25 +767,29 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
|
||||
wdt->wdt_device.bootstatus = s3c2410wdt_get_bootstatus(wdt);
|
||||
wdt->wdt_device.parent = dev;
|
||||
|
||||
/*
|
||||
* If "tmr_atboot" param is non-zero, start the watchdog right now. Also
|
||||
* set WDOG_HW_RUNNING bit, so that watchdog core can kick the watchdog.
|
||||
*
|
||||
* If we're not enabling the watchdog, then ensure it is disabled if it
|
||||
* has been left running from the bootloader or other source.
|
||||
*/
|
||||
if (tmr_atboot) {
|
||||
dev_info(dev, "starting watchdog timer\n");
|
||||
s3c2410wdt_start(&wdt->wdt_device);
|
||||
set_bit(WDOG_HW_RUNNING, &wdt->wdt_device.status);
|
||||
} else {
|
||||
s3c2410wdt_stop(&wdt->wdt_device);
|
||||
}
|
||||
|
||||
ret = watchdog_register_device(&wdt->wdt_device);
|
||||
if (ret)
|
||||
goto err_cpufreq;
|
||||
|
||||
ret = s3c2410wdt_mask_and_disable_reset(wdt, false);
|
||||
ret = s3c2410wdt_enable(wdt, true);
|
||||
if (ret < 0)
|
||||
goto err_unregister;
|
||||
|
||||
if (tmr_atboot && started == 0) {
|
||||
dev_info(dev, "starting watchdog timer\n");
|
||||
s3c2410wdt_start(&wdt->wdt_device);
|
||||
} else if (!tmr_atboot) {
|
||||
/* if we're not enabling the watchdog, then ensure it is
|
||||
* disabled if it has been left running from the bootloader
|
||||
* or other source */
|
||||
|
||||
s3c2410wdt_stop(&wdt->wdt_device);
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, wdt);
|
||||
|
||||
/* print out a statement of readiness */
|
||||
@ -643,10 +809,12 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
|
||||
err_cpufreq:
|
||||
s3c2410wdt_cpufreq_deregister(wdt);
|
||||
|
||||
err_clk:
|
||||
clk_disable_unprepare(wdt->clock);
|
||||
err_src_clk:
|
||||
clk_disable_unprepare(wdt->src_clk);
|
||||
|
||||
err_bus_clk:
|
||||
clk_disable_unprepare(wdt->bus_clk);
|
||||
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -655,7 +823,7 @@ static int s3c2410wdt_remove(struct platform_device *dev)
|
||||
int ret;
|
||||
struct s3c2410_wdt *wdt = platform_get_drvdata(dev);
|
||||
|
||||
ret = s3c2410wdt_mask_and_disable_reset(wdt, true);
|
||||
ret = s3c2410wdt_enable(wdt, false);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@ -663,7 +831,8 @@ static int s3c2410wdt_remove(struct platform_device *dev)
|
||||
|
||||
s3c2410wdt_cpufreq_deregister(wdt);
|
||||
|
||||
clk_disable_unprepare(wdt->clock);
|
||||
clk_disable_unprepare(wdt->src_clk);
|
||||
clk_disable_unprepare(wdt->bus_clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -672,8 +841,7 @@ static void s3c2410wdt_shutdown(struct platform_device *dev)
|
||||
{
|
||||
struct s3c2410_wdt *wdt = platform_get_drvdata(dev);
|
||||
|
||||
s3c2410wdt_mask_and_disable_reset(wdt, true);
|
||||
|
||||
s3c2410wdt_enable(wdt, false);
|
||||
s3c2410wdt_stop(&wdt->wdt_device);
|
||||
}
|
||||
|
||||
@ -688,7 +856,7 @@ static int s3c2410wdt_suspend(struct device *dev)
|
||||
wdt->wtcon_save = readl(wdt->reg_base + S3C2410_WTCON);
|
||||
wdt->wtdat_save = readl(wdt->reg_base + S3C2410_WTDAT);
|
||||
|
||||
ret = s3c2410wdt_mask_and_disable_reset(wdt, true);
|
||||
ret = s3c2410wdt_enable(wdt, false);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@ -708,7 +876,7 @@ static int s3c2410wdt_resume(struct device *dev)
|
||||
writel(wdt->wtdat_save, wdt->reg_base + S3C2410_WTCNT);/* Reset count */
|
||||
writel(wdt->wtcon_save, wdt->reg_base + S3C2410_WTCON);
|
||||
|
||||
ret = s3c2410wdt_mask_and_disable_reset(wdt, false);
|
||||
ret = s3c2410wdt_enable(wdt, true);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
8
include/linux/platform_data/bcm7038_wdt.h
Normal file
8
include/linux/platform_data/bcm7038_wdt.h
Normal file
@ -0,0 +1,8 @@
|
||||
#ifndef __BCM7038_WDT_PDATA_H
|
||||
#define __BCM7038_WDT_PDATA_H
|
||||
|
||||
struct bcm7038_wdt_platform_data {
|
||||
const char *clk_name;
|
||||
};
|
||||
|
||||
#endif /* __BCM7038_WDT_PDATA_H */
|
Loading…
Reference in New Issue
Block a user