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
is an optional feature.
What: /sys/class/watchdog/watchdogn/pretimeout_avaialable_governors
What: /sys/class/watchdog/watchdogn/pretimeout_available_governors
Date: February 2017
Contact: Wim Van Sebroeck <wim@iguana.be>
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:
maxItems: 1
big-endian: true
fsl,ext-reset-output:
$ref: /schemas/types.yaml#/definitions/flag
description: |
@ -93,6 +95,18 @@ allOf:
properties:
fsl,suspend-in-wait: false
- if:
not:
properties:
compatible:
contains:
enum:
- fsl,ls1012a-wdt
- fsl,ls1043a-wdt
then:
properties:
big-endian: false
unevaluatedProperties: false
examples:

View File

@ -26,6 +26,8 @@ properties:
- qcom,apss-wdt-msm8994
- qcom,apss-wdt-qcm2290
- qcom,apss-wdt-qcs404
- qcom,apss-wdt-qcs615
- qcom,apss-wdt-qcs8300
- qcom,apss-wdt-sa8255p
- qcom,apss-wdt-sa8775p
- qcom,apss-wdt-sc7180

View File

@ -26,6 +26,7 @@ properties:
- samsung,exynos7-wdt # for Exynos7
- samsung,exynos850-wdt # for Exynos850
- samsung,exynosautov9-wdt # for Exynosautov9
- samsung,exynosautov920-wdt # for Exynosautov920
- items:
- enum:
- tesla,fsd-wdt
@ -77,6 +78,7 @@ allOf:
- samsung,exynos7-wdt
- samsung,exynos850-wdt
- samsung,exynosautov9-wdt
- samsung,exynosautov920-wdt
then:
required:
- samsung,syscon-phandle
@ -88,6 +90,7 @@ allOf:
- google,gs101-wdt
- samsung,exynos850-wdt
- samsung,exynosautov9-wdt
- samsung,exynosautov920-wdt
then:
properties:
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:
wd0_timeout:
Default watchdog0 timeout in 1/10secs

View File

@ -22407,7 +22407,7 @@ F: drivers/char/hw_random/jh7110-trng.c
STARFIVE WATCHDOG DRIVER
M: Xingyu Wu <xingyu.wu@starfivetech.com>
M: Samin Guo <samin.guo@starfivetech.com>
M: Ziv Xu <ziv.xu@starfivetech.com>
S: Supported
F: Documentation/devicetree/bindings/watchdog/starfive*
F: drivers/watchdog/starfive-wdt.c

View File

@ -408,6 +408,14 @@ config SL28CPLD_WATCHDOG
# 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
tristate "ARM SP805 Watchdog"
depends on (ARM || ARM64 || COMPILE_TEST) && ARM_AMBA
@ -549,6 +557,7 @@ config S3C2410_WATCHDOG
tristate "S3C6410/S5Pv210/Exynos Watchdog"
depends on ARCH_S3C64XX || ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST
select WATCHDOG_CORE
select MFD_SYSCON if ARCH_EXYNOS
help
Watchdog timer block in the Samsung S3C64xx, S5Pv210 and Exynos
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
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
tristate "SMSC SCH311X Watchdog Timer"
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_SBSA_WATCHDOG) += sbsa_gwdt.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_AT91RM9200_WATCHDOG) += at91rm9200_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_SBC8360_WDT) += sbc8360.o
obj-$(CONFIG_SBC7240_WDT) += sbc7240_wdt.o
obj-$(CONFIG_CPU5_WDT) += cpu5wdt.o
obj-$(CONFIG_SMSC_SCH311X_WDT) += sch311x_wdt.o
obj-$(CONFIG_SMSC37B787_WDT) += smsc37b787_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
* reset is queued almost immediately experiments have shown that it
* can take up to ~20-25ms until the SoC is actually reset. Just wait
* 50ms here to be safe.
* can take up to ~120-125ms until the SoC is actually reset. Just
* wait 150ms here to be safe.
*/
(void)readl_relaxed(wdt->regs + APPLE_WDT_WD1_CUR_TIME);
mdelay(50);
(void)readl(wdt->regs + APPLE_WDT_WD1_CUR_TIME);
mdelay(150);
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)
{
struct armada_37xx_watchdog *dev;
struct resource *res;
struct regmap *regmap;
int ret;
@ -266,12 +265,9 @@ static int armada_37xx_wdt_probe(struct platform_device *pdev)
return PTR_ERR(regmap);
dev->cpu_misc = regmap;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -ENODEV;
dev->reg = devm_ioremap(&pdev->dev, res->start, resource_size(res));
if (!dev->reg)
return -ENOMEM;
dev->reg = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(dev->reg))
return PTR_ERR(dev->reg);
/* init clock */
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;
}
ret = devm_watchdog_register_device(dev, &driver_data->wdt);
if (ret != 0)
dev_err(da9055->dev, "watchdog_register_device() failed: %d\n",
ret);
return ret;
return devm_watchdog_register_device(dev, &driver_data->wdt);
}
static struct platform_driver da9055_wdt_driver = {

View File

@ -27,7 +27,6 @@
* others: timeout = 2048 ms * 2^(TWDSCALE-1).
*/
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_MIN 1
@ -230,7 +229,7 @@ static int da9063_wdt_probe(struct platform_device *pdev)
if (!wdd)
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->ops = &da9063_watchdog_ops;
@ -264,11 +263,12 @@ static int da9063_wdt_probe(struct platform_device *pdev)
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 da9063 *da9063 = watchdog_get_drvdata(wdd);
if (!use_sw_pm)
if (!da9063->use_sw_pm)
return 0;
if (watchdog_active(wdd))
@ -277,11 +277,12 @@ static int __maybe_unused da9063_wdt_suspend(struct device *dev)
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 da9063 *da9063 = watchdog_get_drvdata(wdd);
if (!use_sw_pm)
if (!da9063->use_sw_pm)
return 0;
if (watchdog_active(wdd))
@ -290,14 +291,14 @@ static int __maybe_unused da9063_wdt_resume(struct device *dev)
return 0;
}
static SIMPLE_DEV_PM_OPS(da9063_wdt_pm_ops,
da9063_wdt_suspend, da9063_wdt_resume);
static DEFINE_SIMPLE_DEV_PM_OPS(da9063_wdt_pm_ops, da9063_wdt_suspend,
da9063_wdt_resume);
static struct platform_driver da9063_wdt_driver = {
.probe = da9063_wdt_probe,
.driver = {
.name = DA9063_DRVNAME_WATCHDOG,
.pm = &da9063_wdt_pm_ops,
.pm = pm_sleep_ptr(&da9063_wdt_pm_ops),
},
};
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);
err = devm_watchdog_register_device(dev, &drvdata->wdd);
if (err) {
dev_err(dev, "Failed to register watchdog device");
if (err)
return err;
}
dev_info(dev, "HPE GXP watchdog timer");

View File

@ -82,6 +82,13 @@
#define TCO2_CNT(p) (TCOBASE(p) + 0x0a) /* TCO2 Control Register */
#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 */
struct iTCO_wdt_private {
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;
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)
val |= BIT(0);
else
val &= ~BIT(0);
outw(val, TCO1_CNT(p));
newval = inw(TCO1_CNT(p));
newval = inw(TCO1_CNT(p)) & ~NMI_NOW;
/* make sure the update is successful */
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_unregister(&p->wddev);
ret = devm_watchdog_register_device(dev, &p->wddev);
if (ret != 0) {
dev_err(dev, "cannot register watchdog device (err=%d)\n", ret);
if (ret != 0)
return ret;
}
dev_info(dev, "initialized. heartbeat=%d sec (nowayout=%d)\n",
heartbeat, nowayout);

View File

@ -20,6 +20,8 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/bits.h>
#include <linux/dmi.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/kernel.h>
@ -40,6 +42,7 @@
#define VAL 0x2f
/* Logical device Numbers LDN */
#define EC 0x04
#define GPIO 0x07
/* Configuration Registers and Functions */
@ -73,6 +76,12 @@
#define IT8784_ID 0x8784
#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 */
#define WDTCTRL 0x71
#define WDTCFG 0x72
@ -240,6 +249,21 @@ static int wdt_set_timeout(struct watchdog_device *wdd, unsigned int t)
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 = {
.options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING,
.firmware_version = 1,
@ -261,8 +285,10 @@ static struct watchdog_device wdt_dev = {
static int __init it87_wdt_init(void)
{
const struct dmi_system_id *dmi_id;
u8 chip_rev;
u8 ctrl;
int quirks = 0;
int rc;
rc = superio_enter();
@ -273,6 +299,10 @@ static int __init it87_wdt_init(void)
chip_rev = superio_inb(CHIPREV) & 0x0f;
superio_exit();
dmi_id = dmi_first_match(it87_quirks);
if (dmi_id)
quirks = (long)dmi_id->driver_data;
switch (chip_type) {
case IT8702_ID:
max_units = 255;
@ -333,6 +363,15 @@ static int __init it87_wdt_init(void)
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();
if (timeout < 1 || timeout > max_units * 60) {
@ -349,10 +388,8 @@ static int __init it87_wdt_init(void)
watchdog_stop_on_reboot(&wdt_dev);
rc = watchdog_register_device(&wdt_dev);
if (rc) {
pr_err("Cannot register watchdog device (err=%d)\n", rc);
if (rc)
return rc;
}
pr_info("Chip IT%04x revision %d initialized. timeout=%d sec (nowayout=%d testmode=%d)\n",
chip_type, chip_rev, timeout, nowayout, testmode);

View File

@ -10,6 +10,7 @@
*/
#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/mt7986-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,
};
static const struct mtk_wdt_data mt6735_data = {
.toprgu_sw_rst_num = MT6735_TOPRGU_RST_NUM,
};
static const struct mtk_wdt_data mt6795_data = {
.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);
void __iomem *wdt_base;
u32 reg;
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) {
writel(WDT_SWRST_KEY, wdt_base + WDT_SWRST);
mdelay(5);
@ -483,6 +494,7 @@ static int mtk_wdt_resume(struct device *dev)
static const struct of_device_id mtk_wdt_dt_ids[] = {
{ .compatible = "mediatek,mt2712-wdt", .data = &mt2712_data },
{ .compatible = "mediatek,mt6589-wdt" },
{ .compatible = "mediatek,mt6735-wdt", .data = &mt6735_data },
{ .compatible = "mediatek,mt6795-wdt", .data = &mt6795_data },
{ .compatible = "mediatek,mt7986-wdt", .data = &mt7986_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);
ret = watchdog_register_device(&octeon_wdt);
if (ret) {
pr_err("watchdog_register_device() failed: %d\n", ret);
if (ret)
return ret;
}
if (disable) {
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);
port1 = inb_p(base_addr + 1);
/* Has either hearbeat bit changed? */
/* Has either heartbeat bit changed? */
if ((port0 ^ last_port0) & WD_HRTBT ||
(port1 ^ last_port1) & WD_REVC_HRBT) {
retval = 1;

View File

@ -61,7 +61,7 @@
#define MAX_HW_ERROR 250
static int heartbeat = DEFAULT_HEARTBEAT;
static int heartbeat;
/*
* 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->max_hw_heartbeat_ms = (WDT_PRELOAD_MAX << WDT_PRELOAD_SHIFT) /
wdt->freq * 1000;
wdd->timeout = DEFAULT_HEARTBEAT;
wdd->parent = dev;
watchdog_set_drvdata(wdd, wdt);
@ -336,10 +337,8 @@ static int rti_wdt_probe(struct platform_device *pdev)
watchdog_init_timeout(wdd, heartbeat, dev);
ret = watchdog_register_device(wdd);
if (ret) {
dev_err(dev, "cannot register watchdog device\n");
if (ret)
goto err_iomap;
}
if (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 rza_wdt *priv;
unsigned long rate;
int ret;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
@ -218,11 +217,7 @@ static int rza_wdt_probe(struct platform_device *pdev)
watchdog_init_timeout(&priv->wdev, 0, dev);
watchdog_set_drvdata(&priv->wdev, priv);
ret = devm_watchdog_register_device(dev, &priv->wdev);
if (ret)
dev_err(dev, "Cannot register watchdog device\n");
return ret;
return devm_watchdog_register_device(dev, &priv->wdev);
}
static const struct of_device_id rza_wdt_of_match[] = {

View File

@ -12,6 +12,7 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_domain.h>
#include <linux/pm_runtime.h>
#include <linux/reset.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);
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) {
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);
pm_runtime_irq_safe(&pdev->dev);
pm_runtime_enable(&pdev->dev);
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);
/* Any value retrigggers the watchdog */
/* Any value retriggers the watchdog */
writel(0, wdt->base + RZN1_WDT_RETRIGGER);
return 0;

View File

@ -24,9 +24,9 @@
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/of.h>
#include <linux/mfd/syscon.h>
#include <linux/regmap.h>
#include <linux/delay.h>
#include <linux/soc/samsung/exynos-pmu.h>
#define S3C2410_WTCON 0x00
#define S3C2410_WTDAT 0x04
@ -63,6 +63,10 @@
#define EXYNOS850_CLUSTER1_NONCPU_INT_EN 0x1644
#define EXYNOSAUTOV9_CLUSTER1_NONCPU_OUT 0x1520
#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_CLUSTER1_WDTRESET_BIT 23
@ -303,6 +307,32 @@ static const struct s3c2410_wdt_variant drv_data_gs101_cl1 = {
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[] = {
{ .compatible = "google,gs101-wdt",
.data = &drv_data_gs101_cl0 },
@ -320,6 +350,8 @@ static const struct of_device_id s3c2410_wdt_match[] = {
.data = &drv_data_exynos850_cl0 },
{ .compatible = "samsung,exynosautov9-wdt",
.data = &drv_data_exynosautov9_cl0 },
{ .compatible = "samsung,exynosautov920-wdt",
.data = &drv_data_exynosautov920_cl0 },
{},
};
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 */
if (variant == &drv_data_exynos850_cl0 ||
variant == &drv_data_exynosautov9_cl0 ||
variant == &drv_data_gs101_cl0) {
variant == &drv_data_gs101_cl0 ||
variant == &drv_data_exynosautov920_cl0) {
u32 index;
int err;
@ -662,6 +695,8 @@ s3c2410_get_wdt_drv_data(struct platform_device *pdev, struct s3c2410_wdt *wdt)
variant = &drv_data_exynosautov9_cl1;
else if (variant == &drv_data_gs101_cl0)
variant = &drv_data_gs101_cl1;
else if (variant == &drv_data_exynosautov920_cl0)
variant = &drv_data_exynosautov920_cl1;
break;
default:
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;
if (wdt->drv_data->quirks & QUIRKS_HAVE_PMUREG) {
wdt->pmureg = exynos_get_pmu_regmap_by_phandle(dev->of_node,
"samsung,syscon-phandle");
wdt->pmureg = syscon_regmap_lookup_by_phandle(dev->of_node,
"samsung,syscon-phandle");
if (IS_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);

View File

@ -236,8 +236,8 @@ static void sa1100dog_remove(struct platform_device *pdev)
static struct platform_driver sa1100dog_driver = {
.driver.name = "sa1100_wdt",
.probe = sa1100dog_probe,
.remove = sa1100dog_remove,
.probe = sa1100dog_probe,
.remove = sa1100dog_remove,
};
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);
if (ret < 0) {
dev_err(&pdev->dev, "failed to register watchdog device\n");
if (ret < 0)
return ret;
}
dev_info(&pdev->dev, "initial timeout %d sec%s\n",
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,
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) ")");
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 reload; /* Watchdog Reload Control register */
unsigned int enable; /* Watchdog Enable Register */

View File

@ -18,6 +18,7 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_wakeirq.h>
#include <linux/watchdog.h>
#define DEFAULT_TIMEOUT 10
@ -28,6 +29,7 @@
#define IWDG_RLR 0x08 /* ReLoad Register */
#define IWDG_SR 0x0C /* Status Register */
#define IWDG_WINR 0x10 /* Windows Register */
#define IWDG_EWCR 0x14 /* Early Wake-up Register */
/* IWDG_KR register bit mask */
#define KR_KEY_RELOAD 0xAAAA /* reload counter enable */
@ -47,22 +49,29 @@
#define SR_PVU BIT(0) /* Watchdog prescaler 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 */
#define TIMEOUT_US 100000
#define SLEEP_US 1000
struct stm32_iwdg_data {
bool has_pclk;
bool has_early_wakeup;
u32 max_prescaler;
};
static const struct stm32_iwdg_data stm32_iwdg_data = {
.has_pclk = false,
.has_early_wakeup = false,
.max_prescaler = 256,
};
static const struct stm32_iwdg_data stm32mp1_iwdg_data = {
.has_pclk = true,
.has_early_wakeup = true,
.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)
{
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;
dev_dbg(wdd->parent, "%s\n", __func__);
if (!wdd->pretimeout)
wdd->pretimeout = 3 * wdd->timeout / 4;
tout = clamp_t(unsigned int, wdd->timeout,
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);
@ -102,6 +116,7 @@ static int stm32_iwdg_start(struct watchdog_device *wdd)
presc = roundup_pow_of_two(presc);
iwdg_pr = presc <= 1 << PR_SHIFT ? 0 : ilog2(presc) - PR_SHIFT;
iwdg_rlr = ((tout * wdt->rate) / presc) - 1;
iwdg_ewcr = ((ptot * wdt->rate) / presc) - 1;
/* enable write access */
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 */
reg_write(wdt->regs, IWDG_PR, iwdg_pr);
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);
/* 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;
}
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)
{
clk_disable_unprepare(data);
@ -207,11 +252,20 @@ static const struct watchdog_info stm32_iwdg_info = {
.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 = {
.owner = THIS_MODULE,
.start = stm32_iwdg_start,
.ping = stm32_iwdg_ping,
.set_timeout = stm32_iwdg_set_timeout,
.set_pretimeout = stm32_iwdg_set_pretimeout,
};
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);
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)
{
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 *
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_nowayout(wdd, WATCHDOG_NOWAYOUT);
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);
static int __watchdog_register_device(struct watchdog_device *wdd)
static int ___watchdog_register_device(struct watchdog_device *wdd)
{
int ret, id = -1;
@ -337,6 +337,22 @@ static int __watchdog_register_device(struct watchdog_device *wdd)
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
* @wdd: watchdog device
@ -350,7 +366,6 @@ static int __watchdog_register_device(struct watchdog_device *wdd)
int watchdog_register_device(struct watchdog_device *wdd)
{
const char *dev_str;
int ret = 0;
mutex_lock(&wtd_deferred_reg_mutex);
@ -360,13 +375,6 @@ int watchdog_register_device(struct watchdog_device *wdd)
watchdog_deferred_registration_add(wdd);
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;
}
EXPORT_SYMBOL_GPL(watchdog_register_device);

View File

@ -2,7 +2,7 @@
/*
* 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>
@ -36,6 +36,12 @@
#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 closed_window_percent;
@ -54,6 +60,8 @@ MODULE_PARM_DESC(closed_window_percent,
* @xilinx_wwdt_wdd: watchdog device structure
* @freq: source clock frequency of WWDT
* @close_percent: Closed window percent
* @closed_timeout: Closed window timeout in ticks
* @open_timeout: Open window timeout in ticks
*/
struct xwwdt_device {
void __iomem *base;
@ -61,27 +69,22 @@ struct xwwdt_device {
struct watchdog_device xilinx_wwdt_wdd;
unsigned long freq;
u32 close_percent;
u64 closed_timeout;
u64 open_timeout;
};
static int xilinx_wwdt_start(struct watchdog_device *wdd)
{
struct xwwdt_device *xdev = watchdog_get_drvdata(wdd);
struct watchdog_device *xilinx_wwdt_wdd = &xdev->xilinx_wwdt_wdd;
u64 time_out, closed_timeout, open_timeout;
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);
iowrite32(XWWDT_MWR_MASK, xdev->base + XWWDT_MWR_OFFSET);
iowrite32(~(u32)XWWDT_ESR_WEN_MASK, xdev->base + XWWDT_ESR_OFFSET);
iowrite32((u32)closed_timeout, xdev->base + XWWDT_FWR_OFFSET);
iowrite32((u32)open_timeout, xdev->base + XWWDT_SWR_OFFSET);
iowrite32((u32)xdev->closed_timeout, xdev->base + XWWDT_FWR_OFFSET);
iowrite32((u32)xdev->open_timeout, xdev->base + XWWDT_SWR_OFFSET);
/* Enable the window watchdog timer */
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 device *dev = &pdev->dev;
struct xwwdt_device *xdev;
u64 max_per_window_ms;
u64 min_per_window_ms;
u64 timeout_count;
struct clk *clk;
u32 timeout_ms;
u64 ms_count;
int ret;
xdev = devm_kzalloc(dev, sizeof(*xdev), GFP_KERNEL);
@ -154,12 +162,13 @@ static int xwwdt_probe(struct platform_device *pdev)
return PTR_ERR(clk);
xdev->freq = clk_get_rate(clk);
if (!xdev->freq)
if (xdev->freq < 1000000)
return -EINVAL;
xilinx_wwdt_wdd->min_timeout = XWWDT_MIN_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)
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;
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);
watchdog_set_drvdata(xilinx_wwdt_wdd, xdev);
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[] = {
{ "rave-wdt", 0 },
{ "rave-wdt" },
{ }
};
MODULE_DEVICE_TABLE(i2c, ziirave_wdt_id);

View File

@ -78,6 +78,7 @@ struct da9063 {
enum da9063_type type;
unsigned char variant_code;
unsigned int flags;
bool use_sw_pm;
/* Control interface */
struct regmap *regmap;