mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-01-16 01:54:00 +00:00
linux-watchdog 6.13-rc1 tag
-----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.14 (GNU/Linux) iEYEABECAAYFAmdO8FgACgkQ+iyteGJfRsqdmgCgqPArbZweV/LTPcZXx89MaIEd hG8AoLrh8n5L08d6r5v1sII5To4KoZI7 =IX0/ -----END PGP SIGNATURE----- Merge tag 'linux-watchdog-6.13-rc1' of git://www.linux-watchdog.org/linux-watchdog Pull watchdog updates from Wim Van Sebroeck: - Add support for exynosautov920 SoC - Add support for Airoha EN7851 watchdog - Add support for MT6735 TOPRGU/WDT - Delete the cpu5wdt driver - Always print when registering watchdog fails - Several other small fixes and improvements * tag 'linux-watchdog-6.13-rc1' of git://www.linux-watchdog.org/linux-watchdog: (36 commits) watchdog: rti: of: honor timeout-sec property watchdog: s3c2410_wdt: add support for exynosautov920 SoC dt-bindings: watchdog: Document ExynosAutoV920 watchdog bindings watchdog: mediatek: Add support for MT6735 TOPRGU/WDT watchdog: mediatek: Make sure system reset gets asserted in mtk_wdt_restart() dt-bindings: watchdog: fsl-imx-wdt: Add missing 'big-endian' property dt-bindings: watchdog: Document Qualcomm QCS8300 docs: ABI: Fix spelling mistake in pretimeout_avaialable_governors Revert "watchdog: s3c2410_wdt: use exynos_get_pmu_regmap_by_phandle() for PMU regs" watchdog: rzg2l_wdt: Power on the watchdog domain in the restart handler watchdog: Switch back to struct platform_driver::remove() watchdog: it87_wdt: add PWRGD enable quirk for Qotom QCML04 watchdog: da9063: Remove __maybe_unused notations watchdog: da9063: Do not use a global variable watchdog: Delete the cpu5wdt driver watchdog: Add support for Airoha EN7851 watchdog dt-bindings: watchdog: airoha: document watchdog for Airoha EN7581 watchdog: sl28cpld_wdt: don't print out if registering watchdog fails watchdog: rza_wdt: don't print out if registering watchdog fails watchdog: rti_wdt: don't print out if registering watchdog fails ...
This commit is contained in:
commit
42d52acfb1
@ -76,7 +76,7 @@ Description:
|
|||||||
timeout when the pretimeout interrupt is delivered. Pretimeout
|
timeout when the pretimeout interrupt is delivered. Pretimeout
|
||||||
is an optional feature.
|
is an optional feature.
|
||||||
|
|
||||||
What: /sys/class/watchdog/watchdogn/pretimeout_avaialable_governors
|
What: /sys/class/watchdog/watchdogn/pretimeout_available_governors
|
||||||
Date: February 2017
|
Date: February 2017
|
||||||
Contact: Wim Van Sebroeck <wim@iguana.be>
|
Contact: Wim Van Sebroeck <wim@iguana.be>
|
||||||
Description:
|
Description:
|
||||||
|
@ -0,0 +1,47 @@
|
|||||||
|
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||||
|
%YAML 1.2
|
||||||
|
---
|
||||||
|
$id: http://devicetree.org/schemas/watchdog/airoha,en7581-wdt.yaml#
|
||||||
|
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||||
|
|
||||||
|
title: Airoha EN7581 Watchdog Timer
|
||||||
|
|
||||||
|
maintainers:
|
||||||
|
- Christian Marangi <ansuelsmth@gmail.com>
|
||||||
|
|
||||||
|
allOf:
|
||||||
|
- $ref: watchdog.yaml#
|
||||||
|
|
||||||
|
properties:
|
||||||
|
compatible:
|
||||||
|
const: airoha,en7581-wdt
|
||||||
|
|
||||||
|
reg:
|
||||||
|
maxItems: 1
|
||||||
|
|
||||||
|
clocks:
|
||||||
|
description: BUS clock (timer ticks at half the BUS clock)
|
||||||
|
maxItems: 1
|
||||||
|
|
||||||
|
clock-names:
|
||||||
|
const: bus
|
||||||
|
|
||||||
|
required:
|
||||||
|
- compatible
|
||||||
|
- reg
|
||||||
|
- clocks
|
||||||
|
- clock-names
|
||||||
|
|
||||||
|
unevaluatedProperties: false
|
||||||
|
|
||||||
|
examples:
|
||||||
|
- |
|
||||||
|
#include <dt-bindings/clock/en7523-clk.h>
|
||||||
|
|
||||||
|
watchdog@1fbf0100 {
|
||||||
|
compatible = "airoha,en7581-wdt";
|
||||||
|
reg = <0x1fbf0100 0x3c>;
|
||||||
|
|
||||||
|
clocks = <&scuclk EN7523_CLK_BUS>;
|
||||||
|
clock-names = "bus";
|
||||||
|
};
|
@ -48,6 +48,8 @@ properties:
|
|||||||
clocks:
|
clocks:
|
||||||
maxItems: 1
|
maxItems: 1
|
||||||
|
|
||||||
|
big-endian: true
|
||||||
|
|
||||||
fsl,ext-reset-output:
|
fsl,ext-reset-output:
|
||||||
$ref: /schemas/types.yaml#/definitions/flag
|
$ref: /schemas/types.yaml#/definitions/flag
|
||||||
description: |
|
description: |
|
||||||
@ -93,6 +95,18 @@ allOf:
|
|||||||
properties:
|
properties:
|
||||||
fsl,suspend-in-wait: false
|
fsl,suspend-in-wait: false
|
||||||
|
|
||||||
|
- if:
|
||||||
|
not:
|
||||||
|
properties:
|
||||||
|
compatible:
|
||||||
|
contains:
|
||||||
|
enum:
|
||||||
|
- fsl,ls1012a-wdt
|
||||||
|
- fsl,ls1043a-wdt
|
||||||
|
then:
|
||||||
|
properties:
|
||||||
|
big-endian: false
|
||||||
|
|
||||||
unevaluatedProperties: false
|
unevaluatedProperties: false
|
||||||
|
|
||||||
examples:
|
examples:
|
||||||
|
@ -26,6 +26,8 @@ properties:
|
|||||||
- qcom,apss-wdt-msm8994
|
- qcom,apss-wdt-msm8994
|
||||||
- qcom,apss-wdt-qcm2290
|
- qcom,apss-wdt-qcm2290
|
||||||
- qcom,apss-wdt-qcs404
|
- qcom,apss-wdt-qcs404
|
||||||
|
- qcom,apss-wdt-qcs615
|
||||||
|
- qcom,apss-wdt-qcs8300
|
||||||
- qcom,apss-wdt-sa8255p
|
- qcom,apss-wdt-sa8255p
|
||||||
- qcom,apss-wdt-sa8775p
|
- qcom,apss-wdt-sa8775p
|
||||||
- qcom,apss-wdt-sc7180
|
- qcom,apss-wdt-sc7180
|
||||||
|
@ -26,6 +26,7 @@ properties:
|
|||||||
- samsung,exynos7-wdt # for Exynos7
|
- samsung,exynos7-wdt # for Exynos7
|
||||||
- samsung,exynos850-wdt # for Exynos850
|
- samsung,exynos850-wdt # for Exynos850
|
||||||
- samsung,exynosautov9-wdt # for Exynosautov9
|
- samsung,exynosautov9-wdt # for Exynosautov9
|
||||||
|
- samsung,exynosautov920-wdt # for Exynosautov920
|
||||||
- items:
|
- items:
|
||||||
- enum:
|
- enum:
|
||||||
- tesla,fsd-wdt
|
- tesla,fsd-wdt
|
||||||
@ -77,6 +78,7 @@ allOf:
|
|||||||
- samsung,exynos7-wdt
|
- samsung,exynos7-wdt
|
||||||
- samsung,exynos850-wdt
|
- samsung,exynos850-wdt
|
||||||
- samsung,exynosautov9-wdt
|
- samsung,exynosautov9-wdt
|
||||||
|
- samsung,exynosautov920-wdt
|
||||||
then:
|
then:
|
||||||
required:
|
required:
|
||||||
- samsung,syscon-phandle
|
- samsung,syscon-phandle
|
||||||
@ -88,6 +90,7 @@ allOf:
|
|||||||
- google,gs101-wdt
|
- google,gs101-wdt
|
||||||
- samsung,exynos850-wdt
|
- samsung,exynos850-wdt
|
||||||
- samsung,exynosautov9-wdt
|
- samsung,exynosautov9-wdt
|
||||||
|
- samsung,exynosautov920-wdt
|
||||||
then:
|
then:
|
||||||
properties:
|
properties:
|
||||||
clocks:
|
clocks:
|
||||||
|
@ -120,16 +120,6 @@ coh901327_wdt:
|
|||||||
|
|
||||||
-------------------------------------------------
|
-------------------------------------------------
|
||||||
|
|
||||||
cpu5wdt:
|
|
||||||
port:
|
|
||||||
base address of watchdog card, default is 0x91
|
|
||||||
verbose:
|
|
||||||
be verbose, default is 0 (no)
|
|
||||||
ticks:
|
|
||||||
count down ticks, default is 10000
|
|
||||||
|
|
||||||
-------------------------------------------------
|
|
||||||
|
|
||||||
cpwd:
|
cpwd:
|
||||||
wd0_timeout:
|
wd0_timeout:
|
||||||
Default watchdog0 timeout in 1/10secs
|
Default watchdog0 timeout in 1/10secs
|
||||||
|
@ -22407,7 +22407,7 @@ F: drivers/char/hw_random/jh7110-trng.c
|
|||||||
|
|
||||||
STARFIVE WATCHDOG DRIVER
|
STARFIVE WATCHDOG DRIVER
|
||||||
M: Xingyu Wu <xingyu.wu@starfivetech.com>
|
M: Xingyu Wu <xingyu.wu@starfivetech.com>
|
||||||
M: Samin Guo <samin.guo@starfivetech.com>
|
M: Ziv Xu <ziv.xu@starfivetech.com>
|
||||||
S: Supported
|
S: Supported
|
||||||
F: Documentation/devicetree/bindings/watchdog/starfive*
|
F: Documentation/devicetree/bindings/watchdog/starfive*
|
||||||
F: drivers/watchdog/starfive-wdt.c
|
F: drivers/watchdog/starfive-wdt.c
|
||||||
|
@ -408,6 +408,14 @@ config SL28CPLD_WATCHDOG
|
|||||||
|
|
||||||
# ARM Architecture
|
# ARM Architecture
|
||||||
|
|
||||||
|
config AIROHA_WATCHDOG
|
||||||
|
tristate "Airoha EN7581 Watchdog"
|
||||||
|
depends on ARCH_AIROHA || COMPILE_TEST
|
||||||
|
select WATCHDOG_CORE
|
||||||
|
help
|
||||||
|
Watchdog timer embedded into Airoha SoC. This will reboot your
|
||||||
|
system when the timeout is reached.
|
||||||
|
|
||||||
config ARM_SP805_WATCHDOG
|
config ARM_SP805_WATCHDOG
|
||||||
tristate "ARM SP805 Watchdog"
|
tristate "ARM SP805 Watchdog"
|
||||||
depends on (ARM || ARM64 || COMPILE_TEST) && ARM_AMBA
|
depends on (ARM || ARM64 || COMPILE_TEST) && ARM_AMBA
|
||||||
@ -549,6 +557,7 @@ config S3C2410_WATCHDOG
|
|||||||
tristate "S3C6410/S5Pv210/Exynos Watchdog"
|
tristate "S3C6410/S5Pv210/Exynos Watchdog"
|
||||||
depends on ARCH_S3C64XX || ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST
|
depends on ARCH_S3C64XX || ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST
|
||||||
select WATCHDOG_CORE
|
select WATCHDOG_CORE
|
||||||
|
select MFD_SYSCON if ARCH_EXYNOS
|
||||||
help
|
help
|
||||||
Watchdog timer block in the Samsung S3C64xx, S5Pv210 and Exynos
|
Watchdog timer block in the Samsung S3C64xx, S5Pv210 and Exynos
|
||||||
SoCs. This will reboot the system when the timer expires with
|
SoCs. This will reboot the system when the timer expires with
|
||||||
@ -1543,14 +1552,6 @@ config SBC7240_WDT
|
|||||||
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 sbc7240_wdt.
|
module will be called sbc7240_wdt.
|
||||||
|
|
||||||
config CPU5_WDT
|
|
||||||
tristate "SMA CPU5 Watchdog"
|
|
||||||
depends on (X86 || COMPILE_TEST) && HAS_IOPORT
|
|
||||||
help
|
|
||||||
TBD.
|
|
||||||
To compile this driver as a module, choose M here: the
|
|
||||||
module will be called cpu5wdt.
|
|
||||||
|
|
||||||
config SMSC_SCH311X_WDT
|
config SMSC_SCH311X_WDT
|
||||||
tristate "SMSC SCH311X Watchdog Timer"
|
tristate "SMSC SCH311X Watchdog Timer"
|
||||||
depends on (X86 || COMPILE_TEST) && HAS_IOPORT
|
depends on (X86 || COMPILE_TEST) && HAS_IOPORT
|
||||||
|
@ -40,6 +40,7 @@ obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb.o
|
|||||||
obj-$(CONFIG_ARM_SP805_WATCHDOG) += sp805_wdt.o
|
obj-$(CONFIG_ARM_SP805_WATCHDOG) += sp805_wdt.o
|
||||||
obj-$(CONFIG_ARM_SBSA_WATCHDOG) += sbsa_gwdt.o
|
obj-$(CONFIG_ARM_SBSA_WATCHDOG) += sbsa_gwdt.o
|
||||||
obj-$(CONFIG_ARMADA_37XX_WATCHDOG) += armada_37xx_wdt.o
|
obj-$(CONFIG_ARMADA_37XX_WATCHDOG) += armada_37xx_wdt.o
|
||||||
|
obj-$(CONFIG_AIROHA_WATCHDOG) += airoha_wdt.o
|
||||||
obj-$(CONFIG_ASM9260_WATCHDOG) += asm9260_wdt.o
|
obj-$(CONFIG_ASM9260_WATCHDOG) += asm9260_wdt.o
|
||||||
obj-$(CONFIG_AT91RM9200_WATCHDOG) += at91rm9200_wdt.o
|
obj-$(CONFIG_AT91RM9200_WATCHDOG) += at91rm9200_wdt.o
|
||||||
obj-$(CONFIG_AT91SAM9X_WATCHDOG) += at91sam9_wdt.o
|
obj-$(CONFIG_AT91SAM9X_WATCHDOG) += at91sam9_wdt.o
|
||||||
@ -138,7 +139,6 @@ obj-$(CONFIG_RDC321X_WDT) += rdc321x_wdt.o
|
|||||||
obj-$(CONFIG_60XX_WDT) += sbc60xxwdt.o
|
obj-$(CONFIG_60XX_WDT) += sbc60xxwdt.o
|
||||||
obj-$(CONFIG_SBC8360_WDT) += sbc8360.o
|
obj-$(CONFIG_SBC8360_WDT) += sbc8360.o
|
||||||
obj-$(CONFIG_SBC7240_WDT) += sbc7240_wdt.o
|
obj-$(CONFIG_SBC7240_WDT) += sbc7240_wdt.o
|
||||||
obj-$(CONFIG_CPU5_WDT) += cpu5wdt.o
|
|
||||||
obj-$(CONFIG_SMSC_SCH311X_WDT) += sch311x_wdt.o
|
obj-$(CONFIG_SMSC_SCH311X_WDT) += sch311x_wdt.o
|
||||||
obj-$(CONFIG_SMSC37B787_WDT) += smsc37b787_wdt.o
|
obj-$(CONFIG_SMSC37B787_WDT) += smsc37b787_wdt.o
|
||||||
obj-$(CONFIG_TQMX86_WDT) += tqmx86_wdt.o
|
obj-$(CONFIG_TQMX86_WDT) += tqmx86_wdt.o
|
||||||
|
216
drivers/watchdog/airoha_wdt.c
Normal file
216
drivers/watchdog/airoha_wdt.c
Normal file
@ -0,0 +1,216 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
/*
|
||||||
|
* Airoha Watchdog Driver
|
||||||
|
*
|
||||||
|
* Copyright (c) 2024, AIROHA All rights reserved.
|
||||||
|
*
|
||||||
|
* Mayur Kumar <mayur.kumar@airoha.com>
|
||||||
|
* Christian Marangi <ansuelsmth@gmail.com>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/moduleparam.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/bitfield.h>
|
||||||
|
#include <linux/clk.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
#include <linux/math.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/watchdog.h>
|
||||||
|
|
||||||
|
/* Base address of timer and watchdog registers */
|
||||||
|
#define TIMER_CTRL 0x0
|
||||||
|
#define WDT_ENABLE BIT(25)
|
||||||
|
#define WDT_TIMER_INTERRUPT BIT(21)
|
||||||
|
/* Timer3 is used as Watchdog Timer */
|
||||||
|
#define WDT_TIMER_ENABLE BIT(5)
|
||||||
|
#define WDT_TIMER_LOAD_VALUE 0x2c
|
||||||
|
#define WDT_TIMER_CUR_VALUE 0x30
|
||||||
|
#define WDT_TIMER_VAL GENMASK(31, 0)
|
||||||
|
#define WDT_RELOAD 0x38
|
||||||
|
#define WDT_RLD BIT(0)
|
||||||
|
|
||||||
|
/* Airoha watchdog structure description */
|
||||||
|
struct airoha_wdt_desc {
|
||||||
|
struct watchdog_device wdog_dev;
|
||||||
|
unsigned int wdt_freq;
|
||||||
|
void __iomem *base;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define WDT_HEARTBEAT 24
|
||||||
|
static int heartbeat = WDT_HEARTBEAT;
|
||||||
|
module_param(heartbeat, int, 0);
|
||||||
|
MODULE_PARM_DESC(heartbeat, "Watchdog heartbeats in seconds. (default="
|
||||||
|
__MODULE_STRING(WDT_HEARTBEAT) ")");
|
||||||
|
|
||||||
|
static bool nowayout = WATCHDOG_NOWAYOUT;
|
||||||
|
module_param(nowayout, bool, 0);
|
||||||
|
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
|
||||||
|
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
|
||||||
|
|
||||||
|
static int airoha_wdt_start(struct watchdog_device *wdog_dev)
|
||||||
|
{
|
||||||
|
struct airoha_wdt_desc *airoha_wdt = watchdog_get_drvdata(wdog_dev);
|
||||||
|
u32 val;
|
||||||
|
|
||||||
|
val = readl(airoha_wdt->base + TIMER_CTRL);
|
||||||
|
val |= (WDT_TIMER_ENABLE | WDT_ENABLE | WDT_TIMER_INTERRUPT);
|
||||||
|
writel(val, airoha_wdt->base + TIMER_CTRL);
|
||||||
|
val = wdog_dev->timeout * airoha_wdt->wdt_freq;
|
||||||
|
writel(val, airoha_wdt->base + WDT_TIMER_LOAD_VALUE);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int airoha_wdt_stop(struct watchdog_device *wdog_dev)
|
||||||
|
{
|
||||||
|
struct airoha_wdt_desc *airoha_wdt = watchdog_get_drvdata(wdog_dev);
|
||||||
|
u32 val;
|
||||||
|
|
||||||
|
val = readl(airoha_wdt->base + TIMER_CTRL);
|
||||||
|
val &= (~WDT_ENABLE & ~WDT_TIMER_ENABLE);
|
||||||
|
writel(val, airoha_wdt->base + TIMER_CTRL);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int airoha_wdt_ping(struct watchdog_device *wdog_dev)
|
||||||
|
{
|
||||||
|
struct airoha_wdt_desc *airoha_wdt = watchdog_get_drvdata(wdog_dev);
|
||||||
|
u32 val;
|
||||||
|
|
||||||
|
val = readl(airoha_wdt->base + WDT_RELOAD);
|
||||||
|
val |= WDT_RLD;
|
||||||
|
writel(val, airoha_wdt->base + WDT_RELOAD);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int airoha_wdt_set_timeout(struct watchdog_device *wdog_dev, unsigned int timeout)
|
||||||
|
{
|
||||||
|
wdog_dev->timeout = timeout;
|
||||||
|
|
||||||
|
if (watchdog_active(wdog_dev)) {
|
||||||
|
airoha_wdt_stop(wdog_dev);
|
||||||
|
return airoha_wdt_start(wdog_dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned int airoha_wdt_get_timeleft(struct watchdog_device *wdog_dev)
|
||||||
|
{
|
||||||
|
struct airoha_wdt_desc *airoha_wdt = watchdog_get_drvdata(wdog_dev);
|
||||||
|
u32 val;
|
||||||
|
|
||||||
|
val = readl(airoha_wdt->base + WDT_TIMER_CUR_VALUE);
|
||||||
|
return DIV_ROUND_UP(val, airoha_wdt->wdt_freq);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct watchdog_info airoha_wdt_info = {
|
||||||
|
.options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING,
|
||||||
|
.identity = "Airoha Watchdog",
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct watchdog_ops airoha_wdt_ops = {
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
.start = airoha_wdt_start,
|
||||||
|
.stop = airoha_wdt_stop,
|
||||||
|
.ping = airoha_wdt_ping,
|
||||||
|
.set_timeout = airoha_wdt_set_timeout,
|
||||||
|
.get_timeleft = airoha_wdt_get_timeleft,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int airoha_wdt_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct airoha_wdt_desc *airoha_wdt;
|
||||||
|
struct watchdog_device *wdog_dev;
|
||||||
|
struct device *dev = &pdev->dev;
|
||||||
|
struct clk *bus_clk;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
airoha_wdt = devm_kzalloc(dev, sizeof(*airoha_wdt), GFP_KERNEL);
|
||||||
|
if (!airoha_wdt)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
airoha_wdt->base = devm_platform_ioremap_resource(pdev, 0);
|
||||||
|
if (IS_ERR(airoha_wdt->base))
|
||||||
|
return PTR_ERR(airoha_wdt->base);
|
||||||
|
|
||||||
|
bus_clk = devm_clk_get_enabled(dev, "bus");
|
||||||
|
if (IS_ERR(bus_clk))
|
||||||
|
return dev_err_probe(dev, PTR_ERR(bus_clk),
|
||||||
|
"failed to enable bus clock\n");
|
||||||
|
|
||||||
|
/* Watchdog ticks at half the bus rate */
|
||||||
|
airoha_wdt->wdt_freq = clk_get_rate(bus_clk) / 2;
|
||||||
|
|
||||||
|
/* Initialize struct watchdog device */
|
||||||
|
wdog_dev = &airoha_wdt->wdog_dev;
|
||||||
|
wdog_dev->timeout = heartbeat;
|
||||||
|
wdog_dev->info = &airoha_wdt_info;
|
||||||
|
wdog_dev->ops = &airoha_wdt_ops;
|
||||||
|
/* Bus 300MHz, watchdog 150MHz, 28 seconds */
|
||||||
|
wdog_dev->max_timeout = FIELD_MAX(WDT_TIMER_VAL) / airoha_wdt->wdt_freq;
|
||||||
|
wdog_dev->parent = dev;
|
||||||
|
|
||||||
|
watchdog_set_drvdata(wdog_dev, airoha_wdt);
|
||||||
|
watchdog_set_nowayout(wdog_dev, nowayout);
|
||||||
|
watchdog_stop_on_unregister(wdog_dev);
|
||||||
|
|
||||||
|
ret = devm_watchdog_register_device(dev, wdog_dev);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, airoha_wdt);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int airoha_wdt_suspend(struct device *dev)
|
||||||
|
{
|
||||||
|
struct airoha_wdt_desc *airoha_wdt = dev_get_drvdata(dev);
|
||||||
|
|
||||||
|
if (watchdog_active(&airoha_wdt->wdog_dev))
|
||||||
|
airoha_wdt_stop(&airoha_wdt->wdog_dev);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int airoha_wdt_resume(struct device *dev)
|
||||||
|
{
|
||||||
|
struct airoha_wdt_desc *airoha_wdt = dev_get_drvdata(dev);
|
||||||
|
|
||||||
|
if (watchdog_active(&airoha_wdt->wdog_dev)) {
|
||||||
|
airoha_wdt_start(&airoha_wdt->wdog_dev);
|
||||||
|
airoha_wdt_ping(&airoha_wdt->wdog_dev);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct of_device_id airoha_wdt_of_match[] = {
|
||||||
|
{ .compatible = "airoha,en7581-wdt", },
|
||||||
|
{ },
|
||||||
|
};
|
||||||
|
|
||||||
|
MODULE_DEVICE_TABLE(of, airoha_wdt_of_match);
|
||||||
|
|
||||||
|
static DEFINE_SIMPLE_DEV_PM_OPS(airoha_wdt_pm_ops, airoha_wdt_suspend, airoha_wdt_resume);
|
||||||
|
|
||||||
|
static struct platform_driver airoha_wdt_driver = {
|
||||||
|
.probe = airoha_wdt_probe,
|
||||||
|
.driver = {
|
||||||
|
.name = "airoha-wdt",
|
||||||
|
.pm = pm_sleep_ptr(&airoha_wdt_pm_ops),
|
||||||
|
.of_match_table = airoha_wdt_of_match,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
module_platform_driver(airoha_wdt_driver);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Mayur Kumar <mayur.kumar@airoha.com>");
|
||||||
|
MODULE_AUTHOR("Christian Marangi <ansuelsmth@gmail.com>");
|
||||||
|
MODULE_DESCRIPTION("Airoha EN7581 Watchdog Driver");
|
||||||
|
MODULE_LICENSE("GPL");
|
@ -127,11 +127,11 @@ static int apple_wdt_restart(struct watchdog_device *wdd, unsigned long mode,
|
|||||||
/*
|
/*
|
||||||
* Flush writes and then wait for the SoC to reset. Even though the
|
* Flush writes and then wait for the SoC to reset. Even though the
|
||||||
* reset is queued almost immediately experiments have shown that it
|
* reset is queued almost immediately experiments have shown that it
|
||||||
* can take up to ~20-25ms until the SoC is actually reset. Just wait
|
* can take up to ~120-125ms until the SoC is actually reset. Just
|
||||||
* 50ms here to be safe.
|
* wait 150ms here to be safe.
|
||||||
*/
|
*/
|
||||||
(void)readl_relaxed(wdt->regs + APPLE_WDT_WD1_CUR_TIME);
|
(void)readl(wdt->regs + APPLE_WDT_WD1_CUR_TIME);
|
||||||
mdelay(50);
|
mdelay(150);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -248,7 +248,6 @@ static const struct watchdog_ops armada_37xx_wdt_ops = {
|
|||||||
static int armada_37xx_wdt_probe(struct platform_device *pdev)
|
static int armada_37xx_wdt_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct armada_37xx_watchdog *dev;
|
struct armada_37xx_watchdog *dev;
|
||||||
struct resource *res;
|
|
||||||
struct regmap *regmap;
|
struct regmap *regmap;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
@ -266,12 +265,9 @@ static int armada_37xx_wdt_probe(struct platform_device *pdev)
|
|||||||
return PTR_ERR(regmap);
|
return PTR_ERR(regmap);
|
||||||
dev->cpu_misc = regmap;
|
dev->cpu_misc = regmap;
|
||||||
|
|
||||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
dev->reg = devm_platform_ioremap_resource(pdev, 0);
|
||||||
if (!res)
|
if (IS_ERR(dev->reg))
|
||||||
return -ENODEV;
|
return PTR_ERR(dev->reg);
|
||||||
dev->reg = devm_ioremap(&pdev->dev, res->start, resource_size(res));
|
|
||||||
if (!dev->reg)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
/* init clock */
|
/* init clock */
|
||||||
dev->clk = devm_clk_get_enabled(&pdev->dev, NULL);
|
dev->clk = devm_clk_get_enabled(&pdev->dev, NULL);
|
||||||
|
@ -1,284 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
/*
|
|
||||||
* sma cpu5 watchdog driver
|
|
||||||
*
|
|
||||||
* Copyright (C) 2003 Heiko Ronsdorf <hero@ihg.uni-duisburg.de>
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
||||||
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/moduleparam.h>
|
|
||||||
#include <linux/types.h>
|
|
||||||
#include <linux/errno.h>
|
|
||||||
#include <linux/miscdevice.h>
|
|
||||||
#include <linux/fs.h>
|
|
||||||
#include <linux/ioport.h>
|
|
||||||
#include <linux/timer.h>
|
|
||||||
#include <linux/completion.h>
|
|
||||||
#include <linux/jiffies.h>
|
|
||||||
#include <linux/io.h>
|
|
||||||
#include <linux/uaccess.h>
|
|
||||||
#include <linux/watchdog.h>
|
|
||||||
|
|
||||||
/* adjustable parameters */
|
|
||||||
|
|
||||||
static int verbose;
|
|
||||||
static int port = 0x91;
|
|
||||||
static int ticks = 10000;
|
|
||||||
static DEFINE_SPINLOCK(cpu5wdt_lock);
|
|
||||||
|
|
||||||
#define PFX "cpu5wdt: "
|
|
||||||
|
|
||||||
#define CPU5WDT_EXTENT 0x0A
|
|
||||||
|
|
||||||
#define CPU5WDT_STATUS_REG 0x00
|
|
||||||
#define CPU5WDT_TIME_A_REG 0x02
|
|
||||||
#define CPU5WDT_TIME_B_REG 0x03
|
|
||||||
#define CPU5WDT_MODE_REG 0x04
|
|
||||||
#define CPU5WDT_TRIGGER_REG 0x07
|
|
||||||
#define CPU5WDT_ENABLE_REG 0x08
|
|
||||||
#define CPU5WDT_RESET_REG 0x09
|
|
||||||
|
|
||||||
#define CPU5WDT_INTERVAL (HZ/10+1)
|
|
||||||
|
|
||||||
/* some device data */
|
|
||||||
|
|
||||||
static struct {
|
|
||||||
struct completion stop;
|
|
||||||
int running;
|
|
||||||
struct timer_list timer;
|
|
||||||
int queue;
|
|
||||||
int default_ticks;
|
|
||||||
unsigned long inuse;
|
|
||||||
} cpu5wdt_device;
|
|
||||||
|
|
||||||
/* generic helper functions */
|
|
||||||
|
|
||||||
static void cpu5wdt_trigger(struct timer_list *unused)
|
|
||||||
{
|
|
||||||
if (verbose > 2)
|
|
||||||
pr_debug("trigger at %i ticks\n", ticks);
|
|
||||||
|
|
||||||
if (cpu5wdt_device.running)
|
|
||||||
ticks--;
|
|
||||||
|
|
||||||
spin_lock(&cpu5wdt_lock);
|
|
||||||
/* keep watchdog alive */
|
|
||||||
outb(1, port + CPU5WDT_TRIGGER_REG);
|
|
||||||
|
|
||||||
/* requeue?? */
|
|
||||||
if (cpu5wdt_device.queue && ticks)
|
|
||||||
mod_timer(&cpu5wdt_device.timer, jiffies + CPU5WDT_INTERVAL);
|
|
||||||
else {
|
|
||||||
/* ticks doesn't matter anyway */
|
|
||||||
complete(&cpu5wdt_device.stop);
|
|
||||||
}
|
|
||||||
spin_unlock(&cpu5wdt_lock);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static void cpu5wdt_reset(void)
|
|
||||||
{
|
|
||||||
ticks = cpu5wdt_device.default_ticks;
|
|
||||||
|
|
||||||
if (verbose)
|
|
||||||
pr_debug("reset (%i ticks)\n", (int) ticks);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static void cpu5wdt_start(void)
|
|
||||||
{
|
|
||||||
unsigned long flags;
|
|
||||||
|
|
||||||
spin_lock_irqsave(&cpu5wdt_lock, flags);
|
|
||||||
if (!cpu5wdt_device.queue) {
|
|
||||||
cpu5wdt_device.queue = 1;
|
|
||||||
outb(0, port + CPU5WDT_TIME_A_REG);
|
|
||||||
outb(0, port + CPU5WDT_TIME_B_REG);
|
|
||||||
outb(1, port + CPU5WDT_MODE_REG);
|
|
||||||
outb(0, port + CPU5WDT_RESET_REG);
|
|
||||||
outb(0, port + CPU5WDT_ENABLE_REG);
|
|
||||||
mod_timer(&cpu5wdt_device.timer, jiffies + CPU5WDT_INTERVAL);
|
|
||||||
}
|
|
||||||
/* if process dies, counter is not decremented */
|
|
||||||
cpu5wdt_device.running++;
|
|
||||||
spin_unlock_irqrestore(&cpu5wdt_lock, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int cpu5wdt_stop(void)
|
|
||||||
{
|
|
||||||
unsigned long flags;
|
|
||||||
|
|
||||||
spin_lock_irqsave(&cpu5wdt_lock, flags);
|
|
||||||
if (cpu5wdt_device.running)
|
|
||||||
cpu5wdt_device.running = 0;
|
|
||||||
ticks = cpu5wdt_device.default_ticks;
|
|
||||||
spin_unlock_irqrestore(&cpu5wdt_lock, flags);
|
|
||||||
if (verbose)
|
|
||||||
pr_crit("stop not possible\n");
|
|
||||||
return -EIO;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* filesystem operations */
|
|
||||||
|
|
||||||
static int cpu5wdt_open(struct inode *inode, struct file *file)
|
|
||||||
{
|
|
||||||
if (test_and_set_bit(0, &cpu5wdt_device.inuse))
|
|
||||||
return -EBUSY;
|
|
||||||
return stream_open(inode, file);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int cpu5wdt_release(struct inode *inode, struct file *file)
|
|
||||||
{
|
|
||||||
clear_bit(0, &cpu5wdt_device.inuse);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static long cpu5wdt_ioctl(struct file *file, unsigned int cmd,
|
|
||||||
unsigned long arg)
|
|
||||||
{
|
|
||||||
void __user *argp = (void __user *)arg;
|
|
||||||
int __user *p = argp;
|
|
||||||
unsigned int value;
|
|
||||||
static const struct watchdog_info ident = {
|
|
||||||
.options = WDIOF_CARDRESET,
|
|
||||||
.identity = "CPU5 WDT",
|
|
||||||
};
|
|
||||||
|
|
||||||
switch (cmd) {
|
|
||||||
case WDIOC_GETSUPPORT:
|
|
||||||
if (copy_to_user(argp, &ident, sizeof(ident)))
|
|
||||||
return -EFAULT;
|
|
||||||
break;
|
|
||||||
case WDIOC_GETSTATUS:
|
|
||||||
value = inb(port + CPU5WDT_STATUS_REG);
|
|
||||||
value = (value >> 2) & 1;
|
|
||||||
return put_user(value, p);
|
|
||||||
case WDIOC_GETBOOTSTATUS:
|
|
||||||
return put_user(0, p);
|
|
||||||
case WDIOC_SETOPTIONS:
|
|
||||||
if (get_user(value, p))
|
|
||||||
return -EFAULT;
|
|
||||||
if (value & WDIOS_ENABLECARD)
|
|
||||||
cpu5wdt_start();
|
|
||||||
if (value & WDIOS_DISABLECARD)
|
|
||||||
cpu5wdt_stop();
|
|
||||||
break;
|
|
||||||
case WDIOC_KEEPALIVE:
|
|
||||||
cpu5wdt_reset();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return -ENOTTY;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static ssize_t cpu5wdt_write(struct file *file, const char __user *buf,
|
|
||||||
size_t count, loff_t *ppos)
|
|
||||||
{
|
|
||||||
if (!count)
|
|
||||||
return -EIO;
|
|
||||||
cpu5wdt_reset();
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct file_operations cpu5wdt_fops = {
|
|
||||||
.owner = THIS_MODULE,
|
|
||||||
.unlocked_ioctl = cpu5wdt_ioctl,
|
|
||||||
.compat_ioctl = compat_ptr_ioctl,
|
|
||||||
.open = cpu5wdt_open,
|
|
||||||
.write = cpu5wdt_write,
|
|
||||||
.release = cpu5wdt_release,
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct miscdevice cpu5wdt_misc = {
|
|
||||||
.minor = WATCHDOG_MINOR,
|
|
||||||
.name = "watchdog",
|
|
||||||
.fops = &cpu5wdt_fops,
|
|
||||||
};
|
|
||||||
|
|
||||||
/* init/exit function */
|
|
||||||
|
|
||||||
static int cpu5wdt_init(void)
|
|
||||||
{
|
|
||||||
unsigned int val;
|
|
||||||
int err;
|
|
||||||
|
|
||||||
if (verbose)
|
|
||||||
pr_debug("port=0x%x, verbose=%i\n", port, verbose);
|
|
||||||
|
|
||||||
init_completion(&cpu5wdt_device.stop);
|
|
||||||
cpu5wdt_device.queue = 0;
|
|
||||||
timer_setup(&cpu5wdt_device.timer, cpu5wdt_trigger, 0);
|
|
||||||
cpu5wdt_device.default_ticks = ticks;
|
|
||||||
|
|
||||||
if (!request_region(port, CPU5WDT_EXTENT, PFX)) {
|
|
||||||
pr_err("request_region failed\n");
|
|
||||||
err = -EBUSY;
|
|
||||||
goto no_port;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* watchdog reboot? */
|
|
||||||
val = inb(port + CPU5WDT_STATUS_REG);
|
|
||||||
val = (val >> 2) & 1;
|
|
||||||
if (!val)
|
|
||||||
pr_info("sorry, was my fault\n");
|
|
||||||
|
|
||||||
err = misc_register(&cpu5wdt_misc);
|
|
||||||
if (err < 0) {
|
|
||||||
pr_err("misc_register failed\n");
|
|
||||||
goto no_misc;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
pr_info("init success\n");
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
no_misc:
|
|
||||||
release_region(port, CPU5WDT_EXTENT);
|
|
||||||
no_port:
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int cpu5wdt_init_module(void)
|
|
||||||
{
|
|
||||||
return cpu5wdt_init();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void cpu5wdt_exit(void)
|
|
||||||
{
|
|
||||||
if (cpu5wdt_device.queue) {
|
|
||||||
cpu5wdt_device.queue = 0;
|
|
||||||
wait_for_completion(&cpu5wdt_device.stop);
|
|
||||||
timer_shutdown_sync(&cpu5wdt_device.timer);
|
|
||||||
}
|
|
||||||
|
|
||||||
misc_deregister(&cpu5wdt_misc);
|
|
||||||
|
|
||||||
release_region(port, CPU5WDT_EXTENT);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static void cpu5wdt_exit_module(void)
|
|
||||||
{
|
|
||||||
cpu5wdt_exit();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* module entry points */
|
|
||||||
|
|
||||||
module_init(cpu5wdt_init_module);
|
|
||||||
module_exit(cpu5wdt_exit_module);
|
|
||||||
|
|
||||||
MODULE_AUTHOR("Heiko Ronsdorf <hero@ihg.uni-duisburg.de>");
|
|
||||||
MODULE_DESCRIPTION("sma cpu5 watchdog driver");
|
|
||||||
MODULE_LICENSE("GPL");
|
|
||||||
|
|
||||||
module_param_hw(port, int, ioport, 0);
|
|
||||||
MODULE_PARM_DESC(port, "base address of watchdog card, default is 0x91");
|
|
||||||
|
|
||||||
module_param(verbose, int, 0);
|
|
||||||
MODULE_PARM_DESC(verbose, "be verbose, default is 0 (no)");
|
|
||||||
|
|
||||||
module_param(ticks, int, 0);
|
|
||||||
MODULE_PARM_DESC(ticks, "count down ticks, default is 10000");
|
|
@ -146,12 +146,7 @@ static int da9055_wdt_probe(struct platform_device *pdev)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = devm_watchdog_register_device(dev, &driver_data->wdt);
|
return devm_watchdog_register_device(dev, &driver_data->wdt);
|
||||||
if (ret != 0)
|
|
||||||
dev_err(da9055->dev, "watchdog_register_device() failed: %d\n",
|
|
||||||
ret);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct platform_driver da9055_wdt_driver = {
|
static struct platform_driver da9055_wdt_driver = {
|
||||||
|
@ -27,7 +27,6 @@
|
|||||||
* 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
|
||||||
@ -230,7 +229,7 @@ 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");
|
da9063->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;
|
||||||
@ -264,11 +263,12 @@ 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)
|
static int da9063_wdt_suspend(struct device *dev)
|
||||||
{
|
{
|
||||||
struct watchdog_device *wdd = dev_get_drvdata(dev);
|
struct watchdog_device *wdd = dev_get_drvdata(dev);
|
||||||
|
struct da9063 *da9063 = watchdog_get_drvdata(wdd);
|
||||||
|
|
||||||
if (!use_sw_pm)
|
if (!da9063->use_sw_pm)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (watchdog_active(wdd))
|
if (watchdog_active(wdd))
|
||||||
@ -277,11 +277,12 @@ static int __maybe_unused da9063_wdt_suspend(struct device *dev)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __maybe_unused da9063_wdt_resume(struct device *dev)
|
static int da9063_wdt_resume(struct device *dev)
|
||||||
{
|
{
|
||||||
struct watchdog_device *wdd = dev_get_drvdata(dev);
|
struct watchdog_device *wdd = dev_get_drvdata(dev);
|
||||||
|
struct da9063 *da9063 = watchdog_get_drvdata(wdd);
|
||||||
|
|
||||||
if (!use_sw_pm)
|
if (!da9063->use_sw_pm)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (watchdog_active(wdd))
|
if (watchdog_active(wdd))
|
||||||
@ -290,14 +291,14 @@ static int __maybe_unused da9063_wdt_resume(struct device *dev)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static SIMPLE_DEV_PM_OPS(da9063_wdt_pm_ops,
|
static DEFINE_SIMPLE_DEV_PM_OPS(da9063_wdt_pm_ops, da9063_wdt_suspend,
|
||||||
da9063_wdt_suspend, da9063_wdt_resume);
|
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,
|
.pm = pm_sleep_ptr(&da9063_wdt_pm_ops),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
module_platform_driver(da9063_wdt_driver);
|
module_platform_driver(da9063_wdt_driver);
|
||||||
|
@ -151,10 +151,8 @@ static int gxp_wdt_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
watchdog_stop_on_reboot(&drvdata->wdd);
|
watchdog_stop_on_reboot(&drvdata->wdd);
|
||||||
err = devm_watchdog_register_device(dev, &drvdata->wdd);
|
err = devm_watchdog_register_device(dev, &drvdata->wdd);
|
||||||
if (err) {
|
if (err)
|
||||||
dev_err(dev, "Failed to register watchdog device");
|
|
||||||
return err;
|
return err;
|
||||||
}
|
|
||||||
|
|
||||||
dev_info(dev, "HPE GXP watchdog timer");
|
dev_info(dev, "HPE GXP watchdog timer");
|
||||||
|
|
||||||
|
@ -82,6 +82,13 @@
|
|||||||
#define TCO2_CNT(p) (TCOBASE(p) + 0x0a) /* TCO2 Control Register */
|
#define TCO2_CNT(p) (TCOBASE(p) + 0x0a) /* TCO2 Control Register */
|
||||||
#define TCOv2_TMR(p) (TCOBASE(p) + 0x12) /* TCOv2 Timer Initial Value*/
|
#define TCOv2_TMR(p) (TCOBASE(p) + 0x12) /* TCOv2 Timer Initial Value*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* NMI_NOW is bit 8 of TCO1_CNT register
|
||||||
|
* Read/Write
|
||||||
|
* This bit is implemented as RW but has no effect on HW.
|
||||||
|
*/
|
||||||
|
#define NMI_NOW BIT(8)
|
||||||
|
|
||||||
/* internal variables */
|
/* internal variables */
|
||||||
struct iTCO_wdt_private {
|
struct iTCO_wdt_private {
|
||||||
struct watchdog_device wddev;
|
struct watchdog_device wddev;
|
||||||
@ -219,13 +226,23 @@ static int update_no_reboot_bit_cnt(void *priv, bool set)
|
|||||||
struct iTCO_wdt_private *p = priv;
|
struct iTCO_wdt_private *p = priv;
|
||||||
u16 val, newval;
|
u16 val, newval;
|
||||||
|
|
||||||
val = inw(TCO1_CNT(p));
|
/*
|
||||||
|
* writing back 1b1 to NMI_NOW of TCO1_CNT register
|
||||||
|
* causes NMI_NOW bit inversion what consequently does
|
||||||
|
* not allow to perform the register's value comparison
|
||||||
|
* properly.
|
||||||
|
*
|
||||||
|
* NMI_NOW bit masking for TCO1_CNT register values
|
||||||
|
* helps to avoid possible NMI_NOW bit inversions on
|
||||||
|
* following write operation.
|
||||||
|
*/
|
||||||
|
val = inw(TCO1_CNT(p)) & ~NMI_NOW;
|
||||||
if (set)
|
if (set)
|
||||||
val |= BIT(0);
|
val |= BIT(0);
|
||||||
else
|
else
|
||||||
val &= ~BIT(0);
|
val &= ~BIT(0);
|
||||||
outw(val, TCO1_CNT(p));
|
outw(val, TCO1_CNT(p));
|
||||||
newval = inw(TCO1_CNT(p));
|
newval = inw(TCO1_CNT(p)) & ~NMI_NOW;
|
||||||
|
|
||||||
/* make sure the update is successful */
|
/* make sure the update is successful */
|
||||||
return val != newval ? -EIO : 0;
|
return val != newval ? -EIO : 0;
|
||||||
@ -592,10 +609,8 @@ static int iTCO_wdt_probe(struct platform_device *pdev)
|
|||||||
watchdog_stop_on_reboot(&p->wddev);
|
watchdog_stop_on_reboot(&p->wddev);
|
||||||
watchdog_stop_on_unregister(&p->wddev);
|
watchdog_stop_on_unregister(&p->wddev);
|
||||||
ret = devm_watchdog_register_device(dev, &p->wddev);
|
ret = devm_watchdog_register_device(dev, &p->wddev);
|
||||||
if (ret != 0) {
|
if (ret != 0)
|
||||||
dev_err(dev, "cannot register watchdog device (err=%d)\n", ret);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
|
||||||
|
|
||||||
dev_info(dev, "initialized. heartbeat=%d sec (nowayout=%d)\n",
|
dev_info(dev, "initialized. heartbeat=%d sec (nowayout=%d)\n",
|
||||||
heartbeat, nowayout);
|
heartbeat, nowayout);
|
||||||
|
@ -20,6 +20,8 @@
|
|||||||
|
|
||||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||||
|
|
||||||
|
#include <linux/bits.h>
|
||||||
|
#include <linux/dmi.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
@ -40,6 +42,7 @@
|
|||||||
#define VAL 0x2f
|
#define VAL 0x2f
|
||||||
|
|
||||||
/* Logical device Numbers LDN */
|
/* Logical device Numbers LDN */
|
||||||
|
#define EC 0x04
|
||||||
#define GPIO 0x07
|
#define GPIO 0x07
|
||||||
|
|
||||||
/* Configuration Registers and Functions */
|
/* Configuration Registers and Functions */
|
||||||
@ -73,6 +76,12 @@
|
|||||||
#define IT8784_ID 0x8784
|
#define IT8784_ID 0x8784
|
||||||
#define IT8786_ID 0x8786
|
#define IT8786_ID 0x8786
|
||||||
|
|
||||||
|
/* Environment Controller Configuration Registers LDN=0x04 */
|
||||||
|
#define SCR1 0xfa
|
||||||
|
|
||||||
|
/* Environment Controller Bits SCR1 */
|
||||||
|
#define WDT_PWRGD 0x20
|
||||||
|
|
||||||
/* GPIO Configuration Registers LDN=0x07 */
|
/* GPIO Configuration Registers LDN=0x07 */
|
||||||
#define WDTCTRL 0x71
|
#define WDTCTRL 0x71
|
||||||
#define WDTCFG 0x72
|
#define WDTCFG 0x72
|
||||||
@ -240,6 +249,21 @@ static int wdt_set_timeout(struct watchdog_device *wdd, unsigned int t)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum {
|
||||||
|
IT87_WDT_OUTPUT_THROUGH_PWRGD = BIT(0),
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct dmi_system_id it87_quirks[] = {
|
||||||
|
{
|
||||||
|
/* Qotom Q30900P (IT8786) */
|
||||||
|
.matches = {
|
||||||
|
DMI_EXACT_MATCH(DMI_BOARD_NAME, "QCML04"),
|
||||||
|
},
|
||||||
|
.driver_data = (void *)IT87_WDT_OUTPUT_THROUGH_PWRGD,
|
||||||
|
},
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
static const struct watchdog_info ident = {
|
static const struct watchdog_info ident = {
|
||||||
.options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING,
|
.options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING,
|
||||||
.firmware_version = 1,
|
.firmware_version = 1,
|
||||||
@ -261,8 +285,10 @@ static struct watchdog_device wdt_dev = {
|
|||||||
|
|
||||||
static int __init it87_wdt_init(void)
|
static int __init it87_wdt_init(void)
|
||||||
{
|
{
|
||||||
|
const struct dmi_system_id *dmi_id;
|
||||||
u8 chip_rev;
|
u8 chip_rev;
|
||||||
u8 ctrl;
|
u8 ctrl;
|
||||||
|
int quirks = 0;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
rc = superio_enter();
|
rc = superio_enter();
|
||||||
@ -273,6 +299,10 @@ static int __init it87_wdt_init(void)
|
|||||||
chip_rev = superio_inb(CHIPREV) & 0x0f;
|
chip_rev = superio_inb(CHIPREV) & 0x0f;
|
||||||
superio_exit();
|
superio_exit();
|
||||||
|
|
||||||
|
dmi_id = dmi_first_match(it87_quirks);
|
||||||
|
if (dmi_id)
|
||||||
|
quirks = (long)dmi_id->driver_data;
|
||||||
|
|
||||||
switch (chip_type) {
|
switch (chip_type) {
|
||||||
case IT8702_ID:
|
case IT8702_ID:
|
||||||
max_units = 255;
|
max_units = 255;
|
||||||
@ -333,6 +363,15 @@ static int __init it87_wdt_init(void)
|
|||||||
superio_outb(0x00, WDTCTRL);
|
superio_outb(0x00, WDTCTRL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (quirks & IT87_WDT_OUTPUT_THROUGH_PWRGD) {
|
||||||
|
superio_select(EC);
|
||||||
|
ctrl = superio_inb(SCR1);
|
||||||
|
if (!(ctrl & WDT_PWRGD)) {
|
||||||
|
ctrl |= WDT_PWRGD;
|
||||||
|
superio_outb(ctrl, SCR1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
superio_exit();
|
superio_exit();
|
||||||
|
|
||||||
if (timeout < 1 || timeout > max_units * 60) {
|
if (timeout < 1 || timeout > max_units * 60) {
|
||||||
@ -349,10 +388,8 @@ static int __init it87_wdt_init(void)
|
|||||||
|
|
||||||
watchdog_stop_on_reboot(&wdt_dev);
|
watchdog_stop_on_reboot(&wdt_dev);
|
||||||
rc = watchdog_register_device(&wdt_dev);
|
rc = watchdog_register_device(&wdt_dev);
|
||||||
if (rc) {
|
if (rc)
|
||||||
pr_err("Cannot register watchdog device (err=%d)\n", rc);
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
|
||||||
|
|
||||||
pr_info("Chip IT%04x revision %d initialized. timeout=%d sec (nowayout=%d testmode=%d)\n",
|
pr_info("Chip IT%04x revision %d initialized. timeout=%d sec (nowayout=%d testmode=%d)\n",
|
||||||
chip_type, chip_rev, timeout, nowayout, testmode);
|
chip_type, chip_rev, timeout, nowayout, testmode);
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <dt-bindings/reset/mt2712-resets.h>
|
#include <dt-bindings/reset/mt2712-resets.h>
|
||||||
|
#include <dt-bindings/reset/mediatek,mt6735-wdt.h>
|
||||||
#include <dt-bindings/reset/mediatek,mt6795-resets.h>
|
#include <dt-bindings/reset/mediatek,mt6795-resets.h>
|
||||||
#include <dt-bindings/reset/mt7986-resets.h>
|
#include <dt-bindings/reset/mt7986-resets.h>
|
||||||
#include <dt-bindings/reset/mt8183-resets.h>
|
#include <dt-bindings/reset/mt8183-resets.h>
|
||||||
@ -87,6 +88,10 @@ 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 mt6735_data = {
|
||||||
|
.toprgu_sw_rst_num = MT6735_TOPRGU_RST_NUM,
|
||||||
|
};
|
||||||
|
|
||||||
static const struct mtk_wdt_data mt6795_data = {
|
static const struct mtk_wdt_data mt6795_data = {
|
||||||
.toprgu_sw_rst_num = MT6795_TOPRGU_SW_RST_NUM,
|
.toprgu_sw_rst_num = MT6795_TOPRGU_SW_RST_NUM,
|
||||||
};
|
};
|
||||||
@ -225,9 +230,15 @@ static int mtk_wdt_restart(struct watchdog_device *wdt_dev,
|
|||||||
{
|
{
|
||||||
struct mtk_wdt_dev *mtk_wdt = watchdog_get_drvdata(wdt_dev);
|
struct mtk_wdt_dev *mtk_wdt = watchdog_get_drvdata(wdt_dev);
|
||||||
void __iomem *wdt_base;
|
void __iomem *wdt_base;
|
||||||
|
u32 reg;
|
||||||
|
|
||||||
wdt_base = mtk_wdt->wdt_base;
|
wdt_base = mtk_wdt->wdt_base;
|
||||||
|
|
||||||
|
/* Enable reset in order to issue a system reset instead of an IRQ */
|
||||||
|
reg = readl(wdt_base + WDT_MODE);
|
||||||
|
reg &= ~WDT_MODE_IRQ_EN;
|
||||||
|
writel(reg | WDT_MODE_KEY, wdt_base + WDT_MODE);
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
writel(WDT_SWRST_KEY, wdt_base + WDT_SWRST);
|
writel(WDT_SWRST_KEY, wdt_base + WDT_SWRST);
|
||||||
mdelay(5);
|
mdelay(5);
|
||||||
@ -483,6 +494,7 @@ 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,mt6735-wdt", .data = &mt6735_data },
|
||||||
{ .compatible = "mediatek,mt6795-wdt", .data = &mt6795_data },
|
{ .compatible = "mediatek,mt6795-wdt", .data = &mt6795_data },
|
||||||
{ .compatible = "mediatek,mt7986-wdt", .data = &mt7986_data },
|
{ .compatible = "mediatek,mt7986-wdt", .data = &mt7986_data },
|
||||||
{ .compatible = "mediatek,mt7988-wdt", .data = &mt7988_data },
|
{ .compatible = "mediatek,mt7988-wdt", .data = &mt7988_data },
|
||||||
|
@ -559,10 +559,8 @@ static int __init octeon_wdt_init(void)
|
|||||||
watchdog_set_nowayout(&octeon_wdt, nowayout);
|
watchdog_set_nowayout(&octeon_wdt, nowayout);
|
||||||
|
|
||||||
ret = watchdog_register_device(&octeon_wdt);
|
ret = watchdog_register_device(&octeon_wdt);
|
||||||
if (ret) {
|
if (ret)
|
||||||
pr_err("watchdog_register_device() failed: %d\n", ret);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
|
||||||
|
|
||||||
if (disable) {
|
if (disable) {
|
||||||
pr_notice("disabled\n");
|
pr_notice("disabled\n");
|
||||||
|
@ -833,7 +833,7 @@ static int pcwd_isa_match(struct device *dev, unsigned int id)
|
|||||||
port0 = inb_p(base_addr);
|
port0 = inb_p(base_addr);
|
||||||
port1 = inb_p(base_addr + 1);
|
port1 = inb_p(base_addr + 1);
|
||||||
|
|
||||||
/* Has either hearbeat bit changed? */
|
/* Has either heartbeat bit changed? */
|
||||||
if ((port0 ^ last_port0) & WD_HRTBT ||
|
if ((port0 ^ last_port0) & WD_HRTBT ||
|
||||||
(port1 ^ last_port1) & WD_REVC_HRBT) {
|
(port1 ^ last_port1) & WD_REVC_HRBT) {
|
||||||
retval = 1;
|
retval = 1;
|
||||||
|
@ -61,7 +61,7 @@
|
|||||||
|
|
||||||
#define MAX_HW_ERROR 250
|
#define MAX_HW_ERROR 250
|
||||||
|
|
||||||
static int heartbeat = DEFAULT_HEARTBEAT;
|
static int heartbeat;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* struct to hold data for each WDT device
|
* struct to hold data for each WDT device
|
||||||
@ -252,6 +252,7 @@ static int rti_wdt_probe(struct platform_device *pdev)
|
|||||||
wdd->min_timeout = 1;
|
wdd->min_timeout = 1;
|
||||||
wdd->max_hw_heartbeat_ms = (WDT_PRELOAD_MAX << WDT_PRELOAD_SHIFT) /
|
wdd->max_hw_heartbeat_ms = (WDT_PRELOAD_MAX << WDT_PRELOAD_SHIFT) /
|
||||||
wdt->freq * 1000;
|
wdt->freq * 1000;
|
||||||
|
wdd->timeout = DEFAULT_HEARTBEAT;
|
||||||
wdd->parent = dev;
|
wdd->parent = dev;
|
||||||
|
|
||||||
watchdog_set_drvdata(wdd, wdt);
|
watchdog_set_drvdata(wdd, wdt);
|
||||||
@ -336,10 +337,8 @@ static int rti_wdt_probe(struct platform_device *pdev)
|
|||||||
watchdog_init_timeout(wdd, heartbeat, dev);
|
watchdog_init_timeout(wdd, heartbeat, dev);
|
||||||
|
|
||||||
ret = watchdog_register_device(wdd);
|
ret = watchdog_register_device(wdd);
|
||||||
if (ret) {
|
if (ret)
|
||||||
dev_err(dev, "cannot register watchdog device\n");
|
|
||||||
goto err_iomap;
|
goto err_iomap;
|
||||||
}
|
|
||||||
|
|
||||||
if (last_ping)
|
if (last_ping)
|
||||||
watchdog_set_last_hw_keepalive(wdd, last_ping);
|
watchdog_set_last_hw_keepalive(wdd, last_ping);
|
||||||
|
@ -169,7 +169,6 @@ static int rza_wdt_probe(struct platform_device *pdev)
|
|||||||
struct device *dev = &pdev->dev;
|
struct device *dev = &pdev->dev;
|
||||||
struct rza_wdt *priv;
|
struct rza_wdt *priv;
|
||||||
unsigned long rate;
|
unsigned long rate;
|
||||||
int ret;
|
|
||||||
|
|
||||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||||
if (!priv)
|
if (!priv)
|
||||||
@ -218,11 +217,7 @@ static int rza_wdt_probe(struct platform_device *pdev)
|
|||||||
watchdog_init_timeout(&priv->wdev, 0, dev);
|
watchdog_init_timeout(&priv->wdev, 0, dev);
|
||||||
watchdog_set_drvdata(&priv->wdev, priv);
|
watchdog_set_drvdata(&priv->wdev, priv);
|
||||||
|
|
||||||
ret = devm_watchdog_register_device(dev, &priv->wdev);
|
return devm_watchdog_register_device(dev, &priv->wdev);
|
||||||
if (ret)
|
|
||||||
dev_err(dev, "Cannot register watchdog device\n");
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct of_device_id rza_wdt_of_match[] = {
|
static const struct of_device_id rza_wdt_of_match[] = {
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/pm_domain.h>
|
||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
#include <linux/reset.h>
|
#include <linux/reset.h>
|
||||||
#include <linux/units.h>
|
#include <linux/units.h>
|
||||||
@ -166,8 +167,22 @@ 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);
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
clk_prepare_enable(priv->pclk);
|
/*
|
||||||
clk_prepare_enable(priv->osc_clk);
|
* In case of RZ/G3S the watchdog device may be part of an IRQ safe power
|
||||||
|
* domain that is currently powered off. In this case we need to power
|
||||||
|
* it on before accessing registers. Along with this the clocks will be
|
||||||
|
* enabled. We don't undo the pm_runtime_resume_and_get() as the device
|
||||||
|
* need to be on for the reboot to happen.
|
||||||
|
*
|
||||||
|
* For the rest of SoCs not registering a watchdog IRQ safe power
|
||||||
|
* domain it is safe to call pm_runtime_resume_and_get() as the
|
||||||
|
* irq_safe_dev_in_sleep_domain() call in genpd_runtime_resume()
|
||||||
|
* returns non zero value and the genpd_lock() is avoided, thus, there
|
||||||
|
* will be no invalid wait context reported by lockdep.
|
||||||
|
*/
|
||||||
|
ret = pm_runtime_resume_and_get(wdev->parent);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
if (priv->devtype == WDT_RZG2L) {
|
if (priv->devtype == WDT_RZG2L) {
|
||||||
ret = reset_control_deassert(priv->rstc);
|
ret = reset_control_deassert(priv->rstc);
|
||||||
@ -275,6 +290,7 @@ static int rzg2l_wdt_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
priv->devtype = (uintptr_t)of_device_get_match_data(dev);
|
priv->devtype = (uintptr_t)of_device_get_match_data(dev);
|
||||||
|
|
||||||
|
pm_runtime_irq_safe(&pdev->dev);
|
||||||
pm_runtime_enable(&pdev->dev);
|
pm_runtime_enable(&pdev->dev);
|
||||||
|
|
||||||
priv->wdev.info = &rzg2l_wdt_ident;
|
priv->wdev.info = &rzg2l_wdt_ident;
|
||||||
|
@ -52,7 +52,7 @@ static int rzn1_wdt_ping(struct watchdog_device *w)
|
|||||||
{
|
{
|
||||||
struct rzn1_watchdog *wdt = watchdog_get_drvdata(w);
|
struct rzn1_watchdog *wdt = watchdog_get_drvdata(w);
|
||||||
|
|
||||||
/* Any value retrigggers the watchdog */
|
/* Any value retriggers the watchdog */
|
||||||
writel(0, wdt->base + RZN1_WDT_RETRIGGER);
|
writel(0, wdt->base + RZN1_WDT_RETRIGGER);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -24,9 +24,9 @@
|
|||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
|
#include <linux/mfd/syscon.h>
|
||||||
#include <linux/regmap.h>
|
#include <linux/regmap.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/soc/samsung/exynos-pmu.h>
|
|
||||||
|
|
||||||
#define S3C2410_WTCON 0x00
|
#define S3C2410_WTCON 0x00
|
||||||
#define S3C2410_WTDAT 0x04
|
#define S3C2410_WTDAT 0x04
|
||||||
@ -63,6 +63,10 @@
|
|||||||
#define EXYNOS850_CLUSTER1_NONCPU_INT_EN 0x1644
|
#define EXYNOS850_CLUSTER1_NONCPU_INT_EN 0x1644
|
||||||
#define EXYNOSAUTOV9_CLUSTER1_NONCPU_OUT 0x1520
|
#define EXYNOSAUTOV9_CLUSTER1_NONCPU_OUT 0x1520
|
||||||
#define EXYNOSAUTOV9_CLUSTER1_NONCPU_INT_EN 0x1544
|
#define EXYNOSAUTOV9_CLUSTER1_NONCPU_INT_EN 0x1544
|
||||||
|
#define EXYNOSAUTOV920_CLUSTER0_NONCPU_OUT 0x1420
|
||||||
|
#define EXYNOSAUTOV920_CLUSTER0_NONCPU_INT_EN 0x1444
|
||||||
|
#define EXYNOSAUTOV920_CLUSTER1_NONCPU_OUT 0x1720
|
||||||
|
#define EXYNOSAUTOV920_CLUSTER1_NONCPU_INT_EN 0x1744
|
||||||
|
|
||||||
#define EXYNOS850_CLUSTER0_WDTRESET_BIT 24
|
#define EXYNOS850_CLUSTER0_WDTRESET_BIT 24
|
||||||
#define EXYNOS850_CLUSTER1_WDTRESET_BIT 23
|
#define EXYNOS850_CLUSTER1_WDTRESET_BIT 23
|
||||||
@ -303,6 +307,32 @@ static const struct s3c2410_wdt_variant drv_data_gs101_cl1 = {
|
|||||||
QUIRK_HAS_DBGACK_BIT,
|
QUIRK_HAS_DBGACK_BIT,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct s3c2410_wdt_variant drv_data_exynosautov920_cl0 = {
|
||||||
|
.mask_reset_reg = EXYNOSAUTOV920_CLUSTER0_NONCPU_INT_EN,
|
||||||
|
.mask_bit = 2,
|
||||||
|
.mask_reset_inv = true,
|
||||||
|
.rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET,
|
||||||
|
.rst_stat_bit = EXYNOSAUTOV9_CLUSTER0_WDTRESET_BIT,
|
||||||
|
.cnt_en_reg = EXYNOSAUTOV920_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 |
|
||||||
|
QUIRK_HAS_DBGACK_BIT,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct s3c2410_wdt_variant drv_data_exynosautov920_cl1 = {
|
||||||
|
.mask_reset_reg = EXYNOSAUTOV920_CLUSTER1_NONCPU_INT_EN,
|
||||||
|
.mask_bit = 2,
|
||||||
|
.mask_reset_inv = true,
|
||||||
|
.rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET,
|
||||||
|
.rst_stat_bit = EXYNOSAUTOV9_CLUSTER1_WDTRESET_BIT,
|
||||||
|
.cnt_en_reg = EXYNOSAUTOV920_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 |
|
||||||
|
QUIRK_HAS_DBGACK_BIT,
|
||||||
|
};
|
||||||
|
|
||||||
static const struct of_device_id s3c2410_wdt_match[] = {
|
static const struct of_device_id s3c2410_wdt_match[] = {
|
||||||
{ .compatible = "google,gs101-wdt",
|
{ .compatible = "google,gs101-wdt",
|
||||||
.data = &drv_data_gs101_cl0 },
|
.data = &drv_data_gs101_cl0 },
|
||||||
@ -320,6 +350,8 @@ static const struct of_device_id s3c2410_wdt_match[] = {
|
|||||||
.data = &drv_data_exynos850_cl0 },
|
.data = &drv_data_exynos850_cl0 },
|
||||||
{ .compatible = "samsung,exynosautov9-wdt",
|
{ .compatible = "samsung,exynosautov9-wdt",
|
||||||
.data = &drv_data_exynosautov9_cl0 },
|
.data = &drv_data_exynosautov9_cl0 },
|
||||||
|
{ .compatible = "samsung,exynosautov920-wdt",
|
||||||
|
.data = &drv_data_exynosautov920_cl0 },
|
||||||
{},
|
{},
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(of, s3c2410_wdt_match);
|
MODULE_DEVICE_TABLE(of, s3c2410_wdt_match);
|
||||||
@ -643,7 +675,8 @@ s3c2410_get_wdt_drv_data(struct platform_device *pdev, struct s3c2410_wdt *wdt)
|
|||||||
/* Choose Exynos850/ExynosAutov9 driver data w.r.t. cluster index */
|
/* Choose Exynos850/ExynosAutov9 driver data w.r.t. cluster index */
|
||||||
if (variant == &drv_data_exynos850_cl0 ||
|
if (variant == &drv_data_exynos850_cl0 ||
|
||||||
variant == &drv_data_exynosautov9_cl0 ||
|
variant == &drv_data_exynosautov9_cl0 ||
|
||||||
variant == &drv_data_gs101_cl0) {
|
variant == &drv_data_gs101_cl0 ||
|
||||||
|
variant == &drv_data_exynosautov920_cl0) {
|
||||||
u32 index;
|
u32 index;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
@ -662,6 +695,8 @@ s3c2410_get_wdt_drv_data(struct platform_device *pdev, struct s3c2410_wdt *wdt)
|
|||||||
variant = &drv_data_exynosautov9_cl1;
|
variant = &drv_data_exynosautov9_cl1;
|
||||||
else if (variant == &drv_data_gs101_cl0)
|
else if (variant == &drv_data_gs101_cl0)
|
||||||
variant = &drv_data_gs101_cl1;
|
variant = &drv_data_gs101_cl1;
|
||||||
|
else if (variant == &drv_data_exynosautov920_cl0)
|
||||||
|
variant = &drv_data_exynosautov920_cl1;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return dev_err_probe(dev, -EINVAL, "wrong cluster index: %u\n", index);
|
return dev_err_probe(dev, -EINVAL, "wrong cluster index: %u\n", index);
|
||||||
@ -699,11 +734,11 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
|
|||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
if (wdt->drv_data->quirks & QUIRKS_HAVE_PMUREG) {
|
if (wdt->drv_data->quirks & QUIRKS_HAVE_PMUREG) {
|
||||||
wdt->pmureg = exynos_get_pmu_regmap_by_phandle(dev->of_node,
|
wdt->pmureg = syscon_regmap_lookup_by_phandle(dev->of_node,
|
||||||
"samsung,syscon-phandle");
|
"samsung,syscon-phandle");
|
||||||
if (IS_ERR(wdt->pmureg))
|
if (IS_ERR(wdt->pmureg))
|
||||||
return dev_err_probe(dev, PTR_ERR(wdt->pmureg),
|
return dev_err_probe(dev, PTR_ERR(wdt->pmureg),
|
||||||
"PMU regmap lookup failed.\n");
|
"syscon regmap lookup failed.\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
wdt_irq = platform_get_irq(pdev, 0);
|
wdt_irq = platform_get_irq(pdev, 0);
|
||||||
|
@ -236,8 +236,8 @@ static void sa1100dog_remove(struct platform_device *pdev)
|
|||||||
|
|
||||||
static struct platform_driver sa1100dog_driver = {
|
static struct platform_driver sa1100dog_driver = {
|
||||||
.driver.name = "sa1100_wdt",
|
.driver.name = "sa1100_wdt",
|
||||||
.probe = sa1100dog_probe,
|
.probe = sa1100dog_probe,
|
||||||
.remove = sa1100dog_remove,
|
.remove = sa1100dog_remove,
|
||||||
};
|
};
|
||||||
module_platform_driver(sa1100dog_driver);
|
module_platform_driver(sa1100dog_driver);
|
||||||
|
|
||||||
|
@ -198,10 +198,8 @@ static int sl28cpld_wdt_probe(struct platform_device *pdev)
|
|||||||
}
|
}
|
||||||
|
|
||||||
ret = devm_watchdog_register_device(&pdev->dev, wdd);
|
ret = devm_watchdog_register_device(&pdev->dev, wdd);
|
||||||
if (ret < 0) {
|
if (ret < 0)
|
||||||
dev_err(&pdev->dev, "failed to register watchdog device\n");
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
|
||||||
|
|
||||||
dev_info(&pdev->dev, "initial timeout %d sec%s\n",
|
dev_info(&pdev->dev, "initial timeout %d sec%s\n",
|
||||||
wdd->timeout, nowayout ? ", nowayout" : "");
|
wdd->timeout, nowayout ? ", nowayout" : "");
|
||||||
|
@ -485,7 +485,7 @@ static long wb_smsc_wdt_ioctl(struct file *file,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -- Notifier funtions -----------------------------------------*/
|
/* -- Notifier functions -----------------------------------------*/
|
||||||
|
|
||||||
static int wb_smsc_wdt_notify_sys(struct notifier_block *this,
|
static int wb_smsc_wdt_notify_sys(struct notifier_block *this,
|
||||||
unsigned long code, void *unused)
|
unsigned long code, void *unused)
|
||||||
|
@ -80,7 +80,7 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
|
|||||||
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
|
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
|
||||||
|
|
||||||
struct starfive_wdt_variant {
|
struct starfive_wdt_variant {
|
||||||
unsigned int control; /* Watchdog Control Resgister for reset enable */
|
unsigned int control; /* Watchdog Control Register for reset enable */
|
||||||
unsigned int load; /* Watchdog Load register */
|
unsigned int load; /* Watchdog Load register */
|
||||||
unsigned int reload; /* Watchdog Reload Control register */
|
unsigned int reload; /* Watchdog Reload Control register */
|
||||||
unsigned int enable; /* Watchdog Enable Register */
|
unsigned int enable; /* Watchdog Enable Register */
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/pm_wakeirq.h>
|
||||||
#include <linux/watchdog.h>
|
#include <linux/watchdog.h>
|
||||||
|
|
||||||
#define DEFAULT_TIMEOUT 10
|
#define DEFAULT_TIMEOUT 10
|
||||||
@ -28,6 +29,7 @@
|
|||||||
#define IWDG_RLR 0x08 /* ReLoad Register */
|
#define IWDG_RLR 0x08 /* ReLoad Register */
|
||||||
#define IWDG_SR 0x0C /* Status Register */
|
#define IWDG_SR 0x0C /* Status Register */
|
||||||
#define IWDG_WINR 0x10 /* Windows Register */
|
#define IWDG_WINR 0x10 /* Windows Register */
|
||||||
|
#define IWDG_EWCR 0x14 /* Early Wake-up Register */
|
||||||
|
|
||||||
/* IWDG_KR register bit mask */
|
/* IWDG_KR register bit mask */
|
||||||
#define KR_KEY_RELOAD 0xAAAA /* reload counter enable */
|
#define KR_KEY_RELOAD 0xAAAA /* reload counter enable */
|
||||||
@ -47,22 +49,29 @@
|
|||||||
#define SR_PVU BIT(0) /* Watchdog prescaler value update */
|
#define SR_PVU BIT(0) /* Watchdog prescaler value update */
|
||||||
#define SR_RVU BIT(1) /* Watchdog counter reload value update */
|
#define SR_RVU BIT(1) /* Watchdog counter reload value update */
|
||||||
|
|
||||||
|
#define EWCR_EWIT GENMASK(11, 0) /* Watchdog counter window value */
|
||||||
|
#define EWCR_EWIC BIT(14) /* Watchdog early interrupt acknowledge */
|
||||||
|
#define EWCR_EWIE BIT(15) /* Watchdog early interrupt enable */
|
||||||
|
|
||||||
/* set timeout to 100000 us */
|
/* set timeout to 100000 us */
|
||||||
#define TIMEOUT_US 100000
|
#define TIMEOUT_US 100000
|
||||||
#define SLEEP_US 1000
|
#define SLEEP_US 1000
|
||||||
|
|
||||||
struct stm32_iwdg_data {
|
struct stm32_iwdg_data {
|
||||||
bool has_pclk;
|
bool has_pclk;
|
||||||
|
bool has_early_wakeup;
|
||||||
u32 max_prescaler;
|
u32 max_prescaler;
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct stm32_iwdg_data stm32_iwdg_data = {
|
static const struct stm32_iwdg_data stm32_iwdg_data = {
|
||||||
.has_pclk = false,
|
.has_pclk = false,
|
||||||
|
.has_early_wakeup = false,
|
||||||
.max_prescaler = 256,
|
.max_prescaler = 256,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct stm32_iwdg_data stm32mp1_iwdg_data = {
|
static const struct stm32_iwdg_data stm32mp1_iwdg_data = {
|
||||||
.has_pclk = true,
|
.has_pclk = true,
|
||||||
|
.has_early_wakeup = true,
|
||||||
.max_prescaler = 1024,
|
.max_prescaler = 1024,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -88,13 +97,18 @@ static inline void reg_write(void __iomem *base, u32 reg, u32 val)
|
|||||||
static int stm32_iwdg_start(struct watchdog_device *wdd)
|
static int stm32_iwdg_start(struct watchdog_device *wdd)
|
||||||
{
|
{
|
||||||
struct stm32_iwdg *wdt = watchdog_get_drvdata(wdd);
|
struct stm32_iwdg *wdt = watchdog_get_drvdata(wdd);
|
||||||
u32 tout, presc, iwdg_rlr, iwdg_pr, iwdg_sr;
|
u32 tout, ptot, presc, iwdg_rlr, iwdg_ewcr, iwdg_pr, iwdg_sr;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
dev_dbg(wdd->parent, "%s\n", __func__);
|
dev_dbg(wdd->parent, "%s\n", __func__);
|
||||||
|
|
||||||
|
if (!wdd->pretimeout)
|
||||||
|
wdd->pretimeout = 3 * wdd->timeout / 4;
|
||||||
|
|
||||||
tout = clamp_t(unsigned int, wdd->timeout,
|
tout = clamp_t(unsigned int, wdd->timeout,
|
||||||
wdd->min_timeout, wdd->max_hw_heartbeat_ms / 1000);
|
wdd->min_timeout, wdd->max_hw_heartbeat_ms / 1000);
|
||||||
|
ptot = clamp_t(unsigned int, tout - wdd->pretimeout,
|
||||||
|
wdd->min_timeout, tout);
|
||||||
|
|
||||||
presc = DIV_ROUND_UP(tout * wdt->rate, RLR_MAX + 1);
|
presc = DIV_ROUND_UP(tout * wdt->rate, RLR_MAX + 1);
|
||||||
|
|
||||||
@ -102,6 +116,7 @@ static int stm32_iwdg_start(struct watchdog_device *wdd)
|
|||||||
presc = roundup_pow_of_two(presc);
|
presc = roundup_pow_of_two(presc);
|
||||||
iwdg_pr = presc <= 1 << PR_SHIFT ? 0 : ilog2(presc) - PR_SHIFT;
|
iwdg_pr = presc <= 1 << PR_SHIFT ? 0 : ilog2(presc) - PR_SHIFT;
|
||||||
iwdg_rlr = ((tout * wdt->rate) / presc) - 1;
|
iwdg_rlr = ((tout * wdt->rate) / presc) - 1;
|
||||||
|
iwdg_ewcr = ((ptot * wdt->rate) / presc) - 1;
|
||||||
|
|
||||||
/* enable write access */
|
/* enable write access */
|
||||||
reg_write(wdt->regs, IWDG_KR, KR_KEY_EWA);
|
reg_write(wdt->regs, IWDG_KR, KR_KEY_EWA);
|
||||||
@ -109,6 +124,8 @@ static int stm32_iwdg_start(struct watchdog_device *wdd)
|
|||||||
/* set prescaler & reload registers */
|
/* set prescaler & reload registers */
|
||||||
reg_write(wdt->regs, IWDG_PR, iwdg_pr);
|
reg_write(wdt->regs, IWDG_PR, iwdg_pr);
|
||||||
reg_write(wdt->regs, IWDG_RLR, iwdg_rlr);
|
reg_write(wdt->regs, IWDG_RLR, iwdg_rlr);
|
||||||
|
if (wdt->data->has_early_wakeup)
|
||||||
|
reg_write(wdt->regs, IWDG_EWCR, iwdg_ewcr | EWCR_EWIE);
|
||||||
reg_write(wdt->regs, IWDG_KR, KR_KEY_ENABLE);
|
reg_write(wdt->regs, IWDG_KR, KR_KEY_ENABLE);
|
||||||
|
|
||||||
/* wait for the registers to be updated (max 100ms) */
|
/* wait for the registers to be updated (max 100ms) */
|
||||||
@ -151,6 +168,34 @@ static int stm32_iwdg_set_timeout(struct watchdog_device *wdd,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int stm32_iwdg_set_pretimeout(struct watchdog_device *wdd,
|
||||||
|
unsigned int pretimeout)
|
||||||
|
{
|
||||||
|
dev_dbg(wdd->parent, "%s pretimeout: %d sec\n", __func__, pretimeout);
|
||||||
|
|
||||||
|
wdd->pretimeout = pretimeout;
|
||||||
|
|
||||||
|
if (watchdog_active(wdd))
|
||||||
|
return stm32_iwdg_start(wdd);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static irqreturn_t stm32_iwdg_isr(int irq, void *wdog_arg)
|
||||||
|
{
|
||||||
|
struct watchdog_device *wdd = wdog_arg;
|
||||||
|
struct stm32_iwdg *wdt = watchdog_get_drvdata(wdd);
|
||||||
|
u32 reg;
|
||||||
|
|
||||||
|
reg = reg_read(wdt->regs, IWDG_EWCR);
|
||||||
|
reg |= EWCR_EWIC;
|
||||||
|
reg_write(wdt->regs, IWDG_EWCR, reg);
|
||||||
|
|
||||||
|
watchdog_notify_pretimeout(wdd);
|
||||||
|
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
static void stm32_clk_disable_unprepare(void *data)
|
static void stm32_clk_disable_unprepare(void *data)
|
||||||
{
|
{
|
||||||
clk_disable_unprepare(data);
|
clk_disable_unprepare(data);
|
||||||
@ -207,11 +252,20 @@ static const struct watchdog_info stm32_iwdg_info = {
|
|||||||
.identity = "STM32 Independent Watchdog",
|
.identity = "STM32 Independent Watchdog",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct watchdog_info stm32_iwdg_preinfo = {
|
||||||
|
.options = WDIOF_SETTIMEOUT |
|
||||||
|
WDIOF_MAGICCLOSE |
|
||||||
|
WDIOF_KEEPALIVEPING |
|
||||||
|
WDIOF_PRETIMEOUT,
|
||||||
|
.identity = "STM32 Independent Watchdog",
|
||||||
|
};
|
||||||
|
|
||||||
static const struct watchdog_ops stm32_iwdg_ops = {
|
static const struct watchdog_ops stm32_iwdg_ops = {
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
.start = stm32_iwdg_start,
|
.start = stm32_iwdg_start,
|
||||||
.ping = stm32_iwdg_ping,
|
.ping = stm32_iwdg_ping,
|
||||||
.set_timeout = stm32_iwdg_set_timeout,
|
.set_timeout = stm32_iwdg_set_timeout,
|
||||||
|
.set_pretimeout = stm32_iwdg_set_pretimeout,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct of_device_id stm32_iwdg_of_match[] = {
|
static const struct of_device_id stm32_iwdg_of_match[] = {
|
||||||
@ -221,6 +275,40 @@ static const struct of_device_id stm32_iwdg_of_match[] = {
|
|||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(of, stm32_iwdg_of_match);
|
MODULE_DEVICE_TABLE(of, stm32_iwdg_of_match);
|
||||||
|
|
||||||
|
static int stm32_iwdg_irq_init(struct platform_device *pdev,
|
||||||
|
struct stm32_iwdg *wdt)
|
||||||
|
{
|
||||||
|
struct device_node *np = pdev->dev.of_node;
|
||||||
|
struct watchdog_device *wdd = &wdt->wdd;
|
||||||
|
struct device *dev = &pdev->dev;
|
||||||
|
int irq, ret;
|
||||||
|
|
||||||
|
if (!wdt->data->has_early_wakeup)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
irq = platform_get_irq(pdev, 0);
|
||||||
|
if (irq <= 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (of_property_read_bool(np, "wakeup-source")) {
|
||||||
|
ret = device_init_wakeup(dev, true);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = dev_pm_set_wake_irq(dev, irq);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = devm_request_irq(dev, irq, stm32_iwdg_isr, 0,
|
||||||
|
dev_name(dev), wdd);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
wdd->info = &stm32_iwdg_preinfo;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int stm32_iwdg_probe(struct platform_device *pdev)
|
static int stm32_iwdg_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct device *dev = &pdev->dev;
|
struct device *dev = &pdev->dev;
|
||||||
@ -255,6 +343,11 @@ static int stm32_iwdg_probe(struct platform_device *pdev)
|
|||||||
wdd->max_hw_heartbeat_ms = ((RLR_MAX + 1) * wdt->data->max_prescaler *
|
wdd->max_hw_heartbeat_ms = ((RLR_MAX + 1) * wdt->data->max_prescaler *
|
||||||
1000) / wdt->rate;
|
1000) / wdt->rate;
|
||||||
|
|
||||||
|
/* Initialize IRQ, this might override wdd->info, hence it is here. */
|
||||||
|
ret = stm32_iwdg_irq_init(pdev, wdt);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
watchdog_set_drvdata(wdd, wdt);
|
watchdog_set_drvdata(wdd, wdt);
|
||||||
watchdog_set_nowayout(wdd, WATCHDOG_NOWAYOUT);
|
watchdog_set_nowayout(wdd, WATCHDOG_NOWAYOUT);
|
||||||
watchdog_init_timeout(wdd, 0, dev);
|
watchdog_init_timeout(wdd, 0, dev);
|
||||||
|
@ -237,7 +237,7 @@ void watchdog_set_restart_priority(struct watchdog_device *wdd, int priority)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(watchdog_set_restart_priority);
|
EXPORT_SYMBOL_GPL(watchdog_set_restart_priority);
|
||||||
|
|
||||||
static int __watchdog_register_device(struct watchdog_device *wdd)
|
static int ___watchdog_register_device(struct watchdog_device *wdd)
|
||||||
{
|
{
|
||||||
int ret, id = -1;
|
int ret, id = -1;
|
||||||
|
|
||||||
@ -337,6 +337,22 @@ static int __watchdog_register_device(struct watchdog_device *wdd)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int __watchdog_register_device(struct watchdog_device *wdd)
|
||||||
|
{
|
||||||
|
const char *dev_str;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = ___watchdog_register_device(wdd);
|
||||||
|
if (ret) {
|
||||||
|
dev_str = wdd->parent ? dev_name(wdd->parent) :
|
||||||
|
(const char *)wdd->info->identity;
|
||||||
|
pr_err("%s: failed to register watchdog device (err = %d)\n",
|
||||||
|
dev_str, ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* watchdog_register_device() - register a watchdog device
|
* watchdog_register_device() - register a watchdog device
|
||||||
* @wdd: watchdog device
|
* @wdd: watchdog device
|
||||||
@ -350,7 +366,6 @@ static int __watchdog_register_device(struct watchdog_device *wdd)
|
|||||||
|
|
||||||
int watchdog_register_device(struct watchdog_device *wdd)
|
int watchdog_register_device(struct watchdog_device *wdd)
|
||||||
{
|
{
|
||||||
const char *dev_str;
|
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
mutex_lock(&wtd_deferred_reg_mutex);
|
mutex_lock(&wtd_deferred_reg_mutex);
|
||||||
@ -360,13 +375,6 @@ int watchdog_register_device(struct watchdog_device *wdd)
|
|||||||
watchdog_deferred_registration_add(wdd);
|
watchdog_deferred_registration_add(wdd);
|
||||||
mutex_unlock(&wtd_deferred_reg_mutex);
|
mutex_unlock(&wtd_deferred_reg_mutex);
|
||||||
|
|
||||||
if (ret) {
|
|
||||||
dev_str = wdd->parent ? dev_name(wdd->parent) :
|
|
||||||
(const char *)wdd->info->identity;
|
|
||||||
pr_err("%s: failed to register watchdog device (err = %d)\n",
|
|
||||||
dev_str, ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(watchdog_register_device);
|
EXPORT_SYMBOL_GPL(watchdog_register_device);
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
/*
|
/*
|
||||||
* Window watchdog device driver for Xilinx Versal WWDT
|
* Window watchdog device driver for Xilinx Versal WWDT
|
||||||
*
|
*
|
||||||
* Copyright (C) 2022 - 2023, Advanced Micro Devices, Inc.
|
* Copyright (C) 2022 - 2024, Advanced Micro Devices, Inc.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
@ -36,6 +36,12 @@
|
|||||||
|
|
||||||
#define XWWDT_CLOSE_WINDOW_PERCENT 50
|
#define XWWDT_CLOSE_WINDOW_PERCENT 50
|
||||||
|
|
||||||
|
/* Maximum count value of each 32 bit window */
|
||||||
|
#define XWWDT_MAX_COUNT_WINDOW GENMASK(31, 0)
|
||||||
|
|
||||||
|
/* Maximum count value of closed and open window combined */
|
||||||
|
#define XWWDT_MAX_COUNT_WINDOW_COMBINED GENMASK_ULL(32, 1)
|
||||||
|
|
||||||
static int wwdt_timeout;
|
static int wwdt_timeout;
|
||||||
static int closed_window_percent;
|
static int closed_window_percent;
|
||||||
|
|
||||||
@ -54,6 +60,8 @@ MODULE_PARM_DESC(closed_window_percent,
|
|||||||
* @xilinx_wwdt_wdd: watchdog device structure
|
* @xilinx_wwdt_wdd: watchdog device structure
|
||||||
* @freq: source clock frequency of WWDT
|
* @freq: source clock frequency of WWDT
|
||||||
* @close_percent: Closed window percent
|
* @close_percent: Closed window percent
|
||||||
|
* @closed_timeout: Closed window timeout in ticks
|
||||||
|
* @open_timeout: Open window timeout in ticks
|
||||||
*/
|
*/
|
||||||
struct xwwdt_device {
|
struct xwwdt_device {
|
||||||
void __iomem *base;
|
void __iomem *base;
|
||||||
@ -61,27 +69,22 @@ struct xwwdt_device {
|
|||||||
struct watchdog_device xilinx_wwdt_wdd;
|
struct watchdog_device xilinx_wwdt_wdd;
|
||||||
unsigned long freq;
|
unsigned long freq;
|
||||||
u32 close_percent;
|
u32 close_percent;
|
||||||
|
u64 closed_timeout;
|
||||||
|
u64 open_timeout;
|
||||||
};
|
};
|
||||||
|
|
||||||
static int xilinx_wwdt_start(struct watchdog_device *wdd)
|
static int xilinx_wwdt_start(struct watchdog_device *wdd)
|
||||||
{
|
{
|
||||||
struct xwwdt_device *xdev = watchdog_get_drvdata(wdd);
|
struct xwwdt_device *xdev = watchdog_get_drvdata(wdd);
|
||||||
struct watchdog_device *xilinx_wwdt_wdd = &xdev->xilinx_wwdt_wdd;
|
struct watchdog_device *xilinx_wwdt_wdd = &xdev->xilinx_wwdt_wdd;
|
||||||
u64 time_out, closed_timeout, open_timeout;
|
|
||||||
u32 control_status_reg;
|
u32 control_status_reg;
|
||||||
|
|
||||||
/* Calculate timeout count */
|
|
||||||
time_out = xdev->freq * wdd->timeout;
|
|
||||||
closed_timeout = div_u64(time_out * xdev->close_percent, 100);
|
|
||||||
open_timeout = time_out - closed_timeout;
|
|
||||||
wdd->min_hw_heartbeat_ms = xdev->close_percent * 10 * wdd->timeout;
|
|
||||||
|
|
||||||
spin_lock(&xdev->spinlock);
|
spin_lock(&xdev->spinlock);
|
||||||
|
|
||||||
iowrite32(XWWDT_MWR_MASK, xdev->base + XWWDT_MWR_OFFSET);
|
iowrite32(XWWDT_MWR_MASK, xdev->base + XWWDT_MWR_OFFSET);
|
||||||
iowrite32(~(u32)XWWDT_ESR_WEN_MASK, xdev->base + XWWDT_ESR_OFFSET);
|
iowrite32(~(u32)XWWDT_ESR_WEN_MASK, xdev->base + XWWDT_ESR_OFFSET);
|
||||||
iowrite32((u32)closed_timeout, xdev->base + XWWDT_FWR_OFFSET);
|
iowrite32((u32)xdev->closed_timeout, xdev->base + XWWDT_FWR_OFFSET);
|
||||||
iowrite32((u32)open_timeout, xdev->base + XWWDT_SWR_OFFSET);
|
iowrite32((u32)xdev->open_timeout, xdev->base + XWWDT_SWR_OFFSET);
|
||||||
|
|
||||||
/* Enable the window watchdog timer */
|
/* Enable the window watchdog timer */
|
||||||
control_status_reg = ioread32(xdev->base + XWWDT_ESR_OFFSET);
|
control_status_reg = ioread32(xdev->base + XWWDT_ESR_OFFSET);
|
||||||
@ -133,7 +136,12 @@ static int xwwdt_probe(struct platform_device *pdev)
|
|||||||
struct watchdog_device *xilinx_wwdt_wdd;
|
struct watchdog_device *xilinx_wwdt_wdd;
|
||||||
struct device *dev = &pdev->dev;
|
struct device *dev = &pdev->dev;
|
||||||
struct xwwdt_device *xdev;
|
struct xwwdt_device *xdev;
|
||||||
|
u64 max_per_window_ms;
|
||||||
|
u64 min_per_window_ms;
|
||||||
|
u64 timeout_count;
|
||||||
struct clk *clk;
|
struct clk *clk;
|
||||||
|
u32 timeout_ms;
|
||||||
|
u64 ms_count;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
xdev = devm_kzalloc(dev, sizeof(*xdev), GFP_KERNEL);
|
xdev = devm_kzalloc(dev, sizeof(*xdev), GFP_KERNEL);
|
||||||
@ -154,12 +162,13 @@ static int xwwdt_probe(struct platform_device *pdev)
|
|||||||
return PTR_ERR(clk);
|
return PTR_ERR(clk);
|
||||||
|
|
||||||
xdev->freq = clk_get_rate(clk);
|
xdev->freq = clk_get_rate(clk);
|
||||||
if (!xdev->freq)
|
if (xdev->freq < 1000000)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
xilinx_wwdt_wdd->min_timeout = XWWDT_MIN_TIMEOUT;
|
xilinx_wwdt_wdd->min_timeout = XWWDT_MIN_TIMEOUT;
|
||||||
xilinx_wwdt_wdd->timeout = XWWDT_DEFAULT_TIMEOUT;
|
xilinx_wwdt_wdd->timeout = XWWDT_DEFAULT_TIMEOUT;
|
||||||
xilinx_wwdt_wdd->max_hw_heartbeat_ms = 1000 * xilinx_wwdt_wdd->timeout;
|
xilinx_wwdt_wdd->max_hw_heartbeat_ms =
|
||||||
|
div64_u64(XWWDT_MAX_COUNT_WINDOW_COMBINED, xdev->freq) * 1000;
|
||||||
|
|
||||||
if (closed_window_percent == 0 || closed_window_percent >= 100)
|
if (closed_window_percent == 0 || closed_window_percent >= 100)
|
||||||
xdev->close_percent = XWWDT_CLOSE_WINDOW_PERCENT;
|
xdev->close_percent = XWWDT_CLOSE_WINDOW_PERCENT;
|
||||||
@ -167,6 +176,48 @@ static int xwwdt_probe(struct platform_device *pdev)
|
|||||||
xdev->close_percent = closed_window_percent;
|
xdev->close_percent = closed_window_percent;
|
||||||
|
|
||||||
watchdog_init_timeout(xilinx_wwdt_wdd, wwdt_timeout, &pdev->dev);
|
watchdog_init_timeout(xilinx_wwdt_wdd, wwdt_timeout, &pdev->dev);
|
||||||
|
|
||||||
|
/* Calculate ticks for 1 milli-second */
|
||||||
|
ms_count = div_u64(xdev->freq, 1000);
|
||||||
|
timeout_ms = xilinx_wwdt_wdd->timeout * 1000;
|
||||||
|
timeout_count = timeout_ms * ms_count;
|
||||||
|
|
||||||
|
if (timeout_ms > xilinx_wwdt_wdd->max_hw_heartbeat_ms) {
|
||||||
|
/*
|
||||||
|
* To avoid ping restrictions until the minimum hardware heartbeat,
|
||||||
|
* we will solely rely on the open window and
|
||||||
|
* adjust the minimum hardware heartbeat to 0.
|
||||||
|
*/
|
||||||
|
xdev->closed_timeout = 0;
|
||||||
|
xdev->open_timeout = XWWDT_MAX_COUNT_WINDOW;
|
||||||
|
xilinx_wwdt_wdd->min_hw_heartbeat_ms = 0;
|
||||||
|
xilinx_wwdt_wdd->max_hw_heartbeat_ms = xilinx_wwdt_wdd->max_hw_heartbeat_ms / 2;
|
||||||
|
} else {
|
||||||
|
xdev->closed_timeout = div64_u64(timeout_count * xdev->close_percent, 100);
|
||||||
|
xilinx_wwdt_wdd->min_hw_heartbeat_ms =
|
||||||
|
div64_u64(timeout_ms * xdev->close_percent, 100);
|
||||||
|
|
||||||
|
if (timeout_ms > xilinx_wwdt_wdd->max_hw_heartbeat_ms / 2) {
|
||||||
|
max_per_window_ms = xilinx_wwdt_wdd->max_hw_heartbeat_ms / 2;
|
||||||
|
min_per_window_ms = timeout_ms - max_per_window_ms;
|
||||||
|
|
||||||
|
if (xilinx_wwdt_wdd->min_hw_heartbeat_ms > max_per_window_ms) {
|
||||||
|
dev_info(xilinx_wwdt_wdd->parent,
|
||||||
|
"Closed window cannot be set to %d%%. Using maximum supported value.\n",
|
||||||
|
xdev->close_percent);
|
||||||
|
xdev->closed_timeout = max_per_window_ms * ms_count;
|
||||||
|
xilinx_wwdt_wdd->min_hw_heartbeat_ms = max_per_window_ms;
|
||||||
|
} else if (xilinx_wwdt_wdd->min_hw_heartbeat_ms < min_per_window_ms) {
|
||||||
|
dev_info(xilinx_wwdt_wdd->parent,
|
||||||
|
"Closed window cannot be set to %d%%. Using minimum supported value.\n",
|
||||||
|
xdev->close_percent);
|
||||||
|
xdev->closed_timeout = min_per_window_ms * ms_count;
|
||||||
|
xilinx_wwdt_wdd->min_hw_heartbeat_ms = min_per_window_ms;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
xdev->open_timeout = timeout_count - xdev->closed_timeout;
|
||||||
|
}
|
||||||
|
|
||||||
spin_lock_init(&xdev->spinlock);
|
spin_lock_init(&xdev->spinlock);
|
||||||
watchdog_set_drvdata(xilinx_wwdt_wdd, xdev);
|
watchdog_set_drvdata(xilinx_wwdt_wdd, xdev);
|
||||||
watchdog_set_nowayout(xilinx_wwdt_wdd, 1);
|
watchdog_set_nowayout(xilinx_wwdt_wdd, 1);
|
||||||
|
@ -715,7 +715,7 @@ static void ziirave_wdt_remove(struct i2c_client *client)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static const struct i2c_device_id ziirave_wdt_id[] = {
|
static const struct i2c_device_id ziirave_wdt_id[] = {
|
||||||
{ "rave-wdt", 0 },
|
{ "rave-wdt" },
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(i2c, ziirave_wdt_id);
|
MODULE_DEVICE_TABLE(i2c, ziirave_wdt_id);
|
||||||
|
@ -78,6 +78,7 @@ struct da9063 {
|
|||||||
enum da9063_type type;
|
enum da9063_type type;
|
||||||
unsigned char variant_code;
|
unsigned char variant_code;
|
||||||
unsigned int flags;
|
unsigned int flags;
|
||||||
|
bool use_sw_pm;
|
||||||
|
|
||||||
/* Control interface */
|
/* Control interface */
|
||||||
struct regmap *regmap;
|
struct regmap *regmap;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user