diff options
Diffstat (limited to 'kernel/drivers/watchdog')
64 files changed, 2555 insertions, 423 deletions
diff --git a/kernel/drivers/watchdog/Kconfig b/kernel/drivers/watchdog/Kconfig index e5e7c5505..1c427beff 100644 --- a/kernel/drivers/watchdog/Kconfig +++ b/kernel/drivers/watchdog/Kconfig @@ -1,3 +1,4 @@ + # # Watchdog device configuration # @@ -96,6 +97,15 @@ config DA9063_WATCHDOG This driver can be built as a module. The module name is da9063_wdt. +config DA9062_WATCHDOG + tristate "Dialog DA9062 Watchdog" + depends on MFD_DA9062 + select WATCHDOG_CORE + help + Support for the watchdog in the DA9062 PMIC. + + This driver can be built as a module. The module name is da9062_wdt. + config GPIO_WATCHDOG tristate "Watchdog device controlled through GPIO-line" depends on OF_GPIO @@ -104,6 +114,17 @@ config GPIO_WATCHDOG If you say yes here you get support for watchdog device controlled through GPIO-line. +config GPIO_WATCHDOG_ARCH_INITCALL + bool "Register the watchdog as early as possible" + depends on GPIO_WATCHDOG=y + help + In some situations, the default initcall level (module_init) + in not early enough in the boot process to avoid the watchdog + to be triggered. + If you say yes here, the initcall level would be raised to + arch_initcall. + If in doubt, say N. + config MENF21BMC_WATCHDOG tristate "MEN 14F021P00 BMC Watchdog" depends on MFD_MENF21BMC @@ -167,8 +188,18 @@ config AT91SAM9X_WATCHDOG Watchdog timer embedded into AT91SAM9X and AT91CAP9 chips. This will reboot your system when the timeout is reached. +config SAMA5D4_WATCHDOG + tristate "Atmel SAMA5D4 Watchdog Timer" + depends on ARCH_AT91 + select WATCHDOG_CORE + help + Atmel SAMA5D4 watchdog timer is embedded into SAMA5D4 chips. + Its Watchdog Timer Mode Register can be written more than once. + This will reboot your system when the timeout is reached. + config CADENCE_WATCHDOG tristate "Cadence Watchdog Timer" + depends on HAS_IOMEM select WATCHDOG_CORE help Say Y here if you want to include support for the watchdog @@ -408,14 +439,14 @@ config TS72XX_WATCHDOG config MAX63XX_WATCHDOG tristate "Max63xx watchdog" - depends on ARM && HAS_IOMEM + depends on HAS_IOMEM select WATCHDOG_CORE help Support for memory mapped max63{69,70,71,72,73,74} watchdog timer. config IMX2_WDT tristate "IMX2+ Watchdog" - depends on ARCH_MXC + depends on ARCH_MXC || ARCH_LAYERSCAPE select REGMAP_MMIO select WATCHDOG_CORE help @@ -470,6 +501,18 @@ config SIRFSOC_WATCHDOG Support for CSR SiRFprimaII and SiRFatlasVI watchdog. When the watchdog triggers the system will be reset. +config ST_LPC_WATCHDOG + tristate "STMicroelectronics LPC Watchdog" + depends on ARCH_STI + depends on OF + select WATCHDOG_CORE + help + Say Y here to include STMicroelectronics Low Power Controller + (LPC) based Watchdog timer support. + + To compile this driver as a module, choose M here: the + module will be called st_lpc_wdt. + config TEGRA_WATCHDOG tristate "Tegra watchdog" depends on (ARCH_TEGRA || COMPILE_TEST) && HAS_IOMEM @@ -514,6 +557,27 @@ config MEDIATEK_WATCHDOG To compile this driver as a module, choose M here: the module will be called mtk_wdt. +config DIGICOLOR_WATCHDOG + tristate "Conexant Digicolor SoCs watchdog support" + depends on ARCH_DIGICOLOR + select WATCHDOG_CORE + help + Say Y here to include support for the watchdog timer + in Conexant Digicolor SoCs. + To compile this driver as a module, choose M here: the + module will be called digicolor_wdt. + +config LPC18XX_WATCHDOG + tristate "LPC18xx/43xx Watchdog" + depends on ARCH_LPC18XX || COMPILE_TEST + select WATCHDOG_CORE + help + Say Y here if to include support for the watchdog timer + in NXP LPC SoCs family, which includes LPC18xx/LPC43xx + processors. + To compile this driver as a module, choose M here: the + module will be called lpc18xx_wdt. + # AVR32 Architecture config AT32AP700X_WDT @@ -753,7 +817,9 @@ config ITCO_WDT tristate "Intel TCO Timer/Watchdog" depends on (X86 || IA64) && PCI select WATCHDOG_CORE - select LPC_ICH + depends on I2C || I2C=n + select LPC_ICH if !EXPERT + select I2C_I801 if !EXPERT && I2C ---help--- Hardware driver for the intel TCO timer based watchdog devices. These drivers are included in the Intel 82801 I/O Controller @@ -1247,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 @@ -1289,7 +1363,7 @@ config MPC5200_WDT config 8xxx_WDT tristate "MPC8xxx Platform Watchdog Timer" - depends on PPC_8xx || PPC_83xx || PPC_86xx + depends on PPC_8xx || PPC_83xx || PPC_86xx || PPC_MPC512x select WATCHDOG_CORE help This driver is for a SoC level watchdog that exists on some @@ -1343,7 +1417,7 @@ config BOOKE_WDT_DEFAULT_TIMEOUT config MEN_A21_WDT tristate "MEN A21 VME CPU Carrier Board Watchdog Timer" select WATCHDOG_CORE - depends on GPIOLIB + depends on GPIOLIB || COMPILE_TEST help Watchdog driver for MEN A21 VMEbus CPU Carrier Boards. diff --git a/kernel/drivers/watchdog/Makefile b/kernel/drivers/watchdog/Makefile index 5c19294d1..53d4827dd 100644 --- a/kernel/drivers/watchdog/Makefile +++ b/kernel/drivers/watchdog/Makefile @@ -41,6 +41,7 @@ obj-$(CONFIG_IXP4XX_WATCHDOG) += ixp4xx_wdt.o obj-$(CONFIG_KS8695_WATCHDOG) += ks8695_wdt.o obj-$(CONFIG_S3C2410_WATCHDOG) += s3c2410_wdt.o obj-$(CONFIG_SA1100_WATCHDOG) += sa1100_wdt.o +obj-$(CONFIG_SAMA5D4_WATCHDOG) += sama5d4_wdt.o obj-$(CONFIG_DW_WATCHDOG) += dw_wdt.o obj-$(CONFIG_EP93XX_WATCHDOG) += ep93xx_wdt.o obj-$(CONFIG_PNX4008_WATCHDOG) += pnx4008_wdt.o @@ -59,11 +60,15 @@ obj-$(CONFIG_RETU_WATCHDOG) += retu_wdt.o obj-$(CONFIG_BCM2835_WDT) += bcm2835_wdt.o obj-$(CONFIG_MOXART_WDT) += moxart_wdt.o obj-$(CONFIG_SIRFSOC_WATCHDOG) += sirfsoc_wdt.o +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_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 @@ -179,6 +184,7 @@ obj-$(CONFIG_XEN_WDT) += xen_wdt.o # Architecture Independent obj-$(CONFIG_DA9052_WATCHDOG) += da9052_wdt.o obj-$(CONFIG_DA9055_WATCHDOG) += da9055_wdt.o +obj-$(CONFIG_DA9062_WATCHDOG) += da9062_wdt.o obj-$(CONFIG_DA9063_WATCHDOG) += da9063_wdt.o obj-$(CONFIG_GPIO_WATCHDOG) += gpio_wdt.o obj-$(CONFIG_WM831X_WATCHDOG) += wm831x_wdt.o diff --git a/kernel/drivers/watchdog/at91rm9200_wdt.c b/kernel/drivers/watchdog/at91rm9200_wdt.c index 41cecb557..e12a797cb 100644 --- a/kernel/drivers/watchdog/at91rm9200_wdt.c +++ b/kernel/drivers/watchdog/at91rm9200_wdt.c @@ -244,7 +244,7 @@ static int at91wdt_probe(struct platform_device *pdev) } regmap_st = syscon_node_to_regmap(parent->of_node); - if (!regmap_st) + if (IS_ERR(regmap_st)) return -ENODEV; res = misc_register(&at91wdt_miscdev); @@ -269,9 +269,8 @@ static int at91wdt_remove(struct platform_device *pdev) if (res) dev_warn(dev, "failed to unregister restart handler\n"); - res = misc_deregister(&at91wdt_miscdev); - if (!res) - at91wdt_miscdev.parent = NULL; + misc_deregister(&at91wdt_miscdev); + at91wdt_miscdev.parent = NULL; return res; } diff --git a/kernel/drivers/watchdog/at91sam9_wdt.c b/kernel/drivers/watchdog/at91sam9_wdt.c index 1443b3c39..7e6acaf3e 100644 --- a/kernel/drivers/watchdog/at91sam9_wdt.c +++ b/kernel/drivers/watchdog/at91sam9_wdt.c @@ -17,6 +17,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include <linux/clk.h> #include <linux/errno.h> #include <linux/init.h> #include <linux/interrupt.h> @@ -40,9 +41,9 @@ #define DRV_NAME "AT91SAM9 Watchdog" #define wdt_read(wdt, field) \ - __raw_readl((wdt)->base + (field)) + readl_relaxed((wdt)->base + (field)) #define wdt_write(wtd, field, val) \ - __raw_writel((val), (wdt)->base + (field)) + writel_relaxed((val), (wdt)->base + (field)) /* AT91SAM9 watchdog runs a 12bit counter @ 256Hz, * use this to convert a watchdog @@ -90,6 +91,7 @@ struct at91wdt { unsigned long heartbeat; /* WDT heartbeat in jiffies */ bool nowayout; unsigned int irq; + struct clk *sclk; }; /* ......................................................................... */ @@ -352,15 +354,25 @@ static int __init at91wdt_probe(struct platform_device *pdev) if (IS_ERR(wdt->base)) return PTR_ERR(wdt->base); + wdt->sclk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(wdt->sclk)) + return PTR_ERR(wdt->sclk); + + err = clk_prepare_enable(wdt->sclk); + if (err) { + dev_err(&pdev->dev, "Could not enable slow clock\n"); + return err; + } + if (pdev->dev.of_node) { err = of_at91wdt_init(pdev->dev.of_node, wdt); if (err) - return err; + goto err_clk; } err = at91_wdt_init(pdev, wdt); if (err) - return err; + goto err_clk; platform_set_drvdata(pdev, wdt); @@ -368,6 +380,11 @@ static int __init at91wdt_probe(struct platform_device *pdev) wdt->wdd.timeout, wdt->nowayout); return 0; + +err_clk: + clk_disable_unprepare(wdt->sclk); + + return err; } static int __exit at91wdt_remove(struct platform_device *pdev) @@ -377,6 +394,7 @@ static int __exit at91wdt_remove(struct platform_device *pdev) pr_warn("I quit now, hardware will probably reboot!\n"); del_timer(&wdt->timer); + clk_disable_unprepare(wdt->sclk); return 0; } diff --git a/kernel/drivers/watchdog/at91sam9_wdt.h b/kernel/drivers/watchdog/at91sam9_wdt.h index c6fbb2e6c..b79a83b46 100644 --- a/kernel/drivers/watchdog/at91sam9_wdt.h +++ b/kernel/drivers/watchdog/at91sam9_wdt.h @@ -22,11 +22,13 @@ #define AT91_WDT_MR 0x04 /* Watchdog Mode Register */ #define AT91_WDT_WDV (0xfff << 0) /* Counter Value */ +#define AT91_WDT_SET_WDV(x) ((x) & AT91_WDT_WDV) #define AT91_WDT_WDFIEN (1 << 12) /* Fault Interrupt Enable */ #define AT91_WDT_WDRSTEN (1 << 13) /* Reset Processor */ #define AT91_WDT_WDRPROC (1 << 14) /* Timer Restart */ #define AT91_WDT_WDDIS (1 << 15) /* Watchdog Disable */ #define AT91_WDT_WDD (0xfff << 16) /* Delta Value */ +#define AT91_WDT_SET_WDD(x) (((x) << 16) & AT91_WDT_WDD) #define AT91_WDT_WDDBGHLT (1 << 28) /* Debug Halt */ #define AT91_WDT_WDIDLEHLT (1 << 29) /* Idle Halt */ diff --git a/kernel/drivers/watchdog/bcm2835_wdt.c b/kernel/drivers/watchdog/bcm2835_wdt.c index 2b5a9bbf8..8a5ce5b5a 100644 --- a/kernel/drivers/watchdog/bcm2835_wdt.c +++ b/kernel/drivers/watchdog/bcm2835_wdt.c @@ -13,30 +13,43 @@ * option) any later version. */ +#include <linux/delay.h> +#include <linux/reboot.h> #include <linux/types.h> #include <linux/module.h> #include <linux/io.h> #include <linux/watchdog.h> #include <linux/platform_device.h> #include <linux/of_address.h> +#include <linux/of_platform.h> #define PM_RSTC 0x1c +#define PM_RSTS 0x20 #define PM_WDOG 0x24 #define PM_PASSWORD 0x5a000000 #define PM_WDOG_TIME_SET 0x000fffff #define PM_RSTC_WRCFG_CLR 0xffffffcf +#define PM_RSTS_HADWRH_SET 0x00000040 #define PM_RSTC_WRCFG_SET 0x00000030 #define PM_RSTC_WRCFG_FULL_RESET 0x00000020 #define PM_RSTC_RESET 0x00000102 +/* + * The Raspberry Pi firmware uses the RSTS register to know which partiton + * to boot from. The partiton value is spread into bits 0, 2, 4, 6, 8, 10. + * Partiton 63 is a special partition used by the firmware to indicate halt. + */ +#define PM_RSTS_RASPBERRYPI_HALT 0x555 + #define SECS_TO_WDOG_TICKS(x) ((x) << 16) #define WDOG_TICKS_TO_SECS(x) ((x) >> 16) struct bcm2835_wdt { void __iomem *base; spinlock_t lock; + struct notifier_block restart_handler; }; static unsigned int heartbeat; @@ -106,6 +119,52 @@ static struct watchdog_device bcm2835_wdt_wdd = { .timeout = WDOG_TICKS_TO_SECS(PM_WDOG_TIME_SET), }; +static int +bcm2835_restart(struct notifier_block *this, unsigned long mode, void *cmd) +{ + struct bcm2835_wdt *wdt = container_of(this, struct bcm2835_wdt, + restart_handler); + u32 val; + + /* use a timeout of 10 ticks (~150us) */ + writel_relaxed(10 | PM_PASSWORD, wdt->base + PM_WDOG); + val = readl_relaxed(wdt->base + PM_RSTC); + val &= PM_RSTC_WRCFG_CLR; + val |= PM_PASSWORD | PM_RSTC_WRCFG_FULL_RESET; + writel_relaxed(val, wdt->base + PM_RSTC); + + /* No sleeping, possibly atomic. */ + mdelay(1); + + return 0; +} + +/* + * We can't really power off, but if we do the normal reset scheme, and + * indicate to bootcode.bin not to reboot, then most of the chip will be + * powered off. + */ +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); + u32 val; + + /* + * We set the watchdog hard reset bit here to distinguish this reset + * from the normal (full) reset. bootcode.bin will not reboot after a + * hard reset. + */ + val = readl_relaxed(wdt->base + PM_RSTS); + val |= PM_PASSWORD | PM_RSTS_RASPBERRYPI_HALT; + writel_relaxed(val, wdt->base + PM_RSTS); + + /* Continue with normal reset mechanism */ + bcm2835_restart(&wdt->restart_handler, REBOOT_HARD, NULL); +} + static int bcm2835_wdt_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -129,6 +188,7 @@ static int bcm2835_wdt_probe(struct platform_device *pdev) watchdog_set_drvdata(&bcm2835_wdt_wdd, wdt); watchdog_init_timeout(&bcm2835_wdt_wdd, heartbeat, dev); watchdog_set_nowayout(&bcm2835_wdt_wdd, nowayout); + bcm2835_wdt_wdd.parent = &pdev->dev; err = watchdog_register_device(&bcm2835_wdt_wdd); if (err) { dev_err(dev, "Failed to register watchdog device"); @@ -136,6 +196,12 @@ static int bcm2835_wdt_probe(struct platform_device *pdev) return err; } + wdt->restart_handler.notifier_call = bcm2835_restart; + wdt->restart_handler.priority = 128; + register_restart_handler(&wdt->restart_handler); + if (pm_power_off == NULL) + pm_power_off = bcm2835_power_off; + dev_info(dev, "Broadcom BCM2835 watchdog timer"); return 0; } @@ -144,6 +210,9 @@ static int bcm2835_wdt_remove(struct platform_device *pdev) { struct bcm2835_wdt *wdt = platform_get_drvdata(pdev); + unregister_restart_handler(&wdt->restart_handler); + if (pm_power_off == bcm2835_power_off) + pm_power_off = NULL; watchdog_unregister_device(&bcm2835_wdt_wdd); iounmap(wdt->base); diff --git a/kernel/drivers/watchdog/bcm47xx_wdt.c b/kernel/drivers/watchdog/bcm47xx_wdt.c index b28a072ab..4064a43f1 100644 --- a/kernel/drivers/watchdog/bcm47xx_wdt.c +++ b/kernel/drivers/watchdog/bcm47xx_wdt.c @@ -209,6 +209,7 @@ static int bcm47xx_wdt_probe(struct platform_device *pdev) wdt->wdd.info = &bcm47xx_wdt_info; wdt->wdd.timeout = WDT_DEFAULT_TIME; + wdt->wdd.parent = &pdev->dev; ret = wdt->wdd.ops->set_timeout(&wdt->wdd, timeout); if (ret) goto err_timer; diff --git a/kernel/drivers/watchdog/bcm7038_wdt.c b/kernel/drivers/watchdog/bcm7038_wdt.c new file mode 100644 index 000000000..4245b65d6 --- /dev/null +++ b/kernel/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 <linux/clk.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/pm.h> +#include <linux/watchdog.h> + +#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"); diff --git a/kernel/drivers/watchdog/bcm_kona_wdt.c b/kernel/drivers/watchdog/bcm_kona_wdt.c index 22d8ae657..e0c98423f 100644 --- a/kernel/drivers/watchdog/bcm_kona_wdt.c +++ b/kernel/drivers/watchdog/bcm_kona_wdt.c @@ -319,6 +319,7 @@ static int bcm_kona_wdt_probe(struct platform_device *pdev) spin_lock_init(&wdt->lock); platform_set_drvdata(pdev, wdt); watchdog_set_drvdata(&bcm_kona_wdt_wdd, wdt); + bcm_kona_wdt_wdd.parent = &pdev->dev; ret = bcm_kona_wdt_set_timeout_reg(&bcm_kona_wdt_wdd, 0); if (ret) { diff --git a/kernel/drivers/watchdog/booke_wdt.c b/kernel/drivers/watchdog/booke_wdt.c index e96b09b13..04da4b66c 100644 --- a/kernel/drivers/watchdog/booke_wdt.c +++ b/kernel/drivers/watchdog/booke_wdt.c @@ -186,8 +186,6 @@ static int booke_wdt_stop(struct watchdog_device *wdog) static int booke_wdt_set_timeout(struct watchdog_device *wdt_dev, unsigned int timeout) { - if (timeout > MAX_WDT_TIMEOUT) - return -EINVAL; wdt_dev->timeout = timeout; booke_wdt_set(wdt_dev); @@ -211,7 +209,6 @@ static struct watchdog_device booke_wdt_dev = { .info = &booke_wdt_info, .ops = &booke_wdt_ops, .min_timeout = 1, - .max_timeout = 0xFFFF }; static void __exit booke_wdt_exit(void) @@ -229,6 +226,7 @@ static int __init booke_wdt_init(void) booke_wdt_set_timeout(&booke_wdt_dev, period_to_sec(booke_wdt_period)); watchdog_set_nowayout(&booke_wdt_dev, nowayout); + booke_wdt_dev.max_timeout = MAX_WDT_TIMEOUT; if (booke_wdt_enabled) booke_wdt_start(&booke_wdt_dev); diff --git a/kernel/drivers/watchdog/coh901327_wdt.c b/kernel/drivers/watchdog/coh901327_wdt.c index ce12f437f..a099b77fc 100644 --- a/kernel/drivers/watchdog/coh901327_wdt.c +++ b/kernel/drivers/watchdog/coh901327_wdt.c @@ -358,6 +358,7 @@ static int __init coh901327_probe(struct platform_device *pdev) if (ret < 0) coh901327_wdt.timeout = 60; + coh901327_wdt.parent = &pdev->dev; ret = watchdog_register_device(&coh901327_wdt); if (ret == 0) dev_info(&pdev->dev, diff --git a/kernel/drivers/watchdog/da9052_wdt.c b/kernel/drivers/watchdog/da9052_wdt.c index 2e9589652..67e67977b 100644 --- a/kernel/drivers/watchdog/da9052_wdt.c +++ b/kernel/drivers/watchdog/da9052_wdt.c @@ -195,6 +195,7 @@ static int da9052_wdt_probe(struct platform_device *pdev) da9052_wdt->timeout = DA9052_DEF_TIMEOUT; da9052_wdt->info = &da9052_wdt_info; da9052_wdt->ops = &da9052_wdt_ops; + da9052_wdt->parent = &pdev->dev; watchdog_set_drvdata(da9052_wdt, driver_data); kref_init(&driver_data->kref); diff --git a/kernel/drivers/watchdog/da9055_wdt.c b/kernel/drivers/watchdog/da9055_wdt.c index 495089d8d..04d1430d9 100644 --- a/kernel/drivers/watchdog/da9055_wdt.c +++ b/kernel/drivers/watchdog/da9055_wdt.c @@ -161,6 +161,7 @@ static int da9055_wdt_probe(struct platform_device *pdev) da9055_wdt->timeout = DA9055_DEF_TIMEOUT; da9055_wdt->info = &da9055_wdt_info; da9055_wdt->ops = &da9055_wdt_ops; + da9055_wdt->parent = &pdev->dev; watchdog_set_nowayout(da9055_wdt, nowayout); watchdog_set_drvdata(da9055_wdt, driver_data); diff --git a/kernel/drivers/watchdog/da9062_wdt.c b/kernel/drivers/watchdog/da9062_wdt.c new file mode 100644 index 000000000..738611122 --- /dev/null +++ b/kernel/drivers/watchdog/da9062_wdt.c @@ -0,0 +1,254 @@ +/* + * da9062_wdt.c - WDT device driver for DA9062 + * Copyright (C) 2015 Dialog Semiconductor Ltd. + * + * 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 <linux/kernel.h> +#include <linux/module.h> +#include <linux/watchdog.h> +#include <linux/platform_device.h> +#include <linux/uaccess.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/jiffies.h> +#include <linux/mfd/da9062/registers.h> +#include <linux/mfd/da9062/core.h> +#include <linux/regmap.h> +#include <linux/of.h> + +static const unsigned int wdt_timeout[] = { 0, 2, 4, 8, 16, 32, 65, 131 }; +#define DA9062_TWDSCALE_DISABLE 0 +#define DA9062_TWDSCALE_MIN 1 +#define DA9062_TWDSCALE_MAX (ARRAY_SIZE(wdt_timeout) - 1) +#define DA9062_WDT_MIN_TIMEOUT wdt_timeout[DA9062_TWDSCALE_MIN] +#define DA9062_WDT_MAX_TIMEOUT wdt_timeout[DA9062_TWDSCALE_MAX] +#define DA9062_WDG_DEFAULT_TIMEOUT wdt_timeout[DA9062_TWDSCALE_MAX-1] +#define DA9062_RESET_PROTECTION_MS 300 + +struct da9062_watchdog { + struct da9062 *hw; + struct watchdog_device wdtdev; + unsigned long j_time_stamp; +}; + +static void da9062_set_window_start(struct da9062_watchdog *wdt) +{ + wdt->j_time_stamp = jiffies; +} + +static void da9062_apply_window_protection(struct da9062_watchdog *wdt) +{ + unsigned long delay = msecs_to_jiffies(DA9062_RESET_PROTECTION_MS); + unsigned long timeout = wdt->j_time_stamp + delay; + unsigned long now = jiffies; + unsigned int diff_ms; + + /* if time-limit has not elapsed then wait for remainder */ + if (time_before(now, timeout)) { + diff_ms = jiffies_to_msecs(timeout-now); + dev_dbg(wdt->hw->dev, + "Kicked too quickly. Delaying %u msecs\n", diff_ms); + msleep(diff_ms); + } +} + +static unsigned int da9062_wdt_timeout_to_sel(unsigned int secs) +{ + unsigned int i; + + for (i = DA9062_TWDSCALE_MIN; i <= DA9062_TWDSCALE_MAX; i++) { + if (wdt_timeout[i] >= secs) + return i; + } + + return DA9062_TWDSCALE_MAX; +} + +static int da9062_reset_watchdog_timer(struct da9062_watchdog *wdt) +{ + int ret; + + da9062_apply_window_protection(wdt); + + ret = regmap_update_bits(wdt->hw->regmap, + DA9062AA_CONTROL_F, + DA9062AA_WATCHDOG_MASK, + DA9062AA_WATCHDOG_MASK); + + da9062_set_window_start(wdt); + + return ret; +} + +static int da9062_wdt_update_timeout_register(struct da9062_watchdog *wdt, + unsigned int regval) +{ + struct da9062 *chip = wdt->hw; + int ret; + + ret = da9062_reset_watchdog_timer(wdt); + if (ret) + return ret; + + return regmap_update_bits(chip->regmap, + DA9062AA_CONTROL_D, + DA9062AA_TWDSCALE_MASK, + regval); +} + +static int da9062_wdt_start(struct watchdog_device *wdd) +{ + struct da9062_watchdog *wdt = watchdog_get_drvdata(wdd); + unsigned int selector; + int ret; + + selector = da9062_wdt_timeout_to_sel(wdt->wdtdev.timeout); + ret = da9062_wdt_update_timeout_register(wdt, selector); + if (ret) + dev_err(wdt->hw->dev, "Watchdog failed to start (err = %d)\n", + ret); + + return ret; +} + +static int da9062_wdt_stop(struct watchdog_device *wdd) +{ + struct da9062_watchdog *wdt = watchdog_get_drvdata(wdd); + int ret; + + ret = da9062_reset_watchdog_timer(wdt); + if (ret) { + dev_err(wdt->hw->dev, "Failed to ping the watchdog (err = %d)\n", + ret); + return ret; + } + + ret = regmap_update_bits(wdt->hw->regmap, + DA9062AA_CONTROL_D, + DA9062AA_TWDSCALE_MASK, + DA9062_TWDSCALE_DISABLE); + if (ret) + dev_err(wdt->hw->dev, "Watchdog failed to stop (err = %d)\n", + ret); + + return ret; +} + +static int da9062_wdt_ping(struct watchdog_device *wdd) +{ + struct da9062_watchdog *wdt = watchdog_get_drvdata(wdd); + int ret; + + ret = da9062_reset_watchdog_timer(wdt); + if (ret) + dev_err(wdt->hw->dev, "Failed to ping the watchdog (err = %d)\n", + ret); + + return ret; +} + +static int da9062_wdt_set_timeout(struct watchdog_device *wdd, + unsigned int timeout) +{ + struct da9062_watchdog *wdt = watchdog_get_drvdata(wdd); + unsigned int selector; + int ret; + + selector = da9062_wdt_timeout_to_sel(timeout); + ret = da9062_wdt_update_timeout_register(wdt, selector); + if (ret) + dev_err(wdt->hw->dev, "Failed to set watchdog timeout (err = %d)\n", + ret); + else + wdd->timeout = wdt_timeout[selector]; + + return ret; +} + +static const struct watchdog_info da9062_watchdog_info = { + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, + .identity = "DA9062 WDT", +}; + +static const struct watchdog_ops da9062_watchdog_ops = { + .owner = THIS_MODULE, + .start = da9062_wdt_start, + .stop = da9062_wdt_stop, + .ping = da9062_wdt_ping, + .set_timeout = da9062_wdt_set_timeout, +}; + +static int da9062_wdt_probe(struct platform_device *pdev) +{ + int ret; + struct da9062 *chip; + struct da9062_watchdog *wdt; + + chip = dev_get_drvdata(pdev->dev.parent); + if (!chip) + return -EINVAL; + + wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); + if (!wdt) + return -ENOMEM; + + wdt->hw = chip; + + wdt->wdtdev.info = &da9062_watchdog_info; + wdt->wdtdev.ops = &da9062_watchdog_ops; + wdt->wdtdev.min_timeout = DA9062_WDT_MIN_TIMEOUT; + wdt->wdtdev.max_timeout = DA9062_WDT_MAX_TIMEOUT; + wdt->wdtdev.timeout = DA9062_WDG_DEFAULT_TIMEOUT; + wdt->wdtdev.status = WATCHDOG_NOWAYOUT_INIT_STATUS; + wdt->wdtdev.parent = &pdev->dev; + + watchdog_set_drvdata(&wdt->wdtdev, wdt); + dev_set_drvdata(&pdev->dev, wdt); + + ret = watchdog_register_device(&wdt->wdtdev); + if (ret < 0) { + dev_err(wdt->hw->dev, + "watchdog registration failed (%d)\n", ret); + return ret; + } + + da9062_set_window_start(wdt); + + ret = da9062_wdt_ping(&wdt->wdtdev); + if (ret < 0) + watchdog_unregister_device(&wdt->wdtdev); + + return ret; +} + +static int da9062_wdt_remove(struct platform_device *pdev) +{ + struct da9062_watchdog *wdt = dev_get_drvdata(&pdev->dev); + + watchdog_unregister_device(&wdt->wdtdev); + return 0; +} + +static struct platform_driver da9062_wdt_driver = { + .probe = da9062_wdt_probe, + .remove = da9062_wdt_remove, + .driver = { + .name = "da9062-watchdog", + }, +}; +module_platform_driver(da9062_wdt_driver); + +MODULE_AUTHOR("S Twiss <stwiss.opensource@diasemi.com>"); +MODULE_DESCRIPTION("WDT device driver for Dialog DA9062"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:da9062-watchdog"); diff --git a/kernel/drivers/watchdog/da9063_wdt.c b/kernel/drivers/watchdog/da9063_wdt.c index e2fe2ebde..6bf130bd8 100644 --- a/kernel/drivers/watchdog/da9063_wdt.c +++ b/kernel/drivers/watchdog/da9063_wdt.c @@ -175,6 +175,7 @@ static int da9063_wdt_probe(struct platform_device *pdev) wdt->wdtdev.min_timeout = DA9063_WDT_MIN_TIMEOUT; wdt->wdtdev.max_timeout = DA9063_WDT_MAX_TIMEOUT; wdt->wdtdev.timeout = DA9063_WDG_TIMEOUT; + wdt->wdtdev.parent = &pdev->dev; wdt->wdtdev.status = WATCHDOG_NOWAYOUT_INIT_STATUS; diff --git a/kernel/drivers/watchdog/davinci_wdt.c b/kernel/drivers/watchdog/davinci_wdt.c index cfdf8a408..17454ca65 100644 --- a/kernel/drivers/watchdog/davinci_wdt.c +++ b/kernel/drivers/watchdog/davinci_wdt.c @@ -179,6 +179,7 @@ static int davinci_wdt_probe(struct platform_device *pdev) wdd->min_timeout = 1; wdd->max_timeout = MAX_HEARTBEAT; wdd->timeout = DEFAULT_HEARTBEAT; + wdd->parent = &pdev->dev; watchdog_init_timeout(wdd, heartbeat, dev); diff --git a/kernel/drivers/watchdog/diag288_wdt.c b/kernel/drivers/watchdog/diag288_wdt.c index a9a521014..3db9d0e06 100644 --- a/kernel/drivers/watchdog/diag288_wdt.c +++ b/kernel/drivers/watchdog/diag288_wdt.c @@ -29,6 +29,7 @@ #include <linux/watchdog.h> #include <linux/suspend.h> #include <asm/ebcdic.h> +#include <asm/diag.h> #include <linux/io.h> #include <linux/uaccess.h> @@ -94,12 +95,14 @@ static int __diag288(unsigned int func, unsigned int timeout, static int __diag288_vm(unsigned int func, unsigned int timeout, char *cmd, size_t len) { + diag_stat_inc(DIAG_STAT_X288); return __diag288(func, timeout, virt_to_phys(cmd), len); } static int __diag288_lpar(unsigned int func, unsigned int timeout, unsigned long action) { + diag_stat_inc(DIAG_STAT_X288); return __diag288(func, timeout, action, 0); } @@ -141,6 +144,7 @@ static int wdt_stop(struct watchdog_device *dev) { int ret; + diag_stat_inc(DIAG_STAT_X288); ret = __diag288(WDT_FUNC_CANCEL, 0, 0, 0); return ret; } diff --git a/kernel/drivers/watchdog/digicolor_wdt.c b/kernel/drivers/watchdog/digicolor_wdt.c new file mode 100644 index 000000000..50abe1bf6 --- /dev/null +++ b/kernel/drivers/watchdog/digicolor_wdt.c @@ -0,0 +1,206 @@ +/* + * Watchdog driver for Conexant Digicolor + * + * Copyright (C) 2015 Paradox Innovation Ltd. + * + * 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/types.h> +#include <linux/module.h> +#include <linux/io.h> +#include <linux/delay.h> +#include <linux/clk.h> +#include <linux/watchdog.h> +#include <linux/reboot.h> +#include <linux/platform_device.h> +#include <linux/of_address.h> + +#define TIMER_A_CONTROL 0 +#define TIMER_A_COUNT 4 + +#define TIMER_A_ENABLE_COUNT BIT(0) +#define TIMER_A_ENABLE_WATCHDOG BIT(1) + +struct dc_wdt { + void __iomem *base; + struct clk *clk; + struct notifier_block restart_handler; + spinlock_t lock; +}; + +static unsigned timeout; +module_param(timeout, uint, 0); +MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds"); + +static void dc_wdt_set(struct dc_wdt *wdt, u32 ticks) +{ + unsigned long flags; + + spin_lock_irqsave(&wdt->lock, flags); + + writel_relaxed(0, wdt->base + TIMER_A_CONTROL); + writel_relaxed(ticks, wdt->base + TIMER_A_COUNT); + writel_relaxed(TIMER_A_ENABLE_COUNT | TIMER_A_ENABLE_WATCHDOG, + wdt->base + TIMER_A_CONTROL); + + spin_unlock_irqrestore(&wdt->lock, flags); +} + +static int dc_restart_handler(struct notifier_block *this, unsigned long mode, + void *cmd) +{ + struct dc_wdt *wdt = container_of(this, struct dc_wdt, restart_handler); + + dc_wdt_set(wdt, 1); + /* wait for reset to assert... */ + mdelay(500); + + return NOTIFY_DONE; +} + +static int dc_wdt_start(struct watchdog_device *wdog) +{ + struct dc_wdt *wdt = watchdog_get_drvdata(wdog); + + dc_wdt_set(wdt, wdog->timeout * clk_get_rate(wdt->clk)); + + return 0; +} + +static int dc_wdt_stop(struct watchdog_device *wdog) +{ + struct dc_wdt *wdt = watchdog_get_drvdata(wdog); + + writel_relaxed(0, wdt->base + TIMER_A_CONTROL); + + return 0; +} + +static int dc_wdt_set_timeout(struct watchdog_device *wdog, unsigned int t) +{ + struct dc_wdt *wdt = watchdog_get_drvdata(wdog); + + dc_wdt_set(wdt, t * clk_get_rate(wdt->clk)); + wdog->timeout = t; + + return 0; +} + +static unsigned int dc_wdt_get_timeleft(struct watchdog_device *wdog) +{ + struct dc_wdt *wdt = watchdog_get_drvdata(wdog); + uint32_t count = readl_relaxed(wdt->base + TIMER_A_COUNT); + + return count / clk_get_rate(wdt->clk); +} + +static struct watchdog_ops dc_wdt_ops = { + .owner = THIS_MODULE, + .start = dc_wdt_start, + .stop = dc_wdt_stop, + .set_timeout = dc_wdt_set_timeout, + .get_timeleft = dc_wdt_get_timeleft, +}; + +static struct watchdog_info dc_wdt_info = { + .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE + | WDIOF_KEEPALIVEPING, + .identity = "Conexant Digicolor Watchdog", +}; + +static struct watchdog_device dc_wdt_wdd = { + .info = &dc_wdt_info, + .ops = &dc_wdt_ops, + .min_timeout = 1, +}; + +static int dc_wdt_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct dc_wdt *wdt; + int ret; + + wdt = devm_kzalloc(dev, sizeof(struct dc_wdt), GFP_KERNEL); + if (!wdt) + return -ENOMEM; + platform_set_drvdata(pdev, wdt); + + wdt->base = of_iomap(np, 0); + if (!wdt->base) { + dev_err(dev, "Failed to remap watchdog regs"); + return -ENODEV; + } + + wdt->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(wdt->clk)) { + ret = PTR_ERR(wdt->clk); + goto err_iounmap; + } + dc_wdt_wdd.max_timeout = U32_MAX / clk_get_rate(wdt->clk); + dc_wdt_wdd.timeout = dc_wdt_wdd.max_timeout; + dc_wdt_wdd.parent = &pdev->dev; + + spin_lock_init(&wdt->lock); + + watchdog_set_drvdata(&dc_wdt_wdd, wdt); + watchdog_init_timeout(&dc_wdt_wdd, timeout, dev); + ret = watchdog_register_device(&dc_wdt_wdd); + if (ret) { + dev_err(dev, "Failed to register watchdog device"); + goto err_iounmap; + } + + wdt->restart_handler.notifier_call = dc_restart_handler; + wdt->restart_handler.priority = 128; + ret = register_restart_handler(&wdt->restart_handler); + if (ret) + dev_warn(&pdev->dev, "cannot register restart handler\n"); + + return 0; + +err_iounmap: + iounmap(wdt->base); + return ret; +} + +static int dc_wdt_remove(struct platform_device *pdev) +{ + struct dc_wdt *wdt = platform_get_drvdata(pdev); + + unregister_restart_handler(&wdt->restart_handler); + watchdog_unregister_device(&dc_wdt_wdd); + iounmap(wdt->base); + + return 0; +} + +static void dc_wdt_shutdown(struct platform_device *pdev) +{ + dc_wdt_stop(&dc_wdt_wdd); +} + +static const struct of_device_id dc_wdt_of_match[] = { + { .compatible = "cnxt,cx92755-wdt", }, + {}, +}; +MODULE_DEVICE_TABLE(of, dc_wdt_of_match); + +static struct platform_driver dc_wdt_driver = { + .probe = dc_wdt_probe, + .remove = dc_wdt_remove, + .shutdown = dc_wdt_shutdown, + .driver = { + .name = "digicolor-wdt", + .of_match_table = dc_wdt_of_match, + }, +}; +module_platform_driver(dc_wdt_driver); + +MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>"); +MODULE_DESCRIPTION("Driver for Conexant Digicolor watchdog timer"); +MODULE_LICENSE("GPL"); diff --git a/kernel/drivers/watchdog/dw_wdt.c b/kernel/drivers/watchdog/dw_wdt.c index d0bb9499d..6ea063434 100644 --- a/kernel/drivers/watchdog/dw_wdt.c +++ b/kernel/drivers/watchdog/dw_wdt.c @@ -35,7 +35,6 @@ #include <linux/pm.h> #include <linux/platform_device.h> #include <linux/reboot.h> -#include <linux/spinlock.h> #include <linux/timer.h> #include <linux/uaccess.h> #include <linux/watchdog.h> @@ -61,7 +60,6 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started " #define WDT_TIMEOUT (HZ / 2) static struct { - spinlock_t lock; void __iomem *regs; struct clk *clk; unsigned long in_use; @@ -177,7 +175,6 @@ static int dw_wdt_open(struct inode *inode, struct file *filp) /* Make sure we don't get unloaded. */ __module_get(THIS_MODULE); - spin_lock(&dw_wdt.lock); if (!dw_wdt_is_enabled()) { /* * The watchdog is not currently enabled. Set the timeout to @@ -190,8 +187,6 @@ static int dw_wdt_open(struct inode *inode, struct file *filp) dw_wdt_set_next_heartbeat(); - spin_unlock(&dw_wdt.lock); - return nonseekable_open(inode, filp); } @@ -220,6 +215,7 @@ static ssize_t dw_wdt_write(struct file *filp, const char __user *buf, } dw_wdt_set_next_heartbeat(); + dw_wdt_keepalive(); mod_timer(&dw_wdt.timer, jiffies + WDT_TIMEOUT); return len; @@ -348,8 +344,6 @@ static int dw_wdt_drv_probe(struct platform_device *pdev) if (ret) return ret; - spin_lock_init(&dw_wdt.lock); - ret = misc_register(&dw_wdt_miscdev); if (ret) goto out_disable_clk; diff --git a/kernel/drivers/watchdog/ep93xx_wdt.c b/kernel/drivers/watchdog/ep93xx_wdt.c index 7a2cc7191..0a4d7cc05 100644 --- a/kernel/drivers/watchdog/ep93xx_wdt.c +++ b/kernel/drivers/watchdog/ep93xx_wdt.c @@ -132,6 +132,7 @@ static int ep93xx_wdt_probe(struct platform_device *pdev) val = readl(mmio_base + EP93XX_WATCHDOG); ep93xx_wdt_wdd.bootstatus = (val & 0x01) ? WDIOF_CARDRESET : 0; ep93xx_wdt_wdd.timeout = timeout; + ep93xx_wdt_wdd.parent = &pdev->dev; watchdog_set_nowayout(&ep93xx_wdt_wdd, nowayout); diff --git a/kernel/drivers/watchdog/gef_wdt.c b/kernel/drivers/watchdog/gef_wdt.c index cc1bdfc2f..006e23480 100644 --- a/kernel/drivers/watchdog/gef_wdt.c +++ b/kernel/drivers/watchdog/gef_wdt.c @@ -303,6 +303,7 @@ static const struct of_device_id gef_wdt_ids[] = { }, {}, }; +MODULE_DEVICE_TABLE(of, gef_wdt_ids); static struct platform_driver gef_wdt_driver = { .driver = { diff --git a/kernel/drivers/watchdog/gpio_wdt.c b/kernel/drivers/watchdog/gpio_wdt.c index cbc313d37..90d59d3f3 100644 --- a/kernel/drivers/watchdog/gpio_wdt.c +++ b/kernel/drivers/watchdog/gpio_wdt.c @@ -50,12 +50,41 @@ static void gpio_wdt_disable(struct gpio_wdt_priv *priv) gpio_direction_input(priv->gpio); } +static void gpio_wdt_hwping(unsigned long data) +{ + struct watchdog_device *wdd = (struct watchdog_device *)data; + struct gpio_wdt_priv *priv = watchdog_get_drvdata(wdd); + + if (priv->armed && time_after(jiffies, priv->last_jiffies + + msecs_to_jiffies(wdd->timeout * 1000))) { + dev_crit(wdd->dev, "Timer expired. System will reboot soon!\n"); + return; + } + + /* Restart timer */ + mod_timer(&priv->timer, jiffies + priv->hw_margin); + + switch (priv->hw_algo) { + case HW_ALGO_TOGGLE: + /* Toggle output pin */ + priv->state = !priv->state; + gpio_set_value_cansleep(priv->gpio, priv->state); + break; + case HW_ALGO_LEVEL: + /* Pulse */ + gpio_set_value_cansleep(priv->gpio, !priv->active_low); + udelay(1); + gpio_set_value_cansleep(priv->gpio, priv->active_low); + break; + } +} + static void gpio_wdt_start_impl(struct gpio_wdt_priv *priv) { priv->state = priv->active_low; gpio_direction_output(priv->gpio, priv->state); priv->last_jiffies = jiffies; - mod_timer(&priv->timer, priv->last_jiffies + priv->hw_margin); + gpio_wdt_hwping((unsigned long)&priv->wdd); } static int gpio_wdt_start(struct watchdog_device *wdd) @@ -97,35 +126,6 @@ static int gpio_wdt_set_timeout(struct watchdog_device *wdd, unsigned int t) return gpio_wdt_ping(wdd); } -static void gpio_wdt_hwping(unsigned long data) -{ - struct watchdog_device *wdd = (struct watchdog_device *)data; - struct gpio_wdt_priv *priv = watchdog_get_drvdata(wdd); - - if (priv->armed && time_after(jiffies, priv->last_jiffies + - msecs_to_jiffies(wdd->timeout * 1000))) { - dev_crit(wdd->dev, "Timer expired. System will reboot soon!\n"); - return; - } - - /* Restart timer */ - mod_timer(&priv->timer, jiffies + priv->hw_margin); - - switch (priv->hw_algo) { - case HW_ALGO_TOGGLE: - /* Toggle output pin */ - priv->state = !priv->state; - gpio_set_value_cansleep(priv->gpio, priv->state); - break; - case HW_ALGO_LEVEL: - /* Pulse */ - gpio_set_value_cansleep(priv->gpio, !priv->active_low); - udelay(1); - gpio_set_value_cansleep(priv->gpio, priv->active_low); - break; - } -} - static int gpio_wdt_notify_sys(struct notifier_block *nb, unsigned long code, void *unused) { @@ -182,10 +182,10 @@ static int gpio_wdt_probe(struct platform_device *pdev) ret = of_property_read_string(pdev->dev.of_node, "hw_algo", &algo); if (ret) return ret; - if (!strncmp(algo, "toggle", 6)) { + if (!strcmp(algo, "toggle")) { priv->hw_algo = HW_ALGO_TOGGLE; f = GPIOF_IN; - } else if (!strncmp(algo, "level", 5)) { + } else if (!strcmp(algo, "level")) { priv->hw_algo = HW_ALGO_LEVEL; f = priv->active_low ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW; } else { @@ -217,6 +217,7 @@ static int gpio_wdt_probe(struct platform_device *pdev) priv->wdd.ops = &gpio_wdt_ops; priv->wdd.min_timeout = SOFT_TIMEOUT_MIN; priv->wdd.max_timeout = SOFT_TIMEOUT_MAX; + priv->wdd.parent = &pdev->dev; if (watchdog_init_timeout(&priv->wdd, 0, &pdev->dev) < 0) priv->wdd.timeout = SOFT_TIMEOUT_DEF; @@ -267,7 +268,16 @@ static struct platform_driver gpio_wdt_driver = { .probe = gpio_wdt_probe, .remove = gpio_wdt_remove, }; + +#ifdef CONFIG_GPIO_WATCHDOG_ARCH_INITCALL +static int __init gpio_wdt_init(void) +{ + return platform_driver_register(&gpio_wdt_driver); +} +arch_initcall(gpio_wdt_init); +#else module_platform_driver(gpio_wdt_driver); +#endif MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>"); MODULE_DESCRIPTION("GPIO Watchdog"); diff --git a/kernel/drivers/watchdog/hpwdt.c b/kernel/drivers/watchdog/hpwdt.c index ada3e44f9..286369d4f 100644 --- a/kernel/drivers/watchdog/hpwdt.c +++ b/kernel/drivers/watchdog/hpwdt.c @@ -588,7 +588,7 @@ static long hpwdt_ioctl(struct file *file, unsigned int cmd, { void __user *argp = (void __user *)arg; int __user *p = argp; - int new_margin; + int new_margin, options; int ret = -ENOTTY; switch (cmd) { @@ -608,6 +608,20 @@ static long hpwdt_ioctl(struct file *file, unsigned int cmd, ret = 0; break; + case WDIOC_SETOPTIONS: + ret = get_user(options, p); + if (ret) + break; + + if (options & WDIOS_DISABLECARD) + hpwdt_stop(); + + if (options & WDIOS_ENABLECARD) { + hpwdt_start(); + hpwdt_ping(); + } + break; + case WDIOC_SETTIMEOUT: ret = get_user(new_margin, p); if (ret) diff --git a/kernel/drivers/watchdog/iTCO_wdt.c b/kernel/drivers/watchdog/iTCO_wdt.c index 3c3fd417d..0acc6c5f7 100644 --- a/kernel/drivers/watchdog/iTCO_wdt.c +++ b/kernel/drivers/watchdog/iTCO_wdt.c @@ -66,8 +66,7 @@ #include <linux/spinlock.h> /* For spin_lock/spin_unlock/... */ #include <linux/uaccess.h> /* For copy_to_user/put_user/... */ #include <linux/io.h> /* For inb/outb/... */ -#include <linux/mfd/core.h> -#include <linux/mfd/lpc_ich.h> +#include <linux/platform_data/itco_wdt.h> #include "iTCO_vendor.h" @@ -146,59 +145,67 @@ static inline unsigned int ticks_to_seconds(int ticks) return iTCO_wdt_private.iTCO_version == 3 ? ticks : (ticks * 6) / 10; } +static inline u32 no_reboot_bit(void) +{ + u32 enable_bit; + + switch (iTCO_wdt_private.iTCO_version) { + case 3: + enable_bit = 0x00000010; + break; + case 2: + enable_bit = 0x00000020; + break; + case 4: + case 1: + default: + enable_bit = 0x00000002; + break; + } + + return enable_bit; +} + static void iTCO_wdt_set_NO_REBOOT_bit(void) { u32 val32; /* Set the NO_REBOOT bit: this disables reboots */ - if (iTCO_wdt_private.iTCO_version == 3) { - val32 = readl(iTCO_wdt_private.gcs_pmc); - val32 |= 0x00000010; - writel(val32, iTCO_wdt_private.gcs_pmc); - } else if (iTCO_wdt_private.iTCO_version == 2) { + if (iTCO_wdt_private.iTCO_version >= 2) { val32 = readl(iTCO_wdt_private.gcs_pmc); - val32 |= 0x00000020; + val32 |= no_reboot_bit(); writel(val32, iTCO_wdt_private.gcs_pmc); } else if (iTCO_wdt_private.iTCO_version == 1) { pci_read_config_dword(iTCO_wdt_private.pdev, 0xd4, &val32); - val32 |= 0x00000002; + val32 |= no_reboot_bit(); pci_write_config_dword(iTCO_wdt_private.pdev, 0xd4, val32); } } static int iTCO_wdt_unset_NO_REBOOT_bit(void) { - int ret = 0; - u32 val32; + u32 enable_bit = no_reboot_bit(); + u32 val32 = 0; /* Unset the NO_REBOOT bit: this enables reboots */ - if (iTCO_wdt_private.iTCO_version == 3) { - val32 = readl(iTCO_wdt_private.gcs_pmc); - val32 &= 0xffffffef; - writel(val32, iTCO_wdt_private.gcs_pmc); - - val32 = readl(iTCO_wdt_private.gcs_pmc); - if (val32 & 0x00000010) - ret = -EIO; - } else if (iTCO_wdt_private.iTCO_version == 2) { + if (iTCO_wdt_private.iTCO_version >= 2) { val32 = readl(iTCO_wdt_private.gcs_pmc); - val32 &= 0xffffffdf; + val32 &= ~enable_bit; writel(val32, iTCO_wdt_private.gcs_pmc); val32 = readl(iTCO_wdt_private.gcs_pmc); - if (val32 & 0x00000020) - ret = -EIO; } else if (iTCO_wdt_private.iTCO_version == 1) { pci_read_config_dword(iTCO_wdt_private.pdev, 0xd4, &val32); - val32 &= 0xfffffffd; + val32 &= ~enable_bit; pci_write_config_dword(iTCO_wdt_private.pdev, 0xd4, val32); pci_read_config_dword(iTCO_wdt_private.pdev, 0xd4, &val32); - if (val32 & 0x00000002) - ret = -EIO; } - return ret; /* returns: 0 = OK, -EIO = Error */ + if (val32 & enable_bit) + return -EIO; + + return 0; } static int iTCO_wdt_start(struct watchdog_device *wd_dev) @@ -418,9 +425,9 @@ static int iTCO_wdt_probe(struct platform_device *dev) { int ret = -ENODEV; unsigned long val32; - struct lpc_ich_info *ich_info = dev_get_platdata(&dev->dev); + struct itco_wdt_platform_data *pdata = dev_get_platdata(&dev->dev); - if (!ich_info) + if (!pdata) goto out; spin_lock_init(&iTCO_wdt_private.io_lock); @@ -435,7 +442,7 @@ static int iTCO_wdt_probe(struct platform_device *dev) if (!iTCO_wdt_private.smi_res) goto out; - iTCO_wdt_private.iTCO_version = ich_info->iTCO_version; + iTCO_wdt_private.iTCO_version = pdata->version; iTCO_wdt_private.dev = dev; iTCO_wdt_private.pdev = to_pci_dev(dev->dev.parent); @@ -501,15 +508,24 @@ static int iTCO_wdt_probe(struct platform_device *dev) } pr_info("Found a %s TCO device (Version=%d, TCOBASE=0x%04llx)\n", - ich_info->name, ich_info->iTCO_version, (u64)TCOBASE); + pdata->name, pdata->version, (u64)TCOBASE); /* Clear out the (probably old) status */ - if (iTCO_wdt_private.iTCO_version == 3) { + switch (iTCO_wdt_private.iTCO_version) { + case 4: + outw(0x0008, TCO1_STS); /* Clear the Time Out Status bit */ + outw(0x0002, TCO2_STS); /* Clear SECOND_TO_STS bit */ + break; + case 3: outl(0x20008, TCO1_STS); - } else { + break; + case 2: + case 1: + default: outw(0x0008, TCO1_STS); /* Clear the Time Out Status bit */ outw(0x0002, TCO2_STS); /* Clear SECOND_TO_STS bit */ outw(0x0004, TCO2_STS); /* Clear BOOT_STS bit */ + break; } iTCO_wdt_watchdog_dev.bootstatus = 0; diff --git a/kernel/drivers/watchdog/ie6xx_wdt.c b/kernel/drivers/watchdog/ie6xx_wdt.c index 9bc39ae51..78c2541f5 100644 --- a/kernel/drivers/watchdog/ie6xx_wdt.c +++ b/kernel/drivers/watchdog/ie6xx_wdt.c @@ -267,6 +267,7 @@ static int ie6xx_wdt_probe(struct platform_device *pdev) ie6xx_wdt_dev.timeout = timeout; watchdog_set_nowayout(&ie6xx_wdt_dev, nowayout); + ie6xx_wdt_dev.parent = &pdev->dev; spin_lock_init(&ie6xx_wdt_data.unlock_sequence); diff --git a/kernel/drivers/watchdog/imgpdc_wdt.c b/kernel/drivers/watchdog/imgpdc_wdt.c index 0deaa4f97..15ab07230 100644 --- a/kernel/drivers/watchdog/imgpdc_wdt.c +++ b/kernel/drivers/watchdog/imgpdc_wdt.c @@ -9,6 +9,35 @@ * * Based on drivers/watchdog/sunxi_wdt.c Copyright (c) 2013 Carlo Caione * 2012 Henrik Nordstrom + * + * Notes + * ----- + * The timeout value is rounded to the next power of two clock cycles. + * This is configured using the PDC_WDT_CONFIG register, according to this + * formula: + * + * timeout = 2^(delay + 1) clock cycles + * + * Where 'delay' is the value written in PDC_WDT_CONFIG register. + * + * Therefore, the hardware only allows to program watchdog timeouts, expressed + * as a power of two number of watchdog clock cycles. The current implementation + * guarantees that the actual watchdog timeout will be _at least_ the value + * programmed in the imgpdg_wdt driver. + * + * The following table shows how the user-configured timeout relates + * to the actual hardware timeout (watchdog clock @ 40000 Hz): + * + * input timeout | WD_DELAY | actual timeout + * ----------------------------------- + * 10 | 18 | 13 seconds + * 20 | 19 | 26 seconds + * 30 | 20 | 52 seconds + * 60 | 21 | 104 seconds + * + * Albeit coarse, this granularity would suffice most watchdog uses. + * If the platform allows it, the user should be able to change the watchdog + * clock rate and achieve a finer timeout granularity. */ #include <linux/clk.h> @@ -16,6 +45,7 @@ #include <linux/log2.h> #include <linux/module.h> #include <linux/platform_device.h> +#include <linux/reboot.h> #include <linux/slab.h> #include <linux/watchdog.h> @@ -42,7 +72,7 @@ #define PDC_WDT_MIN_TIMEOUT 1 #define PDC_WDT_DEF_TIMEOUT 64 -static int heartbeat = PDC_WDT_DEF_TIMEOUT; +static int heartbeat; module_param(heartbeat, int, 0); MODULE_PARM_DESC(heartbeat, "Watchdog heartbeats in seconds " "(default=" __MODULE_STRING(PDC_WDT_DEF_TIMEOUT) ")"); @@ -57,6 +87,7 @@ struct pdc_wdt_dev { struct clk *wdt_clk; struct clk *sys_clk; void __iomem *base; + struct notifier_block restart_handler; }; static int pdc_wdt_keepalive(struct watchdog_device *wdt_dev) @@ -84,18 +115,24 @@ static int pdc_wdt_stop(struct watchdog_device *wdt_dev) return 0; } +static void __pdc_wdt_set_timeout(struct pdc_wdt_dev *wdt) +{ + unsigned long clk_rate = clk_get_rate(wdt->wdt_clk); + unsigned int val; + + val = readl(wdt->base + PDC_WDT_CONFIG) & ~PDC_WDT_CONFIG_DELAY_MASK; + val |= order_base_2(wdt->wdt_dev.timeout * clk_rate) - 1; + writel(val, wdt->base + PDC_WDT_CONFIG); +} + static int pdc_wdt_set_timeout(struct watchdog_device *wdt_dev, unsigned int new_timeout) { - unsigned int val; struct pdc_wdt_dev *wdt = watchdog_get_drvdata(wdt_dev); - unsigned long clk_rate = clk_get_rate(wdt->wdt_clk); wdt->wdt_dev.timeout = new_timeout; - val = readl(wdt->base + PDC_WDT_CONFIG) & ~PDC_WDT_CONFIG_DELAY_MASK; - val |= order_base_2(new_timeout * clk_rate) - 1; - writel(val, wdt->base + PDC_WDT_CONFIG); + __pdc_wdt_set_timeout(wdt); return 0; } @@ -106,6 +143,8 @@ static int pdc_wdt_start(struct watchdog_device *wdt_dev) unsigned int val; struct pdc_wdt_dev *wdt = watchdog_get_drvdata(wdt_dev); + __pdc_wdt_set_timeout(wdt); + val = readl(wdt->base + PDC_WDT_CONFIG); val |= PDC_WDT_CONFIG_ENABLE; writel(val, wdt->base + PDC_WDT_CONFIG); @@ -128,8 +167,21 @@ static const struct watchdog_ops pdc_wdt_ops = { .set_timeout = pdc_wdt_set_timeout, }; +static int pdc_wdt_restart(struct notifier_block *this, unsigned long mode, + void *cmd) +{ + struct pdc_wdt_dev *wdt = container_of(this, struct pdc_wdt_dev, + restart_handler); + + /* Assert SOFT_RESET */ + writel(0x1, wdt->base + PDC_WDT_SOFT_RESET); + + return NOTIFY_OK; +} + static int pdc_wdt_probe(struct platform_device *pdev) { + u64 div; int ret, val; unsigned long clk_rate; struct resource *res; @@ -189,16 +241,15 @@ static int pdc_wdt_probe(struct platform_device *pdev) pdc_wdt->wdt_dev.info = &pdc_wdt_info; pdc_wdt->wdt_dev.ops = &pdc_wdt_ops; - pdc_wdt->wdt_dev.max_timeout = 1 << PDC_WDT_CONFIG_DELAY_MASK; + + div = 1ULL << (PDC_WDT_CONFIG_DELAY_MASK + 1); + do_div(div, clk_rate); + pdc_wdt->wdt_dev.max_timeout = div; + pdc_wdt->wdt_dev.timeout = PDC_WDT_DEF_TIMEOUT; pdc_wdt->wdt_dev.parent = &pdev->dev; watchdog_set_drvdata(&pdc_wdt->wdt_dev, pdc_wdt); - ret = watchdog_init_timeout(&pdc_wdt->wdt_dev, heartbeat, &pdev->dev); - if (ret < 0) { - pdc_wdt->wdt_dev.timeout = pdc_wdt->wdt_dev.max_timeout; - dev_warn(&pdev->dev, - "Initial timeout out of range! setting max timeout\n"); - } + watchdog_init_timeout(&pdc_wdt->wdt_dev, heartbeat, &pdev->dev); pdc_wdt_stop(&pdc_wdt->wdt_dev); @@ -238,6 +289,13 @@ static int pdc_wdt_probe(struct platform_device *pdev) if (ret) goto disable_wdt_clk; + pdc_wdt->restart_handler.notifier_call = pdc_wdt_restart; + pdc_wdt->restart_handler.priority = 128; + ret = register_restart_handler(&pdc_wdt->restart_handler); + if (ret) + dev_warn(&pdev->dev, "failed to register restart handler: %d\n", + ret); + return 0; disable_wdt_clk: @@ -258,6 +316,7 @@ static int pdc_wdt_remove(struct platform_device *pdev) { struct pdc_wdt_dev *pdc_wdt = platform_get_drvdata(pdev); + unregister_restart_handler(&pdc_wdt->restart_handler); pdc_wdt_stop(&pdc_wdt->wdt_dev); watchdog_unregister_device(&pdc_wdt->wdt_dev); clk_disable_unprepare(pdc_wdt->wdt_clk); diff --git a/kernel/drivers/watchdog/imx2_wdt.c b/kernel/drivers/watchdog/imx2_wdt.c index 5e6d808d3..29ef719a6 100644 --- a/kernel/drivers/watchdog/imx2_wdt.c +++ b/kernel/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); @@ -166,6 +166,8 @@ static int imx2_wdt_set_timeout(struct watchdog_device *wdog, { struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog); + wdog->timeout = new_timeout; + regmap_update_bits(wdev->regmap, IMX2_WDT_WCR, IMX2_WDT_WCR_WT, WDOG_SEC_TO_COUNT(new_timeout)); return 0; @@ -256,8 +258,11 @@ static int __init imx2_wdt_probe(struct platform_device *pdev) wdog->ops = &imx2_wdt_ops; wdog->min_timeout = 1; wdog->max_timeout = IMX2_WDT_MAX_TIME; + wdog->parent = &pdev->dev; - clk_prepare_enable(wdev->clk); + ret = clk_prepare_enable(wdev->clk); + if (ret) + return ret; regmap_read(wdev->regmap, IMX2_WDT_WRSR, &val); wdog->bootstatus = val & IMX2_WDT_WRSR_TOUT ? WDIOF_CARDRESET : 0; @@ -286,7 +291,7 @@ static int __init imx2_wdt_probe(struct platform_device *pdev) ret = watchdog_register_device(wdog); if (ret) { dev_err(&pdev->dev, "cannot register watchdog device\n"); - return ret; + goto disable_clk; } wdev->restart_handler.notifier_call = imx2_restart_handler; @@ -299,6 +304,10 @@ static int __init imx2_wdt_probe(struct platform_device *pdev) wdog->timeout, nowayout); return 0; + +disable_clk: + clk_disable_unprepare(wdev->clk); + return ret; } static int __exit imx2_wdt_remove(struct platform_device *pdev) @@ -362,8 +371,11 @@ static int imx2_wdt_resume(struct device *dev) { struct watchdog_device *wdog = dev_get_drvdata(dev); struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog); + int ret; - clk_prepare_enable(wdev->clk); + ret = clk_prepare_enable(wdev->clk); + if (ret) + return ret; if (watchdog_active(wdog) && !imx2_wdt_is_running(wdev)) { /* diff --git a/kernel/drivers/watchdog/intel-mid_wdt.c b/kernel/drivers/watchdog/intel-mid_wdt.c index 84f6701c3..db36d12e2 100644 --- a/kernel/drivers/watchdog/intel-mid_wdt.c +++ b/kernel/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 = { @@ -137,6 +137,7 @@ static int mid_wdt_probe(struct platform_device *pdev) wdt_dev->min_timeout = MID_WDT_TIMEOUT_MIN; wdt_dev->max_timeout = MID_WDT_TIMEOUT_MAX; wdt_dev->timeout = MID_WDT_DEFAULT_TIMEOUT; + wdt_dev->parent = &pdev->dev; watchdog_set_drvdata(wdt_dev, &pdev->dev); platform_set_drvdata(pdev, wdt_dev); diff --git a/kernel/drivers/watchdog/jz4740_wdt.c b/kernel/drivers/watchdog/jz4740_wdt.c index 4c2cc09c0..6a7d5c365 100644 --- a/kernel/drivers/watchdog/jz4740_wdt.c +++ b/kernel/drivers/watchdog/jz4740_wdt.c @@ -174,6 +174,7 @@ static int jz4740_wdt_probe(struct platform_device *pdev) jz4740_wdt->timeout = heartbeat; jz4740_wdt->min_timeout = 1; jz4740_wdt->max_timeout = MAX_HEARTBEAT; + jz4740_wdt->parent = &pdev->dev; watchdog_set_nowayout(jz4740_wdt, nowayout); watchdog_set_drvdata(jz4740_wdt, drvdata); diff --git a/kernel/drivers/watchdog/ks8695_wdt.c b/kernel/drivers/watchdog/ks8695_wdt.c index b7ea39b45..1e41818a4 100644 --- a/kernel/drivers/watchdog/ks8695_wdt.c +++ b/kernel/drivers/watchdog/ks8695_wdt.c @@ -254,13 +254,10 @@ static int ks8695wdt_probe(struct platform_device *pdev) static int ks8695wdt_remove(struct platform_device *pdev) { - int res; - - res = misc_deregister(&ks8695wdt_miscdev); - if (!res) - ks8695wdt_miscdev.parent = NULL; + misc_deregister(&ks8695wdt_miscdev); + ks8695wdt_miscdev.parent = NULL; - return res; + return 0; } static void ks8695wdt_shutdown(struct platform_device *pdev) diff --git a/kernel/drivers/watchdog/lpc18xx_wdt.c b/kernel/drivers/watchdog/lpc18xx_wdt.c new file mode 100644 index 000000000..ab7b8b185 --- /dev/null +++ b/kernel/drivers/watchdog/lpc18xx_wdt.c @@ -0,0 +1,340 @@ +/* + * NXP LPC18xx Watchdog Timer (WDT) + * + * Copyright (c) 2015 Ariel D'Alessandro <ariel@vanguardiasur.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. + * + * Notes + * ----- + * The Watchdog consists of a fixed divide-by-4 clock pre-scaler and a 24-bit + * counter which decrements on every clock cycle. + */ + +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/reboot.h> +#include <linux/watchdog.h> + +/* Registers */ +#define LPC18XX_WDT_MOD 0x00 +#define LPC18XX_WDT_MOD_WDEN BIT(0) +#define LPC18XX_WDT_MOD_WDRESET BIT(1) + +#define LPC18XX_WDT_TC 0x04 +#define LPC18XX_WDT_TC_MIN 0xff +#define LPC18XX_WDT_TC_MAX 0xffffff + +#define LPC18XX_WDT_FEED 0x08 +#define LPC18XX_WDT_FEED_MAGIC1 0xaa +#define LPC18XX_WDT_FEED_MAGIC2 0x55 + +#define LPC18XX_WDT_TV 0x0c + +/* Clock pre-scaler */ +#define LPC18XX_WDT_CLK_DIV 4 + +/* Timeout values in seconds */ +#define LPC18XX_WDT_DEF_TIMEOUT 30U + +static int heartbeat; +module_param(heartbeat, int, 0); +MODULE_PARM_DESC(heartbeat, "Watchdog heartbeats in seconds (default=" + __MODULE_STRING(LPC18XX_WDT_DEF_TIMEOUT) ")"); + +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 lpc18xx_wdt_dev { + struct watchdog_device wdt_dev; + struct clk *reg_clk; + struct clk *wdt_clk; + unsigned long clk_rate; + void __iomem *base; + struct timer_list timer; + struct notifier_block restart_handler; + spinlock_t lock; +}; + +static int lpc18xx_wdt_feed(struct watchdog_device *wdt_dev) +{ + struct lpc18xx_wdt_dev *lpc18xx_wdt = watchdog_get_drvdata(wdt_dev); + unsigned long flags; + + /* + * An abort condition will occur if an interrupt happens during the feed + * sequence. + */ + spin_lock_irqsave(&lpc18xx_wdt->lock, flags); + writel(LPC18XX_WDT_FEED_MAGIC1, lpc18xx_wdt->base + LPC18XX_WDT_FEED); + writel(LPC18XX_WDT_FEED_MAGIC2, lpc18xx_wdt->base + LPC18XX_WDT_FEED); + spin_unlock_irqrestore(&lpc18xx_wdt->lock, flags); + + return 0; +} + +static void lpc18xx_wdt_timer_feed(unsigned long data) +{ + struct watchdog_device *wdt_dev = (struct watchdog_device *)data; + struct lpc18xx_wdt_dev *lpc18xx_wdt = watchdog_get_drvdata(wdt_dev); + + lpc18xx_wdt_feed(wdt_dev); + + /* Use safe value (1/2 of real timeout) */ + mod_timer(&lpc18xx_wdt->timer, jiffies + + msecs_to_jiffies((wdt_dev->timeout * MSEC_PER_SEC) / 2)); +} + +/* + * Since LPC18xx Watchdog cannot be disabled in hardware, we must keep feeding + * it with a timer until userspace watchdog software takes over. + */ +static int lpc18xx_wdt_stop(struct watchdog_device *wdt_dev) +{ + lpc18xx_wdt_timer_feed((unsigned long)wdt_dev); + + return 0; +} + +static void __lpc18xx_wdt_set_timeout(struct lpc18xx_wdt_dev *lpc18xx_wdt) +{ + unsigned int val; + + val = DIV_ROUND_UP(lpc18xx_wdt->wdt_dev.timeout * lpc18xx_wdt->clk_rate, + LPC18XX_WDT_CLK_DIV); + writel(val, lpc18xx_wdt->base + LPC18XX_WDT_TC); +} + +static int lpc18xx_wdt_set_timeout(struct watchdog_device *wdt_dev, + unsigned int new_timeout) +{ + struct lpc18xx_wdt_dev *lpc18xx_wdt = watchdog_get_drvdata(wdt_dev); + + lpc18xx_wdt->wdt_dev.timeout = new_timeout; + __lpc18xx_wdt_set_timeout(lpc18xx_wdt); + + return 0; +} + +static unsigned int lpc18xx_wdt_get_timeleft(struct watchdog_device *wdt_dev) +{ + struct lpc18xx_wdt_dev *lpc18xx_wdt = watchdog_get_drvdata(wdt_dev); + unsigned int val; + + val = readl(lpc18xx_wdt->base + LPC18XX_WDT_TV); + return (val * LPC18XX_WDT_CLK_DIV) / lpc18xx_wdt->clk_rate; +} + +static int lpc18xx_wdt_start(struct watchdog_device *wdt_dev) +{ + struct lpc18xx_wdt_dev *lpc18xx_wdt = watchdog_get_drvdata(wdt_dev); + unsigned int val; + + if (timer_pending(&lpc18xx_wdt->timer)) + del_timer(&lpc18xx_wdt->timer); + + val = readl(lpc18xx_wdt->base + LPC18XX_WDT_MOD); + val |= LPC18XX_WDT_MOD_WDEN; + val |= LPC18XX_WDT_MOD_WDRESET; + writel(val, lpc18xx_wdt->base + LPC18XX_WDT_MOD); + + /* + * Setting the WDEN bit in the WDMOD register is not sufficient to + * enable the Watchdog. A valid feed sequence must be completed after + * setting WDEN before the Watchdog is capable of generating a reset. + */ + lpc18xx_wdt_feed(wdt_dev); + + return 0; +} + +static struct watchdog_info lpc18xx_wdt_info = { + .identity = "NXP LPC18xx Watchdog", + .options = WDIOF_SETTIMEOUT | + WDIOF_KEEPALIVEPING | + WDIOF_MAGICCLOSE, +}; + +static const struct watchdog_ops lpc18xx_wdt_ops = { + .owner = THIS_MODULE, + .start = lpc18xx_wdt_start, + .stop = lpc18xx_wdt_stop, + .ping = lpc18xx_wdt_feed, + .set_timeout = lpc18xx_wdt_set_timeout, + .get_timeleft = lpc18xx_wdt_get_timeleft, +}; + +static int lpc18xx_wdt_restart(struct notifier_block *this, unsigned long mode, + void *cmd) +{ + struct lpc18xx_wdt_dev *lpc18xx_wdt = container_of(this, + struct lpc18xx_wdt_dev, restart_handler); + unsigned long flags; + int val; + + /* + * Incorrect feed sequence causes immediate watchdog reset if enabled. + */ + spin_lock_irqsave(&lpc18xx_wdt->lock, flags); + + val = readl(lpc18xx_wdt->base + LPC18XX_WDT_MOD); + val |= LPC18XX_WDT_MOD_WDEN; + val |= LPC18XX_WDT_MOD_WDRESET; + writel(val, lpc18xx_wdt->base + LPC18XX_WDT_MOD); + + writel(LPC18XX_WDT_FEED_MAGIC1, lpc18xx_wdt->base + LPC18XX_WDT_FEED); + writel(LPC18XX_WDT_FEED_MAGIC2, lpc18xx_wdt->base + LPC18XX_WDT_FEED); + + writel(LPC18XX_WDT_FEED_MAGIC1, lpc18xx_wdt->base + LPC18XX_WDT_FEED); + writel(LPC18XX_WDT_FEED_MAGIC1, lpc18xx_wdt->base + LPC18XX_WDT_FEED); + + spin_unlock_irqrestore(&lpc18xx_wdt->lock, flags); + + return NOTIFY_OK; +} + +static int lpc18xx_wdt_probe(struct platform_device *pdev) +{ + struct lpc18xx_wdt_dev *lpc18xx_wdt; + struct device *dev = &pdev->dev; + struct resource *res; + int ret; + + lpc18xx_wdt = devm_kzalloc(dev, sizeof(*lpc18xx_wdt), GFP_KERNEL); + if (!lpc18xx_wdt) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + lpc18xx_wdt->base = devm_ioremap_resource(dev, res); + if (IS_ERR(lpc18xx_wdt->base)) + return PTR_ERR(lpc18xx_wdt->base); + + lpc18xx_wdt->reg_clk = devm_clk_get(dev, "reg"); + if (IS_ERR(lpc18xx_wdt->reg_clk)) { + dev_err(dev, "failed to get the reg clock\n"); + return PTR_ERR(lpc18xx_wdt->reg_clk); + } + + lpc18xx_wdt->wdt_clk = devm_clk_get(dev, "wdtclk"); + if (IS_ERR(lpc18xx_wdt->wdt_clk)) { + dev_err(dev, "failed to get the wdt clock\n"); + return PTR_ERR(lpc18xx_wdt->wdt_clk); + } + + ret = clk_prepare_enable(lpc18xx_wdt->reg_clk); + if (ret) { + dev_err(dev, "could not prepare or enable sys clock\n"); + return ret; + } + + ret = clk_prepare_enable(lpc18xx_wdt->wdt_clk); + if (ret) { + dev_err(dev, "could not prepare or enable wdt clock\n"); + goto disable_reg_clk; + } + + /* We use the clock rate to calculate timeouts */ + lpc18xx_wdt->clk_rate = clk_get_rate(lpc18xx_wdt->wdt_clk); + if (lpc18xx_wdt->clk_rate == 0) { + dev_err(dev, "failed to get clock rate\n"); + ret = -EINVAL; + goto disable_wdt_clk; + } + + lpc18xx_wdt->wdt_dev.info = &lpc18xx_wdt_info; + lpc18xx_wdt->wdt_dev.ops = &lpc18xx_wdt_ops; + + lpc18xx_wdt->wdt_dev.min_timeout = DIV_ROUND_UP(LPC18XX_WDT_TC_MIN * + LPC18XX_WDT_CLK_DIV, lpc18xx_wdt->clk_rate); + + lpc18xx_wdt->wdt_dev.max_timeout = (LPC18XX_WDT_TC_MAX * + LPC18XX_WDT_CLK_DIV) / lpc18xx_wdt->clk_rate; + + lpc18xx_wdt->wdt_dev.timeout = min(lpc18xx_wdt->wdt_dev.max_timeout, + LPC18XX_WDT_DEF_TIMEOUT); + + spin_lock_init(&lpc18xx_wdt->lock); + + lpc18xx_wdt->wdt_dev.parent = dev; + watchdog_set_drvdata(&lpc18xx_wdt->wdt_dev, lpc18xx_wdt); + + ret = watchdog_init_timeout(&lpc18xx_wdt->wdt_dev, heartbeat, dev); + + __lpc18xx_wdt_set_timeout(lpc18xx_wdt); + + setup_timer(&lpc18xx_wdt->timer, lpc18xx_wdt_timer_feed, + (unsigned long)&lpc18xx_wdt->wdt_dev); + + watchdog_set_nowayout(&lpc18xx_wdt->wdt_dev, nowayout); + + platform_set_drvdata(pdev, lpc18xx_wdt); + + ret = watchdog_register_device(&lpc18xx_wdt->wdt_dev); + if (ret) + goto disable_wdt_clk; + + lpc18xx_wdt->restart_handler.notifier_call = lpc18xx_wdt_restart; + lpc18xx_wdt->restart_handler.priority = 128; + ret = register_restart_handler(&lpc18xx_wdt->restart_handler); + if (ret) + dev_warn(dev, "failed to register restart handler: %d\n", ret); + + return 0; + +disable_wdt_clk: + clk_disable_unprepare(lpc18xx_wdt->wdt_clk); +disable_reg_clk: + clk_disable_unprepare(lpc18xx_wdt->reg_clk); + return ret; +} + +static void lpc18xx_wdt_shutdown(struct platform_device *pdev) +{ + struct lpc18xx_wdt_dev *lpc18xx_wdt = platform_get_drvdata(pdev); + + lpc18xx_wdt_stop(&lpc18xx_wdt->wdt_dev); +} + +static int lpc18xx_wdt_remove(struct platform_device *pdev) +{ + struct lpc18xx_wdt_dev *lpc18xx_wdt = platform_get_drvdata(pdev); + + unregister_restart_handler(&lpc18xx_wdt->restart_handler); + + dev_warn(&pdev->dev, "I quit now, hardware will probably reboot!\n"); + del_timer(&lpc18xx_wdt->timer); + + watchdog_unregister_device(&lpc18xx_wdt->wdt_dev); + clk_disable_unprepare(lpc18xx_wdt->wdt_clk); + clk_disable_unprepare(lpc18xx_wdt->reg_clk); + + return 0; +} + +static const struct of_device_id lpc18xx_wdt_match[] = { + { .compatible = "nxp,lpc1850-wwdt" }, + {} +}; +MODULE_DEVICE_TABLE(of, lpc18xx_wdt_match); + +static struct platform_driver lpc18xx_wdt_driver = { + .driver = { + .name = "lpc18xx-wdt", + .of_match_table = lpc18xx_wdt_match, + }, + .probe = lpc18xx_wdt_probe, + .remove = lpc18xx_wdt_remove, + .shutdown = lpc18xx_wdt_shutdown, +}; +module_platform_driver(lpc18xx_wdt_driver); + +MODULE_AUTHOR("Ariel D'Alessandro <ariel@vanguardiasur.com.ar>"); +MODULE_DESCRIPTION("NXP LPC18xx Watchdog Timer Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/kernel/drivers/watchdog/max63xx_wdt.c b/kernel/drivers/watchdog/max63xx_wdt.c index 08da3114a..f36ca4be0 100644 --- a/kernel/drivers/watchdog/max63xx_wdt.c +++ b/kernel/drivers/watchdog/max63xx_wdt.c @@ -39,10 +39,22 @@ static bool nowayout = WATCHDOG_NOWAYOUT; #define MAX6369_WDSET (7 << 0) #define MAX6369_WDI (1 << 3) -static DEFINE_SPINLOCK(io_lock); +#define MAX6369_WDSET_DISABLED 3 static int nodelay; -static void __iomem *wdt_base; + +struct max63xx_wdt { + struct watchdog_device wdd; + const struct max63xx_timeout *timeout; + + /* memory mapping */ + void __iomem *base; + spinlock_t lock; + + /* WDI and WSET bits write access routines */ + void (*ping)(struct max63xx_wdt *wdt); + void (*set)(struct max63xx_wdt *wdt, u8 set); +}; /* * The timeout values used are actually the absolute minimum the chip @@ -59,25 +71,25 @@ static void __iomem *wdt_base; /* Timeouts in second */ struct max63xx_timeout { - u8 wdset; - u8 tdelay; - u8 twd; + const u8 wdset; + const u8 tdelay; + const u8 twd; }; -static struct max63xx_timeout max6369_table[] = { +static const struct max63xx_timeout max6369_table[] = { { 5, 1, 1 }, { 6, 10, 10 }, { 7, 60, 60 }, { }, }; -static struct max63xx_timeout max6371_table[] = { +static const struct max63xx_timeout max6371_table[] = { { 6, 60, 3 }, { 7, 60, 60 }, { }, }; -static struct max63xx_timeout max6373_table[] = { +static const struct max63xx_timeout max6373_table[] = { { 2, 60, 1 }, { 5, 0, 1 }, { 1, 3, 3 }, @@ -86,8 +98,6 @@ static struct max63xx_timeout max6373_table[] = { { }, }; -static struct max63xx_timeout *current_timeout; - static struct max63xx_timeout * max63xx_select_timeout(struct max63xx_timeout *table, int value) { @@ -108,59 +118,32 @@ max63xx_select_timeout(struct max63xx_timeout *table, int value) static int max63xx_wdt_ping(struct watchdog_device *wdd) { - u8 val; - - spin_lock(&io_lock); + struct max63xx_wdt *wdt = watchdog_get_drvdata(wdd); - val = __raw_readb(wdt_base); - - __raw_writeb(val | MAX6369_WDI, wdt_base); - __raw_writeb(val & ~MAX6369_WDI, wdt_base); - - spin_unlock(&io_lock); + wdt->ping(wdt); return 0; } static int max63xx_wdt_start(struct watchdog_device *wdd) { - struct max63xx_timeout *entry = watchdog_get_drvdata(wdd); - u8 val; + struct max63xx_wdt *wdt = watchdog_get_drvdata(wdd); - spin_lock(&io_lock); - - val = __raw_readb(wdt_base); - val &= ~MAX6369_WDSET; - val |= entry->wdset; - __raw_writeb(val, wdt_base); - - spin_unlock(&io_lock); + wdt->set(wdt, wdt->timeout->wdset); /* check for a edge triggered startup */ - if (entry->tdelay == 0) - max63xx_wdt_ping(wdd); + if (wdt->timeout->tdelay == 0) + wdt->ping(wdt); return 0; } static int max63xx_wdt_stop(struct watchdog_device *wdd) { - u8 val; + struct max63xx_wdt *wdt = watchdog_get_drvdata(wdd); - spin_lock(&io_lock); - - val = __raw_readb(wdt_base); - val &= ~MAX6369_WDSET; - val |= 3; - __raw_writeb(val, wdt_base); - - spin_unlock(&io_lock); + wdt->set(wdt, MAX6369_WDSET_DISABLED); return 0; } -static const struct watchdog_info max63xx_wdt_info = { - .options = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, - .identity = "max63xx Watchdog", -}; - static const struct watchdog_ops max63xx_wdt_ops = { .owner = THIS_MODULE, .start = max63xx_wdt_start, @@ -168,53 +151,108 @@ static const struct watchdog_ops max63xx_wdt_ops = { .ping = max63xx_wdt_ping, }; -static struct watchdog_device max63xx_wdt_dev = { - .info = &max63xx_wdt_info, - .ops = &max63xx_wdt_ops, +static const struct watchdog_info max63xx_wdt_info = { + .options = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, + .identity = "max63xx Watchdog", }; +static void max63xx_mmap_ping(struct max63xx_wdt *wdt) +{ + u8 val; + + spin_lock(&wdt->lock); + + val = __raw_readb(wdt->base); + + __raw_writeb(val | MAX6369_WDI, wdt->base); + __raw_writeb(val & ~MAX6369_WDI, wdt->base); + + spin_unlock(&wdt->lock); +} + +static void max63xx_mmap_set(struct max63xx_wdt *wdt, u8 set) +{ + u8 val; + + spin_lock(&wdt->lock); + + val = __raw_readb(wdt->base); + val &= ~MAX6369_WDSET; + val |= set & MAX6369_WDSET; + __raw_writeb(val, wdt->base); + + spin_unlock(&wdt->lock); +} + +static int max63xx_mmap_init(struct platform_device *p, struct max63xx_wdt *wdt) +{ + struct resource *mem = platform_get_resource(p, IORESOURCE_MEM, 0); + + wdt->base = devm_ioremap_resource(&p->dev, mem); + if (IS_ERR(wdt->base)) + return PTR_ERR(wdt->base); + + spin_lock_init(&wdt->lock); + + wdt->ping = max63xx_mmap_ping; + wdt->set = max63xx_mmap_set; + return 0; +} + static int max63xx_wdt_probe(struct platform_device *pdev) { - struct resource *wdt_mem; + struct max63xx_wdt *wdt; struct max63xx_timeout *table; + int err; + + wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); + if (!wdt) + return -ENOMEM; table = (struct max63xx_timeout *)pdev->id_entry->driver_data; if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT) heartbeat = DEFAULT_HEARTBEAT; - dev_info(&pdev->dev, "requesting %ds heartbeat\n", heartbeat); - current_timeout = max63xx_select_timeout(table, heartbeat); - - if (!current_timeout) { - dev_err(&pdev->dev, "unable to satisfy heartbeat request\n"); + wdt->timeout = max63xx_select_timeout(table, heartbeat); + if (!wdt->timeout) { + dev_err(&pdev->dev, "unable to satisfy %ds heartbeat request\n", + heartbeat); return -EINVAL; } - dev_info(&pdev->dev, "using %ds heartbeat with %ds initial delay\n", - current_timeout->twd, current_timeout->tdelay); + err = max63xx_mmap_init(pdev, wdt); + if (err) + return err; + + platform_set_drvdata(pdev, &wdt->wdd); + watchdog_set_drvdata(&wdt->wdd, wdt); - heartbeat = current_timeout->twd; + wdt->wdd.parent = &pdev->dev; + wdt->wdd.timeout = wdt->timeout->twd; + wdt->wdd.info = &max63xx_wdt_info; + wdt->wdd.ops = &max63xx_wdt_ops; - wdt_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - wdt_base = devm_ioremap_resource(&pdev->dev, wdt_mem); - if (IS_ERR(wdt_base)) - return PTR_ERR(wdt_base); + watchdog_set_nowayout(&wdt->wdd, nowayout); - max63xx_wdt_dev.timeout = heartbeat; - watchdog_set_nowayout(&max63xx_wdt_dev, nowayout); - watchdog_set_drvdata(&max63xx_wdt_dev, current_timeout); + err = watchdog_register_device(&wdt->wdd); + if (err) + return err; - return watchdog_register_device(&max63xx_wdt_dev); + dev_info(&pdev->dev, "using %ds heartbeat with %ds initial delay\n", + wdt->timeout->twd, wdt->timeout->tdelay); + return 0; } static int max63xx_wdt_remove(struct platform_device *pdev) { - watchdog_unregister_device(&max63xx_wdt_dev); + struct watchdog_device *wdd = platform_get_drvdata(pdev); + + watchdog_unregister_device(wdd); return 0; } -static struct platform_device_id max63xx_id_table[] = { +static const struct platform_device_id max63xx_id_table[] = { { "max6369_wdt", (kernel_ulong_t)max6369_table, }, { "max6370_wdt", (kernel_ulong_t)max6369_table, }, { "max6371_wdt", (kernel_ulong_t)max6371_table, }, diff --git a/kernel/drivers/watchdog/mena21_wdt.c b/kernel/drivers/watchdog/mena21_wdt.c index 96dbba980..098fa9c34 100644 --- a/kernel/drivers/watchdog/mena21_wdt.c +++ b/kernel/drivers/watchdog/mena21_wdt.c @@ -197,6 +197,7 @@ static int a21_wdt_probe(struct platform_device *pdev) watchdog_init_timeout(&a21_wdt, 30, &pdev->dev); watchdog_set_nowayout(&a21_wdt, nowayout); watchdog_set_drvdata(&a21_wdt, drv); + a21_wdt.parent = &pdev->dev; reset = a21_wdt_get_bootstatus(drv); if (reset == 2) @@ -208,14 +209,15 @@ static int a21_wdt_probe(struct platform_device *pdev) else if (reset == 7) a21_wdt.bootstatus |= WDIOF_EXTERN2; + drv->wdt = a21_wdt; + dev_set_drvdata(&pdev->dev, drv); + ret = watchdog_register_device(&a21_wdt); if (ret) { dev_err(&pdev->dev, "Cannot register watchdog device\n"); goto err_register_wd; } - dev_set_drvdata(&pdev->dev, drv); - dev_info(&pdev->dev, "MEN A21 watchdog timer driver enabled\n"); return 0; @@ -251,6 +253,7 @@ static const struct of_device_id a21_wdt_ids[] = { { .compatible = "men,a021-wdt" }, { }, }; +MODULE_DEVICE_TABLE(of, a21_wdt_ids); static struct platform_driver a21_wdt_driver = { .probe = a21_wdt_probe, diff --git a/kernel/drivers/watchdog/menf21bmc_wdt.c b/kernel/drivers/watchdog/menf21bmc_wdt.c index 59f0913c7..3aefddebb 100644 --- a/kernel/drivers/watchdog/menf21bmc_wdt.c +++ b/kernel/drivers/watchdog/menf21bmc_wdt.c @@ -130,6 +130,7 @@ static int menf21bmc_wdt_probe(struct platform_device *pdev) drv_data->wdt.info = &menf21bmc_wdt_info; drv_data->wdt.min_timeout = BMC_WD_TIMEOUT_MIN; drv_data->wdt.max_timeout = BMC_WD_TIMEOUT_MAX; + drv_data->wdt.parent = &pdev->dev; drv_data->i2c_client = i2c_client; /* diff --git a/kernel/drivers/watchdog/moxart_wdt.c b/kernel/drivers/watchdog/moxart_wdt.c index 2789da2c0..60b0605bd 100644 --- a/kernel/drivers/watchdog/moxart_wdt.c +++ b/kernel/drivers/watchdog/moxart_wdt.c @@ -168,6 +168,7 @@ static const struct of_device_id moxart_watchdog_match[] = { { .compatible = "moxa,moxart-watchdog" }, { }, }; +MODULE_DEVICE_TABLE(of, moxart_watchdog_match); static struct platform_driver moxart_wdt_driver = { .probe = moxart_wdt_probe, diff --git a/kernel/drivers/watchdog/mpc8xxx_wdt.c b/kernel/drivers/watchdog/mpc8xxx_wdt.c index 689381a24..5f2273aac 100644 --- a/kernel/drivers/watchdog/mpc8xxx_wdt.c +++ b/kernel/drivers/watchdog/mpc8xxx_wdt.c @@ -50,8 +50,12 @@ struct mpc8xxx_wdt_type { bool hw_enabled; }; -static struct mpc8xxx_wdt __iomem *wd_base; -static int mpc8xxx_wdt_init_late(void); +struct mpc8xxx_wdt_ddata { + struct mpc8xxx_wdt __iomem *base; + struct watchdog_device wdd; + struct timer_list timer; + spinlock_t lock; +}; static u16 timeout = 0xffff; module_param(timeout, ushort, 0); @@ -68,65 +72,59 @@ module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started " "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); -/* - * We always prescale, but if someone really doesn't want to they can set this - * to 0 - */ -static int prescale = 1; - -static DEFINE_SPINLOCK(wdt_spinlock); - -static void mpc8xxx_wdt_keepalive(void) +static void mpc8xxx_wdt_keepalive(struct mpc8xxx_wdt_ddata *ddata) { /* Ping the WDT */ - spin_lock(&wdt_spinlock); - out_be16(&wd_base->swsrr, 0x556c); - out_be16(&wd_base->swsrr, 0xaa39); - spin_unlock(&wdt_spinlock); + spin_lock(&ddata->lock); + out_be16(&ddata->base->swsrr, 0x556c); + out_be16(&ddata->base->swsrr, 0xaa39); + spin_unlock(&ddata->lock); } -static struct watchdog_device mpc8xxx_wdt_dev; -static void mpc8xxx_wdt_timer_ping(unsigned long arg); -static DEFINE_TIMER(wdt_timer, mpc8xxx_wdt_timer_ping, 0, - (unsigned long)&mpc8xxx_wdt_dev); - static void mpc8xxx_wdt_timer_ping(unsigned long arg) { - struct watchdog_device *w = (struct watchdog_device *)arg; + struct mpc8xxx_wdt_ddata *ddata = (void *)arg; - mpc8xxx_wdt_keepalive(); + mpc8xxx_wdt_keepalive(ddata); /* We're pinging it twice faster than needed, just to be sure. */ - mod_timer(&wdt_timer, jiffies + HZ * w->timeout / 2); + mod_timer(&ddata->timer, jiffies + HZ * ddata->wdd.timeout / 2); } static int mpc8xxx_wdt_start(struct watchdog_device *w) { - u32 tmp = SWCRR_SWEN; + struct mpc8xxx_wdt_ddata *ddata = + container_of(w, struct mpc8xxx_wdt_ddata, wdd); + + u32 tmp = SWCRR_SWEN | SWCRR_SWPR; /* Good, fire up the show */ - if (prescale) - tmp |= SWCRR_SWPR; if (reset) tmp |= SWCRR_SWRI; tmp |= timeout << 16; - out_be32(&wd_base->swcrr, tmp); + out_be32(&ddata->base->swcrr, tmp); - del_timer_sync(&wdt_timer); + del_timer_sync(&ddata->timer); return 0; } static int mpc8xxx_wdt_ping(struct watchdog_device *w) { - mpc8xxx_wdt_keepalive(); + struct mpc8xxx_wdt_ddata *ddata = + container_of(w, struct mpc8xxx_wdt_ddata, wdd); + + mpc8xxx_wdt_keepalive(ddata); return 0; } static int mpc8xxx_wdt_stop(struct watchdog_device *w) { - mod_timer(&wdt_timer, jiffies); + struct mpc8xxx_wdt_ddata *ddata = + container_of(w, struct mpc8xxx_wdt_ddata, wdd); + + mod_timer(&ddata->timer, jiffies); return 0; } @@ -143,53 +141,57 @@ static struct watchdog_ops mpc8xxx_wdt_ops = { .stop = mpc8xxx_wdt_stop, }; -static struct watchdog_device mpc8xxx_wdt_dev = { - .info = &mpc8xxx_wdt_info, - .ops = &mpc8xxx_wdt_ops, -}; - -static const struct of_device_id mpc8xxx_wdt_match[]; static int mpc8xxx_wdt_probe(struct platform_device *ofdev) { int ret; - const struct of_device_id *match; - struct device_node *np = ofdev->dev.of_node; + struct resource *res; const struct mpc8xxx_wdt_type *wdt_type; + struct mpc8xxx_wdt_ddata *ddata; u32 freq = fsl_get_sys_freq(); bool enabled; unsigned int timeout_sec; - match = of_match_device(mpc8xxx_wdt_match, &ofdev->dev); - if (!match) + wdt_type = of_device_get_match_data(&ofdev->dev); + if (!wdt_type) return -EINVAL; - wdt_type = match->data; if (!freq || freq == -1) return -EINVAL; - wd_base = of_iomap(np, 0); - if (!wd_base) + ddata = devm_kzalloc(&ofdev->dev, sizeof(*ddata), GFP_KERNEL); + if (!ddata) return -ENOMEM; - enabled = in_be32(&wd_base->swcrr) & SWCRR_SWEN; + res = platform_get_resource(ofdev, IORESOURCE_MEM, 0); + ddata->base = devm_ioremap_resource(&ofdev->dev, res); + if (IS_ERR(ddata->base)) + return PTR_ERR(ddata->base); + + enabled = in_be32(&ddata->base->swcrr) & SWCRR_SWEN; if (!enabled && wdt_type->hw_enabled) { pr_info("could not be enabled in software\n"); - ret = -ENOSYS; - goto err_unmap; + return -ENODEV; } + spin_lock_init(&ddata->lock); + setup_timer(&ddata->timer, mpc8xxx_wdt_timer_ping, + (unsigned long)ddata); + + ddata->wdd.info = &mpc8xxx_wdt_info, + ddata->wdd.ops = &mpc8xxx_wdt_ops, + /* Calculate the timeout in seconds */ - if (prescale) - timeout_sec = (timeout * wdt_type->prescaler) / freq; - else - timeout_sec = timeout / freq; - - mpc8xxx_wdt_dev.timeout = timeout_sec; -#ifdef MODULE - ret = mpc8xxx_wdt_init_late(); - if (ret) - goto err_unmap; -#endif + timeout_sec = (timeout * wdt_type->prescaler) / freq; + + ddata->wdd.timeout = timeout_sec; + + watchdog_set_nowayout(&ddata->wdd, nowayout); + + ret = watchdog_register_device(&ddata->wdd); + if (ret) { + pr_err("cannot register watchdog device (err=%d)\n", ret); + return ret; + } pr_info("WDT driver for MPC8xxx initialized. mode:%s timeout=%d (%d seconds)\n", reset ? "reset" : "interrupt", timeout, timeout_sec); @@ -200,21 +202,20 @@ static int mpc8xxx_wdt_probe(struct platform_device *ofdev) * userspace handles it. */ if (enabled) - mod_timer(&wdt_timer, jiffies); + mod_timer(&ddata->timer, jiffies); + + platform_set_drvdata(ofdev, ddata); return 0; -err_unmap: - iounmap(wd_base); - wd_base = NULL; - return ret; } static int mpc8xxx_wdt_remove(struct platform_device *ofdev) { + struct mpc8xxx_wdt_ddata *ddata = platform_get_drvdata(ofdev); + pr_crit("Watchdog removed, expect the %s soon!\n", reset ? "reset" : "machine check exception"); - del_timer_sync(&wdt_timer); - watchdog_unregister_device(&mpc8xxx_wdt_dev); - iounmap(wd_base); + del_timer_sync(&ddata->timer); + watchdog_unregister_device(&ddata->wdd); return 0; } @@ -253,31 +254,6 @@ static struct platform_driver mpc8xxx_wdt_driver = { }, }; -/* - * We do wdt initialization in two steps: arch_initcall probes the wdt - * very early to start pinging the watchdog (misc devices are not yet - * available), and later module_init() just registers the misc device. - */ -static int mpc8xxx_wdt_init_late(void) -{ - int ret; - - if (!wd_base) - return -ENODEV; - - watchdog_set_nowayout(&mpc8xxx_wdt_dev, nowayout); - - ret = watchdog_register_device(&mpc8xxx_wdt_dev); - if (ret) { - pr_err("cannot register watchdog device (err=%d)\n", ret); - return ret; - } - return 0; -} -#ifndef MODULE -module_init(mpc8xxx_wdt_init_late); -#endif - static int __init mpc8xxx_wdt_init(void) { return platform_driver_register(&mpc8xxx_wdt_driver); diff --git a/kernel/drivers/watchdog/mtk_wdt.c b/kernel/drivers/watchdog/mtk_wdt.c index 938b987de..b751f43d7 100644 --- a/kernel/drivers/watchdog/mtk_wdt.c +++ b/kernel/drivers/watchdog/mtk_wdt.c @@ -123,6 +123,7 @@ static int mtk_wdt_stop(struct watchdog_device *wdt_dev) reg = readl(wdt_base + WDT_MODE); reg &= ~WDT_MODE_EN; + reg |= WDT_MODE_KEY; iowrite32(reg, wdt_base + WDT_MODE); return 0; @@ -210,6 +211,14 @@ static int mtk_wdt_probe(struct platform_device *pdev) return 0; } +static void mtk_wdt_shutdown(struct platform_device *pdev) +{ + struct mtk_wdt_dev *mtk_wdt = platform_get_drvdata(pdev); + + if (watchdog_active(&mtk_wdt->wdt_dev)) + mtk_wdt_stop(&mtk_wdt->wdt_dev); +} + static int mtk_wdt_remove(struct platform_device *pdev) { struct mtk_wdt_dev *mtk_wdt = platform_get_drvdata(pdev); @@ -221,17 +230,48 @@ static int mtk_wdt_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM_SLEEP +static int mtk_wdt_suspend(struct device *dev) +{ + struct mtk_wdt_dev *mtk_wdt = dev_get_drvdata(dev); + + if (watchdog_active(&mtk_wdt->wdt_dev)) + mtk_wdt_stop(&mtk_wdt->wdt_dev); + + return 0; +} + +static int mtk_wdt_resume(struct device *dev) +{ + struct mtk_wdt_dev *mtk_wdt = dev_get_drvdata(dev); + + if (watchdog_active(&mtk_wdt->wdt_dev)) { + mtk_wdt_start(&mtk_wdt->wdt_dev); + mtk_wdt_ping(&mtk_wdt->wdt_dev); + } + + return 0; +} +#endif + static const struct of_device_id mtk_wdt_dt_ids[] = { { .compatible = "mediatek,mt6589-wdt" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, mtk_wdt_dt_ids); +static const struct dev_pm_ops mtk_wdt_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(mtk_wdt_suspend, + mtk_wdt_resume) +}; + static struct platform_driver mtk_wdt_driver = { .probe = mtk_wdt_probe, .remove = mtk_wdt_remove, + .shutdown = mtk_wdt_shutdown, .driver = { .name = DRV_NAME, + .pm = &mtk_wdt_pm_ops, .of_match_table = mtk_wdt_dt_ids, }, }; diff --git a/kernel/drivers/watchdog/nv_tco.c b/kernel/drivers/watchdog/nv_tco.c index c028454be..bd917bb75 100644 --- a/kernel/drivers/watchdog/nv_tco.c +++ b/kernel/drivers/watchdog/nv_tco.c @@ -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_MCP79_SMBUS, + PCI_ANY_ID, PCI_ANY_ID, }, { 0, }, /* End of list */ }; MODULE_DEVICE_TABLE(pci, tco_pci_tbl); diff --git a/kernel/drivers/watchdog/omap_wdt.c b/kernel/drivers/watchdog/omap_wdt.c index c9c97dacf..6f17c935a 100644 --- a/kernel/drivers/watchdog/omap_wdt.c +++ b/kernel/drivers/watchdog/omap_wdt.c @@ -53,7 +53,15 @@ static unsigned timer_margin; module_param(timer_margin, uint, 0); MODULE_PARM_DESC(timer_margin, "initial watchdog timeout (in seconds)"); +#define to_omap_wdt_dev(_wdog) container_of(_wdog, struct omap_wdt_dev, wdog) + +static bool early_enable; +module_param(early_enable, bool, 0); +MODULE_PARM_DESC(early_enable, + "Watchdog is started on module insertion (default=0)"); + struct omap_wdt_dev { + struct watchdog_device wdog; void __iomem *base; /* physical */ struct device *dev; bool omap_wdt_users; @@ -123,7 +131,7 @@ static void omap_wdt_set_timer(struct omap_wdt_dev *wdev, static int omap_wdt_start(struct watchdog_device *wdog) { - struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog); + struct omap_wdt_dev *wdev = to_omap_wdt_dev(wdog); void __iomem *base = wdev->base; mutex_lock(&wdev->lock); @@ -158,7 +166,7 @@ static int omap_wdt_start(struct watchdog_device *wdog) static int omap_wdt_stop(struct watchdog_device *wdog) { - struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog); + struct omap_wdt_dev *wdev = to_omap_wdt_dev(wdog); mutex_lock(&wdev->lock); omap_wdt_disable(wdev); @@ -170,7 +178,7 @@ static int omap_wdt_stop(struct watchdog_device *wdog) static int omap_wdt_ping(struct watchdog_device *wdog) { - struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog); + struct omap_wdt_dev *wdev = to_omap_wdt_dev(wdog); mutex_lock(&wdev->lock); omap_wdt_reload(wdev); @@ -182,7 +190,7 @@ static int omap_wdt_ping(struct watchdog_device *wdog) static int omap_wdt_set_timeout(struct watchdog_device *wdog, unsigned int timeout) { - struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog); + struct omap_wdt_dev *wdev = to_omap_wdt_dev(wdog); mutex_lock(&wdev->lock); omap_wdt_disable(wdev); @@ -195,6 +203,16 @@ static int omap_wdt_set_timeout(struct watchdog_device *wdog, return 0; } +static unsigned int omap_wdt_get_timeleft(struct watchdog_device *wdog) +{ + struct omap_wdt_dev *wdev = to_omap_wdt_dev(wdog); + void __iomem *base = wdev->base; + u32 value; + + value = readl_relaxed(base + OMAP_WATCHDOG_CRR); + return GET_WCCR_SECS(value); +} + static const struct watchdog_info omap_wdt_info = { .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING, .identity = "OMAP Watchdog", @@ -206,21 +224,16 @@ static const struct watchdog_ops omap_wdt_ops = { .stop = omap_wdt_stop, .ping = omap_wdt_ping, .set_timeout = omap_wdt_set_timeout, + .get_timeleft = omap_wdt_get_timeleft, }; static int omap_wdt_probe(struct platform_device *pdev) { struct omap_wd_timer_platform_data *pdata = dev_get_platdata(&pdev->dev); - struct watchdog_device *omap_wdt; struct resource *res; struct omap_wdt_dev *wdev; - u32 rs; int ret; - omap_wdt = devm_kzalloc(&pdev->dev, sizeof(*omap_wdt), GFP_KERNEL); - if (!omap_wdt) - return -ENOMEM; - wdev = devm_kzalloc(&pdev->dev, sizeof(*wdev), GFP_KERNEL); if (!wdev) return -ENOMEM; @@ -236,35 +249,31 @@ static int omap_wdt_probe(struct platform_device *pdev) if (IS_ERR(wdev->base)) return PTR_ERR(wdev->base); - omap_wdt->info = &omap_wdt_info; - omap_wdt->ops = &omap_wdt_ops; - omap_wdt->min_timeout = TIMER_MARGIN_MIN; - omap_wdt->max_timeout = TIMER_MARGIN_MAX; + wdev->wdog.info = &omap_wdt_info; + wdev->wdog.ops = &omap_wdt_ops; + wdev->wdog.min_timeout = TIMER_MARGIN_MIN; + wdev->wdog.max_timeout = TIMER_MARGIN_MAX; + wdev->wdog.parent = &pdev->dev; - if (timer_margin >= TIMER_MARGIN_MIN && - timer_margin <= TIMER_MARGIN_MAX) - omap_wdt->timeout = timer_margin; - else - omap_wdt->timeout = TIMER_MARGIN_DEFAULT; + if (watchdog_init_timeout(&wdev->wdog, timer_margin, &pdev->dev) < 0) + wdev->wdog.timeout = TIMER_MARGIN_DEFAULT; - watchdog_set_drvdata(omap_wdt, wdev); - watchdog_set_nowayout(omap_wdt, nowayout); + watchdog_set_nowayout(&wdev->wdog, nowayout); - platform_set_drvdata(pdev, omap_wdt); + platform_set_drvdata(pdev, wdev); pm_runtime_enable(wdev->dev); pm_runtime_get_sync(wdev->dev); - if (pdata && pdata->read_reset_sources) - rs = pdata->read_reset_sources(); - else - rs = 0; - omap_wdt->bootstatus = (rs & (1 << OMAP_MPU_WD_RST_SRC_ID_SHIFT)) ? - WDIOF_CARDRESET : 0; + if (pdata && pdata->read_reset_sources) { + u32 rs = pdata->read_reset_sources(); + if (rs & (1 << OMAP_MPU_WD_RST_SRC_ID_SHIFT)) + wdev->wdog.bootstatus = WDIOF_CARDRESET; + } omap_wdt_disable(wdev); - ret = watchdog_register_device(omap_wdt); + ret = watchdog_register_device(&wdev->wdog); if (ret) { pm_runtime_disable(wdev->dev); return ret; @@ -272,17 +281,19 @@ static int omap_wdt_probe(struct platform_device *pdev) pr_info("OMAP Watchdog Timer Rev 0x%02x: initial timeout %d sec\n", readl_relaxed(wdev->base + OMAP_WATCHDOG_REV) & 0xFF, - omap_wdt->timeout); + wdev->wdog.timeout); pm_runtime_put_sync(wdev->dev); + if (early_enable) + omap_wdt_start(&wdev->wdog); + return 0; } static void omap_wdt_shutdown(struct platform_device *pdev) { - struct watchdog_device *wdog = platform_get_drvdata(pdev); - struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog); + struct omap_wdt_dev *wdev = platform_get_drvdata(pdev); mutex_lock(&wdev->lock); if (wdev->omap_wdt_users) { @@ -294,11 +305,10 @@ static void omap_wdt_shutdown(struct platform_device *pdev) static int omap_wdt_remove(struct platform_device *pdev) { - struct watchdog_device *wdog = platform_get_drvdata(pdev); - struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog); + struct omap_wdt_dev *wdev = platform_get_drvdata(pdev); pm_runtime_disable(wdev->dev); - watchdog_unregister_device(wdog); + watchdog_unregister_device(&wdev->wdog); return 0; } @@ -313,8 +323,7 @@ static int omap_wdt_remove(struct platform_device *pdev) static int omap_wdt_suspend(struct platform_device *pdev, pm_message_t state) { - struct watchdog_device *wdog = platform_get_drvdata(pdev); - struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog); + struct omap_wdt_dev *wdev = platform_get_drvdata(pdev); mutex_lock(&wdev->lock); if (wdev->omap_wdt_users) { @@ -328,8 +337,7 @@ static int omap_wdt_suspend(struct platform_device *pdev, pm_message_t state) static int omap_wdt_resume(struct platform_device *pdev) { - struct watchdog_device *wdog = platform_get_drvdata(pdev); - struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog); + struct omap_wdt_dev *wdev = platform_get_drvdata(pdev); mutex_lock(&wdev->lock); if (wdev->omap_wdt_users) { diff --git a/kernel/drivers/watchdog/omap_wdt.h b/kernel/drivers/watchdog/omap_wdt.h index 09b774cf7..42f31ec5e 100644 --- a/kernel/drivers/watchdog/omap_wdt.h +++ b/kernel/drivers/watchdog/omap_wdt.h @@ -50,5 +50,6 @@ #define PTV 0 /* prescale */ #define GET_WLDR_VAL(secs) (0xffffffff - ((secs) * (32768/(1<<PTV))) + 1) +#define GET_WCCR_SECS(val) ((0xffffffff - (val) + 1) / (32768/(1<<PTV))) #endif /* _OMAP_WATCHDOG_H */ diff --git a/kernel/drivers/watchdog/orion_wdt.c b/kernel/drivers/watchdog/orion_wdt.c index ef0c628d5..c6b8f4a43 100644 --- a/kernel/drivers/watchdog/orion_wdt.c +++ b/kernel/drivers/watchdog/orion_wdt.c @@ -567,6 +567,7 @@ static int orion_wdt_probe(struct platform_device *pdev) dev->wdt.timeout = wdt_max_duration; dev->wdt.max_timeout = wdt_max_duration; + dev->wdt.parent = &pdev->dev; watchdog_init_timeout(&dev->wdt, heartbeat, &pdev->dev); platform_set_drvdata(pdev, &dev->wdt); diff --git a/kernel/drivers/watchdog/pnx4008_wdt.c b/kernel/drivers/watchdog/pnx4008_wdt.c index b9c6049c3..313cd1c6f 100644 --- a/kernel/drivers/watchdog/pnx4008_wdt.c +++ b/kernel/drivers/watchdog/pnx4008_wdt.c @@ -80,7 +80,7 @@ static unsigned int heartbeat = DEFAULT_HEARTBEAT; static DEFINE_SPINLOCK(io_lock); static void __iomem *wdt_base; -struct clk *wdt_clk; +static struct clk *wdt_clk; static int pnx4008_wdt_start(struct watchdog_device *wdd) { @@ -161,12 +161,13 @@ static int pnx4008_wdt_probe(struct platform_device *pdev) if (IS_ERR(wdt_clk)) return PTR_ERR(wdt_clk); - ret = clk_enable(wdt_clk); + ret = clk_prepare_enable(wdt_clk); if (ret) return ret; pnx4008_wdd.bootstatus = (readl(WDTIM_RES(wdt_base)) & WDOG_RESET) ? WDIOF_CARDRESET : 0; + pnx4008_wdd.parent = &pdev->dev; watchdog_set_nowayout(&pnx4008_wdd, nowayout); pnx4008_wdt_stop(&pnx4008_wdd); /* disable for now */ @@ -183,7 +184,7 @@ static int pnx4008_wdt_probe(struct platform_device *pdev) return 0; disable_clk: - clk_disable(wdt_clk); + clk_disable_unprepare(wdt_clk); return ret; } @@ -191,7 +192,7 @@ static int pnx4008_wdt_remove(struct platform_device *pdev) { watchdog_unregister_device(&pnx4008_wdd); - clk_disable(wdt_clk); + clk_disable_unprepare(wdt_clk); return 0; } diff --git a/kernel/drivers/watchdog/qcom-wdt.c b/kernel/drivers/watchdog/qcom-wdt.c index aa03ca8f2..773dcfaee 100644 --- a/kernel/drivers/watchdog/qcom-wdt.c +++ b/kernel/drivers/watchdog/qcom-wdt.c @@ -171,6 +171,7 @@ static int qcom_wdt_probe(struct platform_device *pdev) wdt->wdd.ops = &qcom_wdt_ops; wdt->wdd.min_timeout = 1; wdt->wdd.max_timeout = 0x10000000U / wdt->rate; + wdt->wdd.parent = &pdev->dev; /* * If 'timeout-sec' unspecified in devicetree, assume a 30 second diff --git a/kernel/drivers/watchdog/retu_wdt.c b/kernel/drivers/watchdog/retu_wdt.c index b7c68e275..39cd51df2 100644 --- a/kernel/drivers/watchdog/retu_wdt.c +++ b/kernel/drivers/watchdog/retu_wdt.c @@ -127,6 +127,7 @@ static int retu_wdt_probe(struct platform_device *pdev) retu_wdt->timeout = RETU_WDT_MAX_TIMER; retu_wdt->min_timeout = 0; retu_wdt->max_timeout = RETU_WDT_MAX_TIMER; + retu_wdt->parent = &pdev->dev; watchdog_set_drvdata(retu_wdt, wdev); watchdog_set_nowayout(retu_wdt, nowayout); diff --git a/kernel/drivers/watchdog/rt2880_wdt.c b/kernel/drivers/watchdog/rt2880_wdt.c index a6f7e2e29..1967919ae 100644 --- a/kernel/drivers/watchdog/rt2880_wdt.c +++ b/kernel/drivers/watchdog/rt2880_wdt.c @@ -161,6 +161,7 @@ static int rt288x_wdt_probe(struct platform_device *pdev) rt288x_wdt_dev.dev = &pdev->dev; rt288x_wdt_dev.bootstatus = rt288x_wdt_bootcause(); rt288x_wdt_dev.max_timeout = (0xfffful / rt288x_wdt_freq); + rt288x_wdt_dev.parent = &pdev->dev; watchdog_init_timeout(&rt288x_wdt_dev, rt288x_wdt_dev.max_timeout, &pdev->dev); diff --git a/kernel/drivers/watchdog/s3c2410_wdt.c b/kernel/drivers/watchdog/s3c2410_wdt.c index e89ae027c..d781000c7 100644 --- a/kernel/drivers/watchdog/s3c2410_wdt.c +++ b/kernel/drivers/watchdog/s3c2410_wdt.c @@ -607,6 +607,7 @@ static int s3c2410wdt_probe(struct platform_device *pdev) watchdog_set_nowayout(&wdt->wdt_device, nowayout); wdt->wdt_device.bootstatus = s3c2410wdt_get_bootstatus(wdt); + wdt->wdt_device.parent = &pdev->dev; ret = watchdog_register_device(&wdt->wdt_device); if (ret) { diff --git a/kernel/drivers/watchdog/sama5d4_wdt.c b/kernel/drivers/watchdog/sama5d4_wdt.c new file mode 100644 index 000000000..a49634cdc --- /dev/null +++ b/kernel/drivers/watchdog/sama5d4_wdt.c @@ -0,0 +1,280 @@ +/* + * Driver for Atmel SAMA5D4 Watchdog Timer + * + * Copyright (C) 2015 Atmel Corporation + * + * Licensed under GPLv2. + */ + +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_irq.h> +#include <linux/platform_device.h> +#include <linux/reboot.h> +#include <linux/watchdog.h> + +#include "at91sam9_wdt.h" + +/* minimum and maximum watchdog timeout, in seconds */ +#define MIN_WDT_TIMEOUT 1 +#define MAX_WDT_TIMEOUT 16 +#define WDT_DEFAULT_TIMEOUT MAX_WDT_TIMEOUT + +#define WDT_SEC2TICKS(s) ((s) ? (((s) << 8) - 1) : 0) + +struct sama5d4_wdt { + struct watchdog_device wdd; + void __iomem *reg_base; + u32 config; +}; + +static int wdt_timeout = WDT_DEFAULT_TIMEOUT; +static bool nowayout = WATCHDOG_NOWAYOUT; + +module_param(wdt_timeout, int, 0); +MODULE_PARM_DESC(wdt_timeout, + "Watchdog timeout in seconds. (default = " + __MODULE_STRING(WDT_DEFAULT_TIMEOUT) ")"); + +module_param(nowayout, bool, 0); +MODULE_PARM_DESC(nowayout, + "Watchdog cannot be stopped once started (default=" + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); + +#define wdt_read(wdt, field) \ + readl_relaxed((wdt)->reg_base + (field)) + +#define wdt_write(wtd, field, val) \ + writel_relaxed((val), (wdt)->reg_base + (field)) + +static int sama5d4_wdt_start(struct watchdog_device *wdd) +{ + struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd); + u32 reg; + + reg = wdt_read(wdt, AT91_WDT_MR); + reg &= ~AT91_WDT_WDDIS; + wdt_write(wdt, AT91_WDT_MR, reg); + + return 0; +} + +static int sama5d4_wdt_stop(struct watchdog_device *wdd) +{ + struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd); + u32 reg; + + reg = wdt_read(wdt, AT91_WDT_MR); + reg |= AT91_WDT_WDDIS; + wdt_write(wdt, AT91_WDT_MR, reg); + + return 0; +} + +static int sama5d4_wdt_ping(struct watchdog_device *wdd) +{ + struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd); + + wdt_write(wdt, AT91_WDT_CR, AT91_WDT_KEY | AT91_WDT_WDRSTT); + + return 0; +} + +static int sama5d4_wdt_set_timeout(struct watchdog_device *wdd, + unsigned int timeout) +{ + struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd); + u32 value = WDT_SEC2TICKS(timeout); + u32 reg; + + reg = wdt_read(wdt, AT91_WDT_MR); + reg &= ~AT91_WDT_WDV; + reg &= ~AT91_WDT_WDD; + reg |= AT91_WDT_SET_WDV(value); + reg |= AT91_WDT_SET_WDD(value); + wdt_write(wdt, AT91_WDT_MR, reg); + + wdd->timeout = timeout; + + return 0; +} + +static const struct watchdog_info sama5d4_wdt_info = { + .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING, + .identity = "Atmel SAMA5D4 Watchdog", +}; + +static struct watchdog_ops sama5d4_wdt_ops = { + .owner = THIS_MODULE, + .start = sama5d4_wdt_start, + .stop = sama5d4_wdt_stop, + .ping = sama5d4_wdt_ping, + .set_timeout = sama5d4_wdt_set_timeout, +}; + +static irqreturn_t sama5d4_wdt_irq_handler(int irq, void *dev_id) +{ + struct sama5d4_wdt *wdt = platform_get_drvdata(dev_id); + + if (wdt_read(wdt, AT91_WDT_SR)) { + pr_crit("Atmel Watchdog Software Reset\n"); + emergency_restart(); + pr_crit("Reboot didn't succeed\n"); + } + + return IRQ_HANDLED; +} + +static int of_sama5d4_wdt_init(struct device_node *np, struct sama5d4_wdt *wdt) +{ + const char *tmp; + + wdt->config = AT91_WDT_WDDIS; + + if (!of_property_read_string(np, "atmel,watchdog-type", &tmp) && + !strcmp(tmp, "software")) + wdt->config |= AT91_WDT_WDFIEN; + else + wdt->config |= AT91_WDT_WDRSTEN; + + if (of_property_read_bool(np, "atmel,idle-halt")) + wdt->config |= AT91_WDT_WDIDLEHLT; + + if (of_property_read_bool(np, "atmel,dbg-halt")) + wdt->config |= AT91_WDT_WDDBGHLT; + + return 0; +} + +static int sama5d4_wdt_init(struct sama5d4_wdt *wdt) +{ + struct watchdog_device *wdd = &wdt->wdd; + u32 value = WDT_SEC2TICKS(wdd->timeout); + u32 reg; + + /* + * Because the fields WDV and WDD must not be modified when the WDDIS + * bit is set, so clear the WDDIS bit before writing the WDT_MR. + */ + reg = wdt_read(wdt, AT91_WDT_MR); + reg &= ~AT91_WDT_WDDIS; + wdt_write(wdt, AT91_WDT_MR, reg); + + reg = wdt->config; + reg |= AT91_WDT_SET_WDD(value); + reg |= AT91_WDT_SET_WDV(value); + + wdt_write(wdt, AT91_WDT_MR, reg); + + return 0; +} + +static int sama5d4_wdt_probe(struct platform_device *pdev) +{ + struct watchdog_device *wdd; + struct sama5d4_wdt *wdt; + struct resource *res; + void __iomem *regs; + u32 irq = 0; + int ret; + + wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); + if (!wdt) + return -ENOMEM; + + wdd = &wdt->wdd; + wdd->timeout = wdt_timeout; + wdd->info = &sama5d4_wdt_info; + wdd->ops = &sama5d4_wdt_ops; + wdd->min_timeout = MIN_WDT_TIMEOUT; + wdd->max_timeout = MAX_WDT_TIMEOUT; + + watchdog_set_drvdata(wdd, wdt); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + wdt->reg_base = regs; + + if (pdev->dev.of_node) { + irq = irq_of_parse_and_map(pdev->dev.of_node, 0); + if (!irq) + dev_warn(&pdev->dev, "failed to get IRQ from DT\n"); + + ret = of_sama5d4_wdt_init(pdev->dev.of_node, wdt); + if (ret) + return ret; + } + + if ((wdt->config & AT91_WDT_WDFIEN) && irq) { + ret = devm_request_irq(&pdev->dev, irq, sama5d4_wdt_irq_handler, + IRQF_SHARED | IRQF_IRQPOLL | + IRQF_NO_SUSPEND, pdev->name, pdev); + if (ret) { + dev_err(&pdev->dev, + "cannot register interrupt handler\n"); + return ret; + } + } + + ret = watchdog_init_timeout(wdd, wdt_timeout, &pdev->dev); + if (ret) { + dev_err(&pdev->dev, "unable to set timeout value\n"); + return ret; + } + + ret = sama5d4_wdt_init(wdt); + if (ret) + return ret; + + watchdog_set_nowayout(wdd, nowayout); + + ret = watchdog_register_device(wdd); + if (ret) { + dev_err(&pdev->dev, "failed to register watchdog device\n"); + return ret; + } + + platform_set_drvdata(pdev, wdt); + + dev_info(&pdev->dev, "initialized (timeout = %d sec, nowayout = %d)\n", + wdt_timeout, nowayout); + + return 0; +} + +static int sama5d4_wdt_remove(struct platform_device *pdev) +{ + struct sama5d4_wdt *wdt = platform_get_drvdata(pdev); + + sama5d4_wdt_stop(&wdt->wdd); + + watchdog_unregister_device(&wdt->wdd); + + return 0; +} + +static const struct of_device_id sama5d4_wdt_of_match[] = { + { .compatible = "atmel,sama5d4-wdt", }, + { } +}; +MODULE_DEVICE_TABLE(of, sama5d4_wdt_of_match); + +static struct platform_driver sama5d4_wdt_driver = { + .probe = sama5d4_wdt_probe, + .remove = sama5d4_wdt_remove, + .driver = { + .name = "sama5d4_wdt", + .of_match_table = sama5d4_wdt_of_match, + } +}; +module_platform_driver(sama5d4_wdt_driver); + +MODULE_AUTHOR("Atmel Corporation"); +MODULE_DESCRIPTION("Atmel SAMA5D4 Watchdog Timer driver"); +MODULE_LICENSE("GPL v2"); diff --git a/kernel/drivers/watchdog/shwdt.c b/kernel/drivers/watchdog/shwdt.c index 567458b13..f90812170 100644 --- a/kernel/drivers/watchdog/shwdt.c +++ b/kernel/drivers/watchdog/shwdt.c @@ -252,6 +252,7 @@ static int sh_wdt_probe(struct platform_device *pdev) watchdog_set_nowayout(&sh_wdt_dev, nowayout); watchdog_set_drvdata(&sh_wdt_dev, wdt); + sh_wdt_dev.parent = &pdev->dev; spin_lock_init(&wdt->lock); diff --git a/kernel/drivers/watchdog/sirfsoc_wdt.c b/kernel/drivers/watchdog/sirfsoc_wdt.c index 42fa5c0c5..d0578ab2e 100644 --- a/kernel/drivers/watchdog/sirfsoc_wdt.c +++ b/kernel/drivers/watchdog/sirfsoc_wdt.c @@ -154,6 +154,7 @@ static int sirfsoc_wdt_probe(struct platform_device *pdev) watchdog_init_timeout(&sirfsoc_wdd, timeout, &pdev->dev); watchdog_set_nowayout(&sirfsoc_wdd, nowayout); + sirfsoc_wdd.parent = &pdev->dev; ret = watchdog_register_device(&sirfsoc_wdd); if (ret) diff --git a/kernel/drivers/watchdog/sp805_wdt.c b/kernel/drivers/watchdog/sp805_wdt.c index c1b03f423..01d816251 100644 --- a/kernel/drivers/watchdog/sp805_wdt.c +++ b/kernel/drivers/watchdog/sp805_wdt.c @@ -4,7 +4,7 @@ * Watchdog driver for ARM SP805 watchdog module * * Copyright (C) 2010 ST Microelectronics - * Viresh Kumar <viresh.linux@gmail.com> + * Viresh Kumar <vireshk@kernel.org> * * This file is licensed under the terms of the GNU General Public * License version 2 or later. This program is licensed "as is" without any @@ -226,6 +226,7 @@ sp805_wdt_probe(struct amba_device *adev, const struct amba_id *id) wdt->adev = adev; wdt->wdd.info = &wdt_info; wdt->wdd.ops = &wdt_ops; + wdt->wdd.parent = &adev->dev; spin_lock_init(&wdt->lock); watchdog_set_nowayout(&wdt->wdd, nowayout); @@ -303,6 +304,6 @@ static struct amba_driver sp805_wdt_driver = { module_amba_driver(sp805_wdt_driver); -MODULE_AUTHOR("Viresh Kumar <viresh.linux@gmail.com>"); +MODULE_AUTHOR("Viresh Kumar <vireshk@kernel.org>"); MODULE_DESCRIPTION("ARM SP805 Watchdog Driver"); MODULE_LICENSE("GPL"); diff --git a/kernel/drivers/watchdog/st_lpc_wdt.c b/kernel/drivers/watchdog/st_lpc_wdt.c new file mode 100644 index 000000000..14e9badf2 --- /dev/null +++ b/kernel/drivers/watchdog/st_lpc_wdt.c @@ -0,0 +1,345 @@ +/* + * ST's LPC Watchdog + * + * Copyright (C) 2014 STMicroelectronics -- All Rights Reserved + * + * Author: David Paris <david.paris@st.com> for STMicroelectronics + * Lee Jones <lee.jones@linaro.org> for STMicroelectronics + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#include <linux/clk.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/watchdog.h> + +#include <dt-bindings/mfd/st-lpc.h> + +/* Low Power Alarm */ +#define LPC_LPA_LSB_OFF 0x410 +#define LPC_LPA_START_OFF 0x418 + +/* LPC as WDT */ +#define LPC_WDT_OFF 0x510 + +static struct watchdog_device st_wdog_dev; + +struct st_wdog_syscfg { + unsigned int reset_type_reg; + unsigned int reset_type_mask; + unsigned int enable_reg; + unsigned int enable_mask; +}; + +struct st_wdog { + void __iomem *base; + struct device *dev; + struct regmap *regmap; + struct st_wdog_syscfg *syscfg; + struct clk *clk; + unsigned long clkrate; + bool warm_reset; +}; + +static struct st_wdog_syscfg stid127_syscfg = { + .reset_type_reg = 0x004, + .reset_type_mask = BIT(2), + .enable_reg = 0x000, + .enable_mask = BIT(2), +}; + +static struct st_wdog_syscfg stih415_syscfg = { + .reset_type_reg = 0x0B8, + .reset_type_mask = BIT(6), + .enable_reg = 0x0B4, + .enable_mask = BIT(7), +}; + +static struct st_wdog_syscfg stih416_syscfg = { + .reset_type_reg = 0x88C, + .reset_type_mask = BIT(6), + .enable_reg = 0x888, + .enable_mask = BIT(7), +}; + +static struct st_wdog_syscfg stih407_syscfg = { + .enable_reg = 0x204, + .enable_mask = BIT(19), +}; + +static const struct of_device_id st_wdog_match[] = { + { + .compatible = "st,stih407-lpc", + .data = &stih407_syscfg, + }, + { + .compatible = "st,stih416-lpc", + .data = &stih416_syscfg, + }, + { + .compatible = "st,stih415-lpc", + .data = &stih415_syscfg, + }, + { + .compatible = "st,stid127-lpc", + .data = &stid127_syscfg, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, st_wdog_match); + +static void st_wdog_setup(struct st_wdog *st_wdog, bool enable) +{ + /* Type of watchdog reset - 0: Cold 1: Warm */ + if (st_wdog->syscfg->reset_type_reg) + regmap_update_bits(st_wdog->regmap, + st_wdog->syscfg->reset_type_reg, + st_wdog->syscfg->reset_type_mask, + st_wdog->warm_reset); + + /* Mask/unmask watchdog reset */ + regmap_update_bits(st_wdog->regmap, + st_wdog->syscfg->enable_reg, + st_wdog->syscfg->enable_mask, + enable ? 0 : st_wdog->syscfg->enable_mask); +} + +static void st_wdog_load_timer(struct st_wdog *st_wdog, unsigned int timeout) +{ + unsigned long clkrate = st_wdog->clkrate; + + writel_relaxed(timeout * clkrate, st_wdog->base + LPC_LPA_LSB_OFF); + writel_relaxed(1, st_wdog->base + LPC_LPA_START_OFF); +} + +static int st_wdog_start(struct watchdog_device *wdd) +{ + struct st_wdog *st_wdog = watchdog_get_drvdata(wdd); + + writel_relaxed(1, st_wdog->base + LPC_WDT_OFF); + + return 0; +} + +static int st_wdog_stop(struct watchdog_device *wdd) +{ + struct st_wdog *st_wdog = watchdog_get_drvdata(wdd); + + writel_relaxed(0, st_wdog->base + LPC_WDT_OFF); + + return 0; +} + +static int st_wdog_set_timeout(struct watchdog_device *wdd, + unsigned int timeout) +{ + struct st_wdog *st_wdog = watchdog_get_drvdata(wdd); + + wdd->timeout = timeout; + st_wdog_load_timer(st_wdog, timeout); + + return 0; +} + +static int st_wdog_keepalive(struct watchdog_device *wdd) +{ + struct st_wdog *st_wdog = watchdog_get_drvdata(wdd); + + st_wdog_load_timer(st_wdog, wdd->timeout); + + return 0; +} + +static const struct watchdog_info st_wdog_info = { + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, + .identity = "ST LPC WDT", +}; + +static const struct watchdog_ops st_wdog_ops = { + .owner = THIS_MODULE, + .start = st_wdog_start, + .stop = st_wdog_stop, + .ping = st_wdog_keepalive, + .set_timeout = st_wdog_set_timeout, +}; + +static struct watchdog_device st_wdog_dev = { + .info = &st_wdog_info, + .ops = &st_wdog_ops, +}; + +static int st_wdog_probe(struct platform_device *pdev) +{ + const struct of_device_id *match; + struct device_node *np = pdev->dev.of_node; + struct st_wdog *st_wdog; + struct regmap *regmap; + struct resource *res; + struct clk *clk; + void __iomem *base; + uint32_t mode; + int ret; + + ret = of_property_read_u32(np, "st,lpc-mode", &mode); + if (ret) { + dev_err(&pdev->dev, "An LPC mode must be provided\n"); + return -EINVAL; + } + + /* LPC can either run as a Clocksource or in RTC or WDT mode */ + if (mode != ST_LPC_MODE_WDT) + return -ENODEV; + + st_wdog = devm_kzalloc(&pdev->dev, sizeof(*st_wdog), GFP_KERNEL); + if (!st_wdog) + return -ENOMEM; + + match = of_match_device(st_wdog_match, &pdev->dev); + if (!match) { + dev_err(&pdev->dev, "Couldn't match device\n"); + return -ENODEV; + } + st_wdog->syscfg = (struct st_wdog_syscfg *)match->data; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg"); + if (IS_ERR(regmap)) { + dev_err(&pdev->dev, "No syscfg phandle specified\n"); + return PTR_ERR(regmap); + } + + clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(clk)) { + dev_err(&pdev->dev, "Unable to request clock\n"); + return PTR_ERR(clk); + } + + st_wdog->dev = &pdev->dev; + st_wdog->base = base; + st_wdog->clk = clk; + st_wdog->regmap = regmap; + st_wdog->warm_reset = of_property_read_bool(np, "st,warm_reset"); + st_wdog->clkrate = clk_get_rate(st_wdog->clk); + + if (!st_wdog->clkrate) { + dev_err(&pdev->dev, "Unable to fetch clock rate\n"); + return -EINVAL; + } + st_wdog_dev.max_timeout = 0xFFFFFFFF / st_wdog->clkrate; + st_wdog_dev.parent = &pdev->dev; + + ret = clk_prepare_enable(clk); + if (ret) { + dev_err(&pdev->dev, "Unable to enable clock\n"); + return ret; + } + + watchdog_set_drvdata(&st_wdog_dev, st_wdog); + watchdog_set_nowayout(&st_wdog_dev, WATCHDOG_NOWAYOUT); + + /* Init Watchdog timeout with value in DT */ + ret = watchdog_init_timeout(&st_wdog_dev, 0, &pdev->dev); + if (ret) { + dev_err(&pdev->dev, "Unable to initialise watchdog timeout\n"); + clk_disable_unprepare(clk); + return ret; + } + + ret = watchdog_register_device(&st_wdog_dev); + if (ret) { + dev_err(&pdev->dev, "Unable to register watchdog\n"); + clk_disable_unprepare(clk); + return ret; + } + + st_wdog_setup(st_wdog, true); + + dev_info(&pdev->dev, "LPC Watchdog driver registered, reset type is %s", + st_wdog->warm_reset ? "warm" : "cold"); + + return ret; +} + +static int st_wdog_remove(struct platform_device *pdev) +{ + struct st_wdog *st_wdog = watchdog_get_drvdata(&st_wdog_dev); + + st_wdog_setup(st_wdog, false); + watchdog_unregister_device(&st_wdog_dev); + clk_disable_unprepare(st_wdog->clk); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int st_wdog_suspend(struct device *dev) +{ + struct st_wdog *st_wdog = watchdog_get_drvdata(&st_wdog_dev); + + if (watchdog_active(&st_wdog_dev)) + st_wdog_stop(&st_wdog_dev); + + st_wdog_setup(st_wdog, false); + + clk_disable(st_wdog->clk); + + return 0; +} + +static int st_wdog_resume(struct device *dev) +{ + struct st_wdog *st_wdog = watchdog_get_drvdata(&st_wdog_dev); + int ret; + + ret = clk_enable(st_wdog->clk); + if (ret) { + dev_err(dev, "Unable to re-enable clock\n"); + watchdog_unregister_device(&st_wdog_dev); + clk_unprepare(st_wdog->clk); + return ret; + } + + st_wdog_setup(st_wdog, true); + + if (watchdog_active(&st_wdog_dev)) { + st_wdog_load_timer(st_wdog, st_wdog_dev.timeout); + st_wdog_start(&st_wdog_dev); + } + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(st_wdog_pm_ops, + st_wdog_suspend, + st_wdog_resume); + +static struct platform_driver st_wdog_driver = { + .driver = { + .name = "st-lpc-wdt", + .pm = &st_wdog_pm_ops, + .of_match_table = st_wdog_match, + }, + .probe = st_wdog_probe, + .remove = st_wdog_remove, +}; +module_platform_driver(st_wdog_driver); + +MODULE_AUTHOR("David Paris <david.paris@st.com>"); +MODULE_DESCRIPTION("ST LPC Watchdog Driver"); +MODULE_LICENSE("GPL"); diff --git a/kernel/drivers/watchdog/stmp3xxx_rtc_wdt.c b/kernel/drivers/watchdog/stmp3xxx_rtc_wdt.c index e7f0d5b60..3ee6128a5 100644 --- a/kernel/drivers/watchdog/stmp3xxx_rtc_wdt.c +++ b/kernel/drivers/watchdog/stmp3xxx_rtc_wdt.c @@ -76,6 +76,7 @@ static int stmp3xxx_wdt_probe(struct platform_device *pdev) watchdog_set_drvdata(&stmp3xxx_wdd, &pdev->dev); stmp3xxx_wdd.timeout = clamp_t(unsigned, heartbeat, 1, STMP3XXX_MAX_TIMEOUT); + stmp3xxx_wdd.parent = &pdev->dev; ret = watchdog_register_device(&stmp3xxx_wdd); if (ret < 0) { diff --git a/kernel/drivers/watchdog/sunxi_wdt.c b/kernel/drivers/watchdog/sunxi_wdt.c index a29afb37c..47bd8a14d 100644 --- a/kernel/drivers/watchdog/sunxi_wdt.c +++ b/kernel/drivers/watchdog/sunxi_wdt.c @@ -184,7 +184,7 @@ static int sunxi_wdt_start(struct watchdog_device *wdt_dev) /* Set system reset function */ reg = readl(wdt_base + regs->wdt_cfg); reg &= ~(regs->wdt_reset_mask); - reg |= ~(regs->wdt_reset_val); + reg |= regs->wdt_reset_val; writel(reg, wdt_base + regs->wdt_cfg); /* Enable watchdog */ diff --git a/kernel/drivers/watchdog/tegra_wdt.c b/kernel/drivers/watchdog/tegra_wdt.c index 30451ea46..9ec57608d 100644 --- a/kernel/drivers/watchdog/tegra_wdt.c +++ b/kernel/drivers/watchdog/tegra_wdt.c @@ -140,8 +140,10 @@ static int tegra_wdt_set_timeout(struct watchdog_device *wdd, { wdd->timeout = timeout; - if (watchdog_active(wdd)) + if (watchdog_active(wdd)) { + tegra_wdt_stop(wdd); return tegra_wdt_start(wdd); + } return 0; } @@ -218,6 +220,7 @@ static int tegra_wdt_probe(struct platform_device *pdev) wdd->ops = &tegra_wdt_ops; wdd->min_timeout = MIN_WDT_TIMEOUT; wdd->max_timeout = MAX_WDT_TIMEOUT; + wdd->parent = &pdev->dev; watchdog_set_drvdata(wdd, wdt); diff --git a/kernel/drivers/watchdog/ts72xx_wdt.c b/kernel/drivers/watchdog/ts72xx_wdt.c index 119beb7f6..4b541934b 100644 --- a/kernel/drivers/watchdog/ts72xx_wdt.c +++ b/kernel/drivers/watchdog/ts72xx_wdt.c @@ -428,7 +428,8 @@ static int ts72xx_wdt_probe(struct platform_device *pdev) static int ts72xx_wdt_remove(struct platform_device *pdev) { - return misc_deregister(&ts72xx_wdt_miscdev); + misc_deregister(&ts72xx_wdt_miscdev); + return 0; } static struct platform_driver ts72xx_wdt_driver = { diff --git a/kernel/drivers/watchdog/twl4030_wdt.c b/kernel/drivers/watchdog/twl4030_wdt.c index 2c1db6fa9..9bf3cc0f3 100644 --- a/kernel/drivers/watchdog/twl4030_wdt.c +++ b/kernel/drivers/watchdog/twl4030_wdt.c @@ -83,6 +83,7 @@ static int twl4030_wdt_probe(struct platform_device *pdev) wdt->timeout = 30; wdt->min_timeout = 1; wdt->max_timeout = 30; + wdt->parent = &pdev->dev; watchdog_set_nowayout(wdt, nowayout); platform_set_drvdata(pdev, wdt); diff --git a/kernel/drivers/watchdog/txx9wdt.c b/kernel/drivers/watchdog/txx9wdt.c index 7f615933d..c2da88029 100644 --- a/kernel/drivers/watchdog/txx9wdt.c +++ b/kernel/drivers/watchdog/txx9wdt.c @@ -131,6 +131,7 @@ static int __init txx9wdt_probe(struct platform_device *dev) txx9wdt.timeout = timeout; txx9wdt.min_timeout = 1; txx9wdt.max_timeout = WD_MAX_TIMEOUT; + txx9wdt.parent = &dev->dev; watchdog_set_nowayout(&txx9wdt, nowayout); ret = watchdog_register_device(&txx9wdt); diff --git a/kernel/drivers/watchdog/ux500_wdt.c b/kernel/drivers/watchdog/ux500_wdt.c index 9de09ab00..37c084353 100644 --- a/kernel/drivers/watchdog/ux500_wdt.c +++ b/kernel/drivers/watchdog/ux500_wdt.c @@ -96,6 +96,7 @@ static int ux500_wdt_probe(struct platform_device *pdev) ux500_wdt.max_timeout = WATCHDOG_MAX28; } + ux500_wdt.parent = &pdev->dev; watchdog_set_nowayout(&ux500_wdt, nowayout); /* disable auto off on sleep */ diff --git a/kernel/drivers/watchdog/via_wdt.c b/kernel/drivers/watchdog/via_wdt.c index 56369c4f1..5f9cbc375 100644 --- a/kernel/drivers/watchdog/via_wdt.c +++ b/kernel/drivers/watchdog/via_wdt.c @@ -206,6 +206,7 @@ static int wdt_probe(struct pci_dev *pdev, timeout = WDT_TIMEOUT; wdt_dev.timeout = timeout; + wdt_dev.parent = &pdev->dev; watchdog_set_nowayout(&wdt_dev, nowayout); if (readl(wdt_mem) & VIA_WDT_FIRED) wdt_dev.bootstatus |= WDIOF_CARDRESET; diff --git a/kernel/drivers/watchdog/w83977f_wdt.c b/kernel/drivers/watchdog/w83977f_wdt.c index 91bf55a20..20e2bba10 100644 --- a/kernel/drivers/watchdog/w83977f_wdt.c +++ b/kernel/drivers/watchdog/w83977f_wdt.c @@ -224,7 +224,7 @@ static int wdt_keepalive(void) static int wdt_set_timeout(int t) { - int tmrval; + unsigned int tmrval; /* * Convert seconds to watchdog counter time units, rounding up. diff --git a/kernel/drivers/watchdog/watchdog_core.c b/kernel/drivers/watchdog/watchdog_core.c index cec9b5596..873f13972 100644 --- a/kernel/drivers/watchdog/watchdog_core.c +++ b/kernel/drivers/watchdog/watchdog_core.c @@ -43,6 +43,45 @@ static DEFINE_IDA(watchdog_ida); static struct class *watchdog_class; +/* + * Deferred Registration infrastructure. + * + * Sometimes watchdog drivers needs to be loaded as soon as possible, + * for example when it's impossible to disable it. To do so, + * raising the initcall level of the watchdog driver is a solution. + * But in such case, the miscdev is maybe not ready (subsys_initcall), and + * watchdog_core need miscdev to register the watchdog as a char device. + * + * The deferred registration infrastructure offer a way for the watchdog + * subsystem to register a watchdog properly, even before miscdev is ready. + */ + +static DEFINE_MUTEX(wtd_deferred_reg_mutex); +static LIST_HEAD(wtd_deferred_reg_list); +static bool wtd_deferred_reg_done; + +static int watchdog_deferred_registration_add(struct watchdog_device *wdd) +{ + list_add_tail(&wdd->deferred, + &wtd_deferred_reg_list); + return 0; +} + +static void watchdog_deferred_registration_del(struct watchdog_device *wdd) +{ + struct list_head *p, *n; + struct watchdog_device *wdd_tmp; + + list_for_each_safe(p, n, &wtd_deferred_reg_list) { + wdd_tmp = list_entry(p, struct watchdog_device, + deferred); + if (wdd_tmp == wdd) { + list_del(&wdd_tmp->deferred); + break; + } + } +} + static void watchdog_check_min_max_timeout(struct watchdog_device *wdd) { /* @@ -98,19 +137,9 @@ int watchdog_init_timeout(struct watchdog_device *wdd, } EXPORT_SYMBOL_GPL(watchdog_init_timeout); -/** - * watchdog_register_device() - register a watchdog device - * @wdd: watchdog device - * - * Register a watchdog device with the kernel so that the - * watchdog timer can be accessed from userspace. - * - * A zero is returned on success and a negative errno code for - * failure. - */ -int watchdog_register_device(struct watchdog_device *wdd) +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; @@ -128,7 +157,18 @@ 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; @@ -164,16 +204,33 @@ int watchdog_register_device(struct watchdog_device *wdd) return 0; } -EXPORT_SYMBOL_GPL(watchdog_register_device); /** - * watchdog_unregister_device() - unregister a watchdog device - * @wdd: watchdog device to unregister + * watchdog_register_device() - register a watchdog device + * @wdd: watchdog device * - * Unregister a watchdog device that was previously successfully - * registered with watchdog_register_device(). + * Register a watchdog device with the kernel so that the + * watchdog timer can be accessed from userspace. + * + * A zero is returned on success and a negative errno code for + * failure. */ -void watchdog_unregister_device(struct watchdog_device *wdd) + +int watchdog_register_device(struct watchdog_device *wdd) +{ + int ret; + + mutex_lock(&wtd_deferred_reg_mutex); + if (wtd_deferred_reg_done) + ret = __watchdog_register_device(wdd); + else + ret = watchdog_deferred_registration_add(wdd); + mutex_unlock(&wtd_deferred_reg_mutex); + return ret; +} +EXPORT_SYMBOL_GPL(watchdog_register_device); + +static void __watchdog_unregister_device(struct watchdog_device *wdd) { int ret; int devno; @@ -189,8 +246,43 @@ void watchdog_unregister_device(struct watchdog_device *wdd) ida_simple_remove(&watchdog_ida, wdd->id); wdd->dev = NULL; } + +/** + * watchdog_unregister_device() - unregister a watchdog device + * @wdd: watchdog device to unregister + * + * Unregister a watchdog device that was previously successfully + * registered with watchdog_register_device(). + */ + +void watchdog_unregister_device(struct watchdog_device *wdd) +{ + mutex_lock(&wtd_deferred_reg_mutex); + if (wtd_deferred_reg_done) + __watchdog_unregister_device(wdd); + else + watchdog_deferred_registration_del(wdd); + mutex_unlock(&wtd_deferred_reg_mutex); +} + EXPORT_SYMBOL_GPL(watchdog_unregister_device); +static int __init watchdog_deferred_registration(void) +{ + mutex_lock(&wtd_deferred_reg_mutex); + wtd_deferred_reg_done = true; + while (!list_empty(&wtd_deferred_reg_list)) { + struct watchdog_device *wdd; + + wdd = list_first_entry(&wtd_deferred_reg_list, + struct watchdog_device, deferred); + list_del(&wdd->deferred); + __watchdog_register_device(wdd); + } + mutex_unlock(&wtd_deferred_reg_mutex); + return 0; +} + static int __init watchdog_init(void) { int err; @@ -207,6 +299,7 @@ static int __init watchdog_init(void) return err; } + watchdog_deferred_registration(); return 0; } @@ -217,7 +310,7 @@ static void __exit watchdog_exit(void) ida_destroy(&watchdog_ida); } -subsys_initcall(watchdog_init); +subsys_initcall_sync(watchdog_init); module_exit(watchdog_exit); MODULE_AUTHOR("Alan Cox <alan@lxorguk.ukuu.org.uk>"); diff --git a/kernel/drivers/watchdog/watchdog_dev.c b/kernel/drivers/watchdog/watchdog_dev.c index 6aaefbad3..56a649e66 100644 --- a/kernel/drivers/watchdog/watchdog_dev.c +++ b/kernel/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; } @@ -295,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; @@ -314,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; } @@ -370,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; @@ -381,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 */ @@ -513,43 +516,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 +567,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; } diff --git a/kernel/drivers/watchdog/wm831x_wdt.c b/kernel/drivers/watchdog/wm831x_wdt.c index 2fa17e746..8d1184aee 100644 --- a/kernel/drivers/watchdog/wm831x_wdt.c +++ b/kernel/drivers/watchdog/wm831x_wdt.c @@ -215,6 +215,7 @@ static int wm831x_wdt_probe(struct platform_device *pdev) wm831x_wdt->info = &wm831x_wdt_info; wm831x_wdt->ops = &wm831x_wdt_ops; + wm831x_wdt->parent = &pdev->dev; watchdog_set_nowayout(wm831x_wdt, nowayout); watchdog_set_drvdata(wm831x_wdt, driver_data); diff --git a/kernel/drivers/watchdog/wm8350_wdt.c b/kernel/drivers/watchdog/wm8350_wdt.c index 34d272ada..4ab4b8347 100644 --- a/kernel/drivers/watchdog/wm8350_wdt.c +++ b/kernel/drivers/watchdog/wm8350_wdt.c @@ -151,6 +151,7 @@ static int wm8350_wdt_probe(struct platform_device *pdev) watchdog_set_nowayout(&wm8350_wdt, nowayout); watchdog_set_drvdata(&wm8350_wdt, wm8350); + wm8350_wdt.parent = &pdev->dev; /* Default to 4s timeout */ wm8350_wdt_set_timeout(&wm8350_wdt, 4); |