From 48a0b0b4b7d7c84c31b307a5ae8bfbc264cf2162 Mon Sep 17 00:00:00 2001 From: Nathan Barrett-Morrison Date: Wed, 24 Apr 2024 20:04:00 -0400 Subject: arch: arm: Add Analog Devices SC5xx machine type Add support for the SC5xx machine type from Analog Devices. This includes support for the SC57x, SC58x, SC59x, and SC59x-64 SoCs, which have many common features such as common ADI IP blocks, and SHARC DSP cores. This commit introduces core functionality required for all boards using an SC5xx SoC, such as: - SPL configuration - Required CPU hooks such as reset - Boot ROM interaction to load the stage 2 bootloader in the reference configuration. Other options are possible but not officially supported at this time - SoC-common configuration expected to be reused by all boards - Early initialization for system clocks and DDR controller Co-developed-by: Greg Malysa Signed-off-by: Greg Malysa Co-developed-by: Ian Roberts Signed-off-by: Ian Roberts Signed-off-by: Vasileios Bimpikas Signed-off-by: Utsav Agarwal Signed-off-by: Arturs Artamonovs Signed-off-by: Nathan Barrett-Morrison --- MAINTAINERS | 13 + arch/arm/Kconfig | 5 + arch/arm/Makefile | 1 + arch/arm/include/asm/arch-adi/sc5xx/sc5xx.h | 39 ++ arch/arm/include/asm/arch-adi/sc5xx/soc.h | 18 + arch/arm/include/asm/arch-adi/sc5xx/spl.h | 43 ++ arch/arm/mach-sc5xx/Kconfig | 475 +++++++++++++ arch/arm/mach-sc5xx/Makefile | 19 + arch/arm/mach-sc5xx/config.mk | 16 + arch/arm/mach-sc5xx/init/Makefile | 11 + arch/arm/mach-sc5xx/init/clkinit.c | 558 ++++++++++++++++ arch/arm/mach-sc5xx/init/clkinit.h | 18 + arch/arm/mach-sc5xx/init/dmcinit.c | 954 +++++++++++++++++++++++++++ arch/arm/mach-sc5xx/init/dmcinit.h | 31 + arch/arm/mach-sc5xx/init/mem/is43tr16512bl.h | 62 ++ arch/arm/mach-sc5xx/init/mem/mt41k128m16jt.h | 50 ++ arch/arm/mach-sc5xx/init/mem/mt41k512m16ha.h | 50 ++ arch/arm/mach-sc5xx/init/mem/mt47h128m16rt.h | 49 ++ arch/arm/mach-sc5xx/rcu.c | 22 + arch/arm/mach-sc5xx/sc57x.c | 32 + arch/arm/mach-sc5xx/sc58x.c | 32 + arch/arm/mach-sc5xx/sc59x.c | 43 ++ arch/arm/mach-sc5xx/sc59x_64.c | 97 +++ arch/arm/mach-sc5xx/soc.c | 179 +++++ arch/arm/mach-sc5xx/spl.c | 102 +++ include/env/adi/adi_boot.env | 122 ++++ 26 files changed, 3041 insertions(+) create mode 100644 arch/arm/include/asm/arch-adi/sc5xx/sc5xx.h create mode 100644 arch/arm/include/asm/arch-adi/sc5xx/soc.h create mode 100644 arch/arm/include/asm/arch-adi/sc5xx/spl.h create mode 100644 arch/arm/mach-sc5xx/Kconfig create mode 100644 arch/arm/mach-sc5xx/Makefile create mode 100644 arch/arm/mach-sc5xx/config.mk create mode 100644 arch/arm/mach-sc5xx/init/Makefile create mode 100644 arch/arm/mach-sc5xx/init/clkinit.c create mode 100644 arch/arm/mach-sc5xx/init/clkinit.h create mode 100644 arch/arm/mach-sc5xx/init/dmcinit.c create mode 100644 arch/arm/mach-sc5xx/init/dmcinit.h create mode 100644 arch/arm/mach-sc5xx/init/mem/is43tr16512bl.h create mode 100644 arch/arm/mach-sc5xx/init/mem/mt41k128m16jt.h create mode 100644 arch/arm/mach-sc5xx/init/mem/mt41k512m16ha.h create mode 100644 arch/arm/mach-sc5xx/init/mem/mt47h128m16rt.h create mode 100644 arch/arm/mach-sc5xx/rcu.c create mode 100644 arch/arm/mach-sc5xx/sc57x.c create mode 100644 arch/arm/mach-sc5xx/sc58x.c create mode 100644 arch/arm/mach-sc5xx/sc59x.c create mode 100644 arch/arm/mach-sc5xx/sc59x_64.c create mode 100644 arch/arm/mach-sc5xx/soc.c create mode 100644 arch/arm/mach-sc5xx/spl.c create mode 100644 include/env/adi/adi_boot.env diff --git a/MAINTAINERS b/MAINTAINERS index 0462ade..d184296 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -599,6 +599,19 @@ R: Marc Murphy S: Supported F: arch/arm/dts/am335x-sancloud* +ARM SC5XX +M: Nathan Barrett-Morrison +M: Greg Malysa +M: Ian Roberts +M: Vasileios Bimpikas +M: Utsav Agarwal +M: Arturs Artamonovs +S: Supported +T: git https://github.com/analogdevicesinc/lnxdsp-u-boot +F: arch/arm/include/asm/arch-adi/ +F: arch/arm/mach-sc5xx/ +F: include/env/adi/ + ARM SNAPDRAGON M: Caleb Connolly M: Neil Armstrong diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index a0842e1..ded35cb 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -1843,6 +1843,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 @@ -2276,6 +2279,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 + * Contact: Greg Malysa + */ +#ifndef ARCH_ADI_SC5XX_SC5XX_H +#define ARCH_ADI_SC5XX_SC5XX_H + +#include + +#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 + * Contact: Greg Malysa + */ + +#ifndef BOARD_ADI_COMMON_SOC_H +#define BOARD_ADI_COMMON_SOC_H + +#include + +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 + * Contact: Greg Malysa + */ +#ifndef ARCH_ADI_SC5XX_SPL_H +#define ARCH_ADI_SC5XX_SPL_H + +#include + +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 +# Contact: Greg Malysa +# + +# 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 +# Contact: Greg Malysa +# + +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 +# Contact: Greg Malysa +# + +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 +# Contact: Greg Malysa +# + +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 + * Contact: Greg Malysa + */ + +#include +#include +#include +#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(÷rs0); + CGU_Init(÷rs1); +} + +#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 + * Contact: Greg Malysa + */ + +#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 + * Contact: Greg Malysa + */ + +#include +#include +#include +#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 + * Contact: Greg Malysa + */ + +#ifndef DMCINIT_H_ +#define DMCINIT_H_ + +#include + +#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 + * Contact: Greg Malysa + */ + +#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 + * Contact: Greg Malysa + */ + +#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 + * Contact: Greg Malysa + */ + +#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 + * Contact: Greg Malysa + */ + +#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 + */ + +#include +#include + +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 + * Contact: Greg Malysa + */ + +#include +#include +#include + +#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 + * Contact: Greg Malysa + */ + +#include +#include +#include + +#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 + * Contact: Greg Malysa + */ + +#include +#include +#include + +#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 + * Contact: Greg Malysa + */ + +#include +#include +#include + +#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 + * Contact: Greg Malysa + */ + +#include +#include +#include +#include +#include + +#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 + * Contact: Greg Malysa + */ + +#include +#include +#include +#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/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 -- cgit v1.1 From 16fe1118da91fedb564cb323bec2eeab6bdddde8 Mon Sep 17 00:00:00 2001 From: Nathan Barrett-Morrison Date: Wed, 24 Apr 2024 20:04:01 -0400 Subject: drivers: clk: adi: Add in SC5XX-family clock driver This adds support for the SC5XX clock trees which are required for reading clock speeds on the SoCs. This is largely a port of the same support for Linux, which has not yet been submitted upstream. Co-developed-by: Greg Malysa Signed-off-by: Greg Malysa Co-developed-by: Ian Roberts Signed-off-by: Ian Roberts Signed-off-by: Vasileios Bimpikas Signed-off-by: Utsav Agarwal Signed-off-by: Arturs Artamonovs Signed-off-by: Nathan Barrett-Morrison --- MAINTAINERS | 1 + drivers/clk/Kconfig | 1 + drivers/clk/Makefile | 1 + drivers/clk/adi/Kconfig | 83 ++++++++ drivers/clk/adi/Makefile | 16 ++ drivers/clk/adi/clk-adi-pll.c | 93 +++++++++ drivers/clk/adi/clk-adi-sc57x.c | 206 +++++++++++++++++++ drivers/clk/adi/clk-adi-sc58x.c | 222 ++++++++++++++++++++ drivers/clk/adi/clk-adi-sc594.c | 231 +++++++++++++++++++++ drivers/clk/adi/clk-adi-sc598.c | 308 ++++++++++++++++++++++++++++ drivers/clk/adi/clk-shared.c | 48 +++++ drivers/clk/adi/clk.h | 123 +++++++++++ include/dt-bindings/clock/adi-sc5xx-clock.h | 271 ++++++++++++++++++++++++ 13 files changed, 1604 insertions(+) create mode 100644 drivers/clk/adi/Kconfig create mode 100644 drivers/clk/adi/Makefile create mode 100644 drivers/clk/adi/clk-adi-pll.c create mode 100644 drivers/clk/adi/clk-adi-sc57x.c create mode 100644 drivers/clk/adi/clk-adi-sc58x.c create mode 100644 drivers/clk/adi/clk-adi-sc594.c create mode 100644 drivers/clk/adi/clk-adi-sc598.c create mode 100644 drivers/clk/adi/clk-shared.c create mode 100644 drivers/clk/adi/clk.h create mode 100644 include/dt-bindings/clock/adi-sc5xx-clock.h diff --git a/MAINTAINERS b/MAINTAINERS index d184296..831f6da 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -610,6 +610,7 @@ 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: include/env/adi/ ARM SNAPDRAGON diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 017dd26..d8c619a 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 +# Contact: Greg Malysa +# + +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 +# Contact: Greg Malysa +# + +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 + * + * Ported from Linux: Nathan Barrett-Morrison + */ + +#include +#include +#include +#include +#include +#include +#include + +#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 + * + * Ported from Linux: Nathan Barrett-Morrison + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + * + * Ported from Linux: Nathan Barrett-Morrison + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + * + * Ported from Linux: Nathan Barrett-Morrison + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + * + * Ported from Linux: Nathan Barrett-Morrison + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + */ + +#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 + * + * Ported from Linux: Nathan Barrett-Morrison + */ + +#ifndef CLK_ADI_CLK_H +#define CLK_ADI_CLK_H + +#include +#include +#include + +#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/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 + * Contact: Greg Malysa + * + */ + +#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 -- cgit v1.1 From 8af2e141e1d988794c1acd682ece4d1437acf296 Mon Sep 17 00:00:00 2001 From: Nathan Barrett-Morrison Date: Wed, 24 Apr 2024 20:04:02 -0400 Subject: drivers: serial: Add in UART for ADI SC5XX-family processors Co-developed-by: Greg Malysa Signed-off-by: Greg Malysa Co-developed-by: Ian Roberts Signed-off-by: Ian Roberts Signed-off-by: Vasileios Bimpikas Signed-off-by: Utsav Agarwal Signed-off-by: Arturs Artamonovs Signed-off-by: Nathan Barrett-Morrison --- MAINTAINERS | 1 + drivers/serial/Makefile | 1 + drivers/serial/serial_adi_uart4.c | 225 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 227 insertions(+) create mode 100644 drivers/serial/serial_adi_uart4.c diff --git a/MAINTAINERS b/MAINTAINERS index 831f6da..1de9cb7 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -611,6 +611,7 @@ 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: include/env/adi/ ARM SNAPDRAGON 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 + * Contact: Greg Malysa + * + */ + +#include +#include +#include +#include +#include +#include + +/* + * 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, ®s->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(®s->status) & DR) ? 1 : 0; + else + return (readl(®s->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(®s->rbr); + writel(-1, ®s->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, ®s->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, ®s->control); + + writel(-1, ®s->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, +}; -- cgit v1.1 From 8691ffa5c06f4a171d1c418cdb5711334dd2470d Mon Sep 17 00:00:00 2001 From: Greg Malysa Date: Wed, 24 Apr 2024 20:04:03 -0400 Subject: drivers: timer: Add in driver support for ADI SC5XX-family GP timer peripheral Co-developed-by: Nathan Barrett-Morrison Signed-off-by: Nathan Barrett-Morrison Co-developed-by: Angelo Dureghello Signed-off-by: Angelo Dureghello Co-developed-by: Ian Roberts Signed-off-by: Ian Roberts Signed-off-by: Greg Malysa --- MAINTAINERS | 1 + drivers/timer/Kconfig | 8 +++ drivers/timer/Makefile | 1 + drivers/timer/adi_sc5xx_timer.c | 145 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 155 insertions(+) create mode 100644 drivers/timer/adi_sc5xx_timer.c diff --git a/MAINTAINERS b/MAINTAINERS index 1de9cb7..71b6d15 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -612,6 +612,7 @@ 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 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 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 + * Additional Contact: Nathan Barrett-Morrison + * + * dm timer implementation for ADI ADSP-SC5xx SoCs + * + */ + +#include +#include +#include +#include +#include +#include + +/* + * 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, +}; -- cgit v1.1