mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-09-18 22:59:29 +02:00
Merge tag 'watchdog-for-v7.3' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging
Pull watchdog updates from Guenter Roeck: "New Drivers: - Nuvoton MA35D1 - Lenovo SE30G2 and SE60 Added support to existing drivers: - snps,dw-wdt: Add RV1106 compatible - apple,wdt: Add t6030, t6031, and t8132 compatibles Other notable changes: - New "dump" pretimeout governor - Propagate errors from optional IRQ lookup - Remove redundant dev_err() and dev_err_probe() messages - npcm, qcom: Improved bootstatus reports - realtek-otto: Change to use regmap API - w83627hf_wdt: Report running watchdog, identify NCT6126 Various other minor fixes and improvements" * tag 'watchdog-for-v7.3' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging: (40 commits) watchdog: orion_wdt: Propagate errors from optional IRQ lookup watchdog: qcom: Propagate errors from optional IRQ lookup watchdog: aspeed: Propagate errors from optional IRQ lookup watchdog: stm32_iwdg: Propagate errors from optional IRQ lookup watchdog: dw_wdt: Propagate errors from optional IRQ lookup watchdog: mediatek: Propagate errors from optional IRQ lookup watchdog: apple: Constify some structures watchdog: pretimeout: Convert dump pretimeout governor to tristate nmi: Export CPU backtrace APIs for loadable modules watchdog: booke_wdt: Document unused parameter of __booke_wdt_disable() watchdog: wdat_wdt: map registers that fall inside ACPI NVS watchdog: Add Nuvoton MA35D1 watchdog driver support dt-bindings: watchdog: Add MA35D1 Watchdog watchdog: qcom: report bootstatus on IPQ9574 and IPQ5332 watchdog: qcom: report WDIOF_POWERUNDER in bootstatus watchdog: sprd: Remove redundant dev_err() watchdog: sama5d4: Remove redundant dev_err() watchdog: realtek_otto: Remove redundant dev_err_probe() watchdog: orion: Remove redundant dev_err() watchdog: marvell_gti: Remove redundant dev_err_probe() ...
This commit is contained in:
@@ -18,7 +18,10 @@ properties:
|
||||
- items:
|
||||
- enum:
|
||||
- apple,t6020-wdt
|
||||
- apple,t6030-wdt
|
||||
- apple,t6031-wdt
|
||||
- apple,t8122-wdt
|
||||
- apple,t8132-wdt
|
||||
- const: apple,t8103-wdt
|
||||
- items:
|
||||
- enum:
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/watchdog/nuvoton,ma35d1-wdt.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Nuvoton MA35D1 Watchdog Timer
|
||||
|
||||
maintainers:
|
||||
- Zi-Yu Chen <zychennvt@gmail.com>
|
||||
|
||||
allOf:
|
||||
- $ref: watchdog.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- nuvoton,ma35d1-wdt
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
wakeup-source: true
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
#include <dt-bindings/clock/nuvoton,ma35d1-clk.h>
|
||||
|
||||
soc {
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
wdt1: watchdog@40440000 {
|
||||
compatible = "nuvoton,ma35d1-wdt";
|
||||
reg = <0x0 0x40440000 0x0 0x100>;
|
||||
interrupts = <GIC_SPI 43 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&clk WDT1_GATE>;
|
||||
};
|
||||
};
|
||||
...
|
||||
@@ -16,6 +16,14 @@ description:
|
||||
|
||||
allOf:
|
||||
- $ref: watchdog.yaml#
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: nuvoton,wpcm450-wdt
|
||||
then:
|
||||
properties:
|
||||
nuvoton,sysgcr: false
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
@@ -40,6 +48,12 @@ properties:
|
||||
clock-frequency:
|
||||
description: Frequency in Hz of the clock that drives the NPCM timer.
|
||||
|
||||
nuvoton,sysgcr:
|
||||
$ref: /schemas/types.yaml#/definitions/phandle
|
||||
description:
|
||||
a phandle to access the GCR reset-status registers that latch
|
||||
watchdog reset indications on NPCM750 and NPCM845.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
@@ -27,6 +27,7 @@ properties:
|
||||
- qcom,apss-wdt-ipq9574
|
||||
- qcom,apss-wdt-ipq9650
|
||||
- qcom,apss-wdt-kaanapali
|
||||
- qcom,apss-wdt-maili
|
||||
- qcom,apss-wdt-msm8226
|
||||
- qcom,apss-wdt-msm8974
|
||||
- qcom,apss-wdt-msm8994
|
||||
|
||||
@@ -35,6 +35,7 @@ properties:
|
||||
- rockchip,rk3576-wdt
|
||||
- rockchip,rk3588-wdt
|
||||
- rockchip,rv1103b-wdt
|
||||
- rockchip,rv1106-wdt
|
||||
- rockchip,rv1108-wdt
|
||||
- rockchip,rv1126-wdt
|
||||
- const: snps,dw-wdt
|
||||
|
||||
@@ -25,4 +25,5 @@ Driver specific
|
||||
hpwdt
|
||||
ics-wdt
|
||||
mlx-wdt
|
||||
npcm_wdt
|
||||
pcwd-watchdog
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
=============
|
||||
NPCM Watchdog
|
||||
=============
|
||||
|
||||
The NPCM watchdog driver can report reset-cause information on
|
||||
``nuvoton,npcm750-wdt`` and ``nuvoton,npcm845-wdt`` systems.
|
||||
|
||||
Userspace can read the latched reset cause through
|
||||
``WDIOC_GETBOOTSTATUS``. When ``CONFIG_WATCHDOG_SYSFS`` is enabled, the
|
||||
same value is also visible through ``/sys/class/watchdog/watchdogN/bootstatus``.
|
||||
|
||||
The mapping is fixed in the driver. It exposes the SoC reset indications
|
||||
through the generic watchdog bootstatus flags and is not configurable from
|
||||
Device Tree.
|
||||
|
||||
.. list-table:: Reset-cause mapping
|
||||
:header-rows: 1
|
||||
|
||||
* - Platform
|
||||
- Reset indication
|
||||
- Bootstatus flag
|
||||
- Reported meaning
|
||||
* - NPCM750 and NPCM845
|
||||
- ``PORST``
|
||||
- ``WDIOF_OVERHEAT``
|
||||
- power-on reset
|
||||
* - NPCM750 and NPCM845
|
||||
- ``CORST``
|
||||
- ``WDIOF_FANFAULT``
|
||||
- core reset
|
||||
* - NPCM750 and NPCM845
|
||||
- ``SWR1RST``
|
||||
- ``WDIOF_EXTERN1``
|
||||
- software reset source 1
|
||||
* - NPCM750 and NPCM845
|
||||
- ``SWR2RST``
|
||||
- ``WDIOF_EXTERN2``
|
||||
- software reset source 2
|
||||
* - NPCM750 and NPCM845
|
||||
- ``SWR3RST``
|
||||
- ``WDIOF_POWERUNDER``
|
||||
- software reset source 3
|
||||
* - NPCM750
|
||||
- ``SWR4RST``
|
||||
- ``WDIOF_POWEROVER``
|
||||
- software reset source 4
|
||||
* - NPCM845
|
||||
- ``TIP reset`` (``INTCR2[25]``)
|
||||
- ``WDIOF_POWEROVER``
|
||||
- TIP reset
|
||||
|
||||
``WDIOF_CARDRESET`` is reported only for the watchdog instance whose own
|
||||
reset-status bit is latched. On systems with three watchdog instances, this
|
||||
maps ``WD0RST``, ``WD1RST``, and ``WD2RST`` to ``watchdog0``, ``watchdog1``,
|
||||
and ``watchdog2`` respectively.
|
||||
|
||||
The driver may report ``WDIOF_CARDRESET`` together with one or more of the
|
||||
reset-cause flags listed above.
|
||||
|
||||
On NPCM750, the driver samples ``RESSR``. When reset bits are still latched,
|
||||
it clears them and stores the sampled value in ``SCRPAD2`` so later watchdog
|
||||
probes can report the same boot-time state.
|
||||
|
||||
On NPCM845, the driver samples ``INTCR2``. When reset bits are still latched,
|
||||
it clears them and stores the sampled value in ``SCRPAD10`` so later watchdog
|
||||
probes can report the same boot-time state.
|
||||
|
||||
The WPCM450 watchdog continues to operate without this reset-indication
|
||||
mapping.
|
||||
@@ -93,10 +93,19 @@ config WATCHDOG_PRETIMEOUT_GOV_SEL
|
||||
tristate
|
||||
depends on WATCHDOG_PRETIMEOUT_GOV
|
||||
default m
|
||||
select WATCHDOG_PRETIMEOUT_GOV_PANIC if WATCHDOG_PRETIMEOUT_GOV_NOOP=n
|
||||
select WATCHDOG_PRETIMEOUT_GOV_PANIC if \
|
||||
WATCHDOG_PRETIMEOUT_GOV_NOOP=n && WATCHDOG_PRETIMEOUT_GOV_DUMP=n
|
||||
|
||||
if WATCHDOG_PRETIMEOUT_GOV
|
||||
|
||||
config WATCHDOG_PRETIMEOUT_GOV_DUMP
|
||||
tristate "Dump watchdog pretimeout governor"
|
||||
depends on WATCHDOG_CORE
|
||||
default WATCHDOG_CORE
|
||||
help
|
||||
Dump watchdog pretimeout governor, all cpu backtrace is
|
||||
added to kernel log buffer.
|
||||
|
||||
config WATCHDOG_PRETIMEOUT_GOV_NOOP
|
||||
tristate "Noop watchdog pretimeout governor"
|
||||
depends on WATCHDOG_CORE
|
||||
@@ -121,6 +130,14 @@ choice
|
||||
The governor takes its action, if a watchdog is capable
|
||||
to report a pretimeout event.
|
||||
|
||||
config WATCHDOG_PRETIMEOUT_DEFAULT_GOV_DUMP
|
||||
bool "dump"
|
||||
depends on WATCHDOG_PRETIMEOUT_GOV_DUMP
|
||||
help
|
||||
Use dump watchdog pretimeout governor by default. If dump
|
||||
governor is selected by a user, dump all cpu backtrace to
|
||||
the kernel log buffer and don't do any system changes.
|
||||
|
||||
config WATCHDOG_PRETIMEOUT_DEFAULT_GOV_NOOP
|
||||
bool "noop"
|
||||
depends on WATCHDOG_PRETIMEOUT_GOV_NOOP
|
||||
@@ -291,6 +308,18 @@ config LENOVO_SE30_WDT
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called lenovo-se30-wdt.
|
||||
|
||||
config LENOVO_SE30G2_SE60_WDT
|
||||
tristate "Lenovo SE30 Gen 2, SE60 Watchdog"
|
||||
depends on (X86 && DMI) || COMPILE_TEST
|
||||
depends on HAS_IOPORT
|
||||
select WATCHDOG_CORE
|
||||
help
|
||||
If you say yes here you get support for the watchdog
|
||||
functionality for the Lenovo SE30 Gen2 and SE60 platforms.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called lenovo-se30g2-se60-wdt.
|
||||
|
||||
config MENF21BMC_WATCHDOG
|
||||
tristate "MEN 14F021P00 BMC Watchdog"
|
||||
depends on MFD_MENF21BMC || COMPILE_TEST
|
||||
@@ -720,6 +749,16 @@ config STMP3XXX_RTC_WATCHDOG
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called stmp3xxx_rtc_wdt.
|
||||
|
||||
config MA35D1_WDT
|
||||
tristate "Nuvoton MA35D1 Watchdog Timer"
|
||||
depends on ARCH_MA35 || COMPILE_TEST
|
||||
select WATCHDOG_CORE
|
||||
help
|
||||
Say Y here to include support for the watchdog timer
|
||||
for the Nuvoton MA35D1 series microprocessor.
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ma35d1_wdt.
|
||||
|
||||
config TS4800_WATCHDOG
|
||||
tristate "TS-4800 Watchdog"
|
||||
depends on HAS_IOMEM && OF
|
||||
@@ -1076,6 +1115,7 @@ config REALTEK_OTTO_WDT
|
||||
depends on MACH_REALTEK_RTL || COMPILE_TEST
|
||||
depends on COMMON_CLK
|
||||
select WATCHDOG_CORE
|
||||
select REGMAP_MMIO
|
||||
default MACH_REALTEK_RTL
|
||||
help
|
||||
Say Y here to include support for the watchdog timer on Realtek
|
||||
|
||||
@@ -11,6 +11,7 @@ watchdog-objs += watchdog_core.o watchdog_dev.o
|
||||
watchdog-$(CONFIG_WATCHDOG_PRETIMEOUT_GOV) += watchdog_pretimeout.o
|
||||
watchdog-$(CONFIG_WATCHDOG_HRTIMER_PRETIMEOUT) += watchdog_hrtimer_pretimeout.o
|
||||
|
||||
obj-$(CONFIG_WATCHDOG_PRETIMEOUT_GOV_DUMP) += pretimeout_dump.o
|
||||
obj-$(CONFIG_WATCHDOG_PRETIMEOUT_GOV_NOOP) += pretimeout_noop.o
|
||||
obj-$(CONFIG_WATCHDOG_PRETIMEOUT_GOV_PANIC) += pretimeout_panic.o
|
||||
|
||||
@@ -104,6 +105,7 @@ 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
|
||||
obj-$(CONFIG_MA35D1_WDT) += ma35d1_wdt.o
|
||||
|
||||
# X86 (i386 + ia64 + x86_64) Architecture
|
||||
obj-$(CONFIG_ACQUIRE_WDT) += acquirewdt.o
|
||||
@@ -127,6 +129,7 @@ obj-$(CONFIG_IE6XX_WDT) += ie6xx_wdt.o
|
||||
obj-$(CONFIG_ITCO_WDT) += iTCO_wdt.o
|
||||
obj-$(CONFIG_LENOVO_SE10_WDT) += lenovo_se10_wdt.o
|
||||
obj-$(CONFIG_LENOVO_SE30_WDT) += lenovo_se30_wdt.o
|
||||
obj-$(CONFIG_LENOVO_SE30G2_SE60_WDT) += lenovo_se30g2_se60_wdt.o
|
||||
obj-$(CONFIG_IT8712F_WDT) += it8712f_wdt.o
|
||||
obj-$(CONFIG_IT87_WDT) += it87_wdt.o
|
||||
obj-$(CONFIG_HP_WATCHDOG) += hpwdt.o
|
||||
|
||||
@@ -139,7 +139,7 @@ static int apple_wdt_restart(struct watchdog_device *wdd, unsigned long mode,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct watchdog_ops apple_wdt_ops = {
|
||||
static const struct watchdog_ops apple_wdt_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.start = apple_wdt_start,
|
||||
.stop = apple_wdt_stop,
|
||||
@@ -149,7 +149,7 @@ static struct watchdog_ops apple_wdt_ops = {
|
||||
.restart = apple_wdt_restart,
|
||||
};
|
||||
|
||||
static struct watchdog_info apple_wdt_info = {
|
||||
static const struct watchdog_info apple_wdt_info = {
|
||||
.identity = "Apple SoC Watchdog",
|
||||
.options = WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT,
|
||||
};
|
||||
|
||||
@@ -429,6 +429,8 @@ static int aspeed_wdt_probe(struct platform_device *pdev)
|
||||
if (wdt->cfg->irq_mask) {
|
||||
int irq = platform_get_irq_optional(pdev, 0);
|
||||
|
||||
if (irq < 0 && irq != -ENXIO)
|
||||
return irq;
|
||||
if (irq > 0) {
|
||||
ret = devm_request_irq(dev, irq, aspeed_wdt_irq,
|
||||
IRQF_SHARED, dev_name(dev),
|
||||
|
||||
@@ -389,8 +389,7 @@ static int bd96801_wdt_probe(struct platform_device *pdev)
|
||||
IRQF_ONESHOT, "bd96801-wdg",
|
||||
NULL);
|
||||
if (ret)
|
||||
return dev_err_probe(&pdev->dev, ret,
|
||||
"Failed to register IRQ\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return devm_watchdog_register_device(&pdev->dev, &w->wdt);
|
||||
|
||||
@@ -149,6 +149,7 @@ static void __booke_wdt_enable(void *data)
|
||||
|
||||
/**
|
||||
* __booke_wdt_disable - disable the watchdog on the given CPU
|
||||
* @data: unused
|
||||
*
|
||||
* This function is called on each CPU. It disables the watchdog on that CPU.
|
||||
*
|
||||
|
||||
@@ -312,12 +312,8 @@ static int cdns_wdt_probe(struct platform_device *pdev)
|
||||
if (!wdt->rst && irq >= 0) {
|
||||
ret = devm_request_irq(dev, irq, cdns_wdt_irq_handler, 0,
|
||||
pdev->name, pdev);
|
||||
if (ret) {
|
||||
dev_err(dev,
|
||||
"cannot register interrupt handler err=%d\n",
|
||||
ret);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/* Initialize the members of cdns_wdt structure */
|
||||
|
||||
@@ -178,8 +178,8 @@ static int __maybe_unused cros_ec_wdt_resume(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
static const struct platform_device_id cros_ec_wdt_id[] = {
|
||||
{ DRV_NAME, 0 },
|
||||
{}
|
||||
{ .name = DRV_NAME },
|
||||
{ }
|
||||
};
|
||||
|
||||
static struct platform_driver cros_ec_wdt_driver = {
|
||||
|
||||
@@ -611,7 +611,7 @@ static int dw_wdt_drv_probe(struct platform_device *pdev)
|
||||
|
||||
dw_wdt->wdd.info = &dw_wdt_pt_ident;
|
||||
} else {
|
||||
if (ret == -EPROBE_DEFER)
|
||||
if (ret != -ENXIO)
|
||||
return ret;
|
||||
|
||||
dw_wdt->wdd.info = &dw_wdt_ident;
|
||||
|
||||
@@ -165,10 +165,8 @@ static int mid_wdt_probe(struct platform_device *pdev)
|
||||
ret = devm_request_irq(dev, pdata->irq, mid_wdt_irq,
|
||||
IRQF_SHARED | IRQF_NO_SUSPEND, "watchdog",
|
||||
wdt_dev);
|
||||
if (ret) {
|
||||
dev_err(dev, "error requesting warning irq %d\n", pdata->irq);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* The firmware followed by U-Boot leaves the watchdog running
|
||||
|
||||
@@ -214,7 +214,7 @@ static int keembay_wdt_probe(struct platform_device *pdev)
|
||||
ret = devm_request_irq(dev, wdt->th_irq, keembay_wdt_th_isr, 0,
|
||||
"keembay-wdt", wdt);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Failed to request IRQ for threshold\n");
|
||||
return ret;
|
||||
|
||||
wdt->to_irq = platform_get_irq_byname(pdev, "timeout");
|
||||
if (wdt->to_irq < 0)
|
||||
@@ -223,7 +223,7 @@ static int keembay_wdt_probe(struct platform_device *pdev)
|
||||
ret = devm_request_irq(dev, wdt->to_irq, keembay_wdt_to_isr, 0,
|
||||
"keembay-wdt", wdt);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Failed to request IRQ for timeout\n");
|
||||
return ret;
|
||||
|
||||
wdt->wdd.parent = dev;
|
||||
wdt->wdd.info = &keembay_wdt_info;
|
||||
|
||||
@@ -0,0 +1,289 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* WDT driver for Lenovo SE30G2 & SE60.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/watchdog.h>
|
||||
|
||||
#define CFG_PORT 0x2E
|
||||
#define CFG_SIZE 2
|
||||
|
||||
#define CFG_LDN 0x07
|
||||
#define CFG_BRAM_LDN 0x10
|
||||
|
||||
#define BRAM_SIZE 2
|
||||
|
||||
#define BRAM_WDT_REG 0x48
|
||||
|
||||
#define DRVNAME "lenovo-se30g2-se60-wdt"
|
||||
|
||||
/*The timeout range is 1-255 seconds*/
|
||||
#define MIN_TIMEOUT 1
|
||||
#define MAX_TIMEOUT 255
|
||||
#define WATCHDOG_TIMEOUT 60 /* 60 sec default timeout */
|
||||
|
||||
static unsigned short bram_base;
|
||||
static struct platform_device *se_30g2_60_pdev;
|
||||
|
||||
static int timeout; /* in seconds */
|
||||
module_param(timeout, int, 0);
|
||||
MODULE_PARM_DESC(timeout,
|
||||
"Watchdog timeout in seconds. 1 <= timeout <= 255, 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) ")");
|
||||
|
||||
struct se_30g2_60_wdt {
|
||||
struct watchdog_device wdd;
|
||||
};
|
||||
|
||||
static void enter_pnp_mode(void)
|
||||
{
|
||||
outb(0x87, CFG_PORT);
|
||||
outb(0x01, CFG_PORT);
|
||||
outb(0x55, CFG_PORT);
|
||||
outb(0x55, CFG_PORT);
|
||||
}
|
||||
|
||||
static void exit_pnp_mode(void)
|
||||
{
|
||||
outb(0x2, CFG_PORT);
|
||||
outb(0x2, CFG_PORT + 1);
|
||||
}
|
||||
|
||||
static void lpc_write(unsigned char index, unsigned char data)
|
||||
{
|
||||
outb(index, CFG_PORT);
|
||||
outb(data, CFG_PORT + 1);
|
||||
}
|
||||
|
||||
static unsigned char lpc_read(unsigned char index)
|
||||
{
|
||||
outb(index, CFG_PORT);
|
||||
return inb(CFG_PORT + 1);
|
||||
}
|
||||
|
||||
static unsigned short lpc_chip_id(void)
|
||||
{
|
||||
unsigned char msb, lsb;
|
||||
|
||||
msb = lpc_read(0x20);
|
||||
lsb = lpc_read(0x21);
|
||||
|
||||
return (msb << 8 | lsb);
|
||||
}
|
||||
|
||||
static void bram_write(unsigned char reg, unsigned char val)
|
||||
{
|
||||
outb(reg | 0x80, bram_base);
|
||||
outb(val, bram_base + 1);
|
||||
}
|
||||
|
||||
static unsigned char bram_read(unsigned char reg)
|
||||
{
|
||||
unsigned char val;
|
||||
|
||||
outb(reg | 0x80, bram_base);
|
||||
val = inb(bram_base + 1);
|
||||
return val;
|
||||
}
|
||||
|
||||
static int wdt_read(unsigned short *val)
|
||||
{
|
||||
if (!request_muxed_region(bram_base, BRAM_SIZE, DRVNAME))
|
||||
return -EACCES;
|
||||
|
||||
*val = ((bram_read(BRAM_WDT_REG) & 0xFF) << 8);
|
||||
*val |= bram_read(BRAM_WDT_REG + 1) & 0xFF;
|
||||
release_region(bram_base, BRAM_SIZE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wdt_write(unsigned short val)
|
||||
{
|
||||
if (!request_muxed_region(bram_base, BRAM_SIZE, DRVNAME))
|
||||
return -EACCES;
|
||||
|
||||
bram_write(BRAM_WDT_REG, (val >> 8) & 0xFF);
|
||||
bram_write(BRAM_WDT_REG + 1, val & 0xFF);
|
||||
release_region(bram_base, BRAM_SIZE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wdt_start(struct watchdog_device *wdog)
|
||||
{
|
||||
return wdt_write(wdog->timeout);
|
||||
}
|
||||
|
||||
static int wdt_stop(struct watchdog_device *wdog)
|
||||
{
|
||||
return wdt_write(0);
|
||||
}
|
||||
|
||||
static int wdt_ping(struct watchdog_device *wdog)
|
||||
{
|
||||
return wdt_write(wdog->timeout);
|
||||
}
|
||||
|
||||
static unsigned int wdt_get_timeleft(struct watchdog_device *wdog)
|
||||
{
|
||||
unsigned short val;
|
||||
int err;
|
||||
|
||||
err = wdt_read(&val);
|
||||
return err ? 0 : val;
|
||||
}
|
||||
|
||||
static const struct watchdog_info wdt_info = {
|
||||
.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
|
||||
.identity = "Lenovo SE30G2 SE60 WDOG",
|
||||
};
|
||||
|
||||
static const struct watchdog_ops se_30g2_60_wdt_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.start = wdt_start,
|
||||
.stop = wdt_stop,
|
||||
.ping = wdt_ping,
|
||||
.get_timeleft = wdt_get_timeleft,
|
||||
};
|
||||
|
||||
static int se_30g2_60_wdt_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct se_30g2_60_wdt *priv;
|
||||
unsigned int chip_id;
|
||||
int ret;
|
||||
|
||||
if (!request_muxed_region(CFG_PORT, CFG_SIZE, DRVNAME))
|
||||
return -EBUSY;
|
||||
|
||||
/* identify the chip */
|
||||
enter_pnp_mode();
|
||||
chip_id = lpc_chip_id();
|
||||
if (chip_id != 0x5782) {
|
||||
exit_pnp_mode();
|
||||
release_region(CFG_PORT, CFG_SIZE);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* probe the BRAM base address */
|
||||
lpc_write(CFG_LDN, CFG_BRAM_LDN);
|
||||
bram_base = (lpc_read(0x60) << 8) | lpc_read(0x61);
|
||||
exit_pnp_mode();
|
||||
release_region(CFG_PORT, CFG_SIZE);
|
||||
dev_info(dev, "Found Lenovo SE30G2 SE60 0x%x\n", chip_id);
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
watchdog_set_drvdata(&priv->wdd, priv);
|
||||
|
||||
priv->wdd.parent = dev;
|
||||
priv->wdd.info = &wdt_info;
|
||||
priv->wdd.ops = &se_30g2_60_wdt_ops;
|
||||
priv->wdd.timeout = WATCHDOG_TIMEOUT; /* Set default timeout */
|
||||
priv->wdd.min_timeout = MIN_TIMEOUT;
|
||||
priv->wdd.max_timeout = MAX_TIMEOUT;
|
||||
|
||||
watchdog_init_timeout(&priv->wdd, timeout, dev);
|
||||
watchdog_set_nowayout(&priv->wdd, nowayout);
|
||||
watchdog_stop_on_reboot(&priv->wdd);
|
||||
watchdog_stop_on_unregister(&priv->wdd);
|
||||
|
||||
ret = devm_watchdog_register_device(dev, &priv->wdd);
|
||||
|
||||
dev_dbg(&pdev->dev, "initialized. timeout=%d sec (nowayout=%d)\n",
|
||||
priv->wdd.timeout, nowayout);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct platform_driver se_30g2_60_wdt_driver = {
|
||||
.driver = {
|
||||
.name = DRVNAME,
|
||||
},
|
||||
.probe = se_30g2_60_wdt_probe,
|
||||
};
|
||||
|
||||
static int se_30g2_60_create_device(const struct dmi_system_id *id)
|
||||
{
|
||||
int err;
|
||||
|
||||
se_30g2_60_pdev = platform_device_alloc("lenovo-se30g2-se60-wdt", -1);
|
||||
if (!se_30g2_60_pdev)
|
||||
return -ENOMEM;
|
||||
|
||||
err = platform_device_add(se_30g2_60_pdev);
|
||||
if (err) {
|
||||
platform_device_put(se_30g2_60_pdev);
|
||||
se_30g2_60_pdev = NULL;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct dmi_system_id se_30g2_60[] __initconst = {
|
||||
{
|
||||
.ident = "LENOVO-SE30G2",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
|
||||
DMI_MATCH(DMI_BOARD_NAME, "33BD"),
|
||||
},
|
||||
.callback = se_30g2_60_create_device,
|
||||
},
|
||||
{
|
||||
.ident = "LENOVO-SE60",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
|
||||
DMI_MATCH(DMI_BOARD_NAME, "33BF"),
|
||||
},
|
||||
.callback = se_30g2_60_create_device,
|
||||
},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(dmi, se_30g2_60);
|
||||
|
||||
static int __init se_30g2_60_wdt_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (!dmi_check_system(se_30g2_60))
|
||||
return -ENODEV;
|
||||
|
||||
err = platform_driver_register(&se_30g2_60_wdt_driver);
|
||||
if (err && se_30g2_60_pdev) {
|
||||
platform_device_unregister(se_30g2_60_pdev);
|
||||
se_30g2_60_pdev = NULL;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __exit se_30g2_60_wdt_exit(void)
|
||||
{
|
||||
if (se_30g2_60_pdev)
|
||||
platform_device_unregister(se_30g2_60_pdev);
|
||||
platform_driver_unregister(&se_30g2_60_wdt_driver);
|
||||
}
|
||||
|
||||
module_init(se_30g2_60_wdt_init);
|
||||
module_exit(se_30g2_60_wdt_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Mark Pearson <mpearson-lenovo@squebb.ca>");
|
||||
MODULE_DESCRIPTION("WDT driver for Lenovo SE30G2 & SE60");
|
||||
@@ -0,0 +1,363 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2026 Nuvoton technology corporation.
|
||||
*
|
||||
* Author: Zi-Yu Chen <zychennvt@gmail.com>
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/bits.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_wakeirq.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/watchdog.h>
|
||||
|
||||
#define DRV_NAME "ma35d1-wdt"
|
||||
|
||||
#define REG_WDT_CTL 0x00
|
||||
#define REG_WDT_RSTCNT 0x08
|
||||
|
||||
#define TOUTSEL GENMASK(11, 8)
|
||||
#define WDTEN BIT(7)
|
||||
#define INTEN BIT(6)
|
||||
#define WKF BIT(5)
|
||||
#define WKEN BIT(4)
|
||||
#define IF BIT(3)
|
||||
#define RSTF BIT(2)
|
||||
#define RSTEN BIT(1)
|
||||
#define SYNC BIT(30)
|
||||
|
||||
#define WDT_DEFAULT_TIMEOUT 32
|
||||
#define RESET_COUNTER 0x00005AA5
|
||||
|
||||
static bool nowayout = WATCHDOG_NOWAYOUT;
|
||||
static unsigned int timeout;
|
||||
|
||||
struct ma35d1_wdt_dev {
|
||||
struct watchdog_device wdt_dev;
|
||||
spinlock_t lock;
|
||||
void __iomem *wdt_base;
|
||||
struct clk *clk;
|
||||
unsigned long clk_rate;
|
||||
int irq;
|
||||
};
|
||||
|
||||
static int ma35d1_wdt_wait_sync(struct ma35d1_wdt_dev *ma35d1_wdt)
|
||||
{
|
||||
unsigned int val;
|
||||
|
||||
return readl_relaxed_poll_timeout_atomic(ma35d1_wdt->wdt_base +
|
||||
REG_WDT_CTL, val, !(val & SYNC), 10, 125);
|
||||
}
|
||||
|
||||
/**
|
||||
* ma35d1_wdt_stop - Stop the watchdog.
|
||||
*
|
||||
* @wdt_dev: watchdog device
|
||||
*
|
||||
* Read the contents of the CTL register, clear the WDTEN bit
|
||||
* in the register and set the access key for successful write.
|
||||
*
|
||||
* Return: 0 on success, negative error otherwise.
|
||||
*/
|
||||
static int ma35d1_wdt_stop(struct watchdog_device *wdt_dev)
|
||||
{
|
||||
struct ma35d1_wdt_dev *ma35d1_wdt = watchdog_get_drvdata(wdt_dev);
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
guard(spinlock_irqsave)(&ma35d1_wdt->lock);
|
||||
val = readl_relaxed(ma35d1_wdt->wdt_base + REG_WDT_CTL);
|
||||
val &= ~WDTEN;
|
||||
writel_relaxed(val, ma35d1_wdt->wdt_base + REG_WDT_CTL);
|
||||
ret = ma35d1_wdt_wait_sync(ma35d1_wdt);
|
||||
if (ret) {
|
||||
dev_err(wdt_dev->parent, "Wait for WDTEN SYNC timeout!\n");
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ma35d1_wdt_ping(struct watchdog_device *wdt_dev)
|
||||
{
|
||||
struct ma35d1_wdt_dev *ma35d1_wdt = watchdog_get_drvdata(wdt_dev);
|
||||
|
||||
writel_relaxed(RESET_COUNTER, ma35d1_wdt->wdt_base + REG_WDT_RSTCNT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ma35d1_wdt_set_timeout(struct watchdog_device *wdt_dev,
|
||||
unsigned int timeout)
|
||||
{
|
||||
struct ma35d1_wdt_dev *ma35d1_wdt = watchdog_get_drvdata(wdt_dev);
|
||||
unsigned long target_ticks;
|
||||
unsigned int val, i;
|
||||
static const uint8_t toutsel_shifts[] = { 4, 6, 8, 10, 12,
|
||||
14, 16, 18, 20 };
|
||||
|
||||
if (timeout < (ma35d1_wdt->wdt_dev.max_hw_heartbeat_ms / 1000)) {
|
||||
target_ticks = (unsigned long)timeout * ma35d1_wdt->clk_rate;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(toutsel_shifts); i++) {
|
||||
if ((1UL << toutsel_shifts[i]) >= target_ticks)
|
||||
break;
|
||||
}
|
||||
/* To avoid truncation errors (0 seconds) during division. */
|
||||
wdt_dev->timeout =
|
||||
(1UL << toutsel_shifts[i]) / ma35d1_wdt->clk_rate;
|
||||
if (wdt_dev->timeout == 0)
|
||||
wdt_dev->timeout = 1;
|
||||
|
||||
} else {
|
||||
i = ARRAY_SIZE(toutsel_shifts) - 1;
|
||||
wdt_dev->timeout = timeout;
|
||||
}
|
||||
|
||||
guard(spinlock_irqsave)(&ma35d1_wdt->lock);
|
||||
val = readl_relaxed(ma35d1_wdt->wdt_base + REG_WDT_CTL);
|
||||
val &= ~TOUTSEL;
|
||||
val |= FIELD_PREP(TOUTSEL, i);
|
||||
writel_relaxed(val, ma35d1_wdt->wdt_base + REG_WDT_CTL);
|
||||
|
||||
ma35d1_wdt_ping(wdt_dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ma35d1_wdt_start(struct watchdog_device *wdt_dev)
|
||||
{
|
||||
struct ma35d1_wdt_dev *ma35d1_wdt = watchdog_get_drvdata(wdt_dev);
|
||||
void __iomem *wdt_base = ma35d1_wdt->wdt_base;
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
ret = ma35d1_wdt_set_timeout(wdt_dev, wdt_dev->timeout);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
guard(spinlock_irqsave)(&ma35d1_wdt->lock);
|
||||
val = readl_relaxed(wdt_base + REG_WDT_CTL);
|
||||
val |= (WDTEN | RSTEN);
|
||||
|
||||
writel_relaxed(val, wdt_base + REG_WDT_CTL);
|
||||
ret = ma35d1_wdt_wait_sync(ma35d1_wdt);
|
||||
if (ret) {
|
||||
dev_err(wdt_dev->parent, "Wait for WDTEN SYNC timeout!\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
writel_relaxed(RESET_COUNTER, wdt_base + REG_WDT_RSTCNT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct watchdog_info ma35d1_wdt_info = {
|
||||
.identity = DRV_NAME,
|
||||
.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE |
|
||||
WDIOF_CARDRESET,
|
||||
};
|
||||
|
||||
static const struct watchdog_ops ma35d1_wdt_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.start = ma35d1_wdt_start,
|
||||
.stop = ma35d1_wdt_stop,
|
||||
.ping = ma35d1_wdt_ping,
|
||||
.set_timeout = ma35d1_wdt_set_timeout,
|
||||
};
|
||||
|
||||
static irqreturn_t ma35d1_wdt_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct ma35d1_wdt_dev *ma35d1_wdt = dev_id;
|
||||
unsigned int val;
|
||||
|
||||
/* Clear the flag if set */
|
||||
guard(spinlock)(&ma35d1_wdt->lock);
|
||||
val = readl_relaxed(ma35d1_wdt->wdt_base + REG_WDT_CTL);
|
||||
writel_relaxed(val, ma35d1_wdt->wdt_base + REG_WDT_CTL);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int ma35d1_wdt_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct ma35d1_wdt_dev *ma35d1_wdt;
|
||||
unsigned long clk_rate, val;
|
||||
int ret;
|
||||
|
||||
ma35d1_wdt = devm_kzalloc(dev, sizeof(*ma35d1_wdt), GFP_KERNEL);
|
||||
if (!ma35d1_wdt)
|
||||
return -ENOMEM;
|
||||
|
||||
spin_lock_init(&ma35d1_wdt->lock);
|
||||
platform_set_drvdata(pdev, ma35d1_wdt);
|
||||
|
||||
ma35d1_wdt->wdt_base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(ma35d1_wdt->wdt_base))
|
||||
return PTR_ERR(ma35d1_wdt->wdt_base);
|
||||
|
||||
ma35d1_wdt->clk = devm_clk_get_enabled(dev, NULL);
|
||||
if (IS_ERR(ma35d1_wdt->clk))
|
||||
return dev_err_probe(dev, PTR_ERR(ma35d1_wdt->clk),
|
||||
"Can't get Watchdog clock\n");
|
||||
|
||||
clk_rate = clk_get_rate(ma35d1_wdt->clk);
|
||||
if (!clk_rate)
|
||||
return -EINVAL;
|
||||
ma35d1_wdt->clk_rate = clk_rate;
|
||||
|
||||
ma35d1_wdt->irq = platform_get_irq(pdev, 0);
|
||||
if (ma35d1_wdt->irq < 0)
|
||||
return dev_err_probe(dev, ma35d1_wdt->irq,
|
||||
"failed to get irq\n");
|
||||
|
||||
ma35d1_wdt->wdt_dev.info = &ma35d1_wdt_info;
|
||||
ma35d1_wdt->wdt_dev.ops = &ma35d1_wdt_ops;
|
||||
ma35d1_wdt->wdt_dev.timeout = WDT_DEFAULT_TIMEOUT;
|
||||
ma35d1_wdt->wdt_dev.min_timeout = 1;
|
||||
ma35d1_wdt->wdt_dev.max_hw_heartbeat_ms = (1U << 20) * 1000 / clk_rate;
|
||||
ma35d1_wdt->wdt_dev.parent = dev;
|
||||
|
||||
val = readl_relaxed(ma35d1_wdt->wdt_base + REG_WDT_CTL);
|
||||
if (val & RSTF) {
|
||||
ma35d1_wdt->wdt_dev.bootstatus = WDIOF_CARDRESET;
|
||||
writel_relaxed(val, ma35d1_wdt->wdt_base + REG_WDT_CTL);
|
||||
}
|
||||
|
||||
if (val & WDTEN)
|
||||
set_bit(WDOG_HW_RUNNING, &ma35d1_wdt->wdt_dev.status);
|
||||
|
||||
watchdog_set_drvdata(&ma35d1_wdt->wdt_dev, ma35d1_wdt);
|
||||
watchdog_set_nowayout(&ma35d1_wdt->wdt_dev, nowayout);
|
||||
watchdog_init_timeout(&ma35d1_wdt->wdt_dev, timeout, &pdev->dev);
|
||||
|
||||
ma35d1_wdt_set_timeout(&ma35d1_wdt->wdt_dev,
|
||||
ma35d1_wdt->wdt_dev.timeout);
|
||||
|
||||
ret = devm_request_irq(dev, ma35d1_wdt->irq, ma35d1_wdt_isr, 0,
|
||||
dev_name(dev), ma35d1_wdt);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "cannot claim IRQ %d\n",
|
||||
ma35d1_wdt->irq);
|
||||
|
||||
if (device_property_read_bool(dev, "wakeup-source")) {
|
||||
ret = devm_device_init_wakeup(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = devm_pm_set_wake_irq(dev, ma35d1_wdt->irq);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
watchdog_stop_on_reboot(&ma35d1_wdt->wdt_dev);
|
||||
watchdog_stop_on_unregister(&ma35d1_wdt->wdt_dev);
|
||||
ret = devm_watchdog_register_device(dev, &ma35d1_wdt->wdt_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ma35d1_wdt_suspend(struct device *dev)
|
||||
{
|
||||
struct ma35d1_wdt_dev *ma35d1_wdt = dev_get_drvdata(dev);
|
||||
|
||||
if (watchdog_active(&ma35d1_wdt->wdt_dev) ||
|
||||
watchdog_hw_running(&ma35d1_wdt->wdt_dev)) {
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
guard(spinlock_irqsave)(&ma35d1_wdt->lock);
|
||||
val = readl_relaxed(ma35d1_wdt->wdt_base + REG_WDT_CTL);
|
||||
|
||||
if (device_may_wakeup(dev)) {
|
||||
val &= ~RSTEN;
|
||||
val |= (INTEN | WKEN);
|
||||
} else {
|
||||
val &= ~(WDTEN | RSTEN);
|
||||
}
|
||||
writel_relaxed(val, ma35d1_wdt->wdt_base + REG_WDT_CTL);
|
||||
ret = ma35d1_wdt_wait_sync(ma35d1_wdt);
|
||||
if (ret) {
|
||||
dev_err(dev, "Wait for WDTEN SYNC timeout!\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ma35d1_wdt_resume(struct device *dev)
|
||||
{
|
||||
struct ma35d1_wdt_dev *ma35d1_wdt = dev_get_drvdata(dev);
|
||||
|
||||
if (watchdog_active(&ma35d1_wdt->wdt_dev) ||
|
||||
watchdog_hw_running(&ma35d1_wdt->wdt_dev)) {
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
guard(spinlock_irqsave)(&ma35d1_wdt->lock);
|
||||
val = readl_relaxed(ma35d1_wdt->wdt_base + REG_WDT_CTL);
|
||||
|
||||
if (device_may_wakeup(dev)) {
|
||||
val |= RSTEN;
|
||||
val &= ~(INTEN | WKEN);
|
||||
} else {
|
||||
val |= (WDTEN | RSTEN);
|
||||
}
|
||||
writel_relaxed(val, ma35d1_wdt->wdt_base + REG_WDT_CTL);
|
||||
writel_relaxed(RESET_COUNTER,
|
||||
ma35d1_wdt->wdt_base + REG_WDT_RSTCNT);
|
||||
ret = ma35d1_wdt_wait_sync(ma35d1_wdt);
|
||||
if (ret) {
|
||||
dev_err(dev, "Wait for WDTEN SYNC timeout!\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static DEFINE_SIMPLE_DEV_PM_OPS(ma35d1_wdt_pm_ops, ma35d1_wdt_suspend,
|
||||
ma35d1_wdt_resume);
|
||||
|
||||
static const struct of_device_id ma35d1_wdt_dt_ids[] = {
|
||||
{ .compatible = "nuvoton,ma35d1-wdt" },
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ma35d1_wdt_dt_ids);
|
||||
|
||||
static struct platform_driver ma35d1_wdt_driver = {
|
||||
.probe = ma35d1_wdt_probe,
|
||||
.driver = {
|
||||
.name = DRV_NAME,
|
||||
.pm = pm_ptr(&ma35d1_wdt_pm_ops),
|
||||
.of_match_table = ma35d1_wdt_dt_ids,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(ma35d1_wdt_driver);
|
||||
|
||||
module_param(timeout, uint, 0);
|
||||
MODULE_PARM_DESC(timeout, "Watchdog heartbeat in seconds");
|
||||
|
||||
module_param(nowayout, bool, 0);
|
||||
MODULE_PARM_DESC(
|
||||
nowayout,
|
||||
"Watchdog cannot be stopped once started (default=" __MODULE_STRING(
|
||||
WATCHDOG_NOWAYOUT) ")");
|
||||
|
||||
MODULE_AUTHOR("Zi-Yu Chen <zychennvt@gmail.com>");
|
||||
MODULE_DESCRIPTION("Nuvoton MA35D1 Watchdog Timer Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -321,7 +321,7 @@ static int gti_wdt_probe(struct platform_device *pdev)
|
||||
err = devm_request_irq(dev, irq, gti_wdt_interrupt, 0,
|
||||
pdev->name, &priv->wdev);
|
||||
if (err)
|
||||
return dev_err_probe(dev, err, "Failed to register interrupt handler\n");
|
||||
return err;
|
||||
|
||||
dev_info(dev, "Watchdog enabled (timeout=%d sec)\n", wdog_dev->timeout);
|
||||
return 0;
|
||||
|
||||
@@ -245,13 +245,13 @@ static int max63xx_wdt_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
static const struct platform_device_id max63xx_id_table[] = {
|
||||
{ "max6369_wdt", (kernel_ulong_t)max6369_table, },
|
||||
{ "max6370_wdt", (kernel_ulong_t)max6369_table, },
|
||||
{ "max6371_wdt", (kernel_ulong_t)max6371_table, },
|
||||
{ "max6372_wdt", (kernel_ulong_t)max6371_table, },
|
||||
{ "max6373_wdt", (kernel_ulong_t)max6373_table, },
|
||||
{ "max6374_wdt", (kernel_ulong_t)max6373_table, },
|
||||
{ },
|
||||
{ .name = "max6369_wdt", .driver_data = (kernel_ulong_t)max6369_table },
|
||||
{ .name = "max6370_wdt", .driver_data = (kernel_ulong_t)max6369_table },
|
||||
{ .name = "max6371_wdt", .driver_data = (kernel_ulong_t)max6371_table },
|
||||
{ .name = "max6372_wdt", .driver_data = (kernel_ulong_t)max6371_table },
|
||||
{ .name = "max6373_wdt", .driver_data = (kernel_ulong_t)max6373_table },
|
||||
{ .name = "max6374_wdt", .driver_data = (kernel_ulong_t)max6373_table },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, max63xx_id_table);
|
||||
|
||||
|
||||
@@ -235,9 +235,9 @@ static int max77620_wdt_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
static const struct platform_device_id max77620_wdt_devtype[] = {
|
||||
{ "max77620-watchdog", (kernel_ulong_t)&max77620_wdt_data },
|
||||
{ "max77714-watchdog", (kernel_ulong_t)&max77714_wdt_data },
|
||||
{ },
|
||||
{ .name = "max77620-watchdog", .driver_data = (kernel_ulong_t)&max77620_wdt_data },
|
||||
{ .name = "max77714-watchdog", .driver_data = (kernel_ulong_t)&max77714_wdt_data },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, max77620_wdt_devtype);
|
||||
|
||||
|
||||
@@ -422,8 +422,8 @@ static int mtk_wdt_probe(struct platform_device *pdev)
|
||||
mtk_wdt->wdt_dev.info = &mtk_wdt_pt_info;
|
||||
mtk_wdt->wdt_dev.pretimeout = WDT_MAX_TIMEOUT / 2;
|
||||
} else {
|
||||
if (irq == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
if (irq != -ENXIO)
|
||||
return irq;
|
||||
|
||||
mtk_wdt->wdt_dev.info = &mtk_wdt_info;
|
||||
}
|
||||
|
||||
+215
-2
@@ -7,14 +7,52 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/watchdog.h>
|
||||
|
||||
#define NPCM_WTCR 0x1C
|
||||
|
||||
/* NPCM GCR module */
|
||||
#define NPCM_RESSR_OFFSET 0x6C
|
||||
#define NPCM_INTCR2_OFFSET 0x60
|
||||
#define NPCM7XX_SCRPAD2_OFFSET 0x84
|
||||
#define NPCM8XX_SCRPAD10_OFFSET 0xE28
|
||||
|
||||
#define NPCM_PORST BIT(31)
|
||||
#define NPCM_CORST BIT(30)
|
||||
#define NPCM_WD0RST BIT(29)
|
||||
#define NPCM_SWR1RST BIT(28)
|
||||
#define NPCM_SWR2RST BIT(27)
|
||||
#define NPCM_SWR3RST BIT(26)
|
||||
#define NPCM_SWR4RST BIT(25)
|
||||
#define NPCM_WD1RST BIT(24)
|
||||
#define NPCM_WD2RST BIT(23)
|
||||
#define NPCM_RST GENMASK(31, 23)
|
||||
#define NPCM8XX_TIP_RESET BIT(25) /* Replaces SWRST4 on NPCM8xx */
|
||||
|
||||
/* Per-instance mapping of MMIO base address to its RESSR/INTCR2 reset bit. */
|
||||
struct npcm_wdt_rst_map {
|
||||
phys_addr_t base;
|
||||
u32 rst_bit;
|
||||
};
|
||||
|
||||
struct npcm_wdt_status_map {
|
||||
u32 rst_bit;
|
||||
u32 wdiof_flag;
|
||||
};
|
||||
|
||||
struct npcm_wdt_data {
|
||||
const struct npcm_wdt_rst_map *rst_map;
|
||||
unsigned int rst_map_size;
|
||||
const struct npcm_wdt_status_map *status_map;
|
||||
unsigned int status_map_size;
|
||||
};
|
||||
|
||||
#define NPCM_WTCLK (BIT(10) | BIT(11)) /* Clock divider */
|
||||
#define NPCM_WTE BIT(7) /* Enable */
|
||||
#define NPCM_WTIE BIT(6) /* Enable irq */
|
||||
@@ -47,6 +85,50 @@ struct npcm_wdt {
|
||||
struct clk *clk;
|
||||
};
|
||||
|
||||
static const struct npcm_wdt_rst_map npcm750_rst_map[] = {
|
||||
{ 0xf000801c, NPCM_WD0RST },
|
||||
{ 0xf000901c, NPCM_WD1RST },
|
||||
{ 0xf000a01c, NPCM_WD2RST },
|
||||
};
|
||||
|
||||
static const struct npcm_wdt_status_map npcm750_status_map[] = {
|
||||
{ NPCM_PORST, WDIOF_OVERHEAT },
|
||||
{ NPCM_CORST, WDIOF_FANFAULT },
|
||||
{ NPCM_SWR1RST, WDIOF_EXTERN1 },
|
||||
{ NPCM_SWR2RST, WDIOF_EXTERN2 },
|
||||
{ NPCM_SWR3RST, WDIOF_POWERUNDER },
|
||||
{ NPCM_SWR4RST, WDIOF_POWEROVER },
|
||||
};
|
||||
|
||||
static const struct npcm_wdt_data __maybe_unused npcm750_data = {
|
||||
.rst_map = npcm750_rst_map,
|
||||
.rst_map_size = ARRAY_SIZE(npcm750_rst_map),
|
||||
.status_map = npcm750_status_map,
|
||||
.status_map_size = ARRAY_SIZE(npcm750_status_map),
|
||||
};
|
||||
|
||||
static const struct npcm_wdt_rst_map npcm845_rst_map[] = {
|
||||
{ 0xf000801c, NPCM_WD0RST },
|
||||
{ 0xf000901c, NPCM_WD1RST },
|
||||
{ 0xf000a01c, NPCM_WD2RST },
|
||||
};
|
||||
|
||||
static const struct npcm_wdt_status_map npcm845_status_map[] = {
|
||||
{ NPCM_PORST, WDIOF_OVERHEAT },
|
||||
{ NPCM_CORST, WDIOF_FANFAULT },
|
||||
{ NPCM_SWR1RST, WDIOF_EXTERN1 },
|
||||
{ NPCM_SWR2RST, WDIOF_EXTERN2 },
|
||||
{ NPCM_SWR3RST, WDIOF_POWERUNDER },
|
||||
{ NPCM8XX_TIP_RESET, WDIOF_POWEROVER },
|
||||
};
|
||||
|
||||
static const struct npcm_wdt_data __maybe_unused npcm845_data = {
|
||||
.rst_map = npcm845_rst_map,
|
||||
.rst_map_size = ARRAY_SIZE(npcm845_rst_map),
|
||||
.status_map = npcm845_status_map,
|
||||
.status_map_size = ARRAY_SIZE(npcm845_status_map),
|
||||
};
|
||||
|
||||
static inline struct npcm_wdt *to_npcm_wdt(struct watchdog_device *wdd)
|
||||
{
|
||||
return container_of(wdd, struct npcm_wdt, wdd);
|
||||
@@ -169,6 +251,111 @@ static bool npcm_is_running(struct watchdog_device *wdd)
|
||||
return readl(wdt->reg) & NPCM_WTE;
|
||||
}
|
||||
|
||||
static void npcm_get_reset_status(struct npcm_wdt *wdt, struct device *dev,
|
||||
const struct npcm_wdt_data *data,
|
||||
resource_size_t start)
|
||||
{
|
||||
struct regmap *gcr_regmap;
|
||||
u32 rstval = 0;
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
if (!data)
|
||||
return;
|
||||
|
||||
gcr_regmap = syscon_regmap_lookup_by_phandle(dev->of_node,
|
||||
"nuvoton,sysgcr");
|
||||
if (IS_ERR(gcr_regmap)) {
|
||||
dev_warn(dev,
|
||||
"Failed to find nuvoton,sysgcr, WD reset status not supported\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (of_device_is_compatible(dev->of_node, "nuvoton,npcm845-wdt")) {
|
||||
ret = regmap_read(gcr_regmap, NPCM_INTCR2_OFFSET, &rstval);
|
||||
if (ret) {
|
||||
dev_warn(dev, "Failed to read INTCR2 reset status: %d\n",
|
||||
ret);
|
||||
return;
|
||||
}
|
||||
|
||||
if (rstval & NPCM_RST) {
|
||||
ret = regmap_write(gcr_regmap, NPCM_INTCR2_OFFSET,
|
||||
rstval & ~NPCM_RST);
|
||||
if (ret) {
|
||||
dev_warn(dev,
|
||||
"Failed to clear INTCR2 reset status: %d\n",
|
||||
ret);
|
||||
return;
|
||||
}
|
||||
|
||||
ret = regmap_write(gcr_regmap, NPCM8XX_SCRPAD10_OFFSET,
|
||||
rstval);
|
||||
if (ret) {
|
||||
dev_warn(dev,
|
||||
"Failed to cache reset status in SCRPAD10: %d\n",
|
||||
ret);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
ret = regmap_read(gcr_regmap, NPCM8XX_SCRPAD10_OFFSET,
|
||||
&rstval);
|
||||
if (ret) {
|
||||
dev_warn(dev,
|
||||
"Failed to read cached reset status from SCRPAD10: %d\n",
|
||||
ret);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else if (of_device_is_compatible(dev->of_node, "nuvoton,npcm750-wdt")) {
|
||||
ret = regmap_read(gcr_regmap, NPCM_RESSR_OFFSET, &rstval);
|
||||
if (ret) {
|
||||
dev_warn(dev, "Failed to read RESSR reset status: %d\n",
|
||||
ret);
|
||||
return;
|
||||
}
|
||||
|
||||
if (rstval & NPCM_RST) {
|
||||
ret = regmap_write(gcr_regmap, NPCM_RESSR_OFFSET,
|
||||
rstval & ~NPCM_RST);
|
||||
if (ret) {
|
||||
dev_warn(dev, "Failed to clear RESSR reset status: %d\n", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
ret = regmap_write(gcr_regmap, NPCM7XX_SCRPAD2_OFFSET,
|
||||
rstval);
|
||||
if (ret) {
|
||||
dev_warn(dev,
|
||||
"Failed to cache reset status in SCRPAD2: %d\n", ret);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
ret = regmap_read(gcr_regmap, NPCM7XX_SCRPAD2_OFFSET,
|
||||
&rstval);
|
||||
if (ret) {
|
||||
dev_warn(dev,
|
||||
"Failed to read cached reset status from SCRPAD2: %d\n",
|
||||
ret);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < data->status_map_size; i++) {
|
||||
if (rstval & data->status_map[i].rst_bit)
|
||||
wdt->wdd.bootstatus |= data->status_map[i].wdiof_flag;
|
||||
}
|
||||
|
||||
for (i = 0; i < data->rst_map_size; i++) {
|
||||
if (data->rst_map[i].base == start &&
|
||||
rstval & data->rst_map[i].rst_bit) {
|
||||
wdt->wdd.bootstatus |= WDIOF_CARDRESET;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const struct watchdog_info npcm_wdt_info = {
|
||||
.identity = KBUILD_MODNAME,
|
||||
.options = WDIOF_SETTIMEOUT
|
||||
@@ -176,6 +363,20 @@ static const struct watchdog_info npcm_wdt_info = {
|
||||
| WDIOF_MAGICCLOSE,
|
||||
};
|
||||
|
||||
static const struct watchdog_info npcm_wdt_rst_info = {
|
||||
.identity = KBUILD_MODNAME,
|
||||
.options = WDIOF_SETTIMEOUT
|
||||
| WDIOF_KEEPALIVEPING
|
||||
| WDIOF_MAGICCLOSE
|
||||
| WDIOF_CARDRESET
|
||||
| WDIOF_OVERHEAT
|
||||
| WDIOF_FANFAULT
|
||||
| WDIOF_EXTERN1
|
||||
| WDIOF_EXTERN2
|
||||
| WDIOF_POWERUNDER
|
||||
| WDIOF_POWEROVER,
|
||||
};
|
||||
|
||||
static const struct watchdog_ops npcm_wdt_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.start = npcm_wdt_start,
|
||||
@@ -188,7 +389,10 @@ static const struct watchdog_ops npcm_wdt_ops = {
|
||||
static int npcm_wdt_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
const struct npcm_wdt_data *data = device_get_match_data(dev);
|
||||
struct resource *res;
|
||||
struct npcm_wdt *wdt;
|
||||
resource_size_t start;
|
||||
int irq;
|
||||
int ret;
|
||||
|
||||
@@ -196,10 +400,16 @@ static int npcm_wdt_probe(struct platform_device *pdev)
|
||||
if (!wdt)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res)
|
||||
return -EINVAL;
|
||||
|
||||
wdt->reg = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(wdt->reg))
|
||||
return PTR_ERR(wdt->reg);
|
||||
|
||||
start = res->start;
|
||||
|
||||
wdt->clk = devm_clk_get_optional(&pdev->dev, NULL);
|
||||
if (IS_ERR(wdt->clk))
|
||||
return PTR_ERR(wdt->clk);
|
||||
@@ -208,7 +418,7 @@ static int npcm_wdt_probe(struct platform_device *pdev)
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
wdt->wdd.info = &npcm_wdt_info;
|
||||
wdt->wdd.info = data ? &npcm_wdt_rst_info : &npcm_wdt_info;
|
||||
wdt->wdd.ops = &npcm_wdt_ops;
|
||||
wdt->wdd.min_timeout = 1;
|
||||
wdt->wdd.max_timeout = 2750;
|
||||
@@ -220,6 +430,8 @@ static int npcm_wdt_probe(struct platform_device *pdev)
|
||||
/* Ensure timeout is able to be represented by the hardware */
|
||||
npcm_wdt_set_timeout(&wdt->wdd, wdt->wdd.timeout);
|
||||
|
||||
npcm_get_reset_status(wdt, dev, data, start);
|
||||
|
||||
if (npcm_is_running(&wdt->wdd)) {
|
||||
/* Restart with the default or device-tree specified timeout */
|
||||
npcm_wdt_start(&wdt->wdd);
|
||||
@@ -243,7 +455,8 @@ static int npcm_wdt_probe(struct platform_device *pdev)
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id npcm_wdt_match[] = {
|
||||
{.compatible = "nuvoton,wpcm450-wdt"},
|
||||
{.compatible = "nuvoton,npcm750-wdt"},
|
||||
{.compatible = "nuvoton,npcm750-wdt", .data = &npcm750_data},
|
||||
{.compatible = "nuvoton,npcm845-wdt", .data = &npcm845_data},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, npcm_wdt_match);
|
||||
|
||||
@@ -606,6 +606,10 @@ static int orion_wdt_probe(struct platform_device *pdev)
|
||||
|
||||
/* Request the IRQ only after the watchdog is disabled */
|
||||
irq = platform_get_irq_optional(pdev, 0);
|
||||
if (irq < 0 && irq != -ENXIO) {
|
||||
ret = irq;
|
||||
goto disable_clk;
|
||||
}
|
||||
if (irq > 0) {
|
||||
/*
|
||||
* Not all supported platforms specify an interrupt for the
|
||||
@@ -613,22 +617,22 @@ static int orion_wdt_probe(struct platform_device *pdev)
|
||||
*/
|
||||
ret = devm_request_irq(&pdev->dev, irq, orion_wdt_irq, 0,
|
||||
pdev->name, dev);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed to request IRQ\n");
|
||||
if (ret < 0)
|
||||
goto disable_clk;
|
||||
}
|
||||
}
|
||||
|
||||
/* Optional 2nd interrupt for pretimeout */
|
||||
irq = platform_get_irq_optional(pdev, 1);
|
||||
if (irq < 0 && irq != -ENXIO) {
|
||||
ret = irq;
|
||||
goto disable_clk;
|
||||
}
|
||||
if (irq > 0) {
|
||||
orion_wdt_info.options |= WDIOF_PRETIMEOUT;
|
||||
ret = devm_request_irq(&pdev->dev, irq, orion_wdt_pre_irq,
|
||||
0, pdev->name, dev);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed to request IRQ\n");
|
||||
if (ret < 0)
|
||||
goto disable_clk;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright 2026 Google LLC
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/nmi.h>
|
||||
#include <linux/watchdog.h>
|
||||
|
||||
#include "watchdog_pretimeout.h"
|
||||
|
||||
/**
|
||||
* pretimeout_dump - Dump on watchdog pretimeout event
|
||||
* @wdd: watchdog_device
|
||||
*
|
||||
* Dump all cpu backtrace on pretimeout event.
|
||||
*/
|
||||
static void pretimeout_dump(struct watchdog_device *wdd)
|
||||
{
|
||||
pr_alert("watchdog%d: pretimeout event\n", wdd->id);
|
||||
if (!trigger_all_cpu_backtrace())
|
||||
pr_alert("trigger_all_cpu_backtrace() isn't available\n");
|
||||
}
|
||||
|
||||
static struct watchdog_governor watchdog_gov_dump = {
|
||||
.name = "dump",
|
||||
.pretimeout = pretimeout_dump,
|
||||
};
|
||||
|
||||
static int __init watchdog_gov_dump_register(void)
|
||||
{
|
||||
return watchdog_register_governor(&watchdog_gov_dump);
|
||||
}
|
||||
|
||||
static void __exit watchdog_gov_dump_unregister(void)
|
||||
{
|
||||
watchdog_unregister_governor(&watchdog_gov_dump);
|
||||
}
|
||||
module_init(watchdog_gov_dump_register);
|
||||
module_exit(watchdog_gov_dump_unregister);
|
||||
|
||||
MODULE_AUTHOR("Tzung-Bi Shih <tzungbi@kernel.org>");
|
||||
MODULE_DESCRIPTION("Dump watchdog pretimeout governor");
|
||||
MODULE_LICENSE("GPL");
|
||||
+37
-18
@@ -44,6 +44,7 @@ struct qcom_wdt_match_data {
|
||||
bool pretimeout;
|
||||
u32 max_tick_count;
|
||||
u32 wdt_reason_val;
|
||||
u32 powerunder_reason_val;
|
||||
};
|
||||
|
||||
struct qcom_wdt {
|
||||
@@ -164,7 +165,8 @@ static const struct watchdog_info qcom_wdt_info = {
|
||||
.options = WDIOF_KEEPALIVEPING
|
||||
| WDIOF_MAGICCLOSE
|
||||
| WDIOF_SETTIMEOUT
|
||||
| WDIOF_CARDRESET,
|
||||
| WDIOF_CARDRESET
|
||||
| WDIOF_POWERUNDER,
|
||||
.identity = KBUILD_MODNAME,
|
||||
};
|
||||
|
||||
@@ -173,7 +175,8 @@ static const struct watchdog_info qcom_wdt_pt_info = {
|
||||
| WDIOF_MAGICCLOSE
|
||||
| WDIOF_SETTIMEOUT
|
||||
| WDIOF_PRETIMEOUT
|
||||
| WDIOF_CARDRESET,
|
||||
| WDIOF_CARDRESET
|
||||
| WDIOF_POWERUNDER,
|
||||
.identity = KBUILD_MODNAME,
|
||||
};
|
||||
|
||||
@@ -188,6 +191,15 @@ static const struct qcom_wdt_match_data match_data_ipq5424 = {
|
||||
.pretimeout = true,
|
||||
.max_tick_count = 0xFFFFFU,
|
||||
.wdt_reason_val = 5,
|
||||
.powerunder_reason_val = 1,
|
||||
};
|
||||
|
||||
static const struct qcom_wdt_match_data match_data_ipq9574 = {
|
||||
.offset = reg_offset_data_kpss,
|
||||
.pretimeout = true,
|
||||
.max_tick_count = 0xFFFFFU,
|
||||
.wdt_reason_val = 1,
|
||||
.powerunder_reason_val = 32,
|
||||
};
|
||||
|
||||
static const struct qcom_wdt_match_data match_data_kpss = {
|
||||
@@ -197,12 +209,13 @@ static const struct qcom_wdt_match_data match_data_kpss = {
|
||||
};
|
||||
|
||||
static int qcom_wdt_get_bootstatus(struct device *dev, struct qcom_wdt *wdt,
|
||||
u32 val)
|
||||
const struct qcom_wdt_match_data *data)
|
||||
{
|
||||
struct device_node *imem;
|
||||
struct resource res;
|
||||
void __iomem *addr;
|
||||
int ret;
|
||||
int val;
|
||||
|
||||
imem = of_parse_phandle(dev->of_node, "sram", 0);
|
||||
if (!imem) {
|
||||
@@ -222,8 +235,11 @@ static int qcom_wdt_get_bootstatus(struct device *dev, struct qcom_wdt *wdt,
|
||||
if (!addr)
|
||||
return -ENOMEM;
|
||||
|
||||
if (readl(addr) == val)
|
||||
val = readl(addr);
|
||||
if (val == data->wdt_reason_val)
|
||||
wdt->wdd.bootstatus = WDIOF_CARDRESET;
|
||||
else if (val == data->powerunder_reason_val)
|
||||
wdt->wdd.bootstatus = WDIOF_POWERUNDER;
|
||||
|
||||
iounmap(addr);
|
||||
|
||||
@@ -287,21 +303,22 @@ static int qcom_wdt_probe(struct platform_device *pdev)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
wdt->wdd.info = &qcom_wdt_info;
|
||||
|
||||
/* check if there is pretimeout support */
|
||||
irq = platform_get_irq_optional(pdev, 0);
|
||||
if (data->pretimeout && irq > 0) {
|
||||
ret = devm_request_irq(dev, irq, qcom_wdt_isr, 0,
|
||||
"wdt_bark", &wdt->wdd);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (data->pretimeout) {
|
||||
irq = platform_get_irq_optional(pdev, 0);
|
||||
if (irq < 0 && irq != -ENXIO)
|
||||
return irq;
|
||||
if (irq > 0) {
|
||||
ret = devm_request_irq(dev, irq, qcom_wdt_isr, 0,
|
||||
"wdt_bark", &wdt->wdd);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
wdt->wdd.info = &qcom_wdt_pt_info;
|
||||
wdt->wdd.pretimeout = 1;
|
||||
} else {
|
||||
if (irq == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
wdt->wdd.info = &qcom_wdt_info;
|
||||
wdt->wdd.info = &qcom_wdt_pt_info;
|
||||
wdt->wdd.pretimeout = 1;
|
||||
}
|
||||
}
|
||||
|
||||
wdt->wdd.ops = &qcom_wdt_ops;
|
||||
@@ -310,7 +327,7 @@ static int qcom_wdt_probe(struct platform_device *pdev)
|
||||
wdt->wdd.parent = dev;
|
||||
wdt->layout = data->offset;
|
||||
|
||||
ret = qcom_wdt_get_bootstatus(dev, wdt, data->wdt_reason_val);
|
||||
ret = qcom_wdt_get_bootstatus(dev, wdt, data);
|
||||
if (ret)
|
||||
dev_err(dev, "failed to get the bootstatus, %d\n", ret);
|
||||
|
||||
@@ -366,7 +383,9 @@ static const struct dev_pm_ops qcom_wdt_pm_ops = {
|
||||
};
|
||||
|
||||
static const struct of_device_id qcom_wdt_of_table[] = {
|
||||
{ .compatible = "qcom,apss-wdt-ipq5332", .data = &match_data_ipq9574 },
|
||||
{ .compatible = "qcom,apss-wdt-ipq5424", .data = &match_data_ipq5424 },
|
||||
{ .compatible = "qcom,apss-wdt-ipq9574", .data = &match_data_ipq9574 },
|
||||
{ .compatible = "qcom,kpss-timer", .data = &match_data_apcs_tmr },
|
||||
{ .compatible = "qcom,scss-timer", .data = &match_data_apcs_tmr },
|
||||
{ .compatible = "qcom,kpss-wdt", .data = &match_data_kpss },
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/watchdog.h>
|
||||
|
||||
#define OTTO_WDT_REG_CNTR 0x0
|
||||
@@ -65,7 +66,7 @@
|
||||
struct otto_wdt_ctrl {
|
||||
struct watchdog_device wdev;
|
||||
struct device *dev;
|
||||
void __iomem *base;
|
||||
struct regmap *regmap;
|
||||
unsigned int clk_rate_khz;
|
||||
int irq_phase1;
|
||||
};
|
||||
@@ -73,24 +74,17 @@ struct otto_wdt_ctrl {
|
||||
static int otto_wdt_start(struct watchdog_device *wdev)
|
||||
{
|
||||
struct otto_wdt_ctrl *ctrl = watchdog_get_drvdata(wdev);
|
||||
u32 v;
|
||||
|
||||
v = ioread32(ctrl->base + OTTO_WDT_REG_CTRL);
|
||||
v |= OTTO_WDT_CTRL_ENABLE;
|
||||
iowrite32(v, ctrl->base + OTTO_WDT_REG_CTRL);
|
||||
|
||||
regmap_set_bits(ctrl->regmap, OTTO_WDT_REG_CTRL, OTTO_WDT_CTRL_ENABLE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int otto_wdt_stop(struct watchdog_device *wdev)
|
||||
{
|
||||
struct otto_wdt_ctrl *ctrl = watchdog_get_drvdata(wdev);
|
||||
u32 v;
|
||||
|
||||
v = ioread32(ctrl->base + OTTO_WDT_REG_CTRL);
|
||||
v &= ~OTTO_WDT_CTRL_ENABLE;
|
||||
iowrite32(v, ctrl->base + OTTO_WDT_REG_CTRL);
|
||||
|
||||
regmap_clear_bits(ctrl->regmap, OTTO_WDT_REG_CTRL,
|
||||
OTTO_WDT_CTRL_ENABLE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -98,8 +92,7 @@ static int otto_wdt_ping(struct watchdog_device *wdev)
|
||||
{
|
||||
struct otto_wdt_ctrl *ctrl = watchdog_get_drvdata(wdev);
|
||||
|
||||
iowrite32(OTTO_WDT_CNTR_PING, ctrl->base + OTTO_WDT_REG_CNTR);
|
||||
|
||||
regmap_write(ctrl->regmap, OTTO_WDT_REG_CNTR, OTTO_WDT_CNTR_PING);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -125,7 +118,7 @@ static int otto_wdt_determine_timeouts(struct watchdog_device *wdev, unsigned in
|
||||
unsigned int total_ticks;
|
||||
unsigned int prescale;
|
||||
unsigned int tick_ms;
|
||||
u32 v;
|
||||
u32 mask, val;
|
||||
|
||||
do {
|
||||
prescale = prescale_next;
|
||||
@@ -141,14 +134,11 @@ static int otto_wdt_determine_timeouts(struct watchdog_device *wdev, unsigned in
|
||||
} while (phase1_ticks > OTTO_WDT_PHASE_TICKS_MAX
|
||||
|| phase2_ticks > OTTO_WDT_PHASE_TICKS_MAX);
|
||||
|
||||
v = ioread32(ctrl->base + OTTO_WDT_REG_CTRL);
|
||||
|
||||
v &= ~(OTTO_WDT_CTRL_PRESCALE | OTTO_WDT_CTRL_PHASE1 | OTTO_WDT_CTRL_PHASE2);
|
||||
v |= FIELD_PREP(OTTO_WDT_CTRL_PHASE1, phase1_ticks - 1);
|
||||
v |= FIELD_PREP(OTTO_WDT_CTRL_PHASE2, phase2_ticks - 1);
|
||||
v |= FIELD_PREP(OTTO_WDT_CTRL_PRESCALE, prescale);
|
||||
|
||||
iowrite32(v, ctrl->base + OTTO_WDT_REG_CTRL);
|
||||
mask = OTTO_WDT_CTRL_PRESCALE | OTTO_WDT_CTRL_PHASE1 | OTTO_WDT_CTRL_PHASE2;
|
||||
val = FIELD_PREP(OTTO_WDT_CTRL_PHASE1, phase1_ticks - 1);
|
||||
val |= FIELD_PREP(OTTO_WDT_CTRL_PHASE2, phase2_ticks - 1);
|
||||
val |= FIELD_PREP(OTTO_WDT_CTRL_PRESCALE, prescale);
|
||||
regmap_update_bits(ctrl->regmap, OTTO_WDT_REG_CTRL, mask, val);
|
||||
|
||||
timeout_ms = total_ticks * tick_ms;
|
||||
ctrl->wdev.timeout = timeout_ms / 1000;
|
||||
@@ -192,7 +182,7 @@ static int otto_wdt_restart(struct watchdog_device *wdev, unsigned long reboot_m
|
||||
|
||||
/* Configure for shortest timeout and wait for reset to occur */
|
||||
v = FIELD_PREP(OTTO_WDT_CTRL_RST_MODE, reset_mode) | OTTO_WDT_CTRL_ENABLE;
|
||||
iowrite32(v, ctrl->base + OTTO_WDT_REG_CTRL);
|
||||
regmap_write(ctrl->regmap, OTTO_WDT_REG_CTRL, v);
|
||||
|
||||
mdelay(3 * otto_wdt_tick_ms(ctrl, 0));
|
||||
|
||||
@@ -203,7 +193,7 @@ static irqreturn_t otto_wdt_phase1_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct otto_wdt_ctrl *ctrl = dev_id;
|
||||
|
||||
iowrite32(OTTO_WDT_INTR_PHASE_1, ctrl->base + OTTO_WDT_REG_INTR);
|
||||
regmap_write(ctrl->regmap, OTTO_WDT_REG_INTR, OTTO_WDT_INTR_PHASE_1);
|
||||
dev_crit(ctrl->dev, "phase 1 timeout\n");
|
||||
watchdog_notify_pretimeout(&ctrl->wdev);
|
||||
|
||||
@@ -249,7 +239,6 @@ static int otto_wdt_probe_reset_mode(struct otto_wdt_ctrl *ctrl)
|
||||
const struct fwnode_handle *node = ctrl->dev->fwnode;
|
||||
int mode_count;
|
||||
u32 mode;
|
||||
u32 v;
|
||||
|
||||
if (!node)
|
||||
return -ENXIO;
|
||||
@@ -271,19 +260,25 @@ static int otto_wdt_probe_reset_mode(struct otto_wdt_ctrl *ctrl)
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
v = ioread32(ctrl->base + OTTO_WDT_REG_CTRL);
|
||||
v &= ~OTTO_WDT_CTRL_RST_MODE;
|
||||
v |= FIELD_PREP(OTTO_WDT_CTRL_RST_MODE, mode);
|
||||
iowrite32(v, ctrl->base + OTTO_WDT_REG_CTRL);
|
||||
|
||||
regmap_update_bits(ctrl->regmap, OTTO_WDT_REG_CTRL,
|
||||
OTTO_WDT_CTRL_RST_MODE,
|
||||
FIELD_PREP(OTTO_WDT_CTRL_RST_MODE, mode));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct regmap_config realtek_otto_wdt_regmap_config = {
|
||||
.reg_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.val_bits = 32,
|
||||
.disable_locking = true,
|
||||
};
|
||||
|
||||
static int otto_wdt_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct otto_wdt_ctrl *ctrl;
|
||||
unsigned int max_tick_ms;
|
||||
void __iomem *base;
|
||||
int ret;
|
||||
|
||||
ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL);
|
||||
@@ -291,18 +286,25 @@ static int otto_wdt_probe(struct platform_device *pdev)
|
||||
return -ENOMEM;
|
||||
|
||||
ctrl->dev = dev;
|
||||
ctrl->base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(ctrl->base))
|
||||
return PTR_ERR(ctrl->base);
|
||||
base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
ctrl->regmap = devm_regmap_init_mmio(dev, base,
|
||||
&realtek_otto_wdt_regmap_config);
|
||||
if (IS_ERR(ctrl->regmap)) {
|
||||
dev_err(dev, "regmap init failed\n");
|
||||
return PTR_ERR(ctrl->regmap);
|
||||
}
|
||||
|
||||
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);
|
||||
regmap_write(ctrl->regmap, OTTO_WDT_REG_INTR,
|
||||
OTTO_WDT_INTR_PHASE_1 | OTTO_WDT_INTR_PHASE_2);
|
||||
regmap_write(ctrl->regmap, OTTO_WDT_REG_CTRL, OTTO_WDT_CTRL_DEFAULT);
|
||||
|
||||
ctrl->irq_phase1 = platform_get_irq_byname(pdev, "phase1");
|
||||
if (ctrl->irq_phase1 < 0)
|
||||
@@ -311,7 +313,7 @@ static int otto_wdt_probe(struct platform_device *pdev)
|
||||
ret = devm_request_irq(dev, ctrl->irq_phase1, otto_wdt_phase1_isr, 0,
|
||||
"realtek-otto-wdt", ctrl);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Failed to get IRQ for phase1\n");
|
||||
return ret;
|
||||
|
||||
ret = otto_wdt_probe_reset_mode(ctrl);
|
||||
if (ret)
|
||||
|
||||
@@ -291,10 +291,8 @@ static int sama5d4_wdt_probe(struct platform_device *pdev)
|
||||
ret = devm_request_irq(dev, irq, sama5d4_wdt_irq_handler,
|
||||
IRQF_SHARED | IRQF_IRQPOLL |
|
||||
IRQF_NO_SUSPEND, pdev->name, pdev);
|
||||
if (ret) {
|
||||
dev_err(dev, "cannot register interrupt handler\n");
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
watchdog_init_timeout(wdd, wdt_timeout, dev);
|
||||
|
||||
@@ -293,10 +293,8 @@ static int sprd_wdt_probe(struct platform_device *pdev)
|
||||
|
||||
ret = devm_request_irq(dev, wdt->irq, sprd_wdt_isr, IRQF_NO_SUSPEND,
|
||||
"sprd-wdt", (void *)wdt);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to register irq\n");
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
wdt->wdd.info = &sprd_wdt_info;
|
||||
wdt->wdd.ops = &sprd_wdt_ops;
|
||||
|
||||
@@ -287,8 +287,11 @@ static int stm32_iwdg_irq_init(struct platform_device *pdev,
|
||||
return 0;
|
||||
|
||||
irq = platform_get_irq_optional(pdev, 0);
|
||||
if (irq <= 0)
|
||||
if (irq < 0) {
|
||||
if (irq != -ENXIO)
|
||||
return irq;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (of_property_read_bool(np, "wakeup-source")) {
|
||||
ret = devm_device_init_wakeup(dev);
|
||||
|
||||
@@ -247,6 +247,7 @@ static const struct pci_device_id wdt_pci_table[] = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_VX855) },
|
||||
{ 0 }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, wdt_pci_table);
|
||||
|
||||
static struct pci_driver wdt_driver = {
|
||||
.name = "via_wdt",
|
||||
|
||||
@@ -49,7 +49,7 @@ static int wdt_cfg_leave = 0xAA;/* key to lock configuration space */
|
||||
enum chips { w83627hf, w83627s, w83697hf, w83697ug, w83637hf, w83627thf,
|
||||
w83687thf, w83627ehf, w83627dhg, w83627uhg, w83667hg, w83627dhg_p,
|
||||
w83667hg_b, nct6775, nct6776, nct6779, nct6791, nct6792, nct6793,
|
||||
nct6795, nct6796, nct6102, nct6116 };
|
||||
nct6795, nct6796, nct6102, nct6116, nct6126 };
|
||||
|
||||
static int timeout; /* in seconds */
|
||||
module_param(timeout, int, 0);
|
||||
@@ -94,7 +94,9 @@ MODULE_PARM_DESC(early_disable, "Disable watchdog at boot time (default=0)");
|
||||
#define NCT6775_ID 0xb4
|
||||
#define NCT6776_ID 0xc3
|
||||
#define NCT6102_ID 0xc4
|
||||
#define NCT6116_ID 0xd2
|
||||
#define NCT6116_ID 0xd2 /* also NCT6126D */
|
||||
#define NCT6126_VER_A_LOW_ID 0x83 /* ... version A */
|
||||
#define NCT6126_VER_B_LOW_ID 0x84 /* ... version B */
|
||||
#define NCT6779_ID 0xc5
|
||||
#define NCT6791_ID 0xc8
|
||||
#define NCT6792_ID 0xc9
|
||||
@@ -217,6 +219,7 @@ static int w83627hf_init(struct watchdog_device *wdog, enum chips chip)
|
||||
case nct6796:
|
||||
case nct6102:
|
||||
case nct6116:
|
||||
case nct6126:
|
||||
/*
|
||||
* These chips have a fixed WDTO# output pin (W83627UHG),
|
||||
* or support more than one WDTO# output pin.
|
||||
@@ -241,6 +244,7 @@ static int w83627hf_init(struct watchdog_device *wdog, enum chips chip)
|
||||
pr_info("Watchdog already running. Resetting timeout to %d sec\n",
|
||||
wdog->timeout);
|
||||
superio_outb(cr_wdt_timeout, wdog->timeout);
|
||||
set_bit(WDOG_HW_RUNNING, &wdog->status);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -313,9 +317,8 @@ static unsigned int wdt_get_time(struct watchdog_device *wdog)
|
||||
* Kernel Interfaces
|
||||
*/
|
||||
|
||||
static const struct watchdog_info wdt_info = {
|
||||
static struct watchdog_info wdt_info = {
|
||||
.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
|
||||
.identity = "W83627HF Watchdog",
|
||||
};
|
||||
|
||||
static const struct watchdog_ops wdt_ops = {
|
||||
@@ -428,7 +431,12 @@ static int wdt_find(int addr)
|
||||
cr_wdt_csr = NCT6102D_WDT_CSR;
|
||||
break;
|
||||
case NCT6116_ID:
|
||||
ret = nct6116;
|
||||
val = superio_inb(0x21);
|
||||
if (val == NCT6126_VER_A_LOW_ID || val == NCT6126_VER_B_LOW_ID)
|
||||
ret = nct6126;
|
||||
else
|
||||
ret = nct6116;
|
||||
|
||||
cr_wdt_timeout = NCT6102D_WDT_TIMEOUT;
|
||||
cr_wdt_control = NCT6102D_WDT_CONTROL;
|
||||
cr_wdt_csr = NCT6102D_WDT_CSR;
|
||||
@@ -499,6 +507,7 @@ static int __init wdt_init(void)
|
||||
"NCT6796",
|
||||
"NCT6102",
|
||||
"NCT6116",
|
||||
"NCT6126"
|
||||
};
|
||||
|
||||
/* Apply system-specific quirks */
|
||||
@@ -516,6 +525,9 @@ static int __init wdt_init(void)
|
||||
pr_info("WDT driver for %s Super I/O chip initialising\n",
|
||||
chip_name[chip]);
|
||||
|
||||
snprintf(wdt_info.identity, sizeof(wdt_info.identity), "%s Watchdog",
|
||||
chip_name[chip]);
|
||||
|
||||
watchdog_init_timeout(&wdt_dev, timeout, NULL);
|
||||
watchdog_set_nowayout(&wdt_dev, nowayout);
|
||||
watchdog_stop_on_reboot(&wdt_dev);
|
||||
|
||||
@@ -240,7 +240,9 @@ EXPORT_SYMBOL_GPL(watchdog_set_restart_priority);
|
||||
|
||||
static int ___watchdog_register_device(struct watchdog_device *wdd)
|
||||
{
|
||||
int ret, id = -1;
|
||||
int ret, min_id, id = -1;
|
||||
struct device_node *np;
|
||||
char alias[16];
|
||||
|
||||
if (wdd == NULL || wdd->info == NULL || wdd->ops == NULL)
|
||||
return -EINVAL;
|
||||
@@ -265,8 +267,26 @@ static int ___watchdog_register_device(struct watchdog_device *wdd)
|
||||
GFP_KERNEL);
|
||||
}
|
||||
|
||||
if (id < 0)
|
||||
id = ida_alloc_max(&watchdog_ida, MAX_DOGS - 1, GFP_KERNEL);
|
||||
/*
|
||||
* Find an id which is not pre-assigned via a DT alias to some
|
||||
* other, possibly not yet probed, watchdog device.
|
||||
*/
|
||||
if (id < 0) {
|
||||
np = of_find_node_by_path("/aliases");
|
||||
|
||||
for (min_id = 0; ; min_id = id + 1) {
|
||||
id = ida_alloc_range(&watchdog_ida, min_id, MAX_DOGS - 1,
|
||||
GFP_KERNEL);
|
||||
if (!np || id < 0)
|
||||
break;
|
||||
|
||||
scnprintf(alias, sizeof(alias), "watchdog%d", id);
|
||||
if (!of_get_property(np, alias, NULL))
|
||||
break;
|
||||
ida_free(&watchdog_ida, id);
|
||||
}
|
||||
of_node_put(np);
|
||||
}
|
||||
|
||||
if (id < 0)
|
||||
return id;
|
||||
|
||||
@@ -24,7 +24,9 @@ int watchdog_pretimeout_governor_get(struct watchdog_device *wdd, char *buf);
|
||||
int watchdog_pretimeout_governor_set(struct watchdog_device *wdd,
|
||||
const char *buf);
|
||||
|
||||
#if IS_ENABLED(CONFIG_WATCHDOG_PRETIMEOUT_DEFAULT_GOV_NOOP)
|
||||
#if IS_ENABLED(CONFIG_WATCHDOG_PRETIMEOUT_DEFAULT_GOV_DUMP)
|
||||
#define WATCHDOG_PRETIMEOUT_DEFAULT_GOV "dump"
|
||||
#elif IS_ENABLED(CONFIG_WATCHDOG_PRETIMEOUT_DEFAULT_GOV_NOOP)
|
||||
#define WATCHDOG_PRETIMEOUT_DEFAULT_GOV "noop"
|
||||
#elif IS_ENABLED(CONFIG_WATCHDOG_PRETIMEOUT_DEFAULT_GOV_PANIC)
|
||||
#define WATCHDOG_PRETIMEOUT_DEFAULT_GOV "panic"
|
||||
|
||||
@@ -7,7 +7,9 @@
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm.h>
|
||||
@@ -309,6 +311,29 @@ static struct watchdog_ops wdat_wdt_ops = {
|
||||
.set_timeout = wdat_wdt_set_timeout,
|
||||
};
|
||||
|
||||
static void __iomem *wdat_wdt_map_mem(struct device *dev, struct resource *res)
|
||||
{
|
||||
resource_size_t size = resource_size(res);
|
||||
void *addr;
|
||||
|
||||
/* Map memory region without reserving it if it falls inside ACPI NVS */
|
||||
if (region_intersects(res->start, size, IORESOURCE_MEM,
|
||||
IORES_DESC_ACPI_NV_STORAGE) == REGION_INTERSECTS) {
|
||||
dev_warn(dev, "%pR is inside ACPI NVS, mapping without reservation\n",
|
||||
res);
|
||||
|
||||
addr = devm_memremap(dev, res->start, size, MEMREMAP_WB);
|
||||
if (IS_ERR(addr)) {
|
||||
dev_err(dev, "failed to map resource %pR\n", res);
|
||||
return IOMEM_ERR_PTR(PTR_ERR(addr));
|
||||
}
|
||||
|
||||
return (void __iomem __force *)addr;
|
||||
}
|
||||
|
||||
return devm_ioremap_resource(dev, res);
|
||||
}
|
||||
|
||||
static int wdat_wdt_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
@@ -362,7 +387,7 @@ static int wdat_wdt_probe(struct platform_device *pdev)
|
||||
|
||||
res = &pdev->resource[i];
|
||||
if (resource_type(res) == IORESOURCE_MEM) {
|
||||
reg = devm_ioremap_resource(dev, res);
|
||||
reg = wdat_wdt_map_mem(dev, res);
|
||||
if (IS_ERR(reg)) {
|
||||
ret = PTR_ERR(reg);
|
||||
goto out_put_table;
|
||||
|
||||
+6
-4
@@ -157,27 +157,29 @@ static inline void touch_nmi_watchdog(void)
|
||||
* to allow calling code to fall back to some other mechanism:
|
||||
*/
|
||||
#ifdef arch_trigger_cpumask_backtrace
|
||||
void cpumask_backtrace(const cpumask_t *mask, int exclude_cpu);
|
||||
|
||||
static inline bool trigger_all_cpu_backtrace(void)
|
||||
{
|
||||
arch_trigger_cpumask_backtrace(cpu_online_mask, -1);
|
||||
cpumask_backtrace(cpu_online_mask, -1);
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool trigger_allbutcpu_cpu_backtrace(int exclude_cpu)
|
||||
{
|
||||
arch_trigger_cpumask_backtrace(cpu_online_mask, exclude_cpu);
|
||||
cpumask_backtrace(cpu_online_mask, exclude_cpu);
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool trigger_cpumask_backtrace(struct cpumask *mask)
|
||||
{
|
||||
arch_trigger_cpumask_backtrace(mask, -1);
|
||||
cpumask_backtrace(mask, -1);
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool trigger_single_cpu_backtrace(int cpu)
|
||||
{
|
||||
arch_trigger_cpumask_backtrace(cpumask_of(cpu), -1);
|
||||
cpumask_backtrace(cpumask_of(cpu), -1);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <linux/stringify.h>
|
||||
#include <linux/nmi.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/sched/debug.h>
|
||||
|
||||
#ifdef arch_trigger_cpumask_backtrace
|
||||
@@ -129,4 +130,10 @@ bool nmi_cpu_backtrace(struct pt_regs *regs)
|
||||
return false;
|
||||
}
|
||||
NOKPROBE_SYMBOL(nmi_cpu_backtrace);
|
||||
|
||||
void cpumask_backtrace(const cpumask_t *mask, int exclude_cpu)
|
||||
{
|
||||
arch_trigger_cpumask_backtrace(mask, exclude_cpu);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cpumask_backtrace);
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user