linux-watchdog 4.20-rc1 tag
-----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.14 (GNU/Linux) iEYEABECAAYFAlvURsoACgkQ+iyteGJfRspw9gCgtLHtFIm++UnS48IhP+ZlioGI KqYAoLMhbd9IzpgPd1F8cLT/N29zAUcU =bvZ1 -----END PGP SIGNATURE----- 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 ...
This commit is contained in:
commit
5ecf3e110c
@ -0,0 +1,23 @@
|
||||
* 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>;
|
||||
};
|
25
Documentation/devicetree/bindings/watchdog/mpc8xxx-wdt.txt
Normal file
25
Documentation/devicetree/bindings/watchdog/mpc8xxx-wdt.txt
Normal file
@ -0,0 +1,25 @@
|
||||
* 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:
|
||||
- "renesas,r8a77990-wdt" (R-Car E3)
|
||||
- "renesas,r8a77995-wdt" (R-Car D3)
|
||||
- "renesas,r7s72100-wdt" (RZ/A1)
|
||||
- "renesas,r7s9210-wdt" (RZ/A2)
|
||||
The generic compatible string must be:
|
||||
- "renesas,rza-wdt" for RZ/A
|
||||
- "renesas,rcar-gen2-wdt" for R-Car Gen2 and RZ/G1
|
||||
|
@ -1,15 +1,12 @@
|
||||
Last reviewed: 05/20/2016
|
||||
Last reviewed: 08/20/2018
|
||||
|
||||
HPE iLO NMI Watchdog Driver
|
||||
NMI sourcing for iLO based ProLiant Servers
|
||||
Documentation and Driver by
|
||||
Thomas Mingarelli
|
||||
for iLO based ProLiant Servers
|
||||
|
||||
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.
|
||||
watchdog functionality and handler for the iLO "Generate NMI to System"
|
||||
virtual button.
|
||||
|
||||
All references to iLO in this document imply it also works on iLO2 and all
|
||||
subsequent generations.
|
||||
|
||||
@ -21,12 +18,16 @@ Last reviewed: 05/20/2016
|
||||
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 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.
|
||||
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
|
||||
timeout - an alias of soft_margin.
|
||||
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
|
||||
be restarted or an impending ASR to be escaped.
|
||||
Default value is set when compiling the kernel. If it is set
|
||||
@ -37,61 +38,29 @@ Last reviewed: 05/20/2016
|
||||
interface to /dev/watchdog can be found in
|
||||
Documentation/watchdog/watchdog-api.txt and Documentation/IPMI.txt.
|
||||
|
||||
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.
|
||||
Due to limitations in the iLO hardware, the NMI pretimeout if enabled,
|
||||
can only be set to 9 seconds. Attempts to set pretimeout to other
|
||||
non-zero values will be rounded, possibly to zero. Users should verify
|
||||
the pretimeout value after attempting to set pretimeout or timeout.
|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
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).
|
||||
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.
|
||||
|
||||
Below is a list of NMIs the HPE BIOS understands along with the associated
|
||||
code (reason):
|
||||
For those wishing to learn more please see:
|
||||
Documentation/kdump/kdump.txt
|
||||
Documentation/admin-guide/kernel-parameters.txt (panic=)
|
||||
Your Linux Distribution specific documentation.
|
||||
|
||||
No source found 00h
|
||||
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.
|
||||
|
||||
Uncorrectable Memory Error 01h
|
||||
--
|
||||
|
||||
ASR NMI 1Bh
|
||||
The HPE iLO NMI Watchdog Driver and documentation were originally developed
|
||||
by Tom Mingarelli.
|
||||
|
||||
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
|
||||
|
||||
PCI Express Error 2Fh
|
||||
|
||||
DMA controller NMI 30h
|
||||
|
||||
Hypertransport/CSI Error 31h
|
||||
|
||||
|
||||
|
||||
-- Tom Mingarelli
|
||||
|
@ -40,6 +40,11 @@ margin: Watchdog margin in seconds (default=60)
|
||||
nowayout: Disable watchdog shutdown on close
|
||||
(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:
|
||||
wdt_time: Watchdog time in seconds. (default=5)
|
||||
nowayout: Watchdog cannot be stopped once started
|
||||
|
@ -9568,7 +9568,7 @@ MEN Z069 WATCHDOG DRIVER
|
||||
M: Johannes Thumshirn <jth@kernel.org>
|
||||
L: linux-watchdog@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/watchdog/menz069_wdt.c
|
||||
F: drivers/watchdog/menz69_wdt.c
|
||||
|
||||
MESON AO CEC DRIVER FOR AMLOGIC SOCS
|
||||
M: Neil Armstrong <narmstrong@baylibre.com>
|
||||
|
@ -273,6 +273,17 @@ config ARM_SBSA_WATCHDOG
|
||||
To compile this driver as module, choose M here: The module
|
||||
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
|
||||
tristate "Alphascale ASM9260 watchdog"
|
||||
depends on MACH_ASM9260 || COMPILE_TEST
|
||||
@ -1621,6 +1632,7 @@ config IMGPDC_WDT
|
||||
config LANTIQ_WDT
|
||||
tristate "Lantiq SoC watchdog"
|
||||
depends on LANTIQ
|
||||
select WATCHDOG_CORE
|
||||
help
|
||||
Hardware driver for the Lantiq SoC Watchdog Timer.
|
||||
|
||||
|
@ -38,6 +38,7 @@ obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb.o
|
||||
# ARM Architecture
|
||||
obj-$(CONFIG_ARM_SP805_WATCHDOG) += sp805_wdt.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_AT91RM9200_WATCHDOG) += at91rm9200_wdt.o
|
||||
obj-$(CONFIG_AT91SAM9X_WATCHDOG) += at91sam9_wdt.o
|
||||
|
388
drivers/watchdog/armada_37xx_wdt.c
Normal file
388
drivers/watchdog/armada_37xx_wdt.c
Normal file
@ -0,0 +1,388 @@
|
||||
// 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, ®);
|
||||
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 @@
|
||||
/* If the kernel parameter wdt=1, the watchdog will be enabled at boot.
|
||||
* Also, the wdt_period sets the watchdog timer period timeout.
|
||||
* 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
|
||||
* occur, and the final time the board will reset.
|
||||
*/
|
||||
|
@ -26,7 +26,7 @@
|
||||
#include <linux/watchdog.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 TICKS_TO_SECS(ticks) ((ticks) * 128 / 1000)
|
||||
#define HPWDT_MAX_TIMER TICKS_TO_SECS(65535)
|
||||
@ -162,7 +162,7 @@ static int hpwdt_pretimeout(unsigned int ulReason, struct pt_regs *regs)
|
||||
if (ilo5 && ulReason == NMI_UNKNOWN && !mynmi)
|
||||
return NMI_DONE;
|
||||
|
||||
if (ilo5 && !pretimeout)
|
||||
if (ilo5 && !pretimeout && !mynmi)
|
||||
return NMI_DONE;
|
||||
|
||||
hpwdt_stop();
|
||||
@ -205,9 +205,7 @@ static struct watchdog_device hpwdt_dev = {
|
||||
.min_timeout = 1,
|
||||
.max_timeout = HPWDT_MAX_TIMER,
|
||||
.timeout = DEFAULT_MARGIN,
|
||||
#ifdef CONFIG_HPWDT_NMI_DECODING
|
||||
.pretimeout = PRETIMEOUT_SEC,
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
@ -313,6 +311,12 @@ static int hpwdt_init_one(struct pci_dev *dev,
|
||||
if (watchdog_init_timeout(&hpwdt_dev, soft_margin, NULL))
|
||||
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;
|
||||
retval = watchdog_register_device(&hpwdt_dev);
|
||||
if (retval < 0) {
|
||||
@ -320,9 +324,12 @@ static int hpwdt_init_one(struct pci_dev *dev,
|
||||
goto error_wd_register;
|
||||
}
|
||||
|
||||
dev_info(&dev->dev, "HPE Watchdog Timer Driver: %s"
|
||||
", timer margin: %d seconds (nowayout=%d).\n",
|
||||
HPWDT_VERSION, hpwdt_dev.timeout, nowayout);
|
||||
dev_info(&dev->dev, "HPE Watchdog Timer Driver: Version: %s\n",
|
||||
HPWDT_VERSION);
|
||||
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)
|
||||
ilo5 = true;
|
||||
@ -364,6 +371,9 @@ MODULE_VERSION(HPWDT_VERSION);
|
||||
module_param(soft_margin, int, 0);
|
||||
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_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
|
||||
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
|
||||
|
@ -3,14 +3,10 @@
|
||||
#ifdef CONFIG_ITCO_VENDOR_SUPPORT
|
||||
extern void iTCO_vendor_pre_start(struct resource *, unsigned int);
|
||||
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);
|
||||
#else
|
||||
#define iTCO_vendor_pre_start(acpibase, heartbeat) {}
|
||||
#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
|
||||
/* 1=check noreboot; 0=don't check */
|
||||
#endif
|
||||
|
@ -38,7 +38,7 @@
|
||||
/* List of vendor support modes */
|
||||
/* SuperMicro Pentium 3 Era 370SSE+-OEM1/P3TSSE */
|
||||
#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
|
||||
/* Broken BIOS */
|
||||
#define BROKEN_BIOS 911
|
||||
@ -46,8 +46,7 @@
|
||||
static int vendorsupport;
|
||||
module_param(vendorsupport, int, 0);
|
||||
MODULE_PARM_DESC(vendorsupport, "iTCO vendor specific support mode, default="
|
||||
"0 (none), 1=SuperMicro Pent3, 2=SuperMicro Pent4+, "
|
||||
"911=Broken SMI BIOS");
|
||||
"0 (none), 1=SuperMicro Pent3, 911=Broken SMI BIOS");
|
||||
|
||||
/*
|
||||
* Vendor Specific Support
|
||||
@ -97,143 +96,6 @@ static void supermicro_old_pre_stop(struct resource *smires)
|
||||
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
|
||||
* Board: Some Intel ICHx based motherboards
|
||||
@ -298,9 +160,6 @@ void iTCO_vendor_pre_start(struct resource *smires,
|
||||
case SUPERMICRO_OLD_BOARD:
|
||||
supermicro_old_pre_start(smires);
|
||||
break;
|
||||
case SUPERMICRO_NEW_BOARD:
|
||||
supermicro_new_pre_start(heartbeat);
|
||||
break;
|
||||
case BROKEN_BIOS:
|
||||
broken_bios_start(smires);
|
||||
break;
|
||||
@ -314,9 +173,6 @@ void iTCO_vendor_pre_stop(struct resource *smires)
|
||||
case SUPERMICRO_OLD_BOARD:
|
||||
supermicro_old_pre_stop(smires);
|
||||
break;
|
||||
case SUPERMICRO_NEW_BOARD:
|
||||
supermicro_new_pre_stop();
|
||||
break;
|
||||
case BROKEN_BIOS:
|
||||
broken_bios_stop(smires);
|
||||
break;
|
||||
@ -324,20 +180,6 @@ void iTCO_vendor_pre_stop(struct resource *smires)
|
||||
}
|
||||
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)
|
||||
{
|
||||
switch (vendorsupport) {
|
||||
@ -351,6 +193,12 @@ EXPORT_SYMBOL(iTCO_vendor_check_noreboot_on);
|
||||
|
||||
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);
|
||||
return 0;
|
||||
}
|
||||
|
@ -304,8 +304,6 @@ static int iTCO_wdt_ping(struct watchdog_device *wd_dev)
|
||||
|
||||
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 */
|
||||
if (p->iTCO_version >= 2) {
|
||||
outw(0x01, TCO_RLD(p));
|
||||
@ -342,8 +340,6 @@ static int iTCO_wdt_set_timeout(struct watchdog_device *wd_dev, unsigned int t)
|
||||
(p->iTCO_version == 1 && tmrval > 0x03f))
|
||||
return -EINVAL;
|
||||
|
||||
iTCO_vendor_pre_set_heartbeat(tmrval);
|
||||
|
||||
/* Write new heartbeat to watchdog */
|
||||
if (p->iTCO_version >= 2) {
|
||||
spin_lock(&p->io_lock);
|
||||
|
@ -8,11 +8,8 @@
|
||||
* Based on EP93xx wdt driver
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/watchdog.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/uaccess.h>
|
||||
@ -40,169 +37,128 @@
|
||||
* essentially the following two magic passwords need to be written to allow
|
||||
* IO access to the WDT core
|
||||
*/
|
||||
#define LTQ_WDT_PW1 0x00BE0000
|
||||
#define LTQ_WDT_PW2 0x00DC0000
|
||||
#define LTQ_WDT_CR_PW1 0x00BE0000
|
||||
#define LTQ_WDT_CR_PW2 0x00DC0000
|
||||
|
||||
#define LTQ_WDT_CR 0x0 /* watchdog control register */
|
||||
#define LTQ_WDT_SR 0x8 /* watchdog status register */
|
||||
#define LTQ_WDT_CR 0x0 /* watchdog control 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_MAX_TIMEOUT ((1 << 16) - 1) /* the reload field is 16 bit */
|
||||
|
||||
static bool nowayout = WATCHDOG_NOWAYOUT;
|
||||
|
||||
static void __iomem *ltq_wdt_membase;
|
||||
static unsigned long ltq_io_region_clk_rate;
|
||||
struct ltq_wdt_hw {
|
||||
int (*bootstatus_get)(struct device *dev);
|
||||
};
|
||||
|
||||
static unsigned long ltq_wdt_bootstatus;
|
||||
static unsigned long ltq_wdt_in_use;
|
||||
static int ltq_wdt_timeout = 30;
|
||||
static int ltq_wdt_ok_to_close;
|
||||
struct ltq_wdt_priv {
|
||||
struct watchdog_device wdt;
|
||||
void __iomem *membase;
|
||||
unsigned long clk_rate;
|
||||
};
|
||||
|
||||
static void
|
||||
ltq_wdt_enable(void)
|
||||
static u32 ltq_wdt_r32(struct ltq_wdt_priv *priv, u32 offset)
|
||||
{
|
||||
unsigned long int timeout = ltq_wdt_timeout *
|
||||
(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);
|
||||
return __raw_readl(priv->membase + offset);
|
||||
}
|
||||
|
||||
static void
|
||||
ltq_wdt_disable(void)
|
||||
static void ltq_wdt_w32(struct ltq_wdt_priv *priv, u32 val, u32 offset)
|
||||
{
|
||||
/* write the first password magic */
|
||||
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);
|
||||
__raw_writel(val, priv->membase + offset);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
ltq_wdt_write(struct file *file, const char __user *data,
|
||||
size_t len, loff_t *ppos)
|
||||
static void ltq_wdt_mask(struct ltq_wdt_priv *priv, u32 clear, u32 set,
|
||||
u32 offset)
|
||||
{
|
||||
if (len) {
|
||||
if (!nowayout) {
|
||||
size_t i;
|
||||
u32 val = ltq_wdt_r32(priv, offset);
|
||||
|
||||
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 |
|
||||
WDIOF_CARDRESET,
|
||||
WDIOF_CARDRESET,
|
||||
.identity = "ltq_wdt",
|
||||
};
|
||||
|
||||
static long
|
||||
ltq_wdt_ioctl(struct file *file,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
static int ltq_wdt_start(struct watchdog_device *wdt)
|
||||
{
|
||||
int ret = -ENOTTY;
|
||||
struct ltq_wdt_priv *priv = ltq_wdt_get_priv(wdt);
|
||||
u32 timeout;
|
||||
|
||||
switch (cmd) {
|
||||
case WDIOC_GETSUPPORT:
|
||||
ret = copy_to_user((struct watchdog_info __user *)arg, &ident,
|
||||
sizeof(ident)) ? -EFAULT : 0;
|
||||
break;
|
||||
timeout = wdt->timeout * priv->clk_rate;
|
||||
|
||||
case WDIOC_GETBOOTSTATUS:
|
||||
ret = put_user(ltq_wdt_bootstatus, (int __user *)arg);
|
||||
break;
|
||||
|
||||
case WDIOC_GETSTATUS:
|
||||
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
|
||||
ltq_wdt_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
if (test_and_set_bit(0, <q_wdt_in_use))
|
||||
return -EBUSY;
|
||||
ltq_wdt_in_use = 1;
|
||||
ltq_wdt_enable();
|
||||
|
||||
return nonseekable_open(inode, file);
|
||||
}
|
||||
|
||||
static int
|
||||
ltq_wdt_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
if (ltq_wdt_ok_to_close)
|
||||
ltq_wdt_disable();
|
||||
else
|
||||
pr_err("watchdog closed without warning\n");
|
||||
ltq_wdt_ok_to_close = 0;
|
||||
clear_bit(0, <q_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_GEN | LTQ_WDT_CR_PWL | LTQ_WDT_CR_CLKDIV |
|
||||
LTQ_WDT_CR_PW2 | timeout,
|
||||
LTQ_WDT_CR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct file_operations ltq_wdt_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.write = ltq_wdt_write,
|
||||
.unlocked_ioctl = ltq_wdt_ioctl,
|
||||
.open = ltq_wdt_open,
|
||||
.release = ltq_wdt_release,
|
||||
.llseek = no_llseek,
|
||||
};
|
||||
|
||||
static struct miscdevice ltq_wdt_miscdev = {
|
||||
.minor = WATCHDOG_MINOR,
|
||||
.name = "watchdog",
|
||||
.fops = <q_wdt_fops,
|
||||
};
|
||||
|
||||
typedef int (*ltq_wdt_bootstatus_set)(struct platform_device *pdev);
|
||||
|
||||
static int ltq_wdt_bootstatus_xrx(struct platform_device *pdev)
|
||||
static int ltq_wdt_stop(struct watchdog_device *wdt)
|
||||
{
|
||||
struct ltq_wdt_priv *priv = ltq_wdt_get_priv(wdt);
|
||||
|
||||
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 ltq_wdt_ping(struct watchdog_device *wdt)
|
||||
{
|
||||
struct ltq_wdt_priv *priv = ltq_wdt_get_priv(wdt);
|
||||
u32 timeout;
|
||||
|
||||
timeout = wdt->timeout * priv->clk_rate;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static unsigned int ltq_wdt_get_timeleft(struct watchdog_device *wdt)
|
||||
{
|
||||
struct ltq_wdt_priv *priv = ltq_wdt_get_priv(wdt);
|
||||
u64 timeout;
|
||||
|
||||
timeout = ltq_wdt_r32(priv, LTQ_WDT_SR) & LTQ_WDT_SR_VALUE_MASK;
|
||||
return do_div(timeout, priv->clk_rate);
|
||||
}
|
||||
|
||||
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_xrx_bootstatus_get(struct device *dev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct regmap *rcu_regmap;
|
||||
u32 val;
|
||||
int err;
|
||||
@ -216,14 +172,13 @@ static int ltq_wdt_bootstatus_xrx(struct platform_device *pdev)
|
||||
return err;
|
||||
|
||||
if (val & LTQ_XRX_RCU_RST_STAT_WDT)
|
||||
ltq_wdt_bootstatus = WDIOF_CARDRESET;
|
||||
return WDIOF_CARDRESET;
|
||||
|
||||
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;
|
||||
u32 val;
|
||||
int err;
|
||||
@ -238,62 +193,90 @@ static int ltq_wdt_bootstatus_falcon(struct platform_device *pdev)
|
||||
return err;
|
||||
|
||||
if ((val & LTQ_FALCON_SYS1_CPU0RS_MASK) == LTQ_FALCON_SYS1_CPU0RS_WDT)
|
||||
ltq_wdt_bootstatus = WDIOF_CARDRESET;
|
||||
return WDIOF_CARDRESET;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
ltq_wdt_probe(struct platform_device *pdev)
|
||||
static int 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;
|
||||
ltq_wdt_bootstatus_set ltq_wdt_bootstatus_set;
|
||||
const struct ltq_wdt_hw *ltq_wdt_hw;
|
||||
int ret;
|
||||
u32 status;
|
||||
|
||||
ltq_wdt_membase = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(ltq_wdt_membase))
|
||||
return PTR_ERR(ltq_wdt_membase);
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
ltq_wdt_bootstatus_set = of_device_get_match_data(&pdev->dev);
|
||||
if (ltq_wdt_bootstatus_set) {
|
||||
ret = ltq_wdt_bootstatus_set(pdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
priv->membase = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(priv->membase))
|
||||
return PTR_ERR(priv->membase);
|
||||
|
||||
/* we do not need to enable the clock as it is always running */
|
||||
clk = clk_get_io();
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(&pdev->dev, "Failed to get clock\n");
|
||||
return -ENOENT;
|
||||
priv->clk_rate = clk_get_rate(clk) / LTQ_WDT_DIVIDER;
|
||||
if (!priv->clk_rate) {
|
||||
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");
|
||||
return misc_register(<q_wdt_miscdev);
|
||||
wdt = &priv->wdt;
|
||||
wdt->info = <q_wdt_info;
|
||||
wdt->ops = <q_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;
|
||||
}
|
||||
|
||||
watchdog_set_nowayout(wdt, nowayout);
|
||||
watchdog_init_timeout(wdt, 0, dev);
|
||||
|
||||
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 devm_watchdog_register_device(dev, wdt);
|
||||
}
|
||||
|
||||
static int
|
||||
ltq_wdt_remove(struct platform_device *pdev)
|
||||
{
|
||||
misc_deregister(<q_wdt_miscdev);
|
||||
static const struct ltq_wdt_hw ltq_wdt_xrx100 = {
|
||||
.bootstatus_get = ltq_wdt_xrx_bootstatus_get,
|
||||
};
|
||||
|
||||
return 0;
|
||||
}
|
||||
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[] = {
|
||||
{ .compatible = "lantiq,wdt", .data = NULL},
|
||||
{ .compatible = "lantiq,xrx100-wdt", .data = ltq_wdt_bootstatus_xrx },
|
||||
{ .compatible = "lantiq,falcon-wdt", .data = ltq_wdt_bootstatus_falcon },
|
||||
{ .compatible = "lantiq,wdt", .data = NULL },
|
||||
{ .compatible = "lantiq,xrx100-wdt", .data = <q_wdt_xrx100 },
|
||||
{ .compatible = "lantiq,falcon-wdt", .data = <q_wdt_falcon },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ltq_wdt_match);
|
||||
|
||||
static struct platform_driver ltq_wdt_driver = {
|
||||
.probe = ltq_wdt_probe,
|
||||
.remove = ltq_wdt_remove,
|
||||
.driver = {
|
||||
.name = "wdt",
|
||||
.of_match_table = ltq_wdt_match,
|
||||
|
@ -17,8 +17,6 @@
|
||||
* option) any later version.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
@ -49,6 +47,7 @@ struct mpc8xxx_wdt {
|
||||
struct mpc8xxx_wdt_type {
|
||||
int prescaler;
|
||||
bool hw_enabled;
|
||||
u32 rsr_mask;
|
||||
};
|
||||
|
||||
struct mpc8xxx_wdt_ddata {
|
||||
@ -137,36 +136,55 @@ static int mpc8xxx_wdt_probe(struct platform_device *ofdev)
|
||||
struct mpc8xxx_wdt_ddata *ddata;
|
||||
u32 freq = fsl_get_sys_freq();
|
||||
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)
|
||||
return -EINVAL;
|
||||
|
||||
if (!freq || freq == -1)
|
||||
return -EINVAL;
|
||||
|
||||
ddata = devm_kzalloc(&ofdev->dev, sizeof(*ddata), GFP_KERNEL);
|
||||
ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL);
|
||||
if (!ddata)
|
||||
return -ENOMEM;
|
||||
|
||||
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))
|
||||
return PTR_ERR(ddata->base);
|
||||
|
||||
enabled = in_be32(&ddata->base->swcrr) & SWCRR_SWEN;
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
ddata->wdd.info = &mpc8xxx_wdt_info,
|
||||
ddata->wdd.ops = &mpc8xxx_wdt_ops,
|
||||
|
||||
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);
|
||||
|
||||
@ -189,12 +207,13 @@ static int mpc8xxx_wdt_probe(struct platform_device *ofdev)
|
||||
|
||||
ret = watchdog_register_device(&ddata->wdd);
|
||||
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;
|
||||
}
|
||||
|
||||
pr_info("WDT driver for MPC8xxx initialized. mode:%s timeout=%d sec\n",
|
||||
reset ? "reset" : "interrupt", ddata->wdd.timeout);
|
||||
dev_info(dev,
|
||||
"WDT driver for MPC8xxx initialized. mode:%s timeout=%d sec\n",
|
||||
reset ? "reset" : "interrupt", ddata->wdd.timeout);
|
||||
|
||||
platform_set_drvdata(ofdev, ddata);
|
||||
return 0;
|
||||
@ -204,8 +223,8 @@ static int mpc8xxx_wdt_remove(struct platform_device *ofdev)
|
||||
{
|
||||
struct mpc8xxx_wdt_ddata *ddata = platform_get_drvdata(ofdev);
|
||||
|
||||
pr_crit("Watchdog removed, expect the %s soon!\n",
|
||||
reset ? "reset" : "machine check exception");
|
||||
dev_crit(&ofdev->dev, "Watchdog removed, expect the %s soon!\n",
|
||||
reset ? "reset" : "machine check exception");
|
||||
watchdog_unregister_device(&ddata->wdd);
|
||||
|
||||
return 0;
|
||||
@ -216,6 +235,7 @@ static const struct of_device_id mpc8xxx_wdt_match[] = {
|
||||
.compatible = "mpc83xx_wdt",
|
||||
.data = &(struct mpc8xxx_wdt_type) {
|
||||
.prescaler = 0x10000,
|
||||
.rsr_mask = BIT(3), /* RSR Bit SWRS */
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -223,6 +243,7 @@ static const struct of_device_id mpc8xxx_wdt_match[] = {
|
||||
.data = &(struct mpc8xxx_wdt_type) {
|
||||
.prescaler = 0x10000,
|
||||
.hw_enabled = true,
|
||||
.rsr_mask = BIT(20), /* RSTRSCR Bit WDT_RR */
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -230,6 +251,7 @@ static const struct of_device_id mpc8xxx_wdt_match[] = {
|
||||
.data = &(struct mpc8xxx_wdt_type) {
|
||||
.prescaler = 0x800,
|
||||
.hw_enabled = true,
|
||||
.rsr_mask = BIT(28), /* RSR Bit SWRS */
|
||||
},
|
||||
},
|
||||
{},
|
||||
|
@ -1,12 +1,9 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Watchdog driver for Renesas WDT watchdog
|
||||
*
|
||||
* Copyright (C) 2015-17 Wolfram Sang, Sang Engineering <wsa@sang-engineering.com>
|
||||
* 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/clk.h>
|
||||
@ -234,6 +231,7 @@ static int rwdt_probe(struct platform_device *pdev)
|
||||
watchdog_set_drvdata(&priv->wdev, priv);
|
||||
watchdog_set_nowayout(&priv->wdev, nowayout);
|
||||
watchdog_set_restart_priority(&priv->wdev, 0);
|
||||
watchdog_stop_on_unregister(&priv->wdev);
|
||||
|
||||
/* This overrides the default timeout only if DT configuration was found */
|
||||
ret = watchdog_init_timeout(&priv->wdev, 0, &pdev->dev);
|
||||
|
@ -1,12 +1,9 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Renesas RZ/A Series WDT Driver
|
||||
*
|
||||
* Copyright (C) 2017 Renesas Electronics America, Inc.
|
||||
* 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>
|
||||
@ -14,6 +11,7 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/watchdog.h>
|
||||
|
||||
@ -34,12 +32,45 @@
|
||||
#define WRCSR_RSTE BIT(6)
|
||||
#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 watchdog_device wdev;
|
||||
void __iomem *base;
|
||||
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)
|
||||
{
|
||||
struct rza_wdt *priv = watchdog_get_drvdata(wdev);
|
||||
@ -51,13 +82,12 @@ static int rza_wdt_start(struct watchdog_device *wdev)
|
||||
readb(priv->base + WRCSR);
|
||||
writew(WRCSR_CLEAR_WOVF, priv->base + WRCSR);
|
||||
|
||||
/*
|
||||
* Start timer with slowest clock source and reset option enabled.
|
||||
*/
|
||||
rza_wdt_calc_timeout(priv, wdev->timeout);
|
||||
|
||||
writew(WRCSR_MAGIC | WRCSR_RSTE, priv->base + WRCSR);
|
||||
writew(WTCNT_MAGIC | 0, priv->base + WTCNT);
|
||||
writew(WTCSR_MAGIC | WTSCR_WT | WTSCR_TME | WTSCR_CKS(7),
|
||||
priv->base + WTCSR);
|
||||
writew(WTCNT_MAGIC | priv->count, priv->base + WTCNT);
|
||||
writew(WTCSR_MAGIC | WTSCR_WT | WTSCR_TME |
|
||||
WTSCR_CKS(priv->cks), priv->base + WTCSR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -75,8 +105,17 @@ static int rza_wdt_ping(struct watchdog_device *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;
|
||||
}
|
||||
|
||||
@ -121,6 +160,7 @@ static const struct watchdog_ops rza_wdt_ops = {
|
||||
.start = rza_wdt_start,
|
||||
.stop = rza_wdt_stop,
|
||||
.ping = rza_wdt_ping,
|
||||
.set_timeout = rza_set_timeout,
|
||||
.restart = rza_wdt_restart,
|
||||
};
|
||||
|
||||
@ -150,20 +190,28 @@ static int rza_wdt_probe(struct platform_device *pdev)
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
/* Assume slowest clock rate possible (CKS=7) */
|
||||
rate /= 16384;
|
||||
|
||||
priv->wdev.info = &rza_wdt_ident,
|
||||
priv->wdev.ops = &rza_wdt_ops,
|
||||
priv->wdev.parent = &pdev->dev;
|
||||
|
||||
/*
|
||||
* 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->cks = (u8)(uintptr_t)of_device_get_match_data(&pdev->dev);
|
||||
if (priv->cks == CKS_4BIT) {
|
||||
/* Assume slowest clock rate possible (CKS=0xF) */
|
||||
priv->wdev.max_timeout = (DIVIDER_4BIT * U8_MAX) / rate;
|
||||
|
||||
} else if (priv->cks == CKS_3BIT) {
|
||||
/* 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.timeout = DEFAULT_TIMEOUT;
|
||||
@ -179,7 +227,8 @@ static int rza_wdt_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
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 */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rza_wdt_of_match);
|
||||
|
@ -247,11 +247,7 @@ static int sama5d4_wdt_probe(struct platform_device *pdev)
|
||||
}
|
||||
}
|
||||
|
||||
ret = watchdog_init_timeout(wdd, wdt_timeout, &pdev->dev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "unable to set timeout value\n");
|
||||
return ret;
|
||||
}
|
||||
watchdog_init_timeout(wdd, wdt_timeout, &pdev->dev);
|
||||
|
||||
timeout = WDT_SEC2TICKS(wdd->timeout);
|
||||
|
||||
|
@ -135,6 +135,7 @@ static int ts4800_wdt_probe(struct platform_device *pdev)
|
||||
/* set regmap and offset to know where to write */
|
||||
wdt->feed_offset = reg;
|
||||
wdt->regmap = syscon_node_to_regmap(syscon_np);
|
||||
of_node_put(syscon_np);
|
||||
if (IS_ERR(wdt->regmap)) {
|
||||
dev_err(&pdev->dev, "cannot get parent's regmap\n");
|
||||
return PTR_ERR(wdt->regmap);
|
||||
|
@ -30,7 +30,7 @@
|
||||
#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.
|
||||
*/
|
||||
#define VIA_WDT_MMIO_LEN 8 /* MMIO region length in bytes */
|
||||
@ -82,7 +82,7 @@ static inline void wdt_reset(void)
|
||||
/*
|
||||
* Timer tick: the timer will make sure that the watchdog timer hardware
|
||||
* 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
|
||||
* next heartbeat/ping. (note: the internal heartbeat is much smaller
|
||||
* then the external/userspace heartbeat).
|
||||
|
@ -50,7 +50,7 @@ static int cr_wdt_csr; /* WDT control & status register */
|
||||
enum chips { w83627hf, w83627s, w83697hf, w83697ug, w83637hf, w83627thf,
|
||||
w83687thf, w83627ehf, w83627dhg, w83627uhg, w83667hg, w83627dhg_p,
|
||||
w83667hg_b, nct6775, nct6776, nct6779, nct6791, nct6792, nct6793,
|
||||
nct6795, nct6102 };
|
||||
nct6795, nct6796, nct6102 };
|
||||
|
||||
static int timeout; /* in seconds */
|
||||
module_param(timeout, int, 0);
|
||||
@ -100,6 +100,7 @@ MODULE_PARM_DESC(early_disable, "Disable watchdog at boot time (default=0)");
|
||||
#define NCT6792_ID 0xc9
|
||||
#define NCT6793_ID 0xd1
|
||||
#define NCT6795_ID 0xd3
|
||||
#define NCT6796_ID 0xd4 /* also NCT9697D, NCT9698D */
|
||||
|
||||
#define W83627HF_WDT_TIMEOUT 0xf6
|
||||
#define W83697HF_WDT_TIMEOUT 0xf4
|
||||
@ -209,6 +210,7 @@ static int w83627hf_init(struct watchdog_device *wdog, enum chips chip)
|
||||
case nct6792:
|
||||
case nct6793:
|
||||
case nct6795:
|
||||
case nct6796:
|
||||
case nct6102:
|
||||
/*
|
||||
* These chips have a fixed WDTO# output pin (W83627UHG),
|
||||
@ -407,6 +409,9 @@ static int wdt_find(int addr)
|
||||
case NCT6795_ID:
|
||||
ret = nct6795;
|
||||
break;
|
||||
case NCT6796_ID:
|
||||
ret = nct6796;
|
||||
break;
|
||||
case NCT6102_ID:
|
||||
ret = nct6102;
|
||||
cr_wdt_timeout = NCT6102D_WDT_TIMEOUT;
|
||||
@ -450,6 +455,7 @@ static int __init wdt_init(void)
|
||||
"NCT6792",
|
||||
"NCT6793",
|
||||
"NCT6795",
|
||||
"NCT6796",
|
||||
"NCT6102",
|
||||
};
|
||||
|
||||
|
@ -1019,16 +1019,16 @@ static void watchdog_cdev_unregister(struct watchdog_device *wdd)
|
||||
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) &&
|
||||
test_bit(WDOG_STOP_ON_UNREGISTER, &wdd->status)) {
|
||||
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);
|
||||
kthread_cancel_work_sync(&wd_data->work);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user