linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH][2.6-test4][4/6]Support for HPET based timer
@ 2003-08-28 23:44 Pallipadi, Venkatesh
  0 siblings, 0 replies; only message in thread
From: Pallipadi, Venkatesh @ 2003-08-28 23:44 UTC (permalink / raw)
  To: torvalds, akpm; +Cc: linux-kernel, Nakajima, Jun

[-- Attachment #1: Type: text/plain, Size: 12510 bytes --]

4/6 - hpet4.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-test4/arch/i386/kernel/apic.c linux-2.6.0-test4-hpet/arch/i386/kernel/apic.c
--- linux-2.6.0-test4/arch/i386/kernel/apic.c	2003-08-22 17:03:01.000000000 -0700
+++ linux-2.6.0-test4-hpet/arch/i386/kernel/apic.c	2003-08-28 12:18:15.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>
 
@@ -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 -purN linux-2.6.0-test4/arch/i386/kernel/time.c linux-2.6.0-test4-hpet/arch/i386/kernel/time.c
--- linux-2.6.0-test4/arch/i386/kernel/time.c	2003-08-22 16:55:44.000000000 -0700
+++ linux-2.6.0-test4-hpet/arch/i386/kernel/time.c	2003-08-28 12:18:15.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"
@@ -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 -purN linux-2.6.0-test4/arch/i386/kernel/time_hpet.c linux-2.6.0-test4-hpet/arch/i386/kernel/time_hpet.c
--- linux-2.6.0-test4/arch/i386/kernel/time_hpet.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.0-test4-hpet/arch/i386/kernel/time_hpet.c	2003-08-28 12:20:34.000000000 -0700
@@ -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 <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 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 -purN linux-2.6.0-test4/include/asm-i386/apic.h linux-2.6.0-test4-hpet/include/asm-i386/apic.h
--- linux-2.6.0-test4/include/asm-i386/apic.h	2003-08-22 17:02:02.000000000 -0700
+++ linux-2.6.0-test4-hpet/include/asm-i386/apic.h	2003-08-28 12:18:15.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-test4/include/asm-i386/hpet.h linux-2.6.0-test4-hpet/include/asm-i386/hpet.h
--- linux-2.6.0-test4/include/asm-i386/hpet.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.0-test4-hpet/include/asm-i386/hpet.h	2003-08-28 12:21:19.000000000 -0700
@@ -0,0 +1,106 @@
+
+#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_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 -purN linux-2.6.0-test4/include/asm-i386/mc146818rtc.h linux-2.6.0-test4-hpet/include/asm-i386/mc146818rtc.h
--- linux-2.6.0-test4/include/asm-i386/mc146818rtc.h	2003-08-22 16:58:00.000000000 -0700
+++ linux-2.6.0-test4-hpet/include/asm-i386/mc146818rtc.h	2003-08-28 12:22:14.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 */



[-- Attachment #2: hpet04.ZIP --]
[-- Type: application/x-zip-compressed, Size: 3936 bytes --]

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2003-08-28 23:47 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2003-08-28 23:44 [PATCH][2.6-test4][4/6]Support for HPET based timer Pallipadi, Venkatesh

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).