HPET rtc emulation
Hi. I am working with a platform that has a problem with the RTC alarm interrupt - tested with rtctest from /Documentation/rtc.txt. So I thought that I could use the HPET emulation. As far as I understand in the HPET emulation mode no RTC irq will be used --> RTC alarm interrupt is done via HPET. Is this a correct assumption? root@OT:/home/vis# hwclock Tue 30 Apr 2013 09:24:28 AM CEST -0.511012 seconds root@OT:/home/vis# hwclock Tue 30 Apr 2013 09:24:29 AM CEST -0.511016 seconds root@OT:/home/vis# hwclock Tue 30 Apr 2013 09:24:30 AM CEST -0.526659 seconds root@OT:/home/vis# hwclock Tue 30 Apr 2013 09:24:31 AM CEST -0.620384 seconds root@OT:/home/vis# hwclock Tue 30 Apr 2013 09:24:32 AM CEST -0.604838 seconds root@OT:/home/vis# hwclock Tue 30 Apr 2013 09:24:33 AM CEST -0.651638 seconds root@OT:/home/vis# hwclock Tue 30 Apr 2013 09:24:34 AM CEST -0.682915 seconds root@OT:/home/vis# hwclock Tue 30 Apr 2013 09:24:35 AM CEST -0.026708 seconds root@OT:/home/vis# hwclock select() to /dev/rtc to wait for clock tick timed ou Doing some quite simple tests indicates that HPET emulation does not emulate RTC alarm interrupts or my HPET has some problems. thanks -- Christian Gmeiner, MSc -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[patch 3/3] Add HPET RTC emulation to RTC_DRV_CMOS
That patch adds the RTC emulation of the HPET timer to the new RTC_DRV_CMOS. The old drivers/char/rtc.ko driver had that functionality and it's important on new systems. Signed-off-by: Bernhard Walle <[EMAIL PROTECTED]> --- arch/x86/Kconfig |2 - drivers/rtc/rtc-cmos.c | 79 - 2 files changed, 67 insertions(+), 14 deletions(-) --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -405,7 +405,7 @@ config HPET_TIMER config HPET_EMULATE_RTC def_bool y - depends on HPET_TIMER && (RTC=y || RTC=m) + depends on HPET_TIMER && (RTC=y || RTC=m || RTC_DRV_CMOS=m || RTC_DRV_CMOS=y) # Mark as embedded because too many people got it wrong. # The code disables itself when not needed. --- a/drivers/rtc/rtc-cmos.c +++ b/drivers/rtc/rtc-cmos.c @@ -35,10 +35,22 @@ #include #include #include +#include /* 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; @@ -199,6 +211,7 @@ static int cmos_set_alarm(struct device 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 +265,8 @@ static int cmos_irq_set_freq(struct devi 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 +328,37 @@ cmos_rtc_ioctl(struct device *dev, unsig 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; } @@ -475,15 +498,25 @@ static irqreturn_t cmos_interrupt(int ir u8 rtc_control; spin_lock(&rtc_lock); - irqstat = CMOS_READ(RTC_INTR_FLAGS); - rtc_control = CMOS_READ(RTC_CONTROL); - irqstat &= (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); @@ -591,8 +624,9 @@ cmos_do_probe(struct device *dev, struct
[PATCH] Add HPET RTC emulation to RTC_DRV_CMOS
That patch adds the RTC emulation of the HPET timer to the new RTC_DRV_CMOS. The old drivers/char/rtc.ko driver had that functionality and it's important on new systems. Signed-off-by: Bernhard Walle <[EMAIL PROTECTED]> --- arch/x86/Kconfig |2 - drivers/rtc/rtc-cmos.c | 79 - 2 files changed, 67 insertions(+), 14 deletions(-) --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -405,7 +405,7 @@ config HPET_TIMER config HPET_EMULATE_RTC def_bool y - depends on HPET_TIMER && (RTC=y || RTC=m) + depends on HPET_TIMER && (RTC=y || RTC=m || RTC_DRV_CMOS=m || RTC_DRV_CMOS=y) # Mark as embedded because too many people got it wrong. # The code disables itself when not needed. --- a/drivers/rtc/rtc-cmos.c +++ b/drivers/rtc/rtc-cmos.c @@ -35,10 +35,22 @@ #include #include #include +#include /* 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; @@ -199,6 +211,7 @@ static int cmos_set_alarm(struct device 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 +265,8 @@ static int cmos_irq_set_freq(struct devi 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 +328,37 @@ cmos_rtc_ioctl(struct device *dev, unsig 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; } @@ -475,15 +498,25 @@ static irqreturn_t cmos_interrupt(int ir u8 rtc_control; spin_lock(&rtc_lock); - irqstat = CMOS_READ(RTC_INTR_FLAGS); - rtc_control = CMOS_READ(RTC_CONTROL); - irqstat &= (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); @@ -591,8 +624,9 @@ cmos_do_probe(struct device *dev, struct
Re: [PATCH] Add HPET RTC emulation to RTC_DRV_CMOS
* Bernhard Walle <[EMAIL PROTECTED]> [2007-12-20 16:24]: ... This was an accident. The patch belongs to a patch series that I'll post later. Please ignore! Thanks, Bernhard -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [EMAIL PROTECTED] More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/