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:
Linus Torvalds 2022-01-17 08:07:57 +02:00
commit 2225acc322
24 changed files with 1497 additions and 497 deletions

View File

@ -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>;
};

View File

@ -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>;
};

View File

@ -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

View File

@ -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

View File

@ -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>;
};
...

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View 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");

View File

@ -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");

View File

@ -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,

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);

View File

@ -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);
}

View File

@ -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);

View File

@ -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);

View 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");

View 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");

View File

@ -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;

View 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 */