diff options
author | Sean Paul <seanpaul@chromium.org> | 2019-04-10 15:50:49 -0400 |
---|---|---|
committer | Sean Paul <seanpaul@chromium.org> | 2019-04-10 15:50:49 -0400 |
commit | 3bfaf1f7044c6a3b1e00fcad2d0529f0da449d67 (patch) | |
tree | 490bc7e8aa29c2ee41eb4813683f1aa23971abd3 /drivers/watchdog | |
parent | 669105a74a287b14cdec04c64eb51db1bb890f64 (diff) | |
parent | 14d2bd53a47a7e1cb3e03d00a6b952734cf90f3f (diff) | |
download | lwn-3bfaf1f7044c6a3b1e00fcad2d0529f0da449d67.tar.gz lwn-3bfaf1f7044c6a3b1e00fcad2d0529f0da449d67.zip |
Merge drm/drm-next into drm-misc-next
Finally have a reason for a backmerge other than "it's been a while"!
Backmerging drm-next to -misc-next to facilitate Rob Herring's work on
Panfrost.
Signed-off-by: Sean Paul <seanpaul@chromium.org>
Diffstat (limited to 'drivers/watchdog')
-rw-r--r-- | drivers/watchdog/Kconfig | 37 | ||||
-rw-r--r-- | drivers/watchdog/Makefile | 2 | ||||
-rw-r--r-- | drivers/watchdog/bcm2835_wdt.c | 26 | ||||
-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/stpmic1_wdt.c | 139 | ||||
-rw-r--r-- | drivers/watchdog/w83877f_wdt.c | 2 |
14 files changed, 503 insertions, 30 deletions
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 57f017d74a97..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 @@ -817,6 +833,18 @@ config STM32_WATCHDOG To compile this driver as a module, choose M here: the module will be called stm32_iwdg. +config STPMIC1_WATCHDOG + tristate "STPMIC1 PMIC watchdog support" + depends on MFD_STPMIC1 + select WATCHDOG_CORE + help + Say Y here to include watchdog support embedded into STPMIC1 PMIC. + If the watchdog timer expires, stpmic1 will shut down all its power + supplies. + + To compile this driver as a module, choose M here: the + module will be called spmic1_wdt. + config UNIPHIER_WATCHDOG tristate "UniPhier watchdog support" depends on ARCH_UNIPHIER || COMPILE_TEST @@ -1145,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 @@ -1163,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 a0917ef28e07..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 @@ -220,3 +221,4 @@ obj-$(CONFIG_SOFT_WATCHDOG) += softdog.o obj-$(CONFIG_MENF21BMC_WATCHDOG) += menf21bmc_wdt.o obj-$(CONFIG_MENZ069_WATCHDOG) += menz69_wdt.o obj-$(CONFIG_RAVE_SP_WATCHDOG) += rave-sp-wdt.o +obj-$(CONFIG_STPMIC1_WATCHDOG) += stpmic1_wdt.o diff --git a/drivers/watchdog/bcm2835_wdt.c b/drivers/watchdog/bcm2835_wdt.c index ed05514cc2dc..1834524ae373 100644 --- a/drivers/watchdog/bcm2835_wdt.c +++ b/drivers/watchdog/bcm2835_wdt.c @@ -12,6 +12,7 @@ #include <linux/delay.h> #include <linux/types.h> +#include <linux/mfd/bcm2835-pm.h> #include <linux/module.h> #include <linux/io.h> #include <linux/watchdog.h> @@ -47,6 +48,8 @@ struct bcm2835_wdt { spinlock_t lock; }; +static struct bcm2835_wdt *bcm2835_power_off_wdt; + static unsigned int heartbeat; static bool nowayout = WATCHDOG_NOWAYOUT; @@ -148,10 +151,7 @@ static struct watchdog_device bcm2835_wdt_wdd = { */ static void bcm2835_power_off(void) { - struct device_node *np = - of_find_compatible_node(NULL, NULL, "brcm,bcm2835-pm-wdt"); - struct platform_device *pdev = of_find_device_by_node(np); - struct bcm2835_wdt *wdt = platform_get_drvdata(pdev); + struct bcm2835_wdt *wdt = bcm2835_power_off_wdt; u32 val; /* @@ -169,7 +169,7 @@ static void bcm2835_power_off(void) static int bcm2835_wdt_probe(struct platform_device *pdev) { - struct resource *res; + struct bcm2835_pm *pm = dev_get_drvdata(pdev->dev.parent); struct device *dev = &pdev->dev; struct bcm2835_wdt *wdt; int err; @@ -181,10 +181,7 @@ static int bcm2835_wdt_probe(struct platform_device *pdev) spin_lock_init(&wdt->lock); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - wdt->base = devm_ioremap_resource(dev, res); - if (IS_ERR(wdt->base)) - return PTR_ERR(wdt->base); + wdt->base = pm->base; watchdog_set_drvdata(&bcm2835_wdt_wdd, wdt); watchdog_init_timeout(&bcm2835_wdt_wdd, heartbeat, dev); @@ -211,8 +208,10 @@ static int bcm2835_wdt_probe(struct platform_device *pdev) return err; } - if (pm_power_off == NULL) + if (pm_power_off == NULL) { pm_power_off = bcm2835_power_off; + bcm2835_power_off_wdt = wdt; + } dev_info(dev, "Broadcom BCM2835 watchdog timer"); return 0; @@ -226,18 +225,11 @@ static int bcm2835_wdt_remove(struct platform_device *pdev) return 0; } -static const struct of_device_id bcm2835_wdt_of_match[] = { - { .compatible = "brcm,bcm2835-pm-wdt", }, - {}, -}; -MODULE_DEVICE_TABLE(of, bcm2835_wdt_of_match); - static struct platform_driver bcm2835_wdt_driver = { .probe = bcm2835_wdt_probe, .remove = bcm2835_wdt_remove, .driver = { .name = "bcm2835-wdt", - .of_match_table = bcm2835_wdt_of_match, }, }; module_platform_driver(bcm2835_wdt_driver); 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/stpmic1_wdt.c b/drivers/watchdog/stpmic1_wdt.c new file mode 100644 index 000000000000..ad431d8ad95f --- /dev/null +++ b/drivers/watchdog/stpmic1_wdt.c @@ -0,0 +1,139 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) STMicroelectronics 2018 +// Author: Pascal Paillet <p.paillet@st.com> for STMicroelectronics. + +#include <linux/kernel.h> +#include <linux/mfd/stpmic1.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/of.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <linux/watchdog.h> + +/* WATCHDOG CONTROL REGISTER bit */ +#define WDT_START BIT(0) +#define WDT_PING BIT(1) +#define WDT_START_MASK BIT(0) +#define WDT_PING_MASK BIT(1) +#define WDT_STOP 0 + +#define PMIC_WDT_MIN_TIMEOUT 1 +#define PMIC_WDT_MAX_TIMEOUT 256 +#define PMIC_WDT_DEFAULT_TIMEOUT 30 + +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); + +struct stpmic1_wdt { + struct stpmic1 *pmic; + struct watchdog_device wdtdev; +}; + +static int pmic_wdt_start(struct watchdog_device *wdd) +{ + struct stpmic1_wdt *wdt = watchdog_get_drvdata(wdd); + + return regmap_update_bits(wdt->pmic->regmap, + WCHDG_CR, WDT_START_MASK, WDT_START); +} + +static int pmic_wdt_stop(struct watchdog_device *wdd) +{ + struct stpmic1_wdt *wdt = watchdog_get_drvdata(wdd); + + return regmap_update_bits(wdt->pmic->regmap, + WCHDG_CR, WDT_START_MASK, WDT_STOP); +} + +static int pmic_wdt_ping(struct watchdog_device *wdd) +{ + struct stpmic1_wdt *wdt = watchdog_get_drvdata(wdd); + + return regmap_update_bits(wdt->pmic->regmap, + WCHDG_CR, WDT_PING_MASK, WDT_PING); +} + +static int pmic_wdt_set_timeout(struct watchdog_device *wdd, + unsigned int timeout) +{ + struct stpmic1_wdt *wdt = watchdog_get_drvdata(wdd); + + wdd->timeout = timeout; + /* timeout is equal to register value + 1 */ + return regmap_write(wdt->pmic->regmap, WCHDG_TIMER_CR, timeout - 1); +} + +static const struct watchdog_info pmic_watchdog_info = { + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, + .identity = "STPMIC1 PMIC Watchdog", +}; + +static const struct watchdog_ops pmic_watchdog_ops = { + .owner = THIS_MODULE, + .start = pmic_wdt_start, + .stop = pmic_wdt_stop, + .ping = pmic_wdt_ping, + .set_timeout = pmic_wdt_set_timeout, +}; + +static int pmic_wdt_probe(struct platform_device *pdev) +{ + int ret; + struct stpmic1 *pmic; + struct stpmic1_wdt *wdt; + + if (!pdev->dev.parent) + return -EINVAL; + + pmic = dev_get_drvdata(pdev->dev.parent); + if (!pmic) + return -EINVAL; + + wdt = devm_kzalloc(&pdev->dev, sizeof(struct stpmic1_wdt), GFP_KERNEL); + if (!wdt) + return -ENOMEM; + + wdt->pmic = pmic; + + wdt->wdtdev.info = &pmic_watchdog_info; + wdt->wdtdev.ops = &pmic_watchdog_ops; + wdt->wdtdev.min_timeout = PMIC_WDT_MIN_TIMEOUT; + wdt->wdtdev.max_timeout = PMIC_WDT_MAX_TIMEOUT; + wdt->wdtdev.parent = &pdev->dev; + + wdt->wdtdev.timeout = PMIC_WDT_DEFAULT_TIMEOUT; + watchdog_init_timeout(&wdt->wdtdev, 0, &pdev->dev); + + watchdog_set_nowayout(&wdt->wdtdev, nowayout); + watchdog_set_drvdata(&wdt->wdtdev, wdt); + + ret = devm_watchdog_register_device(&pdev->dev, &wdt->wdtdev); + if (ret) + return ret; + + dev_dbg(wdt->pmic->dev, "PMIC Watchdog driver probed\n"); + return 0; +} + +static const struct of_device_id of_pmic_wdt_match[] = { + { .compatible = "st,stpmic1-wdt" }, + { }, +}; + +MODULE_DEVICE_TABLE(of, of_pmic_wdt_match); + +static struct platform_driver stpmic1_wdt_driver = { + .probe = pmic_wdt_probe, + .driver = { + .name = "stpmic1-wdt", + .of_match_table = of_pmic_wdt_match, + }, +}; +module_platform_driver(stpmic1_wdt_driver); + +MODULE_DESCRIPTION("Watchdog driver for STPMIC1 device"); +MODULE_AUTHOR("Pascal Paillet <p.paillet@st.com>"); +MODULE_LICENSE("GPL v2"); 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: |