diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2009-01-12 18:56:33 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-01-12 18:56:33 -0500 |
commit | 87aa08b7fee79dac908c80f7593007243448ef89 (patch) | |
tree | 0fce69996e4be3cfce35300831584a76a4ff1519 /drivers | |
parent | 3b1b71950b81c4a20ae63c2322383ab4932cdabd (diff) | |
parent | 618efba999d0e7f4bcde93231dcb9a748223c6e3 (diff) |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/wim/linux-2.6-watchdog
* git://git.kernel.org/pub/scm/linux/kernel/git/wim/linux-2.6-watchdog:
[WATCHDOG] Pika Warp appliance watchdog timer
[WATCHDOG] Enable watchdog timer on GE Fanuc's SBC610
[WATCHDOG] Basic support for GE Fanuc's FPGA based watchdog timer
[WATCHDOG] wm8350: Fix section annotations
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/watchdog/Kconfig | 14 | ||||
-rw-r--r-- | drivers/watchdog/Makefile | 2 | ||||
-rw-r--r-- | drivers/watchdog/gef_wdt.c | 330 | ||||
-rw-r--r-- | drivers/watchdog/pika_wdt.c | 301 | ||||
-rw-r--r-- | drivers/watchdog/wm8350_wdt.c | 6 |
5 files changed, 650 insertions, 3 deletions
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index ec68c741b564..3efa12f9ee50 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig | |||
@@ -770,6 +770,12 @@ config TXX9_WDT | |||
770 | 770 | ||
771 | # POWERPC Architecture | 771 | # POWERPC Architecture |
772 | 772 | ||
773 | config GEF_WDT | ||
774 | tristate "GE Fanuc Watchdog Timer" | ||
775 | depends on GEF_SBC610 | ||
776 | ---help--- | ||
777 | Watchdog timer found in a number of GE Fanuc single board computers. | ||
778 | |||
773 | config MPC5200_WDT | 779 | config MPC5200_WDT |
774 | tristate "MPC5200 Watchdog Timer" | 780 | tristate "MPC5200 Watchdog Timer" |
775 | depends on PPC_MPC52xx | 781 | depends on PPC_MPC52xx |
@@ -790,6 +796,14 @@ config MV64X60_WDT | |||
790 | tristate "MV64X60 (Marvell Discovery) Watchdog Timer" | 796 | tristate "MV64X60 (Marvell Discovery) Watchdog Timer" |
791 | depends on MV64X60 | 797 | depends on MV64X60 |
792 | 798 | ||
799 | config PIKA_WDT | ||
800 | tristate "PIKA FPGA Watchdog" | ||
801 | depends on WARP | ||
802 | default y | ||
803 | help | ||
804 | This enables the watchdog in the PIKA FPGA. Currently used on | ||
805 | the Warp platform. | ||
806 | |||
793 | config BOOKE_WDT | 807 | config BOOKE_WDT |
794 | bool "PowerPC Book-E Watchdog Timer" | 808 | bool "PowerPC Book-E Watchdog Timer" |
795 | depends on BOOKE || 4xx | 809 | depends on BOOKE || 4xx |
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index c19b866f5ed1..806b3eb08536 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile | |||
@@ -111,9 +111,11 @@ obj-$(CONFIG_TXX9_WDT) += txx9wdt.o | |||
111 | # PARISC Architecture | 111 | # PARISC Architecture |
112 | 112 | ||
113 | # POWERPC Architecture | 113 | # POWERPC Architecture |
114 | obj-$(CONFIG_GEF_WDT) += gef_wdt.o | ||
114 | obj-$(CONFIG_MPC5200_WDT) += mpc5200_wdt.o | 115 | obj-$(CONFIG_MPC5200_WDT) += mpc5200_wdt.o |
115 | obj-$(CONFIG_8xxx_WDT) += mpc8xxx_wdt.o | 116 | obj-$(CONFIG_8xxx_WDT) += mpc8xxx_wdt.o |
116 | obj-$(CONFIG_MV64X60_WDT) += mv64x60_wdt.o | 117 | obj-$(CONFIG_MV64X60_WDT) += mv64x60_wdt.o |
118 | obj-$(CONFIG_PIKA_WDT) += pika_wdt.o | ||
117 | obj-$(CONFIG_BOOKE_WDT) += booke_wdt.o | 119 | obj-$(CONFIG_BOOKE_WDT) += booke_wdt.o |
118 | 120 | ||
119 | # PPC64 Architecture | 121 | # PPC64 Architecture |
diff --git a/drivers/watchdog/gef_wdt.c b/drivers/watchdog/gef_wdt.c new file mode 100644 index 000000000000..f0c2b7a1a175 --- /dev/null +++ b/drivers/watchdog/gef_wdt.c | |||
@@ -0,0 +1,330 @@ | |||
1 | /* | ||
2 | * GE Fanuc watchdog userspace interface | ||
3 | * | ||
4 | * Author: Martyn Welch <martyn.welch@gefanuc.com> | ||
5 | * | ||
6 | * Copyright 2008 GE Fanuc Intelligent Platforms Embedded Systems, Inc. | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify it | ||
9 | * under the terms of the GNU General Public License as published by the | ||
10 | * Free Software Foundation; either version 2 of the License, or (at your | ||
11 | * option) any later version. | ||
12 | * | ||
13 | * Based on: mv64x60_wdt.c (MV64X60 watchdog userspace interface) | ||
14 | * Author: James Chapman <jchapman@katalix.com> | ||
15 | */ | ||
16 | |||
17 | /* TODO: | ||
18 | * This driver does not provide support for the hardwares capability of sending | ||
19 | * an interrupt at a programmable threshold. | ||
20 | * | ||
21 | * This driver currently can only support 1 watchdog - there are 2 in the | ||
22 | * hardware that this driver supports. Thus one could be configured as a | ||
23 | * process-based watchdog (via /dev/watchdog), the second (using the interrupt | ||
24 | * capabilities) a kernel-based watchdog. | ||
25 | */ | ||
26 | |||
27 | #include <linux/kernel.h> | ||
28 | #include <linux/compiler.h> | ||
29 | #include <linux/init.h> | ||
30 | #include <linux/module.h> | ||
31 | #include <linux/miscdevice.h> | ||
32 | #include <linux/watchdog.h> | ||
33 | #include <linux/of.h> | ||
34 | #include <linux/of_platform.h> | ||
35 | #include <linux/io.h> | ||
36 | #include <linux/uaccess.h> | ||
37 | |||
38 | #include <sysdev/fsl_soc.h> | ||
39 | |||
40 | /* | ||
41 | * The watchdog configuration register contains a pair of 2-bit fields, | ||
42 | * 1. a reload field, bits 27-26, which triggers a reload of | ||
43 | * the countdown register, and | ||
44 | * 2. an enable field, bits 25-24, which toggles between | ||
45 | * enabling and disabling the watchdog timer. | ||
46 | * Bit 31 is a read-only field which indicates whether the | ||
47 | * watchdog timer is currently enabled. | ||
48 | * | ||
49 | * The low 24 bits contain the timer reload value. | ||
50 | */ | ||
51 | #define GEF_WDC_ENABLE_SHIFT 24 | ||
52 | #define GEF_WDC_SERVICE_SHIFT 26 | ||
53 | #define GEF_WDC_ENABLED_SHIFT 31 | ||
54 | |||
55 | #define GEF_WDC_ENABLED_TRUE 1 | ||
56 | #define GEF_WDC_ENABLED_FALSE 0 | ||
57 | |||
58 | /* Flags bits */ | ||
59 | #define GEF_WDOG_FLAG_OPENED 0 | ||
60 | |||
61 | static unsigned long wdt_flags; | ||
62 | static int wdt_status; | ||
63 | static void __iomem *gef_wdt_regs; | ||
64 | static int gef_wdt_timeout; | ||
65 | static int gef_wdt_count; | ||
66 | static unsigned int bus_clk; | ||
67 | static char expect_close; | ||
68 | static DEFINE_SPINLOCK(gef_wdt_spinlock); | ||
69 | |||
70 | static int nowayout = WATCHDOG_NOWAYOUT; | ||
71 | module_param(nowayout, int, 0); | ||
72 | MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" | ||
73 | __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); | ||
74 | |||
75 | |||
76 | static int gef_wdt_toggle_wdc(int enabled_predicate, int field_shift) | ||
77 | { | ||
78 | u32 data; | ||
79 | u32 enabled; | ||
80 | int ret = 0; | ||
81 | |||
82 | spin_lock(&gef_wdt_spinlock); | ||
83 | data = ioread32be(gef_wdt_regs); | ||
84 | enabled = (data >> GEF_WDC_ENABLED_SHIFT) & 1; | ||
85 | |||
86 | /* only toggle the requested field if enabled state matches predicate */ | ||
87 | if ((enabled ^ enabled_predicate) == 0) { | ||
88 | /* We write a 1, then a 2 -- to the appropriate field */ | ||
89 | data = (1 << field_shift) | gef_wdt_count; | ||
90 | iowrite32be(data, gef_wdt_regs); | ||
91 | |||
92 | data = (2 << field_shift) | gef_wdt_count; | ||
93 | iowrite32be(data, gef_wdt_regs); | ||
94 | ret = 1; | ||
95 | } | ||
96 | spin_unlock(&gef_wdt_spinlock); | ||
97 | |||
98 | return ret; | ||
99 | } | ||
100 | |||
101 | static void gef_wdt_service(void) | ||
102 | { | ||
103 | gef_wdt_toggle_wdc(GEF_WDC_ENABLED_TRUE, | ||
104 | GEF_WDC_SERVICE_SHIFT); | ||
105 | } | ||
106 | |||
107 | static void gef_wdt_handler_enable(void) | ||
108 | { | ||
109 | if (gef_wdt_toggle_wdc(GEF_WDC_ENABLED_FALSE, | ||
110 | GEF_WDC_ENABLE_SHIFT)) { | ||
111 | gef_wdt_service(); | ||
112 | printk(KERN_NOTICE "gef_wdt: watchdog activated\n"); | ||
113 | } | ||
114 | } | ||
115 | |||
116 | static void gef_wdt_handler_disable(void) | ||
117 | { | ||
118 | if (gef_wdt_toggle_wdc(GEF_WDC_ENABLED_TRUE, | ||
119 | GEF_WDC_ENABLE_SHIFT)) | ||
120 | printk(KERN_NOTICE "gef_wdt: watchdog deactivated\n"); | ||
121 | } | ||
122 | |||
123 | static void gef_wdt_set_timeout(unsigned int timeout) | ||
124 | { | ||
125 | /* maximum bus cycle count is 0xFFFFFFFF */ | ||
126 | if (timeout > 0xFFFFFFFF / bus_clk) | ||
127 | timeout = 0xFFFFFFFF / bus_clk; | ||
128 | |||
129 | /* Register only holds upper 24 bits, bit shifted into lower 24 */ | ||
130 | gef_wdt_count = (timeout * bus_clk) >> 8; | ||
131 | gef_wdt_timeout = timeout; | ||
132 | } | ||
133 | |||
134 | |||
135 | static ssize_t gef_wdt_write(struct file *file, const char __user *data, | ||
136 | size_t len, loff_t *ppos) | ||
137 | { | ||
138 | if (len) { | ||
139 | if (!nowayout) { | ||
140 | size_t i; | ||
141 | |||
142 | expect_close = 0; | ||
143 | |||
144 | for (i = 0; i != len; i++) { | ||
145 | char c; | ||
146 | if (get_user(c, data + i)) | ||
147 | return -EFAULT; | ||
148 | if (c == 'V') | ||
149 | expect_close = 42; | ||
150 | } | ||
151 | } | ||
152 | gef_wdt_service(); | ||
153 | } | ||
154 | |||
155 | return len; | ||
156 | } | ||
157 | |||
158 | static long gef_wdt_ioctl(struct file *file, unsigned int cmd, | ||
159 | unsigned long arg) | ||
160 | { | ||
161 | int timeout; | ||
162 | int options; | ||
163 | void __user *argp = (void __user *)arg; | ||
164 | static struct watchdog_info info = { | ||
165 | .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | | ||
166 | WDIOF_KEEPALIVEPING, | ||
167 | .firmware_version = 0, | ||
168 | .identity = "GE Fanuc watchdog", | ||
169 | }; | ||
170 | |||
171 | switch (cmd) { | ||
172 | case WDIOC_GETSUPPORT: | ||
173 | if (copy_to_user(argp, &info, sizeof(info))) | ||
174 | return -EFAULT; | ||
175 | break; | ||
176 | |||
177 | case WDIOC_GETSTATUS: | ||
178 | case WDIOC_GETBOOTSTATUS: | ||
179 | if (put_user(wdt_status, (int __user *)argp)) | ||
180 | return -EFAULT; | ||
181 | wdt_status &= ~WDIOF_KEEPALIVEPING; | ||
182 | break; | ||
183 | |||
184 | case WDIOC_SETOPTIONS: | ||
185 | if (get_user(options, (int __user *)argp)) | ||
186 | return -EFAULT; | ||
187 | |||
188 | if (options & WDIOS_DISABLECARD) | ||
189 | gef_wdt_handler_disable(); | ||
190 | |||
191 | if (options & WDIOS_ENABLECARD) | ||
192 | gef_wdt_handler_enable(); | ||
193 | break; | ||
194 | |||
195 | case WDIOC_KEEPALIVE: | ||
196 | gef_wdt_service(); | ||
197 | wdt_status |= WDIOF_KEEPALIVEPING; | ||
198 | break; | ||
199 | |||
200 | case WDIOC_SETTIMEOUT: | ||
201 | if (get_user(timeout, (int __user *)argp)) | ||
202 | return -EFAULT; | ||
203 | gef_wdt_set_timeout(timeout); | ||
204 | /* Fall through */ | ||
205 | |||
206 | case WDIOC_GETTIMEOUT: | ||
207 | if (put_user(gef_wdt_timeout, (int __user *)argp)) | ||
208 | return -EFAULT; | ||
209 | break; | ||
210 | |||
211 | default: | ||
212 | return -ENOTTY; | ||
213 | } | ||
214 | |||
215 | return 0; | ||
216 | } | ||
217 | |||
218 | static int gef_wdt_open(struct inode *inode, struct file *file) | ||
219 | { | ||
220 | if (test_and_set_bit(GEF_WDOG_FLAG_OPENED, &wdt_flags)) | ||
221 | return -EBUSY; | ||
222 | |||
223 | if (nowayout) | ||
224 | __module_get(THIS_MODULE); | ||
225 | |||
226 | gef_wdt_handler_enable(); | ||
227 | |||
228 | return nonseekable_open(inode, file); | ||
229 | } | ||
230 | |||
231 | static int gef_wdt_release(struct inode *inode, struct file *file) | ||
232 | { | ||
233 | if (expect_close == 42) | ||
234 | gef_wdt_handler_disable(); | ||
235 | else { | ||
236 | printk(KERN_CRIT | ||
237 | "gef_wdt: unexpected close, not stopping timer!\n"); | ||
238 | gef_wdt_service(); | ||
239 | } | ||
240 | expect_close = 0; | ||
241 | |||
242 | clear_bit(GEF_WDOG_FLAG_OPENED, &wdt_flags); | ||
243 | |||
244 | return 0; | ||
245 | } | ||
246 | |||
247 | static const struct file_operations gef_wdt_fops = { | ||
248 | .owner = THIS_MODULE, | ||
249 | .llseek = no_llseek, | ||
250 | .write = gef_wdt_write, | ||
251 | .unlocked_ioctl = gef_wdt_ioctl, | ||
252 | .open = gef_wdt_open, | ||
253 | .release = gef_wdt_release, | ||
254 | }; | ||
255 | |||
256 | static struct miscdevice gef_wdt_miscdev = { | ||
257 | .minor = WATCHDOG_MINOR, | ||
258 | .name = "watchdog", | ||
259 | .fops = &gef_wdt_fops, | ||
260 | }; | ||
261 | |||
262 | |||
263 | static int __devinit gef_wdt_probe(struct of_device *dev, | ||
264 | const struct of_device_id *match) | ||
265 | { | ||
266 | int timeout = 10; | ||
267 | u32 freq; | ||
268 | |||
269 | bus_clk = 133; /* in MHz */ | ||
270 | |||
271 | freq = fsl_get_sys_freq(); | ||
272 | if (freq > 0) | ||
273 | bus_clk = freq; | ||
274 | |||
275 | /* Map devices registers into memory */ | ||
276 | gef_wdt_regs = of_iomap(dev->node, 0); | ||
277 | if (gef_wdt_regs == NULL) | ||
278 | return -ENOMEM; | ||
279 | |||
280 | gef_wdt_set_timeout(timeout); | ||
281 | |||
282 | gef_wdt_handler_disable(); /* in case timer was already running */ | ||
283 | |||
284 | return misc_register(&gef_wdt_miscdev); | ||
285 | } | ||
286 | |||
287 | static int __devexit gef_wdt_remove(struct platform_device *dev) | ||
288 | { | ||
289 | misc_deregister(&gef_wdt_miscdev); | ||
290 | |||
291 | gef_wdt_handler_disable(); | ||
292 | |||
293 | iounmap(gef_wdt_regs); | ||
294 | |||
295 | return 0; | ||
296 | } | ||
297 | |||
298 | static const struct of_device_id gef_wdt_ids[] = { | ||
299 | { | ||
300 | .compatible = "gef,fpga-wdt", | ||
301 | }, | ||
302 | {}, | ||
303 | }; | ||
304 | |||
305 | static struct of_platform_driver gef_wdt_driver = { | ||
306 | .owner = THIS_MODULE, | ||
307 | .name = "gef_wdt", | ||
308 | .match_table = gef_wdt_ids, | ||
309 | .probe = gef_wdt_probe, | ||
310 | }; | ||
311 | |||
312 | static int __init gef_wdt_init(void) | ||
313 | { | ||
314 | printk(KERN_INFO "GE Fanuc watchdog driver\n"); | ||
315 | return of_register_platform_driver(&gef_wdt_driver); | ||
316 | } | ||
317 | |||
318 | static void __exit gef_wdt_exit(void) | ||
319 | { | ||
320 | of_unregister_platform_driver(&gef_wdt_driver); | ||
321 | } | ||
322 | |||
323 | module_init(gef_wdt_init); | ||
324 | module_exit(gef_wdt_exit); | ||
325 | |||
326 | MODULE_AUTHOR("Martyn Welch <martyn.welch@gefanuc.com>"); | ||
327 | MODULE_DESCRIPTION("GE Fanuc watchdog driver"); | ||
328 | MODULE_LICENSE("GPL"); | ||
329 | MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); | ||
330 | MODULE_ALIAS("platform: gef_wdt"); | ||
diff --git a/drivers/watchdog/pika_wdt.c b/drivers/watchdog/pika_wdt.c new file mode 100644 index 000000000000..2d22e996e996 --- /dev/null +++ b/drivers/watchdog/pika_wdt.c | |||
@@ -0,0 +1,301 @@ | |||
1 | /* | ||
2 | * PIKA FPGA based Watchdog Timer | ||
3 | * | ||
4 | * Copyright (c) 2008 PIKA Technologies | ||
5 | * Sean MacLennan <smaclennan@pikatech.com> | ||
6 | */ | ||
7 | |||
8 | #include <linux/init.h> | ||
9 | #include <linux/errno.h> | ||
10 | #include <linux/module.h> | ||
11 | #include <linux/moduleparam.h> | ||
12 | #include <linux/types.h> | ||
13 | #include <linux/kernel.h> | ||
14 | #include <linux/fs.h> | ||
15 | #include <linux/miscdevice.h> | ||
16 | #include <linux/watchdog.h> | ||
17 | #include <linux/reboot.h> | ||
18 | #include <linux/jiffies.h> | ||
19 | #include <linux/timer.h> | ||
20 | #include <linux/bitops.h> | ||
21 | #include <linux/uaccess.h> | ||
22 | #include <linux/io.h> | ||
23 | #include <linux/of_platform.h> | ||
24 | |||
25 | #define DRV_NAME "PIKA-WDT" | ||
26 | #define PFX DRV_NAME ": " | ||
27 | |||
28 | /* Hardware timeout in seconds */ | ||
29 | #define WDT_HW_TIMEOUT 2 | ||
30 | |||
31 | /* Timer heartbeat (500ms) */ | ||
32 | #define WDT_TIMEOUT (HZ/2) | ||
33 | |||
34 | /* User land timeout */ | ||
35 | #define WDT_HEARTBEAT 15 | ||
36 | static int heartbeat = WDT_HEARTBEAT; | ||
37 | module_param(heartbeat, int, 0); | ||
38 | MODULE_PARM_DESC(heartbeat, "Watchdog heartbeats in seconds. " | ||
39 | "(default = " __MODULE_STRING(WDT_HEARTBEAT) ")"); | ||
40 | |||
41 | static int nowayout = WATCHDOG_NOWAYOUT; | ||
42 | module_param(nowayout, int, 0); | ||
43 | MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started " | ||
44 | "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); | ||
45 | |||
46 | static struct { | ||
47 | void __iomem *fpga; | ||
48 | unsigned long next_heartbeat; /* the next_heartbeat for the timer */ | ||
49 | unsigned long open; | ||
50 | char expect_close; | ||
51 | int bootstatus; | ||
52 | struct timer_list timer; /* The timer that pings the watchdog */ | ||
53 | } pikawdt_private; | ||
54 | |||
55 | static struct watchdog_info ident = { | ||
56 | .identity = DRV_NAME, | ||
57 | .options = WDIOF_CARDRESET | | ||
58 | WDIOF_SETTIMEOUT | | ||
59 | WDIOF_KEEPALIVEPING | | ||
60 | WDIOF_MAGICCLOSE, | ||
61 | }; | ||
62 | |||
63 | /* | ||
64 | * Reload the watchdog timer. (ie, pat the watchdog) | ||
65 | */ | ||
66 | static inline void pikawdt_reset(void) | ||
67 | { | ||
68 | /* -- FPGA: Reset Control Register (32bit R/W) (Offset: 0x14) -- | ||
69 | * Bit 7, WTCHDG_EN: When set to 1, the watchdog timer is enabled. | ||
70 | * Once enabled, it cannot be disabled. The watchdog can be | ||
71 | * kicked by performing any write access to the reset | ||
72 | * control register (this register). | ||
73 | * Bit 8-11, WTCHDG_TIMEOUT_SEC: Sets the watchdog timeout value in | ||
74 | * seconds. Valid ranges are 1 to 15 seconds. The value can | ||
75 | * be modified dynamically. | ||
76 | */ | ||
77 | unsigned reset = in_be32(pikawdt_private.fpga + 0x14); | ||
78 | /* enable with max timeout - 15 seconds */ | ||
79 | reset |= (1 << 7) + (WDT_HW_TIMEOUT << 8); | ||
80 | out_be32(pikawdt_private.fpga + 0x14, reset); | ||
81 | } | ||
82 | |||
83 | /* | ||
84 | * Timer tick | ||
85 | */ | ||
86 | static void pikawdt_ping(unsigned long data) | ||
87 | { | ||
88 | if (time_before(jiffies, pikawdt_private.next_heartbeat) || | ||
89 | (!nowayout && !pikawdt_private.open)) { | ||
90 | pikawdt_reset(); | ||
91 | mod_timer(&pikawdt_private.timer, jiffies + WDT_TIMEOUT); | ||
92 | } else | ||
93 | printk(KERN_CRIT PFX "I will reset your machine !\n"); | ||
94 | } | ||
95 | |||
96 | |||
97 | static void pikawdt_keepalive(void) | ||
98 | { | ||
99 | pikawdt_private.next_heartbeat = jiffies + heartbeat * HZ; | ||
100 | } | ||
101 | |||
102 | static void pikawdt_start(void) | ||
103 | { | ||
104 | pikawdt_keepalive(); | ||
105 | mod_timer(&pikawdt_private.timer, jiffies + WDT_TIMEOUT); | ||
106 | } | ||
107 | |||
108 | /* | ||
109 | * Watchdog device is opened, and watchdog starts running. | ||
110 | */ | ||
111 | static int pikawdt_open(struct inode *inode, struct file *file) | ||
112 | { | ||
113 | /* /dev/watchdog can only be opened once */ | ||
114 | if (test_and_set_bit(0, &pikawdt_private.open)) | ||
115 | return -EBUSY; | ||
116 | |||
117 | pikawdt_start(); | ||
118 | |||
119 | return nonseekable_open(inode, file); | ||
120 | } | ||
121 | |||
122 | /* | ||
123 | * Close the watchdog device. | ||
124 | */ | ||
125 | static int pikawdt_release(struct inode *inode, struct file *file) | ||
126 | { | ||
127 | /* stop internal ping */ | ||
128 | if (!pikawdt_private.expect_close) | ||
129 | del_timer(&pikawdt_private.timer); | ||
130 | |||
131 | clear_bit(0, &pikawdt_private.open); | ||
132 | pikawdt_private.expect_close = 0; | ||
133 | return 0; | ||
134 | } | ||
135 | |||
136 | /* | ||
137 | * Pat the watchdog whenever device is written to. | ||
138 | */ | ||
139 | static ssize_t pikawdt_write(struct file *file, const char __user *data, | ||
140 | size_t len, loff_t *ppos) | ||
141 | { | ||
142 | if (!len) | ||
143 | return 0; | ||
144 | |||
145 | /* Scan for magic character */ | ||
146 | if (!nowayout) { | ||
147 | size_t i; | ||
148 | |||
149 | pikawdt_private.expect_close = 0; | ||
150 | |||
151 | for (i = 0; i < len; i++) { | ||
152 | char c; | ||
153 | if (get_user(c, data + i)) | ||
154 | return -EFAULT; | ||
155 | if (c == 'V') { | ||
156 | pikawdt_private.expect_close = 42; | ||
157 | break; | ||
158 | } | ||
159 | } | ||
160 | } | ||
161 | |||
162 | pikawdt_keepalive(); | ||
163 | |||
164 | return len; | ||
165 | } | ||
166 | |||
167 | /* | ||
168 | * Handle commands from user-space. | ||
169 | */ | ||
170 | static long pikawdt_ioctl(struct file *file, | ||
171 | unsigned int cmd, unsigned long arg) | ||
172 | { | ||
173 | void __user *argp = (void __user *)arg; | ||
174 | int __user *p = argp; | ||
175 | int new_value; | ||
176 | |||
177 | switch (cmd) { | ||
178 | case WDIOC_GETSUPPORT: | ||
179 | return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0; | ||
180 | |||
181 | case WDIOC_GETSTATUS: | ||
182 | return put_user(0, p); | ||
183 | |||
184 | case WDIOC_GETBOOTSTATUS: | ||
185 | return put_user(pikawdt_private.bootstatus, p); | ||
186 | |||
187 | case WDIOC_KEEPALIVE: | ||
188 | pikawdt_keepalive(); | ||
189 | return 0; | ||
190 | |||
191 | case WDIOC_SETTIMEOUT: | ||
192 | if (get_user(new_value, p)) | ||
193 | return -EFAULT; | ||
194 | |||
195 | heartbeat = new_value; | ||
196 | pikawdt_keepalive(); | ||
197 | |||
198 | return put_user(new_value, p); /* return current value */ | ||
199 | |||
200 | case WDIOC_GETTIMEOUT: | ||
201 | return put_user(heartbeat, p); | ||
202 | } | ||
203 | return -ENOTTY; | ||
204 | } | ||
205 | |||
206 | |||
207 | static const struct file_operations pikawdt_fops = { | ||
208 | .owner = THIS_MODULE, | ||
209 | .llseek = no_llseek, | ||
210 | .open = pikawdt_open, | ||
211 | .release = pikawdt_release, | ||
212 | .write = pikawdt_write, | ||
213 | .unlocked_ioctl = pikawdt_ioctl, | ||
214 | }; | ||
215 | |||
216 | static struct miscdevice pikawdt_miscdev = { | ||
217 | .minor = WATCHDOG_MINOR, | ||
218 | .name = "watchdog", | ||
219 | .fops = &pikawdt_fops, | ||
220 | }; | ||
221 | |||
222 | static int __init pikawdt_init(void) | ||
223 | { | ||
224 | struct device_node *np; | ||
225 | void __iomem *fpga; | ||
226 | static u32 post1; | ||
227 | int ret; | ||
228 | |||
229 | np = of_find_compatible_node(NULL, NULL, "pika,fpga"); | ||
230 | if (np == NULL) { | ||
231 | printk(KERN_ERR PFX "Unable to find fpga.\n"); | ||
232 | return -ENOENT; | ||
233 | } | ||
234 | |||
235 | pikawdt_private.fpga = of_iomap(np, 0); | ||
236 | of_node_put(np); | ||
237 | if (pikawdt_private.fpga == NULL) { | ||
238 | printk(KERN_ERR PFX "Unable to map fpga.\n"); | ||
239 | return -ENOMEM; | ||
240 | } | ||
241 | |||
242 | ident.firmware_version = in_be32(pikawdt_private.fpga + 0x1c) & 0xffff; | ||
243 | |||
244 | /* POST information is in the sd area. */ | ||
245 | np = of_find_compatible_node(NULL, NULL, "pika,fpga-sd"); | ||
246 | if (np == NULL) { | ||
247 | printk(KERN_ERR PFX "Unable to find fpga-sd.\n"); | ||
248 | ret = -ENOENT; | ||
249 | goto out; | ||
250 | } | ||
251 | |||
252 | fpga = of_iomap(np, 0); | ||
253 | of_node_put(np); | ||
254 | if (fpga == NULL) { | ||
255 | printk(KERN_ERR PFX "Unable to map fpga-sd.\n"); | ||
256 | ret = -ENOMEM; | ||
257 | goto out; | ||
258 | } | ||
259 | |||
260 | /* -- FPGA: POST Test Results Register 1 (32bit R/W) (Offset: 0x4040) -- | ||
261 | * Bit 31, WDOG: Set to 1 when the last reset was caused by a watchdog | ||
262 | * timeout. | ||
263 | */ | ||
264 | post1 = in_be32(fpga + 0x40); | ||
265 | if (post1 & 0x80000000) | ||
266 | pikawdt_private.bootstatus = WDIOF_CARDRESET; | ||
267 | |||
268 | iounmap(fpga); | ||
269 | |||
270 | setup_timer(&pikawdt_private.timer, pikawdt_ping, 0); | ||
271 | |||
272 | ret = misc_register(&pikawdt_miscdev); | ||
273 | if (ret) { | ||
274 | printk(KERN_ERR PFX "Unable to register miscdev.\n"); | ||
275 | goto out; | ||
276 | } | ||
277 | |||
278 | printk(KERN_INFO PFX "initialized. heartbeat=%d sec (nowayout=%d)\n", | ||
279 | heartbeat, nowayout); | ||
280 | return 0; | ||
281 | |||
282 | out: | ||
283 | iounmap(pikawdt_private.fpga); | ||
284 | return ret; | ||
285 | } | ||
286 | |||
287 | static void __exit pikawdt_exit(void) | ||
288 | { | ||
289 | misc_deregister(&pikawdt_miscdev); | ||
290 | |||
291 | iounmap(pikawdt_private.fpga); | ||
292 | } | ||
293 | |||
294 | module_init(pikawdt_init); | ||
295 | module_exit(pikawdt_exit); | ||
296 | |||
297 | MODULE_AUTHOR("Sean MacLennan <smaclennan@pikatech.com>"); | ||
298 | MODULE_DESCRIPTION("PIKA FPGA based Watchdog Timer"); | ||
299 | MODULE_LICENSE("GPL"); | ||
300 | MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); | ||
301 | |||
diff --git a/drivers/watchdog/wm8350_wdt.c b/drivers/watchdog/wm8350_wdt.c index 2bc0d4d4b415..a2d2e8eb2282 100644 --- a/drivers/watchdog/wm8350_wdt.c +++ b/drivers/watchdog/wm8350_wdt.c | |||
@@ -279,7 +279,7 @@ static struct miscdevice wm8350_wdt_miscdev = { | |||
279 | .fops = &wm8350_wdt_fops, | 279 | .fops = &wm8350_wdt_fops, |
280 | }; | 280 | }; |
281 | 281 | ||
282 | static int wm8350_wdt_probe(struct platform_device *pdev) | 282 | static int __devinit wm8350_wdt_probe(struct platform_device *pdev) |
283 | { | 283 | { |
284 | struct wm8350 *wm8350 = platform_get_drvdata(pdev); | 284 | struct wm8350 *wm8350 = platform_get_drvdata(pdev); |
285 | 285 | ||
@@ -296,7 +296,7 @@ static int wm8350_wdt_probe(struct platform_device *pdev) | |||
296 | return misc_register(&wm8350_wdt_miscdev); | 296 | return misc_register(&wm8350_wdt_miscdev); |
297 | } | 297 | } |
298 | 298 | ||
299 | static int __exit wm8350_wdt_remove(struct platform_device *pdev) | 299 | static int __devexit wm8350_wdt_remove(struct platform_device *pdev) |
300 | { | 300 | { |
301 | misc_deregister(&wm8350_wdt_miscdev); | 301 | misc_deregister(&wm8350_wdt_miscdev); |
302 | 302 | ||
@@ -305,7 +305,7 @@ static int __exit wm8350_wdt_remove(struct platform_device *pdev) | |||
305 | 305 | ||
306 | static struct platform_driver wm8350_wdt_driver = { | 306 | static struct platform_driver wm8350_wdt_driver = { |
307 | .probe = wm8350_wdt_probe, | 307 | .probe = wm8350_wdt_probe, |
308 | .remove = wm8350_wdt_remove, | 308 | .remove = __devexit_p(wm8350_wdt_remove), |
309 | .driver = { | 309 | .driver = { |
310 | .name = "wm8350-wdt", | 310 | .name = "wm8350-wdt", |
311 | }, | 311 | }, |