diff options
author | Bernhard Walle <bwalle@suse.de> | 2008-02-06 04:38:52 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2008-02-06 13:41:14 -0500 |
commit | 9d8af78b07976d4d84e0df491abd4e9db848d0ad (patch) | |
tree | c90b4bd57147a9607ed2e860888110aef497ded8 | |
parent | 095b9d546f8fdac335989bd3d60405ff3af40ee9 (diff) |
rtc: 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.
[akpm@linux-foundation.org: unbreak alpha build]
Signed-off-by: Bernhard Walle <bwalle@suse.de>
Cc: Alessandro Zummo <a.zummo@towertech.it>
Cc: David Brownell <david-b@pacbell.net>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Andi Kleen <ak@suse.de>
Cc: john stultz <johnstul@us.ibm.com>
Cc: Robert Picco <Robert.Picco@hp.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | arch/x86/Kconfig | 2 | ||||
-rw-r--r-- | drivers/rtc/rtc-cmos.c | 82 |
2 files changed, 70 insertions, 14 deletions
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 434821187cfc..e6728bd61cc1 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig | |||
@@ -415,7 +415,7 @@ config HPET_TIMER | |||
415 | 415 | ||
416 | config HPET_EMULATE_RTC | 416 | config HPET_EMULATE_RTC |
417 | def_bool y | 417 | def_bool y |
418 | depends on HPET_TIMER && (RTC=y || RTC=m) | 418 | depends on HPET_TIMER && (RTC=y || RTC=m || RTC_DRV_CMOS=m || RTC_DRV_CMOS=y) |
419 | 419 | ||
420 | # Mark as embedded because too many people got it wrong. | 420 | # Mark as embedded because too many people got it wrong. |
421 | # The code disables itself when not needed. | 421 | # The code disables itself when not needed. |
diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c index ff7539a4dbea..e059f94c79eb 100644 --- a/drivers/rtc/rtc-cmos.c +++ b/drivers/rtc/rtc-cmos.c | |||
@@ -36,9 +36,24 @@ | |||
36 | #include <linux/platform_device.h> | 36 | #include <linux/platform_device.h> |
37 | #include <linux/mod_devicetable.h> | 37 | #include <linux/mod_devicetable.h> |
38 | 38 | ||
39 | #ifdef CONFIG_HPET_EMULATE_RTC | ||
40 | #include <asm/hpet.h> | ||
41 | #endif | ||
42 | |||
39 | /* this is for "generic access to PC-style RTC" using CMOS_READ/CMOS_WRITE */ | 43 | /* this is for "generic access to PC-style RTC" using CMOS_READ/CMOS_WRITE */ |
40 | #include <asm-generic/rtc.h> | 44 | #include <asm-generic/rtc.h> |
41 | 45 | ||
46 | #ifndef CONFIG_HPET_EMULATE_RTC | ||
47 | #define is_hpet_enabled() 0 | ||
48 | #define hpet_set_alarm_time(hrs, min, sec) do { } while (0) | ||
49 | #define hpet_set_periodic_freq(arg) 0 | ||
50 | #define hpet_mask_rtc_irq_bit(arg) do { } while (0) | ||
51 | #define hpet_set_rtc_irq_bit(arg) do { } while (0) | ||
52 | #define hpet_rtc_timer_init() do { } while (0) | ||
53 | #define hpet_register_irq_handler(h) 0 | ||
54 | #define hpet_unregister_irq_handler(h) do { } while (0) | ||
55 | extern irqreturn_t hpet_rtc_interrupt(int irq, void *dev_id); | ||
56 | #endif | ||
42 | 57 | ||
43 | struct cmos_rtc { | 58 | struct cmos_rtc { |
44 | struct rtc_device *rtc; | 59 | struct rtc_device *rtc; |
@@ -199,6 +214,7 @@ static int cmos_set_alarm(struct device *dev, struct rtc_wkalrm *t) | |||
199 | sec = t->time.tm_sec; | 214 | sec = t->time.tm_sec; |
200 | sec = (sec < 60) ? BIN2BCD(sec) : 0xff; | 215 | sec = (sec < 60) ? BIN2BCD(sec) : 0xff; |
201 | 216 | ||
217 | hpet_set_alarm_time(t->time.tm_hour, t->time.tm_min, t->time.tm_sec); | ||
202 | spin_lock_irq(&rtc_lock); | 218 | spin_lock_irq(&rtc_lock); |
203 | 219 | ||
204 | /* next rtc irq must not be from previous alarm setting */ | 220 | /* next rtc irq must not be from previous alarm setting */ |
@@ -252,7 +268,8 @@ static int cmos_irq_set_freq(struct device *dev, int freq) | |||
252 | f = 16 - f; | 268 | f = 16 - f; |
253 | 269 | ||
254 | spin_lock_irqsave(&rtc_lock, flags); | 270 | spin_lock_irqsave(&rtc_lock, flags); |
255 | CMOS_WRITE(RTC_REF_CLCK_32KHZ | f, RTC_FREQ_SELECT); | 271 | if (!hpet_set_periodic_freq(freq)) |
272 | CMOS_WRITE(RTC_REF_CLCK_32KHZ | f, RTC_FREQ_SELECT); | ||
256 | spin_unlock_irqrestore(&rtc_lock, flags); | 273 | spin_unlock_irqrestore(&rtc_lock, flags); |
257 | 274 | ||
258 | return 0; | 275 | return 0; |
@@ -314,28 +331,37 @@ cmos_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) | |||
314 | switch (cmd) { | 331 | switch (cmd) { |
315 | case RTC_AIE_OFF: /* alarm off */ | 332 | case RTC_AIE_OFF: /* alarm off */ |
316 | rtc_control &= ~RTC_AIE; | 333 | rtc_control &= ~RTC_AIE; |
334 | hpet_mask_rtc_irq_bit(RTC_AIE); | ||
317 | break; | 335 | break; |
318 | case RTC_AIE_ON: /* alarm on */ | 336 | case RTC_AIE_ON: /* alarm on */ |
319 | rtc_control |= RTC_AIE; | 337 | rtc_control |= RTC_AIE; |
338 | hpet_set_rtc_irq_bit(RTC_AIE); | ||
320 | break; | 339 | break; |
321 | case RTC_UIE_OFF: /* update off */ | 340 | case RTC_UIE_OFF: /* update off */ |
322 | rtc_control &= ~RTC_UIE; | 341 | rtc_control &= ~RTC_UIE; |
342 | hpet_mask_rtc_irq_bit(RTC_UIE); | ||
323 | break; | 343 | break; |
324 | case RTC_UIE_ON: /* update on */ | 344 | case RTC_UIE_ON: /* update on */ |
325 | rtc_control |= RTC_UIE; | 345 | rtc_control |= RTC_UIE; |
346 | hpet_set_rtc_irq_bit(RTC_UIE); | ||
326 | break; | 347 | break; |
327 | case RTC_PIE_OFF: /* periodic off */ | 348 | case RTC_PIE_OFF: /* periodic off */ |
328 | rtc_control &= ~RTC_PIE; | 349 | rtc_control &= ~RTC_PIE; |
350 | hpet_mask_rtc_irq_bit(RTC_PIE); | ||
329 | break; | 351 | break; |
330 | case RTC_PIE_ON: /* periodic on */ | 352 | case RTC_PIE_ON: /* periodic on */ |
331 | rtc_control |= RTC_PIE; | 353 | rtc_control |= RTC_PIE; |
354 | hpet_set_rtc_irq_bit(RTC_PIE); | ||
332 | break; | 355 | break; |
333 | } | 356 | } |
334 | CMOS_WRITE(rtc_control, RTC_CONTROL); | 357 | if (!is_hpet_enabled()) |
358 | CMOS_WRITE(rtc_control, RTC_CONTROL); | ||
359 | |||
335 | rtc_intr = CMOS_READ(RTC_INTR_FLAGS); | 360 | rtc_intr = CMOS_READ(RTC_INTR_FLAGS); |
336 | rtc_intr &= (rtc_control & RTC_IRQMASK) | RTC_IRQF; | 361 | rtc_intr &= (rtc_control & RTC_IRQMASK) | RTC_IRQF; |
337 | if (is_intr(rtc_intr)) | 362 | if (is_intr(rtc_intr)) |
338 | rtc_update_irq(cmos->rtc, 1, rtc_intr); | 363 | rtc_update_irq(cmos->rtc, 1, rtc_intr); |
364 | |||
339 | spin_unlock_irqrestore(&rtc_lock, flags); | 365 | spin_unlock_irqrestore(&rtc_lock, flags); |
340 | return 0; | 366 | return 0; |
341 | } | 367 | } |
@@ -475,15 +501,25 @@ static irqreturn_t cmos_interrupt(int irq, void *p) | |||
475 | u8 rtc_control; | 501 | u8 rtc_control; |
476 | 502 | ||
477 | spin_lock(&rtc_lock); | 503 | spin_lock(&rtc_lock); |
478 | irqstat = CMOS_READ(RTC_INTR_FLAGS); | 504 | /* |
479 | rtc_control = CMOS_READ(RTC_CONTROL); | 505 | * In this case it is HPET RTC interrupt handler |
480 | irqstat &= (rtc_control & RTC_IRQMASK) | RTC_IRQF; | 506 | * calling us, with the interrupt information |
507 | * passed as arg1, instead of irq. | ||
508 | */ | ||
509 | if (is_hpet_enabled()) | ||
510 | irqstat = (unsigned long)irq & 0xF0; | ||
511 | else { | ||
512 | irqstat = CMOS_READ(RTC_INTR_FLAGS); | ||
513 | rtc_control = CMOS_READ(RTC_CONTROL); | ||
514 | irqstat &= (rtc_control & RTC_IRQMASK) | RTC_IRQF; | ||
515 | } | ||
481 | 516 | ||
482 | /* All Linux RTC alarms should be treated as if they were oneshot. | 517 | /* All Linux RTC alarms should be treated as if they were oneshot. |
483 | * Similar code may be needed in system wakeup paths, in case the | 518 | * Similar code may be needed in system wakeup paths, in case the |
484 | * alarm woke the system. | 519 | * alarm woke the system. |
485 | */ | 520 | */ |
486 | if (irqstat & RTC_AIE) { | 521 | if (irqstat & RTC_AIE) { |
522 | rtc_control = CMOS_READ(RTC_CONTROL); | ||
487 | rtc_control &= ~RTC_AIE; | 523 | rtc_control &= ~RTC_AIE; |
488 | CMOS_WRITE(rtc_control, RTC_CONTROL); | 524 | CMOS_WRITE(rtc_control, RTC_CONTROL); |
489 | CMOS_READ(RTC_INTR_FLAGS); | 525 | CMOS_READ(RTC_INTR_FLAGS); |
@@ -591,8 +627,9 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq) | |||
591 | * doesn't use 32KHz here ... for portability we might need to | 627 | * doesn't use 32KHz here ... for portability we might need to |
592 | * do something about other clock frequencies. | 628 | * do something about other clock frequencies. |
593 | */ | 629 | */ |
594 | CMOS_WRITE(RTC_REF_CLCK_32KHZ | 0x06, RTC_FREQ_SELECT); | ||
595 | cmos_rtc.rtc->irq_freq = 1024; | 630 | cmos_rtc.rtc->irq_freq = 1024; |
631 | if (!hpet_set_periodic_freq(cmos_rtc.rtc->irq_freq)) | ||
632 | CMOS_WRITE(RTC_REF_CLCK_32KHZ | 0x06, RTC_FREQ_SELECT); | ||
596 | 633 | ||
597 | /* disable irqs. | 634 | /* disable irqs. |
598 | * | 635 | * |
@@ -615,14 +652,31 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq) | |||
615 | goto cleanup1; | 652 | goto cleanup1; |
616 | } | 653 | } |
617 | 654 | ||
618 | if (is_valid_irq(rtc_irq)) | 655 | if (is_valid_irq(rtc_irq)) { |
619 | retval = request_irq(rtc_irq, cmos_interrupt, IRQF_DISABLED, | 656 | irq_handler_t rtc_cmos_int_handler; |
620 | cmos_rtc.rtc->dev.bus_id, | 657 | |
658 | if (is_hpet_enabled()) { | ||
659 | int err; | ||
660 | |||
661 | rtc_cmos_int_handler = hpet_rtc_interrupt; | ||
662 | err = hpet_register_irq_handler(cmos_interrupt); | ||
663 | if (err != 0) { | ||
664 | printk(KERN_WARNING "hpet_register_irq_handler " | ||
665 | " failed in rtc_init()."); | ||
666 | goto cleanup1; | ||
667 | } | ||
668 | } else | ||
669 | rtc_cmos_int_handler = cmos_interrupt; | ||
670 | |||
671 | retval = request_irq(rtc_irq, rtc_cmos_int_handler, | ||
672 | IRQF_DISABLED, cmos_rtc.rtc->dev.bus_id, | ||
621 | cmos_rtc.rtc); | 673 | cmos_rtc.rtc); |
622 | if (retval < 0) { | 674 | if (retval < 0) { |
623 | dev_dbg(dev, "IRQ %d is already in use\n", rtc_irq); | 675 | dev_dbg(dev, "IRQ %d is already in use\n", rtc_irq); |
624 | goto cleanup1; | 676 | goto cleanup1; |
677 | } | ||
625 | } | 678 | } |
679 | hpet_rtc_timer_init(); | ||
626 | 680 | ||
627 | /* export at least the first block of NVRAM */ | 681 | /* export at least the first block of NVRAM */ |
628 | nvram.size = address_space - NVRAM_OFFSET; | 682 | nvram.size = address_space - NVRAM_OFFSET; |
@@ -677,8 +731,10 @@ static void __exit cmos_do_remove(struct device *dev) | |||
677 | 731 | ||
678 | sysfs_remove_bin_file(&dev->kobj, &nvram); | 732 | sysfs_remove_bin_file(&dev->kobj, &nvram); |
679 | 733 | ||
680 | if (is_valid_irq(cmos->irq)) | 734 | if (is_valid_irq(cmos->irq)) { |
681 | free_irq(cmos->irq, cmos->rtc); | 735 | free_irq(cmos->irq, cmos->rtc); |
736 | hpet_unregister_irq_handler(cmos_interrupt); | ||
737 | } | ||
682 | 738 | ||
683 | rtc_device_unregister(cmos->rtc); | 739 | rtc_device_unregister(cmos->rtc); |
684 | cmos->rtc = NULL; | 740 | cmos->rtc = NULL; |