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 29static bool nowayout = WATCHDOG_NOWAYOUT; 30module_param(nowayout, bool, 0); 31MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started"); 32 33static unsigned int timeout; 34module_param(timeout, uint, 0); 35MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds."); 36 37#define EP93XX_WATCHDOG 0x00 38#define EP93XX_WDSTATUS 0x04 39 40struct ep93xx_wdt_priv { 41 void __iomem *mmio; 42 struct watchdog_device wdd; 43}; 44 45static 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 54static 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 63static 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 72static 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 80static 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 87static 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 130static struct platform_driver ep93xx_wdt_driver = { 131 .driver = { 132 .name = "ep93xx-wdt", 133 }, 134 .probe = ep93xx_wdt_probe, 135}; 136 137module_platform_driver(ep93xx_wdt_driver); 138 139MODULE_AUTHOR("Ray Lehtiniemi <rayl@mail.com>"); 140MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>"); 141MODULE_AUTHOR("H Hartley Sweeten <hsweeten@visionengravers.com>"); 142MODULE_DESCRIPTION("EP93xx Watchdog"); 143MODULE_LICENSE("GPL"); 144