aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBernhard Walle <bwalle@suse.de>2008-02-06 04:38:52 -0500
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2008-02-06 13:41:14 -0500
commit9d8af78b07976d4d84e0df491abd4e9db848d0ad (patch)
treec90b4bd57147a9607ed2e860888110aef497ded8
parent095b9d546f8fdac335989bd3d60405ff3af40ee9 (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/Kconfig2
-rw-r--r--drivers/rtc/rtc-cmos.c82
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
416config HPET_EMULATE_RTC 416config 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)
55extern irqreturn_t hpet_rtc_interrupt(int irq, void *dev_id);
56#endif
42 57
43struct cmos_rtc { 58struct 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;