summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2016-07-31 21:32:22 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2016-07-31 21:32:22 -0400
commit27acbec338113a75b9d72aeb53149a3538031dda (patch)
tree1aa1b9c650491fe19925173296a948170cb82394
parentba929b6646c5b87c7bb15cd8d3e51617725c983b (diff)
parent1ac06563434e5f3302259608d3589bf7002431fe (diff)
Merge git://www.linux-watchdog.org/linux-watchdog
Pull watchdog updates from Wim Van Sebroeck: "Core: - min and max timeout improvements, WDOG_HW_RUNNING improvements, status funtionality - Add a device managed API for watchdog_register_device() New watchdog drivers: - Aspeed SoCs - Maxim PMIC MAX77620 - Amlogic Meson GXBB SoC Enhancements: - support for the r8a7796 watchdog device - support for F81866 watchdog device - support for 5th variation of Apollo Lake - support for MCP78S chipset - clean-up of softdog.c watchdog device driver - pic32-wdt and pic32-dmt fixes - Documentation/watchdog: watchdog-test improvements - several other fixes and improvements" * git://www.linux-watchdog.org/linux-watchdog: (50 commits) watchdog: gpio_wdt: Fix missing platform_set_drvdata() in gpio_wdt_probe() watchdog: core: Clear WDOG_HW_RUNNING before calling the stop function watchdog: core: Fix error handling of watchdog_dev_init() watchdog: pic32-wdt: Fix return value check in pic32_wdt_drv_probe() watchdog: pic32-dmt: Remove .owner field for driver watchdog: pic32-wdt: Remove .owner field for driver watchdog: renesas-wdt: Add support for the r8a7796 wdt Documentation/watchdog: check return value for magic close watchdog: sbsa: Drop status function watchdog: Implement status function in watchdog core watchdog: tangox: Set max_hw_heartbeat_ms instead of max_timeout watchdog: change watchdog_need_worker logic watchdog: add support for MCP78S chipset in nv_tco watchdog: bcm2835_wdt: remove redundant ->set_timeout callback watchdog: bcm2835_wdt: constify _ops and _info structures dt-bindings: watchdog: Add Meson GXBB Watchdog bindings watchdog: Add Meson GXBB Watchdog Driver watchdog: qcom: configure BARK time in addition to BITE time watchdog: qcom: add option for standalone watchdog not in timer block watchdog: qcom: update device tree bindings ...
-rw-r--r--Documentation/devicetree/bindings/watchdog/aspeed-wdt.txt16
-rw-r--r--Documentation/devicetree/bindings/watchdog/meson-gxbb-wdt.txt16
-rw-r--r--Documentation/devicetree/bindings/watchdog/qcom-wdt.txt4
-rw-r--r--Documentation/devicetree/bindings/watchdog/renesas-wdt.txt6
-rw-r--r--Documentation/driver-model/devres.txt3
-rw-r--r--Documentation/watchdog/hpwdt.txt5
-rw-r--r--Documentation/watchdog/src/watchdog-test.c39
-rw-r--r--Documentation/watchdog/watchdog-kernel-api.txt9
-rw-r--r--MAINTAINERS6
-rw-r--r--arch/arm/boot/dts/qcom-apq8064.dtsi3
-rw-r--r--arch/arm/boot/dts/qcom-ipq4019.dtsi2
-rw-r--r--arch/arm/boot/dts/qcom-ipq8064.dtsi3
-rw-r--r--arch/arm/boot/dts/qcom-msm8960.dtsi3
-rw-r--r--drivers/platform/x86/intel_pmc_ipc.c10
-rw-r--r--drivers/watchdog/Kconfig33
-rw-r--r--drivers/watchdog/Makefile3
-rw-r--r--drivers/watchdog/aspeed_wdt.c212
-rw-r--r--drivers/watchdog/bcm2835_wdt.c11
-rw-r--r--drivers/watchdog/da9063_wdt.c2
-rw-r--r--drivers/watchdog/f71808e_wdt.c28
-rw-r--r--drivers/watchdog/gpio_wdt.c2
-rw-r--r--drivers/watchdog/iTCO_wdt.c2
-rw-r--r--drivers/watchdog/max77620_wdt.c227
-rw-r--r--drivers/watchdog/meson_gxbb_wdt.c270
-rw-r--r--drivers/watchdog/nv_tco.c2
-rw-r--r--drivers/watchdog/pcwd.c14
-rw-r--r--drivers/watchdog/pic32-dmt.c5
-rw-r--r--drivers/watchdog/pic32-wdt.c9
-rw-r--r--drivers/watchdog/qcom-wdt.c69
-rw-r--r--drivers/watchdog/sbsa_gwdt.c16
-rw-r--r--drivers/watchdog/sirfsoc_wdt.c15
-rw-r--r--drivers/watchdog/softdog.c92
-rw-r--r--drivers/watchdog/tangox_wdt.c4
-rw-r--r--drivers/watchdog/watchdog_core.c39
-rw-r--r--drivers/watchdog/watchdog_dev.c61
-rw-r--r--drivers/watchdog/ziirave_wdt.c2
-rw-r--r--include/linux/watchdog.h6
37 files changed, 1062 insertions, 187 deletions
diff --git a/Documentation/devicetree/bindings/watchdog/aspeed-wdt.txt b/Documentation/devicetree/bindings/watchdog/aspeed-wdt.txt
new file mode 100644
index 000000000000..c5e74d7b4406
--- /dev/null
+++ b/Documentation/devicetree/bindings/watchdog/aspeed-wdt.txt
@@ -0,0 +1,16 @@
+Aspeed Watchdog Timer
+
+Required properties:
+ - compatible: must be one of:
+ - "aspeed,ast2400-wdt"
+ - "aspeed,ast2500-wdt"
+
+ - reg: physical base address of the controller and length of memory mapped
+ region
+
+Example:
+
+ wdt1: watchdog@1e785000 {
+ compatible = "aspeed,ast2400-wdt";
+ reg = <0x1e785000 0x1c>;
+ };
diff --git a/Documentation/devicetree/bindings/watchdog/meson-gxbb-wdt.txt b/Documentation/devicetree/bindings/watchdog/meson-gxbb-wdt.txt
new file mode 100644
index 000000000000..c7fe36fa739c
--- /dev/null
+++ b/Documentation/devicetree/bindings/watchdog/meson-gxbb-wdt.txt
@@ -0,0 +1,16 @@
+Meson GXBB SoCs Watchdog timer
+
+Required properties:
+
+- compatible : should be "amlogic,meson-gxbb-wdt"
+- reg : Specifies base physical address and size of the registers.
+- clocks : Should be a phandle to the Watchdog clock source, for GXBB the xtal
+ is the default clock source.
+
+Example:
+
+wdt: watchdog@98d0 {
+ compatible = "amlogic,meson-gxbb-wdt";
+ reg = <0 0x98d0 0x0 0x10>;
+ clocks = <&xtal>;
+};
diff --git a/Documentation/devicetree/bindings/watchdog/qcom-wdt.txt b/Documentation/devicetree/bindings/watchdog/qcom-wdt.txt
index 4726924d034e..41aeaa2ff0f8 100644
--- a/Documentation/devicetree/bindings/watchdog/qcom-wdt.txt
+++ b/Documentation/devicetree/bindings/watchdog/qcom-wdt.txt
@@ -7,6 +7,10 @@ Required properties :
"qcom,kpss-wdt-msm8960"
"qcom,kpss-wdt-apq8064"
"qcom,kpss-wdt-ipq8064"
+ "qcom,kpss-wdt-ipq4019"
+ "qcom,kpss-timer"
+ "qcom,scss-timer"
+ "qcom,kpss-wdt"
- reg : shall contain base register location and length
- clocks : shall contain the input clock
diff --git a/Documentation/devicetree/bindings/watchdog/renesas-wdt.txt b/Documentation/devicetree/bindings/watchdog/renesas-wdt.txt
index b9512f1eb80a..da24e3133417 100644
--- a/Documentation/devicetree/bindings/watchdog/renesas-wdt.txt
+++ b/Documentation/devicetree/bindings/watchdog/renesas-wdt.txt
@@ -1,7 +1,11 @@
Renesas Watchdog Timer (WDT) Controller
Required properties:
-- compatible : Should be "renesas,r8a7795-wdt", or "renesas,rcar-gen3-wdt"
+- compatible : Should be "renesas,<soctype>-wdt", and
+ "renesas,rcar-gen3-wdt" as fallback.
+ Examples with soctypes are:
+ - "renesas,r8a7795-wdt" (R-Car H3)
+ - "renesas,r8a7796-wdt" (R-Car M3-W)
When compatible with the generic version, nodes must list the SoC-specific
version corresponding to the platform first, followed by the generic
diff --git a/Documentation/driver-model/devres.txt b/Documentation/driver-model/devres.txt
index c63eea0c1c8c..589296bdc133 100644
--- a/Documentation/driver-model/devres.txt
+++ b/Documentation/driver-model/devres.txt
@@ -357,3 +357,6 @@ SLAVE DMA ENGINE
SPI
devm_spi_register_master()
+
+WATCHDOG
+ devm_watchdog_register_device()
diff --git a/Documentation/watchdog/hpwdt.txt b/Documentation/watchdog/hpwdt.txt
index a40398cce9d1..7a9f635d0258 100644
--- a/Documentation/watchdog/hpwdt.txt
+++ b/Documentation/watchdog/hpwdt.txt
@@ -1,9 +1,9 @@
-Last reviewed: 04/04/2016
+Last reviewed: 05/20/2016
HPE iLO NMI Watchdog Driver
NMI sourcing for iLO based ProLiant Servers
Documentation and Driver by
- Thomas Mingarelli <thomas.mingarelli@hpe.com>
+ Thomas Mingarelli
The HPE iLO NMI Watchdog driver is a kernel module that provides basic
watchdog functionality and the added benefit of NMI sourcing. Both the
@@ -95,4 +95,3 @@ Last reviewed: 04/04/2016
-- Tom Mingarelli
- (thomas.mingarelli@hpe.com)
diff --git a/Documentation/watchdog/src/watchdog-test.c b/Documentation/watchdog/src/watchdog-test.c
index fcdde8fc98be..6983d05097e2 100644
--- a/Documentation/watchdog/src/watchdog-test.c
+++ b/Documentation/watchdog/src/watchdog-test.c
@@ -2,6 +2,7 @@
* Watchdog Driver Test Program
*/
+#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -13,6 +14,7 @@
#include <linux/watchdog.h>
int fd;
+const char v = 'V';
/*
* This function simply sends an IOCTL to the driver, which in turn ticks
@@ -23,6 +25,7 @@ static void keep_alive(void)
{
int dummy;
+ printf(".");
ioctl(fd, WDIOC_KEEPALIVE, &dummy);
}
@@ -33,8 +36,13 @@ static void keep_alive(void)
static void term(int sig)
{
+ int ret = write(fd, &v, 1);
+
close(fd);
- fprintf(stderr, "Stopping watchdog ticks...\n");
+ if (ret < 0)
+ printf("\nStopping watchdog ticks failed (%d)...\n", errno);
+ else
+ printf("\nStopping watchdog ticks...\n");
exit(0);
}
@@ -42,12 +50,14 @@ int main(int argc, char *argv[])
{
int flags;
unsigned int ping_rate = 1;
+ int ret;
+
+ setbuf(stdout, NULL);
fd = open("/dev/watchdog", O_WRONLY);
if (fd == -1) {
- fprintf(stderr, "Watchdog device not enabled.\n");
- fflush(stderr);
+ printf("Watchdog device not enabled.\n");
exit(-1);
}
@@ -55,36 +65,30 @@ int main(int argc, char *argv[])
if (!strncasecmp(argv[1], "-d", 2)) {
flags = WDIOS_DISABLECARD;
ioctl(fd, WDIOC_SETOPTIONS, &flags);
- fprintf(stderr, "Watchdog card disabled.\n");
- fflush(stderr);
+ printf("Watchdog card disabled.\n");
goto end;
} else if (!strncasecmp(argv[1], "-e", 2)) {
flags = WDIOS_ENABLECARD;
ioctl(fd, WDIOC_SETOPTIONS, &flags);
- fprintf(stderr, "Watchdog card enabled.\n");
- fflush(stderr);
+ printf("Watchdog card enabled.\n");
goto end;
} else if (!strncasecmp(argv[1], "-t", 2) && argv[2]) {
flags = atoi(argv[2]);
ioctl(fd, WDIOC_SETTIMEOUT, &flags);
- fprintf(stderr, "Watchdog timeout set to %u seconds.\n", flags);
- fflush(stderr);
+ printf("Watchdog timeout set to %u seconds.\n", flags);
goto end;
} else if (!strncasecmp(argv[1], "-p", 2) && argv[2]) {
ping_rate = strtoul(argv[2], NULL, 0);
- fprintf(stderr, "Watchdog ping rate set to %u seconds.\n", ping_rate);
- fflush(stderr);
+ printf("Watchdog ping rate set to %u seconds.\n", ping_rate);
} else {
- fprintf(stderr, "-d to disable, -e to enable, -t <n> to set " \
+ printf("-d to disable, -e to enable, -t <n> to set " \
"the timeout,\n-p <n> to set the ping rate, and \n");
- fprintf(stderr, "run by itself to tick the card.\n");
- fflush(stderr);
+ printf("run by itself to tick the card.\n");
goto end;
}
}
- fprintf(stderr, "Watchdog Ticking Away!\n");
- fflush(stderr);
+ printf("Watchdog Ticking Away!\n");
signal(SIGINT, term);
@@ -93,6 +97,9 @@ int main(int argc, char *argv[])
sleep(ping_rate);
}
end:
+ ret = write(fd, &v, 1);
+ if (ret < 0)
+ printf("Stopping watchdog ticks failed (%d)...\n", errno);
close(fd);
return 0;
}
diff --git a/Documentation/watchdog/watchdog-kernel-api.txt b/Documentation/watchdog/watchdog-kernel-api.txt
index 917eeeabfa5e..7f31125c123e 100644
--- a/Documentation/watchdog/watchdog-kernel-api.txt
+++ b/Documentation/watchdog/watchdog-kernel-api.txt
@@ -82,8 +82,9 @@ It contains following fields:
* max_timeout: the watchdog timer's maximum timeout value (in seconds),
as seen from userspace. If set, the maximum configurable value for
'timeout'. Not used if max_hw_heartbeat_ms is non-zero.
-* min_hw_heartbeat_ms: Minimum time between heartbeats sent to the chip,
- in milli-seconds.
+* min_hw_heartbeat_ms: Hardware limit for minimum time between heartbeats,
+ in milli-seconds. This value is normally 0; it should only be provided
+ if the hardware can not tolerate lower intervals between heartbeats.
* max_hw_heartbeat_ms: Maximum hardware heartbeat, in milli-seconds.
If set, the infrastructure will send heartbeats to the watchdog driver
if 'timeout' is larger than max_hw_heartbeat_ms, unless WDOG_ACTIVE
@@ -166,6 +167,10 @@ they are supported. These optional routines/operations are:
info structure).
* status: this routine checks the status of the watchdog timer device. The
status of the device is reported with watchdog WDIOF_* status flags/bits.
+ WDIOF_MAGICCLOSE and WDIOF_KEEPALIVEPING are reported by the watchdog core;
+ it is not necessary to report those bits from the driver. Also, if no status
+ function is provided by the driver, the watchdog core reports the status bits
+ provided in the bootstatus variable of struct watchdog_device.
* set_timeout: this routine checks and changes the timeout of the watchdog
timer device. It returns 0 on success, -EINVAL for "parameter out of range"
and -EIO for "could not write value to the watchdog". On success this
diff --git a/MAINTAINERS b/MAINTAINERS
index 8ca0b205759f..0868ea5de465 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -5353,6 +5353,12 @@ T: git git://linuxtv.org/anttip/media_tree.git
S: Maintained
F: drivers/media/dvb-frontends/hd29l2*
+HEWLETT PACKARD ENTERPRISE ILO NMI WATCHDOG DRIVER
+M: Brian Boylston <brian.boylston@hpe.com>
+S: Supported
+F: Documentation/watchdog/hpwdt.txt
+F: drivers/watchdog/hpwdt.c
+
HEWLETT-PACKARD SMART ARRAY RAID DRIVER (hpsa)
M: Don Brace <don.brace@microsemi.com>
L: iss_storagedev@hp.com
diff --git a/arch/arm/boot/dts/qcom-apq8064.dtsi b/arch/arm/boot/dts/qcom-apq8064.dtsi
index df96ccdc9bb4..e318d04319a0 100644
--- a/arch/arm/boot/dts/qcom-apq8064.dtsi
+++ b/arch/arm/boot/dts/qcom-apq8064.dtsi
@@ -247,7 +247,8 @@
};
timer@200a000 {
- compatible = "qcom,kpss-timer", "qcom,msm-timer";
+ compatible = "qcom,kpss-timer",
+ "qcom,kpss-wdt-apq8064", "qcom,msm-timer";
interrupts = <1 1 0x301>,
<1 2 0x301>,
<1 3 0x301>;
diff --git a/arch/arm/boot/dts/qcom-ipq4019.dtsi b/arch/arm/boot/dts/qcom-ipq4019.dtsi
index 5c08d19066c2..e625656a608a 100644
--- a/arch/arm/boot/dts/qcom-ipq4019.dtsi
+++ b/arch/arm/boot/dts/qcom-ipq4019.dtsi
@@ -252,7 +252,7 @@
};
watchdog@b017000 {
- compatible = "qcom,kpss-standalone";
+ compatible = "qcom,kpss-wdt", "qcom,kpss-wdt-ipq4019";
reg = <0xb017000 0x40>;
clocks = <&sleep_clk>;
timeout-sec = <10>;
diff --git a/arch/arm/boot/dts/qcom-ipq8064.dtsi b/arch/arm/boot/dts/qcom-ipq8064.dtsi
index 2601a907947b..2e375576ffd0 100644
--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
+++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
@@ -122,7 +122,8 @@
};
timer@200a000 {
- compatible = "qcom,kpss-timer", "qcom,msm-timer";
+ compatible = "qcom,kpss-timer",
+ "qcom,kpss-wdt-ipq8064", "qcom,msm-timer";
interrupts = <1 1 0x301>,
<1 2 0x301>,
<1 3 0x301>,
diff --git a/arch/arm/boot/dts/qcom-msm8960.dtsi b/arch/arm/boot/dts/qcom-msm8960.dtsi
index da05e28a81a7..288f56e0ccf5 100644
--- a/arch/arm/boot/dts/qcom-msm8960.dtsi
+++ b/arch/arm/boot/dts/qcom-msm8960.dtsi
@@ -87,7 +87,8 @@
};
timer@200a000 {
- compatible = "qcom,kpss-timer", "qcom,msm-timer";
+ compatible = "qcom,kpss-timer",
+ "qcom,kpss-wdt-msm8960", "qcom,msm-timer";
interrupts = <1 1 0x301>,
<1 2 0x301>,
<1 3 0x301>;
diff --git a/drivers/platform/x86/intel_pmc_ipc.c b/drivers/platform/x86/intel_pmc_ipc.c
index 6f497e80c9df..b86e1bcaa055 100644
--- a/drivers/platform/x86/intel_pmc_ipc.c
+++ b/drivers/platform/x86/intel_pmc_ipc.c
@@ -85,7 +85,7 @@
* platform device and to export resources for those functions.
*/
#define TCO_DEVICE_NAME "iTCO_wdt"
-#define SMI_EN_OFFSET 0x30
+#define SMI_EN_OFFSET 0x40
#define SMI_EN_SIZE 4
#define TCO_BASE_OFFSET 0x60
#define TCO_REGS_SIZE 16
@@ -94,6 +94,8 @@
#define TELEM_SSRAM_SIZE 240
#define TELEM_PMC_SSRAM_OFFSET 0x1B00
#define TELEM_PUNIT_SSRAM_OFFSET 0x1A00
+#define TCO_PMC_OFFSET 0x8
+#define TCO_PMC_SIZE 0x4
static const int iTCO_version = 3;
@@ -502,7 +504,7 @@ static struct resource tco_res[] = {
static struct itco_wdt_platform_data tco_info = {
.name = "Apollo Lake SoC",
- .version = 3,
+ .version = 5,
};
#define TELEMETRY_RESOURCE_PUNIT_SSRAM 0
@@ -572,8 +574,8 @@ static int ipc_create_tco_device(void)
res->end = res->start + SMI_EN_SIZE - 1;
res = tco_res + TCO_RESOURCE_GCR_MEM;
- res->start = ipcdev.gcr_base;
- res->end = res->start + ipcdev.gcr_size - 1;
+ res->start = ipcdev.gcr_base + TCO_PMC_OFFSET;
+ res->end = res->start + TCO_PMC_SIZE - 1;
ret = platform_device_add_resources(pdev, tco_res, ARRAY_SIZE(tco_res));
if (ret) {
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index b4b3e256491b..1bffe006ca9a 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -48,7 +48,6 @@ config WATCHDOG_NOWAYOUT
config WATCHDOG_SYSFS
bool "Read different watchdog information through sysfs"
- default n
help
Say Y here if you want to enable watchdog device status read through
sysfs attributes.
@@ -516,6 +515,15 @@ config MAX63XX_WATCHDOG
help
Support for memory mapped max63{69,70,71,72,73,74} watchdog timer.
+config MAX77620_WATCHDOG
+ tristate "Maxim Max77620 Watchdog Timer"
+ depends on MFD_MAX77620
+ help
+ This is the driver for the Max77620 watchdog timer.
+ Say 'Y' here to enable the watchdog timer support for
+ MAX77620 chips. To compile this driver as a module,
+ choose M here: the module will be called max77620_wdt.
+
config IMX2_WDT
tristate "IMX2+ Watchdog"
depends on ARCH_MXC || ARCH_LAYERSCAPE
@@ -609,6 +617,16 @@ config QCOM_WDT
To compile this driver as a module, choose M here: the
module will be called qcom_wdt.
+config MESON_GXBB_WATCHDOG
+ tristate "Amlogic Meson GXBB SoCs watchdog support"
+ depends on ARCH_MESON
+ select WATCHDOG_CORE
+ help
+ Say Y here to include support for the watchdog timer
+ in Amlogic Meson GXBB SoCs.
+ To compile this driver as a module, choose M here: the
+ module will be called meson_gxbb_wdt.
+
config MESON_WATCHDOG
tristate "Amlogic Meson SoCs watchdog support"
depends on ARCH_MESON
@@ -669,6 +687,19 @@ config RENESAS_WDT
This driver adds watchdog support for the integrated watchdogs in the
Renesas R-Car and other SH-Mobile SoCs (usually named RWDT or SWDT).
+config ASPEED_WATCHDOG
+ tristate "Aspeed 2400 watchdog support"
+ depends on ARCH_ASPEED || COMPILE_TEST
+ select WATCHDOG_CORE
+ help
+ Say Y here to include support for the watchdog timer
+ in Apseed BMC SoCs.
+
+ This driver is required to reboot the SoC.
+
+ To compile this driver as a module, choose M here: the
+ module will be called aspeed_wdt.
+
# AVR32 Architecture
config AT32AP700X_WDT
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index a46e7c1380ac..c22ad3ea3539 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -67,6 +67,7 @@ obj-$(CONFIG_ST_LPC_WATCHDOG) += st_lpc_wdt.o
obj-$(CONFIG_QCOM_WDT) += qcom-wdt.o
obj-$(CONFIG_BCM_KONA_WDT) += bcm_kona_wdt.o
obj-$(CONFIG_TEGRA_WATCHDOG) += tegra_wdt.o
+obj-$(CONFIG_MESON_GXBB_WATCHDOG) += meson_gxbb_wdt.o
obj-$(CONFIG_MESON_WATCHDOG) += meson_wdt.o
obj-$(CONFIG_MEDIATEK_WATCHDOG) += mtk_wdt.o
obj-$(CONFIG_DIGICOLOR_WATCHDOG) += digicolor_wdt.o
@@ -74,6 +75,7 @@ obj-$(CONFIG_LPC18XX_WATCHDOG) += lpc18xx_wdt.o
obj-$(CONFIG_BCM7038_WDT) += bcm7038_wdt.o
obj-$(CONFIG_ATLAS7_WATCHDOG) += atlas7_wdt.o
obj-$(CONFIG_RENESAS_WDT) += renesas_wdt.o
+obj-$(CONFIG_ASPEED_WATCHDOG) += aspeed_wdt.o
# AVR32 Architecture
obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o
@@ -203,6 +205,7 @@ obj-$(CONFIG_TANGOX_WATCHDOG) += tangox_wdt.o
obj-$(CONFIG_WM831X_WATCHDOG) += wm831x_wdt.o
obj-$(CONFIG_WM8350_WATCHDOG) += wm8350_wdt.o
obj-$(CONFIG_MAX63XX_WATCHDOG) += max63xx_wdt.o
+obj-$(CONFIG_MAX77620_WATCHDOG) += max77620_wdt.o
obj-$(CONFIG_ZIIRAVE_WATCHDOG) += ziirave_wdt.o
obj-$(CONFIG_SOFT_WATCHDOG) += softdog.o
obj-$(CONFIG_MENF21BMC_WATCHDOG) += menf21bmc_wdt.o
diff --git a/drivers/watchdog/aspeed_wdt.c b/drivers/watchdog/aspeed_wdt.c
new file mode 100644
index 000000000000..f5ad8023c2e6
--- /dev/null
+++ b/drivers/watchdog/aspeed_wdt.c
@@ -0,0 +1,212 @@
+/*
+ * Copyright 2016 IBM Corporation
+ *
+ * Joel Stanley <joel@jms.id.au>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/watchdog.h>
+
+struct aspeed_wdt {
+ struct watchdog_device wdd;
+ void __iomem *base;
+ u32 ctrl;
+};
+
+static const struct of_device_id aspeed_wdt_of_table[] = {
+ { .compatible = "aspeed,ast2400-wdt" },
+ { .compatible = "aspeed,ast2500-wdt" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, aspeed_wdt_of_table);
+
+#define WDT_STATUS 0x00
+#define WDT_RELOAD_VALUE 0x04
+#define WDT_RESTART 0x08
+#define WDT_CTRL 0x0C
+#define WDT_CTRL_RESET_MODE_SOC (0x00 << 5)
+#define WDT_CTRL_RESET_MODE_FULL_CHIP (0x01 << 5)
+#define WDT_CTRL_1MHZ_CLK BIT(4)
+#define WDT_CTRL_WDT_EXT BIT(3)
+#define WDT_CTRL_WDT_INTR BIT(2)
+#define WDT_CTRL_RESET_SYSTEM BIT(1)
+#define WDT_CTRL_ENABLE BIT(0)
+
+#define WDT_RESTART_MAGIC 0x4755
+
+/* 32 bits at 1MHz, in milliseconds */
+#define WDT_MAX_TIMEOUT_MS 4294967
+#define WDT_DEFAULT_TIMEOUT 30
+#define WDT_RATE_1MHZ 1000000
+
+static struct aspeed_wdt *to_aspeed_wdt(struct watchdog_device *wdd)
+{
+ return container_of(wdd, struct aspeed_wdt, wdd);
+}
+
+static void aspeed_wdt_enable(struct aspeed_wdt *wdt, int count)
+{
+ wdt->ctrl |= WDT_CTRL_ENABLE;
+
+ writel(0, wdt->base + WDT_CTRL);
+ writel(count, wdt->base + WDT_RELOAD_VALUE);
+ writel(WDT_RESTART_MAGIC, wdt->base + WDT_RESTART);
+ writel(wdt->ctrl, wdt->base + WDT_CTRL);
+}
+
+static int aspeed_wdt_start(struct watchdog_device *wdd)
+{
+ struct aspeed_wdt *wdt = to_aspeed_wdt(wdd);
+
+ aspeed_wdt_enable(wdt, wdd->timeout * WDT_RATE_1MHZ);
+
+ return 0;
+}
+
+static int aspeed_wdt_stop(struct watchdog_device *wdd)
+{
+ struct aspeed_wdt *wdt = to_aspeed_wdt(wdd);
+
+ wdt->ctrl &= ~WDT_CTRL_ENABLE;
+ writel(wdt->ctrl, wdt->base + WDT_CTRL);
+
+ return 0;
+}
+
+static int aspeed_wdt_ping(struct watchdog_device *wdd)
+{
+ struct aspeed_wdt *wdt = to_aspeed_wdt(wdd);
+
+ writel(WDT_RESTART_MAGIC, wdt->base + WDT_RESTART);
+
+ return 0;
+}
+
+static int aspeed_wdt_set_timeout(struct watchdog_device *wdd,
+ unsigned int timeout)
+{
+ struct aspeed_wdt *wdt = to_aspeed_wdt(wdd);
+ u32 actual;
+
+ wdd->timeout = timeout;
+
+ actual = min(timeout, wdd->max_hw_heartbeat_ms * 1000);
+
+ writel(actual * WDT_RATE_1MHZ, wdt->base + WDT_RELOAD_VALUE);
+ writel(WDT_RESTART_MAGIC, wdt->base + WDT_RESTART);
+
+ return 0;
+}
+
+static int aspeed_wdt_restart(struct watchdog_device *wdd,
+ unsigned long action, void *data)
+{
+ struct aspeed_wdt *wdt = to_aspeed_wdt(wdd);
+
+ aspeed_wdt_enable(wdt, 128 * WDT_RATE_1MHZ / 1000);
+
+ mdelay(1000);
+
+ return 0;
+}
+
+static const struct watchdog_ops aspeed_wdt_ops = {
+ .start = aspeed_wdt_start,
+ .stop = aspeed_wdt_stop,
+ .ping = aspeed_wdt_ping,
+ .set_timeout = aspeed_wdt_set_timeout,
+ .restart = aspeed_wdt_restart,
+ .owner = THIS_MODULE,
+};
+
+static const struct watchdog_info aspeed_wdt_info = {
+ .options = WDIOF_KEEPALIVEPING
+ | WDIOF_MAGICCLOSE
+ | WDIOF_SETTIMEOUT,
+ .identity = KBUILD_MODNAME,
+};
+
+static int aspeed_wdt_remove(struct platform_device *pdev)
+{
+ struct aspeed_wdt *wdt = platform_get_drvdata(pdev);
+
+ watchdog_unregister_device(&wdt->wdd);
+
+ return 0;
+}
+
+static int aspeed_wdt_probe(struct platform_device *pdev)
+{
+ struct aspeed_wdt *wdt;
+ struct resource *res;
+ int ret;
+
+ wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL);
+ if (!wdt)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ wdt->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(wdt->base))
+ return PTR_ERR(wdt->base);
+
+ /*
+ * The ast2400 wdt can run at PCLK, or 1MHz. The ast2500 only
+ * runs at 1MHz. We chose to always run at 1MHz, as there's no
+ * good reason to have a faster watchdog counter.
+ */
+ wdt->wdd.info = &aspeed_wdt_info;
+ wdt->wdd.ops = &aspeed_wdt_ops;
+ wdt->wdd.max_hw_heartbeat_ms = WDT_MAX_TIMEOUT_MS;
+ wdt->wdd.parent = &pdev->dev;
+
+ wdt->wdd.timeout = WDT_DEFAULT_TIMEOUT;
+ watchdog_init_timeout(&wdt->wdd, 0, &pdev->dev);
+
+ /*
+ * Control reset on a per-device basis to ensure the
+ * host is not affected by a BMC reboot, so only reset
+ * the SOC and not the full chip
+ */
+ wdt->ctrl = WDT_CTRL_RESET_MODE_SOC |
+ WDT_CTRL_1MHZ_CLK |
+ WDT_CTRL_RESET_SYSTEM;
+
+ if (readl(wdt->base + WDT_CTRL) & WDT_CTRL_ENABLE) {
+ aspeed_wdt_start(&wdt->wdd);
+ set_bit(WDOG_HW_RUNNING, &wdt->wdd.status);
+ }
+
+ ret = watchdog_register_device(&wdt->wdd);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register\n");
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, wdt);
+
+ return 0;
+}
+
+static struct platform_driver aspeed_watchdog_driver = {
+ .probe = aspeed_wdt_probe,
+ .remove = aspeed_wdt_remove,
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .of_match_table = of_match_ptr(aspeed_wdt_of_table),
+ },
+};
+module_platform_driver(aspeed_watchdog_driver);
+
+MODULE_DESCRIPTION("Aspeed Watchdog Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/watchdog/bcm2835_wdt.c b/drivers/watchdog/bcm2835_wdt.c
index 2e6164c4abc0..4dddd8298a22 100644
--- a/drivers/watchdog/bcm2835_wdt.c
+++ b/drivers/watchdog/bcm2835_wdt.c
@@ -82,12 +82,6 @@ static int bcm2835_wdt_stop(struct watchdog_device *wdog)
return 0;
}
-static int bcm2835_wdt_set_timeout(struct watchdog_device *wdog, unsigned int t)
-{
- wdog->timeout = t;
- return 0;
-}
-
static unsigned int bcm2835_wdt_get_timeleft(struct watchdog_device *wdog)
{
struct bcm2835_wdt *wdt = watchdog_get_drvdata(wdog);
@@ -96,15 +90,14 @@ static unsigned int bcm2835_wdt_get_timeleft(struct watchdog_device *wdog)
return WDOG_TICKS_TO_SECS(ret & PM_WDOG_TIME_SET);
}
-static struct watchdog_ops bcm2835_wdt_ops = {
+static const struct watchdog_ops bcm2835_wdt_ops = {
.owner = THIS_MODULE,
.start = bcm2835_wdt_start,
.stop = bcm2835_wdt_stop,
- .set_timeout = bcm2835_wdt_set_timeout,
.get_timeleft = bcm2835_wdt_get_timeleft,
};
-static struct watchdog_info bcm2835_wdt_info = {
+static const struct watchdog_info bcm2835_wdt_info = {
.options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE |
WDIOF_KEEPALIVEPING,
.identity = "Broadcom BCM2835 Watchdog timer",
diff --git a/drivers/watchdog/da9063_wdt.c b/drivers/watchdog/da9063_wdt.c
index a100f648880d..5d6b4e5f7989 100644
--- a/drivers/watchdog/da9063_wdt.c
+++ b/drivers/watchdog/da9063_wdt.c
@@ -34,6 +34,7 @@ static const unsigned int wdt_timeout[] = { 0, 2, 4, 8, 16, 32, 65, 131 };
#define DA9063_WDT_MIN_TIMEOUT wdt_timeout[DA9063_TWDSCALE_MIN]
#define DA9063_WDT_MAX_TIMEOUT wdt_timeout[DA9063_TWDSCALE_MAX]
#define DA9063_WDG_TIMEOUT wdt_timeout[3]
+#define DA9063_RESET_PROTECTION_MS 256
struct da9063_watchdog {
struct da9063 *da9063;
@@ -171,6 +172,7 @@ static int da9063_wdt_probe(struct platform_device *pdev)
wdt->wdtdev.ops = &da9063_watchdog_ops;
wdt->wdtdev.min_timeout = DA9063_WDT_MIN_TIMEOUT;
wdt->wdtdev.max_timeout = DA9063_WDT_MAX_TIMEOUT;
+ wdt->wdtdev.min_hw_heartbeat_ms = DA9063_RESET_PROTECTION_MS;
wdt->wdtdev.timeout = DA9063_WDG_TIMEOUT;
wdt->wdtdev.parent = &pdev->dev;
diff --git a/drivers/watchdog/f71808e_wdt.c b/drivers/watchdog/f71808e_wdt.c
index d4ba262da7ba..1b7e9169072f 100644
--- a/drivers/watchdog/f71808e_wdt.c
+++ b/drivers/watchdog/f71808e_wdt.c
@@ -45,9 +45,11 @@
#define SIO_REG_DEVREV 0x22 /* Device revision */
#define SIO_REG_MANID 0x23 /* Fintek ID (2 bytes) */
#define SIO_REG_ROM_ADDR_SEL 0x27 /* ROM address select */
+#define SIO_F81866_REG_PORT_SEL 0x27 /* F81866 Multi-Function Register */
#define SIO_REG_MFUNCT1 0x29 /* Multi function select 1 */
#define SIO_REG_MFUNCT2 0x2a /* Multi function select 2 */
#define SIO_REG_MFUNCT3 0x2b /* Multi function select 3 */
+#define SIO_F81866_REG_GPIO1 0x2c /* F81866 GPIO1 Enable Register */
#define SIO_REG_ENABLE 0x30 /* Logical device enable */
#define SIO_REG_ADDR 0x60 /* Logical device address (2 bytes) */
@@ -60,6 +62,7 @@
#define SIO_F71882_ID 0x0541 /* Chipset ID */
#define SIO_F71889_ID 0x0723 /* Chipset ID */
#define SIO_F81865_ID 0x0704 /* Chipset ID */
+#define SIO_F81866_ID 0x1010 /* Chipset ID */
#define F71808FG_REG_WDO_CONF 0xf0
#define F71808FG_REG_WDT_CONF 0xf5
@@ -116,7 +119,8 @@ module_param(start_withtimeout, uint, 0);
MODULE_PARM_DESC(start_withtimeout, "Start watchdog timer on module load with"
" given initial timeout. Zero (default) disables this feature.");
-enum chips { f71808fg, f71858fg, f71862fg, f71869, f71882fg, f71889fg, f81865 };
+enum chips { f71808fg, f71858fg, f71862fg, f71869, f71882fg, f71889fg, f81865,
+ f81866};
static const char *f71808e_names[] = {
"f71808fg",
@@ -126,6 +130,7 @@ static const char *f71808e_names[] = {
"f71882fg",
"f71889fg",
"f81865",
+ "f81866",
};
/* Super-I/O Function prototypes */
@@ -370,6 +375,22 @@ static int watchdog_start(void)
superio_clear_bit(watchdog.sioaddr, SIO_REG_MFUNCT3, 5);
break;
+ case f81866:
+ /* Set pin 70 to WDTRST# */
+ superio_clear_bit(watchdog.sioaddr, SIO_F81866_REG_PORT_SEL,
+ BIT(3) | BIT(0));
+ superio_set_bit(watchdog.sioaddr, SIO_F81866_REG_PORT_SEL,
+ BIT(2));
+ /*
+ * GPIO1 Control Register when 27h BIT3:2 = 01 & BIT0 = 0.
+ * The PIN 70(GPIO15/WDTRST) is controlled by 2Ch:
+ * BIT5: 0 -> WDTRST#
+ * 1 -> GPIO15
+ */
+ superio_clear_bit(watchdog.sioaddr, SIO_F81866_REG_GPIO1,
+ BIT(5));
+ break;
+
default:
/*
* 'default' label to shut up the compiler and catch
@@ -382,7 +403,7 @@ static int watchdog_start(void)
superio_select(watchdog.sioaddr, SIO_F71808FG_LD_WDT);
superio_set_bit(watchdog.sioaddr, SIO_REG_ENABLE, 0);
- if (watchdog.type == f81865)
+ if (watchdog.type == f81865 || watchdog.type == f81866)
superio_set_bit(watchdog.sioaddr, F81865_REG_WDO_CONF,
F81865_FLAG_WDOUT_EN);
else
@@ -788,6 +809,9 @@ static int __init f71808e_find(int sioaddr)
case SIO_F81865_ID:
watchdog.type = f81865;
break;
+ case SIO_F81866_ID:
+ watchdog.type = f81866;
+ break;
default:
pr_info("Unrecognized Fintek device: %04x\n",
(unsigned int)devid);
diff --git a/drivers/watchdog/gpio_wdt.c b/drivers/watchdog/gpio_wdt.c
index ba066e4a707b..93457cabc178 100644
--- a/drivers/watchdog/gpio_wdt.c
+++ b/drivers/watchdog/gpio_wdt.c
@@ -151,6 +151,8 @@ static int gpio_wdt_probe(struct platform_device *pdev)
if (!priv)
return -ENOMEM;
+ platform_set_drvdata(pdev, priv);
+
priv->gpio = of_get_gpio_flags(pdev->dev.of_node, 0, &flags);
if (!gpio_is_valid(priv->gpio))
return priv->gpio;
diff --git a/drivers/watchdog/iTCO_wdt.c b/drivers/watchdog/iTCO_wdt.c
index 0acc6c5f729d..54cab189a763 100644
--- a/drivers/watchdog/iTCO_wdt.c
+++ b/drivers/watchdog/iTCO_wdt.c
@@ -150,6 +150,7 @@ static inline u32 no_reboot_bit(void)
u32 enable_bit;
switch (iTCO_wdt_private.iTCO_version) {
+ case 5:
case 3:
enable_bit = 0x00000010;
break;
@@ -512,6 +513,7 @@ static int iTCO_wdt_probe(struct platform_device *dev)
/* Clear out the (probably old) status */
switch (iTCO_wdt_private.iTCO_version) {
+ case 5:
case 4:
outw(0x0008, TCO1_STS); /* Clear the Time Out Status bit */
outw(0x0002, TCO2_STS); /* Clear SECOND_TO_STS bit */
diff --git a/drivers/watchdog/max77620_wdt.c b/drivers/watchdog/max77620_wdt.c
new file mode 100644
index 000000000000..48b84df2afda
--- /dev/null
+++ b/drivers/watchdog/max77620_wdt.c
@@ -0,0 +1,227 @@
+/*
+ * Maxim MAX77620 Watchdog Driver
+ *
+ * Copyright (C) 2016 NVIDIA CORPORATION. All rights reserved.
+ *
+ * Author: Laxman Dewangan <ldewangan@nvidia.com>
+ *
+ * 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/err.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mfd/max77620.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/watchdog.h>
+
+static bool nowayout = WATCHDOG_NOWAYOUT;
+
+struct max77620_wdt {
+ struct device *dev;
+ struct regmap *rmap;
+ struct watchdog_device wdt_dev;
+};
+
+static int max77620_wdt_start(struct watchdog_device *wdt_dev)
+{
+ struct max77620_wdt *wdt = watchdog_get_drvdata(wdt_dev);
+
+ return regmap_update_bits(wdt->rmap, MAX77620_REG_CNFGGLBL2,
+ MAX77620_WDTEN, MAX77620_WDTEN);
+}
+
+static int max77620_wdt_stop(struct watchdog_device *wdt_dev)
+{
+ struct max77620_wdt *wdt = watchdog_get_drvdata(wdt_dev);
+
+ return regmap_update_bits(wdt->rmap, MAX77620_REG_CNFGGLBL2,
+ MAX77620_WDTEN, 0);
+}
+
+static int max77620_wdt_ping(struct watchdog_device *wdt_dev)
+{
+ struct max77620_wdt *wdt = watchdog_get_drvdata(wdt_dev);
+
+ return regmap_update_bits(wdt->rmap, MAX77620_REG_CNFGGLBL3,
+ MAX77620_WDTC_MASK, 0x1);
+}
+
+static int max77620_wdt_set_timeout(struct watchdog_device *wdt_dev,
+ unsigned int timeout)
+{
+ struct max77620_wdt *wdt = watchdog_get_drvdata(wdt_dev);
+ unsigned int wdt_timeout;
+ u8 regval;
+ int ret;
+
+ switch (timeout) {
+ case 0 ... 2:
+ regval = MAX77620_TWD_2s;
+ wdt_timeout = 2;
+ break;
+
+ case 3 ... 16:
+ regval = MAX77620_TWD_16s;
+ wdt_timeout = 16;
+ break;
+
+ case 17 ... 64:
+ regval = MAX77620_TWD_64s;
+ wdt_timeout = 64;
+ break;
+
+ default:
+ regval = MAX77620_TWD_128s;
+ wdt_timeout = 128;
+ break;
+ }
+
+ ret = regmap_update_bits(wdt->rmap, MAX77620_REG_CNFGGLBL3,
+ MAX77620_WDTC_MASK, 0x1);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_update_bits(wdt->rmap, MAX77620_REG_CNFGGLBL2,
+ MAX77620_TWD_MASK, regval);
+ if (ret < 0)
+ return ret;
+
+ wdt_dev->timeout = wdt_timeout;
+
+ return 0;
+}
+
+static const struct watchdog_info max77620_wdt_info = {
+ .identity = "max77620-watchdog",
+ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
+};
+
+static const struct watchdog_ops max77620_wdt_ops = {
+ .start = max77620_wdt_start,
+ .stop = max77620_wdt_stop,
+ .ping = max77620_wdt_ping,
+ .set_timeout = max77620_wdt_set_timeout,
+};
+
+static int max77620_wdt_probe(struct platform_device *pdev)
+{
+ struct max77620_wdt *wdt;
+ struct watchdog_device *wdt_dev;
+ unsigned int regval;
+ int ret;
+
+ wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL);
+ if (!wdt)
+ return -ENOMEM;
+
+ wdt->dev = &pdev->dev;
+ wdt->rmap = dev_get_regmap(pdev->dev.parent, NULL);
+ if (!wdt->rmap) {
+ dev_err(wdt->dev, "Failed to get parent regmap\n");
+ return -ENODEV;
+ }
+
+ wdt_dev = &wdt->wdt_dev;
+ wdt_dev->info = &max77620_wdt_info;
+ wdt_dev->ops = &max77620_wdt_ops;
+ wdt_dev->min_timeout = 2;
+ wdt_dev->max_timeout = 128;
+ wdt_dev->max_hw_heartbeat_ms = 128 * 1000;
+
+ platform_set_drvdata(pdev, wdt);
+
+ /* Enable WD_RST_WK - WDT expire results in a restart */
+ ret = regmap_update_bits(wdt->rmap, MAX77620_REG_ONOFFCNFG2,
+ MAX77620_ONOFFCNFG2_WD_RST_WK,
+ MAX77620_ONOFFCNFG2_WD_RST_WK);
+ if (ret < 0) {
+ dev_err(wdt->dev, "Failed to set WD_RST_WK: %d\n", ret);
+ return ret;
+ }
+
+ /* Set WDT clear in OFF and sleep mode */
+ ret = regmap_update_bits(wdt->rmap, MAX77620_REG_CNFGGLBL2,
+ MAX77620_WDTOFFC | MAX77620_WDTSLPC,
+ MAX77620_WDTOFFC | MAX77620_WDTSLPC);
+ if (ret < 0) {
+ dev_err(wdt->dev, "Failed to set WDT OFF mode: %d\n", ret);
+ return ret;
+ }
+
+ /* Check if WDT running and if yes then set flags properly */
+ ret = regmap_read(wdt->rmap, MAX77620_REG_CNFGGLBL2, &regval);
+ if (ret < 0) {
+ dev_err(wdt->dev, "Failed to read WDT CFG register: %d\n", ret);
+ return ret;
+ }
+
+ switch (regval & MAX77620_TWD_MASK) {
+ case MAX77620_TWD_2s:
+ wdt_dev->timeout = 2;
+ break;
+ case MAX77620_TWD_16s:
+ wdt_dev->timeout = 16;
+ break;
+ case MAX77620_TWD_64s:
+ wdt_dev->timeout = 64;
+ break;
+ default:
+ wdt_dev->timeout = 128;
+ break;
+ }
+
+ if (regval & MAX77620_WDTEN)
+ set_bit(WDOG_HW_RUNNING, &wdt_dev->status);
+
+ watchdog_set_nowayout(wdt_dev, nowayout);
+ watchdog_set_drvdata(wdt_dev, wdt);
+
+ ret = watchdog_register_device(wdt_dev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "watchdog registration failed: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int max77620_wdt_remove(struct platform_device *pdev)
+{
+ struct max77620_wdt *wdt = platform_get_drvdata(pdev);
+
+ max77620_wdt_stop(&wdt->wdt_dev);
+ watchdog_unregister_device(&wdt->wdt_dev);
+
+ return 0;
+}
+
+static struct platform_device_id max77620_wdt_devtype[] = {
+ { .name = "max77620-watchdog", },
+ { },
+};
+
+static struct platform_driver max77620_wdt_driver = {
+ .driver = {
+ .name = "max77620-watchdog",
+ },
+ .probe = max77620_wdt_probe,
+ .remove = max77620_wdt_remove,
+ .id_table = max77620_wdt_devtype,
+};
+
+module_platform_driver(max77620_wdt_driver);
+
+MODULE_DESCRIPTION("Max77620 watchdog timer driver");
+
+module_param(nowayout, bool, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
+ "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/watchdog/meson_gxbb_wdt.c b/drivers/watchdog/meson_gxbb_wdt.c
new file mode 100644
index 000000000000..44d180a2c5e5
--- /dev/null
+++ b/drivers/watchdog/meson_gxbb_wdt.c
@@ -0,0 +1,270 @@
+/*
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright (c) 2016 BayLibre, SAS.
+ * Author: Neil Armstrong <narmstrong@baylibre.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * BSD LICENSE
+ *
+ * Copyright (c) 2016 BayLibre, SAS.
+ * Author: Neil Armstrong <narmstrong@baylibre.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/watchdog.h>
+
+#define DEFAULT_TIMEOUT 30 /* seconds */
+
+#define GXBB_WDT_CTRL_REG 0x0
+#define GXBB_WDT_TCNT_REG 0x8
+#define GXBB_WDT_RSET_REG 0xc
+
+#define GXBB_WDT_CTRL_CLKDIV_EN BIT(25)
+#define GXBB_WDT_CTRL_CLK_EN BIT(24)
+#define GXBB_WDT_CTRL_EE_RESET BIT(21)
+#define GXBB_WDT_CTRL_EN BIT(18)
+#define GXBB_WDT_CTRL_DIV_MASK (BIT(18) - 1)
+
+#define GXBB_WDT_TCNT_SETUP_MASK (BIT(16) - 1)
+#define GXBB_WDT_TCNT_CNT_SHIFT 16
+
+struct meson_gxbb_wdt {
+ void __iomem *reg_base;
+ struct watchdog_device wdt_dev;
+ struct clk *clk;
+};
+
+static int meson_gxbb_wdt_start(struct watchdog_device *wdt_dev)
+{
+ struct meson_gxbb_wdt *data = watchdog_get_drvdata(wdt_dev);
+
+ writel(readl(data->reg_base + GXBB_WDT_CTRL_REG) | GXBB_WDT_CTRL_EN,
+ data->reg_base + GXBB_WDT_CTRL_REG);
+
+ return 0;
+}
+
+static int meson_gxbb_wdt_stop(struct watchdog_device *wdt_dev)
+{
+ struct meson_gxbb_wdt *data = watchdog_get_drvdata(wdt_dev);
+
+ writel(readl(data->reg_base + GXBB_WDT_CTRL_REG) & ~GXBB_WDT_CTRL_EN,
+ data->reg_base + GXBB_WDT_CTRL_REG);
+
+ return 0;
+}
+
+static int meson_gxbb_wdt_ping(struct watchdog_device *wdt_dev)
+{
+ struct meson_gxbb_wdt *data = watchdog_get_drvdata(wdt_dev);
+
+ writel(0, data->reg_base + GXBB_WDT_RSET_REG);
+
+ return 0;
+}
+
+static int meson_gxbb_wdt_set_timeout(struct watchdog_device *wdt_dev,
+ unsigned int timeout)
+{
+ struct meson_gxbb_wdt *data = watchdog_get_drvdata(wdt_dev);
+ unsigned long tcnt = timeout * 1000;
+
+ if (tcnt > GXBB_WDT_TCNT_SETUP_MASK)
+ tcnt = GXBB_WDT_TCNT_SETUP_MASK;
+
+ wdt_dev->timeout = timeout;
+
+ meson_gxbb_wdt_ping(wdt_dev);
+
+ writel(tcnt, data->reg_base + GXBB_WDT_TCNT_REG);
+
+ return 0;
+}
+
+static unsigned int meson_gxbb_wdt_get_timeleft(struct watchdog_device *wdt_dev)
+{
+ struct meson_gxbb_wdt *data = watchdog_get_drvdata(wdt_dev);
+ unsigned long reg;
+
+ reg = readl(data->reg_base + GXBB_WDT_TCNT_REG);
+
+ return ((reg >> GXBB_WDT_TCNT_CNT_SHIFT) -
+ (reg & GXBB_WDT_TCNT_SETUP_MASK)) / 1000;
+}
+
+static const struct watchdog_ops meson_gxbb_wdt_ops = {
+ .start = meson_gxbb_wdt_start,
+ .stop = meson_gxbb_wdt_stop,
+ .ping = meson_gxbb_wdt_ping,
+ .set_timeout = meson_gxbb_wdt_set_timeout,
+ .get_timeleft = meson_gxbb_wdt_get_timeleft,
+};
+
+static const struct watchdog_info meson_gxbb_wdt_info = {
+ .identity = "Meson GXBB Watchdog",
+ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
+};
+
+static int __maybe_unused meson_gxbb_wdt_resume(struct device *dev)
+{
+ struct meson_gxbb_wdt *data = dev_get_drvdata(dev);
+
+ if (watchdog_active(&data->wdt_dev))
+ meson_gxbb_wdt_start(&data->wdt_dev);
+
+ return 0;
+}
+
+static int __maybe_unused meson_gxbb_wdt_suspend(struct device *dev)
+{
+ struct meson_gxbb_wdt *data = dev_get_drvdata(dev);
+
+ if (watchdog_active(&data->wdt_dev))
+ meson_gxbb_wdt_stop(&data->wdt_dev);
+
+ return 0;
+}
+
+static const struct dev_pm_ops meson_gxbb_wdt_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(meson_gxbb_wdt_suspend, meson_gxbb_wdt_resume)
+};
+
+static const struct of_device_id meson_gxbb_wdt_dt_ids[] = {
+ { .compatible = "amlogic,meson-gxbb-wdt", },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, meson_gxbb_wdt_dt_ids);
+
+static int meson_gxbb_wdt_probe(struct platform_device *pdev)
+{
+ struct meson_gxbb_wdt *data;
+ struct resource *res;
+ int ret;
+
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ data->reg_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(data->reg_base))
+ return PTR_ERR(data->reg_base);
+
+ data->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(data->clk))
+ return PTR_ERR(data->clk);
+
+ clk_prepare_enable(data->clk);
+
+ platform_set_drvdata(pdev, data);
+
+ data->wdt_dev.parent = &pdev->dev;
+ data->wdt_dev.info = &meson_gxbb_wdt_info;
+ data->wdt_dev.ops = &meson_gxbb_wdt_ops;
+ data->wdt_dev.max_hw_heartbeat_ms = GXBB_WDT_TCNT_SETUP_MASK;
+ data->wdt_dev.min_timeout = 1;
+ data->wdt_dev.timeout = DEFAULT_TIMEOUT;
+ watchdog_set_drvdata(&data->wdt_dev, data);
+
+ /* Setup with 1ms timebase */
+ writel(((clk_get_rate(data->clk) / 1000) & GXBB_WDT_CTRL_DIV_MASK) |
+ GXBB_WDT_CTRL_EE_RESET |
+ GXBB_WDT_CTRL_CLK_EN |
+ GXBB_WDT_CTRL_CLKDIV_EN,
+ data->reg_base + GXBB_WDT_CTRL_REG);
+
+ meson_gxbb_wdt_set_timeout(&data->wdt_dev, data->wdt_dev.timeout);
+
+ ret = watchdog_register_device(&data->wdt_dev);
+ if (ret) {
+ clk_disable_unprepare(data->clk);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int meson_gxbb_wdt_remove(struct platform_device *pdev)
+{
+ struct meson_gxbb_wdt *data = platform_get_drvdata(pdev);
+
+ watchdog_unregister_device(&data->wdt_dev);
+
+ clk_disable_unprepare(data->clk);
+
+ return 0;
+}
+
+static void meson_gxbb_wdt_shutdown(struct platform_device *pdev)
+{
+ struct meson_gxbb_wdt *data = platform_get_drvdata(pdev);
+
+ meson_gxbb_wdt_stop(&data->wdt_dev);
+}
+
+static struct platform_driver meson_gxbb_wdt_driver = {
+ .probe = meson_gxbb_wdt_probe,
+ .remove = meson_gxbb_wdt_remove,
+ .shutdown = meson_gxbb_wdt_shutdown,
+ .driver = {
+ .name = "meson-gxbb-wdt",
+ .pm = &meson_gxbb_wdt_pm_ops,
+ .of_match_table = meson_gxbb_wdt_dt_ids,
+ },
+};
+
+module_platform_driver(meson_gxbb_wdt_driver);
+
+MODULE_ALIAS("platform:meson-gxbb-wdt");
+MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
+MODULE_DESCRIPTION("Amlogic Meson GXBB Watchdog timer driver");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/watchdog/nv_tco.c b/drivers/watchdog/nv_tco.c
index bd917bb757b8..a0fabf6f92b0 100644
--- a/drivers/watchdog/nv_tco.c
+++ b/drivers/watchdog/nv_tco.c
@@ -294,6 +294,8 @@ static const struct pci_device_id tco_pci_tbl[] = {
PCI_ANY_ID, PCI_ANY_ID, },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SMBUS,
PCI_ANY_ID, PCI_ANY_ID, },
+ { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP78S_SMBUS,
+ PCI_ANY_ID, PCI_ANY_ID, },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP79_SMBUS,
PCI_ANY_ID, PCI_ANY_ID, },
{ 0, }, /* End of list */
diff --git a/drivers/watchdog/pcwd.c b/drivers/watchdog/pcwd.c
index e936f15dc7c7..3ad5206d7935 100644
--- a/drivers/watchdog/pcwd.c
+++ b/drivers/watchdog/pcwd.c
@@ -992,19 +992,7 @@ static struct isa_driver pcwd_isa_driver = {
},
};
-static int __init pcwd_init_module(void)
-{
- return isa_register_driver(&pcwd_isa_driver, PCWD_ISA_NR_CARDS);
-}
-
-static void __exit pcwd_cleanup_module(void)
-{
- isa_unregister_driver(&pcwd_isa_driver);
- pr_info("Watchdog Module Unloaded\n");
-}
-
-module_init(pcwd_init_module);
-module_exit(pcwd_cleanup_module);
+module_isa_driver(pcwd_isa_driver, PCWD_ISA_NR_CARDS);
MODULE_AUTHOR("Ken Hollis <kenji@bitgate.com>, "
"Wim Van Sebroeck <wim@iguana.be>");
diff --git a/drivers/watchdog/pic32-dmt.c b/drivers/watchdog/pic32-dmt.c
index 962f58c03353..c797305f8338 100644
--- a/drivers/watchdog/pic32-dmt.c
+++ b/drivers/watchdog/pic32-dmt.c
@@ -176,8 +176,8 @@ static int pic32_dmt_probe(struct platform_device *pdev)
struct watchdog_device *wdd = &pic32_dmt_wdd;
dmt = devm_kzalloc(&pdev->dev, sizeof(*dmt), GFP_KERNEL);
- if (IS_ERR(dmt))
- return PTR_ERR(dmt);
+ if (!dmt)
+ return -ENOMEM;
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
dmt->regs = devm_ioremap_resource(&pdev->dev, mem);
@@ -245,7 +245,6 @@ static struct platform_driver pic32_dmt_driver = {
.remove = pic32_dmt_remove,
.driver = {
.name = "pic32-dmt",
- .owner = THIS_MODULE,
.of_match_table = of_match_ptr(pic32_dmt_of_ids),
}
};
diff --git a/drivers/watchdog/pic32-wdt.c b/drivers/watchdog/pic32-wdt.c
index 6047aa89a4d3..e2761068dc6f 100644
--- a/drivers/watchdog/pic32-wdt.c
+++ b/drivers/watchdog/pic32-wdt.c
@@ -174,8 +174,8 @@ static int pic32_wdt_drv_probe(struct platform_device *pdev)
struct resource *mem;
wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL);
- if (IS_ERR(wdt))
- return PTR_ERR(wdt);
+ if (!wdt)
+ return -ENOMEM;
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
wdt->regs = devm_ioremap_resource(&pdev->dev, mem);
@@ -183,8 +183,8 @@ static int pic32_wdt_drv_probe(struct platform_device *pdev)
return PTR_ERR(wdt->regs);
wdt->rst_base = devm_ioremap(&pdev->dev, PIC32_BASE_RESET, 0x10);
- if (IS_ERR(wdt->rst_base))
- return PTR_ERR(wdt->rst_base);
+ if (!wdt->rst_base)
+ return -ENOMEM;
wdt->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(wdt->clk)) {
@@ -251,7 +251,6 @@ static struct platform_driver pic32_wdt_driver = {
.remove = pic32_wdt_drv_remove,
.driver = {
.name = "pic32-wdt",
- .owner = THIS_MODULE,
.of_match_table = of_match_ptr(pic32_wdt_dt_ids),
}
};
diff --git a/drivers/watchdog/qcom-wdt.c b/drivers/watchdog/qcom-wdt.c
index a043fa4f60e5..5796b5d1b3f2 100644
--- a/drivers/watchdog/qcom-wdt.c
+++ b/drivers/watchdog/qcom-wdt.c
@@ -18,19 +18,45 @@
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/watchdog.h>
+#include <linux/of_device.h>
+
+enum wdt_reg {
+ WDT_RST,
+ WDT_EN,
+ WDT_STS,
+ WDT_BARK_TIME,
+ WDT_BITE_TIME,
+};
-#define WDT_RST 0x38
-#define WDT_EN 0x40
-#define WDT_STS 0x44
-#define WDT_BITE_TIME 0x5C
+static const u32 reg_offset_data_apcs_tmr[] = {
+ [WDT_RST] = 0x38,
+ [WDT_EN] = 0x40,
+ [WDT_STS] = 0x44,
+ [WDT_BARK_TIME] = 0x4C,
+ [WDT_BITE_TIME] = 0x5C,
+};
+
+static const u32 reg_offset_data_kpss[] = {
+ [WDT_RST] = 0x4,
+ [WDT_EN] = 0x8,
+ [WDT_STS] = 0xC,
+ [WDT_BARK_TIME] = 0x10,
+ [WDT_BITE_TIME] = 0x14,
+};
struct qcom_wdt {
struct watchdog_device wdd;
struct clk *clk;
unsigned long rate;
void __iomem *base;
+ const u32 *layout;
};
+static void __iomem *wdt_addr(struct qcom_wdt *wdt, enum wdt_reg reg)
+{
+ return wdt->base + wdt->layout[reg];
+}
+
static inline
struct qcom_wdt *to_qcom_wdt(struct watchdog_device *wdd)
{
@@ -41,10 +67,11 @@ static int qcom_wdt_start(struct watchdog_device *wdd)
{
struct qcom_wdt *wdt = to_qcom_wdt(wdd);
- writel(0, wdt->base + WDT_EN);
- writel(1, wdt->base + WDT_RST);
- writel(wdd->timeout * wdt->rate, wdt->base + WDT_BITE_TIME);
- writel(1, wdt->base + WDT_EN);
+ writel(0, wdt_addr(wdt, WDT_EN));
+ writel(1, wdt_addr(wdt, WDT_RST));
+ writel(wdd->timeout * wdt->rate, wdt_addr(wdt, WDT_BARK_TIME));
+ writel(wdd->timeout * wdt->rate, wdt_addr(wdt, WDT_BITE_TIME));
+ writel(1, wdt_addr(wdt, WDT_EN));
return 0;
}
@@ -52,7 +79,7 @@ static int qcom_wdt_stop(struct watchdog_device *wdd)
{
struct qcom_wdt *wdt = to_qcom_wdt(wdd);
- writel(0, wdt->base + WDT_EN);
+ writel(0, wdt_addr(wdt, WDT_EN));
return 0;
}
@@ -60,7 +87,7 @@ static int qcom_wdt_ping(struct watchdog_device *wdd)
{
struct qcom_wdt *wdt = to_qcom_wdt(wdd);
- writel(1, wdt->base + WDT_RST);
+ writel(1, wdt_addr(wdt, WDT_RST));
return 0;
}
@@ -83,10 +110,11 @@ static int qcom_wdt_restart(struct watchdog_device *wdd, unsigned long action,
*/
timeout = 128 * wdt->rate / 1000;
- writel(0, wdt->base + WDT_EN);
- writel(1, wdt->base + WDT_RST);
- writel(timeout, wdt->base + WDT_BITE_TIME);
- writel(1, wdt->base + WDT_EN);
+ writel(0, wdt_addr(wdt, WDT_EN));
+ writel(1, wdt_addr(wdt, WDT_RST));
+ writel(timeout, wdt_addr(wdt, WDT_BARK_TIME));
+ writel(timeout, wdt_addr(wdt, WDT_BITE_TIME));
+ writel(1, wdt_addr(wdt, WDT_EN));
/*
* Actually make sure the above sequence hits hardware before sleeping.
@@ -119,9 +147,16 @@ static int qcom_wdt_probe(struct platform_device *pdev)
struct qcom_wdt *wdt;
struct resource *res;
struct device_node *np = pdev->dev.of_node;
+ const u32 *regs;
u32 percpu_offset;
int ret;
+ regs = of_device_get_match_data(&pdev->dev);
+ if (!regs) {
+ dev_err(&pdev->dev, "Unsupported QCOM WDT module\n");
+ return -ENODEV;
+ }
+
wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL);
if (!wdt)
return -ENOMEM;
@@ -172,6 +207,7 @@ static int qcom_wdt_probe(struct platform_device *pdev)
wdt->wdd.min_timeout = 1;
wdt->wdd.max_timeout = 0x10000000U / wdt->rate;
wdt->wdd.parent = &pdev->dev;
+ wdt->layout = regs;
if (readl(wdt->base + WDT_STS) & 1)
wdt->wdd.bootstatus = WDIOF_CARDRESET;
@@ -208,8 +244,9 @@ static int qcom_wdt_remove(struct platform_device *pdev)
}
static const struct of_device_id qcom_wdt_of_table[] = {
- { .compatible = "qcom,kpss-timer" },
- { .compatible = "qcom,scss-timer" },
+ { .compatible = "qcom,kpss-timer", .data = reg_offset_data_apcs_tmr },
+ { .compatible = "qcom,scss-timer", .data = reg_offset_data_apcs_tmr },
+ { .compatible = "qcom,kpss-wdt", .data = reg_offset_data_kpss },
{ },
};
MODULE_DEVICE_TABLE(of, qcom_wdt_of_table);
diff --git a/drivers/watchdog/sbsa_gwdt.c b/drivers/watchdog/sbsa_gwdt.c
index ad383f6f15fc..ce0c38bd0f00 100644
--- a/drivers/watchdog/sbsa_gwdt.c
+++ b/drivers/watchdog/sbsa_gwdt.c
@@ -180,15 +180,6 @@ static int sbsa_gwdt_keepalive(struct watchdog_device *wdd)
return 0;
}
-static unsigned int sbsa_gwdt_status(struct watchdog_device *wdd)
-{
- struct sbsa_gwdt *gwdt = watchdog_get_drvdata(wdd);
- u32 status = readl(gwdt->control_base + SBSA_GWDT_WCS);
-
- /* is the watchdog timer running? */
- return (status & SBSA_GWDT_WCS_EN) << WDOG_ACTIVE;
-}
-
static int sbsa_gwdt_start(struct watchdog_device *wdd)
{
struct sbsa_gwdt *gwdt = watchdog_get_drvdata(wdd);
@@ -228,7 +219,6 @@ static struct watchdog_ops sbsa_gwdt_ops = {
.owner = THIS_MODULE,
.start = sbsa_gwdt_start,
.stop = sbsa_gwdt_stop,
- .status = sbsa_gwdt_status,
.ping = sbsa_gwdt_keepalive,
.set_timeout = sbsa_gwdt_set_timeout,
.get_timeleft = sbsa_gwdt_get_timeleft,
@@ -273,7 +263,7 @@ static int sbsa_gwdt_probe(struct platform_device *pdev)
wdd->info = &sbsa_gwdt_info;
wdd->ops = &sbsa_gwdt_ops;
wdd->min_timeout = 1;
- wdd->max_timeout = U32_MAX / gwdt->clk;
+ wdd->max_hw_heartbeat_ms = U32_MAX / gwdt->clk * 1000;
wdd->timeout = DEFAULT_TIMEOUT;
watchdog_set_drvdata(wdd, gwdt);
watchdog_set_nowayout(wdd, nowayout);
@@ -283,6 +273,8 @@ static int sbsa_gwdt_probe(struct platform_device *pdev)
dev_warn(dev, "System reset by WDT.\n");
wdd->bootstatus |= WDIOF_CARDRESET;
}
+ if (status & SBSA_GWDT_WCS_EN)
+ set_bit(WDOG_HW_RUNNING, &wdd->status);
if (action) {
irq = platform_get_irq(pdev, 0);
@@ -310,7 +302,7 @@ static int sbsa_gwdt_probe(struct platform_device *pdev)
* the timeout is (WOR * 2), so the maximum timeout should be doubled.
*/
if (!action)
- wdd->max_timeout *= 2;
+ wdd->max_hw_heartbeat_ms *= 2;
watchdog_init_timeout(wdd, timeout, dev);
/*
diff --git a/drivers/watchdog/sirfsoc_wdt.c b/drivers/watchdog/sirfsoc_wdt.c
index d0578ab2e636..3050a0031479 100644
--- a/drivers/watchdog/sirfsoc_wdt.c
+++ b/drivers/watchdog/sirfsoc_wdt.c
@@ -39,13 +39,18 @@ MODULE_PARM_DESC(timeout, "Default watchdog timeout (in seconds)");
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+static void __iomem *sirfsoc_wdt_base(struct watchdog_device *wdd)
+{
+ return (void __iomem __force *)watchdog_get_drvdata(wdd);
+}
+
static unsigned int sirfsoc_wdt_gettimeleft(struct watchdog_device *wdd)
{
u32 counter, match;
void __iomem *wdt_base;
int time_left;
- wdt_base = watchdog_get_drvdata(wdd);
+ wdt_base = sirfsoc_wdt_base(wdd);
counter = readl(wdt_base + SIRFSOC_TIMER_COUNTER_LO);
match = readl(wdt_base +
SIRFSOC_TIMER_MATCH_0 + (SIRFSOC_TIMER_WDT_INDEX << 2));
@@ -61,7 +66,7 @@ static int sirfsoc_wdt_updatetimeout(struct watchdog_device *wdd)
void __iomem *wdt_base;
timeout_ticks = wdd->timeout * CLOCK_FREQ;
- wdt_base = watchdog_get_drvdata(wdd);
+ wdt_base = sirfsoc_wdt_base(wdd);
/* Enable the latch before reading the LATCH_LO register */
writel(1, wdt_base + SIRFSOC_TIMER_LATCH);
@@ -79,7 +84,7 @@ static int sirfsoc_wdt_updatetimeout(struct watchdog_device *wdd)
static int sirfsoc_wdt_enable(struct watchdog_device *wdd)
{
- void __iomem *wdt_base = watchdog_get_drvdata(wdd);
+ void __iomem *wdt_base = sirfsoc_wdt_base(wdd);
sirfsoc_wdt_updatetimeout(wdd);
/*
@@ -96,7 +101,7 @@ static int sirfsoc_wdt_enable(struct watchdog_device *wdd)
static int sirfsoc_wdt_disable(struct watchdog_device *wdd)
{
- void __iomem *wdt_base = watchdog_get_drvdata(wdd);
+ void __iomem *wdt_base = sirfsoc_wdt_base(wdd);
writel(0, wdt_base + SIRFSOC_TIMER_WATCHDOG_EN);
writel(readl(wdt_base + SIRFSOC_TIMER_INT_EN)
@@ -150,7 +155,7 @@ static int sirfsoc_wdt_probe(struct platform_device *pdev)
if (IS_ERR(base))
return PTR_ERR(base);
- watchdog_set_drvdata(&sirfsoc_wdd, base);
+ watchdog_set_drvdata(&sirfsoc_wdd, (__force void *)base);
watchdog_init_timeout(&sirfsoc_wdd, timeout, &pdev->dev);
watchdog_set_nowayout(&sirfsoc_wdd, nowayout);
diff --git a/drivers/watchdog/softdog.c b/drivers/watchdog/softdog.c
index 99a06f9e3930..b067edf246df 100644
--- a/drivers/watchdog/softdog.c
+++ b/drivers/watchdog/softdog.c
@@ -17,36 +17,19 @@
*
* Software only watchdog driver. Unlike its big brother the WDT501P
* driver this won't always recover a failed machine.
- *
- * 03/96: Angelo Haritsis <ah@doc.ic.ac.uk> :
- * Modularised.
- * Added soft_margin; use upon insmod to change the timer delay.
- * NB: uses same minor as wdt (WATCHDOG_MINOR); we could use separate
- * minors.
- *
- * 19980911 Alan Cox
- * Made SMP safe for 2.3.x
- *
- * 20011127 Joel Becker (jlbec@evilplan.org>
- * Added soft_noboot; Allows testing the softdog trigger without
- * requiring a recompile.
- * Added WDIOC_GETTIMEOUT and WDIOC_SETTIMOUT.
- *
- * 20020530 Joel Becker <joel.becker@oracle.com>
- * Added Matt Domsch's nowayout module option.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include <linux/init.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
-#include <linux/types.h>
+#include <linux/reboot.h>
#include <linux/timer.h>
+#include <linux/types.h>
#include <linux/watchdog.h>
-#include <linux/reboot.h>
-#include <linux/init.h>
-#include <linux/jiffies.h>
-#include <linux/kernel.h>
#define TIMER_MARGIN 60 /* Default is 60 seconds */
static unsigned int soft_margin = TIMER_MARGIN; /* in seconds */
@@ -71,25 +54,12 @@ module_param(soft_panic, int, 0);
MODULE_PARM_DESC(soft_panic,
"Softdog action, set to 1 to panic, 0 to reboot (default=0)");
-/*
- * Our timer
- */
-
-static void watchdog_fire(unsigned long);
-
-static struct timer_list watchdog_ticktock =
- TIMER_INITIALIZER(watchdog_fire, 0, 0);
-
-/*
- * If the timer expires..
- */
-
-static void watchdog_fire(unsigned long data)
+static void softdog_fire(unsigned long data)
{
module_put(THIS_MODULE);
- if (soft_noboot)
+ if (soft_noboot) {
pr_crit("Triggered - Reboot ignored\n");
- else if (soft_panic) {
+ } else if (soft_panic) {
pr_crit("Initiating panic\n");
panic("Software Watchdog Timer expired");
} else {
@@ -99,35 +69,24 @@ static void watchdog_fire(unsigned long data)
}
}
-/*
- * Softdog operations
- */
+static struct timer_list softdog_ticktock =
+ TIMER_INITIALIZER(softdog_fire, 0, 0);
static int softdog_ping(struct watchdog_device *w)
{
- if (!mod_timer(&watchdog_ticktock, jiffies+(w->timeout*HZ)))
+ if (!mod_timer(&softdog_ticktock, jiffies + (w->timeout * HZ)))
__module_get(THIS_MODULE);
return 0;
}
static int softdog_stop(struct watchdog_device *w)
{
- if (del_timer(&watchdog_ticktock))
+ if (del_timer(&softdog_ticktock))
module_put(THIS_MODULE);
return 0;
}
-static int softdog_set_timeout(struct watchdog_device *w, unsigned int t)
-{
- w->timeout = t;
- return 0;
-}
-
-/*
- * Kernel Interfaces
- */
-
static struct watchdog_info softdog_info = {
.identity = "Software Watchdog",
.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
@@ -137,29 +96,21 @@ static struct watchdog_ops softdog_ops = {
.owner = THIS_MODULE,
.start = softdog_ping,
.stop = softdog_stop,
- .set_timeout = softdog_set_timeout,
};
static struct watchdog_device softdog_dev = {
.info = &softdog_info,
.ops = &softdog_ops,
.min_timeout = 1,
- .max_timeout = 0xFFFF
+ .max_timeout = 65535,
+ .timeout = TIMER_MARGIN,
};
-static int __init watchdog_init(void)
+static int __init softdog_init(void)
{
int ret;
- /* Check that the soft_margin value is within it's range;
- if not reset to the default */
- if (soft_margin < 1 || soft_margin > 65535) {
- pr_info("soft_margin must be 0 < soft_margin < 65536, using %d\n",
- TIMER_MARGIN);
- return -EINVAL;
- }
- softdog_dev.timeout = soft_margin;
-
+ watchdog_init_timeout(&softdog_dev, soft_margin, NULL);
watchdog_set_nowayout(&softdog_dev, nowayout);
watchdog_stop_on_reboot(&softdog_dev);
@@ -167,19 +118,18 @@ static int __init watchdog_init(void)
if (ret)
return ret;
- pr_info("Software Watchdog Timer: 0.08 initialized. soft_noboot=%d soft_margin=%d sec soft_panic=%d (nowayout=%d)\n",
- soft_noboot, soft_margin, soft_panic, nowayout);
+ pr_info("initialized. soft_noboot=%d soft_margin=%d sec soft_panic=%d (nowayout=%d)\n",
+ soft_noboot, softdog_dev.timeout, soft_panic, nowayout);
return 0;
}
+module_init(softdog_init);
-static void __exit watchdog_exit(void)
+static void __exit softdog_exit(void)
{
watchdog_unregister_device(&softdog_dev);
}
-
-module_init(watchdog_init);
-module_exit(watchdog_exit);
+module_exit(softdog_exit);
MODULE_AUTHOR("Alan Cox");
MODULE_DESCRIPTION("Software Watchdog Device Driver");
diff --git a/drivers/watchdog/tangox_wdt.c b/drivers/watchdog/tangox_wdt.c
index cfbed7e051b6..202c4b9cc921 100644
--- a/drivers/watchdog/tangox_wdt.c
+++ b/drivers/watchdog/tangox_wdt.c
@@ -149,7 +149,7 @@ static int tangox_wdt_probe(struct platform_device *pdev)
dev->wdt.ops = &tangox_wdt_ops;
dev->wdt.timeout = DEFAULT_TIMEOUT;
dev->wdt.min_timeout = 1;
- dev->wdt.max_timeout = (U32_MAX - 1) / dev->clk_rate;
+ dev->wdt.max_hw_heartbeat_ms = (U32_MAX - 1) / dev->clk_rate;
watchdog_init_timeout(&dev->wdt, timeout, &pdev->dev);
watchdog_set_nowayout(&dev->wdt, nowayout);
@@ -170,7 +170,7 @@ static int tangox_wdt_probe(struct platform_device *pdev)
* already running.
*/
if (readl(dev->base + WD_COUNTER)) {
- set_bit(WDOG_ACTIVE, &dev->wdt.status);
+ set_bit(WDOG_HW_RUNNING, &dev->wdt.status);
tangox_wdt_start(&dev->wdt);
}
diff --git a/drivers/watchdog/watchdog_core.c b/drivers/watchdog/watchdog_core.c
index 7c3ba58ae1be..6abb83cd7681 100644
--- a/drivers/watchdog/watchdog_core.c
+++ b/drivers/watchdog/watchdog_core.c
@@ -88,7 +88,7 @@ static void watchdog_check_min_max_timeout(struct watchdog_device *wdd)
* Check that we have valid min and max timeout values, if
* not reset them both to 0 (=not used or unknown)
*/
- if (wdd->min_timeout > wdd->max_timeout) {
+ if (!wdd->max_hw_heartbeat_ms && wdd->min_timeout > wdd->max_timeout) {
pr_info("Invalid min and max timeout values, resetting to 0!\n");
wdd->min_timeout = 0;
wdd->max_timeout = 0;
@@ -329,6 +329,43 @@ void watchdog_unregister_device(struct watchdog_device *wdd)
EXPORT_SYMBOL_GPL(watchdog_unregister_device);
+static void devm_watchdog_unregister_device(struct device *dev, void *res)
+{
+ watchdog_unregister_device(*(struct watchdog_device **)res);
+}
+
+/**
+ * devm_watchdog_register_device() - resource managed watchdog_register_device()
+ * @dev: device that is registering this watchdog device
+ * @wdd: watchdog device
+ *
+ * Managed watchdog_register_device(). For watchdog device registered by this
+ * function, watchdog_unregister_device() is automatically called on driver
+ * detach. See watchdog_register_device() for more information.
+ */
+int devm_watchdog_register_device(struct device *dev,
+ struct watchdog_device *wdd)
+{
+ struct watchdog_device **rcwdd;
+ int ret;
+
+ rcwdd = devres_alloc(devm_watchdog_unregister_device, sizeof(*wdd),
+ GFP_KERNEL);
+ if (!rcwdd)
+ return -ENOMEM;
+
+ ret = watchdog_register_device(wdd);
+ if (!ret) {
+ *rcwdd = wdd;
+ devres_add(dev, rcwdd);
+ } else {
+ devres_free(rcwdd);
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(devm_watchdog_register_device);
+
static int __init watchdog_deferred_registration(void)
{
mutex_lock(&wtd_deferred_reg_mutex);
diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c
index 3595cffa24ea..040bf8382f46 100644
--- a/drivers/watchdog/watchdog_dev.c
+++ b/drivers/watchdog/watchdog_dev.c
@@ -69,6 +69,7 @@ struct watchdog_core_data {
unsigned long status; /* Internal status bits */
#define _WDOG_DEV_OPEN 0 /* Opened ? */
#define _WDOG_ALLOW_RELEASE 1 /* Did we receive the magic char ? */
+#define _WDOG_KEEPALIVE 2 /* Did we receive a keepalive ? */
};
/* the dev_t structure to store the dynamically allocated watchdog devices */
@@ -92,9 +93,13 @@ static inline bool watchdog_need_worker(struct watchdog_device *wdd)
* thus is aware that the framework supports generating heartbeat
* requests.
* - Userspace requests a longer timeout than the hardware can handle.
+ *
+ * Alternatively, if userspace has not opened the watchdog
+ * device, we take care of feeding the watchdog if it is
+ * running.
*/
- return hm && ((watchdog_active(wdd) && t > hm) ||
- (t && !watchdog_active(wdd) && watchdog_hw_running(wdd)));
+ return (hm && watchdog_active(wdd) && t > hm) ||
+ (t && !watchdog_active(wdd) && watchdog_hw_running(wdd));
}
static long watchdog_next_keepalive(struct watchdog_device *wdd)
@@ -107,7 +112,7 @@ static long watchdog_next_keepalive(struct watchdog_device *wdd)
unsigned int hw_heartbeat_ms;
virt_timeout = wd_data->last_keepalive + msecs_to_jiffies(timeout_ms);
- hw_heartbeat_ms = min(timeout_ms, wdd->max_hw_heartbeat_ms);
+ hw_heartbeat_ms = min_not_zero(timeout_ms, wdd->max_hw_heartbeat_ms);
keepalive_interval = msecs_to_jiffies(hw_heartbeat_ms / 2);
if (!watchdog_active(wdd))
@@ -180,6 +185,8 @@ static int watchdog_ping(struct watchdog_device *wdd)
if (!watchdog_active(wdd) && !watchdog_hw_running(wdd))
return 0;
+ set_bit(_WDOG_KEEPALIVE, &wd_data->status);
+
wd_data->last_keepalive = jiffies;
return __watchdog_ping(wdd);
}
@@ -219,6 +226,8 @@ static int watchdog_start(struct watchdog_device *wdd)
if (watchdog_active(wdd))
return 0;
+ set_bit(_WDOG_KEEPALIVE, &wd_data->status);
+
started_at = jiffies;
if (watchdog_hw_running(wdd) && wdd->ops->ping)
err = wdd->ops->ping(wdd);
@@ -258,10 +267,12 @@ static int watchdog_stop(struct watchdog_device *wdd)
return -EBUSY;
}
- if (wdd->ops->stop)
+ if (wdd->ops->stop) {
+ clear_bit(WDOG_HW_RUNNING, &wdd->status);
err = wdd->ops->stop(wdd);
- else
+ } else {
set_bit(WDOG_HW_RUNNING, &wdd->status);
+ }
if (err == 0) {
clear_bit(WDOG_ACTIVE, &wdd->status);
@@ -282,10 +293,27 @@ static int watchdog_stop(struct watchdog_device *wdd)
static unsigned int watchdog_get_status(struct watchdog_device *wdd)
{
- if (!wdd->ops->status)
- return 0;
+ struct watchdog_core_data *wd_data = wdd->wd_data;
+ unsigned int status;
+
+ if (wdd->ops->status)
+ status = wdd->ops->status(wdd);
+ else
+ status = wdd->bootstatus & (WDIOF_CARDRESET |
+ WDIOF_OVERHEAT |
+ WDIOF_FANFAULT |
+ WDIOF_EXTERN1 |
+ WDIOF_EXTERN2 |
+ WDIOF_POWERUNDER |
+ WDIOF_POWEROVER);
- return wdd->ops->status(wdd);
+ if (test_bit(_WDOG_ALLOW_RELEASE, &wd_data->status))
+ status |= WDIOF_MAGICCLOSE;
+
+ if (test_and_clear_bit(_WDOG_KEEPALIVE, &wd_data->status))
+ status |= WDIOF_KEEPALIVEPING;
+
+ return status;
}
/*
@@ -361,7 +389,7 @@ static ssize_t status_show(struct device *dev, struct device_attribute *attr,
status = watchdog_get_status(wdd);
mutex_unlock(&wd_data->lock);
- return sprintf(buf, "%u\n", status);
+ return sprintf(buf, "0x%x\n", status);
}
static DEVICE_ATTR_RO(status);
@@ -429,9 +457,7 @@ static umode_t wdt_is_visible(struct kobject *kobj, struct attribute *attr,
struct watchdog_device *wdd = dev_get_drvdata(dev);
umode_t mode = attr->mode;
- if (attr == &dev_attr_status.attr && !wdd->ops->status)
- mode = 0;
- else if (attr == &dev_attr_timeleft.attr && !wdd->ops->get_timeleft)
+ if (attr == &dev_attr_timeleft.attr && !wdd->ops->get_timeleft)
mode = 0;
return mode;
@@ -948,17 +974,22 @@ int __init watchdog_dev_init(void)
err = class_register(&watchdog_class);
if (err < 0) {
pr_err("couldn't register class\n");
- return err;
+ goto err_register;
}
err = alloc_chrdev_region(&watchdog_devt, 0, MAX_DOGS, "watchdog");
if (err < 0) {
pr_err("watchdog: unable to allocate char dev region\n");
- class_unregister(&watchdog_class);
- return err;
+ goto err_alloc;
}
return 0;
+
+err_alloc:
+ class_unregister(&watchdog_class);
+err_register:
+ destroy_workqueue(watchdog_wq);
+ return err;
}
/*
diff --git a/drivers/watchdog/ziirave_wdt.c b/drivers/watchdog/ziirave_wdt.c
index cbe373de3659..fa1efef3c96e 100644
--- a/drivers/watchdog/ziirave_wdt.c
+++ b/drivers/watchdog/ziirave_wdt.c
@@ -339,7 +339,7 @@ static int ziirave_wdt_remove(struct i2c_client *client)
}
static struct i2c_device_id ziirave_wdt_id[] = {
- { "ziirave-wdt", 0 },
+ { "rave-wdt", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, ziirave_wdt_id);
diff --git a/include/linux/watchdog.h b/include/linux/watchdog.h
index 51732d6c9555..7047bc7f8106 100644
--- a/include/linux/watchdog.h
+++ b/include/linux/watchdog.h
@@ -66,7 +66,8 @@ struct watchdog_ops {
* as configurable from user space. Only relevant if
* max_hw_heartbeat_ms is not provided.
* @min_hw_heartbeat_ms:
- * Minimum time between heartbeats, in milli-seconds.
+ * Hardware limit for minimum time between heartbeats,
+ * in milli-seconds.
* @max_hw_heartbeat_ms:
* Hardware limit for maximum timeout, in milli-seconds.
* Replaces max_timeout if specified.
@@ -180,4 +181,7 @@ extern int watchdog_init_timeout(struct watchdog_device *wdd,
extern int watchdog_register_device(struct watchdog_device *);
extern void watchdog_unregister_device(struct watchdog_device *);
+/* devres register variant */
+int devm_watchdog_register_device(struct device *dev, struct watchdog_device *);
+
#endif /* ifndef _LINUX_WATCHDOG_H */