From patchwork Wed Oct 24 04:24:24 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Graeme Russ X-Patchwork-Id: 193673 X-Patchwork-Delegate: graeme.russ@gmail.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from theia.denx.de (theia.denx.de [85.214.87.163]) by ozlabs.org (Postfix) with ESMTP id 6EBB12C008A for ; Wed, 24 Oct 2012 15:24:34 +1100 (EST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 0FBB04A4AC; Wed, 24 Oct 2012 06:24:33 +0200 (CEST) X-Virus-Scanned: Debian amavisd-new at theia.denx.de Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id DZoY93L5uWBx; Wed, 24 Oct 2012 06:24:32 +0200 (CEST) Received: from theia.denx.de (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id E316F4A4AF; Wed, 24 Oct 2012 06:24:30 +0200 (CEST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 2DB9A4A4AF for ; Wed, 24 Oct 2012 06:24:29 +0200 (CEST) X-Virus-Scanned: Debian amavisd-new at theia.denx.de Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id QdS7X+odINKr for ; Wed, 24 Oct 2012 06:24:28 +0200 (CEST) X-policyd-weight: NOT_IN_SBL_XBL_SPAMHAUS=-1.5 NOT_IN_SPAMCOP=-1.5 NOT_IN_BL_NJABL=-1.5 (only DNSBL check requested) Received: from mail-ie0-f172.google.com (mail-ie0-f172.google.com [209.85.223.172]) by theia.denx.de (Postfix) with ESMTPS id 868994A4AC for ; Wed, 24 Oct 2012 06:24:26 +0200 (CEST) Received: by mail-ie0-f172.google.com with SMTP id 9so149666iec.3 for ; Tue, 23 Oct 2012 21:24:25 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=mime-version:in-reply-to:references:date:message-id:subject:from:to :cc:content-type; bh=s15EmN2ZH6T6m7IJRnEGBfWxEOadlIGAyntnQTFo4Mw=; b=EwciMq9xreKoKpJYv5XOXIWw7cliZWEVA39HCF2tttuhX/lS7sxno6P1kkQpnhLyt8 l3ngF56sxzpistafFffoPnDUE3KHMxR5Zaft/Cw3KS0UhitrW54S4x7qEo9opbQm9hEf qTZttMqovfvAe2F5fas3ZmAsVvGUb2zVwACeqeKS0+5EQGM4UmUaV3fhKxw3IlWm3k8k tBtCQ/nv6BwFLKCfvgS9BlJfB0AlzhNe4RxSj8EvTgnF6CTvJwoUtmJBzcP0Pf9zKIgV dE0Np6h3AXF3ydp018nB9i7NXugaz4kkCrPLbyj/+icg+4SliHQcFNSpC6MhLDE8RzP2 13IQ== MIME-Version: 1.0 Received: by 10.50.171.5 with SMTP id aq5mr1308173igc.36.1351052664852; Tue, 23 Oct 2012 21:24:24 -0700 (PDT) Received: by 10.50.10.199 with HTTP; Tue, 23 Oct 2012 21:24:24 -0700 (PDT) In-Reply-To: <1351051486-6980-2-git-send-email-sjg@chromium.org> References: <1351051486-6980-1-git-send-email-sjg@chromium.org> <1351051486-6980-2-git-send-email-sjg@chromium.org> Date: Wed, 24 Oct 2012 15:24:24 +1100 Message-ID: From: Graeme Russ To: Simon Glass Cc: U-Boot Mailing List , Vadim Bendebury , Stefan Reinauer Subject: Re: [U-Boot] [PATCH 01/15] x86: Add function to read time stamp counter X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.11 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: u-boot-bounces@lists.denx.de Errors-To: u-boot-bounces@lists.denx.de Hi Simon, On Wed, Oct 24, 2012 at 3:04 PM, Simon Glass wrote: > From: Vadim Bendebury > > 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 > Signed-off-by: Stefan Reinauer > Signed-off-by: Simon Glass > --- > 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 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, + * + * 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 +#include +#include +#include + +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, + * + * 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 + +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