1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Watchdog driver for Cirrus Logic EP93xx family of devices. 4 * 5 * Copyright (c) 2004 Ray Lehtiniemi 6 * Copyright (c) 2006 Tower Technologies 7 * Based on ep93xx driver, bits from alim7101_wdt.c 8 * 9 * Authors: Ray Lehtiniemi <rayl@mail.com>, 10 * Alessandro Zummo <a.zummo@towertech.it> 11 * 12 * Copyright (c) 2012 H Hartley Sweeten <hsweeten@visionengravers.com> 13 * Convert to a platform device and use the watchdog framework API 14 * 15 * This watchdog fires after 250msec, which is a too short interval 16 * for us to rely on the user space daemon alone. So we ping the 17 * wdt each ~200msec and eventually stop doing it if the user space 18 * daemon dies. 19 */ 20 21 #include <linux/platform_device.h> 22 #include <linux/module.h> 23 #include <linux/watchdog.h> 24 #include <linux/io.h> 25 26 /* default timeout (secs) */ 27 #define WDT_TIMEOUT 30 28 29 static bool nowayout = WATCHDOG_NOWAYOUT; 30 module_param(nowayout, bool, 0); 31 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started"); 32 33 static unsigned int timeout; 34 module_param(timeout, uint, 0); 35 MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds."); 36 37 #define EP93XX_WATCHDOG 0x00 38 #define EP93XX_WDSTATUS 0x04 39 40 struct ep93xx_wdt_priv { 41 void __iomem *mmio; 42 struct watchdog_device wdd; 43 }; 44 45 static int ep93xx_wdt_start(struct watchdog_device *wdd) 46 { 47 struct ep93xx_wdt_priv *priv = watchdog_get_drvdata(wdd); 48 49 writel(0xaaaa, priv->mmio + EP93XX_WATCHDOG); 50 51 return 0; 52 } 53 54 static int ep93xx_wdt_stop(struct watchdog_device *wdd) 55 { 56 struct ep93xx_wdt_priv *priv = watchdog_get_drvdata(wdd); 57 58 writel(0xaa55, priv->mmio + EP93XX_WATCHDOG); 59 60 return 0; 61 } 62 63 static int ep93xx_wdt_ping(struct watchdog_device *wdd) 64 { 65 struct ep93xx_wdt_priv *priv = watchdog_get_drvdata(wdd); 66 67 writel(0x5555, priv->mmio + EP93XX_WATCHDOG); 68 69 return 0; 70 } 71 72 static const struct watchdog_info ep93xx_wdt_ident = { 73 .options = WDIOF_CARDRESET | 74 WDIOF_SETTIMEOUT | 75 WDIOF_MAGICCLOSE | 76 WDIOF_KEEPALIVEPING, 77 .identity = "EP93xx Watchdog", 78 }; 79 80 static const struct watchdog_ops ep93xx_wdt_ops = { 81 .owner = THIS_MODULE, 82 .start = ep93xx_wdt_start, 83 .stop = ep93xx_wdt_stop, 84 .ping = ep93xx_wdt_ping, 85 }; 86 87 static int ep93xx_wdt_probe(struct platform_device *pdev) 88 { 89 struct device *dev = &pdev->dev; 90 struct ep93xx_wdt_priv *priv; 91 struct watchdog_device *wdd; 92 unsigned long val; 93 int ret; 94 95 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 96 if (!priv) 97 return -ENOMEM; 98 99 priv->mmio = devm_platform_ioremap_resource(pdev, 0); 100 if (IS_ERR(priv->mmio)) 101 return PTR_ERR(priv->mmio); 102 103 val = readl(priv->mmio + EP93XX_WATCHDOG); 104 105 wdd = &priv->wdd; 106 wdd->bootstatus = (val & 0x01) ? WDIOF_CARDRESET : 0; 107 wdd->info = &ep93xx_wdt_ident; 108 wdd->ops = &ep93xx_wdt_ops; 109 wdd->min_timeout = 1; 110 wdd->max_hw_heartbeat_ms = 200; 111 wdd->parent = dev; 112 113 watchdog_set_nowayout(wdd, nowayout); 114 115 wdd->timeout = WDT_TIMEOUT; 116 watchdog_init_timeout(wdd, timeout, dev); 117 118 watchdog_set_drvdata(wdd, priv); 119 120 ret = devm_watchdog_register_device(dev, wdd); 121 if (ret) 122 return ret; 123 124 dev_info(dev, "EP93XX watchdog driver %s\n", 125 (val & 0x08) ? " (nCS1 disable detected)" : ""); 126 127 return 0; 128 } 129 130 static struct platform_driver ep93xx_wdt_driver = { 131 .driver = { 132 .name = "ep93xx-wdt", 133 }, 134 .probe = ep93xx_wdt_probe, 135 }; 136 137 module_platform_driver(ep93xx_wdt_driver); 138 139 MODULE_AUTHOR("Ray Lehtiniemi <rayl@mail.com>"); 140 MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>"); 141 MODULE_AUTHOR("H Hartley Sweeten <hsweeten@visionengravers.com>"); 142 MODULE_DESCRIPTION("EP93xx Watchdog"); 143 MODULE_LICENSE("GPL"); 144