[U-Boot] [PATCH v5 2/2] timer: Add High Precision Event Timers (HPET) support
Andy Shevchenko
andriy.shevchenko at linux.intel.com
Fri Apr 6 19:39:35 UTC 2018
On Fri, 2018-04-06 at 12:18 -0700, Ivan Gorinov 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";
> };
FWIW,
Reviewed-by: Andy Shevchenko <andriy.shevchenko at linux.intel.com>
>
> Signed-off-by: Ivan Gorinov <ivan.gorinov at 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"
> + depends on X86
> + 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 at 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. */
> +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. */
> +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
> +
> /* 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,
> };
--
Andy Shevchenko <andriy.shevchenko at linux.intel.com>
Intel Finland Oy
More information about the U-Boot
mailing list