summaryrefslogtreecommitdiff
path: root/drivers/timer
diff options
context:
space:
mode:
authorTom Rini <[email protected]>2024-05-07 19:24:07 -0600
committerTom Rini <[email protected]>2024-05-07 19:24:07 -0600
commit1afa75c087a7961428f9cd9d0f47d5baa928c445 (patch)
treef4250822a6e6a3d0f1423c02aa7d55de6a1f60db /drivers/timer
parent7e2938beabac24e6c8baad33e254b2383dbe9490 (diff)
parent8691ffa5c06f4a171d1c418cdb5711334dd2470d (diff)
Merge patch series "arm: Add Analog Devices SC5xx Machine Type"
Greg Malysa <[email protected]> says: This series adds support for the ADI SC5xx machine type and includes two core drivers that are required for being able to boot any board--a UART driver, the gptimer driver which is used as a clock reference (CNTVCNT is not supported on the armv7 sc5xx SoCs) and the clock tree driver. Our corresponding Linux support relies on u-boot configuring the clocks correctly before booting, so it is not possible to boot any board without the CGU/CDU configuration happening here. There are also no board files, device trees, or defconfigs included here, but some common definitions that will be used to build board files currently are. The sc5xx SoCs themselves include many armv7 families (sc57x, sc58x, and sc594) all using an ARM Cortex-A5, and one armv8 family (sc598) indended to be a drop-in replacement for the SC594 in terms of peripherals, with a Cortex-A55 instead. Some of the configuration code in dmcinit and clkinit is quite scary and causes a lot of checkpatch violations. It is modified from code initially provided by ADI, but it has not been fully rewritten. There's a question of how important it is to clean up this code--it has some quality violations, but it has been in use (including in production) for over two years and is known to work for performing the low level SoC initialization, while a rewrite might introduce timing or sequence bugs that could take a significant amount of time to detect in the future.
Diffstat (limited to 'drivers/timer')
-rw-r--r--drivers/timer/Kconfig8
-rw-r--r--drivers/timer/Makefile1
-rw-r--r--drivers/timer/adi_sc5xx_timer.c145
3 files changed, 154 insertions, 0 deletions
diff --git a/drivers/timer/Kconfig b/drivers/timer/Kconfig
index 60519c3b536..6b1de82ae38 100644
--- a/drivers/timer/Kconfig
+++ b/drivers/timer/Kconfig
@@ -50,6 +50,14 @@ config TIMER_EARLY
use an early timer. These functions must be supported by your timer
driver: timer_early_get_count() and timer_early_get_rate().
+config ADI_SC5XX_TIMER
+ bool "ADI ADSP-SC5xx Timer Support"
+ depends on TIMER && (SC57X || SC58X || SC59X || SC59X_64)
+ help
+ gptimer based timer support on ADI's ADSP-SC5xx platforms. Available
+ but not required on sc59x-64-based platforms (598 and similar).
+ Required on 32-bit platforms (sc57x, sc58x, sc594 and earlier).
+
config ALTERA_TIMER
bool "Altera timer support"
depends on TIMER
diff --git a/drivers/timer/Makefile b/drivers/timer/Makefile
index b93145e8d43..fb95c8899e3 100644
--- a/drivers/timer/Makefile
+++ b/drivers/timer/Makefile
@@ -3,6 +3,7 @@
# Copyright (C) 2015 Thomas Chou <[email protected]>
obj-y += timer-uclass.o
+obj-$(CONFIG_ADI_SC5XX_TIMER) += adi_sc5xx_timer.o
obj-$(CONFIG_ALTERA_TIMER) += altera_timer.o
obj-$(CONFIG_$(SPL_)ANDES_PLMT_TIMER) += andes_plmt_timer.o
obj-$(CONFIG_ARC_TIMER) += arc_timer.o
diff --git a/drivers/timer/adi_sc5xx_timer.c b/drivers/timer/adi_sc5xx_timer.c
new file mode 100644
index 00000000000..11c098434a8
--- /dev/null
+++ b/drivers/timer/adi_sc5xx_timer.c
@@ -0,0 +1,145 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * (C) Copyright 2022 - Analog Devices, Inc.
+ *
+ * Written and/or maintained by Timesys Corporation
+ *
+ * Converted to driver model by Nathan Barrett-Morrison
+ *
+ * Author: Greg Malysa <[email protected]>
+ * Additional Contact: Nathan Barrett-Morrison <[email protected]>
+ *
+ * dm timer implementation for ADI ADSP-SC5xx SoCs
+ *
+ */
+
+#include <clk.h>
+#include <dm.h>
+#include <timer.h>
+#include <asm/io.h>
+#include <dm/device_compat.h>
+#include <linux/compiler_types.h>
+
+/*
+ * Timer Configuration Register Bits
+ */
+#define TIMER_OUT_DIS 0x0800
+#define TIMER_PULSE_HI 0x0080
+#define TIMER_MODE_PWM_CONT 0x000c
+
+#define __BFP(m) u16 m; u16 __pad_##m
+
+struct gptimer3 {
+ __BFP(config);
+ u32 counter;
+ u32 period;
+ u32 width;
+ u32 delay;
+};
+
+struct gptimer3_group_regs {
+ __BFP(run);
+ __BFP(enable);
+ __BFP(disable);
+ __BFP(stop_cfg);
+ __BFP(stop_cfg_set);
+ __BFP(stop_cfg_clr);
+ __BFP(data_imsk);
+ __BFP(stat_imsk);
+ __BFP(tr_msk);
+ __BFP(tr_ie);
+ __BFP(data_ilat);
+ __BFP(stat_ilat);
+ __BFP(err_status);
+ __BFP(bcast_per);
+ __BFP(bcast_wid);
+ __BFP(bcast_dly);
+};
+
+#define MAX_TIM_LOAD 0xFFFFFFFF
+
+struct adi_gptimer_priv {
+ struct gptimer3_group_regs __iomem *timer_group;
+ struct gptimer3 __iomem *timer_base;
+ u32 prev;
+ u64 upper;
+};
+
+static u64 adi_gptimer_get_count(struct udevice *udev)
+{
+ struct adi_gptimer_priv *priv = dev_get_priv(udev);
+
+ u32 now = readl(&priv->timer_base->counter);
+
+ if (now < priv->prev)
+ priv->upper += (1ull << 32);
+
+ priv->prev = now;
+
+ return (priv->upper + (u64)now);
+}
+
+static const struct timer_ops adi_gptimer_ops = {
+ .get_count = adi_gptimer_get_count,
+};
+
+static int adi_gptimer_probe(struct udevice *udev)
+{
+ struct timer_dev_priv *uc_priv = dev_get_uclass_priv(udev);
+ struct adi_gptimer_priv *priv = dev_get_priv(udev);
+ struct clk clk;
+ u16 imask;
+ int ret;
+
+ priv->timer_group = dev_remap_addr_index(udev, 0);
+ priv->timer_base = dev_remap_addr_index(udev, 1);
+ priv->upper = 0;
+ priv->prev = 0;
+
+ if (!priv->timer_group || !priv->timer_base) {
+ dev_err(udev, "Missing timer_group or timer_base reg entries\n");
+ return -ENODEV;
+ }
+
+ ret = clk_get_by_index(udev, 0, &clk);
+ if (ret < 0) {
+ dev_err(udev, "Missing clock reference for timer\n");
+ return ret;
+ }
+
+ ret = clk_enable(&clk);
+ if (ret) {
+ dev_err(udev, "Failed to enable clock\n");
+ return ret;
+ }
+
+ uc_priv->clock_rate = clk_get_rate(&clk);
+
+ /* Enable timer */
+ writew(TIMER_OUT_DIS | TIMER_MODE_PWM_CONT | TIMER_PULSE_HI,
+ &priv->timer_base->config);
+ writel(MAX_TIM_LOAD, &priv->timer_base->period);
+ writel(MAX_TIM_LOAD - 1, &priv->timer_base->width);
+
+ /* We only use timer 0 in uboot */
+ imask = readw(&priv->timer_group->data_imsk);
+ imask &= ~(1 << 0);
+ writew(imask, &priv->timer_group->data_imsk);
+ writew((1 << 0), &priv->timer_group->enable);
+
+ return 0;
+}
+
+static const struct udevice_id adi_gptimer_ids[] = {
+ { .compatible = "adi,sc5xx-gptimer" },
+ { },
+};
+
+U_BOOT_DRIVER(adi_gptimer) = {
+ .name = "adi_gptimer",
+ .id = UCLASS_TIMER,
+ .of_match = adi_gptimer_ids,
+ .priv_auto = sizeof(struct adi_gptimer_priv),
+ .probe = adi_gptimer_probe,
+ .ops = &adi_gptimer_ops,
+};