Patchwork [U-Boot,01/15] x86: Add function to read time stamp counter

login
register
mail settings
Submitter Graeme Russ
Date Oct. 24, 2012, 4:24 a.m.
Message ID <CALButCKm7shP-N9m3OUmMALo06q6qF1dh2K2UO6X9K_VynbsdQ@mail.gmail.com>
Download mbox | patch
Permalink /patch/193673/
State Not Applicable
Delegated to: Graeme Russ
Headers show

Comments

Graeme Russ - Oct. 24, 2012, 4:24 a.m.
Hi Simon,

On Wed, Oct 24, 2012 at 3:04 PM, Simon Glass <sjg@chromium.org> wrote:
> From: Vadim Bendebury <vbendeb@chromium.org>
>
> Put this function in the u-boot-x86.h header file. We could instead create
> timer.h perhaps.
>
> We support setting a base time, and reading the time relative to this base.
>
> Signed-off-by: Vadim Bendebury <vbendeb@chromium.org>
> Signed-off-by: Stefan Reinauer <reinauer@chromium.org>
> Signed-off-by: Simon Glass <sjg@chromium.org>
> ---
>  arch/x86/include/asm/u-boot-x86.h |   12 ++++++++++++
>  arch/x86/lib/timer.c              |   17 +++++++++++++++++
>  2 files changed, 29 insertions(+), 0 deletions(-)
>
> diff --git a/arch/x86/include/asm/u-boot-x86.h b/arch/x86/include/asm/u-boot-x86.h
> index a4a5ae0..11be5c3 100644
> --- a/arch/x86/include/asm/u-boot-x86.h
> +++ b/arch/x86/include/asm/u-boot-x86.h
> @@ -68,4 +68,16 @@ int video_init(void);
>  void   board_init_f_r_trampoline(ulong) __attribute__ ((noreturn));
>  void   board_init_f_r(void) __attribute__ ((noreturn));
>
> +/* Read the time stamp counter */
> +static inline uint64_t rdtsc(void)
> +{
> +       uint32_t high, low;
> +       __asm__ __volatile__("rdtsc" : "=a" (low), "=d" (high));
> +       return (((uint64_t)high) << 32) | low;
> +}

What CPUs support the rdtsc opcode? I know the AMD SC520 does not
(it's a 486 core). Wikipedia tells me the Pentium CPU was the first to
implement it. The Pentium was released in March 1993, so rdtsc is
nearly 20 years now. But not all non-Intel CPUs are guaranteed to
support it. Oh, and for CPUs that change speed, the internal clock
read by rdtsc may not be constant.

I'm happy to draw a line in the sand and say 'x86 CPUs before
xx/xx/xxx are not supported' - If someone comes along later wanting to
add support, we can mess with the plumbing then (the likelyhood is
practically zero, so I'm not too concerned about how hard that will
be). Given that, perhaps we should look at HPET instead? I have a
patch I worked on 6 month ago (attached below) if you feel like using
it

> +
> +/* board/... */
> +void timer_set_tsc_base(uint64_t new_base);
> +uint64_t timer_get_tsc(void);
> +
>  #endif /* _U_BOOT_I386_H_ */
> diff --git a/arch/x86/lib/timer.c b/arch/x86/lib/timer.c
> index fd7032e..a13424b 100644
> --- a/arch/x86/lib/timer.c
> +++ b/arch/x86/lib/timer.c
> @@ -37,6 +37,7 @@ struct timer_isr_function {
>
>  static struct timer_isr_function *first_timer_isr;
>  static unsigned long system_ticks;
> +static uint64_t base_value;
>
>  /*
>   * register_timer_isr() allows multiple architecture and board specific
> @@ -98,3 +99,19 @@ ulong get_timer(ulong base)
>  {
>         return system_ticks - base;
>  }
> +
> +void timer_set_tsc_base(uint64_t new_base)
> +{
> +       base_value = new_base;
> +}
> +
> +uint64_t timer_get_tsc(void)
> +{
> +       uint64_t time_now;
> +
> +       time_now = rdtsc();
> +       if (!base_value)
> +               base_value = time_now;
> +
> +       return time_now - base_value;
> +}
> --
> 1.7.7.3
>

Here is the HPET driver patch:

---
 Makefile               |    1 +
 drivers/timer/Makefile |   46 ++++++++++++++++++++++
 drivers/timer/hpet.c   |  101 ++++++++++++++++++++++++++++++++++++++++++++++++
 include/hpet.h         |   69 ++++++++++++++++++++++++++++++++
 4 files changed, 217 insertions(+), 0 deletions(-)
 create mode 100644 drivers/timer/Makefile
 create mode 100644 drivers/timer/hpet.c
 create mode 100644 include/hpet.h
Simon Glass - Dec. 1, 2012, 8:52 p.m.
Hi Graeme,

On Tue, Oct 23, 2012 at 9:24 PM, Graeme Russ <graeme.russ@gmail.com> wrote:
> Hi Simon,
>
> On Wed, Oct 24, 2012 at 3:04 PM, Simon Glass <sjg@chromium.org> wrote:
>> From: Vadim Bendebury <vbendeb@chromium.org>
>>
>> Put this function in the u-boot-x86.h header file. We could instead create
>> timer.h perhaps.
>>
>> We support setting a base time, and reading the time relative to this base.
>>
>> Signed-off-by: Vadim Bendebury <vbendeb@chromium.org>
>> Signed-off-by: Stefan Reinauer <reinauer@chromium.org>
>> Signed-off-by: Simon Glass <sjg@chromium.org>
>> ---
>>  arch/x86/include/asm/u-boot-x86.h |   12 ++++++++++++
>>  arch/x86/lib/timer.c              |   17 +++++++++++++++++
>>  2 files changed, 29 insertions(+), 0 deletions(-)
>>
>> diff --git a/arch/x86/include/asm/u-boot-x86.h b/arch/x86/include/asm/u-boot-x86.h
>> index a4a5ae0..11be5c3 100644
>> --- a/arch/x86/include/asm/u-boot-x86.h
>> +++ b/arch/x86/include/asm/u-boot-x86.h
>> @@ -68,4 +68,16 @@ int video_init(void);
>>  void   board_init_f_r_trampoline(ulong) __attribute__ ((noreturn));
>>  void   board_init_f_r(void) __attribute__ ((noreturn));
>>
>> +/* Read the time stamp counter */
>> +static inline uint64_t rdtsc(void)
>> +{
>> +       uint32_t high, low;
>> +       __asm__ __volatile__("rdtsc" : "=a" (low), "=d" (high));
>> +       return (((uint64_t)high) << 32) | low;
>> +}
>
> What CPUs support the rdtsc opcode? I know the AMD SC520 does not
> (it's a 486 core). Wikipedia tells me the Pentium CPU was the first to
> implement it. The Pentium was released in March 1993, so rdtsc is
> nearly 20 years now. But not all non-Intel CPUs are guaranteed to
> support it. Oh, and for CPUs that change speed, the internal clock
> read by rdtsc may not be constant.
>
> I'm happy to draw a line in the sand and say 'x86 CPUs before
> xx/xx/xxx are not supported' - If someone comes along later wanting to
> add support, we can mess with the plumbing then (the likelyhood is
> practically zero, so I'm not too concerned about how hard that will
> be). Given that, perhaps we should look at HPET instead? I have a
> patch I worked on 6 month ago (attached below) if you feel like using
> it

OK - well it seems that HPET is *very* new in comparison. Even so, it
seems to me that it should be safe to move to this since we are
probably not that interested in old hardware, but perhaps we should do
this in the next release to give us a chance to test, etc.?

Regards,
Simon

Patch

diff --git a/Makefile b/Makefile
index 4ddf8d6..c7b5245 100644
--- a/Makefile
+++ b/Makefile
@@ -285,6 +285,7 @@  LIBS += arch/powerpc/cpu/mpc8xxx/lib8xxx.o
 endif
 LIBS += drivers/rtc/librtc.o
 LIBS += drivers/serial/libserial.o
+LIBS += drivers/timer/libtimer.o
 ifeq ($(CONFIG_GENERIC_LPC_TPM),y)
 LIBS += drivers/tpm/libtpm.o
 endif
diff --git a/drivers/timer/Makefile b/drivers/timer/Makefile
new file mode 100644
index 0000000..a8b076a
--- /dev/null
+++ b/drivers/timer/Makefile
@@ -0,0 +1,46 @@ 
+#
+# (C) Copyright 2000-2007
+# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+#
+# 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 $(TOPDIR)/config.mk
+
+LIB	:= $(obj)libtimer.o
+
+COBJS-$(CONFIG_HPET_TIMER) += hpet.o
+
+COBJS	:= $(COBJS-y)
+SRCS	:= $(COBJS:.o=.c)
+OBJS	:= $(addprefix $(obj),$(COBJS))
+
+all:	$(LIB)
+
+$(LIB):	$(obj).depend $(OBJS)
+	$(call cmd_link_o_target, $(OBJS))
+
+#########################################################################
+
+# defines $(obj).depend target
+include $(SRCTREE)/rules.mk
+
+sinclude $(obj).depend
+
+#########################################################################
diff --git a/drivers/timer/hpet.c b/drivers/timer/hpet.c
new file mode 100644
index 0000000..b25f509
--- /dev/null
+++ b/drivers/timer/hpet.c
@@ -0,0 +1,101 @@ 
+/*
+ * (C) Copyright 2011
+ * Graeme Russ, <graeme.russ@gmail.com>
+ *
+ * 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
+ */
+
+/* High Precision Event Timers (HPET) */
+
+#include <common.h>
+#include <asm/io.h>
+#include <hpet.h>
+#include <div64.h>
+
+static struct hpet_regs *hpet_registers = (struct hpet_regs *)HPET_BASE_ADDR;
+
+int hpet_enable(void)
+{
+	u8 hpet8_val;
+
+	hpet8_val = readb(&hpet_registers->general_config);
+	hpet8_val |= HPET_ENABLE;
+	writeb(hpet8_val, &hpet_registers->general_config);
+
+	return 0;
+}
+
+void hpet_udelay(unsigned long usec)
+{
+	u32 count_low;
+	u32 count_high;
+	u32 fs_per_tick;
+	u64 fs_to_wait = (u64)usec * 1000000000;
+	u64 ticks_to_wait;
+	u64 end_count;
+
+	u32 end_count_low;
+	u32 end_count_high;
+
+	count_low = readl(&hpet_registers->main_count_low);
+	count_high = readl(&hpet_registers->main_count_high);
+	fs_per_tick = readl(&hpet_registers->counter_clk_period);
+
+	ticks_to_wait = lldiv(fs_to_wait, fs_per_tick);
+
+	end_count = ((u64)count_high << 32) | ((u64)count_low);
+	end_count += ticks_to_wait;
+
+	end_count_low = (u32)(end_count & 0x00000000ffffffffULL);
+	end_count_high = (u32)((end_count >> 32) & 0x00000000ffffffffULL);
+
+	while (1) {
+		count_low = readl(&hpet_registers->main_count_low);
+		count_high = readl(&hpet_registers->main_count_high);
+
+		if ((count_high > end_count_high) ||
+		    ((count_high == end_count_high) &&
+		     (count_low > end_count_low)))
+			break;
+	}
+}
+
+ulong hpet_get_timer(ulong base)
+{
+	u32 count_low;
+	u32 count_high;
+	u32 fs_per_tick;
+	u64 ticks;
+	u64 fs;
+	u32 ms;
+
+	count_low = readl(&hpet_registers->main_count_low);
+	count_high = readl(&hpet_registers->main_count_high);
+	fs_per_tick = readl(&hpet_registers->counter_clk_period);
+
+	ticks = ((u64)count_high << 32) | ((u64)count_low);
+
+	fs = fs_per_tick * ticks;
+
+	/* Allow a 64/32 bit division by dividing by 4096 */
+	ms = (u32)(lldiv(fs, 244140625) >> 12);
+
+	return ms - base;
+}
+
diff --git a/include/hpet.h b/include/hpet.h
new file mode 100644
index 0000000..18769d0
--- /dev/null
+++ b/include/hpet.h
@@ -0,0 +1,69 @@ 
+/*
+ * (C) Copyright 2011
+ * Graeme Russ, <graeme.russ@gmail.com>
+ *
+ * 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
+ */
+
+/* High Precision Event Timers (HPET) */
+
+#ifndef __HPET_H__
+#define __HPET_H__
+#include <common.h>
+
+struct hpet_timer_regs {
+	u8 timer0_caps;
+	u8 timer0_cnf;
+	u16 timer0_reserved;
+	u32 timer0_int_route_cap;
+	u32 comparator_value;
+	u32 reserved_comparator_value;
+	u32 fsb_int_val;
+	u32 fsb_int_addr;
+	u8 reserved018[8];
+};
+
+struct hpet_regs {
+	u8 rev_id;
+	u8 general_caps;
+	u16 vendor_id;
+	u32 counter_clk_period;
+	u8 reserved008[8];
+	u8 general_config;
+	u8 reserved_manufacturer[3];
+	u8 reserved018[8];
+	u8 general_interupt_status;
+	u8 reserved_gen_int_sts[7];
+	u8 reserved028[200];
+	u32 main_count_high;
+	u32 main_count_low;
+	u8 reserved0f8[8];
+	struct hpet_timer_regs timer_regs[3];
+	u8 reserved160[672];
+};
+
+int hpet_dump_info(void);
+int hpet_enable(void);
+void hpet_udelay(unsigned long usec);
+ulong hpet_get_timer(ulong base);
+
+#define HPET_BASE_ADDR	0xfed00000
+#define HPET_ENABLE	0x01
+
+#endif