Hi Ivan, On Sat, Apr 7, 2018 at 3:18 AM, Ivan Gorinov <ivan.gori...@intel.com> wrote: > Add HPET driver as an alternative timer for x86 (default is TSC). > HPET counter has constant frequency and does not need calibration. > This change also makes TSC timer driver optional on x86. > New HPET driver can also be selected as the early timer on x86. > > HPET can be selected as the tick timer in the Device Tree "chosen" node: > > /include/ "hpet.dtsi" > > ... > > chosen { > tick-timer = "/hpet"; > }; > > Signed-off-by: Ivan Gorinov <ivan.gori...@intel.com> > --- > arch/Kconfig | 2 +- > arch/x86/Kconfig | 21 ++++++ > arch/x86/dts/hpet.dtsi | 7 ++ > drivers/timer/Kconfig | 9 +++ > drivers/timer/Makefile | 1 + > drivers/timer/hpet_timer.c | 179 > +++++++++++++++++++++++++++++++++++++++++++++ > drivers/timer/tsc_timer.c | 8 ++ > 7 files changed, 226 insertions(+), 1 deletion(-) > create mode 100644 arch/x86/dts/hpet.dtsi > create mode 100644 drivers/timer/hpet_timer.c > > diff --git a/arch/Kconfig b/arch/Kconfig > index e599e7a..37dabae 100644 > --- a/arch/Kconfig > +++ b/arch/Kconfig > @@ -104,7 +104,7 @@ config X86 > select DM_PCI > select PCI > select TIMER > - select X86_TSC_TIMER > + imply X86_TSC_TIMER > imply BLK > imply DM_ETH > imply DM_GPIO > diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig > index 5c23b2c..2fe5b6a 100644 > --- a/arch/x86/Kconfig > +++ b/arch/x86/Kconfig > @@ -119,6 +119,27 @@ source "arch/x86/cpu/tangier/Kconfig" > > # architecture-specific options below > > +choice > + prompt "Select which timer to use early on x86"
nits: "on x86" is not needed since this option is only visible in the x86 sub-menu > + depends on X86 This depends is not necessary as this option is already included in arch/x86/Kconfig. But it should really depend on CONFIG_TIMER_EARLY > + default X86_EARLY_TIMER_TSC > + > +config X86_EARLY_TIMER_TSC > + bool "TSC" > + depends on X86_TSC_TIMER > + help > + This selects x86 Time Stamp Counter (TSC) as the early timer. > + See CONFIG_TIMER_EARLY for the early timer description. > + > +config X86_EARLY_TIMER_HPET > + bool "HPET" > + depends on HPET_TIMER > + help > + This selects High Precision Event Timers as the early timer. > + Early HPET base address is specified by CONFIG_HPET_ADDRESS. > + > +endchoice > + > config AHCI > default y > > diff --git a/arch/x86/dts/hpet.dtsi b/arch/x86/dts/hpet.dtsi > new file mode 100644 > index 0000000..a74f739 > --- /dev/null > +++ b/arch/x86/dts/hpet.dtsi > @@ -0,0 +1,7 @@ > +/ { > + hpet: hpet@fed00000 { > + compatible = "hpet-x86"; > + u-boot,dm-pre-reloc; > + reg = <0xfed00000 0x1000>; > + }; > +}; > diff --git a/drivers/timer/Kconfig b/drivers/timer/Kconfig > index 2c96896..26743b7 100644 > --- a/drivers/timer/Kconfig > +++ b/drivers/timer/Kconfig > @@ -65,6 +65,15 @@ config X86_TSC_TIMER > help > Select this to enable Time-Stamp Counter (TSC) timer for x86. > > +config HPET_TIMER > + bool "High Precision Event Timers (HPET) support" > + depends on TIMER > + default y if X86 > + help > + Select this to enable High Precision Event Timers (HPET) on x86. > + HPET main counter increments at constant rate and does not need > + calibration. > + > config OMAP_TIMER > bool "Omap timer support" > depends on TIMER > diff --git a/drivers/timer/Makefile b/drivers/timer/Makefile > index a6e7832..557fecc 100644 > --- a/drivers/timer/Makefile > +++ b/drivers/timer/Makefile > @@ -8,6 +8,7 @@ obj-y += timer-uclass.o > obj-$(CONFIG_ALTERA_TIMER) += altera_timer.o > obj-$(CONFIG_SANDBOX_TIMER) += sandbox_timer.o > obj-$(CONFIG_X86_TSC_TIMER) += tsc_timer.o > +obj-$(CONFIG_HPET_TIMER) += hpet_timer.o > obj-$(CONFIG_OMAP_TIMER) += omap-timer.o > obj-$(CONFIG_AST_TIMER) += ast_timer.o > obj-$(CONFIG_STI_TIMER) += sti-timer.o > diff --git a/drivers/timer/hpet_timer.c b/drivers/timer/hpet_timer.c > new file mode 100644 > index 0000000..b1ce226 > --- /dev/null > +++ b/drivers/timer/hpet_timer.c > @@ -0,0 +1,179 @@ > +/* > + * Copyright (c) 2017 Intel Corporation > + * > + * SPDX-License-Identifier: GPL-2.0+ > + */ > + > +#include <common.h> > +#include <dm.h> > +#include <malloc.h> > +#include <timer.h> > +#include <asm/cpu.h> > +#include <asm/io.h> > +#include <asm/u-boot-x86.h> > + > +#define HPET_PERIOD_REG 0x004 > +#define HPET_CONFIG_REG 0x010 > +#define HPET_MAIN_COUNT 0x0f0 > + > +#define ENABLE_CNF 1 > + > +#define HPET_MAX_PERIOD 100000000 > + > +struct hpet_timer_priv { > + void *regs; > +}; > + > +/* > + * Returns HPET clock frequency in HZ > + * (rounding to the nearest integer), > + * or 0 if HPET is not available. > + */ > +static inline u32 get_clock_frequency(void *regs) > +{ > + u64 d = 1000000000000000ull; > + u32 period; > + > + period = readl(regs + HPET_PERIOD_REG); > + if (period == 0) > + return 0; > + if (period > HPET_MAX_PERIOD) > + return 0; > + > + d += period / 2; > + > + return d / period; > +} > + > +/* Reset and start the main counter. */ nits: remove the ending . > +static void start_main_counter(void *regs) > +{ > + u32 config; > + > + config = readl(regs + HPET_CONFIG_REG); > + config &= ~ENABLE_CNF; > + writel(config, regs + HPET_CONFIG_REG); > + writeq(0, regs + HPET_MAIN_COUNT); > + config |= ENABLE_CNF; > + writel(config, regs + HPET_CONFIG_REG); > +} > + > +/* Read the main counter, repeat if 32-bit rollover happens. */ nits: remove the ending . > +static u64 read_main_counter(void *regs) > +{ > + u64 t, t0; > + > + t = readq(regs + HPET_MAIN_COUNT); > + do { > + t0 = t; > + t = readq(regs + HPET_MAIN_COUNT); > + } while ((t >> 32) != (t0 >> 32)); > + > + return t; > +} > + > +static int hpet_timer_get_count(struct udevice *dev, u64 *count) > +{ > + struct hpet_timer_priv *priv = dev_get_priv(dev); > + > + *count = read_main_counter(priv->regs); > + > + return 0; > +} > + > +static int hpet_timer_probe(struct udevice *dev) > +{ > + struct timer_dev_priv *uc_priv = dev_get_uclass_priv(dev); > + struct hpet_timer_priv *priv = dev_get_priv(dev); > + u32 rate; > + > + rate = get_clock_frequency(priv->regs); > + if (rate == 0) > + return -ENODEV; > + > + start_main_counter(priv->regs); > + > + uc_priv->clock_rate = rate; > + > + return 0; > +} > + > +#ifdef CONFIG_X86_EARLY_TIMER_HPET > + > +static void *early_regs = (void *)CONFIG_HPET_ADDRESS; > + > +unsigned long notrace timer_early_get_rate(void) > +{ > + return get_clock_frequency(early_regs); > +} > + > +u64 notrace timer_early_get_count(void) > +{ > + return read_main_counter(early_regs); > +} > + > +int timer_init(void) > +{ > + if (get_clock_frequency(early_regs) == 0) > + return -ENODEV; > + > + start_main_counter(early_regs); > + > + return 0; > +} > + > +ulong timer_get_boot_us(void) > +{ > + u32 period; > + u64 d; > + > + period = readl(early_regs + HPET_PERIOD_REG); > + if (period == 0) > + return 0; > + if (period > HPET_MAX_PERIOD) > + return 0; > + > + d = read_main_counter(early_regs); > + > + /* > + * Multiplication overflow > + * at 2^64 femtoseconds > + * (more than 5 hours) > + */ > + > + d *= period; > + > + return d / 1000000000; > +} > + > +#endif /* CONFIG_X86_EARLY_TIMER_HPET */ > + > +static int hpet_timer_ofdata_to_platdata(struct udevice *dev) > +{ > + struct hpet_timer_priv *priv = dev_get_priv(dev); > + > + priv->regs = map_physmem(devfdt_get_addr(dev), 0x1000, MAP_NOCACHE); > + > + return 0; > +} > + > +static const struct timer_ops hpet_timer_ops = { > + .get_count = hpet_timer_get_count, > +}; > + > +static const struct udevice_id hpet_timer_ids[] = { > + { .compatible = "hpet-x86", }, > + { .compatible = "intel,ce4100-hpet", }, > + { } > +}; > + > +U_BOOT_DRIVER(hpet_timer) = { > + .name = "hpet_timer", > + .id = UCLASS_TIMER, > + .of_match = hpet_timer_ids, > + .ofdata_to_platdata = hpet_timer_ofdata_to_platdata, > + .priv_auto_alloc_size = sizeof(struct hpet_timer_priv), > + .probe = hpet_timer_probe, > + .ops = &hpet_timer_ops, > + .flags = DM_FLAG_PRE_RELOC, > +}; > diff --git a/drivers/timer/tsc_timer.c b/drivers/timer/tsc_timer.c > index 9296de6..bd0e75c 100644 > --- a/drivers/timer/tsc_timer.c > +++ b/drivers/timer/tsc_timer.c > @@ -277,6 +277,8 @@ success: > return delta / 1000; > } > > +#ifdef CONFIG_X86_EARLY_TIMER_TSC Why do we surround the following APIs with CONFIG_X86_EARLY_TIMER_TSC? These APIs are generic U-Boot timer APIs. If we select CONFIG_X86_EARLY_TIMER_HPET, these APIs are not available and will cause build error. Simon, do you think we should fix such in the timer uclass driver? > + > /* Get the speed of the TSC timer in MHz */ > unsigned notrace long get_tbclk_mhz(void) > { > @@ -322,6 +324,8 @@ void __udelay(unsigned long usec) > #endif > } > > +#endif /* CONFIG_X86_EARLY_TIMER_TSC */ > + > static int tsc_timer_get_count(struct udevice *dev, u64 *count) > { > u64 now_tick = rdtsc(); > @@ -365,6 +369,8 @@ static int tsc_timer_probe(struct udevice *dev) > return 0; > } > > +#ifdef CONFIG_X86_EARLY_TIMER_TSC > + > unsigned long notrace timer_early_get_rate(void) > { > tsc_timer_ensure_setup(); > @@ -377,6 +383,8 @@ u64 notrace timer_early_get_count(void) > return rdtsc() - gd->arch.tsc_base; > } > > +#endif > + > static const struct timer_ops tsc_timer_ops = { > .get_count = tsc_timer_get_count, > }; > -- Regards, Bin _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de https://lists.denx.de/listinfo/u-boot