aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/watchdog
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2012-05-23 12:00:40 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2012-05-23 12:00:40 -0400
commit1d767cae4dbd4116fc3b2cc3251a20760f98339f (patch)
tree79a1a48a67a9b4296ce062d61ee863fe7a46c77f /drivers/watchdog
parent6101167727932a929e37fb8a6eeb68bdbf54d58e (diff)
parent5f19f14fed7786652b9617c633db101d26a42251 (diff)
Merge tag 'sh-for-linus' of git://github.com/pmundt/linux-sh
Pull SuperH updates from Paul Mundt: - New CPUs: SH7734 (SH-4A), SH7264 and SH7269 (SH-2A) - New boards: RSK2+SH7264, RSK2+SH7269 - Unbreaking kgdb for SMP - Consolidation of _32/_64 page fault handling. - watchdog and legacy DMA chainsawing, part 1 - Conversion to evt2irq() hwirq lookup, to support relocation of vectored IRQs for irqdomains. * tag 'sh-for-linus' of git://github.com/pmundt/linux-sh: (98 commits) sh: intc: Kill off special reservation interface. sh: Enable PIO API for hp6xx and se770x. sh: Kill off machvec IRQ hinting. sh: dma: More legacy cpu dma chainsawing. sh: Kill off MAX_DMA_ADDRESS leftovers. sh: Tidy up some of the cpu legacy dma header mess. sh: Move sh4a dma header from cpu-sh4 to cpu-sh4a. sh64: Fix up vmalloc fault range check. Revert "sh: Ensure fixmap and store queue space can co-exist." serial: sh-sci: Fix for port types without BRI interrupts. sh: legacy PCI evt2irq migration. sh: cpu dma evt2irq migration. sh: sh7763rdp evt2irq migration. sh: sdk7780 evt2irq migration. sh: migor evt2irq migration. sh: landisk evt2irq migration. sh: kfr2r09 evt2irq migration. sh: ecovec24 evt2irq migration. sh: ap325rxa evt2irq migration. sh: urquell evt2irq migration. ...
Diffstat (limited to 'drivers/watchdog')
-rw-r--r--drivers/watchdog/Kconfig1
-rw-r--r--drivers/watchdog/shwdt.c306
2 files changed, 94 insertions, 213 deletions
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index eeea76f4dccb..edebaf771f50 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -1138,6 +1138,7 @@ config ZVM_WATCHDOG
1138config SH_WDT 1138config 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 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