Commit 9d69294b authored by Linus Torvalds's avatar Linus Torvalds

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

Pull watchdog updates from Wim Van Sebroeck:

 - Add Mstar MSC313e WDT driver

 - Add support for sama7g5-wdt

 - Add compatible for SC7280 SoC

 - Add compatible for Mediatek MT8195

 - sbsa: Support architecture version 1

 - Removal of the MV64x60 watchdog driver

 - Extra PCI IDs for hpwdt

 - Add hrtimer-based pretimeout feature

 - Add {min,max}_timeout sysfs nodes

 - keembay timeout and pre-timeout handling

 - Several fixes, cleanups and improvements

* tag 'linux-watchdog-5.14-rc1' of git://www.linux-watchdog.org/linux-watchdog: (56 commits)
  watchdog: iTCO_wdt: use dev_err() instead of pr_err()
  watchdog: Add Mstar MSC313e WDT driver
  dt-bindings: watchdog: Add Mstar MSC313e WDT devicetree bindings documentation
  watchdog: iTCO_wdt: Account for rebooting on second timeout
  dt-bindings: watchdog: Convert arm,sbsa-gwdt to DT schema
  dt-bindings: watchdog: sama5d4-wdt: add compatible for sama7g5-wdt
  watchdog: sama5d4_wdt: add support for sama7g5-wdt
  dt-bindings: watchdog: sama5d4-wdt: convert to yaml
  watchdog: ziirave_wdt: Remove VERSION_FMT defines and add sysfs newlines
  dt-bindings: watchdog: Add compatible for Mediatek MT8195
  dt-bindings: watchdog: dw-wdt: add description for rk3568
  watchdog: imx_sc_wdt: fix pretimeout
  watchdog: diag288_wdt: Remove redundant assignment
  watchdog: Add hrtimer-based pretimeout feature
  dt-bindings: watchdog: Add compatible for SC7280 SoC
  watchdog: qcom: Move suspend/resume to suspend_late/resume_early
  watchdog: Fix a typo in the file orion_wdt.c
  watchdog: jz4740: Fix return value check in jz4740_wdt_probe()
  watchdog: Remove MV64x60 watchdog driver
  doc: mtk-wdt: support pre-timeout when the bark irq is available
  ...
parents 0cc2ea8c cf813c67
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/watchdog/atmel,sama5d4-wdt.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Atmel SAMA5D4 Watchdog Timer (WDT) Controller
maintainers:
- Eugen Hristev <eugen.hristev@microchip.com>
allOf:
- $ref: "watchdog.yaml#"
properties:
compatible:
enum:
- atmel,sama5d4-wdt
- microchip,sam9x60-wdt
- microchip,sama7g5-wdt
reg:
maxItems: 1
atmel,watchdog-type:
$ref: /schemas/types.yaml#/definitions/string
description: should be hardware or software.
oneOf:
- description:
Enable watchdog fault reset. A watchdog fault triggers
watchdog reset.
const: hardware
- description:
Enable watchdog fault interrupt. A watchdog fault asserts
watchdog interrupt.
const: software
default: hardware
atmel,idle-halt:
$ref: /schemas/types.yaml#/definitions/flag
description: |
present if you want to stop the watchdog when the CPU is in idle state.
CAUTION: This property should be used with care, it actually makes the
watchdog not counting when the CPU is in idle state, therefore the
watchdog reset time depends on mean CPU usage and will not reset at all
if the CPU stop working while it is in idle state, which is probably
not what you want.
atmel,dbg-halt:
$ref: /schemas/types.yaml#/definitions/flag
description: |
present if you want to stop the watchdog when the CPU is in debug state.
required:
- compatible
- reg
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
watchdog@fc068640 {
compatible = "atmel,sama5d4-wdt";
reg = <0xfc068640 0x10>;
interrupts = <4 IRQ_TYPE_LEVEL_HIGH 5>;
timeout-sec = <10>;
atmel,watchdog-type = "hardware";
atmel,dbg-halt;
atmel,idle-halt;
};
...
* Atmel SAMA5D4 Watchdog Timer (WDT) Controller
Required properties:
- compatible: "atmel,sama5d4-wdt" or "microchip,sam9x60-wdt"
- reg: base physical address and length of memory mapped region.
Optional properties:
- timeout-sec: watchdog timeout value (in seconds).
- interrupts: interrupt number to the CPU.
- atmel,watchdog-type: should be "hardware" or "software".
"hardware": enable watchdog fault reset. A watchdog fault triggers
watchdog reset.
"software": enable watchdog fault interrupt. A watchdog fault asserts
watchdog interrupt.
- atmel,idle-halt: present if you want to stop the watchdog when the CPU is
in idle state.
CAUTION: This property should be used with care, it actually makes the
watchdog not counting when the CPU is in idle state, therefore the
watchdog reset time depends on mean CPU usage and will not reset at all
if the CPU stop working while it is in idle state, which is probably
not what you want.
- atmel,dbg-halt: present if you want to stop the watchdog when the CPU is
in debug state.
Example:
watchdog@fc068640 {
compatible = "atmel,sama5d4-wdt";
reg = <0xfc068640 0x10>;
interrupts = <4 IRQ_TYPE_LEVEL_HIGH 5>;
timeout-sec = <10>;
atmel,watchdog-type = "hardware";
atmel,dbg-halt;
atmel,idle-halt;
};
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/watchdog/mstar,msc313e-wdt.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: MStar Watchdog Device Tree Bindings
maintainers:
- Daniel Palmer <daniel@0x0f.com>
- Romain Perier <romain.perier@gmail.com>
allOf:
- $ref: watchdog.yaml#
properties:
compatible:
enum:
- mstar,msc313e-wdt
reg:
maxItems: 1
clocks:
maxItems: 1
required:
- compatible
- clocks
- reg
unevaluatedProperties: false
examples:
- |
watchdog@6000 {
compatible = "mstar,msc313e-wdt";
reg = <0x6000 0x1f>;
clocks = <&xtal_div2>;
};
Mediatek SoCs Watchdog timer
The watchdog supports a pre-timeout interrupt that fires timeout-sec/2
before the expiry.
Required properties:
- compatible should contain:
......@@ -13,10 +16,12 @@ Required properties:
"mediatek,mt8183-wdt": for MT8183
"mediatek,mt8516-wdt", "mediatek,mt6589-wdt": for MT8516
"mediatek,mt8192-wdt": for MT8192
"mediatek,mt8195-wdt", "mediatek,mt6589-wdt": for MT8195
- reg : Specifies base physical address and size of the registers.
Optional properties:
- interrupts: Watchdog pre-timeout (bark) interrupt.
- timeout-sec: contains the watchdog timeout in seconds.
- #reset-cells: Should be 1.
......@@ -26,6 +31,7 @@ watchdog: watchdog@10007000 {
compatible = "mediatek,mt8183-wdt",
"mediatek,mt6589-wdt";
reg = <0 0x10007000 0 0x100>;
interrupts = <GIC_SPI 139 IRQ_TYPE_NONE>;
timeout-sec = <10>;
#reset-cells = <1>;
};
......@@ -17,6 +17,7 @@ properties:
enum:
- qcom,apss-wdt-qcs404
- qcom,apss-wdt-sc7180
- qcom,apss-wdt-sc7280
- qcom,apss-wdt-sdm845
- qcom,apss-wdt-sdx55
- qcom,apss-wdt-sm8150
......
......@@ -27,6 +27,7 @@ properties:
- rockchip,rk3328-wdt
- rockchip,rk3368-wdt
- rockchip,rk3399-wdt
- rockchip,rk3568-wdt
- rockchip,rv1108-wdt
- const: snps,dw-wdt
......
......@@ -2217,6 +2217,7 @@ F: arch/arm/boot/dts/mstar-*
F: arch/arm/mach-mstar/
F: drivers/clk/mstar/
F: drivers/gpio/gpio-msc313.c
F: drivers/watchdog/msc313e_wdt.c
F: include/dt-bindings/clock/mstar-*
F: include/dt-bindings/gpio/msc313-gpio.h
......
......@@ -22,9 +22,9 @@ menuconfig WATCHDOG
The watchdog is usually used together with the watchdog daemon
which is available from
<ftp://ibiblio.org/pub/Linux/system/daemons/watchdog/>. This daemon can
also monitor NFS connections and can reboot the machine when the process
table is full.
<https://ibiblio.org/pub/Linux/system/daemons/watchdog/>. This daemon
can also monitor NFS connections and can reboot the machine when the
process table is full.
If unsure, say N.
......@@ -73,6 +73,14 @@ config WATCHDOG_SYSFS
Say Y here if you want to enable watchdog device status read through
sysfs attributes.
config WATCHDOG_HRTIMER_PRETIMEOUT
bool "Enable watchdog hrtimer-based pretimeouts"
help
Enable this if you want to use a hrtimer timer based pretimeout for
watchdogs that do not natively support pretimeout support. Be aware
that because this pretimeout functionality uses hrtimers, it may not
be able to fire before the actual watchdog fires in some situations.
comment "Watchdog Pretimeout Governors"
config WATCHDOG_PRETIMEOUT_GOV
......@@ -302,7 +310,7 @@ config XILINX_WATCHDOG
depends on HAS_IOMEM
select WATCHDOG_CORE
help
Watchdog driver for the xps_timebase_wdt ip core.
Watchdog driver for the xps_timebase_wdt IP core.
To compile this driver as a module, choose M here: the
module will be called of_xilinx_wdt.
......@@ -404,8 +412,8 @@ config ASM9260_WATCHDOG
select WATCHDOG_CORE
select RESET_CONTROLLER
help
Watchdog timer embedded into Alphascale asm9260 chips. This will reboot your
system when the timeout is reached.
Watchdog timer embedded into Alphascale asm9260 chips. This will
reboot your system when the timeout is reached.
config AT91RM9200_WATCHDOG
tristate "AT91RM9200 watchdog"
......@@ -548,8 +556,9 @@ config OMAP_WATCHDOG
depends on ARCH_OMAP16XX || ARCH_OMAP2PLUS || COMPILE_TEST
select WATCHDOG_CORE
help
Support for TI OMAP1610/OMAP1710/OMAP2420/OMAP3430/OMAP4430 watchdog. Say 'Y'
here to enable the OMAP1610/OMAP1710/OMAP2420/OMAP3430/OMAP4430 watchdog timer.
Support for TI OMAP1610/OMAP1710/OMAP2420/OMAP3430/OMAP4430 watchdog.
Say 'Y' here to enable the
OMAP1610/OMAP1710/OMAP2420/OMAP3430/OMAP4430 watchdog timer.
config PNX4008_WATCHDOG
tristate "LPC32XX Watchdog"
......@@ -980,6 +989,18 @@ config VISCONTI_WATCHDOG
Say Y here to include support for the watchdog timer in Toshiba
Visconti SoCs.
config MSC313E_WATCHDOG
tristate "MStar MSC313e watchdog"
depends on ARCH_MSTARV7 || COMPILE_TEST
select WATCHDOG_CORE
help
Say Y here to include support for the Watchdog timer embedded
into MStar MSC313e chips. This will reboot your system when the
timeout is reached.
To compile this driver as a module, choose M here: the
module will be called msc313e_wdt.
# X86 (i386 + ia64 + x86_64) Architecture
config ACQUIRE_WDT
......@@ -1096,13 +1117,16 @@ config SBC_FITPC2_WATCHDOG
This is the driver for the built-in watchdog timer on the fit-PC2,
fit-PC2i, CM-iAM single-board computers made by Compulab.
It`s possible to enable watchdog timer either from BIOS (F2) or from booted Linux.
When "Watchdog Timer Value" enabled one can set 31-255 s operational range.
It's possible to enable the watchdog timer either from BIOS (F2) or
from booted Linux.
When the "Watchdog Timer Value" is enabled one can set 31-255 seconds
operational range.
Entering BIOS setup temporary disables watchdog operation regardless to current state,
so system will not be restarted while user in BIOS setup.
Entering BIOS setup temporarily disables watchdog operation regardless
of current state, so system will not be restarted while user is in
BIOS setup.
Once watchdog was enabled the system will be restarted every
Once the watchdog is enabled the system will be restarted every
"Watchdog Timer Value" period, so to prevent it user can restart or
disable the watchdog.
......@@ -1124,11 +1148,12 @@ config IB700_WDT
depends on X86
help
This is the driver for the hardware watchdog on the IB700 Single
Board Computer produced by TMC Technology (www.tmc-uk.com). This watchdog
simply watches your kernel to make sure it doesn't freeze, and if
it does, it reboots your computer after a certain amount of time.
Board Computer produced by TMC Technology (www.tmc-uk.com). This
watchdog simply watches your kernel to make sure it doesn't freeze,
and if it does, it reboots your computer after a certain amount of time.
This driver is like the WDT501 driver but for slightly different hardware.
This driver is like the WDT501 driver but for slightly different
hardware.
To compile this driver as a module, choose M here: the
module will be called ib700wdt.
......@@ -1807,10 +1832,10 @@ config PIC32_DMT
select WATCHDOG_CORE
depends on MACH_PIC32 || (MIPS && COMPILE_TEST)
help
Watchdog driver for PIC32 instruction fetch counting timer. This specific
timer is typically be used in misson critical and safety critical
applications, where any single failure of the software functionality
and sequencing must be detected.
Watchdog driver for PIC32 instruction fetch counting timer. This
specific timer is typically be used in mission critical and safety
critical applications, where any single failure of the software
functionality and sequencing must be detected.
To compile this driver as a loadable module, choose M here.
The module will be called pic32-dmt.
......@@ -1844,10 +1869,6 @@ config 8xxx_WDT
For BookE processors (MPC85xx) use the BOOKE_WDT driver instead.
config MV64X60_WDT
tristate "MV64X60 (Marvell Discovery) Watchdog Timer"
depends on MV64X60 || COMPILE_TEST
config PIKA_WDT
tristate "PIKA FPGA Watchdog"
depends on WARP || (PPC64 && COMPILE_TEST)
......@@ -2013,8 +2034,8 @@ config PCWATCHDOG
This card simply watches your kernel to make sure it doesn't freeze,
and if it does, it reboots your computer after a certain amount of
time. This driver is like the WDT501 driver but for different
hardware. Please read <file:Documentation/watchdog/pcwd-watchdog.rst>. The PC
watchdog cards can be ordered from <http://www.berkprod.com/>.
hardware. Please read <file:Documentation/watchdog/pcwd-watchdog.rst>.
The PC watchdog cards can be ordered from <http://www.berkprod.com/>.
To compile this driver as a module, choose M here: the
module will be called pcwd.
......@@ -2115,7 +2136,7 @@ config KEEMBAY_WATCHDOG
This option enable support for an In-secure watchdog timer driver for
Intel Keem Bay SoC. This WDT has a 32 bit timer and decrements in every
count unit. An interrupt will be triggered, when the count crosses
the thershold configured in the register.
the threshold configured in the register.
To compile this driver as a module, choose M here: the
module will be called keembay_wdt.
......
......@@ -9,6 +9,7 @@ obj-$(CONFIG_WATCHDOG_CORE) += watchdog.o
watchdog-objs += watchdog_core.o watchdog_dev.o
watchdog-$(CONFIG_WATCHDOG_PRETIMEOUT_GOV) += watchdog_pretimeout.o
watchdog-$(CONFIG_WATCHDOG_HRTIMER_PRETIMEOUT) += watchdog_hrtimer_pretimeout.o
obj-$(CONFIG_WATCHDOG_PRETIMEOUT_GOV_NOOP) += pretimeout_noop.o
obj-$(CONFIG_WATCHDOG_PRETIMEOUT_GOV_PANIC) += pretimeout_panic.o
......@@ -92,6 +93,7 @@ obj-$(CONFIG_SPRD_WATCHDOG) += sprd_wdt.o
obj-$(CONFIG_PM8916_WATCHDOG) += pm8916_wdt.o
obj-$(CONFIG_ARM_SMC_WATCHDOG) += arm_smc_wdt.o
obj-$(CONFIG_VISCONTI_WATCHDOG) += visconti_wdt.o
obj-$(CONFIG_MSC313E_WATCHDOG) += msc313e_wdt.o
# X86 (i386 + ia64 + x86_64) Architecture
obj-$(CONFIG_ACQUIRE_WDT) += acquirewdt.o
......@@ -175,7 +177,6 @@ obj-$(CONFIG_PIC32_DMT) += pic32-dmt.o
# POWERPC Architecture
obj-$(CONFIG_GEF_WDT) += gef_wdt.o
obj-$(CONFIG_8xxx_WDT) += mpc8xxx_wdt.o
obj-$(CONFIG_MV64X60_WDT) += mv64x60_wdt.o
obj-$(CONFIG_PIKA_WDT) += pika_wdt.o
obj-$(CONFIG_BOOKE_WDT) += booke_wdt.o
obj-$(CONFIG_MEN_A21_WDT) += mena21_wdt.o
......
......@@ -147,7 +147,7 @@ static int aspeed_wdt_set_timeout(struct watchdog_device *wdd,
wdd->timeout = timeout;
actual = min(timeout, wdd->max_hw_heartbeat_ms * 1000);
actual = min(timeout, wdd->max_hw_heartbeat_ms / 1000);
writel(actual * WDT_RATE_1MHZ, wdt->base + WDT_RELOAD_VALUE);
writel(WDT_RESTART_MAGIC, wdt->base + WDT_RESTART);
......@@ -175,8 +175,8 @@ static ssize_t access_cs0_show(struct device *dev,
struct aspeed_wdt *wdt = dev_get_drvdata(dev);
u32 status = readl(wdt->base + WDT_TIMEOUT_STATUS);
return sprintf(buf, "%u\n",
!(status & WDT_TIMEOUT_STATUS_BOOT_SECONDARY));
return sysfs_emit(buf, "%u\n",
!(status & WDT_TIMEOUT_STATUS_BOOT_SECONDARY));
}
static ssize_t access_cs0_store(struct device *dev,
......
......@@ -34,6 +34,25 @@ struct bcm7038_watchdog {
static bool nowayout = WATCHDOG_NOWAYOUT;
static inline void bcm7038_wdt_write(u32 value, void __iomem *addr)
{
/* MIPS chips strapped for BE will automagically configure the
* peripheral registers for CPU-native byte order.
*/
if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN))
__raw_writel(value, addr);
else
writel_relaxed(value, addr);
}
static inline u32 bcm7038_wdt_read(void __iomem *addr)
{
if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN))
return __raw_readl(addr);
else
return readl_relaxed(addr);
}
static void bcm7038_wdt_set_timeout_reg(struct watchdog_device *wdog)
{
struct bcm7038_watchdog *wdt = watchdog_get_drvdata(wdog);
......@@ -41,15 +60,15 @@ static void bcm7038_wdt_set_timeout_reg(struct watchdog_device *wdog)
timeout = wdt->rate * wdog->timeout;
writel(timeout, wdt->base + WDT_TIMEOUT_REG);
bcm7038_wdt_write(timeout, wdt->base + WDT_TIMEOUT_REG);
}
static int bcm7038_wdt_ping(struct watchdog_device *wdog)
{
struct bcm7038_watchdog *wdt = watchdog_get_drvdata(wdog);
writel(WDT_START_1, wdt->base + WDT_CMD_REG);
writel(WDT_START_2, wdt->base + WDT_CMD_REG);
bcm7038_wdt_write(WDT_START_1, wdt->base + WDT_CMD_REG);
bcm7038_wdt_write(WDT_START_2, wdt->base + WDT_CMD_REG);
return 0;
}
......@@ -66,8 +85,8 @@ static int bcm7038_wdt_stop(struct watchdog_device *wdog)
{
struct bcm7038_watchdog *wdt = watchdog_get_drvdata(wdog);
writel(WDT_STOP_1, wdt->base + WDT_CMD_REG);
writel(WDT_STOP_2, wdt->base + WDT_CMD_REG);
bcm7038_wdt_write(WDT_STOP_1, wdt->base + WDT_CMD_REG);
bcm7038_wdt_write(WDT_STOP_2, wdt->base + WDT_CMD_REG);
return 0;
}
......@@ -88,7 +107,7 @@ static unsigned int bcm7038_wdt_get_timeleft(struct watchdog_device *wdog)
struct bcm7038_watchdog *wdt = watchdog_get_drvdata(wdog);
u32 time_left;
time_left = readl(wdt->base + WDT_CMD_REG);
time_left = bcm7038_wdt_read(wdt->base + WDT_CMD_REG);
return time_left / wdt->rate;
}
......
......@@ -148,7 +148,7 @@ static void __booke_wdt_enable(void *data)
}
/**
* booke_wdt_disable - disable the watchdog on the given CPU
* __booke_wdt_disable - disable the watchdog on the given CPU
*
* This function is called on each CPU. It disables the watchdog on that CPU.
*
......
......@@ -118,8 +118,6 @@ static int wdt_start(struct watchdog_device *dev)
if (test_and_set_bit(DIAG_WDOG_BUSY, &wdt_status))
return -EBUSY;
ret = -ENODEV;
if (MACHINE_IS_VM) {
ebc_cmd = kmalloc(MAX_CMDLEN, GFP_KERNEL);
if (!ebc_cmd) {
......@@ -167,8 +165,6 @@ static int wdt_ping(struct watchdog_device *dev)
int ret;
unsigned int func;
ret = -ENODEV;
if (MACHINE_IS_VM) {
ebc_cmd = kmalloc(MAX_CMDLEN, GFP_KERNEL);
if (!ebc_cmd)
......
......@@ -13,22 +13,21 @@
*/
#include <linux/bitops.h>
#include <linux/limits.h>
#include <linux/kernel.h>
#include <linux/clk.h>
#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/limits.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/interrupt.h>
#include <linux/of.h>
#include <linux/pm.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/reset.h>
#include <linux/watchdog.h>
#include <linux/debugfs.h>
#define WDOG_CONTROL_REG_OFFSET 0x00
#define WDOG_CONTROL_REG_WDT_EN_MASK 0x01
......
......@@ -392,7 +392,7 @@ static struct notifier_block eurwdt_notifier = {
};
/**
* cleanup_module:
* eurwdt_exit:
*
* Unload the watchdog. You cannot do this with any file handles open.
* If your watchdog is set to continue ticking on close and you unload
......
......@@ -45,6 +45,7 @@ static unsigned long __iomem *hpwdt_timer_con;
static const struct pci_device_id hpwdt_devices[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_COMPAQ, 0xB203) }, /* iLO2 */
{ PCI_DEVICE(PCI_VENDOR_ID_HP, 0x3306) }, /* iLO3 */
{ PCI_DEVICE(PCI_VENDOR_ID_HP_3PAR, 0x0389) }, /* PCtrl */
{0}, /* terminate list */
};
MODULE_DEVICE_TABLE(pci, hpwdt_devices);
......
......@@ -71,6 +71,8 @@
#define TCOBASE(p) ((p)->tco_res->start)
/* SMI Control and Enable Register */
#define SMI_EN(p) ((p)->smi_res->start)
#define TCO_EN (1 << 13)
#define GBL_SMI_EN (1 << 0)
#define TCO_RLD(p) (TCOBASE(p) + 0x00) /* TCO Timer Reload/Curr. Value */
#define TCOv1_TMR(p) (TCOBASE(p) + 0x01) /* TCOv1 Timer Initial Value*/
......@@ -355,8 +357,12 @@ static int iTCO_wdt_set_timeout(struct watchdog_device *wd_dev, unsigned int t)
tmrval = seconds_to_ticks(p, t);
/* For TCO v1 the timer counts down twice before rebooting */
if (p->iTCO_version == 1)
/*
* If TCO SMIs are off, the timer counts down twice before rebooting.
* Otherwise, the BIOS generally reboots when the SMI triggers.
*/
if (p->smi_res &&
(SMI_EN(p) & (TCO_EN | GBL_SMI_EN)) != (TCO_EN | GBL_SMI_EN))
tmrval /= 2;
/* from the specs: */
......@@ -479,13 +485,13 @@ static int iTCO_wdt_probe(struct platform_device *pdev)
if (!devm_request_region(dev, p->smi_res->start,
resource_size(p->smi_res),
pdev->name)) {
pr_err("I/O address 0x%04llx already in use, device disabled\n",
dev_err(dev, "I/O address 0x%04llx already in use, device disabled\n",
(u64)SMI_EN(p));
return -EBUSY;
}
} else if (iTCO_vendorsupport ||
turn_SMI_watchdog_clear_off >= p->iTCO_version) {
pr_err("SMI I/O resource is missing\n");
dev_err(dev, "SMI I/O resource is missing\n");
return -ENODEV;
}
......@@ -521,7 +527,7 @@ static int iTCO_wdt_probe(struct platform_device *pdev)
* Disables TCO logic generating an SMI#
*/
val32 = inl(SMI_EN(p));
val32 &= 0xffffdfff; /* Turn off SMI clearing watchdog */
val32 &= ~TCO_EN; /* Turn off SMI clearing watchdog */
outl(val32, SMI_EN(p));
}
......
......@@ -65,6 +65,7 @@ struct imx2_wdt_device {
struct regmap *regmap;
struct watchdog_device wdog;
bool ext_reset;
bool clk_is_on;
};
static bool nowayout = WATCHDOG_NOWAYOUT;
......@@ -160,6 +161,9 @@ static int imx2_wdt_ping(struct watchdog_device *wdog)
{
struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog);
if (!wdev->clk_is_on)
return 0;
regmap_write(wdev->regmap, IMX2_WDT_WSR, IMX2_WDT_SEQ1);
regmap_write(wdev->regmap, IMX2_WDT_WSR, IMX2_WDT_SEQ2);
return 0;
......@@ -301,6 +305,8 @@ static int __init imx2_wdt_probe(struct platform_device *pdev)
if (ret)
return ret;
wdev->clk_is_on = true;
regmap_read(wdev->regmap, IMX2_WDT_WRSR, &val);
wdog->bootstatus = val & IMX2_WDT_WRSR_TOUT ? WDIOF_CARDRESET : 0;
......@@ -361,6 +367,8 @@ static int __maybe_unused imx2_wdt_suspend(struct device *dev)
clk_disable_unprepare(wdev->clk);
wdev->clk_is_on = false;
return 0;
}
......@@ -375,6 +383,8 @@ static int __maybe_unused imx2_wdt_resume(struct device *dev)
if (ret)
return ret;
wdev->clk_is_on = true;
if (watchdog_active(wdog) && !imx2_wdt_is_running(wdev)) {
/*
* If the watchdog is still active and resumes
......
......@@ -183,16 +183,12 @@ static int imx_sc_wdt_probe(struct platform_device *pdev)
watchdog_stop_on_reboot(wdog);
watchdog_stop_on_unregister(wdog);
ret = devm_watchdog_register_device(dev, wdog);
if (ret)
return ret;
ret = imx_scu_irq_group_enable(SC_IRQ_GROUP_WDOG,
SC_IRQ_WDOG,
true);
if (ret) {
dev_warn(dev, "Enable irq failed, pretimeout NOT supported\n");
return 0;
goto register_device;
}
imx_sc_wdd->wdt_notifier.notifier_call = imx_sc_wdt_notify;
......@@ -203,7 +199,7 @@ static int imx_sc_wdt_probe(struct platform_device *pdev)
false);
dev_warn(dev,
"Register irq notifier failed, pretimeout NOT supported\n");
return 0;
goto register_device;
}
ret = devm_add_action_or_reset(dev, imx_sc_wdt_action,
......@@ -213,7 +209,8 @@ static int imx_sc_wdt_probe(struct platform_device *pdev)
else
dev_warn(dev, "Add action failed, pretimeout NOT supported\n");
return 0;
register_device:
return devm_watchdog_register_device(dev, wdog);
}
static int __maybe_unused imx_sc_wdt_suspend(struct device *dev)
......
......@@ -152,14 +152,6 @@ static inline int superio_inw(int reg)
return val;
}
static inline void superio_outw(int val, int reg)
{
outb(reg++, REG);
outb(val >> 8, VAL);
outb(reg, REG);
outb(val, VAL);
}
/* Internal function, should be called after superio_select(GPIO) */
static void _wdt_update_timeout(unsigned int t)
{
......
......@@ -176,9 +176,9 @@ static int jz4740_wdt_probe(struct platform_device *pdev)
watchdog_set_drvdata(jz4740_wdt, drvdata);
drvdata->map = device_node_to_regmap(dev->parent->of_node);
if (!drvdata->map) {
if (IS_ERR(drvdata->map)) {
dev_err(dev, "regmap not found\n");
return -EINVAL;
return PTR_ERR(drvdata->map);
}
return devm_watchdog_register_device(dev, &drvdata->wdt);
......
......@@ -23,12 +23,19 @@
#define TIM_WDOG_EN 0x8
#define TIM_SAFE 0xc
#define WDT_ISR_MASK GENMASK(9, 8)
#define WDT_ISR_CLEAR 0x8200ff18
#define WDT_TH_INT_MASK BIT(8)
#define WDT_TO_INT_MASK BIT(9)
#define WDT_INT_CLEAR_SMC 0x8200ff18
#define WDT_UNLOCK 0xf1d0dead
#define WDT_DISABLE 0x0
#define WDT_ENABLE 0x1
#define WDT_LOAD_MAX U32_MAX
#define WDT_LOAD_MIN 1
#define WDT_TIMEOUT 5
#define WDT_PRETIMEOUT 4
static unsigned int timeout = WDT_TIMEOUT;
module_param(timeout, int, 0);
......@@ -82,8 +89,7 @@ static int keembay_wdt_start(struct watchdog_device *wdog)
{
struct keembay_wdt *wdt = watchdog_get_drvdata(wdog);
keembay_wdt_set_timeout_reg(wdog);
keembay_wdt_writel(wdt, TIM_WDOG_EN, 1);
keembay_wdt_writel(wdt, TIM_WDOG_EN, WDT_ENABLE);
return 0;
}
......@@ -92,7 +98,7 @@ static int keembay_wdt_stop(struct watchdog_device *wdog)
{
struct keembay_wdt *wdt = watchdog_get_drvdata(wdog);
keembay_wdt_writel(wdt, TIM_WDOG_EN, 0);
keembay_wdt_writel(wdt, TIM_WDOG_EN, WDT_DISABLE);
return 0;
}
......@@ -108,6 +114,7 @@ static int keembay_wdt_set_timeout(struct watchdog_device *wdog, u32 t)
{
wdog->timeout = t;
keembay_wdt_set_timeout_reg(wdog);
keembay_wdt_set_pretimeout_reg(wdog);
return 0;
}
......@@ -139,9 +146,8 @@ static irqreturn_t keembay_wdt_to_isr(int irq, void *dev_id)
struct keembay_wdt *wdt = dev_id;
struct arm_smccc_res res;
keembay_wdt_writel(wdt, TIM_WATCHDOG, 1);
arm_smccc_smc(WDT_ISR_CLEAR, WDT_ISR_MASK, 0, 0, 0, 0, 0, 0, &res);
dev_crit(wdt->wdd.parent, "Intel Keem Bay non-sec wdt timeout.\n");
arm_smccc_smc(WDT_INT_CLEAR_SMC, WDT_TO_INT_MASK, 0, 0, 0, 0, 0, 0, &res);
dev_crit(wdt->wdd.parent, "Intel Keem Bay non-secure wdt timeout.\n");
emergency_restart();
return IRQ_HANDLED;
......@@ -152,8 +158,10 @@ static irqreturn_t keembay_wdt_th_isr(int irq, void *dev_id)
struct keembay_wdt *wdt = dev_id;
struct arm_smccc_res res;
arm_smccc_smc(WDT_ISR_CLEAR, WDT_ISR_MASK, 0, 0, 0, 0, 0, 0, &res);
dev_crit(wdt->wdd.parent, "Intel Keem Bay non-sec wdt pre-timeout.\n");
keembay_wdt_set_pretimeout(&wdt->wdd, 0x0);
arm_smccc_smc(WDT_INT_CLEAR_SMC, WDT_TH_INT_MASK, 0, 0, 0, 0, 0, 0, &res);
dev_crit(wdt->wdd.parent, "Intel Keem Bay non-secure wdt pre-timeout.\n");
watchdog_notify_pretimeout(&wdt->wdd);
return IRQ_HANDLED;
......@@ -224,11 +232,13 @@ static int keembay_wdt_probe(struct platform_device *pdev)
wdt->wdd.min_timeout = WDT_LOAD_MIN;
wdt->wdd.max_timeout = WDT_LOAD_MAX / wdt->rate;
wdt->wdd.timeout = WDT_TIMEOUT;
wdt->wdd.pretimeout = WDT_PRETIMEOUT;
watchdog_set_drvdata(&wdt->wdd, wdt);
watchdog_set_nowayout(&wdt->wdd, nowayout);
watchdog_init_timeout(&wdt->wdd, timeout, dev);
keembay_wdt_set_timeout(&wdt->wdd, wdt->wdd.timeout);
keembay_wdt_set_pretimeout(&wdt->wdd, wdt->wdd.pretimeout);
ret = devm_watchdog_register_device(dev, &wdt->wdd);
if (ret)
......@@ -271,8 +281,8 @@ static const struct of_device_id keembay_wdt_match[] = {
MODULE_DEVICE_TABLE(of, keembay_wdt_match);
static struct platform_driver keembay_wdt_driver = {
.probe = keembay_wdt_probe,
.driver = {
.probe = keembay_wdt_probe,
.driver = {
.name = "keembay_wdt",
.of_match_table = keembay_wdt_match,
.pm = &keembay_wdt_pm_ops,
......
......@@ -292,7 +292,7 @@ static int lpc18xx_wdt_remove(struct platform_device *pdev)
struct lpc18xx_wdt_dev *lpc18xx_wdt = platform_get_drvdata(pdev);
dev_warn(&pdev->dev, "I quit now, hardware will probably reboot!\n");
del_timer(&lpc18xx_wdt->timer);
del_timer_sync(&lpc18xx_wdt->timer);
return 0;
}
......
......@@ -105,7 +105,7 @@ struct mei_wdt {
#endif /* CONFIG_DEBUG_FS */
};
/*
/**
* struct mei_mc_hdr - Management Control Command Header
*
* @command: Management Control (0x2)
......@@ -474,7 +474,7 @@ static void mei_wdt_rx(struct mei_cl_device *cldev)
complete(&wdt->response);
}
/*
/**
* mei_wdt_notif - callback for event notification
*
* @cldev: bus device
......
......@@ -162,7 +162,6 @@ static int meson_wdt_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct meson_wdt_dev *meson_wdt;
const struct of_device_id *of_id;
int err;
meson_wdt = devm_kzalloc(dev, sizeof(*meson_wdt), GFP_KERNEL);
......@@ -173,12 +172,7 @@ static int meson_wdt_probe(struct platform_device *pdev)
if (IS_ERR(meson_wdt->wdt_base))
return PTR_ERR(meson_wdt->wdt_base);
of_id = of_match_device(meson_wdt_dt_ids, dev);
if (!of_id) {
dev_err(dev, "Unable to initialize WDT data\n");
return -ENODEV;
}
meson_wdt->data = of_id->data;
meson_wdt->data = device_get_match_data(dev);
meson_wdt->wdt_dev.parent = dev;
meson_wdt->wdt_dev.info = &meson_wdt_info;
......
// SPDX-License-Identifier: GPL-2.0
/*
* MStar WDT driver
*
* Copyright (C) 2019 - 2021 Daniel Palmer
* Copyright (C) 2021 Romain Perier
*
*/
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/watchdog.h>
#define REG_WDT_CLR 0x0
#define REG_WDT_MAX_PRD_L 0x10
#define REG_WDT_MAX_PRD_H 0x14
#define MSC313E_WDT_MIN_TIMEOUT 1
#define MSC313E_WDT_DEFAULT_TIMEOUT 30
static unsigned int timeout;
module_param(timeout, int, 0);
MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds");
struct msc313e_wdt_priv {
void __iomem *base;
struct watchdog_device wdev;
struct clk *clk;
};
static int msc313e_wdt_start(struct watchdog_device *wdev)
{
struct msc313e_wdt_priv *priv = watchdog_get_drvdata(wdev);
u32 timeout;
int err;
err = clk_prepare_enable(priv->clk);
if (err)
return err;
timeout = wdev->timeout * clk_get_rate(priv->clk);
writew(timeout & 0xffff, priv->base + REG_WDT_MAX_PRD_L);
writew((timeout >> 16) & 0xffff, priv->base + REG_WDT_MAX_PRD_H);
writew(1, priv->base + REG_WDT_CLR);
return 0;
}
static int msc313e_wdt_ping(struct watchdog_device *wdev)
{
struct msc313e_wdt_priv *priv = watchdog_get_drvdata(wdev);
writew(1, priv->base + REG_WDT_CLR);
return 0;
}
static int msc313e_wdt_stop(struct watchdog_device *wdev)
{
struct msc313e_wdt_priv *priv = watchdog_get_drvdata(wdev);
writew(0, priv->base + REG_WDT_MAX_PRD_L);
writew(0, priv->base + REG_WDT_MAX_PRD_H);
writew(0, priv->base + REG_WDT_CLR);
clk_disable_unprepare(priv->clk);
return 0;
}
static int msc313e_wdt_settimeout(struct watchdog_device *wdev, unsigned int new_time)
{
wdev->timeout = new_time;
return msc313e_wdt_start(wdev);
}
static const struct watchdog_info msc313e_wdt_ident = {
.identity = "MSC313e watchdog",
.options = WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT,
};
static const struct watchdog_ops msc313e_wdt_ops = {
.owner = THIS_MODULE,
.start = msc313e_wdt_start,
.stop = msc313e_wdt_stop,
.ping = msc313e_wdt_ping,
.set_timeout = msc313e_wdt_settimeout,
};
static const struct of_device_id msc313e_wdt_of_match[] = {
{ .compatible = "mstar,msc313e-wdt", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, msc313e_wdt_of_match);
static int msc313e_wdt_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct msc313e_wdt_priv *priv;
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(priv->base))
return PTR_ERR(priv->base);
priv->clk = devm_clk_get(dev, NULL);
if (IS_ERR(priv->clk)) {
dev_err(dev, "No input clock\n");
return PTR_ERR(priv->clk);
}
priv->wdev.info = &msc313e_wdt_ident,
priv->wdev.ops = &msc313e_wdt_ops,
priv->wdev.parent = dev;
priv->wdev.min_timeout = MSC313E_WDT_MIN_TIMEOUT;
priv->wdev.max_timeout = U32_MAX / clk_get_rate(priv->clk);
priv->wdev.timeout = MSC313E_WDT_DEFAULT_TIMEOUT;
watchdog_set_drvdata(&priv->wdev, priv);
watchdog_init_timeout(&priv->wdev, timeout, dev);
watchdog_stop_on_reboot(&priv->wdev);
watchdog_stop_on_unregister(&priv->wdev);
return devm_watchdog_register_device(dev, &priv->wdev);
}
static int __maybe_unused msc313e_wdt_suspend(struct device *dev)
{
struct msc313e_wdt_priv *priv = dev_get_drvdata(dev);
if (watchdog_active(&priv->wdev))
msc313e_wdt_stop(&priv->wdev);
return 0;
}
static int __maybe_unused msc313e_wdt_resume(struct device *dev)
{
struct msc313e_wdt_priv *priv = dev_get_drvdata(dev);
if (watchdog_active(&priv->wdev))
msc313e_wdt_start(&priv->wdev);
return 0;
}
static SIMPLE_DEV_PM_OPS(msc313e_wdt_pm_ops, msc313e_wdt_suspend, msc313e_wdt_resume);
static struct platform_driver msc313e_wdt_driver = {
.driver = {
.name = "msc313e-wdt",
.of_match_table = msc313e_wdt_of_match,
.pm = &msc313e_wdt_pm_ops,
},
.probe = msc313e_wdt_probe,
};
module_platform_driver(msc313e_wdt_driver);
MODULE_AUTHOR("Daniel Palmer <daniel@thingy.jp>");
MODULE_DESCRIPTION("Watchdog driver for MStar MSC313e");
MODULE_LICENSE("GPL v2");
......@@ -25,9 +25,10 @@
#include <linux/reset-controller.h>
#include <linux/types.h>
#include <linux/watchdog.h>
#include <linux/interrupt.h>
#define WDT_MAX_TIMEOUT 31
#define WDT_MIN_TIMEOUT 1
#define WDT_MIN_TIMEOUT 2
#define WDT_LENGTH_TIMEOUT(n) ((n) << 5)
#define WDT_LENGTH 0x04
......@@ -187,12 +188,19 @@ static int mtk_wdt_set_timeout(struct watchdog_device *wdt_dev,
u32 reg;
wdt_dev->timeout = timeout;
/*
* In dual mode, irq will be triggered at timeout / 2
* the real timeout occurs at timeout
*/
if (wdt_dev->pretimeout)
wdt_dev->pretimeout = timeout / 2;
/*
* One bit is the value of 512 ticks
* The clock has 32 KHz
*/
reg = WDT_LENGTH_TIMEOUT(timeout << 6) | WDT_LENGTH_KEY;
reg = WDT_LENGTH_TIMEOUT((timeout - wdt_dev->pretimeout) << 6)
| WDT_LENGTH_KEY;
iowrite32(reg, wdt_base + WDT_LENGTH);
mtk_wdt_ping(wdt_dev);
......@@ -239,13 +247,48 @@ static int mtk_wdt_start(struct watchdog_device *wdt_dev)
return ret;
reg = ioread32(wdt_base + WDT_MODE);
reg &= ~(WDT_MODE_IRQ_EN | WDT_MODE_DUAL_EN);
if (wdt_dev->pretimeout)
reg |= (WDT_MODE_IRQ_EN | WDT_MODE_DUAL_EN);
else
reg &= ~(WDT_MODE_IRQ_EN | WDT_MODE_DUAL_EN);
reg |= (WDT_MODE_EN | WDT_MODE_KEY);
iowrite32(reg, wdt_base + WDT_MODE);
return 0;
}
static int mtk_wdt_set_pretimeout(struct watchdog_device *wdd,
unsigned int timeout)
{
struct mtk_wdt_dev *mtk_wdt = watchdog_get_drvdata(wdd);
void __iomem *wdt_base = mtk_wdt->wdt_base;
u32 reg = ioread32(wdt_base + WDT_MODE);
if (timeout && !wdd->pretimeout) {
wdd->pretimeout = wdd->timeout / 2;
reg |= (WDT_MODE_IRQ_EN | WDT_MODE_DUAL_EN);
} else if (!timeout && wdd->pretimeout) {
wdd->pretimeout = 0;
reg &= ~(WDT_MODE_IRQ_EN | WDT_MODE_DUAL_EN);
} else {
return 0;
}
reg |= WDT_MODE_KEY;
iowrite32(reg, wdt_base + WDT_MODE);
return mtk_wdt_set_timeout(wdd, wdd->timeout);
}
static irqreturn_t mtk_wdt_isr(int irq, void *arg)
{
struct watchdog_device *wdd = arg;
watchdog_notify_pretimeout(wdd);
return IRQ_HANDLED;
}
static const struct watchdog_info mtk_wdt_info = {
.identity = DRV_NAME,
.options = WDIOF_SETTIMEOUT |
......@@ -253,12 +296,21 @@ static const struct watchdog_info mtk_wdt_info = {
WDIOF_MAGICCLOSE,
};
static const struct watchdog_info mtk_wdt_pt_info = {
.identity = DRV_NAME,
.options = WDIOF_SETTIMEOUT |
WDIOF_PRETIMEOUT |
WDIOF_KEEPALIVEPING |
WDIOF_MAGICCLOSE,
};
static const struct watchdog_ops mtk_wdt_ops = {
.owner = THIS_MODULE,
.start = mtk_wdt_start,
.stop = mtk_wdt_stop,
.ping = mtk_wdt_ping,
.set_timeout = mtk_wdt_set_timeout,
.set_pretimeout = mtk_wdt_set_pretimeout,
.restart = mtk_wdt_restart,
};
......@@ -267,7 +319,7 @@ static int mtk_wdt_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct mtk_wdt_dev *mtk_wdt;
const struct mtk_wdt_data *wdt_data;
int err;
int err, irq;
mtk_wdt = devm_kzalloc(dev, sizeof(*mtk_wdt), GFP_KERNEL);
if (!mtk_wdt)
......@@ -279,7 +331,22 @@ static int mtk_wdt_probe(struct platform_device *pdev)
if (IS_ERR(mtk_wdt->wdt_base))
return PTR_ERR(mtk_wdt->wdt_base);
mtk_wdt->wdt_dev.info = &mtk_wdt_info;
irq = platform_get_irq(pdev, 0);
if (irq > 0) {
err = devm_request_irq(&pdev->dev, irq, mtk_wdt_isr, 0, "wdt_bark",
&mtk_wdt->wdt_dev);
if (err)
return err;
mtk_wdt->wdt_dev.info = &mtk_wdt_pt_info;
mtk_wdt->wdt_dev.pretimeout = WDT_MAX_TIMEOUT / 2;
} else {
if (irq == -EPROBE_DEFER)
return -EPROBE_DEFER;
mtk_wdt->wdt_dev.info = &mtk_wdt_info;
}
mtk_wdt->wdt_dev.ops = &mtk_wdt_ops;
mtk_wdt->wdt_dev.timeout = WDT_MAX_TIMEOUT;
mtk_wdt->wdt_dev.max_hw_heartbeat_ms = WDT_MAX_TIMEOUT * 1000;
......
......@@ -41,8 +41,6 @@
#include <linux/uaccess.h>
#include <linux/gpio/consumer.h>
#include <asm/mach-au1x00/au1000.h>
#define MTX1_WDT_INTERVAL (5 * HZ)
static int ticks = 100 * HZ;
......
// SPDX-License-Identifier: GPL-2.0
/*
* mv64x60_wdt.c - MV64X60 (Marvell Discovery) watchdog userspace interface
*
* Author: James Chapman <jchapman@katalix.com>
*
* Platform-specific setup code should configure the dog to generate
* interrupt or reset as required. This code only enables/disables
* and services the watchdog.
*
* Derived from mpc8xx_wdt.c, with the following copyright.
*
* 2002 (c) Florian Schirmer <jolt@tuxbox.org>
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/watchdog.h>
#include <linux/platform_device.h>
#include <linux/mv643xx.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#define MV64x60_WDT_WDC_OFFSET 0
/*
* The watchdog configuration register contains a pair of 2-bit fields,
* 1. a reload field, bits 27-26, which triggers a reload of
* the countdown register, and
* 2. an enable field, bits 25-24, which toggles between
* enabling and disabling the watchdog timer.
* Bit 31 is a read-only field which indicates whether the
* watchdog timer is currently enabled.
*
* The low 24 bits contain the timer reload value.
*/
#define MV64x60_WDC_ENABLE_SHIFT 24
#define MV64x60_WDC_SERVICE_SHIFT 26
#define MV64x60_WDC_ENABLED_SHIFT 31
#define MV64x60_WDC_ENABLED_TRUE 1
#define MV64x60_WDC_ENABLED_FALSE 0
/* Flags bits */
#define MV64x60_WDOG_FLAG_OPENED 0
static unsigned long wdt_flags;
static int wdt_status;
static void __iomem *mv64x60_wdt_regs;
static int mv64x60_wdt_timeout;
static int mv64x60_wdt_count;
static unsigned int bus_clk;
static char expect_close;
static DEFINE_SPINLOCK(mv64x60_wdt_spinlock);
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 mv64x60_wdt_toggle_wdc(int enabled_predicate, int field_shift)
{
u32 data;
u32 enabled;
int ret = 0;
spin_lock(&mv64x60_wdt_spinlock);
data = readl(mv64x60_wdt_regs + MV64x60_WDT_WDC_OFFSET);
enabled = (data >> MV64x60_WDC_ENABLED_SHIFT) & 1;
/* only toggle the requested field if enabled state matches predicate */
if ((enabled ^ enabled_predicate) == 0) {
/* We write a 1, then a 2 -- to the appropriate field */
data = (1 << field_shift) | mv64x60_wdt_count;
writel(data, mv64x60_wdt_regs + MV64x60_WDT_WDC_OFFSET);
data = (2 << field_shift) | mv64x60_wdt_count;
writel(data, mv64x60_wdt_regs + MV64x60_WDT_WDC_OFFSET);
ret = 1;
}
spin_unlock(&mv64x60_wdt_spinlock);
return ret;
}
static void mv64x60_wdt_service(void)
{
mv64x60_wdt_toggle_wdc(MV64x60_WDC_ENABLED_TRUE,
MV64x60_WDC_SERVICE_SHIFT);
}
static void mv64x60_wdt_handler_enable(void)
{
if (mv64x60_wdt_toggle_wdc(MV64x60_WDC_ENABLED_FALSE,
MV64x60_WDC_ENABLE_SHIFT)) {
mv64x60_wdt_service();
pr_notice("watchdog activated\n");
}
}
static void mv64x60_wdt_handler_disable(void)
{
if (mv64x60_wdt_toggle_wdc(MV64x60_WDC_ENABLED_TRUE,
MV64x60_WDC_ENABLE_SHIFT))
pr_notice("watchdog deactivated\n");
}
static void mv64x60_wdt_set_timeout(unsigned int timeout)
{
/* maximum bus cycle count is 0xFFFFFFFF */
if (timeout > 0xFFFFFFFF / bus_clk)
timeout = 0xFFFFFFFF / bus_clk;
mv64x60_wdt_count = timeout * bus_clk >> 8;
mv64x60_wdt_timeout = timeout;
}
static int mv64x60_wdt_open(struct inode *inode, struct file *file)
{
if (test_and_set_bit(MV64x60_WDOG_FLAG_OPENED, &wdt_flags))
return -EBUSY;
if (nowayout)
__module_get(THIS_MODULE);
mv64x60_wdt_handler_enable();
return stream_open(inode, file);
}
static int mv64x60_wdt_release(struct inode *inode, struct file *file)
{
if (expect_close == 42)
mv64x60_wdt_handler_disable();
else {
pr_crit("unexpected close, not stopping timer!\n");
mv64x60_wdt_service();
}
expect_close = 0;
clear_bit(MV64x60_WDOG_FLAG_OPENED, &wdt_flags);
return 0;
}
static ssize_t mv64x60_wdt_write(struct file *file, const char __user *data,
size_t len, loff_t *ppos)
{
if (len) {
if (!nowayout) {
size_t i;
expect_close = 0;
for (i = 0; i != len; i++) {
char c;
if (get_user(c, data + i))
return -EFAULT;
if (c == 'V')
expect_close = 42;
}
}
mv64x60_wdt_service();
}
return len;
}
static long mv64x60_wdt_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
{
int timeout;
int options;
void __user *argp = (void __user *)arg;
static const struct watchdog_info info = {
.options = WDIOF_SETTIMEOUT |
WDIOF_MAGICCLOSE |
WDIOF_KEEPALIVEPING,
.firmware_version = 0,
.identity = "MV64x60 watchdog",
};
switch (cmd) {
case WDIOC_GETSUPPORT:
if (copy_to_user(argp, &info, sizeof(info)))
return -EFAULT;
break;
case WDIOC_GETSTATUS:
case WDIOC_GETBOOTSTATUS:
if (put_user(wdt_status, (int __user *)argp))
return -EFAULT;
wdt_status &= ~WDIOF_KEEPALIVEPING;
break;
case WDIOC_GETTEMP:
return -EOPNOTSUPP;
case WDIOC_SETOPTIONS:
if (get_user(options, (int __user *)argp))
return -EFAULT;
if (options & WDIOS_DISABLECARD)
mv64x60_wdt_handler_disable();
if (options & WDIOS_ENABLECARD)
mv64x60_wdt_handler_enable();
break;
case WDIOC_KEEPALIVE:
mv64x60_wdt_service();
wdt_status |= WDIOF_KEEPALIVEPING;
break;
case WDIOC_SETTIMEOUT:
if (get_user(timeout, (int __user *)argp))
return -EFAULT;
mv64x60_wdt_set_timeout(timeout);
fallthrough;
case WDIOC_GETTIMEOUT:
if (put_user(mv64x60_wdt_timeout, (int __user *)argp))
return -EFAULT;
break;
default:
return -ENOTTY;
}
return 0;
}
static const struct file_operations mv64x60_wdt_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.write = mv64x60_wdt_write,
.unlocked_ioctl = mv64x60_wdt_ioctl,
.compat_ioctl = compat_ptr_ioctl,
.open = mv64x60_wdt_open,
.release = mv64x60_wdt_release,
};
static struct miscdevice mv64x60_wdt_miscdev = {
.minor = WATCHDOG_MINOR,
.name = "watchdog",
.fops = &mv64x60_wdt_fops,
};
static int mv64x60_wdt_probe(struct platform_device *dev)
{
struct mv64x60_wdt_pdata *pdata = dev_get_platdata(&dev->dev);
struct resource *r;
int timeout = 10;
bus_clk = 133; /* in MHz */
if (pdata) {
timeout = pdata->timeout;
bus_clk = pdata->bus_clk;
}
/* Since bus_clk is truncated MHz, actual frequency could be
* up to 1MHz higher. Round up, since it's better to time out
* too late than too soon.
*/
bus_clk++;
bus_clk *= 1000000; /* convert to Hz */
r = platform_get_resource(dev, IORESOURCE_MEM, 0);
if (!r)
return -ENODEV;
mv64x60_wdt_regs = devm_ioremap(&dev->dev, r->start, resource_size(r));
if (mv64x60_wdt_regs == NULL)
return -ENOMEM;
mv64x60_wdt_set_timeout(timeout);
mv64x60_wdt_handler_disable(); /* in case timer was already running */
return misc_register(&mv64x60_wdt_miscdev);
}
static int mv64x60_wdt_remove(struct platform_device *dev)
{
misc_deregister(&mv64x60_wdt_miscdev);
mv64x60_wdt_handler_disable();
return 0;
}
static struct platform_driver mv64x60_wdt_driver = {
.probe = mv64x60_wdt_probe,
.remove = mv64x60_wdt_remove,
.driver = {
.name = MV64x60_WDT_NAME,
},
};
static int __init mv64x60_wdt_init(void)
{
pr_info("MV64x60 watchdog driver\n");
return platform_driver_register(&mv64x60_wdt_driver);
}
static void __exit mv64x60_wdt_exit(void)
{
platform_driver_unregister(&mv64x60_wdt_driver);
}
module_init(mv64x60_wdt_init);
module_exit(mv64x60_wdt_exit);
MODULE_AUTHOR("James Chapman <jchapman@katalix.com>");
MODULE_DESCRIPTION("MV64x60 watchdog driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:" MV64x60_WDT_NAME);
......@@ -120,7 +120,7 @@ static int cpu2core(int cpu)
}
/**
* Poke the watchdog when an interrupt is received
* octeon_wdt_poke_irq - Poke the watchdog when an interrupt is received
*
* @cpl:
* @dev_id:
......@@ -154,7 +154,7 @@ static irqreturn_t octeon_wdt_poke_irq(int cpl, void *dev_id)
extern int prom_putchar(char c);
/**
* Write a string to the uart
* octeon_wdt_write_string - Write a string to the uart
*
* @str: String to write
*/
......@@ -166,7 +166,7 @@ static void octeon_wdt_write_string(const char *str)
}
/**
* Write a hex number out of the uart
* octeon_wdt_write_hex() - Write a hex number out of the uart
*
* @value: Number to display
* @digits: Number of digits to print (1 to 16)
......@@ -193,6 +193,8 @@ static const char reg_name[][3] = {
};
/**
* octeon_wdt_nmi_stage3:
*
* NMI stage 3 handler. NMIs are handled in the following manner:
* 1) The first NMI handler enables CVMSEG and transfers from
* the bootbus region into normal memory. It is careful to not
......@@ -514,7 +516,7 @@ static struct watchdog_device octeon_wdt = {
static enum cpuhp_state octeon_wdt_online;
/**
* Module/ driver initialization.
* octeon_wdt_init - Module/ driver initialization.
*
* Returns Zero on success
*/
......@@ -586,7 +588,7 @@ static int __init octeon_wdt_init(void)
}
/**
* Module / driver shutdown
* octeon_wdt_cleanup - Module / driver shutdown
*/
static void __exit octeon_wdt_cleanup(void)
{
......
......@@ -6,6 +6,7 @@
* (C) Copyright 2011 (Alejandro Cabrera <aldaya@gmail.com>)
*/
#include <linux/bits.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/module.h>
......@@ -24,12 +25,12 @@
#define XWT_TBR_OFFSET 0x8 /* Timebase Register Offset */
/* Control/Status Register Masks */
#define XWT_CSR0_WRS_MASK 0x00000008 /* Reset status */
#define XWT_CSR0_WDS_MASK 0x00000004 /* Timer state */
#define XWT_CSR0_EWDT1_MASK 0x00000002 /* Enable bit 1 */
#define XWT_CSR0_WRS_MASK BIT(3) /* Reset status */
#define XWT_CSR0_WDS_MASK BIT(2) /* Timer state */
#define XWT_CSR0_EWDT1_MASK BIT(1) /* Enable bit 1 */
/* Control/Status Register 0/1 bits */
#define XWT_CSRX_EWDT2_MASK 0x00000001 /* Enable bit 2 */
#define XWT_CSRX_EWDT2_MASK BIT(0) /* Enable bit 2 */
/* SelfTest constants */
#define XWT_MAX_SELFTEST_LOOP_COUNT 0x00010000
......@@ -40,7 +41,7 @@
struct xwdt_device {
void __iomem *base;
u32 wdt_interval;
spinlock_t spinlock;
spinlock_t spinlock; /* spinlock for register handling */
struct watchdog_device xilinx_wdt_wdd;
struct clk *clk;
};
......@@ -70,6 +71,8 @@ static int xilinx_wdt_start(struct watchdog_device *wdd)
spin_unlock(&xdev->spinlock);
dev_dbg(wdd->parent, "Watchdog Started!\n");
return 0;
}
......@@ -91,7 +94,7 @@ static int xilinx_wdt_stop(struct watchdog_device *wdd)
clk_disable(xdev->clk);
pr_info("Stopped!\n");
dev_dbg(wdd->parent, "Watchdog Stopped!\n");
return 0;
}
......@@ -208,6 +211,15 @@ static int xwdt_probe(struct platform_device *pdev)
"The watchdog clock freq cannot be obtained\n");
} else {
pfreq = clk_get_rate(xdev->clk);
rc = clk_prepare_enable(xdev->clk);
if (rc) {
dev_err(dev, "unable to enable clock\n");
return rc;
}
rc = devm_add_action_or_reset(dev, xwdt_clk_disable_unprepare,
xdev->clk);
if (rc)
return rc;
}
/*
......@@ -221,16 +233,6 @@ static int xwdt_probe(struct platform_device *pdev)
spin_lock_init(&xdev->spinlock);
watchdog_set_drvdata(xilinx_wdt_wdd, xdev);
rc = clk_prepare_enable(xdev->clk);
if (rc) {
dev_err(dev, "unable to enable clock\n");
return rc;
}
rc = devm_add_action_or_reset(dev, xwdt_clk_disable_unprepare,
xdev->clk);
if (rc)
return rc;
rc = xwdt_selftest(xdev);
if (rc == XWT_TIMER_FAILED) {
dev_err(dev, "SelfTest routine error\n");
......@@ -243,8 +245,8 @@ static int xwdt_probe(struct platform_device *pdev)
clk_disable(xdev->clk);
dev_info(dev, "Xilinx Watchdog Timer at %p with timeout %ds\n",
xdev->base, xilinx_wdt_wdd->timeout);
dev_info(dev, "Xilinx Watchdog Timer with timeout %ds\n",
xilinx_wdt_wdd->timeout);
platform_set_drvdata(pdev, xdev);
......
......@@ -174,7 +174,7 @@ static int armadaxp_wdt_clock_init(struct platform_device *pdev,
return ret;
}
/* Fix the wdt and timer1 clock freqency to 25MHz */
/* Fix the wdt and timer1 clock frequency to 25MHz */
val = WDT_AXP_FIXED_ENABLE_BIT | TIMER1_FIXED_ENABLE_BIT;
atomic_io_modify(dev->reg + TIMER_CTRL, val, val);
......
......@@ -445,7 +445,7 @@ static long pc87413_ioctl(struct file *file, unsigned int cmd,
/* -- Notifier funtions -----------------------------------------*/
/**
* notify_sys:
* pc87413_notify_sys:
* @this: our notifier block
* @code: the event being reported
* @unused: unused
......
......@@ -329,7 +329,9 @@ static int __maybe_unused qcom_wdt_resume(struct device *dev)
return 0;
}
static SIMPLE_DEV_PM_OPS(qcom_wdt_pm_ops, qcom_wdt_suspend, qcom_wdt_resume);
static const struct dev_pm_ops qcom_wdt_pm_ops = {
SET_LATE_SYSTEM_SLEEP_PM_OPS(qcom_wdt_suspend, qcom_wdt_resume)
};
static const struct of_device_id qcom_wdt_of_table[] = {
{ .compatible = "qcom,kpss-timer", .data = &match_data_apcs_tmr },
......
......@@ -268,8 +268,10 @@ static int sama5d4_wdt_probe(struct platform_device *pdev)
wdd->min_timeout = MIN_WDT_TIMEOUT;
wdd->max_timeout = MAX_WDT_TIMEOUT;
wdt->last_ping = jiffies;
wdt->sam9x60_support = of_device_is_compatible(dev->of_node,
"microchip,sam9x60-wdt");
if (of_device_is_compatible(dev->of_node, "microchip,sam9x60-wdt") ||
of_device_is_compatible(dev->of_node, "microchip,sama7g5-wdt"))
wdt->sam9x60_support = true;
watchdog_set_drvdata(wdd, wdt);
......@@ -329,6 +331,10 @@ static const struct of_device_id sama5d4_wdt_of_match[] = {
{
.compatible = "microchip,sam9x60-wdt",
},
{
.compatible = "microchip,sama7g5-wdt",
},
{ }
};
MODULE_DEVICE_TABLE(of, sama5d4_wdt_of_match);
......
......@@ -146,7 +146,7 @@ static void wdt_startup(void)
static void wdt_turnoff(void)
{
/* Stop the timer */
del_timer(&timer);
del_timer_sync(&timer);
inb_p(wdt_stop);
pr_info("Watchdog timer is now disabled...\n");
}
......
......@@ -73,16 +73,21 @@
#define SBSA_GWDT_WCS_WS0 BIT(1)
#define SBSA_GWDT_WCS_WS1 BIT(2)
#define SBSA_GWDT_VERSION_MASK 0xF
#define SBSA_GWDT_VERSION_SHIFT 16
/**
* struct sbsa_gwdt - Internal representation of the SBSA GWDT
* @wdd: kernel watchdog_device structure
* @clk: store the System Counter clock frequency, in Hz.
* @version: store the architecture version
* @refresh_base: Virtual address of the watchdog refresh frame
* @control_base: Virtual address of the watchdog control frame
*/
struct sbsa_gwdt {
struct watchdog_device wdd;
u32 clk;
int version;
void __iomem *refresh_base;
void __iomem *control_base;
};
......@@ -112,6 +117,30 @@ MODULE_PARM_DESC(nowayout,
"Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
/*
* Arm Base System Architecture 1.0 introduces watchdog v1 which
* increases the length watchdog offset register to 48 bits.
* - For version 0: WOR is 32 bits;
* - For version 1: WOR is 48 bits which comprises the register
* offset 0x8 and 0xC, and the bits [63:48] are reserved which are
* Read-As-Zero and Writes-Ignored.
*/
static u64 sbsa_gwdt_reg_read(struct sbsa_gwdt *gwdt)
{
if (gwdt->version == 0)
return readl(gwdt->control_base + SBSA_GWDT_WOR);
else
return readq(gwdt->control_base + SBSA_GWDT_WOR);
}
static void sbsa_gwdt_reg_write(u64 val, struct sbsa_gwdt *gwdt)
{
if (gwdt->version == 0)
writel((u32)val, gwdt->control_base + SBSA_GWDT_WOR);
else
writeq(val, gwdt->control_base + SBSA_GWDT_WOR);
}
/*
* watchdog operation functions
*/
......@@ -123,16 +152,14 @@ static int sbsa_gwdt_set_timeout(struct watchdog_device *wdd,
wdd->timeout = timeout;
if (action)
writel(gwdt->clk * timeout,
gwdt->control_base + SBSA_GWDT_WOR);
sbsa_gwdt_reg_write(gwdt->clk * timeout, gwdt);
else
/*
* In the single stage mode, The first signal (WS0) is ignored,
* the timeout is (WOR * 2), so the WOR should be configured
* to half value of timeout.
*/
writel(gwdt->clk / 2 * timeout,
gwdt->control_base + SBSA_GWDT_WOR);
sbsa_gwdt_reg_write(gwdt->clk / 2 * timeout, gwdt);
return 0;
}
......@@ -149,7 +176,7 @@ static unsigned int sbsa_gwdt_get_timeleft(struct watchdog_device *wdd)
*/
if (!action &&
!(readl(gwdt->control_base + SBSA_GWDT_WCS) & SBSA_GWDT_WCS_WS0))
timeleft += readl(gwdt->control_base + SBSA_GWDT_WOR);
timeleft += sbsa_gwdt_reg_read(gwdt);
timeleft += lo_hi_readq(gwdt->control_base + SBSA_GWDT_WCV) -
arch_timer_read_counter();
......@@ -172,6 +199,17 @@ static int sbsa_gwdt_keepalive(struct watchdog_device *wdd)
return 0;
}
static void sbsa_gwdt_get_version(struct watchdog_device *wdd)
{
struct sbsa_gwdt *gwdt = watchdog_get_drvdata(wdd);
int ver;
ver = readl(gwdt->control_base + SBSA_GWDT_W_IIDR);
ver = (ver >> SBSA_GWDT_VERSION_SHIFT) & SBSA_GWDT_VERSION_MASK;
gwdt->version = ver;
}
static int sbsa_gwdt_start(struct watchdog_device *wdd)
{
struct sbsa_gwdt *gwdt = watchdog_get_drvdata(wdd);
......@@ -252,10 +290,14 @@ static int sbsa_gwdt_probe(struct platform_device *pdev)
wdd->info = &sbsa_gwdt_info;
wdd->ops = &sbsa_gwdt_ops;
wdd->min_timeout = 1;
wdd->max_hw_heartbeat_ms = U32_MAX / gwdt->clk * 1000;
wdd->timeout = DEFAULT_TIMEOUT;
watchdog_set_drvdata(wdd, gwdt);
watchdog_set_nowayout(wdd, nowayout);
sbsa_gwdt_get_version(wdd);
if (gwdt->version == 0)
wdd->max_hw_heartbeat_ms = U32_MAX / gwdt->clk * 1000;
else
wdd->max_hw_heartbeat_ms = GENMASK_ULL(47, 0) / gwdt->clk * 1000;
status = readl(cf_base + SBSA_GWDT_WCS);
if (status & SBSA_GWDT_WCS_WS1) {
......
......@@ -186,7 +186,7 @@ static int wdt_startup(void)
static int wdt_turnoff(void)
{
/* Stop the timer */
del_timer(&timer);
del_timer_sync(&timer);
/* Stop the watchdog */
wdt_config(0);
......
......@@ -164,7 +164,7 @@ static int sl28cpld_wdt_probe(struct platform_device *pdev)
/*
* Initial timeout value, may be overwritten by device tree or module
* parmeter in watchdog_init_timeout().
* parameter in watchdog_init_timeout().
*
* Reading a zero here means that either the hardware has a default
* value of zero (which is very unlikely and definitely a hardware
......
......@@ -11,7 +11,6 @@
* warranty of any kind, whether express or implied.
*/
#include <linux/acpi.h>
#include <linux/device.h>
#include <linux/resource.h>
#include <linux/amba/bus.h>
......@@ -23,8 +22,8 @@
#include <linux/math64.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/of.h>
#include <linux/pm.h>
#include <linux/property.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/types.h>
......@@ -58,7 +57,8 @@
* @wdd: instance of struct watchdog_device
* @lock: spin lock protecting dev structure and io access
* @base: base address of wdt
* @clk: clock structure of wdt
* @clk: (optional) clock structure of wdt
* @rate: (optional) clock rate when provided via properties
* @adev: amba device structure of wdt
* @status: current status of wdt
* @load_val: load value to be set for current timeout
......@@ -231,6 +231,7 @@ static int
sp805_wdt_probe(struct amba_device *adev, const struct amba_id *id)
{
struct sp805_wdt *wdt;
u64 rate = 0;
int ret = 0;
wdt = devm_kzalloc(&adev->dev, sizeof(*wdt), GFP_KERNEL);
......@@ -243,25 +244,23 @@ sp805_wdt_probe(struct amba_device *adev, const struct amba_id *id)
if (IS_ERR(wdt->base))
return PTR_ERR(wdt->base);
if (adev->dev.of_node) {
wdt->clk = devm_clk_get(&adev->dev, NULL);
if (IS_ERR(wdt->clk)) {
dev_err(&adev->dev, "Clock not found\n");
return PTR_ERR(wdt->clk);
}
wdt->rate = clk_get_rate(wdt->clk);
} else if (has_acpi_companion(&adev->dev)) {
/*
* When Driver probe with ACPI device, clock devices
* are not available, so watchdog rate get from
* clock-frequency property given in _DSD object.
*/
device_property_read_u64(&adev->dev, "clock-frequency",
&wdt->rate);
if (!wdt->rate) {
dev_err(&adev->dev, "no clock-frequency property\n");
return -ENODEV;
}
/*
* When driver probe with ACPI device, clock devices
* are not available, so watchdog rate get from
* clock-frequency property given in _DSD object.
*/
device_property_read_u64(&adev->dev, "clock-frequency", &rate);
wdt->clk = devm_clk_get_optional(&adev->dev, NULL);
if (IS_ERR(wdt->clk))
return dev_err_probe(&adev->dev, PTR_ERR(wdt->clk), "Clock not found\n");
wdt->rate = clk_get_rate(wdt->clk);
if (!wdt->rate)
wdt->rate = rate;
if (!wdt->rate) {
dev_err(&adev->dev, "no clock-frequency property\n");
return -ENODEV;
}
wdt->adev = adev;
......
......@@ -166,7 +166,7 @@ static void wdt_startup(void)
static void wdt_turnoff(void)
{
/* Stop the timer */
del_timer(&timer);
del_timer_sync(&timer);
wdt_change(WDT_DISABLE);
......
......@@ -7,6 +7,8 @@
*
* (c) Copyright 2008-2011 Wim Van Sebroeck <wim@iguana.be>.
*
* (c) Copyright 2021 Hewlett Packard Enterprise Development LP.
*
* This source code is part of the generic code that can be used
* by all the watchdog timer drivers.
*
......@@ -22,8 +24,38 @@
* This material is provided "AS-IS" and at no charge.
*/
#include <linux/hrtimer.h>
#include <linux/kthread.h>
#define MAX_DOGS 32 /* Maximum number of watchdog devices */
/*
* struct watchdog_core_data - watchdog core internal data
* @dev: The watchdog's internal device
* @cdev: The watchdog's Character device.
* @wdd: Pointer to watchdog device.
* @lock: Lock for watchdog core.
* @status: Watchdog core internal status bits.
*/
struct watchdog_core_data {
struct device dev;
struct cdev cdev;
struct watchdog_device *wdd;
struct mutex lock;
ktime_t last_keepalive;
ktime_t last_hw_keepalive;
ktime_t open_deadline;
struct hrtimer timer;
struct kthread_work work;
#if IS_ENABLED(CONFIG_WATCHDOG_HRTIMER_PRETIMEOUT)
struct hrtimer pretimeout_timer;
#endif
unsigned long status; /* Internal status bits */
#define _WDOG_DEV_OPEN 0 /* Opened ? */
#define _WDOG_ALLOW_RELEASE 1 /* Did we receive the magic char ? */
#define _WDOG_KEEPALIVE 2 /* Did we receive a keepalive ? */
};
/*
* Functions/procedures to be called by the core
*/
......@@ -31,3 +63,19 @@ extern int watchdog_dev_register(struct watchdog_device *);
extern void watchdog_dev_unregister(struct watchdog_device *);
extern int __init watchdog_dev_init(void);
extern void __exit watchdog_dev_exit(void);
static inline bool watchdog_have_pretimeout(struct watchdog_device *wdd)
{
return wdd->info->options & WDIOF_PRETIMEOUT ||
IS_ENABLED(CONFIG_WATCHDOG_HRTIMER_PRETIMEOUT);
}
#if IS_ENABLED(CONFIG_WATCHDOG_HRTIMER_PRETIMEOUT)
void watchdog_hrtimer_pretimeout_init(struct watchdog_device *wdd);
void watchdog_hrtimer_pretimeout_start(struct watchdog_device *wdd);
void watchdog_hrtimer_pretimeout_stop(struct watchdog_device *wdd);
#else
static inline void watchdog_hrtimer_pretimeout_init(struct watchdog_device *wdd) {}
static inline void watchdog_hrtimer_pretimeout_start(struct watchdog_device *wdd) {}
static inline void watchdog_hrtimer_pretimeout_stop(struct watchdog_device *wdd) {}
#endif
......@@ -7,6 +7,7 @@
*
* (c) Copyright 2008-2011 Wim Van Sebroeck <wim@iguana.be>.
*
* (c) Copyright 2021 Hewlett Packard Enterprise Development LP.
*
* This source code is part of the generic code that can be used
* by all the watchdog timer drivers.
......@@ -46,30 +47,6 @@
#include "watchdog_core.h"
#include "watchdog_pretimeout.h"
/*
* struct watchdog_core_data - watchdog core internal data
* @dev: The watchdog's internal device
* @cdev: The watchdog's Character device.
* @wdd: Pointer to watchdog device.
* @lock: Lock for watchdog core.
* @status: Watchdog core internal status bits.
*/
struct watchdog_core_data {
struct device dev;
struct cdev cdev;
struct watchdog_device *wdd;
struct mutex lock;
ktime_t last_keepalive;
ktime_t last_hw_keepalive;
ktime_t open_deadline;
struct hrtimer timer;
struct kthread_work work;
unsigned long status; /* Internal status bits */
#define _WDOG_DEV_OPEN 0 /* Opened ? */
#define _WDOG_ALLOW_RELEASE 1 /* Did we receive the magic char ? */
#define _WDOG_KEEPALIVE 2 /* Did we receive a keepalive ? */
};
/* the dev_t structure to store the dynamically allocated watchdog devices */
static dev_t watchdog_devt;
/* Reference to watchdog device behind /dev/watchdog */
......@@ -185,6 +162,9 @@ static int __watchdog_ping(struct watchdog_device *wdd)
else
err = wdd->ops->start(wdd); /* restart watchdog */
if (err == 0)
watchdog_hrtimer_pretimeout_start(wdd);
watchdog_update_worker(wdd);
return err;
......@@ -275,8 +255,10 @@ static int watchdog_start(struct watchdog_device *wdd)
started_at = ktime_get();
if (watchdog_hw_running(wdd) && wdd->ops->ping) {
err = __watchdog_ping(wdd);
if (err == 0)
if (err == 0) {
set_bit(WDOG_ACTIVE, &wdd->status);
watchdog_hrtimer_pretimeout_start(wdd);
}
} else {
err = wdd->ops->start(wdd);
if (err == 0) {
......@@ -284,6 +266,7 @@ static int watchdog_start(struct watchdog_device *wdd)
wd_data->last_keepalive = started_at;
wd_data->last_hw_keepalive = started_at;
watchdog_update_worker(wdd);
watchdog_hrtimer_pretimeout_start(wdd);
}
}
......@@ -325,6 +308,7 @@ static int watchdog_stop(struct watchdog_device *wdd)
if (err == 0) {
clear_bit(WDOG_ACTIVE, &wdd->status);
watchdog_update_worker(wdd);
watchdog_hrtimer_pretimeout_stop(wdd);
}
return err;
......@@ -361,6 +345,9 @@ static unsigned int watchdog_get_status(struct watchdog_device *wdd)
if (test_and_clear_bit(_WDOG_KEEPALIVE, &wd_data->status))
status |= WDIOF_KEEPALIVEPING;
if (IS_ENABLED(CONFIG_WATCHDOG_HRTIMER_PRETIMEOUT))
status |= WDIOF_PRETIMEOUT;
return status;
}
......@@ -408,7 +395,7 @@ static int watchdog_set_pretimeout(struct watchdog_device *wdd,
{
int err = 0;
if (!(wdd->info->options & WDIOF_PRETIMEOUT))
if (!watchdog_have_pretimeout(wdd))
return -EOPNOTSUPP;
if (watchdog_pretimeout_invalid(wdd, timeout))
......@@ -451,7 +438,8 @@ static ssize_t nowayout_show(struct device *dev, struct device_attribute *attr,
{
struct watchdog_device *wdd = dev_get_drvdata(dev);
return sprintf(buf, "%d\n", !!test_bit(WDOG_NO_WAY_OUT, &wdd->status));
return sysfs_emit(buf, "%d\n", !!test_bit(WDOG_NO_WAY_OUT,
&wdd->status));
}
static ssize_t nowayout_store(struct device *dev, struct device_attribute *attr,
......@@ -485,7 +473,7 @@ static ssize_t status_show(struct device *dev, struct device_attribute *attr,
status = watchdog_get_status(wdd);
mutex_unlock(&wd_data->lock);
return sprintf(buf, "0x%x\n", status);
return sysfs_emit(buf, "0x%x\n", status);
}
static DEVICE_ATTR_RO(status);
......@@ -494,7 +482,7 @@ static ssize_t bootstatus_show(struct device *dev,
{
struct watchdog_device *wdd = dev_get_drvdata(dev);
return sprintf(buf, "%u\n", wdd->bootstatus);
return sysfs_emit(buf, "%u\n", wdd->bootstatus);
}
static DEVICE_ATTR_RO(bootstatus);
......@@ -510,7 +498,7 @@ static ssize_t timeleft_show(struct device *dev, struct device_attribute *attr,
status = watchdog_get_timeleft(wdd, &val);
mutex_unlock(&wd_data->lock);
if (!status)
status = sprintf(buf, "%u\n", val);
status = sysfs_emit(buf, "%u\n", val);
return status;
}
......@@ -521,16 +509,34 @@ static ssize_t timeout_show(struct device *dev, struct device_attribute *attr,
{
struct watchdog_device *wdd = dev_get_drvdata(dev);
return sprintf(buf, "%u\n", wdd->timeout);
return sysfs_emit(buf, "%u\n", wdd->timeout);
}
static DEVICE_ATTR_RO(timeout);
static ssize_t min_timeout_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct watchdog_device *wdd = dev_get_drvdata(dev);
return sysfs_emit(buf, "%u\n", wdd->min_timeout);
}
static DEVICE_ATTR_RO(min_timeout);
static ssize_t max_timeout_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct watchdog_device *wdd = dev_get_drvdata(dev);
return sysfs_emit(buf, "%u\n", wdd->max_timeout);
}
static DEVICE_ATTR_RO(max_timeout);
static ssize_t pretimeout_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct watchdog_device *wdd = dev_get_drvdata(dev);
return sprintf(buf, "%u\n", wdd->pretimeout);
return sysfs_emit(buf, "%u\n", wdd->pretimeout);
}
static DEVICE_ATTR_RO(pretimeout);
......@@ -539,7 +545,7 @@ static ssize_t identity_show(struct device *dev, struct device_attribute *attr,
{
struct watchdog_device *wdd = dev_get_drvdata(dev);
return sprintf(buf, "%s\n", wdd->info->identity);
return sysfs_emit(buf, "%s\n", wdd->info->identity);
}
static DEVICE_ATTR_RO(identity);
......@@ -549,9 +555,9 @@ static ssize_t state_show(struct device *dev, struct device_attribute *attr,
struct watchdog_device *wdd = dev_get_drvdata(dev);
if (watchdog_active(wdd))
return sprintf(buf, "active\n");
return sysfs_emit(buf, "active\n");
return sprintf(buf, "inactive\n");
return sysfs_emit(buf, "inactive\n");
}
static DEVICE_ATTR_RO(state);
......@@ -594,13 +600,11 @@ static umode_t wdt_is_visible(struct kobject *kobj, struct attribute *attr,
if (attr == &dev_attr_timeleft.attr && !wdd->ops->get_timeleft)
mode = 0;
else if (attr == &dev_attr_pretimeout.attr &&
!(wdd->info->options & WDIOF_PRETIMEOUT))
else if (attr == &dev_attr_pretimeout.attr && !watchdog_have_pretimeout(wdd))
mode = 0;
else if ((attr == &dev_attr_pretimeout_governor.attr ||
attr == &dev_attr_pretimeout_available_governors.attr) &&
(!(wdd->info->options & WDIOF_PRETIMEOUT) ||
!IS_ENABLED(CONFIG_WATCHDOG_PRETIMEOUT_GOV)))
(!watchdog_have_pretimeout(wdd) || !IS_ENABLED(CONFIG_WATCHDOG_PRETIMEOUT_GOV)))
mode = 0;
return mode;
......@@ -609,6 +613,8 @@ static struct attribute *wdt_attrs[] = {
&dev_attr_state.attr,
&dev_attr_identity.attr,
&dev_attr_timeout.attr,
&dev_attr_min_timeout.attr,
&dev_attr_max_timeout.attr,
&dev_attr_pretimeout.attr,
&dev_attr_timeleft.attr,
&dev_attr_bootstatus.attr,
......@@ -1009,6 +1015,7 @@ static int watchdog_cdev_register(struct watchdog_device *wdd)
kthread_init_work(&wd_data->work, watchdog_ping_work);
hrtimer_init(&wd_data->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_HARD);
wd_data->timer.function = watchdog_timer_expired;
watchdog_hrtimer_pretimeout_init(wdd);
if (wdd->id == 0) {
old_wd_data = wd_data;
......@@ -1096,6 +1103,7 @@ static void watchdog_cdev_unregister(struct watchdog_device *wdd)
hrtimer_cancel(&wd_data->timer);
kthread_cancel_work_sync(&wd_data->work);
watchdog_hrtimer_pretimeout_stop(wdd);
put_device(&wd_data->dev);
}
......
// SPDX-License-Identifier: GPL-2.0
/*
* (c) Copyright 2021 Hewlett Packard Enterprise Development LP.
*/
#include <linux/hrtimer.h>
#include <linux/watchdog.h>
#include "watchdog_core.h"
#include "watchdog_pretimeout.h"
static enum hrtimer_restart watchdog_hrtimer_pretimeout(struct hrtimer *timer)
{
struct watchdog_core_data *wd_data;
wd_data = container_of(timer, struct watchdog_core_data, pretimeout_timer);
watchdog_notify_pretimeout(wd_data->wdd);
return HRTIMER_NORESTART;
}
void watchdog_hrtimer_pretimeout_init(struct watchdog_device *wdd)
{
struct watchdog_core_data *wd_data = wdd->wd_data;
hrtimer_init(&wd_data->pretimeout_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
wd_data->pretimeout_timer.function = watchdog_hrtimer_pretimeout;
}
void watchdog_hrtimer_pretimeout_start(struct watchdog_device *wdd)
{
if (!(wdd->info->options & WDIOF_PRETIMEOUT) &&
!watchdog_pretimeout_invalid(wdd, wdd->pretimeout))
hrtimer_start(&wdd->wd_data->pretimeout_timer,
ktime_set(wdd->timeout - wdd->pretimeout, 0),
HRTIMER_MODE_REL);
else
hrtimer_cancel(&wdd->wd_data->pretimeout_timer);
}
void watchdog_hrtimer_pretimeout_stop(struct watchdog_device *wdd)
{
hrtimer_cancel(&wdd->wd_data->pretimeout_timer);
}
......@@ -9,6 +9,7 @@
#include <linux/string.h>
#include <linux/watchdog.h>
#include "watchdog_core.h"
#include "watchdog_pretimeout.h"
/* Default watchdog pretimeout governor */
......@@ -55,7 +56,7 @@ int watchdog_pretimeout_available_governors_get(char *buf)
mutex_lock(&governor_lock);
list_for_each_entry(priv, &governor_list, entry)
count += sprintf(buf + count, "%s\n", priv->gov->name);
count += sysfs_emit_at(buf, count, "%s\n", priv->gov->name);
mutex_unlock(&governor_lock);
......@@ -68,7 +69,7 @@ int watchdog_pretimeout_governor_get(struct watchdog_device *wdd, char *buf)
spin_lock_irq(&pretimeout_lock);
if (wdd->gov)
count = sprintf(buf, "%s\n", wdd->gov->name);
count = sysfs_emit(buf, "%s\n", wdd->gov->name);
spin_unlock_irq(&pretimeout_lock);
return count;
......@@ -177,7 +178,7 @@ int watchdog_register_pretimeout(struct watchdog_device *wdd)
{
struct watchdog_pretimeout *p;
if (!(wdd->info->options & WDIOF_PRETIMEOUT))
if (!watchdog_have_pretimeout(wdd))
return 0;
p = kzalloc(sizeof(*p), GFP_KERNEL);
......@@ -197,7 +198,7 @@ void watchdog_unregister_pretimeout(struct watchdog_device *wdd)
{
struct watchdog_pretimeout *p, *t;
if (!(wdd->info->options & WDIOF_PRETIMEOUT))
if (!watchdog_have_pretimeout(wdd))
return;
spin_lock_irq(&pretimeout_lock);
......
......@@ -208,7 +208,7 @@ static int wdat_wdt_enable_reboot(struct wdat_wdt *wdat)
/*
* WDAT specification says that the watchdog is required to reboot
* the system when it fires. However, it also states that it is
* recommeded to make it configurable through hardware register. We
* recommended to make it configurable through hardware register. We
* enable reboot now if it is configurable, just in case.
*/
ret = wdat_wdt_run_action(wdat, ACPI_WDAT_SET_REBOOT, 0, NULL);
......@@ -475,7 +475,7 @@ static int wdat_wdt_suspend_noirq(struct device *dev)
return 0;
/*
* We need to stop the watchdog if firmare is not doing it or if we
* We need to stop the watchdog if firmware is not doing it or if we
* are going suspend to idle (where firmware is not involved). If
* firmware is stopping the watchdog we kick it here one more time
* to give it some time.
......
......@@ -494,7 +494,7 @@ static int wdt_temp_release(struct inode *inode, struct file *file)
}
/**
* notify_sys:
* wdt_notify_sys:
* @this: our notifier block
* @code: the event being reported
* @unused: unused
......@@ -558,7 +558,7 @@ static struct notifier_block wdt_notifier = {
};
/**
* cleanup_module:
* wdt_exit:
*
* Unload the watchdog. You cannot do this with any file handles open.
* If your watchdog is set to continue ticking on close and you unload
......
......@@ -537,7 +537,7 @@ static int wdtpci_temp_release(struct inode *inode, struct file *file)
}
/**
* notify_sys:
* wdtpci_notify_sys:
* @this: our notifier block
* @code: the event being reported
* @unused: unused
......
......@@ -69,9 +69,6 @@ static char *ziirave_reasons[] = {"power cycle", "hw watchdog", NULL, NULL,
#define ZIIRAVE_CMD_JUMP_TO_BOOTLOADER_MAGIC 1
#define ZIIRAVE_CMD_RESET_PROCESSOR_MAGIC 1
#define ZIIRAVE_FW_VERSION_FMT "02.%02u.%02u"
#define ZIIRAVE_BL_VERSION_FMT "01.%02u.%02u"
struct ziirave_wdt_rev {
unsigned char major;
unsigned char minor;
......@@ -445,8 +442,9 @@ static ssize_t ziirave_wdt_sysfs_show_firm(struct device *dev,
if (ret)
return ret;
ret = sprintf(buf, ZIIRAVE_FW_VERSION_FMT, w_priv->firmware_rev.major,
w_priv->firmware_rev.minor);
ret = sysfs_emit(buf, "02.%02u.%02u\n",
w_priv->firmware_rev.major,
w_priv->firmware_rev.minor);
mutex_unlock(&w_priv->sysfs_mutex);
......@@ -468,8 +466,9 @@ static ssize_t ziirave_wdt_sysfs_show_boot(struct device *dev,
if (ret)
return ret;
ret = sprintf(buf, ZIIRAVE_BL_VERSION_FMT, w_priv->bootloader_rev.major,
w_priv->bootloader_rev.minor);
ret = sysfs_emit(buf, "01.%02u.%02u\n",
w_priv->bootloader_rev.major,
w_priv->bootloader_rev.minor);
mutex_unlock(&w_priv->sysfs_mutex);
......@@ -491,7 +490,7 @@ static ssize_t ziirave_wdt_sysfs_show_reason(struct device *dev,
if (ret)
return ret;
ret = sprintf(buf, "%s", ziirave_reasons[w_priv->reset_reason]);
ret = sysfs_emit(buf, "%s\n", ziirave_reasons[w_priv->reset_reason]);
mutex_unlock(&w_priv->sysfs_mutex);
......@@ -536,7 +535,7 @@ static ssize_t ziirave_wdt_sysfs_store_firm(struct device *dev,
}
dev_info(&client->dev,
"Firmware updated to version " ZIIRAVE_FW_VERSION_FMT "\n",
"Firmware updated to version 02.%02u.%02u\n",
w_priv->firmware_rev.major, w_priv->firmware_rev.minor);
/* Restore the watchdog timeout */
......@@ -677,7 +676,7 @@ static int ziirave_wdt_probe(struct i2c_client *client,
}
dev_info(&client->dev,
"Firmware version: " ZIIRAVE_FW_VERSION_FMT "\n",
"Firmware version: 02.%02u.%02u\n",
w_priv->firmware_rev.major, w_priv->firmware_rev.minor);
ret = ziirave_wdt_revision(client, &w_priv->bootloader_rev,
......@@ -688,7 +687,7 @@ static int ziirave_wdt_probe(struct i2c_client *client,
}
dev_info(&client->dev,
"Bootloader version: " ZIIRAVE_BL_VERSION_FMT "\n",
"Bootloader version: 01.%02u.%02u\n",
w_priv->bootloader_rev.major, w_priv->bootloader_rev.minor);
w_priv->reset_reason = i2c_smbus_read_byte_data(client,
......
......@@ -918,12 +918,4 @@
extern void mv64340_irq_init(unsigned int base);
/* Watchdog Platform Device, Driver Data */
#define MV64x60_WDT_NAME "mv64x60_wdt"
struct mv64x60_wdt_pdata {
int timeout; /* watchdog expiry in seconds, default 10 */
int bus_clk; /* bus clock in MHz, default 133 */
};
#endif /* __ASM_MV643XX_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment