diff options
-rw-r--r-- | drivers/watchdog/Kconfig | 1 | ||||
-rw-r--r-- | drivers/watchdog/shwdt.c | 198 |
2 files changed, 45 insertions, 154 deletions
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 37096246c937..67957393393f 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig | |||
@@ -1138,6 +1138,7 @@ config ZVM_WATCHDOG | |||
1138 | config SH_WDT | 1138 | config SH_WDT |
1139 | tristate "SuperH Watchdog" | 1139 | tristate "SuperH Watchdog" |
1140 | depends on SUPERH && (CPU_SH3 || CPU_SH4) | 1140 | depends on SUPERH && (CPU_SH3 || CPU_SH4) |
1141 | select WATCHDOG_CORE | ||
1141 | help | 1142 | help |
1142 | This driver adds watchdog support for the integrated watchdog in the | 1143 | This driver adds watchdog support for the integrated watchdog in the |
1143 | SuperH processors. If you have one of these processors and wish | 1144 | SuperH processors. If you have one of these processors and wish |
diff --git a/drivers/watchdog/shwdt.c b/drivers/watchdog/shwdt.c index 74a261f36702..0beabf238d4f 100644 --- a/drivers/watchdog/shwdt.c +++ b/drivers/watchdog/shwdt.c | |||
@@ -27,12 +27,10 @@ | |||
27 | #include <linux/types.h> | 27 | #include <linux/types.h> |
28 | #include <linux/miscdevice.h> | 28 | #include <linux/miscdevice.h> |
29 | #include <linux/watchdog.h> | 29 | #include <linux/watchdog.h> |
30 | #include <linux/ioport.h> | ||
31 | #include <linux/fs.h> | 30 | #include <linux/fs.h> |
32 | #include <linux/mm.h> | 31 | #include <linux/mm.h> |
33 | #include <linux/slab.h> | 32 | #include <linux/slab.h> |
34 | #include <linux/io.h> | 33 | #include <linux/io.h> |
35 | #include <linux/uaccess.h> | ||
36 | #include <asm/watchdog.h> | 34 | #include <asm/watchdog.h> |
37 | 35 | ||
38 | #define DRV_NAME "sh-wdt" | 36 | #define DRV_NAME "sh-wdt" |
@@ -67,8 +65,6 @@ | |||
67 | static int clock_division_ratio = WTCSR_CKS_4096; | 65 | static int clock_division_ratio = WTCSR_CKS_4096; |
68 | #define next_ping_period(cks) (jiffies + msecs_to_jiffies(cks - 4)) | 66 | #define next_ping_period(cks) (jiffies + msecs_to_jiffies(cks - 4)) |
69 | 67 | ||
70 | static const struct watchdog_info sh_wdt_info; | ||
71 | static struct platform_device *sh_wdt_dev; | ||
72 | static DEFINE_SPINLOCK(shwdt_lock); | 68 | static DEFINE_SPINLOCK(shwdt_lock); |
73 | 69 | ||
74 | #define WATCHDOG_HEARTBEAT 30 /* 30 sec default heartbeat */ | 70 | #define WATCHDOG_HEARTBEAT 30 /* 30 sec default heartbeat */ |
@@ -86,8 +82,9 @@ struct sh_wdt { | |||
86 | char expect_close; | 82 | char expect_close; |
87 | }; | 83 | }; |
88 | 84 | ||
89 | static void sh_wdt_start(struct sh_wdt *wdt) | 85 | static int sh_wdt_start(struct watchdog_device *wdt_dev) |
90 | { | 86 | { |
87 | struct sh_wdt *wdt = watchdog_get_drvdata(wdt_dev); | ||
91 | unsigned long flags; | 88 | unsigned long flags; |
92 | u8 csr; | 89 | u8 csr; |
93 | 90 | ||
@@ -121,10 +118,13 @@ static void sh_wdt_start(struct sh_wdt *wdt) | |||
121 | sh_wdt_write_rstcsr(csr); | 118 | sh_wdt_write_rstcsr(csr); |
122 | #endif | 119 | #endif |
123 | spin_unlock_irqrestore(&shwdt_lock, flags); | 120 | spin_unlock_irqrestore(&shwdt_lock, flags); |
121 | |||
122 | return 0; | ||
124 | } | 123 | } |
125 | 124 | ||
126 | static void sh_wdt_stop(struct sh_wdt *wdt) | 125 | static int sh_wdt_stop(struct watchdog_device *wdt_dev) |
127 | { | 126 | { |
127 | struct sh_wdt *wdt = watchdog_get_drvdata(wdt_dev); | ||
128 | unsigned long flags; | 128 | unsigned long flags; |
129 | u8 csr; | 129 | u8 csr; |
130 | 130 | ||
@@ -137,18 +137,22 @@ static void sh_wdt_stop(struct sh_wdt *wdt) | |||
137 | sh_wdt_write_csr(csr); | 137 | sh_wdt_write_csr(csr); |
138 | 138 | ||
139 | spin_unlock_irqrestore(&shwdt_lock, flags); | 139 | spin_unlock_irqrestore(&shwdt_lock, flags); |
140 | |||
141 | return 0; | ||
140 | } | 142 | } |
141 | 143 | ||
142 | static inline void sh_wdt_keepalive(struct sh_wdt *wdt) | 144 | static int sh_wdt_keepalive(struct watchdog_device *wdt_dev) |
143 | { | 145 | { |
144 | unsigned long flags; | 146 | unsigned long flags; |
145 | 147 | ||
146 | spin_lock_irqsave(&shwdt_lock, flags); | 148 | spin_lock_irqsave(&shwdt_lock, flags); |
147 | next_heartbeat = jiffies + (heartbeat * HZ); | 149 | next_heartbeat = jiffies + (heartbeat * HZ); |
148 | spin_unlock_irqrestore(&shwdt_lock, flags); | 150 | spin_unlock_irqrestore(&shwdt_lock, flags); |
151 | |||
152 | return 0; | ||
149 | } | 153 | } |
150 | 154 | ||
151 | static int sh_wdt_set_heartbeat(int t) | 155 | static int sh_wdt_set_heartbeat(struct watchdog_device *wdt_dev, unsigned t) |
152 | { | 156 | { |
153 | unsigned long flags; | 157 | unsigned long flags; |
154 | 158 | ||
@@ -157,7 +161,9 @@ static int sh_wdt_set_heartbeat(int t) | |||
157 | 161 | ||
158 | spin_lock_irqsave(&shwdt_lock, flags); | 162 | spin_lock_irqsave(&shwdt_lock, flags); |
159 | heartbeat = t; | 163 | heartbeat = t; |
164 | wdt_dev->timeout = t; | ||
160 | spin_unlock_irqrestore(&shwdt_lock, flags); | 165 | spin_unlock_irqrestore(&shwdt_lock, flags); |
166 | |||
161 | return 0; | 167 | return 0; |
162 | } | 168 | } |
163 | 169 | ||
@@ -183,123 +189,6 @@ static void sh_wdt_ping(unsigned long data) | |||
183 | spin_unlock_irqrestore(&shwdt_lock, flags); | 189 | spin_unlock_irqrestore(&shwdt_lock, flags); |
184 | } | 190 | } |
185 | 191 | ||
186 | static int sh_wdt_open(struct inode *inode, struct file *file) | ||
187 | { | ||
188 | struct sh_wdt *wdt = platform_get_drvdata(sh_wdt_dev); | ||
189 | |||
190 | if (test_and_set_bit(0, &wdt->enabled)) | ||
191 | return -EBUSY; | ||
192 | if (nowayout) | ||
193 | __module_get(THIS_MODULE); | ||
194 | |||
195 | file->private_data = wdt; | ||
196 | |||
197 | sh_wdt_start(wdt); | ||
198 | |||
199 | return nonseekable_open(inode, file); | ||
200 | } | ||
201 | |||
202 | static int sh_wdt_close(struct inode *inode, struct file *file) | ||
203 | { | ||
204 | struct sh_wdt *wdt = file->private_data; | ||
205 | |||
206 | if (wdt->expect_close == 42) { | ||
207 | sh_wdt_stop(wdt); | ||
208 | } else { | ||
209 | dev_crit(wdt->dev, "Unexpected close, not " | ||
210 | "stopping watchdog!\n"); | ||
211 | sh_wdt_keepalive(wdt); | ||
212 | } | ||
213 | |||
214 | clear_bit(0, &wdt->enabled); | ||
215 | wdt->expect_close = 0; | ||
216 | |||
217 | return 0; | ||
218 | } | ||
219 | |||
220 | static ssize_t sh_wdt_write(struct file *file, const char *buf, | ||
221 | size_t count, loff_t *ppos) | ||
222 | { | ||
223 | struct sh_wdt *wdt = file->private_data; | ||
224 | |||
225 | if (count) { | ||
226 | if (!nowayout) { | ||
227 | size_t i; | ||
228 | |||
229 | wdt->expect_close = 0; | ||
230 | |||
231 | for (i = 0; i != count; i++) { | ||
232 | char c; | ||
233 | if (get_user(c, buf + i)) | ||
234 | return -EFAULT; | ||
235 | if (c == 'V') | ||
236 | wdt->expect_close = 42; | ||
237 | } | ||
238 | } | ||
239 | sh_wdt_keepalive(wdt); | ||
240 | } | ||
241 | |||
242 | return count; | ||
243 | } | ||
244 | |||
245 | static long sh_wdt_ioctl(struct file *file, unsigned int cmd, | ||
246 | unsigned long arg) | ||
247 | { | ||
248 | struct sh_wdt *wdt = file->private_data; | ||
249 | int new_heartbeat; | ||
250 | int options, retval = -EINVAL; | ||
251 | |||
252 | switch (cmd) { | ||
253 | case WDIOC_GETSUPPORT: | ||
254 | return copy_to_user((struct watchdog_info *)arg, | ||
255 | &sh_wdt_info, sizeof(sh_wdt_info)) ? -EFAULT : 0; | ||
256 | case WDIOC_GETSTATUS: | ||
257 | case WDIOC_GETBOOTSTATUS: | ||
258 | return put_user(0, (int *)arg); | ||
259 | case WDIOC_SETOPTIONS: | ||
260 | if (get_user(options, (int *)arg)) | ||
261 | return -EFAULT; | ||
262 | |||
263 | if (options & WDIOS_DISABLECARD) { | ||
264 | sh_wdt_stop(wdt); | ||
265 | retval = 0; | ||
266 | } | ||
267 | |||
268 | if (options & WDIOS_ENABLECARD) { | ||
269 | sh_wdt_start(wdt); | ||
270 | retval = 0; | ||
271 | } | ||
272 | |||
273 | return retval; | ||
274 | case WDIOC_KEEPALIVE: | ||
275 | sh_wdt_keepalive(wdt); | ||
276 | return 0; | ||
277 | case WDIOC_SETTIMEOUT: | ||
278 | if (get_user(new_heartbeat, (int *)arg)) | ||
279 | return -EFAULT; | ||
280 | |||
281 | if (sh_wdt_set_heartbeat(new_heartbeat)) | ||
282 | return -EINVAL; | ||
283 | |||
284 | sh_wdt_keepalive(wdt); | ||
285 | /* Fall */ | ||
286 | case WDIOC_GETTIMEOUT: | ||
287 | return put_user(heartbeat, (int *)arg); | ||
288 | default: | ||
289 | return -ENOTTY; | ||
290 | } | ||
291 | return 0; | ||
292 | } | ||
293 | |||
294 | static const struct file_operations sh_wdt_fops = { | ||
295 | .owner = THIS_MODULE, | ||
296 | .llseek = no_llseek, | ||
297 | .write = sh_wdt_write, | ||
298 | .unlocked_ioctl = sh_wdt_ioctl, | ||
299 | .open = sh_wdt_open, | ||
300 | .release = sh_wdt_close, | ||
301 | }; | ||
302 | |||
303 | static const struct watchdog_info sh_wdt_info = { | 192 | static const struct watchdog_info sh_wdt_info = { |
304 | .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | | 193 | .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | |
305 | WDIOF_MAGICCLOSE, | 194 | WDIOF_MAGICCLOSE, |
@@ -307,10 +196,17 @@ static const struct watchdog_info sh_wdt_info = { | |||
307 | .identity = "SH WDT", | 196 | .identity = "SH WDT", |
308 | }; | 197 | }; |
309 | 198 | ||
310 | static struct miscdevice sh_wdt_miscdev = { | 199 | static const struct watchdog_ops sh_wdt_ops = { |
311 | .minor = WATCHDOG_MINOR, | 200 | .owner = THIS_MODULE, |
312 | .name = "watchdog", | 201 | .start = sh_wdt_start, |
313 | .fops = &sh_wdt_fops, | 202 | .stop = sh_wdt_stop, |
203 | .ping = sh_wdt_keepalive, | ||
204 | .set_timeout = sh_wdt_set_heartbeat, | ||
205 | }; | ||
206 | |||
207 | static struct watchdog_device sh_wdt_dev = { | ||
208 | .info = &sh_wdt_info, | ||
209 | .ops = &sh_wdt_ops, | ||
314 | }; | 210 | }; |
315 | 211 | ||
316 | static int __devinit sh_wdt_probe(struct platform_device *pdev) | 212 | static int __devinit sh_wdt_probe(struct platform_device *pdev) |
@@ -348,13 +244,25 @@ static int __devinit sh_wdt_probe(struct platform_device *pdev) | |||
348 | goto out_err; | 244 | goto out_err; |
349 | } | 245 | } |
350 | 246 | ||
351 | sh_wdt_miscdev.parent = wdt->dev; | 247 | rc = sh_wdt_set_heartbeat(&sh_wdt_dev, heartbeat); |
248 | if (unlikely(rc)) { | ||
249 | /* Default timeout if invalid */ | ||
250 | sh_wdt_set_heartbeat(&sh_wdt_dev, WATCHDOG_HEARTBEAT); | ||
251 | |||
252 | dev_warn(&pdev->dev, | ||
253 | "heartbeat value must be 1<=x<=3600, using %d\n", | ||
254 | sh_wdt_dev.timeout); | ||
255 | } | ||
256 | |||
257 | dev_info(&pdev->dev, "configured with heartbeat=%d sec (nowayout=%d)\n", | ||
258 | sh_wdt_dev.timeout, nowayout); | ||
352 | 259 | ||
353 | rc = misc_register(&sh_wdt_miscdev); | 260 | watchdog_set_nowayout(&sh_wdt_dev, nowayout); |
261 | watchdog_set_drvdata(&sh_wdt_dev, wdt); | ||
262 | |||
263 | rc = watchdog_register_device(&sh_wdt_dev); | ||
354 | if (unlikely(rc)) { | 264 | if (unlikely(rc)) { |
355 | dev_err(&pdev->dev, | 265 | dev_err(&pdev->dev, "Can't register watchdog (err=%d)\n", rc); |
356 | "Can't register miscdev on minor=%d (err=%d)\n", | ||
357 | sh_wdt_miscdev.minor, rc); | ||
358 | goto out_unmap; | 266 | goto out_unmap; |
359 | } | 267 | } |
360 | 268 | ||
@@ -364,7 +272,6 @@ static int __devinit sh_wdt_probe(struct platform_device *pdev) | |||
364 | wdt->timer.expires = next_ping_period(clock_division_ratio); | 272 | wdt->timer.expires = next_ping_period(clock_division_ratio); |
365 | 273 | ||
366 | platform_set_drvdata(pdev, wdt); | 274 | platform_set_drvdata(pdev, wdt); |
367 | sh_wdt_dev = pdev; | ||
368 | 275 | ||
369 | dev_info(&pdev->dev, "initialized.\n"); | 276 | dev_info(&pdev->dev, "initialized.\n"); |
370 | 277 | ||
@@ -387,9 +294,7 @@ static int __devexit sh_wdt_remove(struct platform_device *pdev) | |||
387 | 294 | ||
388 | platform_set_drvdata(pdev, NULL); | 295 | platform_set_drvdata(pdev, NULL); |
389 | 296 | ||
390 | misc_deregister(&sh_wdt_miscdev); | 297 | watchdog_unregister_device(&sh_wdt_dev); |
391 | |||
392 | sh_wdt_dev = NULL; | ||
393 | 298 | ||
394 | devm_release_mem_region(&pdev->dev, res->start, resource_size(res)); | 299 | devm_release_mem_region(&pdev->dev, res->start, resource_size(res)); |
395 | devm_iounmap(&pdev->dev, wdt->base); | 300 | devm_iounmap(&pdev->dev, wdt->base); |
@@ -400,9 +305,7 @@ static int __devexit sh_wdt_remove(struct platform_device *pdev) | |||
400 | 305 | ||
401 | static void sh_wdt_shutdown(struct platform_device *pdev) | 306 | static void sh_wdt_shutdown(struct platform_device *pdev) |
402 | { | 307 | { |
403 | struct sh_wdt *wdt = platform_get_drvdata(pdev); | 308 | sh_wdt_stop(&sh_wdt_dev); |
404 | |||
405 | sh_wdt_stop(wdt); | ||
406 | } | 309 | } |
407 | 310 | ||
408 | static struct platform_driver sh_wdt_driver = { | 311 | static struct platform_driver sh_wdt_driver = { |
@@ -418,8 +321,6 @@ static struct platform_driver sh_wdt_driver = { | |||
418 | 321 | ||
419 | static int __init sh_wdt_init(void) | 322 | static int __init sh_wdt_init(void) |
420 | { | 323 | { |
421 | int rc; | ||
422 | |||
423 | if (unlikely(clock_division_ratio < 0x5 || | 324 | if (unlikely(clock_division_ratio < 0x5 || |
424 | clock_division_ratio > 0x7)) { | 325 | clock_division_ratio > 0x7)) { |
425 | clock_division_ratio = WTCSR_CKS_4096; | 326 | clock_division_ratio = WTCSR_CKS_4096; |
@@ -428,17 +329,6 @@ static int __init sh_wdt_init(void) | |||
428 | clock_division_ratio); | 329 | clock_division_ratio); |
429 | } | 330 | } |
430 | 331 | ||
431 | rc = sh_wdt_set_heartbeat(heartbeat); | ||
432 | if (unlikely(rc)) { | ||
433 | heartbeat = WATCHDOG_HEARTBEAT; | ||
434 | |||
435 | pr_info("heartbeat value must be 1<=x<=3600, using %d\n", | ||
436 | heartbeat); | ||
437 | } | ||
438 | |||
439 | pr_info("configured with heartbeat=%d sec (nowayout=%d)\n", | ||
440 | heartbeat, nowayout); | ||
441 | |||
442 | return platform_driver_register(&sh_wdt_driver); | 332 | return platform_driver_register(&sh_wdt_driver); |
443 | } | 333 | } |
444 | 334 | ||