timer: Add driver for Nomadik Multi Timer Unit (MTU)

The Nomadik Multi Timer Unit (MTU) provides 4 decrementing
free-running timers. It is used in ST-Ericsson Ux500 SoCs.

The driver uses the first timer to implement UCLASS_TIMER.

Signed-off-by: Stephan Gerhold <stephan@gerhold.net>
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
This commit is contained in:
Stephan Gerhold 2020-01-04 18:45:15 +01:00 committed by Tom Rini
parent c82216803d
commit 057b613990
3 changed files with 124 additions and 0 deletions

View File

@ -127,6 +127,15 @@ config X86_TSC_TIMER_EARLY_FREQ
hardware ways, nor got from device tree at the time when device hardware ways, nor got from device tree at the time when device
tree is not available yet. 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 config OMAP_TIMER
bool "Omap timer support" bool "Omap timer support"
depends on TIMER depends on TIMER

View File

@ -12,6 +12,7 @@ obj-$(CONFIG_ATMEL_PIT_TIMER) += atmel_pit_timer.o
obj-$(CONFIG_CADENCE_TTC_TIMER) += cadence-ttc.o obj-$(CONFIG_CADENCE_TTC_TIMER) += cadence-ttc.o
obj-$(CONFIG_DESIGNWARE_APB_TIMER) += dw-apb-timer.o obj-$(CONFIG_DESIGNWARE_APB_TIMER) += dw-apb-timer.o
obj-$(CONFIG_MPC83XX_TIMER) += mpc83xx_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_OMAP_TIMER) += omap-timer.o
obj-$(CONFIG_RENESAS_OSTM_TIMER) += ostm_timer.o obj-$(CONFIG_RENESAS_OSTM_TIMER) += ostm_timer.o
obj-$(CONFIG_RISCV_TIMER) += riscv_timer.o obj-$(CONFIG_RISCV_TIMER) += riscv_timer.o

View File

@ -0,0 +1,114 @@
// 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>
#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 int nomadik_mtu_get_count(struct udevice *dev, u64 *count)
{
struct nomadik_mtu_priv *priv = dev_get_priv(dev);
/* Decrementing counter: invert the value */
*count = timer_conv_64(~readl(&priv->timer->cv));
return 0;
}
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_alloc_size = sizeof(struct nomadik_mtu_priv),
.probe = nomadik_mtu_probe,
.ops = &nomadik_mtu_ops,
};