watchdog: nowayout is bool
[linux-2.6.git] / drivers / rtc / rtc-s3c.c
index f57a87f..c543f6f 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/clk.h>
 #include <linux/log2.h>
 #include <linux/slab.h>
+#include <linux/of.h>
 
 #include <mach/hardware.h>
 #include <asm/uaccess.h>
@@ -46,21 +47,48 @@ static struct clk *rtc_clk;
 static void __iomem *s3c_rtc_base;
 static int s3c_rtc_alarmno = NO_IRQ;
 static int s3c_rtc_tickno  = NO_IRQ;
+static bool wake_en;
 static enum s3c_cpu_type s3c_rtc_cpu_type;
 
 static DEFINE_SPINLOCK(s3c_rtc_pie_lock);
 
+static void s3c_rtc_alarm_clk_enable(bool enable)
+{
+       static DEFINE_SPINLOCK(s3c_rtc_alarm_clk_lock);
+       static bool alarm_clk_enabled;
+       unsigned long irq_flags;
+
+       spin_lock_irqsave(&s3c_rtc_alarm_clk_lock, irq_flags);
+       if (enable) {
+               if (!alarm_clk_enabled) {
+                       clk_enable(rtc_clk);
+                       alarm_clk_enabled = true;
+               }
+       } else {
+               if (alarm_clk_enabled) {
+                       clk_disable(rtc_clk);
+                       alarm_clk_enabled = false;
+               }
+       }
+       spin_unlock_irqrestore(&s3c_rtc_alarm_clk_lock, irq_flags);
+}
+
 /* IRQ Handlers */
 
 static irqreturn_t s3c_rtc_alarmirq(int irq, void *id)
 {
        struct rtc_device *rdev = id;
 
+       clk_enable(rtc_clk);
        rtc_update_irq(rdev, 1, RTC_AF | RTC_IRQF);
 
        if (s3c_rtc_cpu_type == TYPE_S3C64XX)
                writeb(S3C2410_INTP_ALM, s3c_rtc_base + S3C2410_INTP);
 
+       clk_disable(rtc_clk);
+
+       s3c_rtc_alarm_clk_enable(false);
+
        return IRQ_HANDLED;
 }
 
@@ -68,56 +96,33 @@ static irqreturn_t s3c_rtc_tickirq(int irq, void *id)
 {
        struct rtc_device *rdev = id;
 
+       clk_enable(rtc_clk);
        rtc_update_irq(rdev, 1, RTC_PF | RTC_IRQF);
 
        if (s3c_rtc_cpu_type == TYPE_S3C64XX)
                writeb(S3C2410_INTP_TIC, s3c_rtc_base + S3C2410_INTP);
 
+       clk_disable(rtc_clk);
        return IRQ_HANDLED;
 }
 
 /* Update control registers */
-static void s3c_rtc_setaie(int to)
+static int s3c_rtc_setaie(struct device *dev, unsigned int enabled)
 {
        unsigned int tmp;
 
-       pr_debug("%s: aie=%d\n", __func__, to);
+       pr_debug("%s: aie=%d\n", __func__, enabled);
 
+       clk_enable(rtc_clk);
        tmp = readb(s3c_rtc_base + S3C2410_RTCALM) & ~S3C2410_RTCALM_ALMEN;
 
-       if (to)
+       if (enabled)
                tmp |= S3C2410_RTCALM_ALMEN;
 
        writeb(tmp, s3c_rtc_base + S3C2410_RTCALM);
-}
-
-static int s3c_rtc_setpie(struct device *dev, int enabled)
-{
-       unsigned int tmp;
-
-       pr_debug("%s: pie=%d\n", __func__, enabled);
-
-       spin_lock_irq(&s3c_rtc_pie_lock);
-
-       if (s3c_rtc_cpu_type == TYPE_S3C64XX) {
-               tmp = readb(s3c_rtc_base + S3C2410_RTCCON);
-               tmp &= ~S3C64XX_RTCCON_TICEN;
-
-               if (enabled)
-                       tmp |= S3C64XX_RTCCON_TICEN;
-
-               writew(tmp, s3c_rtc_base + S3C2410_RTCCON);
-       } else {
-               tmp = readb(s3c_rtc_base + S3C2410_TICNT);
-               tmp &= ~S3C2410_TICNT_ENABLE;
-
-               if (enabled)
-                       tmp |= S3C2410_TICNT_ENABLE;
-
-               writeb(tmp, s3c_rtc_base + S3C2410_TICNT);
-       }
+       clk_disable(rtc_clk);
 
-       spin_unlock_irq(&s3c_rtc_pie_lock);
+       s3c_rtc_alarm_clk_enable(enabled);
 
        return 0;
 }
@@ -131,6 +136,7 @@ static int s3c_rtc_setfreq(struct device *dev, int freq)
        if (!is_power_of_2(freq))
                return -EINVAL;
 
+       clk_enable(rtc_clk);
        spin_lock_irq(&s3c_rtc_pie_lock);
 
        if (s3c_rtc_cpu_type == TYPE_S3C2410) {
@@ -142,6 +148,7 @@ static int s3c_rtc_setfreq(struct device *dev, int freq)
 
        writel(tmp, s3c_rtc_base + S3C2410_TICNT);
        spin_unlock_irq(&s3c_rtc_pie_lock);
+       clk_disable(rtc_clk);
 
        return 0;
 }
@@ -153,6 +160,7 @@ static int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)
        unsigned int have_retried = 0;
        void __iomem *base = s3c_rtc_base;
 
+       clk_enable(rtc_clk);
  retry_get_time:
        rtc_tm->tm_min  = readb(base + S3C2410_RTCMIN);
        rtc_tm->tm_hour = readb(base + S3C2410_RTCHOUR);
@@ -171,10 +179,6 @@ static int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)
                goto retry_get_time;
        }
 
-       pr_debug("read time %02x.%02x.%02x %02x/%02x/%02x\n",
-                rtc_tm->tm_year, rtc_tm->tm_mon, rtc_tm->tm_mday,
-                rtc_tm->tm_hour, rtc_tm->tm_min, rtc_tm->tm_sec);
-
        rtc_tm->tm_sec = bcd2bin(rtc_tm->tm_sec);
        rtc_tm->tm_min = bcd2bin(rtc_tm->tm_min);
        rtc_tm->tm_hour = bcd2bin(rtc_tm->tm_hour);
@@ -183,9 +187,15 @@ static int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)
        rtc_tm->tm_year = bcd2bin(rtc_tm->tm_year);
 
        rtc_tm->tm_year += 100;
+
+       pr_debug("read time %04d.%02d.%02d %02d:%02d:%02d\n",
+                1900 + rtc_tm->tm_year, rtc_tm->tm_mon, rtc_tm->tm_mday,
+                rtc_tm->tm_hour, rtc_tm->tm_min, rtc_tm->tm_sec);
+
        rtc_tm->tm_mon -= 1;
 
-       return 0;
+       clk_disable(rtc_clk);
+       return rtc_valid_tm(rtc_tm);
 }
 
 static int s3c_rtc_settime(struct device *dev, struct rtc_time *tm)
@@ -193,8 +203,8 @@ static int s3c_rtc_settime(struct device *dev, struct rtc_time *tm)
        void __iomem *base = s3c_rtc_base;
        int year = tm->tm_year - 100;
 
-       pr_debug("set time %02d.%02d.%02d %02d/%02d/%02d\n",
-                tm->tm_year, tm->tm_mon, tm->tm_mday,
+       pr_debug("set time %04d.%02d.%02d %02d:%02d:%02d\n",
+                1900 + tm->tm_year, tm->tm_mon, tm->tm_mday,
                 tm->tm_hour, tm->tm_min, tm->tm_sec);
 
        /* we get around y2k by simply not supporting it */
@@ -204,12 +214,14 @@ static int s3c_rtc_settime(struct device *dev, struct rtc_time *tm)
                return -EINVAL;
        }
 
+       clk_enable(rtc_clk);
        writeb(bin2bcd(tm->tm_sec),  base + S3C2410_RTCSEC);
        writeb(bin2bcd(tm->tm_min),  base + S3C2410_RTCMIN);
        writeb(bin2bcd(tm->tm_hour), base + S3C2410_RTCHOUR);
        writeb(bin2bcd(tm->tm_mday), base + S3C2410_RTCDATE);
        writeb(bin2bcd(tm->tm_mon + 1), base + S3C2410_RTCMON);
        writeb(bin2bcd(year), base + S3C2410_RTCYEAR);
+       clk_disable(rtc_clk);
 
        return 0;
 }
@@ -220,6 +232,7 @@ static int s3c_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm)
        void __iomem *base = s3c_rtc_base;
        unsigned int alm_en;
 
+       clk_enable(rtc_clk);
        alm_tm->tm_sec  = readb(base + S3C2410_ALMSEC);
        alm_tm->tm_min  = readb(base + S3C2410_ALMMIN);
        alm_tm->tm_hour = readb(base + S3C2410_ALMHOUR);
@@ -231,9 +244,9 @@ static int s3c_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm)
 
        alrm->enabled = (alm_en & S3C2410_RTCALM_ALMEN) ? 1 : 0;
 
-       pr_debug("read alarm %02x %02x.%02x.%02x %02x/%02x/%02x\n",
+       pr_debug("read alarm %d, %04d.%02d.%02d %02d:%02d:%02d\n",
                 alm_en,
-                alm_tm->tm_year, alm_tm->tm_mon, alm_tm->tm_mday,
+                1900 + alm_tm->tm_year, alm_tm->tm_mon, alm_tm->tm_mday,
                 alm_tm->tm_hour, alm_tm->tm_min, alm_tm->tm_sec);
 
 
@@ -242,35 +255,36 @@ static int s3c_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm)
        if (alm_en & S3C2410_RTCALM_SECEN)
                alm_tm->tm_sec = bcd2bin(alm_tm->tm_sec);
        else
-               alm_tm->tm_sec = 0xff;
+               alm_tm->tm_sec = -1;
 
        if (alm_en & S3C2410_RTCALM_MINEN)
                alm_tm->tm_min = bcd2bin(alm_tm->tm_min);
        else
-               alm_tm->tm_min = 0xff;
+               alm_tm->tm_min = -1;
 
        if (alm_en & S3C2410_RTCALM_HOUREN)
                alm_tm->tm_hour = bcd2bin(alm_tm->tm_hour);
        else
-               alm_tm->tm_hour = 0xff;
+               alm_tm->tm_hour = -1;
 
        if (alm_en & S3C2410_RTCALM_DAYEN)
                alm_tm->tm_mday = bcd2bin(alm_tm->tm_mday);
        else
-               alm_tm->tm_mday = 0xff;
+               alm_tm->tm_mday = -1;
 
        if (alm_en & S3C2410_RTCALM_MONEN) {
                alm_tm->tm_mon = bcd2bin(alm_tm->tm_mon);
                alm_tm->tm_mon -= 1;
        } else {
-               alm_tm->tm_mon = 0xff;
+               alm_tm->tm_mon = -1;
        }
 
        if (alm_en & S3C2410_RTCALM_YEAREN)
                alm_tm->tm_year = bcd2bin(alm_tm->tm_year);
        else
-               alm_tm->tm_year = 0xffff;
+               alm_tm->tm_year = -1;
 
+       clk_disable(rtc_clk);
        return 0;
 }
 
@@ -280,11 +294,11 @@ static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
        void __iomem *base = s3c_rtc_base;
        unsigned int alrm_en;
 
-       pr_debug("s3c_rtc_setalarm: %d, %02x/%02x/%02x %02x.%02x.%02x\n",
+       clk_enable(rtc_clk);
+       pr_debug("s3c_rtc_setalarm: %d, %04d.%02d.%02d %02d:%02d:%02d\n",
                 alrm->enabled,
-                tm->tm_mday & 0xff, tm->tm_mon & 0xff, tm->tm_year & 0xff,
-                tm->tm_hour & 0xff, tm->tm_min & 0xff, tm->tm_sec);
-
+                1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday,
+                tm->tm_hour, tm->tm_min, tm->tm_sec);
 
        alrm_en = readb(base + S3C2410_RTCALM) & S3C2410_RTCALM_ALMEN;
        writeb(0x00, base + S3C2410_RTCALM);
@@ -308,8 +322,9 @@ static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
 
        writeb(alrm_en, base + S3C2410_RTCALM);
 
-       s3c_rtc_setaie(alrm->enabled);
+       s3c_rtc_setaie(dev, alrm->enabled);
 
+       clk_disable(rtc_clk);
        return 0;
 }
 
@@ -317,8 +332,9 @@ static int s3c_rtc_proc(struct device *dev, struct seq_file *seq)
 {
        unsigned int ticnt;
 
+       clk_enable(rtc_clk);
        if (s3c_rtc_cpu_type == TYPE_S3C64XX) {
-               ticnt = readb(s3c_rtc_base + S3C2410_RTCCON);
+               ticnt = readw(s3c_rtc_base + S3C2410_RTCCON);
                ticnt &= S3C64XX_RTCCON_TICEN;
        } else {
                ticnt = readb(s3c_rtc_base + S3C2410_TICNT);
@@ -326,60 +342,17 @@ static int s3c_rtc_proc(struct device *dev, struct seq_file *seq)
        }
 
        seq_printf(seq, "periodic_IRQ\t: %s\n", ticnt  ? "yes" : "no");
+       clk_disable(rtc_clk);
        return 0;
 }
 
-static int s3c_rtc_open(struct device *dev)
-{
-       struct platform_device *pdev = to_platform_device(dev);
-       struct rtc_device *rtc_dev = platform_get_drvdata(pdev);
-       int ret;
-
-       ret = request_irq(s3c_rtc_alarmno, s3c_rtc_alarmirq,
-                         IRQF_DISABLED,  "s3c2410-rtc alarm", rtc_dev);
-
-       if (ret) {
-               dev_err(dev, "IRQ%d error %d\n", s3c_rtc_alarmno, ret);
-               return ret;
-       }
-
-       ret = request_irq(s3c_rtc_tickno, s3c_rtc_tickirq,
-                         IRQF_DISABLED,  "s3c2410-rtc tick", rtc_dev);
-
-       if (ret) {
-               dev_err(dev, "IRQ%d error %d\n", s3c_rtc_tickno, ret);
-               goto tick_err;
-       }
-
-       return ret;
-
- tick_err:
-       free_irq(s3c_rtc_alarmno, rtc_dev);
-       return ret;
-}
-
-static void s3c_rtc_release(struct device *dev)
-{
-       struct platform_device *pdev = to_platform_device(dev);
-       struct rtc_device *rtc_dev = platform_get_drvdata(pdev);
-
-       /* do not clear AIE here, it may be needed for wake */
-
-       s3c_rtc_setpie(dev, 0);
-       free_irq(s3c_rtc_alarmno, rtc_dev);
-       free_irq(s3c_rtc_tickno, rtc_dev);
-}
-
 static const struct rtc_class_ops s3c_rtcops = {
-       .open           = s3c_rtc_open,
-       .release        = s3c_rtc_release,
        .read_time      = s3c_rtc_gettime,
        .set_time       = s3c_rtc_settime,
        .read_alarm     = s3c_rtc_getalarm,
        .set_alarm      = s3c_rtc_setalarm,
-       .irq_set_freq   = s3c_rtc_setfreq,
-       .irq_set_state  = s3c_rtc_setpie,
-       .proc           = s3c_rtc_proc,
+       .proc           = s3c_rtc_proc,
+       .alarm_irq_enable = s3c_rtc_setaie,
 };
 
 static void s3c_rtc_enable(struct platform_device *pdev, int en)
@@ -390,12 +363,13 @@ static void s3c_rtc_enable(struct platform_device *pdev, int en)
        if (s3c_rtc_base == NULL)
                return;
 
+       clk_enable(rtc_clk);
        if (!en) {
-               tmp = readb(base + S3C2410_RTCCON);
+               tmp = readw(base + S3C2410_RTCCON);
                if (s3c_rtc_cpu_type == TYPE_S3C64XX)
                        tmp &= ~S3C64XX_RTCCON_TICEN;
                tmp &= ~S3C2410_RTCCON_RTCEN;
-               writeb(tmp, base + S3C2410_RTCCON);
+               writew(tmp, base + S3C2410_RTCCON);
 
                if (s3c_rtc_cpu_type == TYPE_S3C2410) {
                        tmp = readb(base + S3C2410_TICNT);
@@ -405,40 +379,45 @@ static void s3c_rtc_enable(struct platform_device *pdev, int en)
        } else {
                /* re-enable the device, and check it is ok */
 
-               if ((readb(base+S3C2410_RTCCON) & S3C2410_RTCCON_RTCEN) == 0){
+               if ((readw(base+S3C2410_RTCCON) & S3C2410_RTCCON_RTCEN) == 0) {
                        dev_info(&pdev->dev, "rtc disabled, re-enabling\n");
 
-                       tmp = readb(base + S3C2410_RTCCON);
-                       writeb(tmp|S3C2410_RTCCON_RTCEN, base+S3C2410_RTCCON);
+                       tmp = readw(base + S3C2410_RTCCON);
+                       writew(tmp | S3C2410_RTCCON_RTCEN,
+                               base + S3C2410_RTCCON);
                }
 
-               if ((readb(base + S3C2410_RTCCON) & S3C2410_RTCCON_CNTSEL)){
+               if ((readw(base + S3C2410_RTCCON) & S3C2410_RTCCON_CNTSEL)) {
                        dev_info(&pdev->dev, "removing RTCCON_CNTSEL\n");
 
-                       tmp = readb(base + S3C2410_RTCCON);
-                       writeb(tmp& ~S3C2410_RTCCON_CNTSEL, base+S3C2410_RTCCON);
+                       tmp = readw(base + S3C2410_RTCCON);
+                       writew(tmp & ~S3C2410_RTCCON_CNTSEL,
+                               base + S3C2410_RTCCON);
                }
 
-               if ((readb(base + S3C2410_RTCCON) & S3C2410_RTCCON_CLKRST)){
+               if ((readw(base + S3C2410_RTCCON) & S3C2410_RTCCON_CLKRST)) {
                        dev_info(&pdev->dev, "removing RTCCON_CLKRST\n");
 
-                       tmp = readb(base + S3C2410_RTCCON);
-                       writeb(tmp & ~S3C2410_RTCCON_CLKRST, base+S3C2410_RTCCON);
+                       tmp = readw(base + S3C2410_RTCCON);
+                       writew(tmp & ~S3C2410_RTCCON_CLKRST,
+                               base + S3C2410_RTCCON);
                }
        }
+       clk_disable(rtc_clk);
 }
 
 static int __devexit s3c_rtc_remove(struct platform_device *dev)
 {
        struct rtc_device *rtc = platform_get_drvdata(dev);
 
+       free_irq(s3c_rtc_alarmno, rtc);
+       free_irq(s3c_rtc_tickno, rtc);
+
        platform_set_drvdata(dev, NULL);
        rtc_device_unregister(rtc);
 
-       s3c_rtc_setpie(&dev->dev, 0);
-       s3c_rtc_setaie(0);
+       s3c_rtc_setaie(&dev->dev, 0);
 
-       clk_disable(rtc_clk);
        clk_put(rtc_clk);
        rtc_clk = NULL;
 
@@ -452,8 +431,8 @@ static int __devexit s3c_rtc_remove(struct platform_device *dev)
 static int __devinit s3c_rtc_probe(struct platform_device *pdev)
 {
        struct rtc_device *rtc;
+       struct rtc_time rtc_tm;
        struct resource *res;
-       unsigned int tmp, i;
        int ret;
 
        pr_debug("%s: probe=%p\n", __func__, pdev);
@@ -483,8 +462,7 @@ static int __devinit s3c_rtc_probe(struct platform_device *pdev)
                return -ENOENT;
        }
 
-       s3c_rtc_mem = request_mem_region(res->start,
-                                        res->end-res->start+1,
+       s3c_rtc_mem = request_mem_region(res->start, resource_size(res),
                                         pdev->name);
 
        if (s3c_rtc_mem == NULL) {
@@ -493,7 +471,7 @@ static int __devinit s3c_rtc_probe(struct platform_device *pdev)
                goto err_nores;
        }
 
-       s3c_rtc_base = ioremap(res->start, res->end - res->start + 1);
+       s3c_rtc_base = ioremap(res->start, resource_size(res));
        if (s3c_rtc_base == NULL) {
                dev_err(&pdev->dev, "failed ioremap()\n");
                ret = -EINVAL;
@@ -514,8 +492,8 @@ static int __devinit s3c_rtc_probe(struct platform_device *pdev)
 
        s3c_rtc_enable(pdev, 1);
 
-       pr_debug("s3c2410_rtc: RTCCON=%02x\n",
-                readb(s3c_rtc_base + S3C2410_RTCCON));
+       pr_debug("s3c2410_rtc: RTCCON=%02x\n",
+                readw(s3c_rtc_base + S3C2410_RTCCON));
 
        device_init_wakeup(&pdev->dev, 1);
 
@@ -530,15 +508,29 @@ static int __devinit s3c_rtc_probe(struct platform_device *pdev)
                goto err_nortc;
        }
 
-       s3c_rtc_cpu_type = platform_get_device_id(pdev)->driver_data;
+#ifdef CONFIG_OF
+       if (pdev->dev.of_node)
+               s3c_rtc_cpu_type = of_device_is_compatible(pdev->dev.of_node,
+                       "samsung,s3c6410-rtc") ? TYPE_S3C64XX : TYPE_S3C2410;
+       else
+#endif
+               s3c_rtc_cpu_type = platform_get_device_id(pdev)->driver_data;
 
        /* Check RTC Time */
 
-       for (i = S3C2410_RTCSEC; i <= S3C2410_RTCYEAR; i += 0x4) {
-               tmp = readb(s3c_rtc_base + i);
+       s3c_rtc_gettime(NULL, &rtc_tm);
 
-               if ((tmp & 0xf) > 0x9 || ((tmp >> 4) & 0xf) > 0x9)
-                       writeb(0, s3c_rtc_base + i);
+       if (rtc_valid_tm(&rtc_tm)) {
+               rtc_tm.tm_year  = 100;
+               rtc_tm.tm_mon   = 0;
+               rtc_tm.tm_mday  = 1;
+               rtc_tm.tm_hour  = 0;
+               rtc_tm.tm_min   = 0;
+               rtc_tm.tm_sec   = 0;
+
+               s3c_rtc_settime(NULL, &rtc_tm);
+
+               dev_warn(&pdev->dev, "warning: invalid RTC value so initializing it\n");
        }
 
        if (s3c_rtc_cpu_type == TYPE_S3C64XX)
@@ -550,8 +542,32 @@ static int __devinit s3c_rtc_probe(struct platform_device *pdev)
 
        s3c_rtc_setfreq(&pdev->dev, 1);
 
+       ret = request_irq(s3c_rtc_alarmno, s3c_rtc_alarmirq,
+                         0,  "s3c2410-rtc alarm", rtc);
+       if (ret) {
+               dev_err(&pdev->dev, "IRQ%d error %d\n", s3c_rtc_alarmno, ret);
+               goto err_alarm_irq;
+       }
+
+       ret = request_irq(s3c_rtc_tickno, s3c_rtc_tickirq,
+                         0,  "s3c2410-rtc tick", rtc);
+       if (ret) {
+               dev_err(&pdev->dev, "IRQ%d error %d\n", s3c_rtc_tickno, ret);
+               free_irq(s3c_rtc_alarmno, rtc);
+               goto err_tick_irq;
+       }
+
+       clk_disable(rtc_clk);
+
        return 0;
 
+ err_tick_irq:
+       free_irq(s3c_rtc_alarmno, rtc);
+
+ err_alarm_irq:
+       platform_set_drvdata(pdev, NULL);
+       rtc_device_unregister(rtc);
+
  err_nortc:
        s3c_rtc_enable(pdev, 0);
        clk_disable(rtc_clk);
@@ -575,16 +591,22 @@ static int ticnt_save, ticnt_en_save;
 
 static int s3c_rtc_suspend(struct platform_device *pdev, pm_message_t state)
 {
+       clk_enable(rtc_clk);
        /* save TICNT for anyone using periodic interrupts */
        ticnt_save = readb(s3c_rtc_base + S3C2410_TICNT);
        if (s3c_rtc_cpu_type == TYPE_S3C64XX) {
-               ticnt_en_save = readb(s3c_rtc_base + S3C2410_RTCCON);
+               ticnt_en_save = readw(s3c_rtc_base + S3C2410_RTCCON);
                ticnt_en_save &= S3C64XX_RTCCON_TICEN;
        }
        s3c_rtc_enable(pdev, 0);
 
-       if (device_may_wakeup(&pdev->dev))
-               enable_irq_wake(s3c_rtc_alarmno);
+       if (device_may_wakeup(&pdev->dev) && !wake_en) {
+               if (enable_irq_wake(s3c_rtc_alarmno) == 0)
+                       wake_en = true;
+               else
+                       dev_err(&pdev->dev, "enable_irq_wake failed\n");
+       }
+       clk_disable(rtc_clk);
 
        return 0;
 }
@@ -593,15 +615,19 @@ static int s3c_rtc_resume(struct platform_device *pdev)
 {
        unsigned int tmp;
 
+       clk_enable(rtc_clk);
        s3c_rtc_enable(pdev, 1);
        writeb(ticnt_save, s3c_rtc_base + S3C2410_TICNT);
        if (s3c_rtc_cpu_type == TYPE_S3C64XX && ticnt_en_save) {
-               tmp = readb(s3c_rtc_base + S3C2410_RTCCON);
-               writeb(tmp | ticnt_en_save, s3c_rtc_base + S3C2410_RTCCON);
+               tmp = readw(s3c_rtc_base + S3C2410_RTCCON);
+               writew(tmp | ticnt_en_save, s3c_rtc_base + S3C2410_RTCCON);
        }
 
-       if (device_may_wakeup(&pdev->dev))
+       if (device_may_wakeup(&pdev->dev) && wake_en) {
                disable_irq_wake(s3c_rtc_alarmno);
+               wake_en = false;
+       }
+       clk_disable(rtc_clk);
 
        return 0;
 }
@@ -610,6 +636,17 @@ static int s3c_rtc_resume(struct platform_device *pdev)
 #define s3c_rtc_resume  NULL
 #endif
 
+#ifdef CONFIG_OF
+static const struct of_device_id s3c_rtc_dt_match[] = {
+       { .compatible = "samsung,s3c2410-rtc" },
+       { .compatible = "samsung,s3c6410-rtc" },
+       {},
+};
+MODULE_DEVICE_TABLE(of, s3c_rtc_dt_match);
+#else
+#define s3c_rtc_dt_match NULL
+#endif
+
 static struct platform_device_id s3c_rtc_driver_ids[] = {
        {
                .name           = "s3c2410-rtc",
@@ -632,24 +669,11 @@ static struct platform_driver s3c_rtc_driver = {
        .driver         = {
                .name   = "s3c-rtc",
                .owner  = THIS_MODULE,
+               .of_match_table = s3c_rtc_dt_match,
        },
 };
 
-static char __initdata banner[] = "S3C24XX RTC, (c) 2004,2006 Simtec Electronics\n";
-
-static int __init s3c_rtc_init(void)
-{
-       printk(banner);
-       return platform_driver_register(&s3c_rtc_driver);
-}
-
-static void __exit s3c_rtc_exit(void)
-{
-       platform_driver_unregister(&s3c_rtc_driver);
-}
-
-module_init(s3c_rtc_init);
-module_exit(s3c_rtc_exit);
+module_platform_driver(s3c_rtc_driver);
 
 MODULE_DESCRIPTION("Samsung S3C RTC Driver");
 MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");