diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2019-03-11 19:22:15 +0100 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2019-03-11 19:22:15 +0100 |
commit | a089e4fed5c5e8717f233d71bb750fbf9e1f38e0 (patch) | |
tree | e34611231af2b4bb0261864691b7f3618e101b33 /drivers/watchdog | |
parent | Merge tag 'pinctrl-v5.1-1' of git://git.kernel.org/pub/scm/linux/kernel/git/l... (diff) | |
parent | watchdog: w83877f_wdt: Mark expected switch fall-through (diff) | |
download | linux-a089e4fed5c5e8717f233d71bb750fbf9e1f38e0.tar.xz linux-a089e4fed5c5e8717f233d71bb750fbf9e1f38e0.zip |
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/Kconfig | 25 | ||||
-rw-r--r-- | drivers/watchdog/Makefile | 1 | ||||
-rw-r--r-- | drivers/watchdog/dw_wdt.c | 2 | ||||
-rw-r--r-- | drivers/watchdog/mlx_wdt.c | 290 | ||||
-rw-r--r-- | drivers/watchdog/pc87413_wdt.c | 2 | ||||
-rw-r--r-- | drivers/watchdog/pika_wdt.c | 2 | ||||
-rw-r--r-- | drivers/watchdog/qcom-wdt.c | 23 | ||||
-rw-r--r-- | drivers/watchdog/sbc60xxwdt.c | 2 | ||||
-rw-r--r-- | drivers/watchdog/sc1200wdt.c | 2 | ||||
-rw-r--r-- | drivers/watchdog/sc520_wdt.c | 2 | ||||
-rw-r--r-- | drivers/watchdog/smsc37b787_wdt.c | 2 | ||||
-rw-r--r-- | drivers/watchdog/w83877f_wdt.c | 2 |
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, ®val); + 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, ®val); + 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, ®val); + /* 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: |