brcm2708: update to v3.18
[openwrt/staging/wigyori.git] / target / linux / brcm2708 / patches-3.18 / 0005-bcm2708-watchdog-driver.patch
1 From 633194396e40f919974dd8b81e97ddfea463b733 Mon Sep 17 00:00:00 2001
2 From: popcornmix <popcornmix@gmail.com>
3 Date: Wed, 1 May 2013 19:54:32 +0100
4 Subject: [PATCH 005/114] bcm2708 watchdog driver
5
6 Signed-off-by: popcornmix <popcornmix@gmail.com>
7 ---
8 drivers/watchdog/Kconfig | 6 +
9 drivers/watchdog/Makefile | 1 +
10 drivers/watchdog/bcm2708_wdog.c | 382 ++++++++++++++++++++++++++++++++++++++++
11 3 files changed, 389 insertions(+)
12 create mode 100644 drivers/watchdog/bcm2708_wdog.c
13
14 diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
15 index d0107d4..ff56894 100644
16 --- a/drivers/watchdog/Kconfig
17 +++ b/drivers/watchdog/Kconfig
18 @@ -452,6 +452,12 @@ config RETU_WATCHDOG
19 To compile this driver as a module, choose M here: the
20 module will be called retu_wdt.
21
22 +config BCM2708_WDT
23 + tristate "BCM2708 Watchdog"
24 + depends on ARCH_BCM2708
25 + help
26 + Enables BCM2708 watchdog support.
27 +
28 config MOXART_WDT
29 tristate "MOXART watchdog"
30 depends on ARCH_MOXART
31 diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
32 index c569ec8..10e0665 100644
33 --- a/drivers/watchdog/Makefile
34 +++ b/drivers/watchdog/Makefile
35 @@ -56,6 +56,7 @@ obj-$(CONFIG_TS72XX_WATCHDOG) += ts72xx_wdt.o
36 obj-$(CONFIG_IMX2_WDT) += imx2_wdt.o
37 obj-$(CONFIG_UX500_WATCHDOG) += ux500_wdt.o
38 obj-$(CONFIG_RETU_WATCHDOG) += retu_wdt.o
39 +obj-$(CONFIG_BCM2708_WDT) += bcm2708_wdog.o
40 obj-$(CONFIG_BCM2835_WDT) += bcm2835_wdt.o
41 obj-$(CONFIG_MOXART_WDT) += moxart_wdt.o
42 obj-$(CONFIG_SIRFSOC_WATCHDOG) += sirfsoc_wdt.o
43 diff --git a/drivers/watchdog/bcm2708_wdog.c b/drivers/watchdog/bcm2708_wdog.c
44 new file mode 100644
45 index 0000000..8a27d68
46 --- /dev/null
47 +++ b/drivers/watchdog/bcm2708_wdog.c
48 @@ -0,0 +1,382 @@
49 +/*
50 + * Broadcom BCM2708 watchdog driver.
51 + *
52 + * (c) Copyright 2010 Broadcom Europe Ltd
53 + *
54 + * This program is free software; you can redistribute it and/or
55 + * modify it under the terms of the GNU General Public License
56 + * as published by the Free Software Foundation; either version
57 + * 2 of the License, or (at your option) any later version.
58 + *
59 + * BCM2708 watchdog driver. Loosely based on wdt driver.
60 + */
61 +
62 +#include <linux/interrupt.h>
63 +#include <linux/module.h>
64 +#include <linux/moduleparam.h>
65 +#include <linux/types.h>
66 +#include <linux/miscdevice.h>
67 +#include <linux/watchdog.h>
68 +#include <linux/fs.h>
69 +#include <linux/ioport.h>
70 +#include <linux/notifier.h>
71 +#include <linux/reboot.h>
72 +#include <linux/init.h>
73 +#include <linux/io.h>
74 +#include <linux/uaccess.h>
75 +#include <mach/platform.h>
76 +
77 +#define SECS_TO_WDOG_TICKS(x) ((x) << 16)
78 +#define WDOG_TICKS_TO_SECS(x) ((x) >> 16)
79 +
80 +static unsigned long wdog_is_open;
81 +static uint32_t wdog_ticks; /* Ticks to load into wdog timer */
82 +static char expect_close;
83 +
84 +/*
85 + * Module parameters
86 + */
87 +
88 +#define WD_TIMO 10 /* Default heartbeat = 60 seconds */
89 +static int heartbeat = WD_TIMO; /* Heartbeat in seconds */
90 +
91 +module_param(heartbeat, int, 0);
92 +MODULE_PARM_DESC(heartbeat,
93 + "Watchdog heartbeat in seconds. (0 < heartbeat < 65536, default="
94 + __MODULE_STRING(WD_TIMO) ")");
95 +
96 +static int nowayout = WATCHDOG_NOWAYOUT;
97 +module_param(nowayout, int, 0);
98 +MODULE_PARM_DESC(nowayout,
99 + "Watchdog cannot be stopped once started (default="
100 + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
101 +
102 +static DEFINE_SPINLOCK(wdog_lock);
103 +
104 +/**
105 + * Start the watchdog driver.
106 + */
107 +
108 +static int wdog_start(unsigned long timeout)
109 +{
110 + uint32_t cur;
111 + unsigned long flags;
112 + spin_lock_irqsave(&wdog_lock, flags);
113 +
114 + /* enable the watchdog */
115 + iowrite32(PM_PASSWORD | (timeout & PM_WDOG_TIME_SET),
116 + __io_address(PM_WDOG));
117 + cur = ioread32(__io_address(PM_RSTC));
118 + iowrite32(PM_PASSWORD | (cur & PM_RSTC_WRCFG_CLR) |
119 + PM_RSTC_WRCFG_FULL_RESET, __io_address(PM_RSTC));
120 +
121 + spin_unlock_irqrestore(&wdog_lock, flags);
122 + return 0;
123 +}
124 +
125 +/**
126 + * Stop the watchdog driver.
127 + */
128 +
129 +static int wdog_stop(void)
130 +{
131 + iowrite32(PM_PASSWORD | PM_RSTC_RESET, __io_address(PM_RSTC));
132 + printk(KERN_INFO "watchdog stopped\n");
133 + return 0;
134 +}
135 +
136 +/**
137 + * Reload counter one with the watchdog heartbeat. We don't bother
138 + * reloading the cascade counter.
139 + */
140 +
141 +static void wdog_ping(void)
142 +{
143 + wdog_start(wdog_ticks);
144 +}
145 +
146 +/**
147 + * @t: the new heartbeat value that needs to be set.
148 + *
149 + * Set a new heartbeat value for the watchdog device. If the heartbeat
150 + * value is incorrect we keep the old value and return -EINVAL. If
151 + * successful we return 0.
152 + */
153 +
154 +static int wdog_set_heartbeat(int t)
155 +{
156 + if (t < 1 || t > WDOG_TICKS_TO_SECS(PM_WDOG_TIME_SET))
157 + return -EINVAL;
158 +
159 + heartbeat = t;
160 + wdog_ticks = SECS_TO_WDOG_TICKS(t);
161 + return 0;
162 +}
163 +
164 +/**
165 + * @file: file handle to the watchdog
166 + * @buf: buffer to write (unused as data does not matter here
167 + * @count: count of bytes
168 + * @ppos: pointer to the position to write. No seeks allowed
169 + *
170 + * A write to a watchdog device is defined as a keepalive signal.
171 + *
172 + * if 'nowayout' is set then normally a close() is ignored. But
173 + * if you write 'V' first then the close() will stop the timer.
174 + */
175 +
176 +static ssize_t wdog_write(struct file *file, const char __user *buf,
177 + size_t count, loff_t *ppos)
178 +{
179 + if (count) {
180 + if (!nowayout) {
181 + size_t i;
182 +
183 + /* In case it was set long ago */
184 + expect_close = 0;
185 +
186 + for (i = 0; i != count; i++) {
187 + char c;
188 + if (get_user(c, buf + i))
189 + return -EFAULT;
190 + if (c == 'V')
191 + expect_close = 42;
192 + }
193 + }
194 + wdog_ping();
195 + }
196 + return count;
197 +}
198 +
199 +static int wdog_get_status(void)
200 +{
201 + unsigned long flags;
202 + int status = 0;
203 + spin_lock_irqsave(&wdog_lock, flags);
204 + /* FIXME: readback reset reason */
205 + spin_unlock_irqrestore(&wdog_lock, flags);
206 + return status;
207 +}
208 +
209 +static uint32_t wdog_get_remaining(void)
210 +{
211 + uint32_t ret = ioread32(__io_address(PM_WDOG));
212 + return ret & PM_WDOG_TIME_SET;
213 +}
214 +
215 +/**
216 + * @file: file handle to the device
217 + * @cmd: watchdog command
218 + * @arg: argument pointer
219 + *
220 + * The watchdog API defines a common set of functions for all watchdogs
221 + * according to their available features. We only actually usefully support
222 + * querying capabilities and current status.
223 + */
224 +
225 +static long wdog_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
226 +{
227 + void __user *argp = (void __user *)arg;
228 + int __user *p = argp;
229 + int new_heartbeat;
230 + int status;
231 + int options;
232 + uint32_t remaining;
233 +
234 + struct watchdog_info ident = {
235 + .options = WDIOF_SETTIMEOUT|
236 + WDIOF_MAGICCLOSE|
237 + WDIOF_KEEPALIVEPING,
238 + .firmware_version = 1,
239 + .identity = "BCM2708",
240 + };
241 +
242 + switch (cmd) {
243 + case WDIOC_GETSUPPORT:
244 + return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
245 + case WDIOC_GETSTATUS:
246 + status = wdog_get_status();
247 + return put_user(status, p);
248 + case WDIOC_GETBOOTSTATUS:
249 + return put_user(0, p);
250 + case WDIOC_KEEPALIVE:
251 + wdog_ping();
252 + return 0;
253 + case WDIOC_SETTIMEOUT:
254 + if (get_user(new_heartbeat, p))
255 + return -EFAULT;
256 + if (wdog_set_heartbeat(new_heartbeat))
257 + return -EINVAL;
258 + wdog_ping();
259 + /* Fall */
260 + case WDIOC_GETTIMEOUT:
261 + return put_user(heartbeat, p);
262 + case WDIOC_GETTIMELEFT:
263 + remaining = WDOG_TICKS_TO_SECS(wdog_get_remaining());
264 + return put_user(remaining, p);
265 + case WDIOC_SETOPTIONS:
266 + if (get_user(options, p))
267 + return -EFAULT;
268 + if (options & WDIOS_DISABLECARD)
269 + wdog_stop();
270 + if (options & WDIOS_ENABLECARD)
271 + wdog_start(wdog_ticks);
272 + return 0;
273 + default:
274 + return -ENOTTY;
275 + }
276 +}
277 +
278 +/**
279 + * @inode: inode of device
280 + * @file: file handle to device
281 + *
282 + * The watchdog device has been opened. The watchdog device is single
283 + * open and on opening we load the counters.
284 + */
285 +
286 +static int wdog_open(struct inode *inode, struct file *file)
287 +{
288 + if (test_and_set_bit(0, &wdog_is_open))
289 + return -EBUSY;
290 + /*
291 + * Activate
292 + */
293 + wdog_start(wdog_ticks);
294 + return nonseekable_open(inode, file);
295 +}
296 +
297 +/**
298 + * @inode: inode to board
299 + * @file: file handle to board
300 + *
301 + * The watchdog has a configurable API. There is a religious dispute
302 + * between people who want their watchdog to be able to shut down and
303 + * those who want to be sure if the watchdog manager dies the machine
304 + * reboots. In the former case we disable the counters, in the latter
305 + * case you have to open it again very soon.
306 + */
307 +
308 +static int wdog_release(struct inode *inode, struct file *file)
309 +{
310 + if (expect_close == 42) {
311 + wdog_stop();
312 + } else {
313 + printk(KERN_CRIT
314 + "wdt: WDT device closed unexpectedly. WDT will not stop!\n");
315 + wdog_ping();
316 + }
317 + clear_bit(0, &wdog_is_open);
318 + expect_close = 0;
319 + return 0;
320 +}
321 +
322 +/**
323 + * @this: our notifier block
324 + * @code: the event being reported
325 + * @unused: unused
326 + *
327 + * Our notifier is called on system shutdowns. Turn the watchdog
328 + * off so that it does not fire during the next reboot.
329 + */
330 +
331 +static int wdog_notify_sys(struct notifier_block *this, unsigned long code,
332 + void *unused)
333 +{
334 + if (code == SYS_DOWN || code == SYS_HALT)
335 + wdog_stop();
336 + return NOTIFY_DONE;
337 +}
338 +
339 +/*
340 + * Kernel Interfaces
341 + */
342 +
343 +
344 +static const struct file_operations wdog_fops = {
345 + .owner = THIS_MODULE,
346 + .llseek = no_llseek,
347 + .write = wdog_write,
348 + .unlocked_ioctl = wdog_ioctl,
349 + .open = wdog_open,
350 + .release = wdog_release,
351 +};
352 +
353 +static struct miscdevice wdog_miscdev = {
354 + .minor = WATCHDOG_MINOR,
355 + .name = "watchdog",
356 + .fops = &wdog_fops,
357 +};
358 +
359 +/*
360 + * The WDT card needs to learn about soft shutdowns in order to
361 + * turn the timebomb registers off.
362 + */
363 +
364 +static struct notifier_block wdog_notifier = {
365 + .notifier_call = wdog_notify_sys,
366 +};
367 +
368 +/**
369 + * cleanup_module:
370 + *
371 + * Unload the watchdog. You cannot do this with any file handles open.
372 + * If your watchdog is set to continue ticking on close and you unload
373 + * it, well it keeps ticking. We won't get the interrupt but the board
374 + * will not touch PC memory so all is fine. You just have to load a new
375 + * module in 60 seconds or reboot.
376 + */
377 +
378 +static void __exit wdog_exit(void)
379 +{
380 + misc_deregister(&wdog_miscdev);
381 + unregister_reboot_notifier(&wdog_notifier);
382 +}
383 +
384 +static int __init wdog_init(void)
385 +{
386 + int ret;
387 +
388 + /* Check that the heartbeat value is within it's range;
389 + if not reset to the default */
390 + if (wdog_set_heartbeat(heartbeat)) {
391 + wdog_set_heartbeat(WD_TIMO);
392 + printk(KERN_INFO "bcm2708_wdog: heartbeat value must be "
393 + "0 < heartbeat < %d, using %d\n",
394 + WDOG_TICKS_TO_SECS(PM_WDOG_TIME_SET),
395 + WD_TIMO);
396 + }
397 +
398 + ret = register_reboot_notifier(&wdog_notifier);
399 + if (ret) {
400 + printk(KERN_ERR
401 + "wdt: cannot register reboot notifier (err=%d)\n", ret);
402 + goto out_reboot;
403 + }
404 +
405 + ret = misc_register(&wdog_miscdev);
406 + if (ret) {
407 + printk(KERN_ERR
408 + "wdt: cannot register miscdev on minor=%d (err=%d)\n",
409 + WATCHDOG_MINOR, ret);
410 + goto out_misc;
411 + }
412 +
413 + printk(KERN_INFO "bcm2708 watchdog, heartbeat=%d sec (nowayout=%d)\n",
414 + heartbeat, nowayout);
415 + return 0;
416 +
417 +out_misc:
418 + unregister_reboot_notifier(&wdog_notifier);
419 +out_reboot:
420 + return ret;
421 +}
422 +
423 +module_init(wdog_init);
424 +module_exit(wdog_exit);
425 +
426 +MODULE_AUTHOR("Luke Diamand");
427 +MODULE_DESCRIPTION("Driver for BCM2708 watchdog");
428 +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
429 +MODULE_ALIAS_MISCDEV(TEMP_MINOR);
430 +MODULE_LICENSE("GPL");
431 --
432 1.8.3.2
433