diff options
Diffstat (limited to 'drivers/rtc/rtc-cmos.c')
-rw-r--r-- | drivers/rtc/rtc-cmos.c | 79 |
1 files changed, 48 insertions, 31 deletions
diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c index 7c0d60910077..6085261aa2c1 100644 --- a/drivers/rtc/rtc-cmos.c +++ b/drivers/rtc/rtc-cmos.c @@ -46,6 +46,10 @@ struct cmos_rtc { int irq; struct resource *iomem; + void (*wake_on)(struct device *); + void (*wake_off)(struct device *); + + u8 enabled_wake; u8 suspend_ctrl; /* newer hardware extends the original register set */ @@ -203,7 +207,7 @@ static int cmos_set_alarm(struct device *dev, struct rtc_wkalrm *t) 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->class_dev, 1, rtc_intr); + rtc_update_irq(cmos->rtc, 1, rtc_intr); /* update alarm */ CMOS_WRITE(hrs, RTC_HOURS_ALARM); @@ -223,7 +227,7 @@ static int cmos_set_alarm(struct device *dev, struct rtc_wkalrm *t) 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->class_dev, 1, rtc_intr); + rtc_update_irq(cmos->rtc, 1, rtc_intr); } spin_unlock_irq(&rtc_lock); @@ -304,7 +308,7 @@ cmos_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) 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->class_dev, 1, rtc_intr); + rtc_update_irq(cmos->rtc, 1, rtc_intr); spin_unlock_irqrestore(&rtc_lock, flags); return 0; } @@ -379,12 +383,12 @@ static irqreturn_t cmos_interrupt(int irq, void *p) return IRQ_NONE; } -#ifdef CONFIG_PNPACPI -#define is_pnpacpi() 1 +#ifdef CONFIG_PNP +#define is_pnp() 1 #define INITSECTION #else -#define is_pnpacpi() 0 +#define is_pnp() 0 #define INITSECTION __init #endif @@ -405,13 +409,20 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq) cmos_rtc.irq = rtc_irq; cmos_rtc.iomem = ports; - /* For ACPI systems the info comes from the FADT. On others, - * board specific setup provides it as appropriate. + /* For ACPI systems extension info comes from the FADT. On others, + * board specific setup provides it as appropriate. Systems where + * the alarm IRQ isn't automatically a wakeup IRQ (like ACPI, and + * some almost-clones) can provide hooks to make that behave. */ if (info) { cmos_rtc.day_alrm = info->rtc_day_alarm; cmos_rtc.mon_alrm = info->rtc_mon_alarm; cmos_rtc.century = info->rtc_century; + + if (info->wake_on && info->wake_off) { + cmos_rtc.wake_on = info->wake_on; + cmos_rtc.wake_off = info->wake_off; + } } cmos_rtc.rtc = rtc_device_register(driver_name, dev, @@ -427,14 +438,14 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq) * REVISIT for non-x86 systems we may need to handle io memory * resources: ioremap them, and request_mem_region(). */ - if (is_pnpacpi()) { + if (is_pnp()) { retval = request_resource(&ioport_resource, ports); if (retval < 0) { dev_dbg(dev, "i/o registers already in use\n"); goto cleanup0; } } - rename_region(ports, cmos_rtc.rtc->class_dev.class_id); + rename_region(ports, cmos_rtc.rtc->dev.bus_id); spin_lock_irq(&rtc_lock); @@ -470,8 +481,8 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq) if (is_valid_irq(rtc_irq)) retval = request_irq(rtc_irq, cmos_interrupt, IRQF_DISABLED, - cmos_rtc.rtc->class_dev.class_id, - &cmos_rtc.rtc->class_dev); + 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; @@ -483,7 +494,7 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq) */ pr_info("%s: alarms up to one %s%s\n", - cmos_rtc.rtc->class_dev.class_id, + cmos_rtc.rtc->dev.bus_id, is_valid_irq(rtc_irq) ? (cmos_rtc.mon_alrm ? "year" @@ -520,12 +531,12 @@ static void __exit cmos_do_remove(struct device *dev) cmos_do_shutdown(); - if (is_pnpacpi()) + if (is_pnp()) release_resource(cmos->iomem); rename_region(cmos->iomem, NULL); if (is_valid_irq(cmos->irq)) - free_irq(cmos->irq, &cmos_rtc.rtc->class_dev); + free_irq(cmos->irq, cmos_rtc.rtc); rtc_device_unregister(cmos_rtc.rtc); @@ -555,16 +566,20 @@ static int cmos_suspend(struct device *dev, pm_message_t mesg) irqstat = CMOS_READ(RTC_INTR_FLAGS); irqstat &= (tmp & RTC_IRQMASK) | RTC_IRQF; if (is_intr(irqstat)) - rtc_update_irq(&cmos->rtc->class_dev, 1, irqstat); + rtc_update_irq(cmos->rtc, 1, irqstat); } spin_unlock_irq(&rtc_lock); - /* ACPI HOOK: enable ACPI_EVENT_RTC when (tmp & RTC_AIE) - * ... it'd be best if we could do that under rtc_lock. - */ + if (tmp & RTC_AIE) { + cmos->enabled_wake = 1; + if (cmos->wake_on) + cmos->wake_on(dev); + else + enable_irq_wake(cmos->irq); + } pr_debug("%s: suspend%s, ctrl %02x\n", - cmos_rtc.rtc->class_dev.class_id, + cmos_rtc.rtc->dev.bus_id, (tmp & RTC_AIE) ? ", alarm may wake" : "", tmp); @@ -576,26 +591,28 @@ static int cmos_resume(struct device *dev) struct cmos_rtc *cmos = dev_get_drvdata(dev); unsigned char tmp = cmos->suspend_ctrl; - /* REVISIT: a mechanism to resync the system clock (jiffies) - * on resume should be portable between platforms ... - */ - /* re-enable any irqs previously active */ if (tmp & (RTC_PIE|RTC_AIE|RTC_UIE)) { - /* ACPI HOOK: disable ACPI_EVENT_RTC when (tmp & RTC_AIE) */ + if (cmos->enabled_wake) { + if (cmos->wake_off) + cmos->wake_off(dev); + else + disable_irq_wake(cmos->irq); + cmos->enabled_wake = 0; + } spin_lock_irq(&rtc_lock); CMOS_WRITE(tmp, RTC_CONTROL); tmp = CMOS_READ(RTC_INTR_FLAGS); tmp &= (cmos->suspend_ctrl & RTC_IRQMASK) | RTC_IRQF; if (is_intr(tmp)) - rtc_update_irq(&cmos->rtc->class_dev, 1, tmp); + rtc_update_irq(cmos->rtc, 1, tmp); spin_unlock_irq(&rtc_lock); } pr_debug("%s: resume, ctrl %02x\n", - cmos_rtc.rtc->class_dev.class_id, + cmos_rtc.rtc->dev.bus_id, cmos->suspend_ctrl); @@ -613,7 +630,7 @@ static int cmos_resume(struct device *dev) * the device node will always be created as a PNPACPI device. */ -#ifdef CONFIG_PNPACPI +#ifdef CONFIG_PNP #include <linux/pnp.h> @@ -684,11 +701,11 @@ static void __exit cmos_exit(void) } module_exit(cmos_exit); -#else /* no PNPACPI */ +#else /* no PNP */ /*----------------------------------------------------------------*/ -/* Platform setup should have set up an RTC device, when PNPACPI is +/* Platform setup should have set up an RTC device, when PNP is * unavailable ... this could happen even on (older) PCs. */ @@ -734,7 +751,7 @@ static void __exit cmos_exit(void) module_exit(cmos_exit); -#endif /* !PNPACPI */ +#endif /* !PNP */ MODULE_AUTHOR("David Brownell"); MODULE_DESCRIPTION("Driver for PC-style 'CMOS' RTCs"); |