mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-07-22 03:27:30 -04:00
Merge tag 'watchdog-for-v7.2' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging
Pull watchdog updates and fixes from Guenter Roeck:
"Subsystem:
- Unregister PM notifier on watchdog unregister
- Various documentation fixes and improvements
Removed drivers:
- Remove AMD Elan SC520 processor watchdog driver
- Drop SMARC-sAM67 support
- Remove driver for integrated WDT of ZFx86 486-based SoC
New drivers:
- Driver for Andes ATCWDT200
- Driver for Gunyah Watchdog
Added support to existing drivers:
- Add "apple,t8103-wdt" and "apple,t8122-wdt" compatibles to Apple
watchdog driver
- Add rockchip,rk3528-wdt and rockchip,rv1103b-wdt to snps,dw-wdt.yaml
- Document IPQ9650, IPQ5210, Shikra, Nord, and Hawi in qcom-wdt.yaml
Also document sram property and add support to get the bootstatus
to qcom wdt driver
- lenovo_se10_wdt: Fix use-after-rfree and add support for SE10 Gen 2
platform
- ti,rti-wdt: Add ti,am62l-rti-wdt compatible
- renesas: Document RZ/G3L support and rework example for
renesas,r9a09g057-wdt
Other bug fixes and improvements:
- Use named initializers (sc1200, ziirave_wdt)
- Allow pic32-dmt and pic32-wdt to be built with COMPILE_TEST
- realtek-otto: enable clock before using I/O, and prevent PHASE2 underflows
- rti_wdt: Add reaction control
- renesas,rzn1-wdt: Drop interrupt support and other cleanup
- gpio_wdt: Add ACPI support
- imx7ulp_wdt: Keep WDOG running until A55 enters WFI on i.MX94
- sprd_wdt: Remove redundant sprd_wdt_disable() on register failure
- bcm2835_wdt: Switch to new sys-off handler API
- sama5d4_wdt: Fix WDDIS detection on SAM9X60 and SAMA7G5
- hpwdt: Refine hpwdt message for UV platform
- Convert TS-4800 bindings to DT schema
- menz069_wdt: drop unneeded MODULE_ALIAS
- sp5100_tco: Use EFCH MMIO for newer Hygon FCH"
* tag 'watchdog-for-v7.2' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging: (58 commits)
watchdog: sc1200: Drop unused assignment of pnp_device_id driver data
watchdog: unregister PM notifier on watchdog unregister
dt-bindings: watchdog: qcom-wdt: Document IPQ5210 watchdog
watchdog: dev: convert to kernel-doc comments
watchdog: core: clean up some comments
watchdog: uapi: add comments for what bit masks apply to
watchdog: linux/watchdog.h: repair kernel-doc comments
watchdog: add devm_watchdog_register_device() to watchdog-kernel-api
watchdog: ziirave_wdt: Use named initializers for struct i2c_device_id
watchdog: realtek-otto: enable clock before using I/O
watchdog: realtek-otto: prevent PHASE2 underflows
dt-bindings: watchdog: qcom-wdt: Document IPQ9650 watchdog
dt-bindings: watchdog: renesas,rzn1-wdt: interrupts are not required
dt-bindings: watchdog: apple,wdt: Add t8122 compatible
watchdog: apple: Add "apple,t8103-wdt" compatible
watchdog: rzn1: remove now obsolete interrupt support
dt-bindings: watchdog: Add watchdog compatible for RK3528
watchdog: convert the Kconfig dependency on OF_GPIO to OF
watchdog: Remove AMD Elan SC520 processor watchdog driver
watchdog: lenovo_se10_wdt: Fix use-after-free and resource leak risk
...
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/watchdog/andestech,ae350-wdt.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Andes ATCWDT200 Watchdog Timer
|
||||
|
||||
maintainers:
|
||||
- CL Wang <cl634@andestech.com>
|
||||
|
||||
allOf:
|
||||
- $ref: watchdog.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- items:
|
||||
- enum:
|
||||
- andestech,qilai-wdt
|
||||
- const: andestech,ae350-wdt
|
||||
- const: andestech,ae350-wdt
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
andestech,clock-source:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
enum: [0, 1]
|
||||
description: |
|
||||
Select the clock source for the watchdog timer.
|
||||
0 - External clock
|
||||
1 - P clock
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- clocks
|
||||
- andestech,clock-source
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
watchdog@f0500000 {
|
||||
compatible = "andestech,ae350-wdt";
|
||||
reg = <0xf0500000 0x20>;
|
||||
clocks = <&clk_wdt>;
|
||||
andestech,clock-source = <0>;
|
||||
};
|
||||
@@ -16,7 +16,9 @@ properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- items:
|
||||
- const: apple,t6020-wdt
|
||||
- enum:
|
||||
- apple,t6020-wdt
|
||||
- apple,t8122-wdt
|
||||
- const: apple,t8103-wdt
|
||||
- items:
|
||||
- enum:
|
||||
|
||||
@@ -18,12 +18,7 @@ allOf:
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- items:
|
||||
- enum:
|
||||
- kontron,sa67mcu-wdt
|
||||
- const: kontron,sl28cpld-wdt
|
||||
- const: kontron,sl28cpld-wdt
|
||||
const: kontron,sl28cpld-wdt
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
@@ -18,15 +18,19 @@ properties:
|
||||
- items:
|
||||
- enum:
|
||||
- qcom,apss-wdt-glymur
|
||||
- qcom,apss-wdt-hawi
|
||||
- qcom,kpss-wdt-ipq4019
|
||||
- qcom,apss-wdt-ipq5018
|
||||
- qcom,apss-wdt-ipq5210
|
||||
- qcom,apss-wdt-ipq5332
|
||||
- qcom,apss-wdt-ipq5424
|
||||
- qcom,apss-wdt-ipq9574
|
||||
- qcom,apss-wdt-ipq9650
|
||||
- qcom,apss-wdt-kaanapali
|
||||
- qcom,apss-wdt-msm8226
|
||||
- qcom,apss-wdt-msm8974
|
||||
- qcom,apss-wdt-msm8994
|
||||
- qcom,apss-wdt-nord
|
||||
- qcom,apss-wdt-qcm2290
|
||||
- qcom,apss-wdt-qcs404
|
||||
- qcom,apss-wdt-qcs615
|
||||
@@ -40,6 +44,7 @@ properties:
|
||||
- qcom,apss-wdt-sdm845
|
||||
- qcom,apss-wdt-sdx55
|
||||
- qcom,apss-wdt-sdx65
|
||||
- qcom,apss-wdt-shikra
|
||||
- qcom,apss-wdt-sm6115
|
||||
- qcom,apss-wdt-sm6350
|
||||
- qcom,apss-wdt-sm8150
|
||||
@@ -84,6 +89,12 @@ properties:
|
||||
minItems: 1
|
||||
maxItems: 5
|
||||
|
||||
sram:
|
||||
maxItems: 1
|
||||
description:
|
||||
A reference to an region residing in IMEM(on-chip SRAM), which contains
|
||||
the system restart reason value populated by the bootloader.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
@@ -89,11 +89,11 @@ examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/renesas,r9a09g057-cpg.h>
|
||||
|
||||
watchdog@11c00400 {
|
||||
watchdog@14400000 {
|
||||
compatible = "renesas,r9a09g057-wdt";
|
||||
reg = <0x11c00400 0x400>;
|
||||
clocks = <&cpg CPG_MOD 0x4b>, <&cpg CPG_MOD 0x4c>;
|
||||
reg = <0x14400000 0x400>;
|
||||
clocks = <&cpg CPG_MOD 0x4d>, <&cpg CPG_MOD 0x4e>;
|
||||
clock-names = "pclk", "oscclk";
|
||||
resets = <&cpg 0x75>;
|
||||
resets = <&cpg 0x76>;
|
||||
power-domains = <&cpg>;
|
||||
};
|
||||
|
||||
@@ -18,6 +18,7 @@ properties:
|
||||
- renesas,r9a07g044-wdt # RZ/G2{L,LC}
|
||||
- renesas,r9a07g054-wdt # RZ/V2L
|
||||
- renesas,r9a08g045-wdt # RZ/G3S
|
||||
- renesas,r9a08g046-wdt # RZ/G3L
|
||||
- const: renesas,rzg2l-wdt
|
||||
|
||||
- items:
|
||||
|
||||
@@ -29,7 +29,6 @@ properties:
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
|
||||
allOf:
|
||||
|
||||
@@ -29,10 +29,12 @@ properties:
|
||||
- rockchip,rk3368-wdt
|
||||
- rockchip,rk3399-wdt
|
||||
- rockchip,rk3506-wdt
|
||||
- rockchip,rk3528-wdt
|
||||
- rockchip,rk3562-wdt
|
||||
- rockchip,rk3568-wdt
|
||||
- rockchip,rk3576-wdt
|
||||
- rockchip,rk3588-wdt
|
||||
- rockchip,rv1103b-wdt
|
||||
- rockchip,rv1108-wdt
|
||||
- rockchip,rv1126-wdt
|
||||
- const: snps,dw-wdt
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/watchdog/technologic,ts4800-wdt.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Technologic Systems TS-4800 Watchdog
|
||||
|
||||
maintainers:
|
||||
- Eduard Bostina <egbostina@gmail.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: technologic,ts4800-wdt
|
||||
|
||||
syscon:
|
||||
$ref: /schemas/types.yaml#/definitions/phandle-array
|
||||
items:
|
||||
- items:
|
||||
- description: Phandle to the FPGA's syscon
|
||||
- description: Offset to the watchdog register
|
||||
description: Phandle / integers array that points to the syscon node which
|
||||
describes the FPGA's syscon registers.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- syscon
|
||||
|
||||
allOf:
|
||||
- $ref: watchdog.yaml#
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
watchdog {
|
||||
compatible = "technologic,ts4800-wdt";
|
||||
syscon = <&syscon 0xe>;
|
||||
timeout-sec = <10>;
|
||||
};
|
||||
@@ -23,6 +23,7 @@ allOf:
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- ti,am62l-rti-wdt
|
||||
- ti,j7-rti-wdt
|
||||
|
||||
reg:
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
Technologic Systems Watchdog
|
||||
|
||||
Required properties:
|
||||
- compatible: must be "technologic,ts4800-wdt"
|
||||
- syscon: phandle / integer array that points to the syscon node which
|
||||
describes the FPGA's syscon registers.
|
||||
- phandle to FPGA's syscon
|
||||
- offset to the watchdog register
|
||||
|
||||
Optional property:
|
||||
- timeout-sec: contains the watchdog timeout in seconds.
|
||||
|
||||
Example:
|
||||
|
||||
syscon: syscon@b0010000 {
|
||||
compatible = "syscon", "simple-mfd";
|
||||
reg = <0xb0010000 0x3d>;
|
||||
reg-io-width = <2>;
|
||||
|
||||
wdt@e {
|
||||
compatible = "technologic,ts4800-wdt";
|
||||
syscon = <&syscon 0xe>;
|
||||
timeout-sec = <10>;
|
||||
};
|
||||
}
|
||||
@@ -11,12 +11,12 @@ This document shall guide you for this task. The necessary steps are described
|
||||
as well as things to look out for.
|
||||
|
||||
|
||||
Remove the file_operations struct
|
||||
Remove the struct file_operations
|
||||
---------------------------------
|
||||
|
||||
Old drivers define their own file_operations for actions like open(), write(),
|
||||
etc... These are now handled by the framework and just call the driver when
|
||||
needed. So, in general, the 'file_operations' struct and assorted functions can
|
||||
needed. So, in general, the struct file_operations and assorted functions can
|
||||
go. Only very few driver-specific details have to be moved to other functions.
|
||||
Here is a overview of the functions and probably needed actions:
|
||||
|
||||
@@ -36,7 +36,7 @@ Here is a overview of the functions and probably needed actions:
|
||||
from the driver:
|
||||
|
||||
WDIOC_GETSUPPORT:
|
||||
Returns the mandatory watchdog_info struct from the driver
|
||||
Returns the mandatory struct watchdog_info from the driver
|
||||
|
||||
WDIOC_GETSTATUS:
|
||||
Needs the status-callback defined, otherwise returns 0
|
||||
@@ -88,8 +88,8 @@ refactoring. The rest can go.
|
||||
Remove the miscdevice
|
||||
---------------------
|
||||
|
||||
Since the file_operations are gone now, you can also remove the 'struct
|
||||
miscdevice'. The framework will create it on watchdog_dev_register() called by
|
||||
Since the file_operations are gone now, you can also remove the struct
|
||||
miscdevice. The framework will create it on watchdog_dev_register() called by
|
||||
watchdog_register_device()::
|
||||
|
||||
-static struct miscdevice s3c2410wdt_miscdev = {
|
||||
@@ -113,10 +113,10 @@ them. Includes can be removed, too. For example::
|
||||
Add the watchdog operations
|
||||
---------------------------
|
||||
|
||||
All possible callbacks are defined in 'struct watchdog_ops'. You can find it
|
||||
explained in 'watchdog-kernel-api.txt' in this directory. start() and
|
||||
All possible callbacks are defined in struct watchdog_ops. You can find it
|
||||
explained in watchdog-kernel-api.rst in this directory. start() and
|
||||
owner must be set, the rest are optional. You will easily find corresponding
|
||||
functions in the old driver. Note that you will now get a pointer to the
|
||||
functions in the old driver. Note that you will now get a pointer to the struct
|
||||
watchdog_device as a parameter to these functions, so you probably have to
|
||||
change the function header. Other changes are most likely not needed, because
|
||||
here simply happens the direct hardware access. If you have device-specific
|
||||
@@ -151,12 +151,12 @@ A typical function-header change looks like::
|
||||
Add the watchdog device
|
||||
-----------------------
|
||||
|
||||
Now we need to create a 'struct watchdog_device' and populate it with the
|
||||
necessary information for the framework. The struct is also explained in detail
|
||||
in 'watchdog-kernel-api.txt' in this directory. We pass it the mandatory
|
||||
watchdog_info struct and the newly created watchdog_ops. Often, old drivers
|
||||
Now we need to create a struct watchdog_device and populate it with the
|
||||
necessary information for the framework. The structure is also explained in detail
|
||||
in watchdog-kernel-api.rst in this directory. We pass it the mandatory
|
||||
struct watchdog_info and the newly created struct watchdog_ops. Often, old drivers
|
||||
have their own record-keeping for things like bootstatus and timeout using
|
||||
static variables. Those have to be converted to use the members in
|
||||
static variables. Those have to be converted to use the members in struct
|
||||
watchdog_device. Note that the timeout values are unsigned int. Some drivers
|
||||
use signed int, so this has to be converted, too.
|
||||
|
||||
@@ -174,7 +174,7 @@ Handle the 'nowayout' feature
|
||||
A few drivers use nowayout statically, i.e. there is no module parameter for it
|
||||
and only CONFIG_WATCHDOG_NOWAYOUT determines if the feature is going to be
|
||||
used. This needs to be converted by initializing the status variable of the
|
||||
watchdog_device like this::
|
||||
struct watchdog_device like this::
|
||||
|
||||
.status = WATCHDOG_NOWAYOUT_INIT_STATUS,
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
============================================================
|
||||
WDT Watchdog Timer Interfaces For The Linux Operating System
|
||||
============================================================
|
||||
.. SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
==========================================
|
||||
Industrial Computer Source Watchdog Driver
|
||||
==========================================
|
||||
|
||||
Last Reviewed: 10/05/2007
|
||||
|
||||
@@ -4,15 +4,25 @@
|
||||
Watchdog Support
|
||||
================
|
||||
|
||||
Kernel infrastructure
|
||||
=====================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
hpwdt
|
||||
mlx-wdt
|
||||
pcwd-watchdog
|
||||
watchdog-api
|
||||
watchdog-kernel-api
|
||||
watchdog-parameters
|
||||
watchdog-pm
|
||||
wdt
|
||||
convert_drivers_to_kernel_api
|
||||
|
||||
Driver specific
|
||||
===============
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
watchdog-parameters
|
||||
hpwdt
|
||||
ics-wdt
|
||||
mlx-wdt
|
||||
pcwd-watchdog
|
||||
|
||||
@@ -158,10 +158,10 @@ available to ask what the device can do::
|
||||
struct watchdog_info ident;
|
||||
ioctl(fd, WDIOC_GETSUPPORT, &ident);
|
||||
|
||||
the fields returned in the ident struct are:
|
||||
the fields returned in the struct watchdog_info are:
|
||||
|
||||
================ =============================================
|
||||
identity a string identifying the watchdog driver
|
||||
identity a string identifying the watchdog driver
|
||||
firmware_version the firmware version of the card if available
|
||||
options a flags describing what the device supports
|
||||
================ =============================================
|
||||
@@ -268,4 +268,13 @@ The following options are available:
|
||||
WDIOS_TEMPPANIC Kernel panic on temperature trip
|
||||
================= ================================
|
||||
|
||||
[FIXME -- better explanations]
|
||||
The WDIOS_DISABLECARD option allows you to stop the watchdog timer via
|
||||
software. Note that this will only work if the "nowayout" module parameter
|
||||
(or CONFIG_WATCHDOG_NOWAYOUT) is not set.
|
||||
|
||||
The WDIOS_ENABLECARD option turns on the watchdog timer.
|
||||
|
||||
The WDIOS_TEMPPANIC option is used by some drivers (like the pcwd driver)
|
||||
to trigger a system halt (typically a kernel panic or power off) when the
|
||||
temperature trip point is reached. This ensures that the system is halted
|
||||
immediately in case of an overheat condition.
|
||||
|
||||
@@ -42,6 +42,14 @@ The watchdog subsystem includes an registration deferral mechanism,
|
||||
which allows you to register an watchdog as early as you wish during
|
||||
the boot process.
|
||||
|
||||
There is also a resource-managed watchdog_register_device(),
|
||||
devm_watchdog_register_device(). If you use this to register a watchdog
|
||||
device, watchdog_unregister_device() is called automatically on driver
|
||||
detach::
|
||||
|
||||
int devm_watchdog_register_device(struct device *dev,
|
||||
struct watchdog_device *wdd);
|
||||
|
||||
The watchdog device structure looks like this::
|
||||
|
||||
struct watchdog_device {
|
||||
@@ -257,7 +265,7 @@ bit-operations. The status bits that are defined are:
|
||||
To set the WDOG_NO_WAY_OUT status bit (before registering your watchdog
|
||||
timer device) you can either:
|
||||
|
||||
* set it statically in your watchdog_device struct with
|
||||
* set it statically in your struct watchdog_device with
|
||||
|
||||
.status = WATCHDOG_NOWAYOUT_INIT_STATUS,
|
||||
|
||||
|
||||
@@ -284,16 +284,6 @@ ixp4xx_wdt:
|
||||
|
||||
-------------------------------------------------
|
||||
|
||||
machzwd:
|
||||
nowayout:
|
||||
Watchdog cannot be stopped once started
|
||||
(default=kernel config parameter)
|
||||
action:
|
||||
after watchdog resets, generate:
|
||||
0 = RESET(*) 1 = SMI 2 = NMI 3 = SCI
|
||||
|
||||
-------------------------------------------------
|
||||
|
||||
max63xx_wdt:
|
||||
heartbeat:
|
||||
Watchdog heartbeat period in seconds from 1 to 60, default 60
|
||||
@@ -532,15 +522,6 @@ sc1200wdt:
|
||||
|
||||
-------------------------------------------------
|
||||
|
||||
sc520_wdt:
|
||||
timeout:
|
||||
Watchdog timeout in seconds. (1 <= timeout <= 3600, default=30)
|
||||
nowayout:
|
||||
Watchdog cannot be stopped once started
|
||||
(default=kernel config parameter)
|
||||
|
||||
-------------------------------------------------
|
||||
|
||||
sch311x_wdt:
|
||||
force_id:
|
||||
Override the detected device ID
|
||||
|
||||
@@ -1881,6 +1881,12 @@ S: Supported
|
||||
F: Documentation/devicetree/bindings/spi/andestech,ae350-spi.yaml
|
||||
F: drivers/spi/spi-atcspi200.c
|
||||
|
||||
ANDES ATCWDT200 WATCHDOG DRIVER
|
||||
M: CL Wang <cl634@andestech.com>
|
||||
S: Supported
|
||||
F: Documentation/devicetree/bindings/watchdog/andestech,ae350-wdt.yaml
|
||||
F: drivers/watchdog/atcwdt200_wdt.c
|
||||
|
||||
ANDROID DRIVERS
|
||||
M: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
||||
M: Arve Hjønnevåg <arve@android.com>
|
||||
@@ -3344,6 +3350,7 @@ F: arch/arm64/boot/dts/qcom/
|
||||
F: drivers/bus/qcom*
|
||||
F: drivers/firmware/qcom/
|
||||
F: drivers/soc/qcom/
|
||||
F: drivers/watchdog/gunyah_wdt.c
|
||||
F: include/dt-bindings/arm/qcom,ids.h
|
||||
F: include/dt-bindings/firmware/qcom,scm.h
|
||||
F: include/dt-bindings/soc/qcom*
|
||||
|
||||
@@ -250,7 +250,7 @@ config DA9062_WATCHDOG
|
||||
|
||||
config GPIO_WATCHDOG
|
||||
tristate "Watchdog device controlled through GPIO-line"
|
||||
depends on OF_GPIO
|
||||
depends on GPIOLIB && (ACPI || OF)
|
||||
select WATCHDOG_CORE
|
||||
help
|
||||
If you say yes here you get support for watchdog device
|
||||
@@ -882,7 +882,7 @@ config TEGRA_WATCHDOG
|
||||
module will be called tegra_wdt.
|
||||
|
||||
config QCOM_WDT
|
||||
tristate "QCOM watchdog"
|
||||
tristate "Qualcomm watchdog"
|
||||
depends on HAS_IOMEM
|
||||
depends on ARCH_QCOM || COMPILE_TEST
|
||||
select WATCHDOG_CORE
|
||||
@@ -1093,7 +1093,7 @@ config SPRD_WATCHDOG
|
||||
by Spreadtrum system.
|
||||
|
||||
config PM8916_WATCHDOG
|
||||
tristate "QCOM PM8916 pmic watchdog"
|
||||
tristate "Qualcomm PM8916 pmic watchdog"
|
||||
depends on OF && MFD_SPMI_PMIC
|
||||
select WATCHDOG_CORE
|
||||
help
|
||||
@@ -1270,19 +1270,6 @@ config GEODE_WDT
|
||||
You can compile this driver directly into the kernel, or use
|
||||
it as a module. The module will be called geodewdt.
|
||||
|
||||
config SC520_WDT
|
||||
tristate "AMD Elan SC520 processor Watchdog"
|
||||
depends on MELAN || COMPILE_TEST
|
||||
help
|
||||
This is the driver for the hardware watchdog built in to the
|
||||
AMD "Elan" SC520 microcomputer commonly used in embedded systems.
|
||||
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.
|
||||
|
||||
You can compile this driver directly into the kernel, or use
|
||||
it as a module. The module will be called sc520_wdt.
|
||||
|
||||
config SBC_FITPC2_WATCHDOG
|
||||
tristate "Compulab SBC-FITPC2 watchdog"
|
||||
depends on (X86 || COMPILE_TEST) && HAS_IOPORT
|
||||
@@ -1712,19 +1699,6 @@ config W83977F_WDT
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called w83977f_wdt.
|
||||
|
||||
config MACHZ_WDT
|
||||
tristate "ZF MachZ Watchdog"
|
||||
depends on (X86 || COMPILE_TEST) && HAS_IOPORT
|
||||
help
|
||||
If you are using a ZF Micro MachZ processor, say Y here, otherwise
|
||||
N. This is the driver for the watchdog timer built-in on that
|
||||
processor using ZF-Logic interface. 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.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called machzwd.
|
||||
|
||||
config SBC_EPX_C3_WATCHDOG
|
||||
tristate "Winsystems SBC EPX-C3 watchdog"
|
||||
depends on (X86 || COMPILE_TEST) && HAS_IOPORT
|
||||
@@ -2011,7 +1985,7 @@ config MT7621_WDT
|
||||
config PIC32_WDT
|
||||
tristate "Microchip PIC32 hardware watchdog"
|
||||
select WATCHDOG_CORE
|
||||
depends on MACH_PIC32 || (MIPS && COMPILE_TEST)
|
||||
depends on MACH_PIC32 || COMPILE_TEST
|
||||
help
|
||||
Watchdog driver for the built in watchdog hardware in a PIC32.
|
||||
|
||||
@@ -2024,7 +1998,7 @@ config PIC32_WDT
|
||||
config PIC32_DMT
|
||||
tristate "Microchip PIC32 Deadman Timer"
|
||||
select WATCHDOG_CORE
|
||||
depends on MACH_PIC32 || (MIPS && COMPILE_TEST)
|
||||
depends on MACH_PIC32 || COMPILE_TEST
|
||||
help
|
||||
Watchdog driver for PIC32 instruction fetch counting timer. This
|
||||
specific timer is typically be used in mission critical and safety
|
||||
@@ -2132,6 +2106,15 @@ config WATCHDOG_RTAS
|
||||
|
||||
# RISC-V Architecture
|
||||
|
||||
config ATCWDT200_WATCHDOG
|
||||
tristate "Andes ATCWDT200 Watchdog support"
|
||||
depends on ARCH_ANDES || COMPILE_TEST
|
||||
help
|
||||
Driver for the Andes ATCWDT200 watchdog timer. It provides access to
|
||||
programmable reset and interrupt counters with clock-source dependent
|
||||
timing. The driver automatically detects the supported IntTime bit-width
|
||||
and is fully integrated with the Linux Watchdog Framework.
|
||||
|
||||
config STARFIVE_WATCHDOG
|
||||
tristate "StarFive Watchdog support"
|
||||
depends on ARCH_STARFIVE || COMPILE_TEST
|
||||
@@ -2270,10 +2253,11 @@ config MIXCOMWD
|
||||
Most people will say N.
|
||||
|
||||
config WDT
|
||||
tristate "WDT Watchdog timer"
|
||||
tristate "ICS WDT500P/501P Watchdog timer"
|
||||
depends on ISA
|
||||
help
|
||||
If you have a WDT500P or WDT501P watchdog board, say Y here,
|
||||
If you have an Industrial Computer Source (ICS) WDT500P or WDT501P
|
||||
watchdog board, say Y here,
|
||||
otherwise N. It is not possible to probe for this board, which means
|
||||
that you have to inform the kernel about the IO port and IRQ that
|
||||
is needed (you can do this via the io and irq parameters)
|
||||
@@ -2304,10 +2288,11 @@ config PCIPCWATCHDOG
|
||||
Most people will say N.
|
||||
|
||||
config WDTPCI
|
||||
tristate "PCI-WDT500/501 Watchdog timer"
|
||||
tristate "ICS PCI-WDT500/501 Watchdog timer"
|
||||
depends on PCI && HAS_IOPORT
|
||||
help
|
||||
If you have a PCI-WDT500/501 watchdog board, say Y here, otherwise N.
|
||||
If you have an Industrial Computer Source (ICS) PCI-WDT500/501 watchdog
|
||||
board, say Y here, otherwise N.
|
||||
|
||||
If you have a PCI-WDT501 watchdog board then you can enable the
|
||||
temperature sensor by setting the type parameter to 501.
|
||||
@@ -2354,4 +2339,17 @@ config KEEMBAY_WATCHDOG
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called keembay_wdt.
|
||||
|
||||
config GUNYAH_WATCHDOG
|
||||
tristate "Qualcomm Gunyah Watchdog"
|
||||
depends on ARCH_QCOM || COMPILE_TEST
|
||||
depends on HAVE_ARM_SMCCC
|
||||
select WATCHDOG_CORE
|
||||
help
|
||||
Say Y here to include support for watchdog timer provided by the
|
||||
Gunyah hypervisor. The driver uses ARM SMC Calling Convention (SMCCC)
|
||||
to interact with Gunyah Watchdog.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called gunyah_wdt.
|
||||
|
||||
endif # WATCHDOG
|
||||
|
||||
@@ -103,6 +103,7 @@ obj-$(CONFIG_MSC313E_WATCHDOG) += msc313e_wdt.o
|
||||
obj-$(CONFIG_APPLE_WATCHDOG) += apple_wdt.o
|
||||
obj-$(CONFIG_SUNPLUS_WATCHDOG) += sunplus_wdt.o
|
||||
obj-$(CONFIG_MARVELL_GTI_WDT) += marvell_gti_wdt.o
|
||||
obj-$(CONFIG_GUNYAH_WATCHDOG) += gunyah_wdt.o
|
||||
|
||||
# X86 (i386 + ia64 + x86_64) Architecture
|
||||
obj-$(CONFIG_ACQUIRE_WDT) += acquirewdt.o
|
||||
@@ -116,7 +117,6 @@ obj-$(CONFIG_EXAR_WDT) += exar_wdt.o
|
||||
obj-$(CONFIG_F71808E_WDT) += f71808e_wdt.o
|
||||
obj-$(CONFIG_SP5100_TCO) += sp5100_tco.o
|
||||
obj-$(CONFIG_GEODE_WDT) += geodewdt.o
|
||||
obj-$(CONFIG_SC520_WDT) += sc520_wdt.o
|
||||
obj-$(CONFIG_SBC_FITPC2_WATCHDOG) += sbc_fitpc2_wdt.o
|
||||
obj-$(CONFIG_EUROTECH_WDT) += eurotechwdt.o
|
||||
obj-$(CONFIG_IB700_WDT) += ib700wdt.o
|
||||
@@ -146,7 +146,6 @@ obj-$(CONFIG_VIA_WDT) += via_wdt.o
|
||||
obj-$(CONFIG_W83627HF_WDT) += w83627hf_wdt.o
|
||||
obj-$(CONFIG_W83877F_WDT) += w83877f_wdt.o
|
||||
obj-$(CONFIG_W83977F_WDT) += w83977f_wdt.o
|
||||
obj-$(CONFIG_MACHZ_WDT) += machzwd.o
|
||||
obj-$(CONFIG_SBC_EPX_C3_WATCHDOG) += sbc_epx_c3.o
|
||||
obj-$(CONFIG_INTEL_MID_WATCHDOG) += intel-mid_wdt.o
|
||||
obj-$(CONFIG_INTEL_OC_WATCHDOG) += intel_oc_wdt.o
|
||||
@@ -198,6 +197,7 @@ obj-$(CONFIG_PSERIES_WDT) += pseries-wdt.o
|
||||
obj-$(CONFIG_WATCHDOG_RTAS) += wdrtas.o
|
||||
|
||||
# RISC-V Architecture
|
||||
obj-$(CONFIG_ATCWDT200_WATCHDOG) += atcwdt200_wdt.o
|
||||
obj-$(CONFIG_STARFIVE_WATCHDOG) += starfive-wdt.o
|
||||
|
||||
# S390 Architecture
|
||||
|
||||
@@ -218,6 +218,7 @@ static int apple_wdt_suspend(struct device *dev)
|
||||
static DEFINE_SIMPLE_DEV_PM_OPS(apple_wdt_pm_ops, apple_wdt_suspend, apple_wdt_resume);
|
||||
|
||||
static const struct of_device_id apple_wdt_of_match[] = {
|
||||
{ .compatible = "apple,t8103-wdt" },
|
||||
{ .compatible = "apple,wdt" },
|
||||
{},
|
||||
};
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
* Watchdog Timer (WDT) - System peripherals regsters.
|
||||
* Based on AT91SAM9261 datasheet revision D.
|
||||
* Based on SAM9X60 datasheet.
|
||||
* Based on SAMA7G5 datasheet.
|
||||
* Based on SAM9X75 datasheet.
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -27,10 +29,10 @@
|
||||
#define AT91_SAM9X60_PERIODRST BIT(4) /* Period Reset */
|
||||
#define AT91_SAM9X60_RPTHRST BIT(5) /* Minimum Restart Period */
|
||||
#define AT91_WDT_WDFIEN BIT(12) /* Fault Interrupt Enable */
|
||||
#define AT91_SAM9X60_WDDIS BIT(12) /* Watchdog Disable */
|
||||
#define AT91_SAM9X60_WDDIS BIT(12) /* Watchdog Disable (SAM9X60, SAMA7G5, SAM9X75) */
|
||||
#define AT91_WDT_WDRSTEN BIT(13) /* Reset Processor */
|
||||
#define AT91_WDT_WDRPROC BIT(14) /* Timer Restart */
|
||||
#define AT91_WDT_WDDIS BIT(15) /* Watchdog Disable */
|
||||
#define AT91_WDT_WDDIS BIT(15) /* Watchdog Disable (SAMA5, AT91SAM9261) */
|
||||
#define AT91_WDT_WDD (0xfffUL << 16) /* Delta Value */
|
||||
#define AT91_WDT_SET_WDD(x) (((x) << 16) & AT91_WDT_WDD)
|
||||
#define AT91_WDT_WDDBGHLT BIT(28) /* Debug Halt */
|
||||
|
||||
580
drivers/watchdog/atcwdt200_wdt.c
Normal file
580
drivers/watchdog/atcwdt200_wdt.c
Normal file
@@ -0,0 +1,580 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Andes ATCWDT200 watchdog timer driver.
|
||||
*
|
||||
* Copyright (C) 2025 Andes Technology Corporation
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/dev_printk.h>
|
||||
#include <linux/math64.h>
|
||||
#include <linux/minmax.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/watchdog.h>
|
||||
|
||||
/* Register definitions */
|
||||
#define REG_CTRL 0x10
|
||||
#define REG_RESTART 0x14
|
||||
#define REG_WRITE_EN 0x18
|
||||
#define REG_STATUS 0x1C
|
||||
|
||||
/* Control Register */
|
||||
#define CTRL_RST_TIME_MSK GENMASK(10, 8)
|
||||
#define CTRL_RST_TIME_SET(x) FIELD_PREP(CTRL_RST_TIME_MSK, x)
|
||||
#define CTRL_INT_TIME_MSK GENMASK(7, 4)
|
||||
#define CTRL_INT_TIME_SET(x) FIELD_PREP(CTRL_INT_TIME_MSK, x)
|
||||
#define CTRL_INT_TIME_GET(x) FIELD_GET(CTRL_INT_TIME_MSK, x)
|
||||
#define CTRL_RST_EN BIT(3)
|
||||
#define CTRL_CLK_SEL BIT(1)
|
||||
#define CTRL_CLK_SEL_PCLK 1
|
||||
#define CTRL_CLK_SEL_SET(x) FIELD_PREP(CTRL_CLK_SEL, x)
|
||||
#define CTRL_WDT_EN BIT(0)
|
||||
|
||||
/* Restart Register */
|
||||
#define RESTART_MAGIC 0xCAFE
|
||||
|
||||
/* Write Enable Register */
|
||||
#define WRITE_EN_MAGIC 0x5AA5
|
||||
|
||||
/* Status Register */
|
||||
#define STATUS_INT_EXPIRED BIT(1)
|
||||
|
||||
/* The default timeout value in seconds */
|
||||
#define ATCWDT_TIMEOUT 4
|
||||
|
||||
/* Define the array size for each timer type */
|
||||
#define TMR_SZ_RST 8
|
||||
#define TMR_SZ_INT_16 8
|
||||
#define TMR_SZ_INT_32 16
|
||||
|
||||
#define DRV_NAME "atcwdt200"
|
||||
/**
|
||||
* enum timer_type - Supported timer types for ATCWDT200 watchdog driver
|
||||
* @TMR_RST: Reset timer (non-interrupt).
|
||||
* @TMR_INT_16: 16-bit interrupt timer supported by hardware.
|
||||
* @TMR_INT_32: 32-bit interrupt timer supported by hardware.
|
||||
* @TMR_UNKNOWN: Timer type cannot be determined.
|
||||
*/
|
||||
enum timer_type {
|
||||
TMR_RST,
|
||||
TMR_INT_16,
|
||||
TMR_INT_32,
|
||||
TMR_UNKNOWN
|
||||
};
|
||||
|
||||
static unsigned int timeout = ATCWDT_TIMEOUT;
|
||||
static bool nowayout = WATCHDOG_NOWAYOUT;
|
||||
|
||||
/**
|
||||
* struct atcwdt_drv - ATCWDT200 watchdog driver private data
|
||||
* @wdt_dev: Watchdog device used by the watchdog framework.
|
||||
* @regmap: Register map for accessing hardware registers.
|
||||
* @clk: Hardware clock used by the watchdog timer.
|
||||
* @lock: Spinlock protecting register accesses and driver state.
|
||||
* @clk_freq: Input clock frequency of the ATCWDT200.
|
||||
* @clk_src: Selected clock source for the watchdog timer.
|
||||
* @int_timer_type: Detected interrupt timer type (16-bit, 32-bit, or unknown).
|
||||
*/
|
||||
struct atcwdt_drv {
|
||||
struct watchdog_device wdt_dev;
|
||||
struct regmap *regmap;
|
||||
struct clk *clk;
|
||||
spinlock_t lock;
|
||||
unsigned int clk_freq;
|
||||
unsigned char clk_src;
|
||||
unsigned char int_timer_type;
|
||||
};
|
||||
|
||||
static const struct watchdog_info atcwdt_info = {
|
||||
.identity = DRV_NAME,
|
||||
.options = WDIOF_SETTIMEOUT |
|
||||
WDIOF_KEEPALIVEPING |
|
||||
WDIOF_MAGICCLOSE,
|
||||
};
|
||||
|
||||
/**
|
||||
* atcwdt_get_index - Get the interval value for the specified timer type
|
||||
* @index: The index of the interval in the array
|
||||
* @timer_type: The type of timer, which can be TMR_RST, TMR_INT_16, or
|
||||
* TMR_INT_32.
|
||||
*
|
||||
* This function retrieves the interval value based on the timer type and
|
||||
* ensures the index stays within the valid range for the given timer type.
|
||||
* For TMR_RST:
|
||||
* - The maximum array size is 8 (index range: 0-7).
|
||||
* For TMR_INT_16:
|
||||
* - The maximum array size is 8 (index range: 0-7).
|
||||
* For TMR_INT_32:
|
||||
* - The maximum array size is 16 (index range: 0-15).
|
||||
*
|
||||
* If the index exceeds the maximum array size, the function will return
|
||||
* the last element of the respective array.
|
||||
*/
|
||||
static inline unsigned char atcwdt_get_index(unsigned char index,
|
||||
enum timer_type timer_type)
|
||||
{
|
||||
static const unsigned char rst_timer_interval[TMR_SZ_RST] = {
|
||||
7, 8, 9, 10, 11, 12, 13, 14};
|
||||
static const unsigned char int_timer_interval[TMR_SZ_INT_32] = {
|
||||
6, 8, 10, 11, 12, 13, 14, 15, 17, 19, 21, 23, 25, 27, 29, 31};
|
||||
unsigned char array_index;
|
||||
|
||||
if (timer_type == TMR_RST) {
|
||||
array_index = min(index, TMR_SZ_RST - 1);
|
||||
return rst_timer_interval[array_index];
|
||||
}
|
||||
|
||||
if (timer_type == TMR_INT_32)
|
||||
array_index = min(index, TMR_SZ_INT_32 - 1);
|
||||
else
|
||||
array_index = min(index, TMR_SZ_INT_16 - 1);
|
||||
|
||||
return int_timer_interval[array_index];
|
||||
}
|
||||
|
||||
/**
|
||||
* atcwdt_get_clock_period - Calculate the closest clock period based on a
|
||||
* given tick count
|
||||
* @tick: The target tick count to match
|
||||
* @timer_type: The type of timer, which can be TMR_RST, TMR_INT_16, or
|
||||
* TMR_INT_32.
|
||||
* @index: Pointer to store the index of the selected parameter
|
||||
*
|
||||
* This function calculates the closest clock period to the given tick count
|
||||
* by iterating through the timer parameters and selecting the one that
|
||||
* minimizes the difference between the target tick count and the calculated
|
||||
* clock period. The function determines the index of the closest parameter
|
||||
* and returns the difference between the target tick count and the selected
|
||||
* clock period.
|
||||
*
|
||||
* Return: The difference between the target tick count and the selected
|
||||
* clock period.
|
||||
*/
|
||||
static long long atcwdt_get_clock_period(long long tick,
|
||||
enum timer_type timer_type,
|
||||
unsigned char *index)
|
||||
{
|
||||
long long result;
|
||||
unsigned char size;
|
||||
char i;
|
||||
|
||||
if (timer_type == TMR_RST)
|
||||
size = TMR_SZ_RST;
|
||||
else if (timer_type == TMR_INT_32)
|
||||
size = TMR_SZ_INT_32;
|
||||
else
|
||||
size = TMR_SZ_INT_16;
|
||||
|
||||
*index = size - 1;
|
||||
for (i = 0; i < size; i++) {
|
||||
result = tick - (1LL << atcwdt_get_index(i, timer_type));
|
||||
|
||||
if (result <= 1) {
|
||||
*index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* atcwdt_get_timeout_params - Calculate optimal parameters for Watchdog Timer
|
||||
* @drv_data: Pointer to the Watchdog driver data structure
|
||||
* @timeout: Desired timeout value (in seconds)
|
||||
* @int_timer_params: Pointer to store the calculated interrupt timer
|
||||
* parameter index
|
||||
* @rst_timer_params: Pointer to store the calculated reset timer parameter
|
||||
* index
|
||||
*
|
||||
* This function calculates the optimal parameter combination for the
|
||||
* interrupt timer and reset timer of the Watchdog Timer to achieve a
|
||||
* timeout value closest to, but not less than the specified timeout.
|
||||
*
|
||||
* Algorithm:
|
||||
* 1. The parameters for both the interrupt timer and reset timer are
|
||||
* predefined as a series of options represented as powers of 2.
|
||||
* 2. The function first determines the interrupt timer's parameter index
|
||||
* that provides a time closest to and not exceeding the desired timeout.
|
||||
* 3. Based on the selected interrupt timer, it calculates the required
|
||||
* reset timer parameter to ensure the total timeout matches the target.
|
||||
*
|
||||
* Return: The calculated parameter indices are stored in the provided
|
||||
* pointers.
|
||||
*/
|
||||
static void atcwdt_get_timeout_params(struct atcwdt_drv *drv_data,
|
||||
unsigned int timeout,
|
||||
unsigned char *int_timer_params,
|
||||
unsigned char *rst_timer_params)
|
||||
{
|
||||
long long rest_time_ms;
|
||||
long long result;
|
||||
long long tick;
|
||||
unsigned char rst_index;
|
||||
unsigned char int_index;
|
||||
unsigned char above;
|
||||
unsigned char below;
|
||||
|
||||
tick = (long long)timeout * drv_data->clk_freq;
|
||||
result = atcwdt_get_clock_period(tick,
|
||||
drv_data->int_timer_type,
|
||||
&above);
|
||||
if (result == 0 || above == 0) {
|
||||
*int_timer_params = above;
|
||||
*rst_timer_params = 0;
|
||||
return;
|
||||
}
|
||||
below = above - 1;
|
||||
|
||||
int_index = atcwdt_get_index(below, drv_data->int_timer_type);
|
||||
rest_time_ms = timeout * 1000LL
|
||||
- div64_s64(1000LL << int_index, drv_data->clk_freq);
|
||||
|
||||
result = atcwdt_get_clock_period(rest_time_ms * drv_data->clk_freq,
|
||||
TMR_RST,
|
||||
&rst_index);
|
||||
|
||||
if (result > 1) {
|
||||
*int_timer_params = above;
|
||||
*rst_timer_params = 0;
|
||||
} else {
|
||||
*int_timer_params = below;
|
||||
*rst_timer_params = rst_index;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* atcwdt_get_int_timer_type - Get the supported interrupt timer type.
|
||||
* @drv_data: Pointer to the watchdog driver data structure.
|
||||
*
|
||||
* This function tests the writable bits in the IntTime field of the control
|
||||
* register to determine the interrupt timer type supported by the hardware.
|
||||
*
|
||||
* Note: This function must only be called when the ATCWDT200 watchdog is
|
||||
* disabled. If the watchdog is enabled, this function returns TMR_UNKNOWN.
|
||||
*
|
||||
* Returns: The interrupt timer type supported by the hardware.
|
||||
*/
|
||||
static int atcwdt_get_int_timer_type(struct atcwdt_drv *drv_data)
|
||||
{
|
||||
struct device *dev = drv_data->wdt_dev.parent;
|
||||
unsigned int val;
|
||||
int ret = 0;
|
||||
|
||||
spin_lock(&drv_data->lock);
|
||||
regmap_read(drv_data->regmap, REG_CTRL, &val);
|
||||
if (val & CTRL_WDT_EN) {
|
||||
spin_unlock(&drv_data->lock);
|
||||
return TMR_UNKNOWN;
|
||||
}
|
||||
|
||||
/*
|
||||
* Configures the IntTime field with the maximum mask value
|
||||
* (CTRL_INT_TIME_MSK), reads its value from the control register
|
||||
* to identify the maximum writable bits.
|
||||
*/
|
||||
regmap_write(drv_data->regmap, REG_WRITE_EN, WRITE_EN_MAGIC);
|
||||
regmap_write(drv_data->regmap, REG_CTRL, CTRL_INT_TIME_MSK);
|
||||
regmap_read(drv_data->regmap, REG_CTRL, &val);
|
||||
spin_unlock(&drv_data->lock);
|
||||
|
||||
val = CTRL_INT_TIME_GET(val);
|
||||
switch (val) {
|
||||
case 7:
|
||||
drv_data->int_timer_type = TMR_INT_16;
|
||||
break;
|
||||
case 15:
|
||||
drv_data->int_timer_type = TMR_INT_32;
|
||||
break;
|
||||
default:
|
||||
drv_data->int_timer_type = TMR_UNKNOWN;
|
||||
ret = dev_err_probe(dev, -ENODEV,
|
||||
"Failed to detect interrupt timer type\n");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int atcwdt_ping(struct watchdog_device *wdt_dev)
|
||||
{
|
||||
struct atcwdt_drv *drv_data = watchdog_get_drvdata(wdt_dev);
|
||||
|
||||
spin_lock(&drv_data->lock);
|
||||
regmap_write(drv_data->regmap, REG_WRITE_EN, WRITE_EN_MAGIC);
|
||||
regmap_write(drv_data->regmap, REG_RESTART, RESTART_MAGIC);
|
||||
regmap_update_bits(drv_data->regmap, REG_STATUS, STATUS_INT_EXPIRED,
|
||||
STATUS_INT_EXPIRED);
|
||||
spin_unlock(&drv_data->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int atcwdt_set_timeout(struct watchdog_device *wdt_dev,
|
||||
unsigned int timeout)
|
||||
{
|
||||
struct atcwdt_drv *drv_data = watchdog_get_drvdata(wdt_dev);
|
||||
unsigned int value;
|
||||
unsigned char rst_val;
|
||||
unsigned char int_val;
|
||||
|
||||
wdt_dev->timeout = timeout;
|
||||
atcwdt_get_timeout_params(drv_data, timeout, &int_val, &rst_val);
|
||||
|
||||
spin_lock(&drv_data->lock);
|
||||
regmap_write(drv_data->regmap, REG_WRITE_EN, WRITE_EN_MAGIC);
|
||||
|
||||
value = CTRL_RST_TIME_SET(rst_val) |
|
||||
CTRL_INT_TIME_SET(int_val) |
|
||||
CTRL_CLK_SEL_SET(drv_data->clk_src);
|
||||
regmap_update_bits(drv_data->regmap,
|
||||
REG_CTRL,
|
||||
CTRL_RST_TIME_MSK |
|
||||
CTRL_INT_TIME_MSK |
|
||||
CTRL_CLK_SEL,
|
||||
value);
|
||||
|
||||
spin_unlock(&drv_data->lock);
|
||||
atcwdt_ping(wdt_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int atcwdt_start(struct watchdog_device *wdt_dev)
|
||||
{
|
||||
struct atcwdt_drv *drv_data = watchdog_get_drvdata(wdt_dev);
|
||||
|
||||
atcwdt_set_timeout(wdt_dev, wdt_dev->timeout);
|
||||
|
||||
spin_lock(&drv_data->lock);
|
||||
regmap_write(drv_data->regmap, REG_WRITE_EN, WRITE_EN_MAGIC);
|
||||
regmap_update_bits(drv_data->regmap,
|
||||
REG_CTRL,
|
||||
CTRL_RST_EN | CTRL_WDT_EN,
|
||||
CTRL_RST_EN | CTRL_WDT_EN);
|
||||
|
||||
spin_unlock(&drv_data->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int atcwdt_stop(struct watchdog_device *wdt_dev)
|
||||
{
|
||||
struct atcwdt_drv *drv_data = watchdog_get_drvdata(wdt_dev);
|
||||
|
||||
spin_lock(&drv_data->lock);
|
||||
regmap_write(drv_data->regmap, REG_WRITE_EN, WRITE_EN_MAGIC);
|
||||
regmap_update_bits(drv_data->regmap,
|
||||
REG_CTRL,
|
||||
CTRL_RST_EN | CTRL_WDT_EN,
|
||||
0);
|
||||
spin_unlock(&drv_data->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int atcwdt_restart(struct watchdog_device *wdt_dev,
|
||||
unsigned long action, void *data)
|
||||
{
|
||||
struct atcwdt_drv *drv_data = watchdog_get_drvdata(wdt_dev);
|
||||
|
||||
atcwdt_set_timeout(wdt_dev, 0);
|
||||
|
||||
spin_lock(&drv_data->lock);
|
||||
regmap_write(drv_data->regmap, REG_WRITE_EN, WRITE_EN_MAGIC);
|
||||
regmap_update_bits(drv_data->regmap,
|
||||
REG_CTRL,
|
||||
CTRL_RST_EN | CTRL_WDT_EN,
|
||||
CTRL_RST_EN | CTRL_WDT_EN);
|
||||
spin_unlock(&drv_data->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct watchdog_ops atcwdt_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.start = atcwdt_start,
|
||||
.stop = atcwdt_stop,
|
||||
.ping = atcwdt_ping,
|
||||
.set_timeout = atcwdt_set_timeout,
|
||||
.restart = atcwdt_restart,
|
||||
};
|
||||
|
||||
static int atcwdt_init_resource(struct platform_device *pdev,
|
||||
struct atcwdt_drv *drv_data)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
void __iomem *base;
|
||||
const struct regmap_config cfg = {
|
||||
.name = "atcwdt",
|
||||
.reg_bits = 32,
|
||||
.val_bits = 32,
|
||||
.cache_type = REGCACHE_NONE,
|
||||
.reg_stride = 4,
|
||||
.max_register = REG_STATUS,
|
||||
};
|
||||
|
||||
base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(base))
|
||||
return dev_err_probe(dev, PTR_ERR(base),
|
||||
"Failed to ioremap I/O resource\n");
|
||||
|
||||
drv_data->regmap = devm_regmap_init_mmio(dev, base, &cfg);
|
||||
if (IS_ERR(drv_data->regmap))
|
||||
return dev_err_probe(dev, PTR_ERR(drv_data->regmap),
|
||||
"Failed to create regmap\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int atcwdt_enable_clk(struct atcwdt_drv *drv_data)
|
||||
{
|
||||
struct device *dev = drv_data->wdt_dev.parent;
|
||||
unsigned int val;
|
||||
int clk_src;
|
||||
|
||||
drv_data->clk = devm_clk_get_enabled(dev, NULL);
|
||||
if (IS_ERR(drv_data->clk))
|
||||
return dev_err_probe(dev, PTR_ERR(drv_data->clk),
|
||||
"Failed to get watchdog clock\n");
|
||||
|
||||
drv_data->clk_freq = clk_get_rate(drv_data->clk);
|
||||
if (!drv_data->clk_freq)
|
||||
return dev_err_probe(dev, -EINVAL,
|
||||
"Failed to get clock rate\n");
|
||||
|
||||
clk_src = device_property_read_u32(dev, "andestech,clock-source", &val);
|
||||
drv_data->clk_src = (!clk_src && val != 0) ? CTRL_CLK_SEL_PCLK : 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int atcwdt_init_wdt_device(struct device *dev,
|
||||
struct atcwdt_drv *drv_data)
|
||||
{
|
||||
struct watchdog_device *wdd = &drv_data->wdt_dev;
|
||||
|
||||
wdd->parent = dev;
|
||||
wdd->info = &atcwdt_info;
|
||||
wdd->ops = &atcwdt_ops;
|
||||
wdd->timeout = ATCWDT_TIMEOUT;
|
||||
wdd->min_timeout = 1;
|
||||
|
||||
watchdog_set_nowayout(wdd, nowayout);
|
||||
watchdog_set_drvdata(wdd, drv_data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void atcwdt_calc_max_timeout(struct atcwdt_drv *drv_data)
|
||||
{
|
||||
unsigned char rst_idx = atcwdt_get_index(0xFF, TMR_RST);
|
||||
unsigned char int_idx = atcwdt_get_index(0xFF,
|
||||
drv_data->int_timer_type);
|
||||
|
||||
drv_data->wdt_dev.max_timeout =
|
||||
((1U << rst_idx) + (1U << int_idx)) / drv_data->clk_freq;
|
||||
}
|
||||
|
||||
static int atcwdt_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct atcwdt_drv *drv_data;
|
||||
int ret;
|
||||
|
||||
drv_data = devm_kzalloc(dev, sizeof(*drv_data), GFP_KERNEL);
|
||||
if (!drv_data)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, drv_data);
|
||||
spin_lock_init(&drv_data->lock);
|
||||
|
||||
ret = atcwdt_init_wdt_device(dev, drv_data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = atcwdt_init_resource(pdev, drv_data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = atcwdt_enable_clk(drv_data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = atcwdt_get_int_timer_type(drv_data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
atcwdt_calc_max_timeout(drv_data);
|
||||
|
||||
ret = devm_watchdog_register_device(dev, &drv_data->wdt_dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int atcwdt_suspend(struct device *dev)
|
||||
{
|
||||
struct atcwdt_drv *drv_data = dev_get_drvdata(dev);
|
||||
|
||||
if (watchdog_active(&drv_data->wdt_dev)) {
|
||||
atcwdt_stop(&drv_data->wdt_dev);
|
||||
clk_disable_unprepare(drv_data->clk);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int atcwdt_resume(struct device *dev)
|
||||
{
|
||||
struct atcwdt_drv *drv_data = dev_get_drvdata(dev);
|
||||
int ret = 0;
|
||||
|
||||
if (watchdog_active(&drv_data->wdt_dev)) {
|
||||
ret = clk_prepare_enable(drv_data->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
atcwdt_start(&drv_data->wdt_dev);
|
||||
atcwdt_ping(&drv_data->wdt_dev);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct of_device_id atcwdt_match[] = {
|
||||
{ .compatible = "andestech,ae350-wdt" },
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, atcwdt_match);
|
||||
|
||||
static DEFINE_SIMPLE_DEV_PM_OPS(atcwdt_pm_ops, atcwdt_suspend, atcwdt_resume);
|
||||
|
||||
static struct platform_driver atcwdt_driver = {
|
||||
.probe = atcwdt_probe,
|
||||
.driver = {
|
||||
.name = DRV_NAME,
|
||||
.of_match_table = atcwdt_match,
|
||||
.pm = pm_sleep_ptr(&atcwdt_pm_ops),
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(atcwdt_driver);
|
||||
|
||||
module_param(timeout, uint, 0);
|
||||
MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds (default="
|
||||
__MODULE_STRING(ATCWDT_TIMEOUT) ")");
|
||||
|
||||
module_param(nowayout, bool, 0);
|
||||
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
|
||||
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("CL Wang <cl634@andestech.com>");
|
||||
MODULE_DESCRIPTION("Andes ATCWDT200 Watchdog timer driver");
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/reboot.h>
|
||||
|
||||
#define PM_RSTC 0x1c
|
||||
#define PM_RSTS 0x20
|
||||
@@ -49,8 +50,6 @@ struct bcm2835_wdt {
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
static struct bcm2835_wdt *bcm2835_power_off_wdt;
|
||||
|
||||
static unsigned int heartbeat;
|
||||
static bool nowayout = WATCHDOG_NOWAYOUT;
|
||||
|
||||
@@ -150,9 +149,9 @@ static struct watchdog_device bcm2835_wdt_wdd = {
|
||||
* indicate to bootcode.bin not to reboot, then most of the chip will be
|
||||
* powered off.
|
||||
*/
|
||||
static void bcm2835_power_off(void)
|
||||
static int bcm2835_power_off(struct sys_off_data *data)
|
||||
{
|
||||
struct bcm2835_wdt *wdt = bcm2835_power_off_wdt;
|
||||
struct bcm2835_wdt *wdt = data->cb_data;
|
||||
u32 val;
|
||||
|
||||
/*
|
||||
@@ -166,6 +165,8 @@ static void bcm2835_power_off(void)
|
||||
|
||||
/* Continue with normal reset mechanism */
|
||||
__bcm2835_restart(wdt);
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static int bcm2835_wdt_probe(struct platform_device *pdev)
|
||||
@@ -206,28 +207,17 @@ static int bcm2835_wdt_probe(struct platform_device *pdev)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (of_device_is_system_power_controller(pdev->dev.parent->of_node)) {
|
||||
if (!pm_power_off) {
|
||||
pm_power_off = bcm2835_power_off;
|
||||
bcm2835_power_off_wdt = wdt;
|
||||
} else {
|
||||
dev_info(dev, "Poweroff handler already present!\n");
|
||||
}
|
||||
}
|
||||
if (of_device_is_system_power_controller(pdev->dev.parent->of_node))
|
||||
devm_register_sys_off_handler(dev, SYS_OFF_MODE_POWER_OFF,
|
||||
SYS_OFF_PRIO_DEFAULT,
|
||||
bcm2835_power_off, wdt);
|
||||
|
||||
dev_info(dev, "Broadcom BCM2835 watchdog timer");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void bcm2835_wdt_remove(struct platform_device *pdev)
|
||||
{
|
||||
if (pm_power_off == bcm2835_power_off)
|
||||
pm_power_off = NULL;
|
||||
}
|
||||
|
||||
static struct platform_driver bcm2835_wdt_driver = {
|
||||
.probe = bcm2835_wdt_probe,
|
||||
.remove = bcm2835_wdt_remove,
|
||||
.driver = {
|
||||
.name = "bcm2835-wdt",
|
||||
},
|
||||
|
||||
261
drivers/watchdog/gunyah_wdt.c
Normal file
261
drivers/watchdog/gunyah_wdt.c
Normal file
@@ -0,0 +1,261 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
|
||||
*/
|
||||
|
||||
#include <linux/arm-smccc.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/watchdog.h>
|
||||
|
||||
#define GUNYAH_WDT_SMCCC_CALL_VAL(func_id) \
|
||||
ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_32,\
|
||||
ARM_SMCCC_OWNER_VENDOR_HYP, func_id)
|
||||
|
||||
/* SMCCC function IDs for watchdog operations */
|
||||
#define GUNYAH_WDT_CONTROL GUNYAH_WDT_SMCCC_CALL_VAL(0x0005)
|
||||
#define GUNYAH_WDT_STATUS GUNYAH_WDT_SMCCC_CALL_VAL(0x0006)
|
||||
#define GUNYAH_WDT_PING GUNYAH_WDT_SMCCC_CALL_VAL(0x0007)
|
||||
#define GUNYAH_WDT_SET_TIME GUNYAH_WDT_SMCCC_CALL_VAL(0x0008)
|
||||
|
||||
/*
|
||||
* Control values for GUNYAH_WDT_CONTROL.
|
||||
* Bit 0 is used to enable or disable the watchdog. If this bit is set,
|
||||
* then the watchdog is enabled and vice versa.
|
||||
* Bit 1 should always be set to 1 as this bit is reserved in Gunyah and
|
||||
* it's expected to be 1.
|
||||
*/
|
||||
#define WDT_CTRL_ENABLE (BIT(1) | BIT(0))
|
||||
#define WDT_CTRL_DISABLE BIT(1)
|
||||
|
||||
enum gunyah_error {
|
||||
GUNYAH_ERROR_OK = 0,
|
||||
GUNYAH_ERROR_UNIMPLEMENTED = -1,
|
||||
GUNYAH_ERROR_ARG_INVAL = 1,
|
||||
};
|
||||
|
||||
/**
|
||||
* gunyah_error_remap() - Remap Gunyah hypervisor errors into a Linux error code
|
||||
* @gunyah_error: Gunyah hypercall return value
|
||||
*/
|
||||
static inline int gunyah_error_remap(enum gunyah_error gunyah_error)
|
||||
{
|
||||
switch (gunyah_error) {
|
||||
case GUNYAH_ERROR_OK:
|
||||
return 0;
|
||||
case GUNYAH_ERROR_UNIMPLEMENTED:
|
||||
return -EOPNOTSUPP;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int gunyah_wdt_call(unsigned long func_id, unsigned long arg1,
|
||||
unsigned long arg2)
|
||||
{
|
||||
struct arm_smccc_res res;
|
||||
|
||||
arm_smccc_1_1_smc(func_id, arg1, arg2, &res);
|
||||
return gunyah_error_remap(res.a0);
|
||||
}
|
||||
|
||||
static int gunyah_wdt_start(struct watchdog_device *wdd)
|
||||
{
|
||||
unsigned int timeout_ms;
|
||||
struct device *dev = wdd->parent;
|
||||
int ret;
|
||||
|
||||
ret = gunyah_wdt_call(GUNYAH_WDT_CONTROL, WDT_CTRL_DISABLE, 0);
|
||||
if (ret && watchdog_active(wdd)) {
|
||||
dev_err(dev, "%s: Failed to stop gunyah wdt %d\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
timeout_ms = wdd->timeout * 1000;
|
||||
ret = gunyah_wdt_call(GUNYAH_WDT_SET_TIME, timeout_ms, timeout_ms);
|
||||
if (ret) {
|
||||
dev_err(dev, "%s: Failed to set timeout for gunyah wdt %d\n",
|
||||
__func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = gunyah_wdt_call(GUNYAH_WDT_CONTROL, WDT_CTRL_ENABLE, 0);
|
||||
if (ret)
|
||||
dev_err(dev, "%s: Failed to start gunyah wdt %d\n", __func__, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int gunyah_wdt_stop(struct watchdog_device *wdd)
|
||||
{
|
||||
return gunyah_wdt_call(GUNYAH_WDT_CONTROL, WDT_CTRL_DISABLE, 0);
|
||||
}
|
||||
|
||||
static int gunyah_wdt_ping(struct watchdog_device *wdd)
|
||||
{
|
||||
return gunyah_wdt_call(GUNYAH_WDT_PING, 0, 0);
|
||||
}
|
||||
|
||||
static int gunyah_wdt_set_timeout(struct watchdog_device *wdd,
|
||||
unsigned int timeout_sec)
|
||||
{
|
||||
wdd->timeout = timeout_sec;
|
||||
|
||||
if (watchdog_active(wdd))
|
||||
return gunyah_wdt_start(wdd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gunyah_wdt_get_time_since_last_ping(void)
|
||||
{
|
||||
struct arm_smccc_res res;
|
||||
|
||||
arm_smccc_1_1_smc(GUNYAH_WDT_STATUS, 0, 0, &res);
|
||||
if (res.a0)
|
||||
return gunyah_error_remap(res.a0);
|
||||
|
||||
return res.a2 / 1000;
|
||||
}
|
||||
|
||||
static unsigned int gunyah_wdt_get_timeleft(struct watchdog_device *wdd)
|
||||
{
|
||||
int seconds_since_last_ping;
|
||||
|
||||
seconds_since_last_ping = gunyah_wdt_get_time_since_last_ping();
|
||||
if (seconds_since_last_ping < 0 ||
|
||||
seconds_since_last_ping > wdd->timeout)
|
||||
return 0;
|
||||
|
||||
return wdd->timeout - seconds_since_last_ping;
|
||||
}
|
||||
|
||||
static int gunyah_wdt_restart(struct watchdog_device *wdd,
|
||||
unsigned long action, void *data)
|
||||
{
|
||||
/* Set timeout to 1ms and send a ping */
|
||||
gunyah_wdt_call(GUNYAH_WDT_CONTROL, WDT_CTRL_DISABLE, 0);
|
||||
gunyah_wdt_call(GUNYAH_WDT_SET_TIME, 1, 1);
|
||||
gunyah_wdt_call(GUNYAH_WDT_CONTROL, WDT_CTRL_ENABLE, 0);
|
||||
gunyah_wdt_call(GUNYAH_WDT_PING, 0, 0);
|
||||
|
||||
/* Wait to make sure reset occurs */
|
||||
mdelay(100);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct watchdog_info gunyah_wdt_info = {
|
||||
.identity = "Gunyah Watchdog",
|
||||
.options = WDIOF_SETTIMEOUT
|
||||
| WDIOF_KEEPALIVEPING
|
||||
| WDIOF_MAGICCLOSE,
|
||||
};
|
||||
|
||||
static const struct watchdog_ops gunyah_wdt_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.start = gunyah_wdt_start,
|
||||
.stop = gunyah_wdt_stop,
|
||||
.ping = gunyah_wdt_ping,
|
||||
.set_timeout = gunyah_wdt_set_timeout,
|
||||
.get_timeleft = gunyah_wdt_get_timeleft,
|
||||
.restart = gunyah_wdt_restart
|
||||
};
|
||||
|
||||
static int gunyah_wdt_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct watchdog_device *wdd;
|
||||
struct device *dev = &pdev->dev;
|
||||
int ret;
|
||||
|
||||
ret = gunyah_wdt_call(GUNYAH_WDT_STATUS, 0, 0);
|
||||
if (ret == -EOPNOTSUPP)
|
||||
return -ENODEV;
|
||||
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "status check failed\n");
|
||||
|
||||
wdd = devm_kzalloc(dev, sizeof(*wdd), GFP_KERNEL);
|
||||
if (!wdd)
|
||||
return -ENOMEM;
|
||||
|
||||
wdd->info = &gunyah_wdt_info;
|
||||
wdd->ops = &gunyah_wdt_ops;
|
||||
wdd->parent = dev;
|
||||
|
||||
/*
|
||||
* Although Gunyah expects 16-bit unsigned int values as timeout values
|
||||
* in milliseconds, values above 0x8000 are reserved. This limits the
|
||||
* max timeout value to 32 seconds.
|
||||
*/
|
||||
wdd->max_timeout = 32; /* seconds */
|
||||
wdd->min_timeout = 1; /* seconds */
|
||||
wdd->timeout = wdd->max_timeout;
|
||||
|
||||
gunyah_wdt_stop(wdd);
|
||||
platform_set_drvdata(pdev, wdd);
|
||||
watchdog_set_restart_priority(wdd, 0);
|
||||
|
||||
return devm_watchdog_register_device(dev, wdd);
|
||||
}
|
||||
|
||||
static void gunyah_wdt_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct watchdog_device *wdd = platform_get_drvdata(pdev);
|
||||
|
||||
gunyah_wdt_stop(wdd);
|
||||
}
|
||||
|
||||
static int gunyah_wdt_suspend(struct device *dev)
|
||||
{
|
||||
struct watchdog_device *wdd = dev_get_drvdata(dev);
|
||||
|
||||
if (watchdog_active(wdd))
|
||||
gunyah_wdt_stop(wdd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gunyah_wdt_resume(struct device *dev)
|
||||
{
|
||||
struct watchdog_device *wdd = dev_get_drvdata(dev);
|
||||
|
||||
if (watchdog_active(wdd))
|
||||
gunyah_wdt_start(wdd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static DEFINE_SIMPLE_DEV_PM_OPS(gunyah_wdt_pm_ops, gunyah_wdt_suspend, gunyah_wdt_resume);
|
||||
|
||||
/*
|
||||
* Gunyah watchdog is a vendor-specific hypervisor interface provided by the
|
||||
* Gunyah hypervisor. Using QCOM SCM driver to detect Gunyah watchdog SMCCC
|
||||
* hypervisor service and register platform device when the service is available
|
||||
* allows this driver to operate independently of the devicetree and avoids
|
||||
* adding the non-hardware nodes to the devicetree.
|
||||
*/
|
||||
static const struct platform_device_id gunyah_wdt_id[] = {
|
||||
{ .name = "gunyah-wdt" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, gunyah_wdt_id);
|
||||
|
||||
static struct platform_driver gunyah_wdt_driver = {
|
||||
.driver = {
|
||||
.name = "gunyah-wdt",
|
||||
.pm = pm_sleep_ptr(&gunyah_wdt_pm_ops),
|
||||
},
|
||||
.id_table = gunyah_wdt_id,
|
||||
.probe = gunyah_wdt_probe,
|
||||
.remove = gunyah_wdt_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(gunyah_wdt_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Gunyah Watchdog Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <linux/watchdog.h>
|
||||
#ifdef CONFIG_HPWDT_NMI_DECODING
|
||||
#include <asm/nmi.h>
|
||||
#include <asm/uv/uv.h>
|
||||
#endif
|
||||
#include <linux/crash_dump.h>
|
||||
|
||||
@@ -159,24 +160,31 @@ static int hpwdt_set_pretimeout(struct watchdog_device *wdd, unsigned int req)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hpwdt_my_nmi(void)
|
||||
{
|
||||
return ioread8(hpwdt_nmistat) & 0x6;
|
||||
}
|
||||
#define NMISTAT_EASR BIT(0)
|
||||
#define NMISTAT_EWDOG BIT(1)
|
||||
#define NMISTAT_RTRAP BIT(2)
|
||||
#define NMISTAT_DBELL BIT(3)
|
||||
#define NMISTAT_EMSWDG BIT(4)
|
||||
#define NMISTAT_GPI BIT(5)
|
||||
#define NMISTAT_NMIOUT BIT(7)
|
||||
|
||||
/*
|
||||
* NMI Handler
|
||||
*/
|
||||
static int hpwdt_pretimeout(unsigned int ulReason, struct pt_regs *regs)
|
||||
{
|
||||
unsigned int mynmi = hpwdt_my_nmi();
|
||||
static char panic_msg[] =
|
||||
u8 nmistat = ioread8(hpwdt_nmistat);
|
||||
bool mynmi = (nmistat & (NMISTAT_EWDOG | NMISTAT_RTRAP)) != 0;
|
||||
static char panic_msg_default[] =
|
||||
"00: An NMI occurred. Depending on your system the reason "
|
||||
"for the NMI is logged in any one of the following resources:\n"
|
||||
"1. Integrated Management Log (IML)\n"
|
||||
"2. OA Syslog\n"
|
||||
"3. OA Forward Progress Log\n"
|
||||
"4. iLO Event Log";
|
||||
static char panic_msg_uv[] =
|
||||
"00: A watchdog NMI occurred.";
|
||||
char *panic_msg = is_uv_system() ? panic_msg_uv : panic_msg_default;
|
||||
|
||||
if (ulReason == NMI_UNKNOWN && !mynmi)
|
||||
return NMI_DONE;
|
||||
@@ -190,7 +198,7 @@ static int hpwdt_pretimeout(unsigned int ulReason, struct pt_regs *regs)
|
||||
hpwdt_ping_ticks(SECS_TO_TICKS(val));
|
||||
}
|
||||
|
||||
hex_byte_pack(panic_msg, mynmi);
|
||||
hex_byte_pack(panic_msg, nmistat);
|
||||
nmi_panic(regs, panic_msg);
|
||||
|
||||
return NMI_HANDLED;
|
||||
|
||||
@@ -56,6 +56,7 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
|
||||
struct imx_wdt_hw_feature {
|
||||
bool prescaler_enable;
|
||||
bool post_rcs_wait;
|
||||
bool cpu_lpm_auto_cg;
|
||||
u32 wdog_clock_rate;
|
||||
};
|
||||
|
||||
@@ -360,7 +361,7 @@ static int __maybe_unused imx7ulp_wdt_suspend_noirq(struct device *dev)
|
||||
{
|
||||
struct imx7ulp_wdt_device *imx7ulp_wdt = dev_get_drvdata(dev);
|
||||
|
||||
if (watchdog_active(&imx7ulp_wdt->wdd))
|
||||
if (watchdog_active(&imx7ulp_wdt->wdd) && !imx7ulp_wdt->hw->cpu_lpm_auto_cg)
|
||||
imx7ulp_wdt_stop(&imx7ulp_wdt->wdd);
|
||||
|
||||
clk_disable_unprepare(imx7ulp_wdt->clk);
|
||||
@@ -408,10 +409,17 @@ static const struct imx_wdt_hw_feature imx93_wdt_hw = {
|
||||
.wdog_clock_rate = 125,
|
||||
};
|
||||
|
||||
static const struct imx_wdt_hw_feature imx94_wdt_hw = {
|
||||
.prescaler_enable = true,
|
||||
.wdog_clock_rate = 125,
|
||||
.cpu_lpm_auto_cg = true,
|
||||
};
|
||||
|
||||
static const struct of_device_id imx7ulp_wdt_dt_ids[] = {
|
||||
{ .compatible = "fsl,imx7ulp-wdt", .data = &imx7ulp_wdt_hw, },
|
||||
{ .compatible = "fsl,imx8ulp-wdt", .data = &imx8ulp_wdt_hw, },
|
||||
{ .compatible = "fsl,imx93-wdt", .data = &imx93_wdt_hw, },
|
||||
{ .compatible = "fsl,imx94-wdt", .data = &imx94_wdt_hw, },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, imx7ulp_wdt_dt_ids);
|
||||
|
||||
@@ -178,7 +178,7 @@ static int se10_wdt_probe(struct platform_device *pdev)
|
||||
return -EBUSY;
|
||||
|
||||
chip_id = get_chipID();
|
||||
if (chip_id != 0x5632) {
|
||||
if (chip_id != 0x5632 && chip_id != 0x5652) {
|
||||
release_region(CFG_PORT, CFG_SIZE);
|
||||
return -ENODEV;
|
||||
}
|
||||
@@ -224,7 +224,7 @@ static struct platform_driver se10_wdt_driver = {
|
||||
.probe = se10_wdt_probe,
|
||||
};
|
||||
|
||||
static int se10_create_platform_device(const struct dmi_system_id *id)
|
||||
static int se10_create_platform_device(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
@@ -233,9 +233,10 @@ static int se10_create_platform_device(const struct dmi_system_id *id)
|
||||
return -ENOMEM;
|
||||
|
||||
err = platform_device_add(se10_pdev);
|
||||
if (err)
|
||||
if (err) {
|
||||
platform_device_put(se10_pdev);
|
||||
|
||||
se10_pdev = NULL;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -246,7 +247,6 @@ static const struct dmi_system_id se10_dmi_table[] __initconst = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "12NH"),
|
||||
},
|
||||
.callback = se10_create_platform_device,
|
||||
},
|
||||
{
|
||||
.ident = "LENOVO-SE10",
|
||||
@@ -254,7 +254,6 @@ static const struct dmi_system_id se10_dmi_table[] __initconst = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "12NJ"),
|
||||
},
|
||||
.callback = se10_create_platform_device,
|
||||
},
|
||||
{
|
||||
.ident = "LENOVO-SE10",
|
||||
@@ -262,7 +261,6 @@ static const struct dmi_system_id se10_dmi_table[] __initconst = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "12NK"),
|
||||
},
|
||||
.callback = se10_create_platform_device,
|
||||
},
|
||||
{
|
||||
.ident = "LENOVO-SE10",
|
||||
@@ -270,7 +268,6 @@ static const struct dmi_system_id se10_dmi_table[] __initconst = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "12NL"),
|
||||
},
|
||||
.callback = se10_create_platform_device,
|
||||
},
|
||||
{
|
||||
.ident = "LENOVO-SE10",
|
||||
@@ -278,7 +275,62 @@ static const struct dmi_system_id se10_dmi_table[] __initconst = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "12NM"),
|
||||
},
|
||||
.callback = se10_create_platform_device,
|
||||
},
|
||||
{
|
||||
.ident = "LENOVO-SE10-G2",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "13LJ"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.ident = "LENOVO-SE10-G2",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "13LK"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.ident = "LENOVO-SE10-G2",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "13S1"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.ident = "LENOVO-SE10-G2",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "13S2"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.ident = "LENOVO-SE10-G2",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "13S3"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.ident = "LENOVO-SE10-G2",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "13S4"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.ident = "LENOVO-SE10-G2",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "13S5"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.ident = "LENOVO-SE10-G2",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "13S6"),
|
||||
},
|
||||
},
|
||||
{}
|
||||
};
|
||||
@@ -286,10 +338,20 @@ MODULE_DEVICE_TABLE(dmi, se10_dmi_table);
|
||||
|
||||
static int __init se10_wdt_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (!dmi_check_system(se10_dmi_table))
|
||||
return -ENODEV;
|
||||
|
||||
return platform_driver_register(&se10_wdt_driver);
|
||||
err = platform_driver_register(&se10_wdt_driver);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = se10_create_platform_device();
|
||||
if (err)
|
||||
platform_driver_unregister(&se10_wdt_driver);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __exit se10_wdt_exit(void)
|
||||
|
||||
@@ -1,452 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* MachZ ZF-Logic Watchdog Timer driver for Linux
|
||||
*
|
||||
* The author does NOT admit liability nor provide warranty for
|
||||
* any of this software. This material is provided "AS-IS" in
|
||||
* the hope that it may be useful for others.
|
||||
*
|
||||
* Author: Fernando Fuganti <fuganti@conectiva.com.br>
|
||||
*
|
||||
* Based on sbc60xxwdt.c by Jakob Oestergaard
|
||||
*
|
||||
* We have two timers (wd#1, wd#2) driven by a 32 KHz clock with the
|
||||
* following periods:
|
||||
* wd#1 - 2 seconds;
|
||||
* wd#2 - 7.2 ms;
|
||||
* After the expiration of wd#1, it can generate a NMI, SCI, SMI, or
|
||||
* a system RESET and it starts wd#2 that unconditionally will RESET
|
||||
* the system when the counter reaches zero.
|
||||
*
|
||||
* 14-Dec-2001 Matt Domsch <Matt_Domsch@dell.com>
|
||||
* Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/watchdog.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
|
||||
/* ports */
|
||||
#define ZF_IOBASE 0x218
|
||||
#define INDEX 0x218
|
||||
#define DATA_B 0x219
|
||||
#define DATA_W 0x21A
|
||||
#define DATA_D 0x21A
|
||||
|
||||
/* indexes */ /* size */
|
||||
#define ZFL_VERSION 0x02 /* 16 */
|
||||
#define CONTROL 0x10 /* 16 */
|
||||
#define STATUS 0x12 /* 8 */
|
||||
#define COUNTER_1 0x0C /* 16 */
|
||||
#define COUNTER_2 0x0E /* 8 */
|
||||
#define PULSE_LEN 0x0F /* 8 */
|
||||
|
||||
/* controls */
|
||||
#define ENABLE_WD1 0x0001
|
||||
#define ENABLE_WD2 0x0002
|
||||
#define RESET_WD1 0x0010
|
||||
#define RESET_WD2 0x0020
|
||||
#define GEN_SCI 0x0100
|
||||
#define GEN_NMI 0x0200
|
||||
#define GEN_SMI 0x0400
|
||||
#define GEN_RESET 0x0800
|
||||
|
||||
|
||||
/* utilities */
|
||||
|
||||
#define WD1 0
|
||||
#define WD2 1
|
||||
|
||||
#define zf_writew(port, data) { outb(port, INDEX); outw(data, DATA_W); }
|
||||
#define zf_writeb(port, data) { outb(port, INDEX); outb(data, DATA_B); }
|
||||
#define zf_get_ZFL_version() zf_readw(ZFL_VERSION)
|
||||
|
||||
|
||||
static unsigned short zf_readw(unsigned char port)
|
||||
{
|
||||
outb(port, INDEX);
|
||||
return inw(DATA_W);
|
||||
}
|
||||
|
||||
|
||||
MODULE_AUTHOR("Fernando Fuganti <fuganti@conectiva.com.br>");
|
||||
MODULE_DESCRIPTION("MachZ ZF-Logic Watchdog driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
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) ")");
|
||||
|
||||
#define PFX "machzwd"
|
||||
|
||||
static const struct watchdog_info zf_info = {
|
||||
.options = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
|
||||
.firmware_version = 1,
|
||||
.identity = "ZF-Logic watchdog",
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* action refers to action taken when watchdog resets
|
||||
* 0 = GEN_RESET
|
||||
* 1 = GEN_SMI
|
||||
* 2 = GEN_NMI
|
||||
* 3 = GEN_SCI
|
||||
* defaults to GEN_RESET (0)
|
||||
*/
|
||||
static int action;
|
||||
module_param(action, int, 0);
|
||||
MODULE_PARM_DESC(action, "after watchdog resets, generate: "
|
||||
"0 = RESET(*) 1 = SMI 2 = NMI 3 = SCI");
|
||||
|
||||
static void zf_ping(struct timer_list *unused);
|
||||
|
||||
static int zf_action = GEN_RESET;
|
||||
static unsigned long zf_is_open;
|
||||
static char zf_expect_close;
|
||||
static DEFINE_SPINLOCK(zf_port_lock);
|
||||
static DEFINE_TIMER(zf_timer, zf_ping);
|
||||
static unsigned long next_heartbeat;
|
||||
|
||||
|
||||
/* timeout for user land heart beat (10 seconds) */
|
||||
#define ZF_USER_TIMEO (HZ*10)
|
||||
|
||||
/* timeout for hardware watchdog (~500ms) */
|
||||
#define ZF_HW_TIMEO (HZ/2)
|
||||
|
||||
/* number of ticks on WD#1 (driven by a 32KHz clock, 2s) */
|
||||
#define ZF_CTIMEOUT 0xffff
|
||||
|
||||
#ifndef ZF_DEBUG
|
||||
#define dprintk(format, args...)
|
||||
#else
|
||||
#define dprintk(format, args...) \
|
||||
pr_debug(":%s:%d: " format, __func__, __LINE__ , ## args)
|
||||
#endif
|
||||
|
||||
|
||||
static inline void zf_set_status(unsigned char new)
|
||||
{
|
||||
zf_writeb(STATUS, new);
|
||||
}
|
||||
|
||||
|
||||
/* CONTROL register functions */
|
||||
|
||||
static inline unsigned short zf_get_control(void)
|
||||
{
|
||||
return zf_readw(CONTROL);
|
||||
}
|
||||
|
||||
static inline void zf_set_control(unsigned short new)
|
||||
{
|
||||
zf_writew(CONTROL, new);
|
||||
}
|
||||
|
||||
|
||||
/* WD#? counter functions */
|
||||
/*
|
||||
* Just set counter value
|
||||
*/
|
||||
|
||||
static inline void zf_set_timer(unsigned short new, unsigned char n)
|
||||
{
|
||||
switch (n) {
|
||||
case WD1:
|
||||
zf_writew(COUNTER_1, new);
|
||||
fallthrough;
|
||||
case WD2:
|
||||
zf_writeb(COUNTER_2, new > 0xff ? 0xff : new);
|
||||
fallthrough;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* stop hardware timer
|
||||
*/
|
||||
static void zf_timer_off(void)
|
||||
{
|
||||
unsigned int ctrl_reg = 0;
|
||||
unsigned long flags;
|
||||
|
||||
/* stop internal ping */
|
||||
timer_delete_sync(&zf_timer);
|
||||
|
||||
spin_lock_irqsave(&zf_port_lock, flags);
|
||||
/* stop watchdog timer */
|
||||
ctrl_reg = zf_get_control();
|
||||
ctrl_reg |= (ENABLE_WD1|ENABLE_WD2); /* disable wd1 and wd2 */
|
||||
ctrl_reg &= ~(ENABLE_WD1|ENABLE_WD2);
|
||||
zf_set_control(ctrl_reg);
|
||||
spin_unlock_irqrestore(&zf_port_lock, flags);
|
||||
|
||||
pr_info("Watchdog timer is now disabled\n");
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* start hardware timer
|
||||
*/
|
||||
static void zf_timer_on(void)
|
||||
{
|
||||
unsigned int ctrl_reg = 0;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&zf_port_lock, flags);
|
||||
|
||||
zf_writeb(PULSE_LEN, 0xff);
|
||||
|
||||
zf_set_timer(ZF_CTIMEOUT, WD1);
|
||||
|
||||
/* user land ping */
|
||||
next_heartbeat = jiffies + ZF_USER_TIMEO;
|
||||
|
||||
/* start the timer for internal ping */
|
||||
mod_timer(&zf_timer, jiffies + ZF_HW_TIMEO);
|
||||
|
||||
/* start watchdog timer */
|
||||
ctrl_reg = zf_get_control();
|
||||
ctrl_reg |= (ENABLE_WD1|zf_action);
|
||||
zf_set_control(ctrl_reg);
|
||||
spin_unlock_irqrestore(&zf_port_lock, flags);
|
||||
|
||||
pr_info("Watchdog timer is now enabled\n");
|
||||
}
|
||||
|
||||
|
||||
static void zf_ping(struct timer_list *unused)
|
||||
{
|
||||
unsigned int ctrl_reg = 0;
|
||||
unsigned long flags;
|
||||
|
||||
zf_writeb(COUNTER_2, 0xff);
|
||||
|
||||
if (time_before(jiffies, next_heartbeat)) {
|
||||
dprintk("time_before: %ld\n", next_heartbeat - jiffies);
|
||||
/*
|
||||
* reset event is activated by transition from 0 to 1 on
|
||||
* RESET_WD1 bit and we assume that it is already zero...
|
||||
*/
|
||||
|
||||
spin_lock_irqsave(&zf_port_lock, flags);
|
||||
ctrl_reg = zf_get_control();
|
||||
ctrl_reg |= RESET_WD1;
|
||||
zf_set_control(ctrl_reg);
|
||||
|
||||
/* ...and nothing changes until here */
|
||||
ctrl_reg &= ~(RESET_WD1);
|
||||
zf_set_control(ctrl_reg);
|
||||
spin_unlock_irqrestore(&zf_port_lock, flags);
|
||||
|
||||
mod_timer(&zf_timer, jiffies + ZF_HW_TIMEO);
|
||||
} else
|
||||
pr_crit("I will reset your machine\n");
|
||||
}
|
||||
|
||||
static ssize_t zf_write(struct file *file, const char __user *buf, size_t count,
|
||||
loff_t *ppos)
|
||||
{
|
||||
/* See if we got the magic character */
|
||||
if (count) {
|
||||
/*
|
||||
* no need to check for close confirmation
|
||||
* no way to disable watchdog ;)
|
||||
*/
|
||||
if (!nowayout) {
|
||||
size_t ofs;
|
||||
/*
|
||||
* note: just in case someone wrote the magic character
|
||||
* five months ago...
|
||||
*/
|
||||
zf_expect_close = 0;
|
||||
|
||||
/* now scan */
|
||||
for (ofs = 0; ofs != count; ofs++) {
|
||||
char c;
|
||||
if (get_user(c, buf + ofs))
|
||||
return -EFAULT;
|
||||
if (c == 'V') {
|
||||
zf_expect_close = 42;
|
||||
dprintk("zf_expect_close = 42\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Well, anyhow someone wrote to us,
|
||||
* we should return that favour
|
||||
*/
|
||||
next_heartbeat = jiffies + ZF_USER_TIMEO;
|
||||
dprintk("user ping at %ld\n", jiffies);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
static long zf_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
void __user *argp = (void __user *)arg;
|
||||
int __user *p = argp;
|
||||
switch (cmd) {
|
||||
case WDIOC_GETSUPPORT:
|
||||
if (copy_to_user(argp, &zf_info, sizeof(zf_info)))
|
||||
return -EFAULT;
|
||||
break;
|
||||
case WDIOC_GETSTATUS:
|
||||
case WDIOC_GETBOOTSTATUS:
|
||||
return put_user(0, p);
|
||||
case WDIOC_KEEPALIVE:
|
||||
zf_ping(NULL);
|
||||
break;
|
||||
default:
|
||||
return -ENOTTY;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int zf_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
if (test_and_set_bit(0, &zf_is_open))
|
||||
return -EBUSY;
|
||||
if (nowayout)
|
||||
__module_get(THIS_MODULE);
|
||||
zf_timer_on();
|
||||
return stream_open(inode, file);
|
||||
}
|
||||
|
||||
static int zf_close(struct inode *inode, struct file *file)
|
||||
{
|
||||
if (zf_expect_close == 42)
|
||||
zf_timer_off();
|
||||
else {
|
||||
timer_delete(&zf_timer);
|
||||
pr_err("device file closed unexpectedly. Will not stop the WDT!\n");
|
||||
}
|
||||
clear_bit(0, &zf_is_open);
|
||||
zf_expect_close = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Notifier for system down
|
||||
*/
|
||||
|
||||
static int zf_notify_sys(struct notifier_block *this, unsigned long code,
|
||||
void *unused)
|
||||
{
|
||||
if (code == SYS_DOWN || code == SYS_HALT)
|
||||
zf_timer_off();
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static const struct file_operations zf_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.write = zf_write,
|
||||
.unlocked_ioctl = zf_ioctl,
|
||||
.compat_ioctl = compat_ptr_ioctl,
|
||||
.open = zf_open,
|
||||
.release = zf_close,
|
||||
};
|
||||
|
||||
static struct miscdevice zf_miscdev = {
|
||||
.minor = WATCHDOG_MINOR,
|
||||
.name = "watchdog",
|
||||
.fops = &zf_fops,
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* The device needs to learn about soft shutdowns in order to
|
||||
* turn the timebomb registers off.
|
||||
*/
|
||||
static struct notifier_block zf_notifier = {
|
||||
.notifier_call = zf_notify_sys,
|
||||
};
|
||||
|
||||
static void __init zf_show_action(int act)
|
||||
{
|
||||
static const char * const str[] = { "RESET", "SMI", "NMI", "SCI" };
|
||||
|
||||
pr_info("Watchdog using action = %s\n", str[act]);
|
||||
}
|
||||
|
||||
static int __init zf_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
pr_info("MachZ ZF-Logic Watchdog driver initializing\n");
|
||||
|
||||
ret = zf_get_ZFL_version();
|
||||
if (!ret || ret == 0xffff) {
|
||||
pr_warn("no ZF-Logic found\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (action <= 3 && action >= 0)
|
||||
zf_action = zf_action >> action;
|
||||
else
|
||||
action = 0;
|
||||
|
||||
zf_show_action(action);
|
||||
|
||||
if (!request_region(ZF_IOBASE, 3, "MachZ ZFL WDT")) {
|
||||
pr_err("cannot reserve I/O ports at %d\n", ZF_IOBASE);
|
||||
ret = -EBUSY;
|
||||
goto no_region;
|
||||
}
|
||||
|
||||
ret = register_reboot_notifier(&zf_notifier);
|
||||
if (ret) {
|
||||
pr_err("can't register reboot notifier (err=%d)\n", ret);
|
||||
goto no_reboot;
|
||||
}
|
||||
|
||||
ret = misc_register(&zf_miscdev);
|
||||
if (ret) {
|
||||
pr_err("can't misc_register on minor=%d\n", WATCHDOG_MINOR);
|
||||
goto no_misc;
|
||||
}
|
||||
|
||||
zf_set_status(0);
|
||||
zf_set_control(0);
|
||||
|
||||
return 0;
|
||||
|
||||
no_misc:
|
||||
unregister_reboot_notifier(&zf_notifier);
|
||||
no_reboot:
|
||||
release_region(ZF_IOBASE, 3);
|
||||
no_region:
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static void __exit zf_exit(void)
|
||||
{
|
||||
zf_timer_off();
|
||||
|
||||
misc_deregister(&zf_miscdev);
|
||||
unregister_reboot_notifier(&zf_notifier);
|
||||
release_region(ZF_IOBASE, 3);
|
||||
}
|
||||
|
||||
module_init(zf_init);
|
||||
module_exit(zf_exit);
|
||||
@@ -163,5 +163,4 @@ module_mcb_driver(men_z069_driver);
|
||||
MODULE_AUTHOR("Johannes Thumshirn <jth@kernel.org>");
|
||||
MODULE_DESCRIPTION("Watchdog driver for the MEN z069 IP-Core");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("mcb:16z069");
|
||||
MODULE_IMPORT_NS("MCB");
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/watchdog.h>
|
||||
|
||||
@@ -42,6 +43,7 @@ struct qcom_wdt_match_data {
|
||||
const u32 *offset;
|
||||
bool pretimeout;
|
||||
u32 max_tick_count;
|
||||
u32 wdt_reason_val;
|
||||
};
|
||||
|
||||
struct qcom_wdt {
|
||||
@@ -185,6 +187,7 @@ static const struct qcom_wdt_match_data match_data_ipq5424 = {
|
||||
.offset = reg_offset_data_kpss,
|
||||
.pretimeout = true,
|
||||
.max_tick_count = 0xFFFFFU,
|
||||
.wdt_reason_val = 5,
|
||||
};
|
||||
|
||||
static const struct qcom_wdt_match_data match_data_kpss = {
|
||||
@@ -193,6 +196,40 @@ static const struct qcom_wdt_match_data match_data_kpss = {
|
||||
.max_tick_count = 0xFFFFFU,
|
||||
};
|
||||
|
||||
static int qcom_wdt_get_bootstatus(struct device *dev, struct qcom_wdt *wdt,
|
||||
u32 val)
|
||||
{
|
||||
struct device_node *imem;
|
||||
struct resource res;
|
||||
void __iomem *addr;
|
||||
int ret;
|
||||
|
||||
imem = of_parse_phandle(dev->of_node, "sram", 0);
|
||||
if (!imem) {
|
||||
/* Read the EXPIRED_STATUS bit as a fallback */
|
||||
if (readl(wdt_addr(wdt, WDT_STS)) & 1)
|
||||
wdt->wdd.bootstatus = WDIOF_CARDRESET;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = of_address_to_resource(imem, 0, &res);
|
||||
of_node_put(imem);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
addr = ioremap(res.start, resource_size(&res));
|
||||
if (!addr)
|
||||
return -ENOMEM;
|
||||
|
||||
if (readl(addr) == val)
|
||||
wdt->wdd.bootstatus = WDIOF_CARDRESET;
|
||||
|
||||
iounmap(addr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcom_wdt_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
@@ -273,8 +310,9 @@ static int qcom_wdt_probe(struct platform_device *pdev)
|
||||
wdt->wdd.parent = dev;
|
||||
wdt->layout = data->offset;
|
||||
|
||||
if (readl(wdt_addr(wdt, WDT_STS)) & 1)
|
||||
wdt->wdd.bootstatus = WDIOF_CARDRESET;
|
||||
ret = qcom_wdt_get_bootstatus(dev, wdt, data->wdt_reason_val);
|
||||
if (ret)
|
||||
dev_err(dev, "failed to get the bootstatus, %d\n", ret);
|
||||
|
||||
/*
|
||||
* If 'timeout-sec' unspecified in devicetree, assume a 30 second
|
||||
|
||||
@@ -114,12 +114,6 @@ static int otto_wdt_tick_ms(struct otto_wdt_ctrl *ctrl, int prescale)
|
||||
* the value stored in those fields. This means each phase will run for at least
|
||||
* one tick, so small values need to be clamped to correctly reflect the timeout.
|
||||
*/
|
||||
static inline unsigned int div_round_ticks(unsigned int val, unsigned int tick_duration,
|
||||
unsigned int min_ticks)
|
||||
{
|
||||
return max(min_ticks, DIV_ROUND_UP(val, tick_duration));
|
||||
}
|
||||
|
||||
static int otto_wdt_determine_timeouts(struct watchdog_device *wdev, unsigned int timeout,
|
||||
unsigned int pretimeout)
|
||||
{
|
||||
@@ -140,9 +134,9 @@ static int otto_wdt_determine_timeouts(struct watchdog_device *wdev, unsigned in
|
||||
return -EINVAL;
|
||||
|
||||
tick_ms = otto_wdt_tick_ms(ctrl, prescale);
|
||||
total_ticks = div_round_ticks(timeout_ms, tick_ms, 2);
|
||||
phase1_ticks = div_round_ticks(timeout_ms - pretimeout_ms, tick_ms, 1);
|
||||
phase2_ticks = total_ticks - phase1_ticks;
|
||||
total_ticks = max(2, DIV_ROUND_UP(timeout_ms, tick_ms));
|
||||
phase2_ticks = max(1, pretimeout_ms / tick_ms);
|
||||
phase1_ticks = total_ticks - phase2_ticks;
|
||||
|
||||
prescale_next++;
|
||||
} while (phase1_ticks > OTTO_WDT_PHASE_TICKS_MAX
|
||||
@@ -302,15 +296,15 @@ static int otto_wdt_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(ctrl->base))
|
||||
return PTR_ERR(ctrl->base);
|
||||
|
||||
ret = otto_wdt_probe_clk(ctrl);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Clear any old interrupts and reset initial state */
|
||||
iowrite32(OTTO_WDT_INTR_PHASE_1 | OTTO_WDT_INTR_PHASE_2,
|
||||
ctrl->base + OTTO_WDT_REG_INTR);
|
||||
iowrite32(OTTO_WDT_CTRL_DEFAULT, ctrl->base + OTTO_WDT_REG_CTRL);
|
||||
|
||||
ret = otto_wdt_probe_clk(ctrl);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ctrl->irq_phase1 = platform_get_irq_byname(pdev, "phase1");
|
||||
if (ctrl->irq_phase1 < 0)
|
||||
return ctrl->irq_phase1;
|
||||
|
||||
@@ -35,7 +35,8 @@
|
||||
#define RTIWWDRXCTRL 0xa4
|
||||
#define RTIWWDSIZECTRL 0xa8
|
||||
|
||||
#define RTIWWDRX_NMI 0xa
|
||||
#define RTIWWDRXN_RST 0x5
|
||||
#define RTIWWDRXN_NMI 0xa
|
||||
|
||||
#define RTIWWDSIZE_50P 0x50
|
||||
#define RTIWWDSIZE_25P 0x500
|
||||
@@ -63,22 +64,29 @@
|
||||
|
||||
static int heartbeat;
|
||||
|
||||
struct rti_wdt_data {
|
||||
bool nmi;
|
||||
};
|
||||
|
||||
/*
|
||||
* struct to hold data for each WDT device
|
||||
* @base - base io address of WD device
|
||||
* @freq - source clock frequency of WDT
|
||||
* @wdd - hold watchdog device as is in WDT core
|
||||
* @nmi - Set if this WDT instance supports generating NMI
|
||||
*/
|
||||
struct rti_wdt_device {
|
||||
void __iomem *base;
|
||||
unsigned long freq;
|
||||
struct watchdog_device wdd;
|
||||
bool nmi;
|
||||
};
|
||||
|
||||
static int rti_wdt_start(struct watchdog_device *wdd)
|
||||
{
|
||||
u32 timer_margin;
|
||||
struct rti_wdt_device *wdt = watchdog_get_drvdata(wdd);
|
||||
u8 reaction;
|
||||
int ret;
|
||||
|
||||
ret = pm_runtime_resume_and_get(wdd->parent);
|
||||
@@ -101,8 +109,13 @@ static int rti_wdt_start(struct watchdog_device *wdd)
|
||||
*/
|
||||
wdd->min_hw_heartbeat_ms = 520 * wdd->timeout + MAX_HW_ERROR;
|
||||
|
||||
/* Generate NMI when wdt expires */
|
||||
writel_relaxed(RTIWWDRX_NMI, wdt->base + RTIWWDRXCTRL);
|
||||
/* When WDT expires, generate NMI or reset if NMI not supported */
|
||||
if (wdt->nmi)
|
||||
reaction = RTIWWDRXN_NMI;
|
||||
else
|
||||
reaction = RTIWWDRXN_RST;
|
||||
|
||||
writel_relaxed(reaction, wdt->base + RTIWWDRXCTRL);
|
||||
|
||||
/* Open window size 50%; this is the largest window size available */
|
||||
writel_relaxed(RTIWWDSIZE_50P, wdt->base + RTIWWDSIZECTRL);
|
||||
@@ -210,6 +223,7 @@ static int rti_wdt_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret = 0;
|
||||
struct device *dev = &pdev->dev;
|
||||
const struct rti_wdt_data *data;
|
||||
struct watchdog_device *wdd;
|
||||
struct rti_wdt_device *wdt;
|
||||
struct clk *clk;
|
||||
@@ -254,6 +268,14 @@ static int rti_wdt_probe(struct platform_device *pdev)
|
||||
wdd->timeout = DEFAULT_HEARTBEAT;
|
||||
wdd->parent = dev;
|
||||
|
||||
data = device_get_match_data(dev);
|
||||
if (!data) {
|
||||
ret = -ENODEV;
|
||||
goto err_iomap;
|
||||
}
|
||||
|
||||
wdt->nmi = data->nmi;
|
||||
|
||||
watchdog_set_drvdata(wdd, wdt);
|
||||
watchdog_set_nowayout(wdd, 1);
|
||||
watchdog_set_restart_priority(wdd, 128);
|
||||
@@ -361,8 +383,17 @@ static void rti_wdt_remove(struct platform_device *pdev)
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
}
|
||||
|
||||
static const struct rti_wdt_data rti_wdt_j7_data = {
|
||||
.nmi = true,
|
||||
};
|
||||
|
||||
static const struct rti_wdt_data rti_wdt_am62l_data = {
|
||||
.nmi = false,
|
||||
};
|
||||
|
||||
static const struct of_device_id rti_wdt_of_match[] = {
|
||||
{ .compatible = "ti,j7-rti-wdt", },
|
||||
{ .compatible = "ti,j7-rti-wdt", .data = &rti_wdt_j7_data },
|
||||
{ .compatible = "ti,am62l-rti-wdt", .data = &rti_wdt_am62l_data },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rti_wdt_of_match);
|
||||
|
||||
@@ -79,14 +79,6 @@ static int rzn1_wdt_start(struct watchdog_device *w)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t rzn1_wdt_irq(int irq, void *_wdt)
|
||||
{
|
||||
pr_crit("RZN1 Watchdog. Initiating system reboot\n");
|
||||
emergency_restart();
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static struct watchdog_info rzn1_wdt_info = {
|
||||
.identity = "RZ/N1 Watchdog",
|
||||
.options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
|
||||
@@ -102,11 +94,9 @@ static int rzn1_wdt_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct rzn1_watchdog *wdt;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct clk *clk;
|
||||
unsigned long clk_rate;
|
||||
struct clk *clk;
|
||||
int ret;
|
||||
int irq;
|
||||
|
||||
wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
|
||||
if (!wdt)
|
||||
@@ -116,28 +106,13 @@ static int rzn1_wdt_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(wdt->base))
|
||||
return PTR_ERR(wdt->base);
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
ret = devm_request_irq(dev, irq, rzn1_wdt_irq, 0,
|
||||
np->name, wdt);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to request irq %d\n", irq);
|
||||
return ret;
|
||||
}
|
||||
|
||||
clk = devm_clk_get_enabled(dev, NULL);
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(dev, "failed to get the clock\n");
|
||||
return PTR_ERR(clk);
|
||||
}
|
||||
if (IS_ERR(clk))
|
||||
return dev_err_probe(dev, PTR_ERR(clk), "failed to get the clock\n");
|
||||
|
||||
clk_rate = clk_get_rate(clk);
|
||||
if (!clk_rate) {
|
||||
dev_err(dev, "failed to get the clock rate\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!clk_rate)
|
||||
return dev_err_probe(dev, -EINVAL, "failed to get the clock rate\n");
|
||||
|
||||
wdt->clk_rate_khz = clk_rate / 1000;
|
||||
wdt->wdtdev.info = &rzn1_wdt_info;
|
||||
|
||||
@@ -30,6 +30,7 @@ struct sama5d4_wdt {
|
||||
void __iomem *reg_base;
|
||||
u32 mr;
|
||||
u32 ir;
|
||||
u32 wddis_mask;
|
||||
unsigned long last_ping;
|
||||
bool need_irq;
|
||||
bool sam9x60_support;
|
||||
@@ -48,7 +49,10 @@ MODULE_PARM_DESC(nowayout,
|
||||
"Watchdog cannot be stopped once started (default="
|
||||
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
|
||||
|
||||
#define wdt_enabled (!(wdt->mr & AT91_WDT_WDDIS))
|
||||
static inline bool wdt_enabled(struct sama5d4_wdt *wdt)
|
||||
{
|
||||
return !(wdt->mr & wdt->wddis_mask);
|
||||
}
|
||||
|
||||
#define wdt_read(wdt, field) \
|
||||
readl_relaxed((wdt)->reg_base + (field))
|
||||
@@ -81,12 +85,9 @@ static int sama5d4_wdt_start(struct watchdog_device *wdd)
|
||||
{
|
||||
struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd);
|
||||
|
||||
if (wdt->sam9x60_support) {
|
||||
if (wdt->sam9x60_support)
|
||||
writel_relaxed(wdt->ir, wdt->reg_base + AT91_SAM9X60_IER);
|
||||
wdt->mr &= ~AT91_SAM9X60_WDDIS;
|
||||
} else {
|
||||
wdt->mr &= ~AT91_WDT_WDDIS;
|
||||
}
|
||||
wdt->mr &= ~wdt->wddis_mask;
|
||||
wdt_write(wdt, AT91_WDT_MR, wdt->mr);
|
||||
|
||||
return 0;
|
||||
@@ -96,12 +97,9 @@ static int sama5d4_wdt_stop(struct watchdog_device *wdd)
|
||||
{
|
||||
struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd);
|
||||
|
||||
if (wdt->sam9x60_support) {
|
||||
if (wdt->sam9x60_support)
|
||||
writel_relaxed(wdt->ir, wdt->reg_base + AT91_SAM9X60_IDR);
|
||||
wdt->mr |= AT91_SAM9X60_WDDIS;
|
||||
} else {
|
||||
wdt->mr |= AT91_WDT_WDDIS;
|
||||
}
|
||||
wdt->mr |= wdt->wddis_mask;
|
||||
wdt_write(wdt, AT91_WDT_MR, wdt->mr);
|
||||
|
||||
return 0;
|
||||
@@ -117,7 +115,7 @@ static int sama5d4_wdt_ping(struct watchdog_device *wdd)
|
||||
}
|
||||
|
||||
static int sama5d4_wdt_set_timeout(struct watchdog_device *wdd,
|
||||
unsigned int timeout)
|
||||
unsigned int timeout)
|
||||
{
|
||||
struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd);
|
||||
u32 value = WDT_SEC2TICKS(timeout);
|
||||
@@ -140,8 +138,8 @@ static int sama5d4_wdt_set_timeout(struct watchdog_device *wdd,
|
||||
* If the watchdog is enabled, then the timeout can be updated. Else,
|
||||
* wait that the user enables it.
|
||||
*/
|
||||
if (wdt_enabled)
|
||||
wdt_write(wdt, AT91_WDT_MR, wdt->mr & ~AT91_WDT_WDDIS);
|
||||
if (wdt_enabled(wdt))
|
||||
wdt_write(wdt, AT91_WDT_MR, wdt->mr & ~wdt->wddis_mask);
|
||||
|
||||
wdd->timeout = timeout;
|
||||
|
||||
@@ -184,10 +182,7 @@ static int of_sama5d4_wdt_init(struct device_node *np, struct sama5d4_wdt *wdt)
|
||||
{
|
||||
const char *tmp;
|
||||
|
||||
if (wdt->sam9x60_support)
|
||||
wdt->mr = AT91_SAM9X60_WDDIS;
|
||||
else
|
||||
wdt->mr = AT91_WDT_WDDIS;
|
||||
wdt->mr = wdt->wddis_mask;
|
||||
|
||||
if (!of_property_read_string(np, "atmel,watchdog-type", &tmp) &&
|
||||
!strcmp(tmp, "software"))
|
||||
@@ -213,15 +208,11 @@ static int sama5d4_wdt_init(struct sama5d4_wdt *wdt)
|
||||
* If the watchdog is already running, we can safely update it.
|
||||
* Else, we have to disable it properly.
|
||||
*/
|
||||
if (!wdt_enabled) {
|
||||
if (!wdt_enabled(wdt)) {
|
||||
reg = wdt_read(wdt, AT91_WDT_MR);
|
||||
if (wdt->sam9x60_support && (!(reg & AT91_SAM9X60_WDDIS)))
|
||||
if (!(reg & wdt->wddis_mask))
|
||||
wdt_write_nosleep(wdt, AT91_WDT_MR,
|
||||
reg | AT91_SAM9X60_WDDIS);
|
||||
else if (!wdt->sam9x60_support &&
|
||||
(!(reg & AT91_WDT_WDDIS)))
|
||||
wdt_write_nosleep(wdt, AT91_WDT_MR,
|
||||
reg | AT91_WDT_WDDIS);
|
||||
reg | wdt->wddis_mask);
|
||||
}
|
||||
|
||||
if (wdt->sam9x60_support) {
|
||||
@@ -273,6 +264,9 @@ static int sama5d4_wdt_probe(struct platform_device *pdev)
|
||||
of_device_is_compatible(dev->of_node, "microchip,sama7g5-wdt"))
|
||||
wdt->sam9x60_support = true;
|
||||
|
||||
wdt->wddis_mask = wdt->sam9x60_support ? AT91_SAM9X60_WDDIS
|
||||
: AT91_WDT_WDDIS;
|
||||
|
||||
watchdog_set_drvdata(wdd, wdt);
|
||||
|
||||
regs = devm_platform_ioremap_resource(pdev, 0);
|
||||
@@ -306,8 +300,8 @@ static int sama5d4_wdt_probe(struct platform_device *pdev)
|
||||
watchdog_init_timeout(wdd, wdt_timeout, dev);
|
||||
|
||||
reg = wdt_read(wdt, AT91_WDT_MR);
|
||||
if (!(reg & AT91_WDT_WDDIS)) {
|
||||
wdt->mr &= ~AT91_WDT_WDDIS;
|
||||
if (!(reg & wdt->wddis_mask)) {
|
||||
wdt->mr &= ~wdt->wddis_mask;
|
||||
set_bit(WDOG_HW_RUNNING, &wdd->status);
|
||||
}
|
||||
|
||||
|
||||
@@ -339,8 +339,8 @@ static int __init sc1200wdt_probe(void)
|
||||
|
||||
static const struct pnp_device_id scl200wdt_pnp_devices[] = {
|
||||
/* National Semiconductor PC87307/PC97307 watchdog component */
|
||||
{.id = "NSC0800", .driver_data = 0},
|
||||
{.id = ""},
|
||||
{ .id = "NSC0800" },
|
||||
{ }
|
||||
};
|
||||
|
||||
static int scl200wdt_pnp_probe(struct pnp_dev *dev,
|
||||
|
||||
@@ -1,430 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* AMD Elan SC520 processor Watchdog Timer driver
|
||||
*
|
||||
* Based on acquirewdt.c by Alan Cox,
|
||||
* and sbc60xxwdt.c by Jakob Oestergaard <jakob@unthought.net>
|
||||
*
|
||||
* The authors do NOT admit liability nor provide warranty for
|
||||
* any of this software. This material is provided "AS-IS" in
|
||||
* the hope that it may be useful for others.
|
||||
*
|
||||
* (c) Copyright 2001 Scott Jennings <linuxdrivers@oro.net>
|
||||
* 9/27 - 2001 [Initial release]
|
||||
*
|
||||
* Additional fixes Alan Cox
|
||||
* - Fixed formatting
|
||||
* - Removed debug printks
|
||||
* - Fixed SMP built kernel deadlock
|
||||
* - Switched to private locks not lock_kernel
|
||||
* - Used ioremap/writew/readw
|
||||
* - Added NOWAYOUT support
|
||||
* 4/12 - 2002 Changes by Rob Radez <rob@osinvestor.com>
|
||||
* - Change comments
|
||||
* - Eliminate fop_llseek
|
||||
* - Change CONFIG_WATCHDOG_NOWAYOUT semantics
|
||||
* - Add KERN_* tags to printks
|
||||
* - fix possible wdt_is_open race
|
||||
* - Report proper capabilities in watchdog_info
|
||||
* - Add WDIOC_{GETSTATUS, GETBOOTSTATUS, SETTIMEOUT,
|
||||
* GETTIMEOUT, SETOPTIONS} ioctls
|
||||
* 09/8 - 2003 Changes by Wim Van Sebroeck <wim@iguana.be>
|
||||
* - cleanup of trailing spaces
|
||||
* - added extra printk's for startup problems
|
||||
* - use module_param
|
||||
* - made timeout (the emulated heartbeat) a module_param
|
||||
* - made the keepalive ping an internal subroutine
|
||||
* 3/27 - 2004 Changes by Sean Young <sean@mess.org>
|
||||
* - set MMCR_BASE to 0xfffef000
|
||||
* - CBAR does not need to be read
|
||||
* - removed debugging printks
|
||||
*
|
||||
* This WDT driver is different from most other Linux WDT
|
||||
* drivers in that the driver will ping the watchdog by itself,
|
||||
* because this particular WDT has a very short timeout (1.6
|
||||
* seconds) and it would be insane to count on any userspace
|
||||
* daemon always getting scheduled within that time frame.
|
||||
*
|
||||
* This driver uses memory mapped IO, and spinlock.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/watchdog.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
|
||||
/*
|
||||
* The AMD Elan SC520 timeout value is 492us times a power of 2 (0-7)
|
||||
*
|
||||
* 0: 492us 2: 1.01s 4: 4.03s 6: 16.22s
|
||||
* 1: 503ms 3: 2.01s 5: 8.05s 7: 32.21s
|
||||
*
|
||||
* We will program the SC520 watchdog for a timeout of 2.01s.
|
||||
* If we reset the watchdog every ~250ms we should be safe.
|
||||
*/
|
||||
|
||||
#define WDT_INTERVAL (HZ/4+1)
|
||||
|
||||
/*
|
||||
* We must not require too good response from the userspace daemon.
|
||||
* Here we require the userspace daemon to send us a heartbeat
|
||||
* char to /dev/watchdog every 30 seconds.
|
||||
*/
|
||||
|
||||
#define WATCHDOG_TIMEOUT 30 /* 30 sec default timeout */
|
||||
/* in seconds, will be multiplied by HZ to get seconds to wait for a ping */
|
||||
static int timeout = WATCHDOG_TIMEOUT;
|
||||
module_param(timeout, int, 0);
|
||||
MODULE_PARM_DESC(timeout,
|
||||
"Watchdog timeout in seconds. (1 <= timeout <= 3600, default="
|
||||
__MODULE_STRING(WATCHDOG_TIMEOUT) ")");
|
||||
|
||||
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) ")");
|
||||
|
||||
/*
|
||||
* AMD Elan SC520 - Watchdog Timer Registers
|
||||
*/
|
||||
#define MMCR_BASE 0xfffef000 /* The default base address */
|
||||
#define OFFS_WDTMRCTL 0xCB0 /* Watchdog Timer Control Register */
|
||||
|
||||
/* WDT Control Register bit definitions */
|
||||
#define WDT_EXP_SEL_01 0x0001 /* [01] Time-out = 496 us (with 33 Mhz clk). */
|
||||
#define WDT_EXP_SEL_02 0x0002 /* [02] Time-out = 508 ms (with 33 Mhz clk). */
|
||||
#define WDT_EXP_SEL_03 0x0004 /* [03] Time-out = 1.02 s (with 33 Mhz clk). */
|
||||
#define WDT_EXP_SEL_04 0x0008 /* [04] Time-out = 2.03 s (with 33 Mhz clk). */
|
||||
#define WDT_EXP_SEL_05 0x0010 /* [05] Time-out = 4.07 s (with 33 Mhz clk). */
|
||||
#define WDT_EXP_SEL_06 0x0020 /* [06] Time-out = 8.13 s (with 33 Mhz clk). */
|
||||
#define WDT_EXP_SEL_07 0x0040 /* [07] Time-out = 16.27s (with 33 Mhz clk). */
|
||||
#define WDT_EXP_SEL_08 0x0080 /* [08] Time-out = 32.54s (with 33 Mhz clk). */
|
||||
#define WDT_IRQ_FLG 0x1000 /* [12] Interrupt Request Flag */
|
||||
#define WDT_WRST_ENB 0x4000 /* [14] Watchdog Timer Reset Enable */
|
||||
#define WDT_ENB 0x8000 /* [15] Watchdog Timer Enable */
|
||||
|
||||
static __u16 __iomem *wdtmrctl;
|
||||
|
||||
static void wdt_timer_ping(struct timer_list *);
|
||||
static DEFINE_TIMER(timer, wdt_timer_ping);
|
||||
static unsigned long next_heartbeat;
|
||||
static unsigned long wdt_is_open;
|
||||
static char wdt_expect_close;
|
||||
static DEFINE_SPINLOCK(wdt_spinlock);
|
||||
|
||||
/*
|
||||
* Whack the dog
|
||||
*/
|
||||
|
||||
static void wdt_timer_ping(struct timer_list *unused)
|
||||
{
|
||||
/* If we got a heartbeat pulse within the WDT_US_INTERVAL
|
||||
* we agree to ping the WDT
|
||||
*/
|
||||
if (time_before(jiffies, next_heartbeat)) {
|
||||
/* Ping the WDT */
|
||||
spin_lock(&wdt_spinlock);
|
||||
writew(0xAAAA, wdtmrctl);
|
||||
writew(0x5555, wdtmrctl);
|
||||
spin_unlock(&wdt_spinlock);
|
||||
|
||||
/* Re-set the timer interval */
|
||||
mod_timer(&timer, jiffies + WDT_INTERVAL);
|
||||
} else
|
||||
pr_warn("Heartbeat lost! Will not ping the watchdog\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Utility routines
|
||||
*/
|
||||
|
||||
static void wdt_config(int writeval)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
/* buy some time (ping) */
|
||||
spin_lock_irqsave(&wdt_spinlock, flags);
|
||||
readw(wdtmrctl); /* ensure write synchronization */
|
||||
writew(0xAAAA, wdtmrctl);
|
||||
writew(0x5555, wdtmrctl);
|
||||
/* unlock WDT = make WDT configuration register writable one time */
|
||||
writew(0x3333, wdtmrctl);
|
||||
writew(0xCCCC, wdtmrctl);
|
||||
/* write WDT configuration register */
|
||||
writew(writeval, wdtmrctl);
|
||||
spin_unlock_irqrestore(&wdt_spinlock, flags);
|
||||
}
|
||||
|
||||
static int wdt_startup(void)
|
||||
{
|
||||
next_heartbeat = jiffies + (timeout * HZ);
|
||||
|
||||
/* Start the timer */
|
||||
mod_timer(&timer, jiffies + WDT_INTERVAL);
|
||||
|
||||
/* Start the watchdog */
|
||||
wdt_config(WDT_ENB | WDT_WRST_ENB | WDT_EXP_SEL_04);
|
||||
|
||||
pr_info("Watchdog timer is now enabled\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wdt_turnoff(void)
|
||||
{
|
||||
/* Stop the timer */
|
||||
timer_delete_sync(&timer);
|
||||
|
||||
/* Stop the watchdog */
|
||||
wdt_config(0);
|
||||
|
||||
pr_info("Watchdog timer is now disabled...\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wdt_keepalive(void)
|
||||
{
|
||||
/* user land ping */
|
||||
next_heartbeat = jiffies + (timeout * HZ);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wdt_set_heartbeat(int t)
|
||||
{
|
||||
if ((t < 1) || (t > 3600)) /* arbitrary upper limit */
|
||||
return -EINVAL;
|
||||
|
||||
timeout = t;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* /dev/watchdog handling
|
||||
*/
|
||||
|
||||
static ssize_t fop_write(struct file *file, const char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
/* See if we got the magic character 'V' and reload the timer */
|
||||
if (count) {
|
||||
if (!nowayout) {
|
||||
size_t ofs;
|
||||
|
||||
/* note: just in case someone wrote the magic character
|
||||
* five months ago... */
|
||||
wdt_expect_close = 0;
|
||||
|
||||
/* now scan */
|
||||
for (ofs = 0; ofs != count; ofs++) {
|
||||
char c;
|
||||
if (get_user(c, buf + ofs))
|
||||
return -EFAULT;
|
||||
if (c == 'V')
|
||||
wdt_expect_close = 42;
|
||||
}
|
||||
}
|
||||
|
||||
/* Well, anyhow someone wrote to us, we should
|
||||
return that favour */
|
||||
wdt_keepalive();
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
static int fop_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
/* Just in case we're already talking to someone... */
|
||||
if (test_and_set_bit(0, &wdt_is_open))
|
||||
return -EBUSY;
|
||||
if (nowayout)
|
||||
__module_get(THIS_MODULE);
|
||||
|
||||
/* Good, fire up the show */
|
||||
wdt_startup();
|
||||
return stream_open(inode, file);
|
||||
}
|
||||
|
||||
static int fop_close(struct inode *inode, struct file *file)
|
||||
{
|
||||
if (wdt_expect_close == 42)
|
||||
wdt_turnoff();
|
||||
else {
|
||||
pr_crit("Unexpected close, not stopping watchdog!\n");
|
||||
wdt_keepalive();
|
||||
}
|
||||
clear_bit(0, &wdt_is_open);
|
||||
wdt_expect_close = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
void __user *argp = (void __user *)arg;
|
||||
int __user *p = argp;
|
||||
static const struct watchdog_info ident = {
|
||||
.options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT
|
||||
| WDIOF_MAGICCLOSE,
|
||||
.firmware_version = 1,
|
||||
.identity = "SC520",
|
||||
};
|
||||
|
||||
switch (cmd) {
|
||||
case WDIOC_GETSUPPORT:
|
||||
return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
|
||||
case WDIOC_GETSTATUS:
|
||||
case WDIOC_GETBOOTSTATUS:
|
||||
return put_user(0, p);
|
||||
case WDIOC_SETOPTIONS:
|
||||
{
|
||||
int new_options, retval = -EINVAL;
|
||||
|
||||
if (get_user(new_options, p))
|
||||
return -EFAULT;
|
||||
|
||||
if (new_options & WDIOS_DISABLECARD) {
|
||||
wdt_turnoff();
|
||||
retval = 0;
|
||||
}
|
||||
|
||||
if (new_options & WDIOS_ENABLECARD) {
|
||||
wdt_startup();
|
||||
retval = 0;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
case WDIOC_KEEPALIVE:
|
||||
wdt_keepalive();
|
||||
return 0;
|
||||
case WDIOC_SETTIMEOUT:
|
||||
{
|
||||
int new_timeout;
|
||||
|
||||
if (get_user(new_timeout, p))
|
||||
return -EFAULT;
|
||||
|
||||
if (wdt_set_heartbeat(new_timeout))
|
||||
return -EINVAL;
|
||||
|
||||
wdt_keepalive();
|
||||
}
|
||||
fallthrough;
|
||||
case WDIOC_GETTIMEOUT:
|
||||
return put_user(timeout, p);
|
||||
default:
|
||||
return -ENOTTY;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct file_operations wdt_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.write = fop_write,
|
||||
.open = fop_open,
|
||||
.release = fop_close,
|
||||
.unlocked_ioctl = fop_ioctl,
|
||||
.compat_ioctl = compat_ptr_ioctl,
|
||||
};
|
||||
|
||||
static struct miscdevice wdt_miscdev = {
|
||||
.minor = WATCHDOG_MINOR,
|
||||
.name = "watchdog",
|
||||
.fops = &wdt_fops,
|
||||
};
|
||||
|
||||
/*
|
||||
* Notifier for system down
|
||||
*/
|
||||
|
||||
static int wdt_notify_sys(struct notifier_block *this, unsigned long code,
|
||||
void *unused)
|
||||
{
|
||||
if (code == SYS_DOWN || code == SYS_HALT)
|
||||
wdt_turnoff();
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
/*
|
||||
* The WDT needs to learn about soft shutdowns in order to
|
||||
* turn the timebomb registers off.
|
||||
*/
|
||||
|
||||
static struct notifier_block wdt_notifier = {
|
||||
.notifier_call = wdt_notify_sys,
|
||||
};
|
||||
|
||||
static void __exit sc520_wdt_unload(void)
|
||||
{
|
||||
if (!nowayout)
|
||||
wdt_turnoff();
|
||||
|
||||
/* Deregister */
|
||||
misc_deregister(&wdt_miscdev);
|
||||
unregister_reboot_notifier(&wdt_notifier);
|
||||
iounmap(wdtmrctl);
|
||||
}
|
||||
|
||||
static int __init sc520_wdt_init(void)
|
||||
{
|
||||
int rc = -EBUSY;
|
||||
|
||||
/* Check that the timeout value is within it's range ;
|
||||
if not reset to the default */
|
||||
if (wdt_set_heartbeat(timeout)) {
|
||||
wdt_set_heartbeat(WATCHDOG_TIMEOUT);
|
||||
pr_info("timeout value must be 1 <= timeout <= 3600, using %d\n",
|
||||
WATCHDOG_TIMEOUT);
|
||||
}
|
||||
|
||||
wdtmrctl = ioremap(MMCR_BASE + OFFS_WDTMRCTL, 2);
|
||||
if (!wdtmrctl) {
|
||||
pr_err("Unable to remap memory\n");
|
||||
rc = -ENOMEM;
|
||||
goto err_out_region2;
|
||||
}
|
||||
|
||||
rc = register_reboot_notifier(&wdt_notifier);
|
||||
if (rc) {
|
||||
pr_err("cannot register reboot notifier (err=%d)\n", rc);
|
||||
goto err_out_ioremap;
|
||||
}
|
||||
|
||||
rc = misc_register(&wdt_miscdev);
|
||||
if (rc) {
|
||||
pr_err("cannot register miscdev on minor=%d (err=%d)\n",
|
||||
WATCHDOG_MINOR, rc);
|
||||
goto err_out_notifier;
|
||||
}
|
||||
|
||||
pr_info("WDT driver for SC520 initialised. timeout=%d sec (nowayout=%d)\n",
|
||||
timeout, nowayout);
|
||||
|
||||
return 0;
|
||||
|
||||
err_out_notifier:
|
||||
unregister_reboot_notifier(&wdt_notifier);
|
||||
err_out_ioremap:
|
||||
iounmap(wdtmrctl);
|
||||
err_out_region2:
|
||||
return rc;
|
||||
}
|
||||
|
||||
module_init(sc520_wdt_init);
|
||||
module_exit(sc520_wdt_unload);
|
||||
|
||||
MODULE_AUTHOR("Scott and Bill Jennings");
|
||||
MODULE_DESCRIPTION(
|
||||
"Driver for watchdog timer in AMD \"Elan\" SC520 uProcessor");
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -36,7 +36,7 @@
|
||||
* mknod /dev/watchdog c 10 130
|
||||
*
|
||||
* For an example userspace keep-alive daemon, see:
|
||||
* Documentation/watchdog/wdt.rst
|
||||
* samples/watchdog/watchdog-simple.c
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
@@ -92,7 +92,8 @@ static enum tco_reg_layout tco_reg_layout(struct pci_dev *dev)
|
||||
dev->device == PCI_DEVICE_ID_ATI_SBX00_SMBUS &&
|
||||
dev->revision < 0x40) {
|
||||
return sp5100;
|
||||
} else if (dev->vendor == PCI_VENDOR_ID_AMD &&
|
||||
} else if ((dev->vendor == PCI_VENDOR_ID_AMD ||
|
||||
dev->vendor == PCI_VENDOR_ID_HYGON) &&
|
||||
sp5100_tco_pci->device == PCI_DEVICE_ID_AMD_KERNCZ_SMBUS &&
|
||||
sp5100_tco_pci->revision >= AMD_ZEN_SMBUS_PCI_REV) {
|
||||
return efch_mmio;
|
||||
|
||||
@@ -320,10 +320,9 @@ static int sprd_wdt_probe(struct platform_device *pdev)
|
||||
watchdog_init_timeout(&wdt->wdd, 0, dev);
|
||||
|
||||
ret = devm_watchdog_register_device(dev, &wdt->wdd);
|
||||
if (ret) {
|
||||
sprd_wdt_disable(wdt);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, wdt);
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -55,9 +55,9 @@ MODULE_PARM_DESC(stop_on_reboot, "Stop watchdogs on reboot (0=keep watching, 1=s
|
||||
* for example when it's impossible to disable it. To do so,
|
||||
* raising the initcall level of the watchdog driver is a solution.
|
||||
* But in such case, the miscdev is maybe not ready (subsys_initcall), and
|
||||
* watchdog_core need miscdev to register the watchdog as a char device.
|
||||
* watchdog_core needs miscdev to register the watchdog as a char device.
|
||||
*
|
||||
* The deferred registration infrastructure offer a way for the watchdog
|
||||
* The deferred registration infrastructure offers a way for the watchdog
|
||||
* subsystem to register a watchdog properly, even before miscdev is ready.
|
||||
*/
|
||||
|
||||
@@ -222,11 +222,11 @@ static int watchdog_pm_notifier(struct notifier_block *nb, unsigned long mode,
|
||||
* watchdog_set_restart_priority - Change priority of restart handler
|
||||
* @wdd: watchdog device
|
||||
* @priority: priority of the restart handler, should follow these guidelines:
|
||||
* 0: use watchdog's restart function as last resort, has limited restart
|
||||
* capabilies
|
||||
* 128: default restart handler, use if no other handler is expected to be
|
||||
* * 0: use watchdog's restart function as last resort, has limited restart
|
||||
* capabilities
|
||||
* * 128: default restart handler, use if no other handler is expected to be
|
||||
* available and/or if restart is sufficient to restart the entire system
|
||||
* 255: preempt all other handlers
|
||||
* * 255: preempt all other handlers
|
||||
*
|
||||
* If a wdd->ops->restart function is provided when watchdog_register_device is
|
||||
* called, it will be registered as a restart handler with the priority given
|
||||
@@ -391,6 +391,9 @@ static void __watchdog_unregister_device(struct watchdog_device *wdd)
|
||||
if (test_bit(WDOG_STOP_ON_REBOOT, &wdd->status))
|
||||
unregister_reboot_notifier(&wdd->reboot_nb);
|
||||
|
||||
if (test_bit(WDOG_NO_PING_ON_SUSPEND, &wdd->status))
|
||||
unregister_pm_notifier(&wdd->pm_nb);
|
||||
|
||||
watchdog_dev_unregister(wdd);
|
||||
ida_free(&watchdog_ida, wdd->id);
|
||||
}
|
||||
|
||||
@@ -176,7 +176,7 @@ static int __watchdog_ping(struct watchdog_device *wdd)
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* watchdog_ping - ping the watchdog
|
||||
* @wdd: The watchdog device to ping
|
||||
*
|
||||
@@ -236,7 +236,7 @@ static enum hrtimer_restart watchdog_timer_expired(struct hrtimer *timer)
|
||||
return HRTIMER_NORESTART;
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* watchdog_start - wrapper to start the watchdog
|
||||
* @wdd: The watchdog device to start
|
||||
*
|
||||
@@ -279,7 +279,7 @@ static int watchdog_start(struct watchdog_device *wdd)
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* watchdog_stop - wrapper to stop the watchdog
|
||||
* @wdd: The watchdog device to stop
|
||||
*
|
||||
@@ -319,7 +319,7 @@ static int watchdog_stop(struct watchdog_device *wdd)
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* watchdog_get_status - wrapper to get the watchdog status
|
||||
* @wdd: The watchdog device to get the status from
|
||||
*
|
||||
@@ -356,7 +356,7 @@ static unsigned int watchdog_get_status(struct watchdog_device *wdd)
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* watchdog_set_timeout - set the watchdog timer timeout
|
||||
* @wdd: The watchdog device to set the timeout for
|
||||
* @timeout: Timeout to set in seconds
|
||||
@@ -391,7 +391,7 @@ static int watchdog_set_timeout(struct watchdog_device *wdd,
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* watchdog_set_pretimeout - set the watchdog timer pretimeout
|
||||
* @wdd: The watchdog device to set the timeout for
|
||||
* @timeout: pretimeout to set in seconds
|
||||
@@ -417,7 +417,7 @@ static int watchdog_set_pretimeout(struct watchdog_device *wdd,
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* watchdog_get_timeleft - wrapper to get the time left before a reboot
|
||||
* @wdd: The watchdog device to get the remaining time from
|
||||
* @timeleft: The time that's left
|
||||
@@ -659,7 +659,7 @@ __ATTRIBUTE_GROUPS(wdt);
|
||||
#define wdt_groups NULL
|
||||
#endif
|
||||
|
||||
/*
|
||||
/**
|
||||
* watchdog_ioctl_op - call the watchdog drivers ioctl op if defined
|
||||
* @wdd: The watchdog device to do the ioctl on
|
||||
* @cmd: Watchdog command
|
||||
@@ -678,7 +678,7 @@ static int watchdog_ioctl_op(struct watchdog_device *wdd, unsigned int cmd,
|
||||
return wdd->ops->ioctl(wdd, cmd, arg);
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* watchdog_write - writes to the watchdog
|
||||
* @file: File from VFS
|
||||
* @data: User address of data
|
||||
@@ -732,7 +732,7 @@ static ssize_t watchdog_write(struct file *file, const char __user *data,
|
||||
return len;
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* watchdog_ioctl - handle the different ioctl's for the watchdog device
|
||||
* @file: File handle to the device
|
||||
* @cmd: Watchdog command
|
||||
@@ -845,7 +845,7 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd,
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* watchdog_open - open the /dev/watchdog* devices
|
||||
* @inode: Inode of device
|
||||
* @file: File handle to device
|
||||
@@ -923,7 +923,7 @@ static void watchdog_core_data_release(struct device *dev)
|
||||
kfree(wd_data);
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* watchdog_release - release the watchdog device
|
||||
* @inode: Inode of device
|
||||
* @file: File handle to device
|
||||
@@ -932,7 +932,7 @@ static void watchdog_core_data_release(struct device *dev)
|
||||
* stop the watchdog when we have received the magic char (and nowayout
|
||||
* was not set), else the watchdog will keep running.
|
||||
*
|
||||
* Always returns 0.
|
||||
* Returns: Always 0.
|
||||
*/
|
||||
static int watchdog_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
@@ -1004,7 +1004,7 @@ static const struct class watchdog_class = {
|
||||
.dev_groups = wdt_groups,
|
||||
};
|
||||
|
||||
/*
|
||||
/**
|
||||
* watchdog_cdev_register - register watchdog character device
|
||||
* @wdd: Watchdog device
|
||||
*
|
||||
@@ -1105,7 +1105,7 @@ static int watchdog_cdev_register(struct watchdog_device *wdd)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* watchdog_cdev_unregister - unregister watchdog character device
|
||||
* @wdd: Watchdog device
|
||||
*
|
||||
|
||||
@@ -718,7 +718,7 @@ static void ziirave_wdt_remove(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id ziirave_wdt_id[] = {
|
||||
{ "rave-wdt" },
|
||||
{ .name = "rave-wdt" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ziirave_wdt_id);
|
||||
|
||||
@@ -26,7 +26,8 @@ struct watchdog_device;
|
||||
struct watchdog_core_data;
|
||||
struct watchdog_governor;
|
||||
|
||||
/** struct watchdog_ops - The watchdog-devices operations
|
||||
/**
|
||||
* struct watchdog_ops - The watchdog-devices operations
|
||||
*
|
||||
* @owner: The module owner.
|
||||
* @start: The routine for starting the watchdog device.
|
||||
@@ -59,7 +60,8 @@ struct watchdog_ops {
|
||||
long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long);
|
||||
};
|
||||
|
||||
/** struct watchdog_device - The structure that defines a watchdog device
|
||||
/**
|
||||
* struct watchdog_device - The structure that defines a watchdog device
|
||||
*
|
||||
* @id: The watchdog's ID. (Allocated by watchdog_register_device)
|
||||
* @parent: The parent bus device
|
||||
@@ -83,6 +85,8 @@ struct watchdog_ops {
|
||||
* Replaces max_timeout if specified.
|
||||
* @reboot_nb: The notifier block to stop watchdog on reboot.
|
||||
* @restart_nb: The notifier block to register a restart function.
|
||||
* @pm_nb: The notifier block to stop watchdog on suspend and restart it
|
||||
* on resume.
|
||||
* @driver_data:Pointer to the drivers private data.
|
||||
* @wd_data: Pointer to watchdog core internal data.
|
||||
* @status: Field that contains the devices internal status bits.
|
||||
|
||||
@@ -36,6 +36,7 @@ struct watchdog_info {
|
||||
#define WDIOF_UNKNOWN -1 /* Unknown flag error */
|
||||
#define WDIOS_UNKNOWN -1 /* Unknown status error */
|
||||
|
||||
/* Bit masks for watchdog_info.options, GETSTATUS and GETBOOTSTATUS ioctls */
|
||||
#define WDIOF_OVERHEAT 0x0001 /* Reset due to CPU overheat */
|
||||
#define WDIOF_FANFAULT 0x0002 /* Fan failed */
|
||||
#define WDIOF_EXTERN1 0x0004 /* External relay 1 */
|
||||
@@ -50,6 +51,7 @@ struct watchdog_info {
|
||||
other external alarm not a reboot */
|
||||
#define WDIOF_KEEPALIVEPING 0x8000 /* Keep alive ping reply */
|
||||
|
||||
/* Bit masks for WDIOC_SETOPTIONS ioctl */
|
||||
#define WDIOS_DISABLECARD 0x0001 /* Turn off the watchdog timer */
|
||||
#define WDIOS_ENABLECARD 0x0002 /* Turn on the watchdog timer */
|
||||
#define WDIOS_TEMPPANIC 0x0004 /* Kernel panic on temperature trip */
|
||||
|
||||
Reference in New Issue
Block a user