fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / drivers / char / watchdog / s3c2410_wdt.c
index 1699d2c..18cb050 100644 (file)
  *                             Fixed tmr_count / wdt_count confusion
  *                             Added configurable debug
  *
- *     11-Jan-2004     BJD     Fixed divide-by-2 in timeout code
+ *     11-Jan-2005     BJD     Fixed divide-by-2 in timeout code
+ *
+ *     25-Jan-2005     DA      Added suspend/resume support
+ *                             Replaced reboot notifier with .shutdown method
  *
  *     10-Mar-2005     LCVR    Changed S3C2410_VA to S3C24XX_VA
 */
 
 #include <linux/module.h>
 #include <linux/moduleparam.h>
-#include <linux/config.h>
 #include <linux/types.h>
 #include <linux/timer.h>
 #include <linux/miscdevice.h>
 #include <linux/watchdog.h>
 #include <linux/fs.h>
-#include <linux/notifier.h>
-#include <linux/reboot.h>
 #include <linux/init.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/interrupt.h>
+#include <linux/clk.h>
 
 #include <asm/uaccess.h>
 #include <asm/io.h>
 
 #include <asm/arch/map.h>
-#include <asm/hardware/clock.h>
 
 #undef S3C24XX_VA_WATCHDOG
 #define S3C24XX_VA_WATCHDOG (0)
 #define CONFIG_S3C2410_WATCHDOG_ATBOOT         (0)
 #define CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME   (15)
 
-#ifdef CONFIG_WATCHDOG_NOWAYOUT
-static int nowayout = 1;
-#else
-static int nowayout = 0;
-#endif
-
+static int nowayout    = WATCHDOG_NOWAYOUT;
 static int tmr_margin  = CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME;
 static int tmr_atboot  = CONFIG_S3C2410_WATCHDOG_ATBOOT;
 static int soft_noboot = 0;
@@ -218,11 +213,10 @@ static int s3c2410wdt_open(struct inode *inode, struct file *file)
        if(down_trylock(&open_lock))
                return -EBUSY;
 
-       if (nowayout) {
+       if (nowayout)
                __module_get(THIS_MODULE);
-       } else {
-               allow_close = CLOSE_STATE_ALLOW;
-       }
+
+       allow_close = CLOSE_STATE_NOT;
 
        /* start the timer */
        s3c2410wdt_start();
@@ -235,6 +229,7 @@ static int s3c2410wdt_release(struct inode *inode, struct file *file)
         *      Shut off the timer.
         *      Lock it in if it's a module and we set nowayout
         */
+
        if (allow_close == CLOSE_STATE_ALLOW) {
                s3c2410wdt_stop();
        } else {
@@ -293,7 +288,7 @@ static int s3c2410wdt_ioctl(struct inode *inode, struct file *file,
 
        switch (cmd) {
                default:
-                       return -ENOIOCTLCMD;
+                       return -ENOTTY;
 
                case WDIOC_GETSUPPORT:
                        return copy_to_user(argp, &s3c2410_wdt_ident,
@@ -322,23 +317,9 @@ static int s3c2410wdt_ioctl(struct inode *inode, struct file *file,
        }
 }
 
-/*
- *     Notifier for system down
- */
-
-static int s3c2410wdt_notify_sys(struct notifier_block *this, unsigned long code,
-                             void *unused)
-{
-       if(code==SYS_DOWN || code==SYS_HALT) {
-               /* Turn the WDT off */
-               s3c2410wdt_stop();
-       }
-       return NOTIFY_DONE;
-}
-
 /* kernel interface */
 
-static struct file_operations s3c2410wdt_fops = {
+static const struct file_operations s3c2410wdt_fops = {
        .owner          = THIS_MODULE,
        .llseek         = no_llseek,
        .write          = s3c2410wdt_write,
@@ -353,14 +334,9 @@ static struct miscdevice s3c2410wdt_miscdev = {
        .fops           = &s3c2410wdt_fops,
 };
 
-static struct notifier_block s3c2410wdt_notifier = {
-       .notifier_call  = s3c2410wdt_notify_sys,
-};
-
 /* interrupt handler code */
 
-static irqreturn_t s3c2410wdt_irq(int irqno, void *param,
-                                 struct pt_regs *regs)
+static irqreturn_t s3c2410wdt_irq(int irqno, void *param)
 {
        printk(KERN_INFO PFX "Watchdog timer expired!\n");
 
@@ -369,15 +345,14 @@ static irqreturn_t s3c2410wdt_irq(int irqno, void *param,
 }
 /* device interface */
 
-static int s3c2410wdt_probe(struct device *dev)
+static int s3c2410wdt_probe(struct platform_device *pdev)
 {
-       struct platform_device *pdev = to_platform_device(dev);
        struct resource *res;
        int started = 0;
        int ret;
        int size;
 
-       DBG("%s: probe=%p, device=%p\n", __FUNCTION__, pdev, dev);
+       DBG("%s: probe=%p\n", __FUNCTION__, pdev);
 
        /* get the memory region for the watchdog timer */
 
@@ -405,22 +380,24 @@ static int s3c2410wdt_probe(struct device *dev)
        res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
        if (res == NULL) {
                printk(KERN_INFO PFX "failed to get irq resource\n");
+               iounmap(wdt_base);
                return -ENOENT;
        }
 
-       ret = request_irq(res->start, s3c2410wdt_irq, 0, pdev->name, dev);
+       ret = request_irq(res->start, s3c2410wdt_irq, 0, pdev->name, pdev);
        if (ret != 0) {
                printk(KERN_INFO PFX "failed to install irq (%d)\n", ret);
+               iounmap(wdt_base);
                return ret;
        }
 
-       wdt_clock = clk_get(dev, "watchdog");
+       wdt_clock = clk_get(&pdev->dev, "watchdog");
        if (wdt_clock == NULL) {
                printk(KERN_INFO PFX "failed to find watchdog clock source\n");
+               iounmap(wdt_base);
                return -ENOENT;
        }
 
-       clk_use(wdt_clock);
        clk_enable(wdt_clock);
 
        /* see if we can actually set the requested timer margin, and if
@@ -437,30 +414,29 @@ static int s3c2410wdt_probe(struct device *dev)
                }
        }
 
-       ret = register_reboot_notifier(&s3c2410wdt_notifier);
-       if (ret) {
-               printk (KERN_ERR PFX "cannot register reboot notifier (%d)\n",
-                       ret);
-               return ret;
-       }
-
        ret = misc_register(&s3c2410wdt_miscdev);
        if (ret) {
                printk (KERN_ERR PFX "cannot register miscdev on minor=%d (%d)\n",
                        WATCHDOG_MINOR, ret);
-               unregister_reboot_notifier(&s3c2410wdt_notifier);
+               iounmap(wdt_base);
                return ret;
        }
 
        if (tmr_atboot && started == 0) {
                printk(KERN_INFO PFX "Starting Watchdog Timer\n");
                s3c2410wdt_start();
+       } else if (!tmr_atboot) {
+               /* if we're not enabling the watchdog, then ensure it is
+                * disabled if it has been left running from the bootloader
+                * or other source */
+
+               s3c2410wdt_stop();
        }
 
        return 0;
 }
 
-static int s3c2410wdt_remove(struct device *dev)
+static int s3c2410wdt_remove(struct platform_device *dev)
 {
        if (wdt_mem != NULL) {
                release_resource(wdt_mem);
@@ -475,42 +451,88 @@ static int s3c2410wdt_remove(struct device *dev)
 
        if (wdt_clock != NULL) {
                clk_disable(wdt_clock);
-               clk_unuse(wdt_clock);
                clk_put(wdt_clock);
                wdt_clock = NULL;
        }
 
+       iounmap(wdt_base);
        misc_deregister(&s3c2410wdt_miscdev);
        return 0;
 }
 
-static struct device_driver s3c2410wdt_driver = {
-       .name           = "s3c2410-wdt",
-       .bus            = &platform_bus_type,
+static void s3c2410wdt_shutdown(struct platform_device *dev)
+{
+       s3c2410wdt_stop();      
+}
+
+#ifdef CONFIG_PM
+
+static unsigned long wtcon_save;
+static unsigned long wtdat_save;
+
+static int s3c2410wdt_suspend(struct platform_device *dev, pm_message_t state)
+{
+       /* Save watchdog state, and turn it off. */
+       wtcon_save = readl(wdt_base + S3C2410_WTCON);
+       wtdat_save = readl(wdt_base + S3C2410_WTDAT);
+
+       /* Note that WTCNT doesn't need to be saved. */
+       s3c2410wdt_stop();
+
+       return 0;
+}
+
+static int s3c2410wdt_resume(struct platform_device *dev)
+{
+       /* Restore watchdog state. */
+
+       writel(wtdat_save, wdt_base + S3C2410_WTDAT);
+       writel(wtdat_save, wdt_base + S3C2410_WTCNT); /* Reset count */
+       writel(wtcon_save, wdt_base + S3C2410_WTCON);
+
+       printk(KERN_INFO PFX "watchdog %sabled\n",
+              (wtcon_save & S3C2410_WTCON_ENABLE) ? "en" : "dis");
+
+       return 0;
+}
+
+#else
+#define s3c2410wdt_suspend NULL
+#define s3c2410wdt_resume  NULL
+#endif /* CONFIG_PM */
+
+
+static struct platform_driver s3c2410wdt_driver = {
        .probe          = s3c2410wdt_probe,
        .remove         = s3c2410wdt_remove,
+       .shutdown       = s3c2410wdt_shutdown,
+       .suspend        = s3c2410wdt_suspend,
+       .resume         = s3c2410wdt_resume,
+       .driver         = {
+               .owner  = THIS_MODULE,
+               .name   = "s3c2410-wdt",
+       },
 };
 
 
-
 static char banner[] __initdata = KERN_INFO "S3C2410 Watchdog Timer, (c) 2004 Simtec Electronics\n";
 
 static int __init watchdog_init(void)
 {
        printk(banner);
-       return driver_register(&s3c2410wdt_driver);
+       return platform_driver_register(&s3c2410wdt_driver);
 }
 
 static void __exit watchdog_exit(void)
 {
-       driver_unregister(&s3c2410wdt_driver);
-       unregister_reboot_notifier(&s3c2410wdt_notifier);
+       platform_driver_unregister(&s3c2410wdt_driver);
 }
 
 module_init(watchdog_init);
 module_exit(watchdog_exit);
 
-MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
+MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>, "
+             "Dimitry Andric <dimitry.andric@tomtom.com>");
 MODULE_DESCRIPTION("S3C2410 Watchdog Device Driver");
 MODULE_LICENSE("GPL");
 MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);