xref: /openbmc/linux/drivers/watchdog/dw_wdt.c (revision a2cce7a9)
1 /*
2  * Copyright 2010-2011 Picochip Ltd., Jamie Iles
3  * http://www.picochip.com
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version
8  * 2 of the License, or (at your option) any later version.
9  *
10  * This file implements a driver for the Synopsys DesignWare watchdog device
11  * in the many subsystems. The watchdog has 16 different timeout periods
12  * and these are a function of the input clock frequency.
13  *
14  * The DesignWare watchdog cannot be stopped once it has been started so we
15  * use a software timer to implement a ping that will keep the watchdog alive.
16  * If we receive an expected close for the watchdog then we keep the timer
17  * running, otherwise the timer is stopped and the watchdog will expire.
18  */
19 
20 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
21 
22 #include <linux/bitops.h>
23 #include <linux/clk.h>
24 #include <linux/delay.h>
25 #include <linux/device.h>
26 #include <linux/err.h>
27 #include <linux/fs.h>
28 #include <linux/io.h>
29 #include <linux/kernel.h>
30 #include <linux/miscdevice.h>
31 #include <linux/module.h>
32 #include <linux/moduleparam.h>
33 #include <linux/notifier.h>
34 #include <linux/of.h>
35 #include <linux/pm.h>
36 #include <linux/platform_device.h>
37 #include <linux/reboot.h>
38 #include <linux/timer.h>
39 #include <linux/uaccess.h>
40 #include <linux/watchdog.h>
41 
42 #define WDOG_CONTROL_REG_OFFSET		    0x00
43 #define WDOG_CONTROL_REG_WDT_EN_MASK	    0x01
44 #define WDOG_TIMEOUT_RANGE_REG_OFFSET	    0x04
45 #define WDOG_TIMEOUT_RANGE_TOPINIT_SHIFT    4
46 #define WDOG_CURRENT_COUNT_REG_OFFSET	    0x08
47 #define WDOG_COUNTER_RESTART_REG_OFFSET     0x0c
48 #define WDOG_COUNTER_RESTART_KICK_VALUE	    0x76
49 
50 /* The maximum TOP (timeout period) value that can be set in the watchdog. */
51 #define DW_WDT_MAX_TOP		15
52 
53 #define DW_WDT_DEFAULT_SECONDS	30
54 
55 static bool nowayout = WATCHDOG_NOWAYOUT;
56 module_param(nowayout, bool, 0);
57 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
58 		 "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
59 
60 #define WDT_TIMEOUT		(HZ / 2)
61 
62 static struct {
63 	void __iomem		*regs;
64 	struct clk		*clk;
65 	unsigned long		in_use;
66 	unsigned long		next_heartbeat;
67 	struct timer_list	timer;
68 	int			expect_close;
69 	struct notifier_block	restart_handler;
70 } dw_wdt;
71 
72 static inline int dw_wdt_is_enabled(void)
73 {
74 	return readl(dw_wdt.regs + WDOG_CONTROL_REG_OFFSET) &
75 		WDOG_CONTROL_REG_WDT_EN_MASK;
76 }
77 
78 static inline int dw_wdt_top_in_seconds(unsigned top)
79 {
80 	/*
81 	 * There are 16 possible timeout values in 0..15 where the number of
82 	 * cycles is 2 ^ (16 + i) and the watchdog counts down.
83 	 */
84 	return (1 << (16 + top)) / clk_get_rate(dw_wdt.clk);
85 }
86 
87 static int dw_wdt_get_top(void)
88 {
89 	int top = readl(dw_wdt.regs + WDOG_TIMEOUT_RANGE_REG_OFFSET) & 0xF;
90 
91 	return dw_wdt_top_in_seconds(top);
92 }
93 
94 static inline void dw_wdt_set_next_heartbeat(void)
95 {
96 	dw_wdt.next_heartbeat = jiffies + dw_wdt_get_top() * HZ;
97 }
98 
99 static void dw_wdt_keepalive(void)
100 {
101 	writel(WDOG_COUNTER_RESTART_KICK_VALUE, dw_wdt.regs +
102 	       WDOG_COUNTER_RESTART_REG_OFFSET);
103 }
104 
105 static int dw_wdt_set_top(unsigned top_s)
106 {
107 	int i, top_val = DW_WDT_MAX_TOP;
108 
109 	/*
110 	 * Iterate over the timeout values until we find the closest match. We
111 	 * always look for >=.
112 	 */
113 	for (i = 0; i <= DW_WDT_MAX_TOP; ++i)
114 		if (dw_wdt_top_in_seconds(i) >= top_s) {
115 			top_val = i;
116 			break;
117 		}
118 
119 	/*
120 	 * Set the new value in the watchdog.  Some versions of dw_wdt
121 	 * have have TOPINIT in the TIMEOUT_RANGE register (as per
122 	 * CP_WDT_DUAL_TOP in WDT_COMP_PARAMS_1).  On those we
123 	 * effectively get a pat of the watchdog right here.
124 	 */
125 	writel(top_val | top_val << WDOG_TIMEOUT_RANGE_TOPINIT_SHIFT,
126 		dw_wdt.regs + WDOG_TIMEOUT_RANGE_REG_OFFSET);
127 
128 	/*
129 	 * Add an explicit pat to handle versions of the watchdog that
130 	 * don't have TOPINIT.  This won't hurt on versions that have
131 	 * it.
132 	 */
133 	dw_wdt_keepalive();
134 
135 	dw_wdt_set_next_heartbeat();
136 
137 	return dw_wdt_top_in_seconds(top_val);
138 }
139 
140 static int dw_wdt_restart_handle(struct notifier_block *this,
141 				unsigned long mode, void *cmd)
142 {
143 	u32 val;
144 
145 	writel(0, dw_wdt.regs + WDOG_TIMEOUT_RANGE_REG_OFFSET);
146 	val = readl(dw_wdt.regs + WDOG_CONTROL_REG_OFFSET);
147 	if (val & WDOG_CONTROL_REG_WDT_EN_MASK)
148 		writel(WDOG_COUNTER_RESTART_KICK_VALUE, dw_wdt.regs +
149 			WDOG_COUNTER_RESTART_REG_OFFSET);
150 	else
151 		writel(WDOG_CONTROL_REG_WDT_EN_MASK,
152 		       dw_wdt.regs + WDOG_CONTROL_REG_OFFSET);
153 
154 	/* wait for reset to assert... */
155 	mdelay(500);
156 
157 	return NOTIFY_DONE;
158 }
159 
160 static void dw_wdt_ping(unsigned long data)
161 {
162 	if (time_before(jiffies, dw_wdt.next_heartbeat) ||
163 	    (!nowayout && !dw_wdt.in_use)) {
164 		dw_wdt_keepalive();
165 		mod_timer(&dw_wdt.timer, jiffies + WDT_TIMEOUT);
166 	} else
167 		pr_crit("keepalive missed, machine will reset\n");
168 }
169 
170 static int dw_wdt_open(struct inode *inode, struct file *filp)
171 {
172 	if (test_and_set_bit(0, &dw_wdt.in_use))
173 		return -EBUSY;
174 
175 	/* Make sure we don't get unloaded. */
176 	__module_get(THIS_MODULE);
177 
178 	if (!dw_wdt_is_enabled()) {
179 		/*
180 		 * The watchdog is not currently enabled. Set the timeout to
181 		 * something reasonable and then start it.
182 		 */
183 		dw_wdt_set_top(DW_WDT_DEFAULT_SECONDS);
184 		writel(WDOG_CONTROL_REG_WDT_EN_MASK,
185 		       dw_wdt.regs + WDOG_CONTROL_REG_OFFSET);
186 	}
187 
188 	dw_wdt_set_next_heartbeat();
189 
190 	return nonseekable_open(inode, filp);
191 }
192 
193 static ssize_t dw_wdt_write(struct file *filp, const char __user *buf,
194 			    size_t len, loff_t *offset)
195 {
196 	if (!len)
197 		return 0;
198 
199 	if (!nowayout) {
200 		size_t i;
201 
202 		dw_wdt.expect_close = 0;
203 
204 		for (i = 0; i < len; ++i) {
205 			char c;
206 
207 			if (get_user(c, buf + i))
208 				return -EFAULT;
209 
210 			if (c == 'V') {
211 				dw_wdt.expect_close = 1;
212 				break;
213 			}
214 		}
215 	}
216 
217 	dw_wdt_set_next_heartbeat();
218 	dw_wdt_keepalive();
219 	mod_timer(&dw_wdt.timer, jiffies + WDT_TIMEOUT);
220 
221 	return len;
222 }
223 
224 static u32 dw_wdt_time_left(void)
225 {
226 	return readl(dw_wdt.regs + WDOG_CURRENT_COUNT_REG_OFFSET) /
227 		clk_get_rate(dw_wdt.clk);
228 }
229 
230 static const struct watchdog_info dw_wdt_ident = {
231 	.options	= WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT |
232 			  WDIOF_MAGICCLOSE,
233 	.identity	= "Synopsys DesignWare Watchdog",
234 };
235 
236 static long dw_wdt_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
237 {
238 	unsigned long val;
239 	int timeout;
240 
241 	switch (cmd) {
242 	case WDIOC_GETSUPPORT:
243 		return copy_to_user((void __user *)arg, &dw_wdt_ident,
244 				    sizeof(dw_wdt_ident)) ? -EFAULT : 0;
245 
246 	case WDIOC_GETSTATUS:
247 	case WDIOC_GETBOOTSTATUS:
248 		return put_user(0, (int __user *)arg);
249 
250 	case WDIOC_KEEPALIVE:
251 		dw_wdt_set_next_heartbeat();
252 		return 0;
253 
254 	case WDIOC_SETTIMEOUT:
255 		if (get_user(val, (int __user *)arg))
256 			return -EFAULT;
257 		timeout = dw_wdt_set_top(val);
258 		return put_user(timeout , (int __user *)arg);
259 
260 	case WDIOC_GETTIMEOUT:
261 		return put_user(dw_wdt_get_top(), (int __user *)arg);
262 
263 	case WDIOC_GETTIMELEFT:
264 		/* Get the time left until expiry. */
265 		if (get_user(val, (int __user *)arg))
266 			return -EFAULT;
267 		return put_user(dw_wdt_time_left(), (int __user *)arg);
268 
269 	default:
270 		return -ENOTTY;
271 	}
272 }
273 
274 static int dw_wdt_release(struct inode *inode, struct file *filp)
275 {
276 	clear_bit(0, &dw_wdt.in_use);
277 
278 	if (!dw_wdt.expect_close) {
279 		del_timer(&dw_wdt.timer);
280 
281 		if (!nowayout)
282 			pr_crit("unexpected close, system will reboot soon\n");
283 		else
284 			pr_crit("watchdog cannot be disabled, system will reboot soon\n");
285 	}
286 
287 	dw_wdt.expect_close = 0;
288 
289 	return 0;
290 }
291 
292 #ifdef CONFIG_PM_SLEEP
293 static int dw_wdt_suspend(struct device *dev)
294 {
295 	clk_disable_unprepare(dw_wdt.clk);
296 
297 	return 0;
298 }
299 
300 static int dw_wdt_resume(struct device *dev)
301 {
302 	int err = clk_prepare_enable(dw_wdt.clk);
303 
304 	if (err)
305 		return err;
306 
307 	dw_wdt_keepalive();
308 
309 	return 0;
310 }
311 #endif /* CONFIG_PM_SLEEP */
312 
313 static SIMPLE_DEV_PM_OPS(dw_wdt_pm_ops, dw_wdt_suspend, dw_wdt_resume);
314 
315 static const struct file_operations wdt_fops = {
316 	.owner		= THIS_MODULE,
317 	.llseek		= no_llseek,
318 	.open		= dw_wdt_open,
319 	.write		= dw_wdt_write,
320 	.unlocked_ioctl	= dw_wdt_ioctl,
321 	.release	= dw_wdt_release
322 };
323 
324 static struct miscdevice dw_wdt_miscdev = {
325 	.fops		= &wdt_fops,
326 	.name		= "watchdog",
327 	.minor		= WATCHDOG_MINOR,
328 };
329 
330 static int dw_wdt_drv_probe(struct platform_device *pdev)
331 {
332 	int ret;
333 	struct resource *mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
334 
335 	dw_wdt.regs = devm_ioremap_resource(&pdev->dev, mem);
336 	if (IS_ERR(dw_wdt.regs))
337 		return PTR_ERR(dw_wdt.regs);
338 
339 	dw_wdt.clk = devm_clk_get(&pdev->dev, NULL);
340 	if (IS_ERR(dw_wdt.clk))
341 		return PTR_ERR(dw_wdt.clk);
342 
343 	ret = clk_prepare_enable(dw_wdt.clk);
344 	if (ret)
345 		return ret;
346 
347 	ret = misc_register(&dw_wdt_miscdev);
348 	if (ret)
349 		goto out_disable_clk;
350 
351 	dw_wdt.restart_handler.notifier_call = dw_wdt_restart_handle;
352 	dw_wdt.restart_handler.priority = 128;
353 	ret = register_restart_handler(&dw_wdt.restart_handler);
354 	if (ret)
355 		pr_warn("cannot register restart handler\n");
356 
357 	dw_wdt_set_next_heartbeat();
358 	setup_timer(&dw_wdt.timer, dw_wdt_ping, 0);
359 	mod_timer(&dw_wdt.timer, jiffies + WDT_TIMEOUT);
360 
361 	return 0;
362 
363 out_disable_clk:
364 	clk_disable_unprepare(dw_wdt.clk);
365 
366 	return ret;
367 }
368 
369 static int dw_wdt_drv_remove(struct platform_device *pdev)
370 {
371 	unregister_restart_handler(&dw_wdt.restart_handler);
372 
373 	misc_deregister(&dw_wdt_miscdev);
374 
375 	clk_disable_unprepare(dw_wdt.clk);
376 
377 	return 0;
378 }
379 
380 #ifdef CONFIG_OF
381 static const struct of_device_id dw_wdt_of_match[] = {
382 	{ .compatible = "snps,dw-wdt", },
383 	{ /* sentinel */ }
384 };
385 MODULE_DEVICE_TABLE(of, dw_wdt_of_match);
386 #endif
387 
388 static struct platform_driver dw_wdt_driver = {
389 	.probe		= dw_wdt_drv_probe,
390 	.remove		= dw_wdt_drv_remove,
391 	.driver		= {
392 		.name	= "dw_wdt",
393 		.of_match_table = of_match_ptr(dw_wdt_of_match),
394 		.pm	= &dw_wdt_pm_ops,
395 	},
396 };
397 
398 module_platform_driver(dw_wdt_driver);
399 
400 MODULE_AUTHOR("Jamie Iles");
401 MODULE_DESCRIPTION("Synopsys DesignWare Watchdog Driver");
402 MODULE_LICENSE("GPL");
403