Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

watchdog: add driver for Cortina Gemini watchdog

This add support for the Cortina systems Gemini (SL3516)
SoC watchdog.

I have tried to use all the right new kernel interfaces
and tested with busybox' "watchdog" command both to kick
and get timeouts and reboots.

Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>

authored by

Linus Walleij and committed by
Guenter Roeck
eca10ae6 428a6655

+241
+11
drivers/watchdog/Kconfig
··· 301 301 302 302 Not sure? It's safe to say N. 303 303 304 + config GEMINI_WATCHDOG 305 + tristate "Gemini watchdog" 306 + depends on ARCH_GEMINI 307 + select WATCHDOG_CORE 308 + help 309 + Say Y here if to include support for the watchdog timer 310 + embedded in the Cortina Systems Gemini family of devices. 311 + 312 + To compile this driver as a module, choose M here: the 313 + module will be called gemini_wdt. 314 + 304 315 config IXP4XX_WATCHDOG 305 316 tristate "IXP4xx Watchdog" 306 317 depends on ARCH_IXP4XX
+1
drivers/watchdog/Makefile
··· 45 45 obj-$(CONFIG_TWL4030_WATCHDOG) += twl4030_wdt.o 46 46 obj-$(CONFIG_21285_WATCHDOG) += wdt285.o 47 47 obj-$(CONFIG_977_WATCHDOG) += wdt977.o 48 + obj-$(CONFIG_GEMINI_WATCHDOG) += gemini_wdt.o 48 49 obj-$(CONFIG_IXP4XX_WATCHDOG) += ixp4xx_wdt.o 49 50 obj-$(CONFIG_KS8695_WATCHDOG) += ks8695_wdt.o 50 51 obj-$(CONFIG_S3C2410_WATCHDOG) += s3c2410_wdt.o
+229
drivers/watchdog/gemini_wdt.c
··· 1 + /* 2 + * Watchdog driver for Cortina Systems Gemini SoC 3 + * 4 + * Copyright (C) 2017 Linus Walleij <linus.walleij@linaro.org> 5 + * 6 + * Inspired by the out-of-tree drivers from OpenWRT: 7 + * Copyright (C) 2009 Paulius Zaleckas <paulius.zaleckas@teltonika.lt> 8 + * 9 + * This program is free software; you can redistribute it and/or modify 10 + * it under the terms of the GNU General Public License version 2 as 11 + * published by the Free Software Foundation. 12 + */ 13 + 14 + #include <linux/bitops.h> 15 + #include <linux/init.h> 16 + #include <linux/interrupt.h> 17 + #include <linux/io.h> 18 + #include <linux/kernel.h> 19 + #include <linux/module.h> 20 + #include <linux/of_device.h> 21 + #include <linux/platform_device.h> 22 + #include <linux/slab.h> 23 + #include <linux/watchdog.h> 24 + 25 + #define GEMINI_WDCOUNTER 0x0 26 + #define GEMINI_WDLOAD 0x4 27 + #define GEMINI_WDRESTART 0x8 28 + #define GEMINI_WDCR 0xC 29 + 30 + #define WDRESTART_MAGIC 0x5AB9 31 + 32 + #define WDCR_CLOCK_5MHZ BIT(4) 33 + #define WDCR_SYS_RST BIT(1) 34 + #define WDCR_ENABLE BIT(0) 35 + 36 + #define WDT_CLOCK 5000000 /* 5 MHz */ 37 + 38 + struct gemini_wdt { 39 + struct watchdog_device wdd; 40 + struct device *dev; 41 + void __iomem *base; 42 + }; 43 + 44 + static inline 45 + struct gemini_wdt *to_gemini_wdt(struct watchdog_device *wdd) 46 + { 47 + return container_of(wdd, struct gemini_wdt, wdd); 48 + } 49 + 50 + static int gemini_wdt_start(struct watchdog_device *wdd) 51 + { 52 + struct gemini_wdt *gwdt = to_gemini_wdt(wdd); 53 + 54 + writel(wdd->timeout * WDT_CLOCK, gwdt->base + GEMINI_WDLOAD); 55 + writel(WDRESTART_MAGIC, gwdt->base + GEMINI_WDRESTART); 56 + /* set clock before enabling */ 57 + writel(WDCR_CLOCK_5MHZ | WDCR_SYS_RST, 58 + gwdt->base + GEMINI_WDCR); 59 + writel(WDCR_CLOCK_5MHZ | WDCR_SYS_RST | WDCR_ENABLE, 60 + gwdt->base + GEMINI_WDCR); 61 + 62 + return 0; 63 + } 64 + 65 + static int gemini_wdt_stop(struct watchdog_device *wdd) 66 + { 67 + struct gemini_wdt *gwdt = to_gemini_wdt(wdd); 68 + 69 + writel(0, gwdt->base + GEMINI_WDCR); 70 + 71 + return 0; 72 + } 73 + 74 + static int gemini_wdt_ping(struct watchdog_device *wdd) 75 + { 76 + struct gemini_wdt *gwdt = to_gemini_wdt(wdd); 77 + 78 + writel(WDRESTART_MAGIC, gwdt->base + GEMINI_WDRESTART); 79 + 80 + return 0; 81 + } 82 + 83 + static int gemini_wdt_set_timeout(struct watchdog_device *wdd, 84 + unsigned int timeout) 85 + { 86 + wdd->timeout = timeout; 87 + if (watchdog_active(wdd)) 88 + gemini_wdt_start(wdd); 89 + 90 + return 0; 91 + } 92 + 93 + static irqreturn_t gemini_wdt_interrupt(int irq, void *data) 94 + { 95 + struct gemini_wdt *gwdt = data; 96 + 97 + watchdog_notify_pretimeout(&gwdt->wdd); 98 + 99 + return IRQ_HANDLED; 100 + } 101 + 102 + static const struct watchdog_ops gemini_wdt_ops = { 103 + .start = gemini_wdt_start, 104 + .stop = gemini_wdt_stop, 105 + .ping = gemini_wdt_ping, 106 + .set_timeout = gemini_wdt_set_timeout, 107 + .owner = THIS_MODULE, 108 + }; 109 + 110 + static const struct watchdog_info gemini_wdt_info = { 111 + .options = WDIOF_KEEPALIVEPING 112 + | WDIOF_MAGICCLOSE 113 + | WDIOF_SETTIMEOUT, 114 + .identity = KBUILD_MODNAME, 115 + }; 116 + 117 + 118 + static int gemini_wdt_probe(struct platform_device *pdev) 119 + { 120 + struct device *dev = &pdev->dev; 121 + struct resource *res; 122 + struct gemini_wdt *gwdt; 123 + unsigned int reg; 124 + int irq; 125 + int ret; 126 + 127 + gwdt = devm_kzalloc(dev, sizeof(*gwdt), GFP_KERNEL); 128 + if (!gwdt) 129 + return -ENOMEM; 130 + 131 + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 132 + gwdt->base = devm_ioremap_resource(dev, res); 133 + if (IS_ERR(gwdt->base)) 134 + return PTR_ERR(gwdt->base); 135 + 136 + irq = platform_get_irq(pdev, 0); 137 + if (!irq) 138 + return -EINVAL; 139 + 140 + gwdt->dev = dev; 141 + gwdt->wdd.info = &gemini_wdt_info; 142 + gwdt->wdd.ops = &gemini_wdt_ops; 143 + gwdt->wdd.min_timeout = 1; 144 + gwdt->wdd.max_timeout = 0xFFFFFFFF / WDT_CLOCK; 145 + gwdt->wdd.parent = dev; 146 + 147 + /* 148 + * If 'timeout-sec' unspecified in devicetree, assume a 13 second 149 + * default. 150 + */ 151 + gwdt->wdd.timeout = 13U; 152 + watchdog_init_timeout(&gwdt->wdd, 0, dev); 153 + 154 + reg = readw(gwdt->base + GEMINI_WDCR); 155 + if (reg & WDCR_ENABLE) { 156 + /* Watchdog was enabled by the bootloader, disable it. */ 157 + reg &= ~WDCR_ENABLE; 158 + writel(reg, gwdt->base + GEMINI_WDCR); 159 + } 160 + 161 + ret = devm_request_irq(dev, irq, gemini_wdt_interrupt, 0, 162 + "watchdog bark", gwdt); 163 + if (ret) 164 + return ret; 165 + 166 + ret = devm_watchdog_register_device(dev, &gwdt->wdd); 167 + if (ret) { 168 + dev_err(&pdev->dev, "failed to register watchdog\n"); 169 + return ret; 170 + } 171 + 172 + /* Set up platform driver data */ 173 + platform_set_drvdata(pdev, gwdt); 174 + dev_info(dev, "Gemini watchdog driver enabled\n"); 175 + 176 + return 0; 177 + } 178 + 179 + static int __maybe_unused gemini_wdt_suspend(struct device *dev) 180 + { 181 + struct gemini_wdt *gwdt = dev_get_drvdata(dev); 182 + unsigned int reg; 183 + 184 + reg = readw(gwdt->base + GEMINI_WDCR); 185 + reg &= ~WDCR_ENABLE; 186 + writel(reg, gwdt->base + GEMINI_WDCR); 187 + 188 + return 0; 189 + } 190 + 191 + static int __maybe_unused gemini_wdt_resume(struct device *dev) 192 + { 193 + struct gemini_wdt *gwdt = dev_get_drvdata(dev); 194 + unsigned int reg; 195 + 196 + if (watchdog_active(&gwdt->wdd)) { 197 + reg = readw(gwdt->base + GEMINI_WDCR); 198 + reg |= WDCR_ENABLE; 199 + writel(reg, gwdt->base + GEMINI_WDCR); 200 + } 201 + 202 + return 0; 203 + } 204 + 205 + static const struct dev_pm_ops gemini_wdt_dev_pm_ops = { 206 + SET_SYSTEM_SLEEP_PM_OPS(gemini_wdt_suspend, 207 + gemini_wdt_resume) 208 + }; 209 + 210 + #ifdef CONFIG_OF 211 + static const struct of_device_id gemini_wdt_match[] = { 212 + { .compatible = "cortina,gemini-watchdog" }, 213 + {}, 214 + }; 215 + MODULE_DEVICE_TABLE(of, gemini_wdt_match); 216 + #endif 217 + 218 + static struct platform_driver gemini_wdt_driver = { 219 + .probe = gemini_wdt_probe, 220 + .driver = { 221 + .name = "gemini-wdt", 222 + .of_match_table = of_match_ptr(gemini_wdt_match), 223 + .pm = &gemini_wdt_dev_pm_ops, 224 + }, 225 + }; 226 + module_platform_driver(gemini_wdt_driver); 227 + MODULE_AUTHOR("Linus Walleij"); 228 + MODULE_DESCRIPTION("Watchdog driver for Gemini"); 229 + MODULE_LICENSE("GPL");