mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-24 12:44:11 +08:00
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 ...
This commit is contained in:
commit
27acbec338
16
Documentation/devicetree/bindings/watchdog/aspeed-wdt.txt
Normal file
16
Documentation/devicetree/bindings/watchdog/aspeed-wdt.txt
Normal file
@ -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>;
|
||||
};
|
@ -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>;
|
||||
};
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -357,3 +357,6 @@ SLAVE DMA ENGINE
|
||||
|
||||
SPI
|
||||
devm_spi_register_master()
|
||||
|
||||
WATCHDOG
|
||||
devm_watchdog_register_device()
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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>;
|
||||
|
@ -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>;
|
||||
|
@ -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>,
|
||||
|
@ -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>;
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
212
drivers/watchdog/aspeed_wdt.c
Normal file
212
drivers/watchdog/aspeed_wdt.c
Normal file
@ -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");
|
@ -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",
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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 */
|
||||
|
227
drivers/watchdog/max77620_wdt.c
Normal file
227
drivers/watchdog/max77620_wdt.c
Normal file
@ -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, ®val);
|
||||
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");
|
270
drivers/watchdog/meson_gxbb_wdt.c
Normal file
270
drivers/watchdog/meson_gxbb_wdt.c
Normal file
@ -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");
|
@ -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 */
|
||||
|
@ -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>");
|
||||
|
@ -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),
|
||||
}
|
||||
};
|
||||
|
@ -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),
|
||||
}
|
||||
};
|
||||
|
@ -18,19 +18,45 @@
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/watchdog.h>
|
||||
#include <linux/of_device.h>
|
||||
|
||||
#define WDT_RST 0x38
|
||||
#define WDT_EN 0x40
|
||||
#define WDT_STS 0x44
|
||||
#define WDT_BITE_TIME 0x5C
|
||||
enum wdt_reg {
|
||||
WDT_RST,
|
||||
WDT_EN,
|
||||
WDT_STS,
|
||||
WDT_BARK_TIME,
|
||||
WDT_BITE_TIME,
|
||||
};
|
||||
|
||||
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);
|
||||
|
@ -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);
|
||||
/*
|
||||
|
@ -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);
|
||||
|
@ -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/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/watchdog.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/watchdog.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");
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
return wdd->ops->status(wdd);
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -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);
|
||||
|
@ -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 */
|
||||
|
Loading…
Reference in New Issue
Block a user