linux-watchdog 6.16-rc1 tag

-----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2.0.14 (GNU/Linux)
 
 iEYEABECAAYFAmg8N3IACgkQ+iyteGJfRsqKXACeMbCQGXvBg79kCHa6ORCoaSGO
 TbQAoNtzDKMRLkfEuVlA9Ml5+U77J6Bj
 =oB05
 -----END PGP SIGNATURE-----

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

Pull watchdog updates from Wim Van Sebroeck:

 - Add watchdog timer for the NXP S32 platform

 - Add driver for Intel OC WDT

 - Add exynos990-wdt

 - Various other fixes and improvements

* tag 'linux-watchdog-6.16-rc1' of git://www.linux-watchdog.org/linux-watchdog: (22 commits)
  watchdog: iTCO_wdt: Update the heartbeat value after clamping timeout
  watchdog: Add driver for Intel OC WDT
  watchdog: arm_smc_wdt: get wdt status through SMCWD_GET_TIMELEFT
  watchdog: iTCO: Drop driver-internal locking
  watchdog: apple: set max_hw_heartbeat_ms instead of max_timeout
  watchdog: qcom: introduce the device data for IPQ5424 watchdog device
  dt-bindings: watchdog: renesas,wdt: Document RZ/V2N (R9A09G056) support
  watchdog: lenovo_se30_wdt: Fix possible devm_ioremap() NULL pointer dereference in lenovo_se30_wdt_probe()
  watchdog: s3c2410_wdt: Add exynos990-wdt compatible data
  dt-bindings: watchdog: samsung-wdt: Add exynos990-wdt compatible
  dt-bindings: watchdog: Add rk3562 compatible
  dt-bindings: watchdog: fsl,scu-wdt: Document imx8qm
  watchdog: Add the Watchdog Timer for the NXP S32 platform
  dt-bindings: watchdog: Add NXP Software Watchdog Timer
  watchdog: Correct kerneldoc warnings
  watchdog: stm32: Fix wakeup source leaks on device unbind
  watchdog: Do not enable by default during compile testing
  watchdog: cros-ec: Avoid -Wflex-array-member-not-at-end warning
  watchdog: da9052_wdt: respect TWDMIN
  watchdog: da9052_wdt: do not disable wdt during probe
  ...
This commit is contained in:
Linus Torvalds 2025-06-01 09:01:58 -07:00
commit bb1556ec94
22 changed files with 748 additions and 65 deletions

View File

@ -20,6 +20,7 @@ properties:
items:
- enum:
- fsl,imx8dxl-sc-wdt
- fsl,imx8qm-sc-wdt
- fsl,imx8qxp-sc-wdt
- const: fsl,imx-sc-wdt

View File

@ -0,0 +1,54 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/watchdog/nxp,s32g2-swt.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: NXP Software Watchdog Timer (SWT)
maintainers:
- Daniel Lezcano <daniel.lezcano@kernel.org>
allOf:
- $ref: watchdog.yaml#
properties:
compatible:
oneOf:
- const: nxp,s32g2-swt
- items:
- const: nxp,s32g3-swt
- const: nxp,s32g2-swt
reg:
maxItems: 1
clocks:
items:
- description: Counter clock
- description: Module clock
- description: Register clock
clock-names:
items:
- const: counter
- const: module
- const: register
required:
- compatible
- reg
- clocks
- clock-names
unevaluatedProperties: false
examples:
- |
watchdog@40100000 {
compatible = "nxp,s32g2-swt";
reg = <0x40100000 0x1000>;
clocks = <&clks 0x3a>, <&clks 0x3b>, <&clks 0x3c>;
clock-names = "counter", "module", "register";
timeout-sec = <10>;
};

View File

@ -76,7 +76,9 @@ properties:
- const: renesas,rcar-gen4-wdt # R-Car Gen4
- items:
- const: renesas,r9a09g047-wdt # RZ/G3E
- enum:
- renesas,r9a09g047-wdt # RZ/G3E
- renesas,r9a09g056-wdt # RZ/V2N
- const: renesas,r9a09g057-wdt # RZ/V2H(P)
- const: renesas,r9a09g057-wdt # RZ/V2H(P)

View File

@ -25,6 +25,7 @@ properties:
- samsung,exynos5420-wdt # for Exynos5420
- samsung,exynos7-wdt # for Exynos7
- samsung,exynos850-wdt # for Exynos850
- samsung,exynos990-wdt # for Exynos990
- samsung,exynosautov9-wdt # for Exynosautov9
- samsung,exynosautov920-wdt # for Exynosautov920
- items:
@ -49,14 +50,14 @@ properties:
samsung,cluster-index:
$ref: /schemas/types.yaml#/definitions/uint32
description:
Index of CPU cluster on which watchdog is running (in case of Exynos850
or Google gs101).
Index of CPU cluster on which watchdog is running (in case of Exynos850,
Exynos990 or Google gs101).
samsung,syscon-phandle:
$ref: /schemas/types.yaml#/definitions/phandle
description:
Phandle to the PMU system controller node (in case of Exynos5250,
Exynos5420, Exynos7, Exynos850 and gs101).
Exynos5420, Exynos7, Exynos850, Exynos990 and gs101).
required:
- compatible
@ -77,6 +78,7 @@ allOf:
- samsung,exynos5420-wdt
- samsung,exynos7-wdt
- samsung,exynos850-wdt
- samsung,exynos990-wdt
- samsung,exynosautov9-wdt
- samsung,exynosautov920-wdt
then:
@ -89,6 +91,7 @@ allOf:
enum:
- google,gs101-wdt
- samsung,exynos850-wdt
- samsung,exynos990-wdt
- samsung,exynosautov9-wdt
- samsung,exynosautov920-wdt
then:
@ -102,7 +105,7 @@ allOf:
- const: watchdog
- const: watchdog_src
samsung,cluster-index:
enum: [0, 1]
enum: [0, 1, 2]
required:
- samsung,cluster-index
else:

View File

@ -28,6 +28,7 @@ properties:
- rockchip,rk3328-wdt
- rockchip,rk3368-wdt
- rockchip,rk3399-wdt
- rockchip,rk3562-wdt
- rockchip,rk3568-wdt
- rockchip,rk3576-wdt
- rockchip,rk3588-wdt

View File

@ -804,6 +804,15 @@ config IMX7ULP_WDT
To compile this driver as a module, choose M here: the
module will be called imx7ulp_wdt.
config S32G_WDT
tristate "S32G Watchdog"
depends on ARCH_S32 || COMPILE_TEST
select WATCHDOG_CORE
help
This is the driver for the hardware watchdog on the NXP
S32G platforms. If you wish to have watchdog support
enabled, say Y, otherwise say N.
config DB500_WATCHDOG
tristate "ST-Ericsson DB800 watchdog"
depends on MFD_DB8500_PRCMU
@ -1001,7 +1010,7 @@ config STM32_WATCHDOG
tristate "STM32 Independent WatchDoG (IWDG) support"
depends on ARCH_STM32 || COMPILE_TEST
select WATCHDOG_CORE
default y
default ARCH_STM32
help
Say Y here to include support for the watchdog timer
in stm32 SoCs.
@ -1363,6 +1372,17 @@ config INTEL_MID_WATCHDOG
To compile this driver as a module, choose M here.
config INTEL_OC_WATCHDOG
tristate "Intel OC Watchdog"
depends on (X86 || COMPILE_TEST) && ACPI && HAS_IOPORT
select WATCHDOG_CORE
help
Hardware driver for Intel Over-Clocking watchdog present in
Platform Controller Hub (PCH) chipsets.
To compile this driver as a module, choose M here: the
module will be called intel_oc_wdt.
config ITCO_WDT
tristate "Intel TCO Timer/Watchdog"
depends on X86 && PCI
@ -1869,7 +1889,7 @@ config OCTEON_WDT
config MARVELL_GTI_WDT
tristate "Marvell GTI Watchdog driver"
depends on ARCH_THUNDER || (COMPILE_TEST && 64BIT)
default y
default ARCH_THUNDER
select WATCHDOG_CORE
help
Marvell GTI hardware supports watchdog timer. First timeout
@ -2035,7 +2055,7 @@ config 8xxx_WDT
config PIKA_WDT
tristate "PIKA FPGA Watchdog"
depends on WARP || (PPC64 && COMPILE_TEST)
default y
default WARP
help
This enables the watchdog in the PIKA FPGA. Currently used on
the Warp platform.

View File

@ -69,6 +69,7 @@ obj-$(CONFIG_TS72XX_WATCHDOG) += ts72xx_wdt.o
obj-$(CONFIG_IMX2_WDT) += imx2_wdt.o
obj-$(CONFIG_IMX_SC_WDT) += imx_sc_wdt.o
obj-$(CONFIG_IMX7ULP_WDT) += imx7ulp_wdt.o
obj-$(CONFIG_S32G_WDT) += s32g_wdt.o
obj-$(CONFIG_DB500_WATCHDOG) += db8500_wdt.o
obj-$(CONFIG_RETU_WATCHDOG) += retu_wdt.o
obj-$(CONFIG_BCM2835_WDT) += bcm2835_wdt.o
@ -150,6 +151,7 @@ obj-$(CONFIG_W83977F_WDT) += w83977f_wdt.o
obj-$(CONFIG_MACHZ_WDT) += machzwd.o
obj-$(CONFIG_SBC_EPX_C3_WATCHDOG) += sbc_epx_c3.o
obj-$(CONFIG_INTEL_MID_WATCHDOG) += intel-mid_wdt.o
obj-$(CONFIG_INTEL_OC_WATCHDOG) += intel_oc_wdt.o
obj-$(CONFIG_INTEL_MEI_WDT) += mei_wdt.o
obj-$(CONFIG_NI903X_WDT) += ni903x_wdt.o
obj-$(CONFIG_NIC7018_WDT) += nic7018_wdt.o

View File

@ -95,9 +95,12 @@ static int apple_wdt_ping(struct watchdog_device *wdd)
static int apple_wdt_set_timeout(struct watchdog_device *wdd, unsigned int s)
{
struct apple_wdt *wdt = to_apple_wdt(wdd);
u32 actual;
writel_relaxed(0, wdt->regs + APPLE_WDT_WD1_CUR_TIME);
writel_relaxed(wdt->clk_rate * s, wdt->regs + APPLE_WDT_WD1_BITE_TIME);
actual = min(s, wdd->max_hw_heartbeat_ms / 1000);
writel_relaxed(wdt->clk_rate * actual, wdt->regs + APPLE_WDT_WD1_BITE_TIME);
wdd->timeout = s;
@ -177,7 +180,7 @@ static int apple_wdt_probe(struct platform_device *pdev)
wdt->wdd.ops = &apple_wdt_ops;
wdt->wdd.info = &apple_wdt_info;
wdt->wdd.max_timeout = U32_MAX / wdt->clk_rate;
wdt->wdd.max_hw_heartbeat_ms = U32_MAX / wdt->clk_rate * 1000;
wdt->wdd.timeout = APPLE_WDT_TIMEOUT_DEFAULT;
wdt_ctrl = readl_relaxed(wdt->regs + APPLE_WDT_WD1_CTRL);

View File

@ -46,6 +46,8 @@ static int smcwd_call(struct watchdog_device *wdd, enum smcwd_call call,
return -ENODEV;
if (res->a0 == PSCI_RET_INVALID_PARAMS)
return -EINVAL;
if (res->a0 == PSCI_RET_DISABLED)
return -ENODATA;
if (res->a0 != PSCI_RET_SUCCESS)
return -EIO;
return 0;
@ -131,10 +133,19 @@ static int smcwd_probe(struct platform_device *pdev)
wdd->info = &smcwd_info;
/* get_timeleft is optional */
if (smcwd_call(wdd, SMCWD_GET_TIMELEFT, 0, NULL))
wdd->ops = &smcwd_ops;
else
err = smcwd_call(wdd, SMCWD_GET_TIMELEFT, 0, NULL);
switch (err) {
case 0:
set_bit(WDOG_HW_RUNNING, &wdd->status);
fallthrough;
case -ENODATA:
wdd->ops = &smcwd_timeleft_ops;
break;
default:
wdd->ops = &smcwd_ops;
break;
}
wdd->timeout = res.a2;
wdd->max_timeout = res.a2;
wdd->min_timeout = res.a1;

View File

@ -25,26 +25,22 @@ static int cros_ec_wdt_send_cmd(struct cros_ec_device *cros_ec,
union cros_ec_wdt_data *arg)
{
int ret;
struct {
struct cros_ec_command msg;
union cros_ec_wdt_data data;
} __packed buf = {
.msg = {
.version = 0,
.command = EC_CMD_HANG_DETECT,
.insize = (arg->req.command == EC_HANG_DETECT_CMD_GET_STATUS) ?
sizeof(struct ec_response_hang_detect) :
0,
.outsize = sizeof(struct ec_params_hang_detect),
},
.data.req = arg->req
};
DEFINE_RAW_FLEX(struct cros_ec_command, msg, data,
sizeof(union cros_ec_wdt_data));
ret = cros_ec_cmd_xfer_status(cros_ec, &buf.msg);
msg->version = 0;
msg->command = EC_CMD_HANG_DETECT;
msg->insize = (arg->req.command == EC_HANG_DETECT_CMD_GET_STATUS) ?
sizeof(struct ec_response_hang_detect) :
0;
msg->outsize = sizeof(struct ec_params_hang_detect);
*(struct ec_params_hang_detect *)msg->data = arg->req;
ret = cros_ec_cmd_xfer_status(cros_ec, msg);
if (ret < 0)
return ret;
arg->resp = buf.data.resp;
arg->resp = *(struct ec_response_hang_detect *)msg->data;
return 0;
}

View File

@ -30,6 +30,18 @@ struct da9052_wdt_data {
unsigned long jpast;
};
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 timeout;
module_param(timeout, int, 0);
MODULE_PARM_DESC(timeout,
"Watchdog timeout in seconds. (default = "
__MODULE_STRING(WDT_DEFAULT_TIMEOUT) ")");
static const struct {
u8 reg_val;
int time; /* Seconds */
@ -168,10 +180,13 @@ static int da9052_wdt_probe(struct platform_device *pdev)
da9052_wdt = &driver_data->wdt;
da9052_wdt->timeout = DA9052_DEF_TIMEOUT;
da9052_wdt->min_hw_heartbeat_ms = DA9052_TWDMIN;
da9052_wdt->info = &da9052_wdt_info;
da9052_wdt->ops = &da9052_wdt_ops;
da9052_wdt->parent = dev;
watchdog_set_drvdata(da9052_wdt, driver_data);
watchdog_init_timeout(da9052_wdt, timeout, dev);
watchdog_set_nowayout(da9052_wdt, nowayout);
if (da9052->fault_log & DA9052_FAULTLOG_TWDERROR)
da9052_wdt->bootstatus |= WDIOF_CARDRESET;
@ -180,11 +195,15 @@ static int da9052_wdt_probe(struct platform_device *pdev)
if (da9052->fault_log & DA9052_FAULTLOG_VDDFAULT)
da9052_wdt->bootstatus |= WDIOF_POWERUNDER;
ret = da9052_reg_update(da9052, DA9052_CONTROL_D_REG,
DA9052_CONTROLD_TWDSCALE, 0);
if (ret < 0) {
dev_err(dev, "Failed to disable watchdog bits, %d\n", ret);
ret = da9052_reg_read(da9052, DA9052_CONTROL_D_REG);
if (ret < 0)
return ret;
/* Check if FW enabled the watchdog */
if (ret & DA9052_CONTROLD_TWDSCALE) {
/* Ensure proper initialization */
da9052_wdt_start(da9052_wdt);
set_bit(WDOG_HW_RUNNING, &da9052_wdt->status);
}
return devm_watchdog_register_device(dev, &driver_data->wdt);

View File

@ -58,7 +58,6 @@
#include <linux/platform_device.h> /* For platform_driver framework */
#include <linux/pci.h> /* For pci functions */
#include <linux/ioport.h> /* For io-port access */
#include <linux/spinlock.h> /* For spin_lock/spin_unlock/... */
#include <linux/uaccess.h> /* For copy_to_user/put_user/... */
#include <linux/io.h> /* For inb/outb/... */
#include <linux/platform_data/itco_wdt.h>
@ -102,8 +101,6 @@ struct iTCO_wdt_private {
* or memory-mapped PMC register bit 4 (TCO version 3).
*/
unsigned long __iomem *gcs_pmc;
/* the lock for io operations */
spinlock_t io_lock;
/* the PCI-device */
struct pci_dev *pci_dev;
/* whether or not the watchdog has been suspended */
@ -286,13 +283,10 @@ static int iTCO_wdt_start(struct watchdog_device *wd_dev)
struct iTCO_wdt_private *p = watchdog_get_drvdata(wd_dev);
unsigned int val;
spin_lock(&p->io_lock);
iTCO_vendor_pre_start(p->smi_res, wd_dev->timeout);
/* disable chipset's NO_REBOOT bit */
if (p->update_no_reboot_bit(p->no_reboot_priv, false)) {
spin_unlock(&p->io_lock);
dev_err(wd_dev->parent, "failed to reset NO_REBOOT flag, reboot disabled by hardware/BIOS\n");
return -EIO;
}
@ -309,7 +303,6 @@ static int iTCO_wdt_start(struct watchdog_device *wd_dev)
val &= 0xf7ff;
outw(val, TCO1_CNT(p));
val = inw(TCO1_CNT(p));
spin_unlock(&p->io_lock);
if (val & 0x0800)
return -1;
@ -321,8 +314,6 @@ static int iTCO_wdt_stop(struct watchdog_device *wd_dev)
struct iTCO_wdt_private *p = watchdog_get_drvdata(wd_dev);
unsigned int val;
spin_lock(&p->io_lock);
iTCO_vendor_pre_stop(p->smi_res);
/* Bit 11: TCO Timer Halt -> 1 = The TCO timer is disabled */
@ -334,8 +325,6 @@ static int iTCO_wdt_stop(struct watchdog_device *wd_dev)
/* Set the NO_REBOOT bit to prevent later reboots, just for sure */
p->update_no_reboot_bit(p->no_reboot_priv, true);
spin_unlock(&p->io_lock);
if ((val & 0x0800) == 0)
return -1;
return 0;
@ -345,8 +334,6 @@ static int iTCO_wdt_ping(struct watchdog_device *wd_dev)
{
struct iTCO_wdt_private *p = watchdog_get_drvdata(wd_dev);
spin_lock(&p->io_lock);
/* Reload the timer by writing to the TCO Timer Counter register */
if (p->iTCO_version >= 2) {
outw(0x01, TCO_RLD(p));
@ -358,7 +345,6 @@ static int iTCO_wdt_ping(struct watchdog_device *wd_dev)
outb(0x01, TCO_RLD(p));
}
spin_unlock(&p->io_lock);
return 0;
}
@ -385,24 +371,20 @@ static int iTCO_wdt_set_timeout(struct watchdog_device *wd_dev, unsigned int t)
/* Write new heartbeat to watchdog */
if (p->iTCO_version >= 2) {
spin_lock(&p->io_lock);
val16 = inw(TCOv2_TMR(p));
val16 &= 0xfc00;
val16 |= tmrval;
outw(val16, TCOv2_TMR(p));
val16 = inw(TCOv2_TMR(p));
spin_unlock(&p->io_lock);
if ((val16 & 0x3ff) != tmrval)
return -EINVAL;
} else if (p->iTCO_version == 1) {
spin_lock(&p->io_lock);
val8 = inb(TCOv1_TMR(p));
val8 &= 0xc0;
val8 |= (tmrval & 0xff);
outb(val8, TCOv1_TMR(p));
val8 = inb(TCOv1_TMR(p));
spin_unlock(&p->io_lock);
if ((val8 & 0x3f) != tmrval)
return -EINVAL;
@ -421,19 +403,15 @@ static unsigned int iTCO_wdt_get_timeleft(struct watchdog_device *wd_dev)
/* read the TCO Timer */
if (p->iTCO_version >= 2) {
spin_lock(&p->io_lock);
val16 = inw(TCO_RLD(p));
val16 &= 0x3ff;
spin_unlock(&p->io_lock);
time_left = ticks_to_seconds(p, val16);
} else if (p->iTCO_version == 1) {
spin_lock(&p->io_lock);
val8 = inb(TCO_RLD(p));
val8 &= 0x3f;
if (!(inw(TCO1_STS(p)) & 0x0008))
val8 += (inb(TCOv1_TMR(p)) & 0x3f);
spin_unlock(&p->io_lock);
time_left = ticks_to_seconds(p, val8);
}
@ -493,8 +471,6 @@ static int iTCO_wdt_probe(struct platform_device *pdev)
if (!p)
return -ENOMEM;
spin_lock_init(&p->io_lock);
p->tco_res = platform_get_resource(pdev, IORESOURCE_IO, ICH_RES_IO_TCO);
if (!p->tco_res)
return -ENODEV;
@ -604,6 +580,7 @@ static int iTCO_wdt_probe(struct platform_device *pdev)
iTCO_wdt_set_timeout(&p->wddev, WATCHDOG_TIMEOUT);
dev_info(dev, "timeout value out of range, using %d\n",
WATCHDOG_TIMEOUT);
heartbeat = WATCHDOG_TIMEOUT;
}
watchdog_stop_on_reboot(&p->wddev);

View File

@ -0,0 +1,233 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Intel OC Watchdog driver
*
* Copyright (C) 2025, Siemens
* Author: Diogo Ivo <diogo.ivo@siemens.com>
*/
#define DRV_NAME "intel_oc_wdt"
#include <linux/acpi.h>
#include <linux/bits.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/platform_device.h>
#include <linux/watchdog.h>
#define INTEL_OC_WDT_TOV GENMASK(9, 0)
#define INTEL_OC_WDT_MIN_TOV 1
#define INTEL_OC_WDT_MAX_TOV 1024
#define INTEL_OC_WDT_DEF_TOV 60
/*
* One-time writable lock bit. If set forbids
* modification of itself, _TOV and _EN until
* next reboot.
*/
#define INTEL_OC_WDT_CTL_LCK BIT(12)
#define INTEL_OC_WDT_EN BIT(14)
#define INTEL_OC_WDT_NO_ICCSURV_STS BIT(24)
#define INTEL_OC_WDT_ICCSURV_STS BIT(25)
#define INTEL_OC_WDT_RLD BIT(31)
#define INTEL_OC_WDT_STS_BITS (INTEL_OC_WDT_NO_ICCSURV_STS | \
INTEL_OC_WDT_ICCSURV_STS)
#define INTEL_OC_WDT_CTRL_REG(wdt) ((wdt)->ctrl_res->start)
struct intel_oc_wdt {
struct watchdog_device wdd;
struct resource *ctrl_res;
bool locked;
};
static int heartbeat;
module_param(heartbeat, uint, 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 intel_oc_wdt_start(struct watchdog_device *wdd)
{
struct intel_oc_wdt *oc_wdt = watchdog_get_drvdata(wdd);
if (oc_wdt->locked)
return 0;
outl(inl(INTEL_OC_WDT_CTRL_REG(oc_wdt)) | INTEL_OC_WDT_EN,
INTEL_OC_WDT_CTRL_REG(oc_wdt));
return 0;
}
static int intel_oc_wdt_stop(struct watchdog_device *wdd)
{
struct intel_oc_wdt *oc_wdt = watchdog_get_drvdata(wdd);
outl(inl(INTEL_OC_WDT_CTRL_REG(oc_wdt)) & ~INTEL_OC_WDT_EN,
INTEL_OC_WDT_CTRL_REG(oc_wdt));
return 0;
}
static int intel_oc_wdt_ping(struct watchdog_device *wdd)
{
struct intel_oc_wdt *oc_wdt = watchdog_get_drvdata(wdd);
outl(inl(INTEL_OC_WDT_CTRL_REG(oc_wdt)) | INTEL_OC_WDT_RLD,
INTEL_OC_WDT_CTRL_REG(oc_wdt));
return 0;
}
static int intel_oc_wdt_set_timeout(struct watchdog_device *wdd,
unsigned int t)
{
struct intel_oc_wdt *oc_wdt = watchdog_get_drvdata(wdd);
outl((inl(INTEL_OC_WDT_CTRL_REG(oc_wdt)) & ~INTEL_OC_WDT_TOV) | (t - 1),
INTEL_OC_WDT_CTRL_REG(oc_wdt));
wdd->timeout = t;
return 0;
}
static const struct watchdog_info intel_oc_wdt_info = {
.options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING,
.identity = DRV_NAME,
};
static const struct watchdog_ops intel_oc_wdt_ops = {
.owner = THIS_MODULE,
.start = intel_oc_wdt_start,
.stop = intel_oc_wdt_stop,
.ping = intel_oc_wdt_ping,
.set_timeout = intel_oc_wdt_set_timeout,
};
static int intel_oc_wdt_setup(struct intel_oc_wdt *oc_wdt)
{
struct watchdog_info *info;
unsigned long val;
val = inl(INTEL_OC_WDT_CTRL_REG(oc_wdt));
if (val & INTEL_OC_WDT_STS_BITS)
oc_wdt->wdd.bootstatus |= WDIOF_CARDRESET;
oc_wdt->locked = !!(val & INTEL_OC_WDT_CTL_LCK);
if (val & INTEL_OC_WDT_EN) {
/*
* No need to issue a ping here to "commit" the new timeout
* value to hardware as the watchdog core schedules one
* immediately when registering the watchdog.
*/
set_bit(WDOG_HW_RUNNING, &oc_wdt->wdd.status);
if (oc_wdt->locked) {
info = (struct watchdog_info *)&intel_oc_wdt_info;
/*
* Set nowayout unconditionally as we cannot stop
* the watchdog.
*/
nowayout = true;
/*
* If we are locked read the current timeout value
* and inform the core we can't change it.
*/
oc_wdt->wdd.timeout = (val & INTEL_OC_WDT_TOV) + 1;
info->options &= ~WDIOF_SETTIMEOUT;
dev_info(oc_wdt->wdd.parent,
"Register access locked, heartbeat fixed at: %u s\n",
oc_wdt->wdd.timeout);
}
} else if (oc_wdt->locked) {
/*
* In case the watchdog is disabled and locked there
* is nothing we can do with it so just fail probing.
*/
return -EACCES;
}
val &= ~INTEL_OC_WDT_TOV;
outl(val | (oc_wdt->wdd.timeout - 1), INTEL_OC_WDT_CTRL_REG(oc_wdt));
return 0;
}
static int intel_oc_wdt_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct intel_oc_wdt *oc_wdt;
struct watchdog_device *wdd;
int ret;
oc_wdt = devm_kzalloc(&pdev->dev, sizeof(*oc_wdt), GFP_KERNEL);
if (!oc_wdt)
return -ENOMEM;
oc_wdt->ctrl_res = platform_get_resource(pdev, IORESOURCE_IO, 0);
if (!oc_wdt->ctrl_res) {
dev_err(&pdev->dev, "missing I/O resource\n");
return -ENODEV;
}
if (!devm_request_region(&pdev->dev, oc_wdt->ctrl_res->start,
resource_size(oc_wdt->ctrl_res), pdev->name)) {
dev_err(dev, "resource %pR already in use, device disabled\n",
oc_wdt->ctrl_res);
return -EBUSY;
}
wdd = &oc_wdt->wdd;
wdd->min_timeout = INTEL_OC_WDT_MIN_TOV;
wdd->max_timeout = INTEL_OC_WDT_MAX_TOV;
wdd->timeout = INTEL_OC_WDT_DEF_TOV;
wdd->info = &intel_oc_wdt_info;
wdd->ops = &intel_oc_wdt_ops;
wdd->parent = dev;
watchdog_init_timeout(wdd, heartbeat, dev);
ret = intel_oc_wdt_setup(oc_wdt);
if (ret)
return ret;
watchdog_set_drvdata(wdd, oc_wdt);
watchdog_set_nowayout(wdd, nowayout);
watchdog_stop_on_reboot(wdd);
watchdog_stop_on_unregister(wdd);
return devm_watchdog_register_device(dev, wdd);
}
static const struct acpi_device_id intel_oc_wdt_match[] = {
{ "INT3F0D" },
{ "INTC1099" },
{ },
};
MODULE_DEVICE_TABLE(acpi, intel_oc_wdt_match);
static struct platform_driver intel_oc_wdt_platform_driver = {
.driver = {
.name = DRV_NAME,
.acpi_match_table = intel_oc_wdt_match,
},
.probe = intel_oc_wdt_probe,
};
module_platform_driver(intel_oc_wdt_platform_driver);
MODULE_AUTHOR("Diogo Ivo <diogo.ivo@siemens.com>");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Intel OC Watchdog driver");

View File

@ -271,6 +271,8 @@ static int lenovo_se30_wdt_probe(struct platform_device *pdev)
return -EBUSY;
priv->shm_base_addr = devm_ioremap(dev, base_phys, SHM_WIN_SIZE);
if (!priv->shm_base_addr)
return -ENOMEM;
priv->wdt_cfg.mod = WDT_MODULE;
priv->wdt_cfg.idx = WDT_CFG_INDEX;

View File

@ -579,7 +579,7 @@ static struct notifier_block usb_pcwd_notifier = {
.notifier_call = usb_pcwd_notify_sys,
};
/**
/*
* usb_pcwd_delete
*/
static inline void usb_pcwd_delete(struct usb_pcwd_private *usb_pcwd)
@ -590,7 +590,7 @@ static inline void usb_pcwd_delete(struct usb_pcwd_private *usb_pcwd)
kfree(usb_pcwd);
}
/**
/*
* usb_pcwd_probe
*
* Called by the usb core when a new device is connected that it thinks
@ -758,7 +758,7 @@ error:
}
/**
/*
* usb_pcwd_disconnect
*
* Called by the usb core when the device is removed from the system.

View File

@ -11,7 +11,7 @@
/**
* pretimeout_noop - No operation on watchdog pretimeout event
* @wdd - watchdog_device
* @wdd: watchdog_device
*
* This function prints a message about pretimeout to kernel log.
*/

View File

@ -11,7 +11,7 @@
/**
* pretimeout_panic - Panic on watchdog pretimeout event
* @wdd - watchdog_device
* @wdd: watchdog_device
*
* Panic, watchdog has not been fed till pretimeout event.
*/

View File

@ -181,6 +181,12 @@ static const struct qcom_wdt_match_data match_data_apcs_tmr = {
.max_tick_count = 0x10000000U,
};
static const struct qcom_wdt_match_data match_data_ipq5424 = {
.offset = reg_offset_data_kpss,
.pretimeout = true,
.max_tick_count = 0xFFFFFU,
};
static const struct qcom_wdt_match_data match_data_kpss = {
.offset = reg_offset_data_kpss,
.pretimeout = true,
@ -322,6 +328,7 @@ static const struct dev_pm_ops qcom_wdt_pm_ops = {
};
static const struct of_device_id qcom_wdt_of_table[] = {
{ .compatible = "qcom,apss-wdt-ipq5424", .data = &match_data_ipq5424 },
{ .compatible = "qcom,kpss-timer", .data = &match_data_apcs_tmr },
{ .compatible = "qcom,scss-timer", .data = &match_data_apcs_tmr },
{ .compatible = "qcom,kpss-wdt", .data = &match_data_kpss },

315
drivers/watchdog/s32g_wdt.c Normal file
View File

@ -0,0 +1,315 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Watchdog driver for S32G SoC
*
* Copyright 2017-2019, 2021-2025 NXP.
*
*/
#include <linux/clk.h>
#include <linux/debugfs.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/watchdog.h>
#define DRIVER_NAME "s32g-swt"
#define S32G_SWT_CR(__base) ((__base) + 0x00) /* Control Register offset */
#define S32G_SWT_CR_SM (BIT(9) | BIT(10)) /* -> Service Mode */
#define S32G_SWT_CR_STP BIT(2) /* -> Stop Mode Control */
#define S32G_SWT_CR_FRZ BIT(1) /* -> Debug Mode Control */
#define S32G_SWT_CR_WEN BIT(0) /* -> Watchdog Enable */
#define S32G_SWT_TO(__base) ((__base) + 0x08) /* Timeout Register offset */
#define S32G_SWT_SR(__base) ((__base) + 0x10) /* Service Register offset */
#define S32G_WDT_SEQ1 0xA602 /* -> service sequence number 1 */
#define S32G_WDT_SEQ2 0xB480 /* -> service sequence number 2 */
#define S32G_SWT_CO(__base) ((__base) + 0x14) /* Counter output register */
#define S32G_WDT_DEFAULT_TIMEOUT 30
struct s32g_wdt_device {
int rate;
void __iomem *base;
struct watchdog_device wdog;
};
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 unsigned int timeout_param = S32G_WDT_DEFAULT_TIMEOUT;
module_param(timeout_param, uint, 0);
MODULE_PARM_DESC(timeout_param, "Watchdog timeout in seconds (default="
__MODULE_STRING(S32G_WDT_DEFAULT_TIMEOUT) ")");
static bool early_enable;
module_param(early_enable, bool, 0);
MODULE_PARM_DESC(early_enable,
"Watchdog is started on module insertion (default=false)");
static const struct watchdog_info s32g_wdt_info = {
.identity = "s32g watchdog",
.options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE |
WDIOC_GETTIMEOUT | WDIOC_GETTIMELEFT,
};
static struct s32g_wdt_device *wdd_to_s32g_wdt(struct watchdog_device *wdd)
{
return container_of(wdd, struct s32g_wdt_device, wdog);
}
static unsigned int wdog_sec_to_count(struct s32g_wdt_device *wdev, unsigned int timeout)
{
return wdev->rate * timeout;
}
static int s32g_wdt_ping(struct watchdog_device *wdog)
{
struct s32g_wdt_device *wdev = wdd_to_s32g_wdt(wdog);
writel(S32G_WDT_SEQ1, S32G_SWT_SR(wdev->base));
writel(S32G_WDT_SEQ2, S32G_SWT_SR(wdev->base));
return 0;
}
static int s32g_wdt_start(struct watchdog_device *wdog)
{
struct s32g_wdt_device *wdev = wdd_to_s32g_wdt(wdog);
unsigned long val;
val = readl(S32G_SWT_CR(wdev->base));
val |= S32G_SWT_CR_WEN;
writel(val, S32G_SWT_CR(wdev->base));
return 0;
}
static int s32g_wdt_stop(struct watchdog_device *wdog)
{
struct s32g_wdt_device *wdev = wdd_to_s32g_wdt(wdog);
unsigned long val;
val = readl(S32G_SWT_CR(wdev->base));
val &= ~S32G_SWT_CR_WEN;
writel(val, S32G_SWT_CR(wdev->base));
return 0;
}
static int s32g_wdt_set_timeout(struct watchdog_device *wdog, unsigned int timeout)
{
struct s32g_wdt_device *wdev = wdd_to_s32g_wdt(wdog);
writel(wdog_sec_to_count(wdev, timeout), S32G_SWT_TO(wdev->base));
wdog->timeout = timeout;
/*
* Conforming to the documentation, the timeout counter is
* loaded when servicing is operated (aka ping) or when the
* counter is enabled. In case the watchdog is already started
* it must be stopped and started again to update the timeout
* register or a ping can be sent to refresh the counter. Here
* we choose to send a ping to the watchdog which is harmless
* if the watchdog is stopped.
*/
return s32g_wdt_ping(wdog);
}
static unsigned int s32g_wdt_get_timeleft(struct watchdog_device *wdog)
{
struct s32g_wdt_device *wdev = wdd_to_s32g_wdt(wdog);
unsigned long counter;
bool is_running;
/*
* The counter output can be read only if the SWT is
* disabled. Given the latency between the internal counter
* and the counter output update, there can be very small
* difference. However, we can accept this matter of fact
* given the resolution is a second based unit for the output.
*/
is_running = watchdog_hw_running(wdog);
if (is_running)
s32g_wdt_stop(wdog);
counter = readl(S32G_SWT_CO(wdev->base));
if (is_running)
s32g_wdt_start(wdog);
return counter / wdev->rate;
}
static const struct watchdog_ops s32g_wdt_ops = {
.owner = THIS_MODULE,
.start = s32g_wdt_start,
.stop = s32g_wdt_stop,
.ping = s32g_wdt_ping,
.set_timeout = s32g_wdt_set_timeout,
.get_timeleft = s32g_wdt_get_timeleft,
};
static void s32g_wdt_init(struct s32g_wdt_device *wdev)
{
unsigned long val;
/* Set the watchdog's Time-Out value */
val = wdog_sec_to_count(wdev, wdev->wdog.timeout);
writel(val, S32G_SWT_TO(wdev->base));
/*
* Get the control register content. We are at init time, the
* watchdog should not be started.
*/
val = readl(S32G_SWT_CR(wdev->base));
/*
* We want to allow the watchdog timer to be stopped when
* device enters debug mode.
*/
val |= S32G_SWT_CR_FRZ;
/*
* However, when the CPU is in WFI or suspend mode, the
* watchdog must continue. The documentation refers it as the
* stopped mode.
*/
val &= ~S32G_SWT_CR_STP;
/*
* Use Fixed Service Sequence to ping the watchdog which is
* 0x00 configuration value for the service mode. It should be
* already set because it is the default value but we reset it
* in case.
*/
val &= ~S32G_SWT_CR_SM;
writel(val, S32G_SWT_CR(wdev->base));
/*
* When the 'early_enable' option is set, we start the
* watchdog from the kernel.
*/
if (early_enable) {
s32g_wdt_start(&wdev->wdog);
set_bit(WDOG_HW_RUNNING, &wdev->wdog.status);
}
}
static int s32g_wdt_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct resource *res;
struct clk *clk;
struct s32g_wdt_device *wdev;
struct watchdog_device *wdog;
int ret;
wdev = devm_kzalloc(dev, sizeof(*wdev), GFP_KERNEL);
if (!wdev)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
wdev->base = devm_ioremap_resource(dev, res);
if (IS_ERR(wdev->base))
return dev_err_probe(&pdev->dev, PTR_ERR(wdev->base), "Can not get resource\n");
clk = devm_clk_get_enabled(dev, "counter");
if (IS_ERR(clk))
return dev_err_probe(dev, PTR_ERR(clk), "Can't get Watchdog clock\n");
wdev->rate = clk_get_rate(clk);
if (!wdev->rate) {
dev_err(dev, "Input clock rate is not valid\n");
return -EINVAL;
}
wdog = &wdev->wdog;
wdog->info = &s32g_wdt_info;
wdog->ops = &s32g_wdt_ops;
/*
* The code converts the timeout into a counter a value, if
* the value is less than 0x100, then it is clamped by the SWT
* module, so it is safe to specify a zero value as the
* minimum timeout.
*/
wdog->min_timeout = 0;
/*
* The counter register is a 32 bits long, so the maximum
* counter value is UINT_MAX and the timeout in second is the
* value divided by the rate.
*
* For instance, a rate of 51MHz lead to 84 seconds maximum
* timeout.
*/
wdog->max_timeout = UINT_MAX / wdev->rate;
/*
* The module param and the DT 'timeout-sec' property will
* override the default value if they are specified.
*/
ret = watchdog_init_timeout(wdog, timeout_param, dev);
if (ret)
return ret;
/*
* As soon as the watchdog is started, there is no way to stop
* it if the 'nowayout' option is set at boot time
*/
watchdog_set_nowayout(wdog, nowayout);
/*
* The devm_ version of the watchdog_register_device()
* function will call watchdog_unregister_device() when the
* device is removed.
*/
watchdog_stop_on_unregister(wdog);
s32g_wdt_init(wdev);
ret = devm_watchdog_register_device(dev, wdog);
if (ret)
return dev_err_probe(dev, ret, "Cannot register watchdog device\n");
dev_info(dev, "S32G Watchdog Timer Registered, timeout=%ds, nowayout=%d, early_enable=%d\n",
wdog->timeout, nowayout, early_enable);
return 0;
}
static const struct of_device_id s32g_wdt_dt_ids[] = {
{ .compatible = "nxp,s32g2-swt" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, s32g_wdt_dt_ids);
static struct platform_driver s32g_wdt_driver = {
.probe = s32g_wdt_probe,
.driver = {
.name = DRIVER_NAME,
.of_match_table = s32g_wdt_dt_ids,
},
};
module_platform_driver(s32g_wdt_driver);
MODULE_AUTHOR("Daniel Lezcano <daniel.lezcano@linaro.org>");
MODULE_DESCRIPTION("Watchdog driver for S32G SoC");
MODULE_LICENSE("GPL");

View File

@ -82,6 +82,10 @@
#define GS_CLUSTER2_NONCPU_INT_EN 0x1644
#define GS_RST_STAT_REG_OFFSET 0x3B44
#define EXYNOS990_CLUSTER2_NONCPU_OUT 0x1620
#define EXYNOS990_CLUSTER2_NONCPU_INT_EN 0x1644
#define EXYNOS990_CLUSTER2_WDTRESET_BIT 23
/**
* DOC: Quirk flags for different Samsung watchdog IP-cores
*
@ -259,6 +263,32 @@ static const struct s3c2410_wdt_variant drv_data_exynos850_cl1 = {
QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_CNT_EN,
};
static const struct s3c2410_wdt_variant drv_data_exynos990_cl0 = {
.mask_reset_reg = GS_CLUSTER0_NONCPU_INT_EN,
.mask_bit = 2,
.mask_reset_inv = true,
.rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET,
.rst_stat_bit = EXYNOS850_CLUSTER0_WDTRESET_BIT,
.cnt_en_reg = 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_exynos990_cl2 = {
.mask_reset_reg = EXYNOS990_CLUSTER2_NONCPU_INT_EN,
.mask_bit = 2,
.mask_reset_inv = true,
.rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET,
.rst_stat_bit = EXYNOS990_CLUSTER2_WDTRESET_BIT,
.cnt_en_reg = EXYNOS990_CLUSTER2_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_exynosautov9_cl0 = {
.mask_reset_reg = EXYNOS850_CLUSTER0_NONCPU_INT_EN,
.mask_bit = 2,
@ -350,6 +380,8 @@ static const struct of_device_id s3c2410_wdt_match[] = {
.data = &drv_data_exynos7 },
{ .compatible = "samsung,exynos850-wdt",
.data = &drv_data_exynos850_cl0 },
{ .compatible = "samsung,exynos990-wdt",
.data = &drv_data_exynos990_cl0 },
{ .compatible = "samsung,exynosautov9-wdt",
.data = &drv_data_exynosautov9_cl0 },
{ .compatible = "samsung,exynosautov920-wdt",
@ -678,7 +710,8 @@ s3c2410_get_wdt_drv_data(struct platform_device *pdev, struct s3c2410_wdt *wdt)
if (variant == &drv_data_exynos850_cl0 ||
variant == &drv_data_exynosautov9_cl0 ||
variant == &drv_data_gs101_cl0 ||
variant == &drv_data_exynosautov920_cl0) {
variant == &drv_data_exynosautov920_cl0 ||
variant == &drv_data_exynos990_cl0) {
u32 index;
int err;
@ -700,6 +733,10 @@ s3c2410_get_wdt_drv_data(struct platform_device *pdev, struct s3c2410_wdt *wdt)
else if (variant == &drv_data_exynosautov920_cl0)
variant = &drv_data_exynosautov920_cl1;
break;
case 2:
if (variant == &drv_data_exynos990_cl0)
variant = &drv_data_exynos990_cl2;
break;
default:
return dev_err_probe(dev, -EINVAL, "wrong cluster index: %u\n", index);
}

View File

@ -291,7 +291,7 @@ static int stm32_iwdg_irq_init(struct platform_device *pdev,
return 0;
if (of_property_read_bool(np, "wakeup-source")) {
ret = device_init_wakeup(dev, true);
ret = devm_device_init_wakeup(dev);
if (ret)
return ret;

View File

@ -264,7 +264,7 @@ static int wdtpci_get_status(int *status)
return 0;
}
/**
/*
* wdtpci_get_temperature:
*
* Reports the temperature in degrees Fahrenheit. The API is in