summaryrefslogtreecommitdiffstats
path: root/drivers/watchdog
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2019-03-11 11:22:15 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2019-03-11 11:22:15 -0700
commita089e4fed5c5e8717f233d71bb750fbf9e1f38e0 (patch)
treee34611231af2b4bb0261864691b7f3618e101b33 /drivers/watchdog
parentcf0240a755b8b3df51b0b857b03309a666611d58 (diff)
parent458c8961c194f2d2cb6ffed3dd9b6692d74ad65e (diff)
downloadlinux-a089e4fed5c5e8717f233d71bb750fbf9e1f38e0.tar.bz2
Merge tag 'linux-watchdog-5.1-rc1' of git://www.linux-watchdog.org/linux-watchdog
Pull watchdog updates from Wim Van Sebroeck: - a new watchdog driver for the Mellanox systems - renesas-wdt: Document r8a77470 support - numerous 'Mark expected switch fall-throughs' - qcom: Add suspend/resume support - some small fixes and documentation updates * tag 'linux-watchdog-5.1-rc1' of git://www.linux-watchdog.org/linux-watchdog: watchdog: w83877f_wdt: Mark expected switch fall-through watchdog: sc520_wdt: Mark expected switch fall-through watchdog: sbc60xxwdt: Mark expected switch fall-through watchdog: smsc37b787_wdt: Mark expected switch fall-through watchdog: sc1200: Mark expected switch fall-through watchdog: pc87413: Mark expected switch fall-through Documentation/watchdog: Add documentation mlx-wdt driver watchdog: mlx-wdt: introduce a watchdog driver for Mellanox systems. platform_data/mlxreg: additions for Mellanox watchdog driver. watchdog: Update sysfs documentation. watchdog: dw: remove useless pr_fmt watchdog: pika_wdt: drop pointless static qualifier in pikawdt_init watchdog/hpwdt: Update Kconfig documentation dt-bindings: watchdog: renesas-wdt: Document r8a77470 support watchdog: qcom: Add suspend/resume support
Diffstat (limited to 'drivers/watchdog')
-rw-r--r--drivers/watchdog/Kconfig25
-rw-r--r--drivers/watchdog/Makefile1
-rw-r--r--drivers/watchdog/dw_wdt.c2
-rw-r--r--drivers/watchdog/mlx_wdt.c290
-rw-r--r--drivers/watchdog/pc87413_wdt.c2
-rw-r--r--drivers/watchdog/pika_wdt.c2
-rw-r--r--drivers/watchdog/qcom-wdt.c23
-rw-r--r--drivers/watchdog/sbc60xxwdt.c2
-rw-r--r--drivers/watchdog/sc1200wdt.c2
-rw-r--r--drivers/watchdog/sc520_wdt.c2
-rw-r--r--drivers/watchdog/smsc37b787_wdt.c2
-rw-r--r--drivers/watchdog/w83877f_wdt.c2
12 files changed, 342 insertions, 13 deletions
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 65c3c42b5e7c..242eea859637 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -241,6 +241,22 @@ config RAVE_SP_WATCHDOG
help
Support for the watchdog on RAVE SP device.
+config MLX_WDT
+ tristate "Mellanox Watchdog"
+ depends on MELLANOX_PLATFORM
+ select WATCHDOG_CORE
+ select REGMAP
+ help
+ This is the driver for the hardware watchdog on Mellanox systems.
+ If you are going to use it, say Y here, otherwise N.
+ This driver can be used together with the watchdog daemon.
+ It can also watch your kernel to make sure it doesn't freeze,
+ and if it does, it reboots your system after a certain amount of
+ time.
+
+ To compile this driver as a module, choose M here: the
+ module will be called mlx-wdt.
+
# ALPHA Architecture
# ARM Architecture
@@ -1157,7 +1173,7 @@ config HP_WATCHDOG
select WATCHDOG_CORE
depends on X86 && PCI
help
- A software monitoring watchdog and NMI sourcing driver. This driver
+ A software monitoring watchdog and NMI handling driver. This driver
will detect lockups and provide a stack trace. This is a driver that
will only load on an HP ProLiant system with a minimum of iLO2 support.
To compile this driver as a module, choose M here: the module will be
@@ -1175,12 +1191,13 @@ config KEMPLD_WDT
called kempld_wdt.
config HPWDT_NMI_DECODING
- bool "NMI decoding support for the HP ProLiant iLO2+ Hardware Watchdog Timer"
+ bool "NMI support for the HP ProLiant iLO2+ Hardware Watchdog Timer"
depends on HP_WATCHDOG
default y
help
- When an NMI occurs this feature will make the necessary BIOS calls to
- log the cause of the NMI.
+ Enables the NMI handler for the watchdog pretimeout NMI and the iLO
+ "Generate NMI to System" virtual button. When an NMI is claimed
+ by the driver, panic is called.
config SC1200_WDT
tristate "National Semiconductor PC87307/PC97307 (ala SC1200) Watchdog"
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 4e78a8c73f0c..ba930e464657 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -142,6 +142,7 @@ obj-$(CONFIG_INTEL_MID_WATCHDOG) += intel-mid_wdt.o
obj-$(CONFIG_INTEL_MEI_WDT) += mei_wdt.o
obj-$(CONFIG_NI903X_WDT) += ni903x_wdt.o
obj-$(CONFIG_NIC7018_WDT) += nic7018_wdt.o
+obj-$(CONFIG_MLX_WDT) += mlx_wdt.o
# M68K Architecture
obj-$(CONFIG_M54xx_WATCHDOG) += m54xx_wdt.o
diff --git a/drivers/watchdog/dw_wdt.c b/drivers/watchdog/dw_wdt.c
index 501aebb5b81f..aa95f57cc1c3 100644
--- a/drivers/watchdog/dw_wdt.c
+++ b/drivers/watchdog/dw_wdt.c
@@ -16,8 +16,6 @@
* heartbeat requests after the watchdog device has been closed.
*/
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
#include <linux/bitops.h>
#include <linux/clk.h>
#include <linux/delay.h>
diff --git a/drivers/watchdog/mlx_wdt.c b/drivers/watchdog/mlx_wdt.c
new file mode 100644
index 000000000000..70c2cbf9c993
--- /dev/null
+++ b/drivers/watchdog/mlx_wdt.c
@@ -0,0 +1,290 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Mellanox watchdog driver
+ *
+ * Copyright (C) 2019 Mellanox Technologies
+ * Copyright (C) 2019 Michael Shych <mshych@mellanox.com>
+ */
+
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/log2.h>
+#include <linux/module.h>
+#include <linux/platform_data/mlxreg.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <linux/watchdog.h>
+
+#define MLXREG_WDT_CLOCK_SCALE 1000
+#define MLXREG_WDT_MAX_TIMEOUT_TYPE1 32
+#define MLXREG_WDT_MAX_TIMEOUT_TYPE2 255
+#define MLXREG_WDT_MIN_TIMEOUT 1
+#define MLXREG_WDT_OPTIONS_BASE (WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE | \
+ WDIOF_SETTIMEOUT)
+
+/**
+ * struct mlxreg_wdt - wd private data:
+ *
+ * @wdd: watchdog device;
+ * @device: basic device;
+ * @pdata: data received from platform driver;
+ * @regmap: register map of parent device;
+ * @timeout: defined timeout in sec.;
+ * @action_idx: index for direct access to action register;
+ * @timeout_idx:index for direct access to TO register;
+ * @tleft_idx: index for direct access to time left register;
+ * @ping_idx: index for direct access to ping register;
+ * @reset_idx: index for direct access to reset cause register;
+ * @wd_type: watchdog HW type;
+ */
+struct mlxreg_wdt {
+ struct watchdog_device wdd;
+ struct mlxreg_core_platform_data *pdata;
+ void *regmap;
+ int action_idx;
+ int timeout_idx;
+ int tleft_idx;
+ int ping_idx;
+ int reset_idx;
+ enum mlxreg_wdt_type wdt_type;
+};
+
+static void mlxreg_wdt_check_card_reset(struct mlxreg_wdt *wdt)
+{
+ struct mlxreg_core_data *reg_data;
+ u32 regval;
+ int rc;
+
+ if (wdt->reset_idx == -EINVAL)
+ return;
+
+ if (!(wdt->wdd.info->options & WDIOF_CARDRESET))
+ return;
+
+ reg_data = &wdt->pdata->data[wdt->reset_idx];
+ rc = regmap_read(wdt->regmap, reg_data->reg, &regval);
+ if (!rc) {
+ if (regval & ~reg_data->mask) {
+ wdt->wdd.bootstatus = WDIOF_CARDRESET;
+ dev_info(wdt->wdd.parent,
+ "watchdog previously reset the CPU\n");
+ }
+ }
+}
+
+static int mlxreg_wdt_start(struct watchdog_device *wdd)
+{
+ struct mlxreg_wdt *wdt = watchdog_get_drvdata(wdd);
+ struct mlxreg_core_data *reg_data = &wdt->pdata->data[wdt->action_idx];
+
+ return regmap_update_bits(wdt->regmap, reg_data->reg, ~reg_data->mask,
+ BIT(reg_data->bit));
+}
+
+static int mlxreg_wdt_stop(struct watchdog_device *wdd)
+{
+ struct mlxreg_wdt *wdt = watchdog_get_drvdata(wdd);
+ struct mlxreg_core_data *reg_data = &wdt->pdata->data[wdt->action_idx];
+
+ return regmap_update_bits(wdt->regmap, reg_data->reg, ~reg_data->mask,
+ ~BIT(reg_data->bit));
+}
+
+static int mlxreg_wdt_ping(struct watchdog_device *wdd)
+{
+ struct mlxreg_wdt *wdt = watchdog_get_drvdata(wdd);
+ struct mlxreg_core_data *reg_data = &wdt->pdata->data[wdt->ping_idx];
+
+ return regmap_update_bits_base(wdt->regmap, reg_data->reg,
+ ~reg_data->mask, BIT(reg_data->bit),
+ NULL, false, true);
+}
+
+static int mlxreg_wdt_set_timeout(struct watchdog_device *wdd,
+ unsigned int timeout)
+{
+ struct mlxreg_wdt *wdt = watchdog_get_drvdata(wdd);
+ struct mlxreg_core_data *reg_data = &wdt->pdata->data[wdt->timeout_idx];
+ u32 regval, set_time, hw_timeout;
+ int rc;
+
+ if (wdt->wdt_type == MLX_WDT_TYPE1) {
+ rc = regmap_read(wdt->regmap, reg_data->reg, &regval);
+ if (rc)
+ return rc;
+
+ hw_timeout = order_base_2(timeout * MLXREG_WDT_CLOCK_SCALE);
+ regval = (regval & reg_data->mask) | hw_timeout;
+ /* Rowndown to actual closest number of sec. */
+ set_time = BIT(hw_timeout) / MLXREG_WDT_CLOCK_SCALE;
+ } else {
+ set_time = timeout;
+ regval = timeout;
+ }
+
+ wdd->timeout = set_time;
+ rc = regmap_write(wdt->regmap, reg_data->reg, regval);
+
+ if (!rc) {
+ /*
+ * Restart watchdog with new timeout period
+ * if watchdog is already started.
+ */
+ if (watchdog_active(wdd)) {
+ rc = mlxreg_wdt_stop(wdd);
+ if (!rc)
+ rc = mlxreg_wdt_start(wdd);
+ }
+ }
+
+ return rc;
+}
+
+static unsigned int mlxreg_wdt_get_timeleft(struct watchdog_device *wdd)
+{
+ struct mlxreg_wdt *wdt = watchdog_get_drvdata(wdd);
+ struct mlxreg_core_data *reg_data = &wdt->pdata->data[wdt->tleft_idx];
+ u32 regval;
+ int rc;
+
+ rc = regmap_read(wdt->regmap, reg_data->reg, &regval);
+ /* Return 0 timeleft in case of failure register read. */
+ return rc == 0 ? regval : 0;
+}
+
+static const struct watchdog_ops mlxreg_wdt_ops_type1 = {
+ .start = mlxreg_wdt_start,
+ .stop = mlxreg_wdt_stop,
+ .ping = mlxreg_wdt_ping,
+ .set_timeout = mlxreg_wdt_set_timeout,
+ .owner = THIS_MODULE,
+};
+
+static const struct watchdog_ops mlxreg_wdt_ops_type2 = {
+ .start = mlxreg_wdt_start,
+ .stop = mlxreg_wdt_stop,
+ .ping = mlxreg_wdt_ping,
+ .set_timeout = mlxreg_wdt_set_timeout,
+ .get_timeleft = mlxreg_wdt_get_timeleft,
+ .owner = THIS_MODULE,
+};
+
+static const struct watchdog_info mlxreg_wdt_main_info = {
+ .options = MLXREG_WDT_OPTIONS_BASE
+ | WDIOF_CARDRESET,
+ .identity = "mlx-wdt-main",
+};
+
+static const struct watchdog_info mlxreg_wdt_aux_info = {
+ .options = MLXREG_WDT_OPTIONS_BASE
+ | WDIOF_ALARMONLY,
+ .identity = "mlx-wdt-aux",
+};
+
+static void mlxreg_wdt_config(struct mlxreg_wdt *wdt,
+ struct mlxreg_core_platform_data *pdata)
+{
+ struct mlxreg_core_data *data = pdata->data;
+ int i;
+
+ wdt->reset_idx = -EINVAL;
+ for (i = 0; i < pdata->counter; i++, data++) {
+ if (strnstr(data->label, "action", sizeof(data->label)))
+ wdt->action_idx = i;
+ else if (strnstr(data->label, "timeout", sizeof(data->label)))
+ wdt->timeout_idx = i;
+ else if (strnstr(data->label, "timeleft", sizeof(data->label)))
+ wdt->tleft_idx = i;
+ else if (strnstr(data->label, "ping", sizeof(data->label)))
+ wdt->ping_idx = i;
+ else if (strnstr(data->label, "reset", sizeof(data->label)))
+ wdt->reset_idx = i;
+ }
+
+ wdt->pdata = pdata;
+ if (strnstr(pdata->identity, mlxreg_wdt_main_info.identity,
+ sizeof(mlxreg_wdt_main_info.identity)))
+ wdt->wdd.info = &mlxreg_wdt_main_info;
+ else
+ wdt->wdd.info = &mlxreg_wdt_aux_info;
+
+ wdt->wdt_type = pdata->version;
+ if (wdt->wdt_type == MLX_WDT_TYPE2) {
+ wdt->wdd.ops = &mlxreg_wdt_ops_type2;
+ wdt->wdd.max_timeout = MLXREG_WDT_MAX_TIMEOUT_TYPE2;
+ } else {
+ wdt->wdd.ops = &mlxreg_wdt_ops_type1;
+ wdt->wdd.max_timeout = MLXREG_WDT_MAX_TIMEOUT_TYPE1;
+ }
+ wdt->wdd.min_timeout = MLXREG_WDT_MIN_TIMEOUT;
+}
+
+static int mlxreg_wdt_init_timeout(struct mlxreg_wdt *wdt,
+ struct mlxreg_core_platform_data *pdata)
+{
+ u32 timeout;
+
+ timeout = pdata->data[wdt->timeout_idx].health_cntr;
+ return mlxreg_wdt_set_timeout(&wdt->wdd, timeout);
+}
+
+static int mlxreg_wdt_probe(struct platform_device *pdev)
+{
+ struct mlxreg_core_platform_data *pdata;
+ struct mlxreg_wdt *wdt;
+ int rc;
+
+ pdata = dev_get_platdata(&pdev->dev);
+ if (!pdata) {
+ dev_err(&pdev->dev, "Failed to get platform data.\n");
+ return -EINVAL;
+ }
+ wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL);
+ if (!wdt)
+ return -ENOMEM;
+
+ wdt->wdd.parent = &pdev->dev;
+ wdt->regmap = pdata->regmap;
+ mlxreg_wdt_config(wdt, pdata);
+
+ if ((pdata->features & MLXREG_CORE_WD_FEATURE_NOWAYOUT))
+ watchdog_set_nowayout(&wdt->wdd, WATCHDOG_NOWAYOUT);
+ watchdog_stop_on_reboot(&wdt->wdd);
+ watchdog_stop_on_unregister(&wdt->wdd);
+ watchdog_set_drvdata(&wdt->wdd, wdt);
+ rc = mlxreg_wdt_init_timeout(wdt, pdata);
+ if (rc)
+ goto register_error;
+
+ if ((pdata->features & MLXREG_CORE_WD_FEATURE_START_AT_BOOT)) {
+ rc = mlxreg_wdt_start(&wdt->wdd);
+ if (rc)
+ goto register_error;
+ set_bit(WDOG_HW_RUNNING, &wdt->wdd.status);
+ }
+ mlxreg_wdt_check_card_reset(wdt);
+ rc = devm_watchdog_register_device(&pdev->dev, &wdt->wdd);
+
+register_error:
+ if (rc)
+ dev_err(&pdev->dev,
+ "Cannot register watchdog device (err=%d)\n", rc);
+ return rc;
+}
+
+static struct platform_driver mlxreg_wdt_driver = {
+ .probe = mlxreg_wdt_probe,
+ .driver = {
+ .name = "mlx-wdt",
+ },
+};
+
+module_platform_driver(mlxreg_wdt_driver);
+
+MODULE_AUTHOR("Michael Shych <michaelsh@mellanox.com>");
+MODULE_DESCRIPTION("Mellanox watchdog driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:mlx-wdt");
diff --git a/drivers/watchdog/pc87413_wdt.c b/drivers/watchdog/pc87413_wdt.c
index 06a892e36a8d..2ffa39b46970 100644
--- a/drivers/watchdog/pc87413_wdt.c
+++ b/drivers/watchdog/pc87413_wdt.c
@@ -437,7 +437,7 @@ static long pc87413_ioctl(struct file *file, unsigned int cmd,
return -EINVAL;
timeout = new_timeout;
pc87413_refresh();
- /* fall through and return the new timeout... */
+ /* fall through - and return the new timeout... */
case WDIOC_GETTIMEOUT:
new_timeout = timeout * 60;
return put_user(new_timeout, uarg.i);
diff --git a/drivers/watchdog/pika_wdt.c b/drivers/watchdog/pika_wdt.c
index e0a6f8c0f03c..bb97f5b2f7eb 100644
--- a/drivers/watchdog/pika_wdt.c
+++ b/drivers/watchdog/pika_wdt.c
@@ -225,7 +225,7 @@ static int __init pikawdt_init(void)
{
struct device_node *np;
void __iomem *fpga;
- static u32 post1;
+ u32 post1;
int ret;
np = of_find_compatible_node(NULL, NULL, "pika,fpga");
diff --git a/drivers/watchdog/qcom-wdt.c b/drivers/watchdog/qcom-wdt.c
index 780971318810..5dfd604477a4 100644
--- a/drivers/watchdog/qcom-wdt.c
+++ b/drivers/watchdog/qcom-wdt.c
@@ -245,6 +245,28 @@ static int qcom_wdt_remove(struct platform_device *pdev)
return 0;
}
+static int __maybe_unused qcom_wdt_suspend(struct device *dev)
+{
+ struct qcom_wdt *wdt = dev_get_drvdata(dev);
+
+ if (watchdog_active(&wdt->wdd))
+ qcom_wdt_stop(&wdt->wdd);
+
+ return 0;
+}
+
+static int __maybe_unused qcom_wdt_resume(struct device *dev)
+{
+ struct qcom_wdt *wdt = dev_get_drvdata(dev);
+
+ if (watchdog_active(&wdt->wdd))
+ qcom_wdt_start(&wdt->wdd);
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(qcom_wdt_pm_ops, qcom_wdt_suspend, qcom_wdt_resume);
+
static const struct of_device_id qcom_wdt_of_table[] = {
{ .compatible = "qcom,kpss-timer", .data = reg_offset_data_apcs_tmr },
{ .compatible = "qcom,scss-timer", .data = reg_offset_data_apcs_tmr },
@@ -259,6 +281,7 @@ static struct platform_driver qcom_watchdog_driver = {
.driver = {
.name = KBUILD_MODNAME,
.of_match_table = qcom_wdt_of_table,
+ .pm = &qcom_wdt_pm_ops,
},
};
module_platform_driver(qcom_watchdog_driver);
diff --git a/drivers/watchdog/sbc60xxwdt.c b/drivers/watchdog/sbc60xxwdt.c
index 87333a41f753..72d15fd1f183 100644
--- a/drivers/watchdog/sbc60xxwdt.c
+++ b/drivers/watchdog/sbc60xxwdt.c
@@ -270,8 +270,8 @@ static long fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
timeout = new_timeout;
wdt_keepalive();
- /* Fall through */
}
+ /* Fall through */
case WDIOC_GETTIMEOUT:
return put_user(timeout, p);
default:
diff --git a/drivers/watchdog/sc1200wdt.c b/drivers/watchdog/sc1200wdt.c
index 8e4e2fc13f87..e035a4d4b299 100644
--- a/drivers/watchdog/sc1200wdt.c
+++ b/drivers/watchdog/sc1200wdt.c
@@ -239,7 +239,7 @@ static long sc1200wdt_ioctl(struct file *file, unsigned int cmd,
return -EINVAL;
timeout = new_timeout;
sc1200wdt_write_data(WDTO, timeout);
- /* fall through and return the new timeout */
+ /* fall through - and return the new timeout */
case WDIOC_GETTIMEOUT:
return put_user(timeout * 60, p);
diff --git a/drivers/watchdog/sc520_wdt.c b/drivers/watchdog/sc520_wdt.c
index 6aadb56e7faa..403542f9ed8d 100644
--- a/drivers/watchdog/sc520_wdt.c
+++ b/drivers/watchdog/sc520_wdt.c
@@ -324,8 +324,8 @@ static long fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
return -EINVAL;
wdt_keepalive();
- /* Fall through */
}
+ /* Fall through */
case WDIOC_GETTIMEOUT:
return put_user(timeout, p);
default:
diff --git a/drivers/watchdog/smsc37b787_wdt.c b/drivers/watchdog/smsc37b787_wdt.c
index 445ea1ad1fa9..c768dcd53034 100644
--- a/drivers/watchdog/smsc37b787_wdt.c
+++ b/drivers/watchdog/smsc37b787_wdt.c
@@ -478,7 +478,7 @@ static long wb_smsc_wdt_ioctl(struct file *file,
return -EINVAL;
timeout = new_timeout;
wb_smsc_wdt_set_timeout(timeout);
- /* fall through and return the new timeout... */
+ /* fall through - and return the new timeout... */
case WDIOC_GETTIMEOUT:
new_timeout = timeout;
if (unit == UNIT_MINUTE)
diff --git a/drivers/watchdog/w83877f_wdt.c b/drivers/watchdog/w83877f_wdt.c
index 05658ecc0aa4..db9b6488e388 100644
--- a/drivers/watchdog/w83877f_wdt.c
+++ b/drivers/watchdog/w83877f_wdt.c
@@ -292,8 +292,8 @@ static long fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
timeout = new_timeout;
wdt_keepalive();
- /* Fall through */
}
+ /* Fall through */
case WDIOC_GETTIMEOUT:
return put_user(timeout, p);
default: