summaryrefslogtreecommitdiff
path: root/drivers/timer
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/timer')
-rw-r--r--drivers/timer/Kconfig357
-rw-r--r--drivers/timer/Makefile39
-rw-r--r--drivers/timer/adi_sc5xx_timer.c145
-rw-r--r--drivers/timer/altera_timer.c93
-rw-r--r--drivers/timer/andes_plmt_timer.c69
-rw-r--r--drivers/timer/arc_timer.c108
-rw-r--r--drivers/timer/arm_global_timer.c87
-rw-r--r--drivers/timer/arm_twd_timer.c107
-rw-r--r--drivers/timer/ast_ibex_timer.c45
-rw-r--r--drivers/timer/ast_timer.c91
-rw-r--r--drivers/timer/atmel_pit_timer.c86
-rw-r--r--drivers/timer/atmel_tcb_timer.c160
-rw-r--r--drivers/timer/cadence-ttc.c128
-rw-r--r--drivers/timer/dw-apb-timer.c123
-rw-r--r--drivers/timer/fttmr010_timer.c91
-rw-r--r--drivers/timer/gxp-timer.c64
-rw-r--r--drivers/timer/imx-gpt-timer.c162
-rw-r--r--drivers/timer/mchp-pit64b-timer.c106
-rw-r--r--drivers/timer/mpc83xx_timer.c253
-rw-r--r--drivers/timer/mtk_timer.c110
-rw-r--r--drivers/timer/nomadik-mtu-timer.c109
-rw-r--r--drivers/timer/npcm-timer.c74
-rw-r--r--drivers/timer/omap-timer.c128
-rw-r--r--drivers/timer/orion-timer.c149
-rw-r--r--drivers/timer/ostm_timer.c91
-rw-r--r--drivers/timer/riscv_aclint_timer.c97
-rw-r--r--drivers/timer/riscv_timer.c119
-rw-r--r--drivers/timer/rockchip_timer.c169
-rw-r--r--drivers/timer/sandbox_timer.c77
-rw-r--r--drivers/timer/sp804_timer.c106
-rw-r--r--drivers/timer/starfive-timer.c95
-rw-r--r--drivers/timer/stm32_timer.c137
-rw-r--r--drivers/timer/tegra-timer.c129
-rw-r--r--drivers/timer/timer-uclass.c181
-rw-r--r--drivers/timer/tsc_timer.c517
-rw-r--r--drivers/timer/xilinx-timer.c81
36 files changed, 4683 insertions, 0 deletions
diff --git a/drivers/timer/Kconfig b/drivers/timer/Kconfig
new file mode 100644
index 00000000000..cb6fc0e7fda
--- /dev/null
+++ b/drivers/timer/Kconfig
@@ -0,0 +1,357 @@
+menu "Timer Support"
+
+config TIMER
+ bool "Enable driver model for timer drivers"
+ depends on DM
+ help
+ Enable driver model for timer access. It uses the same API as
+ lib/time.c, but now implemented by the uclass. The first timer
+ will be used. The timer is usually a 32 bits free-running up
+ counter. There may be no real tick, and no timer interrupt.
+
+config SPL_TIMER
+ bool "Enable driver model for timer drivers in SPL"
+ depends on TIMER && SPL
+ help
+ Enable support for timer drivers in SPL. These can be used to get
+ a timer value when in SPL, or perhaps for implementing a delay
+ function. This enables the drivers in drivers/timer as part of an
+ SPL build.
+
+config TPL_TIMER
+ bool "Enable driver model for timer drivers in TPL"
+ depends on TIMER && TPL
+ help
+ Enable support for timer drivers in TPL. These can be used to get
+ a timer value when in TPL, or perhaps for implementing a delay
+ function. This enables the drivers in drivers/timer as part of an
+ TPL build.
+
+config VPL_TIMER
+ bool "Enable driver model for timer drivers in VPL"
+ depends on TIMER && VPL
+ default y if TPL_TIMER
+ help
+ Enable support for timer drivers in VPL. These can be used to get
+ a timer value when in VPL, or perhaps for implementing a delay
+ function. This enables the drivers in drivers/timer as part of an
+ TPL build.
+
+config TIMER_EARLY
+ bool "Allow timer to be used early in U-Boot"
+ depends on TIMER
+ # initr_bootstage() requires a timer and is called before initr_dm()
+ # so only the early timer is available
+ default y if X86 && BOOTSTAGE
+ help
+ In some cases the timer must be accessible before driver model is
+ active. Examples include when using CONFIG_TRACE to trace U-Boot's
+ execution before driver model is set up. Enable this option to
+ use an early timer. These functions must be supported by your timer
+ driver: timer_early_get_count() and timer_early_get_rate().
+
+config ADI_SC5XX_TIMER
+ bool "ADI ADSP-SC5xx Timer Support"
+ depends on TIMER && (SC57X || SC58X || SC59X || SC59X_64)
+ help
+ gptimer based timer support on ADI's ADSP-SC5xx platforms. Available
+ but not required on sc59x-64-based platforms (598 and similar).
+ Required on 32-bit platforms (sc57x, sc58x, sc594 and earlier).
+
+config ALTERA_TIMER
+ bool "Altera timer support"
+ depends on TIMER
+ help
+ Select this to enable a timer for Altera devices. Please find
+ details on the "Embedded Peripherals IP User Guide" of Altera.
+
+config ANDES_PLMT_TIMER
+ bool
+ depends on RISCV_MMODE
+ help
+ The Andes PLMT block holds memory-mapped mtime register
+ associated with timer tick.
+
+config SPL_ANDES_PLMT_TIMER
+ bool
+ depends on SPL_RISCV_MMODE
+ help
+ The Andes PLMT block holds memory-mapped mtime register
+ associated with timer tick.
+
+config ARC_TIMER
+ bool "ARC timer support"
+ depends on TIMER && ARC && CLK
+ help
+ Select this to enable built-in ARC timers.
+ ARC cores may have up to 2 built-in timers: timer0 and timer1,
+ usually at least one of them exists. Either of them is supported
+ in U-Boot.
+
+config ARM_TWD_TIMER
+ bool "ARM timer watchdog (TWD) timer support"
+ depends on TIMER && CLK
+ help
+ Select this to enable support for the ARM global timer watchdog timer.
+
+config AST_TIMER
+ bool "Aspeed ast2400/ast2500 timer support"
+ depends on TIMER
+ default y if ARCH_ASPEED
+ help
+ Select this to enable timer for Aspeed ast2400/ast2500 devices.
+ This is a simple sys timer driver, it is compatible with lib/time.c,
+ but does not support any interrupts. Even though SoC has 8 hardware
+ counters, they are all treated as a single device by this driver.
+ This is mostly because they all share several registers which
+ makes it difficult to completely separate them.
+
+config AST_IBEX_TIMER
+ bool "Aspeed ast2700 Ibex timer"
+ depends on TIMER
+ help
+ Select this to enable a timer support for the Ibex RV32-based MCUs in AST2700.
+
+config ATCPIT100_TIMER
+ bool "ATCPIT100 timer support"
+ depends on TIMER
+ help
+ Select this to enable a ATCPIT100 timer which will be embedded
+ in AE3XX, AE250 boards.
+
+config ATMEL_PIT_TIMER
+ bool "Atmel periodic interval timer support"
+ depends on TIMER
+ help
+ Select this to enable a periodic interval timer for Atmel devices,
+ it is designed to offer maximum accuracy and efficient management,
+ even for systems with long response time.
+
+config SPL_ATMEL_PIT_TIMER
+ bool "Atmel periodic interval timer support in SPL"
+ depends on SPL_TIMER
+ help
+ Select this to enable a periodic interval timer for Atmel devices,
+ it is designed to offer maximum accuracy and efficient management,
+ even for systems with long response time.
+ Select this to be available in SPL.
+
+config ATMEL_TCB_TIMER
+ bool "Atmel timer counter support"
+ depends on TIMER
+ depends on ARCH_AT91
+ help
+ Select this to enable the use of the timer counter as a monotonic
+ counter.
+
+config SPL_ATMEL_TCB_TIMER
+ bool "Atmel timer counter support in SPL"
+ depends on SPL_TIMER
+ depends on ARCH_AT91
+ help
+ Select this to enable the use of the timer counter as a monotonic
+ counter in SPL.
+
+config CADENCE_TTC_TIMER
+ bool "Cadence TTC (Triple Timer Counter)"
+ depends on TIMER
+ help
+ Enables support for the cadence ttc driver. This driver is present
+ on Xilinx Zynq and ZynqMP SoCs.
+
+config DESIGNWARE_APB_TIMER
+ bool "Designware APB Timer"
+ depends on TIMER
+ help
+ Enables support for the Designware APB Timer driver. This timer is
+ present on Altera SoCFPGA SoCs.
+
+config FTTMR010_TIMER
+ bool "Faraday Technology timer support"
+ depends on TIMER
+ help
+ Select this to enable support for the timer found on
+ devices using Faraday Technology's IP.
+
+config GXP_TIMER
+ bool "HPE GXP Timer"
+ depends on TIMER
+ help
+ Enables support for the GXP Timer driver. This timer is
+ present on HPE GXP SoCs.
+
+config MPC83XX_TIMER
+ bool "MPC83xx timer support"
+ depends on TIMER
+ help
+ Select this to enable support for the timer found on
+ devices based on the MPC83xx family of SoCs.
+
+config RENESAS_OSTM_TIMER
+ bool "Renesas RZ/A1 R7S72100 OSTM Timer"
+ depends on TIMER
+ help
+ Enables support for the Renesas OSTM Timer driver.
+ This timer is present on Renesas RZ/A1 R7S72100 SoCs.
+
+config X86_TSC_TIMER_FREQ
+ int "x86 TSC timer frequency in Hz"
+ depends on X86_TSC_TIMER
+ default 1000000000
+ help
+ Sets the estimated CPU frequency in Hz when TSC is used as the
+ early timer and the frequency can neither be calibrated via some
+ hardware ways, nor got from device tree at the time when device
+ tree is not available yet.
+
+config NOMADIK_MTU_TIMER
+ bool "Nomadik MTU Timer"
+ depends on TIMER
+ help
+ Enables support for the Nomadik Multi Timer Unit (MTU),
+ used in ST-Ericsson Ux500 SoCs.
+ The MTU provides 4 decrementing free-running timers.
+ At the moment, only the first timer is used by the driver.
+
+config NPCM_TIMER
+ bool "Nuvoton NPCM timer support"
+ depends on TIMER
+ help
+ Select this to enable a timer on Nuvoton NPCM SoCs.
+ NPCM timer module has 5 down-counting timers, only the first timer
+ is used to implement timer ops. No support for early timer and
+ boot timer.
+
+config OMAP_TIMER
+ bool "Omap timer support"
+ depends on TIMER
+ help
+ Select this to enable an timer for Omap devices.
+
+config ORION_TIMER
+ bool "Orion timer support"
+ depends on TIMER
+ default y if ARCH_KIRKWOOD || (ARCH_MVEBU && ARMADA_32BIT)
+ select TIMER_EARLY if ARCH_MVEBU
+ help
+ Select this to enable an timer for Orion and Armada devices
+ like Armada XP etc.
+
+config RISCV_TIMER
+ bool "RISC-V timer support"
+ depends on TIMER && RISCV
+ help
+ Select this to enable support for a generic RISC-V S-Mode timer
+ driver.
+
+config ROCKCHIP_TIMER
+ bool "Rockchip timer support"
+ depends on TIMER
+ help
+ Select this to enable support for the timer found on
+ Rockchip devices.
+
+config SANDBOX_TIMER
+ bool "Sandbox timer support"
+ depends on SANDBOX && TIMER
+ help
+ Select this to enable an emulated timer for sandbox. It gets
+ time from host os.
+
+config ARM_GLOBAL_TIMER
+ bool "ARM Cortex A9 global timer support"
+ depends on TIMER
+ depends on ARM
+ default y if ARCH_STI
+ help
+ Select this to enable global timer found on ARM Cortex A9
+ based devices.
+
+config SP804_TIMER
+ bool "ARM SP804 timer support"
+ depends on TIMER
+ help
+ ARM SP804 dual timer IP support
+
+config STM32_TIMER
+ bool "STM32 timer support"
+ depends on TIMER
+ help
+ Select this to enable support for the timer found on
+ STM32 devices.
+
+config TEGRA_TIMER
+ bool "Tegra timer support"
+ depends on TIMER
+ select TIMER_EARLY
+ help
+ Select this to enable support for the timer found on
+ Tegra devices.
+
+config X86_TSC_TIMER
+ bool "x86 Time-Stamp Counter (TSC) timer support"
+ depends on TIMER && X86
+ help
+ Select this to enable Time-Stamp Counter (TSC) timer for x86.
+
+config X86_TSC_READ_BASE
+ bool "Read the TSC timer base on start-up"
+ depends on X86_TSC_TIMER
+ help
+ On x86 platforms the TSC timer tick starts at the value 0 on reset.
+ This it makes no sense to read the timer on boot and use that as the
+ base, since we will miss some time taken to load U-Boot, etc. This
+ delay is controlled by the SoC and we cannot reduce it, but for
+ bootstage we want to record the time since reset as accurately as
+ possible.
+
+ The only exception is when U-Boot is used as a secondary bootloader,
+ where this option should be enabled.
+
+config TPL_X86_TSC_TIMER_NATIVE
+ bool "x86 TSC timer uses native calibration"
+ depends on TPL && X86_TSC_TIMER
+ help
+ Selects native timer calibration for TPL and don't include the other
+ methods in the code. This helps to reduce code size in TPL and works
+ on fairly modern Intel chips. Code-size reductions is about 700
+ bytes.
+
+config MTK_TIMER
+ bool "MediaTek timer support"
+ depends on TIMER
+ help
+ Select this to enable support for the timer found on
+ MediaTek devices.
+
+config MCHP_PIT64B_TIMER
+ bool "Microchip 64-bit periodic interval timer support"
+ depends on TIMER
+ help
+ Select this to enable support for Microchip 64-bit periodic
+ interval timer.
+
+config IMX_GPT_TIMER
+ bool "NXP i.MX GPT timer support"
+ depends on TIMER
+ help
+ Select this to enable support for the timer found on
+ NXP i.MX devices.
+
+config XILINX_TIMER
+ bool "Xilinx timer support"
+ depends on TIMER
+ select REGMAP
+ select SPL_REGMAP if SPL
+ help
+ Select this to enable support for the timer found on
+ any Xilinx boards (axi timer).
+
+config STARFIVE_TIMER
+ bool "Starfive timer support"
+ depends on TIMER
+ help
+ Select this to enable support for the timer found on
+ Starfive SoC.
+
+endmenu
diff --git a/drivers/timer/Makefile b/drivers/timer/Makefile
new file mode 100644
index 00000000000..7a847e8388b
--- /dev/null
+++ b/drivers/timer/Makefile
@@ -0,0 +1,39 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Copyright (C) 2015 Thomas Chou <thomas@wytron.com.tw>
+
+obj-y += timer-uclass.o
+obj-$(CONFIG_ADI_SC5XX_TIMER) += adi_sc5xx_timer.o
+obj-$(CONFIG_ALTERA_TIMER) += altera_timer.o
+obj-$(CONFIG_$(XPL_)ANDES_PLMT_TIMER) += andes_plmt_timer.o
+obj-$(CONFIG_ARC_TIMER) += arc_timer.o
+obj-$(CONFIG_ARM_TWD_TIMER) += arm_twd_timer.o
+obj-$(CONFIG_AST_TIMER) += ast_timer.o
+obj-$(CONFIG_AST_IBEX_TIMER) += ast_ibex_timer.o
+obj-$(CONFIG_ATCPIT100_TIMER) += atcpit100_timer.o
+obj-$(CONFIG_$(XPL_)ATMEL_PIT_TIMER) += atmel_pit_timer.o
+obj-$(CONFIG_$(XPL_)ATMEL_TCB_TIMER) += atmel_tcb_timer.o
+obj-$(CONFIG_CADENCE_TTC_TIMER) += cadence-ttc.o
+obj-$(CONFIG_DESIGNWARE_APB_TIMER) += dw-apb-timer.o
+obj-$(CONFIG_FTTMR010_TIMER) += fttmr010_timer.o
+obj-$(CONFIG_GXP_TIMER) += gxp-timer.o
+obj-$(CONFIG_MPC83XX_TIMER) += mpc83xx_timer.o
+obj-$(CONFIG_NOMADIK_MTU_TIMER) += nomadik-mtu-timer.o
+obj-$(CONFIG_NPCM_TIMER) += npcm-timer.o
+obj-$(CONFIG_OMAP_TIMER) += omap-timer.o
+obj-$(CONFIG_ORION_TIMER) += orion-timer.o
+obj-$(CONFIG_RENESAS_OSTM_TIMER) += ostm_timer.o
+obj-$(CONFIG_RISCV_TIMER) += riscv_timer.o
+obj-$(CONFIG_ROCKCHIP_TIMER) += rockchip_timer.o
+obj-$(CONFIG_SANDBOX_TIMER) += sandbox_timer.o
+obj-$(CONFIG_SP804_TIMER) += sp804_timer.o
+obj-$(CONFIG_$(XPL_)RISCV_ACLINT) += riscv_aclint_timer.o
+obj-$(CONFIG_ARM_GLOBAL_TIMER) += arm_global_timer.o
+obj-$(CONFIG_STM32_TIMER) += stm32_timer.o
+obj-$(CONFIG_TEGRA_TIMER) += tegra-timer.o
+obj-$(CONFIG_X86_TSC_TIMER) += tsc_timer.o
+obj-$(CONFIG_MTK_TIMER) += mtk_timer.o
+obj-$(CONFIG_MCHP_PIT64B_TIMER) += mchp-pit64b-timer.o
+obj-$(CONFIG_IMX_GPT_TIMER) += imx-gpt-timer.o
+obj-$(CONFIG_XILINX_TIMER) += xilinx-timer.o
+obj-$(CONFIG_STARFIVE_TIMER) += starfive-timer.o
diff --git a/drivers/timer/adi_sc5xx_timer.c b/drivers/timer/adi_sc5xx_timer.c
new file mode 100644
index 00000000000..11c098434a8
--- /dev/null
+++ b/drivers/timer/adi_sc5xx_timer.c
@@ -0,0 +1,145 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * (C) Copyright 2022 - Analog Devices, Inc.
+ *
+ * Written and/or maintained by Timesys Corporation
+ *
+ * Converted to driver model by Nathan Barrett-Morrison
+ *
+ * Author: Greg Malysa <greg.malysa@timesys.com>
+ * Additional Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com>
+ *
+ * dm timer implementation for ADI ADSP-SC5xx SoCs
+ *
+ */
+
+#include <clk.h>
+#include <dm.h>
+#include <timer.h>
+#include <asm/io.h>
+#include <dm/device_compat.h>
+#include <linux/compiler_types.h>
+
+/*
+ * Timer Configuration Register Bits
+ */
+#define TIMER_OUT_DIS 0x0800
+#define TIMER_PULSE_HI 0x0080
+#define TIMER_MODE_PWM_CONT 0x000c
+
+#define __BFP(m) u16 m; u16 __pad_##m
+
+struct gptimer3 {
+ __BFP(config);
+ u32 counter;
+ u32 period;
+ u32 width;
+ u32 delay;
+};
+
+struct gptimer3_group_regs {
+ __BFP(run);
+ __BFP(enable);
+ __BFP(disable);
+ __BFP(stop_cfg);
+ __BFP(stop_cfg_set);
+ __BFP(stop_cfg_clr);
+ __BFP(data_imsk);
+ __BFP(stat_imsk);
+ __BFP(tr_msk);
+ __BFP(tr_ie);
+ __BFP(data_ilat);
+ __BFP(stat_ilat);
+ __BFP(err_status);
+ __BFP(bcast_per);
+ __BFP(bcast_wid);
+ __BFP(bcast_dly);
+};
+
+#define MAX_TIM_LOAD 0xFFFFFFFF
+
+struct adi_gptimer_priv {
+ struct gptimer3_group_regs __iomem *timer_group;
+ struct gptimer3 __iomem *timer_base;
+ u32 prev;
+ u64 upper;
+};
+
+static u64 adi_gptimer_get_count(struct udevice *udev)
+{
+ struct adi_gptimer_priv *priv = dev_get_priv(udev);
+
+ u32 now = readl(&priv->timer_base->counter);
+
+ if (now < priv->prev)
+ priv->upper += (1ull << 32);
+
+ priv->prev = now;
+
+ return (priv->upper + (u64)now);
+}
+
+static const struct timer_ops adi_gptimer_ops = {
+ .get_count = adi_gptimer_get_count,
+};
+
+static int adi_gptimer_probe(struct udevice *udev)
+{
+ struct timer_dev_priv *uc_priv = dev_get_uclass_priv(udev);
+ struct adi_gptimer_priv *priv = dev_get_priv(udev);
+ struct clk clk;
+ u16 imask;
+ int ret;
+
+ priv->timer_group = dev_remap_addr_index(udev, 0);
+ priv->timer_base = dev_remap_addr_index(udev, 1);
+ priv->upper = 0;
+ priv->prev = 0;
+
+ if (!priv->timer_group || !priv->timer_base) {
+ dev_err(udev, "Missing timer_group or timer_base reg entries\n");
+ return -ENODEV;
+ }
+
+ ret = clk_get_by_index(udev, 0, &clk);
+ if (ret < 0) {
+ dev_err(udev, "Missing clock reference for timer\n");
+ return ret;
+ }
+
+ ret = clk_enable(&clk);
+ if (ret) {
+ dev_err(udev, "Failed to enable clock\n");
+ return ret;
+ }
+
+ uc_priv->clock_rate = clk_get_rate(&clk);
+
+ /* Enable timer */
+ writew(TIMER_OUT_DIS | TIMER_MODE_PWM_CONT | TIMER_PULSE_HI,
+ &priv->timer_base->config);
+ writel(MAX_TIM_LOAD, &priv->timer_base->period);
+ writel(MAX_TIM_LOAD - 1, &priv->timer_base->width);
+
+ /* We only use timer 0 in uboot */
+ imask = readw(&priv->timer_group->data_imsk);
+ imask &= ~(1 << 0);
+ writew(imask, &priv->timer_group->data_imsk);
+ writew((1 << 0), &priv->timer_group->enable);
+
+ return 0;
+}
+
+static const struct udevice_id adi_gptimer_ids[] = {
+ { .compatible = "adi,sc5xx-gptimer" },
+ { },
+};
+
+U_BOOT_DRIVER(adi_gptimer) = {
+ .name = "adi_gptimer",
+ .id = UCLASS_TIMER,
+ .of_match = adi_gptimer_ids,
+ .priv_auto = sizeof(struct adi_gptimer_priv),
+ .probe = adi_gptimer_probe,
+ .ops = &adi_gptimer_ops,
+};
diff --git a/drivers/timer/altera_timer.c b/drivers/timer/altera_timer.c
new file mode 100644
index 00000000000..ece246c23d2
--- /dev/null
+++ b/drivers/timer/altera_timer.c
@@ -0,0 +1,93 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2000-2002
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ *
+ * (C) Copyright 2004, Psyent Corporation <www.psyent.com>
+ * Scott McNutt <smcnutt@psyent.com>
+ */
+
+#include <dm.h>
+#include <errno.h>
+#include <timer.h>
+#include <asm/io.h>
+#include <linux/bitops.h>
+
+/* control register */
+#define ALTERA_TIMER_CONT BIT(1) /* Continuous mode */
+#define ALTERA_TIMER_START BIT(2) /* Start timer */
+#define ALTERA_TIMER_STOP BIT(3) /* Stop timer */
+
+struct altera_timer_regs {
+ u32 status; /* Timer status reg */
+ u32 control; /* Timer control reg */
+ u32 periodl; /* Timeout period low */
+ u32 periodh; /* Timeout period high */
+ u32 snapl; /* Snapshot low */
+ u32 snaph; /* Snapshot high */
+};
+
+struct altera_timer_plat {
+ struct altera_timer_regs *regs;
+};
+
+static u64 altera_timer_get_count(struct udevice *dev)
+{
+ struct altera_timer_plat *plat = dev_get_plat(dev);
+ struct altera_timer_regs *const regs = plat->regs;
+ u32 val;
+
+ /* Trigger update */
+ writel(0x0, &regs->snapl);
+
+ /* Read timer value */
+ val = readl(&regs->snapl) & 0xffff;
+ val |= (readl(&regs->snaph) & 0xffff) << 16;
+ return timer_conv_64(~val);
+}
+
+static int altera_timer_probe(struct udevice *dev)
+{
+ struct altera_timer_plat *plat = dev_get_plat(dev);
+ struct altera_timer_regs *const regs = plat->regs;
+
+ writel(0, &regs->status);
+ writel(0, &regs->control);
+ writel(ALTERA_TIMER_STOP, &regs->control);
+
+ writel(0xffff, &regs->periodl);
+ writel(0xffff, &regs->periodh);
+ writel(ALTERA_TIMER_CONT | ALTERA_TIMER_START, &regs->control);
+
+ return 0;
+}
+
+static int altera_timer_of_to_plat(struct udevice *dev)
+{
+ struct altera_timer_plat *plat = dev_get_plat(dev);
+
+ plat->regs = map_physmem(dev_read_addr(dev),
+ sizeof(struct altera_timer_regs),
+ MAP_NOCACHE);
+
+ return 0;
+}
+
+static const struct timer_ops altera_timer_ops = {
+ .get_count = altera_timer_get_count,
+};
+
+static const struct udevice_id altera_timer_ids[] = {
+ { .compatible = "altr,timer-1.0" },
+ {}
+};
+
+U_BOOT_DRIVER(altera_timer) = {
+ .name = "altera_timer",
+ .id = UCLASS_TIMER,
+ .of_match = altera_timer_ids,
+ .of_to_plat = altera_timer_of_to_plat,
+ .plat_auto = sizeof(struct altera_timer_plat),
+ .probe = altera_timer_probe,
+ .ops = &altera_timer_ops,
+};
diff --git a/drivers/timer/andes_plmt_timer.c b/drivers/timer/andes_plmt_timer.c
new file mode 100644
index 00000000000..20baaf61307
--- /dev/null
+++ b/drivers/timer/andes_plmt_timer.c
@@ -0,0 +1,69 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019, Rick Chen <rick@andestech.com>
+ * Copyright (C) 2020, Sean Anderson <seanga2@gmail.com>
+ *
+ * U-Boot syscon driver for Andes's Platform Level Machine Timer (PLMT).
+ * The PLMT block holds memory-mapped mtime register
+ * associated with timer tick.
+ */
+
+#include <dm.h>
+#include <timer.h>
+#include <asm/io.h>
+#include <dm/device-internal.h>
+#include <linux/err.h>
+
+/* mtime register */
+#define MTIME_REG(base) ((ulong)(base))
+
+static u64 notrace andes_plmt_get_count(struct udevice *dev)
+{
+ return readq((void __iomem *)MTIME_REG(dev_get_priv(dev)));
+}
+
+#if CONFIG_IS_ENABLED(RISCV_MMODE) && IS_ENABLED(CONFIG_TIMER_EARLY)
+/**
+ * timer_early_get_rate() - Get the timer rate before driver model
+ */
+unsigned long notrace timer_early_get_rate(void)
+{
+ return RISCV_MMODE_TIMER_FREQ;
+}
+
+/**
+ * timer_early_get_count() - Get the timer count before driver model
+ *
+ */
+u64 notrace timer_early_get_count(void)
+{
+ return readq((void __iomem *)MTIME_REG(RISCV_MMODE_TIMERBASE));
+}
+#endif
+
+static const struct timer_ops andes_plmt_ops = {
+ .get_count = andes_plmt_get_count,
+};
+
+static int andes_plmt_probe(struct udevice *dev)
+{
+ dev_set_priv(dev, dev_read_addr_ptr(dev));
+ if (!dev_get_priv(dev))
+ return -EINVAL;
+
+ return timer_timebase_fallback(dev);
+}
+
+static const struct udevice_id andes_plmt_ids[] = {
+ { .compatible = "andestech,plmt0" },
+ { }
+};
+
+U_BOOT_DRIVER(andes_plmt) = {
+ .name = "andes_plmt",
+ .id = UCLASS_TIMER,
+ .of_match = andes_plmt_ids,
+ .ops = &andes_plmt_ops,
+ .probe = andes_plmt_probe,
+ .flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/drivers/timer/arc_timer.c b/drivers/timer/arc_timer.c
new file mode 100644
index 00000000000..6ad520c10aa
--- /dev/null
+++ b/drivers/timer/arc_timer.c
@@ -0,0 +1,108 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2016 Synopsys, Inc. All rights reserved.
+ */
+
+#include <dm.h>
+#include <errno.h>
+#include <timer.h>
+#include <asm/arcregs.h>
+#include <asm/global_data.h>
+#include <asm/io.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define NH_MODE (1 << 1)
+
+/*
+ * ARC timer control registers are mapped to auxiliary address space.
+ * There are special ARC asm command to access that addresses.
+ * Therefore we use built-in functions to read from and write to timer
+ * control register.
+ */
+
+/* Driver private data. Contains timer id. Could be either 0 or 1. */
+struct arc_timer_priv {
+ uint timer_id;
+};
+
+static u64 arc_timer_get_count(struct udevice *dev)
+{
+ u32 val = 0;
+ struct arc_timer_priv *priv = dev_get_priv(dev);
+
+ switch (priv->timer_id) {
+ case 0:
+ val = read_aux_reg(ARC_AUX_TIMER0_CNT);
+ break;
+ case 1:
+ val = read_aux_reg(ARC_AUX_TIMER1_CNT);
+ break;
+ }
+ return timer_conv_64(val);
+}
+
+static int arc_timer_probe(struct udevice *dev)
+{
+ int id;
+ struct arc_timer_priv *priv = dev_get_priv(dev);
+
+ /* Get registers offset and size */
+ id = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), "reg", -1);
+ if (id < 0)
+ return -EINVAL;
+
+ if (id > 1)
+ return -ENXIO;
+
+ priv->timer_id = (uint)id;
+
+ /*
+ * In ARC core there're special registers (Auxiliary or AUX) in its
+ * separate memory space that are used for accessing some hardware
+ * features of the core. They are not mapped in normal memory space
+ * and also always have the same location regardless core configuration.
+ * Thus to simplify understanding of the programming model we chose to
+ * access AUX regs of Timer0 and Timer1 separately instead of using
+ * offsets from some base address.
+ */
+
+ switch (priv->timer_id) {
+ case 0:
+ /* Disable timer if CPU is halted */
+ write_aux_reg(ARC_AUX_TIMER0_CTRL, NH_MODE);
+ /* Set max value for counter/timer */
+ write_aux_reg(ARC_AUX_TIMER0_LIMIT, 0xffffffff);
+ /* Set initial count value and restart counter/timer */
+ write_aux_reg(ARC_AUX_TIMER0_CNT, 0);
+ break;
+ case 1:
+ /* Disable timer if CPU is halted */
+ write_aux_reg(ARC_AUX_TIMER1_CTRL, NH_MODE);
+ /* Set max value for counter/timer */
+ write_aux_reg(ARC_AUX_TIMER1_LIMIT, 0xffffffff);
+ /* Set initial count value and restart counter/timer */
+ write_aux_reg(ARC_AUX_TIMER1_CNT, 0);
+ break;
+ }
+
+ return 0;
+}
+
+static const struct timer_ops arc_timer_ops = {
+ .get_count = arc_timer_get_count,
+};
+
+static const struct udevice_id arc_timer_ids[] = {
+ { .compatible = "snps,arc-timer" },
+ {}
+};
+
+U_BOOT_DRIVER(arc_timer) = {
+ .name = "arc_timer",
+ .id = UCLASS_TIMER,
+ .of_match = arc_timer_ids,
+ .probe = arc_timer_probe,
+ .ops = &arc_timer_ops,
+ .priv_auto = sizeof(struct arc_timer_priv),
+};
diff --git a/drivers/timer/arm_global_timer.c b/drivers/timer/arm_global_timer.c
new file mode 100644
index 00000000000..b8057929f99
--- /dev/null
+++ b/drivers/timer/arm_global_timer.c
@@ -0,0 +1,87 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
+ * Author(s): Patrice Chotard, <patrice.chotard@foss.st.com> for STMicroelectronics.
+ *
+ * ARM Cortext A9 global timer driver
+ */
+
+#include <config.h>
+#include <dm.h>
+#include <clk.h>
+#include <timer.h>
+#include <linux/err.h>
+
+#include <asm/io.h>
+#include <asm/arch-armv7/globaltimer.h>
+
+struct arm_global_timer_priv {
+ struct globaltimer *global_timer;
+};
+
+static u64 arm_global_timer_get_count(struct udevice *dev)
+{
+ struct arm_global_timer_priv *priv = dev_get_priv(dev);
+ struct globaltimer *global_timer = priv->global_timer;
+ u32 low, high;
+ u64 timer;
+ u32 old = readl(&global_timer->cnt_h);
+
+ while (1) {
+ low = readl(&global_timer->cnt_l);
+ high = readl(&global_timer->cnt_h);
+ if (old == high)
+ break;
+ else
+ old = high;
+ }
+ timer = high;
+ return (u64)((timer << 32) | low);
+}
+
+static int arm_global_timer_probe(struct udevice *dev)
+{
+ struct timer_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ struct arm_global_timer_priv *priv = dev_get_priv(dev);
+ struct clk clk;
+ int err;
+ ulong ret;
+
+ /* get arm global timer base address */
+ priv->global_timer = (struct globaltimer *)dev_read_addr_ptr(dev);
+ if (!priv->global_timer)
+ return -ENOENT;
+
+ err = clk_get_by_index(dev, 0, &clk);
+ if (!err) {
+ ret = clk_get_rate(&clk);
+ if (IS_ERR_VALUE(ret))
+ return ret;
+ uc_priv->clock_rate = ret;
+ } else {
+ uc_priv->clock_rate = CFG_SYS_HZ_CLOCK;
+ }
+
+ /* init timer */
+ writel(0x01, &priv->global_timer->ctl);
+
+ return 0;
+}
+
+static const struct timer_ops arm_global_timer_ops = {
+ .get_count = arm_global_timer_get_count,
+};
+
+static const struct udevice_id arm_global_timer_ids[] = {
+ { .compatible = "arm,cortex-a9-global-timer" },
+ {}
+};
+
+U_BOOT_DRIVER(arm_global_timer) = {
+ .name = "arm_global_timer",
+ .id = UCLASS_TIMER,
+ .of_match = arm_global_timer_ids,
+ .priv_auto = sizeof(struct arm_global_timer_priv),
+ .probe = arm_global_timer_probe,
+ .ops = &arm_global_timer_ops,
+};
diff --git a/drivers/timer/arm_twd_timer.c b/drivers/timer/arm_twd_timer.c
new file mode 100644
index 00000000000..2b2f3591173
--- /dev/null
+++ b/drivers/timer/arm_twd_timer.c
@@ -0,0 +1,107 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2017-2022 Weidmüller Interface GmbH & Co. KG
+ * Stefan Herbrechtsmeier <stefan.herbrechtsmeier@weidmueller.com>
+ *
+ * Copyright (C) 2012 Michal Simek <monstr@monstr.eu>
+ * Copyright (C) 2011-2017 Xilinx, Inc. All rights reserved.
+ *
+ * (C) Copyright 2008
+ * Guennadi Liakhovetki, DENX Software Engineering, <lg@denx.de>
+ *
+ * (C) Copyright 2004
+ * Philippe Robin, ARM Ltd. <philippe.robin@arm.com>
+ *
+ * (C) Copyright 2002-2004
+ * Gary Jennejohn, DENX Software Engineering, <gj@denx.de>
+ *
+ * (C) Copyright 2003
+ * Texas Instruments <www.ti.com>
+ *
+ * (C) Copyright 2002
+ * Sysgo Real-Time Solutions, GmbH <www.elinos.com>
+ * Marius Groeger <mgroeger@sysgo.de>
+ *
+ * (C) Copyright 2002
+ * Sysgo Real-Time Solutions, GmbH <www.elinos.com>
+ * Alex Zuepke <azu@sysgo.de>
+ */
+
+#include <dm.h>
+#include <fdtdec.h>
+#include <timer.h>
+#include <linux/bitops.h>
+
+#include <asm/io.h>
+
+#define SCUTIMER_CONTROL_PRESCALER_MASK 0x0000FF00 /* Prescaler */
+#define SCUTIMER_CONTROL_AUTO_RELOAD_MASK 0x00000002 /* Auto-reload */
+#define SCUTIMER_CONTROL_ENABLE_MASK 0x00000001 /* Timer enable */
+
+#define TIMER_LOAD_VAL 0xFFFFFFFF
+
+struct arm_twd_timer_regs {
+ u32 load; /* Timer Load Register */
+ u32 counter; /* Timer Counter Register */
+ u32 control; /* Timer Control Register */
+};
+
+struct arm_twd_timer_priv {
+ struct arm_twd_timer_regs *base;
+};
+
+static u64 arm_twd_timer_get_count(struct udevice *dev)
+{
+ struct arm_twd_timer_priv *priv = dev_get_priv(dev);
+ struct arm_twd_timer_regs *regs = priv->base;
+ u32 count = TIMER_LOAD_VAL - readl(&regs->counter);
+
+ return timer_conv_64(count);
+}
+
+static int arm_twd_timer_probe(struct udevice *dev)
+{
+ struct arm_twd_timer_priv *priv = dev_get_priv(dev);
+ struct arm_twd_timer_regs *regs;
+ fdt_addr_t addr;
+
+ addr = dev_read_addr(dev);
+ if (addr == FDT_ADDR_T_NONE)
+ return -EINVAL;
+
+ priv->base = (struct arm_twd_timer_regs *)addr;
+
+ regs = priv->base;
+
+ /* Load the timer counter register */
+ writel(0xFFFFFFFF, &regs->load);
+
+ /*
+ * Start the A9Timer device
+ * Enable Auto reload mode, Clear prescaler control bits
+ * Set prescaler value, Enable the decrementer
+ */
+ clrsetbits_le32(&regs->control, SCUTIMER_CONTROL_PRESCALER_MASK,
+ SCUTIMER_CONTROL_AUTO_RELOAD_MASK |
+ SCUTIMER_CONTROL_ENABLE_MASK);
+
+ return 0;
+}
+
+static const struct timer_ops arm_twd_timer_ops = {
+ .get_count = arm_twd_timer_get_count,
+};
+
+static const struct udevice_id arm_twd_timer_ids[] = {
+ { .compatible = "arm,cortex-a9-twd-timer" },
+ {}
+};
+
+U_BOOT_DRIVER(arm_twd_timer) = {
+ .name = "arm_twd_timer",
+ .id = UCLASS_TIMER,
+ .of_match = arm_twd_timer_ids,
+ .priv_auto = sizeof(struct arm_twd_timer_priv),
+ .probe = arm_twd_timer_probe,
+ .ops = &arm_twd_timer_ops,
+};
diff --git a/drivers/timer/ast_ibex_timer.c b/drivers/timer/ast_ibex_timer.c
new file mode 100644
index 00000000000..261839661e9
--- /dev/null
+++ b/drivers/timer/ast_ibex_timer.c
@@ -0,0 +1,45 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2024 Aspeed Technology Inc.
+ */
+
+#include <asm/csr.h>
+#include <asm/io.h>
+#include <dm.h>
+#include <errno.h>
+#include <timer.h>
+
+#define CSR_MCYCLE 0xb00
+#define CSR_MCYCLEH 0xb80
+
+static u64 ast_ibex_timer_get_count(struct udevice *dev)
+{
+ uint32_t cnt_l, cnt_h;
+
+ cnt_l = csr_read(CSR_MCYCLE);
+ cnt_h = csr_read(CSR_MCYCLEH);
+
+ return ((uint64_t)cnt_h << 32) | cnt_l;
+}
+
+static int ast_ibex_timer_probe(struct udevice *dev)
+{
+ return 0;
+}
+
+static const struct timer_ops ast_ibex_timer_ops = {
+ .get_count = ast_ibex_timer_get_count,
+};
+
+static const struct udevice_id ast_ibex_timer_ids[] = {
+ { .compatible = "aspeed,ast2700-ibex-timer" },
+ { }
+};
+
+U_BOOT_DRIVER(ast_ibex_timer) = {
+ .name = "ast_ibex_timer",
+ .id = UCLASS_TIMER,
+ .of_match = ast_ibex_timer_ids,
+ .probe = ast_ibex_timer_probe,
+ .ops = &ast_ibex_timer_ops,
+};
diff --git a/drivers/timer/ast_timer.c b/drivers/timer/ast_timer.c
new file mode 100644
index 00000000000..6601cab7b16
--- /dev/null
+++ b/drivers/timer/ast_timer.c
@@ -0,0 +1,91 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2016 Google Inc.
+ */
+
+#include <dm.h>
+#include <errno.h>
+#include <timer.h>
+#include <asm/io.h>
+#include <asm/arch/timer.h>
+#include <linux/err.h>
+
+#define AST_TICK_TIMER 1
+#define AST_TMC_RELOAD_VAL 0xffffffff
+
+struct ast_timer_priv {
+ struct ast_timer *regs;
+ struct ast_timer_counter *tmc;
+};
+
+static struct ast_timer_counter *ast_get_timer_counter(struct ast_timer *timer,
+ int n)
+{
+ if (n > 3)
+ return &timer->timers2[n - 4];
+ else
+ return &timer->timers1[n - 1];
+}
+
+static int ast_timer_probe(struct udevice *dev)
+{
+ struct ast_timer_priv *priv = dev_get_priv(dev);
+ struct timer_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+
+ writel(AST_TMC_RELOAD_VAL, &priv->tmc->reload_val);
+
+ /*
+ * Stop the timer. This will also load reload_val into
+ * the status register.
+ */
+ clrbits_le32(&priv->regs->ctrl1,
+ AST_TMC_EN << AST_TMC_CTRL1_SHIFT(AST_TICK_TIMER));
+ /* Start the timer from the fixed 1MHz clock. */
+ setbits_le32(&priv->regs->ctrl1,
+ (AST_TMC_EN | AST_TMC_1MHZ) <<
+ AST_TMC_CTRL1_SHIFT(AST_TICK_TIMER));
+
+ uc_priv->clock_rate = AST_TMC_RATE;
+
+ return 0;
+}
+
+static u64 ast_timer_get_count(struct udevice *dev)
+{
+ struct ast_timer_priv *priv = dev_get_priv(dev);
+
+ return AST_TMC_RELOAD_VAL - readl(&priv->tmc->status);
+}
+
+static int ast_timer_of_to_plat(struct udevice *dev)
+{
+ struct ast_timer_priv *priv = dev_get_priv(dev);
+
+ priv->regs = dev_read_addr_ptr(dev);
+ if (!priv->regs)
+ return -EINVAL;
+
+ priv->tmc = ast_get_timer_counter(priv->regs, AST_TICK_TIMER);
+
+ return 0;
+}
+
+static const struct timer_ops ast_timer_ops = {
+ .get_count = ast_timer_get_count,
+};
+
+static const struct udevice_id ast_timer_ids[] = {
+ { .compatible = "aspeed,ast2500-timer" },
+ { .compatible = "aspeed,ast2400-timer" },
+ { }
+};
+
+U_BOOT_DRIVER(ast_timer) = {
+ .name = "ast_timer",
+ .id = UCLASS_TIMER,
+ .of_match = ast_timer_ids,
+ .probe = ast_timer_probe,
+ .priv_auto = sizeof(struct ast_timer_priv),
+ .of_to_plat = ast_timer_of_to_plat,
+ .ops = &ast_timer_ops,
+};
diff --git a/drivers/timer/atmel_pit_timer.c b/drivers/timer/atmel_pit_timer.c
new file mode 100644
index 00000000000..0a367a5a7f4
--- /dev/null
+++ b/drivers/timer/atmel_pit_timer.c
@@ -0,0 +1,86 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2017 Microchip Corporation
+ * Wenyou.Yang <wenyou.yang@microchip.com>
+ */
+
+#include <clk.h>
+#include <dm.h>
+#include <timer.h>
+#include <asm/io.h>
+#include <linux/bitops.h>
+
+#define AT91_PIT_VALUE 0xfffff
+#define AT91_PIT_PITEN BIT(24) /* Timer Enabled */
+
+struct atmel_pit_regs {
+ u32 mode;
+ u32 status;
+ u32 value;
+ u32 value_image;
+};
+
+struct atmel_pit_plat {
+ struct atmel_pit_regs *regs;
+};
+
+static u64 atmel_pit_get_count(struct udevice *dev)
+{
+ struct atmel_pit_plat *plat = dev_get_plat(dev);
+ struct atmel_pit_regs *const regs = plat->regs;
+ u32 val = readl(&regs->value_image);
+
+ return timer_conv_64(val);
+}
+
+static int atmel_pit_probe(struct udevice *dev)
+{
+ struct atmel_pit_plat *plat = dev_get_plat(dev);
+ struct atmel_pit_regs *const regs = plat->regs;
+ struct timer_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ struct clk clk;
+ ulong clk_rate;
+ int ret;
+
+ ret = clk_get_by_index(dev, 0, &clk);
+ if (ret)
+ return -EINVAL;
+
+ clk_rate = clk_get_rate(&clk);
+ if (!clk_rate)
+ return -EINVAL;
+
+ uc_priv->clock_rate = clk_rate / 16;
+
+ writel(AT91_PIT_VALUE | AT91_PIT_PITEN, &regs->mode);
+
+ return 0;
+}
+
+static int atmel_pit_of_to_plat(struct udevice *dev)
+{
+ struct atmel_pit_plat *plat = dev_get_plat(dev);
+
+ plat->regs = dev_read_addr_ptr(dev);
+
+ return 0;
+}
+
+static const struct timer_ops atmel_pit_ops = {
+ .get_count = atmel_pit_get_count,
+};
+
+static const struct udevice_id atmel_pit_ids[] = {
+ { .compatible = "atmel,at91sam9260-pit" },
+ { }
+};
+
+U_BOOT_DRIVER(atmel_pit) = {
+ .name = "atmel_pit",
+ .id = UCLASS_TIMER,
+ .of_match = atmel_pit_ids,
+ .of_to_plat = atmel_pit_of_to_plat,
+ .plat_auto = sizeof(struct atmel_pit_plat),
+ .probe = atmel_pit_probe,
+ .ops = &atmel_pit_ops,
+};
diff --git a/drivers/timer/atmel_tcb_timer.c b/drivers/timer/atmel_tcb_timer.c
new file mode 100644
index 00000000000..3a328b2f6c7
--- /dev/null
+++ b/drivers/timer/atmel_tcb_timer.c
@@ -0,0 +1,160 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2022 Microchip Corporation
+ *
+ * Author: Clément Léger <clement.leger@bootlin.com>
+ */
+
+#include <clk.h>
+#include <dm.h>
+#include <timer.h>
+#include <asm/io.h>
+#include <linux/bitops.h>
+
+#define TCB_CHAN(chan) ((chan) * 0x40)
+
+#define TCB_CCR(chan) (0x0 + TCB_CHAN(chan))
+#define TCB_CCR_CLKEN (1 << 0)
+
+#define TCB_CMR(chan) (0x4 + TCB_CHAN(chan))
+#define TCB_CMR_WAVE (1 << 15)
+#define TCB_CMR_TIMER_CLOCK2 1
+#define TCB_CMR_XC1 6
+#define TCB_CMR_ACPA_SET (1 << 16)
+#define TCB_CMR_ACPC_CLEAR (2 << 18)
+
+#define TCB_CV(chan) (0x10 + TCB_CHAN(chan))
+
+#define TCB_RA(chan) (0x14 + TCB_CHAN(chan))
+#define TCB_RC(chan) (0x1c + TCB_CHAN(chan))
+
+#define TCB_IDR(chan) (0x28 + TCB_CHAN(chan))
+
+#define TCB_BCR 0xc0
+#define TCB_BCR_SYNC (1 << 0)
+
+#define TCB_BMR 0xc4
+#define TCB_BMR_TC1XC1S_TIOA0 (2 << 2)
+
+#define TCB_WPMR 0xe4
+#define TCB_WPMR_WAKEY 0x54494d
+
+#define TCB_CLK_DIVISOR 8
+struct atmel_tcb_plat {
+ void __iomem *base;
+};
+
+static u64 atmel_tcb_get_count(struct udevice *dev)
+{
+ struct atmel_tcb_plat *plat = dev_get_plat(dev);
+ u64 cv0 = 0;
+ u64 cv1 = 0;
+
+ do {
+ cv1 = readl(plat->base + TCB_CV(1));
+ cv0 = readl(plat->base + TCB_CV(0));
+ } while (readl(plat->base + TCB_CV(1)) != cv1);
+
+ cv0 |= cv1 << 32;
+
+ return cv0;
+}
+
+static void atmel_tcb_configure(void __iomem *base)
+{
+ /* Disable write protection */
+ writel(TCB_WPMR_WAKEY, base + TCB_WPMR);
+
+ /* Disable all irqs for both channel 0 & 1 */
+ writel(0xff, base + TCB_IDR(0));
+ writel(0xff, base + TCB_IDR(1));
+
+ /*
+ * In order to avoid wrapping, use a 64 bit counter by chaining
+ * two channels.
+ * Channel 0 is configured to generate a clock on TIOA0 which is cleared
+ * when reaching 0x80000000 and set when reaching 0.
+ */
+ writel(TCB_CMR_TIMER_CLOCK2 | TCB_CMR_WAVE | TCB_CMR_ACPA_SET
+ | TCB_CMR_ACPC_CLEAR, base + TCB_CMR(0));
+ writel(0x80000000, base + TCB_RC(0));
+ writel(0x1, base + TCB_RA(0));
+ writel(TCB_CCR_CLKEN, base + TCB_CCR(0));
+
+ /* Channel 1 is configured to use TIOA0 as input */
+ writel(TCB_CMR_XC1 | TCB_CMR_WAVE, base + TCB_CMR(1));
+ writel(TCB_CCR_CLKEN, base + TCB_CCR(1));
+
+ /* Set XC1 input to be TIOA0 (ie output of Channel 0) */
+ writel(TCB_BMR_TC1XC1S_TIOA0, base + TCB_BMR);
+
+ /* Sync & start all timers */
+ writel(TCB_BCR_SYNC, base + TCB_BCR);
+}
+
+static int atmel_tcb_probe(struct udevice *dev)
+{
+ struct atmel_tcb_plat *plat = dev_get_plat(dev);
+ struct timer_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ struct clk clk;
+ ulong clk_rate;
+ int ret;
+
+ if (!device_is_compatible(dev->parent, "atmel,sama5d2-tcb"))
+ return -EINVAL;
+
+ /* Currently, we only support channel 0 and 1 to be chained */
+ if (dev_read_addr_index(dev, 0) != 0 &&
+ dev_read_addr_index(dev, 1) != 1) {
+ printf("Error: only chained timers 0 and 1 are supported\n");
+ return -EINVAL;
+ }
+
+ ret = clk_get_by_name(dev->parent, "t0_clk", &clk);
+ if (ret)
+ return -EINVAL;
+
+ ret = clk_enable(&clk);
+ if (ret)
+ return ret;
+
+ clk_rate = clk_get_rate(&clk);
+ if (!clk_rate) {
+ clk_disable(&clk);
+ return -EINVAL;
+ }
+
+ uc_priv->clock_rate = clk_rate / TCB_CLK_DIVISOR;
+
+ atmel_tcb_configure(plat->base);
+
+ return 0;
+}
+
+static int atmel_tcb_of_to_plat(struct udevice *dev)
+{
+ struct atmel_tcb_plat *plat = dev_get_plat(dev);
+
+ plat->base = dev_read_addr_ptr(dev->parent);
+
+ return 0;
+}
+
+static const struct timer_ops atmel_tcb_ops = {
+ .get_count = atmel_tcb_get_count,
+};
+
+static const struct udevice_id atmel_tcb_ids[] = {
+ { .compatible = "atmel,tcb-timer" },
+ { }
+};
+
+U_BOOT_DRIVER(atmel_tcb) = {
+ .name = "atmel_tcb",
+ .id = UCLASS_TIMER,
+ .of_match = atmel_tcb_ids,
+ .of_to_plat = atmel_tcb_of_to_plat,
+ .plat_auto = sizeof(struct atmel_tcb_plat),
+ .probe = atmel_tcb_probe,
+ .ops = &atmel_tcb_ops,
+};
diff --git a/drivers/timer/cadence-ttc.c b/drivers/timer/cadence-ttc.c
new file mode 100644
index 00000000000..3cffb1bb88d
--- /dev/null
+++ b/drivers/timer/cadence-ttc.c
@@ -0,0 +1,128 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018 Xilinx, Inc. (Michal Simek)
+ */
+
+#include <bootstage.h>
+#include <dm.h>
+#include <errno.h>
+#include <init.h>
+#include <timer.h>
+#include <asm/global_data.h>
+#include <asm/io.h>
+#include <linux/bitops.h>
+#include <linux/err.h>
+
+#define CNT_CNTRL_RESET BIT(4)
+
+struct cadence_ttc_regs {
+ u32 clk_cntrl1; /* 0x0 - Clock Control 1 */
+ u32 clk_cntrl2; /* 0x4 - Clock Control 2 */
+ u32 clk_cntrl3; /* 0x8 - Clock Control 3 */
+ u32 counter_cntrl1; /* 0xC - Counter Control 1 */
+ u32 counter_cntrl2; /* 0x10 - Counter Control 2 */
+ u32 counter_cntrl3; /* 0x14 - Counter Control 3 */
+ u32 counter_val1; /* 0x18 - Counter Control 1 */
+ u32 counter_val2; /* 0x1C - Counter Control 2 */
+ u32 counter_val3; /* 0x20 - Counter Control 3 */
+ u32 reserved[15];
+ u32 interrupt_enable1; /* 0x60 - Interrupt Enable 1 */
+ u32 interrupt_enable2; /* 0x64 - Interrupt Enable 2 */
+ u32 interrupt_enable3; /* 0x68 - Interrupt Enable 3 */
+};
+
+struct cadence_ttc_priv {
+ struct cadence_ttc_regs *regs;
+};
+
+#if CONFIG_IS_ENABLED(BOOTSTAGE)
+ulong timer_get_boot_us(void)
+{
+ u64 ticks = 0;
+ u32 rate = 1;
+ u64 us;
+ int ret;
+
+ ret = dm_timer_init();
+ if (!ret) {
+ /* The timer is available */
+ rate = timer_get_rate(gd->timer);
+ timer_get_count(gd->timer, &ticks);
+ } else {
+ return 0;
+ }
+
+ us = (ticks * 1000) / rate;
+ return us;
+}
+#endif
+
+static u64 cadence_ttc_get_count(struct udevice *dev)
+{
+ struct cadence_ttc_priv *priv = dev_get_priv(dev);
+
+ return readl(&priv->regs->counter_val1);
+}
+
+static int cadence_ttc_probe(struct udevice *dev)
+{
+ struct cadence_ttc_priv *priv = dev_get_priv(dev);
+
+ /* Disable interrupts for sure */
+ writel(0, &priv->regs->interrupt_enable1);
+ writel(0, &priv->regs->interrupt_enable2);
+ writel(0, &priv->regs->interrupt_enable3);
+
+ /* Make sure that clocks are configured properly without prescaller */
+ writel(0, &priv->regs->clk_cntrl1);
+ writel(0, &priv->regs->clk_cntrl2);
+ writel(0, &priv->regs->clk_cntrl3);
+
+ /* Reset and enable this counter */
+ writel(CNT_CNTRL_RESET, &priv->regs->counter_cntrl1);
+
+ return 0;
+}
+
+static int cadence_ttc_of_to_plat(struct udevice *dev)
+{
+ struct cadence_ttc_priv *priv = dev_get_priv(dev);
+
+ priv->regs = map_physmem(dev_read_addr(dev),
+ sizeof(struct cadence_ttc_regs), MAP_NOCACHE);
+ if (IS_ERR(priv->regs))
+ return PTR_ERR(priv->regs);
+
+ return 0;
+}
+
+static int cadence_ttc_bind(struct udevice *dev)
+{
+ const char *cells;
+
+ cells = dev_read_prop(dev, "#pwm-cells", NULL);
+ if (cells)
+ return -ENODEV;
+
+ return 0;
+}
+
+static const struct timer_ops cadence_ttc_ops = {
+ .get_count = cadence_ttc_get_count,
+};
+
+static const struct udevice_id cadence_ttc_ids[] = {
+ { .compatible = "cdns,ttc" },
+ {}
+};
+
+U_BOOT_DRIVER(cadence_ttc) = {
+ .name = "cadence_ttc",
+ .id = UCLASS_TIMER,
+ .of_match = cadence_ttc_ids,
+ .of_to_plat = cadence_ttc_of_to_plat,
+ .priv_auto = sizeof(struct cadence_ttc_priv),
+ .probe = cadence_ttc_probe,
+ .ops = &cadence_ttc_ops,
+ .bind = cadence_ttc_bind,
+};
diff --git a/drivers/timer/dw-apb-timer.c b/drivers/timer/dw-apb-timer.c
new file mode 100644
index 00000000000..77ccb98cb8d
--- /dev/null
+++ b/drivers/timer/dw-apb-timer.c
@@ -0,0 +1,123 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Designware APB Timer driver
+ *
+ * Copyright (C) 2018 Marek Vasut <marex@denx.de>
+ */
+
+#include <dm.h>
+#include <clk.h>
+#include <dt-structs.h>
+#include <malloc.h>
+#include <reset.h>
+#include <timer.h>
+#include <dm/device_compat.h>
+
+#include <asm/io.h>
+#include <asm/arch/timer.h>
+
+#define DW_APB_LOAD_VAL 0x0
+#define DW_APB_CURR_VAL 0x4
+#define DW_APB_CTRL 0x8
+
+struct dw_apb_timer_priv {
+ uintptr_t regs;
+ struct reset_ctl_bulk resets;
+};
+
+struct dw_apb_timer_plat {
+#if CONFIG_IS_ENABLED(OF_PLATDATA)
+ struct dtd_snps_dw_apb_timer dtplat;
+#endif
+};
+
+static u64 dw_apb_timer_get_count(struct udevice *dev)
+{
+ struct dw_apb_timer_priv *priv = dev_get_priv(dev);
+
+ /*
+ * The DW APB counter counts down, but this function
+ * requires the count to be incrementing. Invert the
+ * result.
+ */
+ return timer_conv_64(~readl(priv->regs + DW_APB_CURR_VAL));
+}
+
+static int dw_apb_timer_probe(struct udevice *dev)
+{
+ struct timer_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ struct dw_apb_timer_priv *priv = dev_get_priv(dev);
+ struct clk clk;
+ int ret;
+#if CONFIG_IS_ENABLED(OF_PLATDATA)
+ struct dw_apb_timer_plat *plat = dev_get_plat(dev);
+ struct dtd_snps_dw_apb_timer *dtplat = &plat->dtplat;
+
+ priv->regs = dtplat->reg[0];
+
+ ret = clk_get_by_phandle(dev, &dtplat->clocks[0], &clk);
+ if (ret < 0)
+ return ret;
+
+ uc_priv->clock_rate = dtplat->clock_frequency;
+#endif
+ if (CONFIG_IS_ENABLED(OF_REAL)) {
+ ret = reset_get_bulk(dev, &priv->resets);
+ if (ret)
+ dev_warn(dev, "Can't get reset: %d\n", ret);
+ else
+ reset_deassert_bulk(&priv->resets);
+
+ ret = clk_get_by_index(dev, 0, &clk);
+ if (ret)
+ return ret;
+
+ uc_priv->clock_rate = clk_get_rate(&clk);
+ }
+
+ /* init timer */
+ writel(0xffffffff, priv->regs + DW_APB_LOAD_VAL);
+ writel(0xffffffff, priv->regs + DW_APB_CURR_VAL);
+ setbits_le32(priv->regs + DW_APB_CTRL, 0x3);
+
+ return 0;
+}
+
+static int dw_apb_timer_of_to_plat(struct udevice *dev)
+{
+ if (CONFIG_IS_ENABLED(OF_REAL)) {
+ struct dw_apb_timer_priv *priv = dev_get_priv(dev);
+
+ priv->regs = dev_read_addr(dev);
+ }
+
+ return 0;
+}
+
+static int dw_apb_timer_remove(struct udevice *dev)
+{
+ struct dw_apb_timer_priv *priv = dev_get_priv(dev);
+
+ return reset_release_bulk(&priv->resets);
+}
+
+static const struct timer_ops dw_apb_timer_ops = {
+ .get_count = dw_apb_timer_get_count,
+};
+
+static const struct udevice_id dw_apb_timer_ids[] = {
+ { .compatible = "snps,dw-apb-timer" },
+ {}
+};
+
+U_BOOT_DRIVER(snps_dw_apb_timer) = {
+ .name = "snps_dw_apb_timer",
+ .id = UCLASS_TIMER,
+ .ops = &dw_apb_timer_ops,
+ .probe = dw_apb_timer_probe,
+ .of_match = dw_apb_timer_ids,
+ .of_to_plat = dw_apb_timer_of_to_plat,
+ .remove = dw_apb_timer_remove,
+ .priv_auto = sizeof(struct dw_apb_timer_priv),
+ .plat_auto = sizeof(struct dw_apb_timer_plat),
+};
diff --git a/drivers/timer/fttmr010_timer.c b/drivers/timer/fttmr010_timer.c
new file mode 100644
index 00000000000..c41bbfc1d57
--- /dev/null
+++ b/drivers/timer/fttmr010_timer.c
@@ -0,0 +1,91 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2009 Faraday Technology
+ * Po-Yu Chuang <ratbert@faraday-tech.com>
+ *
+ * 23/08/2022 Port to DM
+ */
+#include <dm.h>
+#include <log.h>
+#include <timer.h>
+#include <asm/io.h>
+#include <dm/ofnode.h>
+#include <faraday/fttmr010.h>
+#include <asm/global_data.h>
+
+#define TIMER_LOAD_VAL 0xffffffff
+
+struct fttmr010_timer_priv {
+ struct fttmr010 __iomem *regs;
+};
+
+static u64 fttmr010_timer_get_count(struct udevice *dev)
+{
+ struct fttmr010_timer_priv *priv = dev_get_priv(dev);
+ struct fttmr010 *tmr = priv->regs;
+ u32 now = TIMER_LOAD_VAL - readl(&tmr->timer3_counter);
+
+ /* increment tbu if tbl has rolled over */
+ if (now < gd->arch.tbl)
+ gd->arch.tbu++;
+ gd->arch.tbl = now;
+
+ return ((u64)gd->arch.tbu << 32) | gd->arch.tbl;
+}
+
+static int fttmr010_timer_probe(struct udevice *dev)
+{
+ struct fttmr010_timer_priv *priv = dev_get_priv(dev);
+ struct fttmr010 *tmr;
+ unsigned int cr;
+
+ priv->regs = dev_read_addr_ptr(dev);
+ if (!priv->regs)
+ return -EINVAL;
+ tmr = priv->regs;
+
+ debug("Faraday FTTMR010 timer revision 0x%08X\n", readl(&tmr->revision));
+
+ /* disable timers */
+ writel(0, &tmr->cr);
+
+ /* setup timer */
+ writel(TIMER_LOAD_VAL, &tmr->timer3_load);
+ writel(TIMER_LOAD_VAL, &tmr->timer3_counter);
+ writel(0, &tmr->timer3_match1);
+ writel(0, &tmr->timer3_match2);
+
+ /* we don't want timer to issue interrupts */
+ writel(FTTMR010_TM3_MATCH1 |
+ FTTMR010_TM3_MATCH2 |
+ FTTMR010_TM3_OVERFLOW,
+ &tmr->interrupt_mask);
+
+ cr = readl(&tmr->cr);
+ cr |= FTTMR010_TM3_CLOCK; /* use external clock */
+ cr |= FTTMR010_TM3_ENABLE;
+ writel(cr, &tmr->cr);
+
+ gd->arch.tbl = 0;
+ gd->arch.tbu = 0;
+
+ return 0;
+}
+
+static const struct timer_ops fttmr010_timer_ops = {
+ .get_count = fttmr010_timer_get_count,
+};
+
+static const struct udevice_id fttmr010_timer_ids[] = {
+ { .compatible = "faraday,fttmr010-timer" },
+ {}
+};
+
+U_BOOT_DRIVER(fttmr010_timer) = {
+ .name = "fttmr010_timer",
+ .id = UCLASS_TIMER,
+ .of_match = fttmr010_timer_ids,
+ .priv_auto = sizeof(struct fttmr010_timer_priv),
+ .probe = fttmr010_timer_probe,
+ .ops = &fttmr010_timer_ops,
+};
diff --git a/drivers/timer/gxp-timer.c b/drivers/timer/gxp-timer.c
new file mode 100644
index 00000000000..6f316bc8c5c
--- /dev/null
+++ b/drivers/timer/gxp-timer.c
@@ -0,0 +1,64 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * GXP timer driver
+ *
+ * (C) Copyright 2022 Hewlett Packard Enterprise Development LP.
+ * Author: Nick Hawkins <nick.hawkins@hpe.com>
+ * Author: Jean-Marie Verdun <verdun@hpe.com>
+ */
+
+#include <clk.h>
+#include <dm.h>
+#include <timer.h>
+#include <asm/io.h>
+
+#define USTIMELO 0x18
+#define USTIMEHI 0x1C
+
+struct gxp_timer_priv {
+ void __iomem *base;
+};
+
+static u64 gxp_timer_get_count(struct udevice *dev)
+{
+ struct gxp_timer_priv *priv = dev_get_priv(dev);
+ u64 val;
+
+ val = readl(priv->base + USTIMEHI);
+ val = (val << 32) | readl(priv->base + USTIMELO);
+
+ return val;
+}
+
+static int gxp_timer_probe(struct udevice *dev)
+{
+ struct timer_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ struct gxp_timer_priv *priv = dev_get_priv(dev);
+
+ priv->base = dev_read_addr_ptr(dev);
+ if (!priv->base)
+ return -ENOENT;
+
+ uc_priv->clock_rate = 1000000;
+
+ return 0;
+}
+
+static const struct timer_ops gxp_timer_ops = {
+ .get_count = gxp_timer_get_count,
+};
+
+static const struct udevice_id gxp_timer_ids[] = {
+ { .compatible = "hpe,gxp-timer" },
+ {}
+};
+
+U_BOOT_DRIVER(gxp_timer) = {
+ .name = "gxp-timer",
+ .id = UCLASS_TIMER,
+ .of_match = gxp_timer_ids,
+ .priv_auto = sizeof(struct gxp_timer_priv),
+ .probe = gxp_timer_probe,
+ .ops = &gxp_timer_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/drivers/timer/imx-gpt-timer.c b/drivers/timer/imx-gpt-timer.c
new file mode 100644
index 00000000000..07b9fdb5e18
--- /dev/null
+++ b/drivers/timer/imx-gpt-timer.c
@@ -0,0 +1,162 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2021
+ * Author(s): Giulio Benetti <giulio.benetti@benettiengineering.com>
+ */
+
+#include <config.h>
+#include <clk.h>
+#include <dm.h>
+#include <fdtdec.h>
+#include <timer.h>
+#include <dm/device_compat.h>
+
+#include <asm/io.h>
+
+#define GPT_CR_EN BIT(0)
+#define GPT_CR_FRR BIT(9)
+#define GPT_CR_EN_24M BIT(10)
+#define GPT_CR_SWR BIT(15)
+
+#define GPT_PR_PRESCALER24M_MASK 0x0000F000
+#define GPT_PR_PRESCALER24M_SHIFT 12
+#define GPT_PR_PRESCALER24M_MAX (GPT_PR_PRESCALER24M_MASK >> GPT_PR_PRESCALER24M_SHIFT)
+#define GPT_PR_PRESCALER_MASK 0x00000FFF
+#define GPT_PR_PRESCALER_SHIFT 0
+#define GPT_PR_PRESCALER_MAX (GPT_PR_PRESCALER_MASK >> GPT_PR_PRESCALER_SHIFT)
+
+#define GPT_CLKSRC_IPG_CLK (1 << 6)
+#define GPT_CLKSRC_IPG_CLK_24M (5 << 6)
+
+/* If CFG_SYS_HZ_CLOCK not specified et's default to 3Mhz */
+#ifndef CFG_SYS_HZ_CLOCK
+#define CFG_SYS_HZ_CLOCK 3000000
+#endif
+
+struct imx_gpt_timer_regs {
+ u32 cr;
+ u32 pr;
+ u32 sr;
+ u32 ir;
+ u32 ocr1;
+ u32 ocr2;
+ u32 ocr3;
+ u32 icr1;
+ u32 icr2;
+ u32 cnt;
+};
+
+struct imx_gpt_timer_priv {
+ struct imx_gpt_timer_regs *base;
+};
+
+static u64 imx_gpt_timer_get_count(struct udevice *dev)
+{
+ struct imx_gpt_timer_priv *priv = dev_get_priv(dev);
+ struct imx_gpt_timer_regs *regs = priv->base;
+
+ return timer_conv_64(readl(&regs->cnt));
+}
+
+static int imx_gpt_setup(struct imx_gpt_timer_regs *regs, u32 rate)
+{
+ u32 prescaler = (rate / CFG_SYS_HZ_CLOCK) - 1;
+
+ /* Reset the timer */
+ setbits_le32(&regs->cr, GPT_CR_SWR);
+
+ /* Wait for timer to finish reset */
+ while (readl(&regs->cr) & GPT_CR_SWR)
+ ;
+
+ if (rate == 24000000UL) {
+ /* Set timer frequency if using 24M clock source */
+ if (prescaler > GPT_PR_PRESCALER24M_MAX)
+ return -EINVAL;
+
+ /* Set 24M prescaler */
+ writel((prescaler << GPT_PR_PRESCALER24M_SHIFT), &regs->pr);
+ /* Set Oscillator as clock source, enable 24M input and set gpt
+ * in free-running mode
+ */
+ writel(GPT_CLKSRC_IPG_CLK_24M | GPT_CR_EN_24M | GPT_CR_FRR, &regs->cr);
+ } else {
+ if (prescaler > GPT_PR_PRESCALER_MAX)
+ return -EINVAL;
+
+ /* Set prescaler */
+ writel((prescaler << GPT_PR_PRESCALER_SHIFT), &regs->pr);
+ /* Set Peripheral as clock source and set gpt in free-running
+ * mode
+ */
+ writel(GPT_CLKSRC_IPG_CLK | GPT_CR_FRR, &regs->cr);
+ }
+
+ /* Start timer */
+ setbits_le32(&regs->cr, GPT_CR_EN);
+
+ return 0;
+}
+
+static int imx_gpt_timer_probe(struct udevice *dev)
+{
+ struct timer_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ struct imx_gpt_timer_priv *priv = dev_get_priv(dev);
+ struct imx_gpt_timer_regs *regs;
+ struct clk clk;
+ fdt_addr_t addr;
+ u32 clk_rate;
+ int ret;
+
+ addr = dev_read_addr(dev);
+ if (addr == FDT_ADDR_T_NONE)
+ return -EINVAL;
+
+ priv->base = (struct imx_gpt_timer_regs *)addr;
+ regs = priv->base;
+
+ ret = clk_get_by_index(dev, 0, &clk);
+ if (ret < 0)
+ return ret;
+
+ ret = clk_enable(&clk);
+ if (ret) {
+ dev_err(dev, "Failed to enable clock\n");
+ return ret;
+ }
+
+ /* Get timer clock rate */
+ clk_rate = clk_get_rate(&clk);
+ if (clk_rate <= 0) {
+ dev_err(dev, "Could not get clock rate...\n");
+ return -EINVAL;
+ }
+
+ ret = imx_gpt_setup(regs, clk_rate);
+ if (ret) {
+ dev_err(dev, "Could not setup timer\n");
+ return ret;
+ }
+
+ uc_priv->clock_rate = CFG_SYS_HZ_CLOCK;
+
+ return 0;
+}
+
+static const struct timer_ops imx_gpt_timer_ops = {
+ .get_count = imx_gpt_timer_get_count,
+};
+
+static const struct udevice_id imx_gpt_timer_ids[] = {
+ { .compatible = "fsl,imxrt-gpt" },
+ {}
+};
+
+U_BOOT_DRIVER(imx_gpt_timer) = {
+ .name = "imx_gpt_timer",
+ .id = UCLASS_TIMER,
+ .of_match = imx_gpt_timer_ids,
+ .priv_auto = sizeof(struct imx_gpt_timer_priv),
+ .probe = imx_gpt_timer_probe,
+ .ops = &imx_gpt_timer_ops,
+};
diff --git a/drivers/timer/mchp-pit64b-timer.c b/drivers/timer/mchp-pit64b-timer.c
new file mode 100644
index 00000000000..1a5b2e6a0dc
--- /dev/null
+++ b/drivers/timer/mchp-pit64b-timer.c
@@ -0,0 +1,106 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * 64-bit Periodic Interval Timer driver
+ *
+ * Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries
+ *
+ * Author: Claudiu Beznea <claudiu.beznea@microchip.com>
+ */
+
+#include <clk.h>
+#include <dm.h>
+#include <timer.h>
+#include <asm/io.h>
+
+#define MCHP_PIT64B_CR 0x00 /* Control Register */
+#define MCHP_PIT64B_CR_START BIT(0)
+#define MCHP_PIT64B_CR_SWRST BIT(8)
+#define MCHP_PIT64B_MR 0x04 /* Mode Register */
+#define MCHP_PIT64B_MR_CONT BIT(0)
+#define MCHP_PIT64B_LSB_PR 0x08 /* LSB Period Register */
+#define MCHP_PIT64B_MSB_PR 0x0C /* MSB Period Register */
+#define MCHP_PIT64B_TLSBR 0x20 /* Timer LSB Register */
+#define MCHP_PIT64B_TMSBR 0x24 /* Timer MSB Register */
+
+struct mchp_pit64b_priv {
+ void __iomem *base;
+};
+
+static u64 mchp_pit64b_get_count(struct udevice *dev)
+{
+ struct mchp_pit64b_priv *priv = dev_get_priv(dev);
+
+ u32 lsb = readl(priv->base + MCHP_PIT64B_TLSBR);
+ u32 msb = readl(priv->base + MCHP_PIT64B_TMSBR);
+
+ return ((u64)msb << 32) | lsb;
+}
+
+static int mchp_pit64b_probe(struct udevice *dev)
+{
+ struct timer_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ struct mchp_pit64b_priv *priv = dev_get_priv(dev);
+ struct clk clk;
+ ulong rate;
+ int ret;
+
+ priv->base = dev_read_addr_ptr(dev);
+ if (IS_ERR(priv->base))
+ return PTR_ERR(priv->base);
+
+ ret = clk_get_by_index(dev, 0, &clk);
+ if (ret)
+ return ret;
+
+ ret = clk_enable(&clk);
+ if (ret)
+ return ret;
+
+ rate = clk_get_rate(&clk);
+ if (!rate) {
+ clk_disable(&clk);
+ return -ENOTSUPP;
+ }
+
+ /* Reset the timer in case it was used by previous bootloaders. */
+ writel(MCHP_PIT64B_CR_SWRST, priv->base + MCHP_PIT64B_CR);
+
+ /*
+ * Use highest prescaller (for a peripheral clock running at 200MHz
+ * this will lead to the timer running at 12.5MHz) and continuous mode.
+ */
+ writel((15 << 8) | MCHP_PIT64B_MR_CONT, priv->base + MCHP_PIT64B_MR);
+ uc_priv->clock_rate = rate / 16;
+
+ /*
+ * Simulate free running counter by setting max values to period
+ * registers.
+ */
+ writel(~0UL, priv->base + MCHP_PIT64B_MSB_PR);
+ writel(~0UL, priv->base + MCHP_PIT64B_LSB_PR);
+
+ /* Start the timer. */
+ writel(MCHP_PIT64B_CR_START, priv->base + MCHP_PIT64B_CR);
+
+ return 0;
+}
+
+static const struct timer_ops mchp_pit64b_ops = {
+ .get_count = mchp_pit64b_get_count,
+};
+
+static const struct udevice_id mchp_pit64b_ids[] = {
+ { .compatible = "microchip,sam9x60-pit64b", },
+ { .compatible = "microchip,sama7g5-pit64b", },
+ { }
+};
+
+U_BOOT_DRIVER(mchp_pit64b) = {
+ .name = "mchp-pit64b",
+ .id = UCLASS_TIMER,
+ .of_match = mchp_pit64b_ids,
+ .priv_auto = sizeof(struct mchp_pit64b_priv),
+ .probe = mchp_pit64b_probe,
+ .ops = &mchp_pit64b_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/drivers/timer/mpc83xx_timer.c b/drivers/timer/mpc83xx_timer.c
new file mode 100644
index 00000000000..f92009e4ccc
--- /dev/null
+++ b/drivers/timer/mpc83xx_timer.c
@@ -0,0 +1,253 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2018
+ * Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
+ */
+
+#include <config.h>
+#include <clk.h>
+#include <dm.h>
+#include <irq_func.h>
+#include <log.h>
+#include <status_led.h>
+#include <sysinfo.h>
+#include <time.h>
+#include <timer.h>
+#include <watchdog.h>
+#include <asm/global_data.h>
+#include <asm/ptrace.h>
+#include <linux/bitops.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#ifndef CFG_SYS_WATCHDOG_FREQ
+#define CFG_SYS_WATCHDOG_FREQ (CONFIG_SYS_HZ / 2)
+#endif
+
+/**
+ * struct mpc83xx_timer_priv - Private data structure for MPC83xx timer driver
+ * @decrementer_count: Value to which the decrementer register should be re-set
+ * to when a timer interrupt occurs, thus determines the
+ * interrupt frequency (value for 1e6/HZ microseconds)
+ * @timestamp: Counter for the number of timer interrupts that have
+ * occurred (i.e. can be used to trigger events
+ * periodically in the timer interrupt)
+ */
+struct mpc83xx_timer_priv {
+ uint decrementer_count;
+ ulong timestamp;
+};
+
+/*
+ * Bitmask for enabling the time base in the SPCR (System Priority
+ * Configuration Register)
+ */
+static const u32 SPCR_TBEN_MASK = BIT(31 - 9);
+
+/**
+ * get_dec() - Get the value of the decrementer register
+ *
+ * Return: The value of the decrementer register
+ */
+static inline unsigned long get_dec(void)
+{
+ unsigned long val;
+
+ asm volatile ("mfdec %0" : "=r" (val) : );
+
+ return val;
+}
+
+/**
+ * set_dec() - Set the value of the decrementer register
+ * @val: The value of the decrementer register to be set
+ */
+static inline void set_dec(unsigned long val)
+{
+ if (val)
+ asm volatile ("mtdec %0"::"r" (val));
+}
+
+/**
+ * mftbu() - Get value of TBU (upper time base) register
+ *
+ * Return: Value of the TBU register
+ */
+static inline u32 mftbu(void)
+{
+ u32 rval;
+
+ asm volatile("mftbu %0" : "=r" (rval));
+ return rval;
+}
+
+/**
+ * mftb() - Get value of TBL (lower time base) register
+ *
+ * Return: Value of the TBL register
+ */
+static inline u32 mftb(void)
+{
+ u32 rval;
+
+ asm volatile("mftb %0" : "=r" (rval));
+ return rval;
+}
+
+/*
+ * TODO(mario.six@gdsys.cc): This should really be done by timer_init, and the
+ * interrupt init should go into a interrupt driver.
+ */
+int interrupt_init(void)
+{
+ immap_t *immr = (immap_t *)CONFIG_SYS_IMMR;
+ struct udevice *csb;
+ struct udevice *sysinfo;
+ struct udevice *timer;
+ struct mpc83xx_timer_priv *timer_priv;
+ struct clk clock;
+ int ret;
+
+ ret = uclass_first_device_err(UCLASS_TIMER, &timer);
+ if (ret) {
+ debug("%s: Could not find timer device (error: %d)",
+ __func__, ret);
+ return ret;
+ }
+
+ timer_priv = dev_get_priv(timer);
+
+ if (sysinfo_get(&sysinfo)) {
+ debug("%s: sysinfo device could not be fetched.\n", __func__);
+ return -ENOENT;
+ }
+
+ ret = uclass_get_device_by_phandle(UCLASS_SIMPLE_BUS, sysinfo,
+ "csb", &csb);
+ if (ret) {
+ debug("%s: Could not retrieve CSB device (error: %d)",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = clk_get_by_index(csb, 0, &clock);
+ if (ret) {
+ debug("%s: Could not retrieve clock (error: %d)",
+ __func__, ret);
+ return ret;
+ }
+
+ timer_priv->decrementer_count = (clk_get_rate(&clock) / 4)
+ / CONFIG_SYS_HZ;
+ /* Enable e300 time base */
+ setbits_be32(&immr->sysconf.spcr, SPCR_TBEN_MASK);
+
+ set_dec(timer_priv->decrementer_count);
+
+ /* Switch on interrupts */
+ set_msr(get_msr() | MSR_EE);
+
+ return 0;
+}
+
+/**
+ * timer_interrupt() - Handler for the timer interrupt
+ * @regs: Array of register values
+ */
+void timer_interrupt(struct pt_regs *regs)
+{
+ struct udevice *timer = gd->timer;
+ struct mpc83xx_timer_priv *priv;
+
+ /*
+ * During initialization, gd->timer might not be set yet, but the timer
+ * interrupt may already be enabled. In this case, wait for the
+ * initialization to complete
+ */
+ if (!timer)
+ return;
+
+ priv = dev_get_priv(timer);
+
+ /* Restore Decrementer Count */
+ set_dec(priv->decrementer_count);
+
+ priv->timestamp++;
+
+#if defined(CONFIG_WATCHDOG) || defined(CONFIG_HW_WATCHDOG)
+ if (CFG_SYS_WATCHDOG_FREQ && (priv->timestamp % (CFG_SYS_WATCHDOG_FREQ)) == 0)
+ schedule();
+#endif /* CONFIG_WATCHDOG || CONFIG_HW_WATCHDOG */
+
+#ifdef CONFIG_LED_STATUS
+ status_led_tick(priv->timestamp);
+#endif /* CONFIG_LED_STATUS */
+}
+
+void wait_ticks(ulong ticks)
+{
+ ulong end = get_ticks() + ticks;
+
+ while (end > get_ticks())
+ schedule();
+}
+
+static u64 mpc83xx_timer_get_count(struct udevice *dev)
+{
+ u32 tbu, tbl;
+
+ /*
+ * To make sure that no tbl overflow occurred between reading tbl and
+ * tbu, read tbu again, and compare it with the previously read tbu
+ * value: If they're different, a tbl overflow has occurred.
+ */
+ do {
+ tbu = mftbu();
+ tbl = mftb();
+ } while (tbu != mftbu());
+
+ return (uint64_t)tbu << 32 | tbl;
+}
+
+static int mpc83xx_timer_probe(struct udevice *dev)
+{
+ struct timer_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ struct clk clock;
+ int ret;
+
+ ret = interrupt_init();
+ if (ret) {
+ debug("%s: interrupt_init failed (err = %d)\n",
+ dev->name, ret);
+ return ret;
+ }
+
+ ret = clk_get_by_index(dev, 0, &clock);
+ if (ret) {
+ debug("%s: Could not retrieve clock (err = %d)\n",
+ dev->name, ret);
+ return ret;
+ }
+
+ uc_priv->clock_rate = (clk_get_rate(&clock) + 3L) / 4L;
+
+ return 0;
+}
+
+static const struct timer_ops mpc83xx_timer_ops = {
+ .get_count = mpc83xx_timer_get_count,
+};
+
+static const struct udevice_id mpc83xx_timer_ids[] = {
+ { .compatible = "fsl,mpc83xx-timer" },
+ { /* sentinel */ }
+};
+
+U_BOOT_DRIVER(mpc83xx_timer) = {
+ .name = "mpc83xx_timer",
+ .id = UCLASS_TIMER,
+ .of_match = mpc83xx_timer_ids,
+ .probe = mpc83xx_timer_probe,
+ .ops = &mpc83xx_timer_ops,
+ .priv_auto = sizeof(struct mpc83xx_timer_priv),
+};
diff --git a/drivers/timer/mtk_timer.c b/drivers/timer/mtk_timer.c
new file mode 100644
index 00000000000..06deb23eb99
--- /dev/null
+++ b/drivers/timer/mtk_timer.c
@@ -0,0 +1,110 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * MediaTek timer driver
+ *
+ * Copyright (C) 2018 MediaTek Inc.
+ * Author: Ryder Lee <ryder.lee@mediatek.com>
+ */
+
+#include <clk.h>
+#include <dm.h>
+#include <timer.h>
+#include <asm/io.h>
+#include <linux/bitops.h>
+
+#define MTK_GPT4_OFFSET_V1 0x40
+#define MTK_GPT4_OFFSET_V2 0x80
+
+#define MTK_GPT_CON 0x0
+#define MTK_GPT_V1_CLK 0x4
+#define MTK_GPT_CNT 0x8
+
+#define GPT_ENABLE BIT(0)
+#define GPT_CLEAR BIT(1)
+#define GPT_V1_FREERUN GENMASK(5, 4)
+#define GPT_V2_FREERUN GENMASK(6, 5)
+
+enum mtk_gpt_ver {
+ MTK_GPT_V1,
+ MTK_GPT_V2
+};
+
+struct mtk_timer_priv {
+ void __iomem *base;
+ unsigned int gpt4_offset;
+};
+
+static u64 mtk_timer_get_count(struct udevice *dev)
+{
+ struct mtk_timer_priv *priv = dev_get_priv(dev);
+ u32 val = readl(priv->base + priv->gpt4_offset + MTK_GPT_CNT);
+
+ return timer_conv_64(val);
+}
+
+static int mtk_timer_probe(struct udevice *dev)
+{
+ struct timer_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ struct mtk_timer_priv *priv = dev_get_priv(dev);
+ struct clk clk, parent;
+ int ret, gpt_ver;
+
+ priv->base = dev_read_addr_ptr(dev);
+ gpt_ver = dev_get_driver_data(dev);
+
+ if (!priv->base)
+ return -ENOENT;
+
+ if (gpt_ver == MTK_GPT_V2) {
+ priv->gpt4_offset = MTK_GPT4_OFFSET_V2;
+
+ writel(GPT_V2_FREERUN | GPT_CLEAR | GPT_ENABLE,
+ priv->base + priv->gpt4_offset + MTK_GPT_CON);
+ } else {
+ priv->gpt4_offset = MTK_GPT4_OFFSET_V1;
+
+ writel(GPT_V1_FREERUN | GPT_CLEAR | GPT_ENABLE,
+ priv->base + priv->gpt4_offset + MTK_GPT_CON);
+ writel(0, priv->base + priv->gpt4_offset + MTK_GPT_V1_CLK);
+ }
+
+ ret = clk_get_by_index(dev, 0, &clk);
+ if (ret)
+ return ret;
+
+ ret = clk_get_by_index(dev, 1, &parent);
+ /* Skip setting the parent with dummy fixed-clock */
+ if (!ret && parent.dev->driver != DM_DRIVER_GET(fixed_clock)) {
+ ret = clk_set_parent(&clk, &parent);
+ if (ret)
+ return ret;
+ }
+
+ uc_priv->clock_rate = clk_get_rate(&clk);
+ if (!uc_priv->clock_rate)
+ return -EINVAL;
+
+ return 0;
+}
+
+static const struct timer_ops mtk_timer_ops = {
+ .get_count = mtk_timer_get_count,
+};
+
+static const struct udevice_id mtk_timer_ids[] = {
+ { .compatible = "mediatek,timer", .data = MTK_GPT_V1 },
+ { .compatible = "mediatek,mt6577-timer", .data = MTK_GPT_V1 },
+ { .compatible = "mediatek,mt7981-timer", .data = MTK_GPT_V2 },
+ { .compatible = "mediatek,mt7986-timer", .data = MTK_GPT_V2 },
+ { }
+};
+
+U_BOOT_DRIVER(mtk_timer) = {
+ .name = "mtk_timer",
+ .id = UCLASS_TIMER,
+ .of_match = mtk_timer_ids,
+ .priv_auto = sizeof(struct mtk_timer_priv),
+ .probe = mtk_timer_probe,
+ .ops = &mtk_timer_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/drivers/timer/nomadik-mtu-timer.c b/drivers/timer/nomadik-mtu-timer.c
new file mode 100644
index 00000000000..9a05582c0d5
--- /dev/null
+++ b/drivers/timer/nomadik-mtu-timer.c
@@ -0,0 +1,109 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2019 Stephan Gerhold <stephan@gerhold.net>
+ *
+ * Based on arch/arm/cpu/armv7/u8500/timer.c:
+ * Copyright (C) 2010 Linaro Limited
+ * John Rigby <john.rigby@linaro.org>
+ *
+ * Based on Linux kernel source and internal ST-Ericsson U-Boot source:
+ * Copyright (C) 2009 Alessandro Rubini
+ * Copyright (C) 2010 ST-Ericsson
+ * Copyright (C) 2010 Linus Walleij for ST-Ericsson
+ */
+
+#include <dm.h>
+#include <timer.h>
+#include <asm/io.h>
+#include <linux/bitops.h>
+
+#define MTU_NUM_TIMERS 4
+
+/* The timers */
+struct nomadik_mtu_timer_regs {
+ u32 lr; /* Load register */
+ u32 cv; /* Current value */
+ u32 cr; /* Control register */
+ u32 bglr; /* Background load register */
+};
+
+/* The MTU that contains the timers */
+struct nomadik_mtu_regs {
+ u32 imsc; /* Interrupt mask set/clear */
+ u32 ris; /* Raw interrupt status */
+ u32 mis; /* Masked interrupt status */
+ u32 icr; /* Interrupt clear register */
+
+ struct nomadik_mtu_timer_regs timers[MTU_NUM_TIMERS];
+};
+
+/* Bits for the control register */
+#define MTU_CR_ONESHOT BIT(0) /* if 0 = wraps reloading from BGLR */
+#define MTU_CR_32BITS BIT(1) /* if 0 = 16-bit counter */
+
+#define MTU_CR_PRESCALE_SHIFT 2
+#define MTU_CR_PRESCALE_1 (0 << MTU_CR_PRESCALE_SHIFT)
+#define MTU_CR_PRESCALE_16 (1 << MTU_CR_PRESCALE_SHIFT)
+#define MTU_CR_PRESCALE_256 (2 << MTU_CR_PRESCALE_SHIFT)
+
+#define MTU_CR_PERIODIC BIT(6) /* if 0 = free-running */
+#define MTU_CR_ENABLE BIT(7)
+
+struct nomadik_mtu_priv {
+ struct nomadik_mtu_timer_regs *timer;
+};
+
+static u64 nomadik_mtu_get_count(struct udevice *dev)
+{
+ struct nomadik_mtu_priv *priv = dev_get_priv(dev);
+
+ /* Decrementing counter: invert the value */
+ return timer_conv_64(~readl(&priv->timer->cv));
+}
+
+static int nomadik_mtu_probe(struct udevice *dev)
+{
+ struct timer_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ struct nomadik_mtu_priv *priv = dev_get_priv(dev);
+ struct nomadik_mtu_regs *mtu;
+ u32 prescale;
+
+ mtu = dev_read_addr_ptr(dev);
+ if (!mtu)
+ return -EINVAL;
+ priv->timer = mtu->timers; /* Use first timer */
+
+ if (!uc_priv->clock_rate)
+ return -EINVAL;
+
+ /* Use divide-by-16 counter if tick rate is more than 32 MHz */
+ if (uc_priv->clock_rate > 32000000) {
+ uc_priv->clock_rate /= 16;
+ prescale = MTU_CR_PRESCALE_16;
+ } else {
+ prescale = MTU_CR_PRESCALE_1;
+ }
+
+ /* Configure a free-running, auto-wrap counter with selected prescale */
+ writel(MTU_CR_ENABLE | prescale | MTU_CR_32BITS, &priv->timer->cr);
+
+ return 0;
+}
+
+static const struct timer_ops nomadik_mtu_ops = {
+ .get_count = nomadik_mtu_get_count,
+};
+
+static const struct udevice_id nomadik_mtu_ids[] = {
+ { .compatible = "st,nomadik-mtu" },
+ {}
+};
+
+U_BOOT_DRIVER(nomadik_mtu) = {
+ .name = "nomadik_mtu",
+ .id = UCLASS_TIMER,
+ .of_match = nomadik_mtu_ids,
+ .priv_auto = sizeof(struct nomadik_mtu_priv),
+ .probe = nomadik_mtu_probe,
+ .ops = &nomadik_mtu_ops,
+};
diff --git a/drivers/timer/npcm-timer.c b/drivers/timer/npcm-timer.c
new file mode 100644
index 00000000000..5627c2331ca
--- /dev/null
+++ b/drivers/timer/npcm-timer.c
@@ -0,0 +1,74 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2022 Nuvoton Technology Corp.
+ */
+
+#include <dm.h>
+#include <timer.h>
+#include <asm/io.h>
+
+#define NPCM_TIMER_CLOCK_RATE 25000000UL /* 25MHz */
+
+/* Register offsets */
+#define SECCNT 0x0 /* Seconds Counter Register */
+#define CNTR25M 0x4 /* 25MHz Counter Register */
+
+struct npcm_timer_priv {
+ void __iomem *base;
+};
+
+static u64 npcm_timer_get_count(struct udevice *dev)
+{
+ struct npcm_timer_priv *priv = dev_get_priv(dev);
+ u64 reg_sec, reg_25m;
+ u64 counter;
+
+ reg_sec = readl(priv->base + SECCNT);
+ reg_25m = readl(priv->base + CNTR25M);
+ /*
+ * When CNTR25M reaches 25M, it goes to 0 and SECCNT is increased by 1.
+ * When CNTR25M is zero, wait for CNTR25M to become non-zero in case
+ * SECCNT is not updated yet.
+ */
+ if (reg_25m == 0) {
+ while (reg_25m == 0)
+ reg_25m = readl(priv->base + CNTR25M);
+ reg_sec = readl(priv->base + SECCNT);
+ }
+ counter = reg_sec * NPCM_TIMER_CLOCK_RATE + reg_25m;
+
+ return counter;
+}
+
+static int npcm_timer_probe(struct udevice *dev)
+{
+ struct timer_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ struct npcm_timer_priv *priv = dev_get_priv(dev);
+
+ priv->base = dev_read_addr_ptr(dev);
+ if (!priv->base)
+ return -EINVAL;
+ uc_priv->clock_rate = NPCM_TIMER_CLOCK_RATE;
+
+ return 0;
+}
+
+static const struct timer_ops npcm_timer_ops = {
+ .get_count = npcm_timer_get_count,
+};
+
+static const struct udevice_id npcm_timer_ids[] = {
+ { .compatible = "nuvoton,npcm845-timer"},
+ { .compatible = "nuvoton,npcm750-timer"},
+ {}
+};
+
+U_BOOT_DRIVER(npcm_timer) = {
+ .name = "npcm_timer",
+ .id = UCLASS_TIMER,
+ .of_match = npcm_timer_ids,
+ .priv_auto = sizeof(struct npcm_timer_priv),
+ .probe = npcm_timer_probe,
+ .ops = &npcm_timer_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/drivers/timer/omap-timer.c b/drivers/timer/omap-timer.c
new file mode 100644
index 00000000000..fda6356fdba
--- /dev/null
+++ b/drivers/timer/omap-timer.c
@@ -0,0 +1,128 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * TI OMAP timer driver
+ *
+ * Copyright (C) 2015, Texas Instruments, Incorporated
+ */
+
+#include <dm.h>
+#include <errno.h>
+#include <timer.h>
+#include <asm/io.h>
+#include <asm/arch/clock.h>
+#include <asm/omap_common.h>
+#include <linux/bitops.h>
+
+/* Timer register bits */
+#define TCLR_START BIT(0) /* Start=1 */
+#define TCLR_AUTO_RELOAD BIT(1) /* Auto reload */
+#define TCLR_PRE_EN BIT(5) /* Pre-scaler enable */
+#define TCLR_PTV_SHIFT (2) /* Pre-scaler shift value */
+
+struct omap_gptimer_regs {
+ unsigned int tidr; /* offset 0x00 */
+ unsigned char res1[12];
+ unsigned int tiocp_cfg; /* offset 0x10 */
+ unsigned char res2[12];
+ unsigned int tier; /* offset 0x20 */
+ unsigned int tistatr; /* offset 0x24 */
+ unsigned int tistat; /* offset 0x28 */
+ unsigned int tisr; /* offset 0x2c */
+ unsigned int tcicr; /* offset 0x30 */
+ unsigned int twer; /* offset 0x34 */
+ unsigned int tclr; /* offset 0x38 */
+ unsigned int tcrr; /* offset 0x3c */
+ unsigned int tldr; /* offset 0x40 */
+ unsigned int ttgr; /* offset 0x44 */
+ unsigned int twpc; /* offset 0x48 */
+ unsigned int tmar; /* offset 0x4c */
+ unsigned int tcar1; /* offset 0x50 */
+ unsigned int tscir; /* offset 0x54 */
+ unsigned int tcar2; /* offset 0x58 */
+};
+
+/* Omap Timer Priv */
+struct omap_timer_priv {
+ struct omap_gptimer_regs *regs;
+};
+
+static u64 omap_timer_get_count(struct udevice *dev)
+{
+ struct omap_timer_priv *priv = dev_get_priv(dev);
+
+ return timer_conv_64(readl(&priv->regs->tcrr));
+}
+
+static int omap_timer_probe(struct udevice *dev)
+{
+ struct timer_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ struct omap_timer_priv *priv = dev_get_priv(dev);
+
+ if (!uc_priv->clock_rate)
+ uc_priv->clock_rate = V_SCLK;
+
+ uc_priv->clock_rate /= (2 << SYS_PTV);
+
+ /* start the counter ticking up, reload value on overflow */
+ writel(0, &priv->regs->tldr);
+ writel(0, &priv->regs->tcrr);
+ /* enable timer */
+ writel((SYS_PTV << 2) | TCLR_PRE_EN | TCLR_AUTO_RELOAD |
+ TCLR_START, &priv->regs->tclr);
+
+ return 0;
+}
+
+static int omap_timer_of_to_plat(struct udevice *dev)
+{
+ struct omap_timer_priv *priv = dev_get_priv(dev);
+
+ priv->regs = map_physmem(dev_read_addr(dev),
+ sizeof(struct omap_gptimer_regs), MAP_NOCACHE);
+
+ return 0;
+}
+
+#if CONFIG_IS_ENABLED(BOOTSTAGE)
+ulong timer_get_boot_us(void)
+{
+ u64 ticks = 0;
+ u32 rate = 1;
+ u64 us;
+ int ret;
+
+ ret = dm_timer_init();
+ if (!ret) {
+ /* The timer is available */
+ rate = timer_get_rate(gd->timer);
+ timer_get_count(gd->timer, &ticks);
+ } else {
+ return 0;
+ }
+
+ us = (ticks * 1000) / rate;
+ return us;
+}
+#endif
+
+static const struct timer_ops omap_timer_ops = {
+ .get_count = omap_timer_get_count,
+};
+
+static const struct udevice_id omap_timer_ids[] = {
+ { .compatible = "ti,am335x-timer" },
+ { .compatible = "ti,am4372-timer" },
+ { .compatible = "ti,omap5430-timer" },
+ { .compatible = "ti,am654-timer" },
+ {}
+};
+
+U_BOOT_DRIVER(omap_timer) = {
+ .name = "omap_timer",
+ .id = UCLASS_TIMER,
+ .of_match = omap_timer_ids,
+ .of_to_plat = omap_timer_of_to_plat,
+ .priv_auto = sizeof(struct omap_timer_priv),
+ .probe = omap_timer_probe,
+ .ops = &omap_timer_ops,
+};
diff --git a/drivers/timer/orion-timer.c b/drivers/timer/orion-timer.c
new file mode 100644
index 00000000000..821b681a232
--- /dev/null
+++ b/drivers/timer/orion-timer.c
@@ -0,0 +1,149 @@
+// SPDX-License-Identifier: GPL-2.0+
+#include <asm/io.h>
+#include <config.h>
+#include <div64.h>
+#include <dm/device.h>
+#include <dm/fdtaddr.h>
+#include <timer.h>
+
+#define TIMER_CTRL 0x00
+#define TIMER0_EN BIT(0)
+#define TIMER0_RELOAD_EN BIT(1)
+#define TIMER0_RELOAD 0x10
+#define TIMER0_VAL 0x14
+
+enum input_clock_type {
+ INPUT_CLOCK_NON_FIXED,
+ INPUT_CLOCK_25MHZ, /* input clock rate is fixed to 25MHz */
+};
+
+struct orion_timer_priv {
+ void *base;
+};
+
+#define MVEBU_TIMER_FIXED_RATE_25MHZ 25000000
+
+static bool early_init_done(void *base)
+{
+ if ((readl(base + TIMER_CTRL) & TIMER0_EN) &&
+ (readl(base + TIMER0_RELOAD) == ~0))
+ return true;
+ return false;
+}
+
+/* Common functions for early (boot) and DM based timer */
+static void orion_timer_init(void *base, enum input_clock_type type)
+{
+ /* Only init the timer once */
+ if (early_init_done(base))
+ return;
+
+ writel(~0, base + TIMER0_VAL);
+ writel(~0, base + TIMER0_RELOAD);
+
+ if (type == INPUT_CLOCK_25MHZ) {
+ /*
+ * On Armada XP / 38x ..., the 25MHz clock source needs to
+ * be enabled
+ */
+ setbits_le32(base + TIMER_CTRL, BIT(11));
+ }
+
+ /* enable timer */
+ setbits_le32(base + TIMER_CTRL, TIMER0_EN | TIMER0_RELOAD_EN);
+}
+
+static uint64_t orion_timer_get_count(void *base)
+{
+ return timer_conv_64(~readl(base + TIMER0_VAL));
+}
+
+/* Early (e.g. bootstage etc) timer functions */
+static void notrace timer_early_init(void)
+{
+ if (IS_ENABLED(CONFIG_ARCH_MVEBU))
+ orion_timer_init((void *)MVEBU_TIMER_BASE, INPUT_CLOCK_25MHZ);
+ else
+ orion_timer_init((void *)MVEBU_TIMER_BASE, INPUT_CLOCK_NON_FIXED);
+}
+
+/**
+ * timer_early_get_rate() - Get the timer rate before driver model
+ */
+unsigned long notrace timer_early_get_rate(void)
+{
+ timer_early_init();
+
+ if (IS_ENABLED(CONFIG_ARCH_MVEBU))
+ return MVEBU_TIMER_FIXED_RATE_25MHZ;
+ else
+ return CFG_SYS_TCLK;
+}
+
+/**
+ * timer_early_get_count() - Get the timer count before driver model
+ *
+ */
+u64 notrace timer_early_get_count(void)
+{
+ timer_early_init();
+
+ return orion_timer_get_count((void *)MVEBU_TIMER_BASE);
+}
+
+ulong timer_get_boot_us(void)
+{
+ u64 ticks;
+
+ ticks = timer_early_get_count();
+ return lldiv(ticks * 1000, timer_early_get_rate());
+}
+
+/* DM timer functions */
+static uint64_t dm_orion_timer_get_count(struct udevice *dev)
+{
+ struct orion_timer_priv *priv = dev_get_priv(dev);
+
+ return orion_timer_get_count(priv->base);
+}
+
+static int orion_timer_probe(struct udevice *dev)
+{
+ struct timer_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ enum input_clock_type type = dev_get_driver_data(dev);
+ struct orion_timer_priv *priv = dev_get_priv(dev);
+
+ priv->base = devfdt_remap_addr_index(dev, 0);
+ if (!priv->base) {
+ debug("unable to map registers\n");
+ return -ENOMEM;
+ }
+
+ if (type == INPUT_CLOCK_25MHZ)
+ uc_priv->clock_rate = MVEBU_TIMER_FIXED_RATE_25MHZ;
+ else
+ uc_priv->clock_rate = CFG_SYS_TCLK;
+ orion_timer_init(priv->base, type);
+
+ return 0;
+}
+
+static const struct timer_ops orion_timer_ops = {
+ .get_count = dm_orion_timer_get_count,
+};
+
+static const struct udevice_id orion_timer_ids[] = {
+ { .compatible = "marvell,orion-timer", .data = INPUT_CLOCK_NON_FIXED },
+ { .compatible = "marvell,armada-370-timer", .data = INPUT_CLOCK_25MHZ },
+ { .compatible = "marvell,armada-xp-timer", .data = INPUT_CLOCK_25MHZ },
+ {}
+};
+
+U_BOOT_DRIVER(orion_timer) = {
+ .name = "orion_timer",
+ .id = UCLASS_TIMER,
+ .of_match = orion_timer_ids,
+ .probe = orion_timer_probe,
+ .ops = &orion_timer_ops,
+ .priv_auto = sizeof(struct orion_timer_priv),
+};
diff --git a/drivers/timer/ostm_timer.c b/drivers/timer/ostm_timer.c
new file mode 100644
index 00000000000..314f956cdfb
--- /dev/null
+++ b/drivers/timer/ostm_timer.c
@@ -0,0 +1,91 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Renesas RZ/A1 R7S72100 OSTM Timer driver
+ *
+ * Copyright (C) 2019 Marek Vasut <marek.vasut@gmail.com>
+ */
+
+#include <clock_legacy.h>
+#include <malloc.h>
+#include <asm/global_data.h>
+#include <asm/io.h>
+#include <dm.h>
+#include <clk.h>
+#include <timer.h>
+#include <linux/bitops.h>
+
+#define OSTM_CMP 0x00
+#define OSTM_CNT 0x04
+#define OSTM_TE 0x10
+#define OSTM_TS 0x14
+#define OSTM_TT 0x18
+#define OSTM_CTL 0x20
+#define OSTM_CTL_D BIT(1)
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct ostm_priv {
+ fdt_addr_t regs;
+};
+
+static u64 ostm_get_count(struct udevice *dev)
+{
+ struct ostm_priv *priv = dev_get_priv(dev);
+
+ return timer_conv_64(readl(priv->regs + OSTM_CNT));
+}
+
+static int ostm_probe(struct udevice *dev)
+{
+ struct timer_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ struct ostm_priv *priv = dev_get_priv(dev);
+#if CONFIG_IS_ENABLED(CLK)
+ struct clk clk;
+ int ret;
+
+ ret = clk_get_by_index(dev, 0, &clk);
+ if (ret)
+ return ret;
+
+ uc_priv->clock_rate = clk_get_rate(&clk);
+#else
+ uc_priv->clock_rate = get_board_sys_clk() / 2;
+#endif
+
+ readb(priv->regs + OSTM_CTL);
+ writeb(OSTM_CTL_D, priv->regs + OSTM_CTL);
+
+ setbits_8(priv->regs + OSTM_TT, BIT(0));
+ writel(0xffffffff, priv->regs + OSTM_CMP);
+ setbits_8(priv->regs + OSTM_TS, BIT(0));
+
+ return 0;
+}
+
+static int ostm_of_to_plat(struct udevice *dev)
+{
+ struct ostm_priv *priv = dev_get_priv(dev);
+
+ priv->regs = dev_read_addr(dev);
+
+ return 0;
+}
+
+static const struct timer_ops ostm_ops = {
+ .get_count = ostm_get_count,
+};
+
+static const struct udevice_id ostm_ids[] = {
+ { .compatible = "renesas,ostm" },
+ {}
+};
+
+U_BOOT_DRIVER(ostm_timer) = {
+ .name = "ostm-timer",
+ .id = UCLASS_TIMER,
+ .ops = &ostm_ops,
+ .probe = ostm_probe,
+ .of_match = ostm_ids,
+ .of_to_plat = ostm_of_to_plat,
+ .priv_auto = sizeof(struct ostm_priv),
+};
diff --git a/drivers/timer/riscv_aclint_timer.c b/drivers/timer/riscv_aclint_timer.c
new file mode 100644
index 00000000000..35da1ea2fd2
--- /dev/null
+++ b/drivers/timer/riscv_aclint_timer.c
@@ -0,0 +1,97 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2020, Sean Anderson <seanga2@gmail.com>
+ * Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com>
+ */
+
+#include <config.h>
+#include <clk.h>
+#include <div64.h>
+#include <dm.h>
+#include <timer.h>
+#include <asm/io.h>
+#include <dm/device-internal.h>
+#include <linux/err.h>
+
+#define CLINT_MTIME_OFFSET 0xbff8
+#define ACLINT_MTIME_OFFSET 0
+
+/* mtime register */
+#define MTIME_REG(base, offset) ((ulong)(base) + (offset))
+
+static u64 notrace riscv_aclint_timer_get_count(struct udevice *dev)
+{
+ return readq((void __iomem *)MTIME_REG(dev_get_priv(dev),
+ dev_get_driver_data(dev)));
+}
+
+#if CONFIG_IS_ENABLED(RISCV_MMODE) && IS_ENABLED(CONFIG_TIMER_EARLY)
+/**
+ * timer_early_get_rate() - Get the timer rate before driver model
+ */
+unsigned long notrace timer_early_get_rate(void)
+{
+ return RISCV_MMODE_TIMER_FREQ;
+}
+
+/**
+ * timer_early_get_count() - Get the timer count before driver model
+ *
+ */
+u64 notrace timer_early_get_count(void)
+{
+ return readq((void __iomem *)MTIME_REG(RISCV_MMODE_TIMERBASE,
+ RISCV_MMODE_TIMEROFF));
+}
+#endif
+
+#if CONFIG_IS_ENABLED(RISCV_MMODE) && CONFIG_IS_ENABLED(BOOTSTAGE)
+ulong timer_get_boot_us(void)
+{
+ int ret;
+ u64 ticks = 0;
+ u32 rate;
+
+ ret = dm_timer_init();
+ if (!ret) {
+ rate = timer_get_rate(gd->timer);
+ timer_get_count(gd->timer, &ticks);
+ } else {
+ rate = RISCV_MMODE_TIMER_FREQ;
+ ticks = readq((void __iomem *)MTIME_REG(RISCV_MMODE_TIMERBASE,
+ RISCV_MMODE_TIMEROFF));
+ }
+
+ /* Below is converted from time(us) = (tick / rate) * 10000000 */
+ return lldiv(ticks * 1000, (rate / 1000));
+}
+#endif
+
+static const struct timer_ops riscv_aclint_timer_ops = {
+ .get_count = riscv_aclint_timer_get_count,
+};
+
+static int riscv_aclint_timer_probe(struct udevice *dev)
+{
+ dev_set_priv(dev, dev_read_addr_ptr(dev));
+ if (!dev_get_priv(dev))
+ return -EINVAL;
+
+ return timer_timebase_fallback(dev);
+}
+
+static const struct udevice_id riscv_aclint_timer_ids[] = {
+ { .compatible = "riscv,clint0", .data = CLINT_MTIME_OFFSET },
+ { .compatible = "sifive,clint0", .data = CLINT_MTIME_OFFSET },
+ { .compatible = "riscv,aclint-mtimer", .data = ACLINT_MTIME_OFFSET },
+ { }
+};
+
+U_BOOT_DRIVER(riscv_aclint_timer) = {
+ .name = "riscv_aclint_timer",
+ .id = UCLASS_TIMER,
+ .of_match = riscv_aclint_timer_ids,
+ .probe = riscv_aclint_timer_probe,
+ .ops = &riscv_aclint_timer_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/drivers/timer/riscv_timer.c b/drivers/timer/riscv_timer.c
new file mode 100644
index 00000000000..1f4980ceb38
--- /dev/null
+++ b/drivers/timer/riscv_timer.c
@@ -0,0 +1,119 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2020, Sean Anderson <seanga2@gmail.com>
+ * Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com>
+ * Copyright (C) 2018, Anup Patel <anup@brainfault.org>
+ * Copyright (C) 2012 Regents of the University of California
+ *
+ * RISC-V architecturally-defined generic timer driver
+ *
+ * This driver provides generic timer support for S-mode U-Boot.
+ */
+
+#include <config.h>
+#include <div64.h>
+#include <dm.h>
+#include <errno.h>
+#include <fdt_support.h>
+#include <timer.h>
+#include <asm/csr.h>
+
+static u64 notrace riscv_timer_get_count(struct udevice *dev)
+{
+ __maybe_unused u32 hi, lo;
+
+ if (IS_ENABLED(CONFIG_64BIT))
+ return csr_read(CSR_TIME);
+
+ do {
+ hi = csr_read(CSR_TIMEH);
+ lo = csr_read(CSR_TIME);
+ } while (hi != csr_read(CSR_TIMEH));
+
+ return ((u64)hi << 32) | lo;
+}
+
+#if CONFIG_IS_ENABLED(RISCV_SMODE) && IS_ENABLED(CONFIG_TIMER_EARLY)
+/**
+ * timer_early_get_rate() - Get the timer rate before driver model
+ */
+unsigned long notrace timer_early_get_rate(void)
+{
+ return RISCV_SMODE_TIMER_FREQ;
+}
+
+/**
+ * timer_early_get_count() - Get the timer count before driver model
+ *
+ */
+u64 notrace timer_early_get_count(void)
+{
+ return riscv_timer_get_count(NULL);
+}
+#endif
+
+#if CONFIG_IS_ENABLED(RISCV_SMODE) && CONFIG_IS_ENABLED(BOOTSTAGE)
+ulong timer_get_boot_us(void)
+{
+ int ret;
+ u64 ticks = 0;
+ u32 rate;
+
+ ret = dm_timer_init();
+ if (!ret) {
+ rate = timer_get_rate(gd->timer);
+ timer_get_count(gd->timer, &ticks);
+ } else {
+ rate = RISCV_SMODE_TIMER_FREQ;
+ ticks = riscv_timer_get_count(NULL);
+ }
+
+ /* Below is converted from time(us) = (tick / rate) * 10000000 */
+ return lldiv(ticks * 1000, (rate / 1000));
+}
+#endif
+
+static int riscv_timer_probe(struct udevice *dev)
+{
+ struct timer_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ u32 rate;
+
+ /* When this function was called from the CPU driver, clock
+ * frequency is passed as driver data.
+ */
+ rate = dev->driver_data;
+
+ /* When called from an FDT match, the rate needs to be looked up. */
+ if (!rate && gd->fdt_blob) {
+ rate = fdt_getprop_u32_default(gd->fdt_blob,
+ "/cpus", "timebase-frequency", 0);
+ }
+
+ uc_priv->clock_rate = rate;
+
+ /* With rate==0, timer uclass post_probe might later fail with -EINVAL.
+ * Give a hint at the cause for debugging.
+ */
+ if (!rate)
+ log_err("riscv_timer_probe with invalid clock rate 0!\n");
+
+ return 0;
+}
+
+static const struct timer_ops riscv_timer_ops = {
+ .get_count = riscv_timer_get_count,
+};
+
+static const struct udevice_id riscv_timer_ids[] = {
+ { .compatible = "riscv,timer", },
+ { }
+};
+
+U_BOOT_DRIVER(riscv_timer) = {
+ .name = "riscv_timer",
+ .id = UCLASS_TIMER,
+ .of_match = of_match_ptr(riscv_timer_ids),
+ .probe = riscv_timer_probe,
+ .ops = &riscv_timer_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/drivers/timer/rockchip_timer.c b/drivers/timer/rockchip_timer.c
new file mode 100644
index 00000000000..96c010f4dcc
--- /dev/null
+++ b/drivers/timer/rockchip_timer.c
@@ -0,0 +1,169 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2017 Theobroma Systems Design und Consulting GmbH
+ */
+
+#include <bootstage.h>
+#include <dm.h>
+#include <init.h>
+#include <log.h>
+#include <asm/global_data.h>
+#include <dm/ofnode.h>
+#include <mapmem.h>
+#include <asm/arch-rockchip/timer.h>
+#include <dt-structs.h>
+#include <timer.h>
+#include <asm/io.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#if CONFIG_IS_ENABLED(OF_PLATDATA)
+struct rockchip_timer_plat {
+ struct dtd_rockchip_rk3288_timer dtd;
+};
+#endif
+
+/* Driver private data. Contains timer id. Could be either 0 or 1. */
+struct rockchip_timer_priv {
+ struct rk_timer *timer;
+};
+
+static inline int64_t rockchip_timer_get_curr_value(struct rk_timer *timer)
+{
+ uint64_t timebase_h, timebase_l;
+ uint64_t cntr;
+
+ timebase_l = readl(&timer->timer_curr_value0);
+ timebase_h = readl(&timer->timer_curr_value1);
+
+ cntr = timebase_h << 32 | timebase_l;
+ return cntr;
+}
+
+#if CONFIG_IS_ENABLED(BOOTSTAGE)
+ulong timer_get_boot_us(void)
+{
+ uint64_t ticks = 0;
+ uint32_t rate;
+ uint64_t us;
+ int ret;
+
+ ret = dm_timer_init();
+
+ if (!ret) {
+ /* The timer is available */
+ rate = timer_get_rate(gd->timer);
+ timer_get_count(gd->timer, &ticks);
+ } else if (CONFIG_IS_ENABLED(OF_REAL) && ret == -EAGAIN) {
+ /* We have been called so early that the DM is not ready,... */
+ ofnode node = offset_to_ofnode(-1);
+ struct rk_timer *timer = NULL;
+
+ /*
+ * ... so we try to access the raw timer, if it is specified
+ * via the tick-timer property in /chosen.
+ */
+ node = ofnode_get_chosen_node("tick-timer");
+ if (!ofnode_valid(node)) {
+ debug("%s: no /chosen/tick-timer\n", __func__);
+ return 0;
+ }
+
+ timer = (struct rk_timer *)ofnode_get_addr(node);
+
+ /* This timer is down-counting */
+ ticks = ~0uLL - rockchip_timer_get_curr_value(timer);
+ if (ofnode_read_u32(node, "clock-frequency", &rate)) {
+ debug("%s: could not read clock-frequency\n", __func__);
+ return 0;
+ }
+ } else {
+ return 0;
+ }
+
+ us = (ticks * 1000) / rate;
+ return us;
+}
+#endif
+
+static u64 rockchip_timer_get_count(struct udevice *dev)
+{
+ struct rockchip_timer_priv *priv = dev_get_priv(dev);
+ uint64_t cntr = rockchip_timer_get_curr_value(priv->timer);
+
+ /* timers are down-counting */
+ return ~0ull - cntr;
+}
+
+static int rockchip_clk_of_to_plat(struct udevice *dev)
+{
+ if (CONFIG_IS_ENABLED(OF_REAL)) {
+ struct rockchip_timer_priv *priv = dev_get_priv(dev);
+
+ priv->timer = dev_read_addr_ptr(dev);
+ if (!priv->timer)
+ return -ENOENT;
+ }
+
+ return 0;
+}
+
+static int rockchip_timer_start(struct udevice *dev)
+{
+ struct rockchip_timer_priv *priv = dev_get_priv(dev);
+ const uint64_t reload_val = ~0uLL;
+ const uint32_t reload_val_l = reload_val & 0xffffffff;
+ const uint32_t reload_val_h = reload_val >> 32;
+
+ /* don't reinit, if the timer is already running and set up */
+ if ((readl(&priv->timer->timer_ctrl_reg) & 1) == 1 &&
+ (readl(&priv->timer->timer_load_count0) == reload_val_l) &&
+ (readl(&priv->timer->timer_load_count1) == reload_val_h))
+ return 0;
+
+ /* disable timer and reset all control */
+ writel(0, &priv->timer->timer_ctrl_reg);
+ /* write reload value */
+ writel(reload_val_l, &priv->timer->timer_load_count0);
+ writel(reload_val_h, &priv->timer->timer_load_count1);
+ /* enable timer */
+ writel(1, &priv->timer->timer_ctrl_reg);
+
+ return 0;
+}
+
+static int rockchip_timer_probe(struct udevice *dev)
+{
+#if CONFIG_IS_ENABLED(OF_PLATDATA)
+ struct timer_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ struct rockchip_timer_priv *priv = dev_get_priv(dev);
+ struct rockchip_timer_plat *plat = dev_get_plat(dev);
+
+ priv->timer = map_sysmem(plat->dtd.reg[0], plat->dtd.reg[1]);
+ uc_priv->clock_rate = plat->dtd.clock_frequency;
+#endif
+
+ return rockchip_timer_start(dev);
+}
+
+static const struct timer_ops rockchip_timer_ops = {
+ .get_count = rockchip_timer_get_count,
+};
+
+static const struct udevice_id rockchip_timer_ids[] = {
+ { .compatible = "rockchip,rk3288-timer" },
+ {}
+};
+
+U_BOOT_DRIVER(rockchip_rk3288_timer) = {
+ .name = "rockchip_rk3288_timer",
+ .id = UCLASS_TIMER,
+ .of_match = rockchip_timer_ids,
+ .probe = rockchip_timer_probe,
+ .ops = &rockchip_timer_ops,
+ .priv_auto = sizeof(struct rockchip_timer_priv),
+#if CONFIG_IS_ENABLED(OF_PLATDATA)
+ .plat_auto = sizeof(struct rockchip_timer_plat),
+#endif
+ .of_to_plat = rockchip_clk_of_to_plat,
+};
diff --git a/drivers/timer/sandbox_timer.c b/drivers/timer/sandbox_timer.c
new file mode 100644
index 00000000000..c1baf3c69ec
--- /dev/null
+++ b/drivers/timer/sandbox_timer.c
@@ -0,0 +1,77 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2015 Thomas Chou <thomas@wytron.com.tw>
+ */
+
+#include <dm.h>
+#include <errno.h>
+#include <timer.h>
+#include <os.h>
+
+#define SANDBOX_TIMER_RATE 1000000
+
+/* system timer offset in ms */
+static unsigned long sandbox_timer_offset;
+
+void timer_test_add_offset(unsigned long offset)
+{
+ sandbox_timer_offset += offset;
+}
+
+ulong timer_test_get_offset(void)
+{
+ return sandbox_timer_offset;
+};
+
+u64 notrace timer_early_get_count(void)
+{
+ return os_get_nsec() / 1000 + sandbox_timer_offset * 1000;
+}
+
+unsigned long notrace timer_early_get_rate(void)
+{
+ return SANDBOX_TIMER_RATE;
+}
+
+static notrace u64 sandbox_timer_get_count(struct udevice *dev)
+{
+ return timer_early_get_count();
+}
+
+static int sandbox_timer_probe(struct udevice *dev)
+{
+ struct timer_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+
+ if (CONFIG_IS_ENABLED(CPU) &&
+ dev_read_bool(dev, "sandbox,timebase-frequency-fallback"))
+ return timer_timebase_fallback(dev);
+ else if (!uc_priv->clock_rate)
+ uc_priv->clock_rate = SANDBOX_TIMER_RATE;
+
+ return 0;
+}
+
+static const struct timer_ops sandbox_timer_ops = {
+ .get_count = sandbox_timer_get_count,
+};
+
+static const struct udevice_id sandbox_timer_ids[] = {
+ { .compatible = "sandbox,timer" },
+ { }
+};
+
+U_BOOT_DRIVER(sandbox_timer) = {
+ .name = "sandbox_timer",
+ .id = UCLASS_TIMER,
+ .of_match = sandbox_timer_ids,
+ .probe = sandbox_timer_probe,
+ .ops = &sandbox_timer_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+};
+
+/* This is here in case we don't have a device tree */
+#if !CONFIG_IS_ENABLED(OF_PLATDATA)
+U_BOOT_DRVINFO(sandbox_timer_non_fdt) = {
+ .name = "sandbox_timer",
+};
+#endif
diff --git a/drivers/timer/sp804_timer.c b/drivers/timer/sp804_timer.c
new file mode 100644
index 00000000000..3e57f4b98ba
--- /dev/null
+++ b/drivers/timer/sp804_timer.c
@@ -0,0 +1,106 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * ARM PrimeCell Dual-Timer Module (SP804) driver
+ * Copyright (C) 2022 Arm Ltd.
+ */
+
+#include <clk.h>
+#include <dm.h>
+#include <init.h>
+#include <log.h>
+#include <asm/global_data.h>
+#include <dm/ofnode.h>
+#include <mapmem.h>
+#include <dt-structs.h>
+#include <timer.h>
+#include <asm/io.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define SP804_TIMERX_LOAD 0x00
+#define SP804_TIMERX_VALUE 0x04
+#define SP804_TIMERX_CONTROL 0x08
+
+#define SP804_CTRL_TIMER_ENABLE (1U << 7)
+#define SP804_CTRL_TIMER_PERIODIC (1U << 6)
+#define SP804_CTRL_INT_ENABLE (1U << 5)
+#define SP804_CTRL_TIMER_PRESCALE_SHIFT 2
+#define SP804_CTRL_TIMER_PRESCALE_MASK (3U << SP804_CTRL_TIMER_PRESCALE_SHIFT)
+#define SP804_CTRL_TIMER_32BIT (1U << 1)
+#define SP804_CTRL_ONESHOT (1U << 0)
+
+struct sp804_timer_plat {
+ uintptr_t base;
+};
+
+static u64 sp804_timer_get_count(struct udevice *dev)
+{
+ struct sp804_timer_plat *plat = dev_get_plat(dev);
+ uint32_t cntr = readl(plat->base + SP804_TIMERX_VALUE);
+
+ /* timers are down-counting */
+ return ~0u - cntr;
+}
+
+static int sp804_clk_of_to_plat(struct udevice *dev)
+{
+ struct sp804_timer_plat *plat = dev_get_plat(dev);
+
+ plat->base = dev_read_addr(dev);
+ if (!plat->base)
+ return -ENOENT;
+
+ return 0;
+}
+
+static int sp804_timer_probe(struct udevice *dev)
+{
+ struct sp804_timer_plat *plat = dev_get_plat(dev);
+ struct timer_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ struct clk base_clk;
+ unsigned int divider = 1;
+ uint32_t ctlr;
+ int ret;
+
+ ctlr = readl(plat->base + SP804_TIMERX_CONTROL);
+ ctlr &= SP804_CTRL_TIMER_PRESCALE_MASK;
+ switch (ctlr >> SP804_CTRL_TIMER_PRESCALE_SHIFT) {
+ case 0x0: divider = 1; break;
+ case 0x1: divider = 16; break;
+ case 0x2: divider = 256; break;
+ case 0x3: printf("illegal!\n"); break;
+ }
+
+ ret = clk_get_by_index(dev, 0, &base_clk);
+ if (ret) {
+ printf("could not find SP804 timer base clock in DT\n");
+ return ret;
+ }
+
+ uc_priv->clock_rate = clk_get_rate(&base_clk) / divider;
+
+ /* keep divider, free-running, wrapping, no IRQs, 32-bit mode */
+ ctlr |= SP804_CTRL_TIMER_32BIT | SP804_CTRL_TIMER_ENABLE;
+ writel(ctlr, plat->base + SP804_TIMERX_CONTROL);
+
+ return 0;
+}
+
+static const struct timer_ops sp804_timer_ops = {
+ .get_count = sp804_timer_get_count,
+};
+
+static const struct udevice_id sp804_timer_ids[] = {
+ { .compatible = "arm,sp804" },
+ {}
+};
+
+U_BOOT_DRIVER(arm_sp804_timer) = {
+ .name = "arm_sp804_timer",
+ .id = UCLASS_TIMER,
+ .of_match = sp804_timer_ids,
+ .probe = sp804_timer_probe,
+ .ops = &sp804_timer_ops,
+ .plat_auto = sizeof(struct sp804_timer_plat),
+ .of_to_plat = sp804_clk_of_to_plat,
+};
diff --git a/drivers/timer/starfive-timer.c b/drivers/timer/starfive-timer.c
new file mode 100644
index 00000000000..6b79c8858b5
--- /dev/null
+++ b/drivers/timer/starfive-timer.c
@@ -0,0 +1,95 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2022 StarFive, Inc. All rights reserved.
+ * Author: Kuan Lim Lee <kuanlim.lee@starfivetech.com>
+ */
+
+#include <clk.h>
+#include <dm.h>
+#include <time.h>
+#include <timer.h>
+#include <asm/io.h>
+#include <dm/device-internal.h>
+#include <linux/err.h>
+
+#define STF_TIMER_INT_STATUS 0x00
+#define STF_TIMER_CTL 0x04
+#define STF_TIMER_LOAD 0x08
+#define STF_TIMER_ENABLE 0x10
+#define STF_TIMER_RELOAD 0x14
+#define STF_TIMER_VALUE 0x18
+#define STF_TIMER_INT_CLR 0x20
+#define STF_TIMER_INT_MASK 0x24
+
+struct starfive_timer_priv {
+ void __iomem *base;
+ u32 timer_size;
+};
+
+static u64 notrace starfive_get_count(struct udevice *dev)
+{
+ struct starfive_timer_priv *priv = dev_get_priv(dev);
+
+ /* Read decrement timer value and convert to increment value */
+ return priv->timer_size - readl(priv->base + STF_TIMER_VALUE);
+}
+
+static const struct timer_ops starfive_ops = {
+ .get_count = starfive_get_count,
+};
+
+static int starfive_probe(struct udevice *dev)
+{
+ struct timer_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ struct starfive_timer_priv *priv = dev_get_priv(dev);
+ int timer_channel;
+ struct clk clk;
+ int ret;
+
+ priv->base = dev_read_addr_ptr(dev);
+ if (!priv->base)
+ return -EINVAL;
+
+ timer_channel = dev_read_u32_default(dev, "channel", 0);
+ priv->base = priv->base + (0x40 * timer_channel);
+
+ /* Get clock rate from channel selectecd*/
+ ret = clk_get_by_index(dev, timer_channel, &clk);
+ if (ret)
+ return ret;
+
+ ret = clk_enable(&clk);
+ if (ret)
+ return ret;
+ uc_priv->clock_rate = clk_get_rate(&clk);
+
+ /*
+ * Initiate timer, channel 0
+ * Unmask Interrupt Mask
+ */
+ writel(0, priv->base + STF_TIMER_INT_MASK);
+ /* Single run mode Setting */
+ if (dev_read_bool(dev, "single-run"))
+ writel(1, priv->base + STF_TIMER_CTL);
+ /* Set Reload value */
+ priv->timer_size = dev_read_u32_default(dev, "timer-size", -1U);
+ writel(priv->timer_size, priv->base + STF_TIMER_LOAD);
+ /* Enable to start timer */
+ writel(1, priv->base + STF_TIMER_ENABLE);
+
+ return 0;
+}
+
+static const struct udevice_id starfive_ids[] = {
+ { .compatible = "starfive,jh8100-timers" },
+ { }
+};
+
+U_BOOT_DRIVER(jh8100_starfive_timer) = {
+ .name = "starfive_timer",
+ .id = UCLASS_TIMER,
+ .of_match = starfive_ids,
+ .probe = starfive_probe,
+ .ops = &starfive_ops,
+ .priv_auto = sizeof(struct starfive_timer_priv),
+};
diff --git a/drivers/timer/stm32_timer.c b/drivers/timer/stm32_timer.c
new file mode 100644
index 00000000000..1dc21c5c1be
--- /dev/null
+++ b/drivers/timer/stm32_timer.c
@@ -0,0 +1,137 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2018, STMicroelectronics - All Rights Reserved
+ * Author(s): Patrice Chotard, <patrice.chotard@foss.st.com> for STMicroelectronics.
+ */
+
+#define LOG_CATEGORY UCLASS_TIMER
+
+#include <config.h>
+#include <clk.h>
+#include <dm.h>
+#include <fdtdec.h>
+#include <timer.h>
+#include <dm/device_compat.h>
+#include <linux/bitops.h>
+
+#include <asm/io.h>
+
+/* Timer control1 register */
+#define CR1_CEN BIT(0)
+#define CR1_ARPE BIT(7)
+
+/* Event Generation Register register */
+#define EGR_UG BIT(0)
+
+/* Auto reload register for free running config */
+#define GPT_FREE_RUNNING 0xFFFFFFFF
+
+struct stm32_timer_regs {
+ u32 cr1;
+ u32 cr2;
+ u32 smcr;
+ u32 dier;
+ u32 sr;
+ u32 egr;
+ u32 ccmr1;
+ u32 ccmr2;
+ u32 ccer;
+ u32 cnt;
+ u32 psc;
+ u32 arr;
+ u32 reserved;
+ u32 ccr1;
+ u32 ccr2;
+ u32 ccr3;
+ u32 ccr4;
+ u32 reserved1;
+ u32 dcr;
+ u32 dmar;
+ u32 tim2_5_or;
+};
+
+struct stm32_timer_priv {
+ struct stm32_timer_regs *base;
+};
+
+static u64 stm32_timer_get_count(struct udevice *dev)
+{
+ struct stm32_timer_priv *priv = dev_get_priv(dev);
+ struct stm32_timer_regs *regs = priv->base;
+
+ return readl(&regs->cnt);
+}
+
+static int stm32_timer_probe(struct udevice *dev)
+{
+ struct timer_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ struct stm32_timer_priv *priv = dev_get_priv(dev);
+ struct stm32_timer_regs *regs;
+ struct clk clk;
+ fdt_addr_t addr;
+ int ret;
+ u32 rate, psc;
+
+ addr = dev_read_addr(dev);
+ if (addr == FDT_ADDR_T_NONE)
+ return -EINVAL;
+
+ priv->base = (struct stm32_timer_regs *)addr;
+
+ ret = clk_get_by_index(dev, 0, &clk);
+ if (ret < 0)
+ return ret;
+
+ ret = clk_enable(&clk);
+ if (ret) {
+ dev_err(dev, "failed to enable clock\n");
+ return ret;
+ }
+
+ regs = priv->base;
+
+ /* Stop the timer */
+ clrbits_le32(&regs->cr1, CR1_CEN);
+
+ /* get timer clock */
+ rate = clk_get_rate(&clk);
+
+ /* we set timer prescaler to obtain a 1MHz timer counter frequency */
+ psc = (rate / CFG_SYS_HZ_CLOCK) - 1;
+ writel(psc, &regs->psc);
+
+ /* Set timer frequency to 1MHz */
+ uc_priv->clock_rate = CFG_SYS_HZ_CLOCK;
+
+ /* Configure timer for auto-reload */
+ setbits_le32(&regs->cr1, CR1_ARPE);
+
+ /* load value for auto reload */
+ writel(GPT_FREE_RUNNING, &regs->arr);
+
+ /* start timer */
+ setbits_le32(&regs->cr1, CR1_CEN);
+
+ /* Update generation */
+ setbits_le32(&regs->egr, EGR_UG);
+
+ return 0;
+}
+
+static const struct timer_ops stm32_timer_ops = {
+ .get_count = stm32_timer_get_count,
+};
+
+static const struct udevice_id stm32_timer_ids[] = {
+ { .compatible = "st,stm32-timer" },
+ {}
+};
+
+U_BOOT_DRIVER(stm32_timer) = {
+ .name = "stm32_timer",
+ .id = UCLASS_TIMER,
+ .of_match = stm32_timer_ids,
+ .priv_auto = sizeof(struct stm32_timer_priv),
+ .probe = stm32_timer_probe,
+ .ops = &stm32_timer_ops,
+};
diff --git a/drivers/timer/tegra-timer.c b/drivers/timer/tegra-timer.c
new file mode 100644
index 00000000000..3545424889d
--- /dev/null
+++ b/drivers/timer/tegra-timer.c
@@ -0,0 +1,129 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2022 Svyatoslav Ryhel <clamor95@gmail.com>
+ */
+
+#include <dm.h>
+#include <errno.h>
+#include <timer.h>
+
+#include <asm/io.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/tegra.h>
+
+#define TEGRA_OSC_CLK_ENB_L_SET (NV_PA_CLK_RST_BASE + 0x320)
+#define TEGRA_OSC_SET_CLK_ENB_TMR BIT(5)
+
+#define TEGRA_TIMER_USEC_CNTR (NV_PA_TMRUS_BASE + 0)
+#define TEGRA_TIMER_USEC_CFG (NV_PA_TMRUS_BASE + 4)
+
+#define TEGRA_TIMER_RATE 1000000 /* 1 MHz */
+
+/*
+ * On pre-DM stage timer should be left configured by
+ * previous bootloader for correct 1MHz clock.
+ * In the case of reset default value is set to 1/13 of
+ * CLK_M which should be decent enough to safely
+ * get to DM stage.
+ */
+u64 notrace timer_early_get_count(void)
+{
+ /* At this stage raw timer is used */
+ return readl(TEGRA_TIMER_USEC_CNTR);
+}
+
+unsigned long notrace timer_early_get_rate(void)
+{
+ return TEGRA_TIMER_RATE;
+}
+
+ulong timer_get_boot_us(void)
+{
+ return timer_early_get_count();
+}
+
+/*
+ * At moment of calling get_count, timer driver is already
+ * probed and is configured to have precise 1MHz clock.
+ * Tegra timer has a step of 1 microsecond which removes
+ * need of using adjusments involving uc_priv->clock_rate.
+ */
+static notrace u64 tegra_timer_get_count(struct udevice *dev)
+{
+ u32 val = timer_early_get_count();
+ return timer_conv_64(val);
+}
+
+static int tegra_timer_probe(struct udevice *dev)
+{
+ struct timer_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ u32 usec_config, value;
+
+ /* Timer rate has to be set unconditionally */
+ uc_priv->clock_rate = TEGRA_TIMER_RATE;
+
+ /*
+ * Configure microsecond timers to have 1MHz clock
+ * Config register is 0xqqww, where qq is "dividend", ww is "divisor"
+ * Uses n+1 scheme
+ */
+ switch (clock_get_rate(CLOCK_ID_CLK_M)) {
+ case 12000000:
+ usec_config = 0x000b; /* (11+1)/(0+1) */
+ break;
+ case 12800000:
+ usec_config = 0x043f; /* (63+1)/(4+1) */
+ break;
+ case 13000000:
+ usec_config = 0x000c; /* (12+1)/(0+1) */
+ break;
+ case 16800000:
+ usec_config = 0x0453; /* (83+1)/(4+1) */
+ break;
+ case 19200000:
+ usec_config = 0x045f; /* (95+1)/(4+1) */
+ break;
+ case 26000000:
+ usec_config = 0x0019; /* (25+1)/(0+1) */
+ break;
+ case 38400000:
+ usec_config = 0x04bf; /* (191+1)/(4+1) */
+ break;
+ case 48000000:
+ usec_config = 0x002f; /* (47+1)/(0+1) */
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Enable clock to timer hardware */
+ value = readl_relaxed(TEGRA_OSC_CLK_ENB_L_SET);
+ writel_relaxed(value | TEGRA_OSC_SET_CLK_ENB_TMR,
+ TEGRA_OSC_CLK_ENB_L_SET);
+
+ writel_relaxed(usec_config, TEGRA_TIMER_USEC_CFG);
+
+ return 0;
+}
+
+static const struct timer_ops tegra_timer_ops = {
+ .get_count = tegra_timer_get_count,
+};
+
+static const struct udevice_id tegra_timer_ids[] = {
+ { .compatible = "nvidia,tegra20-timer" },
+ { .compatible = "nvidia,tegra30-timer" },
+ { .compatible = "nvidia,tegra114-timer" },
+ { .compatible = "nvidia,tegra124-timer" },
+ { .compatible = "nvidia,tegra210-timer" },
+ { }
+};
+
+U_BOOT_DRIVER(tegra_timer) = {
+ .name = "tegra_timer",
+ .id = UCLASS_TIMER,
+ .of_match = tegra_timer_ids,
+ .probe = tegra_timer_probe,
+ .ops = &tegra_timer_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/drivers/timer/timer-uclass.c b/drivers/timer/timer-uclass.c
new file mode 100644
index 00000000000..8305f06d318
--- /dev/null
+++ b/drivers/timer/timer-uclass.c
@@ -0,0 +1,181 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2015 Thomas Chou <thomas@wytron.com.tw>
+ */
+
+#define LOG_CATEGORY UCLASS_TIMER
+
+#include <clk.h>
+#include <cpu.h>
+#include <dm.h>
+#include <asm/global_data.h>
+#include <dm/lists.h>
+#include <dm/device_compat.h>
+#include <dm/device-internal.h>
+#include <dm/root.h>
+#include <errno.h>
+#include <init.h>
+#include <timer.h>
+#include <linux/err.h>
+#include <relocate.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/*
+ * Implement a timer uclass to work with lib/time.c. The timer is usually
+ * a 32/64 bits free-running up counter. The get_rate() method is used to get
+ * the input clock frequency of the timer. The get_count() method is used
+ * to get the current 64 bits count value. If the hardware is counting down,
+ * the value should be inversed inside the method. There may be no real
+ * tick, and no timer interrupt.
+ */
+
+int notrace timer_get_count(struct udevice *dev, u64 *count)
+{
+ struct timer_ops *ops = timer_get_ops(dev);
+
+ if (!ops->get_count)
+ return -ENOSYS;
+
+ *count = ops->get_count(dev);
+ return 0;
+}
+
+unsigned long notrace timer_get_rate(struct udevice *dev)
+{
+ struct timer_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+
+ return uc_priv->clock_rate;
+}
+
+static int timer_pre_probe(struct udevice *dev)
+{
+ if (CONFIG_IS_ENABLED(OF_REAL)) {
+ struct timer_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ struct clk timer_clk;
+ int err;
+ ulong ret;
+
+ /*
+ * It is possible that a timer device has a null ofnode
+ */
+ if (!dev_has_ofnode(dev))
+ return 0;
+
+ err = clk_get_by_index(dev, 0, &timer_clk);
+ if (!err) {
+ ret = clk_get_rate(&timer_clk);
+ if (!IS_ERR_VALUE(ret)) {
+ uc_priv->clock_rate = ret;
+ return 0;
+ }
+ }
+
+ uc_priv->clock_rate = dev_read_u32_default(dev, "clock-frequency", 0);
+ }
+
+ return 0;
+}
+
+static int timer_post_probe(struct udevice *dev)
+{
+ struct timer_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+
+ if (!uc_priv->clock_rate)
+ return -EINVAL;
+
+ return 0;
+}
+
+#if CONFIG_IS_ENABLED(CPU)
+int timer_timebase_fallback(struct udevice *dev)
+{
+ struct udevice *cpu;
+ struct cpu_plat *cpu_plat;
+ struct timer_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+
+ /* Did we get our clock rate from the device tree? */
+ if (uc_priv->clock_rate)
+ return 0;
+
+ /* Fall back to timebase-frequency */
+ dev_dbg(dev, "missing clocks or clock-frequency property; falling back on timebase-frequency\n");
+ cpu = cpu_get_current_dev();
+ if (!cpu)
+ return -ENODEV;
+
+ cpu_plat = dev_get_parent_plat(cpu);
+ if (!cpu_plat)
+ return -ENODEV;
+
+ uc_priv->clock_rate = cpu_plat->timebase_freq;
+ return 0;
+}
+#endif
+
+u64 timer_conv_64(u32 count)
+{
+ /* increment tbh if tbl has rolled over */
+ if (count < gd->timebase_l)
+ gd->timebase_h++;
+ gd->timebase_l = count;
+ return ((u64)gd->timebase_h << 32) | gd->timebase_l;
+}
+
+int dm_timer_init(void)
+{
+ struct udevice *dev = NULL;
+ __maybe_unused ofnode node;
+ int ret;
+
+ if (gd->timer)
+ return 0;
+
+ /*
+ * Directly access gd->dm_root to suppress error messages, if the
+ * virtual root driver does not yet exist.
+ */
+ if (gd->dm_root == NULL)
+ return -EAGAIN;
+
+ if (CONFIG_IS_ENABLED(OF_REAL)) {
+ /* Check for a chosen timer to be used for tick */
+ node = ofnode_get_chosen_node("tick-timer");
+
+ if (ofnode_valid(node) &&
+ uclass_get_device_by_ofnode(UCLASS_TIMER, node, &dev)) {
+ /*
+ * If the timer is not marked to be bound before
+ * relocation, bind it anyway.
+ */
+ if (!lists_bind_fdt(dm_root(), node, &dev, NULL, false)) {
+ ret = device_probe(dev);
+ if (ret)
+ return ret;
+ }
+ }
+ }
+
+ if (!dev) {
+ /* Fall back to the first available timer */
+ ret = uclass_first_device_err(UCLASS_TIMER, &dev);
+ if (ret)
+ return ret;
+ }
+
+ if (dev) {
+ gd->timer = dev;
+ return 0;
+ }
+
+ return -ENODEV;
+}
+
+UCLASS_DRIVER(timer) = {
+ .id = UCLASS_TIMER,
+ .name = "timer",
+ .pre_probe = timer_pre_probe,
+ .flags = DM_UC_FLAG_SEQ_ALIAS,
+ .post_probe = timer_post_probe,
+ .per_device_auto = sizeof(struct timer_dev_priv),
+};
diff --git a/drivers/timer/tsc_timer.c b/drivers/timer/tsc_timer.c
new file mode 100644
index 00000000000..dd16ab320b2
--- /dev/null
+++ b/drivers/timer/tsc_timer.c
@@ -0,0 +1,517 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2012 The Chromium OS Authors.
+ *
+ * TSC calibration codes are adapted from Linux kernel
+ * arch/x86/kernel/tsc_msr.c and arch/x86/kernel/tsc.c
+ */
+
+#include <bootstage.h>
+#include <dm.h>
+#include <log.h>
+#include <malloc.h>
+#include <time.h>
+#include <timer.h>
+#include <asm/cpu.h>
+#include <asm/global_data.h>
+#include <asm/io.h>
+#include <asm/i8254.h>
+#include <asm/ibmpc.h>
+#include <asm/msr.h>
+#include <asm/u-boot-x86.h>
+#include <linux/delay.h>
+
+#define MAX_NUM_FREQS 9
+
+#define INTEL_FAM6_SKYLAKE_MOBILE 0x4E
+#define INTEL_FAM6_ATOM_GOLDMONT 0x5C /* Apollo Lake */
+#define INTEL_FAM6_SKYLAKE_DESKTOP 0x5E
+#define INTEL_FAM6_ATOM_GOLDMONT_X 0x5F /* Denverton */
+#define INTEL_FAM6_KABYLAKE_MOBILE 0x8E
+#define INTEL_FAM6_KABYLAKE_DESKTOP 0x9E
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/*
+ * native_calibrate_tsc
+ * Determine TSC frequency via CPUID, else return 0.
+ */
+static unsigned long native_calibrate_tsc(void)
+{
+ struct cpuid_result tsc_info;
+ unsigned int crystal_freq;
+
+ if (gd->arch.x86_vendor != X86_VENDOR_INTEL)
+ return 0;
+
+ if (cpuid_eax(0) < 0x15)
+ return 0;
+
+ tsc_info = cpuid(0x15);
+
+ if (tsc_info.ebx == 0 || tsc_info.eax == 0)
+ return 0;
+
+ crystal_freq = tsc_info.ecx / 1000;
+ if (!CONFIG_IS_ENABLED(X86_TSC_TIMER_NATIVE) && !crystal_freq) {
+ switch (gd->arch.x86_model) {
+ case INTEL_FAM6_SKYLAKE_MOBILE:
+ case INTEL_FAM6_SKYLAKE_DESKTOP:
+ case INTEL_FAM6_KABYLAKE_MOBILE:
+ case INTEL_FAM6_KABYLAKE_DESKTOP:
+ crystal_freq = 24000; /* 24.0 MHz */
+ break;
+ case INTEL_FAM6_ATOM_GOLDMONT_X:
+ crystal_freq = 25000; /* 25.0 MHz */
+ break;
+ case INTEL_FAM6_ATOM_GOLDMONT:
+ crystal_freq = 19200; /* 19.2 MHz */
+ break;
+ default:
+ return 0;
+ }
+ }
+
+ return (crystal_freq * tsc_info.ebx / tsc_info.eax) / 1000;
+}
+
+static unsigned long cpu_mhz_from_cpuid(void)
+{
+ if (gd->arch.x86_vendor != X86_VENDOR_INTEL)
+ return 0;
+
+ if (cpuid_eax(0) < 0x16)
+ return 0;
+
+ return cpuid_eax(0x15);
+}
+
+/*
+ * According to Intel 64 and IA-32 System Programming Guide,
+ * if MSR_PERF_STAT[31] is set, the maximum resolved bus ratio can be
+ * read in MSR_PLATFORM_ID[12:8], otherwise in MSR_PERF_STAT[44:40].
+ * Unfortunately some Intel Atom SoCs aren't quite compliant to this,
+ * so we need manually differentiate SoC families. This is what the
+ * field msr_plat does.
+ */
+struct freq_desc {
+ u8 x86_family; /* CPU family */
+ u8 x86_model; /* model */
+ /* 2: use 100MHz, 1: use MSR_PLATFORM_INFO, 0: MSR_IA32_PERF_STATUS */
+ u8 msr_plat;
+ u32 freqs[MAX_NUM_FREQS];
+};
+
+static struct freq_desc freq_desc_tables[] = {
+ /* PNW */
+ { 6, 0x27, 0, { 0, 0, 0, 0, 0, 99840, 0, 83200, 0 } },
+ /* CLV+ */
+ { 6, 0x35, 0, { 0, 133200, 0, 0, 0, 99840, 0, 83200, 0 } },
+ /* TNG - Intel Atom processor Z3400 series */
+ { 6, 0x4a, 1, { 0, 100000, 133300, 0, 0, 0, 0, 0, 0 } },
+ /* VLV2 - Intel Atom processor E3000, Z3600, Z3700 series */
+ { 6, 0x37, 1, { 83300, 100000, 133300, 116700, 80000, 0, 0, 0, 0 } },
+ /* ANN - Intel Atom processor Z3500 series */
+ { 6, 0x5a, 1, { 83300, 100000, 133300, 100000, 0, 0, 0, 0, 0 } },
+ /* AMT - Intel Atom processor X7-Z8000 and X5-Z8000 series */
+ { 6, 0x4c, 1, { 83300, 100000, 133300, 116700,
+ 80000, 93300, 90000, 88900, 87500 } },
+ /* Ivybridge */
+ { 6, 0x3a, 2, { 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
+};
+
+static int match_cpu(u8 family, u8 model)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(freq_desc_tables); i++) {
+ if ((family == freq_desc_tables[i].x86_family) &&
+ (model == freq_desc_tables[i].x86_model))
+ return i;
+ }
+
+ return -1;
+}
+
+/* Map CPU reference clock freq ID(0-7) to CPU reference clock freq(KHz) */
+#define id_to_freq(cpu_index, freq_id) \
+ (freq_desc_tables[cpu_index].freqs[freq_id])
+
+/*
+ * TSC on Intel Atom SoCs capable of determining TSC frequency by MSR is
+ * reliable and the frequency is known (provided by HW).
+ *
+ * On these platforms PIT/HPET is generally not available so calibration won't
+ * work at all and there is no other clocksource to act as a watchdog for the
+ * TSC, so we have no other choice than to trust it.
+ *
+ * Returns the TSC frequency in MHz or 0 if HW does not provide it.
+ */
+static unsigned long __maybe_unused cpu_mhz_from_msr(void)
+{
+ u32 lo, hi, ratio, freq_id, freq;
+ unsigned long res;
+ int cpu_index;
+
+ if (gd->arch.x86_vendor != X86_VENDOR_INTEL)
+ return 0;
+
+ cpu_index = match_cpu(gd->arch.x86, gd->arch.x86_model);
+ if (cpu_index < 0)
+ return 0;
+
+ if (freq_desc_tables[cpu_index].msr_plat) {
+ rdmsr(MSR_PLATFORM_INFO, lo, hi);
+ ratio = (lo >> 8) & 0xff;
+ } else {
+ rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
+ ratio = (hi >> 8) & 0x1f;
+ }
+ debug("Maximum core-clock to bus-clock ratio: 0x%x\n", ratio);
+
+ if (freq_desc_tables[cpu_index].msr_plat == 2) {
+ /* TODO: Figure out how best to deal with this */
+ freq = 100000;
+ debug("Using frequency: %u KHz\n", freq);
+ } else {
+ /* Get FSB FREQ ID */
+ rdmsr(MSR_FSB_FREQ, lo, hi);
+ freq_id = lo & 0x7;
+ freq = id_to_freq(cpu_index, freq_id);
+ debug("Resolved frequency ID: %u, frequency: %u KHz\n",
+ freq_id, freq);
+ }
+
+ /* TSC frequency = maximum resolved freq * maximum resolved bus ratio */
+ res = freq * ratio / 1000;
+ debug("TSC runs at %lu MHz\n", res);
+
+ return res;
+}
+
+/*
+ * This reads the current MSB of the PIT counter, and
+ * checks if we are running on sufficiently fast and
+ * non-virtualized hardware.
+ *
+ * Our expectations are:
+ *
+ * - the PIT is running at roughly 1.19MHz
+ *
+ * - each IO is going to take about 1us on real hardware,
+ * but we allow it to be much faster (by a factor of 10) or
+ * _slightly_ slower (ie we allow up to a 2us read+counter
+ * update - anything else implies a unacceptably slow CPU
+ * or PIT for the fast calibration to work.
+ *
+ * - with 256 PIT ticks to read the value, we have 214us to
+ * see the same MSB (and overhead like doing a single TSC
+ * read per MSB value etc).
+ *
+ * - We're doing 2 reads per loop (LSB, MSB), and we expect
+ * them each to take about a microsecond on real hardware.
+ * So we expect a count value of around 100. But we'll be
+ * generous, and accept anything over 50.
+ *
+ * - if the PIT is stuck, and we see *many* more reads, we
+ * return early (and the next caller of pit_expect_msb()
+ * then consider it a failure when they don't see the
+ * next expected value).
+ *
+ * These expectations mean that we know that we have seen the
+ * transition from one expected value to another with a fairly
+ * high accuracy, and we didn't miss any events. We can thus
+ * use the TSC value at the transitions to calculate a pretty
+ * good value for the TSC frequencty.
+ */
+static inline int pit_verify_msb(unsigned char val)
+{
+ /* Ignore LSB */
+ inb(0x42);
+ return inb(0x42) == val;
+}
+
+static inline int pit_expect_msb(unsigned char val, u64 *tscp,
+ unsigned long *deltap)
+{
+ int count;
+ u64 tsc = 0, prev_tsc = 0;
+
+ for (count = 0; count < 50000; count++) {
+ if (!pit_verify_msb(val))
+ break;
+ prev_tsc = tsc;
+ tsc = rdtsc();
+ }
+ *deltap = rdtsc() - prev_tsc;
+ *tscp = tsc;
+
+ /*
+ * We require _some_ success, but the quality control
+ * will be based on the error terms on the TSC values.
+ */
+ return count > 5;
+}
+
+/*
+ * How many MSB values do we want to see? We aim for
+ * a maximum error rate of 500ppm (in practice the
+ * real error is much smaller), but refuse to spend
+ * more than 50ms on it.
+ */
+#define MAX_QUICK_PIT_MS 50
+#define MAX_QUICK_PIT_ITERATIONS (MAX_QUICK_PIT_MS * PIT_TICK_RATE / 1000 / 256)
+
+static unsigned long __maybe_unused quick_pit_calibrate(void)
+{
+ int i;
+ u64 tsc, delta;
+ unsigned long d1, d2;
+
+ /* Set the Gate high, disable speaker */
+ outb((inb(0x61) & ~0x02) | 0x01, 0x61);
+
+ /*
+ * Counter 2, mode 0 (one-shot), binary count
+ *
+ * NOTE! Mode 2 decrements by two (and then the
+ * output is flipped each time, giving the same
+ * final output frequency as a decrement-by-one),
+ * so mode 0 is much better when looking at the
+ * individual counts.
+ */
+ outb(0xb0, 0x43);
+
+ /* Start at 0xffff */
+ outb(0xff, 0x42);
+ outb(0xff, 0x42);
+
+ /*
+ * The PIT starts counting at the next edge, so we
+ * need to delay for a microsecond. The easiest way
+ * to do that is to just read back the 16-bit counter
+ * once from the PIT.
+ */
+ pit_verify_msb(0);
+
+ if (pit_expect_msb(0xff, &tsc, &d1)) {
+ for (i = 1; i <= MAX_QUICK_PIT_ITERATIONS; i++) {
+ if (!pit_expect_msb(0xff-i, &delta, &d2))
+ break;
+
+ delta -= tsc;
+
+ /*
+ * Extrapolate the error and fail fast if the error will
+ * never be below 500 ppm.
+ */
+ if (i == 1 &&
+ d1 + d2 >= (delta * MAX_QUICK_PIT_ITERATIONS) >> 11)
+ return 0;
+
+ /*
+ * Iterate until the error is less than 500 ppm
+ */
+ if (d1+d2 >= delta >> 11)
+ continue;
+
+ /*
+ * Check the PIT one more time to verify that
+ * all TSC reads were stable wrt the PIT.
+ *
+ * This also guarantees serialization of the
+ * last cycle read ('d2') in pit_expect_msb.
+ */
+ if (!pit_verify_msb(0xfe - i))
+ break;
+ goto success;
+ }
+ }
+ debug("Fast TSC calibration failed\n");
+ return 0;
+
+success:
+ /*
+ * Ok, if we get here, then we've seen the
+ * MSB of the PIT decrement 'i' times, and the
+ * error has shrunk to less than 500 ppm.
+ *
+ * As a result, we can depend on there not being
+ * any odd delays anywhere, and the TSC reads are
+ * reliable (within the error).
+ *
+ * kHz = ticks / time-in-seconds / 1000;
+ * kHz = (t2 - t1) / (I * 256 / PIT_TICK_RATE) / 1000
+ * kHz = ((t2 - t1) * PIT_TICK_RATE) / (I * 256 * 1000)
+ */
+ delta *= PIT_TICK_RATE;
+ delta /= (i*256*1000);
+ debug("Fast TSC calibration using PIT\n");
+ return delta / 1000;
+}
+
+/* Get the speed of the TSC timer in MHz */
+unsigned notrace long get_tbclk_mhz(void)
+{
+ return get_tbclk() / 1000000;
+}
+
+static ulong get_ms_timer(void)
+{
+ return (get_ticks() * 1000) / get_tbclk();
+}
+
+ulong get_timer(ulong base)
+{
+ return get_ms_timer() - base;
+}
+
+ulong notrace timer_get_us(void)
+{
+ return get_ticks() / get_tbclk_mhz();
+}
+
+ulong timer_get_boot_us(void)
+{
+ return timer_get_us();
+}
+
+void __udelay(unsigned long usec)
+{
+ u64 now = get_ticks();
+ u64 stop;
+
+ stop = now + (u64)usec * get_tbclk_mhz();
+
+ while ((int64_t)(stop - get_ticks()) > 0)
+#if defined(CONFIG_QEMU) && defined(CONFIG_SMP)
+ /*
+ * Add a 'pause' instruction on qemu target,
+ * to give other VCPUs a chance to run.
+ */
+ asm volatile("pause");
+#else
+ ;
+#endif
+}
+
+static u64 tsc_timer_get_count(struct udevice *dev)
+{
+ u64 now_tick = rdtsc();
+
+ return now_tick - gd->arch.tsc_base;
+}
+
+static void tsc_timer_ensure_setup(bool early)
+{
+ if (gd->arch.tsc_inited)
+ return;
+ if (IS_ENABLED(CONFIG_X86_TSC_READ_BASE))
+ gd->arch.tsc_base = rdtsc();
+
+ if (!gd->arch.clock_rate) {
+ unsigned long fast_calibrate;
+
+ /* deal with this being called before x86_cpu_init_f() */
+ if (!gd->arch.x86_vendor)
+ x86_get_identity_for_timer();
+
+ /**
+ * There is no obvious way to obtain this information from EFI
+ * boot services. This value was measured on a Framework Laptop
+ * which has a 12th Gen Intel Core
+ */
+ if (IS_ENABLED(CONFIG_EFI_APP)) {
+ fast_calibrate = 2750;
+ goto done;
+ }
+ fast_calibrate = native_calibrate_tsc();
+ if (fast_calibrate)
+ goto done;
+
+ /* Reduce code size by dropping other methods */
+ if (CONFIG_IS_ENABLED(X86_TSC_TIMER_NATIVE))
+ panic("no timer");
+
+ fast_calibrate = cpu_mhz_from_cpuid();
+ if (fast_calibrate)
+ goto done;
+
+ fast_calibrate = cpu_mhz_from_msr();
+ if (fast_calibrate)
+ goto done;
+
+ fast_calibrate = quick_pit_calibrate();
+ if (fast_calibrate)
+ goto done;
+
+ if (early)
+ gd->arch.clock_rate = CONFIG_X86_TSC_TIMER_FREQ;
+ else
+ return;
+
+done:
+ fast_calibrate = min(fast_calibrate, 4000UL);
+ if (!gd->arch.clock_rate)
+ gd->arch.clock_rate = fast_calibrate * 1000000;
+ }
+ gd->arch.tsc_inited = true;
+}
+
+static int tsc_timer_probe(struct udevice *dev)
+{
+ struct timer_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+
+ /* Try hardware calibration first */
+ tsc_timer_ensure_setup(false);
+ if (!gd->arch.clock_rate) {
+ /*
+ * Use the clock frequency specified in the
+ * device tree as last resort
+ */
+ if (!uc_priv->clock_rate)
+ panic("TSC frequency is ZERO");
+ } else {
+ uc_priv->clock_rate = gd->arch.clock_rate;
+ }
+
+ return 0;
+}
+
+unsigned long notrace timer_early_get_rate(void)
+{
+ /*
+ * When TSC timer is used as the early timer, be warned that the timer
+ * clock rate can only be calibrated via some hardware ways. Specifying
+ * it in the device tree won't work for the early timer.
+ */
+ tsc_timer_ensure_setup(true);
+
+ return gd->arch.clock_rate;
+}
+
+u64 notrace timer_early_get_count(void)
+{
+ tsc_timer_ensure_setup(true);
+
+ return rdtsc() - gd->arch.tsc_base;
+}
+
+static const struct timer_ops tsc_timer_ops = {
+ .get_count = tsc_timer_get_count,
+};
+
+#if CONFIG_IS_ENABLED(OF_REAL)
+static const struct udevice_id tsc_timer_ids[] = {
+ { .compatible = "x86,tsc-timer", },
+ { }
+};
+#endif
+
+U_BOOT_DRIVER(x86_tsc_timer) = {
+ .name = "x86_tsc_timer",
+ .id = UCLASS_TIMER,
+ .of_match = of_match_ptr(tsc_timer_ids),
+ .probe = tsc_timer_probe,
+ .ops = &tsc_timer_ops,
+};
diff --git a/drivers/timer/xilinx-timer.c b/drivers/timer/xilinx-timer.c
new file mode 100644
index 00000000000..54148aa1689
--- /dev/null
+++ b/drivers/timer/xilinx-timer.c
@@ -0,0 +1,81 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2022 Advanced Micro Devices, Inc
+ * Michal Simek <michal.simek@amd.com>
+ *
+ * (C) Copyright 2007 Michal Simek
+ * Michal SIMEK <monstr@monstr.eu>
+ */
+
+#include <dm.h>
+#include <timer.h>
+#include <regmap.h>
+#include <dm/device_compat.h>
+
+#define TIMER_ENABLE_ALL 0x400 /* ENALL */
+#define TIMER_PWM 0x200 /* PWMA0 */
+#define TIMER_INTERRUPT 0x100 /* T0INT */
+#define TIMER_ENABLE 0x080 /* ENT0 */
+#define TIMER_ENABLE_INTR 0x040 /* ENIT0 */
+#define TIMER_RESET 0x020 /* LOAD0 */
+#define TIMER_RELOAD 0x010 /* ARHT0 */
+#define TIMER_EXT_CAPTURE 0x008 /* CAPT0 */
+#define TIMER_EXT_COMPARE 0x004 /* GENT0 */
+#define TIMER_DOWN_COUNT 0x002 /* UDT0 */
+#define TIMER_CAPTURE_MODE 0x001 /* MDT0 */
+
+#define TIMER_CONTROL_OFFSET 0
+#define TIMER_LOADREG_OFFSET 4
+#define TIMER_COUNTER_OFFSET 8
+
+struct xilinx_timer_priv {
+ struct regmap *regs;
+};
+
+static u64 xilinx_timer_get_count(struct udevice *dev)
+{
+ struct xilinx_timer_priv *priv = dev_get_priv(dev);
+ u32 value;
+
+ regmap_read(priv->regs, TIMER_COUNTER_OFFSET, &value);
+
+ return timer_conv_64(value);
+}
+
+static int xilinx_timer_probe(struct udevice *dev)
+{
+ struct xilinx_timer_priv *priv = dev_get_priv(dev);
+ int ret;
+
+ /* uc_priv->clock_rate has already clock rate */
+ ret = regmap_init_mem(dev_ofnode(dev), &priv->regs);
+ if (ret) {
+ dev_dbg(dev, "failed to get regbase of timer\n");
+ return ret;
+ }
+
+ regmap_write(priv->regs, TIMER_LOADREG_OFFSET, 0);
+ regmap_write(priv->regs, TIMER_CONTROL_OFFSET, TIMER_RESET);
+ regmap_write(priv->regs, TIMER_CONTROL_OFFSET,
+ TIMER_ENABLE | TIMER_RELOAD);
+
+ return 0;
+}
+
+static const struct timer_ops xilinx_timer_ops = {
+ .get_count = xilinx_timer_get_count,
+};
+
+static const struct udevice_id xilinx_timer_ids[] = {
+ { .compatible = "xlnx,xps-timer-1.00.a" },
+ {}
+};
+
+U_BOOT_DRIVER(xilinx_timer) = {
+ .name = "xilinx_timer",
+ .id = UCLASS_TIMER,
+ .of_match = xilinx_timer_ids,
+ .priv_auto = sizeof(struct xilinx_timer_priv),
+ .probe = xilinx_timer_probe,
+ .ops = &xilinx_timer_ops,
+};