[U-Boot] [PATCH v2 07/16] x86: Replace timer implementation with TSC timer

Simon Glass sjg at chromium.org
Fri Apr 26 15:43:41 CEST 2013


This timer runs at a rate that can be calculated, well over 100MHz. It is
ideal for accurate timing and does not need interrupt servicing.

Remove the old timer implementations at the same time.

To provide a consistent view of boot time, we use the same time
base as coreboot. Use the base timestamp supplied by coreboot
as U-Boot's base time.

While we don't want PCAT timers for timing, we want timer 2 so that we can
still make a beep. Re-purpose the PCAT driver for this, and enable it in
coreboot.

The CONFIG_INTEL_CORE_ARCH option was only used for timer selection, and
is no longer needed since we switch over udelay() to use the new timer
also.

Signed-off-by: Simon Glass <sjg at chromium.org>
---
Changes in v2:
- Squash PCAT and TSC timer patches into a single patch
- Remove all code related to CONFIG_INTEL_CORE_ARCH
- Update timer commit message

 arch/x86/cpu/Makefile             |   2 +-
 arch/x86/cpu/coreboot/timestamp.c |   4 +-
 arch/x86/cpu/interrupts.c         |  28 ---------
 arch/x86/cpu/timer.c              |  17 ------
 arch/x86/include/asm/u-boot-x86.h |   3 +
 arch/x86/lib/Makefile             |   4 +-
 arch/x86/lib/pcat_timer.c         |  69 +---------------------
 arch/x86/lib/timer.c              | 118 --------------------------------------
 arch/x86/lib/tsc_timer.c          | 107 ++++++++++++++++++++++++++++++++++
 include/configs/coreboot.h        |   5 +-
 10 files changed, 121 insertions(+), 236 deletions(-)
 delete mode 100644 arch/x86/cpu/timer.c
 delete mode 100644 arch/x86/lib/timer.c
 create mode 100644 arch/x86/lib/tsc_timer.c

diff --git a/arch/x86/cpu/Makefile b/arch/x86/cpu/Makefile
index 7b520f8..cddf0dd 100644
--- a/arch/x86/cpu/Makefile
+++ b/arch/x86/cpu/Makefile
@@ -30,7 +30,7 @@ LIB	= $(obj)lib$(CPU).o
 
 START-y	= start.o
 START-$(CONFIG_X86_RESET_VECTOR) += resetvec.o start16.o
-COBJS	= interrupts.o cpu.o timer.o
+COBJS	= interrupts.o cpu.o
 
 SRCS	:= $(START:.o=.S) $(SOBJS:.o=.S) $(COBJS:.o=.c)
 OBJS	:= $(addprefix $(obj),$(SOBJS) $(COBJS))
diff --git a/arch/x86/cpu/coreboot/timestamp.c b/arch/x86/cpu/coreboot/timestamp.c
index 2ca7a57..d26718e 100644
--- a/arch/x86/cpu/coreboot/timestamp.c
+++ b/arch/x86/cpu/coreboot/timestamp.c
@@ -39,7 +39,9 @@ static struct timestamp_table *ts_table  __attribute__((section(".data")));
 void timestamp_init(void)
 {
 	ts_table = lib_sysinfo.tstamp_table;
-	timer_set_tsc_base(ts_table->base_time);
+#ifdef CONFIG_SYS_X86_TSC_TIMER
+	timer_set_base(ts_table->base_time);
+#endif
 	timestamp_add_now(TS_U_BOOT_INITTED);
 }
 
diff --git a/arch/x86/cpu/interrupts.c b/arch/x86/cpu/interrupts.c
index e733bcb..7eb2012 100644
--- a/arch/x86/cpu/interrupts.c
+++ b/arch/x86/cpu/interrupts.c
@@ -619,31 +619,3 @@ asm(".globl irq_common_entry\n" \
 	DECLARE_INTERRUPT(253) \
 	DECLARE_INTERRUPT(254) \
 	DECLARE_INTERRUPT(255));
-
-#if defined(CONFIG_INTEL_CORE_ARCH)
-/*
- * Get the number of CPU time counter ticks since it was read first time after
- * restart. This yields a free running counter guaranteed to take almost 6
- * years to wrap around even at 100GHz clock rate.
- */
-u64 get_ticks(void)
-{
-	u64 now_tick = rdtsc();
-
-	if (!gd->arch.tsc_base)
-		gd->arch.tsc_base = now_tick;
-
-	return now_tick - gd->arch.tsc_base;
-}
-
-#define PLATFORM_INFO_MSR 0xce
-
-unsigned long get_tbclk(void)
-{
-	u32 ratio;
-	u64 platform_info = native_read_msr(PLATFORM_INFO_MSR);
-
-	ratio = (platform_info >> 8) & 0xff;
-	return 100 * 1000 * 1000 * ratio; /* 100MHz times Max Non Turbo ratio */
-}
-#endif
diff --git a/arch/x86/cpu/timer.c b/arch/x86/cpu/timer.c
deleted file mode 100644
index 149109d..0000000
--- a/arch/x86/cpu/timer.c
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- * Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- */
-
-#include <common.h>
-
-unsigned long timer_get_us(void)
-{
-	printf("timer_get_us used but not implemented.\n");
-	return 0;
-}
diff --git a/arch/x86/include/asm/u-boot-x86.h b/arch/x86/include/asm/u-boot-x86.h
index 5a59db6..22e0934 100644
--- a/arch/x86/include/asm/u-boot-x86.h
+++ b/arch/x86/include/asm/u-boot-x86.h
@@ -39,6 +39,9 @@ void panic_puts(const char *str);
 void timer_isr(void *);
 typedef void (timer_fnc_t) (void);
 int register_timer_isr (timer_fnc_t *isr_func);
+unsigned long get_tbclk_mhz(void);
+void timer_set_base(uint64_t base);
+int pcat_timer_init(void);
 
 /* Architecture specific - can be in arch/x86/cpu/, arch/x86/lib/, or $(BOARD)/ */
 int dram_init_f(void);
diff --git a/arch/x86/lib/Makefile b/arch/x86/lib/Makefile
index 962593d..f66ad30 100644
--- a/arch/x86/lib/Makefile
+++ b/arch/x86/lib/Makefile
@@ -31,12 +31,12 @@ COBJS-y	+= gcc.o
 COBJS-y	+= init_helpers.o
 COBJS-y	+= interrupts.o
 COBJS-$(CONFIG_SYS_PCAT_INTERRUPTS) += pcat_interrupts.o
-COBJS-$(CONFIG_SYS_GENERIC_TIMER) += pcat_timer.o
+COBJS-$(CONFIG_SYS_PCAT_TIMER) += pcat_timer.o
 COBJS-$(CONFIG_PCI) += pci_type1.o
 COBJS-y	+= relocate.o
 COBJS-y += physmem.o
 COBJS-y	+= string.o
-COBJS-$(CONFIG_SYS_X86_ISR_TIMER)	+= timer.o
+COBJS-$(CONFIG_SYS_X86_TSC_TIMER)	+= tsc_timer.o
 COBJS-$(CONFIG_VIDEO_VGA)	+= video.o
 COBJS-$(CONFIG_CMD_ZBOOT)	+= zimage.o
 
diff --git a/arch/x86/lib/pcat_timer.c b/arch/x86/lib/pcat_timer.c
index b0b6637..1ca3eb9 100644
--- a/arch/x86/lib/pcat_timer.c
+++ b/arch/x86/lib/pcat_timer.c
@@ -24,83 +24,20 @@
 #include <common.h>
 #include <asm/io.h>
 #include <asm/i8254.h>
-#include <asm/ibmpc.h>
-#include <asm/interrupt.h>
 
-#define TIMER0_VALUE 0x04aa /* 1kHz 1.9318MHz / 1000 */
 #define TIMER2_VALUE 0x0a8e /* 440Hz */
 
-static int timer_init_done;
-
-int timer_init(void)
+int pcat_timer_init(void)
 {
-	/* initialize timer 0 and 2
-	 *
-	 * Timer 0 is used to increment system_tick 1000 times/sec
-	 * Timer 1 was used for DRAM refresh in early PC's
-	 * Timer 2 is used to drive the speaker
+	/*
+	 * initialize 2, used to drive the speaker
 	 * (to start a beep: write 3 to port 0x61,
 	 * to stop it again: write 0)
 	 */
-	outb(PIT_CMD_CTR0 | PIT_CMD_BOTH | PIT_CMD_MODE2,
-			PIT_BASE + PIT_COMMAND);
-	outb(TIMER0_VALUE & 0xff, PIT_BASE + PIT_T0);
-	outb(TIMER0_VALUE >> 8, PIT_BASE + PIT_T0);
-
 	outb(PIT_CMD_CTR2 | PIT_CMD_BOTH | PIT_CMD_MODE3,
 			PIT_BASE + PIT_COMMAND);
 	outb(TIMER2_VALUE & 0xff, PIT_BASE + PIT_T2);
 	outb(TIMER2_VALUE >> 8, PIT_BASE + PIT_T2);
 
-	irq_install_handler(0, timer_isr, NULL);
-	unmask_irq(0);
-
-	timer_init_done = 1;
-
 	return 0;
 }
-
-static u16 read_pit(void)
-{
-	u8 low;
-
-	outb(PIT_CMD_LATCH, PIT_BASE + PIT_COMMAND);
-	low = inb(PIT_BASE + PIT_T0);
-
-	return (inb(PIT_BASE + PIT_T0) << 8) | low;
-}
-
-/* this is not very exact */
-void __udelay(unsigned long usec)
-{
-	int counter;
-	int wraps;
-
-	if (timer_init_done) {
-		counter = read_pit();
-		wraps = usec / 1000;
-		usec = usec % 1000;
-
-		usec *= 1194;
-		usec /= 1000;
-		usec += counter;
-
-		while (usec > 1194) {
-			usec -= 1194;
-			wraps++;
-		}
-
-		while (1) {
-			int new_count = read_pit();
-
-			if (((new_count < usec) && !wraps) || wraps < 0)
-				break;
-
-			if (new_count > counter)
-				wraps--;
-
-			counter = new_count;
-		}
-	}
-
-}
diff --git a/arch/x86/lib/timer.c b/arch/x86/lib/timer.c
deleted file mode 100644
index fb11bfe..0000000
--- a/arch/x86/lib/timer.c
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * (C) Copyright 2008,2009
- * Graeme Russ, <graeme.russ at gmail.com>
- *
- * (C) Copyright 2002
- * Daniel Engström, Omicron Ceti AB, <daniel at omicron.se>
- *
- * See file CREDITS for list of people who contributed to this
- * project.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
- * MA 02111-1307 USA
- */
-
-#include <common.h>
-#include <malloc.h>
-#include <asm/io.h>
-#include <asm/i8254.h>
-#include <asm/ibmpc.h>
-
-DECLARE_GLOBAL_DATA_PTR;
-
-struct timer_isr_function {
-	struct timer_isr_function *next;
-	timer_fnc_t *isr_func;
-};
-
-static struct timer_isr_function *first_timer_isr;
-static unsigned long system_ticks;
-
-/*
- * register_timer_isr() allows multiple architecture and board specific
- * functions to be called every millisecond. Keep the execution time of
- * each function as low as possible
- */
-int register_timer_isr(timer_fnc_t *isr_func)
-{
-	struct timer_isr_function *new_func;
-	struct timer_isr_function *temp;
-	int flag;
-
-	new_func = malloc(sizeof(struct timer_isr_function));
-
-	if (new_func == NULL)
-		return 1;
-
-	new_func->isr_func = isr_func;
-	new_func->next = NULL;
-
-	/*
-	 *  Don't allow timer interrupts while the
-	 *  linked list is being modified
-	 */
-	flag = disable_interrupts();
-
-	if (first_timer_isr == NULL) {
-		first_timer_isr = new_func;
-	} else {
-		temp = first_timer_isr;
-		while (temp->next != NULL)
-			temp = temp->next;
-		temp->next = new_func;
-	}
-
-	if (flag)
-		enable_interrupts();
-
-	return 0;
-}
-
-/*
- * timer_isr() MUST be the registered interrupt handler for
- */
-void timer_isr(void *unused)
-{
-	struct timer_isr_function *temp = first_timer_isr;
-
-	system_ticks++;
-
-	/* Execute each registered function */
-	while (temp != NULL) {
-		temp->isr_func();
-		temp = temp->next;
-	}
-}
-
-ulong get_timer(ulong base)
-{
-	return system_ticks - base;
-}
-
-void timer_set_tsc_base(uint64_t new_base)
-{
-	gd->arch.tsc_base = new_base;
-}
-
-uint64_t timer_get_tsc(void)
-{
-	uint64_t time_now;
-
-	time_now = rdtsc();
-	if (!gd->arch.tsc_base)
-		gd->arch.tsc_base = time_now;
-
-	return time_now - gd->arch.tsc_base;
-}
diff --git a/arch/x86/lib/tsc_timer.c b/arch/x86/lib/tsc_timer.c
new file mode 100644
index 0000000..c509801
--- /dev/null
+++ b/arch/x86/lib/tsc_timer.c
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2012 The Chromium OS Authors.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <common.h>
+#include <malloc.h>
+#include <asm/io.h>
+#include <asm/i8254.h>
+#include <asm/ibmpc.h>
+#include <asm/msr.h>
+#include <asm/u-boot-x86.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+void timer_set_base(u64 base)
+{
+	gd->arch.tsc_base = base;
+}
+
+/*
+ * Get the number of CPU time counter ticks since it was read first time after
+ * restart. This yields a free running counter guaranteed to take almost 6
+ * years to wrap around even at 100GHz clock rate.
+ */
+u64 get_ticks(void)
+{
+	u64 now_tick = rdtsc();
+
+	/* We assume that 0 means the base hasn't been set yet */
+	if (!gd->arch.tsc_base)
+		panic("No tick base available");
+	return now_tick - gd->arch.tsc_base;
+}
+
+#define PLATFORM_INFO_MSR 0xce
+
+/* Get the speed of the TSC timer in MHz */
+unsigned long get_tbclk_mhz(void)
+{
+	u32 ratio;
+	u64 platform_info = native_read_msr(PLATFORM_INFO_MSR);
+
+	/* 100MHz times Max Non Turbo ratio */
+	ratio = (platform_info >> 8) & 0xff;
+	return 100 * ratio;
+}
+
+unsigned long get_tbclk(void)
+{
+	return get_tbclk_mhz() * 1000 * 1000;
+}
+
+static ulong get_ms_timer(void)
+{
+	return (get_ticks() * 1000) / get_tbclk();
+}
+
+ulong get_timer(ulong base)
+{
+	return get_ms_timer() - base;
+}
+
+ulong timer_get_us(void)
+{
+	return get_ticks() / get_tbclk_mhz();
+}
+
+ulong timer_get_boot_us(void)
+{
+	return timer_get_us();
+}
+
+void __udelay(unsigned long usec)
+{
+	u64 now = get_ticks();
+	u64 stop;
+
+	stop = now + usec * get_tbclk_mhz();
+
+	while ((int64_t)(stop - get_ticks()) > 0)
+		;
+}
+
+int timer_init(void)
+{
+#ifdef CONFIG_SYS_PCAT_TIMER
+	/* Set up the PCAT timer if required */
+	pcat_timer_init();
+#endif
+
+	return 0;
+}
diff --git a/include/configs/coreboot.h b/include/configs/coreboot.h
index 5bacc77..7953ec6 100644
--- a/include/configs/coreboot.h
+++ b/include/configs/coreboot.h
@@ -38,7 +38,6 @@
 #define CONFIG_SHOW_BOOT_PROGRESS
 #define CONFIG_LAST_STAGE_INIT
 #define CONFIG_SYS_VSNPRINTF
-#define CONFIG_INTEL_CORE_ARCH	/* Sandy bridge and ivy bridge chipsets. */
 #define CONFIG_ZBOOT_32
 #define CONFIG_PHYSMEM
 #define CONFIG_SYS_EARLY_PCI_INIT
@@ -218,7 +217,6 @@
 #define CONFIG_SYS_MEMTEST_END			0x01000000
 #define CONFIG_SYS_LOAD_ADDR			0x100000
 #define CONFIG_SYS_HZ				1000
-#define CONFIG_SYS_X86_ISR_TIMER
 
 /*-----------------------------------------------------------------------
  * SDRAM Configuration
@@ -235,8 +233,9 @@
  * CPU Features
  */
 
-#define CONFIG_SYS_GENERIC_TIMER
+#define CONFIG_SYS_X86_TSC_TIMER
 #define CONFIG_SYS_PCAT_INTERRUPTS
+#define CONFIG_SYS_PCAT_TIMER
 #define CONFIG_SYS_NUM_IRQS			16
 
 /*-----------------------------------------------------------------------
-- 
1.8.2.1



More information about the U-Boot mailing list