Add a watchdog driver for ARM MPcore processors. Signed-off-by: Felix Fietkau --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -355,6 +355,13 @@ config KS8695_WATCHDOG Watchdog timer embedded into KS8695 processor. This will reboot your system when the timeout is reached. +config MPCORE_WATCHDOG + tristate "MPcore watchdog" + depends on HAVE_ARM_TWD + select WATCHDOG_CORE + help + Watchdog timer embedded into the MPcore system + config HAVE_S3C2410_WATCHDOG bool help --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -49,6 +49,7 @@ obj-$(CONFIG_977_WATCHDOG) += wdt977.o obj-$(CONFIG_GEMINI_WATCHDOG) += gemini_wdt.o obj-$(CONFIG_IXP4XX_WATCHDOG) += ixp4xx_wdt.o obj-$(CONFIG_KS8695_WATCHDOG) += ks8695_wdt.o +obj-$(CONFIG_MPCORE_WATCHDOG) += mpcore_wdt.o obj-$(CONFIG_S3C2410_WATCHDOG) += s3c2410_wdt.o obj-$(CONFIG_SA1100_WATCHDOG) += sa1100_wdt.o obj-$(CONFIG_SAMA5D4_WATCHDOG) += sama5d4_wdt.o --- /dev/null +++ b/drivers/watchdog/mpcore_wdt.c @@ -0,0 +1,118 @@ +/* + * Watchdog driver for ARM MPcore + * + * Copyright (C) 2017 Felix Fietkau + */ + +#include +#include +#include +#include +#include +#include +#include + +static void __iomem *wdt_base; +static int wdt_timeout = 60; + +static int mpcore_wdt_keepalive(struct watchdog_device *wdd) +{ + static int perturb; + u32 count; + + count = (twd_timer_get_rate() / 256) * wdt_timeout; + + /* Reload register needs a different value on each refresh */ + count += perturb; + perturb = !perturb; + + iowrite32(count, wdt_base + TWD_WDOG_LOAD); + + return 0; +} + +static int mpcore_wdt_start(struct watchdog_device *wdd) +{ + mpcore_wdt_keepalive(wdd); + + /* prescale = 256, mode = 1, enable = 1 */ + iowrite32(0x0000FF09, wdt_base + TWD_WDOG_CONTROL); + + return 0; +} + +static int mpcore_wdt_stop(struct watchdog_device *wdd) +{ + iowrite32(0x12345678, wdt_base + TWD_WDOG_DISABLE); + iowrite32(0x87654321, wdt_base + TWD_WDOG_DISABLE); + iowrite32(0x0, wdt_base + TWD_WDOG_CONTROL); + + return 0; +} + +static int mpcore_wdt_set_timeout(struct watchdog_device *wdd, + unsigned int timeout) +{ + mpcore_wdt_stop(wdd); + wdt_timeout = timeout; + mpcore_wdt_start(wdd); + + return 0; +} + +static const struct watchdog_info mpcore_wdt_info = { + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, + .identity = "MPcore Watchdog", +}; + +static const struct watchdog_ops mpcore_wdt_ops = { + .owner = THIS_MODULE, + .start = mpcore_wdt_start, + .stop = mpcore_wdt_stop, + .ping = mpcore_wdt_keepalive, + .set_timeout = mpcore_wdt_set_timeout, +}; + +static struct watchdog_device mpcore_wdt = { + .info = &mpcore_wdt_info, + .ops = &mpcore_wdt_ops, + .min_timeout = 1, + .max_timeout = 65535, +}; + +static int mpcore_wdt_probe(struct platform_device *pdev) +{ + struct resource *res; + unsigned long rate = twd_timer_get_rate(); + + pr_info("MPCore WD init. clockrate: %u prescaler: %u countrate: %u timeout: %us\n", rate, 256, rate / 256, wdt_timeout); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + + wdt_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(wdt_base)) + return PTR_ERR(wdt_base); + + watchdog_register_device(&mpcore_wdt); + return 0; +} + +static int mpcore_wdt_remove(struct platform_device *dev) +{ + watchdog_unregister_device(&mpcore_wdt); + return 0; +} + +static struct platform_driver mpcore_wdt_driver = { + .probe = mpcore_wdt_probe, + .remove = mpcore_wdt_remove, + .driver = { + .name = "mpcore_wdt", + }, +}; + +module_platform_driver(mpcore_wdt_driver); +MODULE_AUTHOR("Felix Fietkau "); +MODULE_LICENSE("GPL"); --- a/arch/arm/include/asm/smp_twd.h +++ b/arch/arm/include/asm/smp_twd.h @@ -34,5 +34,6 @@ struct twd_local_timer name __initdata = }; int twd_local_timer_register(struct twd_local_timer *); +unsigned long twd_timer_get_rate(void); #endif --- a/arch/arm/kernel/smp_twd.c +++ b/arch/arm/kernel/smp_twd.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -380,6 +381,14 @@ int __init twd_local_timer_register(stru return twd_local_timer_common_register(NULL); } +/* Needed by mpcore_wdt */ +unsigned long twd_timer_get_rate(void) +{ + return twd_timer_rate; +} +EXPORT_SYMBOL_GPL(twd_timer_get_rate); + + #ifdef CONFIG_OF static int __init twd_local_timer_of_register(struct device_node *np) {