aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/watchdog/shwdt.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/watchdog/shwdt.c')
-rw-r--r--drivers/watchdog/shwdt.c306
1 files changed, 93 insertions, 213 deletions
diff --git a/drivers/watchdog/shwdt.c b/drivers/watchdog/shwdt.c
index 93958a7763e6..e5b59bebcdb1 100644
--- a/drivers/watchdog/shwdt.c
+++ b/drivers/watchdog/shwdt.c
@@ -3,7 +3,7 @@
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 - 2010 Paul Mundt <lethal@linux-sh.org> 6 * Copyright (C) 2001 - 2012 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
@@ -25,16 +25,15 @@
25#include <linux/platform_device.h> 25#include <linux/platform_device.h>
26#include <linux/init.h> 26#include <linux/init.h>
27#include <linux/types.h> 27#include <linux/types.h>
28#include <linux/spinlock.h>
28#include <linux/miscdevice.h> 29#include <linux/miscdevice.h>
29#include <linux/watchdog.h> 30#include <linux/watchdog.h>
30#include <linux/reboot.h> 31#include <linux/pm_runtime.h>
31#include <linux/notifier.h>
32#include <linux/ioport.h>
33#include <linux/fs.h> 32#include <linux/fs.h>
34#include <linux/mm.h> 33#include <linux/mm.h>
35#include <linux/slab.h> 34#include <linux/slab.h>
36#include <linux/io.h> 35#include <linux/io.h>
37#include <linux/uaccess.h> 36#include <linux/clk.h>
38#include <asm/watchdog.h> 37#include <asm/watchdog.h>
39 38
40#define DRV_NAME "sh-wdt" 39#define DRV_NAME "sh-wdt"
@@ -69,10 +68,6 @@
69static int clock_division_ratio = WTCSR_CKS_4096; 68static int clock_division_ratio = WTCSR_CKS_4096;
70#define next_ping_period(cks) (jiffies + msecs_to_jiffies(cks - 4)) 69#define next_ping_period(cks) (jiffies + msecs_to_jiffies(cks - 4))
71 70
72static const struct watchdog_info sh_wdt_info;
73static struct platform_device *sh_wdt_dev;
74static DEFINE_SPINLOCK(shwdt_lock);
75
76#define WATCHDOG_HEARTBEAT 30 /* 30 sec default heartbeat */ 71#define WATCHDOG_HEARTBEAT 30 /* 30 sec default heartbeat */
77static int heartbeat = WATCHDOG_HEARTBEAT; /* in seconds */ 72static int heartbeat = WATCHDOG_HEARTBEAT; /* in seconds */
78static bool nowayout = WATCHDOG_NOWAYOUT; 73static bool nowayout = WATCHDOG_NOWAYOUT;
@@ -81,19 +76,22 @@ static unsigned long next_heartbeat;
81struct sh_wdt { 76struct sh_wdt {
82 void __iomem *base; 77 void __iomem *base;
83 struct device *dev; 78 struct device *dev;
79 struct clk *clk;
80 spinlock_t lock;
84 81
85 struct timer_list timer; 82 struct timer_list timer;
86
87 unsigned long enabled;
88 char expect_close;
89}; 83};
90 84
91static void sh_wdt_start(struct sh_wdt *wdt) 85static int sh_wdt_start(struct watchdog_device *wdt_dev)
92{ 86{
87 struct sh_wdt *wdt = watchdog_get_drvdata(wdt_dev);
93 unsigned long flags; 88 unsigned long flags;
94 u8 csr; 89 u8 csr;
95 90
96 spin_lock_irqsave(&shwdt_lock, flags); 91 pm_runtime_get_sync(wdt->dev);
92 clk_enable(wdt->clk);
93
94 spin_lock_irqsave(&wdt->lock, flags);
97 95
98 next_heartbeat = jiffies + (heartbeat * HZ); 96 next_heartbeat = jiffies + (heartbeat * HZ);
99 mod_timer(&wdt->timer, next_ping_period(clock_division_ratio)); 97 mod_timer(&wdt->timer, next_ping_period(clock_division_ratio));
@@ -122,15 +120,18 @@ static void sh_wdt_start(struct sh_wdt *wdt)
122 csr &= ~RSTCSR_RSTS; 120 csr &= ~RSTCSR_RSTS;
123 sh_wdt_write_rstcsr(csr); 121 sh_wdt_write_rstcsr(csr);
124#endif 122#endif
125 spin_unlock_irqrestore(&shwdt_lock, flags); 123 spin_unlock_irqrestore(&wdt->lock, flags);
124
125 return 0;
126} 126}
127 127
128static void sh_wdt_stop(struct sh_wdt *wdt) 128static int sh_wdt_stop(struct watchdog_device *wdt_dev)
129{ 129{
130 struct sh_wdt *wdt = watchdog_get_drvdata(wdt_dev);
130 unsigned long flags; 131 unsigned long flags;
131 u8 csr; 132 u8 csr;
132 133
133 spin_lock_irqsave(&shwdt_lock, flags); 134 spin_lock_irqsave(&wdt->lock, flags);
134 135
135 del_timer(&wdt->timer); 136 del_timer(&wdt->timer);
136 137
@@ -138,28 +139,39 @@ static void sh_wdt_stop(struct sh_wdt *wdt)
138 csr &= ~WTCSR_TME; 139 csr &= ~WTCSR_TME;
139 sh_wdt_write_csr(csr); 140 sh_wdt_write_csr(csr);
140 141
141 spin_unlock_irqrestore(&shwdt_lock, flags); 142 spin_unlock_irqrestore(&wdt->lock, flags);
143
144 clk_disable(wdt->clk);
145 pm_runtime_put_sync(wdt->dev);
146
147 return 0;
142} 148}
143 149
144static inline void sh_wdt_keepalive(struct sh_wdt *wdt) 150static int sh_wdt_keepalive(struct watchdog_device *wdt_dev)
145{ 151{
152 struct sh_wdt *wdt = watchdog_get_drvdata(wdt_dev);
146 unsigned long flags; 153 unsigned long flags;
147 154
148 spin_lock_irqsave(&shwdt_lock, flags); 155 spin_lock_irqsave(&wdt->lock, flags);
149 next_heartbeat = jiffies + (heartbeat * HZ); 156 next_heartbeat = jiffies + (heartbeat * HZ);
150 spin_unlock_irqrestore(&shwdt_lock, flags); 157 spin_unlock_irqrestore(&wdt->lock, flags);
158
159 return 0;
151} 160}
152 161
153static int sh_wdt_set_heartbeat(int t) 162static int sh_wdt_set_heartbeat(struct watchdog_device *wdt_dev, unsigned t)
154{ 163{
164 struct sh_wdt *wdt = watchdog_get_drvdata(wdt_dev);
155 unsigned long flags; 165 unsigned long flags;
156 166
157 if (unlikely(t < 1 || t > 3600)) /* arbitrary upper limit */ 167 if (unlikely(t < 1 || t > 3600)) /* arbitrary upper limit */
158 return -EINVAL; 168 return -EINVAL;
159 169
160 spin_lock_irqsave(&shwdt_lock, flags); 170 spin_lock_irqsave(&wdt->lock, flags);
161 heartbeat = t; 171 heartbeat = t;
162 spin_unlock_irqrestore(&shwdt_lock, flags); 172 wdt_dev->timeout = t;
173 spin_unlock_irqrestore(&wdt->lock, flags);
174
163 return 0; 175 return 0;
164} 176}
165 177
@@ -168,7 +180,7 @@ static void sh_wdt_ping(unsigned long data)
168 struct sh_wdt *wdt = (struct sh_wdt *)data; 180 struct sh_wdt *wdt = (struct sh_wdt *)data;
169 unsigned long flags; 181 unsigned long flags;
170 182
171 spin_lock_irqsave(&shwdt_lock, flags); 183 spin_lock_irqsave(&wdt->lock, flags);
172 if (time_before(jiffies, next_heartbeat)) { 184 if (time_before(jiffies, next_heartbeat)) {
173 u8 csr; 185 u8 csr;
174 186
@@ -182,137 +194,9 @@ static void sh_wdt_ping(unsigned long data)
182 } else 194 } else
183 dev_warn(wdt->dev, "Heartbeat lost! Will not ping " 195 dev_warn(wdt->dev, "Heartbeat lost! Will not ping "
184 "the watchdog\n"); 196 "the watchdog\n");
185 spin_unlock_irqrestore(&shwdt_lock, flags); 197 spin_unlock_irqrestore(&wdt->lock, flags);
186}
187
188static int sh_wdt_open(struct inode *inode, struct file *file)
189{
190 struct sh_wdt *wdt = platform_get_drvdata(sh_wdt_dev);
191
192 if (test_and_set_bit(0, &wdt->enabled))
193 return -EBUSY;
194 if (nowayout)
195 __module_get(THIS_MODULE);
196
197 file->private_data = wdt;
198
199 sh_wdt_start(wdt);
200
201 return nonseekable_open(inode, file);
202}
203
204static int sh_wdt_close(struct inode *inode, struct file *file)
205{
206 struct sh_wdt *wdt = file->private_data;
207
208 if (wdt->expect_close == 42) {
209 sh_wdt_stop(wdt);
210 } else {
211 dev_crit(wdt->dev, "Unexpected close, not "
212 "stopping watchdog!\n");
213 sh_wdt_keepalive(wdt);
214 }
215
216 clear_bit(0, &wdt->enabled);
217 wdt->expect_close = 0;
218
219 return 0;
220}
221
222static ssize_t sh_wdt_write(struct file *file, const char *buf,
223 size_t count, loff_t *ppos)
224{
225 struct sh_wdt *wdt = file->private_data;
226
227 if (count) {
228 if (!nowayout) {
229 size_t i;
230
231 wdt->expect_close = 0;
232
233 for (i = 0; i != count; i++) {
234 char c;
235 if (get_user(c, buf + i))
236 return -EFAULT;
237 if (c == 'V')
238 wdt->expect_close = 42;
239 }
240 }
241 sh_wdt_keepalive(wdt);
242 }
243
244 return count;
245} 198}
246 199
247static long sh_wdt_ioctl(struct file *file, unsigned int cmd,
248 unsigned long arg)
249{
250 struct sh_wdt *wdt = file->private_data;
251 int new_heartbeat;
252 int options, retval = -EINVAL;
253
254 switch (cmd) {
255 case WDIOC_GETSUPPORT:
256 return copy_to_user((struct watchdog_info *)arg,
257 &sh_wdt_info, sizeof(sh_wdt_info)) ? -EFAULT : 0;
258 case WDIOC_GETSTATUS:
259 case WDIOC_GETBOOTSTATUS:
260 return put_user(0, (int *)arg);
261 case WDIOC_SETOPTIONS:
262 if (get_user(options, (int *)arg))
263 return -EFAULT;
264
265 if (options & WDIOS_DISABLECARD) {
266 sh_wdt_stop(wdt);
267 retval = 0;
268 }
269
270 if (options & WDIOS_ENABLECARD) {
271 sh_wdt_start(wdt);
272 retval = 0;
273 }
274
275 return retval;
276 case WDIOC_KEEPALIVE:
277 sh_wdt_keepalive(wdt);
278 return 0;
279 case WDIOC_SETTIMEOUT:
280 if (get_user(new_heartbeat, (int *)arg))
281 return -EFAULT;
282
283 if (sh_wdt_set_heartbeat(new_heartbeat))
284 return -EINVAL;
285
286 sh_wdt_keepalive(wdt);
287 /* Fall */
288 case WDIOC_GETTIMEOUT:
289 return put_user(heartbeat, (int *)arg);
290 default:
291 return -ENOTTY;
292 }
293 return 0;
294}
295
296static int sh_wdt_notify_sys(struct notifier_block *this,
297 unsigned long code, void *unused)
298{
299 struct sh_wdt *wdt = platform_get_drvdata(sh_wdt_dev);
300
301 if (code == SYS_DOWN || code == SYS_HALT)
302 sh_wdt_stop(wdt);
303
304 return NOTIFY_DONE;
305}
306
307static const struct file_operations sh_wdt_fops = {
308 .owner = THIS_MODULE,
309 .llseek = no_llseek,
310 .write = sh_wdt_write,
311 .unlocked_ioctl = sh_wdt_ioctl,
312 .open = sh_wdt_open,
313 .release = sh_wdt_close,
314};
315
316static const struct watchdog_info sh_wdt_info = { 200static const struct watchdog_info sh_wdt_info = {
317 .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | 201 .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT |
318 WDIOF_MAGICCLOSE, 202 WDIOF_MAGICCLOSE,
@@ -320,14 +204,17 @@ static const struct watchdog_info sh_wdt_info = {
320 .identity = "SH WDT", 204 .identity = "SH WDT",
321}; 205};
322 206
323static struct notifier_block sh_wdt_notifier = { 207static const struct watchdog_ops sh_wdt_ops = {
324 .notifier_call = sh_wdt_notify_sys, 208 .owner = THIS_MODULE,
209 .start = sh_wdt_start,
210 .stop = sh_wdt_stop,
211 .ping = sh_wdt_keepalive,
212 .set_timeout = sh_wdt_set_heartbeat,
325}; 213};
326 214
327static struct miscdevice sh_wdt_miscdev = { 215static struct watchdog_device sh_wdt_dev = {
328 .minor = WATCHDOG_MINOR, 216 .info = &sh_wdt_info,
329 .name = "watchdog", 217 .ops = &sh_wdt_ops,
330 .fops = &sh_wdt_fops,
331}; 218};
332 219
333static int __devinit sh_wdt_probe(struct platform_device *pdev) 220static int __devinit sh_wdt_probe(struct platform_device *pdev)
@@ -347,39 +234,49 @@ static int __devinit sh_wdt_probe(struct platform_device *pdev)
347 if (unlikely(!res)) 234 if (unlikely(!res))
348 return -EINVAL; 235 return -EINVAL;
349 236
350 if (!devm_request_mem_region(&pdev->dev, res->start,
351 resource_size(res), DRV_NAME))
352 return -EBUSY;
353
354 wdt = devm_kzalloc(&pdev->dev, sizeof(struct sh_wdt), GFP_KERNEL); 237 wdt = devm_kzalloc(&pdev->dev, sizeof(struct sh_wdt), GFP_KERNEL);
355 if (unlikely(!wdt)) { 238 if (unlikely(!wdt))
356 rc = -ENOMEM; 239 return -ENOMEM;
357 goto out_release;
358 }
359 240
360 wdt->dev = &pdev->dev; 241 wdt->dev = &pdev->dev;
361 242
362 wdt->base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); 243 wdt->clk = clk_get(&pdev->dev, NULL);
244 if (IS_ERR(wdt->clk)) {
245 /*
246 * Clock framework support is optional, continue on
247 * anyways if we don't find a matching clock.
248 */
249 wdt->clk = NULL;
250 }
251
252 wdt->base = devm_request_and_ioremap(wdt->dev, res);
363 if (unlikely(!wdt->base)) { 253 if (unlikely(!wdt->base)) {
364 rc = -ENXIO; 254 rc = -EADDRNOTAVAIL;
365 goto out_err; 255 goto err;
366 } 256 }
367 257
368 rc = register_reboot_notifier(&sh_wdt_notifier); 258 watchdog_set_nowayout(&sh_wdt_dev, nowayout);
259 watchdog_set_drvdata(&sh_wdt_dev, wdt);
260
261 spin_lock_init(&wdt->lock);
262
263 rc = sh_wdt_set_heartbeat(&sh_wdt_dev, heartbeat);
369 if (unlikely(rc)) { 264 if (unlikely(rc)) {
370 dev_err(&pdev->dev, 265 /* Default timeout if invalid */
371 "Can't register reboot notifier (err=%d)\n", rc); 266 sh_wdt_set_heartbeat(&sh_wdt_dev, WATCHDOG_HEARTBEAT);
372 goto out_unmap; 267
268 dev_warn(&pdev->dev,
269 "heartbeat value must be 1<=x<=3600, using %d\n",
270 sh_wdt_dev.timeout);
373 } 271 }
374 272
375 sh_wdt_miscdev.parent = wdt->dev; 273 dev_info(&pdev->dev, "configured with heartbeat=%d sec (nowayout=%d)\n",
274 sh_wdt_dev.timeout, nowayout);
376 275
377 rc = misc_register(&sh_wdt_miscdev); 276 rc = watchdog_register_device(&sh_wdt_dev);
378 if (unlikely(rc)) { 277 if (unlikely(rc)) {
379 dev_err(&pdev->dev, 278 dev_err(&pdev->dev, "Can't register watchdog (err=%d)\n", rc);
380 "Can't register miscdev on minor=%d (err=%d)\n", 279 goto err;
381 sh_wdt_miscdev.minor, rc);
382 goto out_unreg;
383 } 280 }
384 281
385 init_timer(&wdt->timer); 282 init_timer(&wdt->timer);
@@ -388,20 +285,15 @@ static int __devinit sh_wdt_probe(struct platform_device *pdev)
388 wdt->timer.expires = next_ping_period(clock_division_ratio); 285 wdt->timer.expires = next_ping_period(clock_division_ratio);
389 286
390 platform_set_drvdata(pdev, wdt); 287 platform_set_drvdata(pdev, wdt);
391 sh_wdt_dev = pdev;
392 288
393 dev_info(&pdev->dev, "initialized.\n"); 289 dev_info(&pdev->dev, "initialized.\n");
394 290
291 pm_runtime_enable(&pdev->dev);
292
395 return 0; 293 return 0;
396 294
397out_unreg: 295err:
398 unregister_reboot_notifier(&sh_wdt_notifier); 296 clk_put(wdt->clk);
399out_unmap:
400 devm_iounmap(&pdev->dev, wdt->base);
401out_err:
402 devm_kfree(&pdev->dev, wdt);
403out_release:
404 devm_release_mem_region(&pdev->dev, res->start, resource_size(res));
405 297
406 return rc; 298 return rc;
407} 299}
@@ -409,36 +301,35 @@ out_release:
409static int __devexit sh_wdt_remove(struct platform_device *pdev) 301static int __devexit sh_wdt_remove(struct platform_device *pdev)
410{ 302{
411 struct sh_wdt *wdt = platform_get_drvdata(pdev); 303 struct sh_wdt *wdt = platform_get_drvdata(pdev);
412 struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
413 304
414 platform_set_drvdata(pdev, NULL); 305 platform_set_drvdata(pdev, NULL);
415 306
416 misc_deregister(&sh_wdt_miscdev); 307 watchdog_unregister_device(&sh_wdt_dev);
417 308
418 sh_wdt_dev = NULL; 309 pm_runtime_disable(&pdev->dev);
419 310 clk_put(wdt->clk);
420 unregister_reboot_notifier(&sh_wdt_notifier);
421 devm_release_mem_region(&pdev->dev, res->start, resource_size(res));
422 devm_iounmap(&pdev->dev, wdt->base);
423 devm_kfree(&pdev->dev, wdt);
424 311
425 return 0; 312 return 0;
426} 313}
427 314
315static void sh_wdt_shutdown(struct platform_device *pdev)
316{
317 sh_wdt_stop(&sh_wdt_dev);
318}
319
428static struct platform_driver sh_wdt_driver = { 320static struct platform_driver sh_wdt_driver = {
429 .driver = { 321 .driver = {
430 .name = DRV_NAME, 322 .name = DRV_NAME,
431 .owner = THIS_MODULE, 323 .owner = THIS_MODULE,
432 }, 324 },
433 325
434 .probe = sh_wdt_probe, 326 .probe = sh_wdt_probe,
435 .remove = __devexit_p(sh_wdt_remove), 327 .remove = __devexit_p(sh_wdt_remove),
328 .shutdown = sh_wdt_shutdown,
436}; 329};
437 330
438static int __init sh_wdt_init(void) 331static int __init sh_wdt_init(void)
439{ 332{
440 int rc;
441
442 if (unlikely(clock_division_ratio < 0x5 || 333 if (unlikely(clock_division_ratio < 0x5 ||
443 clock_division_ratio > 0x7)) { 334 clock_division_ratio > 0x7)) {
444 clock_division_ratio = WTCSR_CKS_4096; 335 clock_division_ratio = WTCSR_CKS_4096;
@@ -447,17 +338,6 @@ static int __init sh_wdt_init(void)
447 clock_division_ratio); 338 clock_division_ratio);
448 } 339 }
449 340
450 rc = sh_wdt_set_heartbeat(heartbeat);
451 if (unlikely(rc)) {
452 heartbeat = WATCHDOG_HEARTBEAT;
453
454 pr_info("heartbeat value must be 1<=x<=3600, using %d\n",
455 heartbeat);
456 }
457
458 pr_info("configured with heartbeat=%d sec (nowayout=%d)\n",
459 heartbeat, nowayout);
460
461 return platform_driver_register(&sh_wdt_driver); 341 return platform_driver_register(&sh_wdt_driver);
462} 342}
463 343