2003-08-19 22:08:54

by Pallipadi, Venkatesh

[permalink] [raw]
Subject: [PATCH][2.6][2/5]Support for HPET based timer

2/5 - hpet2.patch - All the changes required to use HPET in place
of PIT as the kernel base-timer at IRQ 0.




diff -purN linux-2.6.0-test1/arch/i386/kernel/apic.c linux-2.6.0-test1-hpet/arch/i386/kernel/apic.c
--- linux-2.6.0-test1/arch/i386/kernel/apic.c 2003-07-13 20:39:27.000000000 -0700
+++ linux-2.6.0-test1-hpet/arch/i386/kernel/apic.c 2003-08-13 11:36:24.000000000 -0700
@@ -34,6 +34,7 @@
#include <asm/pgalloc.h>
#include <asm/desc.h>
#include <asm/arch_hooks.h>
+#include <asm/hpet.h>

#include <mach_apic.h>

@@ -749,7 +750,8 @@ static unsigned int __init get_8254_time
return count;
}

-void __init wait_8254_wraparound(void)
+/* next tick in 8254 can be caught by catching timer wraparound */
+static void __init wait_8254_wraparound(void)
{
unsigned int curr_count, prev_count=~0;
int delta;
@@ -771,6 +773,12 @@ void __init wait_8254_wraparound(void)
}

/*
+ * Default initialization for 8254 timers. If we use other timers like HPET,
+ * we override this later
+ */
+void (*wait_timer_tick)(void) = wait_8254_wraparound;
+
+/*
* This function sets up the local APIC timer, with a timeout of
* 'clocks' APIC bus clock. During calibration we actually call
* this function twice on the boot CPU, once with a bogus timeout
@@ -811,7 +819,7 @@ static void setup_APIC_timer(unsigned in
/*
* Wait for IRQ0's slice:
*/
- wait_8254_wraparound();
+ wait_timer_tick();

__setup_APIC_LVTT(clocks);

@@ -854,7 +862,7 @@ int __init calibrate_APIC_clock(void)
* (the current tick might have been already half done)
*/

- wait_8254_wraparound();
+ wait_timer_tick();

/*
* We wrapped around just now. Let's start:
@@ -867,7 +875,7 @@ int __init calibrate_APIC_clock(void)
* Let's wait LOOPS wraprounds:
*/
for (i = 0; i < LOOPS; i++)
- wait_8254_wraparound();
+ wait_timer_tick();

tt2 = apic_read(APIC_TMCCT);
if (cpu_has_tsc)
diff -purN linux-2.6.0-test1/arch/i386/kernel/time.c linux-2.6.0-test1-hpet/arch/i386/kernel/time.c
--- linux-2.6.0-test1/arch/i386/kernel/time.c 2003-07-13 20:34:29.000000000 -0700
+++ linux-2.6.0-test1-hpet/arch/i386/kernel/time.c 2003-08-18 12:36:25.000000000 -0700
@@ -60,6 +60,8 @@
#include <linux/timex.h>
#include <linux/config.h>

+#include <asm/hpet.h>
+
#include <asm/arch_hooks.h>

#include "io_ports.h"
@@ -298,6 +300,12 @@ void __init time_init(void)
xtime.tv_nsec = (INITIAL_JIFFIES % HZ) * (NSEC_PER_SEC / HZ);
wall_to_monotonic.tv_nsec = -xtime.tv_nsec;

+#ifdef CONFIG_HPET_TIMER
+ if (hpet_enable() >= 0) {
+ printk("Using HPET for base-timer\n");
+ }
+#endif
+
cur_timer = select_timer();
time_init_hook();
}
diff -purN linux-2.6.0-test1/arch/i386/kernel/time_hpet.c linux-2.6.0-test1-hpet/arch/i386/kernel/time_hpet.c
--- linux-2.6.0-test1/arch/i386/kernel/time_hpet.c 1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.0-test1-hpet/arch/i386/kernel/time_hpet.c 2003-08-18 20:22:06.000000000 -0700
@@ -0,0 +1,153 @@
+/*
+ * linux/arch/i386/kernel/time_hpet.c
+ * This code largely copied from arch/x86_64/kernel/time.c
+ * See that file for credits.
+ *
+ * 2003-06-30 Venkatesh Pallipadi - Additional changes for HPET support
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/param.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/smp.h>
+
+#include <asm/timer.h>
+#include <asm/fixmap.h>
+#include <asm/apic.h>
+
+#include <linux/timex.h>
+#include <linux/config.h>
+
+#include <asm/hpet.h>
+
+unsigned long hpet_period; /* fsecs / HPET clock */
+unsigned long hpet_tick; /* hpet clks count per tick */
+unsigned long hpet_address; /* hpet memory map address */
+
+static int use_hpet; /* can be used for runtime check of hpet */
+static int boot_hpet_disable; /* boottime override for HPET timer */
+
+#define FSEC_TO_USEC (1000000000UL)
+
+#ifdef CONFIG_X86_LOCAL_APIC
+/*
+ * HPET counters dont wrap around on every tick. They just change the
+ * comparator value and continue. Next tick can be caught by checking
+ * for a change in the comparator value. Used in apic.c.
+ */
+void __init wait_hpet_tick(void)
+{
+ unsigned int start_cmp_val, end_cmp_val;
+
+ start_cmp_val = hpet_readl(HPET_T0_CMP);
+ do {
+ end_cmp_val = hpet_readl(HPET_T0_CMP);
+ } while (start_cmp_val == end_cmp_val);
+}
+#endif
+
+/*
+ * Check whether HPET was found by ACPI boot parse. If yes setup HPET
+ * counter 0 for kernel base timer.
+ */
+int __init hpet_enable(void)
+{
+ unsigned int cfg, id;
+ unsigned long tick_fsec_low, tick_fsec_high; /* tick in femto sec */
+ unsigned long hpet_tick_rem;
+
+ if (boot_hpet_disable)
+ return -1;
+
+ if (!hpet_address) {
+ return -1;
+ }
+ set_fixmap_nocache(FIX_HPET_BASE, hpet_address);
+ printk(KERN_INFO "HPET enabled at %#lx\n", hpet_address);
+
+ /*
+ * Read the period, compute tick and quotient.
+ */
+ id = hpet_readl(HPET_ID);
+
+ /*
+ * We are checking for value '1' or more in number field.
+ * So, we are OK with HPET_EMULATE_RTC part too, where we need
+ * to have atleast 2 timers.
+ */
+ if (!(id & HPET_ID_NUMBER) ||
+ !(id & HPET_ID_LEGSUP))
+ return -1;
+
+ if (((id & HPET_ID_VENDOR) >> HPET_ID_VENDOR_SHIFT) !=
+ HPET_ID_VENDOR_8086)
+ return -1;
+
+ hpet_period = hpet_readl(HPET_PERIOD);
+ if ((hpet_period < HPET_MIN_PERIOD) || (hpet_period > HPET_MAX_PERIOD))
+ return -1;
+
+ /*
+ * 64 bit math
+ * First changing tick into fsec
+ * Then 64 bit div to find number of hpet clk per tick
+ */
+ ASM_MUL64_REG(tick_fsec_low, tick_fsec_high,
+ KERNEL_TICK_USEC, FSEC_TO_USEC);
+ ASM_DIV64_REG(hpet_tick, hpet_tick_rem,
+ hpet_period, tick_fsec_low, tick_fsec_high);
+
+ if (hpet_tick_rem > (hpet_period >> 1))
+ hpet_tick++; /* rounding the result */
+
+ /*
+ * Stop the timers and reset the main counter.
+ */
+ cfg = hpet_readl(HPET_CFG);
+ cfg &= ~HPET_CFG_ENABLE;
+ hpet_writel(cfg, HPET_CFG);
+ hpet_writel(0, HPET_COUNTER);
+ hpet_writel(0, HPET_COUNTER + 4);
+
+ /*
+ * Set up timer 0, as periodic with first interrupt to happen at
+ * hpet_tick, and period also hpet_tick.
+ */
+ cfg = hpet_readl(HPET_T0_CFG);
+ cfg |= HPET_TN_ENABLE | HPET_TN_PERIODIC |
+ HPET_TN_SETVAL | HPET_TN_32BIT;
+ hpet_writel(cfg, HPET_T0_CFG);
+ hpet_writel(hpet_tick, HPET_T0_CMP);
+
+ /*
+ * Go!
+ */
+ cfg = hpet_readl(HPET_CFG);
+ cfg |= HPET_CFG_ENABLE | HPET_CFG_LEGACY;
+ hpet_writel(cfg, HPET_CFG);
+
+ use_hpet = 1;
+#ifdef CONFIG_X86_LOCAL_APIC
+ wait_timer_tick = wait_hpet_tick;
+#endif
+ return 0;
+}
+
+int is_hpet_enabled(void)
+{
+ return use_hpet;
+}
+
+static int __init hpet_setup(char* str)
+{
+ if (str) {
+ if (!strncmp("disable", str, 7))
+ boot_hpet_disable = 1;
+ }
+ return 1;
+}
+
+__setup("hpet=", hpet_setup);
+
diff -purN linux-2.6.0-test1/include/asm-i386/apic.h linux-2.6.0-test1-hpet/include/asm-i386/apic.h
--- linux-2.6.0-test1/include/asm-i386/apic.h 2003-07-13 20:38:53.000000000 -0700
+++ linux-2.6.0-test1-hpet/include/asm-i386/apic.h 2003-08-13 11:34:37.000000000 -0700
@@ -64,6 +64,8 @@ static inline void ack_APIC_irq(void)
apic_write_around(APIC_EOI, 0);
}

+extern void (*wait_timer_tick)(void);
+
extern int get_maxlvt(void);
extern void clear_local_APIC(void);
extern void connect_bsp_APIC (void);
diff -purN linux-2.6.0-test1/include/asm-i386/fixmap.h linux-2.6.0-test1-hpet/include/asm-i386/fixmap.h
--- linux-2.6.0-test1/include/asm-i386/fixmap.h 2003-07-13 20:29:30.000000000 -0700
+++ linux-2.6.0-test1-hpet/include/asm-i386/fixmap.h 2003-08-14 13:10:31.000000000 -0700
@@ -44,6 +44,9 @@
enum fixed_addresses {
FIX_HOLE,
FIX_VSYSCALL,
+#ifdef CONFIG_HPET_TIMER
+ FIX_HPET_BASE,
+#endif
#ifdef CONFIG_X86_LOCAL_APIC
FIX_APIC_BASE, /* local (CPU) APIC) -- required for SMP or not */
#endif
diff -purN linux-2.6.0-test1/include/asm-i386/hpet.h linux-2.6.0-test1-hpet/include/asm-i386/hpet.h
--- linux-2.6.0-test1/include/asm-i386/hpet.h 1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.0-test1-hpet/include/asm-i386/hpet.h 2003-08-18 20:22:40.000000000 -0700
@@ -0,0 +1,104 @@
+
+#ifndef _I386_HPET_H
+#define _I386_HPET_H
+
+#ifdef CONFIG_HPET_TIMER
+
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/param.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/time.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/smp.h>
+
+#include <asm/io.h>
+#include <asm/smp.h>
+#include <asm/irq.h>
+#include <asm/msr.h>
+#include <asm/delay.h>
+#include <asm/mpspec.h>
+#include <asm/uaccess.h>
+#include <asm/processor.h>
+
+#include <linux/timex.h>
+#include <linux/config.h>
+
+#include <asm/fixmap.h>
+
+/*
+ * Documentation on HPET can be found at:
+ * http://www.intel.com/ial/home/sp/pcmmspec.htm
+ * ftp://download.intel.com/ial/home/sp/mmts098.pdf
+ */
+
+#define HPET_ID 0x000
+#define HPET_PERIOD 0x004
+#define HPET_CFG 0x010
+#define HPET_STATUS 0x020
+#define HPET_COUNTER 0x0f0
+#define HPET_T0_CFG 0x100
+#define HPET_T0_CMP 0x108
+#define HPET_T0_ROUTE 0x110
+#define HPET_T1_CFG 0x120
+#define HPET_T1_CMP 0x128
+#define HPET_T1_ROUTE 0x130
+#define HPET_T2_CFG 0x140
+#define HPET_T2_CMP 0x148
+#define HPET_T2_ROUTE 0x150
+
+#define HPET_ID_VENDOR 0xffff0000
+#define HPET_ID_LEGSUP 0x00008000
+#define HPET_ID_NUMBER 0x00001f00
+#define HPET_ID_REV 0x000000ff
+
+#define HPET_ID_VENDOR_SHIFT 16
+#define HPET_ID_VENDOR_8086 0x8086
+
+#define HPET_CFG_ENABLE 0x001
+#define HPET_CFG_LEGACY 0x002
+
+#define HPET_TN_ENABLE 0x004
+#define HPET_TN_PERIODIC 0x008
+#define HPET_TN_PERIODIC_CAP 0x010
+#define HPET_TN_SETVAL 0x040
+#define HPET_TN_32BIT 0x100
+
+/* Use our own asm for 64 bit multiply/divide */
+#define ASM_MUL64_REG(eax_out,edx_out,reg_in,eax_in) \
+ __asm__ __volatile__("mull %2" \
+ :"=a" (eax_out), "=d" (edx_out) \
+ :"r" (reg_in), "0" (eax_in))
+
+#define ASM_DIV64_REG(eax_out,edx_out,reg_in,eax_in,edx_in) \
+ __asm__ __volatile__("divl %2" \
+ :"=a" (eax_out), "=d" (edx_out) \
+ :"r" (reg_in), "0" (eax_in), "1" (edx_in))
+
+#define hpet_readl(a) readl(fix_to_virt(FIX_HPET_BASE) + a)
+#define hpet_writel(d,a) writel(d, fix_to_virt(FIX_HPET_BASE) + a)
+
+#define KERNEL_TICK_USEC (1000000UL/HZ) /* tick value in microsec */
+/* Max HPET Period is 10^8 femto sec as in HPET spec */
+#define HPET_MAX_PERIOD (100000000UL)
+/*
+ * Min HPET period is 10^5 femto sec just for safety. If it is less than this,
+ * then 32 bit HPET counter wrapsaround in less than 0.5 sec.
+ */
+#define HPET_MIN_PERIOD (100000UL)
+
+extern unsigned long hpet_period; /* fsecs / HPET clock */
+extern unsigned long hpet_tick; /* hpet clks count per tick */
+extern unsigned long hpet_address; /* hpet memory map address */
+extern unsigned long hpet_virt_address; /* hpet kernel virtual address */
+
+extern int hpet_enable(void);
+extern int is_hpet_enabled(void);
+
+#endif /* CONFIG_HPET_TIMER */
+#endif /* _I386_HPET_H */
diff -purN linux-2.6.0-test1/include/asm-i386/mc146818rtc.h linux-2.6.0-test1-hpet/include/asm-i386/mc146818rtc.h
--- linux-2.6.0-test1/include/asm-i386/mc146818rtc.h 2003-07-13 20:36:37.000000000 -0700
+++ linux-2.6.0-test1-hpet/include/asm-i386/mc146818rtc.h 2003-08-18 20:23:43.000000000 -0700
@@ -24,6 +24,10 @@ outb_p((addr),RTC_PORT(0)); \
outb_p((val),RTC_PORT(1)); \
})

+#ifdef CONFIG_HPET_TIMER
+#define RTC_IRQ 0
+#else
#define RTC_IRQ 8
+#endif

#endif /* _ASM_MC146818RTC_H */
diff -purN linux-2.6.0-test1/Documentation/kernel-parameters.txt linux-2.6.0-test1-hpet/Documentation/kernel-parameters.txt
--- linux-2.6.0-test1/Documentation/kernel-parameters.txt 2003-07-13 20:39:36.000000000 -0700
+++ linux-2.6.0-test1-hpet/Documentation/kernel-parameters.txt 2003-08-18 20:37:41.000000000 -0700
@@ -212,7 +212,10 @@ running once the system is up.
when calculating gettimeofday(). If specicified timesource
is not avalible, it defaults to PIT.
Format: { pit | tsc | cyclone | ... }
-
+
+ hpet= [IA-32,HPET] option to disable HPET and use PIT.
+ Format: disable
+
cm206= [HW,CD]
Format: { auto | [<io>,][<irq>] }




Attachments:
hpet2.ZIP (4.00 kB)
hpet2.ZIP

2003-08-19 22:39:36

by Vojtech Pavlik

[permalink] [raw]
Subject: Re: [PATCH][2.6][2/5]Support for HPET based timer

On Tue, Aug 19, 2003 at 12:20:02PM -0700, Pallipadi, Venkatesh wrote:

> 2/5 - hpet2.patch - All the changes required to use HPET in place
> of PIT as the kernel base-timer at IRQ 0.

What are you using the fixmap() for? i386 doesn't have vsyscalls ...

Why are you limiting the HPET vendor ID to Intel? This code will
happily run on other vendor hardware as well ...


> diff -purN linux-2.6.0-test1/arch/i386/kernel/apic.c linux-2.6.0-test1-hpet/arch/i386/kernel/apic.c
> --- linux-2.6.0-test1/arch/i386/kernel/apic.c 2003-07-13 20:39:27.000000000 -0700
> +++ linux-2.6.0-test1-hpet/arch/i386/kernel/apic.c 2003-08-13 11:36:24.000000000 -0700
> @@ -34,6 +34,7 @@
> #include <asm/pgalloc.h>
> #include <asm/desc.h>
> #include <asm/arch_hooks.h>
> +#include <asm/hpet.h>
>
> #include <mach_apic.h>
>
> @@ -749,7 +750,8 @@ static unsigned int __init get_8254_time
> return count;
> }
>
> -void __init wait_8254_wraparound(void)
> +/* next tick in 8254 can be caught by catching timer wraparound */
> +static void __init wait_8254_wraparound(void)
> {
> unsigned int curr_count, prev_count=~0;
> int delta;
> @@ -771,6 +773,12 @@ void __init wait_8254_wraparound(void)
> }
>
> /*
> + * Default initialization for 8254 timers. If we use other timers like HPET,
> + * we override this later
> + */
> +void (*wait_timer_tick)(void) = wait_8254_wraparound;
> +
> +/*
> * This function sets up the local APIC timer, with a timeout of
> * 'clocks' APIC bus clock. During calibration we actually call
> * this function twice on the boot CPU, once with a bogus timeout
> @@ -811,7 +819,7 @@ static void setup_APIC_timer(unsigned in
> /*
> * Wait for IRQ0's slice:
> */
> - wait_8254_wraparound();
> + wait_timer_tick();
>
> __setup_APIC_LVTT(clocks);
>
> @@ -854,7 +862,7 @@ int __init calibrate_APIC_clock(void)
> * (the current tick might have been already half done)
> */
>
> - wait_8254_wraparound();
> + wait_timer_tick();
>
> /*
> * We wrapped around just now. Let's start:
> @@ -867,7 +875,7 @@ int __init calibrate_APIC_clock(void)
> * Let's wait LOOPS wraprounds:
> */
> for (i = 0; i < LOOPS; i++)
> - wait_8254_wraparound();
> + wait_timer_tick();
>
> tt2 = apic_read(APIC_TMCCT);
> if (cpu_has_tsc)
> diff -purN linux-2.6.0-test1/arch/i386/kernel/time.c linux-2.6.0-test1-hpet/arch/i386/kernel/time.c
> --- linux-2.6.0-test1/arch/i386/kernel/time.c 2003-07-13 20:34:29.000000000 -0700
> +++ linux-2.6.0-test1-hpet/arch/i386/kernel/time.c 2003-08-18 12:36:25.000000000 -0700
> @@ -60,6 +60,8 @@
> #include <linux/timex.h>
> #include <linux/config.h>
>
> +#include <asm/hpet.h>
> +
> #include <asm/arch_hooks.h>
>
> #include "io_ports.h"
> @@ -298,6 +300,12 @@ void __init time_init(void)
> xtime.tv_nsec = (INITIAL_JIFFIES % HZ) * (NSEC_PER_SEC / HZ);
> wall_to_monotonic.tv_nsec = -xtime.tv_nsec;
>
> +#ifdef CONFIG_HPET_TIMER
> + if (hpet_enable() >= 0) {
> + printk("Using HPET for base-timer\n");
> + }
> +#endif
> +
> cur_timer = select_timer();
> time_init_hook();
> }
> diff -purN linux-2.6.0-test1/arch/i386/kernel/time_hpet.c linux-2.6.0-test1-hpet/arch/i386/kernel/time_hpet.c
> --- linux-2.6.0-test1/arch/i386/kernel/time_hpet.c 1969-12-31 16:00:00.000000000 -0800
> +++ linux-2.6.0-test1-hpet/arch/i386/kernel/time_hpet.c 2003-08-18 20:22:06.000000000 -0700
> @@ -0,0 +1,153 @@
> +/*
> + * linux/arch/i386/kernel/time_hpet.c
> + * This code largely copied from arch/x86_64/kernel/time.c
> + * See that file for credits.
> + *
> + * 2003-06-30 Venkatesh Pallipadi - Additional changes for HPET support
> + */
> +
> +#include <linux/errno.h>
> +#include <linux/kernel.h>
> +#include <linux/param.h>
> +#include <linux/string.h>
> +#include <linux/init.h>
> +#include <linux/smp.h>
> +
> +#include <asm/timer.h>
> +#include <asm/fixmap.h>
> +#include <asm/apic.h>
> +
> +#include <linux/timex.h>
> +#include <linux/config.h>
> +
> +#include <asm/hpet.h>
> +
> +unsigned long hpet_period; /* fsecs / HPET clock */
> +unsigned long hpet_tick; /* hpet clks count per tick */
> +unsigned long hpet_address; /* hpet memory map address */
> +
> +static int use_hpet; /* can be used for runtime check of hpet */
> +static int boot_hpet_disable; /* boottime override for HPET timer */
> +
> +#define FSEC_TO_USEC (1000000000UL)
> +
> +#ifdef CONFIG_X86_LOCAL_APIC
> +/*
> + * HPET counters dont wrap around on every tick. They just change the
> + * comparator value and continue. Next tick can be caught by checking
> + * for a change in the comparator value. Used in apic.c.
> + */
> +void __init wait_hpet_tick(void)
> +{
> + unsigned int start_cmp_val, end_cmp_val;
> +
> + start_cmp_val = hpet_readl(HPET_T0_CMP);
> + do {
> + end_cmp_val = hpet_readl(HPET_T0_CMP);
> + } while (start_cmp_val == end_cmp_val);
> +}
> +#endif
> +
> +/*
> + * Check whether HPET was found by ACPI boot parse. If yes setup HPET
> + * counter 0 for kernel base timer.
> + */
> +int __init hpet_enable(void)
> +{
> + unsigned int cfg, id;
> + unsigned long tick_fsec_low, tick_fsec_high; /* tick in femto sec */
> + unsigned long hpet_tick_rem;
> +
> + if (boot_hpet_disable)
> + return -1;
> +
> + if (!hpet_address) {
> + return -1;
> + }
> + set_fixmap_nocache(FIX_HPET_BASE, hpet_address);
> + printk(KERN_INFO "HPET enabled at %#lx\n", hpet_address);
> +
> + /*
> + * Read the period, compute tick and quotient.
> + */
> + id = hpet_readl(HPET_ID);
> +
> + /*
> + * We are checking for value '1' or more in number field.
> + * So, we are OK with HPET_EMULATE_RTC part too, where we need
> + * to have atleast 2 timers.
> + */
> + if (!(id & HPET_ID_NUMBER) ||
> + !(id & HPET_ID_LEGSUP))
> + return -1;
> +
> + if (((id & HPET_ID_VENDOR) >> HPET_ID_VENDOR_SHIFT) !=
> + HPET_ID_VENDOR_8086)
> + return -1;
> +
> + hpet_period = hpet_readl(HPET_PERIOD);
> + if ((hpet_period < HPET_MIN_PERIOD) || (hpet_period > HPET_MAX_PERIOD))
> + return -1;
> +
> + /*
> + * 64 bit math
> + * First changing tick into fsec
> + * Then 64 bit div to find number of hpet clk per tick
> + */
> + ASM_MUL64_REG(tick_fsec_low, tick_fsec_high,
> + KERNEL_TICK_USEC, FSEC_TO_USEC);
> + ASM_DIV64_REG(hpet_tick, hpet_tick_rem,
> + hpet_period, tick_fsec_low, tick_fsec_high);
> +
> + if (hpet_tick_rem > (hpet_period >> 1))
> + hpet_tick++; /* rounding the result */
> +
> + /*
> + * Stop the timers and reset the main counter.
> + */
> + cfg = hpet_readl(HPET_CFG);
> + cfg &= ~HPET_CFG_ENABLE;
> + hpet_writel(cfg, HPET_CFG);
> + hpet_writel(0, HPET_COUNTER);
> + hpet_writel(0, HPET_COUNTER + 4);
> +
> + /*
> + * Set up timer 0, as periodic with first interrupt to happen at
> + * hpet_tick, and period also hpet_tick.
> + */
> + cfg = hpet_readl(HPET_T0_CFG);
> + cfg |= HPET_TN_ENABLE | HPET_TN_PERIODIC |
> + HPET_TN_SETVAL | HPET_TN_32BIT;
> + hpet_writel(cfg, HPET_T0_CFG);
> + hpet_writel(hpet_tick, HPET_T0_CMP);
> +
> + /*
> + * Go!
> + */
> + cfg = hpet_readl(HPET_CFG);
> + cfg |= HPET_CFG_ENABLE | HPET_CFG_LEGACY;
> + hpet_writel(cfg, HPET_CFG);
> +
> + use_hpet = 1;
> +#ifdef CONFIG_X86_LOCAL_APIC
> + wait_timer_tick = wait_hpet_tick;
> +#endif
> + return 0;
> +}
> +
> +int is_hpet_enabled(void)
> +{
> + return use_hpet;
> +}
> +
> +static int __init hpet_setup(char* str)
> +{
> + if (str) {
> + if (!strncmp("disable", str, 7))
> + boot_hpet_disable = 1;
> + }
> + return 1;
> +}
> +
> +__setup("hpet=", hpet_setup);
> +
> diff -purN linux-2.6.0-test1/include/asm-i386/apic.h linux-2.6.0-test1-hpet/include/asm-i386/apic.h
> --- linux-2.6.0-test1/include/asm-i386/apic.h 2003-07-13 20:38:53.000000000 -0700
> +++ linux-2.6.0-test1-hpet/include/asm-i386/apic.h 2003-08-13 11:34:37.000000000 -0700
> @@ -64,6 +64,8 @@ static inline void ack_APIC_irq(void)
> apic_write_around(APIC_EOI, 0);
> }
>
> +extern void (*wait_timer_tick)(void);
> +
> extern int get_maxlvt(void);
> extern void clear_local_APIC(void);
> extern void connect_bsp_APIC (void);
> diff -purN linux-2.6.0-test1/include/asm-i386/fixmap.h linux-2.6.0-test1-hpet/include/asm-i386/fixmap.h
> --- linux-2.6.0-test1/include/asm-i386/fixmap.h 2003-07-13 20:29:30.000000000 -0700
> +++ linux-2.6.0-test1-hpet/include/asm-i386/fixmap.h 2003-08-14 13:10:31.000000000 -0700
> @@ -44,6 +44,9 @@
> enum fixed_addresses {
> FIX_HOLE,
> FIX_VSYSCALL,
> +#ifdef CONFIG_HPET_TIMER
> + FIX_HPET_BASE,
> +#endif
> #ifdef CONFIG_X86_LOCAL_APIC
> FIX_APIC_BASE, /* local (CPU) APIC) -- required for SMP or not */
> #endif
> diff -purN linux-2.6.0-test1/include/asm-i386/hpet.h linux-2.6.0-test1-hpet/include/asm-i386/hpet.h
> --- linux-2.6.0-test1/include/asm-i386/hpet.h 1969-12-31 16:00:00.000000000 -0800
> +++ linux-2.6.0-test1-hpet/include/asm-i386/hpet.h 2003-08-18 20:22:40.000000000 -0700
> @@ -0,0 +1,104 @@
> +
> +#ifndef _I386_HPET_H
> +#define _I386_HPET_H
> +
> +#ifdef CONFIG_HPET_TIMER
> +
> +#include <linux/errno.h>
> +#include <linux/module.h>
> +#include <linux/sched.h>
> +#include <linux/kernel.h>
> +#include <linux/param.h>
> +#include <linux/string.h>
> +#include <linux/mm.h>
> +#include <linux/interrupt.h>
> +#include <linux/time.h>
> +#include <linux/delay.h>
> +#include <linux/init.h>
> +#include <linux/smp.h>
> +
> +#include <asm/io.h>
> +#include <asm/smp.h>
> +#include <asm/irq.h>
> +#include <asm/msr.h>
> +#include <asm/delay.h>
> +#include <asm/mpspec.h>
> +#include <asm/uaccess.h>
> +#include <asm/processor.h>
> +
> +#include <linux/timex.h>
> +#include <linux/config.h>
> +
> +#include <asm/fixmap.h>
> +
> +/*
> + * Documentation on HPET can be found at:
> + * http://www.intel.com/ial/home/sp/pcmmspec.htm
> + * ftp://download.intel.com/ial/home/sp/mmts098.pdf
> + */
> +
> +#define HPET_ID 0x000
> +#define HPET_PERIOD 0x004
> +#define HPET_CFG 0x010
> +#define HPET_STATUS 0x020
> +#define HPET_COUNTER 0x0f0
> +#define HPET_T0_CFG 0x100
> +#define HPET_T0_CMP 0x108
> +#define HPET_T0_ROUTE 0x110
> +#define HPET_T1_CFG 0x120
> +#define HPET_T1_CMP 0x128
> +#define HPET_T1_ROUTE 0x130
> +#define HPET_T2_CFG 0x140
> +#define HPET_T2_CMP 0x148
> +#define HPET_T2_ROUTE 0x150
> +
> +#define HPET_ID_VENDOR 0xffff0000
> +#define HPET_ID_LEGSUP 0x00008000
> +#define HPET_ID_NUMBER 0x00001f00
> +#define HPET_ID_REV 0x000000ff
> +
> +#define HPET_ID_VENDOR_SHIFT 16
> +#define HPET_ID_VENDOR_8086 0x8086
> +
> +#define HPET_CFG_ENABLE 0x001
> +#define HPET_CFG_LEGACY 0x002
> +
> +#define HPET_TN_ENABLE 0x004
> +#define HPET_TN_PERIODIC 0x008
> +#define HPET_TN_PERIODIC_CAP 0x010
> +#define HPET_TN_SETVAL 0x040
> +#define HPET_TN_32BIT 0x100
> +
> +/* Use our own asm for 64 bit multiply/divide */
> +#define ASM_MUL64_REG(eax_out,edx_out,reg_in,eax_in) \
> + __asm__ __volatile__("mull %2" \
> + :"=a" (eax_out), "=d" (edx_out) \
> + :"r" (reg_in), "0" (eax_in))
> +
> +#define ASM_DIV64_REG(eax_out,edx_out,reg_in,eax_in,edx_in) \
> + __asm__ __volatile__("divl %2" \
> + :"=a" (eax_out), "=d" (edx_out) \
> + :"r" (reg_in), "0" (eax_in), "1" (edx_in))
> +
> +#define hpet_readl(a) readl(fix_to_virt(FIX_HPET_BASE) + a)
> +#define hpet_writel(d,a) writel(d, fix_to_virt(FIX_HPET_BASE) + a)
> +
> +#define KERNEL_TICK_USEC (1000000UL/HZ) /* tick value in microsec */
> +/* Max HPET Period is 10^8 femto sec as in HPET spec */
> +#define HPET_MAX_PERIOD (100000000UL)
> +/*
> + * Min HPET period is 10^5 femto sec just for safety. If it is less than this,
> + * then 32 bit HPET counter wrapsaround in less than 0.5 sec.
> + */
> +#define HPET_MIN_PERIOD (100000UL)
> +
> +extern unsigned long hpet_period; /* fsecs / HPET clock */
> +extern unsigned long hpet_tick; /* hpet clks count per tick */
> +extern unsigned long hpet_address; /* hpet memory map address */
> +extern unsigned long hpet_virt_address; /* hpet kernel virtual address */
> +
> +extern int hpet_enable(void);
> +extern int is_hpet_enabled(void);
> +
> +#endif /* CONFIG_HPET_TIMER */
> +#endif /* _I386_HPET_H */
> diff -purN linux-2.6.0-test1/include/asm-i386/mc146818rtc.h linux-2.6.0-test1-hpet/include/asm-i386/mc146818rtc.h
> --- linux-2.6.0-test1/include/asm-i386/mc146818rtc.h 2003-07-13 20:36:37.000000000 -0700
> +++ linux-2.6.0-test1-hpet/include/asm-i386/mc146818rtc.h 2003-08-18 20:23:43.000000000 -0700
> @@ -24,6 +24,10 @@ outb_p((addr),RTC_PORT(0)); \
> outb_p((val),RTC_PORT(1)); \
> })
>
> +#ifdef CONFIG_HPET_TIMER
> +#define RTC_IRQ 0
> +#else
> #define RTC_IRQ 8
> +#endif
>
> #endif /* _ASM_MC146818RTC_H */
> diff -purN linux-2.6.0-test1/Documentation/kernel-parameters.txt linux-2.6.0-test1-hpet/Documentation/kernel-parameters.txt
> --- linux-2.6.0-test1/Documentation/kernel-parameters.txt 2003-07-13 20:39:36.000000000 -0700
> +++ linux-2.6.0-test1-hpet/Documentation/kernel-parameters.txt 2003-08-18 20:37:41.000000000 -0700
> @@ -212,7 +212,10 @@ running once the system is up.
> when calculating gettimeofday(). If specicified timesource
> is not avalible, it defaults to PIT.
> Format: { pit | tsc | cyclone | ... }
> -
> +
> + hpet= [IA-32,HPET] option to disable HPET and use PIT.
> + Format: disable
> +
> cm206= [HW,CD]
> Format: { auto | [<io>,][<irq>] }
>
>
>



--
Vojtech Pavlik
SuSE Labs, SuSE CR

2003-08-19 22:42:36

by Andi Kleen

[permalink] [raw]
Subject: Re: [PATCH][2.6][2/5]Support for HPET based timer

"Pallipadi, Venkatesh" <[email protected]> writes:

> /*
> + * Default initialization for 8254 timers. If we use other timers like HPET,
> + * we override this later
> + */
> +void (*wait_timer_tick)(void) = wait_8254_wraparound;

It would be much cleaner to just poll the generic monotonic time source here,
not add more special cases.

> diff -purN linux-2.6.0-test1/arch/i386/kernel/time_hpet.c linux-2.6.0-test1-hpet/arch/i386/kernel/time_hpet.c
> --- linux-2.6.0-test1/arch/i386/kernel/time_hpet.c 1969-12-31 16:00:00.000000000 -0800
> +++ linux-2.6.0-test1-hpet/arch/i386/kernel/time_hpet.c 2003-08-18 20:22:06.000
000000 -0700

Shouldn't that be in arch/i386/kernel/timers/hpet.c ?

Also I suspect it should be made an generic timer object there with
a timer_ops structure. If some hook for that is missing it could be added to
timer_ops and timers/timer.c

When there is already a generic framework to add new timers it would be a shame
not to use it.

-Andi

2003-08-20 00:19:35

by Pallipadi, Venkatesh

[permalink] [raw]
Subject: RE: [PATCH][2.6][2/5]Support for HPET based timer


Fixmap is for HPET memory map address access. As the timer
initialization happen
early in the boot sequence (before vm initialization), we need to have
fixmap()
and fix_to_virt() to access HPET memory map address.

Yes. Most of the code is generic, and can run on other platforms. The
only reason
I have that check is because, I haven't got chance to test this patch on
other
platforms.

Thanks,
-Venkatesh

> -----Original Message-----
> From: Vojtech Pavlik [mailto:[email protected]]
> Sent: Tuesday, August 19, 2003 3:39 PM
> To: Pallipadi, Venkatesh
> Cc: [email protected]; [email protected];
> Nakajima, Jun; Mallick, Asit K
> Subject: Re: [PATCH][2.6][2/5]Support for HPET based timer
>
>
> On Tue, Aug 19, 2003 at 12:20:02PM -0700, Pallipadi, Venkatesh wrote:
>
> > 2/5 - hpet2.patch - All the changes required to use HPET in place
> > of PIT as the kernel base-timer at IRQ 0.
>
> What are you using the fixmap() for? i386 doesn't have vsyscalls ...
>
> Why are you limiting the HPET vendor ID to Intel? This code will
> happily run on other vendor hardware as well ...
>
>
> > diff -purN linux-2.6.0-test1/arch/i386/kernel/apic.c
> linux-2.6.0-test1-hpet/arch/i386/kernel/apic.c
> > --- linux-2.6.0-test1/arch/i386/kernel/apic.c
> 2003-07-13 20:39:27.000000000 -0700
> > +++ linux-2.6.0-test1-hpet/arch/i386/kernel/apic.c
> 2003-08-13 11:36:24.000000000 -0700
> > @@ -34,6 +34,7 @@
> > #include <asm/pgalloc.h>
> > #include <asm/desc.h>
> > #include <asm/arch_hooks.h>
> > +#include <asm/hpet.h>
> >
> > #include <mach_apic.h>
> >
> > @@ -749,7 +750,8 @@ static unsigned int __init get_8254_time
> > return count;
> > }
> >
> > -void __init wait_8254_wraparound(void)
> > +/* next tick in 8254 can be caught by catching timer wraparound */
> > +static void __init wait_8254_wraparound(void)
> > {
> > unsigned int curr_count, prev_count=~0;
> > int delta;
> > @@ -771,6 +773,12 @@ void __init wait_8254_wraparound(void)
> > }
> >
> > /*
> > + * Default initialization for 8254 timers. If we use other
> timers like HPET,
> > + * we override this later
> > + */
> > +void (*wait_timer_tick)(void) = wait_8254_wraparound;
> > +
> > +/*
> > * This function sets up the local APIC timer, with a timeout of
> > * 'clocks' APIC bus clock. During calibration we actually call
> > * this function twice on the boot CPU, once with a bogus timeout
> > @@ -811,7 +819,7 @@ static void setup_APIC_timer(unsigned in
> > /*
> > * Wait for IRQ0's slice:
> > */
> > - wait_8254_wraparound();
> > + wait_timer_tick();
> >
> > __setup_APIC_LVTT(clocks);
> >
> > @@ -854,7 +862,7 @@ int __init calibrate_APIC_clock(void)
> > * (the current tick might have been already half done)
> > */
> >
> > - wait_8254_wraparound();
> > + wait_timer_tick();
> >
> > /*
> > * We wrapped around just now. Let's start:
> > @@ -867,7 +875,7 @@ int __init calibrate_APIC_clock(void)
> > * Let's wait LOOPS wraprounds:
> > */
> > for (i = 0; i < LOOPS; i++)
> > - wait_8254_wraparound();
> > + wait_timer_tick();
> >
> > tt2 = apic_read(APIC_TMCCT);
> > if (cpu_has_tsc)
> > diff -purN linux-2.6.0-test1/arch/i386/kernel/time.c
> linux-2.6.0-test1-hpet/arch/i386/kernel/time.c
> > --- linux-2.6.0-test1/arch/i386/kernel/time.c
> 2003-07-13 20:34:29.000000000 -0700
> > +++ linux-2.6.0-test1-hpet/arch/i386/kernel/time.c
> 2003-08-18 12:36:25.000000000 -0700
> > @@ -60,6 +60,8 @@
> > #include <linux/timex.h>
> > #include <linux/config.h>
> >
> > +#include <asm/hpet.h>
> > +
> > #include <asm/arch_hooks.h>
> >
> > #include "io_ports.h"
> > @@ -298,6 +300,12 @@ void __init time_init(void)
> > xtime.tv_nsec = (INITIAL_JIFFIES % HZ) * (NSEC_PER_SEC / HZ);
> > wall_to_monotonic.tv_nsec = -xtime.tv_nsec;
> >
> > +#ifdef CONFIG_HPET_TIMER
> > + if (hpet_enable() >= 0) {
> > + printk("Using HPET for base-timer\n");
> > + }
> > +#endif
> > +
> > cur_timer = select_timer();
> > time_init_hook();
> > }
> > diff -purN linux-2.6.0-test1/arch/i386/kernel/time_hpet.c
> linux-2.6.0-test1-hpet/arch/i386/kernel/time_hpet.c
> > --- linux-2.6.0-test1/arch/i386/kernel/time_hpet.c
> 1969-12-31 16:00:00.000000000 -0800
> > +++ linux-2.6.0-test1-hpet/arch/i386/kernel/time_hpet.c
> 2003-08-18 20:22:06.000000000 -0700
> > @@ -0,0 +1,153 @@
> > +/*
> > + * linux/arch/i386/kernel/time_hpet.c
> > + * This code largely copied from arch/x86_64/kernel/time.c
> > + * See that file for credits.
> > + *
> > + * 2003-06-30 Venkatesh Pallipadi - Additional changes
> for HPET support
> > + */
> > +
> > +#include <linux/errno.h>
> > +#include <linux/kernel.h>
> > +#include <linux/param.h>
> > +#include <linux/string.h>
> > +#include <linux/init.h>
> > +#include <linux/smp.h>
> > +
> > +#include <asm/timer.h>
> > +#include <asm/fixmap.h>
> > +#include <asm/apic.h>
> > +
> > +#include <linux/timex.h>
> > +#include <linux/config.h>
> > +
> > +#include <asm/hpet.h>
> > +
> > +unsigned long hpet_period; /* fsecs / HPET clock */
> > +unsigned long hpet_tick; /* hpet clks count per tick */
> > +unsigned long hpet_address; /* hpet memory map address */
> > +
> > +static int use_hpet; /* can be used for
> runtime check of hpet */
> > +static int boot_hpet_disable; /* boottime override
> for HPET timer */
> > +
> > +#define FSEC_TO_USEC (1000000000UL)
> > +
> > +#ifdef CONFIG_X86_LOCAL_APIC
> > +/*
> > + * HPET counters dont wrap around on every tick. They just
> change the
> > + * comparator value and continue. Next tick can be caught
> by checking
> > + * for a change in the comparator value. Used in apic.c.
> > + */
> > +void __init wait_hpet_tick(void)
> > +{
> > + unsigned int start_cmp_val, end_cmp_val;
> > +
> > + start_cmp_val = hpet_readl(HPET_T0_CMP);
> > + do {
> > + end_cmp_val = hpet_readl(HPET_T0_CMP);
> > + } while (start_cmp_val == end_cmp_val);
> > +}
> > +#endif
> > +
> > +/*
> > + * Check whether HPET was found by ACPI boot parse. If yes
> setup HPET
> > + * counter 0 for kernel base timer.
> > + */
> > +int __init hpet_enable(void)
> > +{
> > + unsigned int cfg, id;
> > + unsigned long tick_fsec_low, tick_fsec_high; /* tick in
> femto sec */
> > + unsigned long hpet_tick_rem;
> > +
> > + if (boot_hpet_disable)
> > + return -1;
> > +
> > + if (!hpet_address) {
> > + return -1;
> > + }
> > + set_fixmap_nocache(FIX_HPET_BASE, hpet_address);
> > + printk(KERN_INFO "HPET enabled at %#lx\n", hpet_address);
> > +
> > + /*
> > + * Read the period, compute tick and quotient.
> > + */
> > + id = hpet_readl(HPET_ID);
> > +
> > + /*
> > + * We are checking for value '1' or more in number field.
> > + * So, we are OK with HPET_EMULATE_RTC part too, where we need
> > + * to have atleast 2 timers.
> > + */
> > + if (!(id & HPET_ID_NUMBER) ||
> > + !(id & HPET_ID_LEGSUP))
> > + return -1;
> > +
> > + if (((id & HPET_ID_VENDOR) >> HPET_ID_VENDOR_SHIFT) !=
> > + HPET_ID_VENDOR_8086)
> > + return -1;
> > +
> > + hpet_period = hpet_readl(HPET_PERIOD);
> > + if ((hpet_period < HPET_MIN_PERIOD) || (hpet_period >
> HPET_MAX_PERIOD))
> > + return -1;
> > +
> > + /*
> > + * 64 bit math
> > + * First changing tick into fsec
> > + * Then 64 bit div to find number of hpet clk per tick
> > + */
> > + ASM_MUL64_REG(tick_fsec_low, tick_fsec_high,
> > + KERNEL_TICK_USEC, FSEC_TO_USEC);
> > + ASM_DIV64_REG(hpet_tick, hpet_tick_rem,
> > + hpet_period, tick_fsec_low, tick_fsec_high);
> > +
> > + if (hpet_tick_rem > (hpet_period >> 1))
> > + hpet_tick++; /* rounding the result */
> > +
> > + /*
> > + * Stop the timers and reset the main counter.
> > + */
> > + cfg = hpet_readl(HPET_CFG);
> > + cfg &= ~HPET_CFG_ENABLE;
> > + hpet_writel(cfg, HPET_CFG);
> > + hpet_writel(0, HPET_COUNTER);
> > + hpet_writel(0, HPET_COUNTER + 4);
> > +
> > + /*
> > + * Set up timer 0, as periodic with first interrupt to
> happen at
> > + * hpet_tick, and period also hpet_tick.
> > + */
> > + cfg = hpet_readl(HPET_T0_CFG);
> > + cfg |= HPET_TN_ENABLE | HPET_TN_PERIODIC |
> > + HPET_TN_SETVAL | HPET_TN_32BIT;
> > + hpet_writel(cfg, HPET_T0_CFG);
> > + hpet_writel(hpet_tick, HPET_T0_CMP);
> > +
> > + /*
> > + * Go!
> > + */
> > + cfg = hpet_readl(HPET_CFG);
> > + cfg |= HPET_CFG_ENABLE | HPET_CFG_LEGACY;
> > + hpet_writel(cfg, HPET_CFG);
> > +
> > + use_hpet = 1;
> > +#ifdef CONFIG_X86_LOCAL_APIC
> > + wait_timer_tick = wait_hpet_tick;
> > +#endif
> > + return 0;
> > +}
> > +
> > +int is_hpet_enabled(void)
> > +{
> > + return use_hpet;
> > +}
> > +
> > +static int __init hpet_setup(char* str)
> > +{
> > + if (str) {
> > + if (!strncmp("disable", str, 7))
> > + boot_hpet_disable = 1;
> > + }
> > + return 1;
> > +}
> > +
> > +__setup("hpet=", hpet_setup);
> > +
> > diff -purN linux-2.6.0-test1/include/asm-i386/apic.h
> linux-2.6.0-test1-hpet/include/asm-i386/apic.h
> > --- linux-2.6.0-test1/include/asm-i386/apic.h
> 2003-07-13 20:38:53.000000000 -0700
> > +++ linux-2.6.0-test1-hpet/include/asm-i386/apic.h
> 2003-08-13 11:34:37.000000000 -0700
> > @@ -64,6 +64,8 @@ static inline void ack_APIC_irq(void)
> > apic_write_around(APIC_EOI, 0);
> > }
> >
> > +extern void (*wait_timer_tick)(void);
> > +
> > extern int get_maxlvt(void);
> > extern void clear_local_APIC(void);
> > extern void connect_bsp_APIC (void);
> > diff -purN linux-2.6.0-test1/include/asm-i386/fixmap.h
> linux-2.6.0-test1-hpet/include/asm-i386/fixmap.h
> > --- linux-2.6.0-test1/include/asm-i386/fixmap.h
> 2003-07-13 20:29:30.000000000 -0700
> > +++ linux-2.6.0-test1-hpet/include/asm-i386/fixmap.h
> 2003-08-14 13:10:31.000000000 -0700
> > @@ -44,6 +44,9 @@
> > enum fixed_addresses {
> > FIX_HOLE,
> > FIX_VSYSCALL,
> > +#ifdef CONFIG_HPET_TIMER
> > + FIX_HPET_BASE,
> > +#endif
> > #ifdef CONFIG_X86_LOCAL_APIC
> > FIX_APIC_BASE, /* local (CPU) APIC) -- required for
> SMP or not */
> > #endif
> > diff -purN linux-2.6.0-test1/include/asm-i386/hpet.h
> linux-2.6.0-test1-hpet/include/asm-i386/hpet.h
> > --- linux-2.6.0-test1/include/asm-i386/hpet.h
> 1969-12-31 16:00:00.000000000 -0800
> > +++ linux-2.6.0-test1-hpet/include/asm-i386/hpet.h
> 2003-08-18 20:22:40.000000000 -0700
> > @@ -0,0 +1,104 @@
> > +
> > +#ifndef _I386_HPET_H
> > +#define _I386_HPET_H
> > +
> > +#ifdef CONFIG_HPET_TIMER
> > +
> > +#include <linux/errno.h>
> > +#include <linux/module.h>
> > +#include <linux/sched.h>
> > +#include <linux/kernel.h>
> > +#include <linux/param.h>
> > +#include <linux/string.h>
> > +#include <linux/mm.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/time.h>
> > +#include <linux/delay.h>
> > +#include <linux/init.h>
> > +#include <linux/smp.h>
> > +
> > +#include <asm/io.h>
> > +#include <asm/smp.h>
> > +#include <asm/irq.h>
> > +#include <asm/msr.h>
> > +#include <asm/delay.h>
> > +#include <asm/mpspec.h>
> > +#include <asm/uaccess.h>
> > +#include <asm/processor.h>
> > +
> > +#include <linux/timex.h>
> > +#include <linux/config.h>
> > +
> > +#include <asm/fixmap.h>
> > +
> > +/*
> > + * Documentation on HPET can be found at:
> > + * http://www.intel.com/ial/home/sp/pcmmspec.htm
> > + * ftp://download.intel.com/ial/home/sp/mmts098.pdf
> > + */
> > +
> > +#define HPET_ID 0x000
> > +#define HPET_PERIOD 0x004
> > +#define HPET_CFG 0x010
> > +#define HPET_STATUS 0x020
> > +#define HPET_COUNTER 0x0f0
> > +#define HPET_T0_CFG 0x100
> > +#define HPET_T0_CMP 0x108
> > +#define HPET_T0_ROUTE 0x110
> > +#define HPET_T1_CFG 0x120
> > +#define HPET_T1_CMP 0x128
> > +#define HPET_T1_ROUTE 0x130
> > +#define HPET_T2_CFG 0x140
> > +#define HPET_T2_CMP 0x148
> > +#define HPET_T2_ROUTE 0x150
> > +
> > +#define HPET_ID_VENDOR 0xffff0000
> > +#define HPET_ID_LEGSUP 0x00008000
> > +#define HPET_ID_NUMBER 0x00001f00
> > +#define HPET_ID_REV 0x000000ff
> > +
> > +#define HPET_ID_VENDOR_SHIFT 16
> > +#define HPET_ID_VENDOR_8086 0x8086
> > +
> > +#define HPET_CFG_ENABLE 0x001
> > +#define HPET_CFG_LEGACY 0x002
> > +
> > +#define HPET_TN_ENABLE 0x004
> > +#define HPET_TN_PERIODIC 0x008
> > +#define HPET_TN_PERIODIC_CAP 0x010
> > +#define HPET_TN_SETVAL 0x040
> > +#define HPET_TN_32BIT 0x100
> > +
> > +/* Use our own asm for 64 bit multiply/divide */
> > +#define ASM_MUL64_REG(eax_out,edx_out,reg_in,eax_in)
> \
> > + __asm__ __volatile__("mull %2"
> \
> > + :"=a" (eax_out), "=d" (edx_out)
> \
> > + :"r" (reg_in), "0" (eax_in))
> > +
> > +#define
> ASM_DIV64_REG(eax_out,edx_out,reg_in,eax_in,edx_in) \
> > + __asm__ __volatile__("divl %2"
> \
> > + :"=a" (eax_out), "=d" (edx_out)
> \
> > + :"r" (reg_in), "0" (eax_in),
> "1" (edx_in))
> > +
> > +#define hpet_readl(a)
> readl(fix_to_virt(FIX_HPET_BASE) + a)
> > +#define hpet_writel(d,a) writel(d,
> fix_to_virt(FIX_HPET_BASE) + a)
> > +
> > +#define KERNEL_TICK_USEC (1000000UL/HZ) /* tick value
> in microsec */
> > +/* Max HPET Period is 10^8 femto sec as in HPET spec */
> > +#define HPET_MAX_PERIOD (100000000UL)
> > +/*
> > + * Min HPET period is 10^5 femto sec just for safety. If
> it is less than this,
> > + * then 32 bit HPET counter wrapsaround in less than 0.5 sec.
> > + */
> > +#define HPET_MIN_PERIOD (100000UL)
> > +
> > +extern unsigned long hpet_period; /* fsecs / HPET clock */
> > +extern unsigned long hpet_tick; /* hpet clks count per tick */
> > +extern unsigned long hpet_address; /* hpet memory map address */
> > +extern unsigned long hpet_virt_address; /* hpet kernel
> virtual address */
> > +
> > +extern int hpet_enable(void);
> > +extern int is_hpet_enabled(void);
> > +
> > +#endif /* CONFIG_HPET_TIMER */
> > +#endif /* _I386_HPET_H */
> > diff -purN linux-2.6.0-test1/include/asm-i386/mc146818rtc.h
> linux-2.6.0-test1-hpet/include/asm-i386/mc146818rtc.h
> > --- linux-2.6.0-test1/include/asm-i386/mc146818rtc.h
> 2003-07-13 20:36:37.000000000 -0700
> > +++ linux-2.6.0-test1-hpet/include/asm-i386/mc146818rtc.h
> 2003-08-18 20:23:43.000000000 -0700
> > @@ -24,6 +24,10 @@ outb_p((addr),RTC_PORT(0)); \
> > outb_p((val),RTC_PORT(1)); \
> > })
> >
> > +#ifdef CONFIG_HPET_TIMER
> > +#define RTC_IRQ 0
> > +#else
> > #define RTC_IRQ 8
> > +#endif
> >
> > #endif /* _ASM_MC146818RTC_H */
> > diff -purN
> linux-2.6.0-test1/Documentation/kernel-parameters.txt
> linux-2.6.0-test1-hpet/Documentation/kernel-parameters.txt
> > --- linux-2.6.0-test1/Documentation/kernel-parameters.txt
> 2003-07-13 20:39:36.000000000 -0700
> > +++
> linux-2.6.0-test1-hpet/Documentation/kernel-parameters.txt
> 2003-08-18 20:37:41.000000000 -0700
> > @@ -212,7 +212,10 @@ running once the system is up.
> > when calculating gettimeofday(). If
> specicified timesource
> > is not avalible, it defaults to PIT.
> > Format: { pit | tsc | cyclone | ... }
> > -
> > +
> > + hpet= [IA-32,HPET] option to disable HPET and use PIT.
> > + Format: disable
> > +
> > cm206= [HW,CD]
> > Format: { auto | [<io>,][<irq>] }
> >
> >
> >
>
>
>
> --
> Vojtech Pavlik
> SuSE Labs, SuSE CR
>

2003-08-20 01:30:31

by Pallipadi, Venkatesh

[permalink] [raw]
Subject: RE: [PATCH][2.6][2/5]Support for HPET based timer



The timer stuff in kernel is divided into two levels:
1) Base kernel timer, the one that generates periodic timer interrupt on
IRQ 0. There is also associated initializations like APIC timer
initialization in case of SMP,
which again depends on the timer hardware. As of now, kernel has only
have PIT
in this level.

2) Various timers under arch/i386/kernel/timers, basically used during
gettimeofday().
we currently have different timers here like, timer_cyclone, timer_tsc
or timer_pit.
This part has a clean infrastructure to add and/or prioritize different
timers.


With HPET support we are changing stuff at both the levels.
1) We use HPET hardware to generate HZ interrupts on IRQ 0. This is the
change that
is there in PATCH 2/5. Unfortunately, we cannot use the existing timers
infrastructure
for this part. We tried to keep the changes here as less as possible.
But, still had to
do changes in apic.c as it was assuming PIT for base timer. And the
other change is in
time.c, wherein we have to calibrate/initialize HPET for base timer, in
place of PIT.
The reason we kept timer_hpet.c in arch/i386/kernel is because it has
more to do with
initialization of the base-kernel-timer, than the gettimeofday-timer.

2) The timers for gettimeofday will change too, with HPET. The timer
list will be
something like, timer_cyclone, timer_hpet, timer_tsc. This change is
there in
PATCH 3/5. This change uses the exisiting timer infrastructure in
arch/i386/kernel/timers


Thanks,
-Venkatesh

> -----Original Message-----
> From: Andi Kleen [mailto:[email protected]]
> Sent: Tuesday, August 19, 2003 3:41 PM
> To: Pallipadi, Venkatesh
> Cc: [email protected]
> Subject: Re: [PATCH][2.6][2/5]Support for HPET based timer
>
>
> "Pallipadi, Venkatesh" <[email protected]> writes:
>
> > /*
> > + * Default initialization for 8254 timers. If we use other
> timers like HPET,
> > + * we override this later
> > + */
> > +void (*wait_timer_tick)(void) = wait_8254_wraparound;
>
> It would be much cleaner to just poll the generic monotonic
> time source here,
> not add more special cases.
>
> > diff -purN linux-2.6.0-test1/arch/i386/kernel/time_hpet.c
> linux-2.6.0-test1-hpet/arch/i386/kernel/time_hpet.c
> > --- linux-2.6.0-test1/arch/i386/kernel/time_hpet.c
> 1969-12-31 16:00:00.000000000 -0800
> > +++ linux-2.6.0-test1-hpet/arch/i386/kernel/time_hpet.c
> 2003-08-18 20:22:06.000
> 000000 -0700
>
> Shouldn't that be in arch/i386/kernel/timers/hpet.c ?
>
> Also I suspect it should be made an generic timer object there with
> a timer_ops structure. If some hook for that is missing it
> could be added to
> timer_ops and timers/timer.c
>
> When there is already a generic framework to add new timers
> it would be a shame
> not to use it.
>
> -Andi
>

2003-08-20 08:15:00

by Vojtech Pavlik

[permalink] [raw]
Subject: Re: [PATCH][2.6][2/5]Support for HPET based timer

On Tue, Aug 19, 2003 at 05:18:50PM -0700, Pallipadi, Venkatesh wrote:

> Fixmap is for HPET memory map address access. As the timer
> initialization happen
> early in the boot sequence (before vm initialization), we need to have
> fixmap()
> and fix_to_virt() to access HPET memory map address.

Ahh, yes, you're right. You can't use ioremap at that time. Actually I
did the same on x86_64 not only because of vsyscalls.

> Yes. Most of the code is generic, and can run on other platforms. The
> only reason
> I have that check is because, I haven't got chance to test this patch on
> other
> platforms.
>
> Thanks,
> -Venkatesh
>
> > -----Original Message-----
> > From: Vojtech Pavlik [mailto:[email protected]]
> > Sent: Tuesday, August 19, 2003 3:39 PM
> > To: Pallipadi, Venkatesh
> > Cc: [email protected]; [email protected];
> > Nakajima, Jun; Mallick, Asit K
> > Subject: Re: [PATCH][2.6][2/5]Support for HPET based timer
> >
> >
> > On Tue, Aug 19, 2003 at 12:20:02PM -0700, Pallipadi, Venkatesh wrote:
> >
> > > 2/5 - hpet2.patch - All the changes required to use HPET in place
> > > of PIT as the kernel base-timer at IRQ 0.
> >
> > What are you using the fixmap() for? i386 doesn't have vsyscalls ...
> >
> > Why are you limiting the HPET vendor ID to Intel? This code will
> > happily run on other vendor hardware as well ...
> >
> >
> > > diff -purN linux-2.6.0-test1/arch/i386/kernel/apic.c
> > linux-2.6.0-test1-hpet/arch/i386/kernel/apic.c
> > > --- linux-2.6.0-test1/arch/i386/kernel/apic.c
> > 2003-07-13 20:39:27.000000000 -0700
> > > +++ linux-2.6.0-test1-hpet/arch/i386/kernel/apic.c
> > 2003-08-13 11:36:24.000000000 -0700
> > > @@ -34,6 +34,7 @@
> > > #include <asm/pgalloc.h>
> > > #include <asm/desc.h>
> > > #include <asm/arch_hooks.h>
> > > +#include <asm/hpet.h>
> > >
> > > #include <mach_apic.h>
> > >
> > > @@ -749,7 +750,8 @@ static unsigned int __init get_8254_time
> > > return count;
> > > }
> > >
> > > -void __init wait_8254_wraparound(void)
> > > +/* next tick in 8254 can be caught by catching timer wraparound */
> > > +static void __init wait_8254_wraparound(void)
> > > {
> > > unsigned int curr_count, prev_count=~0;
> > > int delta;
> > > @@ -771,6 +773,12 @@ void __init wait_8254_wraparound(void)
> > > }
> > >
> > > /*
> > > + * Default initialization for 8254 timers. If we use other
> > timers like HPET,
> > > + * we override this later
> > > + */
> > > +void (*wait_timer_tick)(void) = wait_8254_wraparound;
> > > +
> > > +/*
> > > * This function sets up the local APIC timer, with a timeout of
> > > * 'clocks' APIC bus clock. During calibration we actually call
> > > * this function twice on the boot CPU, once with a bogus timeout
> > > @@ -811,7 +819,7 @@ static void setup_APIC_timer(unsigned in
> > > /*
> > > * Wait for IRQ0's slice:
> > > */
> > > - wait_8254_wraparound();
> > > + wait_timer_tick();
> > >
> > > __setup_APIC_LVTT(clocks);
> > >
> > > @@ -854,7 +862,7 @@ int __init calibrate_APIC_clock(void)
> > > * (the current tick might have been already half done)
> > > */
> > >
> > > - wait_8254_wraparound();
> > > + wait_timer_tick();
> > >
> > > /*
> > > * We wrapped around just now. Let's start:
> > > @@ -867,7 +875,7 @@ int __init calibrate_APIC_clock(void)
> > > * Let's wait LOOPS wraprounds:
> > > */
> > > for (i = 0; i < LOOPS; i++)
> > > - wait_8254_wraparound();
> > > + wait_timer_tick();
> > >
> > > tt2 = apic_read(APIC_TMCCT);
> > > if (cpu_has_tsc)
> > > diff -purN linux-2.6.0-test1/arch/i386/kernel/time.c
> > linux-2.6.0-test1-hpet/arch/i386/kernel/time.c
> > > --- linux-2.6.0-test1/arch/i386/kernel/time.c
> > 2003-07-13 20:34:29.000000000 -0700
> > > +++ linux-2.6.0-test1-hpet/arch/i386/kernel/time.c
> > 2003-08-18 12:36:25.000000000 -0700
> > > @@ -60,6 +60,8 @@
> > > #include <linux/timex.h>
> > > #include <linux/config.h>
> > >
> > > +#include <asm/hpet.h>
> > > +
> > > #include <asm/arch_hooks.h>
> > >
> > > #include "io_ports.h"
> > > @@ -298,6 +300,12 @@ void __init time_init(void)
> > > xtime.tv_nsec = (INITIAL_JIFFIES % HZ) * (NSEC_PER_SEC / HZ);
> > > wall_to_monotonic.tv_nsec = -xtime.tv_nsec;
> > >
> > > +#ifdef CONFIG_HPET_TIMER
> > > + if (hpet_enable() >= 0) {
> > > + printk("Using HPET for base-timer\n");
> > > + }
> > > +#endif
> > > +
> > > cur_timer = select_timer();
> > > time_init_hook();
> > > }
> > > diff -purN linux-2.6.0-test1/arch/i386/kernel/time_hpet.c
> > linux-2.6.0-test1-hpet/arch/i386/kernel/time_hpet.c
> > > --- linux-2.6.0-test1/arch/i386/kernel/time_hpet.c
> > 1969-12-31 16:00:00.000000000 -0800
> > > +++ linux-2.6.0-test1-hpet/arch/i386/kernel/time_hpet.c
> > 2003-08-18 20:22:06.000000000 -0700
> > > @@ -0,0 +1,153 @@
> > > +/*
> > > + * linux/arch/i386/kernel/time_hpet.c
> > > + * This code largely copied from arch/x86_64/kernel/time.c
> > > + * See that file for credits.
> > > + *
> > > + * 2003-06-30 Venkatesh Pallipadi - Additional changes
> > for HPET support
> > > + */
> > > +
> > > +#include <linux/errno.h>
> > > +#include <linux/kernel.h>
> > > +#include <linux/param.h>
> > > +#include <linux/string.h>
> > > +#include <linux/init.h>
> > > +#include <linux/smp.h>
> > > +
> > > +#include <asm/timer.h>
> > > +#include <asm/fixmap.h>
> > > +#include <asm/apic.h>
> > > +
> > > +#include <linux/timex.h>
> > > +#include <linux/config.h>
> > > +
> > > +#include <asm/hpet.h>
> > > +
> > > +unsigned long hpet_period; /* fsecs / HPET clock */
> > > +unsigned long hpet_tick; /* hpet clks count per tick */
> > > +unsigned long hpet_address; /* hpet memory map address */
> > > +
> > > +static int use_hpet; /* can be used for
> > runtime check of hpet */
> > > +static int boot_hpet_disable; /* boottime override
> > for HPET timer */
> > > +
> > > +#define FSEC_TO_USEC (1000000000UL)
> > > +
> > > +#ifdef CONFIG_X86_LOCAL_APIC
> > > +/*
> > > + * HPET counters dont wrap around on every tick. They just
> > change the
> > > + * comparator value and continue. Next tick can be caught
> > by checking
> > > + * for a change in the comparator value. Used in apic.c.
> > > + */
> > > +void __init wait_hpet_tick(void)
> > > +{
> > > + unsigned int start_cmp_val, end_cmp_val;
> > > +
> > > + start_cmp_val = hpet_readl(HPET_T0_CMP);
> > > + do {
> > > + end_cmp_val = hpet_readl(HPET_T0_CMP);
> > > + } while (start_cmp_val == end_cmp_val);
> > > +}
> > > +#endif
> > > +
> > > +/*
> > > + * Check whether HPET was found by ACPI boot parse. If yes
> > setup HPET
> > > + * counter 0 for kernel base timer.
> > > + */
> > > +int __init hpet_enable(void)
> > > +{
> > > + unsigned int cfg, id;
> > > + unsigned long tick_fsec_low, tick_fsec_high; /* tick in
> > femto sec */
> > > + unsigned long hpet_tick_rem;
> > > +
> > > + if (boot_hpet_disable)
> > > + return -1;
> > > +
> > > + if (!hpet_address) {
> > > + return -1;
> > > + }
> > > + set_fixmap_nocache(FIX_HPET_BASE, hpet_address);
> > > + printk(KERN_INFO "HPET enabled at %#lx\n", hpet_address);
> > > +
> > > + /*
> > > + * Read the period, compute tick and quotient.
> > > + */
> > > + id = hpet_readl(HPET_ID);
> > > +
> > > + /*
> > > + * We are checking for value '1' or more in number field.
> > > + * So, we are OK with HPET_EMULATE_RTC part too, where we need
> > > + * to have atleast 2 timers.
> > > + */
> > > + if (!(id & HPET_ID_NUMBER) ||
> > > + !(id & HPET_ID_LEGSUP))
> > > + return -1;
> > > +
> > > + if (((id & HPET_ID_VENDOR) >> HPET_ID_VENDOR_SHIFT) !=
> > > + HPET_ID_VENDOR_8086)
> > > + return -1;
> > > +
> > > + hpet_period = hpet_readl(HPET_PERIOD);
> > > + if ((hpet_period < HPET_MIN_PERIOD) || (hpet_period >
> > HPET_MAX_PERIOD))
> > > + return -1;
> > > +
> > > + /*
> > > + * 64 bit math
> > > + * First changing tick into fsec
> > > + * Then 64 bit div to find number of hpet clk per tick
> > > + */
> > > + ASM_MUL64_REG(tick_fsec_low, tick_fsec_high,
> > > + KERNEL_TICK_USEC, FSEC_TO_USEC);
> > > + ASM_DIV64_REG(hpet_tick, hpet_tick_rem,
> > > + hpet_period, tick_fsec_low, tick_fsec_high);
> > > +
> > > + if (hpet_tick_rem > (hpet_period >> 1))
> > > + hpet_tick++; /* rounding the result */
> > > +
> > > + /*
> > > + * Stop the timers and reset the main counter.
> > > + */
> > > + cfg = hpet_readl(HPET_CFG);
> > > + cfg &= ~HPET_CFG_ENABLE;
> > > + hpet_writel(cfg, HPET_CFG);
> > > + hpet_writel(0, HPET_COUNTER);
> > > + hpet_writel(0, HPET_COUNTER + 4);
> > > +
> > > + /*
> > > + * Set up timer 0, as periodic with first interrupt to
> > happen at
> > > + * hpet_tick, and period also hpet_tick.
> > > + */
> > > + cfg = hpet_readl(HPET_T0_CFG);
> > > + cfg |= HPET_TN_ENABLE | HPET_TN_PERIODIC |
> > > + HPET_TN_SETVAL | HPET_TN_32BIT;
> > > + hpet_writel(cfg, HPET_T0_CFG);
> > > + hpet_writel(hpet_tick, HPET_T0_CMP);
> > > +
> > > + /*
> > > + * Go!
> > > + */
> > > + cfg = hpet_readl(HPET_CFG);
> > > + cfg |= HPET_CFG_ENABLE | HPET_CFG_LEGACY;
> > > + hpet_writel(cfg, HPET_CFG);
> > > +
> > > + use_hpet = 1;
> > > +#ifdef CONFIG_X86_LOCAL_APIC
> > > + wait_timer_tick = wait_hpet_tick;
> > > +#endif
> > > + return 0;
> > > +}
> > > +
> > > +int is_hpet_enabled(void)
> > > +{
> > > + return use_hpet;
> > > +}
> > > +
> > > +static int __init hpet_setup(char* str)
> > > +{
> > > + if (str) {
> > > + if (!strncmp("disable", str, 7))
> > > + boot_hpet_disable = 1;
> > > + }
> > > + return 1;
> > > +}
> > > +
> > > +__setup("hpet=", hpet_setup);
> > > +
> > > diff -purN linux-2.6.0-test1/include/asm-i386/apic.h
> > linux-2.6.0-test1-hpet/include/asm-i386/apic.h
> > > --- linux-2.6.0-test1/include/asm-i386/apic.h
> > 2003-07-13 20:38:53.000000000 -0700
> > > +++ linux-2.6.0-test1-hpet/include/asm-i386/apic.h
> > 2003-08-13 11:34:37.000000000 -0700
> > > @@ -64,6 +64,8 @@ static inline void ack_APIC_irq(void)
> > > apic_write_around(APIC_EOI, 0);
> > > }
> > >
> > > +extern void (*wait_timer_tick)(void);
> > > +
> > > extern int get_maxlvt(void);
> > > extern void clear_local_APIC(void);
> > > extern void connect_bsp_APIC (void);
> > > diff -purN linux-2.6.0-test1/include/asm-i386/fixmap.h
> > linux-2.6.0-test1-hpet/include/asm-i386/fixmap.h
> > > --- linux-2.6.0-test1/include/asm-i386/fixmap.h
> > 2003-07-13 20:29:30.000000000 -0700
> > > +++ linux-2.6.0-test1-hpet/include/asm-i386/fixmap.h
> > 2003-08-14 13:10:31.000000000 -0700
> > > @@ -44,6 +44,9 @@
> > > enum fixed_addresses {
> > > FIX_HOLE,
> > > FIX_VSYSCALL,
> > > +#ifdef CONFIG_HPET_TIMER
> > > + FIX_HPET_BASE,
> > > +#endif
> > > #ifdef CONFIG_X86_LOCAL_APIC
> > > FIX_APIC_BASE, /* local (CPU) APIC) -- required for
> > SMP or not */
> > > #endif
> > > diff -purN linux-2.6.0-test1/include/asm-i386/hpet.h
> > linux-2.6.0-test1-hpet/include/asm-i386/hpet.h
> > > --- linux-2.6.0-test1/include/asm-i386/hpet.h
> > 1969-12-31 16:00:00.000000000 -0800
> > > +++ linux-2.6.0-test1-hpet/include/asm-i386/hpet.h
> > 2003-08-18 20:22:40.000000000 -0700
> > > @@ -0,0 +1,104 @@
> > > +
> > > +#ifndef _I386_HPET_H
> > > +#define _I386_HPET_H
> > > +
> > > +#ifdef CONFIG_HPET_TIMER
> > > +
> > > +#include <linux/errno.h>
> > > +#include <linux/module.h>
> > > +#include <linux/sched.h>
> > > +#include <linux/kernel.h>
> > > +#include <linux/param.h>
> > > +#include <linux/string.h>
> > > +#include <linux/mm.h>
> > > +#include <linux/interrupt.h>
> > > +#include <linux/time.h>
> > > +#include <linux/delay.h>
> > > +#include <linux/init.h>
> > > +#include <linux/smp.h>
> > > +
> > > +#include <asm/io.h>
> > > +#include <asm/smp.h>
> > > +#include <asm/irq.h>
> > > +#include <asm/msr.h>
> > > +#include <asm/delay.h>
> > > +#include <asm/mpspec.h>
> > > +#include <asm/uaccess.h>
> > > +#include <asm/processor.h>
> > > +
> > > +#include <linux/timex.h>
> > > +#include <linux/config.h>
> > > +
> > > +#include <asm/fixmap.h>
> > > +
> > > +/*
> > > + * Documentation on HPET can be found at:
> > > + * http://www.intel.com/ial/home/sp/pcmmspec.htm
> > > + * ftp://download.intel.com/ial/home/sp/mmts098.pdf
> > > + */
> > > +
> > > +#define HPET_ID 0x000
> > > +#define HPET_PERIOD 0x004
> > > +#define HPET_CFG 0x010
> > > +#define HPET_STATUS 0x020
> > > +#define HPET_COUNTER 0x0f0
> > > +#define HPET_T0_CFG 0x100
> > > +#define HPET_T0_CMP 0x108
> > > +#define HPET_T0_ROUTE 0x110
> > > +#define HPET_T1_CFG 0x120
> > > +#define HPET_T1_CMP 0x128
> > > +#define HPET_T1_ROUTE 0x130
> > > +#define HPET_T2_CFG 0x140
> > > +#define HPET_T2_CMP 0x148
> > > +#define HPET_T2_ROUTE 0x150
> > > +
> > > +#define HPET_ID_VENDOR 0xffff0000
> > > +#define HPET_ID_LEGSUP 0x00008000
> > > +#define HPET_ID_NUMBER 0x00001f00
> > > +#define HPET_ID_REV 0x000000ff
> > > +
> > > +#define HPET_ID_VENDOR_SHIFT 16
> > > +#define HPET_ID_VENDOR_8086 0x8086
> > > +
> > > +#define HPET_CFG_ENABLE 0x001
> > > +#define HPET_CFG_LEGACY 0x002
> > > +
> > > +#define HPET_TN_ENABLE 0x004
> > > +#define HPET_TN_PERIODIC 0x008
> > > +#define HPET_TN_PERIODIC_CAP 0x010
> > > +#define HPET_TN_SETVAL 0x040
> > > +#define HPET_TN_32BIT 0x100
> > > +
> > > +/* Use our own asm for 64 bit multiply/divide */
> > > +#define ASM_MUL64_REG(eax_out,edx_out,reg_in,eax_in)
> > \
> > > + __asm__ __volatile__("mull %2"
> > \
> > > + :"=a" (eax_out), "=d" (edx_out)
> > \
> > > + :"r" (reg_in), "0" (eax_in))
> > > +
> > > +#define
> > ASM_DIV64_REG(eax_out,edx_out,reg_in,eax_in,edx_in) \
> > > + __asm__ __volatile__("divl %2"
> > \
> > > + :"=a" (eax_out), "=d" (edx_out)
> > \
> > > + :"r" (reg_in), "0" (eax_in),
> > "1" (edx_in))
> > > +
> > > +#define hpet_readl(a)
> > readl(fix_to_virt(FIX_HPET_BASE) + a)
> > > +#define hpet_writel(d,a) writel(d,
> > fix_to_virt(FIX_HPET_BASE) + a)
> > > +
> > > +#define KERNEL_TICK_USEC (1000000UL/HZ) /* tick value
> > in microsec */
> > > +/* Max HPET Period is 10^8 femto sec as in HPET spec */
> > > +#define HPET_MAX_PERIOD (100000000UL)
> > > +/*
> > > + * Min HPET period is 10^5 femto sec just for safety. If
> > it is less than this,
> > > + * then 32 bit HPET counter wrapsaround in less than 0.5 sec.
> > > + */
> > > +#define HPET_MIN_PERIOD (100000UL)
> > > +
> > > +extern unsigned long hpet_period; /* fsecs / HPET clock */
> > > +extern unsigned long hpet_tick; /* hpet clks count per tick */
> > > +extern unsigned long hpet_address; /* hpet memory map address */
> > > +extern unsigned long hpet_virt_address; /* hpet kernel
> > virtual address */
> > > +
> > > +extern int hpet_enable(void);
> > > +extern int is_hpet_enabled(void);
> > > +
> > > +#endif /* CONFIG_HPET_TIMER */
> > > +#endif /* _I386_HPET_H */
> > > diff -purN linux-2.6.0-test1/include/asm-i386/mc146818rtc.h
> > linux-2.6.0-test1-hpet/include/asm-i386/mc146818rtc.h
> > > --- linux-2.6.0-test1/include/asm-i386/mc146818rtc.h
> > 2003-07-13 20:36:37.000000000 -0700
> > > +++ linux-2.6.0-test1-hpet/include/asm-i386/mc146818rtc.h
> > 2003-08-18 20:23:43.000000000 -0700
> > > @@ -24,6 +24,10 @@ outb_p((addr),RTC_PORT(0)); \
> > > outb_p((val),RTC_PORT(1)); \
> > > })
> > >
> > > +#ifdef CONFIG_HPET_TIMER
> > > +#define RTC_IRQ 0
> > > +#else
> > > #define RTC_IRQ 8
> > > +#endif
> > >
> > > #endif /* _ASM_MC146818RTC_H */
> > > diff -purN
> > linux-2.6.0-test1/Documentation/kernel-parameters.txt
> > linux-2.6.0-test1-hpet/Documentation/kernel-parameters.txt
> > > --- linux-2.6.0-test1/Documentation/kernel-parameters.txt
> > 2003-07-13 20:39:36.000000000 -0700
> > > +++
> > linux-2.6.0-test1-hpet/Documentation/kernel-parameters.txt
> > 2003-08-18 20:37:41.000000000 -0700
> > > @@ -212,7 +212,10 @@ running once the system is up.
> > > when calculating gettimeofday(). If
> > specicified timesource
> > > is not avalible, it defaults to PIT.
> > > Format: { pit | tsc | cyclone | ... }
> > > -
> > > +
> > > + hpet= [IA-32,HPET] option to disable HPET and use PIT.
> > > + Format: disable
> > > +
> > > cm206= [HW,CD]
> > > Format: { auto | [<io>,][<irq>] }
> > >
> > >
> > >
> >
> >
> >
> > --
> > Vojtech Pavlik
> > SuSE Labs, SuSE CR
> >
>

--
Vojtech Pavlik
SuSE Labs, SuSE CR

2003-08-20 10:01:16

by Andi Kleen

[permalink] [raw]
Subject: Re: [PATCH][2.6][2/5]Support for HPET based timer

Vojtech Pavlik <[email protected]> writes:

> On Tue, Aug 19, 2003 at 05:18:50PM -0700, Pallipadi, Venkatesh wrote:
>
> > Fixmap is for HPET memory map address access. As the timer
> > initialization happen
> > early in the boot sequence (before vm initialization), we need to have
> > fixmap()
> > and fix_to_virt() to access HPET memory map address.
>
> Ahh, yes, you're right. You can't use ioremap at that time. Actually I
> did the same on x86_64 not only because of vsyscalls.

iirc i386 has an ioremap_early or somesuch.

-Andi

2003-08-20 10:47:19

by Mikael Pettersson

[permalink] [raw]
Subject: Re: [PATCH][2.6][2/5]Support for HPET based timer

Andi Kleen writes:
> Vojtech Pavlik <[email protected]> writes:
>
> > On Tue, Aug 19, 2003 at 05:18:50PM -0700, Pallipadi, Venkatesh wrote:
> >
> > > Fixmap is for HPET memory map address access. As the timer
> > > initialization happen
> > > early in the boot sequence (before vm initialization), we need to have
> > > fixmap()
> > > and fix_to_virt() to access HPET memory map address.
> >
> > Ahh, yes, you're right. You can't use ioremap at that time. Actually I
> > did the same on x86_64 not only because of vsyscalls.
>
> iirc i386 has an ioremap_early or somesuch.

bt_ioremap(). I wrote it to support early DMI scan so DMI data
could be used to blacklist BIOSen that break local APICs.
This was done pretty much just to handle Dell laptops.

2003-08-20 17:10:27

by Dave Hansen

[permalink] [raw]
Subject: Re: [PATCH][2.6][2/5]Support for HPET based timer

On Wed, 2003-08-20 at 03:01, Andi Kleen wrote:
> Vojtech Pavlik <[email protected]> writes:
>
> > On Tue, Aug 19, 2003 at 05:18:50PM -0700, Pallipadi, Venkatesh wrote:
> >
> > > Fixmap is for HPET memory map address access. As the timer
> > > initialization happen
> > > early in the boot sequence (before vm initialization), we need to have
> > > fixmap()
> > > and fix_to_virt() to access HPET memory map address.
> >
> > Ahh, yes, you're right. You can't use ioremap at that time. Actually I
> > did the same on x86_64 not only because of vsyscalls.
>
> iirc i386 has an ioremap_early or somesuch.

Yep, we have boot_ioremap(). It's used to do ioremap() even while we're
still using the original early boot pagetables (before paging_init()):
arch/i386/mm/boot_ioremap.c

--
Dave Hansen
[email protected]

2003-08-20 17:02:29

by Pallipadi, Venkatesh

[permalink] [raw]
Subject: RE: [PATCH][2.6][2/5]Support for HPET based timer


Yes. I hadn't thought about early_ioremap option. There seems to be
multiple ways of doing early ioremap: bt_ioremap() and boot_ioremap(). I
will look at them closer and work on changing HPET code to use one of
these in place of fixmap.

Thanks,
-Venkatesh

> -----Original Message-----
> From: Mikael Pettersson [mailto:[email protected]]
> Sent: Wednesday, August 20, 2003 3:47 AM
> To: Andi Kleen
> Cc: Vojtech Pavlik; [email protected]; Pallipadi, Venkatesh
> Subject: Re: [PATCH][2.6][2/5]Support for HPET based timer
>
>
> Andi Kleen writes:
> > Vojtech Pavlik <[email protected]> writes:
> >
> > > On Tue, Aug 19, 2003 at 05:18:50PM -0700, Pallipadi,
> Venkatesh wrote:
> > >
> > > > Fixmap is for HPET memory map address access. As the timer
> > > > initialization happen
> > > > early in the boot sequence (before vm initialization),
> we need to have
> > > > fixmap()
> > > > and fix_to_virt() to access HPET memory map address.
> > >
> > > Ahh, yes, you're right. You can't use ioremap at that
> time. Actually I
> > > did the same on x86_64 not only because of vsyscalls.
> >
> > iirc i386 has an ioremap_early or somesuch.
>
> bt_ioremap(). I wrote it to support early DMI scan so DMI data
> could be used to blacklist BIOSen that break local APICs.
> This was done pretty much just to handle Dell laptops.
>

2003-08-26 18:31:26

by Pallipadi, Venkatesh

[permalink] [raw]
Subject: RE: [PATCH][2.6][2/5]Support for HPET based timer


Hi,

This is an update on the option of using some sort of early_ioremap in
place of fixmap for
HPET timers.

Problem Description:
The requirement from HPET side is, we need to map HPET physical
address during timer_init()
routine and also during any read/write HPET addresses. We need to have
this mapping kind of
permanently, as we will do HPET reads/writes during every timer
interrupt and also during
every gettimeofday (if we don't use tsc timer).
And the timer_init() happens before mem_init() (but after paging
init()), so we cannot
directly use ioremap(). Current implementation is using a separate
fixmap region for HPET.

Possible alternatives:
1) boot_ioremap - This looks like a temporary and very short time use
mapping. We may not be
able to use it forever, for HPET purposes, as someone else can do the
mapping and overwrite
HPET mapping anytime. Will need significant changes to supports multiple
users doing
mapping/unmapping.
2) bt_ioremap - Even this seems to be aiming at temporary mapping.
Currently, this supports
only one user at any time. This can be changed to support multiple users
and map and unmap
depending on the address. But, bt_ioremap uses fixmap in its
implementation, and the fixmap
is boot time only. So, we need to transparently switch to ioremap, at
some place before
all inits are done.

Even after changing either boot_ioremap() or bt_ioremap(), we have to
add some more
hooks to move from early ioremap to regular io_remap() after mem_init()
is done. And HPET
addresses can get accessed from both process and interrupt context, may
make things slightly
more difficult. Still it is doable as we can move to regular ioremap
before smp_init(), without
much of a race condition. So, possible changes in this scenario will be:
- Add support for multiple callers using different pages in
bt_ioremap(or boot_ioremap).
- Add HPET specific hook, somewhere in between mem_init() and
smp_init(), to transparently
move from bt_ioremap to ioremap().

The question I have is,
- Is it really worth to move from the current fixmap implementation to
bt_ioremap/ioremap
combination, given all the changes that is required?
- Isn't current implementation (using own fixmap) a cleaner way to do
it? Only drawback
I see is it consumes one page in virtual memory when HPET is configured,
irrespective of
HPET is used at run time or not.

Let me know your thoughts on this, and correct me if I am missing
anything here.

Thanks,
-Venkatesh


> -----Original Message-----
> From: Pallipadi, Venkatesh
> Sent: Wednesday, August 20, 2003 10:02 AM
> To: Mikael Pettersson; Andi Kleen
> Cc: Vojtech Pavlik; [email protected]
> Subject: RE: [PATCH][2.6][2/5]Support for HPET based timer
>
>
>
> Yes. I hadn't thought about early_ioremap option. There seems to be
> multiple ways of doing early ioremap: bt_ioremap() and
> boot_ioremap(). I
> will look at them closer and work on changing HPET code to use one of
> these in place of fixmap.
>
> Thanks,
> -Venkatesh
>
> > -----Original Message-----
> > From: Mikael Pettersson [mailto:[email protected]]
> > Sent: Wednesday, August 20, 2003 3:47 AM
> > To: Andi Kleen
> > Cc: Vojtech Pavlik; [email protected];
> Pallipadi, Venkatesh
> > Subject: Re: [PATCH][2.6][2/5]Support for HPET based timer
> >
> >
> > Andi Kleen writes:
> > > Vojtech Pavlik <[email protected]> writes:
> > >
> > > > On Tue, Aug 19, 2003 at 05:18:50PM -0700, Pallipadi,
> > Venkatesh wrote:
> > > >
> > > > > Fixmap is for HPET memory map address access. As the timer
> > > > > initialization happen
> > > > > early in the boot sequence (before vm initialization),
> > we need to have
> > > > > fixmap()
> > > > > and fix_to_virt() to access HPET memory map address.
> > > >
> > > > Ahh, yes, you're right. You can't use ioremap at that
> > time. Actually I
> > > > did the same on x86_64 not only because of vsyscalls.
> > >
> > > iirc i386 has an ioremap_early or somesuch.
> >
> > bt_ioremap(). I wrote it to support early DMI scan so DMI data
> > could be used to blacklist BIOSen that break local APICs.
> > This was done pretty much just to handle Dell laptops.
> >
> -
> 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/
>

2003-08-26 18:49:15

by Andrew Morton

[permalink] [raw]
Subject: Re: [PATCH][2.6][2/5]Support for HPET based timer

"Pallipadi, Venkatesh" <[email protected]> wrote:
>
> Problem Description:
> The requirement from HPET side is, we need to map HPET physical
> address during timer_init()
> routine and also during any read/write HPET addresses. We need to have
> this mapping kind of
> permanently, as we will do HPET reads/writes during every timer
> interrupt and also during
> every gettimeofday (if we don't use tsc timer).
> And the timer_init() happens before mem_init() (but after paging
> init()), so we cannot
> directly use ioremap(). Current implementation is using a separate
> fixmap region for HPET.

I doubt if we really need the timer running that early, apart from for
calibrate_delay().

You can probably move the time_init() and calibrate_delay() so they occur
after mem_init(). A close review would be needed to see if that is likely
to break anything. If it is, then consider creating a new late_time_init()
thing, and call that and calibrate_delay() after mem_init().



2003-08-26 19:55:59

by Andrew Morton

[permalink] [raw]
Subject: Re: [PATCH][2.6][2/5]Support for HPET based timer

Andrew Morton <[email protected]> wrote:
>
> I doubt if we really need the timer running that early, apart from for
> calibrate_delay().
>
> You can probably move the time_init() and calibrate_delay() so they occur
> after mem_init(). A close review would be needed to see if that is likely
> to break anything. If it is, then consider creating a new late_time_init()
> thing, and call that and calibrate_delay() after mem_init().
>

Actually, I think some platforms (ppc64) will explode if we do the
local_irq_enable() prior to time_init().

So I suggest you look at the latter option:

- change time_init() so that it doesn't actually touch the HPET hardware
in the HPET timer case.

- add late_time_init() after mem_init().

- then do calibrate_delay().

Or whatever. The bottom line is that init/main.c is fragile, but not
inviolable ;)


2003-08-26 21:49:22

by Mikael Pettersson

[permalink] [raw]
Subject: RE: [PATCH][2.6][2/5]Support for HPET based timer

On Tue, 26 Aug 2003 11:31:16 -0700, "Pallipadi, Venkatesh" wrote:
> This is an update on the option of using some sort of early_ioremap in
>place of fixmap for
>HPET timers.
>
>Problem Description:
> The requirement from HPET side is, we need to map HPET physical
>address during timer_init()
>routine and also during any read/write HPET addresses. We need to have
>this mapping kind of
>permanently, as we will do HPET reads/writes during every timer
>interrupt and also during
>every gettimeofday (if we don't use tsc timer).
> And the timer_init() happens before mem_init() (but after paging
>init()), so we cannot
>directly use ioremap(). Current implementation is using a separate
>fixmap region for HPET.
...
>The question I have is,
>- Is it really worth to move from the current fixmap implementation to
>bt_ioremap/ioremap
>combination, given all the changes that is required?
>- Isn't current implementation (using own fixmap) a cleaner way to do
>it? Only drawback
>I see is it consumes one page in virtual memory when HPET is configured,
>irrespective of
>HPET is used at run time or not.

As long as you _must_ map the HPET before ioremap() is working,
and you also must have a permanent mapping, then grabbing a fixmap
page is IMO the cleanest solution. This is for example how the
local APIC mapping is handled. boot/bt ioremap are as you noticed
for temporary mappings only.

However, dynamically migrating the timer to HPET after mem_init()
would be even better, since that avoids the problem altogether.

/Mikael

2003-08-26 23:27:38

by john stultz

[permalink] [raw]
Subject: Re: [PATCH][2.6][2/5]Support for HPET based timer

On Tue, 2003-08-26 at 11:55, Andrew Morton wrote:
> So I suggest you look at the latter option:
>
> - change time_init() so that it doesn't actually touch the HPET hardware
> in the HPET timer case.

Well, the difficult part is deciding in time_init() if we are going to
use HPET without touching the hardware (to say, check if its actually
there).

> - add late_time_init() after mem_init().
>
> - then do calibrate_delay().
>
> Or whatever. The bottom line is that init/main.c is fragile, but not
> inviolable ;)


We could pick a simple time source (ie: PIT) that would get us through
early boot, then choose the real time source in late_time_init(). That
would also make implementing the ACPI PM time-source much simpler as we
could wait until after ACPI is up, letting us avoid having to parse the
tables by hand.

However I'm not sure it would be trivial and bug free. ;)

-john

2003-08-26 23:51:10

by Pallipadi, Venkatesh

[permalink] [raw]
Subject: RE: [PATCH][2.6][2/5]Support for HPET based timer



> -----Original Message-----
> From: john stultz [mailto:[email protected]]
>
> Well, the difficult part is deciding in time_init() if we are going to
> use HPET without touching the hardware (to say, check if its actually
> there).
>

Yes. But, currently we are using ACPI early table parse to look at HPET.
So we should be OK here.
Even if accessing HPET hardware fails later, in the late_time_init(),
we can fallback to PIT, as long as calibrate_delay() is done after
late_time_init().

> We could pick a simple time source (ie: PIT) that would get us through
> early boot, then choose the real time source in late_time_init(). That
> would also make implementing the ACPI PM time-source much simpler as
we
> could wait until after ACPI is up, letting us avoid having to parse
the
> tables by hand.
>
> However I'm not sure it would be trivial and bug free. ;)
>

Depending on multiple time sources may not be good idea, IMO. May be,
one day we will have a fully-legacy-free system with no PIT. :)

Thanks,
-Venkatesh