X-Git-Url: http://git.samba.org/samba.git/?a=blobdiff_plain;f=drivers%2Frtc%2Frtc-cmos.c;h=e059f94c79eb2663632feff0e7a990d7aaa0a42c;hb=e5e54bc86a1fed9849b22fd736c30b23c4719046;hp=e3fe83a23cf720049018dadb623de3c8b28d78d1;hpb=347c53dca73fca317d57781f510f5ff4f6c0d0d7;p=sfrench%2Fcifs-2.6.git diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c index e3fe83a23cf7..e059f94c79eb 100644 --- a/drivers/rtc/rtc-cmos.c +++ b/drivers/rtc/rtc-cmos.c @@ -36,9 +36,24 @@ #include #include +#ifdef CONFIG_HPET_EMULATE_RTC +#include +#endif + /* this is for "generic access to PC-style RTC" using CMOS_READ/CMOS_WRITE */ #include +#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; @@ -120,7 +135,8 @@ static int cmos_read_alarm(struct device *dev, struct rtc_wkalrm *t) t->time.tm_hour = CMOS_READ(RTC_HOURS_ALARM); if (cmos->day_alrm) { - t->time.tm_mday = CMOS_READ(cmos->day_alrm); + /* ignore upper bits on readback per ACPI spec */ + t->time.tm_mday = CMOS_READ(cmos->day_alrm) & 0x3f; if (!t->time.tm_mday) t->time.tm_mday = -1; @@ -198,6 +214,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 */ @@ -251,7 +268,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; @@ -313,28 +331,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; } @@ -392,15 +419,111 @@ static const struct rtc_class_ops cmos_rtc_ops = { /*----------------------------------------------------------------*/ +/* + * All these chips have at least 64 bytes of address space, shared by + * RTC registers and NVRAM. Most of those bytes of NVRAM are used + * by boot firmware. Modern chips have 128 or 256 bytes. + */ + +#define NVRAM_OFFSET (RTC_REG_D + 1) + +static ssize_t +cmos_nvram_read(struct kobject *kobj, struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + int retval; + + if (unlikely(off >= attr->size)) + return 0; + if ((off + count) > attr->size) + count = attr->size - off; + + spin_lock_irq(&rtc_lock); + for (retval = 0, off += NVRAM_OFFSET; count--; retval++, off++) + *buf++ = CMOS_READ(off); + spin_unlock_irq(&rtc_lock); + + return retval; +} + +static ssize_t +cmos_nvram_write(struct kobject *kobj, struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct cmos_rtc *cmos; + int retval; + + cmos = dev_get_drvdata(container_of(kobj, struct device, kobj)); + if (unlikely(off >= attr->size)) + return -EFBIG; + if ((off + count) > attr->size) + count = attr->size - off; + + /* NOTE: on at least PCs and Ataris, the boot firmware uses a + * checksum on part of the NVRAM data. That's currently ignored + * here. If userspace is smart enough to know what fields of + * NVRAM to update, updating checksums is also part of its job. + */ + spin_lock_irq(&rtc_lock); + for (retval = 0, off += NVRAM_OFFSET; count--; retval++, off++) { + /* don't trash RTC registers */ + if (off == cmos->day_alrm + || off == cmos->mon_alrm + || off == cmos->century) + buf++; + else + CMOS_WRITE(*buf++, off); + } + spin_unlock_irq(&rtc_lock); + + return retval; +} + +static struct bin_attribute nvram = { + .attr = { + .name = "nvram", + .mode = S_IRUGO | S_IWUSR, + .owner = THIS_MODULE, + }, + + .read = cmos_nvram_read, + .write = cmos_nvram_write, + /* size gets set up later */ +}; + +/*----------------------------------------------------------------*/ + 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)) { @@ -411,11 +534,9 @@ static irqreturn_t cmos_interrupt(int irq, void *p) } #ifdef CONFIG_PNP -#define is_pnp() 1 #define INITSECTION #else -#define is_pnp() 0 #define INITSECTION __init #endif @@ -425,6 +546,7 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq) struct cmos_rtc_board_info *info = dev->platform_data; int retval = 0; unsigned char rtc_control; + unsigned address_space; /* there can be only one ... */ if (cmos_rtc.dev) @@ -449,15 +571,36 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq) cmos_rtc.irq = rtc_irq; cmos_rtc.iomem = ports; + /* Heuristic to deduce NVRAM size ... do what the legacy NVRAM + * driver did, but don't reject unknown configs. Old hardware + * won't address 128 bytes, and for now we ignore the way newer + * chips can address 256 bytes (using two more i/o ports). + */ +#if defined(CONFIG_ATARI) + address_space = 64; +#elif defined(__i386__) || defined(__x86_64__) || defined(__arm__) + address_space = 128; +#else +#warning Assuming 128 bytes of RTC+NVRAM address space, not 64 bytes. + address_space = 128; +#endif + /* 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. + * + * Note that ACPI doesn't preclude putting these registers into + * "extended" areas of the chip, including some that we won't yet + * expect CMOS_READ and friends to handle. */ 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->rtc_day_alarm && info->rtc_day_alarm < 128) + cmos_rtc.day_alrm = info->rtc_day_alarm; + if (info->rtc_mon_alarm && info->rtc_mon_alarm < 128) + cmos_rtc.mon_alrm = info->rtc_mon_alarm; + if (info->rtc_century && info->rtc_century < 128) + cmos_rtc.century = info->rtc_century; if (info->wake_on && info->wake_off) { cmos_rtc.wake_on = info->wake_on; @@ -484,8 +627,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. * @@ -508,19 +652,39 @@ 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(); - /* REVISIT optionally make 50 or 114 bytes NVRAM available, - * like rtc-ds1553, rtc-ds1742 ... this will often include - * registers for century, and day/month alarm. - */ + /* export at least the first block of NVRAM */ + nvram.size = address_space - NVRAM_OFFSET; + retval = sysfs_create_bin_file(&dev->kobj, &nvram); + if (retval < 0) { + dev_dbg(dev, "can't create nvram file? %d\n", retval); + goto cleanup2; + } pr_info("%s: alarms up to one %s%s\n", cmos_rtc.rtc->dev.bus_id, @@ -535,6 +699,9 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq) return 0; +cleanup2: + if (is_valid_irq(rtc_irq)) + free_irq(rtc_irq, cmos_rtc.rtc); cleanup1: cmos_rtc.dev = NULL; rtc_device_unregister(cmos_rtc.rtc); @@ -562,8 +729,12 @@ static void __exit cmos_do_remove(struct device *dev) cmos_do_shutdown(); - if (is_valid_irq(cmos->irq)) + sysfs_remove_bin_file(&dev->kobj, &nvram); + + 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; @@ -658,9 +829,12 @@ static int cmos_resume(struct device *dev) /*----------------------------------------------------------------*/ -/* The "CMOS" RTC normally lives on the platform_bus. On ACPI systems, - * the device node will always be created as a PNPACPI device. Plus - * pre-ACPI PCs probably list it in the PNPBIOS tables. +/* On non-x86 systems, a "CMOS" RTC lives most naturally on platform_bus. + * ACPI systems always list these as PNPACPI devices, and pre-ACPI PCs + * probably list them in similar PNPBIOS tables; so PNP is more common. + * + * We don't use legacy "poke at the hardware" probing. Ancient PCs that + * predate even PNPBIOS should set up platform_bus devices. */ #ifdef CONFIG_PNP