aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/watchdog/Kconfig1
-rw-r--r--drivers/watchdog/s3c2410_wdt.c176
2 files changed, 38 insertions, 139 deletions
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 64c6752ea2c6..40a36c50a5f4 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -170,6 +170,7 @@ config HAVE_S3C2410_WATCHDOG
170config S3C2410_WATCHDOG 170config S3C2410_WATCHDOG
171 tristate "S3C2410 Watchdog" 171 tristate "S3C2410 Watchdog"
172 depends on ARCH_S3C2410 || HAVE_S3C2410_WATCHDOG 172 depends on ARCH_S3C2410 || HAVE_S3C2410_WATCHDOG
173 select WATCHDOG_CORE
173 help 174 help
174 Watchdog timer block in the Samsung SoCs. This will reboot 175 Watchdog timer block in the Samsung SoCs. This will reboot
175 the system when the timer expires with the watchdog enabled. 176 the system when the timer expires with the watchdog enabled.
diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c
index 30da88f47cd3..5de7e4fa5b8a 100644
--- a/drivers/watchdog/s3c2410_wdt.c
+++ b/drivers/watchdog/s3c2410_wdt.c
@@ -27,9 +27,8 @@
27#include <linux/moduleparam.h> 27#include <linux/moduleparam.h>
28#include <linux/types.h> 28#include <linux/types.h>
29#include <linux/timer.h> 29#include <linux/timer.h>
30#include <linux/miscdevice.h> 30#include <linux/miscdevice.h> /* for MODULE_ALIAS_MISCDEV */
31#include <linux/watchdog.h> 31#include <linux/watchdog.h>
32#include <linux/fs.h>
33#include <linux/init.h> 32#include <linux/init.h>
34#include <linux/platform_device.h> 33#include <linux/platform_device.h>
35#include <linux/interrupt.h> 34#include <linux/interrupt.h>
@@ -38,6 +37,7 @@
38#include <linux/io.h> 37#include <linux/io.h>
39#include <linux/cpufreq.h> 38#include <linux/cpufreq.h>
40#include <linux/slab.h> 39#include <linux/slab.h>
40#include <linux/err.h>
41 41
42#include <mach/map.h> 42#include <mach/map.h>
43 43
@@ -74,14 +74,12 @@ MODULE_PARM_DESC(soft_noboot, "Watchdog action, set to 1 to ignore reboots, "
74 "0 to reboot (default 0)"); 74 "0 to reboot (default 0)");
75MODULE_PARM_DESC(debug, "Watchdog debug, set to >1 for debug (default 0)"); 75MODULE_PARM_DESC(debug, "Watchdog debug, set to >1 for debug (default 0)");
76 76
77static unsigned long open_lock;
78static struct device *wdt_dev; /* platform device attached to */ 77static struct device *wdt_dev; /* platform device attached to */
79static struct resource *wdt_mem; 78static struct resource *wdt_mem;
80static struct resource *wdt_irq; 79static struct resource *wdt_irq;
81static struct clk *wdt_clock; 80static struct clk *wdt_clock;
82static void __iomem *wdt_base; 81static void __iomem *wdt_base;
83static unsigned int wdt_count; 82static unsigned int wdt_count;
84static char expect_close;
85static DEFINE_SPINLOCK(wdt_lock); 83static DEFINE_SPINLOCK(wdt_lock);
86 84
87/* watchdog control routines */ 85/* watchdog control routines */
@@ -93,11 +91,13 @@ static DEFINE_SPINLOCK(wdt_lock);
93 91
94/* functions */ 92/* functions */
95 93
96static void s3c2410wdt_keepalive(void) 94static int s3c2410wdt_keepalive(struct watchdog_device *wdd)
97{ 95{
98 spin_lock(&wdt_lock); 96 spin_lock(&wdt_lock);
99 writel(wdt_count, wdt_base + S3C2410_WTCNT); 97 writel(wdt_count, wdt_base + S3C2410_WTCNT);
100 spin_unlock(&wdt_lock); 98 spin_unlock(&wdt_lock);
99
100 return 0;
101} 101}
102 102
103static void __s3c2410wdt_stop(void) 103static void __s3c2410wdt_stop(void)
@@ -109,14 +109,16 @@ static void __s3c2410wdt_stop(void)
109 writel(wtcon, wdt_base + S3C2410_WTCON); 109 writel(wtcon, wdt_base + S3C2410_WTCON);
110} 110}
111 111
112static void s3c2410wdt_stop(void) 112static int s3c2410wdt_stop(struct watchdog_device *wdd)
113{ 113{
114 spin_lock(&wdt_lock); 114 spin_lock(&wdt_lock);
115 __s3c2410wdt_stop(); 115 __s3c2410wdt_stop();
116 spin_unlock(&wdt_lock); 116 spin_unlock(&wdt_lock);
117
118 return 0;
117} 119}
118 120
119static void s3c2410wdt_start(void) 121static int s3c2410wdt_start(struct watchdog_device *wdd)
120{ 122{
121 unsigned long wtcon; 123 unsigned long wtcon;
122 124
@@ -142,6 +144,8 @@ static void s3c2410wdt_start(void)
142 writel(wdt_count, wdt_base + S3C2410_WTCNT); 144 writel(wdt_count, wdt_base + S3C2410_WTCNT);
143 writel(wtcon, wdt_base + S3C2410_WTCON); 145 writel(wtcon, wdt_base + S3C2410_WTCON);
144 spin_unlock(&wdt_lock); 146 spin_unlock(&wdt_lock);
147
148 return 0;
145} 149}
146 150
147static inline int s3c2410wdt_is_running(void) 151static inline int s3c2410wdt_is_running(void)
@@ -149,7 +153,7 @@ static inline int s3c2410wdt_is_running(void)
149 return readl(wdt_base + S3C2410_WTCON) & S3C2410_WTCON_ENABLE; 153 return readl(wdt_base + S3C2410_WTCON) & S3C2410_WTCON_ENABLE;
150} 154}
151 155
152static int s3c2410wdt_set_heartbeat(int timeout) 156static int s3c2410wdt_set_heartbeat(struct watchdog_device *wdd, unsigned timeout)
153{ 157{
154 unsigned long freq = clk_get_rate(wdt_clock); 158 unsigned long freq = clk_get_rate(wdt_clock);
155 unsigned int count; 159 unsigned int count;
@@ -182,8 +186,6 @@ static int s3c2410wdt_set_heartbeat(int timeout)
182 } 186 }
183 } 187 }
184 188
185 tmr_margin = timeout;
186
187 DBG("%s: timeout=%d, divisor=%d, count=%d (%08x)\n", 189 DBG("%s: timeout=%d, divisor=%d, count=%d (%08x)\n",
188 __func__, timeout, divisor, count, count/divisor); 190 __func__, timeout, divisor, count, count/divisor);
189 191
@@ -201,70 +203,6 @@ static int s3c2410wdt_set_heartbeat(int timeout)
201 return 0; 203 return 0;
202} 204}
203 205
204/*
205 * /dev/watchdog handling
206 */
207
208static int s3c2410wdt_open(struct inode *inode, struct file *file)
209{
210 if (test_and_set_bit(0, &open_lock))
211 return -EBUSY;
212
213 if (nowayout)
214 __module_get(THIS_MODULE);
215
216 expect_close = 0;
217
218 /* start the timer */
219 s3c2410wdt_start();
220 return nonseekable_open(inode, file);
221}
222
223static int s3c2410wdt_release(struct inode *inode, struct file *file)
224{
225 /*
226 * Shut off the timer.
227 * Lock it in if it's a module and we set nowayout
228 */
229
230 if (expect_close == 42)
231 s3c2410wdt_stop();
232 else {
233 dev_err(wdt_dev, "Unexpected close, not stopping watchdog\n");
234 s3c2410wdt_keepalive();
235 }
236 expect_close = 0;
237 clear_bit(0, &open_lock);
238 return 0;
239}
240
241static ssize_t s3c2410wdt_write(struct file *file, const char __user *data,
242 size_t len, loff_t *ppos)
243{
244 /*
245 * Refresh the timer.
246 */
247 if (len) {
248 if (!nowayout) {
249 size_t i;
250
251 /* In case it was set long ago */
252 expect_close = 0;
253
254 for (i = 0; i != len; i++) {
255 char c;
256
257 if (get_user(c, data + i))
258 return -EFAULT;
259 if (c == 'V')
260 expect_close = 42;
261 }
262 }
263 s3c2410wdt_keepalive();
264 }
265 return len;
266}
267
268#define OPTIONS (WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE) 206#define OPTIONS (WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE)
269 207
270static const struct watchdog_info s3c2410_wdt_ident = { 208static const struct watchdog_info s3c2410_wdt_ident = {
@@ -273,53 +211,17 @@ static const struct watchdog_info s3c2410_wdt_ident = {
273 .identity = "S3C2410 Watchdog", 211 .identity = "S3C2410 Watchdog",
274}; 212};
275 213
276 214static struct watchdog_ops s3c2410wdt_ops = {
277static long s3c2410wdt_ioctl(struct file *file, unsigned int cmd, 215 .owner = THIS_MODULE,
278 unsigned long arg) 216 .start = s3c2410wdt_start,
279{ 217 .stop = s3c2410wdt_stop,
280 void __user *argp = (void __user *)arg; 218 .ping = s3c2410wdt_keepalive,
281 int __user *p = argp; 219 .set_timeout = s3c2410wdt_set_heartbeat,
282 int new_margin;
283
284 switch (cmd) {
285 case WDIOC_GETSUPPORT:
286 return copy_to_user(argp, &s3c2410_wdt_ident,
287 sizeof(s3c2410_wdt_ident)) ? -EFAULT : 0;
288 case WDIOC_GETSTATUS:
289 case WDIOC_GETBOOTSTATUS:
290 return put_user(0, p);
291 case WDIOC_KEEPALIVE:
292 s3c2410wdt_keepalive();
293 return 0;
294 case WDIOC_SETTIMEOUT:
295 if (get_user(new_margin, p))
296 return -EFAULT;
297 if (s3c2410wdt_set_heartbeat(new_margin))
298 return -EINVAL;
299 s3c2410wdt_keepalive();
300 return put_user(tmr_margin, p);
301 case WDIOC_GETTIMEOUT:
302 return put_user(tmr_margin, p);
303 default:
304 return -ENOTTY;
305 }
306}
307
308/* kernel interface */
309
310static const struct file_operations s3c2410wdt_fops = {
311 .owner = THIS_MODULE,
312 .llseek = no_llseek,
313 .write = s3c2410wdt_write,
314 .unlocked_ioctl = s3c2410wdt_ioctl,
315 .open = s3c2410wdt_open,
316 .release = s3c2410wdt_release,
317}; 220};
318 221
319static struct miscdevice s3c2410wdt_miscdev = { 222static struct watchdog_device s3c2410_wdd = {
320 .minor = WATCHDOG_MINOR, 223 .info = &s3c2410_wdt_ident,
321 .name = "watchdog", 224 .ops = &s3c2410wdt_ops,
322 .fops = &s3c2410wdt_fops,
323}; 225};
324 226
325/* interrupt handler code */ 227/* interrupt handler code */
@@ -328,7 +230,7 @@ static irqreturn_t s3c2410wdt_irq(int irqno, void *param)
328{ 230{
329 dev_info(wdt_dev, "watchdog timer expired (irq)\n"); 231 dev_info(wdt_dev, "watchdog timer expired (irq)\n");
330 232
331 s3c2410wdt_keepalive(); 233 s3c2410wdt_keepalive(&s3c2410_wdd);
332 return IRQ_HANDLED; 234 return IRQ_HANDLED;
333} 235}
334 236
@@ -349,14 +251,14 @@ static int s3c2410wdt_cpufreq_transition(struct notifier_block *nb,
349 * the watchdog is running. 251 * the watchdog is running.
350 */ 252 */
351 253
352 s3c2410wdt_keepalive(); 254 s3c2410wdt_keepalive(&s3c2410_wdd);
353 } else if (val == CPUFREQ_POSTCHANGE) { 255 } else if (val == CPUFREQ_POSTCHANGE) {
354 s3c2410wdt_stop(); 256 s3c2410wdt_stop(&s3c2410_wdd);
355 257
356 ret = s3c2410wdt_set_heartbeat(tmr_margin); 258 ret = s3c2410wdt_set_heartbeat(&s3c2410_wdd, s3c2410_wdd.timeout);
357 259
358 if (ret >= 0) 260 if (ret >= 0)
359 s3c2410wdt_start(); 261 s3c2410wdt_start(&s3c2410_wdd);
360 else 262 else
361 goto err; 263 goto err;
362 } 264 }
@@ -365,7 +267,8 @@ done:
365 return 0; 267 return 0;
366 268
367 err: 269 err:
368 dev_err(wdt_dev, "cannot set new value for timeout %d\n", tmr_margin); 270 dev_err(wdt_dev, "cannot set new value for timeout %d\n",
271 s3c2410_wdd.timeout);
369 return ret; 272 return ret;
370} 273}
371 274
@@ -396,10 +299,6 @@ static inline void s3c2410wdt_cpufreq_deregister(void)
396} 299}
397#endif 300#endif
398 301
399
400
401/* device interface */
402
403static int __devinit s3c2410wdt_probe(struct platform_device *pdev) 302static int __devinit s3c2410wdt_probe(struct platform_device *pdev)
404{ 303{
405 struct device *dev; 304 struct device *dev;
@@ -466,8 +365,8 @@ static int __devinit s3c2410wdt_probe(struct platform_device *pdev)
466 /* see if we can actually set the requested timer margin, and if 365 /* see if we can actually set the requested timer margin, and if
467 * not, try the default value */ 366 * not, try the default value */
468 367
469 if (s3c2410wdt_set_heartbeat(tmr_margin)) { 368 if (s3c2410wdt_set_heartbeat(&s3c2410_wdd, tmr_margin)) {
470 started = s3c2410wdt_set_heartbeat( 369 started = s3c2410wdt_set_heartbeat(&s3c2410_wdd,
471 CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME); 370 CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME);
472 371
473 if (started == 0) 372 if (started == 0)
@@ -479,22 +378,21 @@ static int __devinit s3c2410wdt_probe(struct platform_device *pdev)
479 "cannot start\n"); 378 "cannot start\n");
480 } 379 }
481 380
482 ret = misc_register(&s3c2410wdt_miscdev); 381 ret = watchdog_register_device(&s3c2410_wdd);
483 if (ret) { 382 if (ret) {
484 dev_err(dev, "cannot register miscdev on minor=%d (%d)\n", 383 dev_err(dev, "cannot register watchdog (%d)\n", ret);
485 WATCHDOG_MINOR, ret);
486 goto err_cpufreq; 384 goto err_cpufreq;
487 } 385 }
488 386
489 if (tmr_atboot && started == 0) { 387 if (tmr_atboot && started == 0) {
490 dev_info(dev, "starting watchdog timer\n"); 388 dev_info(dev, "starting watchdog timer\n");
491 s3c2410wdt_start(); 389 s3c2410wdt_start(&s3c2410_wdd);
492 } else if (!tmr_atboot) { 390 } else if (!tmr_atboot) {
493 /* if we're not enabling the watchdog, then ensure it is 391 /* if we're not enabling the watchdog, then ensure it is
494 * disabled if it has been left running from the bootloader 392 * disabled if it has been left running from the bootloader
495 * or other source */ 393 * or other source */
496 394
497 s3c2410wdt_stop(); 395 s3c2410wdt_stop(&s3c2410_wdd);
498 } 396 }
499 397
500 /* print out a statement of readiness */ 398 /* print out a statement of readiness */
@@ -530,7 +428,7 @@ static int __devinit s3c2410wdt_probe(struct platform_device *pdev)
530 428
531static int __devexit s3c2410wdt_remove(struct platform_device *dev) 429static int __devexit s3c2410wdt_remove(struct platform_device *dev)
532{ 430{
533 misc_deregister(&s3c2410wdt_miscdev); 431 watchdog_unregister_device(&s3c2410_wdd);
534 432
535 s3c2410wdt_cpufreq_deregister(); 433 s3c2410wdt_cpufreq_deregister();
536 434
@@ -550,7 +448,7 @@ static int __devexit s3c2410wdt_remove(struct platform_device *dev)
550 448
551static void s3c2410wdt_shutdown(struct platform_device *dev) 449static void s3c2410wdt_shutdown(struct platform_device *dev)
552{ 450{
553 s3c2410wdt_stop(); 451 s3c2410wdt_stop(&s3c2410_wdd);
554} 452}
555 453
556#ifdef CONFIG_PM 454#ifdef CONFIG_PM
@@ -565,7 +463,7 @@ static int s3c2410wdt_suspend(struct platform_device *dev, pm_message_t state)
565 wtdat_save = readl(wdt_base + S3C2410_WTDAT); 463 wtdat_save = readl(wdt_base + S3C2410_WTDAT);
566 464
567 /* Note that WTCNT doesn't need to be saved. */ 465 /* Note that WTCNT doesn't need to be saved. */
568 s3c2410wdt_stop(); 466 s3c2410wdt_stop(&s3c2410_wdd);
569 467
570 return 0; 468 return 0;
571} 469}