diff --git a/Documentation/devicetree/bindings/watchdog/cortina,gemin-watchdog.txt b/Documentation/devicetree/bindings/watchdog/cortina,gemin-watchdog.txt new file mode 100644 index 000000000000..bc4b865d178b --- /dev/null +++ b/Documentation/devicetree/bindings/watchdog/cortina,gemin-watchdog.txt @@ -0,0 +1,17 @@ +Cortina Systems Gemini SoC Watchdog + +Required properties: +- compatible : must be "cortina,gemini-watchdog" +- reg : shall contain base register location and length +- interrupts : shall contain the interrupt for the watchdog + +Optional properties: +- timeout-sec : the default watchdog timeout in seconds. + +Example: + +watchdog@41000000 { + compatible = "cortina,gemini-watchdog"; + reg = <0x41000000 0x1000>; + interrupts = <3 IRQ_TYPE_LEVEL_HIGH>; +}; diff --git a/Documentation/devicetree/bindings/watchdog/samsung-wdt.txt b/Documentation/devicetree/bindings/watchdog/samsung-wdt.txt index 8f3d96af81d7..1f6e101e299a 100644 --- a/Documentation/devicetree/bindings/watchdog/samsung-wdt.txt +++ b/Documentation/devicetree/bindings/watchdog/samsung-wdt.txt @@ -6,10 +6,11 @@ occurred. Required properties: - compatible : should be one among the following - (a) "samsung,s3c2410-wdt" for Exynos4 and previous SoCs - (b) "samsung,exynos5250-wdt" for Exynos5250 - (c) "samsung,exynos5420-wdt" for Exynos5420 - (c) "samsung,exynos7-wdt" for Exynos7 + - "samsung,s3c2410-wdt" for S3C2410 + - "samsung,s3c6410-wdt" for S3C6410, S5PV210 and Exynos4 + - "samsung,exynos5250-wdt" for Exynos5250 + - "samsung,exynos5420-wdt" for Exynos5420 + - "samsung,exynos7-wdt" for Exynos7 - reg : base physical address of the controller and length of memory mapped region. diff --git a/Documentation/devicetree/bindings/watchdog/zte,zx2967-wdt.txt b/Documentation/devicetree/bindings/watchdog/zte,zx2967-wdt.txt new file mode 100644 index 000000000000..06ce67766756 --- /dev/null +++ b/Documentation/devicetree/bindings/watchdog/zte,zx2967-wdt.txt @@ -0,0 +1,32 @@ +ZTE zx2967 Watchdog timer + +Required properties: + +- compatible : should be one of the following. + * zte,zx296718-wdt +- reg : Specifies base physical address and size of the registers. +- clocks : Pairs of phandle and specifier referencing the controller's clocks. +- resets : Reference to the reset controller controlling the watchdog + controller. + +Optional properties: + +- timeout-sec : Contains the watchdog timeout in seconds. +- zte,wdt-reset-sysctrl : Directs how to reset system by the watchdog. + if we don't want to restart system when watchdog been triggered, + it's not required, vice versa. + It should include following fields. + * phandle of aon-sysctrl. + * offset of register that be written, should be 0xb0. + * configure value that be written to aon-sysctrl. + * bit mask, corresponding bits will be affected. + +Example: + +wdt: watchdog@1465000 { + compatible = "zte,zx296718-wdt"; + reg = <0x1465000 0x1000>; + clocks = <&topcrm WDT_WCLK>; + resets = <&toprst 35>; + zte,wdt-reset-sysctrl = <&aon_sysctrl 0xb0 1 0x115>; +}; diff --git a/Documentation/watchdog/watchdog-kernel-api.txt b/Documentation/watchdog/watchdog-kernel-api.txt index ea277478982f..9b93953f69cf 100644 --- a/Documentation/watchdog/watchdog-kernel-api.txt +++ b/Documentation/watchdog/watchdog-kernel-api.txt @@ -280,6 +280,12 @@ To disable the watchdog on reboot, the user must call the following helper: static inline void watchdog_stop_on_reboot(struct watchdog_device *wdd); +To disable the watchdog when unregistering the watchdog, the user must call +the following helper. Note that this will only stop the watchdog if the +nowayout flag is not set. + +static inline void watchdog_stop_on_unregister(struct watchdog_device *wdd); + To change the priority of the restart handler the following helper should be used: diff --git a/Documentation/watchdog/watchdog-parameters.txt b/Documentation/watchdog/watchdog-parameters.txt index e21850e270a0..4f7d86dd0a5d 100644 --- a/Documentation/watchdog/watchdog-parameters.txt +++ b/Documentation/watchdog/watchdog-parameters.txt @@ -209,6 +209,11 @@ timeout: Initial watchdog timeout in seconds (0 #include #include -#include #include #include @@ -59,7 +58,6 @@ struct asm9260_wdt_priv { struct clk *clk; struct clk *clk_ahb; struct reset_control *rst; - struct notifier_block restart_handler; void __iomem *iobase; int irq; @@ -172,15 +170,14 @@ static irqreturn_t asm9260_wdt_irq(int irq, void *devid) return IRQ_HANDLED; } -static int asm9260_restart_handler(struct notifier_block *this, - unsigned long mode, void *cmd) +static int asm9260_restart(struct watchdog_device *wdd, unsigned long action, + void *data) { - struct asm9260_wdt_priv *priv = - container_of(this, struct asm9260_wdt_priv, restart_handler); + struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd); asm9260_wdt_sys_reset(priv); - return NOTIFY_DONE; + return 0; } static const struct watchdog_info asm9260_wdt_ident = { @@ -189,13 +186,14 @@ static const struct watchdog_info asm9260_wdt_ident = { .identity = "Alphascale asm9260 Watchdog", }; -static struct watchdog_ops asm9260_wdt_ops = { +static const struct watchdog_ops asm9260_wdt_ops = { .owner = THIS_MODULE, .start = asm9260_wdt_enable, .stop = asm9260_wdt_disable, .get_timeleft = asm9260_wdt_gettimeleft, .ping = asm9260_wdt_feed, .set_timeout = asm9260_wdt_settimeout, + .restart = asm9260_restart, }; static int asm9260_wdt_get_dt_clks(struct asm9260_wdt_priv *priv) @@ -335,18 +333,14 @@ static int asm9260_wdt_probe(struct platform_device *pdev) dev_warn(&pdev->dev, "failed to request IRQ\n"); } + watchdog_set_restart_priority(wdd, 128); + ret = watchdog_register_device(wdd); if (ret) goto clk_off; platform_set_drvdata(pdev, priv); - priv->restart_handler.notifier_call = asm9260_restart_handler; - priv->restart_handler.priority = 128; - ret = register_restart_handler(&priv->restart_handler); - if (ret) - dev_warn(&pdev->dev, "cannot register restart handler\n"); - dev_info(&pdev->dev, "Watchdog enabled (timeout: %d sec, mode: %s)\n", wdd->timeout, mode_name[priv->mode]); return 0; @@ -370,8 +364,6 @@ static int asm9260_wdt_remove(struct platform_device *pdev) asm9260_wdt_disable(&priv->wdd); - unregister_restart_handler(&priv->restart_handler); - watchdog_unregister_device(&priv->wdd); clk_disable_unprepare(priv->clk); diff --git a/drivers/watchdog/aspeed_wdt.c b/drivers/watchdog/aspeed_wdt.c index f5ad8023c2e6..1c652582de40 100644 --- a/drivers/watchdog/aspeed_wdt.c +++ b/drivers/watchdog/aspeed_wdt.c @@ -136,15 +136,6 @@ static const struct watchdog_info aspeed_wdt_info = { .identity = KBUILD_MODNAME, }; -static int aspeed_wdt_remove(struct platform_device *pdev) -{ - struct aspeed_wdt *wdt = platform_get_drvdata(pdev); - - watchdog_unregister_device(&wdt->wdd); - - return 0; -} - static int aspeed_wdt_probe(struct platform_device *pdev) { struct aspeed_wdt *wdt; @@ -187,20 +178,17 @@ static int aspeed_wdt_probe(struct platform_device *pdev) set_bit(WDOG_HW_RUNNING, &wdt->wdd.status); } - ret = watchdog_register_device(&wdt->wdd); + ret = devm_watchdog_register_device(&pdev->dev, &wdt->wdd); if (ret) { dev_err(&pdev->dev, "failed to register\n"); return ret; } - platform_set_drvdata(pdev, wdt); - return 0; } static struct platform_driver aspeed_watchdog_driver = { .probe = aspeed_wdt_probe, - .remove = aspeed_wdt_remove, .driver = { .name = KBUILD_MODNAME, .of_match_table = of_match_ptr(aspeed_wdt_of_table), diff --git a/drivers/watchdog/atlas7_wdt.c b/drivers/watchdog/atlas7_wdt.c index ed80734befae..4abdcabd8219 100644 --- a/drivers/watchdog/atlas7_wdt.c +++ b/drivers/watchdog/atlas7_wdt.c @@ -105,7 +105,7 @@ static const struct watchdog_info atlas7_wdt_ident = { .identity = "atlas7 Watchdog", }; -static struct watchdog_ops atlas7_wdt_ops = { +static const struct watchdog_ops atlas7_wdt_ops = { .owner = THIS_MODULE, .start = atlas7_wdt_enable, .stop = atlas7_wdt_disable, diff --git a/drivers/watchdog/bcm2835_wdt.c b/drivers/watchdog/bcm2835_wdt.c index c32c45bd8b09..496f6c106bb1 100644 --- a/drivers/watchdog/bcm2835_wdt.c +++ b/drivers/watchdog/bcm2835_wdt.c @@ -14,7 +14,6 @@ */ #include -#include #include #include #include @@ -49,7 +48,6 @@ struct bcm2835_wdt { void __iomem *base; spinlock_t lock; - struct notifier_block restart_handler; }; static unsigned int heartbeat; @@ -99,11 +97,37 @@ static unsigned int bcm2835_wdt_get_timeleft(struct watchdog_device *wdog) return WDOG_TICKS_TO_SECS(ret & PM_WDOG_TIME_SET); } +static void __bcm2835_restart(struct bcm2835_wdt *wdt) +{ + 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); +} + +static int bcm2835_restart(struct watchdog_device *wdog, + unsigned long action, void *data) +{ + struct bcm2835_wdt *wdt = watchdog_get_drvdata(wdog); + + __bcm2835_restart(wdt); + + return 0; +} + static const struct watchdog_ops bcm2835_wdt_ops = { .owner = THIS_MODULE, .start = bcm2835_wdt_start, .stop = bcm2835_wdt_stop, .get_timeleft = bcm2835_wdt_get_timeleft, + .restart = bcm2835_restart, }; static const struct watchdog_info bcm2835_wdt_info = { @@ -120,26 +144,6 @@ 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 @@ -163,13 +167,13 @@ static void bcm2835_power_off(void) writel_relaxed(val, wdt->base + PM_RSTS); /* Continue with normal reset mechanism */ - bcm2835_restart(&wdt->restart_handler, REBOOT_HARD, NULL); + __bcm2835_restart(wdt); } static int bcm2835_wdt_probe(struct platform_device *pdev) { + struct resource *res; struct device *dev = &pdev->dev; - struct device_node *np = dev->of_node; struct bcm2835_wdt *wdt; int err; @@ -180,16 +184,15 @@ static int bcm2835_wdt_probe(struct platform_device *pdev) spin_lock_init(&wdt->lock); - wdt->base = of_iomap(np, 0); - if (!wdt->base) { - dev_err(dev, "Failed to remap watchdog regs"); - return -ENODEV; - } + 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); 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; + bcm2835_wdt_wdd.parent = dev; if (bcm2835_wdt_is_running(wdt)) { /* * The currently active timeout value (set by the @@ -201,16 +204,16 @@ static int bcm2835_wdt_probe(struct platform_device *pdev) */ set_bit(WDOG_HW_RUNNING, &bcm2835_wdt_wdd.status); } - err = watchdog_register_device(&bcm2835_wdt_wdd); + + watchdog_set_restart_priority(&bcm2835_wdt_wdd, 128); + + watchdog_stop_on_reboot(&bcm2835_wdt_wdd); + err = devm_watchdog_register_device(dev, &bcm2835_wdt_wdd); if (err) { dev_err(dev, "Failed to register watchdog device"); - iounmap(wdt->base); 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; @@ -220,22 +223,12 @@ static int bcm2835_wdt_probe(struct platform_device *pdev) 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); return 0; } -static void bcm2835_wdt_shutdown(struct platform_device *pdev) -{ - bcm2835_wdt_stop(&bcm2835_wdt_wdd); -} - static const struct of_device_id bcm2835_wdt_of_match[] = { { .compatible = "brcm,bcm2835-pm-wdt", }, {}, @@ -245,7 +238,6 @@ MODULE_DEVICE_TABLE(of, bcm2835_wdt_of_match); static struct platform_driver bcm2835_wdt_driver = { .probe = bcm2835_wdt_probe, .remove = bcm2835_wdt_remove, - .shutdown = bcm2835_wdt_shutdown, .driver = { .name = "bcm2835-wdt", .of_match_table = bcm2835_wdt_of_match, diff --git a/drivers/watchdog/bcm47xx_wdt.c b/drivers/watchdog/bcm47xx_wdt.c index a1900b9ab6c4..35725e21b18a 100644 --- a/drivers/watchdog/bcm47xx_wdt.c +++ b/drivers/watchdog/bcm47xx_wdt.c @@ -226,9 +226,6 @@ static int bcm47xx_wdt_remove(struct platform_device *pdev) { struct bcm47xx_wdt *wdt = dev_get_platdata(&pdev->dev); - if (!wdt) - return -ENXIO; - watchdog_unregister_device(&wdt->wdd); return 0; diff --git a/drivers/watchdog/bcm7038_wdt.c b/drivers/watchdog/bcm7038_wdt.c index 4814c00b32f6..c1b8e534fb55 100644 --- a/drivers/watchdog/bcm7038_wdt.c +++ b/drivers/watchdog/bcm7038_wdt.c @@ -101,7 +101,7 @@ static unsigned int bcm7038_wdt_get_timeleft(struct watchdog_device *wdog) return time_left / wdt->rate; } -static struct watchdog_info bcm7038_wdt_info = { +static const struct watchdog_info bcm7038_wdt_info = { .identity = "Broadcom BCM7038 Watchdog Timer", .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE diff --git a/drivers/watchdog/bcm_kona_wdt.c b/drivers/watchdog/bcm_kona_wdt.c index e0c98423f2c9..6fce17d5b9f1 100644 --- a/drivers/watchdog/bcm_kona_wdt.c +++ b/drivers/watchdog/bcm_kona_wdt.c @@ -266,7 +266,7 @@ static int bcm_kona_wdt_stop(struct watchdog_device *wdog) SECWDOG_SRSTEN_MASK, 0); } -static struct watchdog_ops bcm_kona_wdt_ops = { +static const struct watchdog_ops bcm_kona_wdt_ops = { .owner = THIS_MODULE, .start = bcm_kona_wdt_start, .stop = bcm_kona_wdt_stop, @@ -274,7 +274,7 @@ static struct watchdog_ops bcm_kona_wdt_ops = { .get_timeleft = bcm_kona_wdt_get_timeleft, }; -static struct watchdog_info bcm_kona_wdt_info = { +static const struct watchdog_info bcm_kona_wdt_info = { .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING, .identity = "Broadcom Kona Watchdog Timer", diff --git a/drivers/watchdog/booke_wdt.c b/drivers/watchdog/booke_wdt.c index 04da4b66c75e..3ad1e44bef44 100644 --- a/drivers/watchdog/booke_wdt.c +++ b/drivers/watchdog/booke_wdt.c @@ -192,12 +192,12 @@ static int booke_wdt_set_timeout(struct watchdog_device *wdt_dev, return 0; } -static struct watchdog_info booke_wdt_info = { +static struct watchdog_info booke_wdt_info __ro_after_init = { .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, .identity = "PowerPC Book-E Watchdog", }; -static struct watchdog_ops booke_wdt_ops = { +static const struct watchdog_ops booke_wdt_ops = { .owner = THIS_MODULE, .start = booke_wdt_start, .stop = booke_wdt_stop, diff --git a/drivers/watchdog/cadence_wdt.c b/drivers/watchdog/cadence_wdt.c index 98acef72334d..8d61e8bfe60b 100644 --- a/drivers/watchdog/cadence_wdt.c +++ b/drivers/watchdog/cadence_wdt.c @@ -262,7 +262,7 @@ static irqreturn_t cdns_wdt_irq_handler(int irq, void *dev_id) * Info structure used to indicate the features supported by the device * to the upper layers. This is defined in watchdog.h header file. */ -static struct watchdog_info cdns_wdt_info = { +static const struct watchdog_info cdns_wdt_info = { .identity = "cdns_wdt watchdog", .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, diff --git a/drivers/watchdog/coh901327_wdt.c b/drivers/watchdog/coh901327_wdt.c index a099b77fc0b9..38dd60f0cfcc 100644 --- a/drivers/watchdog/coh901327_wdt.c +++ b/drivers/watchdog/coh901327_wdt.c @@ -68,17 +68,10 @@ /* Default timeout in seconds = 1 minute */ static unsigned int margin = 60; -static resource_size_t phybase; -static resource_size_t physize; static int irq; static void __iomem *virtbase; static struct device *parent; -/* - * The watchdog block is of course always clocked, the - * clk_enable()/clk_disable() calls are mainly for performing reference - * counting higher up in the clock hierarchy. - */ static struct clk *clk; /* @@ -90,7 +83,6 @@ static void coh901327_enable(u16 timeout) unsigned long freq; unsigned long delay_ns; - clk_enable(clk); /* Restart timer if it is disabled */ val = readw(virtbase + U300_WDOG_D2R); if (val == U300_WDOG_D2R_DISABLE_STATUS_DISABLED) @@ -118,7 +110,6 @@ static void coh901327_enable(u16 timeout) */ (void) readw(virtbase + U300_WDOG_CR); val = readw(virtbase + U300_WDOG_D2R); - clk_disable(clk); if (val != U300_WDOG_D2R_DISABLE_STATUS_ENABLED) dev_err(parent, "%s(): watchdog not enabled! D2R value %04x\n", @@ -129,7 +120,6 @@ static void coh901327_disable(void) { u16 val; - clk_enable(clk); /* Disable the watchdog interrupt if it is active */ writew(0x0000U, virtbase + U300_WDOG_IMR); /* If the watchdog is currently enabled, attempt to disable it */ @@ -144,7 +134,6 @@ static void coh901327_disable(void) virtbase + U300_WDOG_D2R); } val = readw(virtbase + U300_WDOG_D2R); - clk_disable(clk); if (val != U300_WDOG_D2R_DISABLE_STATUS_DISABLED) dev_err(parent, "%s(): watchdog not disabled! D2R value %04x\n", @@ -165,11 +154,9 @@ static int coh901327_stop(struct watchdog_device *wdt_dev) static int coh901327_ping(struct watchdog_device *wdd) { - clk_enable(clk); /* Feed the watchdog */ writew(U300_WDOG_FR_FEED_RESTART_TIMER, virtbase + U300_WDOG_FR); - clk_disable(clk); return 0; } @@ -177,13 +164,11 @@ static int coh901327_settimeout(struct watchdog_device *wdt_dev, unsigned int time) { wdt_dev->timeout = time; - clk_enable(clk); /* Set new timeout value */ writew(time * 100, virtbase + U300_WDOG_TR); /* Feed the dog */ writew(U300_WDOG_FR_FEED_RESTART_TIMER, virtbase + U300_WDOG_FR); - clk_disable(clk); return 0; } @@ -191,13 +176,11 @@ static unsigned int coh901327_gettimeleft(struct watchdog_device *wdt_dev) { u16 val; - clk_enable(clk); /* Read repeatedly until the value is stable! */ val = readw(virtbase + U300_WDOG_CR); while (val & U300_WDOG_CR_VALID_IND) val = readw(virtbase + U300_WDOG_CR); val &= U300_WDOG_CR_COUNT_VALUE_MASK; - clk_disable(clk); if (val != 0) val /= 100; @@ -221,13 +204,11 @@ static irqreturn_t coh901327_interrupt(int irq, void *data) * to prevent a watchdog reset by feeding the watchdog at this * point. */ - clk_enable(clk); val = readw(virtbase + U300_WDOG_IER); if (val == U300_WDOG_IER_WILL_BARK_IRQ_EVENT_IND) writew(U300_WDOG_IER_WILL_BARK_IRQ_ACK_ENABLE, virtbase + U300_WDOG_IER); writew(0x0000U, virtbase + U300_WDOG_IMR); - clk_disable(clk); dev_crit(parent, "watchdog is barking!\n"); return IRQ_HANDLED; } @@ -263,81 +244,63 @@ static int __exit coh901327_remove(struct platform_device *pdev) watchdog_unregister_device(&coh901327_wdt); coh901327_disable(); free_irq(irq, pdev); - clk_unprepare(clk); + clk_disable_unprepare(clk); clk_put(clk); - iounmap(virtbase); - release_mem_region(phybase, physize); return 0; } static int __init coh901327_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; int ret; u16 val; struct resource *res; + parent = dev; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -ENOENT; + virtbase = devm_ioremap_resource(dev, res); + if (IS_ERR(virtbase)) + return PTR_ERR(virtbase); - parent = &pdev->dev; - physize = resource_size(res); - phybase = res->start; - - if (request_mem_region(phybase, physize, DRV_NAME) == NULL) { - ret = -EBUSY; - goto out; - } - - virtbase = ioremap(phybase, physize); - if (!virtbase) { - ret = -ENOMEM; - goto out_no_remap; - } - - clk = clk_get(&pdev->dev, NULL); + clk = clk_get(dev, NULL); if (IS_ERR(clk)) { ret = PTR_ERR(clk); - dev_err(&pdev->dev, "could not get clock\n"); - goto out_no_clk; + dev_err(dev, "could not get clock\n"); + return ret; } ret = clk_prepare_enable(clk); if (ret) { - dev_err(&pdev->dev, "could not prepare and enable clock\n"); + dev_err(dev, "could not prepare and enable clock\n"); goto out_no_clk_enable; } val = readw(virtbase + U300_WDOG_SR); switch (val) { case U300_WDOG_SR_STATUS_TIMED_OUT: - dev_info(&pdev->dev, - "watchdog timed out since last chip reset!\n"); + dev_info(dev, "watchdog timed out since last chip reset!\n"); coh901327_wdt.bootstatus |= WDIOF_CARDRESET; /* Status will be cleared below */ break; case U300_WDOG_SR_STATUS_NORMAL: - dev_info(&pdev->dev, - "in normal status, no timeouts have occurred.\n"); + dev_info(dev, "in normal status, no timeouts have occurred.\n"); break; default: - dev_info(&pdev->dev, - "contains an illegal status code (%08x)\n", val); + dev_info(dev, "contains an illegal status code (%08x)\n", val); break; } val = readw(virtbase + U300_WDOG_D2R); switch (val) { case U300_WDOG_D2R_DISABLE_STATUS_DISABLED: - dev_info(&pdev->dev, "currently disabled.\n"); + dev_info(dev, "currently disabled.\n"); break; case U300_WDOG_D2R_DISABLE_STATUS_ENABLED: - dev_info(&pdev->dev, - "currently enabled! (disabling it now)\n"); + dev_info(dev, "currently enabled! (disabling it now)\n"); coh901327_disable(); break; default: - dev_err(&pdev->dev, - "contains an illegal enable/disable code (%08x)\n", + dev_err(dev, "contains an illegal enable/disable code (%08x)\n", val); break; } @@ -352,20 +315,16 @@ static int __init coh901327_probe(struct platform_device *pdev) goto out_no_irq; } - clk_disable(clk); - - ret = watchdog_init_timeout(&coh901327_wdt, margin, &pdev->dev); + ret = watchdog_init_timeout(&coh901327_wdt, margin, dev); if (ret < 0) coh901327_wdt.timeout = 60; - coh901327_wdt.parent = &pdev->dev; + coh901327_wdt.parent = dev; ret = watchdog_register_device(&coh901327_wdt); - if (ret == 0) - dev_info(&pdev->dev, - "initialized. timer margin=%d sec\n", margin); - else + if (ret) goto out_no_wdog; + dev_info(dev, "initialized. timer margin=%d sec\n", margin); return 0; out_no_wdog: @@ -374,11 +333,6 @@ out_no_irq: clk_disable_unprepare(clk); out_no_clk_enable: clk_put(clk); -out_no_clk: - iounmap(virtbase); -out_no_remap: - release_mem_region(phybase, SZ_4K); -out: return ret; } diff --git a/drivers/watchdog/da9052_wdt.c b/drivers/watchdog/da9052_wdt.c index 2fc19a32a320..d6d5006efa71 100644 --- a/drivers/watchdog/da9052_wdt.c +++ b/drivers/watchdog/da9052_wdt.c @@ -128,19 +128,17 @@ static int da9052_wdt_ping(struct watchdog_device *wdt_dev) ret = da9052_reg_update(da9052, DA9052_CONTROL_D_REG, DA9052_CONTROLD_WATCHDOG, 1 << 7); if (ret < 0) - goto err_strobe; + return ret; /* * FIXME: Reset the watchdog core, in general PMIC * is supposed to do this */ - ret = da9052_reg_update(da9052, DA9052_CONTROL_D_REG, - DA9052_CONTROLD_WATCHDOG, 0 << 7); -err_strobe: - return ret; + return da9052_reg_update(da9052, DA9052_CONTROL_D_REG, + DA9052_CONTROLD_WATCHDOG, 0 << 7); } -static struct watchdog_info da9052_wdt_info = { +static const struct watchdog_info da9052_wdt_info = { .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, .identity = "DA9052 Watchdog", }; @@ -163,10 +161,8 @@ static int da9052_wdt_probe(struct platform_device *pdev) driver_data = devm_kzalloc(&pdev->dev, sizeof(*driver_data), GFP_KERNEL); - if (!driver_data) { - ret = -ENOMEM; - goto err; - } + if (!driver_data) + return -ENOMEM; driver_data->da9052 = da9052; da9052_wdt = &driver_data->wdt; @@ -182,33 +178,21 @@ static int da9052_wdt_probe(struct platform_device *pdev) if (ret < 0) { dev_err(&pdev->dev, "Failed to disable watchdog bits, %d\n", ret); - goto err; + return ret; } - ret = watchdog_register_device(&driver_data->wdt); + ret = devm_watchdog_register_device(&pdev->dev, &driver_data->wdt); if (ret != 0) { dev_err(da9052->dev, "watchdog_register_device() failed: %d\n", ret); - goto err; + return ret; } - platform_set_drvdata(pdev, driver_data); -err: return ret; } -static int da9052_wdt_remove(struct platform_device *pdev) -{ - struct da9052_wdt_data *driver_data = platform_get_drvdata(pdev); - - watchdog_unregister_device(&driver_data->wdt); - - return 0; -} - static struct platform_driver da9052_wdt_driver = { .probe = da9052_wdt_probe, - .remove = da9052_wdt_remove, .driver = { .name = "da9052-watchdog", }, diff --git a/drivers/watchdog/da9055_wdt.c b/drivers/watchdog/da9055_wdt.c index 8377c43f3f20..50bdd1022186 100644 --- a/drivers/watchdog/da9055_wdt.c +++ b/drivers/watchdog/da9055_wdt.c @@ -108,7 +108,7 @@ static int da9055_wdt_stop(struct watchdog_device *wdt_dev) return da9055_wdt_set_timeout(wdt_dev, 0); } -static struct watchdog_info da9055_wdt_info = { +static const struct watchdog_info da9055_wdt_info = { .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, .identity = "DA9055 Watchdog", }; @@ -147,32 +147,19 @@ static int da9055_wdt_probe(struct platform_device *pdev) ret = da9055_wdt_stop(da9055_wdt); if (ret < 0) { dev_err(&pdev->dev, "Failed to stop watchdog, %d\n", ret); - goto err; + return ret; } - platform_set_drvdata(pdev, driver_data); - - ret = watchdog_register_device(&driver_data->wdt); + ret = devm_watchdog_register_device(&pdev->dev, &driver_data->wdt); if (ret != 0) dev_err(da9055->dev, "watchdog_register_device() failed: %d\n", ret); -err: return ret; } -static int da9055_wdt_remove(struct platform_device *pdev) -{ - struct da9055_wdt_data *driver_data = platform_get_drvdata(pdev); - - watchdog_unregister_device(&driver_data->wdt); - - return 0; -} - static struct platform_driver da9055_wdt_driver = { .probe = da9055_wdt_probe, - .remove = da9055_wdt_remove, .driver = { .name = "da9055-watchdog", }, diff --git a/drivers/watchdog/da9062_wdt.c b/drivers/watchdog/da9062_wdt.c index a02cee6820a1..9083d3d922b0 100644 --- a/drivers/watchdog/da9062_wdt.c +++ b/drivers/watchdog/da9062_wdt.c @@ -220,9 +220,8 @@ static int da9062_wdt_probe(struct platform_device *pdev) wdt->wdtdev.parent = &pdev->dev; watchdog_set_drvdata(&wdt->wdtdev, wdt); - dev_set_drvdata(&pdev->dev, wdt); - ret = watchdog_register_device(&wdt->wdtdev); + ret = devm_watchdog_register_device(&pdev->dev, &wdt->wdtdev); if (ret < 0) { dev_err(wdt->hw->dev, "watchdog registration failed (%d)\n", ret); @@ -231,24 +230,11 @@ static int da9062_wdt_probe(struct platform_device *pdev) 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; + return da9062_wdt_ping(&wdt->wdtdev); } static struct platform_driver da9062_wdt_driver = { .probe = da9062_wdt_probe, - .remove = da9062_wdt_remove, .driver = { .name = "da9062-watchdog", .of_match_table = da9062_compatible_id_table, diff --git a/drivers/watchdog/da9063_wdt.c b/drivers/watchdog/da9063_wdt.c index 5d6b4e5f7989..4691c5509129 100644 --- a/drivers/watchdog/da9063_wdt.c +++ b/drivers/watchdog/da9063_wdt.c @@ -151,7 +151,6 @@ static const struct watchdog_ops da9063_watchdog_ops = { static int da9063_wdt_probe(struct platform_device *pdev) { - int ret; struct da9063 *da9063; struct da9063_watchdog *wdt; @@ -181,27 +180,12 @@ static int da9063_wdt_probe(struct platform_device *pdev) watchdog_set_restart_priority(&wdt->wdtdev, 128); watchdog_set_drvdata(&wdt->wdtdev, wdt); - dev_set_drvdata(&pdev->dev, wdt); - ret = watchdog_register_device(&wdt->wdtdev); - if (ret) - return ret; - - return 0; -} - -static int da9063_wdt_remove(struct platform_device *pdev) -{ - struct da9063_watchdog *wdt = dev_get_drvdata(&pdev->dev); - - watchdog_unregister_device(&wdt->wdtdev); - - return 0; + return devm_watchdog_register_device(&pdev->dev, &wdt->wdtdev); } static struct platform_driver da9063_wdt_driver = { .probe = da9063_wdt_probe, - .remove = da9063_wdt_remove, .driver = { .name = DA9063_DRVNAME_WATCHDOG, }, diff --git a/drivers/watchdog/diag288_wdt.c b/drivers/watchdog/diag288_wdt.c index 861d3d3133f8..6f591084bb7a 100644 --- a/drivers/watchdog/diag288_wdt.c +++ b/drivers/watchdog/diag288_wdt.c @@ -205,7 +205,7 @@ static int wdt_set_timeout(struct watchdog_device * dev, unsigned int new_to) return wdt_ping(dev); } -static struct watchdog_ops wdt_ops = { +static const struct watchdog_ops wdt_ops = { .owner = THIS_MODULE, .start = wdt_start, .stop = wdt_stop, diff --git a/drivers/watchdog/digicolor_wdt.c b/drivers/watchdog/digicolor_wdt.c index 77df772406b0..5e4ef93caa02 100644 --- a/drivers/watchdog/digicolor_wdt.c +++ b/drivers/watchdog/digicolor_wdt.c @@ -96,7 +96,7 @@ static unsigned int dc_wdt_get_timeleft(struct watchdog_device *wdog) return count / clk_get_rate(wdt->clk); } -static struct watchdog_ops dc_wdt_ops = { +static const struct watchdog_ops dc_wdt_ops = { .owner = THIS_MODULE, .start = dc_wdt_start, .stop = dc_wdt_stop, @@ -105,7 +105,7 @@ static struct watchdog_ops dc_wdt_ops = { .restart = dc_wdt_restart, }; -static struct watchdog_info dc_wdt_info = { +static const struct watchdog_info dc_wdt_info = { .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING, .identity = "Conexant Digicolor Watchdog", @@ -119,62 +119,40 @@ static struct watchdog_device dc_wdt_wdd = { static int dc_wdt_probe(struct platform_device *pdev) { + struct resource *res; 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; - } + 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(&pdev->dev, NULL); - if (IS_ERR(wdt->clk)) { - ret = PTR_ERR(wdt->clk); - goto err_iounmap; - } + wdt->clk = devm_clk_get(dev, NULL); + if (IS_ERR(wdt->clk)) + return PTR_ERR(wdt->clk); 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; + dc_wdt_wdd.parent = dev; spin_lock_init(&wdt->lock); watchdog_set_drvdata(&dc_wdt_wdd, wdt); watchdog_set_restart_priority(&dc_wdt_wdd, 128); watchdog_init_timeout(&dc_wdt_wdd, timeout, dev); - ret = watchdog_register_device(&dc_wdt_wdd); + watchdog_stop_on_reboot(&dc_wdt_wdd); + ret = devm_watchdog_register_device(dev, &dc_wdt_wdd); if (ret) { dev_err(dev, "Failed to register watchdog device"); - goto err_iounmap; + return ret; } 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); - - 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[] = { @@ -185,8 +163,6 @@ 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, diff --git a/drivers/watchdog/dw_wdt.c b/drivers/watchdog/dw_wdt.c index 3c6a3de13a1b..914da3a4d334 100644 --- a/drivers/watchdog/dw_wdt.c +++ b/drivers/watchdog/dw_wdt.c @@ -26,11 +26,9 @@ #include #include #include -#include #include #include #include -#include #include #define WDOG_CONTROL_REG_OFFSET 0x00 @@ -55,7 +53,6 @@ struct dw_wdt { void __iomem *regs; struct clk *clk; unsigned long rate; - struct notifier_block restart_handler; struct watchdog_device wdd; }; @@ -136,14 +133,12 @@ static int dw_wdt_start(struct watchdog_device *wdd) return 0; } -static int dw_wdt_restart_handle(struct notifier_block *this, - unsigned long mode, void *cmd) +static int dw_wdt_restart(struct watchdog_device *wdd, + unsigned long action, void *data) { - struct dw_wdt *dw_wdt; + struct dw_wdt *dw_wdt = to_dw_wdt(wdd); u32 val; - dw_wdt = container_of(this, struct dw_wdt, restart_handler); - writel(0, dw_wdt->regs + WDOG_TIMEOUT_RANGE_REG_OFFSET); val = readl(dw_wdt->regs + WDOG_CONTROL_REG_OFFSET); if (val & WDOG_CONTROL_REG_WDT_EN_MASK) @@ -156,7 +151,7 @@ static int dw_wdt_restart_handle(struct notifier_block *this, /* wait for reset to assert... */ mdelay(500); - return NOTIFY_DONE; + return 0; } static unsigned int dw_wdt_get_timeleft(struct watchdog_device *wdd) @@ -179,6 +174,7 @@ static const struct watchdog_ops dw_wdt_ops = { .ping = dw_wdt_ping, .set_timeout = dw_wdt_set_timeout, .get_timeleft = dw_wdt_get_timeleft, + .restart = dw_wdt_restart, }; #ifdef CONFIG_PM_SLEEP @@ -265,16 +261,12 @@ static int dw_wdt_drv_probe(struct platform_device *pdev) platform_set_drvdata(pdev, dw_wdt); + watchdog_set_restart_priority(wdd, 128); + ret = watchdog_register_device(wdd); if (ret) goto out_disable_clk; - dw_wdt->restart_handler.notifier_call = dw_wdt_restart_handle; - dw_wdt->restart_handler.priority = 128; - ret = register_restart_handler(&dw_wdt->restart_handler); - if (ret) - pr_warn("cannot register restart handler\n"); - return 0; out_disable_clk: @@ -286,7 +278,6 @@ static int dw_wdt_drv_remove(struct platform_device *pdev) { struct dw_wdt *dw_wdt = platform_get_drvdata(pdev); - unregister_restart_handler(&dw_wdt->restart_handler); watchdog_unregister_device(&dw_wdt->wdd); clk_disable_unprepare(dw_wdt->clk); diff --git a/drivers/watchdog/ebc-c384_wdt.c b/drivers/watchdog/ebc-c384_wdt.c index 4b849b8e37c2..2170b275ea01 100644 --- a/drivers/watchdog/ebc-c384_wdt.c +++ b/drivers/watchdog/ebc-c384_wdt.c @@ -121,18 +121,7 @@ static int ebc_c384_wdt_probe(struct device *dev, unsigned int id) dev_warn(dev, "Invalid timeout (%u seconds), using default (%u seconds)\n", timeout, WATCHDOG_TIMEOUT); - dev_set_drvdata(dev, wdd); - - return watchdog_register_device(wdd); -} - -static int ebc_c384_wdt_remove(struct device *dev, unsigned int id) -{ - struct watchdog_device *wdd = dev_get_drvdata(dev); - - watchdog_unregister_device(wdd); - - return 0; + return devm_watchdog_register_device(dev, wdd); } static struct isa_driver ebc_c384_wdt_driver = { @@ -140,7 +129,6 @@ static struct isa_driver ebc_c384_wdt_driver = { .driver = { .name = MODULE_NAME }, - .remove = ebc_c384_wdt_remove }; static int __init ebc_c384_wdt_init(void) diff --git a/drivers/watchdog/ep93xx_wdt.c b/drivers/watchdog/ep93xx_wdt.c index 0a4d7cc05d54..f9b14e6efd9a 100644 --- a/drivers/watchdog/ep93xx_wdt.c +++ b/drivers/watchdog/ep93xx_wdt.c @@ -19,21 +19,13 @@ * for us to rely on the user space daemon alone. So we ping the * wdt each ~200msec and eventually stop doing it if the user space * daemon dies. - * - * TODO: - * - * - Test last reset from watchdog status - * - Add a few missing ioctls */ #include #include #include -#include #include -#define WDT_VERSION "0.4" - /* default timeout (secs) */ #define WDT_TIMEOUT 30 @@ -41,126 +33,109 @@ static bool nowayout = WATCHDOG_NOWAYOUT; module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started"); -static unsigned int timeout = WDT_TIMEOUT; +static unsigned int timeout; module_param(timeout, uint, 0); -MODULE_PARM_DESC(timeout, - "Watchdog timeout in seconds. (1<=timeout<=3600, default=" - __MODULE_STRING(WDT_TIMEOUT) ")"); - -static void __iomem *mmio_base; -static struct timer_list timer; -static unsigned long next_heartbeat; +MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds."); #define EP93XX_WATCHDOG 0x00 #define EP93XX_WDSTATUS 0x04 -/* reset the wdt every ~200ms - the heartbeat of the device is 0.250 seconds*/ -#define WDT_INTERVAL (HZ/5) - -static void ep93xx_wdt_timer_ping(unsigned long data) -{ - if (time_before(jiffies, next_heartbeat)) - writel(0x5555, mmio_base + EP93XX_WATCHDOG); - - /* Re-set the timer interval */ - mod_timer(&timer, jiffies + WDT_INTERVAL); -} +struct ep93xx_wdt_priv { + void __iomem *mmio; + struct watchdog_device wdd; +}; static int ep93xx_wdt_start(struct watchdog_device *wdd) { - next_heartbeat = jiffies + (timeout * HZ); + struct ep93xx_wdt_priv *priv = watchdog_get_drvdata(wdd); - writel(0xaaaa, mmio_base + EP93XX_WATCHDOG); - mod_timer(&timer, jiffies + WDT_INTERVAL); + writel(0xaaaa, priv->mmio + EP93XX_WATCHDOG); return 0; } static int ep93xx_wdt_stop(struct watchdog_device *wdd) { - del_timer_sync(&timer); - writel(0xaa55, mmio_base + EP93XX_WATCHDOG); + struct ep93xx_wdt_priv *priv = watchdog_get_drvdata(wdd); + + writel(0xaa55, priv->mmio + EP93XX_WATCHDOG); return 0; } -static int ep93xx_wdt_keepalive(struct watchdog_device *wdd) +static int ep93xx_wdt_ping(struct watchdog_device *wdd) { - /* user land ping */ - next_heartbeat = jiffies + (timeout * HZ); + struct ep93xx_wdt_priv *priv = watchdog_get_drvdata(wdd); + + writel(0x5555, priv->mmio + EP93XX_WATCHDOG); return 0; } static const struct watchdog_info ep93xx_wdt_ident = { .options = WDIOF_CARDRESET | + WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING, .identity = "EP93xx Watchdog", }; -static struct watchdog_ops ep93xx_wdt_ops = { +static const struct watchdog_ops ep93xx_wdt_ops = { .owner = THIS_MODULE, .start = ep93xx_wdt_start, .stop = ep93xx_wdt_stop, - .ping = ep93xx_wdt_keepalive, -}; - -static struct watchdog_device ep93xx_wdt_wdd = { - .info = &ep93xx_wdt_ident, - .ops = &ep93xx_wdt_ops, + .ping = ep93xx_wdt_ping, }; static int ep93xx_wdt_probe(struct platform_device *pdev) { + struct ep93xx_wdt_priv *priv; + struct watchdog_device *wdd; struct resource *res; unsigned long val; - int err; + int ret; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - mmio_base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(mmio_base)) - return PTR_ERR(mmio_base); + priv->mmio = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(priv->mmio)) + return PTR_ERR(priv->mmio); - if (timeout < 1 || timeout > 3600) { - timeout = WDT_TIMEOUT; - dev_warn(&pdev->dev, - "timeout value must be 1<=x<=3600, using %d\n", - timeout); - } + val = readl(priv->mmio + EP93XX_WATCHDOG); - 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; + wdd = &priv->wdd; + wdd->bootstatus = (val & 0x01) ? WDIOF_CARDRESET : 0; + wdd->info = &ep93xx_wdt_ident; + wdd->ops = &ep93xx_wdt_ops; + wdd->min_timeout = 1; + wdd->max_hw_heartbeat_ms = 200; + wdd->parent = &pdev->dev; - watchdog_set_nowayout(&ep93xx_wdt_wdd, nowayout); + watchdog_set_nowayout(wdd, nowayout); - setup_timer(&timer, ep93xx_wdt_timer_ping, 1); + wdd->timeout = WDT_TIMEOUT; + watchdog_init_timeout(wdd, timeout, &pdev->dev); - err = watchdog_register_device(&ep93xx_wdt_wdd); - if (err) - return err; + watchdog_set_drvdata(wdd, priv); - dev_info(&pdev->dev, - "EP93XX watchdog, driver version " WDT_VERSION "%s\n", + ret = devm_watchdog_register_device(&pdev->dev, wdd); + if (ret) + return ret; + + dev_info(&pdev->dev, "EP93XX watchdog driver %s\n", (val & 0x08) ? " (nCS1 disable detected)" : ""); return 0; } -static int ep93xx_wdt_remove(struct platform_device *pdev) -{ - watchdog_unregister_device(&ep93xx_wdt_wdd); - return 0; -} - static struct platform_driver ep93xx_wdt_driver = { .driver = { .name = "ep93xx-wdt", }, .probe = ep93xx_wdt_probe, - .remove = ep93xx_wdt_remove, }; module_platform_driver(ep93xx_wdt_driver); @@ -170,4 +145,3 @@ MODULE_AUTHOR("Alessandro Zummo "); MODULE_AUTHOR("H Hartley Sweeten "); MODULE_DESCRIPTION("EP93xx Watchdog"); MODULE_LICENSE("GPL"); -MODULE_VERSION(WDT_VERSION); diff --git a/drivers/watchdog/gemini_wdt.c b/drivers/watchdog/gemini_wdt.c new file mode 100644 index 000000000000..8155aa619e4c --- /dev/null +++ b/drivers/watchdog/gemini_wdt.c @@ -0,0 +1,229 @@ +/* + * Watchdog driver for Cortina Systems Gemini SoC + * + * Copyright (C) 2017 Linus Walleij + * + * Inspired by the out-of-tree drivers from OpenWRT: + * Copyright (C) 2009 Paulius Zaleckas + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define GEMINI_WDCOUNTER 0x0 +#define GEMINI_WDLOAD 0x4 +#define GEMINI_WDRESTART 0x8 +#define GEMINI_WDCR 0xC + +#define WDRESTART_MAGIC 0x5AB9 + +#define WDCR_CLOCK_5MHZ BIT(4) +#define WDCR_SYS_RST BIT(1) +#define WDCR_ENABLE BIT(0) + +#define WDT_CLOCK 5000000 /* 5 MHz */ + +struct gemini_wdt { + struct watchdog_device wdd; + struct device *dev; + void __iomem *base; +}; + +static inline +struct gemini_wdt *to_gemini_wdt(struct watchdog_device *wdd) +{ + return container_of(wdd, struct gemini_wdt, wdd); +} + +static int gemini_wdt_start(struct watchdog_device *wdd) +{ + struct gemini_wdt *gwdt = to_gemini_wdt(wdd); + + writel(wdd->timeout * WDT_CLOCK, gwdt->base + GEMINI_WDLOAD); + writel(WDRESTART_MAGIC, gwdt->base + GEMINI_WDRESTART); + /* set clock before enabling */ + writel(WDCR_CLOCK_5MHZ | WDCR_SYS_RST, + gwdt->base + GEMINI_WDCR); + writel(WDCR_CLOCK_5MHZ | WDCR_SYS_RST | WDCR_ENABLE, + gwdt->base + GEMINI_WDCR); + + return 0; +} + +static int gemini_wdt_stop(struct watchdog_device *wdd) +{ + struct gemini_wdt *gwdt = to_gemini_wdt(wdd); + + writel(0, gwdt->base + GEMINI_WDCR); + + return 0; +} + +static int gemini_wdt_ping(struct watchdog_device *wdd) +{ + struct gemini_wdt *gwdt = to_gemini_wdt(wdd); + + writel(WDRESTART_MAGIC, gwdt->base + GEMINI_WDRESTART); + + return 0; +} + +static int gemini_wdt_set_timeout(struct watchdog_device *wdd, + unsigned int timeout) +{ + wdd->timeout = timeout; + if (watchdog_active(wdd)) + gemini_wdt_start(wdd); + + return 0; +} + +static irqreturn_t gemini_wdt_interrupt(int irq, void *data) +{ + struct gemini_wdt *gwdt = data; + + watchdog_notify_pretimeout(&gwdt->wdd); + + return IRQ_HANDLED; +} + +static const struct watchdog_ops gemini_wdt_ops = { + .start = gemini_wdt_start, + .stop = gemini_wdt_stop, + .ping = gemini_wdt_ping, + .set_timeout = gemini_wdt_set_timeout, + .owner = THIS_MODULE, +}; + +static const struct watchdog_info gemini_wdt_info = { + .options = WDIOF_KEEPALIVEPING + | WDIOF_MAGICCLOSE + | WDIOF_SETTIMEOUT, + .identity = KBUILD_MODNAME, +}; + + +static int gemini_wdt_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct resource *res; + struct gemini_wdt *gwdt; + unsigned int reg; + int irq; + int ret; + + gwdt = devm_kzalloc(dev, sizeof(*gwdt), GFP_KERNEL); + if (!gwdt) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + gwdt->base = devm_ioremap_resource(dev, res); + if (IS_ERR(gwdt->base)) + return PTR_ERR(gwdt->base); + + irq = platform_get_irq(pdev, 0); + if (!irq) + return -EINVAL; + + gwdt->dev = dev; + gwdt->wdd.info = &gemini_wdt_info; + gwdt->wdd.ops = &gemini_wdt_ops; + gwdt->wdd.min_timeout = 1; + gwdt->wdd.max_timeout = 0xFFFFFFFF / WDT_CLOCK; + gwdt->wdd.parent = dev; + + /* + * If 'timeout-sec' unspecified in devicetree, assume a 13 second + * default. + */ + gwdt->wdd.timeout = 13U; + watchdog_init_timeout(&gwdt->wdd, 0, dev); + + reg = readw(gwdt->base + GEMINI_WDCR); + if (reg & WDCR_ENABLE) { + /* Watchdog was enabled by the bootloader, disable it. */ + reg &= ~WDCR_ENABLE; + writel(reg, gwdt->base + GEMINI_WDCR); + } + + ret = devm_request_irq(dev, irq, gemini_wdt_interrupt, 0, + "watchdog bark", gwdt); + if (ret) + return ret; + + ret = devm_watchdog_register_device(dev, &gwdt->wdd); + if (ret) { + dev_err(&pdev->dev, "failed to register watchdog\n"); + return ret; + } + + /* Set up platform driver data */ + platform_set_drvdata(pdev, gwdt); + dev_info(dev, "Gemini watchdog driver enabled\n"); + + return 0; +} + +static int __maybe_unused gemini_wdt_suspend(struct device *dev) +{ + struct gemini_wdt *gwdt = dev_get_drvdata(dev); + unsigned int reg; + + reg = readw(gwdt->base + GEMINI_WDCR); + reg &= ~WDCR_ENABLE; + writel(reg, gwdt->base + GEMINI_WDCR); + + return 0; +} + +static int __maybe_unused gemini_wdt_resume(struct device *dev) +{ + struct gemini_wdt *gwdt = dev_get_drvdata(dev); + unsigned int reg; + + if (watchdog_active(&gwdt->wdd)) { + reg = readw(gwdt->base + GEMINI_WDCR); + reg |= WDCR_ENABLE; + writel(reg, gwdt->base + GEMINI_WDCR); + } + + return 0; +} + +static const struct dev_pm_ops gemini_wdt_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(gemini_wdt_suspend, + gemini_wdt_resume) +}; + +#ifdef CONFIG_OF +static const struct of_device_id gemini_wdt_match[] = { + { .compatible = "cortina,gemini-watchdog" }, + {}, +}; +MODULE_DEVICE_TABLE(of, gemini_wdt_match); +#endif + +static struct platform_driver gemini_wdt_driver = { + .probe = gemini_wdt_probe, + .driver = { + .name = "gemini-wdt", + .of_match_table = of_match_ptr(gemini_wdt_match), + .pm = &gemini_wdt_dev_pm_ops, + }, +}; +module_platform_driver(gemini_wdt_driver); +MODULE_AUTHOR("Linus Walleij"); +MODULE_DESCRIPTION("Watchdog driver for Gemini"); +MODULE_LICENSE("GPL"); diff --git a/drivers/watchdog/iTCO_wdt.c b/drivers/watchdog/iTCO_wdt.c index 06fcb6c8c917..3d0abc0d59b4 100644 --- a/drivers/watchdog/iTCO_wdt.c +++ b/drivers/watchdog/iTCO_wdt.c @@ -72,22 +72,24 @@ /* Address definitions for the TCO */ /* TCO base address */ -#define TCOBASE (iTCO_wdt_private.tco_res->start) +#define TCOBASE(p) ((p)->tco_res->start) /* SMI Control and Enable Register */ -#define SMI_EN (iTCO_wdt_private.smi_res->start) +#define SMI_EN(p) ((p)->smi_res->start) -#define TCO_RLD (TCOBASE + 0x00) /* TCO Timer Reload and Curr. Value */ -#define TCOv1_TMR (TCOBASE + 0x01) /* TCOv1 Timer Initial Value */ -#define TCO_DAT_IN (TCOBASE + 0x02) /* TCO Data In Register */ -#define TCO_DAT_OUT (TCOBASE + 0x03) /* TCO Data Out Register */ -#define TCO1_STS (TCOBASE + 0x04) /* TCO1 Status Register */ -#define TCO2_STS (TCOBASE + 0x06) /* TCO2 Status Register */ -#define TCO1_CNT (TCOBASE + 0x08) /* TCO1 Control Register */ -#define TCO2_CNT (TCOBASE + 0x0a) /* TCO2 Control Register */ -#define TCOv2_TMR (TCOBASE + 0x12) /* TCOv2 Timer Initial Value */ +#define TCO_RLD(p) (TCOBASE(p) + 0x00) /* TCO Timer Reload/Curr. Value */ +#define TCOv1_TMR(p) (TCOBASE(p) + 0x01) /* TCOv1 Timer Initial Value*/ +#define TCO_DAT_IN(p) (TCOBASE(p) + 0x02) /* TCO Data In Register */ +#define TCO_DAT_OUT(p) (TCOBASE(p) + 0x03) /* TCO Data Out Register */ +#define TCO1_STS(p) (TCOBASE(p) + 0x04) /* TCO1 Status Register */ +#define TCO2_STS(p) (TCOBASE(p) + 0x06) /* TCO2 Status Register */ +#define TCO1_CNT(p) (TCOBASE(p) + 0x08) /* TCO1 Control Register */ +#define TCO2_CNT(p) (TCOBASE(p) + 0x0a) /* TCO2 Control Register */ +#define TCOv2_TMR(p) (TCOBASE(p) + 0x12) /* TCOv2 Timer Initial Value*/ /* internal variables */ -static struct { /* this is private data for the iTCO_wdt device */ +struct iTCO_wdt_private { + struct watchdog_device wddev; + /* TCO version/generation */ unsigned int iTCO_version; struct resource *tco_res; @@ -100,12 +102,11 @@ static struct { /* this is private data for the iTCO_wdt device */ unsigned long __iomem *gcs_pmc; /* the lock for io operations */ spinlock_t io_lock; - struct platform_device *dev; /* the PCI-device */ - struct pci_dev *pdev; + struct pci_dev *pci_dev; /* whether or not the watchdog has been suspended */ bool suspended; -} iTCO_wdt_private; +}; /* module parameters */ #define WATCHDOG_TIMEOUT 30 /* 30 sec default heartbeat */ @@ -135,21 +136,23 @@ MODULE_PARM_DESC(turn_SMI_watchdog_clear_off, * every 0.6 seconds. v3's internal timer is stored as seconds (some * datasheets incorrectly state 0.6 seconds). */ -static inline unsigned int seconds_to_ticks(int secs) +static inline unsigned int seconds_to_ticks(struct iTCO_wdt_private *p, + int secs) { - return iTCO_wdt_private.iTCO_version == 3 ? secs : (secs * 10) / 6; + return p->iTCO_version == 3 ? secs : (secs * 10) / 6; } -static inline unsigned int ticks_to_seconds(int ticks) +static inline unsigned int ticks_to_seconds(struct iTCO_wdt_private *p, + int ticks) { - return iTCO_wdt_private.iTCO_version == 3 ? ticks : (ticks * 6) / 10; + return p->iTCO_version == 3 ? ticks : (ticks * 6) / 10; } -static inline u32 no_reboot_bit(void) +static inline u32 no_reboot_bit(struct iTCO_wdt_private *p) { u32 enable_bit; - switch (iTCO_wdt_private.iTCO_version) { + switch (p->iTCO_version) { case 5: case 3: enable_bit = 0x00000010; @@ -167,40 +170,40 @@ static inline u32 no_reboot_bit(void) return enable_bit; } -static void iTCO_wdt_set_NO_REBOOT_bit(void) +static void iTCO_wdt_set_NO_REBOOT_bit(struct iTCO_wdt_private *p) { u32 val32; /* Set the NO_REBOOT bit: this disables reboots */ - if (iTCO_wdt_private.iTCO_version >= 2) { - val32 = readl(iTCO_wdt_private.gcs_pmc); - 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 |= no_reboot_bit(); - pci_write_config_dword(iTCO_wdt_private.pdev, 0xd4, val32); + if (p->iTCO_version >= 2) { + val32 = readl(p->gcs_pmc); + val32 |= no_reboot_bit(p); + writel(val32, p->gcs_pmc); + } else if (p->iTCO_version == 1) { + pci_read_config_dword(p->pci_dev, 0xd4, &val32); + val32 |= no_reboot_bit(p); + pci_write_config_dword(p->pci_dev, 0xd4, val32); } } -static int iTCO_wdt_unset_NO_REBOOT_bit(void) +static int iTCO_wdt_unset_NO_REBOOT_bit(struct iTCO_wdt_private *p) { - u32 enable_bit = no_reboot_bit(); + u32 enable_bit = no_reboot_bit(p); u32 val32 = 0; /* Unset the NO_REBOOT bit: this enables reboots */ - if (iTCO_wdt_private.iTCO_version >= 2) { - val32 = readl(iTCO_wdt_private.gcs_pmc); + if (p->iTCO_version >= 2) { + val32 = readl(p->gcs_pmc); val32 &= ~enable_bit; - writel(val32, iTCO_wdt_private.gcs_pmc); + writel(val32, p->gcs_pmc); - val32 = readl(iTCO_wdt_private.gcs_pmc); - } else if (iTCO_wdt_private.iTCO_version == 1) { - pci_read_config_dword(iTCO_wdt_private.pdev, 0xd4, &val32); + val32 = readl(p->gcs_pmc); + } else if (p->iTCO_version == 1) { + pci_read_config_dword(p->pci_dev, 0xd4, &val32); val32 &= ~enable_bit; - pci_write_config_dword(iTCO_wdt_private.pdev, 0xd4, val32); + pci_write_config_dword(p->pci_dev, 0xd4, val32); - pci_read_config_dword(iTCO_wdt_private.pdev, 0xd4, &val32); + pci_read_config_dword(p->pci_dev, 0xd4, &val32); } if (val32 & enable_bit) @@ -211,32 +214,33 @@ static int iTCO_wdt_unset_NO_REBOOT_bit(void) static int iTCO_wdt_start(struct watchdog_device *wd_dev) { + struct iTCO_wdt_private *p = watchdog_get_drvdata(wd_dev); unsigned int val; - spin_lock(&iTCO_wdt_private.io_lock); + spin_lock(&p->io_lock); - iTCO_vendor_pre_start(iTCO_wdt_private.smi_res, wd_dev->timeout); + iTCO_vendor_pre_start(p->smi_res, wd_dev->timeout); /* disable chipset's NO_REBOOT bit */ - if (iTCO_wdt_unset_NO_REBOOT_bit()) { - spin_unlock(&iTCO_wdt_private.io_lock); + if (iTCO_wdt_unset_NO_REBOOT_bit(p)) { + spin_unlock(&p->io_lock); pr_err("failed to reset NO_REBOOT flag, reboot disabled by hardware/BIOS\n"); return -EIO; } /* Force the timer to its reload value by writing to the TCO_RLD register */ - if (iTCO_wdt_private.iTCO_version >= 2) - outw(0x01, TCO_RLD); - else if (iTCO_wdt_private.iTCO_version == 1) - outb(0x01, TCO_RLD); + if (p->iTCO_version >= 2) + outw(0x01, TCO_RLD(p)); + else if (p->iTCO_version == 1) + outb(0x01, TCO_RLD(p)); /* Bit 11: TCO Timer Halt -> 0 = The TCO timer is enabled to count */ - val = inw(TCO1_CNT); + val = inw(TCO1_CNT(p)); val &= 0xf7ff; - outw(val, TCO1_CNT); - val = inw(TCO1_CNT); - spin_unlock(&iTCO_wdt_private.io_lock); + outw(val, TCO1_CNT(p)); + val = inw(TCO1_CNT(p)); + spin_unlock(&p->io_lock); if (val & 0x0800) return -1; @@ -245,22 +249,23 @@ static int iTCO_wdt_start(struct watchdog_device *wd_dev) static int iTCO_wdt_stop(struct watchdog_device *wd_dev) { + struct iTCO_wdt_private *p = watchdog_get_drvdata(wd_dev); unsigned int val; - spin_lock(&iTCO_wdt_private.io_lock); + spin_lock(&p->io_lock); - iTCO_vendor_pre_stop(iTCO_wdt_private.smi_res); + iTCO_vendor_pre_stop(p->smi_res); /* Bit 11: TCO Timer Halt -> 1 = The TCO timer is disabled */ - val = inw(TCO1_CNT); + val = inw(TCO1_CNT(p)); val |= 0x0800; - outw(val, TCO1_CNT); - val = inw(TCO1_CNT); + outw(val, TCO1_CNT(p)); + val = inw(TCO1_CNT(p)); /* Set the NO_REBOOT bit to prevent later reboots, just for sure */ - iTCO_wdt_set_NO_REBOOT_bit(); + iTCO_wdt_set_NO_REBOOT_bit(p); - spin_unlock(&iTCO_wdt_private.io_lock); + spin_unlock(&p->io_lock); if ((val & 0x0800) == 0) return -1; @@ -269,67 +274,70 @@ static int iTCO_wdt_stop(struct watchdog_device *wd_dev) static int iTCO_wdt_ping(struct watchdog_device *wd_dev) { - spin_lock(&iTCO_wdt_private.io_lock); + struct iTCO_wdt_private *p = watchdog_get_drvdata(wd_dev); - iTCO_vendor_pre_keepalive(iTCO_wdt_private.smi_res, wd_dev->timeout); + spin_lock(&p->io_lock); + + iTCO_vendor_pre_keepalive(p->smi_res, wd_dev->timeout); /* Reload the timer by writing to the TCO Timer Counter register */ - if (iTCO_wdt_private.iTCO_version >= 2) { - outw(0x01, TCO_RLD); - } else if (iTCO_wdt_private.iTCO_version == 1) { + if (p->iTCO_version >= 2) { + outw(0x01, TCO_RLD(p)); + } else if (p->iTCO_version == 1) { /* Reset the timeout status bit so that the timer * needs to count down twice again before rebooting */ - outw(0x0008, TCO1_STS); /* write 1 to clear bit */ + outw(0x0008, TCO1_STS(p)); /* write 1 to clear bit */ - outb(0x01, TCO_RLD); + outb(0x01, TCO_RLD(p)); } - spin_unlock(&iTCO_wdt_private.io_lock); + spin_unlock(&p->io_lock); return 0; } static int iTCO_wdt_set_timeout(struct watchdog_device *wd_dev, unsigned int t) { + struct iTCO_wdt_private *p = watchdog_get_drvdata(wd_dev); unsigned int val16; unsigned char val8; unsigned int tmrval; - tmrval = seconds_to_ticks(t); + tmrval = seconds_to_ticks(p, t); /* For TCO v1 the timer counts down twice before rebooting */ - if (iTCO_wdt_private.iTCO_version == 1) + if (p->iTCO_version == 1) tmrval /= 2; /* from the specs: */ /* "Values of 0h-3h are ignored and should not be attempted" */ if (tmrval < 0x04) return -EINVAL; - if (((iTCO_wdt_private.iTCO_version >= 2) && (tmrval > 0x3ff)) || - ((iTCO_wdt_private.iTCO_version == 1) && (tmrval > 0x03f))) + if ((p->iTCO_version >= 2 && tmrval > 0x3ff) || + (p->iTCO_version == 1 && tmrval > 0x03f)) return -EINVAL; iTCO_vendor_pre_set_heartbeat(tmrval); /* Write new heartbeat to watchdog */ - if (iTCO_wdt_private.iTCO_version >= 2) { - spin_lock(&iTCO_wdt_private.io_lock); - val16 = inw(TCOv2_TMR); + if (p->iTCO_version >= 2) { + spin_lock(&p->io_lock); + val16 = inw(TCOv2_TMR(p)); val16 &= 0xfc00; val16 |= tmrval; - outw(val16, TCOv2_TMR); - val16 = inw(TCOv2_TMR); - spin_unlock(&iTCO_wdt_private.io_lock); + outw(val16, TCOv2_TMR(p)); + val16 = inw(TCOv2_TMR(p)); + spin_unlock(&p->io_lock); if ((val16 & 0x3ff) != tmrval) return -EINVAL; - } else if (iTCO_wdt_private.iTCO_version == 1) { - spin_lock(&iTCO_wdt_private.io_lock); - val8 = inb(TCOv1_TMR); + } else if (p->iTCO_version == 1) { + spin_lock(&p->io_lock); + val8 = inb(TCOv1_TMR(p)); val8 &= 0xc0; val8 |= (tmrval & 0xff); - outb(val8, TCOv1_TMR); - val8 = inb(TCOv1_TMR); - spin_unlock(&iTCO_wdt_private.io_lock); + outb(val8, TCOv1_TMR(p)); + val8 = inb(TCOv1_TMR(p)); + spin_unlock(&p->io_lock); if ((val8 & 0x3f) != tmrval) return -EINVAL; @@ -341,27 +349,28 @@ static int iTCO_wdt_set_timeout(struct watchdog_device *wd_dev, unsigned int t) static unsigned int iTCO_wdt_get_timeleft(struct watchdog_device *wd_dev) { + struct iTCO_wdt_private *p = watchdog_get_drvdata(wd_dev); unsigned int val16; unsigned char val8; unsigned int time_left = 0; /* read the TCO Timer */ - if (iTCO_wdt_private.iTCO_version >= 2) { - spin_lock(&iTCO_wdt_private.io_lock); - val16 = inw(TCO_RLD); + if (p->iTCO_version >= 2) { + spin_lock(&p->io_lock); + val16 = inw(TCO_RLD(p)); val16 &= 0x3ff; - spin_unlock(&iTCO_wdt_private.io_lock); + spin_unlock(&p->io_lock); - time_left = ticks_to_seconds(val16); - } else if (iTCO_wdt_private.iTCO_version == 1) { - spin_lock(&iTCO_wdt_private.io_lock); - val8 = inb(TCO_RLD); + time_left = ticks_to_seconds(p, val16); + } else if (p->iTCO_version == 1) { + spin_lock(&p->io_lock); + val8 = inb(TCO_RLD(p)); val8 &= 0x3f; - if (!(inw(TCO1_STS) & 0x0008)) - val8 += (inb(TCOv1_TMR) & 0x3f); - spin_unlock(&iTCO_wdt_private.io_lock); + if (!(inw(TCO1_STS(p)) & 0x0008)) + val8 += (inb(TCOv1_TMR(p)) & 0x3f); + spin_unlock(&p->io_lock); - time_left = ticks_to_seconds(val8); + time_left = ticks_to_seconds(p, val8); } return time_left; } @@ -387,211 +396,154 @@ static const struct watchdog_ops iTCO_wdt_ops = { .get_timeleft = iTCO_wdt_get_timeleft, }; -static struct watchdog_device iTCO_wdt_watchdog_dev = { - .info = &ident, - .ops = &iTCO_wdt_ops, -}; - /* * Init & exit routines */ -static void iTCO_wdt_cleanup(void) +static int iTCO_wdt_probe(struct platform_device *pdev) { - /* Stop the timer before we leave */ - if (!nowayout) - iTCO_wdt_stop(&iTCO_wdt_watchdog_dev); - - /* Deregister */ - watchdog_unregister_device(&iTCO_wdt_watchdog_dev); - - /* release resources */ - release_region(iTCO_wdt_private.tco_res->start, - resource_size(iTCO_wdt_private.tco_res)); - release_region(iTCO_wdt_private.smi_res->start, - resource_size(iTCO_wdt_private.smi_res)); - if (iTCO_wdt_private.iTCO_version >= 2) { - iounmap(iTCO_wdt_private.gcs_pmc); - release_mem_region(iTCO_wdt_private.gcs_pmc_res->start, - resource_size(iTCO_wdt_private.gcs_pmc_res)); - } - - iTCO_wdt_private.tco_res = NULL; - iTCO_wdt_private.smi_res = NULL; - iTCO_wdt_private.gcs_pmc_res = NULL; - iTCO_wdt_private.gcs_pmc = NULL; -} - -static int iTCO_wdt_probe(struct platform_device *dev) -{ - int ret = -ENODEV; + struct device *dev = &pdev->dev; + struct itco_wdt_platform_data *pdata = dev_get_platdata(dev); + struct iTCO_wdt_private *p; unsigned long val32; - struct itco_wdt_platform_data *pdata = dev_get_platdata(&dev->dev); + int ret; if (!pdata) - goto out; + return -ENODEV; - spin_lock_init(&iTCO_wdt_private.io_lock); + p = devm_kzalloc(dev, sizeof(*p), GFP_KERNEL); + if (!p) + return -ENOMEM; - iTCO_wdt_private.tco_res = - platform_get_resource(dev, IORESOURCE_IO, ICH_RES_IO_TCO); - if (!iTCO_wdt_private.tco_res) - goto out; + spin_lock_init(&p->io_lock); - iTCO_wdt_private.smi_res = - platform_get_resource(dev, IORESOURCE_IO, ICH_RES_IO_SMI); - if (!iTCO_wdt_private.smi_res) - goto out; + p->tco_res = platform_get_resource(pdev, IORESOURCE_IO, ICH_RES_IO_TCO); + if (!p->tco_res) + return -ENODEV; - iTCO_wdt_private.iTCO_version = pdata->version; - iTCO_wdt_private.dev = dev; - iTCO_wdt_private.pdev = to_pci_dev(dev->dev.parent); + p->smi_res = platform_get_resource(pdev, IORESOURCE_IO, ICH_RES_IO_SMI); + if (!p->smi_res) + return -ENODEV; + + p->iTCO_version = pdata->version; + p->pci_dev = to_pci_dev(dev->parent); /* * Get the Memory-Mapped GCS or PMC register, we need it for the * NO_REBOOT flag (TCO v2 and v3). */ - if (iTCO_wdt_private.iTCO_version >= 2) { - iTCO_wdt_private.gcs_pmc_res = platform_get_resource(dev, - IORESOURCE_MEM, - ICH_RES_MEM_GCS_PMC); - - if (!iTCO_wdt_private.gcs_pmc_res) - goto out; - - if (!request_mem_region(iTCO_wdt_private.gcs_pmc_res->start, - resource_size(iTCO_wdt_private.gcs_pmc_res), dev->name)) { - ret = -EBUSY; - goto out; - } - iTCO_wdt_private.gcs_pmc = ioremap(iTCO_wdt_private.gcs_pmc_res->start, - resource_size(iTCO_wdt_private.gcs_pmc_res)); - if (!iTCO_wdt_private.gcs_pmc) { - ret = -EIO; - goto unreg_gcs_pmc; - } + if (p->iTCO_version >= 2) { + p->gcs_pmc_res = platform_get_resource(pdev, + IORESOURCE_MEM, + ICH_RES_MEM_GCS_PMC); + p->gcs_pmc = devm_ioremap_resource(dev, p->gcs_pmc_res); + if (IS_ERR(p->gcs_pmc)) + return PTR_ERR(p->gcs_pmc); } /* Check chipset's NO_REBOOT bit */ - if (iTCO_wdt_unset_NO_REBOOT_bit() && iTCO_vendor_check_noreboot_on()) { + if (iTCO_wdt_unset_NO_REBOOT_bit(p) && + iTCO_vendor_check_noreboot_on()) { pr_info("unable to reset NO_REBOOT flag, device disabled by hardware/BIOS\n"); - ret = -ENODEV; /* Cannot reset NO_REBOOT bit */ - goto unmap_gcs_pmc; + return -ENODEV; /* Cannot reset NO_REBOOT bit */ } /* Set the NO_REBOOT bit to prevent later reboots, just for sure */ - iTCO_wdt_set_NO_REBOOT_bit(); + iTCO_wdt_set_NO_REBOOT_bit(p); /* The TCO logic uses the TCO_EN bit in the SMI_EN register */ - if (!request_region(iTCO_wdt_private.smi_res->start, - resource_size(iTCO_wdt_private.smi_res), dev->name)) { + if (!devm_request_region(dev, p->smi_res->start, + resource_size(p->smi_res), + pdev->name)) { pr_err("I/O address 0x%04llx already in use, device disabled\n", - (u64)SMI_EN); - ret = -EBUSY; - goto unmap_gcs_pmc; + (u64)SMI_EN(p)); + return -EBUSY; } - if (turn_SMI_watchdog_clear_off >= iTCO_wdt_private.iTCO_version) { + if (turn_SMI_watchdog_clear_off >= p->iTCO_version) { /* * Bit 13: TCO_EN -> 0 * Disables TCO logic generating an SMI# */ - val32 = inl(SMI_EN); + val32 = inl(SMI_EN(p)); val32 &= 0xffffdfff; /* Turn off SMI clearing watchdog */ - outl(val32, SMI_EN); + outl(val32, SMI_EN(p)); } - if (!request_region(iTCO_wdt_private.tco_res->start, - resource_size(iTCO_wdt_private.tco_res), dev->name)) { + if (!devm_request_region(dev, p->tco_res->start, + resource_size(p->tco_res), + pdev->name)) { pr_err("I/O address 0x%04llx already in use, device disabled\n", - (u64)TCOBASE); - ret = -EBUSY; - goto unreg_smi; + (u64)TCOBASE(p)); + return -EBUSY; } pr_info("Found a %s TCO device (Version=%d, TCOBASE=0x%04llx)\n", - pdata->name, pdata->version, (u64)TCOBASE); + pdata->name, pdata->version, (u64)TCOBASE(p)); /* Clear out the (probably old) status */ - switch (iTCO_wdt_private.iTCO_version) { + switch (p->iTCO_version) { case 5: case 4: - outw(0x0008, TCO1_STS); /* Clear the Time Out Status bit */ - outw(0x0002, TCO2_STS); /* Clear SECOND_TO_STS bit */ + outw(0x0008, TCO1_STS(p)); /* Clear the Time Out Status bit */ + outw(0x0002, TCO2_STS(p)); /* Clear SECOND_TO_STS bit */ break; case 3: - outl(0x20008, TCO1_STS); + outl(0x20008, TCO1_STS(p)); 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 */ + outw(0x0008, TCO1_STS(p)); /* Clear the Time Out Status bit */ + outw(0x0002, TCO2_STS(p)); /* Clear SECOND_TO_STS bit */ + outw(0x0004, TCO2_STS(p)); /* Clear BOOT_STS bit */ break; } - iTCO_wdt_watchdog_dev.bootstatus = 0; - iTCO_wdt_watchdog_dev.timeout = WATCHDOG_TIMEOUT; - watchdog_set_nowayout(&iTCO_wdt_watchdog_dev, nowayout); - iTCO_wdt_watchdog_dev.parent = &dev->dev; + p->wddev.info = &ident, + p->wddev.ops = &iTCO_wdt_ops, + p->wddev.bootstatus = 0; + p->wddev.timeout = WATCHDOG_TIMEOUT; + watchdog_set_nowayout(&p->wddev, nowayout); + p->wddev.parent = dev; + + watchdog_set_drvdata(&p->wddev, p); + platform_set_drvdata(pdev, p); /* Make sure the watchdog is not running */ - iTCO_wdt_stop(&iTCO_wdt_watchdog_dev); + iTCO_wdt_stop(&p->wddev); /* Check that the heartbeat value is within it's range; if not reset to the default */ - if (iTCO_wdt_set_timeout(&iTCO_wdt_watchdog_dev, heartbeat)) { - iTCO_wdt_set_timeout(&iTCO_wdt_watchdog_dev, WATCHDOG_TIMEOUT); + if (iTCO_wdt_set_timeout(&p->wddev, heartbeat)) { + iTCO_wdt_set_timeout(&p->wddev, WATCHDOG_TIMEOUT); pr_info("timeout value out of range, using %d\n", WATCHDOG_TIMEOUT); } - ret = watchdog_register_device(&iTCO_wdt_watchdog_dev); + watchdog_stop_on_reboot(&p->wddev); + ret = devm_watchdog_register_device(dev, &p->wddev); if (ret != 0) { pr_err("cannot register watchdog device (err=%d)\n", ret); - goto unreg_tco; + return ret; } pr_info("initialized. heartbeat=%d sec (nowayout=%d)\n", heartbeat, nowayout); return 0; - -unreg_tco: - release_region(iTCO_wdt_private.tco_res->start, - resource_size(iTCO_wdt_private.tco_res)); -unreg_smi: - release_region(iTCO_wdt_private.smi_res->start, - resource_size(iTCO_wdt_private.smi_res)); -unmap_gcs_pmc: - if (iTCO_wdt_private.iTCO_version >= 2) - iounmap(iTCO_wdt_private.gcs_pmc); -unreg_gcs_pmc: - if (iTCO_wdt_private.iTCO_version >= 2) - release_mem_region(iTCO_wdt_private.gcs_pmc_res->start, - resource_size(iTCO_wdt_private.gcs_pmc_res)); -out: - iTCO_wdt_private.tco_res = NULL; - iTCO_wdt_private.smi_res = NULL; - iTCO_wdt_private.gcs_pmc_res = NULL; - iTCO_wdt_private.gcs_pmc = NULL; - - return ret; } -static int iTCO_wdt_remove(struct platform_device *dev) +static int iTCO_wdt_remove(struct platform_device *pdev) { - if (iTCO_wdt_private.tco_res || iTCO_wdt_private.smi_res) - iTCO_wdt_cleanup(); + struct iTCO_wdt_private *p = platform_get_drvdata(pdev); + + /* Stop the timer before we leave */ + if (!nowayout) + iTCO_wdt_stop(&p->wddev); return 0; } -static void iTCO_wdt_shutdown(struct platform_device *dev) -{ - iTCO_wdt_stop(NULL); -} - #ifdef CONFIG_PM_SLEEP /* * Suspend-to-idle requires this, because it stops the ticks and timekeeping, so @@ -610,21 +562,24 @@ static inline bool need_suspend(void) { return true; } static int iTCO_wdt_suspend_noirq(struct device *dev) { + struct iTCO_wdt_private *p = dev_get_drvdata(dev); int ret = 0; - iTCO_wdt_private.suspended = false; - if (watchdog_active(&iTCO_wdt_watchdog_dev) && need_suspend()) { - ret = iTCO_wdt_stop(&iTCO_wdt_watchdog_dev); + p->suspended = false; + if (watchdog_active(&p->wddev) && need_suspend()) { + ret = iTCO_wdt_stop(&p->wddev); if (!ret) - iTCO_wdt_private.suspended = true; + p->suspended = true; } return ret; } static int iTCO_wdt_resume_noirq(struct device *dev) { - if (iTCO_wdt_private.suspended) - iTCO_wdt_start(&iTCO_wdt_watchdog_dev); + struct iTCO_wdt_private *p = dev_get_drvdata(dev); + + if (p->suspended) + iTCO_wdt_start(&p->wddev); return 0; } @@ -642,7 +597,6 @@ static const struct dev_pm_ops iTCO_wdt_pm = { static struct platform_driver iTCO_wdt_driver = { .probe = iTCO_wdt_probe, .remove = iTCO_wdt_remove, - .shutdown = iTCO_wdt_shutdown, .driver = { .name = DRV_NAME, .pm = ITCO_WDT_PM_OPS, @@ -651,15 +605,9 @@ static struct platform_driver iTCO_wdt_driver = { static int __init iTCO_wdt_init_module(void) { - int err; - pr_info("Intel TCO WatchDog Timer Driver v%s\n", DRV_VERSION); - err = platform_driver_register(&iTCO_wdt_driver); - if (err) - return err; - - return 0; + return platform_driver_register(&iTCO_wdt_driver); } static void __exit iTCO_wdt_cleanup_module(void) diff --git a/drivers/watchdog/imgpdc_wdt.c b/drivers/watchdog/imgpdc_wdt.c index 516fbef00856..6ed39dee995f 100644 --- a/drivers/watchdog/imgpdc_wdt.c +++ b/drivers/watchdog/imgpdc_wdt.c @@ -161,7 +161,7 @@ static int pdc_wdt_restart(struct watchdog_device *wdt_dev, return 0; } -static struct watchdog_info pdc_wdt_info = { +static const struct watchdog_info pdc_wdt_info = { .identity = "IMG PDC Watchdog", .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | diff --git a/drivers/watchdog/intel-mid_wdt.c b/drivers/watchdog/intel-mid_wdt.c index a4b729259b12..45e4d02221b5 100644 --- a/drivers/watchdog/intel-mid_wdt.c +++ b/drivers/watchdog/intel-mid_wdt.c @@ -137,7 +137,6 @@ static int mid_wdt_probe(struct platform_device *pdev) wdt_dev->parent = &pdev->dev; watchdog_set_drvdata(wdt_dev, &pdev->dev); - platform_set_drvdata(pdev, wdt_dev); ret = devm_request_irq(&pdev->dev, pdata->irq, mid_wdt_irq, IRQF_SHARED | IRQF_NO_SUSPEND, "watchdog", @@ -151,7 +150,7 @@ static int mid_wdt_probe(struct platform_device *pdev) /* Make sure the watchdog is not running */ wdt_stop(wdt_dev); - ret = watchdog_register_device(wdt_dev); + ret = devm_watchdog_register_device(&pdev->dev, wdt_dev); if (ret) { dev_err(&pdev->dev, "error registering watchdog device\n"); return ret; @@ -162,16 +161,8 @@ static int mid_wdt_probe(struct platform_device *pdev) return 0; } -static int mid_wdt_remove(struct platform_device *pdev) -{ - struct watchdog_device *wd = platform_get_drvdata(pdev); - watchdog_unregister_device(wd); - return 0; -} - static struct platform_driver mid_wdt_driver = { .probe = mid_wdt_probe, - .remove = mid_wdt_remove, .driver = { .name = "intel_mid_wdt", }, diff --git a/drivers/watchdog/kempld_wdt.c b/drivers/watchdog/kempld_wdt.c index 8e302d0e346c..73c46b3a09ab 100644 --- a/drivers/watchdog/kempld_wdt.c +++ b/drivers/watchdog/kempld_wdt.c @@ -422,7 +422,7 @@ static int kempld_wdt_probe_stages(struct watchdog_device *wdd) return 0; } -static struct watchdog_info kempld_wdt_info = { +static const struct watchdog_info kempld_wdt_info = { .identity = "KEMPLD Watchdog", .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | diff --git a/drivers/watchdog/lantiq_wdt.c b/drivers/watchdog/lantiq_wdt.c index 582f2fa1b8d9..e0823677d8c1 100644 --- a/drivers/watchdog/lantiq_wdt.c +++ b/drivers/watchdog/lantiq_wdt.c @@ -3,7 +3,7 @@ * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * - * Copyright (C) 2010 John Crispin + * Copyright (C) 2010 John Crispin * Based on EP93xx wdt driver */ @@ -240,6 +240,6 @@ module_platform_driver(ltq_wdt_driver); module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started"); -MODULE_AUTHOR("John Crispin "); +MODULE_AUTHOR("John Crispin "); MODULE_DESCRIPTION("Lantiq SoC Watchdog"); MODULE_LICENSE("GPL"); diff --git a/drivers/watchdog/lpc18xx_wdt.c b/drivers/watchdog/lpc18xx_wdt.c index fd171e6caa16..3b8bb59adf02 100644 --- a/drivers/watchdog/lpc18xx_wdt.c +++ b/drivers/watchdog/lpc18xx_wdt.c @@ -181,7 +181,7 @@ static int lpc18xx_wdt_restart(struct watchdog_device *wdt_dev, return 0; } -static struct watchdog_info lpc18xx_wdt_info = { +static const struct watchdog_info lpc18xx_wdt_info = { .identity = "NXP LPC18xx Watchdog", .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | diff --git a/drivers/watchdog/mena21_wdt.c b/drivers/watchdog/mena21_wdt.c index af6a7c489f08..045201a6fdb3 100644 --- a/drivers/watchdog/mena21_wdt.c +++ b/drivers/watchdog/mena21_wdt.c @@ -212,33 +212,14 @@ static int a21_wdt_probe(struct platform_device *pdev) drv->wdt = a21_wdt; dev_set_drvdata(&pdev->dev, drv); - ret = watchdog_register_device(&a21_wdt); + ret = devm_watchdog_register_device(&pdev->dev, &a21_wdt); if (ret) { dev_err(&pdev->dev, "Cannot register watchdog device\n"); - goto err_register_wd; + return ret; } dev_info(&pdev->dev, "MEN A21 watchdog timer driver enabled\n"); - return 0; - -err_register_wd: - mutex_destroy(&drv->lock); - - return ret; -} - -static int a21_wdt_remove(struct platform_device *pdev) -{ - struct a21_wdt_drv *drv = dev_get_drvdata(&pdev->dev); - - dev_warn(&pdev->dev, - "Unregistering A21 watchdog driver, board may reboot\n"); - - watchdog_unregister_device(&drv->wdt); - - mutex_destroy(&drv->lock); - return 0; } @@ -257,7 +238,6 @@ MODULE_DEVICE_TABLE(of, a21_wdt_ids); static struct platform_driver a21_wdt_driver = { .probe = a21_wdt_probe, - .remove = a21_wdt_remove, .shutdown = a21_wdt_shutdown, .driver = { .name = "a21-watchdog", diff --git a/drivers/watchdog/meson_wdt.c b/drivers/watchdog/meson_wdt.c index 56ea1caf71c3..491b9bf13d84 100644 --- a/drivers/watchdog/meson_wdt.c +++ b/drivers/watchdog/meson_wdt.c @@ -201,38 +201,19 @@ static int meson_wdt_probe(struct platform_device *pdev) meson_wdt_stop(&meson_wdt->wdt_dev); - err = watchdog_register_device(&meson_wdt->wdt_dev); + watchdog_stop_on_reboot(&meson_wdt->wdt_dev); + err = devm_watchdog_register_device(&pdev->dev, &meson_wdt->wdt_dev); if (err) return err; - platform_set_drvdata(pdev, meson_wdt); - dev_info(&pdev->dev, "Watchdog enabled (timeout=%d sec, nowayout=%d)", meson_wdt->wdt_dev.timeout, nowayout); return 0; } -static int meson_wdt_remove(struct platform_device *pdev) -{ - struct meson_wdt_dev *meson_wdt = platform_get_drvdata(pdev); - - watchdog_unregister_device(&meson_wdt->wdt_dev); - - return 0; -} - -static void meson_wdt_shutdown(struct platform_device *pdev) -{ - struct meson_wdt_dev *meson_wdt = platform_get_drvdata(pdev); - - meson_wdt_stop(&meson_wdt->wdt_dev); -} - static struct platform_driver meson_wdt_driver = { .probe = meson_wdt_probe, - .remove = meson_wdt_remove, - .shutdown = meson_wdt_shutdown, .driver = { .name = DRV_NAME, .of_match_table = meson_wdt_dt_ids, diff --git a/drivers/watchdog/mt7621_wdt.c b/drivers/watchdog/mt7621_wdt.c index d5735c12067d..48a06067075d 100644 --- a/drivers/watchdog/mt7621_wdt.c +++ b/drivers/watchdog/mt7621_wdt.c @@ -1,7 +1,7 @@ /* * Ralink MT7621/MT7628 built-in hardware watchdog timer * - * Copyright (C) 2014 John Crispin + * Copyright (C) 2014 John Crispin * * This driver was based on: drivers/watchdog/rt2880_wdt.c * @@ -110,7 +110,7 @@ static struct watchdog_info mt7621_wdt_info = { .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, }; -static struct watchdog_ops mt7621_wdt_ops = { +static const struct watchdog_ops mt7621_wdt_ops = { .owner = THIS_MODULE, .start = mt7621_wdt_start, .stop = mt7621_wdt_stop, @@ -181,5 +181,5 @@ static struct platform_driver mt7621_wdt_driver = { module_platform_driver(mt7621_wdt_driver); MODULE_DESCRIPTION("MediaTek MT762x hardware watchdog driver"); -MODULE_AUTHOR("John Crispin +#include +#include +#include +#include +#include +#include + +#define LOCK 0xA5 +#define UNLOCK 0x5A + +#define WDT_CTRL_RESET_EN BIT(7) +#define WDT_RELOAD_PORT_EN BIT(7) + +#define WDT_CTRL 1 +#define WDT_RELOAD_CTRL 2 +#define WDT_PRESET_PRESCALE 4 +#define WDT_REG_LOCK 5 +#define WDT_COUNT 6 +#define WDT_RELOAD_PORT 7 + +#define WDT_MIN_TIMEOUT 1 +#define WDT_MAX_TIMEOUT 464 +#define WDT_DEFAULT_TIMEOUT 80 + +#define WDT_MAX_COUNTER 15 + +static unsigned int timeout; +module_param(timeout, uint, 0); +MODULE_PARM_DESC(timeout, + "Watchdog timeout in seconds. (default=" + __MODULE_STRING(WDT_DEFAULT_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 nic7018_wdt { + u16 io_base; + u32 period; + struct watchdog_device wdd; +}; + +struct nic7018_config { + u32 period; + u8 divider; +}; + +static const struct nic7018_config nic7018_configs[] = { + { 2, 4 }, + { 32, 5 }, +}; + +static inline u32 nic7018_timeout(u32 period, u8 counter) +{ + return period * counter - period / 2; +} + +static const struct nic7018_config *nic7018_get_config(u32 timeout, + u8 *counter) +{ + const struct nic7018_config *config; + u8 count; + + if (timeout < 30 && timeout != 16) { + config = &nic7018_configs[0]; + count = timeout / 2 + 1; + } else { + config = &nic7018_configs[1]; + count = DIV_ROUND_UP(timeout + 16, 32); + + if (count > WDT_MAX_COUNTER) + count = WDT_MAX_COUNTER; + } + *counter = count; + return config; +} + +static int nic7018_set_timeout(struct watchdog_device *wdd, + unsigned int timeout) +{ + struct nic7018_wdt *wdt = watchdog_get_drvdata(wdd); + const struct nic7018_config *config; + u8 counter; + + config = nic7018_get_config(timeout, &counter); + + outb(counter << 4 | config->divider, + wdt->io_base + WDT_PRESET_PRESCALE); + + wdd->timeout = nic7018_timeout(config->period, counter); + wdt->period = config->period; + + return 0; +} + +static int nic7018_start(struct watchdog_device *wdd) +{ + struct nic7018_wdt *wdt = watchdog_get_drvdata(wdd); + u8 control; + + nic7018_set_timeout(wdd, wdd->timeout); + + control = inb(wdt->io_base + WDT_RELOAD_CTRL); + outb(control | WDT_RELOAD_PORT_EN, wdt->io_base + WDT_RELOAD_CTRL); + + outb(1, wdt->io_base + WDT_RELOAD_PORT); + + control = inb(wdt->io_base + WDT_CTRL); + outb(control | WDT_CTRL_RESET_EN, wdt->io_base + WDT_CTRL); + + return 0; +} + +static int nic7018_stop(struct watchdog_device *wdd) +{ + struct nic7018_wdt *wdt = watchdog_get_drvdata(wdd); + + outb(0, wdt->io_base + WDT_CTRL); + outb(0, wdt->io_base + WDT_RELOAD_CTRL); + outb(0xF0, wdt->io_base + WDT_PRESET_PRESCALE); + + return 0; +} + +static int nic7018_ping(struct watchdog_device *wdd) +{ + struct nic7018_wdt *wdt = watchdog_get_drvdata(wdd); + + outb(1, wdt->io_base + WDT_RELOAD_PORT); + + return 0; +} + +static unsigned int nic7018_get_timeleft(struct watchdog_device *wdd) +{ + struct nic7018_wdt *wdt = watchdog_get_drvdata(wdd); + u8 count; + + count = inb(wdt->io_base + WDT_COUNT) & 0xF; + if (!count) + return 0; + + return nic7018_timeout(wdt->period, count); +} + +static const struct watchdog_info nic7018_wdd_info = { + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, + .identity = "NIC7018 Watchdog", +}; + +static const struct watchdog_ops nic7018_wdd_ops = { + .owner = THIS_MODULE, + .start = nic7018_start, + .stop = nic7018_stop, + .ping = nic7018_ping, + .set_timeout = nic7018_set_timeout, + .get_timeleft = nic7018_get_timeleft, +}; + +static int nic7018_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct watchdog_device *wdd; + struct nic7018_wdt *wdt; + struct resource *io_rc; + int ret; + + wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL); + if (!wdt) + return -ENOMEM; + + platform_set_drvdata(pdev, wdt); + + io_rc = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (!io_rc) { + dev_err(dev, "missing IO resources\n"); + return -EINVAL; + } + + if (!devm_request_region(dev, io_rc->start, resource_size(io_rc), + KBUILD_MODNAME)) { + dev_err(dev, "failed to get IO region\n"); + return -EBUSY; + } + + wdt->io_base = io_rc->start; + wdd = &wdt->wdd; + wdd->info = &nic7018_wdd_info; + wdd->ops = &nic7018_wdd_ops; + wdd->min_timeout = WDT_MIN_TIMEOUT; + wdd->max_timeout = WDT_MAX_TIMEOUT; + wdd->timeout = WDT_DEFAULT_TIMEOUT; + wdd->parent = dev; + + watchdog_set_drvdata(wdd, wdt); + watchdog_set_nowayout(wdd, nowayout); + + ret = watchdog_init_timeout(wdd, timeout, dev); + if (ret) + dev_warn(dev, "unable to set timeout value, using default\n"); + + /* Unlock WDT register */ + outb(UNLOCK, wdt->io_base + WDT_REG_LOCK); + + ret = watchdog_register_device(wdd); + if (ret) { + outb(LOCK, wdt->io_base + WDT_REG_LOCK); + dev_err(dev, "failed to register watchdog\n"); + return ret; + } + + dev_dbg(dev, "io_base=0x%04X, timeout=%d, nowayout=%d\n", + wdt->io_base, timeout, nowayout); + return 0; +} + +static int nic7018_remove(struct platform_device *pdev) +{ + struct nic7018_wdt *wdt = platform_get_drvdata(pdev); + + watchdog_unregister_device(&wdt->wdd); + + /* Lock WDT register */ + outb(LOCK, wdt->io_base + WDT_REG_LOCK); + + return 0; +} + +static const struct acpi_device_id nic7018_device_ids[] = { + {"NIC7018", 0}, + {"", 0}, +}; +MODULE_DEVICE_TABLE(acpi, nic7018_device_ids); + +static struct platform_driver watchdog_driver = { + .probe = nic7018_probe, + .remove = nic7018_remove, + .driver = { + .name = KBUILD_MODNAME, + .acpi_match_table = ACPI_PTR(nic7018_device_ids), + }, +}; + +module_platform_driver(watchdog_driver); + +MODULE_DESCRIPTION("National Instruments NIC7018 Watchdog driver"); +MODULE_AUTHOR("Hui Chun Ong "); +MODULE_LICENSE("GPL"); diff --git a/drivers/watchdog/orion_wdt.c b/drivers/watchdog/orion_wdt.c index c6b8f4a43bde..39be4dd8035e 100644 --- a/drivers/watchdog/orion_wdt.c +++ b/drivers/watchdog/orion_wdt.c @@ -395,7 +395,7 @@ static void __iomem *orion_wdt_ioremap_rstout(struct platform_device *pdev, rstout = internal_regs + ORION_RSTOUT_MASK_OFFSET; - WARN(1, FW_BUG "falling back to harcoded RSTOUT reg %pa\n", &rstout); + WARN(1, FW_BUG "falling back to hardcoded RSTOUT reg %pa\n", &rstout); return devm_ioremap(&pdev->dev, rstout, 0x4); } diff --git a/drivers/watchdog/pika_wdt.c b/drivers/watchdog/pika_wdt.c index 0cdfee266690..e35cf5e87907 100644 --- a/drivers/watchdog/pika_wdt.c +++ b/drivers/watchdog/pika_wdt.c @@ -54,7 +54,7 @@ static struct { struct timer_list timer; /* The timer that pings the watchdog */ } pikawdt_private; -static struct watchdog_info ident = { +static struct watchdog_info ident __ro_after_init = { .identity = DRV_NAME, .options = WDIOF_CARDRESET | WDIOF_SETTIMEOUT | diff --git a/drivers/watchdog/rn5t618_wdt.c b/drivers/watchdog/rn5t618_wdt.c index 0805ee2acd7a..e60f55702ab7 100644 --- a/drivers/watchdog/rn5t618_wdt.c +++ b/drivers/watchdog/rn5t618_wdt.c @@ -130,7 +130,7 @@ static int rn5t618_wdt_ping(struct watchdog_device *wdt_dev) RN5T618_PWRIRQ_IR_WDOG, 0); } -static struct watchdog_info rn5t618_wdt_info = { +static const struct watchdog_info rn5t618_wdt_info = { .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING, .identity = DRIVER_NAME, diff --git a/drivers/watchdog/rt2880_wdt.c b/drivers/watchdog/rt2880_wdt.c index 14b4fd428fff..05524baf7dcc 100644 --- a/drivers/watchdog/rt2880_wdt.c +++ b/drivers/watchdog/rt2880_wdt.c @@ -2,7 +2,7 @@ * Ralink RT288x/RT3xxx/MT76xx built-in hardware watchdog timer * * Copyright (C) 2011 Gabor Juhos - * Copyright (C) 2013 John Crispin + * Copyright (C) 2013 John Crispin * * This driver was based on: drivers/watchdog/softdog.c * @@ -124,7 +124,7 @@ static struct watchdog_info rt288x_wdt_info = { .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, }; -static struct watchdog_ops rt288x_wdt_ops = { +static const struct watchdog_ops rt288x_wdt_ops = { .owner = THIS_MODULE, .start = rt288x_wdt_start, .stop = rt288x_wdt_stop, diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c index 59e95762a6de..6ed97596ca80 100644 --- a/drivers/watchdog/s3c2410_wdt.c +++ b/drivers/watchdog/s3c2410_wdt.c @@ -23,8 +23,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - #include #include #include @@ -46,6 +44,7 @@ #define S3C2410_WTCON 0x00 #define S3C2410_WTDAT 0x04 #define S3C2410_WTCNT 0x08 +#define S3C2410_WTCLRINT 0x0c #define S3C2410_WTCNT_MAXCNT 0xffff @@ -64,14 +63,15 @@ #define S3C2410_WTCON_PRESCALE_MASK (0xff << 8) #define S3C2410_WTCON_PRESCALE_MAX 0xff -#define CONFIG_S3C2410_WATCHDOG_ATBOOT (0) -#define CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME (15) +#define S3C2410_WATCHDOG_ATBOOT (0) +#define S3C2410_WATCHDOG_DEFAULT_TIME (15) #define EXYNOS5_RST_STAT_REG_OFFSET 0x0404 #define EXYNOS5_WDT_DISABLE_REG_OFFSET 0x0408 #define EXYNOS5_WDT_MASK_RESET_REG_OFFSET 0x040c #define QUIRK_HAS_PMU_CONFIG (1 << 0) #define QUIRK_HAS_RST_STAT (1 << 1) +#define QUIRK_HAS_WTCLRINT_REG (1 << 2) /* These quirks require that we have a PMU register map */ #define QUIRKS_HAVE_PMUREG (QUIRK_HAS_PMU_CONFIG | \ @@ -79,26 +79,23 @@ static bool nowayout = WATCHDOG_NOWAYOUT; static int tmr_margin; -static int tmr_atboot = CONFIG_S3C2410_WATCHDOG_ATBOOT; +static int tmr_atboot = S3C2410_WATCHDOG_ATBOOT; static int soft_noboot; -static int debug; module_param(tmr_margin, int, 0); module_param(tmr_atboot, int, 0); module_param(nowayout, bool, 0); module_param(soft_noboot, int, 0); -module_param(debug, int, 0); MODULE_PARM_DESC(tmr_margin, "Watchdog tmr_margin in seconds. (default=" - __MODULE_STRING(CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME) ")"); + __MODULE_STRING(S3C2410_WATCHDOG_DEFAULT_TIME) ")"); MODULE_PARM_DESC(tmr_atboot, "Watchdog is started at boot time if set to 1, default=" - __MODULE_STRING(CONFIG_S3C2410_WATCHDOG_ATBOOT)); + __MODULE_STRING(S3C2410_WATCHDOG_ATBOOT)); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); MODULE_PARM_DESC(soft_noboot, "Watchdog action, set to 1 to ignore reboots, " "0 to reboot (default 0)"); -MODULE_PARM_DESC(debug, "Watchdog debug, set to >1 for debug (default 0)"); /** * struct s3c2410_wdt_variant - Per-variant config data @@ -143,13 +140,18 @@ static const struct s3c2410_wdt_variant drv_data_s3c2410 = { }; #ifdef CONFIG_OF +static const struct s3c2410_wdt_variant drv_data_s3c6410 = { + .quirks = QUIRK_HAS_WTCLRINT_REG, +}; + static const struct s3c2410_wdt_variant drv_data_exynos5250 = { .disable_reg = EXYNOS5_WDT_DISABLE_REG_OFFSET, .mask_reset_reg = EXYNOS5_WDT_MASK_RESET_REG_OFFSET, .mask_bit = 20, .rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET, .rst_stat_bit = 20, - .quirks = QUIRK_HAS_PMU_CONFIG | QUIRK_HAS_RST_STAT, + .quirks = QUIRK_HAS_PMU_CONFIG | QUIRK_HAS_RST_STAT \ + | QUIRK_HAS_WTCLRINT_REG, }; static const struct s3c2410_wdt_variant drv_data_exynos5420 = { @@ -158,7 +160,8 @@ static const struct s3c2410_wdt_variant drv_data_exynos5420 = { .mask_bit = 0, .rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET, .rst_stat_bit = 9, - .quirks = QUIRK_HAS_PMU_CONFIG | QUIRK_HAS_RST_STAT, + .quirks = QUIRK_HAS_PMU_CONFIG | QUIRK_HAS_RST_STAT \ + | QUIRK_HAS_WTCLRINT_REG, }; static const struct s3c2410_wdt_variant drv_data_exynos7 = { @@ -167,12 +170,15 @@ static const struct s3c2410_wdt_variant drv_data_exynos7 = { .mask_bit = 23, .rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET, .rst_stat_bit = 23, /* A57 WDTRESET */ - .quirks = QUIRK_HAS_PMU_CONFIG | QUIRK_HAS_RST_STAT, + .quirks = QUIRK_HAS_PMU_CONFIG | QUIRK_HAS_RST_STAT \ + | QUIRK_HAS_WTCLRINT_REG, }; static const struct of_device_id s3c2410_wdt_match[] = { { .compatible = "samsung,s3c2410-wdt", .data = &drv_data_s3c2410 }, + { .compatible = "samsung,s3c6410-wdt", + .data = &drv_data_s3c6410 }, { .compatible = "samsung,exynos5250-wdt", .data = &drv_data_exynos5250 }, { .compatible = "samsung,exynos5420-wdt", @@ -193,14 +199,6 @@ static const struct platform_device_id s3c2410_wdt_ids[] = { }; MODULE_DEVICE_TABLE(platform, s3c2410_wdt_ids); -/* watchdog control routines */ - -#define DBG(fmt, ...) \ -do { \ - if (debug) \ - pr_info(fmt, ##__VA_ARGS__); \ -} while (0) - /* functions */ static inline unsigned int s3c2410wdt_max_timeout(struct clk *clock) @@ -296,8 +294,8 @@ static int s3c2410wdt_start(struct watchdog_device *wdd) wtcon |= S3C2410_WTCON_RSTEN; } - DBG("%s: count=0x%08x, wtcon=%08lx\n", - __func__, wdt->count, wtcon); + dev_dbg(wdt->dev, "Starting watchdog: count=0x%08x, wtcon=%08lx\n", + wdt->count, wtcon); writel(wdt->count, wdt->reg_base + S3C2410_WTDAT); writel(wdt->count, wdt->reg_base + S3C2410_WTCNT); @@ -326,8 +324,8 @@ static int s3c2410wdt_set_heartbeat(struct watchdog_device *wdd, unsigned timeou freq = DIV_ROUND_UP(freq, 128); count = timeout * freq; - DBG("%s: count=%d, timeout=%d, freq=%lu\n", - __func__, count, timeout, freq); + dev_dbg(wdt->dev, "Heartbeat: count=%d, timeout=%d, freq=%lu\n", + count, timeout, freq); /* if the count is bigger than the watchdog register, then work out what we need to do (and if) we can @@ -343,8 +341,8 @@ static int s3c2410wdt_set_heartbeat(struct watchdog_device *wdd, unsigned timeou } } - DBG("%s: timeout=%d, divisor=%d, count=%d (%08x)\n", - __func__, timeout, divisor, count, DIV_ROUND_UP(count, divisor)); + dev_dbg(wdt->dev, "Heartbeat: timeout=%d, divisor=%d, count=%d (%08x)\n", + timeout, divisor, count, DIV_ROUND_UP(count, divisor)); count = DIV_ROUND_UP(count, divisor); wdt->count = count; @@ -394,7 +392,7 @@ static const struct watchdog_info s3c2410_wdt_ident = { .identity = "S3C2410 Watchdog", }; -static struct watchdog_ops s3c2410wdt_ops = { +static const struct watchdog_ops s3c2410wdt_ops = { .owner = THIS_MODULE, .start = s3c2410wdt_start, .stop = s3c2410wdt_stop, @@ -406,7 +404,7 @@ static struct watchdog_ops s3c2410wdt_ops = { static struct watchdog_device s3c2410_wdd = { .info = &s3c2410_wdt_ident, .ops = &s3c2410wdt_ops, - .timeout = CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME, + .timeout = S3C2410_WATCHDOG_DEFAULT_TIME, }; /* interrupt handler code */ @@ -418,6 +416,10 @@ static irqreturn_t s3c2410wdt_irq(int irqno, void *param) dev_info(wdt->dev, "watchdog timer expired (irq)\n"); s3c2410wdt_keepalive(&wdt->wdt_device); + + if (wdt->drv_data->quirks & QUIRK_HAS_WTCLRINT_REG) + writel(0x1, wdt->reg_base + S3C2410_WTCLRINT); + return IRQ_HANDLED; } @@ -505,9 +507,8 @@ static inline unsigned int s3c2410wdt_get_bootstatus(struct s3c2410_wdt *wdt) return 0; } -/* s3c2410_get_wdt_driver_data */ static inline struct s3c2410_wdt_variant * -get_wdt_drv_data(struct platform_device *pdev) +s3c2410_get_wdt_drv_data(struct platform_device *pdev) { if (pdev->dev.of_node) { const struct of_device_id *match; @@ -529,8 +530,6 @@ static int s3c2410wdt_probe(struct platform_device *pdev) int started = 0; int ret; - DBG("%s: probe=%p\n", __func__, pdev); - dev = &pdev->dev; wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL); @@ -541,7 +540,7 @@ static int s3c2410wdt_probe(struct platform_device *pdev) spin_lock_init(&wdt->lock); wdt->wdt_device = s3c2410_wdd; - wdt->drv_data = get_wdt_drv_data(pdev); + wdt->drv_data = s3c2410_get_wdt_drv_data(pdev); if (wdt->drv_data->quirks & QUIRKS_HAVE_PMUREG) { wdt->pmureg = syscon_regmap_lookup_by_phandle(dev->of_node, "samsung,syscon-phandle"); @@ -566,8 +565,6 @@ static int s3c2410wdt_probe(struct platform_device *pdev) goto err; } - DBG("probe: mapped reg_base=%p\n", wdt->reg_base); - wdt->clock = devm_clk_get(dev, "watchdog"); if (IS_ERR(wdt->clock)) { dev_err(dev, "failed to find watchdog clock source\n"); @@ -600,12 +597,12 @@ static int s3c2410wdt_probe(struct platform_device *pdev) wdt->wdt_device.timeout); if (ret) { started = s3c2410wdt_set_heartbeat(&wdt->wdt_device, - CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME); + S3C2410_WATCHDOG_DEFAULT_TIME); if (started == 0) dev_info(dev, "tmr_margin value out of range, default %d used\n", - CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME); + S3C2410_WATCHDOG_DEFAULT_TIME); else dev_info(dev, "default timer value is out of range, " "cannot start\n"); diff --git a/drivers/watchdog/sa1100_wdt.c b/drivers/watchdog/sa1100_wdt.c index 8965e3f536c3..d3be4f831db5 100644 --- a/drivers/watchdog/sa1100_wdt.c +++ b/drivers/watchdog/sa1100_wdt.c @@ -188,12 +188,14 @@ static int __init sa1100dog_init(void) pre_margin = oscr_freq * margin; ret = misc_register(&sa1100dog_miscdev); - if (ret == 0) + if (ret == 0) { pr_info("SA1100/PXA2xx Watchdog Timer: timer margin %d sec\n", margin); - return ret; -err: + return 0; + } + clk_disable_unprepare(clk); +err: clk_put(clk); return ret; } diff --git a/drivers/watchdog/sama5d4_wdt.c b/drivers/watchdog/sama5d4_wdt.c index a49634cdc1cc..f709962018ac 100644 --- a/drivers/watchdog/sama5d4_wdt.c +++ b/drivers/watchdog/sama5d4_wdt.c @@ -28,7 +28,7 @@ struct sama5d4_wdt { struct watchdog_device wdd; void __iomem *reg_base; - u32 config; + u32 mr; }; static int wdt_timeout = WDT_DEFAULT_TIMEOUT; @@ -53,11 +53,9 @@ MODULE_PARM_DESC(nowayout, 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); + wdt->mr &= ~AT91_WDT_WDDIS; + wdt_write(wdt, AT91_WDT_MR, wdt->mr); return 0; } @@ -65,11 +63,9 @@ static int sama5d4_wdt_start(struct watchdog_device *wdd) 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); + wdt->mr |= AT91_WDT_WDDIS; + wdt_write(wdt, AT91_WDT_MR, wdt->mr); return 0; } @@ -88,14 +84,12 @@ static int sama5d4_wdt_set_timeout(struct watchdog_device *wdd, { 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); + wdt->mr &= ~AT91_WDT_WDV; + wdt->mr &= ~AT91_WDT_WDD; + wdt->mr |= AT91_WDT_SET_WDV(value); + wdt->mr |= AT91_WDT_SET_WDD(value); + wdt_write(wdt, AT91_WDT_MR, wdt->mr); wdd->timeout = timeout; @@ -107,7 +101,7 @@ static const struct watchdog_info sama5d4_wdt_info = { .identity = "Atmel SAMA5D4 Watchdog", }; -static struct watchdog_ops sama5d4_wdt_ops = { +static const struct watchdog_ops sama5d4_wdt_ops = { .owner = THIS_MODULE, .start = sama5d4_wdt_start, .stop = sama5d4_wdt_stop, @@ -132,19 +126,19 @@ static int of_sama5d4_wdt_init(struct device_node *np, struct sama5d4_wdt *wdt) { const char *tmp; - wdt->config = AT91_WDT_WDDIS; + wdt->mr = AT91_WDT_WDDIS; if (!of_property_read_string(np, "atmel,watchdog-type", &tmp) && !strcmp(tmp, "software")) - wdt->config |= AT91_WDT_WDFIEN; + wdt->mr |= AT91_WDT_WDFIEN; else - wdt->config |= AT91_WDT_WDRSTEN; + wdt->mr |= AT91_WDT_WDRSTEN; if (of_property_read_bool(np, "atmel,idle-halt")) - wdt->config |= AT91_WDT_WDIDLEHLT; + wdt->mr |= AT91_WDT_WDIDLEHLT; if (of_property_read_bool(np, "atmel,dbg-halt")) - wdt->config |= AT91_WDT_WDDBGHLT; + wdt->mr |= AT91_WDT_WDDBGHLT; return 0; } @@ -163,11 +157,10 @@ static int sama5d4_wdt_init(struct sama5d4_wdt *wdt) 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->mr |= AT91_WDT_SET_WDD(value); + wdt->mr |= AT91_WDT_SET_WDV(value); - wdt_write(wdt, AT91_WDT_MR, reg); + wdt_write(wdt, AT91_WDT_MR, wdt->mr); return 0; } @@ -211,7 +204,7 @@ static int sama5d4_wdt_probe(struct platform_device *pdev) return ret; } - if ((wdt->config & AT91_WDT_WDFIEN) && irq) { + if ((wdt->mr & 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); @@ -265,11 +258,28 @@ static const struct of_device_id sama5d4_wdt_of_match[] = { }; MODULE_DEVICE_TABLE(of, sama5d4_wdt_of_match); +#ifdef CONFIG_PM_SLEEP +static int sama5d4_wdt_resume(struct device *dev) +{ + struct sama5d4_wdt *wdt = dev_get_drvdata(dev); + + wdt_write(wdt, AT91_WDT_MR, wdt->mr & ~AT91_WDT_WDDIS); + if (wdt->mr & AT91_WDT_WDDIS) + wdt_write(wdt, AT91_WDT_MR, wdt->mr); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(sama5d4_wdt_pm_ops, NULL, + sama5d4_wdt_resume); + static struct platform_driver sama5d4_wdt_driver = { .probe = sama5d4_wdt_probe, .remove = sama5d4_wdt_remove, .driver = { .name = "sama5d4_wdt", + .pm = &sama5d4_wdt_pm_ops, .of_match_table = sama5d4_wdt_of_match, } }; diff --git a/drivers/watchdog/sbsa_gwdt.c b/drivers/watchdog/sbsa_gwdt.c index ce0c38bd0f00..316c2eb122d2 100644 --- a/drivers/watchdog/sbsa_gwdt.c +++ b/drivers/watchdog/sbsa_gwdt.c @@ -207,7 +207,7 @@ static irqreturn_t sbsa_gwdt_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } -static struct watchdog_info sbsa_gwdt_info = { +static const struct watchdog_info sbsa_gwdt_info = { .identity = WATCHDOG_NAME, .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | @@ -215,7 +215,7 @@ static struct watchdog_info sbsa_gwdt_info = { WDIOF_CARDRESET, }; -static struct watchdog_ops sbsa_gwdt_ops = { +static const struct watchdog_ops sbsa_gwdt_ops = { .owner = THIS_MODULE, .start = sbsa_gwdt_start, .stop = sbsa_gwdt_stop, diff --git a/drivers/watchdog/sirfsoc_wdt.c b/drivers/watchdog/sirfsoc_wdt.c index 3050a0031479..4eea351e09b0 100644 --- a/drivers/watchdog/sirfsoc_wdt.c +++ b/drivers/watchdog/sirfsoc_wdt.c @@ -127,7 +127,7 @@ static const struct watchdog_info sirfsoc_wdt_ident = { .identity = "SiRFSOC Watchdog", }; -static struct watchdog_ops sirfsoc_wdt_ops = { +static const struct watchdog_ops sirfsoc_wdt_ops = { .owner = THIS_MODULE, .start = sirfsoc_wdt_enable, .stop = sirfsoc_wdt_disable, diff --git a/drivers/watchdog/softdog.c b/drivers/watchdog/softdog.c index c7bdc986dca1..7983029852ab 100644 --- a/drivers/watchdog/softdog.c +++ b/drivers/watchdog/softdog.c @@ -87,11 +87,13 @@ static int softdog_ping(struct watchdog_device *w) if (!mod_timer(&softdog_ticktock, jiffies + (w->timeout * HZ))) __module_get(THIS_MODULE); - if (w->pretimeout) - mod_timer(&softdog_preticktock, jiffies + - (w->timeout - w->pretimeout) * HZ); - else - del_timer(&softdog_preticktock); + if (IS_ENABLED(CONFIG_SOFT_WATCHDOG_PRETIMEOUT)) { + if (w->pretimeout) + mod_timer(&softdog_preticktock, jiffies + + (w->timeout - w->pretimeout) * HZ); + else + del_timer(&softdog_preticktock); + } return 0; } @@ -101,15 +103,15 @@ static int softdog_stop(struct watchdog_device *w) if (del_timer(&softdog_ticktock)) module_put(THIS_MODULE); - del_timer(&softdog_preticktock); + if (IS_ENABLED(CONFIG_SOFT_WATCHDOG_PRETIMEOUT)) + del_timer(&softdog_preticktock); return 0; } static struct watchdog_info softdog_info = { .identity = "Software Watchdog", - .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE | - WDIOF_PRETIMEOUT, + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, }; static const struct watchdog_ops softdog_ops = { @@ -134,6 +136,9 @@ static int __init softdog_init(void) watchdog_set_nowayout(&softdog_dev, nowayout); watchdog_stop_on_reboot(&softdog_dev); + if (IS_ENABLED(CONFIG_SOFT_WATCHDOG_PRETIMEOUT)) + softdog_info.options |= WDIOF_PRETIMEOUT; + ret = watchdog_register_device(&softdog_dev); if (ret) return ret; diff --git a/drivers/watchdog/sun4v_wdt.c b/drivers/watchdog/sun4v_wdt.c index 1467fe50a76f..00907973608c 100644 --- a/drivers/watchdog/sun4v_wdt.c +++ b/drivers/watchdog/sun4v_wdt.c @@ -77,7 +77,7 @@ static const struct watchdog_info sun4v_wdt_ident = { .firmware_version = 0, }; -static struct watchdog_ops sun4v_wdt_ops = { +static const struct watchdog_ops sun4v_wdt_ops = { .owner = THIS_MODULE, .start = sun4v_wdt_ping, .stop = sun4v_wdt_stop, diff --git a/drivers/watchdog/sunxi_wdt.c b/drivers/watchdog/sunxi_wdt.c index 953bb7b7446f..9728fa32c357 100644 --- a/drivers/watchdog/sunxi_wdt.c +++ b/drivers/watchdog/sunxi_wdt.c @@ -242,8 +242,6 @@ static int sunxi_wdt_probe(struct platform_device *pdev) if (!sunxi_wdt) return -EINVAL; - platform_set_drvdata(pdev, sunxi_wdt); - device = of_match_device(sunxi_wdt_dt_ids, &pdev->dev); if (!device) return -ENODEV; @@ -270,7 +268,8 @@ static int sunxi_wdt_probe(struct platform_device *pdev) sunxi_wdt_stop(&sunxi_wdt->wdt_dev); - err = watchdog_register_device(&sunxi_wdt->wdt_dev); + watchdog_stop_on_reboot(&sunxi_wdt->wdt_dev); + err = devm_watchdog_register_device(&pdev->dev, &sunxi_wdt->wdt_dev); if (unlikely(err)) return err; @@ -280,27 +279,8 @@ static int sunxi_wdt_probe(struct platform_device *pdev) return 0; } -static int sunxi_wdt_remove(struct platform_device *pdev) -{ - struct sunxi_wdt_dev *sunxi_wdt = platform_get_drvdata(pdev); - - watchdog_unregister_device(&sunxi_wdt->wdt_dev); - watchdog_set_drvdata(&sunxi_wdt->wdt_dev, NULL); - - return 0; -} - -static void sunxi_wdt_shutdown(struct platform_device *pdev) -{ - struct sunxi_wdt_dev *sunxi_wdt = platform_get_drvdata(pdev); - - sunxi_wdt_stop(&sunxi_wdt->wdt_dev); -} - static struct platform_driver sunxi_wdt_driver = { .probe = sunxi_wdt_probe, - .remove = sunxi_wdt_remove, - .shutdown = sunxi_wdt_shutdown, .driver = { .name = DRV_NAME, .of_match_table = sunxi_wdt_dt_ids, diff --git a/drivers/watchdog/tangox_wdt.c b/drivers/watchdog/tangox_wdt.c index 202c4b9cc921..d5fcce062920 100644 --- a/drivers/watchdog/tangox_wdt.c +++ b/drivers/watchdog/tangox_wdt.c @@ -15,9 +15,7 @@ #include #include #include -#include #include -#include #include #define DEFAULT_TIMEOUT 30 @@ -47,7 +45,6 @@ struct tangox_wdt_device { void __iomem *base; unsigned long clk_rate; struct clk *clk; - struct notifier_block restart; }; static int tangox_wdt_set_timeout(struct watchdog_device *wdt, @@ -96,24 +93,24 @@ static const struct watchdog_info tangox_wdt_info = { .identity = "tangox watchdog", }; +static int tangox_wdt_restart(struct watchdog_device *wdt, + unsigned long action, void *data) +{ + struct tangox_wdt_device *dev = watchdog_get_drvdata(wdt); + + writel(1, dev->base + WD_COUNTER); + + return 0; +} + static const struct watchdog_ops tangox_wdt_ops = { .start = tangox_wdt_start, .stop = tangox_wdt_stop, .set_timeout = tangox_wdt_set_timeout, .get_timeleft = tangox_wdt_get_timeleft, + .restart = tangox_wdt_restart, }; -static int tangox_wdt_restart(struct notifier_block *nb, unsigned long action, - void *data) -{ - struct tangox_wdt_device *dev = - container_of(nb, struct tangox_wdt_device, restart); - - writel(1, dev->base + WD_COUNTER); - - return NOTIFY_DONE; -} - static int tangox_wdt_probe(struct platform_device *pdev) { struct tangox_wdt_device *dev; @@ -174,18 +171,14 @@ static int tangox_wdt_probe(struct platform_device *pdev) tangox_wdt_start(&dev->wdt); } + watchdog_set_restart_priority(&dev->wdt, 128); + err = watchdog_register_device(&dev->wdt); if (err) goto err; platform_set_drvdata(pdev, dev); - dev->restart.notifier_call = tangox_wdt_restart; - dev->restart.priority = 128; - err = register_restart_handler(&dev->restart); - if (err) - dev_warn(&pdev->dev, "failed to register restart handler\n"); - dev_info(&pdev->dev, "SMP86xx/SMP87xx watchdog registered\n"); return 0; @@ -202,7 +195,6 @@ static int tangox_wdt_remove(struct platform_device *pdev) tangox_wdt_stop(&dev->wdt); clk_disable_unprepare(dev->clk); - unregister_restart_handler(&dev->restart); watchdog_unregister_device(&dev->wdt); return 0; diff --git a/drivers/watchdog/tegra_wdt.c b/drivers/watchdog/tegra_wdt.c index 2d53c3f9394f..9403c08816e3 100644 --- a/drivers/watchdog/tegra_wdt.c +++ b/drivers/watchdog/tegra_wdt.c @@ -226,7 +226,7 @@ static int tegra_wdt_probe(struct platform_device *pdev) watchdog_set_nowayout(wdd, nowayout); - ret = watchdog_register_device(wdd); + ret = devm_watchdog_register_device(&pdev->dev, wdd); if (ret) { dev_err(&pdev->dev, "failed to register watchdog device\n"); @@ -248,8 +248,6 @@ static int tegra_wdt_remove(struct platform_device *pdev) tegra_wdt_stop(&wdt->wdd); - watchdog_unregister_device(&wdt->wdd); - dev_info(&pdev->dev, "removed wdt\n"); return 0; diff --git a/drivers/watchdog/ts72xx_wdt.c b/drivers/watchdog/ts72xx_wdt.c index 4b541934b6c5..17c25daebcce 100644 --- a/drivers/watchdog/ts72xx_wdt.c +++ b/drivers/watchdog/ts72xx_wdt.c @@ -13,428 +13,159 @@ * warranty of any kind, whether express or implied. */ -#include -#include -#include -#include -#include -#include #include -#include +#include #include -#include +#include -#define TS72XX_WDT_FEED_VAL 0x05 -#define TS72XX_WDT_DEFAULT_TIMEOUT 8 +#define TS72XX_WDT_DEFAULT_TIMEOUT 30 -static int timeout = TS72XX_WDT_DEFAULT_TIMEOUT; +static int timeout; module_param(timeout, int, 0); -MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds. " - "(1 <= timeout <= 8, default=" - __MODULE_STRING(TS72XX_WDT_DEFAULT_TIMEOUT) - ")"); +MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds."); static bool nowayout = WATCHDOG_NOWAYOUT; module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Disable watchdog shutdown on close"); -/** - * struct ts72xx_wdt - watchdog control structure - * @lock: lock that protects this structure - * @regval: watchdog timeout value suitable for control register - * @flags: flags controlling watchdog device state - * @control_reg: watchdog control register - * @feed_reg: watchdog feed register - * @pdev: back pointer to platform dev - */ -struct ts72xx_wdt { - struct mutex lock; - int regval; +/* priv->control_reg */ +#define TS72XX_WDT_CTRL_DISABLE 0x00 +#define TS72XX_WDT_CTRL_250MS 0x01 +#define TS72XX_WDT_CTRL_500MS 0x02 +#define TS72XX_WDT_CTRL_1SEC 0x03 +#define TS72XX_WDT_CTRL_RESERVED 0x04 +#define TS72XX_WDT_CTRL_2SEC 0x05 +#define TS72XX_WDT_CTRL_4SEC 0x06 +#define TS72XX_WDT_CTRL_8SEC 0x07 -#define TS72XX_WDT_BUSY_FLAG 1 -#define TS72XX_WDT_EXPECT_CLOSE_FLAG 2 - int flags; +/* priv->feed_reg */ +#define TS72XX_WDT_FEED_VAL 0x05 +struct ts72xx_wdt_priv { void __iomem *control_reg; void __iomem *feed_reg; - - struct platform_device *pdev; + struct watchdog_device wdd; + unsigned char regval; }; -static struct platform_device *ts72xx_wdt_pdev; - -/* - * TS-72xx Watchdog supports following timeouts (value written - * to control register): - * value description - * ------------------------- - * 0x00 watchdog disabled - * 0x01 250ms - * 0x02 500ms - * 0x03 1s - * 0x04 reserved - * 0x05 2s - * 0x06 4s - * 0x07 8s - * - * Timeouts below 1s are not very usable so we don't - * allow them at all. - * - * We provide two functions that convert between these: - * timeout_to_regval() and regval_to_timeout(). - */ -static const struct { - int timeout; - int regval; -} ts72xx_wdt_map[] = { - { 1, 3 }, - { 2, 5 }, - { 4, 6 }, - { 8, 7 }, -}; - -/** - * timeout_to_regval() - converts given timeout to control register value - * @new_timeout: timeout in seconds to be converted - * - * Function converts given @new_timeout into valid value that can - * be programmed into watchdog control register. When conversion is - * not possible, function returns %-EINVAL. - */ -static int timeout_to_regval(int new_timeout) +static int ts72xx_wdt_start(struct watchdog_device *wdd) { - int i; + struct ts72xx_wdt_priv *priv = watchdog_get_drvdata(wdd); - /* first limit it to 1 - 8 seconds */ - new_timeout = clamp_val(new_timeout, 1, 8); + writeb(TS72XX_WDT_FEED_VAL, priv->feed_reg); + writeb(priv->regval, priv->control_reg); - for (i = 0; i < ARRAY_SIZE(ts72xx_wdt_map); i++) { - if (ts72xx_wdt_map[i].timeout >= new_timeout) - return ts72xx_wdt_map[i].regval; - } - - return -EINVAL; -} - -/** - * regval_to_timeout() - converts control register value to timeout - * @regval: control register value to be converted - * - * Function converts given @regval to timeout in seconds (1, 2, 4 or 8). - * If @regval cannot be converted, function returns %-EINVAL. - */ -static int regval_to_timeout(int regval) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(ts72xx_wdt_map); i++) { - if (ts72xx_wdt_map[i].regval == regval) - return ts72xx_wdt_map[i].timeout; - } - - return -EINVAL; -} - -/** - * ts72xx_wdt_kick() - kick the watchdog - * @wdt: watchdog to be kicked - * - * Called with @wdt->lock held. - */ -static inline void ts72xx_wdt_kick(struct ts72xx_wdt *wdt) -{ - __raw_writeb(TS72XX_WDT_FEED_VAL, wdt->feed_reg); -} - -/** - * ts72xx_wdt_start() - starts the watchdog timer - * @wdt: watchdog to be started - * - * This function programs timeout to watchdog timer - * and starts it. - * - * Called with @wdt->lock held. - */ -static void ts72xx_wdt_start(struct ts72xx_wdt *wdt) -{ - /* - * To program the wdt, it first must be "fed" and - * only after that (within 30 usecs) the configuration - * can be changed. - */ - ts72xx_wdt_kick(wdt); - __raw_writeb((u8)wdt->regval, wdt->control_reg); -} - -/** - * ts72xx_wdt_stop() - stops the watchdog timer - * @wdt: watchdog to be stopped - * - * Called with @wdt->lock held. - */ -static void ts72xx_wdt_stop(struct ts72xx_wdt *wdt) -{ - ts72xx_wdt_kick(wdt); - __raw_writeb(0, wdt->control_reg); -} - -static int ts72xx_wdt_open(struct inode *inode, struct file *file) -{ - struct ts72xx_wdt *wdt = platform_get_drvdata(ts72xx_wdt_pdev); - int regval; - - /* - * Try to convert default timeout to valid register - * value first. - */ - regval = timeout_to_regval(timeout); - if (regval < 0) { - dev_err(&wdt->pdev->dev, - "failed to convert timeout (%d) to register value\n", - timeout); - return regval; - } - - if (mutex_lock_interruptible(&wdt->lock)) - return -ERESTARTSYS; - - if ((wdt->flags & TS72XX_WDT_BUSY_FLAG) != 0) { - mutex_unlock(&wdt->lock); - return -EBUSY; - } - - wdt->flags = TS72XX_WDT_BUSY_FLAG; - wdt->regval = regval; - file->private_data = wdt; - - ts72xx_wdt_start(wdt); - - mutex_unlock(&wdt->lock); - return nonseekable_open(inode, file); -} - -static int ts72xx_wdt_release(struct inode *inode, struct file *file) -{ - struct ts72xx_wdt *wdt = file->private_data; - - if (mutex_lock_interruptible(&wdt->lock)) - return -ERESTARTSYS; - - if ((wdt->flags & TS72XX_WDT_EXPECT_CLOSE_FLAG) != 0) { - ts72xx_wdt_stop(wdt); - } else { - dev_warn(&wdt->pdev->dev, - "TS-72XX WDT device closed unexpectly. " - "Watchdog timer will not stop!\n"); - /* - * Kick it one more time, to give userland some time - * to recover (for example, respawning the kicker - * daemon). - */ - ts72xx_wdt_kick(wdt); - } - - wdt->flags = 0; - - mutex_unlock(&wdt->lock); return 0; } -static ssize_t ts72xx_wdt_write(struct file *file, - const char __user *data, - size_t len, - loff_t *ppos) +static int ts72xx_wdt_stop(struct watchdog_device *wdd) { - struct ts72xx_wdt *wdt = file->private_data; + struct ts72xx_wdt_priv *priv = watchdog_get_drvdata(wdd); - if (!len) - return 0; + writeb(TS72XX_WDT_FEED_VAL, priv->feed_reg); + writeb(TS72XX_WDT_CTRL_DISABLE, priv->control_reg); - if (mutex_lock_interruptible(&wdt->lock)) - return -ERESTARTSYS; - - ts72xx_wdt_kick(wdt); - - /* - * Support for magic character closing. User process - * writes 'V' into the device, just before it is closed. - * This means that we know that the wdt timer can be - * stopped after user closes the device. - */ - if (!nowayout) { - int i; - - for (i = 0; i < len; i++) { - char c; - - /* In case it was set long ago */ - wdt->flags &= ~TS72XX_WDT_EXPECT_CLOSE_FLAG; - - if (get_user(c, data + i)) { - mutex_unlock(&wdt->lock); - return -EFAULT; - } - if (c == 'V') { - wdt->flags |= TS72XX_WDT_EXPECT_CLOSE_FLAG; - break; - } - } - } - - mutex_unlock(&wdt->lock); - return len; + return 0; } -static const struct watchdog_info winfo = { - .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | +static int ts72xx_wdt_ping(struct watchdog_device *wdd) +{ + struct ts72xx_wdt_priv *priv = watchdog_get_drvdata(wdd); + + writeb(TS72XX_WDT_FEED_VAL, priv->feed_reg); + + return 0; +} + +static int ts72xx_wdt_settimeout(struct watchdog_device *wdd, unsigned int to) +{ + struct ts72xx_wdt_priv *priv = watchdog_get_drvdata(wdd); + + if (to == 1) { + priv->regval = TS72XX_WDT_CTRL_1SEC; + } else if (to == 2) { + priv->regval = TS72XX_WDT_CTRL_2SEC; + } else if (to <= 4) { + priv->regval = TS72XX_WDT_CTRL_4SEC; + to = 4; + } else { + priv->regval = TS72XX_WDT_CTRL_8SEC; + if (to <= 8) + to = 8; + } + + wdd->timeout = to; + + if (watchdog_active(wdd)) { + ts72xx_wdt_stop(wdd); + ts72xx_wdt_start(wdd); + } + + return 0; +} + +static const struct watchdog_info ts72xx_wdt_ident = { + .options = WDIOF_KEEPALIVEPING | + WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE, .firmware_version = 1, .identity = "TS-72XX WDT", }; -static long ts72xx_wdt_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) -{ - struct ts72xx_wdt *wdt = file->private_data; - void __user *argp = (void __user *)arg; - int __user *p = (int __user *)argp; - int error = 0; - - if (mutex_lock_interruptible(&wdt->lock)) - return -ERESTARTSYS; - - switch (cmd) { - case WDIOC_GETSUPPORT: - if (copy_to_user(argp, &winfo, sizeof(winfo))) - error = -EFAULT; - break; - - case WDIOC_GETSTATUS: - case WDIOC_GETBOOTSTATUS: - error = put_user(0, p); - break; - - case WDIOC_KEEPALIVE: - ts72xx_wdt_kick(wdt); - break; - - case WDIOC_SETOPTIONS: { - int options; - - error = get_user(options, p); - if (error) - break; - - error = -EINVAL; - - if ((options & WDIOS_DISABLECARD) != 0) { - ts72xx_wdt_stop(wdt); - error = 0; - } - if ((options & WDIOS_ENABLECARD) != 0) { - ts72xx_wdt_start(wdt); - error = 0; - } - - break; - } - - case WDIOC_SETTIMEOUT: { - int new_timeout; - int regval; - - error = get_user(new_timeout, p); - if (error) - break; - - regval = timeout_to_regval(new_timeout); - if (regval < 0) { - error = regval; - break; - } - ts72xx_wdt_stop(wdt); - wdt->regval = regval; - ts72xx_wdt_start(wdt); - - /*FALLTHROUGH*/ - } - - case WDIOC_GETTIMEOUT: - error = put_user(regval_to_timeout(wdt->regval), p); - break; - - default: - error = -ENOTTY; - break; - } - - mutex_unlock(&wdt->lock); - return error; -} - -static const struct file_operations ts72xx_wdt_fops = { +static struct watchdog_ops ts72xx_wdt_ops = { .owner = THIS_MODULE, - .llseek = no_llseek, - .open = ts72xx_wdt_open, - .release = ts72xx_wdt_release, - .write = ts72xx_wdt_write, - .unlocked_ioctl = ts72xx_wdt_ioctl, -}; - -static struct miscdevice ts72xx_wdt_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &ts72xx_wdt_fops, + .start = ts72xx_wdt_start, + .stop = ts72xx_wdt_stop, + .ping = ts72xx_wdt_ping, + .set_timeout = ts72xx_wdt_settimeout, }; static int ts72xx_wdt_probe(struct platform_device *pdev) { - struct ts72xx_wdt *wdt; - struct resource *r1, *r2; - int error = 0; + struct ts72xx_wdt_priv *priv; + struct watchdog_device *wdd; + struct resource *res; + int ret; - wdt = devm_kzalloc(&pdev->dev, sizeof(struct ts72xx_wdt), GFP_KERNEL); - if (!wdt) + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) return -ENOMEM; - r1 = platform_get_resource(pdev, IORESOURCE_MEM, 0); - wdt->control_reg = devm_ioremap_resource(&pdev->dev, r1); - if (IS_ERR(wdt->control_reg)) - return PTR_ERR(wdt->control_reg); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->control_reg = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(priv->control_reg)) + return PTR_ERR(priv->control_reg); - r2 = platform_get_resource(pdev, IORESOURCE_MEM, 1); - wdt->feed_reg = devm_ioremap_resource(&pdev->dev, r2); - if (IS_ERR(wdt->feed_reg)) - return PTR_ERR(wdt->feed_reg); + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + priv->feed_reg = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(priv->feed_reg)) + return PTR_ERR(priv->feed_reg); - platform_set_drvdata(pdev, wdt); - ts72xx_wdt_pdev = pdev; - wdt->pdev = pdev; - mutex_init(&wdt->lock); + wdd = &priv->wdd; + wdd->info = &ts72xx_wdt_ident; + wdd->ops = &ts72xx_wdt_ops; + wdd->min_timeout = 1; + wdd->max_hw_heartbeat_ms = 8000; + wdd->parent = &pdev->dev; - /* make sure that the watchdog is disabled */ - ts72xx_wdt_stop(wdt); + watchdog_set_nowayout(wdd, nowayout); - error = misc_register(&ts72xx_wdt_miscdev); - if (error) { - dev_err(&pdev->dev, "failed to register miscdev\n"); - return error; - } + wdd->timeout = TS72XX_WDT_DEFAULT_TIMEOUT; + watchdog_init_timeout(wdd, timeout, &pdev->dev); + + watchdog_set_drvdata(wdd, priv); + + ret = devm_watchdog_register_device(&pdev->dev, wdd); + if (ret) + return ret; dev_info(&pdev->dev, "TS-72xx Watchdog driver\n"); return 0; } -static int ts72xx_wdt_remove(struct platform_device *pdev) -{ - misc_deregister(&ts72xx_wdt_miscdev); - return 0; -} - static struct platform_driver ts72xx_wdt_driver = { .probe = ts72xx_wdt_probe, - .remove = ts72xx_wdt_remove, .driver = { .name = "ts72xx-wdt", }, diff --git a/drivers/watchdog/w83627hf_wdt.c b/drivers/watchdog/w83627hf_wdt.c index ef2ecaf53a14..98fd186c6878 100644 --- a/drivers/watchdog/w83627hf_wdt.c +++ b/drivers/watchdog/w83627hf_wdt.c @@ -297,7 +297,7 @@ static unsigned int wdt_get_time(struct watchdog_device *wdog) * Kernel Interfaces */ -static struct watchdog_info wdt_info = { +static const struct watchdog_info wdt_info = { .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, .identity = "W83627HF Watchdog", }; diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c index 32930a073a12..d5d2bbd8f428 100644 --- a/drivers/watchdog/watchdog_dev.c +++ b/drivers/watchdog/watchdog_dev.c @@ -987,6 +987,11 @@ static void watchdog_cdev_unregister(struct watchdog_device *wdd) wdd->wd_data = NULL; mutex_unlock(&wd_data->lock); + if (watchdog_active(wdd) && + test_bit(WDOG_STOP_ON_UNREGISTER, &wdd->status)) { + watchdog_stop(wdd); + } + cancel_delayed_work_sync(&wd_data->work); kref_put(&wd_data->kref, watchdog_core_data_release); diff --git a/drivers/watchdog/wm831x_wdt.c b/drivers/watchdog/wm831x_wdt.c index 8d1184aee932..1ddc1f742cd4 100644 --- a/drivers/watchdog/wm831x_wdt.c +++ b/drivers/watchdog/wm831x_wdt.c @@ -194,7 +194,7 @@ static int wm831x_wdt_probe(struct platform_device *pdev) if (ret < 0) { dev_err(wm831x->dev, "Failed to read watchdog status: %d\n", ret); - goto err; + return ret; } reg = ret; @@ -203,10 +203,8 @@ static int wm831x_wdt_probe(struct platform_device *pdev) driver_data = devm_kzalloc(&pdev->dev, sizeof(*driver_data), GFP_KERNEL); - if (!driver_data) { - ret = -ENOMEM; - goto err; - } + if (!driver_data) + return -ENOMEM; mutex_init(&driver_data->lock); driver_data->wm831x = wm831x; @@ -253,7 +251,7 @@ static int wm831x_wdt_probe(struct platform_device *pdev) dev_err(wm831x->dev, "Failed to request update GPIO: %d\n", ret); - goto err; + return ret; } driver_data->update_gpio = pdata->update_gpio; @@ -269,37 +267,22 @@ static int wm831x_wdt_probe(struct platform_device *pdev) } else { dev_err(wm831x->dev, "Failed to unlock security key: %d\n", ret); - goto err; + return ret; } } - ret = watchdog_register_device(&driver_data->wdt); + ret = devm_watchdog_register_device(&pdev->dev, &driver_data->wdt); if (ret != 0) { dev_err(wm831x->dev, "watchdog_register_device() failed: %d\n", ret); - goto err; + return ret; } - platform_set_drvdata(pdev, driver_data); - - return 0; - -err: - return ret; -} - -static int wm831x_wdt_remove(struct platform_device *pdev) -{ - struct wm831x_wdt_drvdata *driver_data = platform_get_drvdata(pdev); - - watchdog_unregister_device(&driver_data->wdt); - return 0; } static struct platform_driver wm831x_wdt_driver = { .probe = wm831x_wdt_probe, - .remove = wm831x_wdt_remove, .driver = { .name = "wm831x-watchdog", }, diff --git a/drivers/watchdog/zx2967_wdt.c b/drivers/watchdog/zx2967_wdt.c new file mode 100644 index 000000000000..e290d5a13a6d --- /dev/null +++ b/drivers/watchdog/zx2967_wdt.c @@ -0,0 +1,291 @@ +/* + * watchdog driver for ZTE's zx2967 family + * + * Copyright (C) 2017 ZTE Ltd. + * + * Author: Baoyou Xie + * + * License terms: GNU General Public License (GPL) version 2 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ZX2967_WDT_CFG_REG 0x4 +#define ZX2967_WDT_LOAD_REG 0x8 +#define ZX2967_WDT_REFRESH_REG 0x18 +#define ZX2967_WDT_START_REG 0x1c + +#define ZX2967_WDT_REFRESH_MASK GENMASK(5, 0) + +#define ZX2967_WDT_CFG_DIV(n) ((((n) & 0xff) - 1) << 8) +#define ZX2967_WDT_START_EN 0x1 + +/* + * Hardware magic number. + * When watchdog reg is written, the lowest 16 bits are valid, but + * the highest 16 bits should be always this number. + */ +#define ZX2967_WDT_WRITEKEY (0x1234 << 16) +#define ZX2967_WDT_VAL_MASK GENMASK(15, 0) + +#define ZX2967_WDT_DIV_DEFAULT 16 +#define ZX2967_WDT_DEFAULT_TIMEOUT 32 +#define ZX2967_WDT_MIN_TIMEOUT 1 +#define ZX2967_WDT_MAX_TIMEOUT 524 +#define ZX2967_WDT_MAX_COUNT 0xffff + +#define ZX2967_WDT_CLK_FREQ 0x8000 + +#define ZX2967_WDT_FLAG_REBOOT_MON BIT(0) + +struct zx2967_wdt { + struct watchdog_device wdt_device; + void __iomem *reg_base; + struct clk *clock; +}; + +static inline u32 zx2967_wdt_readl(struct zx2967_wdt *wdt, u16 reg) +{ + return readl_relaxed(wdt->reg_base + reg); +} + +static inline void zx2967_wdt_writel(struct zx2967_wdt *wdt, u16 reg, u32 val) +{ + writel_relaxed(val | ZX2967_WDT_WRITEKEY, wdt->reg_base + reg); +} + +static void zx2967_wdt_refresh(struct zx2967_wdt *wdt) +{ + u32 val; + + val = zx2967_wdt_readl(wdt, ZX2967_WDT_REFRESH_REG); + /* + * Bit 4-5, 1 and 2: refresh config info + * Bit 2-3, 1 and 2: refresh counter + * Bit 0-1, 1 and 2: refresh int-value + * we shift each group value between 1 and 2 to refresh all data. + */ + val ^= ZX2967_WDT_REFRESH_MASK; + zx2967_wdt_writel(wdt, ZX2967_WDT_REFRESH_REG, + val & ZX2967_WDT_VAL_MASK); +} + +static int +zx2967_wdt_set_timeout(struct watchdog_device *wdd, unsigned int timeout) +{ + struct zx2967_wdt *wdt = watchdog_get_drvdata(wdd); + unsigned int divisor = ZX2967_WDT_DIV_DEFAULT; + u32 count; + + count = timeout * ZX2967_WDT_CLK_FREQ; + if (count > divisor * ZX2967_WDT_MAX_COUNT) + divisor = DIV_ROUND_UP(count, ZX2967_WDT_MAX_COUNT); + count = DIV_ROUND_UP(count, divisor); + zx2967_wdt_writel(wdt, ZX2967_WDT_CFG_REG, + ZX2967_WDT_CFG_DIV(divisor) & ZX2967_WDT_VAL_MASK); + zx2967_wdt_writel(wdt, ZX2967_WDT_LOAD_REG, + count & ZX2967_WDT_VAL_MASK); + zx2967_wdt_refresh(wdt); + wdd->timeout = (count * divisor) / ZX2967_WDT_CLK_FREQ; + + return 0; +} + +static void __zx2967_wdt_start(struct zx2967_wdt *wdt) +{ + u32 val; + + val = zx2967_wdt_readl(wdt, ZX2967_WDT_START_REG); + val |= ZX2967_WDT_START_EN; + zx2967_wdt_writel(wdt, ZX2967_WDT_START_REG, + val & ZX2967_WDT_VAL_MASK); +} + +static void __zx2967_wdt_stop(struct zx2967_wdt *wdt) +{ + u32 val; + + val = zx2967_wdt_readl(wdt, ZX2967_WDT_START_REG); + val &= ~ZX2967_WDT_START_EN; + zx2967_wdt_writel(wdt, ZX2967_WDT_START_REG, + val & ZX2967_WDT_VAL_MASK); +} + +static int zx2967_wdt_start(struct watchdog_device *wdd) +{ + struct zx2967_wdt *wdt = watchdog_get_drvdata(wdd); + + zx2967_wdt_set_timeout(wdd, wdd->timeout); + __zx2967_wdt_start(wdt); + + return 0; +} + +static int zx2967_wdt_stop(struct watchdog_device *wdd) +{ + struct zx2967_wdt *wdt = watchdog_get_drvdata(wdd); + + __zx2967_wdt_stop(wdt); + + return 0; +} + +static int zx2967_wdt_keepalive(struct watchdog_device *wdd) +{ + struct zx2967_wdt *wdt = watchdog_get_drvdata(wdd); + + zx2967_wdt_refresh(wdt); + + return 0; +} + +#define ZX2967_WDT_OPTIONS \ + (WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE) +static const struct watchdog_info zx2967_wdt_ident = { + .options = ZX2967_WDT_OPTIONS, + .identity = "zx2967 watchdog", +}; + +static struct watchdog_ops zx2967_wdt_ops = { + .owner = THIS_MODULE, + .start = zx2967_wdt_start, + .stop = zx2967_wdt_stop, + .ping = zx2967_wdt_keepalive, + .set_timeout = zx2967_wdt_set_timeout, +}; + +static void zx2967_wdt_reset_sysctrl(struct device *dev) +{ + int ret; + void __iomem *regmap; + unsigned int offset, mask, config; + struct of_phandle_args out_args; + + ret = of_parse_phandle_with_fixed_args(dev->of_node, + "zte,wdt-reset-sysctrl", 3, 0, &out_args); + if (ret) + return; + + offset = out_args.args[0]; + config = out_args.args[1]; + mask = out_args.args[2]; + + regmap = syscon_node_to_regmap(out_args.np); + if (IS_ERR(regmap)) { + of_node_put(out_args.np); + return; + } + + regmap_update_bits(regmap, offset, mask, config); + of_node_put(out_args.np); +} + +static int zx2967_wdt_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct zx2967_wdt *wdt; + struct resource *base; + int ret; + struct reset_control *rstc; + + wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL); + if (!wdt) + return -ENOMEM; + + platform_set_drvdata(pdev, wdt); + + wdt->wdt_device.info = &zx2967_wdt_ident; + wdt->wdt_device.ops = &zx2967_wdt_ops; + wdt->wdt_device.timeout = ZX2967_WDT_DEFAULT_TIMEOUT; + wdt->wdt_device.max_timeout = ZX2967_WDT_MAX_TIMEOUT; + wdt->wdt_device.min_timeout = ZX2967_WDT_MIN_TIMEOUT; + wdt->wdt_device.parent = &pdev->dev; + + base = platform_get_resource(pdev, IORESOURCE_MEM, 0); + wdt->reg_base = devm_ioremap_resource(dev, base); + if (IS_ERR(wdt->reg_base)) { + dev_err(dev, "ioremap failed\n"); + return PTR_ERR(wdt->reg_base); + } + + zx2967_wdt_reset_sysctrl(dev); + + wdt->clock = devm_clk_get(dev, NULL); + if (IS_ERR(wdt->clock)) { + dev_err(dev, "failed to find watchdog clock source\n"); + return PTR_ERR(wdt->clock); + } + + ret = clk_prepare_enable(wdt->clock); + if (ret < 0) { + dev_err(dev, "failed to enable clock\n"); + return ret; + } + clk_set_rate(wdt->clock, ZX2967_WDT_CLK_FREQ); + + rstc = devm_reset_control_get(dev, NULL); + if (IS_ERR(rstc)) { + dev_err(dev, "failed to get rstc"); + ret = PTR_ERR(rstc); + goto err; + } + + reset_control_assert(rstc); + reset_control_deassert(rstc); + + watchdog_set_drvdata(&wdt->wdt_device, wdt); + watchdog_init_timeout(&wdt->wdt_device, + ZX2967_WDT_DEFAULT_TIMEOUT, dev); + watchdog_set_nowayout(&wdt->wdt_device, WATCHDOG_NOWAYOUT); + + ret = watchdog_register_device(&wdt->wdt_device); + if (ret) + goto err; + + dev_info(dev, "watchdog enabled (timeout=%d sec, nowayout=%d)", + wdt->wdt_device.timeout, WATCHDOG_NOWAYOUT); + + return 0; + +err: + clk_disable_unprepare(wdt->clock); + return ret; +} + +static int zx2967_wdt_remove(struct platform_device *pdev) +{ + struct zx2967_wdt *wdt = platform_get_drvdata(pdev); + + watchdog_unregister_device(&wdt->wdt_device); + clk_disable_unprepare(wdt->clock); + + return 0; +} + +static const struct of_device_id zx2967_wdt_match[] = { + { .compatible = "zte,zx296718-wdt", }, + {} +}; +MODULE_DEVICE_TABLE(of, zx2967_wdt_match); + +static struct platform_driver zx2967_wdt_driver = { + .probe = zx2967_wdt_probe, + .remove = zx2967_wdt_remove, + .driver = { + .name = "zx2967-wdt", + .of_match_table = of_match_ptr(zx2967_wdt_match), + }, +}; +module_platform_driver(zx2967_wdt_driver); + +MODULE_AUTHOR("Baoyou Xie "); +MODULE_DESCRIPTION("ZTE zx2967 Watchdog Device Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/watchdog.h b/include/linux/watchdog.h index 35a4d8185b51..a786e5e8973b 100644 --- a/include/linux/watchdog.h +++ b/include/linux/watchdog.h @@ -117,6 +117,7 @@ struct watchdog_device { #define WDOG_NO_WAY_OUT 1 /* Is 'nowayout' feature set ? */ #define WDOG_STOP_ON_REBOOT 2 /* Should be stopped on reboot */ #define WDOG_HW_RUNNING 3 /* True if HW watchdog running */ +#define WDOG_STOP_ON_UNREGISTER 4 /* Should be stopped on unregister */ struct list_head deferred; }; @@ -151,6 +152,12 @@ static inline void watchdog_stop_on_reboot(struct watchdog_device *wdd) set_bit(WDOG_STOP_ON_REBOOT, &wdd->status); } +/* Use the following function to stop the watchdog when unregistering it */ +static inline void watchdog_stop_on_unregister(struct watchdog_device *wdd) +{ + set_bit(WDOG_STOP_ON_UNREGISTER, &wdd->status); +} + /* Use the following function to check if a timeout value is invalid */ static inline bool watchdog_timeout_invalid(struct watchdog_device *wdd, unsigned int t) {