Root/target/linux/brcm2708/patches-3.3/0003-bcm2708-watchdog-driver.patch

1From ed8c2d720954efc5a440912292ed11da2f50aaea Mon Sep 17 00:00:00 2001
2From: popcornmix <popcornmix@gmail.com>
3Date: Tue, 17 Jan 2012 19:20:57 +0000
4Subject: [PATCH 3/7] bcm2708 watchdog driver
5
6Signed-off-by: popcornmix <popcornmix@gmail.com>
7---
8 drivers/watchdog/Kconfig | 6 +
9 drivers/watchdog/Makefile | 1 +
10 drivers/watchdog/bcm2708_wdog.c | 385 +++++++++++++++++++++++++++++++++++++++
11 3 files changed, 392 insertions(+), 0 deletions(-)
12 create mode 100644 drivers/watchdog/bcm2708_wdog.c
13
14diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
15index 86b0735..7675ebc 100644
16--- a/drivers/watchdog/Kconfig
17+++ b/drivers/watchdog/Kconfig
18@@ -348,6 +348,12 @@ config IMX2_WDT
19       To compile this driver as a module, choose M here: the
20       module will be called imx2_wdt.
21 
22+config BCM2708_WDT
23+ tristate "BCM2708 Watchdog"
24+ depends on ARCH_BCM2708
25+ help
26+ Enables BCM2708 watchdog support.
27+
28 # AVR32 Architecture
29 
30 config AT32AP700X_WDT
31diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
32index 55bd574..803f0bc 100644
33--- a/drivers/watchdog/Makefile
34+++ b/drivers/watchdog/Makefile
35@@ -54,6 +54,7 @@ obj-$(CONFIG_NUC900_WATCHDOG) += nuc900_wdt.o
36 obj-$(CONFIG_ADX_WATCHDOG) += adx_wdt.o
37 obj-$(CONFIG_TS72XX_WATCHDOG) += ts72xx_wdt.o
38 obj-$(CONFIG_IMX2_WDT) += imx2_wdt.o
39+obj-$(CONFIG_BCM2708_WDT) += bcm2708_wdog.o
40 
41 # AVR32 Architecture
42 obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o
43diff --git a/drivers/watchdog/bcm2708_wdog.c b/drivers/watchdog/bcm2708_wdog.c
44new file mode 100644
45index 0000000..dd33c35
46--- /dev/null
47+++ b/drivers/watchdog/bcm2708_wdog.c
48@@ -0,0 +1,385 @@
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+#include <asm/system.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");
433+
434--
4351.7.5.4
436
437

Archive Download this file



interactive