mirror of
https://github.com/xen-project/xen.git
synced 2026-10-30 03:00:05 +01:00
Add HPET support to Xen. It is quicker to access and more
precise than the PIT. Signed-off-by: Keir Fraser <keir@xensource.com>
This commit is contained in:
1 parent
e386da97d1
commit
5e8bc03f67
5 files changed
+198
-10
No files matched your search
@@ -382,7 +382,7 @@ static int __init acpi_parse_hpet(unsigned long phys, unsigned long size)
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_X86_64
|
||||
#if 0/*def CONFIG_X86_64*/
|
||||
vxtime.hpet_address = hpet_tbl->addr.addrl |
|
||||
((long) hpet_tbl->addr.addrh << 32);
|
||||
|
||||
|
||||
+143
-9
@@ -28,11 +28,18 @@
|
||||
#include <asm/fixmap.h>
|
||||
#include <asm/mc146818rtc.h>
|
||||
#include <asm/div64.h>
|
||||
#include <asm/hpet.h>
|
||||
#include <io_ports.h>
|
||||
|
||||
/* opt_hpet_force: If true, force HPET configuration via PCI space. */
|
||||
/* NB. This is a gross hack. Mainly useful for HPET testing. */
|
||||
static int opt_hpet_force = 0;
|
||||
boolean_param("hpet_force", opt_hpet_force);
|
||||
|
||||
#define EPOCH MILLISECS(1000)
|
||||
|
||||
unsigned long cpu_khz; /* CPU clock frequency in kHz. */
|
||||
unsigned long hpet_address;
|
||||
spinlock_t rtc_lock = SPIN_LOCK_UNLOCKED;
|
||||
int timer_ack = 0;
|
||||
unsigned long volatile jiffies;
|
||||
@@ -58,6 +65,7 @@ static s_time_t stime_platform_stamp;
|
||||
static u64 platform_timer_stamp;
|
||||
static struct time_scale platform_timer_scale;
|
||||
static spinlock_t platform_timer_lock = SPIN_LOCK_UNLOCKED;
|
||||
static u64 (*read_platform_count)(void);
|
||||
|
||||
static inline u32 down_shift(u64 time, int shift)
|
||||
{
|
||||
@@ -225,8 +233,13 @@ void calibrate_tsc_ap(void)
|
||||
atomic_dec(&tsc_calibrate_gang);
|
||||
}
|
||||
|
||||
|
||||
/************************************************************
|
||||
* PLATFORM TIMER 1: PROGRAMMABLE INTERVAL TIMER (LEGACY PIT)
|
||||
*/
|
||||
|
||||
/* Protected by platform_timer_lock. */
|
||||
static u64 platform_pit_counter;
|
||||
static u64 pit_counter64;
|
||||
static u16 pit_stamp;
|
||||
static struct ac_timer pit_overflow_timer;
|
||||
|
||||
@@ -246,21 +259,137 @@ static void pit_overflow(void *unused)
|
||||
|
||||
spin_lock(&platform_timer_lock);
|
||||
counter = pit_read_counter();
|
||||
platform_pit_counter += (u16)(pit_stamp - counter);
|
||||
pit_counter64 += (u16)(pit_stamp - counter);
|
||||
pit_stamp = counter;
|
||||
spin_unlock(&platform_timer_lock);
|
||||
|
||||
set_ac_timer(&pit_overflow_timer, NOW() + MILLISECS(20));
|
||||
}
|
||||
|
||||
static void init_platform_timer(void)
|
||||
static u64 read_pit_count(void)
|
||||
{
|
||||
return pit_counter64 + (u16)(pit_stamp - pit_read_counter());
|
||||
}
|
||||
|
||||
static int init_pit(void)
|
||||
{
|
||||
read_platform_count = read_pit_count;
|
||||
|
||||
init_ac_timer(&pit_overflow_timer, pit_overflow, NULL, 0);
|
||||
pit_overflow(NULL);
|
||||
platform_timer_stamp = platform_pit_counter;
|
||||
platform_timer_stamp = pit_counter64;
|
||||
set_time_scale(&platform_timer_scale, CLOCK_TICK_RATE);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/************************************************************
|
||||
* PLATFORM TIMER 2: HIGH PRECISION EVENT TIMER (HPET)
|
||||
*/
|
||||
|
||||
/* Protected by platform_timer_lock. */
|
||||
static u64 hpet_counter64, hpet_overflow_period;
|
||||
static u32 hpet_stamp;
|
||||
static struct ac_timer hpet_overflow_timer;
|
||||
|
||||
static void hpet_overflow(void *unused)
|
||||
{
|
||||
u32 counter;
|
||||
|
||||
spin_lock(&platform_timer_lock);
|
||||
counter = hpet_read32(HPET_COUNTER);
|
||||
hpet_counter64 += (u32)(counter - hpet_stamp);
|
||||
hpet_stamp = counter;
|
||||
spin_unlock(&platform_timer_lock);
|
||||
|
||||
set_ac_timer(&hpet_overflow_timer, NOW() + hpet_overflow_period);
|
||||
}
|
||||
|
||||
static u64 read_hpet_count(void)
|
||||
{
|
||||
return hpet_counter64 + (u32)(hpet_read32(HPET_COUNTER) - hpet_stamp);
|
||||
}
|
||||
|
||||
static int init_hpet(void)
|
||||
{
|
||||
u64 hpet_rate;
|
||||
u32 hpet_id, hpet_period, cfg;
|
||||
int i;
|
||||
|
||||
if ( (hpet_address == 0) && opt_hpet_force )
|
||||
{
|
||||
printk(KERN_WARNING "WARNING: Enabling HPET base manually!\n");
|
||||
outl(0x800038a0, 0xcf8);
|
||||
outl(0xff000001, 0xcfc);
|
||||
outl(0x800038a0, 0xcf8);
|
||||
hpet_address = inl(0xcfc) & 0xfffffffe;
|
||||
printk(KERN_WARNING "WARNING: Enabled HPET at %#lx.\n", hpet_address);
|
||||
}
|
||||
|
||||
if ( hpet_address == 0 )
|
||||
return 0;
|
||||
|
||||
set_fixmap_nocache(FIX_HPET_BASE, hpet_address);
|
||||
|
||||
hpet_id = hpet_read32(HPET_ID);
|
||||
if ( hpet_id == 0 )
|
||||
{
|
||||
printk("BAD HPET vendor id.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check for sane period (100ps <= period <= 100ns). */
|
||||
hpet_period = hpet_read32(HPET_PERIOD);
|
||||
if ( (hpet_period > 100000000) || (hpet_period < 100000) )
|
||||
{
|
||||
printk("BAD HPET period %u.\n", hpet_period);
|
||||
return 0;
|
||||
}
|
||||
|
||||
cfg = hpet_read32(HPET_CFG);
|
||||
cfg &= ~(HPET_CFG_ENABLE | HPET_CFG_LEGACY);
|
||||
hpet_write32(cfg, HPET_CFG);
|
||||
|
||||
for ( i = 0; i <= ((hpet_id >> 8) & 31); i++ )
|
||||
{
|
||||
cfg = hpet_read32(HPET_T0_CFG + i*0x20);
|
||||
cfg &= ~HPET_TN_ENABLE;
|
||||
hpet_write32(cfg & ~HPET_TN_ENABLE, HPET_T0_CFG);
|
||||
}
|
||||
|
||||
cfg = hpet_read32(HPET_CFG);
|
||||
cfg |= HPET_CFG_ENABLE;
|
||||
hpet_write32(cfg, HPET_CFG);
|
||||
|
||||
read_platform_count = read_hpet_count;
|
||||
|
||||
hpet_rate = 1000000000000000ULL; /* 10^15 */
|
||||
(void)do_div(hpet_rate, hpet_period);
|
||||
set_time_scale(&platform_timer_scale, hpet_rate);
|
||||
|
||||
/* Trigger overflow avoidance roughly when counter increments 2^31. */
|
||||
if ( (hpet_rate >> 31) != 0 )
|
||||
{
|
||||
hpet_overflow_period = MILLISECS(1000);
|
||||
(void)do_div(hpet_overflow_period, (u32)(hpet_rate >> 31) + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
hpet_overflow_period = MILLISECS(1000) << 31;
|
||||
(void)do_div(hpet_overflow_period, (u32)hpet_rate);
|
||||
}
|
||||
|
||||
init_ac_timer(&hpet_overflow_timer, hpet_overflow, NULL, 0);
|
||||
hpet_overflow(NULL);
|
||||
platform_timer_stamp = hpet_counter64;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/************************************************************
|
||||
* GENERIC PLATFORM TIMER INFRASTRUCTURE
|
||||
*/
|
||||
|
||||
static s_time_t __read_platform_stime(u64 platform_time)
|
||||
{
|
||||
u64 diff64 = platform_time - platform_timer_stamp;
|
||||
@@ -276,7 +405,7 @@ static s_time_t read_platform_stime(void)
|
||||
s_time_t stime;
|
||||
|
||||
spin_lock(&platform_timer_lock);
|
||||
counter = platform_pit_counter + (u16)(pit_stamp - pit_read_counter());
|
||||
counter = read_platform_count();
|
||||
stime = __read_platform_stime(counter);
|
||||
spin_unlock(&platform_timer_lock);
|
||||
|
||||
@@ -289,13 +418,19 @@ static void platform_time_calibration(void)
|
||||
s_time_t stamp;
|
||||
|
||||
spin_lock(&platform_timer_lock);
|
||||
counter = platform_pit_counter + (u16)(pit_stamp - pit_read_counter());
|
||||
counter = read_platform_count();
|
||||
stamp = __read_platform_stime(counter);
|
||||
stime_platform_stamp = stamp;
|
||||
platform_timer_stamp = counter;
|
||||
spin_unlock(&platform_timer_lock);
|
||||
}
|
||||
|
||||
static void init_platform_timer(void)
|
||||
{
|
||||
if ( !init_hpet() )
|
||||
BUG_ON(!init_pit());
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
* CMOS Timer functions
|
||||
@@ -494,9 +629,8 @@ static void local_time_calibration(void *unused)
|
||||
#if 0
|
||||
printk("PRE%d: tsc=%lld stime=%lld master=%lld\n",
|
||||
cpu, prev_tsc, prev_local_stime, prev_master_stime);
|
||||
printk("CUR%d: tsc=%lld stime=%lld master=%lld %lld\n",
|
||||
cpu, curr_tsc, curr_local_stime, curr_master_stime,
|
||||
platform_pit_counter);
|
||||
printk("CUR%d: tsc=%lld stime=%lld master=%lld\n",
|
||||
cpu, curr_tsc, curr_local_stime, curr_master_stime);
|
||||
#endif
|
||||
|
||||
/* Local time warps forward if it lags behind master time. */
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#define CONFIG_X86_LOCAL_APIC 1
|
||||
#define CONFIG_X86_GOOD_APIC 1
|
||||
#define CONFIG_X86_IO_APIC 1
|
||||
#define CONFIG_HPET_TIMER 1
|
||||
|
||||
/* Intel P4 currently has largest cache line (L2 line size is 128 bytes). */
|
||||
#define CONFIG_X86_L1_CACHE_SHIFT 7
|
||||
|
||||
@@ -30,6 +30,7 @@ enum fixed_addresses {
|
||||
FIX_IO_APIC_BASE_END = FIX_IO_APIC_BASE_0 + MAX_IO_APICS-1,
|
||||
FIX_ACPI_BEGIN,
|
||||
FIX_ACPI_END = FIX_ACPI_BEGIN + FIX_ACPI_PAGES - 1,
|
||||
FIX_HPET_BASE,
|
||||
__end_of_fixed_addresses
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
#ifndef __X86_HPET_H__
|
||||
#define __X86_HPET_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_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_NUMBER_SHIFT 8
|
||||
|
||||
#define HPET_ID_VENDOR_SHIFT 16
|
||||
#define HPET_ID_VENDOR_8086 0x8086
|
||||
|
||||
#define HPET_CFG_ENABLE 0x001
|
||||
#define HPET_CFG_LEGACY 0x002
|
||||
#define HPET_LEGACY_8254 2
|
||||
#define HPET_LEGACY_RTC 8
|
||||
|
||||
#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
|
||||
|
||||
#define hpet_read32(x) \
|
||||
(*(volatile u32 *)(fix_to_virt(FIX_HPET_BASE) + (x)))
|
||||
#define hpet_write32(y,x) \
|
||||
(*(volatile u32 *)(fix_to_virt(FIX_HPET_BASE) + (x)) = (y))
|
||||
|
||||
#endif /* __X86_HPET_H__ */
|
||||