]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/rtc/rtc-cmos.c
generic-ipi: powerpc/generic-ipi tree build failure
[linux-2.6-omap-h63xx.git] / drivers / rtc / rtc-cmos.c
index ab455ddb16cf9f053b411d31ab5eeb67d6834e94..d060a06ce05b67c7351a73e3a76bbd34444eb54c 100644 (file)
 #include <linux/platform_device.h>
 #include <linux/mod_devicetable.h>
 
+#ifdef CONFIG_HPET_EMULATE_RTC
+#include <asm/hpet.h>
+#endif
+
 /* this is for "generic access to PC-style RTC" using CMOS_READ/CMOS_WRITE */
 #include <asm-generic/rtc.h>
 
+#ifndef CONFIG_HPET_EMULATE_RTC
+#define is_hpet_enabled()                      0
+#define hpet_set_alarm_time(hrs, min, sec)     do { } while (0)
+#define hpet_set_periodic_freq(arg)            0
+#define hpet_mask_rtc_irq_bit(arg)             do { } while (0)
+#define hpet_set_rtc_irq_bit(arg)              do { } while (0)
+#define hpet_rtc_timer_init()                  do { } while (0)
+#define hpet_register_irq_handler(h)           0
+#define hpet_unregister_irq_handler(h)         do { } while (0)
+extern irqreturn_t hpet_rtc_interrupt(int irq, void *dev_id);
+#endif
 
 struct cmos_rtc {
        struct rtc_device       *rtc;
@@ -183,9 +198,8 @@ static int cmos_set_alarm(struct device *dev, struct rtc_wkalrm *t)
 
        /* Writing 0xff means "don't care" or "match all".  */
 
-       mon = t->time.tm_mon;
-       mon = (mon < 12) ? BIN2BCD(mon) : 0xff;
-       mon++;
+       mon = t->time.tm_mon + 1;
+       mon = (mon <= 12) ? BIN2BCD(mon) : 0xff;
 
        mday = t->time.tm_mday;
        mday = (mday >= 1 && mday <= 31) ? BIN2BCD(mday) : 0xff;
@@ -199,6 +213,7 @@ static int cmos_set_alarm(struct device *dev, struct rtc_wkalrm *t)
        sec = t->time.tm_sec;
        sec = (sec < 60) ? BIN2BCD(sec) : 0xff;
 
+       hpet_set_alarm_time(t->time.tm_hour, t->time.tm_min, t->time.tm_sec);
        spin_lock_irq(&rtc_lock);
 
        /* next rtc irq must not be from previous alarm setting */
@@ -252,7 +267,8 @@ static int cmos_irq_set_freq(struct device *dev, int freq)
        f = 16 - f;
 
        spin_lock_irqsave(&rtc_lock, flags);
-       CMOS_WRITE(RTC_REF_CLCK_32KHZ | f, RTC_FREQ_SELECT);
+       if (!hpet_set_periodic_freq(freq))
+               CMOS_WRITE(RTC_REF_CLCK_32KHZ | f, RTC_FREQ_SELECT);
        spin_unlock_irqrestore(&rtc_lock, flags);
 
        return 0;
@@ -314,28 +330,37 @@ cmos_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
        switch (cmd) {
        case RTC_AIE_OFF:       /* alarm off */
                rtc_control &= ~RTC_AIE;
+               hpet_mask_rtc_irq_bit(RTC_AIE);
                break;
        case RTC_AIE_ON:        /* alarm on */
                rtc_control |= RTC_AIE;
+               hpet_set_rtc_irq_bit(RTC_AIE);
                break;
        case RTC_UIE_OFF:       /* update off */
                rtc_control &= ~RTC_UIE;
+               hpet_mask_rtc_irq_bit(RTC_UIE);
                break;
        case RTC_UIE_ON:        /* update on */
                rtc_control |= RTC_UIE;
+               hpet_set_rtc_irq_bit(RTC_UIE);
                break;
        case RTC_PIE_OFF:       /* periodic off */
                rtc_control &= ~RTC_PIE;
+               hpet_mask_rtc_irq_bit(RTC_PIE);
                break;
        case RTC_PIE_ON:        /* periodic on */
                rtc_control |= RTC_PIE;
+               hpet_set_rtc_irq_bit(RTC_PIE);
                break;
        }
-       CMOS_WRITE(rtc_control, RTC_CONTROL);
+       if (!is_hpet_enabled())
+               CMOS_WRITE(rtc_control, RTC_CONTROL);
+
        rtc_intr = CMOS_READ(RTC_INTR_FLAGS);
        rtc_intr &= (rtc_control & RTC_IRQMASK) | RTC_IRQF;
        if (is_intr(rtc_intr))
                rtc_update_irq(cmos->rtc, 1, rtc_intr);
+
        spin_unlock_irqrestore(&rtc_lock, flags);
        return 0;
 }
@@ -362,6 +387,7 @@ static int cmos_procfs(struct device *dev, struct seq_file *seq)
        return seq_printf(seq,
                        "periodic_IRQ\t: %s\n"
                        "update_IRQ\t: %s\n"
+                       "HPET_emulated\t: %s\n"
                        // "square_wave\t: %s\n"
                        // "BCD\t\t: %s\n"
                        "DST_enable\t: %s\n"
@@ -369,6 +395,7 @@ static int cmos_procfs(struct device *dev, struct seq_file *seq)
                        "batt_status\t: %s\n",
                        (rtc_control & RTC_PIE) ? "yes" : "no",
                        (rtc_control & RTC_UIE) ? "yes" : "no",
+                       is_hpet_enabled() ? "yes" : "no",
                        // (rtc_control & RTC_SQWE) ? "yes" : "no",
                        // (rtc_control & RTC_DM_BINARY) ? "no" : "yes",
                        (rtc_control & RTC_DST_EN) ? "yes" : "no",
@@ -472,10 +499,32 @@ static struct cmos_rtc    cmos_rtc;
 static irqreturn_t cmos_interrupt(int irq, void *p)
 {
        u8              irqstat;
+       u8              rtc_control;
 
        spin_lock(&rtc_lock);
-       irqstat = CMOS_READ(RTC_INTR_FLAGS);
-       irqstat &= (CMOS_READ(RTC_CONTROL) & RTC_IRQMASK) | RTC_IRQF;
+       /*
+        * In this case it is HPET RTC interrupt handler
+        * calling us, with the interrupt information
+        * passed as arg1, instead of irq.
+        */
+       if (is_hpet_enabled())
+               irqstat = (unsigned long)irq & 0xF0;
+       else {
+               irqstat = CMOS_READ(RTC_INTR_FLAGS);
+               rtc_control = CMOS_READ(RTC_CONTROL);
+               irqstat &= (rtc_control & RTC_IRQMASK) | RTC_IRQF;
+       }
+
+       /* All Linux RTC alarms should be treated as if they were oneshot.
+        * Similar code may be needed in system wakeup paths, in case the
+        * alarm woke the system.
+        */
+       if (irqstat & RTC_AIE) {
+               rtc_control = CMOS_READ(RTC_CONTROL);
+               rtc_control &= ~RTC_AIE;
+               CMOS_WRITE(rtc_control, RTC_CONTROL);
+               CMOS_READ(RTC_INTR_FLAGS);
+       }
        spin_unlock(&rtc_lock);
 
        if (is_intr(irqstat)) {
@@ -579,8 +628,9 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq)
         * doesn't use 32KHz here ... for portability we might need to
         * do something about other clock frequencies.
         */
-       CMOS_WRITE(RTC_REF_CLCK_32KHZ | 0x06, RTC_FREQ_SELECT);
        cmos_rtc.rtc->irq_freq = 1024;
+       if (!hpet_set_periodic_freq(cmos_rtc.rtc->irq_freq))
+               CMOS_WRITE(RTC_REF_CLCK_32KHZ | 0x06, RTC_FREQ_SELECT);
 
        /* disable irqs.
         *
@@ -603,14 +653,31 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq)
                goto cleanup1;
        }
 
-       if (is_valid_irq(rtc_irq))
-               retval = request_irq(rtc_irq, cmos_interrupt, IRQF_DISABLED,
-                               cmos_rtc.rtc->dev.bus_id,
+       if (is_valid_irq(rtc_irq)) {
+               irq_handler_t rtc_cmos_int_handler;
+
+               if (is_hpet_enabled()) {
+                       int err;
+
+                       rtc_cmos_int_handler = hpet_rtc_interrupt;
+                       err = hpet_register_irq_handler(cmos_interrupt);
+                       if (err != 0) {
+                               printk(KERN_WARNING "hpet_register_irq_handler "
+                                               " failed in rtc_init().");
+                               goto cleanup1;
+                       }
+               } else
+                       rtc_cmos_int_handler = cmos_interrupt;
+
+               retval = request_irq(rtc_irq, rtc_cmos_int_handler,
+                               IRQF_DISABLED, cmos_rtc.rtc->dev.bus_id,
                                cmos_rtc.rtc);
-       if (retval < 0) {
-               dev_dbg(dev, "IRQ %d is already in use\n", rtc_irq);
-               goto cleanup1;
+               if (retval < 0) {
+                       dev_dbg(dev, "IRQ %d is already in use\n", rtc_irq);
+                       goto cleanup1;
+               }
        }
+       hpet_rtc_timer_init();
 
        /* export at least the first block of NVRAM */
        nvram.size = address_space - NVRAM_OFFSET;
@@ -665,8 +732,10 @@ static void __exit cmos_do_remove(struct device *dev)
 
        sysfs_remove_bin_file(&dev->kobj, &nvram);
 
-       if (is_valid_irq(cmos->irq))
+       if (is_valid_irq(cmos->irq)) {
                free_irq(cmos->irq, cmos->rtc);
+               hpet_unregister_irq_handler(cmos_interrupt);
+       }
 
        rtc_device_unregister(cmos->rtc);
        cmos->rtc = NULL;
@@ -785,11 +854,12 @@ cmos_pnp_probe(struct pnp_dev *pnp, const struct pnp_device_id *id)
                 * don't define the IRQ. It should always be safe to
                 * hardcode it in these cases
                 */
-               return cmos_do_probe(&pnp->dev, &pnp->res.port_resource[0], 8);
+               return cmos_do_probe(&pnp->dev,
+                               pnp_get_resource(pnp, IORESOURCE_IO, 0), 8);
        else
                return cmos_do_probe(&pnp->dev,
-                                    &pnp->res.port_resource[0],
-                                    pnp->res.irq_resource[0].start);
+                               pnp_get_resource(pnp, IORESOURCE_IO, 0),
+                               pnp_irq(pnp, 0));
 }
 
 static void __exit cmos_pnp_remove(struct pnp_dev *pnp)
@@ -873,6 +943,9 @@ static void cmos_platform_shutdown(struct platform_device *pdev)
        cmos_do_shutdown();
 }
 
+/* work with hotplug and coldplug */
+MODULE_ALIAS("platform:rtc_cmos");
+
 static struct platform_driver cmos_platform_driver = {
        .remove         = __exit_p(cmos_platform_remove),
        .shutdown       = cmos_platform_shutdown,