提交 96752be4 编写于 作者: L Linus Torvalds

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

Pull watchdog updates from Wim Van Sebroeck:

 - Add MediaTek MT8186 support

 - Add Mediatek MT7986 reset-controller support

 - Add i.MX93 support

 - Add watchdog driver for Sunplus SP7021

 - Add SC8180X and SC8280XP compatibles

 - Add Renesas RZ/N1 Watchdog driver and support for RZ/N1

 - rzg2l_wdt improvements and fixes

 - Several other improvements and fixes

* tag 'linux-watchdog-5.19-rc1' of git://www.linux-watchdog.org/linux-watchdog: (38 commits)
  watchdog: ts4800_wdt: Fix refcount leak in ts4800_wdt_probe
  dt-bindings: watchdog: renesas,wdt: R-Car V3U is R-Car Gen4
  watchdog: Add Renesas RZ/N1 Watchdog driver
  dt-bindings: watchdog: renesas,wdt: Add support for RZ/N1
  watchdog: wdat_wdt: Stop watchdog when uninstalling module
  watchdog: wdat_wdt: Stop watchdog when rebooting the system
  watchdog: wdat_wdt: Using the existing function to check parameter timeout
  dt-bindings: watchdog: da9062: add watchdog timeout mode
  dt-bindings: watchdog: renesas,wdt: Document RZ/G2UL SoC
  watchdog: iTCO_wdt: Using existing macro define covers more scenarios
  watchdog: rti-wdt: Fix pm_runtime_get_sync() error checking
  dt-bindings: watchdog: Add SC8180X and SC8280XP compatibles
  watchdog: rti_wdt: Fix calculation and evaluation of preset heartbeat
  dt-bindings: watchdog: uniphier: Use unevaluatedProperties
  watchdog: sp805: disable watchdog on remove
  watchdog: da9063: optionally disable watchdog during suspend
  dt-bindings: mfd: da9063: watchdog: add suspend disable option
  dt-bindings: watchdog: sunxi: clarify clock support
  dt-bindings: watchdog: sunxi: fix F1C100s compatible
  watchdog: Add watchdog driver for Sunplus SP7021
  ...
...@@ -64,10 +64,13 @@ Sub-nodes: ...@@ -64,10 +64,13 @@ Sub-nodes:
and KEY_SLEEP. and KEY_SLEEP.
- watchdog : This node defines settings for the Watchdog timer associated - watchdog : This node defines settings for the Watchdog timer associated
with the DA9063 and DA9063L. There are currently no entries in this with the DA9063 and DA9063L. The node should contain the compatible property
binding, however compatible = "dlg,da9063-watchdog" should be added with the value "dlg,da9063-watchdog".
if a node is created.
Optional watchdog properties:
- dlg,use-sw-pm: Add this property to disable the watchdog during suspend.
Only use this option if you can't use the watchdog automatic suspend
function during a suspend (see register CONTROL_B).
Example: Example:
......
...@@ -10,6 +10,12 @@ Optional properties: ...@@ -10,6 +10,12 @@ Optional properties:
- dlg,use-sw-pm: Add this property to disable the watchdog during suspend. - dlg,use-sw-pm: Add this property to disable the watchdog during suspend.
Only use this option if you can't use the watchdog automatic suspend Only use this option if you can't use the watchdog automatic suspend
function during a suspend (see register CONTROL_B). function during a suspend (see register CONTROL_B).
- dlg,wdt-sd: Set what happens on watchdog timeout. If this bit is set the
watchdog timeout triggers SHUTDOWN, if cleared the watchdog triggers
POWERDOWN. Can be 0 or 1. Only use this option if you want to change the
default chip's OTP setting for WATCHDOG_SD bit. If this property is NOT
set the WATCHDOG_SD bit and on timeout watchdog behavior will match the
chip's OTP settings.
Example: DA9062 Example: DA9062
......
Faraday Technology FTWDT010 watchdog
This is an IP part from Faraday Technology found in the Gemini
SoCs and others.
Required properties:
- compatible : must be one of
"faraday,ftwdt010"
"cortina,gemini-watchdog", "faraday,ftwdt010"
- reg : shall contain base register location and length
- interrupts : shall contain the interrupt for the watchdog
Optional properties:
- timeout-sec : the default watchdog timeout in seconds.
Example:
watchdog@41000000 {
compatible = "faraday,ftwdt010";
reg = <0x41000000 0x1000>;
interrupts = <3 IRQ_TYPE_LEVEL_HIGH>;
};
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/watchdog/faraday,ftwdt010.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Faraday Technology FTWDT010 watchdog
maintainers:
- Linus Walleij <linus.walleij@linaro.org>
- Corentin Labbe <clabbe@baylibre.com>
description: |
This is an IP part from Faraday Technology found in the Gemini
SoCs and others.
allOf:
- $ref: "watchdog.yaml#"
properties:
compatible:
oneOf:
- const: faraday,ftwdt010
- items:
- enum:
- cortina,gemini-watchdog
- moxa,moxart-watchdog
- const: faraday,ftwdt010
reg:
maxItems: 1
resets:
maxItems: 1
clocks:
maxItems: 1
clock-names:
const: PCLK
interrupts:
maxItems: 1
required:
- compatible
- reg
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
watchdog@41000000 {
compatible = "faraday,ftwdt010";
reg = <0x41000000 0x1000>;
interrupts = <3 IRQ_TYPE_LEVEL_HIGH>;
timeout-secs = <5>;
};
- |
watchdog: watchdog@98500000 {
compatible = "moxa,moxart-watchdog", "faraday,ftwdt010";
reg = <0x98500000 0x10>;
clocks = <&clk_apb>;
clock-names = "PCLK";
};
...
...@@ -19,6 +19,7 @@ properties: ...@@ -19,6 +19,7 @@ properties:
- items: - items:
- const: fsl,imx8ulp-wdt - const: fsl,imx8ulp-wdt
- const: fsl,imx7ulp-wdt - const: fsl,imx7ulp-wdt
- const: fsl,imx93-wdt
reg: reg:
maxItems: 1 maxItems: 1
......
...@@ -16,6 +16,7 @@ Required properties: ...@@ -16,6 +16,7 @@ Required properties:
"mediatek,mt7629-wdt", "mediatek,mt6589-wdt": for MT7629 "mediatek,mt7629-wdt", "mediatek,mt6589-wdt": for MT7629
"mediatek,mt7986-wdt", "mediatek,mt6589-wdt": for MT7986 "mediatek,mt7986-wdt", "mediatek,mt6589-wdt": for MT7986
"mediatek,mt8183-wdt": for MT8183 "mediatek,mt8183-wdt": for MT8183
"mediatek,mt8186-wdt", "mediatek,mt6589-wdt": for MT8186
"mediatek,mt8516-wdt", "mediatek,mt6589-wdt": for MT8516 "mediatek,mt8516-wdt", "mediatek,mt6589-wdt": for MT8516
"mediatek,mt8192-wdt": for MT8192 "mediatek,mt8192-wdt": for MT8192
"mediatek,mt8195-wdt", "mediatek,mt6589-wdt": for MT8195 "mediatek,mt8195-wdt", "mediatek,mt6589-wdt": for MT8195
......
...@@ -14,22 +14,29 @@ allOf: ...@@ -14,22 +14,29 @@ allOf:
properties: properties:
compatible: compatible:
enum: oneOf:
- qcom,apss-wdt-qcs404 - items:
- qcom,apss-wdt-sc7180 - enum:
- qcom,apss-wdt-sc7280 - qcom,apss-wdt-qcs404
- qcom,apss-wdt-sdm845 - qcom,apss-wdt-sc7180
- qcom,apss-wdt-sdx55 - qcom,apss-wdt-sc7280
- qcom,apss-wdt-sm6350 - qcom,apss-wdt-sc8180x
- qcom,apss-wdt-sm8150 - qcom,apss-wdt-sc8280xp
- qcom,apss-wdt-sm8250 - qcom,apss-wdt-sdm845
- qcom,kpss-timer - qcom,apss-wdt-sdx55
- qcom,kpss-wdt - qcom,apss-wdt-sm6350
- qcom,kpss-wdt-apq8064 - qcom,apss-wdt-sm8150
- qcom,kpss-wdt-ipq4019 - qcom,apss-wdt-sm8250
- qcom,kpss-wdt-ipq8064 - const: qcom,kpss-wdt
- qcom,kpss-wdt-msm8960 - items:
- qcom,scss-timer - enum:
- qcom,kpss-wdt
- qcom,kpss-timer
- qcom,kpss-wdt-apq8064
- qcom,kpss-wdt-ipq4019
- qcom,kpss-wdt-ipq8064
- qcom,kpss-wdt-msm8960
- qcom,scss-timer
reg: reg:
maxItems: 1 maxItems: 1
......
...@@ -21,8 +21,15 @@ properties: ...@@ -21,8 +21,15 @@ properties:
- items: - items:
- enum: - enum:
- renesas,r9a06g032-wdt # RZ/N1D
- const: renesas,rzn1-wdt # RZ/N1
- items:
- enum:
- renesas,r9a07g043-wdt # RZ/G2UL
- renesas,r9a07g044-wdt # RZ/G2{L,LC} - renesas,r9a07g044-wdt # RZ/G2{L,LC}
- const: renesas,rzg2l-wdt # RZ/G2L - renesas,r9a07g054-wdt # RZ/V2L
- const: renesas,rzg2l-wdt
- items: - items:
- enum: - enum:
...@@ -52,11 +59,11 @@ properties: ...@@ -52,11 +59,11 @@ properties:
- renesas,r8a77980-wdt # R-Car V3H - renesas,r8a77980-wdt # R-Car V3H
- renesas,r8a77990-wdt # R-Car E3 - renesas,r8a77990-wdt # R-Car E3
- renesas,r8a77995-wdt # R-Car D3 - renesas,r8a77995-wdt # R-Car D3
- renesas,r8a779a0-wdt # R-Car V3U
- const: renesas,rcar-gen3-wdt # R-Car Gen3 and RZ/G2 - const: renesas,rcar-gen3-wdt # R-Car Gen3 and RZ/G2
- items: - items:
- enum: - enum:
- renesas,r8a779a0-wdt # R-Car V3U
- renesas,r8a779f0-wdt # R-Car S4-8 - renesas,r8a779f0-wdt # R-Car S4-8
- const: renesas,rcar-gen4-wdt # R-Car Gen4 - const: renesas,rcar-gen4-wdt # R-Car Gen4
...@@ -94,6 +101,7 @@ allOf: ...@@ -94,6 +101,7 @@ allOf:
contains: contains:
enum: enum:
- renesas,rza-wdt - renesas,rza-wdt
- renesas,rzn1-wdt
then: then:
required: required:
- power-domains - power-domains
......
...@@ -19,7 +19,7 @@ properties: ...@@ -19,7 +19,7 @@ properties:
required: required:
- compatible - compatible
additionalProperties: false unevaluatedProperties: false
examples: examples:
- | - |
......
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
# Copyright (C) Sunplus Co., Ltd. 2021
%YAML 1.2
---
$id: http://devicetree.org/schemas/watchdog/sunplus,sp7021-wdt.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Sunplus SoCs Watchdog
maintainers:
- XianTao Hu <xt.hu@cqplus1.com>
allOf:
- $ref: watchdog.yaml#
properties:
compatible:
const: sunplus,sp7021-wdt
reg:
items:
- description: watchdog registers regions
- description: miscellaneous control registers regions
clocks:
maxItems: 1
resets:
maxItems: 1
required:
- compatible
- reg
- clocks
- resets
additionalProperties: false
examples:
- |
watchdog: watchdog@9c000630 {
compatible = "sunplus,sp7021-wdt";
reg = <0x9c000630 0x08>, <0x9c000274 0x04>;
clocks = <&clkc 0x24>;
resets = <&rstc 0x14>;
};
...
...@@ -19057,6 +19057,13 @@ S: Maintained ...@@ -19057,6 +19057,13 @@ S: Maintained
F: Documentation/devicetree/bindings/serial/sunplus,sp7021-uart.yaml F: Documentation/devicetree/bindings/serial/sunplus,sp7021-uart.yaml
F: drivers/tty/serial/sunplus-uart.c F: drivers/tty/serial/sunplus-uart.c
SUNPLUS WATCHDOG DRIVER
M: Xiantao Hu <xt.hu@cqplus1.com>
L: linux-watchdog@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/watchdog/sunplus,sp7021-wdt.yaml
F: drivers/watchdog/sunplus_wdt.c
SUPERH SUPERH
M: Yoshinori Sato <ysato@users.sourceforge.jp> M: Yoshinori Sato <ysato@users.sourceforge.jp>
M: Rich Felker <dalias@libc.org> M: Rich Felker <dalias@libc.org>
......
...@@ -883,6 +883,14 @@ config RENESAS_RZAWDT ...@@ -883,6 +883,14 @@ config RENESAS_RZAWDT
This driver adds watchdog support for the integrated watchdogs in the This driver adds watchdog support for the integrated watchdogs in the
Renesas RZ/A SoCs. These watchdogs can be used to reset a system. Renesas RZ/A SoCs. These watchdogs can be used to reset a system.
config RENESAS_RZN1WDT
tristate "Renesas RZ/N1 watchdog"
depends on ARCH_RENESAS || COMPILE_TEST
select WATCHDOG_CORE
help
This driver adds watchdog support for the integrated watchdogs in the
Renesas RZ/N1 SoCs. These watchdogs can be used to reset a system.
config RENESAS_RZG2LWDT config RENESAS_RZG2LWDT
tristate "Renesas RZ/G2L WDT Watchdog" tristate "Renesas RZ/G2L WDT Watchdog"
depends on ARCH_RENESAS || COMPILE_TEST depends on ARCH_RENESAS || COMPILE_TEST
...@@ -1011,6 +1019,17 @@ config APPLE_WATCHDOG ...@@ -1011,6 +1019,17 @@ config APPLE_WATCHDOG
To compile this driver as a module, choose M here: the To compile this driver as a module, choose M here: the
module will be called apple_wdt. module will be called apple_wdt.
config SUNPLUS_WATCHDOG
tristate "Sunplus watchdog support"
depends on ARCH_SUNPLUS || COMPILE_TEST
select WATCHDOG_CORE
help
Say Y here to include support for the watchdog timer
in Sunplus SoCs.
To compile this driver as a module, choose M here: the
module will be called sunplus_wdt.
# X86 (i386 + ia64 + x86_64) Architecture # X86 (i386 + ia64 + x86_64) Architecture
config ACQUIRE_WDT config ACQUIRE_WDT
......
...@@ -84,6 +84,7 @@ obj-$(CONFIG_LPC18XX_WATCHDOG) += lpc18xx_wdt.o ...@@ -84,6 +84,7 @@ obj-$(CONFIG_LPC18XX_WATCHDOG) += lpc18xx_wdt.o
obj-$(CONFIG_BCM7038_WDT) += bcm7038_wdt.o obj-$(CONFIG_BCM7038_WDT) += bcm7038_wdt.o
obj-$(CONFIG_RENESAS_WDT) += renesas_wdt.o obj-$(CONFIG_RENESAS_WDT) += renesas_wdt.o
obj-$(CONFIG_RENESAS_RZAWDT) += rza_wdt.o obj-$(CONFIG_RENESAS_RZAWDT) += rza_wdt.o
obj-$(CONFIG_RENESAS_RZN1WDT) += rzn1_wdt.o
obj-$(CONFIG_RENESAS_RZG2LWDT) += rzg2l_wdt.o obj-$(CONFIG_RENESAS_RZG2LWDT) += rzg2l_wdt.o
obj-$(CONFIG_ASPEED_WATCHDOG) += aspeed_wdt.o obj-$(CONFIG_ASPEED_WATCHDOG) += aspeed_wdt.o
obj-$(CONFIG_STM32_WATCHDOG) += stm32_iwdg.o obj-$(CONFIG_STM32_WATCHDOG) += stm32_iwdg.o
...@@ -95,6 +96,7 @@ obj-$(CONFIG_ARM_SMC_WATCHDOG) += arm_smc_wdt.o ...@@ -95,6 +96,7 @@ obj-$(CONFIG_ARM_SMC_WATCHDOG) += arm_smc_wdt.o
obj-$(CONFIG_VISCONTI_WATCHDOG) += visconti_wdt.o obj-$(CONFIG_VISCONTI_WATCHDOG) += visconti_wdt.o
obj-$(CONFIG_MSC313E_WATCHDOG) += msc313e_wdt.o obj-$(CONFIG_MSC313E_WATCHDOG) += msc313e_wdt.o
obj-$(CONFIG_APPLE_WATCHDOG) += apple_wdt.o obj-$(CONFIG_APPLE_WATCHDOG) += apple_wdt.o
obj-$(CONFIG_SUNPLUS_WATCHDOG) += sunplus_wdt.o
# X86 (i386 + ia64 + x86_64) Architecture # X86 (i386 + ia64 + x86_64) Architecture
obj-$(CONFIG_ACQUIRE_WDT) += acquirewdt.o obj-$(CONFIG_ACQUIRE_WDT) += acquirewdt.o
......
...@@ -218,6 +218,7 @@ static SIMPLE_DEV_PM_OPS(bcm7038_wdt_pm_ops, bcm7038_wdt_suspend, ...@@ -218,6 +218,7 @@ static SIMPLE_DEV_PM_OPS(bcm7038_wdt_pm_ops, bcm7038_wdt_suspend,
bcm7038_wdt_resume); bcm7038_wdt_resume);
static const struct of_device_id bcm7038_wdt_match[] = { static const struct of_device_id bcm7038_wdt_match[] = {
{ .compatible = "brcm,bcm6345-wdt" },
{ .compatible = "brcm,bcm7038-wdt" }, { .compatible = "brcm,bcm7038-wdt" },
{}, {},
}; };
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/mfd/da9063/registers.h> #include <linux/mfd/da9063/registers.h>
#include <linux/mfd/da9063/core.h> #include <linux/mfd/da9063/core.h>
#include <linux/property.h>
#include <linux/regmap.h> #include <linux/regmap.h>
/* /*
...@@ -26,6 +27,8 @@ ...@@ -26,6 +27,8 @@
* others: timeout = 2048 ms * 2^(TWDSCALE-1). * others: timeout = 2048 ms * 2^(TWDSCALE-1).
*/ */
static const unsigned int wdt_timeout[] = { 0, 2, 4, 8, 16, 32, 65, 131 }; static const unsigned int wdt_timeout[] = { 0, 2, 4, 8, 16, 32, 65, 131 };
static bool use_sw_pm;
#define DA9063_TWDSCALE_DISABLE 0 #define DA9063_TWDSCALE_DISABLE 0
#define DA9063_TWDSCALE_MIN 1 #define DA9063_TWDSCALE_MIN 1
#define DA9063_TWDSCALE_MAX (ARRAY_SIZE(wdt_timeout) - 1) #define DA9063_TWDSCALE_MAX (ARRAY_SIZE(wdt_timeout) - 1)
...@@ -218,6 +221,8 @@ static int da9063_wdt_probe(struct platform_device *pdev) ...@@ -218,6 +221,8 @@ static int da9063_wdt_probe(struct platform_device *pdev)
if (!wdd) if (!wdd)
return -ENOMEM; return -ENOMEM;
use_sw_pm = device_property_present(dev, "dlg,use-sw-pm");
wdd->info = &da9063_watchdog_info; wdd->info = &da9063_watchdog_info;
wdd->ops = &da9063_watchdog_ops; wdd->ops = &da9063_watchdog_ops;
wdd->min_timeout = DA9063_WDT_MIN_TIMEOUT; wdd->min_timeout = DA9063_WDT_MIN_TIMEOUT;
...@@ -228,6 +233,7 @@ static int da9063_wdt_probe(struct platform_device *pdev) ...@@ -228,6 +233,7 @@ static int da9063_wdt_probe(struct platform_device *pdev)
watchdog_set_restart_priority(wdd, 128); watchdog_set_restart_priority(wdd, 128);
watchdog_set_drvdata(wdd, da9063); watchdog_set_drvdata(wdd, da9063);
dev_set_drvdata(dev, wdd);
wdd->timeout = DA9063_WDG_TIMEOUT; wdd->timeout = DA9063_WDG_TIMEOUT;
...@@ -249,10 +255,40 @@ static int da9063_wdt_probe(struct platform_device *pdev) ...@@ -249,10 +255,40 @@ static int da9063_wdt_probe(struct platform_device *pdev)
return devm_watchdog_register_device(dev, wdd); return devm_watchdog_register_device(dev, wdd);
} }
static int __maybe_unused da9063_wdt_suspend(struct device *dev)
{
struct watchdog_device *wdd = dev_get_drvdata(dev);
if (!use_sw_pm)
return 0;
if (watchdog_active(wdd))
return da9063_wdt_stop(wdd);
return 0;
}
static int __maybe_unused da9063_wdt_resume(struct device *dev)
{
struct watchdog_device *wdd = dev_get_drvdata(dev);
if (!use_sw_pm)
return 0;
if (watchdog_active(wdd))
return da9063_wdt_start(wdd);
return 0;
}
static SIMPLE_DEV_PM_OPS(da9063_wdt_pm_ops,
da9063_wdt_suspend, da9063_wdt_resume);
static struct platform_driver da9063_wdt_driver = { static struct platform_driver da9063_wdt_driver = {
.probe = da9063_wdt_probe, .probe = da9063_wdt_probe,
.driver = { .driver = {
.name = DA9063_DRVNAME_WATCHDOG, .name = DA9063_DRVNAME_WATCHDOG,
.pm = &da9063_wdt_pm_ops,
}, },
}; };
module_platform_driver(da9063_wdt_driver); module_platform_driver(da9063_wdt_driver);
......
...@@ -596,7 +596,6 @@ static int iTCO_wdt_probe(struct platform_device *pdev) ...@@ -596,7 +596,6 @@ static int iTCO_wdt_probe(struct platform_device *pdev)
return 0; return 0;
} }
#ifdef CONFIG_PM_SLEEP
/* /*
* Suspend-to-idle requires this, because it stops the ticks and timekeeping, so * Suspend-to-idle requires this, because it stops the ticks and timekeeping, so
* the watchdog cannot be pinged while in that state. In ACPI sleep states the * the watchdog cannot be pinged while in that state. In ACPI sleep states the
...@@ -604,15 +603,15 @@ static int iTCO_wdt_probe(struct platform_device *pdev) ...@@ -604,15 +603,15 @@ static int iTCO_wdt_probe(struct platform_device *pdev)
*/ */
#ifdef CONFIG_ACPI #ifdef CONFIG_ACPI
static inline bool need_suspend(void) static inline bool __maybe_unused need_suspend(void)
{ {
return acpi_target_system_state() == ACPI_STATE_S0; return acpi_target_system_state() == ACPI_STATE_S0;
} }
#else #else
static inline bool need_suspend(void) { return true; } static inline bool __maybe_unused need_suspend(void) { return true; }
#endif #endif
static int iTCO_wdt_suspend_noirq(struct device *dev) static int __maybe_unused iTCO_wdt_suspend_noirq(struct device *dev)
{ {
struct iTCO_wdt_private *p = dev_get_drvdata(dev); struct iTCO_wdt_private *p = dev_get_drvdata(dev);
int ret = 0; int ret = 0;
...@@ -626,7 +625,7 @@ static int iTCO_wdt_suspend_noirq(struct device *dev) ...@@ -626,7 +625,7 @@ static int iTCO_wdt_suspend_noirq(struct device *dev)
return ret; return ret;
} }
static int iTCO_wdt_resume_noirq(struct device *dev) static int __maybe_unused iTCO_wdt_resume_noirq(struct device *dev)
{ {
struct iTCO_wdt_private *p = dev_get_drvdata(dev); struct iTCO_wdt_private *p = dev_get_drvdata(dev);
...@@ -637,20 +636,15 @@ static int iTCO_wdt_resume_noirq(struct device *dev) ...@@ -637,20 +636,15 @@ static int iTCO_wdt_resume_noirq(struct device *dev)
} }
static const struct dev_pm_ops iTCO_wdt_pm = { static const struct dev_pm_ops iTCO_wdt_pm = {
.suspend_noirq = iTCO_wdt_suspend_noirq, SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(iTCO_wdt_suspend_noirq,
.resume_noirq = iTCO_wdt_resume_noirq, iTCO_wdt_resume_noirq)
}; };
#define ITCO_WDT_PM_OPS (&iTCO_wdt_pm)
#else
#define ITCO_WDT_PM_OPS NULL
#endif /* CONFIG_PM_SLEEP */
static struct platform_driver iTCO_wdt_driver = { static struct platform_driver iTCO_wdt_driver = {
.probe = iTCO_wdt_probe, .probe = iTCO_wdt_probe,
.driver = { .driver = {
.name = DRV_NAME, .name = DRV_NAME,
.pm = ITCO_WDT_PM_OPS, .pm = &iTCO_wdt_pm,
}, },
}; };
......
...@@ -10,7 +10,9 @@ ...@@ -10,7 +10,9 @@
*/ */
#include <dt-bindings/reset/mt2712-resets.h> #include <dt-bindings/reset/mt2712-resets.h>
#include <dt-bindings/reset/mt7986-resets.h>
#include <dt-bindings/reset/mt8183-resets.h> #include <dt-bindings/reset/mt8183-resets.h>
#include <dt-bindings/reset/mt8186-resets.h>
#include <dt-bindings/reset/mt8192-resets.h> #include <dt-bindings/reset/mt8192-resets.h>
#include <dt-bindings/reset/mt8195-resets.h> #include <dt-bindings/reset/mt8195-resets.h>
#include <linux/delay.h> #include <linux/delay.h>
...@@ -76,10 +78,18 @@ static const struct mtk_wdt_data mt2712_data = { ...@@ -76,10 +78,18 @@ static const struct mtk_wdt_data mt2712_data = {
.toprgu_sw_rst_num = MT2712_TOPRGU_SW_RST_NUM, .toprgu_sw_rst_num = MT2712_TOPRGU_SW_RST_NUM,
}; };
static const struct mtk_wdt_data mt7986_data = {
.toprgu_sw_rst_num = MT7986_TOPRGU_SW_RST_NUM,
};
static const struct mtk_wdt_data mt8183_data = { static const struct mtk_wdt_data mt8183_data = {
.toprgu_sw_rst_num = MT8183_TOPRGU_SW_RST_NUM, .toprgu_sw_rst_num = MT8183_TOPRGU_SW_RST_NUM,
}; };
static const struct mtk_wdt_data mt8186_data = {
.toprgu_sw_rst_num = MT8186_TOPRGU_SW_RST_NUM,
};
static const struct mtk_wdt_data mt8192_data = { static const struct mtk_wdt_data mt8192_data = {
.toprgu_sw_rst_num = MT8192_TOPRGU_SW_RST_NUM, .toprgu_sw_rst_num = MT8192_TOPRGU_SW_RST_NUM,
}; };
...@@ -418,7 +428,9 @@ static int mtk_wdt_resume(struct device *dev) ...@@ -418,7 +428,9 @@ static int mtk_wdt_resume(struct device *dev)
static const struct of_device_id mtk_wdt_dt_ids[] = { static const struct of_device_id mtk_wdt_dt_ids[] = {
{ .compatible = "mediatek,mt2712-wdt", .data = &mt2712_data }, { .compatible = "mediatek,mt2712-wdt", .data = &mt2712_data },
{ .compatible = "mediatek,mt6589-wdt" }, { .compatible = "mediatek,mt6589-wdt" },
{ .compatible = "mediatek,mt7986-wdt", .data = &mt7986_data },
{ .compatible = "mediatek,mt8183-wdt", .data = &mt8183_data }, { .compatible = "mediatek,mt8183-wdt", .data = &mt8183_data },
{ .compatible = "mediatek,mt8186-wdt", .data = &mt8186_data },
{ .compatible = "mediatek,mt8192-wdt", .data = &mt8192_data }, { .compatible = "mediatek,mt8192-wdt", .data = &mt8192_data },
{ .compatible = "mediatek,mt8195-wdt", .data = &mt8195_data }, { .compatible = "mediatek,mt8195-wdt", .data = &mt8195_data },
{ /* sentinel */ } { /* sentinel */ }
......
...@@ -226,7 +226,7 @@ static int rti_wdt_probe(struct platform_device *pdev) ...@@ -226,7 +226,7 @@ static int rti_wdt_probe(struct platform_device *pdev)
pm_runtime_enable(dev); pm_runtime_enable(dev);
ret = pm_runtime_get_sync(dev); ret = pm_runtime_get_sync(dev);
if (ret) { if (ret < 0) {
pm_runtime_put_noidle(dev); pm_runtime_put_noidle(dev);
pm_runtime_disable(&pdev->dev); pm_runtime_disable(&pdev->dev);
return dev_err_probe(dev, ret, "runtime pm failed\n"); return dev_err_probe(dev, ret, "runtime pm failed\n");
...@@ -253,6 +253,7 @@ static int rti_wdt_probe(struct platform_device *pdev) ...@@ -253,6 +253,7 @@ static int rti_wdt_probe(struct platform_device *pdev)
} }
if (readl(wdt->base + RTIDWDCTRL) == WDENABLE_KEY) { if (readl(wdt->base + RTIDWDCTRL) == WDENABLE_KEY) {
int preset_heartbeat;
u32 time_left_ms; u32 time_left_ms;
u64 heartbeat_ms; u64 heartbeat_ms;
u32 wsize; u32 wsize;
...@@ -263,11 +264,12 @@ static int rti_wdt_probe(struct platform_device *pdev) ...@@ -263,11 +264,12 @@ static int rti_wdt_probe(struct platform_device *pdev)
heartbeat_ms <<= WDT_PRELOAD_SHIFT; heartbeat_ms <<= WDT_PRELOAD_SHIFT;
heartbeat_ms *= 1000; heartbeat_ms *= 1000;
do_div(heartbeat_ms, wdt->freq); do_div(heartbeat_ms, wdt->freq);
if (heartbeat_ms != heartbeat * 1000) preset_heartbeat = heartbeat_ms + 500;
preset_heartbeat /= 1000;
if (preset_heartbeat != heartbeat)
dev_warn(dev, "watchdog already running, ignoring heartbeat config!\n"); dev_warn(dev, "watchdog already running, ignoring heartbeat config!\n");
heartbeat = heartbeat_ms; heartbeat = preset_heartbeat;
heartbeat /= 1000;
wsize = readl(wdt->base + RTIWWDSIZECTRL); wsize = readl(wdt->base + RTIWWDSIZECTRL);
ret = rti_wdt_setup_hw_hb(wdd, wsize); ret = rti_wdt_setup_hw_hb(wdd, wsize);
......
...@@ -21,8 +21,11 @@ ...@@ -21,8 +21,11 @@
#define WDTSET 0x04 #define WDTSET 0x04
#define WDTTIM 0x08 #define WDTTIM 0x08
#define WDTINT 0x0C #define WDTINT 0x0C
#define PECR 0x10
#define PEEN 0x14
#define WDTCNT_WDTEN BIT(0) #define WDTCNT_WDTEN BIT(0)
#define WDTINT_INTDISP BIT(0) #define WDTINT_INTDISP BIT(0)
#define PEEN_FORCE BIT(0)
#define WDT_DEFAULT_TIMEOUT 60U #define WDT_DEFAULT_TIMEOUT 60U
...@@ -43,6 +46,8 @@ struct rzg2l_wdt_priv { ...@@ -43,6 +46,8 @@ struct rzg2l_wdt_priv {
struct reset_control *rstc; struct reset_control *rstc;
unsigned long osc_clk_rate; unsigned long osc_clk_rate;
unsigned long delay; unsigned long delay;
struct clk *pclk;
struct clk *osc_clk;
}; };
static void rzg2l_wdt_wait_delay(struct rzg2l_wdt_priv *priv) static void rzg2l_wdt_wait_delay(struct rzg2l_wdt_priv *priv)
...@@ -53,7 +58,7 @@ static void rzg2l_wdt_wait_delay(struct rzg2l_wdt_priv *priv) ...@@ -53,7 +58,7 @@ static void rzg2l_wdt_wait_delay(struct rzg2l_wdt_priv *priv)
static u32 rzg2l_wdt_get_cycle_usec(unsigned long cycle, u32 wdttime) static u32 rzg2l_wdt_get_cycle_usec(unsigned long cycle, u32 wdttime)
{ {
u64 timer_cycle_us = 1024 * 1024 * (wdttime + 1) * MICRO; u64 timer_cycle_us = 1024 * 1024ULL * (wdttime + 1) * MICRO;
return div64_ul(timer_cycle_us, cycle); return div64_ul(timer_cycle_us, cycle);
} }
...@@ -86,7 +91,6 @@ static int rzg2l_wdt_start(struct watchdog_device *wdev) ...@@ -86,7 +91,6 @@ static int rzg2l_wdt_start(struct watchdog_device *wdev)
{ {
struct rzg2l_wdt_priv *priv = watchdog_get_drvdata(wdev); struct rzg2l_wdt_priv *priv = watchdog_get_drvdata(wdev);
reset_control_deassert(priv->rstc);
pm_runtime_get_sync(wdev->parent); pm_runtime_get_sync(wdev->parent);
/* Initialize time out */ /* Initialize time out */
...@@ -106,7 +110,26 @@ static int rzg2l_wdt_stop(struct watchdog_device *wdev) ...@@ -106,7 +110,26 @@ static int rzg2l_wdt_stop(struct watchdog_device *wdev)
struct rzg2l_wdt_priv *priv = watchdog_get_drvdata(wdev); struct rzg2l_wdt_priv *priv = watchdog_get_drvdata(wdev);
pm_runtime_put(wdev->parent); pm_runtime_put(wdev->parent);
reset_control_assert(priv->rstc); reset_control_reset(priv->rstc);
return 0;
}
static int rzg2l_wdt_set_timeout(struct watchdog_device *wdev, unsigned int timeout)
{
struct rzg2l_wdt_priv *priv = watchdog_get_drvdata(wdev);
wdev->timeout = timeout;
/*
* If the watchdog is active, reset the module for updating the WDTSET
* register so that it is updated with new timeout values.
*/
if (watchdog_active(wdev)) {
pm_runtime_put(wdev->parent);
reset_control_reset(priv->rstc);
rzg2l_wdt_start(wdev);
}
return 0; return 0;
} }
...@@ -116,15 +139,14 @@ static int rzg2l_wdt_restart(struct watchdog_device *wdev, ...@@ -116,15 +139,14 @@ static int rzg2l_wdt_restart(struct watchdog_device *wdev,
{ {
struct rzg2l_wdt_priv *priv = watchdog_get_drvdata(wdev); struct rzg2l_wdt_priv *priv = watchdog_get_drvdata(wdev);
/* Reset the module before we modify any register */ clk_prepare_enable(priv->pclk);
reset_control_reset(priv->rstc); clk_prepare_enable(priv->osc_clk);
pm_runtime_get_sync(wdev->parent);
/* smallest counter value to reboot soon */ /* Generate Reset (WDTRSTB) Signal on parity error */
rzg2l_wdt_write(priv, WDTSET_COUNTER_VAL(1), WDTSET); rzg2l_wdt_write(priv, 0, PECR);
/* Enable watchdog timer*/ /* Force parity error */
rzg2l_wdt_write(priv, WDTCNT_WDTEN, WDTCNT); rzg2l_wdt_write(priv, PEEN_FORCE, PEEN);
return 0; return 0;
} }
...@@ -148,15 +170,15 @@ static const struct watchdog_ops rzg2l_wdt_ops = { ...@@ -148,15 +170,15 @@ static const struct watchdog_ops rzg2l_wdt_ops = {
.start = rzg2l_wdt_start, .start = rzg2l_wdt_start,
.stop = rzg2l_wdt_stop, .stop = rzg2l_wdt_stop,
.ping = rzg2l_wdt_ping, .ping = rzg2l_wdt_ping,
.set_timeout = rzg2l_wdt_set_timeout,
.restart = rzg2l_wdt_restart, .restart = rzg2l_wdt_restart,
}; };
static void rzg2l_wdt_reset_assert_pm_disable_put(void *data) static void rzg2l_wdt_reset_assert_pm_disable(void *data)
{ {
struct watchdog_device *wdev = data; struct watchdog_device *wdev = data;
struct rzg2l_wdt_priv *priv = watchdog_get_drvdata(wdev); struct rzg2l_wdt_priv *priv = watchdog_get_drvdata(wdev);
pm_runtime_put(wdev->parent);
pm_runtime_disable(wdev->parent); pm_runtime_disable(wdev->parent);
reset_control_assert(priv->rstc); reset_control_assert(priv->rstc);
} }
...@@ -166,7 +188,6 @@ static int rzg2l_wdt_probe(struct platform_device *pdev) ...@@ -166,7 +188,6 @@ static int rzg2l_wdt_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct rzg2l_wdt_priv *priv; struct rzg2l_wdt_priv *priv;
unsigned long pclk_rate; unsigned long pclk_rate;
struct clk *wdt_clk;
int ret; int ret;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
...@@ -178,22 +199,20 @@ static int rzg2l_wdt_probe(struct platform_device *pdev) ...@@ -178,22 +199,20 @@ static int rzg2l_wdt_probe(struct platform_device *pdev)
return PTR_ERR(priv->base); return PTR_ERR(priv->base);
/* Get watchdog main clock */ /* Get watchdog main clock */
wdt_clk = clk_get(&pdev->dev, "oscclk"); priv->osc_clk = devm_clk_get(&pdev->dev, "oscclk");
if (IS_ERR(wdt_clk)) if (IS_ERR(priv->osc_clk))
return dev_err_probe(&pdev->dev, PTR_ERR(wdt_clk), "no oscclk"); return dev_err_probe(&pdev->dev, PTR_ERR(priv->osc_clk), "no oscclk");
priv->osc_clk_rate = clk_get_rate(wdt_clk); priv->osc_clk_rate = clk_get_rate(priv->osc_clk);
clk_put(wdt_clk);
if (!priv->osc_clk_rate) if (!priv->osc_clk_rate)
return dev_err_probe(&pdev->dev, -EINVAL, "oscclk rate is 0"); return dev_err_probe(&pdev->dev, -EINVAL, "oscclk rate is 0");
/* Get Peripheral clock */ /* Get Peripheral clock */
wdt_clk = clk_get(&pdev->dev, "pclk"); priv->pclk = devm_clk_get(&pdev->dev, "pclk");
if (IS_ERR(wdt_clk)) if (IS_ERR(priv->pclk))
return dev_err_probe(&pdev->dev, PTR_ERR(wdt_clk), "no pclk"); return dev_err_probe(&pdev->dev, PTR_ERR(priv->pclk), "no pclk");
pclk_rate = clk_get_rate(wdt_clk); pclk_rate = clk_get_rate(priv->pclk);
clk_put(wdt_clk);
if (!pclk_rate) if (!pclk_rate)
return dev_err_probe(&pdev->dev, -EINVAL, "pclk rate is 0"); return dev_err_probe(&pdev->dev, -EINVAL, "pclk rate is 0");
...@@ -204,13 +223,11 @@ static int rzg2l_wdt_probe(struct platform_device *pdev) ...@@ -204,13 +223,11 @@ static int rzg2l_wdt_probe(struct platform_device *pdev)
return dev_err_probe(&pdev->dev, PTR_ERR(priv->rstc), return dev_err_probe(&pdev->dev, PTR_ERR(priv->rstc),
"failed to get cpg reset"); "failed to get cpg reset");
reset_control_deassert(priv->rstc); ret = reset_control_deassert(priv->rstc);
if (ret)
return dev_err_probe(dev, ret, "failed to deassert");
pm_runtime_enable(&pdev->dev); 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.info = &rzg2l_wdt_ident;
priv->wdev.ops = &rzg2l_wdt_ops; priv->wdev.ops = &rzg2l_wdt_ops;
...@@ -222,7 +239,7 @@ static int rzg2l_wdt_probe(struct platform_device *pdev) ...@@ -222,7 +239,7 @@ static int rzg2l_wdt_probe(struct platform_device *pdev)
watchdog_set_drvdata(&priv->wdev, priv); watchdog_set_drvdata(&priv->wdev, priv);
ret = devm_add_action_or_reset(&pdev->dev, ret = devm_add_action_or_reset(&pdev->dev,
rzg2l_wdt_reset_assert_pm_disable_put, rzg2l_wdt_reset_assert_pm_disable,
&priv->wdev); &priv->wdev);
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -235,12 +252,6 @@ static int rzg2l_wdt_probe(struct platform_device *pdev) ...@@ -235,12 +252,6 @@ static int rzg2l_wdt_probe(struct platform_device *pdev)
dev_warn(dev, "Specified timeout invalid, using default"); dev_warn(dev, "Specified timeout invalid, using default");
return devm_watchdog_register_device(&pdev->dev, &priv->wdev); 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[] = { static const struct of_device_id rzg2l_wdt_ids[] = {
......
// SPDX-License-Identifier: GPL-2.0
/*
* Renesas RZ/N1 Watchdog timer.
* This is a 12-bit timer driver from a (62.5/16384) MHz clock. It can't even
* cope with 2 seconds.
*
* Copyright 2018 Renesas Electronics Europe Ltd.
*
* Derived from Ralink RT288x watchdog timer.
*/
#include <linux/clk.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include <linux/reboot.h>
#include <linux/watchdog.h>
#define DEFAULT_TIMEOUT 60
#define RZN1_WDT_RETRIGGER 0x0
#define RZN1_WDT_RETRIGGER_RELOAD_VAL 0
#define RZN1_WDT_RETRIGGER_RELOAD_VAL_MASK 0xfff
#define RZN1_WDT_RETRIGGER_PRESCALE BIT(12)
#define RZN1_WDT_RETRIGGER_ENABLE BIT(13)
#define RZN1_WDT_RETRIGGER_WDSI (0x2 << 14)
#define RZN1_WDT_PRESCALER 16384
#define RZN1_WDT_MAX 4095
struct rzn1_watchdog {
struct watchdog_device wdtdev;
void __iomem *base;
unsigned long clk_rate_khz;
};
static inline uint32_t max_heart_beat_ms(unsigned long clk_rate_khz)
{
return (RZN1_WDT_MAX * RZN1_WDT_PRESCALER) / clk_rate_khz;
}
static inline uint32_t compute_reload_value(uint32_t tick_ms,
unsigned long clk_rate_khz)
{
return (tick_ms * clk_rate_khz) / RZN1_WDT_PRESCALER;
}
static int rzn1_wdt_ping(struct watchdog_device *w)
{
struct rzn1_watchdog *wdt = watchdog_get_drvdata(w);
/* Any value retrigggers the watchdog */
writel(0, wdt->base + RZN1_WDT_RETRIGGER);
return 0;
}
static int rzn1_wdt_start(struct watchdog_device *w)
{
struct rzn1_watchdog *wdt = watchdog_get_drvdata(w);
u32 val;
/*
* The hardware allows you to write to this reg only once.
* Since this includes the reload value, there is no way to change the
* timeout once started. Also note that the WDT clock is half the bus
* fabric clock rate, so if the bus fabric clock rate is changed after
* the WDT is started, the WDT interval will be wrong.
*/
val = RZN1_WDT_RETRIGGER_WDSI;
val |= RZN1_WDT_RETRIGGER_ENABLE;
val |= RZN1_WDT_RETRIGGER_PRESCALE;
val |= compute_reload_value(w->max_hw_heartbeat_ms, wdt->clk_rate_khz);
writel(val, wdt->base + RZN1_WDT_RETRIGGER);
return 0;
}
static irqreturn_t rzn1_wdt_irq(int irq, void *_wdt)
{
pr_crit("RZN1 Watchdog. Initiating system reboot\n");
emergency_restart();
return IRQ_HANDLED;
}
static struct watchdog_info rzn1_wdt_info = {
.identity = "RZ/N1 Watchdog",
.options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
};
static const struct watchdog_ops rzn1_wdt_ops = {
.owner = THIS_MODULE,
.start = rzn1_wdt_start,
.ping = rzn1_wdt_ping,
};
static void rzn1_wdt_clk_disable_unprepare(void *data)
{
clk_disable_unprepare(data);
}
static int rzn1_wdt_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct rzn1_watchdog *wdt;
struct device_node *np = dev->of_node;
struct clk *clk;
unsigned long clk_rate;
int ret;
int irq;
wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
if (!wdt)
return -ENOMEM;
wdt->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(wdt->base))
return PTR_ERR(wdt->base);
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
ret = devm_request_irq(dev, irq, rzn1_wdt_irq, 0,
np->name, wdt);
if (ret) {
dev_err(dev, "failed to request irq %d\n", irq);
return ret;
}
clk = devm_clk_get(dev, NULL);
if (IS_ERR(clk)) {
dev_err(dev, "failed to get the clock\n");
return PTR_ERR(clk);
}
ret = clk_prepare_enable(clk);
if (ret) {
dev_err(dev, "failed to prepare/enable the clock\n");
return ret;
}
ret = devm_add_action_or_reset(dev, rzn1_wdt_clk_disable_unprepare,
clk);
if (ret)
return ret;
clk_rate = clk_get_rate(clk);
if (!clk_rate) {
dev_err(dev, "failed to get the clock rate\n");
return -EINVAL;
}
wdt->clk_rate_khz = clk_rate / 1000;
wdt->wdtdev.info = &rzn1_wdt_info,
wdt->wdtdev.ops = &rzn1_wdt_ops,
wdt->wdtdev.status = WATCHDOG_NOWAYOUT_INIT_STATUS,
wdt->wdtdev.parent = dev;
/*
* The period of the watchdog cannot be changed once set
* and is limited to a very short period.
* Configure it for a 1s period once and for all, and
* rely on the heart-beat provided by the watchdog core
* to make this usable by the user-space.
*/
wdt->wdtdev.max_hw_heartbeat_ms = max_heart_beat_ms(wdt->clk_rate_khz);
if (wdt->wdtdev.max_hw_heartbeat_ms > 1000)
wdt->wdtdev.max_hw_heartbeat_ms = 1000;
wdt->wdtdev.timeout = DEFAULT_TIMEOUT;
ret = watchdog_init_timeout(&wdt->wdtdev, 0, dev);
if (ret)
return ret;
watchdog_set_drvdata(&wdt->wdtdev, wdt);
return devm_watchdog_register_device(dev, &wdt->wdtdev);
}
static const struct of_device_id rzn1_wdt_match[] = {
{ .compatible = "renesas,rzn1-wdt" },
{},
};
MODULE_DEVICE_TABLE(of, rzn1_wdt_match);
static struct platform_driver rzn1_wdt_driver = {
.probe = rzn1_wdt_probe,
.driver = {
.name = KBUILD_MODNAME,
.of_match_table = rzn1_wdt_match,
},
};
module_platform_driver(rzn1_wdt_driver);
MODULE_DESCRIPTION("Renesas RZ/N1 hardware watchdog");
MODULE_AUTHOR("Phil Edworthy <phil.edworthy@renesas.com>");
MODULE_LICENSE("GPL");
...@@ -272,6 +272,7 @@ sp805_wdt_probe(struct amba_device *adev, const struct amba_id *id) ...@@ -272,6 +272,7 @@ sp805_wdt_probe(struct amba_device *adev, const struct amba_id *id)
watchdog_set_nowayout(&wdt->wdd, nowayout); watchdog_set_nowayout(&wdt->wdd, nowayout);
watchdog_set_drvdata(&wdt->wdd, wdt); watchdog_set_drvdata(&wdt->wdd, wdt);
watchdog_set_restart_priority(&wdt->wdd, 128); watchdog_set_restart_priority(&wdt->wdd, 128);
watchdog_stop_on_unregister(&wdt->wdd);
/* /*
* If 'timeout-sec' devicetree property is specified, use that. * If 'timeout-sec' devicetree property is specified, use that.
......
// SPDX-License-Identifier: GPL-2.0-only
/*
* sunplus Watchdog Driver
*
* Copyright (C) 2021 Sunplus Technology Co., Ltd.
*
*/
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include <linux/reset.h>
#include <linux/watchdog.h>
#define WDT_CTRL 0x00
#define WDT_CNT 0x04
#define WDT_STOP 0x3877
#define WDT_RESUME 0x4A4B
#define WDT_CLRIRQ 0x7482
#define WDT_UNLOCK 0xAB00
#define WDT_LOCK 0xAB01
#define WDT_CONMAX 0xDEAF
/* TIMEOUT_MAX = ffff0/90kHz =11.65, so longer than 11 seconds will time out. */
#define SP_WDT_MAX_TIMEOUT 11U
#define SP_WDT_DEFAULT_TIMEOUT 10
#define STC_CLK 90000
#define DEVICE_NAME "sunplus-wdt"
static unsigned int timeout;
module_param(timeout, int, 0);
MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds");
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 sp_wdt_priv {
struct watchdog_device wdev;
void __iomem *base;
struct clk *clk;
struct reset_control *rstc;
};
static int sp_wdt_restart(struct watchdog_device *wdev,
unsigned long action, void *data)
{
struct sp_wdt_priv *priv = watchdog_get_drvdata(wdev);
void __iomem *base = priv->base;
writel(WDT_STOP, base + WDT_CTRL);
writel(WDT_UNLOCK, base + WDT_CTRL);
writel(0x0001, base + WDT_CNT);
writel(WDT_LOCK, base + WDT_CTRL);
writel(WDT_RESUME, base + WDT_CTRL);
return 0;
}
static int sp_wdt_ping(struct watchdog_device *wdev)
{
struct sp_wdt_priv *priv = watchdog_get_drvdata(wdev);
void __iomem *base = priv->base;
u32 count;
if (wdev->timeout > SP_WDT_MAX_TIMEOUT) {
/* WDT_CONMAX sets the count to the maximum (down-counting). */
writel(WDT_CONMAX, base + WDT_CTRL);
} else {
writel(WDT_UNLOCK, base + WDT_CTRL);
/*
* Watchdog timer is a 20-bit down-counting based on STC_CLK.
* This register bits[16:0] is from bit[19:4] of the watchdog
* timer counter.
*/
count = (wdev->timeout * STC_CLK) >> 4;
writel(count, base + WDT_CNT);
writel(WDT_LOCK, base + WDT_CTRL);
}
return 0;
}
static int sp_wdt_stop(struct watchdog_device *wdev)
{
struct sp_wdt_priv *priv = watchdog_get_drvdata(wdev);
void __iomem *base = priv->base;
writel(WDT_STOP, base + WDT_CTRL);
return 0;
}
static int sp_wdt_start(struct watchdog_device *wdev)
{
struct sp_wdt_priv *priv = watchdog_get_drvdata(wdev);
void __iomem *base = priv->base;
writel(WDT_RESUME, base + WDT_CTRL);
return 0;
}
static unsigned int sp_wdt_get_timeleft(struct watchdog_device *wdev)
{
struct sp_wdt_priv *priv = watchdog_get_drvdata(wdev);
void __iomem *base = priv->base;
u32 val;
val = readl(base + WDT_CNT);
val &= 0xffff;
val = val << 4;
return val;
}
static const struct watchdog_info sp_wdt_info = {
.identity = DEVICE_NAME,
.options = WDIOF_SETTIMEOUT |
WDIOF_MAGICCLOSE |
WDIOF_KEEPALIVEPING,
};
static const struct watchdog_ops sp_wdt_ops = {
.owner = THIS_MODULE,
.start = sp_wdt_start,
.stop = sp_wdt_stop,
.ping = sp_wdt_ping,
.get_timeleft = sp_wdt_get_timeleft,
.restart = sp_wdt_restart,
};
static void sp_clk_disable_unprepare(void *data)
{
clk_disable_unprepare(data);
}
static void sp_reset_control_assert(void *data)
{
reset_control_assert(data);
}
static int sp_wdt_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct sp_wdt_priv *priv;
int ret;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->clk = devm_clk_get(dev, NULL);
if (IS_ERR(priv->clk))
return dev_err_probe(dev, PTR_ERR(priv->clk), "Failed to get clock\n");
ret = clk_prepare_enable(priv->clk);
if (ret)
return dev_err_probe(dev, ret, "Failed to enable clock\n");
ret = devm_add_action_or_reset(dev, sp_clk_disable_unprepare, priv->clk);
if (ret)
return ret;
/* The timer and watchdog shared the STC reset */
priv->rstc = devm_reset_control_get_shared(dev, NULL);
if (IS_ERR(priv->rstc))
return dev_err_probe(dev, PTR_ERR(priv->rstc), "Failed to get reset\n");
reset_control_deassert(priv->rstc);
ret = devm_add_action_or_reset(dev, sp_reset_control_assert, priv->rstc);
if (ret)
return ret;
priv->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(priv->base))
return PTR_ERR(priv->base);
priv->wdev.info = &sp_wdt_info;
priv->wdev.ops = &sp_wdt_ops;
priv->wdev.timeout = SP_WDT_DEFAULT_TIMEOUT;
priv->wdev.max_hw_heartbeat_ms = SP_WDT_MAX_TIMEOUT * 1000;
priv->wdev.min_timeout = 1;
priv->wdev.parent = dev;
watchdog_set_drvdata(&priv->wdev, priv);
watchdog_init_timeout(&priv->wdev, timeout, dev);
watchdog_set_nowayout(&priv->wdev, nowayout);
watchdog_stop_on_reboot(&priv->wdev);
watchdog_set_restart_priority(&priv->wdev, 128);
return devm_watchdog_register_device(dev, &priv->wdev);
}
static const struct of_device_id sp_wdt_of_match[] = {
{.compatible = "sunplus,sp7021-wdt", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, sp_wdt_of_match);
static struct platform_driver sp_wdt_driver = {
.probe = sp_wdt_probe,
.driver = {
.name = DEVICE_NAME,
.of_match_table = sp_wdt_of_match,
},
};
module_platform_driver(sp_wdt_driver);
MODULE_AUTHOR("Xiantao Hu <xt.hu@cqplus1.com>");
MODULE_DESCRIPTION("Sunplus Watchdog Timer Driver");
MODULE_LICENSE("GPL");
...@@ -125,13 +125,16 @@ static int ts4800_wdt_probe(struct platform_device *pdev) ...@@ -125,13 +125,16 @@ static int ts4800_wdt_probe(struct platform_device *pdev)
ret = of_property_read_u32_index(np, "syscon", 1, &reg); ret = of_property_read_u32_index(np, "syscon", 1, &reg);
if (ret < 0) { if (ret < 0) {
dev_err(dev, "no offset in syscon\n"); dev_err(dev, "no offset in syscon\n");
of_node_put(syscon_np);
return ret; return ret;
} }
/* allocate memory for watchdog struct */ /* allocate memory for watchdog struct */
wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL); wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
if (!wdt) if (!wdt) {
of_node_put(syscon_np);
return -ENOMEM; return -ENOMEM;
}
/* set regmap and offset to know where to write */ /* set regmap and offset to know where to write */
wdt->feed_offset = reg; wdt->feed_offset = reg;
......
...@@ -344,6 +344,7 @@ static int wdat_wdt_probe(struct platform_device *pdev) ...@@ -344,6 +344,7 @@ static int wdat_wdt_probe(struct platform_device *pdev)
wdat->period = tbl->timer_period; wdat->period = tbl->timer_period;
wdat->wdd.min_hw_heartbeat_ms = wdat->period * tbl->min_count; wdat->wdd.min_hw_heartbeat_ms = wdat->period * tbl->min_count;
wdat->wdd.max_hw_heartbeat_ms = wdat->period * tbl->max_count; wdat->wdd.max_hw_heartbeat_ms = wdat->period * tbl->max_count;
wdat->wdd.min_timeout = 1;
wdat->stopped_in_sleep = tbl->flags & ACPI_WDAT_STOPPED; wdat->stopped_in_sleep = tbl->flags & ACPI_WDAT_STOPPED;
wdat->wdd.info = &wdat_wdt_info; wdat->wdd.info = &wdat_wdt_info;
wdat->wdd.ops = &wdat_wdt_ops; wdat->wdd.ops = &wdat_wdt_ops;
...@@ -450,8 +451,7 @@ static int wdat_wdt_probe(struct platform_device *pdev) ...@@ -450,8 +451,7 @@ static int wdat_wdt_probe(struct platform_device *pdev)
* watchdog properly after it has opened the device. In some cases * watchdog properly after it has opened the device. In some cases
* the BIOS default is too short and causes immediate reboot. * the BIOS default is too short and causes immediate reboot.
*/ */
if (timeout * 1000 < wdat->wdd.min_hw_heartbeat_ms || if (watchdog_timeout_invalid(&wdat->wdd, timeout)) {
timeout * 1000 > wdat->wdd.max_hw_heartbeat_ms) {
dev_warn(dev, "Invalid timeout %d given, using %d\n", dev_warn(dev, "Invalid timeout %d given, using %d\n",
timeout, WDAT_DEFAULT_TIMEOUT); timeout, WDAT_DEFAULT_TIMEOUT);
timeout = WDAT_DEFAULT_TIMEOUT; timeout = WDAT_DEFAULT_TIMEOUT;
...@@ -462,6 +462,8 @@ static int wdat_wdt_probe(struct platform_device *pdev) ...@@ -462,6 +462,8 @@ static int wdat_wdt_probe(struct platform_device *pdev)
return ret; return ret;
watchdog_set_nowayout(&wdat->wdd, nowayout); watchdog_set_nowayout(&wdat->wdd, nowayout);
watchdog_stop_on_reboot(&wdat->wdd);
watchdog_stop_on_unregister(&wdat->wdd);
return devm_watchdog_register_device(dev, &wdat->wdd); return devm_watchdog_register_device(dev, &wdat->wdd);
} }
......
/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */
/*
* Copyright (c) 2022 MediaTek Inc.
* Author: Sam Shih <sam.shih@mediatek.com>
*/
#ifndef _DT_BINDINGS_RESET_CONTROLLER_MT7986
#define _DT_BINDINGS_RESET_CONTROLLER_MT7986
/* INFRACFG resets */
#define MT7986_INFRACFG_PEXTP_MAC_SW_RST 6
#define MT7986_INFRACFG_SSUSB_SW_RST 7
#define MT7986_INFRACFG_EIP97_SW_RST 8
#define MT7986_INFRACFG_AUDIO_SW_RST 13
#define MT7986_INFRACFG_CQ_DMA_SW_RST 14
#define MT7986_INFRACFG_TRNG_SW_RST 17
#define MT7986_INFRACFG_AP_DMA_SW_RST 32
#define MT7986_INFRACFG_I2C_SW_RST 33
#define MT7986_INFRACFG_NFI_SW_RST 34
#define MT7986_INFRACFG_SPI0_SW_RST 35
#define MT7986_INFRACFG_SPI1_SW_RST 36
#define MT7986_INFRACFG_UART0_SW_RST 37
#define MT7986_INFRACFG_UART1_SW_RST 38
#define MT7986_INFRACFG_UART2_SW_RST 39
#define MT7986_INFRACFG_AUXADC_SW_RST 43
#define MT7986_INFRACFG_APXGPT_SW_RST 66
#define MT7986_INFRACFG_PWM_SW_RST 68
#define MT7986_INFRACFG_SW_RST_NUM 69
/* TOPRGU resets */
#define MT7986_TOPRGU_APMIXEDSYS_SW_RST 0
#define MT7986_TOPRGU_SGMII0_SW_RST 1
#define MT7986_TOPRGU_SGMII1_SW_RST 2
#define MT7986_TOPRGU_INFRA_SW_RST 3
#define MT7986_TOPRGU_U2PHY_SW_RST 5
#define MT7986_TOPRGU_PCIE_SW_RST 6
#define MT7986_TOPRGU_SSUSB_SW_RST 7
#define MT7986_TOPRGU_ETHDMA_SW_RST 20
#define MT7986_TOPRGU_CONSYS_SW_RST 23
#define MT7986_TOPRGU_SW_RST_NUM 24
/* ETHSYS Subsystem resets */
#define MT7986_ETHSYS_FE_SW_RST 6
#define MT7986_ETHSYS_PMTR_SW_RST 8
#define MT7986_ETHSYS_GMAC_SW_RST 23
#define MT7986_ETHSYS_PPE0_SW_RST 30
#define MT7986_ETHSYS_PPE1_SW_RST 31
#define MT7986_ETHSYS_SW_RST_NUM 32
#endif /* _DT_BINDINGS_RESET_CONTROLLER_MT7986 */
/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */
/*
* Copyright (c) 2022 MediaTek Inc.
* Author: Runyang Chen <runyang.chen@mediatek.com>
*/
#ifndef _DT_BINDINGS_RESET_CONTROLLER_MT8186
#define _DT_BINDINGS_RESET_CONTROLLER_MT8186
#define MT8186_TOPRGU_INFRA_SW_RST 0
#define MT8186_TOPRGU_MM_SW_RST 1
#define MT8186_TOPRGU_MFG_SW_RST 2
#define MT8186_TOPRGU_VENC_SW_RST 3
#define MT8186_TOPRGU_VDEC_SW_RST 4
#define MT8186_TOPRGU_IMG_SW_RST 5
#define MT8186_TOPRGU_DDR_SW_RST 6
#define MT8186_TOPRGU_INFRA_AO_SW_RST 8
#define MT8186_TOPRGU_CONNSYS_SW_RST 9
#define MT8186_TOPRGU_APMIXED_SW_RST 10
#define MT8186_TOPRGU_PWRAP_SW_RST 11
#define MT8186_TOPRGU_CONN_MCU_SW_RST 12
#define MT8186_TOPRGU_IPNNA_SW_RST 13
#define MT8186_TOPRGU_WPE_SW_RST 14
#define MT8186_TOPRGU_ADSP_SW_RST 15
#define MT8186_TOPRGU_AUDIO_SW_RST 17
#define MT8186_TOPRGU_CAM_MAIN_SW_RST 18
#define MT8186_TOPRGU_CAM_RAWA_SW_RST 19
#define MT8186_TOPRGU_CAM_RAWB_SW_RST 20
#define MT8186_TOPRGU_IPE_SW_RST 21
#define MT8186_TOPRGU_IMG2_SW_RST 22
#define MT8186_TOPRGU_SW_RST_NUM 23
/* MMSYS resets */
#define MT8186_MMSYS_SW0_RST_B_DISP_DSI0 19
#endif /* _DT_BINDINGS_RESET_CONTROLLER_MT8186 */
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册