mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-23 20:24:12 +08:00
linux-watchdog 5.14-rc1 tag
-----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.14 (GNU/Linux) iEYEABECAAYFAmDltyYACgkQ+iyteGJfRsqsYgCffWETSHEY5Ydo1ocA74lKLYa6 zuQAnjvBNRiuDXazyF1c3FTydg1A6BUy =AF+Z -----END PGP SIGNATURE----- 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 ...
This commit is contained in:
commit
9d69294be2
@ -0,0 +1,74 @@
|
||||
# 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;
|
||||
};
|
||||
|
||||
...
|
@ -1,34 +0,0 @@
|
||||
* 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;
|
||||
};
|
@ -0,0 +1,40 @@
|
||||
# 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>;
|
||||
};
|
@ -1,5 +1,8 @@
|
||||
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 @@ out:
|
||||
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;
|
||||
|
166
drivers/watchdog/msc313e_wdt.c
Normal file
166
drivers/watchdog/msc313e_wdt.c
Normal file
@ -0,0 +1,166 @@
|
||||
// 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;
|
||||
|
@ -1,324 +0,0 @@
|
||||
// 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 @@ err:
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
44
drivers/watchdog/watchdog_hrtimer_pretimeout.c
Normal file
44
drivers/watchdog/watchdog_hrtimer_pretimeout.c
Normal file
@ -0,0 +1,44 @@
|
||||
// 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 */
|
||||
|
Loading…
Reference in New Issue
Block a user