Skip to contentSkip to conversation

Implement CONFIG_HPET_EMULATE_RTC for RTC_DRV_CMOS

PATCH New Bernhard Walle 1 participant 4 messages Dec 20, 2007

Subsystems (4)
lore
Share & tools
lore
#0 lore raw

cover Implement CONFIG_HPET_EMULATE_RTC for RTC_DRV_CMOS

The new rtc-cmos driver misses HPET support. If the hardware has HPET enabled, then interrupts don't work for the rtc-cmos driver which results in RTC_AIE*, RTC_PIE* and RTC_ALM being unusable. This affects hwclock from util-linux-ng at least on i386 since that uses RTC_PIE_ON. (For x86-64, a polling method is used for unknown reasons.)

This patch series now

1. export the functions from arch/x86/kernel/hpet.c that the old char/rtc driver uses to work around that problem,

2. makes it possible to compile the old rtc driver as module, while still having CONFIG_HPET_EMULATE_RTC enabled and

3. makes use of the exported functions in (1) in the new rtc-cmos driver.

The design is not changed. Please review and give me feedback!

This patch series is against 2.6.24-rc5-mm1. It passes the test in Documentation/rtc.txt after http://lkml.org/lkml/2007/12/20/249 is applied.

#1 lore raw

PATCH 1/3 Make CONFIG_HPET_EMULATE_RTC usable from modules

This patch makes the RTC emulation functions in arch/x86/kernel/hpet.c usable for kernel modules. It

- exports the functions (EXPORT_SYMBOL_GPL()),
- adds an interface to register the interrupt callback function instead of using only a fixed callback function and
- replaces the rtc_get_rtc_time() function which depends on CONFIG_RTC with a call to get_rtc_time() which is defined in include/asm-generic/rtc.h.

The only dependency to CONFIG_RTC is the call to rtc_interrupt() which is removed by the next patch. After this, there's no (code) dependency of this functions to CONFIG_RTC=y any more.

---
 arch/x86/kernel/hpet.c |   47 ++++++++++++++++++++++++++++++++++++++++++++++-
 include/asm-x86/hpet.h |    3 +++
 2 files changed, 49 insertions(+), 1 deletion(-)
arch/x86/kernel/hpet.c +46 -1
@@ -107,6 +107,7 @@ int is_hpet_enabled(void)
 {
 	return is_hpet_capable() && hpet_legacy_int_enabled;
 }
+EXPORT_SYMBOL_GPL(is_hpet_enabled);
 
 /*
  * When the hpet driver (/dev/hpet) is enabled, we need to reserve
@@ -478,6 +479,7 @@ void hpet_disable(void)
  */
 #include <linux/mc146818rtc.h>
 #include <linux/rtc.h>
+#include <asm/rtc.h>
 
 #define DEFAULT_RTC_INT_FREQ	64
 #define DEFAULT_RTC_SHIFT	6
@@ -492,6 +494,38 @@ static unsigned long hpet_default_delta;
 static unsigned long hpet_pie_delta;
 static unsigned long hpet_pie_limit;
 
+static rtc_irq_handler irq_handler;
+
+/*
+ * Registers a IRQ handler.
+ */
+int hpet_register_irq_handler(rtc_irq_handler handler)
+{
+	if (!is_hpet_enabled())
+		return -ENODEV;
+	if (irq_handler)
+		return -EBUSY;
+
+	irq_handler = handler;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(hpet_register_irq_handler);
+
+/*
+ * Deregisters the IRQ handler registered with hpet_register_irq_handler()
+ * and does cleanup.
+ */
+void hpet_unregister_irq_handler(rtc_irq_handler handler)
+{
+	if (!is_hpet_enabled())
+		return;
+
+	irq_handler = NULL;
+	hpet_rtc_flags = 0;
+}
+EXPORT_SYMBOL_GPL(hpet_unregister_irq_handler);
+
 /*
  * Timer 1 for RTC emulation. We use one shot mode, as periodic mode
  * is not supported by all HPET implementations for timer 1.
@@ -533,6 +567,7 @@ int hpet_rtc_timer_init(void)
 
 	return 1;
 }
+EXPORT_SYMBOL_GPL(hpet_rtc_timer_init);
 
 /*
  * The functions below are called from rtc driver.
@@ -547,6 +582,7 @@ int hpet_mask_rtc_irq_bit(unsigned long 
 	hpet_rtc_flags &= ~bit_mask;
 	return 1;
 }
+EXPORT_SYMBOL_GPL(hpet_mask_rtc_irq_bit);
 
 int hpet_set_rtc_irq_bit(unsigned long bit_mask)
 {
@@ -562,6 +598,7 @@ int hpet_set_rtc_irq_bit(unsigned long b
 
 	return 1;
 }
+EXPORT_SYMBOL_GPL(hpet_set_rtc_irq_bit);
 
 int hpet_set_alarm_time(unsigned char hrs, unsigned char min,
 			unsigned char sec)
@@ -575,6 +612,7 @@ int hpet_set_alarm_time(unsigned char hr
 
 	return 1;
 }
+EXPORT_SYMBOL_GPL(hpet_set_alarm_time);
 
 int hpet_set_periodic_freq(unsigned long freq)
 {
@@ -593,11 +631,13 @@ int hpet_set_periodic_freq(unsigned long
 	}
 	return 1;
 }
+EXPORT_SYMBOL_GPL(hpet_set_periodic_freq);
 
 int hpet_rtc_dropped_irq(void)
 {
 	return is_hpet_enabled();
 }
+EXPORT_SYMBOL_GPL(hpet_rtc_dropped_irq);
 
 static void hpet_rtc_timer_reinit(void)
 {
@@ -641,9 +681,10 @@ irqreturn_t hpet_rtc_interrupt(int irq, 
 	unsigned long rtc_int_flag = 0;
 
 	hpet_rtc_timer_reinit();
+	memset(&curr_time, 0, sizeof(struct rtc_time));
 
 	if (hpet_rtc_flags & (RTC_UIE | RTC_AIE))
-		rtc_get_rtc_time(&curr_time);
+		get_rtc_time(&curr_time);
 
 	if (hpet_rtc_flags & RTC_UIE &&
 	    curr_time.tm_sec != hpet_prev_update_sec) {
@@ -665,8 +706,12 @@ irqreturn_t hpet_rtc_interrupt(int irq, 
 
 	if (rtc_int_flag) {
 		rtc_int_flag |= (RTC_IRQF | (RTC_NUM_INTS << 8));
+		if (irq_handler)
+			irq_handler(rtc_int_flag, dev_id);
+
 		rtc_interrupt(rtc_int_flag, dev_id);
 	}
 	return IRQ_HANDLED;
 }
+EXPORT_SYMBOL_GPL(hpet_rtc_interrupt);
 #endif
include/asm-x86/hpet.h +3 -0
@@ -69,6 +69,7 @@ extern void force_hpet_resume(void);
 
 #include <linux/interrupt.h>
 
+typedef irqreturn_t (*rtc_irq_handler)(int interrupt, void *cookie);
 extern int hpet_mask_rtc_irq_bit(unsigned long bit_mask);
 extern int hpet_set_rtc_irq_bit(unsigned long bit_mask);
 extern int hpet_set_alarm_time(unsigned char hrs, unsigned char min,
@@ -77,6 +78,8 @@ extern int hpet_set_periodic_freq(unsign
 extern int hpet_rtc_dropped_irq(void);
 extern int hpet_rtc_timer_init(void);
 extern irqreturn_t hpet_rtc_interrupt(int irq, void *dev_id);
+extern int hpet_register_irq_handler(rtc_irq_handler handler);
+extern void hpet_unregister_irq_handler(rtc_irq_handler handler);
 
 #endif /* CONFIG_HPET_EMULATE_RTC */
 
#2 lore raw

PATCH 2/3 Use the IRQ callback interface in (old) RTC driver

In reply to Bernhard Walle

That function uses the new registration callback mechanism which was added in the previous patch in the old RTC driver. It also removes the direct rtc_interrupt() call from arch/x86/kernel/hpetc.c so that there's finally no (code) dependency to CONFIG_RTC in arch/x86/kernel/hpet.c.

Because of this, it's possible to compile the drivers/char/rtc.ko driver as module and still use the HPET emulation functionality. This is also expressed in Kconfig.

---
 arch/x86/Kconfig       |    2 +-
 arch/x86/kernel/hpet.c |    2 --
 drivers/char/rtc.c     |   15 ++++++++++++++-
 3 files changed, 15 insertions(+), 4 deletions(-)
arch/x86/Kconfig +1 -1
@@ -405,7 +405,7 @@ config HPET_TIMER
 
 config HPET_EMULATE_RTC
 	def_bool y
-	depends on HPET_TIMER && RTC=y
+	depends on HPET_TIMER && (RTC=y || RTC=m)
 
 # Mark as embedded because too many people got it wrong.
 # The code disables itself when not needed.
arch/x86/kernel/hpet.c +0 -2
@@ -708,8 +708,6 @@ irqreturn_t hpet_rtc_interrupt(int irq, 
 		rtc_int_flag |= (RTC_IRQF | (RTC_NUM_INTS << 8));
 		if (irq_handler)
 			irq_handler(rtc_int_flag, dev_id);
-
-		rtc_interrupt(rtc_int_flag, dev_id);
 	}
 	return IRQ_HANDLED;
 }
drivers/char/rtc.c +14 -1
@@ -110,6 +110,8 @@ static int rtc_has_irq = 1;
 #define hpet_set_rtc_irq_bit(arg)		0
 #define hpet_rtc_timer_init()			do { } while (0)
 #define hpet_rtc_dropped_irq()			0
+#define hpet_register_irq_handler(h)		0
+#define hpet_unregister_irq_handler(h)		0
 #ifdef RTC_IRQ
 static irqreturn_t hpet_rtc_interrupt(int irq, void *dev_id)
 {
@@ -1027,7 +1029,15 @@ no_irq:
 
 #ifdef RTC_IRQ
 	if (is_hpet_enabled()) {
+		int err;
+
 		rtc_int_handler_ptr = hpet_rtc_interrupt;
+		err = hpet_register_irq_handler(rtc_interrupt);
+		if (err != 0) {
+			printk(KERN_WARNING "hpet_register_irq_handler failed "
+					"in rtc_init().");
+			return err;
+		}
 	} else {
 		rtc_int_handler_ptr = rtc_interrupt;
 	}
@@ -1050,6 +1060,7 @@ no_irq:
 	if (misc_register(&rtc_dev)) {
 #ifdef RTC_IRQ
 		free_irq(RTC_IRQ, NULL);
+		hpet_unregister_irq_handler(rtc_interrupt);
 		rtc_has_irq = 0;
 #endif
 		rtc_release_region();
@@ -1141,8 +1152,10 @@ static void __exit rtc_exit(void)
 #else
 	rtc_release_region();
 #ifdef RTC_IRQ
-	if (rtc_has_irq)
+	if (rtc_has_irq) {
 		free_irq(RTC_IRQ, NULL);
+		hpet_unregister_irq_handler(hpet_rtc_interrupt);
+	}
 #endif
 #endif /* CONFIG_SPARC32 */
 }
#3 lore raw

PATCH 3/3 Add HPET RTC emulation to RTC_DRV_CMOS

In reply to Bernhard Walle

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.

---
 arch/x86/Kconfig       |    2 -
 drivers/rtc/rtc-cmos.c |   79 ++++++++++++++++++++++++++++++++++++++++---------
 2 files changed, 67 insertions(+), 14 deletions(-)
arch/x86/Kconfig +1 -1
@@ -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.
drivers/rtc/rtc-cmos.c +66 -13

162 lines not on this page · raw message