From: "Pallipadi, Venkatesh" All the changes required to use HPET in place of PIT as the kernel base-timer at IRQ 0. 25-akpm/arch/i386/kernel/apic.c | 16 ++- 25-akpm/arch/i386/kernel/time.c | 32 ++++++ 25-akpm/arch/i386/kernel/time_hpet.c | 171 +++++++++++++++++++++++++++++++++ 25-akpm/include/asm-i386/apic.h | 2 25-akpm/include/asm-i386/hpet.h | 106 ++++++++++++++++++++ 25-akpm/include/asm-i386/mc146818rtc.h | 4 6 files changed, 327 insertions(+), 4 deletions(-) diff -puN arch/i386/kernel/apic.c~hpet-04-core arch/i386/kernel/apic.c --- 25/arch/i386/kernel/apic.c~hpet-04-core Fri Aug 29 10:51:17 2003 +++ 25-akpm/arch/i386/kernel/apic.c Fri Aug 29 10:51:17 2003 @@ -34,6 +34,7 @@ #include #include #include +#include #include @@ -779,7 +780,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; @@ -801,6 +803,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 @@ -841,7 +849,7 @@ static void setup_APIC_timer(unsigned in /* * Wait for IRQ0's slice: */ - wait_8254_wraparound(); + wait_timer_tick(); __setup_APIC_LVTT(clocks); @@ -884,7 +892,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: @@ -897,7 +905,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 -puN arch/i386/kernel/time.c~hpet-04-core arch/i386/kernel/time.c --- 25/arch/i386/kernel/time.c~hpet-04-core Fri Aug 29 10:51:17 2003 +++ 25-akpm/arch/i386/kernel/time.c Fri Aug 29 10:51:17 2003 @@ -60,6 +60,8 @@ #include #include +#include + #include #include "io_ports.h" @@ -291,8 +293,38 @@ static int time_init_device(void) device_initcall(time_init_device); +#ifdef CONFIG_HPET_TIMER +extern void (*late_time_init)(void); +/* Duplicate of time_init() below, with hpet_enable part added */ +void __init hpet_time_init(void) +{ + xtime.tv_sec = get_cmos_time(); + wall_to_monotonic.tv_sec = -xtime.tv_sec; + xtime.tv_nsec = (INITIAL_JIFFIES % HZ) * (NSEC_PER_SEC / HZ); + wall_to_monotonic.tv_nsec = -xtime.tv_nsec; + + if (hpet_enable() >= 0) { + printk("Using HPET for base-timer\n"); + } + + cur_timer = select_timer(); + time_init_hook(); +} +#endif + void __init time_init(void) { +#ifdef CONFIG_HPET_TIMER + if (is_hpet_capable()) { + /* + * HPET initialization needs to do memory-mapped io. So, let + * us do a late initialization after mem_init(). + */ + late_time_init = hpet_time_init; + return; + } +#endif + xtime.tv_sec = get_cmos_time(); wall_to_monotonic.tv_sec = -xtime.tv_sec; xtime.tv_nsec = (INITIAL_JIFFIES % HZ) * (NSEC_PER_SEC / HZ); diff -puN /dev/null arch/i386/kernel/time_hpet.c --- /dev/null Thu Apr 11 07:25:15 2002 +++ 25-akpm/arch/i386/kernel/time_hpet.c Fri Aug 29 10:51:17 2003 @@ -0,0 +1,171 @@ +/* + * 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 +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include + +unsigned long hpet_period; /* fsecs / HPET clock */ +unsigned long hpet_tick; /* hpet clks count per tick */ +unsigned long hpet_address; /* hpet memory map physical address */ + +static int use_hpet; /* can be used for runtime check of hpet */ +static int boot_hpet_disable; /* boottime override for HPET timer */ +static unsigned long hpet_virt_address; /* hpet kernel virtual address */ + +#define FSEC_TO_USEC (1000000000UL) + +int hpet_readl(unsigned long a) +{ + return readl(hpet_virt_address + a); +} + +void hpet_writel(unsigned long d, unsigned long a) +{ + writel(d, hpet_virt_address + a); +} + +#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; + } + hpet_virt_address = (unsigned long) ioremap_nocache(hpet_address, + HPET_MMAP_SIZE); + /* + * 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; +} + +int is_hpet_capable(void) +{ + if (!boot_hpet_disable && hpet_address) + return 1; + return 0; +} + +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 -puN include/asm-i386/apic.h~hpet-04-core include/asm-i386/apic.h --- 25/include/asm-i386/apic.h~hpet-04-core Fri Aug 29 10:51:17 2003 +++ 25-akpm/include/asm-i386/apic.h Fri Aug 29 10:51:17 2003 @@ -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 -puN /dev/null include/asm-i386/hpet.h --- /dev/null Thu Apr 11 07:25:15 2002 +++ 25-akpm/include/asm-i386/hpet.h Fri Aug 29 10:51:17 2003 @@ -0,0 +1,106 @@ + +#ifndef _I386_HPET_H +#define _I386_HPET_H + +#ifdef CONFIG_HPET_TIMER + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +/* + * 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_MMAP_SIZE 1024 + +#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 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 physical address */ + +extern int hpet_rtc_timer_init(void); +extern int hpet_enable(void); +extern int is_hpet_enabled(void); +extern int is_hpet_capable(void); +extern int hpet_readl(unsigned long a); +extern void hpet_writel(unsigned long d, unsigned long a); + +#endif /* CONFIG_HPET_TIMER */ +#endif /* _I386_HPET_H */ diff -puN include/asm-i386/mc146818rtc.h~hpet-04-core include/asm-i386/mc146818rtc.h --- 25/include/asm-i386/mc146818rtc.h~hpet-04-core Fri Aug 29 10:51:17 2003 +++ 25-akpm/include/asm-i386/mc146818rtc.h Fri Aug 29 10:51:17 2003 @@ -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 */ _