7f44f24e38849800358409e8d49935e0d893e65e
[15.05/openwrt.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