timer: Add driver for Nomadik Multi Timer Unit (MTU)
authorStephan Gerhold <stephan@gerhold.net>
Sat, 4 Jan 2020 17:45:15 +0000 (18:45 +0100)
committerTom Rini <trini@konsulko.com>
Wed, 22 Jan 2020 22:47:56 +0000 (17:47 -0500)
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>
drivers/timer/Kconfig
drivers/timer/Makefile
drivers/timer/nomadik-mtu-timer.c [new file with mode: 0644]

index 96cc49273f4d89600893e8808dcfef77849a0b97..637024445c16e7da67bfa292eea0029056775d22 100644 (file)
@@ -127,6 +127,15 @@ config X86_TSC_TIMER_EARLY_FREQ
          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
index fa35bea6c5b2ea6d920714d8f6bd593d40f87d54..c22ffebcdec766ad01da70dffec0be6b26d35ede 100644 (file)
@@ -12,6 +12,7 @@ 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
diff --git a/drivers/timer/nomadik-mtu-timer.c b/drivers/timer/nomadik-mtu-timer.c
new file mode 100644 (file)
index 0000000..8648f1f
--- /dev/null
@@ -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,
+};