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:
Linus Torvalds 2024-12-05 10:03:43 -08:00
commit 42d52acfb1
35 changed files with 631 additions and 394 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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[] = {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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