From 2bdf6acbfead7e9aa69f36ee5682d1e5c8f70367 Mon Sep 17 00:00:00 2001 From: Andreas Färber Date: Tue, 5 Sep 2017 01:16:01 +0200 Subject: watchdog: Add Realtek RTD1295 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a watchdog driver for the Realtek RTD1295 SoC. Based on QNAP's arch/arm/mach-rtk119x/driver/rtk_watchdog.c code and mach-rtk119x/driver/dc2vo/fpga/include/iso_reg.h register defines. Signed-off-by: Andreas Färber Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/Makefile | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/watchdog/Makefile') diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 715a21078e0c..cb5fe3d414bb 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -88,6 +88,7 @@ obj-$(CONFIG_ASPEED_WATCHDOG) += aspeed_wdt.o obj-$(CONFIG_ZX2967_WATCHDOG) += zx2967_wdt.o obj-$(CONFIG_STM32_WATCHDOG) += stm32_iwdg.o obj-$(CONFIG_UNIPHIER_WATCHDOG) += uniphier_wdt.o +obj-$(CONFIG_RTD119X_WATCHDOG) += rtd119x_wdt.o # AVR32 Architecture obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o -- cgit v1.2.3 From 766a2aad645e6c4c4dec0c02be34318d5538e75e Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 16 Oct 2017 22:54:24 +0200 Subject: watchdog: gemini/ftwdt010: rename driver and symbols This renames all the driver files and symbols for the Gemini watchdog to FTWDT010 as it has been revealed that this IP block is a generic watchdog timer from Faraday Technology used in several SoC designs. Select this driver by default for the Gemini, it is a sensible driver to always have enabled. Signed-off-by: Linus Walleij Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/Kconfig | 14 +-- drivers/watchdog/Makefile | 2 +- drivers/watchdog/ftwdt010_wdt.c | 230 ++++++++++++++++++++++++++++++++++++++++ drivers/watchdog/gemini_wdt.c | 229 --------------------------------------- 4 files changed, 239 insertions(+), 236 deletions(-) create mode 100644 drivers/watchdog/ftwdt010_wdt.c delete mode 100644 drivers/watchdog/gemini_wdt.c (limited to 'drivers/watchdog/Makefile') diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index ce8013cbdcf2..be37e102dd30 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -321,16 +321,18 @@ config 977_WATCHDOG Not sure? It's safe to say N. -config GEMINI_WATCHDOG - tristate "Gemini watchdog" - depends on ARCH_GEMINI +config FTWDT010_WATCHDOG + tristate "Faraday Technology FTWDT010 watchdog" + depends on ARM || COMPILE_TEST select WATCHDOG_CORE + default ARCH_GEMINI help - Say Y here if to include support for the watchdog timer - embedded in the Cortina Systems Gemini family of devices. + Say Y here if to include support for the Faraday Technology + FTWDT010 watchdog timer embedded in the Cortina Systems Gemini + family of devices. To compile this driver as a module, choose M here: the - module will be called gemini_wdt. + module will be called ftwdt010_wdt. config IXP4XX_WATCHDOG tristate "IXP4xx Watchdog" diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index cb5fe3d414bb..b6cf527b31fb 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -46,7 +46,7 @@ obj-$(CONFIG_OMAP_WATCHDOG) += omap_wdt.o obj-$(CONFIG_TWL4030_WATCHDOG) += twl4030_wdt.o obj-$(CONFIG_21285_WATCHDOG) += wdt285.o obj-$(CONFIG_977_WATCHDOG) += wdt977.o -obj-$(CONFIG_GEMINI_WATCHDOG) += gemini_wdt.o +obj-$(CONFIG_FTWDT010_WATCHDOG) += ftwdt010_wdt.o obj-$(CONFIG_IXP4XX_WATCHDOG) += ixp4xx_wdt.o obj-$(CONFIG_KS8695_WATCHDOG) += ks8695_wdt.o obj-$(CONFIG_S3C2410_WATCHDOG) += s3c2410_wdt.o diff --git a/drivers/watchdog/ftwdt010_wdt.c b/drivers/watchdog/ftwdt010_wdt.c new file mode 100644 index 000000000000..637ffd812f0b --- /dev/null +++ b/drivers/watchdog/ftwdt010_wdt.c @@ -0,0 +1,230 @@ +/* + * Watchdog driver for Faraday Technology FTWDT010 + * + * 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 FTWDT010_WDCOUNTER 0x0 +#define FTWDT010_WDLOAD 0x4 +#define FTWDT010_WDRESTART 0x8 +#define FTWDT010_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 ftwdt010_wdt { + struct watchdog_device wdd; + struct device *dev; + void __iomem *base; +}; + +static inline +struct ftwdt010_wdt *to_ftwdt010_wdt(struct watchdog_device *wdd) +{ + return container_of(wdd, struct ftwdt010_wdt, wdd); +} + +static int ftwdt010_wdt_start(struct watchdog_device *wdd) +{ + struct ftwdt010_wdt *gwdt = to_ftwdt010_wdt(wdd); + + writel(wdd->timeout * WDT_CLOCK, gwdt->base + FTWDT010_WDLOAD); + writel(WDRESTART_MAGIC, gwdt->base + FTWDT010_WDRESTART); + /* set clock before enabling */ + writel(WDCR_CLOCK_5MHZ | WDCR_SYS_RST, + gwdt->base + FTWDT010_WDCR); + writel(WDCR_CLOCK_5MHZ | WDCR_SYS_RST | WDCR_ENABLE, + gwdt->base + FTWDT010_WDCR); + + return 0; +} + +static int ftwdt010_wdt_stop(struct watchdog_device *wdd) +{ + struct ftwdt010_wdt *gwdt = to_ftwdt010_wdt(wdd); + + writel(0, gwdt->base + FTWDT010_WDCR); + + return 0; +} + +static int ftwdt010_wdt_ping(struct watchdog_device *wdd) +{ + struct ftwdt010_wdt *gwdt = to_ftwdt010_wdt(wdd); + + writel(WDRESTART_MAGIC, gwdt->base + FTWDT010_WDRESTART); + + return 0; +} + +static int ftwdt010_wdt_set_timeout(struct watchdog_device *wdd, + unsigned int timeout) +{ + wdd->timeout = timeout; + if (watchdog_active(wdd)) + ftwdt010_wdt_start(wdd); + + return 0; +} + +static irqreturn_t ftwdt010_wdt_interrupt(int irq, void *data) +{ + struct ftwdt010_wdt *gwdt = data; + + watchdog_notify_pretimeout(&gwdt->wdd); + + return IRQ_HANDLED; +} + +static const struct watchdog_ops ftwdt010_wdt_ops = { + .start = ftwdt010_wdt_start, + .stop = ftwdt010_wdt_stop, + .ping = ftwdt010_wdt_ping, + .set_timeout = ftwdt010_wdt_set_timeout, + .owner = THIS_MODULE, +}; + +static const struct watchdog_info ftwdt010_wdt_info = { + .options = WDIOF_KEEPALIVEPING + | WDIOF_MAGICCLOSE + | WDIOF_SETTIMEOUT, + .identity = KBUILD_MODNAME, +}; + + +static int ftwdt010_wdt_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct resource *res; + struct ftwdt010_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 = &ftwdt010_wdt_info; + gwdt->wdd.ops = &ftwdt010_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 + FTWDT010_WDCR); + if (reg & WDCR_ENABLE) { + /* Watchdog was enabled by the bootloader, disable it. */ + reg &= ~WDCR_ENABLE; + writel(reg, gwdt->base + FTWDT010_WDCR); + } + + ret = devm_request_irq(dev, irq, ftwdt010_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, "FTWDT010 watchdog driver enabled\n"); + + return 0; +} + +static int __maybe_unused ftwdt010_wdt_suspend(struct device *dev) +{ + struct ftwdt010_wdt *gwdt = dev_get_drvdata(dev); + unsigned int reg; + + reg = readw(gwdt->base + FTWDT010_WDCR); + reg &= ~WDCR_ENABLE; + writel(reg, gwdt->base + FTWDT010_WDCR); + + return 0; +} + +static int __maybe_unused ftwdt010_wdt_resume(struct device *dev) +{ + struct ftwdt010_wdt *gwdt = dev_get_drvdata(dev); + unsigned int reg; + + if (watchdog_active(&gwdt->wdd)) { + reg = readw(gwdt->base + FTWDT010_WDCR); + reg |= WDCR_ENABLE; + writel(reg, gwdt->base + FTWDT010_WDCR); + } + + return 0; +} + +static const struct dev_pm_ops ftwdt010_wdt_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(ftwdt010_wdt_suspend, + ftwdt010_wdt_resume) +}; + +#ifdef CONFIG_OF +static const struct of_device_id ftwdt010_wdt_match[] = { + { .compatible = "faraday,ftwdt010" }, + { .compatible = "cortina,gemini-watchdog" }, + {}, +}; +MODULE_DEVICE_TABLE(of, ftwdt010_wdt_match); +#endif + +static struct platform_driver ftwdt010_wdt_driver = { + .probe = ftwdt010_wdt_probe, + .driver = { + .name = "ftwdt010-wdt", + .of_match_table = of_match_ptr(ftwdt010_wdt_match), + .pm = &ftwdt010_wdt_dev_pm_ops, + }, +}; +module_platform_driver(ftwdt010_wdt_driver); +MODULE_AUTHOR("Linus Walleij"); +MODULE_DESCRIPTION("Watchdog driver for Faraday Technology FTWDT010"); +MODULE_LICENSE("GPL"); diff --git a/drivers/watchdog/gemini_wdt.c b/drivers/watchdog/gemini_wdt.c deleted file mode 100644 index 8155aa619e4c..000000000000 --- a/drivers/watchdog/gemini_wdt.c +++ /dev/null @@ -1,229 +0,0 @@ -/* - * 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"); -- cgit v1.2.3 From 477603467009673d0965ca7ec165a09bd0fcb87e Mon Sep 17 00:00:00 2001 From: Eric Long Date: Mon, 6 Nov 2017 10:46:28 +0800 Subject: watchdog: Add Spreadtrum watchdog driver This patch adds the watchdog driver for Spreadtrum SC9860 platform. Signed-off-by: Eric Long Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/Kconfig | 8 + drivers/watchdog/Makefile | 1 + drivers/watchdog/sprd_wdt.c | 399 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 408 insertions(+) create mode 100644 drivers/watchdog/sprd_wdt.c (limited to 'drivers/watchdog/Makefile') diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index be37e102dd30..633173260f5a 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -799,6 +799,14 @@ config RTD119X_WATCHDOG Say Y here to include support for the watchdog timer in Realtek RTD1295 SoCs. +config SPRD_WATCHDOG + tristate "Spreadtrum watchdog support" + depends on ARCH_SPRD || COMPILE_TEST + select WATCHDOG_CORE + help + Say Y here to include watchdog timer supported + by Spreadtrum system. + # AVR32 Architecture config AT32AP700X_WDT diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index b6cf527b31fb..4b374b9e55e8 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -89,6 +89,7 @@ obj-$(CONFIG_ZX2967_WATCHDOG) += zx2967_wdt.o obj-$(CONFIG_STM32_WATCHDOG) += stm32_iwdg.o obj-$(CONFIG_UNIPHIER_WATCHDOG) += uniphier_wdt.o obj-$(CONFIG_RTD119X_WATCHDOG) += rtd119x_wdt.o +obj-$(CONFIG_SPRD_WATCHDOG) += sprd_wdt.o # AVR32 Architecture obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o diff --git a/drivers/watchdog/sprd_wdt.c b/drivers/watchdog/sprd_wdt.c new file mode 100644 index 000000000000..a8b280ff33e0 --- /dev/null +++ b/drivers/watchdog/sprd_wdt.c @@ -0,0 +1,399 @@ +/* + * Spreadtrum watchdog driver + * Copyright (C) 2017 Spreadtrum - http://www.spreadtrum.com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SPRD_WDT_LOAD_LOW 0x0 +#define SPRD_WDT_LOAD_HIGH 0x4 +#define SPRD_WDT_CTRL 0x8 +#define SPRD_WDT_INT_CLR 0xc +#define SPRD_WDT_INT_RAW 0x10 +#define SPRD_WDT_INT_MSK 0x14 +#define SPRD_WDT_CNT_LOW 0x18 +#define SPRD_WDT_CNT_HIGH 0x1c +#define SPRD_WDT_LOCK 0x20 +#define SPRD_WDT_IRQ_LOAD_LOW 0x2c +#define SPRD_WDT_IRQ_LOAD_HIGH 0x30 + +/* WDT_CTRL */ +#define SPRD_WDT_INT_EN_BIT BIT(0) +#define SPRD_WDT_CNT_EN_BIT BIT(1) +#define SPRD_WDT_NEW_VER_EN BIT(2) +#define SPRD_WDT_RST_EN_BIT BIT(3) + +/* WDT_INT_CLR */ +#define SPRD_WDT_INT_CLEAR_BIT BIT(0) +#define SPRD_WDT_RST_CLEAR_BIT BIT(3) + +/* WDT_INT_RAW */ +#define SPRD_WDT_INT_RAW_BIT BIT(0) +#define SPRD_WDT_RST_RAW_BIT BIT(3) +#define SPRD_WDT_LD_BUSY_BIT BIT(4) + +/* 1s equal to 32768 counter steps */ +#define SPRD_WDT_CNT_STEP 32768 + +#define SPRD_WDT_UNLOCK_KEY 0xe551 +#define SPRD_WDT_MIN_TIMEOUT 3 +#define SPRD_WDT_MAX_TIMEOUT 60 + +#define SPRD_WDT_CNT_HIGH_SHIFT 16 +#define SPRD_WDT_LOW_VALUE_MASK GENMASK(15, 0) +#define SPRD_WDT_LOAD_TIMEOUT 1000 + +struct sprd_wdt { + void __iomem *base; + struct watchdog_device wdd; + struct clk *enable; + struct clk *rtc_enable; + int irq; +}; + +static inline struct sprd_wdt *to_sprd_wdt(struct watchdog_device *wdd) +{ + return container_of(wdd, struct sprd_wdt, wdd); +} + +static inline void sprd_wdt_lock(void __iomem *addr) +{ + writel_relaxed(0x0, addr + SPRD_WDT_LOCK); +} + +static inline void sprd_wdt_unlock(void __iomem *addr) +{ + writel_relaxed(SPRD_WDT_UNLOCK_KEY, addr + SPRD_WDT_LOCK); +} + +static irqreturn_t sprd_wdt_isr(int irq, void *dev_id) +{ + struct sprd_wdt *wdt = (struct sprd_wdt *)dev_id; + + sprd_wdt_unlock(wdt->base); + writel_relaxed(SPRD_WDT_INT_CLEAR_BIT, wdt->base + SPRD_WDT_INT_CLR); + sprd_wdt_lock(wdt->base); + watchdog_notify_pretimeout(&wdt->wdd); + return IRQ_HANDLED; +} + +static u32 sprd_wdt_get_cnt_value(struct sprd_wdt *wdt) +{ + u32 val; + + val = readl_relaxed(wdt->base + SPRD_WDT_CNT_HIGH) << + SPRD_WDT_CNT_HIGH_SHIFT; + val |= readl_relaxed(wdt->base + SPRD_WDT_CNT_LOW) & + SPRD_WDT_LOW_VALUE_MASK; + + return val; +} + +static int sprd_wdt_load_value(struct sprd_wdt *wdt, u32 timeout, + u32 pretimeout) +{ + u32 val, delay_cnt = 0; + u32 tmr_step = timeout * SPRD_WDT_CNT_STEP; + u32 prtmr_step = pretimeout * SPRD_WDT_CNT_STEP; + + sprd_wdt_unlock(wdt->base); + writel_relaxed((tmr_step >> SPRD_WDT_CNT_HIGH_SHIFT) & + SPRD_WDT_LOW_VALUE_MASK, wdt->base + SPRD_WDT_LOAD_HIGH); + writel_relaxed((tmr_step & SPRD_WDT_LOW_VALUE_MASK), + wdt->base + SPRD_WDT_LOAD_LOW); + writel_relaxed((prtmr_step >> SPRD_WDT_CNT_HIGH_SHIFT) & + SPRD_WDT_LOW_VALUE_MASK, + wdt->base + SPRD_WDT_IRQ_LOAD_HIGH); + writel_relaxed(prtmr_step & SPRD_WDT_LOW_VALUE_MASK, + wdt->base + SPRD_WDT_IRQ_LOAD_LOW); + sprd_wdt_lock(wdt->base); + + /* + * Waiting the load value operation done, + * it needs two or three RTC clock cycles. + */ + do { + val = readl_relaxed(wdt->base + SPRD_WDT_INT_RAW); + if (!(val & SPRD_WDT_LD_BUSY_BIT)) + break; + + cpu_relax(); + } while (delay_cnt++ < SPRD_WDT_LOAD_TIMEOUT); + + if (delay_cnt >= SPRD_WDT_LOAD_TIMEOUT) + return -EBUSY; + return 0; +} + +static int sprd_wdt_enable(struct sprd_wdt *wdt) +{ + u32 val; + int ret; + + ret = clk_prepare_enable(wdt->enable); + if (ret) + return ret; + ret = clk_prepare_enable(wdt->rtc_enable); + if (ret) + return ret; + + sprd_wdt_unlock(wdt->base); + val = readl_relaxed(wdt->base + SPRD_WDT_CTRL); + val |= SPRD_WDT_NEW_VER_EN; + writel_relaxed(val, wdt->base + SPRD_WDT_CTRL); + sprd_wdt_lock(wdt->base); + return 0; +} + +static void sprd_wdt_disable(void *_data) +{ + struct sprd_wdt *wdt = _data; + + sprd_wdt_unlock(wdt->base); + writel_relaxed(0x0, wdt->base + SPRD_WDT_CTRL); + sprd_wdt_lock(wdt->base); + + clk_disable_unprepare(wdt->rtc_enable); + clk_disable_unprepare(wdt->enable); +} + +static int sprd_wdt_start(struct watchdog_device *wdd) +{ + struct sprd_wdt *wdt = to_sprd_wdt(wdd); + u32 val; + int ret; + + ret = sprd_wdt_load_value(wdt, wdd->timeout, wdd->pretimeout); + if (ret) + return ret; + + sprd_wdt_unlock(wdt->base); + val = readl_relaxed(wdt->base + SPRD_WDT_CTRL); + val |= SPRD_WDT_CNT_EN_BIT | SPRD_WDT_INT_EN_BIT | SPRD_WDT_RST_EN_BIT; + writel_relaxed(val, wdt->base + SPRD_WDT_CTRL); + sprd_wdt_lock(wdt->base); + set_bit(WDOG_HW_RUNNING, &wdd->status); + + return 0; +} + +static int sprd_wdt_stop(struct watchdog_device *wdd) +{ + struct sprd_wdt *wdt = to_sprd_wdt(wdd); + u32 val; + + sprd_wdt_unlock(wdt->base); + val = readl_relaxed(wdt->base + SPRD_WDT_CTRL); + val &= ~(SPRD_WDT_CNT_EN_BIT | SPRD_WDT_RST_EN_BIT | + SPRD_WDT_INT_EN_BIT); + writel_relaxed(val, wdt->base + SPRD_WDT_CTRL); + sprd_wdt_lock(wdt->base); + return 0; +} + +static int sprd_wdt_set_timeout(struct watchdog_device *wdd, + u32 timeout) +{ + struct sprd_wdt *wdt = to_sprd_wdt(wdd); + + if (timeout == wdd->timeout) + return 0; + + wdd->timeout = timeout; + + return sprd_wdt_load_value(wdt, timeout, wdd->pretimeout); +} + +static int sprd_wdt_set_pretimeout(struct watchdog_device *wdd, + u32 new_pretimeout) +{ + struct sprd_wdt *wdt = to_sprd_wdt(wdd); + + if (new_pretimeout < wdd->min_timeout) + return -EINVAL; + + wdd->pretimeout = new_pretimeout; + + return sprd_wdt_load_value(wdt, wdd->timeout, new_pretimeout); +} + +static u32 sprd_wdt_get_timeleft(struct watchdog_device *wdd) +{ + struct sprd_wdt *wdt = to_sprd_wdt(wdd); + u32 val; + + val = sprd_wdt_get_cnt_value(wdt); + val = val / SPRD_WDT_CNT_STEP; + + return val; +} + +static const struct watchdog_ops sprd_wdt_ops = { + .owner = THIS_MODULE, + .start = sprd_wdt_start, + .stop = sprd_wdt_stop, + .set_timeout = sprd_wdt_set_timeout, + .set_pretimeout = sprd_wdt_set_pretimeout, + .get_timeleft = sprd_wdt_get_timeleft, +}; + +static const struct watchdog_info sprd_wdt_info = { + .options = WDIOF_SETTIMEOUT | + WDIOF_PRETIMEOUT | + WDIOF_MAGICCLOSE | + WDIOF_KEEPALIVEPING, + .identity = "Spreadtrum Watchdog Timer", +}; + +static int sprd_wdt_probe(struct platform_device *pdev) +{ + struct resource *wdt_res; + struct sprd_wdt *wdt; + int ret; + + wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); + if (!wdt) + return -ENOMEM; + + wdt_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + wdt->base = devm_ioremap_resource(&pdev->dev, wdt_res); + if (IS_ERR(wdt->base)) { + dev_err(&pdev->dev, "failed to map memory resource\n"); + return PTR_ERR(wdt->base); + } + + wdt->enable = devm_clk_get(&pdev->dev, "enable"); + if (IS_ERR(wdt->enable)) { + dev_err(&pdev->dev, "can't get the enable clock\n"); + return PTR_ERR(wdt->enable); + } + + wdt->rtc_enable = devm_clk_get(&pdev->dev, "rtc_enable"); + if (IS_ERR(wdt->rtc_enable)) { + dev_err(&pdev->dev, "can't get the rtc enable clock\n"); + return PTR_ERR(wdt->rtc_enable); + } + + wdt->irq = platform_get_irq(pdev, 0); + if (wdt->irq < 0) { + dev_err(&pdev->dev, "failed to get IRQ resource\n"); + return wdt->irq; + } + + ret = devm_request_irq(&pdev->dev, wdt->irq, sprd_wdt_isr, + IRQF_NO_SUSPEND, "sprd-wdt", (void *)wdt); + if (ret) { + dev_err(&pdev->dev, "failed to register irq\n"); + return ret; + } + + wdt->wdd.info = &sprd_wdt_info; + wdt->wdd.ops = &sprd_wdt_ops; + wdt->wdd.parent = &pdev->dev; + wdt->wdd.min_timeout = SPRD_WDT_MIN_TIMEOUT; + wdt->wdd.max_timeout = SPRD_WDT_MAX_TIMEOUT; + wdt->wdd.timeout = SPRD_WDT_MAX_TIMEOUT; + + ret = sprd_wdt_enable(wdt); + if (ret) { + dev_err(&pdev->dev, "failed to enable wdt\n"); + return ret; + } + ret = devm_add_action(&pdev->dev, sprd_wdt_disable, wdt); + if (ret) { + sprd_wdt_disable(wdt); + dev_err(&pdev->dev, "Failed to add wdt disable action\n"); + return ret; + } + + watchdog_set_nowayout(&wdt->wdd, WATCHDOG_NOWAYOUT); + watchdog_init_timeout(&wdt->wdd, 0, &pdev->dev); + + ret = devm_watchdog_register_device(&pdev->dev, &wdt->wdd); + if (ret) { + sprd_wdt_disable(wdt); + dev_err(&pdev->dev, "failed to register watchdog\n"); + return ret; + } + platform_set_drvdata(pdev, wdt); + + return 0; +} + +static int __maybe_unused sprd_wdt_pm_suspend(struct device *dev) +{ + struct watchdog_device *wdd = dev_get_drvdata(dev); + struct sprd_wdt *wdt = dev_get_drvdata(dev); + + if (watchdog_active(wdd)) + sprd_wdt_stop(&wdt->wdd); + sprd_wdt_disable(wdt); + + return 0; +} + +static int __maybe_unused sprd_wdt_pm_resume(struct device *dev) +{ + struct watchdog_device *wdd = dev_get_drvdata(dev); + struct sprd_wdt *wdt = dev_get_drvdata(dev); + int ret; + + ret = sprd_wdt_enable(wdt); + if (ret) + return ret; + + if (watchdog_active(wdd)) { + ret = sprd_wdt_start(&wdt->wdd); + if (ret) { + sprd_wdt_disable(wdt); + return ret; + } + } + + return 0; +} + +static const struct dev_pm_ops sprd_wdt_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(sprd_wdt_pm_suspend, + sprd_wdt_pm_resume) +}; + +static const struct of_device_id sprd_wdt_match_table[] = { + { .compatible = "sprd,sp9860-wdt", }, + {}, +}; +MODULE_DEVICE_TABLE(of, sprd_wdt_match_table); + +static struct platform_driver sprd_watchdog_driver = { + .probe = sprd_wdt_probe, + .driver = { + .name = "sprd-wdt", + .of_match_table = sprd_wdt_match_table, + .pm = &sprd_wdt_pm_ops, + }, +}; +module_platform_driver(sprd_watchdog_driver); + +MODULE_AUTHOR("Eric Long "); +MODULE_DESCRIPTION("Spreadtrum Watchdog Timer Controller Driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From d040a2ee0e6292b73a328e671eb92a7c9753d18f Mon Sep 17 00:00:00 2001 From: Corentin Labbe Date: Thu, 18 Jan 2018 20:52:56 +0100 Subject: watchdog: remove at32ap700x_wdt Since AVR32 is gone, this driver is useless. Signed-off-by: Corentin Labbe Acked-by: Hans-Christian Noren Egtvedt Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/Kconfig | 9 - drivers/watchdog/Makefile | 3 - drivers/watchdog/at32ap700x_wdt.c | 433 -------------------------------------- 3 files changed, 445 deletions(-) delete mode 100644 drivers/watchdog/at32ap700x_wdt.c (limited to 'drivers/watchdog/Makefile') diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 633173260f5a..6a602f70aaa4 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -807,15 +807,6 @@ config SPRD_WATCHDOG Say Y here to include watchdog timer supported by Spreadtrum system. -# AVR32 Architecture - -config AT32AP700X_WDT - tristate "AT32AP700x watchdog" - depends on CPU_AT32AP700X || COMPILE_TEST - help - Watchdog timer embedded into AT32AP700x devices. This will reboot - your system when the timeout is reached. - # BLACKFIN Architecture config BFIN_WDT diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 4b374b9e55e8..b7123bab9f8e 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -91,9 +91,6 @@ obj-$(CONFIG_UNIPHIER_WATCHDOG) += uniphier_wdt.o obj-$(CONFIG_RTD119X_WATCHDOG) += rtd119x_wdt.o obj-$(CONFIG_SPRD_WATCHDOG) += sprd_wdt.o -# AVR32 Architecture -obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o - # BLACKFIN Architecture obj-$(CONFIG_BFIN_WDT) += bfin_wdt.o diff --git a/drivers/watchdog/at32ap700x_wdt.c b/drivers/watchdog/at32ap700x_wdt.c deleted file mode 100644 index 81ba8920f127..000000000000 --- a/drivers/watchdog/at32ap700x_wdt.c +++ /dev/null @@ -1,433 +0,0 @@ -/* - * Watchdog driver for Atmel AT32AP700X devices - * - * Copyright (C) 2005-2006 Atmel Corporation - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * - * Errata: WDT Clear is blocked after WDT Reset - * - * A watchdog timer event will, after reset, block writes to the WDT_CLEAR - * register, preventing the program to clear the next Watchdog Timer Reset. - * - * If you still want to use the WDT after a WDT reset a small code can be - * insterted at the startup checking the AVR32_PM.rcause register for WDT reset - * and use a GPIO pin to reset the system. This method requires that one of the - * GPIO pins are available and connected externally to the RESET_N pin. After - * the GPIO pin has pulled down the reset line the GPIO will be reset and leave - * the pin tristated with pullup. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define TIMEOUT_MIN 1 -#define TIMEOUT_MAX 2 -#define TIMEOUT_DEFAULT TIMEOUT_MAX - -/* module parameters */ -static int timeout = TIMEOUT_DEFAULT; -module_param(timeout, int, 0); -MODULE_PARM_DESC(timeout, - "Timeout value. Limited to be 1 or 2 seconds. (default=" - __MODULE_STRING(TIMEOUT_DEFAULT) ")"); - -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) ")"); - -/* Watchdog registers and write/read macro */ -#define WDT_CTRL 0x00 -#define WDT_CTRL_EN 0 -#define WDT_CTRL_PSEL 8 -#define WDT_CTRL_KEY 24 - -#define WDT_CLR 0x04 - -#define WDT_RCAUSE 0x10 -#define WDT_RCAUSE_POR 0 -#define WDT_RCAUSE_EXT 2 -#define WDT_RCAUSE_WDT 3 -#define WDT_RCAUSE_JTAG 4 -#define WDT_RCAUSE_SERP 5 - -#define WDT_BIT(name) (1 << WDT_##name) -#define WDT_BF(name, value) ((value) << WDT_##name) - -#define wdt_readl(dev, reg) \ - __raw_readl((dev)->regs + WDT_##reg) -#define wdt_writel(dev, reg, value) \ - __raw_writel((value), (dev)->regs + WDT_##reg) - -struct wdt_at32ap700x { - void __iomem *regs; - spinlock_t io_lock; - int timeout; - int boot_status; - unsigned long users; - struct miscdevice miscdev; -}; - -static struct wdt_at32ap700x *wdt; -static char expect_release; - -/* - * Disable the watchdog. - */ -static inline void at32_wdt_stop(void) -{ - unsigned long psel; - - spin_lock(&wdt->io_lock); - psel = wdt_readl(wdt, CTRL) & WDT_BF(CTRL_PSEL, 0x0f); - wdt_writel(wdt, CTRL, psel | WDT_BF(CTRL_KEY, 0x55)); - wdt_writel(wdt, CTRL, psel | WDT_BF(CTRL_KEY, 0xaa)); - spin_unlock(&wdt->io_lock); -} - -/* - * Enable and reset the watchdog. - */ -static inline void at32_wdt_start(void) -{ - /* 0xf is 2^16 divider = 2 sec, 0xe is 2^15 divider = 1 sec */ - unsigned long psel = (wdt->timeout > 1) ? 0xf : 0xe; - - spin_lock(&wdt->io_lock); - wdt_writel(wdt, CTRL, WDT_BIT(CTRL_EN) - | WDT_BF(CTRL_PSEL, psel) - | WDT_BF(CTRL_KEY, 0x55)); - wdt_writel(wdt, CTRL, WDT_BIT(CTRL_EN) - | WDT_BF(CTRL_PSEL, psel) - | WDT_BF(CTRL_KEY, 0xaa)); - spin_unlock(&wdt->io_lock); -} - -/* - * Pat the watchdog timer. - */ -static inline void at32_wdt_pat(void) -{ - spin_lock(&wdt->io_lock); - wdt_writel(wdt, CLR, 0x42); - spin_unlock(&wdt->io_lock); -} - -/* - * Watchdog device is opened, and watchdog starts running. - */ -static int at32_wdt_open(struct inode *inode, struct file *file) -{ - if (test_and_set_bit(1, &wdt->users)) - return -EBUSY; - - at32_wdt_start(); - return nonseekable_open(inode, file); -} - -/* - * Close the watchdog device. - */ -static int at32_wdt_close(struct inode *inode, struct file *file) -{ - if (expect_release == 42) { - at32_wdt_stop(); - } else { - dev_dbg(wdt->miscdev.parent, - "unexpected close, not stopping watchdog!\n"); - at32_wdt_pat(); - } - clear_bit(1, &wdt->users); - expect_release = 0; - return 0; -} - -/* - * Change the watchdog time interval. - */ -static int at32_wdt_settimeout(int time) -{ - /* - * All counting occurs at 1 / SLOW_CLOCK (32 kHz) and max prescaler is - * 2 ^ 16 allowing up to 2 seconds timeout. - */ - if ((time < TIMEOUT_MIN) || (time > TIMEOUT_MAX)) - return -EINVAL; - - /* - * Set new watchdog time. It will be used when at32_wdt_start() is - * called. - */ - wdt->timeout = time; - return 0; -} - -/* - * Get the watchdog status. - */ -static int at32_wdt_get_status(void) -{ - int rcause; - int status = 0; - - rcause = wdt_readl(wdt, RCAUSE); - - switch (rcause) { - case WDT_BIT(RCAUSE_EXT): - status = WDIOF_EXTERN1; - break; - case WDT_BIT(RCAUSE_WDT): - status = WDIOF_CARDRESET; - break; - case WDT_BIT(RCAUSE_POR): /* fall through */ - case WDT_BIT(RCAUSE_JTAG): /* fall through */ - case WDT_BIT(RCAUSE_SERP): /* fall through */ - default: - break; - } - - return status; -} - -static const struct watchdog_info at32_wdt_info = { - .identity = "at32ap700x watchdog", - .options = WDIOF_SETTIMEOUT | - WDIOF_KEEPALIVEPING | - WDIOF_MAGICCLOSE, -}; - -/* - * Handle commands from user-space. - */ -static long at32_wdt_ioctl(struct file *file, - unsigned int cmd, unsigned long arg) -{ - int ret = -ENOTTY; - int time; - void __user *argp = (void __user *)arg; - int __user *p = argp; - - switch (cmd) { - case WDIOC_GETSUPPORT: - ret = copy_to_user(argp, &at32_wdt_info, - sizeof(at32_wdt_info)) ? -EFAULT : 0; - break; - case WDIOC_GETSTATUS: - ret = put_user(0, p); - break; - case WDIOC_GETBOOTSTATUS: - ret = put_user(wdt->boot_status, p); - break; - case WDIOC_SETOPTIONS: - ret = get_user(time, p); - if (ret) - break; - if (time & WDIOS_DISABLECARD) - at32_wdt_stop(); - if (time & WDIOS_ENABLECARD) - at32_wdt_start(); - ret = 0; - break; - case WDIOC_KEEPALIVE: - at32_wdt_pat(); - ret = 0; - break; - case WDIOC_SETTIMEOUT: - ret = get_user(time, p); - if (ret) - break; - ret = at32_wdt_settimeout(time); - if (ret) - break; - /* Enable new time value */ - at32_wdt_start(); - /* fall through */ - case WDIOC_GETTIMEOUT: - ret = put_user(wdt->timeout, p); - break; - } - - return ret; -} - -static ssize_t at32_wdt_write(struct file *file, const char __user *data, - size_t len, loff_t *ppos) -{ - /* See if we got the magic character 'V' and reload the timer */ - if (len) { - if (!nowayout) { - size_t i; - - /* - * note: just in case someone wrote the magic - * character five months ago... - */ - expect_release = 0; - - /* - * scan to see whether or not we got the magic - * character - */ - for (i = 0; i != len; i++) { - char c; - if (get_user(c, data + i)) - return -EFAULT; - if (c == 'V') - expect_release = 42; - } - } - /* someone wrote to us, we should pat the watchdog */ - at32_wdt_pat(); - } - return len; -} - -static const struct file_operations at32_wdt_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .unlocked_ioctl = at32_wdt_ioctl, - .open = at32_wdt_open, - .release = at32_wdt_close, - .write = at32_wdt_write, -}; - -static int __init at32_wdt_probe(struct platform_device *pdev) -{ - struct resource *regs; - int ret; - - if (wdt) { - dev_dbg(&pdev->dev, "only 1 wdt instance supported.\n"); - return -EBUSY; - } - - regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!regs) { - dev_dbg(&pdev->dev, "missing mmio resource\n"); - return -ENXIO; - } - - wdt = devm_kzalloc(&pdev->dev, sizeof(struct wdt_at32ap700x), - GFP_KERNEL); - if (!wdt) - return -ENOMEM; - - wdt->regs = devm_ioremap(&pdev->dev, regs->start, resource_size(regs)); - if (!wdt->regs) { - ret = -ENOMEM; - dev_dbg(&pdev->dev, "could not map I/O memory\n"); - goto err_free; - } - - spin_lock_init(&wdt->io_lock); - wdt->boot_status = at32_wdt_get_status(); - - /* Work-around for watchdog silicon errata. */ - if (wdt->boot_status & WDIOF_CARDRESET) { - dev_info(&pdev->dev, "CPU must be reset with external " - "reset or POR due to silicon errata.\n"); - ret = -EIO; - goto err_free; - } else { - wdt->users = 0; - } - - wdt->miscdev.minor = WATCHDOG_MINOR; - wdt->miscdev.name = "watchdog"; - wdt->miscdev.fops = &at32_wdt_fops; - wdt->miscdev.parent = &pdev->dev; - - platform_set_drvdata(pdev, wdt); - - if (at32_wdt_settimeout(timeout)) { - at32_wdt_settimeout(TIMEOUT_DEFAULT); - dev_dbg(&pdev->dev, - "default timeout invalid, set to %d sec.\n", - TIMEOUT_DEFAULT); - } - - ret = misc_register(&wdt->miscdev); - if (ret) { - dev_dbg(&pdev->dev, "failed to register wdt miscdev\n"); - goto err_free; - } - - dev_info(&pdev->dev, - "AT32AP700X WDT at 0x%p, timeout %d sec (nowayout=%d)\n", - wdt->regs, wdt->timeout, nowayout); - - return 0; - -err_free: - wdt = NULL; - return ret; -} - -static int __exit at32_wdt_remove(struct platform_device *pdev) -{ - if (wdt && platform_get_drvdata(pdev) == wdt) { - /* Stop the timer before we leave */ - if (!nowayout) - at32_wdt_stop(); - - misc_deregister(&wdt->miscdev); - wdt = NULL; - } - return 0; -} - -static void at32_wdt_shutdown(struct platform_device *pdev) -{ - at32_wdt_stop(); -} - -#ifdef CONFIG_PM -static int at32_wdt_suspend(struct platform_device *pdev, pm_message_t message) -{ - at32_wdt_stop(); - return 0; -} - -static int at32_wdt_resume(struct platform_device *pdev) -{ - if (wdt->users) - at32_wdt_start(); - return 0; -} -#else -#define at32_wdt_suspend NULL -#define at32_wdt_resume NULL -#endif - -/* work with hotplug and coldplug */ -MODULE_ALIAS("platform:at32_wdt"); - -static struct platform_driver at32_wdt_driver = { - .remove = __exit_p(at32_wdt_remove), - .suspend = at32_wdt_suspend, - .resume = at32_wdt_resume, - .driver = { - .name = "at32_wdt", - }, - .shutdown = at32_wdt_shutdown, -}; - -module_platform_driver_probe(at32_wdt_driver, at32_wdt_probe); - -MODULE_AUTHOR("Hans-Christian Egtvedt "); -MODULE_DESCRIPTION("Watchdog driver for Atmel AT32AP700X"); -MODULE_LICENSE("GPL"); -- cgit v1.2.3