Commit 5ecf3e11 authored by Linus Torvalds's avatar Linus Torvalds

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

Pull watchdog updates from Wim Van Sebroeck:

 - Add Armada 37xx CPU watchdog

 - w83627hf_wdt: Add Support for NCT6796D, NCT6797D, NCT6798D

 - hpwdt: several improvements

 - renesas_wdt: SPDX identifiers, stop when unregistering, support for
   R7S9210

 - rza_wdt: SPDX identifiers, support longer timeouts

 - core: fix null pointer dereference when releasing cdev

 - iTCO_wdt: Drop option vendorsupport=2

 - sama5d4: fix timeout-sec usage

 - lantiq_wdt: convert to watchdog framework

 - several small fixes

* tag 'linux-watchdog-4.20-rc1' of git://www.linux-watchdog.org/linux-watchdog: (30 commits)
  watchdog: ts4800: release syscon device node in ts4800_wdt_probe()
  watchdog: armada_37xx_wdt: use do_div for u64 division
  documentation: watchdog: add documentation for armada-37xx-wdt
  dt-bindings: watchdog: Document armada-37xx-wdt binding
  watchdog: Add support for Armada 37xx CPU watchdog
  dt-bindings: watchdog: add mpc8xxx-wdt support
  watchdog: mpc8xxx: provide boot status
  MAINTAINERS: Fix file pattern for MEN Z069 watchdog driver
  dt-bindings: watchdog: renesas-wdt: Add support for R7S9210
  watchdog: rza_wdt: Support longer timeouts
  watchdog: hpwdt: Disable PreTimeout when Timeout is smaller
  watchdog: w83627hf_wdt: Support NCT6796D, NCT6797D, NCT6798D
  watchdog: mpc8xxx: use dev_xxxx() instead of pr_xxxx()
  watchdog: lantiq: add get_timeleft callback
  watchdog: lantiq: Convert to watchdog_device
  watchdog: lantiq: update register names to better match spec
  watchdog: sama5d4: fix timeout-sec usage
  watchdog: fix a small number of "watchog" typos in comments
  watchdog: rza_wdt: convert to SPDX identifiers
  watchdog: iTCO_wdt: Remove unused hooks
  ...
parents ed3f4e23 cd6ba41c
* Armada 37xx CPU Watchdog Timer Controller
Required properties:
- compatible : must be "marvell,armada-3700-wdt"
- reg : base physical address of the controller and length of memory mapped
region.
- clocks : the clock feeding the watchdog timer. See clock-bindings.txt
- marvell,system-controller : reference to syscon node for the CPU Miscellaneous
Registers
Example:
cpu_misc: system-controller@d000 {
compatible = "marvell,armada-3700-cpu-misc", "syscon";
reg = <0xd000 0x1000>;
};
wdt: watchdog@8300 {
compatible = "marvell,armada-3700-wdt";
reg = <0x8300 0x40>;
marvell,system-controller = <&cpu_misc>;
clocks = <&xtalclk>;
};
* Freescale mpc8xxx watchdog driver (For 83xx, 86xx and 8xx)
Required properties:
- compatible: Shall contain one of the following:
"mpc83xx_wdt" for an mpc83xx
"fsl,mpc8610-wdt" for an mpc86xx
"fsl,mpc823-wdt" for an mpc8xx
- reg: base physical address and length of the area hosting the
watchdog registers.
On the 83xx, "Watchdog Timer Registers" area: <0x200 0x100>
On the 86xx, "Watchdog Timer Registers" area: <0xe4000 0x100>
On the 8xx, "General System Interface Unit" area: <0x0 0x10>
Optional properties:
- reg: additional physical address and length (4) of location of the
Reset Status Register (called RSTRSCR on the mpc86xx)
On the 83xx, it is located at offset 0x910
On the 86xx, it is located at offset 0xe0094
On the 8xx, it is located at offset 0x288
Example:
WDT: watchdog@0 {
compatible = "fsl,mpc823-wdt";
reg = <0x0 0x10 0x288 0x4>;
};
...@@ -21,6 +21,7 @@ Required properties: ...@@ -21,6 +21,7 @@ Required properties:
- "renesas,r8a77990-wdt" (R-Car E3) - "renesas,r8a77990-wdt" (R-Car E3)
- "renesas,r8a77995-wdt" (R-Car D3) - "renesas,r8a77995-wdt" (R-Car D3)
- "renesas,r7s72100-wdt" (RZ/A1) - "renesas,r7s72100-wdt" (RZ/A1)
- "renesas,r7s9210-wdt" (RZ/A2)
The generic compatible string must be: The generic compatible string must be:
- "renesas,rza-wdt" for RZ/A - "renesas,rza-wdt" for RZ/A
- "renesas,rcar-gen2-wdt" for R-Car Gen2 and RZ/G1 - "renesas,rcar-gen2-wdt" for R-Car Gen2 and RZ/G1
......
Last reviewed: 05/20/2016 Last reviewed: 08/20/2018
HPE iLO NMI Watchdog Driver HPE iLO NMI Watchdog Driver
NMI sourcing for iLO based ProLiant Servers for iLO based ProLiant Servers
Documentation and Driver by
Thomas Mingarelli
The HPE iLO 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 handler for the iLO "Generate NMI to System"
watchdog functionality and the NMI sourcing capability need to be enabled virtual button.
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 All references to iLO in this document imply it also works on iLO2 and all
subsequent generations. subsequent generations.
...@@ -21,12 +18,16 @@ Last reviewed: 05/20/2016 ...@@ -21,12 +18,16 @@ Last reviewed: 05/20/2016
not be updated in a timely fashion and a hardware system reset (also known as not be updated in a timely fashion and a hardware system reset (also known as
an Automatic Server Recovery (ASR)) event will occur. an Automatic Server Recovery (ASR)) event will occur.
The hpwdt driver also has three (3) module parameters. They are the following: The hpwdt driver also has the following module parameters:
soft_margin - allows the user to set the watchdog timer value. soft_margin - allows the user to set the watchdog timer value.
Default value is 30 seconds. Default value is 30 seconds.
allow_kdump - allows the user to save off a kernel dump image after an NMI. timeout - an alias of soft_margin.
Default value is 1/ON pretimeout - allows the user to set the watchdog pretimeout value.
This is the number of seconds before timeout when an
NMI is delivered to the system. Setting the value to
zero disables the pretimeout NMI.
Default value is 9 seconds.
nowayout - basic watchdog parameter that does not allow the timer to nowayout - basic watchdog parameter that does not allow the timer to
be restarted or an impending ASR to be escaped. be restarted or an impending ASR to be escaped.
Default value is set when compiling the kernel. If it is set Default value is set when compiling the kernel. If it is set
...@@ -37,61 +38,29 @@ Last reviewed: 05/20/2016 ...@@ -37,61 +38,29 @@ Last reviewed: 05/20/2016
interface to /dev/watchdog can be found in interface to /dev/watchdog can be found in
Documentation/watchdog/watchdog-api.txt and Documentation/IPMI.txt. Documentation/watchdog/watchdog-api.txt and Documentation/IPMI.txt.
The NMI sourcing capability is disabled by default due to the inability to Due to limitations in the iLO hardware, the NMI pretimeout if enabled,
distinguish between "NMI Watchdog Ticks" and "HW generated NMI events" in the can only be set to 9 seconds. Attempts to set pretimeout to other
Linux kernel. What this means is that the hpwdt nmi handler code is called non-zero values will be rounded, possibly to zero. Users should verify
each time the NMI signal fires off. This could amount to several thousands of the pretimeout value after attempting to set pretimeout or timeout.
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.
1. If the kernel has not been booted with nmi_watchdog turned off then
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 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 HPE BIOS).
Below is a list of NMIs the HPE BIOS understands along with the associated
code (reason):
No source found 00h
Uncorrectable Memory Error 01h
ASR NMI 1Bh
PCI Parity Error 20h
NMI Button Press 27h
SB_BUS_NMI 28h
ILO Doorbell NMI 29h
ILO IOP NMI 2Ah
ILO Watchdog NMI 2Bh
Proc Throt NMI 2Ch
Front Side Bus NMI 2Dh Upon receipt of an NMI from the iLO, the hpwdt driver will initiate a
panic. This is to allow for a crash dump to be collected. It is incumbent
upon the user to have properly configured the system for kdump.
PCI Express Error 2Fh The default Linux kernel behavior upon panic is to print a kernel tombstone
and loop forever. This is generally not what a watchdog user wants.
DMA controller NMI 30h For those wishing to learn more please see:
Documentation/kdump/kdump.txt
Documentation/admin-guide/kernel-parameters.txt (panic=)
Your Linux Distribution specific documentation.
Hypertransport/CSI Error 31h If the hpwdt does not receive the NMI associated with an expiring timer,
the iLO will proceed to reset the system at timeout if the timer hasn't
been updated.
--
The HPE iLO NMI Watchdog Driver and documentation were originally developed
by Tom Mingarelli.
-- Tom Mingarelli
...@@ -40,6 +40,11 @@ margin: Watchdog margin in seconds (default=60) ...@@ -40,6 +40,11 @@ margin: Watchdog margin in seconds (default=60)
nowayout: Disable watchdog shutdown on close nowayout: Disable watchdog shutdown on close
(default=kernel config parameter) (default=kernel config parameter)
------------------------------------------------- -------------------------------------------------
armada_37xx_wdt:
timeout: Watchdog timeout in seconds. (default=120)
nowayout: Disable watchdog shutdown on close
(default=kernel config parameter)
-------------------------------------------------
at91rm9200_wdt: at91rm9200_wdt:
wdt_time: Watchdog time in seconds. (default=5) wdt_time: Watchdog time in seconds. (default=5)
nowayout: Watchdog cannot be stopped once started nowayout: Watchdog cannot be stopped once started
......
...@@ -9568,7 +9568,7 @@ MEN Z069 WATCHDOG DRIVER ...@@ -9568,7 +9568,7 @@ MEN Z069 WATCHDOG DRIVER
M: Johannes Thumshirn <jth@kernel.org> M: Johannes Thumshirn <jth@kernel.org>
L: linux-watchdog@vger.kernel.org L: linux-watchdog@vger.kernel.org
S: Maintained S: Maintained
F: drivers/watchdog/menz069_wdt.c F: drivers/watchdog/menz69_wdt.c
MESON AO CEC DRIVER FOR AMLOGIC SOCS MESON AO CEC DRIVER FOR AMLOGIC SOCS
M: Neil Armstrong <narmstrong@baylibre.com> M: Neil Armstrong <narmstrong@baylibre.com>
......
...@@ -273,6 +273,17 @@ config ARM_SBSA_WATCHDOG ...@@ -273,6 +273,17 @@ config ARM_SBSA_WATCHDOG
To compile this driver as module, choose M here: The module To compile this driver as module, choose M here: The module
will be called sbsa_gwdt. will be called sbsa_gwdt.
config ARMADA_37XX_WATCHDOG
tristate "Armada 37xx watchdog"
depends on ARCH_MVEBU || COMPILE_TEST
select MFD_SYSCON
select WATCHDOG_CORE
help
Say Y here to include support for the watchdog timer found on
Marvell Armada 37xx SoCs.
To compile this driver as a module, choose M here: the
module will be called armada_37xx_wdt.
config ASM9260_WATCHDOG config ASM9260_WATCHDOG
tristate "Alphascale ASM9260 watchdog" tristate "Alphascale ASM9260 watchdog"
depends on MACH_ASM9260 || COMPILE_TEST depends on MACH_ASM9260 || COMPILE_TEST
...@@ -1621,6 +1632,7 @@ config IMGPDC_WDT ...@@ -1621,6 +1632,7 @@ config IMGPDC_WDT
config LANTIQ_WDT config LANTIQ_WDT
tristate "Lantiq SoC watchdog" tristate "Lantiq SoC watchdog"
depends on LANTIQ depends on LANTIQ
select WATCHDOG_CORE
help help
Hardware driver for the Lantiq SoC Watchdog Timer. Hardware driver for the Lantiq SoC Watchdog Timer.
......
...@@ -38,6 +38,7 @@ obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb.o ...@@ -38,6 +38,7 @@ obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb.o
# ARM Architecture # ARM Architecture
obj-$(CONFIG_ARM_SP805_WATCHDOG) += sp805_wdt.o obj-$(CONFIG_ARM_SP805_WATCHDOG) += sp805_wdt.o
obj-$(CONFIG_ARM_SBSA_WATCHDOG) += sbsa_gwdt.o obj-$(CONFIG_ARM_SBSA_WATCHDOG) += sbsa_gwdt.o
obj-$(CONFIG_ARMADA_37XX_WATCHDOG) += armada_37xx_wdt.o
obj-$(CONFIG_ASM9260_WATCHDOG) += asm9260_wdt.o obj-$(CONFIG_ASM9260_WATCHDOG) += asm9260_wdt.o
obj-$(CONFIG_AT91RM9200_WATCHDOG) += at91rm9200_wdt.o obj-$(CONFIG_AT91RM9200_WATCHDOG) += at91rm9200_wdt.o
obj-$(CONFIG_AT91SAM9X_WATCHDOG) += at91sam9_wdt.o obj-$(CONFIG_AT91SAM9X_WATCHDOG) += at91sam9_wdt.o
......
// SPDX-License-Identifier: GPL-2.0+
/*
* Watchdog driver for Marvell Armada 37xx SoCs
*
* Author: Marek Behun <marek.behun@nic.cz>
*/
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/types.h>
#include <linux/watchdog.h>
/*
* There are four counters that can be used for watchdog on Armada 37xx.
* The addresses for counter control registers are register base plus ID*0x10,
* where ID is 0, 1, 2 or 3.
*
* In this driver we use IDs 0 and 1. Counter ID 1 is used as watchdog counter,
* while counter ID 0 is used to implement pinging the watchdog: counter ID 1 is
* set to restart counting from initial value on counter ID 0 end count event.
* Pinging is done by forcing immediate end count event on counter ID 0.
* If only one counter was used, pinging would have to be implemented by
* disabling and enabling the counter, leaving the system in a vulnerable state
* for a (really) short period of time.
*
* Counters ID 2 and 3 are enabled by default even before U-Boot loads,
* therefore this driver does not provide a way to use them, eg. by setting a
* property in device tree.
*/
#define CNTR_ID_RETRIGGER 0
#define CNTR_ID_WDOG 1
/* relative to cpu_misc */
#define WDT_TIMER_SELECT 0x64
#define WDT_TIMER_SELECT_MASK 0xf
#define WDT_TIMER_SELECT_VAL BIT(CNTR_ID_WDOG)
/* relative to reg */
#define CNTR_CTRL(id) ((id) * 0x10)
#define CNTR_CTRL_ENABLE 0x0001
#define CNTR_CTRL_ACTIVE 0x0002
#define CNTR_CTRL_MODE_MASK 0x000c
#define CNTR_CTRL_MODE_ONESHOT 0x0000
#define CNTR_CTRL_MODE_HWSIG 0x000c
#define CNTR_CTRL_TRIG_SRC_MASK 0x00f0
#define CNTR_CTRL_TRIG_SRC_PREV_CNTR 0x0050
#define CNTR_CTRL_PRESCALE_MASK 0xff00
#define CNTR_CTRL_PRESCALE_MIN 2
#define CNTR_CTRL_PRESCALE_SHIFT 8
#define CNTR_COUNT_LOW(id) (CNTR_CTRL(id) + 0x4)
#define CNTR_COUNT_HIGH(id) (CNTR_CTRL(id) + 0x8)
#define WATCHDOG_TIMEOUT 120
static unsigned int timeout;
module_param(timeout, int, 0);
MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds");
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 armada_37xx_watchdog {
struct watchdog_device wdt;
struct regmap *cpu_misc;
void __iomem *reg;
u64 timeout; /* in clock ticks */
unsigned long clk_rate;
struct clk *clk;
};
static u64 get_counter_value(struct armada_37xx_watchdog *dev, int id)
{
u64 val;
/*
* when low is read, high is latched into flip-flops so that it can be
* read consistently without using software debouncing
*/
val = readl(dev->reg + CNTR_COUNT_LOW(id));
val |= ((u64)readl(dev->reg + CNTR_COUNT_HIGH(id))) << 32;
return val;
}
static void set_counter_value(struct armada_37xx_watchdog *dev, int id, u64 val)
{
writel(val & 0xffffffff, dev->reg + CNTR_COUNT_LOW(id));
writel(val >> 32, dev->reg + CNTR_COUNT_HIGH(id));
}
static void counter_enable(struct armada_37xx_watchdog *dev, int id)
{
u32 reg;
reg = readl(dev->reg + CNTR_CTRL(id));
reg |= CNTR_CTRL_ENABLE;
writel(reg, dev->reg + CNTR_CTRL(id));
}
static void counter_disable(struct armada_37xx_watchdog *dev, int id)
{
u32 reg;
reg = readl(dev->reg + CNTR_CTRL(id));
reg &= ~CNTR_CTRL_ENABLE;
writel(reg, dev->reg + CNTR_CTRL(id));
}
static void init_counter(struct armada_37xx_watchdog *dev, int id, u32 mode,
u32 trig_src)
{
u32 reg;
reg = readl(dev->reg + CNTR_CTRL(id));
reg &= ~(CNTR_CTRL_MODE_MASK | CNTR_CTRL_PRESCALE_MASK |
CNTR_CTRL_TRIG_SRC_MASK);
/* set mode */
reg |= mode & CNTR_CTRL_MODE_MASK;
/* set prescaler to the min value */
reg |= CNTR_CTRL_PRESCALE_MIN << CNTR_CTRL_PRESCALE_SHIFT;
/* set trigger source */
reg |= trig_src & CNTR_CTRL_TRIG_SRC_MASK;
writel(reg, dev->reg + CNTR_CTRL(id));
}
static int armada_37xx_wdt_ping(struct watchdog_device *wdt)
{
struct armada_37xx_watchdog *dev = watchdog_get_drvdata(wdt);
/* counter 1 is retriggered by forcing end count on counter 0 */
counter_disable(dev, CNTR_ID_RETRIGGER);
counter_enable(dev, CNTR_ID_RETRIGGER);
return 0;
}
static unsigned int armada_37xx_wdt_get_timeleft(struct watchdog_device *wdt)
{
struct armada_37xx_watchdog *dev = watchdog_get_drvdata(wdt);
u64 res;
res = get_counter_value(dev, CNTR_ID_WDOG) * CNTR_CTRL_PRESCALE_MIN;
do_div(res, dev->clk_rate);
return res;
}
static int armada_37xx_wdt_set_timeout(struct watchdog_device *wdt,
unsigned int timeout)
{
struct armada_37xx_watchdog *dev = watchdog_get_drvdata(wdt);
wdt->timeout = timeout;
/*
* Compute the timeout in clock rate. We use smallest possible
* prescaler, which divides the clock rate by 2
* (CNTR_CTRL_PRESCALE_MIN).
*/
dev->timeout = (u64)dev->clk_rate * timeout;
do_div(dev->timeout, CNTR_CTRL_PRESCALE_MIN);
return 0;
}
static bool armada_37xx_wdt_is_running(struct armada_37xx_watchdog *dev)
{
u32 reg;
regmap_read(dev->cpu_misc, WDT_TIMER_SELECT, &reg);
if ((reg & WDT_TIMER_SELECT_MASK) != WDT_TIMER_SELECT_VAL)
return false;
reg = readl(dev->reg + CNTR_CTRL(CNTR_ID_WDOG));
return !!(reg & CNTR_CTRL_ACTIVE);
}
static int armada_37xx_wdt_start(struct watchdog_device *wdt)
{
struct armada_37xx_watchdog *dev = watchdog_get_drvdata(wdt);
/* select counter 1 as watchdog counter */
regmap_write(dev->cpu_misc, WDT_TIMER_SELECT, WDT_TIMER_SELECT_VAL);
/* init counter 0 as retrigger counter for counter 1 */
init_counter(dev, CNTR_ID_RETRIGGER, CNTR_CTRL_MODE_ONESHOT, 0);
set_counter_value(dev, CNTR_ID_RETRIGGER, 0);
/* init counter 1 to be retriggerable by counter 0 end count */
init_counter(dev, CNTR_ID_WDOG, CNTR_CTRL_MODE_HWSIG,
CNTR_CTRL_TRIG_SRC_PREV_CNTR);
set_counter_value(dev, CNTR_ID_WDOG, dev->timeout);
/* enable counter 1 */
counter_enable(dev, CNTR_ID_WDOG);
/* start counter 1 by forcing immediate end count on counter 0 */
counter_enable(dev, CNTR_ID_RETRIGGER);
return 0;
}
static int armada_37xx_wdt_stop(struct watchdog_device *wdt)
{
struct armada_37xx_watchdog *dev = watchdog_get_drvdata(wdt);
counter_disable(dev, CNTR_ID_WDOG);
counter_disable(dev, CNTR_ID_RETRIGGER);
regmap_write(dev->cpu_misc, WDT_TIMER_SELECT, 0);
return 0;
}
static const struct watchdog_info armada_37xx_wdt_info = {
.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
.identity = "Armada 37xx Watchdog",
};
static const struct watchdog_ops armada_37xx_wdt_ops = {
.owner = THIS_MODULE,
.start = armada_37xx_wdt_start,
.stop = armada_37xx_wdt_stop,
.ping = armada_37xx_wdt_ping,
.set_timeout = armada_37xx_wdt_set_timeout,
.get_timeleft = armada_37xx_wdt_get_timeleft,
};
static int armada_37xx_wdt_probe(struct platform_device *pdev)
{
struct armada_37xx_watchdog *dev;
struct resource *res;
struct regmap *regmap;
int ret;
dev = devm_kzalloc(&pdev->dev, sizeof(struct armada_37xx_watchdog),
GFP_KERNEL);
if (!dev)
return -ENOMEM;
dev->wdt.info = &armada_37xx_wdt_info;
dev->wdt.ops = &armada_37xx_wdt_ops;
regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
"marvell,system-controller");
if (IS_ERR(regmap))
return PTR_ERR(regmap);
dev->cpu_misc = regmap;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -ENODEV;
dev->reg = devm_ioremap(&pdev->dev, res->start, resource_size(res));
/* init clock */
dev->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(dev->clk))
return PTR_ERR(dev->clk);
ret = clk_prepare_enable(dev->clk);
if (ret)
return ret;
dev->clk_rate = clk_get_rate(dev->clk);
if (!dev->clk_rate) {
ret = -EINVAL;
goto disable_clk;
}
/*
* Since the timeout in seconds is given as 32 bit unsigned int, and
* the counters hold 64 bit values, even after multiplication by clock
* rate the counter can hold timeout of UINT_MAX seconds.
*/
dev->wdt.min_timeout = 1;
dev->wdt.max_timeout = UINT_MAX;
dev->wdt.parent = &pdev->dev;
/* default value, possibly override by module parameter or dtb */
dev->wdt.timeout = WATCHDOG_TIMEOUT;
watchdog_init_timeout(&dev->wdt, timeout, &pdev->dev);
platform_set_drvdata(pdev, &dev->wdt);
watchdog_set_drvdata(&dev->wdt, dev);
armada_37xx_wdt_set_timeout(&dev->wdt, dev->wdt.timeout);
if (armada_37xx_wdt_is_running(dev))
set_bit(WDOG_HW_RUNNING, &dev->wdt.status);
watchdog_set_nowayout(&dev->wdt, nowayout);
ret = watchdog_register_device(&dev->wdt);
if (ret)
goto disable_clk;
dev_info(&pdev->dev, "Initial timeout %d sec%s\n",
dev->wdt.timeout, nowayout ? ", nowayout" : "");
return 0;
disable_clk:
clk_disable_unprepare(dev->clk);
return ret;
}
static int armada_37xx_wdt_remove(struct platform_device *pdev)
{
struct watchdog_device *wdt = platform_get_drvdata(pdev);
struct armada_37xx_watchdog *dev = watchdog_get_drvdata(wdt);
watchdog_unregister_device(wdt);
clk_disable_unprepare(dev->clk);
return 0;
}
static void armada_37xx_wdt_shutdown(struct platform_device *pdev)
{
struct watchdog_device *wdt = platform_get_drvdata(pdev);
armada_37xx_wdt_stop(wdt);
}
static int __maybe_unused armada_37xx_wdt_suspend(struct device *dev)
{
struct watchdog_device *wdt = dev_get_drvdata(dev);
return armada_37xx_wdt_stop(wdt);
}
static int __maybe_unused armada_37xx_wdt_resume(struct device *dev)
{
struct watchdog_device *wdt = dev_get_drvdata(dev);
if (watchdog_active(wdt))
return armada_37xx_wdt_start(wdt);
return 0;
}
static const struct dev_pm_ops armada_37xx_wdt_dev_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(armada_37xx_wdt_suspend,
armada_37xx_wdt_resume)
};
#ifdef CONFIG_OF
static const struct of_device_id armada_37xx_wdt_match[] = {
{ .compatible = "marvell,armada-3700-wdt", },
{},
};
MODULE_DEVICE_TABLE(of, armada_37xx_wdt_match);
#endif
static struct platform_driver armada_37xx_wdt_driver = {
.probe = armada_37xx_wdt_probe,
.remove = armada_37xx_wdt_remove,
.shutdown = armada_37xx_wdt_shutdown,
.driver = {
.name = "armada_37xx_wdt",
.of_match_table = of_match_ptr(armada_37xx_wdt_match),
.pm = &armada_37xx_wdt_dev_pm_ops,
},
};
module_platform_driver(armada_37xx_wdt_driver);
MODULE_AUTHOR("Marek Behun <marek.behun@nic.cz>");
MODULE_DESCRIPTION("Armada 37xx CPU Watchdog");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:armada_37xx_wdt");
...@@ -25,7 +25,7 @@ ...@@ -25,7 +25,7 @@
/* If the kernel parameter wdt=1, the watchdog will be enabled at boot. /* If the kernel parameter wdt=1, the watchdog will be enabled at boot.
* Also, the wdt_period sets the watchdog timer period timeout. * Also, the wdt_period sets the watchdog timer period timeout.
* For E500 cpus the wdt_period sets which bit changing from 0->1 will * For E500 cpus the wdt_period sets which bit changing from 0->1 will
* trigger a watchog timeout. This watchdog timeout will occur 3 times, the * trigger a watchdog timeout. This watchdog timeout will occur 3 times, the
* first time nothing will happen, the second time a watchdog exception will * first time nothing will happen, the second time a watchdog exception will
* occur, and the final time the board will reset. * occur, and the final time the board will reset.
*/ */
......
...@@ -26,7 +26,7 @@ ...@@ -26,7 +26,7 @@
#include <linux/watchdog.h> #include <linux/watchdog.h>
#include <asm/nmi.h> #include <asm/nmi.h>
#define HPWDT_VERSION "2.0.0" #define HPWDT_VERSION "2.0.1"
#define SECS_TO_TICKS(secs) ((secs) * 1000 / 128) #define SECS_TO_TICKS(secs) ((secs) * 1000 / 128)
#define TICKS_TO_SECS(ticks) ((ticks) * 128 / 1000) #define TICKS_TO_SECS(ticks) ((ticks) * 128 / 1000)
#define HPWDT_MAX_TIMER TICKS_TO_SECS(65535) #define HPWDT_MAX_TIMER TICKS_TO_SECS(65535)
...@@ -162,7 +162,7 @@ static int hpwdt_pretimeout(unsigned int ulReason, struct pt_regs *regs) ...@@ -162,7 +162,7 @@ static int hpwdt_pretimeout(unsigned int ulReason, struct pt_regs *regs)
if (ilo5 && ulReason == NMI_UNKNOWN && !mynmi) if (ilo5 && ulReason == NMI_UNKNOWN && !mynmi)
return NMI_DONE; return NMI_DONE;
if (ilo5 && !pretimeout) if (ilo5 && !pretimeout && !mynmi)
return NMI_DONE; return NMI_DONE;
hpwdt_stop(); hpwdt_stop();
...@@ -205,9 +205,7 @@ static struct watchdog_device hpwdt_dev = { ...@@ -205,9 +205,7 @@ static struct watchdog_device hpwdt_dev = {
.min_timeout = 1, .min_timeout = 1,
.max_timeout = HPWDT_MAX_TIMER, .max_timeout = HPWDT_MAX_TIMER,
.timeout = DEFAULT_MARGIN, .timeout = DEFAULT_MARGIN,
#ifdef CONFIG_HPWDT_NMI_DECODING
.pretimeout = PRETIMEOUT_SEC, .pretimeout = PRETIMEOUT_SEC,
#endif
}; };
...@@ -313,6 +311,12 @@ static int hpwdt_init_one(struct pci_dev *dev, ...@@ -313,6 +311,12 @@ static int hpwdt_init_one(struct pci_dev *dev,
if (watchdog_init_timeout(&hpwdt_dev, soft_margin, NULL)) if (watchdog_init_timeout(&hpwdt_dev, soft_margin, NULL))
dev_warn(&dev->dev, "Invalid soft_margin: %d.\n", soft_margin); dev_warn(&dev->dev, "Invalid soft_margin: %d.\n", soft_margin);
if (pretimeout && hpwdt_dev.timeout <= PRETIMEOUT_SEC) {
dev_warn(&dev->dev, "timeout <= pretimeout. Setting pretimeout to zero\n");
pretimeout = 0;
}
hpwdt_dev.pretimeout = pretimeout ? PRETIMEOUT_SEC : 0;
hpwdt_dev.parent = &dev->dev; hpwdt_dev.parent = &dev->dev;
retval = watchdog_register_device(&hpwdt_dev); retval = watchdog_register_device(&hpwdt_dev);
if (retval < 0) { if (retval < 0) {
...@@ -320,9 +324,12 @@ static int hpwdt_init_one(struct pci_dev *dev, ...@@ -320,9 +324,12 @@ static int hpwdt_init_one(struct pci_dev *dev,
goto error_wd_register; goto error_wd_register;
} }
dev_info(&dev->dev, "HPE Watchdog Timer Driver: %s" dev_info(&dev->dev, "HPE Watchdog Timer Driver: Version: %s\n",
", timer margin: %d seconds (nowayout=%d).\n", HPWDT_VERSION);
HPWDT_VERSION, hpwdt_dev.timeout, nowayout); dev_info(&dev->dev, "timeout: %d seconds (nowayout=%d)\n",
hpwdt_dev.timeout, nowayout);
dev_info(&dev->dev, "pretimeout: %s.\n",
pretimeout ? "on" : "off");
if (dev->subsystem_vendor == PCI_VENDOR_ID_HP_3PAR) if (dev->subsystem_vendor == PCI_VENDOR_ID_HP_3PAR)
ilo5 = true; ilo5 = true;
...@@ -364,6 +371,9 @@ MODULE_VERSION(HPWDT_VERSION); ...@@ -364,6 +371,9 @@ MODULE_VERSION(HPWDT_VERSION);
module_param(soft_margin, int, 0); module_param(soft_margin, int, 0);
MODULE_PARM_DESC(soft_margin, "Watchdog timeout in seconds"); MODULE_PARM_DESC(soft_margin, "Watchdog timeout in seconds");
module_param_named(timeout, soft_margin, int, 0);
MODULE_PARM_DESC(timeout, "Alias of soft_margin");
module_param(nowayout, bool, 0); module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
......
...@@ -3,14 +3,10 @@ ...@@ -3,14 +3,10 @@
#ifdef CONFIG_ITCO_VENDOR_SUPPORT #ifdef CONFIG_ITCO_VENDOR_SUPPORT
extern void iTCO_vendor_pre_start(struct resource *, unsigned int); extern void iTCO_vendor_pre_start(struct resource *, unsigned int);
extern void iTCO_vendor_pre_stop(struct resource *); extern void iTCO_vendor_pre_stop(struct resource *);
extern void iTCO_vendor_pre_keepalive(struct resource *, unsigned int);
extern void iTCO_vendor_pre_set_heartbeat(unsigned int);
extern int iTCO_vendor_check_noreboot_on(void); extern int iTCO_vendor_check_noreboot_on(void);
#else #else
#define iTCO_vendor_pre_start(acpibase, heartbeat) {} #define iTCO_vendor_pre_start(acpibase, heartbeat) {}
#define iTCO_vendor_pre_stop(acpibase) {} #define iTCO_vendor_pre_stop(acpibase) {}
#define iTCO_vendor_pre_keepalive(acpibase, heartbeat) {}
#define iTCO_vendor_pre_set_heartbeat(heartbeat) {}
#define iTCO_vendor_check_noreboot_on() 1 #define iTCO_vendor_check_noreboot_on() 1
/* 1=check noreboot; 0=don't check */ /* 1=check noreboot; 0=don't check */
#endif #endif
...@@ -38,7 +38,7 @@ ...@@ -38,7 +38,7 @@
/* List of vendor support modes */ /* List of vendor support modes */
/* SuperMicro Pentium 3 Era 370SSE+-OEM1/P3TSSE */ /* SuperMicro Pentium 3 Era 370SSE+-OEM1/P3TSSE */
#define SUPERMICRO_OLD_BOARD 1 #define SUPERMICRO_OLD_BOARD 1
/* SuperMicro Pentium 4 / Xeon 4 / EMT64T Era Systems */ /* SuperMicro Pentium 4 / Xeon 4 / EMT64T Era Systems - no longer supported */
#define SUPERMICRO_NEW_BOARD 2 #define SUPERMICRO_NEW_BOARD 2
/* Broken BIOS */ /* Broken BIOS */
#define BROKEN_BIOS 911 #define BROKEN_BIOS 911
...@@ -46,8 +46,7 @@ ...@@ -46,8 +46,7 @@
static int vendorsupport; static int vendorsupport;
module_param(vendorsupport, int, 0); module_param(vendorsupport, int, 0);
MODULE_PARM_DESC(vendorsupport, "iTCO vendor specific support mode, default=" MODULE_PARM_DESC(vendorsupport, "iTCO vendor specific support mode, default="
"0 (none), 1=SuperMicro Pent3, 2=SuperMicro Pent4+, " "0 (none), 1=SuperMicro Pent3, 911=Broken SMI BIOS");
"911=Broken SMI BIOS");
/* /*
* Vendor Specific Support * Vendor Specific Support
...@@ -97,143 +96,6 @@ static void supermicro_old_pre_stop(struct resource *smires) ...@@ -97,143 +96,6 @@ static void supermicro_old_pre_stop(struct resource *smires)
outl(val32, smires->start); /* Needed to deactivate watchdog */ outl(val32, smires->start); /* Needed to deactivate watchdog */
} }
/*
* Vendor Support: 2
* Board: Super Micro Computer Inc. P4SBx, P4DPx
* iTCO chipset: ICH4
*
* Code contributed by: R. Seretny <lkpatches@paypc.com>
* Documentation obtained by R. Seretny from SuperMicro Technical Support
*
* To enable Watchdog function:
* 1. BIOS
* For P4SBx:
* BIOS setup -> Advanced -> Integrated Peripherals -> Watch Dog Feature
* For P4DPx:
* BIOS setup -> Advanced -> I/O Device Configuration -> Watch Dog
* This setting enables or disables Watchdog function. When enabled, the
* default watchdog timer is set to be 5 minutes (about 4m35s). It is
* enough to load and run the OS. The application (service or driver) has
* to take over the control once OS is running up and before watchdog
* expires.
*
* 2. JUMPER
* For P4SBx: JP39
* For P4DPx: JP37
* This jumper is used for safety. Closed is enabled. This jumper
* prevents user enables watchdog in BIOS by accident.
*
* To enable Watch Dog function, both BIOS and JUMPER must be enabled.
*
* The documentation lists motherboards P4SBx and P4DPx series as of
* 20-March-2002. However, this code works flawlessly with much newer
* motherboards, such as my X6DHR-8G2 (SuperServer 6014H-82).
*
* The original iTCO driver as written does not actually reset the
* watchdog timer on these machines, as a result they reboot after five
* minutes.
*
* NOTE: You may leave the Watchdog function disabled in the SuperMicro
* BIOS to avoid a "boot-race"... This driver will enable watchdog
* functionality even if it's disabled in the BIOS once the /dev/watchdog
* file is opened.
*/
/* I/O Port's */
#define SM_REGINDEX 0x2e /* SuperMicro ICH4+ Register Index */
#define SM_DATAIO 0x2f /* SuperMicro ICH4+ Register Data I/O */
/* Control Register's */
#define SM_CTLPAGESW 0x07 /* SuperMicro ICH4+ Control Page Switch */
#define SM_CTLPAGE 0x08 /* SuperMicro ICH4+ Control Page Num */
#define SM_WATCHENABLE 0x30 /* Watchdog enable: Bit 0: 0=off, 1=on */
#define SM_WATCHPAGE 0x87 /* Watchdog unlock control page */
#define SM_ENDWATCH 0xAA /* Watchdog lock control page */
#define SM_COUNTMODE 0xf5 /* Watchdog count mode select */
/* (Bit 3: 0 = seconds, 1 = minutes */
#define SM_WATCHTIMER 0xf6 /* 8-bits, Watchdog timer counter (RW) */
#define SM_RESETCONTROL 0xf7 /* Watchdog reset control */
/* Bit 6: timer is reset by kbd interrupt */
/* Bit 7: timer is reset by mouse interrupt */
static void supermicro_new_unlock_watchdog(void)
{
/* Write 0x87 to port 0x2e twice */
outb(SM_WATCHPAGE, SM_REGINDEX);
outb(SM_WATCHPAGE, SM_REGINDEX);
/* Switch to watchdog control page */
outb(SM_CTLPAGESW, SM_REGINDEX);
outb(SM_CTLPAGE, SM_DATAIO);
}
static void supermicro_new_lock_watchdog(void)
{
outb(SM_ENDWATCH, SM_REGINDEX);
}
static void supermicro_new_pre_start(unsigned int heartbeat)
{
unsigned int val;
supermicro_new_unlock_watchdog();
/* Watchdog timer setting needs to be in seconds*/
outb(SM_COUNTMODE, SM_REGINDEX);
val = inb(SM_DATAIO);
val &= 0xF7;
outb(val, SM_DATAIO);
/* Write heartbeat interval to WDOG */
outb(SM_WATCHTIMER, SM_REGINDEX);
outb((heartbeat & 255), SM_DATAIO);
/* Make sure keyboard/mouse interrupts don't interfere */
outb(SM_RESETCONTROL, SM_REGINDEX);
val = inb(SM_DATAIO);
val &= 0x3f;
outb(val, SM_DATAIO);
/* enable watchdog by setting bit 0 of Watchdog Enable to 1 */
outb(SM_WATCHENABLE, SM_REGINDEX);
val = inb(SM_DATAIO);
val |= 0x01;
outb(val, SM_DATAIO);
supermicro_new_lock_watchdog();
}
static void supermicro_new_pre_stop(void)
{
unsigned int val;
supermicro_new_unlock_watchdog();
/* disable watchdog by setting bit 0 of Watchdog Enable to 0 */
outb(SM_WATCHENABLE, SM_REGINDEX);
val = inb(SM_DATAIO);
val &= 0xFE;
outb(val, SM_DATAIO);
supermicro_new_lock_watchdog();
}
static void supermicro_new_pre_set_heartbeat(unsigned int heartbeat)
{
supermicro_new_unlock_watchdog();
/* reset watchdog timeout to heartveat value */
outb(SM_WATCHTIMER, SM_REGINDEX);
outb((heartbeat & 255), SM_DATAIO);
supermicro_new_lock_watchdog();
}
/* /*
* Vendor Support: 911 * Vendor Support: 911
* Board: Some Intel ICHx based motherboards * Board: Some Intel ICHx based motherboards
...@@ -298,9 +160,6 @@ void iTCO_vendor_pre_start(struct resource *smires, ...@@ -298,9 +160,6 @@ void iTCO_vendor_pre_start(struct resource *smires,
case SUPERMICRO_OLD_BOARD: case SUPERMICRO_OLD_BOARD:
supermicro_old_pre_start(smires); supermicro_old_pre_start(smires);
break; break;
case SUPERMICRO_NEW_BOARD:
supermicro_new_pre_start(heartbeat);
break;
case BROKEN_BIOS: case BROKEN_BIOS:
broken_bios_start(smires); broken_bios_start(smires);
break; break;
...@@ -314,9 +173,6 @@ void iTCO_vendor_pre_stop(struct resource *smires) ...@@ -314,9 +173,6 @@ void iTCO_vendor_pre_stop(struct resource *smires)
case SUPERMICRO_OLD_BOARD: case SUPERMICRO_OLD_BOARD:
supermicro_old_pre_stop(smires); supermicro_old_pre_stop(smires);
break; break;
case SUPERMICRO_NEW_BOARD:
supermicro_new_pre_stop();
break;
case BROKEN_BIOS: case BROKEN_BIOS:
broken_bios_stop(smires); broken_bios_stop(smires);
break; break;
...@@ -324,20 +180,6 @@ void iTCO_vendor_pre_stop(struct resource *smires) ...@@ -324,20 +180,6 @@ void iTCO_vendor_pre_stop(struct resource *smires)
} }
EXPORT_SYMBOL(iTCO_vendor_pre_stop); EXPORT_SYMBOL(iTCO_vendor_pre_stop);
void iTCO_vendor_pre_keepalive(struct resource *smires, unsigned int heartbeat)
{
if (vendorsupport == SUPERMICRO_NEW_BOARD)
supermicro_new_pre_set_heartbeat(heartbeat);
}
EXPORT_SYMBOL(iTCO_vendor_pre_keepalive);
void iTCO_vendor_pre_set_heartbeat(unsigned int heartbeat)
{
if (vendorsupport == SUPERMICRO_NEW_BOARD)
supermicro_new_pre_set_heartbeat(heartbeat);
}
EXPORT_SYMBOL(iTCO_vendor_pre_set_heartbeat);
int iTCO_vendor_check_noreboot_on(void) int iTCO_vendor_check_noreboot_on(void)
{ {
switch (vendorsupport) { switch (vendorsupport) {
...@@ -351,6 +193,12 @@ EXPORT_SYMBOL(iTCO_vendor_check_noreboot_on); ...@@ -351,6 +193,12 @@ EXPORT_SYMBOL(iTCO_vendor_check_noreboot_on);
static int __init iTCO_vendor_init_module(void) static int __init iTCO_vendor_init_module(void)
{ {
if (vendorsupport == SUPERMICRO_NEW_BOARD) {
pr_warn("Option vendorsupport=%d is no longer supported, "
"please use the w83627hf_wdt driver instead\n",
SUPERMICRO_NEW_BOARD);
return -EINVAL;
}
pr_info("vendor-support=%d\n", vendorsupport); pr_info("vendor-support=%d\n", vendorsupport);
return 0; return 0;
} }
......
...@@ -304,8 +304,6 @@ static int iTCO_wdt_ping(struct watchdog_device *wd_dev) ...@@ -304,8 +304,6 @@ static int iTCO_wdt_ping(struct watchdog_device *wd_dev)
spin_lock(&p->io_lock); spin_lock(&p->io_lock);
iTCO_vendor_pre_keepalive(p->smi_res, wd_dev->timeout);
/* Reload the timer by writing to the TCO Timer Counter register */ /* Reload the timer by writing to the TCO Timer Counter register */
if (p->iTCO_version >= 2) { if (p->iTCO_version >= 2) {
outw(0x01, TCO_RLD(p)); outw(0x01, TCO_RLD(p));
...@@ -342,8 +340,6 @@ static int iTCO_wdt_set_timeout(struct watchdog_device *wd_dev, unsigned int t) ...@@ -342,8 +340,6 @@ static int iTCO_wdt_set_timeout(struct watchdog_device *wd_dev, unsigned int t)
(p->iTCO_version == 1 && tmrval > 0x03f)) (p->iTCO_version == 1 && tmrval > 0x03f))
return -EINVAL; return -EINVAL;
iTCO_vendor_pre_set_heartbeat(tmrval);
/* Write new heartbeat to watchdog */ /* Write new heartbeat to watchdog */
if (p->iTCO_version >= 2) { if (p->iTCO_version >= 2) {
spin_lock(&p->io_lock); spin_lock(&p->io_lock);
......
...@@ -8,11 +8,8 @@ ...@@ -8,11 +8,8 @@
* Based on EP93xx wdt driver * Based on EP93xx wdt driver
*/ */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h> #include <linux/module.h>
#include <linux/fs.h> #include <linux/bitops.h>
#include <linux/miscdevice.h>
#include <linux/watchdog.h> #include <linux/watchdog.h>
#include <linux/of_platform.h> #include <linux/of_platform.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
...@@ -40,169 +37,128 @@ ...@@ -40,169 +37,128 @@
* essentially the following two magic passwords need to be written to allow * essentially the following two magic passwords need to be written to allow
* IO access to the WDT core * IO access to the WDT core
*/ */
#define LTQ_WDT_PW1 0x00BE0000 #define LTQ_WDT_CR_PW1 0x00BE0000
#define LTQ_WDT_PW2 0x00DC0000 #define LTQ_WDT_CR_PW2 0x00DC0000
#define LTQ_WDT_CR 0x0 /* watchdog control register */ #define LTQ_WDT_CR 0x0 /* watchdog control register */
#define LTQ_WDT_SR 0x8 /* watchdog status register */ #define LTQ_WDT_CR_GEN BIT(31) /* enable bit */
/* Pre-warning limit set to 1/16 of max WDT period */
#define LTQ_WDT_CR_PWL (0x3 << 26)
/* set clock divider to 0x40000 */
#define LTQ_WDT_CR_CLKDIV (0x3 << 24)
#define LTQ_WDT_CR_PW_MASK GENMASK(23, 16) /* Password field */
#define LTQ_WDT_CR_MAX_TIMEOUT ((1 << 16) - 1) /* The reload field is 16 bit */
#define LTQ_WDT_SR 0x8 /* watchdog status register */
#define LTQ_WDT_SR_EN BIT(31) /* Enable */
#define LTQ_WDT_SR_VALUE_MASK GENMASK(15, 0) /* Timer value */
#define LTQ_WDT_SR_EN (0x1 << 31) /* enable bit */
#define LTQ_WDT_SR_PWD (0x3 << 26) /* turn on power */
#define LTQ_WDT_SR_CLKDIV (0x3 << 24) /* turn on clock and set */
/* divider to 0x40000 */
#define LTQ_WDT_DIVIDER 0x40000 #define LTQ_WDT_DIVIDER 0x40000
#define LTQ_MAX_TIMEOUT ((1 << 16) - 1) /* the reload field is 16 bit */
static bool nowayout = WATCHDOG_NOWAYOUT; static bool nowayout = WATCHDOG_NOWAYOUT;
static void __iomem *ltq_wdt_membase; struct ltq_wdt_hw {
static unsigned long ltq_io_region_clk_rate; int (*bootstatus_get)(struct device *dev);
};
static unsigned long ltq_wdt_bootstatus; struct ltq_wdt_priv {
static unsigned long ltq_wdt_in_use; struct watchdog_device wdt;
static int ltq_wdt_timeout = 30; void __iomem *membase;
static int ltq_wdt_ok_to_close; unsigned long clk_rate;
};
static void static u32 ltq_wdt_r32(struct ltq_wdt_priv *priv, u32 offset)
ltq_wdt_enable(void)
{ {
unsigned long int timeout = ltq_wdt_timeout * return __raw_readl(priv->membase + offset);
(ltq_io_region_clk_rate / LTQ_WDT_DIVIDER) + 0x1000;
if (timeout > LTQ_MAX_TIMEOUT)
timeout = LTQ_MAX_TIMEOUT;
/* write the first password magic */
ltq_w32(LTQ_WDT_PW1, ltq_wdt_membase + LTQ_WDT_CR);
/* write the second magic plus the configuration and new timeout */
ltq_w32(LTQ_WDT_SR_EN | LTQ_WDT_SR_PWD | LTQ_WDT_SR_CLKDIV |
LTQ_WDT_PW2 | timeout, ltq_wdt_membase + LTQ_WDT_CR);
} }
static void static void ltq_wdt_w32(struct ltq_wdt_priv *priv, u32 val, u32 offset)
ltq_wdt_disable(void)
{ {
/* write the first password magic */ __raw_writel(val, priv->membase + offset);
ltq_w32(LTQ_WDT_PW1, ltq_wdt_membase + LTQ_WDT_CR);
/*
* write the second password magic with no config
* this turns the watchdog off
*/
ltq_w32(LTQ_WDT_PW2, ltq_wdt_membase + LTQ_WDT_CR);
} }
static ssize_t static void ltq_wdt_mask(struct ltq_wdt_priv *priv, u32 clear, u32 set,
ltq_wdt_write(struct file *file, const char __user *data, u32 offset)
size_t len, loff_t *ppos)
{ {
if (len) { u32 val = ltq_wdt_r32(priv, offset);
if (!nowayout) {
size_t i;
ltq_wdt_ok_to_close = 0;
for (i = 0; i != len; i++) {
char c;
if (get_user(c, data + i))
return -EFAULT;
if (c == 'V')
ltq_wdt_ok_to_close = 1;
else
ltq_wdt_ok_to_close = 0;
}
}
ltq_wdt_enable();
}
return len; val &= ~(clear);
val |= set;
ltq_wdt_w32(priv, val, offset);
} }
static struct watchdog_info ident = { static struct ltq_wdt_priv *ltq_wdt_get_priv(struct watchdog_device *wdt)
{
return container_of(wdt, struct ltq_wdt_priv, wdt);
}
static struct watchdog_info ltq_wdt_info = {
.options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING |
WDIOF_CARDRESET, WDIOF_CARDRESET,
.identity = "ltq_wdt", .identity = "ltq_wdt",
}; };
static long static int ltq_wdt_start(struct watchdog_device *wdt)
ltq_wdt_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
{ {
int ret = -ENOTTY; struct ltq_wdt_priv *priv = ltq_wdt_get_priv(wdt);
u32 timeout;
switch (cmd) {
case WDIOC_GETSUPPORT: timeout = wdt->timeout * priv->clk_rate;
ret = copy_to_user((struct watchdog_info __user *)arg, &ident,
sizeof(ident)) ? -EFAULT : 0; ltq_wdt_mask(priv, LTQ_WDT_CR_PW_MASK, LTQ_WDT_CR_PW1, LTQ_WDT_CR);
break; /* write the second magic plus the configuration and new timeout */
ltq_wdt_mask(priv, LTQ_WDT_CR_PW_MASK | LTQ_WDT_CR_MAX_TIMEOUT,
case WDIOC_GETBOOTSTATUS: LTQ_WDT_CR_GEN | LTQ_WDT_CR_PWL | LTQ_WDT_CR_CLKDIV |
ret = put_user(ltq_wdt_bootstatus, (int __user *)arg); LTQ_WDT_CR_PW2 | timeout,
break; LTQ_WDT_CR);
case WDIOC_GETSTATUS: return 0;
ret = put_user(0, (int __user *)arg);
break;
case WDIOC_SETTIMEOUT:
ret = get_user(ltq_wdt_timeout, (int __user *)arg);
if (!ret)
ltq_wdt_enable();
/* intentional drop through */
case WDIOC_GETTIMEOUT:
ret = put_user(ltq_wdt_timeout, (int __user *)arg);
break;
case WDIOC_KEEPALIVE:
ltq_wdt_enable();
ret = 0;
break;
}
return ret;
} }
static int static int ltq_wdt_stop(struct watchdog_device *wdt)
ltq_wdt_open(struct inode *inode, struct file *file)
{ {
if (test_and_set_bit(0, &ltq_wdt_in_use)) struct ltq_wdt_priv *priv = ltq_wdt_get_priv(wdt);
return -EBUSY;
ltq_wdt_in_use = 1;
ltq_wdt_enable();
return nonseekable_open(inode, file); ltq_wdt_mask(priv, LTQ_WDT_CR_PW_MASK, LTQ_WDT_CR_PW1, LTQ_WDT_CR);
ltq_wdt_mask(priv, LTQ_WDT_CR_GEN | LTQ_WDT_CR_PW_MASK,
LTQ_WDT_CR_PW2, LTQ_WDT_CR);
return 0;
} }
static int static int ltq_wdt_ping(struct watchdog_device *wdt)
ltq_wdt_release(struct inode *inode, struct file *file)
{ {
if (ltq_wdt_ok_to_close) struct ltq_wdt_priv *priv = ltq_wdt_get_priv(wdt);
ltq_wdt_disable(); u32 timeout;
else
pr_err("watchdog closed without warning\n"); timeout = wdt->timeout * priv->clk_rate;
ltq_wdt_ok_to_close = 0;
clear_bit(0, &ltq_wdt_in_use); ltq_wdt_mask(priv, LTQ_WDT_CR_PW_MASK, LTQ_WDT_CR_PW1, LTQ_WDT_CR);
/* write the second magic plus the configuration and new timeout */
ltq_wdt_mask(priv, LTQ_WDT_CR_PW_MASK | LTQ_WDT_CR_MAX_TIMEOUT,
LTQ_WDT_CR_PW2 | timeout, LTQ_WDT_CR);
return 0; return 0;
} }
static const struct file_operations ltq_wdt_fops = { static unsigned int ltq_wdt_get_timeleft(struct watchdog_device *wdt)
.owner = THIS_MODULE, {
.write = ltq_wdt_write, struct ltq_wdt_priv *priv = ltq_wdt_get_priv(wdt);
.unlocked_ioctl = ltq_wdt_ioctl, u64 timeout;
.open = ltq_wdt_open,
.release = ltq_wdt_release,
.llseek = no_llseek,
};
static struct miscdevice ltq_wdt_miscdev = { timeout = ltq_wdt_r32(priv, LTQ_WDT_SR) & LTQ_WDT_SR_VALUE_MASK;
.minor = WATCHDOG_MINOR, return do_div(timeout, priv->clk_rate);
.name = "watchdog", }
.fops = &ltq_wdt_fops,
};
typedef int (*ltq_wdt_bootstatus_set)(struct platform_device *pdev); static const struct watchdog_ops ltq_wdt_ops = {
.owner = THIS_MODULE,
.start = ltq_wdt_start,
.stop = ltq_wdt_stop,
.ping = ltq_wdt_ping,
.get_timeleft = ltq_wdt_get_timeleft,
};
static int ltq_wdt_bootstatus_xrx(struct platform_device *pdev) static int ltq_wdt_xrx_bootstatus_get(struct device *dev)
{ {
struct device *dev = &pdev->dev;
struct regmap *rcu_regmap; struct regmap *rcu_regmap;
u32 val; u32 val;
int err; int err;
...@@ -216,14 +172,13 @@ static int ltq_wdt_bootstatus_xrx(struct platform_device *pdev) ...@@ -216,14 +172,13 @@ static int ltq_wdt_bootstatus_xrx(struct platform_device *pdev)
return err; return err;
if (val & LTQ_XRX_RCU_RST_STAT_WDT) if (val & LTQ_XRX_RCU_RST_STAT_WDT)
ltq_wdt_bootstatus = WDIOF_CARDRESET; return WDIOF_CARDRESET;
return 0; return 0;
} }
static int ltq_wdt_bootstatus_falcon(struct platform_device *pdev) static int ltq_wdt_falcon_bootstatus_get(struct device *dev)
{ {
struct device *dev = &pdev->dev;
struct regmap *rcu_regmap; struct regmap *rcu_regmap;
u32 val; u32 val;
int err; int err;
...@@ -238,62 +193,90 @@ static int ltq_wdt_bootstatus_falcon(struct platform_device *pdev) ...@@ -238,62 +193,90 @@ static int ltq_wdt_bootstatus_falcon(struct platform_device *pdev)
return err; return err;
if ((val & LTQ_FALCON_SYS1_CPU0RS_MASK) == LTQ_FALCON_SYS1_CPU0RS_WDT) if ((val & LTQ_FALCON_SYS1_CPU0RS_MASK) == LTQ_FALCON_SYS1_CPU0RS_WDT)
ltq_wdt_bootstatus = WDIOF_CARDRESET; return WDIOF_CARDRESET;
return 0; return 0;
} }
static int static int ltq_wdt_probe(struct platform_device *pdev)
ltq_wdt_probe(struct platform_device *pdev)
{ {
struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); struct device *dev = &pdev->dev;
struct ltq_wdt_priv *priv;
struct watchdog_device *wdt;
struct resource *res;
struct clk *clk; struct clk *clk;
ltq_wdt_bootstatus_set ltq_wdt_bootstatus_set; const struct ltq_wdt_hw *ltq_wdt_hw;
int ret; int ret;
u32 status;
ltq_wdt_membase = devm_ioremap_resource(&pdev->dev, res); priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (IS_ERR(ltq_wdt_membase)) if (!priv)
return PTR_ERR(ltq_wdt_membase); return -ENOMEM;
ltq_wdt_bootstatus_set = of_device_get_match_data(&pdev->dev); res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (ltq_wdt_bootstatus_set) { priv->membase = devm_ioremap_resource(dev, res);
ret = ltq_wdt_bootstatus_set(pdev); if (IS_ERR(priv->membase))
if (ret) return PTR_ERR(priv->membase);
return ret;
}
/* we do not need to enable the clock as it is always running */ /* we do not need to enable the clock as it is always running */
clk = clk_get_io(); clk = clk_get_io();
if (IS_ERR(clk)) { priv->clk_rate = clk_get_rate(clk) / LTQ_WDT_DIVIDER;
dev_err(&pdev->dev, "Failed to get clock\n"); if (!priv->clk_rate) {
return -ENOENT; dev_err(dev, "clock rate less than divider %i\n",
LTQ_WDT_DIVIDER);
return -EINVAL;
} }
ltq_io_region_clk_rate = clk_get_rate(clk);
clk_put(clk);
dev_info(&pdev->dev, "Init done\n"); wdt = &priv->wdt;
return misc_register(&ltq_wdt_miscdev); wdt->info = &ltq_wdt_info;
} wdt->ops = &ltq_wdt_ops;
wdt->min_timeout = 1;
wdt->max_timeout = LTQ_WDT_CR_MAX_TIMEOUT / priv->clk_rate;
wdt->timeout = wdt->max_timeout;
wdt->parent = dev;
ltq_wdt_hw = of_device_get_match_data(dev);
if (ltq_wdt_hw && ltq_wdt_hw->bootstatus_get) {
ret = ltq_wdt_hw->bootstatus_get(dev);
if (ret >= 0)
wdt->bootstatus = ret;
}
static int watchdog_set_nowayout(wdt, nowayout);
ltq_wdt_remove(struct platform_device *pdev) watchdog_init_timeout(wdt, 0, dev);
{
misc_deregister(&ltq_wdt_miscdev); status = ltq_wdt_r32(priv, LTQ_WDT_SR);
if (status & LTQ_WDT_SR_EN) {
/*
* If the watchdog is already running overwrite it with our
* new settings. Stop is not needed as the start call will
* replace all settings anyway.
*/
ltq_wdt_start(wdt);
set_bit(WDOG_HW_RUNNING, &wdt->status);
}
return 0; return devm_watchdog_register_device(dev, wdt);
} }
static const struct ltq_wdt_hw ltq_wdt_xrx100 = {
.bootstatus_get = ltq_wdt_xrx_bootstatus_get,
};
static const struct ltq_wdt_hw ltq_wdt_falcon = {
.bootstatus_get = ltq_wdt_falcon_bootstatus_get,
};
static const struct of_device_id ltq_wdt_match[] = { static const struct of_device_id ltq_wdt_match[] = {
{ .compatible = "lantiq,wdt", .data = NULL}, { .compatible = "lantiq,wdt", .data = NULL },
{ .compatible = "lantiq,xrx100-wdt", .data = ltq_wdt_bootstatus_xrx }, { .compatible = "lantiq,xrx100-wdt", .data = &ltq_wdt_xrx100 },
{ .compatible = "lantiq,falcon-wdt", .data = ltq_wdt_bootstatus_falcon }, { .compatible = "lantiq,falcon-wdt", .data = &ltq_wdt_falcon },
{}, {},
}; };
MODULE_DEVICE_TABLE(of, ltq_wdt_match); MODULE_DEVICE_TABLE(of, ltq_wdt_match);
static struct platform_driver ltq_wdt_driver = { static struct platform_driver ltq_wdt_driver = {
.probe = ltq_wdt_probe, .probe = ltq_wdt_probe,
.remove = ltq_wdt_remove,
.driver = { .driver = {
.name = "wdt", .name = "wdt",
.of_match_table = ltq_wdt_match, .of_match_table = ltq_wdt_match,
......
...@@ -17,8 +17,6 @@ ...@@ -17,8 +17,6 @@
* option) any later version. * option) any later version.
*/ */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/kernel.h> #include <linux/kernel.h>
...@@ -49,6 +47,7 @@ struct mpc8xxx_wdt { ...@@ -49,6 +47,7 @@ struct mpc8xxx_wdt {
struct mpc8xxx_wdt_type { struct mpc8xxx_wdt_type {
int prescaler; int prescaler;
bool hw_enabled; bool hw_enabled;
u32 rsr_mask;
}; };
struct mpc8xxx_wdt_ddata { struct mpc8xxx_wdt_ddata {
...@@ -137,36 +136,55 @@ static int mpc8xxx_wdt_probe(struct platform_device *ofdev) ...@@ -137,36 +136,55 @@ static int mpc8xxx_wdt_probe(struct platform_device *ofdev)
struct mpc8xxx_wdt_ddata *ddata; struct mpc8xxx_wdt_ddata *ddata;
u32 freq = fsl_get_sys_freq(); u32 freq = fsl_get_sys_freq();
bool enabled; bool enabled;
struct device *dev = &ofdev->dev;
wdt_type = of_device_get_match_data(&ofdev->dev); wdt_type = of_device_get_match_data(dev);
if (!wdt_type) if (!wdt_type)
return -EINVAL; return -EINVAL;
if (!freq || freq == -1) if (!freq || freq == -1)
return -EINVAL; return -EINVAL;
ddata = devm_kzalloc(&ofdev->dev, sizeof(*ddata), GFP_KERNEL); ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL);
if (!ddata) if (!ddata)
return -ENOMEM; return -ENOMEM;
res = platform_get_resource(ofdev, IORESOURCE_MEM, 0); res = platform_get_resource(ofdev, IORESOURCE_MEM, 0);
ddata->base = devm_ioremap_resource(&ofdev->dev, res); ddata->base = devm_ioremap_resource(dev, res);
if (IS_ERR(ddata->base)) if (IS_ERR(ddata->base))
return PTR_ERR(ddata->base); return PTR_ERR(ddata->base);
enabled = in_be32(&ddata->base->swcrr) & SWCRR_SWEN; enabled = in_be32(&ddata->base->swcrr) & SWCRR_SWEN;
if (!enabled && wdt_type->hw_enabled) { if (!enabled && wdt_type->hw_enabled) {
pr_info("could not be enabled in software\n"); dev_info(dev, "could not be enabled in software\n");
return -ENODEV; return -ENODEV;
} }
res = platform_get_resource(ofdev, IORESOURCE_MEM, 1);
if (res) {
bool status;
u32 __iomem *rsr = ioremap(res->start, resource_size(res));
if (!rsr)
return -ENOMEM;
status = in_be32(rsr) & wdt_type->rsr_mask;
ddata->wdd.bootstatus = status ? WDIOF_CARDRESET : 0;
/* clear reset status bits related to watchdog timer */
out_be32(rsr, wdt_type->rsr_mask);
iounmap(rsr);
dev_info(dev, "Last boot was %scaused by watchdog\n",
status ? "" : "not ");
}
spin_lock_init(&ddata->lock); spin_lock_init(&ddata->lock);
ddata->wdd.info = &mpc8xxx_wdt_info, ddata->wdd.info = &mpc8xxx_wdt_info,
ddata->wdd.ops = &mpc8xxx_wdt_ops, ddata->wdd.ops = &mpc8xxx_wdt_ops,
ddata->wdd.timeout = WATCHDOG_TIMEOUT; ddata->wdd.timeout = WATCHDOG_TIMEOUT;
watchdog_init_timeout(&ddata->wdd, timeout, &ofdev->dev); watchdog_init_timeout(&ddata->wdd, timeout, dev);
watchdog_set_nowayout(&ddata->wdd, nowayout); watchdog_set_nowayout(&ddata->wdd, nowayout);
...@@ -189,12 +207,13 @@ static int mpc8xxx_wdt_probe(struct platform_device *ofdev) ...@@ -189,12 +207,13 @@ static int mpc8xxx_wdt_probe(struct platform_device *ofdev)
ret = watchdog_register_device(&ddata->wdd); ret = watchdog_register_device(&ddata->wdd);
if (ret) { if (ret) {
pr_err("cannot register watchdog device (err=%d)\n", ret); dev_err(dev, "cannot register watchdog device (err=%d)\n", ret);
return ret; return ret;
} }
pr_info("WDT driver for MPC8xxx initialized. mode:%s timeout=%d sec\n", dev_info(dev,
reset ? "reset" : "interrupt", ddata->wdd.timeout); "WDT driver for MPC8xxx initialized. mode:%s timeout=%d sec\n",
reset ? "reset" : "interrupt", ddata->wdd.timeout);
platform_set_drvdata(ofdev, ddata); platform_set_drvdata(ofdev, ddata);
return 0; return 0;
...@@ -204,8 +223,8 @@ static int mpc8xxx_wdt_remove(struct platform_device *ofdev) ...@@ -204,8 +223,8 @@ static int mpc8xxx_wdt_remove(struct platform_device *ofdev)
{ {
struct mpc8xxx_wdt_ddata *ddata = platform_get_drvdata(ofdev); struct mpc8xxx_wdt_ddata *ddata = platform_get_drvdata(ofdev);
pr_crit("Watchdog removed, expect the %s soon!\n", dev_crit(&ofdev->dev, "Watchdog removed, expect the %s soon!\n",
reset ? "reset" : "machine check exception"); reset ? "reset" : "machine check exception");
watchdog_unregister_device(&ddata->wdd); watchdog_unregister_device(&ddata->wdd);
return 0; return 0;
...@@ -216,6 +235,7 @@ static const struct of_device_id mpc8xxx_wdt_match[] = { ...@@ -216,6 +235,7 @@ static const struct of_device_id mpc8xxx_wdt_match[] = {
.compatible = "mpc83xx_wdt", .compatible = "mpc83xx_wdt",
.data = &(struct mpc8xxx_wdt_type) { .data = &(struct mpc8xxx_wdt_type) {
.prescaler = 0x10000, .prescaler = 0x10000,
.rsr_mask = BIT(3), /* RSR Bit SWRS */
}, },
}, },
{ {
...@@ -223,6 +243,7 @@ static const struct of_device_id mpc8xxx_wdt_match[] = { ...@@ -223,6 +243,7 @@ static const struct of_device_id mpc8xxx_wdt_match[] = {
.data = &(struct mpc8xxx_wdt_type) { .data = &(struct mpc8xxx_wdt_type) {
.prescaler = 0x10000, .prescaler = 0x10000,
.hw_enabled = true, .hw_enabled = true,
.rsr_mask = BIT(20), /* RSTRSCR Bit WDT_RR */
}, },
}, },
{ {
...@@ -230,6 +251,7 @@ static const struct of_device_id mpc8xxx_wdt_match[] = { ...@@ -230,6 +251,7 @@ static const struct of_device_id mpc8xxx_wdt_match[] = {
.data = &(struct mpc8xxx_wdt_type) { .data = &(struct mpc8xxx_wdt_type) {
.prescaler = 0x800, .prescaler = 0x800,
.hw_enabled = true, .hw_enabled = true,
.rsr_mask = BIT(28), /* RSR Bit SWRS */
}, },
}, },
{}, {},
......
// SPDX-License-Identifier: GPL-2.0
/* /*
* Watchdog driver for Renesas WDT watchdog * Watchdog driver for Renesas WDT watchdog
* *
* Copyright (C) 2015-17 Wolfram Sang, Sang Engineering <wsa@sang-engineering.com> * Copyright (C) 2015-17 Wolfram Sang, Sang Engineering <wsa@sang-engineering.com>
* Copyright (C) 2015-17 Renesas Electronics Corporation * Copyright (C) 2015-17 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/bitops.h>
#include <linux/clk.h> #include <linux/clk.h>
...@@ -234,6 +231,7 @@ static int rwdt_probe(struct platform_device *pdev) ...@@ -234,6 +231,7 @@ static int rwdt_probe(struct platform_device *pdev)
watchdog_set_drvdata(&priv->wdev, priv); watchdog_set_drvdata(&priv->wdev, priv);
watchdog_set_nowayout(&priv->wdev, nowayout); watchdog_set_nowayout(&priv->wdev, nowayout);
watchdog_set_restart_priority(&priv->wdev, 0); watchdog_set_restart_priority(&priv->wdev, 0);
watchdog_stop_on_unregister(&priv->wdev);
/* This overrides the default timeout only if DT configuration was found */ /* This overrides the default timeout only if DT configuration was found */
ret = watchdog_init_timeout(&priv->wdev, 0, &pdev->dev); ret = watchdog_init_timeout(&priv->wdev, 0, &pdev->dev);
......
// SPDX-License-Identifier: GPL-2.0
/* /*
* Renesas RZ/A Series WDT Driver * Renesas RZ/A Series WDT Driver
* *
* Copyright (C) 2017 Renesas Electronics America, Inc. * Copyright (C) 2017 Renesas Electronics America, Inc.
* Copyright (C) 2017 Chris Brandt * Copyright (C) 2017 Chris Brandt
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*/ */
#include <linux/bitops.h> #include <linux/bitops.h>
...@@ -14,6 +11,7 @@ ...@@ -14,6 +11,7 @@
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of_address.h> #include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/watchdog.h> #include <linux/watchdog.h>
...@@ -34,12 +32,45 @@ ...@@ -34,12 +32,45 @@
#define WRCSR_RSTE BIT(6) #define WRCSR_RSTE BIT(6)
#define WRCSR_CLEAR_WOVF 0xA500 /* special value */ #define WRCSR_CLEAR_WOVF 0xA500 /* special value */
/* The maximum CKS register setting value to get the longest timeout */
#define CKS_3BIT 0x7
#define CKS_4BIT 0xF
#define DIVIDER_3BIT 16384 /* Clock divider when CKS = 0x7 */
#define DIVIDER_4BIT 4194304 /* Clock divider when CKS = 0xF */
struct rza_wdt { struct rza_wdt {
struct watchdog_device wdev; struct watchdog_device wdev;
void __iomem *base; void __iomem *base;
struct clk *clk; struct clk *clk;
u8 count;
u8 cks;
}; };
static void rza_wdt_calc_timeout(struct rza_wdt *priv, int timeout)
{
unsigned long rate = clk_get_rate(priv->clk);
unsigned int ticks;
if (priv->cks == CKS_4BIT) {
ticks = DIV_ROUND_UP(timeout * rate, DIVIDER_4BIT);
/*
* Since max_timeout was set in probe, we know that the timeout
* value passed will never calculate to a tick value greater
* than 256.
*/
priv->count = 256 - ticks;
} else {
/* Start timer with longest timeout */
priv->count = 0;
}
pr_debug("%s: timeout set to %u (WTCNT=%d)\n", __func__,
timeout, priv->count);
}
static int rza_wdt_start(struct watchdog_device *wdev) static int rza_wdt_start(struct watchdog_device *wdev)
{ {
struct rza_wdt *priv = watchdog_get_drvdata(wdev); struct rza_wdt *priv = watchdog_get_drvdata(wdev);
...@@ -51,13 +82,12 @@ static int rza_wdt_start(struct watchdog_device *wdev) ...@@ -51,13 +82,12 @@ static int rza_wdt_start(struct watchdog_device *wdev)
readb(priv->base + WRCSR); readb(priv->base + WRCSR);
writew(WRCSR_CLEAR_WOVF, priv->base + WRCSR); writew(WRCSR_CLEAR_WOVF, priv->base + WRCSR);
/* rza_wdt_calc_timeout(priv, wdev->timeout);
* Start timer with slowest clock source and reset option enabled.
*/
writew(WRCSR_MAGIC | WRCSR_RSTE, priv->base + WRCSR); writew(WRCSR_MAGIC | WRCSR_RSTE, priv->base + WRCSR);
writew(WTCNT_MAGIC | 0, priv->base + WTCNT); writew(WTCNT_MAGIC | priv->count, priv->base + WTCNT);
writew(WTCSR_MAGIC | WTSCR_WT | WTSCR_TME | WTSCR_CKS(7), writew(WTCSR_MAGIC | WTSCR_WT | WTSCR_TME |
priv->base + WTCSR); WTSCR_CKS(priv->cks), priv->base + WTCSR);
return 0; return 0;
} }
...@@ -75,8 +105,17 @@ static int rza_wdt_ping(struct watchdog_device *wdev) ...@@ -75,8 +105,17 @@ static int rza_wdt_ping(struct watchdog_device *wdev)
{ {
struct rza_wdt *priv = watchdog_get_drvdata(wdev); struct rza_wdt *priv = watchdog_get_drvdata(wdev);
writew(WTCNT_MAGIC | 0, priv->base + WTCNT); writew(WTCNT_MAGIC | priv->count, priv->base + WTCNT);
pr_debug("%s: timeout = %u\n", __func__, wdev->timeout);
return 0;
}
static int rza_set_timeout(struct watchdog_device *wdev, unsigned int timeout)
{
wdev->timeout = timeout;
rza_wdt_start(wdev);
return 0; return 0;
} }
...@@ -121,6 +160,7 @@ static const struct watchdog_ops rza_wdt_ops = { ...@@ -121,6 +160,7 @@ static const struct watchdog_ops rza_wdt_ops = {
.start = rza_wdt_start, .start = rza_wdt_start,
.stop = rza_wdt_stop, .stop = rza_wdt_stop,
.ping = rza_wdt_ping, .ping = rza_wdt_ping,
.set_timeout = rza_set_timeout,
.restart = rza_wdt_restart, .restart = rza_wdt_restart,
}; };
...@@ -150,20 +190,28 @@ static int rza_wdt_probe(struct platform_device *pdev) ...@@ -150,20 +190,28 @@ static int rza_wdt_probe(struct platform_device *pdev)
return -ENOENT; return -ENOENT;
} }
/* Assume slowest clock rate possible (CKS=7) */
rate /= 16384;
priv->wdev.info = &rza_wdt_ident, priv->wdev.info = &rza_wdt_ident,
priv->wdev.ops = &rza_wdt_ops, priv->wdev.ops = &rza_wdt_ops,
priv->wdev.parent = &pdev->dev; priv->wdev.parent = &pdev->dev;
/* priv->cks = (u8)(uintptr_t)of_device_get_match_data(&pdev->dev);
* Since the max possible timeout of our 8-bit count register is less if (priv->cks == CKS_4BIT) {
* than a second, we must use max_hw_heartbeat_ms. /* Assume slowest clock rate possible (CKS=0xF) */
*/ priv->wdev.max_timeout = (DIVIDER_4BIT * U8_MAX) / rate;
priv->wdev.max_hw_heartbeat_ms = (1000 * U8_MAX) / rate;
dev_dbg(&pdev->dev, "max hw timeout of %dms\n", } else if (priv->cks == CKS_3BIT) {
priv->wdev.max_hw_heartbeat_ms); /* Assume slowest clock rate possible (CKS=7) */
rate /= DIVIDER_3BIT;
/*
* Since the max possible timeout of our 8-bit count
* register is less than a second, we must use
* max_hw_heartbeat_ms.
*/
priv->wdev.max_hw_heartbeat_ms = (1000 * U8_MAX) / rate;
dev_dbg(&pdev->dev, "max hw timeout of %dms\n",
priv->wdev.max_hw_heartbeat_ms);
}
priv->wdev.min_timeout = 1; priv->wdev.min_timeout = 1;
priv->wdev.timeout = DEFAULT_TIMEOUT; priv->wdev.timeout = DEFAULT_TIMEOUT;
...@@ -179,7 +227,8 @@ static int rza_wdt_probe(struct platform_device *pdev) ...@@ -179,7 +227,8 @@ static int rza_wdt_probe(struct platform_device *pdev)
} }
static const struct of_device_id rza_wdt_of_match[] = { static const struct of_device_id rza_wdt_of_match[] = {
{ .compatible = "renesas,rza-wdt", }, { .compatible = "renesas,r7s9210-wdt", .data = (void *)CKS_4BIT, },
{ .compatible = "renesas,rza-wdt", .data = (void *)CKS_3BIT, },
{ /* sentinel */ } { /* sentinel */ }
}; };
MODULE_DEVICE_TABLE(of, rza_wdt_of_match); MODULE_DEVICE_TABLE(of, rza_wdt_of_match);
......
...@@ -247,11 +247,7 @@ static int sama5d4_wdt_probe(struct platform_device *pdev) ...@@ -247,11 +247,7 @@ static int sama5d4_wdt_probe(struct platform_device *pdev)
} }
} }
ret = watchdog_init_timeout(wdd, wdt_timeout, &pdev->dev); watchdog_init_timeout(wdd, wdt_timeout, &pdev->dev);
if (ret) {
dev_err(&pdev->dev, "unable to set timeout value\n");
return ret;
}
timeout = WDT_SEC2TICKS(wdd->timeout); timeout = WDT_SEC2TICKS(wdd->timeout);
......
...@@ -135,6 +135,7 @@ static int ts4800_wdt_probe(struct platform_device *pdev) ...@@ -135,6 +135,7 @@ static int ts4800_wdt_probe(struct platform_device *pdev)
/* set regmap and offset to know where to write */ /* set regmap and offset to know where to write */
wdt->feed_offset = reg; wdt->feed_offset = reg;
wdt->regmap = syscon_node_to_regmap(syscon_np); wdt->regmap = syscon_node_to_regmap(syscon_np);
of_node_put(syscon_np);
if (IS_ERR(wdt->regmap)) { if (IS_ERR(wdt->regmap)) {
dev_err(&pdev->dev, "cannot get parent's regmap\n"); dev_err(&pdev->dev, "cannot get parent's regmap\n");
return PTR_ERR(wdt->regmap); return PTR_ERR(wdt->regmap);
......
...@@ -30,7 +30,7 @@ ...@@ -30,7 +30,7 @@
#define VIA_WDT_CONF_MMIO 0x02 /* 1: enable watchdog MMIO */ #define VIA_WDT_CONF_MMIO 0x02 /* 1: enable watchdog MMIO */
/* /*
* The MMIO region contains the watchog control register and the * The MMIO region contains the watchdog control register and the
* hardware timer counter. * hardware timer counter.
*/ */
#define VIA_WDT_MMIO_LEN 8 /* MMIO region length in bytes */ #define VIA_WDT_MMIO_LEN 8 /* MMIO region length in bytes */
...@@ -82,7 +82,7 @@ static inline void wdt_reset(void) ...@@ -82,7 +82,7 @@ static inline void wdt_reset(void)
/* /*
* Timer tick: the timer will make sure that the watchdog timer hardware * Timer tick: the timer will make sure that the watchdog timer hardware
* is being reset in time. The conditions to do this are: * is being reset in time. The conditions to do this are:
* 1) the watchog timer has been started and /dev/watchdog is open * 1) the watchdog timer has been started and /dev/watchdog is open
* and there is still time left before userspace should send the * and there is still time left before userspace should send the
* next heartbeat/ping. (note: the internal heartbeat is much smaller * next heartbeat/ping. (note: the internal heartbeat is much smaller
* then the external/userspace heartbeat). * then the external/userspace heartbeat).
......
...@@ -50,7 +50,7 @@ static int cr_wdt_csr; /* WDT control & status register */ ...@@ -50,7 +50,7 @@ static int cr_wdt_csr; /* WDT control & status register */
enum chips { w83627hf, w83627s, w83697hf, w83697ug, w83637hf, w83627thf, enum chips { w83627hf, w83627s, w83697hf, w83697ug, w83637hf, w83627thf,
w83687thf, w83627ehf, w83627dhg, w83627uhg, w83667hg, w83627dhg_p, w83687thf, w83627ehf, w83627dhg, w83627uhg, w83667hg, w83627dhg_p,
w83667hg_b, nct6775, nct6776, nct6779, nct6791, nct6792, nct6793, w83667hg_b, nct6775, nct6776, nct6779, nct6791, nct6792, nct6793,
nct6795, nct6102 }; nct6795, nct6796, nct6102 };
static int timeout; /* in seconds */ static int timeout; /* in seconds */
module_param(timeout, int, 0); module_param(timeout, int, 0);
...@@ -100,6 +100,7 @@ MODULE_PARM_DESC(early_disable, "Disable watchdog at boot time (default=0)"); ...@@ -100,6 +100,7 @@ MODULE_PARM_DESC(early_disable, "Disable watchdog at boot time (default=0)");
#define NCT6792_ID 0xc9 #define NCT6792_ID 0xc9
#define NCT6793_ID 0xd1 #define NCT6793_ID 0xd1
#define NCT6795_ID 0xd3 #define NCT6795_ID 0xd3
#define NCT6796_ID 0xd4 /* also NCT9697D, NCT9698D */
#define W83627HF_WDT_TIMEOUT 0xf6 #define W83627HF_WDT_TIMEOUT 0xf6
#define W83697HF_WDT_TIMEOUT 0xf4 #define W83697HF_WDT_TIMEOUT 0xf4
...@@ -209,6 +210,7 @@ static int w83627hf_init(struct watchdog_device *wdog, enum chips chip) ...@@ -209,6 +210,7 @@ static int w83627hf_init(struct watchdog_device *wdog, enum chips chip)
case nct6792: case nct6792:
case nct6793: case nct6793:
case nct6795: case nct6795:
case nct6796:
case nct6102: case nct6102:
/* /*
* These chips have a fixed WDTO# output pin (W83627UHG), * These chips have a fixed WDTO# output pin (W83627UHG),
...@@ -407,6 +409,9 @@ static int wdt_find(int addr) ...@@ -407,6 +409,9 @@ static int wdt_find(int addr)
case NCT6795_ID: case NCT6795_ID:
ret = nct6795; ret = nct6795;
break; break;
case NCT6796_ID:
ret = nct6796;
break;
case NCT6102_ID: case NCT6102_ID:
ret = nct6102; ret = nct6102;
cr_wdt_timeout = NCT6102D_WDT_TIMEOUT; cr_wdt_timeout = NCT6102D_WDT_TIMEOUT;
...@@ -450,6 +455,7 @@ static int __init wdt_init(void) ...@@ -450,6 +455,7 @@ static int __init wdt_init(void)
"NCT6792", "NCT6792",
"NCT6793", "NCT6793",
"NCT6795", "NCT6795",
"NCT6796",
"NCT6102", "NCT6102",
}; };
......
...@@ -1019,16 +1019,16 @@ static void watchdog_cdev_unregister(struct watchdog_device *wdd) ...@@ -1019,16 +1019,16 @@ static void watchdog_cdev_unregister(struct watchdog_device *wdd)
old_wd_data = NULL; old_wd_data = NULL;
} }
mutex_lock(&wd_data->lock);
wd_data->wdd = NULL;
wdd->wd_data = NULL;
mutex_unlock(&wd_data->lock);
if (watchdog_active(wdd) && if (watchdog_active(wdd) &&
test_bit(WDOG_STOP_ON_UNREGISTER, &wdd->status)) { test_bit(WDOG_STOP_ON_UNREGISTER, &wdd->status)) {
watchdog_stop(wdd); watchdog_stop(wdd);
} }
mutex_lock(&wd_data->lock);
wd_data->wdd = NULL;
wdd->wd_data = NULL;
mutex_unlock(&wd_data->lock);
hrtimer_cancel(&wd_data->timer); hrtimer_cancel(&wd_data->timer);
kthread_cancel_work_sync(&wd_data->work); kthread_cancel_work_sync(&wd_data->work);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment