diff options
Diffstat (limited to 'roms/u-boot/drivers/timer')
26 files changed, 3376 insertions, 0 deletions
diff --git a/roms/u-boot/drivers/timer/Kconfig b/roms/u-boot/drivers/timer/Kconfig new file mode 100644 index 000000000..ee81dfa77 --- /dev/null +++ b/roms/u-boot/drivers/timer/Kconfig @@ -0,0 +1,237 @@ +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 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 AG101P_TIMER + bool "AG101P timer support" + depends on TIMER && NDS32 + help + Select this to enable a timer for AG01P devices. + +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 || 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 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 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 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 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_EARLY_FREQ + int "x86 TSC timer frequency in MHz when used as the early timer" + depends on X86_TSC_TIMER + default 1000 + help + Sets the estimated CPU frequency in MHz 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 OMAP_TIMER + bool "Omap timer support" + depends on TIMER + help + Select this to enable an timer for Omap devices. + +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 STI_TIMER + bool "STi timer support" + depends on TIMER + default y if ARCH_STI + help + Select this to enable a timer for STi devices. + +config STM32_TIMER + bool "STM32 timer support" + depends on TIMER + help + Select this to enable support for the timer found on + STM32 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. + +endmenu diff --git a/roms/u-boot/drivers/timer/Makefile b/roms/u-boot/drivers/timer/Makefile new file mode 100644 index 000000000..e2bd530eb --- /dev/null +++ b/roms/u-boot/drivers/timer/Makefile @@ -0,0 +1,28 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright (C) 2015 Thomas Chou <thomas@wytron.com.tw> + +obj-y += timer-uclass.o +obj-$(CONFIG_AG101P_TIMER) += ag101p_timer.o +obj-$(CONFIG_ALTERA_TIMER) += altera_timer.o +obj-$(CONFIG_ANDES_PLMT_TIMER) += andes_plmt_timer.o +obj-$(CONFIG_ARC_TIMER) += arc_timer.o +obj-$(CONFIG_AST_TIMER) += ast_timer.o +obj-$(CONFIG_ATCPIT100_TIMER) += atcpit100_timer.o +obj-$(CONFIG_ATMEL_PIT_TIMER) += atmel_pit_timer.o +obj-$(CONFIG_CADENCE_TTC_TIMER) += cadence-ttc.o +obj-$(CONFIG_DESIGNWARE_APB_TIMER) += dw-apb-timer.o +obj-$(CONFIG_MPC83XX_TIMER) += mpc83xx_timer.o +obj-$(CONFIG_NOMADIK_MTU_TIMER) += nomadik-mtu-timer.o +obj-$(CONFIG_OMAP_TIMER) += omap-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_$(SPL_)SIFIVE_CLINT) += sifive_clint_timer.o +obj-$(CONFIG_STI_TIMER) += sti-timer.o +obj-$(CONFIG_STM32_TIMER) += stm32_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 diff --git a/roms/u-boot/drivers/timer/ag101p_timer.c b/roms/u-boot/drivers/timer/ag101p_timer.c new file mode 100644 index 000000000..27cf9b024 --- /dev/null +++ b/roms/u-boot/drivers/timer/ag101p_timer.c @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Andestech ATFTMR010 timer driver + * + * (C) Copyright 2016 + * Rick Chen, NDS32 Software Engineering, rick@andestech.com + */ +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <timer.h> +#include <linux/io.h> + +/* + * Timer Control Register + */ +#define T3_UPDOWN (1 << 11) +#define T2_UPDOWN (1 << 10) +#define T1_UPDOWN (1 << 9) +#define T3_OFENABLE (1 << 8) +#define T3_CLOCK (1 << 7) +#define T3_ENABLE (1 << 6) +#define T2_OFENABLE (1 << 5) +#define T2_CLOCK (1 << 4) +#define T2_ENABLE (1 << 3) +#define T1_OFENABLE (1 << 2) +#define T1_CLOCK (1 << 1) +#define T1_ENABLE (1 << 0) + +/* + * Timer Interrupt State & Mask Registers + */ +#define T3_OVERFLOW (1 << 8) +#define T3_MATCH2 (1 << 7) +#define T3_MATCH1 (1 << 6) +#define T2_OVERFLOW (1 << 5) +#define T2_MATCH2 (1 << 4) +#define T2_MATCH1 (1 << 3) +#define T1_OVERFLOW (1 << 2) +#define T1_MATCH2 (1 << 1) +#define T1_MATCH1 (1 << 0) + +struct atftmr_timer_regs { + u32 t1_counter; /* 0x00 */ + u32 t1_load; /* 0x04 */ + u32 t1_match1; /* 0x08 */ + u32 t1_match2; /* 0x0c */ + u32 t2_counter; /* 0x10 */ + u32 t2_load; /* 0x14 */ + u32 t2_match1; /* 0x18 */ + u32 t2_match2; /* 0x1c */ + u32 t3_counter; /* 0x20 */ + u32 t3_load; /* 0x24 */ + u32 t3_match1; /* 0x28 */ + u32 t3_match2; /* 0x2c */ + u32 cr; /* 0x30 */ + u32 int_state; /* 0x34 */ + u32 int_mask; /* 0x38 */ +}; + +struct atftmr_timer_plat { + struct atftmr_timer_regs *regs; +}; + +static u64 atftmr_timer_get_count(struct udevice *dev) +{ + struct atftmr_timer_plat *plat = dev_get_plat(dev); + struct atftmr_timer_regs *const regs = plat->regs; + u32 val; + val = readl(®s->t3_counter); + return timer_conv_64(val); +} + +static int atftmr_timer_probe(struct udevice *dev) +{ + struct atftmr_timer_plat *plat = dev_get_plat(dev); + struct atftmr_timer_regs *const regs = plat->regs; + u32 cr; + writel(0, ®s->t3_load); + writel(0, ®s->t3_counter); + writel(TIMER_LOAD_VAL, ®s->t3_match1); + writel(TIMER_LOAD_VAL, ®s->t3_match2); + /* disable interrupts */ + writel(T3_MATCH1|T3_MATCH2|T3_OVERFLOW , ®s->int_mask); + cr = readl(®s->cr); + cr |= (T3_ENABLE|T3_UPDOWN); + writel(cr, ®s->cr); + return 0; +} + +static int atftme_timer_of_to_plat(struct udevice *dev) +{ + struct atftmr_timer_plat *plat = dev_get_plat(dev); + plat->regs = map_physmem(dev_read_addr(dev), + sizeof(struct atftmr_timer_regs), + MAP_NOCACHE); + return 0; +} + +static const struct timer_ops ag101p_timer_ops = { + .get_count = atftmr_timer_get_count, +}; + +static const struct udevice_id ag101p_timer_ids[] = { + { .compatible = "andestech,attmr010" }, + {} +}; + +U_BOOT_DRIVER(altera_timer) = { + .name = "ag101p_timer", + .id = UCLASS_TIMER, + .of_match = ag101p_timer_ids, + .of_to_plat = atftme_timer_of_to_plat, + .plat_auto = sizeof(struct atftmr_timer_plat), + .probe = atftmr_timer_probe, + .ops = &ag101p_timer_ops, +}; diff --git a/roms/u-boot/drivers/timer/altera_timer.c b/roms/u-boot/drivers/timer/altera_timer.c new file mode 100644 index 000000000..040dc65f4 --- /dev/null +++ b/roms/u-boot/drivers/timer/altera_timer.c @@ -0,0 +1,94 @@ +// 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 <common.h> +#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, ®s->snapl); + + /* Read timer value */ + val = readl(®s->snapl) & 0xffff; + val |= (readl(®s->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, ®s->status); + writel(0, ®s->control); + writel(ALTERA_TIMER_STOP, ®s->control); + + writel(0xffff, ®s->periodl); + writel(0xffff, ®s->periodh); + writel(ALTERA_TIMER_CONT | ALTERA_TIMER_START, ®s->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/roms/u-boot/drivers/timer/andes_plmt_timer.c b/roms/u-boot/drivers/timer/andes_plmt_timer.c new file mode 100644 index 000000000..a3797b22c --- /dev/null +++ b/roms/u-boot/drivers/timer/andes_plmt_timer.c @@ -0,0 +1,70 @@ +// 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 <common.h> +#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 = "riscv,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/roms/u-boot/drivers/timer/arc_timer.c b/roms/u-boot/drivers/timer/arc_timer.c new file mode 100644 index 000000000..497f8a041 --- /dev/null +++ b/roms/u-boot/drivers/timer/arc_timer.c @@ -0,0 +1,110 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2016 Synopsys, Inc. All rights reserved. + */ + +#include <common.h> +#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/roms/u-boot/drivers/timer/ast_timer.c b/roms/u-boot/drivers/timer/ast_timer.c new file mode 100644 index 000000000..78adc96cc --- /dev/null +++ b/roms/u-boot/drivers/timer/ast_timer.c @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2016 Google Inc. + */ + +#include <common.h> +#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/roms/u-boot/drivers/timer/atcpit100_timer.c b/roms/u-boot/drivers/timer/atcpit100_timer.c new file mode 100644 index 000000000..fbc7fac1b --- /dev/null +++ b/roms/u-boot/drivers/timer/atcpit100_timer.c @@ -0,0 +1,112 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Andestech ATCPIT100 timer driver + * + * (C) Copyright 2016 + * Rick Chen, NDS32 Software Engineering, rick@andestech.com + */ +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <timer.h> +#include <linux/io.h> + +#define REG32_TMR(x) (*(u32 *) ((plat->regs) + (x>>2))) + +/* + * Definition of register offsets + */ + +/* ID and Revision Register */ +#define ID_REV 0x0 + +/* Configuration Register */ +#define CFG 0x10 + +/* Interrupt Enable Register */ +#define INT_EN 0x14 +#define CH_INT_EN(c , i) ((1<<i)<<(4*c)) + +/* Interrupt Status Register */ +#define INT_STA 0x18 +#define CH_INT_STA(c , i) ((1<<i)<<(4*c)) + +/* Channel Enable Register */ +#define CH_EN 0x1C +#define CH_TMR_EN(c , t) ((1<<t)<<(4*c)) + +/* Ch n Control REgister */ +#define CH_CTL(n) (0x20+0x10*n) +/* Channel clock source , bit 3 , 0:External clock , 1:APB clock */ +#define APB_CLK (1<<3) +/* Channel mode , bit 0~2 */ +#define TMR_32 1 +#define TMR_16 2 +#define TMR_8 3 +#define PWM 4 + +#define CH_REL(n) (0x24+0x10*n) +#define CH_CNT(n) (0x28+0x10*n) + +struct atctmr_timer_regs { + u32 id_rev; /* 0x00 */ + u32 reservd[3]; /* 0x04 ~ 0x0c */ + u32 cfg; /* 0x10 */ + u32 int_en; /* 0x14 */ + u32 int_st; /* 0x18 */ + u32 ch_en; /* 0x1c */ + u32 ch0_ctrl; /* 0x20 */ + u32 ch0_reload; /* 0x24 */ + u32 ch0_cntr; /* 0x28 */ + u32 reservd1; /* 0x2c */ + u32 ch1_ctrl; /* 0x30 */ + u32 ch1_reload; /* 0x34 */ + u32 int_mask; /* 0x38 */ +}; + +struct atcpit_timer_plat { + u32 *regs; +}; + +static u64 atcpit_timer_get_count(struct udevice *dev) +{ + struct atcpit_timer_plat *plat = dev_get_plat(dev); + u32 val; + val = ~(REG32_TMR(CH_CNT(1))+0xffffffff); + return timer_conv_64(val); +} + +static int atcpit_timer_probe(struct udevice *dev) +{ + struct atcpit_timer_plat *plat = dev_get_plat(dev); + REG32_TMR(CH_REL(1)) = 0xffffffff; + REG32_TMR(CH_CTL(1)) = APB_CLK|TMR_32; + REG32_TMR(CH_EN) |= CH_TMR_EN(1 , 0); + return 0; +} + +static int atcpit_timer_of_to_plat(struct udevice *dev) +{ + struct atcpit_timer_plat *plat = dev_get_plat(dev); + plat->regs = map_physmem(dev_read_addr(dev), 0x100 , MAP_NOCACHE); + return 0; +} + +static const struct timer_ops atcpit_timer_ops = { + .get_count = atcpit_timer_get_count, +}; + +static const struct udevice_id atcpit_timer_ids[] = { + { .compatible = "andestech,atcpit100" }, + {} +}; + +U_BOOT_DRIVER(atcpit100_timer) = { + .name = "atcpit100_timer", + .id = UCLASS_TIMER, + .of_match = atcpit_timer_ids, + .of_to_plat = atcpit_timer_of_to_plat, + .plat_auto = sizeof(struct atcpit_timer_plat), + .probe = atcpit_timer_probe, + .ops = &atcpit_timer_ops, +}; diff --git a/roms/u-boot/drivers/timer/atmel_pit_timer.c b/roms/u-boot/drivers/timer/atmel_pit_timer.c new file mode 100644 index 000000000..3178e5888 --- /dev/null +++ b/roms/u-boot/drivers/timer/atmel_pit_timer.c @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2017 Microchip Corporation + * Wenyou.Yang <wenyou.yang@microchip.com> + */ + +#include <common.h> +#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(®s->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, ®s->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/roms/u-boot/drivers/timer/cadence-ttc.c b/roms/u-boot/drivers/timer/cadence-ttc.c new file mode 100644 index 000000000..2f95d45ec --- /dev/null +++ b/roms/u-boot/drivers/timer/cadence-ttc.c @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 Xilinx, Inc. (Michal Simek) + */ + +#include <common.h> +#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 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, +}; diff --git a/roms/u-boot/drivers/timer/dw-apb-timer.c b/roms/u-boot/drivers/timer/dw-apb-timer.c new file mode 100644 index 000000000..9aed5dd21 --- /dev/null +++ b/roms/u-boot/drivers/timer/dw-apb-timer.c @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Designware APB Timer driver + * + * Copyright (C) 2018 Marek Vasut <marex@denx.de> + */ + +#include <common.h> +#include <dm.h> +#include <clk.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 { + fdt_addr_t regs; + struct reset_ctl_bulk resets; +}; + +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; + + 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); + + clk_free(&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) +{ + 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(dw_apb_timer) = { + .name = "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), +}; diff --git a/roms/u-boot/drivers/timer/imx-gpt-timer.c b/roms/u-boot/drivers/timer/imx-gpt-timer.c new file mode 100644 index 000000000..72be29775 --- /dev/null +++ b/roms/u-boot/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 <common.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 CONFIG_SYS_HZ_CLOCK not specified et's default to 3Mhz */ +#ifndef CONFIG_SYS_HZ_CLOCK +#define CONFIG_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(®s->cnt)); +} + +static int imx_gpt_setup(struct imx_gpt_timer_regs *regs, u32 rate) +{ + u32 prescaler = (rate / CONFIG_SYS_HZ_CLOCK) - 1; + + /* Reset the timer */ + setbits_le32(®s->cr, GPT_CR_SWR); + + /* Wait for timer to finish reset */ + while (readl(®s->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), ®s->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, ®s->cr); + } else { + if (prescaler > GPT_PR_PRESCALER_MAX) + return -EINVAL; + + /* Set prescaler */ + writel((prescaler << GPT_PR_PRESCALER_SHIFT), ®s->pr); + /* Set Peripheral as clock source and set gpt in free-running + * mode + */ + writel(GPT_CLKSRC_IPG_CLK | GPT_CR_FRR, ®s->cr); + } + + /* Start timer */ + setbits_le32(®s->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 = CONFIG_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/roms/u-boot/drivers/timer/mchp-pit64b-timer.c b/roms/u-boot/drivers/timer/mchp-pit64b-timer.c new file mode 100644 index 000000000..c9806d7ee --- /dev/null +++ b/roms/u-boot/drivers/timer/mchp-pit64b-timer.c @@ -0,0 +1,107 @@ +// 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 <common.h> +#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/roms/u-boot/drivers/timer/mpc83xx_timer.c b/roms/u-boot/drivers/timer/mpc83xx_timer.c new file mode 100644 index 000000000..952293195 --- /dev/null +++ b/roms/u-boot/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 <common.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 CONFIG_SYS_WATCHDOG_FREQ +#define CONFIG_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 (CONFIG_SYS_WATCHDOG_FREQ && (priv->timestamp % (CONFIG_SYS_WATCHDOG_FREQ)) == 0) + WATCHDOG_RESET(); +#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()) + WATCHDOG_RESET(); +} + +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 (tbu * 0x10000ULL) + 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/roms/u-boot/drivers/timer/mtk_timer.c b/roms/u-boot/drivers/timer/mtk_timer.c new file mode 100644 index 000000000..f6b97f868 --- /dev/null +++ b/roms/u-boot/drivers/timer/mtk_timer.c @@ -0,0 +1,95 @@ +// 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 <common.h> +#include <dm.h> +#include <timer.h> +#include <asm/io.h> +#include <linux/bitops.h> + +#define MTK_GPT4_CTRL 0x40 +#define MTK_GPT4_CLK 0x44 +#define MTK_GPT4_CNT 0x48 + +#define GPT4_ENABLE BIT(0) +#define GPT4_CLEAR BIT(1) +#define GPT4_FREERUN GENMASK(5, 4) +#define GPT4_CLK_SYS 0x0 +#define GPT4_CLK_DIV1 0x0 + +struct mtk_timer_priv { + void __iomem *base; +}; + +static u64 mtk_timer_get_count(struct udevice *dev) +{ + struct mtk_timer_priv *priv = dev_get_priv(dev); + u32 val = readl(priv->base + MTK_GPT4_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; + + priv->base = dev_read_addr_ptr(dev); + if (!priv->base) + return -ENOENT; + + ret = clk_get_by_index(dev, 0, &clk); + if (ret) + return ret; + + ret = clk_get_by_index(dev, 1, &parent); + if (!ret) { + 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; + + /* + * Initialize the timer: + * 1. set clock source to system clock with clock divider setting to 1 + * 2. set timer mode to free running + * 3. reset timer counter to 0 then enable the timer + */ + writel(GPT4_CLK_SYS | GPT4_CLK_DIV1, priv->base + MTK_GPT4_CLK); + writel(GPT4_FREERUN | GPT4_CLEAR | GPT4_ENABLE, + priv->base + MTK_GPT4_CTRL); + + 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" }, + { .compatible = "mediatek,mt6577-timer" }, + { } +}; + +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/roms/u-boot/drivers/timer/nomadik-mtu-timer.c b/roms/u-boot/drivers/timer/nomadik-mtu-timer.c new file mode 100644 index 000000000..417b419d4 --- /dev/null +++ b/roms/u-boot/drivers/timer/nomadik-mtu-timer.c @@ -0,0 +1,113 @@ +// 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 <common.h> +#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; + fdt_addr_t addr; + u32 prescale; + + addr = dev_read_addr(dev); + if (addr == FDT_ADDR_T_NONE) + return -EINVAL; + + mtu = (struct nomadik_mtu_regs *)addr; + 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/roms/u-boot/drivers/timer/omap-timer.c b/roms/u-boot/drivers/timer/omap-timer.c new file mode 100644 index 000000000..721e385fd --- /dev/null +++ b/roms/u-boot/drivers/timer/omap-timer.c @@ -0,0 +1,106 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * TI OMAP timer driver + * + * Copyright (C) 2015, Texas Instruments, Incorporated + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <timer.h> +#include <asm/io.h> +#include <asm/arch/clock.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 << CONFIG_SYS_PTV); + + /* start the counter ticking up, reload value on overflow */ + writel(0, &priv->regs->tldr); + writel(0, &priv->regs->tcrr); + /* enable timer */ + writel((CONFIG_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; +} + + +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" }, + {} +}; + +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/roms/u-boot/drivers/timer/ostm_timer.c b/roms/u-boot/drivers/timer/ostm_timer.c new file mode 100644 index 000000000..24813de26 --- /dev/null +++ b/roms/u-boot/drivers/timer/ostm_timer.c @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Renesas RZ/A1 R7S72100 OSTM Timer driver + * + * Copyright (C) 2019 Marek Vasut <marek.vasut@gmail.com> + */ + +#include <common.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); + + clk_free(&clk); +#else + uc_priv->clock_rate = CONFIG_SYS_CLK_FREQ / 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/roms/u-boot/drivers/timer/riscv_timer.c b/roms/u-boot/drivers/timer/riscv_timer.c new file mode 100644 index 000000000..3627ed79b --- /dev/null +++ b/roms/u-boot/drivers/timer/riscv_timer.c @@ -0,0 +1,73 @@ +// 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 <common.h> +#include <dm.h> +#include <errno.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 + +static int riscv_timer_probe(struct udevice *dev) +{ + struct timer_dev_priv *uc_priv = dev_get_uclass_priv(dev); + + /* clock frequency was passed from the cpu driver as driver data */ + uc_priv->clock_rate = dev->driver_data; + + return 0; +} + +static const struct timer_ops riscv_timer_ops = { + .get_count = riscv_timer_get_count, +}; + +U_BOOT_DRIVER(riscv_timer) = { + .name = "riscv_timer", + .id = UCLASS_TIMER, + .probe = riscv_timer_probe, + .ops = &riscv_timer_ops, + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/roms/u-boot/drivers/timer/rockchip_timer.c b/roms/u-boot/drivers/timer/rockchip_timer.c new file mode 100644 index 000000000..18c61450a --- /dev/null +++ b/roms/u-boot/drivers/timer/rockchip_timer.c @@ -0,0 +1,174 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2017 Theobroma Systems Design und Consulting GmbH + */ + +#include <common.h> +#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_rk3368_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); +#if !CONFIG_IS_ENABLED(OF_PLATDATA) + } else if (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; + } +#endif + } 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_PLATDATA) + struct rockchip_timer_priv *priv = dev_get_priv(dev); + + priv->timer = dev_read_addr_ptr(dev); + if (!priv->timer) + return -ENOENT; +#endif + + 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,rk3188-timer" }, + { .compatible = "rockchip,rk3288-timer" }, + { .compatible = "rockchip,rk3368-timer" }, + {} +}; + +U_BOOT_DRIVER(rockchip_rk3368_timer) = { + .name = "rockchip_rk3368_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/roms/u-boot/drivers/timer/sandbox_timer.c b/roms/u-boot/drivers/timer/sandbox_timer.c new file mode 100644 index 000000000..c846bfb9f --- /dev/null +++ b/roms/u-boot/drivers/timer/sandbox_timer.c @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2015 Thomas Chou <thomas@wytron.com.tw> + */ + +#include <common.h> +#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; +} + +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 */ +U_BOOT_DRVINFO(sandbox_timer_non_fdt) = { + .name = "sandbox_timer", +}; diff --git a/roms/u-boot/drivers/timer/sifive_clint_timer.c b/roms/u-boot/drivers/timer/sifive_clint_timer.c new file mode 100644 index 000000000..939b99d93 --- /dev/null +++ b/roms/u-boot/drivers/timer/sifive_clint_timer.c @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2020, Sean Anderson <seanga2@gmail.com> + * Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com> + */ + +#include <common.h> +#include <clk.h> +#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) + 0xbff8) + +static u64 notrace sifive_clint_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 sifive_clint_ops = { + .get_count = sifive_clint_get_count, +}; + +static int sifive_clint_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 sifive_clint_ids[] = { + { .compatible = "riscv,clint0" }, + { .compatible = "sifive,clint0" }, + { } +}; + +U_BOOT_DRIVER(sifive_clint) = { + .name = "sifive_clint", + .id = UCLASS_TIMER, + .of_match = sifive_clint_ids, + .probe = sifive_clint_probe, + .ops = &sifive_clint_ops, + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/roms/u-boot/drivers/timer/sti-timer.c b/roms/u-boot/drivers/timer/sti-timer.c new file mode 100644 index 000000000..87444a065 --- /dev/null +++ b/roms/u-boot/drivers/timer/sti-timer.c @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2017, STMicroelectronics - All Rights Reserved + * Author(s): Patrice Chotard, <patrice.chotard@foss.st.com> for STMicroelectronics. + */ + +#include <common.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 sti_timer_priv { + struct globaltimer *global_timer; +}; + +static u64 sti_timer_get_count(struct udevice *dev) +{ + struct sti_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 sti_timer_probe(struct udevice *dev) +{ + struct timer_dev_priv *uc_priv = dev_get_uclass_priv(dev); + struct sti_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 = CONFIG_SYS_HZ_CLOCK; + } + + /* init timer */ + writel(0x01, &priv->global_timer->ctl); + + return 0; +} + +static const struct timer_ops sti_timer_ops = { + .get_count = sti_timer_get_count, +}; + +static const struct udevice_id sti_timer_ids[] = { + { .compatible = "arm,cortex-a9-global-timer" }, + {} +}; + +U_BOOT_DRIVER(sti_timer) = { + .name = "sti_timer", + .id = UCLASS_TIMER, + .of_match = sti_timer_ids, + .priv_auto = sizeof(struct sti_timer_priv), + .probe = sti_timer_probe, + .ops = &sti_timer_ops, +}; diff --git a/roms/u-boot/drivers/timer/stm32_timer.c b/roms/u-boot/drivers/timer/stm32_timer.c new file mode 100644 index 000000000..e34f5202f --- /dev/null +++ b/roms/u-boot/drivers/timer/stm32_timer.c @@ -0,0 +1,138 @@ +// 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 <common.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(®s->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(®s->cr1, CR1_CEN); + + /* get timer clock */ + rate = clk_get_rate(&clk); + + /* we set timer prescaler to obtain a 1MHz timer counter frequency */ + psc = (rate / CONFIG_SYS_HZ_CLOCK) - 1; + writel(psc, ®s->psc); + + /* Set timer frequency to 1MHz */ + uc_priv->clock_rate = CONFIG_SYS_HZ_CLOCK; + + /* Configure timer for auto-reload */ + setbits_le32(®s->cr1, CR1_ARPE); + + /* load value for auto reload */ + writel(GPT_FREE_RUNNING, ®s->arr); + + /* start timer */ + setbits_le32(®s->cr1, CR1_CEN); + + /* Update generation */ + setbits_le32(®s->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/roms/u-boot/drivers/timer/timer-uclass.c b/roms/u-boot/drivers/timer/timer-uclass.c new file mode 100644 index 000000000..73b4a5cd2 --- /dev/null +++ b/roms/u-boot/drivers/timer/timer-uclass.c @@ -0,0 +1,177 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2015 Thomas Chou <thomas@wytron.com.tw> + */ + +#include <common.h> +#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> + +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) +{ + const struct timer_ops *ops = device_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_PLATDATA) + 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)) + return ret; + uc_priv->clock_rate = ret; + } else { + uc_priv->clock_rate = + dev_read_u32_default(dev, "clock-frequency", 0); + } +#endif + + 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 notrace 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_PLATDATA) + /* 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, false)) { + ret = device_probe(dev); + if (ret) + return ret; + } + } +#endif + + 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/roms/u-boot/drivers/timer/tsc_timer.c b/roms/u-boot/drivers/timer/tsc_timer.c new file mode 100644 index 000000000..7d19a9962 --- /dev/null +++ b/roms/u-boot/drivers/timer/tsc_timer.c @@ -0,0 +1,494 @@ +// 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 <common.h> +#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(0x16); +} + +/* + * 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; + + /* + * Iterate until the error is less than 500 ppm + */ + delta -= tsc; + 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; + + 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) + fast_calibrate = CONFIG_X86_TSC_TIMER_EARLY_FREQ; + else + return; + +done: + 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_PLATDATA) +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, +}; |