diff options
Diffstat (limited to 'drivers/watchdog')
-rw-r--r-- | drivers/watchdog/Kconfig | 36 | ||||
-rw-r--r-- | drivers/watchdog/Makefile | 3 | ||||
-rw-r--r-- | drivers/watchdog/cpwd.c | 4 | ||||
-rw-r--r-- | drivers/watchdog/ebc-c384_wdt.c | 43 | ||||
-rw-r--r-- | drivers/watchdog/f71808e_wdt.c | 30 | ||||
-rw-r--r-- | drivers/watchdog/imx2_wdt.c | 19 | ||||
-rw-r--r-- | drivers/watchdog/jz4740_wdt.c | 4 | ||||
-rw-r--r-- | drivers/watchdog/octeon-wdt-main.c | 2 | ||||
-rw-r--r-- | drivers/watchdog/pic32-dmt.c | 257 | ||||
-rw-r--r-- | drivers/watchdog/pic32-wdt.c | 263 | ||||
-rw-r--r-- | drivers/watchdog/qcom-wdt.c | 7 | ||||
-rw-r--r-- | drivers/watchdog/renesas_wdt.c | 213 | ||||
-rw-r--r-- | drivers/watchdog/shwdt.c | 4 | ||||
-rw-r--r-- | drivers/watchdog/watchdog_core.c | 4 |
14 files changed, 834 insertions, 55 deletions
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index fb947655b..b4b3e2564 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -661,6 +661,14 @@ config ATLAS7_WATCHDOG To compile this driver as a module, choose M here: the module will be called atlas7_wdt. +config RENESAS_WDT + tristate "Renesas WDT Watchdog" + depends on ARCH_RENESAS || COMPILE_TEST + select WATCHDOG_CORE + help + This driver adds watchdog support for the integrated watchdogs in the + Renesas R-Car and other SH-Mobile SoCs (usually named RWDT or SWDT). + # AVR32 Architecture config AT32AP700X_WDT @@ -738,7 +746,7 @@ config ALIM7101_WDT config EBC_C384_WDT tristate "WinSystems EBC-C384 Watchdog Timer" - depends on X86 + depends on X86 && ISA_BUS_API select WATCHDOG_CORE help Enables watchdog timer support for the watchdog timer on the @@ -1475,6 +1483,32 @@ config MT7621_WDT help Hardware driver for the Mediatek/Ralink MT7621/8 SoC Watchdog Timer. +config PIC32_WDT + tristate "Microchip PIC32 hardware watchdog" + select WATCHDOG_CORE + depends on MACH_PIC32 + help + Watchdog driver for the built in watchdog hardware in a PIC32. + + Configuration bits must be set appropriately for the watchdog to be + controlled by this driver. + + To compile this driver as a loadable module, choose M here. + The module will be called pic32-wdt. + +config PIC32_DMT + tristate "Microchip PIC32 Deadman Timer" + select WATCHDOG_CORE + depends on MACH_PIC32 + help + Watchdog driver for PIC32 instruction fetch counting timer. This specific + timer is typically be used in misson critical and safety critical + applications, where any single failure of the software functionality + and sequencing must be detected. + + To compile this driver as a loadable module, choose M here. + The module will be called pic32-dmt. + # PARISC Architecture # POWERPC Architecture diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index feb6270fd..a46e7c138 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -73,6 +73,7 @@ obj-$(CONFIG_DIGICOLOR_WATCHDOG) += digicolor_wdt.o obj-$(CONFIG_LPC18XX_WATCHDOG) += lpc18xx_wdt.o obj-$(CONFIG_BCM7038_WDT) += bcm7038_wdt.o obj-$(CONFIG_ATLAS7_WATCHDOG) += atlas7_wdt.o +obj-$(CONFIG_RENESAS_WDT) += renesas_wdt.o # AVR32 Architecture obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o @@ -157,6 +158,8 @@ obj-$(CONFIG_LANTIQ_WDT) += lantiq_wdt.o obj-$(CONFIG_RALINK_WDT) += rt2880_wdt.o obj-$(CONFIG_IMGPDC_WDT) += imgpdc_wdt.o obj-$(CONFIG_MT7621_WDT) += mt7621_wdt.o +obj-$(CONFIG_PIC32_WDT) += pic32-wdt.o +obj-$(CONFIG_PIC32_DMT) += pic32-dmt.o # PARISC Architecture diff --git a/drivers/watchdog/cpwd.c b/drivers/watchdog/cpwd.c index 02007689e..71ee07950 100644 --- a/drivers/watchdog/cpwd.c +++ b/drivers/watchdog/cpwd.c @@ -611,9 +611,7 @@ static int cpwd_probe(struct platform_device *op) } if (p->broken) { - init_timer(&cpwd_timer); - cpwd_timer.function = cpwd_brokentimer; - cpwd_timer.data = (unsigned long) p; + setup_timer(&cpwd_timer, cpwd_brokentimer, (unsigned long)p); cpwd_timer.expires = WD_BTIMEOUT; pr_info("PLD defect workaround enabled for model %s\n", diff --git a/drivers/watchdog/ebc-c384_wdt.c b/drivers/watchdog/ebc-c384_wdt.c index 77fda0b4b..4b849b8e3 100644 --- a/drivers/watchdog/ebc-c384_wdt.c +++ b/drivers/watchdog/ebc-c384_wdt.c @@ -16,10 +16,10 @@ #include <linux/errno.h> #include <linux/io.h> #include <linux/ioport.h> +#include <linux/isa.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/moduleparam.h> -#include <linux/platform_device.h> #include <linux/types.h> #include <linux/watchdog.h> @@ -95,9 +95,8 @@ static const struct watchdog_info ebc_c384_wdt_info = { .identity = MODULE_NAME }; -static int __init ebc_c384_wdt_probe(struct platform_device *pdev) +static int ebc_c384_wdt_probe(struct device *dev, unsigned int id) { - struct device *dev = &pdev->dev; struct watchdog_device *wdd; if (!devm_request_region(dev, BASE_ADDR, ADDR_EXTENT, dev_name(dev))) { @@ -122,61 +121,39 @@ static int __init ebc_c384_wdt_probe(struct platform_device *pdev) dev_warn(dev, "Invalid timeout (%u seconds), using default (%u seconds)\n", timeout, WATCHDOG_TIMEOUT); - platform_set_drvdata(pdev, wdd); + dev_set_drvdata(dev, wdd); return watchdog_register_device(wdd); } -static int ebc_c384_wdt_remove(struct platform_device *pdev) +static int ebc_c384_wdt_remove(struct device *dev, unsigned int id) { - struct watchdog_device *wdd = platform_get_drvdata(pdev); + struct watchdog_device *wdd = dev_get_drvdata(dev); watchdog_unregister_device(wdd); return 0; } -static struct platform_driver ebc_c384_wdt_driver = { +static struct isa_driver ebc_c384_wdt_driver = { + .probe = ebc_c384_wdt_probe, .driver = { .name = MODULE_NAME }, .remove = ebc_c384_wdt_remove }; -static struct platform_device *ebc_c384_wdt_device; - static int __init ebc_c384_wdt_init(void) { - int err; - if (!dmi_match(DMI_BOARD_NAME, "EBC-C384 SBC")) return -ENODEV; - ebc_c384_wdt_device = platform_device_alloc(MODULE_NAME, -1); - if (!ebc_c384_wdt_device) - return -ENOMEM; - - err = platform_device_add(ebc_c384_wdt_device); - if (err) - goto err_platform_device; - - err = platform_driver_probe(&ebc_c384_wdt_driver, ebc_c384_wdt_probe); - if (err) - goto err_platform_driver; - - return 0; - -err_platform_driver: - platform_device_del(ebc_c384_wdt_device); -err_platform_device: - platform_device_put(ebc_c384_wdt_device); - return err; + return isa_register_driver(&ebc_c384_wdt_driver, 1); } static void __exit ebc_c384_wdt_exit(void) { - platform_device_unregister(ebc_c384_wdt_device); - platform_driver_unregister(&ebc_c384_wdt_driver); + isa_unregister_driver(&ebc_c384_wdt_driver); } module_init(ebc_c384_wdt_init); @@ -185,4 +162,4 @@ module_exit(ebc_c384_wdt_exit); MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>"); MODULE_DESCRIPTION("WinSystems EBC-C384 watchdog timer driver"); MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:" MODULE_NAME); +MODULE_ALIAS("isa:" MODULE_NAME); diff --git a/drivers/watchdog/f71808e_wdt.c b/drivers/watchdog/f71808e_wdt.c index 016bd9355..d4ba262da 100644 --- a/drivers/watchdog/f71808e_wdt.c +++ b/drivers/watchdog/f71808e_wdt.c @@ -38,7 +38,7 @@ #define SIO_F71808FG_LD_WDT 0x07 /* Watchdog timer logical device */ #define SIO_UNLOCK_KEY 0x87 /* Key to enable Super-I/O */ -#define SIO_LOCK_KEY 0xAA /* Key to diasble Super-I/O */ +#define SIO_LOCK_KEY 0xAA /* Key to disable Super-I/O */ #define SIO_REG_LDSEL 0x07 /* Logical device select */ #define SIO_REG_DEVID 0x20 /* Device ID (2 bytes) */ @@ -59,6 +59,7 @@ #define SIO_F71869A_ID 0x1007 /* Chipset ID */ #define SIO_F71882_ID 0x0541 /* Chipset ID */ #define SIO_F71889_ID 0x0723 /* Chipset ID */ +#define SIO_F81865_ID 0x0704 /* Chipset ID */ #define F71808FG_REG_WDO_CONF 0xf0 #define F71808FG_REG_WDT_CONF 0xf5 @@ -66,11 +67,14 @@ #define F71808FG_FLAG_WDOUT_EN 7 -#define F71808FG_FLAG_WDTMOUT_STS 5 +#define F71808FG_FLAG_WDTMOUT_STS 6 #define F71808FG_FLAG_WD_EN 5 #define F71808FG_FLAG_WD_PULSE 4 #define F71808FG_FLAG_WD_UNIT 3 +#define F81865_REG_WDO_CONF 0xfa +#define F81865_FLAG_WDOUT_EN 0 + /* Default values */ #define WATCHDOG_TIMEOUT 60 /* 1 minute default timeout */ #define WATCHDOG_MAX_TIMEOUT (60 * 255) @@ -112,7 +116,7 @@ module_param(start_withtimeout, uint, 0); MODULE_PARM_DESC(start_withtimeout, "Start watchdog timer on module load with" " given initial timeout. Zero (default) disables this feature."); -enum chips { f71808fg, f71858fg, f71862fg, f71869, f71882fg, f71889fg }; +enum chips { f71808fg, f71858fg, f71862fg, f71869, f71882fg, f71889fg, f81865 }; static const char *f71808e_names[] = { "f71808fg", @@ -121,6 +125,7 @@ static const char *f71808e_names[] = { "f71869", "f71882fg", "f71889fg", + "f81865", }; /* Super-I/O Function prototypes */ @@ -360,6 +365,11 @@ static int watchdog_start(void) superio_inb(watchdog.sioaddr, SIO_REG_MFUNCT3) & 0xcf); break; + case f81865: + /* Set pin 70 to WDTRST# */ + superio_clear_bit(watchdog.sioaddr, SIO_REG_MFUNCT3, 5); + break; + default: /* * 'default' label to shut up the compiler and catch @@ -371,8 +381,13 @@ static int watchdog_start(void) superio_select(watchdog.sioaddr, SIO_F71808FG_LD_WDT); superio_set_bit(watchdog.sioaddr, SIO_REG_ENABLE, 0); - superio_set_bit(watchdog.sioaddr, F71808FG_REG_WDO_CONF, - F71808FG_FLAG_WDOUT_EN); + + if (watchdog.type == f81865) + superio_set_bit(watchdog.sioaddr, F81865_REG_WDO_CONF, + F81865_FLAG_WDOUT_EN); + else + superio_set_bit(watchdog.sioaddr, F71808FG_REG_WDO_CONF, + F71808FG_FLAG_WDOUT_EN); superio_set_bit(watchdog.sioaddr, F71808FG_REG_WDT_CONF, F71808FG_FLAG_WD_EN); @@ -655,7 +670,7 @@ static int __init watchdog_init(int sioaddr) superio_select(watchdog.sioaddr, SIO_F71808FG_LD_WDT); wdt_conf = superio_inb(sioaddr, F71808FG_REG_WDT_CONF); - watchdog.caused_reboot = wdt_conf & F71808FG_FLAG_WDTMOUT_STS; + watchdog.caused_reboot = wdt_conf & BIT(F71808FG_FLAG_WDTMOUT_STS); superio_exit(sioaddr); @@ -770,6 +785,9 @@ static int __init f71808e_find(int sioaddr) /* Confirmed (by datasheet) not to have a watchdog. */ err = -ENODEV; goto exit; + case SIO_F81865_ID: + watchdog.type = f81865; + break; default: pr_info("Unrecognized Fintek device: %04x\n", (unsigned int)devid); diff --git a/drivers/watchdog/imx2_wdt.c b/drivers/watchdog/imx2_wdt.c index 331aed831..62f346bb4 100644 --- a/drivers/watchdog/imx2_wdt.c +++ b/drivers/watchdog/imx2_wdt.c @@ -37,6 +37,8 @@ #define IMX2_WDT_WCR 0x00 /* Control Register */ #define IMX2_WDT_WCR_WT (0xFF << 8) /* -> Watchdog Timeout Field */ +#define IMX2_WDT_WCR_WDA (1 << 5) /* -> External Reset WDOG_B */ +#define IMX2_WDT_WCR_SRS (1 << 4) /* -> Software Reset Signal */ #define IMX2_WDT_WCR_WRE (1 << 3) /* -> WDOG Reset Enable */ #define IMX2_WDT_WCR_WDE (1 << 2) /* -> Watchdog Enable */ #define IMX2_WDT_WCR_WDZST (1 << 0) /* -> Watchdog timer Suspend */ @@ -59,6 +61,7 @@ struct imx2_wdt_device { struct clk *clk; struct regmap *regmap; struct watchdog_device wdog; + bool ext_reset; }; static bool nowayout = WATCHDOG_NOWAYOUT; @@ -83,6 +86,12 @@ static int imx2_wdt_restart(struct watchdog_device *wdog, unsigned long action, struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog); unsigned int wcr_enable = IMX2_WDT_WCR_WDE; + /* Use internal reset or external - not both */ + if (wdev->ext_reset) + wcr_enable |= IMX2_WDT_WCR_SRS; /* do not assert int reset */ + else + wcr_enable |= IMX2_WDT_WCR_WDA; /* do not assert ext-reset */ + /* Assert SRS signal */ regmap_write(wdev->regmap, IMX2_WDT_WCR, wcr_enable); /* @@ -112,8 +121,12 @@ static inline void imx2_wdt_setup(struct watchdog_device *wdog) val |= IMX2_WDT_WCR_WDZST; /* Strip the old watchdog Time-Out value */ val &= ~IMX2_WDT_WCR_WT; - /* Generate reset if WDOG times out */ - val &= ~IMX2_WDT_WCR_WRE; + /* Generate internal chip-level reset if WDOG times out */ + if (!wdev->ext_reset) + val &= ~IMX2_WDT_WCR_WRE; + /* Or if external-reset assert WDOG_B reset only on time-out */ + else + val |= IMX2_WDT_WCR_WRE; /* Keep Watchdog Disabled */ val &= ~IMX2_WDT_WCR_WDE; /* Set the watchdog's Time-Out value */ @@ -230,6 +243,8 @@ static int __init imx2_wdt_probe(struct platform_device *pdev) regmap_read(wdev->regmap, IMX2_WDT_WRSR, &val); wdog->bootstatus = val & IMX2_WDT_WRSR_TOUT ? WDIOF_CARDRESET : 0; + wdev->ext_reset = of_property_read_bool(pdev->dev.of_node, + "fsl,ext-reset-output"); wdog->timeout = clamp_t(unsigned, timeout, 1, IMX2_WDT_MAX_TIME); if (wdog->timeout != timeout) dev_warn(&pdev->dev, "Initial timeout out of range! Clamped from %u to %u\n", diff --git a/drivers/watchdog/jz4740_wdt.c b/drivers/watchdog/jz4740_wdt.c index 6a7d5c365..c8d51ddb2 100644 --- a/drivers/watchdog/jz4740_wdt.c +++ b/drivers/watchdog/jz4740_wdt.c @@ -160,10 +160,8 @@ static int jz4740_wdt_probe(struct platform_device *pdev) drvdata = devm_kzalloc(&pdev->dev, sizeof(struct jz4740_wdt_drvdata), GFP_KERNEL); - if (!drvdata) { - dev_err(&pdev->dev, "Unable to alloacate watchdog device\n"); + if (!drvdata) return -ENOMEM; - } if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT) heartbeat = DEFAULT_HEARTBEAT; diff --git a/drivers/watchdog/octeon-wdt-main.c b/drivers/watchdog/octeon-wdt-main.c index 14521c8b3..b55981f88 100644 --- a/drivers/watchdog/octeon-wdt-main.c +++ b/drivers/watchdog/octeon-wdt-main.c @@ -431,7 +431,7 @@ static int octeon_wdt_cpu_callback(struct notifier_block *nfb, { unsigned int cpu = (unsigned long)hcpu; - switch (action) { + switch (action & ~CPU_TASKS_FROZEN) { case CPU_DOWN_PREPARE: octeon_wdt_disable_interrupt(cpu); break; diff --git a/drivers/watchdog/pic32-dmt.c b/drivers/watchdog/pic32-dmt.c new file mode 100644 index 000000000..962f58c03 --- /dev/null +++ b/drivers/watchdog/pic32-dmt.c @@ -0,0 +1,257 @@ +/* + * PIC32 deadman timer driver + * + * Purna Chandra Mandal <purna.mandal@microchip.com> + * Copyright (c) 2016, Microchip Technology Inc. + * + * 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/clk.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/pm.h> +#include <linux/watchdog.h> + +#include <asm/mach-pic32/pic32.h> + +/* Deadman Timer Regs */ +#define DMTCON_REG 0x00 +#define DMTPRECLR_REG 0x10 +#define DMTCLR_REG 0x20 +#define DMTSTAT_REG 0x30 +#define DMTCNT_REG 0x40 +#define DMTPSCNT_REG 0x60 +#define DMTPSINTV_REG 0x70 + +/* Deadman Timer Regs fields */ +#define DMT_ON BIT(15) +#define DMT_STEP1_KEY BIT(6) +#define DMT_STEP2_KEY BIT(3) +#define DMTSTAT_WINOPN BIT(0) +#define DMTSTAT_EVENT BIT(5) +#define DMTSTAT_BAD2 BIT(6) +#define DMTSTAT_BAD1 BIT(7) + +/* Reset Control Register fields for watchdog */ +#define RESETCON_DMT_TIMEOUT BIT(5) + +struct pic32_dmt { + void __iomem *regs; + struct clk *clk; +}; + +static inline void dmt_enable(struct pic32_dmt *dmt) +{ + writel(DMT_ON, PIC32_SET(dmt->regs + DMTCON_REG)); +} + +static inline void dmt_disable(struct pic32_dmt *dmt) +{ + writel(DMT_ON, PIC32_CLR(dmt->regs + DMTCON_REG)); + /* + * Cannot touch registers in the CPU cycle following clearing the + * ON bit. + */ + nop(); +} + +static inline int dmt_bad_status(struct pic32_dmt *dmt) +{ + u32 val; + + val = readl(dmt->regs + DMTSTAT_REG); + val &= (DMTSTAT_BAD1 | DMTSTAT_BAD2 | DMTSTAT_EVENT); + if (val) + return -EAGAIN; + + return 0; +} + +static inline int dmt_keepalive(struct pic32_dmt *dmt) +{ + u32 v; + u32 timeout = 500; + + /* set pre-clear key */ + writel(DMT_STEP1_KEY << 8, dmt->regs + DMTPRECLR_REG); + + /* wait for DMT window to open */ + while (--timeout) { + v = readl(dmt->regs + DMTSTAT_REG) & DMTSTAT_WINOPN; + if (v == DMTSTAT_WINOPN) + break; + } + + /* apply key2 */ + writel(DMT_STEP2_KEY, dmt->regs + DMTCLR_REG); + + /* check whether keys are latched correctly */ + return dmt_bad_status(dmt); +} + +static inline u32 pic32_dmt_get_timeout_secs(struct pic32_dmt *dmt) +{ + unsigned long rate; + + rate = clk_get_rate(dmt->clk); + if (rate) + return readl(dmt->regs + DMTPSCNT_REG) / rate; + + return 0; +} + +static inline u32 pic32_dmt_bootstatus(struct pic32_dmt *dmt) +{ + u32 v; + void __iomem *rst_base; + + rst_base = ioremap(PIC32_BASE_RESET, 0x10); + if (!rst_base) + return 0; + + v = readl(rst_base); + + writel(RESETCON_DMT_TIMEOUT, PIC32_CLR(rst_base)); + + iounmap(rst_base); + return v & RESETCON_DMT_TIMEOUT; +} + +static int pic32_dmt_start(struct watchdog_device *wdd) +{ + struct pic32_dmt *dmt = watchdog_get_drvdata(wdd); + + dmt_enable(dmt); + return dmt_keepalive(dmt); +} + +static int pic32_dmt_stop(struct watchdog_device *wdd) +{ + struct pic32_dmt *dmt = watchdog_get_drvdata(wdd); + + dmt_disable(dmt); + + return 0; +} + +static int pic32_dmt_ping(struct watchdog_device *wdd) +{ + struct pic32_dmt *dmt = watchdog_get_drvdata(wdd); + + return dmt_keepalive(dmt); +} + +static const struct watchdog_ops pic32_dmt_fops = { + .owner = THIS_MODULE, + .start = pic32_dmt_start, + .stop = pic32_dmt_stop, + .ping = pic32_dmt_ping, +}; + +static const struct watchdog_info pic32_dmt_ident = { + .options = WDIOF_KEEPALIVEPING | + WDIOF_MAGICCLOSE, + .identity = "PIC32 Deadman Timer", +}; + +static struct watchdog_device pic32_dmt_wdd = { + .info = &pic32_dmt_ident, + .ops = &pic32_dmt_fops, +}; + +static int pic32_dmt_probe(struct platform_device *pdev) +{ + int ret; + struct pic32_dmt *dmt; + struct resource *mem; + struct watchdog_device *wdd = &pic32_dmt_wdd; + + dmt = devm_kzalloc(&pdev->dev, sizeof(*dmt), GFP_KERNEL); + if (IS_ERR(dmt)) + return PTR_ERR(dmt); + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + dmt->regs = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(dmt->regs)) + return PTR_ERR(dmt->regs); + + dmt->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(dmt->clk)) { + dev_err(&pdev->dev, "clk not found\n"); + return PTR_ERR(dmt->clk); + } + + ret = clk_prepare_enable(dmt->clk); + if (ret) + return ret; + + wdd->timeout = pic32_dmt_get_timeout_secs(dmt); + if (!wdd->timeout) { + dev_err(&pdev->dev, + "failed to read watchdog register timeout\n"); + ret = -EINVAL; + goto out_disable_clk; + } + + dev_info(&pdev->dev, "timeout %d\n", wdd->timeout); + + wdd->bootstatus = pic32_dmt_bootstatus(dmt) ? WDIOF_CARDRESET : 0; + + watchdog_set_nowayout(wdd, WATCHDOG_NOWAYOUT); + watchdog_set_drvdata(wdd, dmt); + + ret = watchdog_register_device(wdd); + if (ret) { + dev_err(&pdev->dev, "watchdog register failed, err %d\n", ret); + goto out_disable_clk; + } + + platform_set_drvdata(pdev, wdd); + return 0; + +out_disable_clk: + clk_disable_unprepare(dmt->clk); + return ret; +} + +static int pic32_dmt_remove(struct platform_device *pdev) +{ + struct watchdog_device *wdd = platform_get_drvdata(pdev); + struct pic32_dmt *dmt = watchdog_get_drvdata(wdd); + + watchdog_unregister_device(wdd); + clk_disable_unprepare(dmt->clk); + + return 0; +} + +static const struct of_device_id pic32_dmt_of_ids[] = { + { .compatible = "microchip,pic32mzda-dmt",}, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, pic32_dmt_of_ids); + +static struct platform_driver pic32_dmt_driver = { + .probe = pic32_dmt_probe, + .remove = pic32_dmt_remove, + .driver = { + .name = "pic32-dmt", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(pic32_dmt_of_ids), + } +}; + +module_platform_driver(pic32_dmt_driver); + +MODULE_AUTHOR("Purna Chandra Mandal <purna.mandal@microchip.com>"); +MODULE_DESCRIPTION("Microchip PIC32 DMT Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/watchdog/pic32-wdt.c b/drivers/watchdog/pic32-wdt.c new file mode 100644 index 000000000..6047aa89a --- /dev/null +++ b/drivers/watchdog/pic32-wdt.c @@ -0,0 +1,263 @@ +/* + * PIC32 watchdog driver + * + * Joshua Henderson <joshua.henderson@microchip.com> + * Copyright (c) 2016, Microchip Technology Inc. + * + * 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/clk.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/pm.h> +#include <linux/watchdog.h> + +#include <asm/mach-pic32/pic32.h> + +/* Watchdog Timer Registers */ +#define WDTCON_REG 0x00 + +/* Watchdog Timer Control Register fields */ +#define WDTCON_WIN_EN BIT(0) +#define WDTCON_RMCS_MASK 0x0003 +#define WDTCON_RMCS_SHIFT 0x0006 +#define WDTCON_RMPS_MASK 0x001F +#define WDTCON_RMPS_SHIFT 0x0008 +#define WDTCON_ON BIT(15) +#define WDTCON_CLR_KEY 0x5743 + +/* Reset Control Register fields for watchdog */ +#define RESETCON_TIMEOUT_IDLE BIT(2) +#define RESETCON_TIMEOUT_SLEEP BIT(3) +#define RESETCON_WDT_TIMEOUT BIT(4) + +struct pic32_wdt { + void __iomem *regs; + void __iomem *rst_base; + struct clk *clk; +}; + +static inline bool pic32_wdt_is_win_enabled(struct pic32_wdt *wdt) +{ + return !!(readl(wdt->regs + WDTCON_REG) & WDTCON_WIN_EN); +} + +static inline u32 pic32_wdt_get_post_scaler(struct pic32_wdt *wdt) +{ + u32 v = readl(wdt->regs + WDTCON_REG); + + return (v >> WDTCON_RMPS_SHIFT) & WDTCON_RMPS_MASK; +} + +static inline u32 pic32_wdt_get_clk_id(struct pic32_wdt *wdt) +{ + u32 v = readl(wdt->regs + WDTCON_REG); + + return (v >> WDTCON_RMCS_SHIFT) & WDTCON_RMCS_MASK; +} + +static int pic32_wdt_bootstatus(struct pic32_wdt *wdt) +{ + u32 v = readl(wdt->rst_base); + + writel(RESETCON_WDT_TIMEOUT, PIC32_CLR(wdt->rst_base)); + + return v & RESETCON_WDT_TIMEOUT; +} + +static u32 pic32_wdt_get_timeout_secs(struct pic32_wdt *wdt, struct device *dev) +{ + unsigned long rate; + u32 period, ps, terminal; + + rate = clk_get_rate(wdt->clk); + + dev_dbg(dev, "wdt: clk_id %d, clk_rate %lu (prescale)\n", + pic32_wdt_get_clk_id(wdt), rate); + + /* default, prescaler of 32 (i.e. div-by-32) is implicit. */ + rate >>= 5; + if (!rate) + return 0; + + /* calculate terminal count from postscaler. */ + ps = pic32_wdt_get_post_scaler(wdt); + terminal = BIT(ps); + + /* find time taken (in secs) to reach terminal count */ + period = terminal / rate; + dev_dbg(dev, + "wdt: clk_rate %lu (postscale) / terminal %d, timeout %dsec\n", + rate, terminal, period); + + return period; +} + +static void pic32_wdt_keepalive(struct pic32_wdt *wdt) +{ + /* write key through single half-word */ + writew(WDTCON_CLR_KEY, wdt->regs + WDTCON_REG + 2); +} + +static int pic32_wdt_start(struct watchdog_device *wdd) +{ + struct pic32_wdt *wdt = watchdog_get_drvdata(wdd); + + writel(WDTCON_ON, PIC32_SET(wdt->regs + WDTCON_REG)); + pic32_wdt_keepalive(wdt); + + return 0; +} + +static int pic32_wdt_stop(struct watchdog_device *wdd) +{ + struct pic32_wdt *wdt = watchdog_get_drvdata(wdd); + + writel(WDTCON_ON, PIC32_CLR(wdt->regs + WDTCON_REG)); + + /* + * Cannot touch registers in the CPU cycle following clearing the + * ON bit. + */ + nop(); + + return 0; +} + +static int pic32_wdt_ping(struct watchdog_device *wdd) +{ + struct pic32_wdt *wdt = watchdog_get_drvdata(wdd); + + pic32_wdt_keepalive(wdt); + + return 0; +} + +static const struct watchdog_ops pic32_wdt_fops = { + .owner = THIS_MODULE, + .start = pic32_wdt_start, + .stop = pic32_wdt_stop, + .ping = pic32_wdt_ping, +}; + +static const struct watchdog_info pic32_wdt_ident = { + .options = WDIOF_KEEPALIVEPING | + WDIOF_MAGICCLOSE | WDIOF_CARDRESET, + .identity = "PIC32 Watchdog", +}; + +static struct watchdog_device pic32_wdd = { + .info = &pic32_wdt_ident, + .ops = &pic32_wdt_fops, +}; + +static const struct of_device_id pic32_wdt_dt_ids[] = { + { .compatible = "microchip,pic32mzda-wdt", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, pic32_wdt_dt_ids); + +static int pic32_wdt_drv_probe(struct platform_device *pdev) +{ + int ret; + struct watchdog_device *wdd = &pic32_wdd; + struct pic32_wdt *wdt; + struct resource *mem; + + wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); + if (IS_ERR(wdt)) + return PTR_ERR(wdt); + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + wdt->regs = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(wdt->regs)) + return PTR_ERR(wdt->regs); + + wdt->rst_base = devm_ioremap(&pdev->dev, PIC32_BASE_RESET, 0x10); + if (IS_ERR(wdt->rst_base)) + return PTR_ERR(wdt->rst_base); + + wdt->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(wdt->clk)) { + dev_err(&pdev->dev, "clk not found\n"); + return PTR_ERR(wdt->clk); + } + + ret = clk_prepare_enable(wdt->clk); + if (ret) { + dev_err(&pdev->dev, "clk enable failed\n"); + return ret; + } + + if (pic32_wdt_is_win_enabled(wdt)) { + dev_err(&pdev->dev, "windowed-clear mode is not supported.\n"); + ret = -ENODEV; + goto out_disable_clk; + } + + wdd->timeout = pic32_wdt_get_timeout_secs(wdt, &pdev->dev); + if (!wdd->timeout) { + dev_err(&pdev->dev, + "failed to read watchdog register timeout\n"); + ret = -EINVAL; + goto out_disable_clk; + } + + dev_info(&pdev->dev, "timeout %d\n", wdd->timeout); + + wdd->bootstatus = pic32_wdt_bootstatus(wdt) ? WDIOF_CARDRESET : 0; + + watchdog_set_nowayout(wdd, WATCHDOG_NOWAYOUT); + watchdog_set_drvdata(wdd, wdt); + + ret = watchdog_register_device(wdd); + if (ret) { + dev_err(&pdev->dev, "watchdog register failed, err %d\n", ret); + goto out_disable_clk; + } + + platform_set_drvdata(pdev, wdd); + + return 0; + +out_disable_clk: + clk_disable_unprepare(wdt->clk); + + return ret; +} + +static int pic32_wdt_drv_remove(struct platform_device *pdev) +{ + struct watchdog_device *wdd = platform_get_drvdata(pdev); + struct pic32_wdt *wdt = watchdog_get_drvdata(wdd); + + watchdog_unregister_device(wdd); + clk_disable_unprepare(wdt->clk); + + return 0; +} + +static struct platform_driver pic32_wdt_driver = { + .probe = pic32_wdt_drv_probe, + .remove = pic32_wdt_drv_remove, + .driver = { + .name = "pic32-wdt", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(pic32_wdt_dt_ids), + } +}; + +module_platform_driver(pic32_wdt_driver); + +MODULE_AUTHOR("Joshua Henderson <joshua.henderson@microchip.com>"); +MODULE_DESCRIPTION("Microchip PIC32 Watchdog Timer"); +MODULE_LICENSE("GPL"); diff --git a/drivers/watchdog/qcom-wdt.c b/drivers/watchdog/qcom-wdt.c index 20563ccb7..a043fa4f6 100644 --- a/drivers/watchdog/qcom-wdt.c +++ b/drivers/watchdog/qcom-wdt.c @@ -21,6 +21,7 @@ #define WDT_RST 0x38 #define WDT_EN 0x40 +#define WDT_STS 0x44 #define WDT_BITE_TIME 0x5C struct qcom_wdt { @@ -108,7 +109,8 @@ static const struct watchdog_ops qcom_wdt_ops = { static const struct watchdog_info qcom_wdt_info = { .options = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE - | WDIOF_SETTIMEOUT, + | WDIOF_SETTIMEOUT + | WDIOF_CARDRESET, .identity = KBUILD_MODNAME, }; @@ -171,6 +173,9 @@ static int qcom_wdt_probe(struct platform_device *pdev) wdt->wdd.max_timeout = 0x10000000U / wdt->rate; wdt->wdd.parent = &pdev->dev; + if (readl(wdt->base + WDT_STS) & 1) + wdt->wdd.bootstatus = WDIOF_CARDRESET; + /* * If 'timeout-sec' unspecified in devicetree, assume a 30 second * default, unless the max timeout is less than 30 seconds, then use diff --git a/drivers/watchdog/renesas_wdt.c b/drivers/watchdog/renesas_wdt.c new file mode 100644 index 000000000..cf61c92f7 --- /dev/null +++ b/drivers/watchdog/renesas_wdt.c @@ -0,0 +1,213 @@ +/* + * Watchdog driver for Renesas WDT watchdog + * + * Copyright (C) 2015-16 Wolfram Sang, Sang Engineering <wsa@sang-engineering.com> + * Copyright (C) 2015-16 Renesas Electronics Corporation + * + * 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. + */ +#include <linux/bitops.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/watchdog.h> + +#define RWTCNT 0 +#define RWTCSRA 4 +#define RWTCSRA_WOVF BIT(4) +#define RWTCSRA_WRFLG BIT(5) +#define RWTCSRA_TME BIT(7) + +#define RWDT_DEFAULT_TIMEOUT 60U + +static const unsigned int clk_divs[] = { 1, 4, 16, 32, 64, 128, 1024 }; + +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 rwdt_priv { + void __iomem *base; + struct watchdog_device wdev; + struct clk *clk; + unsigned int clks_per_sec; + u8 cks; +}; + +static void rwdt_write(struct rwdt_priv *priv, u32 val, unsigned int reg) +{ + if (reg == RWTCNT) + val |= 0x5a5a0000; + else + val |= 0xa5a5a500; + + writel_relaxed(val, priv->base + reg); +} + +static int rwdt_init_timeout(struct watchdog_device *wdev) +{ + struct rwdt_priv *priv = watchdog_get_drvdata(wdev); + + rwdt_write(priv, 65536 - wdev->timeout * priv->clks_per_sec, RWTCNT); + + return 0; +} + +static int rwdt_start(struct watchdog_device *wdev) +{ + struct rwdt_priv *priv = watchdog_get_drvdata(wdev); + + clk_prepare_enable(priv->clk); + + rwdt_write(priv, priv->cks, RWTCSRA); + rwdt_init_timeout(wdev); + + while (readb_relaxed(priv->base + RWTCSRA) & RWTCSRA_WRFLG) + cpu_relax(); + + rwdt_write(priv, priv->cks | RWTCSRA_TME, RWTCSRA); + + return 0; +} + +static int rwdt_stop(struct watchdog_device *wdev) +{ + struct rwdt_priv *priv = watchdog_get_drvdata(wdev); + + rwdt_write(priv, priv->cks, RWTCSRA); + clk_disable_unprepare(priv->clk); + + return 0; +} + +static unsigned int rwdt_get_timeleft(struct watchdog_device *wdev) +{ + struct rwdt_priv *priv = watchdog_get_drvdata(wdev); + u16 val = readw_relaxed(priv->base + RWTCNT); + + return DIV_ROUND_CLOSEST(65536 - val, priv->clks_per_sec); +} + +static const struct watchdog_info rwdt_ident = { + .options = WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT, + .identity = "Renesas WDT Watchdog", +}; + +static const struct watchdog_ops rwdt_ops = { + .owner = THIS_MODULE, + .start = rwdt_start, + .stop = rwdt_stop, + .ping = rwdt_init_timeout, + .get_timeleft = rwdt_get_timeleft, +}; + +static int rwdt_probe(struct platform_device *pdev) +{ + struct rwdt_priv *priv; + struct resource *res; + unsigned long rate; + unsigned int clks_per_sec; + int ret, i; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + priv->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(priv->clk)) + return PTR_ERR(priv->clk); + + rate = clk_get_rate(priv->clk); + if (!rate) + return -ENOENT; + + for (i = ARRAY_SIZE(clk_divs) - 1; i >= 0; i--) { + clks_per_sec = DIV_ROUND_UP(rate, clk_divs[i]); + if (clks_per_sec) { + priv->clks_per_sec = clks_per_sec; + priv->cks = i; + break; + } + } + + if (!clks_per_sec) { + dev_err(&pdev->dev, "Can't find suitable clock divider\n"); + return -ERANGE; + } + + pm_runtime_enable(&pdev->dev); + pm_runtime_get_sync(&pdev->dev); + + priv->wdev.info = &rwdt_ident, + priv->wdev.ops = &rwdt_ops, + priv->wdev.parent = &pdev->dev; + priv->wdev.min_timeout = 1; + priv->wdev.max_timeout = 65536 / clks_per_sec; + priv->wdev.timeout = min(priv->wdev.max_timeout, RWDT_DEFAULT_TIMEOUT); + + platform_set_drvdata(pdev, priv); + watchdog_set_drvdata(&priv->wdev, priv); + watchdog_set_nowayout(&priv->wdev, nowayout); + + /* This overrides the default timeout only if DT configuration was found */ + ret = watchdog_init_timeout(&priv->wdev, 0, &pdev->dev); + if (ret) + dev_warn(&pdev->dev, "Specified timeout value invalid, using default\n"); + + ret = watchdog_register_device(&priv->wdev); + if (ret < 0) { + pm_runtime_put(&pdev->dev); + pm_runtime_disable(&pdev->dev); + return ret; + } + + return 0; +} + +static int rwdt_remove(struct platform_device *pdev) +{ + struct rwdt_priv *priv = platform_get_drvdata(pdev); + + watchdog_unregister_device(&priv->wdev); + pm_runtime_put(&pdev->dev); + pm_runtime_disable(&pdev->dev); + + return 0; +} + +/* + * This driver does also fit for R-Car Gen2 (r8a779[0-4]) WDT. However, for SMP + * to work there, one also needs a RESET (RST) driver which does not exist yet + * due to HW issues. This needs to be solved before adding compatibles here. + */ +static const struct of_device_id rwdt_ids[] = { + { .compatible = "renesas,rcar-gen3-wdt", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, rwdt_ids); + +static struct platform_driver rwdt_driver = { + .driver = { + .name = "renesas_wdt", + .of_match_table = rwdt_ids, + }, + .probe = rwdt_probe, + .remove = rwdt_remove, +}; +module_platform_driver(rwdt_driver); + +MODULE_DESCRIPTION("Renesas WDT Watchdog Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Wolfram Sang <wsa@sang-engineering.com>"); diff --git a/drivers/watchdog/shwdt.c b/drivers/watchdog/shwdt.c index f90812170..517a73317 100644 --- a/drivers/watchdog/shwdt.c +++ b/drivers/watchdog/shwdt.c @@ -275,9 +275,7 @@ static int sh_wdt_probe(struct platform_device *pdev) return rc; } - init_timer(&wdt->timer); - wdt->timer.function = sh_wdt_ping; - wdt->timer.data = (unsigned long)wdt; + setup_timer(&wdt->timer, sh_wdt_ping, (unsigned long)wdt); wdt->timer.expires = next_ping_period(clock_division_ratio); dev_info(&pdev->dev, "initialized.\n"); diff --git a/drivers/watchdog/watchdog_core.c b/drivers/watchdog/watchdog_core.c index c1658fe73..7c3ba58ae 100644 --- a/drivers/watchdog/watchdog_core.c +++ b/drivers/watchdog/watchdog_core.c @@ -104,7 +104,7 @@ static void watchdog_check_min_max_timeout(struct watchdog_device *wdd) * timeout module parameter (if it is valid value) or the timeout-sec property * (only if it is a valid value and the timeout_parm is out of bounds). * If none of them are valid then we keep the old value (which should normally - * be the default timeout value. + * be the default timeout value). * * A zero is returned on success and -EINVAL for failure. */ @@ -262,7 +262,7 @@ static int __watchdog_register_device(struct watchdog_device *wdd) ret = register_restart_handler(&wdd->restart_nb); if (ret) - pr_warn("watchog%d: Cannot register restart handler (%d)\n", + pr_warn("watchdog%d: Cannot register restart handler (%d)\n", wdd->id, ret); } |