diff options
Diffstat (limited to 'drivers/watchdog')
-rw-r--r-- | drivers/watchdog/Kconfig | 8 | ||||
-rw-r--r-- | drivers/watchdog/shwdt.c | 365 |
2 files changed, 170 insertions, 203 deletions
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 2e2400e7322e..faa9127aecaa 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig | |||
@@ -1083,14 +1083,6 @@ config SH_WDT | |||
1083 | To compile this driver as a module, choose M here: the | 1083 | To compile this driver as a module, choose M here: the |
1084 | module will be called shwdt. | 1084 | module will be called shwdt. |
1085 | 1085 | ||
1086 | config SH_WDT_MMAP | ||
1087 | bool "Allow mmap of SH WDT" | ||
1088 | default n | ||
1089 | depends on SH_WDT | ||
1090 | help | ||
1091 | If you say Y here, user applications will be able to mmap the | ||
1092 | WDT/CPG registers. | ||
1093 | |||
1094 | # SPARC Architecture | 1086 | # SPARC Architecture |
1095 | 1087 | ||
1096 | # SPARC64 Architecture | 1088 | # SPARC64 Architecture |
diff --git a/drivers/watchdog/shwdt.c b/drivers/watchdog/shwdt.c index 6fc74065abee..4e3e7eb5919c 100644 --- a/drivers/watchdog/shwdt.c +++ b/drivers/watchdog/shwdt.c | |||
@@ -1,9 +1,9 @@ | |||
1 | /* | 1 | /* |
2 | * drivers/char/watchdog/shwdt.c | 2 | * drivers/watchdog/shwdt.c |
3 | * | 3 | * |
4 | * Watchdog driver for integrated watchdog in the SuperH processors. | 4 | * Watchdog driver for integrated watchdog in the SuperH processors. |
5 | * | 5 | * |
6 | * Copyright (C) 2001, 2002, 2003 Paul Mundt <lethal@linux-sh.org> | 6 | * Copyright (C) 2001 - 2010 Paul Mundt <lethal@linux-sh.org> |
7 | * | 7 | * |
8 | * This program is free software; you can redistribute it and/or modify it | 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 | 9 | * under the terms of the GNU General Public License as published by the |
@@ -19,6 +19,7 @@ | |||
19 | */ | 19 | */ |
20 | #include <linux/module.h> | 20 | #include <linux/module.h> |
21 | #include <linux/moduleparam.h> | 21 | #include <linux/moduleparam.h> |
22 | #include <linux/platform_device.h> | ||
22 | #include <linux/init.h> | 23 | #include <linux/init.h> |
23 | #include <linux/types.h> | 24 | #include <linux/types.h> |
24 | #include <linux/miscdevice.h> | 25 | #include <linux/miscdevice.h> |
@@ -28,11 +29,12 @@ | |||
28 | #include <linux/ioport.h> | 29 | #include <linux/ioport.h> |
29 | #include <linux/fs.h> | 30 | #include <linux/fs.h> |
30 | #include <linux/mm.h> | 31 | #include <linux/mm.h> |
32 | #include <linux/slab.h> | ||
31 | #include <linux/io.h> | 33 | #include <linux/io.h> |
32 | #include <linux/uaccess.h> | 34 | #include <linux/uaccess.h> |
33 | #include <asm/watchdog.h> | 35 | #include <asm/watchdog.h> |
34 | 36 | ||
35 | #define PFX "shwdt: " | 37 | #define DRV_NAME "sh-wdt" |
36 | 38 | ||
37 | /* | 39 | /* |
38 | * Default clock division ratio is 5.25 msecs. For an additional table of | 40 | * Default clock division ratio is 5.25 msecs. For an additional table of |
@@ -62,37 +64,36 @@ | |||
62 | * misses its deadline, the kernel timer will allow the WDT to overflow. | 64 | * misses its deadline, the kernel timer will allow the WDT to overflow. |
63 | */ | 65 | */ |
64 | static int clock_division_ratio = WTCSR_CKS_4096; | 66 | static int clock_division_ratio = WTCSR_CKS_4096; |
65 | |||
66 | #define next_ping_period(cks) msecs_to_jiffies(cks - 4) | 67 | #define next_ping_period(cks) msecs_to_jiffies(cks - 4) |
67 | 68 | ||
68 | static void sh_wdt_ping(unsigned long data); | ||
69 | |||
70 | static unsigned long shwdt_is_open; | ||
71 | static const struct watchdog_info sh_wdt_info; | 69 | static const struct watchdog_info sh_wdt_info; |
72 | static char shwdt_expect_close; | 70 | static struct platform_device *sh_wdt_dev; |
73 | static DEFINE_TIMER(timer, sh_wdt_ping, 0, 0); | ||
74 | static unsigned long next_heartbeat; | ||
75 | static DEFINE_SPINLOCK(shwdt_lock); | 71 | static DEFINE_SPINLOCK(shwdt_lock); |
76 | 72 | ||
77 | #define WATCHDOG_HEARTBEAT 30 /* 30 sec default heartbeat */ | 73 | #define WATCHDOG_HEARTBEAT 30 /* 30 sec default heartbeat */ |
78 | static int heartbeat = WATCHDOG_HEARTBEAT; /* in seconds */ | 74 | static int heartbeat = WATCHDOG_HEARTBEAT; /* in seconds */ |
79 | |||
80 | static int nowayout = WATCHDOG_NOWAYOUT; | 75 | static int nowayout = WATCHDOG_NOWAYOUT; |
76 | static unsigned long next_heartbeat; | ||
81 | 77 | ||
82 | /** | 78 | struct sh_wdt { |
83 | * sh_wdt_start - Start the Watchdog | 79 | void __iomem *base; |
84 | * | 80 | struct device *dev; |
85 | * Starts the watchdog. | 81 | |
86 | */ | 82 | struct timer_list timer; |
87 | static void sh_wdt_start(void) | 83 | |
84 | unsigned long enabled; | ||
85 | char expect_close; | ||
86 | }; | ||
87 | |||
88 | static void sh_wdt_start(struct sh_wdt *wdt) | ||
88 | { | 89 | { |
89 | __u8 csr; | ||
90 | unsigned long flags; | 90 | unsigned long flags; |
91 | u8 csr; | ||
91 | 92 | ||
92 | spin_lock_irqsave(&shwdt_lock, flags); | 93 | spin_lock_irqsave(&shwdt_lock, flags); |
93 | 94 | ||
94 | next_heartbeat = jiffies + (heartbeat * HZ); | 95 | next_heartbeat = jiffies + (heartbeat * HZ); |
95 | mod_timer(&timer, next_ping_period(clock_division_ratio)); | 96 | mod_timer(&wdt->timer, next_ping_period(clock_division_ratio)); |
96 | 97 | ||
97 | csr = sh_wdt_read_csr(); | 98 | csr = sh_wdt_read_csr(); |
98 | csr |= WTCSR_WT | clock_division_ratio; | 99 | csr |= WTCSR_WT | clock_division_ratio; |
@@ -114,15 +115,6 @@ static void sh_wdt_start(void) | |||
114 | sh_wdt_write_csr(csr); | 115 | sh_wdt_write_csr(csr); |
115 | 116 | ||
116 | #ifdef CONFIG_CPU_SH2 | 117 | #ifdef CONFIG_CPU_SH2 |
117 | /* | ||
118 | * Whoever came up with the RSTCSR semantics must've been smoking | ||
119 | * some of the good stuff, since in addition to the WTCSR/WTCNT write | ||
120 | * brain-damage, it's managed to fuck things up one step further.. | ||
121 | * | ||
122 | * If we need to clear the WOVF bit, the upper byte has to be 0xa5.. | ||
123 | * but if we want to touch RSTE or RSTS, the upper byte has to be | ||
124 | * 0x5a.. | ||
125 | */ | ||
126 | csr = sh_wdt_read_rstcsr(); | 118 | csr = sh_wdt_read_rstcsr(); |
127 | csr &= ~RSTCSR_RSTS; | 119 | csr &= ~RSTCSR_RSTS; |
128 | sh_wdt_write_rstcsr(csr); | 120 | sh_wdt_write_rstcsr(csr); |
@@ -130,30 +122,23 @@ static void sh_wdt_start(void) | |||
130 | spin_unlock_irqrestore(&shwdt_lock, flags); | 122 | spin_unlock_irqrestore(&shwdt_lock, flags); |
131 | } | 123 | } |
132 | 124 | ||
133 | /** | 125 | static void sh_wdt_stop(struct sh_wdt *wdt) |
134 | * sh_wdt_stop - Stop the Watchdog | ||
135 | * Stops the watchdog. | ||
136 | */ | ||
137 | static void sh_wdt_stop(void) | ||
138 | { | 126 | { |
139 | __u8 csr; | ||
140 | unsigned long flags; | 127 | unsigned long flags; |
128 | u8 csr; | ||
141 | 129 | ||
142 | spin_lock_irqsave(&shwdt_lock, flags); | 130 | spin_lock_irqsave(&shwdt_lock, flags); |
143 | 131 | ||
144 | del_timer(&timer); | 132 | del_timer(&wdt->timer); |
145 | 133 | ||
146 | csr = sh_wdt_read_csr(); | 134 | csr = sh_wdt_read_csr(); |
147 | csr &= ~WTCSR_TME; | 135 | csr &= ~WTCSR_TME; |
148 | sh_wdt_write_csr(csr); | 136 | sh_wdt_write_csr(csr); |
137 | |||
149 | spin_unlock_irqrestore(&shwdt_lock, flags); | 138 | spin_unlock_irqrestore(&shwdt_lock, flags); |
150 | } | 139 | } |
151 | 140 | ||
152 | /** | 141 | static inline void sh_wdt_keepalive(struct sh_wdt *wdt) |
153 | * sh_wdt_keepalive - Keep the Userspace Watchdog Alive | ||
154 | * The Userspace watchdog got a KeepAlive: schedule the next heartbeat. | ||
155 | */ | ||
156 | static inline void sh_wdt_keepalive(void) | ||
157 | { | 142 | { |
158 | unsigned long flags; | 143 | unsigned long flags; |
159 | 144 | ||
@@ -162,10 +147,6 @@ static inline void sh_wdt_keepalive(void) | |||
162 | spin_unlock_irqrestore(&shwdt_lock, flags); | 147 | spin_unlock_irqrestore(&shwdt_lock, flags); |
163 | } | 148 | } |
164 | 149 | ||
165 | /** | ||
166 | * sh_wdt_set_heartbeat - Set the Userspace Watchdog heartbeat | ||
167 | * Set the Userspace Watchdog heartbeat | ||
168 | */ | ||
169 | static int sh_wdt_set_heartbeat(int t) | 150 | static int sh_wdt_set_heartbeat(int t) |
170 | { | 151 | { |
171 | unsigned long flags; | 152 | unsigned long flags; |
@@ -179,19 +160,14 @@ static int sh_wdt_set_heartbeat(int t) | |||
179 | return 0; | 160 | return 0; |
180 | } | 161 | } |
181 | 162 | ||
182 | /** | ||
183 | * sh_wdt_ping - Ping the Watchdog | ||
184 | * @data: Unused | ||
185 | * | ||
186 | * Clears overflow bit, resets timer counter. | ||
187 | */ | ||
188 | static void sh_wdt_ping(unsigned long data) | 163 | static void sh_wdt_ping(unsigned long data) |
189 | { | 164 | { |
165 | struct sh_wdt *wdt = (struct sh_wdt *)data; | ||
190 | unsigned long flags; | 166 | unsigned long flags; |
191 | 167 | ||
192 | spin_lock_irqsave(&shwdt_lock, flags); | 168 | spin_lock_irqsave(&shwdt_lock, flags); |
193 | if (time_before(jiffies, next_heartbeat)) { | 169 | if (time_before(jiffies, next_heartbeat)) { |
194 | __u8 csr; | 170 | u8 csr; |
195 | 171 | ||
196 | csr = sh_wdt_read_csr(); | 172 | csr = sh_wdt_read_csr(); |
197 | csr &= ~WTCSR_IOVF; | 173 | csr &= ~WTCSR_IOVF; |
@@ -199,148 +175,76 @@ static void sh_wdt_ping(unsigned long data) | |||
199 | 175 | ||
200 | sh_wdt_write_cnt(0); | 176 | sh_wdt_write_cnt(0); |
201 | 177 | ||
202 | mod_timer(&timer, next_ping_period(clock_division_ratio)); | 178 | mod_timer(&wdt->timer, next_ping_period(clock_division_ratio)); |
203 | } else | 179 | } else |
204 | printk(KERN_WARNING PFX "Heartbeat lost! Will not ping " | 180 | dev_warn(wdt->dev, "Heartbeat lost! Will not ping " |
205 | "the watchdog\n"); | 181 | "the watchdog\n"); |
206 | spin_unlock_irqrestore(&shwdt_lock, flags); | 182 | spin_unlock_irqrestore(&shwdt_lock, flags); |
207 | } | 183 | } |
208 | 184 | ||
209 | /** | ||
210 | * sh_wdt_open - Open the Device | ||
211 | * @inode: inode of device | ||
212 | * @file: file handle of device | ||
213 | * | ||
214 | * Watchdog device is opened and started. | ||
215 | */ | ||
216 | static int sh_wdt_open(struct inode *inode, struct file *file) | 185 | static int sh_wdt_open(struct inode *inode, struct file *file) |
217 | { | 186 | { |
218 | if (test_and_set_bit(0, &shwdt_is_open)) | 187 | struct sh_wdt *wdt = platform_get_drvdata(sh_wdt_dev); |
188 | |||
189 | if (test_and_set_bit(0, &wdt->enabled)) | ||
219 | return -EBUSY; | 190 | return -EBUSY; |
220 | if (nowayout) | 191 | if (nowayout) |
221 | __module_get(THIS_MODULE); | 192 | __module_get(THIS_MODULE); |
222 | 193 | ||
223 | sh_wdt_start(); | 194 | file->private_data = wdt; |
195 | |||
196 | sh_wdt_start(wdt); | ||
224 | 197 | ||
225 | return nonseekable_open(inode, file); | 198 | return nonseekable_open(inode, file); |
226 | } | 199 | } |
227 | 200 | ||
228 | /** | ||
229 | * sh_wdt_close - Close the Device | ||
230 | * @inode: inode of device | ||
231 | * @file: file handle of device | ||
232 | * | ||
233 | * Watchdog device is closed and stopped. | ||
234 | */ | ||
235 | static int sh_wdt_close(struct inode *inode, struct file *file) | 201 | static int sh_wdt_close(struct inode *inode, struct file *file) |
236 | { | 202 | { |
237 | if (shwdt_expect_close == 42) { | 203 | struct sh_wdt *wdt = file->private_data; |
238 | sh_wdt_stop(); | 204 | |
205 | if (wdt->expect_close == 42) { | ||
206 | sh_wdt_stop(wdt); | ||
239 | } else { | 207 | } else { |
240 | printk(KERN_CRIT PFX "Unexpected close, not " | 208 | dev_crit(wdt->dev, "Unexpected close, not " |
241 | "stopping watchdog!\n"); | 209 | "stopping watchdog!\n"); |
242 | sh_wdt_keepalive(); | 210 | sh_wdt_keepalive(wdt); |
243 | } | 211 | } |
244 | 212 | ||
245 | clear_bit(0, &shwdt_is_open); | 213 | clear_bit(0, &wdt->enabled); |
246 | shwdt_expect_close = 0; | 214 | wdt->expect_close = 0; |
247 | 215 | ||
248 | return 0; | 216 | return 0; |
249 | } | 217 | } |
250 | 218 | ||
251 | /** | ||
252 | * sh_wdt_write - Write to Device | ||
253 | * @file: file handle of device | ||
254 | * @buf: buffer to write | ||
255 | * @count: length of buffer | ||
256 | * @ppos: offset | ||
257 | * | ||
258 | * Pings the watchdog on write. | ||
259 | */ | ||
260 | static ssize_t sh_wdt_write(struct file *file, const char *buf, | 219 | static ssize_t sh_wdt_write(struct file *file, const char *buf, |
261 | size_t count, loff_t *ppos) | 220 | size_t count, loff_t *ppos) |
262 | { | 221 | { |
222 | struct sh_wdt *wdt = file->private_data; | ||
223 | |||
263 | if (count) { | 224 | if (count) { |
264 | if (!nowayout) { | 225 | if (!nowayout) { |
265 | size_t i; | 226 | size_t i; |
266 | 227 | ||
267 | shwdt_expect_close = 0; | 228 | wdt->expect_close = 0; |
268 | 229 | ||
269 | for (i = 0; i != count; i++) { | 230 | for (i = 0; i != count; i++) { |
270 | char c; | 231 | char c; |
271 | if (get_user(c, buf + i)) | 232 | if (get_user(c, buf + i)) |
272 | return -EFAULT; | 233 | return -EFAULT; |
273 | if (c == 'V') | 234 | if (c == 'V') |
274 | shwdt_expect_close = 42; | 235 | wdt->expect_close = 42; |
275 | } | 236 | } |
276 | } | 237 | } |
277 | sh_wdt_keepalive(); | 238 | sh_wdt_keepalive(wdt); |
278 | } | 239 | } |
279 | 240 | ||
280 | return count; | 241 | return count; |
281 | } | 242 | } |
282 | 243 | ||
283 | /** | ||
284 | * sh_wdt_mmap - map WDT/CPG registers into userspace | ||
285 | * @file: file structure for the device | ||
286 | * @vma: VMA to map the registers into | ||
287 | * | ||
288 | * A simple mmap() implementation for the corner cases where the counter | ||
289 | * needs to be mapped in userspace directly. Due to the relatively small | ||
290 | * size of the area, neighbouring registers not necessarily tied to the | ||
291 | * CPG will also be accessible through the register page, so this remains | ||
292 | * configurable for users that really know what they're doing. | ||
293 | * | ||
294 | * Additionaly, the register page maps in the CPG register base relative | ||
295 | * to the nearest page-aligned boundary, which requires that userspace do | ||
296 | * the appropriate CPU subtype math for calculating the page offset for | ||
297 | * the counter value. | ||
298 | */ | ||
299 | static int sh_wdt_mmap(struct file *file, struct vm_area_struct *vma) | ||
300 | { | ||
301 | int ret = -ENOSYS; | ||
302 | |||
303 | #ifdef CONFIG_SH_WDT_MMAP | ||
304 | unsigned long addr; | ||
305 | |||
306 | /* Only support the simple cases where we map in a register page. */ | ||
307 | if (((vma->vm_end - vma->vm_start) != PAGE_SIZE) || vma->vm_pgoff) | ||
308 | return -EINVAL; | ||
309 | |||
310 | /* | ||
311 | * Pick WTCNT as the start, it's usually the first register after the | ||
312 | * FRQCR, and neither one are generally page-aligned out of the box. | ||
313 | */ | ||
314 | addr = WTCNT & ~(PAGE_SIZE - 1); | ||
315 | |||
316 | vma->vm_flags |= VM_IO; | ||
317 | vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); | ||
318 | |||
319 | if (io_remap_pfn_range(vma, vma->vm_start, addr >> PAGE_SHIFT, | ||
320 | PAGE_SIZE, vma->vm_page_prot)) { | ||
321 | printk(KERN_ERR PFX "%s: io_remap_pfn_range failed\n", | ||
322 | __func__); | ||
323 | return -EAGAIN; | ||
324 | } | ||
325 | |||
326 | ret = 0; | ||
327 | #endif | ||
328 | |||
329 | return ret; | ||
330 | } | ||
331 | |||
332 | /** | ||
333 | * sh_wdt_ioctl - Query Device | ||
334 | * @file: file handle of device | ||
335 | * @cmd: watchdog command | ||
336 | * @arg: argument | ||
337 | * | ||
338 | * Query basic information from the device or ping it, as outlined by the | ||
339 | * watchdog API. | ||
340 | */ | ||
341 | static long sh_wdt_ioctl(struct file *file, unsigned int cmd, | 244 | static long sh_wdt_ioctl(struct file *file, unsigned int cmd, |
342 | unsigned long arg) | 245 | unsigned long arg) |
343 | { | 246 | { |
247 | struct sh_wdt *wdt = file->private_data; | ||
344 | int new_heartbeat; | 248 | int new_heartbeat; |
345 | int options, retval = -EINVAL; | 249 | int options, retval = -EINVAL; |
346 | 250 | ||
@@ -356,18 +260,18 @@ static long sh_wdt_ioctl(struct file *file, unsigned int cmd, | |||
356 | return -EFAULT; | 260 | return -EFAULT; |
357 | 261 | ||
358 | if (options & WDIOS_DISABLECARD) { | 262 | if (options & WDIOS_DISABLECARD) { |
359 | sh_wdt_stop(); | 263 | sh_wdt_stop(wdt); |
360 | retval = 0; | 264 | retval = 0; |
361 | } | 265 | } |
362 | 266 | ||
363 | if (options & WDIOS_ENABLECARD) { | 267 | if (options & WDIOS_ENABLECARD) { |
364 | sh_wdt_start(); | 268 | sh_wdt_start(wdt); |
365 | retval = 0; | 269 | retval = 0; |
366 | } | 270 | } |
367 | 271 | ||
368 | return retval; | 272 | return retval; |
369 | case WDIOC_KEEPALIVE: | 273 | case WDIOC_KEEPALIVE: |
370 | sh_wdt_keepalive(); | 274 | sh_wdt_keepalive(wdt); |
371 | return 0; | 275 | return 0; |
372 | case WDIOC_SETTIMEOUT: | 276 | case WDIOC_SETTIMEOUT: |
373 | if (get_user(new_heartbeat, (int *)arg)) | 277 | if (get_user(new_heartbeat, (int *)arg)) |
@@ -376,7 +280,7 @@ static long sh_wdt_ioctl(struct file *file, unsigned int cmd, | |||
376 | if (sh_wdt_set_heartbeat(new_heartbeat)) | 280 | if (sh_wdt_set_heartbeat(new_heartbeat)) |
377 | return -EINVAL; | 281 | return -EINVAL; |
378 | 282 | ||
379 | sh_wdt_keepalive(); | 283 | sh_wdt_keepalive(wdt); |
380 | /* Fall */ | 284 | /* Fall */ |
381 | case WDIOC_GETTIMEOUT: | 285 | case WDIOC_GETTIMEOUT: |
382 | return put_user(heartbeat, (int *)arg); | 286 | return put_user(heartbeat, (int *)arg); |
@@ -386,20 +290,13 @@ static long sh_wdt_ioctl(struct file *file, unsigned int cmd, | |||
386 | return 0; | 290 | return 0; |
387 | } | 291 | } |
388 | 292 | ||
389 | /** | ||
390 | * sh_wdt_notify_sys - Notifier Handler | ||
391 | * @this: notifier block | ||
392 | * @code: notifier event | ||
393 | * @unused: unused | ||
394 | * | ||
395 | * Handles specific events, such as turning off the watchdog during a | ||
396 | * shutdown event. | ||
397 | */ | ||
398 | static int sh_wdt_notify_sys(struct notifier_block *this, | 293 | static int sh_wdt_notify_sys(struct notifier_block *this, |
399 | unsigned long code, void *unused) | 294 | unsigned long code, void *unused) |
400 | { | 295 | { |
296 | struct sh_wdt *wdt = platform_get_drvdata(sh_wdt_dev); | ||
297 | |||
401 | if (code == SYS_DOWN || code == SYS_HALT) | 298 | if (code == SYS_DOWN || code == SYS_HALT) |
402 | sh_wdt_stop(); | 299 | sh_wdt_stop(wdt); |
403 | 300 | ||
404 | return NOTIFY_DONE; | 301 | return NOTIFY_DONE; |
405 | } | 302 | } |
@@ -411,7 +308,6 @@ static const struct file_operations sh_wdt_fops = { | |||
411 | .unlocked_ioctl = sh_wdt_ioctl, | 308 | .unlocked_ioctl = sh_wdt_ioctl, |
412 | .open = sh_wdt_open, | 309 | .open = sh_wdt_open, |
413 | .release = sh_wdt_close, | 310 | .release = sh_wdt_close, |
414 | .mmap = sh_wdt_mmap, | ||
415 | }; | 311 | }; |
416 | 312 | ||
417 | static const struct watchdog_info sh_wdt_info = { | 313 | static const struct watchdog_info sh_wdt_info = { |
@@ -431,66 +327,148 @@ static struct miscdevice sh_wdt_miscdev = { | |||
431 | .fops = &sh_wdt_fops, | 327 | .fops = &sh_wdt_fops, |
432 | }; | 328 | }; |
433 | 329 | ||
434 | /** | 330 | static int __devinit sh_wdt_probe(struct platform_device *pdev) |
435 | * sh_wdt_init - Initialize module | ||
436 | * Registers the device and notifier handler. Actual device | ||
437 | * initialization is handled by sh_wdt_open(). | ||
438 | */ | ||
439 | static int __init sh_wdt_init(void) | ||
440 | { | 331 | { |
332 | struct sh_wdt *wdt; | ||
333 | struct resource *res; | ||
441 | int rc; | 334 | int rc; |
442 | 335 | ||
443 | if (clock_division_ratio < 0x5 || clock_division_ratio > 0x7) { | 336 | /* |
444 | clock_division_ratio = WTCSR_CKS_4096; | 337 | * As this driver only covers the global watchdog case, reject |
445 | printk(KERN_INFO PFX | 338 | * any attempts to register per-CPU watchdogs. |
446 | "clock_division_ratio value must be 0x5<=x<=0x7, using %d\n", | 339 | */ |
447 | clock_division_ratio); | 340 | if (pdev->id != -1) |
341 | return -EINVAL; | ||
342 | |||
343 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
344 | if (unlikely(!res)) | ||
345 | return -EINVAL; | ||
346 | |||
347 | if (!devm_request_mem_region(&pdev->dev, res->start, | ||
348 | resource_size(res), DRV_NAME)) | ||
349 | return -EBUSY; | ||
350 | |||
351 | wdt = devm_kzalloc(&pdev->dev, sizeof(struct sh_wdt), GFP_KERNEL); | ||
352 | if (unlikely(!wdt)) { | ||
353 | rc = -ENOMEM; | ||
354 | goto out_release; | ||
448 | } | 355 | } |
449 | 356 | ||
450 | rc = sh_wdt_set_heartbeat(heartbeat); | 357 | wdt->dev = &pdev->dev; |
451 | if (unlikely(rc)) { | 358 | |
452 | heartbeat = WATCHDOG_HEARTBEAT; | 359 | wdt->base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); |
453 | printk(KERN_INFO PFX | 360 | if (unlikely(!wdt->base)) { |
454 | "heartbeat value must be 1<=x<=3600, using %d\n", | 361 | rc = -ENXIO; |
455 | heartbeat); | 362 | goto out_err; |
456 | } | 363 | } |
457 | 364 | ||
458 | rc = register_reboot_notifier(&sh_wdt_notifier); | 365 | rc = register_reboot_notifier(&sh_wdt_notifier); |
459 | if (unlikely(rc)) { | 366 | if (unlikely(rc)) { |
460 | printk(KERN_ERR PFX | 367 | dev_err(&pdev->dev, |
461 | "Can't register reboot notifier (err=%d)\n", rc); | 368 | "Can't register reboot notifier (err=%d)\n", rc); |
462 | return rc; | 369 | goto out_unmap; |
463 | } | 370 | } |
464 | 371 | ||
372 | sh_wdt_miscdev.parent = wdt->dev; | ||
373 | |||
465 | rc = misc_register(&sh_wdt_miscdev); | 374 | rc = misc_register(&sh_wdt_miscdev); |
466 | if (unlikely(rc)) { | 375 | if (unlikely(rc)) { |
467 | printk(KERN_ERR PFX | 376 | dev_err(&pdev->dev, |
468 | "Can't register miscdev on minor=%d (err=%d)\n", | 377 | "Can't register miscdev on minor=%d (err=%d)\n", |
469 | sh_wdt_miscdev.minor, rc); | 378 | sh_wdt_miscdev.minor, rc); |
470 | unregister_reboot_notifier(&sh_wdt_notifier); | 379 | goto out_unreg; |
471 | return rc; | ||
472 | } | 380 | } |
473 | 381 | ||
474 | printk(KERN_INFO PFX "initialized. heartbeat=%d sec (nowayout=%d)\n", | 382 | init_timer(&wdt->timer); |
475 | heartbeat, nowayout); | 383 | wdt->timer.function = sh_wdt_ping; |
384 | wdt->timer.data = (unsigned long)wdt; | ||
385 | wdt->timer.expires = next_ping_period(clock_division_ratio); | ||
386 | |||
387 | platform_set_drvdata(pdev, wdt); | ||
388 | sh_wdt_dev = pdev; | ||
389 | |||
390 | dev_info(&pdev->dev, "initialized.\n"); | ||
476 | 391 | ||
477 | return 0; | 392 | return 0; |
393 | |||
394 | out_unreg: | ||
395 | unregister_reboot_notifier(&sh_wdt_notifier); | ||
396 | out_unmap: | ||
397 | devm_iounmap(&pdev->dev, wdt->base); | ||
398 | out_err: | ||
399 | devm_kfree(&pdev->dev, wdt); | ||
400 | out_release: | ||
401 | devm_release_mem_region(&pdev->dev, res->start, resource_size(res)); | ||
402 | |||
403 | return rc; | ||
478 | } | 404 | } |
479 | 405 | ||
480 | /** | 406 | static int __devexit sh_wdt_remove(struct platform_device *pdev) |
481 | * sh_wdt_exit - Deinitialize module | ||
482 | * Unregisters the device and notifier handler. Actual device | ||
483 | * deinitialization is handled by sh_wdt_close(). | ||
484 | */ | ||
485 | static void __exit sh_wdt_exit(void) | ||
486 | { | 407 | { |
408 | struct sh_wdt *wdt = platform_get_drvdata(pdev); | ||
409 | struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
410 | |||
411 | platform_set_drvdata(pdev, NULL); | ||
412 | |||
487 | misc_deregister(&sh_wdt_miscdev); | 413 | misc_deregister(&sh_wdt_miscdev); |
414 | |||
415 | sh_wdt_dev = NULL; | ||
416 | |||
488 | unregister_reboot_notifier(&sh_wdt_notifier); | 417 | unregister_reboot_notifier(&sh_wdt_notifier); |
418 | devm_release_mem_region(&pdev->dev, res->start, resource_size(res)); | ||
419 | devm_iounmap(&pdev->dev, wdt->base); | ||
420 | devm_kfree(&pdev->dev, wdt); | ||
421 | |||
422 | return 0; | ||
489 | } | 423 | } |
490 | 424 | ||
425 | static struct platform_driver sh_wdt_driver = { | ||
426 | .driver = { | ||
427 | .name = DRV_NAME, | ||
428 | .owner = THIS_MODULE, | ||
429 | }, | ||
430 | |||
431 | .probe = sh_wdt_probe, | ||
432 | .remove = __devexit_p(sh_wdt_remove), | ||
433 | }; | ||
434 | |||
435 | static int __init sh_wdt_init(void) | ||
436 | { | ||
437 | int rc; | ||
438 | |||
439 | if (unlikely(clock_division_ratio < 0x5 || | ||
440 | clock_division_ratio > 0x7)) { | ||
441 | clock_division_ratio = WTCSR_CKS_4096; | ||
442 | |||
443 | pr_info("%s: divisor must be 0x5<=x<=0x7, using %d\n", | ||
444 | DRV_NAME, clock_division_ratio); | ||
445 | } | ||
446 | |||
447 | rc = sh_wdt_set_heartbeat(heartbeat); | ||
448 | if (unlikely(rc)) { | ||
449 | heartbeat = WATCHDOG_HEARTBEAT; | ||
450 | |||
451 | pr_info("%s: heartbeat value must be 1<=x<=3600, using %d\n", | ||
452 | DRV_NAME, heartbeat); | ||
453 | } | ||
454 | |||
455 | pr_info("%s: configured with heartbeat=%d sec (nowayout=%d)\n", | ||
456 | DRV_NAME, heartbeat, nowayout); | ||
457 | |||
458 | return platform_driver_register(&sh_wdt_driver); | ||
459 | } | ||
460 | |||
461 | static void __exit sh_wdt_exit(void) | ||
462 | { | ||
463 | platform_driver_unregister(&sh_wdt_driver); | ||
464 | } | ||
465 | module_init(sh_wdt_init); | ||
466 | module_exit(sh_wdt_exit); | ||
467 | |||
491 | MODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>"); | 468 | MODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>"); |
492 | MODULE_DESCRIPTION("SuperH watchdog driver"); | 469 | MODULE_DESCRIPTION("SuperH watchdog driver"); |
493 | MODULE_LICENSE("GPL"); | 470 | MODULE_LICENSE("GPL"); |
471 | MODULE_ALIAS("platform:" DRV_NAME); | ||
494 | MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); | 472 | MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); |
495 | 473 | ||
496 | module_param(clock_division_ratio, int, 0); | 474 | module_param(clock_division_ratio, int, 0); |
@@ -507,6 +485,3 @@ module_param(nowayout, int, 0); | |||
507 | MODULE_PARM_DESC(nowayout, | 485 | MODULE_PARM_DESC(nowayout, |
508 | "Watchdog cannot be stopped once started (default=" | 486 | "Watchdog cannot be stopped once started (default=" |
509 | __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); | 487 | __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); |
510 | |||
511 | module_init(sh_wdt_init); | ||
512 | module_exit(sh_wdt_exit); | ||