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