diff options
| author | Tom Rini <[email protected]> | 2024-05-07 19:24:07 -0600 |
|---|---|---|
| committer | Tom Rini <[email protected]> | 2024-05-07 19:24:07 -0600 |
| commit | 1afa75c087a7961428f9cd9d0f47d5baa928c445 (patch) | |
| tree | f4250822a6e6a3d0f1423c02aa7d55de6a1f60db /drivers/timer | |
| parent | 7e2938beabac24e6c8baad33e254b2383dbe9490 (diff) | |
| parent | 8691ffa5c06f4a171d1c418cdb5711334dd2470d (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/Kconfig | 8 | ||||
| -rw-r--r-- | drivers/timer/Makefile | 1 | ||||
| -rw-r--r-- | drivers/timer/adi_sc5xx_timer.c | 145 |
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, +}; |
