Merge git://www.linux-watchdog.org/linux-watchdog
Pull watchdog updates from Wim Van Sebroeck: - add support for Fintek F81865 Super-IO chip - add support for watchdogs (RWDT and SWDT) found on RCar Gen3 based SoCs from Renesas - octeon: Handle the FROZEN hot plug notifier actions - f71808e_wdt fixes and cleanups - some small improvements in code and documentation * git://www.linux-watchdog.org/linux-watchdog: MAINTAINERS: Add file patterns for watchdog device tree bindings Documentation: Add ebc-c384_wdt watchdog-parameters.txt entry watchdog: shwdt: Use setup_timer() watchdog: cpwd: Use setup_timer() arm64: defconfig: enable Renesas Watchdog Timer watchdog: renesas-wdt: add driver watchdog: remove error message when unable to allocate watchdog device watchdog: f71808e_wdt: Fix WDTMOUT_STS register read watchdog: f71808e_wdt: Fix typo watchdog: f71808e_wdt: Add F81865 support watchdog: sp5100_tco: properly check for new register layouts watchdog: core: Fix circular locking dependency watchdog: core: fix trivial typo in a comment watchdog: hpwdt: Adjust documentation to match latest kernel module parameters. watchdog: imx2_wdt: add external reset support via dt prop watchdog: octeon: Handle the FROZEN hot plug notifier actions. watchdog: qcom: Report reboot reason
This commit is contained in:
commit
1f93d2abf4
|
@ -5,10 +5,12 @@ Required properties:
|
|||
- reg : Should contain WDT registers location and length
|
||||
- interrupts : Should contain WDT interrupt
|
||||
|
||||
Optional property:
|
||||
Optional properties:
|
||||
- big-endian: If present the watchdog device's registers are implemented
|
||||
in big endian mode, otherwise in native mode(same with CPU), for more
|
||||
detail please see: Documentation/devicetree/bindings/regmap/regmap.txt.
|
||||
- fsl,ext-reset-output: If present the watchdog device is configured to
|
||||
assert its external reset (WDOG_B) instead of issuing a software reset.
|
||||
|
||||
Examples:
|
||||
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
Renesas Watchdog Timer (WDT) Controller
|
||||
|
||||
Required properties:
|
||||
- compatible : Should be "renesas,r8a7795-wdt", or "renesas,rcar-gen3-wdt"
|
||||
|
||||
When compatible with the generic version, nodes must list the SoC-specific
|
||||
version corresponding to the platform first, followed by the generic
|
||||
version.
|
||||
|
||||
- reg : Should contain WDT registers location and length
|
||||
- clocks : the clock feeding the watchdog timer.
|
||||
|
||||
Optional properties:
|
||||
- timeout-sec : Contains the watchdog timeout in seconds
|
||||
- power-domains : the power domain the WDT belongs to
|
||||
|
||||
Examples:
|
||||
|
||||
wdt0: watchdog@e6020000 {
|
||||
compatible = "renesas,r8a7795-wdt", "renesas,rcar-gen3-wdt";
|
||||
reg = <0 0xe6020000 0 0x0c>;
|
||||
clocks = <&cpg CPG_MOD 402>;
|
||||
power-domains = <&cpg>;
|
||||
timeout-sec = <60>;
|
||||
};
|
|
@ -1,64 +1,67 @@
|
|||
Last reviewed: 06/02/2009
|
||||
Last reviewed: 04/04/2016
|
||||
|
||||
HP iLO2 NMI Watchdog Driver
|
||||
NMI sourcing for iLO2 based ProLiant Servers
|
||||
HPE iLO NMI Watchdog Driver
|
||||
NMI sourcing for iLO based ProLiant Servers
|
||||
Documentation and Driver by
|
||||
Thomas Mingarelli <thomas.mingarelli@hp.com>
|
||||
Thomas Mingarelli <thomas.mingarelli@hpe.com>
|
||||
|
||||
The HP iLO2 NMI Watchdog driver is a kernel module that provides basic
|
||||
The HPE iLO NMI Watchdog driver is a kernel module that provides basic
|
||||
watchdog functionality and the added benefit of NMI sourcing. Both the
|
||||
watchdog functionality and the NMI sourcing capability need to be enabled
|
||||
by the user. Remember that the two modes are not dependent on one another.
|
||||
A user can have the NMI sourcing without the watchdog timer and vice-versa.
|
||||
All references to iLO in this document imply it also works on iLO2 and all
|
||||
subsequent generations.
|
||||
|
||||
Watchdog functionality is enabled like any other common watchdog driver. That
|
||||
is, an application needs to be started that kicks off the watchdog timer. A
|
||||
basic application exists in the Documentation/watchdog/src directory called
|
||||
watchdog-test.c. Simply compile the C file and kick it off. If the system
|
||||
gets into a bad state and hangs, the HP ProLiant iLO 2 timer register will
|
||||
gets into a bad state and hangs, the HPE ProLiant iLO timer register will
|
||||
not be updated in a timely fashion and a hardware system reset (also known as
|
||||
an Automatic Server Recovery (ASR)) event will occur.
|
||||
|
||||
The hpwdt driver also has four (4) module parameters. They are the following:
|
||||
The hpwdt driver also has three (3) module parameters. They are the following:
|
||||
|
||||
soft_margin - allows the user to set the watchdog timer value
|
||||
allow_kdump - allows the user to save off a kernel dump image after an NMI
|
||||
soft_margin - allows the user to set the watchdog timer value.
|
||||
Default value is 30 seconds.
|
||||
allow_kdump - allows the user to save off a kernel dump image after an NMI.
|
||||
Default value is 1/ON
|
||||
nowayout - basic watchdog parameter that does not allow the timer to
|
||||
be restarted or an impending ASR to be escaped.
|
||||
priority - determines whether or not the hpwdt driver is first on the
|
||||
die_notify list to handle NMIs or last. The default value
|
||||
for this module parameter is 0 or LAST. If the user wants to
|
||||
enable NMI sourcing then reload the hpwdt driver with
|
||||
priority=1 (and boot with nmi_watchdog=0).
|
||||
Default value is set when compiling the kernel. If it is set
|
||||
to "Y", then there is no way of disabling the watchdog once
|
||||
it has been started.
|
||||
|
||||
NOTE: More information about watchdog drivers in general, including the ioctl
|
||||
interface to /dev/watchdog can be found in
|
||||
Documentation/watchdog/watchdog-api.txt and Documentation/IPMI.txt.
|
||||
|
||||
The priority parameter was introduced due to other kernel software that relied
|
||||
on handling NMIs (like oprofile). Keeping hpwdt's priority at 0 (or LAST)
|
||||
enables the users of NMIs for non critical events to be work as expected.
|
||||
|
||||
The NMI sourcing capability is disabled by default due to the inability to
|
||||
distinguish between "NMI Watchdog Ticks" and "HW generated NMI events" in the
|
||||
Linux kernel. What this means is that the hpwdt nmi handler code is called
|
||||
each time the NMI signal fires off. This could amount to several thousands of
|
||||
NMIs in a matter of seconds. If a user sees the Linux kernel's "dazed and
|
||||
confused" message in the logs or if the system gets into a hung state, then
|
||||
the hpwdt driver can be reloaded with the "priority" module parameter set
|
||||
(priority=1).
|
||||
the hpwdt driver can be reloaded.
|
||||
|
||||
1. If the kernel has not been booted with nmi_watchdog turned off then
|
||||
edit /boot/grub/menu.lst and place the nmi_watchdog=0 at the end of the
|
||||
currently booting kernel line.
|
||||
edit and place the nmi_watchdog=0 at the end of the currently booting
|
||||
kernel line. Depending on your Linux distribution and platform setup:
|
||||
For non-UEFI systems
|
||||
/boot/grub/grub.conf or
|
||||
/boot/grub/menu.lst
|
||||
For UEFI systems
|
||||
/boot/efi/EFI/distroname/grub.conf or
|
||||
/boot/efi/efi/distroname/elilo.conf
|
||||
2. reboot the sever
|
||||
3. Once the system comes up perform a rmmod hpwdt
|
||||
4. insmod /lib/modules/`uname -r`/kernel/drivers/char/watchdog/hpwdt.ko priority=1
|
||||
3. Once the system comes up perform a modprobe -r hpwdt
|
||||
4. modprobe /lib/modules/`uname -r`/kernel/drivers/watchdog/hpwdt.ko
|
||||
|
||||
Now, the hpwdt can successfully receive and source the NMI and provide a log
|
||||
message that details the reason for the NMI (as determined by the HP BIOS).
|
||||
message that details the reason for the NMI (as determined by the HPE BIOS).
|
||||
|
||||
Below is a list of NMIs the HP BIOS understands along with the associated
|
||||
Below is a list of NMIs the HPE BIOS understands along with the associated
|
||||
code (reason):
|
||||
|
||||
No source found 00h
|
||||
|
@ -92,4 +95,4 @@ Last reviewed: 06/02/2009
|
|||
|
||||
|
||||
-- Tom Mingarelli
|
||||
(thomas.mingarelli@hp.com)
|
||||
(thomas.mingarelli@hpe.com)
|
||||
|
|
|
@ -86,6 +86,10 @@ nowayout: Watchdog cannot be stopped once started
|
|||
davinci_wdt:
|
||||
heartbeat: Watchdog heartbeat period in seconds from 1 to 600, default 60
|
||||
-------------------------------------------------
|
||||
ebc-c384_wdt:
|
||||
timeout: Watchdog timeout in seconds. (1<=timeout<=15300, default=60)
|
||||
nowayout: Watchdog cannot be stopped once started
|
||||
-------------------------------------------------
|
||||
ep93xx_wdt:
|
||||
nowayout: Watchdog cannot be stopped once started
|
||||
timeout: Watchdog timeout in seconds. (1<=timeout<=3600, default=TBD)
|
||||
|
|
|
@ -12345,6 +12345,7 @@ L: linux-watchdog@vger.kernel.org
|
|||
W: http://www.linux-watchdog.org/
|
||||
T: git git://www.linux-watchdog.org/linux-watchdog.git
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/watchdog/
|
||||
F: Documentation/watchdog/
|
||||
F: drivers/watchdog/
|
||||
F: include/linux/watchdog.h
|
||||
|
|
|
@ -200,6 +200,8 @@ CONFIG_SENSORS_INA2XX=m
|
|||
CONFIG_THERMAL=y
|
||||
CONFIG_THERMAL_EMULATION=y
|
||||
CONFIG_EXYNOS_THERMAL=y
|
||||
CONFIG_WATCHDOG=y
|
||||
CONFIG_RENESAS_WDT=y
|
||||
CONFIG_MFD_SPMI_PMIC=y
|
||||
CONFIG_MFD_SEC_CORE=y
|
||||
CONFIG_MFD_HI655X_PMIC=y
|
||||
|
|
|
@ -661,6 +661,14 @@ config ATLAS7_WATCHDOG
|
|||
To compile this driver as a module, choose M here: the
|
||||
module will be called atlas7_wdt.
|
||||
|
||||
config RENESAS_WDT
|
||||
tristate "Renesas WDT Watchdog"
|
||||
depends on ARCH_RENESAS || COMPILE_TEST
|
||||
select WATCHDOG_CORE
|
||||
help
|
||||
This driver adds watchdog support for the integrated watchdogs in the
|
||||
Renesas R-Car and other SH-Mobile SoCs (usually named RWDT or SWDT).
|
||||
|
||||
# AVR32 Architecture
|
||||
|
||||
config AT32AP700X_WDT
|
||||
|
|
|
@ -73,6 +73,7 @@ obj-$(CONFIG_DIGICOLOR_WATCHDOG) += digicolor_wdt.o
|
|||
obj-$(CONFIG_LPC18XX_WATCHDOG) += lpc18xx_wdt.o
|
||||
obj-$(CONFIG_BCM7038_WDT) += bcm7038_wdt.o
|
||||
obj-$(CONFIG_ATLAS7_WATCHDOG) += atlas7_wdt.o
|
||||
obj-$(CONFIG_RENESAS_WDT) += renesas_wdt.o
|
||||
|
||||
# AVR32 Architecture
|
||||
obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o
|
||||
|
|
|
@ -611,9 +611,7 @@ static int cpwd_probe(struct platform_device *op)
|
|||
}
|
||||
|
||||
if (p->broken) {
|
||||
init_timer(&cpwd_timer);
|
||||
cpwd_timer.function = cpwd_brokentimer;
|
||||
cpwd_timer.data = (unsigned long) p;
|
||||
setup_timer(&cpwd_timer, cpwd_brokentimer, (unsigned long)p);
|
||||
cpwd_timer.expires = WD_BTIMEOUT;
|
||||
|
||||
pr_info("PLD defect workaround enabled for model %s\n",
|
||||
|
|
|
@ -38,7 +38,7 @@
|
|||
|
||||
#define SIO_F71808FG_LD_WDT 0x07 /* Watchdog timer logical device */
|
||||
#define SIO_UNLOCK_KEY 0x87 /* Key to enable Super-I/O */
|
||||
#define SIO_LOCK_KEY 0xAA /* Key to diasble Super-I/O */
|
||||
#define SIO_LOCK_KEY 0xAA /* Key to disable Super-I/O */
|
||||
|
||||
#define SIO_REG_LDSEL 0x07 /* Logical device select */
|
||||
#define SIO_REG_DEVID 0x20 /* Device ID (2 bytes) */
|
||||
|
@ -59,6 +59,7 @@
|
|||
#define SIO_F71869A_ID 0x1007 /* Chipset ID */
|
||||
#define SIO_F71882_ID 0x0541 /* Chipset ID */
|
||||
#define SIO_F71889_ID 0x0723 /* Chipset ID */
|
||||
#define SIO_F81865_ID 0x0704 /* Chipset ID */
|
||||
|
||||
#define F71808FG_REG_WDO_CONF 0xf0
|
||||
#define F71808FG_REG_WDT_CONF 0xf5
|
||||
|
@ -66,11 +67,14 @@
|
|||
|
||||
#define F71808FG_FLAG_WDOUT_EN 7
|
||||
|
||||
#define F71808FG_FLAG_WDTMOUT_STS 5
|
||||
#define F71808FG_FLAG_WDTMOUT_STS 6
|
||||
#define F71808FG_FLAG_WD_EN 5
|
||||
#define F71808FG_FLAG_WD_PULSE 4
|
||||
#define F71808FG_FLAG_WD_UNIT 3
|
||||
|
||||
#define F81865_REG_WDO_CONF 0xfa
|
||||
#define F81865_FLAG_WDOUT_EN 0
|
||||
|
||||
/* Default values */
|
||||
#define WATCHDOG_TIMEOUT 60 /* 1 minute default timeout */
|
||||
#define WATCHDOG_MAX_TIMEOUT (60 * 255)
|
||||
|
@ -112,7 +116,7 @@ module_param(start_withtimeout, uint, 0);
|
|||
MODULE_PARM_DESC(start_withtimeout, "Start watchdog timer on module load with"
|
||||
" given initial timeout. Zero (default) disables this feature.");
|
||||
|
||||
enum chips { f71808fg, f71858fg, f71862fg, f71869, f71882fg, f71889fg };
|
||||
enum chips { f71808fg, f71858fg, f71862fg, f71869, f71882fg, f71889fg, f81865 };
|
||||
|
||||
static const char *f71808e_names[] = {
|
||||
"f71808fg",
|
||||
|
@ -121,6 +125,7 @@ static const char *f71808e_names[] = {
|
|||
"f71869",
|
||||
"f71882fg",
|
||||
"f71889fg",
|
||||
"f81865",
|
||||
};
|
||||
|
||||
/* Super-I/O Function prototypes */
|
||||
|
@ -360,6 +365,11 @@ static int watchdog_start(void)
|
|||
superio_inb(watchdog.sioaddr, SIO_REG_MFUNCT3) & 0xcf);
|
||||
break;
|
||||
|
||||
case f81865:
|
||||
/* Set pin 70 to WDTRST# */
|
||||
superio_clear_bit(watchdog.sioaddr, SIO_REG_MFUNCT3, 5);
|
||||
break;
|
||||
|
||||
default:
|
||||
/*
|
||||
* 'default' label to shut up the compiler and catch
|
||||
|
@ -371,8 +381,13 @@ static int watchdog_start(void)
|
|||
|
||||
superio_select(watchdog.sioaddr, SIO_F71808FG_LD_WDT);
|
||||
superio_set_bit(watchdog.sioaddr, SIO_REG_ENABLE, 0);
|
||||
superio_set_bit(watchdog.sioaddr, F71808FG_REG_WDO_CONF,
|
||||
F71808FG_FLAG_WDOUT_EN);
|
||||
|
||||
if (watchdog.type == f81865)
|
||||
superio_set_bit(watchdog.sioaddr, F81865_REG_WDO_CONF,
|
||||
F81865_FLAG_WDOUT_EN);
|
||||
else
|
||||
superio_set_bit(watchdog.sioaddr, F71808FG_REG_WDO_CONF,
|
||||
F71808FG_FLAG_WDOUT_EN);
|
||||
|
||||
superio_set_bit(watchdog.sioaddr, F71808FG_REG_WDT_CONF,
|
||||
F71808FG_FLAG_WD_EN);
|
||||
|
@ -655,7 +670,7 @@ static int __init watchdog_init(int sioaddr)
|
|||
superio_select(watchdog.sioaddr, SIO_F71808FG_LD_WDT);
|
||||
|
||||
wdt_conf = superio_inb(sioaddr, F71808FG_REG_WDT_CONF);
|
||||
watchdog.caused_reboot = wdt_conf & F71808FG_FLAG_WDTMOUT_STS;
|
||||
watchdog.caused_reboot = wdt_conf & BIT(F71808FG_FLAG_WDTMOUT_STS);
|
||||
|
||||
superio_exit(sioaddr);
|
||||
|
||||
|
@ -770,6 +785,9 @@ static int __init f71808e_find(int sioaddr)
|
|||
/* Confirmed (by datasheet) not to have a watchdog. */
|
||||
err = -ENODEV;
|
||||
goto exit;
|
||||
case SIO_F81865_ID:
|
||||
watchdog.type = f81865;
|
||||
break;
|
||||
default:
|
||||
pr_info("Unrecognized Fintek device: %04x\n",
|
||||
(unsigned int)devid);
|
||||
|
|
|
@ -37,6 +37,8 @@
|
|||
|
||||
#define IMX2_WDT_WCR 0x00 /* Control Register */
|
||||
#define IMX2_WDT_WCR_WT (0xFF << 8) /* -> Watchdog Timeout Field */
|
||||
#define IMX2_WDT_WCR_WDA (1 << 5) /* -> External Reset WDOG_B */
|
||||
#define IMX2_WDT_WCR_SRS (1 << 4) /* -> Software Reset Signal */
|
||||
#define IMX2_WDT_WCR_WRE (1 << 3) /* -> WDOG Reset Enable */
|
||||
#define IMX2_WDT_WCR_WDE (1 << 2) /* -> Watchdog Enable */
|
||||
#define IMX2_WDT_WCR_WDZST (1 << 0) /* -> Watchdog timer Suspend */
|
||||
|
@ -59,6 +61,7 @@ struct imx2_wdt_device {
|
|||
struct clk *clk;
|
||||
struct regmap *regmap;
|
||||
struct watchdog_device wdog;
|
||||
bool ext_reset;
|
||||
};
|
||||
|
||||
static bool nowayout = WATCHDOG_NOWAYOUT;
|
||||
|
@ -83,6 +86,12 @@ static int imx2_wdt_restart(struct watchdog_device *wdog, unsigned long action,
|
|||
struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog);
|
||||
unsigned int wcr_enable = IMX2_WDT_WCR_WDE;
|
||||
|
||||
/* Use internal reset or external - not both */
|
||||
if (wdev->ext_reset)
|
||||
wcr_enable |= IMX2_WDT_WCR_SRS; /* do not assert int reset */
|
||||
else
|
||||
wcr_enable |= IMX2_WDT_WCR_WDA; /* do not assert ext-reset */
|
||||
|
||||
/* Assert SRS signal */
|
||||
regmap_write(wdev->regmap, IMX2_WDT_WCR, wcr_enable);
|
||||
/*
|
||||
|
@ -112,8 +121,12 @@ static inline void imx2_wdt_setup(struct watchdog_device *wdog)
|
|||
val |= IMX2_WDT_WCR_WDZST;
|
||||
/* Strip the old watchdog Time-Out value */
|
||||
val &= ~IMX2_WDT_WCR_WT;
|
||||
/* Generate reset if WDOG times out */
|
||||
val &= ~IMX2_WDT_WCR_WRE;
|
||||
/* Generate internal chip-level reset if WDOG times out */
|
||||
if (!wdev->ext_reset)
|
||||
val &= ~IMX2_WDT_WCR_WRE;
|
||||
/* Or if external-reset assert WDOG_B reset only on time-out */
|
||||
else
|
||||
val |= IMX2_WDT_WCR_WRE;
|
||||
/* Keep Watchdog Disabled */
|
||||
val &= ~IMX2_WDT_WCR_WDE;
|
||||
/* Set the watchdog's Time-Out value */
|
||||
|
@ -230,6 +243,8 @@ static int __init imx2_wdt_probe(struct platform_device *pdev)
|
|||
regmap_read(wdev->regmap, IMX2_WDT_WRSR, &val);
|
||||
wdog->bootstatus = val & IMX2_WDT_WRSR_TOUT ? WDIOF_CARDRESET : 0;
|
||||
|
||||
wdev->ext_reset = of_property_read_bool(pdev->dev.of_node,
|
||||
"fsl,ext-reset-output");
|
||||
wdog->timeout = clamp_t(unsigned, timeout, 1, IMX2_WDT_MAX_TIME);
|
||||
if (wdog->timeout != timeout)
|
||||
dev_warn(&pdev->dev, "Initial timeout out of range! Clamped from %u to %u\n",
|
||||
|
|
|
@ -160,10 +160,8 @@ static int jz4740_wdt_probe(struct platform_device *pdev)
|
|||
|
||||
drvdata = devm_kzalloc(&pdev->dev, sizeof(struct jz4740_wdt_drvdata),
|
||||
GFP_KERNEL);
|
||||
if (!drvdata) {
|
||||
dev_err(&pdev->dev, "Unable to alloacate watchdog device\n");
|
||||
if (!drvdata)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT)
|
||||
heartbeat = DEFAULT_HEARTBEAT;
|
||||
|
|
|
@ -431,7 +431,7 @@ static int octeon_wdt_cpu_callback(struct notifier_block *nfb,
|
|||
{
|
||||
unsigned int cpu = (unsigned long)hcpu;
|
||||
|
||||
switch (action) {
|
||||
switch (action & ~CPU_TASKS_FROZEN) {
|
||||
case CPU_DOWN_PREPARE:
|
||||
octeon_wdt_disable_interrupt(cpu);
|
||||
break;
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
#define WDT_RST 0x38
|
||||
#define WDT_EN 0x40
|
||||
#define WDT_STS 0x44
|
||||
#define WDT_BITE_TIME 0x5C
|
||||
|
||||
struct qcom_wdt {
|
||||
|
@ -108,7 +109,8 @@ static const struct watchdog_ops qcom_wdt_ops = {
|
|||
static const struct watchdog_info qcom_wdt_info = {
|
||||
.options = WDIOF_KEEPALIVEPING
|
||||
| WDIOF_MAGICCLOSE
|
||||
| WDIOF_SETTIMEOUT,
|
||||
| WDIOF_SETTIMEOUT
|
||||
| WDIOF_CARDRESET,
|
||||
.identity = KBUILD_MODNAME,
|
||||
};
|
||||
|
||||
|
@ -171,6 +173,9 @@ static int qcom_wdt_probe(struct platform_device *pdev)
|
|||
wdt->wdd.max_timeout = 0x10000000U / wdt->rate;
|
||||
wdt->wdd.parent = &pdev->dev;
|
||||
|
||||
if (readl(wdt->base + WDT_STS) & 1)
|
||||
wdt->wdd.bootstatus = WDIOF_CARDRESET;
|
||||
|
||||
/*
|
||||
* If 'timeout-sec' unspecified in devicetree, assume a 30 second
|
||||
* default, unless the max timeout is less than 30 seconds, then use
|
||||
|
|
|
@ -0,0 +1,213 @@
|
|||
/*
|
||||
* Watchdog driver for Renesas WDT watchdog
|
||||
*
|
||||
* Copyright (C) 2015-16 Wolfram Sang, Sang Engineering <wsa@sang-engineering.com>
|
||||
* Copyright (C) 2015-16 Renesas Electronics Corporation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*/
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/watchdog.h>
|
||||
|
||||
#define RWTCNT 0
|
||||
#define RWTCSRA 4
|
||||
#define RWTCSRA_WOVF BIT(4)
|
||||
#define RWTCSRA_WRFLG BIT(5)
|
||||
#define RWTCSRA_TME BIT(7)
|
||||
|
||||
#define RWDT_DEFAULT_TIMEOUT 60U
|
||||
|
||||
static const unsigned int clk_divs[] = { 1, 4, 16, 32, 64, 128, 1024 };
|
||||
|
||||
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 rwdt_priv {
|
||||
void __iomem *base;
|
||||
struct watchdog_device wdev;
|
||||
struct clk *clk;
|
||||
unsigned int clks_per_sec;
|
||||
u8 cks;
|
||||
};
|
||||
|
||||
static void rwdt_write(struct rwdt_priv *priv, u32 val, unsigned int reg)
|
||||
{
|
||||
if (reg == RWTCNT)
|
||||
val |= 0x5a5a0000;
|
||||
else
|
||||
val |= 0xa5a5a500;
|
||||
|
||||
writel_relaxed(val, priv->base + reg);
|
||||
}
|
||||
|
||||
static int rwdt_init_timeout(struct watchdog_device *wdev)
|
||||
{
|
||||
struct rwdt_priv *priv = watchdog_get_drvdata(wdev);
|
||||
|
||||
rwdt_write(priv, 65536 - wdev->timeout * priv->clks_per_sec, RWTCNT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rwdt_start(struct watchdog_device *wdev)
|
||||
{
|
||||
struct rwdt_priv *priv = watchdog_get_drvdata(wdev);
|
||||
|
||||
clk_prepare_enable(priv->clk);
|
||||
|
||||
rwdt_write(priv, priv->cks, RWTCSRA);
|
||||
rwdt_init_timeout(wdev);
|
||||
|
||||
while (readb_relaxed(priv->base + RWTCSRA) & RWTCSRA_WRFLG)
|
||||
cpu_relax();
|
||||
|
||||
rwdt_write(priv, priv->cks | RWTCSRA_TME, RWTCSRA);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rwdt_stop(struct watchdog_device *wdev)
|
||||
{
|
||||
struct rwdt_priv *priv = watchdog_get_drvdata(wdev);
|
||||
|
||||
rwdt_write(priv, priv->cks, RWTCSRA);
|
||||
clk_disable_unprepare(priv->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int rwdt_get_timeleft(struct watchdog_device *wdev)
|
||||
{
|
||||
struct rwdt_priv *priv = watchdog_get_drvdata(wdev);
|
||||
u16 val = readw_relaxed(priv->base + RWTCNT);
|
||||
|
||||
return DIV_ROUND_CLOSEST(65536 - val, priv->clks_per_sec);
|
||||
}
|
||||
|
||||
static const struct watchdog_info rwdt_ident = {
|
||||
.options = WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT,
|
||||
.identity = "Renesas WDT Watchdog",
|
||||
};
|
||||
|
||||
static const struct watchdog_ops rwdt_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.start = rwdt_start,
|
||||
.stop = rwdt_stop,
|
||||
.ping = rwdt_init_timeout,
|
||||
.get_timeleft = rwdt_get_timeleft,
|
||||
};
|
||||
|
||||
static int rwdt_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct rwdt_priv *priv;
|
||||
struct resource *res;
|
||||
unsigned long rate;
|
||||
unsigned int clks_per_sec;
|
||||
int ret, i;
|
||||
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
priv->base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(priv->base))
|
||||
return PTR_ERR(priv->base);
|
||||
|
||||
priv->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(priv->clk))
|
||||
return PTR_ERR(priv->clk);
|
||||
|
||||
rate = clk_get_rate(priv->clk);
|
||||
if (!rate)
|
||||
return -ENOENT;
|
||||
|
||||
for (i = ARRAY_SIZE(clk_divs) - 1; i >= 0; i--) {
|
||||
clks_per_sec = DIV_ROUND_UP(rate, clk_divs[i]);
|
||||
if (clks_per_sec) {
|
||||
priv->clks_per_sec = clks_per_sec;
|
||||
priv->cks = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!clks_per_sec) {
|
||||
dev_err(&pdev->dev, "Can't find suitable clock divider\n");
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
pm_runtime_get_sync(&pdev->dev);
|
||||
|
||||
priv->wdev.info = &rwdt_ident,
|
||||
priv->wdev.ops = &rwdt_ops,
|
||||
priv->wdev.parent = &pdev->dev;
|
||||
priv->wdev.min_timeout = 1;
|
||||
priv->wdev.max_timeout = 65536 / clks_per_sec;
|
||||
priv->wdev.timeout = min(priv->wdev.max_timeout, RWDT_DEFAULT_TIMEOUT);
|
||||
|
||||
platform_set_drvdata(pdev, priv);
|
||||
watchdog_set_drvdata(&priv->wdev, priv);
|
||||
watchdog_set_nowayout(&priv->wdev, nowayout);
|
||||
|
||||
/* This overrides the default timeout only if DT configuration was found */
|
||||
ret = watchdog_init_timeout(&priv->wdev, 0, &pdev->dev);
|
||||
if (ret)
|
||||
dev_warn(&pdev->dev, "Specified timeout value invalid, using default\n");
|
||||
|
||||
ret = watchdog_register_device(&priv->wdev);
|
||||
if (ret < 0) {
|
||||
pm_runtime_put(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rwdt_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct rwdt_priv *priv = platform_get_drvdata(pdev);
|
||||
|
||||
watchdog_unregister_device(&priv->wdev);
|
||||
pm_runtime_put(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This driver does also fit for R-Car Gen2 (r8a779[0-4]) WDT. However, for SMP
|
||||
* to work there, one also needs a RESET (RST) driver which does not exist yet
|
||||
* due to HW issues. This needs to be solved before adding compatibles here.
|
||||
*/
|
||||
static const struct of_device_id rwdt_ids[] = {
|
||||
{ .compatible = "renesas,rcar-gen3-wdt", },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rwdt_ids);
|
||||
|
||||
static struct platform_driver rwdt_driver = {
|
||||
.driver = {
|
||||
.name = "renesas_wdt",
|
||||
.of_match_table = rwdt_ids,
|
||||
},
|
||||
.probe = rwdt_probe,
|
||||
.remove = rwdt_remove,
|
||||
};
|
||||
module_platform_driver(rwdt_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Renesas WDT Watchdog Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_AUTHOR("Wolfram Sang <wsa@sang-engineering.com>");
|
|
@ -275,9 +275,7 @@ static int sh_wdt_probe(struct platform_device *pdev)
|
|||
return rc;
|
||||
}
|
||||
|
||||
init_timer(&wdt->timer);
|
||||
wdt->timer.function = sh_wdt_ping;
|
||||
wdt->timer.data = (unsigned long)wdt;
|
||||
setup_timer(&wdt->timer, sh_wdt_ping, (unsigned long)wdt);
|
||||
wdt->timer.expires = next_ping_period(clock_division_ratio);
|
||||
|
||||
dev_info(&pdev->dev, "initialized.\n");
|
||||
|
|
|
@ -73,6 +73,13 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started."
|
|||
/*
|
||||
* Some TCO specific functions
|
||||
*/
|
||||
|
||||
static bool tco_has_sp5100_reg_layout(struct pci_dev *dev)
|
||||
{
|
||||
return dev->device == PCI_DEVICE_ID_ATI_SBX00_SMBUS &&
|
||||
dev->revision < 0x40;
|
||||
}
|
||||
|
||||
static void tco_timer_start(void)
|
||||
{
|
||||
u32 val;
|
||||
|
@ -129,7 +136,7 @@ static void tco_timer_enable(void)
|
|||
{
|
||||
int val;
|
||||
|
||||
if (sp5100_tco_pci->revision >= 0x40) {
|
||||
if (!tco_has_sp5100_reg_layout(sp5100_tco_pci)) {
|
||||
/* For SB800 or later */
|
||||
/* Set the Watchdog timer resolution to 1 sec */
|
||||
outb(SB800_PM_WATCHDOG_CONFIG, SB800_IO_PM_INDEX_REG);
|
||||
|
@ -342,8 +349,7 @@ static unsigned char sp5100_tco_setupdevice(void)
|
|||
/*
|
||||
* Determine type of southbridge chipset.
|
||||
*/
|
||||
if (sp5100_tco_pci->device == PCI_DEVICE_ID_ATI_SBX00_SMBUS &&
|
||||
sp5100_tco_pci->revision < 0x40) {
|
||||
if (tco_has_sp5100_reg_layout(sp5100_tco_pci)) {
|
||||
dev_name = SP5100_DEVNAME;
|
||||
index_reg = SP5100_IO_PM_INDEX_REG;
|
||||
data_reg = SP5100_IO_PM_DATA_REG;
|
||||
|
@ -388,8 +394,7 @@ static unsigned char sp5100_tco_setupdevice(void)
|
|||
* Secondly, Find the watchdog timer MMIO address
|
||||
* from SBResource_MMIO register.
|
||||
*/
|
||||
if (sp5100_tco_pci->device == PCI_DEVICE_ID_ATI_SBX00_SMBUS &&
|
||||
sp5100_tco_pci->revision < 0x40) {
|
||||
if (tco_has_sp5100_reg_layout(sp5100_tco_pci)) {
|
||||
/* Read SBResource_MMIO from PCI config(PCI_Reg: 9Ch) */
|
||||
pci_read_config_dword(sp5100_tco_pci,
|
||||
SP5100_SB_RESOURCE_MMIO_BASE, &val);
|
||||
|
|
|
@ -104,7 +104,7 @@ static void watchdog_check_min_max_timeout(struct watchdog_device *wdd)
|
|||
* timeout module parameter (if it is valid value) or the timeout-sec property
|
||||
* (only if it is a valid value and the timeout_parm is out of bounds).
|
||||
* If none of them are valid then we keep the old value (which should normally
|
||||
* be the default timeout value.
|
||||
* be the default timeout value).
|
||||
*
|
||||
* A zero is returned on success and -EINVAL for failure.
|
||||
*/
|
||||
|
|
|
@ -736,7 +736,6 @@ static int watchdog_release(struct inode *inode, struct file *file)
|
|||
watchdog_ping(wdd);
|
||||
}
|
||||
|
||||
cancel_delayed_work_sync(&wd_data->work);
|
||||
watchdog_update_worker(wdd);
|
||||
|
||||
/* make sure that /dev/watchdog can be re-opened */
|
||||
|
|
Loading…
Reference in New Issue