mirror of
git://git.yoctoproject.org/linux-yocto.git
synced 2025-10-23 07:23:12 +02:00
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:
commit
bb1556ec94
|
@ -20,6 +20,7 @@ properties:
|
|||
items:
|
||||
- enum:
|
||||
- fsl,imx8dxl-sc-wdt
|
||||
- fsl,imx8qm-sc-wdt
|
||||
- fsl,imx8qxp-sc-wdt
|
||||
- const: fsl,imx-sc-wdt
|
||||
|
||||
|
|
|
@ -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>;
|
||||
};
|
|
@ -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)
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
233
drivers/watchdog/intel_oc_wdt.c
Normal file
233
drivers/watchdog/intel_oc_wdt.c
Normal 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");
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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
315
drivers/watchdog/s32g_wdt.c
Normal 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");
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue
Block a user