@@ -74,6 +74,8 @@ extern void hpet_disable(void);
extern unsigned int hpet_readl(unsigned int a);
extern void hpet_writel(unsigned int d, unsigned int a);
extern void force_hpet_resume(void);
+extern void hpet_set_comparator_periodic(int channel, unsigned int cmp,
+ unsigned int period);
#ifdef CONFIG_HPET_EMULATE_RTC
@@ -294,6 +294,39 @@ static void hpet_enable_legacy_int(void)
hpet_legacy_int_enabled = true;
}
+/**
+ * hpet_set_comparator_periodic() - Helper function to set periodic channel
+ * @channel: The HPET channel
+ * @cmp: The value to be written to the comparator/accumulator
+ * @period: Number of ticks per period
+ *
+ * Helper function for updating comparator, accumulator and period values.
+ *
+ * In periodic mode, HPET needs HPET_TN_SETVAL to be set before writing
+ * to the Tn_CMP to update the accumulator. Then, HPET needs a second
+ * write (with HPET_TN_SETVAL cleared) to Tn_CMP to set the period.
+ * The HPET_TN_SETVAL bit is automatically cleared after the first write.
+ *
+ * This function takes a 1 microsecond delay. However, this function is supposed
+ * to be called only once (or when reprogramming the timer) as it deals with a
+ * periodic timer channel.
+ *
+ * See the following documents:
+ * - Intel IA-PC HPET (High Precision Event Timers) Specification
+ * - AMD-8111 HyperTransport I/O Hub Data Sheet, Publication # 24674
+ */
+void hpet_set_comparator_periodic(int channel, unsigned int cmp, unsigned int period)
+{
+ unsigned int v = hpet_readl(HPET_Tn_CFG(channel));
+
+ hpet_writel(v | HPET_TN_SETVAL, HPET_Tn_CFG(channel));
+
+ hpet_writel(cmp, HPET_Tn_CMP(channel));
+
+ udelay(1);
+ hpet_writel(period, HPET_Tn_CMP(channel));
+}
+
static int hpet_clkevt_set_state_periodic(struct clock_event_device *evt)
{
unsigned int channel = clockevent_to_channel(evt)->num;
@@ -306,19 +339,11 @@ static int hpet_clkevt_set_state_periodic(struct clock_event_device *evt)
now = hpet_readl(HPET_COUNTER);
cmp = now + (unsigned int)delta;
cfg = hpet_readl(HPET_Tn_CFG(channel));
- cfg |= HPET_TN_ENABLE | HPET_TN_PERIODIC | HPET_TN_SETVAL |
- HPET_TN_32BIT;
+ cfg |= HPET_TN_ENABLE | HPET_TN_PERIODIC | HPET_TN_32BIT;
hpet_writel(cfg, HPET_Tn_CFG(channel));
- hpet_writel(cmp, HPET_Tn_CMP(channel));
- udelay(1);
- /*
- * HPET on AMD 81xx needs a second write (with HPET_TN_SETVAL
- * cleared) to T0_CMP to set the period. The HPET_TN_SETVAL
- * bit is automatically cleared after the first write.
- * (See AMD-8111 HyperTransport I/O Hub Data Sheet,
- * Publication # 24674)
- */
- hpet_writel((unsigned int)delta, HPET_Tn_CMP(channel));
+
+ hpet_set_comparator_periodic(channel, cmp, (unsigned int)delta);
+
hpet_start_counter();
hpet_print_config();