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