Commit 27acbec3 authored by Linus Torvalds's avatar Linus Torvalds

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
  ...
parents ba929b66 1ac06563
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>;
};
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>;
};
...@@ -7,6 +7,10 @@ Required properties : ...@@ -7,6 +7,10 @@ Required properties :
"qcom,kpss-wdt-msm8960" "qcom,kpss-wdt-msm8960"
"qcom,kpss-wdt-apq8064" "qcom,kpss-wdt-apq8064"
"qcom,kpss-wdt-ipq8064" "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 - reg : shall contain base register location and length
- clocks : shall contain the input clock - clocks : shall contain the input clock
......
Renesas Watchdog Timer (WDT) Controller Renesas Watchdog Timer (WDT) Controller
Required properties: 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 When compatible with the generic version, nodes must list the SoC-specific
version corresponding to the platform first, followed by the generic version corresponding to the platform first, followed by the generic
......
...@@ -357,3 +357,6 @@ SLAVE DMA ENGINE ...@@ -357,3 +357,6 @@ SLAVE DMA ENGINE
SPI SPI
devm_spi_register_master() devm_spi_register_master()
WATCHDOG
devm_watchdog_register_device()
Last reviewed: 04/04/2016 Last reviewed: 05/20/2016
HPE iLO NMI Watchdog Driver HPE iLO NMI Watchdog Driver
NMI sourcing for iLO based ProLiant Servers NMI sourcing for iLO based ProLiant Servers
Documentation and Driver by 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 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 added benefit of NMI sourcing. Both the
...@@ -95,4 +95,3 @@ Last reviewed: 04/04/2016 ...@@ -95,4 +95,3 @@ Last reviewed: 04/04/2016
-- Tom Mingarelli -- Tom Mingarelli
(thomas.mingarelli@hpe.com)
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
* Watchdog Driver Test Program * Watchdog Driver Test Program
*/ */
#include <errno.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
...@@ -13,6 +14,7 @@ ...@@ -13,6 +14,7 @@
#include <linux/watchdog.h> #include <linux/watchdog.h>
int fd; int fd;
const char v = 'V';
/* /*
* This function simply sends an IOCTL to the driver, which in turn ticks * This function simply sends an IOCTL to the driver, which in turn ticks
...@@ -23,6 +25,7 @@ static void keep_alive(void) ...@@ -23,6 +25,7 @@ static void keep_alive(void)
{ {
int dummy; int dummy;
printf(".");
ioctl(fd, WDIOC_KEEPALIVE, &dummy); ioctl(fd, WDIOC_KEEPALIVE, &dummy);
} }
...@@ -33,8 +36,13 @@ static void keep_alive(void) ...@@ -33,8 +36,13 @@ static void keep_alive(void)
static void term(int sig) static void term(int sig)
{ {
int ret = write(fd, &v, 1);
close(fd); 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); exit(0);
} }
...@@ -42,12 +50,14 @@ int main(int argc, char *argv[]) ...@@ -42,12 +50,14 @@ int main(int argc, char *argv[])
{ {
int flags; int flags;
unsigned int ping_rate = 1; unsigned int ping_rate = 1;
int ret;
setbuf(stdout, NULL);
fd = open("/dev/watchdog", O_WRONLY); fd = open("/dev/watchdog", O_WRONLY);
if (fd == -1) { if (fd == -1) {
fprintf(stderr, "Watchdog device not enabled.\n"); printf("Watchdog device not enabled.\n");
fflush(stderr);
exit(-1); exit(-1);
} }
...@@ -55,36 +65,30 @@ int main(int argc, char *argv[]) ...@@ -55,36 +65,30 @@ int main(int argc, char *argv[])
if (!strncasecmp(argv[1], "-d", 2)) { if (!strncasecmp(argv[1], "-d", 2)) {
flags = WDIOS_DISABLECARD; flags = WDIOS_DISABLECARD;
ioctl(fd, WDIOC_SETOPTIONS, &flags); ioctl(fd, WDIOC_SETOPTIONS, &flags);
fprintf(stderr, "Watchdog card disabled.\n"); printf("Watchdog card disabled.\n");
fflush(stderr);
goto end; goto end;
} else if (!strncasecmp(argv[1], "-e", 2)) { } else if (!strncasecmp(argv[1], "-e", 2)) {
flags = WDIOS_ENABLECARD; flags = WDIOS_ENABLECARD;
ioctl(fd, WDIOC_SETOPTIONS, &flags); ioctl(fd, WDIOC_SETOPTIONS, &flags);
fprintf(stderr, "Watchdog card enabled.\n"); printf("Watchdog card enabled.\n");
fflush(stderr);
goto end; goto end;
} else if (!strncasecmp(argv[1], "-t", 2) && argv[2]) { } else if (!strncasecmp(argv[1], "-t", 2) && argv[2]) {
flags = atoi(argv[2]); flags = atoi(argv[2]);
ioctl(fd, WDIOC_SETTIMEOUT, &flags); ioctl(fd, WDIOC_SETTIMEOUT, &flags);
fprintf(stderr, "Watchdog timeout set to %u seconds.\n", flags); printf("Watchdog timeout set to %u seconds.\n", flags);
fflush(stderr);
goto end; goto end;
} else if (!strncasecmp(argv[1], "-p", 2) && argv[2]) { } else if (!strncasecmp(argv[1], "-p", 2) && argv[2]) {
ping_rate = strtoul(argv[2], NULL, 0); ping_rate = strtoul(argv[2], NULL, 0);
fprintf(stderr, "Watchdog ping rate set to %u seconds.\n", ping_rate); printf("Watchdog ping rate set to %u seconds.\n", ping_rate);
fflush(stderr);
} else { } 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"); "the timeout,\n-p <n> to set the ping rate, and \n");
fprintf(stderr, "run by itself to tick the card.\n"); printf("run by itself to tick the card.\n");
fflush(stderr);
goto end; goto end;
} }
} }
fprintf(stderr, "Watchdog Ticking Away!\n"); printf("Watchdog Ticking Away!\n");
fflush(stderr);
signal(SIGINT, term); signal(SIGINT, term);
...@@ -93,6 +97,9 @@ int main(int argc, char *argv[]) ...@@ -93,6 +97,9 @@ int main(int argc, char *argv[])
sleep(ping_rate); sleep(ping_rate);
} }
end: end:
ret = write(fd, &v, 1);
if (ret < 0)
printf("Stopping watchdog ticks failed (%d)...\n", errno);
close(fd); close(fd);
return 0; return 0;
} }
...@@ -82,8 +82,9 @@ It contains following fields: ...@@ -82,8 +82,9 @@ It contains following fields:
* max_timeout: the watchdog timer's maximum timeout value (in seconds), * max_timeout: the watchdog timer's maximum timeout value (in seconds),
as seen from userspace. If set, the maximum configurable value for as seen from userspace. If set, the maximum configurable value for
'timeout'. Not used if max_hw_heartbeat_ms is non-zero. 'timeout'. Not used if max_hw_heartbeat_ms is non-zero.
* min_hw_heartbeat_ms: Minimum time between heartbeats sent to the chip, * min_hw_heartbeat_ms: Hardware limit for minimum time between heartbeats,
in milli-seconds. 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. * max_hw_heartbeat_ms: Maximum hardware heartbeat, in milli-seconds.
If set, the infrastructure will send heartbeats to the watchdog driver If set, the infrastructure will send heartbeats to the watchdog driver
if 'timeout' is larger than max_hw_heartbeat_ms, unless WDOG_ACTIVE if 'timeout' is larger than max_hw_heartbeat_ms, unless WDOG_ACTIVE
...@@ -166,6 +167,10 @@ they are supported. These optional routines/operations are: ...@@ -166,6 +167,10 @@ they are supported. These optional routines/operations are:
info structure). info structure).
* status: this routine checks the status of the watchdog timer device. The * status: this routine checks the status of the watchdog timer device. The
status of the device is reported with watchdog WDIOF_* status flags/bits. 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 * 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" 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 and -EIO for "could not write value to the watchdog". On success this
......
...@@ -5353,6 +5353,12 @@ T: git git://linuxtv.org/anttip/media_tree.git ...@@ -5353,6 +5353,12 @@ T: git git://linuxtv.org/anttip/media_tree.git
S: Maintained S: Maintained
F: drivers/media/dvb-frontends/hd29l2* 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) HEWLETT-PACKARD SMART ARRAY RAID DRIVER (hpsa)
M: Don Brace <don.brace@microsemi.com> M: Don Brace <don.brace@microsemi.com>
L: iss_storagedev@hp.com L: iss_storagedev@hp.com
......
...@@ -247,7 +247,8 @@ intc: interrupt-controller@2000000 { ...@@ -247,7 +247,8 @@ intc: interrupt-controller@2000000 {
}; };
timer@200a000 { timer@200a000 {
compatible = "qcom,kpss-timer", "qcom,msm-timer"; compatible = "qcom,kpss-timer",
"qcom,kpss-wdt-apq8064", "qcom,msm-timer";
interrupts = <1 1 0x301>, interrupts = <1 1 0x301>,
<1 2 0x301>, <1 2 0x301>,
<1 3 0x301>; <1 3 0x301>;
......
...@@ -252,7 +252,7 @@ serial@78b0000 { ...@@ -252,7 +252,7 @@ serial@78b0000 {
}; };
watchdog@b017000 { watchdog@b017000 {
compatible = "qcom,kpss-standalone"; compatible = "qcom,kpss-wdt", "qcom,kpss-wdt-ipq4019";
reg = <0xb017000 0x40>; reg = <0xb017000 0x40>;
clocks = <&sleep_clk>; clocks = <&sleep_clk>;
timeout-sec = <10>; timeout-sec = <10>;
......
...@@ -122,7 +122,8 @@ intc: interrupt-controller@2000000 { ...@@ -122,7 +122,8 @@ intc: interrupt-controller@2000000 {
}; };
timer@200a000 { timer@200a000 {
compatible = "qcom,kpss-timer", "qcom,msm-timer"; compatible = "qcom,kpss-timer",
"qcom,kpss-wdt-ipq8064", "qcom,msm-timer";
interrupts = <1 1 0x301>, interrupts = <1 1 0x301>,
<1 2 0x301>, <1 2 0x301>,
<1 3 0x301>, <1 3 0x301>,
......
...@@ -87,7 +87,8 @@ intc: interrupt-controller@2000000 { ...@@ -87,7 +87,8 @@ intc: interrupt-controller@2000000 {
}; };
timer@200a000 { timer@200a000 {
compatible = "qcom,kpss-timer", "qcom,msm-timer"; compatible = "qcom,kpss-timer",
"qcom,kpss-wdt-msm8960", "qcom,msm-timer";
interrupts = <1 1 0x301>, interrupts = <1 1 0x301>,
<1 2 0x301>, <1 2 0x301>,
<1 3 0x301>; <1 3 0x301>;
......
...@@ -85,7 +85,7 @@ ...@@ -85,7 +85,7 @@
* platform device and to export resources for those functions. * platform device and to export resources for those functions.
*/ */
#define TCO_DEVICE_NAME "iTCO_wdt" #define TCO_DEVICE_NAME "iTCO_wdt"
#define SMI_EN_OFFSET 0x30 #define SMI_EN_OFFSET 0x40
#define SMI_EN_SIZE 4 #define SMI_EN_SIZE 4
#define TCO_BASE_OFFSET 0x60 #define TCO_BASE_OFFSET 0x60
#define TCO_REGS_SIZE 16 #define TCO_REGS_SIZE 16
...@@ -94,6 +94,8 @@ ...@@ -94,6 +94,8 @@
#define TELEM_SSRAM_SIZE 240 #define TELEM_SSRAM_SIZE 240
#define TELEM_PMC_SSRAM_OFFSET 0x1B00 #define TELEM_PMC_SSRAM_OFFSET 0x1B00
#define TELEM_PUNIT_SSRAM_OFFSET 0x1A00 #define TELEM_PUNIT_SSRAM_OFFSET 0x1A00
#define TCO_PMC_OFFSET 0x8
#define TCO_PMC_SIZE 0x4
static const int iTCO_version = 3; static const int iTCO_version = 3;
...@@ -502,7 +504,7 @@ static struct resource tco_res[] = { ...@@ -502,7 +504,7 @@ static struct resource tco_res[] = {
static struct itco_wdt_platform_data tco_info = { static struct itco_wdt_platform_data tco_info = {
.name = "Apollo Lake SoC", .name = "Apollo Lake SoC",
.version = 3, .version = 5,
}; };
#define TELEMETRY_RESOURCE_PUNIT_SSRAM 0 #define TELEMETRY_RESOURCE_PUNIT_SSRAM 0
...@@ -572,8 +574,8 @@ static int ipc_create_tco_device(void) ...@@ -572,8 +574,8 @@ static int ipc_create_tco_device(void)
res->end = res->start + SMI_EN_SIZE - 1; res->end = res->start + SMI_EN_SIZE - 1;
res = tco_res + TCO_RESOURCE_GCR_MEM; res = tco_res + TCO_RESOURCE_GCR_MEM;
res->start = ipcdev.gcr_base; res->start = ipcdev.gcr_base + TCO_PMC_OFFSET;
res->end = res->start + ipcdev.gcr_size - 1; res->end = res->start + TCO_PMC_SIZE - 1;
ret = platform_device_add_resources(pdev, tco_res, ARRAY_SIZE(tco_res)); ret = platform_device_add_resources(pdev, tco_res, ARRAY_SIZE(tco_res));
if (ret) { if (ret) {
......
...@@ -48,7 +48,6 @@ config WATCHDOG_NOWAYOUT ...@@ -48,7 +48,6 @@ config WATCHDOG_NOWAYOUT
config WATCHDOG_SYSFS config WATCHDOG_SYSFS
bool "Read different watchdog information through sysfs" bool "Read different watchdog information through sysfs"
default n
help help
Say Y here if you want to enable watchdog device status read through Say Y here if you want to enable watchdog device status read through
sysfs attributes. sysfs attributes.
...@@ -516,6 +515,15 @@ config MAX63XX_WATCHDOG ...@@ -516,6 +515,15 @@ config MAX63XX_WATCHDOG
help help
Support for memory mapped max63{69,70,71,72,73,74} watchdog timer. 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 config IMX2_WDT
tristate "IMX2+ Watchdog" tristate "IMX2+ Watchdog"
depends on ARCH_MXC || ARCH_LAYERSCAPE depends on ARCH_MXC || ARCH_LAYERSCAPE
...@@ -609,6 +617,16 @@ config QCOM_WDT ...@@ -609,6 +617,16 @@ config QCOM_WDT
To compile this driver as a module, choose M here: the To compile this driver as a module, choose M here: the
module will be called qcom_wdt. 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 config MESON_WATCHDOG
tristate "Amlogic Meson SoCs watchdog support" tristate "Amlogic Meson SoCs watchdog support"
depends on ARCH_MESON depends on ARCH_MESON
...@@ -669,6 +687,19 @@ config RENESAS_WDT ...@@ -669,6 +687,19 @@ config RENESAS_WDT
This driver adds watchdog support for the integrated watchdogs in the This driver adds watchdog support for the integrated watchdogs in the
Renesas R-Car and other SH-Mobile SoCs (usually named RWDT or SWDT). 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 # AVR32 Architecture
config AT32AP700X_WDT config AT32AP700X_WDT
......
...@@ -67,6 +67,7 @@ obj-$(CONFIG_ST_LPC_WATCHDOG) += st_lpc_wdt.o ...@@ -67,6 +67,7 @@ obj-$(CONFIG_ST_LPC_WATCHDOG) += st_lpc_wdt.o
obj-$(CONFIG_QCOM_WDT) += qcom-wdt.o obj-$(CONFIG_QCOM_WDT) += qcom-wdt.o
obj-$(CONFIG_BCM_KONA_WDT) += bcm_kona_wdt.o obj-$(CONFIG_BCM_KONA_WDT) += bcm_kona_wdt.o
obj-$(CONFIG_TEGRA_WATCHDOG) += tegra_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_MESON_WATCHDOG) += meson_wdt.o
obj-$(CONFIG_MEDIATEK_WATCHDOG) += mtk_wdt.o obj-$(CONFIG_MEDIATEK_WATCHDOG) += mtk_wdt.o
obj-$(CONFIG_DIGICOLOR_WATCHDOG) += digicolor_wdt.o obj-$(CONFIG_DIGICOLOR_WATCHDOG) += digicolor_wdt.o
...@@ -74,6 +75,7 @@ obj-$(CONFIG_LPC18XX_WATCHDOG) += lpc18xx_wdt.o ...@@ -74,6 +75,7 @@ obj-$(CONFIG_LPC18XX_WATCHDOG) += lpc18xx_wdt.o
obj-$(CONFIG_BCM7038_WDT) += bcm7038_wdt.o obj-$(CONFIG_BCM7038_WDT) += bcm7038_wdt.o
obj-$(CONFIG_ATLAS7_WATCHDOG) += atlas7_wdt.o obj-$(CONFIG_ATLAS7_WATCHDOG) += atlas7_wdt.o
obj-$(CONFIG_RENESAS_WDT) += renesas_wdt.o obj-$(CONFIG_RENESAS_WDT) += renesas_wdt.o
obj-$(CONFIG_ASPEED_WATCHDOG) += aspeed_wdt.o
# AVR32 Architecture # AVR32 Architecture
obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o
...@@ -203,6 +205,7 @@ obj-$(CONFIG_TANGOX_WATCHDOG) += tangox_wdt.o ...@@ -203,6 +205,7 @@ obj-$(CONFIG_TANGOX_WATCHDOG) += tangox_wdt.o
obj-$(CONFIG_WM831X_WATCHDOG) += wm831x_wdt.o obj-$(CONFIG_WM831X_WATCHDOG) += wm831x_wdt.o
obj-$(CONFIG_WM8350_WATCHDOG) += wm8350_wdt.o obj-$(CONFIG_WM8350_WATCHDOG) += wm8350_wdt.o
obj-$(CONFIG_MAX63XX_WATCHDOG) += max63xx_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_ZIIRAVE_WATCHDOG) += ziirave_wdt.o
obj-$(CONFIG_SOFT_WATCHDOG) += softdog.o obj-$(CONFIG_SOFT_WATCHDOG) += softdog.o
obj-$(CONFIG_MENF21BMC_WATCHDOG) += menf21bmc_wdt.o obj-$(CONFIG_MENF21BMC_WATCHDOG) += menf21bmc_wdt.o
/*
* 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");
...@@ -82,12 +82,6 @@ static int bcm2835_wdt_stop(struct watchdog_device *wdog) ...@@ -82,12 +82,6 @@ static int bcm2835_wdt_stop(struct watchdog_device *wdog)
return 0; 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) static unsigned int bcm2835_wdt_get_timeleft(struct watchdog_device *wdog)
{ {
struct bcm2835_wdt *wdt = watchdog_get_drvdata(wdog); struct bcm2835_wdt *wdt = watchdog_get_drvdata(wdog);
...@@ -96,15 +90,14 @@ static unsigned int bcm2835_wdt_get_timeleft(struct watchdog_device *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); 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, .owner = THIS_MODULE,
.start = bcm2835_wdt_start, .start = bcm2835_wdt_start,
.stop = bcm2835_wdt_stop, .stop = bcm2835_wdt_stop,
.set_timeout = bcm2835_wdt_set_timeout,
.get_timeleft = bcm2835_wdt_get_timeleft, .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 | .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE |
WDIOF_KEEPALIVEPING, WDIOF_KEEPALIVEPING,
.identity = "Broadcom BCM2835 Watchdog timer", .identity = "Broadcom BCM2835 Watchdog timer",
......
...@@ -34,6 +34,7 @@ static const unsigned int wdt_timeout[] = { 0, 2, 4, 8, 16, 32, 65, 131 }; ...@@ -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_MIN_TIMEOUT wdt_timeout[DA9063_TWDSCALE_MIN]
#define DA9063_WDT_MAX_TIMEOUT wdt_timeout[DA9063_TWDSCALE_MAX] #define DA9063_WDT_MAX_TIMEOUT wdt_timeout[DA9063_TWDSCALE_MAX]
#define DA9063_WDG_TIMEOUT wdt_timeout[3] #define DA9063_WDG_TIMEOUT wdt_timeout[3]
#define DA9063_RESET_PROTECTION_MS 256
struct da9063_watchdog { struct da9063_watchdog {
struct da9063 *da9063; struct da9063 *da9063;
...@@ -171,6 +172,7 @@ static int da9063_wdt_probe(struct platform_device *pdev) ...@@ -171,6 +172,7 @@ static int da9063_wdt_probe(struct platform_device *pdev)
wdt->wdtdev.ops = &da9063_watchdog_ops; wdt->wdtdev.ops = &da9063_watchdog_ops;
wdt->wdtdev.min_timeout = DA9063_WDT_MIN_TIMEOUT; wdt->wdtdev.min_timeout = DA9063_WDT_MIN_TIMEOUT;
wdt->wdtdev.max_timeout = DA9063_WDT_MAX_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.timeout = DA9063_WDG_TIMEOUT;
wdt->wdtdev.parent = &pdev->dev; wdt->wdtdev.parent = &pdev->dev;
......
...@@ -45,9 +45,11 @@ ...@@ -45,9 +45,11 @@
#define SIO_REG_DEVREV 0x22 /* Device revision */ #define SIO_REG_DEVREV 0x22 /* Device revision */
#define SIO_REG_MANID 0x23 /* Fintek ID (2 bytes) */ #define SIO_REG_MANID 0x23 /* Fintek ID (2 bytes) */
#define SIO_REG_ROM_ADDR_SEL 0x27 /* ROM address select */ #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_MFUNCT1 0x29 /* Multi function select 1 */
#define SIO_REG_MFUNCT2 0x2a /* Multi function select 2 */ #define SIO_REG_MFUNCT2 0x2a /* Multi function select 2 */
#define SIO_REG_MFUNCT3 0x2b /* Multi function select 3 */ #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_ENABLE 0x30 /* Logical device enable */
#define SIO_REG_ADDR 0x60 /* Logical device address (2 bytes) */ #define SIO_REG_ADDR 0x60 /* Logical device address (2 bytes) */
...@@ -60,6 +62,7 @@ ...@@ -60,6 +62,7 @@
#define SIO_F71882_ID 0x0541 /* Chipset ID */ #define SIO_F71882_ID 0x0541 /* Chipset ID */
#define SIO_F71889_ID 0x0723 /* Chipset ID */ #define SIO_F71889_ID 0x0723 /* Chipset ID */
#define SIO_F81865_ID 0x0704 /* 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_WDO_CONF 0xf0
#define F71808FG_REG_WDT_CONF 0xf5 #define F71808FG_REG_WDT_CONF 0xf5
...@@ -116,7 +119,8 @@ module_param(start_withtimeout, uint, 0); ...@@ -116,7 +119,8 @@ module_param(start_withtimeout, uint, 0);
MODULE_PARM_DESC(start_withtimeout, "Start watchdog timer on module load with" MODULE_PARM_DESC(start_withtimeout, "Start watchdog timer on module load with"
" given initial timeout. Zero (default) disables this feature."); " 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[] = { static const char *f71808e_names[] = {
"f71808fg", "f71808fg",
...@@ -126,6 +130,7 @@ static const char *f71808e_names[] = { ...@@ -126,6 +130,7 @@ static const char *f71808e_names[] = {
"f71882fg", "f71882fg",
"f71889fg", "f71889fg",
"f81865", "f81865",
"f81866",
}; };
/* Super-I/O Function prototypes */ /* Super-I/O Function prototypes */
...@@ -370,6 +375,22 @@ static int watchdog_start(void) ...@@ -370,6 +375,22 @@ static int watchdog_start(void)
superio_clear_bit(watchdog.sioaddr, SIO_REG_MFUNCT3, 5); superio_clear_bit(watchdog.sioaddr, SIO_REG_MFUNCT3, 5);
break; 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:
/* /*
* 'default' label to shut up the compiler and catch * 'default' label to shut up the compiler and catch
...@@ -382,7 +403,7 @@ static int watchdog_start(void) ...@@ -382,7 +403,7 @@ static int watchdog_start(void)
superio_select(watchdog.sioaddr, SIO_F71808FG_LD_WDT); superio_select(watchdog.sioaddr, SIO_F71808FG_LD_WDT);
superio_set_bit(watchdog.sioaddr, SIO_REG_ENABLE, 0); 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, superio_set_bit(watchdog.sioaddr, F81865_REG_WDO_CONF,
F81865_FLAG_WDOUT_EN); F81865_FLAG_WDOUT_EN);
else else
...@@ -788,6 +809,9 @@ static int __init f71808e_find(int sioaddr) ...@@ -788,6 +809,9 @@ static int __init f71808e_find(int sioaddr)
case SIO_F81865_ID: case SIO_F81865_ID:
watchdog.type = f81865; watchdog.type = f81865;
break; break;
case SIO_F81866_ID:
watchdog.type = f81866;
break;
default: default:
pr_info("Unrecognized Fintek device: %04x\n", pr_info("Unrecognized Fintek device: %04x\n",
(unsigned int)devid); (unsigned int)devid);
......
...@@ -151,6 +151,8 @@ static int gpio_wdt_probe(struct platform_device *pdev) ...@@ -151,6 +151,8 @@ static int gpio_wdt_probe(struct platform_device *pdev)
if (!priv) if (!priv)
return -ENOMEM; return -ENOMEM;
platform_set_drvdata(pdev, priv);
priv->gpio = of_get_gpio_flags(pdev->dev.of_node, 0, &flags); priv->gpio = of_get_gpio_flags(pdev->dev.of_node, 0, &flags);
if (!gpio_is_valid(priv->gpio)) if (!gpio_is_valid(priv->gpio))
return priv->gpio; return priv->gpio;
......
...@@ -150,6 +150,7 @@ static inline u32 no_reboot_bit(void) ...@@ -150,6 +150,7 @@ static inline u32 no_reboot_bit(void)
u32 enable_bit; u32 enable_bit;
switch (iTCO_wdt_private.iTCO_version) { switch (iTCO_wdt_private.iTCO_version) {
case 5:
case 3: case 3:
enable_bit = 0x00000010; enable_bit = 0x00000010;
break; break;
...@@ -512,6 +513,7 @@ static int iTCO_wdt_probe(struct platform_device *dev) ...@@ -512,6 +513,7 @@ static int iTCO_wdt_probe(struct platform_device *dev)
/* Clear out the (probably old) status */ /* Clear out the (probably old) status */
switch (iTCO_wdt_private.iTCO_version) { switch (iTCO_wdt_private.iTCO_version) {
case 5:
case 4: case 4:
outw(0x0008, TCO1_STS); /* Clear the Time Out Status bit */ outw(0x0008, TCO1_STS); /* Clear the Time Out Status bit */
outw(0x0002, TCO2_STS); /* Clear SECOND_TO_STS bit */ outw(0x0002, TCO2_STS); /* Clear SECOND_TO_STS bit */
......
/*
* 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");
/*
* 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");
...@@ -294,6 +294,8 @@ static const struct pci_device_id tco_pci_tbl[] = { ...@@ -294,6 +294,8 @@ static const struct pci_device_id tco_pci_tbl[] = {
PCI_ANY_ID, PCI_ANY_ID, }, PCI_ANY_ID, PCI_ANY_ID, },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SMBUS, { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SMBUS,
PCI_ANY_ID, PCI_ANY_ID, }, 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_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP79_SMBUS,
PCI_ANY_ID, PCI_ANY_ID, }, PCI_ANY_ID, PCI_ANY_ID, },
{ 0, }, /* End of list */ { 0, }, /* End of list */
......
...@@ -992,19 +992,7 @@ static struct isa_driver pcwd_isa_driver = { ...@@ -992,19 +992,7 @@ static struct isa_driver pcwd_isa_driver = {
}, },
}; };
static int __init pcwd_init_module(void) module_isa_driver(pcwd_isa_driver, PCWD_ISA_NR_CARDS);
{
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_AUTHOR("Ken Hollis <kenji@bitgate.com>, " MODULE_AUTHOR("Ken Hollis <kenji@bitgate.com>, "
"Wim Van Sebroeck <wim@iguana.be>"); "Wim Van Sebroeck <wim@iguana.be>");
......
...@@ -176,8 +176,8 @@ static int pic32_dmt_probe(struct platform_device *pdev) ...@@ -176,8 +176,8 @@ static int pic32_dmt_probe(struct platform_device *pdev)
struct watchdog_device *wdd = &pic32_dmt_wdd; struct watchdog_device *wdd = &pic32_dmt_wdd;
dmt = devm_kzalloc(&pdev->dev, sizeof(*dmt), GFP_KERNEL); dmt = devm_kzalloc(&pdev->dev, sizeof(*dmt), GFP_KERNEL);
if (IS_ERR(dmt)) if (!dmt)
return PTR_ERR(dmt); return -ENOMEM;
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
dmt->regs = devm_ioremap_resource(&pdev->dev, mem); dmt->regs = devm_ioremap_resource(&pdev->dev, mem);
...@@ -245,7 +245,6 @@ static struct platform_driver pic32_dmt_driver = { ...@@ -245,7 +245,6 @@ static struct platform_driver pic32_dmt_driver = {
.remove = pic32_dmt_remove, .remove = pic32_dmt_remove,
.driver = { .driver = {
.name = "pic32-dmt", .name = "pic32-dmt",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(pic32_dmt_of_ids), .of_match_table = of_match_ptr(pic32_dmt_of_ids),
} }
}; };
......
...@@ -174,8 +174,8 @@ static int pic32_wdt_drv_probe(struct platform_device *pdev) ...@@ -174,8 +174,8 @@ static int pic32_wdt_drv_probe(struct platform_device *pdev)
struct resource *mem; struct resource *mem;
wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL);
if (IS_ERR(wdt)) if (!wdt)
return PTR_ERR(wdt); return -ENOMEM;
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
wdt->regs = devm_ioremap_resource(&pdev->dev, mem); wdt->regs = devm_ioremap_resource(&pdev->dev, mem);
...@@ -183,8 +183,8 @@ static int pic32_wdt_drv_probe(struct platform_device *pdev) ...@@ -183,8 +183,8 @@ static int pic32_wdt_drv_probe(struct platform_device *pdev)
return PTR_ERR(wdt->regs); return PTR_ERR(wdt->regs);
wdt->rst_base = devm_ioremap(&pdev->dev, PIC32_BASE_RESET, 0x10); wdt->rst_base = devm_ioremap(&pdev->dev, PIC32_BASE_RESET, 0x10);
if (IS_ERR(wdt->rst_base)) if (!wdt->rst_base)
return PTR_ERR(wdt->rst_base); return -ENOMEM;
wdt->clk = devm_clk_get(&pdev->dev, NULL); wdt->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(wdt->clk)) { if (IS_ERR(wdt->clk)) {
...@@ -251,7 +251,6 @@ static struct platform_driver pic32_wdt_driver = { ...@@ -251,7 +251,6 @@ static struct platform_driver pic32_wdt_driver = {
.remove = pic32_wdt_drv_remove, .remove = pic32_wdt_drv_remove,
.driver = { .driver = {
.name = "pic32-wdt", .name = "pic32-wdt",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(pic32_wdt_dt_ids), .of_match_table = of_match_ptr(pic32_wdt_dt_ids),
} }
}; };
......
...@@ -18,19 +18,45 @@ ...@@ -18,19 +18,45 @@
#include <linux/of.h> #include <linux/of.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/watchdog.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 static const u32 reg_offset_data_apcs_tmr[] = {
#define WDT_EN 0x40 [WDT_RST] = 0x38,
#define WDT_STS 0x44 [WDT_EN] = 0x40,
#define WDT_BITE_TIME 0x5C [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 qcom_wdt {
struct watchdog_device wdd; struct watchdog_device wdd;
struct clk *clk; struct clk *clk;
unsigned long rate; unsigned long rate;
void __iomem *base; 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 static inline
struct qcom_wdt *to_qcom_wdt(struct watchdog_device *wdd) struct qcom_wdt *to_qcom_wdt(struct watchdog_device *wdd)
{ {
...@@ -41,10 +67,11 @@ static int qcom_wdt_start(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); struct qcom_wdt *wdt = to_qcom_wdt(wdd);
writel(0, wdt->base + WDT_EN); writel(0, wdt_addr(wdt, WDT_EN));
writel(1, wdt->base + WDT_RST); writel(1, wdt_addr(wdt, WDT_RST));
writel(wdd->timeout * wdt->rate, wdt->base + WDT_BITE_TIME); writel(wdd->timeout * wdt->rate, wdt_addr(wdt, WDT_BARK_TIME));
writel(1, wdt->base + WDT_EN); writel(wdd->timeout * wdt->rate, wdt_addr(wdt, WDT_BITE_TIME));
writel(1, wdt_addr(wdt, WDT_EN));
return 0; return 0;
} }
...@@ -52,7 +79,7 @@ static int qcom_wdt_stop(struct watchdog_device *wdd) ...@@ -52,7 +79,7 @@ static int qcom_wdt_stop(struct watchdog_device *wdd)
{ {
struct qcom_wdt *wdt = to_qcom_wdt(wdd); struct qcom_wdt *wdt = to_qcom_wdt(wdd);
writel(0, wdt->base + WDT_EN); writel(0, wdt_addr(wdt, WDT_EN));
return 0; return 0;
} }
...@@ -60,7 +87,7 @@ static int qcom_wdt_ping(struct watchdog_device *wdd) ...@@ -60,7 +87,7 @@ static int qcom_wdt_ping(struct watchdog_device *wdd)
{ {
struct qcom_wdt *wdt = to_qcom_wdt(wdd); struct qcom_wdt *wdt = to_qcom_wdt(wdd);
writel(1, wdt->base + WDT_RST); writel(1, wdt_addr(wdt, WDT_RST));
return 0; return 0;
} }
...@@ -83,10 +110,11 @@ static int qcom_wdt_restart(struct watchdog_device *wdd, unsigned long action, ...@@ -83,10 +110,11 @@ static int qcom_wdt_restart(struct watchdog_device *wdd, unsigned long action,
*/ */
timeout = 128 * wdt->rate / 1000; timeout = 128 * wdt->rate / 1000;
writel(0, wdt->base + WDT_EN); writel(0, wdt_addr(wdt, WDT_EN));
writel(1, wdt->base + WDT_RST); writel(1, wdt_addr(wdt, WDT_RST));
writel(timeout, wdt->base + WDT_BITE_TIME); writel(timeout, wdt_addr(wdt, WDT_BARK_TIME));
writel(1, wdt->base + WDT_EN); 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. * Actually make sure the above sequence hits hardware before sleeping.
...@@ -119,9 +147,16 @@ static int qcom_wdt_probe(struct platform_device *pdev) ...@@ -119,9 +147,16 @@ static int qcom_wdt_probe(struct platform_device *pdev)
struct qcom_wdt *wdt; struct qcom_wdt *wdt;
struct resource *res; struct resource *res;
struct device_node *np = pdev->dev.of_node; struct device_node *np = pdev->dev.of_node;
const u32 *regs;
u32 percpu_offset; u32 percpu_offset;
int ret; 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); wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL);
if (!wdt) if (!wdt)
return -ENOMEM; return -ENOMEM;
...@@ -172,6 +207,7 @@ static int qcom_wdt_probe(struct platform_device *pdev) ...@@ -172,6 +207,7 @@ static int qcom_wdt_probe(struct platform_device *pdev)
wdt->wdd.min_timeout = 1; wdt->wdd.min_timeout = 1;
wdt->wdd.max_timeout = 0x10000000U / wdt->rate; wdt->wdd.max_timeout = 0x10000000U / wdt->rate;
wdt->wdd.parent = &pdev->dev; wdt->wdd.parent = &pdev->dev;
wdt->layout = regs;
if (readl(wdt->base + WDT_STS) & 1) if (readl(wdt->base + WDT_STS) & 1)
wdt->wdd.bootstatus = WDIOF_CARDRESET; wdt->wdd.bootstatus = WDIOF_CARDRESET;
...@@ -208,8 +244,9 @@ static int qcom_wdt_remove(struct platform_device *pdev) ...@@ -208,8 +244,9 @@ static int qcom_wdt_remove(struct platform_device *pdev)
} }
static const struct of_device_id qcom_wdt_of_table[] = { static const struct of_device_id qcom_wdt_of_table[] = {
{ .compatible = "qcom,kpss-timer" }, { .compatible = "qcom,kpss-timer", .data = reg_offset_data_apcs_tmr },
{ .compatible = "qcom,scss-timer" }, { .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); MODULE_DEVICE_TABLE(of, qcom_wdt_of_table);
......
...@@ -180,15 +180,6 @@ static int sbsa_gwdt_keepalive(struct watchdog_device *wdd) ...@@ -180,15 +180,6 @@ static int sbsa_gwdt_keepalive(struct watchdog_device *wdd)
return 0; 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) static int sbsa_gwdt_start(struct watchdog_device *wdd)
{ {
struct sbsa_gwdt *gwdt = watchdog_get_drvdata(wdd); struct sbsa_gwdt *gwdt = watchdog_get_drvdata(wdd);
...@@ -228,7 +219,6 @@ static struct watchdog_ops sbsa_gwdt_ops = { ...@@ -228,7 +219,6 @@ static struct watchdog_ops sbsa_gwdt_ops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.start = sbsa_gwdt_start, .start = sbsa_gwdt_start,
.stop = sbsa_gwdt_stop, .stop = sbsa_gwdt_stop,
.status = sbsa_gwdt_status,
.ping = sbsa_gwdt_keepalive, .ping = sbsa_gwdt_keepalive,
.set_timeout = sbsa_gwdt_set_timeout, .set_timeout = sbsa_gwdt_set_timeout,
.get_timeleft = sbsa_gwdt_get_timeleft, .get_timeleft = sbsa_gwdt_get_timeleft,
...@@ -273,7 +263,7 @@ static int sbsa_gwdt_probe(struct platform_device *pdev) ...@@ -273,7 +263,7 @@ static int sbsa_gwdt_probe(struct platform_device *pdev)
wdd->info = &sbsa_gwdt_info; wdd->info = &sbsa_gwdt_info;
wdd->ops = &sbsa_gwdt_ops; wdd->ops = &sbsa_gwdt_ops;
wdd->min_timeout = 1; 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; wdd->timeout = DEFAULT_TIMEOUT;
watchdog_set_drvdata(wdd, gwdt); watchdog_set_drvdata(wdd, gwdt);
watchdog_set_nowayout(wdd, nowayout); watchdog_set_nowayout(wdd, nowayout);
...@@ -283,6 +273,8 @@ static int sbsa_gwdt_probe(struct platform_device *pdev) ...@@ -283,6 +273,8 @@ static int sbsa_gwdt_probe(struct platform_device *pdev)
dev_warn(dev, "System reset by WDT.\n"); dev_warn(dev, "System reset by WDT.\n");
wdd->bootstatus |= WDIOF_CARDRESET; wdd->bootstatus |= WDIOF_CARDRESET;
} }
if (status & SBSA_GWDT_WCS_EN)
set_bit(WDOG_HW_RUNNING, &wdd->status);
if (action) { if (action) {
irq = platform_get_irq(pdev, 0); irq = platform_get_irq(pdev, 0);
...@@ -310,7 +302,7 @@ static int sbsa_gwdt_probe(struct platform_device *pdev) ...@@ -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. * the timeout is (WOR * 2), so the maximum timeout should be doubled.
*/ */
if (!action) if (!action)
wdd->max_timeout *= 2; wdd->max_hw_heartbeat_ms *= 2;
watchdog_init_timeout(wdd, timeout, dev); watchdog_init_timeout(wdd, timeout, dev);
/* /*
......
...@@ -39,13 +39,18 @@ MODULE_PARM_DESC(timeout, "Default watchdog timeout (in seconds)"); ...@@ -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_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); __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) static unsigned int sirfsoc_wdt_gettimeleft(struct watchdog_device *wdd)
{ {
u32 counter, match; u32 counter, match;
void __iomem *wdt_base; void __iomem *wdt_base;
int time_left; int time_left;
wdt_base = watchdog_get_drvdata(wdd); wdt_base = sirfsoc_wdt_base(wdd);
counter = readl(wdt_base + SIRFSOC_TIMER_COUNTER_LO); counter = readl(wdt_base + SIRFSOC_TIMER_COUNTER_LO);
match = readl(wdt_base + match = readl(wdt_base +
SIRFSOC_TIMER_MATCH_0 + (SIRFSOC_TIMER_WDT_INDEX << 2)); SIRFSOC_TIMER_MATCH_0 + (SIRFSOC_TIMER_WDT_INDEX << 2));
...@@ -61,7 +66,7 @@ static int sirfsoc_wdt_updatetimeout(struct watchdog_device *wdd) ...@@ -61,7 +66,7 @@ static int sirfsoc_wdt_updatetimeout(struct watchdog_device *wdd)
void __iomem *wdt_base; void __iomem *wdt_base;
timeout_ticks = wdd->timeout * CLOCK_FREQ; 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 */ /* Enable the latch before reading the LATCH_LO register */
writel(1, wdt_base + SIRFSOC_TIMER_LATCH); writel(1, wdt_base + SIRFSOC_TIMER_LATCH);
...@@ -79,7 +84,7 @@ static int sirfsoc_wdt_updatetimeout(struct watchdog_device *wdd) ...@@ -79,7 +84,7 @@ static int sirfsoc_wdt_updatetimeout(struct watchdog_device *wdd)
static int sirfsoc_wdt_enable(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); sirfsoc_wdt_updatetimeout(wdd);
/* /*
...@@ -96,7 +101,7 @@ static int sirfsoc_wdt_enable(struct watchdog_device *wdd) ...@@ -96,7 +101,7 @@ static int sirfsoc_wdt_enable(struct watchdog_device *wdd)
static int sirfsoc_wdt_disable(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(0, wdt_base + SIRFSOC_TIMER_WATCHDOG_EN);
writel(readl(wdt_base + SIRFSOC_TIMER_INT_EN) writel(readl(wdt_base + SIRFSOC_TIMER_INT_EN)
...@@ -150,7 +155,7 @@ static int sirfsoc_wdt_probe(struct platform_device *pdev) ...@@ -150,7 +155,7 @@ static int sirfsoc_wdt_probe(struct platform_device *pdev)
if (IS_ERR(base)) if (IS_ERR(base))
return PTR_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_init_timeout(&sirfsoc_wdd, timeout, &pdev->dev);
watchdog_set_nowayout(&sirfsoc_wdd, nowayout); watchdog_set_nowayout(&sirfsoc_wdd, nowayout);
......
...@@ -17,36 +17,19 @@ ...@@ -17,36 +17,19 @@
* *
* Software only watchdog driver. Unlike its big brother the WDT501P * Software only watchdog driver. Unlike its big brother the WDT501P
* driver this won't always recover a failed machine. * 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 #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/module.h>
#include <linux/moduleparam.h> #include <linux/moduleparam.h>
#include <linux/types.h> #include <linux/reboot.h>
#include <linux/timer.h> #include <linux/timer.h>
#include <linux/types.h>
#include <linux/watchdog.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 */ #define TIMER_MARGIN 60 /* Default is 60 seconds */
static unsigned int soft_margin = TIMER_MARGIN; /* in seconds */ static unsigned int soft_margin = TIMER_MARGIN; /* in seconds */
...@@ -71,25 +54,12 @@ module_param(soft_panic, int, 0); ...@@ -71,25 +54,12 @@ module_param(soft_panic, int, 0);
MODULE_PARM_DESC(soft_panic, MODULE_PARM_DESC(soft_panic,
"Softdog action, set to 1 to panic, 0 to reboot (default=0)"); "Softdog action, set to 1 to panic, 0 to reboot (default=0)");
/* static void softdog_fire(unsigned long data)
* 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)
{ {
module_put(THIS_MODULE); module_put(THIS_MODULE);
if (soft_noboot) if (soft_noboot) {
pr_crit("Triggered - Reboot ignored\n"); pr_crit("Triggered - Reboot ignored\n");
else if (soft_panic) { } else if (soft_panic) {
pr_crit("Initiating panic\n"); pr_crit("Initiating panic\n");
panic("Software Watchdog Timer expired"); panic("Software Watchdog Timer expired");
} else { } else {
...@@ -99,35 +69,24 @@ static void watchdog_fire(unsigned long data) ...@@ -99,35 +69,24 @@ static void watchdog_fire(unsigned long data)
} }
} }
/* static struct timer_list softdog_ticktock =
* Softdog operations TIMER_INITIALIZER(softdog_fire, 0, 0);
*/
static int softdog_ping(struct watchdog_device *w) 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); __module_get(THIS_MODULE);
return 0; return 0;
} }
static int softdog_stop(struct watchdog_device *w) static int softdog_stop(struct watchdog_device *w)
{ {
if (del_timer(&watchdog_ticktock)) if (del_timer(&softdog_ticktock))
module_put(THIS_MODULE); module_put(THIS_MODULE);
return 0; 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 = { static struct watchdog_info softdog_info = {
.identity = "Software Watchdog", .identity = "Software Watchdog",
.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
...@@ -137,29 +96,21 @@ static struct watchdog_ops softdog_ops = { ...@@ -137,29 +96,21 @@ static struct watchdog_ops softdog_ops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.start = softdog_ping, .start = softdog_ping,
.stop = softdog_stop, .stop = softdog_stop,
.set_timeout = softdog_set_timeout,
}; };
static struct watchdog_device softdog_dev = { static struct watchdog_device softdog_dev = {
.info = &softdog_info, .info = &softdog_info,
.ops = &softdog_ops, .ops = &softdog_ops,
.min_timeout = 1, .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; int ret;
/* Check that the soft_margin value is within it's range; watchdog_init_timeout(&softdog_dev, soft_margin, NULL);
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_set_nowayout(&softdog_dev, nowayout); watchdog_set_nowayout(&softdog_dev, nowayout);
watchdog_stop_on_reboot(&softdog_dev); watchdog_stop_on_reboot(&softdog_dev);
...@@ -167,19 +118,18 @@ static int __init watchdog_init(void) ...@@ -167,19 +118,18 @@ static int __init watchdog_init(void)
if (ret) if (ret)
return ret; return ret;
pr_info("Software Watchdog Timer: 0.08 initialized. soft_noboot=%d soft_margin=%d sec soft_panic=%d (nowayout=%d)\n", pr_info("initialized. soft_noboot=%d soft_margin=%d sec soft_panic=%d (nowayout=%d)\n",
soft_noboot, soft_margin, soft_panic, nowayout); soft_noboot, softdog_dev.timeout, soft_panic, nowayout);
return 0; return 0;
} }
module_init(softdog_init);
static void __exit watchdog_exit(void) static void __exit softdog_exit(void)
{ {
watchdog_unregister_device(&softdog_dev); watchdog_unregister_device(&softdog_dev);
} }
module_exit(softdog_exit);
module_init(watchdog_init);
module_exit(watchdog_exit);
MODULE_AUTHOR("Alan Cox"); MODULE_AUTHOR("Alan Cox");
MODULE_DESCRIPTION("Software Watchdog Device Driver"); MODULE_DESCRIPTION("Software Watchdog Device Driver");
......
...@@ -149,7 +149,7 @@ static int tangox_wdt_probe(struct platform_device *pdev) ...@@ -149,7 +149,7 @@ static int tangox_wdt_probe(struct platform_device *pdev)
dev->wdt.ops = &tangox_wdt_ops; dev->wdt.ops = &tangox_wdt_ops;
dev->wdt.timeout = DEFAULT_TIMEOUT; dev->wdt.timeout = DEFAULT_TIMEOUT;
dev->wdt.min_timeout = 1; 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_init_timeout(&dev->wdt, timeout, &pdev->dev);
watchdog_set_nowayout(&dev->wdt, nowayout); watchdog_set_nowayout(&dev->wdt, nowayout);
...@@ -170,7 +170,7 @@ static int tangox_wdt_probe(struct platform_device *pdev) ...@@ -170,7 +170,7 @@ static int tangox_wdt_probe(struct platform_device *pdev)
* already running. * already running.
*/ */
if (readl(dev->base + WD_COUNTER)) { 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); tangox_wdt_start(&dev->wdt);
} }
......
...@@ -88,7 +88,7 @@ static void watchdog_check_min_max_timeout(struct watchdog_device *wdd) ...@@ -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 * Check that we have valid min and max timeout values, if
* not reset them both to 0 (=not used or unknown) * 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"); pr_info("Invalid min and max timeout values, resetting to 0!\n");
wdd->min_timeout = 0; wdd->min_timeout = 0;
wdd->max_timeout = 0; wdd->max_timeout = 0;
...@@ -329,6 +329,43 @@ void watchdog_unregister_device(struct watchdog_device *wdd) ...@@ -329,6 +329,43 @@ void watchdog_unregister_device(struct watchdog_device *wdd)
EXPORT_SYMBOL_GPL(watchdog_unregister_device); 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) static int __init watchdog_deferred_registration(void)
{ {
mutex_lock(&wtd_deferred_reg_mutex); mutex_lock(&wtd_deferred_reg_mutex);
......
...@@ -69,6 +69,7 @@ struct watchdog_core_data { ...@@ -69,6 +69,7 @@ struct watchdog_core_data {
unsigned long status; /* Internal status bits */ unsigned long status; /* Internal status bits */
#define _WDOG_DEV_OPEN 0 /* Opened ? */ #define _WDOG_DEV_OPEN 0 /* Opened ? */
#define _WDOG_ALLOW_RELEASE 1 /* Did we receive the magic char ? */ #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 */ /* 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) ...@@ -92,9 +93,13 @@ static inline bool watchdog_need_worker(struct watchdog_device *wdd)
* thus is aware that the framework supports generating heartbeat * thus is aware that the framework supports generating heartbeat
* requests. * requests.
* - Userspace requests a longer timeout than the hardware can handle. * - 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) || return (hm && watchdog_active(wdd) && t > hm) ||
(t && !watchdog_active(wdd) && watchdog_hw_running(wdd))); (t && !watchdog_active(wdd) && watchdog_hw_running(wdd));
} }
static long watchdog_next_keepalive(struct watchdog_device *wdd) static long watchdog_next_keepalive(struct watchdog_device *wdd)
...@@ -107,7 +112,7 @@ 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; unsigned int hw_heartbeat_ms;
virt_timeout = wd_data->last_keepalive + msecs_to_jiffies(timeout_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); keepalive_interval = msecs_to_jiffies(hw_heartbeat_ms / 2);
if (!watchdog_active(wdd)) if (!watchdog_active(wdd))
...@@ -180,6 +185,8 @@ static int watchdog_ping(struct watchdog_device *wdd) ...@@ -180,6 +185,8 @@ static int watchdog_ping(struct watchdog_device *wdd)
if (!watchdog_active(wdd) && !watchdog_hw_running(wdd)) if (!watchdog_active(wdd) && !watchdog_hw_running(wdd))
return 0; return 0;
set_bit(_WDOG_KEEPALIVE, &wd_data->status);
wd_data->last_keepalive = jiffies; wd_data->last_keepalive = jiffies;
return __watchdog_ping(wdd); return __watchdog_ping(wdd);
} }
...@@ -219,6 +226,8 @@ static int watchdog_start(struct watchdog_device *wdd) ...@@ -219,6 +226,8 @@ static int watchdog_start(struct watchdog_device *wdd)
if (watchdog_active(wdd)) if (watchdog_active(wdd))
return 0; return 0;
set_bit(_WDOG_KEEPALIVE, &wd_data->status);
started_at = jiffies; started_at = jiffies;
if (watchdog_hw_running(wdd) && wdd->ops->ping) if (watchdog_hw_running(wdd) && wdd->ops->ping)
err = wdd->ops->ping(wdd); err = wdd->ops->ping(wdd);
...@@ -258,10 +267,12 @@ static int watchdog_stop(struct watchdog_device *wdd) ...@@ -258,10 +267,12 @@ static int watchdog_stop(struct watchdog_device *wdd)
return -EBUSY; return -EBUSY;
} }
if (wdd->ops->stop) if (wdd->ops->stop) {
clear_bit(WDOG_HW_RUNNING, &wdd->status);
err = wdd->ops->stop(wdd); err = wdd->ops->stop(wdd);
else } else {
set_bit(WDOG_HW_RUNNING, &wdd->status); set_bit(WDOG_HW_RUNNING, &wdd->status);
}
if (err == 0) { if (err == 0) {
clear_bit(WDOG_ACTIVE, &wdd->status); clear_bit(WDOG_ACTIVE, &wdd->status);
...@@ -282,10 +293,27 @@ static int watchdog_stop(struct watchdog_device *wdd) ...@@ -282,10 +293,27 @@ static int watchdog_stop(struct watchdog_device *wdd)
static unsigned int watchdog_get_status(struct watchdog_device *wdd) static unsigned int watchdog_get_status(struct watchdog_device *wdd)
{ {
if (!wdd->ops->status) struct watchdog_core_data *wd_data = wdd->wd_data;
return 0; 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, ...@@ -361,7 +389,7 @@ static ssize_t status_show(struct device *dev, struct device_attribute *attr,
status = watchdog_get_status(wdd); status = watchdog_get_status(wdd);
mutex_unlock(&wd_data->lock); mutex_unlock(&wd_data->lock);
return sprintf(buf, "%u\n", status); return sprintf(buf, "0x%x\n", status);
} }
static DEVICE_ATTR_RO(status); static DEVICE_ATTR_RO(status);
...@@ -429,9 +457,7 @@ static umode_t wdt_is_visible(struct kobject *kobj, struct attribute *attr, ...@@ -429,9 +457,7 @@ static umode_t wdt_is_visible(struct kobject *kobj, struct attribute *attr,
struct watchdog_device *wdd = dev_get_drvdata(dev); struct watchdog_device *wdd = dev_get_drvdata(dev);
umode_t mode = attr->mode; umode_t mode = attr->mode;
if (attr == &dev_attr_status.attr && !wdd->ops->status) if (attr == &dev_attr_timeleft.attr && !wdd->ops->get_timeleft)
mode = 0;
else if (attr == &dev_attr_timeleft.attr && !wdd->ops->get_timeleft)
mode = 0; mode = 0;
return mode; return mode;
...@@ -948,17 +974,22 @@ int __init watchdog_dev_init(void) ...@@ -948,17 +974,22 @@ int __init watchdog_dev_init(void)
err = class_register(&watchdog_class); err = class_register(&watchdog_class);
if (err < 0) { if (err < 0) {
pr_err("couldn't register class\n"); pr_err("couldn't register class\n");
return err; goto err_register;
} }
err = alloc_chrdev_region(&watchdog_devt, 0, MAX_DOGS, "watchdog"); err = alloc_chrdev_region(&watchdog_devt, 0, MAX_DOGS, "watchdog");
if (err < 0) { if (err < 0) {
pr_err("watchdog: unable to allocate char dev region\n"); pr_err("watchdog: unable to allocate char dev region\n");
class_unregister(&watchdog_class); goto err_alloc;
return err;
} }
return 0; return 0;
err_alloc:
class_unregister(&watchdog_class);
err_register:
destroy_workqueue(watchdog_wq);
return err;
} }
/* /*
......
...@@ -339,7 +339,7 @@ static int ziirave_wdt_remove(struct i2c_client *client) ...@@ -339,7 +339,7 @@ static int ziirave_wdt_remove(struct i2c_client *client)
} }
static struct i2c_device_id ziirave_wdt_id[] = { static struct i2c_device_id ziirave_wdt_id[] = {
{ "ziirave-wdt", 0 }, { "rave-wdt", 0 },
{ } { }
}; };
MODULE_DEVICE_TABLE(i2c, ziirave_wdt_id); MODULE_DEVICE_TABLE(i2c, ziirave_wdt_id);
......
...@@ -66,7 +66,8 @@ struct watchdog_ops { ...@@ -66,7 +66,8 @@ struct watchdog_ops {
* as configurable from user space. Only relevant if * as configurable from user space. Only relevant if
* max_hw_heartbeat_ms is not provided. * max_hw_heartbeat_ms is not provided.
* @min_hw_heartbeat_ms: * @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: * @max_hw_heartbeat_ms:
* Hardware limit for maximum timeout, in milli-seconds. * Hardware limit for maximum timeout, in milli-seconds.
* Replaces max_timeout if specified. * Replaces max_timeout if specified.
...@@ -180,4 +181,7 @@ extern int watchdog_init_timeout(struct watchdog_device *wdd, ...@@ -180,4 +181,7 @@ extern int watchdog_init_timeout(struct watchdog_device *wdd,
extern int watchdog_register_device(struct watchdog_device *); extern int watchdog_register_device(struct watchdog_device *);
extern void watchdog_unregister_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 */ #endif /* ifndef _LINUX_WATCHDOG_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment