brcm2708: update 4.1 patches
[openwrt/openwrt.git] / target / linux / brcm2708 / patches-4.1 / 0005-bcm2708-watchdog-driver.patch
1 From 5ec663250bcf18ee6d5792ab03ca49b15e2ea882 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/171] 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");