From 7a3629fe999028e09bc96ccb0c9e22e0a9cf2725 Mon Sep 17 00:00:00 2001 From: Justin Chen Date: Mon, 31 Aug 2015 11:02:43 -0700 Subject: watchdog: Watchdog driver for Broadcom Set-Top Box Watchdog driver for Broadcom 7038 and newer chips. Signed-off-by: Justin Chen Reviewed-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/Kconfig | 8 ++ drivers/watchdog/Makefile | 1 + drivers/watchdog/bcm7038_wdt.c | 237 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 246 insertions(+) create mode 100644 drivers/watchdog/bcm7038_wdt.c (limited to 'drivers') diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 79e1aa1b0959..7a8a6c6952e9 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -1313,6 +1313,14 @@ config BCM_KONA_WDT_DEBUG If in doubt, say 'N'. +config BCM7038_WDT + tristate "BCM7038 Watchdog" + select WATCHDOG_CORE + help + Watchdog driver for the built-in hardware in Broadcom 7038 SoCs. + + Say 'Y or 'M' here to enable the driver. + config IMGPDC_WDT tristate "Imagination Technologies PDC Watchdog Timer" depends on HAS_IOMEM diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 0c616e3f67bb..53d4827ddfe1 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -68,6 +68,7 @@ obj-$(CONFIG_MESON_WATCHDOG) += meson_wdt.o obj-$(CONFIG_MEDIATEK_WATCHDOG) += mtk_wdt.o obj-$(CONFIG_DIGICOLOR_WATCHDOG) += digicolor_wdt.o obj-$(CONFIG_LPC18XX_WATCHDOG) += lpc18xx_wdt.o +obj-$(CONFIG_BCM7038_WDT) += bcm7038_wdt.o # AVR32 Architecture obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o diff --git a/drivers/watchdog/bcm7038_wdt.c b/drivers/watchdog/bcm7038_wdt.c new file mode 100644 index 000000000000..4245b65d645c --- /dev/null +++ b/drivers/watchdog/bcm7038_wdt.c @@ -0,0 +1,237 @@ +/* + * Copyright (C) 2015 Broadcom Corporation + * + * 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. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define WDT_START_1 0xff00 +#define WDT_START_2 0x00ff +#define WDT_STOP_1 0xee00 +#define WDT_STOP_2 0x00ee + +#define WDT_TIMEOUT_REG 0x0 +#define WDT_CMD_REG 0x4 + +#define WDT_MIN_TIMEOUT 1 /* seconds */ +#define WDT_DEFAULT_TIMEOUT 30 /* seconds */ +#define WDT_DEFAULT_RATE 27000000 + +struct bcm7038_watchdog { + void __iomem *base; + struct watchdog_device wdd; + u32 rate; + struct clk *clk; +}; + +static bool nowayout = WATCHDOG_NOWAYOUT; + +static void bcm7038_wdt_set_timeout_reg(struct watchdog_device *wdog) +{ + struct bcm7038_watchdog *wdt = watchdog_get_drvdata(wdog); + u32 timeout; + + timeout = wdt->rate * wdog->timeout; + + writel(timeout, wdt->base + WDT_TIMEOUT_REG); +} + +static int bcm7038_wdt_ping(struct watchdog_device *wdog) +{ + struct bcm7038_watchdog *wdt = watchdog_get_drvdata(wdog); + + writel(WDT_START_1, wdt->base + WDT_CMD_REG); + writel(WDT_START_2, wdt->base + WDT_CMD_REG); + + return 0; +} + +static int bcm7038_wdt_start(struct watchdog_device *wdog) +{ + bcm7038_wdt_set_timeout_reg(wdog); + bcm7038_wdt_ping(wdog); + + return 0; +} + +static int bcm7038_wdt_stop(struct watchdog_device *wdog) +{ + struct bcm7038_watchdog *wdt = watchdog_get_drvdata(wdog); + + writel(WDT_STOP_1, wdt->base + WDT_CMD_REG); + writel(WDT_STOP_2, wdt->base + WDT_CMD_REG); + + return 0; +} + +static int bcm7038_wdt_set_timeout(struct watchdog_device *wdog, + unsigned int t) +{ + /* Can't modify timeout value if watchdog timer is running */ + bcm7038_wdt_stop(wdog); + wdog->timeout = t; + bcm7038_wdt_start(wdog); + + return 0; +} + +static unsigned int bcm7038_wdt_get_timeleft(struct watchdog_device *wdog) +{ + struct bcm7038_watchdog *wdt = watchdog_get_drvdata(wdog); + u32 time_left; + + time_left = readl(wdt->base + WDT_CMD_REG); + + return time_left / wdt->rate; +} + +static struct watchdog_info bcm7038_wdt_info = { + .identity = "Broadcom BCM7038 Watchdog Timer", + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | + WDIOF_MAGICCLOSE +}; + +static struct watchdog_ops bcm7038_wdt_ops = { + .owner = THIS_MODULE, + .start = bcm7038_wdt_start, + .stop = bcm7038_wdt_stop, + .set_timeout = bcm7038_wdt_set_timeout, + .get_timeleft = bcm7038_wdt_get_timeleft, +}; + +static int bcm7038_wdt_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct bcm7038_watchdog *wdt; + struct resource *res; + int err; + + wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL); + if (!wdt) + return -ENOMEM; + + platform_set_drvdata(pdev, wdt); + + 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->clk = devm_clk_get(dev, NULL); + /* If unable to get clock, use default frequency */ + if (!IS_ERR(wdt->clk)) { + clk_prepare_enable(wdt->clk); + wdt->rate = clk_get_rate(wdt->clk); + /* Prevent divide-by-zero exception */ + if (!wdt->rate) + wdt->rate = WDT_DEFAULT_RATE; + } else { + wdt->rate = WDT_DEFAULT_RATE; + wdt->clk = NULL; + } + + wdt->wdd.info = &bcm7038_wdt_info; + wdt->wdd.ops = &bcm7038_wdt_ops; + wdt->wdd.min_timeout = WDT_MIN_TIMEOUT; + wdt->wdd.timeout = WDT_DEFAULT_TIMEOUT; + wdt->wdd.max_timeout = 0xffffffff / wdt->rate; + wdt->wdd.parent = dev; + watchdog_set_drvdata(&wdt->wdd, wdt); + + err = watchdog_register_device(&wdt->wdd); + if (err) { + dev_err(dev, "Failed to register watchdog device\n"); + clk_disable_unprepare(wdt->clk); + return err; + } + + dev_info(dev, "Registered BCM7038 Watchdog\n"); + + return 0; +} + +static int bcm7038_wdt_remove(struct platform_device *pdev) +{ + struct bcm7038_watchdog *wdt = platform_get_drvdata(pdev); + + if (!nowayout) + bcm7038_wdt_stop(&wdt->wdd); + + watchdog_unregister_device(&wdt->wdd); + clk_disable_unprepare(wdt->clk); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int bcm7038_wdt_suspend(struct device *dev) +{ + struct bcm7038_watchdog *wdt = dev_get_drvdata(dev); + + if (watchdog_active(&wdt->wdd)) + return bcm7038_wdt_stop(&wdt->wdd); + + return 0; +} + +static int bcm7038_wdt_resume(struct device *dev) +{ + struct bcm7038_watchdog *wdt = dev_get_drvdata(dev); + + if (watchdog_active(&wdt->wdd)) + return bcm7038_wdt_start(&wdt->wdd); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(bcm7038_wdt_pm_ops, bcm7038_wdt_suspend, + bcm7038_wdt_resume); + +static void bcm7038_wdt_shutdown(struct platform_device *pdev) +{ + struct bcm7038_watchdog *wdt = platform_get_drvdata(pdev); + + if (watchdog_active(&wdt->wdd)) + bcm7038_wdt_stop(&wdt->wdd); +} + +static const struct of_device_id bcm7038_wdt_match[] = { + { .compatible = "brcm,bcm7038-wdt" }, + {}, +}; + +static struct platform_driver bcm7038_wdt_driver = { + .probe = bcm7038_wdt_probe, + .remove = bcm7038_wdt_remove, + .shutdown = bcm7038_wdt_shutdown, + .driver = { + .name = "bcm7038-wdt", + .of_match_table = bcm7038_wdt_match, + .pm = &bcm7038_wdt_pm_ops, + } +}; +module_platform_driver(bcm7038_wdt_driver); + +module_param(nowayout, bool, 0); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Driver for Broadcom 7038 SoCs Watchdog"); +MODULE_AUTHOR("Justin Chen"); -- cgit v1.2.3-58-ga151 From 9dd4e173f71d5ac6ea37f7dbf49af6e5cd44f9fb Mon Sep 17 00:00:00 2001 From: Justin Chen Date: Wed, 2 Sep 2015 11:00:17 -0700 Subject: watchdog: watchdog_dev: Use device tree alias for naming watchdogs Currently there is no way to easily differentiate multiple watchdog devices. The watchdogs are named by the order they are probed. 1st probed watchdog: /dev/watchdog0 2nd probed watchdog: /dev/watchdog1 ... This change uses the alias of the watchdog device node for the name of the watchdog. aliases { watchdog0 = "/...../...." watchdog3 = "/..../....." watchdog2 = "/..../....." ... } This will translate to... /dev/watchdog0 /dev/watchdog3 /dev/watchdog2 v2 Assign alias number to id in watchdog_core instead of watchdog_dev. If failed to get id, fallback to original ida_simple_get call. Signed-off-by: Justin Chen Reviewed-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/watchdog_core.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/watchdog_core.c b/drivers/watchdog/watchdog_core.c index 1a8059455413..873f13972cf4 100644 --- a/drivers/watchdog/watchdog_core.c +++ b/drivers/watchdog/watchdog_core.c @@ -139,7 +139,7 @@ EXPORT_SYMBOL_GPL(watchdog_init_timeout); static int __watchdog_register_device(struct watchdog_device *wdd) { - int ret, id, devno; + int ret, id = -1, devno; if (wdd == NULL || wdd->info == NULL || wdd->ops == NULL) return -EINVAL; @@ -157,7 +157,18 @@ static int __watchdog_register_device(struct watchdog_device *wdd) */ mutex_init(&wdd->lock); - id = ida_simple_get(&watchdog_ida, 0, MAX_DOGS, GFP_KERNEL); + + /* Use alias for watchdog id if possible */ + if (wdd->parent) { + ret = of_alias_get_id(wdd->parent->of_node, "watchdog"); + if (ret >= 0) + id = ida_simple_get(&watchdog_ida, ret, + ret + 1, GFP_KERNEL); + } + + if (id < 0) + id = ida_simple_get(&watchdog_ida, 0, MAX_DOGS, GFP_KERNEL); + if (id < 0) return id; wdd->id = id; -- cgit v1.2.3-58-ga151 From 9493c0d824f7012dab7034a5b527ac8f07db5bed Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Fri, 2 Oct 2015 00:25:28 -0300 Subject: watchdog: imx2_wdt: Use register definition in regmap_write() In order to improve readability it is better to pass the register name definition rather than to pass its hardcoded offset. Signed-off-by: Fabio Estevam Reviewed-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/imx2_wdt.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/imx2_wdt.c b/drivers/watchdog/imx2_wdt.c index 0bb1a1d1b170..29ef719a6a3c 100644 --- a/drivers/watchdog/imx2_wdt.c +++ b/drivers/watchdog/imx2_wdt.c @@ -91,7 +91,7 @@ static int imx2_restart_handler(struct notifier_block *this, unsigned long mode, struct imx2_wdt_device, restart_handler); /* Assert SRS signal */ - regmap_write(wdev->regmap, 0, wcr_enable); + regmap_write(wdev->regmap, IMX2_WDT_WCR, wcr_enable); /* * Due to imx6q errata ERR004346 (WDOG: WDOG SRS bit requires to be * written twice), we add another two writes to ensure there must be at @@ -99,8 +99,8 @@ static int imx2_restart_handler(struct notifier_block *this, unsigned long mode, * the target check here, since the writes shouldn't be a huge burden * for other platforms. */ - regmap_write(wdev->regmap, 0, wcr_enable); - regmap_write(wdev->regmap, 0, wcr_enable); + regmap_write(wdev->regmap, IMX2_WDT_WCR, wcr_enable); + regmap_write(wdev->regmap, IMX2_WDT_WCR, wcr_enable); /* wait for reset to assert... */ mdelay(500); -- cgit v1.2.3-58-ga151 From 8cbb97ea3e386eb95ecbd99260d681792963cebf Mon Sep 17 00:00:00 2001 From: David Cohen Date: Mon, 5 Oct 2015 16:49:58 -0700 Subject: watchdog: intel-mid: add Magic Closure flag Adding WDIOF_MAGICCLOSE to Intel MID watchdog driver. Once the watchdog is opened, it makes sense to disable watchdog only if it was gracefully released. Signed-off-by: David Cohen Reviewed-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/intel-mid_wdt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/watchdog/intel-mid_wdt.c b/drivers/watchdog/intel-mid_wdt.c index 0a436b5d1e84..db36d12e2b52 100644 --- a/drivers/watchdog/intel-mid_wdt.c +++ b/drivers/watchdog/intel-mid_wdt.c @@ -101,7 +101,7 @@ static irqreturn_t mid_wdt_irq(int irq, void *dev_id) static const struct watchdog_info mid_wdt_info = { .identity = "Intel MID SCU watchdog", - .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT, + .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE, }; static const struct watchdog_ops mid_wdt_ops = { -- cgit v1.2.3-58-ga151 From bc794ac3b5836ee2b2420b0597f33538ad100be0 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 29 Sep 2015 01:27:25 -0700 Subject: watchdog: watchdog_dev: Use single variable name for struct watchdog_device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The current code uses 'wdd', wddev', and 'watchdog' as variable names for struct watchdog_device. This is confusing and makes it difficult to enhance the code. Replace it all with 'wdd'. Signed-off-by: Guenter Roeck Cc: Timo Kokkonen Acked-by: Uwe Kleine-König Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/watchdog_dev.c | 151 ++++++++++++++++++++-------------------- 1 file changed, 75 insertions(+), 76 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c index 6aaefbad303e..06171c73daf5 100644 --- a/drivers/watchdog/watchdog_dev.c +++ b/drivers/watchdog/watchdog_dev.c @@ -51,7 +51,7 @@ static struct watchdog_device *old_wdd; /* * watchdog_ping: ping the watchdog. - * @wddev: the watchdog device to ping + * @wdd: the watchdog device to ping * * If the watchdog has no own ping operation then it needs to be * restarted via the start operation. This wrapper function does @@ -59,65 +59,65 @@ static struct watchdog_device *old_wdd; * We only ping when the watchdog device is running. */ -static int watchdog_ping(struct watchdog_device *wddev) +static int watchdog_ping(struct watchdog_device *wdd) { int err = 0; - mutex_lock(&wddev->lock); + mutex_lock(&wdd->lock); - if (test_bit(WDOG_UNREGISTERED, &wddev->status)) { + if (test_bit(WDOG_UNREGISTERED, &wdd->status)) { err = -ENODEV; goto out_ping; } - if (!watchdog_active(wddev)) + if (!watchdog_active(wdd)) goto out_ping; - if (wddev->ops->ping) - err = wddev->ops->ping(wddev); /* ping the watchdog */ + if (wdd->ops->ping) + err = wdd->ops->ping(wdd); /* ping the watchdog */ else - err = wddev->ops->start(wddev); /* restart watchdog */ + err = wdd->ops->start(wdd); /* restart watchdog */ out_ping: - mutex_unlock(&wddev->lock); + mutex_unlock(&wdd->lock); return err; } /* * watchdog_start: wrapper to start the watchdog. - * @wddev: the watchdog device to start + * @wdd: the watchdog device to start * * Start the watchdog if it is not active and mark it active. * This function returns zero on success or a negative errno code for * failure. */ -static int watchdog_start(struct watchdog_device *wddev) +static int watchdog_start(struct watchdog_device *wdd) { int err = 0; - mutex_lock(&wddev->lock); + mutex_lock(&wdd->lock); - if (test_bit(WDOG_UNREGISTERED, &wddev->status)) { + if (test_bit(WDOG_UNREGISTERED, &wdd->status)) { err = -ENODEV; goto out_start; } - if (watchdog_active(wddev)) + if (watchdog_active(wdd)) goto out_start; - err = wddev->ops->start(wddev); + err = wdd->ops->start(wdd); if (err == 0) - set_bit(WDOG_ACTIVE, &wddev->status); + set_bit(WDOG_ACTIVE, &wdd->status); out_start: - mutex_unlock(&wddev->lock); + mutex_unlock(&wdd->lock); return err; } /* * watchdog_stop: wrapper to stop the watchdog. - * @wddev: the watchdog device to stop + * @wdd: the watchdog device to stop * * Stop the watchdog if it is still active and unmark it active. * This function returns zero on success or a negative errno code for @@ -125,155 +125,154 @@ out_start: * If the 'nowayout' feature was set, the watchdog cannot be stopped. */ -static int watchdog_stop(struct watchdog_device *wddev) +static int watchdog_stop(struct watchdog_device *wdd) { int err = 0; - mutex_lock(&wddev->lock); + mutex_lock(&wdd->lock); - if (test_bit(WDOG_UNREGISTERED, &wddev->status)) { + if (test_bit(WDOG_UNREGISTERED, &wdd->status)) { err = -ENODEV; goto out_stop; } - if (!watchdog_active(wddev)) + if (!watchdog_active(wdd)) goto out_stop; - if (test_bit(WDOG_NO_WAY_OUT, &wddev->status)) { - dev_info(wddev->dev, "nowayout prevents watchdog being stopped!\n"); + if (test_bit(WDOG_NO_WAY_OUT, &wdd->status)) { + dev_info(wdd->dev, "nowayout prevents watchdog being stopped!\n"); err = -EBUSY; goto out_stop; } - err = wddev->ops->stop(wddev); + err = wdd->ops->stop(wdd); if (err == 0) - clear_bit(WDOG_ACTIVE, &wddev->status); + clear_bit(WDOG_ACTIVE, &wdd->status); out_stop: - mutex_unlock(&wddev->lock); + mutex_unlock(&wdd->lock); return err; } /* * watchdog_get_status: wrapper to get the watchdog status - * @wddev: the watchdog device to get the status from + * @wdd: the watchdog device to get the status from * @status: the status of the watchdog device * * Get the watchdog's status flags. */ -static int watchdog_get_status(struct watchdog_device *wddev, +static int watchdog_get_status(struct watchdog_device *wdd, unsigned int *status) { int err = 0; *status = 0; - if (!wddev->ops->status) + if (!wdd->ops->status) return -EOPNOTSUPP; - mutex_lock(&wddev->lock); + mutex_lock(&wdd->lock); - if (test_bit(WDOG_UNREGISTERED, &wddev->status)) { + if (test_bit(WDOG_UNREGISTERED, &wdd->status)) { err = -ENODEV; goto out_status; } - *status = wddev->ops->status(wddev); + *status = wdd->ops->status(wdd); out_status: - mutex_unlock(&wddev->lock); + mutex_unlock(&wdd->lock); return err; } /* * watchdog_set_timeout: set the watchdog timer timeout - * @wddev: the watchdog device to set the timeout for + * @wdd: the watchdog device to set the timeout for * @timeout: timeout to set in seconds */ -static int watchdog_set_timeout(struct watchdog_device *wddev, +static int watchdog_set_timeout(struct watchdog_device *wdd, unsigned int timeout) { int err; - if ((wddev->ops->set_timeout == NULL) || - !(wddev->info->options & WDIOF_SETTIMEOUT)) + if (!wdd->ops->set_timeout || !(wdd->info->options & WDIOF_SETTIMEOUT)) return -EOPNOTSUPP; - if (watchdog_timeout_invalid(wddev, timeout)) + if (watchdog_timeout_invalid(wdd, timeout)) return -EINVAL; - mutex_lock(&wddev->lock); + mutex_lock(&wdd->lock); - if (test_bit(WDOG_UNREGISTERED, &wddev->status)) { + if (test_bit(WDOG_UNREGISTERED, &wdd->status)) { err = -ENODEV; goto out_timeout; } - err = wddev->ops->set_timeout(wddev, timeout); + err = wdd->ops->set_timeout(wdd, timeout); out_timeout: - mutex_unlock(&wddev->lock); + mutex_unlock(&wdd->lock); return err; } /* * watchdog_get_timeleft: wrapper to get the time left before a reboot - * @wddev: the watchdog device to get the remaining time from + * @wdd: the watchdog device to get the remaining time from * @timeleft: the time that's left * * Get the time before a watchdog will reboot (if not pinged). */ -static int watchdog_get_timeleft(struct watchdog_device *wddev, +static int watchdog_get_timeleft(struct watchdog_device *wdd, unsigned int *timeleft) { int err = 0; *timeleft = 0; - if (!wddev->ops->get_timeleft) + if (!wdd->ops->get_timeleft) return -EOPNOTSUPP; - mutex_lock(&wddev->lock); + mutex_lock(&wdd->lock); - if (test_bit(WDOG_UNREGISTERED, &wddev->status)) { + if (test_bit(WDOG_UNREGISTERED, &wdd->status)) { err = -ENODEV; goto out_timeleft; } - *timeleft = wddev->ops->get_timeleft(wddev); + *timeleft = wdd->ops->get_timeleft(wdd); out_timeleft: - mutex_unlock(&wddev->lock); + mutex_unlock(&wdd->lock); return err; } /* * watchdog_ioctl_op: call the watchdog drivers ioctl op if defined - * @wddev: the watchdog device to do the ioctl on + * @wdd: the watchdog device to do the ioctl on * @cmd: watchdog command * @arg: argument pointer */ -static int watchdog_ioctl_op(struct watchdog_device *wddev, unsigned int cmd, +static int watchdog_ioctl_op(struct watchdog_device *wdd, unsigned int cmd, unsigned long arg) { int err; - if (!wddev->ops->ioctl) + if (!wdd->ops->ioctl) return -ENOIOCTLCMD; - mutex_lock(&wddev->lock); + mutex_lock(&wdd->lock); - if (test_bit(WDOG_UNREGISTERED, &wddev->status)) { + if (test_bit(WDOG_UNREGISTERED, &wdd->status)) { err = -ENODEV; goto out_ioctl; } - err = wddev->ops->ioctl(wddev, cmd, arg); + err = wdd->ops->ioctl(wdd, cmd, arg); out_ioctl: - mutex_unlock(&wddev->lock); + mutex_unlock(&wdd->lock); return err; } @@ -513,43 +512,43 @@ static struct miscdevice watchdog_miscdev = { /* * watchdog_dev_register: register a watchdog device - * @watchdog: watchdog device + * @wdd: watchdog device * * Register a watchdog device including handling the legacy * /dev/watchdog node. /dev/watchdog is actually a miscdevice and * thus we set it up like that. */ -int watchdog_dev_register(struct watchdog_device *watchdog) +int watchdog_dev_register(struct watchdog_device *wdd) { int err, devno; - if (watchdog->id == 0) { - old_wdd = watchdog; - watchdog_miscdev.parent = watchdog->parent; + if (wdd->id == 0) { + old_wdd = wdd; + watchdog_miscdev.parent = wdd->parent; err = misc_register(&watchdog_miscdev); if (err != 0) { pr_err("%s: cannot register miscdev on minor=%d (err=%d).\n", - watchdog->info->identity, WATCHDOG_MINOR, err); + wdd->info->identity, WATCHDOG_MINOR, err); if (err == -EBUSY) pr_err("%s: a legacy watchdog module is probably present.\n", - watchdog->info->identity); + wdd->info->identity); old_wdd = NULL; return err; } } /* Fill in the data structures */ - devno = MKDEV(MAJOR(watchdog_devt), watchdog->id); - cdev_init(&watchdog->cdev, &watchdog_fops); - watchdog->cdev.owner = watchdog->ops->owner; + devno = MKDEV(MAJOR(watchdog_devt), wdd->id); + cdev_init(&wdd->cdev, &watchdog_fops); + wdd->cdev.owner = wdd->ops->owner; /* Add the device */ - err = cdev_add(&watchdog->cdev, devno, 1); + err = cdev_add(&wdd->cdev, devno, 1); if (err) { pr_err("watchdog%d unable to add device %d:%d\n", - watchdog->id, MAJOR(watchdog_devt), watchdog->id); - if (watchdog->id == 0) { + wdd->id, MAJOR(watchdog_devt), wdd->id); + if (wdd->id == 0) { misc_deregister(&watchdog_miscdev); old_wdd = NULL; } @@ -564,14 +563,14 @@ int watchdog_dev_register(struct watchdog_device *watchdog) * Unregister the watchdog and if needed the legacy /dev/watchdog device. */ -int watchdog_dev_unregister(struct watchdog_device *watchdog) +int watchdog_dev_unregister(struct watchdog_device *wdd) { - mutex_lock(&watchdog->lock); - set_bit(WDOG_UNREGISTERED, &watchdog->status); - mutex_unlock(&watchdog->lock); + mutex_lock(&wdd->lock); + set_bit(WDOG_UNREGISTERED, &wdd->status); + mutex_unlock(&wdd->lock); - cdev_del(&watchdog->cdev); - if (watchdog->id == 0) { + cdev_del(&wdd->cdev); + if (wdd->id == 0) { misc_deregister(&watchdog_miscdev); old_wdd = NULL; } -- cgit v1.2.3-58-ga151 From 5ef796639c2cacaebc07cf7e63bd20d64f7b3cb0 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Mon, 26 Oct 2015 14:07:58 +0200 Subject: watchdog: core: propagate ping error code to the user space Watchdog ping return errors are ignored by watchdog core, Whatchdog daemon should be informed about possible hardware error or underlaying device driver get unregistered. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Reviewed-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/watchdog_dev.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c index 06171c73daf5..56a649e66eb2 100644 --- a/drivers/watchdog/watchdog_dev.c +++ b/drivers/watchdog/watchdog_dev.c @@ -294,6 +294,7 @@ static ssize_t watchdog_write(struct file *file, const char __user *data, struct watchdog_device *wdd = file->private_data; size_t i; char c; + int err; if (len == 0) return 0; @@ -313,7 +314,9 @@ static ssize_t watchdog_write(struct file *file, const char __user *data, } /* someone wrote to us, so we send the watchdog a keepalive ping */ - watchdog_ping(wdd); + err = watchdog_ping(wdd); + if (err < 0) + return err; return len; } @@ -369,8 +372,7 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd, case WDIOC_KEEPALIVE: if (!(wdd->info->options & WDIOF_KEEPALIVEPING)) return -EOPNOTSUPP; - watchdog_ping(wdd); - return 0; + return watchdog_ping(wdd); case WDIOC_SETTIMEOUT: if (get_user(val, p)) return -EFAULT; @@ -380,7 +382,9 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd, /* If the watchdog is active then we send a keepalive ping * to make sure that the watchdog keep's running (and if * possible that it takes the new timeout) */ - watchdog_ping(wdd); + err = watchdog_ping(wdd); + if (err < 0) + return err; /* Fall */ case WDIOC_GETTIMEOUT: /* timeout == 0 means that we don't know the timeout */ -- cgit v1.2.3-58-ga151