[U-Boot] [PATCH v2] timer: add High Precision Event Timers (HPET) support
Bin Meng
bmeng.cn at gmail.com
Fri Mar 30 09:52:38 UTC 2018
Hi Ivan,
On Fri, Mar 30, 2018 at 6:29 AM, Ivan Gorinov <ivan.gorinov at intel.com> wrote:
> Adding HPET 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.
> If X86_TSC is disabled, early timer functions are provided by HPET.
>
> Signed-off-by: Ivan Gorinov <ivan.gorinov at intel.com>
> ---
> arch/Kconfig | 2 +-
> arch/x86/dts/hpet.dtsi | 7 ++
> drivers/timer/Kconfig | 6 ++
> drivers/timer/Makefile | 1 +
> drivers/timer/hpet_timer.c | 191 +++++++++++++++++++++++++++++++++++++++++++++
> 5 files changed, 206 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/dts/hpet.dtsi b/arch/x86/dts/hpet.dtsi
> new file mode 100644
> index 0000000..037dc7e
> --- /dev/null
> +++ b/arch/x86/dts/hpet.dtsi
> @@ -0,0 +1,7 @@
> +/ {
> + hpet0: hpet at fed00000 {
nits: use 'hpet' instead of 'hpet0'? since there is only one hpet
timer in the system.
> + compatible = "hpet-x86";
> + u-boot,dm-pre-reloc;
> + reg = <0xfed00000 0x1000>;
> + };
> +};
> diff --git a/drivers/timer/Kconfig b/drivers/timer/Kconfig
> index 2c96896..72ae6c5 100644
> --- a/drivers/timer/Kconfig
> +++ b/drivers/timer/Kconfig
> @@ -65,6 +65,12 @@ 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 && X86
> + help
> + Select this to enable High Precision Event Timers (HPET) for x86.
> +
> 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..0bef859
> --- /dev/null
> +++ b/drivers/timer/hpet_timer.c
> @@ -0,0 +1,191 @@
> +/*
> + * 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_L 0x0f0
> +#define HPET_MAIN_COUNT_H 0x0f4
> +
> +#define HPET_MAX_PERIOD 100000000
> +
> +DECLARE_GLOBAL_DATA_PTR;
This is not needed.
> +
> +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: use one line comment format
> +static void start_main_counter(void *regs)
> +{
> + u32 config;
> +
> + config = readl(regs + HPET_CONFIG_REG);
> + config &= ~1;
> + writel(config, regs + HPET_CONFIG_REG);
> + writel(0, regs + HPET_MAIN_COUNT_L);
> + writel(0, regs + HPET_MAIN_COUNT_H);
> + config |= 1;
> + writel(config, regs + HPET_CONFIG_REG);
> +}
> +
> +/*
> + * Read the main counter as two 32-bit registers,
> + * repeat if rollover happens.
> + */
> +static u64 read_main_counter(void *regs)
> +{
> + u64 now_tick;
> + u32 tl, th, th0;
> +
> + th = readl(regs + HPET_MAIN_COUNT_H);
> + do {
> + th0 = th;
> + tl = readl(regs + HPET_MAIN_COUNT_L);
> + th = readl(regs + HPET_MAIN_COUNT_H);
> + } while (th != th0);
> +
> + now_tick = th;
> + now_tick <<= 32;
> + now_tick |= tl;
> +
> + return now_tick;
> +}
> +
> +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;
> +}
> +
> +#ifndef CONFIG_X86_TSC_TIMER
This looks not good. If we support more timers on x86, the #ifdef
logic will become un-maintainable. Can we support such from the timer
library instead?
> +
> +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_TSC_TIMER */
> +
> +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,
> +};
> --
I will try to test this patch on several x86 boards.
Regards,
Bin
More information about the U-Boot
mailing list