aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--MAINTAINERS16
-rw-r--r--arch/arm/Kconfig5
-rw-r--r--arch/arm/Makefile1
-rw-r--r--arch/arm/include/asm/arch-adi/sc5xx/sc5xx.h39
-rw-r--r--arch/arm/include/asm/arch-adi/sc5xx/soc.h18
-rw-r--r--arch/arm/include/asm/arch-adi/sc5xx/spl.h43
-rw-r--r--arch/arm/mach-sc5xx/Kconfig475
-rw-r--r--arch/arm/mach-sc5xx/Makefile19
-rw-r--r--arch/arm/mach-sc5xx/config.mk16
-rw-r--r--arch/arm/mach-sc5xx/init/Makefile11
-rw-r--r--arch/arm/mach-sc5xx/init/clkinit.c558
-rw-r--r--arch/arm/mach-sc5xx/init/clkinit.h18
-rw-r--r--arch/arm/mach-sc5xx/init/dmcinit.c954
-rw-r--r--arch/arm/mach-sc5xx/init/dmcinit.h31
-rw-r--r--arch/arm/mach-sc5xx/init/mem/is43tr16512bl.h62
-rw-r--r--arch/arm/mach-sc5xx/init/mem/mt41k128m16jt.h50
-rw-r--r--arch/arm/mach-sc5xx/init/mem/mt41k512m16ha.h50
-rw-r--r--arch/arm/mach-sc5xx/init/mem/mt47h128m16rt.h49
-rw-r--r--arch/arm/mach-sc5xx/rcu.c22
-rw-r--r--arch/arm/mach-sc5xx/sc57x.c32
-rw-r--r--arch/arm/mach-sc5xx/sc58x.c32
-rw-r--r--arch/arm/mach-sc5xx/sc59x.c43
-rw-r--r--arch/arm/mach-sc5xx/sc59x_64.c97
-rw-r--r--arch/arm/mach-sc5xx/soc.c179
-rw-r--r--arch/arm/mach-sc5xx/spl.c102
-rw-r--r--drivers/clk/Kconfig1
-rw-r--r--drivers/clk/Makefile1
-rw-r--r--drivers/clk/adi/Kconfig83
-rw-r--r--drivers/clk/adi/Makefile16
-rw-r--r--drivers/clk/adi/clk-adi-pll.c93
-rw-r--r--drivers/clk/adi/clk-adi-sc57x.c206
-rw-r--r--drivers/clk/adi/clk-adi-sc58x.c222
-rw-r--r--drivers/clk/adi/clk-adi-sc594.c231
-rw-r--r--drivers/clk/adi/clk-adi-sc598.c308
-rw-r--r--drivers/clk/adi/clk-shared.c48
-rw-r--r--drivers/clk/adi/clk.h123
-rw-r--r--drivers/serial/Makefile1
-rw-r--r--drivers/serial/serial_adi_uart4.c225
-rw-r--r--drivers/timer/Kconfig8
-rw-r--r--drivers/timer/Makefile1
-rw-r--r--drivers/timer/adi_sc5xx_timer.c145
-rw-r--r--include/dt-bindings/clock/adi-sc5xx-clock.h271
-rw-r--r--include/env/adi/adi_boot.env122
43 files changed, 5027 insertions, 0 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index 7a3b4d3..6853288 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -598,6 +598,22 @@ R: Marc Murphy <marc.murphy@sancloud.com>
S: Supported
F: arch/arm/dts/am335x-sancloud*
+ARM SC5XX
+M: Nathan Barrett-Morrison <nathan.morrison@timesys.com>
+M: Greg Malysa <greg.malysa@timesys.com>
+M: Ian Roberts <ian.roberts@timesys.com>
+M: Vasileios Bimpikas <vasileios.bimpikas@analog.com>
+M: Utsav Agarwal <utsav.agarwal@analog.com>
+M: Arturs Artamonovs <arturs.artamonovs@analog.com>
+S: Supported
+T: git https://github.com/analogdevicesinc/lnxdsp-u-boot
+F: arch/arm/include/asm/arch-adi/
+F: arch/arm/mach-sc5xx/
+F: drivers/clk/adi/
+F: drivers/serial/serial_adi_uart4.c
+F: drivers/timer/adi_sc5xx_timer.c
+F: include/env/adi/
+
ARM SNAPDRAGON
M: Caleb Connolly <caleb.connolly@linaro.org>
M: Neil Armstrong <neil.armstrong@linaro.org>
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 93e12d8..8d46707 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -1852,6 +1852,9 @@ config TARGET_LS1046AFRWY
development platform that supports the QorIQ LS1046A
Layerscape Architecture processor.
+config ARCH_SC5XX
+ bool "Analog Devices SC5XX-processor family"
+
config TARGET_SL28
bool "Support sl28"
select ARCH_LS1028A
@@ -2285,6 +2288,8 @@ source "arch/arm/mach-rockchip/Kconfig"
source "arch/arm/mach-s5pc1xx/Kconfig"
+source "arch/arm/mach-sc5xx/Kconfig"
+
source "arch/arm/mach-snapdragon/Kconfig"
source "arch/arm/mach-socfpga/Kconfig"
diff --git a/arch/arm/Makefile b/arch/arm/Makefile
index a4266a3..734c6d6 100644
--- a/arch/arm/Makefile
+++ b/arch/arm/Makefile
@@ -78,6 +78,7 @@ machine-$(CONFIG_ARCH_OWL) += owl
machine-$(CONFIG_ARCH_RENESAS) += renesas
machine-$(CONFIG_ARCH_ROCKCHIP) += rockchip
machine-$(CONFIG_ARCH_S5PC1XX) += s5pc1xx
+machine-$(CONFIG_ARCH_SC5XX) += sc5xx
machine-$(CONFIG_ARCH_SNAPDRAGON) += snapdragon
machine-$(CONFIG_ARCH_SOCFPGA) += socfpga
machine-$(CONFIG_ARCH_STM32) += stm32
diff --git a/arch/arm/include/asm/arch-adi/sc5xx/sc5xx.h b/arch/arm/include/asm/arch-adi/sc5xx/sc5xx.h
new file mode 100644
index 0000000..683e3d4
--- /dev/null
+++ b/arch/arm/include/asm/arch-adi/sc5xx/sc5xx.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * (C) Copyright 2022 - Analog Devices, Inc.
+ *
+ * Written and/or maintained by Timesys Corporation
+ *
+ * Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com>
+ * Contact: Greg Malysa <greg.malysa@timesys.com>
+ */
+#ifndef ARCH_ADI_SC5XX_SC5XX_H
+#define ARCH_ADI_SC5XX_SC5XX_H
+
+#include <linux/types.h>
+
+#define TWI0_CLKDIV 0x31001400 // TWI0 SCL Clock Divider Register
+#define TWI1_CLKDIV 0x31001500 // TWI1 SCL Clock Divider Register
+#define TWI2_CLKDIV 0x31001600 // TWI2 SCL Clock Divider Register
+
+const char *sc5xx_get_boot_mode(u32 *bmode);
+void sc5xx_enable_rgmii(void);
+
+void sc5xx_enable_ns_sharc_access(uintptr_t securec0_base);
+void sc5xx_disable_spu0(uintptr_t spu0_start, uintptr_t spu0_end);
+void sc5xx_enable_pmu(void);
+
+/**
+ * Per-SoC init function to be used to initialize hw-specific things. Examples:
+ * enable PMU on armv7, enable coresight timer on armv8, etc.
+ */
+void sc5xx_soc_init(void);
+
+/*
+ * Reconfigure SPI memory map region for OSPI use. The adi-spi3 driver
+ * does not use the memory map, while the OSPI driver requires it. Only
+ * available on sc59x and sc59x-64
+ */
+void sc59x_remap_ospi(void);
+
+#endif
diff --git a/arch/arm/include/asm/arch-adi/sc5xx/soc.h b/arch/arm/include/asm/arch-adi/sc5xx/soc.h
new file mode 100644
index 0000000..430dbe2
--- /dev/null
+++ b/arch/arm/include/asm/arch-adi/sc5xx/soc.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * (C) Copyright 2022 - Analog Devices, Inc.
+ *
+ * Written and/or maintained by Timesys Corporation
+ *
+ * Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com>
+ * Contact: Greg Malysa <greg.malysa@timesys.com>
+ */
+
+#ifndef BOARD_ADI_COMMON_SOC_H
+#define BOARD_ADI_COMMON_SOC_H
+
+#include <phy.h>
+
+void fixup_dp83867_phy(struct phy_device *phydev);
+
+#endif
diff --git a/arch/arm/include/asm/arch-adi/sc5xx/spl.h b/arch/arm/include/asm/arch-adi/sc5xx/spl.h
new file mode 100644
index 0000000..c215e6b
--- /dev/null
+++ b/arch/arm/include/asm/arch-adi/sc5xx/spl.h
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * (C) Copyright 2022 - Analog Devices, Inc.
+ *
+ * Written and/or maintained by Timesys Corporation
+ *
+ * Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com>
+ * Contact: Greg Malysa <greg.malysa@timesys.com>
+ */
+#ifndef ARCH_ADI_SC5XX_SPL_H
+#define ARCH_ADI_SC5XX_SPL_H
+
+#include <linux/types.h>
+
+struct adi_boot_args {
+ phys_addr_t addr;
+ u32 flags;
+ u32 cmd;
+};
+
+extern u32 bmode;
+
+/**
+ * This table stores the arguments to the rom boot function per bootmode,
+ * and it is populated per SoC in the corresponding SoC support file (sc7x, sc58x,
+ * and so on).
+ */
+extern const struct adi_boot_args adi_rom_boot_args[8];
+
+/**
+ * Struct layout for the boot config is also specific to an SoC, so you should
+ * only access it inside an SoC-specific boot hook function, which will be called
+ * from the boot rom while going from SPL to proper u-boot
+ */
+struct ADI_ROM_BOOT_CONFIG;
+int32_t adi_rom_boot_hook(struct ADI_ROM_BOOT_CONFIG *cfg, int32_t cause);
+
+typedef void (*adi_rom_boot_fn)(void *address, uint32_t flags, int32_t count,
+ void *hook, uint32_t command);
+
+extern adi_rom_boot_fn adi_rom_boot;
+
+#endif
diff --git a/arch/arm/mach-sc5xx/Kconfig b/arch/arm/mach-sc5xx/Kconfig
new file mode 100644
index 0000000..3846b4f
--- /dev/null
+++ b/arch/arm/mach-sc5xx/Kconfig
@@ -0,0 +1,475 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# (C) Copyright 2022 - Analog Devices, Inc.
+#
+# Written and/or maintained by Timesys Corporation
+#
+# Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com>
+# Contact: Greg Malysa <greg.malysa@timesys.com>
+#
+
+# All 32-bit platforms require SYS_ARM_CACHE_WRITETHROUGH
+# But it is ignored if selected here, so it must be in the defconfig
+
+if ARCH_SC5XX
+
+config SC57X
+ bool
+ select SUPPORT_SPL
+ select CPU_V7A
+ select PANIC_HANG
+ select COMMON_CLK_ADI_SC57X
+ select TIMER
+ select ADI_SC5XX_TIMER
+
+config SC58X
+ bool
+ select SUPPORT_SPL
+ select CPU_V7A
+ select PANIC_HANG
+ select COMMON_CLK_ADI_SC58X
+ select TIMER
+ select ADI_SC5XX_TIMER
+
+config SC59X
+ bool
+ select SUPPORT_SPL
+ select CPU_V7A
+ select PANIC_HANG
+ select COMMON_CLK_ADI_SC594
+ select TIMER
+ select ADI_SC5XX_TIMER
+ select NOP_PHY
+
+config SC59X_64
+ bool
+ select SUPPORT_SPL
+ select PANIC_HANG
+ select MMC_SDHCI_ADMA_FORCE_32BIT
+ select ARM64
+ select DM
+ select DM_SERIAL
+ select COMMON_CLK_ADI_SC598
+ select GICV3
+ select GIC_600_CLEAR_RDPD
+ select NOP_PHY
+
+config SC_BOOT_MODE
+ int "SC5XX boot mode select"
+ default 1
+ range 0 7
+ help
+ Mode 0: do nothing, just idle
+ Mode 1: boot ldr out of serial flash
+ Mode 7: boot ldr over uart
+
+config SC_BOOT_SPI_BUS
+ int "sc5xx spi boot bus"
+ default 2
+ range 0 4
+ help
+ This is the SPI peripheral number to use for booting, X in the
+ expression `sf probe X:Y`
+
+config SC_BOOT_SPI_SSEL
+ int "sc5xx spi boot chipselect"
+ default 1
+ range 0 6
+ help
+ This is the SPI chip select number to use for booting, Y in the
+ expression `sf probe X:Y`
+
+config SC_BOOT_OSPI_BUS
+ int "sc5xx ospi boot bus"
+ default 0
+ help
+ This is the OSPI peripheral number to use for booting, X in the
+ expression `sf probe X:Y`
+
+config SC_BOOT_OSPI_SSEL
+ int "sc5xx ospi boot chipselect"
+ default 0
+ help
+ This is the OSPI chip select number to use for booting, Y in the
+ expression `sf probe X:Y`
+
+config SYS_FLASH_BASE
+ hex
+ default 0x60000000
+
+config UART_CONSOLE
+ int
+ default 0
+
+config UART4_SERIAL
+ bool
+ depends on DM_SERIAL
+ default y
+
+config WDT_ADI
+ bool
+ default y
+
+config WATCHDOG_TIMEOUT_MSECS
+ int
+ default 30000
+
+config DW_PORTS
+ int
+ default 1
+
+config ADI_BUG_EZKHW21
+ bool "SC584 EZKIT phy bug workaround"
+ depends on SC58X
+ help
+ This workaround affects the SC584 EZKIT and addresses bug EZKHW21.
+ It disables gigabit ethernet mode and limits the board to 100 Mbps
+
+config ADI_CARRIER_SOMCRR_EZKIT
+ bool "Support the EV-SOMCRR-EZKIT"
+ depends on (SC59X || SC59X_64)
+ help
+ Say y to include support for the EV-SOMCRR-EZKIT carrier board,
+ which is compatible with the SC594 and SC598 SOMs. The EZKIT is
+ mutually incompatible with the EZLITE.
+
+config ADI_CARRIER_SOMCRR_EZLITE
+ bool "Support the EV-SOMCRR-EZLITE"
+ depends on (SC59X || SC59X_64)
+ help
+ Say y to include support for the EV-SOMCRR-EZLITE carrier board,
+ which is compatible with the SC594 and SC598 SOMs. The EZLITE is
+ mutually incompatible with the EZKIT.
+
+config ADI_SPL_FORCE_BMODE
+ int "Force the SPL to use this BMODE device during next boot stage"
+ default 0
+ range 0 9
+ depends on SPL
+ help
+ Force the SPL to use this BMODE device during next boot stage.
+ For example, if booting via QSPI, we can force the second stage
+ Of the boot process to use other peripherals via:
+ 1 = QSPI -> QSPI
+ 5 = QSPI -> OSPI
+ 6 = QSPI -> eMMC
+
+config ADI_USE_DMC0
+ bool "Configure DMC0"
+ default y
+ help
+ During hardware initialization, channel 0 of the DMC will be
+ initialized. Select this if you have DMC0 connected to external
+ DDR memory. This is expected to be true for every board using
+ an SC5xx SoC.
+
+config ADI_USE_DMC1
+ bool "Configure DMC1"
+ help
+ During hardware initialization, channel 1 of the DMC will be
+ initialized. Not all processors have a DMC1. Select this if your
+ SoC has DMC1 and you have it connected to external DDR memory.
+
+config ADI_USE_DDR2
+ bool "Configure DMC for DDR2 mode"
+ help
+ Configure the DMC in DDR2 mode. The default is DDR3 and not all
+ parts may actually support DDR2. Please consult the manual for
+ the SoC that you are using to determine if DDR2 mode is supported.
+ This also requires that DDR2 memory is present on the board or it
+ will probably cause strange failure.
+
+menu "Clock configuration"
+
+config CGU0_DF_DIV
+ int "CGU0_DF_DIV"
+ range 0 1
+ help
+ Select 0 to pass CLKIN to PLL
+ Select 1 to pass CLKIN/2 to PLL
+
+config CGU0_VCO_MULT
+ int "CGU0_VCO_MULT"
+ range 0 127
+ help
+ VCO_MULT controls the MSEL (multiplier) bits in PLL_CTL
+ A value of 0 means 128
+
+config CGU0_CCLK_DIV
+ int "CGU0_CCLK_DIV"
+ range 0 31
+ help
+ CCLK_DIV controls the core clock divider
+ A value of 0 means 32
+ CCLK = ((CLKIN / (1 + DF)) * VCO_MULT) / CCLK_DIV
+
+config CGU0_SCLK_DIV
+ int "CGU0_SCLK_DIV"
+ range 0 31
+ help
+ SCLK_DIV controls the system clock divider
+ A value of 0 means 32
+ SCLK = ((CLKIN / (1 + DF)) * VCO_MULT) / SYSCLK_DIV
+
+config CGU0_SCLK0_DIV
+ int "CGU0_SCLK0_DIV"
+ range 0 7
+ help
+ A value of 0 means 8
+ SCLK0 = SCLK / SCLK0_DIV
+
+config CGU0_SCLK1_DIV
+ int "CGU0_SCLK1_DIV"
+ depends on (SC57X || SC58X)
+ range 0 7
+ help
+ A value of 0 means 8
+ SCLK1 = SCLK / SCLK1_DIV
+
+config CGU0_DCLK_DIV
+ int "CGU0_DCLK_DIV"
+ range 0 31
+ help
+ DCLK_DIV controls the DDR clock divider
+ A value of 0 means 32
+ DCLK = ((CLKIN / (1 + DF)) * VCO_MULT) / DCLK_DIV
+
+config CGU0_OCLK_DIV
+ int "CGU0_OCLK_DIV"
+ range 0 127
+ help
+ OCLK_DIV controls the output clock divider
+ A value of 0 means 128
+ OCLK = ((CLKIN / (1 + DF)) * VCO_MULT) / OCLK_DIV
+
+config CGU0_DIV_S1SELEX
+ int "CGU0_DIV_S1SELEX"
+ depends on !SC57X && !SC58X
+ range 0 255
+ help
+ CGU0 SCLK1 Extended divisor register.
+ A value of 0 means 256.
+ SCLK1 = ((CLKIN / (1 + DF)) * VCO_MULT) / DIV_S1SELEX
+
+config CGU0_CLKOUTSEL
+ int "CGU0_CLKOUTSEL"
+ default 0
+ range 0 31
+ help
+ Select signal driven through CLKOUT pin multiplexer.
+ This value varies on each SOC. Refer to
+ CGU_CLKOUTSEL.CLKOUTSEL in the Hardware Reference Manual
+ for values applicable to each SOC.
+ Commonly, values 0 and 1 select CLKIN0 or CLKIN1 respectively.
+
+config CGU1_PLL3_DDRCLK
+ bool "DDRCLK From 3rd PLL"
+ depends on SC59X_64
+ help
+ 3rd PLL output is connected to DMC block when set.
+ When cleared, DDR clock is CLKO3 output of CDU.
+
+config CGU1_PLL3_VCO_MSEL
+ int "CGU0_PLL3_VCO_MSEL"
+ depends on CGU1_PLL3_DDRCLK
+ range 1 128
+ help
+ PLL multiplier value for the 3rd PLL.
+ DCLK = (CLKIN * PLL3_VCO_MSEL) / PLL3_DCLK_DIV
+
+config CGU1_PLL3_DCLK_DIV
+ int "CGU0_PLL3_DCLK_DIV"
+ depends on CGU1_PLL3_DDRCLK
+ range 1 32
+ help
+ PLL divider value for the 3rd PLL.
+ DCLK = (CLKIN * PLL3_VCO_MSEL) / PLL3_DCLK_DIV
+
+config CGU1_DF_DIV
+ int "CGU1_DF_DIV"
+ range 0 1
+ help
+ Select 0 to pass CLKIN to PLL
+ Select 1 to pass CLKIN/2 to PLL
+
+config CGU1_VCO_MULT
+ int "CGU1_VCO_MULT"
+ range 0 127
+ help
+ VCO_MULT controls the MSEL (multiplier) bits in PLL_CTL
+ A value of 0 means 128
+
+config CGU1_CCLK_DIV
+ int "CGU1_CCLK_DIV"
+ range 0 31
+ help
+ CCLK_DIV controls the core clock divider
+ A value of 0 means 32
+ CCLK = ((CLKIN / (1 + DF)) * VCO_MULT) / CCLK_DIV
+
+config CGU1_SCLK_DIV
+ int "CGU1_SCLK_DIV"
+ range 0 31
+ help
+ SCLK_DIV controls the system clock divider
+ A value of 0 means 32
+ SCLK = ((CLKIN / (1 + DF)) * VCO_MULT) / SYSCLK_DIV
+
+config CGU1_SCLK0_DIV
+ int "CGU1_SCLK0_DIV"
+ depends on (SC57X || SC58X || SC59X)
+ range 0 7
+ help
+ A value of 0 means 8
+ SCLK0 = SCLK / SCLK0_DIV
+
+config CGU1_SCLK1_DIV
+ int "CGU1_SCLK1_DIV"
+ depends on (SC57X || SC58X)
+ range 0 7
+ help
+ A value of 0 means 8
+ SCLK1 = SCLK / SCLK1_DIV
+
+config CGU1_DCLK_DIV
+ int "CGU1_DCLK_DIV"
+ range 0 31
+ help
+ DCLK_DIV controls the DDR clock divider
+ A value of 0 means 32
+ DCLK = ((CLKIN / (1 + DF)) * VCO_MULT) / DCLK_DIV
+
+config CGU1_OCLK_DIV
+ int "CGU1_OCLK_DIV"
+ range 0 127
+ help
+ OCLK_DIV controls the output clock divider
+ A value of 0 means 128
+ OCLK = ((CLKIN / (1 + DF)) * VCO_MULT) / OCLK_DIV
+
+config CGU1_DIV_S0SELEX
+ int "CGU1_DIV_S0SELEX"
+ depends on !SC57X && !SC58X && !SC59X
+ range 0 255
+ help
+ CGU1 SCLK0 Extended divisor register.
+ A value of 0 means 256.
+ SCLK0 = ((CLKIN / (1 + DF)) * VCO_MULT) / DIV_S0SELEX
+
+config CGU1_DIV_S1SELEX
+ int "CGU1_DIV_S1SELEX"
+ depends on !SC57X && !SC58X
+ range 0 255
+ help
+ CGU1 SCLK1 Extended divisor register.
+ A value of 0 means 256.
+ SCLK1 = ((CLKIN / (1 + DF)) * VCO_MULT) / DIV_S1SELEX
+
+config CDU0_CGU1_CLKIN
+ int "CDU0 CGU1 CLKINn Select"
+ default 0
+ range 0 1
+ help
+ Selects source clock for CGU1.
+ 0 for CLKIN0
+ 1 for CLKIN1
+
+config CDU0_CLKO0
+ int "CDU0_CLKO0"
+ range 1 7
+ help
+ Clock source select. Refer to SOC Hardware Reference Manual
+
+config CDU0_CLKO1
+ int "CDU0_CLKO1"
+ range 1 7
+ help
+ Clock source select. Refer to SOC Hardware Reference Manual
+
+config CDU0_CLKO2
+ int "CDU0_CLKO2"
+ range 1 7
+ help
+ Clock source select. Refer to SOC Hardware Reference Manual
+
+config CDU0_CLKO3
+ int "CDU0_CLKO3"
+ range 1 7
+ help
+ Clock source select. Refer to SOC Hardware Reference Manual
+
+config CDU0_CLKO4
+ int "CDU0_CLKO4"
+ range 1 7
+ help
+ Clock source select. Refer to SOC Hardware Reference Manual
+
+config CDU0_CLKO5
+ int "CDU0_CLKO5"
+ range 1 7
+ help
+ Clock source select. Refer to SOC Hardware Reference Manual
+
+config CDU0_CLKO6
+ int "CDU0_CLKO6"
+ range 1 7
+ help
+ Clock source select. Refer to SOC Hardware Reference Manual
+
+config CDU0_CLKO7
+ int "CDU0_CLKO7"
+ range 1 7
+ help
+ Clock source select. Refer to SOC Hardware Reference Manual
+
+config CDU0_CLKO8
+ int "CDU0_CLKO8"
+ range 1 7
+ help
+ Clock source select. Refer to SOC Hardware Reference Manual
+
+config CDU0_CLKO9
+ int "CDU0_CLKO9"
+ range 1 7
+ help
+ Clock source select. Refer to SOC Hardware Reference Manual
+
+config CDU0_CLKO10
+ int "CDU0_CLKO10"
+ range 1 7
+ depends on (SC59X || SC59X_64)
+ help
+ Clock source select. Refer to SOC Hardware Reference Manual
+
+config CDU0_CLKO12
+ int "CDU0_CLKO12"
+ range 1 7
+ depends on (SC59X || SC59X_64)
+ help
+ Clock source select. Refer to SOC Hardware Reference Manual
+
+config CDU0_CLKO13
+ int "CDU0_CLKO13"
+ range 1 7
+ depends on SC59X_64
+ help
+ Clock source select. Refer to SOC Hardware Reference Manual
+
+config CDU0_CLKO14
+ int "CDU0_CLKO14"
+ range 1 7
+ depends on SC59X_64
+ help
+ Clock source select. Refer to SOC Hardware Reference Manual
+
+endmenu
+
+config ADI_GPIO
+ bool
+ default y
+
+config PINCTRL_ADI
+ bool
+ default y
+
+endif
diff --git a/arch/arm/mach-sc5xx/Makefile b/arch/arm/mach-sc5xx/Makefile
new file mode 100644
index 0000000..eeb56c0
--- /dev/null
+++ b/arch/arm/mach-sc5xx/Makefile
@@ -0,0 +1,19 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# (C) Copyright 2022 - Analog Devices, Inc.
+#
+# Written and/or maintained by Timesys Corporation
+#
+# Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com>
+# Contact: Greg Malysa <greg.malysa@timesys.com>
+#
+
+obj-y += soc.o init/
+
+obj-$(CONFIG_SC57X) += sc57x.o
+obj-$(CONFIG_SC58X) += sc58x.o
+obj-$(CONFIG_SC59X) += sc59x.o
+obj-$(CONFIG_SC59X_64) += sc59x_64.o
+
+obj-$(CONFIG_SPL_BUILD) += spl.o
+obj-$(CONFIG_SYSCON) += rcu.o
diff --git a/arch/arm/mach-sc5xx/config.mk b/arch/arm/mach-sc5xx/config.mk
new file mode 100644
index 0000000..580964e
--- /dev/null
+++ b/arch/arm/mach-sc5xx/config.mk
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# (C) Copyright 2022 - Analog Devices, Inc.
+#
+# Written and/or maintained by Timesys Corporation
+#
+# Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com>
+# Contact: Greg Malysa <greg.malysa@timesys.com>
+#
+
+ifdef CONFIG_SPL_BUILD
+INPUTS-y += $(obj)/u-boot-spl.ldr
+endif
+
+LDR_FLAGS += --bcode=$(CONFIG_SC_BOOT_MODE)
+LDR_FLAGS += --use-vmas
diff --git a/arch/arm/mach-sc5xx/init/Makefile b/arch/arm/mach-sc5xx/init/Makefile
new file mode 100644
index 0000000..9d4920f
--- /dev/null
+++ b/arch/arm/mach-sc5xx/init/Makefile
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# (C) Copyright 2022 - Analog Devices, Inc.
+#
+# Written and/or maintained by Timesys Corporation
+#
+# Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com>
+# Contact: Greg Malysa <greg.malysa@timesys.com>
+#
+
+obj-y += dmcinit.o clkinit.o
diff --git a/arch/arm/mach-sc5xx/init/clkinit.c b/arch/arm/mach-sc5xx/init/clkinit.c
new file mode 100644
index 0000000..ae53cd6
--- /dev/null
+++ b/arch/arm/mach-sc5xx/init/clkinit.c
@@ -0,0 +1,558 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * (C) Copyright 2022 - Analog Devices, Inc.
+ *
+ * Written and/or maintained by Timesys Corporation
+ *
+ * Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com>
+ * Contact: Greg Malysa <greg.malysa@timesys.com>
+ */
+
+#include <asm/arch-adi/sc5xx/sc5xx.h>
+#include <asm/io.h>
+#include <linux/types.h>
+#include "clkinit.h"
+#include "dmcinit.h"
+
+#ifdef CONFIG_CGU0_SCLK0_DIV
+ #define VAL_CGU0_SCLK0_DIV CONFIG_CGU0_SCLK0_DIV
+#else
+ #define VAL_CGU0_SCLK0_DIV 1
+#endif
+#ifdef CONFIG_CGU0_SCLK1_DIV
+ #define VAL_CGU0_SCLK1_DIV CONFIG_CGU0_SCLK1_DIV
+#else
+ #define VAL_CGU0_SCLK1_DIV 1
+#endif
+#ifdef CONFIG_CGU0_DIV_S0SELEX
+ #define VAL_CGU0_DIV_S0SELEX CONFIG_CGU0_DIV_S0SELEX
+#else
+ #define VAL_CGU0_DIV_S0SELEX -1
+#endif
+#ifdef CONFIG_CGU0_DIV_S1SELEX
+ #define VAL_CGU0_DIV_S1SELEX CONFIG_CGU0_DIV_S1SELEX
+#else
+ #define VAL_CGU0_DIV_S1SELEX -1
+#endif
+#ifdef CONFIG_CGU0_CLKOUTSEL
+ #define VAL_CGU0_CLKOUTSEL CONFIG_CGU0_CLKOUTSEL
+#else
+ #define VAL_CGU0_CLKOUTSEL -1
+#endif
+#ifdef CONFIG_CGU1_SCLK0_DIV
+ #define VAL_CGU1_SCLK0_DIV CONFIG_CGU1_SCLK0_DIV
+#else
+ #define VAL_CGU1_SCLK0_DIV 1
+#endif
+#ifdef CONFIG_CGU1_SCLK1_DIV
+ #define VAL_CGU1_SCLK1_DIV CONFIG_CGU1_SCLK1_DIV
+#else
+ #define VAL_CGU1_SCLK1_DIV 1
+#endif
+#ifdef CONFIG_CGU1_DIV_S0SELEX
+ #define VAL_CGU1_DIV_S0SELEX CONFIG_CGU1_DIV_S0SELEX
+#else
+ #define VAL_CGU1_DIV_S0SELEX -1
+#endif
+#ifdef CONFIG_CGU1_DIV_S1SELEX
+ #define VAL_CGU1_DIV_S1SELEX CONFIG_CGU1_DIV_S1SELEX
+#else
+ #define VAL_CGU1_DIV_S1SELEX -1
+#endif
+#ifdef CONFIG_CGU1_CLKOUTSEL
+ #define VAL_CGU1_CLKOUTSEL CONFIG_CGU1_CLKOUTSEL
+#else
+ #define VAL_CGU1_CLKOUTSEL -1
+#endif
+
+#define REG_MISC_REG10_tst_addr 0x310A902C
+
+#define CGU0_REGBASE 0x3108D000
+#define CGU1_REGBASE 0x3108E000
+
+#define CGU_CTL 0x00 // CGU0 Control Register
+#define CGU_PLLCTL 0x04 // CGU0 PLL Control Register
+#define CGU_STAT 0x08 // CGU0 Status Register
+#define CGU_DIV 0x0C // CGU0 Clocks Divisor Register
+#define CGU_CLKOUTSEL 0x10 // CGU0 CLKOUT Select Register
+#define CGU_DIVEX 0x40 // CGU0 DIV Register Extension
+
+#define BITP_CGU_DIV_OSEL 22 // OUTCLK Divisor
+#define BITP_CGU_DIV_DSEL 16 // DCLK Divisor
+#define BITP_CGU_DIV_S1SEL 13 // SCLK 1 Divisor
+#define BITP_CGU_DIV_SYSSEL 8 // SYSCLK Divisor
+#define BITP_CGU_DIV_S0SEL 5 // SCLK 0 Divisor
+#define BITP_CGU_DIV_CSEL 0 // CCLK Divisor
+
+#define BITP_CGU_CTL_MSEL 8 // Multiplier Select
+#define BITP_CGU_CTL_DF 0 // Divide Frequency
+
+#define BITM_CGU_STAT_CLKSALGN 0x00000008
+#define BITM_CGU_STAT_PLOCK 0x00000004
+#define BITM_CGU_STAT_PLLBP 0x00000002
+#define BITM_CGU_STAT_PLLEN 0x00000001
+
+/* PLL Multiplier and Divisor Selections (Required Value, Bit Position) */
+/* PLL Multiplier Select */
+#define MSEL(X) (((X) << BITP_CGU_CTL_MSEL) & \
+ BITM_CGU_CTL_MSEL)
+/* Divide frequency[true or false] */
+#define DF(X) (((X) << BITP_CGU_CTL_DF) & \
+ BITM_CGU_CTL_DF)
+/* Core Clock Divisor Select */
+#define CSEL(X) (((X) << BITP_CGU_DIV_CSEL) & \
+ BITM_CGU_DIV_CSEL)
+/* System Clock Divisor Select */
+#define SYSSEL(X) (((X) << BITP_CGU_DIV_SYSSEL) & \
+ BITM_CGU_DIV_SYSSEL)
+/* SCLK0 Divisor Select */
+#define S0SEL(X) (((X) << BITP_CGU_DIV_S0SEL) & \
+ BITM_CGU_DIV_S0SEL)
+/* SCLK1 Divisor Select */
+#define S1SEL(X) (((X) << BITP_CGU_DIV_S1SEL) & \
+ BITM_CGU_DIV_S1SEL)
+/* DDR Clock Divisor Select */
+#define DSEL(X) (((X) << BITP_CGU_DIV_DSEL) & \
+ BITM_CGU_DIV_DSEL)
+/* OUTCLK Divisor Select */
+#define OSEL(X) (((X) << BITP_CGU_DIV_OSEL) & \
+ BITM_CGU_DIV_OSEL)
+/* CLKOUT select */
+#define CLKOUTSEL(X) (((X) << BITP_CGU_CLKOUTSEL_CLKOUTSEL) & \
+ BITM_CGU_CLKOUTSEL_CLKOUTSEL)
+#define S0SELEX(X) (((X) << BITP_CGU_DIVEX_S0SELEX) & \
+ BITM_CGU_DIVEX_S0SELEX)
+#define S1SELEX(X) (((X) << BITP_CGU_DIVEX_S1SELEX) & \
+ BITM_CGU_DIVEX_S1SELEX)
+
+struct CGU_Settings {
+ phys_addr_t rbase;
+ u32 ctl_MSEL:7;
+ u32 ctl_DF:1;
+ u32 div_CSEL:5;
+ u32 div_SYSSEL:5;
+ u32 div_S0SEL:3;
+ u32 div_S1SEL:3;
+ u32 div_DSEL:5;
+ u32 div_OSEL:7;
+ s16 divex_S0SELEX;
+ s16 divex_S1SELEX;
+ s8 clkoutsel;
+};
+
+/* CGU Registers */
+#define BITM_CGU_CTL_LOCK 0x80000000 /* Lock */
+
+#define BITM_CGU_CTL_MSEL 0x00007F00 /* Multiplier Select */
+#define BITM_CGU_CTL_DF 0x00000001 /* Divide Frequency */
+#define BITM_CGU_CTL_S1SELEXEN 0x00020000 /* SCLK1 Extension Divider Enable */
+#define BITM_CGU_CTL_S0SELEXEN 0x00010000 /* SCLK0 Extension Divider Enable */
+
+#define BITM_CGU_DIV_LOCK 0x80000000 /* Lock */
+#define BITM_CGU_DIV_UPDT 0x40000000 /* Update Clock Divisors */
+#define BITM_CGU_DIV_ALGN 0x20000000 /* Align */
+#define BITM_CGU_DIV_OSEL 0x1FC00000 /* OUTCLK Divisor */
+#define BITM_CGU_DIV_DSEL 0x001F0000 /* DCLK Divisor */
+#define BITM_CGU_DIV_S1SEL 0x0000E000 /* SCLK 1 Divisor */
+#define BITM_CGU_DIV_SYSSEL 0x00001F00 /* SYSCLK Divisor */
+#define BITM_CGU_DIV_S0SEL 0x000000E0 /* SCLK 0 Divisor */
+#define BITM_CGU_DIV_CSEL 0x0000001F /* CCLK Divisor */
+
+#define BITP_CGU_DIVEX_S0SELEX 0
+#define BITM_CGU_DIVEX_S0SELEX 0x000000FF /* SCLK 0 Extension Divisor */
+
+#define BITP_CGU_DIVEX_S1SELEX 16
+#define BITM_CGU_DIVEX_S1SELEX 0x00FF0000 /* SCLK 1 Extension Divisor */
+
+#define BITM_CGU_PLLCTL_PLLEN 0x00000008 /* PLL Enable */
+#define BITM_CGU_PLLCTL_PLLBPCL 0x00000002 /* PLL Bypass Clear */
+#define BITM_CGU_PLLCTL_PLLBPST 0x00000001 /* PLL Bypass Set */
+
+#define BITP_CGU_CLKOUTSEL_CLKOUTSEL 0 /* CLKOUT Select */
+#define BITM_CGU_CLKOUTSEL_CLKOUTSEL 0x0000001F /* CLKOUT Select */
+
+#define CGU_STAT_MASK (BITM_CGU_STAT_PLLEN | BITM_CGU_STAT_PLOCK | \
+ BITM_CGU_STAT_CLKSALGN)
+#define CGU_STAT_ALGN_LOCK (BITM_CGU_STAT_PLLEN | BITM_CGU_STAT_PLOCK)
+
+/* Clock Distribution Unit Registers */
+#define REG_CDU0_CFG0 0x3108F000
+#define REG_CDU0_CFG1 0x3108F004
+#define REG_CDU0_CFG2 0x3108F008
+#define REG_CDU0_CFG3 0x3108F00C
+#define REG_CDU0_CFG4 0x3108F010
+#define REG_CDU0_CFG5 0x3108F014
+#define REG_CDU0_CFG6 0x3108F018
+#define REG_CDU0_CFG7 0x3108F01C
+#define REG_CDU0_CFG8 0x3108F020
+#define REG_CDU0_CFG9 0x3108F024
+#define REG_CDU0_CFG10 0x3108F028
+#define REG_CDU0_CFG11 0x3108F02C
+#define REG_CDU0_CFG12 0x3108F030
+#define REG_CDU0_CFG13 0x3108F034
+#define REG_CDU0_CFG14 0x3108F038
+#define REG_CDU0_STAT 0x3108F040
+#define REG_CDU0_CLKINSEL 0x3108F044
+#define REG_CDU0_REVID 0x3108F048
+
+#define BITM_REG10_MSEL3 0x000007F0
+#define BITP_REG10_MSEL3 4
+
+#define BITM_REG10_DSEL3 0x0001F000
+#define BITP_REG10_DSEL3 12
+
+/* Selected clock macros */
+#define CGUn_MULT(cgu) ((CONFIG_CGU##cgu##_VCO_MULT == 0) ? \
+ 128 : CONFIG_CGU##cgu##_VCO_MULT)
+#define CGUn_DIV(clkname, cgu) ((CONFIG_CGU##cgu##_##clkname##_DIV == 0) ? \
+ 32 : CONFIG_CGU##cgu##_##clkname##_DIV)
+#define CCLK1_n_RATIO(cgu) (((CGUn_MULT(cgu)) / \
+ (1 + CONFIG_CGU##cgu##_DF_DIV)) / \
+ CGUn_DIV(CCLK, cgu))
+#define CCLK2_n_RATIO(cgu) (((CGUn_MULT(cgu) * 2) / 3) / \
+ (1 + CONFIG_CGU##cgu##_DF_DIV))
+#define DCLK_n_RATIO(cgu) (((CGUn_MULT(cgu)) / \
+ (1 + CONFIG_CGU##cgu##_DF_DIV)) / \
+ CGUn_DIV(DCLK, cgu))
+#define SYSCLK_n_RATIO(cgu) (((CGUn_MULT(cgu)) / \
+ (1 + CONFIG_CGU##cgu##_DF_DIV)) / \
+ CGUn_DIV(SCLK, cgu))
+#define PLL3_RATIO ((CONFIG_CGU1_PLL3_VCO_MSEL) / \
+ (CONFIG_CGU1_PLL3_DCLK_DIV))
+
+#if (1 == CONFIG_CDU0_CLKO2)
+ #define ARMCLK_IN 0
+ #define ARMCLK_RATIO CCLK1_n_RATIO(0)
+#elif (3 == CONFIG_CDU0_CLKO2) && \
+ (defined(CONFIG_SC57X) || defined(CONFIG_SC58X))
+ #define ARMCLK_IN 0
+ #define ARMCLK_RATIO SYSCLK_n_RATIO(0)
+#elif (5 == CONFIG_CDU0_CLKO2) && defined(CONFIG_SC59X_64)
+ #define ARMCLK_IN 0
+ #define ARMCLK_RATIO CCLK2_n_RATIO(0)
+#elif (7 == CONFIG_CDU0_CLKO2) && defined(CONFIG_SC59X_64)
+ #define ARMCLK_IN CDU0_CGU1_CLKIN
+ #define ARMCLK_RATIO CCLK2_n_RATIO(1)
+#endif
+
+#ifdef CONFIG_CGU1_PLL3_DDRCLK
+ #define DDRCLK_IN CDU0_CGU1_CLKIN
+ #define DDRCLK_RATIO PLL3_RATIO
+#elif (1 == CONFIG_CDU0_CLKO3)
+ #define DDRCLK_IN 0
+ #define DDRCLK_RATIO DCLK_n_RATIO(0)
+#elif (3 == CONFIG_CDU0_CLKO3)
+ #define DDRCLK_IN CDU0_CGU1_CLKIN
+ #define DDRCLK_RATIO DCLK_n_RATIO(1)
+#endif
+
+#ifndef ARMCLK_RATIO
+ #error Invalid/unknown ARMCLK selection!
+#endif
+#ifndef DDRCLK_RATIO
+ #error Invalid/unknown DDRCLK selection!
+#endif
+
+#define ARMDDR_CLK_RATIO_FPERCISION 1000
+
+#if ARMCLK_IN != DDRCLK_IN
+ #ifndef CUSTOM_ARMDDR_CLK_RATIO
+ /**
+ * SYS_CLKINx are defined within the device tree, not configs.
+ * Thus, we can only determine cross-CGU clock ratios if they
+ * use the same SYS_CLKINx.
+ */
+ #error Define CUSTOM_ARMDDR_CLK_RATIO for different SYS_CLKINs
+ #else
+ #define ARMDDR_CLK_RATIO CUSTOM_ARMDDR_CLK_RATIO
+ #endif
+#else
+ #define ARMDDR_CLK_RATIO (ARMDDR_CLK_RATIO_FPERCISION *\
+ ARMCLK_RATIO / DDRCLK_RATIO)
+#endif
+
+void dmcdelay(uint32_t delay)
+{
+ /* There is no zero-overhead loop on ARM, so assume each iteration
+ * takes 4 processor cycles (based on examination of -O3 and -Ofast
+ * output).
+ */
+ u32 i, remainder;
+
+ /* Convert DDR cycles to core clock cycles */
+ u32 f = delay * ARMDDR_CLK_RATIO;
+
+ delay = f + 500;
+ delay /= ARMDDR_CLK_RATIO_FPERCISION;
+
+ /* Round up to multiple of 4 */
+ remainder = delay % 4;
+ if (remainder != 0u)
+ delay += (4u - remainder);
+
+ for (i = 0; i < delay; i += 4)
+ asm("nop");
+}
+
+static void program_cgu(const struct CGU_Settings *cgu)
+{
+ const uintptr_t b = cgu->rbase;
+ const bool use_extension0 = cgu->divex_S0SELEX >= 0;
+ const bool use_extension1 = cgu->divex_S1SELEX >= 0;
+ u32 temp;
+
+ temp = OSEL(cgu->div_OSEL);
+ temp |= SYSSEL(cgu->div_SYSSEL);
+ temp |= CSEL(cgu->div_CSEL);
+ temp |= DSEL(cgu->div_DSEL);
+ temp |= (S0SEL(cgu->div_S0SEL));
+ temp |= (S1SEL(cgu->div_S1SEL));
+ temp &= ~BITM_CGU_DIV_LOCK;
+
+ //Put PLL in to Bypass Mode
+ writel(BITM_CGU_PLLCTL_PLLEN | BITM_CGU_PLLCTL_PLLBPST,
+ b + CGU_PLLCTL);
+ while (!(readl(b + CGU_STAT) & BITM_CGU_STAT_PLLBP))
+ ;
+
+ while (!((readl(b + CGU_STAT) & CGU_STAT_MASK) == CGU_STAT_ALGN_LOCK))
+ ;
+
+ dmcdelay(1000);
+
+ writel(temp & (~BITM_CGU_DIV_ALGN) & (~BITM_CGU_DIV_UPDT),
+ b + CGU_DIV);
+
+ dmcdelay(1000);
+
+ temp = MSEL(cgu->ctl_MSEL) | DF(cgu->ctl_DF);
+ if (use_extension0)
+ temp |= BITM_CGU_CTL_S0SELEXEN;
+ if (use_extension1)
+ temp |= BITM_CGU_CTL_S1SELEXEN;
+
+ writel(temp & (~BITM_CGU_CTL_LOCK), b + CGU_CTL);
+
+ if (use_extension0 || use_extension1) {
+ u32 mask = BITM_CGU_CTL_S1SELEXEN | BITM_CGU_CTL_S0SELEXEN;
+
+ while (!(readl(b + CGU_CTL) & mask))
+ ;
+
+ temp = readl(b + CGU_DIVEX);
+
+ if (use_extension0) {
+ temp &= ~BITM_CGU_DIVEX_S0SELEX;
+ temp |= S0SELEX(cgu->divex_S0SELEX);
+ }
+
+ if (use_extension1) {
+ temp &= ~BITM_CGU_DIVEX_S1SELEX;
+ temp |= S1SELEX(cgu->divex_S1SELEX);
+ }
+
+ writel(temp, b + CGU_DIVEX);
+ }
+
+ dmcdelay(1000);
+
+ //Take PLL out of Bypass Mode
+ writel(BITM_CGU_PLLCTL_PLLEN | BITM_CGU_PLLCTL_PLLBPCL,
+ b + CGU_PLLCTL);
+ while ((readl(b + CGU_STAT) &
+ (BITM_CGU_STAT_PLLBP | BITM_CGU_STAT_CLKSALGN)))
+ ;
+
+ dmcdelay(1000);
+
+ if (cgu->clkoutsel >= 0) {
+ temp = readl(b + CGU_CLKOUTSEL);
+ temp &= ~BITM_CGU_CLKOUTSEL_CLKOUTSEL;
+ temp |= CLKOUTSEL(cgu->clkoutsel);
+ writel(temp, b + CGU_CLKOUTSEL);
+ }
+}
+
+void adi_config_third_pll(void)
+{
+#if defined(CONFIG_CGU1_PLL3_VCO_MSEL) && defined(CONFIG_CGU1_PLL3_DCLK_DIV)
+ u32 temp;
+
+ u32 msel = CONFIG_CGU1_PLL3_VCO_MSEL - 1;
+ u32 dsel = CONFIG_CGU1_PLL3_DCLK_DIV - 1;
+
+ temp = readl(REG_MISC_REG10_tst_addr);
+ temp &= 0xFFFE0000;
+ writel(temp, REG_MISC_REG10_tst_addr);
+
+ dmcdelay(4000u);
+
+ //update MSEL [10:4]
+ temp = readl(REG_MISC_REG10_tst_addr);
+ temp |= ((msel << BITP_REG10_MSEL3) & BITM_REG10_MSEL3);
+ writel(temp, REG_MISC_REG10_tst_addr);
+
+ temp = readl(REG_MISC_REG10_tst_addr);
+ temp |= 0x2;
+ writel(temp, REG_MISC_REG10_tst_addr);
+
+ dmcdelay(100000u);
+
+ temp = readl(REG_MISC_REG10_tst_addr);
+ temp |= 0x1;
+ writel(temp, REG_MISC_REG10_tst_addr);
+
+ temp = readl(REG_MISC_REG10_tst_addr);
+ temp |= 0x800;
+ writel(temp, REG_MISC_REG10_tst_addr);
+
+ temp = readl(REG_MISC_REG10_tst_addr);
+ temp &= 0xFFFFF7F8;
+ writel(temp, REG_MISC_REG10_tst_addr);
+
+ dmcdelay(4000u);
+
+ temp = readl(REG_MISC_REG10_tst_addr);
+ temp |= ((dsel << BITP_REG10_DSEL3) & BITM_REG10_DSEL3);
+ writel(temp, REG_MISC_REG10_tst_addr);
+
+ temp = readl(REG_MISC_REG10_tst_addr);
+ temp |= 0x4;
+ writel(temp, REG_MISC_REG10_tst_addr);
+
+ dmcdelay(100000u);
+
+ temp = readl(REG_MISC_REG10_tst_addr);
+ temp |= 0x1;
+ writel(temp, REG_MISC_REG10_tst_addr);
+
+ temp = readl(REG_MISC_REG10_tst_addr);
+ temp |= 0x800;
+ writel(temp, REG_MISC_REG10_tst_addr);
+#endif
+}
+
+static void Active_To_Fullon(const struct CGU_Settings *pCGU)
+{
+ u32 tmp;
+
+ while (1) {
+ tmp = readl(pCGU->rbase + CGU_STAT);
+ if ((tmp & BITM_CGU_STAT_PLLEN) &&
+ (tmp & BITM_CGU_STAT_PLLBP))
+ break;
+ }
+
+ writel(BITM_CGU_PLLCTL_PLLBPCL, pCGU->rbase + CGU_PLLCTL);
+
+ while (1) {
+ tmp = readl(pCGU->rbase + CGU_STAT);
+ if ((tmp & BITM_CGU_STAT_PLLEN) &&
+ ~(tmp & BITM_CGU_STAT_PLLBP) &&
+ ~(tmp & BITM_CGU_STAT_CLKSALGN))
+ break;
+ }
+}
+
+static void CGU_Init(const struct CGU_Settings *pCGU)
+{
+ const uintptr_t b = pCGU->rbase;
+
+#if defined(CONFIG_SC59X) || defined(CONFIG_SC59X_64)
+ if (readl(b + CGU_STAT) & BITM_CGU_STAT_PLLEN)
+ writel(BITM_CGU_PLLCTL_PLLEN, b + CGU_PLLCTL);
+
+ dmcdelay(1000);
+#endif
+
+ /* Check if processor is in Active mode */
+ if (readl(b + CGU_STAT) & BITM_CGU_STAT_PLLBP)
+ Active_To_Fullon(pCGU);
+
+#if defined(CONFIG_SC59X) || defined(CONFIG_SC59X_64)
+ dmcdelay(1000);
+#endif
+
+ program_cgu(pCGU);
+}
+
+void cgu_init(void)
+{
+ const struct CGU_Settings dividers0 = {
+ .rbase = CGU0_REGBASE,
+ .ctl_MSEL = CONFIG_CGU0_VCO_MULT,
+ .ctl_DF = CONFIG_CGU0_DF_DIV,
+ .div_CSEL = CONFIG_CGU0_CCLK_DIV,
+ .div_SYSSEL = CONFIG_CGU0_SCLK_DIV,
+ .div_S0SEL = VAL_CGU0_SCLK0_DIV,
+ .div_S1SEL = VAL_CGU0_SCLK1_DIV,
+ .div_DSEL = CONFIG_CGU0_DCLK_DIV,
+ .div_OSEL = CONFIG_CGU0_OCLK_DIV,
+ .divex_S0SELEX = VAL_CGU0_DIV_S0SELEX,
+ .divex_S1SELEX = VAL_CGU0_DIV_S1SELEX,
+ .clkoutsel = VAL_CGU0_CLKOUTSEL,
+ };
+ const struct CGU_Settings dividers1 = {
+ .rbase = CGU1_REGBASE,
+ .ctl_MSEL = CONFIG_CGU1_VCO_MULT,
+ .ctl_DF = CONFIG_CGU1_DF_DIV,
+ .div_CSEL = CONFIG_CGU1_CCLK_DIV,
+ .div_SYSSEL = CONFIG_CGU1_SCLK_DIV,
+ .div_S0SEL = VAL_CGU1_SCLK0_DIV,
+ .div_S1SEL = VAL_CGU1_SCLK1_DIV,
+ .div_DSEL = CONFIG_CGU1_DCLK_DIV,
+ .div_OSEL = CONFIG_CGU1_OCLK_DIV,
+ .divex_S0SELEX = VAL_CGU1_DIV_S0SELEX,
+ .divex_S1SELEX = VAL_CGU1_DIV_S1SELEX,
+ .clkoutsel = VAL_CGU1_CLKOUTSEL,
+ };
+
+ CGU_Init(&dividers0);
+ CGU_Init(&dividers1);
+}
+
+#define CONFIGURE_CDU0(a, b, c) \
+ writel(a, b); \
+ while (readl(REG_CDU0_STAT) & (1 << (c)))
+
+void cdu_init(void)
+{
+ while (readl(REG_CDU0_STAT) & 0xffff)
+ ;
+ writel((CONFIG_CDU0_CGU1_CLKIN & 0x1), REG_CDU0_CLKINSEL);
+
+ CONFIGURE_CDU0(CONFIG_CDU0_CLKO0, REG_CDU0_CFG0, 0);
+ CONFIGURE_CDU0(CONFIG_CDU0_CLKO1, REG_CDU0_CFG1, 1);
+ CONFIGURE_CDU0(CONFIG_CDU0_CLKO2, REG_CDU0_CFG2, 2);
+ CONFIGURE_CDU0(CONFIG_CDU0_CLKO3, REG_CDU0_CFG3, 3);
+ CONFIGURE_CDU0(CONFIG_CDU0_CLKO4, REG_CDU0_CFG4, 4);
+ CONFIGURE_CDU0(CONFIG_CDU0_CLKO5, REG_CDU0_CFG5, 5);
+ CONFIGURE_CDU0(CONFIG_CDU0_CLKO6, REG_CDU0_CFG6, 6);
+ CONFIGURE_CDU0(CONFIG_CDU0_CLKO7, REG_CDU0_CFG7, 7);
+ CONFIGURE_CDU0(CONFIG_CDU0_CLKO8, REG_CDU0_CFG8, 8);
+ CONFIGURE_CDU0(CONFIG_CDU0_CLKO9, REG_CDU0_CFG9, 9);
+#ifdef CONFIG_CDU0_CLKO10
+ CONFIGURE_CDU0(CONFIG_CDU0_CLKO10, REG_CDU0_CFG10, 10);
+#endif
+#ifdef CONFIG_CDU0_CLKO12
+ CONFIGURE_CDU0(CONFIG_CDU0_CLKO12, REG_CDU0_CFG12, 12);
+#endif
+#ifdef CONFIG_CDU0_CLKO13
+ CONFIGURE_CDU0(CONFIG_CDU0_CLKO13, REG_CDU0_CFG13, 13);
+#endif
+#ifdef CONFIG_CDU0_CLKO14
+ CONFIGURE_CDU0(CONFIG_CDU0_CLKO14, REG_CDU0_CFG14, 14);
+#endif
+}
+
+void clks_init(void)
+{
+ adi_dmc_reset_lanes(true);
+
+ cdu_init();
+ cgu_init();
+
+ adi_config_third_pll();
+
+ adi_dmc_reset_lanes(false);
+}
diff --git a/arch/arm/mach-sc5xx/init/clkinit.h b/arch/arm/mach-sc5xx/init/clkinit.h
new file mode 100644
index 0000000..b05f432
--- /dev/null
+++ b/arch/arm/mach-sc5xx/init/clkinit.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * (C) Copyright 2022 - Analog Devices, Inc.
+ *
+ * Written and/or maintained by Timesys Corporation
+ *
+ * Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com>
+ * Contact: Greg Malysa <greg.malysa@timesys.com>
+ */
+
+#ifndef CLKINIT_H_
+#define CLKINIT_H_
+
+void clks_init(void);
+
+void dmcdelay(uint32_t delay);
+
+#endif
diff --git a/arch/arm/mach-sc5xx/init/dmcinit.c b/arch/arm/mach-sc5xx/init/dmcinit.c
new file mode 100644
index 0000000..e375b5c
--- /dev/null
+++ b/arch/arm/mach-sc5xx/init/dmcinit.c
@@ -0,0 +1,954 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * (C) Copyright 2022 - Analog Devices, Inc.
+ *
+ * Written and/or maintained by Timesys Corporation
+ *
+ * Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com>
+ * Contact: Greg Malysa <greg.malysa@timesys.com>
+ */
+
+#include <asm/io.h>
+#include <asm/arch-adi/sc5xx/sc5xx.h>
+#include <linux/types.h>
+#include "clkinit.h"
+#include "dmcinit.h"
+
+#define REG_DMC0_BASE 0x31070000
+#define REG_DMC1_BASE 0x31073000
+
+#define REG_DMC_CTL 0x0004 // Control Register
+#define REG_DMC_STAT 0x0008 // Status Register
+#define REG_DMC_CFG 0x0040 // Configuration Register
+#define REG_DMC_TR0 0x0044 // Timing 0 Register
+#define REG_DMC_TR1 0x0048 // Timing 1 Register
+#define REG_DMC_TR2 0x004C // Timing 2 Register
+#define REG_DMC_MR 0x0060 // Shadow MR Register (DDR3)
+#define REG_DMC_EMR1 0x0064 // Shadow EMR1 Register
+#define REG_DMC_EMR2 0x0068 // Shadow EMR2 Register
+#define REG_DMC_EMR3 0x006C
+#define REG_DMC_DLLCTL 0x0080 // DLL Control Register
+#define REG_DMC_DT_CALIB_ADDR 0x0090 // Data Calibration Address Register
+#define REG_DMC_CPHY_CTL 0x01C0 // Controller to PHY Interface Register
+
+/* SC57x && SC58x DMC REGs */
+#define REG_DMC_PHY_CTL0 0x1000 // PHY Control 0 Register
+#define REG_DMC_PHY_CTL1 0x1004 // PHY Control 1 Register
+#define REG_DMC_PHY_CTL2 0x1008 // PHY Control 2 Register
+#define REG_DMC_PHY_CTL3 0x100c // PHY Control 3 Register
+#define REG_DMC_PHY_CTL4 0x1010 // PHY Control 4 Register
+#define REG_DMC_CAL_PADCTL0 0x1034 // CALIBRATION PAD CTL 0 Register
+#define REG_DMC_CAL_PADCTL2 0x103C // CALIBRATION PAD CTL2 Register
+/* END */
+
+/* SC59x DMC REGs */
+#define REG_DMC_DDR_LANE0_CTL0 0x1000 // Data Lane 0 Control Register 0
+#define REG_DMC_DDR_LANE0_CTL1 0x1004 // Data Lane 0 Control Register 1
+#define REG_DMC_DDR_LANE1_CTL0 0x100C // Data Lane 1 Control Register 0
+#define REG_DMC_DDR_LANE1_CTL1 0x1010 // Data Lane 1 Control Register 1
+#define REG_DMC_DDR_ROOT_CTL 0x1018 // DDR ROOT Module Control Register
+#define REG_DMC_DDR_ZQ_CTL0 0x1034 // DDR Calibration Control Register 0
+#define REG_DMC_DDR_ZQ_CTL1 0x1038 // DDR Calibration Control Register 1
+#define REG_DMC_DDR_ZQ_CTL2 0x103C // DDR Calibration Control Register 2
+#define REG_DMC_DDR_CA_CTL 0x1068 // DDR CA Lane Control Register
+/* END */
+
+#define REG_DMC_DDR_SCRATCH_2 0x1074
+#define REG_DMC_DDR_SCRATCH_3 0x1078
+#define REG_DMC_DDR_SCRATCH_6 0x1084
+#define REG_DMC_DDR_SCRATCH_7 0x1088
+
+#define REG_DMC_DDR_SCRATCH_STAT0 0x107C
+#define REG_DMC_DDR_SCRATCH_STAT1 0x1080
+
+#define DMC0_DATA_CALIB_ADD 0x80000000
+#define DMC1_DATA_CALIB_ADD 0xC0000000
+
+#define BITM_DMC_CFG_EXTBANK 0x0000F000 /* External Banks */
+#define ENUM_DMC_CFG_EXTBANK1 0x00000000 /* EXTBANK: 1 External Bank */
+#define BITM_DMC_CFG_SDRSIZE 0x00000F00 /* SDRAM Size */
+#define ENUM_DMC_CFG_SDRSIZE64 0x00000000 /* SDRSIZE: 64M Bit SDRAM (LPDDR Only) */
+#define ENUM_DMC_CFG_SDRSIZE128 0x00000100 /* SDRSIZE: 128M Bit SDRAM (LPDDR Only) */
+#define ENUM_DMC_CFG_SDRSIZE256 0x00000200 /* SDRSIZE: 256M Bit SDRAM */
+#define ENUM_DMC_CFG_SDRSIZE512 0x00000300 /* SDRSIZE: 512M Bit SDRAM */
+#define ENUM_DMC_CFG_SDRSIZE1G 0x00000400 /* SDRSIZE: 1G Bit SDRAM */
+#define ENUM_DMC_CFG_SDRSIZE2G 0x00000500 /* SDRSIZE: 2G Bit SDRAM */
+#define ENUM_DMC_CFG_SDRSIZE4G 0x00000600 /* SDRSIZE: 4G Bit SDRAM */
+#define ENUM_DMC_CFG_SDRSIZE8G 0x00000700 /* SDRSIZE: 8G Bit SDRAM */
+#define BITM_DMC_CFG_SDRWID 0x000000F0 /* SDRAM Width */
+#define ENUM_DMC_CFG_SDRWID16 0x00000020 /* SDRWID: 16-Bit Wide SDRAM */
+#define BITM_DMC_CFG_IFWID 0x0000000F /* Interface Width */
+#define ENUM_DMC_CFG_IFWID16 0x00000002 /* IFWID: 16-Bit Wide Interface */
+
+#define BITM_DMC_CTL_DDR3EN 0x00000001
+#define BITM_DMC_CTL_INIT 0x00000004
+#define BITP_DMC_STAT_INITDONE 2 /* Initialization Done */
+#define BITM_DMC_STAT_INITDONE 0x00000004
+
+#define BITP_DMC_CTL_AL_EN 27
+#define BITP_DMC_CTL_ZQCL 25 /* ZQ Calibration Long */
+#define BITP_DMC_CTL_ZQCS 24 /* ZQ Calibration Short */
+#define BITP_DMC_CTL_DLLCAL 13 /* DLL Calibration Start */
+#define BITP_DMC_CTL_PPREF 12 /* Postpone Refresh */
+#define BITP_DMC_CTL_RDTOWR 9 /* Read-to-Write Cycle */
+#define BITP_DMC_CTL_ADDRMODE 8 /* Addressing (Page/Bank) Mode */
+#define BITP_DMC_CTL_RESET 7 /* Reset SDRAM */
+#define BITP_DMC_CTL_PREC 6 /* Precharge */
+#define BITP_DMC_CTL_DPDREQ 5 /* Deep Power Down Request */
+#define BITP_DMC_CTL_PDREQ 4 /* Power Down Request */
+#define BITP_DMC_CTL_SRREQ 3 /* Self Refresh Request */
+#define BITP_DMC_CTL_INIT 2 /* Initialize DRAM Start */
+#define BITP_DMC_CTL_LPDDR 1 /* Low Power DDR Mode */
+#define BITP_DMC_CTL_DDR3EN 0 /* DDR3 Mode */
+
+#ifdef CONFIG_TARGET_SC584_EZKIT
+ #define DMC_PADCTL2_VALUE 0x0078283C
+#elif CONFIG_TARGET_SC573_EZKIT
+ #define DMC_PADCTL2_VALUE 0x00782828
+#elif CONFIG_TARGET_SC589_MINI || CONFIG_TARGET_SC589_EZKIT
+ #define DMC_PADCTL2_VALUE 0x00783C3C
+#elif defined(CONFIG_SC57X) || defined(CONFIG_SC58X)
+ #error "PADCTL2 not specified for custom board!"
+#else
+ //Newer DMC. Legacy calibration obsolete
+ #define DMC_PADCTL2_VALUE 0x0
+#endif
+
+#define DMC_CPHYCTL_VALUE 0x0000001A
+
+#define BITP_DMC_MR1_QOFF 12 /* Output Buffer Enable */
+#define BITP_DMC_MR1_TDQS 11 /* Termination Data Strobe */
+#define BITP_DMC_MR1_RTT2 9 /* Rtt_nom */
+#define BITP_DMC_MR1_WL 7 /* Write Leveling Enable. */
+#define BITP_DMC_MR1_RTT1 6 /* Rtt_nom */
+#define BITP_DMC_MR1_DIC1 5 /* Output Driver Impedance Control */
+#define BITP_DMC_MR1_AL 3 /* Additive Latency */
+#define BITP_DMC_MR1_RTT0 2 /* Rtt_nom */
+#define BITP_DMC_MR1_DIC0 1 /* Output Driver Impedance control */
+#define BITP_DMC_MR1_DLLEN 0 /* DLL Enable */
+
+#define BITP_DMC_MR2_CWL 3 /* CAS write Latency */
+
+#define BITP_DMC_TR0_TMRD 28 /* Timing Mode Register Delay */
+#define BITP_DMC_TR0_TRC 20 /* Timing Row Cycle */
+#define BITP_DMC_TR0_TRAS 12 /* Timing Row Active Time */
+#define BITP_DMC_TR0_TRP 8 /* Timing RAS Precharge. */
+#define BITP_DMC_TR0_TWTR 4 /* Timing Write to Read */
+#define BITP_DMC_TR0_TRCD 0 /* Timing RAS to CAS Delay */
+
+#define BITP_DMC_TR1_TRRD 28 /* Timing Read-Read Delay */
+#define BITP_DMC_TR1_TRFC 16 /* Timing Refresh-to-Command */
+#define BITP_DMC_TR1_TREF 0 /* Timing Refresh Interval */
+
+#define BITP_DMC_TR2_TCKE 20 /* Timing Clock Enable */
+#define BITP_DMC_TR2_TXP 16 /* Timing Exit Powerdown */
+#define BITP_DMC_TR2_TWR 12 /* Timing Write Recovery */
+#define BITP_DMC_TR2_TRTP 8 /* Timing Read-to-Precharge */
+#define BITP_DMC_TR2_TFAW 0 /* Timing Four-Activated-Window */
+
+#define BITP_DMC_MR_PD 12 /* Active Powerdown Mode */
+#define BITP_DMC_MR_WRRECOV 9 /* Write Recovery */
+#define BITP_DMC_MR_DLLRST 8 /* DLL Reset */
+#define BITP_DMC_MR_CL 4 /* CAS Latency */
+#define BITP_DMC_MR_CL0 2 /* CAS Latency */
+#define BITP_DMC_MR_BLEN 0 /* Burst Length */
+
+#define BITP_DMC_DLLCTL_DATACYC 8 /* Data Cycles */
+#define BITP_DMC_DLLCTL_DLLCALRDCNT 0 /* DLL Calibration RD Count */
+
+#define BITM_DMC_DLLCTL_DATACYC 0x00000F00 /* Data Cycles */
+#define BITM_DMC_DLLCTL_DLLCALRDCNT 0x000000FF /* DLL Calib RD Count */
+
+#define BITP_DMC_STAT_PHYRDPHASE 20 /* PHY Read Phase */
+
+#define BITM_DMC_DDR_LANE0_CTL0_CB_RSTDAT 0x08000000 /* Rst Data Pads */
+#define BITM_DMC_DDR_LANE1_CTL0_CB_RSTDAT 0x08000000 /* Rst Data Pads */
+#define BITM_DMC_DDR_LANE0_CTL1_COMP_DCYCLE 0x00000002 /* Compute Dcycle */
+#define BITM_DMC_DDR_LANE1_CTL1_COMP_DCYCLE 0x00000002 /* Compute Dcycle */
+#define BITM_DMC_DDR_LANE1_CTL0_CB_RSTDLL 0x00000100 /* Rst Lane DLL */
+#define BITM_DMC_DDR_LANE0_CTL0_CB_RSTDLL 0x00000100 /* Rst Lane DLL */
+#define BITP_DMC_DDR_ROOT_CTL_PIPE_OFSTDCYCLE 10 /* Pipeline offset for PHYC_DATACYCLE */
+#define BITM_DMC_DDR_ROOT_CTL_SW_REFRESH 0x00002000 /* Refresh Lane DLL Code */
+#define BITM_DMC_DDR_CA_CTL_SW_REFRESH 0x00004000 /* Refresh Lane DLL Code */
+
+#define BITP_DMC_CTL_RL_DQS 26 /* RL_DQS */
+#define BITM_DMC_CTL_RL_DQS 0x04000000 /* RL_DQS */
+#define BITP_DMC_EMR3_MPR 2 /* Multi Purpose Read Enable (Read Leveling)*/
+#define BITM_DMC_EMR3_MPR 0x00000004 /* Multi Purpose Read Enable (Read Leveling)*/
+#define BITM_DMC_MR1_WL 0x00000080 /* Write Leveling Enable.*/
+#define BITM_DMC_STAT_PHYRDPHASE 0x00F00000 /* PHY Read Phase */
+
+#define BITP_DMC_DDR_LANE0_CTL1_BYPCODE 10
+#define BITM_DMC_DDR_LANE0_CTL1_BYPCODE 0x00007C00
+#define BITP_DMC_DDR_LANE0_CTL1_BYPDELCHAINEN 15
+#define BITM_DMC_DDR_LANE0_CTL1_BYPDELCHAINEN 0x00008000
+
+#define DMC_ZQCTL0_VALUE 0x00785A64
+#define DMC_ZQCTL1_VALUE 0
+#define DMC_ZQCTL2_VALUE 0x70000000
+
+#define DMC_TRIG_CALIB 0
+#define DMC_OFSTDCYCLE 2
+
+#define BITP_DMC_CAL_PADCTL0_RTTCALEN 31 /* RTT Calibration Enable */
+#define BITP_DMC_CAL_PADCTL0_PDCALEN 30 /* PULLDOWN Calib Enable */
+#define BITP_DMC_CAL_PADCTL0_PUCALEN 29 /* PULLUP Calib Enable */
+#define BITP_DMC_CAL_PADCTL0_CALSTRT 28 /* Start New Calib ( Hardware Cleared) */
+#define BITM_DMC_CAL_PADCTL0_RTTCALEN 0x80000000 /* RTT Calibration Enable */
+#define BITM_DMC_CAL_PADCTL0_PDCALEN 0x40000000 /* PULLDOWN Calib Enable */
+#define BITM_DMC_CAL_PADCTL0_PUCALEN 0x20000000 /* PULLUP Calib Enable */
+#define BITM_DMC_CAL_PADCTL0_CALSTRT 0x10000000 /* Start New Calib ( Hardware Cleared) */
+#define ENUM_DMC_PHY_CTL4_DDR3 0x00000000 /* DDRMODE: DDR3 Mode */
+#define ENUM_DMC_PHY_CTL4_DDR2 0x00000001 /* DDRMODE: DDR2 Mode */
+#define ENUM_DMC_PHY_CTL4_LPDDR 0x00000003 /* DDRMODE: LPDDR Mode */
+
+#define BITP_DMC_DDR_ZQ_CTL0_IMPRTT 16 /* Data/DQS ODT */
+#define BITP_DMC_DDR_ZQ_CTL0_IMPWRDQ 8 /* Data/DQS/DM/CLK Drive Strength */
+#define BITP_DMC_DDR_ZQ_CTL0_IMPWRADD 0 /* Address/Command Drive Strength */
+#define BITM_DMC_DDR_ZQ_CTL0_IMPRTT 0x00FF0000 /* Data/DQS ODT */
+#define BITM_DMC_DDR_ZQ_CTL0_IMPWRDQ 0x0000FF00 /* Data/DQS/DM/CLK Drive Strength */
+#define BITM_DMC_DDR_ZQ_CTL0_IMPWRADD 0x000000FF /* Address/Command Drive Strength */
+
+#define BITM_DMC_DDR_ROOT_CTL_TRIG_RD_XFER_ALL 0x00200000 /* All Lane Read Status */
+
+#if defined(CONFIG_ADI_USE_DDR2)
+ #define DMC_MR0_VALUE \
+ ((DMC_BL / 4 + 1) << BITP_DMC_MR_BLEN) | \
+ (DMC_CL << BITP_DMC_MR_CL) | \
+ (DMC_WRRECOV << BITP_DMC_MR_WRRECOV)
+
+ #define DMC_MR1_VALUE \
+ (DMC_MR1_AL << BITP_DMC_MR1_AL | 0x04) \
+
+ #define DMC_MR2_VALUE 0
+ #define DMC_MR3_VALUE 0
+
+ #define DMC_CTL_VALUE \
+ (DMC_RDTOWR << BITP_DMC_CTL_RDTOWR) | \
+ (1 << BITP_DMC_CTL_DLLCAL) | \
+ (BITM_DMC_CTL_INIT)
+#else
+ #define DMC_MR0_VALUE \
+ (0 << BITP_DMC_MR_BLEN) | \
+ (DMC_CL0 << BITP_DMC_MR_CL0) | \
+ (DMC_CL123 << BITP_DMC_MR_CL) | \
+ (DMC_WRRECOV << BITP_DMC_MR_WRRECOV) | \
+ (1 << BITP_DMC_MR_DLLRST)
+
+ #define DMC_MR1_VALUE \
+ (DMC_MR1_DLLEN << BITP_DMC_MR1_DLLEN) | \
+ (DMC_MR1_DIC0 << BITP_DMC_MR1_DIC0) | \
+ (DMC_MR1_RTT0 << BITP_DMC_MR1_RTT0) | \
+ (DMC_MR1_AL << BITP_DMC_MR1_AL) | \
+ (DMC_MR1_DIC1 << BITP_DMC_MR1_DIC1) | \
+ (DMC_MR1_RTT1 << BITP_DMC_MR1_RTT1) | \
+ (DMC_MR1_RTT2 << BITP_DMC_MR1_RTT2) | \
+ (DMC_MR1_WL << BITP_DMC_MR1_WL) | \
+ (DMC_MR1_TDQS << BITP_DMC_MR1_TDQS) | \
+ (DMC_MR1_QOFF << BITP_DMC_MR1_QOFF)
+
+ #define DMC_MR2_VALUE \
+ ((DMC_WL) << BITP_DMC_MR2_CWL)
+
+ #define DMC_MR3_VALUE \
+ ((DMC_WL) << BITP_DMC_MR2_CWL)
+
+ #define DMC_CTL_VALUE \
+ (DMC_RDTOWR << BITP_DMC_CTL_RDTOWR) | \
+ (BITM_DMC_CTL_INIT) | \
+ (BITM_DMC_CTL_DDR3EN) | \
+ (DMC_CTL_AL_EN << BITP_DMC_CTL_AL_EN)
+#endif
+
+#define DMC_DLLCTL_VALUE \
+ (DMC_DATACYC << BITP_DMC_DLLCTL_DATACYC) | \
+ (DMC_DLLCALRDCNT << BITP_DMC_DLLCTL_DLLCALRDCNT)
+
+#define DMC_CFG_VALUE \
+ ENUM_DMC_CFG_IFWID16 | \
+ ENUM_DMC_CFG_SDRWID16 | \
+ SDR_CHIP_SIZE | \
+ ENUM_DMC_CFG_EXTBANK1
+
+#define DMC_TR0_VALUE \
+ (DMC_TRCD << BITP_DMC_TR0_TRCD) | \
+ (DMC_TWTR << BITP_DMC_TR0_TWTR) | \
+ (DMC_TRP << BITP_DMC_TR0_TRP) | \
+ (DMC_TRAS << BITP_DMC_TR0_TRAS) | \
+ (DMC_TRC << BITP_DMC_TR0_TRC) | \
+ (DMC_TMRD << BITP_DMC_TR0_TMRD)
+
+#define DMC_TR1_VALUE \
+ (DMC_TREF << BITP_DMC_TR1_TREF) | \
+ (DMC_TRFC << BITP_DMC_TR1_TRFC) | \
+ (DMC_TRRD << BITP_DMC_TR1_TRRD)
+
+#define DMC_TR2_VALUE \
+ (DMC_TFAW << BITP_DMC_TR2_TFAW) | \
+ (DMC_TRTP << BITP_DMC_TR2_TRTP) | \
+ (DMC_TWR << BITP_DMC_TR2_TWR) | \
+ (DMC_TXP << BITP_DMC_TR2_TXP) | \
+ (DMC_TCKE << BITP_DMC_TR2_TCKE)
+
+enum DDR_MODE {
+ DDR3_MODE,
+ DDR2_MODE,
+ LPDDR_MODE,
+};
+
+enum CALIBRATION_MODE {
+ CALIBRATION_LEGACY,
+ CALIBRATION_METHOD1,
+ CALIBRATION_METHOD2,
+};
+
+static struct dmc_param {
+ phys_addr_t reg;
+ u32 ddr_mode;
+ u32 padctl2_value;
+ u32 dmc_cphyctl_value;
+ u32 dmc_cfg_value;
+ u32 dmc_dllctl_value;
+ u32 dmc_ctl_value;
+ u32 dmc_tr0_value;
+ u32 dmc_tr1_value;
+ u32 dmc_tr2_value;
+ u32 dmc_mr0_value;
+ u32 dmc_mr1_value;
+ u32 dmc_mr2_value;
+ u32 dmc_mr3_value;
+ u32 dmc_zqctl0_value;
+ u32 dmc_zqctl1_value;
+ u32 dmc_zqctl2_value;
+ u32 dmc_data_calib_add_value;
+ bool phy_init_required;
+ bool anomaly_20000037_applicable;
+ enum CALIBRATION_MODE calib_mode;
+} dmc;
+
+#ifdef CONFIG_SC59X_64
+#define DQS_DEFAULT_DELAY 3ul
+
+#define DELAYTRIM 1
+#define LANE0_DQS_DELAY 1
+#define LANE1_DQS_DELAY 1
+
+#define CLKDIR 0ul
+
+#define DQSTRIM 0
+#define DQSCODE 0ul
+
+#define CLKTRIM 0
+#define CLKCODE 0ul
+#endif
+
+static inline void calibration_legacy(void)
+{
+ u32 temp;
+
+ /* 1. Set DDR mode to DDR3/DDR2/LPDDR in DMCx_PHY_CTL4 register */
+ if (dmc.ddr_mode == DDR3_MODE)
+ writel(ENUM_DMC_PHY_CTL4_DDR3, dmc.reg + REG_DMC_PHY_CTL4);
+ else if (dmc.ddr_mode == DDR2_MODE)
+ writel(ENUM_DMC_PHY_CTL4_DDR2, dmc.reg + REG_DMC_PHY_CTL4);
+ else if (dmc.ddr_mode == LPDDR_MODE)
+ writel(ENUM_DMC_PHY_CTL4_LPDDR, dmc.reg + REG_DMC_PHY_CTL4);
+
+ /*
+ * 2. Make sure that the bits 6, 7, 25, and 27 of the DMC_PHY_
+ * CTL3 register are set
+ */
+ writel(0x0A0000C0, dmc.reg + REG_DMC_PHY_CTL3);
+
+ /*
+ * 3. For DDR2/DDR3 mode, make sure that the bits 0, 1, 2, 3 of
+ * the DMC_PHY_CTL0 register and the bits 26, 27, 28, 29, 30, 31
+ * of the DMC_PHY_CTL2 are set.
+ */
+ if (dmc.ddr_mode == DDR3_MODE ||
+ dmc.ddr_mode == DDR2_MODE) {
+ writel(0XFC000000, dmc.reg + REG_DMC_PHY_CTL2);
+ writel(0x0000000f, dmc.reg + REG_DMC_PHY_CTL0);
+ }
+
+ writel(0x00000000, dmc.reg + REG_DMC_PHY_CTL1);
+
+ /* 4. For DDR3 mode, set bit 1 and configure bits [5:2] of the
+ * DMC_CPHY_CTL register with WL=CWL+AL in DCLK cycles.
+ */
+ if (dmc.ddr_mode == DDR3_MODE)
+ writel(dmc.dmc_cphyctl_value, dmc.reg + REG_DMC_CPHY_CTL);
+ /* 5. Perform On Die Termination(ODT) & Driver Impedance Calibration */
+ if (dmc.ddr_mode == LPDDR_MODE) {
+ /* Bypass processor ODT */
+ writel(0x80000, dmc.reg + REG_DMC_PHY_CTL1);
+ } else {
+ /* Set bits RTTCALEN, PDCALEN, PUCALEN of register */
+ temp = BITM_DMC_CAL_PADCTL0_RTTCALEN |
+ BITM_DMC_CAL_PADCTL0_PDCALEN |
+ BITM_DMC_CAL_PADCTL0_PUCALEN;
+ writel(temp, dmc.reg + REG_DMC_CAL_PADCTL0);
+ /* Configure ODT and drive impedance values in the
+ * DMCx_CAL_PADCTL2 register
+ */
+ writel(dmc.padctl2_value, dmc.reg + REG_DMC_CAL_PADCTL2);
+ /* start calibration */
+ temp |= BITM_DMC_CAL_PADCTL0_CALSTRT;
+ writel(temp, dmc.reg + REG_DMC_CAL_PADCTL0);
+ /* Wait for PAD calibration to complete - 300 DCLK cycle.
+ * Worst case: CCLK=450 MHz, DCLK=125 MHz
+ */
+ dmcdelay(300);
+ }
+}
+
+static inline void calibration_method1(void)
+{
+#if defined(CONFIG_SC59X) || defined(CONFIG_SC59X_64)
+ writel(dmc.dmc_zqctl0_value, dmc.reg + REG_DMC_DDR_ZQ_CTL0);
+ writel(dmc.dmc_zqctl1_value, dmc.reg + REG_DMC_DDR_ZQ_CTL1);
+ writel(dmc.dmc_zqctl2_value, dmc.reg + REG_DMC_DDR_ZQ_CTL2);
+
+ /* Generate the trigger */
+ writel(0x0ul, dmc.reg + REG_DMC_DDR_CA_CTL);
+ writel(0x0ul, dmc.reg + REG_DMC_DDR_ROOT_CTL);
+ writel(0x00010000ul, dmc.reg + REG_DMC_DDR_ROOT_CTL);
+ dmcdelay(8000u);
+
+ /* The [31:26] bits may change if pad ring changes */
+ writel(0x0C000001ul | DMC_TRIG_CALIB, dmc.reg + REG_DMC_DDR_CA_CTL);
+ dmcdelay(8000u);
+ writel(0x0ul, dmc.reg + REG_DMC_DDR_CA_CTL);
+ writel(0x0ul, dmc.reg + REG_DMC_DDR_ROOT_CTL);
+#endif
+}
+
+static inline void calibration_method2(void)
+{
+#if defined(CONFIG_SC59X) || defined(CONFIG_SC59X_64)
+ u32 stat_value = 0x0u;
+ u32 drv_pu, drv_pd, odt_pu, odt_pd;
+ u32 ro_dt, clk_dqs_drv_impedance;
+ u32 temp;
+
+ /* Reset trigger */
+ writel(0x0ul, dmc.reg + REG_DMC_DDR_CA_CTL);
+ writel(0x0ul, dmc.reg + REG_DMC_DDR_ROOT_CTL);
+ writel(0x0ul, dmc.reg + REG_DMC_DDR_SCRATCH_3);
+ writel(0x0ul, dmc.reg + REG_DMC_DDR_SCRATCH_2);
+
+ /* Writing internal registers in calib pad to zero. Calib mode set
+ * to 1 [26], trig M1 S1 write [16], this enables usage of scratch
+ * registers instead of ZQCTL registers
+ */
+ writel(0x04010000ul, dmc.reg + REG_DMC_DDR_ROOT_CTL);
+ dmcdelay(2500u);
+
+ /* TRIGGER FOR M2-S2 WRITE -> slave id 31:26 trig m2,s2 write
+ * bit 1->1 slave1 address is 4
+ */
+ writel(0x10000002ul, dmc.reg + REG_DMC_DDR_CA_CTL);
+ dmcdelay(2500u);
+
+ /* reset Trigger */
+ writel(0x0u, dmc.reg + REG_DMC_DDR_CA_CTL);
+ writel(0x0u, dmc.reg + REG_DMC_DDR_ROOT_CTL);
+
+ /* write to slave 1, make the power down bit high */
+ writel(0x1ul << 12, dmc.reg + REG_DMC_DDR_SCRATCH_3);
+ writel(0x0ul, dmc.reg + REG_DMC_DDR_SCRATCH_2);
+ dmcdelay(2500u);
+
+ /* Calib mode set to 1 [26], trig M1 S1 write [16] */
+ writel(0x04010000ul, dmc.reg + REG_DMC_DDR_ROOT_CTL);
+ dmcdelay(2500u);
+
+ writel(0x10000002ul, dmc.reg + REG_DMC_DDR_CA_CTL);
+ dmcdelay(2500u);
+
+ writel(0x0ul, dmc.reg + REG_DMC_DDR_CA_CTL);
+ writel(0x0ul, dmc.reg + REG_DMC_DDR_ROOT_CTL);
+ writel(0x0, dmc.reg + REG_DMC_DDR_SCRATCH_3);
+
+ /* for slave 0 */
+ writel(dmc.dmc_zqctl0_value, dmc.reg + REG_DMC_DDR_SCRATCH_2);
+
+ /* Calib mode set to 1 [26], trig M1 S1 write [16] */
+ writel(0x04010000ul, dmc.reg + REG_DMC_DDR_ROOT_CTL);
+ dmcdelay(2500u);
+
+ writel(0x0C000002ul, dmc.reg + REG_DMC_DDR_CA_CTL);
+ dmcdelay(2500u);
+
+ writel(0x0ul, dmc.reg + REG_DMC_DDR_CA_CTL);
+ writel(0x0ul, dmc.reg + REG_DMC_DDR_ROOT_CTL);
+
+ /* writing to slave 1
+ * calstrt is 0, but other programming is done
+ *
+ * make power down LOW again, to kickstart BIAS circuit
+ */
+ writel(0x0ul, dmc.reg + REG_DMC_DDR_SCRATCH_3);
+ writel(0x30000000ul, dmc.reg + REG_DMC_DDR_SCRATCH_2);
+
+ /* write to ca_ctl lane, calib mode set to 1 [26],
+ * trig M1 S1 write [16]
+ */
+ writel(0x04010000ul, dmc.reg + REG_DMC_DDR_ROOT_CTL);
+ dmcdelay(2500u);
+
+ /* copies data to lane controller slave
+ * TRIGGER FOR M2-S2 WRITE -> slave id 31:26
+ * trig m2,s2 write bit 1->1
+ * slave1 address is 4
+ */
+ writel(0x10000002ul, dmc.reg + REG_DMC_DDR_CA_CTL);
+ dmcdelay(2500u);
+
+ /* reset Trigger */
+ writel(0x0ul, dmc.reg + REG_DMC_DDR_CA_CTL);
+ writel(0x0ul, dmc.reg + REG_DMC_DDR_ROOT_CTL);
+ writel(0x0ul, dmc.reg + REG_DMC_DDR_SCRATCH_3);
+ writel(0x0ul, dmc.reg + REG_DMC_DDR_SCRATCH_2);
+ writel(0x0ul, dmc.reg + REG_DMC_DDR_SCRATCH_3);
+ writel(0x0ul, dmc.reg + REG_DMC_DDR_SCRATCH_2);
+ writel(0x04010000ul, dmc.reg + REG_DMC_DDR_ROOT_CTL);
+ dmcdelay(2500u);
+ writel(0x10000002ul, dmc.reg + REG_DMC_DDR_CA_CTL);
+ dmcdelay(2500u);
+ writel(0x0ul, dmc.reg + REG_DMC_DDR_CA_CTL);
+ writel(0x0ul, dmc.reg + REG_DMC_DDR_ROOT_CTL);
+ writel(0x0ul, dmc.reg + REG_DMC_DDR_SCRATCH_3);
+ writel(0x0ul, dmc.reg + REG_DMC_DDR_SCRATCH_2);
+ writel(0x0ul, dmc.reg + REG_DMC_DDR_SCRATCH_3);
+ writel(0x50000000ul, dmc.reg + REG_DMC_DDR_SCRATCH_2);
+ writel(0x04010000ul, dmc.reg + REG_DMC_DDR_ROOT_CTL);
+ dmcdelay(2500u);
+ writel(0x10000002ul, dmc.reg + REG_DMC_DDR_CA_CTL);
+ dmcdelay(2500u);
+ writel(0u, dmc.reg + REG_DMC_DDR_CA_CTL);
+ writel(0u, dmc.reg + REG_DMC_DDR_ROOT_CTL);
+ writel(0x0C000004u, dmc.reg + REG_DMC_DDR_CA_CTL);
+ dmcdelay(2500u);
+ writel(BITM_DMC_DDR_ROOT_CTL_TRIG_RD_XFER_ALL,
+ dmc.reg + REG_DMC_DDR_ROOT_CTL);
+ dmcdelay(2500u);
+ writel(0u, dmc.reg + REG_DMC_DDR_CA_CTL);
+ writel(0u, dmc.reg + REG_DMC_DDR_ROOT_CTL);
+ // calculate ODT PU and PD values
+ stat_value = ((readl(dmc.reg + REG_DMC_DDR_SCRATCH_7) & 0x0000FFFFu) <<
+ 16);
+ stat_value |= ((readl(dmc.reg + REG_DMC_DDR_SCRATCH_6) & 0xFFFF0000u) >>
+ 16);
+ clk_dqs_drv_impedance = ((dmc.dmc_zqctl0_value) &
+ BITM_DMC_DDR_ZQ_CTL0_IMPWRDQ) >> BITP_DMC_DDR_ZQ_CTL0_IMPWRDQ;
+ ro_dt = ((dmc.dmc_zqctl0_value) & BITM_DMC_DDR_ZQ_CTL0_IMPRTT) >>
+ BITP_DMC_DDR_ZQ_CTL0_IMPRTT;
+ drv_pu = stat_value & 0x0000003Fu;
+ drv_pd = (stat_value >> 12) & 0x0000003Fu;
+ odt_pu = (drv_pu * clk_dqs_drv_impedance) / ro_dt;
+ odt_pd = (drv_pd * clk_dqs_drv_impedance) / ro_dt;
+ temp = ((1uL << 24) |
+ ((drv_pd & 0x0000003Fu)) |
+ ((odt_pd & 0x0000003Fu) << 6) |
+ ((drv_pu & 0x0000003Fu) << 12) |
+ ((odt_pu & 0x0000003Fu) << 18));
+ temp |= readl(dmc.reg + REG_DMC_DDR_SCRATCH_2);
+ writel(temp, dmc.reg + REG_DMC_DDR_SCRATCH_2);
+ writel(0x0C010000u, dmc.reg + REG_DMC_DDR_ROOT_CTL);
+ dmcdelay(2500u);
+ writel(0x08000002u, dmc.reg + REG_DMC_DDR_CA_CTL);
+ dmcdelay(2500u);
+ writel(0u, dmc.reg + REG_DMC_DDR_CA_CTL);
+ writel(0u, dmc.reg + REG_DMC_DDR_ROOT_CTL);
+ writel(0x04010000u, dmc.reg + REG_DMC_DDR_ROOT_CTL);
+ dmcdelay(2500u);
+ writel(0x80000002u, dmc.reg + REG_DMC_DDR_CA_CTL);
+ dmcdelay(2500u);
+ writel(0u, dmc.reg + REG_DMC_DDR_CA_CTL);
+ writel(0u, dmc.reg + REG_DMC_DDR_ROOT_CTL);
+#endif
+}
+
+static inline void adi_dmc_lane_reset(bool reset, uint32_t dmc_no)
+{
+#if defined(CONFIG_SC59X) || defined(CONFIG_SC59X_64)
+ u32 temp;
+ phys_addr_t base = (dmc_no == 0) ? REG_DMC0_BASE : REG_DMC1_BASE;
+ phys_addr_t ln0 = base + REG_DMC_DDR_LANE0_CTL0;
+ phys_addr_t ln1 = base + REG_DMC_DDR_LANE1_CTL0;
+
+ if (reset) {
+ temp = readl(ln0);
+ temp |= BITM_DMC_DDR_LANE0_CTL0_CB_RSTDLL;
+ writel(temp, ln0);
+
+ temp = readl(ln1);
+ temp |= BITM_DMC_DDR_LANE1_CTL0_CB_RSTDLL;
+ writel(temp, ln1);
+ } else {
+ temp = readl(ln0);
+ temp &= ~BITM_DMC_DDR_LANE0_CTL0_CB_RSTDLL;
+ writel(temp, ln0);
+
+ temp = readl(ln1);
+ temp &= ~BITM_DMC_DDR_LANE1_CTL0_CB_RSTDLL;
+ writel(temp, ln1);
+ }
+ dmcdelay(9000u);
+#endif
+}
+
+void adi_dmc_reset_lanes(bool reset)
+{
+ if (!IS_ENABLED(CONFIG_ADI_USE_DDR2)) {
+ if (IS_ENABLED(CONFIG_SC59X) || IS_ENABLED(CONFIG_SC59X_64)) {
+ if (IS_ENABLED(CONFIG_ADI_USE_DMC0))
+ adi_dmc_lane_reset(reset, 0);
+ if (IS_ENABLED(CONFIG_ADI_USE_DMC1))
+ adi_dmc_lane_reset(reset, 1);
+ }
+ else {
+ u32 temp = reset ? 0x800 : 0x0;
+
+ if (IS_ENABLED(CONFIG_ADI_USE_DMC0))
+ writel(temp, REG_DMC0_BASE + REG_DMC_PHY_CTL0);
+ if (IS_ENABLED(CONFIG_ADI_USE_DMC1))
+ writel(temp, REG_DMC1_BASE + REG_DMC_PHY_CTL0);
+ }
+ }
+}
+
+static inline void dmc_controller_init(void)
+{
+#if defined(CONFIG_SC59X) || defined(CONFIG_SC59X_64)
+ u32 phyphase, rd_cnt, t_EMR1, t_EMR3, t_CTL, data_cyc, temp;
+#endif
+
+ /* 1. Program the DMC controller registers: DMCx_CFG, DMCx_TR0,
+ * DMCx_TR1, DMCx_TR2, DMCx_MR(DDR2/LPDDR)/DMCx_MR0(DDR3),
+ * DMCx_EMR1(DDR2)/DMCx_MR1(DDR3),
+ * DMCx_EMR2(DDR2)/DMCx_EMR(LPDDR)/DMCx_MR2(DDR3)
+ */
+ writel(dmc.dmc_cfg_value, dmc.reg + REG_DMC_CFG);
+ writel(dmc.dmc_tr0_value, dmc.reg + REG_DMC_TR0);
+ writel(dmc.dmc_tr1_value, dmc.reg + REG_DMC_TR1);
+ writel(dmc.dmc_tr2_value, dmc.reg + REG_DMC_TR2);
+ writel(dmc.dmc_mr0_value, dmc.reg + REG_DMC_MR);
+ writel(dmc.dmc_mr1_value, dmc.reg + REG_DMC_EMR1);
+ writel(dmc.dmc_mr2_value, dmc.reg + REG_DMC_EMR2);
+
+#if defined(CONFIG_SC59X) || defined(CONFIG_SC59X_64)
+ writel(dmc.dmc_mr3_value, dmc.reg + REG_DMC_EMR3);
+ writel(dmc.dmc_dllctl_value, dmc.reg + REG_DMC_DLLCTL);
+ dmcdelay(2000u);
+
+ temp = readl(dmc.reg + REG_DMC_DDR_CA_CTL);
+ temp |= BITM_DMC_DDR_CA_CTL_SW_REFRESH;
+ writel(temp, dmc.reg + REG_DMC_DDR_CA_CTL);
+ dmcdelay(5u);
+
+ temp = readl(dmc.reg + REG_DMC_DDR_ROOT_CTL);
+ temp |= BITM_DMC_DDR_ROOT_CTL_SW_REFRESH |
+ (DMC_OFSTDCYCLE << BITP_DMC_DDR_ROOT_CTL_PIPE_OFSTDCYCLE);
+ writel(temp, dmc.reg + REG_DMC_DDR_ROOT_CTL);
+#endif
+
+ /* 2. Make sure that the REG_DMC_DT_CALIB_ADDR register is programmed
+ * to an unused DMC location corresponding to a burst of 16 bytes
+ * (by default it is the starting address of the DMC address range).
+ */
+#ifndef CONFIG_SC59X
+ writel(dmc.dmc_data_calib_add_value, dmc.reg + REG_DMC_DT_CALIB_ADDR);
+#endif
+ /* 3. Program the DMCx_CTL register with INIT bit set to start
+ * the DMC initialization sequence
+ */
+ writel(dmc.dmc_ctl_value, dmc.reg + REG_DMC_CTL);
+ /* 4. Wait for the DMC initialization to complete by polling
+ * DMCx_STAT.INITDONE bit.
+ */
+
+#if defined(CONFIG_SC59X) || defined(CONFIG_SC59X_64)
+ dmcdelay(722000u);
+
+ /* Add necessary delay depending on the configuration */
+ t_EMR1 = (dmc.dmc_mr1_value & BITM_DMC_MR1_WL) >> BITP_DMC_MR1_WL;
+
+ dmcdelay(600u);
+ if (t_EMR1 != 0u)
+ while ((readl(dmc.reg + REG_DMC_EMR1) & BITM_DMC_MR1_WL) != 0)
+ ;
+
+ t_EMR3 = (dmc.dmc_mr3_value & BITM_DMC_EMR3_MPR) >>
+ BITP_DMC_EMR3_MPR;
+ dmcdelay(2000u);
+ if (t_EMR3 != 0u)
+ while ((readl(dmc.reg + REG_DMC_EMR3) & BITM_DMC_EMR3_MPR) != 0)
+ ;
+
+ t_CTL = (dmc.dmc_ctl_value & BITM_DMC_CTL_RL_DQS) >> BITP_DMC_CTL_RL_DQS;
+ dmcdelay(600u);
+ if (t_CTL != 0u)
+ while ((readl(dmc.reg + REG_DMC_CTL) & BITM_DMC_CTL_RL_DQS) != 0)
+ ;
+#endif
+
+ /* check if DMC initialization finished*/
+ while ((readl(dmc.reg + REG_DMC_STAT) & BITM_DMC_STAT_INITDONE) == 0)
+ ;
+
+#if defined(CONFIG_SC59X) || defined(CONFIG_SC59X_64)
+ /* toggle DCYCLE */
+ temp = readl(dmc.reg + REG_DMC_DDR_LANE0_CTL1);
+ temp |= BITM_DMC_DDR_LANE0_CTL1_COMP_DCYCLE;
+ writel(temp, dmc.reg + REG_DMC_DDR_LANE0_CTL1);
+
+ temp = readl(dmc.reg + REG_DMC_DDR_LANE1_CTL1);
+ temp |= BITM_DMC_DDR_LANE1_CTL1_COMP_DCYCLE;
+ writel(temp, dmc.reg + REG_DMC_DDR_LANE1_CTL1);
+
+ dmcdelay(10u);
+
+ temp = readl(dmc.reg + REG_DMC_DDR_LANE0_CTL1);
+ temp &= (~BITM_DMC_DDR_LANE0_CTL1_COMP_DCYCLE);
+ writel(temp, dmc.reg + REG_DMC_DDR_LANE0_CTL1);
+
+ temp = readl(dmc.reg + REG_DMC_DDR_LANE1_CTL1);
+ temp &= (~BITM_DMC_DDR_LANE1_CTL1_COMP_DCYCLE);
+ writel(temp, dmc.reg + REG_DMC_DDR_LANE1_CTL1);
+
+ /* toggle RSTDAT */
+ temp = readl(dmc.reg + REG_DMC_DDR_LANE0_CTL0);
+ temp |= BITM_DMC_DDR_LANE0_CTL0_CB_RSTDAT;
+ writel(temp, dmc.reg + REG_DMC_DDR_LANE0_CTL0);
+
+ temp = readl(dmc.reg + REG_DMC_DDR_LANE0_CTL0);
+ temp &= (~BITM_DMC_DDR_LANE0_CTL0_CB_RSTDAT);
+ writel(temp, dmc.reg + REG_DMC_DDR_LANE0_CTL0);
+
+ temp = readl(dmc.reg + REG_DMC_DDR_LANE1_CTL0);
+ temp |= BITM_DMC_DDR_LANE1_CTL0_CB_RSTDAT;
+ writel(temp, dmc.reg + REG_DMC_DDR_LANE1_CTL0);
+
+ temp = readl(dmc.reg + REG_DMC_DDR_LANE1_CTL0);
+ temp &= (~BITM_DMC_DDR_LANE1_CTL0_CB_RSTDAT);
+ writel(temp, dmc.reg + REG_DMC_DDR_LANE1_CTL0);
+
+ dmcdelay(2500u);
+
+ /* Program phyphase*/
+ phyphase = (readl(dmc.reg + REG_DMC_STAT) &
+ BITM_DMC_STAT_PHYRDPHASE) >> BITP_DMC_STAT_PHYRDPHASE;
+ data_cyc = (phyphase << BITP_DMC_DLLCTL_DATACYC) &
+ BITM_DMC_DLLCTL_DATACYC;
+ rd_cnt = dmc.dmc_dllctl_value;
+ rd_cnt <<= BITP_DMC_DLLCTL_DLLCALRDCNT;
+ rd_cnt &= BITM_DMC_DLLCTL_DLLCALRDCNT;
+ writel(rd_cnt | data_cyc, dmc.reg + REG_DMC_DLLCTL);
+ writel((dmc.dmc_ctl_value & (~BITM_DMC_CTL_INIT) &
+ (~BITM_DMC_CTL_RL_DQS)), dmc.reg + REG_DMC_CTL);
+
+#if DELAYTRIM
+ /* DQS delay trim*/
+ u32 stat_value, WL_code_LDQS, WL_code_UDQS;
+
+ /* For LDQS */
+ temp = readl(dmc.reg + REG_DMC_DDR_LANE0_CTL1) | (0x000000D0);
+ writel(temp, dmc.reg + REG_DMC_DDR_LANE0_CTL1);
+ dmcdelay(2500u);
+ writel(0x00400000, dmc.reg + REG_DMC_DDR_ROOT_CTL);
+ dmcdelay(2500u);
+ writel(0x0, dmc.reg + REG_DMC_DDR_ROOT_CTL);
+ stat_value = (readl(dmc.reg + REG_DMC_DDR_SCRATCH_STAT0) &
+ (0xFFFF0000)) >> 16;
+ WL_code_LDQS = (stat_value) & (0x0000001F);
+
+ temp = readl(dmc.reg + REG_DMC_DDR_LANE0_CTL1);
+ temp &= ~(BITM_DMC_DDR_LANE0_CTL1_BYPCODE |
+ BITM_DMC_DDR_LANE0_CTL1_BYPDELCHAINEN);
+ writel(temp, dmc.reg + REG_DMC_DDR_LANE0_CTL1);
+
+ /* If write leveling is enabled */
+ if ((dmc.dmc_mr1_value & BITM_DMC_MR1_WL) >> BITP_DMC_MR1_WL) {
+ temp = readl(dmc.reg + REG_DMC_DDR_LANE0_CTL1);
+ temp |= (((WL_code_LDQS + LANE0_DQS_DELAY) <<
+ BITP_DMC_DDR_LANE0_CTL1_BYPCODE) &
+ BITM_DMC_DDR_LANE0_CTL1_BYPCODE) |
+ BITM_DMC_DDR_LANE0_CTL1_BYPDELCHAINEN;
+ writel(temp, dmc.reg + REG_DMC_DDR_LANE0_CTL1);
+ } else {
+ temp = readl(dmc.reg + REG_DMC_DDR_LANE0_CTL1);
+ temp |= (((DQS_DEFAULT_DELAY + LANE0_DQS_DELAY) <<
+ BITP_DMC_DDR_LANE0_CTL1_BYPCODE) &
+ BITM_DMC_DDR_LANE0_CTL1_BYPCODE) |
+ BITM_DMC_DDR_LANE0_CTL1_BYPDELCHAINEN;
+ writel(temp, dmc.reg + REG_DMC_DDR_LANE0_CTL1);
+ }
+ dmcdelay(2500u);
+
+ /* For UDQS */
+ temp = readl(dmc.reg + REG_DMC_DDR_LANE1_CTL1) | (0x000000D0);
+ writel(temp, dmc.reg + REG_DMC_DDR_LANE1_CTL1);
+ dmcdelay(2500u);
+ writel(0x00800000, dmc.reg + REG_DMC_DDR_ROOT_CTL);
+ dmcdelay(2500u);
+ writel(0x0, dmc.reg + REG_DMC_DDR_ROOT_CTL);
+ stat_value = (readl(dmc.reg + REG_DMC_DDR_SCRATCH_STAT1) &
+ (0xFFFF0000)) >> 16;
+ WL_code_UDQS = (stat_value) & (0x0000001F);
+
+ temp = readl(dmc.reg + REG_DMC_DDR_LANE1_CTL1);
+ temp &= ~(BITM_DMC_DDR_LANE0_CTL1_BYPCODE |
+ BITM_DMC_DDR_LANE0_CTL1_BYPDELCHAINEN);
+ writel(temp, dmc.reg + REG_DMC_DDR_LANE1_CTL1);
+
+ /* If write leveling is enabled */
+ if ((dmc.dmc_mr1_value & BITM_DMC_MR1_WL) >> BITP_DMC_MR1_WL) {
+ temp = readl(dmc.reg + REG_DMC_DDR_LANE1_CTL1);
+ temp |= (((WL_code_UDQS + LANE1_DQS_DELAY) <<
+ BITP_DMC_DDR_LANE0_CTL1_BYPCODE) &
+ BITM_DMC_DDR_LANE0_CTL1_BYPCODE) |
+ BITM_DMC_DDR_LANE0_CTL1_BYPDELCHAINEN;
+ writel(temp, dmc.reg + REG_DMC_DDR_LANE1_CTL1);
+ } else {
+ temp = readl(dmc.reg + REG_DMC_DDR_LANE1_CTL1);
+ temp |= (((DQS_DEFAULT_DELAY + LANE1_DQS_DELAY) <<
+ BITP_DMC_DDR_LANE0_CTL1_BYPCODE) &
+ BITM_DMC_DDR_LANE0_CTL1_BYPCODE) |
+ BITM_DMC_DDR_LANE0_CTL1_BYPDELCHAINEN;
+ writel(temp, dmc.reg + REG_DMC_DDR_LANE1_CTL1);
+ }
+ dmcdelay(2500u);
+#endif
+
+#else
+ /* 5. Program the DMCx_CTL.DLLCTL register with 0x948 value
+ * (DATACYC=9, DLLCALRDCNT=72).
+ */
+ writel(0x00000948, dmc.reg + REG_DMC_DLLCTL);
+#endif
+
+ /* 6. Workaround for anomaly#20000037 */
+ if (dmc.anomaly_20000037_applicable) {
+ /* Perform dummy read to any DMC location */
+ readl(0x80000000);
+
+ writel(readl(dmc.reg + REG_DMC_PHY_CTL0) | 0x1000,
+ dmc.reg + REG_DMC_PHY_CTL0);
+ /* Clear DMCx_PHY_CTL0.RESETDAT bit */
+ writel(readl(dmc.reg + REG_DMC_PHY_CTL0) & (~0x1000),
+ dmc.reg + REG_DMC_PHY_CTL0);
+ }
+}
+
+static inline void dmc_init(void)
+{
+ /* PHY Calibration+Initialization */
+ if (!dmc.phy_init_required)
+ goto out;
+
+ switch (dmc.calib_mode) {
+ case CALIBRATION_LEGACY:
+ calibration_legacy();
+ break;
+ case CALIBRATION_METHOD1:
+ calibration_method1();
+ break;
+ case CALIBRATION_METHOD2:
+ calibration_method2();
+ break;
+ }
+
+#if DQSTRIM
+ /* DQS duty trim */
+ temp = readl(dmc.reg + REG_DMC_DDR_LANE0_CTL0);
+ temp |= ((DQSCODE) << BITP_DMC_DDR_LANE0_CTL0_BYPENB) &
+ (BITM_DMC_DDR_LANE1_CTL0_BYPENB |
+ BITM_DMC_DDR_LANE0_CTL0_BYPSELP |
+ BITM_DMC_DDR_LANE0_CTL0_BYPCODE);
+ writel(temp, dmc.reg + REG_DMC_DDR_LANE0_CTL0);
+
+ temp = readl(dmc.reg + REG_DMC_DDR_LANE1_CTL0);
+ temp |= ((DQSCODE) << BITP_DMC_DDR_LANE1_CTL0_BYPENB) &
+ (BITM_DMC_DDR_LANE1_CTL1_BYPCODE |
+ BITM_DMC_DDR_LANE1_CTL0_BYPSELP |
+ BITM_DMC_DDR_LANE1_CTL0_BYPCODE);
+ writel(temp, dmc.reg + REG_DMC_DDR_LANE1_CTL0);
+#endif
+
+#if CLKTRIM
+ /* Clock duty trim */
+ temp = readl(dmc.reg + REG_DMC_DDR_CA_CTL);
+ temp |= (((CLKCODE << BITP_DMC_DDR_CA_CTL_BYPCODE1) &
+ BITM_DMC_DDR_CA_CTL_BYPCODE1) |
+ BITM_DMC_DDR_CA_CTL_BYPENB |
+ ((CLKDIR << BITP_DMC_DDR_CA_CTL_BYPSELP) &
+ BITM_DMC_DDR_CA_CTL_BYPSELP));
+ writel(temp, dmc.reg + REG_DMC_DDR_CA_CTL);
+#endif
+
+out:
+ /* Controller Initialization */
+ dmc_controller_init();
+}
+
+static inline void __dmc_config(uint32_t dmc_no)
+{
+ if (dmc_no == 0) {
+ dmc.reg = REG_DMC0_BASE;
+ dmc.dmc_data_calib_add_value = DMC0_DATA_CALIB_ADD;
+ } else if (dmc_no == 1) {
+ dmc.reg = REG_DMC1_BASE;
+ dmc.dmc_data_calib_add_value = DMC1_DATA_CALIB_ADD;
+ } else {
+ return;
+ }
+
+ if (IS_ENABLED(CONFIG_ADI_USE_DDR2))
+ dmc.ddr_mode = DDR2_MODE;
+ else
+ dmc.ddr_mode = DDR3_MODE;
+
+ dmc.phy_init_required = true;
+
+#if defined(CONFIG_SC59X) || defined(CONFIG_SC59X_64)
+ dmc.anomaly_20000037_applicable = false;
+ dmc.dmc_dllctl_value = DMC_DLLCTL_VALUE;
+ dmc.calib_mode = CALIBRATION_METHOD2;
+#else
+ dmc.anomaly_20000037_applicable = true;
+ dmc.calib_mode = CALIBRATION_LEGACY;
+#endif
+
+ dmc.dmc_ctl_value = DMC_CTL_VALUE;
+ dmc.dmc_cfg_value = DMC_CFG_VALUE;
+ dmc.dmc_tr0_value = DMC_TR0_VALUE;
+ dmc.dmc_tr1_value = DMC_TR1_VALUE;
+ dmc.dmc_tr2_value = DMC_TR2_VALUE;
+ dmc.dmc_mr0_value = DMC_MR0_VALUE;
+ dmc.dmc_mr1_value = DMC_MR1_VALUE;
+ dmc.dmc_mr2_value = DMC_MR2_VALUE;
+
+#if defined(CONFIG_SC59X) || defined(CONFIG_SC59X_64)
+ dmc.dmc_mr3_value = DMC_MR3_VALUE;
+ dmc.dmc_zqctl0_value = DMC_ZQCTL0_VALUE;
+ dmc.dmc_zqctl1_value = DMC_ZQCTL1_VALUE;
+ dmc.dmc_zqctl2_value = DMC_ZQCTL2_VALUE;
+#endif
+
+ dmc.padctl2_value = DMC_PADCTL2_VALUE;
+ dmc.dmc_cphyctl_value = DMC_CPHYCTL_VALUE;
+
+ /* Initialize DMC now */
+ dmc_init();
+}
+
+void DMC_Config(void)
+{
+ if (IS_ENABLED(CONFIG_ADI_USE_DMC0))
+ __dmc_config(0);
+
+ if (IS_ENABLED(CONFIG_ADI_USE_DMC1))
+ __dmc_config(1);
+}
diff --git a/arch/arm/mach-sc5xx/init/dmcinit.h b/arch/arm/mach-sc5xx/init/dmcinit.h
new file mode 100644
index 0000000..46ff729
--- /dev/null
+++ b/arch/arm/mach-sc5xx/init/dmcinit.h
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * (C) Copyright 2022 - Analog Devices, Inc.
+ *
+ * Written and/or maintained by Timesys Corporation
+ *
+ * Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com>
+ * Contact: Greg Malysa <greg.malysa@timesys.com>
+ */
+
+#ifndef DMCINIT_H_
+#define DMCINIT_H_
+
+#include <config.h>
+
+#ifdef MEM_MT41K512M16HA
+ #include "mem/mt41k512m16ha.h"
+#elif defined(MEM_MT41K128M16JT)
+ #include "mem/mt41k128m16jt.h"
+#elif defined(MEM_MT47H128M16RT)
+ #include "mem/mt47h128m16rt.h"
+#elif defined(MEM_IS43TR16512BL)
+ #include "mem/is43tr16512bl.h"
+#else
+ #error "No DDR part name is defined for this board."
+#endif
+
+void DMC_Config(void);
+void adi_dmc_reset_lanes(bool reset);
+
+#endif
diff --git a/arch/arm/mach-sc5xx/init/mem/is43tr16512bl.h b/arch/arm/mach-sc5xx/init/mem/is43tr16512bl.h
new file mode 100644
index 0000000..a583837
--- /dev/null
+++ b/arch/arm/mach-sc5xx/init/mem/is43tr16512bl.h
@@ -0,0 +1,62 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * (C) Copyright 2022 - Analog Devices, Inc.
+ *
+ * Written and/or maintained by Timesys Corporation
+ *
+ * Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com>
+ * Contact: Greg Malysa <greg.malysa@timesys.com>
+ */
+
+#ifndef IS43TR16512BL_H
+#define IS43TR16512BL_H
+
+/* DMC0 setup for the EV-21593-SOM and EV-SC594-SOM :
+ * - uses a single 8GB IS43TR16512BL-125KBL DDR3 chip configured for
+ * 800 MHz DCLK.
+ * DMC0 setup for the EV-SC594-SOMS :
+ * - uses a single 4GB IS43TR16256BL-093NBL DDR3 chip configured for
+ * 800 MHz DCLK.
+ */
+#define DMC_DLLCALRDCNT 240
+#define DMC_DATACYC 12
+#define DMC_TRCD 11
+#define DMC_TWTR 6
+#define DMC_TRP 11
+#define DMC_TRAS 28
+#define DMC_TRC 39
+#define DMC_TMRD 4
+#define DMC_TREF 6240
+#define DMC_TRRD 6
+#define DMC_TFAW 32
+#define DMC_TRTP 6
+#define DMC_TWR 12
+#define DMC_TXP 5
+#define DMC_TCKE 4
+#define DMC_CL0 0
+#define DMC_CL123 7
+#define DMC_WRRECOV 6
+#define DMC_MR1_DLLEN 0
+#define DMC_MR1_DIC0 0
+#define DMC_MR1_RTT0 0
+#define DMC_MR1_AL 0
+#define DMC_MR1_DIC1 0
+#define DMC_MR1_RTT1 1
+#define DMC_MR1_WL 0
+#define DMC_MR1_RTT2 0
+#define DMC_MR1_TDQS 0
+#define DMC_MR1_QOFF 0
+#define DMC_WL 3
+#define DMC_RDTOWR 5
+#define DMC_CTL_AL_EN 1
+#if defined(MEM_ISSI_4Gb_DDR3_800MHZ)
+ #define SDR_CHIP_SIZE (ENUM_DMC_CFG_SDRSIZE4G)
+ #define DMC_TRFC 208ul
+#elif defined(MEM_ISSI_8Gb_DDR3_800MHZ)
+ #define SDR_CHIP_SIZE (ENUM_DMC_CFG_SDRSIZE8G)
+ #define DMC_TRFC 280ul
+#else
+ #error "Need to select MEM_ISSI_4Gb_DDR3_800MHZ or MEM_ISSI_8Gb_DDR3_800MHZ"
+#endif
+
+#endif
diff --git a/arch/arm/mach-sc5xx/init/mem/mt41k128m16jt.h b/arch/arm/mach-sc5xx/init/mem/mt41k128m16jt.h
new file mode 100644
index 0000000..8827775
--- /dev/null
+++ b/arch/arm/mach-sc5xx/init/mem/mt41k128m16jt.h
@@ -0,0 +1,50 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * (C) Copyright 2022 - Analog Devices, Inc.
+ *
+ * Written and/or maintained by Timesys Corporation
+ *
+ * Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com>
+ * Contact: Greg Malysa <greg.malysa@timesys.com>
+ */
+
+#ifndef MT41K128M16JT_H
+#define MT41K128M16JT_H
+
+/* Default DDR3 part assumed: MT41K128M16JT-125, 2Gb part */
+/* For DCLK= 450 MHz */
+#define DMC_DLLCALRDCNT 72
+#define DMC_DATACYC 9
+#define DMC_TRCD 6
+#define DMC_TWTR 4
+#define DMC_TRP 6
+#define DMC_TRAS 17
+#define DMC_TRC 23
+#define DMC_TMRD 4
+#define DMC_TREF 3510
+#define DMC_TRFC 72
+#define DMC_TRRD 4
+#define DMC_TFAW 17
+#define DMC_TRTP 4
+#define DMC_TWR 7
+#define DMC_TXP 4
+#define DMC_TCKE 3
+#define DMC_CL0 0
+#define DMC_CL123 3
+#define DMC_WRRECOV (DMC_TWR - 1)
+#define DMC_MR1_DLLEN 0
+#define DMC_MR1_DIC0 1
+#define DMC_MR1_RTT0 1
+#define DMC_MR1_AL 0
+#define DMC_MR1_DIC1 0
+#define DMC_MR1_RTT1 0
+#define DMC_MR1_WL 0
+#define DMC_MR1_RTT2 0
+#define DMC_MR1_TDQS 0
+#define DMC_MR1_QOFF 0
+#define DMC_WL 1
+#define DMC_RDTOWR 2
+#define DMC_CTL_AL_EN 0
+#define SDR_CHIP_SIZE ENUM_DMC_CFG_SDRSIZE2G
+
+#endif
diff --git a/arch/arm/mach-sc5xx/init/mem/mt41k512m16ha.h b/arch/arm/mach-sc5xx/init/mem/mt41k512m16ha.h
new file mode 100644
index 0000000..5735b87
--- /dev/null
+++ b/arch/arm/mach-sc5xx/init/mem/mt41k512m16ha.h
@@ -0,0 +1,50 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * (C) Copyright 2022 - Analog Devices, Inc.
+ *
+ * Written and/or maintained by Timesys Corporation
+ *
+ * Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com>
+ * Contact: Greg Malysa <greg.malysa@timesys.com>
+ */
+
+#ifndef MT41K512M16HA_H
+#define MT41K512M16HA_H
+
+/* Default DDR3 part assumed: MT41K512M16HA-107, 8Gb part */
+/* For DCLK= 450 MHz */
+#define DMC_DLLCALRDCNT 72
+#define DMC_DATACYC 9
+#define DMC_TRCD 7
+#define DMC_TWTR 4
+#define DMC_TRP 7
+#define DMC_TRAS 10
+#define DMC_TRC 16
+#define DMC_TMRD 4
+#define DMC_TREF 3510
+#define DMC_TRFC 158
+#define DMC_TRRD 6
+#define DMC_TFAW 16
+#define DMC_TRTP 4
+#define DMC_TWR 7
+#define DMC_TXP 3
+#define DMC_TCKE 3
+#define DMC_CL0 0
+#define DMC_CL123 3
+#define DMC_WRRECOV (DMC_TWR - 1)
+#define DMC_MR1_DLLEN 0
+#define DMC_MR1_DIC0 1
+#define DMC_MR1_RTT0 1
+#define DMC_MR1_AL 0
+#define DMC_MR1_DIC1 0
+#define DMC_MR1_RTT1 0
+#define DMC_MR1_WL 0
+#define DMC_MR1_RTT2 0
+#define DMC_MR1_TDQS 0
+#define DMC_MR1_QOFF 0
+#define DMC_WL 1
+#define DMC_RDTOWR 2
+#define DMC_CTL_AL_EN 0
+#define SDR_CHIP_SIZE ENUM_DMC_CFG_SDRSIZE8G
+
+#endif
diff --git a/arch/arm/mach-sc5xx/init/mem/mt47h128m16rt.h b/arch/arm/mach-sc5xx/init/mem/mt47h128m16rt.h
new file mode 100644
index 0000000..5ada7f2
--- /dev/null
+++ b/arch/arm/mach-sc5xx/init/mem/mt47h128m16rt.h
@@ -0,0 +1,49 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * (C) Copyright 2022 - Analog Devices, Inc.
+ *
+ * Written and/or maintained by Timesys Corporation
+ *
+ * Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com>
+ * Contact: Greg Malysa <greg.malysa@timesys.com>
+ */
+
+#ifndef MT47H128M16RT_H
+#define MT47H128M16RT_H
+
+/* Default DDR2 part: MT47H128M16RT-25E XIT:C, 2 Gb part */
+/* For DCLK= 400 MHz */
+#define DMC_DLLCALRDCNT 72
+#define DMC_DATACYC 9
+#define DMC_TRCD 5
+#define DMC_TWTR 3
+#define DMC_TRP 5
+#define DMC_TRAS 16
+#define DMC_TRC 22
+#define DMC_TMRD 2
+#define DMC_TREF 3120
+#define DMC_TRFC 78
+#define DMC_TRRD 4
+#define DMC_TFAW 18
+#define DMC_TRTP 3
+#define DMC_TWR 6
+#define DMC_TXP 2
+#define DMC_TCKE 3
+#define DMC_CL 5
+#define DMC_WRRECOV (DMC_TWR - 1)
+#define DMC_MR1_DLLEN 0
+#define DMC_MR1_DIC0 1
+#define DMC_MR1_RTT0 1
+#define DMC_MR1_AL 4
+#define DMC_MR1_DIC1 0
+#define DMC_MR1_RTT1 0
+#define DMC_MR1_WL 0
+#define DMC_MR1_RTT2 0
+#define DMC_MR1_TDQS 0
+#define DMC_MR1_QOFF 0
+#define DMC_BL 4
+#define DMC_RDTOWR 2
+#define DMC_CTL_AL_EN 0
+#define SDR_CHIP_SIZE ENUM_DMC_CFG_SDRSIZE2G
+
+#endif
diff --git a/arch/arm/mach-sc5xx/rcu.c b/arch/arm/mach-sc5xx/rcu.c
new file mode 100644
index 0000000..4935750
--- /dev/null
+++ b/arch/arm/mach-sc5xx/rcu.c
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * (C) Copyright 2024 - Analog Devices, Inc.
+ *
+ * Written and/or maintained by Timesys Corporation
+ *
+ * Contact: Ian Roberts <ian.roberts@timesys.com>
+ */
+
+#include <dm.h>
+#include <syscon.h>
+
+static const struct udevice_id adi_syscon_ids[] = {
+ { .compatible = "adi,reset-controller" },
+ { }
+};
+
+U_BOOT_DRIVER(syscon_sc5xx_rcu) = {
+ .name = "sc5xx_rcu",
+ .id = UCLASS_SYSCON,
+ .of_match = adi_syscon_ids,
+};
diff --git a/arch/arm/mach-sc5xx/sc57x.c b/arch/arm/mach-sc5xx/sc57x.c
new file mode 100644
index 0000000..b058768
--- /dev/null
+++ b/arch/arm/mach-sc5xx/sc57x.c
@@ -0,0 +1,32 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * (C) Copyright 2024 - Analog Devices, Inc.
+ *
+ * Written and/or maintained by Timesys Corporation
+ *
+ * Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com>
+ * Contact: Greg Malysa <greg.malysa@timesys.com>
+ */
+
+#include <asm/io.h>
+#include <asm/arch-adi/sc5xx/sc5xx.h>
+#include <asm/arch-adi/sc5xx/spl.h>
+
+#define REG_SPU0_SECUREC0 0x3108B980
+#define REG_PADS0_PCFG0 0x31004404
+#define REG_SPU0_SECUREP_START 0x3108BA00
+#define REG_SPU0_SECUREP_END 0x3108BD24
+
+adi_rom_boot_fn adi_rom_boot = (adi_rom_boot_fn)0x000000e1;
+
+void sc5xx_enable_rgmii(void)
+{
+ writel((readl(REG_PADS0_PCFG0) | 0xc), REG_PADS0_PCFG0);
+}
+
+void sc5xx_soc_init(void)
+{
+ sc5xx_enable_ns_sharc_access(REG_SPU0_SECUREC0);
+ sc5xx_disable_spu0(REG_SPU0_SECUREP_START, REG_SPU0_SECUREP_END);
+ sc5xx_enable_pmu();
+}
diff --git a/arch/arm/mach-sc5xx/sc58x.c b/arch/arm/mach-sc5xx/sc58x.c
new file mode 100644
index 0000000..0f89277
--- /dev/null
+++ b/arch/arm/mach-sc5xx/sc58x.c
@@ -0,0 +1,32 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * (C) Copyright 2024 - Analog Devices, Inc.
+ *
+ * Written and/or maintained by Timesys Corporation
+ *
+ * Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com>
+ * Contact: Greg Malysa <greg.malysa@timesys.com>
+ */
+
+#include <asm/io.h>
+#include <asm/arch-adi/sc5xx/sc5xx.h>
+#include <asm/arch-adi/sc5xx/spl.h>
+
+#define REG_SPU0_SECUREC0 0x3108C980
+#define REG_PADS0_PCFG0 0x31004404
+#define REG_SPU0_SECUREP_START 0x3108CA00
+#define REG_SPU0_SECUREP_END 0x3108CCF0
+
+adi_rom_boot_fn adi_rom_boot = (adi_rom_boot_fn)0x000000e1;
+
+void sc5xx_enable_rgmii(void)
+{
+ writel((readl(REG_PADS0_PCFG0) | 0xc), REG_PADS0_PCFG0);
+}
+
+void sc5xx_soc_init(void)
+{
+ sc5xx_enable_ns_sharc_access(REG_SPU0_SECUREC0);
+ sc5xx_disable_spu0(REG_SPU0_SECUREP_START, REG_SPU0_SECUREP_END);
+ sc5xx_enable_pmu();
+}
diff --git a/arch/arm/mach-sc5xx/sc59x.c b/arch/arm/mach-sc5xx/sc59x.c
new file mode 100644
index 0000000..174c6f5
--- /dev/null
+++ b/arch/arm/mach-sc5xx/sc59x.c
@@ -0,0 +1,43 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * (C) Copyright 2024 - Analog Devices, Inc.
+ *
+ * Written and/or maintained by Timesys Corporation
+ *
+ * Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com>
+ * Contact: Greg Malysa <greg.malysa@timesys.com>
+ */
+
+#include <asm/io.h>
+#include <asm/arch-adi/sc5xx/sc5xx.h>
+#include <asm/arch-adi/sc5xx/spl.h>
+
+#define REG_SPU0_SECUREC0 0x3108B980
+#define REG_PADS0_PCFG0 0x31004604
+#define REG_SPU0_SECUREP_START 0x3108BA00
+#define REG_SPU0_SECUREP_END 0x3108BD24
+
+#define REG_SCB5_SPI2_OSPI_REMAP 0x30400000
+#define BITM_SCB5_SPI2_OSPI_REMAP_REMAP 0x00000003
+#define ENUM_SCB5_SPI2_OSPI_REMAP_OSPI0 0x00000001
+
+adi_rom_boot_fn adi_rom_boot = (adi_rom_boot_fn)0x000000e9;
+
+void sc5xx_enable_rgmii(void)
+{
+ writel((readl(REG_PADS0_PCFG0) | 0xc), REG_PADS0_PCFG0);
+}
+
+void sc59x_remap_ospi(void)
+{
+ clrsetbits_le32(REG_SCB5_SPI2_OSPI_REMAP,
+ BITM_SCB5_SPI2_OSPI_REMAP_REMAP,
+ ENUM_SCB5_SPI2_OSPI_REMAP_OSPI0);
+}
+
+void sc5xx_soc_init(void)
+{
+ sc5xx_enable_ns_sharc_access(REG_SPU0_SECUREC0);
+ sc5xx_disable_spu0(REG_SPU0_SECUREP_START, REG_SPU0_SECUREP_END);
+ sc5xx_enable_pmu();
+}
diff --git a/arch/arm/mach-sc5xx/sc59x_64.c b/arch/arm/mach-sc5xx/sc59x_64.c
new file mode 100644
index 0000000..82537bf
--- /dev/null
+++ b/arch/arm/mach-sc5xx/sc59x_64.c
@@ -0,0 +1,97 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * (C) Copyright 2024 - Analog Devices, Inc.
+ *
+ * Written and/or maintained by Timesys Corporation
+ *
+ * Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com>
+ * Contact: Greg Malysa <greg.malysa@timesys.com>
+ */
+
+#include <asm/io.h>
+#include <asm/arch-adi/sc5xx/sc5xx.h>
+#include <asm/arch-adi/sc5xx/spl.h>
+
+#define REG_TSGENWR0_CNTCR 0x310AE000
+#define REG_PADS0_PCFG0 0x31004604
+#define REG_RCU0_BCODE 0x3108C028
+
+#define REG_SPU0_SECUREP_START 0x3108BA00
+#define REG_SPU0_WP_START 0x3108B400
+#define REG_SPU0_SECUREC0 0x3108B980
+
+#define REG_SCB5_SPI2_OSPI_REMAP 0x30400000
+#define BITM_SCB5_SPI2_OSPI_REMAP_REMAP 0x00000003
+#define ENUM_SCB5_SPI2_OSPI_REMAP_OSPI0 0x00000001
+
+adi_rom_boot_fn adi_rom_boot = (adi_rom_boot_fn)0x000000e4;
+
+void sc5xx_enable_rgmii(void)
+{
+ writel((readl(REG_PADS0_PCFG0) | 0xc), REG_PADS0_PCFG0);
+
+ // Set dw for little endian operation as well
+ writel(readl(REG_PADS0_PCFG0) & ~(1 << 19), REG_PADS0_PCFG0);
+ writel(readl(REG_PADS0_PCFG0) & ~(1 << 20), REG_PADS0_PCFG0);
+}
+
+void sc59x_remap_ospi(void)
+{
+ clrsetbits_le32(REG_SCB5_SPI2_OSPI_REMAP,
+ BITM_SCB5_SPI2_OSPI_REMAP_REMAP,
+ ENUM_SCB5_SPI2_OSPI_REMAP_OSPI0);
+}
+
+/**
+ * SPU/SMPU configuration is the default for permissive access from non-secure
+ * EL1. If TFA and OPTEE are configured, they run *after* this code, as the
+ * current boot flow is SPL -> TFA -> OPTEE -> Proper -> Linux, and will
+ * be expected to configure peripheral security correctly. If they are not
+ * configured, then this permissive setting will allow Linux (which always
+ * runs in NS EL1) to control all access to these peripherals. Without it,
+ * the peripherals would simply be unavailable in a non-security build,
+ * which is not OK.
+ */
+void sc5xx_soc_init(void)
+{
+ phys_addr_t smpus[] = {
+ 0x31007800, //SMPU0
+ 0x31083800, //SMPU2
+ 0x31084800, //SMPU3
+ 0x31085800, //SMPU4
+ 0x31086800, //SMPU5
+ 0x31087800, //SMPU6
+ 0x310A0800, //SMPU9
+ 0x310A1800, //SMPU11
+ 0x31012800, //SMPU12
+ };
+ size_t i;
+
+ // Enable coresight timer
+ writel(1, REG_TSGENWR0_CNTCR);
+
+ //Do not rerun preboot routine --
+ // Without this, hardware resets triggered by RCU0_CTL:SYSRST
+ // lead to a deadlock somewhere in the boot ROM
+ writel(0x200, REG_RCU0_BCODE);
+
+ /* Alter outstanding transactions property of A55*/
+ writel(0x1, 0x30643108); /* SCB6 A55 M0 Ib.fn Mod */
+ isb();
+
+ /* configure DDR prefetch behavior, per ADI */
+ writel(0x1, 0x31076000);
+
+ /* configure smart mode, per ADI */
+ writel(0x1307, 0x31076004);
+
+ // Disable SPU and SPU WP registers
+ sc5xx_disable_spu0(REG_SPU0_SECUREP_START, REG_SPU0_SECUREP_START + 4*213);
+ sc5xx_disable_spu0(REG_SPU0_WP_START, REG_SPU0_WP_START + 4*213);
+
+ /* configure smpus permissively */
+ for (i = 0; i < ARRAY_SIZE(smpus); ++i)
+ writel(0x500, smpus[i]);
+
+ sc5xx_enable_ns_sharc_access(REG_SPU0_SECUREC0);
+}
diff --git a/arch/arm/mach-sc5xx/soc.c b/arch/arm/mach-sc5xx/soc.c
new file mode 100644
index 0000000..8f13127
--- /dev/null
+++ b/arch/arm/mach-sc5xx/soc.c
@@ -0,0 +1,179 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * (C) Copyright 2022 - Analog Devices, Inc.
+ *
+ * Written and/or maintained by Timesys Corporation
+ *
+ * Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com>
+ * Contact: Greg Malysa <greg.malysa@timesys.com>
+ */
+
+#include <asm/arch-adi/sc5xx/sc5xx.h>
+#include <asm/arch-adi/sc5xx/soc.h>
+#include <asm/global_data.h>
+#include <asm/io.h>
+#include <cpu_func.h>
+
+#ifdef CONFIG_SC58X
+ #define RCU0_CTL 0x3108B000
+ #define RCU0_STAT 0x3108B004
+ #define RCU0_CRCTL 0x3108B008
+ #define RCU0_CRSTAT 0x3108B00C
+ #define RCU0_SIDIS 0x3108B010
+ #define RCU0_MSG_SET 0x3108B064
+#elif defined(CONFIG_SC57X) || defined(CONFIG_SC59X) || defined(CONFIG_SC59X_64)
+ #define RCU0_CTL 0x3108C000
+ #define RCU0_STAT 0x3108C004
+ #define RCU0_CRCTL 0x3108C008
+ #define RCU0_CRSTAT 0x3108C00C
+ #define RCU0_SIDIS 0x3108C01C
+ #define RCU0_MSG_SET 0x3108C070
+#else
+ #error "No SC5xx SoC CONFIG_ enabled"
+#endif
+
+#define BITP_RCU_STAT_BMODE 8
+#define BITM_RCU_STAT_BMODE 0x00000F00
+
+#define REG_ARMPMU0_PMCR 0x31121E04
+#define REG_ARMPMU0_PMUSERENR 0x31121E08
+#define REG_ARMPMU0_PMLAR 0x31121FB0
+
+DECLARE_GLOBAL_DATA_PTR;
+
+void reset_cpu(void)
+{
+ u32 val = readl(RCU0_CTL);
+ writel(val | 1, RCU0_CTL);
+}
+
+void enable_caches(void)
+{
+ if (!IS_ENABLED(CONFIG_SYS_DCACHE_OFF))
+ dcache_enable();
+}
+
+void sc5xx_enable_ns_sharc_access(uintptr_t securec0_base)
+{
+ writel(0, securec0_base);
+ writel(0, securec0_base + 0x4);
+ writel(0, securec0_base + 0x8);
+}
+
+void sc5xx_disable_spu0(uintptr_t spu0_start, uintptr_t spu0_end)
+{
+ for (uintptr_t i = spu0_start; i <= spu0_end; i += 4)
+ writel(0, i);
+}
+
+/**
+ * PMU is only available on armv7 platforms and all share the same location
+ */
+void sc5xx_enable_pmu(void)
+{
+ if (!IS_ENABLED(CONFIG_SC59X_64)) {
+ writel(readl(REG_ARMPMU0_PMUSERENR) | 0x01, REG_ARMPMU0_PMUSERENR);
+ writel(0xc5acce55, REG_ARMPMU0_PMLAR);
+ writel(readl(REG_ARMPMU0_PMCR) | (1 << 1), REG_ARMPMU0_PMCR);
+ }
+}
+
+const char *sc5xx_get_boot_mode(u32 *bmode)
+{
+ static const char * const bmodes[] = {
+ "JTAG/BOOTROM",
+ "QSPI Master",
+ "QSPI Slave",
+ "UART",
+ "LP0 Slave",
+ "OSPI",
+#ifdef CONFIG_SC59X_64
+ "eMMC"
+#endif
+ };
+ u32 local_mode;
+
+ local_mode = (readl(RCU0_STAT) & BITM_RCU_STAT_BMODE) >> BITP_RCU_STAT_BMODE;
+
+#if CONFIG_ADI_SPL_FORCE_BMODE != 0
+ /*
+ * In case we want to force boot sequences such as:
+ * QSPI -> OSPI
+ * QSPI -> eMMC
+ * If this is not set, then we will always try to use the BMODE setting
+ * for both stages... i.e.
+ * QSPI -> QSPI
+ */
+
+ // (Don't allow skipping JTAG/UART BMODE settings)
+ if (local_mode != 0 && local_mode != 3)
+ local_mode = CONFIG_ADI_SPL_FORCE_BMODE;
+#endif
+
+ *bmode = local_mode;
+
+ if (local_mode >= 0 && local_mode <= ARRAY_SIZE(bmodes))
+ return bmodes[local_mode];
+ return "unknown";
+}
+
+void print_cpu_id(void)
+{
+ if (!IS_ENABLED(CONFIG_ARM64)) {
+ u32 cpuid = 0;
+
+ __asm__ __volatile__("mrc p15, 0, %0, c0, c0, 0" : "=r"(cpuid));
+
+ printf("Detected Revision: %d.%d\n", cpuid & 0xf00000 >> 20, cpuid & 0xf);
+ }
+}
+
+int print_cpuinfo(void)
+{
+ u32 bmode;
+
+ printf("CPU: ADSP %s (%s boot)\n", CONFIG_LDR_CPU, sc5xx_get_boot_mode(&bmode));
+ print_cpu_id();
+
+ return 0;
+}
+
+void fixup_dp83867_phy(struct phy_device *phydev)
+{
+ int phy_data = 0;
+
+ phy_data = phy_read(phydev, MDIO_DEVAD_NONE, 0x32);
+ phy_write(phydev, MDIO_DEVAD_NONE, 0x32, (1 << 7) | phy_data);
+ int cfg3 = 0;
+ #define MII_DP83867_CFG3 (0x1e)
+ /*
+ * Pin INT/PWDN on DP83867 should be configured as an Interrupt Output
+ * instead of a Power-Down Input on ADI SC5XX boards in order to
+ * prevent the signal interference from other peripherals during they
+ * are running at the same time.
+ */
+ cfg3 = phy_read(phydev, MDIO_DEVAD_NONE, MII_DP83867_CFG3);
+ cfg3 |= (1 << 7);
+ phy_write(phydev, MDIO_DEVAD_NONE, MII_DP83867_CFG3, cfg3);
+
+ // Mystery second port fixup on ezkits with two PHYs
+ if (CONFIG_DW_PORTS & 2)
+ phy_write(phydev, MDIO_DEVAD_NONE, 0x11, 3);
+
+ if (IS_ENABLED(CONFIG_ADI_BUG_EZKHW21)) {
+ phydev->advertising &= PHY_BASIC_FEATURES;
+ phydev->speed = SPEED_100;
+ }
+
+ if (phydev->drv->config)
+ phydev->drv->config(phydev);
+
+ if (IS_ENABLED(CONFIG_ADI_BUG_EZKHW21))
+ phy_write(phydev, MDIO_DEVAD_NONE, 0, 0x3100);
+}
+
+int dram_init(void)
+{
+ gd->ram_size = CFG_SYS_SDRAM_SIZE;
+ return 0;
+}
diff --git a/arch/arm/mach-sc5xx/spl.c b/arch/arm/mach-sc5xx/spl.c
new file mode 100644
index 0000000..68e0310
--- /dev/null
+++ b/arch/arm/mach-sc5xx/spl.c
@@ -0,0 +1,102 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * (C) Copyright 2022 - Analog Devices, Inc.
+ *
+ * Written and/or maintained by Timesys Corporation
+ *
+ * Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com>
+ * Contact: Greg Malysa <greg.malysa@timesys.com>
+ */
+
+#include <spl.h>
+#include <asm/arch-adi/sc5xx/sc5xx.h>
+#include <asm/arch-adi/sc5xx/spl.h>
+#include "init/clkinit.h"
+#include "init/dmcinit.h"
+
+static bool adi_start_uboot_proper;
+
+static int adi_sf_default_bus = CONFIG_SF_DEFAULT_BUS;
+static int adi_sf_default_cs = CONFIG_SF_DEFAULT_CS;
+static int adi_sf_default_speed = CONFIG_SF_DEFAULT_SPEED;
+
+u32 bmode;
+
+int spl_start_uboot(void)
+{
+ return adi_start_uboot_proper;
+}
+
+unsigned int spl_spi_get_default_speed(void)
+{
+ return adi_sf_default_speed;
+}
+
+unsigned int spl_spi_get_default_bus(void)
+{
+ return adi_sf_default_bus;
+}
+
+unsigned int spl_spi_get_default_cs(void)
+{
+ return adi_sf_default_cs;
+}
+
+void board_boot_order(u32 *spl_boot_list)
+{
+ const char *bmodestring = sc5xx_get_boot_mode(&bmode);
+
+ printf("ADI Boot Mode: 0x%x (%s)\n", bmode, bmodestring);
+
+ /*
+ * By default everything goes back to the bootrom, where we'll read table
+ * parameters and ask for another image to be loaded
+ */
+ spl_boot_list[0] = BOOT_DEVICE_BOOTROM;
+
+ if (bmode == 0) {
+ printf("SPL execution has completed. Please load U-Boot Proper via JTAG");
+ while (1)
+ ;
+ }
+}
+
+int32_t __weak adi_rom_boot_hook(struct ADI_ROM_BOOT_CONFIG *config, int32_t cause)
+{
+ return 0;
+}
+
+int board_return_to_bootrom(struct spl_image_info *spl_image,
+ struct spl_boot_device *bootdev)
+{
+#if CONFIG_ADI_SPL_FORCE_BMODE != 0
+ // see above
+ if (bmode != 0 && bmode != 3)
+ bmode = CONFIG_ADI_SPL_FORCE_BMODE;
+#endif
+
+ if (bmode >= (ARRAY_SIZE(adi_rom_boot_args)))
+ bmode = 0;
+
+ adi_rom_boot((void *)adi_rom_boot_args[bmode].addr,
+ adi_rom_boot_args[bmode].flags,
+ 0, &adi_rom_boot_hook,
+ adi_rom_boot_args[bmode].cmd);
+ return 0;
+};
+
+void board_init_f(ulong dummy)
+{
+ int ret;
+
+ clks_init();
+ DMC_Config();
+ sc5xx_soc_init();
+
+ ret = spl_early_init();
+ if (ret)
+ panic("spl_early_init() failed\n");
+
+ preloader_console_init();
+}
+
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index bda6873..9acbc47 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -246,6 +246,7 @@ config CLK_ZYNQMP
This clock driver adds support for clock realted settings for
ZynqMP platform.
+source "drivers/clk/adi/Kconfig"
source "drivers/clk/analogbits/Kconfig"
source "drivers/clk/at91/Kconfig"
source "drivers/clk/exynos/Kconfig"
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 638ad04..847b9b2 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_$(SPL_TPL_)CLK_CCF) += clk-fixed-factor.o
obj-$(CONFIG_$(SPL_TPL_)CLK_COMPOSITE_CCF) += clk-composite.o
obj-$(CONFIG_$(SPL_TPL_)CLK_GPIO) += clk-gpio.o
+obj-y += adi/
obj-y += analogbits/
obj-y += imx/
obj-$(CONFIG_CLK_JH7110) += starfive/
diff --git a/drivers/clk/adi/Kconfig b/drivers/clk/adi/Kconfig
new file mode 100644
index 0000000..5745bed
--- /dev/null
+++ b/drivers/clk/adi/Kconfig
@@ -0,0 +1,83 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# (C) Copyright 2022 - Analog Devices, Inc.
+#
+# Written and/or maintained by Timesys Corporation
+#
+# Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com>
+# Contact: Greg Malysa <greg.malysa@timesys.com>
+#
+
+config COMMON_CLK_ADI_SHARED
+ bool "Enable shared ADI clock framework code"
+ help
+ Required for shared code between SoC clock drivers. Automatically
+ selected by an appropriate SoC-specific clock driver version.
+
+config COMMON_CLK_ADI_SC598
+ bool "Clock driver for ADI SC598 SoCs"
+ select DM
+ select CLK
+ select CLK_CCF
+ select OF_CONTROL
+ select CMD_CLK
+ select SPL_DM if SPL
+ select SPL_CLK if SPL
+ select SPL_CLK_CCF if SPL
+ select SPL_OF_CONTROL if SPL
+ select COMMON_CLK_ADI_SHARED
+ depends on SC59X_64
+ help
+ This driver supports the system clocks on Analog Devices SC598-series
+ SoCs. It includes CGU and CDU clocks and supports gating unused clocks.
+ Modifying PLL configuration is not supported; that must be done prior
+ to booting the kernel. Clock dividers after the PLLs may be configured.
+
+config COMMON_CLK_ADI_SC594
+ bool "Clock driver for ADI SC594 SoCs"
+ select DM
+ select CLK
+ select CLK_CCF
+ select OF_CONTROL
+ select CMD_CLK
+ select SPL_DM if SPL
+ select SPL_CLK if SPL
+ select SPL_CLK_CCF if SPL
+ select SPL_OF_CONTROL if SPL
+ select COMMON_CLK_ADI_SHARED
+ depends on SC59X
+ help
+ This driver supports the system clocks on Analog Devices SC594-series
+ SoCs. It includes CGU and CDU clocks and supports gating unused clocks.
+ Modifying PLL configuration is not supported; that must be done prior
+ to booting the kernel. Clock dividers after the PLLs may be configured.
+
+config COMMON_CLK_ADI_SC58X
+ bool "Clock driver for ADI SC58X SoCs"
+ select DM
+ select CLK
+ select CLK_CCF
+ select OF_CONTROL
+ select CMD_CLK
+ select COMMON_CLK_ADI_SHARED
+ depends on SC58X
+ help
+ This driver supports the system clocks on Analog Devices SC58x-series
+ SoCs. It includes CGU and CDU clocks and supports gating unused clocks.
+ Modifying PLL configuration is not supported; that must be done prior
+ to booting the kernel. Clock dividers after the PLLs may be configured.
+
+config COMMON_CLK_ADI_SC57X
+ bool "Clock driver for ADI SC57X SoCs"
+ select DM
+ select CLK
+ select CLK_CCF
+ select OF_CONTROL
+ select CMD_CLK
+ select COMMON_CLK_ADI_SHARED
+ depends on SC57X
+ help
+ This driver supports the system clocks on Analog Devices SC57x-series
+ SoCs. It includes CGU and CDU clocks and supports gating unused clocks.
+ Modifying PLL configuration is not supported; that must be done prior
+ to booting the kernel. Clock dividers after the PLLs may be configured.
diff --git a/drivers/clk/adi/Makefile b/drivers/clk/adi/Makefile
new file mode 100644
index 0000000..f3f1fd9
--- /dev/null
+++ b/drivers/clk/adi/Makefile
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# (C) Copyright 2022 - Analog Devices, Inc.
+#
+# Written and/or maintained by Timesys Corporation
+#
+# Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com>
+# Contact: Greg Malysa <greg.malysa@timesys.com>
+#
+
+obj-$(CONFIG_COMMON_CLK_ADI_SHARED) += clk-shared.o clk-adi-pll.o
+
+obj-$(CONFIG_COMMON_CLK_ADI_SC594) += clk-adi-sc594.o
+obj-$(CONFIG_COMMON_CLK_ADI_SC598) += clk-adi-sc598.o
+obj-$(CONFIG_COMMON_CLK_ADI_SC58X) += clk-adi-sc58x.o
+obj-$(CONFIG_COMMON_CLK_ADI_SC57X) += clk-adi-sc57x.o
diff --git a/drivers/clk/adi/clk-adi-pll.c b/drivers/clk/adi/clk-adi-pll.c
new file mode 100644
index 0000000..372baa9
--- /dev/null
+++ b/drivers/clk/adi/clk-adi-pll.c
@@ -0,0 +1,93 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * (C) Copyright 2022 - Analog Devices, Inc.
+ *
+ * Written and/or maintained by Timesys Corporation
+ *
+ * Author: Greg Malysa <greg.malysa@timesys.com>
+ *
+ * Ported from Linux: Nathan Barrett-Morrison <nathan.morrison@timesys.com>
+ */
+
+#include <clk.h>
+#include <clk-uclass.h>
+#include <asm/io.h>
+#include <dm/device.h>
+#include <linux/compiler_types.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+
+#include "clk.h"
+
+#define ADI_CLK_PLL_GENERIC "adi_clk_pll_generic"
+
+struct clk_sc5xx_cgu_pll {
+ struct clk clk;
+ void __iomem *base;
+ u32 mask;
+ u32 max;
+ u32 m_offset;
+ u8 shift;
+ bool half_m;
+};
+
+#define to_clk_sc5xx_cgu_pll(_clk) container_of(_clk, struct clk_sc5xx_cgu_pll, clk)
+
+static unsigned long sc5xx_cgu_pll_get_rate(struct clk *clk)
+{
+ struct clk_sc5xx_cgu_pll *pll = to_clk_sc5xx_cgu_pll(dev_get_clk_ptr(clk->dev));
+ unsigned long parent_rate = clk_get_parent_rate(clk);
+
+ u32 reg = readl(pll->base);
+ u32 m = ((reg & pll->mask) >> pll->shift) + pll->m_offset;
+
+ if (m == 0)
+ m = pll->max;
+
+ if (pll->half_m)
+ return parent_rate * m * 2;
+ return parent_rate * m;
+}
+
+static const struct clk_ops clk_sc5xx_cgu_pll_ops = {
+ .get_rate = sc5xx_cgu_pll_get_rate,
+};
+
+struct clk *sc5xx_cgu_pll(const char *name, const char *parent_name,
+ void __iomem *base, u8 shift, u8 width, u32 m_offset,
+ bool half_m)
+{
+ struct clk_sc5xx_cgu_pll *pll;
+ struct clk *clk;
+ int ret;
+ char *drv_name = ADI_CLK_PLL_GENERIC;
+
+ pll = kzalloc(sizeof(*pll), GFP_KERNEL);
+ if (!pll)
+ return ERR_PTR(-ENOMEM);
+
+ pll->base = base;
+ pll->shift = shift;
+ pll->mask = GENMASK(width - 1, 0) << shift;
+ pll->max = pll->mask + 1;
+ pll->m_offset = m_offset;
+ pll->half_m = half_m;
+
+ clk = &pll->clk;
+
+ ret = clk_register(clk, drv_name, name, parent_name);
+ if (ret) {
+ pr_err("Failed to register %s in %s: %d\n", name, __func__, ret);
+ kfree(pll);
+ return ERR_PTR(ret);
+ }
+
+ return clk;
+}
+
+U_BOOT_DRIVER(clk_adi_pll_generic) = {
+ .name = ADI_CLK_PLL_GENERIC,
+ .id = UCLASS_CLK,
+ .ops = &clk_sc5xx_cgu_pll_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/drivers/clk/adi/clk-adi-sc57x.c b/drivers/clk/adi/clk-adi-sc57x.c
new file mode 100644
index 0000000..b17563f
--- /dev/null
+++ b/drivers/clk/adi/clk-adi-sc57x.c
@@ -0,0 +1,206 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * (C) Copyright 2022 - Analog Devices, Inc.
+ *
+ * Written and/or maintained by Timesys Corporation
+ *
+ * Author: Greg Malysa <greg.malysa@timesys.com>
+ *
+ * Ported from Linux: Nathan Barrett-Morrison <nathan.morrison@timesys.com>
+ */
+
+#include <clk.h>
+#include <clk-uclass.h>
+#include <dm.h>
+#include <dt-bindings/clock/adi-sc5xx-clock.h>
+#include <linux/compiler_types.h>
+#include <linux/clk-provider.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/printk.h>
+#include <linux/types.h>
+
+#include "clk.h"
+
+static const char * const cgu1_in_sels[] = {"sys_clkin0", "sys_clkin1"};
+static const char * const sharc0_sels[] = {"cclk0_0", "sysclk_0", "dummy", "dummy"};
+static const char * const sharc1_sels[] = {"cclk0_0", "sysclk_0", "dummy", "dummy"};
+static const char * const arm_sels[] = {"cclk1_0", "sysclk_0", "dummy", "dummy"};
+static const char * const cdu_ddr_sels[] = {"dclk_0", "dclk_1", "dummy", "dummy"};
+static const char * const can_sels[] = {"oclk_0", "oclk_1", "dclk_1", "oclk_0_half"};
+static const char * const spdif_sels[] = {"oclk_0", "oclk_1", "dclk_1", "dclk_0"};
+static const char * const gige_sels[] = {"sclk1_0", "sclk1_1", "cclk0_1", "oclk_0"};
+static const char * const sdio_sels[] = {"oclk_0_half", "cclk1_1_half", "cclk1_1",
+ "dclk_1"};
+
+static int sc57x_clock_probe(struct udevice *dev)
+{
+ void __iomem *cgu0;
+ void __iomem *cgu1;
+ void __iomem *cdu;
+ int ret;
+ struct resource res;
+
+ struct clk *clks[ADSP_SC57X_CLK_END];
+ struct clk dummy, clkin0, clkin1;
+
+ ret = dev_read_resource_byname(dev, "cgu0", &res);
+ if (ret)
+ return ret;
+ cgu0 = devm_ioremap(dev, res.start, resource_size(&res));
+
+ ret = dev_read_resource_byname(dev, "cgu1", &res);
+ if (ret)
+ return ret;
+ cgu1 = devm_ioremap(dev, res.start, resource_size(&res));
+
+ ret = dev_read_resource_byname(dev, "cdu", &res);
+ if (ret)
+ return ret;
+ cdu = devm_ioremap(dev, res.start, resource_size(&res));
+
+ // Input clock configuration
+ clk_get_by_name(dev, "dummy", &dummy);
+ clk_get_by_name(dev, "sys_clkin0", &clkin0);
+ clk_get_by_name(dev, "sys_clkin1", &clkin1);
+
+ clks[ADSP_SC57X_CLK_DUMMY] = &dummy;
+ clks[ADSP_SC57X_CLK_SYS_CLKIN0] = &clkin0;
+ clks[ADSP_SC57X_CLK_SYS_CLKIN1] = &clkin1;
+
+ clks[ADSP_SC57X_CLK_CGU1_IN] = clk_register_mux(NULL, "cgu1_in_sel", cgu1_in_sels,
+ 2, CLK_SET_RATE_PARENT,
+ cdu + CDU_CLKINSEL, 0, 1, 0);
+
+ // CGU configuration and internal clocks
+ clks[ADSP_SC57X_CLK_CGU0_PLL_IN] = clk_register_divider(NULL, "cgu0_df",
+ "sys_clkin0",
+ CLK_SET_RATE_PARENT,
+ cgu0 + CGU_CTL, 0, 1, 0);
+ clks[ADSP_SC57X_CLK_CGU1_PLL_IN] = clk_register_divider(NULL, "cgu1_df",
+ "cgu1_in_sel",
+ CLK_SET_RATE_PARENT,
+ cgu1 + CGU_CTL, 0, 1, 0);
+
+ // VCO output == PLL output
+ clks[ADSP_SC57X_CLK_CGU0_PLLCLK] = sc5xx_cgu_pll("cgu0_pllclk", "cgu0_df",
+ cgu0 + CGU_CTL, CGU_MSEL_SHIFT,
+ CGU_MSEL_WIDTH, 0, false);
+ clks[ADSP_SC57X_CLK_CGU1_PLLCLK] = sc5xx_cgu_pll("cgu1_pllclk", "cgu1_df",
+ cgu1 + CGU_CTL, CGU_MSEL_SHIFT,
+ CGU_MSEL_WIDTH, 0, false);
+
+ // Dividers from pll output
+ clks[ADSP_SC57X_CLK_CGU0_CDIV] = cgu_divider("cgu0_cdiv", "cgu0_pllclk",
+ cgu0 + CGU_DIV, 0, 5, 0);
+ clks[ADSP_SC57X_CLK_CGU0_SYSCLK] = cgu_divider("sysclk_0", "cgu0_pllclk",
+ cgu0 + CGU_DIV, 8, 5, 0);
+ clks[ADSP_SC57X_CLK_CGU0_DDIV] = cgu_divider("cgu0_ddiv", "cgu0_pllclk",
+ cgu0 + CGU_DIV, 16, 5, 0);
+ clks[ADSP_SC57X_CLK_CGU0_ODIV] = cgu_divider("cgu0_odiv", "cgu0_pllclk",
+ cgu0 + CGU_DIV, 22, 7, 0);
+ clks[ADSP_SC57X_CLK_CGU0_S0SELDIV] = cgu_divider("cgu0_s0seldiv", "sysclk_0",
+ cgu0 + CGU_DIV, 5, 3, 0);
+ clks[ADSP_SC57X_CLK_CGU0_S1SELDIV] = cgu_divider("cgu0_s1seldiv", "sysclk_0",
+ cgu0 + CGU_DIV, 13, 3, 0);
+
+ clks[ADSP_SC57X_CLK_CGU1_CDIV] = cgu_divider("cgu1_cdiv", "cgu1_pllclk",
+ cgu1 + CGU_DIV, 0, 5, 0);
+ clks[ADSP_SC57X_CLK_CGU1_SYSCLK] = cgu_divider("sysclk_1", "cgu1_pllclk",
+ cgu1 + CGU_DIV, 8, 5, 0);
+ clks[ADSP_SC57X_CLK_CGU1_DDIV] = cgu_divider("cgu1_ddiv", "cgu1_pllclk",
+ cgu1 + CGU_DIV, 16, 5, 0);
+ clks[ADSP_SC57X_CLK_CGU1_ODIV] = cgu_divider("cgu1_odiv", "cgu1_pllclk",
+ cgu1 + CGU_DIV, 22, 7, 0);
+ clks[ADSP_SC57X_CLK_CGU1_S0SELDIV] = cgu_divider("cgu1_s0seldiv",
+ "sysclk_1", cgu1 + CGU_DIV, 5,
+ 3, 0);
+ clks[ADSP_SC57X_CLK_CGU1_S1SELDIV] = cgu_divider("cgu1_s1seldiv",
+ "sysclk_1", cgu1 + CGU_DIV, 13,
+ 3, 0);
+
+ // Gates to enable CGU outputs
+ clks[ADSP_SC57X_CLK_CGU0_CCLK0] = cgu_gate("cclk0_0", "cgu0_cdiv",
+ cgu0 + CGU_CCBF_DIS, 0);
+ clks[ADSP_SC57X_CLK_CGU0_CCLK1] = cgu_gate("cclk1_0", "cgu0_cdiv",
+ cgu1 + CGU_CCBF_DIS, 1);
+ clks[ADSP_SC57X_CLK_CGU0_OCLK] = cgu_gate("oclk_0", "cgu0_odiv",
+ cgu0 + CGU_SCBF_DIS, 3);
+ clks[ADSP_SC57X_CLK_CGU0_DCLK] = cgu_gate("dclk_0", "cgu0_ddiv",
+ cgu0 + CGU_SCBF_DIS, 2);
+ clks[ADSP_SC57X_CLK_CGU0_SCLK1] = cgu_gate("sclk1_0", "cgu0_s1seldiv",
+ cgu0 + CGU_SCBF_DIS, 1);
+ clks[ADSP_SC57X_CLK_CGU0_SCLK0] = cgu_gate("sclk0_0", "cgu0_s0seldiv",
+ cgu0 + CGU_SCBF_DIS, 0);
+
+ clks[ADSP_SC57X_CLK_CGU1_CCLK0] = cgu_gate("cclk0_1", "cgu1_cdiv",
+ cgu1 + CGU_CCBF_DIS, 0);
+ clks[ADSP_SC57X_CLK_CGU1_CCLK1] = cgu_gate("cclk1_1", "cgu1_cdiv",
+ cgu1 + CGU_CCBF_DIS, 1);
+ clks[ADSP_SC57X_CLK_CGU1_OCLK] = cgu_gate("oclk_1", "cgu1_odiv",
+ cgu1 + CGU_SCBF_DIS, 3);
+ clks[ADSP_SC57X_CLK_CGU1_DCLK] = cgu_gate("dclk_1", "cgu1_ddiv",
+ cgu1 + CGU_SCBF_DIS, 2);
+ clks[ADSP_SC57X_CLK_CGU1_SCLK1] = cgu_gate("sclk1_1", "cgu1_s1seldiv",
+ cgu1 + CGU_SCBF_DIS, 1);
+ clks[ADSP_SC57X_CLK_CGU1_SCLK0] = cgu_gate("sclk0_1", "cgu1_s0seldiv",
+ cgu1 + CGU_SCBF_DIS, 0);
+
+ // Extra half rate clocks generated in the CDU
+ clks[ADSP_SC57X_CLK_OCLK0_HALF] = clk_register_fixed_factor(NULL, "oclk_0_half",
+ "oclk_0",
+ CLK_SET_RATE_PARENT,
+ 1, 2);
+ clks[ADSP_SC57X_CLK_CCLK1_1_HALF] = clk_register_fixed_factor(NULL,
+ "cclk1_1_half",
+ "cclk1_1",
+ CLK_SET_RATE_PARENT,
+ 1, 2);
+
+ // CDU output muxes
+ clks[ADSP_SC57X_CLK_SHARC0_SEL] = cdu_mux("sharc0_sel", cdu + CDU_CFG0,
+ sharc0_sels);
+ clks[ADSP_SC57X_CLK_SHARC1_SEL] = cdu_mux("sharc1_sel", cdu + CDU_CFG1,
+ sharc1_sels);
+ clks[ADSP_SC57X_CLK_ARM_SEL] = cdu_mux("arm_sel", cdu + CDU_CFG2, arm_sels);
+ clks[ADSP_SC57X_CLK_CDU_DDR_SEL] = cdu_mux("cdu_ddr_sel", cdu + CDU_CFG3,
+ cdu_ddr_sels);
+ clks[ADSP_SC57X_CLK_CAN_SEL] = cdu_mux("can_sel", cdu + CDU_CFG4, can_sels);
+ clks[ADSP_SC57X_CLK_SPDIF_SEL] = cdu_mux("spdif_sel", cdu + CDU_CFG5, spdif_sels);
+ clks[ADSP_SC57X_CLK_GIGE_SEL] = cdu_mux("gige_sel", cdu + CDU_CFG7, gige_sels);
+ clks[ADSP_SC57X_CLK_SDIO_SEL] = cdu_mux("sdio_sel", cdu + CDU_CFG9, sdio_sels);
+
+ // CDU output enable gates
+ clks[ADSP_SC57X_CLK_SHARC0] = cdu_gate("sharc0", "sharc0_sel", cdu + CDU_CFG0,
+ CLK_IS_CRITICAL);
+ clks[ADSP_SC57X_CLK_SHARC1] = cdu_gate("sharc1", "sharc1_sel", cdu + CDU_CFG1,
+ CLK_IS_CRITICAL);
+ clks[ADSP_SC57X_CLK_ARM] = cdu_gate("arm", "arm_sel", cdu + CDU_CFG2,
+ CLK_IS_CRITICAL);
+ clks[ADSP_SC57X_CLK_CDU_DDR] = cdu_gate("cdu_ddr", "cdu_ddr_sel", cdu + CDU_CFG3,
+ CLK_IS_CRITICAL);
+ clks[ADSP_SC57X_CLK_CAN] = cdu_gate("can", "can_sel", cdu + CDU_CFG4, 0);
+ clks[ADSP_SC57X_CLK_SPDIF] = cdu_gate("spdif", "spdif_sel", cdu + CDU_CFG5, 0);
+ clks[ADSP_SC57X_CLK_GIGE] = cdu_gate("gige", "gige_sel", cdu + CDU_CFG7, 0);
+ clks[ADSP_SC57X_CLK_SDIO] = cdu_gate("sdio", "sdio_sel", cdu + CDU_CFG9, 0);
+
+ ret = cdu_check_clocks(clks, ARRAY_SIZE(clks));
+ if (ret)
+ pr_err("CDU error detected\n");
+
+ return ret;
+}
+
+static const struct udevice_id adi_sc57x_clk_ids[] = {
+ { .compatible = "adi,sc57x-clocks" },
+ { },
+};
+
+U_BOOT_DRIVER(adi_sc57x_clk) = {
+ .name = "clk_adi_sc57x",
+ .id = UCLASS_CLK,
+ .of_match = adi_sc57x_clk_ids,
+ .ops = &adi_clk_ops,
+ .probe = sc57x_clock_probe,
+ .flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/drivers/clk/adi/clk-adi-sc58x.c b/drivers/clk/adi/clk-adi-sc58x.c
new file mode 100644
index 0000000..05a0fed
--- /dev/null
+++ b/drivers/clk/adi/clk-adi-sc58x.c
@@ -0,0 +1,222 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * (C) Copyright 2022 - Analog Devices, Inc.
+ *
+ * Written and/or maintained by Timesys Corporation
+ *
+ * Author: Greg Malysa <greg.malysa@timesys.com>
+ *
+ * Ported from Linux: Nathan Barrett-Morrison <nathan.morrison@timesys.com>
+ */
+
+#include <clk.h>
+#include <clk-uclass.h>
+#include <dm.h>
+#include <dt-bindings/clock/adi-sc5xx-clock.h>
+#include <linux/compiler_types.h>
+#include <linux/clk-provider.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/printk.h>
+#include <linux/types.h>
+
+#include "clk.h"
+
+static const char * const cgu1_in_sels[] = {"sys_clkin0", "sys_clkin1"};
+static const char * const sharc0_sels[] = {"cclk0_0", "sysclk_0", "dummy", "dummy"};
+static const char * const sharc1_sels[] = {"cclk0_0", "sysclk_0", "dummy", "dummy"};
+static const char * const arm_sels[] = {"cclk1_0", "sysclk_0", "dummy", "dummy"};
+static const char * const cdu_ddr_sels[] = {"dclk_0", "dclk_1", "dummy", "dummy"};
+static const char * const can_sels[] = {"oclk_0", "oclk_1", "dclk_1", "dummy"};
+static const char * const spdif_sels[] = {"oclk_0", "oclk_1", "dclk_1", "dclk_0"};
+static const char * const reserved_sels[] = {"sclk0_0", "oclk_0", "dummy", "dummy"};
+static const char * const gige_sels[] = {"sclk0_0", "sclk1_1", "cclk0_1", "oclk_0"};
+static const char * const lp_sels[] = {"sclk0_0", "sclk0_1", "cclk1_1", "dclk_1"};
+static const char * const sdio_sels[] = {"oclk_0_half", "cclk1_1_half", "cclk1_1",
+ "dclk_1"};
+
+static int sc58x_clock_probe(struct udevice *dev)
+{
+ void __iomem *cgu0;
+ void __iomem *cgu1;
+ void __iomem *cdu;
+ int ret;
+ struct resource res;
+
+ struct clk *clks[ADSP_SC58X_CLK_END];
+ struct clk dummy, clkin0, clkin1;
+
+ ret = dev_read_resource_byname(dev, "cgu0", &res);
+ if (ret)
+ return ret;
+ cgu0 = devm_ioremap(dev, res.start, resource_size(&res));
+
+ ret = dev_read_resource_byname(dev, "cgu1", &res);
+ if (ret)
+ return ret;
+ cgu1 = devm_ioremap(dev, res.start, resource_size(&res));
+
+ ret = dev_read_resource_byname(dev, "cdu", &res);
+ if (ret)
+ return ret;
+ cdu = devm_ioremap(dev, res.start, resource_size(&res));
+
+ // Input clock configuration
+ clk_get_by_name(dev, "dummy", &dummy);
+ clk_get_by_name(dev, "sys_clkin0", &clkin0);
+ clk_get_by_name(dev, "sys_clkin1", &clkin1);
+
+ clks[ADSP_SC58X_CLK_DUMMY] = &dummy;
+ clks[ADSP_SC58X_CLK_SYS_CLKIN0] = &clkin0;
+ clks[ADSP_SC58X_CLK_SYS_CLKIN1] = &clkin1;
+
+ clks[ADSP_SC58X_CLK_CGU1_IN] = clk_register_mux(NULL, "cgu1_in_sel", cgu1_in_sels,
+ 2, CLK_SET_RATE_PARENT,
+ cdu + CDU_CLKINSEL, 0, 1, 0);
+
+ // CGU configuration and internal clocks
+ clks[ADSP_SC58X_CLK_CGU0_PLL_IN] = clk_register_divider(NULL, "cgu0_df",
+ "sys_clkin0",
+ CLK_SET_RATE_PARENT,
+ cgu0 + CGU_CTL, 0, 1, 0);
+ clks[ADSP_SC58X_CLK_CGU1_PLL_IN] = clk_register_divider(NULL, "cgu1_df",
+ "cgu1_in_sel",
+ CLK_SET_RATE_PARENT,
+ cgu1 + CGU_CTL, 0, 1, 0);
+
+ // VCO output inside PLL
+ clks[ADSP_SC58X_CLK_CGU0_VCO_OUT] = sc5xx_cgu_pll("cgu0_vco", "cgu0_df",
+ cgu0 + CGU_CTL, CGU_MSEL_SHIFT,
+ CGU_MSEL_WIDTH, 0, false);
+ clks[ADSP_SC58X_CLK_CGU1_VCO_OUT] = sc5xx_cgu_pll("cgu1_vco", "cgu1_df",
+ cgu1 + CGU_CTL, CGU_MSEL_SHIFT,
+ CGU_MSEL_WIDTH, 0, false);
+
+ // Final PLL output
+ clks[ADSP_SC58X_CLK_CGU0_PLLCLK] = clk_register_fixed_factor(NULL, "cgu0_pllclk",
+ "cgu0_vco",
+ CLK_SET_RATE_PARENT,
+ 1, 1);
+ clks[ADSP_SC58X_CLK_CGU1_PLLCLK] = clk_register_fixed_factor(NULL, "cgu1_pllclk",
+ "cgu1_vco",
+ CLK_SET_RATE_PARENT,
+ 1, 1);
+
+ // Dividers from pll output
+ clks[ADSP_SC58X_CLK_CGU0_CDIV] = cgu_divider("cgu0_cdiv", "cgu0_pllclk",
+ cgu0 + CGU_DIV, 0, 5, 0);
+ clks[ADSP_SC58X_CLK_CGU0_SYSCLK] = cgu_divider("sysclk_0", "cgu0_pllclk",
+ cgu0 + CGU_DIV, 8, 5, 0);
+ clks[ADSP_SC58X_CLK_CGU0_DDIV] = cgu_divider("cgu0_ddiv", "cgu0_pllclk",
+ cgu0 + CGU_DIV, 16, 5, 0);
+ clks[ADSP_SC58X_CLK_CGU0_ODIV] = cgu_divider("cgu0_odiv", "cgu0_pllclk",
+ cgu0 + CGU_DIV, 22, 7, 0);
+ clks[ADSP_SC58X_CLK_CGU0_S0SELDIV] = cgu_divider("cgu0_s0seldiv", "sysclk_0",
+ cgu0 + CGU_DIV, 5, 3, 0);
+ clks[ADSP_SC58X_CLK_CGU0_S1SELDIV] = cgu_divider("cgu0_s1seldiv", "sysclk_0",
+ cgu0 + CGU_DIV, 13, 3, 0);
+
+ clks[ADSP_SC58X_CLK_CGU1_CDIV] = cgu_divider("cgu1_cdiv", "cgu1_pllclk",
+ cgu1 + CGU_DIV, 0, 5, 0);
+ clks[ADSP_SC58X_CLK_CGU1_SYSCLK] = cgu_divider("sysclk_1", "cgu1_pllclk",
+ cgu1 + CGU_DIV, 8, 5, 0);
+ clks[ADSP_SC58X_CLK_CGU1_DDIV] = cgu_divider("cgu1_ddiv", "cgu1_pllclk",
+ cgu1 + CGU_DIV, 16, 5, 0);
+ clks[ADSP_SC58X_CLK_CGU1_ODIV] = cgu_divider("cgu1_odiv", "cgu1_pllclk",
+ cgu1 + CGU_DIV, 22, 7, 0);
+ clks[ADSP_SC58X_CLK_CGU1_S0SELDIV] = cgu_divider("cgu1_s0seldiv", "sysclk_1",
+ cgu1 + CGU_DIV, 5, 3, 0);
+ clks[ADSP_SC58X_CLK_CGU1_S1SELDIV] = cgu_divider("cgu1_s1seldiv", "sysclk_1",
+ cgu1 + CGU_DIV, 13, 3, 0);
+
+ // Gates to enable CGU outputs
+ clks[ADSP_SC58X_CLK_CGU0_CCLK0] = cgu_gate("cclk0_0", "cgu0_cdiv",
+ cgu0 + CGU_CCBF_DIS, 0);
+ clks[ADSP_SC58X_CLK_CGU0_CCLK1] = cgu_gate("cclk1_0", "cgu0_cdiv",
+ cgu1 + CGU_CCBF_DIS, 1);
+ clks[ADSP_SC58X_CLK_CGU0_OCLK] = cgu_gate("oclk_0", "cgu0_odiv",
+ cgu0 + CGU_SCBF_DIS, 3);
+ clks[ADSP_SC58X_CLK_CGU0_DCLK] = cgu_gate("dclk_0", "cgu0_ddiv",
+ cgu0 + CGU_SCBF_DIS, 2);
+ clks[ADSP_SC58X_CLK_CGU0_SCLK1] = cgu_gate("sclk1_0", "cgu0_s1seldiv",
+ cgu0 + CGU_SCBF_DIS, 1);
+ clks[ADSP_SC58X_CLK_CGU0_SCLK0] = cgu_gate("sclk0_0", "cgu0_s0seldiv",
+ cgu0 + CGU_SCBF_DIS, 0);
+
+ clks[ADSP_SC58X_CLK_CGU1_CCLK0] = cgu_gate("cclk0_1", "cgu1_cdiv",
+ cgu1 + CGU_CCBF_DIS, 0);
+ clks[ADSP_SC58X_CLK_CGU1_CCLK1] = cgu_gate("cclk1_1", "cgu1_cdiv",
+ cgu1 + CGU_CCBF_DIS, 1);
+ clks[ADSP_SC58X_CLK_CGU1_OCLK] = cgu_gate("oclk_1", "cgu1_odiv",
+ cgu1 + CGU_SCBF_DIS, 3);
+ clks[ADSP_SC58X_CLK_CGU1_DCLK] = cgu_gate("dclk_1", "cgu1_ddiv",
+ cgu1 + CGU_SCBF_DIS, 2);
+ clks[ADSP_SC58X_CLK_CGU1_SCLK1] = cgu_gate("sclk1_1", "cgu1_s1seldiv",
+ cgu1 + CGU_SCBF_DIS, 1);
+ clks[ADSP_SC58X_CLK_CGU1_SCLK0] = cgu_gate("sclk0_1", "cgu1_s0seldiv",
+ cgu1 + CGU_SCBF_DIS, 0);
+
+ // Extra half rate clocks generated in the CDU
+ clks[ADSP_SC58X_CLK_OCLK0_HALF] = clk_register_fixed_factor(NULL, "oclk_0_half",
+ "oclk_0",
+ CLK_SET_RATE_PARENT,
+ 1, 2);
+ clks[ADSP_SC58X_CLK_CCLK1_1_HALF] = clk_register_fixed_factor(NULL,
+ "cclk1_1_half",
+ "cclk1_1",
+ CLK_SET_RATE_PARENT,
+ 1, 2);
+
+ // CDU output muxes
+ clks[ADSP_SC58X_CLK_SHARC0_SEL] = cdu_mux("sharc0_sel", cdu + CDU_CFG0,
+ sharc0_sels);
+ clks[ADSP_SC58X_CLK_SHARC1_SEL] = cdu_mux("sharc1_sel", cdu + CDU_CFG1,
+ sharc1_sels);
+ clks[ADSP_SC58X_CLK_ARM_SEL] = cdu_mux("arm_sel", cdu + CDU_CFG2, arm_sels);
+ clks[ADSP_SC58X_CLK_CDU_DDR_SEL] = cdu_mux("cdu_ddr_sel", cdu + CDU_CFG3,
+ cdu_ddr_sels);
+ clks[ADSP_SC58X_CLK_CAN_SEL] = cdu_mux("can_sel", cdu + CDU_CFG4, can_sels);
+ clks[ADSP_SC58X_CLK_SPDIF_SEL] = cdu_mux("spdif_sel", cdu + CDU_CFG5, spdif_sels);
+ clks[ADSP_SC58X_CLK_RESERVED_SEL] = cdu_mux("reserved_sel", cdu + CDU_CFG6,
+ reserved_sels);
+ clks[ADSP_SC58X_CLK_GIGE_SEL] = cdu_mux("gige_sel", cdu + CDU_CFG7, gige_sels);
+ clks[ADSP_SC58X_CLK_LP_SEL] = cdu_mux("lp_sel", cdu + CDU_CFG8, lp_sels);
+ clks[ADSP_SC58X_CLK_SDIO_SEL] = cdu_mux("sdio_sel", cdu + CDU_CFG9, sdio_sels);
+
+ // CDU output enable gates
+ clks[ADSP_SC58X_CLK_SHARC0] = cdu_gate("sharc0", "sharc0_sel", cdu + CDU_CFG0,
+ CLK_IS_CRITICAL);
+ clks[ADSP_SC58X_CLK_SHARC1] = cdu_gate("sharc1", "sharc1_sel", cdu + CDU_CFG1,
+ CLK_IS_CRITICAL);
+ clks[ADSP_SC58X_CLK_ARM] = cdu_gate("arm", "arm_sel", cdu + CDU_CFG2,
+ CLK_IS_CRITICAL);
+ clks[ADSP_SC58X_CLK_CDU_DDR] = cdu_gate("cdu_ddr", "cdu_ddr_sel", cdu + CDU_CFG3,
+ CLK_IS_CRITICAL);
+ clks[ADSP_SC58X_CLK_CAN] = cdu_gate("can", "can_sel", cdu + CDU_CFG4, 0);
+ clks[ADSP_SC58X_CLK_SPDIF] = cdu_gate("spdif", "spdif_sel", cdu + CDU_CFG5, 0);
+ clks[ADSP_SC58X_CLK_RESERVED] = cdu_gate("reserved", "reserved_sel",
+ cdu + CDU_CFG6, 0);
+ clks[ADSP_SC58X_CLK_GIGE] = cdu_gate("gige", "gige_sel", cdu + CDU_CFG7, 0);
+ clks[ADSP_SC58X_CLK_LP] = cdu_gate("lp", "lp_sel", cdu + CDU_CFG8, 0);
+ clks[ADSP_SC58X_CLK_SDIO] = cdu_gate("sdio", "sdio_sel", cdu + CDU_CFG9, 0);
+
+ ret = cdu_check_clocks(clks, ARRAY_SIZE(clks));
+ if (ret)
+ pr_err("CDU error detected\n");
+
+ return ret;
+}
+
+static const struct udevice_id adi_sc58x_clk_ids[] = {
+ { .compatible = "adi,sc58x-clocks" },
+ { },
+};
+
+U_BOOT_DRIVER(adi_sc58x_clk) = {
+ .name = "clk_adi_sc58x",
+ .id = UCLASS_CLK,
+ .of_match = adi_sc58x_clk_ids,
+ .ops = &adi_clk_ops,
+ .probe = sc58x_clock_probe,
+ .flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/drivers/clk/adi/clk-adi-sc594.c b/drivers/clk/adi/clk-adi-sc594.c
new file mode 100644
index 0000000..c80bbf9
--- /dev/null
+++ b/drivers/clk/adi/clk-adi-sc594.c
@@ -0,0 +1,231 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * (C) Copyright 2022 - Analog Devices, Inc.
+ *
+ * Written and/or maintained by Timesys Corporation
+ *
+ * Author: Greg Malysa <greg.malysa@timesys.com>
+ *
+ * Ported from Linux: Nathan Barrett-Morrison <nathan.morrison@timesys.com>
+ */
+
+#include <clk.h>
+#include <clk-uclass.h>
+#include <dm.h>
+#include <dt-bindings/clock/adi-sc5xx-clock.h>
+#include <linux/compiler_types.h>
+#include <linux/clk-provider.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/printk.h>
+#include <linux/types.h>
+
+#include "clk.h"
+
+static const char * const cgu1_in_sels[] = {"sys_clkin0", "sys_clkin1"};
+static const char * const cgu0_s1sels[] = {"cgu0_s1seldiv", "cgu0_s1selexdiv"};
+static const char * const cgu1_s1sels[] = {"cgu1_s1seldiv", "cgu1_s1selexdiv"};
+static const char * const sharc0_sels[] = {"cclk0_0", "dummy", "dummy", "dummy"};
+static const char * const sharc1_sels[] = {"cclk0_0", "dummy", "dummy", "dummy"};
+static const char * const arm_sels[] = {"cclk1_0", "dummy", "dummy", "dummy"};
+static const char * const cdu_ddr_sels[] = {"dclk_0", "dclk_1", "dummy", "dummy"};
+static const char * const can_sels[] = {"oclk_0", "oclk_1", "dummy", "dummy"};
+static const char * const spdif_sels[] = {"sclk1_0", "dummy", "dummy", "dummy"};
+static const char * const spi_sels[] = {"sclk0_0", "oclk_0", "dummy", "dummy"};
+static const char * const gige_sels[] = {"sclk0_0", "sclk0_1", "cclk0_1", "dummy"};
+static const char * const lp_sels[] = {"oclk_0", "sclk0_0", "cclk0_1", "dummy"};
+static const char * const lpddr_sels[] = {"oclk_0", "dclk_0", "sysclkin_1", "dummy"};
+static const char * const ospi_sels[] = {"sysclk_0", "sclk0_0", "sclk1_1", "dummy"};
+static const char * const trace_sels[] = {"sclk0_0", "dummy", "dummy", "dummy"};
+
+static int sc594_clock_probe(struct udevice *dev)
+{
+ void __iomem *cgu0;
+ void __iomem *cgu1;
+ void __iomem *cdu;
+ int ret;
+ struct resource res;
+
+ struct clk *clks[ADSP_SC594_CLK_END];
+ struct clk dummy, clkin0, clkin1;
+
+ ret = dev_read_resource_byname(dev, "cgu0", &res);
+ if (ret)
+ return ret;
+ cgu0 = devm_ioremap(dev, res.start, resource_size(&res));
+
+ ret = dev_read_resource_byname(dev, "cgu1", &res);
+ if (ret)
+ return ret;
+ cgu1 = devm_ioremap(dev, res.start, resource_size(&res));
+
+ ret = dev_read_resource_byname(dev, "cdu", &res);
+ if (ret)
+ return ret;
+ cdu = devm_ioremap(dev, res.start, resource_size(&res));
+
+ // Input clock configuration
+ clk_get_by_name(dev, "dummy", &dummy);
+ clk_get_by_name(dev, "sys_clkin0", &clkin0);
+ clk_get_by_name(dev, "sys_clkin1", &clkin1);
+
+ clks[ADSP_SC594_CLK_DUMMY] = &dummy;
+ clks[ADSP_SC594_CLK_SYS_CLKIN0] = &clkin0;
+ clks[ADSP_SC594_CLK_SYS_CLKIN1] = &clkin1;
+ clks[ADSP_SC594_CLK_CGU1_IN] = clk_register_mux(NULL, "cgu1_in_sel", cgu1_in_sels,
+ 2, CLK_SET_RATE_PARENT,
+ cdu + CDU_CLKINSEL, 0, 1, 0);
+
+ // CGU configuration and internal clocks
+ clks[ADSP_SC594_CLK_CGU0_PLL_IN] = clk_register_divider(NULL, "cgu0_df",
+ "sys_clkin0",
+ CLK_SET_RATE_PARENT,
+ cgu0 + CGU_CTL, 0, 1, 0);
+ clks[ADSP_SC594_CLK_CGU1_PLL_IN] = clk_register_divider(NULL, "cgu1_df",
+ "cgu1_in_sel",
+ CLK_SET_RATE_PARENT,
+ cgu1 + CGU_CTL, 0, 1, 0);
+
+ // VCO output inside PLL
+ clks[ADSP_SC594_CLK_CGU0_VCO_OUT] = sc5xx_cgu_pll("cgu0_vco", "cgu0_df",
+ cgu0 + CGU_CTL, CGU_MSEL_SHIFT,
+ CGU_MSEL_WIDTH, 0, false);
+ clks[ADSP_SC594_CLK_CGU1_VCO_OUT] = sc5xx_cgu_pll("cgu1_vco", "cgu1_df",
+ cgu1 + CGU_CTL, CGU_MSEL_SHIFT,
+ CGU_MSEL_WIDTH, 0, false);
+
+ // Final PLL output
+ clks[ADSP_SC594_CLK_CGU0_PLLCLK] = clk_register_fixed_factor(NULL, "cgu0_pllclk",
+ "cgu0_vco",
+ CLK_SET_RATE_PARENT,
+ 1, 1);
+ clks[ADSP_SC594_CLK_CGU1_PLLCLK] = clk_register_fixed_factor(NULL, "cgu1_pllclk",
+ "cgu1_vco",
+ CLK_SET_RATE_PARENT,
+ 1, 1);
+
+ // Dividers from pll output
+ clks[ADSP_SC594_CLK_CGU0_CDIV] = cgu_divider("cgu0_cdiv", "cgu0_pllclk",
+ cgu0 + CGU_DIV, 0, 5, 0);
+ clks[ADSP_SC594_CLK_CGU0_SYSCLK] = cgu_divider("sysclk_0", "cgu0_pllclk",
+ cgu0 + CGU_DIV, 8, 5, 0);
+ clks[ADSP_SC594_CLK_CGU0_DDIV] = cgu_divider("cgu0_ddiv", "cgu0_pllclk",
+ cgu0 + CGU_DIV, 16, 5, 0);
+ clks[ADSP_SC594_CLK_CGU0_ODIV] = cgu_divider("cgu0_odiv", "cgu0_pllclk",
+ cgu0 + CGU_DIV, 22, 7, 0);
+ clks[ADSP_SC594_CLK_CGU0_S0SELDIV] = cgu_divider("cgu0_s0seldiv", "sysclk_0",
+ cgu0 + CGU_DIV, 5, 3, 0);
+ clks[ADSP_SC594_CLK_CGU0_S1SELDIV] = cgu_divider("cgu0_s1seldiv", "sysclk_0",
+ cgu0 + CGU_DIV, 13, 3, 0);
+ clks[ADSP_SC594_CLK_CGU0_S1SELEXDIV] = cgu_divider("cgu0_s1selexdiv",
+ "cgu0_pllclk",
+ cgu0 + CGU_DIVEX, 16, 8, 0);
+ clks[ADSP_SC594_CLK_CGU0_S1SEL] = clk_register_mux(NULL, "cgu0_sclk1sel",
+ cgu0_s1sels, 2,
+ CLK_SET_RATE_PARENT,
+ cgu0 + CGU_CTL, 17, 1, 0);
+
+ clks[ADSP_SC594_CLK_CGU1_CDIV] = cgu_divider("cgu1_cdiv", "cgu1_pllclk",
+ cgu1 + CGU_DIV, 0, 5, 0);
+ clks[ADSP_SC594_CLK_CGU1_SYSCLK] = cgu_divider("sysclk_1", "cgu1_pllclk",
+ cgu1 + CGU_DIV, 8, 5, 0);
+ clks[ADSP_SC594_CLK_CGU1_DDIV] = cgu_divider("cgu1_ddiv", "cgu1_pllclk",
+ cgu1 + CGU_DIV, 16, 5, 0);
+ clks[ADSP_SC594_CLK_CGU1_ODIV] = cgu_divider("cgu1_odiv", "cgu1_pllclk",
+ cgu1 + CGU_DIV, 22, 7, 0);
+ clks[ADSP_SC594_CLK_CGU1_S0SELDIV] = cgu_divider("cgu1_s0seldiv", "sysclk_1",
+ cgu1 + CGU_DIV, 5, 3, 0);
+ clks[ADSP_SC594_CLK_CGU1_S1SELDIV] = cgu_divider("cgu1_s1seldiv", "sysclk_1",
+ cgu1 + CGU_DIV, 13, 3, 0);
+ clks[ADSP_SC594_CLK_CGU1_S1SELEXDIV] = cgu_divider("cgu1_s1selexdiv",
+ "cgu1_pllclk",
+ cgu1 + CGU_DIVEX, 16, 8, 0);
+ clks[ADSP_SC594_CLK_CGU1_S1SEL] = clk_register_mux(NULL, "cgu1_sclk1sel",
+ cgu1_s1sels, 2,
+ CLK_SET_RATE_PARENT,
+ cgu1 + CGU_CTL, 17, 1, 0);
+
+ // Gates to enable CGU outputs
+ clks[ADSP_SC594_CLK_CGU0_CCLK0] = cgu_gate("cclk0_0", "cgu0_cdiv",
+ cgu0 + CGU_CCBF_DIS, 0);
+ clks[ADSP_SC594_CLK_CGU0_CCLK1] = cgu_gate("cclk1_0", "cgu0_cdiv",
+ cgu1 + CGU_CCBF_DIS, 1);
+ clks[ADSP_SC594_CLK_CGU0_OCLK] = cgu_gate("oclk_0", "cgu0_odiv",
+ cgu0 + CGU_SCBF_DIS, 3);
+ clks[ADSP_SC594_CLK_CGU0_DCLK] = cgu_gate("dclk_0", "cgu0_ddiv",
+ cgu0 + CGU_SCBF_DIS, 2);
+ clks[ADSP_SC594_CLK_CGU0_SCLK1] = cgu_gate("sclk1_0", "cgu0_sclk1sel",
+ cgu0 + CGU_SCBF_DIS, 1);
+ clks[ADSP_SC594_CLK_CGU0_SCLK0] = cgu_gate("sclk0_0", "cgu0_s0seldiv",
+ cgu0 + CGU_SCBF_DIS, 0);
+
+ clks[ADSP_SC594_CLK_CGU1_CCLK0] = cgu_gate("cclk0_1", "cgu1_cdiv",
+ cgu1 + CGU_CCBF_DIS, 0);
+ clks[ADSP_SC594_CLK_CGU1_CCLK1] = cgu_gate("cclk1_1", "cgu1_cdiv",
+ cgu1 + CGU_CCBF_DIS, 1);
+ clks[ADSP_SC594_CLK_CGU1_OCLK] = cgu_gate("oclk_1", "cgu1_odiv",
+ cgu1 + CGU_SCBF_DIS, 3);
+ clks[ADSP_SC594_CLK_CGU1_DCLK] = cgu_gate("dclk_1", "cgu1_ddiv",
+ cgu1 + CGU_SCBF_DIS, 2);
+ clks[ADSP_SC594_CLK_CGU1_SCLK1] = cgu_gate("sclk1_1", "cgu1_sclk1sel",
+ cgu1 + CGU_SCBF_DIS, 1);
+ clks[ADSP_SC594_CLK_CGU1_SCLK0] = cgu_gate("sclk0_1", "cgu1_s0seldiv",
+ cgu1 + CGU_SCBF_DIS, 0);
+
+ // CDU output muxes
+ clks[ADSP_SC594_CLK_SHARC0_SEL] = cdu_mux("sharc0_sel", cdu + CDU_CFG0,
+ sharc0_sels);
+ clks[ADSP_SC594_CLK_SHARC1_SEL] = cdu_mux("sharc1_sel", cdu + CDU_CFG1,
+ sharc1_sels);
+ clks[ADSP_SC594_CLK_ARM_SEL] = cdu_mux("arm_sel", cdu + CDU_CFG2, arm_sels);
+ clks[ADSP_SC594_CLK_CDU_DDR_SEL] = cdu_mux("cdu_ddr_sel", cdu + CDU_CFG3,
+ cdu_ddr_sels);
+ clks[ADSP_SC594_CLK_CAN_SEL] = cdu_mux("can_sel", cdu + CDU_CFG4, can_sels);
+ clks[ADSP_SC594_CLK_SPDIF_SEL] = cdu_mux("spdif_sel", cdu + CDU_CFG5, spdif_sels);
+ clks[ADSP_SC594_CLK_RESERVED_SEL] = cdu_mux("spi_sel", cdu + CDU_CFG6, spi_sels);
+ clks[ADSP_SC594_CLK_GIGE_SEL] = cdu_mux("gige_sel", cdu + CDU_CFG7, gige_sels);
+ clks[ADSP_SC594_CLK_LP_SEL] = cdu_mux("lp_sel", cdu + CDU_CFG8, lp_sels);
+ clks[ADSP_SC594_CLK_LPDDR_SEL] = cdu_mux("lpddr_sel", cdu + CDU_CFG9, lpddr_sels);
+ clks[ADSP_SC594_CLK_OSPI_SEL] = cdu_mux("ospi_sel", cdu + CDU_CFG10,
+ ospi_sels);
+ clks[ADSP_SC594_CLK_TRACE_SEL] = cdu_mux("trace_sel", cdu + CDU_CFG12,
+ trace_sels);
+
+ // CDU output enable gates
+ clks[ADSP_SC594_CLK_SHARC0] = cdu_gate("sharc0", "sharc0_sel",
+ cdu + CDU_CFG0, CLK_IS_CRITICAL);
+ clks[ADSP_SC594_CLK_SHARC1] = cdu_gate("sharc1", "sharc1_sel",
+ cdu + CDU_CFG1, CLK_IS_CRITICAL);
+ clks[ADSP_SC594_CLK_ARM] = cdu_gate("arm", "arm_sel", cdu + CDU_CFG2,
+ CLK_IS_CRITICAL);
+ clks[ADSP_SC594_CLK_CDU_DDR] = cdu_gate("cdu_ddr", "cdu_ddr_sel",
+ cdu + CDU_CFG3, CLK_IS_CRITICAL);
+ clks[ADSP_SC594_CLK_CAN] = cdu_gate("can", "can_sel", cdu + CDU_CFG4, 0);
+ clks[ADSP_SC594_CLK_SPDIF] = cdu_gate("spdif", "spdif_sel", cdu + CDU_CFG5, 0);
+ clks[ADSP_SC594_CLK_SPI] = cdu_gate("spi", "spi_sel", cdu + CDU_CFG6, 0);
+ clks[ADSP_SC594_CLK_GIGE] = cdu_gate("gige", "gige_sel", cdu + CDU_CFG7, 0);
+ clks[ADSP_SC594_CLK_LP] = cdu_gate("lp", "lp_sel", cdu + CDU_CFG8, 0);
+ clks[ADSP_SC594_CLK_LPDDR] = cdu_gate("lpddr", "lpddr_sel", cdu + CDU_CFG9, 0);
+ clks[ADSP_SC594_CLK_OSPI] = cdu_gate("ospi", "ospi_sel", cdu + CDU_CFG10, 0);
+ clks[ADSP_SC594_CLK_TRACE] = cdu_gate("trace", "trace_sel", cdu + CDU_CFG12, 0);
+
+ ret = cdu_check_clocks(clks, ARRAY_SIZE(clks));
+ if (ret)
+ pr_err("CDU error detected\n");
+
+ return ret;
+}
+
+static const struct udevice_id adi_sc594_clk_ids[] = {
+ { .compatible = "adi,sc594-clocks" },
+ { },
+};
+
+U_BOOT_DRIVER(adi_sc594_clk) = {
+ .name = "clk_adi_sc594",
+ .id = UCLASS_CLK,
+ .of_match = adi_sc594_clk_ids,
+ .ops = &adi_clk_ops,
+ .probe = sc594_clock_probe,
+ .flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/drivers/clk/adi/clk-adi-sc598.c b/drivers/clk/adi/clk-adi-sc598.c
new file mode 100644
index 0000000..d4a16ac
--- /dev/null
+++ b/drivers/clk/adi/clk-adi-sc598.c
@@ -0,0 +1,308 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * (C) Copyright 2022 - Analog Devices, Inc.
+ *
+ * Written and/or maintained by Timesys Corporation
+ *
+ * Author: Greg Malysa <greg.malysa@timesys.com>
+ *
+ * Ported from Linux: Nathan Barrett-Morrison <nathan.morrison@timesys.com>
+ */
+
+#include <clk.h>
+#include <clk-uclass.h>
+#include <dm.h>
+#include <dt-bindings/clock/adi-sc5xx-clock.h>
+#include <linux/compiler_types.h>
+#include <linux/clk-provider.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/printk.h>
+#include <linux/types.h>
+
+#include "clk.h"
+
+static const char * const cgu1_in_sels[] = {"sys_clkin0", "sys_clkin1"};
+static const char * const cgu0_s1sels[] = {"cgu0_s1seldiv", "cgu0_s1selexdiv"};
+static const char * const cgu1_s0sels[] = {"cgu1_s0seldiv", "cgu1_s0selexdiv"};
+static const char * const cgu1_s1sels[] = {"cgu1_s1seldiv", "cgu1_s1selexdiv"};
+static const char * const sharc0_sels[] = {"cclk0_0", "dummy", "dummy", "dummy"};
+static const char * const sharc1_sels[] = {"cclk0_0", "dummy", "dummy", "dummy"};
+static const char * const arm_sels[] = {"dummy", "dummy", "cclk2_0", "cclk2_1"};
+static const char * const cdu_ddr_sels[] = {"dclk_0", "dclk_1", "dummy", "dummy"};
+static const char * const can_sels[] = {"dummy", "oclk_1", "dummy", "dummy"};
+static const char * const spdif_sels[] = {"sclk1_0", "dummy", "dummy", "dummy"};
+static const char * const spi_sels[] = {"sclk0_0", "oclk_0", "dummy", "dummy"};
+static const char * const gige_sels[] = {"sclk0_0", "sclk0_1", "dummy", "dummy"};
+static const char * const lp_sels[] = {"oclk_0", "sclk0_0", "cclk0_1", "dummy"};
+static const char * const lp_ddr_sels[] = {"oclk_0", "dclk_0", "sysclk_1", "dummy"};
+static const char * const ospi_refclk_sels[] = {"sysclk_0", "sclk0_0", "sclk1_1",
+ "dummy"};
+static const char * const trace_sels[] = {"sclk0_0", "dummy", "dummy", "dummy"};
+static const char * const emmc_sels[] = {"oclk_0", "sclk0_1", "dclk_0_half",
+ "dclk_1_half"};
+static const char * const emmc_timer_sels[] = {"dummy", "sclk1_1_half", "dummy",
+ "dummy"};
+static const char * const ddr_sels[] = {"cdu_ddr", "3pll_ddiv"};
+
+static int sc598_clock_probe(struct udevice *dev)
+{
+ void __iomem *cgu0;
+ void __iomem *cgu1;
+ void __iomem *cdu;
+ void __iomem *pll3;
+ int ret;
+ struct resource res;
+
+ struct clk *clks[ADSP_SC598_CLK_END];
+ struct clk dummy, clkin0, clkin1;
+
+ ret = dev_read_resource_byname(dev, "cgu0", &res);
+ if (ret)
+ return ret;
+ cgu0 = devm_ioremap(dev, res.start, resource_size(&res));
+
+ ret = dev_read_resource_byname(dev, "cgu1", &res);
+ if (ret)
+ return ret;
+ cgu1 = devm_ioremap(dev, res.start, resource_size(&res));
+
+ ret = dev_read_resource_byname(dev, "cdu", &res);
+ if (ret)
+ return ret;
+ cdu = devm_ioremap(dev, res.start, resource_size(&res));
+
+ ret = dev_read_resource_byname(dev, "pll3", &res);
+ if (ret)
+ return ret;
+ pll3 = devm_ioremap(dev, res.start, resource_size(&res));
+
+ // We only access this one register for pll3
+ pll3 = pll3 + PLL3_OFFSET;
+
+ // Input clock configuration
+ clk_get_by_name(dev, "dummy", &dummy);
+ clk_get_by_name(dev, "sys_clkin0", &clkin0);
+ clk_get_by_name(dev, "sys_clkin1", &clkin1);
+
+ clks[ADSP_SC598_CLK_DUMMY] = &dummy;
+ clks[ADSP_SC598_CLK_SYS_CLKIN0] = &clkin0;
+ clks[ADSP_SC598_CLK_SYS_CLKIN1] = &clkin1;
+
+ clks[ADSP_SC598_CLK_CGU1_IN] = clk_register_mux(NULL, "cgu1_in_sel", cgu1_in_sels,
+ 2, CLK_SET_RATE_PARENT,
+ cdu + CDU_CLKINSEL, 0, 1, 0);
+
+ // 3rd pll reuses cgu1 clk in selection, feeds directly into 3pll df
+ // changing the cgu1 in sel mux will affect 3pll so reuse the same clocks
+
+ // CGU configuration and internal clocks
+ clks[ADSP_SC598_CLK_CGU0_PLL_IN] = clk_register_divider(NULL, "cgu0_df",
+ "sys_clkin0",
+ CLK_SET_RATE_PARENT,
+ cgu0 + CGU_CTL, 0, 1, 0);
+ clks[ADSP_SC598_CLK_CGU1_PLL_IN] = clk_register_divider(NULL, "cgu1_df",
+ "cgu1_in_sel",
+ CLK_SET_RATE_PARENT,
+ cgu1 + CGU_CTL, 0, 1, 0);
+ clks[ADSP_SC598_CLK_3PLL_PLL_IN] = clk_register_divider(NULL, "3pll_df",
+ "cgu1_in_sel",
+ CLK_SET_RATE_PARENT,
+ pll3, 3, 1, 0);
+
+ // VCO output inside PLL
+ clks[ADSP_SC598_CLK_CGU0_VCO_OUT] = sc5xx_cgu_pll("cgu0_vco", "cgu0_df",
+ cgu0 + CGU_CTL, CGU_MSEL_SHIFT,
+ CGU_MSEL_WIDTH, 0, true);
+ clks[ADSP_SC598_CLK_CGU1_VCO_OUT] = sc5xx_cgu_pll("cgu1_vco", "cgu1_df",
+ cgu1 + CGU_CTL, CGU_MSEL_SHIFT,
+ CGU_MSEL_WIDTH, 0, true);
+ clks[ADSP_SC598_CLK_3PLL_VCO_OUT] = sc5xx_cgu_pll("3pll_vco", "3pll_df",
+ pll3, PLL3_MSEL_SHIFT,
+ PLL3_MSEL_WIDTH, 1, true);
+
+ // Final PLL output
+ clks[ADSP_SC598_CLK_CGU0_PLLCLK] = clk_register_fixed_factor(NULL, "cgu0_pllclk",
+ "cgu0_vco",
+ CLK_SET_RATE_PARENT,
+ 1, 2);
+ clks[ADSP_SC598_CLK_CGU1_PLLCLK] = clk_register_fixed_factor(NULL, "cgu1_pllclk",
+ "cgu1_vco",
+ CLK_SET_RATE_PARENT,
+ 1, 2);
+ clks[ADSP_SC598_CLK_3PLL_PLLCLK] = clk_register_fixed_factor(NULL, "3pll_pllclk",
+ "3pll_vco",
+ CLK_SET_RATE_PARENT,
+ 1, 2);
+
+ // Dividers from pll output
+ clks[ADSP_SC598_CLK_CGU0_CDIV] = cgu_divider("cgu0_cdiv", "cgu0_pllclk",
+ cgu0 + CGU_DIV, 0, 5, 0);
+ clks[ADSP_SC598_CLK_CGU0_SYSCLK] = cgu_divider("sysclk_0", "cgu0_pllclk",
+ cgu0 + CGU_DIV, 8, 5, 0);
+ clks[ADSP_SC598_CLK_CGU0_DDIV] = cgu_divider("cgu0_ddiv", "cgu0_pllclk",
+ cgu0 + CGU_DIV, 16, 5, 0);
+ clks[ADSP_SC598_CLK_CGU0_ODIV] = cgu_divider("cgu0_odiv", "cgu0_pllclk",
+ cgu0 + CGU_DIV, 22, 7, 0);
+ clks[ADSP_SC598_CLK_CGU0_S0SELDIV] = cgu_divider("cgu0_s0seldiv", "sysclk_0",
+ cgu0 + CGU_DIV, 5, 3, 0);
+ clks[ADSP_SC598_CLK_CGU0_S1SELDIV] = cgu_divider("cgu0_s1seldiv", "sysclk_0",
+ cgu0 + CGU_DIV, 13, 3, 0);
+ clks[ADSP_SC598_CLK_CGU0_S1SELEXDIV] = cgu_divider("cgu0_s1selexdiv",
+ "cgu0_pllclk",
+ cgu0 + CGU_DIVEX, 16, 8, 0);
+ clks[ADSP_SC598_CLK_CGU0_S1SEL] = clk_register_mux(NULL, "cgu0_sclk1sel",
+ cgu0_s1sels, 2,
+ CLK_SET_RATE_PARENT,
+ cgu0 + CGU_CTL, 17, 1, 0);
+ clks[ADSP_SC598_CLK_CGU0_CCLK2] = clk_register_fixed_factor(NULL, "cclk2_0",
+ "cgu0_vco",
+ CLK_SET_RATE_PARENT,
+ 1, 3);
+
+ clks[ADSP_SC598_CLK_CGU1_CDIV] = cgu_divider("cgu1_cdiv", "cgu1_pllclk",
+ cgu1 + CGU_DIV, 0, 5, 0);
+ clks[ADSP_SC598_CLK_CGU1_SYSCLK] = cgu_divider("sysclk_1", "cgu1_pllclk",
+ cgu1 + CGU_DIV, 8, 5, 0);
+ clks[ADSP_SC598_CLK_CGU1_DDIV] = cgu_divider("cgu1_ddiv", "cgu1_pllclk",
+ cgu1 + CGU_DIV, 16, 5, 0);
+ clks[ADSP_SC598_CLK_CGU1_ODIV] = cgu_divider("cgu1_odiv", "cgu1_pllclk",
+ cgu1 + CGU_DIV, 22, 7, 0);
+ clks[ADSP_SC598_CLK_CGU1_S0SELDIV] = cgu_divider("cgu1_s0seldiv", "sysclk_1",
+ cgu1 + CGU_DIV, 5, 3, 0);
+ clks[ADSP_SC598_CLK_CGU1_S1SELDIV] = cgu_divider("cgu1_s1seldiv", "sysclk_1",
+ cgu1 + CGU_DIV, 13, 3, 0);
+ clks[ADSP_SC598_CLK_CGU1_S0SELEXDIV] = cgu_divider("cgu1_s0selexdiv",
+ "cgu1_pllclk",
+ cgu1 + CGU_DIVEX, 0, 8, 0);
+ clks[ADSP_SC598_CLK_CGU1_S1SELEXDIV] = cgu_divider("cgu1_s1selexdiv",
+ "cgu1_pllclk",
+ cgu1 + CGU_DIVEX, 16, 8, 0);
+ clks[ADSP_SC598_CLK_CGU1_S0SEL] = clk_register_mux(NULL, "cgu1_sclk0sel",
+ cgu1_s0sels, 2,
+ CLK_SET_RATE_PARENT,
+ cgu1 + CGU_CTL, 16, 1, 0);
+ clks[ADSP_SC598_CLK_CGU1_S1SEL] = clk_register_mux(NULL, "cgu1_sclk1sel",
+ cgu1_s1sels, 2,
+ CLK_SET_RATE_PARENT,
+ cgu1 + CGU_CTL, 17, 1, 0);
+ clks[ADSP_SC598_CLK_CGU1_CCLK2] = clk_register_fixed_factor(NULL, "cclk2_1",
+ "cgu1_vco",
+ CLK_SET_RATE_PARENT,
+ 1, 3);
+
+ clks[ADSP_SC598_CLK_3PLL_DDIV] = clk_register_divider(NULL, "3pll_ddiv",
+ "3pll_pllclk",
+ CLK_SET_RATE_PARENT, pll3,
+ 12, 5, 0);
+
+ // Gates to enable CGU outputs
+ clks[ADSP_SC598_CLK_CGU0_CCLK0] = cgu_gate("cclk0_0", "cgu0_cdiv",
+ cgu0 + CGU_CCBF_DIS, 0);
+ clks[ADSP_SC598_CLK_CGU0_OCLK] = cgu_gate("oclk_0", "cgu0_odiv",
+ cgu0 + CGU_SCBF_DIS, 3);
+ clks[ADSP_SC598_CLK_CGU0_DCLK] = cgu_gate("dclk_0", "cgu0_ddiv",
+ cgu0 + CGU_SCBF_DIS, 2);
+ clks[ADSP_SC598_CLK_CGU0_SCLK1] = cgu_gate("sclk1_0", "cgu0_sclk1sel",
+ cgu0 + CGU_SCBF_DIS, 1);
+ clks[ADSP_SC598_CLK_CGU0_SCLK0] = cgu_gate("sclk0_0", "cgu0_s0seldiv",
+ cgu0 + CGU_SCBF_DIS, 0);
+
+ clks[ADSP_SC598_CLK_CGU1_CCLK0] = cgu_gate("cclk0_1", "cgu1_cdiv",
+ cgu1 + CGU_CCBF_DIS, 0);
+ clks[ADSP_SC598_CLK_CGU1_OCLK] = cgu_gate("oclk_1", "cgu1_odiv",
+ cgu1 + CGU_SCBF_DIS, 3);
+ clks[ADSP_SC598_CLK_CGU1_DCLK] = cgu_gate("dclk_1", "cgu1_ddiv",
+ cgu1 + CGU_SCBF_DIS, 2);
+ clks[ADSP_SC598_CLK_CGU1_SCLK1] = cgu_gate("sclk1_1", "cgu1_sclk1sel",
+ cgu1 + CGU_SCBF_DIS, 1);
+ clks[ADSP_SC598_CLK_CGU1_SCLK0] = cgu_gate("sclk0_1", "cgu1_sclk0sel",
+ cgu1 + CGU_SCBF_DIS, 0);
+
+ // Extra half rate clocks generated in the CDU
+ clks[ADSP_SC598_CLK_DCLK0_HALF] = clk_register_fixed_factor(NULL, "dclk_0_half",
+ "dclk_0",
+ CLK_SET_RATE_PARENT,
+ 1, 2);
+ clks[ADSP_SC598_CLK_DCLK1_HALF] = clk_register_fixed_factor(NULL, "dclk_1_half",
+ "dclk_1",
+ CLK_SET_RATE_PARENT,
+ 1, 2);
+ clks[ADSP_SC598_CLK_CGU1_SCLK1_HALF] = clk_register_fixed_factor(NULL,
+ "sclk1_1_half",
+ "sclk1_1",
+ CLK_SET_RATE_PARENT,
+ 1, 2);
+
+ // CDU output muxes
+ clks[ADSP_SC598_CLK_SHARC0_SEL] = cdu_mux("sharc0_sel", cdu + CDU_CFG0,
+ sharc0_sels);
+ clks[ADSP_SC598_CLK_SHARC1_SEL] = cdu_mux("sharc1_sel", cdu + CDU_CFG1,
+ sharc1_sels);
+ clks[ADSP_SC598_CLK_ARM_SEL] = cdu_mux("arm_sel", cdu + CDU_CFG2, arm_sels);
+ clks[ADSP_SC598_CLK_CDU_DDR_SEL] = cdu_mux("cdu_ddr_sel", cdu + CDU_CFG3,
+ cdu_ddr_sels);
+ clks[ADSP_SC598_CLK_CAN_SEL] = cdu_mux("can_sel", cdu + CDU_CFG4, can_sels);
+ clks[ADSP_SC598_CLK_SPDIF_SEL] = cdu_mux("spdif_sel", cdu + CDU_CFG5, spdif_sels);
+ clks[ADSP_SC598_CLK_SPI_SEL] = cdu_mux("spi_sel", cdu + CDU_CFG6, spi_sels);
+ clks[ADSP_SC598_CLK_GIGE_SEL] = cdu_mux("gige_sel", cdu + CDU_CFG7, gige_sels);
+ clks[ADSP_SC598_CLK_LP_SEL] = cdu_mux("lp_sel", cdu + CDU_CFG8, lp_sels);
+ clks[ADSP_SC598_CLK_LP_DDR_SEL] = cdu_mux("lp_ddr_sel", cdu + CDU_CFG9,
+ lp_ddr_sels);
+ clks[ADSP_SC598_CLK_OSPI_REFCLK_SEL] = cdu_mux("ospi_refclk_sel", cdu + CDU_CFG10,
+ ospi_refclk_sels);
+ clks[ADSP_SC598_CLK_TRACE_SEL] = cdu_mux("trace_sel", cdu + CDU_CFG12,
+ trace_sels);
+ clks[ADSP_SC598_CLK_EMMC_SEL] = cdu_mux("emmc_sel", cdu + CDU_CFG13, emmc_sels);
+ clks[ADSP_SC598_CLK_EMMC_TIMER_QMC_SEL] = cdu_mux("emmc_timer_qmc_sel",
+ cdu + CDU_CFG14,
+ emmc_timer_sels);
+
+ // CDU output enable gates
+ clks[ADSP_SC598_CLK_SHARC0] = cdu_gate("sharc0", "sharc0_sel", cdu + CDU_CFG0,
+ CLK_IS_CRITICAL);
+ clks[ADSP_SC598_CLK_SHARC1] = cdu_gate("sharc1", "sharc1_sel", cdu + CDU_CFG1,
+ CLK_IS_CRITICAL);
+ clks[ADSP_SC598_CLK_ARM] = cdu_gate("arm", "arm_sel", cdu + CDU_CFG2,
+ CLK_IS_CRITICAL);
+ clks[ADSP_SC598_CLK_CDU_DDR] = cdu_gate("cdu_ddr", "cdu_ddr_sel", cdu + CDU_CFG3,
+ 0);
+ clks[ADSP_SC598_CLK_CAN] = cdu_gate("can", "can_sel", cdu + CDU_CFG4, 0);
+ clks[ADSP_SC598_CLK_SPDIF] = cdu_gate("spdif", "spdif_sel", cdu + CDU_CFG5, 0);
+ clks[ADSP_SC598_CLK_SPI] = cdu_gate("spi", "spi_sel", cdu + CDU_CFG6, 0);
+ clks[ADSP_SC598_CLK_GIGE] = cdu_gate("gige", "gige_sel", cdu + CDU_CFG7, 0);
+ clks[ADSP_SC598_CLK_LP] = cdu_gate("lp", "lp_sel", cdu + CDU_CFG8, 0);
+ clks[ADSP_SC598_CLK_LP_DDR] = cdu_gate("lp_ddr", "lp_ddr_sel", cdu + CDU_CFG9, 0);
+ clks[ADSP_SC598_CLK_OSPI_REFCLK] = cdu_gate("ospi_refclk", "ospi_refclk_sel",
+ cdu + CDU_CFG10, 0);
+ clks[ADSP_SC598_CLK_TRACE] = cdu_gate("trace", "trace_sel", cdu + CDU_CFG12, 0);
+ clks[ADSP_SC598_CLK_EMMC] = cdu_gate("emmc", "emmc_sel", cdu + CDU_CFG13, 0);
+ clks[ADSP_SC598_CLK_EMMC_TIMER_QMC] = cdu_gate("emmc_timer_qmc",
+ "emmc_timer_qmc_sel",
+ cdu + CDU_CFG14, 0);
+
+ // Dedicated DDR output mux
+ clks[ADSP_SC598_CLK_DDR] = clk_register_mux(NULL, "ddr", ddr_sels, 2,
+ CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
+ pll3, 11, 1, 0);
+
+ ret = cdu_check_clocks(clks, ARRAY_SIZE(clks));
+ if (ret)
+ pr_err("CDU error detected\n");
+
+ return ret;
+}
+
+static const struct udevice_id adi_sc598_clk_ids[] = {
+ { .compatible = "adi,sc598-clocks" },
+ { },
+};
+
+U_BOOT_DRIVER(adi_sc598_clk) = {
+ .name = "clk_adi_sc598",
+ .id = UCLASS_CLK,
+ .of_match = adi_sc598_clk_ids,
+ .ops = &adi_clk_ops,
+ .probe = sc598_clock_probe,
+ .flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/drivers/clk/adi/clk-shared.c b/drivers/clk/adi/clk-shared.c
new file mode 100644
index 0000000..dcadcaf
--- /dev/null
+++ b/drivers/clk/adi/clk-shared.c
@@ -0,0 +1,48 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * (C) Copyright 2022 - Analog Devices, Inc.
+ *
+ * Written and/or maintained by Timesys Corporation
+ *
+ * Author: Greg Malysa <greg.malysa@timesys.com>
+ */
+
+#include "clk.h"
+
+static ulong adi_get_rate(struct clk *clk)
+{
+ struct clk *c;
+ int ret;
+
+ ret = clk_get_by_id(clk->id, &c);
+ if (ret)
+ return ret;
+
+ return clk_get_rate(c);
+}
+
+static ulong adi_set_rate(struct clk *clk, ulong rate)
+{
+ //Not yet implemented
+ return 0;
+}
+
+static int adi_enable(struct clk *clk)
+{
+ //Not yet implemented
+ return 0;
+}
+
+static int adi_disable(struct clk *clk)
+{
+ //Not yet implemented
+ return 0;
+}
+
+const struct clk_ops adi_clk_ops = {
+ .set_rate = adi_set_rate,
+ .get_rate = adi_get_rate,
+ .enable = adi_enable,
+ .disable = adi_disable,
+};
+
diff --git a/drivers/clk/adi/clk.h b/drivers/clk/adi/clk.h
new file mode 100644
index 0000000..f230205
--- /dev/null
+++ b/drivers/clk/adi/clk.h
@@ -0,0 +1,123 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * (C) Copyright 2022 - Analog Devices, Inc.
+ *
+ * Written and/or maintained by Timesys Corporation
+ *
+ * Author: Greg Malysa <greg.malysa@timesys.com>
+ *
+ * Ported from Linux: Nathan Barrett-Morrison <nathan.morrison@timesys.com>
+ */
+
+#ifndef CLK_ADI_CLK_H
+#define CLK_ADI_CLK_H
+
+#include <linux/compiler_types.h>
+#include <linux/types.h>
+#include <linux/clk-provider.h>
+
+#define CGU_CTL 0x00
+#define CGU_PLLCTL 0x04
+#define CGU_STAT 0x08
+#define CGU_DIV 0x0C
+#define CGU_CLKOUTSEL 0x10
+#define CGU_OSCWDCTL 0x14
+#define CGU_TSCTL 0x18
+#define CGU_TSVALUE0 0x1C
+#define CGU_TSVALUE1 0x20
+#define CGU_TSCOUNT0 0x24
+#define CGU_TSCOUNT1 0x28
+#define CGU_CCBF_DIS 0x2C
+#define CGU_CCBF_STAT 0x30
+#define CGU_SCBF_DIS 0x38
+#define CGU_SCBF_STAT 0x3C
+#define CGU_DIVEX 0x40
+#define CGU_REVID 0x48
+
+#define CDU_CFG0 0x00
+#define CDU_CFG1 0x04
+#define CDU_CFG2 0x08
+#define CDU_CFG3 0x0C
+#define CDU_CFG4 0x10
+#define CDU_CFG5 0x14
+#define CDU_CFG6 0x18
+#define CDU_CFG7 0x1C
+#define CDU_CFG8 0x20
+#define CDU_CFG9 0x24
+#define CDU_CFG10 0x28
+#define CDU_CFG11 0x2C
+#define CDU_CFG12 0x30
+#define CDU_CFG13 0x34
+#define CDU_CFG14 0x38
+
+#define PLL3_OFFSET 0x2c
+
+#define CDU_CLKINSEL 0x44
+
+#define CGU_MSEL_SHIFT 8
+#define CGU_MSEL_WIDTH 7
+
+#define PLL3_MSEL_SHIFT 4
+#define PLL3_MSEL_WIDTH 7
+
+#define CDU_MUX_SIZE 4
+#define CDU_MUX_SHIFT 1
+#define CDU_MUX_WIDTH 2
+#define CDU_EN_BIT 0
+
+extern const struct clk_ops adi_clk_ops;
+
+struct clk *sc5xx_cgu_pll(const char *name, const char *parent_name,
+ void __iomem *base, u8 shift, u8 width, u32 m_offset, bool half_m);
+
+/**
+ * All CDU clock muxes are the same size
+ */
+static inline struct clk *cdu_mux(const char *name, void __iomem *reg,
+ const char * const *parents)
+{
+ return clk_register_mux(NULL, name, parents, CDU_MUX_SIZE,
+ CLK_SET_RATE_PARENT, reg, CDU_MUX_SHIFT, CDU_MUX_WIDTH, 0);
+}
+
+static inline struct clk *cgu_divider(const char *name, const char *parent,
+ void __iomem *reg, u8 shift, u8 width, u8 extra_flags)
+{
+ return clk_register_divider(NULL, name, parent, CLK_SET_RATE_PARENT,
+ reg, shift, width, CLK_DIVIDER_MAX_AT_ZERO | extra_flags);
+}
+
+static inline struct clk *cdu_gate(const char *name, const char *parent,
+ void __iomem *reg, u32 flags)
+{
+ return clk_register_gate(NULL, name, parent, CLK_SET_RATE_PARENT | flags,
+ reg, CDU_EN_BIT, 0, NULL);
+}
+
+static inline struct clk *cgu_gate(const char *name, const char *parent,
+ void __iomem *reg, u8 bit)
+{
+ return clk_register_gate(NULL, name, parent, CLK_SET_RATE_PARENT, reg, bit,
+ CLK_GATE_SET_TO_DISABLE, NULL);
+}
+
+static inline int cdu_check_clocks(struct clk *clks[], size_t count)
+{
+ size_t i;
+
+ for (i = 0; i < count; ++i) {
+ if (clks[i]) {
+ if (IS_ERR(clks[i])) {
+ pr_err("Clock %zu failed to register: %ld\n", i, PTR_ERR(clks[i]));
+ return PTR_ERR(clks[i]);
+ }
+ clks[i]->id = i;
+ } else {
+ pr_err("ADI Clock framework: Null pointer detected on clock %zu\n", i);
+ }
+ }
+
+ return 0;
+}
+
+#endif
diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile
index 403ab1d..dbe598b 100644
--- a/drivers/serial/Makefile
+++ b/drivers/serial/Makefile
@@ -65,3 +65,4 @@ obj-$(CONFIG_S5P4418_PL011_SERIAL) += serial_s5p4418_pl011.o
ifndef CONFIG_SPL_BUILD
obj-$(CONFIG_USB_TTY) += usbtty.o
endif
+obj-$(CONFIG_UART4_SERIAL) += serial_adi_uart4.o
diff --git a/drivers/serial/serial_adi_uart4.c b/drivers/serial/serial_adi_uart4.c
new file mode 100644
index 0000000..45f8315
--- /dev/null
+++ b/drivers/serial/serial_adi_uart4.c
@@ -0,0 +1,225 @@
+// 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
+ *
+ * Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com>
+ * Contact: Greg Malysa <greg.malysa@timesys.com>
+ *
+ */
+
+#include <clk.h>
+#include <dm.h>
+#include <serial.h>
+#include <asm/io.h>
+#include <dm/device_compat.h>
+#include <linux/bitops.h>
+
+/*
+ * UART4 Masks
+ */
+
+/* UART_CONTROL */
+#define UEN BIT(0)
+#define LOOP_ENA BIT(1)
+#define UMOD (3 << 4)
+#define UMOD_UART (0 << 4)
+#define UMOD_MDB BIT(4)
+#define UMOD_IRDA BIT(4)
+#define WLS (3 << 8)
+#define WLS_5 (0 << 8)
+#define WLS_6 BIT(8)
+#define WLS_7 (2 << 8)
+#define WLS_8 (3 << 8)
+#define STB BIT(12)
+#define STBH BIT(13)
+#define PEN BIT(14)
+#define EPS BIT(15)
+#define STP BIT(16)
+#define FPE BIT(17)
+#define FFE BIT(18)
+#define SB BIT(19)
+#define FCPOL BIT(22)
+#define RPOLC BIT(23)
+#define TPOLC BIT(24)
+#define MRTS BIT(25)
+#define XOFF BIT(26)
+#define ARTS BIT(27)
+#define ACTS BIT(28)
+#define RFIT BIT(29)
+#define RFRT BIT(30)
+
+/* UART_STATUS */
+#define DR BIT(0)
+#define OE BIT(1)
+#define PE BIT(2)
+#define FE BIT(3)
+#define BI BIT(4)
+#define THRE BIT(5)
+#define TEMT BIT(7)
+#define TFI BIT(8)
+#define ASTKY BIT(9)
+#define ADDR BIT(10)
+#define RO BIT(11)
+#define SCTS BIT(12)
+#define CTS BIT(16)
+#define RFCS BIT(17)
+
+/* UART_EMASK */
+#define ERBFI BIT(0)
+#define ETBEI BIT(1)
+#define ELSI BIT(2)
+#define EDSSI BIT(3)
+#define EDTPTI BIT(4)
+#define ETFI BIT(5)
+#define ERFCI BIT(6)
+#define EAWI BIT(7)
+#define ERXS BIT(8)
+#define ETXS BIT(9)
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct uart4_reg {
+ u32 revid;
+ u32 control;
+ u32 status;
+ u32 scr;
+ u32 clock;
+ u32 emask;
+ u32 emaskst;
+ u32 emaskcl;
+ u32 rbr;
+ u32 thr;
+ u32 taip;
+ u32 tsr;
+ u32 rsr;
+ u32 txdiv_cnt;
+ u32 rxdiv_cnt;
+};
+
+struct adi_uart4_platdata {
+ // Hardware registers
+ struct uart4_reg *regs;
+
+ // Enable divide-by-one baud rate setting
+ bool edbo;
+};
+
+static int adi_uart4_set_brg(struct udevice *dev, int baudrate)
+{
+ struct adi_uart4_platdata *plat = dev_get_plat(dev);
+ struct uart4_reg *regs = plat->regs;
+ u32 divisor, uart_base_clk_rate;
+ struct clk uart_base_clk;
+
+ if (clk_get_by_index(dev, 0, &uart_base_clk)) {
+ dev_err(dev, "Could not get UART base clock\n");
+ return -1;
+ }
+
+ uart_base_clk_rate = clk_get_rate(&uart_base_clk);
+
+ if (plat->edbo) {
+ u16 divisor16 = (uart_base_clk_rate + (baudrate / 2)) / baudrate;
+
+ divisor = divisor16 | BIT(31);
+ } else {
+ // Divisor is only 16 bits
+ divisor = 0x0000ffff & ((uart_base_clk_rate + (baudrate * 8)) / (baudrate * 16));
+ }
+
+ writel(divisor, &regs->clock);
+ return 0;
+}
+
+static int adi_uart4_pending(struct udevice *dev, bool input)
+{
+ struct adi_uart4_platdata *plat = dev_get_plat(dev);
+ struct uart4_reg *regs = plat->regs;
+
+ if (input)
+ return (readl(&regs->status) & DR) ? 1 : 0;
+ else
+ return (readl(&regs->status) & THRE) ? 0 : 1;
+}
+
+static int adi_uart4_getc(struct udevice *dev)
+{
+ struct adi_uart4_platdata *plat = dev_get_plat(dev);
+ struct uart4_reg *regs = plat->regs;
+ int uart_rbr_val;
+
+ if (!adi_uart4_pending(dev, true))
+ return -EAGAIN;
+
+ uart_rbr_val = readl(&regs->rbr);
+ writel(-1, &regs->status);
+
+ return uart_rbr_val;
+}
+
+static int adi_uart4_putc(struct udevice *dev, const char ch)
+{
+ struct adi_uart4_platdata *plat = dev_get_plat(dev);
+ struct uart4_reg *regs = plat->regs;
+
+ if (adi_uart4_pending(dev, false))
+ return -EAGAIN;
+
+ writel(ch, &regs->thr);
+ return 0;
+}
+
+static const struct dm_serial_ops adi_uart4_serial_ops = {
+ .setbrg = adi_uart4_set_brg,
+ .getc = adi_uart4_getc,
+ .putc = adi_uart4_putc,
+ .pending = adi_uart4_pending,
+};
+
+static int adi_uart4_of_to_plat(struct udevice *dev)
+{
+ struct adi_uart4_platdata *plat = dev_get_plat(dev);
+ fdt_addr_t addr;
+
+ addr = dev_read_addr(dev);
+ if (addr == FDT_ADDR_T_NONE)
+ return -EINVAL;
+
+ plat->regs = (struct uart4_reg *)addr;
+ plat->edbo = dev_read_bool(dev, "adi,enable-edbo");
+
+ return 0;
+}
+
+static int adi_uart4_probe(struct udevice *dev)
+{
+ struct adi_uart4_platdata *plat = dev_get_plat(dev);
+ struct uart4_reg *regs = plat->regs;
+
+ /* always enable UART to 8-bit mode */
+ writel(UEN | UMOD_UART | WLS_8, &regs->control);
+
+ writel(-1, &regs->status);
+
+ return 0;
+}
+
+static const struct udevice_id adi_uart4_serial_ids[] = {
+ { .compatible = "adi,uart4" },
+ { }
+};
+
+U_BOOT_DRIVER(serial_adi_uart4) = {
+ .name = "serial_adi_uart4",
+ .id = UCLASS_SERIAL,
+ .of_match = adi_uart4_serial_ids,
+ .of_to_plat = adi_uart4_of_to_plat,
+ .plat_auto = sizeof(struct adi_uart4_platdata),
+ .probe = adi_uart4_probe,
+ .ops = &adi_uart4_serial_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/drivers/timer/Kconfig b/drivers/timer/Kconfig
index 60519c3..6b1de82 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 b93145e..fb95c88 100644
--- a/drivers/timer/Makefile
+++ b/drivers/timer/Makefile
@@ -3,6 +3,7 @@
# Copyright (C) 2015 Thomas Chou <thomas@wytron.com.tw>
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 0000000..11c0984
--- /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 <greg.malysa@timesys.com>
+ * Additional Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com>
+ *
+ * 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,
+};
diff --git a/include/dt-bindings/clock/adi-sc5xx-clock.h b/include/dt-bindings/clock/adi-sc5xx-clock.h
new file mode 100644
index 0000000..4a5373d
--- /dev/null
+++ b/include/dt-bindings/clock/adi-sc5xx-clock.h
@@ -0,0 +1,271 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * (C) Copyright 2022 - Analog Devices, Inc.
+ *
+ * Written and/or maintained by Timesys Corporation
+ *
+ * Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com>
+ * Contact: Greg Malysa <greg.malysa@timesys.com>
+ *
+ */
+
+#ifndef DT_BINDINGS_CLOCK_ADI_SC5XX_CLOCK_H
+#define DT_BINDINGS_CLOCK_ADI_SC5XX_CLOCK_H
+
+//ADSP-SC594
+#define ADSP_SC594_CLK_DUMMY 0
+#define ADSP_SC594_CLK_SYS_CLKIN0 1
+#define ADSP_SC594_CLK_SYS_CLKIN1 2
+#define ADSP_SC594_CLK_CGU1_IN 3
+#define ADSP_SC594_CLK_CGU0_PLL_IN 4
+#define ADSP_SC594_CLK_CGU1_PLL_IN 5
+#define ADSP_SC594_CLK_CGU0_VCO_OUT 6
+#define ADSP_SC594_CLK_CGU1_VCO_OUT 7
+#define ADSP_SC594_CLK_CGU0_PLLCLK 8
+#define ADSP_SC594_CLK_CGU1_PLLCLK 9
+#define ADSP_SC594_CLK_CGU0_CDIV 10
+#define ADSP_SC594_CLK_CGU0_SYSCLK 11
+#define ADSP_SC594_CLK_CGU0_DDIV 12
+#define ADSP_SC594_CLK_CGU0_ODIV 13
+#define ADSP_SC594_CLK_CGU0_S0SELDIV 14
+#define ADSP_SC594_CLK_CGU0_S1SELDIV 15
+#define ADSP_SC594_CLK_CGU0_S1SELEXDIV 16
+#define ADSP_SC594_CLK_CGU0_S1SEL 17
+#define ADSP_SC594_CLK_CGU1_CDIV 18
+#define ADSP_SC594_CLK_CGU1_SYSCLK 19
+#define ADSP_SC594_CLK_CGU1_DDIV 20
+#define ADSP_SC594_CLK_CGU1_ODIV 21
+#define ADSP_SC594_CLK_CGU1_S0SELDIV 22
+#define ADSP_SC594_CLK_CGU1_S1SELDIV 23
+#define ADSP_SC594_CLK_CGU1_S1SELEXDIV 24
+#define ADSP_SC594_CLK_CGU1_S1SEL 25
+#define ADSP_SC594_CLK_CGU0_CCLK0 26
+#define ADSP_SC594_CLK_CGU0_CCLK1 27
+#define ADSP_SC594_CLK_CGU0_OCLK 28
+#define ADSP_SC594_CLK_CGU0_DCLK 29
+#define ADSP_SC594_CLK_CGU0_SCLK1 30
+#define ADSP_SC594_CLK_CGU0_SCLK0 31
+#define ADSP_SC594_CLK_CGU1_CCLK0 32
+#define ADSP_SC594_CLK_CGU1_CCLK1 33
+#define ADSP_SC594_CLK_CGU1_OCLK 34
+#define ADSP_SC594_CLK_CGU1_DCLK 35
+#define ADSP_SC594_CLK_CGU1_SCLK1 36
+#define ADSP_SC594_CLK_CGU1_SCLK0 37
+#define ADSP_SC594_CLK_SHARC0_SEL 38
+#define ADSP_SC594_CLK_SHARC1_SEL 39
+#define ADSP_SC594_CLK_ARM_SEL 40
+#define ADSP_SC594_CLK_CDU_DDR_SEL 41
+#define ADSP_SC594_CLK_CAN_SEL 42
+#define ADSP_SC594_CLK_SPDIF_SEL 43
+#define ADSP_SC594_CLK_RESERVED_SEL 44
+#define ADSP_SC594_CLK_GIGE_SEL 45
+#define ADSP_SC594_CLK_LP_SEL 46
+#define ADSP_SC594_CLK_LPDDR_SEL 47
+#define ADSP_SC594_CLK_OSPI_SEL 48
+#define ADSP_SC594_CLK_TRACE_SEL 49
+#define ADSP_SC594_CLK_SHARC0 50
+#define ADSP_SC594_CLK_SHARC1 51
+#define ADSP_SC594_CLK_ARM 52
+#define ADSP_SC594_CLK_CDU_DDR 53
+#define ADSP_SC594_CLK_CAN 54
+#define ADSP_SC594_CLK_SPDIF 55
+#define ADSP_SC594_CLK_SPI 56
+#define ADSP_SC594_CLK_GIGE 57
+#define ADSP_SC594_CLK_LP 58
+#define ADSP_SC594_CLK_LPDDR 59
+#define ADSP_SC594_CLK_OSPI 60
+#define ADSP_SC594_CLK_TRACE 61
+#define ADSP_SC594_CLK_END 62
+
+//ADSP-SC598
+#define ADSP_SC598_CLK_DUMMY 0
+#define ADSP_SC598_CLK_SYS_CLKIN0 1
+#define ADSP_SC598_CLK_SYS_CLKIN1 2
+#define ADSP_SC598_CLK_CGU0_PLL_IN 3
+#define ADSP_SC598_CLK_CGU0_VCO_OUT 4
+#define ADSP_SC598_CLK_CGU0_PLLCLK 5
+#define ADSP_SC598_CLK_CGU1_IN 6
+#define ADSP_SC598_CLK_CGU1_PLL_IN 7
+#define ADSP_SC598_CLK_CGU1_VCO_OUT 8
+#define ADSP_SC598_CLK_CGU1_PLLCLK 9
+#define ADSP_SC598_CLK_CGU0_CDIV 10
+#define ADSP_SC598_CLK_CGU0_SYSCLK 11
+#define ADSP_SC598_CLK_CGU0_DDIV 12
+#define ADSP_SC598_CLK_CGU0_ODIV 13
+#define ADSP_SC598_CLK_CGU0_S0SELDIV 14
+#define ADSP_SC598_CLK_CGU0_S1SELDIV 15
+#define ADSP_SC598_CLK_CGU0_S1SELEXDIV 16
+#define ADSP_SC598_CLK_CGU0_S1SEL 17
+#define ADSP_SC598_CLK_CGU1_CDIV 18
+#define ADSP_SC598_CLK_CGU1_SYSCLK 19
+#define ADSP_SC598_CLK_CGU1_DDIV 20
+#define ADSP_SC598_CLK_CGU1_ODIV 21
+#define ADSP_SC598_CLK_CGU1_S0SELDIV 22
+#define ADSP_SC598_CLK_CGU1_S1SELDIV 23
+#define ADSP_SC598_CLK_CGU1_S0SELEXDIV 24
+#define ADSP_SC598_CLK_CGU1_S1SELEXDIV 25
+#define ADSP_SC598_CLK_CGU1_S0SEL 26
+#define ADSP_SC598_CLK_CGU1_S1SEL 27
+#define ADSP_SC598_CLK_CGU0_CCLK2 28
+#define ADSP_SC598_CLK_CGU0_CCLK0 29
+#define ADSP_SC598_CLK_CGU0_OCLK 30
+#define ADSP_SC598_CLK_CGU0_DCLK 31
+#define ADSP_SC598_CLK_CGU0_SCLK1 32
+#define ADSP_SC598_CLK_CGU0_SCLK0 33
+#define ADSP_SC598_CLK_CGU1_CCLK0 34
+#define ADSP_SC598_CLK_CGU1_OCLK 35
+#define ADSP_SC598_CLK_CGU1_DCLK 36
+#define ADSP_SC598_CLK_CGU1_SCLK1 37
+#define ADSP_SC598_CLK_CGU1_SCLK0 38
+#define ADSP_SC598_CLK_CGU1_CCLK2 39
+#define ADSP_SC598_CLK_DCLK0_HALF 40
+#define ADSP_SC598_CLK_DCLK1_HALF 41
+#define ADSP_SC598_CLK_CGU1_SCLK1_HALF 42
+#define ADSP_SC598_CLK_SHARC0_SEL 43
+#define ADSP_SC598_CLK_SHARC1_SEL 44
+#define ADSP_SC598_CLK_ARM_SEL 45
+#define ADSP_SC598_CLK_CDU_DDR_SEL 46
+#define ADSP_SC598_CLK_CAN_SEL 47
+#define ADSP_SC598_CLK_SPDIF_SEL 48
+#define ADSP_SC598_CLK_SPI_SEL 49
+#define ADSP_SC598_CLK_GIGE_SEL 50
+#define ADSP_SC598_CLK_LP_SEL 51
+#define ADSP_SC598_CLK_LP_DDR_SEL 52
+#define ADSP_SC598_CLK_OSPI_REFCLK_SEL 53
+#define ADSP_SC598_CLK_TRACE_SEL 54
+#define ADSP_SC598_CLK_EMMC_SEL 55
+#define ADSP_SC598_CLK_EMMC_TIMER_QMC_SEL 56
+#define ADSP_SC598_CLK_SHARC0 57
+#define ADSP_SC598_CLK_SHARC1 58
+#define ADSP_SC598_CLK_ARM 59
+#define ADSP_SC598_CLK_CDU_DDR 60
+#define ADSP_SC598_CLK_CAN 61
+#define ADSP_SC598_CLK_SPDIF 62
+#define ADSP_SC598_CLK_SPI 63
+#define ADSP_SC598_CLK_GIGE 64
+#define ADSP_SC598_CLK_LP 65
+#define ADSP_SC598_CLK_LP_DDR 66
+#define ADSP_SC598_CLK_OSPI_REFCLK 67
+#define ADSP_SC598_CLK_TRACE 68
+#define ADSP_SC598_CLK_EMMC 69
+#define ADSP_SC598_CLK_EMMC_TIMER_QMC 70
+#define ADSP_SC598_CLK_3PLL_PLL_IN 71
+#define ADSP_SC598_CLK_3PLL_VCO_OUT 72
+#define ADSP_SC598_CLK_3PLL_PLLCLK 73
+#define ADSP_SC598_CLK_3PLL_DDIV 74
+#define ADSP_SC598_CLK_DDR 75
+#define ADSP_SC598_CLK_END 76
+
+//ADSP-SC58X
+#define ADSP_SC58X_CLK_DUMMY 0
+#define ADSP_SC58X_CLK_SYS_CLKIN0 1
+#define ADSP_SC58X_CLK_SYS_CLKIN1 2
+#define ADSP_SC58X_CLK_CGU0_PLL_IN 3
+#define ADSP_SC58X_CLK_CGU0_VCO_OUT 4
+#define ADSP_SC58X_CLK_CGU0_PLLCLK 5
+#define ADSP_SC58X_CLK_CGU1_IN 6
+#define ADSP_SC58X_CLK_CGU1_PLL_IN 7
+#define ADSP_SC58X_CLK_CGU1_VCO_OUT 8
+#define ADSP_SC58X_CLK_CGU1_PLLCLK 9
+#define ADSP_SC58X_CLK_CGU0_CDIV 10
+#define ADSP_SC58X_CLK_CGU0_SYSCLK 11
+#define ADSP_SC58X_CLK_CGU0_DDIV 12
+#define ADSP_SC58X_CLK_CGU0_ODIV 13
+#define ADSP_SC58X_CLK_CGU0_S0SELDIV 14
+#define ADSP_SC58X_CLK_CGU0_S1SELDIV 15
+#define ADSP_SC58X_CLK_CGU1_CDIV 16
+#define ADSP_SC58X_CLK_CGU1_SYSCLK 17
+#define ADSP_SC58X_CLK_CGU1_DDIV 18
+#define ADSP_SC58X_CLK_CGU1_ODIV 19
+#define ADSP_SC58X_CLK_CGU1_S0SELDIV 20
+#define ADSP_SC58X_CLK_CGU1_S1SELDIV 21
+#define ADSP_SC58X_CLK_CGU0_CCLK0 22
+#define ADSP_SC58X_CLK_CGU0_CCLK1 23
+#define ADSP_SC58X_CLK_CGU0_OCLK 24
+#define ADSP_SC58X_CLK_CGU0_DCLK 25
+#define ADSP_SC58X_CLK_CGU0_SCLK1 26
+#define ADSP_SC58X_CLK_CGU0_SCLK0 27
+#define ADSP_SC58X_CLK_CGU1_CCLK0 28
+#define ADSP_SC58X_CLK_CGU1_CCLK1 29
+#define ADSP_SC58X_CLK_CGU1_OCLK 30
+#define ADSP_SC58X_CLK_CGU1_DCLK 31
+#define ADSP_SC58X_CLK_CGU1_SCLK1 32
+#define ADSP_SC58X_CLK_CGU1_SCLK0 33
+#define ADSP_SC58X_CLK_OCLK0_HALF 34
+#define ADSP_SC58X_CLK_CCLK1_1_HALF 35
+#define ADSP_SC58X_CLK_SHARC0_SEL 36
+#define ADSP_SC58X_CLK_SHARC1_SEL 37
+#define ADSP_SC58X_CLK_ARM_SEL 38
+#define ADSP_SC58X_CLK_CDU_DDR_SEL 39
+#define ADSP_SC58X_CLK_CAN_SEL 40
+#define ADSP_SC58X_CLK_SPDIF_SEL 41
+#define ADSP_SC58X_CLK_RESERVED_SEL 42
+#define ADSP_SC58X_CLK_GIGE_SEL 43
+#define ADSP_SC58X_CLK_LP_SEL 44
+#define ADSP_SC58X_CLK_SDIO_SEL 45
+#define ADSP_SC58X_CLK_SHARC0 46
+#define ADSP_SC58X_CLK_SHARC1 47
+#define ADSP_SC58X_CLK_ARM 48
+#define ADSP_SC58X_CLK_CDU_DDR 49
+#define ADSP_SC58X_CLK_CAN 50
+#define ADSP_SC58X_CLK_SPDIF 51
+#define ADSP_SC58X_CLK_RESERVED 52
+#define ADSP_SC58X_CLK_GIGE 53
+#define ADSP_SC58X_CLK_LP 54
+#define ADSP_SC58X_CLK_SDIO 55
+#define ADSP_SC58X_CLK_END 56
+
+//ADSP-SC57X
+#define ADSP_SC57X_CLK_DUMMY 0
+#define ADSP_SC57X_CLK_SYS_CLKIN0 1
+#define ADSP_SC57X_CLK_SYS_CLKIN1 2
+#define ADSP_SC57X_CLK_CGU0_PLL_IN 3
+#define ADSP_SC57X_CLK_CGU0_PLLCLK 4
+#define ADSP_SC57X_CLK_CGU1_IN 5
+#define ADSP_SC57X_CLK_CGU1_PLL_IN 6
+#define ADSP_SC57X_CLK_CGU1_PLLCLK 7
+#define ADSP_SC57X_CLK_CGU0_CDIV 8
+#define ADSP_SC57X_CLK_CGU0_SYSCLK 9
+#define ADSP_SC57X_CLK_CGU0_DDIV 10
+#define ADSP_SC57X_CLK_CGU0_ODIV 11
+#define ADSP_SC57X_CLK_CGU0_S0SELDIV 12
+#define ADSP_SC57X_CLK_CGU0_S1SELDIV 13
+#define ADSP_SC57X_CLK_CGU1_CDIV 14
+#define ADSP_SC57X_CLK_CGU1_SYSCLK 15
+#define ADSP_SC57X_CLK_CGU1_DDIV 16
+#define ADSP_SC57X_CLK_CGU1_ODIV 17
+#define ADSP_SC57X_CLK_CGU1_S0SELDIV 18
+#define ADSP_SC57X_CLK_CGU1_S1SELDIV 19
+#define ADSP_SC57X_CLK_CGU0_CCLK0 20
+#define ADSP_SC57X_CLK_CGU0_CCLK1 21
+#define ADSP_SC57X_CLK_CGU0_OCLK 22
+#define ADSP_SC57X_CLK_CGU0_DCLK 23
+#define ADSP_SC57X_CLK_CGU0_SCLK1 24
+#define ADSP_SC57X_CLK_CGU0_SCLK0 25
+#define ADSP_SC57X_CLK_CGU1_CCLK0 26
+#define ADSP_SC57X_CLK_CGU1_CCLK1 27
+#define ADSP_SC57X_CLK_CGU1_OCLK 28
+#define ADSP_SC57X_CLK_CGU1_DCLK 29
+#define ADSP_SC57X_CLK_CGU1_SCLK1 30
+#define ADSP_SC57X_CLK_CGU1_SCLK0 31
+#define ADSP_SC57X_CLK_OCLK0_HALF 32
+#define ADSP_SC57X_CLK_CCLK1_1_HALF 33
+#define ADSP_SC57X_CLK_SHARC0_SEL 34
+#define ADSP_SC57X_CLK_SHARC1_SEL 35
+#define ADSP_SC57X_CLK_ARM_SEL 36
+#define ADSP_SC57X_CLK_CDU_DDR_SEL 37
+#define ADSP_SC57X_CLK_CAN_SEL 38
+#define ADSP_SC57X_CLK_SPDIF_SEL 39
+#define ADSP_SC57X_CLK_GIGE_SEL 40
+#define ADSP_SC57X_CLK_SDIO_SEL 41
+#define ADSP_SC57X_CLK_SHARC0 42
+#define ADSP_SC57X_CLK_SHARC1 43
+#define ADSP_SC57X_CLK_ARM 44
+#define ADSP_SC57X_CLK_CDU_DDR 45
+#define ADSP_SC57X_CLK_CAN 46
+#define ADSP_SC57X_CLK_SPDIF 47
+#define ADSP_SC57X_CLK_GIGE 48
+#define ADSP_SC57X_CLK_SDIO 49
+#define ADSP_SC57X_CLK_END 50
+
+#endif
diff --git a/include/env/adi/adi_boot.env b/include/env/adi/adi_boot.env
new file mode 100644
index 0000000..d56b14f
--- /dev/null
+++ b/include/env/adi/adi_boot.env
@@ -0,0 +1,122 @@
+/*
+ * A target board needs to set these variables for the commands below to work:
+ *
+ * - adi_stage2_offset, the location of stage2-boot.ldr on the SPI flash
+ * - adi_image_offset, location of the fitImage on the SPI flash
+ * - adi_rfs_offset, location of the RFS on the SPI flash
+ * - loadaddr, where you want to load things
+ * - jffs2file, name of the jffs2 file for update, ex adsp-sc5xx-tiny-adsp-sc573.jffs2
+ */
+
+#ifdef CONFIG_SC59X_64
+#define EARLY_PRINTK earlycon=adi_uart,0x31003000
+#else
+#define EARLY_PRINTK earlyprintk=serial,uart0,CONFIG_BAUDRATE
+#endif
+
+/* Config options */
+imagefile=fitImage
+ethaddr=02:80:ad:20:31:e8
+eth1addr=02:80:ad:20:31:e9
+uart_console=CONFIG_UART_CONSOLE
+#ifdef CONFIG_SC59X_64
+fdt_high=0xffffffffffffffff
+initrd_high=0xffffffffffffffff
+#else
+fdt_high=0xffffffff
+initrd_high=0xffffffff
+#endif
+
+/* Helper routines */
+init_ethernet=mii info;
+ dhcp;
+ setenv serverip ${tftpserverip}
+
+/* Args for each boot mode */
+adi_bootargs=EARLY_PRINTK console=ttySC0,CONFIG_BAUDRATE vmalloc=512M
+ramargs=setenv bootargs ${adi_bootargs}
+
+addip=setenv bootargs ${bootargs} ip=${ipaddr}:${serverip}:${gatewayip}:${netmask}:${hostname}:eth0:off
+
+/* Boot modes are selectable and should be defined in the board env before including */
+#if defined(USE_NFS)
+// rootpath is set by CONFIG_ROOTPATH
+nfsargs=setenv bootargs root=/dev/nfs rw nfsroot=${serverip}${rootpath},tcp,nfsvers=3 ${adi_bootargs}
+nfsboot=run init_ethernet;
+ tftp ${loadaddr} ${tftp_dir_prefix}${imagefile};
+ run nfsargs;
+ run addip;
+ bootm ${loadaddr}
+#endif
+
+#if defined(USE_MMC)
+mmcargs=setenv bootargs root=/dev/mmcblk0p1 rw rootfstype=ext4 rootwait ${adi_bootargs}
+mmcboot=mmc rescan;
+ ext4load mmc 0:1 ${loadaddr} /boot/${imagefile};
+ run mmcargs;
+ bootm ${loadaddr}
+#endif
+
+#if defined(USE_SPI) || defined(USE_OSPI)
+spiargs=setenv bootargs root=/dev/mtdblock4 rw rootfstype=jffs2 ${adi_bootargs}
+spiboot=run spiargs;
+ sf probe ${sfdev};
+ sf read ${loadaddr} ${adi_image_offset} ${imagesize};
+ bootm ${loadaddr}
+#endif
+
+#if defined(USE_OSPI)
+ospiboot=run spiboot
+#endif
+
+#if defined(USE_RAM)
+ramboot=run init_ethernet;
+ tftp ${loadaddr} ${tfpt_dir_prefix}${imagefile};
+ run ramargs;
+ bootm ${loadaddr}
+#endif
+
+/* Update commands */
+stage1file=stage1-boot.ldr
+stage2file=stage2-boot.ldr
+
+#if defined(USE_SPI) || defined(USE_OSPI)
+update_spi_uboot_stage1=tftp ${loadaddr} ${tftp_dir_prefix}${stage1file};
+ sf probe ${sfdev};
+ sf update ${loadaddr} 0x0 ${filesize}
+update_spi_uboot_stage2=tftp ${loadaddr} ${tftp_dir_prefix}${stage2file};
+ sf probe ${sfdev};
+ sf update ${loadaddr} ${adi_stage2_offset} ${filesize}
+update_spi_uboot=run update_spi_uboot_stage1;
+ run update_spi_uboot_stage2;
+update_spi_fit=tftp ${loadaddr} ${tftp_dir_prefix}${imagefile};
+ sf probe ${sfdev};
+ sf update ${loadaddr} ${adi_image_offset} ${filesize};
+ setenv imagesize ${filesize}
+update_spi_rfs=tftp ${loadaddr} ${tftp_dir_prefix}${jffs2file};
+ sf probe ${sfdev};
+ sf update ${loadaddr} ${adi_rfs_offset} ${filesize}
+
+start_update_spi=run init_ethernet;
+ run update_spi_uboot;
+ run update_spi_fit;
+ run update_spi_rfs;
+start_update_spi_uboot_only=run init_ethernet;
+ run update_spi_uboot;
+#endif
+
+#if defined(USE_SPI)
+update_spi=setenv sfdev CONFIG_SC_BOOT_SPI_BUS:CONFIG_SC_BOOT_SPI_SSEL;
+ setenv bootcmd run spiboot;
+ setenv argscmd spiargs;
+ run start_update_spi;
+ saveenv
+#endif
+
+#if defined(USE_OSPI)
+update_ospi=setenv sfdev CONFIG_SC_BOOT_OSPI_BUS:CONFIG_SC_BOOT_OSPI_SSEL;
+ setenv bootcmd run ospiboot;
+ setenv argscmd spiargs;
+ run start_update_spi;
+ saveenv
+#endif