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(val: 0xaaaa, addr: 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(val: 0xaa55, addr: 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(val: 0x5555, addr: 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, size: sizeof(*priv), GFP_KERNEL); |
96 | if (!priv) |
97 | return -ENOMEM; |
98 | |
99 | priv->mmio = devm_platform_ioremap_resource(pdev, index: 0); |
100 | if (IS_ERR(ptr: priv->mmio)) |
101 | return PTR_ERR(ptr: priv->mmio); |
102 | |
103 | val = readl(addr: 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_parm: timeout, dev); |
117 | |
118 | watchdog_set_drvdata(wdd, data: 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 | |