diff options
author | Angelos Mouzakitis <a.mouzakitis@virtualopensystems.com> | 2023-10-10 14:33:42 +0000 |
---|---|---|
committer | Angelos Mouzakitis <a.mouzakitis@virtualopensystems.com> | 2023-10-10 14:33:42 +0000 |
commit | af1a266670d040d2f4083ff309d732d648afba2a (patch) | |
tree | 2fc46203448ddcc6f81546d379abfaeb323575e9 /roms/u-boot/arch/arm/mach-imx | |
parent | e02cda008591317b1625707ff8e115a4841aa889 (diff) |
Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec
Diffstat (limited to 'roms/u-boot/arch/arm/mach-imx')
95 files changed, 28525 insertions, 0 deletions
diff --git a/roms/u-boot/arch/arm/mach-imx/Kconfig b/roms/u-boot/arch/arm/mach-imx/Kconfig new file mode 100644 index 000000000..26bfc5ccc --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/Kconfig @@ -0,0 +1,174 @@ +config HAS_CAAM + bool + +config IMX_CONFIG + string + +config ROM_UNIFIED_SECTIONS + bool + +config SYSCOUNTER_TIMER + bool + +config GPT_TIMER + bool + +config IMX_RDC + bool "i.MX Resource domain controller driver" + depends on ARCH_MX6 || ARCH_MX7 + help + i.MX Resource domain controller is used to assign masters + and peripherals to differet domains. This can be used to + isolate resources. + +config IMX_BOOTAUX + bool "Support boot auxiliary core" + depends on ARCH_MX7 || ARCH_MX6 || ARCH_VF610 || ARCH_IMX8M + help + bootaux [addr] to boot auxiliary core. + +config IMX_MODULE_FUSE + bool "i.MX Module Fuse" + depends on ARCH_MX6 + help + i.MX module fuse to runtime disable some driver, including + Linux OS device node. + +config USE_IMXIMG_PLUGIN + bool "Use imximage plugin code" + depends on ARCH_MX7 || ARCH_MX6 || ARCH_MX7ULP + help + i.MX6/7 supports DCD and Plugin. Enable this configuration + to use Plugin, otherwise DCD will be used. + +config IMX_HAB + bool "Support i.MX HAB features" + depends on ARCH_MX7 || ARCH_MX6 || ARCH_MX5 || ARCH_IMX8M + select FSL_CAAM if HAS_CAAM + imply CMD_DEKBLOB if HAS_CAAM + help + This option enables the support for secure boot (HAB). + See doc/imx/habv4/* for more details. + +config CSF_SIZE + hex "Maximum size for Command Sequence File (CSF) binary" + depends on IMX_HAB + default 0x2000 if ARCH_IMX8M + default 0x2060 + help + Define the maximum size for Command Sequence File (CSF) binary + this information is used to define the image boot data. + +config CMD_BMODE + bool "Support the 'bmode' command" + default y + depends on ARCH_MX7 || ARCH_MX6 || ARCH_MX5 + help + This enables the 'bmode' (bootmode) command for forcing + a boot from specific media. + + This is useful for forcing the ROM's usb downloader to + activate upon a watchdog reset which is nice when iterating + on U-Boot. Using the reset button or running bmode normal + will set it back to normal. This command currently + supports i.MX53 and i.MX6. + +config CMD_DEKBLOB + bool "Support the 'dek_blob' command" + select IMX_CAAM_DEK_ENCAP if ARCH_MX6 || ARCH_MX7 || ARCH_MX7ULP + select IMX_OPTEE_DEK_ENCAP if ARCH_IMX8M + select IMX_SECO_DEK_ENCAP if ARCH_IMX8 + help + This enables the 'dek_blob' command which is used with the + Freescale secure boot mechanism. This command encapsulates and + creates a blob of data. See also CMD_BLOB and doc/imx/habv4/* for + more information. + +config IMX_CAAM_DEK_ENCAP + bool "Support the DEK blob encapsulation with CAAM U-Boot driver" + help + This enables the DEK blob encapsulation with the U-Boot CAAM driver. + This option is only available on imx6, imx7 and imx7ulp. + +config IMX_OPTEE_DEK_ENCAP + select TEE + select OPTEE + bool "Support the DEK blob encapsulation with OP-TEE" + help + This enabled the DEK blob encapsulation with OP-TEE. The communication + with OP-TEE is done through a SMC call and OP-TEE shared memory. This + option is available on imx8mm. + +config IMX_SECO_DEK_ENCAP + bool "Support the DEK blob encapsulation with SECO" + help + This enabled the DEK blob encapsulation with the SECO API. This option + is only available on imx8. + +config CMD_PRIBLOB + bool "Support the set_priblob_bitfield command" + depends on HAS_CAAM && IMX_HAB + help + This option enables the priblob command which can be used + to set the priblob setting to 0x3. + +config CMD_HDMIDETECT + bool "Support the 'hdmidet' command" + help + This enables the 'hdmidet' command which detects if an HDMI monitor + is connected. + +config CMD_NANDBCB + bool "i.MX6 NAND Boot Control Block(BCB) command" + depends on MTD_RAW_NAND && CMD_MTDPARTS + select BCH if MX6UL || MX6ULL + default y if ((ARCH_MX6 || ARCH_MX7 || ARCH_IMX8M) && NAND_MXS) + help + Unlike normal 'nand write/erase' commands, this command update + Boot Control Block(BCB) for i.MX6 platform NAND IP's. + + This is similar to kobs-ng, which is used in Linux as separate + rootfs package. + +config FSL_MFGPROT + bool "Support the 'mfgprot' command" + depends on IMX_HAB && ARCH_MX7 + help + This option enables the manufacturing protection command + which can be used has a protection feature for Manufacturing + process. With this tool is possible to authenticate the + chip to the OEM's server. + +config NXP_BOARD_REVISION + bool "Read NXP board revision from fuses" + depends on ARCH_MX6 || ARCH_MX7 + help + NXP boards based on i.MX6/7 contain the board revision information + stored in the fuses. Select this option if you want to be able to + retrieve the board revision information. + +config DDRMC_VF610_CALIBRATION + bool "Enable DDRMC (DDR3) on-chip calibration" + depends on ARCH_VF610 + help + Vybrid (vf610) SoC provides some on-chip facility to tune the DDR3 + memory parameters. Select this option if you want to calculate them + at boot time. + NOTE: + NXP does NOT recommend to perform this calibration at each boot. One + shall perform it on a new PCB and then use those values to program + the ddrmc_cr_setting on relevant board file. + +config SPL_IMX_ROMAPI_LOADADDR + hex "Default load address to load image through ROM API" + depends on IMX8MN || IMX8MP + +config IMX_DCD_ADDR + hex "DCD Blocks location on the image" + default 0x00910000 if !ARCH_MX7ULP + default 0x2f010000 if ARCH_MX7ULP + help + Indicates where the Device Configuration Data, a binary table used by + the ROM code to configure the device at early boot stage, is located. + This information is shared with the user via mkimage -l just so the + image can be signed. diff --git a/roms/u-boot/arch/arm/mach-imx/Makefile b/roms/u-boot/arch/arm/mach-imx/Makefile new file mode 100644 index 000000000..82aa39dee --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/Makefile @@ -0,0 +1,234 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright 2000-2006 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# (C) Copyright 2011 Freescale Semiconductor, Inc. + +ifeq ($(SOC),$(filter $(SOC),mx25 mx35 mx5 mx6 mx7 imx8m vf610)) +obj-y = iomux-v3.o +endif + +ifeq ($(SOC),$(filter $(SOC),imx8m)) +ifneq ($(CONFIG_SPL_BUILD),y) +obj-$(CONFIG_IMX_BOOTAUX) += imx_bootaux.o +endif +obj-$(CONFIG_ENV_IS_IN_MMC) += mmc_env.o +obj-$(CONFIG_FEC_MXC) += mac.o +obj-$(CONFIG_SYS_I2C_MXC) += i2c-mxv7.o +obj-$(CONFIG_IMX_HAB) += hab.o +obj-y += cpu.o +endif + +ifeq ($(SOC),$(filter $(SOC),mx5 mx6)) +obj-y += cpu.o speed.o +ifneq ($(CONFIG_MX51),y) +obj-y += mmdc_size.o +endif +obj-$(CONFIG_GPT_TIMER) += timer.o +obj-$(CONFIG_SYS_I2C_MXC) += i2c-mxv7.o +endif +ifeq ($(SOC),$(filter $(SOC),mx7 mx6 mxs imx8m imx8 imxrt)) +obj-y += misc.o +obj-$(CONFIG_CMD_PRIBLOB) += priblob.o +obj-$(CONFIG_SPL_BUILD) += spl.o +endif +ifeq ($(SOC),$(filter $(SOC),mx7)) +obj-y += cpu.o +obj-$(CONFIG_SYS_I2C_MXC) += i2c-mxv7.o +obj-$(CONFIG_ENV_IS_IN_MMC) += mmc_env.o +obj-$(CONFIG_FSL_MFGPROT) += cmd_mfgprot.o +endif +ifeq ($(SOC),$(filter $(SOC),mx5 mx6 mx7)) +obj-$(CONFIG_IMX_VIDEO_SKIP) += video.o +endif +ifeq ($(SOC),$(filter $(SOC),mx6 mx7)) +obj-y += cache.o init.o +obj-$(CONFIG_FEC_MXC) += mac.o +obj-$(CONFIG_IMX_RDC) += rdc-sema.o +ifneq ($(CONFIG_SPL_BUILD),y) +obj-$(CONFIG_IMX_BOOTAUX) += imx_bootaux.o +endif +obj-$(CONFIG_SATA) += sata.o +obj-$(CONFIG_IMX_HAB) += hab.o +obj-$(CONFIG_SYSCOUNTER_TIMER) += syscounter.o +endif +ifeq ($(SOC),$(filter $(SOC),mx7ulp)) +obj-y += cache.o mmdc_size.o +obj-$(CONFIG_IMX_HAB) += hab.o +endif +ifeq ($(SOC),$(filter $(SOC),vf610)) +obj-y += ddrmc-vf610.o +obj-$(CONFIG_DDRMC_VF610_CALIBRATION) += ddrmc-vf610-calibration.o +endif +ifneq ($(CONFIG_SPL_BUILD),y) +obj-$(CONFIG_CMD_BMODE) += cmd_bmode.o +obj-$(CONFIG_CMD_HDMIDETECT) += cmd_hdmidet.o +obj-$(CONFIG_CMD_DEKBLOB) += cmd_dek.o +obj-$(CONFIG_CMD_NANDBCB) += cmd_nandbcb.o +endif + +PLUGIN = board/$(BOARDDIR)/plugin + +ifeq ($(CONFIG_USE_IMXIMG_PLUGIN),y) + +$(PLUGIN).o: $(PLUGIN).S FORCE + $(Q)mkdir -p $(dir $@) + $(call if_changed_dep,as_o_S) + +$(PLUGIN).bin: $(PLUGIN).o FORCE + $(Q)mkdir -p $(dir $@) + $(OBJCOPY) -O binary --gap-fill 0xff $< $@ +else + +$(PLUGIN).bin: + +endif + +quiet_cmd_cpp_cfg = CFGS $@ + cmd_cpp_cfg = $(CPP) $(cpp_flags) -x c -o $@ $< + +# mkimage source config file +IMX_CONFIG = $(CONFIG_IMX_CONFIG:"%"=%) + +# How to create a cpp processed config file, they all use the same source +%.cfgout: $(IMX_CONFIG) FORCE + $(Q)mkdir -p $(dir $@) + $(call if_changed_dep,cpp_cfg) + +IMX_CONTAINER_CFG = $(CONFIG_IMX_CONTAINER_CFG:"%"=%) +container.cfg: $(IMX_CONTAINER_CFG) FORCE + $(Q)mkdir -p $(dir $@) + $(call if_changed_dep,cpp_cfg) + +ifeq ($(CONFIG_ARCH_IMX8), y) +CNTR_DEPFILES := $(srctree)/tools/imx_cntr_image.sh +IMAGE_TYPE := imx8image +ifeq ($(CONFIG_SPL_BUILD),y) +SPL_DEPFILE_EXISTS := $(shell $(CPP) $(cpp_flags) -x c -o spl/u-boot-spl.cfgout $(srctree)/$(IMX_CONFIG); if [ -f spl/u-boot-spl.cfgout ]; then $(CNTR_DEPFILES) spl/u-boot-spl.cfgout; echo $$?; fi) +endif +DEPFILE_EXISTS := $(shell $(CPP) $(cpp_flags) -x c -o u-boot-dtb.cfgout $(srctree)/$(IMX_CONFIG); if [ -f u-boot-dtb.cfgout ]; then $(CNTR_DEPFILES) u-boot-dtb.cfgout; echo $$?; fi) +else ifeq ($(CONFIG_ARCH_IMX8M), y) +IMAGE_TYPE := imx8mimage +IMX8M_DEPFILES := $(srctree)/tools/imx8m_image.sh +DEPFILE_EXISTS := $(shell $(CPP) $(cpp_flags) -x c -o spl/u-boot-spl.cfgout $(srctree)/$(IMX_CONFIG);if [ -f spl/u-boot-spl.cfgout ]; then $(IMX8M_DEPFILES) spl/u-boot-spl.cfgout 0; echo $$?; fi) +else +IMAGE_TYPE := imximage +DEPFILE_EXISTS := 0 +endif + +MKIMAGEFLAGS_u-boot.imx = -n $(filter-out $(PLUGIN).bin $< $(PHONY),$^) \ + -T $(IMAGE_TYPE) -e $(CONFIG_SYS_TEXT_BASE) +u-boot.imx: MKIMAGEOUTPUT = u-boot.imx.log + +u-boot.imx: u-boot.bin u-boot.cfgout $(PLUGIN).bin FORCE + $(call if_changed,mkimage) + +ifeq ($(CONFIG_MULTI_DTB_FIT),y) +MKIMAGEFLAGS_u-boot-dtb.imx = -n $(filter-out $(PLUGIN).bin $< $(PHONY),$^) \ + -T $(IMAGE_TYPE) -e $(CONFIG_SYS_TEXT_BASE) +u-boot-dtb.imx: MKIMAGEOUTPUT = u-boot-dtb.imx.log + +u-boot-dtb.imx: u-boot-fit-dtb.bin u-boot-dtb.cfgout $(PLUGIN).bin FORCE +ifeq ($(DEPFILE_EXISTS),0) + $(call if_changed,mkimage) +endif +else ifeq ($(CONFIG_OF_SEPARATE),y) +MKIMAGEFLAGS_u-boot-dtb.imx = -n $(filter-out $(PLUGIN).bin $< $(PHONY),$^) \ + -T $(IMAGE_TYPE) -e $(CONFIG_SYS_TEXT_BASE) +u-boot-dtb.imx: MKIMAGEOUTPUT = u-boot-dtb.imx.log + +u-boot-dtb.imx: u-boot-dtb.bin u-boot-dtb.cfgout $(PLUGIN).bin FORCE +ifeq ($(DEPFILE_EXISTS),0) + $(call if_changed,mkimage) +endif +endif + +ifdef CONFIG_ARM64 +ifeq ($(CONFIG_ARCH_IMX8M), y) +SPL: + +MKIMAGEFLAGS_flash.bin = -n spl/u-boot-spl.cfgout \ + -T $(IMAGE_TYPE) -e $(CONFIG_SPL_TEXT_BASE) +flash.bin: MKIMAGEOUTPUT = flash.log + +spl/u-boot-spl-ddr.bin: spl/u-boot-spl.bin spl/u-boot-spl.cfgout FORCE +ifeq ($(DEPFILE_EXISTS),0) + $(IMX8M_DEPFILES) spl/u-boot-spl.cfgout 1 +endif + +flash.bin: spl/u-boot-spl-ddr.bin u-boot.itb FORCE + $(call if_changed,mkimage) +endif + +ifeq ($(CONFIG_ARCH_IMX8), y) +SPL: + +MKIMAGEFLAGS_flash.bin = -n spl/u-boot-spl.cfgout -T $(IMAGE_TYPE) -e 0x100000 +flash.bin: MKIMAGEOUTPUT = flash.log + +MKIMAGEFLAGS_u-boot.cnt = -n container.cfg -T $(IMAGE_TYPE) -e 0x100000 +u-boot.cnt: MKIMAGEOUTPUT = u-boot.cnt.log + +ifeq ($(CONFIG_SPL_LOAD_IMX_CONTAINER), y) +u-boot.cnt: u-boot.bin container.cfg FORCE + $(call if_changed,mkimage) +flash.bin: spl/u-boot-spl.bin FORCE + $(call if_changed,mkimage) + @flashbin_size=`wc -c flash.bin | awk '{print $$1}'`; \ + pad_cnt=$$(((flashbin_size + 0x400 - 1) / 0x400)); \ + echo "append u-boot.cnt at $$pad_cnt KB"; \ + dd if=u-boot.cnt of=flash.bin bs=1K seek=$$pad_cnt; +else +flash.bin: spl/u-boot-spl.bin FORCE + $(call if_changed,mkimage) +endif +endif + +else +MKIMAGEFLAGS_SPL = -n $(filter-out $(PLUGIN).bin $< $(PHONY),$^) \ + -T $(IMAGE_TYPE) -e $(CONFIG_SPL_TEXT_BASE) +SPL: MKIMAGEOUTPUT = SPL.log + +SPL: spl/u-boot-spl.bin spl/u-boot-spl.cfgout $(PLUGIN).bin FORCE + $(call if_changed,mkimage) + +MKIMAGEFLAGS_u-boot.uim = -A arm -O U-Boot -a $(CONFIG_SYS_TEXT_BASE) \ + -e $(CONFIG_SYS_TEXT_BASE) -C none -T firmware + +u-boot.uim: u-boot.bin FORCE + $(call if_changed,mkimage) + +OBJCOPYFLAGS += -I binary -O binary --pad-to=$(CONFIG_SPL_PAD_TO) +append = cat $(filter-out $< $(PHONY), $^) >> $@ + +quiet_cmd_pad_cat = CAT $@ +cmd_pad_cat = $(cmd_objcopy) && $(append) || rm -f $@ + +u-boot-with-spl.imx: SPL $(if $(CONFIG_OF_SEPARATE),u-boot.img,u-boot.uim) FORCE + $(call if_changed,pad_cat) + +u-boot-with-nand-spl.imx: spl/u-boot-nand-spl.imx $(if $(CONFIG_OF_SEPARATE),u-boot.img,u-boot.uim) FORCE + $(call if_changed,pad_cat) + +quiet_cmd_u-boot-nand-spl_imx = GEN $@ +cmd_u-boot-nand-spl_imx = (printf '\000\000\000\000\106\103\102\040\001' && \ + dd bs=1015 count=1 if=/dev/zero 2>/dev/null) | cat - $< > $@ + +spl/u-boot-nand-spl.imx: SPL FORCE + $(call if_changed,u-boot-nand-spl_imx) +endif + +targets += $(addprefix ../../../,SPL spl/u-boot-spl.cfgout u-boot-dtb.cfgout u-boot.cfgout u-boot.uim spl/u-boot-nand-spl.imx) + +obj-$(CONFIG_ARM64) += lowlevel.o + +obj-$(CONFIG_MX5) += mx5/ +obj-$(CONFIG_MX6) += mx6/ +obj-$(CONFIG_MX7) += mx7/ +obj-$(CONFIG_ARCH_MX7ULP) += mx7ulp/ +obj-$(CONFIG_IMX8M) += imx8m/ +obj-$(CONFIG_ARCH_IMX8) += imx8/ +obj-$(CONFIG_ARCH_IMXRT) += imxrt/ + +obj-$(CONFIG_SPL_BOOTROM_SUPPORT) += spl_imx_romapi.o diff --git a/roms/u-boot/arch/arm/mach-imx/cache.c b/roms/u-boot/arch/arm/mach-imx/cache.c new file mode 100644 index 000000000..4e3b49a3f --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/cache.c @@ -0,0 +1,152 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2015 Freescale Semiconductor, Inc. + */ + +#include <common.h> +#include <cpu_func.h> +#include <asm/armv7.h> +#include <asm/cache.h> +#include <asm/pl310.h> +#include <asm/io.h> +#include <asm/mach-imx/sys_proto.h> + +static void enable_ca7_smp(void) +{ + u32 val; + + /* Read MIDR */ + asm volatile ("mrc p15, 0, %0, c0, c0, 0\n\t" : "=r"(val)); + val = (val >> 4); + val &= 0xf; + + /* Only set the SMP for Cortex A7 */ + if (val == 0x7) { + /* Read auxiliary control register */ + asm volatile ("mrc p15, 0, %0, c1, c0, 1\n\t" : "=r"(val)); + + if (val & (1 << 6)) + return; + + /* Enable SMP */ + val |= (1 << 6); + + /* Write auxiliary control register */ + asm volatile ("mcr p15, 0, %0, c1, c0, 1\n\t" : : "r"(val)); + + DSB; + ISB; + } +} + +#if !CONFIG_IS_ENABLED(SYS_DCACHE_OFF) +void enable_caches(void) +{ +#if defined(CONFIG_SYS_ARM_CACHE_WRITETHROUGH) + enum dcache_option option = DCACHE_WRITETHROUGH; +#else + enum dcache_option option = DCACHE_WRITEBACK; +#endif + /* Avoid random hang when download by usb */ + invalidate_dcache_all(); + + /* Set ACTLR.SMP bit for Cortex-A7 */ + enable_ca7_smp(); + + /* Enable D-cache. I-cache is already enabled in start.S */ + dcache_enable(); + + /* Enable caching on OCRAM and ROM */ + mmu_set_region_dcache_behaviour(ROMCP_ARB_BASE_ADDR, + ROMCP_ARB_END_ADDR, + option); + mmu_set_region_dcache_behaviour(IRAM_BASE_ADDR, + IRAM_SIZE, + option); +} +#else +void enable_caches(void) +{ + /* + * Set ACTLR.SMP bit for Cortex-A7, even if the caches are + * disabled by u-boot + */ + enable_ca7_smp(); + + puts("WARNING: Caches not enabled\n"); +} +#endif + +#ifndef CONFIG_SYS_L2CACHE_OFF +#ifdef CONFIG_SYS_L2_PL310 +#define IOMUXC_GPR11_L2CACHE_AS_OCRAM 0x00000002 +void v7_outer_cache_enable(void) +{ + struct pl310_regs *const pl310 = (struct pl310_regs *)L2_PL310_BASE; + struct iomuxc *iomux = (struct iomuxc *)IOMUXC_BASE_ADDR; + unsigned int val, cache_id; + + + /* + * Must disable the L2 before changing the latency parameters + * and auxiliary control register. + */ + clrbits_le32(&pl310->pl310_ctrl, L2X0_CTRL_EN); + + /* + * Set bit 22 in the auxiliary control register. If this bit + * is cleared, PL310 treats Normal Shared Non-cacheable + * accesses as Cacheable no-allocate. + */ + setbits_le32(&pl310->pl310_aux_ctrl, L310_SHARED_ATT_OVERRIDE_ENABLE); + + if (is_mx6sl() || is_mx6sll()) { + val = readl(&iomux->gpr[11]); + if (val & IOMUXC_GPR11_L2CACHE_AS_OCRAM) { + /* L2 cache configured as OCRAM, reset it */ + val &= ~IOMUXC_GPR11_L2CACHE_AS_OCRAM; + writel(val, &iomux->gpr[11]); + } + } + + writel(0x132, &pl310->pl310_tag_latency_ctrl); + writel(0x132, &pl310->pl310_data_latency_ctrl); + + val = readl(&pl310->pl310_prefetch_ctrl); + + /* Turn on the L2 I/D prefetch, double linefill */ + /* Set prefetch offset with any value except 23 as per errata 765569 */ + val |= 0x7000000f; + + /* + * The L2 cache controller(PL310) version on the i.MX6D/Q is r3p1-50rel0 + * The L2 cache controller(PL310) version on the i.MX6DL/SOLO/SL/SX/DQP + * is r3p2. + * But according to ARM PL310 errata: 752271 + * ID: 752271: Double linefill feature can cause data corruption + * Fault Status: Present in: r3p0, r3p1, r3p1-50rel0. Fixed in r3p2 + * Workaround: The only workaround to this erratum is to disable the + * double linefill feature. This is the default behavior. + */ + cache_id = readl(&pl310->pl310_cache_id); + if (((cache_id & L2X0_CACHE_ID_PART_MASK) == L2X0_CACHE_ID_PART_L310) + && ((cache_id & L2X0_CACHE_ID_RTL_MASK) < L2X0_CACHE_ID_RTL_R3P2)) + val &= ~(1 << 30); + writel(val, &pl310->pl310_prefetch_ctrl); + + val = readl(&pl310->pl310_power_ctrl); + val |= L2X0_DYNAMIC_CLK_GATING_EN; + val |= L2X0_STNDBY_MODE_EN; + writel(val, &pl310->pl310_power_ctrl); + + setbits_le32(&pl310->pl310_ctrl, L2X0_CTRL_EN); +} + +void v7_outer_cache_disable(void) +{ + struct pl310_regs *const pl310 = (struct pl310_regs *)L2_PL310_BASE; + + clrbits_le32(&pl310->pl310_ctrl, L2X0_CTRL_EN); +} +#endif /* !CONFIG_SYS_L2_PL310 */ +#endif /* !CONFIG_SYS_L2CACHE_OFF */ diff --git a/roms/u-boot/arch/arm/mach-imx/cmd_bmode.c b/roms/u-boot/arch/arm/mach-imx/cmd_bmode.c new file mode 100644 index 000000000..cb317499d --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/cmd_bmode.c @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2012 Boundary Devices Inc. + */ +#include <common.h> +#include <linux/errno.h> +#include <asm/io.h> +#include <asm/mach-imx/boot_mode.h> +#include <malloc.h> +#include <command.h> + +static const struct boot_mode *modes[2]; + +static const struct boot_mode *search_modes(char *arg) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(modes); i++) { + const struct boot_mode *p = modes[i]; + if (p) { + while (p->name) { + if (!strcmp(p->name, arg)) + return p; + p++; + } + } + } + return NULL; +} + +static int create_usage(char *dest) +{ + int i; + int size = 0; + + for (i = 0; i < ARRAY_SIZE(modes); i++) { + const struct boot_mode *p = modes[i]; + if (p) { + while (p->name) { + int len = strlen(p->name); + if (dest) { + memcpy(dest, p->name, len); + dest += len; + *dest++ = '|'; + } + size += len + 1; + p++; + } + } + } + if (dest) + memcpy(dest - 1, " [noreset]", 11); /* include trailing 0 */ + size += 10; + + if (dest) + memcpy(dest - 1, "\nbmode - getprisec", 19); + size += 18; + + return size; +} + +__weak int boot_mode_getprisec(void) +{ + return 0; +} + +static int do_boot_mode(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + const struct boot_mode *p; + int reset_requested = 1; + + if (argc < 2) + return CMD_RET_USAGE; + if (!strcmp(argv[1], "getprisec")) + return boot_mode_getprisec(); + p = search_modes(argv[1]); + if (!p) + return CMD_RET_USAGE; + if (argc == 3) { + if (strcmp(argv[2], "noreset")) + return CMD_RET_USAGE; + reset_requested = 0; + } + + boot_mode_apply(p->cfg_val); + if (reset_requested && p->cfg_val) + do_reset(NULL, 0, 0, NULL); + return 0; +} + +U_BOOT_CMD( + bmode, 3, 0, do_boot_mode, + NULL, + ""); + +void add_board_boot_modes(const struct boot_mode *p) +{ + int size; + char *dest; + + struct cmd_tbl *entry = ll_entry_get(struct cmd_tbl, bmode, cmd); + + if (entry->usage) { + free(entry->usage); + entry->usage = NULL; + } + + modes[0] = p; + modes[1] = soc_boot_modes; + size = create_usage(NULL); + dest = malloc(size); + if (dest) { + create_usage(dest); + entry->usage = dest; + } +} diff --git a/roms/u-boot/arch/arm/mach-imx/cmd_dek.c b/roms/u-boot/arch/arm/mach-imx/cmd_dek.c new file mode 100644 index 000000000..b10ead194 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/cmd_dek.c @@ -0,0 +1,320 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2008-2015 Freescale Semiconductor, Inc. + * + * Command for encapsulating DEK blob + */ + +#include <common.h> +#include <command.h> +#include <log.h> +#include <malloc.h> +#include <asm/byteorder.h> +#include <linux/compiler.h> +#include <fsl_sec.h> +#include <asm/arch/clock.h> +#include <mapmem.h> +#include <tee.h> +#ifdef CONFIG_IMX_SECO_DEK_ENCAP +#include <asm/arch/sci/sci.h> +#include <asm/arch/image.h> +#endif +#include <cpu_func.h> + +/** +* blob_dek() - Encapsulate the DEK as a blob using CAM's Key +* @src: - Address of data to be encapsulated +* @dst: - Desination address of encapsulated data +* @len: - Size of data to be encapsulated +* +* Returns zero on success,and negative on error. +*/ +#ifdef CONFIG_IMX_CAAM_DEK_ENCAP +static int blob_encap_dek(u32 src_addr, u32 dst_addr, u32 len) +{ + u8 *src_ptr, *dst_ptr; + + src_ptr = map_sysmem(src_addr, len / 8); + dst_ptr = map_sysmem(dst_addr, BLOB_SIZE(len / 8)); + + hab_caam_clock_enable(1); + + u32 out_jr_size = sec_in32(CONFIG_SYS_FSL_JR0_ADDR + + FSL_CAAM_ORSR_JRa_OFFSET); + if (out_jr_size != FSL_CAAM_MAX_JR_SIZE) + sec_init(); + + if (!((len == 128) | (len == 192) | (len == 256))) { + debug("Invalid DEK size. Valid sizes are 128, 192 and 256b\n"); + return -1; + } + + len /= 8; + return blob_dek(src_ptr, dst_ptr, len); +} +#endif /* CONFIG_IMX_CAAM_DEK_ENCAP */ + +#ifdef CONFIG_IMX_OPTEE_DEK_ENCAP + +#define PTA_DEK_BLOB_PTA_UUID {0xef477737, 0x0db1, 0x4a9d, \ + {0x84, 0x37, 0xf2, 0xf5, 0x35, 0xc0, 0xbd, 0x92} } + +#define OPTEE_BLOB_HDR_SIZE 8 + +static int blob_encap_dek(u32 src_addr, u32 dst_addr, u32 len) +{ + struct udevice *dev = NULL; + struct tee_shm *shm_input, *shm_output; + struct tee_open_session_arg arg = {0}; + struct tee_invoke_arg arg_func = {0}; + const struct tee_optee_ta_uuid uuid = PTA_DEK_BLOB_PTA_UUID; + struct tee_param param[4] = {0}; + int ret; + + /* Get tee device */ + dev = tee_find_device(NULL, NULL, NULL, NULL); + if (!dev) { + printf("Cannot get OP-TEE device\n"); + return -1; + } + + /* Set TA UUID */ + tee_optee_ta_uuid_to_octets(arg.uuid, &uuid); + + /* Open TA session */ + ret = tee_open_session(dev, &arg, 0, NULL); + if (ret < 0) { + printf("Cannot open session with PTA Blob 0x%X\n", ret); + return -1; + } + + /* Allocate shared input and output buffers for TA */ + ret = tee_shm_register(dev, (void *)(ulong)src_addr, len / 8, 0x0, &shm_input); + if (ret < 0) { + printf("Cannot register input shared memory 0x%X\n", ret); + goto error; + } + + ret = tee_shm_register(dev, (void *)(ulong)dst_addr, + BLOB_SIZE(len / 8) + OPTEE_BLOB_HDR_SIZE, + 0x0, &shm_output); + if (ret < 0) { + printf("Cannot register output shared memory 0x%X\n", ret); + goto error; + } + + param[0].u.memref.shm = shm_input; + param[0].u.memref.size = shm_input->size; + param[0].attr = TEE_PARAM_ATTR_TYPE_MEMREF_INPUT; + param[1].u.memref.shm = shm_output; + param[1].u.memref.size = shm_output->size; + param[1].attr = TEE_PARAM_ATTR_TYPE_MEMREF_OUTPUT; + param[2].attr = TEE_PARAM_ATTR_TYPE_NONE; + param[3].attr = TEE_PARAM_ATTR_TYPE_NONE; + + arg_func.func = 0; + arg_func.session = arg.session; + + /* Generate DEK blob */ + arg_func.session = arg.session; + ret = tee_invoke_func(dev, &arg_func, 4, param); + if (ret < 0) + printf("Cannot generate Blob with PTA DEK Blob 0x%X\n", ret); + +error: + /* Free shared memory */ + tee_shm_free(shm_input); + tee_shm_free(shm_output); + + /* Close session */ + ret = tee_close_session(dev, arg.session); + if (ret < 0) + printf("Cannot close session with PTA DEK Blob 0x%X\n", ret); + + return ret; +} +#endif /* CONFIG_IMX_OPTEE_DEK_ENCAP */ +#ifdef CONFIG_IMX_SECO_DEK_ENCAP + +#define DEK_BLOB_KEY_ID 0x0 + +#define AHAB_PRIVATE_KEY 0x81 +#define AHAB_VERSION 0x00 +#define AHAB_MODE_CBC 0x67 +#define AHAB_ALG_AES 0x55 +#define AHAB_128_AES_KEY 0x10 +#define AHAB_192_AES_KEY 0x18 +#define AHAB_256_AES_KEY 0x20 +#define AHAB_FLAG_KEK 0x80 +#define AHAB_DEK_BLOB 0x01 + +#define DEK_BLOB_HDR_SIZE 8 +#define SECO_PT 2U + +static int blob_encap_dek(u32 src_addr, u32 dst_addr, u32 len) +{ + sc_err_t err; + sc_rm_mr_t mr_input, mr_output; + struct generate_key_blob_hdr hdr; + u8 in_size, out_size; + u8 *src_ptr, *dst_ptr; + int ret = 0; + int i; + + /* Set sizes */ + in_size = sizeof(struct generate_key_blob_hdr) + len / 8; + out_size = BLOB_SIZE(len / 8) + DEK_BLOB_HDR_SIZE; + + /* Get src and dst virtual addresses */ + src_ptr = map_sysmem(src_addr, in_size); + dst_ptr = map_sysmem(dst_addr, out_size); + + /* Check addr input */ + if (!(src_ptr && dst_ptr)) { + debug("src_addr or dst_addr invalid\n"); + return -1; + } + + /* Build key header */ + hdr.version = AHAB_VERSION; + hdr.length_lsb = sizeof(struct generate_key_blob_hdr) + len / 8; + hdr.length_msb = 0x00; + hdr.tag = AHAB_PRIVATE_KEY; + hdr.flags = AHAB_DEK_BLOB; + hdr.algorithm = AHAB_ALG_AES; + hdr.mode = AHAB_MODE_CBC; + + switch (len) { + case 128: + hdr.size = AHAB_128_AES_KEY; + break; + case 192: + hdr.size = AHAB_192_AES_KEY; + break; + case 256: + hdr.size = AHAB_256_AES_KEY; + break; + default: + /* Not supported */ + debug("Invalid DEK size. Valid sizes are 128, 192 and 256b\n"); + return -1; + } + + /* Build input message */ + memmove((void *)(src_ptr + sizeof(struct generate_key_blob_hdr)), + (void *)src_ptr, len / 8); + memcpy((void *)src_ptr, (void *)&hdr, + sizeof(struct generate_key_blob_hdr)); + + /* Flush the cache before triggering the CAAM DMA */ + flush_dcache_range(src_addr, src_addr + in_size); + + /* Find input memory region */ + err = sc_rm_find_memreg((-1), &mr_input, src_addr & ~(CONFIG_SYS_CACHELINE_SIZE - 1), + ALIGN(src_addr + in_size, CONFIG_SYS_CACHELINE_SIZE)); + if (err) { + printf("Error: find memory region 0x%X\n", src_addr); + return -ENOMEM; + } + + /* Find output memory region */ + err = sc_rm_find_memreg((-1), &mr_output, dst_addr & ~(CONFIG_SYS_CACHELINE_SIZE - 1), + ALIGN(dst_addr + out_size, CONFIG_SYS_CACHELINE_SIZE)); + if (err) { + printf("Error: find memory region 0x%X\n", dst_addr); + return -ENOMEM; + } + + /* Set memory region permissions for SECO */ + err = sc_rm_set_memreg_permissions(-1, mr_input, SECO_PT, + SC_RM_PERM_FULL); + if (err) { + printf("Set permission failed for input memory region\n"); + ret = -EPERM; + goto error; + } + + err = sc_rm_set_memreg_permissions(-1, mr_output, SECO_PT, + SC_RM_PERM_FULL); + if (err) { + printf("Set permission failed for output memory region\n"); + ret = -EPERM; + goto error; + } + + /* Flush output data before SECO operation */ + flush_dcache_range((ulong)dst_ptr, (ulong)(dst_ptr + + roundup(out_size, ARCH_DMA_MINALIGN))); + + /* Generate DEK blob */ + err = sc_seco_gen_key_blob((-1), 0x0, src_addr, dst_addr, out_size); + if (err) { + ret = -EPERM; + goto error; + } + + /* Invalidate output buffer */ + invalidate_dcache_range((ulong)dst_ptr, (ulong)(dst_ptr + + roundup(out_size, ARCH_DMA_MINALIGN))); + + printf("DEK Blob\n"); + for (i = 0; i < DEK_BLOB_HDR_SIZE + BLOB_SIZE(len / 8); i++) + printf("%02X", dst_ptr[i]); + printf("\n"); + +error: + /* Remove memory region permission to SECO */ + err = sc_rm_set_memreg_permissions(-1, mr_input, SECO_PT, + SC_RM_PERM_NONE); + if (err) { + printf("Error: remove permission failed for input\n"); + ret = -EPERM; + } + + err = sc_rm_set_memreg_permissions(-1, mr_output, SECO_PT, + SC_RM_PERM_NONE); + if (err) { + printf("Error: remove permission failed for output\n"); + ret = -EPERM; + } + + return ret; +} +#endif /* CONFIG_IMX_SECO_DEK_ENCAP */ + +/** + * do_dek_blob() - Handle the "dek_blob" command-line command + * @cmdtp: Command data struct pointer + * @flag: Command flag + * @argc: Command-line argument count + * @argv: Array of command-line arguments + * + * Returns zero on success, CMD_RET_USAGE in case of misuse and negative + * on error. + */ +static int do_dek_blob(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + uint32_t src_addr, dst_addr, len; + + if (argc != 4) + return CMD_RET_USAGE; + + src_addr = simple_strtoul(argv[1], NULL, 16); + dst_addr = simple_strtoul(argv[2], NULL, 16); + len = simple_strtoul(argv[3], NULL, 10); + + return blob_encap_dek(src_addr, dst_addr, len); +} + +/***************************************************/ +static char dek_blob_help_text[] = + "src dst len - Encapsulate and create blob of data\n" + " $len bits long at address $src and\n" + " store the result at address $dst.\n"; + +U_BOOT_CMD( + dek_blob, 4, 1, do_dek_blob, + "Data Encryption Key blob encapsulation", + dek_blob_help_text +); diff --git a/roms/u-boot/arch/arm/mach-imx/cmd_hdmidet.c b/roms/u-boot/arch/arm/mach-imx/cmd_hdmidet.c new file mode 100644 index 000000000..e2571adfb --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/cmd_hdmidet.c @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2012 Boundary Devices Inc. + */ +#include <common.h> +#include <command.h> +#include <asm/arch/imx-regs.h> +#include <asm/arch/mxc_hdmi.h> +#include <asm/io.h> + +static int do_hdmidet(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct hdmi_regs *hdmi = (struct hdmi_regs *)HDMI_ARB_BASE_ADDR; + return (readb(&hdmi->phy_stat0) & HDMI_DVI_STAT) ? 0 : 1; +} + +U_BOOT_CMD(hdmidet, 1, 1, do_hdmidet, + "detect HDMI monitor", + "" +); diff --git a/roms/u-boot/arch/arm/mach-imx/cmd_mfgprot.c b/roms/u-boot/arch/arm/mach-imx/cmd_mfgprot.c new file mode 100644 index 000000000..1430f6190 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/cmd_mfgprot.c @@ -0,0 +1,150 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2016 Freescale Semiconductor, Inc. + * Copyright 2017 NXP + * + * These commands enable the use of the CAAM MPPubK-generation and MPSign + * functions in supported i.MX devices. + */ + +#include <asm/byteorder.h> +#include <asm/arch/clock.h> +#include <linux/compiler.h> +#include <command.h> +#include <common.h> +#include <environment.h> +#include <fsl_sec.h> +#include <mapmem.h> +#include <memalign.h> + +DECLARE_GLOBAL_DATA_PTR; + +/** + * do_mfgprot() - Handle the "mfgprot" command-line command + * @cmdtp: Command data struct pointer + * @flag: Command flag + * @argc: Command-line argument count + * @argv: Array of command-line arguments + * + * Returns zero on success, CMD_RET_USAGE in case of misuse and negative + * on error. + */ +static int do_mfgprot(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) +{ + u8 *m_ptr, *dgst_ptr, *c_ptr, *d_ptr, *dst_ptr; + char *pubk, *sign, *sel; + int m_size, i, ret; + u32 m_addr; + + pubk = "pubk"; + sign = "sign"; + sel = argv[1]; + + /* Enable HAB clock */ + hab_caam_clock_enable(1); + + u32 out_jr_size = sec_in32(CONFIG_SYS_FSL_JR0_ADDR + + FSL_CAAM_ORSR_JRa_OFFSET); + + if (out_jr_size != FSL_CAAM_MAX_JR_SIZE) + sec_init(); + + if (strcmp(sel, pubk) == 0) { + dst_ptr = malloc_cache_aligned(FSL_CAAM_MP_PUBK_BYTES); + if (!dst_ptr) + return -ENOMEM; + + ret = gen_mppubk(dst_ptr); + if (ret) { + free(dst_ptr); + return ret; + } + + /* Output results */ + puts("Public key:\n"); + for (i = 0; i < FSL_CAAM_MP_PUBK_BYTES; i++) + printf("%02X", (dst_ptr)[i]); + puts("\n"); + free(dst_ptr); + + } else if (strcmp(sel, sign) == 0) { + if (argc != 4) + return CMD_RET_USAGE; + + m_addr = simple_strtoul(argv[2], NULL, 16); + m_size = simple_strtoul(argv[3], NULL, 10); + m_ptr = map_physmem(m_addr, m_size, MAP_NOCACHE); + if (!m_ptr) + return -ENOMEM; + + dgst_ptr = malloc_cache_aligned(FSL_CAAM_MP_MES_DGST_BYTES); + if (!dgst_ptr) { + ret = -ENOMEM; + goto free_m; + } + + c_ptr = malloc_cache_aligned(FSL_CAAM_MP_PRVK_BYTES); + if (!c_ptr) { + ret = -ENOMEM; + goto free_dgst; + } + + d_ptr = malloc_cache_aligned(FSL_CAAM_MP_PRVK_BYTES); + if (!d_ptr) { + ret = -ENOMEM; + goto free_c; + } + + ret = sign_mppubk(m_ptr, m_size, dgst_ptr, c_ptr, d_ptr); + if (ret) + goto free_d; + + /* Output results */ + puts("Message: "); + for (i = 0; i < m_size; i++) + printf("%02X ", (m_ptr)[i]); + puts("\n"); + + puts("Message Representative Digest(SHA-256):\n"); + for (i = 0; i < FSL_CAAM_MP_MES_DGST_BYTES; i++) + printf("%02X", (dgst_ptr)[i]); + puts("\n"); + + puts("Signature:\n"); + puts("C:\n"); + for (i = 0; i < FSL_CAAM_MP_PRVK_BYTES; i++) + printf("%02X", (c_ptr)[i]); + puts("\n"); + + puts("d:\n"); + for (i = 0; i < FSL_CAAM_MP_PRVK_BYTES; i++) + printf("%02X", (d_ptr)[i]); + puts("\n"); +free_d: + free(d_ptr); +free_c: + free(c_ptr); +free_dgst: + free(dgst_ptr); +free_m: + unmap_sysmem(m_ptr); + + } else { + return CMD_RET_USAGE; + } + return ret; +} + +/***************************************************/ +static char mfgprot_help_text[] = + "Usage:\n" + "Print the public key for Manufacturing Protection\n" + "\tmfgprot pubk\n" + "Generates a Manufacturing Protection signature\n" + "\tmfgprot sign <data_addr> <size>"; + +U_BOOT_CMD( + mfgprot, 4, 1, do_mfgprot, + "Manufacturing Protection\n", + mfgprot_help_text +); diff --git a/roms/u-boot/arch/arm/mach-imx/cmd_nandbcb.c b/roms/u-boot/arch/arm/mach-imx/cmd_nandbcb.c new file mode 100644 index 000000000..7157c9e97 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/cmd_nandbcb.c @@ -0,0 +1,1551 @@ +/* + * i.MX nand boot control block(bcb). + * + * Based on the common/imx-bbu-nand-fcb.c from barebox and imx kobs-ng + * + * Copyright (C) 2017 Jagan Teki <jagan@amarulasolutions.com> + * Copyright (C) 2016 Sergey Kubushyn <ksi@koi8.net> + * + * Reconstucted by Han Xu <han.xu@nxp.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <command.h> +#include <log.h> +#include <malloc.h> +#include <nand.h> +#include <dm/devres.h> +#include <linux/bug.h> + +#include <asm/io.h> +#include <jffs2/jffs2.h> +#include <linux/bch.h> +#include <linux/mtd/mtd.h> + +#include <asm/arch/sys_proto.h> +#include <asm/mach-imx/imx-nandbcb.h> +#include <asm/mach-imx/imximage.cfg> +#include <mxs_nand.h> +#include <linux/mtd/mtd.h> +#include <nand.h> +#include <fuse.h> + +#include "../../../cmd/legacy-mtd-utils.h" + +/* FCB related flags */ +/* FCB layout with leading 12B reserved */ +#define FCB_LAYOUT_RESV_12B BIT(0) +/* FCB layout with leading 32B meta data */ +#define FCB_LAYOUT_META_32B BIT(1) +/* FCB encrypted by Hamming code */ +#define FCB_ENCODE_HAMMING BIT(2) +/* FCB encrypted by 40bit BCH */ +#define FCB_ENCODE_BCH_40b BIT(3) +/* FCB encrypted by 62bit BCH */ +#define FCB_ENCODE_BCH_62b BIT(4) +/* FCB encrypted by BCH */ +#define FCB_ENCODE_BCH (FCB_ENCODE_BCH_40b | FCB_ENCODE_BCH_62b) +/* FCB data was randomized */ +#define FCB_RANDON_ENABLED BIT(5) + +/* Firmware related flags */ +/* No 1K padding */ +#define FIRMWARE_NEED_PADDING BIT(8) +/* Extra firmware*/ +#define FIRMWARE_EXTRA_ONE BIT(9) +/* Secondary firmware on fixed address */ +#define FIRMWARE_SECONDARY_FIXED_ADDR BIT(10) + +/* Boot search related flags */ +#define BT_SEARCH_CNT_FROM_FUSE BIT(16) + +struct platform_config { + int misc_flags; +}; + +static struct platform_config plat_config; + +/* imx6q/dl/solo */ +static struct platform_config imx6qdl_plat_config = { + .misc_flags = FCB_LAYOUT_RESV_12B | + FCB_ENCODE_HAMMING | + FIRMWARE_NEED_PADDING, +}; + +static struct platform_config imx6sx_plat_config = { + .misc_flags = FCB_LAYOUT_META_32B | + FCB_ENCODE_BCH_62b | + FIRMWARE_NEED_PADDING | + FCB_RANDON_ENABLED, +}; + +static struct platform_config imx7d_plat_config = { + .misc_flags = FCB_LAYOUT_META_32B | + FCB_ENCODE_BCH_62b | + FIRMWARE_NEED_PADDING | + FCB_RANDON_ENABLED, +}; + +/* imx6ul/ull/ulz */ +static struct platform_config imx6ul_plat_config = { + .misc_flags = FCB_LAYOUT_META_32B | + FCB_ENCODE_BCH_40b | + FIRMWARE_NEED_PADDING, +}; + +static struct platform_config imx8mq_plat_config = { + .misc_flags = FCB_LAYOUT_META_32B | + FCB_ENCODE_BCH_62b | + FIRMWARE_NEED_PADDING | + FCB_RANDON_ENABLED | + FIRMWARE_EXTRA_ONE, +}; + +/* all other imx8mm */ +static struct platform_config imx8mm_plat_config = { + .misc_flags = FCB_LAYOUT_META_32B | + FCB_ENCODE_BCH_62b | + FIRMWARE_NEED_PADDING | + FCB_RANDON_ENABLED, +}; + +/* imx8mn */ +static struct platform_config imx8mn_plat_config = { + .misc_flags = FCB_LAYOUT_META_32B | + FCB_ENCODE_BCH_62b | + FCB_RANDON_ENABLED | + FIRMWARE_SECONDARY_FIXED_ADDR | + BT_SEARCH_CNT_FROM_FUSE, +}; + +/* imx8qx/qm */ +static struct platform_config imx8q_plat_config = { + .misc_flags = FCB_LAYOUT_META_32B | + FCB_ENCODE_BCH_62b | + FCB_RANDON_ENABLED | + FIRMWARE_SECONDARY_FIXED_ADDR | + BT_SEARCH_CNT_FROM_FUSE, +}; + +/* boot search related variables and definitions */ +static int g_boot_search_count = 4; +static int g_boot_search_stride; +static int g_pages_per_stride; + +/* mtd config structure */ +struct boot_config { + int dev; + struct mtd_info *mtd; + loff_t maxsize; + loff_t input_size; + loff_t offset; + loff_t boot_stream1_address; + loff_t boot_stream2_address; + size_t boot_stream1_size; + size_t boot_stream2_size; + size_t max_boot_stream_size; + int stride_size_in_byte; + int search_area_size_in_bytes; + int search_area_size_in_pages; + int secondary_boot_stream_off_in_MB; +}; + +/* boot_stream config structure */ +struct boot_stream_config { + char bs_label[32]; + loff_t bs_addr; + size_t bs_size; + void *bs_buf; + loff_t next_bs_addr; + bool need_padding; +}; + +/* FW index */ +#define FW1_ONLY 1 +#define FW2_ONLY 2 +#define FW_ALL FW1_ONLY | FW2_ONLY +#define FW_INX(x) (1 << (x)) + +/* NAND convert macros */ +#define CONV_TO_PAGES(x) ((u32)(x) / (u32)(mtd->writesize)) +#define CONV_TO_BLOCKS(x) ((u32)(x) / (u32)(mtd->erasesize)) + +#define GETBIT(v, n) (((v) >> (n)) & 0x1) +#define IMX8MQ_SPL_SZ 0x3e000 +#define IMX8MQ_HDMI_FW_SZ 0x19c00 + +static int nandbcb_get_info(int argc, char * const argv[], + struct boot_config *boot_cfg) +{ + int dev; + struct mtd_info *mtd; + + dev = nand_curr_device; + if (dev < 0) { + printf("failed to get nand_curr_device, run nand device\n"); + return CMD_RET_FAILURE; + } + + mtd = get_nand_dev_by_index(dev); + if (!mtd) { + printf("failed to get mtd info\n"); + return CMD_RET_FAILURE; + } + + boot_cfg->dev = dev; + boot_cfg->mtd = mtd; + + return CMD_RET_SUCCESS; +} + +static int nandbcb_get_size(int argc, char * const argv[], int num, + struct boot_config *boot_cfg) +{ + int dev; + loff_t offset, size, maxsize; + struct mtd_info *mtd; + + dev = boot_cfg->dev; + mtd = boot_cfg->mtd; + size = 0; + + if (mtd_arg_off_size(argc - num, argv + num, &dev, &offset, &size, + &maxsize, MTD_DEV_TYPE_NAND, mtd->size)) + return CMD_RET_FAILURE; + + boot_cfg->maxsize = maxsize; + boot_cfg->offset = offset; + + debug("max: %llx, offset: %llx\n", maxsize, offset); + + if (size && size != maxsize) + boot_cfg->input_size = size; + + return CMD_RET_SUCCESS; +} + +static int nandbcb_set_boot_config(int argc, char * const argv[], + struct boot_config *boot_cfg) +{ + struct mtd_info *mtd; + loff_t maxsize; + loff_t boot_stream1_address, boot_stream2_address, max_boot_stream_size; + + if (!boot_cfg->mtd) { + printf("Didn't get the mtd info, quit\n"); + return CMD_RET_FAILURE; + } + mtd = boot_cfg->mtd; + + /* + * By default + * set the search count as 4 + * set each FCB/DBBT/Firmware offset at the beginning of blocks + * customers may change the value as needed + */ + + /* if need more compact layout, change these values */ + /* g_boot_search_count was set as 4 at the definition*/ + /* g_pages_per_stride was set as block size */ + + g_pages_per_stride = mtd->erasesize / mtd->writesize; + + g_boot_search_stride = mtd->writesize * g_pages_per_stride; + + boot_cfg->stride_size_in_byte = g_boot_search_stride * mtd->writesize; + boot_cfg->search_area_size_in_bytes = + g_boot_search_count * g_boot_search_stride; + boot_cfg->search_area_size_in_pages = + boot_cfg->search_area_size_in_bytes / mtd->writesize; + + /* after FCB/DBBT, split the rest of area for two Firmwares */ + if (!boot_cfg->maxsize) { + printf("Didn't get the maxsize, quit\n"); + return CMD_RET_FAILURE; + } + maxsize = boot_cfg->maxsize; + /* align to page boundary */ + maxsize = ((u32)(maxsize + mtd->writesize - 1)) / (u32)mtd->writesize + * mtd->writesize; + + boot_stream1_address = 2 * boot_cfg->search_area_size_in_bytes; + boot_stream2_address = ((maxsize - boot_stream1_address) / 2 + + boot_stream1_address); + + if (boot_cfg->secondary_boot_stream_off_in_MB) + boot_stream2_address = + (loff_t)boot_cfg->secondary_boot_stream_off_in_MB * 1024 * 1024; + + max_boot_stream_size = boot_stream2_address - boot_stream1_address; + + /* sanity check */ + if (max_boot_stream_size <= 0) { + debug("st1_addr: %llx, st2_addr: %llx, max: %llx\n", + boot_stream1_address, boot_stream2_address, + max_boot_stream_size); + printf("something wrong with firmware address settings\n"); + return CMD_RET_FAILURE; + } + boot_cfg->boot_stream1_address = boot_stream1_address; + boot_cfg->boot_stream2_address = boot_stream2_address; + boot_cfg->max_boot_stream_size = max_boot_stream_size; + + /* set the boot_stream size as the input size now */ + if (boot_cfg->input_size) { + boot_cfg->boot_stream1_size = boot_cfg->input_size; + boot_cfg->boot_stream2_size = boot_cfg->input_size; + } + + return CMD_RET_SUCCESS; +} + +static int nandbcb_check_space(struct boot_config *boot_cfg) +{ + size_t maxsize = boot_cfg->maxsize; + size_t max_boot_stream_size = boot_cfg->max_boot_stream_size; + loff_t boot_stream2_address = boot_cfg->boot_stream2_address; + + if (boot_cfg->boot_stream1_size && + boot_cfg->boot_stream1_size > max_boot_stream_size) { + printf("boot stream1 doesn't fit, check partition size or settings\n"); + return CMD_RET_FAILURE; + } + + if (boot_cfg->boot_stream2_size && + boot_cfg->boot_stream2_size > maxsize - boot_stream2_address) { + printf("boot stream2 doesn't fit, check partition size or settings\n"); + return CMD_RET_FAILURE; + } + + return CMD_RET_SUCCESS; +} + +#if defined(CONFIG_MX6UL) || defined(CONFIG_MX6ULL) +static uint8_t reverse_bit(uint8_t b) +{ + b = (b & 0xf0) >> 4 | (b & 0x0f) << 4; + b = (b & 0xcc) >> 2 | (b & 0x33) << 2; + b = (b & 0xaa) >> 1 | (b & 0x55) << 1; + + return b; +} + +static void encode_bch_ecc(void *buf, struct fcb_block *fcb, int eccbits) +{ + int i, j, m = 13; + int blocksize = 128; + int numblocks = 8; + int ecc_buf_size = (m * eccbits + 7) / 8; + struct bch_control *bch = init_bch(m, eccbits, 0); + u8 *ecc_buf = kzalloc(ecc_buf_size, GFP_KERNEL); + u8 *tmp_buf = kzalloc(blocksize * numblocks, GFP_KERNEL); + u8 *psrc, *pdst; + + /* + * The blocks here are bit aligned. If eccbits is a multiple of 8, + * we just can copy bytes. Otherwiese we must move the blocks to + * the next free bit position. + */ + WARN_ON(eccbits % 8); + + memcpy(tmp_buf, fcb, sizeof(*fcb)); + + for (i = 0; i < numblocks; i++) { + memset(ecc_buf, 0, ecc_buf_size); + psrc = tmp_buf + i * blocksize; + pdst = buf + i * (blocksize + ecc_buf_size); + + /* copy data byte aligned to destination buf */ + memcpy(pdst, psrc, blocksize); + + /* + * imx-kobs use a modified encode_bch which reverse the + * bit order of the data before calculating bch. + * Do this in the buffer and use the bch lib here. + */ + for (j = 0; j < blocksize; j++) + psrc[j] = reverse_bit(psrc[j]); + + encode_bch(bch, psrc, blocksize, ecc_buf); + + /* reverse ecc bit */ + for (j = 0; j < ecc_buf_size; j++) + ecc_buf[j] = reverse_bit(ecc_buf[j]); + + /* Here eccbuf is byte aligned and we can just copy it */ + memcpy(pdst + blocksize, ecc_buf, ecc_buf_size); + } + + kfree(ecc_buf); + kfree(tmp_buf); + free_bch(bch); +} +#else + +static u8 calculate_parity_13_8(u8 d) +{ + u8 p = 0; + + p |= (GETBIT(d, 6) ^ GETBIT(d, 5) ^ GETBIT(d, 3) ^ GETBIT(d, 2)) << 0; + p |= (GETBIT(d, 7) ^ GETBIT(d, 5) ^ GETBIT(d, 4) ^ GETBIT(d, 2) ^ + GETBIT(d, 1)) << 1; + p |= (GETBIT(d, 7) ^ GETBIT(d, 6) ^ GETBIT(d, 5) ^ GETBIT(d, 1) ^ + GETBIT(d, 0)) << 2; + p |= (GETBIT(d, 7) ^ GETBIT(d, 4) ^ GETBIT(d, 3) ^ GETBIT(d, 0)) << 3; + p |= (GETBIT(d, 6) ^ GETBIT(d, 4) ^ GETBIT(d, 3) ^ GETBIT(d, 2) ^ + GETBIT(d, 1) ^ GETBIT(d, 0)) << 4; + + return p; +} + +static void encode_hamming_13_8(void *_src, void *_ecc, size_t size) +{ + int i; + u8 *src = _src; + u8 *ecc = _ecc; + + for (i = 0; i < size; i++) + ecc[i] = calculate_parity_13_8(src[i]); +} +#endif + +static u32 calc_chksum(void *buf, size_t size) +{ + u32 chksum = 0; + u8 *bp = buf; + size_t i; + + for (i = 0; i < size; i++) + chksum += bp[i]; + + return ~chksum; +} + +static void fill_fcb(struct fcb_block *fcb, struct boot_config *boot_cfg) +{ + struct mtd_info *mtd = boot_cfg->mtd; + struct nand_chip *chip = mtd_to_nand(mtd); + struct mxs_nand_info *nand_info = nand_get_controller_data(chip); + struct mxs_nand_layout l; + + mxs_nand_get_layout(mtd, &l); + + fcb->fingerprint = FCB_FINGERPRINT; + fcb->version = FCB_VERSION_1; + + fcb->datasetup = 80; + fcb->datahold = 60; + fcb->addr_setup = 25; + fcb->dsample_time = 6; + + fcb->pagesize = mtd->writesize; + fcb->oob_pagesize = mtd->writesize + mtd->oobsize; + fcb->sectors = mtd->erasesize / mtd->writesize; + + fcb->meta_size = l.meta_size; + fcb->nr_blocks = l.nblocks; + fcb->ecc_nr = l.data0_size; + fcb->ecc_level = l.ecc0; + fcb->ecc_size = l.datan_size; + fcb->ecc_type = l.eccn; + fcb->bchtype = l.gf_len; + + /* DBBT search area starts from the next block after all FCB */ + fcb->dbbt_start = boot_cfg->search_area_size_in_pages; + + fcb->bb_byte = nand_info->bch_geometry.block_mark_byte_offset; + fcb->bb_start_bit = nand_info->bch_geometry.block_mark_bit_offset; + + fcb->phy_offset = mtd->writesize; + + fcb->disbbm = 0; + + fcb->fw1_start = CONV_TO_PAGES(boot_cfg->boot_stream1_address); + fcb->fw2_start = CONV_TO_PAGES(boot_cfg->boot_stream2_address); + fcb->fw1_pages = CONV_TO_PAGES(boot_cfg->boot_stream1_size); + fcb->fw2_pages = CONV_TO_PAGES(boot_cfg->boot_stream2_size); + + fcb->checksum = calc_chksum((void *)fcb + 4, sizeof(*fcb) - 4); +} + +static int fill_dbbt_data(struct mtd_info *mtd, void *buf, int num_blocks) +{ + int n, n_bad_blocks = 0; + u32 *bb = buf + 0x8; + u32 *n_bad_blocksp = buf + 0x4; + + for (n = 0; n < num_blocks; n++) { + loff_t offset = (loff_t)n * mtd->erasesize; + if (mtd_block_isbad(mtd, offset)) { + n_bad_blocks++; + *bb = n; + bb++; + } + } + + *n_bad_blocksp = n_bad_blocks; + + return n_bad_blocks; +} + +/* + * return 1 - bad block + * return 0 - read successfully + * return < 0 - read failed + */ +static int read_fcb(struct boot_config *boot_cfg, struct fcb_block *fcb, + loff_t off) +{ + struct mtd_info *mtd; + void *fcb_raw_page; + size_t size; + int ret = 0; + + mtd = boot_cfg->mtd; + if (mtd_block_isbad(mtd, off)) { + printf("Block %d is bad, skipped\n", (int)CONV_TO_BLOCKS(off)); + return 1; + } + + fcb_raw_page = kzalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL); + if (!fcb_raw_page) { + debug("failed to allocate fcb_raw_page\n"); + ret = -ENOMEM; + return ret; + } + + /* + * User BCH hardware to decode ECC for FCB + */ + if (plat_config.misc_flags & FCB_ENCODE_BCH) { + size = sizeof(struct fcb_block); + + /* switch nand BCH to FCB compatible settings */ + if (plat_config.misc_flags & FCB_ENCODE_BCH_62b) + mxs_nand_mode_fcb_62bit(mtd); + else if (plat_config.misc_flags & FCB_ENCODE_BCH_40b) + mxs_nand_mode_fcb_40bit(mtd); + + ret = nand_read(mtd, off, &size, (u_char *)fcb); + + /* switch BCH back */ + mxs_nand_mode_normal(mtd); + printf("NAND FCB read from 0x%llx offset 0x%zx read: %s\n", + off, size, ret ? "ERROR" : "OK"); + + } else if (plat_config.misc_flags & FCB_ENCODE_HAMMING) { + /* raw read*/ + mtd_oob_ops_t ops = { + .datbuf = (u8 *)fcb_raw_page, + .oobbuf = ((u8 *)fcb_raw_page) + mtd->writesize, + .len = mtd->writesize, + .ooblen = mtd->oobsize, + .mode = MTD_OPS_RAW + }; + + ret = mtd_read_oob(mtd, off, &ops); + printf("NAND FCB read from 0x%llx offset 0x%zx read: %s\n", + off, ops.len, ret ? "ERROR" : "OK"); + } + + if (ret) + goto fcb_raw_page_err; + + if ((plat_config.misc_flags & FCB_ENCODE_HAMMING) && + (plat_config.misc_flags & FCB_LAYOUT_RESV_12B)) + memcpy(fcb, fcb_raw_page + 12, sizeof(struct fcb_block)); + +/* TODO: check if it can pass Hamming check */ + +fcb_raw_page_err: + kfree(fcb_raw_page); + + return ret; +} + +static int write_fcb(struct boot_config *boot_cfg, struct fcb_block *fcb) +{ + struct mtd_info *mtd; + void *fcb_raw_page = NULL; + int i, ret = 0; + loff_t off; + size_t size; + + mtd = boot_cfg->mtd; + + /* + * We prepare raw page only for i.MX6, for i.MX7 we + * leverage BCH hw module instead + */ + if ((plat_config.misc_flags & FCB_ENCODE_HAMMING) && + (plat_config.misc_flags & FCB_LAYOUT_RESV_12B)) { + fcb_raw_page = kzalloc(mtd->writesize + mtd->oobsize, + GFP_KERNEL); + if (!fcb_raw_page) { + debug("failed to allocate fcb_raw_page\n"); + ret = -ENOMEM; + return ret; + } + +#if defined(CONFIG_MX6UL) || defined(CONFIG_MX6ULL) + /* 40 bit BCH, for i.MX6UL(L) */ + encode_bch_ecc(fcb_raw_page + 32, fcb, 40); +#else + memcpy(fcb_raw_page + 12, fcb, sizeof(struct fcb_block)); + encode_hamming_13_8(fcb_raw_page + 12, + fcb_raw_page + 12 + 512, 512); +#endif + /* + * Set the first and second byte of OOB data to 0xFF, + * not 0x00. These bytes are used as the Manufacturers Bad + * Block Marker (MBBM). Since the FCB is mostly written to + * the first page in a block, a scan for + * factory bad blocks will detect these blocks as bad, e.g. + * when function nand_scan_bbt() is executed to build a new + * bad block table. + */ + memset(fcb_raw_page + mtd->writesize, 0xFF, 2); + } + + /* start writing FCB from the very beginning */ + off = 0; + + for (i = 0; i < g_boot_search_count; i++) { + if (mtd_block_isbad(mtd, off)) { + printf("Block %d is bad, skipped\n", i); + continue; + } + + /* + * User BCH hardware module to generate ECC for FCB + */ + if (plat_config.misc_flags & FCB_ENCODE_BCH) { + size = sizeof(struct fcb_block); + + /* switch nand BCH to FCB compatible settings */ + if (plat_config.misc_flags & FCB_ENCODE_BCH_62b) + mxs_nand_mode_fcb_62bit(mtd); + else if (plat_config.misc_flags & FCB_ENCODE_BCH_40b) + mxs_nand_mode_fcb_40bit(mtd); + + ret = nand_write(mtd, off, &size, (u_char *)fcb); + + /* switch BCH back */ + mxs_nand_mode_normal(mtd); + printf("NAND FCB write to 0x%zx offset 0x%llx written: %s\n", + size, off, ret ? "ERROR" : "OK"); + + } else if (plat_config.misc_flags & FCB_ENCODE_HAMMING) { + /* raw write */ + mtd_oob_ops_t ops = { + .datbuf = (u8 *)fcb_raw_page, + .oobbuf = ((u8 *)fcb_raw_page) + + mtd->writesize, + .len = mtd->writesize, + .ooblen = mtd->oobsize, + .mode = MTD_OPS_RAW + }; + + ret = mtd_write_oob(mtd, off, &ops); + printf("NAND FCB write to 0x%llxx offset 0x%zx written: %s\n", off, ops.len, ret ? "ERROR" : "OK"); + } + + if (ret) + goto fcb_raw_page_err; + + /* next writing location */ + off += g_boot_search_stride; + } + +fcb_raw_page_err: + kfree(fcb_raw_page); + + return ret; +} + +/* + * return 1 - bad block + * return 0 - read successfully + * return < 0 - read failed + */ +static int read_dbbt(struct boot_config *boot_cfg, struct dbbt_block *dbbt, + void *dbbt_data_page, loff_t off) +{ + size_t size; + struct mtd_info *mtd; + loff_t to; + int ret; + + mtd = boot_cfg->mtd; + + if (mtd_block_isbad(mtd, off)) { + printf("Block %d is bad, skipped\n", + (int)CONV_TO_BLOCKS(off)); + return 1; + } + + size = sizeof(struct dbbt_block); + ret = nand_read(mtd, off, &size, (u_char *)dbbt); + printf("NAND DBBT read from 0x%llx offset 0x%zx read: %s\n", + off, size, ret ? "ERROR" : "OK"); + if (ret) + return ret; + + /* dbbtpages == 0 if no bad blocks */ + if (dbbt->dbbtpages > 0) { + to = off + 4 * mtd->writesize; + size = mtd->writesize; + ret = nand_read(mtd, to, &size, dbbt_data_page); + printf("DBBT data read from 0x%llx offset 0x%zx read: %s\n", + to, size, ret ? "ERROR" : "OK"); + + if (ret) + return ret; + } + + return 0; +} + +static int write_dbbt(struct boot_config *boot_cfg, struct dbbt_block *dbbt, + void *dbbt_data_page) +{ + int i; + loff_t off, to; + size_t size; + struct mtd_info *mtd; + int ret; + + mtd = boot_cfg->mtd; + + /* start writing DBBT after all FCBs */ + off = boot_cfg->search_area_size_in_bytes; + size = mtd->writesize; + + for (i = 0; i < g_boot_search_count; i++) { + if (mtd_block_isbad(mtd, off)) { + printf("Block %d is bad, skipped\n", + (int)(i + CONV_TO_BLOCKS(off))); + continue; + } + + ret = nand_write(mtd, off, &size, (u_char *)dbbt); + printf("NAND DBBT write to 0x%llx offset 0x%zx written: %s\n", + off, size, ret ? "ERROR" : "OK"); + if (ret) + return ret; + + /* dbbtpages == 0 if no bad blocks */ + if (dbbt->dbbtpages > 0) { + to = off + 4 * mtd->writesize; + ret = nand_write(mtd, to, &size, dbbt_data_page); + printf("DBBT data write to 0x%llx offset 0x%zx written: %s\n", + to, size, ret ? "ERROR" : "OK"); + + if (ret) + return ret; + } + + /* next writing location */ + off += g_boot_search_stride; + } + + return 0; +} + +/* reuse the check_skip_len from nand_util.c with minor change*/ +static int check_skip_length(struct boot_config *boot_cfg, loff_t offset, + size_t length, size_t *used) +{ + struct mtd_info *mtd = boot_cfg->mtd; + size_t maxsize = boot_cfg->maxsize; + size_t len_excl_bad = 0; + int ret = 0; + + while (len_excl_bad < length) { + size_t block_len, block_off; + loff_t block_start; + + if (offset >= maxsize) + return -1; + + block_start = offset & ~(loff_t)(mtd->erasesize - 1); + block_off = offset & (mtd->erasesize - 1); + block_len = mtd->erasesize - block_off; + + if (!nand_block_isbad(mtd, block_start)) + len_excl_bad += block_len; + else + ret = 1; + + offset += block_len; + *used += block_len; + } + + /* If the length is not a multiple of block_len, adjust. */ + if (len_excl_bad > length) + *used -= (len_excl_bad - length); + + return ret; +} + +static int nandbcb_get_next_good_blk_addr(struct boot_config *boot_cfg, + struct boot_stream_config *bs_cfg) +{ + struct mtd_info *mtd = boot_cfg->mtd; + loff_t offset = bs_cfg->bs_addr; + size_t length = bs_cfg->bs_size; + size_t used = 0; + int ret; + + ret = check_skip_length(boot_cfg, offset, length, &used); + + if (ret < 0) + return ret; + + /* get next image address */ + bs_cfg->next_bs_addr = (u32)(offset + used + mtd->erasesize - 1) + / (u32)mtd->erasesize * mtd->erasesize; + + return ret; +} + +static int nandbcb_write_bs_skip_bad(struct boot_config *boot_cfg, + struct boot_stream_config *bs_cfg) +{ + struct mtd_info *mtd; + void *buf; + loff_t offset, maxsize; + size_t size; + size_t length; + int ret; + bool padding_flag = false; + + mtd = boot_cfg->mtd; + offset = bs_cfg->bs_addr; + maxsize = boot_cfg->maxsize; + size = bs_cfg->bs_size; + + /* some boot images may need leading offset */ + if (bs_cfg->need_padding && + (plat_config.misc_flags & FIRMWARE_NEED_PADDING)) + padding_flag = 1; + + if (padding_flag) + length = ALIGN(size + FLASH_OFFSET_STANDARD, mtd->writesize); + else + length = ALIGN(size, mtd->writesize); + + buf = kzalloc(length, GFP_KERNEL); + if (!buf) { + printf("failed to allocate buffer for firmware\n"); + ret = -ENOMEM; + return ret; + } + + if (padding_flag) + memcpy(buf + FLASH_OFFSET_STANDARD, bs_cfg->bs_buf, size); + else + memcpy(buf, bs_cfg->bs_buf, size); + + ret = nand_write_skip_bad(mtd, offset, &length, NULL, maxsize, + (u_char *)buf, WITH_WR_VERIFY); + printf("Write %s @0x%llx offset, 0x%zx bytes written: %s\n", + bs_cfg->bs_label, offset, length, ret ? "ERROR" : "OK"); + + if (ret) + /* write image failed, quit */ + goto err; + + /* get next good blk address if needed */ + if (bs_cfg->need_padding) { + ret = nandbcb_get_next_good_blk_addr(boot_cfg, bs_cfg); + if (ret < 0) { + printf("Next image cannot fit in NAND partition\n"); + goto err; + } + } + + /* now we know how the exact image size written to NAND */ + bs_cfg->bs_size = length; + return 0; +err: + kfree(buf); + return ret; +} + +static int nandbcb_write_fw(struct boot_config *boot_cfg, u_char *buf, + int index) +{ + int i; + loff_t offset; + size_t size; + loff_t next_bs_addr; + struct boot_stream_config bs_cfg; + int ret; + + for (i = 0; i < 2; ++i) { + if (!(FW_INX(i) & index)) + continue; + + if (i == 0) { + offset = boot_cfg->boot_stream1_address; + size = boot_cfg->boot_stream1_size; + } else { + offset = boot_cfg->boot_stream2_address; + size = boot_cfg->boot_stream2_size; + } + + /* write Firmware*/ + if (!(plat_config.misc_flags & FIRMWARE_EXTRA_ONE)) { + memset(&bs_cfg, 0, sizeof(struct boot_stream_config)); + sprintf(bs_cfg.bs_label, "firmware%d", i); + bs_cfg.bs_addr = offset; + bs_cfg.bs_size = size; + bs_cfg.bs_buf = buf; + bs_cfg.need_padding = 1; + + ret = nandbcb_write_bs_skip_bad(boot_cfg, &bs_cfg); + if (ret) + return ret; + + /* update the boot stream size */ + if (i == 0) + boot_cfg->boot_stream1_size = bs_cfg.bs_size; + else + boot_cfg->boot_stream2_size = bs_cfg.bs_size; + + } else { + /* some platforms need extra firmware */ + memset(&bs_cfg, 0, sizeof(struct boot_stream_config)); + sprintf(bs_cfg.bs_label, "fw%d_part%d", i, 1); + bs_cfg.bs_addr = offset; + bs_cfg.bs_size = IMX8MQ_HDMI_FW_SZ; + bs_cfg.bs_buf = buf; + bs_cfg.need_padding = 1; + + ret = nandbcb_write_bs_skip_bad(boot_cfg, &bs_cfg); + if (ret) + return ret; + + /* update the boot stream size */ + if (i == 0) + boot_cfg->boot_stream1_size = bs_cfg.bs_size; + else + boot_cfg->boot_stream2_size = bs_cfg.bs_size; + + /* get next image address */ + next_bs_addr = bs_cfg.next_bs_addr; + + memset(&bs_cfg, 0, sizeof(struct boot_stream_config)); + sprintf(bs_cfg.bs_label, "fw%d_part%d", i, 2); + bs_cfg.bs_addr = next_bs_addr; + bs_cfg.bs_size = IMX8MQ_SPL_SZ; + bs_cfg.bs_buf = (u_char *)(buf + IMX8MQ_HDMI_FW_SZ); + bs_cfg.need_padding = 0; + + ret = nandbcb_write_bs_skip_bad(boot_cfg, &bs_cfg); + if (ret) + return ret; + } + } + + return 0; +} + +static int nandbcb_init(struct boot_config *boot_cfg, u_char *buf) +{ + struct mtd_info *mtd; + nand_erase_options_t opts; + struct fcb_block *fcb; + struct dbbt_block *dbbt; + void *dbbt_page, *dbbt_data_page; + int ret; + loff_t maxsize, off; + + mtd = boot_cfg->mtd; + maxsize = boot_cfg->maxsize; + off = boot_cfg->offset; + + /* erase */ + memset(&opts, 0, sizeof(opts)); + opts.offset = off; + opts.length = maxsize - 1; + ret = nand_erase_opts(mtd, &opts); + if (ret) { + printf("%s: erase failed (ret = %d)\n", __func__, ret); + return ret; + } + + /* + * Reference documentation from i.MX6DQRM section 8.5.2.2 + * + * Nand Boot Control Block(BCB) contains two data structures, + * - Firmware Configuration Block(FCB) + * - Discovered Bad Block Table(DBBT) + * + * FCB contains, + * - nand timings + * - DBBT search page address, + * - start page address of primary firmware + * - start page address of secondary firmware + * + * setup fcb: + * - number of blocks = mtd partition size / mtd erasesize + * - two firmware blocks, primary and secondary + * - first 4 block for FCB/DBBT + * - rest split in half for primary and secondary firmware + * - same firmware write twice + */ + + /* write Firmware*/ + ret = nandbcb_write_fw(boot_cfg, buf, FW_ALL); + if (ret) + goto err; + + /* fill fcb */ + fcb = kzalloc(sizeof(*fcb), GFP_KERNEL); + if (!fcb) { + debug("failed to allocate fcb\n"); + ret = -ENOMEM; + return ret; + } + fill_fcb(fcb, boot_cfg); + + ret = write_fcb(boot_cfg, fcb); + + /* fill dbbt */ + dbbt_page = kzalloc(mtd->writesize, GFP_KERNEL); + if (!dbbt_page) { + debug("failed to allocate dbbt_page\n"); + ret = -ENOMEM; + goto fcb_err; + } + + dbbt_data_page = kzalloc(mtd->writesize, GFP_KERNEL); + if (!dbbt_data_page) { + debug("failed to allocate dbbt_data_page\n"); + ret = -ENOMEM; + goto dbbt_page_err; + } + + dbbt = dbbt_page; + dbbt->checksum = 0; + dbbt->fingerprint = DBBT_FINGERPRINT; + dbbt->version = DBBT_VERSION_1; + ret = fill_dbbt_data(mtd, dbbt_data_page, CONV_TO_BLOCKS(maxsize)); + if (ret < 0) + goto dbbt_data_page_err; + else if (ret > 0) + dbbt->dbbtpages = 1; + + /* write dbbt */ + ret = write_dbbt(boot_cfg, dbbt, dbbt_data_page); + if (ret < 0) + printf("failed to write FCB/DBBT\n"); + +dbbt_data_page_err: + kfree(dbbt_data_page); +dbbt_page_err: + kfree(dbbt_page); +fcb_err: + kfree(fcb); +err: + return ret; +} + +static int do_nandbcb_bcbonly(int argc, char *const argv[]) +{ + struct fcb_block *fcb; + struct dbbt_block *dbbt; + struct mtd_info *mtd; + nand_erase_options_t opts; + size_t maxsize; + loff_t off; + void *dbbt_page, *dbbt_data_page; + int ret; + struct boot_config cfg; + + if (argc < 4) + return CMD_RET_USAGE; + + memset(&cfg, 0, sizeof(struct boot_config)); + if (nandbcb_get_info(argc, argv, &cfg)) + return CMD_RET_FAILURE; + + /* only get the partition info */ + if (nandbcb_get_size(2, argv, 1, &cfg)) + return CMD_RET_FAILURE; + + if (nandbcb_set_boot_config(argc, argv, &cfg)) + return CMD_RET_FAILURE; + + mtd = cfg.mtd; + + cfg.boot_stream1_address = simple_strtoul(argv[2], NULL, 16); + cfg.boot_stream1_size = simple_strtoul(argv[3], NULL, 16); + cfg.boot_stream1_size = ALIGN(cfg.boot_stream1_size, mtd->writesize); + + if (argc > 5) { + cfg.boot_stream2_address = simple_strtoul(argv[4], NULL, 16); + cfg.boot_stream2_size = simple_strtoul(argv[5], NULL, 16); + cfg.boot_stream2_size = ALIGN(cfg.boot_stream2_size, + mtd->writesize); + } + + /* sanity check */ + nandbcb_check_space(&cfg); + + maxsize = cfg.maxsize; + off = cfg.offset; + + /* erase the previous FCB/DBBT */ + memset(&opts, 0, sizeof(opts)); + opts.offset = off; + opts.length = g_boot_search_stride * 2; + ret = nand_erase_opts(mtd, &opts); + if (ret) { + printf("%s: erase failed (ret = %d)\n", __func__, ret); + return CMD_RET_FAILURE; + } + + /* fill fcb */ + fcb = kzalloc(sizeof(*fcb), GFP_KERNEL); + if (!fcb) { + printf("failed to allocate fcb\n"); + ret = -ENOMEM; + return CMD_RET_FAILURE; + } + + fill_fcb(fcb, &cfg); + + /* write fcb */ + ret = write_fcb(&cfg, fcb); + + /* fill dbbt */ + dbbt_page = kzalloc(mtd->writesize, GFP_KERNEL); + if (!dbbt_page) { + printf("failed to allocate dbbt_page\n"); + ret = -ENOMEM; + goto fcb_err; + } + + dbbt_data_page = kzalloc(mtd->writesize, GFP_KERNEL); + if (!dbbt_data_page) { + printf("failed to allocate dbbt_data_page\n"); + ret = -ENOMEM; + goto dbbt_page_err; + } + + dbbt = dbbt_page; + dbbt->checksum = 0; + dbbt->fingerprint = DBBT_FINGERPRINT; + dbbt->version = DBBT_VERSION_1; + ret = fill_dbbt_data(mtd, dbbt_data_page, CONV_TO_BLOCKS(maxsize)); + if (ret < 0) + goto dbbt_data_page_err; + else if (ret > 0) + dbbt->dbbtpages = 1; + + /* write dbbt */ + ret = write_dbbt(&cfg, dbbt, dbbt_data_page); + +dbbt_data_page_err: + kfree(dbbt_data_page); +dbbt_page_err: + kfree(dbbt_page); +fcb_err: + kfree(fcb); + + if (ret < 0) { + printf("failed to write FCB/DBBT\n"); + return CMD_RET_FAILURE; + } + + return CMD_RET_SUCCESS; +} + +/* dump data which is read from NAND chip */ +void dump_structure(struct boot_config *boot_cfg, struct fcb_block *fcb, + struct dbbt_block *dbbt, void *dbbt_data_page) +{ + int i; + struct mtd_info *mtd = boot_cfg->mtd; + + #define P1(x) printf(" %s = 0x%08x\n", #x, fcb->x) + printf("FCB\n"); + P1(checksum); + P1(fingerprint); + P1(version); + #undef P1 + #define P1(x) printf(" %s = %d\n", #x, fcb->x) + P1(datasetup); + P1(datahold); + P1(addr_setup); + P1(dsample_time); + P1(pagesize); + P1(oob_pagesize); + P1(sectors); + P1(nr_nand); + P1(nr_die); + P1(celltype); + P1(ecc_type); + P1(ecc_nr); + P1(ecc_size); + P1(ecc_level); + P1(meta_size); + P1(nr_blocks); + P1(ecc_type_sdk); + P1(ecc_nr_sdk); + P1(ecc_size_sdk); + P1(ecc_level_sdk); + P1(nr_blocks_sdk); + P1(meta_size_sdk); + P1(erase_th); + P1(bootpatch); + P1(patch_size); + P1(fw1_start); + P1(fw2_start); + P1(fw1_pages); + P1(fw2_pages); + P1(dbbt_start); + P1(bb_byte); + P1(bb_start_bit); + P1(phy_offset); + P1(bchtype); + P1(readlatency); + P1(predelay); + P1(cedelay); + P1(postdelay); + P1(cmdaddpause); + P1(datapause); + P1(tmspeed); + P1(busytimeout); + P1(disbbm); + P1(spare_offset); +#if !defined(CONFIG_MX6) || defined(CONFIG_MX6SX) || \ + defined(CONFIG_MX6UL) || defined(CONFIG_MX6ULL) + P1(onfi_sync_enable); + P1(onfi_sync_speed); + P1(onfi_sync_nand_data); + P1(disbbm_search); + P1(disbbm_search_limit); + P1(read_retry_enable); +#endif + #undef P1 + #define P1(x) printf(" %s = 0x%08x\n", #x, dbbt->x) + printf("DBBT :\n"); + P1(checksum); + P1(fingerprint); + P1(version); + #undef P1 + #define P1(x) printf(" %s = %d\n", #x, dbbt->x) + P1(dbbtpages); + #undef P1 + + for (i = 0; i < dbbt->dbbtpages; ++i) + printf("%d ", *((u32 *)(dbbt_data_page + i))); + + if (!(plat_config.misc_flags & FIRMWARE_EXTRA_ONE)) { + printf("Firmware: image #0 @ 0x%x size 0x%x\n", + fcb->fw1_start, fcb->fw1_pages * mtd->writesize); + printf("Firmware: image #1 @ 0x%x size 0x%x\n", + fcb->fw2_start, fcb->fw2_pages * mtd->writesize); + } else { + printf("Firmware: image #0 @ 0x%x size 0x%x\n", + fcb->fw1_start, fcb->fw1_pages * mtd->writesize); + printf("Firmware: image #1 @ 0x%x size 0x%x\n", + fcb->fw2_start, fcb->fw2_pages * mtd->writesize); + /* TODO: Add extra image information */ + } +} + +static bool check_fingerprint(void *data, int fingerprint) +{ + int off = 4; + + return (*(int *)(data + off) == fingerprint); +} + +static int fuse_to_search_count(u32 bank, u32 word, u32 mask, u32 off) +{ + int err; + u32 val; + int ret; + + /* by default, the boot search count from fuse should be 2 */ + err = fuse_read(bank, word, &val); + if (err) + return 2; + + val = (val & mask) >> off; + + switch (val) { + case 0: + ret = 2; + break; + case 1: + case 2: + case 3: + ret = 1 << val; + break; + default: + ret = 2; + } + + return ret; +} + +static int nandbcb_dump(struct boot_config *boot_cfg) +{ + int i; + loff_t off; + struct mtd_info *mtd = boot_cfg->mtd; + struct fcb_block fcb, fcb_copy; + struct dbbt_block dbbt, dbbt_copy; + void *dbbt_data_page, *dbbt_data_page_copy; + bool fcb_not_found, dbbt_not_found; + int ret = 0; + + dbbt_data_page = kzalloc(mtd->writesize, GFP_KERNEL); + if (!dbbt_data_page) { + printf("failed to allocate dbbt_data_page\n"); + ret = -ENOMEM; + return ret; + } + + dbbt_data_page_copy = kzalloc(mtd->writesize, GFP_KERNEL); + if (!dbbt_data_page_copy) { + printf("failed to allocate dbbt_data_page\n"); + ret = -ENOMEM; + goto dbbt_page_err; + } + + /* read fcb */ + fcb_not_found = 1; + off = 0; + for (i = 0; i < g_boot_search_count; ++i) { + if (fcb_not_found) { + ret = read_fcb(boot_cfg, &fcb, off); + + if (ret < 0) + goto dbbt_page_copy_err; + else if (ret == 1) + continue; + else if (ret == 0) + if (check_fingerprint(&fcb, FCB_FINGERPRINT)) + fcb_not_found = 0; + } else { + ret = read_fcb(boot_cfg, &fcb_copy, off); + + if (ret < 0) + goto dbbt_page_copy_err; + if (memcmp(&fcb, &fcb_copy, + sizeof(struct fcb_block))) { + printf("FCB copies are not identical\n"); + ret = -EINVAL; + goto dbbt_page_copy_err; + } + } + + /* next read location */ + off += g_boot_search_stride; + } + + /* read dbbt*/ + dbbt_not_found = 1; + off = boot_cfg->search_area_size_in_bytes; + for (i = 0; i < g_boot_search_count; ++i) { + if (dbbt_not_found) { + ret = read_dbbt(boot_cfg, &dbbt, dbbt_data_page, off); + + if (ret < 0) + goto dbbt_page_copy_err; + else if (ret == 1) + continue; + else if (ret == 0) + if (check_fingerprint(&dbbt, DBBT_FINGERPRINT)) + dbbt_not_found = 0; + } else { + ret = read_dbbt(boot_cfg, &dbbt_copy, + dbbt_data_page_copy, off); + + if (ret < 0) + goto dbbt_page_copy_err; + if (memcmp(&dbbt, &dbbt_copy, + sizeof(struct dbbt_block))) { + printf("DBBT copies are not identical\n"); + ret = -EINVAL; + goto dbbt_page_copy_err; + } + if (dbbt.dbbtpages > 0 && + memcmp(dbbt_data_page, dbbt_data_page_copy, + mtd->writesize)) { + printf("DBBT data copies are not identical\n"); + ret = -EINVAL; + goto dbbt_page_copy_err; + } + } + + /* next read location */ + off += g_boot_search_stride; + } + + dump_structure(boot_cfg, &fcb, &dbbt, dbbt_data_page); + +dbbt_page_copy_err: + kfree(dbbt_data_page_copy); +dbbt_page_err: + kfree(dbbt_data_page); + + return ret; +} + +static int do_nandbcb_dump(int argc, char * const argv[]) +{ + struct boot_config cfg; + int ret; + + if (argc != 2) + return CMD_RET_USAGE; + + memset(&cfg, 0, sizeof(struct boot_config)); + if (nandbcb_get_info(argc, argv, &cfg)) + return CMD_RET_FAILURE; + + if (nandbcb_get_size(argc, argv, 1, &cfg)) + return CMD_RET_FAILURE; + + if (nandbcb_set_boot_config(argc, argv, &cfg)) + return CMD_RET_FAILURE; + + ret = nandbcb_dump(&cfg); + if (ret) + return ret; + + return ret; +} + +static int do_nandbcb_init(int argc, char * const argv[]) +{ + u_char *buf; + size_t size; + loff_t addr; + char *endp; + int ret; + struct boot_config cfg; + + if (argc != 4) + return CMD_RET_USAGE; + + memset(&cfg, 0, sizeof(struct boot_config)); + if (nandbcb_get_info(argc, argv, &cfg)) + return CMD_RET_FAILURE; + + if (nandbcb_get_size(argc, argv, 2, &cfg)) + return CMD_RET_FAILURE; + size = cfg.boot_stream1_size; + + if (nandbcb_set_boot_config(argc, argv, &cfg)) + return CMD_RET_FAILURE; + + addr = simple_strtoul(argv[1], &endp, 16); + if (*argv[1] == 0 || *endp != 0) + return CMD_RET_FAILURE; + + buf = map_physmem(addr, size, MAP_WRBACK); + if (!buf) { + puts("failed to map physical memory\n"); + return CMD_RET_FAILURE; + } + + ret = nandbcb_init(&cfg, buf); + + return ret == 0 ? CMD_RET_SUCCESS : CMD_RET_FAILURE; +} + +static int do_nandbcb(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + const char *cmd; + int ret = 0; + + if (argc < 3) + goto usage; + + /* check the platform config first */ + if (is_mx6sx()) { + plat_config = imx6sx_plat_config; + } else if (is_mx7()) { + plat_config = imx7d_plat_config; + } else if (is_mx6ul() || is_mx6ull()) { + plat_config = imx6ul_plat_config; + } else if (is_mx6() && !is_mx6sx() && !is_mx6ul() && !is_mx6ull()) { + plat_config = imx6qdl_plat_config; + } else if (is_imx8mq()) { + plat_config = imx8mq_plat_config; + } else if (is_imx8mm()) { + plat_config = imx8mm_plat_config; + } else if (is_imx8mn() || is_imx8mp()) { + plat_config = imx8mn_plat_config; + } else if (is_imx8qm() || is_imx8qxp()) { + plat_config = imx8q_plat_config; + } else { + printf("ERROR: Unknown platform\n"); + return CMD_RET_FAILURE; + } + + if ((plat_config.misc_flags) & BT_SEARCH_CNT_FROM_FUSE) { + if (is_imx8qxp()) + g_boot_search_count = fuse_to_search_count(0, 720, 0xc0, 6); + if (is_imx8mn() || is_imx8mp()) + g_boot_search_count = fuse_to_search_count(2, 2, 0x6000, 13); + printf("search count set to %d from fuse\n", + g_boot_search_count); + } + + cmd = argv[1]; + --argc; + ++argv; + + if (strcmp(cmd, "init") == 0) { + ret = do_nandbcb_init(argc, argv); + goto done; + } + + if (strcmp(cmd, "dump") == 0) { + ret = do_nandbcb_dump(argc, argv); + goto done; + } + + if (strcmp(cmd, "bcbonly") == 0) { + ret = do_nandbcb_bcbonly(argc, argv); + goto done; + } + +done: + if (ret != -1) + return ret; +usage: + return CMD_RET_USAGE; +} + +#ifdef CONFIG_SYS_LONGHELP +static char nandbcb_help_text[] = + "init addr off|partition len - update 'len' bytes starting at\n" + " 'off|part' to memory address 'addr', skipping bad blocks\n" + "nandbcb bcbonly off|partition fw1-off fw1-size [fw2-off fw2-size]\n" + " - write BCB only (FCB and DBBT)\n" + " where `fwx-size` is fw sizes in bytes, `fw1-off`\n" + " and `fw2-off` - firmware offsets\n" + " FIY, BCB isn't erased automatically, so mtd erase should\n" + " be called in advance before writing new BCB:\n" + " > mtd erase mx7-bcb\n" + "nandbcb dump off|partition - dump/verify boot structures\n"; +#endif + +U_BOOT_CMD(nandbcb, 7, 1, do_nandbcb, + "i.MX NAND Boot Control Blocks write", + nandbcb_help_text +); diff --git a/roms/u-boot/arch/arm/mach-imx/cpu.c b/roms/u-boot/arch/arm/mach-imx/cpu.c new file mode 100644 index 000000000..423b71535 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/cpu.c @@ -0,0 +1,514 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2007 + * Sascha Hauer, Pengutronix + * + * (C) Copyright 2009 Freescale Semiconductor, Inc. + */ + +#include <bootm.h> +#include <common.h> +#include <dm.h> +#include <init.h> +#include <log.h> +#include <net.h> +#include <netdev.h> +#include <linux/errno.h> +#include <asm/io.h> +#include <asm/arch/imx-regs.h> +#include <asm/arch/clock.h> +#include <asm/arch/sys_proto.h> +#include <asm/arch/crm_regs.h> +#include <asm/mach-imx/boot_mode.h> +#include <imx_thermal.h> +#include <ipu_pixfmt.h> +#include <thermal.h> +#include <sata.h> +#include <dm/device-internal.h> +#include <dm/uclass-internal.h> + +#ifdef CONFIG_FSL_ESDHC_IMX +#include <fsl_esdhc_imx.h> +#endif + +static u32 reset_cause = -1; + +u32 get_imx_reset_cause(void) +{ + struct src *src_regs = (struct src *)SRC_BASE_ADDR; + + if (reset_cause == -1) { + reset_cause = readl(&src_regs->srsr); +/* preserve the value for U-Boot proper */ +#if !defined(CONFIG_SPL_BUILD) + writel(reset_cause, &src_regs->srsr); +#endif + } + + return reset_cause; +} + +#if defined(CONFIG_DISPLAY_CPUINFO) && !defined(CONFIG_SPL_BUILD) +static char *get_reset_cause(void) +{ + switch (get_imx_reset_cause()) { + case 0x00001: + case 0x00011: + return "POR"; + case 0x00004: + return "CSU"; + case 0x00008: + return "IPP USER"; + case 0x00010: +#ifdef CONFIG_MX7 + return "WDOG1"; +#else + return "WDOG"; +#endif + case 0x00020: + return "JTAG HIGH-Z"; + case 0x00040: + return "JTAG SW"; + case 0x00080: + return "WDOG3"; +#ifdef CONFIG_MX7 + case 0x00100: + return "WDOG4"; + case 0x00200: + return "TEMPSENSE"; +#elif defined(CONFIG_IMX8M) + case 0x00100: + return "WDOG2"; + case 0x00200: + return "TEMPSENSE"; +#else + case 0x00100: + return "TEMPSENSE"; + case 0x10000: + return "WARM BOOT"; +#endif + default: + return "unknown reset"; + } +} +#endif + +#if defined(CONFIG_DISPLAY_CPUINFO) && !defined(CONFIG_SPL_BUILD) + +const char *get_imx_type(u32 imxtype) +{ + switch (imxtype) { + case MXC_CPU_IMX8MP: + return "8MP[8]"; /* Quad-core version of the imx8mp */ + case MXC_CPU_IMX8MPD: + return "8MP Dual[3]"; /* Dual-core version of the imx8mp */ + case MXC_CPU_IMX8MPL: + return "8MP Lite[4]"; /* Quad-core Lite version of the imx8mp */ + case MXC_CPU_IMX8MP6: + return "8MP[6]"; /* Quad-core version of the imx8mp, NPU fused */ + case MXC_CPU_IMX8MN: + return "8MNano Quad"; /* Quad-core version */ + case MXC_CPU_IMX8MND: + return "8MNano Dual"; /* Dual-core version */ + case MXC_CPU_IMX8MNS: + return "8MNano Solo"; /* Single-core version */ + case MXC_CPU_IMX8MNL: + return "8MNano QuadLite"; /* Quad-core Lite version */ + case MXC_CPU_IMX8MNDL: + return "8MNano DualLite"; /* Dual-core Lite version */ + case MXC_CPU_IMX8MNSL: + return "8MNano SoloLite";/* Single-core Lite version of the imx8mn */ + case MXC_CPU_IMX8MNUQ: + return "8MNano UltraLite Quad";/* Quad-core UltraLite version of the imx8mn */ + case MXC_CPU_IMX8MNUD: + return "8MNano UltraLite Dual";/* Dual-core UltraLite version of the imx8mn */ + case MXC_CPU_IMX8MNUS: + return "8MNano UltraLite Solo";/* Single-core UltraLite version of the imx8mn */ + case MXC_CPU_IMX8MM: + return "8MMQ"; /* Quad-core version of the imx8mm */ + case MXC_CPU_IMX8MML: + return "8MMQL"; /* Quad-core Lite version of the imx8mm */ + case MXC_CPU_IMX8MMD: + return "8MMD"; /* Dual-core version of the imx8mm */ + case MXC_CPU_IMX8MMDL: + return "8MMDL"; /* Dual-core Lite version of the imx8mm */ + case MXC_CPU_IMX8MMS: + return "8MMS"; /* Single-core version of the imx8mm */ + case MXC_CPU_IMX8MMSL: + return "8MMSL"; /* Single-core Lite version of the imx8mm */ + case MXC_CPU_IMX8MQ: + return "8MQ"; /* Quad-core version of the imx8mq */ + case MXC_CPU_IMX8MQL: + return "8MQLite"; /* Quad-core Lite version of the imx8mq */ + case MXC_CPU_IMX8MD: + return "8MD"; /* Dual-core version of the imx8mq */ + case MXC_CPU_MX7S: + return "7S"; /* Single-core version of the mx7 */ + case MXC_CPU_MX7D: + return "7D"; /* Dual-core version of the mx7 */ + case MXC_CPU_MX6QP: + return "6QP"; /* Quad-Plus version of the mx6 */ + case MXC_CPU_MX6DP: + return "6DP"; /* Dual-Plus version of the mx6 */ + case MXC_CPU_MX6Q: + return "6Q"; /* Quad-core version of the mx6 */ + case MXC_CPU_MX6D: + return "6D"; /* Dual-core version of the mx6 */ + case MXC_CPU_MX6DL: + return "6DL"; /* Dual Lite version of the mx6 */ + case MXC_CPU_MX6SOLO: + return "6SOLO"; /* Solo version of the mx6 */ + case MXC_CPU_MX6SL: + return "6SL"; /* Solo-Lite version of the mx6 */ + case MXC_CPU_MX6SLL: + return "6SLL"; /* SLL version of the mx6 */ + case MXC_CPU_MX6SX: + return "6SX"; /* SoloX version of the mx6 */ + case MXC_CPU_MX6UL: + return "6UL"; /* Ultra-Lite version of the mx6 */ + case MXC_CPU_MX6ULL: + return "6ULL"; /* ULL version of the mx6 */ + case MXC_CPU_MX6ULZ: + return "6ULZ"; /* ULZ version of the mx6 */ + case MXC_CPU_MX51: + return "51"; + case MXC_CPU_MX53: + return "53"; + default: + return "??"; + } +} + +int print_cpuinfo(void) +{ + u32 cpurev; + __maybe_unused u32 max_freq; + + cpurev = get_cpu_rev(); + +#if defined(CONFIG_IMX_THERMAL) || defined(CONFIG_IMX_TMU) + struct udevice *thermal_dev; + int cpu_tmp, minc, maxc, ret; + + printf("CPU: Freescale i.MX%s rev%d.%d", + get_imx_type((cpurev & 0x1FF000) >> 12), + (cpurev & 0x000F0) >> 4, + (cpurev & 0x0000F) >> 0); + max_freq = get_cpu_speed_grade_hz(); + if (!max_freq || max_freq == mxc_get_clock(MXC_ARM_CLK)) { + printf(" at %dMHz\n", mxc_get_clock(MXC_ARM_CLK) / 1000000); + } else { + printf(" %d MHz (running at %d MHz)\n", max_freq / 1000000, + mxc_get_clock(MXC_ARM_CLK) / 1000000); + } +#else + printf("CPU: Freescale i.MX%s rev%d.%d at %d MHz\n", + get_imx_type((cpurev & 0x1FF000) >> 12), + (cpurev & 0x000F0) >> 4, + (cpurev & 0x0000F) >> 0, + mxc_get_clock(MXC_ARM_CLK) / 1000000); +#endif + +#if defined(CONFIG_IMX_THERMAL) || defined(CONFIG_IMX_TMU) + puts("CPU: "); + switch (get_cpu_temp_grade(&minc, &maxc)) { + case TEMP_AUTOMOTIVE: + puts("Automotive temperature grade "); + break; + case TEMP_INDUSTRIAL: + puts("Industrial temperature grade "); + break; + case TEMP_EXTCOMMERCIAL: + puts("Extended Commercial temperature grade "); + break; + default: + puts("Commercial temperature grade "); + break; + } + printf("(%dC to %dC)", minc, maxc); + ret = uclass_get_device(UCLASS_THERMAL, 0, &thermal_dev); + if (!ret) { + ret = thermal_get_temp(thermal_dev, &cpu_tmp); + + if (!ret) + printf(" at %dC", cpu_tmp); + else + debug(" - invalid sensor data\n"); + } else { + debug(" - invalid sensor device\n"); + } + puts("\n"); +#endif + + printf("Reset cause: %s\n", get_reset_cause()); + return 0; +} +#endif + +int cpu_eth_init(struct bd_info *bis) +{ + int rc = -ENODEV; + +#if defined(CONFIG_FEC_MXC) + rc = fecmxc_initialize(bis); +#endif + + return rc; +} + +#ifdef CONFIG_FSL_ESDHC_IMX +/* + * Initializes on-chip MMC controllers. + * to override, implement board_mmc_init() + */ +int cpu_mmc_init(struct bd_info *bis) +{ + return fsl_esdhc_mmc_init(bis); +} +#endif + +#if !(defined(CONFIG_MX7) || defined(CONFIG_IMX8M)) +u32 get_ahb_clk(void) +{ + struct mxc_ccm_reg *imx_ccm = (struct mxc_ccm_reg *)CCM_BASE_ADDR; + u32 reg, ahb_podf; + + reg = __raw_readl(&imx_ccm->cbcdr); + reg &= MXC_CCM_CBCDR_AHB_PODF_MASK; + ahb_podf = reg >> MXC_CCM_CBCDR_AHB_PODF_OFFSET; + + return get_periph_clk() / (ahb_podf + 1); +} +#endif + +void arch_preboot_os(void) +{ +#if defined(CONFIG_PCIE_IMX) && !CONFIG_IS_ENABLED(DM_PCI) + imx_pcie_remove(); +#endif + +#if defined(CONFIG_IMX_AHCI) + struct udevice *dev; + int rc; + + rc = uclass_find_device(UCLASS_AHCI, 0, &dev); + if (!rc && dev) { + rc = device_remove(dev, DM_REMOVE_NORMAL); + if (rc) + printf("Cannot remove SATA device '%s' (err=%d)\n", + dev->name, rc); + } +#endif + +#if defined(CONFIG_SATA) + if (!is_mx6sdl()) { + sata_remove(0); +#if defined(CONFIG_MX6) + disable_sata_clock(); +#endif + } +#endif +#if defined(CONFIG_VIDEO_IPUV3) + /* disable video before launching O/S */ + ipuv3_fb_shutdown(); +#endif +#if defined(CONFIG_VIDEO_MXS) && !defined(CONFIG_DM_VIDEO) + lcdif_power_down(); +#endif +} + +#ifndef CONFIG_IMX8M +void set_chipselect_size(int const cs_size) +{ + unsigned int reg; + struct iomuxc *iomuxc_regs = (struct iomuxc *)IOMUXC_BASE_ADDR; + reg = readl(&iomuxc_regs->gpr[1]); + + switch (cs_size) { + case CS0_128: + reg &= ~0x7; /* CS0=128MB, CS1=0, CS2=0, CS3=0 */ + reg |= 0x5; + break; + case CS0_64M_CS1_64M: + reg &= ~0x3F; /* CS0=64MB, CS1=64MB, CS2=0, CS3=0 */ + reg |= 0x1B; + break; + case CS0_64M_CS1_32M_CS2_32M: + reg &= ~0x1FF; /* CS0=64MB, CS1=32MB, CS2=32MB, CS3=0 */ + reg |= 0x4B; + break; + case CS0_32M_CS1_32M_CS2_32M_CS3_32M: + reg &= ~0xFFF; /* CS0=32MB, CS1=32MB, CS2=32MB, CS3=32MB */ + reg |= 0x249; + break; + default: + printf("Unknown chip select size: %d\n", cs_size); + break; + } + + writel(reg, &iomuxc_regs->gpr[1]); +} +#endif + +#if defined(CONFIG_MX7) || defined(CONFIG_IMX8M) +/* + * OCOTP_TESTER3[9:8] (see Fusemap Description Table offset 0x440) + * defines a 2-bit SPEED_GRADING + */ +#define OCOTP_TESTER3_SPEED_SHIFT 8 +enum cpu_speed { + OCOTP_TESTER3_SPEED_GRADE0, + OCOTP_TESTER3_SPEED_GRADE1, + OCOTP_TESTER3_SPEED_GRADE2, + OCOTP_TESTER3_SPEED_GRADE3, + OCOTP_TESTER3_SPEED_GRADE4, +}; + +u32 get_cpu_speed_grade_hz(void) +{ + struct ocotp_regs *ocotp = (struct ocotp_regs *)OCOTP_BASE_ADDR; + struct fuse_bank *bank = &ocotp->bank[1]; + struct fuse_bank1_regs *fuse = + (struct fuse_bank1_regs *)bank->fuse_regs; + uint32_t val; + + val = readl(&fuse->tester3); + val >>= OCOTP_TESTER3_SPEED_SHIFT; + + if (is_imx8mn() || is_imx8mp()) { + val &= 0xf; + return 2300000000 - val * 100000000; + } + + if (is_imx8mm()) + val &= 0x7; + else + val &= 0x3; + + switch(val) { + case OCOTP_TESTER3_SPEED_GRADE0: + return 800000000; + case OCOTP_TESTER3_SPEED_GRADE1: + return (is_mx7() ? 500000000 : (is_imx8mq() ? 1000000000 : 1200000000)); + case OCOTP_TESTER3_SPEED_GRADE2: + return (is_mx7() ? 1000000000 : (is_imx8mq() ? 1300000000 : 1600000000)); + case OCOTP_TESTER3_SPEED_GRADE3: + return (is_mx7() ? 1200000000 : (is_imx8mq() ? 1500000000 : 1800000000)); + case OCOTP_TESTER3_SPEED_GRADE4: + return 2000000000; + } + + return 0; +} + +/* + * OCOTP_TESTER3[7:6] (see Fusemap Description Table offset 0x440) + * defines a 2-bit SPEED_GRADING + */ +#define OCOTP_TESTER3_TEMP_SHIFT 6 + +/* iMX8MP uses OCOTP_TESTER3[6:5] for Market segment */ +#define IMX8MP_OCOTP_TESTER3_TEMP_SHIFT 5 + +u32 get_cpu_temp_grade(int *minc, int *maxc) +{ + struct ocotp_regs *ocotp = (struct ocotp_regs *)OCOTP_BASE_ADDR; + struct fuse_bank *bank = &ocotp->bank[1]; + struct fuse_bank1_regs *fuse = + (struct fuse_bank1_regs *)bank->fuse_regs; + uint32_t val; + + val = readl(&fuse->tester3); + if (is_imx8mp()) + val >>= IMX8MP_OCOTP_TESTER3_TEMP_SHIFT; + else + val >>= OCOTP_TESTER3_TEMP_SHIFT; + val &= 0x3; + + if (minc && maxc) { + if (val == TEMP_AUTOMOTIVE) { + *minc = -40; + *maxc = 125; + } else if (val == TEMP_INDUSTRIAL) { + *minc = -40; + *maxc = 105; + } else if (val == TEMP_EXTCOMMERCIAL) { + *minc = -20; + *maxc = 105; + } else { + *minc = 0; + *maxc = 95; + } + } + return val; +} +#endif + +#if defined(CONFIG_MX7) || defined(CONFIG_IMX8MQ) || defined(CONFIG_IMX8MM) +enum boot_device get_boot_device(void) +{ + struct bootrom_sw_info **p = + (struct bootrom_sw_info **)(ulong)ROM_SW_INFO_ADDR; + + enum boot_device boot_dev = SD1_BOOT; + u8 boot_type = (*p)->boot_dev_type; + u8 boot_instance = (*p)->boot_dev_instance; + + switch (boot_type) { + case BOOT_TYPE_SD: + boot_dev = boot_instance + SD1_BOOT; + break; + case BOOT_TYPE_MMC: + boot_dev = boot_instance + MMC1_BOOT; + break; + case BOOT_TYPE_NAND: + boot_dev = NAND_BOOT; + break; + case BOOT_TYPE_QSPI: + boot_dev = QSPI_BOOT; + break; + case BOOT_TYPE_WEIM: + boot_dev = WEIM_NOR_BOOT; + break; + case BOOT_TYPE_SPINOR: + boot_dev = SPI_NOR_BOOT; + break; + case BOOT_TYPE_USB: + boot_dev = USB_BOOT; + break; + default: +#ifdef CONFIG_IMX8M + if (((readl(SRC_BASE_ADDR + 0x58) & 0x00007FFF) >> 12) == 0x4) + boot_dev = QSPI_BOOT; +#endif + break; + } + + return boot_dev; +} +#endif + +#ifdef CONFIG_NXP_BOARD_REVISION +int nxp_board_rev(void) +{ + /* + * Get Board ID information from OCOTP_GP1[15:8] + * RevA: 0x1 + * RevB: 0x2 + * RevC: 0x3 + */ + struct ocotp_regs *ocotp = (struct ocotp_regs *)OCOTP_BASE_ADDR; + struct fuse_bank *bank = &ocotp->bank[4]; + struct fuse_bank4_regs *fuse = + (struct fuse_bank4_regs *)bank->fuse_regs; + + return (readl(&fuse->gp1) >> 8 & 0x0F); +} + +char nxp_board_rev_string(void) +{ + const char *rev = "A"; + + return (*rev + nxp_board_rev() - 1); +} +#endif diff --git a/roms/u-boot/arch/arm/mach-imx/ddrmc-vf610-calibration.c b/roms/u-boot/arch/arm/mach-imx/ddrmc-vf610-calibration.c new file mode 100644 index 000000000..cd7e95e61 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/ddrmc-vf610-calibration.c @@ -0,0 +1,343 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * ddrmc DDR3 calibration code for NXP's VF610 + * + * Copyright (C) 2018 DENX Software Engineering + * Lukasz Majewski, DENX Software Engineering, lukma@denx.de + * + */ +/* #define DEBUG */ +#include <common.h> +#include <log.h> +#include <asm/io.h> +#include <asm/arch/imx-regs.h> +#include <linux/bitmap.h> + +#include "ddrmc-vf610-calibration.h" + +/* + * Documents: + * + * [1] "Vybrid: About DDR leveling feature on DDRMC." + * https://community.nxp.com/thread/395323 + * + * [2] VFxxx Controller Reference Manual, Rev. 0, 10/2016 + * + * + * NOTE + * ==== + * + * NXP recommends setting 'fixed' parameters instead of performing the + * training at each boot. + * + * Use those functions to determine those values on new HW, read the + * calculated value from registers and add them to the board specific + * struct ddrmc_cr_setting. + * + * SW leveling supported operations - CR93[SW_LVL_MODE]: + * + * - 0x0 (b'00) - No leveling + * + * - 0x1 (b'01) - WRLVL_DL_X - It is not recommended to perform this tuning + * on HW designs utilizing non-flyback topology + * (Single DDR3 with x16). + * Instead the WRLVL_DL_0/1 fields shall be set + * based on trace length differences from their + * layout. + * Mismatches up to 25% or tCK (clock period) are + * allowed, so the value in the filed doesn’t have + * to be very accurate. + * + * - 0x2 (b'10) - RDLVL_DL_0/1 - refers to adjusting the DQS strobe in relation + * to the DQ signals so that the strobe edge is + * centered in the window of valid read data. + * + * - 0x3 (b'11) - RDLVL_GTDL_0/1 - refers to the delay the PHY uses to un-gate + * the Read DQS strobe pad from the time that the + * PHY enables the pad to input the strobe signal. + * + */ +static int ddr_cal_get_first_edge_index(unsigned long *bmap, enum edge e, + int samples, int start, int max) +{ + int i, ret = -1; + + /* + * We look only for the first value (and filter out + * some wrong data) + */ + switch (e) { + case RISING_EDGE: + for (i = start; i <= max - samples; i++) { + if (test_bit(i, bmap)) { + if (!test_bit(i - 1, bmap) && + test_bit(i + 1, bmap) && + test_bit(i + 2, bmap) && + test_bit(i + 3, bmap)) { + return i; + } + } + } + break; + case FALLING_EDGE: + for (i = start; i <= max - samples; i++) { + if (!test_bit(i, bmap)) { + if (test_bit(i - 1, bmap) && + test_bit(i - 2, bmap) && + test_bit(i - 3, bmap)) { + return i; + } + } + } + } + + return ret; +} + +static void bitmap_print(unsigned long *bmap, int max) +{ + int i; + + debug("BITMAP [0x%p]:\n", bmap); + for (i = 0; i <= max; i++) { + debug("%d ", test_bit(i, bmap) ? 1 : 0); + if (i && (i % 32) == (32 - 1)) + debug("\n"); + } + debug("\n"); +} + +#define sw_leveling_op_done \ + while (!(readl(&ddrmr->cr[94]) & DDRMC_CR94_SWLVL_OP_DONE)) + +#define sw_leveling_load_value \ + do { clrsetbits_le32(&ddrmr->cr[93], DDRMC_CR93_SWLVL_LOAD, \ + DDRMC_CR93_SWLVL_LOAD); } while (0) + +#define sw_leveling_start \ + do { clrsetbits_le32(&ddrmr->cr[93], DDRMC_CR93_SWLVL_START, \ + DDRMC_CR93_SWLVL_START); } while (0) + +#define sw_leveling_exit \ + do { clrsetbits_le32(&ddrmr->cr[94], DDRMC_CR94_SWLVL_EXIT, \ + DDRMC_CR94_SWLVL_EXIT); } while (0) + +/* + * RDLVL_DL calibration: + * + * NXP is _NOT_ recommending performing the leveling at each + * boot. Instead - one shall run this procedure on new boards + * and then use hardcoded values. + * + */ +static int ddrmc_cal_dqs_to_dq(struct ddrmr_regs *ddrmr) +{ + DECLARE_BITMAP(rdlvl_rsp, DDRMC_DQS_DQ_MAX_DELAY + 1); + int rdlvl_dl_0_min = -1, rdlvl_dl_0_max = -1; + int rdlvl_dl_1_min = -1, rdlvl_dl_1_max = -1; + int rdlvl_dl_0, rdlvl_dl_1; + u8 swlvl_rsp; + u32 tmp; + int i; + + /* Read defaults */ + u16 rdlvl_dl_0_def = + (readl(&ddrmr->cr[105]) >> DDRMC_CR105_RDLVL_DL_0_OFF) & 0xFFFF; + u16 rdlvl_dl_1_def = readl(&ddrmr->cr[110]) & 0xFFFF; + + debug("\nRDLVL: ======================\n"); + debug("RDLVL: DQS to DQ (RDLVL)\n"); + + debug("RDLVL: RDLVL_DL_0_DFL:\t 0x%x\n", rdlvl_dl_0_def); + debug("RDLVL: RDLVL_DL_1_DFL:\t 0x%x\n", rdlvl_dl_1_def); + + /* + * Set/Read setup for calibration + * + * Values necessary for leveling from Vybrid RM [2] - page 1600 + */ + writel(0x40703030, &ddrmr->cr[144]); + writel(0x40, &ddrmr->cr[145]); + writel(0x40, &ddrmr->cr[146]); + + tmp = readl(&ddrmr->cr[144]); + debug("RDLVL: PHY_RDLVL_RES:\t 0x%x\n", (tmp >> 24) & 0xFF);// set 0x40 + debug("RDLVL: PHY_RDLV_LOAD:\t 0x%x\n", (tmp >> 16) & 0xFF);// set 0x70 + debug("RDLVL: PHY_RDLV_DLL:\t 0x%x\n", (tmp >> 8) & 0xFF); // set 0x30 + debug("RDLVL: PHY_RDLV_EN:\t 0x%x\n", tmp & 0xFF); //set 0x30 + + tmp = readl(&ddrmr->cr[145]); + debug("RDLVL: PHY_RDLV_RR:\t 0x%x\n", tmp & 0x3FF); //set 0x40 + + tmp = readl(&ddrmr->cr[146]); + debug("RDLVL: PHY_RDLV_RESP:\t 0x%x\n", tmp); //set 0x40 + + /* + * Program/read the leveling edge RDLVL_EDGE = 0 + * + * 0x00 is the correct output on SWLVL_RSP_X + * If by any chance 1s are visible -> wrong number read + */ + clrbits_le32(&ddrmr->cr[101], DDRMC_CR101_PHY_RDLVL_EDGE); + + tmp = readl(&ddrmr->cr[101]); + debug("RDLVL: PHY_RDLVL_EDGE:\t 0x%x\n", + (tmp >> DDRMC_CR101_PHY_RDLVL_EDGE_OFF) & 0x1); //set 0 + + /* Program Leveling mode - CR93[SW_LVL_MODE] to ’b10 */ + clrsetbits_le32(&ddrmr->cr[93], DDRMC_CR93_SW_LVL_MODE(0x3), + DDRMC_CR93_SW_LVL_MODE(0x2)); + tmp = readl(&ddrmr->cr[93]); + debug("RDLVL: SW_LVL_MODE:\t 0x%x\n", + (tmp >> DDRMC_CR93_SW_LVL_MODE_OFF) & 0x3); + + /* Start procedure - CR93[SWLVL_START] to ’b1 */ + sw_leveling_start; + + /* Poll CR94[SWLVL_OP_DONE] */ + sw_leveling_op_done; + + /* + * Program delays for RDLVL_DL_0 + * + * The procedure is to increase the delay values from 0 to 0xFF + * and read the response from the DDRMC + */ + debug("\nRDLVL: ---> RDLVL_DL_0\n"); + bitmap_zero(rdlvl_rsp, DDRMC_DQS_DQ_MAX_DELAY + 1); + + for (i = 0; i <= DDRMC_DQS_DQ_MAX_DELAY; i++) { + clrsetbits_le32(&ddrmr->cr[105], + 0xFFFF << DDRMC_CR105_RDLVL_DL_0_OFF, + i << DDRMC_CR105_RDLVL_DL_0_OFF); + + /* Load values CR93[SWLVL_LOAD] to ’b1 */ + sw_leveling_load_value; + + /* Poll CR94[SWLVL_OP_DONE] */ + sw_leveling_op_done; + + /* + * Read Responses - SWLVL_RESP_0 + * + * The 0x00 (correct response when PHY_RDLVL_EDGE = 0) + * -> 1 in the bit vector + */ + swlvl_rsp = (readl(&ddrmr->cr[94]) >> + DDRMC_CR94_SWLVL_RESP_0_OFF) & 0xF; + if (swlvl_rsp == 0) + generic_set_bit(i, rdlvl_rsp); + } + + bitmap_print(rdlvl_rsp, DDRMC_DQS_DQ_MAX_DELAY); + + /* + * First test for rising edge 0x0 -> 0x1 in bitmap + */ + rdlvl_dl_0_min = ddr_cal_get_first_edge_index(rdlvl_rsp, RISING_EDGE, + N_SAMPLES, N_SAMPLES, + DDRMC_DQS_DQ_MAX_DELAY); + + /* + * Secondly test for falling edge 0x1 -> 0x0 in bitmap + */ + rdlvl_dl_0_max = ddr_cal_get_first_edge_index(rdlvl_rsp, FALLING_EDGE, + N_SAMPLES, rdlvl_dl_0_min, + DDRMC_DQS_DQ_MAX_DELAY); + + debug("RDLVL: DL_0 min: %d [0x%x] DL_0 max: %d [0x%x]\n", + rdlvl_dl_0_min, rdlvl_dl_0_min, rdlvl_dl_0_max, rdlvl_dl_0_max); + rdlvl_dl_0 = (rdlvl_dl_0_max - rdlvl_dl_0_min) / 2; + + if (rdlvl_dl_0_max == -1 || rdlvl_dl_0_min == -1 || rdlvl_dl_0 <= 0) { + debug("RDLVL: The DQS to DQ delay cannot be found!\n"); + debug("RDLVL: Using default - slice 0: %d!\n", rdlvl_dl_0_def); + rdlvl_dl_0 = rdlvl_dl_0_def; + } + + debug("\nRDLVL: ---> RDLVL_DL_1\n"); + bitmap_zero(rdlvl_rsp, DDRMC_DQS_DQ_MAX_DELAY + 1); + + for (i = 0; i <= DDRMC_DQS_DQ_MAX_DELAY; i++) { + clrsetbits_le32(&ddrmr->cr[110], + 0xFFFF << DDRMC_CR110_RDLVL_DL_1_OFF, + i << DDRMC_CR110_RDLVL_DL_1_OFF); + + /* Load values CR93[SWLVL_LOAD] to ’b1 */ + sw_leveling_load_value; + + /* Poll CR94[SWLVL_OP_DONE] */ + sw_leveling_op_done; + + /* + * Read Responses - SWLVL_RESP_1 + * + * The 0x00 (correct response when PHY_RDLVL_EDGE = 0) + * -> 1 in the bit vector + */ + swlvl_rsp = (readl(&ddrmr->cr[95]) >> + DDRMC_CR95_SWLVL_RESP_1_OFF) & 0xF; + if (swlvl_rsp == 0) + generic_set_bit(i, rdlvl_rsp); + } + + bitmap_print(rdlvl_rsp, DDRMC_DQS_DQ_MAX_DELAY); + + /* + * First test for rising edge 0x0 -> 0x1 in bitmap + */ + rdlvl_dl_1_min = ddr_cal_get_first_edge_index(rdlvl_rsp, RISING_EDGE, + N_SAMPLES, N_SAMPLES, + DDRMC_DQS_DQ_MAX_DELAY); + + /* + * Secondly test for falling edge 0x1 -> 0x0 in bitmap + */ + rdlvl_dl_1_max = ddr_cal_get_first_edge_index(rdlvl_rsp, FALLING_EDGE, + N_SAMPLES, rdlvl_dl_1_min, + DDRMC_DQS_DQ_MAX_DELAY); + + debug("RDLVL: DL_1 min: %d [0x%x] DL_1 max: %d [0x%x]\n", + rdlvl_dl_1_min, rdlvl_dl_1_min, rdlvl_dl_1_max, rdlvl_dl_1_max); + rdlvl_dl_1 = (rdlvl_dl_1_max - rdlvl_dl_1_min) / 2; + + if (rdlvl_dl_1_max == -1 || rdlvl_dl_1_min == -1 || rdlvl_dl_1 <= 0) { + debug("RDLVL: The DQS to DQ delay cannot be found!\n"); + debug("RDLVL: Using default - slice 1: %d!\n", rdlvl_dl_1_def); + rdlvl_dl_1 = rdlvl_dl_1_def; + } + + debug("RDLVL: CALIBRATED: rdlvl_dl_0: 0x%x\t rdlvl_dl_1: 0x%x\n", + rdlvl_dl_0, rdlvl_dl_1); + + /* Write new delay values */ + writel(DDRMC_CR105_RDLVL_DL_0(rdlvl_dl_0), &ddrmr->cr[105]); + writel(DDRMC_CR110_RDLVL_DL_1(rdlvl_dl_1), &ddrmr->cr[110]); + + sw_leveling_load_value; + sw_leveling_op_done; + + /* Exit procedure - CR94[SWLVL_EXIT] to ’b1 */ + sw_leveling_exit; + + /* Poll CR94[SWLVL_OP_DONE] */ + sw_leveling_op_done; + + return 0; +} + +/* + * WRLVL_DL calibration: + * + * For non-flyback memory architecture - where one have a single DDR3 x16 + * memory - it is NOT necessary to perform "Write Leveling" + * [3] 'Vybrid DDR3 write leveling' https://community.nxp.com/thread/429362 + * + */ + +int ddrmc_calibration(struct ddrmr_regs *ddrmr) +{ + ddrmc_cal_dqs_to_dq(ddrmr); + + return 0; +} diff --git a/roms/u-boot/arch/arm/mach-imx/ddrmc-vf610-calibration.h b/roms/u-boot/arch/arm/mach-imx/ddrmc-vf610-calibration.h new file mode 100644 index 000000000..e82e217ab --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/ddrmc-vf610-calibration.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * ddrmc DDR3 calibration code for NXP's VF610 + * + * Copyright (C) 2018 DENX Software Engineering + * Lukasz Majewski, DENX Software Engineering, lukma@denx.de + * + */ + +#ifndef __DDRMC_VF610_CALIBRATOIN_H_ +#define __DDRMC_VF610_CALIBRATOIN_H_ + +/* + * Number of "samples" in the calibration bitmap + * to be considered during calibration. + */ +#define N_SAMPLES 3 + +/* + * Constants to indicate if we are looking for a rising or + * falling edge in the calibration bitmap + */ +enum edge { + FALLING_EDGE = 1, + RISING_EDGE +}; + +/* + * The max number of delay elements when DQS to DQ setting + */ +#define DDRMC_DQS_DQ_MAX_DELAY 0xFF + +/** + * ddrmc_calibration - Vybrid's (VF610) DDR3 calibration code + * + * This function is calculating proper memory controller values + * during run time. + * + * @param ddrmr_regs - memory controller registers + * + * @return 0 on success, otherwise error code + */ +int ddrmc_calibration(struct ddrmr_regs *ddrmr); + +#endif /* __DDRMC_VF610_CALIBRATOIN_H_ */ diff --git a/roms/u-boot/arch/arm/mach-imx/ddrmc-vf610.c b/roms/u-boot/arch/arm/mach-imx/ddrmc-vf610.c new file mode 100644 index 000000000..7895ee66f --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/ddrmc-vf610.c @@ -0,0 +1,245 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2015 Toradex, Inc. + * + * Based on vf610twr: + * Copyright 2013 Freescale Semiconductor, Inc. + */ + +#include <common.h> +#include <asm/io.h> +#include <asm/arch/imx-regs.h> +#include <asm/arch/iomux-vf610.h> +#include <asm/arch/ddrmc-vf610.h> +#include <linux/delay.h> +#include "ddrmc-vf610-calibration.h" + +void ddrmc_setup_iomux(const iomux_v3_cfg_t *pads, int pads_count) +{ + static const iomux_v3_cfg_t default_pads[] = { + VF610_PAD_DDR_A15__DDR_A_15, + VF610_PAD_DDR_A14__DDR_A_14, + VF610_PAD_DDR_A13__DDR_A_13, + VF610_PAD_DDR_A12__DDR_A_12, + VF610_PAD_DDR_A11__DDR_A_11, + VF610_PAD_DDR_A10__DDR_A_10, + VF610_PAD_DDR_A9__DDR_A_9, + VF610_PAD_DDR_A8__DDR_A_8, + VF610_PAD_DDR_A7__DDR_A_7, + VF610_PAD_DDR_A6__DDR_A_6, + VF610_PAD_DDR_A5__DDR_A_5, + VF610_PAD_DDR_A4__DDR_A_4, + VF610_PAD_DDR_A3__DDR_A_3, + VF610_PAD_DDR_A2__DDR_A_2, + VF610_PAD_DDR_A1__DDR_A_1, + VF610_PAD_DDR_A0__DDR_A_0, + VF610_PAD_DDR_BA2__DDR_BA_2, + VF610_PAD_DDR_BA1__DDR_BA_1, + VF610_PAD_DDR_BA0__DDR_BA_0, + VF610_PAD_DDR_CAS__DDR_CAS_B, + VF610_PAD_DDR_CKE__DDR_CKE_0, + VF610_PAD_DDR_CLK__DDR_CLK_0, + VF610_PAD_DDR_CS__DDR_CS_B_0, + VF610_PAD_DDR_D15__DDR_D_15, + VF610_PAD_DDR_D14__DDR_D_14, + VF610_PAD_DDR_D13__DDR_D_13, + VF610_PAD_DDR_D12__DDR_D_12, + VF610_PAD_DDR_D11__DDR_D_11, + VF610_PAD_DDR_D10__DDR_D_10, + VF610_PAD_DDR_D9__DDR_D_9, + VF610_PAD_DDR_D8__DDR_D_8, + VF610_PAD_DDR_D7__DDR_D_7, + VF610_PAD_DDR_D6__DDR_D_6, + VF610_PAD_DDR_D5__DDR_D_5, + VF610_PAD_DDR_D4__DDR_D_4, + VF610_PAD_DDR_D3__DDR_D_3, + VF610_PAD_DDR_D2__DDR_D_2, + VF610_PAD_DDR_D1__DDR_D_1, + VF610_PAD_DDR_D0__DDR_D_0, + VF610_PAD_DDR_DQM1__DDR_DQM_1, + VF610_PAD_DDR_DQM0__DDR_DQM_0, + VF610_PAD_DDR_DQS1__DDR_DQS_1, + VF610_PAD_DDR_DQS0__DDR_DQS_0, + VF610_PAD_DDR_RAS__DDR_RAS_B, + VF610_PAD_DDR_WE__DDR_WE_B, + VF610_PAD_DDR_ODT1__DDR_ODT_0, + VF610_PAD_DDR_ODT0__DDR_ODT_1, + VF610_PAD_DDR_DDRBYTE1__DDR_DDRBYTE1, + VF610_PAD_DDR_DDRBYTE2__DDR_DDRBYTE2, + VF610_PAD_DDR_RESETB, + }; + + if ((pads == NULL) || (pads_count == 0)) { + pads = default_pads; + pads_count = ARRAY_SIZE(default_pads); + } + + imx_iomux_v3_setup_multiple_pads(pads, pads_count); +} + +static struct ddrmc_phy_setting default_phy_settings[] = { + { DDRMC_PHY_DQ_TIMING, 0 }, + { DDRMC_PHY_DQ_TIMING, 16 }, + { DDRMC_PHY_DQ_TIMING, 32 }, + + { DDRMC_PHY_DQS_TIMING, 1 }, + { DDRMC_PHY_DQS_TIMING, 17 }, + + { DDRMC_PHY_CTRL, 2 }, + { DDRMC_PHY_CTRL, 18 }, + { DDRMC_PHY_CTRL, 34 }, + + { DDRMC_PHY_MASTER_CTRL, 3 }, + { DDRMC_PHY_MASTER_CTRL, 19 }, + { DDRMC_PHY_MASTER_CTRL, 35 }, + + { DDRMC_PHY_SLAVE_CTRL, 4 }, + { DDRMC_PHY_SLAVE_CTRL, 20 }, + { DDRMC_PHY_SLAVE_CTRL, 36 }, + + /* LPDDR2 only parameter */ + { DDRMC_PHY_OFF, 49 }, + + { DDRMC_PHY50_DDR3_MODE | DDRMC_PHY50_EN_SW_HALF_CYCLE, 50 }, + + /* Processor Pad ODT settings */ + { DDRMC_PHY_PROC_PAD_ODT, 52 }, + + /* end marker */ + { 0, -1 } +}; + +void ddrmc_ctrl_init_ddr3(struct ddr3_jedec_timings const *timings, + struct ddrmc_cr_setting *board_cr_settings, + struct ddrmc_phy_setting *board_phy_settings, + int col_diff, int row_diff) +{ + struct ddrmr_regs *ddrmr = (struct ddrmr_regs *)DDR_BASE_ADDR; + struct ddrmc_cr_setting *cr_setting; + struct ddrmc_phy_setting *phy_setting; + + writel(DDRMC_CR00_DRAM_CLASS_DDR3, &ddrmr->cr[0]); + writel(DDRMC_CR02_DRAM_TINIT(timings->tinit), &ddrmr->cr[2]); + writel(DDRMC_CR10_TRST_PWRON(timings->trst_pwron), &ddrmr->cr[10]); + + writel(DDRMC_CR11_CKE_INACTIVE(timings->cke_inactive), &ddrmr->cr[11]); + writel(DDRMC_CR12_WRLAT(timings->wrlat) | + DDRMC_CR12_CASLAT_LIN(timings->caslat_lin), &ddrmr->cr[12]); + writel(DDRMC_CR13_TRC(timings->trc) | DDRMC_CR13_TRRD(timings->trrd) | + DDRMC_CR13_TCCD(timings->tccd) | + DDRMC_CR13_TBST_INT_INTERVAL(timings->tbst_int_interval), + &ddrmr->cr[13]); + writel(DDRMC_CR14_TFAW(timings->tfaw) | DDRMC_CR14_TRP(timings->trp) | + DDRMC_CR14_TWTR(timings->twtr) | + DDRMC_CR14_TRAS_MIN(timings->tras_min), &ddrmr->cr[14]); + writel(DDRMC_CR16_TMRD(timings->tmrd) | + DDRMC_CR16_TRTP(timings->trtp), &ddrmr->cr[16]); + writel(DDRMC_CR17_TRAS_MAX(timings->tras_max) | + DDRMC_CR17_TMOD(timings->tmod), &ddrmr->cr[17]); + writel(DDRMC_CR18_TCKESR(timings->tckesr) | + DDRMC_CR18_TCKE(timings->tcke), &ddrmr->cr[18]); + + writel(DDRMC_CR20_AP_EN, &ddrmr->cr[20]); + writel(DDRMC_CR21_TRCD_INT(timings->trcd_int) | DDRMC_CR21_CCMAP_EN | + DDRMC_CR21_TRAS_LOCKOUT(timings->tras_lockout), + &ddrmr->cr[21]); + + writel(DDRMC_CR22_TDAL(timings->tdal), &ddrmr->cr[22]); + writel(DDRMC_CR23_BSTLEN(timings->bstlen) | + DDRMC_CR23_TDLL(timings->tdll), &ddrmr->cr[23]); + writel(DDRMC_CR24_TRP_AB(timings->trp_ab), &ddrmr->cr[24]); + + writel(DDRMC_CR25_TREF_EN, &ddrmr->cr[25]); + writel(DDRMC_CR26_TREF(timings->tref) | + DDRMC_CR26_TRFC(timings->trfc), &ddrmr->cr[26]); + writel(DDRMC_CR28_TREF_INT(timings->tref_int), &ddrmr->cr[28]); + writel(DDRMC_CR29_TPDEX(timings->tpdex), &ddrmr->cr[29]); + + writel(DDRMC_CR30_TXPDLL(timings->txpdll), &ddrmr->cr[30]); + writel(DDRMC_CR31_TXSNR(timings->txsnr) | + DDRMC_CR31_TXSR(timings->txsr), &ddrmr->cr[31]); + writel(DDRMC_CR33_EN_QK_SREF, &ddrmr->cr[33]); + writel(DDRMC_CR34_CKSRX(timings->cksrx) | + DDRMC_CR34_CKSRE(timings->cksre), &ddrmr->cr[34]); + + writel(DDRMC_CR38_FREQ_CHG_EN(timings->freq_chg_en), &ddrmr->cr[38]); + writel(DDRMC_CR39_PHY_INI_COM(1024) | DDRMC_CR39_PHY_INI_STA(16) | + DDRMC_CR39_FRQ_CH_DLLOFF(2), &ddrmr->cr[39]); + + writel(DDRMC_CR41_PHY_INI_STRT_INI_DIS, &ddrmr->cr[41]); + writel(DDRMC_CR48_MR1_DA_0(70) | + DDRMC_CR48_MR0_DA_0(1056), &ddrmr->cr[48]); + + writel(DDRMC_CR66_ZQCL(timings->zqcl) | + DDRMC_CR66_ZQINIT(timings->zqinit), &ddrmr->cr[66]); + writel(DDRMC_CR67_ZQCS(timings->zqcs), &ddrmr->cr[67]); + writel(DDRMC_CR69_ZQ_ON_SREF_EX(2), &ddrmr->cr[69]); + + writel(DDRMC_CR70_REF_PER_ZQ(timings->ref_per_zq), &ddrmr->cr[70]); + writel(DDRMC_CR72_ZQCS_ROTATE(timings->zqcs_rotate), &ddrmr->cr[72]); + + writel(DDRMC_CR73_APREBIT(timings->aprebit) | + DDRMC_CR73_COL_DIFF(col_diff) | + DDRMC_CR73_ROW_DIFF(row_diff), &ddrmr->cr[73]); + writel(DDRMC_CR74_BANKSPLT_EN | DDRMC_CR74_ADDR_CMP_EN | + DDRMC_CR74_CMD_AGE_CNT(timings->cmd_age_cnt) | + DDRMC_CR74_AGE_CNT(timings->age_cnt), + &ddrmr->cr[74]); + writel(DDRMC_CR75_RW_PG_EN | DDRMC_CR75_RW_EN | DDRMC_CR75_PRI_EN | + DDRMC_CR75_PLEN, &ddrmr->cr[75]); + writel(DDRMC_CR76_NQENT_ACTDIS(3) | DDRMC_CR76_D_RW_G_BKCN(3) | + DDRMC_CR76_W2R_SPLT_EN, &ddrmr->cr[76]); + writel(DDRMC_CR77_CS_MAP | DDRMC_CR77_DI_RD_INTLEAVE | + DDRMC_CR77_SWAP_EN, &ddrmr->cr[77]); + writel(DDRMC_CR78_Q_FULLNESS(timings->q_fullness) | + DDRMC_CR78_BUR_ON_FLY_BIT(12), &ddrmr->cr[78]); + + writel(DDRMC_CR82_INT_MASK, &ddrmr->cr[82]); + + writel(DDRMC_CR87_ODT_RD_MAPCS0(timings->odt_rd_mapcs0) | + DDRMC_CR87_ODT_WR_MAPCS0(timings->odt_wr_mapcs0), + &ddrmr->cr[87]); + writel(DDRMC_CR88_TODTL_CMD(4), &ddrmr->cr[88]); + writel(DDRMC_CR89_AODT_RWSMCS(2), &ddrmr->cr[89]); + + writel(DDRMC_CR91_R2W_SMCSDL(2), &ddrmr->cr[91]); + writel(DDRMC_CR96_WLMRD(timings->wlmrd) | + DDRMC_CR96_WLDQSEN(timings->wldqsen), &ddrmr->cr[96]); + + /* execute custom CR setting sequence (may be NULL) */ + cr_setting = board_cr_settings; + if (cr_setting != NULL) + while (cr_setting->cr_rnum >= 0) { + writel(cr_setting->setting, + &ddrmr->cr[cr_setting->cr_rnum]); + cr_setting++; + } + + /* perform default PHY settings (may be overridden by custom settings */ + phy_setting = default_phy_settings; + while (phy_setting->phy_rnum >= 0) { + writel(phy_setting->setting, + &ddrmr->phy[phy_setting->phy_rnum]); + phy_setting++; + } + + /* execute custom PHY setting sequence (may be NULL) */ + phy_setting = board_phy_settings; + if (phy_setting != NULL) + while (phy_setting->phy_rnum >= 0) { + writel(phy_setting->setting, + &ddrmr->phy[phy_setting->phy_rnum]); + phy_setting++; + } + + /* all inits done, start the DDR controller */ + writel(DDRMC_CR00_DRAM_CLASS_DDR3 | DDRMC_CR00_START, &ddrmr->cr[0]); + + while (!(readl(&ddrmr->cr[80]) & DDRMC_CR80_MC_INIT_COMPLETE)) + udelay(10); + writel(DDRMC_CR80_MC_INIT_COMPLETE, &ddrmr->cr[81]); + +#ifdef CONFIG_DDRMC_VF610_CALIBRATION + ddrmc_calibration(ddrmr); +#endif +} diff --git a/roms/u-boot/arch/arm/mach-imx/hab.c b/roms/u-boot/arch/arm/mach-imx/hab.c new file mode 100644 index 000000000..00bd157d0 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/hab.c @@ -0,0 +1,1011 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2010-2015 Freescale Semiconductor, Inc. + */ + +#include <common.h> +#include <command.h> +#include <config.h> +#include <fuse.h> +#include <mapmem.h> +#include <image.h> +#include <asm/io.h> +#include <asm/global_data.h> +#include <asm/system.h> +#include <asm/arch/clock.h> +#include <asm/arch/sys_proto.h> +#include <asm/mach-imx/hab.h> +#include <linux/arm-smccc.h> + +DECLARE_GLOBAL_DATA_PTR; + +#define ALIGN_SIZE 0x1000 +#define MX6DQ_PU_IROM_MMU_EN_VAR 0x009024a8 +#define MX6DLS_PU_IROM_MMU_EN_VAR 0x00901dd0 +#define MX6SL_PU_IROM_MMU_EN_VAR 0x00901c60 +#define IS_HAB_ENABLED_BIT \ + (is_soc_type(MXC_SOC_MX7ULP) ? 0x80000000 : \ + ((is_soc_type(MXC_SOC_MX7) || is_soc_type(MXC_SOC_IMX8M)) ? 0x2000000 : 0x2)) + +#ifdef CONFIG_MX7ULP +#define HAB_M4_PERSISTENT_START ((soc_rev() >= CHIP_REV_2_0) ? 0x20008040 : \ + 0x20008180) +#define HAB_M4_PERSISTENT_BYTES 0xB80 +#endif + +static int ivt_header_error(const char *err_str, struct ivt_header *ivt_hdr) +{ + printf("%s magic=0x%x length=0x%02x version=0x%x\n", err_str, + ivt_hdr->magic, ivt_hdr->length, ivt_hdr->version); + + return 1; +} + +static int verify_ivt_header(struct ivt_header *ivt_hdr) +{ + int result = 0; + + if (ivt_hdr->magic != IVT_HEADER_MAGIC) + result = ivt_header_error("bad magic", ivt_hdr); + + if (be16_to_cpu(ivt_hdr->length) != IVT_TOTAL_LENGTH) + result = ivt_header_error("bad length", ivt_hdr); + + if ((ivt_hdr->version & HAB_MAJ_MASK) != HAB_MAJ_VER) + result = ivt_header_error("bad version", ivt_hdr); + + return result; +} + +#ifdef CONFIG_ARM64 +#define FSL_SIP_HAB 0xC2000007 +#define FSL_SIP_HAB_AUTHENTICATE 0x00 +#define FSL_SIP_HAB_ENTRY 0x01 +#define FSL_SIP_HAB_EXIT 0x02 +#define FSL_SIP_HAB_REPORT_EVENT 0x03 +#define FSL_SIP_HAB_REPORT_STATUS 0x04 +#define FSL_SIP_HAB_FAILSAFE 0x05 +#define FSL_SIP_HAB_CHECK_TARGET 0x06 +static volatile gd_t *gd_save; +#endif + +static inline void save_gd(void) +{ +#ifdef CONFIG_ARM64 + gd_save = gd; +#endif +} + +static inline void restore_gd(void) +{ +#ifdef CONFIG_ARM64 + /* + * Make will already error that reserving x18 is not supported at the + * time of writing, clang: error: unknown argument: '-ffixed-x18' + */ + __asm__ volatile("mov x18, %0\n" : : "r" (gd_save)); +#endif +} + +enum hab_status hab_rvt_report_event(enum hab_status status, u32 index, + u8 *event, size_t *bytes) +{ + enum hab_status ret; + hab_rvt_report_event_t *hab_rvt_report_event_func; + struct arm_smccc_res res __maybe_unused; + + hab_rvt_report_event_func = (hab_rvt_report_event_t *)HAB_RVT_REPORT_EVENT; +#if defined(CONFIG_ARM64) + if (current_el() != 3) { + /* call sip */ + arm_smccc_smc(FSL_SIP_HAB, FSL_SIP_HAB_REPORT_EVENT, (unsigned long)index, + (unsigned long)event, (unsigned long)bytes, 0, 0, 0, &res); + return (enum hab_status)res.a0; + } +#endif + + save_gd(); + ret = hab_rvt_report_event_func(status, index, event, bytes); + restore_gd(); + + return ret; + +} + +enum hab_status hab_rvt_report_status(enum hab_config *config, enum hab_state *state) +{ + enum hab_status ret; + hab_rvt_report_status_t *hab_rvt_report_status_func; + struct arm_smccc_res res __maybe_unused; + + hab_rvt_report_status_func = (hab_rvt_report_status_t *)HAB_RVT_REPORT_STATUS; +#if defined(CONFIG_ARM64) + if (current_el() != 3) { + /* call sip */ + arm_smccc_smc(FSL_SIP_HAB, FSL_SIP_HAB_REPORT_STATUS, (unsigned long)config, + (unsigned long)state, 0, 0, 0, 0, &res); + return (enum hab_status)res.a0; + } +#endif + + save_gd(); + ret = hab_rvt_report_status_func(config, state); + restore_gd(); + + return ret; +} + +enum hab_status hab_rvt_entry(void) +{ + enum hab_status ret; + hab_rvt_entry_t *hab_rvt_entry_func; + struct arm_smccc_res res __maybe_unused; + + hab_rvt_entry_func = (hab_rvt_entry_t *)HAB_RVT_ENTRY; +#if defined(CONFIG_ARM64) + if (current_el() != 3) { + /* call sip */ + arm_smccc_smc(FSL_SIP_HAB, FSL_SIP_HAB_ENTRY, 0, 0, 0, 0, 0, 0, &res); + return (enum hab_status)res.a0; + } +#endif + + save_gd(); + ret = hab_rvt_entry_func(); + restore_gd(); + + return ret; +} + +enum hab_status hab_rvt_exit(void) +{ + enum hab_status ret; + hab_rvt_exit_t *hab_rvt_exit_func; + struct arm_smccc_res res __maybe_unused; + + hab_rvt_exit_func = (hab_rvt_exit_t *)HAB_RVT_EXIT; +#if defined(CONFIG_ARM64) + if (current_el() != 3) { + /* call sip */ + arm_smccc_smc(FSL_SIP_HAB, FSL_SIP_HAB_EXIT, 0, 0, 0, 0, 0, 0, &res); + return (enum hab_status)res.a0; + } +#endif + + save_gd(); + ret = hab_rvt_exit_func(); + restore_gd(); + + return ret; +} + +void hab_rvt_failsafe(void) +{ + hab_rvt_failsafe_t *hab_rvt_failsafe_func; + + hab_rvt_failsafe_func = (hab_rvt_failsafe_t *)HAB_RVT_FAILSAFE; +#if defined(CONFIG_ARM64) + if (current_el() != 3) { + /* call sip */ + arm_smccc_smc(FSL_SIP_HAB, FSL_SIP_HAB_FAILSAFE, 0, 0, 0, 0, 0, 0, NULL); + return; + } +#endif + + save_gd(); + hab_rvt_failsafe_func(); + restore_gd(); +} + +enum hab_status hab_rvt_check_target(enum hab_target type, const void *start, + size_t bytes) +{ + enum hab_status ret; + hab_rvt_check_target_t *hab_rvt_check_target_func; + struct arm_smccc_res res __maybe_unused; + + hab_rvt_check_target_func = (hab_rvt_check_target_t *)HAB_RVT_CHECK_TARGET; +#if defined(CONFIG_ARM64) + if (current_el() != 3) { + /* call sip */ + arm_smccc_smc(FSL_SIP_HAB, FSL_SIP_HAB_CHECK_TARGET, (unsigned long)type, + (unsigned long)start, (unsigned long)bytes, 0, 0, 0, &res); + return (enum hab_status)res.a0; + } +#endif + + save_gd(); + ret = hab_rvt_check_target_func(type, start, bytes); + restore_gd(); + + return ret; +} + +void *hab_rvt_authenticate_image(uint8_t cid, ptrdiff_t ivt_offset, + void **start, size_t *bytes, hab_loader_callback_f_t loader) +{ + void *ret; + hab_rvt_authenticate_image_t *hab_rvt_authenticate_image_func; + struct arm_smccc_res res __maybe_unused; + + hab_rvt_authenticate_image_func = (hab_rvt_authenticate_image_t *)HAB_RVT_AUTHENTICATE_IMAGE; +#if defined(CONFIG_ARM64) + if (current_el() != 3) { + /* call sip */ + arm_smccc_smc(FSL_SIP_HAB, FSL_SIP_HAB_AUTHENTICATE, (unsigned long)ivt_offset, + (unsigned long)start, (unsigned long)bytes, 0, 0, 0, &res); + return (void *)res.a0; + } +#endif + + save_gd(); + ret = hab_rvt_authenticate_image_func(cid, ivt_offset, start, bytes, loader); + restore_gd(); + + return ret; +} + +#if !defined(CONFIG_SPL_BUILD) + +#define MAX_RECORD_BYTES (8*1024) /* 4 kbytes */ + +struct record { + uint8_t tag; /* Tag */ + uint8_t len[2]; /* Length */ + uint8_t par; /* Version */ + uint8_t contents[MAX_RECORD_BYTES];/* Record Data */ + bool any_rec_flag; +}; + +static char *rsn_str[] = { + "RSN = HAB_RSN_ANY (0x00)\n", + "RSN = HAB_ENG_FAIL (0x30)\n", + "RSN = HAB_INV_ADDRESS (0x22)\n", + "RSN = HAB_INV_ASSERTION (0x0C)\n", + "RSN = HAB_INV_CALL (0x28)\n", + "RSN = HAB_INV_CERTIFICATE (0x21)\n", + "RSN = HAB_INV_COMMAND (0x06)\n", + "RSN = HAB_INV_CSF (0x11)\n", + "RSN = HAB_INV_DCD (0x27)\n", + "RSN = HAB_INV_INDEX (0x0F)\n", + "RSN = HAB_INV_IVT (0x05)\n", + "RSN = HAB_INV_KEY (0x1D)\n", + "RSN = HAB_INV_RETURN (0x1E)\n", + "RSN = HAB_INV_SIGNATURE (0x18)\n", + "RSN = HAB_INV_SIZE (0x17)\n", + "RSN = HAB_MEM_FAIL (0x2E)\n", + "RSN = HAB_OVR_COUNT (0x2B)\n", + "RSN = HAB_OVR_STORAGE (0x2D)\n", + "RSN = HAB_UNS_ALGORITHM (0x12)\n", + "RSN = HAB_UNS_COMMAND (0x03)\n", + "RSN = HAB_UNS_ENGINE (0x0A)\n", + "RSN = HAB_UNS_ITEM (0x24)\n", + "RSN = HAB_UNS_KEY (0x1B)\n", + "RSN = HAB_UNS_PROTOCOL (0x14)\n", + "RSN = HAB_UNS_STATE (0x09)\n", + "RSN = INVALID\n", + NULL +}; + +static char *sts_str[] = { + "STS = HAB_SUCCESS (0xF0)\n", + "STS = HAB_FAILURE (0x33)\n", + "STS = HAB_WARNING (0x69)\n", + "STS = INVALID\n", + NULL +}; + +static char *eng_str[] = { + "ENG = HAB_ENG_ANY (0x00)\n", + "ENG = HAB_ENG_SCC (0x03)\n", + "ENG = HAB_ENG_RTIC (0x05)\n", + "ENG = HAB_ENG_SAHARA (0x06)\n", + "ENG = HAB_ENG_CSU (0x0A)\n", + "ENG = HAB_ENG_SRTC (0x0C)\n", + "ENG = HAB_ENG_DCP (0x1B)\n", + "ENG = HAB_ENG_CAAM (0x1D)\n", + "ENG = HAB_ENG_SNVS (0x1E)\n", + "ENG = HAB_ENG_OCOTP (0x21)\n", + "ENG = HAB_ENG_DTCP (0x22)\n", + "ENG = HAB_ENG_ROM (0x36)\n", + "ENG = HAB_ENG_HDCP (0x24)\n", + "ENG = HAB_ENG_RTL (0x77)\n", + "ENG = HAB_ENG_SW (0xFF)\n", + "ENG = INVALID\n", + NULL +}; + +static char *ctx_str[] = { + "CTX = HAB_CTX_ANY(0x00)\n", + "CTX = HAB_CTX_FAB (0xFF)\n", + "CTX = HAB_CTX_ENTRY (0xE1)\n", + "CTX = HAB_CTX_TARGET (0x33)\n", + "CTX = HAB_CTX_AUTHENTICATE (0x0A)\n", + "CTX = HAB_CTX_DCD (0xDD)\n", + "CTX = HAB_CTX_CSF (0xCF)\n", + "CTX = HAB_CTX_COMMAND (0xC0)\n", + "CTX = HAB_CTX_AUT_DAT (0xDB)\n", + "CTX = HAB_CTX_ASSERT (0xA0)\n", + "CTX = HAB_CTX_EXIT (0xEE)\n", + "CTX = INVALID\n", + NULL +}; + +static uint8_t hab_statuses[5] = { + HAB_STS_ANY, + HAB_FAILURE, + HAB_WARNING, + HAB_SUCCESS, + -1 +}; + +static uint8_t hab_reasons[26] = { + HAB_RSN_ANY, + HAB_ENG_FAIL, + HAB_INV_ADDRESS, + HAB_INV_ASSERTION, + HAB_INV_CALL, + HAB_INV_CERTIFICATE, + HAB_INV_COMMAND, + HAB_INV_CSF, + HAB_INV_DCD, + HAB_INV_INDEX, + HAB_INV_IVT, + HAB_INV_KEY, + HAB_INV_RETURN, + HAB_INV_SIGNATURE, + HAB_INV_SIZE, + HAB_MEM_FAIL, + HAB_OVR_COUNT, + HAB_OVR_STORAGE, + HAB_UNS_ALGORITHM, + HAB_UNS_COMMAND, + HAB_UNS_ENGINE, + HAB_UNS_ITEM, + HAB_UNS_KEY, + HAB_UNS_PROTOCOL, + HAB_UNS_STATE, + -1 +}; + +static uint8_t hab_contexts[12] = { + HAB_CTX_ANY, + HAB_CTX_FAB, + HAB_CTX_ENTRY, + HAB_CTX_TARGET, + HAB_CTX_AUTHENTICATE, + HAB_CTX_DCD, + HAB_CTX_CSF, + HAB_CTX_COMMAND, + HAB_CTX_AUT_DAT, + HAB_CTX_ASSERT, + HAB_CTX_EXIT, + -1 +}; + +static uint8_t hab_engines[16] = { + HAB_ENG_ANY, + HAB_ENG_SCC, + HAB_ENG_RTIC, + HAB_ENG_SAHARA, + HAB_ENG_CSU, + HAB_ENG_SRTC, + HAB_ENG_DCP, + HAB_ENG_CAAM, + HAB_ENG_SNVS, + HAB_ENG_OCOTP, + HAB_ENG_DTCP, + HAB_ENG_ROM, + HAB_ENG_HDCP, + HAB_ENG_RTL, + HAB_ENG_SW, + -1 +}; + +static inline uint8_t get_idx(uint8_t *list, uint8_t tgt) +{ + uint8_t idx = 0; + uint8_t element = list[idx]; + while (element != -1) { + if (element == tgt) + return idx; + element = list[++idx]; + } + return -1; +} + +static void process_event_record(uint8_t *event_data, size_t bytes) +{ + struct record *rec = (struct record *)event_data; + + printf("\n\n%s", sts_str[get_idx(hab_statuses, rec->contents[0])]); + printf("%s", rsn_str[get_idx(hab_reasons, rec->contents[1])]); + printf("%s", ctx_str[get_idx(hab_contexts, rec->contents[2])]); + printf("%s", eng_str[get_idx(hab_engines, rec->contents[3])]); +} + +static void display_event(uint8_t *event_data, size_t bytes) +{ + uint32_t i; + + if (!(event_data && bytes > 0)) + return; + + for (i = 0; i < bytes; i++) { + if (i == 0) + printf("\t0x%02x", event_data[i]); + else if ((i % 8) == 0) + printf("\n\t0x%02x", event_data[i]); + else + printf(" 0x%02x", event_data[i]); + } + + process_event_record(event_data, bytes); +} + +static int get_hab_status(void) +{ + uint32_t index = 0; /* Loop index */ + uint8_t event_data[128]; /* Event data buffer */ + size_t bytes = sizeof(event_data); /* Event size in bytes */ + enum hab_config config = 0; + enum hab_state state = 0; + + if (imx_hab_is_enabled()) + puts("\nSecure boot enabled\n"); + else + puts("\nSecure boot disabled\n"); + + /* Check HAB status */ + if (hab_rvt_report_status(&config, &state) != HAB_SUCCESS) { + printf("\nHAB Configuration: 0x%02x, HAB State: 0x%02x\n", + config, state); + + /* Display HAB events */ + while (hab_rvt_report_event(HAB_STS_ANY, index, event_data, + &bytes) == HAB_SUCCESS) { + puts("\n"); + printf("--------- HAB Event %d -----------------\n", + index + 1); + puts("event data:\n"); + display_event(event_data, bytes); + puts("\n"); + bytes = sizeof(event_data); + index++; + } + } + /* Display message if no HAB events are found */ + else { + printf("\nHAB Configuration: 0x%02x, HAB State: 0x%02x\n", + config, state); + puts("No HAB Events Found!\n\n"); + } + return 0; +} + +#ifdef CONFIG_MX7ULP + +static int get_record_len(struct record *rec) +{ + return (size_t)((rec->len[0] << 8) + (rec->len[1])); +} + +static int get_hab_status_m4(void) +{ + unsigned int index = 0; + uint8_t event_data[128]; + size_t record_len, offset = 0; + enum hab_config config = 0; + enum hab_state state = 0; + + if (imx_hab_is_enabled()) + puts("\nSecure boot enabled\n"); + else + puts("\nSecure boot disabled\n"); + + /* + * HAB in both A7 and M4 gather the security state + * and configuration of the chip from + * shared SNVS module + */ + hab_rvt_report_status(&config, &state); + printf("\nHAB Configuration: 0x%02x, HAB State: 0x%02x\n", + config, state); + + struct record *rec = (struct record *)(HAB_M4_PERSISTENT_START); + + record_len = get_record_len(rec); + + /* Check if HAB persistent memory is valid */ + if (rec->tag != HAB_TAG_EVT_DEF || + record_len != sizeof(struct evt_def) || + (rec->par & HAB_MAJ_MASK) != HAB_MAJ_VER) { + puts("\nERROR: Invalid HAB persistent memory\n"); + return 1; + } + + /* Parse events in HAB M4 persistent memory region */ + while (offset < HAB_M4_PERSISTENT_BYTES) { + rec = (struct record *)(HAB_M4_PERSISTENT_START + offset); + + record_len = get_record_len(rec); + + if (rec->tag == HAB_TAG_EVT) { + memcpy(&event_data, rec, record_len); + puts("\n"); + printf("--------- HAB Event %d -----------------\n", + index + 1); + puts("event data:\n"); + display_event(event_data, record_len); + puts("\n"); + index++; + } + + offset += record_len; + + /* Ensure all records start on a word boundary */ + if ((offset % 4) != 0) + offset = offset + (4 - (offset % 4)); + } + + if (!index) + puts("No HAB Events Found!\n\n"); + + return 0; +} +#endif + +static int do_hab_status(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ +#ifdef CONFIG_MX7ULP + if ((argc > 2)) { + cmd_usage(cmdtp); + return 1; + } + + if (strcmp("m4", argv[1]) == 0) + get_hab_status_m4(); + else + get_hab_status(); +#else + if ((argc != 1)) { + cmd_usage(cmdtp); + return 1; + } + + get_hab_status(); +#endif + + return 0; +} + +static ulong get_image_ivt_offset(ulong img_addr) +{ + const void *buf; + + buf = map_sysmem(img_addr, 0); + switch (genimg_get_format(buf)) { +#if CONFIG_IS_ENABLED(LEGACY_IMAGE_FORMAT) + case IMAGE_FORMAT_LEGACY: + return (image_get_image_size((image_header_t *)img_addr) + + 0x1000 - 1) & ~(0x1000 - 1); +#endif +#if IMAGE_ENABLE_FIT + case IMAGE_FORMAT_FIT: + return (fit_get_size(buf) + 0x1000 - 1) & ~(0x1000 - 1); +#endif + default: + return 0; + } +} + +static int do_authenticate_image(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + ulong addr, length, ivt_offset; + int rcode = 0; + + if (argc < 3) + return CMD_RET_USAGE; + + addr = simple_strtoul(argv[1], NULL, 16); + length = simple_strtoul(argv[2], NULL, 16); + if (argc == 3) + ivt_offset = get_image_ivt_offset(addr); + else + ivt_offset = simple_strtoul(argv[3], NULL, 16); + + rcode = imx_hab_authenticate_image(addr, length, ivt_offset); + if (rcode == 0) + rcode = CMD_RET_SUCCESS; + else + rcode = CMD_RET_FAILURE; + + return rcode; +} + +static int do_hab_failsafe(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + if (argc != 1) { + cmd_usage(cmdtp); + return 1; + } + + hab_rvt_failsafe(); + + return 0; +} + +static int do_hab_version(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct hab_hdr *hdr = (struct hab_hdr *)HAB_RVT_BASE; + + if (hdr->tag != HAB_TAG_RVT) { + printf("Unexpected header tag: %x\n", hdr->tag); + return CMD_RET_FAILURE; + } + + printf("HAB version: %d.%d\n", hdr->par >> 4, hdr->par & 0xf); + + return 0; +} + +static int do_authenticate_image_or_failover(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + int ret = CMD_RET_FAILURE; + + if (argc != 4) { + ret = CMD_RET_USAGE; + goto error; + } + + if (!imx_hab_is_enabled()) { + printf("error: secure boot disabled\n"); + goto error; + } + + if (do_authenticate_image(NULL, flag, argc, argv) != CMD_RET_SUCCESS) { + fprintf(stderr, "authentication fail -> %s %s %s %s\n", + argv[0], argv[1], argv[2], argv[3]); + do_hab_failsafe(0, 0, 1, NULL); + }; + ret = CMD_RET_SUCCESS; +error: + return ret; +} + +#ifdef CONFIG_MX7ULP +U_BOOT_CMD( + hab_status, CONFIG_SYS_MAXARGS, 2, do_hab_status, + "display HAB status and events", + "hab_status - A7 HAB event and status\n" + "hab_status m4 - M4 HAB event and status" + ); +#else +U_BOOT_CMD( + hab_status, CONFIG_SYS_MAXARGS, 1, do_hab_status, + "display HAB status", + "" + ); +#endif + +U_BOOT_CMD( + hab_auth_img, 4, 0, do_authenticate_image, + "authenticate image via HAB", + "addr length ivt_offset\n" + "addr - image hex address\n" + "length - image hex length\n" + "ivt_offset - hex offset of IVT in the image" + ); + +U_BOOT_CMD( + hab_failsafe, CONFIG_SYS_MAXARGS, 1, do_hab_failsafe, + "run BootROM failsafe routine", + "" + ); + +U_BOOT_CMD( + hab_auth_img_or_fail, 4, 0, + do_authenticate_image_or_failover, + "authenticate image via HAB on failure drop to USB BootROM mode", + "addr length ivt_offset\n" + "addr - image hex address\n" + "length - image hex length\n" + "ivt_offset - hex offset of IVT in the image" + ); + +U_BOOT_CMD( + hab_version, 1, 0, do_hab_version, + "print HAB major/minor version", + "" + ); + +#endif /* !defined(CONFIG_SPL_BUILD) */ + +/* Get CSF Header length */ +static int get_hab_hdr_len(struct hab_hdr *hdr) +{ + return (size_t)((hdr->len[0] << 8) + (hdr->len[1])); +} + +/* Check whether addr lies between start and + * end and is within the length of the image + */ +static int chk_bounds(u8 *addr, size_t bytes, u8 *start, u8 *end) +{ + size_t csf_size = (size_t)((end + 1) - addr); + + return (addr && (addr >= start) && (addr <= end) && + (csf_size >= bytes)); +} + +/* Get Length of each command in CSF */ +static int get_csf_cmd_hdr_len(u8 *csf_hdr) +{ + if (*csf_hdr == HAB_CMD_HDR) + return sizeof(struct hab_hdr); + + return get_hab_hdr_len((struct hab_hdr *)csf_hdr); +} + +/* Check if CSF is valid */ +static bool csf_is_valid(struct ivt *ivt, ulong start_addr, size_t bytes) +{ + u8 *start = (u8 *)start_addr; + u8 *csf_hdr; + u8 *end; + + size_t csf_hdr_len; + size_t cmd_hdr_len; + size_t offset = 0; + + if (bytes != 0) + end = start + bytes - 1; + else + end = start; + + /* Verify if CSF pointer content is zero */ + if (!ivt->csf) { + puts("Error: CSF pointer is NULL\n"); + return false; + } + + csf_hdr = (u8 *)(ulong)ivt->csf; + + /* Verify if CSF Header exist */ + if (*csf_hdr != HAB_CMD_HDR) { + puts("Error: CSF header command not found\n"); + return false; + } + + csf_hdr_len = get_hab_hdr_len((struct hab_hdr *)csf_hdr); + + /* Check if the CSF lies within the image bounds */ + if (!chk_bounds(csf_hdr, csf_hdr_len, start, end)) { + puts("Error: CSF lies outside the image bounds\n"); + return false; + } + + do { + struct hab_hdr *cmd; + + cmd = (struct hab_hdr *)&csf_hdr[offset]; + + switch (cmd->tag) { + case (HAB_CMD_WRT_DAT): + puts("Error: Deprecated write command found\n"); + return false; + case (HAB_CMD_CHK_DAT): + puts("Error: Deprecated check command found\n"); + return false; + case (HAB_CMD_SET): + if (cmd->par == HAB_PAR_MID) { + puts("Error: Deprecated Set MID command found\n"); + return false; + } + default: + break; + } + + cmd_hdr_len = get_csf_cmd_hdr_len(&csf_hdr[offset]); + if (!cmd_hdr_len) { + puts("Error: Invalid command length\n"); + return false; + } + offset += cmd_hdr_len; + + } while (offset < csf_hdr_len); + + return true; +} + +/* + * Validate IVT structure of the image being authenticated + */ +static int validate_ivt(struct ivt *ivt_initial) +{ + struct ivt_header *ivt_hdr = &ivt_initial->hdr; + + if ((ulong)ivt_initial & 0x3) { + puts("Error: Image's start address is not 4 byte aligned\n"); + return 0; + } + + /* Check IVT fields before allowing authentication */ + if ((!verify_ivt_header(ivt_hdr)) && \ + (ivt_initial->entry != 0x0) && \ + (ivt_initial->reserved1 == 0x0) && \ + (ivt_initial->self == \ + (uint32_t)((ulong)ivt_initial & 0xffffffff)) && \ + (ivt_initial->csf != 0x0) && \ + (ivt_initial->reserved2 == 0x0)) { + /* Report boot failure if DCD pointer is found in IVT */ + if (ivt_initial->dcd != 0x0) + puts("Error: DCD pointer must be 0\n"); + else + return 1; + } + + puts("Error: Invalid IVT structure\n"); + debug("\nAllowed IVT structure:\n"); + debug("IVT HDR = 0x4X2000D1\n"); + debug("IVT ENTRY = 0xXXXXXXXX\n"); + debug("IVT RSV1 = 0x0\n"); + debug("IVT DCD = 0x0\n"); /* Recommended */ + debug("IVT BOOT_DATA = 0xXXXXXXXX\n"); /* Commonly 0x0 */ + debug("IVT SELF = 0xXXXXXXXX\n"); /* = ddr_start + ivt_offset */ + debug("IVT CSF = 0xXXXXXXXX\n"); + debug("IVT RSV2 = 0x0\n"); + + /* Invalid IVT structure */ + return 0; +} + +bool imx_hab_is_enabled(void) +{ + struct imx_sec_config_fuse_t *fuse = + (struct imx_sec_config_fuse_t *)&imx_sec_config_fuse; + uint32_t reg; + int ret; + + ret = fuse_read(fuse->bank, fuse->word, ®); + if (ret) { + puts("\nSecure boot fuse read error\n"); + return ret; + } + + return (reg & IS_HAB_ENABLED_BIT) == IS_HAB_ENABLED_BIT; +} + +int imx_hab_authenticate_image(uint32_t ddr_start, uint32_t image_size, + uint32_t ivt_offset) +{ + ulong load_addr = 0; + size_t bytes; + ulong ivt_addr = 0; + int result = 1; + ulong start; + struct ivt *ivt; + enum hab_status status; + + if (!imx_hab_is_enabled()) + puts("hab fuse not enabled\n"); + + printf("\nAuthenticate image from DDR location 0x%x...\n", + ddr_start); + + hab_caam_clock_enable(1); + + /* Calculate IVT address header */ + ivt_addr = (ulong) (ddr_start + ivt_offset); + ivt = (struct ivt *)ivt_addr; + + /* Verify IVT header bugging out on error */ + if (!validate_ivt(ivt)) + goto hab_authentication_exit; + + start = ddr_start; + bytes = image_size; + + /* Verify CSF */ + if (!csf_is_valid(ivt, start, bytes)) + goto hab_authentication_exit; + + if (hab_rvt_entry() != HAB_SUCCESS) { + puts("hab entry function fail\n"); + goto hab_exit_failure_print_status; + } + + status = hab_rvt_check_target(HAB_TGT_MEMORY, (void *)(ulong)ddr_start, bytes); + if (status != HAB_SUCCESS) { + printf("HAB check target 0x%08x-0x%08lx fail\n", + ddr_start, ddr_start + (ulong)bytes); + goto hab_exit_failure_print_status; + } +#ifdef DEBUG + printf("\nivt_offset = 0x%x, ivt addr = 0x%lx\n", ivt_offset, ivt_addr); + printf("ivt entry = 0x%08x, dcd = 0x%08x, csf = 0x%08x\n", ivt->entry, + ivt->dcd, ivt->csf); + puts("Dumping IVT\n"); + print_buffer(ivt_addr, (void *)(ivt_addr), 4, 0x8, 0); + + puts("Dumping CSF Header\n"); + print_buffer(ivt->csf, (void *)(ivt->csf), 4, 0x10, 0); + +#if !defined(CONFIG_SPL_BUILD) + get_hab_status(); +#endif + + puts("\nCalling authenticate_image in ROM\n"); + printf("\tivt_offset = 0x%x\n", ivt_offset); + printf("\tstart = 0x%08lx\n", start); + printf("\tbytes = 0x%x\n", bytes); +#endif + +#ifndef CONFIG_ARM64 + /* + * If the MMU is enabled, we have to notify the ROM + * code, or it won't flush the caches when needed. + * This is done, by setting the "pu_irom_mmu_enabled" + * word to 1. You can find its address by looking in + * the ROM map. This is critical for + * authenticate_image(). If MMU is enabled, without + * setting this bit, authentication will fail and may + * crash. + */ + /* Check MMU enabled */ + if (is_soc_type(MXC_SOC_MX6) && get_cr() & CR_M) { + if (is_mx6dq()) { + /* + * This won't work on Rev 1.0.0 of + * i.MX6Q/D, since their ROM doesn't + * do cache flushes. don't think any + * exist, so we ignore them. + */ + if (!is_mx6dqp()) + writel(1, MX6DQ_PU_IROM_MMU_EN_VAR); + } else if (is_mx6sdl()) { + writel(1, MX6DLS_PU_IROM_MMU_EN_VAR); + } else if (is_mx6sl()) { + writel(1, MX6SL_PU_IROM_MMU_EN_VAR); + } + } +#endif + + load_addr = (ulong)hab_rvt_authenticate_image( + HAB_CID_UBOOT, + ivt_offset, (void **)&start, + (size_t *)&bytes, NULL); + if (hab_rvt_exit() != HAB_SUCCESS) { + puts("hab exit function fail\n"); + load_addr = 0; + } + +hab_exit_failure_print_status: +#if !defined(CONFIG_SPL_BUILD) + get_hab_status(); +#endif + +hab_authentication_exit: + + if (load_addr != 0 || !imx_hab_is_enabled()) + result = 0; + + return result; +} + +int authenticate_image(u32 ddr_start, u32 raw_image_size) +{ + u32 ivt_offset; + size_t bytes; + + ivt_offset = (raw_image_size + ALIGN_SIZE - 1) & + ~(ALIGN_SIZE - 1); + bytes = ivt_offset + IVT_SIZE + CSF_PAD_SIZE; + + return imx_hab_authenticate_image(ddr_start, bytes, ivt_offset); +} diff --git a/roms/u-boot/arch/arm/mach-imx/i2c-mxv7.c b/roms/u-boot/arch/arm/mach-imx/i2c-mxv7.c new file mode 100644 index 000000000..d36347d8e --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/i2c-mxv7.c @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2012 Boundary Devices Inc. + */ +#include <common.h> +#include <malloc.h> +#include <asm/arch/clock.h> +#include <asm/arch/imx-regs.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <asm/gpio.h> +#include <asm/mach-imx/mxc_i2c.h> +#include <watchdog.h> + +int force_idle_bus(void *priv) +{ + int i; + int sda, scl; + ulong elapsed, start_time; + struct i2c_pads_info *p = (struct i2c_pads_info *)priv; + int ret = 0; + + gpio_direction_input(p->sda.gp); + gpio_direction_input(p->scl.gp); + + imx_iomux_v3_setup_pad(p->sda.gpio_mode); + imx_iomux_v3_setup_pad(p->scl.gpio_mode); + + sda = gpio_get_value(p->sda.gp); + scl = gpio_get_value(p->scl.gp); + if ((sda & scl) == 1) + goto exit; /* Bus is idle already */ + + printf("%s: sda=%d scl=%d sda.gp=0x%x scl.gp=0x%x\n", __func__, + sda, scl, p->sda.gp, p->scl.gp); + /* Send high and low on the SCL line */ + for (i = 0; i < 9; i++) { + gpio_direction_output(p->scl.gp, 0); + udelay(50); + gpio_direction_input(p->scl.gp); + udelay(50); + } + start_time = get_timer(0); + for (;;) { + sda = gpio_get_value(p->sda.gp); + scl = gpio_get_value(p->scl.gp); + if ((sda & scl) == 1) + break; + WATCHDOG_RESET(); + elapsed = get_timer(start_time); + if (elapsed > (CONFIG_SYS_HZ / 5)) { /* .2 seconds */ + ret = -EBUSY; + printf("%s: failed to clear bus, sda=%d scl=%d\n", + __func__, sda, scl); + break; + } + } +exit: + imx_iomux_v3_setup_pad(p->sda.i2c_mode); + imx_iomux_v3_setup_pad(p->scl.i2c_mode); + return ret; +} + +static void * const i2c_bases[] = { + (void *)I2C1_BASE_ADDR, + (void *)I2C2_BASE_ADDR, +#ifdef I2C3_BASE_ADDR + (void *)I2C3_BASE_ADDR, +#endif +#ifdef I2C4_BASE_ADDR + (void *)I2C4_BASE_ADDR, +#endif +}; + +/* i2c_index can be from 0 - 3 */ +int setup_i2c(unsigned i2c_index, int speed, int slave_addr, + struct i2c_pads_info *p) +{ + char name[9]; + int ret; + + if (i2c_index >= ARRAY_SIZE(i2c_bases)) + return -EINVAL; + + snprintf(name, sizeof(name), "i2c_sda%01d", i2c_index); + ret = gpio_request(p->sda.gp, name); + if (ret) + return ret; + + snprintf(name, sizeof(name), "i2c_scl%01d", i2c_index); + ret = gpio_request(p->scl.gp, name); + if (ret) + goto err_req; + + /* Enable i2c clock */ + ret = enable_i2c_clk(1, i2c_index); + if (ret) + goto err_clk; + + /* Make sure bus is idle */ + ret = force_idle_bus(p); + if (ret) + goto err_idle; + +#if !CONFIG_IS_ENABLED(DM_I2C) + bus_i2c_init(i2c_index, speed, slave_addr, force_idle_bus, p); +#endif + + return 0; + +err_idle: +err_clk: + gpio_free(p->scl.gp); +err_req: + gpio_free(p->sda.gp); + + return ret; +} diff --git a/roms/u-boot/arch/arm/mach-imx/imx8/Kconfig b/roms/u-boot/arch/arm/mach-imx/imx8/Kconfig new file mode 100644 index 000000000..71221d8d0 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/imx8/Kconfig @@ -0,0 +1,130 @@ +if ARCH_IMX8 + +config AHAB_BOOT + bool "Support i.MX8 AHAB features" + imply CMD_DEKBLOB + help + This option enables the support for AHAB secure boot. + +config IMX8 + bool + +config MU_BASE_SPL + hex "MU base address used in SPL" + default 0x5d1b0000 + help + SPL runs in EL3 mode, it use MU0_A to communicate with SCU. + So we could not reuse the one in dts which is for normal U-Boot. + +config IMX8QM + select IMX8 + select SUPPORT_SPL + select SPL_RECOVER_DATA_SECTION + bool + +config IMX8QXP + select IMX8 + select SUPPORT_SPL + select SPL_RECOVER_DATA_SECTION + bool + +config SYS_SOC + default "imx8" + +config SPL_LOAD_IMX_CONTAINER + bool "Enable SPL loading U-Boot as a i.MX Container image" + depends on SPL + help + This is to let SPL could load i.MX8 Container image + +config IMX_CONTAINER_CFG + string "i.MX Container config file" + depends on SPL + help + This is to specific the cfg file for generating container + image which will be loaded by SPL. + +config BOOTAUX_RESERVED_MEM_BASE + hex "i.MX auxiliary core dram memory base" + default 0 + +config BOOTAUX_RESERVED_MEM_SIZE + hex "i.MX auxiliary core dram memory size" + default 0 + +choice + prompt "i.MX8 board select" + optional + +config TARGET_APALIS_IMX8 + bool "Support Apalis iMX8 module" + select BOARD_LATE_INIT + select IMX8QM + +config TARGET_COLIBRI_IMX8X + bool "Support Colibri iMX8X module" + select BOARD_LATE_INIT + select IMX8QXP + +config TARGET_APALIS_IMX8X + bool "Support Apalis iMX8X module" + select BOARD_LATE_INIT + select IMX8QXP + +config TARGET_DENEB + bool "Support i.MX8QXP Capricorn Deneb board" + select BOARD_LATE_INIT + select IMX8QXP + +config TARGET_GIEDI + bool "Support i.MX8QXP Capricorn Giedi board" + select BOARD_LATE_INIT + select IMX8QXP + +config TARGET_IMX8QM_MEK + bool "Support i.MX8QM MEK board" + select BOARD_LATE_INIT + select IMX8QM + +config TARGET_CONGA_QMX8 + bool "Support congatec conga-QMX8 board" + select BOARD_LATE_INIT + select SUPPORT_SPL + select IMX8QM + +config TARGET_IMX8QM_ROM7720_A1 + bool "Support i.MX8QM ROM-7720-A1" + select BOARD_LATE_INIT + select SUPPORT_SPL + select IMX8QM + +config TARGET_IMX8QXP_MEK + bool "Support i.MX8QXP MEK board" + select BOARD_LATE_INIT + select IMX8QXP + +endchoice + +source "board/freescale/imx8qm_mek/Kconfig" +source "board/freescale/imx8qxp_mek/Kconfig" +source "board/congatec/cgtqmx8/Kconfig" +source "board/advantech/imx8qm_rom7720_a1/Kconfig" +source "board/toradex/apalis-imx8/Kconfig" +source "board/toradex/colibri-imx8x/Kconfig" +source "board/toradex/apalis-imx8x/Kconfig" +source "board/siemens/capricorn/Kconfig" + +config IMX_SNVS_SEC_SC + bool "Support SNVS configuration" + help + Allow to configure the SNVS via SCU API to configure tampers and secure + violation. + +config IMX_SNVS_SEC_SC_AUTO + bool "Support SNVS configuration command" + depends on IMX_SNVS_SEC_SC + help + This configuration will apply the selected configurations automatically + at boot. + +endif diff --git a/roms/u-boot/arch/arm/mach-imx/imx8/Makefile b/roms/u-boot/arch/arm/mach-imx/imx8/Makefile new file mode 100644 index 000000000..bbb41adbe --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/imx8/Makefile @@ -0,0 +1,14 @@ +# +# Copyright 2018 NXP +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-y += cpu.o iomux.o misc.o lowlevel_init.o +obj-$(CONFIG_OF_SYSTEM_SETUP) += fdt.o +obj-$(CONFIG_AHAB_BOOT) += ahab.o + +ifdef CONFIG_SPL_BUILD +obj-$(CONFIG_SPL_LOAD_IMX_CONTAINER) += image.o parse-container.o +endif +obj-$(CONFIG_IMX_SNVS_SEC_SC) += snvs_security_sc.o diff --git a/roms/u-boot/arch/arm/mach-imx/imx8/ahab.c b/roms/u-boot/arch/arm/mach-imx/imx8/ahab.c new file mode 100644 index 000000000..6392fe267 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/imx8/ahab.c @@ -0,0 +1,351 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2018-2019 NXP + */ + +#include <common.h> +#include <command.h> +#include <errno.h> +#include <log.h> +#include <asm/global_data.h> +#include <asm/io.h> +#include <asm/arch/sci/sci.h> +#include <asm/mach-imx/sys_proto.h> +#include <asm/arch-imx/cpu.h> +#include <asm/arch/sys_proto.h> +#include <asm/arch/image.h> +#include <console.h> +#include <cpu_func.h> + +DECLARE_GLOBAL_DATA_PTR; + +#define SEC_SECURE_RAM_BASE (0x31800000UL) +#define SEC_SECURE_RAM_END_BASE (SEC_SECURE_RAM_BASE + 0xFFFFUL) +#define SECO_LOCAL_SEC_SEC_SECURE_RAM_BASE (0x60000000UL) + +#define SECO_PT 2U + +static inline bool check_in_dram(ulong addr) +{ + int i; + struct bd_info *bd = gd->bd; + + for (i = 0; i < CONFIG_NR_DRAM_BANKS; ++i) { + if (bd->bi_dram[i].size) { + if (addr >= bd->bi_dram[i].start && + addr < (bd->bi_dram[i].start + bd->bi_dram[i].size)) + return true; + } + } + + return false; +} + +int authenticate_os_container(ulong addr) +{ + struct container_hdr *phdr; + int i, ret = 0; + int err; + sc_rm_mr_t mr; + sc_faddr_t start, end; + u16 length; + struct boot_img_t *img; + unsigned long s, e; + + if (addr % 4) { + puts("Error: Image's address is not 4 byte aligned\n"); + return -EINVAL; + } + + if (!check_in_dram(addr)) { + puts("Error: Image's address is invalid\n"); + return -EINVAL; + } + + phdr = (struct container_hdr *)addr; + if (phdr->tag != 0x87 && phdr->version != 0x0) { + printf("Error: Wrong container header\n"); + return -EFAULT; + } + + if (!phdr->num_images) { + printf("Error: Wrong container, no image found\n"); + return -EFAULT; + } + + length = phdr->length_lsb + (phdr->length_msb << 8); + + debug("container length %u\n", length); + memcpy((void *)SEC_SECURE_RAM_BASE, (const void *)addr, + ALIGN(length, CONFIG_SYS_CACHELINE_SIZE)); + + err = sc_seco_authenticate(-1, SC_SECO_AUTH_CONTAINER, + SECO_LOCAL_SEC_SEC_SECURE_RAM_BASE); + if (err) { + printf("Authenticate container hdr failed, return %d\n", + err); + ret = -EIO; + goto exit; + } + + /* Copy images to dest address */ + for (i = 0; i < phdr->num_images; i++) { + img = (struct boot_img_t *)(addr + + sizeof(struct container_hdr) + + i * sizeof(struct boot_img_t)); + + debug("img %d, dst 0x%x, src 0x%lux, size 0x%x\n", + i, (uint32_t) img->dst, img->offset + addr, img->size); + + memcpy((void *)img->dst, (const void *)(img->offset + addr), + img->size); + + s = img->dst & ~(CONFIG_SYS_CACHELINE_SIZE - 1); + e = ALIGN(img->dst + img->size, CONFIG_SYS_CACHELINE_SIZE) - 1; + + flush_dcache_range(s, e); + + /* Find the memreg and set permission for seco pt */ + err = sc_rm_find_memreg(-1, &mr, s, e); + if (err) { + printf("Error: can't find memreg for image load address 0x%llx, error %d\n", img->dst, err); + ret = -ENOMEM; + goto exit; + } + + err = sc_rm_get_memreg_info(-1, mr, &start, &end); + if (!err) + debug("memreg %u 0x%llx -- 0x%llx\n", mr, start, end); + + err = sc_rm_set_memreg_permissions(-1, mr, SECO_PT, + SC_RM_PERM_FULL); + if (err) { + printf("Set permission failed for img %d, error %d\n", + i, err); + ret = -EPERM; + goto exit; + } + + err = sc_seco_authenticate(-1, SC_SECO_VERIFY_IMAGE, + (1 << i)); + if (err) { + printf("Authenticate img %d failed, return %d\n", + i, err); + ret = -EIO; + } + + err = sc_rm_set_memreg_permissions(-1, mr, SECO_PT, + SC_RM_PERM_NONE); + if (err) { + printf("Remove permission failed for img %d, err %d\n", + i, err); + ret = -EPERM; + } + + if (ret) + goto exit; + } + +exit: + if (sc_seco_authenticate(-1, SC_SECO_REL_CONTAINER, 0) != SC_ERR_NONE) + printf("Error: release container failed!\n"); + + return ret; +} + +static int do_authenticate(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + ulong addr; + + if (argc < 2) + return CMD_RET_USAGE; + + addr = simple_strtoul(argv[1], NULL, 16); + + printf("Authenticate OS container at 0x%lx\n", addr); + + if (authenticate_os_container(addr)) + return CMD_RET_FAILURE; + + return CMD_RET_SUCCESS; +} + +static void display_life_cycle(u16 lc) +{ + printf("Lifecycle: 0x%04X, ", lc); + switch (lc) { + case 0x1: + printf("Pristine\n\n"); + break; + case 0x2: + printf("Fab\n\n"); + break; + case 0x8: + printf("Open\n\n"); + break; + case 0x20: + printf("NXP closed\n\n"); + break; + case 0x80: + printf("OEM closed\n\n"); + break; + case 0x100: + printf("Partial field return\n\n"); + break; + case 0x200: + printf("Full field return\n\n"); + break; + case 0x400: + printf("No return\n\n"); + break; + default: + printf("Unknown\n\n"); + break; + } +} + +#define AHAB_AUTH_CONTAINER_REQ 0x87 +#define AHAB_VERIFY_IMAGE_REQ 0x88 + +#define AHAB_NO_AUTHENTICATION_IND 0xee +#define AHAB_BAD_KEY_HASH_IND 0xfa +#define AHAB_INVALID_KEY_IND 0xf9 +#define AHAB_BAD_SIGNATURE_IND 0xf0 +#define AHAB_BAD_HASH_IND 0xf1 + +static void display_ahab_auth_event(u32 event) +{ + u8 cmd = (event >> 16) & 0xff; + u8 resp_ind = (event >> 8) & 0xff; + + switch (cmd) { + case AHAB_AUTH_CONTAINER_REQ: + printf("\tCMD = AHAB_AUTH_CONTAINER_REQ (0x%02X)\n", cmd); + printf("\tIND = "); + break; + case AHAB_VERIFY_IMAGE_REQ: + printf("\tCMD = AHAB_VERIFY_IMAGE_REQ (0x%02X)\n", cmd); + printf("\tIND = "); + break; + default: + return; + } + + switch (resp_ind) { + case AHAB_NO_AUTHENTICATION_IND: + printf("AHAB_NO_AUTHENTICATION_IND (0x%02X)\n\n", resp_ind); + break; + case AHAB_BAD_KEY_HASH_IND: + printf("AHAB_BAD_KEY_HASH_IND (0x%02X)\n\n", resp_ind); + break; + case AHAB_INVALID_KEY_IND: + printf("AHAB_INVALID_KEY_IND (0x%02X)\n\n", resp_ind); + break; + case AHAB_BAD_SIGNATURE_IND: + printf("AHAB_BAD_SIGNATURE_IND (0x%02X)\n\n", resp_ind); + break; + case AHAB_BAD_HASH_IND: + printf("AHAB_BAD_HASH_IND (0x%02X)\n\n", resp_ind); + break; + default: + printf("Unknown Indicator (0x%02X)\n\n", resp_ind); + break; + } +} + +static int do_ahab_status(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + int err; + u8 idx = 0U; + u32 event; + u16 lc; + + err = sc_seco_chip_info(-1, &lc, NULL, NULL, NULL); + if (err != SC_ERR_NONE) { + printf("Error in get lifecycle\n"); + return -EIO; + } + + display_life_cycle(lc); + + err = sc_seco_get_event(-1, idx, &event); + while (err == SC_ERR_NONE) { + printf("SECO Event[%u] = 0x%08X\n", idx, event); + display_ahab_auth_event(event); + + idx++; + err = sc_seco_get_event(-1, idx, &event); + } + + if (idx == 0) + printf("No SECO Events Found!\n\n"); + + return 0; +} + +static int confirm_close(void) +{ + puts("Warning: Please ensure your sample is in NXP closed state, " + "OEM SRK hash has been fused, \n" + " and you are able to boot a signed image successfully " + "without any SECO events reported.\n" + " If not, your sample will be unrecoverable.\n" + "\nReally perform this operation? <y/N>\n"); + + if (confirm_yesno()) + return 1; + + puts("Ahab close aborted\n"); + return 0; +} + +static int do_ahab_close(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + int confirmed = argc >= 2 && !strcmp(argv[1], "-y"); + int err; + u16 lc; + + if (!confirmed && !confirm_close()) + return -EACCES; + + err = sc_seco_chip_info(-1, &lc, NULL, NULL, NULL); + if (err != SC_ERR_NONE) { + printf("Error in get lifecycle\n"); + return -EIO; + } + + if (lc != 0x20) { + puts("Current lifecycle is NOT NXP closed, can't move to OEM closed\n"); + display_life_cycle(lc); + return -EPERM; + } + + err = sc_seco_forward_lifecycle(-1, 16); + if (err != SC_ERR_NONE) { + printf("Error in forward lifecycle to OEM closed\n"); + return -EIO; + } + + printf("Change to OEM closed successfully\n"); + + return 0; +} + +U_BOOT_CMD(auth_cntr, CONFIG_SYS_MAXARGS, 1, do_authenticate, + "autenticate OS container via AHAB", + "addr\n" + "addr - OS container hex address\n" +); + +U_BOOT_CMD(ahab_status, CONFIG_SYS_MAXARGS, 1, do_ahab_status, + "display AHAB lifecycle and events from seco", + "" +); + +U_BOOT_CMD(ahab_close, CONFIG_SYS_MAXARGS, 1, do_ahab_close, + "Change AHAB lifecycle to OEM closed", + "" +); diff --git a/roms/u-boot/arch/arm/mach-imx/imx8/clock.c b/roms/u-boot/arch/arm/mach-imx/imx8/clock.c new file mode 100644 index 000000000..9941b57b4 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/imx8/clock.c @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2018 NXP + */ + +#include <common.h> +#include <asm/global_data.h> +#include <linux/errno.h> +#include <asm/arch/clock.h> + +DECLARE_GLOBAL_DATA_PTR; + +u32 mxc_get_clock(enum mxc_clock clk) +{ + switch (clk) { + default: + printf("Unsupported mxc_clock %d\n", clk); + break; + } + + return 0; +} diff --git a/roms/u-boot/arch/arm/mach-imx/imx8/cpu.c b/roms/u-boot/arch/arm/mach-imx/imx8/cpu.c new file mode 100644 index 000000000..02db322f5 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/imx8/cpu.c @@ -0,0 +1,658 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2018 NXP + */ + +#include <common.h> +#include <clk.h> +#include <cpu.h> +#include <cpu_func.h> +#include <dm.h> +#include <init.h> +#include <log.h> +#include <asm/cache.h> +#include <asm/global_data.h> +#include <dm/device-internal.h> +#include <dm/lists.h> +#include <dm/uclass.h> +#include <errno.h> +#include <spl.h> +#include <thermal.h> +#include <asm/arch/sci/sci.h> +#include <asm/arch/sys_proto.h> +#include <asm/arch-imx/cpu.h> +#include <asm/armv8/cpu.h> +#include <asm/armv8/mmu.h> +#include <asm/setup.h> +#include <asm/mach-imx/boot_mode.h> +#include <spl.h> + +DECLARE_GLOBAL_DATA_PTR; + +#define BT_PASSOVER_TAG 0x504F +struct pass_over_info_t *get_pass_over_info(void) +{ + struct pass_over_info_t *p = + (struct pass_over_info_t *)PASS_OVER_INFO_ADDR; + + if (p->barker != BT_PASSOVER_TAG || + p->len != sizeof(struct pass_over_info_t)) + return NULL; + + return p; +} + +int arch_cpu_init(void) +{ +#if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_RECOVER_DATA_SECTION) + spl_save_restore_data(); +#endif + +#ifdef CONFIG_SPL_BUILD + struct pass_over_info_t *pass_over; + + if (is_soc_rev(CHIP_REV_A)) { + pass_over = get_pass_over_info(); + if (pass_over && pass_over->g_ap_mu == 0) { + /* + * When ap_mu is 0, means the U-Boot booted + * from first container + */ + sc_misc_boot_status(-1, SC_MISC_BOOT_STATUS_SUCCESS); + } + } +#endif + + return 0; +} + +int arch_cpu_init_dm(void) +{ + struct udevice *devp; + int node, ret; + + node = fdt_node_offset_by_compatible(gd->fdt_blob, -1, "fsl,imx8-mu"); + + ret = uclass_get_device_by_of_offset(UCLASS_MISC, node, &devp); + if (ret) { + printf("could not get scu %d\n", ret); + return ret; + } + + if (is_imx8qm()) { + ret = sc_pm_set_resource_power_mode(-1, SC_R_SMMU, + SC_PM_PW_MODE_ON); + if (ret) + return ret; + } + + return 0; +} + +int print_bootinfo(void) +{ + enum boot_device bt_dev = get_boot_device(); + + puts("Boot: "); + switch (bt_dev) { + case SD1_BOOT: + puts("SD0\n"); + break; + case SD2_BOOT: + puts("SD1\n"); + break; + case SD3_BOOT: + puts("SD2\n"); + break; + case MMC1_BOOT: + puts("MMC0\n"); + break; + case MMC2_BOOT: + puts("MMC1\n"); + break; + case MMC3_BOOT: + puts("MMC2\n"); + break; + case FLEXSPI_BOOT: + puts("FLEXSPI\n"); + break; + case SATA_BOOT: + puts("SATA\n"); + break; + case NAND_BOOT: + puts("NAND\n"); + break; + case USB_BOOT: + puts("USB\n"); + break; + default: + printf("Unknown device %u\n", bt_dev); + break; + } + + return 0; +} + +enum boot_device get_boot_device(void) +{ + enum boot_device boot_dev = SD1_BOOT; + + sc_rsrc_t dev_rsrc; + + sc_misc_get_boot_dev(-1, &dev_rsrc); + + switch (dev_rsrc) { + case SC_R_SDHC_0: + boot_dev = MMC1_BOOT; + break; + case SC_R_SDHC_1: + boot_dev = SD2_BOOT; + break; + case SC_R_SDHC_2: + boot_dev = SD3_BOOT; + break; + case SC_R_NAND: + boot_dev = NAND_BOOT; + break; + case SC_R_FSPI_0: + boot_dev = FLEXSPI_BOOT; + break; + case SC_R_SATA_0: + boot_dev = SATA_BOOT; + break; + case SC_R_USB_0: + case SC_R_USB_1: + case SC_R_USB_2: + boot_dev = USB_BOOT; + break; + default: + break; + } + + return boot_dev; +} + +#ifdef CONFIG_SERIAL_TAG +#define FUSE_UNIQUE_ID_WORD0 16 +#define FUSE_UNIQUE_ID_WORD1 17 +void get_board_serial(struct tag_serialnr *serialnr) +{ + sc_err_t err; + u32 val1 = 0, val2 = 0; + u32 word1, word2; + + if (!serialnr) + return; + + word1 = FUSE_UNIQUE_ID_WORD0; + word2 = FUSE_UNIQUE_ID_WORD1; + + err = sc_misc_otp_fuse_read(-1, word1, &val1); + if (err != SC_ERR_NONE) { + printf("%s fuse %d read error: %d\n", __func__, word1, err); + return; + } + + err = sc_misc_otp_fuse_read(-1, word2, &val2); + if (err != SC_ERR_NONE) { + printf("%s fuse %d read error: %d\n", __func__, word2, err); + return; + } + serialnr->low = val1; + serialnr->high = val2; +} +#endif /*CONFIG_SERIAL_TAG*/ + +#ifdef CONFIG_ENV_IS_IN_MMC +__weak int board_mmc_get_env_dev(int devno) +{ + return CONFIG_SYS_MMC_ENV_DEV; +} + +int mmc_get_env_dev(void) +{ + sc_rsrc_t dev_rsrc; + int devno; + + sc_misc_get_boot_dev(-1, &dev_rsrc); + + switch (dev_rsrc) { + case SC_R_SDHC_0: + devno = 0; + break; + case SC_R_SDHC_1: + devno = 1; + break; + case SC_R_SDHC_2: + devno = 2; + break; + default: + /* If not boot from sd/mmc, use default value */ + return CONFIG_SYS_MMC_ENV_DEV; + } + + return board_mmc_get_env_dev(devno); +} +#endif + +#define MEMSTART_ALIGNMENT SZ_2M /* Align the memory start with 2MB */ + +static int get_owned_memreg(sc_rm_mr_t mr, sc_faddr_t *addr_start, + sc_faddr_t *addr_end) +{ + sc_faddr_t start, end; + int ret; + bool owned; + + owned = sc_rm_is_memreg_owned(-1, mr); + if (owned) { + ret = sc_rm_get_memreg_info(-1, mr, &start, &end); + if (ret) { + printf("Memreg get info failed, %d\n", ret); + return -EINVAL; + } + debug("0x%llx -- 0x%llx\n", start, end); + *addr_start = start; + *addr_end = end; + + return 0; + } + + return -EINVAL; +} + +__weak void board_mem_get_layout(u64 *phys_sdram_1_start, + u64 *phys_sdram_1_size, + u64 *phys_sdram_2_start, + u64 *phys_sdram_2_size) +{ + *phys_sdram_1_start = PHYS_SDRAM_1; + *phys_sdram_1_size = PHYS_SDRAM_1_SIZE; + *phys_sdram_2_start = PHYS_SDRAM_2; + *phys_sdram_2_size = PHYS_SDRAM_2_SIZE; +} + +phys_size_t get_effective_memsize(void) +{ + sc_rm_mr_t mr; + sc_faddr_t start, end, end1, start_aligned; + u64 phys_sdram_1_start, phys_sdram_1_size; + u64 phys_sdram_2_start, phys_sdram_2_size; + int err; + + board_mem_get_layout(&phys_sdram_1_start, &phys_sdram_1_size, + &phys_sdram_2_start, &phys_sdram_2_size); + + + end1 = (sc_faddr_t)phys_sdram_1_start + phys_sdram_1_size; + for (mr = 0; mr < 64; mr++) { + err = get_owned_memreg(mr, &start, &end); + if (!err) { + start_aligned = roundup(start, MEMSTART_ALIGNMENT); + /* Too small memory region, not use it */ + if (start_aligned > end) + continue; + + /* Find the memory region runs the U-Boot */ + if (start >= phys_sdram_1_start && start <= end1 && + (start <= CONFIG_SYS_TEXT_BASE && + end >= CONFIG_SYS_TEXT_BASE)) { + if ((end + 1) <= + ((sc_faddr_t)phys_sdram_1_start + + phys_sdram_1_size)) + return (end - phys_sdram_1_start + 1); + else + return phys_sdram_1_size; + } + } + } + + return phys_sdram_1_size; +} + +int dram_init(void) +{ + sc_rm_mr_t mr; + sc_faddr_t start, end, end1, end2; + u64 phys_sdram_1_start, phys_sdram_1_size; + u64 phys_sdram_2_start, phys_sdram_2_size; + int err; + + board_mem_get_layout(&phys_sdram_1_start, &phys_sdram_1_size, + &phys_sdram_2_start, &phys_sdram_2_size); + + end1 = (sc_faddr_t)phys_sdram_1_start + phys_sdram_1_size; + end2 = (sc_faddr_t)phys_sdram_2_start + phys_sdram_2_size; + for (mr = 0; mr < 64; mr++) { + err = get_owned_memreg(mr, &start, &end); + if (!err) { + start = roundup(start, MEMSTART_ALIGNMENT); + /* Too small memory region, not use it */ + if (start > end) + continue; + + if (start >= phys_sdram_1_start && start <= end1) { + if ((end + 1) <= end1) + gd->ram_size += end - start + 1; + else + gd->ram_size += end1 - start; + } else if (start >= phys_sdram_2_start && + start <= end2) { + if ((end + 1) <= end2) + gd->ram_size += end - start + 1; + else + gd->ram_size += end2 - start; + } + } + } + + /* If error, set to the default value */ + if (!gd->ram_size) { + gd->ram_size = phys_sdram_1_size; + gd->ram_size += phys_sdram_2_size; + } + return 0; +} + +static void dram_bank_sort(int current_bank) +{ + phys_addr_t start; + phys_size_t size; + + while (current_bank > 0) { + if (gd->bd->bi_dram[current_bank - 1].start > + gd->bd->bi_dram[current_bank].start) { + start = gd->bd->bi_dram[current_bank - 1].start; + size = gd->bd->bi_dram[current_bank - 1].size; + + gd->bd->bi_dram[current_bank - 1].start = + gd->bd->bi_dram[current_bank].start; + gd->bd->bi_dram[current_bank - 1].size = + gd->bd->bi_dram[current_bank].size; + + gd->bd->bi_dram[current_bank].start = start; + gd->bd->bi_dram[current_bank].size = size; + } + current_bank--; + } +} + +int dram_init_banksize(void) +{ + sc_rm_mr_t mr; + sc_faddr_t start, end, end1, end2; + int i = 0; + u64 phys_sdram_1_start, phys_sdram_1_size; + u64 phys_sdram_2_start, phys_sdram_2_size; + int err; + + board_mem_get_layout(&phys_sdram_1_start, &phys_sdram_1_size, + &phys_sdram_2_start, &phys_sdram_2_size); + + end1 = (sc_faddr_t)phys_sdram_1_start + phys_sdram_1_size; + end2 = (sc_faddr_t)phys_sdram_2_start + phys_sdram_2_size; + for (mr = 0; mr < 64 && i < CONFIG_NR_DRAM_BANKS; mr++) { + err = get_owned_memreg(mr, &start, &end); + if (!err) { + start = roundup(start, MEMSTART_ALIGNMENT); + if (start > end) /* Small memory region, no use it */ + continue; + + if (start >= phys_sdram_1_start && start <= end1) { + gd->bd->bi_dram[i].start = start; + + if ((end + 1) <= end1) + gd->bd->bi_dram[i].size = + end - start + 1; + else + gd->bd->bi_dram[i].size = end1 - start; + + dram_bank_sort(i); + i++; + } else if (start >= phys_sdram_2_start && start <= end2) { + gd->bd->bi_dram[i].start = start; + + if ((end + 1) <= end2) + gd->bd->bi_dram[i].size = + end - start + 1; + else + gd->bd->bi_dram[i].size = end2 - start; + + dram_bank_sort(i); + i++; + } + } + } + + /* If error, set to the default value */ + if (!i) { + gd->bd->bi_dram[0].start = phys_sdram_1_start; + gd->bd->bi_dram[0].size = phys_sdram_1_size; + gd->bd->bi_dram[1].start = phys_sdram_2_start; + gd->bd->bi_dram[1].size = phys_sdram_2_size; + } + + return 0; +} + +static u64 get_block_attrs(sc_faddr_t addr_start) +{ + u64 attr = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | PTE_BLOCK_NON_SHARE | + PTE_BLOCK_PXN | PTE_BLOCK_UXN; + u64 phys_sdram_1_start, phys_sdram_1_size; + u64 phys_sdram_2_start, phys_sdram_2_size; + + board_mem_get_layout(&phys_sdram_1_start, &phys_sdram_1_size, + &phys_sdram_2_start, &phys_sdram_2_size); + + if ((addr_start >= phys_sdram_1_start && + addr_start <= ((sc_faddr_t)phys_sdram_1_start + phys_sdram_1_size)) || + (addr_start >= phys_sdram_2_start && + addr_start <= ((sc_faddr_t)phys_sdram_2_start + phys_sdram_2_size))) + return (PTE_BLOCK_MEMTYPE(MT_NORMAL) | PTE_BLOCK_OUTER_SHARE); + + return attr; +} + +static u64 get_block_size(sc_faddr_t addr_start, sc_faddr_t addr_end) +{ + sc_faddr_t end1, end2; + u64 phys_sdram_1_start, phys_sdram_1_size; + u64 phys_sdram_2_start, phys_sdram_2_size; + + board_mem_get_layout(&phys_sdram_1_start, &phys_sdram_1_size, + &phys_sdram_2_start, &phys_sdram_2_size); + + + end1 = (sc_faddr_t)phys_sdram_1_start + phys_sdram_1_size; + end2 = (sc_faddr_t)phys_sdram_2_start + phys_sdram_2_size; + + if (addr_start >= phys_sdram_1_start && addr_start <= end1) { + if ((addr_end + 1) > end1) + return end1 - addr_start; + } else if (addr_start >= phys_sdram_2_start && addr_start <= end2) { + if ((addr_end + 1) > end2) + return end2 - addr_start; + } + + return (addr_end - addr_start + 1); +} + +#define MAX_PTE_ENTRIES 512 +#define MAX_MEM_MAP_REGIONS 16 + +static struct mm_region imx8_mem_map[MAX_MEM_MAP_REGIONS]; +struct mm_region *mem_map = imx8_mem_map; + +void enable_caches(void) +{ + sc_rm_mr_t mr; + sc_faddr_t start, end; + int err, i; + + /* Create map for registers access from 0x1c000000 to 0x80000000*/ + imx8_mem_map[0].virt = 0x1c000000UL; + imx8_mem_map[0].phys = 0x1c000000UL; + imx8_mem_map[0].size = 0x64000000UL; + imx8_mem_map[0].attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | + PTE_BLOCK_NON_SHARE | PTE_BLOCK_PXN | PTE_BLOCK_UXN; + + i = 1; + for (mr = 0; mr < 64 && i < MAX_MEM_MAP_REGIONS; mr++) { + err = get_owned_memreg(mr, &start, &end); + if (!err) { + imx8_mem_map[i].virt = start; + imx8_mem_map[i].phys = start; + imx8_mem_map[i].size = get_block_size(start, end); + imx8_mem_map[i].attrs = get_block_attrs(start); + i++; + } + } + + if (i < MAX_MEM_MAP_REGIONS) { + imx8_mem_map[i].size = 0; + imx8_mem_map[i].attrs = 0; + } else { + puts("Error, need more MEM MAP REGIONS reserved\n"); + icache_enable(); + return; + } + + for (i = 0; i < MAX_MEM_MAP_REGIONS; i++) { + debug("[%d] vir = 0x%llx phys = 0x%llx size = 0x%llx attrs = 0x%llx\n", + i, imx8_mem_map[i].virt, imx8_mem_map[i].phys, + imx8_mem_map[i].size, imx8_mem_map[i].attrs); + } + + icache_enable(); + dcache_enable(); +} + +#if !CONFIG_IS_ENABLED(SYS_DCACHE_OFF) +u64 get_page_table_size(void) +{ + u64 one_pt = MAX_PTE_ENTRIES * sizeof(u64); + u64 size = 0; + + /* + * For each memory region, the max table size: + * 2 level 3 tables + 2 level 2 tables + 1 level 1 table + */ + size = (2 + 2 + 1) * one_pt * MAX_MEM_MAP_REGIONS + one_pt; + + /* + * We need to duplicate our page table once to have an emergency pt to + * resort to when splitting page tables later on + */ + size *= 2; + + /* + * We may need to split page tables later on if dcache settings change, + * so reserve up to 4 (random pick) page tables for that. + */ + size += one_pt * 4; + + return size; +} +#endif + +#if defined(CONFIG_IMX8QM) +#define FUSE_MAC0_WORD0 452 +#define FUSE_MAC0_WORD1 453 +#define FUSE_MAC1_WORD0 454 +#define FUSE_MAC1_WORD1 455 +#elif defined(CONFIG_IMX8QXP) +#define FUSE_MAC0_WORD0 708 +#define FUSE_MAC0_WORD1 709 +#define FUSE_MAC1_WORD0 710 +#define FUSE_MAC1_WORD1 711 +#endif + +void imx_get_mac_from_fuse(int dev_id, unsigned char *mac) +{ + u32 word[2], val[2] = {}; + int i, ret; + + if (dev_id == 0) { + word[0] = FUSE_MAC0_WORD0; + word[1] = FUSE_MAC0_WORD1; + } else { + word[0] = FUSE_MAC1_WORD0; + word[1] = FUSE_MAC1_WORD1; + } + + for (i = 0; i < 2; i++) { + ret = sc_misc_otp_fuse_read(-1, word[i], &val[i]); + if (ret < 0) + goto err; + } + + mac[0] = val[0]; + mac[1] = val[0] >> 8; + mac[2] = val[0] >> 16; + mac[3] = val[0] >> 24; + mac[4] = val[1]; + mac[5] = val[1] >> 8; + + debug("%s: MAC%d: %02x.%02x.%02x.%02x.%02x.%02x\n", + __func__, dev_id, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + return; +err: + printf("%s: fuse %d, err: %d\n", __func__, word[i], ret); +} + +u32 get_cpu_rev(void) +{ + u32 id = 0, rev = 0; + int ret; + + ret = sc_misc_get_control(-1, SC_R_SYSTEM, SC_C_ID, &id); + if (ret) + return 0; + + rev = (id >> 5) & 0xf; + id = (id & 0x1f) + MXC_SOC_IMX8; /* Dummy ID for chip */ + + return (id << 12) | rev; +} + +void board_boot_order(u32 *spl_boot_list) +{ + spl_boot_list[0] = spl_boot_device(); + + if (spl_boot_list[0] == BOOT_DEVICE_SPI) { + /* Check whether we own the flexspi0, if not, use NOR boot */ + if (!sc_rm_is_resource_owned(-1, SC_R_FSPI_0)) + spl_boot_list[0] = BOOT_DEVICE_NOR; + } +} + +bool m4_parts_booted(void) +{ + sc_rm_pt_t m4_parts[2]; + int err; + + err = sc_rm_get_resource_owner(-1, SC_R_M4_0_PID0, &m4_parts[0]); + if (err) { + printf("%s get resource [%d] owner error: %d\n", __func__, + SC_R_M4_0_PID0, err); + return false; + } + + if (sc_pm_is_partition_started(-1, m4_parts[0])) + return true; + + if (is_imx8qm()) { + err = sc_rm_get_resource_owner(-1, SC_R_M4_1_PID0, &m4_parts[1]); + if (err) { + printf("%s get resource [%d] owner error: %d\n", + __func__, SC_R_M4_1_PID0, err); + return false; + } + + if (sc_pm_is_partition_started(-1, m4_parts[1])) + return true; + } + + return false; +} diff --git a/roms/u-boot/arch/arm/mach-imx/imx8/fdt.c b/roms/u-boot/arch/arm/mach-imx/imx8/fdt.c new file mode 100644 index 000000000..a132ce2e6 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/imx8/fdt.c @@ -0,0 +1,304 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2019 NXP + */ + +#include <common.h> +#include <log.h> +#include <asm/arch/sci/sci.h> +#include <asm/arch/sys_proto.h> +#include <asm/global_data.h> +#include <dm/ofnode.h> +#include <fdt_support.h> +#include <linux/libfdt.h> + +DECLARE_GLOBAL_DATA_PTR; + +static bool check_owned_resource(sc_rsrc_t rsrc_id) +{ + bool owned; + + owned = sc_rm_is_resource_owned(-1, rsrc_id); + + return owned; +} + +static int disable_fdt_node(void *blob, int nodeoffset) +{ + int rc, ret; + const char *status = "disabled"; + + do { + rc = fdt_setprop(blob, nodeoffset, "status", status, + strlen(status) + 1); + if (rc) { + if (rc == -FDT_ERR_NOSPACE) { + ret = fdt_increase_size(blob, 512); + if (ret) + return ret; + } + } + } while (rc == -FDT_ERR_NOSPACE); + + return rc; +} + +static void update_fdt_with_owned_resources(void *blob) +{ + /* + * Traverses the fdt nodes, check its power domain and use + * the resource id in the power domain for checking whether + * it is owned by current partition + */ + struct fdtdec_phandle_args args; + int offset = 0, depth = 0; + u32 rsrc_id; + int rc, i; + + for (offset = fdt_next_node(blob, offset, &depth); offset > 0; + offset = fdt_next_node(blob, offset, &depth)) { + debug("Node name: %s, depth %d\n", + fdt_get_name(blob, offset, NULL), depth); + + if (!fdt_get_property(blob, offset, "power-domains", NULL)) { + debug(" - ignoring node %s\n", + fdt_get_name(blob, offset, NULL)); + continue; + } + + if (!fdtdec_get_is_enabled(blob, offset)) { + debug(" - ignoring node %s\n", + fdt_get_name(blob, offset, NULL)); + continue; + } + + i = 0; + while (true) { + rc = fdtdec_parse_phandle_with_args(blob, offset, + "power-domains", + "#power-domain-cells", + 0, i++, &args); + if (rc == -ENOENT) { + break; + } else if (rc) { + printf("Parse power-domains of %s wrong: %d\n", + fdt_get_name(blob, offset, NULL), rc); + continue; + } + + rsrc_id = args.args[0]; + + if (!check_owned_resource(rsrc_id)) { + rc = disable_fdt_node(blob, offset); + if (!rc) { + printf("Disable %s rsrc %u not owned\n", + fdt_get_name(blob, offset, NULL), + rsrc_id); + } else { + printf("Unable to disable %s, err=%s\n", + fdt_get_name(blob, offset, NULL), + fdt_strerror(rc)); + } + } + } + } +} + +static int config_smmu_resource_sid(int rsrc, int sid) +{ + int err; + + err = sc_rm_set_master_sid(-1, rsrc, sid); + debug("set_master_sid rsrc=%d sid=0x%x err=%d\n", rsrc, sid, err); + if (err != SC_ERR_NONE) { + if (!check_owned_resource(rsrc)) { + printf("%s rsrc[%d] not owned\n", __func__, rsrc); + return -1; + } + pr_err("fail set_master_sid rsrc=%d sid=0x%x err=%d\n", rsrc, sid, err); + return -EINVAL; + } + + return 0; +} + +static int config_smmu_fdt_device_sid(void *blob, int device_offset, int sid) +{ + const char *name = fdt_get_name(blob, device_offset, NULL); + struct fdtdec_phandle_args args; + int rsrc, ret; + int proplen; + const fdt32_t *prop; + int i; + + prop = fdt_getprop(blob, device_offset, "fsl,sc_rsrc_id", &proplen); + if (prop) { + int i; + + debug("configure node %s sid 0x%x for %d resources\n", + name, sid, (int)(proplen / sizeof(fdt32_t))); + for (i = 0; i < proplen / sizeof(fdt32_t); ++i) { + ret = config_smmu_resource_sid(fdt32_to_cpu(prop[i]), + sid); + if (ret) + return ret; + } + + return 0; + } + + i = 0; + while (true) { + ret = fdtdec_parse_phandle_with_args(blob, device_offset, + "power-domains", + "#power-domain-cells", + 0, i++, &args); + if (ret == -ENOENT) { + break; + } else if (ret) { + printf("Parse power-domains of node %s wrong: %d\n", + fdt_get_name(blob, device_offset, NULL), ret); + continue; + } + + debug("configure node %s sid 0x%x rsrc=%d\n", + name, sid, rsrc); + rsrc = args.args[0]; + + ret = config_smmu_resource_sid(rsrc, sid); + if (ret) + break; + } + + return ret; +} + +static int config_smmu_fdt(void *blob) +{ + int offset, proplen, i, ret; + const fdt32_t *prop; + const char *name; + + /* Legacy smmu bindings, still used by xen. */ + offset = fdt_node_offset_by_compatible(blob, 0, "arm,mmu-500"); + prop = fdt_getprop(blob, offset, "mmu-masters", &proplen); + if (offset > 0 && prop) { + debug("found legacy mmu-masters property\n"); + + for (i = 0; i < proplen / 8; ++i) { + u32 phandle = fdt32_to_cpu(prop[2 * i]); + int sid = fdt32_to_cpu(prop[2 * i + 1]); + int device_offset; + + device_offset = fdt_node_offset_by_phandle(blob, + phandle); + if (device_offset < 0) { + pr_err("Not find device from mmu_masters: %d", + device_offset); + continue; + } + ret = config_smmu_fdt_device_sid(blob, device_offset, + sid); + if (ret) + return ret; + } + + /* Ignore new bindings if old bindings found, just like linux. */ + return 0; + } + + /* Generic smmu bindings */ + offset = 0; + while ((offset = fdt_next_node(blob, offset, NULL)) > 0) { + name = fdt_get_name(blob, offset, NULL); + prop = fdt_getprop(blob, offset, "iommus", &proplen); + if (!prop) + continue; + debug("node %s iommus proplen %d\n", name, proplen); + + if (proplen == 12) { + int sid = fdt32_to_cpu(prop[1]); + + config_smmu_fdt_device_sid(blob, offset, sid); + } else if (proplen != 4) { + debug("node %s ignore unexpected iommus proplen=%d\n", + name, proplen); + } + } + + return 0; +} + +static int ft_add_optee_node(void *fdt, struct bd_info *bd) +{ + const char *path, *subpath; + int offs; + + /* + * No TEE space allocated indicating no TEE running, so no + * need to add optee node in dts + */ + if (!boot_pointer[1]) + return 0; + + offs = fdt_increase_size(fdt, 512); + if (offs) { + printf("No Space for dtb\n"); + return 1; + } + + path = "/firmware"; + offs = fdt_path_offset(fdt, path); + if (offs < 0) { + path = "/"; + offs = fdt_path_offset(fdt, path); + + if (offs < 0) { + printf("Could not find root node.\n"); + return offs; + } + + subpath = "firmware"; + offs = fdt_add_subnode(fdt, offs, subpath); + if (offs < 0) { + printf("Could not create %s node.\n", subpath); + return offs; + } + } + + subpath = "optee"; + offs = fdt_add_subnode(fdt, offs, subpath); + if (offs < 0) { + printf("Could not create %s node.\n", subpath); + return offs; + } + + fdt_setprop_string(fdt, offs, "compatible", "linaro,optee-tz"); + fdt_setprop_string(fdt, offs, "method", "smc"); + + return 0; +} + +int ft_system_setup(void *blob, struct bd_info *bd) +{ + int ret; + int off; + + if (CONFIG_BOOTAUX_RESERVED_MEM_BASE) { + off = fdt_add_mem_rsv(blob, CONFIG_BOOTAUX_RESERVED_MEM_BASE, + CONFIG_BOOTAUX_RESERVED_MEM_SIZE); + if (off < 0) + printf("Failed to reserve memory for bootaux: %s\n", + fdt_strerror(off)); + } + + update_fdt_with_owned_resources(blob); + + if (is_imx8qm()) { + ret = config_smmu_fdt(blob); + if (ret) + return ret; + } + + return ft_add_optee_node(blob, bd); +} diff --git a/roms/u-boot/arch/arm/mach-imx/imx8/image.c b/roms/u-boot/arch/arm/mach-imx/imx8/image.c new file mode 100644 index 000000000..5abc0d3a3 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/imx8/image.c @@ -0,0 +1,249 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2019 NXP + */ + +#include <common.h> +#include <errno.h> +#include <log.h> +#include <malloc.h> +#include <asm/io.h> +#include <mmc.h> +#include <spi_flash.h> +#include <nand.h> +#include <asm/arch/image.h> +#include <asm/arch/sys_proto.h> +#include <asm/mach-imx/boot_mode.h> + +#define MMC_DEV 0 +#define QSPI_DEV 1 +#define NAND_DEV 2 +#define QSPI_NOR_DEV 3 + +static int __get_container_size(ulong addr) +{ + struct container_hdr *phdr; + struct boot_img_t *img_entry; + struct signature_block_hdr *sign_hdr; + u8 i = 0; + u32 max_offset = 0, img_end; + + phdr = (struct container_hdr *)addr; + if (phdr->tag != 0x87 && phdr->version != 0x0) { + debug("Wrong container header\n"); + return -EFAULT; + } + + max_offset = sizeof(struct container_hdr); + + img_entry = (struct boot_img_t *)(addr + sizeof(struct container_hdr)); + for (i = 0; i < phdr->num_images; i++) { + img_end = img_entry->offset + img_entry->size; + if (img_end > max_offset) + max_offset = img_end; + + debug("img[%u], end = 0x%x\n", i, img_end); + + img_entry++; + } + + if (phdr->sig_blk_offset != 0) { + sign_hdr = (struct signature_block_hdr *)(addr + phdr->sig_blk_offset); + u16 len = sign_hdr->length_lsb + (sign_hdr->length_msb << 8); + + if (phdr->sig_blk_offset + len > max_offset) + max_offset = phdr->sig_blk_offset + len; + + debug("sigblk, end = 0x%x\n", phdr->sig_blk_offset + len); + } + + return max_offset; +} + +static int get_container_size(void *dev, int dev_type, unsigned long offset) +{ + u8 *buf = malloc(CONTAINER_HDR_ALIGNMENT); + int ret = 0; + + if (!buf) { + printf("Malloc buffer failed\n"); + return -ENOMEM; + } + +#ifdef CONFIG_SPL_MMC_SUPPORT + if (dev_type == MMC_DEV) { + unsigned long count = 0; + struct mmc *mmc = (struct mmc *)dev; + + count = blk_dread(mmc_get_blk_desc(mmc), + offset / mmc->read_bl_len, + CONTAINER_HDR_ALIGNMENT / mmc->read_bl_len, + buf); + if (count == 0) { + printf("Read container image from MMC/SD failed\n"); + return -EIO; + } + } +#endif + +#ifdef CONFIG_SPL_SPI_LOAD + if (dev_type == QSPI_DEV) { + struct spi_flash *flash = (struct spi_flash *)dev; + + ret = spi_flash_read(flash, offset, + CONTAINER_HDR_ALIGNMENT, buf); + if (ret != 0) { + printf("Read container image from QSPI failed\n"); + return -EIO; + } + } +#endif + +#ifdef CONFIG_SPL_NAND_SUPPORT + if (dev_type == NAND_DEV) { + ret = nand_spl_load_image(offset, CONTAINER_HDR_ALIGNMENT, + buf); + if (ret != 0) { + printf("Read container image from NAND failed\n"); + return -EIO; + } + } +#endif + +#ifdef CONFIG_SPL_NOR_SUPPORT + if (dev_type == QSPI_NOR_DEV) + memcpy(buf, (const void *)offset, CONTAINER_HDR_ALIGNMENT); +#endif + + ret = __get_container_size((ulong)buf); + + free(buf); + + return ret; +} + +static unsigned long get_boot_device_offset(void *dev, int dev_type) +{ + unsigned long offset = 0; + + if (dev_type == MMC_DEV) { + struct mmc *mmc = (struct mmc *)dev; + + if (IS_SD(mmc) || mmc->part_config == MMCPART_NOAVAILABLE) { + offset = CONTAINER_HDR_MMCSD_OFFSET; + } else { + u8 part = EXT_CSD_EXTRACT_BOOT_PART(mmc->part_config); + + if (part == 1 || part == 2) { + if (is_imx8qxp() && is_soc_rev(CHIP_REV_B)) + offset = CONTAINER_HDR_MMCSD_OFFSET; + else + offset = CONTAINER_HDR_EMMC_OFFSET; + } else { + offset = CONTAINER_HDR_MMCSD_OFFSET; + } + } + } else if (dev_type == QSPI_DEV) { + offset = CONTAINER_HDR_QSPI_OFFSET; + } else if (dev_type == NAND_DEV) { + offset = CONTAINER_HDR_NAND_OFFSET; + } else if (dev_type == QSPI_NOR_DEV) { + offset = CONTAINER_HDR_QSPI_OFFSET + 0x08000000; + } + + return offset; +} + +static int get_imageset_end(void *dev, int dev_type) +{ + unsigned long offset1 = 0, offset2 = 0; + int value_container[2]; + + offset1 = get_boot_device_offset(dev, dev_type); + offset2 = CONTAINER_HDR_ALIGNMENT + offset1; + + value_container[0] = get_container_size(dev, dev_type, offset1); + if (value_container[0] < 0) { + printf("Parse seco container failed %d\n", value_container[0]); + return value_container[0]; + } + + debug("seco container size 0x%x\n", value_container[0]); + + value_container[1] = get_container_size(dev, dev_type, offset2); + if (value_container[1] < 0) { + debug("Parse scu container failed %d, only seco container\n", + value_container[1]); + /* return seco container total size */ + return value_container[0] + offset1; + } + + debug("scu container size 0x%x\n", value_container[1]); + + return value_container[1] + offset2; +} + +#ifdef CONFIG_SPL_SPI_LOAD +unsigned long spl_spi_get_uboot_offs(struct spi_flash *flash) +{ + int end; + + end = get_imageset_end(flash, QSPI_DEV); + end = ROUND(end, SZ_1K); + + printf("Load image from QSPI 0x%x\n", end); + + return end; +} +#endif + +#ifdef CONFIG_SPL_MMC_SUPPORT +unsigned long spl_mmc_get_uboot_raw_sector(struct mmc *mmc, + unsigned long raw_sect) +{ + int end; + + end = get_imageset_end(mmc, MMC_DEV); + end = ROUND(end, SZ_1K); + + printf("Load image from MMC/SD 0x%x\n", end); + + return end / mmc->read_bl_len; +} +#endif + +#ifdef CONFIG_SPL_NAND_SUPPORT +uint32_t spl_nand_get_uboot_raw_page(void) +{ + int end; + + end = get_imageset_end((void *)NULL, NAND_DEV); + end = ROUND(end, SZ_16K); + + printf("Load image from NAND 0x%x\n", end); + + return end; +} +#endif + +#ifdef CONFIG_SPL_NOR_SUPPORT +unsigned long spl_nor_get_uboot_base(void) +{ + int end; + + /* Calculate the image set end, + * if it is less than CONFIG_SYS_UBOOT_BASE(0x8281000), + * we use CONFIG_SYS_UBOOT_BASE + * Otherwise, use the calculated address + */ + end = get_imageset_end((void *)NULL, QSPI_NOR_DEV); + if (end <= CONFIG_SYS_UBOOT_BASE) + end = CONFIG_SYS_UBOOT_BASE; + else + end = ROUND(end, SZ_1K); + + printf("Load image from NOR 0x%x\n", end); + + return end; +} +#endif diff --git a/roms/u-boot/arch/arm/mach-imx/imx8/iomux.c b/roms/u-boot/arch/arm/mach-imx/imx8/iomux.c new file mode 100644 index 000000000..9c3cfbf00 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/imx8/iomux.c @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2018 NXP + */ + +#include <common.h> +#include <log.h> +#include <asm/global_data.h> +#include <asm/io.h> +#include <asm/arch/iomux.h> +#include <asm/arch/sci/sci.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* + * configures a single pad in the iomuxer + */ +void imx8_iomux_setup_pad(iomux_cfg_t pad) +{ + sc_pad_t pin_id = pad & PIN_ID_MASK; + int ret; + + u32 val = (u32)((pad & MUX_PAD_CTRL_MASK) >> MUX_PAD_CTRL_SHIFT); + + val |= PADRING_IFMUX_EN_MASK; + val |= PADRING_GP_EN_MASK; + + ret = sc_pad_set(-1, pin_id, val); + if (ret) + printf("sc_pad_set failed!, pin: %u, val: 0x%x\n", pin_id, val); + + debug("iomux: pin %d, val = 0x%x\n", pin_id, val); +} + +/* configures a list of pads within declared with IOMUX_PADS macro */ +void imx8_iomux_setup_multiple_pads(iomux_cfg_t const *pad_list, u32 count) +{ + iomux_cfg_t const *p = pad_list; + int i; + + for (i = 0; i < count; i++) { + imx8_iomux_setup_pad(*p); + p++; + } +} diff --git a/roms/u-boot/arch/arm/mach-imx/imx8/lowlevel_init.S b/roms/u-boot/arch/arm/mach-imx/imx8/lowlevel_init.S new file mode 100644 index 000000000..a66243c5e --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/imx8/lowlevel_init.S @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2019 NXP + */ + +#include <config.h> + +.align 8 +.global boot_pointer +boot_pointer: + .space 32 + +/* + * Routine: save_boot_params (called after reset from start.S) + */ + +.global save_boot_params +save_boot_params: + /* The firmware provided ATAG/FDT address can be found in r2/x0 */ + adr x0, boot_pointer + stp x1, x2, [x0], #16 + stp x3, x4, [x0], #16 + + /* + * We use absolute address not PC relative address for return. + * When running SPL on iMX8, the A core starts at address 0, + * an alias to OCRAM 0x100000, our linker address for SPL is + * from 0x100000. So using absolute address can jump to the OCRAM + * address from the alias. The alias only map first 96KB of OCRAM, + * so this require the SPL size can't beyond 96KB. + * But when using SPL DM, the size increase significantly and + * always beyonds 96KB. That's why we have to jump to OCRAM. + * Normal u-boot also runs into this codes, but there is no impact. + */ + ldr x1, =save_boot_params_ret + br x1 diff --git a/roms/u-boot/arch/arm/mach-imx/imx8/misc.c b/roms/u-boot/arch/arm/mach-imx/imx8/misc.c new file mode 100644 index 000000000..de19955e2 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/imx8/misc.c @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: GPL-2.0+ +#include <common.h> +#include <log.h> +#include <asm/arch/sci/sci.h> +#include <asm/mach-imx/sys_proto.h> +#include <imx_sip.h> +#include <linux/arm-smccc.h> + +int sc_pm_setup_uart(sc_rsrc_t uart_rsrc, sc_pm_clock_rate_t clk_rate) +{ + sc_pm_clock_rate_t rate = clk_rate; + int ret; + + /* Power up UARTn */ + ret = sc_pm_set_resource_power_mode(-1, uart_rsrc, SC_PM_PW_MODE_ON); + if (ret) + return ret; + + /* Set UARTn clock root to 'rate' MHz */ + ret = sc_pm_set_clock_rate(-1, uart_rsrc, SC_PM_CLK_PER, &rate); + if (ret) + return ret; + + /* Enable UARTn clock root */ + ret = sc_pm_clock_enable(-1, uart_rsrc, SC_PM_CLK_PER, true, false); + if (ret) + return ret; + + return 0; +} + +void build_info(void) +{ + struct arm_smccc_res res; + u32 seco_build = 0, seco_commit = 0; + u32 sc_build = 0, sc_commit = 0; + ulong atf_commit = 0; + + /* Get SCFW build and commit id */ + sc_misc_build_info(-1, &sc_build, &sc_commit); + if (!sc_build) { + printf("SCFW does not support build info\n"); + sc_commit = 0; /* Display 0 if build info not supported */ + } + + /* Get SECO FW build and commit id */ + sc_seco_build_info(-1, &seco_build, &seco_commit); + if (!seco_build) { + debug("SECO FW does not support build info\n"); + /* Display 0 when the build info is not supported */ + seco_commit = 0; + } + + /* Get ARM Trusted Firmware commit id */ + arm_smccc_smc(IMX_SIP_BUILDINFO, IMX_SIP_BUILDINFO_GET_COMMITHASH, + 0, 0, 0, 0, 0, 0, &res); + atf_commit = res.a0; + if (atf_commit == 0xffffffff) { + debug("ATF does not support build info\n"); + atf_commit = 0x30; /* Display 0 */ + } + + printf("Build: SCFW %08x, SECO-FW %08x, ATF %s\n", + sc_commit, seco_commit, (char *)&atf_commit); +} diff --git a/roms/u-boot/arch/arm/mach-imx/imx8/parse-container.c b/roms/u-boot/arch/arm/mach-imx/imx8/parse-container.c new file mode 100644 index 000000000..375098902 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/imx8/parse-container.c @@ -0,0 +1,208 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2018-2019 NXP + */ + +#include <common.h> +#include <errno.h> +#include <log.h> +#include <spl.h> +#include <asm/arch/image.h> +#include <asm/arch/sci/sci.h> + +#define SEC_SECURE_RAM_BASE 0x31800000UL +#define SEC_SECURE_RAM_END_BASE (SEC_SECURE_RAM_BASE + 0xFFFFUL) +#define SECO_LOCAL_SEC_SEC_SECURE_RAM_BASE 0x60000000UL + +#define SECO_PT 2U + +#ifdef CONFIG_AHAB_BOOT +static int authenticate_image(struct boot_img_t *img, int image_index) +{ + sc_faddr_t start, end; + sc_rm_mr_t mr; + int err; + int ret = 0; + + debug("img %d, dst 0x%x, src 0x%x, size 0x%x\n", + image_index, (uint32_t)img->dst, img->offset, img->size); + + /* Find the memreg and set permission for seco pt */ + err = sc_rm_find_memreg(-1, &mr, + img->dst & ~(CONFIG_SYS_CACHELINE_SIZE - 1), + ALIGN(img->dst + img->size, CONFIG_SYS_CACHELINE_SIZE) - 1); + + if (err) { + printf("can't find memreg for image %d load address 0x%x, error %d\n", + image_index, img->dst & ~(CONFIG_SYS_CACHELINE_SIZE - 1), err); + return -ENOMEM; + } + + err = sc_rm_get_memreg_info(-1, mr, &start, &end); + if (!err) + debug("memreg %u 0x%x -- 0x%x\n", mr, start, end); + + err = sc_rm_set_memreg_permissions(-1, mr, + SECO_PT, SC_RM_PERM_FULL); + if (err) { + printf("set permission failed for img %d, error %d\n", + image_index, err); + return -EPERM; + } + + err = sc_seco_authenticate(-1, SC_SECO_VERIFY_IMAGE, + 1 << image_index); + if (err) { + printf("authenticate img %d failed, return %d\n", + image_index, err); + ret = -EIO; + } + + err = sc_rm_set_memreg_permissions(-1, mr, + SECO_PT, SC_RM_PERM_NONE); + if (err) { + printf("remove permission failed for img %d, error %d\n", + image_index, err); + ret = -EPERM; + } + + return ret; +} +#endif + +static struct boot_img_t *read_auth_image(struct spl_image_info *spl_image, + struct spl_load_info *info, + struct container_hdr *container, + int image_index, + u32 container_sector) +{ + struct boot_img_t *images; + ulong sector; + u32 sectors; + + if (image_index > container->num_images) { + debug("Invalid image number\n"); + return NULL; + } + + images = (struct boot_img_t *)((u8 *)container + + sizeof(struct container_hdr)); + + if (images[image_index].offset % info->bl_len) { + printf("%s: image%d offset not aligned to %u\n", + __func__, image_index, info->bl_len); + return NULL; + } + + sectors = roundup(images[image_index].size, info->bl_len) / + info->bl_len; + sector = images[image_index].offset / info->bl_len + + container_sector; + + debug("%s: container: %p sector: %lu sectors: %u\n", __func__, + container, sector, sectors); + if (info->read(info, sector, sectors, + (void *)images[image_index].entry) != sectors) { + printf("%s wrong\n", __func__); + return NULL; + } + +#ifdef CONFIG_AHAB_BOOT + if (authenticate_image(&images[image_index], image_index)) { + printf("Failed to authenticate image %d\n", image_index); + return NULL; + } +#endif + + return &images[image_index]; +} + +static int read_auth_container(struct spl_image_info *spl_image, + struct spl_load_info *info, ulong sector) +{ + struct container_hdr *container = NULL; + u16 length; + u32 sectors; + int i, size, ret = 0; + + size = roundup(CONTAINER_HDR_ALIGNMENT, info->bl_len); + sectors = size / info->bl_len; + + /* + * It will not override the ATF code, so safe to use it here, + * no need malloc + */ + container = (struct container_hdr *)spl_get_load_buffer(-size, size); + + debug("%s: container: %p sector: %lu sectors: %u\n", __func__, + container, sector, sectors); + if (info->read(info, sector, sectors, container) != sectors) + return -EIO; + + if (container->tag != 0x87 && container->version != 0x0) { + printf("Wrong container header"); + return -ENOENT; + } + + if (!container->num_images) { + printf("Wrong container, no image found"); + return -ENOENT; + } + + length = container->length_lsb + (container->length_msb << 8); + debug("Container length %u\n", length); + + if (length > CONTAINER_HDR_ALIGNMENT) { + size = roundup(length, info->bl_len); + sectors = size / info->bl_len; + + container = (struct container_hdr *)spl_get_load_buffer(-size, size); + + debug("%s: container: %p sector: %lu sectors: %u\n", + __func__, container, sector, sectors); + if (info->read(info, sector, sectors, container) != + sectors) + return -EIO; + } + +#ifdef CONFIG_AHAB_BOOT + memcpy((void *)SEC_SECURE_RAM_BASE, (const void *)container, + ALIGN(length, CONFIG_SYS_CACHELINE_SIZE)); + + ret = sc_seco_authenticate(-1, SC_SECO_AUTH_CONTAINER, + SECO_LOCAL_SEC_SEC_SECURE_RAM_BASE); + if (ret) { + printf("authenticate container hdr failed, return %d\n", ret); + return ret; + } +#endif + + for (i = 0; i < container->num_images; i++) { + struct boot_img_t *image = read_auth_image(spl_image, info, + container, i, + sector); + + if (!image) { + ret = -EINVAL; + goto end_auth; + } + + if (i == 0) { + spl_image->load_addr = image->dst; + spl_image->entry_point = image->entry; + } + } + +end_auth: +#ifdef CONFIG_AHAB_BOOT + if (sc_seco_authenticate(-1, SC_SECO_REL_CONTAINER, 0)) + printf("Error: release container failed!\n"); +#endif + return ret; +} + +int spl_load_imx_container(struct spl_image_info *spl_image, + struct spl_load_info *info, ulong sector) +{ + return read_auth_container(spl_image, info, sector); +} diff --git a/roms/u-boot/arch/arm/mach-imx/imx8/snvs_security_sc.c b/roms/u-boot/arch/arm/mach-imx/imx8/snvs_security_sc.c new file mode 100644 index 000000000..6f9b1c99f --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/imx8/snvs_security_sc.c @@ -0,0 +1,925 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2019-2020 NXP. + */ + +/* + * Configuration of the Tamper pins in different mode: + * - default (no tamper pins): _default_ + * - passive mode expecting VCC on the line: "_passive_vcc_" + * - passive mode expecting VCC on the line: "_passive_gnd_" + * - active mode: "_active_" + */ + +#include <command.h> +#include <log.h> +#include <stddef.h> +#include <common.h> +#include <asm/arch/sci/sci.h> +#include <asm/arch-imx8/imx8-pins.h> +#include <asm/arch-imx8/snvs_security_sc.h> +#include <asm/global_data.h> + +/* Access to gd */ +DECLARE_GLOBAL_DATA_PTR; + +#define SC_WRITE_CONF 1 + +#define PGD_HEX_VALUE 0x41736166 +#define SRTC_EN 0x1 +#define DP_EN BIT(5) + +struct snvs_security_sc_conf { + struct snvs_hp_conf { + u32 lock; /* HPLR - HP Lock */ + u32 __cmd; /* HPCOMR - HP Command */ + u32 __ctl; /* HPCR - HP Control */ + u32 secvio_intcfg; /* HPSICR - Security Violation Int + * Config + */ + u32 secvio_ctl; /* HPSVCR - Security Violation Control*/ + u32 status; /* HPSR - HP Status */ + u32 secvio_status; /* HPSVSR - Security Violation Status */ + u32 __ha_counteriv; /* High Assurance Counter IV */ + u32 __ha_counter; /* High Assurance Counter */ + u32 __rtc_msb; /* Real Time Clock/Counter MSB */ + u32 __rtc_lsb; /* Real Time Counter LSB */ + u32 __time_alarm_msb; /* Time Alarm MSB */ + u32 __time_alarm_lsb; /* Time Alarm LSB */ + } hp; + struct snvs_lp_conf { + u32 lock; + u32 __ctl; + u32 __mstr_key_ctl; /* Master Key Control */ + u32 secvio_ctl; /* Security Violation Control */ + u32 tamper_filt_cfg; /* Tamper Glitch Filters Configuration*/ + u32 tamper_det_cfg; /* Tamper Detectors Configuration */ + u32 status; + u32 __srtc_msb; /* Secure Real Time Clock/Counter MSB */ + u32 __srtc_lsb; /* Secure Real Time Clock/Counter LSB */ + u32 __time_alarm; /* Time Alarm */ + u32 __smc_msb; /* Secure Monotonic Counter MSB */ + u32 __smc_lsb; /* Secure Monotonic Counter LSB */ + u32 __pwr_glitch_det; /* Power Glitch Detector */ + u32 __gen_purpose; + u8 __zmk[32]; /* Zeroizable Master Key */ + u32 __rsvd0; + u32 __gen_purposes[4]; /* gp0_30 to gp0_33 */ + u32 tamper_det_cfg2; /* Tamper Detectors Configuration2 */ + u32 tamper_det_status; /* Tamper Detectors status */ + u32 tamper_filt1_cfg; /* Tamper Glitch Filter1 Configuration*/ + u32 tamper_filt2_cfg; /* Tamper Glitch Filter2 Configuration*/ + u32 __rsvd1[4]; + u32 act_tamper1_cfg; /* Active Tamper1 Configuration */ + u32 act_tamper2_cfg; /* Active Tamper2 Configuration */ + u32 act_tamper3_cfg; /* Active Tamper3 Configuration */ + u32 act_tamper4_cfg; /* Active Tamper4 Configuration */ + u32 act_tamper5_cfg; /* Active Tamper5 Configuration */ + u32 __rsvd2[3]; + u32 act_tamper_ctl; /* Active Tamper Control */ + u32 act_tamper_clk_ctl; /* Active Tamper Clock Control */ + u32 act_tamper_routing_ctl1;/* Active Tamper Routing Control1 */ + u32 act_tamper_routing_ctl2;/* Active Tamper Routing Control2 */ + } lp; +}; + +static struct snvs_security_sc_conf snvs_default_config = { + .hp = { + .lock = 0x1f0703ff, + .secvio_ctl = 0x3000007f, + }, + .lp = { + .lock = 0x1f0003ff, + .secvio_ctl = 0x36, + .tamper_filt_cfg = 0, + .tamper_det_cfg = 0x76, /* analogic tampers + * + rollover tampers + */ + .tamper_det_cfg2 = 0, + .tamper_filt1_cfg = 0, + .tamper_filt2_cfg = 0, + .act_tamper1_cfg = 0, + .act_tamper2_cfg = 0, + .act_tamper3_cfg = 0, + .act_tamper4_cfg = 0, + .act_tamper5_cfg = 0, + .act_tamper_ctl = 0, + .act_tamper_clk_ctl = 0, + .act_tamper_routing_ctl1 = 0, + .act_tamper_routing_ctl2 = 0, + } +}; + +static struct snvs_security_sc_conf snvs_passive_vcc_config = { + .hp = { + .lock = 0x1f0703ff, + .secvio_ctl = 0x3000007f, + }, + .lp = { + .lock = 0x1f0003ff, + .secvio_ctl = 0x36, + .tamper_filt_cfg = 0, + .tamper_det_cfg = 0x276, /* ET1 will trig on line at GND + * + analogic tampers + * + rollover tampers + */ + .tamper_det_cfg2 = 0, + .tamper_filt1_cfg = 0, + .tamper_filt2_cfg = 0, + .act_tamper1_cfg = 0, + .act_tamper2_cfg = 0, + .act_tamper3_cfg = 0, + .act_tamper4_cfg = 0, + .act_tamper5_cfg = 0, + .act_tamper_ctl = 0, + .act_tamper_clk_ctl = 0, + .act_tamper_routing_ctl1 = 0, + .act_tamper_routing_ctl2 = 0, + } +}; + +static struct snvs_security_sc_conf snvs_passive_gnd_config = { + .hp = { + .lock = 0x1f0703ff, + .secvio_ctl = 0x3000007f, + }, + .lp = { + .lock = 0x1f0003ff, + .secvio_ctl = 0x36, + .tamper_filt_cfg = 0, + .tamper_det_cfg = 0xa76, /* ET1 will trig on line at VCC + * + analogic tampers + * + rollover tampers + */ + .tamper_det_cfg2 = 0, + .tamper_filt1_cfg = 0, + .tamper_filt2_cfg = 0, + .act_tamper1_cfg = 0, + .act_tamper2_cfg = 0, + .act_tamper3_cfg = 0, + .act_tamper4_cfg = 0, + .act_tamper5_cfg = 0, + .act_tamper_ctl = 0, + .act_tamper_clk_ctl = 0, + .act_tamper_routing_ctl1 = 0, + .act_tamper_routing_ctl2 = 0, + } +}; + +static struct snvs_security_sc_conf snvs_active_config = { + .hp = { + .lock = 0x1f0703ff, + .secvio_ctl = 0x3000007f, + }, + .lp = { + .lock = 0x1f0003ff, + .secvio_ctl = 0x36, + .tamper_filt_cfg = 0x00800000, /* Enable filtering */ + .tamper_det_cfg = 0x276, /* ET1 enabled + analogic tampers + * + rollover tampers + */ + .tamper_det_cfg2 = 0, + .tamper_filt1_cfg = 0, + .tamper_filt2_cfg = 0, + .act_tamper1_cfg = 0x84001111, + .act_tamper2_cfg = 0, + .act_tamper3_cfg = 0, + .act_tamper4_cfg = 0, + .act_tamper5_cfg = 0, + .act_tamper_ctl = 0x00010001, + .act_tamper_clk_ctl = 0, + .act_tamper_routing_ctl1 = 0x1, + .act_tamper_routing_ctl2 = 0, + } +}; + +static struct snvs_security_sc_conf *get_snvs_config(void) +{ + return &snvs_default_config; +} + +struct snvs_dgo_conf { + u32 tamper_offset_ctl; + u32 tamper_pull_ctl; + u32 tamper_ana_test_ctl; + u32 tamper_sensor_trim_ctl; + u32 tamper_misc_ctl; + u32 tamper_core_volt_mon_ctl; +}; + +static struct snvs_dgo_conf snvs_dgo_default_config = { + .tamper_misc_ctl = 0x80000000, /* Lock the DGO */ +}; + +static struct snvs_dgo_conf snvs_dgo_passive_vcc_config = { + .tamper_misc_ctl = 0x80000000, /* Lock the DGO */ + .tamper_pull_ctl = 0x00000001, /* Pull down ET1 */ + .tamper_ana_test_ctl = 0x20000000, /* Enable tamper */ +}; + +static struct snvs_dgo_conf snvs_dgo_passive_gnd_config = { + .tamper_misc_ctl = 0x80000000, /* Lock the DGO */ + .tamper_pull_ctl = 0x00000401, /* Pull up ET1 */ + .tamper_ana_test_ctl = 0x20000000, /* Enable tamper */ +}; + +static struct snvs_dgo_conf snvs_dgo_active_config = { + .tamper_misc_ctl = 0x80000000, /* Lock the DGO */ + .tamper_ana_test_ctl = 0x20000000, /* Enable tamper */ +}; + +static struct snvs_dgo_conf *get_snvs_dgo_config(void) +{ + return &snvs_dgo_default_config; +} + +struct tamper_pin_cfg { + u32 pad; + u32 mux_conf; +}; + +static struct tamper_pin_cfg tamper_pin_list_default_config[] = { + {SC_P_CSI_D00, 0}, /* Tamp_Out0 */ + {SC_P_CSI_D01, 0}, /* Tamp_Out1 */ + {SC_P_CSI_D02, 0}, /* Tamp_Out2 */ + {SC_P_CSI_D03, 0}, /* Tamp_Out3 */ + {SC_P_CSI_D04, 0}, /* Tamp_Out4 */ + {SC_P_CSI_D05, 0}, /* Tamp_In0 */ + {SC_P_CSI_D06, 0}, /* Tamp_In1 */ + {SC_P_CSI_D07, 0}, /* Tamp_In2 */ + {SC_P_CSI_HSYNC, 0}, /* Tamp_In3 */ + {SC_P_CSI_VSYNC, 0}, /* Tamp_In4 */ +}; + +static struct tamper_pin_cfg tamper_pin_list_passive_vcc_config[] = { + {SC_P_CSI_D05, 0x1c000060}, /* Tamp_In0 */ /* Sel tamper + OD input */ +}; + +static struct tamper_pin_cfg tamper_pin_list_passive_gnd_config[] = { + {SC_P_CSI_D05, 0x1c000060}, /* Tamp_In0 */ /* Sel tamper + OD input */ +}; + +static struct tamper_pin_cfg tamper_pin_list_active_config[] = { + {SC_P_CSI_D00, 0x1a000060}, /* Tamp_Out0 */ /* Sel tamper + OD */ + {SC_P_CSI_D05, 0x1c000060}, /* Tamp_In0 */ /* Sel tamper + OD input */ +}; + +#define TAMPER_PIN_LIST_CHOSEN tamper_pin_list_default_config + +static struct tamper_pin_cfg *get_tamper_pin_cfg_list(u32 *size) +{ + *size = sizeof(TAMPER_PIN_LIST_CHOSEN) / + sizeof(TAMPER_PIN_LIST_CHOSEN[0]); + + return TAMPER_PIN_LIST_CHOSEN; +} + +#define SC_CONF_OFFSET_OF(_field) \ + (offsetof(struct snvs_security_sc_conf, _field)) + +static u32 ptr_value(u32 *_p) +{ + return (_p) ? *_p : 0xdeadbeef; +} + +static int check_write_secvio_config(u32 id, u32 *_p1, u32 *_p2, + u32 *_p3, u32 *_p4, u32 *_p5, + u32 _cnt) +{ + int scierr = 0; + u32 d1 = ptr_value(_p1); + u32 d2 = ptr_value(_p2); + u32 d3 = ptr_value(_p3); + u32 d4 = ptr_value(_p4); + u32 d5 = ptr_value(_p5); + + scierr = sc_seco_secvio_config(-1, id, SC_WRITE_CONF, &d1, &d2, &d3, + &d4, &d4, _cnt); + if (scierr != SC_ERR_NONE) { + printf("Failed to set secvio configuration\n"); + debug("Failed to set conf id 0x%x with values ", id); + debug("0x%.8x 0x%.8x 0x%.8x 0x%.8x 0x%.8x (cnt: %d)\n", + d1, d2, d3, d4, d5, _cnt); + goto exit; + } + + if (_p1) + *(u32 *)_p1 = d1; + if (_p2) + *(u32 *)_p2 = d2; + if (_p3) + *(u32 *)_p3 = d3; + if (_p4) + *(u32 *)_p4 = d4; + if (_p5) + *(u32 *)_p5 = d5; + +exit: + return scierr; +} + +#define SC_CHECK_WRITE1(id, _p1) \ + check_write_secvio_config(id, _p1, NULL, NULL, NULL, NULL, 1) + +static int apply_snvs_config(struct snvs_security_sc_conf *cnf) +{ + int scierr = 0; + + debug("%s\n", __func__); + + debug("Applying config:\n" + "\thp.lock = 0x%.8x\n" + "\thp.secvio_ctl = 0x%.8x\n" + "\tlp.lock = 0x%.8x\n" + "\tlp.secvio_ctl = 0x%.8x\n" + "\tlp.tamper_filt_cfg = 0x%.8x\n" + "\tlp.tamper_det_cfg = 0x%.8x\n" + "\tlp.tamper_det_cfg2 = 0x%.8x\n" + "\tlp.tamper_filt1_cfg = 0x%.8x\n" + "\tlp.tamper_filt2_cfg = 0x%.8x\n" + "\tlp.act_tamper1_cfg = 0x%.8x\n" + "\tlp.act_tamper2_cfg = 0x%.8x\n" + "\tlp.act_tamper3_cfg = 0x%.8x\n" + "\tlp.act_tamper4_cfg = 0x%.8x\n" + "\tlp.act_tamper5_cfg = 0x%.8x\n" + "\tlp.act_tamper_ctl = 0x%.8x\n" + "\tlp.act_tamper_clk_ctl = 0x%.8x\n" + "\tlp.act_tamper_routing_ctl1 = 0x%.8x\n" + "\tlp.act_tamper_routing_ctl2 = 0x%.8x\n", + cnf->hp.lock, + cnf->hp.secvio_ctl, + cnf->lp.lock, + cnf->lp.secvio_ctl, + cnf->lp.tamper_filt_cfg, + cnf->lp.tamper_det_cfg, + cnf->lp.tamper_det_cfg2, + cnf->lp.tamper_filt1_cfg, + cnf->lp.tamper_filt2_cfg, + cnf->lp.act_tamper1_cfg, + cnf->lp.act_tamper2_cfg, + cnf->lp.act_tamper3_cfg, + cnf->lp.act_tamper4_cfg, + cnf->lp.act_tamper5_cfg, + cnf->lp.act_tamper_ctl, + cnf->lp.act_tamper_clk_ctl, + cnf->lp.act_tamper_routing_ctl1, + cnf->lp.act_tamper_routing_ctl2); + + scierr = check_write_secvio_config(SC_CONF_OFFSET_OF(lp.tamper_filt_cfg), + &cnf->lp.tamper_filt_cfg, + &cnf->lp.tamper_filt1_cfg, + &cnf->lp.tamper_filt2_cfg, NULL, + NULL, 3); + if (scierr != SC_ERR_NONE) + goto exit; + + /* Configure AT */ + scierr = check_write_secvio_config(SC_CONF_OFFSET_OF(lp.act_tamper1_cfg), + &cnf->lp.act_tamper1_cfg, + &cnf->lp.act_tamper2_cfg, + &cnf->lp.act_tamper2_cfg, + &cnf->lp.act_tamper2_cfg, + &cnf->lp.act_tamper2_cfg, 5); + if (scierr != SC_ERR_NONE) + goto exit; + + /* Configure AT routing */ + scierr = check_write_secvio_config(SC_CONF_OFFSET_OF(lp.act_tamper_routing_ctl1), + &cnf->lp.act_tamper_routing_ctl1, + &cnf->lp.act_tamper_routing_ctl2, + NULL, NULL, NULL, 2); + if (scierr != SC_ERR_NONE) + goto exit; + + /* Configure AT frequency */ + scierr = SC_CHECK_WRITE1(SC_CONF_OFFSET_OF(lp.act_tamper_clk_ctl), + &cnf->lp.act_tamper_clk_ctl); + if (scierr != SC_ERR_NONE) + goto exit; + + /* Activate the ATs */ + scierr = SC_CHECK_WRITE1(SC_CONF_OFFSET_OF(lp.act_tamper_ctl), + &cnf->lp.act_tamper_ctl); + if (scierr != SC_ERR_NONE) + goto exit; + + /* Activate the detectors */ + scierr = check_write_secvio_config(SC_CONF_OFFSET_OF(lp.tamper_det_cfg), + &cnf->lp.tamper_det_cfg, + &cnf->lp.tamper_det_cfg2, NULL, NULL, + NULL, 2); + if (scierr != SC_ERR_NONE) + goto exit; + + /* Configure LP secvio */ + scierr = SC_CHECK_WRITE1(SC_CONF_OFFSET_OF(lp.secvio_ctl), + &cnf->lp.secvio_ctl); + if (scierr != SC_ERR_NONE) + goto exit; + + /* Configure HP secvio */ + scierr = SC_CHECK_WRITE1(SC_CONF_OFFSET_OF(hp.secvio_ctl), + &cnf->hp.secvio_ctl); + if (scierr != SC_ERR_NONE) + goto exit; + + /* Lock access */ + scierr = SC_CHECK_WRITE1(SC_CONF_OFFSET_OF(hp.lock), &cnf->hp.lock); + if (scierr != SC_ERR_NONE) + goto exit; + + scierr = SC_CHECK_WRITE1(SC_CONF_OFFSET_OF(lp.lock), &cnf->lp.lock); + if (scierr != SC_ERR_NONE) + goto exit; + +exit: + return (scierr == SC_ERR_NONE) ? 0 : -EIO; +} + +static int dgo_write(u32 _id, u8 _access, u32 *_pdata) +{ + int scierr = sc_seco_secvio_dgo_config(-1, _id, _access, _pdata); + + if (scierr != SC_ERR_NONE) { + printf("Failed to set dgo configuration\n"); + debug("Failed to set conf id 0x%x : 0x%.8x", _id, *_pdata); + } + + return scierr; +} + +static int apply_snvs_dgo_config(struct snvs_dgo_conf *cnf) +{ + int scierr = 0; + + debug("%s\n", __func__); + + debug("Applying config:\n" + "\ttamper_offset_ctl = 0x%.8x\n" + "\ttamper_pull_ctl = 0x%.8x\n" + "\ttamper_ana_test_ctl = 0x%.8x\n" + "\ttamper_sensor_trim_ctl = 0x%.8x\n" + "\ttamper_misc_ctl = 0x%.8x\n" + "\ttamper_core_volt_mon_ctl = 0x%.8x\n", + cnf->tamper_offset_ctl, + cnf->tamper_pull_ctl, + cnf->tamper_ana_test_ctl, + cnf->tamper_sensor_trim_ctl, + cnf->tamper_misc_ctl, + cnf->tamper_core_volt_mon_ctl); + + dgo_write(0x04, 1, &cnf->tamper_offset_ctl); + if (scierr != SC_ERR_NONE) + goto exit; + + dgo_write(0x14, 1, &cnf->tamper_pull_ctl); + if (scierr != SC_ERR_NONE) + goto exit; + + dgo_write(0x24, 1, &cnf->tamper_ana_test_ctl); + if (scierr != SC_ERR_NONE) + goto exit; + + dgo_write(0x34, 1, &cnf->tamper_sensor_trim_ctl); + if (scierr != SC_ERR_NONE) + goto exit; + + dgo_write(0x54, 1, &cnf->tamper_core_volt_mon_ctl); + if (scierr != SC_ERR_NONE) + goto exit; + + /* Last as it could lock the writes */ + dgo_write(0x44, 1, &cnf->tamper_misc_ctl); + if (scierr != SC_ERR_NONE) + goto exit; + +exit: + return (scierr == SC_ERR_NONE) ? 0 : -EIO; +} + +static int pad_write(u32 _pad, u32 _value) +{ + int scierr = sc_pad_set(-1, _pad, _value); + + if (scierr != SC_ERR_NONE) { + printf("Failed to set pad configuration\n"); + debug("Failed to set conf pad 0x%x : 0x%.8x", _pad, _value); + } + + return scierr; +} + +static int apply_tamper_pin_list_config(struct tamper_pin_cfg *confs, u32 size) +{ + int scierr = 0; + u32 idx; + + debug("%s\n", __func__); + + for (idx = 0; idx < size; idx++) { + debug("\t idx %d: pad %d: 0x%.8x\n", idx, confs[idx].pad, + confs[idx].mux_conf); + pad_write(confs[idx].pad, 3 << 30 | confs[idx].mux_conf); + if (scierr != SC_ERR_NONE) + goto exit; + } + +exit: + return (scierr == SC_ERR_NONE) ? 0 : -EIO; +} + +int examples(void) +{ + u32 size; + struct snvs_security_sc_conf *snvs_conf; + struct snvs_dgo_conf *snvs_dgo_conf; + struct tamper_pin_cfg *tamper_pin_conf; + + /* Caller */ + snvs_conf = get_snvs_config(); + snvs_dgo_conf = get_snvs_dgo_config(); + tamper_pin_conf = get_tamper_pin_cfg_list(&size); + + /* Default */ + snvs_conf = &snvs_default_config; + snvs_dgo_conf = &snvs_dgo_default_config; + tamper_pin_conf = tamper_pin_list_default_config; + + /* Passive tamper expecting VCC on the line */ + snvs_conf = &snvs_passive_vcc_config; + snvs_dgo_conf = &snvs_dgo_passive_vcc_config; + tamper_pin_conf = tamper_pin_list_passive_vcc_config; + + /* Passive tamper expecting GND on the line */ + snvs_conf = &snvs_passive_gnd_config; + snvs_dgo_conf = &snvs_dgo_passive_gnd_config; + tamper_pin_conf = tamper_pin_list_passive_gnd_config; + + /* Active tamper */ + snvs_conf = &snvs_active_config; + snvs_dgo_conf = &snvs_dgo_active_config; + tamper_pin_conf = tamper_pin_list_active_config; + + return !snvs_conf + !snvs_dgo_conf + !tamper_pin_conf; +} + +#ifdef CONFIG_IMX_SNVS_SEC_SC_AUTO +int snvs_security_sc_init(void) +{ + int err = 0; + + struct snvs_security_sc_conf *snvs_conf; + struct snvs_dgo_conf *snvs_dgo_conf; + struct tamper_pin_cfg *tamper_pin_conf; + u32 size; + + debug("%s\n", __func__); + + snvs_conf = get_snvs_config(); + snvs_dgo_conf = get_snvs_dgo_config(); + + tamper_pin_conf = get_tamper_pin_cfg_list(&size); + + err = apply_tamper_pin_list_config(tamper_pin_conf, size); + if (err) { + debug("Failed to set pins\n"); + goto exit; + } + + err = apply_snvs_dgo_config(snvs_dgo_conf); + if (err) { + debug("Failed to set dgo\n"); + goto exit; + } + + err = apply_snvs_config(snvs_conf); + if (err) { + debug("Failed to set snvs\n"); + goto exit; + } + +exit: + return err; +} +#endif /* CONFIG_IMX_SNVS_SEC_SC_AUTO */ + +static char snvs_cfg_help_text[] = + "snvs_cfg\n" + "\thp.lock\n" + "\thp.secvio_ctl\n" + "\tlp.lock\n" + "\tlp.secvio_ctl\n" + "\tlp.tamper_filt_cfg\n" + "\tlp.tamper_det_cfg\n" + "\tlp.tamper_det_cfg2\n" + "\tlp.tamper_filt1_cfg\n" + "\tlp.tamper_filt2_cfg\n" + "\tlp.act_tamper1_cfg\n" + "\tlp.act_tamper2_cfg\n" + "\tlp.act_tamper3_cfg\n" + "\tlp.act_tamper4_cfg\n" + "\tlp.act_tamper5_cfg\n" + "\tlp.act_tamper_ctl\n" + "\tlp.act_tamper_clk_ctl\n" + "\tlp.act_tamper_routing_ctl1\n" + "\tlp.act_tamper_routing_ctl2\n" + "\n" + "ALL values should be in hexadecimal format"; + +#define NB_REGISTERS 18 +static int do_snvs_cfg(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + int err = 0; + u32 idx = 0; + + struct snvs_security_sc_conf conf = {0}; + + if (argc != (NB_REGISTERS + 1)) + return CMD_RET_USAGE; + + conf.hp.lock = simple_strtoul(argv[++idx], NULL, 16); + conf.hp.secvio_ctl = simple_strtoul(argv[++idx], NULL, 16); + conf.lp.lock = simple_strtoul(argv[++idx], NULL, 16); + conf.lp.secvio_ctl = simple_strtoul(argv[++idx], NULL, 16); + conf.lp.tamper_filt_cfg = simple_strtoul(argv[++idx], NULL, 16); + conf.lp.tamper_det_cfg = simple_strtoul(argv[++idx], NULL, 16); + conf.lp.tamper_det_cfg2 = simple_strtoul(argv[++idx], NULL, 16); + conf.lp.tamper_filt1_cfg = simple_strtoul(argv[++idx], NULL, 16); + conf.lp.tamper_filt2_cfg = simple_strtoul(argv[++idx], NULL, 16); + conf.lp.act_tamper1_cfg = simple_strtoul(argv[++idx], NULL, 16); + conf.lp.act_tamper2_cfg = simple_strtoul(argv[++idx], NULL, 16); + conf.lp.act_tamper3_cfg = simple_strtoul(argv[++idx], NULL, 16); + conf.lp.act_tamper4_cfg = simple_strtoul(argv[++idx], NULL, 16); + conf.lp.act_tamper5_cfg = simple_strtoul(argv[++idx], NULL, 16); + conf.lp.act_tamper_ctl = simple_strtoul(argv[++idx], NULL, 16); + conf.lp.act_tamper_clk_ctl = simple_strtoul(argv[++idx], NULL, 16); + conf.lp.act_tamper_routing_ctl1 = simple_strtoul(argv[++idx], NULL, 16); + conf.lp.act_tamper_routing_ctl2 = simple_strtoul(argv[++idx], NULL, 16); + + err = apply_snvs_config(&conf); + + return err; +} + +U_BOOT_CMD(snvs_cfg, + NB_REGISTERS + 1, 1, do_snvs_cfg, + "Security violation configuration", + snvs_cfg_help_text +); + +static char snvs_dgo_cfg_help_text[] = + "snvs_dgo_cfg\n" + "\ttamper_offset_ctl\n" + "\ttamper_pull_ctl\n" + "\ttamper_ana_test_ctl\n" + "\ttamper_sensor_trim_ctl\n" + "\ttamper_misc_ctl\n" + "\ttamper_core_volt_mon_ctl\n" + "\n" + "ALL values should be in hexadecimal format"; + +static int do_snvs_dgo_cfg(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + int err = 0; + u32 idx = 0; + + struct snvs_dgo_conf conf = {0}; + + if (argc != (6 + 1)) + return CMD_RET_USAGE; + + conf.tamper_offset_ctl = simple_strtoul(argv[++idx], NULL, 16); + conf.tamper_pull_ctl = simple_strtoul(argv[++idx], NULL, 16); + conf.tamper_ana_test_ctl = simple_strtoul(argv[++idx], NULL, 16); + conf.tamper_sensor_trim_ctl = simple_strtoul(argv[++idx], NULL, 16); + conf.tamper_misc_ctl = simple_strtoul(argv[++idx], NULL, 16); + conf.tamper_core_volt_mon_ctl = simple_strtoul(argv[++idx], NULL, 16); + + err = apply_snvs_dgo_config(&conf); + + return err; +} + +U_BOOT_CMD(snvs_dgo_cfg, + 7, 1, do_snvs_dgo_cfg, + "SNVS DGO configuration", + snvs_dgo_cfg_help_text +); + +static char tamper_pin_cfg_help_text[] = + "snvs_dgo_cfg\n" + "\tpad\n" + "\tvalue\n" + "\n" + "ALL values should be in hexadecimal format"; + +static int do_tamper_pin_cfg(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + int err = 0; + u32 idx = 0; + + struct tamper_pin_cfg conf = {0}; + + if (argc != (2 + 1)) + return CMD_RET_USAGE; + + conf.pad = simple_strtoul(argv[++idx], NULL, 10); + conf.mux_conf = simple_strtoul(argv[++idx], NULL, 16); + + err = apply_tamper_pin_list_config(&conf, 1); + + return err; +} + +U_BOOT_CMD(tamper_pin_cfg, + 3, 1, do_tamper_pin_cfg, + "tamper pin configuration", + tamper_pin_cfg_help_text +); + +static char snvs_clear_status_help_text[] = + "snvs_clear_status\n" + "\tHPSR\n" + "\tHPSVSR\n" + "\tLPSR\n" + "\tLPTDSR\n" + "\n" + "Write the status registers with the value provided," + " clearing the status"; + +static int do_snvs_clear_status(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + int scierr = 0; + u32 idx = 0; + + struct snvs_security_sc_conf conf = {0}; + + if (argc != (2 + 1)) + return CMD_RET_USAGE; + + conf.lp.status = simple_strtoul(argv[++idx], NULL, 16); + conf.lp.tamper_det_status = simple_strtoul(argv[++idx], NULL, 16); + + scierr = check_write_secvio_config(SC_CONF_OFFSET_OF(lp.status), + &conf.lp.status, NULL, NULL, NULL, + NULL, 1); + if (scierr != SC_ERR_NONE) + goto exit; + + scierr = check_write_secvio_config(SC_CONF_OFFSET_OF(lp.tamper_det_status), + &conf.lp.tamper_det_status, NULL, + NULL, NULL, NULL, 1); + if (scierr != SC_ERR_NONE) + goto exit; + +exit: + return (scierr == SC_ERR_NONE) ? 0 : 1; +} + +U_BOOT_CMD(snvs_clear_status, + 3, 1, do_snvs_clear_status, + "snvs clear status", + snvs_clear_status_help_text +); + +static char snvs_sec_status_help_text[] = + "snvs_sec_status\n" + "Display information about the security related to tamper and secvio"; + +static int do_snvs_sec_status(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + int scierr; + u32 idx; + + u32 data[5]; + + u32 pads[] = { + SC_P_CSI_D00, + SC_P_CSI_D01, + SC_P_CSI_D02, + SC_P_CSI_D03, + SC_P_CSI_D04, + SC_P_CSI_D05, + SC_P_CSI_D06, + SC_P_CSI_D07, + SC_P_CSI_HSYNC, + SC_P_CSI_VSYNC, + }; + + u32 fuses[] = { + 14, + 30, + 31, + 260, + 261, + 262, + 263, + 768, + }; + + struct snvs_reg { + u32 id; + u32 nb; + } snvs[] = { + /* Locks */ + {0x0, 1}, + {0x34, 1}, + /* Security violation */ + {0xc, 1}, + {0x10, 1}, + {0x18, 1}, + {0x40, 1}, + /* Temper detectors */ + {0x48, 2}, + {0x4c, 1}, + {0xa4, 1}, + /* */ + {0x44, 3}, + {0xe0, 1}, + {0xe4, 1}, + {0xe8, 2}, + /* Misc */ + {0x3c, 1}, + {0x5c, 2}, + {0x64, 1}, + {0xf8, 2}, + }; + + u32 dgo[] = { + 0x0, + 0x10, + 0x20, + 0x30, + 0x40, + 0x50, + }; + + /* Pins */ + printf("Pins:\n"); + for (idx = 0; idx < ARRAY_SIZE(pads); idx++) { + u8 pad_id = pads[idx]; + + scierr = sc_pad_get(-1, pad_id, &data[0]); + if (scierr == 0) + printf("\t- Pin %d: %.8x\n", pad_id, data[0]); + else + printf("Failed to read Pin %d\n", pad_id); + } + + /* Fuses */ + printf("Fuses:\n"); + for (idx = 0; idx < ARRAY_SIZE(fuses); idx++) { + u32 fuse_id = fuses[idx]; + + scierr = sc_misc_otp_fuse_read(-1, fuse_id, &data[0]); + if (scierr == 0) + printf("\t- Fuse %d: %.8x\n", fuse_id, data[0]); + else + printf("Failed to read Fuse %d\n", fuse_id); + } + + /* SNVS */ + printf("SNVS:\n"); + for (idx = 0; idx < ARRAY_SIZE(snvs); idx++) { + struct snvs_reg *reg = &snvs[idx]; + + scierr = sc_seco_secvio_config(-1, reg->id, 0, &data[0], + &data[1], &data[2], &data[3], + &data[4], reg->nb); + if (scierr == 0) { + int subidx; + + printf("\t- SNVS %.2x(%d):", reg->id, reg->nb); + for (subidx = 0; subidx < reg->nb; subidx++) + printf(" %.8x", data[subidx]); + printf("\n"); + } else { + printf("Failed to read SNVS %d\n", reg->id); + } + } + + /* DGO */ + printf("DGO:\n"); + for (idx = 0; idx < ARRAY_SIZE(dgo); idx++) { + u8 dgo_id = dgo[idx]; + + scierr = sc_seco_secvio_dgo_config(-1, dgo_id, 0, &data[0]); + if (scierr == 0) + printf("\t- DGO %.2x: %.8x\n", dgo_id, data[0]); + else + printf("Failed to read DGO %d\n", dgo_id); + } + + return 0; +} + +U_BOOT_CMD(snvs_sec_status, + 1, 1, do_snvs_sec_status, + "tamper pin configuration", + snvs_sec_status_help_text +); diff --git a/roms/u-boot/arch/arm/mach-imx/imx8m/Kconfig b/roms/u-boot/arch/arm/mach-imx/imx8m/Kconfig new file mode 100644 index 000000000..0669363c0 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/imx8m/Kconfig @@ -0,0 +1,158 @@ +if ARCH_IMX8M + +config IMX8M + bool + select HAS_CAAM + select ROM_UNIFIED_SECTIONS + +config IMX8MQ + bool + select IMX8M + +config IMX8MM + bool + select IMX8M + +config IMX8MN + bool + select IMX8M + +config IMX8MP + bool + select IMX8M + +config SYS_SOC + default "imx8m" + +choice + prompt "NXP i.MX8M board select" + optional + +config TARGET_IMX8MQ_CM + bool "Ronetix iMX8MQ-CM SoM" + select BINMAN + select IMX8MQ + select IMX8M_LPDDR4 + +config TARGET_IMX8MQ_EVK + bool "imx8mq_evk" + select IMX8MQ + select IMX8M_LPDDR4 + +config TARGET_IMX8MQ_PHANBELL + bool "imx8mq_phanbell" + select IMX8MQ + select IMX8M_LPDDR4 + +config TARGET_IMX8MM_EVK + bool "imx8mm LPDDR4 EVK board" + select BINMAN + select IMX8MM + select SUPPORT_SPL + select IMX8M_LPDDR4 + +config TARGET_IMX8MM_ICORE_MX8MM + bool "Engicam i.Core MX8M Mini SOM" + select IMX8MM + select SUPPORT_SPL + select IMX8M_LPDDR4 + help + i.Core MX8M Mini is an EDIMM SOM based on NXP i.MX8MM. + + i.Core MX8M Mini EDIMM2.2: + * EDIMM2.2 is a Form Factor Capacitive Evaluation Board. + * i.Core MX8M Mini needs to mount on top of EDIMM2.2 for + creating complete i.Core MX8M Mini EDIMM2.2 Starter Kit. + + i.Core MX8M Mini C.TOUCH 2.0 + * C.TOUCH 2.0 is a general purpose Carrier board. + * i.Core MX8M Mini needs to mount on top of this Carrier board + for creating complete i.Core MX8M Mini C.TOUCH 2.0 board. + +config TARGET_IMX8MM_VENICE + bool "Support Gateworks Venice iMX8M Mini module" + select IMX8MM + select SUPPORT_SPL + select IMX8M_LPDDR4 + +config TARGET_IMX8MN_EVK + bool "imx8mn LPDDR4 EVK board" + select BINMAN + select IMX8MN + select SUPPORT_SPL + select IMX8M_LPDDR4 + +config TARGET_IMX8MN_DDR4_EVK + bool "imx8mn DDR4 EVK board" + select BINMAN + select IMX8MN + select SUPPORT_SPL + select IMX8M_DDR4 + +config TARGET_IMX8MP_EVK + bool "imx8mp LPDDR4 EVK board" + select BINMAN + select IMX8MP + select SUPPORT_SPL + select IMX8M_LPDDR4 + +config TARGET_PICO_IMX8MQ + bool "Support Technexion Pico iMX8MQ" + select IMX8MQ + select IMX8M_LPDDR4 + +config TARGET_VERDIN_IMX8MM + bool "Support Toradex Verdin iMX8M Mini module" + select IMX8MM + select SUPPORT_SPL + select IMX8M_LPDDR4 + +config TARGET_IMX8MM_BEACON + bool "imx8mm Beacon Embedded devkit" + select IMX8MM + select SUPPORT_SPL + select IMX8M_LPDDR4 + +config TARGET_IMX8MN_BEACON + bool "imx8mn Beacon Embedded devkit" + select IMX8MN + select SUPPORT_SPL + select IMX8M_LPDDR4 + +config TARGET_PHYCORE_IMX8MM + bool "PHYTEC PHYCORE i.MX8MM" + select IMX8MM + select SUPPORT_SPL + select IMX8M_LPDDR4 + +config TARGET_PHYCORE_IMX8MP + bool "PHYTEC PHYCORE i.MX8MP" + select IMX8MP + select SUPPORT_SPL + select IMX8M_LPDDR4 + +config TARGET_IMX8MM_CL_IOT_GATE + bool "CompuLab iot-gate-imx8" + select BINMAN + select IMX8MM + select SUPPORT_SPL + select IMX8M_LPDDR4 +endchoice + +source "board/beacon/imx8mm/Kconfig" +source "board/beacon/imx8mn/Kconfig" +source "board/compulab/imx8mm-cl-iot-gate/Kconfig" +source "board/engicam/imx8mm/Kconfig" +source "board/freescale/imx8mq_evk/Kconfig" +source "board/freescale/imx8mm_evk/Kconfig" +source "board/freescale/imx8mn_evk/Kconfig" +source "board/freescale/imx8mp_evk/Kconfig" +source "board/gateworks/venice/Kconfig" +source "board/google/imx8mq_phanbell/Kconfig" +source "board/phytec/phycore_imx8mm/Kconfig" +source "board/phytec/phycore_imx8mp/Kconfig" +source "board/ronetix/imx8mq-cm/Kconfig" +source "board/technexion/pico-imx8mq/Kconfig" +source "board/toradex/verdin-imx8mm/Kconfig" + +endif diff --git a/roms/u-boot/arch/arm/mach-imx/imx8m/Makefile b/roms/u-boot/arch/arm/mach-imx/imx8m/Makefile new file mode 100644 index 000000000..d9dee894a --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/imx8m/Makefile @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright 2017 NXP + +obj-y += lowlevel_init.o +obj-y += clock_slice.o soc.o +obj-$(CONFIG_IMX8MQ) += clock_imx8mq.o +obj-$(CONFIG_IMX8MM)$(CONFIG_IMX8MN)$(CONFIG_IMX8MP) += clock_imx8mm.o diff --git a/roms/u-boot/arch/arm/mach-imx/imx8m/clock_imx8mm.c b/roms/u-boot/arch/arm/mach-imx/imx8m/clock_imx8mm.c new file mode 100644 index 000000000..f8e4ec0d9 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/imx8m/clock_imx8mm.c @@ -0,0 +1,938 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2018-2019 NXP + * + * Peng Fan <peng.fan@nxp.com> + */ + +#include <common.h> +#include <asm/arch/clock.h> +#include <asm/arch/imx-regs.h> +#include <asm/arch/sys_proto.h> +#include <asm/global_data.h> +#include <asm/io.h> +#include <div64.h> +#include <errno.h> +#include <linux/bitops.h> +#include <linux/delay.h> + +DECLARE_GLOBAL_DATA_PTR; + +static struct anamix_pll *ana_pll = (struct anamix_pll *)ANATOP_BASE_ADDR; + +static u32 get_root_clk(enum clk_root_index clock_id); + +#ifdef CONFIG_IMX_HAB +void hab_caam_clock_enable(unsigned char enable) +{ + /* The CAAM clock is always on for iMX8M */ +} +#endif + +void enable_ocotp_clk(unsigned char enable) +{ + clock_enable(CCGR_OCOTP, !!enable); +} + +int enable_i2c_clk(unsigned char enable, unsigned i2c_num) +{ + /* 0 - 3 is valid i2c num */ + if (i2c_num > 3) + return -EINVAL; + + clock_enable(CCGR_I2C1 + i2c_num, !!enable); + + return 0; +} + +#ifdef CONFIG_SPL_BUILD +static struct imx_int_pll_rate_table imx8mm_fracpll_tbl[] = { + PLL_1443X_RATE(1000000000U, 250, 3, 1, 0), + PLL_1443X_RATE(800000000U, 300, 9, 0, 0), + PLL_1443X_RATE(750000000U, 250, 8, 0, 0), + PLL_1443X_RATE(650000000U, 325, 3, 2, 0), + PLL_1443X_RATE(600000000U, 300, 3, 2, 0), + PLL_1443X_RATE(594000000U, 99, 1, 2, 0), + PLL_1443X_RATE(400000000U, 300, 9, 1, 0), + PLL_1443X_RATE(266000000U, 400, 9, 2, 0), + PLL_1443X_RATE(167000000U, 334, 3, 4, 0), + PLL_1443X_RATE(100000000U, 300, 9, 3, 0), +}; + +static int fracpll_configure(enum pll_clocks pll, u32 freq) +{ + int i; + u32 tmp, div_val; + void *pll_base; + struct imx_int_pll_rate_table *rate; + + for (i = 0; i < ARRAY_SIZE(imx8mm_fracpll_tbl); i++) { + if (freq == imx8mm_fracpll_tbl[i].rate) + break; + } + + if (i == ARRAY_SIZE(imx8mm_fracpll_tbl)) { + printf("%s: No matched freq table %u\n", __func__, freq); + return -EINVAL; + } + + rate = &imx8mm_fracpll_tbl[i]; + + switch (pll) { + case ANATOP_DRAM_PLL: + setbits_le32(GPC_BASE_ADDR + 0xEC, 1 << 7); + setbits_le32(GPC_BASE_ADDR + 0xF8, 1 << 5); + writel(SRC_DDR1_ENABLE_MASK, SRC_BASE_ADDR + 0x1004); + + pll_base = &ana_pll->dram_pll_gnrl_ctl; + break; + case ANATOP_VIDEO_PLL: + pll_base = &ana_pll->video_pll1_gnrl_ctl; + break; + default: + return 0; + } + /* Bypass clock and set lock to pll output lock */ + tmp = readl(pll_base); + tmp |= BYPASS_MASK; + writel(tmp, pll_base); + + /* Enable RST */ + tmp &= ~RST_MASK; + writel(tmp, pll_base); + + div_val = (rate->mdiv << MDIV_SHIFT) | (rate->pdiv << PDIV_SHIFT) | + (rate->sdiv << SDIV_SHIFT); + writel(div_val, pll_base + 4); + writel(rate->kdiv << KDIV_SHIFT, pll_base + 8); + + __udelay(100); + + /* Disable RST */ + tmp |= RST_MASK; + writel(tmp, pll_base); + + /* Wait Lock*/ + while (!(readl(pll_base) & LOCK_STATUS)) + ; + + /* Bypass */ + tmp &= ~BYPASS_MASK; + writel(tmp, pll_base); + + return 0; +} + +void dram_pll_init(ulong pll_val) +{ + fracpll_configure(ANATOP_DRAM_PLL, pll_val); +} + +static struct dram_bypass_clk_setting imx8mm_dram_bypass_tbl[] = { + DRAM_BYPASS_ROOT_CONFIG(MHZ(100), 2, CLK_ROOT_PRE_DIV1, 2, + CLK_ROOT_PRE_DIV2), + DRAM_BYPASS_ROOT_CONFIG(MHZ(250), 3, CLK_ROOT_PRE_DIV2, 2, + CLK_ROOT_PRE_DIV2), + DRAM_BYPASS_ROOT_CONFIG(MHZ(400), 1, CLK_ROOT_PRE_DIV2, 3, + CLK_ROOT_PRE_DIV2), +}; + +void dram_enable_bypass(ulong clk_val) +{ + int i; + struct dram_bypass_clk_setting *config; + + for (i = 0; i < ARRAY_SIZE(imx8mm_dram_bypass_tbl); i++) { + if (clk_val == imx8mm_dram_bypass_tbl[i].clk) + break; + } + + if (i == ARRAY_SIZE(imx8mm_dram_bypass_tbl)) { + printf("%s: No matched freq table %lu\n", __func__, clk_val); + return; + } + + config = &imx8mm_dram_bypass_tbl[i]; + + clock_set_target_val(DRAM_ALT_CLK_ROOT, CLK_ROOT_ON | + CLK_ROOT_SOURCE_SEL(config->alt_root_sel) | + CLK_ROOT_PRE_DIV(config->alt_pre_div)); + clock_set_target_val(DRAM_APB_CLK_ROOT, CLK_ROOT_ON | + CLK_ROOT_SOURCE_SEL(config->apb_root_sel) | + CLK_ROOT_PRE_DIV(config->apb_pre_div)); + clock_set_target_val(DRAM_SEL_CFG, CLK_ROOT_ON | + CLK_ROOT_SOURCE_SEL(1)); +} + +void dram_disable_bypass(void) +{ + clock_set_target_val(DRAM_SEL_CFG, CLK_ROOT_ON | + CLK_ROOT_SOURCE_SEL(0)); + clock_set_target_val(DRAM_APB_CLK_ROOT, CLK_ROOT_ON | + CLK_ROOT_SOURCE_SEL(4) | + CLK_ROOT_PRE_DIV(CLK_ROOT_PRE_DIV5)); +} +#endif + +int intpll_configure(enum pll_clocks pll, ulong freq) +{ + void __iomem *pll_gnrl_ctl, __iomem *pll_div_ctl; + u32 pll_div_ctl_val, pll_clke_masks; + + switch (pll) { + case ANATOP_SYSTEM_PLL1: + pll_gnrl_ctl = &ana_pll->sys_pll1_gnrl_ctl; + pll_div_ctl = &ana_pll->sys_pll1_div_ctl; + pll_clke_masks = INTPLL_DIV20_CLKE_MASK | + INTPLL_DIV10_CLKE_MASK | INTPLL_DIV8_CLKE_MASK | + INTPLL_DIV6_CLKE_MASK | INTPLL_DIV5_CLKE_MASK | + INTPLL_DIV4_CLKE_MASK | INTPLL_DIV3_CLKE_MASK | + INTPLL_DIV2_CLKE_MASK | INTPLL_CLKE_MASK; + break; + case ANATOP_SYSTEM_PLL2: + pll_gnrl_ctl = &ana_pll->sys_pll2_gnrl_ctl; + pll_div_ctl = &ana_pll->sys_pll2_div_ctl; + pll_clke_masks = INTPLL_DIV20_CLKE_MASK | + INTPLL_DIV10_CLKE_MASK | INTPLL_DIV8_CLKE_MASK | + INTPLL_DIV6_CLKE_MASK | INTPLL_DIV5_CLKE_MASK | + INTPLL_DIV4_CLKE_MASK | INTPLL_DIV3_CLKE_MASK | + INTPLL_DIV2_CLKE_MASK | INTPLL_CLKE_MASK; + break; + case ANATOP_SYSTEM_PLL3: + pll_gnrl_ctl = &ana_pll->sys_pll3_gnrl_ctl; + pll_div_ctl = &ana_pll->sys_pll3_div_ctl; + pll_clke_masks = INTPLL_CLKE_MASK; + break; + case ANATOP_ARM_PLL: + pll_gnrl_ctl = &ana_pll->arm_pll_gnrl_ctl; + pll_div_ctl = &ana_pll->arm_pll_div_ctl; + pll_clke_masks = INTPLL_CLKE_MASK; + break; + case ANATOP_GPU_PLL: + pll_gnrl_ctl = &ana_pll->gpu_pll_gnrl_ctl; + pll_div_ctl = &ana_pll->gpu_pll_div_ctl; + pll_clke_masks = INTPLL_CLKE_MASK; + break; + case ANATOP_VPU_PLL: + pll_gnrl_ctl = &ana_pll->vpu_pll_gnrl_ctl; + pll_div_ctl = &ana_pll->vpu_pll_div_ctl; + pll_clke_masks = INTPLL_CLKE_MASK; + break; + default: + return -EINVAL; + }; + + switch (freq) { + case MHZ(600): + /* 24 * 0x12c / 3 / 2 ^ 2 */ + pll_div_ctl_val = INTPLL_MAIN_DIV_VAL(0x12c) | + INTPLL_PRE_DIV_VAL(3) | INTPLL_POST_DIV_VAL(2); + break; + case MHZ(750): + /* 24 * 0xfa / 2 / 2 ^ 2 */ + pll_div_ctl_val = INTPLL_MAIN_DIV_VAL(0xfa) | + INTPLL_PRE_DIV_VAL(2) | INTPLL_POST_DIV_VAL(2); + break; + case MHZ(800): + /* 24 * 0x190 / 3 / 2 ^ 2 */ + pll_div_ctl_val = INTPLL_MAIN_DIV_VAL(0x190) | + INTPLL_PRE_DIV_VAL(3) | INTPLL_POST_DIV_VAL(2); + break; + case MHZ(1000): + /* 24 * 0xfa / 3 / 2 ^ 1 */ + pll_div_ctl_val = INTPLL_MAIN_DIV_VAL(0xfa) | + INTPLL_PRE_DIV_VAL(3) | INTPLL_POST_DIV_VAL(1); + break; + case MHZ(1200): + /* 24 * 0xc8 / 2 / 2 ^ 1 */ + pll_div_ctl_val = INTPLL_MAIN_DIV_VAL(0xc8) | + INTPLL_PRE_DIV_VAL(2) | INTPLL_POST_DIV_VAL(1); + break; + case MHZ(2000): + /* 24 * 0xfa / 3 / 2 ^ 0 */ + pll_div_ctl_val = INTPLL_MAIN_DIV_VAL(0xfa) | + INTPLL_PRE_DIV_VAL(3) | INTPLL_POST_DIV_VAL(0); + break; + default: + return -EINVAL; + }; + /* Bypass clock and set lock to pll output lock */ + setbits_le32(pll_gnrl_ctl, INTPLL_BYPASS_MASK | INTPLL_LOCK_SEL_MASK); + /* Enable reset */ + clrbits_le32(pll_gnrl_ctl, INTPLL_RST_MASK); + /* Configure */ + writel(pll_div_ctl_val, pll_div_ctl); + + __udelay(100); + + /* Disable reset */ + setbits_le32(pll_gnrl_ctl, INTPLL_RST_MASK); + /* Wait Lock */ + while (!(readl(pll_gnrl_ctl) & INTPLL_LOCK_MASK)) + ; + /* Clear bypass */ + clrbits_le32(pll_gnrl_ctl, INTPLL_BYPASS_MASK); + setbits_le32(pll_gnrl_ctl, pll_clke_masks); + + return 0; +} + +void init_uart_clk(u32 index) +{ + /* + * set uart clock root + * 24M OSC + */ + switch (index) { + case 0: + clock_enable(CCGR_UART1, 0); + clock_set_target_val(UART1_CLK_ROOT, CLK_ROOT_ON | + CLK_ROOT_SOURCE_SEL(0)); + clock_enable(CCGR_UART1, 1); + return; + case 1: + clock_enable(CCGR_UART2, 0); + clock_set_target_val(UART2_CLK_ROOT, CLK_ROOT_ON | + CLK_ROOT_SOURCE_SEL(0)); + clock_enable(CCGR_UART2, 1); + return; + case 2: + clock_enable(CCGR_UART3, 0); + clock_set_target_val(UART3_CLK_ROOT, CLK_ROOT_ON | + CLK_ROOT_SOURCE_SEL(0)); + clock_enable(CCGR_UART3, 1); + return; + case 3: + clock_enable(CCGR_UART4, 0); + clock_set_target_val(UART4_CLK_ROOT, CLK_ROOT_ON | + CLK_ROOT_SOURCE_SEL(0)); + clock_enable(CCGR_UART4, 1); + return; + default: + printf("Invalid uart index\n"); + return; + } +} + +void init_wdog_clk(void) +{ + clock_enable(CCGR_WDOG1, 0); + clock_enable(CCGR_WDOG2, 0); + clock_enable(CCGR_WDOG3, 0); + clock_set_target_val(WDOG_CLK_ROOT, CLK_ROOT_ON | + CLK_ROOT_SOURCE_SEL(0)); + clock_enable(CCGR_WDOG1, 1); + clock_enable(CCGR_WDOG2, 1); + clock_enable(CCGR_WDOG3, 1); +} + +void init_clk_usdhc(u32 index) +{ + /* + * set usdhc clock root + * sys pll1 400M + */ + switch (index) { + case 0: + clock_enable(CCGR_USDHC1, 0); + clock_set_target_val(USDHC1_CLK_ROOT, CLK_ROOT_ON | + CLK_ROOT_SOURCE_SEL(1)); + clock_enable(CCGR_USDHC1, 1); + return; + case 1: + clock_enable(CCGR_USDHC2, 0); + clock_set_target_val(USDHC2_CLK_ROOT, CLK_ROOT_ON | + CLK_ROOT_SOURCE_SEL(1)); + clock_enable(CCGR_USDHC2, 1); + return; + case 2: + clock_enable(CCGR_USDHC3, 0); + clock_set_target_val(USDHC3_CLK_ROOT, CLK_ROOT_ON | + CLK_ROOT_SOURCE_SEL(1)); + clock_enable(CCGR_USDHC3, 1); + return; + default: + printf("Invalid usdhc index\n"); + return; + } +} + +void init_clk_ecspi(u32 index) +{ + switch (index) { + case 0: + clock_enable(CCGR_ECSPI1, 0); + clock_set_target_val(ECSPI1_CLK_ROOT, CLK_ROOT_ON | CLK_ROOT_SOURCE_SEL(0)); + clock_enable(CCGR_ECSPI1, 1); + return; + case 1: + clock_enable(CCGR_ECSPI2, 0); + clock_set_target_val(ECSPI2_CLK_ROOT, CLK_ROOT_ON | CLK_ROOT_SOURCE_SEL(0)); + clock_enable(CCGR_ECSPI2, 1); + return; + case 2: + clock_enable(CCGR_ECSPI3, 0); + clock_set_target_val(ECSPI3_CLK_ROOT, CLK_ROOT_ON | CLK_ROOT_SOURCE_SEL(0)); + clock_enable(CCGR_ECSPI3, 1); + return; + default: + printf("Invalid ecspi index\n"); + return; + } +} + +void init_nand_clk(void) +{ + /* + * set rawnand root + * sys pll1 400M + */ + clock_enable(CCGR_RAWNAND, 0); + clock_set_target_val(NAND_CLK_ROOT, CLK_ROOT_ON | + CLK_ROOT_SOURCE_SEL(3) | CLK_ROOT_POST_DIV(CLK_ROOT_POST_DIV4)); /* 100M */ + clock_enable(CCGR_RAWNAND, 1); +} + +int clock_init(void) +{ + u32 val_cfg0; + + /* + * The gate is not exported to clk tree, so configure them here. + * According to ANAMIX SPEC + * sys pll1 fixed at 800MHz + * sys pll2 fixed at 1GHz + * Here we only enable the outputs. + */ + val_cfg0 = readl(&ana_pll->sys_pll1_gnrl_ctl); + val_cfg0 |= INTPLL_CLKE_MASK | INTPLL_DIV2_CLKE_MASK | + INTPLL_DIV3_CLKE_MASK | INTPLL_DIV4_CLKE_MASK | + INTPLL_DIV5_CLKE_MASK | INTPLL_DIV6_CLKE_MASK | + INTPLL_DIV8_CLKE_MASK | INTPLL_DIV10_CLKE_MASK | + INTPLL_DIV20_CLKE_MASK; + writel(val_cfg0, &ana_pll->sys_pll1_gnrl_ctl); + + val_cfg0 = readl(&ana_pll->sys_pll2_gnrl_ctl); + val_cfg0 |= INTPLL_CLKE_MASK | INTPLL_DIV2_CLKE_MASK | + INTPLL_DIV3_CLKE_MASK | INTPLL_DIV4_CLKE_MASK | + INTPLL_DIV5_CLKE_MASK | INTPLL_DIV6_CLKE_MASK | + INTPLL_DIV8_CLKE_MASK | INTPLL_DIV10_CLKE_MASK | + INTPLL_DIV20_CLKE_MASK; + writel(val_cfg0, &ana_pll->sys_pll2_gnrl_ctl); + + /* Configure ARM at 1.2GHz */ + clock_set_target_val(ARM_A53_CLK_ROOT, CLK_ROOT_ON | + CLK_ROOT_SOURCE_SEL(2)); + + intpll_configure(ANATOP_ARM_PLL, MHZ(1200)); + + /* Bypass CCM A53 ROOT, Switch to ARM PLL -> MUX-> CPU */ + clock_set_target_val(CORE_SEL_CFG, CLK_ROOT_SOURCE_SEL(1)); + + if (is_imx8mn() || is_imx8mp()) + intpll_configure(ANATOP_SYSTEM_PLL3, MHZ(600)); + else + intpll_configure(ANATOP_SYSTEM_PLL3, MHZ(750)); + +#ifdef CONFIG_IMX8MP + /* 8MP ROM already set NOC to 800Mhz, only need to configure NOC_IO clk to 600Mhz */ + /* 8MP ROM already set GIC to 400Mhz, system_pll1_800m with div = 2 */ + clock_set_target_val(NOC_IO_CLK_ROOT, CLK_ROOT_ON | CLK_ROOT_SOURCE_SEL(2)); +#else + clock_set_target_val(NOC_CLK_ROOT, CLK_ROOT_ON | CLK_ROOT_SOURCE_SEL(2)); + + /* config GIC to sys_pll2_100m */ + clock_enable(CCGR_GIC, 0); + clock_set_target_val(GIC_CLK_ROOT, CLK_ROOT_ON | + CLK_ROOT_SOURCE_SEL(3)); + clock_enable(CCGR_GIC, 1); +#endif + + clock_set_target_val(NAND_USDHC_BUS_CLK_ROOT, CLK_ROOT_ON | + CLK_ROOT_SOURCE_SEL(1)); + + clock_enable(CCGR_DDR1, 0); + clock_set_target_val(DRAM_ALT_CLK_ROOT, CLK_ROOT_ON | + CLK_ROOT_SOURCE_SEL(1)); + clock_set_target_val(DRAM_APB_CLK_ROOT, CLK_ROOT_ON | + CLK_ROOT_SOURCE_SEL(1)); + clock_enable(CCGR_DDR1, 1); + + init_wdog_clk(); + + clock_enable(CCGR_TEMP_SENSOR, 1); + + clock_enable(CCGR_SEC_DEBUG, 1); + + return 0; +}; + +u32 imx_get_uartclk(void) +{ + return 24000000U; +} + +static u32 decode_intpll(enum clk_root_src intpll) +{ + u32 pll_gnrl_ctl, pll_div_ctl, pll_clke_mask; + u32 main_div, pre_div, post_div, div; + u64 freq; + + switch (intpll) { + case ARM_PLL_CLK: + pll_gnrl_ctl = readl(&ana_pll->arm_pll_gnrl_ctl); + pll_div_ctl = readl(&ana_pll->arm_pll_div_ctl); + break; + case GPU_PLL_CLK: + pll_gnrl_ctl = readl(&ana_pll->gpu_pll_gnrl_ctl); + pll_div_ctl = readl(&ana_pll->gpu_pll_div_ctl); + break; + case VPU_PLL_CLK: + pll_gnrl_ctl = readl(&ana_pll->vpu_pll_gnrl_ctl); + pll_div_ctl = readl(&ana_pll->vpu_pll_div_ctl); + break; + case SYSTEM_PLL1_800M_CLK: + case SYSTEM_PLL1_400M_CLK: + case SYSTEM_PLL1_266M_CLK: + case SYSTEM_PLL1_200M_CLK: + case SYSTEM_PLL1_160M_CLK: + case SYSTEM_PLL1_133M_CLK: + case SYSTEM_PLL1_100M_CLK: + case SYSTEM_PLL1_80M_CLK: + case SYSTEM_PLL1_40M_CLK: + pll_gnrl_ctl = readl(&ana_pll->sys_pll1_gnrl_ctl); + pll_div_ctl = readl(&ana_pll->sys_pll1_div_ctl); + break; + case SYSTEM_PLL2_1000M_CLK: + case SYSTEM_PLL2_500M_CLK: + case SYSTEM_PLL2_333M_CLK: + case SYSTEM_PLL2_250M_CLK: + case SYSTEM_PLL2_200M_CLK: + case SYSTEM_PLL2_166M_CLK: + case SYSTEM_PLL2_125M_CLK: + case SYSTEM_PLL2_100M_CLK: + case SYSTEM_PLL2_50M_CLK: + pll_gnrl_ctl = readl(&ana_pll->sys_pll2_gnrl_ctl); + pll_div_ctl = readl(&ana_pll->sys_pll2_div_ctl); + break; + case SYSTEM_PLL3_CLK: + pll_gnrl_ctl = readl(&ana_pll->sys_pll3_gnrl_ctl); + pll_div_ctl = readl(&ana_pll->sys_pll3_div_ctl); + break; + default: + return -EINVAL; + } + + /* Only support SYS_XTAL 24M, PAD_CLK not take into consideration */ + if ((pll_gnrl_ctl & INTPLL_REF_CLK_SEL_MASK) != 0) + return 0; + + if ((pll_gnrl_ctl & INTPLL_RST_MASK) == 0) + return 0; + + /* + * When BYPASS is equal to 1, PLL enters the bypass mode + * regardless of the values of RESETB + */ + if (pll_gnrl_ctl & INTPLL_BYPASS_MASK) + return 24000000u; + + if (!(pll_gnrl_ctl & INTPLL_LOCK_MASK)) { + puts("pll not locked\n"); + return 0; + } + + switch (intpll) { + case ARM_PLL_CLK: + case GPU_PLL_CLK: + case VPU_PLL_CLK: + case SYSTEM_PLL3_CLK: + case SYSTEM_PLL1_800M_CLK: + case SYSTEM_PLL2_1000M_CLK: + pll_clke_mask = INTPLL_CLKE_MASK; + div = 1; + break; + + case SYSTEM_PLL1_400M_CLK: + case SYSTEM_PLL2_500M_CLK: + pll_clke_mask = INTPLL_DIV2_CLKE_MASK; + div = 2; + break; + + case SYSTEM_PLL1_266M_CLK: + case SYSTEM_PLL2_333M_CLK: + pll_clke_mask = INTPLL_DIV3_CLKE_MASK; + div = 3; + break; + + case SYSTEM_PLL1_200M_CLK: + case SYSTEM_PLL2_250M_CLK: + pll_clke_mask = INTPLL_DIV4_CLKE_MASK; + div = 4; + break; + + case SYSTEM_PLL1_160M_CLK: + case SYSTEM_PLL2_200M_CLK: + pll_clke_mask = INTPLL_DIV5_CLKE_MASK; + div = 5; + break; + + case SYSTEM_PLL1_133M_CLK: + case SYSTEM_PLL2_166M_CLK: + pll_clke_mask = INTPLL_DIV6_CLKE_MASK; + div = 6; + break; + + case SYSTEM_PLL1_100M_CLK: + case SYSTEM_PLL2_125M_CLK: + pll_clke_mask = INTPLL_DIV8_CLKE_MASK; + div = 8; + break; + + case SYSTEM_PLL1_80M_CLK: + case SYSTEM_PLL2_100M_CLK: + pll_clke_mask = INTPLL_DIV10_CLKE_MASK; + div = 10; + break; + + case SYSTEM_PLL1_40M_CLK: + case SYSTEM_PLL2_50M_CLK: + pll_clke_mask = INTPLL_DIV20_CLKE_MASK; + div = 20; + break; + default: + return -EINVAL; + } + + if ((pll_gnrl_ctl & pll_clke_mask) == 0) + return 0; + + main_div = (pll_div_ctl & INTPLL_MAIN_DIV_MASK) >> + INTPLL_MAIN_DIV_SHIFT; + pre_div = (pll_div_ctl & INTPLL_PRE_DIV_MASK) >> + INTPLL_PRE_DIV_SHIFT; + post_div = (pll_div_ctl & INTPLL_POST_DIV_MASK) >> + INTPLL_POST_DIV_SHIFT; + + /* FFVCO = (m * FFIN) / p, FFOUT = (m * FFIN) / (p * 2^s) */ + freq = 24000000ULL * main_div; + return lldiv(freq, pre_div * (1 << post_div) * div); +} + +static u32 decode_fracpll(enum clk_root_src frac_pll) +{ + u32 pll_gnrl_ctl, pll_fdiv_ctl0, pll_fdiv_ctl1; + u32 main_div, pre_div, post_div, k; + + switch (frac_pll) { + case DRAM_PLL1_CLK: + pll_gnrl_ctl = readl(&ana_pll->dram_pll_gnrl_ctl); + pll_fdiv_ctl0 = readl(&ana_pll->dram_pll_fdiv_ctl0); + pll_fdiv_ctl1 = readl(&ana_pll->dram_pll_fdiv_ctl1); + break; + case AUDIO_PLL1_CLK: + pll_gnrl_ctl = readl(&ana_pll->audio_pll1_gnrl_ctl); + pll_fdiv_ctl0 = readl(&ana_pll->audio_pll1_fdiv_ctl0); + pll_fdiv_ctl1 = readl(&ana_pll->audio_pll1_fdiv_ctl1); + break; + case AUDIO_PLL2_CLK: + pll_gnrl_ctl = readl(&ana_pll->audio_pll2_gnrl_ctl); + pll_fdiv_ctl0 = readl(&ana_pll->audio_pll2_fdiv_ctl0); + pll_fdiv_ctl1 = readl(&ana_pll->audio_pll2_fdiv_ctl1); + break; + case VIDEO_PLL_CLK: + pll_gnrl_ctl = readl(&ana_pll->video_pll1_gnrl_ctl); + pll_fdiv_ctl0 = readl(&ana_pll->video_pll1_fdiv_ctl0); + pll_fdiv_ctl1 = readl(&ana_pll->video_pll1_fdiv_ctl1); + break; + default: + printf("Unsupported clk_root_src %d\n", frac_pll); + return 0; + } + + /* Only support SYS_XTAL 24M, PAD_CLK not take into consideration */ + if ((pll_gnrl_ctl & GENMASK(1, 0)) != 0) + return 0; + + if ((pll_gnrl_ctl & RST_MASK) == 0) + return 0; + /* + * When BYPASS is equal to 1, PLL enters the bypass mode + * regardless of the values of RESETB + */ + if (pll_gnrl_ctl & BYPASS_MASK) + return 24000000u; + + if (!(pll_gnrl_ctl & LOCK_STATUS)) { + puts("pll not locked\n"); + return 0; + } + + if (!(pll_gnrl_ctl & CLKE_MASK)) + return 0; + + main_div = (pll_fdiv_ctl0 & MDIV_MASK) >> + MDIV_SHIFT; + pre_div = (pll_fdiv_ctl0 & PDIV_MASK) >> + PDIV_SHIFT; + post_div = (pll_fdiv_ctl0 & SDIV_MASK) >> + SDIV_SHIFT; + + k = pll_fdiv_ctl1 & KDIV_MASK; + + return lldiv((main_div * 65536 + k) * 24000000ULL, + 65536 * pre_div * (1 << post_div)); +} + +static u32 get_root_src_clk(enum clk_root_src root_src) +{ + switch (root_src) { + case OSC_24M_CLK: + return 24000000u; + case OSC_HDMI_CLK: + return 26000000u; + case OSC_32K_CLK: + return 32000u; + case ARM_PLL_CLK: + case GPU_PLL_CLK: + case VPU_PLL_CLK: + case SYSTEM_PLL1_800M_CLK: + case SYSTEM_PLL1_400M_CLK: + case SYSTEM_PLL1_266M_CLK: + case SYSTEM_PLL1_200M_CLK: + case SYSTEM_PLL1_160M_CLK: + case SYSTEM_PLL1_133M_CLK: + case SYSTEM_PLL1_100M_CLK: + case SYSTEM_PLL1_80M_CLK: + case SYSTEM_PLL1_40M_CLK: + case SYSTEM_PLL2_1000M_CLK: + case SYSTEM_PLL2_500M_CLK: + case SYSTEM_PLL2_333M_CLK: + case SYSTEM_PLL2_250M_CLK: + case SYSTEM_PLL2_200M_CLK: + case SYSTEM_PLL2_166M_CLK: + case SYSTEM_PLL2_125M_CLK: + case SYSTEM_PLL2_100M_CLK: + case SYSTEM_PLL2_50M_CLK: + case SYSTEM_PLL3_CLK: + return decode_intpll(root_src); + case DRAM_PLL1_CLK: + case AUDIO_PLL1_CLK: + case AUDIO_PLL2_CLK: + case VIDEO_PLL_CLK: + return decode_fracpll(root_src); + case ARM_A53_ALT_CLK: + return get_root_clk(ARM_A53_CLK_ROOT); + default: + return 0; + } + + return 0; +} + +static u32 get_root_clk(enum clk_root_index clock_id) +{ + enum clk_root_src root_src; + u32 post_podf, pre_podf, root_src_clk; + + if (clock_root_enabled(clock_id) <= 0) + return 0; + + if (clock_get_prediv(clock_id, &pre_podf) < 0) + return 0; + + if (clock_get_postdiv(clock_id, &post_podf) < 0) + return 0; + + if (clock_get_src(clock_id, &root_src) < 0) + return 0; + + root_src_clk = get_root_src_clk(root_src); + + return root_src_clk / (post_podf + 1) / (pre_podf + 1); +} + +u32 get_arm_core_clk(void) +{ + enum clk_root_src root_src; + u32 root_src_clk; + + if (clock_get_src(CORE_SEL_CFG, &root_src) < 0) + return 0; + + root_src_clk = get_root_src_clk(root_src); + + return root_src_clk; +} + +u32 mxc_get_clock(enum mxc_clock clk) +{ + u32 val; + + switch (clk) { + case MXC_ARM_CLK: + return get_arm_core_clk(); + case MXC_IPG_CLK: + clock_get_target_val(IPG_CLK_ROOT, &val); + val = val & 0x3; + return get_root_clk(AHB_CLK_ROOT) / 2 / (val + 1); + case MXC_CSPI_CLK: + return get_root_clk(ECSPI1_CLK_ROOT); + case MXC_ESDHC_CLK: + return get_root_clk(USDHC1_CLK_ROOT); + case MXC_ESDHC2_CLK: + return get_root_clk(USDHC2_CLK_ROOT); + case MXC_ESDHC3_CLK: + return get_root_clk(USDHC3_CLK_ROOT); + case MXC_I2C_CLK: + return get_root_clk(I2C1_CLK_ROOT); + case MXC_UART_CLK: + return get_root_clk(UART1_CLK_ROOT); + case MXC_QSPI_CLK: + return get_root_clk(QSPI_CLK_ROOT); + default: + printf("Unsupported mxc_clock %d\n", clk); + break; + } + + return 0; +} + +#ifdef CONFIG_DWC_ETH_QOS +int set_clk_eqos(enum enet_freq type) +{ + u32 target; + u32 enet1_ref; + + switch (type) { + case ENET_125MHZ: + enet1_ref = ENET1_REF_CLK_ROOT_FROM_PLL_ENET_MAIN_125M_CLK; + break; + case ENET_50MHZ: + enet1_ref = ENET1_REF_CLK_ROOT_FROM_PLL_ENET_MAIN_50M_CLK; + break; + case ENET_25MHZ: + enet1_ref = ENET1_REF_CLK_ROOT_FROM_PLL_ENET_MAIN_25M_CLK; + break; + default: + return -EINVAL; + } + + /* disable the clock first */ + clock_enable(CCGR_QOS_ETHENET, 0); + clock_enable(CCGR_SDMA2, 0); + + /* set enet axi clock 266Mhz */ + target = CLK_ROOT_ON | ENET_AXI_CLK_ROOT_FROM_SYS1_PLL_266M | + CLK_ROOT_PRE_DIV(CLK_ROOT_PRE_DIV1) | + CLK_ROOT_POST_DIV(CLK_ROOT_POST_DIV1); + clock_set_target_val(ENET_AXI_CLK_ROOT, target); + + target = CLK_ROOT_ON | enet1_ref | + CLK_ROOT_PRE_DIV(CLK_ROOT_PRE_DIV1) | + CLK_ROOT_POST_DIV(CLK_ROOT_POST_DIV1); + clock_set_target_val(ENET_QOS_CLK_ROOT, target); + + target = CLK_ROOT_ON | + ENET1_TIME_CLK_ROOT_FROM_PLL_ENET_MAIN_100M_CLK | + CLK_ROOT_PRE_DIV(CLK_ROOT_PRE_DIV1) | + CLK_ROOT_POST_DIV(CLK_ROOT_POST_DIV4); + clock_set_target_val(ENET_QOS_TIMER_CLK_ROOT, target); + + /* enable clock */ + clock_enable(CCGR_QOS_ETHENET, 1); + clock_enable(CCGR_SDMA2, 1); + + return 0; +} + +int imx_eqos_txclk_set_rate(ulong rate) +{ + u32 val; + u32 eqos_post_div; + + /* disable the clock first */ + clock_enable(CCGR_QOS_ETHENET, 0); + clock_enable(CCGR_SDMA2, 0); + + switch (rate) { + case 125000000: + eqos_post_div = 1; + break; + case 25000000: + eqos_post_div = 125000000 / 25000000; + break; + case 2500000: + eqos_post_div = 125000000 / 2500000; + break; + default: + return -EINVAL; + } + + clock_get_target_val(ENET_QOS_CLK_ROOT, &val); + val &= ~(CLK_ROOT_PRE_DIV_MASK | CLK_ROOT_POST_DIV_MASK); + val |= CLK_ROOT_PRE_DIV(CLK_ROOT_PRE_DIV1) | + CLK_ROOT_POST_DIV(eqos_post_div - 1); + clock_set_target_val(ENET_QOS_CLK_ROOT, val); + + /* enable clock */ + clock_enable(CCGR_QOS_ETHENET, 1); + clock_enable(CCGR_SDMA2, 1); + + return 0; +} + +u32 imx_get_eqos_csr_clk(void) +{ + return get_root_clk(ENET_AXI_CLK_ROOT); +} +#endif + +#ifdef CONFIG_FEC_MXC +int set_clk_enet(enum enet_freq type) +{ + u32 target; + u32 enet1_ref; + + switch (type) { + case ENET_125MHZ: + enet1_ref = ENET1_REF_CLK_ROOT_FROM_PLL_ENET_MAIN_125M_CLK; + break; + case ENET_50MHZ: + enet1_ref = ENET1_REF_CLK_ROOT_FROM_PLL_ENET_MAIN_50M_CLK; + break; + case ENET_25MHZ: + enet1_ref = ENET1_REF_CLK_ROOT_FROM_PLL_ENET_MAIN_25M_CLK; + break; + default: + return -EINVAL; + } + + /* disable the clock first */ + clock_enable(CCGR_ENET1, 0); + clock_enable(CCGR_SIM_ENET, 0); + + /* set enet axi clock 266Mhz */ + target = CLK_ROOT_ON | ENET_AXI_CLK_ROOT_FROM_SYS1_PLL_266M | + CLK_ROOT_PRE_DIV(CLK_ROOT_PRE_DIV1) | + CLK_ROOT_POST_DIV(CLK_ROOT_POST_DIV1); + clock_set_target_val(ENET_AXI_CLK_ROOT, target); + + target = CLK_ROOT_ON | enet1_ref | + CLK_ROOT_PRE_DIV(CLK_ROOT_PRE_DIV1) | + CLK_ROOT_POST_DIV(CLK_ROOT_POST_DIV1); + clock_set_target_val(ENET_REF_CLK_ROOT, target); + + target = CLK_ROOT_ON | + ENET1_TIME_CLK_ROOT_FROM_PLL_ENET_MAIN_100M_CLK | + CLK_ROOT_PRE_DIV(CLK_ROOT_PRE_DIV1) | + CLK_ROOT_POST_DIV(CLK_ROOT_POST_DIV4); + clock_set_target_val(ENET_TIMER_CLK_ROOT, target); + + /* enable clock */ + clock_enable(CCGR_SIM_ENET, 1); + clock_enable(CCGR_ENET1, 1); + + return 0; +} +#endif diff --git a/roms/u-boot/arch/arm/mach-imx/imx8m/clock_imx8mq.c b/roms/u-boot/arch/arm/mach-imx/imx8m/clock_imx8mq.c new file mode 100644 index 000000000..8fecc60ec --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/imx8m/clock_imx8mq.c @@ -0,0 +1,828 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2017 NXP + * + * Peng Fan <peng.fan@nxp.com> + */ + +#include <common.h> +#include <command.h> +#include <asm/arch/clock.h> +#include <asm/arch/imx-regs.h> +#include <asm/io.h> +#include <asm/arch/sys_proto.h> +#include <errno.h> +#include <linux/delay.h> +#include <linux/iopoll.h> + +static struct anamix_pll *ana_pll = (struct anamix_pll *)ANATOP_BASE_ADDR; + +static u32 get_root_clk(enum clk_root_index clock_id); + +static u32 decode_frac_pll(enum clk_root_src frac_pll) +{ + u32 pll_cfg0, pll_cfg1, pllout; + u32 pll_refclk_sel, pll_refclk; + u32 divr_val, divq_val, divf_val, divff, divfi; + u32 pllout_div_shift, pllout_div_mask, pllout_div; + + switch (frac_pll) { + case ARM_PLL_CLK: + pll_cfg0 = readl(&ana_pll->arm_pll_cfg0); + pll_cfg1 = readl(&ana_pll->arm_pll_cfg1); + pllout_div_shift = HW_FRAC_ARM_PLL_DIV_SHIFT; + pllout_div_mask = HW_FRAC_ARM_PLL_DIV_MASK; + break; + default: + printf("Frac PLL %d not supporte\n", frac_pll); + return 0; + } + + pllout_div = readl(&ana_pll->frac_pllout_div_cfg); + pllout_div = (pllout_div & pllout_div_mask) >> pllout_div_shift; + + /* Power down */ + if (pll_cfg0 & FRAC_PLL_PD_MASK) + return 0; + + /* output not enabled */ + if ((pll_cfg0 & FRAC_PLL_CLKE_MASK) == 0) + return 0; + + pll_refclk_sel = pll_cfg0 & FRAC_PLL_REFCLK_SEL_MASK; + + if (pll_refclk_sel == FRAC_PLL_REFCLK_SEL_OSC_25M) + pll_refclk = 25000000u; + else if (pll_refclk_sel == FRAC_PLL_REFCLK_SEL_OSC_27M) + pll_refclk = 27000000u; + else if (pll_refclk_sel == FRAC_PLL_REFCLK_SEL_HDMI_PHY_27M) + pll_refclk = 27000000u; + else + pll_refclk = 0; + + if (pll_cfg0 & FRAC_PLL_BYPASS_MASK) + return pll_refclk; + + divr_val = (pll_cfg0 & FRAC_PLL_REFCLK_DIV_VAL_MASK) >> + FRAC_PLL_REFCLK_DIV_VAL_SHIFT; + divq_val = pll_cfg0 & FRAC_PLL_OUTPUT_DIV_VAL_MASK; + + divff = (pll_cfg1 & FRAC_PLL_FRAC_DIV_CTL_MASK) >> + FRAC_PLL_FRAC_DIV_CTL_SHIFT; + divfi = pll_cfg1 & FRAC_PLL_INT_DIV_CTL_MASK; + + divf_val = 1 + divfi + divff / (1 << 24); + + pllout = pll_refclk / (divr_val + 1) * 8 * divf_val / + ((divq_val + 1) * 2); + + return pllout / (pllout_div + 1); +} + +static u32 decode_sscg_pll(enum clk_root_src sscg_pll) +{ + u32 pll_cfg0, pll_cfg1, pll_cfg2; + u32 pll_refclk_sel, pll_refclk; + u32 divr1, divr2, divf1, divf2, divq, div; + u32 sse; + u32 pll_clke; + u32 pllout_div_shift, pllout_div_mask, pllout_div; + u32 pllout; + + switch (sscg_pll) { + case SYSTEM_PLL1_800M_CLK: + case SYSTEM_PLL1_400M_CLK: + case SYSTEM_PLL1_266M_CLK: + case SYSTEM_PLL1_200M_CLK: + case SYSTEM_PLL1_160M_CLK: + case SYSTEM_PLL1_133M_CLK: + case SYSTEM_PLL1_100M_CLK: + case SYSTEM_PLL1_80M_CLK: + case SYSTEM_PLL1_40M_CLK: + pll_cfg0 = readl(&ana_pll->sys_pll1_cfg0); + pll_cfg1 = readl(&ana_pll->sys_pll1_cfg1); + pll_cfg2 = readl(&ana_pll->sys_pll1_cfg2); + pllout_div_shift = HW_SSCG_SYSTEM_PLL1_DIV_SHIFT; + pllout_div_mask = HW_SSCG_SYSTEM_PLL1_DIV_MASK; + break; + case SYSTEM_PLL2_1000M_CLK: + case SYSTEM_PLL2_500M_CLK: + case SYSTEM_PLL2_333M_CLK: + case SYSTEM_PLL2_250M_CLK: + case SYSTEM_PLL2_200M_CLK: + case SYSTEM_PLL2_166M_CLK: + case SYSTEM_PLL2_125M_CLK: + case SYSTEM_PLL2_100M_CLK: + case SYSTEM_PLL2_50M_CLK: + pll_cfg0 = readl(&ana_pll->sys_pll2_cfg0); + pll_cfg1 = readl(&ana_pll->sys_pll2_cfg1); + pll_cfg2 = readl(&ana_pll->sys_pll2_cfg2); + pllout_div_shift = HW_SSCG_SYSTEM_PLL2_DIV_SHIFT; + pllout_div_mask = HW_SSCG_SYSTEM_PLL2_DIV_MASK; + break; + case SYSTEM_PLL3_CLK: + pll_cfg0 = readl(&ana_pll->sys_pll3_cfg0); + pll_cfg1 = readl(&ana_pll->sys_pll3_cfg1); + pll_cfg2 = readl(&ana_pll->sys_pll3_cfg2); + pllout_div_shift = HW_SSCG_SYSTEM_PLL3_DIV_SHIFT; + pllout_div_mask = HW_SSCG_SYSTEM_PLL3_DIV_MASK; + break; + case DRAM_PLL1_CLK: + pll_cfg0 = readl(&ana_pll->dram_pll_cfg0); + pll_cfg1 = readl(&ana_pll->dram_pll_cfg1); + pll_cfg2 = readl(&ana_pll->dram_pll_cfg2); + pllout_div_shift = HW_SSCG_DRAM_PLL_DIV_SHIFT; + pllout_div_mask = HW_SSCG_DRAM_PLL_DIV_MASK; + break; + default: + printf("sscg pll %d not supporte\n", sscg_pll); + return 0; + } + + switch (sscg_pll) { + case DRAM_PLL1_CLK: + pll_clke = SSCG_PLL_DRAM_PLL_CLKE_MASK; + div = 1; + break; + case SYSTEM_PLL3_CLK: + pll_clke = SSCG_PLL_PLL3_CLKE_MASK; + div = 1; + break; + case SYSTEM_PLL2_1000M_CLK: + case SYSTEM_PLL1_800M_CLK: + pll_clke = SSCG_PLL_CLKE_MASK; + div = 1; + break; + case SYSTEM_PLL2_500M_CLK: + case SYSTEM_PLL1_400M_CLK: + pll_clke = SSCG_PLL_DIV2_CLKE_MASK; + div = 2; + break; + case SYSTEM_PLL2_333M_CLK: + case SYSTEM_PLL1_266M_CLK: + pll_clke = SSCG_PLL_DIV3_CLKE_MASK; + div = 3; + break; + case SYSTEM_PLL2_250M_CLK: + case SYSTEM_PLL1_200M_CLK: + pll_clke = SSCG_PLL_DIV4_CLKE_MASK; + div = 4; + break; + case SYSTEM_PLL2_200M_CLK: + case SYSTEM_PLL1_160M_CLK: + pll_clke = SSCG_PLL_DIV5_CLKE_MASK; + div = 5; + break; + case SYSTEM_PLL2_166M_CLK: + case SYSTEM_PLL1_133M_CLK: + pll_clke = SSCG_PLL_DIV6_CLKE_MASK; + div = 6; + break; + case SYSTEM_PLL2_125M_CLK: + case SYSTEM_PLL1_100M_CLK: + pll_clke = SSCG_PLL_DIV8_CLKE_MASK; + div = 8; + break; + case SYSTEM_PLL2_100M_CLK: + case SYSTEM_PLL1_80M_CLK: + pll_clke = SSCG_PLL_DIV10_CLKE_MASK; + div = 10; + break; + case SYSTEM_PLL2_50M_CLK: + case SYSTEM_PLL1_40M_CLK: + pll_clke = SSCG_PLL_DIV20_CLKE_MASK; + div = 20; + break; + default: + printf("sscg pll %d not supporte\n", sscg_pll); + return 0; + } + + /* Power down */ + if (pll_cfg0 & SSCG_PLL_PD_MASK) + return 0; + + /* output not enabled */ + if ((pll_cfg0 & pll_clke) == 0) + return 0; + + pllout_div = readl(&ana_pll->sscg_pllout_div_cfg); + pllout_div = (pllout_div & pllout_div_mask) >> pllout_div_shift; + + pll_refclk_sel = pll_cfg0 & SSCG_PLL_REFCLK_SEL_MASK; + + if (pll_refclk_sel == SSCG_PLL_REFCLK_SEL_OSC_25M) + pll_refclk = 25000000u; + else if (pll_refclk_sel == SSCG_PLL_REFCLK_SEL_OSC_27M) + pll_refclk = 27000000u; + else if (pll_refclk_sel == SSCG_PLL_REFCLK_SEL_HDMI_PHY_27M) + pll_refclk = 27000000u; + else + pll_refclk = 0; + + /* We assume bypass1/2 are the same value */ + if ((pll_cfg0 & SSCG_PLL_BYPASS1_MASK) || + (pll_cfg0 & SSCG_PLL_BYPASS2_MASK)) + return pll_refclk; + + divr1 = (pll_cfg2 & SSCG_PLL_REF_DIVR1_MASK) >> + SSCG_PLL_REF_DIVR1_SHIFT; + divr2 = (pll_cfg2 & SSCG_PLL_REF_DIVR2_MASK) >> + SSCG_PLL_REF_DIVR2_SHIFT; + divf1 = (pll_cfg2 & SSCG_PLL_FEEDBACK_DIV_F1_MASK) >> + SSCG_PLL_FEEDBACK_DIV_F1_SHIFT; + divf2 = (pll_cfg2 & SSCG_PLL_FEEDBACK_DIV_F2_MASK) >> + SSCG_PLL_FEEDBACK_DIV_F2_SHIFT; + divq = (pll_cfg2 & SSCG_PLL_OUTPUT_DIV_VAL_MASK) >> + SSCG_PLL_OUTPUT_DIV_VAL_SHIFT; + sse = pll_cfg1 & SSCG_PLL_SSE_MASK; + + if (sse) + sse = 8; + else + sse = 2; + + pllout = pll_refclk / (divr1 + 1) * sse * (divf1 + 1) / + (divr2 + 1) * (divf2 + 1) / (divq + 1); + + return pllout / (pllout_div + 1) / div; +} + +static u32 get_root_src_clk(enum clk_root_src root_src) +{ + switch (root_src) { + case OSC_25M_CLK: + return 25000000; + case OSC_27M_CLK: + return 27000000; + case OSC_32K_CLK: + return 32768; + case ARM_PLL_CLK: + return decode_frac_pll(root_src); + case SYSTEM_PLL1_800M_CLK: + case SYSTEM_PLL1_400M_CLK: + case SYSTEM_PLL1_266M_CLK: + case SYSTEM_PLL1_200M_CLK: + case SYSTEM_PLL1_160M_CLK: + case SYSTEM_PLL1_133M_CLK: + case SYSTEM_PLL1_100M_CLK: + case SYSTEM_PLL1_80M_CLK: + case SYSTEM_PLL1_40M_CLK: + case SYSTEM_PLL2_1000M_CLK: + case SYSTEM_PLL2_500M_CLK: + case SYSTEM_PLL2_333M_CLK: + case SYSTEM_PLL2_250M_CLK: + case SYSTEM_PLL2_200M_CLK: + case SYSTEM_PLL2_166M_CLK: + case SYSTEM_PLL2_125M_CLK: + case SYSTEM_PLL2_100M_CLK: + case SYSTEM_PLL2_50M_CLK: + case SYSTEM_PLL3_CLK: + return decode_sscg_pll(root_src); + case ARM_A53_ALT_CLK: + return get_root_clk(ARM_A53_CLK_ROOT); + default: + return 0; + } + + return 0; +} + +static u32 get_root_clk(enum clk_root_index clock_id) +{ + enum clk_root_src root_src; + u32 post_podf, pre_podf, root_src_clk; + + if (clock_root_enabled(clock_id) <= 0) + return 0; + + if (clock_get_prediv(clock_id, &pre_podf) < 0) + return 0; + + if (clock_get_postdiv(clock_id, &post_podf) < 0) + return 0; + + if (clock_get_src(clock_id, &root_src) < 0) + return 0; + + root_src_clk = get_root_src_clk(root_src); + + return root_src_clk / (post_podf + 1) / (pre_podf + 1); +} + +#ifdef CONFIG_IMX_HAB +void hab_caam_clock_enable(unsigned char enable) +{ + /* The CAAM clock is always on for iMX8M */ +} +#endif + +#ifdef CONFIG_MXC_OCOTP +void enable_ocotp_clk(unsigned char enable) +{ + clock_enable(CCGR_OCOTP, !!enable); +} +#endif + +int enable_i2c_clk(unsigned char enable, unsigned int i2c_num) +{ + /* 0 - 3 is valid i2c num */ + if (i2c_num > 3) + return -EINVAL; + + clock_enable(CCGR_I2C1 + i2c_num, !!enable); + + return 0; +} + +u32 get_arm_core_clk(void) +{ + enum clk_root_src root_src; + u32 root_src_clk; + + if (clock_get_src(CORE_SEL_CFG, &root_src) < 0) + return 0; + + root_src_clk = get_root_src_clk(root_src); + + return root_src_clk; +} + +unsigned int mxc_get_clock(enum mxc_clock clk) +{ + u32 val; + + switch (clk) { + case MXC_ARM_CLK: + return get_arm_core_clk(); + case MXC_IPG_CLK: + clock_get_target_val(IPG_CLK_ROOT, &val); + val = val & 0x3; + return get_root_clk(AHB_CLK_ROOT) / (val + 1); + case MXC_ESDHC_CLK: + return get_root_clk(USDHC1_CLK_ROOT); + case MXC_ESDHC2_CLK: + return get_root_clk(USDHC2_CLK_ROOT); + default: + return get_root_clk(clk); + } +} + +u32 imx_get_uartclk(void) +{ + return mxc_get_clock(UART1_CLK_ROOT); +} + +void mxs_set_lcdclk(u32 base_addr, u32 freq) +{ + /* + * LCDIF_PIXEL_CLK: select 800MHz root clock, + * select pre divider 8, output is 100 MHz + */ + clock_set_target_val(LCDIF_PIXEL_CLK_ROOT, CLK_ROOT_ON | + CLK_ROOT_SOURCE_SEL(4) | + CLK_ROOT_PRE_DIV(CLK_ROOT_PRE_DIV8)); +} + +void init_wdog_clk(void) +{ + clock_enable(CCGR_WDOG1, 0); + clock_enable(CCGR_WDOG2, 0); + clock_enable(CCGR_WDOG3, 0); + clock_set_target_val(WDOG_CLK_ROOT, CLK_ROOT_ON | + CLK_ROOT_SOURCE_SEL(0)); + clock_set_target_val(WDOG_CLK_ROOT, CLK_ROOT_ON | + CLK_ROOT_SOURCE_SEL(0)); + clock_set_target_val(WDOG_CLK_ROOT, CLK_ROOT_ON | + CLK_ROOT_SOURCE_SEL(0)); + clock_enable(CCGR_WDOG1, 1); + clock_enable(CCGR_WDOG2, 1); + clock_enable(CCGR_WDOG3, 1); +} + + +void init_nand_clk(void) +{ + clock_enable(CCGR_RAWNAND, 0); + clock_set_target_val(NAND_CLK_ROOT, + CLK_ROOT_ON | CLK_ROOT_SOURCE_SEL(3) | + CLK_ROOT_POST_DIV(CLK_ROOT_POST_DIV4)); + clock_enable(CCGR_RAWNAND, 1); +} + +void init_uart_clk(u32 index) +{ + /* Set uart clock root 25M OSC */ + switch (index) { + case 0: + clock_enable(CCGR_UART1, 0); + clock_set_target_val(UART1_CLK_ROOT, CLK_ROOT_ON | + CLK_ROOT_SOURCE_SEL(0)); + clock_enable(CCGR_UART1, 1); + return; + case 1: + clock_enable(CCGR_UART2, 0); + clock_set_target_val(UART2_CLK_ROOT, CLK_ROOT_ON | + CLK_ROOT_SOURCE_SEL(0)); + clock_enable(CCGR_UART2, 1); + return; + case 2: + clock_enable(CCGR_UART3, 0); + clock_set_target_val(UART3_CLK_ROOT, CLK_ROOT_ON | + CLK_ROOT_SOURCE_SEL(0)); + clock_enable(CCGR_UART3, 1); + return; + case 3: + clock_enable(CCGR_UART4, 0); + clock_set_target_val(UART4_CLK_ROOT, CLK_ROOT_ON | + CLK_ROOT_SOURCE_SEL(0)); + clock_enable(CCGR_UART4, 1); + return; + default: + printf("Invalid uart index\n"); + return; + } +} + +void init_clk_usdhc(u32 index) +{ + /* + * set usdhc clock root + * sys pll1 400M + */ + switch (index) { + case 0: + clock_enable(CCGR_USDHC1, 0); + clock_set_target_val(USDHC1_CLK_ROOT, CLK_ROOT_ON | + CLK_ROOT_SOURCE_SEL(1)); + clock_enable(CCGR_USDHC1, 1); + return; + case 1: + clock_enable(CCGR_USDHC2, 0); + clock_set_target_val(USDHC2_CLK_ROOT, CLK_ROOT_ON | + CLK_ROOT_SOURCE_SEL(1)); + clock_enable(CCGR_USDHC2, 1); + return; + default: + printf("Invalid usdhc index\n"); + return; + } +} + +int set_clk_qspi(void) +{ + /* + * set qspi root + * sys pll1 100M + */ + clock_enable(CCGR_QSPI, 0); + clock_set_target_val(QSPI_CLK_ROOT, CLK_ROOT_ON | + CLK_ROOT_SOURCE_SEL(7)); + clock_enable(CCGR_QSPI, 1); + + return 0; +} + +#ifdef CONFIG_FEC_MXC +int set_clk_enet(enum enet_freq type) +{ + u32 target; + u32 enet1_ref; + + switch (type) { + case ENET_125MHZ: + enet1_ref = ENET1_REF_CLK_ROOT_FROM_PLL_ENET_MAIN_125M_CLK; + break; + case ENET_50MHZ: + enet1_ref = ENET1_REF_CLK_ROOT_FROM_PLL_ENET_MAIN_50M_CLK; + break; + case ENET_25MHZ: + enet1_ref = ENET1_REF_CLK_ROOT_FROM_PLL_ENET_MAIN_25M_CLK; + break; + default: + return -EINVAL; + } + + /* disable the clock first */ + clock_enable(CCGR_ENET1, 0); + clock_enable(CCGR_SIM_ENET, 0); + + /* set enet axi clock 266Mhz */ + target = CLK_ROOT_ON | ENET_AXI_CLK_ROOT_FROM_SYS1_PLL_266M | + CLK_ROOT_PRE_DIV(CLK_ROOT_PRE_DIV1) | + CLK_ROOT_POST_DIV(CLK_ROOT_POST_DIV1); + clock_set_target_val(ENET_AXI_CLK_ROOT, target); + + target = CLK_ROOT_ON | enet1_ref | + CLK_ROOT_PRE_DIV(CLK_ROOT_PRE_DIV1) | + CLK_ROOT_POST_DIV(CLK_ROOT_POST_DIV1); + clock_set_target_val(ENET_REF_CLK_ROOT, target); + + target = CLK_ROOT_ON | + ENET1_TIME_CLK_ROOT_FROM_PLL_ENET_MAIN_100M_CLK | + CLK_ROOT_PRE_DIV(CLK_ROOT_PRE_DIV1) | + CLK_ROOT_POST_DIV(CLK_ROOT_POST_DIV4); + clock_set_target_val(ENET_TIMER_CLK_ROOT, target); + + /* enable clock */ + clock_enable(CCGR_SIM_ENET, 1); + clock_enable(CCGR_ENET1, 1); + + return 0; +} +#endif + +u32 imx_get_fecclk(void) +{ + return get_root_clk(ENET_AXI_CLK_ROOT); +} + +static struct dram_bypass_clk_setting imx8mq_dram_bypass_tbl[] = { + DRAM_BYPASS_ROOT_CONFIG(MHZ(100), 2, CLK_ROOT_PRE_DIV1, 2, + CLK_ROOT_PRE_DIV2), + DRAM_BYPASS_ROOT_CONFIG(MHZ(250), 3, CLK_ROOT_PRE_DIV2, 2, + CLK_ROOT_PRE_DIV2), + DRAM_BYPASS_ROOT_CONFIG(MHZ(400), 1, CLK_ROOT_PRE_DIV2, 3, + CLK_ROOT_PRE_DIV2), +}; + +void dram_enable_bypass(ulong clk_val) +{ + int i; + struct dram_bypass_clk_setting *config; + + for (i = 0; i < ARRAY_SIZE(imx8mq_dram_bypass_tbl); i++) { + if (clk_val == imx8mq_dram_bypass_tbl[i].clk) + break; + } + + if (i == ARRAY_SIZE(imx8mq_dram_bypass_tbl)) { + printf("No matched freq table %lu\n", clk_val); + return; + } + + config = &imx8mq_dram_bypass_tbl[i]; + + clock_set_target_val(DRAM_ALT_CLK_ROOT, CLK_ROOT_ON | + CLK_ROOT_SOURCE_SEL(config->alt_root_sel) | + CLK_ROOT_PRE_DIV(config->alt_pre_div)); + clock_set_target_val(DRAM_APB_CLK_ROOT, CLK_ROOT_ON | + CLK_ROOT_SOURCE_SEL(config->apb_root_sel) | + CLK_ROOT_PRE_DIV(config->apb_pre_div)); + clock_set_target_val(DRAM_SEL_CFG, CLK_ROOT_ON | + CLK_ROOT_SOURCE_SEL(1)); +} + +void dram_disable_bypass(void) +{ + clock_set_target_val(DRAM_SEL_CFG, CLK_ROOT_ON | + CLK_ROOT_SOURCE_SEL(0)); + clock_set_target_val(DRAM_APB_CLK_ROOT, CLK_ROOT_ON | + CLK_ROOT_SOURCE_SEL(4) | + CLK_ROOT_PRE_DIV(CLK_ROOT_PRE_DIV5)); +} + +#ifdef CONFIG_SPL_BUILD +void dram_pll_init(ulong pll_val) +{ + u32 val; + void __iomem *pll_control_reg = &ana_pll->dram_pll_cfg0; + void __iomem *pll_cfg_reg2 = &ana_pll->dram_pll_cfg2; + + /* Bypass */ + setbits_le32(pll_control_reg, SSCG_PLL_BYPASS1_MASK); + setbits_le32(pll_control_reg, SSCG_PLL_BYPASS2_MASK); + + switch (pll_val) { + case MHZ(800): + val = readl(pll_cfg_reg2); + val &= ~(SSCG_PLL_OUTPUT_DIV_VAL_MASK | + SSCG_PLL_FEEDBACK_DIV_F2_MASK | + SSCG_PLL_FEEDBACK_DIV_F1_MASK | + SSCG_PLL_REF_DIVR2_MASK); + val |= SSCG_PLL_OUTPUT_DIV_VAL(0); + val |= SSCG_PLL_FEEDBACK_DIV_F2_VAL(11); + val |= SSCG_PLL_FEEDBACK_DIV_F1_VAL(39); + val |= SSCG_PLL_REF_DIVR2_VAL(29); + writel(val, pll_cfg_reg2); + break; + case MHZ(600): + val = readl(pll_cfg_reg2); + val &= ~(SSCG_PLL_OUTPUT_DIV_VAL_MASK | + SSCG_PLL_FEEDBACK_DIV_F2_MASK | + SSCG_PLL_FEEDBACK_DIV_F1_MASK | + SSCG_PLL_REF_DIVR2_MASK); + val |= SSCG_PLL_OUTPUT_DIV_VAL(1); + val |= SSCG_PLL_FEEDBACK_DIV_F2_VAL(17); + val |= SSCG_PLL_FEEDBACK_DIV_F1_VAL(39); + val |= SSCG_PLL_REF_DIVR2_VAL(29); + writel(val, pll_cfg_reg2); + break; + case MHZ(400): + val = readl(pll_cfg_reg2); + val &= ~(SSCG_PLL_OUTPUT_DIV_VAL_MASK | + SSCG_PLL_FEEDBACK_DIV_F2_MASK | + SSCG_PLL_FEEDBACK_DIV_F1_MASK | + SSCG_PLL_REF_DIVR2_MASK); + val |= SSCG_PLL_OUTPUT_DIV_VAL(1); + val |= SSCG_PLL_FEEDBACK_DIV_F2_VAL(11); + val |= SSCG_PLL_FEEDBACK_DIV_F1_VAL(39); + val |= SSCG_PLL_REF_DIVR2_VAL(29); + writel(val, pll_cfg_reg2); + break; + case MHZ(167): + val = readl(pll_cfg_reg2); + val &= ~(SSCG_PLL_OUTPUT_DIV_VAL_MASK | + SSCG_PLL_FEEDBACK_DIV_F2_MASK | + SSCG_PLL_FEEDBACK_DIV_F1_MASK | + SSCG_PLL_REF_DIVR2_MASK); + val |= SSCG_PLL_OUTPUT_DIV_VAL(3); + val |= SSCG_PLL_FEEDBACK_DIV_F2_VAL(8); + val |= SSCG_PLL_FEEDBACK_DIV_F1_VAL(45); + val |= SSCG_PLL_REF_DIVR2_VAL(30); + writel(val, pll_cfg_reg2); + break; + default: + break; + } + + /* Clear power down bit */ + clrbits_le32(pll_control_reg, SSCG_PLL_PD_MASK); + /* Eanble ARM_PLL/SYS_PLL */ + setbits_le32(pll_control_reg, SSCG_PLL_DRAM_PLL_CLKE_MASK); + + /* Clear bypass */ + clrbits_le32(pll_control_reg, SSCG_PLL_BYPASS1_MASK); + __udelay(100); + clrbits_le32(pll_control_reg, SSCG_PLL_BYPASS2_MASK); + /* Wait lock */ + while (!(readl(pll_control_reg) & SSCG_PLL_LOCK_MASK)) + ; +} + +static int frac_pll_init(u32 pll, enum frac_pll_out_val val) +{ + void __iomem *pll_cfg0, __iomem *pll_cfg1; + u32 val_cfg0, val_cfg1, divq; + int ret; + + switch (pll) { + case ANATOP_ARM_PLL: + pll_cfg0 = &ana_pll->arm_pll_cfg0; + pll_cfg1 = &ana_pll->arm_pll_cfg1; + + if (val == FRAC_PLL_OUT_1000M) { + val_cfg1 = FRAC_PLL_INT_DIV_CTL_VAL(49); + divq = 0; + } else { + val_cfg1 = FRAC_PLL_INT_DIV_CTL_VAL(79); + divq = 1; + } + val_cfg0 = FRAC_PLL_CLKE_MASK | FRAC_PLL_REFCLK_SEL_OSC_25M | + FRAC_PLL_LOCK_SEL_MASK | FRAC_PLL_NEWDIV_VAL_MASK | + FRAC_PLL_REFCLK_DIV_VAL(4) | + FRAC_PLL_OUTPUT_DIV_VAL(divq); + break; + default: + return -EINVAL; + } + + /* bypass the clock */ + setbits_le32(pll_cfg0, FRAC_PLL_BYPASS_MASK); + /* Set the value */ + writel(val_cfg1, pll_cfg1); + writel(val_cfg0 | FRAC_PLL_BYPASS_MASK, pll_cfg0); + val_cfg0 = readl(pll_cfg0); + /* unbypass the clock */ + clrbits_le32(pll_cfg0, FRAC_PLL_BYPASS_MASK); + ret = readl_poll_timeout(pll_cfg0, val_cfg0, + val_cfg0 & FRAC_PLL_LOCK_MASK, 1); + if (ret) + printf("%s timeout\n", __func__); + clrbits_le32(pll_cfg0, FRAC_PLL_NEWDIV_VAL_MASK); + + return 0; +} + + +int clock_init(void) +{ + u32 grade; + + clock_set_target_val(ARM_A53_CLK_ROOT, CLK_ROOT_ON | + CLK_ROOT_SOURCE_SEL(0)); + + /* + * 8MQ only supports two grades: consumer and industrial. + * We set ARM clock to 1Ghz for consumer, 800Mhz for industrial + */ + grade = get_cpu_temp_grade(NULL, NULL); + if (!grade) + frac_pll_init(ANATOP_ARM_PLL, FRAC_PLL_OUT_1000M); + else + frac_pll_init(ANATOP_ARM_PLL, FRAC_PLL_OUT_800M); + + /* Bypass CCM A53 ROOT, Switch to ARM PLL -> MUX-> CPU */ + clock_set_target_val(CORE_SEL_CFG, CLK_ROOT_SOURCE_SEL(1)); + + /* + * According to ANAMIX SPEC + * sys pll1 fixed at 800MHz + * sys pll2 fixed at 1GHz + * Here we only enable the outputs. + */ + setbits_le32(&ana_pll->sys_pll1_cfg0, SSCG_PLL_CLKE_MASK | + SSCG_PLL_DIV2_CLKE_MASK | SSCG_PLL_DIV3_CLKE_MASK | + SSCG_PLL_DIV4_CLKE_MASK | SSCG_PLL_DIV5_CLKE_MASK | + SSCG_PLL_DIV6_CLKE_MASK | SSCG_PLL_DIV8_CLKE_MASK | + SSCG_PLL_DIV10_CLKE_MASK | SSCG_PLL_DIV20_CLKE_MASK); + + setbits_le32(&ana_pll->sys_pll2_cfg0, SSCG_PLL_CLKE_MASK | + SSCG_PLL_DIV2_CLKE_MASK | SSCG_PLL_DIV3_CLKE_MASK | + SSCG_PLL_DIV4_CLKE_MASK | SSCG_PLL_DIV5_CLKE_MASK | + SSCG_PLL_DIV6_CLKE_MASK | SSCG_PLL_DIV8_CLKE_MASK | + SSCG_PLL_DIV10_CLKE_MASK | SSCG_PLL_DIV20_CLKE_MASK); + + clock_set_target_val(NAND_USDHC_BUS_CLK_ROOT, CLK_ROOT_ON | + CLK_ROOT_SOURCE_SEL(1)); + + init_wdog_clk(); + clock_enable(CCGR_TSENSOR, 1); + clock_enable(CCGR_OCOTP, 1); + + /* config GIC ROOT to sys_pll2_200m */ + clock_enable(CCGR_GIC, 0); + clock_set_target_val(GIC_CLK_ROOT, + CLK_ROOT_ON | CLK_ROOT_SOURCE_SEL(1)); + clock_enable(CCGR_GIC, 1); + + return 0; +} +#endif + +/* + * Dump some clockes. + */ +#ifndef CONFIG_SPL_BUILD +static int do_imx8m_showclocks(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + u32 freq; + + freq = decode_frac_pll(ARM_PLL_CLK); + printf("ARM_PLL %8d MHz\n", freq / 1000000); + freq = decode_sscg_pll(DRAM_PLL1_CLK); + printf("DRAM_PLL %8d MHz\n", freq / 1000000); + freq = decode_sscg_pll(SYSTEM_PLL1_800M_CLK); + printf("SYS_PLL1_800 %8d MHz\n", freq / 1000000); + freq = decode_sscg_pll(SYSTEM_PLL1_400M_CLK); + printf("SYS_PLL1_400 %8d MHz\n", freq / 1000000); + freq = decode_sscg_pll(SYSTEM_PLL1_266M_CLK); + printf("SYS_PLL1_266 %8d MHz\n", freq / 1000000); + freq = decode_sscg_pll(SYSTEM_PLL1_200M_CLK); + printf("SYS_PLL1_200 %8d MHz\n", freq / 1000000); + freq = decode_sscg_pll(SYSTEM_PLL1_160M_CLK); + printf("SYS_PLL1_160 %8d MHz\n", freq / 1000000); + freq = decode_sscg_pll(SYSTEM_PLL1_133M_CLK); + printf("SYS_PLL1_133 %8d MHz\n", freq / 1000000); + freq = decode_sscg_pll(SYSTEM_PLL1_100M_CLK); + printf("SYS_PLL1_100 %8d MHz\n", freq / 1000000); + freq = decode_sscg_pll(SYSTEM_PLL1_80M_CLK); + printf("SYS_PLL1_80 %8d MHz\n", freq / 1000000); + freq = decode_sscg_pll(SYSTEM_PLL1_40M_CLK); + printf("SYS_PLL1_40 %8d MHz\n", freq / 1000000); + freq = decode_sscg_pll(SYSTEM_PLL2_1000M_CLK); + printf("SYS_PLL2_1000 %8d MHz\n", freq / 1000000); + freq = decode_sscg_pll(SYSTEM_PLL2_500M_CLK); + printf("SYS_PLL2_500 %8d MHz\n", freq / 1000000); + freq = decode_sscg_pll(SYSTEM_PLL2_333M_CLK); + printf("SYS_PLL2_333 %8d MHz\n", freq / 1000000); + freq = decode_sscg_pll(SYSTEM_PLL2_250M_CLK); + printf("SYS_PLL2_250 %8d MHz\n", freq / 1000000); + freq = decode_sscg_pll(SYSTEM_PLL2_200M_CLK); + printf("SYS_PLL2_200 %8d MHz\n", freq / 1000000); + freq = decode_sscg_pll(SYSTEM_PLL2_166M_CLK); + printf("SYS_PLL2_166 %8d MHz\n", freq / 1000000); + freq = decode_sscg_pll(SYSTEM_PLL2_125M_CLK); + printf("SYS_PLL2_125 %8d MHz\n", freq / 1000000); + freq = decode_sscg_pll(SYSTEM_PLL2_100M_CLK); + printf("SYS_PLL2_100 %8d MHz\n", freq / 1000000); + freq = decode_sscg_pll(SYSTEM_PLL2_50M_CLK); + printf("SYS_PLL2_50 %8d MHz\n", freq / 1000000); + freq = decode_sscg_pll(SYSTEM_PLL3_CLK); + printf("SYS_PLL3 %8d MHz\n", freq / 1000000); + freq = mxc_get_clock(UART1_CLK_ROOT); + printf("UART1 %8d MHz\n", freq / 1000000); + freq = mxc_get_clock(USDHC1_CLK_ROOT); + printf("USDHC1 %8d MHz\n", freq / 1000000); + freq = mxc_get_clock(QSPI_CLK_ROOT); + printf("QSPI %8d MHz\n", freq / 1000000); + return 0; +} + +U_BOOT_CMD( + clocks, CONFIG_SYS_MAXARGS, 1, do_imx8m_showclocks, + "display clocks", + "" +); +#endif diff --git a/roms/u-boot/arch/arm/mach-imx/imx8m/clock_slice.c b/roms/u-boot/arch/arm/mach-imx/imx8m/clock_slice.c new file mode 100644 index 000000000..b5ed27a92 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/imx8m/clock_slice.c @@ -0,0 +1,1871 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2017 NXP + * + * Peng Fan <peng.fan@nxp.com> + */ + +#include <common.h> +#include <asm/arch/clock.h> +#include <asm/arch/imx-regs.h> +#include <asm/io.h> +#include <errno.h> + +static struct ccm_reg *ccm_reg = (struct ccm_reg *)CCM_BASE_ADDR; + +#ifdef CONFIG_IMX8MQ +static struct clk_root_map root_array[] = { + {ARM_A53_CLK_ROOT, CORE_CLOCK_SLICE, 0, + {OSC_25M_CLK, ARM_PLL_CLK, SYSTEM_PLL2_500M_CLK, + SYSTEM_PLL2_1000M_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL1_400M_CLK, AUDIO_PLL1_CLK, SYSTEM_PLL3_CLK} + }, + {ARM_M4_CLK_ROOT, CORE_CLOCK_SLICE, 1, + {OSC_25M_CLK, SYSTEM_PLL2_200M_CLK, SYSTEM_PLL2_250M_CLK, + SYSTEM_PLL1_266M_CLK, SYSTEM_PLL1_800M_CLK, + AUDIO_PLL1_CLK, VIDEO_PLL_CLK, SYSTEM_PLL3_CLK} + }, + {VPU_A53_CLK_ROOT, CORE_CLOCK_SLICE, 2, + {OSC_25M_CLK, ARM_PLL_CLK, SYSTEM_PLL2_500M_CLK, + SYSTEM_PLL2_1000M_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL1_400M_CLK, AUDIO_PLL1_CLK, VPU_PLL_CLK} + }, + {GPU_CORE_CLK_ROOT, CORE_CLOCK_SLICE, 3, + {OSC_25M_CLK, GPU_PLL_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL3_CLK, SYSTEM_PLL2_1000M_CLK, + AUDIO_PLL1_CLK, VIDEO_PLL_CLK, AUDIO_PLL2_CLK} + }, + {GPU_SHADER_CLK_ROOT, CORE_CLOCK_SLICE, 4, + {OSC_25M_CLK, GPU_PLL_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL3_CLK, SYSTEM_PLL2_1000M_CLK, + AUDIO_PLL1_CLK, VIDEO_PLL_CLK, AUDIO_PLL2_CLK} + }, + {MAIN_AXI_CLK_ROOT, BUS_CLOCK_SLICE, 0, + {OSC_25M_CLK, SYSTEM_PLL2_333M_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL2_250M_CLK, SYSTEM_PLL2_1000M_CLK, + AUDIO_PLL1_CLK, VIDEO_PLL_CLK, SYSTEM_PLL1_100M_CLK} + }, + {ENET_AXI_CLK_ROOT, BUS_CLOCK_SLICE, 1, + {OSC_25M_CLK, SYSTEM_PLL1_266M_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL2_250M_CLK, SYSTEM_PLL2_200M_CLK, + AUDIO_PLL1_CLK, VIDEO_PLL_CLK, SYSTEM_PLL3_CLK} + }, + {NAND_USDHC_BUS_CLK_ROOT, BUS_CLOCK_SLICE, 2, + {OSC_25M_CLK, SYSTEM_PLL1_266M_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL2_200M_CLK, SYSTEM_PLL1_133M_CLK, + SYSTEM_PLL3_CLK, SYSTEM_PLL2_250M_CLK, AUDIO_PLL1_CLK} + }, + {VPU_BUS_CLK_ROOT, BUS_CLOCK_SLICE, 3, + {OSC_25M_CLK, SYSTEM_PLL1_800M_CLK, VPU_PLL_CLK, + AUDIO_PLL2_CLK, SYSTEM_PLL3_CLK, SYSTEM_PLL2_1000M_CLK, + SYSTEM_PLL2_200M_CLK, SYSTEM_PLL1_100M_CLK} + }, + {DISPLAY_AXI_CLK_ROOT, BUS_CLOCK_SLICE, 4, + {OSC_25M_CLK, SYSTEM_PLL2_125M_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL3_CLK, SYSTEM_PLL1_400M_CLK, AUDIO_PLL2_CLK, + EXT_CLK_1, EXT_CLK_4} + }, + {DISPLAY_APB_CLK_ROOT, BUS_CLOCK_SLICE, 5, + {OSC_25M_CLK, SYSTEM_PLL2_125M_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL3_CLK, SYSTEM_PLL1_400M_CLK, AUDIO_PLL2_CLK, + EXT_CLK_1, EXT_CLK_3} + }, + {DISPLAY_RTRM_CLK_ROOT, BUS_CLOCK_SLICE, 6, + {OSC_25M_CLK, SYSTEM_PLL1_800M_CLK, SYSTEM_PLL2_200M_CLK, + SYSTEM_PLL1_400M_CLK, AUDIO_PLL1_CLK, VIDEO_PLL_CLK, + EXT_CLK_2, EXT_CLK_3} + }, + {USB_BUS_CLK_ROOT, BUS_CLOCK_SLICE, 7, + {OSC_25M_CLK, SYSTEM_PLL2_500M_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL2_100M_CLK, SYSTEM_PLL2_200M_CLK, + EXT_CLK_2, EXT_CLK_4, AUDIO_PLL2_CLK} + }, + {GPU_AXI_CLK_ROOT, BUS_CLOCK_SLICE, 8, + {OSC_25M_CLK, SYSTEM_PLL1_800M_CLK, GPU_PLL_CLK, + SYSTEM_PLL3_CLK, SYSTEM_PLL2_1000M_CLK, + AUDIO_PLL1_CLK, VIDEO_PLL_CLK, AUDIO_PLL2_CLK} + }, + {GPU_AHB_CLK_ROOT, BUS_CLOCK_SLICE, 9, + {OSC_25M_CLK, SYSTEM_PLL1_800M_CLK, GPU_PLL_CLK, + SYSTEM_PLL3_CLK, SYSTEM_PLL2_1000M_CLK, + AUDIO_PLL1_CLK, VIDEO_PLL_CLK, AUDIO_PLL2_CLK} + }, + {NOC_CLK_ROOT, BUS_CLOCK_SLICE, 10, + {OSC_25M_CLK, SYSTEM_PLL1_800M_CLK, SYSTEM_PLL3_CLK, + SYSTEM_PLL2_1000M_CLK, SYSTEM_PLL2_500M_CLK, + AUDIO_PLL1_CLK, VIDEO_PLL_CLK, AUDIO_PLL2_CLK} + }, + {NOC_APB_CLK_ROOT, BUS_CLOCK_SLICE, 11, + {OSC_25M_CLK, SYSTEM_PLL1_400M_CLK, SYSTEM_PLL3_CLK, + SYSTEM_PLL2_333M_CLK, SYSTEM_PLL2_200M_CLK, + SYSTEM_PLL1_800M_CLK, AUDIO_PLL1_CLK, VIDEO_PLL_CLK} + }, + {AHB_CLK_ROOT, AHB_CLOCK_SLICE, 0, + {OSC_25M_CLK, SYSTEM_PLL1_133M_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL1_400M_CLK, SYSTEM_PLL2_125M_CLK, + SYSTEM_PLL3_CLK, AUDIO_PLL1_CLK, VIDEO_PLL_CLK} + }, + {IPG_CLK_ROOT, IPG_CLOCK_SLICE, 0, + {} + }, + {AUDIO_AHB_CLK_ROOT, AHB_CLOCK_SLICE, 1, + {OSC_25M_CLK, SYSTEM_PLL2_500M_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL2_1000M_CLK, SYSTEM_PLL2_166M_CLK, + SYSTEM_PLL3_CLK, AUDIO_PLL1_CLK, VIDEO_PLL_CLK} + }, + {MIPI_DSI_ESC_RX_CLK_ROOT, AHB_CLOCK_SLICE, 2, + {OSC_25M_CLK, SYSTEM_PLL2_100M_CLK, SYSTEM_PLL1_40M_CLK, + SYSTEM_PLL1_800M_CLK, SYSTEM_PLL2_1000M_CLK, + SYSTEM_PLL3_CLK, EXT_CLK_3, AUDIO_PLL1_CLK }, + }, + {DRAM_ALT_CLK_ROOT, IP_CLOCK_SLICE, 0, + {OSC_25M_CLK, SYSTEM_PLL1_800M_CLK, SYSTEM_PLL1_100M_CLK, + SYSTEM_PLL2_500M_CLK, SYSTEM_PLL2_250M_CLK, + SYSTEM_PLL1_400M_CLK, AUDIO_PLL1_CLK, SYSTEM_PLL1_266M_CLK} + }, + {DRAM_APB_CLK_ROOT, IP_CLOCK_SLICE, 1, + {OSC_25M_CLK, SYSTEM_PLL2_200M_CLK, SYSTEM_PLL1_40M_CLK, + SYSTEM_PLL1_160M_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL3_CLK, SYSTEM_PLL2_250M_CLK, AUDIO_PLL2_CLK} + }, + {VPU_G1_CLK_ROOT, IP_CLOCK_SLICE, 2, + {OSC_25M_CLK, VPU_PLL_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL2_1000M_CLK, SYSTEM_PLL1_100M_CLK, + SYSTEM_PLL2_125M_CLK, SYSTEM_PLL3_CLK, AUDIO_PLL1_CLK} + }, + {VPU_G2_CLK_ROOT, IP_CLOCK_SLICE, 3, + {OSC_25M_CLK, VPU_PLL_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL2_1000M_CLK, SYSTEM_PLL1_100M_CLK, + SYSTEM_PLL2_125M_CLK, SYSTEM_PLL3_CLK, AUDIO_PLL1_CLK} + }, + {DISPLAY_DTRC_CLK_ROOT, IP_CLOCK_SLICE, 4, + {OSC_25M_CLK, VPU_PLL_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL2_1000M_CLK, SYSTEM_PLL1_160M_CLK, + SYSTEM_PLL2_100M_CLK, SYSTEM_PLL3_CLK, AUDIO_PLL2_CLK} + }, + {DISPLAY_DC8000_CLK_ROOT, IP_CLOCK_SLICE, 5, + {OSC_25M_CLK, VPU_PLL_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL2_1000M_CLK, SYSTEM_PLL1_160M_CLK, + SYSTEM_PLL2_100M_CLK, SYSTEM_PLL3_CLK, AUDIO_PLL2_CLK} + }, + {PCIE1_CTRL_CLK_ROOT, IP_CLOCK_SLICE, 6, + {OSC_25M_CLK, SYSTEM_PLL2_250M_CLK, SYSTEM_PLL2_200M_CLK, + SYSTEM_PLL1_266M_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL2_500M_CLK, SYSTEM_PLL2_333M_CLK, SYSTEM_PLL3_CLK} + }, + {PCIE1_PHY_CLK_ROOT, IP_CLOCK_SLICE, 7, + {OSC_25M_CLK, SYSTEM_PLL2_100M_CLK, SYSTEM_PLL2_500M_CLK, + EXT_CLK_1, EXT_CLK_2, EXT_CLK_3, EXT_CLK_4, + SYSTEM_PLL1_400M_CLK} + }, + {PCIE1_AUX_CLK_ROOT, IP_CLOCK_SLICE, 8, + {OSC_25M_CLK, SYSTEM_PLL2_200M_CLK, SYSTEM_PLL2_50M_CLK, + SYSTEM_PLL3_CLK, SYSTEM_PLL2_100M_CLK, SYSTEM_PLL1_80M_CLK, + SYSTEM_PLL1_160M_CLK, SYSTEM_PLL1_200M_CLK} + }, + {DC_PIXEL_CLK_ROOT, IP_CLOCK_SLICE, 9, + {OSC_25M_CLK, VIDEO_PLL_CLK, AUDIO_PLL2_CLK, + AUDIO_PLL1_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL2_1000M_CLK, SYSTEM_PLL3_CLK, EXT_CLK_4} + }, + {LCDIF_PIXEL_CLK_ROOT, IP_CLOCK_SLICE, 10, + {OSC_25M_CLK, VIDEO_PLL_CLK, AUDIO_PLL2_CLK, + AUDIO_PLL1_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL2_1000M_CLK, SYSTEM_PLL3_CLK, EXT_CLK_4} + }, + {SAI1_CLK_ROOT, IP_CLOCK_SLICE, 11, + {OSC_25M_CLK, AUDIO_PLL1_CLK, AUDIO_PLL2_CLK, + VIDEO_PLL_CLK, SYSTEM_PLL1_133M_CLK, + OSC_27M_CLK, EXT_CLK_1, EXT_CLK_2} + }, + {SAI2_CLK_ROOT, IP_CLOCK_SLICE, 12, + {OSC_25M_CLK, AUDIO_PLL1_CLK, AUDIO_PLL2_CLK, + VIDEO_PLL_CLK, SYSTEM_PLL1_133M_CLK, + OSC_27M_CLK, EXT_CLK_2, EXT_CLK_3} + }, + {SAI3_CLK_ROOT, IP_CLOCK_SLICE, 13, + {OSC_25M_CLK, AUDIO_PLL1_CLK, AUDIO_PLL2_CLK, + VIDEO_PLL_CLK, SYSTEM_PLL1_133M_CLK, + OSC_27M_CLK, EXT_CLK_3, EXT_CLK_4} + }, + {SAI4_CLK_ROOT, IP_CLOCK_SLICE, 14, + {OSC_25M_CLK, AUDIO_PLL1_CLK, AUDIO_PLL2_CLK, + VIDEO_PLL_CLK, SYSTEM_PLL1_133M_CLK, + OSC_27M_CLK, EXT_CLK_1, EXT_CLK_2} + }, + {SAI5_CLK_ROOT, IP_CLOCK_SLICE, 15, + {OSC_25M_CLK, AUDIO_PLL1_CLK, AUDIO_PLL2_CLK, + VIDEO_PLL_CLK, SYSTEM_PLL1_133M_CLK, + OSC_27M_CLK, EXT_CLK_2, EXT_CLK_3} + }, + {SAI6_CLK_ROOT, IP_CLOCK_SLICE, 16, + {OSC_25M_CLK, AUDIO_PLL1_CLK, AUDIO_PLL2_CLK, + VIDEO_PLL_CLK, SYSTEM_PLL1_133M_CLK, + OSC_27M_CLK, EXT_CLK_3, EXT_CLK_4} + }, + {SPDIF1_CLK_ROOT, IP_CLOCK_SLICE, 17, + {OSC_25M_CLK, AUDIO_PLL1_CLK, AUDIO_PLL2_CLK, + VIDEO_PLL_CLK, SYSTEM_PLL1_133M_CLK, + OSC_27M_CLK, EXT_CLK_2, EXT_CLK_3} + }, + {SPDIF2_CLK_ROOT, IP_CLOCK_SLICE, 18, + {OSC_25M_CLK, AUDIO_PLL1_CLK, AUDIO_PLL2_CLK, + VIDEO_PLL_CLK, SYSTEM_PLL1_133M_CLK, + OSC_27M_CLK, EXT_CLK_3, EXT_CLK_4} + }, + {ENET_REF_CLK_ROOT, IP_CLOCK_SLICE, 19, + {OSC_25M_CLK, SYSTEM_PLL2_125M_CLK, SYSTEM_PLL2_50M_CLK, + SYSTEM_PLL2_100M_CLK, SYSTEM_PLL1_160M_CLK, + AUDIO_PLL1_CLK, VIDEO_PLL_CLK, EXT_CLK_4} + }, + {ENET_TIMER_CLK_ROOT, IP_CLOCK_SLICE, 20, + {OSC_25M_CLK, SYSTEM_PLL2_100M_CLK, AUDIO_PLL1_CLK, + EXT_CLK_1, EXT_CLK_2, EXT_CLK_3, EXT_CLK_4, + VIDEO_PLL_CLK} + }, + {ENET_PHY_REF_CLK_ROOT, IP_CLOCK_SLICE, 21, + {OSC_25M_CLK, SYSTEM_PLL2_50M_CLK, SYSTEM_PLL2_125M_CLK, + SYSTEM_PLL2_200M_CLK, SYSTEM_PLL2_500M_CLK, + AUDIO_PLL1_CLK, VIDEO_PLL_CLK, AUDIO_PLL2_CLK} + }, + {NAND_CLK_ROOT, IP_CLOCK_SLICE, 22, + {OSC_25M_CLK, SYSTEM_PLL2_500M_CLK, AUDIO_PLL1_CLK, + SYSTEM_PLL1_400M_CLK, AUDIO_PLL2_CLK, SYSTEM_PLL3_CLK, + SYSTEM_PLL2_250M_CLK, VIDEO_PLL_CLK} + }, + {QSPI_CLK_ROOT, IP_CLOCK_SLICE, 23, + {OSC_25M_CLK, SYSTEM_PLL1_400M_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL2_500M_CLK, AUDIO_PLL2_CLK, + SYSTEM_PLL1_266M_CLK, SYSTEM_PLL3_CLK, SYSTEM_PLL1_100M_CLK} + }, + {USDHC1_CLK_ROOT, IP_CLOCK_SLICE, 24, + {OSC_25M_CLK, SYSTEM_PLL1_400M_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL2_500M_CLK, AUDIO_PLL2_CLK, + SYSTEM_PLL1_266M_CLK, SYSTEM_PLL3_CLK, SYSTEM_PLL1_100M_CLK} + }, + {USDHC2_CLK_ROOT, IP_CLOCK_SLICE, 25, + {OSC_25M_CLK, SYSTEM_PLL1_400M_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL2_500M_CLK, AUDIO_PLL2_CLK, + SYSTEM_PLL1_266M_CLK, SYSTEM_PLL3_CLK, SYSTEM_PLL1_100M_CLK} + }, + {I2C1_CLK_ROOT, IP_CLOCK_SLICE, 26, + {OSC_25M_CLK, SYSTEM_PLL1_160M_CLK, SYSTEM_PLL2_50M_CLK, + SYSTEM_PLL3_CLK, AUDIO_PLL1_CLK, VIDEO_PLL_CLK, + AUDIO_PLL2_CLK, SYSTEM_PLL1_133M_CLK} + }, + {I2C2_CLK_ROOT, IP_CLOCK_SLICE, 27, + {OSC_25M_CLK, SYSTEM_PLL1_160M_CLK, SYSTEM_PLL2_50M_CLK, + SYSTEM_PLL3_CLK, AUDIO_PLL1_CLK, VIDEO_PLL_CLK, + AUDIO_PLL2_CLK, SYSTEM_PLL1_133M_CLK} + }, + {I2C3_CLK_ROOT, IP_CLOCK_SLICE, 28, + {OSC_25M_CLK, SYSTEM_PLL1_160M_CLK, SYSTEM_PLL2_50M_CLK, + SYSTEM_PLL3_CLK, AUDIO_PLL1_CLK, VIDEO_PLL_CLK, + AUDIO_PLL2_CLK, SYSTEM_PLL1_133M_CLK} + }, + {I2C4_CLK_ROOT, IP_CLOCK_SLICE, 29, + {OSC_25M_CLK, SYSTEM_PLL1_160M_CLK, SYSTEM_PLL2_50M_CLK, + SYSTEM_PLL3_CLK, AUDIO_PLL1_CLK, VIDEO_PLL_CLK, + AUDIO_PLL2_CLK, SYSTEM_PLL1_133M_CLK} + }, + {UART1_CLK_ROOT, IP_CLOCK_SLICE, 30, + {OSC_25M_CLK, SYSTEM_PLL1_80M_CLK, SYSTEM_PLL2_200M_CLK, + SYSTEM_PLL2_100M_CLK, SYSTEM_PLL3_CLK, + EXT_CLK_2, EXT_CLK_4, AUDIO_PLL2_CLK} + }, + {UART2_CLK_ROOT, IP_CLOCK_SLICE, 31, + {OSC_25M_CLK, SYSTEM_PLL1_80M_CLK, SYSTEM_PLL2_200M_CLK, + SYSTEM_PLL2_100M_CLK, SYSTEM_PLL3_CLK, + EXT_CLK_2, EXT_CLK_3, AUDIO_PLL2_CLK} + }, + {UART3_CLK_ROOT, IP_CLOCK_SLICE, 32, + {OSC_25M_CLK, SYSTEM_PLL1_80M_CLK, SYSTEM_PLL2_200M_CLK, + SYSTEM_PLL2_100M_CLK, SYSTEM_PLL3_CLK, + EXT_CLK_2, EXT_CLK_4, AUDIO_PLL2_CLK} + }, + {UART4_CLK_ROOT, IP_CLOCK_SLICE, 33, + {OSC_25M_CLK, SYSTEM_PLL1_80M_CLK, SYSTEM_PLL2_200M_CLK, + SYSTEM_PLL2_100M_CLK, SYSTEM_PLL3_CLK, + EXT_CLK_2, EXT_CLK_3, AUDIO_PLL2_CLK} + }, + {USB_CORE_REF_CLK_ROOT, IP_CLOCK_SLICE, 34, + {OSC_25M_CLK, SYSTEM_PLL1_100M_CLK, SYSTEM_PLL1_40M_CLK, + SYSTEM_PLL2_100M_CLK, SYSTEM_PLL2_200M_CLK, + EXT_CLK_2, EXT_CLK_3, AUDIO_PLL2_CLK} + }, + {USB_PHY_REF_CLK_ROOT, IP_CLOCK_SLICE, 35, + {OSC_25M_CLK, SYSTEM_PLL1_100M_CLK, SYSTEM_PLL1_40M_CLK, + SYSTEM_PLL2_100M_CLK, SYSTEM_PLL2_200M_CLK, + EXT_CLK_2, EXT_CLK_3, AUDIO_PLL2_CLK} + }, + {GIC_CLK_ROOT, IP_CLOCK_SLICE, 36, + {OSC_25M_CLK, SYSTEM_PLL2_200M_CLK, SYSTEM_PLL1_40M_CLK, + SYSTEM_PLL2_100M_CLK, SYSTEM_PLL1_800M_CLK, + EXT_CLK_2, EXT_CLK_4, AUDIO_PLL2_CLK} + }, + {ECSPI1_CLK_ROOT, IP_CLOCK_SLICE, 37, + {OSC_25M_CLK, SYSTEM_PLL2_200M_CLK, SYSTEM_PLL1_40M_CLK, + SYSTEM_PLL1_160M_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL3_CLK, SYSTEM_PLL2_250M_CLK, AUDIO_PLL2_CLK} + }, + {ECSPI2_CLK_ROOT, IP_CLOCK_SLICE, 38, + {OSC_25M_CLK, SYSTEM_PLL2_200M_CLK, SYSTEM_PLL1_40M_CLK, + SYSTEM_PLL1_160M_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL3_CLK, SYSTEM_PLL2_250M_CLK, AUDIO_PLL2_CLK} + }, + {PWM1_CLK_ROOT, IP_CLOCK_SLICE, 39, + {OSC_25M_CLK, SYSTEM_PLL2_100M_CLK, SYSTEM_PLL1_160M_CLK, + SYSTEM_PLL1_40M_CLK, SYSTEM_PLL3_CLK, EXT_CLK_1, + SYSTEM_PLL1_80M_CLK, VIDEO_PLL_CLK} + }, + {PWM2_CLK_ROOT, IP_CLOCK_SLICE, 40, + {OSC_25M_CLK, SYSTEM_PLL2_100M_CLK, SYSTEM_PLL1_160M_CLK, + SYSTEM_PLL1_40M_CLK, SYSTEM_PLL3_CLK, EXT_CLK_1, + SYSTEM_PLL1_80M_CLK, VIDEO_PLL_CLK} + }, + {PWM3_CLK_ROOT, IP_CLOCK_SLICE, 41, + {OSC_25M_CLK, SYSTEM_PLL2_100M_CLK, SYSTEM_PLL1_160M_CLK, + SYSTEM_PLL1_40M_CLK, SYSTEM_PLL3_CLK, EXT_CLK_1, + SYSTEM_PLL1_80M_CLK, VIDEO_PLL_CLK} + }, + {PWM4_CLK_ROOT, IP_CLOCK_SLICE, 42, + {OSC_25M_CLK, SYSTEM_PLL2_100M_CLK, SYSTEM_PLL1_160M_CLK, + SYSTEM_PLL1_40M_CLK, SYSTEM_PLL3_CLK, EXT_CLK_1, + SYSTEM_PLL1_80M_CLK, VIDEO_PLL_CLK} + }, + {GPT1_CLK_ROOT, IP_CLOCK_SLICE, 43, + {OSC_25M_CLK, SYSTEM_PLL2_100M_CLK, SYSTEM_PLL1_400M_CLK, + SYSTEM_PLL1_40M_CLK, VIDEO_PLL_CLK, + SYSTEM_PLL1_80M_CLK, AUDIO_PLL1_CLK, EXT_CLK_1} + }, + {GPT2_CLK_ROOT, IP_CLOCK_SLICE, 44, + {OSC_25M_CLK, SYSTEM_PLL2_100M_CLK, SYSTEM_PLL1_400M_CLK, + SYSTEM_PLL1_40M_CLK, VIDEO_PLL_CLK, + SYSTEM_PLL1_80M_CLK, AUDIO_PLL1_CLK, EXT_CLK_2} + }, + {GPT3_CLK_ROOT, IP_CLOCK_SLICE, 45, + {OSC_25M_CLK, SYSTEM_PLL2_100M_CLK, SYSTEM_PLL1_400M_CLK, + SYSTEM_PLL1_40M_CLK, VIDEO_PLL_CLK, + SYSTEM_PLL1_80M_CLK, AUDIO_PLL1_CLK, EXT_CLK_3} + }, + {GPT4_CLK_ROOT, IP_CLOCK_SLICE, 46, + {OSC_25M_CLK, SYSTEM_PLL2_100M_CLK, SYSTEM_PLL1_400M_CLK, + SYSTEM_PLL1_40M_CLK, VIDEO_PLL_CLK, + SYSTEM_PLL1_80M_CLK, AUDIO_PLL1_CLK, EXT_CLK_1} + }, + {GPT5_CLK_ROOT, IP_CLOCK_SLICE, 47, + {OSC_25M_CLK, SYSTEM_PLL2_100M_CLK, SYSTEM_PLL1_400M_CLK, + SYSTEM_PLL1_40M_CLK, VIDEO_PLL_CLK, + SYSTEM_PLL1_80M_CLK, AUDIO_PLL1_CLK, EXT_CLK_2} + }, + {GPT6_CLK_ROOT, IP_CLOCK_SLICE, 48, + {OSC_25M_CLK, SYSTEM_PLL2_100M_CLK, SYSTEM_PLL1_400M_CLK, + SYSTEM_PLL1_40M_CLK, VIDEO_PLL_CLK, + SYSTEM_PLL1_80M_CLK, AUDIO_PLL1_CLK, EXT_CLK_3} + }, + {TRACE_CLK_ROOT, IP_CLOCK_SLICE, 49, + {OSC_25M_CLK, SYSTEM_PLL1_133M_CLK, SYSTEM_PLL1_160M_CLK, + VPU_PLL_CLK, SYSTEM_PLL2_125M_CLK, + SYSTEM_PLL3_CLK, EXT_CLK_1, EXT_CLK_3} + }, + {WDOG_CLK_ROOT, IP_CLOCK_SLICE, 50, + {OSC_25M_CLK, SYSTEM_PLL1_133M_CLK, SYSTEM_PLL1_160M_CLK, + VPU_PLL_CLK, SYSTEM_PLL2_125M_CLK, + SYSTEM_PLL3_CLK, SYSTEM_PLL1_80M_CLK, SYSTEM_PLL2_166M_CLK} + }, + {WRCLK_CLK_ROOT, IP_CLOCK_SLICE, 51, + {OSC_25M_CLK, SYSTEM_PLL1_40M_CLK, VPU_PLL_CLK, + SYSTEM_PLL3_CLK, SYSTEM_PLL2_200M_CLK, + SYSTEM_PLL1_266M_CLK, SYSTEM_PLL2_500M_CLK, SYSTEM_PLL1_100M_CLK} + }, + {IPP_DO_CLKO1, IP_CLOCK_SLICE, 52, + {OSC_25M_CLK, SYSTEM_PLL1_800M_CLK, OSC_27M_CLK, + SYSTEM_PLL1_200M_CLK, AUDIO_PLL2_CLK, + SYSTEM_PLL2_500M_CLK, VPU_PLL_CLK, SYSTEM_PLL1_80M_CLK} + }, + {IPP_DO_CLKO2, IP_CLOCK_SLICE, 53, + {OSC_25M_CLK, SYSTEM_PLL2_200M_CLK, SYSTEM_PLL1_400M_CLK, + SYSTEM_PLL2_166M_CLK, SYSTEM_PLL3_CLK, + AUDIO_PLL1_CLK, VIDEO_PLL_CLK, OSC_32K_CLK} + }, + {MIPI_DSI_CORE_CLK_ROOT, IP_CLOCK_SLICE, 54, + {OSC_25M_CLK, SYSTEM_PLL1_266M_CLK, SYSTEM_PLL2_250M_CLK, + SYSTEM_PLL1_800M_CLK, SYSTEM_PLL2_1000M_CLK, + SYSTEM_PLL3_CLK, AUDIO_PLL2_CLK, VIDEO_PLL_CLK} + }, + {MIPI_DSI_PHY_REF_CLK_ROOT, IP_CLOCK_SLICE, 55, + {OSC_25M_CLK, SYSTEM_PLL2_125M_CLK, SYSTEM_PLL2_100M_CLK, + SYSTEM_PLL1_800M_CLK, SYSTEM_PLL2_1000M_CLK, + EXT_CLK_2, AUDIO_PLL2_CLK, VIDEO_PLL_CLK} + }, + {MIPI_DSI_DBI_CLK_ROOT, IP_CLOCK_SLICE, 56, + {OSC_25M_CLK, SYSTEM_PLL1_266M_CLK, SYSTEM_PLL2_100M_CLK, + SYSTEM_PLL1_800M_CLK, SYSTEM_PLL2_1000M_CLK, + SYSTEM_PLL3_CLK, AUDIO_PLL2_CLK, VIDEO_PLL_CLK} + }, + {OLD_MIPI_DSI_ESC_CLK_ROOT, IP_CLOCK_SLICE, 57, + {OSC_25M_CLK, SYSTEM_PLL2_100M_CLK, SYSTEM_PLL1_80M_CLK, + SYSTEM_PLL1_800M_CLK, SYSTEM_PLL2_1000M_CLK, + SYSTEM_PLL3_CLK, EXT_CLK_3, AUDIO_PLL2_CLK} + }, + {MIPI_CSI1_CORE_CLK_ROOT, IP_CLOCK_SLICE, 58, + {OSC_25M_CLK, SYSTEM_PLL1_266M_CLK, SYSTEM_PLL2_250M_CLK, + SYSTEM_PLL1_800M_CLK, SYSTEM_PLL2_1000M_CLK, + SYSTEM_PLL3_CLK, AUDIO_PLL2_CLK, VIDEO_PLL_CLK} + }, + {MIPI_CSI1_PHY_REF_CLK_ROOT, IP_CLOCK_SLICE, 59, + {OSC_25M_CLK, SYSTEM_PLL2_125M_CLK, SYSTEM_PLL2_100M_CLK, + SYSTEM_PLL1_800M_CLK, SYSTEM_PLL2_1000M_CLK, + EXT_CLK_2, AUDIO_PLL2_CLK, VIDEO_PLL_CLK} + }, + {MIPI_CSI1_ESC_CLK_ROOT, IP_CLOCK_SLICE, 60, + {OSC_25M_CLK, SYSTEM_PLL2_100M_CLK, SYSTEM_PLL1_80M_CLK, + SYSTEM_PLL1_800M_CLK, SYSTEM_PLL2_1000M_CLK, + SYSTEM_PLL3_CLK, EXT_CLK_3, AUDIO_PLL2_CLK} + }, + {MIPI_CSI2_CORE_CLK_ROOT, IP_CLOCK_SLICE, 61, + {OSC_25M_CLK, SYSTEM_PLL1_266M_CLK, SYSTEM_PLL2_250M_CLK, + SYSTEM_PLL1_800M_CLK, SYSTEM_PLL2_1000M_CLK, + SYSTEM_PLL3_CLK, AUDIO_PLL2_CLK, VIDEO_PLL_CLK} + }, + {MIPI_CSI2_PHY_REF_CLK_ROOT, IP_CLOCK_SLICE, 62, + {OSC_25M_CLK, SYSTEM_PLL2_125M_CLK, SYSTEM_PLL2_100M_CLK, + SYSTEM_PLL1_800M_CLK, SYSTEM_PLL2_1000M_CLK, + EXT_CLK_2, AUDIO_PLL2_CLK, VIDEO_PLL_CLK} + }, + {MIPI_CSI2_ESC_CLK_ROOT, IP_CLOCK_SLICE, 63, + {OSC_25M_CLK, SYSTEM_PLL2_100M_CLK, SYSTEM_PLL1_80M_CLK, + SYSTEM_PLL1_800M_CLK, SYSTEM_PLL2_1000M_CLK, + SYSTEM_PLL3_CLK, EXT_CLK_3, AUDIO_PLL2_CLK} + }, + {PCIE2_CTRL_CLK_ROOT, IP_CLOCK_SLICE, 64, + {OSC_25M_CLK, SYSTEM_PLL2_250M_CLK, SYSTEM_PLL2_200M_CLK, + SYSTEM_PLL1_266M_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL2_500M_CLK, SYSTEM_PLL2_333M_CLK, SYSTEM_PLL3_CLK} + }, + {PCIE2_PHY_CLK_ROOT, IP_CLOCK_SLICE, 65, + {OSC_25M_CLK, SYSTEM_PLL2_100M_CLK, SYSTEM_PLL2_500M_CLK, + EXT_CLK_1, EXT_CLK_2, EXT_CLK_3, + EXT_CLK_4, SYSTEM_PLL1_400M_CLK} + }, + {PCIE2_AUX_CLK_ROOT, IP_CLOCK_SLICE, 66, + {OSC_25M_CLK, SYSTEM_PLL2_200M_CLK, SYSTEM_PLL2_50M_CLK, + SYSTEM_PLL3_CLK, SYSTEM_PLL2_100M_CLK, + SYSTEM_PLL1_80M_CLK, SYSTEM_PLL1_160M_CLK, SYSTEM_PLL1_200M_CLK} + }, + {ECSPI3_CLK_ROOT, IP_CLOCK_SLICE, 67, + {OSC_25M_CLK, SYSTEM_PLL2_200M_CLK, SYSTEM_PLL1_40M_CLK, + SYSTEM_PLL1_160M_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL3_CLK, SYSTEM_PLL2_250M_CLK, AUDIO_PLL2_CLK} + }, + {OLD_MIPI_DSI_ESC_RX_ROOT, IP_CLOCK_SLICE, 68, + {OSC_25M_CLK, SYSTEM_PLL2_100M_CLK, SYSTEM_PLL1_80M_CLK, + SYSTEM_PLL1_800M_CLK, SYSTEM_PLL2_1000M_CLK, + SYSTEM_PLL3_CLK, EXT_CLK_3, AUDIO_PLL2_CLK}, + }, + {DISPLAY_HDMI_CLK_ROOT, IP_CLOCK_SLICE, 69, + {OSC_25M_CLK, SYSTEM_PLL1_200M_CLK, SYSTEM_PLL2_200M_CLK, + VPU_PLL_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL2_1000M_CLK, SYSTEM_PLL3_CLK, EXT_CLK_4} + }, + {DRAM_SEL_CFG, DRAM_SEL_CLOCK_SLICE, 0, + {DRAM_PLL1_CLK} + }, + {CORE_SEL_CFG, CORE_SEL_CLOCK_SLICE, 0, + {ARM_A53_ALT_CLK, ARM_PLL_CLK} + }, +}; +#elif defined(CONFIG_IMX8MM) +static struct clk_root_map root_array[] = { + {ARM_A53_CLK_ROOT, CORE_CLOCK_SLICE, 0, + {OSC_24M_CLK, ARM_PLL_CLK, SYSTEM_PLL2_500M_CLK, + SYSTEM_PLL2_1000M_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL1_400M_CLK, AUDIO_PLL1_CLK, SYSTEM_PLL3_CLK} + }, + {ARM_M4_CLK_ROOT, CORE_CLOCK_SLICE, 1, + {OSC_24M_CLK, SYSTEM_PLL2_200M_CLK, SYSTEM_PLL2_250M_CLK, + SYSTEM_PLL1_266M_CLK, SYSTEM_PLL1_800M_CLK, + AUDIO_PLL1_CLK, VIDEO_PLL_CLK, SYSTEM_PLL3_CLK} + }, + {VPU_A53_CLK_ROOT, CORE_CLOCK_SLICE, 2, + {OSC_24M_CLK, ARM_PLL_CLK, SYSTEM_PLL2_500M_CLK, + SYSTEM_PLL2_1000M_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL1_400M_CLK, AUDIO_PLL1_CLK, VPU_PLL_CLK} + }, + {GPU3D_CLK_ROOT, CORE_CLOCK_SLICE, 3, + {OSC_24M_CLK, GPU_PLL_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL3_CLK, SYSTEM_PLL2_1000M_CLK, + AUDIO_PLL1_CLK, VIDEO_PLL_CLK, AUDIO_PLL2_CLK} + }, + {GPU2D_CLK_ROOT, CORE_CLOCK_SLICE, 4, + {OSC_24M_CLK, GPU_PLL_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL3_CLK, SYSTEM_PLL2_1000M_CLK, + AUDIO_PLL1_CLK, VIDEO_PLL_CLK, AUDIO_PLL2_CLK} + }, + {MAIN_AXI_CLK_ROOT, BUS_CLOCK_SLICE, 0, + {OSC_24M_CLK, SYSTEM_PLL2_333M_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL2_250M_CLK, SYSTEM_PLL2_1000M_CLK, + AUDIO_PLL1_CLK, VIDEO_PLL_CLK, SYSTEM_PLL1_100M_CLK} + }, + {ENET_AXI_CLK_ROOT, BUS_CLOCK_SLICE, 1, + {OSC_24M_CLK, SYSTEM_PLL1_266M_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL2_250M_CLK, SYSTEM_PLL2_200M_CLK, + AUDIO_PLL1_CLK, VIDEO_PLL_CLK, SYSTEM_PLL3_CLK} + }, + {NAND_USDHC_BUS_CLK_ROOT, BUS_CLOCK_SLICE, 2, + {OSC_24M_CLK, SYSTEM_PLL1_266M_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL2_200M_CLK, SYSTEM_PLL1_133M_CLK, + SYSTEM_PLL3_CLK, SYSTEM_PLL2_250M_CLK, AUDIO_PLL1_CLK} + }, + {VPU_BUS_CLK_ROOT, BUS_CLOCK_SLICE, 3, + {OSC_24M_CLK, SYSTEM_PLL1_800M_CLK, VPU_PLL_CLK, + AUDIO_PLL2_CLK, SYSTEM_PLL3_CLK, SYSTEM_PLL2_1000M_CLK, + SYSTEM_PLL2_200M_CLK, SYSTEM_PLL1_100M_CLK} + }, + {DISPLAY_AXI_CLK_ROOT, BUS_CLOCK_SLICE, 4, + {OSC_24M_CLK, SYSTEM_PLL2_1000M_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL3_CLK, SYSTEM_PLL1_40M_CLK, AUDIO_PLL2_CLK, + EXT_CLK_1, EXT_CLK_4} + }, + {DISPLAY_APB_CLK_ROOT, BUS_CLOCK_SLICE, 5, + {OSC_24M_CLK, SYSTEM_PLL2_125M_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL3_CLK, SYSTEM_PLL1_40M_CLK, AUDIO_PLL2_CLK, + EXT_CLK_1, EXT_CLK_3} + }, + {DISPLAY_RTRM_CLK_ROOT, BUS_CLOCK_SLICE, 6, + {OSC_24M_CLK, SYSTEM_PLL1_800M_CLK, SYSTEM_PLL2_200M_CLK, + SYSTEM_PLL2_1000M_CLK, AUDIO_PLL1_CLK, VIDEO_PLL_CLK, + EXT_CLK_2, EXT_CLK_3} + }, + {USB_BUS_CLK_ROOT, BUS_CLOCK_SLICE, 7, + {OSC_24M_CLK, SYSTEM_PLL2_500M_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL2_100M_CLK, SYSTEM_PLL2_200M_CLK, + EXT_CLK_2, EXT_CLK_4, AUDIO_PLL2_CLK} + }, + {GPU_AXI_CLK_ROOT, BUS_CLOCK_SLICE, 8, + {OSC_24M_CLK, SYSTEM_PLL1_800M_CLK, GPU_PLL_CLK, + SYSTEM_PLL3_CLK, SYSTEM_PLL2_1000M_CLK, + AUDIO_PLL1_CLK, VIDEO_PLL_CLK, AUDIO_PLL2_CLK} + }, + {GPU_AHB_CLK_ROOT, BUS_CLOCK_SLICE, 9, + {OSC_24M_CLK, SYSTEM_PLL1_800M_CLK, GPU_PLL_CLK, + SYSTEM_PLL3_CLK, SYSTEM_PLL2_1000M_CLK, + AUDIO_PLL1_CLK, VIDEO_PLL_CLK, AUDIO_PLL2_CLK} + }, + {NOC_CLK_ROOT, BUS_CLOCK_SLICE, 10, + {OSC_24M_CLK, SYSTEM_PLL1_800M_CLK, SYSTEM_PLL3_CLK, + SYSTEM_PLL2_1000M_CLK, SYSTEM_PLL2_500M_CLK, + AUDIO_PLL1_CLK, VIDEO_PLL_CLK, AUDIO_PLL2_CLK} + }, + {NOC_APB_CLK_ROOT, BUS_CLOCK_SLICE, 11, + {OSC_24M_CLK, SYSTEM_PLL1_400M_CLK, SYSTEM_PLL3_CLK, + SYSTEM_PLL2_333M_CLK, SYSTEM_PLL2_200M_CLK, + SYSTEM_PLL1_800M_CLK, AUDIO_PLL1_CLK, VIDEO_PLL_CLK} + }, + {AHB_CLK_ROOT, AHB_CLOCK_SLICE, 0, + {OSC_24M_CLK, SYSTEM_PLL1_133M_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL1_400M_CLK, SYSTEM_PLL2_125M_CLK, + SYSTEM_PLL3_CLK, AUDIO_PLL1_CLK, VIDEO_PLL_CLK} + }, + {AUDIO_AHB_CLK_ROOT, AHB_CLOCK_SLICE, 1, + {OSC_24M_CLK, SYSTEM_PLL2_500M_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL2_1000M_CLK, SYSTEM_PLL2_166M_CLK, + SYSTEM_PLL3_CLK, AUDIO_PLL1_CLK, VIDEO_PLL_CLK} + }, + {MIPI_DSI_ESC_RX_CLK_ROOT, AHB_CLOCK_SLICE, 2, + {OSC_24M_CLK, SYSTEM_PLL2_100M_CLK, SYSTEM_PLL1_80M_CLK, + SYSTEM_PLL1_800M_CLK, SYSTEM_PLL2_1000M_CLK, + SYSTEM_PLL3_CLK, EXT_CLK_3, AUDIO_PLL2_CLK} + }, + {DRAM_ALT_CLK_ROOT, IP_CLOCK_SLICE, 0, + {OSC_24M_CLK, SYSTEM_PLL1_800M_CLK, SYSTEM_PLL1_100M_CLK, + SYSTEM_PLL2_500M_CLK, SYSTEM_PLL2_1000M_CLK, + SYSTEM_PLL3_CLK, AUDIO_PLL1_CLK, SYSTEM_PLL1_266M_CLK} + }, + {DRAM_APB_CLK_ROOT, IP_CLOCK_SLICE, 1, + {OSC_24M_CLK, SYSTEM_PLL2_200M_CLK, SYSTEM_PLL1_40M_CLK, + SYSTEM_PLL1_160M_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL3_CLK, SYSTEM_PLL2_250M_CLK, AUDIO_PLL2_CLK} + }, + {VPU_G1_CLK_ROOT, IP_CLOCK_SLICE, 2, + {OSC_24M_CLK, VPU_PLL_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL2_1000M_CLK, SYSTEM_PLL1_100M_CLK, + SYSTEM_PLL2_125M_CLK, SYSTEM_PLL3_CLK, AUDIO_PLL1_CLK} + }, + {VPU_G2_CLK_ROOT, IP_CLOCK_SLICE, 3, + {OSC_24M_CLK, VPU_PLL_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL2_1000M_CLK, SYSTEM_PLL1_100M_CLK, + SYSTEM_PLL2_125M_CLK, SYSTEM_PLL3_CLK, AUDIO_PLL1_CLK} + }, + {DISPLAY_DTRC_CLK_ROOT, IP_CLOCK_SLICE, 4, + {OSC_24M_CLK, VIDEO_PLL2_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL2_1000M_CLK, SYSTEM_PLL1_160M_CLK, + VIDEO_PLL_CLK, SYSTEM_PLL3_CLK, AUDIO_PLL2_CLK} + }, + {DISPLAY_DC8000_CLK_ROOT, IP_CLOCK_SLICE, 5, + {OSC_24M_CLK, VIDEO_PLL2_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL2_1000M_CLK, SYSTEM_PLL1_160M_CLK, + VIDEO_PLL_CLK, SYSTEM_PLL3_CLK, AUDIO_PLL2_CLK} + }, + {PCIE_CTRL_CLK_ROOT, IP_CLOCK_SLICE, 6, + {OSC_24M_CLK, SYSTEM_PLL2_250M_CLK, SYSTEM_PLL2_200M_CLK, + SYSTEM_PLL1_266M_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL2_500M_CLK, SYSTEM_PLL2_333M_CLK, SYSTEM_PLL3_CLK} + }, + {PCIE_PHY_CLK_ROOT, IP_CLOCK_SLICE, 7, + {OSC_24M_CLK, SYSTEM_PLL2_100M_CLK, SYSTEM_PLL2_500M_CLK, + EXT_CLK_1, EXT_CLK_2, EXT_CLK_3, EXT_CLK_4, + SYSTEM_PLL1_400M_CLK} + }, + {PCIE_AUX_CLK_ROOT, IP_CLOCK_SLICE, 8, + {OSC_24M_CLK, SYSTEM_PLL2_200M_CLK, SYSTEM_PLL2_50M_CLK, + SYSTEM_PLL3_CLK, SYSTEM_PLL2_100M_CLK, SYSTEM_PLL1_80M_CLK, + SYSTEM_PLL1_160M_CLK, SYSTEM_PLL1_200M_CLK} + }, + {DC_PIXEL_CLK_ROOT, IP_CLOCK_SLICE, 9, + {OSC_24M_CLK, VIDEO_PLL_CLK, AUDIO_PLL2_CLK, + AUDIO_PLL1_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL2_1000M_CLK, SYSTEM_PLL3_CLK, EXT_CLK_4} + }, + {LCDIF_PIXEL_CLK_ROOT, IP_CLOCK_SLICE, 10, + {OSC_24M_CLK, VIDEO_PLL_CLK, AUDIO_PLL2_CLK, + AUDIO_PLL1_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL2_1000M_CLK, SYSTEM_PLL3_CLK, EXT_CLK_4} + }, + {SAI1_CLK_ROOT, IP_CLOCK_SLICE, 11, + {OSC_24M_CLK, AUDIO_PLL1_CLK, AUDIO_PLL2_CLK, + VIDEO_PLL_CLK, SYSTEM_PLL1_133M_CLK, + OSC_HDMI_CLK, EXT_CLK_1, EXT_CLK_2} + }, + {SAI2_CLK_ROOT, IP_CLOCK_SLICE, 12, + {OSC_24M_CLK, AUDIO_PLL1_CLK, AUDIO_PLL2_CLK, + VIDEO_PLL_CLK, SYSTEM_PLL1_133M_CLK, + OSC_HDMI_CLK, EXT_CLK_2, EXT_CLK_3} + }, + {SAI3_CLK_ROOT, IP_CLOCK_SLICE, 13, + {OSC_24M_CLK, AUDIO_PLL1_CLK, AUDIO_PLL2_CLK, + VIDEO_PLL_CLK, SYSTEM_PLL1_133M_CLK, + OSC_HDMI_CLK, EXT_CLK_3, EXT_CLK_4} + }, + {SAI4_CLK_ROOT, IP_CLOCK_SLICE, 14, + {OSC_24M_CLK, AUDIO_PLL1_CLK, AUDIO_PLL2_CLK, + VIDEO_PLL_CLK, SYSTEM_PLL1_133M_CLK, + OSC_HDMI_CLK, EXT_CLK_1, EXT_CLK_2} + }, + {SAI5_CLK_ROOT, IP_CLOCK_SLICE, 15, + {OSC_24M_CLK, AUDIO_PLL1_CLK, AUDIO_PLL2_CLK, + VIDEO_PLL_CLK, SYSTEM_PLL1_133M_CLK, + OSC_HDMI_CLK, EXT_CLK_2, EXT_CLK_3} + }, + {SAI6_CLK_ROOT, IP_CLOCK_SLICE, 16, + {OSC_24M_CLK, AUDIO_PLL1_CLK, AUDIO_PLL2_CLK, + VIDEO_PLL_CLK, SYSTEM_PLL1_133M_CLK, + OSC_HDMI_CLK, EXT_CLK_3, EXT_CLK_4} + }, + {SPDIF1_CLK_ROOT, IP_CLOCK_SLICE, 17, + {OSC_24M_CLK, AUDIO_PLL1_CLK, AUDIO_PLL2_CLK, + VIDEO_PLL_CLK, SYSTEM_PLL1_133M_CLK, + OSC_HDMI_CLK, EXT_CLK_2, EXT_CLK_3} + }, + {SPDIF2_CLK_ROOT, IP_CLOCK_SLICE, 18, + {OSC_24M_CLK, AUDIO_PLL1_CLK, AUDIO_PLL2_CLK, + VIDEO_PLL_CLK, SYSTEM_PLL1_133M_CLK, + OSC_HDMI_CLK, EXT_CLK_3, EXT_CLK_4} + }, + {ENET_REF_CLK_ROOT, IP_CLOCK_SLICE, 19, + {OSC_24M_CLK, SYSTEM_PLL2_125M_CLK, SYSTEM_PLL2_50M_CLK, + SYSTEM_PLL2_100M_CLK, SYSTEM_PLL1_160M_CLK, + AUDIO_PLL1_CLK, VIDEO_PLL_CLK, EXT_CLK_4} + }, + {ENET_TIMER_CLK_ROOT, IP_CLOCK_SLICE, 20, + {OSC_24M_CLK, SYSTEM_PLL2_100M_CLK, AUDIO_PLL1_CLK, + EXT_CLK_1, EXT_CLK_2, EXT_CLK_3, EXT_CLK_4, + VIDEO_PLL_CLK} + }, + {ENET_PHY_REF_CLK_ROOT, IP_CLOCK_SLICE, 21, + {OSC_24M_CLK, SYSTEM_PLL2_50M_CLK, SYSTEM_PLL2_125M_CLK, + SYSTEM_PLL2_200M_CLK, SYSTEM_PLL2_500M_CLK, + AUDIO_PLL1_CLK, VIDEO_PLL_CLK, AUDIO_PLL2_CLK} + }, + {NAND_CLK_ROOT, IP_CLOCK_SLICE, 22, + {OSC_24M_CLK, SYSTEM_PLL2_500M_CLK, AUDIO_PLL1_CLK, + SYSTEM_PLL1_400M_CLK, AUDIO_PLL2_CLK, SYSTEM_PLL3_CLK, + SYSTEM_PLL2_250M_CLK, VIDEO_PLL_CLK} + }, + {QSPI_CLK_ROOT, IP_CLOCK_SLICE, 23, + {OSC_24M_CLK, SYSTEM_PLL1_400M_CLK, SYSTEM_PLL2_333M_CLK, + SYSTEM_PLL2_500M_CLK, AUDIO_PLL2_CLK, + SYSTEM_PLL1_266M_CLK, SYSTEM_PLL3_CLK, SYSTEM_PLL1_100M_CLK} + }, + {USDHC1_CLK_ROOT, IP_CLOCK_SLICE, 24, + {OSC_24M_CLK, SYSTEM_PLL1_400M_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL2_500M_CLK, SYSTEM_PLL3_CLK, + SYSTEM_PLL1_266M_CLK, AUDIO_PLL2_CLK, SYSTEM_PLL1_100M_CLK} + }, + {USDHC2_CLK_ROOT, IP_CLOCK_SLICE, 25, + {OSC_24M_CLK, SYSTEM_PLL1_400M_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL2_500M_CLK, SYSTEM_PLL3_CLK, + SYSTEM_PLL1_266M_CLK, AUDIO_PLL2_CLK, SYSTEM_PLL1_100M_CLK} + }, + {I2C1_CLK_ROOT, IP_CLOCK_SLICE, 26, + {OSC_24M_CLK, SYSTEM_PLL1_160M_CLK, SYSTEM_PLL2_50M_CLK, + SYSTEM_PLL3_CLK, AUDIO_PLL1_CLK, VIDEO_PLL_CLK, + AUDIO_PLL2_CLK, SYSTEM_PLL1_133M_CLK} + }, + {I2C2_CLK_ROOT, IP_CLOCK_SLICE, 27, + {OSC_24M_CLK, SYSTEM_PLL1_160M_CLK, SYSTEM_PLL2_50M_CLK, + SYSTEM_PLL3_CLK, AUDIO_PLL1_CLK, VIDEO_PLL_CLK, + AUDIO_PLL2_CLK, SYSTEM_PLL1_133M_CLK} + }, + {I2C3_CLK_ROOT, IP_CLOCK_SLICE, 28, + {OSC_24M_CLK, SYSTEM_PLL1_160M_CLK, SYSTEM_PLL2_50M_CLK, + SYSTEM_PLL3_CLK, AUDIO_PLL1_CLK, VIDEO_PLL_CLK, + AUDIO_PLL2_CLK, SYSTEM_PLL1_133M_CLK} + }, + {I2C4_CLK_ROOT, IP_CLOCK_SLICE, 29, + {OSC_24M_CLK, SYSTEM_PLL1_160M_CLK, SYSTEM_PLL2_50M_CLK, + SYSTEM_PLL3_CLK, AUDIO_PLL1_CLK, VIDEO_PLL_CLK, + AUDIO_PLL2_CLK, SYSTEM_PLL1_133M_CLK} + }, + {UART1_CLK_ROOT, IP_CLOCK_SLICE, 30, + {OSC_24M_CLK, SYSTEM_PLL1_80M_CLK, SYSTEM_PLL2_200M_CLK, + SYSTEM_PLL2_100M_CLK, SYSTEM_PLL3_CLK, + EXT_CLK_2, EXT_CLK_4, AUDIO_PLL2_CLK} + }, + {UART2_CLK_ROOT, IP_CLOCK_SLICE, 31, + {OSC_24M_CLK, SYSTEM_PLL1_80M_CLK, SYSTEM_PLL2_200M_CLK, + SYSTEM_PLL2_100M_CLK, SYSTEM_PLL3_CLK, + EXT_CLK_2, EXT_CLK_3, AUDIO_PLL2_CLK} + }, + {UART3_CLK_ROOT, IP_CLOCK_SLICE, 32, + {OSC_24M_CLK, SYSTEM_PLL1_80M_CLK, SYSTEM_PLL2_200M_CLK, + SYSTEM_PLL2_100M_CLK, SYSTEM_PLL3_CLK, + EXT_CLK_2, EXT_CLK_4, AUDIO_PLL2_CLK} + }, + {UART4_CLK_ROOT, IP_CLOCK_SLICE, 33, + {OSC_24M_CLK, SYSTEM_PLL1_80M_CLK, SYSTEM_PLL2_200M_CLK, + SYSTEM_PLL2_100M_CLK, SYSTEM_PLL3_CLK, + EXT_CLK_2, EXT_CLK_3, AUDIO_PLL2_CLK} + }, + {USB_CORE_REF_CLK_ROOT, IP_CLOCK_SLICE, 34, + {OSC_24M_CLK, SYSTEM_PLL1_100M_CLK, SYSTEM_PLL1_40M_CLK, + SYSTEM_PLL2_100M_CLK, SYSTEM_PLL2_200M_CLK, + EXT_CLK_2, EXT_CLK_3, AUDIO_PLL2_CLK} + }, + {USB_PHY_REF_CLK_ROOT, IP_CLOCK_SLICE, 35, + {OSC_24M_CLK, SYSTEM_PLL1_100M_CLK, SYSTEM_PLL1_40M_CLK, + SYSTEM_PLL2_100M_CLK, SYSTEM_PLL2_200M_CLK, + EXT_CLK_2, EXT_CLK_3, AUDIO_PLL2_CLK} + }, + {GIC_CLK_ROOT, IP_CLOCK_SLICE, 36, + {OSC_24M_CLK, SYSTEM_PLL2_200M_CLK, SYSTEM_PLL1_40M_CLK, + SYSTEM_PLL2_100M_CLK, SYSTEM_PLL1_800M_CLK, + EXT_CLK_2, EXT_CLK_4, AUDIO_PLL2_CLK} + }, + {ECSPI1_CLK_ROOT, IP_CLOCK_SLICE, 37, + {OSC_24M_CLK, SYSTEM_PLL2_200M_CLK, SYSTEM_PLL1_40M_CLK, + SYSTEM_PLL1_160M_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL3_CLK, SYSTEM_PLL2_250M_CLK, AUDIO_PLL2_CLK} + }, + {ECSPI2_CLK_ROOT, IP_CLOCK_SLICE, 38, + {OSC_24M_CLK, SYSTEM_PLL2_200M_CLK, SYSTEM_PLL1_40M_CLK, + SYSTEM_PLL1_160M_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL3_CLK, SYSTEM_PLL2_250M_CLK, AUDIO_PLL2_CLK} + }, + {PWM1_CLK_ROOT, IP_CLOCK_SLICE, 39, + {OSC_24M_CLK, SYSTEM_PLL2_100M_CLK, SYSTEM_PLL1_160M_CLK, + SYSTEM_PLL1_40M_CLK, SYSTEM_PLL3_CLK, EXT_CLK_1, + SYSTEM_PLL1_80M_CLK, VIDEO_PLL_CLK} + }, + {PWM2_CLK_ROOT, IP_CLOCK_SLICE, 40, + {OSC_24M_CLK, SYSTEM_PLL2_100M_CLK, SYSTEM_PLL1_160M_CLK, + SYSTEM_PLL1_40M_CLK, SYSTEM_PLL3_CLK, EXT_CLK_1, + SYSTEM_PLL1_80M_CLK, VIDEO_PLL_CLK} + }, + {PWM3_CLK_ROOT, IP_CLOCK_SLICE, 41, + {OSC_24M_CLK, SYSTEM_PLL2_100M_CLK, SYSTEM_PLL1_160M_CLK, + SYSTEM_PLL1_40M_CLK, SYSTEM_PLL3_CLK, EXT_CLK_2, + SYSTEM_PLL1_80M_CLK, VIDEO_PLL_CLK} + }, + {PWM4_CLK_ROOT, IP_CLOCK_SLICE, 42, + {OSC_24M_CLK, SYSTEM_PLL2_100M_CLK, SYSTEM_PLL1_160M_CLK, + SYSTEM_PLL1_40M_CLK, SYSTEM_PLL3_CLK, EXT_CLK_2, + SYSTEM_PLL1_80M_CLK, VIDEO_PLL_CLK} + }, + {GPT1_CLK_ROOT, IP_CLOCK_SLICE, 43, + {OSC_24M_CLK, SYSTEM_PLL2_100M_CLK, SYSTEM_PLL1_400M_CLK, + SYSTEM_PLL1_40M_CLK, VIDEO_PLL_CLK, + SYSTEM_PLL1_80M_CLK, AUDIO_PLL1_CLK, EXT_CLK_1} + }, + {GPT2_CLK_ROOT, IP_CLOCK_SLICE, 44, + {OSC_24M_CLK, SYSTEM_PLL2_100M_CLK, SYSTEM_PLL1_400M_CLK, + SYSTEM_PLL1_40M_CLK, VIDEO_PLL_CLK, + SYSTEM_PLL1_80M_CLK, AUDIO_PLL1_CLK, EXT_CLK_2} + }, + {GPT3_CLK_ROOT, IP_CLOCK_SLICE, 45, + {OSC_24M_CLK, SYSTEM_PLL2_100M_CLK, SYSTEM_PLL1_400M_CLK, + SYSTEM_PLL1_40M_CLK, VIDEO_PLL_CLK, + SYSTEM_PLL1_80M_CLK, AUDIO_PLL1_CLK, EXT_CLK_3} + }, + {GPT4_CLK_ROOT, IP_CLOCK_SLICE, 46, + {OSC_24M_CLK, SYSTEM_PLL2_100M_CLK, SYSTEM_PLL1_400M_CLK, + SYSTEM_PLL1_40M_CLK, VIDEO_PLL_CLK, + SYSTEM_PLL1_80M_CLK, AUDIO_PLL1_CLK, EXT_CLK_1} + }, + {GPT5_CLK_ROOT, IP_CLOCK_SLICE, 47, + {OSC_24M_CLK, SYSTEM_PLL2_100M_CLK, SYSTEM_PLL1_400M_CLK, + SYSTEM_PLL1_40M_CLK, VIDEO_PLL_CLK, + SYSTEM_PLL1_80M_CLK, AUDIO_PLL1_CLK, EXT_CLK_2} + }, + {GPT6_CLK_ROOT, IP_CLOCK_SLICE, 48, + {OSC_24M_CLK, SYSTEM_PLL2_100M_CLK, SYSTEM_PLL1_400M_CLK, + SYSTEM_PLL1_40M_CLK, VIDEO_PLL_CLK, + SYSTEM_PLL1_80M_CLK, AUDIO_PLL1_CLK, EXT_CLK_3} + }, + {TRACE_CLK_ROOT, IP_CLOCK_SLICE, 49, + {OSC_24M_CLK, SYSTEM_PLL1_133M_CLK, SYSTEM_PLL1_160M_CLK, + VPU_PLL_CLK, SYSTEM_PLL2_125M_CLK, + SYSTEM_PLL3_CLK, EXT_CLK_1, EXT_CLK_3} + }, + {WDOG_CLK_ROOT, IP_CLOCK_SLICE, 50, + {OSC_24M_CLK, SYSTEM_PLL1_133M_CLK, SYSTEM_PLL1_160M_CLK, + VPU_PLL_CLK, SYSTEM_PLL2_125M_CLK, + SYSTEM_PLL3_CLK, SYSTEM_PLL1_80M_CLK, SYSTEM_PLL2_166M_CLK} + }, + {WRCLK_CLK_ROOT, IP_CLOCK_SLICE, 51, + {OSC_24M_CLK, SYSTEM_PLL1_40M_CLK, VPU_PLL_CLK, + SYSTEM_PLL3_CLK, SYSTEM_PLL2_200M_CLK, + SYSTEM_PLL1_266M_CLK, SYSTEM_PLL2_500M_CLK, SYSTEM_PLL1_100M_CLK} + }, + {IPP_DO_CLKO1, IP_CLOCK_SLICE, 52, + {OSC_24M_CLK, SYSTEM_PLL1_800M_CLK, OSC_HDMI_CLK, + SYSTEM_PLL1_200M_CLK, AUDIO_PLL2_CLK, + SYSTEM_PLL2_500M_CLK, VPU_PLL_CLK, SYSTEM_PLL1_80M_CLK} + }, + {IPP_DO_CLKO2, IP_CLOCK_SLICE, 53, + {OSC_24M_CLK, SYSTEM_PLL2_200M_CLK, SYSTEM_PLL1_400M_CLK, + SYSTEM_PLL2_166M_CLK, SYSTEM_PLL3_CLK, + AUDIO_PLL1_CLK, VIDEO_PLL_CLK, OSC_32K_CLK} + }, + {MIPI_DSI_CORE_CLK_ROOT, IP_CLOCK_SLICE, 54, + {OSC_24M_CLK, SYSTEM_PLL1_266M_CLK, SYSTEM_PLL2_250M_CLK, + SYSTEM_PLL1_800M_CLK, SYSTEM_PLL2_1000M_CLK, + SYSTEM_PLL3_CLK, AUDIO_PLL2_CLK, VIDEO_PLL_CLK} + }, + {MIPI_DSI_PHY_REF_CLK_ROOT, IP_CLOCK_SLICE, 55, + {OSC_24M_CLK, SYSTEM_PLL2_125M_CLK, SYSTEM_PLL2_100M_CLK, + SYSTEM_PLL1_800M_CLK, SYSTEM_PLL2_1000M_CLK, + EXT_CLK_2, AUDIO_PLL2_CLK, VIDEO_PLL_CLK} + }, + {MIPI_DSI_DBI_CLK_ROOT, IP_CLOCK_SLICE, 56, + {OSC_24M_CLK, SYSTEM_PLL1_266M_CLK, SYSTEM_PLL2_100M_CLK, + SYSTEM_PLL1_800M_CLK, SYSTEM_PLL2_1000M_CLK, + SYSTEM_PLL3_CLK, AUDIO_PLL2_CLK, VIDEO_PLL_CLK} + }, + {USDHC3_CLK_ROOT, IP_CLOCK_SLICE, 57, + {OSC_24M_CLK, SYSTEM_PLL1_400M_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL2_500M_CLK, SYSTEM_PLL3_CLK, + SYSTEM_PLL1_266M_CLK, AUDIO_PLL2_CLK, SYSTEM_PLL1_100M_CLK} + }, + {MIPI_CSI1_CORE_CLK_ROOT, IP_CLOCK_SLICE, 58, + {OSC_24M_CLK, SYSTEM_PLL1_266M_CLK, SYSTEM_PLL2_250M_CLK, + SYSTEM_PLL1_800M_CLK, SYSTEM_PLL2_1000M_CLK, + SYSTEM_PLL3_CLK, AUDIO_PLL2_CLK, VIDEO_PLL_CLK} + }, + {MIPI_CSI1_PHY_REF_CLK_ROOT, IP_CLOCK_SLICE, 59, + {OSC_24M_CLK, SYSTEM_PLL2_333M_CLK, SYSTEM_PLL2_100M_CLK, + SYSTEM_PLL1_800M_CLK, SYSTEM_PLL2_1000M_CLK, + EXT_CLK_2, AUDIO_PLL2_CLK, VIDEO_PLL_CLK} + }, + {MIPI_CSI1_ESC_CLK_ROOT, IP_CLOCK_SLICE, 60, + {OSC_24M_CLK, SYSTEM_PLL2_100M_CLK, SYSTEM_PLL1_80M_CLK, + SYSTEM_PLL1_800M_CLK, SYSTEM_PLL2_1000M_CLK, + SYSTEM_PLL3_CLK, EXT_CLK_3, AUDIO_PLL2_CLK} + }, + {MIPI_CSI2_CORE_CLK_ROOT, IP_CLOCK_SLICE, 61, + {OSC_24M_CLK, SYSTEM_PLL1_266M_CLK, SYSTEM_PLL2_250M_CLK, + SYSTEM_PLL1_800M_CLK, SYSTEM_PLL2_1000M_CLK, + SYSTEM_PLL3_CLK, AUDIO_PLL2_CLK, VIDEO_PLL_CLK} + }, + {MIPI_CSI2_PHY_REF_CLK_ROOT, IP_CLOCK_SLICE, 62, + {OSC_24M_CLK, SYSTEM_PLL2_333M_CLK, SYSTEM_PLL2_100M_CLK, + SYSTEM_PLL1_800M_CLK, SYSTEM_PLL2_1000M_CLK, + EXT_CLK_2, AUDIO_PLL2_CLK, VIDEO_PLL_CLK} + }, + {MIPI_CSI2_ESC_CLK_ROOT, IP_CLOCK_SLICE, 63, + {OSC_24M_CLK, SYSTEM_PLL2_100M_CLK, SYSTEM_PLL1_80M_CLK, + SYSTEM_PLL1_800M_CLK, SYSTEM_PLL2_1000M_CLK, + SYSTEM_PLL3_CLK, EXT_CLK_3, AUDIO_PLL2_CLK} + }, + {PCIE2_CTRL_CLK_ROOT, IP_CLOCK_SLICE, 64, + {OSC_24M_CLK, SYSTEM_PLL2_250M_CLK, SYSTEM_PLL2_200M_CLK, + SYSTEM_PLL1_266M_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL2_500M_CLK, SYSTEM_PLL2_333M_CLK, SYSTEM_PLL3_CLK} + }, + {PCIE2_PHY_CLK_ROOT, IP_CLOCK_SLICE, 65, + {OSC_24M_CLK, SYSTEM_PLL2_100M_CLK, SYSTEM_PLL2_500M_CLK, + EXT_CLK_1, EXT_CLK_2, EXT_CLK_3, + EXT_CLK_4, SYSTEM_PLL1_400M_CLK} + }, + {PCIE2_AUX_CLK_ROOT, IP_CLOCK_SLICE, 66, + {OSC_24M_CLK, SYSTEM_PLL2_200M_CLK, SYSTEM_PLL2_50M_CLK, + SYSTEM_PLL3_CLK, SYSTEM_PLL2_100M_CLK, + SYSTEM_PLL1_80M_CLK, SYSTEM_PLL1_160M_CLK, SYSTEM_PLL1_200M_CLK} + }, + {ECSPI3_CLK_ROOT, IP_CLOCK_SLICE, 67, + {OSC_24M_CLK, SYSTEM_PLL2_200M_CLK, SYSTEM_PLL1_40M_CLK, + SYSTEM_PLL1_160M_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL3_CLK, SYSTEM_PLL2_250M_CLK, AUDIO_PLL2_CLK} + }, + {PDM_CLK_ROOT, IP_CLOCK_SLICE, 68, + {OSC_24M_CLK, SYSTEM_PLL2_100M_CLK, AUDIO_PLL1_CLK, + SYSTEM_PLL1_800M_CLK, SYSTEM_PLL2_1000M_CLK, + SYSTEM_PLL3_CLK, EXT_CLK_3, AUDIO_PLL2_CLK}, + }, + {VPU_H1_CLK_ROOT, IP_CLOCK_SLICE, 69, + {OSC_24M_CLK, VPU_PLL_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL2_1000M_CLK, AUDIO_PLL2_CLK, + SYSTEM_PLL2_125M_CLK, SYSTEM_PLL3_CLK, AUDIO_PLL1_CLK} + }, + {DRAM_SEL_CFG, DRAM_SEL_CLOCK_SLICE, 0, + {DRAM_PLL1_CLK} + }, + {CORE_SEL_CFG, CORE_SEL_CLOCK_SLICE, 0, + {ARM_A53_ALT_CLK, ARM_PLL_CLK} + }, +}; +#elif defined(CONFIG_IMX8MN) +static struct clk_root_map root_array[] = { + {ARM_A53_CLK_ROOT, CORE_CLOCK_SLICE, 0, + {OSC_24M_CLK, ARM_PLL_CLK, SYSTEM_PLL2_500M_CLK, + SYSTEM_PLL2_1000M_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL1_400M_CLK, AUDIO_PLL1_CLK, SYSTEM_PLL3_CLK} + }, + {ARM_M7_CLK_ROOT, CORE_CLOCK_SLICE, 1, + {OSC_24M_CLK, SYSTEM_PLL2_200M_CLK, SYSTEM_PLL2_250M_CLK, + VPU_PLL_CLK, SYSTEM_PLL1_800M_CLK, + AUDIO_PLL1_CLK, VIDEO_PLL_CLK, SYSTEM_PLL3_CLK} + }, + {GPU_CORE_CLK_ROOT, CORE_CLOCK_SLICE, 3, + {OSC_24M_CLK, GPU_PLL_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL3_CLK, SYSTEM_PLL2_1000M_CLK, + AUDIO_PLL1_CLK, VIDEO_PLL_CLK, AUDIO_PLL2_CLK} + }, + {GPU_SHADER_CLK_ROOT, CORE_CLOCK_SLICE, 4, + {OSC_24M_CLK, GPU_PLL_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL3_CLK, SYSTEM_PLL2_1000M_CLK, + AUDIO_PLL1_CLK, VIDEO_PLL_CLK, AUDIO_PLL2_CLK} + }, + {MAIN_AXI_CLK_ROOT, BUS_CLOCK_SLICE, 0, + {OSC_24M_CLK, SYSTEM_PLL2_333M_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL2_250M_CLK, SYSTEM_PLL2_1000M_CLK, + AUDIO_PLL1_CLK, VIDEO_PLL_CLK, SYSTEM_PLL1_100M_CLK} + }, + {ENET_AXI_CLK_ROOT, BUS_CLOCK_SLICE, 1, + {OSC_24M_CLK, SYSTEM_PLL1_266M_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL2_250M_CLK, SYSTEM_PLL2_200M_CLK, + AUDIO_PLL1_CLK, VIDEO_PLL_CLK, SYSTEM_PLL3_CLK} + }, + {NAND_USDHC_BUS_CLK_ROOT, BUS_CLOCK_SLICE, 2, + {OSC_24M_CLK, SYSTEM_PLL1_266M_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL2_200M_CLK, SYSTEM_PLL1_133M_CLK, + SYSTEM_PLL3_CLK, SYSTEM_PLL2_250M_CLK, AUDIO_PLL1_CLK} + }, + {DISPLAY_AXI_CLK_ROOT, BUS_CLOCK_SLICE, 4, + {OSC_24M_CLK, SYSTEM_PLL2_1000M_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL3_CLK, SYSTEM_PLL1_40M_CLK, AUDIO_PLL2_CLK, + EXT_CLK_1, EXT_CLK_4} + }, + {DISPLAY_APB_CLK_ROOT, BUS_CLOCK_SLICE, 5, + {OSC_24M_CLK, SYSTEM_PLL2_125M_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL3_CLK, SYSTEM_PLL1_40M_CLK, AUDIO_PLL2_CLK, + EXT_CLK_1, EXT_CLK_3} + }, + {USB_BUS_CLK_ROOT, BUS_CLOCK_SLICE, 7, + {OSC_24M_CLK, SYSTEM_PLL2_500M_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL2_100M_CLK, SYSTEM_PLL2_200M_CLK, + EXT_CLK_2, EXT_CLK_4, AUDIO_PLL2_CLK} + }, + {GPU_AXI_CLK_ROOT, BUS_CLOCK_SLICE, 8, + {OSC_24M_CLK, SYSTEM_PLL1_800M_CLK, GPU_PLL_CLK, + SYSTEM_PLL3_CLK, SYSTEM_PLL2_1000M_CLK, + AUDIO_PLL1_CLK, VIDEO_PLL_CLK, AUDIO_PLL2_CLK} + }, + {GPU_AHB_CLK_ROOT, BUS_CLOCK_SLICE, 9, + {OSC_24M_CLK, SYSTEM_PLL1_800M_CLK, GPU_PLL_CLK, + SYSTEM_PLL3_CLK, SYSTEM_PLL2_1000M_CLK, + AUDIO_PLL1_CLK, VIDEO_PLL_CLK, AUDIO_PLL2_CLK} + }, + {NOC_CLK_ROOT, BUS_CLOCK_SLICE, 10, + {OSC_24M_CLK, SYSTEM_PLL1_800M_CLK, SYSTEM_PLL3_CLK, + SYSTEM_PLL2_1000M_CLK, SYSTEM_PLL2_500M_CLK, + AUDIO_PLL1_CLK, VIDEO_PLL_CLK, AUDIO_PLL2_CLK} + }, + {AHB_CLK_ROOT, AHB_CLOCK_SLICE, 0, + {OSC_24M_CLK, SYSTEM_PLL1_133M_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL1_400M_CLK, SYSTEM_PLL2_125M_CLK, + SYSTEM_PLL3_CLK, AUDIO_PLL1_CLK, VIDEO_PLL_CLK} + }, + {AUDIO_AHB_CLK_ROOT, AHB_CLOCK_SLICE, 1, + {OSC_24M_CLK, SYSTEM_PLL2_500M_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL2_1000M_CLK, SYSTEM_PLL2_166M_CLK, + SYSTEM_PLL3_CLK, AUDIO_PLL1_CLK, VIDEO_PLL_CLK} + }, + {DRAM_ALT_CLK_ROOT, IP_CLOCK_SLICE, 0, + {OSC_24M_CLK, SYSTEM_PLL1_800M_CLK, SYSTEM_PLL1_100M_CLK, + SYSTEM_PLL2_500M_CLK, SYSTEM_PLL2_1000M_CLK, + SYSTEM_PLL3_CLK, AUDIO_PLL1_CLK, SYSTEM_PLL1_266M_CLK} + }, + {DRAM_APB_CLK_ROOT, IP_CLOCK_SLICE, 1, + {OSC_24M_CLK, SYSTEM_PLL2_200M_CLK, SYSTEM_PLL1_40M_CLK, + SYSTEM_PLL1_160M_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL3_CLK, SYSTEM_PLL2_250M_CLK, AUDIO_PLL2_CLK} + }, + {DISPLAY_PIXEL_CLK_ROOT, IP_CLOCK_SLICE, 10, + {OSC_24M_CLK, VIDEO_PLL_CLK, AUDIO_PLL2_CLK, + AUDIO_PLL1_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL2_1000M_CLK, SYSTEM_PLL3_CLK, EXT_CLK_4} + }, + {SAI2_CLK_ROOT, IP_CLOCK_SLICE, 12, + {OSC_24M_CLK, AUDIO_PLL1_CLK, AUDIO_PLL2_CLK, + VIDEO_PLL_CLK, SYSTEM_PLL1_133M_CLK, + OSC_HDMI_CLK, EXT_CLK_2, EXT_CLK_3} + }, + {SAI3_CLK_ROOT, IP_CLOCK_SLICE, 13, + {OSC_24M_CLK, AUDIO_PLL1_CLK, AUDIO_PLL2_CLK, + VIDEO_PLL_CLK, SYSTEM_PLL1_133M_CLK, + OSC_HDMI_CLK, EXT_CLK_3, EXT_CLK_4} + }, + {SAI5_CLK_ROOT, IP_CLOCK_SLICE, 15, + {OSC_24M_CLK, AUDIO_PLL1_CLK, AUDIO_PLL2_CLK, + VIDEO_PLL_CLK, SYSTEM_PLL1_133M_CLK, + OSC_HDMI_CLK, EXT_CLK_2, EXT_CLK_3} + }, + {SAI6_CLK_ROOT, IP_CLOCK_SLICE, 16, + {OSC_24M_CLK, AUDIO_PLL1_CLK, AUDIO_PLL2_CLK, + VIDEO_PLL_CLK, SYSTEM_PLL1_133M_CLK, + OSC_HDMI_CLK, EXT_CLK_3, EXT_CLK_4} + }, + {SPDIF1_CLK_ROOT, IP_CLOCK_SLICE, 17, + {OSC_24M_CLK, AUDIO_PLL1_CLK, AUDIO_PLL2_CLK, + VIDEO_PLL_CLK, SYSTEM_PLL1_133M_CLK, + OSC_HDMI_CLK, EXT_CLK_2, EXT_CLK_3} + }, + {ENET_REF_CLK_ROOT, IP_CLOCK_SLICE, 19, + {OSC_24M_CLK, SYSTEM_PLL2_125M_CLK, SYSTEM_PLL2_50M_CLK, + SYSTEM_PLL2_100M_CLK, SYSTEM_PLL1_160M_CLK, + AUDIO_PLL1_CLK, VIDEO_PLL_CLK, EXT_CLK_4} + }, + {ENET_TIMER_CLK_ROOT, IP_CLOCK_SLICE, 20, + {OSC_24M_CLK, SYSTEM_PLL2_100M_CLK, AUDIO_PLL1_CLK, + EXT_CLK_1, EXT_CLK_2, EXT_CLK_3, EXT_CLK_4, + VIDEO_PLL_CLK} + }, + {ENET_PHY_REF_CLK_ROOT, IP_CLOCK_SLICE, 21, + {OSC_24M_CLK, SYSTEM_PLL2_50M_CLK, SYSTEM_PLL2_125M_CLK, + SYSTEM_PLL2_200M_CLK, SYSTEM_PLL2_500M_CLK, + AUDIO_PLL1_CLK, VIDEO_PLL_CLK, AUDIO_PLL2_CLK} + }, + {NAND_CLK_ROOT, IP_CLOCK_SLICE, 22, + {OSC_24M_CLK, SYSTEM_PLL2_500M_CLK, AUDIO_PLL1_CLK, + SYSTEM_PLL1_400M_CLK, AUDIO_PLL2_CLK, SYSTEM_PLL3_CLK, + SYSTEM_PLL2_250M_CLK, VIDEO_PLL_CLK} + }, + {QSPI_CLK_ROOT, IP_CLOCK_SLICE, 23, + {OSC_24M_CLK, SYSTEM_PLL1_400M_CLK, SYSTEM_PLL2_333M_CLK, + SYSTEM_PLL2_500M_CLK, AUDIO_PLL2_CLK, + SYSTEM_PLL1_266M_CLK, SYSTEM_PLL3_CLK, SYSTEM_PLL1_100M_CLK} + }, + {USDHC1_CLK_ROOT, IP_CLOCK_SLICE, 24, + {OSC_24M_CLK, SYSTEM_PLL1_400M_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL2_500M_CLK, SYSTEM_PLL3_CLK, + SYSTEM_PLL1_266M_CLK, AUDIO_PLL2_CLK, SYSTEM_PLL1_100M_CLK} + }, + {USDHC2_CLK_ROOT, IP_CLOCK_SLICE, 25, + {OSC_24M_CLK, SYSTEM_PLL1_400M_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL2_500M_CLK, SYSTEM_PLL3_CLK, + SYSTEM_PLL1_266M_CLK, AUDIO_PLL2_CLK, SYSTEM_PLL1_100M_CLK} + }, + {I2C1_CLK_ROOT, IP_CLOCK_SLICE, 26, + {OSC_24M_CLK, SYSTEM_PLL1_160M_CLK, SYSTEM_PLL2_50M_CLK, + SYSTEM_PLL3_CLK, AUDIO_PLL1_CLK, VIDEO_PLL_CLK, + AUDIO_PLL2_CLK, SYSTEM_PLL1_133M_CLK} + }, + {I2C2_CLK_ROOT, IP_CLOCK_SLICE, 27, + {OSC_24M_CLK, SYSTEM_PLL1_160M_CLK, SYSTEM_PLL2_50M_CLK, + SYSTEM_PLL3_CLK, AUDIO_PLL1_CLK, VIDEO_PLL_CLK, + AUDIO_PLL2_CLK, SYSTEM_PLL1_133M_CLK} + }, + {I2C3_CLK_ROOT, IP_CLOCK_SLICE, 28, + {OSC_24M_CLK, SYSTEM_PLL1_160M_CLK, SYSTEM_PLL2_50M_CLK, + SYSTEM_PLL3_CLK, AUDIO_PLL1_CLK, VIDEO_PLL_CLK, + AUDIO_PLL2_CLK, SYSTEM_PLL1_133M_CLK} + }, + {I2C4_CLK_ROOT, IP_CLOCK_SLICE, 29, + {OSC_24M_CLK, SYSTEM_PLL1_160M_CLK, SYSTEM_PLL2_50M_CLK, + SYSTEM_PLL3_CLK, AUDIO_PLL1_CLK, VIDEO_PLL_CLK, + AUDIO_PLL2_CLK, SYSTEM_PLL1_133M_CLK} + }, + {UART1_CLK_ROOT, IP_CLOCK_SLICE, 30, + {OSC_24M_CLK, SYSTEM_PLL1_80M_CLK, SYSTEM_PLL2_200M_CLK, + SYSTEM_PLL2_100M_CLK, SYSTEM_PLL3_CLK, + EXT_CLK_2, EXT_CLK_4, AUDIO_PLL2_CLK} + }, + {UART2_CLK_ROOT, IP_CLOCK_SLICE, 31, + {OSC_24M_CLK, SYSTEM_PLL1_80M_CLK, SYSTEM_PLL2_200M_CLK, + SYSTEM_PLL2_100M_CLK, SYSTEM_PLL3_CLK, + EXT_CLK_2, EXT_CLK_3, AUDIO_PLL2_CLK} + }, + {UART3_CLK_ROOT, IP_CLOCK_SLICE, 32, + {OSC_24M_CLK, SYSTEM_PLL1_80M_CLK, SYSTEM_PLL2_200M_CLK, + SYSTEM_PLL2_100M_CLK, SYSTEM_PLL3_CLK, + EXT_CLK_2, EXT_CLK_4, AUDIO_PLL2_CLK} + }, + {UART4_CLK_ROOT, IP_CLOCK_SLICE, 33, + {OSC_24M_CLK, SYSTEM_PLL1_80M_CLK, SYSTEM_PLL2_200M_CLK, + SYSTEM_PLL2_100M_CLK, SYSTEM_PLL3_CLK, + EXT_CLK_2, EXT_CLK_3, AUDIO_PLL2_CLK} + }, + {USB_CORE_REF_CLK_ROOT, IP_CLOCK_SLICE, 34, + {OSC_24M_CLK, SYSTEM_PLL1_100M_CLK, SYSTEM_PLL1_40M_CLK, + SYSTEM_PLL2_100M_CLK, SYSTEM_PLL2_200M_CLK, + EXT_CLK_2, EXT_CLK_3, AUDIO_PLL2_CLK} + }, + {USB_PHY_REF_CLK_ROOT, IP_CLOCK_SLICE, 35, + {OSC_24M_CLK, SYSTEM_PLL1_100M_CLK, SYSTEM_PLL1_40M_CLK, + SYSTEM_PLL2_100M_CLK, SYSTEM_PLL2_200M_CLK, + EXT_CLK_2, EXT_CLK_3, AUDIO_PLL2_CLK} + }, + {GIC_CLK_ROOT, IP_CLOCK_SLICE, 36, + {OSC_24M_CLK, SYSTEM_PLL2_200M_CLK, SYSTEM_PLL1_40M_CLK, + SYSTEM_PLL2_100M_CLK, SYSTEM_PLL1_800M_CLK, + EXT_CLK_2, EXT_CLK_4, AUDIO_PLL2_CLK} + }, + {ECSPI1_CLK_ROOT, IP_CLOCK_SLICE, 37, + {OSC_24M_CLK, SYSTEM_PLL2_200M_CLK, SYSTEM_PLL1_40M_CLK, + SYSTEM_PLL1_160M_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL3_CLK, SYSTEM_PLL2_250M_CLK, AUDIO_PLL2_CLK} + }, + {ECSPI2_CLK_ROOT, IP_CLOCK_SLICE, 38, + {OSC_24M_CLK, SYSTEM_PLL2_200M_CLK, SYSTEM_PLL1_40M_CLK, + SYSTEM_PLL1_160M_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL3_CLK, SYSTEM_PLL2_250M_CLK, AUDIO_PLL2_CLK} + }, + {PWM1_CLK_ROOT, IP_CLOCK_SLICE, 39, + {OSC_24M_CLK, SYSTEM_PLL2_100M_CLK, SYSTEM_PLL1_160M_CLK, + SYSTEM_PLL1_40M_CLK, SYSTEM_PLL3_CLK, EXT_CLK_1, + SYSTEM_PLL1_80M_CLK, VIDEO_PLL_CLK} + }, + {PWM2_CLK_ROOT, IP_CLOCK_SLICE, 40, + {OSC_24M_CLK, SYSTEM_PLL2_100M_CLK, SYSTEM_PLL1_160M_CLK, + SYSTEM_PLL1_40M_CLK, SYSTEM_PLL3_CLK, EXT_CLK_1, + SYSTEM_PLL1_80M_CLK, VIDEO_PLL_CLK} + }, + {PWM3_CLK_ROOT, IP_CLOCK_SLICE, 41, + {OSC_24M_CLK, SYSTEM_PLL2_100M_CLK, SYSTEM_PLL1_160M_CLK, + SYSTEM_PLL1_40M_CLK, SYSTEM_PLL3_CLK, EXT_CLK_2, + SYSTEM_PLL1_80M_CLK, VIDEO_PLL_CLK} + }, + {PWM4_CLK_ROOT, IP_CLOCK_SLICE, 42, + {OSC_24M_CLK, SYSTEM_PLL2_100M_CLK, SYSTEM_PLL1_160M_CLK, + SYSTEM_PLL1_40M_CLK, SYSTEM_PLL3_CLK, EXT_CLK_2, + SYSTEM_PLL1_80M_CLK, VIDEO_PLL_CLK} + }, + {GPT1_CLK_ROOT, IP_CLOCK_SLICE, 43, + {OSC_24M_CLK, SYSTEM_PLL2_100M_CLK, SYSTEM_PLL1_400M_CLK, + SYSTEM_PLL1_40M_CLK, VIDEO_PLL_CLK, + SYSTEM_PLL1_80M_CLK, AUDIO_PLL1_CLK, EXT_CLK_1} + }, + {GPT2_CLK_ROOT, IP_CLOCK_SLICE, 44, + {OSC_24M_CLK, SYSTEM_PLL2_100M_CLK, SYSTEM_PLL1_400M_CLK, + SYSTEM_PLL1_40M_CLK, VIDEO_PLL_CLK, + SYSTEM_PLL1_80M_CLK, AUDIO_PLL1_CLK, EXT_CLK_2} + }, + {GPT3_CLK_ROOT, IP_CLOCK_SLICE, 45, + {OSC_24M_CLK, SYSTEM_PLL2_100M_CLK, SYSTEM_PLL1_400M_CLK, + SYSTEM_PLL1_40M_CLK, VIDEO_PLL_CLK, + SYSTEM_PLL1_80M_CLK, AUDIO_PLL1_CLK, EXT_CLK_3} + }, + {GPT4_CLK_ROOT, IP_CLOCK_SLICE, 46, + {OSC_24M_CLK, SYSTEM_PLL2_100M_CLK, SYSTEM_PLL1_400M_CLK, + SYSTEM_PLL1_40M_CLK, VIDEO_PLL_CLK, + SYSTEM_PLL1_80M_CLK, AUDIO_PLL1_CLK, EXT_CLK_1} + }, + {GPT5_CLK_ROOT, IP_CLOCK_SLICE, 47, + {OSC_24M_CLK, SYSTEM_PLL2_100M_CLK, SYSTEM_PLL1_400M_CLK, + SYSTEM_PLL1_40M_CLK, VIDEO_PLL_CLK, + SYSTEM_PLL1_80M_CLK, AUDIO_PLL1_CLK, EXT_CLK_2} + }, + {GPT6_CLK_ROOT, IP_CLOCK_SLICE, 48, + {OSC_24M_CLK, SYSTEM_PLL2_100M_CLK, SYSTEM_PLL1_400M_CLK, + SYSTEM_PLL1_40M_CLK, VIDEO_PLL_CLK, + SYSTEM_PLL1_80M_CLK, AUDIO_PLL1_CLK, EXT_CLK_3} + }, + {TRACE_CLK_ROOT, IP_CLOCK_SLICE, 49, + {OSC_24M_CLK, SYSTEM_PLL1_133M_CLK, SYSTEM_PLL1_160M_CLK, + VPU_PLL_CLK, SYSTEM_PLL2_125M_CLK, + SYSTEM_PLL3_CLK, EXT_CLK_1, EXT_CLK_3} + }, + {WDOG_CLK_ROOT, IP_CLOCK_SLICE, 50, + {OSC_24M_CLK, SYSTEM_PLL1_133M_CLK, SYSTEM_PLL1_160M_CLK, + VPU_PLL_CLK, SYSTEM_PLL2_125M_CLK, + SYSTEM_PLL3_CLK, SYSTEM_PLL1_80M_CLK, SYSTEM_PLL2_166M_CLK} + }, + {WRCLK_CLK_ROOT, IP_CLOCK_SLICE, 51, + {OSC_24M_CLK, SYSTEM_PLL1_40M_CLK, VPU_PLL_CLK, + SYSTEM_PLL3_CLK, SYSTEM_PLL2_200M_CLK, + SYSTEM_PLL1_266M_CLK, SYSTEM_PLL2_500M_CLK, SYSTEM_PLL1_100M_CLK} + }, + {IPP_DO_CLKO1, IP_CLOCK_SLICE, 52, + {OSC_24M_CLK, SYSTEM_PLL1_800M_CLK, OSC_HDMI_CLK, + SYSTEM_PLL1_200M_CLK, AUDIO_PLL2_CLK, + SYSTEM_PLL2_500M_CLK, VPU_PLL_CLK, SYSTEM_PLL1_80M_CLK} + }, + {IPP_DO_CLKO2, IP_CLOCK_SLICE, 53, + {OSC_24M_CLK, SYSTEM_PLL2_200M_CLK, SYSTEM_PLL1_400M_CLK, + SYSTEM_PLL2_166M_CLK, SYSTEM_PLL3_CLK, + AUDIO_PLL1_CLK, VIDEO_PLL_CLK, OSC_32K_CLK} + }, + {MIPI_DSI_CORE_CLK_ROOT, IP_CLOCK_SLICE, 54, + {OSC_24M_CLK, SYSTEM_PLL1_266M_CLK, SYSTEM_PLL2_250M_CLK, + SYSTEM_PLL1_800M_CLK, SYSTEM_PLL2_1000M_CLK, + SYSTEM_PLL3_CLK, AUDIO_PLL2_CLK, VIDEO_PLL_CLK} + }, + {DISPLAY_DSI_PHY_REF_CLK_ROOT, IP_CLOCK_SLICE, 55, + {OSC_24M_CLK, SYSTEM_PLL2_125M_CLK, SYSTEM_PLL2_100M_CLK, + SYSTEM_PLL1_800M_CLK, SYSTEM_PLL2_1000M_CLK, + EXT_CLK_2, AUDIO_PLL2_CLK, VIDEO_PLL_CLK} + }, + {MIPI_DSI_DBI_CLK_ROOT, IP_CLOCK_SLICE, 56, + {OSC_24M_CLK, SYSTEM_PLL1_266M_CLK, SYSTEM_PLL2_100M_CLK, + SYSTEM_PLL1_800M_CLK, SYSTEM_PLL2_1000M_CLK, + SYSTEM_PLL3_CLK, AUDIO_PLL2_CLK, VIDEO_PLL_CLK} + }, + {USDHC3_CLK_ROOT, IP_CLOCK_SLICE, 57, + {OSC_24M_CLK, SYSTEM_PLL1_400M_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL2_500M_CLK, SYSTEM_PLL3_CLK, + SYSTEM_PLL1_266M_CLK, AUDIO_PLL2_CLK, SYSTEM_PLL1_100M_CLK} + }, + {DISPLAY_CAMERA_PIXEL_CLK_ROOT, IP_CLOCK_SLICE, 58, + {OSC_24M_CLK, SYSTEM_PLL1_266M_CLK, SYSTEM_PLL2_250M_CLK, + SYSTEM_PLL1_800M_CLK, SYSTEM_PLL2_1000M_CLK, + SYSTEM_PLL3_CLK, AUDIO_PLL2_CLK, VIDEO_PLL_CLK} + }, + {MIPI_CSI1_PHY_REF_CLK_ROOT, IP_CLOCK_SLICE, 59, + {OSC_24M_CLK, SYSTEM_PLL2_333M_CLK, SYSTEM_PLL2_100M_CLK, + SYSTEM_PLL1_800M_CLK, SYSTEM_PLL2_1000M_CLK, + EXT_CLK_2, AUDIO_PLL2_CLK, VIDEO_PLL_CLK} + }, + {MIPI_CSI2_PHY_REF_CLK_ROOT, IP_CLOCK_SLICE, 62, + {OSC_24M_CLK, SYSTEM_PLL2_333M_CLK, SYSTEM_PLL2_100M_CLK, + SYSTEM_PLL1_800M_CLK, SYSTEM_PLL2_1000M_CLK, + EXT_CLK_2, AUDIO_PLL2_CLK, VIDEO_PLL_CLK} + }, + {MIPI_CSI2_ESC_CLK_ROOT, IP_CLOCK_SLICE, 63, + {OSC_24M_CLK, SYSTEM_PLL2_100M_CLK, SYSTEM_PLL1_80M_CLK, + SYSTEM_PLL1_800M_CLK, SYSTEM_PLL2_1000M_CLK, + SYSTEM_PLL3_CLK, EXT_CLK_3, AUDIO_PLL2_CLK} + }, + {ECSPI3_CLK_ROOT, IP_CLOCK_SLICE, 67, + {OSC_24M_CLK, SYSTEM_PLL2_200M_CLK, SYSTEM_PLL1_40M_CLK, + SYSTEM_PLL1_160M_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL3_CLK, SYSTEM_PLL2_250M_CLK, AUDIO_PLL2_CLK} + }, + {PDM_CLK_ROOT, IP_CLOCK_SLICE, 68, + {OSC_24M_CLK, SYSTEM_PLL2_100M_CLK, AUDIO_PLL1_CLK, + SYSTEM_PLL1_800M_CLK, SYSTEM_PLL2_1000M_CLK, + SYSTEM_PLL3_CLK, EXT_CLK_3, AUDIO_PLL2_CLK}, + }, + {SAI7_CLK_ROOT, IP_CLOCK_SLICE, 70, + {OSC_24M_CLK, AUDIO_PLL1_CLK, AUDIO_PLL2_CLK, + VIDEO_PLL_CLK, SYSTEM_PLL1_133M_CLK, + OSC_HDMI_CLK, EXT_CLK_3, EXT_CLK_4} + }, + {DRAM_SEL_CFG, DRAM_SEL_CLOCK_SLICE, 0, + {DRAM_PLL1_CLK} + }, + {CORE_SEL_CFG, CORE_SEL_CLOCK_SLICE, 0, + {ARM_A53_ALT_CLK, ARM_PLL_CLK} + }, +}; +#elif defined(CONFIG_IMX8MP) +static struct clk_root_map root_array[] = { + {ARM_A53_CLK_ROOT, CORE_CLOCK_SLICE, 0, + {OSC_24M_CLK, ARM_PLL_CLK, SYSTEM_PLL2_500M_CLK, + SYSTEM_PLL2_1000M_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL1_400M_CLK, AUDIO_PLL1_CLK, SYSTEM_PLL3_CLK} + }, + {ARM_M7_CLK_ROOT, CORE_CLOCK_SLICE, 1, + {OSC_24M_CLK, SYSTEM_PLL2_200M_CLK, SYSTEM_PLL2_250M_CLK, + VPU_PLL_CLK, SYSTEM_PLL1_800M_CLK, + AUDIO_PLL1_CLK, VIDEO_PLL_CLK, SYSTEM_PLL3_CLK} + }, + {ML_CLK_ROOT, CORE_CLOCK_SLICE, 2, + {OSC_24M_CLK, SYSTEM_PLL2_200M_CLK, SYSTEM_PLL2_250M_CLK, + VPU_PLL_CLK, SYSTEM_PLL1_800M_CLK, + AUDIO_PLL1_CLK, VIDEO_PLL_CLK, SYSTEM_PLL3_CLK} + }, + {HSIO_AXI_CLK_ROOT, CORE_CLOCK_SLICE, 7, + {OSC_24M_CLK, SYSTEM_PLL2_500M_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL2_100M_CLK, SYSTEM_PLL2_200M_CLK, EXT_CLK_2, + EXT_CLK_4, AUDIO_PLL2_CLK} + }, + {MAIN_AXI_CLK_ROOT, BUS_CLOCK_SLICE, 0, + {OSC_24M_CLK, SYSTEM_PLL2_333M_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL2_250M_CLK, SYSTEM_PLL2_1000M_CLK, AUDIO_PLL1_CLK, + VIDEO_PLL_CLK, SYSTEM_PLL1_100M_CLK} + }, + {ENET_AXI_CLK_ROOT, BUS_CLOCK_SLICE, 1, + {OSC_24M_CLK, SYSTEM_PLL1_266M_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL2_250M_CLK, SYSTEM_PLL2_200M_CLK, AUDIO_PLL1_CLK, + VIDEO_PLL_CLK, SYSTEM_PLL3_CLK} + }, + {NAND_USDHC_BUS_CLK_ROOT, BUS_CLOCK_SLICE, 2, + {OSC_24M_CLK, SYSTEM_PLL1_266M_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL2_200M_CLK, SYSTEM_PLL1_133M_CLK, + SYSTEM_PLL3_CLK, SYSTEM_PLL2_250M_CLK, AUDIO_PLL1_CLK} + }, + {MEDIA_AXI_CLK_ROOT, BUS_CLOCK_SLICE, 4, + {OSC_24M_CLK, SYSTEM_PLL2_1000M_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL3_CLK, SYSTEM_PLL1_40M_CLK, + AUDIO_PLL2_CLK, EXT_CLK_1, SYSTEM_PLL2_500M_CLK} + }, + {MEDIA_APB_CLK_ROOT, BUS_CLOCK_SLICE, 5, + {OSC_24M_CLK, SYSTEM_PLL2_125M_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL3_CLK, SYSTEM_PLL1_40M_CLK, + AUDIO_PLL2_CLK, EXT_CLK_1, SYSTEM_PLL1_133M_CLK} + }, + {HDMI_APB_CLK_ROOT, BUS_CLOCK_SLICE, 6, + {OSC_24M_CLK, SYSTEM_PLL2_125M_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL3_CLK, SYSTEM_PLL1_40M_CLK, + AUDIO_PLL2_CLK, EXT_CLK_1, SYSTEM_PLL1_133M_CLK} + }, + {HDMI_AXI_CLK_ROOT, BUS_CLOCK_SLICE, 7, + {OSC_24M_CLK, SYSTEM_PLL2_1000M_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL3_CLK, SYSTEM_PLL1_40M_CLK, + AUDIO_PLL2_CLK, EXT_CLK_1, SYSTEM_PLL2_500M_CLK} + }, + {NOC_CLK_ROOT, BUS_CLOCK_SLICE, 10, + {OSC_24M_CLK, SYSTEM_PLL1_800M_CLK, SYSTEM_PLL3_CLK, + SYSTEM_PLL2_1000M_CLK, SYSTEM_PLL2_500M_CLK, + AUDIO_PLL1_CLK, VIDEO_PLL_CLK, AUDIO_PLL2_CLK} + }, + {NOC_IO_CLK_ROOT, BUS_CLOCK_SLICE, 11, + {OSC_24M_CLK, SYSTEM_PLL1_800M_CLK, SYSTEM_PLL3_CLK, + SYSTEM_PLL2_1000M_CLK, SYSTEM_PLL2_500M_CLK, + AUDIO_PLL1_CLK, VIDEO_PLL_CLK, AUDIO_PLL2_CLK} + }, + {ML_AXI_CLK_ROOT, BUS_CLOCK_SLICE, 12, + {OSC_24M_CLK, SYSTEM_PLL1_800M_CLK, GPU_PLL_CLK, + SYSTEM_PLL3_CLK, SYSTEM_PLL2_1000M_CLK, + AUDIO_PLL1_CLK, VIDEO_PLL_CLK, AUDIO_PLL2_CLK} + }, + {ML_AHB_CLK_ROOT, BUS_CLOCK_SLICE, 13, + {OSC_24M_CLK, SYSTEM_PLL1_800M_CLK, GPU_PLL_CLK, + SYSTEM_PLL3_CLK, SYSTEM_PLL2_1000M_CLK, + AUDIO_PLL1_CLK, VIDEO_PLL_CLK, AUDIO_PLL2_CLK} + }, + {AHB_CLK_ROOT, AHB_CLOCK_SLICE, 0, + {OSC_24M_CLK, SYSTEM_PLL1_133M_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL1_400M_CLK, SYSTEM_PLL2_125M_CLK, + SYSTEM_PLL3_CLK, AUDIO_PLL1_CLK, VIDEO_PLL_CLK} + }, + {MEDIA_DISP2_CLK_ROOT, AHB_CLOCK_SLICE, 3, + {OSC_24M_CLK, VIDEO_PLL_CLK, AUDIO_PLL2_CLK, + AUDIO_PLL1_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL2_1000M_CLK, SYSTEM_PLL3_CLK, EXT_CLK_4} + }, + {DRAM_ALT_CLK_ROOT, IP_CLOCK_SLICE, 0, + {OSC_24M_CLK, SYSTEM_PLL1_800M_CLK, SYSTEM_PLL1_100M_CLK, + SYSTEM_PLL2_500M_CLK, SYSTEM_PLL2_1000M_CLK, + SYSTEM_PLL3_CLK, AUDIO_PLL1_CLK, SYSTEM_PLL1_266M_CLK} + }, + {DRAM_APB_CLK_ROOT, IP_CLOCK_SLICE, 1, + {OSC_24M_CLK, SYSTEM_PLL2_200M_CLK, SYSTEM_PLL1_40M_CLK, + SYSTEM_PLL1_160M_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL3_CLK, SYSTEM_PLL2_250M_CLK, AUDIO_PLL2_CLK} + }, + {I2C5_CLK_ROOT, IP_CLOCK_SLICE, 9, + {OSC_24M_CLK, SYSTEM_PLL1_160M_CLK, SYSTEM_PLL2_50M_CLK, + SYSTEM_PLL3_CLK, AUDIO_PLL1_CLK, VIDEO_PLL_CLK, AUDIO_PLL2_CLK, + SYSTEM_PLL1_133M_CLK} + }, + {I2C6_CLK_ROOT, IP_CLOCK_SLICE, 10, + {OSC_24M_CLK, SYSTEM_PLL1_160M_CLK, SYSTEM_PLL2_50M_CLK, + SYSTEM_PLL3_CLK, AUDIO_PLL1_CLK, VIDEO_PLL_CLK, AUDIO_PLL2_CLK, + SYSTEM_PLL1_133M_CLK} + }, + {ENET_QOS_CLK_ROOT, IP_CLOCK_SLICE, 17, + {OSC_24M_CLK, SYSTEM_PLL2_125M_CLK, SYSTEM_PLL2_50M_CLK, + SYSTEM_PLL2_100M_CLK, SYSTEM_PLL1_160M_CLK, + AUDIO_PLL1_CLK, VIDEO_PLL_CLK, EXT_CLK_4} + }, + {ENET_QOS_TIMER_CLK_ROOT, IP_CLOCK_SLICE, 18, + {OSC_24M_CLK, SYSTEM_PLL2_100M_CLK, AUDIO_PLL1_CLK, EXT_CLK_1, + EXT_CLK_2, EXT_CLK_3, EXT_CLK_4, VIDEO_PLL_CLK} + }, + {ENET_REF_CLK_ROOT, IP_CLOCK_SLICE, 19, + {OSC_24M_CLK, SYSTEM_PLL2_125M_CLK, SYSTEM_PLL2_50M_CLK, + SYSTEM_PLL2_100M_CLK, SYSTEM_PLL1_160M_CLK, + AUDIO_PLL1_CLK, VIDEO_PLL_CLK, EXT_CLK_4} + }, + {ENET_TIMER_CLK_ROOT, IP_CLOCK_SLICE, 20, + {OSC_24M_CLK, SYSTEM_PLL2_100M_CLK, AUDIO_PLL1_CLK, EXT_CLK_1, + EXT_CLK_2, EXT_CLK_3, EXT_CLK_4, VIDEO_PLL_CLK} + }, + {ENET_PHY_REF_CLK_ROOT, IP_CLOCK_SLICE, 21, + {OSC_24M_CLK, SYSTEM_PLL2_50M_CLK, SYSTEM_PLL2_125M_CLK, + SYSTEM_PLL2_200M_CLK, SYSTEM_PLL2_500M_CLK, AUDIO_PLL1_CLK, + VIDEO_PLL_CLK, AUDIO_PLL2_CLK} + }, + {NAND_CLK_ROOT, IP_CLOCK_SLICE, 22, + {OSC_24M_CLK, SYSTEM_PLL2_500M_CLK, AUDIO_PLL1_CLK, + SYSTEM_PLL1_400M_CLK, AUDIO_PLL2_CLK, SYSTEM_PLL3_CLK, + SYSTEM_PLL2_250M_CLK, VIDEO_PLL_CLK} + }, + {QSPI_CLK_ROOT, IP_CLOCK_SLICE, 23, + {OSC_24M_CLK, SYSTEM_PLL1_400M_CLK, SYSTEM_PLL2_333M_CLK, + SYSTEM_PLL2_500M_CLK, AUDIO_PLL2_CLK, + SYSTEM_PLL1_266M_CLK, SYSTEM_PLL3_CLK, SYSTEM_PLL1_100M_CLK} + }, + {USDHC1_CLK_ROOT, IP_CLOCK_SLICE, 24, + {OSC_24M_CLK, SYSTEM_PLL1_400M_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL2_500M_CLK, SYSTEM_PLL3_CLK, + SYSTEM_PLL1_266M_CLK, AUDIO_PLL2_CLK, SYSTEM_PLL1_100M_CLK} + }, + {USDHC2_CLK_ROOT, IP_CLOCK_SLICE, 25, + {OSC_24M_CLK, SYSTEM_PLL1_400M_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL2_500M_CLK, SYSTEM_PLL3_CLK, + SYSTEM_PLL1_266M_CLK, AUDIO_PLL2_CLK, SYSTEM_PLL1_100M_CLK} + }, + {I2C1_CLK_ROOT, IP_CLOCK_SLICE, 26, + {OSC_24M_CLK, SYSTEM_PLL1_160M_CLK, SYSTEM_PLL2_50M_CLK, + SYSTEM_PLL3_CLK, AUDIO_PLL1_CLK, VIDEO_PLL_CLK, + AUDIO_PLL2_CLK, SYSTEM_PLL1_133M_CLK} + }, + {I2C2_CLK_ROOT, IP_CLOCK_SLICE, 27, + {OSC_24M_CLK, SYSTEM_PLL1_160M_CLK, SYSTEM_PLL2_50M_CLK, + SYSTEM_PLL3_CLK, AUDIO_PLL1_CLK, VIDEO_PLL_CLK, + AUDIO_PLL2_CLK, SYSTEM_PLL1_133M_CLK} + }, + {I2C3_CLK_ROOT, IP_CLOCK_SLICE, 28, + {OSC_24M_CLK, SYSTEM_PLL1_160M_CLK, SYSTEM_PLL2_50M_CLK, + SYSTEM_PLL3_CLK, AUDIO_PLL1_CLK, VIDEO_PLL_CLK, + AUDIO_PLL2_CLK, SYSTEM_PLL1_133M_CLK} + }, + {I2C4_CLK_ROOT, IP_CLOCK_SLICE, 29, + {OSC_24M_CLK, SYSTEM_PLL1_160M_CLK, SYSTEM_PLL2_50M_CLK, + SYSTEM_PLL3_CLK, AUDIO_PLL1_CLK, VIDEO_PLL_CLK, + AUDIO_PLL2_CLK, SYSTEM_PLL1_133M_CLK} + }, + {UART1_CLK_ROOT, IP_CLOCK_SLICE, 30, + {OSC_24M_CLK, SYSTEM_PLL1_80M_CLK, SYSTEM_PLL2_200M_CLK, + SYSTEM_PLL2_100M_CLK, SYSTEM_PLL3_CLK, + EXT_CLK_2, EXT_CLK_4, AUDIO_PLL2_CLK} + }, + {UART2_CLK_ROOT, IP_CLOCK_SLICE, 31, + {OSC_24M_CLK, SYSTEM_PLL1_80M_CLK, SYSTEM_PLL2_200M_CLK, + SYSTEM_PLL2_100M_CLK, SYSTEM_PLL3_CLK, + EXT_CLK_2, EXT_CLK_3, AUDIO_PLL2_CLK} + }, + {UART3_CLK_ROOT, IP_CLOCK_SLICE, 32, + {OSC_24M_CLK, SYSTEM_PLL1_80M_CLK, SYSTEM_PLL2_200M_CLK, + SYSTEM_PLL2_100M_CLK, SYSTEM_PLL3_CLK, + EXT_CLK_2, EXT_CLK_4, AUDIO_PLL2_CLK} + }, + {UART4_CLK_ROOT, IP_CLOCK_SLICE, 33, + {OSC_24M_CLK, SYSTEM_PLL1_80M_CLK, SYSTEM_PLL2_200M_CLK, + SYSTEM_PLL2_100M_CLK, SYSTEM_PLL3_CLK, + EXT_CLK_2, EXT_CLK_3, AUDIO_PLL2_CLK} + }, + {USB_PHY_REF_CLK_ROOT, IP_CLOCK_SLICE, 35, + {OSC_24M_CLK, SYSTEM_PLL1_100M_CLK, SYSTEM_PLL1_40M_CLK, + SYSTEM_PLL2_100M_CLK, SYSTEM_PLL2_200M_CLK, + EXT_CLK_2, EXT_CLK_3, AUDIO_PLL2_CLK} + }, + {GIC_CLK_ROOT, IP_CLOCK_SLICE, 36, + {OSC_24M_CLK, SYSTEM_PLL2_200M_CLK, SYSTEM_PLL1_40M_CLK, + SYSTEM_PLL2_100M_CLK, SYSTEM_PLL1_800M_CLK, + EXT_CLK_2, EXT_CLK_4, AUDIO_PLL2_CLK} + }, + {ECSPI1_CLK_ROOT, IP_CLOCK_SLICE, 37, + {OSC_24M_CLK, SYSTEM_PLL2_200M_CLK, SYSTEM_PLL1_40M_CLK, + SYSTEM_PLL1_160M_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL3_CLK, SYSTEM_PLL2_250M_CLK, AUDIO_PLL2_CLK} + }, + {ECSPI2_CLK_ROOT, IP_CLOCK_SLICE, 38, + {OSC_24M_CLK, SYSTEM_PLL2_200M_CLK, SYSTEM_PLL1_40M_CLK, + SYSTEM_PLL1_160M_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL3_CLK, SYSTEM_PLL2_250M_CLK, AUDIO_PLL2_CLK} + }, + {PWM1_CLK_ROOT, IP_CLOCK_SLICE, 39, + {OSC_24M_CLK, SYSTEM_PLL2_100M_CLK, SYSTEM_PLL1_160M_CLK, + SYSTEM_PLL1_40M_CLK, SYSTEM_PLL3_CLK, EXT_CLK_1, + SYSTEM_PLL1_80M_CLK, VIDEO_PLL_CLK} + }, + {PWM2_CLK_ROOT, IP_CLOCK_SLICE, 40, + {OSC_24M_CLK, SYSTEM_PLL2_100M_CLK, SYSTEM_PLL1_160M_CLK, + SYSTEM_PLL1_40M_CLK, SYSTEM_PLL3_CLK, EXT_CLK_1, + SYSTEM_PLL1_80M_CLK, VIDEO_PLL_CLK} + }, + {PWM3_CLK_ROOT, IP_CLOCK_SLICE, 41, + {OSC_24M_CLK, SYSTEM_PLL2_100M_CLK, SYSTEM_PLL1_160M_CLK, + SYSTEM_PLL1_40M_CLK, SYSTEM_PLL3_CLK, EXT_CLK_2, + SYSTEM_PLL1_80M_CLK, VIDEO_PLL_CLK} + }, + {PWM4_CLK_ROOT, IP_CLOCK_SLICE, 42, + {OSC_24M_CLK, SYSTEM_PLL2_100M_CLK, SYSTEM_PLL1_160M_CLK, + SYSTEM_PLL1_40M_CLK, SYSTEM_PLL3_CLK, EXT_CLK_2, + SYSTEM_PLL1_80M_CLK, VIDEO_PLL_CLK} + }, + {GPT1_CLK_ROOT, IP_CLOCK_SLICE, 43, + {OSC_24M_CLK, SYSTEM_PLL2_100M_CLK, SYSTEM_PLL1_400M_CLK, + SYSTEM_PLL1_40M_CLK, VIDEO_PLL_CLK, + SYSTEM_PLL1_80M_CLK, AUDIO_PLL1_CLK, EXT_CLK_1} + }, + {GPT2_CLK_ROOT, IP_CLOCK_SLICE, 44, + {OSC_24M_CLK, SYSTEM_PLL2_100M_CLK, SYSTEM_PLL1_400M_CLK, + SYSTEM_PLL1_40M_CLK, VIDEO_PLL_CLK, + SYSTEM_PLL1_80M_CLK, AUDIO_PLL1_CLK, EXT_CLK_2} + }, + {GPT3_CLK_ROOT, IP_CLOCK_SLICE, 45, + {OSC_24M_CLK, SYSTEM_PLL2_100M_CLK, SYSTEM_PLL1_400M_CLK, + SYSTEM_PLL1_40M_CLK, VIDEO_PLL_CLK, + SYSTEM_PLL1_80M_CLK, AUDIO_PLL1_CLK, EXT_CLK_3} + }, + {GPT4_CLK_ROOT, IP_CLOCK_SLICE, 46, + {OSC_24M_CLK, SYSTEM_PLL2_100M_CLK, SYSTEM_PLL1_400M_CLK, + SYSTEM_PLL1_40M_CLK, VIDEO_PLL_CLK, + SYSTEM_PLL1_80M_CLK, AUDIO_PLL1_CLK, EXT_CLK_1} + }, + {GPT5_CLK_ROOT, IP_CLOCK_SLICE, 47, + {OSC_24M_CLK, SYSTEM_PLL2_100M_CLK, SYSTEM_PLL1_400M_CLK, + SYSTEM_PLL1_40M_CLK, VIDEO_PLL_CLK, + SYSTEM_PLL1_80M_CLK, AUDIO_PLL1_CLK, EXT_CLK_2} + }, + {GPT6_CLK_ROOT, IP_CLOCK_SLICE, 48, + {OSC_24M_CLK, SYSTEM_PLL2_100M_CLK, SYSTEM_PLL1_400M_CLK, + SYSTEM_PLL1_40M_CLK, VIDEO_PLL_CLK, + SYSTEM_PLL1_80M_CLK, AUDIO_PLL1_CLK, EXT_CLK_3} + }, + {TRACE_CLK_ROOT, IP_CLOCK_SLICE, 49, + {OSC_24M_CLK, SYSTEM_PLL1_133M_CLK, SYSTEM_PLL1_160M_CLK, + VPU_PLL_CLK, SYSTEM_PLL2_125M_CLK, + SYSTEM_PLL3_CLK, EXT_CLK_1, EXT_CLK_3} + }, + {WDOG_CLK_ROOT, IP_CLOCK_SLICE, 50, + {OSC_24M_CLK, SYSTEM_PLL1_133M_CLK, SYSTEM_PLL1_160M_CLK, + VPU_PLL_CLK, SYSTEM_PLL2_125M_CLK, + SYSTEM_PLL3_CLK, SYSTEM_PLL1_80M_CLK, SYSTEM_PLL2_166M_CLK} + }, + {WRCLK_CLK_ROOT, IP_CLOCK_SLICE, 51, + {OSC_24M_CLK, SYSTEM_PLL1_40M_CLK, VPU_PLL_CLK, + SYSTEM_PLL3_CLK, SYSTEM_PLL2_200M_CLK, + SYSTEM_PLL1_266M_CLK, SYSTEM_PLL2_500M_CLK, SYSTEM_PLL1_100M_CLK} + }, + {HDMI_REF_266M_CLK_ROOT, IP_CLOCK_SLICE, 56, + {OSC_24M_CLK, SYSTEM_PLL1_400M_CLK, SYSTEM_PLL3_CLK, + SYSTEM_PLL2_333M_CLK, SYSTEM_PLL1_266M_CLK, + SYSTEM_PLL2_200M_CLK, AUDIO_PLL1_CLK, VIDEO_PLL_CLK} + }, + {USDHC3_CLK_ROOT, IP_CLOCK_SLICE, 57, + {OSC_24M_CLK, SYSTEM_PLL1_400M_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL2_500M_CLK, SYSTEM_PLL3_CLK, + SYSTEM_PLL1_266M_CLK, AUDIO_PLL2_CLK, SYSTEM_PLL1_100M_CLK} + }, + {MEDIA_MIPI_PHY1_REF_CLK_ROOT, IP_CLOCK_SLICE, 59, + {OSC_24M_CLK, SYSTEM_PLL2_333M_CLK, SYSTEM_PLL2_100M_CLK, + SYSTEM_PLL1_800M_CLK, SYSTEM_PLL2_1000M_CLK, + EXT_CLK_2, AUDIO_PLL2_CLK, VIDEO_PLL_CLK} + }, + {MEDIA_DISP1_PIX_CLK_ROOT, IP_CLOCK_SLICE, 60, + {OSC_24M_CLK, VIDEO_PLL_CLK, AUDIO_PLL2_CLK, + AUDIO_PLL1_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL2_1000M_CLK, SYSTEM_PLL3_CLK, EXT_CLK_4} + }, + {MEDIA_LDB_CLK_ROOT, IP_CLOCK_SLICE, 62, + {OSC_24M_CLK, SYSTEM_PLL2_333M_CLK, SYSTEM_PLL2_100M_CLK, + SYSTEM_PLL1_800M_CLK, SYSTEM_PLL2_1000M_CLK, + EXT_CLK_2, AUDIO_PLL2_CLK, VIDEO_PLL_CLK} + }, + {MEMREPAIR_CLK_ROOT, IP_CLOCK_SLICE, 63, + {OSC_24M_CLK, SYSTEM_PLL2_100M_CLK, SYSTEM_PLL1_80M_CLK, + SYSTEM_PLL1_800M_CLK, SYSTEM_PLL2_1000M_CLK, + SYSTEM_PLL3_CLK, EXT_CLK_3, AUDIO_PLL2_CLK} + }, + {ECSPI3_CLK_ROOT, IP_CLOCK_SLICE, 67, + {OSC_24M_CLK, SYSTEM_PLL2_200M_CLK, SYSTEM_PLL1_40M_CLK, + SYSTEM_PLL1_160M_CLK, SYSTEM_PLL1_800M_CLK, + SYSTEM_PLL3_CLK, SYSTEM_PLL2_250M_CLK, AUDIO_PLL2_CLK} + }, + {DRAM_SEL_CFG, DRAM_SEL_CLOCK_SLICE, 0, + {DRAM_PLL1_CLK} + }, + {CORE_SEL_CFG, CORE_SEL_CLOCK_SLICE, 0, + {ARM_A53_ALT_CLK, ARM_PLL_CLK} + }, +}; +#endif + +static int select(enum clk_root_index clock_id) +{ + int i, size; + struct clk_root_map *p = root_array; + + size = ARRAY_SIZE(root_array); + + for (i = 0; i < size; i++, p++) { + if (clock_id == p->entry) + return i; + } + + return -EINVAL; +} + +static void __iomem *get_clk_root_target(enum clk_slice_type slice_type, + u32 slice_index) +{ + void __iomem *clk_root_target; + + switch (slice_type) { + case CORE_CLOCK_SLICE: + clk_root_target = + (void __iomem *)&ccm_reg->core_root[slice_index]; + break; + case BUS_CLOCK_SLICE: + clk_root_target = + (void __iomem *)&ccm_reg->bus_root[slice_index]; + break; + case IP_CLOCK_SLICE: + clk_root_target = + (void __iomem *)&ccm_reg->ip_root[slice_index]; + break; + case AHB_CLOCK_SLICE: + clk_root_target = + (void __iomem *)&ccm_reg->ahb_ipg_root[slice_index * 2]; + break; + case IPG_CLOCK_SLICE: + clk_root_target = + (void __iomem *)&ccm_reg->ahb_ipg_root[slice_index * 2 + 1]; + break; + case CORE_SEL_CLOCK_SLICE: + clk_root_target = (void __iomem *)&ccm_reg->core_sel; + break; + case DRAM_SEL_CLOCK_SLICE: + clk_root_target = (void __iomem *)&ccm_reg->dram_sel; + break; + default: + return NULL; + } + + return clk_root_target; +} + +int clock_get_target_val(enum clk_root_index clock_id, u32 *val) +{ + int root_entry; + struct clk_root_map *p; + void __iomem *clk_root_target; + + if (clock_id >= CLK_ROOT_MAX) + return -EINVAL; + + root_entry = select(clock_id); + if (root_entry < 0) + return -EINVAL; + + p = &root_array[root_entry]; + clk_root_target = get_clk_root_target(p->slice_type, p->slice_index); + if (!clk_root_target) + return -EINVAL; + + *val = readl(clk_root_target); + + return 0; +} + +int clock_set_target_val(enum clk_root_index clock_id, u32 val) +{ + int root_entry; + struct clk_root_map *p; + void __iomem *clk_root_target; + + if (clock_id >= CLK_ROOT_MAX) + return -EINVAL; + + root_entry = select(clock_id); + if (root_entry < 0) + return -EINVAL; + + p = &root_array[root_entry]; + clk_root_target = get_clk_root_target(p->slice_type, p->slice_index); + if (!clk_root_target) + return -EINVAL; + + writel(val, clk_root_target); + + return 0; +} + +int clock_root_enabled(enum clk_root_index clock_id) +{ + void __iomem *clk_root_target; + u32 slice_index, slice_type; + u32 val; + int root_entry; + + if (clock_id >= CLK_ROOT_MAX) + return -EINVAL; + + root_entry = select(clock_id); + if (root_entry < 0) + return -EINVAL; + + slice_type = root_array[root_entry].slice_type; + slice_index = root_array[root_entry].slice_index; + + if ((slice_type == IPG_CLOCK_SLICE) || + (slice_type == DRAM_SEL_CLOCK_SLICE) || + (slice_type == CORE_SEL_CLOCK_SLICE)) { + /* + * Not supported, from CCM doc + * TODO + */ + return 0; + } + + clk_root_target = get_clk_root_target(slice_type, slice_index); + if (!clk_root_target) + return -EINVAL; + + val = readl(clk_root_target); + + return (val & CLK_ROOT_ON) ? 1 : 0; +} + +/* CCGR CLK gate operation */ +int clock_enable(enum clk_ccgr_index index, bool enable) +{ + void __iomem *ccgr; + + if (index >= CCGR_MAX) + return -EINVAL; + + if (enable) + ccgr = (void __iomem *)&ccm_reg->ccgr_array[index].ccgr_set; + else + ccgr = (void __iomem *)&ccm_reg->ccgr_array[index].ccgr_clr; + + writel(CCGR_CLK_ON_MASK, ccgr); + + return 0; +} + +int clock_get_prediv(enum clk_root_index clock_id, enum root_pre_div *pre_div) +{ + u32 val; + int root_entry; + struct clk_root_map *p; + void __iomem *clk_root_target; + + if (clock_id >= CLK_ROOT_MAX) + return -EINVAL; + + root_entry = select(clock_id); + if (root_entry < 0) + return -EINVAL; + + p = &root_array[root_entry]; + + if ((p->slice_type == CORE_CLOCK_SLICE) || + (p->slice_type == IPG_CLOCK_SLICE) || + (p->slice_type == CORE_SEL_CLOCK_SLICE) || + (p->slice_type == DRAM_SEL_CLOCK_SLICE)) { + *pre_div = 0; + return 0; + } + + clk_root_target = get_clk_root_target(p->slice_type, p->slice_index); + if (!clk_root_target) + return -EINVAL; + + val = readl(clk_root_target); + val &= CLK_ROOT_PRE_DIV_MASK; + val >>= CLK_ROOT_PRE_DIV_SHIFT; + + *pre_div = val; + + return 0; +} + +int clock_get_postdiv(enum clk_root_index clock_id, + enum root_post_div *post_div) +{ + u32 val, mask; + int root_entry; + struct clk_root_map *p; + void __iomem *clk_root_target; + + if (clock_id >= CLK_ROOT_MAX) + return -EINVAL; + + root_entry = select(clock_id); + if (root_entry < 0) + return -EINVAL; + + p = &root_array[root_entry]; + + if ((p->slice_type == CORE_SEL_CLOCK_SLICE) || + (p->slice_type == DRAM_SEL_CLOCK_SLICE)) { + *post_div = 0; + return 0; + } + + clk_root_target = get_clk_root_target(p->slice_type, p->slice_index); + if (!clk_root_target) + return -EINVAL; + + if (p->slice_type == IPG_CLOCK_SLICE) + mask = CLK_ROOT_IPG_POST_DIV_MASK; + else if (p->slice_type == CORE_CLOCK_SLICE) + mask = CLK_ROOT_CORE_POST_DIV_MASK; + else + mask = CLK_ROOT_POST_DIV_MASK; + + val = readl(clk_root_target); + val &= mask; + val >>= CLK_ROOT_POST_DIV_SHIFT; + + *post_div = val; + + return 0; +} + +int clock_get_src(enum clk_root_index clock_id, enum clk_root_src *p_clock_src) +{ + u32 val; + int root_entry; + struct clk_root_map *p; + void __iomem *clk_root_target; + + if (clock_id >= CLK_ROOT_MAX) + return -EINVAL; + + root_entry = select(clock_id); + if (root_entry < 0) + return -EINVAL; + + p = &root_array[root_entry]; + + clk_root_target = get_clk_root_target(p->slice_type, p->slice_index); + if (!clk_root_target) + return -EINVAL; + + val = readl(clk_root_target); + val &= CLK_ROOT_SRC_MUX_MASK; + val >>= CLK_ROOT_SRC_MUX_SHIFT; + + *p_clock_src = p->src_mux[val]; + + return 0; +} diff --git a/roms/u-boot/arch/arm/mach-imx/imx8m/imximage-8mm-lpddr4.cfg b/roms/u-boot/arch/arm/mach-imx/imx8m/imximage-8mm-lpddr4.cfg new file mode 100644 index 000000000..1a2e43e67 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/imx8m/imximage-8mm-lpddr4.cfg @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2019 NXP + */ + +#define __ASSEMBLY__ + +FIT +BOOT_FROM sd +LOADER spl/u-boot-spl-ddr.bin 0x7E1000 +SECOND_LOADER u-boot.itb 0x40200000 0x60000 + +DDR_FW lpddr4_pmu_train_1d_imem.bin +DDR_FW lpddr4_pmu_train_1d_dmem.bin +DDR_FW lpddr4_pmu_train_2d_imem.bin +DDR_FW lpddr4_pmu_train_2d_dmem.bin diff --git a/roms/u-boot/arch/arm/mach-imx/imx8m/imximage-8mn-ddr4.cfg b/roms/u-boot/arch/arm/mach-imx/imx8m/imximage-8mn-ddr4.cfg new file mode 100644 index 000000000..1405c6560 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/imx8m/imximage-8mn-ddr4.cfg @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2019 NXP + */ + +#define __ASSEMBLY__ + +FIT +ROM_VERSION v2 +BOOT_FROM sd +LOADER spl/u-boot-spl-ddr.bin 0x912000 +SECOND_LOADER u-boot.itb 0x40200000 0x60000 + +DDR_FW ddr4_imem_1d.bin +DDR_FW ddr4_dmem_1d.bin +DDR_FW ddr4_imem_2d.bin +DDR_FW ddr4_dmem_2d.bin diff --git a/roms/u-boot/arch/arm/mach-imx/imx8m/imximage-8mn-lpddr4.cfg b/roms/u-boot/arch/arm/mach-imx/imx8m/imximage-8mn-lpddr4.cfg new file mode 100644 index 000000000..4c63b31db --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/imx8m/imximage-8mn-lpddr4.cfg @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2019 NXP + */ + +#define __ASSEMBLY__ + +FIT +ROM_VERSION v2 +BOOT_FROM sd +LOADER spl/u-boot-spl-ddr.bin 0x912000 +SECOND_LOADER u-boot.itb 0x40200000 0x60000 + +DDR_FW lpddr4_pmu_train_1d_imem.bin +DDR_FW lpddr4_pmu_train_1d_dmem.bin +DDR_FW lpddr4_pmu_train_2d_imem.bin +DDR_FW lpddr4_pmu_train_2d_dmem.bin diff --git a/roms/u-boot/arch/arm/mach-imx/imx8m/imximage-8mp-lpddr4.cfg b/roms/u-boot/arch/arm/mach-imx/imx8m/imximage-8mp-lpddr4.cfg new file mode 100644 index 000000000..586a5ff30 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/imx8m/imximage-8mp-lpddr4.cfg @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2019 NXP + */ + +#define __ASSEMBLY__ + +FIT +ROM_VERSION v2 +BOOT_FROM sd +LOADER spl/u-boot-spl-ddr.bin 0x920000 +SECOND_LOADER u-boot.itb 0x40200000 0x60000 + +DDR_FW lpddr4_pmu_train_1d_imem.bin +DDR_FW lpddr4_pmu_train_1d_dmem.bin +DDR_FW lpddr4_pmu_train_2d_imem.bin +DDR_FW lpddr4_pmu_train_2d_dmem.bin diff --git a/roms/u-boot/arch/arm/mach-imx/imx8m/imximage.cfg b/roms/u-boot/arch/arm/mach-imx/imx8m/imximage.cfg new file mode 100644 index 000000000..714b24273 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/imx8m/imximage.cfg @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2018 NXP + */ + +#define __ASSEMBLY__ + +FIT +BOOT_FROM sd +SIGNED_HDMI signed_hdmi_imx8m.bin +LOADER spl/u-boot-spl-ddr.bin 0x7E1000 +SECOND_LOADER u-boot.itb 0x40200000 0x60000 + +DDR_FW lpddr4_pmu_train_1d_imem.bin +DDR_FW lpddr4_pmu_train_1d_dmem.bin +DDR_FW lpddr4_pmu_train_2d_imem.bin +DDR_FW lpddr4_pmu_train_2d_dmem.bin diff --git a/roms/u-boot/arch/arm/mach-imx/imx8m/lowlevel_init.S b/roms/u-boot/arch/arm/mach-imx/imx8m/lowlevel_init.S new file mode 100644 index 000000000..dd263c406 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/imx8m/lowlevel_init.S @@ -0,0 +1,74 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2017 NXP + */ + +#include <config.h> + +.align 4 +.global rom_pointer +rom_pointer: + .space 256 + +/* + * Routine: save_boot_params (called after reset from start.S) + */ + +.global save_boot_params +save_boot_params: + /* The firmware provided ATAG/FDT address can be found in r2/x0 */ + adr x0, rom_pointer + stp x1, x2, [x0], #16 + stp x3, x4, [x0], #16 + stp x5, x6, [x0], #16 + stp x7, x8, [x0], #16 + stp x9, x10, [x0], #16 + stp x11, x12, [x0], #16 + stp x13, x14, [x0], #16 + stp x15, x16, [x0], #16 + stp x17, x18, [x0], #16 + stp x19, x20, [x0], #16 + stp x21, x22, [x0], #16 + stp x23, x24, [x0], #16 + stp x25, x26, [x0], #16 + stp x27, x28, [x0], #16 + stp x29, x30, [x0], #16 + mov x30, sp + str x30, [x0], #8 + + /* Returns */ + b save_boot_params_ret + +.global restore_boot_params +restore_boot_params: + adr x0, rom_pointer + ldp x1, x2, [x0], #16 + ldp x3, x4, [x0], #16 + ldp x5, x6, [x0], #16 + ldp x7, x8, [x0], #16 + ldp x9, x10, [x0], #16 + ldp x11, x12, [x0], #16 + ldp x13, x14, [x0], #16 + ldp x15, x16, [x0], #16 + ldp x17, x18, [x0], #16 + ldp x19, x20, [x0], #16 + ldp x21, x22, [x0], #16 + ldp x23, x24, [x0], #16 + ldp x25, x26, [x0], #16 + ldp x27, x28, [x0], #16 + ldp x29, x30, [x0], #16 + ldr x0, [x0] + mov sp, x0 + ret + +.global armv8_el2_to_aarch32 +armv8_el2_to_aarch32: + cmp x0, #0 + bne 0f + mov x3, x2 + mov x2, x1 + mov x1, x4 + ldr x0, =0xc20000fd +0: + smc #0 + ret diff --git a/roms/u-boot/arch/arm/mach-imx/imx8m/soc.c b/roms/u-boot/arch/arm/mach-imx/imx8m/soc.c new file mode 100644 index 000000000..0c44022a6 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/imx8m/soc.c @@ -0,0 +1,1285 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2017-2019 NXP + * + * Peng Fan <peng.fan@nxp.com> + */ + +#include <common.h> +#include <cpu_func.h> +#include <init.h> +#include <log.h> +#include <asm/arch/imx-regs.h> +#include <asm/global_data.h> +#include <asm/io.h> +#include <asm/arch/clock.h> +#include <asm/arch/sys_proto.h> +#include <asm/mach-imx/hab.h> +#include <asm/mach-imx/boot_mode.h> +#include <asm/mach-imx/syscounter.h> +#include <asm/ptrace.h> +#include <asm/armv8/mmu.h> +#include <dm/uclass.h> +#include <efi_loader.h> +#include <env.h> +#include <env_internal.h> +#include <errno.h> +#include <fdt_support.h> +#include <fsl_wdog.h> +#include <imx_sip.h> +#include <linux/arm-smccc.h> +#include <linux/bitops.h> + +DECLARE_GLOBAL_DATA_PTR; + +#if defined(CONFIG_IMX_HAB) +struct imx_sec_config_fuse_t const imx_sec_config_fuse = { + .bank = 1, + .word = 3, +}; +#endif + +int timer_init(void) +{ +#ifdef CONFIG_SPL_BUILD + struct sctr_regs *sctr = (struct sctr_regs *)SYSCNT_CTRL_BASE_ADDR; + unsigned long freq = readl(&sctr->cntfid0); + + /* Update with accurate clock frequency */ + asm volatile("msr cntfrq_el0, %0" : : "r" (freq) : "memory"); + + clrsetbits_le32(&sctr->cntcr, SC_CNTCR_FREQ0 | SC_CNTCR_FREQ1, + SC_CNTCR_FREQ0 | SC_CNTCR_ENABLE | SC_CNTCR_HDBG); +#endif + + gd->arch.tbl = 0; + gd->arch.tbu = 0; + + return 0; +} + +void enable_tzc380(void) +{ + struct iomuxc_gpr_base_regs *gpr = + (struct iomuxc_gpr_base_regs *)IOMUXC_GPR_BASE_ADDR; + + /* Enable TZASC and lock setting */ + setbits_le32(&gpr->gpr[10], GPR_TZASC_EN); + setbits_le32(&gpr->gpr[10], GPR_TZASC_EN_LOCK); + if (is_imx8mm() || is_imx8mn() || is_imx8mp()) + setbits_le32(&gpr->gpr[10], BIT(1)); + /* + * set Region 0 attribute to allow secure and non-secure + * read/write permission. Found some masters like usb dwc3 + * controllers can't work with secure memory. + */ + writel(0xf0000000, TZASC_BASE_ADDR + 0x108); +} + +void set_wdog_reset(struct wdog_regs *wdog) +{ + /* + * Output WDOG_B signal to reset external pmic or POR_B decided by + * the board design. Without external reset, the peripherals/DDR/ + * PMIC are not reset, that may cause system working abnormal. + * WDZST bit is write-once only bit. Align this bit in kernel, + * otherwise kernel code will have no chance to set this bit. + */ + setbits_le16(&wdog->wcr, WDOG_WDT_MASK | WDOG_WDZST_MASK); +} + +static struct mm_region imx8m_mem_map[] = { + { + /* ROM */ + .virt = 0x0UL, + .phys = 0x0UL, + .size = 0x100000UL, + .attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) | + PTE_BLOCK_OUTER_SHARE + }, { + /* CAAM */ + .virt = 0x100000UL, + .phys = 0x100000UL, + .size = 0x8000UL, + .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | + PTE_BLOCK_NON_SHARE | + PTE_BLOCK_PXN | PTE_BLOCK_UXN + }, { + /* OCRAM_S */ + .virt = 0x180000UL, + .phys = 0x180000UL, + .size = 0x8000UL, + .attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) | + PTE_BLOCK_OUTER_SHARE + }, { + /* TCM */ + .virt = 0x7C0000UL, + .phys = 0x7C0000UL, + .size = 0x80000UL, + .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | + PTE_BLOCK_NON_SHARE | + PTE_BLOCK_PXN | PTE_BLOCK_UXN + }, { + /* OCRAM */ + .virt = 0x900000UL, + .phys = 0x900000UL, + .size = 0x200000UL, + .attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) | + PTE_BLOCK_OUTER_SHARE + }, { + /* AIPS */ + .virt = 0xB00000UL, + .phys = 0xB00000UL, + .size = 0x3f500000UL, + .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | + PTE_BLOCK_NON_SHARE | + PTE_BLOCK_PXN | PTE_BLOCK_UXN + }, { + /* DRAM1 */ + .virt = 0x40000000UL, + .phys = 0x40000000UL, + .size = PHYS_SDRAM_SIZE, + .attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) | + PTE_BLOCK_OUTER_SHARE +#ifdef PHYS_SDRAM_2_SIZE + }, { + /* DRAM2 */ + .virt = 0x100000000UL, + .phys = 0x100000000UL, + .size = PHYS_SDRAM_2_SIZE, + .attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) | + PTE_BLOCK_OUTER_SHARE +#endif + }, { + /* empty entrie to split table entry 5 if needed when TEEs are used */ + 0, + }, { + /* List terminator */ + 0, + } +}; + +struct mm_region *mem_map = imx8m_mem_map; + +static unsigned int imx8m_find_dram_entry_in_mem_map(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(imx8m_mem_map); i++) + if (imx8m_mem_map[i].phys == CONFIG_SYS_SDRAM_BASE) + return i; + + hang(); /* Entry not found, this must never happen. */ +} + +void enable_caches(void) +{ + /* If OPTEE runs, remove OPTEE memory from MMU table to avoid speculative prefetch */ + if (rom_pointer[1]) { + /* + * TEE are loaded, So the ddr bank structures + * have been modified update mmu table accordingly + */ + int i = 0; + /* + * please make sure that entry initial value matches + * imx8m_mem_map for DRAM1 + */ + int entry = imx8m_find_dram_entry_in_mem_map(); + u64 attrs = imx8m_mem_map[entry].attrs; + + while (i < CONFIG_NR_DRAM_BANKS && + entry < ARRAY_SIZE(imx8m_mem_map)) { + if (gd->bd->bi_dram[i].start == 0) + break; + imx8m_mem_map[entry].phys = gd->bd->bi_dram[i].start; + imx8m_mem_map[entry].virt = gd->bd->bi_dram[i].start; + imx8m_mem_map[entry].size = gd->bd->bi_dram[i].size; + imx8m_mem_map[entry].attrs = attrs; + debug("Added memory mapping (%d): %llx %llx\n", entry, + imx8m_mem_map[entry].phys, imx8m_mem_map[entry].size); + i++; entry++; + } + } + + icache_enable(); + dcache_enable(); +} + +__weak int board_phys_sdram_size(phys_size_t *size) +{ + if (!size) + return -EINVAL; + + *size = PHYS_SDRAM_SIZE; + return 0; +} + +int dram_init(void) +{ + unsigned int entry = imx8m_find_dram_entry_in_mem_map(); + phys_size_t sdram_size; + int ret; + + ret = board_phys_sdram_size(&sdram_size); + if (ret) + return ret; + + /* rom_pointer[1] contains the size of TEE occupies */ + if (rom_pointer[1]) + gd->ram_size = sdram_size - rom_pointer[1]; + else + gd->ram_size = sdram_size; + + /* also update the SDRAM size in the mem_map used externally */ + imx8m_mem_map[entry].size = sdram_size; + +#ifdef PHYS_SDRAM_2_SIZE + gd->ram_size += PHYS_SDRAM_2_SIZE; +#endif + + return 0; +} + +int dram_init_banksize(void) +{ + int bank = 0; + int ret; + phys_size_t sdram_size; + + ret = board_phys_sdram_size(&sdram_size); + if (ret) + return ret; + + gd->bd->bi_dram[bank].start = PHYS_SDRAM; + if (rom_pointer[1]) { + phys_addr_t optee_start = (phys_addr_t)rom_pointer[0]; + phys_size_t optee_size = (size_t)rom_pointer[1]; + + gd->bd->bi_dram[bank].size = optee_start - gd->bd->bi_dram[bank].start; + if ((optee_start + optee_size) < (PHYS_SDRAM + sdram_size)) { + if (++bank >= CONFIG_NR_DRAM_BANKS) { + puts("CONFIG_NR_DRAM_BANKS is not enough\n"); + return -1; + } + + gd->bd->bi_dram[bank].start = optee_start + optee_size; + gd->bd->bi_dram[bank].size = PHYS_SDRAM + + sdram_size - gd->bd->bi_dram[bank].start; + } + } else { + gd->bd->bi_dram[bank].size = sdram_size; + } + +#ifdef PHYS_SDRAM_2_SIZE + if (++bank >= CONFIG_NR_DRAM_BANKS) { + puts("CONFIG_NR_DRAM_BANKS is not enough for SDRAM_2\n"); + return -1; + } + gd->bd->bi_dram[bank].start = PHYS_SDRAM_2; + gd->bd->bi_dram[bank].size = PHYS_SDRAM_2_SIZE; +#endif + + return 0; +} + +phys_size_t get_effective_memsize(void) +{ + /* return the first bank as effective memory */ + if (rom_pointer[1]) + return ((phys_addr_t)rom_pointer[0] - PHYS_SDRAM); + +#ifdef PHYS_SDRAM_2_SIZE + return gd->ram_size - PHYS_SDRAM_2_SIZE; +#else + return gd->ram_size; +#endif +} + +static u32 get_cpu_variant_type(u32 type) +{ + struct ocotp_regs *ocotp = (struct ocotp_regs *)OCOTP_BASE_ADDR; + struct fuse_bank *bank = &ocotp->bank[1]; + struct fuse_bank1_regs *fuse = + (struct fuse_bank1_regs *)bank->fuse_regs; + + u32 value = readl(&fuse->tester4); + + if (type == MXC_CPU_IMX8MQ) { + if ((value & 0x3) == 0x2) + return MXC_CPU_IMX8MD; + else if (value & 0x200000) + return MXC_CPU_IMX8MQL; + + } else if (type == MXC_CPU_IMX8MM) { + switch (value & 0x3) { + case 2: + if (value & 0x1c0000) + return MXC_CPU_IMX8MMDL; + else + return MXC_CPU_IMX8MMD; + case 3: + if (value & 0x1c0000) + return MXC_CPU_IMX8MMSL; + else + return MXC_CPU_IMX8MMS; + default: + if (value & 0x1c0000) + return MXC_CPU_IMX8MML; + break; + } + } else if (type == MXC_CPU_IMX8MN) { + switch (value & 0x3) { + case 2: + if (value & 0x1000000) { + if (value & 0x10000000) /* MIPI DSI */ + return MXC_CPU_IMX8MNUD; + else + return MXC_CPU_IMX8MNDL; + } else { + return MXC_CPU_IMX8MND; + } + case 3: + if (value & 0x1000000) { + if (value & 0x10000000) /* MIPI DSI */ + return MXC_CPU_IMX8MNUS; + else + return MXC_CPU_IMX8MNSL; + } else { + return MXC_CPU_IMX8MNS; + } + default: + if (value & 0x1000000) { + if (value & 0x10000000) /* MIPI DSI */ + return MXC_CPU_IMX8MNUQ; + else + return MXC_CPU_IMX8MNL; + } + break; + } + } else if (type == MXC_CPU_IMX8MP) { + u32 value0 = readl(&fuse->tester3); + u32 flag = 0; + + if ((value0 & 0xc0000) == 0x80000) + return MXC_CPU_IMX8MPD; + + /* vpu disabled */ + if ((value0 & 0x43000000) == 0x43000000) + flag = 1; + + /* npu disabled*/ + if ((value & 0x8) == 0x8) + flag |= (1 << 1); + + /* isp disabled */ + if ((value & 0x3) == 0x3) + flag |= (1 << 2); + + switch (flag) { + case 7: + return MXC_CPU_IMX8MPL; + case 2: + return MXC_CPU_IMX8MP6; + default: + break; + } + + } + + return type; +} + +u32 get_cpu_rev(void) +{ + struct anamix_pll *ana_pll = (struct anamix_pll *)ANATOP_BASE_ADDR; + u32 reg = readl(&ana_pll->digprog); + u32 type = (reg >> 16) & 0xff; + u32 major_low = (reg >> 8) & 0xff; + u32 rom_version; + + reg &= 0xff; + + /* iMX8MP */ + if (major_low == 0x43) { + type = get_cpu_variant_type(MXC_CPU_IMX8MP); + } else if (major_low == 0x42) { + /* iMX8MN */ + type = get_cpu_variant_type(MXC_CPU_IMX8MN); + } else if (major_low == 0x41) { + type = get_cpu_variant_type(MXC_CPU_IMX8MM); + } else { + if (reg == CHIP_REV_1_0) { + /* + * For B0 chip, the DIGPROG is not updated, + * it is still TO1.0. we have to check ROM + * version or OCOTP_READ_FUSE_DATA. + * 0xff0055aa is magic number for B1. + */ + if (readl((void __iomem *)(OCOTP_BASE_ADDR + 0x40)) == 0xff0055aa) { + /* + * B2 uses same DIGPROG and OCOTP_READ_FUSE_DATA value with B1, + * so have to check ROM to distinguish them + */ + rom_version = readl((void __iomem *)ROM_VERSION_B0); + rom_version &= 0xff; + if (rom_version == CHIP_REV_2_2) + reg = CHIP_REV_2_2; + else + reg = CHIP_REV_2_1; + } else { + rom_version = + readl((void __iomem *)ROM_VERSION_A0); + if (rom_version != CHIP_REV_1_0) { + rom_version = readl((void __iomem *)ROM_VERSION_B0); + rom_version &= 0xff; + if (rom_version == CHIP_REV_2_0) + reg = CHIP_REV_2_0; + } + } + } + + type = get_cpu_variant_type(type); + } + + return (type << 12) | reg; +} + +static void imx_set_wdog_powerdown(bool enable) +{ + struct wdog_regs *wdog1 = (struct wdog_regs *)WDOG1_BASE_ADDR; + struct wdog_regs *wdog2 = (struct wdog_regs *)WDOG2_BASE_ADDR; + struct wdog_regs *wdog3 = (struct wdog_regs *)WDOG3_BASE_ADDR; + + /* Write to the PDE (Power Down Enable) bit */ + writew(enable, &wdog1->wmcr); + writew(enable, &wdog2->wmcr); + writew(enable, &wdog3->wmcr); +} + +int arch_cpu_init_dm(void) +{ + struct udevice *dev; + int ret; + + if (CONFIG_IS_ENABLED(CLK)) { + ret = uclass_get_device_by_name(UCLASS_CLK, + "clock-controller@30380000", + &dev); + if (ret < 0) { + printf("Failed to find clock node. Check device tree\n"); + return ret; + } + } + + return 0; +} + +int arch_cpu_init(void) +{ + struct ocotp_regs *ocotp = (struct ocotp_regs *)OCOTP_BASE_ADDR; + /* + * ROM might disable clock for SCTR, + * enable the clock before timer_init. + */ + if (IS_ENABLED(CONFIG_SPL_BUILD)) + clock_enable(CCGR_SCTR, 1); + /* + * Init timer at very early state, because sscg pll setting + * will use it + */ + timer_init(); + + if (IS_ENABLED(CONFIG_SPL_BUILD)) { + clock_init(); + imx_set_wdog_powerdown(false); + + if (is_imx8md() || is_imx8mmd() || is_imx8mmdl() || is_imx8mms() || + is_imx8mmsl() || is_imx8mnd() || is_imx8mndl() || is_imx8mns() || + is_imx8mnsl() || is_imx8mpd() || is_imx8mnud() || is_imx8mnus()) { + /* Power down cpu core 1, 2 and 3 for iMX8M Dual core or Single core */ + struct pgc_reg *pgc_core1 = (struct pgc_reg *)(GPC_BASE_ADDR + 0x840); + struct pgc_reg *pgc_core2 = (struct pgc_reg *)(GPC_BASE_ADDR + 0x880); + struct pgc_reg *pgc_core3 = (struct pgc_reg *)(GPC_BASE_ADDR + 0x8C0); + struct gpc_reg *gpc = (struct gpc_reg *)GPC_BASE_ADDR; + + writel(0x1, &pgc_core2->pgcr); + writel(0x1, &pgc_core3->pgcr); + if (is_imx8mms() || is_imx8mmsl() || is_imx8mns() || is_imx8mnsl() || is_imx8mnus()) { + writel(0x1, &pgc_core1->pgcr); + writel(0xE, &gpc->cpu_pgc_dn_trg); + } else { + writel(0xC, &gpc->cpu_pgc_dn_trg); + } + } + } + + if (is_imx8mq()) { + clock_enable(CCGR_OCOTP, 1); + if (readl(&ocotp->ctrl) & 0x200) + writel(0x200, &ocotp->ctrl_clr); + } + + return 0; +} + +#if defined(CONFIG_IMX8MN) || defined(CONFIG_IMX8MP) +struct rom_api *g_rom_api = (struct rom_api *)0x980; + +enum boot_device get_boot_device(void) +{ + volatile gd_t *pgd = gd; + int ret; + u32 boot; + u16 boot_type; + u8 boot_instance; + enum boot_device boot_dev = SD1_BOOT; + + ret = g_rom_api->query_boot_infor(QUERY_BT_DEV, &boot, + ((uintptr_t)&boot) ^ QUERY_BT_DEV); + set_gd(pgd); + + if (ret != ROM_API_OKAY) { + puts("ROMAPI: failure at query_boot_info\n"); + return -1; + } + + boot_type = boot >> 16; + boot_instance = (boot >> 8) & 0xff; + + switch (boot_type) { + case BT_DEV_TYPE_SD: + boot_dev = boot_instance + SD1_BOOT; + break; + case BT_DEV_TYPE_MMC: + boot_dev = boot_instance + MMC1_BOOT; + break; + case BT_DEV_TYPE_NAND: + boot_dev = NAND_BOOT; + break; + case BT_DEV_TYPE_FLEXSPINOR: + boot_dev = QSPI_BOOT; + break; + case BT_DEV_TYPE_USB: + boot_dev = USB_BOOT; + break; + default: + break; + } + + return boot_dev; +} +#endif + +bool is_usb_boot(void) +{ + return get_boot_device() == USB_BOOT; +} + +#ifdef CONFIG_OF_SYSTEM_SETUP +bool check_fdt_new_path(void *blob) +{ + const char *soc_path = "/soc@0"; + int nodeoff; + + nodeoff = fdt_path_offset(blob, soc_path); + if (nodeoff < 0) + return false; + + return true; +} + +static int disable_fdt_nodes(void *blob, const char *const nodes_path[], int size_array) +{ + int i = 0; + int rc; + int nodeoff; + const char *status = "disabled"; + + for (i = 0; i < size_array; i++) { + nodeoff = fdt_path_offset(blob, nodes_path[i]); + if (nodeoff < 0) + continue; /* Not found, skip it */ + + printf("Found %s node\n", nodes_path[i]); + +add_status: + rc = fdt_setprop(blob, nodeoff, "status", status, strlen(status) + 1); + if (rc) { + if (rc == -FDT_ERR_NOSPACE) { + rc = fdt_increase_size(blob, 512); + if (!rc) + goto add_status; + } + printf("Unable to update property %s:%s, err=%s\n", + nodes_path[i], "status", fdt_strerror(rc)); + } else { + printf("Modify %s:%s disabled\n", + nodes_path[i], "status"); + } + } + + return 0; +} + +#ifdef CONFIG_IMX8MQ +bool check_dcss_fused(void) +{ + struct ocotp_regs *ocotp = (struct ocotp_regs *)OCOTP_BASE_ADDR; + struct fuse_bank *bank = &ocotp->bank[1]; + struct fuse_bank1_regs *fuse = + (struct fuse_bank1_regs *)bank->fuse_regs; + u32 value = readl(&fuse->tester4); + + if (value & 0x4000000) + return true; + + return false; +} + +static int disable_mipi_dsi_nodes(void *blob) +{ + static const char * const nodes_path[] = { + "/mipi_dsi@30A00000", + "/mipi_dsi_bridge@30A00000", + "/dsi_phy@30A00300", + "/soc@0/bus@30800000/mipi_dsi@30a00000", + "/soc@0/bus@30800000/dphy@30a00300", + "/soc@0/bus@30800000/mipi-dsi@30a00000", + }; + + return disable_fdt_nodes(blob, nodes_path, ARRAY_SIZE(nodes_path)); +} + +static int disable_dcss_nodes(void *blob) +{ + static const char * const nodes_path[] = { + "/dcss@0x32e00000", + "/dcss@32e00000", + "/hdmi@32c00000", + "/hdmi_cec@32c33800", + "/hdmi_drm@32c00000", + "/display-subsystem", + "/sound-hdmi", + "/sound-hdmi-arc", + "/soc@0/bus@32c00000/display-controller@32e00000", + "/soc@0/bus@32c00000/hdmi@32c00000", + }; + + return disable_fdt_nodes(blob, nodes_path, ARRAY_SIZE(nodes_path)); +} + +static int check_mipi_dsi_nodes(void *blob) +{ + static const char * const lcdif_path[] = { + "/lcdif@30320000", + "/soc@0/bus@30000000/lcdif@30320000", + "/soc@0/bus@30000000/lcd-controller@30320000" + }; + static const char * const mipi_dsi_path[] = { + "/mipi_dsi@30A00000", + "/soc@0/bus@30800000/mipi_dsi@30a00000" + }; + static const char * const lcdif_ep_path[] = { + "/lcdif@30320000/port@0/mipi-dsi-endpoint", + "/soc@0/bus@30000000/lcdif@30320000/port@0/endpoint", + "/soc@0/bus@30000000/lcd-controller@30320000/port@0/endpoint" + }; + static const char * const mipi_dsi_ep_path[] = { + "/mipi_dsi@30A00000/port@1/endpoint", + "/soc@0/bus@30800000/mipi_dsi@30a00000/ports/port@0/endpoint", + "/soc@0/bus@30800000/mipi-dsi@30a00000/ports/port@0/endpoint@0" + }; + + int lookup_node; + int nodeoff; + bool new_path = check_fdt_new_path(blob); + int i = new_path ? 1 : 0; + + nodeoff = fdt_path_offset(blob, lcdif_path[i]); + if (nodeoff < 0 || !fdtdec_get_is_enabled(blob, nodeoff)) { + /* + * If can't find lcdif node or lcdif node is disabled, + * then disable all mipi dsi, since they only can input + * from DCSS + */ + return disable_mipi_dsi_nodes(blob); + } + + nodeoff = fdt_path_offset(blob, mipi_dsi_path[i]); + if (nodeoff < 0 || !fdtdec_get_is_enabled(blob, nodeoff)) + return 0; + + nodeoff = fdt_path_offset(blob, lcdif_ep_path[i]); + if (nodeoff < 0) { + /* + * If can't find lcdif endpoint, then disable all mipi dsi, + * since they only can input from DCSS + */ + return disable_mipi_dsi_nodes(blob); + } + + lookup_node = fdtdec_lookup_phandle(blob, nodeoff, "remote-endpoint"); + nodeoff = fdt_path_offset(blob, mipi_dsi_ep_path[i]); + + if (nodeoff > 0 && nodeoff == lookup_node) + return 0; + + return disable_mipi_dsi_nodes(blob); +} +#endif + +int disable_vpu_nodes(void *blob) +{ + static const char * const nodes_path_8mq[] = { + "/vpu@38300000", + "/soc@0/vpu@38300000" + }; + + static const char * const nodes_path_8mm[] = { + "/vpu_g1@38300000", + "/vpu_g2@38310000", + "/vpu_h1@38320000" + }; + + static const char * const nodes_path_8mp[] = { + "/vpu_g1@38300000", + "/vpu_g2@38310000", + "/vpu_vc8000e@38320000" + }; + + if (is_imx8mq()) + return disable_fdt_nodes(blob, nodes_path_8mq, ARRAY_SIZE(nodes_path_8mq)); + else if (is_imx8mm()) + return disable_fdt_nodes(blob, nodes_path_8mm, ARRAY_SIZE(nodes_path_8mm)); + else if (is_imx8mp()) + return disable_fdt_nodes(blob, nodes_path_8mp, ARRAY_SIZE(nodes_path_8mp)); + else + return -EPERM; +} + +#ifdef CONFIG_IMX8MN_LOW_DRIVE_MODE +static int low_drive_gpu_freq(void *blob) +{ + static const char *nodes_path_8mn[] = { + "/gpu@38000000", + "/soc@0/gpu@38000000" + }; + + int nodeoff, cnt, i; + u32 assignedclks[7]; + + nodeoff = fdt_path_offset(blob, nodes_path_8mn[0]); + if (nodeoff < 0) + return nodeoff; + + cnt = fdtdec_get_int_array_count(blob, nodeoff, "assigned-clock-rates", assignedclks, 7); + if (cnt < 0) + return cnt; + + if (cnt != 7) + printf("Warning: %s, assigned-clock-rates count %d\n", nodes_path_8mn[0], cnt); + + assignedclks[cnt - 1] = 200000000; + assignedclks[cnt - 2] = 200000000; + + for (i = 0; i < cnt; i++) { + debug("<%u>, ", assignedclks[i]); + assignedclks[i] = cpu_to_fdt32(assignedclks[i]); + } + debug("\n"); + + return fdt_setprop(blob, nodeoff, "assigned-clock-rates", &assignedclks, sizeof(assignedclks)); +} +#endif + +int disable_gpu_nodes(void *blob) +{ + static const char * const nodes_path_8mn[] = { + "/gpu@38000000", + "/soc@/gpu@38000000" + }; + + return disable_fdt_nodes(blob, nodes_path_8mn, ARRAY_SIZE(nodes_path_8mn)); +} + +int disable_npu_nodes(void *blob) +{ + static const char * const nodes_path_8mp[] = { + "/vipsi@38500000" + }; + + return disable_fdt_nodes(blob, nodes_path_8mp, ARRAY_SIZE(nodes_path_8mp)); +} + +int disable_isp_nodes(void *blob) +{ + static const char * const nodes_path_8mp[] = { + "/soc@0/bus@32c00000/camera/isp@32e10000", + "/soc@0/bus@32c00000/camera/isp@32e20000" + }; + + return disable_fdt_nodes(blob, nodes_path_8mp, ARRAY_SIZE(nodes_path_8mp)); +} + +int disable_dsp_nodes(void *blob) +{ + static const char * const nodes_path_8mp[] = { + "/dsp@3b6e8000" + }; + + return disable_fdt_nodes(blob, nodes_path_8mp, ARRAY_SIZE(nodes_path_8mp)); +} + +static void disable_thermal_cpu_nodes(void *blob, u32 disabled_cores) +{ + static const char * const thermal_path[] = { + "/thermal-zones/cpu-thermal/cooling-maps/map0" + }; + + int nodeoff, cnt, i, ret, j; + u32 cooling_dev[12]; + + for (i = 0; i < ARRAY_SIZE(thermal_path); i++) { + nodeoff = fdt_path_offset(blob, thermal_path[i]); + if (nodeoff < 0) + continue; /* Not found, skip it */ + + cnt = fdtdec_get_int_array_count(blob, nodeoff, "cooling-device", cooling_dev, 12); + if (cnt < 0) + continue; + + if (cnt != 12) + printf("Warning: %s, cooling-device count %d\n", thermal_path[i], cnt); + + for (j = 0; j < cnt; j++) + cooling_dev[j] = cpu_to_fdt32(cooling_dev[j]); + + ret = fdt_setprop(blob, nodeoff, "cooling-device", &cooling_dev, + sizeof(u32) * (12 - disabled_cores * 3)); + if (ret < 0) { + printf("Warning: %s, cooling-device setprop failed %d\n", + thermal_path[i], ret); + continue; + } + + printf("Update node %s, cooling-device prop\n", thermal_path[i]); + } +} + +static void disable_pmu_cpu_nodes(void *blob, u32 disabled_cores) +{ + static const char * const pmu_path[] = { + "/pmu" + }; + + int nodeoff, cnt, i, ret, j; + u32 irq_affinity[4]; + + for (i = 0; i < ARRAY_SIZE(pmu_path); i++) { + nodeoff = fdt_path_offset(blob, pmu_path[i]); + if (nodeoff < 0) + continue; /* Not found, skip it */ + + cnt = fdtdec_get_int_array_count(blob, nodeoff, "interrupt-affinity", + irq_affinity, 4); + if (cnt < 0) + continue; + + if (cnt != 4) + printf("Warning: %s, interrupt-affinity count %d\n", pmu_path[i], cnt); + + for (j = 0; j < cnt; j++) + irq_affinity[j] = cpu_to_fdt32(irq_affinity[j]); + + ret = fdt_setprop(blob, nodeoff, "interrupt-affinity", &irq_affinity, + sizeof(u32) * (4 - disabled_cores)); + if (ret < 0) { + printf("Warning: %s, interrupt-affinity setprop failed %d\n", + pmu_path[i], ret); + continue; + } + + printf("Update node %s, interrupt-affinity prop\n", pmu_path[i]); + } +} + +static int disable_cpu_nodes(void *blob, u32 disabled_cores) +{ + static const char * const nodes_path[] = { + "/cpus/cpu@1", + "/cpus/cpu@2", + "/cpus/cpu@3", + }; + u32 i = 0; + int rc; + int nodeoff; + + if (disabled_cores > 3) + return -EINVAL; + + i = 3 - disabled_cores; + + for (; i < 3; i++) { + nodeoff = fdt_path_offset(blob, nodes_path[i]); + if (nodeoff < 0) + continue; /* Not found, skip it */ + + debug("Found %s node\n", nodes_path[i]); + + rc = fdt_del_node(blob, nodeoff); + if (rc < 0) { + printf("Unable to delete node %s, err=%s\n", + nodes_path[i], fdt_strerror(rc)); + } else { + printf("Delete node %s\n", nodes_path[i]); + } + } + + disable_thermal_cpu_nodes(blob, disabled_cores); + disable_pmu_cpu_nodes(blob, disabled_cores); + + return 0; +} + +int ft_system_setup(void *blob, struct bd_info *bd) +{ +#ifdef CONFIG_IMX8MQ + int i = 0; + int rc; + int nodeoff; + + if (get_boot_device() == USB_BOOT) { + disable_dcss_nodes(blob); + + bool new_path = check_fdt_new_path(blob); + int v = new_path ? 1 : 0; + static const char * const usb_dwc3_path[] = { + "/usb@38100000/dwc3", + "/soc@0/usb@38100000" + }; + + nodeoff = fdt_path_offset(blob, usb_dwc3_path[v]); + if (nodeoff >= 0) { + const char *speed = "high-speed"; + + printf("Found %s node\n", usb_dwc3_path[v]); + +usb_modify_speed: + + rc = fdt_setprop(blob, nodeoff, "maximum-speed", speed, strlen(speed) + 1); + if (rc) { + if (rc == -FDT_ERR_NOSPACE) { + rc = fdt_increase_size(blob, 512); + if (!rc) + goto usb_modify_speed; + } + printf("Unable to set property %s:%s, err=%s\n", + usb_dwc3_path[v], "maximum-speed", fdt_strerror(rc)); + } else { + printf("Modify %s:%s = %s\n", + usb_dwc3_path[v], "maximum-speed", speed); + } + } else { + printf("Can't found %s node\n", usb_dwc3_path[v]); + } + } + + /* Disable the CPU idle for A0 chip since the HW does not support it */ + if (is_soc_rev(CHIP_REV_1_0)) { + static const char * const nodes_path[] = { + "/cpus/cpu@0", + "/cpus/cpu@1", + "/cpus/cpu@2", + "/cpus/cpu@3", + }; + + for (i = 0; i < ARRAY_SIZE(nodes_path); i++) { + nodeoff = fdt_path_offset(blob, nodes_path[i]); + if (nodeoff < 0) + continue; /* Not found, skip it */ + + debug("Found %s node\n", nodes_path[i]); + + rc = fdt_delprop(blob, nodeoff, "cpu-idle-states"); + if (rc == -FDT_ERR_NOTFOUND) + continue; + if (rc) { + printf("Unable to update property %s:%s, err=%s\n", + nodes_path[i], "status", fdt_strerror(rc)); + return rc; + } + + debug("Remove %s:%s\n", nodes_path[i], + "cpu-idle-states"); + } + } + + if (is_imx8mql()) { + disable_vpu_nodes(blob); + if (check_dcss_fused()) { + printf("DCSS is fused\n"); + disable_dcss_nodes(blob); + check_mipi_dsi_nodes(blob); + } + } + + if (is_imx8md()) + disable_cpu_nodes(blob, 2); + +#elif defined(CONFIG_IMX8MM) + if (is_imx8mml() || is_imx8mmdl() || is_imx8mmsl()) + disable_vpu_nodes(blob); + + if (is_imx8mmd() || is_imx8mmdl()) + disable_cpu_nodes(blob, 2); + else if (is_imx8mms() || is_imx8mmsl()) + disable_cpu_nodes(blob, 3); + +#elif defined(CONFIG_IMX8MN) + if (is_imx8mnl() || is_imx8mndl() || is_imx8mnsl()) + disable_gpu_nodes(blob); +#ifdef CONFIG_IMX8MN_LOW_DRIVE_MODE + else { + int ldm_gpu = low_drive_gpu_freq(blob); + + if (ldm_gpu < 0) + printf("Update GPU node assigned-clock-rates failed\n"); + else + printf("Update GPU node assigned-clock-rates ok\n"); + } +#endif + + if (is_imx8mnd() || is_imx8mndl() || is_imx8mnud()) + disable_cpu_nodes(blob, 2); + else if (is_imx8mns() || is_imx8mnsl() || is_imx8mnus()) + disable_cpu_nodes(blob, 3); + +#elif defined(CONFIG_IMX8MP) + if (is_imx8mpl()) + disable_vpu_nodes(blob); + + if (is_imx8mpl() || is_imx8mp6()) + disable_npu_nodes(blob); + + if (is_imx8mpl()) + disable_isp_nodes(blob); + + if (is_imx8mpl() || is_imx8mp6()) + disable_dsp_nodes(blob); + + if (is_imx8mpd()) + disable_cpu_nodes(blob, 2); +#endif + + return 0; +} +#endif + +#if !CONFIG_IS_ENABLED(SYSRESET) +void reset_cpu(void) +{ + struct watchdog_regs *wdog = (struct watchdog_regs *)WDOG1_BASE_ADDR; + + /* Clear WDA to trigger WDOG_B immediately */ + writew((SET_WCR_WT(1) | WCR_WDT | WCR_WDE | WCR_SRS), &wdog->wcr); + + while (1) { + /* + * spin for .5 seconds before reset + */ + } +} +#endif + +#if defined(CONFIG_ARCH_MISC_INIT) +static void acquire_buildinfo(void) +{ + u64 atf_commit = 0; + struct arm_smccc_res res; + + /* Get ARM Trusted Firmware commit id */ + arm_smccc_smc(IMX_SIP_BUILDINFO, IMX_SIP_BUILDINFO_GET_COMMITHASH, + 0, 0, 0, 0, 0, 0, &res); + atf_commit = res.a0; + if (atf_commit == 0xffffffff) { + debug("ATF does not support build info\n"); + atf_commit = 0x30; /* Display 0, 0 ascii is 0x30 */ + } + + printf("\n BuildInfo:\n - ATF %s\n\n", (char *)&atf_commit); +} + +int arch_misc_init(void) +{ + acquire_buildinfo(); + + return 0; +} +#endif + +void imx_tmu_arch_init(void *reg_base) +{ + if (is_imx8mm() || is_imx8mn()) { + /* Load TCALIV and TASR from fuses */ + struct ocotp_regs *ocotp = + (struct ocotp_regs *)OCOTP_BASE_ADDR; + struct fuse_bank *bank = &ocotp->bank[3]; + struct fuse_bank3_regs *fuse = + (struct fuse_bank3_regs *)bank->fuse_regs; + + u32 tca_rt, tca_hr, tca_en; + u32 buf_vref, buf_slope; + + tca_rt = fuse->ana0 & 0xFF; + tca_hr = (fuse->ana0 & 0xFF00) >> 8; + tca_en = (fuse->ana0 & 0x2000000) >> 25; + + buf_vref = (fuse->ana0 & 0x1F00000) >> 20; + buf_slope = (fuse->ana0 & 0xF0000) >> 16; + + writel(buf_vref | (buf_slope << 16), (ulong)reg_base + 0x28); + writel((tca_en << 31) | (tca_hr << 16) | tca_rt, + (ulong)reg_base + 0x30); + } +#ifdef CONFIG_IMX8MP + /* Load TCALIV0/1/m40 and TRIM from fuses */ + struct ocotp_regs *ocotp = (struct ocotp_regs *)OCOTP_BASE_ADDR; + struct fuse_bank *bank = &ocotp->bank[38]; + struct fuse_bank38_regs *fuse = + (struct fuse_bank38_regs *)bank->fuse_regs; + struct fuse_bank *bank2 = &ocotp->bank[39]; + struct fuse_bank39_regs *fuse2 = + (struct fuse_bank39_regs *)bank2->fuse_regs; + u32 buf_vref, buf_slope, bjt_cur, vlsb, bgr; + u32 reg; + u32 tca40[2], tca25[2], tca105[2]; + + /* For blank sample */ + if (!fuse->ana_trim2 && !fuse->ana_trim3 && + !fuse->ana_trim4 && !fuse2->ana_trim5) { + /* Use a default 25C binary codes */ + tca25[0] = 1596; + tca25[1] = 1596; + writel(tca25[0], (ulong)reg_base + 0x30); + writel(tca25[1], (ulong)reg_base + 0x34); + return; + } + + buf_vref = (fuse->ana_trim2 & 0xc0) >> 6; + buf_slope = (fuse->ana_trim2 & 0xF00) >> 8; + bjt_cur = (fuse->ana_trim2 & 0xF000) >> 12; + bgr = (fuse->ana_trim2 & 0xF0000) >> 16; + vlsb = (fuse->ana_trim2 & 0xF00000) >> 20; + writel(buf_vref | (buf_slope << 16), (ulong)reg_base + 0x28); + + reg = (bgr << 28) | (bjt_cur << 20) | (vlsb << 12) | (1 << 7); + writel(reg, (ulong)reg_base + 0x3c); + + tca40[0] = (fuse->ana_trim3 & 0xFFF0000) >> 16; + tca25[0] = (fuse->ana_trim3 & 0xF0000000) >> 28; + tca25[0] |= ((fuse->ana_trim4 & 0xFF) << 4); + tca105[0] = (fuse->ana_trim4 & 0xFFF00) >> 8; + tca40[1] = (fuse->ana_trim4 & 0xFFF00000) >> 20; + tca25[1] = fuse2->ana_trim5 & 0xFFF; + tca105[1] = (fuse2->ana_trim5 & 0xFFF000) >> 12; + + /* use 25c for 1p calibration */ + writel(tca25[0] | (tca105[0] << 16), (ulong)reg_base + 0x30); + writel(tca25[1] | (tca105[1] << 16), (ulong)reg_base + 0x34); + writel(tca40[0] | (tca40[1] << 16), (ulong)reg_base + 0x38); +#endif +} + +#if defined(CONFIG_SPL_BUILD) +#if defined(CONFIG_IMX8MQ) || defined(CONFIG_IMX8MM) || defined(CONFIG_IMX8MN) +bool serror_need_skip = true; + +void do_error(struct pt_regs *pt_regs, unsigned int esr) +{ + /* + * If stack is still in ROM reserved OCRAM not switch to SPL, + * it is the ROM SError + */ + ulong sp; + + asm volatile("mov %0, sp" : "=r"(sp) : ); + + if (serror_need_skip && sp < 0x910000 && sp >= 0x900000) { + /* Check for ERR050342, imx8mq HDCP enabled parts */ + if (is_imx8mq() && !(readl(OCOTP_BASE_ADDR + 0x450) & 0x08000000)) { + serror_need_skip = false; + return; /* Do nothing skip the SError in ROM */ + } + + /* Check for ERR050350, field return mode for imx8mq, mm and mn */ + if (readl(OCOTP_BASE_ADDR + 0x630) & 0x1) { + serror_need_skip = false; + return; /* Do nothing skip the SError in ROM */ + } + } + + efi_restore_gd(); + printf("\"Error\" handler, esr 0x%08x\n", esr); + show_regs(pt_regs); + panic("Resetting CPU ...\n"); +} +#endif +#endif + +#if defined(CONFIG_IMX8MN) || defined(CONFIG_IMX8MP) +enum env_location env_get_location(enum env_operation op, int prio) +{ + enum boot_device dev = get_boot_device(); + enum env_location env_loc = ENVL_UNKNOWN; + + if (prio) + return env_loc; + + switch (dev) { +#ifdef CONFIG_ENV_IS_IN_SPI_FLASH + case QSPI_BOOT: + env_loc = ENVL_SPI_FLASH; + break; +#endif +#ifdef CONFIG_ENV_IS_IN_NAND + case NAND_BOOT: + env_loc = ENVL_NAND; + break; +#endif +#ifdef CONFIG_ENV_IS_IN_MMC + case SD1_BOOT: + case SD2_BOOT: + case SD3_BOOT: + case MMC1_BOOT: + case MMC2_BOOT: + case MMC3_BOOT: + env_loc = ENVL_MMC; + break; +#endif + default: +#if defined(CONFIG_ENV_IS_NOWHERE) + env_loc = ENVL_NOWHERE; +#endif + break; + } + + return env_loc; +} + +#ifndef ENV_IS_EMBEDDED +long long env_get_offset(long long defautl_offset) +{ + enum boot_device dev = get_boot_device(); + + switch (dev) { + case NAND_BOOT: + return (60 << 20); /* 60MB offset for NAND */ + default: + break; + } + + return defautl_offset; +} +#endif +#endif diff --git a/roms/u-boot/arch/arm/mach-imx/imx_bootaux.c b/roms/u-boot/arch/arm/mach-imx/imx_bootaux.c new file mode 100644 index 000000000..30fb45d48 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/imx_bootaux.c @@ -0,0 +1,199 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2016 Freescale Semiconductor, Inc. + */ + +#include <common.h> +#include <log.h> +#include <asm/io.h> +#include <asm/mach-imx/sys_proto.h> +#include <command.h> +#include <elf.h> +#include <imx_sip.h> +#include <linux/arm-smccc.h> +#include <linux/compiler.h> +#include <cpu_func.h> + +#ifndef CONFIG_IMX8M +const __weak struct rproc_att hostmap[] = { }; + +static const struct rproc_att *get_host_mapping(unsigned long auxcore) +{ + const struct rproc_att *mmap = hostmap; + + while (mmap && mmap->size) { + if (mmap->da <= auxcore && + mmap->da + mmap->size > auxcore) + return mmap; + mmap++; + } + + return NULL; +} + +/* + * A very simple elf loader for the auxilary core, assumes the image + * is valid, returns the entry point address. + * Translates load addresses in the elf file to the U-Boot address space. + */ +static unsigned long load_elf_image_m_core_phdr(unsigned long addr) +{ + Elf32_Ehdr *ehdr; /* ELF header structure pointer */ + Elf32_Phdr *phdr; /* Program header structure pointer */ + int i; + + ehdr = (Elf32_Ehdr *)addr; + phdr = (Elf32_Phdr *)(addr + ehdr->e_phoff); + + /* Load each program header */ + for (i = 0; i < ehdr->e_phnum; ++i, ++phdr) { + const struct rproc_att *mmap = get_host_mapping(phdr->p_paddr); + void *dst, *src; + + if (phdr->p_type != PT_LOAD) + continue; + + if (!mmap) { + printf("Invalid aux core address: %08x", + phdr->p_paddr); + return 0; + } + + dst = (void *)(phdr->p_paddr - mmap->da) + mmap->sa; + src = (void *)addr + phdr->p_offset; + + debug("Loading phdr %i to 0x%p (%i bytes)\n", + i, dst, phdr->p_filesz); + + if (phdr->p_filesz) + memcpy(dst, src, phdr->p_filesz); + if (phdr->p_filesz != phdr->p_memsz) + memset(dst + phdr->p_filesz, 0x00, + phdr->p_memsz - phdr->p_filesz); + flush_cache((unsigned long)dst & + ~(CONFIG_SYS_CACHELINE_SIZE - 1), + ALIGN(phdr->p_filesz, CONFIG_SYS_CACHELINE_SIZE)); + } + + return ehdr->e_entry; +} +#endif + +int arch_auxiliary_core_up(u32 core_id, ulong addr) +{ + ulong stack, pc; + + if (!addr) + return -EINVAL; + +#ifdef CONFIG_IMX8M + stack = *(u32 *)addr; + pc = *(u32 *)(addr + 4); +#else + /* + * handling ELF64 binaries + * isn't supported yet. + */ + if (valid_elf_image(addr)) { + stack = 0x0; + pc = load_elf_image_m_core_phdr(addr); + if (!pc) + return CMD_RET_FAILURE; + + } else { + /* + * Assume binary file with vector table at the beginning. + * Cortex-M4 vector tables start with the stack pointer (SP) + * and reset vector (initial PC). + */ + stack = *(u32 *)addr; + pc = *(u32 *)(addr + 4); + } +#endif + printf("## Starting auxiliary core stack = 0x%08lX, pc = 0x%08lX...\n", + stack, pc); + + /* Set the stack and pc to M4 bootROM */ + writel(stack, M4_BOOTROM_BASE_ADDR); + writel(pc, M4_BOOTROM_BASE_ADDR + 4); + + flush_dcache_all(); + + /* Enable M4 */ +#ifdef CONFIG_IMX8M + arm_smccc_smc(IMX_SIP_SRC, IMX_SIP_SRC_M4_START, 0, 0, + 0, 0, 0, 0, NULL); +#else + clrsetbits_le32(SRC_BASE_ADDR + SRC_M4_REG_OFFSET, + SRC_M4C_NON_SCLR_RST_MASK, SRC_M4_ENABLE_MASK); +#endif + + return 0; +} + +int arch_auxiliary_core_check_up(u32 core_id) +{ +#ifdef CONFIG_IMX8M + struct arm_smccc_res res; + + arm_smccc_smc(IMX_SIP_SRC, IMX_SIP_SRC_M4_STARTED, 0, 0, + 0, 0, 0, 0, &res); + + return res.a0; +#else + unsigned int val; + + val = readl(SRC_BASE_ADDR + SRC_M4_REG_OFFSET); + + if (val & SRC_M4C_NON_SCLR_RST_MASK) + return 0; /* assert in reset */ + + return 1; +#endif +} + +/* + * To i.MX6SX and i.MX7D, the image supported by bootaux needs + * the reset vector at the head for the image, with SP and PC + * as the first two words. + * + * Per the cortex-M reference manual, the reset vector of M4 needs + * to exist at 0x0 (TCMUL). The PC and SP are the first two addresses + * of that vector. So to boot M4, the A core must build the M4's reset + * vector with getting the PC and SP from image and filling them to + * TCMUL. When M4 is kicked, it will load the PC and SP by itself. + * The TCMUL is mapped to (M4_BOOTROM_BASE_ADDR) at A core side for + * accessing the M4 TCMUL. + */ +static int do_bootaux(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + ulong addr; + int ret, up; + + if (argc < 2) + return CMD_RET_USAGE; + + up = arch_auxiliary_core_check_up(0); + if (up) { + printf("## Auxiliary core is already up\n"); + return CMD_RET_SUCCESS; + } + + addr = simple_strtoul(argv[1], NULL, 16); + + if (!addr) + return CMD_RET_FAILURE; + + ret = arch_auxiliary_core_up(0, addr); + if (ret) + return CMD_RET_FAILURE; + + return CMD_RET_SUCCESS; +} + +U_BOOT_CMD( + bootaux, CONFIG_SYS_MAXARGS, 1, do_bootaux, + "Start auxiliary core", + "" +); diff --git a/roms/u-boot/arch/arm/mach-imx/imxrt/Kconfig b/roms/u-boot/arch/arm/mach-imx/imxrt/Kconfig new file mode 100644 index 000000000..d275fdf72 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/imxrt/Kconfig @@ -0,0 +1,34 @@ +if ARCH_IMXRT + +config IMXRT + bool + +config IMXRT1020 + bool + select IMXRT + +config IMXRT1050 + bool + select IMXRT + +config SYS_SOC + default "imxrt" + +choice + prompt "NXP i.MXRT board select" + optional + +config TARGET_IMXRT1020_EVK + bool "Support imxrt1020 EVK board" + select IMXRT1020 + +config TARGET_IMXRT1050_EVK + bool "Support imxrt1050 EVK board" + select IMXRT1050 + +endchoice + +source "board/freescale/imxrt1020-evk/Kconfig" +source "board/freescale/imxrt1050-evk/Kconfig" + +endif diff --git a/roms/u-boot/arch/arm/mach-imx/imxrt/Makefile b/roms/u-boot/arch/arm/mach-imx/imxrt/Makefile new file mode 100644 index 000000000..9621a8335 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/imxrt/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright 2019 +# Author(s): Giulio Benetti <giulio.benetti@benettiengineering.com> +# + +obj-y := soc.o diff --git a/roms/u-boot/arch/arm/mach-imx/imxrt/soc.c b/roms/u-boot/arch/arm/mach-imx/imxrt/soc.c new file mode 100644 index 000000000..ba015992e --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/imxrt/soc.c @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2019 + * Author(s): Giulio Benetti <giulio.benetti@benettiengineering.com> + */ + +#include <common.h> +#include <init.h> +#include <asm/io.h> +#include <asm/armv7_mpu.h> +#include <asm/mach-imx/sys_proto.h> +#include <linux/bitops.h> + +int arch_cpu_init(void) +{ + int i; + + struct mpu_region_config imxrt_region_config[] = { + { 0x00000000, REGION_0, XN_DIS, PRIV_RW_USR_RW, + STRONG_ORDER, REGION_4GB }, + { PHYS_SDRAM, REGION_1, XN_DIS, PRIV_RW_USR_RW, + O_I_WB_RD_WR_ALLOC, (ffs(PHYS_SDRAM_SIZE) - 2) }, + { DMAMEM_BASE, + REGION_2, XN_DIS, PRIV_RW_USR_RW, + STRONG_ORDER, (ffs(DMAMEM_SZ_ALL) - 2) }, + }; + + /* + * Configure the memory protection unit (MPU) to allow full access to + * the whole 4GB address space. + */ + disable_mpu(); + for (i = 0; i < ARRAY_SIZE(imxrt_region_config); i++) + mpu_config(&imxrt_region_config[i]); + enable_mpu(); + + return 0; +} + +u32 get_cpu_rev(void) +{ +#if defined(CONFIG_IMXRT1020) + return MXC_CPU_IMXRT1020 << 12; +#elif defined(CONFIG_IMXRT1050) + return MXC_CPU_IMXRT1050 << 12; +#else +#error This IMXRT SoC is not supported +#endif +} diff --git a/roms/u-boot/arch/arm/mach-imx/init.c b/roms/u-boot/arch/arm/mach-imx/init.c new file mode 100644 index 000000000..ce3eb4b0b --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/init.c @@ -0,0 +1,140 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2015 Freescale Semiconductor, Inc. + */ + +#include <asm/io.h> +#include <asm/arch/imx-regs.h> +#include <asm/arch/clock.h> +#include <asm/arch/sys_proto.h> +#include <asm/mach-imx/boot_mode.h> +#include <asm/arch/crm_regs.h> + +void init_aips(void) +{ + struct aipstz_regs *aips1, *aips2, *aips3; + + aips1 = (struct aipstz_regs *)AIPS1_BASE_ADDR; + aips2 = (struct aipstz_regs *)AIPS2_BASE_ADDR; + aips3 = (struct aipstz_regs *)AIPS3_BASE_ADDR; + + /* + * Set all MPROTx to be non-bufferable, trusted for R/W, + * not forced to user-mode. + */ + writel(0x77777777, &aips1->mprot0); + writel(0x77777777, &aips1->mprot1); + writel(0x77777777, &aips2->mprot0); + writel(0x77777777, &aips2->mprot1); + + /* + * Set all OPACRx to be non-bufferable, not require + * supervisor privilege level for access,allow for + * write access and untrusted master access. + */ + writel(0x00000000, &aips1->opacr0); + writel(0x00000000, &aips1->opacr1); + writel(0x00000000, &aips1->opacr2); + writel(0x00000000, &aips1->opacr3); + writel(0x00000000, &aips1->opacr4); + writel(0x00000000, &aips2->opacr0); + writel(0x00000000, &aips2->opacr1); + writel(0x00000000, &aips2->opacr2); + writel(0x00000000, &aips2->opacr3); + writel(0x00000000, &aips2->opacr4); + + if (is_mx6ull() || is_mx6sx() || is_mx7()) { + /* + * Set all MPROTx to be non-bufferable, trusted for R/W, + * not forced to user-mode. + */ + writel(0x77777777, &aips3->mprot0); + writel(0x77777777, &aips3->mprot1); + + /* + * Set all OPACRx to be non-bufferable, not require + * supervisor privilege level for access,allow for + * write access and untrusted master access. + */ + writel(0x00000000, &aips3->opacr0); + writel(0x00000000, &aips3->opacr1); + writel(0x00000000, &aips3->opacr2); + writel(0x00000000, &aips3->opacr3); + writel(0x00000000, &aips3->opacr4); + } +} + +void imx_wdog_disable_powerdown(void) +{ + struct wdog_regs *wdog1 = (struct wdog_regs *)WDOG1_BASE_ADDR; + struct wdog_regs *wdog2 = (struct wdog_regs *)WDOG2_BASE_ADDR; + struct wdog_regs *wdog3 = (struct wdog_regs *)WDOG3_BASE_ADDR; +#ifdef CONFIG_MX7D + struct wdog_regs *wdog4 = (struct wdog_regs *)WDOG4_BASE_ADDR; +#endif + + /* Write to the PDE (Power Down Enable) bit */ + writew(0, &wdog1->wmcr); + writew(0, &wdog2->wmcr); + + if (is_mx6sx() || is_mx6ul() || is_mx6ull() || is_mx7()) + writew(0, &wdog3->wmcr); +#ifdef CONFIG_MX7D + writew(0, &wdog4->wmcr); +#endif +} + +#define SRC_SCR_WARM_RESET_ENABLE 0 + +void init_src(void) +{ + struct src *src_regs = (struct src *)SRC_BASE_ADDR; + u32 val; + + /* + * force warm reset sources to generate cold reset + * for a more reliable restart + */ + val = readl(&src_regs->scr); + val &= ~(1 << SRC_SCR_WARM_RESET_ENABLE); + writel(val, &src_regs->scr); +} + +#ifdef CONFIG_CMD_BMODE +void boot_mode_apply(unsigned cfg_val) +{ +#ifdef CONFIG_MX6 + const u32 persist_sec = IMX6_SRC_GPR10_PERSIST_SECONDARY_BOOT; + const u32 bmode = IMX6_SRC_GPR10_BMODE; +#elif CONFIG_MX7 + const u32 persist_sec = IMX7_SRC_GPR10_PERSIST_SECONDARY_BOOT; + const u32 bmode = IMX7_SRC_GPR10_BMODE; +#endif + struct src *psrc = (struct src *)SRC_BASE_ADDR; + unsigned reg; + + if (cfg_val == MAKE_CFGVAL_PRIMARY_BOOT) + clrbits_le32(&psrc->gpr10, persist_sec); + else if (cfg_val == MAKE_CFGVAL_SECONDARY_BOOT) + setbits_le32(&psrc->gpr10, persist_sec); + else { + writel(cfg_val, &psrc->gpr9); + reg = readl(&psrc->gpr10); + if (cfg_val) + reg |= bmode; + else + reg &= ~bmode; + writel(reg, &psrc->gpr10); + } +} +#endif + +#if defined(CONFIG_MX6) +u32 imx6_src_get_boot_mode(void) +{ + if (readl(&src_base->gpr10) & IMX6_SRC_GPR10_BMODE) + return readl(&src_base->gpr9); + else + return readl(&src_base->sbmr1); +} +#endif diff --git a/roms/u-boot/arch/arm/mach-imx/iomux-v3.c b/roms/u-boot/arch/arm/mach-imx/iomux-v3.c new file mode 100644 index 000000000..18131a20f --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/iomux-v3.c @@ -0,0 +1,148 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Based on the iomux-v3.c from Linux kernel: + * Copyright (C) 2008 by Sascha Hauer <kernel@pengutronix.de> + * Copyright (C) 2009 by Jan Weitzel Phytec Messtechnik GmbH, + * <armlinux@phytec.de> + * + * Copyright (C) 2004-2011 Freescale Semiconductor, Inc. + */ +#include <common.h> +#include <asm/io.h> +#include <asm/arch/imx-regs.h> +#include <asm/mach-imx/iomux-v3.h> +#include <asm/mach-imx/sys_proto.h> + +static void *base = (void *)IOMUXC_BASE_ADDR; + +/* + * configures a single pad in the iomuxer + */ +void imx_iomux_v3_setup_pad(iomux_v3_cfg_t pad) +{ + u32 mux_ctrl_ofs = (pad & MUX_CTRL_OFS_MASK) >> MUX_CTRL_OFS_SHIFT; + u32 mux_mode = (pad & MUX_MODE_MASK) >> MUX_MODE_SHIFT; + u32 sel_input_ofs = + (pad & MUX_SEL_INPUT_OFS_MASK) >> MUX_SEL_INPUT_OFS_SHIFT; + u32 sel_input = + (pad & MUX_SEL_INPUT_MASK) >> MUX_SEL_INPUT_SHIFT; + u32 pad_ctrl_ofs = + (pad & MUX_PAD_CTRL_OFS_MASK) >> MUX_PAD_CTRL_OFS_SHIFT; + u32 pad_ctrl = (pad & MUX_PAD_CTRL_MASK) >> MUX_PAD_CTRL_SHIFT; + +#if defined(CONFIG_MX6SL) || defined(CONFIG_MX6SLL) + /* Check whether LVE bit needs to be set */ + if (pad_ctrl & PAD_CTL_LVE) { + pad_ctrl &= ~PAD_CTL_LVE; + pad_ctrl |= PAD_CTL_LVE_BIT; + } +#endif + +#ifdef CONFIG_IOMUX_LPSR + u32 lpsr = (pad & MUX_MODE_LPSR) >> MUX_MODE_SHIFT; + +#ifdef CONFIG_MX7 + if (lpsr == IOMUX_CONFIG_LPSR) { + base = (void *)IOMUXC_LPSR_BASE_ADDR; + mux_mode &= ~IOMUX_CONFIG_LPSR; + /* set daisy chain sel_input */ + if (sel_input_ofs) + sel_input_ofs += IOMUX_LPSR_SEL_INPUT_OFS; + } +#else + if (is_mx6ull() || is_mx6sll()) { + if (lpsr == IOMUX_CONFIG_LPSR) { + base = (void *)IOMUXC_SNVS_BASE_ADDR; + mux_mode &= ~IOMUX_CONFIG_LPSR; + } + } +#endif +#endif + + if (is_mx7() || is_mx6ull() || is_mx6sll() || mux_ctrl_ofs) + __raw_writel(mux_mode, base + mux_ctrl_ofs); + + if (sel_input_ofs) + __raw_writel(sel_input, base + sel_input_ofs); + +#ifdef CONFIG_IOMUX_SHARE_CONF_REG + if (!(pad_ctrl & NO_PAD_CTRL)) + __raw_writel((mux_mode << PAD_MUX_MODE_SHIFT) | pad_ctrl, + base + pad_ctrl_ofs); +#else + if (!(pad_ctrl & NO_PAD_CTRL) && pad_ctrl_ofs) + __raw_writel(pad_ctrl, base + pad_ctrl_ofs); +#if defined(CONFIG_MX6SLL) + else if ((pad_ctrl & NO_PAD_CTRL) && pad_ctrl_ofs) + clrbits_le32(base + pad_ctrl_ofs, PAD_CTL_IPD_BIT); +#endif +#endif + +#ifdef CONFIG_IOMUX_LPSR + if (lpsr == IOMUX_CONFIG_LPSR) + base = (void *)IOMUXC_BASE_ADDR; +#endif + +} + +/* configures a list of pads within declared with IOMUX_PADS macro */ +void imx_iomux_v3_setup_multiple_pads(iomux_v3_cfg_t const *pad_list, + unsigned count) +{ + iomux_v3_cfg_t const *p = pad_list; + int stride; + int i; + +#if defined(CONFIG_MX6QDL) + stride = 2; + if (!is_mx6dq() && !is_mx6dqp()) + p += 1; +#else + stride = 1; +#endif + for (i = 0; i < count; i++) { + imx_iomux_v3_setup_pad(*p); + p += stride; + } +} + +void imx_iomux_set_gpr_register(int group, int start_bit, + int num_bits, int value) +{ + int i = 0; + u32 reg; + reg = readl(base + group * 4); + while (num_bits) { + reg &= ~(1<<(start_bit + i)); + i++; + num_bits--; + } + reg |= (value << start_bit); + writel(reg, base + group * 4); +} + +#ifdef CONFIG_IOMUX_SHARE_CONF_REG +void imx_iomux_gpio_set_direction(unsigned int gpio, + unsigned int direction) +{ + u32 reg; + /* + * Only on Vybrid the input/output buffer enable flags + * are part of the shared mux/conf register. + */ + reg = readl(base + (gpio << 2)); + + if (direction) + reg |= 0x2; + else + reg &= ~0x2; + + writel(reg, base + (gpio << 2)); +} + +void imx_iomux_gpio_get_function(unsigned int gpio, u32 *gpio_state) +{ + *gpio_state = readl(base + (gpio << 2)) & + ((0X07 << PAD_MUX_MODE_SHIFT) | PAD_CTL_OBE_IBE_ENABLE); +} +#endif diff --git a/roms/u-boot/arch/arm/mach-imx/lowlevel.S b/roms/u-boot/arch/arm/mach-imx/lowlevel.S new file mode 100644 index 000000000..158fdb7d8 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/lowlevel.S @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2019 NXP + */ + +#include <linux/linkage.h> + +ENTRY(lowlevel_init) + mrs x0, CurrentEL + cmp x0, #8 + b.eq 1f + ret +1: + msr daifclr, #4 + + /* set HCR_EL2.AMO to catch SERROR */ + mrs x0, hcr_el2 + orr x0, x0, #0x20 + msr hcr_el2, x0 + isb + ret +ENDPROC(lowlevel_init) diff --git a/roms/u-boot/arch/arm/mach-imx/mac.c b/roms/u-boot/arch/arm/mach-imx/mac.c new file mode 100644 index 000000000..3b1496b20 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/mac.c @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2017 NXP + * + * Peng Fan <peng.fan@nxp.com> + */ + +#include <common.h> +#include <asm/arch/imx-regs.h> +#include <asm/io.h> +#include <asm/arch/sys_proto.h> +#include <errno.h> + +struct imx_mac_fuse { + u32 mac_addr0; + u32 rsvd0[3]; + u32 mac_addr1; + u32 rsvd1[3]; + u32 mac_addr2; + u32 rsvd2[7]; +}; + +#define MAC_FUSE_MX6_OFFSET 0x620 +#define MAC_FUSE_MX7_OFFSET 0x640 + +void imx_get_mac_from_fuse(int dev_id, unsigned char *mac) +{ + struct imx_mac_fuse *fuse; + u32 offset; + bool has_second_mac; + + offset = is_mx6() ? MAC_FUSE_MX6_OFFSET : MAC_FUSE_MX7_OFFSET; + fuse = (struct imx_mac_fuse *)(ulong)(OCOTP_BASE_ADDR + offset); + has_second_mac = is_mx7() || is_mx6sx() || is_mx6ul() || is_mx6ull(); + + if (has_second_mac && dev_id == 1) { + u32 value = readl(&fuse->mac_addr2); + + mac[0] = value >> 24; + mac[1] = value >> 16; + mac[2] = value >> 8; + mac[3] = value; + + value = readl(&fuse->mac_addr1); + mac[4] = value >> 24; + mac[5] = value >> 16; + + } else { + u32 value = readl(&fuse->mac_addr1); + + mac[0] = value >> 8; + mac[1] = value; + + value = readl(&fuse->mac_addr0); + mac[2] = value >> 24; + mac[3] = value >> 16; + mac[4] = value >> 8; + mac[5] = value; + } +} diff --git a/roms/u-boot/arch/arm/mach-imx/misc.c b/roms/u-boot/arch/arm/mach-imx/misc.c new file mode 100644 index 000000000..d82efa7f8 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/misc.c @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2013 Stefan Roese <sr@denx.de> + */ + +#include <common.h> +#include <lmb.h> +#include <log.h> +#include <asm/arch/sys_proto.h> +#include <asm/global_data.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <asm/io.h> +#include <asm/mach-imx/regs-common.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* 1 second delay should be plenty of time for block reset. */ +#define RESET_MAX_TIMEOUT 1000000 + +#define MXS_BLOCK_SFTRST (1 << 31) +#define MXS_BLOCK_CLKGATE (1 << 30) + +int mxs_wait_mask_set(struct mxs_register_32 *reg, uint32_t mask, unsigned + int timeout) +{ + while (--timeout) { + if ((readl(®->reg) & mask) == mask) + break; + udelay(1); + } + + return !timeout; +} + +int mxs_wait_mask_clr(struct mxs_register_32 *reg, uint32_t mask, unsigned + int timeout) +{ + while (--timeout) { + if ((readl(®->reg) & mask) == 0) + break; + udelay(1); + } + + return !timeout; +} + +int mxs_reset_block(struct mxs_register_32 *reg) +{ + /* Clear SFTRST */ + writel(MXS_BLOCK_SFTRST, ®->reg_clr); + + if (mxs_wait_mask_clr(reg, MXS_BLOCK_SFTRST, RESET_MAX_TIMEOUT)) + return 1; + + /* Clear CLKGATE */ + writel(MXS_BLOCK_CLKGATE, ®->reg_clr); + + /* Set SFTRST */ + writel(MXS_BLOCK_SFTRST, ®->reg_set); + + /* Wait for CLKGATE being set */ + if (mxs_wait_mask_set(reg, MXS_BLOCK_CLKGATE, RESET_MAX_TIMEOUT)) + return 1; + + /* Clear SFTRST */ + writel(MXS_BLOCK_SFTRST, ®->reg_clr); + + if (mxs_wait_mask_clr(reg, MXS_BLOCK_SFTRST, RESET_MAX_TIMEOUT)) + return 1; + + /* Clear CLKGATE */ + writel(MXS_BLOCK_CLKGATE, ®->reg_clr); + + if (mxs_wait_mask_clr(reg, MXS_BLOCK_CLKGATE, RESET_MAX_TIMEOUT)) + return 1; + + return 0; +} + +static ulong get_sp(void) +{ + ulong ret; + + asm("mov %0, sp" : "=r"(ret) : ); + return ret; +} + +void board_lmb_reserve(struct lmb *lmb) +{ + ulong sp, bank_end; + int bank; + + sp = get_sp(); + debug("## Current stack ends at 0x%08lx ", sp); + + /* adjust sp by 16K to be safe */ + sp -= 4096 << 2; + for (bank = 0; bank < CONFIG_NR_DRAM_BANKS; bank++) { + if (sp < gd->bd->bi_dram[bank].start) + continue; + bank_end = gd->bd->bi_dram[bank].start + + gd->bd->bi_dram[bank].size; + if (sp >= bank_end) + continue; + lmb_reserve(lmb, sp, bank_end - sp); + break; + } +} diff --git a/roms/u-boot/arch/arm/mach-imx/mkimage_fit_atf.sh b/roms/u-boot/arch/arm/mach-imx/mkimage_fit_atf.sh new file mode 100755 index 000000000..2a1796879 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/mkimage_fit_atf.sh @@ -0,0 +1,143 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0+ +# +# script to generate FIT image source for i.MX8MQ boards with +# ARM Trusted Firmware and multiple device trees (given on the command line) +# +# usage: $0 <dt_name> [<dt_name> [<dt_name] ...] + +[ -z "$BL31" ] && BL31="bl31.bin" +[ -z "$TEE_LOAD_ADDR" ] && TEE_LOAD_ADDR="0xfe000000" +[ -z "$ATF_LOAD_ADDR" ] && ATF_LOAD_ADDR="0x00910000" +[ -z "$BL33_LOAD_ADDR" ] && BL33_LOAD_ADDR="0x40200000" + +if [ ! -f $BL31 ]; then + echo "ERROR: BL31 file $BL31 NOT found" >&2 + exit 0 +else + echo "$BL31 size: " >&2 + stat -c %s $BL31 >&2 +fi + +BL32="tee.bin" + +if [ ! -f $BL32 ]; then + BL32=/dev/null +else + echo "Building with TEE support, make sure your $BL31 is compiled with spd. If you do not want tee, please delete $BL31" >&2 + echo "$BL32 size: " >&2 + stat -c %s $BL32 >&2 +fi + +BL33="u-boot-nodtb.bin" + +if [ ! -f $BL33 ]; then + echo "ERROR: $BL33 file NOT found" >&2 + exit 0 +else + echo "u-boot-nodtb.bin size: " >&2 + stat -c %s u-boot-nodtb.bin >&2 +fi + +for dtname in $* +do + echo "$dtname size: " >&2 + stat -c %s $dtname >&2 +done + + +cat << __HEADER_EOF +/dts-v1/; + +/ { + description = "Configuration to load ATF before U-Boot"; + + images { + uboot@1 { + description = "U-Boot (64-bit)"; + os = "u-boot"; + data = /incbin/("$BL33"); + type = "standalone"; + arch = "arm64"; + compression = "none"; + load = <$BL33_LOAD_ADDR>; + }; +__HEADER_EOF + +cnt=1 +for dtname in $* +do + cat << __FDT_IMAGE_EOF + fdt@$cnt { + description = "$(basename $dtname .dtb)"; + data = /incbin/("$dtname"); + type = "flat_dt"; + compression = "none"; + }; +__FDT_IMAGE_EOF +cnt=$((cnt+1)) +done + +cat << __HEADER_EOF + atf@1 { + description = "ARM Trusted Firmware"; + os = "arm-trusted-firmware"; + data = /incbin/("$BL31"); + type = "firmware"; + arch = "arm64"; + compression = "none"; + load = <$ATF_LOAD_ADDR>; + entry = <$ATF_LOAD_ADDR>; + }; +__HEADER_EOF + +if [ -f $BL32 ]; then +cat << __HEADER_EOF + tee@1 { + description = "TEE firmware"; + data = /incbin/("$BL32"); + type = "firmware"; + arch = "arm64"; + compression = "none"; + load = <$TEE_LOAD_ADDR>; + entry = <$TEE_LOAD_ADDR>; + }; +__HEADER_EOF +fi + +cat << __CONF_HEADER_EOF + }; + configurations { + default = "config@1"; + +__CONF_HEADER_EOF + +cnt=1 +for dtname in $* +do +if [ -f $BL32 ]; then +cat << __CONF_SECTION_EOF + config@$cnt { + description = "$(basename $dtname .dtb)"; + firmware = "uboot@1"; + loadables = "atf@1", "tee@1"; + fdt = "fdt@$cnt"; + }; +__CONF_SECTION_EOF +else +cat << __CONF_SECTION1_EOF + config@$cnt { + description = "$(basename $dtname .dtb)"; + firmware = "uboot@1"; + loadables = "atf@1"; + fdt = "fdt@$cnt"; + }; +__CONF_SECTION1_EOF +fi +cnt=$((cnt+1)) +done + +cat << __ITS_EOF + }; +}; +__ITS_EOF diff --git a/roms/u-boot/arch/arm/mach-imx/mmc_env.c b/roms/u-boot/arch/arm/mach-imx/mmc_env.c new file mode 100644 index 000000000..9c822f721 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/mmc_env.c @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2017 NXP + */ + +#include <common.h> +#include <asm/arch/imx-regs.h> +#include <asm/arch/sys_proto.h> +#include <asm/io.h> +#include <asm/mach-imx/boot_mode.h> + +__weak int board_mmc_get_env_dev(int devno) +{ + return CONFIG_SYS_MMC_ENV_DEV; +} + +int mmc_get_env_dev(void) +{ + struct bootrom_sw_info **p = + (struct bootrom_sw_info **)(ulong)ROM_SW_INFO_ADDR; + int devno = (*p)->boot_dev_instance; + u8 boot_type = (*p)->boot_dev_type; + + /* If not boot from sd/mmc, use default value */ + if ((boot_type != BOOT_TYPE_SD) && (boot_type != BOOT_TYPE_MMC)) + return CONFIG_SYS_MMC_ENV_DEV; + + return board_mmc_get_env_dev(devno); +} diff --git a/roms/u-boot/arch/arm/mach-imx/mmdc_size.c b/roms/u-boot/arch/arm/mach-imx/mmdc_size.c new file mode 100644 index 000000000..1a094726a --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/mmdc_size.c @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include <common.h> +#include <asm/io.h> + +#if defined(CONFIG_MX53) +#define MEMCTL_BASE ESDCTL_BASE_ADDR +#elif defined(CONFIG_MX6) +#define MEMCTL_BASE MMDC_P0_BASE_ADDR +#elif defined(CONFIG_MX7ULP) +#define MEMCTL_BASE MMDC0_RBASE +#endif +static const unsigned char col_lookup[] = {9, 10, 11, 8, 12, 9, 9, 9}; +static const unsigned char bank_lookup[] = {3, 2}; + +/* these MMDC registers are common to the IMX53 and IMX6 */ +struct esd_mmdc_regs { + u32 ctl; + u32 pdc; + u32 otc; + u32 cfg0; + u32 cfg1; + u32 cfg2; + u32 misc; +}; + +#define ESD_MMDC_CTL_GET_ROW(mdctl) ((ctl >> 24) & 7) +#define ESD_MMDC_CTL_GET_COLUMN(mdctl) ((ctl >> 20) & 7) +#define ESD_MMDC_CTL_GET_WIDTH(mdctl) ((ctl >> 16) & 3) +#define ESD_MMDC_CTL_GET_CS1(mdctl) ((ctl >> 30) & 1) +#define ESD_MMDC_MISC_GET_BANK(mdmisc) ((misc >> 5) & 1) + +/* + * imx_ddr_size - return size in bytes of DRAM according MMDC config + * The MMDC MDCTL register holds the number of bits for row, col, and data + * width and the MMDC MDMISC register holds the number of banks. Combine + * all these bits to determine the meme size the MMDC has been configured for + */ +unsigned int imx_ddr_size(void) +{ + struct esd_mmdc_regs *mem = (struct esd_mmdc_regs *)MEMCTL_BASE; + unsigned int ctl = readl(&mem->ctl); + unsigned int misc = readl(&mem->misc); + int bits = 11 + 0 + 0 + 1; /* row + col + bank + width */ + + bits += ESD_MMDC_CTL_GET_ROW(ctl); + bits += col_lookup[ESD_MMDC_CTL_GET_COLUMN(ctl)]; + bits += bank_lookup[ESD_MMDC_MISC_GET_BANK(misc)]; + bits += ESD_MMDC_CTL_GET_WIDTH(ctl); + bits += ESD_MMDC_CTL_GET_CS1(ctl); + + /* The MX6 can do only 3840 MiB of DRAM */ + if (bits == 32) + return 0xf0000000; + + return 1 << bits; +} diff --git a/roms/u-boot/arch/arm/mach-imx/mx2/Kconfig b/roms/u-boot/arch/arm/mach-imx/mx2/Kconfig new file mode 100644 index 000000000..fad5dcc94 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/mx2/Kconfig @@ -0,0 +1,23 @@ +if ARCH_MX25 + +config MX25 + bool + default y + select SYS_FSL_ERRATUM_ESDHC_A001 +choice + prompt "MX25 board select" + optional + +config TARGET_ZMX25 + bool "Support zmx25" + select BOARD_LATE_INIT + select CPU_ARM926EJS + +endchoice + +config SYS_SOC + default "mx25" + +source "board/syteco/zmx25/Kconfig" + +endif diff --git a/roms/u-boot/arch/arm/mach-imx/mx3/Kconfig b/roms/u-boot/arch/arm/mach-imx/mx3/Kconfig new file mode 100644 index 000000000..42bba4822 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/mx3/Kconfig @@ -0,0 +1,33 @@ +if ARCH_MX31 + +config MX31 + bool + default y +choice + prompt "MX31 board select" + optional + +config TARGET_MX31PDK + bool "Support the i.MX31 PDK board from Freescale/NXP" + select BOARD_EARLY_INIT_F + select BOARD_LATE_INIT + select SUPPORT_SPL + +endchoice + +config MX31_HCLK_FREQ + int "i.MX31 HCLK frequency" + default 26000000 + help + Frequency in Hz of the high frequency input clock. Typically + 26000000 Hz. + +config MX31_CLK32 + int "i.MX31 CLK32 Frequency" + default 32768 + help + Frequency in Hz of the low frequency input clock. Typically + 32768 or 32000 Hz. + + +endif diff --git a/roms/u-boot/arch/arm/mach-imx/mx5/Kconfig b/roms/u-boot/arch/arm/mach-imx/mx5/Kconfig new file mode 100644 index 000000000..580b45818 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/mx5/Kconfig @@ -0,0 +1,83 @@ +if ARCH_MX5 + +config MX5 + bool + default y + select GPT_TIMER + +config MX51 + bool + select ARM_CORTEX_A8_CVE_2017_5715 + select SYS_FSL_ERRATUM_ESDHC_A001 + +config MX53 + bool + select ARM_CORTEX_A8_CVE_2017_5715 + +choice + prompt "MX5 board select" + optional + +config TARGET_KP_IMX53 + bool "Support K+P imx53 board" + select BOARD_LATE_INIT + select DM + select DM_ETH + select DM_GPIO + select DM_I2C + select DM_PMIC + select DM_SERIAL + select DM_MMC + select BLK + select DM_USB + select DM_REGULATOR + select MX53 + imply CMD_DM + +config TARGET_M53MENLO + bool "Support m53menlo" + select MX53 + select SUPPORT_SPL + +config TARGET_MX51EVK + bool "Support mx51evk" + select BOARD_LATE_INIT + select MX51 + +config TARGET_MX53CX9020 + bool "Support CX9020" + select BOARD_LATE_INIT + select DM + select DM_SERIAL + select MX53 + imply CMD_DM + +config TARGET_MX53LOCO + bool "Support mx53loco" + select BOARD_LATE_INIT + select MX53 + +config TARGET_MX53PPD + bool "Support mx53ppd" + select MX53 + help + Enable support for the GE Healthcare PPD. + +config TARGET_USBARMORY + bool "Support USB armory" + select MX53 + +endchoice + +config SYS_SOC + default "mx5" + +source "board/beckhoff/mx53cx9020/Kconfig" +source "board/freescale/mx51evk/Kconfig" +source "board/freescale/mx53loco/Kconfig" +source "board/ge/mx53ppd/Kconfig" +source "board/inversepath/usbarmory/Kconfig" +source "board/k+p/kp_imx53/Kconfig" +source "board/menlo/m53menlo/Kconfig" + +endif diff --git a/roms/u-boot/arch/arm/mach-imx/mx5/Makefile b/roms/u-boot/arch/arm/mach-imx/mx5/Makefile new file mode 100644 index 000000000..40d199863 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/mx5/Makefile @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright 2000-2006 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# (C) Copyright 2009 Freescale Semiconductor, Inc. + +obj-y := soc.o clock.o +obj-y += lowlevel_init.o + +# common files for mx53 dram initialization +obj-$(CONFIG_TARGET_MX53CX9020) += mx53_dram.o +obj-$(CONFIG_TARGET_MX53LOCO) += mx53_dram.o diff --git a/roms/u-boot/arch/arm/mach-imx/mx5/clock.c b/roms/u-boot/arch/arm/mach-imx/mx5/clock.c new file mode 100644 index 000000000..bbaddd5a3 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/mx5/clock.c @@ -0,0 +1,982 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2007 + * Sascha Hauer, Pengutronix + * + * (C) Copyright 2009 Freescale Semiconductor, Inc. + */ + +#include <common.h> +#include <command.h> +#include <log.h> +#include <asm/io.h> +#include <linux/errno.h> +#include <asm/arch/imx-regs.h> +#include <asm/arch/crm_regs.h> +#include <asm/arch/clock.h> +#include <div64.h> +#include <asm/arch/sys_proto.h> + +enum pll_clocks { + PLL1_CLOCK = 0, + PLL2_CLOCK, + PLL3_CLOCK, +#ifdef CONFIG_MX53 + PLL4_CLOCK, +#endif + PLL_CLOCKS, +}; + +struct mxc_pll_reg *mxc_plls[PLL_CLOCKS] = { + [PLL1_CLOCK] = (struct mxc_pll_reg *)PLL1_BASE_ADDR, + [PLL2_CLOCK] = (struct mxc_pll_reg *)PLL2_BASE_ADDR, + [PLL3_CLOCK] = (struct mxc_pll_reg *)PLL3_BASE_ADDR, +#ifdef CONFIG_MX53 + [PLL4_CLOCK] = (struct mxc_pll_reg *)PLL4_BASE_ADDR, +#endif +}; + +#define AHB_CLK_ROOT 133333333 +#define SZ_DEC_1M 1000000 +#define PLL_PD_MAX 16 /* Actual pd+1 */ +#define PLL_MFI_MAX 15 +#define PLL_MFI_MIN 5 +#define ARM_DIV_MAX 8 +#define IPG_DIV_MAX 4 +#define AHB_DIV_MAX 8 +#define EMI_DIV_MAX 8 +#define NFC_DIV_MAX 8 + +#define MX5_CBCMR 0x00015154 +#define MX5_CBCDR 0x02888945 + +struct fixed_pll_mfd { + u32 ref_clk_hz; + u32 mfd; +}; + +const struct fixed_pll_mfd fixed_mfd[] = { + {MXC_HCLK, 24 * 16}, +}; + +struct pll_param { + u32 pd; + u32 mfi; + u32 mfn; + u32 mfd; +}; + +#define PLL_FREQ_MAX(ref_clk) (4 * (ref_clk) * PLL_MFI_MAX) +#define PLL_FREQ_MIN(ref_clk) \ + ((2 * (ref_clk) * (PLL_MFI_MIN - 1)) / PLL_PD_MAX) +#define MAX_DDR_CLK 420000000 +#define NFC_CLK_MAX 34000000 + +struct mxc_ccm_reg *mxc_ccm = (struct mxc_ccm_reg *)MXC_CCM_BASE; + +void set_usboh3_clk(void) +{ + clrsetbits_le32(&mxc_ccm->cscmr1, + MXC_CCM_CSCMR1_USBOH3_CLK_SEL_MASK, + MXC_CCM_CSCMR1_USBOH3_CLK_SEL(1)); + clrsetbits_le32(&mxc_ccm->cscdr1, + MXC_CCM_CSCDR1_USBOH3_CLK_PODF_MASK | + MXC_CCM_CSCDR1_USBOH3_CLK_PRED_MASK, + MXC_CCM_CSCDR1_USBOH3_CLK_PRED(4) | + MXC_CCM_CSCDR1_USBOH3_CLK_PODF(1)); +} + +void enable_usboh3_clk(bool enable) +{ + unsigned int cg = enable ? MXC_CCM_CCGR_CG_ON : MXC_CCM_CCGR_CG_OFF; + + clrsetbits_le32(&mxc_ccm->CCGR2, + MXC_CCM_CCGR2_USBOH3_60M(MXC_CCM_CCGR_CG_MASK), + MXC_CCM_CCGR2_USBOH3_60M(cg)); +} + +#ifdef CONFIG_SYS_I2C_MXC +/* i2c_num can be from 0, to 1 for i.MX51 and 2 for i.MX53 */ +int enable_i2c_clk(unsigned char enable, unsigned i2c_num) +{ + u32 mask; + +#if defined(CONFIG_MX51) + if (i2c_num > 1) +#elif defined(CONFIG_MX53) + if (i2c_num > 2) +#endif + return -EINVAL; + mask = MXC_CCM_CCGR_CG_MASK << + (MXC_CCM_CCGR1_I2C1_OFFSET + (i2c_num << 1)); + if (enable) + setbits_le32(&mxc_ccm->CCGR1, mask); + else + clrbits_le32(&mxc_ccm->CCGR1, mask); + return 0; +} +#endif + +void set_usb_phy_clk(void) +{ + clrbits_le32(&mxc_ccm->cscmr1, MXC_CCM_CSCMR1_USB_PHY_CLK_SEL); +} + +#if defined(CONFIG_MX51) +void enable_usb_phy1_clk(bool enable) +{ + unsigned int cg = enable ? MXC_CCM_CCGR_CG_ON : MXC_CCM_CCGR_CG_OFF; + + clrsetbits_le32(&mxc_ccm->CCGR2, + MXC_CCM_CCGR2_USB_PHY(MXC_CCM_CCGR_CG_MASK), + MXC_CCM_CCGR2_USB_PHY(cg)); +} + +void enable_usb_phy2_clk(bool enable) +{ + /* i.MX51 has a single USB PHY clock, so do nothing here. */ +} +#elif defined(CONFIG_MX53) +void enable_usb_phy1_clk(bool enable) +{ + unsigned int cg = enable ? MXC_CCM_CCGR_CG_ON : MXC_CCM_CCGR_CG_OFF; + + clrsetbits_le32(&mxc_ccm->CCGR4, + MXC_CCM_CCGR4_USB_PHY1(MXC_CCM_CCGR_CG_MASK), + MXC_CCM_CCGR4_USB_PHY1(cg)); +} + +void enable_usb_phy2_clk(bool enable) +{ + unsigned int cg = enable ? MXC_CCM_CCGR_CG_ON : MXC_CCM_CCGR_CG_OFF; + + clrsetbits_le32(&mxc_ccm->CCGR4, + MXC_CCM_CCGR4_USB_PHY2(MXC_CCM_CCGR_CG_MASK), + MXC_CCM_CCGR4_USB_PHY2(cg)); +} +#endif + +/* + * Calculate the frequency of PLLn. + */ +static uint32_t decode_pll(struct mxc_pll_reg *pll, uint32_t infreq) +{ + uint32_t ctrl, op, mfd, mfn, mfi, pdf, ret; + uint64_t refclk, temp; + int32_t mfn_abs; + + ctrl = readl(&pll->ctrl); + + if (ctrl & MXC_DPLLC_CTL_HFSM) { + mfn = readl(&pll->hfs_mfn); + mfd = readl(&pll->hfs_mfd); + op = readl(&pll->hfs_op); + } else { + mfn = readl(&pll->mfn); + mfd = readl(&pll->mfd); + op = readl(&pll->op); + } + + mfd &= MXC_DPLLC_MFD_MFD_MASK; + mfn &= MXC_DPLLC_MFN_MFN_MASK; + pdf = op & MXC_DPLLC_OP_PDF_MASK; + mfi = MXC_DPLLC_OP_MFI_RD(op); + + /* 21.2.3 */ + if (mfi < 5) + mfi = 5; + + /* Sign extend */ + if (mfn >= 0x04000000) { + mfn |= 0xfc000000; + mfn_abs = -mfn; + } else + mfn_abs = mfn; + + refclk = infreq * 2; + if (ctrl & MXC_DPLLC_CTL_DPDCK0_2_EN) + refclk *= 2; + + do_div(refclk, pdf + 1); + temp = refclk * mfn_abs; + do_div(temp, mfd + 1); + ret = refclk * mfi; + + if ((int)mfn < 0) + ret -= temp; + else + ret += temp; + + return ret; +} + +#ifdef CONFIG_MX51 +/* + * This function returns the Frequency Pre-Multiplier clock. + */ +static u32 get_fpm(void) +{ + u32 mult; + u32 ccr = readl(&mxc_ccm->ccr); + + if (ccr & MXC_CCM_CCR_FPM_MULT) + mult = 1024; + else + mult = 512; + + return MXC_CLK32 * mult; +} +#endif + +/* + * This function returns the low power audio clock. + */ +static u32 get_lp_apm(void) +{ + u32 ret_val = 0; + u32 ccsr = readl(&mxc_ccm->ccsr); + + if (ccsr & MXC_CCM_CCSR_LP_APM) +#if defined(CONFIG_MX51) + ret_val = get_fpm(); +#elif defined(CONFIG_MX53) + ret_val = decode_pll(mxc_plls[PLL4_CLOCK], MXC_HCLK); +#endif + else + ret_val = MXC_HCLK; + + return ret_val; +} + +/* + * Get mcu main rate + */ +u32 get_mcu_main_clk(void) +{ + u32 reg, freq; + + reg = MXC_CCM_CACRR_ARM_PODF_RD(readl(&mxc_ccm->cacrr)); + freq = decode_pll(mxc_plls[PLL1_CLOCK], MXC_HCLK); + return freq / (reg + 1); +} + +/* + * Get the rate of peripheral's root clock. + */ +u32 get_periph_clk(void) +{ + u32 reg; + + reg = readl(&mxc_ccm->cbcdr); + if (!(reg & MXC_CCM_CBCDR_PERIPH_CLK_SEL)) + return decode_pll(mxc_plls[PLL2_CLOCK], MXC_HCLK); + reg = readl(&mxc_ccm->cbcmr); + switch (MXC_CCM_CBCMR_PERIPH_CLK_SEL_RD(reg)) { + case 0: + return decode_pll(mxc_plls[PLL1_CLOCK], MXC_HCLK); + case 1: + return decode_pll(mxc_plls[PLL3_CLOCK], MXC_HCLK); + case 2: + return get_lp_apm(); + default: + return 0; + } + /* NOTREACHED */ +} + +/* + * Get the rate of ipg clock. + */ +static u32 get_ipg_clk(void) +{ + uint32_t freq, reg, div; + + freq = get_ahb_clk(); + + reg = readl(&mxc_ccm->cbcdr); + div = MXC_CCM_CBCDR_IPG_PODF_RD(reg) + 1; + + return freq / div; +} + +/* + * Get the rate of ipg_per clock. + */ +static u32 get_ipg_per_clk(void) +{ + u32 freq, pred1, pred2, podf; + + if (readl(&mxc_ccm->cbcmr) & MXC_CCM_CBCMR_PERCLK_IPG_CLK_SEL) + return get_ipg_clk(); + + if (readl(&mxc_ccm->cbcmr) & MXC_CCM_CBCMR_PERCLK_LP_APM_CLK_SEL) + freq = get_lp_apm(); + else + freq = get_periph_clk(); + podf = readl(&mxc_ccm->cbcdr); + pred1 = MXC_CCM_CBCDR_PERCLK_PRED1_RD(podf); + pred2 = MXC_CCM_CBCDR_PERCLK_PRED2_RD(podf); + podf = MXC_CCM_CBCDR_PERCLK_PODF_RD(podf); + return freq / ((pred1 + 1) * (pred2 + 1) * (podf + 1)); +} + +/* Get the output clock rate of a standard PLL MUX for peripherals. */ +static u32 get_standard_pll_sel_clk(u32 clk_sel) +{ + u32 freq = 0; + + switch (clk_sel & 0x3) { + case 0: + freq = decode_pll(mxc_plls[PLL1_CLOCK], MXC_HCLK); + break; + case 1: + freq = decode_pll(mxc_plls[PLL2_CLOCK], MXC_HCLK); + break; + case 2: + freq = decode_pll(mxc_plls[PLL3_CLOCK], MXC_HCLK); + break; + case 3: + freq = get_lp_apm(); + break; + } + + return freq; +} + +/* + * Get the rate of uart clk. + */ +static u32 get_uart_clk(void) +{ + unsigned int clk_sel, freq, reg, pred, podf; + + reg = readl(&mxc_ccm->cscmr1); + clk_sel = MXC_CCM_CSCMR1_UART_CLK_SEL_RD(reg); + freq = get_standard_pll_sel_clk(clk_sel); + + reg = readl(&mxc_ccm->cscdr1); + pred = MXC_CCM_CSCDR1_UART_CLK_PRED_RD(reg); + podf = MXC_CCM_CSCDR1_UART_CLK_PODF_RD(reg); + freq /= (pred + 1) * (podf + 1); + + return freq; +} + +/* + * get cspi clock rate. + */ +static u32 imx_get_cspiclk(void) +{ + u32 ret_val = 0, pdf, pre_pdf, clk_sel, freq; + u32 cscmr1 = readl(&mxc_ccm->cscmr1); + u32 cscdr2 = readl(&mxc_ccm->cscdr2); + + pre_pdf = MXC_CCM_CSCDR2_CSPI_CLK_PRED_RD(cscdr2); + pdf = MXC_CCM_CSCDR2_CSPI_CLK_PODF_RD(cscdr2); + clk_sel = MXC_CCM_CSCMR1_CSPI_CLK_SEL_RD(cscmr1); + freq = get_standard_pll_sel_clk(clk_sel); + ret_val = freq / ((pre_pdf + 1) * (pdf + 1)); + return ret_val; +} + +/* + * get esdhc clock rate. + */ +static u32 get_esdhc_clk(u32 port) +{ + u32 clk_sel = 0, pred = 0, podf = 0, freq = 0; + u32 cscmr1 = readl(&mxc_ccm->cscmr1); + u32 cscdr1 = readl(&mxc_ccm->cscdr1); + + switch (port) { + case 0: + clk_sel = MXC_CCM_CSCMR1_ESDHC1_MSHC1_CLK_SEL_RD(cscmr1); + pred = MXC_CCM_CSCDR1_ESDHC1_MSHC1_CLK_PRED_RD(cscdr1); + podf = MXC_CCM_CSCDR1_ESDHC1_MSHC1_CLK_PODF_RD(cscdr1); + break; + case 1: + clk_sel = MXC_CCM_CSCMR1_ESDHC2_MSHC2_CLK_SEL_RD(cscmr1); + pred = MXC_CCM_CSCDR1_ESDHC2_MSHC2_CLK_PRED_RD(cscdr1); + podf = MXC_CCM_CSCDR1_ESDHC2_MSHC2_CLK_PODF_RD(cscdr1); + break; + case 2: + if (cscmr1 & MXC_CCM_CSCMR1_ESDHC3_CLK_SEL) + return get_esdhc_clk(1); + else + return get_esdhc_clk(0); + case 3: + if (cscmr1 & MXC_CCM_CSCMR1_ESDHC4_CLK_SEL) + return get_esdhc_clk(1); + else + return get_esdhc_clk(0); + default: + break; + } + + freq = get_standard_pll_sel_clk(clk_sel) / ((pred + 1) * (podf + 1)); + return freq; +} + +static u32 get_axi_a_clk(void) +{ + u32 cbcdr = readl(&mxc_ccm->cbcdr); + u32 pdf = MXC_CCM_CBCDR_AXI_A_PODF_RD(cbcdr); + + return get_periph_clk() / (pdf + 1); +} + +static u32 get_axi_b_clk(void) +{ + u32 cbcdr = readl(&mxc_ccm->cbcdr); + u32 pdf = MXC_CCM_CBCDR_AXI_B_PODF_RD(cbcdr); + + return get_periph_clk() / (pdf + 1); +} + +static u32 get_emi_slow_clk(void) +{ + u32 cbcdr = readl(&mxc_ccm->cbcdr); + u32 emi_clk_sel = cbcdr & MXC_CCM_CBCDR_EMI_CLK_SEL; + u32 pdf = MXC_CCM_CBCDR_EMI_PODF_RD(cbcdr); + + if (emi_clk_sel) + return get_ahb_clk() / (pdf + 1); + + return get_periph_clk() / (pdf + 1); +} + +static u32 get_ddr_clk(void) +{ + u32 ret_val = 0; + u32 cbcmr = readl(&mxc_ccm->cbcmr); + u32 ddr_clk_sel = MXC_CCM_CBCMR_DDR_CLK_SEL_RD(cbcmr); +#ifdef CONFIG_MX51 + u32 cbcdr = readl(&mxc_ccm->cbcdr); + if (cbcdr & MXC_CCM_CBCDR_DDR_HIFREQ_SEL) { + u32 ddr_clk_podf = MXC_CCM_CBCDR_DDR_PODF_RD(cbcdr); + + ret_val = decode_pll(mxc_plls[PLL1_CLOCK], MXC_HCLK); + ret_val /= ddr_clk_podf + 1; + + return ret_val; + } +#endif + switch (ddr_clk_sel) { + case 0: + ret_val = get_axi_a_clk(); + break; + case 1: + ret_val = get_axi_b_clk(); + break; + case 2: + ret_val = get_emi_slow_clk(); + break; + case 3: + ret_val = get_ahb_clk(); + break; + default: + break; + } + + return ret_val; +} + +/* + * The API of get mxc clocks. + */ +unsigned int mxc_get_clock(enum mxc_clock clk) +{ + switch (clk) { + case MXC_ARM_CLK: + return get_mcu_main_clk(); + case MXC_AHB_CLK: + return get_ahb_clk(); + case MXC_IPG_CLK: + return get_ipg_clk(); + case MXC_IPG_PERCLK: + case MXC_I2C_CLK: + return get_ipg_per_clk(); + case MXC_UART_CLK: + return get_uart_clk(); + case MXC_CSPI_CLK: + return imx_get_cspiclk(); + case MXC_ESDHC_CLK: + return get_esdhc_clk(0); + case MXC_ESDHC2_CLK: + return get_esdhc_clk(1); + case MXC_ESDHC3_CLK: + return get_esdhc_clk(2); + case MXC_ESDHC4_CLK: + return get_esdhc_clk(3); + case MXC_FEC_CLK: + return get_ipg_clk(); + case MXC_SATA_CLK: + return get_ahb_clk(); + case MXC_DDR_CLK: + return get_ddr_clk(); + default: + break; + } + return -EINVAL; +} + +u32 imx_get_uartclk(void) +{ + return get_uart_clk(); +} + +u32 imx_get_fecclk(void) +{ + return get_ipg_clk(); +} + +static int gcd(int m, int n) +{ + int t; + while (m > 0) { + if (n > m) { + t = m; + m = n; + n = t; + } /* swap */ + m -= n; + } + return n; +} + +/* + * This is to calculate various parameters based on reference clock and + * targeted clock based on the equation: + * t_clk = 2*ref_freq*(mfi + mfn/(mfd+1))/(pd+1) + * This calculation is based on a fixed MFD value for simplicity. + */ +static int calc_pll_params(u32 ref, u32 target, struct pll_param *pll) +{ + u64 pd, mfi = 1, mfn, mfd, t1; + u32 n_target = target; + u32 n_ref = ref, i; + + /* + * Make sure targeted freq is in the valid range. + * Otherwise the following calculation might be wrong!!! + */ + if (n_target < PLL_FREQ_MIN(ref) || + n_target > PLL_FREQ_MAX(ref)) { + printf("Targeted peripheral clock should be" + "within [%d - %d]\n", + PLL_FREQ_MIN(ref) / SZ_DEC_1M, + PLL_FREQ_MAX(ref) / SZ_DEC_1M); + return -EINVAL; + } + + for (i = 0; i < ARRAY_SIZE(fixed_mfd); i++) { + if (fixed_mfd[i].ref_clk_hz == ref) { + mfd = fixed_mfd[i].mfd; + break; + } + } + + if (i == ARRAY_SIZE(fixed_mfd)) + return -EINVAL; + + /* Use n_target and n_ref to avoid overflow */ + for (pd = 1; pd <= PLL_PD_MAX; pd++) { + t1 = n_target * pd; + do_div(t1, (4 * n_ref)); + mfi = t1; + if (mfi > PLL_MFI_MAX) + return -EINVAL; + else if (mfi < 5) + continue; + break; + } + /* + * Now got pd and mfi already + * + * mfn = (((n_target * pd) / 4 - n_ref * mfi) * mfd) / n_ref; + */ + t1 = n_target * pd; + do_div(t1, 4); + t1 -= n_ref * mfi; + t1 *= mfd; + do_div(t1, n_ref); + mfn = t1; + debug("ref=%d, target=%d, pd=%d," "mfi=%d,mfn=%d, mfd=%d\n", + ref, n_target, (u32)pd, (u32)mfi, (u32)mfn, (u32)mfd); + i = 1; + if (mfn != 0) + i = gcd(mfd, mfn); + pll->pd = (u32)pd; + pll->mfi = (u32)mfi; + do_div(mfn, i); + pll->mfn = (u32)mfn; + do_div(mfd, i); + pll->mfd = (u32)mfd; + + return 0; +} + +#define calc_div(tgt_clk, src_clk, limit) ({ \ + u32 v = 0; \ + if (((src_clk) % (tgt_clk)) <= 100) \ + v = (src_clk) / (tgt_clk); \ + else \ + v = ((src_clk) / (tgt_clk)) + 1;\ + if (v > limit) \ + v = limit; \ + (v - 1); \ + }) + +#define CHANGE_PLL_SETTINGS(pll, pd, fi, fn, fd) \ + { \ + writel(0x1232, &pll->ctrl); \ + writel(0x2, &pll->config); \ + writel((((pd) - 1) << 0) | ((fi) << 4), \ + &pll->op); \ + writel(fn, &(pll->mfn)); \ + writel((fd) - 1, &pll->mfd); \ + writel((((pd) - 1) << 0) | ((fi) << 4), \ + &pll->hfs_op); \ + writel(fn, &pll->hfs_mfn); \ + writel((fd) - 1, &pll->hfs_mfd); \ + writel(0x1232, &pll->ctrl); \ + while (!readl(&pll->ctrl) & 0x1) \ + ;\ + } + +static int config_pll_clk(enum pll_clocks index, struct pll_param *pll_param) +{ + u32 ccsr = readl(&mxc_ccm->ccsr); + struct mxc_pll_reg *pll = mxc_plls[index]; + + switch (index) { + case PLL1_CLOCK: + /* Switch ARM to PLL2 clock */ + writel(ccsr | MXC_CCM_CCSR_PLL1_SW_CLK_SEL, + &mxc_ccm->ccsr); + CHANGE_PLL_SETTINGS(pll, pll_param->pd, + pll_param->mfi, pll_param->mfn, + pll_param->mfd); + /* Switch back */ + writel(ccsr & ~MXC_CCM_CCSR_PLL1_SW_CLK_SEL, + &mxc_ccm->ccsr); + break; + case PLL2_CLOCK: + /* Switch to pll2 bypass clock */ + writel(ccsr | MXC_CCM_CCSR_PLL2_SW_CLK_SEL, + &mxc_ccm->ccsr); + CHANGE_PLL_SETTINGS(pll, pll_param->pd, + pll_param->mfi, pll_param->mfn, + pll_param->mfd); + /* Switch back */ + writel(ccsr & ~MXC_CCM_CCSR_PLL2_SW_CLK_SEL, + &mxc_ccm->ccsr); + break; + case PLL3_CLOCK: + /* Switch to pll3 bypass clock */ + writel(ccsr | MXC_CCM_CCSR_PLL3_SW_CLK_SEL, + &mxc_ccm->ccsr); + CHANGE_PLL_SETTINGS(pll, pll_param->pd, + pll_param->mfi, pll_param->mfn, + pll_param->mfd); + /* Switch back */ + writel(ccsr & ~MXC_CCM_CCSR_PLL3_SW_CLK_SEL, + &mxc_ccm->ccsr); + break; +#ifdef CONFIG_MX53 + case PLL4_CLOCK: + /* Switch to pll4 bypass clock */ + writel(ccsr | MXC_CCM_CCSR_PLL4_SW_CLK_SEL, + &mxc_ccm->ccsr); + CHANGE_PLL_SETTINGS(pll, pll_param->pd, + pll_param->mfi, pll_param->mfn, + pll_param->mfd); + /* Switch back */ + writel(ccsr & ~MXC_CCM_CCSR_PLL4_SW_CLK_SEL, + &mxc_ccm->ccsr); + break; +#endif + default: + return -EINVAL; + } + + return 0; +} + +/* Config CPU clock */ +static int config_core_clk(u32 ref, u32 freq) +{ + int ret = 0; + struct pll_param pll_param; + + memset(&pll_param, 0, sizeof(struct pll_param)); + + /* The case that periph uses PLL1 is not considered here */ + ret = calc_pll_params(ref, freq, &pll_param); + if (ret != 0) { + printf("Error:Can't find pll parameters: %d\n", ret); + return ret; + } + + return config_pll_clk(PLL1_CLOCK, &pll_param); +} + +static int config_nfc_clk(u32 nfc_clk) +{ + u32 parent_rate = get_emi_slow_clk(); + u32 div; + + if (nfc_clk == 0) + return -EINVAL; + div = parent_rate / nfc_clk; + if (div == 0) + div++; + if (parent_rate / div > NFC_CLK_MAX) + div++; + clrsetbits_le32(&mxc_ccm->cbcdr, + MXC_CCM_CBCDR_NFC_PODF_MASK, + MXC_CCM_CBCDR_NFC_PODF(div - 1)); + while (readl(&mxc_ccm->cdhipr) != 0) + ; + return 0; +} + +void enable_nfc_clk(unsigned char enable) +{ + unsigned int cg = enable ? MXC_CCM_CCGR_CG_ON : MXC_CCM_CCGR_CG_OFF; + + clrsetbits_le32(&mxc_ccm->CCGR5, + MXC_CCM_CCGR5_EMI_ENFC(MXC_CCM_CCGR_CG_MASK), + MXC_CCM_CCGR5_EMI_ENFC(cg)); +} + +#ifdef CONFIG_FSL_IIM +void enable_efuse_prog_supply(bool enable) +{ + if (enable) + setbits_le32(&mxc_ccm->cgpr, + MXC_CCM_CGPR_EFUSE_PROG_SUPPLY_GATE); + else + clrbits_le32(&mxc_ccm->cgpr, + MXC_CCM_CGPR_EFUSE_PROG_SUPPLY_GATE); +} +#endif + +/* Config main_bus_clock for periphs */ +static int config_periph_clk(u32 ref, u32 freq) +{ + int ret = 0; + struct pll_param pll_param; + + memset(&pll_param, 0, sizeof(struct pll_param)); + + if (readl(&mxc_ccm->cbcdr) & MXC_CCM_CBCDR_PERIPH_CLK_SEL) { + ret = calc_pll_params(ref, freq, &pll_param); + if (ret != 0) { + printf("Error:Can't find pll parameters: %d\n", + ret); + return ret; + } + switch (MXC_CCM_CBCMR_PERIPH_CLK_SEL_RD( + readl(&mxc_ccm->cbcmr))) { + case 0: + return config_pll_clk(PLL1_CLOCK, &pll_param); + break; + case 1: + return config_pll_clk(PLL3_CLOCK, &pll_param); + break; + default: + return -EINVAL; + } + } + + return 0; +} + +static int config_ddr_clk(u32 emi_clk) +{ + u32 clk_src; + s32 shift = 0, clk_sel, div = 1; + u32 cbcmr = readl(&mxc_ccm->cbcmr); + + if (emi_clk > MAX_DDR_CLK) { + printf("Warning:DDR clock should not exceed %d MHz\n", + MAX_DDR_CLK / SZ_DEC_1M); + emi_clk = MAX_DDR_CLK; + } + + clk_src = get_periph_clk(); + /* Find DDR clock input */ + clk_sel = MXC_CCM_CBCMR_DDR_CLK_SEL_RD(cbcmr); + switch (clk_sel) { + case 0: + shift = 16; + break; + case 1: + shift = 19; + break; + case 2: + shift = 22; + break; + case 3: + shift = 10; + break; + default: + return -EINVAL; + } + + if ((clk_src % emi_clk) < 10000000) + div = clk_src / emi_clk; + else + div = (clk_src / emi_clk) + 1; + if (div > 8) + div = 8; + + clrsetbits_le32(&mxc_ccm->cbcdr, 0x7 << shift, (div - 1) << shift); + while (readl(&mxc_ccm->cdhipr) != 0) + ; + writel(0x0, &mxc_ccm->ccdr); + + return 0; +} + +#ifdef CONFIG_MX53 +static int config_ldb_clk(u32 ref, u32 freq) +{ + int ret = 0; + struct pll_param pll_param; + + memset(&pll_param, 0, sizeof(struct pll_param)); + + ret = calc_pll_params(ref, freq, &pll_param); + if (ret != 0) { + printf("Error:Can't find pll parameters: %d\n", + ret); + return ret; + } + + return config_pll_clk(PLL4_CLOCK, &pll_param); +} +#else +static int config_ldb_clk(u32 ref, u32 freq) +{ + /* Platform not supported */ + return -EINVAL; +} +#endif + +/* + * This function assumes the expected core clock has to be changed by + * modifying the PLL. This is NOT true always but for most of the times, + * it is. So it assumes the PLL output freq is the same as the expected + * core clock (presc=1) unless the core clock is less than PLL_FREQ_MIN. + * In the latter case, it will try to increase the presc value until + * (presc*core_clk) is greater than PLL_FREQ_MIN. It then makes call to + * calc_pll_params() and obtains the values of PD, MFI,MFN, MFD based + * on the targeted PLL and reference input clock to the PLL. Lastly, + * it sets the register based on these values along with the dividers. + * Note 1) There is no value checking for the passed-in divider values + * so the caller has to make sure those values are sensible. + * 2) Also adjust the NFC divider such that the NFC clock doesn't + * exceed NFC_CLK_MAX. + * 3) IPU HSP clock is independent of AHB clock. Even it can go up to + * 177MHz for higher voltage, this function fixes the max to 133MHz. + * 4) This function should not have allowed diag_printf() calls since + * the serial driver has been stoped. But leave then here to allow + * easy debugging by NOT calling the cyg_hal_plf_serial_stop(). + */ +int mxc_set_clock(u32 ref, u32 freq, enum mxc_clock clk) +{ + freq *= SZ_DEC_1M; + + switch (clk) { + case MXC_ARM_CLK: + if (config_core_clk(ref, freq)) + return -EINVAL; + break; + case MXC_PERIPH_CLK: + if (config_periph_clk(ref, freq)) + return -EINVAL; + break; + case MXC_DDR_CLK: + if (config_ddr_clk(freq)) + return -EINVAL; + break; + case MXC_NFC_CLK: + if (config_nfc_clk(freq)) + return -EINVAL; + break; + case MXC_LDB_CLK: + if (config_ldb_clk(ref, freq)) + return -EINVAL; + break; + default: + printf("Warning:Unsupported or invalid clock type\n"); + } + + return 0; +} + +#ifdef CONFIG_MX53 +/* + * The clock for the external interface can be set to use internal clock + * if fuse bank 4, row 3, bit 2 is set. + * This is an undocumented feature and it was confirmed by Freescale's support: + * Fuses (but not pins) may be used to configure SATA clocks. + * Particularly the i.MX53 Fuse_Map contains the next information + * about configuring SATA clocks : SATA_ALT_REF_CLK[1:0] (offset 0x180C) + * '00' - 100MHz (External) + * '01' - 50MHz (External) + * '10' - 120MHz, internal (USB PHY) + * '11' - Reserved +*/ +void mxc_set_sata_internal_clock(void) +{ + u32 *tmp_base = + (u32 *)(IIM_BASE_ADDR + 0x180c); + + set_usb_phy_clk(); + + clrsetbits_le32(tmp_base, 0x6, 0x4); +} +#endif + +#ifndef CONFIG_SPL_BUILD +/* + * Dump some core clockes. + */ +static int do_mx5_showclocks(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + u32 freq; + + freq = decode_pll(mxc_plls[PLL1_CLOCK], MXC_HCLK); + printf("PLL1 %8d MHz\n", freq / 1000000); + freq = decode_pll(mxc_plls[PLL2_CLOCK], MXC_HCLK); + printf("PLL2 %8d MHz\n", freq / 1000000); + freq = decode_pll(mxc_plls[PLL3_CLOCK], MXC_HCLK); + printf("PLL3 %8d MHz\n", freq / 1000000); +#ifdef CONFIG_MX53 + freq = decode_pll(mxc_plls[PLL4_CLOCK], MXC_HCLK); + printf("PLL4 %8d MHz\n", freq / 1000000); +#endif + + printf("\n"); + printf("AHB %8d kHz\n", mxc_get_clock(MXC_AHB_CLK) / 1000); + printf("IPG %8d kHz\n", mxc_get_clock(MXC_IPG_CLK) / 1000); + printf("IPG PERCLK %8d kHz\n", mxc_get_clock(MXC_IPG_PERCLK) / 1000); + printf("DDR %8d kHz\n", mxc_get_clock(MXC_DDR_CLK) / 1000); +#ifdef CONFIG_MXC_SPI + printf("CSPI %8d kHz\n", mxc_get_clock(MXC_CSPI_CLK) / 1000); +#endif + return 0; +} + +/***************************************************/ + +U_BOOT_CMD( + clocks, CONFIG_SYS_MAXARGS, 1, do_mx5_showclocks, + "display clocks", + "" +); +#endif diff --git a/roms/u-boot/arch/arm/mach-imx/mx5/lowlevel_init.S b/roms/u-boot/arch/arm/mach-imx/mx5/lowlevel_init.S new file mode 100644 index 000000000..b42cc3e9e --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/mx5/lowlevel_init.S @@ -0,0 +1,428 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2007, Guennadi Liakhovetski <lg@denx.de> + * + * (C) Copyright 2009 Freescale Semiconductor, Inc. + */ + +#include <config.h> +#include <asm/arch/imx-regs.h> +#include <generated/asm-offsets.h> +#include <linux/linkage.h> + +.section ".text.init", "x" + +.macro init_arm_erratum + /* ARM erratum ID #468414 */ + mrc 15, 0, r1, c1, c0, 1 + orr r1, r1, #(1 << 5) /* enable L1NEON bit */ + mcr 15, 0, r1, c1, c0, 1 +.endm + +/* + * L2CC Cache setup/invalidation/disable + */ +.macro init_l2cc + /* explicitly disable L2 cache */ + mrc 15, 0, r0, c1, c0, 1 + bic r0, r0, #0x2 + mcr 15, 0, r0, c1, c0, 1 + + /* reconfigure L2 cache aux control reg */ + ldr r0, =0xC0 | /* tag RAM */ \ + 0x4 | /* data RAM */ \ + 1 << 24 | /* disable write allocate delay */ \ + 1 << 23 | /* disable write allocate combine */ \ + 1 << 22 /* disable write allocate */ + +#if defined(CONFIG_MX51) + ldr r3, [r4, #ROM_SI_REV] + cmp r3, #0x10 + + /* disable write combine for TO 2 and lower revs */ + orrls r0, r0, #1 << 25 +#endif + + mcr 15, 1, r0, c9, c0, 2 + + /* enable L2 cache */ + mrc 15, 0, r0, c1, c0, 1 + orr r0, r0, #2 + mcr 15, 0, r0, c1, c0, 1 + +.endm /* init_l2cc */ + +/* AIPS setup - Only setup MPROTx registers. + * The PACR default values are good.*/ +.macro init_aips + /* + * Set all MPROTx to be non-bufferable, trusted for R/W, + * not forced to user-mode. + */ + ldr r0, =AIPS1_BASE_ADDR + ldr r1, =0x77777777 + str r1, [r0, #0x0] + str r1, [r0, #0x4] + ldr r0, =AIPS2_BASE_ADDR + str r1, [r0, #0x0] + str r1, [r0, #0x4] + /* + * Clear the on and off peripheral modules Supervisor Protect bit + * for SDMA to access them. Did not change the AIPS control registers + * (offset 0x20) access type + */ +.endm /* init_aips */ + +/* M4IF setup */ +.macro init_m4if +#ifdef CONFIG_MX51 + /* VPU and IPU given higher priority (0x4) + * IPU accesses with ID=0x1 given highest priority (=0xA) + */ + ldr r0, =M4IF_BASE_ADDR + + ldr r1, =0x00000203 + str r1, [r0, #0x40] + + str r4, [r0, #0x44] + + ldr r1, =0x00120125 + str r1, [r0, #0x9C] + + ldr r1, =0x001901A3 + str r1, [r0, #0x48] + +#endif +.endm /* init_m4if */ + +.macro setup_pll pll, freq + ldr r0, =\pll + adr r2, W_DP_\freq + bl setup_pll_func +.endm + +#define W_DP_OP 0 +#define W_DP_MFD 4 +#define W_DP_MFN 8 + +setup_pll_func: + ldr r1, =0x00001232 + str r1, [r0, #PLL_DP_CTL] /* Set DPLL ON (set UPEN bit): BRMO=1 */ + mov r1, #0x2 + str r1, [r0, #PLL_DP_CONFIG] /* Enable auto-restart AREN bit */ + + ldr r1, [r2, #W_DP_OP] + str r1, [r0, #PLL_DP_OP] + str r1, [r0, #PLL_DP_HFS_OP] + + ldr r1, [r2, #W_DP_MFD] + str r1, [r0, #PLL_DP_MFD] + str r1, [r0, #PLL_DP_HFS_MFD] + + ldr r1, [r2, #W_DP_MFN] + str r1, [r0, #PLL_DP_MFN] + str r1, [r0, #PLL_DP_HFS_MFN] + + ldr r1, =0x00001232 + str r1, [r0, #PLL_DP_CTL] +1: ldr r1, [r0, #PLL_DP_CTL] + ands r1, r1, #0x1 + beq 1b + + /* r10 saved upper lr */ + mov pc, lr + +.macro setup_pll_errata pll, freq + ldr r2, =\pll + str r4, [r2, #PLL_DP_CONFIG] /* Disable auto-restart AREN bit */ + ldr r1, =0x00001236 + str r1, [r2, #PLL_DP_CTL] /* Restart PLL with PLM=1 */ +1: ldr r1, [r2, #PLL_DP_CTL] /* Wait for lock */ + ands r1, r1, #0x1 + beq 1b + + ldr r5, \freq + str r5, [r2, #PLL_DP_MFN] /* Modify MFN value */ + str r5, [r2, #PLL_DP_HFS_MFN] + + mov r1, #0x1 + str r1, [r2, #PLL_DP_CONFIG] /* Reload MFN value */ + +2: ldr r1, [r2, #PLL_DP_CONFIG] + tst r1, #1 + bne 2b + + ldr r1, =100 /* Wait at least 4 us */ +3: subs r1, r1, #1 + bge 3b + + mov r1, #0x2 + str r1, [r2, #PLL_DP_CONFIG] /* Enable auto-restart AREN bit */ +.endm + +.macro init_clock +#if defined (CONFIG_MX51) + ldr r0, =CCM_BASE_ADDR + + /* Gate of clocks to the peripherals first */ + ldr r1, =0x3FFFFFFF + str r1, [r0, #CLKCTL_CCGR0] + str r4, [r0, #CLKCTL_CCGR1] + str r4, [r0, #CLKCTL_CCGR2] + str r4, [r0, #CLKCTL_CCGR3] + + ldr r1, =0x00030000 + str r1, [r0, #CLKCTL_CCGR4] + ldr r1, =0x00FFF030 + str r1, [r0, #CLKCTL_CCGR5] + ldr r1, =0x00000300 + str r1, [r0, #CLKCTL_CCGR6] + + /* Disable IPU and HSC dividers */ + mov r1, #0x60000 + str r1, [r0, #CLKCTL_CCDR] + + /* Make sure to switch the DDR away from PLL 1 */ + ldr r1, =0x19239145 + str r1, [r0, #CLKCTL_CBCDR] + /* make sure divider effective */ +1: ldr r1, [r0, #CLKCTL_CDHIPR] + cmp r1, #0x0 + bne 1b + + /* Switch ARM to step clock */ + mov r1, #0x4 + str r1, [r0, #CLKCTL_CCSR] + +#if defined(CONFIG_MX51_PLL_ERRATA) + setup_pll PLL1_BASE_ADDR, 864 + setup_pll_errata PLL1_BASE_ADDR, W_DP_MFN_800_DIT +#else + setup_pll PLL1_BASE_ADDR, 800 +#endif + + setup_pll PLL3_BASE_ADDR, 665 + + /* Switch peripheral to PLL 3 */ + ldr r0, =CCM_BASE_ADDR + ldr r1, =0x000010C0 | CONFIG_SYS_DDR_CLKSEL + str r1, [r0, #CLKCTL_CBCMR] + ldr r1, =0x13239145 + str r1, [r0, #CLKCTL_CBCDR] + setup_pll PLL2_BASE_ADDR, 665 + + /* Switch peripheral to PLL2 */ + ldr r0, =CCM_BASE_ADDR + ldr r1, =0x19239145 + str r1, [r0, #CLKCTL_CBCDR] + ldr r1, =0x000020C0 | CONFIG_SYS_DDR_CLKSEL + str r1, [r0, #CLKCTL_CBCMR] + + setup_pll PLL3_BASE_ADDR, 216 + + /* Set the platform clock dividers */ + ldr r0, =ARM_BASE_ADDR + ldr r1, =0x00000725 + str r1, [r0, #0x14] + + ldr r0, =CCM_BASE_ADDR + + /* Run 3.0 at Full speed, for other TO's wait till we increase VDDGP */ + ldr r3, [r4, #ROM_SI_REV] + cmp r3, #0x10 + movls r1, #0x1 + movhi r1, #0 + + str r1, [r0, #CLKCTL_CACRR] + + /* Switch ARM back to PLL 1 */ + str r4, [r0, #CLKCTL_CCSR] + + /* setup the rest */ + /* Use lp_apm (24MHz) source for perclk */ + ldr r1, =0x000020C2 | CONFIG_SYS_DDR_CLKSEL + str r1, [r0, #CLKCTL_CBCMR] + /* ddr clock from PLL 1, all perclk dividers are 1 since using 24MHz */ + ldr r1, =CONFIG_SYS_CLKTL_CBCDR + str r1, [r0, #CLKCTL_CBCDR] + + /* Restore the default values in the Gate registers */ + ldr r1, =0xFFFFFFFF + str r1, [r0, #CLKCTL_CCGR0] + str r1, [r0, #CLKCTL_CCGR1] + str r1, [r0, #CLKCTL_CCGR2] + str r1, [r0, #CLKCTL_CCGR3] + str r1, [r0, #CLKCTL_CCGR4] + str r1, [r0, #CLKCTL_CCGR5] + str r1, [r0, #CLKCTL_CCGR6] + + /* Use PLL 2 for UART's, get 66.5MHz from it */ + ldr r1, =0xA5A2A020 + str r1, [r0, #CLKCTL_CSCMR1] + ldr r1, =0x00C30321 + str r1, [r0, #CLKCTL_CSCDR1] + /* make sure divider effective */ +1: ldr r1, [r0, #CLKCTL_CDHIPR] + cmp r1, #0x0 + bne 1b + + str r4, [r0, #CLKCTL_CCDR] + + /* for cko - for ARM div by 8 */ + mov r1, #0x000A0000 + add r1, r1, #0x00000F0 + str r1, [r0, #CLKCTL_CCOSR] +#else /* CONFIG_MX53 */ + ldr r0, =CCM_BASE_ADDR + + /* Gate of clocks to the peripherals first */ + ldr r1, =0x3FFFFFFF + str r1, [r0, #CLKCTL_CCGR0] + str r4, [r0, #CLKCTL_CCGR1] + str r4, [r0, #CLKCTL_CCGR2] + str r4, [r0, #CLKCTL_CCGR3] + str r4, [r0, #CLKCTL_CCGR7] + ldr r1, =0x00030000 + str r1, [r0, #CLKCTL_CCGR4] + ldr r1, =0x00FFF030 + str r1, [r0, #CLKCTL_CCGR5] + ldr r1, =0x0F00030F + str r1, [r0, #CLKCTL_CCGR6] + + /* Switch ARM to step clock */ + mov r1, #0x4 + str r1, [r0, #CLKCTL_CCSR] + + setup_pll PLL1_BASE_ADDR, 800 + + setup_pll PLL3_BASE_ADDR, 400 + + /* Switch peripheral to PLL3 */ + ldr r0, =CCM_BASE_ADDR + ldr r1, =0x00015154 + str r1, [r0, #CLKCTL_CBCMR] + ldr r1, =0x02898945 + str r1, [r0, #CLKCTL_CBCDR] + /* make sure change is effective */ +1: ldr r1, [r0, #CLKCTL_CDHIPR] + cmp r1, #0x0 + bne 1b + + setup_pll PLL2_BASE_ADDR, 400 + + /* Switch peripheral to PLL2 */ + ldr r0, =CCM_BASE_ADDR + ldr r1, =0x00888945 + str r1, [r0, #CLKCTL_CBCDR] + + ldr r1, =0x00016154 + str r1, [r0, #CLKCTL_CBCMR] + + /*change uart clk parent to pll2*/ + ldr r1, [r0, #CLKCTL_CSCMR1] + and r1, r1, #0xfcffffff + orr r1, r1, #0x01000000 + str r1, [r0, #CLKCTL_CSCMR1] + + /* make sure change is effective */ +1: ldr r1, [r0, #CLKCTL_CDHIPR] + cmp r1, #0x0 + bne 1b + + setup_pll PLL3_BASE_ADDR, 216 + + setup_pll PLL4_BASE_ADDR, 455 + + /* Set the platform clock dividers */ + ldr r0, =ARM_BASE_ADDR + ldr r1, =0x00000124 + str r1, [r0, #0x14] + + ldr r0, =CCM_BASE_ADDR + mov r1, #0 + str r1, [r0, #CLKCTL_CACRR] + + /* Switch ARM back to PLL 1. */ + mov r1, #0x0 + str r1, [r0, #CLKCTL_CCSR] + + /* make uart div=6 */ + ldr r1, [r0, #CLKCTL_CSCDR1] + and r1, r1, #0xffffffc0 + orr r1, r1, #0x0a + str r1, [r0, #CLKCTL_CSCDR1] + + /* Restore the default values in the Gate registers */ + ldr r1, =0xFFFFFFFF + str r1, [r0, #CLKCTL_CCGR0] + str r1, [r0, #CLKCTL_CCGR1] + str r1, [r0, #CLKCTL_CCGR2] + str r1, [r0, #CLKCTL_CCGR3] + str r1, [r0, #CLKCTL_CCGR4] + str r1, [r0, #CLKCTL_CCGR5] + str r1, [r0, #CLKCTL_CCGR6] + str r1, [r0, #CLKCTL_CCGR7] + + mov r1, #0x00000 + str r1, [r0, #CLKCTL_CCDR] + + /* for cko - for ARM div by 8 */ + mov r1, #0x000A0000 + add r1, r1, #0x00000F0 + str r1, [r0, #CLKCTL_CCOSR] + +#endif /* CONFIG_MX53 */ +.endm + +ENTRY(lowlevel_init) + mov r10, lr + mov r4, #0 /* Fix R4 to 0 */ + +#if defined(CONFIG_SYS_MAIN_PWR_ON) + ldr r0, =GPIO1_BASE_ADDR + ldr r1, [r0, #0x0] + orr r1, r1, #1 << 23 + str r1, [r0, #0x0] + ldr r1, [r0, #0x4] + orr r1, r1, #1 << 23 + str r1, [r0, #0x4] +#endif + + init_arm_erratum + + init_l2cc + + init_aips + + init_m4if + + init_clock + + mov pc, r10 +ENDPROC(lowlevel_init) + +/* Board level setting value */ +#if defined(CONFIG_MX51_PLL_ERRATA) +W_DP_864: .word DP_OP_864 + .word DP_MFD_864 + .word DP_MFN_864 +W_DP_MFN_800_DIT: .word DP_MFN_800_DIT +#else +W_DP_800: .word DP_OP_800 + .word DP_MFD_800 + .word DP_MFN_800 +#endif +#if defined(CONFIG_MX51) +W_DP_665: .word DP_OP_665 + .word DP_MFD_665 + .word DP_MFN_665 +#endif +W_DP_216: .word DP_OP_216 + .word DP_MFD_216 + .word DP_MFN_216 +W_DP_400: .word DP_OP_400 + .word DP_MFD_400 + .word DP_MFN_400 +W_DP_455: .word DP_OP_455 + .word DP_MFD_455 + .word DP_MFN_455 diff --git a/roms/u-boot/arch/arm/mach-imx/mx5/mx53_dram.c b/roms/u-boot/arch/arm/mach-imx/mx5/mx53_dram.c new file mode 100644 index 000000000..f74414419 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/mx5/mx53_dram.c @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2017 Beckhoff Automation GmbH & Co. KG + * Patrick Bruenn <p.bruenn@beckhoff.com> + */ + +#include <common.h> +#include <init.h> +#include <asm/global_data.h> + +DECLARE_GLOBAL_DATA_PTR; + +phys_size_t get_effective_memsize(void) +{ + /* + * WARNING: We must override get_effective_memsize() function here + * to report only the size of the first DRAM bank. This is to make + * U-Boot relocator place U-Boot into valid memory, that is, at the + * end of the first DRAM bank. If we did not override this function + * like so, U-Boot would be placed at the address of the first DRAM + * bank + total DRAM size - sizeof(uboot), which in the setup where + * each DRAM bank contains 512MiB of DRAM would result in placing + * U-Boot into invalid memory area close to the end of the first + * DRAM bank. + */ + return get_ram_size((void *)PHYS_SDRAM_1, 1 << 30); +} + +int dram_init(void) +{ + gd->ram_size = get_ram_size((void *)PHYS_SDRAM_1, 1 << 30); + gd->ram_size += get_ram_size((void *)PHYS_SDRAM_2, 1 << 30); + + return 0; +} + +int dram_init_banksize(void) +{ + gd->bd->bi_dram[0].start = PHYS_SDRAM_1; + gd->bd->bi_dram[0].size = get_ram_size((void *)PHYS_SDRAM_1, 1 << 30); + + gd->bd->bi_dram[1].start = PHYS_SDRAM_2; + gd->bd->bi_dram[1].size = get_ram_size((void *)PHYS_SDRAM_2, 1 << 30); + + return 0; +} diff --git a/roms/u-boot/arch/arm/mach-imx/mx5/soc.c b/roms/u-boot/arch/arm/mach-imx/mx5/soc.c new file mode 100644 index 000000000..47f531dc8 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/mx5/soc.c @@ -0,0 +1,136 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2007 + * Sascha Hauer, Pengutronix + * + * (C) Copyright 2009 Freescale Semiconductor, Inc. + */ + +#include <common.h> +#include <cpu_func.h> +#include <asm/arch/imx-regs.h> +#include <asm/arch/clock.h> +#include <asm/arch/sys_proto.h> +#include <asm/cache.h> + +#include <linux/errno.h> +#include <asm/io.h> +#include <asm/mach-imx/boot_mode.h> + +#if !(defined(CONFIG_MX51) || defined(CONFIG_MX53)) +#error "CPU_TYPE not defined" +#endif + +u32 get_cpu_rev(void) +{ +#ifdef CONFIG_MX51 + int system_rev = 0x51000; +#else + int system_rev = 0x53000; +#endif + int reg = __raw_readl(ROM_SI_REV); + +#if defined(CONFIG_MX51) + switch (reg) { + case 0x02: + system_rev |= CHIP_REV_1_1; + break; + case 0x10: + if ((__raw_readl(GPIO1_BASE_ADDR + 0x0) & (0x1 << 22)) == 0) + system_rev |= CHIP_REV_2_5; + else + system_rev |= CHIP_REV_2_0; + break; + case 0x20: + system_rev |= CHIP_REV_3_0; + break; + default: + system_rev |= CHIP_REV_1_0; + break; + } +#else + if (reg < 0x20) + system_rev |= CHIP_REV_1_0; + else + system_rev |= reg; +#endif + return system_rev; +} + +#ifdef CONFIG_REVISION_TAG +u32 __weak get_board_rev(void) +{ + return get_cpu_rev(); +} +#endif + +#if !CONFIG_IS_ENABLED(SYS_DCACHE_OFF) +void enable_caches(void) +{ + /* Enable D-cache. I-cache is already enabled in start.S */ + dcache_enable(); +} +#endif + +#if defined(CONFIG_FEC_MXC) +void imx_get_mac_from_fuse(int dev_id, unsigned char *mac) +{ + int i; + struct iim_regs *iim = (struct iim_regs *)IMX_IIM_BASE; + struct fuse_bank *bank = &iim->bank[1]; + struct fuse_bank1_regs *fuse = + (struct fuse_bank1_regs *)bank->fuse_regs; + + for (i = 0; i < 6; i++) + mac[i] = readl(&fuse->mac_addr[i]) & 0xff; +} +#endif + +#ifdef CONFIG_MX53 +#define IMX53_SRTC_LPGR_PERSIST_SECONDARY_BOOT BIT(30) + +void boot_mode_apply(unsigned cfg_val) +{ + void *lpgr = &((struct srtc_regs *)SRTC_BASE_ADDR)->lpgr; + + if (cfg_val == MAKE_CFGVAL_PRIMARY_BOOT) + clrbits_le32(lpgr, IMX53_SRTC_LPGR_PERSIST_SECONDARY_BOOT); + else if (cfg_val == MAKE_CFGVAL_SECONDARY_BOOT) + setbits_le32(lpgr, IMX53_SRTC_LPGR_PERSIST_SECONDARY_BOOT); + else + writel(cfg_val, lpgr); +} + +int boot_mode_getprisec(void) +{ + void *lpgr = &((struct srtc_regs *)SRTC_BASE_ADDR)->lpgr; + + return !!(readl(lpgr) & IMX53_SRTC_LPGR_PERSIST_SECONDARY_BOOT); +} + +/* + * cfg_val will be used for + * Boot_cfg3[7:0]:Boot_cfg2[7:0]:Boot_cfg1[7:0] + * + * If bit 28 of LPGR is set upon watchdog reset, + * bits[25:0] of LPGR will move to SBMR. + */ +const struct boot_mode soc_boot_modes[] = { + {"normal", MAKE_CFGVAL(0x00, 0x00, 0x00, 0x00)}, + /* usb or serial download */ + {"usb", MAKE_CFGVAL(0x00, 0x00, 0x00, 0x13)}, + {"sata", MAKE_CFGVAL(0x28, 0x00, 0x00, 0x12)}, + {"escpi1:0", MAKE_CFGVAL(0x38, 0x20, 0x00, 0x12)}, + {"escpi1:1", MAKE_CFGVAL(0x38, 0x20, 0x04, 0x12)}, + {"escpi1:2", MAKE_CFGVAL(0x38, 0x20, 0x08, 0x12)}, + {"escpi1:3", MAKE_CFGVAL(0x38, 0x20, 0x0c, 0x12)}, + /* 4 bit bus width */ + {"esdhc1", MAKE_CFGVAL(0x40, 0x20, 0x00, 0x12)}, + {"esdhc2", MAKE_CFGVAL(0x40, 0x20, 0x08, 0x12)}, + {"esdhc3", MAKE_CFGVAL(0x40, 0x20, 0x10, 0x12)}, + {"esdhc4", MAKE_CFGVAL(0x40, 0x20, 0x18, 0x12)}, + {"primary", MAKE_CFGVAL_PRIMARY_BOOT}, + {"secondary", MAKE_CFGVAL_SECONDARY_BOOT}, + {NULL, 0}, +}; +#endif diff --git a/roms/u-boot/arch/arm/mach-imx/mx6/Kconfig b/roms/u-boot/arch/arm/mach-imx/mx6/Kconfig new file mode 100644 index 000000000..23cab3932 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/mx6/Kconfig @@ -0,0 +1,696 @@ +if ARCH_MX6 + +config MX6_SMP + bool + select ARM_ERRATA_751472 + select ARM_ERRATA_761320 + select ARM_ERRATA_794072 + select ARM_ERRATA_845369 + select MP + +config MX6 + bool + default y + select ARM_ERRATA_743622 if !MX6UL && !MX6ULL + select GPT_TIMER if !MX6UL && !MX6ULL + imply CMD_FUSE + +choice + prompt "i.MX6 SoC select" + +config MX6D + bool "i.MX 6Dual SoC support" + select HAS_CAAM + select MX6_SMP + +config MX6DL + bool "i.MX 6DualLite SoC support" + select HAS_CAAM + select MX6_SMP + +config MX6Q + bool "i.MX 6Quad SoC support" + select HAS_CAAM + select MX6_SMP + +config MX6QDL + bool "i.MX 6Dual and 6Quad SoC support" + select HAS_CAAM + select MX6_SMP + +config MX6S + bool "i.MX 6Solo SoC support" + select HAS_CAAM + +config MX6SL + bool "i.MX 6SoloLite SoC support" + +config MX6SX + bool "i.MX 6SoloX SoC support" + select HAS_CAAM + select ROM_UNIFIED_SECTIONS + +config MX6SLL + bool "i.MX 6SLL SoC support" + select ROM_UNIFIED_SECTIONS + +config MX6UL + bool "i.MX 6UltraLite SoC support" + select HAS_CAAM + select ROM_UNIFIED_SECTIONS + select SYSCOUNTER_TIMER + select SYS_L2CACHE_OFF + +config MX6ULL + bool "i.MX 6ULL SoC support" + select ROM_UNIFIED_SECTIONS + select SYSCOUNTER_TIMER + select SYS_L2CACHE_OFF + +endchoice + +config MX6UL_LITESOM + bool + select DM + select DM_THERMAL + select SUPPORT_SPL + imply CMD_DM + +config MX6UL_OPOS6UL + bool + select BOARD_LATE_INIT + select DM + select DM_GPIO + select DM_MMC + select DM_THERMAL + select SPL_DM if SPL + select SPL_OF_CONTROL if SPL + select SPL_PINCTRL if SPL + select SPL_SEPARATE_BSS if SPL + select SUPPORT_SPL + imply CMD_DM + +config MX6_OCRAM_256KB + bool "Support 256KB OCRAM" + depends on MX6D || MX6Q + help + Allows using the full 256KB size of the OCRAM on the MX6Q/MX6D series + of chips, such as for SPL. The OCRAM of the Lite series of chips is + only 128KB, so using this option will prevent the resulting code from + working on those chips. + +config MX6_DDRCAL + bool "Include dynamic DDR calibration routines" + depends on SPL + default n + help + Say "Y" if your board uses dynamic (per-boot) DDR calibration. + If unsure, say N. + +choice + prompt "MX6 board select" + optional + +config TARGET_APALIS_IMX6 + bool "Toradex Apalis iMX6 board" + depends on MX6Q + select BOARD_LATE_INIT + select DM + select DM_SERIAL + select DM_THERMAL + select SUPPORT_SPL + imply CMD_DM + imply CMD_SATA + +config TARGET_ARISTAINETOS2C + bool "Support aristainetos2-revC" + depends on MX6DL + select BOARD_LATE_INIT + select SYS_I2C_MXC + select MXC_UART + select FEC_MXC + select DM + imply CMD_SATA + imply CMD_DM + +config TARGET_ARISTAINETOS2CCSLB + bool "Support aristainetos2-revC CSL" + depends on MX6DL + select BOARD_LATE_INIT + select SYS_I2C_MXC + select MXC_UART + select FEC_MXC + select DM + imply CMD_SATA + imply CMD_DM + +config TARGET_CM_FX6 + bool "CM-FX6" + depends on MX6QDL + select BOARD_LATE_INIT + select DM + select DM_GPIO + select DM_SERIAL + select SUPPORT_SPL + imply CMD_DM + +config TARGET_COLIBRI_IMX6 + bool "Toradex Colibri iMX6 board" + select BOARD_LATE_INIT + select DM + select DM_SERIAL + select DM_THERMAL + select SUPPORT_SPL + imply CMD_DM + +config TARGET_COLIBRI_IMX6ULL + bool "Toradex Colibri iMX6ULL" + depends on MX6ULL + select BOARD_LATE_INIT + select DM + select DM_THERMAL + +config TARGET_DART_6UL + bool "Variscite imx6ULL dart(DART-SOM-6ULL)" + depends on MX6ULL + select DM + select DM_ETH + select DM_GPIO + select DM_I2C + select DM_MMC + select DM_SERIAL + select DM_THERMAL + select SUPPORT_SPL + +config TARGET_DHCOMIMX6 + bool "dh_imx6" + depends on MX6QDL + select BOARD_EARLY_INIT_F + select BOARD_LATE_INIT + select DM + select DM_THERMAL + select SUPPORT_SPL + imply CMD_DM + imply CMD_SPL + +config TARGET_DISPLAY5 + bool "LWN DISPLAY5 board" + depends on MX6Q + select DM + select DM_ETH + select DM_I2C + select DM_MMC + select DM_SPI + select DM_GPIO + select DM_SERIAL + select SUPPORT_SPL + imply CMD_DM + +config TARGET_EMBESTMX6BOARDS + bool "embestmx6boards" + select BOARD_LATE_INIT + select SUPPORT_SPL + +config TARGET_GE_BX50V3 + bool "General Electric Bx50v3" + depends on MX6Q + select BOARD_LATE_INIT + +config TARGET_GE_B1X5V2 + bool "General Electric B1x5v2" + depends on MX6QDL + select BOARD_LATE_INIT + select DM + select DM_THERMAL + select SUPPORT_SPL + +config TARGET_GW_VENTANA + bool "gw_ventana" + depends on MX6QDL + select SUPPORT_SPL + imply CMD_SATA + imply CMD_SPL + +config TARGET_KOSAGI_NOVENA + bool "Kosagi Novena" + select BOARD_LATE_INIT + select DM_ETH + select DM_GPIO + select DM_MMC + select DM_PCI + select DM_SCSI + select DM_USB + select DM_VIDEO + select OF_CONTROL + select SUPPORT_SPL + imply CMD_DM + +config TARGET_MCCMON6 + bool "mccmon6" + depends on MX6QDL + select SUPPORT_SPL + select DM + select DM_GPIO + select DM_ETH + select DM_SERIAL + select DM_I2C + select DM_SPI + imply CMD_DM + +config TARGET_MX6CUBOXI + bool "Solid-run mx6 boards" + depends on MX6QDL + select BOARD_LATE_INIT + select SUPPORT_SPL + +config TARGET_MX6LOGICPD + bool "Logic PD i.MX6 SOM" + depends on MX6Q + select SUPPORT_SPL + select BOARD_EARLY_INIT_F + select BOARD_LATE_INIT + select DM + select DM_ETH + select DM_GPIO + select DM_I2C + select DM_MMC + select DM_PMIC + select OF_CONTROL + imply CMD_DM + +config TARGET_MX6MEMCAL + bool "mx6memcal" + depends on MX6QDL + select SUPPORT_SPL + help + The mx6memcal board is a virtual board that can be used to validate + and characterize the memory layout of a new design during the initial + development and pre-production stages. + +config TARGET_MX6DL_MAMOJ + bool "Support BTicino Mamoj" + depends on MX6QDL + select DM + select DM_ETH + select DM_GPIO + select DM_I2C + select DM_MMC + select DM_PMIC + select DM_PMIC_PFUZE100 + select DM_THERMAL + select OF_CONTROL + select PINCTRL + select SPL + select SPL_DM if SPL + select SPL_GPIO_SUPPORT if SPL + select SPL_LIBCOMMON_SUPPORT if SPL + select SPL_LIBDISK_SUPPORT if SPL + select SPL_LIBGENERIC_SUPPORT if SPL + select SPL_MMC_SUPPORT if SPL + select SPL_OF_CONTROL if SPL + select SPL_OF_LIBFDT if SPL + select SPL_PINCTRL if SPL + select SPL_SEPARATE_BSS if SPL + select SPL_SERIAL_SUPPORT if SPL + select SPL_USB_GADGET if SPL + select SPL_USB_HOST_SUPPORT if SPL + select SPL_USB_SDP_SUPPORT if SPL + select SPL_WATCHDOG_SUPPORT if SPL + select SUPPORT_SPL + imply CMD_DM + +config TARGET_MX6Q_ENGICAM + bool "Support Engicam i.Core(RQS)" + depends on MX6QDL + select BOARD_LATE_INIT + select DM + select DM_ETH + select DM_GPIO + select DM_I2C + select DM_MMC + select DM_THERMAL + select OF_CONTROL + select SPL_DM if SPL + select SPL_OF_CONTROL if SPL + select SPL_OF_LIBFDT + select SPL_PINCTRL if SPL + select SPL_SEPARATE_BSS if SPL + select SUPPORT_SPL + imply CMD_DM + +config TARGET_MX6SABREAUTO + bool "mx6sabreauto" + depends on MX6QDL + select BOARD_EARLY_INIT_F + select BOARD_LATE_INIT + select DM + select DM_THERMAL + select SUPPORT_SPL + imply CMD_DM + +config TARGET_MX6SABRESD + bool "mx6sabresd" + depends on MX6QDL + select BOARD_EARLY_INIT_F + select BOARD_LATE_INIT + select DM + select DM_THERMAL + select SUPPORT_SPL + imply CMD_DM + +config TARGET_MX6SLEVK + bool "mx6slevk" + depends on MX6SL + select SUPPORT_SPL + +config TARGET_MX6SLLEVK + bool "mx6sll evk" + depends on MX6SLL + select BOARD_LATE_INIT + select DM + select DM_THERMAL + imply CMD_DM + +config TARGET_MX6SXSABRESD + bool "mx6sxsabresd" + depends on MX6SX + select BOARD_EARLY_INIT_F + select BOARD_LATE_INIT + select DM + select DM_THERMAL + select SUPPORT_SPL + +config TARGET_MX6SXSABREAUTO + bool "mx6sxsabreauto" + depends on MX6SX + select BOARD_EARLY_INIT_F + select BOARD_LATE_INIT + select DM + select DM_THERMAL + imply CMD_DM + +config TARGET_MX6UL_9X9_EVK + bool "mx6ul_9x9_evk" + depends on MX6UL + select BOARD_LATE_INIT + select DM + select DM_THERMAL + select SUPPORT_SPL + imply CMD_DM + +config TARGET_MX6UL_14X14_EVK + bool "mx6ul_14x14_evk" + depends on MX6UL + select BOARD_LATE_INIT + select DM + select DM_THERMAL + select SUPPORT_SPL + imply CMD_DM + +config TARGET_MX6UL_ENGICAM + bool "Support Engicam GEAM6UL/Is.IoT" + depends on MX6UL + select BOARD_LATE_INIT + select DM + select DM_ETH + select DM_GPIO + select DM_I2C + select DM_MMC + select DM_THERMAL + select OF_CONTROL + select SPL_DM if SPL + select SPL_OF_CONTROL if SPL + select SPL_PINCTRL if SPL + select SPL_SEPARATE_BSS if SPL + select SUPPORT_SPL + imply CMD_DM + +config TARGET_MX6ULL_14X14_EVK + bool "Support mx6ull_14x14_evk" + depends on MX6ULL + select BOARD_LATE_INIT + select DM + select DM_THERMAL + imply CMD_DM + +config TARGET_MYS_6ULX + bool "MYiR MYS-6ULX" + depends on MX6ULL + select DM + select DM_ETH + select DM_GPIO + select DM_I2C + select DM_MMC + select DM_SERIAL + select DM_THERMAL + select SUPPORT_SPL + +config TARGET_NITROGEN6X + bool "nitrogen6x" + depends on MX6DL || MX6Q || MX6QDL || MX6S + imply USB_ETHER_ASIX + imply USB_ETHER_MCS7830 + imply USB_ETHER_SMSC95XX + imply USB_HOST_ETHER + +config TARGET_NPI_IMX6ULL + bool "Seeed NPI-IMX6ULL" + depends on MX6ULL + select DM + select DM_ETH + select DM_MMC + select DM_GPIO + select DM_SERIAL + select DM_THERMAL + select SUPPORT_SPL + +config TARGET_OPOS6ULDEV + bool "Armadeus OPOS6ULDev board" + depends on MX6UL + select MX6UL_OPOS6UL + +config TARGET_PICO_IMX6 + bool "PICO-IMX6" + depends on MX6QDL + select BOARD_EARLY_INIT_F + select BOARD_LATE_INIT + select DM + select DM_THERMAL + select SUPPORT_SPL + imply CMD_DM + +config TARGET_PICO_IMX6UL + bool "PICO-IMX6UL-EMMC" + depends on MX6UL + select DM + select SUPPORT_SPL + imply CMD_DM + +config TARGET_LITEBOARD + bool "Grinn liteBoard (i.MX6UL)" + depends on MX6UL + select BOARD_LATE_INIT + select MX6UL_LITESOM + +config TARGET_PCM058 + bool "Phytec PCM058 i.MX6 Quad" + depends on MX6Q + select BOARD_LATE_INIT + select SUPPORT_SPL + select DM + select OF_CONTROL + imply CMD_DM + +config TARGET_PCL063 + bool "PHYTEC PCL063 (phyCORE-i.MX6UL)" + depends on MX6UL + select DM + select DM_ETH + select DM_GPIO + select DM_I2C + select DM_MMC + select DM_SERIAL + select DM_THERMAL + select SUPPORT_SPL + +config TARGET_PCL063_ULL + bool "PHYTEC PCL063 (phyCORE-i.MX6ULL)" + depends on MX6ULL + select DM + select DM_ETH + select DM_GPIO + select DM_I2C + select DM_MMC + select DM_SERIAL + select DM_THERMAL + select SUPPORT_SPL + +config TARGET_SOMLABS_VISIONSOM_6ULL + bool "visionsom-6ull" + depends on MX6ULL + select BOARD_LATE_INIT + select DM + select DM_ETH + select DM_GPIO + select DM_MMC + select DM_SERIAL + select DM_THERMAL + imply CMD_DM + +config TARGET_TBS2910 + bool "TBS2910 Matrix ARM mini PC" + depends on MX6Q + +config TARGET_KP_IMX6Q_TPC + bool "K+P KP_IMX6Q_TPC i.MX6 Quad" + depends on MX6QDL + select BOARD_EARLY_INIT_F + select BOARD_LATE_INIT + select DM + select SPL_DM if SPL + select DM_THERMAL + select DM_MMC + select DM_ETH + select DM_REGULATOR + select SPL_DM_REGULATOR if SPL + select DM_SERIAL + select DM_I2C + select DM_GPIO + select DM_USB + select SUPPORT_SPL + select SPL_SEPARATE_BSS if SPL + imply CMD_DM + imply CMD_SPL + +config TARGET_TQMA6 + bool "TQ Systems TQMa6 board" + select BOARD_EARLY_INIT_F + select BOARD_LATE_INIT + select MXC_SPI + select SPI + imply DM + imply DM_GPIO + imply DM_MMC + imply DM_SPI + imply DM_SPI_FLASH + imply DM_I2C + imply CMD_SF + imply CMD_DM + +config TARGET_UDOO + bool "udoo" + depends on MX6QDL + select BOARD_LATE_INIT + select SUPPORT_SPL + +config TARGET_UDOO_NEO + bool "UDOO Neo" + depends on MX6SX + select BOARD_LATE_INIT + select DM + select DM_THERMAL + select SUPPORT_SPL + imply CMD_DM + +config TARGET_SOFTING_VINING_2000 + bool "Softing VIN|ING 2000" + depends on MX6SX + select BOARD_LATE_INIT + select DM + select DM_THERMAL + select SUPPORT_SPL + imply CMD_DM + +config TARGET_WANDBOARD + bool "wandboard" + depends on MX6QDL + select BOARD_LATE_INIT + select SUPPORT_SPL + +config TARGET_WARP + bool "WaRP" + depends on MX6SL + select BOARD_LATE_INIT + +config TARGET_BRPPT2 + bool "brppt2" + depends on MX6QDL + select BOARD_LATE_INIT + select OF_CONTROL + select SPL_OF_LIBFDT + select DM + select DM_ETH + select DM_GPIO + select DM_I2C + select DM_MMC + select SUPPORT_SPL + select SPL_DM if SPL + select SPL_OF_CONTROL if SPL + help + Support + B&R BRPPT2 platform + based on Freescale's iMX6 SoC + +config TARGET_O4_IMX6ULL_NANO + bool "O4-iMX6ULL-NANO" + depends on MX6ULL + select BOARD_LATE_INIT + select DM + select DM_THERMAL + imply CMD_DM + help + Support for www.out4.ru O4-iMX6UL-NANO platform + based on Freescale's i.MX6UL/i.MX6ULL SoC. + +endchoice + +config SYS_SOC + default "mx6" + +source "board/ge/bx50v3/Kconfig" +source "board/ge/b1x5v2/Kconfig" +source "board/aristainetos/Kconfig" +source "board/armadeus/opos6uldev/Kconfig" +source "board/boundary/nitrogen6x/Kconfig" +source "board/bticino/mamoj/Kconfig" +source "board/compulab/cm_fx6/Kconfig" +source "board/dhelectronics/dh_imx6/Kconfig" +source "board/embest/mx6boards/Kconfig" +source "board/engicam/imx6q/Kconfig" +source "board/engicam/imx6ul/Kconfig" +source "board/freescale/mx6memcal/Kconfig" +source "board/freescale/mx6sabreauto/Kconfig" +source "board/freescale/mx6sabresd/Kconfig" +source "board/freescale/mx6slevk/Kconfig" +source "board/freescale/mx6sllevk/Kconfig" +source "board/freescale/mx6sxsabresd/Kconfig" +source "board/freescale/mx6sxsabreauto/Kconfig" +source "board/freescale/mx6ul_14x14_evk/Kconfig" +source "board/freescale/mx6ullevk/Kconfig" +source "board/grinn/liteboard/Kconfig" +source "board/phytec/pcm058/Kconfig" +source "board/phytec/pcl063/Kconfig" +source "board/gateworks/gw_ventana/Kconfig" +source "board/kosagi/novena/Kconfig" +source "board/softing/vining_2000/Kconfig" +source "board/liebherr/display5/Kconfig" +source "board/liebherr/mccmon6/Kconfig" +source "board/logicpd/imx6/Kconfig" +source "board/solidrun/mx6cuboxi/Kconfig" +source "board/somlabs/visionsom-6ull/Kconfig" +source "board/technexion/pico-imx6/Kconfig" +source "board/technexion/pico-imx6ul/Kconfig" +source "board/tbs/tbs2910/Kconfig" +source "board/tqc/tqma6/Kconfig" +source "board/toradex/apalis_imx6/Kconfig" +source "board/toradex/colibri_imx6/Kconfig" +source "board/toradex/colibri-imx6ull/Kconfig" +source "board/k+p/kp_imx6q_tpc/Kconfig" +source "board/udoo/Kconfig" +source "board/udoo/neo/Kconfig" +source "board/wandboard/Kconfig" +source "board/warp/Kconfig" +source "board/BuR/brppt2/Kconfig" +source "board/out4/o4-imx6ull-nano/Kconfig" + +endif diff --git a/roms/u-boot/arch/arm/mach-imx/mx6/Makefile b/roms/u-boot/arch/arm/mach-imx/mx6/Makefile new file mode 100644 index 000000000..7ea8f91e4 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/mx6/Makefile @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright 2000-2006 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# (C) Copyright 2011 Freescale Semiconductor, Inc. + +obj-y := soc.o clock.o +obj-$(CONFIG_IMX_MODULE_FUSE) += module_fuse.o +obj-$(CONFIG_SPL_BUILD) += ddr.o +obj-$(CONFIG_MP) += mp.o +obj-$(CONFIG_MX6UL_LITESOM) += litesom.o +obj-$(CONFIG_MX6UL_OPOS6UL) += opos6ul.o diff --git a/roms/u-boot/arch/arm/mach-imx/mx6/clock.c b/roms/u-boot/arch/arm/mach-imx/mx6/clock.c new file mode 100644 index 000000000..cb9d629be --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/mx6/clock.c @@ -0,0 +1,1500 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2010-2011 Freescale Semiconductor, Inc. + */ + +#include <common.h> +#include <command.h> +#include <div64.h> +#include <log.h> +#include <asm/io.h> +#include <linux/errno.h> +#include <asm/arch/imx-regs.h> +#include <asm/arch/crm_regs.h> +#include <asm/arch/clock.h> +#include <asm/arch/sys_proto.h> + +enum pll_clocks { + PLL_SYS, /* System PLL */ + PLL_BUS, /* System Bus PLL*/ + PLL_USBOTG, /* OTG USB PLL */ + PLL_ENET, /* ENET PLL */ + PLL_AUDIO, /* AUDIO PLL */ + PLL_VIDEO, /* VIDEO PLL */ +}; + +struct mxc_ccm_reg *imx_ccm = (struct mxc_ccm_reg *)CCM_BASE_ADDR; + +#ifdef CONFIG_MXC_OCOTP +void enable_ocotp_clk(unsigned char enable) +{ + u32 reg; + + reg = __raw_readl(&imx_ccm->CCGR2); + if (enable) + reg |= MXC_CCM_CCGR2_OCOTP_CTRL_MASK; + else + reg &= ~MXC_CCM_CCGR2_OCOTP_CTRL_MASK; + __raw_writel(reg, &imx_ccm->CCGR2); +} +#endif + +#ifdef CONFIG_NAND_MXS +void setup_gpmi_io_clk(u32 cfg) +{ + /* Disable clocks per ERR007177 from MX6 errata */ + clrbits_le32(&imx_ccm->CCGR4, + MXC_CCM_CCGR4_RAWNAND_U_BCH_INPUT_APB_MASK | + MXC_CCM_CCGR4_RAWNAND_U_GPMI_BCH_INPUT_BCH_MASK | + MXC_CCM_CCGR4_RAWNAND_U_GPMI_BCH_INPUT_GPMI_IO_MASK | + MXC_CCM_CCGR4_RAWNAND_U_GPMI_INPUT_APB_MASK | + MXC_CCM_CCGR4_PL301_MX6QPER1_BCH_MASK); + +#if defined(CONFIG_MX6SX) + clrbits_le32(&imx_ccm->CCGR4, MXC_CCM_CCGR4_QSPI2_ENFC_MASK); + + clrsetbits_le32(&imx_ccm->cs2cdr, + MXC_CCM_CS2CDR_QSPI2_CLK_PODF_MASK | + MXC_CCM_CS2CDR_QSPI2_CLK_PRED_MASK | + MXC_CCM_CS2CDR_QSPI2_CLK_SEL_MASK, + cfg); + + setbits_le32(&imx_ccm->CCGR4, MXC_CCM_CCGR4_QSPI2_ENFC_MASK); +#else + clrbits_le32(&imx_ccm->CCGR2, MXC_CCM_CCGR2_IOMUX_IPT_CLK_IO_MASK); + + clrsetbits_le32(&imx_ccm->cs2cdr, + MXC_CCM_CS2CDR_ENFC_CLK_PODF_MASK | + MXC_CCM_CS2CDR_ENFC_CLK_PRED_MASK | + MXC_CCM_CS2CDR_ENFC_CLK_SEL_MASK, + cfg); + + setbits_le32(&imx_ccm->CCGR2, MXC_CCM_CCGR2_IOMUX_IPT_CLK_IO_MASK); +#endif + setbits_le32(&imx_ccm->CCGR4, + MXC_CCM_CCGR4_RAWNAND_U_BCH_INPUT_APB_MASK | + MXC_CCM_CCGR4_RAWNAND_U_GPMI_BCH_INPUT_BCH_MASK | + MXC_CCM_CCGR4_RAWNAND_U_GPMI_BCH_INPUT_GPMI_IO_MASK | + MXC_CCM_CCGR4_RAWNAND_U_GPMI_INPUT_APB_MASK | + MXC_CCM_CCGR4_PL301_MX6QPER1_BCH_MASK); +} +#endif + +void enable_usboh3_clk(unsigned char enable) +{ + u32 reg; + + reg = __raw_readl(&imx_ccm->CCGR6); + if (enable) + reg |= MXC_CCM_CCGR6_USBOH3_MASK; + else + reg &= ~(MXC_CCM_CCGR6_USBOH3_MASK); + __raw_writel(reg, &imx_ccm->CCGR6); + +} + +#if defined(CONFIG_FEC_MXC) && !defined(CONFIG_MX6SX) +void enable_enet_clk(unsigned char enable) +{ + u32 mask, *addr; + + if (is_mx6ull()) { + mask = MXC_CCM_CCGR0_ENET_CLK_ENABLE_MASK; + addr = &imx_ccm->CCGR0; + } else if (is_mx6ul()) { + mask = MXC_CCM_CCGR3_ENET_MASK; + addr = &imx_ccm->CCGR3; + } else { + mask = MXC_CCM_CCGR1_ENET_MASK; + addr = &imx_ccm->CCGR1; + } + + if (enable) + setbits_le32(addr, mask); + else + clrbits_le32(addr, mask); +} +#endif + +#ifdef CONFIG_MXC_UART +void enable_uart_clk(unsigned char enable) +{ + u32 mask; + + if (is_mx6ul() || is_mx6ull()) + mask = MXC_CCM_CCGR5_UART_MASK; + else + mask = MXC_CCM_CCGR5_UART_MASK | MXC_CCM_CCGR5_UART_SERIAL_MASK; + + if (enable) + setbits_le32(&imx_ccm->CCGR5, mask); + else + clrbits_le32(&imx_ccm->CCGR5, mask); +} +#endif + +#ifdef CONFIG_MMC +int enable_usdhc_clk(unsigned char enable, unsigned bus_num) +{ + u32 mask; + + if (bus_num > 3) + return -EINVAL; + + mask = MXC_CCM_CCGR_CG_MASK << (bus_num * 2 + 2); + if (enable) + setbits_le32(&imx_ccm->CCGR6, mask); + else + clrbits_le32(&imx_ccm->CCGR6, mask); + + return 0; +} +#endif + +#ifdef CONFIG_SYS_I2C_MXC +/* i2c_num can be from 0 - 3 */ +int enable_i2c_clk(unsigned char enable, unsigned i2c_num) +{ + u32 reg; + u32 mask; + u32 *addr; + + if (i2c_num > 3) + return -EINVAL; + if (i2c_num < 3) { + mask = MXC_CCM_CCGR_CG_MASK + << (MXC_CCM_CCGR2_I2C1_SERIAL_OFFSET + + (i2c_num << 1)); + reg = __raw_readl(&imx_ccm->CCGR2); + if (enable) + reg |= mask; + else + reg &= ~mask; + __raw_writel(reg, &imx_ccm->CCGR2); + } else { + if (is_mx6sll()) + return -EINVAL; + if (is_mx6sx() || is_mx6ul() || is_mx6ull()) { + mask = MXC_CCM_CCGR6_I2C4_MASK; + addr = &imx_ccm->CCGR6; + } else { + mask = MXC_CCM_CCGR1_I2C4_SERIAL_MASK; + addr = &imx_ccm->CCGR1; + } + reg = __raw_readl(addr); + if (enable) + reg |= mask; + else + reg &= ~mask; + __raw_writel(reg, addr); + } + return 0; +} +#endif + +/* spi_num can be from 0 - SPI_MAX_NUM */ +int enable_spi_clk(unsigned char enable, unsigned spi_num) +{ + u32 reg; + u32 mask; + + if (spi_num > SPI_MAX_NUM) + return -EINVAL; + + mask = MXC_CCM_CCGR_CG_MASK << (spi_num << 1); + reg = __raw_readl(&imx_ccm->CCGR1); + if (enable) + reg |= mask; + else + reg &= ~mask; + __raw_writel(reg, &imx_ccm->CCGR1); + return 0; +} +static u32 decode_pll(enum pll_clocks pll, u32 infreq) +{ + u32 div, test_div, pll_num, pll_denom; + + switch (pll) { + case PLL_SYS: + div = __raw_readl(&imx_ccm->analog_pll_sys); + div &= BM_ANADIG_PLL_SYS_DIV_SELECT; + + return (infreq * div) >> 1; + case PLL_BUS: + div = __raw_readl(&imx_ccm->analog_pll_528); + div &= BM_ANADIG_PLL_528_DIV_SELECT; + + return infreq * (20 + (div << 1)); + case PLL_USBOTG: + div = __raw_readl(&imx_ccm->analog_usb1_pll_480_ctrl); + div &= BM_ANADIG_USB1_PLL_480_CTRL_DIV_SELECT; + + return infreq * (20 + (div << 1)); + case PLL_ENET: + div = __raw_readl(&imx_ccm->analog_pll_enet); + div &= BM_ANADIG_PLL_ENET_DIV_SELECT; + + return 25000000 * (div + (div >> 1) + 1); + case PLL_AUDIO: + div = __raw_readl(&imx_ccm->analog_pll_audio); + if (!(div & BM_ANADIG_PLL_AUDIO_ENABLE)) + return 0; + /* BM_ANADIG_PLL_AUDIO_BYPASS_CLK_SRC is ignored */ + if (div & BM_ANADIG_PLL_AUDIO_BYPASS) + return MXC_HCLK; + pll_num = __raw_readl(&imx_ccm->analog_pll_audio_num); + pll_denom = __raw_readl(&imx_ccm->analog_pll_audio_denom); + test_div = (div & BM_ANADIG_PLL_AUDIO_TEST_DIV_SELECT) >> + BP_ANADIG_PLL_AUDIO_TEST_DIV_SELECT; + div &= BM_ANADIG_PLL_AUDIO_DIV_SELECT; + if (test_div == 3) { + debug("Error test_div\n"); + return 0; + } + test_div = 1 << (2 - test_div); + + return infreq * (div + pll_num / pll_denom) / test_div; + case PLL_VIDEO: + div = __raw_readl(&imx_ccm->analog_pll_video); + if (!(div & BM_ANADIG_PLL_VIDEO_ENABLE)) + return 0; + /* BM_ANADIG_PLL_AUDIO_BYPASS_CLK_SRC is ignored */ + if (div & BM_ANADIG_PLL_VIDEO_BYPASS) + return MXC_HCLK; + pll_num = __raw_readl(&imx_ccm->analog_pll_video_num); + pll_denom = __raw_readl(&imx_ccm->analog_pll_video_denom); + test_div = (div & BM_ANADIG_PLL_VIDEO_POST_DIV_SELECT) >> + BP_ANADIG_PLL_VIDEO_POST_DIV_SELECT; + div &= BM_ANADIG_PLL_VIDEO_DIV_SELECT; + if (test_div == 3) { + debug("Error test_div\n"); + return 0; + } + test_div = 1 << (2 - test_div); + + return infreq * (div + pll_num / pll_denom) / test_div; + default: + return 0; + } + /* NOTREACHED */ +} +static u32 mxc_get_pll_pfd(enum pll_clocks pll, int pfd_num) +{ + u32 div; + u64 freq; + + switch (pll) { + case PLL_BUS: + if (!is_mx6ul() && !is_mx6ull()) { + if (pfd_num == 3) { + /* No PFD3 on PLL2 */ + return 0; + } + } + div = __raw_readl(&imx_ccm->analog_pfd_528); + freq = (u64)decode_pll(PLL_BUS, MXC_HCLK); + break; + case PLL_USBOTG: + div = __raw_readl(&imx_ccm->analog_pfd_480); + freq = (u64)decode_pll(PLL_USBOTG, MXC_HCLK); + break; + default: + /* No PFD on other PLL */ + return 0; + } + + return lldiv(freq * 18, (div & ANATOP_PFD_FRAC_MASK(pfd_num)) >> + ANATOP_PFD_FRAC_SHIFT(pfd_num)); +} + +static u32 get_mcu_main_clk(void) +{ + u32 reg, freq; + + reg = __raw_readl(&imx_ccm->cacrr); + reg &= MXC_CCM_CACRR_ARM_PODF_MASK; + reg >>= MXC_CCM_CACRR_ARM_PODF_OFFSET; + freq = decode_pll(PLL_SYS, MXC_HCLK); + + return freq / (reg + 1); +} + +u32 get_periph_clk(void) +{ + u32 reg, div = 0, freq = 0; + + reg = __raw_readl(&imx_ccm->cbcdr); + if (reg & MXC_CCM_CBCDR_PERIPH_CLK_SEL) { + div = (reg & MXC_CCM_CBCDR_PERIPH_CLK2_PODF_MASK) >> + MXC_CCM_CBCDR_PERIPH_CLK2_PODF_OFFSET; + reg = __raw_readl(&imx_ccm->cbcmr); + reg &= MXC_CCM_CBCMR_PERIPH_CLK2_SEL_MASK; + reg >>= MXC_CCM_CBCMR_PERIPH_CLK2_SEL_OFFSET; + + switch (reg) { + case 0: + freq = decode_pll(PLL_USBOTG, MXC_HCLK); + break; + case 1: + case 2: + freq = MXC_HCLK; + break; + default: + break; + } + } else { + reg = __raw_readl(&imx_ccm->cbcmr); + reg &= MXC_CCM_CBCMR_PRE_PERIPH_CLK_SEL_MASK; + reg >>= MXC_CCM_CBCMR_PRE_PERIPH_CLK_SEL_OFFSET; + + switch (reg) { + case 0: + freq = decode_pll(PLL_BUS, MXC_HCLK); + break; + case 1: + freq = mxc_get_pll_pfd(PLL_BUS, 2); + break; + case 2: + freq = mxc_get_pll_pfd(PLL_BUS, 0); + break; + case 3: + /* static / 2 divider */ + freq = mxc_get_pll_pfd(PLL_BUS, 2) / 2; + break; + default: + break; + } + } + + return freq / (div + 1); +} + +static u32 get_ipg_clk(void) +{ + u32 reg, ipg_podf; + + reg = __raw_readl(&imx_ccm->cbcdr); + reg &= MXC_CCM_CBCDR_IPG_PODF_MASK; + ipg_podf = reg >> MXC_CCM_CBCDR_IPG_PODF_OFFSET; + + return get_ahb_clk() / (ipg_podf + 1); +} + +static u32 get_ipg_per_clk(void) +{ + u32 reg, perclk_podf; + + reg = __raw_readl(&imx_ccm->cscmr1); + if (is_mx6sll() || is_mx6sl() || is_mx6sx() || + is_mx6dqp() || is_mx6ul() || is_mx6ull()) { + if (reg & MXC_CCM_CSCMR1_PER_CLK_SEL_MASK) + return MXC_HCLK; /* OSC 24Mhz */ + } + + perclk_podf = reg & MXC_CCM_CSCMR1_PERCLK_PODF_MASK; + + return get_ipg_clk() / (perclk_podf + 1); +} + +static u32 get_uart_clk(void) +{ + u32 reg, uart_podf; + u32 freq = decode_pll(PLL_USBOTG, MXC_HCLK) / 6; /* static divider */ + reg = __raw_readl(&imx_ccm->cscdr1); + + if (is_mx6sl() || is_mx6sx() || is_mx6dqp() || is_mx6ul() || + is_mx6sll() || is_mx6ull()) { + if (reg & MXC_CCM_CSCDR1_UART_CLK_SEL) + freq = MXC_HCLK; + } + + reg &= MXC_CCM_CSCDR1_UART_CLK_PODF_MASK; + uart_podf = reg >> MXC_CCM_CSCDR1_UART_CLK_PODF_OFFSET; + + return freq / (uart_podf + 1); +} + +static u32 get_cspi_clk(void) +{ + u32 reg, cspi_podf; + + reg = __raw_readl(&imx_ccm->cscdr2); + cspi_podf = (reg & MXC_CCM_CSCDR2_ECSPI_CLK_PODF_MASK) >> + MXC_CCM_CSCDR2_ECSPI_CLK_PODF_OFFSET; + + if (is_mx6dqp() || is_mx6sl() || is_mx6sx() || is_mx6ul() || + is_mx6sll() || is_mx6ull()) { + if (reg & MXC_CCM_CSCDR2_ECSPI_CLK_SEL_MASK) + return MXC_HCLK / (cspi_podf + 1); + } + + return decode_pll(PLL_USBOTG, MXC_HCLK) / (8 * (cspi_podf + 1)); +} + +static u32 get_axi_clk(void) +{ + u32 root_freq, axi_podf; + u32 cbcdr = __raw_readl(&imx_ccm->cbcdr); + + axi_podf = cbcdr & MXC_CCM_CBCDR_AXI_PODF_MASK; + axi_podf >>= MXC_CCM_CBCDR_AXI_PODF_OFFSET; + + if (cbcdr & MXC_CCM_CBCDR_AXI_SEL) { + if (cbcdr & MXC_CCM_CBCDR_AXI_ALT_SEL) + root_freq = mxc_get_pll_pfd(PLL_USBOTG, 1); + else + root_freq = mxc_get_pll_pfd(PLL_BUS, 2); + } else + root_freq = get_periph_clk(); + + return root_freq / (axi_podf + 1); +} + +static u32 get_emi_slow_clk(void) +{ + u32 emi_clk_sel, emi_slow_podf, cscmr1, root_freq = 0; + + cscmr1 = __raw_readl(&imx_ccm->cscmr1); + emi_clk_sel = cscmr1 & MXC_CCM_CSCMR1_ACLK_EMI_SLOW_MASK; + emi_clk_sel >>= MXC_CCM_CSCMR1_ACLK_EMI_SLOW_OFFSET; + emi_slow_podf = cscmr1 & MXC_CCM_CSCMR1_ACLK_EMI_SLOW_PODF_MASK; + emi_slow_podf >>= MXC_CCM_CSCMR1_ACLK_EMI_SLOW_PODF_OFFSET; + + switch (emi_clk_sel) { + case 0: + root_freq = get_axi_clk(); + break; + case 1: + root_freq = decode_pll(PLL_USBOTG, MXC_HCLK); + break; + case 2: + root_freq = mxc_get_pll_pfd(PLL_BUS, 2); + break; + case 3: + root_freq = mxc_get_pll_pfd(PLL_BUS, 0); + break; + } + + return root_freq / (emi_slow_podf + 1); +} + +static u32 get_mmdc_ch0_clk(void) +{ + u32 cbcmr = __raw_readl(&imx_ccm->cbcmr); + u32 cbcdr = __raw_readl(&imx_ccm->cbcdr); + + u32 freq, podf, per2_clk2_podf, pmu_misc2_audio_div; + + if (is_mx6sx() || is_mx6ul() || is_mx6ull() || is_mx6sl() || + is_mx6sll()) { + podf = (cbcdr & MXC_CCM_CBCDR_MMDC_CH1_PODF_MASK) >> + MXC_CCM_CBCDR_MMDC_CH1_PODF_OFFSET; + if (cbcdr & MXC_CCM_CBCDR_PERIPH2_CLK_SEL) { + per2_clk2_podf = (cbcdr & MXC_CCM_CBCDR_PERIPH2_CLK2_PODF_MASK) >> + MXC_CCM_CBCDR_PERIPH2_CLK2_PODF_OFFSET; + if (is_mx6sl()) { + if (cbcmr & MXC_CCM_CBCMR_PERIPH2_CLK2_SEL) + freq = MXC_HCLK; + else + freq = decode_pll(PLL_USBOTG, MXC_HCLK); + } else { + if (cbcmr & MXC_CCM_CBCMR_PERIPH2_CLK2_SEL) + freq = decode_pll(PLL_BUS, MXC_HCLK); + else + freq = decode_pll(PLL_USBOTG, MXC_HCLK); + } + } else { + per2_clk2_podf = 0; + switch ((cbcmr & + MXC_CCM_CBCMR_PRE_PERIPH2_CLK_SEL_MASK) >> + MXC_CCM_CBCMR_PRE_PERIPH2_CLK_SEL_OFFSET) { + case 0: + freq = decode_pll(PLL_BUS, MXC_HCLK); + break; + case 1: + freq = mxc_get_pll_pfd(PLL_BUS, 2); + break; + case 2: + freq = mxc_get_pll_pfd(PLL_BUS, 0); + break; + case 3: + if (is_mx6sl()) { + freq = mxc_get_pll_pfd(PLL_BUS, 2) >> 1; + break; + } + + pmu_misc2_audio_div = PMU_MISC2_AUDIO_DIV(__raw_readl(&imx_ccm->pmu_misc2)); + switch (pmu_misc2_audio_div) { + case 0: + case 2: + pmu_misc2_audio_div = 1; + break; + case 1: + pmu_misc2_audio_div = 2; + break; + case 3: + pmu_misc2_audio_div = 4; + break; + } + freq = decode_pll(PLL_AUDIO, MXC_HCLK) / + pmu_misc2_audio_div; + break; + } + } + return freq / (podf + 1) / (per2_clk2_podf + 1); + } else { + podf = (cbcdr & MXC_CCM_CBCDR_MMDC_CH0_PODF_MASK) >> + MXC_CCM_CBCDR_MMDC_CH0_PODF_OFFSET; + return get_periph_clk() / (podf + 1); + } +} + +#if defined(CONFIG_VIDEO_MXS) +static int enable_pll_video(u32 pll_div, u32 pll_num, u32 pll_denom, + u32 post_div) +{ + u32 reg = 0; + ulong start; + + debug("pll5 div = %d, num = %d, denom = %d\n", + pll_div, pll_num, pll_denom); + + /* Power up PLL5 video */ + writel(BM_ANADIG_PLL_VIDEO_POWERDOWN | + BM_ANADIG_PLL_VIDEO_BYPASS | + BM_ANADIG_PLL_VIDEO_DIV_SELECT | + BM_ANADIG_PLL_VIDEO_POST_DIV_SELECT, + &imx_ccm->analog_pll_video_clr); + + /* Set div, num and denom */ + switch (post_div) { + case 1: + writel(BF_ANADIG_PLL_VIDEO_DIV_SELECT(pll_div) | + BF_ANADIG_PLL_VIDEO_POST_DIV_SELECT(0x2), + &imx_ccm->analog_pll_video_set); + break; + case 2: + writel(BF_ANADIG_PLL_VIDEO_DIV_SELECT(pll_div) | + BF_ANADIG_PLL_VIDEO_POST_DIV_SELECT(0x1), + &imx_ccm->analog_pll_video_set); + break; + case 4: + writel(BF_ANADIG_PLL_VIDEO_DIV_SELECT(pll_div) | + BF_ANADIG_PLL_VIDEO_POST_DIV_SELECT(0x0), + &imx_ccm->analog_pll_video_set); + break; + default: + puts("Wrong test_div!\n"); + return -EINVAL; + } + + writel(BF_ANADIG_PLL_VIDEO_NUM_A(pll_num), + &imx_ccm->analog_pll_video_num); + writel(BF_ANADIG_PLL_VIDEO_DENOM_B(pll_denom), + &imx_ccm->analog_pll_video_denom); + + /* Wait PLL5 lock */ + start = get_timer(0); /* Get current timestamp */ + + do { + reg = readl(&imx_ccm->analog_pll_video); + if (reg & BM_ANADIG_PLL_VIDEO_LOCK) { + /* Enable PLL out */ + writel(BM_ANADIG_PLL_VIDEO_ENABLE, + &imx_ccm->analog_pll_video_set); + return 0; + } + } while (get_timer(0) < (start + 10)); /* Wait 10ms */ + + puts("Lock PLL5 timeout\n"); + + return -ETIME; +} + +/* + * 24M--> PLL_VIDEO -> LCDIFx_PRED -> LCDIFx_PODF -> LCD + * + * 'freq' using KHz as unit, see driver/video/mxsfb.c. + */ +void mxs_set_lcdclk(u32 base_addr, u32 freq) +{ + u32 reg = 0; + u32 hck = MXC_HCLK / 1000; + /* DIV_SELECT ranges from 27 to 54 */ + u32 min = hck * 27; + u32 max = hck * 54; + u32 temp, best = 0; + u32 i, j, max_pred = 8, max_postd = 8, pred = 1, postd = 1; + u32 pll_div, pll_num, pll_denom, post_div = 1; + + debug("mxs_set_lcdclk, freq = %dKHz\n", freq); + + if (!is_mx6sx() && !is_mx6ul() && !is_mx6ull() && !is_mx6sl() && + !is_mx6sll()) { + debug("This chip not support lcd!\n"); + return; + } + + if (!is_mx6sl()) { + if (base_addr == LCDIF1_BASE_ADDR) { + reg = readl(&imx_ccm->cscdr2); + /* Can't change clocks when clock not from pre-mux */ + if ((reg & MXC_CCM_CSCDR2_LCDIF1_CLK_SEL_MASK) != 0) + return; + } + } + + if (is_mx6sx()) { + reg = readl(&imx_ccm->cscdr2); + /* Can't change clocks when clock not from pre-mux */ + if ((reg & MXC_CCM_CSCDR2_LCDIF2_CLK_SEL_MASK) != 0) + return; + } + + temp = freq * max_pred * max_postd; + if (temp < min) { + /* + * Register: PLL_VIDEO + * Bit Field: POST_DIV_SELECT + * 00 — Divide by 4. + * 01 — Divide by 2. + * 10 — Divide by 1. + * 11 — Reserved + * No need to check post_div(1) + */ + for (post_div = 2; post_div <= 4; post_div <<= 1) { + if ((temp * post_div) > min) { + freq *= post_div; + break; + } + } + + if (post_div > 4) { + printf("Fail to set rate to %dkhz", freq); + return; + } + } + + /* Choose the best pred and postd to match freq for lcd */ + for (i = 1; i <= max_pred; i++) { + for (j = 1; j <= max_postd; j++) { + temp = freq * i * j; + if (temp > max || temp < min) + continue; + if (best == 0 || temp < best) { + best = temp; + pred = i; + postd = j; + } + } + } + + if (best == 0) { + printf("Fail to set rate to %dKHz", freq); + return; + } + + debug("best %d, pred = %d, postd = %d\n", best, pred, postd); + + pll_div = best / hck; + pll_denom = 1000000; + pll_num = (best - hck * pll_div) * pll_denom / hck; + + /* + * pll_num + * (24MHz * (pll_div + --------- )) + * pll_denom + *freq KHz = -------------------------------- + * post_div * pred * postd * 1000 + */ + + if (base_addr == LCDIF1_BASE_ADDR) { + if (enable_pll_video(pll_div, pll_num, pll_denom, post_div)) + return; + + enable_lcdif_clock(base_addr, 0); + if (!is_mx6sl()) { + /* Select pre-lcd clock to PLL5 and set pre divider */ + clrsetbits_le32(&imx_ccm->cscdr2, + MXC_CCM_CSCDR2_LCDIF1_PRED_SEL_MASK | + MXC_CCM_CSCDR2_LCDIF1_PRE_DIV_MASK, + (0x2 << MXC_CCM_CSCDR2_LCDIF1_PRED_SEL_OFFSET) | + ((pred - 1) << + MXC_CCM_CSCDR2_LCDIF1_PRE_DIV_OFFSET)); + + /* Set the post divider */ + clrsetbits_le32(&imx_ccm->cbcmr, + MXC_CCM_CBCMR_LCDIF1_PODF_MASK, + ((postd - 1) << + MXC_CCM_CBCMR_LCDIF1_PODF_OFFSET)); + } else { + /* Select pre-lcd clock to PLL5 and set pre divider */ + clrsetbits_le32(&imx_ccm->cscdr2, + MXC_CCM_CSCDR2_LCDIF_PIX_CLK_SEL_MASK | + MXC_CCM_CSCDR2_LCDIF_PIX_PRE_DIV_MASK, + (0x2 << MXC_CCM_CSCDR2_LCDIF_PIX_CLK_SEL_OFFSET) | + ((pred - 1) << + MXC_CCM_CSCDR2_LCDIF_PIX_PRE_DIV_OFFSET)); + + /* Set the post divider */ + clrsetbits_le32(&imx_ccm->cscmr1, + MXC_CCM_CSCMR1_LCDIF_PIX_PODF_MASK, + (((postd - 1)^0x6) << + MXC_CCM_CSCMR1_LCDIF_PIX_PODF_OFFSET)); + } + + enable_lcdif_clock(base_addr, 1); + } else if (is_mx6sx()) { + /* Setting LCDIF2 for i.MX6SX */ + if (enable_pll_video(pll_div, pll_num, pll_denom, post_div)) + return; + + enable_lcdif_clock(base_addr, 0); + /* Select pre-lcd clock to PLL5 and set pre divider */ + clrsetbits_le32(&imx_ccm->cscdr2, + MXC_CCM_CSCDR2_LCDIF2_PRED_SEL_MASK | + MXC_CCM_CSCDR2_LCDIF2_PRE_DIV_MASK, + (0x2 << MXC_CCM_CSCDR2_LCDIF2_PRED_SEL_OFFSET) | + ((pred - 1) << + MXC_CCM_CSCDR2_LCDIF2_PRE_DIV_OFFSET)); + + /* Set the post divider */ + clrsetbits_le32(&imx_ccm->cscmr1, + MXC_CCM_CSCMR1_LCDIF2_PODF_MASK, + ((postd - 1) << + MXC_CCM_CSCMR1_LCDIF2_PODF_OFFSET)); + + enable_lcdif_clock(base_addr, 1); + } +} + +int enable_lcdif_clock(u32 base_addr, bool enable) +{ + u32 reg = 0; + u32 lcdif_clk_sel_mask, lcdif_ccgr3_mask; + + if (is_mx6sx()) { + if ((base_addr != LCDIF1_BASE_ADDR) && + (base_addr != LCDIF2_BASE_ADDR)) { + puts("Wrong LCD interface!\n"); + return -EINVAL; + } + /* Set to pre-mux clock at default */ + lcdif_clk_sel_mask = (base_addr == LCDIF2_BASE_ADDR) ? + MXC_CCM_CSCDR2_LCDIF2_CLK_SEL_MASK : + MXC_CCM_CSCDR2_LCDIF1_CLK_SEL_MASK; + lcdif_ccgr3_mask = (base_addr == LCDIF2_BASE_ADDR) ? + (MXC_CCM_CCGR3_LCDIF2_PIX_MASK | + MXC_CCM_CCGR3_DISP_AXI_MASK) : + (MXC_CCM_CCGR3_LCDIF1_PIX_MASK | + MXC_CCM_CCGR3_DISP_AXI_MASK); + } else if (is_mx6ul() || is_mx6ull() || is_mx6sll()) { + if (base_addr != LCDIF1_BASE_ADDR) { + puts("Wrong LCD interface!\n"); + return -EINVAL; + } + /* Set to pre-mux clock at default */ + lcdif_clk_sel_mask = MXC_CCM_CSCDR2_LCDIF1_CLK_SEL_MASK; + lcdif_ccgr3_mask = MXC_CCM_CCGR3_LCDIF1_PIX_MASK; + } else if (is_mx6sl()) { + if (base_addr != LCDIF1_BASE_ADDR) { + puts("Wrong LCD interface!\n"); + return -EINVAL; + } + + reg = readl(&imx_ccm->CCGR3); + reg &= ~(MXC_CCM_CCGR3_LCDIF_AXI_MASK | + MXC_CCM_CCGR3_LCDIF_PIX_MASK); + writel(reg, &imx_ccm->CCGR3); + + if (enable) { + reg = readl(&imx_ccm->cscdr3); + reg &= ~MXC_CCM_CSCDR3_LCDIF_AXI_CLK_SEL_MASK; + reg |= 1 << MXC_CCM_CSCDR3_LCDIF_AXI_CLK_SEL_OFFSET; + writel(reg, &imx_ccm->cscdr3); + + reg = readl(&imx_ccm->CCGR3); + reg |= MXC_CCM_CCGR3_LCDIF_AXI_MASK | + MXC_CCM_CCGR3_LCDIF_PIX_MASK; + writel(reg, &imx_ccm->CCGR3); + } + + return 0; + } else { + return 0; + } + + /* Gate LCDIF clock first */ + reg = readl(&imx_ccm->CCGR3); + reg &= ~lcdif_ccgr3_mask; + writel(reg, &imx_ccm->CCGR3); + + reg = readl(&imx_ccm->CCGR2); + reg &= ~MXC_CCM_CCGR2_LCD_MASK; + writel(reg, &imx_ccm->CCGR2); + + if (enable) { + /* Select pre-mux */ + reg = readl(&imx_ccm->cscdr2); + reg &= ~lcdif_clk_sel_mask; + writel(reg, &imx_ccm->cscdr2); + + /* Enable the LCDIF pix clock */ + reg = readl(&imx_ccm->CCGR3); + reg |= lcdif_ccgr3_mask; + writel(reg, &imx_ccm->CCGR3); + + reg = readl(&imx_ccm->CCGR2); + reg |= MXC_CCM_CCGR2_LCD_MASK; + writel(reg, &imx_ccm->CCGR2); + } + + return 0; +} +#endif + +#ifdef CONFIG_FSL_QSPI +/* qspi_num can be from 0 - 1 */ +void enable_qspi_clk(int qspi_num) +{ + u32 reg = 0; + /* Enable QuadSPI clock */ + switch (qspi_num) { + case 0: + /* disable the clock gate */ + clrbits_le32(&imx_ccm->CCGR3, MXC_CCM_CCGR3_QSPI1_MASK); + + /* set 50M : (50 = 396 / 2 / 4) */ + reg = readl(&imx_ccm->cscmr1); + reg &= ~(MXC_CCM_CSCMR1_QSPI1_PODF_MASK | + MXC_CCM_CSCMR1_QSPI1_CLK_SEL_MASK); + reg |= ((1 << MXC_CCM_CSCMR1_QSPI1_PODF_OFFSET) | + (2 << MXC_CCM_CSCMR1_QSPI1_CLK_SEL_OFFSET)); + writel(reg, &imx_ccm->cscmr1); + + /* enable the clock gate */ + setbits_le32(&imx_ccm->CCGR3, MXC_CCM_CCGR3_QSPI1_MASK); + break; + case 1: + /* + * disable the clock gate + * QSPI2 and GPMI_BCH_INPUT_GPMI_IO share the same clock gate, + * disable both of them. + */ + clrbits_le32(&imx_ccm->CCGR4, MXC_CCM_CCGR4_QSPI2_ENFC_MASK | + MXC_CCM_CCGR4_RAWNAND_U_GPMI_BCH_INPUT_GPMI_IO_MASK); + + /* set 50M : (50 = 396 / 2 / 4) */ + reg = readl(&imx_ccm->cs2cdr); + reg &= ~(MXC_CCM_CS2CDR_QSPI2_CLK_PODF_MASK | + MXC_CCM_CS2CDR_QSPI2_CLK_PRED_MASK | + MXC_CCM_CS2CDR_QSPI2_CLK_SEL_MASK); + reg |= (MXC_CCM_CS2CDR_QSPI2_CLK_PRED(0x1) | + MXC_CCM_CS2CDR_QSPI2_CLK_SEL(0x3)); + writel(reg, &imx_ccm->cs2cdr); + + /*enable the clock gate*/ + setbits_le32(&imx_ccm->CCGR4, MXC_CCM_CCGR4_QSPI2_ENFC_MASK | + MXC_CCM_CCGR4_RAWNAND_U_GPMI_BCH_INPUT_GPMI_IO_MASK); + break; + default: + break; + } +} +#endif + +#ifdef CONFIG_FEC_MXC +int enable_fec_anatop_clock(int fec_id, enum enet_freq freq) +{ + u32 reg = 0; + s32 timeout = 100000; + + struct anatop_regs __iomem *anatop = + (struct anatop_regs __iomem *)ANATOP_BASE_ADDR; + + if (freq < ENET_25MHZ || freq > ENET_125MHZ) + return -EINVAL; + + reg = readl(&anatop->pll_enet); + + if (fec_id == 0) { + reg &= ~BM_ANADIG_PLL_ENET_DIV_SELECT; + reg |= BF_ANADIG_PLL_ENET_DIV_SELECT(freq); + } else if (fec_id == 1) { + /* Only i.MX6SX/UL support ENET2 */ + if (!(is_mx6sx() || is_mx6ul() || is_mx6ull())) + return -EINVAL; + reg &= ~BM_ANADIG_PLL_ENET2_DIV_SELECT; + reg |= BF_ANADIG_PLL_ENET2_DIV_SELECT(freq); + } else { + return -EINVAL; + } + + if ((reg & BM_ANADIG_PLL_ENET_POWERDOWN) || + (!(reg & BM_ANADIG_PLL_ENET_LOCK))) { + reg &= ~BM_ANADIG_PLL_ENET_POWERDOWN; + writel(reg, &anatop->pll_enet); + while (timeout--) { + if (readl(&anatop->pll_enet) & BM_ANADIG_PLL_ENET_LOCK) + break; + } + if (timeout < 0) + return -ETIMEDOUT; + } + + /* Enable FEC clock */ + if (fec_id == 0) + reg |= BM_ANADIG_PLL_ENET_ENABLE; + else + reg |= BM_ANADIG_PLL_ENET2_ENABLE; + reg &= ~BM_ANADIG_PLL_ENET_BYPASS; + writel(reg, &anatop->pll_enet); + +#ifdef CONFIG_MX6SX + /* Disable enet system clcok before switching clock parent */ + reg = readl(&imx_ccm->CCGR3); + reg &= ~MXC_CCM_CCGR3_ENET_MASK; + writel(reg, &imx_ccm->CCGR3); + + /* + * Set enet ahb clock to 200MHz + * pll2_pfd2_396m-> ENET_PODF-> ENET_AHB + */ + reg = readl(&imx_ccm->chsccdr); + reg &= ~(MXC_CCM_CHSCCDR_ENET_PRE_CLK_SEL_MASK + | MXC_CCM_CHSCCDR_ENET_PODF_MASK + | MXC_CCM_CHSCCDR_ENET_CLK_SEL_MASK); + /* PLL2 PFD2 */ + reg |= (4 << MXC_CCM_CHSCCDR_ENET_PRE_CLK_SEL_OFFSET); + /* Div = 2*/ + reg |= (1 << MXC_CCM_CHSCCDR_ENET_PODF_OFFSET); + reg |= (0 << MXC_CCM_CHSCCDR_ENET_CLK_SEL_OFFSET); + writel(reg, &imx_ccm->chsccdr); + + /* Enable enet system clock */ + reg = readl(&imx_ccm->CCGR3); + reg |= MXC_CCM_CCGR3_ENET_MASK; + writel(reg, &imx_ccm->CCGR3); +#endif + return 0; +} +#endif + +static u32 get_usdhc_clk(u32 port) +{ + u32 root_freq = 0, usdhc_podf = 0, clk_sel = 0; + u32 cscmr1 = __raw_readl(&imx_ccm->cscmr1); + u32 cscdr1 = __raw_readl(&imx_ccm->cscdr1); + + if (is_mx6ul() || is_mx6ull()) { + if (port > 1) + return 0; + } + + if (is_mx6sll()) { + if (port > 2) + return 0; + } + + switch (port) { + case 0: + usdhc_podf = (cscdr1 & MXC_CCM_CSCDR1_USDHC1_PODF_MASK) >> + MXC_CCM_CSCDR1_USDHC1_PODF_OFFSET; + clk_sel = cscmr1 & MXC_CCM_CSCMR1_USDHC1_CLK_SEL; + + break; + case 1: + usdhc_podf = (cscdr1 & MXC_CCM_CSCDR1_USDHC2_PODF_MASK) >> + MXC_CCM_CSCDR1_USDHC2_PODF_OFFSET; + clk_sel = cscmr1 & MXC_CCM_CSCMR1_USDHC2_CLK_SEL; + + break; + case 2: + usdhc_podf = (cscdr1 & MXC_CCM_CSCDR1_USDHC3_PODF_MASK) >> + MXC_CCM_CSCDR1_USDHC3_PODF_OFFSET; + clk_sel = cscmr1 & MXC_CCM_CSCMR1_USDHC3_CLK_SEL; + + break; + case 3: + usdhc_podf = (cscdr1 & MXC_CCM_CSCDR1_USDHC4_PODF_MASK) >> + MXC_CCM_CSCDR1_USDHC4_PODF_OFFSET; + clk_sel = cscmr1 & MXC_CCM_CSCMR1_USDHC4_CLK_SEL; + + break; + default: + break; + } + + if (clk_sel) + root_freq = mxc_get_pll_pfd(PLL_BUS, 0); + else + root_freq = mxc_get_pll_pfd(PLL_BUS, 2); + + return root_freq / (usdhc_podf + 1); +} + +u32 imx_get_uartclk(void) +{ + return get_uart_clk(); +} + +u32 imx_get_fecclk(void) +{ + return mxc_get_clock(MXC_IPG_CLK); +} + +#if defined(CONFIG_SATA) || defined(CONFIG_PCIE_IMX) +static int enable_enet_pll(uint32_t en) +{ + struct mxc_ccm_reg *const imx_ccm + = (struct mxc_ccm_reg *) CCM_BASE_ADDR; + s32 timeout = 100000; + u32 reg = 0; + + /* Enable PLLs */ + reg = readl(&imx_ccm->analog_pll_enet); + reg &= ~BM_ANADIG_PLL_SYS_POWERDOWN; + writel(reg, &imx_ccm->analog_pll_enet); + reg |= BM_ANADIG_PLL_SYS_ENABLE; + while (timeout--) { + if (readl(&imx_ccm->analog_pll_enet) & BM_ANADIG_PLL_SYS_LOCK) + break; + } + if (timeout <= 0) + return -EIO; + reg &= ~BM_ANADIG_PLL_SYS_BYPASS; + writel(reg, &imx_ccm->analog_pll_enet); + reg |= en; + writel(reg, &imx_ccm->analog_pll_enet); + return 0; +} +#endif + +#ifdef CONFIG_SATA +static void ungate_sata_clock(void) +{ + struct mxc_ccm_reg *const imx_ccm = + (struct mxc_ccm_reg *)CCM_BASE_ADDR; + + /* Enable SATA clock. */ + setbits_le32(&imx_ccm->CCGR5, MXC_CCM_CCGR5_SATA_MASK); +} + +int enable_sata_clock(void) +{ + ungate_sata_clock(); + return enable_enet_pll(BM_ANADIG_PLL_ENET_ENABLE_SATA); +} + +void disable_sata_clock(void) +{ + struct mxc_ccm_reg *const imx_ccm = + (struct mxc_ccm_reg *)CCM_BASE_ADDR; + + clrbits_le32(&imx_ccm->CCGR5, MXC_CCM_CCGR5_SATA_MASK); +} +#endif + +#ifdef CONFIG_PCIE_IMX +static void ungate_pcie_clock(void) +{ + struct mxc_ccm_reg *const imx_ccm = + (struct mxc_ccm_reg *)CCM_BASE_ADDR; + + /* Enable PCIe clock. */ + setbits_le32(&imx_ccm->CCGR4, MXC_CCM_CCGR4_PCIE_MASK); +} + +int enable_pcie_clock(void) +{ + struct anatop_regs *anatop_regs = + (struct anatop_regs *)ANATOP_BASE_ADDR; + struct mxc_ccm_reg *ccm_regs = (struct mxc_ccm_reg *)CCM_BASE_ADDR; + u32 lvds1_clk_sel; + + /* + * Here be dragons! + * + * The register ANATOP_MISC1 is not documented in the Freescale + * MX6RM. The register that is mapped in the ANATOP space and + * marked as ANATOP_MISC1 is actually documented in the PMU section + * of the datasheet as PMU_MISC1. + * + * Switch LVDS clock source to SATA (0xb) on mx6q/dl or PCI (0xa) on + * mx6sx, disable clock INPUT and enable clock OUTPUT. This is important + * for PCI express link that is clocked from the i.MX6. + */ +#define ANADIG_ANA_MISC1_LVDSCLK1_IBEN (1 << 12) +#define ANADIG_ANA_MISC1_LVDSCLK1_OBEN (1 << 10) +#define ANADIG_ANA_MISC1_LVDS1_CLK_SEL_MASK 0x0000001F +#define ANADIG_ANA_MISC1_LVDS1_CLK_SEL_PCIE_REF 0xa +#define ANADIG_ANA_MISC1_LVDS1_CLK_SEL_SATA_REF 0xb + + if (is_mx6sx()) + lvds1_clk_sel = ANADIG_ANA_MISC1_LVDS1_CLK_SEL_PCIE_REF; + else + lvds1_clk_sel = ANADIG_ANA_MISC1_LVDS1_CLK_SEL_SATA_REF; + + clrsetbits_le32(&anatop_regs->ana_misc1, + ANADIG_ANA_MISC1_LVDSCLK1_IBEN | + ANADIG_ANA_MISC1_LVDS1_CLK_SEL_MASK, + ANADIG_ANA_MISC1_LVDSCLK1_OBEN | lvds1_clk_sel); + + /* PCIe reference clock sourced from AXI. */ + clrbits_le32(&ccm_regs->cbcmr, MXC_CCM_CBCMR_PCIE_AXI_CLK_SEL); + + /* Party time! Ungate the clock to the PCIe. */ +#ifdef CONFIG_SATA + ungate_sata_clock(); +#endif + ungate_pcie_clock(); + + return enable_enet_pll(BM_ANADIG_PLL_ENET_ENABLE_SATA | + BM_ANADIG_PLL_ENET_ENABLE_PCIE); +} +#endif + +#ifdef CONFIG_IMX_HAB +void hab_caam_clock_enable(unsigned char enable) +{ + u32 reg; + + if (is_mx6ull() || is_mx6sll()) { + /* CG5, DCP clock */ + reg = __raw_readl(&imx_ccm->CCGR0); + if (enable) + reg |= MXC_CCM_CCGR0_DCP_CLK_MASK; + else + reg &= ~MXC_CCM_CCGR0_DCP_CLK_MASK; + __raw_writel(reg, &imx_ccm->CCGR0); + } else { + /* CG4 ~ CG6, CAAM clocks */ + reg = __raw_readl(&imx_ccm->CCGR0); + if (enable) + reg |= (MXC_CCM_CCGR0_CAAM_WRAPPER_IPG_MASK | + MXC_CCM_CCGR0_CAAM_WRAPPER_ACLK_MASK | + MXC_CCM_CCGR0_CAAM_SECURE_MEM_MASK); + else + reg &= ~(MXC_CCM_CCGR0_CAAM_WRAPPER_IPG_MASK | + MXC_CCM_CCGR0_CAAM_WRAPPER_ACLK_MASK | + MXC_CCM_CCGR0_CAAM_SECURE_MEM_MASK); + __raw_writel(reg, &imx_ccm->CCGR0); + } + + /* EMI slow clk */ + reg = __raw_readl(&imx_ccm->CCGR6); + if (enable) + reg |= MXC_CCM_CCGR6_EMI_SLOW_MASK; + else + reg &= ~MXC_CCM_CCGR6_EMI_SLOW_MASK; + __raw_writel(reg, &imx_ccm->CCGR6); +} +#endif + +static void enable_pll3(void) +{ + struct anatop_regs __iomem *anatop = + (struct anatop_regs __iomem *)ANATOP_BASE_ADDR; + + /* make sure pll3 is enabled */ + if ((readl(&anatop->usb1_pll_480_ctrl) & + BM_ANADIG_USB1_PLL_480_CTRL_LOCK) == 0) { + /* enable pll's power */ + writel(BM_ANADIG_USB1_PLL_480_CTRL_POWER, + &anatop->usb1_pll_480_ctrl_set); + writel(0x80, &anatop->ana_misc2_clr); + /* wait for pll lock */ + while ((readl(&anatop->usb1_pll_480_ctrl) & + BM_ANADIG_USB1_PLL_480_CTRL_LOCK) == 0) + ; + /* disable bypass */ + writel(BM_ANADIG_USB1_PLL_480_CTRL_BYPASS, + &anatop->usb1_pll_480_ctrl_clr); + /* enable pll output */ + writel(BM_ANADIG_USB1_PLL_480_CTRL_ENABLE, + &anatop->usb1_pll_480_ctrl_set); + } +} + +void enable_thermal_clk(void) +{ + enable_pll3(); +} + +#ifdef CONFIG_MTD_NOR_FLASH +void enable_eim_clk(unsigned char enable) +{ + u32 reg; + + reg = __raw_readl(&imx_ccm->CCGR6); + if (enable) + reg |= MXC_CCM_CCGR6_EMI_SLOW_MASK; + else + reg &= ~MXC_CCM_CCGR6_EMI_SLOW_MASK; + __raw_writel(reg, &imx_ccm->CCGR6); +} +#endif + +unsigned int mxc_get_clock(enum mxc_clock clk) +{ + switch (clk) { + case MXC_ARM_CLK: + return get_mcu_main_clk(); + case MXC_PER_CLK: + return get_periph_clk(); + case MXC_AHB_CLK: + return get_ahb_clk(); + case MXC_IPG_CLK: + return get_ipg_clk(); + case MXC_IPG_PERCLK: + case MXC_I2C_CLK: + return get_ipg_per_clk(); + case MXC_UART_CLK: + return get_uart_clk(); + case MXC_CSPI_CLK: + return get_cspi_clk(); + case MXC_AXI_CLK: + return get_axi_clk(); + case MXC_EMI_SLOW_CLK: + return get_emi_slow_clk(); + case MXC_DDR_CLK: + return get_mmdc_ch0_clk(); + case MXC_ESDHC_CLK: + return get_usdhc_clk(0); + case MXC_ESDHC2_CLK: + return get_usdhc_clk(1); + case MXC_ESDHC3_CLK: + return get_usdhc_clk(2); + case MXC_ESDHC4_CLK: + return get_usdhc_clk(3); + case MXC_SATA_CLK: + return get_ahb_clk(); + default: + printf("Unsupported MXC CLK: %d\n", clk); + break; + } + + return 0; +} + +#ifndef CONFIG_MX6SX +void enable_ipu_clock(void) +{ + struct mxc_ccm_reg *mxc_ccm = (struct mxc_ccm_reg *)CCM_BASE_ADDR; + + setbits_le32(&mxc_ccm->CCGR3, MXC_CCM_CCGR3_IPU1_IPU_MASK); + + if (is_mx6dqp()) { + setbits_le32(&mxc_ccm->CCGR6, MXC_CCM_CCGR6_PRG_CLK0_MASK); + setbits_le32(&mxc_ccm->CCGR3, MXC_CCM_CCGR3_IPU2_IPU_MASK); + } +} + +void disable_ipu_clock(void) +{ + struct mxc_ccm_reg *mxc_ccm = (struct mxc_ccm_reg *)CCM_BASE_ADDR; + + clrbits_le32(&mxc_ccm->CCGR3, MXC_CCM_CCGR3_IPU1_IPU_MASK); + + if (is_mx6dqp()) { + clrbits_le32(&mxc_ccm->CCGR6, MXC_CCM_CCGR6_PRG_CLK0_MASK); + clrbits_le32(&mxc_ccm->CCGR3, MXC_CCM_CCGR3_IPU2_IPU_MASK); + } +} +#endif + +#ifndef CONFIG_SPL_BUILD +/* + * Dump some core clockes. + */ +int do_mx6_showclocks(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + u32 freq; + freq = decode_pll(PLL_SYS, MXC_HCLK); + printf("PLL_SYS %8d MHz\n", freq / 1000000); + freq = decode_pll(PLL_BUS, MXC_HCLK); + printf("PLL_BUS %8d MHz\n", freq / 1000000); + freq = decode_pll(PLL_USBOTG, MXC_HCLK); + printf("PLL_OTG %8d MHz\n", freq / 1000000); + freq = decode_pll(PLL_ENET, MXC_HCLK); + printf("PLL_NET %8d MHz\n", freq / 1000000); + + printf("\n"); + printf("ARM %8d kHz\n", mxc_get_clock(MXC_ARM_CLK) / 1000); + printf("IPG %8d kHz\n", mxc_get_clock(MXC_IPG_CLK) / 1000); + printf("UART %8d kHz\n", mxc_get_clock(MXC_UART_CLK) / 1000); +#ifdef CONFIG_MXC_SPI + printf("CSPI %8d kHz\n", mxc_get_clock(MXC_CSPI_CLK) / 1000); +#endif + printf("AHB %8d kHz\n", mxc_get_clock(MXC_AHB_CLK) / 1000); + printf("AXI %8d kHz\n", mxc_get_clock(MXC_AXI_CLK) / 1000); + printf("DDR %8d kHz\n", mxc_get_clock(MXC_DDR_CLK) / 1000); + printf("USDHC1 %8d kHz\n", mxc_get_clock(MXC_ESDHC_CLK) / 1000); + printf("USDHC2 %8d kHz\n", mxc_get_clock(MXC_ESDHC2_CLK) / 1000); + printf("USDHC3 %8d kHz\n", mxc_get_clock(MXC_ESDHC3_CLK) / 1000); + printf("USDHC4 %8d kHz\n", mxc_get_clock(MXC_ESDHC4_CLK) / 1000); + printf("EMI SLOW %8d kHz\n", mxc_get_clock(MXC_EMI_SLOW_CLK) / 1000); + printf("IPG PERCLK %8d kHz\n", mxc_get_clock(MXC_IPG_PERCLK) / 1000); + + return 0; +} + +#if defined(CONFIG_MX6Q) || defined(CONFIG_MX6D) || defined(CONFIG_MX6DL) || \ + defined(CONFIG_MX6S) || defined(CONFIG_MX6QDL) +static void disable_ldb_di_clock_sources(void) +{ + struct mxc_ccm_reg *mxc_ccm = (struct mxc_ccm_reg *)CCM_BASE_ADDR; + int reg; + + /* Make sure PFDs are disabled at boot. */ + reg = readl(&mxc_ccm->analog_pfd_528); + /* Cannot disable pll2_pfd2_396M, as it is the MMDC clock in iMX6DL */ + if (is_mx6sdl()) + reg |= 0x80008080; + else + reg |= 0x80808080; + writel(reg, &mxc_ccm->analog_pfd_528); + + /* Disable PLL3 PFDs */ + reg = readl(&mxc_ccm->analog_pfd_480); + reg |= 0x80808080; + writel(reg, &mxc_ccm->analog_pfd_480); + + /* Disable PLL5 */ + reg = readl(&mxc_ccm->analog_pll_video); + reg &= ~(1 << 13); + writel(reg, &mxc_ccm->analog_pll_video); +} + +static void enable_ldb_di_clock_sources(void) +{ + struct mxc_ccm_reg *mxc_ccm = (struct mxc_ccm_reg *)CCM_BASE_ADDR; + int reg; + + reg = readl(&mxc_ccm->analog_pfd_528); + if (is_mx6sdl()) + reg &= ~(0x80008080); + else + reg &= ~(0x80808080); + writel(reg, &mxc_ccm->analog_pfd_528); + + reg = readl(&mxc_ccm->analog_pfd_480); + reg &= ~(0x80808080); + writel(reg, &mxc_ccm->analog_pfd_480); +} + +/* + * Try call this function as early in the boot process as possible since the + * function temporarily disables PLL2 PFD's, PLL3 PFD's and PLL5. + */ +void select_ldb_di_clock_source(enum ldb_di_clock clk) +{ + struct mxc_ccm_reg *mxc_ccm = (struct mxc_ccm_reg *)CCM_BASE_ADDR; + int reg; + + /* + * Need to follow a strict procedure when changing the LDB + * clock, else we can introduce a glitch. Things to keep in + * mind: + * 1. The current and new parent clocks must be disabled. + * 2. The default clock for ldb_dio_clk is mmdc_ch1 which has + * no CG bit. + * 3. In the RTL implementation of the LDB_DI_CLK_SEL mux + * the top four options are in one mux and the PLL3 option along + * with another option is in the second mux. There is third mux + * used to decide between the first and second mux. + * The code below switches the parent to the bottom mux first + * and then manipulates the top mux. This ensures that no glitch + * will enter the divider. + * + * Need to disable MMDC_CH1 clock manually as there is no CG bit + * for this clock. The only way to disable this clock is to move + * it to pll3_sw_clk and then to disable pll3_sw_clk + * Make sure periph2_clk2_sel is set to pll3_sw_clk + */ + + /* Disable all ldb_di clock parents */ + disable_ldb_di_clock_sources(); + + /* Set MMDC_CH1 mask bit */ + reg = readl(&mxc_ccm->ccdr); + reg |= MXC_CCM_CCDR_MMDC_CH1_HS_MASK; + writel(reg, &mxc_ccm->ccdr); + + /* Set periph2_clk2_sel to be sourced from PLL3_sw_clk */ + reg = readl(&mxc_ccm->cbcmr); + reg &= ~MXC_CCM_CBCMR_PERIPH2_CLK2_SEL; + writel(reg, &mxc_ccm->cbcmr); + + /* + * Set the periph2_clk_sel to the top mux so that + * mmdc_ch1 is from pll3_sw_clk. + */ + reg = readl(&mxc_ccm->cbcdr); + reg |= MXC_CCM_CBCDR_PERIPH2_CLK_SEL; + writel(reg, &mxc_ccm->cbcdr); + + /* Wait for the clock switch */ + while (readl(&mxc_ccm->cdhipr)) + ; + /* Disable pll3_sw_clk by selecting bypass clock source */ + reg = readl(&mxc_ccm->ccsr); + reg |= MXC_CCM_CCSR_PLL3_SW_CLK_SEL; + writel(reg, &mxc_ccm->ccsr); + + /* Set the ldb_di0_clk and ldb_di1_clk to 111b */ + reg = readl(&mxc_ccm->cs2cdr); + reg |= ((7 << MXC_CCM_CS2CDR_LDB_DI1_CLK_SEL_OFFSET) + | (7 << MXC_CCM_CS2CDR_LDB_DI0_CLK_SEL_OFFSET)); + writel(reg, &mxc_ccm->cs2cdr); + + /* Set the ldb_di0_clk and ldb_di1_clk to 100b */ + reg = readl(&mxc_ccm->cs2cdr); + reg &= ~(MXC_CCM_CS2CDR_LDB_DI1_CLK_SEL_MASK + | MXC_CCM_CS2CDR_LDB_DI0_CLK_SEL_MASK); + reg |= ((4 << MXC_CCM_CS2CDR_LDB_DI1_CLK_SEL_OFFSET) + | (4 << MXC_CCM_CS2CDR_LDB_DI0_CLK_SEL_OFFSET)); + writel(reg, &mxc_ccm->cs2cdr); + + /* Set the ldb_di0_clk and ldb_di1_clk to desired source */ + reg = readl(&mxc_ccm->cs2cdr); + reg &= ~(MXC_CCM_CS2CDR_LDB_DI1_CLK_SEL_MASK + | MXC_CCM_CS2CDR_LDB_DI0_CLK_SEL_MASK); + reg |= ((clk << MXC_CCM_CS2CDR_LDB_DI1_CLK_SEL_OFFSET) + | (clk << MXC_CCM_CS2CDR_LDB_DI0_CLK_SEL_OFFSET)); + writel(reg, &mxc_ccm->cs2cdr); + + /* Unbypass pll3_sw_clk */ + reg = readl(&mxc_ccm->ccsr); + reg &= ~MXC_CCM_CCSR_PLL3_SW_CLK_SEL; + writel(reg, &mxc_ccm->ccsr); + + /* + * Set the periph2_clk_sel back to the bottom mux so that + * mmdc_ch1 is from its original parent. + */ + reg = readl(&mxc_ccm->cbcdr); + reg &= ~MXC_CCM_CBCDR_PERIPH2_CLK_SEL; + writel(reg, &mxc_ccm->cbcdr); + + /* Wait for the clock switch */ + while (readl(&mxc_ccm->cdhipr)) + ; + /* Clear MMDC_CH1 mask bit */ + reg = readl(&mxc_ccm->ccdr); + reg &= ~MXC_CCM_CCDR_MMDC_CH1_HS_MASK; + writel(reg, &mxc_ccm->ccdr); + + enable_ldb_di_clock_sources(); +} +#endif + +/***************************************************/ + +U_BOOT_CMD( + clocks, CONFIG_SYS_MAXARGS, 1, do_mx6_showclocks, + "display clocks", + "" +); +#endif diff --git a/roms/u-boot/arch/arm/mach-imx/mx6/ddr.c b/roms/u-boot/arch/arm/mach-imx/mx6/ddr.c new file mode 100644 index 000000000..f872bfdab --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/mx6/ddr.c @@ -0,0 +1,1684 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2014 Gateworks Corporation + * Author: Tim Harvey <tharvey@gateworks.com> + */ + +#include <common.h> +#include <hang.h> +#include <log.h> +#include <linux/delay.h> +#include <linux/types.h> +#include <asm/arch/clock.h> +#include <asm/arch/mx6-ddr.h> +#include <asm/arch/sys_proto.h> +#include <asm/io.h> +#include <asm/types.h> +#include <wait_bit.h> + +#if defined(CONFIG_MX6_DDRCAL) +static void reset_read_data_fifos(void) +{ + struct mmdc_p_regs *mmdc0 = (struct mmdc_p_regs *)MMDC_P0_BASE_ADDR; + + /* Reset data FIFOs twice. */ + setbits_le32(&mmdc0->mpdgctrl0, 1 << 31); + wait_for_bit_le32(&mmdc0->mpdgctrl0, 1 << 31, 0, 100, 0); + + setbits_le32(&mmdc0->mpdgctrl0, 1 << 31); + wait_for_bit_le32(&mmdc0->mpdgctrl0, 1 << 31, 0, 100, 0); +} + +static void precharge_all(const bool cs0_enable, const bool cs1_enable) +{ + struct mmdc_p_regs *mmdc0 = (struct mmdc_p_regs *)MMDC_P0_BASE_ADDR; + + /* + * Issue the Precharge-All command to the DDR device for both + * chip selects. Note, CON_REQ bit should also remain set. If + * only using one chip select, then precharge only the desired + * chip select. + */ + if (cs0_enable) { /* CS0 */ + writel(0x04008050, &mmdc0->mdscr); + wait_for_bit_le32(&mmdc0->mdscr, 1 << 14, 1, 100, 0); + } + + if (cs1_enable) { /* CS1 */ + writel(0x04008058, &mmdc0->mdscr); + wait_for_bit_le32(&mmdc0->mdscr, 1 << 14, 1, 100, 0); + } +} + +static void force_delay_measurement(int bus_size) +{ + struct mmdc_p_regs *mmdc0 = (struct mmdc_p_regs *)MMDC_P0_BASE_ADDR; + struct mmdc_p_regs *mmdc1 = (struct mmdc_p_regs *)MMDC_P1_BASE_ADDR; + + writel(0x800, &mmdc0->mpmur0); + if (bus_size == 0x2) + writel(0x800, &mmdc1->mpmur0); +} + +static void modify_dg_result(u32 *reg_st0, u32 *reg_st1, u32 *reg_ctrl) +{ + u32 dg_tmp_val, dg_dl_abs_offset, dg_hc_del, val_ctrl; + + /* + * DQS gating absolute offset should be modified from reflecting + * (HW_DG_LOWx + HW_DG_UPx)/2 to reflecting (HW_DG_UPx - 0x80) + */ + + val_ctrl = readl(reg_ctrl); + val_ctrl &= 0xf0000000; + + dg_tmp_val = ((readl(reg_st0) & 0x07ff0000) >> 16) - 0xc0; + dg_dl_abs_offset = dg_tmp_val & 0x7f; + dg_hc_del = (dg_tmp_val & 0x780) << 1; + + val_ctrl |= dg_dl_abs_offset + dg_hc_del; + + dg_tmp_val = ((readl(reg_st1) & 0x07ff0000) >> 16) - 0xc0; + dg_dl_abs_offset = dg_tmp_val & 0x7f; + dg_hc_del = (dg_tmp_val & 0x780) << 1; + + val_ctrl |= (dg_dl_abs_offset + dg_hc_del) << 16; + + writel(val_ctrl, reg_ctrl); +} + +static void correct_mpwldectr_result(void *reg) +{ + /* Limit is 200/256 of CK, which is WL_HC_DELx | 0x48. */ + const unsigned int limit = 0x148; + u32 val = readl(reg); + u32 old = val; + + if ((val & 0x17f) > limit) + val &= 0xffff << 16; + + if (((val >> 16) & 0x17f) > limit) + val &= 0xffff; + + if (old != val) + writel(val, reg); +} + +int mmdc_do_write_level_calibration(struct mx6_ddr_sysinfo const *sysinfo) +{ + struct mmdc_p_regs *mmdc0 = (struct mmdc_p_regs *)MMDC_P0_BASE_ADDR; + struct mmdc_p_regs *mmdc1 = (struct mmdc_p_regs *)MMDC_P1_BASE_ADDR; + u32 esdmisc_val, zq_val; + u32 errors = 0; + u32 ldectrl[4] = {0}; + u32 ddr_mr1 = 0x4; + u32 rwalat_max; + + /* + * Stash old values in case calibration fails, + * we need to restore them + */ + ldectrl[0] = readl(&mmdc0->mpwldectrl0); + ldectrl[1] = readl(&mmdc0->mpwldectrl1); + if (sysinfo->dsize == 2) { + ldectrl[2] = readl(&mmdc1->mpwldectrl0); + ldectrl[3] = readl(&mmdc1->mpwldectrl1); + } + + /* disable DDR logic power down timer */ + clrbits_le32(&mmdc0->mdpdc, 0xff00); + + /* disable Adopt power down timer */ + setbits_le32(&mmdc0->mapsr, 0x1); + + debug("Starting write leveling calibration.\n"); + + /* + * 2. disable auto refresh and ZQ calibration + * before proceeding with Write Leveling calibration + */ + esdmisc_val = readl(&mmdc0->mdref); + writel(0x0000C000, &mmdc0->mdref); + zq_val = readl(&mmdc0->mpzqhwctrl); + writel(zq_val & ~0x3, &mmdc0->mpzqhwctrl); + + /* 3. increase walat and ralat to maximum */ + rwalat_max = (1 << 6) | (1 << 7) | (1 << 8) | (1 << 16) | (1 << 17); + setbits_le32(&mmdc0->mdmisc, rwalat_max); + if (sysinfo->dsize == 2) + setbits_le32(&mmdc1->mdmisc, rwalat_max); + /* + * 4 & 5. Configure the external DDR device to enter write-leveling + * mode through Load Mode Register command. + * Register setting: + * Bits[31:16] MR1 value (0x0080 write leveling enable) + * Bit[9] set WL_EN to enable MMDC DQS output + * Bits[6:4] set CMD bits for Load Mode Register programming + * Bits[2:0] set CMD_BA to 0x1 for DDR MR1 programming + */ + writel(0x00808231, &mmdc0->mdscr); + + /* 6. Activate automatic calibration by setting MPWLGCR[HW_WL_EN] */ + writel(0x00000001, &mmdc0->mpwlgcr); + + /* + * 7. Upon completion of this process the MMDC de-asserts + * the MPWLGCR[HW_WL_EN] + */ + wait_for_bit_le32(&mmdc0->mpwlgcr, 1 << 0, 0, 100, 0); + + /* + * 8. check for any errors: check both PHYs for x64 configuration, + * if x32, check only PHY0 + */ + if (readl(&mmdc0->mpwlgcr) & 0x00000F00) + errors |= 1; + if (sysinfo->dsize == 2) + if (readl(&mmdc1->mpwlgcr) & 0x00000F00) + errors |= 2; + + debug("Ending write leveling calibration. Error mask: 0x%x\n", errors); + + /* check to see if cal failed */ + if ((readl(&mmdc0->mpwldectrl0) == 0x001F001F) && + (readl(&mmdc0->mpwldectrl1) == 0x001F001F) && + ((sysinfo->dsize < 2) || + ((readl(&mmdc1->mpwldectrl0) == 0x001F001F) && + (readl(&mmdc1->mpwldectrl1) == 0x001F001F)))) { + debug("Cal seems to have soft-failed due to memory not supporting write leveling on all channels. Restoring original write leveling values.\n"); + writel(ldectrl[0], &mmdc0->mpwldectrl0); + writel(ldectrl[1], &mmdc0->mpwldectrl1); + if (sysinfo->dsize == 2) { + writel(ldectrl[2], &mmdc1->mpwldectrl0); + writel(ldectrl[3], &mmdc1->mpwldectrl1); + } + errors |= 4; + } + + correct_mpwldectr_result(&mmdc0->mpwldectrl0); + correct_mpwldectr_result(&mmdc0->mpwldectrl1); + if (sysinfo->dsize == 2) { + correct_mpwldectr_result(&mmdc1->mpwldectrl0); + correct_mpwldectr_result(&mmdc1->mpwldectrl1); + } + + /* + * User should issue MRS command to exit write leveling mode + * through Load Mode Register command + * Register setting: + * Bits[31:16] MR1 value "ddr_mr1" value from initialization + * Bit[9] clear WL_EN to disable MMDC DQS output + * Bits[6:4] set CMD bits for Load Mode Register programming + * Bits[2:0] set CMD_BA to 0x1 for DDR MR1 programming + */ + writel((ddr_mr1 << 16) + 0x8031, &mmdc0->mdscr); + + /* re-enable auto refresh and zq cal */ + writel(esdmisc_val, &mmdc0->mdref); + writel(zq_val, &mmdc0->mpzqhwctrl); + + debug("\tMMDC_MPWLDECTRL0 after write level cal: 0x%08x\n", + readl(&mmdc0->mpwldectrl0)); + debug("\tMMDC_MPWLDECTRL1 after write level cal: 0x%08x\n", + readl(&mmdc0->mpwldectrl1)); + if (sysinfo->dsize == 2) { + debug("\tMMDC_MPWLDECTRL0 after write level cal: 0x%08x\n", + readl(&mmdc1->mpwldectrl0)); + debug("\tMMDC_MPWLDECTRL1 after write level cal: 0x%08x\n", + readl(&mmdc1->mpwldectrl1)); + } + + /* We must force a readback of these values, to get them to stick */ + readl(&mmdc0->mpwldectrl0); + readl(&mmdc0->mpwldectrl1); + if (sysinfo->dsize == 2) { + readl(&mmdc1->mpwldectrl0); + readl(&mmdc1->mpwldectrl1); + } + + /* enable DDR logic power down timer: */ + setbits_le32(&mmdc0->mdpdc, 0x00005500); + + /* Enable Adopt power down timer: */ + clrbits_le32(&mmdc0->mapsr, 0x1); + + /* Clear CON_REQ */ + writel(0, &mmdc0->mdscr); + + return errors; +} + +static void mmdc_set_sdqs(bool set) +{ + struct mx6sdl_iomux_ddr_regs *mx6sdl_ddr_iomux = + (struct mx6sdl_iomux_ddr_regs *)MX6SDL_IOM_DDR_BASE; + struct mx6dq_iomux_ddr_regs *mx6dq_ddr_iomux = + (struct mx6dq_iomux_ddr_regs *)MX6DQ_IOM_DDR_BASE; + struct mx6sx_iomux_ddr_regs *mx6sx_ddr_iomux = + (struct mx6sx_iomux_ddr_regs *)MX6SX_IOM_DDR_BASE; + struct mx6sl_iomux_ddr_regs *mx6sl_ddr_iomux = + (struct mx6sl_iomux_ddr_regs *)MX6SL_IOM_DDR_BASE; + struct mx6ul_iomux_ddr_regs *mx6ul_ddr_iomux = + (struct mx6ul_iomux_ddr_regs *)MX6UL_IOM_DDR_BASE; + int i, sdqs_cnt; + u32 sdqs; + + if (is_mx6sx()) { + sdqs = (u32)(&mx6sx_ddr_iomux->dram_sdqs0); + sdqs_cnt = 2; + } else if (is_mx6sl()) { + sdqs = (u32)(&mx6sl_ddr_iomux->dram_sdqs0); + sdqs_cnt = 2; + } else if (is_mx6ul() || is_mx6ull()) { + sdqs = (u32)(&mx6ul_ddr_iomux->dram_sdqs0); + sdqs_cnt = 2; + } else if (is_mx6sdl()) { + sdqs = (u32)(&mx6sdl_ddr_iomux->dram_sdqs0); + sdqs_cnt = 8; + } else { /* MX6DQ */ + sdqs = (u32)(&mx6dq_ddr_iomux->dram_sdqs0); + sdqs_cnt = 8; + } + + for (i = 0; i < sdqs_cnt; i++) { + if (set) + setbits_le32(sdqs + (4 * i), 0x7000); + else + clrbits_le32(sdqs + (4 * i), 0x7000); + } +} + +int mmdc_do_dqs_calibration(struct mx6_ddr_sysinfo const *sysinfo) +{ + struct mmdc_p_regs *mmdc0 = (struct mmdc_p_regs *)MMDC_P0_BASE_ADDR; + struct mmdc_p_regs *mmdc1 = (struct mmdc_p_regs *)MMDC_P1_BASE_ADDR; + bool cs0_enable; + bool cs1_enable; + bool cs0_enable_initial; + bool cs1_enable_initial; + u32 esdmisc_val; + u32 temp_ref; + u32 pddword = 0x00ffff00; /* best so far, place into MPPDCMPR1 */ + u32 errors = 0; + u32 initdelay = 0x40404040; + + /* check to see which chip selects are enabled */ + cs0_enable_initial = readl(&mmdc0->mdctl) & 0x80000000; + cs1_enable_initial = readl(&mmdc0->mdctl) & 0x40000000; + + /* disable DDR logic power down timer: */ + clrbits_le32(&mmdc0->mdpdc, 0xff00); + + /* disable Adopt power down timer: */ + setbits_le32(&mmdc0->mapsr, 0x1); + + /* set DQS pull ups */ + mmdc_set_sdqs(true); + + /* Save old RALAT and WALAT values */ + esdmisc_val = readl(&mmdc0->mdmisc); + + setbits_le32(&mmdc0->mdmisc, + (1 << 6) | (1 << 7) | (1 << 8) | (1 << 16) | (1 << 17)); + + /* Disable auto refresh before proceeding with calibration */ + temp_ref = readl(&mmdc0->mdref); + writel(0x0000c000, &mmdc0->mdref); + + /* + * Per the ref manual, issue one refresh cycle MDSCR[CMD]= 0x2, + * this also sets the CON_REQ bit. + */ + if (cs0_enable_initial) + writel(0x00008020, &mmdc0->mdscr); + if (cs1_enable_initial) + writel(0x00008028, &mmdc0->mdscr); + + /* poll to make sure the con_ack bit was asserted */ + wait_for_bit_le32(&mmdc0->mdscr, 1 << 14, 1, 100, 0); + + /* + * Check MDMISC register CALIB_PER_CS to see which CS calibration + * is targeted to (under normal cases, it should be cleared + * as this is the default value, indicating calibration is directed + * to CS0). + * Disable the other chip select not being target for calibration + * to avoid any potential issues. This will get re-enabled at end + * of calibration. + */ + if ((readl(&mmdc0->mdmisc) & 0x00100000) == 0) + clrbits_le32(&mmdc0->mdctl, 1 << 30); /* clear SDE_1 */ + else + clrbits_le32(&mmdc0->mdctl, 1 << 31); /* clear SDE_0 */ + + /* + * Check to see which chip selects are now enabled for + * the remainder of the calibration. + */ + cs0_enable = readl(&mmdc0->mdctl) & 0x80000000; + cs1_enable = readl(&mmdc0->mdctl) & 0x40000000; + + precharge_all(cs0_enable, cs1_enable); + + /* Write the pre-defined value into MPPDCMPR1 */ + writel(pddword, &mmdc0->mppdcmpr1); + + /* + * Issue a write access to the external DDR device by setting + * the bit SW_DUMMY_WR (bit 0) in the MPSWDAR0 and then poll + * this bit until it clears to indicate completion of the write access. + */ + setbits_le32(&mmdc0->mpswdar0, 1); + wait_for_bit_le32(&mmdc0->mpswdar0, 1 << 0, 0, 100, 0); + + /* Set the RD_DL_ABS# bits to their default values + * (will be calibrated later in the read delay-line calibration). + * Both PHYs for x64 configuration, if x32, do only PHY0. + */ + writel(initdelay, &mmdc0->mprddlctl); + if (sysinfo->dsize == 0x2) + writel(initdelay, &mmdc1->mprddlctl); + + /* Force a measurment, for previous delay setup to take effect. */ + force_delay_measurement(sysinfo->dsize); + + /* + * *************************** + * Read DQS Gating calibration + * *************************** + */ + debug("Starting Read DQS Gating calibration.\n"); + + /* + * Reset the read data FIFOs (two resets); only need to issue reset + * to PHY0 since in x64 mode, the reset will also go to PHY1. + */ + reset_read_data_fifos(); + + /* + * Start the automatic read DQS gating calibration process by + * asserting MPDGCTRL0[HW_DG_EN] and MPDGCTRL0[DG_CMP_CYC] + * and then poll MPDGCTRL0[HW_DG_EN]] until this bit clears + * to indicate completion. + * Also, ensure that MPDGCTRL0[HW_DG_ERR] is clear to indicate + * no errors were seen during calibration. + */ + + /* + * Set bit 30: chooses option to wait 32 cycles instead of + * 16 before comparing read data. + */ + setbits_le32(&mmdc0->mpdgctrl0, 1 << 30); + if (sysinfo->dsize == 2) + setbits_le32(&mmdc1->mpdgctrl0, 1 << 30); + + /* Set bit 28 to start automatic read DQS gating calibration */ + setbits_le32(&mmdc0->mpdgctrl0, 5 << 28); + + /* Poll for completion. MPDGCTRL0[HW_DG_EN] should be 0 */ + wait_for_bit_le32(&mmdc0->mpdgctrl0, 1 << 28, 0, 100, 0); + + /* + * Check to see if any errors were encountered during calibration + * (check MPDGCTRL0[HW_DG_ERR]). + * Check both PHYs for x64 configuration, if x32, check only PHY0. + */ + if (readl(&mmdc0->mpdgctrl0) & 0x00001000) + errors |= 1; + + if ((sysinfo->dsize == 0x2) && (readl(&mmdc1->mpdgctrl0) & 0x00001000)) + errors |= 2; + + /* now disable mpdgctrl0[DG_CMP_CYC] */ + clrbits_le32(&mmdc0->mpdgctrl0, 1 << 30); + if (sysinfo->dsize == 2) + clrbits_le32(&mmdc1->mpdgctrl0, 1 << 30); + + /* + * DQS gating absolute offset should be modified from + * reflecting (HW_DG_LOWx + HW_DG_UPx)/2 to + * reflecting (HW_DG_UPx - 0x80) + */ + modify_dg_result(&mmdc0->mpdghwst0, &mmdc0->mpdghwst1, + &mmdc0->mpdgctrl0); + modify_dg_result(&mmdc0->mpdghwst2, &mmdc0->mpdghwst3, + &mmdc0->mpdgctrl1); + if (sysinfo->dsize == 0x2) { + modify_dg_result(&mmdc1->mpdghwst0, &mmdc1->mpdghwst1, + &mmdc1->mpdgctrl0); + modify_dg_result(&mmdc1->mpdghwst2, &mmdc1->mpdghwst3, + &mmdc1->mpdgctrl1); + } + debug("Ending Read DQS Gating calibration. Error mask: 0x%x\n", errors); + + /* + * ********************** + * Read Delay calibration + * ********************** + */ + debug("Starting Read Delay calibration.\n"); + + reset_read_data_fifos(); + + /* + * 4. Issue the Precharge-All command to the DDR device for both + * chip selects. If only using one chip select, then precharge + * only the desired chip select. + */ + precharge_all(cs0_enable, cs1_enable); + + /* + * 9. Read delay-line calibration + * Start the automatic read calibration process by asserting + * MPRDDLHWCTL[HW_RD_DL_EN]. + */ + writel(0x00000030, &mmdc0->mprddlhwctl); + + /* + * 10. poll for completion + * MMDC indicates that the write data calibration had finished by + * setting MPRDDLHWCTL[HW_RD_DL_EN] = 0. Also, ensure that + * no error bits were set. + */ + wait_for_bit_le32(&mmdc0->mprddlhwctl, 1 << 4, 0, 100, 0); + + /* check both PHYs for x64 configuration, if x32, check only PHY0 */ + if (readl(&mmdc0->mprddlhwctl) & 0x0000000f) + errors |= 4; + + if ((sysinfo->dsize == 0x2) && + (readl(&mmdc1->mprddlhwctl) & 0x0000000f)) + errors |= 8; + + debug("Ending Read Delay calibration. Error mask: 0x%x\n", errors); + + /* + * *********************** + * Write Delay Calibration + * *********************** + */ + debug("Starting Write Delay calibration.\n"); + + reset_read_data_fifos(); + + /* + * 4. Issue the Precharge-All command to the DDR device for both + * chip selects. If only using one chip select, then precharge + * only the desired chip select. + */ + precharge_all(cs0_enable, cs1_enable); + + /* + * 8. Set the WR_DL_ABS# bits to their default values. + * Both PHYs for x64 configuration, if x32, do only PHY0. + */ + writel(initdelay, &mmdc0->mpwrdlctl); + if (sysinfo->dsize == 0x2) + writel(initdelay, &mmdc1->mpwrdlctl); + + /* + * XXX This isn't in the manual. Force a measurement, + * for previous delay setup to effect. + */ + force_delay_measurement(sysinfo->dsize); + + /* + * 9. 10. Start the automatic write calibration process + * by asserting MPWRDLHWCTL0[HW_WR_DL_EN]. + */ + writel(0x00000030, &mmdc0->mpwrdlhwctl); + + /* + * Poll for completion. + * MMDC indicates that the write data calibration had finished + * by setting MPWRDLHWCTL[HW_WR_DL_EN] = 0. + * Also, ensure that no error bits were set. + */ + wait_for_bit_le32(&mmdc0->mpwrdlhwctl, 1 << 4, 0, 100, 0); + + /* Check both PHYs for x64 configuration, if x32, check only PHY0 */ + if (readl(&mmdc0->mpwrdlhwctl) & 0x0000000f) + errors |= 16; + + if ((sysinfo->dsize == 0x2) && + (readl(&mmdc1->mpwrdlhwctl) & 0x0000000f)) + errors |= 32; + + debug("Ending Write Delay calibration. Error mask: 0x%x\n", errors); + + reset_read_data_fifos(); + + /* Enable DDR logic power down timer */ + setbits_le32(&mmdc0->mdpdc, 0x00005500); + + /* Enable Adopt power down timer */ + clrbits_le32(&mmdc0->mapsr, 0x1); + + /* Restore MDMISC value (RALAT, WALAT) to MMDCP1 */ + writel(esdmisc_val, &mmdc0->mdmisc); + + /* Clear DQS pull ups */ + mmdc_set_sdqs(false); + + /* Re-enable SDE (chip selects) if they were set initially */ + if (cs1_enable_initial) + /* Set SDE_1 */ + setbits_le32(&mmdc0->mdctl, 1 << 30); + + if (cs0_enable_initial) + /* Set SDE_0 */ + setbits_le32(&mmdc0->mdctl, 1 << 31); + + /* Re-enable to auto refresh */ + writel(temp_ref, &mmdc0->mdref); + + /* Clear the MDSCR (including the con_req bit) */ + writel(0x0, &mmdc0->mdscr); /* CS0 */ + + /* Poll to make sure the con_ack bit is clear */ + wait_for_bit_le32(&mmdc0->mdscr, 1 << 14, 0, 100, 0); + + /* + * Print out the registers that were updated as a result + * of the calibration process. + */ + debug("MMDC registers updated from calibration\n"); + debug("Read DQS gating calibration:\n"); + debug("\tMPDGCTRL0 PHY0 = 0x%08x\n", readl(&mmdc0->mpdgctrl0)); + debug("\tMPDGCTRL1 PHY0 = 0x%08x\n", readl(&mmdc0->mpdgctrl1)); + if (sysinfo->dsize == 2) { + debug("\tMPDGCTRL0 PHY1 = 0x%08x\n", readl(&mmdc1->mpdgctrl0)); + debug("\tMPDGCTRL1 PHY1 = 0x%08x\n", readl(&mmdc1->mpdgctrl1)); + } + debug("Read calibration:\n"); + debug("\tMPRDDLCTL PHY0 = 0x%08x\n", readl(&mmdc0->mprddlctl)); + if (sysinfo->dsize == 2) + debug("\tMPRDDLCTL PHY1 = 0x%08x\n", readl(&mmdc1->mprddlctl)); + debug("Write calibration:\n"); + debug("\tMPWRDLCTL PHY0 = 0x%08x\n", readl(&mmdc0->mpwrdlctl)); + if (sysinfo->dsize == 2) + debug("\tMPWRDLCTL PHY1 = 0x%08x\n", readl(&mmdc1->mpwrdlctl)); + + /* + * Registers below are for debugging purposes. These print out + * the upper and lower boundaries captured during + * read DQS gating calibration. + */ + debug("Status registers bounds for read DQS gating:\n"); + debug("\tMPDGHWST0 PHY0 = 0x%08x\n", readl(&mmdc0->mpdghwst0)); + debug("\tMPDGHWST1 PHY0 = 0x%08x\n", readl(&mmdc0->mpdghwst1)); + debug("\tMPDGHWST2 PHY0 = 0x%08x\n", readl(&mmdc0->mpdghwst2)); + debug("\tMPDGHWST3 PHY0 = 0x%08x\n", readl(&mmdc0->mpdghwst3)); + if (sysinfo->dsize == 2) { + debug("\tMPDGHWST0 PHY1 = 0x%08x\n", readl(&mmdc1->mpdghwst0)); + debug("\tMPDGHWST1 PHY1 = 0x%08x\n", readl(&mmdc1->mpdghwst1)); + debug("\tMPDGHWST2 PHY1 = 0x%08x\n", readl(&mmdc1->mpdghwst2)); + debug("\tMPDGHWST3 PHY1 = 0x%08x\n", readl(&mmdc1->mpdghwst3)); + } + + debug("Final do_dqs_calibration error mask: 0x%x\n", errors); + + return errors; +} +#endif + +#if defined(CONFIG_MX6SX) +/* Configure MX6SX mmdc iomux */ +void mx6sx_dram_iocfg(unsigned width, + const struct mx6sx_iomux_ddr_regs *ddr, + const struct mx6sx_iomux_grp_regs *grp) +{ + struct mx6sx_iomux_ddr_regs *mx6_ddr_iomux; + struct mx6sx_iomux_grp_regs *mx6_grp_iomux; + + mx6_ddr_iomux = (struct mx6sx_iomux_ddr_regs *)MX6SX_IOM_DDR_BASE; + mx6_grp_iomux = (struct mx6sx_iomux_grp_regs *)MX6SX_IOM_GRP_BASE; + + /* DDR IO TYPE */ + writel(grp->grp_ddr_type, &mx6_grp_iomux->grp_ddr_type); + writel(grp->grp_ddrpke, &mx6_grp_iomux->grp_ddrpke); + + /* CLOCK */ + writel(ddr->dram_sdclk_0, &mx6_ddr_iomux->dram_sdclk_0); + + /* ADDRESS */ + writel(ddr->dram_cas, &mx6_ddr_iomux->dram_cas); + writel(ddr->dram_ras, &mx6_ddr_iomux->dram_ras); + writel(grp->grp_addds, &mx6_grp_iomux->grp_addds); + + /* Control */ + writel(ddr->dram_reset, &mx6_ddr_iomux->dram_reset); + writel(ddr->dram_sdba2, &mx6_ddr_iomux->dram_sdba2); + writel(ddr->dram_sdcke0, &mx6_ddr_iomux->dram_sdcke0); + writel(ddr->dram_sdcke1, &mx6_ddr_iomux->dram_sdcke1); + writel(ddr->dram_odt0, &mx6_ddr_iomux->dram_odt0); + writel(ddr->dram_odt1, &mx6_ddr_iomux->dram_odt1); + writel(grp->grp_ctlds, &mx6_grp_iomux->grp_ctlds); + + /* Data Strobes */ + writel(grp->grp_ddrmode_ctl, &mx6_grp_iomux->grp_ddrmode_ctl); + writel(ddr->dram_sdqs0, &mx6_ddr_iomux->dram_sdqs0); + writel(ddr->dram_sdqs1, &mx6_ddr_iomux->dram_sdqs1); + if (width >= 32) { + writel(ddr->dram_sdqs2, &mx6_ddr_iomux->dram_sdqs2); + writel(ddr->dram_sdqs3, &mx6_ddr_iomux->dram_sdqs3); + } + + /* Data */ + writel(grp->grp_ddrmode, &mx6_grp_iomux->grp_ddrmode); + writel(grp->grp_b0ds, &mx6_grp_iomux->grp_b0ds); + writel(grp->grp_b1ds, &mx6_grp_iomux->grp_b1ds); + if (width >= 32) { + writel(grp->grp_b2ds, &mx6_grp_iomux->grp_b2ds); + writel(grp->grp_b3ds, &mx6_grp_iomux->grp_b3ds); + } + writel(ddr->dram_dqm0, &mx6_ddr_iomux->dram_dqm0); + writel(ddr->dram_dqm1, &mx6_ddr_iomux->dram_dqm1); + if (width >= 32) { + writel(ddr->dram_dqm2, &mx6_ddr_iomux->dram_dqm2); + writel(ddr->dram_dqm3, &mx6_ddr_iomux->dram_dqm3); + } +} +#endif + +#if defined(CONFIG_MX6UL) || defined(CONFIG_MX6ULL) +void mx6ul_dram_iocfg(unsigned width, + const struct mx6ul_iomux_ddr_regs *ddr, + const struct mx6ul_iomux_grp_regs *grp) +{ + struct mx6ul_iomux_ddr_regs *mx6_ddr_iomux; + struct mx6ul_iomux_grp_regs *mx6_grp_iomux; + + mx6_ddr_iomux = (struct mx6ul_iomux_ddr_regs *)MX6UL_IOM_DDR_BASE; + mx6_grp_iomux = (struct mx6ul_iomux_grp_regs *)MX6UL_IOM_GRP_BASE; + + /* DDR IO TYPE */ + writel(grp->grp_ddr_type, &mx6_grp_iomux->grp_ddr_type); + writel(grp->grp_ddrpke, &mx6_grp_iomux->grp_ddrpke); + + /* CLOCK */ + writel(ddr->dram_sdclk_0, &mx6_ddr_iomux->dram_sdclk_0); + + /* ADDRESS */ + writel(ddr->dram_cas, &mx6_ddr_iomux->dram_cas); + writel(ddr->dram_ras, &mx6_ddr_iomux->dram_ras); + writel(grp->grp_addds, &mx6_grp_iomux->grp_addds); + + /* Control */ + writel(ddr->dram_reset, &mx6_ddr_iomux->dram_reset); + writel(ddr->dram_sdba2, &mx6_ddr_iomux->dram_sdba2); + writel(ddr->dram_odt0, &mx6_ddr_iomux->dram_odt0); + writel(ddr->dram_odt1, &mx6_ddr_iomux->dram_odt1); + writel(grp->grp_ctlds, &mx6_grp_iomux->grp_ctlds); + + /* Data Strobes */ + writel(grp->grp_ddrmode_ctl, &mx6_grp_iomux->grp_ddrmode_ctl); + writel(ddr->dram_sdqs0, &mx6_ddr_iomux->dram_sdqs0); + writel(ddr->dram_sdqs1, &mx6_ddr_iomux->dram_sdqs1); + + /* Data */ + writel(grp->grp_ddrmode, &mx6_grp_iomux->grp_ddrmode); + writel(grp->grp_b0ds, &mx6_grp_iomux->grp_b0ds); + writel(grp->grp_b1ds, &mx6_grp_iomux->grp_b1ds); + writel(ddr->dram_dqm0, &mx6_ddr_iomux->dram_dqm0); + writel(ddr->dram_dqm1, &mx6_ddr_iomux->dram_dqm1); +} +#endif + +#if defined(CONFIG_MX6SL) +void mx6sl_dram_iocfg(unsigned width, + const struct mx6sl_iomux_ddr_regs *ddr, + const struct mx6sl_iomux_grp_regs *grp) +{ + struct mx6sl_iomux_ddr_regs *mx6_ddr_iomux; + struct mx6sl_iomux_grp_regs *mx6_grp_iomux; + + mx6_ddr_iomux = (struct mx6sl_iomux_ddr_regs *)MX6SL_IOM_DDR_BASE; + mx6_grp_iomux = (struct mx6sl_iomux_grp_regs *)MX6SL_IOM_GRP_BASE; + + /* DDR IO TYPE */ + mx6_grp_iomux->grp_ddr_type = grp->grp_ddr_type; + mx6_grp_iomux->grp_ddrpke = grp->grp_ddrpke; + + /* CLOCK */ + mx6_ddr_iomux->dram_sdclk_0 = ddr->dram_sdclk_0; + + /* ADDRESS */ + mx6_ddr_iomux->dram_cas = ddr->dram_cas; + mx6_ddr_iomux->dram_ras = ddr->dram_ras; + mx6_grp_iomux->grp_addds = grp->grp_addds; + + /* Control */ + mx6_ddr_iomux->dram_reset = ddr->dram_reset; + mx6_ddr_iomux->dram_sdba2 = ddr->dram_sdba2; + mx6_grp_iomux->grp_ctlds = grp->grp_ctlds; + + /* Data Strobes */ + mx6_grp_iomux->grp_ddrmode_ctl = grp->grp_ddrmode_ctl; + mx6_ddr_iomux->dram_sdqs0 = ddr->dram_sdqs0; + mx6_ddr_iomux->dram_sdqs1 = ddr->dram_sdqs1; + if (width >= 32) { + mx6_ddr_iomux->dram_sdqs2 = ddr->dram_sdqs2; + mx6_ddr_iomux->dram_sdqs3 = ddr->dram_sdqs3; + } + + /* Data */ + mx6_grp_iomux->grp_ddrmode = grp->grp_ddrmode; + mx6_grp_iomux->grp_b0ds = grp->grp_b0ds; + mx6_grp_iomux->grp_b1ds = grp->grp_b1ds; + if (width >= 32) { + mx6_grp_iomux->grp_b2ds = grp->grp_b2ds; + mx6_grp_iomux->grp_b3ds = grp->grp_b3ds; + } + + mx6_ddr_iomux->dram_dqm0 = ddr->dram_dqm0; + mx6_ddr_iomux->dram_dqm1 = ddr->dram_dqm1; + if (width >= 32) { + mx6_ddr_iomux->dram_dqm2 = ddr->dram_dqm2; + mx6_ddr_iomux->dram_dqm3 = ddr->dram_dqm3; + } +} +#endif + +#if defined(CONFIG_MX6QDL) || defined(CONFIG_MX6Q) || defined(CONFIG_MX6D) +/* Configure MX6DQ mmdc iomux */ +void mx6dq_dram_iocfg(unsigned width, + const struct mx6dq_iomux_ddr_regs *ddr, + const struct mx6dq_iomux_grp_regs *grp) +{ + volatile struct mx6dq_iomux_ddr_regs *mx6_ddr_iomux; + volatile struct mx6dq_iomux_grp_regs *mx6_grp_iomux; + + mx6_ddr_iomux = (struct mx6dq_iomux_ddr_regs *)MX6DQ_IOM_DDR_BASE; + mx6_grp_iomux = (struct mx6dq_iomux_grp_regs *)MX6DQ_IOM_GRP_BASE; + + /* DDR IO Type */ + mx6_grp_iomux->grp_ddr_type = grp->grp_ddr_type; + mx6_grp_iomux->grp_ddrpke = grp->grp_ddrpke; + + /* Clock */ + mx6_ddr_iomux->dram_sdclk_0 = ddr->dram_sdclk_0; + mx6_ddr_iomux->dram_sdclk_1 = ddr->dram_sdclk_1; + + /* Address */ + mx6_ddr_iomux->dram_cas = ddr->dram_cas; + mx6_ddr_iomux->dram_ras = ddr->dram_ras; + mx6_grp_iomux->grp_addds = grp->grp_addds; + + /* Control */ + mx6_ddr_iomux->dram_reset = ddr->dram_reset; + mx6_ddr_iomux->dram_sdcke0 = ddr->dram_sdcke0; + mx6_ddr_iomux->dram_sdcke1 = ddr->dram_sdcke1; + mx6_ddr_iomux->dram_sdba2 = ddr->dram_sdba2; + mx6_ddr_iomux->dram_sdodt0 = ddr->dram_sdodt0; + mx6_ddr_iomux->dram_sdodt1 = ddr->dram_sdodt1; + mx6_grp_iomux->grp_ctlds = grp->grp_ctlds; + + /* Data Strobes */ + mx6_grp_iomux->grp_ddrmode_ctl = grp->grp_ddrmode_ctl; + mx6_ddr_iomux->dram_sdqs0 = ddr->dram_sdqs0; + mx6_ddr_iomux->dram_sdqs1 = ddr->dram_sdqs1; + if (width >= 32) { + mx6_ddr_iomux->dram_sdqs2 = ddr->dram_sdqs2; + mx6_ddr_iomux->dram_sdqs3 = ddr->dram_sdqs3; + } + if (width >= 64) { + mx6_ddr_iomux->dram_sdqs4 = ddr->dram_sdqs4; + mx6_ddr_iomux->dram_sdqs5 = ddr->dram_sdqs5; + mx6_ddr_iomux->dram_sdqs6 = ddr->dram_sdqs6; + mx6_ddr_iomux->dram_sdqs7 = ddr->dram_sdqs7; + } + + /* Data */ + mx6_grp_iomux->grp_ddrmode = grp->grp_ddrmode; + mx6_grp_iomux->grp_b0ds = grp->grp_b0ds; + mx6_grp_iomux->grp_b1ds = grp->grp_b1ds; + if (width >= 32) { + mx6_grp_iomux->grp_b2ds = grp->grp_b2ds; + mx6_grp_iomux->grp_b3ds = grp->grp_b3ds; + } + if (width >= 64) { + mx6_grp_iomux->grp_b4ds = grp->grp_b4ds; + mx6_grp_iomux->grp_b5ds = grp->grp_b5ds; + mx6_grp_iomux->grp_b6ds = grp->grp_b6ds; + mx6_grp_iomux->grp_b7ds = grp->grp_b7ds; + } + mx6_ddr_iomux->dram_dqm0 = ddr->dram_dqm0; + mx6_ddr_iomux->dram_dqm1 = ddr->dram_dqm1; + if (width >= 32) { + mx6_ddr_iomux->dram_dqm2 = ddr->dram_dqm2; + mx6_ddr_iomux->dram_dqm3 = ddr->dram_dqm3; + } + if (width >= 64) { + mx6_ddr_iomux->dram_dqm4 = ddr->dram_dqm4; + mx6_ddr_iomux->dram_dqm5 = ddr->dram_dqm5; + mx6_ddr_iomux->dram_dqm6 = ddr->dram_dqm6; + mx6_ddr_iomux->dram_dqm7 = ddr->dram_dqm7; + } +} +#endif + +#if defined(CONFIG_MX6QDL) || defined(CONFIG_MX6DL) || defined(CONFIG_MX6S) +/* Configure MX6SDL mmdc iomux */ +void mx6sdl_dram_iocfg(unsigned width, + const struct mx6sdl_iomux_ddr_regs *ddr, + const struct mx6sdl_iomux_grp_regs *grp) +{ + volatile struct mx6sdl_iomux_ddr_regs *mx6_ddr_iomux; + volatile struct mx6sdl_iomux_grp_regs *mx6_grp_iomux; + + mx6_ddr_iomux = (struct mx6sdl_iomux_ddr_regs *)MX6SDL_IOM_DDR_BASE; + mx6_grp_iomux = (struct mx6sdl_iomux_grp_regs *)MX6SDL_IOM_GRP_BASE; + + /* DDR IO Type */ + mx6_grp_iomux->grp_ddr_type = grp->grp_ddr_type; + mx6_grp_iomux->grp_ddrpke = grp->grp_ddrpke; + + /* Clock */ + mx6_ddr_iomux->dram_sdclk_0 = ddr->dram_sdclk_0; + mx6_ddr_iomux->dram_sdclk_1 = ddr->dram_sdclk_1; + + /* Address */ + mx6_ddr_iomux->dram_cas = ddr->dram_cas; + mx6_ddr_iomux->dram_ras = ddr->dram_ras; + mx6_grp_iomux->grp_addds = grp->grp_addds; + + /* Control */ + mx6_ddr_iomux->dram_reset = ddr->dram_reset; + mx6_ddr_iomux->dram_sdcke0 = ddr->dram_sdcke0; + mx6_ddr_iomux->dram_sdcke1 = ddr->dram_sdcke1; + mx6_ddr_iomux->dram_sdba2 = ddr->dram_sdba2; + mx6_ddr_iomux->dram_sdodt0 = ddr->dram_sdodt0; + mx6_ddr_iomux->dram_sdodt1 = ddr->dram_sdodt1; + mx6_grp_iomux->grp_ctlds = grp->grp_ctlds; + + /* Data Strobes */ + mx6_grp_iomux->grp_ddrmode_ctl = grp->grp_ddrmode_ctl; + mx6_ddr_iomux->dram_sdqs0 = ddr->dram_sdqs0; + mx6_ddr_iomux->dram_sdqs1 = ddr->dram_sdqs1; + if (width >= 32) { + mx6_ddr_iomux->dram_sdqs2 = ddr->dram_sdqs2; + mx6_ddr_iomux->dram_sdqs3 = ddr->dram_sdqs3; + } + if (width >= 64) { + mx6_ddr_iomux->dram_sdqs4 = ddr->dram_sdqs4; + mx6_ddr_iomux->dram_sdqs5 = ddr->dram_sdqs5; + mx6_ddr_iomux->dram_sdqs6 = ddr->dram_sdqs6; + mx6_ddr_iomux->dram_sdqs7 = ddr->dram_sdqs7; + } + + /* Data */ + mx6_grp_iomux->grp_ddrmode = grp->grp_ddrmode; + mx6_grp_iomux->grp_b0ds = grp->grp_b0ds; + mx6_grp_iomux->grp_b1ds = grp->grp_b1ds; + if (width >= 32) { + mx6_grp_iomux->grp_b2ds = grp->grp_b2ds; + mx6_grp_iomux->grp_b3ds = grp->grp_b3ds; + } + if (width >= 64) { + mx6_grp_iomux->grp_b4ds = grp->grp_b4ds; + mx6_grp_iomux->grp_b5ds = grp->grp_b5ds; + mx6_grp_iomux->grp_b6ds = grp->grp_b6ds; + mx6_grp_iomux->grp_b7ds = grp->grp_b7ds; + } + mx6_ddr_iomux->dram_dqm0 = ddr->dram_dqm0; + mx6_ddr_iomux->dram_dqm1 = ddr->dram_dqm1; + if (width >= 32) { + mx6_ddr_iomux->dram_dqm2 = ddr->dram_dqm2; + mx6_ddr_iomux->dram_dqm3 = ddr->dram_dqm3; + } + if (width >= 64) { + mx6_ddr_iomux->dram_dqm4 = ddr->dram_dqm4; + mx6_ddr_iomux->dram_dqm5 = ddr->dram_dqm5; + mx6_ddr_iomux->dram_dqm6 = ddr->dram_dqm6; + mx6_ddr_iomux->dram_dqm7 = ddr->dram_dqm7; + } +} +#endif + +/* + * Configure mx6 mmdc registers based on: + * - board-specific memory configuration + * - board-specific calibration data + * - ddr3/lpddr2 chip details + * + * The various calculations here are derived from the Freescale + * 1. i.Mx6DQSDL DDR3 Script Aid spreadsheet (DOC-94917) designed to generate + * MMDC configuration registers based on memory system and memory chip + * parameters. + * + * 2. i.Mx6SL LPDDR2 Script Aid spreadsheet V0.04 designed to generate MMDC + * configuration registers based on memory system and memory chip + * parameters. + * + * The defaults here are those which were specified in the spreadsheet. + * For details on each register, refer to the IMX6DQRM and/or IMX6SDLRM + * and/or IMX6SLRM section titled MMDC initialization. + */ +#define MR(val, ba, cmd, cs1) \ + ((val << 16) | (1 << 15) | (cmd << 4) | (cs1 << 3) | ba) +#define MMDC1(entry, value) do { \ + if (!is_mx6sx() && !is_mx6ul() && !is_mx6ull() && !is_mx6sl()) \ + mmdc1->entry = value; \ + } while (0) + +/* see BOOT_CFG3 description Table 5-4. EIM Boot Fusemap */ +#define BOOT_CFG3_DDR_MASK 0x30 +#define BOOT_CFG3_EXT_DDR_MASK 0x33 + +#define DDR_MMAP_NOC_SINGLE 0 +#define DDR_MMAP_NOC_DUAL 0x31 + +/* NoC ACTIVATE shifts */ +#define NOC_RD_SHIFT 0 +#define NOC_FAW_PERIOD_SHIFT 4 +#define NOC_FAW_BANKS_SHIFT 10 + +/* NoC DdrTiming shifts */ +#define NOC_ACT_TO_ACT_SHIFT 0 +#define NOC_RD_TO_MISS_SHIFT 6 +#define NOC_WR_TO_MISS_SHIFT 12 +#define NOC_BURST_LEN_SHIFT 18 +#define NOC_RD_TO_WR_SHIFT 21 +#define NOC_WR_TO_RD_SHIFT 26 +#define NOC_BW_RATIO_SHIFT 31 + +/* + * According JESD209-2B-LPDDR2: Table 103 + * WL: write latency + */ +static int lpddr2_wl(uint32_t mem_speed) +{ + switch (mem_speed) { + case 1066: + case 933: + return 4; + case 800: + return 3; + case 677: + case 533: + return 2; + case 400: + case 333: + return 1; + default: + puts("invalid memory speed\n"); + hang(); + } + + return 0; +} + +/* + * According JESD209-2B-LPDDR2: Table 103 + * RL: read latency + */ +static int lpddr2_rl(uint32_t mem_speed) +{ + switch (mem_speed) { + case 1066: + return 8; + case 933: + return 7; + case 800: + return 6; + case 677: + return 5; + case 533: + return 4; + case 400: + case 333: + return 3; + default: + puts("invalid memory speed\n"); + hang(); + } + + return 0; +} + +void mx6_lpddr2_cfg(const struct mx6_ddr_sysinfo *sysinfo, + const struct mx6_mmdc_calibration *calib, + const struct mx6_lpddr2_cfg *lpddr2_cfg) +{ + volatile struct mmdc_p_regs *mmdc0; + u32 val; + u8 tcke, tcksrx, tcksre, trrd; + u8 twl, txp, tfaw, tcl; + u16 tras, twr, tmrd, trtp, twtr, trfc, txsr; + u16 trcd_lp, trppb_lp, trpab_lp, trc_lp; + u16 cs0_end; + u8 coladdr; + int clkper; /* clock period in picoseconds */ + int clock; /* clock freq in mHz */ + int cs; + + /* only support 16/32 bits */ + if (sysinfo->dsize > 1) + hang(); + + mmdc0 = (struct mmdc_p_regs *)MMDC_P0_BASE_ADDR; + + clock = mxc_get_clock(MXC_DDR_CLK) / 1000000U; + clkper = (1000 * 1000) / clock; /* pico seconds */ + + twl = lpddr2_wl(lpddr2_cfg->mem_speed) - 1; + + /* LPDDR2-S2 and LPDDR2-S4 have the same tRFC value. */ + switch (lpddr2_cfg->density) { + case 1: + case 2: + case 4: + trfc = DIV_ROUND_UP(130000, clkper) - 1; + txsr = DIV_ROUND_UP(140000, clkper) - 1; + break; + case 8: + trfc = DIV_ROUND_UP(210000, clkper) - 1; + txsr = DIV_ROUND_UP(220000, clkper) - 1; + break; + default: + /* + * 64Mb, 128Mb, 256Mb, 512Mb are not supported currently. + */ + hang(); + break; + } + /* + * txpdll, txpr, taonpd and taofpd are not relevant in LPDDR2 mode, + * set them to 0. */ + txp = DIV_ROUND_UP(7500, clkper) - 1; + tcke = 3; + if (lpddr2_cfg->mem_speed == 333) + tfaw = DIV_ROUND_UP(60000, clkper) - 1; + else + tfaw = DIV_ROUND_UP(50000, clkper) - 1; + trrd = DIV_ROUND_UP(10000, clkper) - 1; + + /* tckesr for LPDDR2 */ + tcksre = DIV_ROUND_UP(15000, clkper); + tcksrx = tcksre; + twr = DIV_ROUND_UP(15000, clkper) - 1; + /* + * tMRR: 2, tMRW: 5 + * tMRD should be set to max(tMRR, tMRW) + */ + tmrd = 5; + tras = DIV_ROUND_UP(lpddr2_cfg->trasmin, clkper / 10) - 1; + /* LPDDR2 mode use tRCD_LP filed in MDCFG3. */ + trcd_lp = DIV_ROUND_UP(lpddr2_cfg->trcd_lp, clkper / 10) - 1; + trc_lp = DIV_ROUND_UP(lpddr2_cfg->trasmin + lpddr2_cfg->trppb_lp, + clkper / 10) - 1; + trppb_lp = DIV_ROUND_UP(lpddr2_cfg->trppb_lp, clkper / 10) - 1; + trpab_lp = DIV_ROUND_UP(lpddr2_cfg->trpab_lp, clkper / 10) - 1; + /* To LPDDR2, CL in MDCFG0 refers to RL */ + tcl = lpddr2_rl(lpddr2_cfg->mem_speed) - 3; + twtr = DIV_ROUND_UP(7500, clkper) - 1; + trtp = DIV_ROUND_UP(7500, clkper) - 1; + + cs0_end = 4 * sysinfo->cs_density - 1; + + debug("density:%d Gb (%d Gb per chip)\n", + sysinfo->cs_density, lpddr2_cfg->density); + debug("clock: %dMHz (%d ps)\n", clock, clkper); + debug("memspd:%d\n", lpddr2_cfg->mem_speed); + debug("trcd_lp=%d\n", trcd_lp); + debug("trppb_lp=%d\n", trppb_lp); + debug("trpab_lp=%d\n", trpab_lp); + debug("trc_lp=%d\n", trc_lp); + debug("tcke=%d\n", tcke); + debug("tcksrx=%d\n", tcksrx); + debug("tcksre=%d\n", tcksre); + debug("trfc=%d\n", trfc); + debug("txsr=%d\n", txsr); + debug("txp=%d\n", txp); + debug("tfaw=%d\n", tfaw); + debug("tcl=%d\n", tcl); + debug("tras=%d\n", tras); + debug("twr=%d\n", twr); + debug("tmrd=%d\n", tmrd); + debug("twl=%d\n", twl); + debug("trtp=%d\n", trtp); + debug("twtr=%d\n", twtr); + debug("trrd=%d\n", trrd); + debug("cs0_end=%d\n", cs0_end); + debug("ncs=%d\n", sysinfo->ncs); + + /* + * board-specific configuration: + * These values are determined empirically and vary per board layout + */ + mmdc0->mpwldectrl0 = calib->p0_mpwldectrl0; + mmdc0->mpwldectrl1 = calib->p0_mpwldectrl1; + mmdc0->mpdgctrl0 = calib->p0_mpdgctrl0; + mmdc0->mpdgctrl1 = calib->p0_mpdgctrl1; + mmdc0->mprddlctl = calib->p0_mprddlctl; + mmdc0->mpwrdlctl = calib->p0_mpwrdlctl; + mmdc0->mpzqlp2ctl = calib->mpzqlp2ctl; + + /* Read data DQ Byte0-3 delay */ + mmdc0->mprddqby0dl = 0x33333333; + mmdc0->mprddqby1dl = 0x33333333; + if (sysinfo->dsize > 0) { + mmdc0->mprddqby2dl = 0x33333333; + mmdc0->mprddqby3dl = 0x33333333; + } + + /* Write data DQ Byte0-3 delay */ + mmdc0->mpwrdqby0dl = 0xf3333333; + mmdc0->mpwrdqby1dl = 0xf3333333; + if (sysinfo->dsize > 0) { + mmdc0->mpwrdqby2dl = 0xf3333333; + mmdc0->mpwrdqby3dl = 0xf3333333; + } + + /* + * In LPDDR2 mode this register should be cleared, + * so no termination will be activated. + */ + mmdc0->mpodtctrl = 0; + + /* complete calibration */ + val = (1 << 11); /* Force measurement on delay-lines */ + mmdc0->mpmur0 = val; + + /* Step 1: configuration request */ + mmdc0->mdscr = (u32)(1 << 15); /* config request */ + + /* Step 2: Timing configuration */ + mmdc0->mdcfg0 = (trfc << 24) | (txsr << 16) | (txp << 13) | + (tfaw << 4) | tcl; + mmdc0->mdcfg1 = (tras << 16) | (twr << 9) | (tmrd << 5) | twl; + mmdc0->mdcfg2 = (trtp << 6) | (twtr << 3) | trrd; + mmdc0->mdcfg3lp = (trc_lp << 16) | (trcd_lp << 8) | + (trppb_lp << 4) | trpab_lp; + mmdc0->mdotc = 0; + + mmdc0->mdasp = cs0_end; /* CS addressing */ + + /* Step 3: Configure DDR type */ + mmdc0->mdmisc = (sysinfo->cs1_mirror << 19) | (sysinfo->walat << 16) | + (sysinfo->bi_on << 12) | (sysinfo->mif3_mode << 9) | + (sysinfo->ralat << 6) | (1 << 3); + + /* Step 4: Configure delay while leaving reset */ + mmdc0->mdor = (sysinfo->sde_to_rst << 8) | + (sysinfo->rst_to_cke << 0); + + /* Step 5: Configure DDR physical parameters (density and burst len) */ + coladdr = lpddr2_cfg->coladdr; + if (lpddr2_cfg->coladdr == 8) /* 8-bit COL is 0x3 */ + coladdr += 4; + else if (lpddr2_cfg->coladdr == 12) /* 12-bit COL is 0x4 */ + coladdr += 1; + mmdc0->mdctl = (lpddr2_cfg->rowaddr - 11) << 24 | /* ROW */ + (coladdr - 9) << 20 | /* COL */ + (0 << 19) | /* Burst Length = 4 for LPDDR2 */ + (sysinfo->dsize << 16); /* DDR data bus size */ + + /* Step 6: Perform ZQ calibration */ + val = 0xa1390003; /* one-time HW ZQ calib */ + mmdc0->mpzqhwctrl = val; + + /* Step 7: Enable MMDC with desired chip select */ + mmdc0->mdctl |= (1 << 31) | /* SDE_0 for CS0 */ + ((sysinfo->ncs == 2) ? 1 : 0) << 30; /* SDE_1 for CS1 */ + + /* Step 8: Write Mode Registers to Init LPDDR2 devices */ + for (cs = 0; cs < sysinfo->ncs; cs++) { + /* MR63: reset */ + mmdc0->mdscr = MR(63, 0, 3, cs); + /* MR10: calibration, + * 0xff is calibration command after intilization. + */ + val = 0xA | (0xff << 8); + mmdc0->mdscr = MR(val, 0, 3, cs); + /* MR1 */ + val = 0x1 | (0x82 << 8); + mmdc0->mdscr = MR(val, 0, 3, cs); + /* MR2 */ + val = 0x2 | (0x04 << 8); + mmdc0->mdscr = MR(val, 0, 3, cs); + /* MR3 */ + val = 0x3 | (0x02 << 8); + mmdc0->mdscr = MR(val, 0, 3, cs); + } + + /* Step 10: Power down control and self-refresh */ + mmdc0->mdpdc = (tcke & 0x7) << 16 | + 5 << 12 | /* PWDT_1: 256 cycles */ + 5 << 8 | /* PWDT_0: 256 cycles */ + 1 << 6 | /* BOTH_CS_PD */ + (tcksrx & 0x7) << 3 | + (tcksre & 0x7); + mmdc0->mapsr = 0x00001006; /* ADOPT power down enabled */ + + /* Step 11: Configure ZQ calibration: one-time and periodic 1ms */ + val = 0xa1310003; + mmdc0->mpzqhwctrl = val; + + /* Step 12: Configure and activate periodic refresh */ + mmdc0->mdref = (sysinfo->refsel << 14) | (sysinfo->refr << 11); + + /* Step 13: Deassert config request - init complete */ + mmdc0->mdscr = 0x00000000; + + /* wait for auto-ZQ calibration to complete */ + mdelay(1); +} + +void mx6_ddr3_cfg(const struct mx6_ddr_sysinfo *sysinfo, + const struct mx6_mmdc_calibration *calib, + const struct mx6_ddr3_cfg *ddr3_cfg) +{ + volatile struct mmdc_p_regs *mmdc0; + volatile struct mmdc_p_regs *mmdc1; + struct src *src_regs = (struct src *)SRC_BASE_ADDR; + u8 soc_boot_cfg3 = (readl(&src_regs->sbmr1) >> 16) & 0xff; + u32 val; + u8 tcke, tcksrx, tcksre, txpdll, taofpd, taonpd, trrd; + u8 todtlon, taxpd, tanpd, tcwl, txp, tfaw, tcl; + u8 todt_idle_off = 0x4; /* from DDR3 Script Aid spreadsheet */ + u16 trcd, trc, tras, twr, tmrd, trtp, trp, twtr, trfc, txs, txpr; + u16 cs0_end; + u16 tdllk = 0x1ff; /* DLL locking time: 512 cycles (JEDEC DDR3) */ + u8 coladdr; + int clkper; /* clock period in picoseconds */ + int clock; /* clock freq in MHz */ + int cs; + u16 mem_speed = ddr3_cfg->mem_speed; + + mmdc0 = (struct mmdc_p_regs *)MMDC_P0_BASE_ADDR; + if (!is_mx6sx() && !is_mx6ul() && !is_mx6ull() && !is_mx6sl()) + mmdc1 = (struct mmdc_p_regs *)MMDC_P1_BASE_ADDR; + + /* Limit mem_speed for MX6D/MX6Q */ + if (is_mx6dq() || is_mx6dqp()) { + if (mem_speed > 1066) + mem_speed = 1066; /* 1066 MT/s */ + + tcwl = 4; + } + /* Limit mem_speed for MX6S/MX6DL */ + else { + if (mem_speed > 800) + mem_speed = 800; /* 800 MT/s */ + + tcwl = 3; + } + + clock = mem_speed / 2; + /* + * Data rate of 1066 MT/s requires 533 MHz DDR3 clock, but MX6D/Q supports + * up to 528 MHz, so reduce the clock to fit chip specs + */ + if (is_mx6dq() || is_mx6dqp()) { + if (clock > 528) + clock = 528; /* 528 MHz */ + } + + clkper = (1000 * 1000) / clock; /* pico seconds */ + todtlon = tcwl; + taxpd = tcwl; + tanpd = tcwl; + + switch (ddr3_cfg->density) { + case 1: /* 1Gb per chip */ + trfc = DIV_ROUND_UP(110000, clkper) - 1; + txs = DIV_ROUND_UP(120000, clkper) - 1; + break; + case 2: /* 2Gb per chip */ + trfc = DIV_ROUND_UP(160000, clkper) - 1; + txs = DIV_ROUND_UP(170000, clkper) - 1; + break; + case 4: /* 4Gb per chip */ + trfc = DIV_ROUND_UP(260000, clkper) - 1; + txs = DIV_ROUND_UP(270000, clkper) - 1; + break; + case 8: /* 8Gb per chip */ + trfc = DIV_ROUND_UP(350000, clkper) - 1; + txs = DIV_ROUND_UP(360000, clkper) - 1; + break; + default: + /* invalid density */ + puts("invalid chip density\n"); + hang(); + break; + } + txpr = txs; + + switch (mem_speed) { + case 800: + txp = DIV_ROUND_UP(max(3 * clkper, 7500), clkper) - 1; + tcke = DIV_ROUND_UP(max(3 * clkper, 7500), clkper) - 1; + if (ddr3_cfg->pagesz == 1) { + tfaw = DIV_ROUND_UP(40000, clkper) - 1; + trrd = DIV_ROUND_UP(max(4 * clkper, 10000), clkper) - 1; + } else { + tfaw = DIV_ROUND_UP(50000, clkper) - 1; + trrd = DIV_ROUND_UP(max(4 * clkper, 10000), clkper) - 1; + } + break; + case 1066: + txp = DIV_ROUND_UP(max(3 * clkper, 7500), clkper) - 1; + tcke = DIV_ROUND_UP(max(3 * clkper, 5625), clkper) - 1; + if (ddr3_cfg->pagesz == 1) { + tfaw = DIV_ROUND_UP(37500, clkper) - 1; + trrd = DIV_ROUND_UP(max(4 * clkper, 7500), clkper) - 1; + } else { + tfaw = DIV_ROUND_UP(50000, clkper) - 1; + trrd = DIV_ROUND_UP(max(4 * clkper, 10000), clkper) - 1; + } + break; + default: + puts("invalid memory speed\n"); + hang(); + break; + } + txpdll = DIV_ROUND_UP(max(10 * clkper, 24000), clkper) - 1; + tcksre = DIV_ROUND_UP(max(5 * clkper, 10000), clkper); + taonpd = DIV_ROUND_UP(2000, clkper) - 1; + tcksrx = tcksre; + taofpd = taonpd; + twr = DIV_ROUND_UP(15000, clkper) - 1; + tmrd = DIV_ROUND_UP(max(12 * clkper, 15000), clkper) - 1; + trc = DIV_ROUND_UP(ddr3_cfg->trcmin, clkper / 10) - 1; + tras = DIV_ROUND_UP(ddr3_cfg->trasmin, clkper / 10) - 1; + tcl = DIV_ROUND_UP(ddr3_cfg->trcd, clkper / 10) - 3; + trp = DIV_ROUND_UP(ddr3_cfg->trcd, clkper / 10) - 1; + twtr = ROUND(max(4 * clkper, 7500) / clkper, 1) - 1; + trcd = trp; + trtp = twtr; + cs0_end = 4 * sysinfo->cs_density - 1; + + debug("density:%d Gb (%d Gb per chip)\n", + sysinfo->cs_density, ddr3_cfg->density); + debug("clock: %dMHz (%d ps)\n", clock, clkper); + debug("memspd:%d\n", mem_speed); + debug("tcke=%d\n", tcke); + debug("tcksrx=%d\n", tcksrx); + debug("tcksre=%d\n", tcksre); + debug("taofpd=%d\n", taofpd); + debug("taonpd=%d\n", taonpd); + debug("todtlon=%d\n", todtlon); + debug("tanpd=%d\n", tanpd); + debug("taxpd=%d\n", taxpd); + debug("trfc=%d\n", trfc); + debug("txs=%d\n", txs); + debug("txp=%d\n", txp); + debug("txpdll=%d\n", txpdll); + debug("tfaw=%d\n", tfaw); + debug("tcl=%d\n", tcl); + debug("trcd=%d\n", trcd); + debug("trp=%d\n", trp); + debug("trc=%d\n", trc); + debug("tras=%d\n", tras); + debug("twr=%d\n", twr); + debug("tmrd=%d\n", tmrd); + debug("tcwl=%d\n", tcwl); + debug("tdllk=%d\n", tdllk); + debug("trtp=%d\n", trtp); + debug("twtr=%d\n", twtr); + debug("trrd=%d\n", trrd); + debug("txpr=%d\n", txpr); + debug("cs0_end=%d\n", cs0_end); + debug("ncs=%d\n", sysinfo->ncs); + debug("Rtt_wr=%d\n", sysinfo->rtt_wr); + debug("Rtt_nom=%d\n", sysinfo->rtt_nom); + debug("SRT=%d\n", ddr3_cfg->SRT); + debug("twr=%d\n", twr); + + /* + * board-specific configuration: + * These values are determined empirically and vary per board layout + * see: + * appnote, ddr3 spreadsheet + */ + mmdc0->mpwldectrl0 = calib->p0_mpwldectrl0; + mmdc0->mpwldectrl1 = calib->p0_mpwldectrl1; + mmdc0->mpdgctrl0 = calib->p0_mpdgctrl0; + mmdc0->mpdgctrl1 = calib->p0_mpdgctrl1; + mmdc0->mprddlctl = calib->p0_mprddlctl; + mmdc0->mpwrdlctl = calib->p0_mpwrdlctl; + if (sysinfo->dsize > 1) { + MMDC1(mpwldectrl0, calib->p1_mpwldectrl0); + MMDC1(mpwldectrl1, calib->p1_mpwldectrl1); + MMDC1(mpdgctrl0, calib->p1_mpdgctrl0); + MMDC1(mpdgctrl1, calib->p1_mpdgctrl1); + MMDC1(mprddlctl, calib->p1_mprddlctl); + MMDC1(mpwrdlctl, calib->p1_mpwrdlctl); + } + + /* Read data DQ Byte0-3 delay */ + mmdc0->mprddqby0dl = 0x33333333; + mmdc0->mprddqby1dl = 0x33333333; + if (sysinfo->dsize > 0) { + mmdc0->mprddqby2dl = 0x33333333; + mmdc0->mprddqby3dl = 0x33333333; + } + + if (sysinfo->dsize > 1) { + MMDC1(mprddqby0dl, 0x33333333); + MMDC1(mprddqby1dl, 0x33333333); + MMDC1(mprddqby2dl, 0x33333333); + MMDC1(mprddqby3dl, 0x33333333); + } + + /* MMDC Termination: rtt_nom:2 RZQ/2(120ohm), rtt_nom:1 RZQ/4(60ohm) */ + val = (sysinfo->rtt_nom == 2) ? 0x00011117 : 0x00022227; + mmdc0->mpodtctrl = val; + if (sysinfo->dsize > 1) + MMDC1(mpodtctrl, val); + + /* complete calibration */ + val = (1 << 11); /* Force measurement on delay-lines */ + mmdc0->mpmur0 = val; + if (sysinfo->dsize > 1) + MMDC1(mpmur0, val); + + /* Step 1: configuration request */ + mmdc0->mdscr = (u32)(1 << 15); /* config request */ + + /* Step 2: Timing configuration */ + mmdc0->mdcfg0 = (trfc << 24) | (txs << 16) | (txp << 13) | + (txpdll << 9) | (tfaw << 4) | tcl; + mmdc0->mdcfg1 = (trcd << 29) | (trp << 26) | (trc << 21) | + (tras << 16) | (1 << 15) /* trpa */ | + (twr << 9) | (tmrd << 5) | tcwl; + mmdc0->mdcfg2 = (tdllk << 16) | (trtp << 6) | (twtr << 3) | trrd; + mmdc0->mdotc = (taofpd << 27) | (taonpd << 24) | (tanpd << 20) | + (taxpd << 16) | (todtlon << 12) | (todt_idle_off << 4); + mmdc0->mdasp = cs0_end; /* CS addressing */ + + /* Step 3: Configure DDR type */ + mmdc0->mdmisc = (sysinfo->cs1_mirror << 19) | (sysinfo->walat << 16) | + (sysinfo->bi_on << 12) | (sysinfo->mif3_mode << 9) | + (sysinfo->ralat << 6); + + /* Step 4: Configure delay while leaving reset */ + mmdc0->mdor = (txpr << 16) | (sysinfo->sde_to_rst << 8) | + (sysinfo->rst_to_cke << 0); + + /* Step 5: Configure DDR physical parameters (density and burst len) */ + coladdr = ddr3_cfg->coladdr; + if (ddr3_cfg->coladdr == 8) /* 8-bit COL is 0x3 */ + coladdr += 4; + else if (ddr3_cfg->coladdr == 12) /* 12-bit COL is 0x4 */ + coladdr += 1; + mmdc0->mdctl = (ddr3_cfg->rowaddr - 11) << 24 | /* ROW */ + (coladdr - 9) << 20 | /* COL */ + (1 << 19) | /* Burst Length = 8 for DDR3 */ + (sysinfo->dsize << 16); /* DDR data bus size */ + + /* Step 6: Perform ZQ calibration */ + val = 0xa1390001; /* one-time HW ZQ calib */ + mmdc0->mpzqhwctrl = val; + if (sysinfo->dsize > 1) + MMDC1(mpzqhwctrl, val); + + /* Step 7: Enable MMDC with desired chip select */ + mmdc0->mdctl |= (1 << 31) | /* SDE_0 for CS0 */ + ((sysinfo->ncs == 2) ? 1 : 0) << 30; /* SDE_1 for CS1 */ + + /* Step 8: Write Mode Registers to Init DDR3 devices */ + for (cs = 0; cs < sysinfo->ncs; cs++) { + /* MR2 */ + val = (sysinfo->rtt_wr & 3) << 9 | (ddr3_cfg->SRT & 1) << 7 | + ((tcwl - 3) & 3) << 3; + debug("MR2 CS%d: 0x%08x\n", cs, (u32)MR(val, 2, 3, cs)); + mmdc0->mdscr = MR(val, 2, 3, cs); + /* MR3 */ + debug("MR3 CS%d: 0x%08x\n", cs, (u32)MR(0, 3, 3, cs)); + mmdc0->mdscr = MR(0, 3, 3, cs); + /* MR1 */ + val = ((sysinfo->rtt_nom & 1) ? 1 : 0) << 2 | + ((sysinfo->rtt_nom & 2) ? 1 : 0) << 6; + debug("MR1 CS%d: 0x%08x\n", cs, (u32)MR(val, 1, 3, cs)); + mmdc0->mdscr = MR(val, 1, 3, cs); + /* MR0 */ + val = ((tcl - 1) << 4) | /* CAS */ + (1 << 8) | /* DLL Reset */ + ((twr - 3) << 9) | /* Write Recovery */ + (sysinfo->pd_fast_exit << 12); /* Precharge PD PLL on */ + debug("MR0 CS%d: 0x%08x\n", cs, (u32)MR(val, 0, 3, cs)); + mmdc0->mdscr = MR(val, 0, 3, cs); + /* ZQ calibration */ + val = (1 << 10); + mmdc0->mdscr = MR(val, 0, 4, cs); + } + + /* Step 10: Power down control and self-refresh */ + mmdc0->mdpdc = (tcke & 0x7) << 16 | + 5 << 12 | /* PWDT_1: 256 cycles */ + 5 << 8 | /* PWDT_0: 256 cycles */ + 1 << 6 | /* BOTH_CS_PD */ + (tcksrx & 0x7) << 3 | + (tcksre & 0x7); + if (!sysinfo->pd_fast_exit) + mmdc0->mdpdc |= (1 << 7); /* SLOW_PD */ + mmdc0->mapsr = 0x00001006; /* ADOPT power down enabled */ + + /* Step 11: Configure ZQ calibration: one-time and periodic 1ms */ + val = 0xa1390003; + mmdc0->mpzqhwctrl = val; + if (sysinfo->dsize > 1) + MMDC1(mpzqhwctrl, val); + + /* Step 12: Configure and activate periodic refresh */ + mmdc0->mdref = (sysinfo->refsel << 14) | (sysinfo->refr << 11); + + /* + * Step 13: i.MX6DQP only: If the NoC scheduler is enabled, + * configure it and disable MMDC arbitration/reordering (see EB828) + */ + if (is_mx6dqp() && + ((soc_boot_cfg3 & BOOT_CFG3_DDR_MASK) == DDR_MMAP_NOC_SINGLE || + (soc_boot_cfg3 & BOOT_CFG3_EXT_DDR_MASK) == DDR_MMAP_NOC_DUAL)) { + struct mx6dqp_noc_sched_regs *noc_sched = + (struct mx6dqp_noc_sched_regs *)MX6DQP_NOC_SCHED_BASE; + + /* + * These values are fixed based on integration parameters and + * should not be modified + */ + noc_sched->rlat = 0x00000040; + noc_sched->ipu1 = 0x00000020; + noc_sched->ipu2 = 0x00000020; + + noc_sched->activate = (1 << NOC_FAW_BANKS_SHIFT) | + (tfaw << NOC_FAW_PERIOD_SHIFT) | + (trrd << NOC_RD_SHIFT); + noc_sched->ddrtiming = (((sysinfo->dsize == 1) ? 1 : 0) + << NOC_BW_RATIO_SHIFT) | + ((tcwl + twtr) << NOC_WR_TO_RD_SHIFT) | + ((tcl - tcwl + 2) << NOC_RD_TO_WR_SHIFT) | + (4 << NOC_BURST_LEN_SHIFT) | /* BL8 */ + ((tcwl + twr + trp + trcd) + << NOC_WR_TO_MISS_SHIFT) | + ((trtp + trp + trcd - 4) + << NOC_RD_TO_MISS_SHIFT) | + (trc << NOC_ACT_TO_ACT_SHIFT); + + if (sysinfo->dsize == 2) { + if (ddr3_cfg->coladdr == 10) { + if (ddr3_cfg->rowaddr == 15 && + sysinfo->ncs == 2) + noc_sched->ddrconf = 4; + else + noc_sched->ddrconf = 0; + } else if (ddr3_cfg->coladdr == 11) { + noc_sched->ddrconf = 1; + } + } else { + if (ddr3_cfg->coladdr == 9) { + if (ddr3_cfg->rowaddr == 13) + noc_sched->ddrconf = 2; + else if (ddr3_cfg->rowaddr == 14) + noc_sched->ddrconf = 15; + } else if (ddr3_cfg->coladdr == 10) { + if (ddr3_cfg->rowaddr == 14 && + sysinfo->ncs == 2) + noc_sched->ddrconf = 14; + else if (ddr3_cfg->rowaddr == 15 && + sysinfo->ncs == 2) + noc_sched->ddrconf = 9; + else + noc_sched->ddrconf = 3; + } else if (ddr3_cfg->coladdr == 11) { + if (ddr3_cfg->rowaddr == 15 && + sysinfo->ncs == 2) + noc_sched->ddrconf = 4; + else + noc_sched->ddrconf = 0; + } else if (ddr3_cfg->coladdr == 12) { + if (ddr3_cfg->rowaddr == 14) + noc_sched->ddrconf = 1; + } + } + + /* Disable MMDC arbitration/reordering */ + mmdc0->maarcr = 0x14420000; + } + + /* Step 13: Deassert config request - init complete */ + mmdc0->mdscr = 0x00000000; + + /* wait for auto-ZQ calibration to complete */ + mdelay(1); +} + +void mmdc_read_calibration(struct mx6_ddr_sysinfo const *sysinfo, + struct mx6_mmdc_calibration *calib) +{ + struct mmdc_p_regs *mmdc0 = (struct mmdc_p_regs *)MMDC_P0_BASE_ADDR; + struct mmdc_p_regs *mmdc1 = (struct mmdc_p_regs *)MMDC_P1_BASE_ADDR; + + calib->p0_mpwldectrl0 = readl(&mmdc0->mpwldectrl0); + calib->p0_mpwldectrl1 = readl(&mmdc0->mpwldectrl1); + calib->p0_mpdgctrl0 = readl(&mmdc0->mpdgctrl0); + calib->p0_mpdgctrl1 = readl(&mmdc0->mpdgctrl1); + calib->p0_mprddlctl = readl(&mmdc0->mprddlctl); + calib->p0_mpwrdlctl = readl(&mmdc0->mpwrdlctl); + + if (sysinfo->dsize == 2) { + calib->p1_mpwldectrl0 = readl(&mmdc1->mpwldectrl0); + calib->p1_mpwldectrl1 = readl(&mmdc1->mpwldectrl1); + calib->p1_mpdgctrl0 = readl(&mmdc1->mpdgctrl0); + calib->p1_mpdgctrl1 = readl(&mmdc1->mpdgctrl1); + calib->p1_mprddlctl = readl(&mmdc1->mprddlctl); + calib->p1_mpwrdlctl = readl(&mmdc1->mpwrdlctl); + } +} + +void mx6_dram_cfg(const struct mx6_ddr_sysinfo *sysinfo, + const struct mx6_mmdc_calibration *calib, + const void *ddr_cfg) +{ + if (sysinfo->ddr_type == DDR_TYPE_DDR3) { + mx6_ddr3_cfg(sysinfo, calib, ddr_cfg); + } else if (sysinfo->ddr_type == DDR_TYPE_LPDDR2) { + mx6_lpddr2_cfg(sysinfo, calib, ddr_cfg); + } else { + puts("Unsupported ddr type\n"); + hang(); + } +} diff --git a/roms/u-boot/arch/arm/mach-imx/mx6/litesom.c b/roms/u-boot/arch/arm/mach-imx/mx6/litesom.c new file mode 100644 index 000000000..699a3dc31 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/mx6/litesom.c @@ -0,0 +1,202 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2015-2016 Freescale Semiconductor, Inc. + * Copyright (C) 2016 Grinn + */ + +#include <init.h> +#include <asm/arch/clock.h> +#include <asm/arch/iomux.h> +#include <asm/arch/imx-regs.h> +#include <asm/arch/crm_regs.h> +#include <asm/arch/mx6ul_pins.h> +#include <asm/arch/mx6-pins.h> +#include <asm/arch/sys_proto.h> +#include <asm/global_data.h> +#include <asm/gpio.h> +#include <asm/mach-imx/iomux-v3.h> +#include <asm/mach-imx/boot_mode.h> +#include <asm/io.h> +#include <common.h> +#include <fsl_esdhc_imx.h> +#include <linux/delay.h> +#include <linux/sizes.h> +#include <mmc.h> + +DECLARE_GLOBAL_DATA_PTR; + +#define USDHC_PAD_CTRL (PAD_CTL_PKE | PAD_CTL_PUE | \ + PAD_CTL_PUS_22K_UP | PAD_CTL_SPEED_LOW | \ + PAD_CTL_DSE_80ohm | PAD_CTL_SRE_FAST | PAD_CTL_HYS) + +int dram_init(void) +{ + gd->ram_size = imx_ddr_size(); + + return 0; +} + +static iomux_v3_cfg_t const emmc_pads[] = { + MX6_PAD_NAND_RE_B__USDHC2_CLK | MUX_PAD_CTRL(USDHC_PAD_CTRL), + MX6_PAD_NAND_WE_B__USDHC2_CMD | MUX_PAD_CTRL(USDHC_PAD_CTRL), + MX6_PAD_NAND_DATA00__USDHC2_DATA0 | MUX_PAD_CTRL(USDHC_PAD_CTRL), + MX6_PAD_NAND_DATA01__USDHC2_DATA1 | MUX_PAD_CTRL(USDHC_PAD_CTRL), + MX6_PAD_NAND_DATA02__USDHC2_DATA2 | MUX_PAD_CTRL(USDHC_PAD_CTRL), + MX6_PAD_NAND_DATA03__USDHC2_DATA3 | MUX_PAD_CTRL(USDHC_PAD_CTRL), + MX6_PAD_NAND_DATA04__USDHC2_DATA4 | MUX_PAD_CTRL(USDHC_PAD_CTRL), + MX6_PAD_NAND_DATA05__USDHC2_DATA5 | MUX_PAD_CTRL(USDHC_PAD_CTRL), + MX6_PAD_NAND_DATA06__USDHC2_DATA6 | MUX_PAD_CTRL(USDHC_PAD_CTRL), + MX6_PAD_NAND_DATA07__USDHC2_DATA7 | MUX_PAD_CTRL(USDHC_PAD_CTRL), + + /* RST_B */ + MX6_PAD_NAND_ALE__GPIO4_IO10 | MUX_PAD_CTRL(NO_PAD_CTRL), +}; + +#ifdef CONFIG_FSL_ESDHC_IMX +static struct fsl_esdhc_cfg emmc_cfg = {USDHC2_BASE_ADDR, 0, 8}; + +#define EMMC_PWR_GPIO IMX_GPIO_NR(4, 10) + +int litesom_mmc_init(struct bd_info *bis) +{ + int ret; + + /* eMMC */ + imx_iomux_v3_setup_multiple_pads(emmc_pads, ARRAY_SIZE(emmc_pads)); + gpio_direction_output(EMMC_PWR_GPIO, 0); + udelay(500); + gpio_direction_output(EMMC_PWR_GPIO, 1); + emmc_cfg.sdhc_clk = mxc_get_clock(MXC_ESDHC2_CLK); + + ret = fsl_esdhc_initialize(bis, &emmc_cfg); + if (ret) { + printf("Warning: failed to initialize mmc dev 1 (eMMC)\n"); + return ret; + } + + return 0; +} +#endif + +#ifdef CONFIG_SPL_BUILD +#include <linux/libfdt.h> +#include <spl.h> +#include <asm/arch/mx6-ddr.h> + + +static struct mx6ul_iomux_grp_regs mx6_grp_ioregs = { + .grp_addds = 0x00000030, + .grp_ddrmode_ctl = 0x00020000, + .grp_b0ds = 0x00000030, + .grp_ctlds = 0x00000030, + .grp_b1ds = 0x00000030, + .grp_ddrpke = 0x00000000, + .grp_ddrmode = 0x00020000, + .grp_ddr_type = 0x000c0000, +}; + +static struct mx6ul_iomux_ddr_regs mx6_ddr_ioregs = { + .dram_dqm0 = 0x00000030, + .dram_dqm1 = 0x00000030, + .dram_ras = 0x00000030, + .dram_cas = 0x00000030, + .dram_odt0 = 0x00000030, + .dram_odt1 = 0x00000030, + .dram_sdba2 = 0x00000000, + .dram_sdclk_0 = 0x00000030, + .dram_sdqs0 = 0x00000030, + .dram_sdqs1 = 0x00000030, + .dram_reset = 0x00000030, +}; + +static struct mx6_mmdc_calibration mx6_mmcd_calib = { + .p0_mpwldectrl0 = 0x00000000, + .p0_mpdgctrl0 = 0x41570155, + .p0_mprddlctl = 0x4040474A, + .p0_mpwrdlctl = 0x40405550, +}; + +struct mx6_ddr_sysinfo ddr_sysinfo = { + .dsize = 0, + .cs_density = 20, + .ncs = 1, + .cs1_mirror = 0, + .rtt_wr = 2, + .rtt_nom = 1, /* RTT_Nom = RZQ/2 */ + .walat = 0, /* Write additional latency */ + .ralat = 5, /* Read additional latency */ + .mif3_mode = 3, /* Command prediction working mode */ + .bi_on = 1, /* Bank interleaving enabled */ + .sde_to_rst = 0x10, /* 14 cycles, 200us (JEDEC default) */ + .rst_to_cke = 0x23, /* 33 cycles, 500us (JEDEC default) */ + .ddr_type = DDR_TYPE_DDR3, + .refsel = 0, /* Refresh cycles at 64KHz */ + .refr = 1, /* 2 refresh commands per refresh cycle */ +}; + +static struct mx6_ddr3_cfg mem_ddr = { + .mem_speed = 800, + .density = 4, + .width = 16, + .banks = 8, + .rowaddr = 15, + .coladdr = 10, + .pagesz = 2, + .trcd = 1375, + .trcmin = 4875, + .trasmin = 3500, +}; + +static void ccgr_init(void) +{ + struct mxc_ccm_reg *ccm = (struct mxc_ccm_reg *)CCM_BASE_ADDR; + + writel(0xFFFFFFFF, &ccm->CCGR0); + writel(0xFFFFFFFF, &ccm->CCGR1); + writel(0xFFFFFFFF, &ccm->CCGR2); + writel(0xFFFFFFFF, &ccm->CCGR3); + writel(0xFFFFFFFF, &ccm->CCGR4); + writel(0xFFFFFFFF, &ccm->CCGR5); + writel(0xFFFFFFFF, &ccm->CCGR6); + writel(0xFFFFFFFF, &ccm->CCGR7); +} + +static void spl_dram_init(void) +{ + unsigned long ram_size; + + mx6ul_dram_iocfg(mem_ddr.width, &mx6_ddr_ioregs, &mx6_grp_ioregs); + mx6_dram_cfg(&ddr_sysinfo, &mx6_mmcd_calib, &mem_ddr); + + /* + * Get actual RAM size, so we can adjust DDR row size for <512M + * memories + */ + ram_size = get_ram_size((void *)CONFIG_SYS_SDRAM_BASE, SZ_512M); + if (ram_size < SZ_512M) { + mem_ddr.rowaddr = 14; + mx6_dram_cfg(&ddr_sysinfo, &mx6_mmcd_calib, &mem_ddr); + } +} + +void litesom_init_f(void) +{ + ccgr_init(); + + /* setup AIPS and disable watchdog */ + arch_cpu_init(); + +#ifdef CONFIG_BOARD_EARLY_INIT_F + board_early_init_f(); +#endif + + /* setup GP timer */ + timer_init(); + + /* UART clocks enabled and gd valid - init serial console */ + preloader_console_init(); + + /* DDR initialization */ + spl_dram_init(); +} +#endif diff --git a/roms/u-boot/arch/arm/mach-imx/mx6/module_fuse.c b/roms/u-boot/arch/arm/mach-imx/mx6/module_fuse.c new file mode 100644 index 000000000..0f4565e31 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/mx6/module_fuse.c @@ -0,0 +1,322 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2019 NXP + */ + +#include <common.h> +#include <fdt_support.h> +#include <asm/io.h> +#include <asm/arch/sys_proto.h> +#include <asm/arch/imx-regs.h> +#include <asm/mach-imx/module_fuse.h> +#include <linux/errno.h> + +static struct fuse_entry_desc mx6_fuse_descs[] = { +#if defined(CONFIG_MX6ULL) + {MODULE_TSC, "/soc/aips-bus@2000000/tsc@2040000", 0x430, 22}, + {MODULE_ADC2, "/soc/aips-bus@2100000/adc@219c000", 0x430, 23}, + {MODULE_EPDC, "/soc/aips-bus@2200000/epdc@228c000", 0x430, 24}, + {MODULE_ESAI, "/soc/aips-bus@2000000/spba-bus@2000000/esai@2024000", 0x430, 25}, + {MODULE_FLEXCAN1, "/soc/aips-bus@2000000/can@2090000", 0x430, 26}, + {MODULE_FLEXCAN2, "/soc/aips-bus@2000000/can@2094000", 0x430, 27}, + {MODULE_SPDIF, "/soc/aips-bus@2000000/spba-bus@2000000/spdif@2004000", 0x440, 2}, + {MODULE_EIM, "/soc/aips-bus@2100000/weim@21b8000", 0x440, 3}, + {MODULE_SD1, "/soc/aips-bus@2100000/usdhc@2190000", 0x440, 4}, + {MODULE_SD2, "/soc/aips-bus@2100000/usdhc@2194000", 0x440, 5}, + {MODULE_QSPI1, "/soc/aips-bus@2100000/qspi@21e0000", 0x440, 6}, + {MODULE_GPMI, "/soc/gpmi-nand@1806000", 0x440, 7}, + {MODULE_APBHDMA, "/soc/dma-apbh@1804000", 0x440, 7}, + {MODULE_LCDIF, "/soc/aips-bus@2100000/lcdif@21c8000", 0x440, 8}, + {MODULE_PXP, "/soc/aips-bus@2100000/pxp@21cc000", 0x440, 9}, + {MODULE_CSI, "/soc/aips-bus@2100000/csi@21c4000", 0x440, 10}, + {MODULE_ADC1, "/soc/aips-bus@2100000/adc@2198000", 0x440, 11}, + {MODULE_ENET1, "/soc/aips-bus@2100000/ethernet@2188000", 0x440, 12}, + {MODULE_ENET2, "/soc/aips-bus@2000000/ethernet@20b4000", 0x440, 13}, + {MODULE_DCP, "/soc/aips-bus@2200000/dcp@2280000", 0x440, 14}, + {MODULE_USB_OTG2, "/soc/aips-bus@2100000/usb@2184200", 0x440, 15}, + {MODULE_SAI2, "/soc/aips-bus@2000000/spba-bus@2000000/sai@202c000", 0x440, 24}, + {MODULE_SAI3, "/soc/aips-bus@2000000/spba-bus@2000000/sai@2030000", 0x440, 24}, + {MODULE_DCP_CRYPTO, "/soc/aips-bus@2200000/dcp@2280000", 0x440, 25}, + {MODULE_UART5, "/soc/aips-bus@2100000/serial@21f4000", 0x440, 26}, + {MODULE_UART6, "/soc/aips-bus@2100000/serial@21fc000", 0x440, 26}, + {MODULE_UART7, "/soc/aips-bus@2000000/spba-bus@2000000/serial@2018000", 0x440, 26}, + {MODULE_UART8, "/soc/aips-bus@2200000/serial@2288000", 0x440, 26}, + {MODULE_PWM5, "/soc/aips-bus@2000000/pwm@20f0000", 0x440, 27}, + {MODULE_PWM6, "/soc/aips-bus@2000000/pwm@20f4000", 0x440, 27}, + {MODULE_PWM7, "/soc/aips-bus@2000000/pwm@20f8000", 0x440, 27}, + {MODULE_PWM8, "/soc/aips-bus@2000000/pwm@20fc000", 0x440, 27}, + {MODULE_ECSPI3, "/soc/aips-bus@2000000/spba-bus@2000000/ecspi@2010000", 0x440, 28}, + {MODULE_ECSPI4, "/soc/aips-bus@2000000/spba-bus@2000000/ecspi@2014000", 0x440, 28}, + {MODULE_I2C3, "/soc/aips-bus@2100000/i2c@21a8000", 0x440, 29}, + {MODULE_I2C4, "/soc/aips-bus@2100000/i2c@21f8000", 0x440, 29}, + {MODULE_GPT2, "/soc/aips-bus@2000000/gpt@20e8000", 0x440, 30}, + {MODULE_EPIT2, "/soc/aips-bus@2000000/epit@20d4000", 0x440, 31}, + /* Paths for older imx tree: */ + {MODULE_TSC, "/soc/aips-bus@02000000/tsc@02040000", 0x430, 22}, + {MODULE_ADC2, "/soc/aips-bus@02100000/adc@0219c000", 0x430, 23}, + {MODULE_EPDC, "/soc/aips-bus@02200000/epdc@0228c000", 0x430, 24}, + {MODULE_ESAI, "/soc/aips-bus@02000000/spba-bus@02000000/esai@02024000", 0x430, 25}, + {MODULE_FLEXCAN1, "/soc/aips-bus@02000000/can@02090000", 0x430, 26}, + {MODULE_FLEXCAN2, "/soc/aips-bus@02000000/can@02094000", 0x430, 27}, + {MODULE_SPDIF, "/soc/aips-bus@02000000/spba-bus@02000000/spdif@02004000", 0x440, 2}, + {MODULE_EIM, "/soc/aips-bus@02100000/weim@021b8000", 0x440, 3}, + {MODULE_SD1, "/soc/aips-bus@02100000/usdhc@02190000", 0x440, 4}, + {MODULE_SD2, "/soc/aips-bus@02100000/usdhc@02194000", 0x440, 5}, + {MODULE_QSPI1, "/soc/aips-bus@02100000/qspi@021e0000", 0x440, 6}, + {MODULE_GPMI, "/soc/gpmi-nand@01806000", 0x440, 7}, + {MODULE_APBHDMA, "/soc/dma-apbh@01804000", 0x440, 7}, + {MODULE_LCDIF, "/soc/aips-bus@02100000/lcdif@021c8000", 0x440, 8}, + {MODULE_PXP, "/soc/aips-bus@02100000/pxp@021cc000", 0x440, 9}, + {MODULE_CSI, "/soc/aips-bus@02100000/csi@021c4000", 0x440, 10}, + {MODULE_ADC1, "/soc/aips-bus@02100000/adc@02198000", 0x440, 11}, + {MODULE_ENET1, "/soc/aips-bus@02100000/ethernet@02188000", 0x440, 12}, + {MODULE_ENET2, "/soc/aips-bus@02000000/ethernet@020b4000", 0x440, 13}, + {MODULE_DCP, "/soc/aips-bus@02200000/dcp@02280000", 0x440, 14}, + {MODULE_USB_OTG2, "/soc/aips-bus@02100000/usb@02184200", 0x440, 15}, + {MODULE_SAI2, "/soc/aips-bus@02000000/spba-bus@02000000/sai@0202c000", 0x440, 24}, + {MODULE_SAI3, "/soc/aips-bus@02000000/spba-bus@02000000/sai@02030000", 0x440, 24}, + {MODULE_DCP_CRYPTO, "/soc/aips-bus@02200000/dcp@02280000", 0x440, 25}, + {MODULE_UART5, "/soc/aips-bus@02100000/serial@021f4000", 0x440, 26}, + {MODULE_UART6, "/soc/aips-bus@02100000/serial@021fc000", 0x440, 26}, + {MODULE_UART7, "/soc/aips-bus@02000000/spba-bus@02000000/serial@02018000", 0x440, 26}, + {MODULE_UART8, "/soc/aips-bus@02200000/serial@02288000", 0x440, 26}, + {MODULE_PWM5, "/soc/aips-bus@02000000/pwm@020f0000", 0x440, 27}, + {MODULE_PWM6, "/soc/aips-bus@02000000/pwm@020f4000", 0x440, 27}, + {MODULE_PWM7, "/soc/aips-bus@02000000/pwm@020f8000", 0x440, 27}, + {MODULE_PWM8, "/soc/aips-bus@02000000/pwm@020fc000", 0x440, 27}, + {MODULE_ECSPI3, "/soc/aips-bus@02000000/spba-bus@02000000/ecspi@02010000", 0x440, 28}, + {MODULE_ECSPI4, "/soc/aips-bus@02000000/spba-bus@02000000/ecspi@02014000", 0x440, 28}, + {MODULE_I2C3, "/soc/aips-bus@02100000/i2c@021a8000", 0x440, 29}, + {MODULE_I2C4, "/soc/aips-bus@02100000/i2c@021f8000", 0x440, 29}, + {MODULE_GPT2, "/soc/aips-bus@02000000/gpt@020e8000", 0x440, 30}, + {MODULE_EPIT2, "/soc/aips-bus@02000000/epit@020d4000", 0x440, 31}, +#elif defined(CONFIG_MX6UL) + {MODULE_TSC, "/soc/aips-bus@2000000/tsc@2040000", 0x430, 22}, + {MODULE_ADC2, "/soc/aips-bus@2100000/adc@219c000", 0x430, 23}, + {MODULE_SIM1, "/soc/aips-bus@2100000/sim@218c000", 0x430, 24}, + {MODULE_SIM2, "/soc/aips-bus@2100000/sim@21b4000", 0x430, 25}, + {MODULE_FLEXCAN1, "/soc/aips-bus@2000000/can@2090000", 0x430, 26}, + {MODULE_FLEXCAN2, "/soc/aips-bus@2000000/can@2094000", 0x430, 27}, + {MODULE_SPDIF, "/soc/aips-bus@2000000/spba-bus@2000000/spdif@2004000", 0x440, 2}, + {MODULE_EIM, "/soc/aips-bus@2100000/weim@21b8000", 0x440, 3}, + {MODULE_SD1, "/soc/aips-bus@2100000/usdhc@2190000", 0x440, 4}, + {MODULE_SD2, "/soc/aips-bus@2100000/usdhc@2194000", 0x440, 5}, + {MODULE_QSPI1, "/soc/aips-bus@2100000/qspi@21e0000", 0x440, 6}, + {MODULE_GPMI, "/soc/gpmi-nand@1806000", 0x440, 7}, + {MODULE_APBHDMA, "/soc/dma-apbh@1804000", 0x440, 7}, + {MODULE_LCDIF, "/soc/aips-bus@2100000/lcdif@21c8000", 0x440, 8}, + {MODULE_PXP, "/soc/aips-bus@2100000/pxp@21cc000", 0x440, 9}, + {MODULE_CSI, "/soc/aips-bus@2100000/csi@21c4000", 0x440, 10}, + {MODULE_ADC1, "/soc/aips-bus@2100000/adc@2198000", 0x440, 11}, + {MODULE_ENET1, "/soc/aips-bus@2100000/ethernet@2188000", 0x440, 12}, + {MODULE_ENET2, "/soc/aips-bus@2000000/ethernet@20b4000", 0x440, 13}, + {MODULE_CAAM, "/soc/aips-bus@2100000/caam@2140000", 0x440, 14}, + {MODULE_USB_OTG2, "/soc/aips-bus@2100000/usb@2184200", 0x440, 15}, + {MODULE_SAI2, "/soc/aips-bus@2000000/spba-bus@2000000/sai@202c000", 0x440, 24}, + {MODULE_SAI3, "/soc/aips-bus@2000000/spba-bus@2000000/sai@2030000", 0x440, 24}, + {MODULE_BEE, "/soc/aips-bus@2000000/bee@2044000", 0x440, 25}, + {MODULE_UART5, "/soc/aips-bus@2100000/serial@21f4000", 0x440, 26}, + {MODULE_UART6, "/soc/aips-bus@2100000/serial@21fc000", 0x440, 26}, + {MODULE_UART7, "/soc/aips-bus@2000000/spba-bus@2000000/serial@2018000", 0x440, 26}, + {MODULE_UART8, "/soc/aips-bus@2000000/spba-bus@2000000/serial@2024000", 0x440, 26}, + {MODULE_PWM5, "/soc/aips-bus@2000000/pwm@20f0000", 0x440, 27}, + {MODULE_PWM6, "/soc/aips-bus@2000000/pwm@20f4000", 0x440, 27}, + {MODULE_PWM7, "/soc/aips-bus@2000000/pwm@20f8000", 0x440, 27}, + {MODULE_PWM8, "/soc/aips-bus@2000000/pwm@20fc000", 0x440, 27}, + {MODULE_ECSPI3, "/soc/aips-bus@2000000/spba-bus@2000000/ecspi@2010000", 0x440, 28}, + {MODULE_ECSPI4, "/soc/aips-bus@2000000/spba-bus@2000000/ecspi@2014000", 0x440, 28}, + {MODULE_I2C3, "/soc/aips-bus@2100000/i2c@21a8000", 0x440, 29}, + {MODULE_I2C4, "/soc/aips-bus@2100000/i2c@21f8000", 0x440, 29}, + {MODULE_GPT2, "/soc/aips-bus@2000000/gpt@20e8000", 0x440, 30}, + {MODULE_EPIT2, "/soc/aips-bus@2000000/epit@20d4000", 0x440, 31}, + /* Paths for older imx tree: */ + {MODULE_TSC, "/soc/aips-bus@02000000/tsc@02040000", 0x430, 22}, + {MODULE_ADC2, "/soc/aips-bus@02100000/adc@0219c000", 0x430, 23}, + {MODULE_SIM1, "/soc/aips-bus@02100000/sim@0218c000", 0x430, 24}, + {MODULE_SIM2, "/soc/aips-bus@02100000/sim@021b4000", 0x430, 25}, + {MODULE_FLEXCAN1, "/soc/aips-bus@02000000/can@02090000", 0x430, 26}, + {MODULE_FLEXCAN2, "/soc/aips-bus@02000000/can@02094000", 0x430, 27}, + {MODULE_SPDIF, "/soc/aips-bus@02000000/spba-bus@02000000/spdif@02004000", 0x440, 2}, + {MODULE_EIM, "/soc/aips-bus@02100000/weim@021b8000", 0x440, 3}, + {MODULE_SD1, "/soc/aips-bus@02100000/usdhc@02190000", 0x440, 4}, + {MODULE_SD2, "/soc/aips-bus@02100000/usdhc@02194000", 0x440, 5}, + {MODULE_QSPI1, "/soc/aips-bus@02100000/qspi@021e0000", 0x440, 6}, + {MODULE_GPMI, "/soc/gpmi-nand@01806000", 0x440, 7}, + {MODULE_APBHDMA, "/soc/dma-apbh@01804000", 0x440, 7}, + {MODULE_LCDIF, "/soc/aips-bus@02100000/lcdif@021c8000", 0x440, 8}, + {MODULE_PXP, "/soc/aips-bus@02100000/pxp@021cc000", 0x440, 9}, + {MODULE_CSI, "/soc/aips-bus@02100000/csi@021c4000", 0x440, 10}, + {MODULE_ADC1, "/soc/aips-bus@02100000/adc@02198000", 0x440, 11}, + {MODULE_ENET1, "/soc/aips-bus@02100000/ethernet@02188000", 0x440, 12}, + {MODULE_ENET2, "/soc/aips-bus@02000000/ethernet@020b4000", 0x440, 13}, + {MODULE_CAAM, "/soc/aips-bus@02100000/caam@2140000", 0x440, 14}, + {MODULE_USB_OTG2, "/soc/aips-bus@02100000/usb@02184200", 0x440, 15}, + {MODULE_SAI2, "/soc/aips-bus@02000000/spba-bus@02000000/sai@0202c000", 0x440, 24}, + {MODULE_SAI3, "/soc/aips-bus@02000000/spba-bus@02000000/sai@02030000", 0x440, 24}, + {MODULE_BEE, "/soc/aips-bus@02000000/bee@02044000", 0x440, 25}, + {MODULE_UART5, "/soc/aips-bus@02100000/serial@021f4000", 0x440, 26}, + {MODULE_UART6, "/soc/aips-bus@02100000/serial@021fc000", 0x440, 26}, + {MODULE_UART7, "/soc/aips-bus@02000000/spba-bus@02000000/serial@02018000", 0x440, 26}, + {MODULE_UART8, "/soc/aips-bus@02000000/spba-bus@02000000/serial@02024000", 0x440, 26}, + {MODULE_PWM5, "/soc/aips-bus@02000000/pwm@020f0000", 0x440, 27}, + {MODULE_PWM6, "/soc/aips-bus@02000000/pwm@020f4000", 0x440, 27}, + {MODULE_PWM7, "/soc/aips-bus@02000000/pwm@020f8000", 0x440, 27}, + {MODULE_PWM8, "/soc/aips-bus@02000000/pwm@020fc000", 0x440, 27}, + {MODULE_ECSPI3, "/soc/aips-bus@02000000/spba-bus@02000000/ecspi@02010000", 0x440, 28}, + {MODULE_ECSPI4, "/soc/aips-bus@02000000/spba-bus@02000000/ecspi@02014000", 0x440, 28}, + {MODULE_I2C3, "/soc/aips-bus@02100000/i2c@021a8000", 0x440, 29}, + {MODULE_I2C4, "/soc/aips-bus@02100000/i2c@021f8000", 0x440, 29}, + {MODULE_GPT2, "/soc/aips-bus@02000000/gpt@020e8000", 0x440, 30}, + {MODULE_EPIT2, "/soc/aips-bus@02000000/epit@020d4000", 0x440, 31}, +#endif +}; + +u32 check_module_fused(enum fuse_module_type module) +{ + u32 i, reg; + + for (i = 0; i < ARRAY_SIZE(mx6_fuse_descs); i++) { + if (mx6_fuse_descs[i].module == module) { + reg = readl(OCOTP_BASE_ADDR + + mx6_fuse_descs[i].fuse_word_offset); + if (reg & BIT(mx6_fuse_descs[i].fuse_bit_offset)) + return 1; /* disabled */ + else + return 0; /* enabled */ + } + } + + return 0; /* Not has a fuse, always enabled */ +} + +#ifdef CONFIG_OF_SYSTEM_SETUP +int ft_system_setup(void *blob, struct bd_info *bd) +{ + const char *status = "disabled"; + u32 i, reg; + int rc, off; + + for (i = 0; i < ARRAY_SIZE(mx6_fuse_descs); i++) { + reg = readl(OCOTP_BASE_ADDR + + mx6_fuse_descs[i].fuse_word_offset); + if (reg & BIT(mx6_fuse_descs[i].fuse_bit_offset)) { + off = fdt_path_offset(blob, + mx6_fuse_descs[i].node_path); + + if (off < 0) + continue; /* Not found, skip it */ +add_status: + rc = fdt_setprop(blob, nodeoff, "status", status, + strlen(status) + 1); + if (rc) { + if (rc == -FDT_ERR_NOSPACE) { + rc = fdt_increase_size(blob, 512); + if (!rc) + goto add_status; + } + printf("Unable to update property %s:%s, err=%s\n", mx6_fuse_descs[i].node_path, "status", fdt_strerror(rc)); + } else { + printf("Modify %s disabled\n", mx6_fuse_descs[i].node_path); + } + } + } + + return 0; +} +#endif + +u32 esdhc_fused(ulong base_addr) +{ + switch (base_addr) { + case USDHC1_BASE_ADDR: + return check_module_fused(MODULE_SD1); + case USDHC2_BASE_ADDR: + return check_module_fused(MODULE_SD2); +#ifdef USDHC3_BASE_ADDR + case USDHC3_BASE_ADDR: + return check_module_fused(MODULE_SD3); +#endif +#ifdef USDHC4_BASE_ADDR + case USDHC4_BASE_ADDR: + return check_module_fused(MODULE_SD4); +#endif + default: + return 0; + } +} + +u32 ecspi_fused(ulong base_addr) +{ + switch (base_addr) { + case ECSPI1_BASE_ADDR: + return check_module_fused(MODULE_ECSPI1); + case ECSPI2_BASE_ADDR: + return check_module_fused(MODULE_ECSPI2); + case ECSPI3_BASE_ADDR: + return check_module_fused(MODULE_ECSPI3); + case ECSPI4_BASE_ADDR: + return check_module_fused(MODULE_ECSPI4); +#ifdef ECSPI5_BASE_ADDR + case ECSPI5_BASE_ADDR: + return check_module_fused(MODULE_ECSPI5); +#endif + default: + return 0; + } +} + +u32 usb_fused(ulong base_addr) +{ + int i = (base_addr - USB_BASE_ADDR) / 0x200; + + return check_module_fused(MODULE_USB_OTG1 + i); +} + +u32 qspi_fused(ulong base_addr) +{ + switch (base_addr) { +#ifdef QSPI1_BASE_ADDR + case QSPI1_BASE_ADDR: + return check_module_fused(MODULE_QSPI1); +#endif + +#ifdef QSPI2_BASE_ADDR + case QSPI2_BASE_ADDR: + return check_module_fused(MODULE_QSPI2); +#endif + default: + return 0; + } +} + +u32 i2c_fused(ulong base_addr) +{ + switch (base_addr) { + case I2C1_BASE_ADDR: + return check_module_fused(MODULE_I2C1); + case I2C2_BASE_ADDR: + return check_module_fused(MODULE_I2C2); + case I2C3_BASE_ADDR: + return check_module_fused(MODULE_I2C3); +#ifdef I2C4_BASE_ADDR + case I2C4_BASE_ADDR: + return check_module_fused(MODULE_I2C4); +#endif + } + + return 0; +} + +u32 enet_fused(ulong base_addr) +{ + switch (base_addr) { + case ENET_BASE_ADDR: + return check_module_fused(MODULE_ENET1); +#ifdef ENET2_BASE_ADDR + case ENET2_BASE_ADDR: + return check_module_fused(MODULE_ENET2); +#endif + default: + return 0; + } +} diff --git a/roms/u-boot/arch/arm/mach-imx/mx6/mp.c b/roms/u-boot/arch/arm/mach-imx/mx6/mp.c new file mode 100644 index 000000000..2fdf070a0 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/mx6/mp.c @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2014 + * Gabriel Huau <contact@huau-gabriel.fr> + * + * (C) Copyright 2009 Freescale Semiconductor, Inc. + */ + +#include <common.h> +#include <cpu_func.h> +#include <asm/io.h> +#include <linux/errno.h> +#include <asm/arch/sys_proto.h> +#include <asm/arch/imx-regs.h> + +#define MAX_CPUS 4 +static struct src *src = (struct src *)SRC_BASE_ADDR; + +static uint32_t cpu_reset_mask[MAX_CPUS] = { + 0, /* We don't really want to modify the cpu0 */ + SRC_SCR_CORE_1_RESET_MASK, + SRC_SCR_CORE_2_RESET_MASK, + SRC_SCR_CORE_3_RESET_MASK +}; + +static uint32_t cpu_ctrl_mask[MAX_CPUS] = { + 0, /* We don't really want to modify the cpu0 */ + SRC_SCR_CORE_1_ENABLE_MASK, + SRC_SCR_CORE_2_ENABLE_MASK, + SRC_SCR_CORE_3_ENABLE_MASK +}; + +int cpu_reset(u32 nr) +{ + /* Software reset of the CPU N */ + src->scr |= cpu_reset_mask[nr]; + return 0; +} + +int cpu_status(u32 nr) +{ + printf("core %d => %d\n", nr, !!(src->scr & cpu_ctrl_mask[nr])); + return 0; +} + +int cpu_release(u32 nr, int argc, char *const argv[]) +{ + uint32_t boot_addr; + + boot_addr = simple_strtoul(argv[0], NULL, 16); + + switch (nr) { + case 1: + src->gpr3 = boot_addr; + break; + case 2: + src->gpr5 = boot_addr; + break; + case 3: + src->gpr7 = boot_addr; + break; + default: + return 1; + } + + /* CPU N is ready to start */ + src->scr |= cpu_ctrl_mask[nr]; + + return 0; +} + +int is_core_valid(unsigned int core) +{ + uint32_t nr_cores = get_nr_cpus(); + + if (core > nr_cores) + return 0; + + return 1; +} + +int cpu_disable(u32 nr) +{ + /* Disable the CPU N */ + src->scr &= ~cpu_ctrl_mask[nr]; + return 0; +} diff --git a/roms/u-boot/arch/arm/mach-imx/mx6/opos6ul.c b/roms/u-boot/arch/arm/mach-imx/mx6/opos6ul.c new file mode 100644 index 000000000..e9d78740a --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/mx6/opos6ul.c @@ -0,0 +1,213 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2018 Armadeus Systems + */ + +#include <init.h> +#include <asm/arch/clock.h> +#include <asm/arch/crm_regs.h> +#include <asm/arch/imx-regs.h> +#include <asm/arch/sys_proto.h> +#include <asm/global_data.h> +#include <asm/io.h> +#include <common.h> +#include <env.h> + +DECLARE_GLOBAL_DATA_PTR; + +#ifdef CONFIG_FEC_MXC +#include <miiphy.h> + +int board_phy_config(struct phy_device *phydev) +{ + phy_write(phydev, MDIO_DEVAD_NONE, 0x1f, 0x8190); + + if (phydev->drv->config) + phydev->drv->config(phydev); + + return 0; +} + +static int setup_fec(void) +{ + struct iomuxc *const iomuxc_regs = (struct iomuxc *)IOMUXC_BASE_ADDR; + + /* Use 50M anatop loopback REF_CLK1 for ENET1, + * clear gpr1[13], set gpr1[17] */ + clrsetbits_le32(&iomuxc_regs->gpr[1], IOMUX_GPR1_FEC1_MASK, + IOMUX_GPR1_FEC1_CLOCK_MUX1_SEL_MASK); + + return enable_fec_anatop_clock(0, ENET_50MHZ); +} +#endif /* CONFIG_FEC_MXC */ + +int board_init(void) +{ + /* Address of boot parameters */ + gd->bd->bi_boot_params = CONFIG_SYS_SDRAM_BASE + 0x100; + +#ifdef CONFIG_FEC_MXC + setup_fec(); +#endif + + return 0; +} + +int __weak opos6ul_board_late_init(void) +{ + return 0; +} + +int board_late_init(void) +{ + struct src *psrc = (struct src *)SRC_BASE_ADDR; + unsigned reg = readl(&psrc->sbmr2); + + /* In bootstrap don't use the env vars */ + if (((reg & 0x3000000) >> 24) == 0x1) { + env_set_default(NULL, 0); + env_set("preboot", ""); + } + + return opos6ul_board_late_init(); +} + +int dram_init(void) +{ + gd->ram_size = imx_ddr_size(); + + return 0; +} + +#ifdef CONFIG_SPL_BUILD +#include <asm/arch/mx6-ddr.h> +#include <linux/libfdt.h> +#include <spl.h> + +static struct mx6ul_iomux_grp_regs mx6_grp_ioregs = { + .grp_addds = 0x00000030, + .grp_ddrmode_ctl = 0x00020000, + .grp_b0ds = 0x00000030, + .grp_ctlds = 0x00000030, + .grp_b1ds = 0x00000030, + .grp_ddrpke = 0x00000000, + .grp_ddrmode = 0x00020000, + .grp_ddr_type = 0x000c0000, +}; + +static struct mx6ul_iomux_ddr_regs mx6_ddr_ioregs = { + .dram_dqm0 = 0x00000030, + .dram_dqm1 = 0x00000030, + .dram_ras = 0x00000030, + .dram_cas = 0x00000030, + .dram_odt0 = 0x00000030, + .dram_odt1 = 0x00000030, + .dram_sdba2 = 0x00000000, + .dram_sdclk_0 = 0x00000008, + .dram_sdqs0 = 0x00000038, + .dram_sdqs1 = 0x00000030, + .dram_reset = 0x00000030, +}; + +static struct mx6_mmdc_calibration mx6_mmcd_calib = { + .p0_mpwldectrl0 = 0x00070007, + .p0_mpdgctrl0 = 0x41490145, + .p0_mprddlctl = 0x40404546, + .p0_mpwrdlctl = 0x4040524D, +}; + +struct mx6_ddr_sysinfo ddr_sysinfo = { + .dsize = 0, + .cs_density = 20, + .ncs = 1, + .cs1_mirror = 0, + .rtt_wr = 2, + .rtt_nom = 1, /* RTT_Nom = RZQ/2 */ + .walat = 1, /* Write additional latency */ + .ralat = 5, /* Read additional latency */ + .mif3_mode = 3, /* Command prediction working mode */ + .bi_on = 1, /* Bank interleaving enabled */ + .sde_to_rst = 0x10, /* 14 cycles, 200us (JEDEC default) */ + .rst_to_cke = 0x23, /* 33 cycles, 500us (JEDEC default) */ + .ddr_type = DDR_TYPE_DDR3, + .refsel = 1, /* Refresh cycles at 32KHz */ + .refr = 7, /* 8 refreshes commands per refresh cycle */ +}; + +static struct mx6_ddr3_cfg mem_ddr = { + .mem_speed = 800, + .density = 2, + .width = 16, + .banks = 8, + .rowaddr = 14, + .coladdr = 10, + .pagesz = 2, + .trcd = 1500, + .trcmin = 5250, + .trasmin = 3750, +}; + +void board_boot_order(u32 *spl_boot_list) +{ + unsigned int bmode = readl(&src_base->sbmr2); + + if (((bmode >> 24) & 0x03) == 0x01) /* Serial Downloader */ + spl_boot_list[0] = BOOT_DEVICE_UART; + else + spl_boot_list[0] = spl_boot_device(); +} + +static void ccgr_init(void) +{ + struct mxc_ccm_reg *ccm = (struct mxc_ccm_reg *)CCM_BASE_ADDR; + + writel(0xFFFFFFFF, &ccm->CCGR0); + writel(0xFFFFFFFF, &ccm->CCGR1); + writel(0xFFFFFFFF, &ccm->CCGR2); + writel(0xFFFFFFFF, &ccm->CCGR3); + writel(0xFFFFFFFF, &ccm->CCGR4); + writel(0xFFFFFFFF, &ccm->CCGR5); + writel(0xFFFFFFFF, &ccm->CCGR6); + writel(0xFFFFFFFF, &ccm->CCGR7); +} + +static void spl_dram_init(void) +{ + struct ocotp_regs *ocotp = (struct ocotp_regs *)OCOTP_BASE_ADDR; + struct fuse_bank *bank = &ocotp->bank[4]; + struct fuse_bank4_regs *fuse = + (struct fuse_bank4_regs *)bank->fuse_regs; + int reg = readl(&fuse->gp1); + + /* 512MB of RAM */ + if (reg & 0x1) { + mem_ddr.density = 4; + mem_ddr.rowaddr = 15; + mem_ddr.trcd = 1375; + mem_ddr.trcmin = 4875; + mem_ddr.trasmin = 3500; + } + + mx6ul_dram_iocfg(mem_ddr.width, &mx6_ddr_ioregs, &mx6_grp_ioregs); + mx6_dram_cfg(&ddr_sysinfo, &mx6_mmcd_calib, &mem_ddr); +} + +void spl_board_init(void) +{ + preloader_console_init(); +} + +void board_init_f(ulong dummy) +{ + ccgr_init(); + + /* setup AIPS and disable watchdog */ + arch_cpu_init(); + + /* setup GP timer */ + timer_init(); + + /* DDR initialization */ + spl_dram_init(); +} +#endif /* CONFIG_SPL_BUILD */ diff --git a/roms/u-boot/arch/arm/mach-imx/mx6/soc.c b/roms/u-boot/arch/arm/mach-imx/mx6/soc.c new file mode 100644 index 000000000..aacfc854a --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/mx6/soc.c @@ -0,0 +1,772 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2007 + * Sascha Hauer, Pengutronix + * + * (C) Copyright 2009 Freescale Semiconductor, Inc. + */ + +#include <common.h> +#include <env.h> +#include <init.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <asm/io.h> +#include <asm/arch/imx-regs.h> +#include <asm/arch/clock.h> +#include <asm/arch/sys_proto.h> +#include <asm/bootm.h> +#include <asm/mach-imx/boot_mode.h> +#include <asm/mach-imx/dma.h> +#include <asm/mach-imx/hab.h> +#include <stdbool.h> +#include <asm/arch/mxc_hdmi.h> +#include <asm/arch/crm_regs.h> +#include <dm.h> +#include <fsl_sec.h> +#include <imx_thermal.h> +#include <mmc.h> + +#define has_err007805() \ + (is_mx6sl() || is_mx6dl() || is_mx6solo() || is_mx6ull()) + +struct scu_regs { + u32 ctrl; + u32 config; + u32 status; + u32 invalidate; + u32 fpga_rev; +}; + +#if !defined(CONFIG_SPL_BUILD) && defined(CONFIG_IMX_THERMAL) +static const struct imx_thermal_plat imx6_thermal_plat = { + .regs = (void *)ANATOP_BASE_ADDR, + .fuse_bank = 1, + .fuse_word = 6, +}; + +U_BOOT_DRVINFO(imx6_thermal) = { + .name = "imx_thermal", + .plat = &imx6_thermal_plat, +}; +#endif + +#if defined(CONFIG_IMX_HAB) +struct imx_sec_config_fuse_t const imx_sec_config_fuse = { + .bank = 0, + .word = 6, +}; +#endif + +u32 get_nr_cpus(void) +{ + struct scu_regs *scu = (struct scu_regs *)SCU_BASE_ADDR; + return readl(&scu->config) & 3; +} + +u32 get_cpu_rev(void) +{ + struct anatop_regs *anatop = (struct anatop_regs *)ANATOP_BASE_ADDR; + u32 reg = readl(&anatop->digprog_sololite); + u32 type = ((reg >> 16) & 0xff); + u32 major, cfg = 0; + + if (type != MXC_CPU_MX6SL) { + reg = readl(&anatop->digprog); + struct scu_regs *scu = (struct scu_regs *)SCU_BASE_ADDR; + cfg = readl(&scu->config) & 3; + type = ((reg >> 16) & 0xff); + if (type == MXC_CPU_MX6DL) { + if (!cfg) + type = MXC_CPU_MX6SOLO; + } + + if (type == MXC_CPU_MX6Q) { + if (cfg == 1) + type = MXC_CPU_MX6D; + } + + if (type == MXC_CPU_MX6ULL) { + if (readl(SRC_BASE_ADDR + 0x1c) & (1 << 6)) + type = MXC_CPU_MX6ULZ; + } + } + major = ((reg >> 8) & 0xff); + if ((major >= 1) && + ((type == MXC_CPU_MX6Q) || (type == MXC_CPU_MX6D))) { + major--; + type = MXC_CPU_MX6QP; + if (cfg == 1) + type = MXC_CPU_MX6DP; + } + reg &= 0xff; /* mx6 silicon revision */ + + /* For 6DQ, the value 0x00630005 is Silicon revision 1.3*/ + if (((type == MXC_CPU_MX6Q) || (type == MXC_CPU_MX6D)) && (reg == 0x5)) + reg = 0x3; + + return (type << 12) | (reg + (0x10 * (major + 1))); +} + +/* + * OCOTP_CFG3[17:16] (see Fusemap Description Table offset 0x440) + * defines a 2-bit SPEED_GRADING + */ +#define OCOTP_CFG3_SPEED_SHIFT 16 +#define OCOTP_CFG3_SPEED_800MHZ 0 +#define OCOTP_CFG3_SPEED_850MHZ 1 +#define OCOTP_CFG3_SPEED_1GHZ 2 +#define OCOTP_CFG3_SPEED_1P2GHZ 3 + +/* + * For i.MX6UL + */ +#define OCOTP_CFG3_SPEED_528MHZ 1 +#define OCOTP_CFG3_SPEED_696MHZ 2 + +/* + * For i.MX6ULL + */ +#define OCOTP_CFG3_SPEED_792MHZ 2 +#define OCOTP_CFG3_SPEED_900MHZ 3 + +u32 get_cpu_speed_grade_hz(void) +{ + struct ocotp_regs *ocotp = (struct ocotp_regs *)OCOTP_BASE_ADDR; + struct fuse_bank *bank = &ocotp->bank[0]; + struct fuse_bank0_regs *fuse = + (struct fuse_bank0_regs *)bank->fuse_regs; + uint32_t val; + + val = readl(&fuse->cfg3); + val >>= OCOTP_CFG3_SPEED_SHIFT; + val &= 0x3; + + if (is_mx6ul()) { + if (val == OCOTP_CFG3_SPEED_528MHZ) + return 528000000; + else if (val == OCOTP_CFG3_SPEED_696MHZ) + return 696000000; + else + return 0; + } + + if (is_mx6ull()) { + if (val == OCOTP_CFG3_SPEED_528MHZ) + return 528000000; + else if (val == OCOTP_CFG3_SPEED_792MHZ) + return 792000000; + else if (val == OCOTP_CFG3_SPEED_900MHZ) + return 900000000; + else + return 0; + } + + switch (val) { + /* Valid for IMX6DQ */ + case OCOTP_CFG3_SPEED_1P2GHZ: + if (is_mx6dq() || is_mx6dqp()) + return 1200000000; + /* Valid for IMX6SX/IMX6SDL/IMX6DQ */ + case OCOTP_CFG3_SPEED_1GHZ: + return 996000000; + /* Valid for IMX6DQ */ + case OCOTP_CFG3_SPEED_850MHZ: + if (is_mx6dq() || is_mx6dqp()) + return 852000000; + /* Valid for IMX6SX/IMX6SDL/IMX6DQ */ + case OCOTP_CFG3_SPEED_800MHZ: + return 792000000; + } + return 0; +} + +/* + * OCOTP_MEM0[7:6] (see Fusemap Description Table offset 0x480) + * defines a 2-bit Temperature Grade + * + * return temperature grade and min/max temperature in Celsius + */ +#define OCOTP_MEM0_TEMP_SHIFT 6 + +u32 get_cpu_temp_grade(int *minc, int *maxc) +{ + struct ocotp_regs *ocotp = (struct ocotp_regs *)OCOTP_BASE_ADDR; + struct fuse_bank *bank = &ocotp->bank[1]; + struct fuse_bank1_regs *fuse = + (struct fuse_bank1_regs *)bank->fuse_regs; + uint32_t val; + + val = readl(&fuse->mem0); + val >>= OCOTP_MEM0_TEMP_SHIFT; + val &= 0x3; + + if (minc && maxc) { + if (val == TEMP_AUTOMOTIVE) { + *minc = -40; + *maxc = 125; + } else if (val == TEMP_INDUSTRIAL) { + *minc = -40; + *maxc = 105; + } else if (val == TEMP_EXTCOMMERCIAL) { + *minc = -20; + *maxc = 105; + } else { + *minc = 0; + *maxc = 95; + } + } + return val; +} + +#ifdef CONFIG_REVISION_TAG +u32 __weak get_board_rev(void) +{ + u32 cpurev = get_cpu_rev(); + u32 type = ((cpurev >> 12) & 0xff); + if (type == MXC_CPU_MX6SOLO) + cpurev = (MXC_CPU_MX6DL) << 12 | (cpurev & 0xFFF); + + if (type == MXC_CPU_MX6D) + cpurev = (MXC_CPU_MX6Q) << 12 | (cpurev & 0xFFF); + + return cpurev; +} +#endif + +static void clear_ldo_ramp(void) +{ + struct anatop_regs *anatop = (struct anatop_regs *)ANATOP_BASE_ADDR; + int reg; + + /* ROM may modify LDO ramp up time according to fuse setting, so in + * order to be in the safe side we neeed to reset these settings to + * match the reset value: 0'b00 + */ + reg = readl(&anatop->ana_misc2); + reg &= ~(0x3f << 24); + writel(reg, &anatop->ana_misc2); +} + +/* + * Set the PMU_REG_CORE register + * + * Set LDO_SOC/PU/ARM regulators to the specified millivolt level. + * Possible values are from 0.725V to 1.450V in steps of + * 0.025V (25mV). + */ +int set_ldo_voltage(enum ldo_reg ldo, u32 mv) +{ + struct anatop_regs *anatop = (struct anatop_regs *)ANATOP_BASE_ADDR; + u32 val, step, old, reg = readl(&anatop->reg_core); + u8 shift; + + /* No LDO_SOC/PU/ARM */ + if (is_mx6sll()) + return 0; + + if (mv < 725) + val = 0x00; /* Power gated off */ + else if (mv > 1450) + val = 0x1F; /* Power FET switched full on. No regulation */ + else + val = (mv - 700) / 25; + + clear_ldo_ramp(); + + switch (ldo) { + case LDO_SOC: + shift = 18; + break; + case LDO_PU: + shift = 9; + break; + case LDO_ARM: + shift = 0; + break; + default: + return -EINVAL; + } + + old = (reg & (0x1F << shift)) >> shift; + step = abs(val - old); + if (step == 0) + return 0; + + reg = (reg & ~(0x1F << shift)) | (val << shift); + writel(reg, &anatop->reg_core); + + /* + * The LDO ramp-up is based on 64 clock cycles of 24 MHz = 2.6 us per + * step + */ + udelay(3 * step); + + return 0; +} + +static void set_ahb_rate(u32 val) +{ + struct mxc_ccm_reg *mxc_ccm = (struct mxc_ccm_reg *)CCM_BASE_ADDR; + u32 reg, div; + + div = get_periph_clk() / val - 1; + reg = readl(&mxc_ccm->cbcdr); + + writel((reg & (~MXC_CCM_CBCDR_AHB_PODF_MASK)) | + (div << MXC_CCM_CBCDR_AHB_PODF_OFFSET), &mxc_ccm->cbcdr); +} + +static void clear_mmdc_ch_mask(void) +{ + struct mxc_ccm_reg *mxc_ccm = (struct mxc_ccm_reg *)CCM_BASE_ADDR; + u32 reg; + reg = readl(&mxc_ccm->ccdr); + + /* Clear MMDC channel mask */ + if (is_mx6sx() || is_mx6ul() || is_mx6ull() || is_mx6sl() || is_mx6sll()) + reg &= ~(MXC_CCM_CCDR_MMDC_CH1_HS_MASK); + else + reg &= ~(MXC_CCM_CCDR_MMDC_CH1_HS_MASK | MXC_CCM_CCDR_MMDC_CH0_HS_MASK); + writel(reg, &mxc_ccm->ccdr); +} + +#define OCOTP_MEM0_REFTOP_TRIM_SHIFT 8 + +static void init_bandgap(void) +{ + struct anatop_regs *anatop = (struct anatop_regs *)ANATOP_BASE_ADDR; + struct ocotp_regs *ocotp = (struct ocotp_regs *)OCOTP_BASE_ADDR; + struct fuse_bank *bank = &ocotp->bank[1]; + struct fuse_bank1_regs *fuse = + (struct fuse_bank1_regs *)bank->fuse_regs; + uint32_t val; + + /* + * Ensure the bandgap has stabilized. + */ + while (!(readl(&anatop->ana_misc0) & 0x80)) + ; + /* + * For best noise performance of the analog blocks using the + * outputs of the bandgap, the reftop_selfbiasoff bit should + * be set. + */ + writel(BM_ANADIG_ANA_MISC0_REFTOP_SELBIASOFF, &anatop->ana_misc0_set); + /* + * On i.MX6ULL,we need to set VBGADJ bits according to the + * REFTOP_TRIM[3:0] in fuse table + * 000 - set REFTOP_VBGADJ[2:0] to 3b'110, + * 110 - set REFTOP_VBGADJ[2:0] to 3b'000, + * 001 - set REFTOP_VBGADJ[2:0] to 3b'001, + * 010 - set REFTOP_VBGADJ[2:0] to 3b'010, + * 011 - set REFTOP_VBGADJ[2:0] to 3b'011, + * 100 - set REFTOP_VBGADJ[2:0] to 3b'100, + * 101 - set REFTOP_VBGADJ[2:0] to 3b'101, + * 111 - set REFTOP_VBGADJ[2:0] to 3b'111, + */ + if (is_mx6ull()) { + val = readl(&fuse->mem0); + val >>= OCOTP_MEM0_REFTOP_TRIM_SHIFT; + val &= 0x7; + + writel(val << BM_ANADIG_ANA_MISC0_REFTOP_VBGADJ_SHIFT, + &anatop->ana_misc0_set); + } +} + +#if defined(CONFIG_MX6Q) || defined(CONFIG_MX6QDL) +static void noc_setup(void) +{ + enable_ipu_clock(); + + writel(0x80000201, 0xbb0608); + /* Bypass IPU1 QoS generator */ + writel(0x00000002, 0x00bb048c); + /* Bypass IPU2 QoS generator */ + writel(0x00000002, 0x00bb050c); + /* Bandwidth THR for of PRE0 */ + writel(0x00000200, 0x00bb0690); + /* Bandwidth THR for of PRE1 */ + writel(0x00000200, 0x00bb0710); + /* Bandwidth THR for of PRE2 */ + writel(0x00000200, 0x00bb0790); + /* Bandwidth THR for of PRE3 */ + writel(0x00000200, 0x00bb0810); + /* Saturation THR for of PRE0 */ + writel(0x00000010, 0x00bb0694); + /* Saturation THR for of PRE1 */ + writel(0x00000010, 0x00bb0714); + /* Saturation THR for of PRE2 */ + writel(0x00000010, 0x00bb0794); + /* Saturation THR for of PRE */ + writel(0x00000010, 0x00bb0814); + + disable_ipu_clock(); +} +#endif + +int arch_cpu_init(void) +{ + struct mxc_ccm_reg *ccm = (struct mxc_ccm_reg *)CCM_BASE_ADDR; + + init_aips(); + + /* Need to clear MMDC_CHx_MASK to make warm reset work. */ + clear_mmdc_ch_mask(); + + /* + * Disable self-bias circuit in the analog bandap. + * The self-bias circuit is used by the bandgap during startup. + * This bit should be set after the bandgap has initialized. + */ + init_bandgap(); + + if (!is_mx6ul() && !is_mx6ull()) { + /* + * When low freq boot is enabled, ROM will not set AHB + * freq, so we need to ensure AHB freq is 132MHz in such + * scenario. + * + * To i.MX6UL, when power up, default ARM core and + * AHB rate is 396M and 132M. + */ + if (mxc_get_clock(MXC_ARM_CLK) == 396000000) + set_ahb_rate(132000000); + } + + if (is_mx6ul()) { + if (is_soc_rev(CHIP_REV_1_0) == 0) { + /* + * According to the design team's requirement on + * i.MX6UL,the PMIC_STBY_REQ PAD should be configured + * as open drain 100K (0x0000b8a0). + * Only exists on TO1.0 + */ + writel(0x0000b8a0, IOMUXC_BASE_ADDR + 0x29c); + } else { + /* + * From TO1.1, SNVS adds internal pull up control + * for POR_B, the register filed is GPBIT[1:0], + * after system boot up, it can be set to 2b'01 + * to disable internal pull up.It can save about + * 30uA power in SNVS mode. + */ + writel((readl(MX6UL_SNVS_LP_BASE_ADDR + 0x10) & + (~0x1400)) | 0x400, + MX6UL_SNVS_LP_BASE_ADDR + 0x10); + } + } + + if (is_mx6ull()) { + /* + * GPBIT[1:0] is suggested to set to 2'b11: + * 2'b00 : always PUP100K + * 2'b01 : PUP100K when PMIC_ON_REQ or SOC_NOT_FAIL + * 2'b10 : always disable PUP100K + * 2'b11 : PDN100K when SOC_FAIL, PUP100K when SOC_NOT_FAIL + * register offset is different from i.MX6UL, since + * i.MX6UL is fixed by ECO. + */ + writel(readl(MX6UL_SNVS_LP_BASE_ADDR) | + 0x3, MX6UL_SNVS_LP_BASE_ADDR); + } + + /* Set perclk to source from OSC 24MHz */ + if (has_err007805()) + setbits_le32(&ccm->cscmr1, MXC_CCM_CSCMR1_PER_CLK_SEL_MASK); + + imx_wdog_disable_powerdown(); /* Disable PDE bit of WMCR register */ + + if (is_mx6sx()) + setbits_le32(&ccm->cscdr1, MXC_CCM_CSCDR1_UART_CLK_SEL); + + init_src(); + +#if defined(CONFIG_MX6Q) || defined(CONFIG_MX6QDL) + if (is_mx6dqp()) + noc_setup(); +#endif + return 0; +} + +#ifdef CONFIG_ENV_IS_IN_MMC +__weak int board_mmc_get_env_dev(int devno) +{ + return CONFIG_SYS_MMC_ENV_DEV; +} + +static int mmc_get_boot_dev(void) +{ + struct src *src_regs = (struct src *)SRC_BASE_ADDR; + u32 soc_sbmr = readl(&src_regs->sbmr1); + u32 bootsel; + int devno; + + /* + * Refer to + * "i.MX 6Dual/6Quad Applications Processor Reference Manual" + * Chapter "8.5.3.1 Expansion Device eFUSE Configuration" + * i.MX6SL/SX/UL has same layout. + */ + bootsel = (soc_sbmr & 0x000000FF) >> 6; + + /* No boot from sd/mmc */ + if (bootsel != 1) + return -1; + + /* BOOT_CFG2[3] and BOOT_CFG2[4] */ + devno = (soc_sbmr & 0x00001800) >> 11; + + return devno; +} + +int mmc_get_env_dev(void) +{ + int devno = mmc_get_boot_dev(); + + /* If not boot from sd/mmc, use default value */ + if (devno < 0) + return CONFIG_SYS_MMC_ENV_DEV; + + return board_mmc_get_env_dev(devno); +} + +#ifdef CONFIG_SYS_MMC_ENV_PART +__weak int board_mmc_get_env_part(int devno) +{ + return CONFIG_SYS_MMC_ENV_PART; +} + +uint mmc_get_env_part(struct mmc *mmc) +{ + int devno = mmc_get_boot_dev(); + + /* If not boot from sd/mmc, use default value */ + if (devno < 0) + return CONFIG_SYS_MMC_ENV_PART; + + return board_mmc_get_env_part(devno); +} +#endif +#endif + +int board_postclk_init(void) +{ + /* NO LDO SOC on i.MX6SLL */ + if (is_mx6sll()) + return 0; + + set_ldo_voltage(LDO_SOC, 1175); /* Set VDDSOC to 1.175V */ + + return 0; +} + +#ifndef CONFIG_SPL_BUILD +/* + * cfg_val will be used for + * Boot_cfg4[7:0]:Boot_cfg3[7:0]:Boot_cfg2[7:0]:Boot_cfg1[7:0] + * After reset, if GPR10[28] is 1, ROM will use GPR9[25:0] + * instead of SBMR1 to determine the boot device. + */ +const struct boot_mode soc_boot_modes[] = { + {"normal", MAKE_CFGVAL(0x00, 0x00, 0x00, 0x00)}, + /* reserved value should start rom usb */ +#if defined(CONFIG_MX6UL) || defined(CONFIG_MX6ULL) + {"usb", MAKE_CFGVAL(0x20, 0x00, 0x00, 0x00)}, +#else + {"usb", MAKE_CFGVAL(0x10, 0x00, 0x00, 0x00)}, +#endif + {"sata", MAKE_CFGVAL(0x20, 0x00, 0x00, 0x00)}, + {"ecspi1:0", MAKE_CFGVAL(0x30, 0x00, 0x00, 0x08)}, + {"ecspi1:1", MAKE_CFGVAL(0x30, 0x00, 0x00, 0x18)}, + {"ecspi1:2", MAKE_CFGVAL(0x30, 0x00, 0x00, 0x28)}, + {"ecspi1:3", MAKE_CFGVAL(0x30, 0x00, 0x00, 0x38)}, + /* 4 bit bus width */ + {"esdhc1", MAKE_CFGVAL(0x40, 0x20, 0x00, 0x00)}, + {"esdhc2", MAKE_CFGVAL(0x40, 0x28, 0x00, 0x00)}, + {"esdhc3", MAKE_CFGVAL(0x40, 0x30, 0x00, 0x00)}, + {"esdhc4", MAKE_CFGVAL(0x40, 0x38, 0x00, 0x00)}, + {NULL, 0}, +}; +#endif + +void reset_misc(void) +{ +#ifndef CONFIG_SPL_BUILD +#if defined(CONFIG_VIDEO_MXS) && !defined(CONFIG_DM_VIDEO) + lcdif_power_down(); +#endif +#endif +} + +void s_init(void) +{ + struct anatop_regs *anatop = (struct anatop_regs *)ANATOP_BASE_ADDR; + struct mxc_ccm_reg *ccm = (struct mxc_ccm_reg *)CCM_BASE_ADDR; + u32 mask480; + u32 mask528; + u32 reg, periph1, periph2; + + if (is_mx6sx() || is_mx6ul() || is_mx6ull() || is_mx6sll()) + return; + + /* Due to hardware limitation, on MX6Q we need to gate/ungate all PFDs + * to make sure PFD is working right, otherwise, PFDs may + * not output clock after reset, MX6DL and MX6SL have added 396M pfd + * workaround in ROM code, as bus clock need it + */ + + mask480 = ANATOP_PFD_CLKGATE_MASK(0) | + ANATOP_PFD_CLKGATE_MASK(1) | + ANATOP_PFD_CLKGATE_MASK(2) | + ANATOP_PFD_CLKGATE_MASK(3); + mask528 = ANATOP_PFD_CLKGATE_MASK(1) | + ANATOP_PFD_CLKGATE_MASK(3); + + reg = readl(&ccm->cbcmr); + periph2 = ((reg & MXC_CCM_CBCMR_PRE_PERIPH2_CLK_SEL_MASK) + >> MXC_CCM_CBCMR_PRE_PERIPH2_CLK_SEL_OFFSET); + periph1 = ((reg & MXC_CCM_CBCMR_PRE_PERIPH_CLK_SEL_MASK) + >> MXC_CCM_CBCMR_PRE_PERIPH_CLK_SEL_OFFSET); + + /* Checking if PLL2 PFD0 or PLL2 PFD2 is using for periph clock */ + if ((periph2 != 0x2) && (periph1 != 0x2)) + mask528 |= ANATOP_PFD_CLKGATE_MASK(0); + + if ((periph2 != 0x1) && (periph1 != 0x1) && + (periph2 != 0x3) && (periph1 != 0x3)) + mask528 |= ANATOP_PFD_CLKGATE_MASK(2); + + writel(mask480, &anatop->pfd_480_set); + writel(mask528, &anatop->pfd_528_set); + writel(mask480, &anatop->pfd_480_clr); + writel(mask528, &anatop->pfd_528_clr); +} + +#ifdef CONFIG_IMX_HDMI +void imx_enable_hdmi_phy(void) +{ + struct hdmi_regs *hdmi = (struct hdmi_regs *)HDMI_ARB_BASE_ADDR; + u8 reg; + reg = readb(&hdmi->phy_conf0); + reg |= HDMI_PHY_CONF0_PDZ_MASK; + writeb(reg, &hdmi->phy_conf0); + udelay(3000); + reg |= HDMI_PHY_CONF0_ENTMDS_MASK; + writeb(reg, &hdmi->phy_conf0); + udelay(3000); + reg |= HDMI_PHY_CONF0_GEN2_TXPWRON_MASK; + writeb(reg, &hdmi->phy_conf0); + writeb(HDMI_MC_PHYRSTZ_ASSERT, &hdmi->mc_phyrstz); +} + +void imx_setup_hdmi(void) +{ + struct mxc_ccm_reg *mxc_ccm = (struct mxc_ccm_reg *)CCM_BASE_ADDR; + struct hdmi_regs *hdmi = (struct hdmi_regs *)HDMI_ARB_BASE_ADDR; + int reg, count; + u8 val; + + /* Turn on HDMI PHY clock */ + reg = readl(&mxc_ccm->CCGR2); + reg |= MXC_CCM_CCGR2_HDMI_TX_IAHBCLK_MASK| + MXC_CCM_CCGR2_HDMI_TX_ISFRCLK_MASK; + writel(reg, &mxc_ccm->CCGR2); + writeb(HDMI_MC_PHYRSTZ_DEASSERT, &hdmi->mc_phyrstz); + reg = readl(&mxc_ccm->chsccdr); + reg &= ~(MXC_CCM_CHSCCDR_IPU1_DI0_PRE_CLK_SEL_MASK| + MXC_CCM_CHSCCDR_IPU1_DI0_PODF_MASK| + MXC_CCM_CHSCCDR_IPU1_DI0_CLK_SEL_MASK); + reg |= (CHSCCDR_PODF_DIVIDE_BY_3 + << MXC_CCM_CHSCCDR_IPU1_DI0_PODF_OFFSET) + |(CHSCCDR_IPU_PRE_CLK_540M_PFD + << MXC_CCM_CHSCCDR_IPU1_DI0_PRE_CLK_SEL_OFFSET); + writel(reg, &mxc_ccm->chsccdr); + + /* Clear the overflow condition */ + if (readb(&hdmi->ih_fc_stat2) & HDMI_IH_FC_STAT2_OVERFLOW_MASK) { + /* TMDS software reset */ + writeb((u8)~HDMI_MC_SWRSTZ_TMDSSWRST_REQ, &hdmi->mc_swrstz); + val = readb(&hdmi->fc_invidconf); + /* Need minimum 3 times to write to clear the register */ + for (count = 0 ; count < 5 ; count++) + writeb(val, &hdmi->fc_invidconf); + } +} +#endif + +#ifdef CONFIG_ARCH_MISC_INIT +/* + * UNIQUE_ID describes a unique ID based on silicon wafer + * and die X/Y position + * + * UNIQUE_ID offset 0x410 + * 31:0 fuse 0 + * FSL-wide unique, encoded LOT ID STD II/SJC CHALLENGE/ Unique ID + * + * UNIQUE_ID offset 0x420 + * 31:24 fuse 1 + * The X-coordinate of the die location on the wafer/SJC CHALLENGE/ Unique ID + * 23:16 fuse 1 + * The Y-coordinate of the die location on the wafer/SJC CHALLENGE/ Unique ID + * 15:11 fuse 1 + * The wafer number of the wafer on which the device was fabricated/SJC + * CHALLENGE/ Unique ID + * 10:0 fuse 1 + * FSL-wide unique, encoded LOT ID STD II/SJC CHALLENGE/ Unique ID + */ +static void setup_serial_number(void) +{ + char serial_string[17]; + struct ocotp_regs *ocotp = (struct ocotp_regs *)OCOTP_BASE_ADDR; + struct fuse_bank *bank = &ocotp->bank[0]; + struct fuse_bank0_regs *fuse = + (struct fuse_bank0_regs *)bank->fuse_regs; + + if (env_get("serial#")) + return; + + snprintf(serial_string, sizeof(serial_string), "%08x%08x", + fuse->uid_low, fuse->uid_high); + env_set("serial#", serial_string); +} + +int arch_misc_init(void) +{ +#ifdef CONFIG_FSL_CAAM + sec_init(); +#endif + setup_serial_number(); + return 0; +} +#endif + +/* + * gpr_init() function is common for boards using MX6S, MX6DL, MX6D, + * MX6Q and MX6QP processors + */ +void gpr_init(void) +{ + struct iomuxc *iomux = (struct iomuxc *)IOMUXC_BASE_ADDR; + + /* + * If this function is used in a common MX6 spl implementation + * we have to ensure that it is only called for suitable cpu types, + * otherwise it breaks hardware parts like enet1, can1, can2, etc. + */ + if (!is_mx6dqp() && !is_mx6dq() && !is_mx6sdl()) + return; + + /* enable AXI cache for VDOA/VPU/IPU */ + writel(0xF00000CF, &iomux->gpr[4]); + if (is_mx6dqp()) { + /* set IPU AXI-id1 Qos=0x1 AXI-id0/2/3 Qos=0x7 */ + writel(0x77177717, &iomux->gpr[6]); + writel(0x77177717, &iomux->gpr[7]); + } else { + /* set IPU AXI-id0 Qos=0xf(bypass) AXI-id1 Qos=0x7 */ + writel(0x007F007F, &iomux->gpr[6]); + writel(0x007F007F, &iomux->gpr[7]); + } +} diff --git a/roms/u-boot/arch/arm/mach-imx/mx7/Kconfig b/roms/u-boot/arch/arm/mach-imx/mx7/Kconfig new file mode 100644 index 000000000..adedc0116 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/mx7/Kconfig @@ -0,0 +1,110 @@ +if ARCH_MX7 + +config MX7 + bool + default y + select ARCH_SUPPORT_PSCI + select CPU_V7_HAS_NONSEC + select CPU_V7_HAS_VIRT + select ROM_UNIFIED_SECTIONS + select SYSCOUNTER_TIMER + imply CMD_FUSE + +config MX7D + bool + select HAS_CAAM + select ROM_UNIFIED_SECTIONS + imply CMD_FUSE + +config SYS_TEXT_BASE + default 0x87800000 + +config SPL_TEXT_BASE + depends on SPL + default 0x00912000 + +choice + prompt "MX7 board select" + optional + +config TARGET_CL_SOM_IMX7 + bool "CL-SOM-iMX7" + select DM + select DM_THERMAL + select MX7D + select SUPPORT_SPL + imply CMD_DM + +config TARGET_IMX7_CM + bool "Ronetix iMX7-CM" + select BOARD_LATE_INIT + select DM + select DM_THERMAL + select MX7D + select SUPPORT_SPL + imply CMD_DM + +config TARGET_MEERKAT96 + bool "NovTech Meerkat96 board" + select BOARD_LATE_INIT + select DM + select DM_SERIAL + select DM_THERMAL + select MX7D + imply CMD_DM + +config TARGET_MX7DSABRESD + bool "mx7dsabresd" + select BOARD_LATE_INIT + select DM + select DM_THERMAL + select MX7D + imply CMD_DM + +config TARGET_PICO_IMX7D + bool "pico-imx7d" + select BOARD_LATE_INIT + select DM + select DM_THERMAL + select MX7D + select SUPPORT_SPL + imply CMD_DM + +config TARGET_SMEGW01 + bool "smegw01" + select BOARD_LATE_INIT + select DM + select DM_THERMAL + select MX7D + imply CMD_DM + +config TARGET_WARP7 + bool "warp7" + select BOARD_LATE_INIT + select DM + select DM_THERMAL + select MX7D + imply CMD_DM + +config TARGET_COLIBRI_IMX7 + bool "Support Colibri iMX7S/iMX7D modules" + select DM + select DM_SERIAL + select DM_THERMAL + imply CMD_DM + +endchoice + +config SYS_SOC + default "mx7" + +source "board/compulab/cl-som-imx7/Kconfig" +source "board/ronetix/imx7-cm/Kconfig" +source "board/freescale/mx7dsabresd/Kconfig" +source "board/novtech/meerkat96/Kconfig" +source "board/storopack/smegw01/Kconfig" +source "board/technexion/pico-imx7d/Kconfig" +source "board/toradex/colibri_imx7/Kconfig" +source "board/warp7/Kconfig" + +endif diff --git a/roms/u-boot/arch/arm/mach-imx/mx7/Makefile b/roms/u-boot/arch/arm/mach-imx/mx7/Makefile new file mode 100644 index 000000000..f1436e2d0 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/mx7/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright 2015 Freescale Semiconductor, Inc. +# + +obj-y := soc.o clock.o clock_slice.o ddr.o snvs.o +obj-$(CONFIG_ARMV7_PSCI) += psci-mx7.o psci-suspend.o diff --git a/roms/u-boot/arch/arm/mach-imx/mx7/clock.c b/roms/u-boot/arch/arm/mach-imx/mx7/clock.c new file mode 100644 index 000000000..304a03031 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/mx7/clock.c @@ -0,0 +1,1139 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2015 Freescale Semiconductor, Inc. + * + * Author: + * Peng Fan <Peng.Fan@freescale.com> + */ + +#include <common.h> +#include <clock_legacy.h> +#include <command.h> +#include <div64.h> +#include <log.h> +#include <asm/global_data.h> +#include <asm/io.h> +#include <linux/errno.h> +#include <asm/arch/imx-regs.h> +#include <asm/arch/crm_regs.h> +#include <asm/arch/clock.h> +#include <asm/arch/sys_proto.h> + +struct mxc_ccm_anatop_reg *ccm_anatop = (struct mxc_ccm_anatop_reg *) + ANATOP_BASE_ADDR; +struct mxc_ccm_reg *ccm_reg = (struct mxc_ccm_reg *)CCM_BASE_ADDR; + +#ifdef CONFIG_FSL_ESDHC_IMX +DECLARE_GLOBAL_DATA_PTR; +#endif + +int get_clocks(void) +{ +#ifdef CONFIG_FSL_ESDHC_IMX +#if CONFIG_SYS_FSL_ESDHC_ADDR == USDHC2_BASE_ADDR + gd->arch.sdhc_clk = mxc_get_clock(MXC_ESDHC2_CLK); +#elif CONFIG_SYS_FSL_ESDHC_ADDR == USDHC3_BASE_ADDR + gd->arch.sdhc_clk = mxc_get_clock(MXC_ESDHC3_CLK); +#else + gd->arch.sdhc_clk = mxc_get_clock(MXC_ESDHC_CLK); +#endif +#endif + return 0; +} + +u32 get_ahb_clk(void) +{ + return get_root_clk(AHB_CLK_ROOT); +} + +static u32 get_ipg_clk(void) +{ + /* + * The AHB and IPG are fixed at 2:1 ratio, and synchronized to + * each other. + */ + return get_ahb_clk() / 2; +} + +u32 imx_get_uartclk(void) +{ + return get_root_clk(UART_CLK_ROOT); +} + +u32 imx_get_fecclk(void) +{ + return get_root_clk(ENET_AXI_CLK_ROOT); +} + +#ifdef CONFIG_MXC_OCOTP +void enable_ocotp_clk(unsigned char enable) +{ + clock_enable(CCGR_OCOTP, enable); +} + +void enable_thermal_clk(void) +{ + enable_ocotp_clk(1); +} +#endif + +void enable_usboh3_clk(unsigned char enable) +{ + u32 target; + + if (enable) { + /* disable the clock gate first */ + clock_enable(CCGR_USB_HSIC, 0); + + /* 120Mhz */ + target = CLK_ROOT_ON | + USB_HSIC_CLK_ROOT_FROM_PLL_SYS_MAIN_480M_CLK | + CLK_ROOT_PRE_DIV(CLK_ROOT_PRE_DIV1) | + CLK_ROOT_POST_DIV(CLK_ROOT_POST_DIV1); + clock_set_target_val(USB_HSIC_CLK_ROOT, target); + + /* enable the clock gate */ + clock_enable(CCGR_USB_CTRL, 1); + clock_enable(CCGR_USB_HSIC, 1); + clock_enable(CCGR_USB_PHY1, 1); + clock_enable(CCGR_USB_PHY2, 1); + } else { + clock_enable(CCGR_USB_CTRL, 0); + clock_enable(CCGR_USB_HSIC, 0); + clock_enable(CCGR_USB_PHY1, 0); + clock_enable(CCGR_USB_PHY2, 0); + } +} + +static u32 decode_pll(enum pll_clocks pll, u32 infreq) +{ + u32 reg, div_sel; + u32 num, denom; + + /* + * Alought there are four choices for the bypass src, + * we choose OSC_24M which is the default set in ROM. + */ + switch (pll) { + case PLL_CORE: + reg = readl(&ccm_anatop->pll_arm); + + if (reg & CCM_ANALOG_PLL_ARM_POWERDOWN_MASK) + return 0; + + if (reg & CCM_ANALOG_PLL_ARM_BYPASS_MASK) + return MXC_HCLK; + + div_sel = (reg & CCM_ANALOG_PLL_ARM_DIV_SELECT_MASK) >> + CCM_ANALOG_PLL_ARM_DIV_SELECT_SHIFT; + + return (infreq * div_sel) / 2; + + case PLL_SYS: + reg = readl(&ccm_anatop->pll_480); + + if (reg & CCM_ANALOG_PLL_480_POWERDOWN_MASK) + return 0; + + if (reg & CCM_ANALOG_PLL_480_BYPASS_MASK) + return MXC_HCLK; + + if (((reg & CCM_ANALOG_PLL_480_DIV_SELECT_MASK) >> + CCM_ANALOG_PLL_480_DIV_SELECT_SHIFT) == 0) + return 480000000u; + else + return 528000000u; + + case PLL_ENET: + reg = readl(&ccm_anatop->pll_enet); + + if (reg & CCM_ANALOG_PLL_ENET_POWERDOWN_MASK) + return 0; + + if (reg & CCM_ANALOG_PLL_ENET_BYPASS_MASK) + return MXC_HCLK; + + return 1000000000u; + + case PLL_DDR: + reg = readl(&ccm_anatop->pll_ddr); + + if (reg & CCM_ANALOG_PLL_DDR_POWERDOWN_MASK) + return 0; + + num = ccm_anatop->pll_ddr_num; + denom = ccm_anatop->pll_ddr_denom; + + if (reg & CCM_ANALOG_PLL_DDR_BYPASS_MASK) + return MXC_HCLK; + + div_sel = (reg & CCM_ANALOG_PLL_DDR_DIV_SELECT_MASK) >> + CCM_ANALOG_PLL_DDR_DIV_SELECT_SHIFT; + + return infreq * (div_sel + num / denom); + + case PLL_USB: + return 480000000u; + + default: + printf("Unsupported pll clocks %d\n", pll); + break; + } + + return 0; +} + +static u32 mxc_get_pll_sys_derive(int derive) +{ + u32 freq, div, frac; + u32 reg; + + div = 1; + reg = readl(&ccm_anatop->pll_480); + freq = decode_pll(PLL_SYS, MXC_HCLK); + + switch (derive) { + case PLL_SYS_MAIN_480M_CLK: + if (reg & CCM_ANALOG_PLL_480_MAIN_DIV1_CLKGATE_MASK) + return 0; + else + return freq; + case PLL_SYS_MAIN_240M_CLK: + if (reg & CCM_ANALOG_PLL_480_MAIN_DIV2_CLKGATE_MASK) + return 0; + else + return freq / 2; + case PLL_SYS_MAIN_120M_CLK: + if (reg & CCM_ANALOG_PLL_480_MAIN_DIV4_CLKGATE_MASK) + return 0; + else + return freq / 4; + case PLL_SYS_PFD0_392M_CLK: + reg = readl(&ccm_anatop->pfd_480a); + if (reg & CCM_ANALOG_PFD_480A_PFD0_DIV1_CLKGATE_MASK) + return 0; + frac = (reg & CCM_ANALOG_PFD_480A_PFD0_FRAC_MASK) >> + CCM_ANALOG_PFD_480A_PFD0_FRAC_SHIFT; + break; + case PLL_SYS_PFD0_196M_CLK: + if (reg & CCM_ANALOG_PLL_480_PFD0_DIV2_CLKGATE_MASK) + return 0; + reg = readl(&ccm_anatop->pfd_480a); + frac = (reg & CCM_ANALOG_PFD_480A_PFD0_FRAC_MASK) >> + CCM_ANALOG_PFD_480A_PFD0_FRAC_SHIFT; + div = 2; + break; + case PLL_SYS_PFD1_332M_CLK: + reg = readl(&ccm_anatop->pfd_480a); + if (reg & CCM_ANALOG_PFD_480A_PFD1_DIV1_CLKGATE_MASK) + return 0; + frac = (reg & CCM_ANALOG_PFD_480A_PFD1_FRAC_MASK) >> + CCM_ANALOG_PFD_480A_PFD1_FRAC_SHIFT; + break; + case PLL_SYS_PFD1_166M_CLK: + if (reg & CCM_ANALOG_PLL_480_PFD1_DIV2_CLKGATE_MASK) + return 0; + reg = readl(&ccm_anatop->pfd_480a); + frac = (reg & CCM_ANALOG_PFD_480A_PFD1_FRAC_MASK) >> + CCM_ANALOG_PFD_480A_PFD1_FRAC_SHIFT; + div = 2; + break; + case PLL_SYS_PFD2_270M_CLK: + reg = readl(&ccm_anatop->pfd_480a); + if (reg & CCM_ANALOG_PFD_480A_PFD2_DIV1_CLKGATE_MASK) + return 0; + frac = (reg & CCM_ANALOG_PFD_480A_PFD2_FRAC_MASK) >> + CCM_ANALOG_PFD_480A_PFD2_FRAC_SHIFT; + break; + case PLL_SYS_PFD2_135M_CLK: + if (reg & CCM_ANALOG_PLL_480_PFD2_DIV2_CLKGATE_MASK) + return 0; + reg = readl(&ccm_anatop->pfd_480a); + frac = (reg & CCM_ANALOG_PFD_480A_PFD2_FRAC_MASK) >> + CCM_ANALOG_PFD_480A_PFD2_FRAC_SHIFT; + div = 2; + break; + case PLL_SYS_PFD3_CLK: + reg = readl(&ccm_anatop->pfd_480a); + if (reg & CCM_ANALOG_PFD_480A_PFD3_DIV1_CLKGATE_MASK) + return 0; + frac = (reg & CCM_ANALOG_PFD_480A_PFD3_FRAC_MASK) >> + CCM_ANALOG_PFD_480A_PFD3_FRAC_SHIFT; + break; + case PLL_SYS_PFD4_CLK: + reg = readl(&ccm_anatop->pfd_480b); + if (reg & CCM_ANALOG_PFD_480B_PFD4_DIV1_CLKGATE_MASK) + return 0; + frac = (reg & CCM_ANALOG_PFD_480B_PFD4_FRAC_MASK) >> + CCM_ANALOG_PFD_480B_PFD4_FRAC_SHIFT; + break; + case PLL_SYS_PFD5_CLK: + reg = readl(&ccm_anatop->pfd_480b); + if (reg & CCM_ANALOG_PFD_480B_PFD5_DIV1_CLKGATE_MASK) + return 0; + frac = (reg & CCM_ANALOG_PFD_480B_PFD5_FRAC_MASK) >> + CCM_ANALOG_PFD_480B_PFD5_FRAC_SHIFT; + break; + case PLL_SYS_PFD6_CLK: + reg = readl(&ccm_anatop->pfd_480b); + if (reg & CCM_ANALOG_PFD_480B_PFD6_DIV1_CLKGATE_MASK) + return 0; + frac = (reg & CCM_ANALOG_PFD_480B_PFD6_FRAC_MASK) >> + CCM_ANALOG_PFD_480B_PFD6_FRAC_SHIFT; + break; + case PLL_SYS_PFD7_CLK: + reg = readl(&ccm_anatop->pfd_480b); + if (reg & CCM_ANALOG_PFD_480B_PFD7_DIV1_CLKGATE_MASK) + return 0; + frac = (reg & CCM_ANALOG_PFD_480B_PFD7_FRAC_MASK) >> + CCM_ANALOG_PFD_480B_PFD7_FRAC_SHIFT; + break; + default: + printf("Error derived pll_sys clock %d\n", derive); + return 0; + } + + return ((freq / frac) * 18) / div; +} + +static u32 mxc_get_pll_enet_derive(int derive) +{ + u32 freq, reg; + + freq = decode_pll(PLL_ENET, MXC_HCLK); + reg = readl(&ccm_anatop->pll_enet); + + switch (derive) { + case PLL_ENET_MAIN_500M_CLK: + if (reg & CCM_ANALOG_PLL_ENET_ENABLE_CLK_500MHZ_MASK) + return freq / 2; + break; + case PLL_ENET_MAIN_250M_CLK: + if (reg & CCM_ANALOG_PLL_ENET_ENABLE_CLK_250MHZ_MASK) + return freq / 4; + break; + case PLL_ENET_MAIN_125M_CLK: + if (reg & CCM_ANALOG_PLL_ENET_ENABLE_CLK_125MHZ_MASK) + return freq / 8; + break; + case PLL_ENET_MAIN_100M_CLK: + if (reg & CCM_ANALOG_PLL_ENET_ENABLE_CLK_100MHZ_MASK) + return freq / 10; + break; + case PLL_ENET_MAIN_50M_CLK: + if (reg & CCM_ANALOG_PLL_ENET_ENABLE_CLK_50MHZ_MASK) + return freq / 20; + break; + case PLL_ENET_MAIN_40M_CLK: + if (reg & CCM_ANALOG_PLL_ENET_ENABLE_CLK_40MHZ_MASK) + return freq / 25; + break; + case PLL_ENET_MAIN_25M_CLK: + if (reg & CCM_ANALOG_PLL_ENET_ENABLE_CLK_25MHZ_MASK) + return freq / 40; + break; + default: + printf("Error derived pll_enet clock %d\n", derive); + break; + } + + return 0; +} + +static u32 mxc_get_pll_ddr_derive(int derive) +{ + u32 freq, reg; + + freq = decode_pll(PLL_DDR, MXC_HCLK); + reg = readl(&ccm_anatop->pll_ddr); + + switch (derive) { + case PLL_DRAM_MAIN_1066M_CLK: + return freq; + case PLL_DRAM_MAIN_533M_CLK: + if (reg & CCM_ANALOG_PLL_DDR_DIV2_ENABLE_CLK_MASK) + return freq / 2; + break; + default: + printf("Error derived pll_ddr clock %d\n", derive); + break; + } + + return 0; +} + +static u32 mxc_get_pll_derive(enum pll_clocks pll, int derive) +{ + switch (pll) { + case PLL_SYS: + return mxc_get_pll_sys_derive(derive); + case PLL_ENET: + return mxc_get_pll_enet_derive(derive); + case PLL_DDR: + return mxc_get_pll_ddr_derive(derive); + default: + printf("Error pll.\n"); + return 0; + } +} + +static u32 get_root_src_clk(enum clk_root_src root_src) +{ + switch (root_src) { + case OSC_24M_CLK: + return 24000000u; + case PLL_ARM_MAIN_800M_CLK: + return decode_pll(PLL_CORE, MXC_HCLK); + + case PLL_SYS_MAIN_480M_CLK: + case PLL_SYS_MAIN_240M_CLK: + case PLL_SYS_MAIN_120M_CLK: + case PLL_SYS_PFD0_392M_CLK: + case PLL_SYS_PFD0_196M_CLK: + case PLL_SYS_PFD1_332M_CLK: + case PLL_SYS_PFD1_166M_CLK: + case PLL_SYS_PFD2_270M_CLK: + case PLL_SYS_PFD2_135M_CLK: + case PLL_SYS_PFD3_CLK: + case PLL_SYS_PFD4_CLK: + case PLL_SYS_PFD5_CLK: + case PLL_SYS_PFD6_CLK: + case PLL_SYS_PFD7_CLK: + return mxc_get_pll_derive(PLL_SYS, root_src); + + case PLL_ENET_MAIN_500M_CLK: + case PLL_ENET_MAIN_250M_CLK: + case PLL_ENET_MAIN_125M_CLK: + case PLL_ENET_MAIN_100M_CLK: + case PLL_ENET_MAIN_50M_CLK: + case PLL_ENET_MAIN_40M_CLK: + case PLL_ENET_MAIN_25M_CLK: + return mxc_get_pll_derive(PLL_ENET, root_src); + + case PLL_DRAM_MAIN_1066M_CLK: + case PLL_DRAM_MAIN_533M_CLK: + return mxc_get_pll_derive(PLL_DDR, root_src); + + case PLL_AUDIO_MAIN_CLK: + return decode_pll(PLL_AUDIO, MXC_HCLK); + case PLL_VIDEO_MAIN_CLK: + return decode_pll(PLL_VIDEO, MXC_HCLK); + + case PLL_USB_MAIN_480M_CLK: + return decode_pll(PLL_USB, MXC_HCLK); + + case REF_1M_CLK: + return 1000000; + case OSC_32K_CLK: + return MXC_CLK32; + + case EXT_CLK_1: + case EXT_CLK_2: + case EXT_CLK_3: + case EXT_CLK_4: + printf("No EXT CLK supported??\n"); + break; + }; + + return 0; +} + +u32 get_root_clk(enum clk_root_index clock_id) +{ + enum clk_root_src root_src; + u32 post_podf, pre_podf, auto_podf, root_src_clk; + int auto_en; + + if (clock_root_enabled(clock_id) <= 0) + return 0; + + if (clock_get_prediv(clock_id, &pre_podf) < 0) + return 0; + + if (clock_get_postdiv(clock_id, &post_podf) < 0) + return 0; + + if (clock_get_autopostdiv(clock_id, &auto_podf, &auto_en) < 0) + return 0; + + if (auto_en == 0) + auto_podf = 0; + + if (clock_get_src(clock_id, &root_src) < 0) + return 0; + + root_src_clk = get_root_src_clk(root_src); + + /* + * bypass clk is ignored. + */ + + return root_src_clk / (post_podf + 1) / (pre_podf + 1) / + (auto_podf + 1); +} + +static u32 get_ddrc_clk(void) +{ + u32 reg, freq; + enum root_post_div post_div; + + reg = readl(&ccm_reg->root[DRAM_CLK_ROOT].target_root); + if (reg & CLK_ROOT_MUX_MASK) + /* DRAM_ALT_CLK_ROOT */ + freq = get_root_clk(DRAM_ALT_CLK_ROOT); + else + /* PLL_DRAM_MAIN_1066M_CLK */ + freq = mxc_get_pll_derive(PLL_DDR, PLL_DRAM_MAIN_1066M_CLK); + + post_div = reg & DRAM_CLK_ROOT_POST_DIV_MASK; + + return freq / (post_div + 1) / 2; +} + +unsigned int mxc_get_clock(enum mxc_clock clk) +{ + switch (clk) { + case MXC_ARM_CLK: + return get_root_clk(ARM_A7_CLK_ROOT); + case MXC_AXI_CLK: + return get_root_clk(MAIN_AXI_CLK_ROOT); + case MXC_AHB_CLK: + return get_root_clk(AHB_CLK_ROOT); + case MXC_IPG_CLK: + return get_ipg_clk(); + case MXC_I2C_CLK: + return get_root_clk(I2C1_CLK_ROOT); + case MXC_UART_CLK: + return get_root_clk(UART1_CLK_ROOT); + case MXC_CSPI_CLK: + return get_root_clk(ECSPI1_CLK_ROOT); + case MXC_DDR_CLK: + return get_ddrc_clk(); + case MXC_ESDHC_CLK: + return get_root_clk(USDHC1_CLK_ROOT); + case MXC_ESDHC2_CLK: + return get_root_clk(USDHC2_CLK_ROOT); + case MXC_ESDHC3_CLK: + return get_root_clk(USDHC3_CLK_ROOT); + default: + printf("Unsupported mxc_clock %d\n", clk); + break; + } + + return 0; +} + +#ifdef CONFIG_SYS_I2C_MXC +/* i2c_num can be 0 - 3 */ +int enable_i2c_clk(unsigned char enable, unsigned i2c_num) +{ + u32 target; + + if (i2c_num >= 4) + return -EINVAL; + + if (enable) { + clock_enable(CCGR_I2C1 + i2c_num, 0); + + /* Set i2c root clock to PLL_SYS_MAIN_120M_CLK */ + + target = CLK_ROOT_ON | + I2C1_CLK_ROOT_FROM_PLL_SYS_MAIN_120M_CLK | + CLK_ROOT_PRE_DIV(CLK_ROOT_PRE_DIV1) | + CLK_ROOT_POST_DIV(CLK_ROOT_POST_DIV2); + clock_set_target_val(I2C1_CLK_ROOT + i2c_num, target); + + clock_enable(CCGR_I2C1 + i2c_num, 1); + } else { + clock_enable(CCGR_I2C1 + i2c_num, 0); + } + + return 0; +} +#endif + +static void init_clk_esdhc(void) +{ + u32 target; + + /* disable the clock gate first */ + clock_enable(CCGR_USDHC1, 0); + clock_enable(CCGR_USDHC2, 0); + clock_enable(CCGR_USDHC3, 0); + + /* 196: 392/2 */ + target = CLK_ROOT_ON | USDHC1_CLK_ROOT_FROM_PLL_SYS_PFD0_392M_CLK | + CLK_ROOT_PRE_DIV(CLK_ROOT_PRE_DIV1) | + CLK_ROOT_POST_DIV(CLK_ROOT_POST_DIV2); + clock_set_target_val(USDHC1_CLK_ROOT, target); + + target = CLK_ROOT_ON | USDHC1_CLK_ROOT_FROM_PLL_SYS_PFD0_392M_CLK | + CLK_ROOT_PRE_DIV(CLK_ROOT_PRE_DIV1) | + CLK_ROOT_POST_DIV(CLK_ROOT_POST_DIV2); + clock_set_target_val(USDHC2_CLK_ROOT, target); + + target = CLK_ROOT_ON | USDHC1_CLK_ROOT_FROM_PLL_SYS_PFD0_392M_CLK | + CLK_ROOT_PRE_DIV(CLK_ROOT_PRE_DIV1) | + CLK_ROOT_POST_DIV(CLK_ROOT_POST_DIV2); + clock_set_target_val(USDHC3_CLK_ROOT, target); + + /* enable the clock gate */ + clock_enable(CCGR_USDHC1, 1); + clock_enable(CCGR_USDHC2, 1); + clock_enable(CCGR_USDHC3, 1); +} + +static void init_clk_uart(void) +{ + u32 target; + + /* disable the clock gate first */ + clock_enable(CCGR_UART1, 0); + clock_enable(CCGR_UART2, 0); + clock_enable(CCGR_UART3, 0); + clock_enable(CCGR_UART4, 0); + clock_enable(CCGR_UART5, 0); + clock_enable(CCGR_UART6, 0); + clock_enable(CCGR_UART7, 0); + + /* 24Mhz */ + target = CLK_ROOT_ON | UART1_CLK_ROOT_FROM_OSC_24M_CLK | + CLK_ROOT_PRE_DIV(CLK_ROOT_PRE_DIV1) | + CLK_ROOT_POST_DIV(CLK_ROOT_POST_DIV1); + clock_set_target_val(UART1_CLK_ROOT, target); + + target = CLK_ROOT_ON | UART2_CLK_ROOT_FROM_OSC_24M_CLK | + CLK_ROOT_PRE_DIV(CLK_ROOT_PRE_DIV1) | + CLK_ROOT_POST_DIV(CLK_ROOT_POST_DIV1); + clock_set_target_val(UART2_CLK_ROOT, target); + + target = CLK_ROOT_ON | UART3_CLK_ROOT_FROM_OSC_24M_CLK | + CLK_ROOT_PRE_DIV(CLK_ROOT_PRE_DIV1) | + CLK_ROOT_POST_DIV(CLK_ROOT_POST_DIV1); + clock_set_target_val(UART3_CLK_ROOT, target); + + target = CLK_ROOT_ON | UART4_CLK_ROOT_FROM_OSC_24M_CLK | + CLK_ROOT_PRE_DIV(CLK_ROOT_PRE_DIV1) | + CLK_ROOT_POST_DIV(CLK_ROOT_POST_DIV1); + clock_set_target_val(UART4_CLK_ROOT, target); + + target = CLK_ROOT_ON | UART5_CLK_ROOT_FROM_OSC_24M_CLK | + CLK_ROOT_PRE_DIV(CLK_ROOT_PRE_DIV1) | + CLK_ROOT_POST_DIV(CLK_ROOT_POST_DIV1); + clock_set_target_val(UART5_CLK_ROOT, target); + + target = CLK_ROOT_ON | UART6_CLK_ROOT_FROM_OSC_24M_CLK | + CLK_ROOT_PRE_DIV(CLK_ROOT_PRE_DIV1) | + CLK_ROOT_POST_DIV(CLK_ROOT_POST_DIV1); + clock_set_target_val(UART6_CLK_ROOT, target); + + target = CLK_ROOT_ON | UART7_CLK_ROOT_FROM_OSC_24M_CLK | + CLK_ROOT_PRE_DIV(CLK_ROOT_PRE_DIV1) | + CLK_ROOT_POST_DIV(CLK_ROOT_POST_DIV1); + clock_set_target_val(UART7_CLK_ROOT, target); + + /* enable the clock gate */ + clock_enable(CCGR_UART1, 1); + clock_enable(CCGR_UART2, 1); + clock_enable(CCGR_UART3, 1); + clock_enable(CCGR_UART4, 1); + clock_enable(CCGR_UART5, 1); + clock_enable(CCGR_UART6, 1); + clock_enable(CCGR_UART7, 1); +} + +static void init_clk_weim(void) +{ + u32 target; + + /* disable the clock gate first */ + clock_enable(CCGR_WEIM, 0); + + /* 120Mhz */ + target = CLK_ROOT_ON | EIM_CLK_ROOT_FROM_PLL_SYS_MAIN_120M_CLK | + CLK_ROOT_PRE_DIV(CLK_ROOT_PRE_DIV1) | + CLK_ROOT_POST_DIV(CLK_ROOT_POST_DIV1); + clock_set_target_val(EIM_CLK_ROOT, target); + + /* enable the clock gate */ + clock_enable(CCGR_WEIM, 1); +} + +static void init_clk_ecspi(void) +{ + u32 target; + + /* disable the clock gate first */ + clock_enable(CCGR_ECSPI1, 0); + clock_enable(CCGR_ECSPI2, 0); + clock_enable(CCGR_ECSPI3, 0); + clock_enable(CCGR_ECSPI4, 0); + + /* 60Mhz: 240/4 */ + target = CLK_ROOT_ON | ECSPI1_CLK_ROOT_FROM_PLL_SYS_MAIN_240M_CLK | + CLK_ROOT_PRE_DIV(CLK_ROOT_PRE_DIV1) | + CLK_ROOT_POST_DIV(CLK_ROOT_POST_DIV4); + clock_set_target_val(ECSPI1_CLK_ROOT, target); + + target = CLK_ROOT_ON | ECSPI2_CLK_ROOT_FROM_PLL_SYS_MAIN_240M_CLK | + CLK_ROOT_PRE_DIV(CLK_ROOT_PRE_DIV1) | + CLK_ROOT_POST_DIV(CLK_ROOT_POST_DIV4); + clock_set_target_val(ECSPI2_CLK_ROOT, target); + + target = CLK_ROOT_ON | ECSPI3_CLK_ROOT_FROM_PLL_SYS_MAIN_240M_CLK | + CLK_ROOT_PRE_DIV(CLK_ROOT_PRE_DIV1) | + CLK_ROOT_POST_DIV(CLK_ROOT_POST_DIV4); + clock_set_target_val(ECSPI3_CLK_ROOT, target); + + target = CLK_ROOT_ON | ECSPI4_CLK_ROOT_FROM_PLL_SYS_MAIN_240M_CLK | + CLK_ROOT_PRE_DIV(CLK_ROOT_PRE_DIV1) | + CLK_ROOT_POST_DIV(CLK_ROOT_POST_DIV4); + clock_set_target_val(ECSPI4_CLK_ROOT, target); + + /* enable the clock gate */ + clock_enable(CCGR_ECSPI1, 1); + clock_enable(CCGR_ECSPI2, 1); + clock_enable(CCGR_ECSPI3, 1); + clock_enable(CCGR_ECSPI4, 1); +} + +static void init_clk_wdog(void) +{ + u32 target; + + /* disable the clock gate first */ + clock_enable(CCGR_WDOG1, 0); + clock_enable(CCGR_WDOG2, 0); + clock_enable(CCGR_WDOG3, 0); + clock_enable(CCGR_WDOG4, 0); + + /* 24Mhz */ + target = CLK_ROOT_ON | WDOG_CLK_ROOT_FROM_OSC_24M_CLK | + CLK_ROOT_PRE_DIV(CLK_ROOT_PRE_DIV1) | + CLK_ROOT_POST_DIV(CLK_ROOT_POST_DIV1); + clock_set_target_val(WDOG_CLK_ROOT, target); + + /* enable the clock gate */ + clock_enable(CCGR_WDOG1, 1); + clock_enable(CCGR_WDOG2, 1); + clock_enable(CCGR_WDOG3, 1); + clock_enable(CCGR_WDOG4, 1); +} + +#ifdef CONFIG_MXC_EPDC +static void init_clk_epdc(void) +{ + u32 target; + + /* disable the clock gate first */ + clock_enable(CCGR_EPDC, 0); + + /* 24Mhz */ + target = CLK_ROOT_ON | EPDC_PIXEL_CLK_ROOT_FROM_PLL_SYS_MAIN_480M_CLK | + CLK_ROOT_PRE_DIV(CLK_ROOT_PRE_DIV1) | + CLK_ROOT_POST_DIV(CLK_ROOT_POST_DIV12); + clock_set_target_val(EPDC_PIXEL_CLK_ROOT, target); + + /* enable the clock gate */ + clock_enable(CCGR_EPDC, 1); +} +#endif + +static int enable_pll_enet(void) +{ + u32 reg; + s32 timeout = 100000; + + reg = readl(&ccm_anatop->pll_enet); + /* If pll_enet powered up, no need to set it again */ + if (reg & ANADIG_PLL_ENET_PWDN_MASK) { + reg &= ~ANADIG_PLL_ENET_PWDN_MASK; + writel(reg, &ccm_anatop->pll_enet); + + while (timeout--) { + if (readl(&ccm_anatop->pll_enet) & ANADIG_PLL_LOCK) + break; + } + + if (timeout <= 0) { + /* If timeout, we set pwdn for pll_enet. */ + reg |= ANADIG_PLL_ENET_PWDN_MASK; + return -ETIME; + } + } + + /* Clear bypass */ + writel(CCM_ANALOG_PLL_ENET_BYPASS_MASK, &ccm_anatop->pll_enet_clr); + + writel((CCM_ANALOG_PLL_ENET_ENABLE_CLK_500MHZ_MASK + | CCM_ANALOG_PLL_ENET_ENABLE_CLK_250MHZ_MASK + | CCM_ANALOG_PLL_ENET_ENABLE_CLK_125MHZ_MASK + | CCM_ANALOG_PLL_ENET_ENABLE_CLK_100MHZ_MASK + | CCM_ANALOG_PLL_ENET_ENABLE_CLK_50MHZ_MASK + | CCM_ANALOG_PLL_ENET_ENABLE_CLK_40MHZ_MASK + | CCM_ANALOG_PLL_ENET_ENABLE_CLK_25MHZ_MASK), + &ccm_anatop->pll_enet_set); + + return 0; +} +static int enable_pll_video(u32 pll_div, u32 pll_num, u32 pll_denom, + u32 post_div) +{ + u32 reg = 0; + ulong start; + + debug("pll5 div = %d, num = %d, denom = %d\n", + pll_div, pll_num, pll_denom); + + /* Power up PLL5 video and disable its output */ + writel(CCM_ANALOG_PLL_VIDEO_CLR_ENABLE_CLK_MASK | + CCM_ANALOG_PLL_VIDEO_CLR_POWERDOWN_MASK | + CCM_ANALOG_PLL_VIDEO_CLR_BYPASS_MASK | + CCM_ANALOG_PLL_VIDEO_CLR_DIV_SELECT_MASK | + CCM_ANALOG_PLL_VIDEO_CLR_POST_DIV_SEL_MASK | + CCM_ANALOG_PLL_VIDEO_CLR_TEST_DIV_SELECT_MASK, + &ccm_anatop->pll_video_clr); + + /* Set div, num and denom */ + switch (post_div) { + case 1: + writel(CCM_ANALOG_PLL_VIDEO_SET_DIV_SELECT(pll_div) | + CCM_ANALOG_PLL_VIDEO_SET_TEST_DIV_SELECT(0x1) | + CCM_ANALOG_PLL_VIDEO_SET_POST_DIV_SEL(0x0), + &ccm_anatop->pll_video_set); + break; + case 2: + writel(CCM_ANALOG_PLL_VIDEO_SET_DIV_SELECT(pll_div) | + CCM_ANALOG_PLL_VIDEO_SET_TEST_DIV_SELECT(0x0) | + CCM_ANALOG_PLL_VIDEO_SET_POST_DIV_SEL(0x0), + &ccm_anatop->pll_video_set); + break; + case 3: + writel(CCM_ANALOG_PLL_VIDEO_SET_DIV_SELECT(pll_div) | + CCM_ANALOG_PLL_VIDEO_SET_TEST_DIV_SELECT(0x0) | + CCM_ANALOG_PLL_VIDEO_SET_POST_DIV_SEL(0x1), + &ccm_anatop->pll_video_set); + break; + case 4: + writel(CCM_ANALOG_PLL_VIDEO_SET_DIV_SELECT(pll_div) | + CCM_ANALOG_PLL_VIDEO_SET_TEST_DIV_SELECT(0x0) | + CCM_ANALOG_PLL_VIDEO_SET_POST_DIV_SEL(0x3), + &ccm_anatop->pll_video_set); + break; + case 0: + default: + writel(CCM_ANALOG_PLL_VIDEO_SET_DIV_SELECT(pll_div) | + CCM_ANALOG_PLL_VIDEO_SET_TEST_DIV_SELECT(0x2) | + CCM_ANALOG_PLL_VIDEO_SET_POST_DIV_SEL(0x0), + &ccm_anatop->pll_video_set); + break; + } + + writel(CCM_ANALOG_PLL_VIDEO_NUM_A(pll_num), + &ccm_anatop->pll_video_num); + + writel(CCM_ANALOG_PLL_VIDEO_DENOM_B(pll_denom), + &ccm_anatop->pll_video_denom); + + /* Wait PLL5 lock */ + start = get_timer(0); /* Get current timestamp */ + + do { + reg = readl(&ccm_anatop->pll_video); + if (reg & CCM_ANALOG_PLL_VIDEO_LOCK_MASK) { + /* Enable PLL out */ + writel(CCM_ANALOG_PLL_VIDEO_CLR_ENABLE_CLK_MASK, + &ccm_anatop->pll_video_set); + return 0; + } + } while (get_timer(0) < (start + 10)); /* Wait 10ms */ + + printf("Lock PLL5 timeout\n"); + + return 1; +} + +int set_clk_qspi(void) +{ + u32 target; + + /* disable the clock gate first */ + clock_enable(CCGR_QSPI, 0); + + /* 49M: 392/2/4 */ + target = CLK_ROOT_ON | QSPI_CLK_ROOT_FROM_PLL_SYS_PFD4_CLK | + CLK_ROOT_PRE_DIV(CLK_ROOT_PRE_DIV1) | + CLK_ROOT_POST_DIV(CLK_ROOT_POST_DIV2); + clock_set_target_val(QSPI_CLK_ROOT, target); + + /* enable the clock gate */ + clock_enable(CCGR_QSPI, 1); + + return 0; +} + +int set_clk_nand(void) +{ + u32 target; + + /* disable the clock gate first */ + clock_enable(CCGR_RAWNAND, 0); + + enable_pll_enet(); + /* 100: 500/5 */ + target = CLK_ROOT_ON | NAND_CLK_ROOT_FROM_PLL_ENET_MAIN_500M_CLK | + CLK_ROOT_PRE_DIV(CLK_ROOT_PRE_DIV1) | + CLK_ROOT_POST_DIV(CLK_ROOT_POST_DIV5); + clock_set_target_val(NAND_CLK_ROOT, target); + + /* enable the clock gate */ + clock_enable(CCGR_RAWNAND, 1); + + return 0; +} + +void mxs_set_lcdclk(uint32_t base_addr, uint32_t freq) +{ + u32 hck = MXC_HCLK/1000; + u32 min = hck * 27; + u32 max = hck * 54; + u32 temp, best = 0; + u32 i, j, pred = 1, postd = 1; + u32 pll_div, pll_num, pll_denom, post_div = 0; + u32 target; + + debug("mxs_set_lcdclk, freq = %d\n", freq); + + clock_enable(CCGR_LCDIF, 0); + + temp = (freq * 8 * 8); + if (temp < min) { + for (i = 1; i <= 4; i++) { + if ((temp * (1 << i)) > min) { + post_div = i; + freq = (freq * (1 << i)); + break; + } + } + + if (5 == i) { + printf("Fail to set rate to %u kHz", freq); + return; + } + } + + for (i = 1; i <= 8; i++) { + for (j = 1; j <= 8; j++) { + temp = freq * i * j; + if (temp > max || temp < min) + continue; + + if (best == 0 || temp < best) { + best = temp; + pred = i; + postd = j; + } + } + } + + if (best == 0) { + printf("Fail to set rate to %u kHz", freq); + return; + } + + debug("best %d, pred = %d, postd = %d\n", best, pred, postd); + + pll_div = best / hck; + pll_denom = 1000000; + pll_num = (best - hck * pll_div) * pll_denom / hck; + + if (enable_pll_video(pll_div, pll_num, pll_denom, post_div)) + return; + + target = CLK_ROOT_ON | LCDIF_PIXEL_CLK_ROOT_FROM_PLL_VIDEO_MAIN_CLK | + CLK_ROOT_PRE_DIV((pred - 1)) | CLK_ROOT_POST_DIV((postd - 1)); + clock_set_target_val(LCDIF_PIXEL_CLK_ROOT, target); + + clock_enable(CCGR_LCDIF, 1); +} + +#ifdef CONFIG_FEC_MXC +int set_clk_enet(enum enet_freq type) +{ + u32 target; + int ret; + u32 enet1_ref, enet2_ref; + + /* disable the clock first */ + clock_enable(CCGR_ENET1, 0); + clock_enable(CCGR_ENET2, 0); + + switch (type) { + case ENET_125MHZ: + enet1_ref = ENET1_REF_CLK_ROOT_FROM_PLL_ENET_MAIN_125M_CLK; + enet2_ref = ENET2_REF_CLK_ROOT_FROM_PLL_ENET_MAIN_125M_CLK; + break; + case ENET_50MHZ: + enet1_ref = ENET1_REF_CLK_ROOT_FROM_PLL_ENET_MAIN_50M_CLK; + enet2_ref = ENET2_REF_CLK_ROOT_FROM_PLL_ENET_MAIN_50M_CLK; + break; + case ENET_25MHZ: + enet1_ref = ENET1_REF_CLK_ROOT_FROM_PLL_ENET_MAIN_25M_CLK; + enet2_ref = ENET2_REF_CLK_ROOT_FROM_PLL_ENET_MAIN_25M_CLK; + break; + default: + return -EINVAL; + } + + ret = enable_pll_enet(); + if (ret != 0) + return ret; + + /* set enet axi clock 196M: 392/2 */ + target = CLK_ROOT_ON | ENET_AXI_CLK_ROOT_FROM_PLL_SYS_PFD4_CLK | + CLK_ROOT_PRE_DIV(CLK_ROOT_PRE_DIV1) | + CLK_ROOT_POST_DIV(CLK_ROOT_POST_DIV2); + clock_set_target_val(ENET_AXI_CLK_ROOT, target); + + target = CLK_ROOT_ON | enet1_ref | + CLK_ROOT_PRE_DIV(CLK_ROOT_PRE_DIV1) | + CLK_ROOT_POST_DIV(CLK_ROOT_POST_DIV1); + clock_set_target_val(ENET1_REF_CLK_ROOT, target); + + target = CLK_ROOT_ON | ENET1_TIME_CLK_ROOT_FROM_PLL_ENET_MAIN_100M_CLK | + CLK_ROOT_PRE_DIV(CLK_ROOT_PRE_DIV1) | + CLK_ROOT_POST_DIV(CLK_ROOT_POST_DIV4); + clock_set_target_val(ENET1_TIME_CLK_ROOT, target); + + target = CLK_ROOT_ON | enet2_ref | + CLK_ROOT_PRE_DIV(CLK_ROOT_PRE_DIV1) | + CLK_ROOT_POST_DIV(CLK_ROOT_POST_DIV1); + clock_set_target_val(ENET2_REF_CLK_ROOT, target); + + target = CLK_ROOT_ON | ENET2_TIME_CLK_ROOT_FROM_PLL_ENET_MAIN_100M_CLK | + CLK_ROOT_PRE_DIV(CLK_ROOT_PRE_DIV1) | + CLK_ROOT_POST_DIV(CLK_ROOT_POST_DIV4); + clock_set_target_val(ENET2_TIME_CLK_ROOT, target); + +#ifdef CONFIG_FEC_MXC_25M_REF_CLK + target = CLK_ROOT_ON | + ENET_PHY_REF_CLK_ROOT_FROM_PLL_ENET_MAIN_25M_CLK | + CLK_ROOT_PRE_DIV(CLK_ROOT_PRE_DIV1) | + CLK_ROOT_POST_DIV(CLK_ROOT_POST_DIV1); + clock_set_target_val(ENET_PHY_REF_CLK_ROOT, target); +#endif + /* enable clock */ + clock_enable(CCGR_ENET1, 1); + clock_enable(CCGR_ENET2, 1); + + return 0; +} +#endif + +/* Configure PLL/PFD freq */ +void clock_init(void) +{ +/* Rom has enabled PLL_ARM, PLL_DDR, PLL_SYS, PLL_ENET + * In u-boot, we have to: + * 1. Configure PFD3- PFD7 for freq we needed in u-boot + * 2. Set clock root for peripherals (ip channel) used in u-boot but without set rate + * interface. The clocks for these peripherals are enabled after this intialization. + * 3. Other peripherals with set clock rate interface does not be set in this function. + */ + u32 reg; + + /* + * Configure PFD4 to 392M + * 480M * 18 / 0x16 = 392M + */ + reg = readl(&ccm_anatop->pfd_480b); + + reg &= ~(ANATOP_PFD480B_PFD4_FRAC_MASK | + CCM_ANALOG_PFD_480B_PFD4_DIV1_CLKGATE_MASK); + reg |= ANATOP_PFD480B_PFD4_FRAC_392M_VAL; + + writel(reg, &ccm_anatop->pfd_480b); + + init_clk_esdhc(); + init_clk_uart(); + init_clk_weim(); + init_clk_ecspi(); + init_clk_wdog(); +#ifdef CONFIG_MXC_EPDC + init_clk_epdc(); +#endif + + enable_usboh3_clk(1); + + clock_enable(CCGR_SNVS, 1); + +#ifdef CONFIG_NAND_MXS + clock_enable(CCGR_RAWNAND, 1); +#endif + + if (IS_ENABLED(CONFIG_IMX_RDC)) { + clock_enable(CCGR_RDC, 1); + clock_enable(CCGR_SEMA1, 1); + clock_enable(CCGR_SEMA2, 1); + } +} + +#ifdef CONFIG_IMX_HAB +void hab_caam_clock_enable(unsigned char enable) +{ + if (enable) + clock_enable(CCGR_CAAM, 1); + else + clock_enable(CCGR_CAAM, 0); +} +#endif + +#ifdef CONFIG_MXC_EPDC +void epdc_clock_enable(void) +{ + clock_enable(CCGR_EPDC, 1); +} +void epdc_clock_disable(void) +{ + clock_enable(CCGR_EPDC, 0); +} +#endif + +#ifndef CONFIG_SPL_BUILD +/* + * Dump some core clockes. + */ +int do_mx7_showclocks(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + u32 freq; + freq = decode_pll(PLL_CORE, MXC_HCLK); + printf("PLL_CORE %8d MHz\n", freq / 1000000); + freq = decode_pll(PLL_SYS, MXC_HCLK); + printf("PLL_SYS %8d MHz\n", freq / 1000000); + freq = decode_pll(PLL_ENET, MXC_HCLK); + printf("PLL_NET %8d MHz\n", freq / 1000000); + + printf("\n"); + + printf("IPG %8u kHz\n", mxc_get_clock(MXC_IPG_CLK) / 1000); + printf("UART %8u kHz\n", mxc_get_clock(MXC_UART_CLK) / 1000); +#ifdef CONFIG_MXC_SPI + printf("CSPI %8u kHz\n", mxc_get_clock(MXC_CSPI_CLK) / 1000); +#endif + printf("AHB %8u kHz\n", mxc_get_clock(MXC_AHB_CLK) / 1000); + printf("AXI %8u kHz\n", mxc_get_clock(MXC_AXI_CLK) / 1000); + printf("DDR %8u kHz\n", mxc_get_clock(MXC_DDR_CLK) / 1000); + printf("USDHC1 %8u kHz\n", mxc_get_clock(MXC_ESDHC_CLK) / 1000); + printf("USDHC2 %8u kHz\n", mxc_get_clock(MXC_ESDHC2_CLK) / 1000); + printf("USDHC3 %8u kHz\n", mxc_get_clock(MXC_ESDHC3_CLK) / 1000); + + return 0; +} + +U_BOOT_CMD( + clocks, CONFIG_SYS_MAXARGS, 1, do_mx7_showclocks, + "display clocks", + "" +); +#endif diff --git a/roms/u-boot/arch/arm/mach-imx/mx7/clock_slice.c b/roms/u-boot/arch/arm/mach-imx/mx7/clock_slice.c new file mode 100644 index 000000000..dd731d949 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/mx7/clock_slice.c @@ -0,0 +1,756 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2015 Freescale Semiconductor, Inc. + * + * Author: + * Peng Fan <Peng.Fan@freescale.com> + */ + +#include <common.h> +#include <div64.h> +#include <asm/io.h> +#include <linux/errno.h> +#include <asm/arch/imx-regs.h> +#include <asm/arch/crm_regs.h> +#include <asm/arch/clock.h> +#include <asm/arch/sys_proto.h> + +struct mxc_ccm_reg *imx_ccm = (struct mxc_ccm_reg *)CCM_BASE_ADDR; + +static struct clk_root_map root_array[] = { + {ARM_A7_CLK_ROOT, CCM_CORE_CHANNEL, + {OSC_24M_CLK, PLL_ARM_MAIN_800M_CLK, PLL_ENET_MAIN_500M_CLK, + PLL_DRAM_MAIN_1066M_CLK, PLL_SYS_MAIN_480M_CLK, + PLL_SYS_PFD0_392M_CLK, PLL_AUDIO_MAIN_CLK, PLL_USB_MAIN_480M_CLK} + }, + {ARM_M4_CLK_ROOT, CCM_BUS_CHANNEL, + {OSC_24M_CLK, PLL_SYS_MAIN_240M_CLK, PLL_ENET_MAIN_250M_CLK, + PLL_SYS_PFD2_270M_CLK, PLL_DRAM_MAIN_533M_CLK, PLL_AUDIO_MAIN_CLK, + PLL_VIDEO_MAIN_CLK, PLL_USB_MAIN_480M_CLK} + }, + {ARM_M0_CLK_ROOT, CCM_BUS_CHANNEL, + {OSC_24M_CLK, PLL_SYS_MAIN_120M_CLK, PLL_ENET_MAIN_125M_CLK, + PLL_SYS_PFD2_135M_CLK, PLL_DRAM_MAIN_533M_CLK, PLL_AUDIO_MAIN_CLK, + PLL_VIDEO_MAIN_CLK, PLL_USB_MAIN_480M_CLK} + }, + {MAIN_AXI_CLK_ROOT, CCM_BUS_CHANNEL, + {OSC_24M_CLK, PLL_SYS_PFD1_332M_CLK, PLL_DRAM_MAIN_533M_CLK, + PLL_ENET_MAIN_250M_CLK, PLL_SYS_PFD5_CLK, PLL_AUDIO_MAIN_CLK, + PLL_VIDEO_MAIN_CLK, PLL_SYS_PFD7_CLK} + }, + {DISP_AXI_CLK_ROOT, CCM_BUS_CHANNEL, + {OSC_24M_CLK, PLL_SYS_PFD1_332M_CLK, PLL_DRAM_MAIN_533M_CLK, + PLL_ENET_MAIN_250M_CLK, PLL_SYS_PFD6_CLK, PLL_SYS_PFD7_CLK, + PLL_AUDIO_MAIN_CLK, PLL_VIDEO_MAIN_CLK} + }, + {ENET_AXI_CLK_ROOT, CCM_IP_CHANNEL, + {OSC_24M_CLK, PLL_SYS_PFD2_270M_CLK, PLL_DRAM_MAIN_533M_CLK, + PLL_ENET_MAIN_250M_CLK, PLL_SYS_MAIN_240M_CLK, PLL_AUDIO_MAIN_CLK, + PLL_VIDEO_MAIN_CLK, PLL_SYS_PFD4_CLK} + }, + {NAND_USDHC_BUS_CLK_ROOT, CCM_IP_CHANNEL, + {OSC_24M_CLK, PLL_SYS_PFD2_270M_CLK, PLL_DRAM_MAIN_533M_CLK, + PLL_SYS_MAIN_240M_CLK, PLL_SYS_PFD2_135M_CLK, PLL_SYS_PFD6_CLK, + PLL_ENET_MAIN_250M_CLK, PLL_AUDIO_MAIN_CLK} + }, + {AHB_CLK_ROOT, CCM_AHB_CHANNEL, + {OSC_24M_CLK, PLL_SYS_PFD2_270M_CLK, PLL_DRAM_MAIN_533M_CLK, + PLL_SYS_PFD0_392M_CLK, PLL_ENET_MAIN_125M_CLK, PLL_USB_MAIN_480M_CLK, + PLL_AUDIO_MAIN_CLK, PLL_VIDEO_MAIN_CLK} + }, + {DRAM_PHYM_CLK_ROOT, CCM_DRAM_PHYM_CHANNEL, + {PLL_DRAM_MAIN_1066M_CLK, DRAM_PHYM_ALT_CLK_ROOT} + }, + {DRAM_CLK_ROOT, CCM_DRAM_CHANNEL, + {PLL_DRAM_MAIN_1066M_CLK, DRAM_ALT_CLK_ROOT} + }, + {DRAM_PHYM_ALT_CLK_ROOT, CCM_IP_CHANNEL, + {OSC_24M_CLK, PLL_DRAM_MAIN_533M_CLK, PLL_SYS_MAIN_480M_CLK, + PLL_ENET_MAIN_500M_CLK, PLL_USB_MAIN_480M_CLK, PLL_SYS_PFD7_CLK, + PLL_AUDIO_MAIN_CLK, PLL_VIDEO_MAIN_CLK} + }, + {DRAM_ALT_CLK_ROOT, CCM_IP_CHANNEL, + {OSC_24M_CLK, PLL_DRAM_MAIN_533M_CLK, PLL_SYS_MAIN_480M_CLK, + PLL_ENET_MAIN_500M_CLK, PLL_ENET_MAIN_250M_CLK, + PLL_SYS_PFD0_392M_CLK, PLL_AUDIO_MAIN_CLK, PLL_SYS_PFD2_270M_CLK} + }, + {USB_HSIC_CLK_ROOT, CCM_IP_CHANNEL, + {OSC_24M_CLK, PLL_SYS_MAIN_480M_CLK, PLL_USB_MAIN_480M_CLK, + PLL_SYS_PFD3_CLK, PLL_SYS_PFD4_CLK, PLL_SYS_PFD5_CLK, + PLL_SYS_PFD6_CLK, PLL_SYS_PFD7_CLK} + }, + {PCIE_CTRL_CLK_ROOT, CCM_IP_CHANNEL, + {OSC_24M_CLK, PLL_ENET_MAIN_250M_CLK, PLL_SYS_MAIN_240M_CLK, + PLL_SYS_PFD2_270M_CLK, PLL_DRAM_MAIN_533M_CLK, + PLL_ENET_MAIN_500M_CLK, PLL_SYS_PFD1_332M_CLK, PLL_SYS_PFD6_CLK} + }, + {PCIE_PHY_CLK_ROOT, CCM_IP_CHANNEL, + {OSC_24M_CLK, PLL_ENET_MAIN_100M_CLK, PLL_ENET_MAIN_500M_CLK, + EXT_CLK_1, EXT_CLK_2, EXT_CLK_3, + EXT_CLK_4, PLL_SYS_PFD0_392M_CLK} + }, + {EPDC_PIXEL_CLK_ROOT, CCM_IP_CHANNEL, + {OSC_24M_CLK, PLL_SYS_PFD1_332M_CLK, PLL_DRAM_MAIN_533M_CLK, + PLL_SYS_MAIN_480M_CLK, PLL_SYS_PFD5_CLK, PLL_SYS_PFD6_CLK, + PLL_SYS_PFD7_CLK, PLL_VIDEO_MAIN_CLK} + }, + {LCDIF_PIXEL_CLK_ROOT, CCM_IP_CHANNEL, + {OSC_24M_CLK, PLL_SYS_PFD5_CLK, PLL_DRAM_MAIN_533M_CLK, + EXT_CLK_3, PLL_SYS_PFD4_CLK, PLL_SYS_PFD2_270M_CLK, + PLL_VIDEO_MAIN_CLK, PLL_USB_MAIN_480M_CLK} + }, + {MIPI_DSI_EXTSER_CLK_ROOT, CCM_IP_CHANNEL, + {OSC_24M_CLK, PLL_SYS_PFD5_CLK, PLL_SYS_PFD3_CLK, + PLL_SYS_MAIN_480M_CLK, PLL_SYS_PFD0_196M_CLK, PLL_DRAM_MAIN_533M_CLK, + PLL_VIDEO_MAIN_CLK, PLL_AUDIO_MAIN_CLK} + }, + {MIPI_CSI_WARP_CLK_ROOT, CCM_IP_CHANNEL, + {OSC_24M_CLK, PLL_SYS_PFD4_CLK, PLL_SYS_PFD3_CLK, + PLL_SYS_MAIN_480M_CLK, PLL_SYS_PFD0_196M_CLK, PLL_DRAM_MAIN_533M_CLK, + PLL_VIDEO_MAIN_CLK, PLL_AUDIO_MAIN_CLK} + }, + {MIPI_DPHY_REF_CLK_ROOT, CCM_IP_CHANNEL, + {OSC_24M_CLK, PLL_SYS_MAIN_120M_CLK, PLL_DRAM_MAIN_533M_CLK, + PLL_SYS_PFD5_CLK, REF_1M_CLK, EXT_CLK_2, + PLL_VIDEO_MAIN_CLK, EXT_CLK_3} + }, + {SAI1_CLK_ROOT, CCM_IP_CHANNEL, + {OSC_24M_CLK, PLL_SYS_PFD2_135M_CLK, PLL_AUDIO_MAIN_CLK, + PLL_DRAM_MAIN_533M_CLK, PLL_VIDEO_MAIN_CLK, PLL_SYS_PFD4_CLK, + PLL_ENET_MAIN_125M_CLK, EXT_CLK_2} + }, + {SAI2_CLK_ROOT, CCM_IP_CHANNEL, + {OSC_24M_CLK, PLL_SYS_PFD2_135M_CLK, PLL_AUDIO_MAIN_CLK, + PLL_DRAM_MAIN_533M_CLK, PLL_VIDEO_MAIN_CLK, PLL_SYS_PFD4_CLK, + PLL_ENET_MAIN_125M_CLK, EXT_CLK_2} + }, + {SAI3_CLK_ROOT, CCM_IP_CHANNEL, + {OSC_24M_CLK, PLL_SYS_PFD2_135M_CLK, PLL_AUDIO_MAIN_CLK, + PLL_DRAM_MAIN_533M_CLK, PLL_VIDEO_MAIN_CLK, PLL_SYS_PFD4_CLK, + PLL_ENET_MAIN_125M_CLK, EXT_CLK_3} + }, + {SPDIF_CLK_ROOT, CCM_IP_CHANNEL, + {OSC_24M_CLK, PLL_SYS_PFD2_135M_CLK, PLL_AUDIO_MAIN_CLK, + PLL_DRAM_MAIN_533M_CLK, PLL_VIDEO_MAIN_CLK, PLL_SYS_PFD4_CLK, + PLL_ENET_MAIN_125M_CLK, EXT_CLK_3} + }, + {ENET1_REF_CLK_ROOT, CCM_IP_CHANNEL, + {OSC_24M_CLK, PLL_ENET_MAIN_125M_CLK, PLL_ENET_MAIN_50M_CLK, + PLL_ENET_MAIN_25M_CLK, PLL_SYS_MAIN_120M_CLK, PLL_AUDIO_MAIN_CLK, + PLL_VIDEO_MAIN_CLK, EXT_CLK_4} + }, + {ENET1_TIME_CLK_ROOT, CCM_IP_CHANNEL, + {OSC_24M_CLK, PLL_ENET_MAIN_100M_CLK, PLL_AUDIO_MAIN_CLK, + EXT_CLK_1, EXT_CLK_2, EXT_CLK_3, + EXT_CLK_4, PLL_VIDEO_MAIN_CLK} + }, + {ENET2_REF_CLK_ROOT, CCM_IP_CHANNEL, + {OSC_24M_CLK, PLL_ENET_MAIN_125M_CLK, PLL_ENET_MAIN_50M_CLK, + PLL_ENET_MAIN_25M_CLK, PLL_SYS_MAIN_120M_CLK, PLL_AUDIO_MAIN_CLK, + PLL_VIDEO_MAIN_CLK, EXT_CLK_4} + }, + {ENET2_TIME_CLK_ROOT, CCM_IP_CHANNEL, + {OSC_24M_CLK, PLL_ENET_MAIN_100M_CLK, PLL_AUDIO_MAIN_CLK, + EXT_CLK_1, EXT_CLK_2, EXT_CLK_3, + EXT_CLK_4, PLL_VIDEO_MAIN_CLK} + }, + {ENET_PHY_REF_CLK_ROOT, CCM_IP_CHANNEL, + {OSC_24M_CLK, PLL_ENET_MAIN_25M_CLK, PLL_ENET_MAIN_50M_CLK, + PLL_ENET_MAIN_125M_CLK, PLL_DRAM_MAIN_533M_CLK, PLL_AUDIO_MAIN_CLK, + PLL_VIDEO_MAIN_CLK, PLL_SYS_PFD3_CLK} + }, + {EIM_CLK_ROOT, CCM_IP_CHANNEL, + {OSC_24M_CLK, PLL_SYS_PFD2_135M_CLK, PLL_SYS_MAIN_120M_CLK, + PLL_DRAM_MAIN_533M_CLK, PLL_SYS_PFD2_270M_CLK, PLL_SYS_PFD3_CLK, + PLL_ENET_MAIN_125M_CLK, PLL_USB_MAIN_480M_CLK} + }, + {NAND_CLK_ROOT, CCM_IP_CHANNEL, + {OSC_24M_CLK, PLL_SYS_MAIN_480M_CLK, PLL_DRAM_MAIN_533M_CLK, + PLL_SYS_PFD0_392M_CLK, PLL_SYS_PFD3_CLK, PLL_ENET_MAIN_500M_CLK, + PLL_ENET_MAIN_250M_CLK, PLL_VIDEO_MAIN_CLK} + }, + {QSPI_CLK_ROOT, CCM_IP_CHANNEL, + {OSC_24M_CLK, PLL_SYS_PFD4_CLK, PLL_DRAM_MAIN_533M_CLK, + PLL_ENET_MAIN_500M_CLK, PLL_SYS_PFD3_CLK, PLL_SYS_PFD2_270M_CLK, + PLL_SYS_PFD6_CLK, PLL_SYS_PFD7_CLK} + }, + {USDHC1_CLK_ROOT, CCM_IP_CHANNEL, + {OSC_24M_CLK, PLL_SYS_PFD0_392M_CLK, PLL_DRAM_MAIN_533M_CLK, + PLL_ENET_MAIN_500M_CLK, PLL_SYS_PFD4_CLK, PLL_SYS_PFD2_270M_CLK, + PLL_SYS_PFD6_CLK, PLL_SYS_PFD7_CLK} + }, + {USDHC2_CLK_ROOT, CCM_IP_CHANNEL, + {OSC_24M_CLK, PLL_SYS_PFD0_392M_CLK, PLL_DRAM_MAIN_533M_CLK, + PLL_ENET_MAIN_500M_CLK, PLL_SYS_PFD4_CLK, PLL_SYS_PFD2_270M_CLK, + PLL_SYS_PFD6_CLK, PLL_SYS_PFD7_CLK} + }, + {USDHC3_CLK_ROOT, CCM_IP_CHANNEL, + {OSC_24M_CLK, PLL_SYS_PFD0_392M_CLK, PLL_DRAM_MAIN_533M_CLK, + PLL_ENET_MAIN_500M_CLK, PLL_SYS_PFD4_CLK, PLL_SYS_PFD2_270M_CLK, + PLL_SYS_PFD6_CLK, PLL_SYS_PFD7_CLK} + }, + {CAN1_CLK_ROOT, CCM_IP_CHANNEL, + {OSC_24M_CLK, PLL_SYS_MAIN_120M_CLK, PLL_DRAM_MAIN_533M_CLK, + PLL_SYS_MAIN_480M_CLK, PLL_ENET_MAIN_40M_CLK, PLL_USB_MAIN_480M_CLK, + EXT_CLK_1, EXT_CLK_4} + }, + {CAN2_CLK_ROOT, CCM_IP_CHANNEL, + {OSC_24M_CLK, PLL_SYS_MAIN_120M_CLK, PLL_DRAM_MAIN_533M_CLK, + PLL_SYS_MAIN_480M_CLK, PLL_ENET_MAIN_40M_CLK, PLL_USB_MAIN_480M_CLK, + EXT_CLK_1, EXT_CLK_3} + }, + {I2C1_CLK_ROOT, CCM_IP_CHANNEL, + {OSC_24M_CLK, PLL_SYS_MAIN_120M_CLK, PLL_ENET_MAIN_50M_CLK, + PLL_DRAM_MAIN_533M_CLK, PLL_AUDIO_MAIN_CLK, PLL_VIDEO_MAIN_CLK, + PLL_USB_MAIN_480M_CLK, PLL_SYS_PFD2_135M_CLK} + }, + {I2C2_CLK_ROOT, CCM_IP_CHANNEL, + {OSC_24M_CLK, PLL_SYS_MAIN_120M_CLK, PLL_ENET_MAIN_50M_CLK, + PLL_DRAM_MAIN_533M_CLK, PLL_AUDIO_MAIN_CLK, PLL_VIDEO_MAIN_CLK, + PLL_USB_MAIN_480M_CLK, PLL_SYS_PFD2_135M_CLK} + }, + {I2C3_CLK_ROOT, CCM_IP_CHANNEL, + {OSC_24M_CLK, PLL_SYS_MAIN_120M_CLK, PLL_ENET_MAIN_50M_CLK, + PLL_DRAM_MAIN_533M_CLK, PLL_AUDIO_MAIN_CLK, PLL_VIDEO_MAIN_CLK, + PLL_USB_MAIN_480M_CLK, PLL_SYS_PFD2_135M_CLK} + }, + {I2C4_CLK_ROOT, CCM_IP_CHANNEL, + {OSC_24M_CLK, PLL_SYS_MAIN_120M_CLK, PLL_ENET_MAIN_50M_CLK, + PLL_DRAM_MAIN_533M_CLK, PLL_AUDIO_MAIN_CLK, PLL_VIDEO_MAIN_CLK, + PLL_USB_MAIN_480M_CLK, PLL_SYS_PFD2_135M_CLK} + }, + {UART1_CLK_ROOT, CCM_IP_CHANNEL, + {OSC_24M_CLK, PLL_SYS_MAIN_240M_CLK, PLL_ENET_MAIN_40M_CLK, + PLL_ENET_MAIN_100M_CLK, PLL_SYS_MAIN_480M_CLK, EXT_CLK_2, + EXT_CLK_4, PLL_USB_MAIN_480M_CLK} + }, + {UART2_CLK_ROOT, CCM_IP_CHANNEL, + {OSC_24M_CLK, PLL_SYS_MAIN_240M_CLK, PLL_ENET_MAIN_40M_CLK, + PLL_ENET_MAIN_100M_CLK, PLL_SYS_MAIN_480M_CLK, EXT_CLK_2, + EXT_CLK_3, PLL_USB_MAIN_480M_CLK} + }, + {UART3_CLK_ROOT, CCM_IP_CHANNEL, + {OSC_24M_CLK, PLL_SYS_MAIN_240M_CLK, PLL_ENET_MAIN_40M_CLK, + PLL_ENET_MAIN_100M_CLK, PLL_SYS_MAIN_480M_CLK, EXT_CLK_2, + EXT_CLK_4, PLL_USB_MAIN_480M_CLK} + }, + {UART4_CLK_ROOT, CCM_IP_CHANNEL, + {OSC_24M_CLK, PLL_SYS_MAIN_240M_CLK, PLL_ENET_MAIN_40M_CLK, + PLL_ENET_MAIN_100M_CLK, PLL_SYS_MAIN_480M_CLK, EXT_CLK_2, + EXT_CLK_3, PLL_USB_MAIN_480M_CLK} + }, + {UART5_CLK_ROOT, CCM_IP_CHANNEL, + {OSC_24M_CLK, PLL_SYS_MAIN_240M_CLK, PLL_ENET_MAIN_40M_CLK, + PLL_ENET_MAIN_100M_CLK, PLL_SYS_MAIN_480M_CLK, EXT_CLK_2, + EXT_CLK_4, PLL_USB_MAIN_480M_CLK} + }, + {UART6_CLK_ROOT, CCM_IP_CHANNEL, + {OSC_24M_CLK, PLL_SYS_MAIN_240M_CLK, PLL_ENET_MAIN_40M_CLK, + PLL_ENET_MAIN_100M_CLK, PLL_SYS_MAIN_480M_CLK, EXT_CLK_2, + EXT_CLK_3, PLL_USB_MAIN_480M_CLK} + }, + {UART7_CLK_ROOT, CCM_IP_CHANNEL, + {OSC_24M_CLK, PLL_SYS_MAIN_240M_CLK, PLL_ENET_MAIN_40M_CLK, + PLL_ENET_MAIN_100M_CLK, PLL_SYS_MAIN_480M_CLK, EXT_CLK_2, + EXT_CLK_4, PLL_USB_MAIN_480M_CLK} + }, + {ECSPI1_CLK_ROOT, CCM_IP_CHANNEL, + {OSC_24M_CLK, PLL_SYS_MAIN_240M_CLK, PLL_ENET_MAIN_40M_CLK, + PLL_SYS_MAIN_120M_CLK, PLL_SYS_MAIN_480M_CLK, PLL_SYS_PFD4_CLK, + PLL_ENET_MAIN_250M_CLK, PLL_USB_MAIN_480M_CLK} + }, + {ECSPI2_CLK_ROOT, CCM_IP_CHANNEL, + {OSC_24M_CLK, PLL_SYS_MAIN_240M_CLK, PLL_ENET_MAIN_40M_CLK, + PLL_SYS_MAIN_120M_CLK, PLL_SYS_MAIN_480M_CLK, PLL_SYS_PFD4_CLK, + PLL_ENET_MAIN_250M_CLK, PLL_USB_MAIN_480M_CLK} + }, + {ECSPI3_CLK_ROOT, CCM_IP_CHANNEL, + {OSC_24M_CLK, PLL_SYS_MAIN_240M_CLK, PLL_ENET_MAIN_40M_CLK, + PLL_SYS_MAIN_120M_CLK, PLL_SYS_MAIN_480M_CLK, PLL_SYS_PFD4_CLK, + PLL_ENET_MAIN_250M_CLK, PLL_USB_MAIN_480M_CLK} + }, + {ECSPI4_CLK_ROOT, CCM_IP_CHANNEL, + {OSC_24M_CLK, PLL_SYS_MAIN_240M_CLK, PLL_ENET_MAIN_40M_CLK, + PLL_SYS_MAIN_120M_CLK, PLL_SYS_MAIN_480M_CLK, PLL_SYS_PFD4_CLK, + PLL_ENET_MAIN_250M_CLK, PLL_USB_MAIN_480M_CLK} + }, + {PWM1_CLK_ROOT, CCM_IP_CHANNEL, + {OSC_24M_CLK, PLL_ENET_MAIN_100M_CLK, PLL_SYS_MAIN_120M_CLK, + PLL_ENET_MAIN_40M_CLK, PLL_AUDIO_MAIN_CLK, EXT_CLK_1, + REF_1M_CLK, PLL_VIDEO_MAIN_CLK} + }, + {PWM2_CLK_ROOT, CCM_IP_CHANNEL, + {OSC_24M_CLK, PLL_ENET_MAIN_100M_CLK, PLL_SYS_MAIN_120M_CLK, + PLL_ENET_MAIN_40M_CLK, PLL_AUDIO_MAIN_CLK, EXT_CLK_1, + REF_1M_CLK, PLL_VIDEO_MAIN_CLK} + }, + {PWM3_CLK_ROOT, CCM_IP_CHANNEL, + {OSC_24M_CLK, PLL_ENET_MAIN_100M_CLK, PLL_SYS_MAIN_120M_CLK, + PLL_ENET_MAIN_40M_CLK, PLL_AUDIO_MAIN_CLK, EXT_CLK_2, + REF_1M_CLK, PLL_VIDEO_MAIN_CLK} + }, + {PWM4_CLK_ROOT, CCM_IP_CHANNEL, + {OSC_24M_CLK, PLL_ENET_MAIN_100M_CLK, PLL_SYS_MAIN_120M_CLK, + PLL_ENET_MAIN_40M_CLK, PLL_AUDIO_MAIN_CLK, EXT_CLK_2, + REF_1M_CLK, PLL_VIDEO_MAIN_CLK} + }, + {FLEXTIMER1_CLK_ROOT, CCM_IP_CHANNEL, + {OSC_24M_CLK, PLL_ENET_MAIN_100M_CLK, PLL_SYS_MAIN_120M_CLK, + PLL_ENET_MAIN_40M_CLK, PLL_AUDIO_MAIN_CLK, EXT_CLK_3, + REF_1M_CLK, PLL_VIDEO_MAIN_CLK} + }, + {FLEXTIMER2_CLK_ROOT, CCM_IP_CHANNEL, + {OSC_24M_CLK, PLL_ENET_MAIN_100M_CLK, PLL_SYS_MAIN_120M_CLK, + PLL_ENET_MAIN_40M_CLK, PLL_AUDIO_MAIN_CLK, EXT_CLK_3, + REF_1M_CLK, PLL_VIDEO_MAIN_CLK} + }, + {SIM1_CLK_ROOT, CCM_IP_CHANNEL, + {OSC_24M_CLK, PLL_SYS_PFD2_135M_CLK, PLL_SYS_MAIN_120M_CLK, + PLL_DRAM_MAIN_533M_CLK, PLL_USB_MAIN_480M_CLK, PLL_AUDIO_MAIN_CLK, + PLL_ENET_MAIN_125M_CLK, PLL_SYS_PFD7_CLK} + }, + {SIM2_CLK_ROOT, CCM_IP_CHANNEL, + {OSC_24M_CLK, PLL_SYS_PFD2_135M_CLK, PLL_SYS_MAIN_120M_CLK, + PLL_DRAM_MAIN_533M_CLK, PLL_USB_MAIN_480M_CLK, PLL_VIDEO_MAIN_CLK, + PLL_ENET_MAIN_125M_CLK, PLL_SYS_PFD7_CLK} + }, + {GPT1_CLK_ROOT, CCM_IP_CHANNEL, + {OSC_24M_CLK, PLL_ENET_MAIN_100M_CLK, PLL_SYS_PFD0_392M_CLK, + PLL_ENET_MAIN_40M_CLK, PLL_VIDEO_MAIN_CLK, REF_1M_CLK, + PLL_AUDIO_MAIN_CLK, EXT_CLK_1} + }, + {GPT2_CLK_ROOT, CCM_IP_CHANNEL, + {OSC_24M_CLK, PLL_ENET_MAIN_100M_CLK, PLL_SYS_PFD0_392M_CLK, + PLL_ENET_MAIN_40M_CLK, PLL_VIDEO_MAIN_CLK, REF_1M_CLK, + PLL_AUDIO_MAIN_CLK, EXT_CLK_2} + }, + {GPT3_CLK_ROOT, CCM_IP_CHANNEL, + {OSC_24M_CLK, PLL_ENET_MAIN_100M_CLK, PLL_SYS_PFD0_392M_CLK, + PLL_ENET_MAIN_40M_CLK, PLL_VIDEO_MAIN_CLK, REF_1M_CLK, + PLL_AUDIO_MAIN_CLK, EXT_CLK_3} + }, + {GPT4_CLK_ROOT, CCM_IP_CHANNEL, + {OSC_24M_CLK, PLL_ENET_MAIN_100M_CLK, PLL_SYS_PFD0_392M_CLK, + PLL_ENET_MAIN_40M_CLK, PLL_VIDEO_MAIN_CLK, REF_1M_CLK, + PLL_AUDIO_MAIN_CLK, EXT_CLK_4} + }, + {TRACE_CLK_ROOT, CCM_IP_CHANNEL, + {OSC_24M_CLK, PLL_SYS_PFD2_135M_CLK, PLL_SYS_MAIN_120M_CLK, + PLL_DRAM_MAIN_533M_CLK, PLL_ENET_MAIN_125M_CLK, PLL_USB_MAIN_480M_CLK, + EXT_CLK_1, EXT_CLK_3} + }, + {WDOG_CLK_ROOT, CCM_IP_CHANNEL, + {OSC_24M_CLK, PLL_SYS_PFD2_135M_CLK, PLL_SYS_MAIN_120M_CLK, + PLL_DRAM_MAIN_533M_CLK, PLL_ENET_MAIN_125M_CLK, PLL_USB_MAIN_480M_CLK, + REF_1M_CLK, PLL_SYS_PFD1_166M_CLK} + }, + {CSI_MCLK_CLK_ROOT, CCM_IP_CHANNEL, + {OSC_24M_CLK, PLL_SYS_PFD2_135M_CLK, PLL_SYS_MAIN_120M_CLK, + PLL_DRAM_MAIN_533M_CLK, PLL_ENET_MAIN_125M_CLK, PLL_AUDIO_MAIN_CLK, + PLL_VIDEO_MAIN_CLK, PLL_USB_MAIN_480M_CLK} + }, + {AUDIO_MCLK_CLK_ROOT, CCM_IP_CHANNEL, + {OSC_24M_CLK, PLL_SYS_PFD2_135M_CLK, PLL_SYS_MAIN_120M_CLK, + PLL_DRAM_MAIN_533M_CLK, PLL_ENET_MAIN_125M_CLK, PLL_AUDIO_MAIN_CLK, + PLL_VIDEO_MAIN_CLK, PLL_USB_MAIN_480M_CLK} + }, + {WRCLK_CLK_ROOT, CCM_IP_CHANNEL, + {OSC_24M_CLK, PLL_ENET_MAIN_40M_CLK, PLL_DRAM_MAIN_533M_CLK, + PLL_USB_MAIN_480M_CLK, PLL_SYS_MAIN_240M_CLK, PLL_SYS_PFD2_270M_CLK, + PLL_ENET_MAIN_500M_CLK, PLL_SYS_PFD7_CLK} + }, + {IPP_DO_CLKO1, CCM_IP_CHANNEL, + {OSC_24M_CLK, PLL_SYS_MAIN_480M_CLK, PLL_SYS_MAIN_240M_CLK, + PLL_SYS_PFD0_196M_CLK, PLL_SYS_PFD3_CLK, PLL_ENET_MAIN_500M_CLK, + PLL_DRAM_MAIN_533M_CLK, REF_1M_CLK} + }, + {IPP_DO_CLKO2, CCM_IP_CHANNEL, + {OSC_24M_CLK, PLL_SYS_MAIN_240M_CLK, PLL_SYS_PFD0_392M_CLK, + PLL_SYS_PFD1_166M_CLK, PLL_SYS_PFD4_CLK, PLL_AUDIO_MAIN_CLK, + PLL_VIDEO_MAIN_CLK, OSC_32K_CLK} + }, +}; + +/* select which entry of root_array */ +static int select(enum clk_root_index clock_id) +{ + int i, size; + struct clk_root_map *p = root_array; + + size = ARRAY_SIZE(root_array); + + for (i = 0; i < size; i++, p++) { + if (clock_id == p->entry) + return i; + } + + return -EINVAL; +} + +static int src_supported(int entry, enum clk_root_src clock_src) +{ + int i, size; + struct clk_root_map *p = &root_array[entry]; + + if ((p->type == CCM_DRAM_PHYM_CHANNEL) || (p->type == CCM_DRAM_CHANNEL)) + size = 2; + else + size = 8; + + for (i = 0; i < size; i++) { + if (p->src_mux[i] == clock_src) + return i; + } + + return -EINVAL; +} + +/* Set src for clock root slice. */ +int clock_set_src(enum clk_root_index clock_id, enum clk_root_src clock_src) +{ + int root_entry, src_entry; + u32 reg; + + if (clock_id >= CLK_ROOT_MAX) + return -EINVAL; + + root_entry = select(clock_id); + if (root_entry < 0) + return -EINVAL; + + src_entry = src_supported(root_entry, clock_src); + if (src_entry < 0) + return -EINVAL; + + reg = __raw_readl(&imx_ccm->root[clock_id].target_root); + reg &= ~CLK_ROOT_MUX_MASK; + reg |= src_entry << CLK_ROOT_MUX_SHIFT; + __raw_writel(reg, &imx_ccm->root[clock_id].target_root); + + return 0; +} + +/* Get src of a clock root slice. */ +int clock_get_src(enum clk_root_index clock_id, enum clk_root_src *p_clock_src) +{ + u32 val; + int root_entry; + struct clk_root_map *p; + + if (clock_id >= CLK_ROOT_MAX) + return -EINVAL; + + val = __raw_readl(&imx_ccm->root[clock_id].target_root); + val &= CLK_ROOT_MUX_MASK; + val >>= CLK_ROOT_MUX_SHIFT; + + root_entry = select(clock_id); + if (root_entry < 0) + return -EINVAL; + + p = &root_array[root_entry]; + *p_clock_src = p->src_mux[val]; + + return 0; +} + +int clock_set_prediv(enum clk_root_index clock_id, enum root_pre_div pre_div) +{ + int root_entry; + struct clk_root_map *p; + u32 reg; + + if (clock_id >= CLK_ROOT_MAX) + return -EINVAL; + + root_entry = select(clock_id); + if (root_entry < 0) + return -EINVAL; + + p = &root_array[root_entry]; + + if ((p->type == CCM_CORE_CHANNEL) || + (p->type == CCM_DRAM_PHYM_CHANNEL) || + (p->type == CCM_DRAM_CHANNEL)) { + if (pre_div != CLK_ROOT_PRE_DIV1) { + printf("Error pre div!\n"); + return -EINVAL; + } + } + + reg = __raw_readl(&imx_ccm->root[clock_id].target_root); + reg &= ~CLK_ROOT_PRE_DIV_MASK; + reg |= pre_div << CLK_ROOT_PRE_DIV_SHIFT; + __raw_writel(reg, &imx_ccm->root[clock_id].target_root); + + return 0; +} + +int clock_get_prediv(enum clk_root_index clock_id, enum root_pre_div *pre_div) +{ + u32 val; + int root_entry; + struct clk_root_map *p; + + if (clock_id >= CLK_ROOT_MAX) + return -EINVAL; + + root_entry = select(clock_id); + if (root_entry < 0) + return -EINVAL; + + p = &root_array[root_entry]; + + if ((p->type == CCM_CORE_CHANNEL) || + (p->type == CCM_DRAM_PHYM_CHANNEL) || + (p->type == CCM_DRAM_CHANNEL)) { + *pre_div = 0; + return 0; + } + + val = __raw_readl(&imx_ccm->root[clock_id].target_root); + val &= CLK_ROOT_PRE_DIV_MASK; + val >>= CLK_ROOT_PRE_DIV_SHIFT; + + *pre_div = val; + + return 0; +} + +int clock_set_postdiv(enum clk_root_index clock_id, enum root_post_div div) +{ + u32 reg; + + if (clock_id >= CLK_ROOT_MAX) + return -EINVAL; + + if (clock_id == DRAM_PHYM_CLK_ROOT) { + if (div != CLK_ROOT_POST_DIV1) { + printf("Error post div!\n"); + return -EINVAL; + } + } + + /* Only 3 bit post div. */ + if ((clock_id == DRAM_CLK_ROOT) && (div > CLK_ROOT_POST_DIV7)) { + printf("Error post div!\n"); + return -EINVAL; + } + + reg = __raw_readl(&imx_ccm->root[clock_id].target_root); + reg &= ~CLK_ROOT_POST_DIV_MASK; + reg |= div << CLK_ROOT_POST_DIV_SHIFT; + __raw_writel(reg, &imx_ccm->root[clock_id].target_root); + + return 0; +} + +int clock_get_postdiv(enum clk_root_index clock_id, enum root_post_div *div) +{ + u32 val; + + if (clock_id >= CLK_ROOT_MAX) + return -EINVAL; + + if (clock_id == DRAM_PHYM_CLK_ROOT) { + *div = 0; + return 0; + } + + val = __raw_readl(&imx_ccm->root[clock_id].target_root); + if (clock_id == DRAM_CLK_ROOT) + val &= DRAM_CLK_ROOT_POST_DIV_MASK; + else + val &= CLK_ROOT_POST_DIV_MASK; + val >>= CLK_ROOT_POST_DIV_SHIFT; + + *div = val; + + return 0; +} + +int clock_set_autopostdiv(enum clk_root_index clock_id, enum root_auto_div div, + int auto_en) +{ + u32 val; + int root_entry; + struct clk_root_map *p; + + if (clock_id >= CLK_ROOT_MAX) + return -EINVAL; + + root_entry = select(clock_id); + if (root_entry < 0) + return -EINVAL; + + p = &root_array[root_entry]; + + if ((p->type != CCM_BUS_CHANNEL) && (p->type != CCM_AHB_CHANNEL)) { + printf("Auto postdiv not supported.!\n"); + return -EINVAL; + } + + /* + * Each time only one filed can be changed, no use target_root_set. + */ + val = __raw_readl(&imx_ccm->root[clock_id].target_root); + val &= ~CLK_ROOT_AUTO_DIV_MASK; + val |= (div << CLK_ROOT_AUTO_DIV_SHIFT); + + if (auto_en) + val |= CLK_ROOT_AUTO_EN; + else + val &= ~CLK_ROOT_AUTO_EN; + + __raw_writel(val, &imx_ccm->root[clock_id].target_root); + + return 0; +} + +int clock_get_autopostdiv(enum clk_root_index clock_id, enum root_auto_div *div, + int *auto_en) +{ + u32 val; + int root_entry; + struct clk_root_map *p; + + if (clock_id >= CLK_ROOT_MAX) + return -EINVAL; + + root_entry = select(clock_id); + if (root_entry < 0) + return -EINVAL; + + p = &root_array[root_entry]; + + /* + * Only bus/ahb channel supports auto div. + * If unsupported, just set auto_en and div with 0. + */ + if ((p->type != CCM_BUS_CHANNEL) && (p->type != CCM_AHB_CHANNEL)) { + *auto_en = 0; + *div = 0; + return 0; + } + + val = __raw_readl(&imx_ccm->root[clock_id].target_root); + if ((val & CLK_ROOT_AUTO_EN_MASK) == 0) + *auto_en = 0; + else + *auto_en = 1; + + val &= CLK_ROOT_AUTO_DIV_MASK; + val >>= CLK_ROOT_AUTO_DIV_SHIFT; + + *div = val; + + return 0; +} + +int clock_get_target_val(enum clk_root_index clock_id, u32 *val) +{ + if (clock_id >= CLK_ROOT_MAX) + return -EINVAL; + + *val = __raw_readl(&imx_ccm->root[clock_id].target_root); + + return 0; +} + +int clock_set_target_val(enum clk_root_index clock_id, u32 val) +{ + if (clock_id >= CLK_ROOT_MAX) + return -EINVAL; + + __raw_writel(val, &imx_ccm->root[clock_id].target_root); + + return 0; +} + +/* Auto_div and auto_en is ignored, they are rarely used. */ +int clock_root_cfg(enum clk_root_index clock_id, enum root_pre_div pre_div, + enum root_post_div post_div, enum clk_root_src clock_src) +{ + u32 val; + int root_entry, src_entry; + struct clk_root_map *p; + + if (clock_id >= CLK_ROOT_MAX) + return -EINVAL; + + root_entry = select(clock_id); + if (root_entry < 0) + return -EINVAL; + + p = &root_array[root_entry]; + + if ((p->type == CCM_CORE_CHANNEL) || + (p->type == CCM_DRAM_PHYM_CHANNEL) || + (p->type == CCM_DRAM_CHANNEL)) { + if (pre_div != CLK_ROOT_PRE_DIV1) { + printf("Error pre div!\n"); + return -EINVAL; + } + } + + /* Only 3 bit post div. */ + if (p->type == CCM_DRAM_CHANNEL) { + if (post_div > CLK_ROOT_POST_DIV7) { + printf("Error post div!\n"); + return -EINVAL; + } + } + + if (p->type == CCM_DRAM_PHYM_CHANNEL) { + if (post_div != CLK_ROOT_POST_DIV1) { + printf("Error post div!\n"); + return -EINVAL; + } + } + + src_entry = src_supported(root_entry, clock_src); + if (src_entry < 0) + return -EINVAL; + + val = CLK_ROOT_ON | pre_div << CLK_ROOT_PRE_DIV_SHIFT | + post_div << CLK_ROOT_POST_DIV_SHIFT | + src_entry << CLK_ROOT_MUX_SHIFT; + + __raw_writel(val, &imx_ccm->root[clock_id].target_root); + + return 0; +} + +int clock_root_enabled(enum clk_root_index clock_id) +{ + u32 val; + + if (clock_id >= CLK_ROOT_MAX) + return -EINVAL; + + /* + * No enable bit for DRAM controller and PHY. Just return enabled. + */ + if ((clock_id == DRAM_PHYM_CLK_ROOT) || (clock_id == DRAM_CLK_ROOT)) + return 1; + + val = __raw_readl(&imx_ccm->root[clock_id].target_root); + + return (val & CLK_ROOT_ENABLE_MASK) ? 1 : 0; +} + +/* CCGR gate operation */ +int clock_enable(enum clk_ccgr_index index, bool enable) +{ + if (index >= CCGR_MAX) + return -EINVAL; + + if (enable) + __raw_writel(CCM_CLK_ON_MSK, + &imx_ccm->ccgr_array[index].ccgr_set); + else + __raw_writel(CCM_CLK_ON_MSK, + &imx_ccm->ccgr_array[index].ccgr_clr); + + return 0; +} diff --git a/roms/u-boot/arch/arm/mach-imx/mx7/ddr.c b/roms/u-boot/arch/arm/mach-imx/mx7/ddr.c new file mode 100644 index 000000000..cf2556976 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/mx7/ddr.c @@ -0,0 +1,230 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * DDR controller configuration for the i.MX7 architecture + * + * (C) Copyright 2017 CompuLab, Ltd. http://www.compulab.com + * + * Author: Uri Mashiach <uri.mashiach@compulab.co.il> + */ + +#include <linux/types.h> +#include <asm/io.h> +#include <asm/arch/imx-regs.h> +#include <asm/arch/crm_regs.h> +#include <asm/arch/mx7-ddr.h> +#include <common.h> +#include <linux/delay.h> + +/* + * Routine: mx7_dram_cfg + * Description: DDR controller configuration + * + * @ddrc_regs_val: DDRC registers value + * @ddrc_mp_val: DDRC_MP registers value + * @ddr_phy_regs_val: DDR_PHY registers value + * @calib_param: calibration parameters + * + */ +void mx7_dram_cfg(struct ddrc *ddrc_regs_val, struct ddrc_mp *ddrc_mp_val, + struct ddr_phy *ddr_phy_regs_val, + struct mx7_calibration *calib_param) +{ + struct src *const src_regs = (struct src *)SRC_BASE_ADDR; + struct ddrc *const ddrc_regs = (struct ddrc *)DDRC_IPS_BASE_ADDR; + struct ddrc_mp *const ddrc_mp_reg = (struct ddrc_mp *)DDRC_MP_BASE_ADDR; + struct ddr_phy *const ddr_phy_regs = + (struct ddr_phy *)DDRPHY_IPS_BASE_ADDR; + struct iomuxc_gpr_base_regs *const iomuxc_gpr_regs = + (struct iomuxc_gpr_base_regs *)IOMUXC_GPR_BASE_ADDR; + int i; + + /* + * iMX7D RM 9.2.4.9.3 Power removal flow Table 9-11. Re-enabling power + * row 2 says "Reset controller / PHY by driving core_ddrc_rst = 0 , + * aresetn_n = 0, presetn = 0. That means reset everything. + */ + writel(SRC_DDRC_RCR_DDRC_CORE_RST_MASK | SRC_DDRC_RCR_DDRC_PRST_MASK, + &src_regs->ddrc_rcr); + + /* + * iMX7D RM 6.2.7.26 SRC_DDRC_RCR says wait 30 cycles (of unknown). + * If we assume this is 30 cycles at 100 MHz (about the rate of a + * DRAM bus), that's 300 nS, so waiting 10 uS is more then plenty. + */ + udelay(10); + + /* De-assert DDR Controller 'preset' and DDR PHY reset */ + clrbits_le32(&src_regs->ddrc_rcr, SRC_DDRC_RCR_DDRC_PRST_MASK); + + /* DDR controller configuration */ + writel(ddrc_regs_val->mstr, &ddrc_regs->mstr); + writel(ddrc_regs_val->rfshtmg, &ddrc_regs->rfshtmg); + writel(ddrc_mp_val->pctrl_0, &ddrc_mp_reg->pctrl_0); + writel(ddrc_regs_val->init1, &ddrc_regs->init1); + writel(ddrc_regs_val->init0, &ddrc_regs->init0); + writel(ddrc_regs_val->init3, &ddrc_regs->init3); + writel(ddrc_regs_val->init4, &ddrc_regs->init4); + writel(ddrc_regs_val->init5, &ddrc_regs->init5); + writel(ddrc_regs_val->rankctl, &ddrc_regs->rankctl); + writel(ddrc_regs_val->dramtmg0, &ddrc_regs->dramtmg0); + writel(ddrc_regs_val->dramtmg1, &ddrc_regs->dramtmg1); + writel(ddrc_regs_val->dramtmg2, &ddrc_regs->dramtmg2); + writel(ddrc_regs_val->dramtmg3, &ddrc_regs->dramtmg3); + writel(ddrc_regs_val->dramtmg4, &ddrc_regs->dramtmg4); + writel(ddrc_regs_val->dramtmg5, &ddrc_regs->dramtmg5); + writel(ddrc_regs_val->dramtmg8, &ddrc_regs->dramtmg8); + writel(ddrc_regs_val->zqctl0, &ddrc_regs->zqctl0); + writel(ddrc_regs_val->zqctl1, &ddrc_regs->zqctl1); + writel(ddrc_regs_val->dfitmg0, &ddrc_regs->dfitmg0); + writel(ddrc_regs_val->dfitmg1, &ddrc_regs->dfitmg1); + writel(ddrc_regs_val->dfiupd0, &ddrc_regs->dfiupd0); + writel(ddrc_regs_val->dfiupd1, &ddrc_regs->dfiupd1); + writel(ddrc_regs_val->dfiupd2, &ddrc_regs->dfiupd2); + writel(ddrc_regs_val->addrmap0, &ddrc_regs->addrmap0); + writel(ddrc_regs_val->addrmap1, &ddrc_regs->addrmap1); + writel(ddrc_regs_val->addrmap4, &ddrc_regs->addrmap4); + writel(ddrc_regs_val->addrmap5, &ddrc_regs->addrmap5); + writel(ddrc_regs_val->addrmap6, &ddrc_regs->addrmap6); + writel(ddrc_regs_val->odtcfg, &ddrc_regs->odtcfg); + writel(ddrc_regs_val->odtmap, &ddrc_regs->odtmap); + + /* De-assert DDR Controller 'core_ddrc_rstn' and 'aresetn' */ + clrbits_le32(&src_regs->ddrc_rcr, SRC_DDRC_RCR_DDRC_CORE_RST_MASK); + + /* PHY configuration */ + writel(ddr_phy_regs_val->phy_con0, &ddr_phy_regs->phy_con0); + writel(ddr_phy_regs_val->phy_con1, &ddr_phy_regs->phy_con1); + writel(ddr_phy_regs_val->phy_con4, &ddr_phy_regs->phy_con4); + writel(ddr_phy_regs_val->mdll_con0, &ddr_phy_regs->mdll_con0); + writel(ddr_phy_regs_val->drvds_con0, &ddr_phy_regs->drvds_con0); + writel(ddr_phy_regs_val->offset_wr_con0, &ddr_phy_regs->offset_wr_con0); + writel(ddr_phy_regs_val->offset_rd_con0, &ddr_phy_regs->offset_rd_con0); + writel(ddr_phy_regs_val->cmd_sdll_con0 | + DDR_PHY_CMD_SDLL_CON0_CTRL_RESYNC_MASK, + &ddr_phy_regs->cmd_sdll_con0); + writel(ddr_phy_regs_val->cmd_sdll_con0 & + ~DDR_PHY_CMD_SDLL_CON0_CTRL_RESYNC_MASK, + &ddr_phy_regs->cmd_sdll_con0); + writel(ddr_phy_regs_val->offset_lp_con0, &ddr_phy_regs->offset_lp_con0); + writel(ddr_phy_regs_val->cmd_deskew_con0, + &ddr_phy_regs->cmd_deskew_con0); + writel(ddr_phy_regs_val->cmd_deskew_con1, + &ddr_phy_regs->cmd_deskew_con1); + writel(ddr_phy_regs_val->cmd_deskew_con2, + &ddr_phy_regs->cmd_deskew_con2); + writel(ddr_phy_regs_val->cmd_deskew_con3, + &ddr_phy_regs->cmd_deskew_con3); + writel(ddr_phy_regs_val->cmd_lvl_con0, &ddr_phy_regs->cmd_lvl_con0); + + /* calibration */ + for (i = 0; i < calib_param->num_val; i++) + writel(calib_param->values[i], &ddr_phy_regs->zq_con0); + + /* Wake_up DDR PHY */ + HW_CCM_CCGR_WR(CCGR_IDX_DDR, CCM_CLK_ON_N_N); + writel(IOMUXC_GPR_GPR8_ddr_phy_ctrl_wake_up(0xf) | + IOMUXC_GPR_GPR8_ddr_phy_dfi_init_start_MASK, + &iomuxc_gpr_regs->gpr[8]); + HW_CCM_CCGR_WR(CCGR_IDX_DDR, CCM_CLK_ON_R_W); +} + +/* + * Routine: imx_ddr_size + * Description: extract the current DRAM size from the DDRC registers + * + * @return: DRAM size + */ +unsigned int imx_ddr_size(void) +{ + struct ddrc *const ddrc_regs = (struct ddrc *)DDRC_IPS_BASE_ADDR; + u32 reg_val, field_val; + int bits = 0;/* Number of address bits */ + + /* Count data bus width bits */ + reg_val = readl(&ddrc_regs->mstr); + field_val = (reg_val & MSTR_DATA_BUS_WIDTH_MASK) >> MSTR_DATA_BUS_WIDTH_SHIFT; + bits += 2 - field_val; + /* Count rank address bits */ + field_val = (reg_val & MSTR_DATA_ACTIVE_RANKS_MASK) >> MSTR_DATA_ACTIVE_RANKS_SHIFT; + if (field_val > 1) + bits += field_val - 1; + /* Count column address bits */ + bits += 2;/* Column address 0 and 1 are fixed mapped */ + reg_val = readl(&ddrc_regs->addrmap2); + field_val = (reg_val & ADDRMAP2_COL_B2_MASK) >> ADDRMAP2_COL_B2_SHIFT; + if (field_val <= 7) + bits++; + field_val = (reg_val & ADDRMAP2_COL_B3_MASK) >> ADDRMAP2_COL_B3_SHIFT; + if (field_val <= 7) + bits++; + field_val = (reg_val & ADDRMAP2_COL_B4_MASK) >> ADDRMAP2_COL_B4_SHIFT; + if (field_val <= 7) + bits++; + field_val = (reg_val & ADDRMAP2_COL_B5_MASK) >> ADDRMAP2_COL_B5_SHIFT; + if (field_val <= 7) + bits++; + reg_val = readl(&ddrc_regs->addrmap3); + field_val = (reg_val & ADDRMAP3_COL_B6_MASK) >> ADDRMAP3_COL_B6_SHIFT; + if (field_val <= 7) + bits++; + field_val = (reg_val & ADDRMAP3_COL_B7_MASK) >> ADDRMAP3_COL_B7_SHIFT; + if (field_val <= 7) + bits++; + field_val = (reg_val & ADDRMAP3_COL_B8_MASK) >> ADDRMAP3_COL_B8_SHIFT; + if (field_val <= 7) + bits++; + field_val = (reg_val & ADDRMAP3_COL_B9_MASK) >> ADDRMAP3_COL_B9_SHIFT; + if (field_val <= 7) + bits++; + reg_val = readl(&ddrc_regs->addrmap4); + field_val = (reg_val & ADDRMAP4_COL_B10_MASK) >> ADDRMAP4_COL_B10_SHIFT; + if (field_val <= 7) + bits++; + field_val = (reg_val & ADDRMAP4_COL_B11_MASK) >> ADDRMAP4_COL_B11_SHIFT; + if (field_val <= 7) + bits++; + /* Count row address bits */ + reg_val = readl(&ddrc_regs->addrmap5); + field_val = (reg_val & ADDRMAP5_ROW_B0_MASK) >> ADDRMAP5_ROW_B0_SHIFT; + if (field_val <= 11) + bits++; + field_val = (reg_val & ADDRMAP5_ROW_B1_MASK) >> ADDRMAP5_ROW_B1_SHIFT; + if (field_val <= 11) + bits++; + field_val = (reg_val & ADDRMAP5_ROW_B2_10_MASK) >> ADDRMAP5_ROW_B2_10_SHIFT; + if (field_val <= 11) + bits += 9; + field_val = (reg_val & ADDRMAP5_ROW_B11_MASK) >> ADDRMAP5_ROW_B11_SHIFT; + if (field_val <= 11) + bits++; + reg_val = readl(&ddrc_regs->addrmap6); + field_val = (reg_val & ADDRMAP6_ROW_B12_MASK) >> ADDRMAP6_ROW_B12_SHIFT; + if (field_val <= 11) + bits++; + field_val = (reg_val & ADDRMAP6_ROW_B13_MASK) >> ADDRMAP6_ROW_B13_SHIFT; + if (field_val <= 11) + bits++; + field_val = (reg_val & ADDRMAP6_ROW_B14_MASK) >> ADDRMAP6_ROW_B14_SHIFT; + if (field_val <= 11) + bits++; + field_val = (reg_val & ADDRMAP6_ROW_B15_MASK) >> ADDRMAP6_ROW_B15_SHIFT; + if (field_val <= 11) + bits++; + /* Count bank bits */ + reg_val = readl(&ddrc_regs->addrmap1); + field_val = (reg_val & ADDRMAP1_BANK_B0_MASK) >> ADDRMAP1_BANK_B0_SHIFT; + if (field_val <= 30) + bits++; + field_val = (reg_val & ADDRMAP1_BANK_B1_MASK) >> ADDRMAP1_BANK_B1_SHIFT; + if (field_val <= 30) + bits++; + field_val = (reg_val & ADDRMAP1_BANK_B2_MASK) >> ADDRMAP1_BANK_B2_SHIFT; + if (field_val <= 29) + bits++; + + /* cap to max 2 GB */ + if (bits > 31) + bits = 31; + + return 1 << bits; +} diff --git a/roms/u-boot/arch/arm/mach-imx/mx7/psci-mx7.c b/roms/u-boot/arch/arm/mach-imx/mx7/psci-mx7.c new file mode 100644 index 000000000..f32945ea3 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/mx7/psci-mx7.c @@ -0,0 +1,690 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2015-2016 Freescale Semiconductor, Inc. + * Copyright 2017 NXP + */ + +#include <cpu_func.h> +#include <asm/cache.h> +#include <asm/io.h> +#include <asm/psci.h> +#include <asm/secure.h> +#include <asm/arch/imx-regs.h> +#include <asm/armv7.h> +#include <asm/gic.h> +#include <linux/bitops.h> +#include <common.h> +#include <fsl_wdog.h> + +#define GPC_LPCR_A7_BSC 0x0 +#define GPC_LPCR_A7_AD 0x4 +#define GPC_SLPCR 0x14 +#define GPC_PGC_ACK_SEL_A7 0x24 +#define GPC_IMR1_CORE0 0x30 +#define GPC_SLOT0_CFG 0xb0 +#define GPC_CPU_PGC_SW_PUP_REQ 0xf0 +#define GPC_CPU_PGC_SW_PDN_REQ 0xfc +#define GPC_PGC_C0 0x800 +#define GPC_PGC_C0 0x800 +#define GPC_PGC_C1 0x840 +#define GPC_PGC_SCU 0x880 + +#define BM_LPCR_A7_BSC_CPU_CLK_ON_LPM 0x4000 +#define BM_LPCR_A7_BSC_LPM1 0xc +#define BM_LPCR_A7_BSC_LPM0 0x3 +#define BP_LPCR_A7_BSC_LPM0 0 +#define BM_SLPCR_EN_DSM 0x80000000 +#define BM_SLPCR_RBC_EN 0x40000000 +#define BM_SLPCR_REG_BYPASS_COUNT 0x3f000000 +#define BM_SLPCR_VSTBY 0x4 +#define BM_SLPCR_SBYOS 0x2 +#define BM_SLPCR_BYPASS_PMIC_READY 0x1 +#define BM_LPCR_A7_AD_L2PGE 0x10000 +#define BM_LPCR_A7_AD_EN_C1_PUP 0x800 +#define BM_LPCR_A7_AD_EN_C0_PUP 0x200 +#define BM_LPCR_A7_AD_EN_PLAT_PDN 0x10 +#define BM_LPCR_A7_AD_EN_C1_PDN 0x8 +#define BM_LPCR_A7_AD_EN_C0_PDN 0x2 + +#define BM_CPU_PGC_SW_PDN_PUP_REQ_CORE0_A7 0x1 +#define BM_CPU_PGC_SW_PDN_PUP_REQ_CORE1_A7 0x2 + +#define BM_GPC_PGC_ACK_SEL_A7_PD_DUMMY_ACK 0x8000 +#define BM_GPC_PGC_ACK_SEL_A7_PU_DUMMY_ACK 0x80000000 + +#define MAX_SLOT_NUMBER 10 +#define A7_LPM_WAIT 0x5 +#define A7_LPM_STOP 0xa + +#define BM_SYS_COUNTER_CNTCR_FCR1 0x200 +#define BM_SYS_COUNTER_CNTCR_FCR0 0x100 + +#define REG_SET 0x4 +#define REG_CLR 0x8 + +#define ANADIG_ARM_PLL 0x60 +#define ANADIG_DDR_PLL 0x70 +#define ANADIG_SYS_PLL 0xb0 +#define ANADIG_ENET_PLL 0xe0 +#define ANADIG_AUDIO_PLL 0xf0 +#define ANADIG_VIDEO_PLL 0x130 +#define BM_ANATOP_ARM_PLL_OVERRIDE BIT(20) +#define BM_ANATOP_DDR_PLL_OVERRIDE BIT(19) +#define BM_ANATOP_SYS_PLL_OVERRIDE (0x1ff << 17) +#define BM_ANATOP_ENET_PLL_OVERRIDE BIT(13) +#define BM_ANATOP_AUDIO_PLL_OVERRIDE BIT(24) +#define BM_ANATOP_VIDEO_PLL_OVERRIDE BIT(24) + +#define DDRC_STAT 0x4 +#define DDRC_PWRCTL 0x30 +#define DDRC_PSTAT 0x3fc + +#define SRC_GPR1_MX7D 0x074 +#define SRC_GPR2_MX7D 0x078 +#define SRC_A7RCR0 0x004 +#define SRC_A7RCR1 0x008 + +#define BP_SRC_A7RCR0_A7_CORE_RESET0 0 +#define BP_SRC_A7RCR1_A7_CORE1_ENABLE 1 + +#define SNVS_LPCR 0x38 +#define BP_SNVS_LPCR_DP_EN 0x20 +#define BP_SNVS_LPCR_TOP 0x40 + +#define CCM_CCGR_SNVS 0x4250 + +#define CCM_ROOT_WDOG 0xbb80 +#define CCM_CCGR_WDOG1 0x49c0 + +#define MPIDR_AFF0 GENMASK(7, 0) + +#define IMX7D_PSCI_NR_CPUS 2 +#if IMX7D_PSCI_NR_CPUS > CONFIG_ARMV7_PSCI_NR_CPUS +#error "invalid value for CONFIG_ARMV7_PSCI_NR_CPUS" +#endif + +#define imx_cpu_gpr_entry_offset(cpu) \ + (SRC_BASE_ADDR + SRC_GPR1_MX7D + cpu * 8) +#define imx_cpu_gpr_para_offset(cpu) \ + (imx_cpu_gpr_entry_offset(cpu) + 4) + +#define IMX_CPU_SYNC_OFF ~0 +#define IMX_CPU_SYNC_ON 0 + +u8 psci_state[IMX7D_PSCI_NR_CPUS] __secure_data = { + PSCI_AFFINITY_LEVEL_ON, + PSCI_AFFINITY_LEVEL_OFF}; + +enum imx_gpc_slot { + CORE0_A7, + CORE1_A7, + SCU_A7, + FAST_MEGA_MIX, + MIPI_PHY, + PCIE_PHY, + USB_OTG1_PHY, + USB_OTG2_PHY, + USB_HSIC_PHY, + CORE0_M4, +}; + +enum mxc_cpu_pwr_mode { + RUN, + WAIT, + STOP, +}; + +extern void psci_system_resume(void); + +static inline void psci_set_state(int cpu, u8 state) +{ + psci_state[cpu] = state; + dsb(); + isb(); +} + +static inline void imx_gpcv2_set_m_core_pgc(bool enable, u32 offset) +{ + writel(enable, GPC_IPS_BASE_ADDR + offset); +} + +__secure void imx_gpcv2_set_core_power(int cpu, bool pdn) +{ + u32 reg = pdn ? GPC_CPU_PGC_SW_PUP_REQ : GPC_CPU_PGC_SW_PDN_REQ; + u32 pgc = cpu ? GPC_PGC_C1 : GPC_PGC_C0; + u32 pdn_pup_req = cpu ? BM_CPU_PGC_SW_PDN_PUP_REQ_CORE1_A7 : + BM_CPU_PGC_SW_PDN_PUP_REQ_CORE0_A7; + u32 val; + + imx_gpcv2_set_m_core_pgc(true, pgc); + + val = readl(GPC_IPS_BASE_ADDR + reg); + val |= pdn_pup_req; + writel(val, GPC_IPS_BASE_ADDR + reg); + + while ((readl(GPC_IPS_BASE_ADDR + reg) & pdn_pup_req) != 0) + ; + + imx_gpcv2_set_m_core_pgc(false, pgc); +} + +__secure void imx_enable_cpu_ca7(int cpu, bool enable) +{ + u32 mask, val; + + mask = 1 << (BP_SRC_A7RCR1_A7_CORE1_ENABLE + cpu - 1); + val = readl(SRC_BASE_ADDR + SRC_A7RCR1); + val = enable ? val | mask : val & ~mask; + writel(val, SRC_BASE_ADDR + SRC_A7RCR1); +} + +__secure void psci_arch_cpu_entry(void) +{ + u32 cpu = psci_get_cpu_id(); + + psci_set_state(cpu, PSCI_AFFINITY_LEVEL_ON); +} + +__secure s32 psci_cpu_on(u32 __always_unused function_id, u32 mpidr, u32 ep, + u32 context_id) +{ + u32 cpu = mpidr & MPIDR_AFF0; + + if (mpidr & ~MPIDR_AFF0) + return ARM_PSCI_RET_INVAL; + + if (cpu >= IMX7D_PSCI_NR_CPUS) + return ARM_PSCI_RET_INVAL; + + if (psci_state[cpu] == PSCI_AFFINITY_LEVEL_ON) + return ARM_PSCI_RET_ALREADY_ON; + + if (psci_state[cpu] == PSCI_AFFINITY_LEVEL_ON_PENDING) + return ARM_PSCI_RET_ON_PENDING; + + psci_save(cpu, ep, context_id); + + writel((u32)psci_cpu_entry, imx_cpu_gpr_entry_offset(cpu)); + + psci_set_state(cpu, PSCI_AFFINITY_LEVEL_ON_PENDING); + + imx_gpcv2_set_core_power(cpu, true); + imx_enable_cpu_ca7(cpu, true); + + return ARM_PSCI_RET_SUCCESS; +} + +__secure s32 psci_cpu_off(void) +{ + int cpu; + + cpu = psci_get_cpu_id(); + + psci_cpu_off_common(); + psci_set_state(cpu, PSCI_AFFINITY_LEVEL_OFF); + + imx_enable_cpu_ca7(cpu, false); + imx_gpcv2_set_core_power(cpu, false); + /* + * We use the cpu jumping argument register to sync with + * psci_affinity_info() which is running on cpu0 to kill the cpu. + */ + writel(IMX_CPU_SYNC_OFF, imx_cpu_gpr_para_offset(cpu)); + + while (1) + wfi(); +} + +__secure void psci_system_reset(void) +{ + struct wdog_regs *wdog = (struct wdog_regs *)WDOG1_BASE_ADDR; + + /* make sure WDOG1 clock is enabled */ + writel(0x1 << 28, CCM_BASE_ADDR + CCM_ROOT_WDOG); + writel(0x3, CCM_BASE_ADDR + CCM_CCGR_WDOG1); + writew(WCR_WDE, &wdog->wcr); + + while (1) + wfi(); +} + +__secure void psci_system_off(void) +{ + u32 val; + + /* make sure SNVS clock is enabled */ + writel(0x3, CCM_BASE_ADDR + CCM_CCGR_SNVS); + + val = readl(SNVS_BASE_ADDR + SNVS_LPCR); + val |= BP_SNVS_LPCR_DP_EN | BP_SNVS_LPCR_TOP; + writel(val, SNVS_BASE_ADDR + SNVS_LPCR); + + while (1) + wfi(); +} + +__secure u32 psci_version(void) +{ + return ARM_PSCI_VER_1_0; +} + +__secure s32 psci_cpu_suspend(u32 __always_unused function_id, u32 power_state, + u32 entry_point_address, + u32 context_id) +{ + return ARM_PSCI_RET_INVAL; +} + +__secure s32 psci_affinity_info(u32 __always_unused function_id, + u32 target_affinity, + u32 lowest_affinity_level) +{ + u32 cpu = target_affinity & MPIDR_AFF0; + + if (lowest_affinity_level > 0) + return ARM_PSCI_RET_INVAL; + + if (target_affinity & ~MPIDR_AFF0) + return ARM_PSCI_RET_INVAL; + + if (cpu >= IMX7D_PSCI_NR_CPUS) + return ARM_PSCI_RET_INVAL; + + /* CPU is waiting for killed */ + if (readl(imx_cpu_gpr_para_offset(cpu)) == IMX_CPU_SYNC_OFF) { + imx_enable_cpu_ca7(cpu, false); + imx_gpcv2_set_core_power(cpu, false); + writel(IMX_CPU_SYNC_ON, imx_cpu_gpr_para_offset(cpu)); + } + + return psci_state[cpu]; +} + +__secure u32 psci_migrate_info_type(void) +{ + /* Trusted OS is either not present or does not require migration */ + return 2; +} + +__secure s32 psci_features(u32 __always_unused function_id, u32 psci_fid) +{ + switch (psci_fid) { + case ARM_PSCI_0_2_FN_PSCI_VERSION: + case ARM_PSCI_0_2_FN_CPU_OFF: + case ARM_PSCI_0_2_FN_CPU_ON: + case ARM_PSCI_0_2_FN_AFFINITY_INFO: + case ARM_PSCI_0_2_FN_MIGRATE_INFO_TYPE: + case ARM_PSCI_0_2_FN_SYSTEM_OFF: + case ARM_PSCI_0_2_FN_SYSTEM_RESET: + case ARM_PSCI_1_0_FN_PSCI_FEATURES: + case ARM_PSCI_1_0_FN_SYSTEM_SUSPEND: + return 0x0; + } + return ARM_PSCI_RET_NI; +} + +static __secure void imx_gpcv2_set_lpm_mode(enum mxc_cpu_pwr_mode mode) +{ + u32 val1, val2, val3; + + val1 = readl(GPC_IPS_BASE_ADDR + GPC_LPCR_A7_BSC); + val2 = readl(GPC_IPS_BASE_ADDR + GPC_SLPCR); + + /* all cores' LPM settings must be same */ + val1 &= ~(BM_LPCR_A7_BSC_LPM0 | BM_LPCR_A7_BSC_LPM1); + val1 |= BM_LPCR_A7_BSC_CPU_CLK_ON_LPM; + + val2 &= ~(BM_SLPCR_EN_DSM | BM_SLPCR_VSTBY | BM_SLPCR_RBC_EN | + BM_SLPCR_SBYOS | BM_SLPCR_BYPASS_PMIC_READY); + /* + * GPC: When improper low-power sequence is used, + * the SoC enters low power mode before the ARM core executes WFI. + * + * Software workaround: + * 1) Software should trigger IRQ #32 (IOMUX) to be always pending + * by setting IOMUX_GPR1_IRQ. + * 2) Software should then unmask IRQ #32 in GPC before setting GPC + * Low-Power mode. + * 3) Software should mask IRQ #32 right after GPC Low-Power mode + * is set. + */ + switch (mode) { + case RUN: + val3 = readl(GPC_IPS_BASE_ADDR + GPC_IMR1_CORE0); + val3 &= ~0x1; + writel(val3, GPC_IPS_BASE_ADDR + GPC_IMR1_CORE0); + break; + case WAIT: + val1 |= A7_LPM_WAIT << BP_LPCR_A7_BSC_LPM0; + val1 &= ~BM_LPCR_A7_BSC_CPU_CLK_ON_LPM; + val3 = readl(GPC_IPS_BASE_ADDR + GPC_IMR1_CORE0); + val3 &= ~0x1; + writel(val3, GPC_IPS_BASE_ADDR + GPC_IMR1_CORE0); + break; + case STOP: + val1 |= A7_LPM_STOP << BP_LPCR_A7_BSC_LPM0; + val1 &= ~BM_LPCR_A7_BSC_CPU_CLK_ON_LPM; + val2 |= BM_SLPCR_EN_DSM; + val2 |= BM_SLPCR_SBYOS; + val2 |= BM_SLPCR_VSTBY; + val2 |= BM_SLPCR_BYPASS_PMIC_READY; + val3 = readl(GPC_IPS_BASE_ADDR + GPC_IMR1_CORE0); + val3 |= 0x1; + writel(val3, GPC_IPS_BASE_ADDR + GPC_IMR1_CORE0); + break; + default: + return; + } + writel(val1, GPC_IPS_BASE_ADDR + GPC_LPCR_A7_BSC); + writel(val2, GPC_IPS_BASE_ADDR + GPC_SLPCR); +} + +static __secure void imx_gpcv2_set_plat_power_gate_by_lpm(bool pdn) +{ + u32 val = readl(GPC_IPS_BASE_ADDR + GPC_LPCR_A7_AD); + + val &= ~(BM_LPCR_A7_AD_EN_PLAT_PDN | BM_LPCR_A7_AD_L2PGE); + if (pdn) + val |= BM_LPCR_A7_AD_EN_PLAT_PDN | BM_LPCR_A7_AD_L2PGE; + + writel(val, GPC_IPS_BASE_ADDR + GPC_LPCR_A7_AD); +} + +static __secure void imx_gpcv2_set_cpu_power_gate_by_lpm(u32 cpu, bool pdn) +{ + u32 val; + + val = readl(GPC_IPS_BASE_ADDR + GPC_LPCR_A7_AD); + if (cpu == 0) { + if (pdn) + val |= BM_LPCR_A7_AD_EN_C0_PDN | + BM_LPCR_A7_AD_EN_C0_PUP; + else + val &= ~(BM_LPCR_A7_AD_EN_C0_PDN | + BM_LPCR_A7_AD_EN_C0_PUP); + } + if (cpu == 1) { + if (pdn) + val |= BM_LPCR_A7_AD_EN_C1_PDN | + BM_LPCR_A7_AD_EN_C1_PUP; + else + val &= ~(BM_LPCR_A7_AD_EN_C1_PDN | + BM_LPCR_A7_AD_EN_C1_PUP); + } + writel(val, GPC_IPS_BASE_ADDR + GPC_LPCR_A7_AD); +} + +static __secure void imx_gpcv2_set_slot_ack(u32 index, enum imx_gpc_slot m_core, + bool mode, bool ack) +{ + u32 val; + + if (index >= MAX_SLOT_NUMBER) + return; + + /* set slot */ + writel(readl(GPC_IPS_BASE_ADDR + GPC_SLOT0_CFG + index * 4) | + ((mode + 1) << (m_core * 2)), + GPC_IPS_BASE_ADDR + GPC_SLOT0_CFG + index * 4); + + if (ack) { + /* set ack */ + val = readl(GPC_IPS_BASE_ADDR + GPC_PGC_ACK_SEL_A7); + /* clear dummy ack */ + val &= ~(mode ? BM_GPC_PGC_ACK_SEL_A7_PU_DUMMY_ACK : + BM_GPC_PGC_ACK_SEL_A7_PD_DUMMY_ACK); + val |= 1 << (m_core + (mode ? 16 : 0)); + writel(val, GPC_IPS_BASE_ADDR + GPC_PGC_ACK_SEL_A7); + } +} + +static __secure void imx_system_counter_resume(void) +{ + u32 val; + + val = readl(SYSCNT_CTRL_IPS_BASE_ADDR); + val &= ~BM_SYS_COUNTER_CNTCR_FCR1; + val |= BM_SYS_COUNTER_CNTCR_FCR0; + writel(val, SYSCNT_CTRL_IPS_BASE_ADDR); +} + +static __secure void imx_system_counter_suspend(void) +{ + u32 val; + + val = readl(SYSCNT_CTRL_IPS_BASE_ADDR); + val &= ~BM_SYS_COUNTER_CNTCR_FCR0; + val |= BM_SYS_COUNTER_CNTCR_FCR1; + writel(val, SYSCNT_CTRL_IPS_BASE_ADDR); +} + +static __secure void gic_resume(void) +{ + u32 itlinesnr, i; + u32 gic_dist_addr = GIC400_ARB_BASE_ADDR + GIC_DIST_OFFSET; + + /* enable the GIC distributor */ + writel(readl(gic_dist_addr + GICD_CTLR) | 0x03, + gic_dist_addr + GICD_CTLR); + + /* TYPER[4:0] contains an encoded number of available interrupts */ + itlinesnr = readl(gic_dist_addr + GICD_TYPER) & 0x1f; + + /* set all bits in the GIC group registers to one to allow access + * from non-secure state. The first 32 interrupts are private per + * CPU and will be set later when enabling the GIC for each core + */ + for (i = 1; i <= itlinesnr; i++) + writel((u32)-1, gic_dist_addr + GICD_IGROUPRn + 4 * i); +} + +static inline void imx_pll_suspend(void) +{ + writel(BM_ANATOP_ARM_PLL_OVERRIDE, + ANATOP_BASE_ADDR + ANADIG_ARM_PLL + REG_SET); + writel(BM_ANATOP_DDR_PLL_OVERRIDE, + ANATOP_BASE_ADDR + ANADIG_DDR_PLL + REG_SET); + writel(BM_ANATOP_SYS_PLL_OVERRIDE, + ANATOP_BASE_ADDR + ANADIG_SYS_PLL + REG_SET); + writel(BM_ANATOP_ENET_PLL_OVERRIDE, + ANATOP_BASE_ADDR + ANADIG_ENET_PLL + REG_SET); + writel(BM_ANATOP_AUDIO_PLL_OVERRIDE, + ANATOP_BASE_ADDR + ANADIG_AUDIO_PLL + REG_SET); + writel(BM_ANATOP_VIDEO_PLL_OVERRIDE, + ANATOP_BASE_ADDR + ANADIG_VIDEO_PLL + REG_SET); +} + +static inline void imx_pll_resume(void) +{ + writel(BM_ANATOP_ARM_PLL_OVERRIDE, + ANATOP_BASE_ADDR + ANADIG_ARM_PLL + REG_CLR); + writel(BM_ANATOP_DDR_PLL_OVERRIDE, + ANATOP_BASE_ADDR + ANADIG_DDR_PLL + REG_CLR); + writel(BM_ANATOP_SYS_PLL_OVERRIDE, + ANATOP_BASE_ADDR + ANADIG_SYS_PLL + REG_CLR); + writel(BM_ANATOP_ENET_PLL_OVERRIDE, + ANATOP_BASE_ADDR + ANADIG_ENET_PLL + REG_CLR); + writel(BM_ANATOP_AUDIO_PLL_OVERRIDE, + ANATOP_BASE_ADDR + ANADIG_AUDIO_PLL + REG_CLR); + writel(BM_ANATOP_VIDEO_PLL_OVERRIDE, + ANATOP_BASE_ADDR + ANADIG_VIDEO_PLL + REG_CLR); +} + +static inline void imx_udelay(u32 usec) +{ + u32 freq; + u64 start, end; + + asm volatile("mrc p15, 0, %0, c14, c0, 0" : "=r" (freq)); + asm volatile("mrrc p15, 0, %Q0, %R0, c14" : "=r" (start)); + do { + asm volatile("mrrc p15, 0, %Q0, %R0, c14" : "=r" (end)); + if ((end - start) > usec * (freq / 1000000)) + break; + } while (1); +} + +static inline void imx_ddrc_enter_self_refresh(void) +{ + writel(0, DDRC_IPS_BASE_ADDR + DDRC_PWRCTL); + while (readl(DDRC_IPS_BASE_ADDR + DDRC_PSTAT) & 0x10001) + ; + + writel(0x20, DDRC_IPS_BASE_ADDR + DDRC_PWRCTL); + while ((readl(DDRC_IPS_BASE_ADDR + DDRC_STAT) & 0x23) != 0x23) + ; + writel(readl(DDRC_IPS_BASE_ADDR + DDRC_PWRCTL) | 0x8, + DDRC_IPS_BASE_ADDR + DDRC_PWRCTL); +} + +static inline void imx_ddrc_exit_self_refresh(void) +{ + writel(0, DDRC_IPS_BASE_ADDR + DDRC_PWRCTL); + while ((readl(DDRC_IPS_BASE_ADDR + DDRC_STAT) & 0x3) == 0x3) + ; + writel(readl(DDRC_IPS_BASE_ADDR + DDRC_PWRCTL) | 0x1, + DDRC_IPS_BASE_ADDR + DDRC_PWRCTL); +} + +__secure void imx_system_resume(void) +{ + unsigned int i, val, imr[4], entry; + + entry = psci_get_target_pc(0); + imx_ddrc_exit_self_refresh(); + imx_system_counter_resume(); + imx_gpcv2_set_lpm_mode(RUN); + imx_gpcv2_set_cpu_power_gate_by_lpm(0, false); + imx_gpcv2_set_plat_power_gate_by_lpm(false); + imx_gpcv2_set_m_core_pgc(false, GPC_PGC_C0); + imx_gpcv2_set_m_core_pgc(false, GPC_PGC_SCU); + + /* + * need to mask all interrupts in GPC before + * operating RBC configurations + */ + for (i = 0; i < 4; i++) { + imr[i] = readl(GPC_IPS_BASE_ADDR + GPC_IMR1_CORE0 + i * 4); + writel(~0, GPC_IPS_BASE_ADDR + GPC_IMR1_CORE0 + i * 4); + } + + /* configure RBC enable bit */ + val = readl(GPC_IPS_BASE_ADDR + GPC_SLPCR); + val &= ~BM_SLPCR_RBC_EN; + writel(val, GPC_IPS_BASE_ADDR + GPC_SLPCR); + + /* configure RBC count */ + val = readl(GPC_IPS_BASE_ADDR + GPC_SLPCR); + val &= ~BM_SLPCR_REG_BYPASS_COUNT; + writel(val, GPC_IPS_BASE_ADDR + GPC_SLPCR); + + /* + * need to delay at least 2 cycles of CKIL(32K) + * due to hardware design requirement, which is + * ~61us, here we use 65us for safe + */ + imx_udelay(65); + + /* restore GPC interrupt mask settings */ + for (i = 0; i < 4; i++) + writel(imr[i], GPC_IPS_BASE_ADDR + GPC_IMR1_CORE0 + i * 4); + + /* initialize gic distributor */ + gic_resume(); + _nonsec_init(); + + /* save cpu0 entry */ + psci_save(0, entry, 0); + psci_cpu_entry(); +} + +__secure void psci_system_suspend(u32 __always_unused function_id, + u32 ep, u32 context_id) +{ + u32 gpc_mask[4]; + u32 i, val; + + psci_save(0, ep, context_id); + /* overwrite PLL to be controlled by low power mode */ + imx_pll_suspend(); + imx_system_counter_suspend(); + /* set CA7 platform to enter STOP mode */ + imx_gpcv2_set_lpm_mode(STOP); + /* enable core0/scu power down/up with low power mode */ + imx_gpcv2_set_cpu_power_gate_by_lpm(0, true); + imx_gpcv2_set_plat_power_gate_by_lpm(true); + /* time slot settings for core0 and scu */ + imx_gpcv2_set_slot_ack(0, CORE0_A7, false, false); + imx_gpcv2_set_slot_ack(1, SCU_A7, false, true); + imx_gpcv2_set_slot_ack(5, SCU_A7, true, false); + imx_gpcv2_set_slot_ack(6, CORE0_A7, true, true); + imx_gpcv2_set_m_core_pgc(true, GPC_PGC_C0); + imx_gpcv2_set_m_core_pgc(true, GPC_PGC_SCU); + psci_v7_flush_dcache_all(); + + imx_ddrc_enter_self_refresh(); + + /* + * e10133: ARM: Boot failure after A7 enters into + * low-power idle mode + * + * Workaround: + * If both CPU0/CPU1 are IDLE, the last IDLE CPU should + * disable GIC first, then REG_BYPASS_COUNTER is used + * to mask wakeup INT, and then execute “wfi” is used to + * bring the system into power down processing safely. + * The counter must be enabled as close to the “wfi” state + * as possible. The following equation can be used to + * determine the RBC counter value: + * RBC_COUNT * (1/32K RTC frequency) >= + * (46 + PDNSCR_SW + PDNSCR_SW2ISO ) ( 1/IPG_CLK frequency ). + */ + + /* disable GIC distributor */ + writel(0, GIC400_ARB_BASE_ADDR + GIC_DIST_OFFSET); + + for (i = 0; i < 4; i++) + gpc_mask[i] = readl(GPC_IPS_BASE_ADDR + GPC_IMR1_CORE0 + i * 4); + + /* + * enable the RBC bypass counter here + * to hold off the interrupts. RBC counter + * = 8 (240us). With this setting, the latency + * from wakeup interrupt to ARM power up + * is ~250uS. + */ + val = readl(GPC_IPS_BASE_ADDR + GPC_SLPCR); + val &= ~(0x3f << 24); + val |= (0x8 << 24); + writel(val, GPC_IPS_BASE_ADDR + GPC_SLPCR); + + /* enable the counter. */ + val = readl(GPC_IPS_BASE_ADDR + GPC_SLPCR); + val |= (1 << 30); + writel(val, GPC_IPS_BASE_ADDR + GPC_SLPCR); + + /* unmask all the GPC interrupts. */ + for (i = 0; i < 4; i++) + writel(gpc_mask[i], GPC_IPS_BASE_ADDR + GPC_IMR1_CORE0 + i * 4); + + /* + * now delay for a short while (3usec) + * ARM is at 1GHz at this point + * so a short loop should be enough. + * this delay is required to ensure that + * the RBC counter can start counting in + * case an interrupt is already pending + * or in case an interrupt arrives just + * as ARM is about to assert DSM_request. + */ + imx_udelay(3); + + /* save resume entry and sp in CPU0 GPR registers */ + asm volatile("mov %0, sp" : "=r" (val)); + writel((u32)psci_system_resume, SRC_BASE_ADDR + SRC_GPR1_MX7D); + writel(val, SRC_BASE_ADDR + SRC_GPR2_MX7D); + + /* sleep */ + while (1) + wfi(); +} diff --git a/roms/u-boot/arch/arm/mach-imx/mx7/psci-suspend.S b/roms/u-boot/arch/arm/mach-imx/mx7/psci-suspend.S new file mode 100644 index 000000000..a21403f73 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/mx7/psci-suspend.S @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2018 NXP + */ + +#include <config.h> +#include <linux/linkage.h> + +#include <asm/armv7.h> +#include <asm/psci.h> + + .pushsection ._secure.text, "ax" + + .arch_extension sec + +.globl v7_invalidate_l1 +v7_invalidate_l1: + mov r0, #0 + mcr p15, 2, r0, c0, c0, 0 + mrc p15, 1, r0, c0, c0, 0 + + movw r1, #0x7fff + and r2, r1, r0, lsr #13 + + movw r1, #0x3ff + + and r3, r1, r0, lsr #3 @ NumWays - 1 + add r2, r2, #1 @ NumSets + + and r0, r0, #0x7 + add r0, r0, #4 @ SetShift + + clz r1, r3 @ WayShift + add r4, r3, #1 @ NumWays +1: + sub r2, r2, #1 @ NumSets-- + mov r3, r4 @ Temp = NumWays +2: + subs r3, r3, #1 @ Temp-- + mov r5, r3, lsl r1 + mov r6, r2, lsl r0 + orr r5, r5, r6 @ Reg = (Temp<<WayShift)|(NumSets<<SetShift) + mcr p15, 0, r5, c7, c6, 2 + bgt 2b + cmp r2, #0 + bgt 1b + dsb st + isb + mov pc, lr + +.globl psci_system_resume +psci_system_resume: + mov sp, r0 + + /* invalidate L1 I-cache first */ + mov r6, #0x0 + mcr p15, 0, r6, c7, c5, 0 + mcr p15, 0, r6, c7, c5, 6 + /* enable the Icache and branch prediction */ + mov r6, #0x1800 + mcr p15, 0, r6, c1, c0, 0 + isb + + bl v7_invalidate_l1 + b imx_system_resume + + .popsection diff --git a/roms/u-boot/arch/arm/mach-imx/mx7/snvs.c b/roms/u-boot/arch/arm/mach-imx/mx7/snvs.c new file mode 100644 index 000000000..359bbbb41 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/mx7/snvs.c @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2018 Linaro + */ + +#include <asm/io.h> +#include <asm/arch/imx-regs.h> +#include <linux/bitops.h> + +#define SNVS_HPCOMR 0x04 +#define SNVS_HPCOMR_NPSWA_EN BIT(31) + +void init_snvs(void) +{ + u32 val; + + /* Ensure SNVS HPCOMR sets NPSWA_EN to allow unpriv access to SNVS LP */ + val = readl(SNVS_BASE_ADDR + SNVS_HPCOMR); + val |= SNVS_HPCOMR_NPSWA_EN; + writel(val, SNVS_BASE_ADDR + SNVS_HPCOMR); +} diff --git a/roms/u-boot/arch/arm/mach-imx/mx7/soc.c b/roms/u-boot/arch/arm/mach-imx/mx7/soc.c new file mode 100644 index 000000000..fda25ba66 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/mx7/soc.c @@ -0,0 +1,438 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2015 Freescale Semiconductor, Inc. + */ + +#include <common.h> +#include <init.h> +#include <asm/io.h> +#include <asm/arch/imx-regs.h> +#include <asm/arch/clock.h> +#include <asm/arch/sys_proto.h> +#include <asm/mach-imx/dma.h> +#include <asm/mach-imx/hab.h> +#include <asm/mach-imx/rdc-sema.h> +#include <asm/arch/imx-rdc.h> +#include <asm/mach-imx/boot_mode.h> +#include <asm/arch/crm_regs.h> +#include <dm.h> +#include <env.h> +#include <imx_thermal.h> +#include <fsl_sec.h> +#include <asm/setup.h> +#include <linux/delay.h> + +#define IOMUXC_GPR1 0x4 +#define BM_IOMUXC_GPR1_IRQ 0x1000 + +#define GPC_LPCR_A7_BSC 0x0 +#define GPC_LPCR_M4 0x8 +#define GPC_SLPCR 0x14 +#define GPC_PGC_ACK_SEL_A7 0x24 +#define GPC_IMR1_CORE0 0x30 +#define GPC_IMR1_CORE1 0x40 +#define GPC_IMR1_M4 0x50 +#define GPC_PGC_CPU_MAPPING 0xec +#define GPC_PGC_C0_PUPSCR 0x804 +#define GPC_PGC_SCU_TIMING 0x890 +#define GPC_PGC_C1_PUPSCR 0x844 + +#define BM_LPCR_A7_BSC_IRQ_SRC_A7_WAKEUP 0x70000000 +#define BM_LPCR_A7_BSC_CPU_CLK_ON_LPM 0x4000 +#define BM_LPCR_M4_MASK_DSM_TRIGGER 0x80000000 +#define BM_SLPCR_EN_DSM 0x80000000 +#define BM_SLPCR_RBC_EN 0x40000000 +#define BM_SLPCR_REG_BYPASS_COUNT 0x3f000000 +#define BM_SLPCR_VSTBY 0x4 +#define BM_SLPCR_SBYOS 0x2 +#define BM_SLPCR_BYPASS_PMIC_READY 0x1 +#define BM_SLPCR_EN_A7_FASTWUP_WAIT_MODE 0x10000 + +#define BM_GPC_PGC_ACK_SEL_A7_DUMMY_PUP_ACK 0x80000000 +#define BM_GPC_PGC_ACK_SEL_A7_DUMMY_PDN_ACK 0x8000 + +#define BM_GPC_PGC_CORE_PUPSCR 0x7fff80 + +#if defined(CONFIG_IMX_THERMAL) +static const struct imx_thermal_plat imx7_thermal_plat = { + .regs = (void *)ANATOP_BASE_ADDR, + .fuse_bank = 3, + .fuse_word = 3, +}; + +U_BOOT_DRVINFO(imx7_thermal) = { + .name = "imx_thermal", + .plat = &imx7_thermal_plat, +}; +#endif + +#if CONFIG_IS_ENABLED(IMX_RDC) +/* + * In current design, if any peripheral was assigned to both A7 and M4, + * it will receive ipg_stop or ipg_wait when any of the 2 platforms enter + * low power mode. So M4 sleep will cause some peripherals fail to work + * at A7 core side. At default, all resources are in domain 0 - 3. + * + * There are 26 peripherals impacted by this IC issue: + * SIM2(sim2/emvsim2) + * SIM1(sim1/emvsim1) + * UART1/UART2/UART3/UART4/UART5/UART6/UART7 + * SAI1/SAI2/SAI3 + * WDOG1/WDOG2/WDOG3/WDOG4 + * GPT1/GPT2/GPT3/GPT4 + * PWM1/PWM2/PWM3/PWM4 + * ENET1/ENET2 + * Software Workaround: + * Here we setup some resources to domain 0 where M4 codes will move + * the M4 out of this domain. Then M4 is not able to access them any longer. + * This is a workaround for ic issue. So the peripherals are not shared + * by them. This way requires the uboot implemented the RDC driver and + * set the 26 IPs above to domain 0 only. M4 code will assign resource + * to its own domain, if it want to use the resource. + */ +static rdc_peri_cfg_t const resources[] = { + (RDC_PER_SIM1 | RDC_DOMAIN(0)), + (RDC_PER_SIM2 | RDC_DOMAIN(0)), + (RDC_PER_UART1 | RDC_DOMAIN(0)), + (RDC_PER_UART2 | RDC_DOMAIN(0)), + (RDC_PER_UART3 | RDC_DOMAIN(0)), + (RDC_PER_UART4 | RDC_DOMAIN(0)), + (RDC_PER_UART5 | RDC_DOMAIN(0)), + (RDC_PER_UART6 | RDC_DOMAIN(0)), + (RDC_PER_UART7 | RDC_DOMAIN(0)), + (RDC_PER_SAI1 | RDC_DOMAIN(0)), + (RDC_PER_SAI2 | RDC_DOMAIN(0)), + (RDC_PER_SAI3 | RDC_DOMAIN(0)), + (RDC_PER_WDOG1 | RDC_DOMAIN(0)), + (RDC_PER_WDOG2 | RDC_DOMAIN(0)), + (RDC_PER_WDOG3 | RDC_DOMAIN(0)), + (RDC_PER_WDOG4 | RDC_DOMAIN(0)), + (RDC_PER_GPT1 | RDC_DOMAIN(0)), + (RDC_PER_GPT2 | RDC_DOMAIN(0)), + (RDC_PER_GPT3 | RDC_DOMAIN(0)), + (RDC_PER_GPT4 | RDC_DOMAIN(0)), + (RDC_PER_PWM1 | RDC_DOMAIN(0)), + (RDC_PER_PWM2 | RDC_DOMAIN(0)), + (RDC_PER_PWM3 | RDC_DOMAIN(0)), + (RDC_PER_PWM4 | RDC_DOMAIN(0)), + (RDC_PER_ENET1 | RDC_DOMAIN(0)), + (RDC_PER_ENET2 | RDC_DOMAIN(0)), +}; + +static void isolate_resource(void) +{ + imx_rdc_setup_peripherals(resources, ARRAY_SIZE(resources)); +} +#endif + +#if defined(CONFIG_IMX_HAB) +struct imx_sec_config_fuse_t const imx_sec_config_fuse = { + .bank = 1, + .word = 3, +}; +#endif + +static bool is_mx7d(void) +{ + struct ocotp_regs *ocotp = (struct ocotp_regs *)OCOTP_BASE_ADDR; + struct fuse_bank *bank = &ocotp->bank[1]; + struct fuse_bank1_regs *fuse = + (struct fuse_bank1_regs *)bank->fuse_regs; + int val; + + val = readl(&fuse->tester4); + if (val & 1) + return false; + else + return true; +} + +u32 get_cpu_rev(void) +{ + struct mxc_ccm_anatop_reg *ccm_anatop = (struct mxc_ccm_anatop_reg *) + ANATOP_BASE_ADDR; + u32 reg = readl(&ccm_anatop->digprog); + u32 type = (reg >> 16) & 0xff; + + if (!is_mx7d()) + type = MXC_CPU_MX7S; + + reg &= 0xff; + return (type << 12) | reg; +} + +#ifdef CONFIG_REVISION_TAG +u32 __weak get_board_rev(void) +{ + return get_cpu_rev(); +} +#endif + +static void imx_enet_mdio_fixup(void) +{ + struct iomuxc_gpr_base_regs *gpr_regs = + (struct iomuxc_gpr_base_regs *)IOMUXC_GPR_BASE_ADDR; + + /* + * The management data input/output (MDIO) requires open-drain, + * i.MX7D TO1.0 ENET MDIO pin has no open drain, but TO1.1 supports + * this feature. So to TO1.1, need to enable open drain by setting + * bits GPR0[8:7]. + */ + + if (soc_rev() >= CHIP_REV_1_1) { + setbits_le32(&gpr_regs->gpr[0], + IOMUXC_GPR_GPR0_ENET_MDIO_OPEN_DRAIN_MASK); + } +} + +static void init_cpu_basic(void) +{ + imx_enet_mdio_fixup(); + +#ifdef CONFIG_APBH_DMA + /* Start APBH DMA */ + mxs_dma_init(); +#endif +} + +#ifdef CONFIG_IMX_BOOTAUX +/* + * Table of mappings of physical mem regions in both + * Cortex-A7 and Cortex-M4 address spaces. + * + * For additional details check sections 2.1.2 and 2.1.3 in + * i.MX7Dual Applications Processor Reference Manual + * + */ +const struct rproc_att hostmap[] = { + /* aux core , host core, size */ + { 0x00000000, 0x00180000, 0x8000 }, /* OCRAM_S */ + { 0x00180000, 0x00180000, 0x8000 }, /* OCRAM_S */ + { 0x20180000, 0x00180000, 0x8000 }, /* OCRAM_S */ + { 0x1fff8000, 0x007f8000, 0x8000 }, /* TCML */ + { 0x20000000, 0x00800000, 0x8000 }, /* TCMU */ + { 0x00900000, 0x00900000, 0x20000 }, /* OCRAM_128KB */ + { 0x20200000, 0x00900000, 0x20000 }, /* OCRAM_128KB */ + { 0x00920000, 0x00920000, 0x20000 }, /* OCRAM_EPDC */ + { 0x20220000, 0x00920000, 0x20000 }, /* OCRAM_EPDC */ + { 0x00940000, 0x00940000, 0x20000 }, /* OCRAM_PXP */ + { 0x20240000, 0x00940000, 0x20000 }, /* OCRAM_PXP */ + { 0x10000000, 0x80000000, 0x0fff0000 }, /* DDR Code alias */ + { 0x80000000, 0x80000000, 0x60000000 }, /* DDRC */ + { /* sentinel */ } +}; +#endif + +#ifndef CONFIG_SKIP_LOWLEVEL_INIT +/* enable all periherial can be accessed in nosec mode */ +static void init_csu(void) +{ + int i = 0; + + for (i = 0; i < CSU_NUM_REGS; i++) + writel(CSU_INIT_SEC_LEVEL0, CSU_IPS_BASE_ADDR + i * 4); +} + +static void imx_gpcv2_init(void) +{ + u32 val, i; + + /* + * Force IOMUXC irq pending, so that the interrupt to GPC can be + * used to deassert dsm_request signal when the signal gets + * asserted unexpectedly. + */ + val = readl(IOMUXC_GPR_BASE_ADDR + IOMUXC_GPR1); + val |= BM_IOMUXC_GPR1_IRQ; + writel(val, IOMUXC_GPR_BASE_ADDR + IOMUXC_GPR1); + + /* Initially mask all interrupts */ + for (i = 0; i < 4; i++) { + writel(~0, GPC_IPS_BASE_ADDR + GPC_IMR1_CORE0 + i * 4); + writel(~0, GPC_IPS_BASE_ADDR + GPC_IMR1_CORE1 + i * 4); + writel(~0, GPC_IPS_BASE_ADDR + GPC_IMR1_M4 + i * 4); + } + + /* set SCU timing */ + writel((0x59 << 10) | 0x5B | (0x2 << 20), + GPC_IPS_BASE_ADDR + GPC_PGC_SCU_TIMING); + + /* only external IRQs to wake up LPM and core 0/1 */ + val = readl(GPC_IPS_BASE_ADDR + GPC_LPCR_A7_BSC); + val |= BM_LPCR_A7_BSC_IRQ_SRC_A7_WAKEUP; + writel(val, GPC_IPS_BASE_ADDR + GPC_LPCR_A7_BSC); + + /* set C0 power up timming per design requirement */ + val = readl(GPC_IPS_BASE_ADDR + GPC_PGC_C0_PUPSCR); + val &= ~BM_GPC_PGC_CORE_PUPSCR; + val |= (0x1A << 7); + writel(val, GPC_IPS_BASE_ADDR + GPC_PGC_C0_PUPSCR); + + /* set C1 power up timming per design requirement */ + val = readl(GPC_IPS_BASE_ADDR + GPC_PGC_C1_PUPSCR); + val &= ~BM_GPC_PGC_CORE_PUPSCR; + val |= (0x1A << 7); + writel(val, GPC_IPS_BASE_ADDR + GPC_PGC_C1_PUPSCR); + + /* dummy ack for time slot by default */ + writel(BM_GPC_PGC_ACK_SEL_A7_DUMMY_PUP_ACK | + BM_GPC_PGC_ACK_SEL_A7_DUMMY_PDN_ACK, + GPC_IPS_BASE_ADDR + GPC_PGC_ACK_SEL_A7); + + /* mask M4 DSM trigger */ + writel(readl(GPC_IPS_BASE_ADDR + GPC_LPCR_M4) | + BM_LPCR_M4_MASK_DSM_TRIGGER, + GPC_IPS_BASE_ADDR + GPC_LPCR_M4); + + /* set mega/fast mix in A7 domain */ + writel(0x1, GPC_IPS_BASE_ADDR + GPC_PGC_CPU_MAPPING); + + /* DSM related settings */ + val = readl(GPC_IPS_BASE_ADDR + GPC_SLPCR); + val &= ~(BM_SLPCR_EN_DSM | BM_SLPCR_VSTBY | BM_SLPCR_RBC_EN | + BM_SLPCR_SBYOS | BM_SLPCR_BYPASS_PMIC_READY | + BM_SLPCR_REG_BYPASS_COUNT); + val |= BM_SLPCR_EN_A7_FASTWUP_WAIT_MODE; + writel(val, GPC_IPS_BASE_ADDR + GPC_SLPCR); + + /* + * disabling RBC need to delay at least 2 cycles of CKIL(32K) + * due to hardware design requirement, which is + * ~61us, here we use 65us for safe + */ + udelay(65); +} + +int arch_cpu_init(void) +{ + init_aips(); + + init_csu(); + /* Disable PDE bit of WMCR register */ + imx_wdog_disable_powerdown(); + + init_cpu_basic(); + +#if CONFIG_IS_ENABLED(IMX_RDC) + isolate_resource(); +#endif + + init_snvs(); + + imx_gpcv2_init(); + + return 0; +} +#else +int arch_cpu_init(void) +{ + init_cpu_basic(); + + return 0; +} +#endif + +#ifdef CONFIG_ARCH_MISC_INIT +int arch_misc_init(void) +{ +#ifdef CONFIG_ENV_VARS_UBOOT_RUNTIME_CONFIG + if (is_mx7d()) + env_set("soc", "imx7d"); + else + env_set("soc", "imx7s"); +#endif + +#ifdef CONFIG_FSL_CAAM + sec_init(); +#endif + + return 0; +} +#endif + +#ifdef CONFIG_SERIAL_TAG +/* + * OCOTP_TESTER + * i.MX 7Solo Applications Processor Reference Manual, Rev. 0.1, 08/2016 + * OCOTP_TESTER describes a unique ID based on silicon wafer + * and die X/Y position + * + * OCOTOP_TESTER offset 0x410 + * 31:0 fuse 0 + * FSL-wide unique, encoded LOT ID STD II/SJC CHALLENGE/ Unique ID + * + * OCOTP_TESTER1 offset 0x420 + * 31:24 fuse 1 + * The X-coordinate of the die location on the wafer/SJC CHALLENGE/ Unique ID + * 23:16 fuse 1 + * The Y-coordinate of the die location on the wafer/SJC CHALLENGE/ Unique ID + * 15:11 fuse 1 + * The wafer number of the wafer on which the device was fabricated/SJC + * CHALLENGE/ Unique ID + * 10:0 fuse 1 + * FSL-wide unique, encoded LOT ID STD II/SJC CHALLENGE/ Unique ID + */ +void get_board_serial(struct tag_serialnr *serialnr) +{ + struct ocotp_regs *ocotp = (struct ocotp_regs *)OCOTP_BASE_ADDR; + struct fuse_bank *bank = &ocotp->bank[0]; + struct fuse_bank0_regs *fuse = + (struct fuse_bank0_regs *)bank->fuse_regs; + + serialnr->low = fuse->tester0; + serialnr->high = fuse->tester1; +} +#endif + +void set_wdog_reset(struct wdog_regs *wdog) +{ + u32 reg = readw(&wdog->wcr); + /* + * Output WDOG_B signal to reset external pmic or POR_B decided by + * the board desgin. Without external reset, the peripherals/DDR/ + * PMIC are not reset, that may cause system working abnormal. + */ + reg = readw(&wdog->wcr); + reg |= 1 << 3; + /* + * WDZST bit is write-once only bit. Align this bit in kernel, + * otherwise kernel code will have no chance to set this bit. + */ + reg |= 1 << 0; + writew(reg, &wdog->wcr); +} + +void s_init(void) +{ + /* clock configuration. */ + clock_init(); + + return; +} + +#ifndef CONFIG_SPL_BUILD +const struct boot_mode soc_boot_modes[] = { + {"normal", MAKE_CFGVAL(0x00, 0x00, 0x00, 0x00)}, + {"primary", MAKE_CFGVAL_PRIMARY_BOOT}, + {"secondary", MAKE_CFGVAL_SECONDARY_BOOT}, + {NULL, 0}, +}; + +int boot_mode_getprisec(void) +{ + struct src *psrc = (struct src *)SRC_BASE_ADDR; + + return !!(readl(&psrc->gpr10) & IMX7_SRC_GPR10_PERSIST_SECONDARY_BOOT); +} +#endif + +void reset_misc(void) +{ +#ifndef CONFIG_SPL_BUILD +#if defined(CONFIG_VIDEO_MXS) && !defined(CONFIG_DM_VIDEO) + lcdif_power_down(); +#endif +#endif +} + diff --git a/roms/u-boot/arch/arm/mach-imx/mx7ulp/Kconfig b/roms/u-boot/arch/arm/mach-imx/mx7ulp/Kconfig new file mode 100644 index 000000000..2ffac9cf7 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/mx7ulp/Kconfig @@ -0,0 +1,34 @@ +if ARCH_MX7ULP + +config SYS_SOC + default "mx7ulp" + +config LDO_ENABLED_MODE + bool "i.MX7ULP LDO Enabled Mode" + help + Select this option to enable the PMC1 LDO. + +config MX7ULP + select HAS_CAAM + bool + +choice + prompt "MX7ULP board select" + optional + +config TARGET_MX7ULP_COM + bool "Support MX7ULP COM board" + select MX7ULP + select SYS_ARCH_TIMER + +config TARGET_MX7ULP_EVK + bool "Support mx7ulp EVK board" + select MX7ULP + select SYS_ARCH_TIMER + +endchoice + +source "board/ea/mx7ulp_com/Kconfig" +source "board/freescale/mx7ulp_evk/Kconfig" + +endif diff --git a/roms/u-boot/arch/arm/mach-imx/mx7ulp/Makefile b/roms/u-boot/arch/arm/mach-imx/mx7ulp/Makefile new file mode 100644 index 000000000..adb8d7aec --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/mx7ulp/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright 2016 Freescale Semiconductor, Inc. +# + +obj-y := soc.o clock.o iomux.o pcc.o scg.o diff --git a/roms/u-boot/arch/arm/mach-imx/mx7ulp/clock.c b/roms/u-boot/arch/arm/mach-imx/mx7ulp/clock.c new file mode 100644 index 000000000..619115391 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/mx7ulp/clock.c @@ -0,0 +1,370 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2016 Freescale Semiconductor, Inc. + */ + +#include <common.h> +#include <clock_legacy.h> +#include <command.h> +#include <div64.h> +#include <asm/global_data.h> +#include <asm/io.h> +#include <errno.h> +#include <asm/arch/clock.h> +#include <asm/arch/sys_proto.h> + +DECLARE_GLOBAL_DATA_PTR; + +int get_clocks(void) +{ +#ifdef CONFIG_FSL_ESDHC_IMX +#if CONFIG_SYS_FSL_ESDHC_ADDR == USDHC0_RBASE + gd->arch.sdhc_clk = mxc_get_clock(MXC_ESDHC_CLK); +#elif CONFIG_SYS_FSL_ESDHC_ADDR == USDHC1_RBASE + gd->arch.sdhc_clk = mxc_get_clock(MXC_ESDHC2_CLK); +#endif +#endif + return 0; +} + +static u32 get_fast_plat_clk(void) +{ + return scg_clk_get_rate(SCG_NIC0_CLK); +} + +static u32 get_slow_plat_clk(void) +{ + return scg_clk_get_rate(SCG_NIC1_CLK); +} + +static u32 get_ipg_clk(void) +{ + return scg_clk_get_rate(SCG_NIC1_BUS_CLK); +} + +u32 get_lpuart_clk(void) +{ + int index = 0; + + const u32 lpuart_array[] = { + LPUART0_RBASE, + LPUART1_RBASE, + LPUART2_RBASE, + LPUART3_RBASE, + LPUART4_RBASE, + LPUART5_RBASE, + LPUART6_RBASE, + LPUART7_RBASE, + }; + + const enum pcc_clk lpuart_pcc_clks[] = { + PER_CLK_LPUART4, + PER_CLK_LPUART5, + PER_CLK_LPUART6, + PER_CLK_LPUART7, + }; + + for (index = 0; index < 8; index++) { + if (lpuart_array[index] == LPUART_BASE) + break; + } + + if (index < 4 || index > 7) + return 0; + + return pcc_clock_get_rate(lpuart_pcc_clks[index - 4]); +} + +#ifdef CONFIG_SYS_I2C_IMX_LPI2C +int enable_i2c_clk(unsigned char enable, unsigned i2c_num) +{ + /* Set parent to FIRC DIV2 clock */ + const enum pcc_clk lpi2c_pcc_clks[] = { + PER_CLK_LPI2C4, + PER_CLK_LPI2C5, + PER_CLK_LPI2C6, + PER_CLK_LPI2C7, + }; + + if (i2c_num < 4 || i2c_num > 7) + return -EINVAL; + + if (enable) { + pcc_clock_enable(lpi2c_pcc_clks[i2c_num - 4], false); + pcc_clock_sel(lpi2c_pcc_clks[i2c_num - 4], SCG_FIRC_DIV2_CLK); + pcc_clock_enable(lpi2c_pcc_clks[i2c_num - 4], true); + } else { + pcc_clock_enable(lpi2c_pcc_clks[i2c_num - 4], false); + } + return 0; +} + +u32 imx_get_i2cclk(unsigned i2c_num) +{ + const enum pcc_clk lpi2c_pcc_clks[] = { + PER_CLK_LPI2C4, + PER_CLK_LPI2C5, + PER_CLK_LPI2C6, + PER_CLK_LPI2C7, + }; + + if (i2c_num < 4 || i2c_num > 7) + return 0; + + return pcc_clock_get_rate(lpi2c_pcc_clks[i2c_num - 4]); +} +#endif + +unsigned int mxc_get_clock(enum mxc_clock clk) +{ + switch (clk) { + case MXC_ARM_CLK: + return scg_clk_get_rate(SCG_CORE_CLK); + case MXC_AXI_CLK: + return get_fast_plat_clk(); + case MXC_AHB_CLK: + return get_slow_plat_clk(); + case MXC_IPG_CLK: + return get_ipg_clk(); + case MXC_I2C_CLK: + return pcc_clock_get_rate(PER_CLK_LPI2C4); + case MXC_UART_CLK: + return get_lpuart_clk(); + case MXC_ESDHC_CLK: + return pcc_clock_get_rate(PER_CLK_USDHC0); + case MXC_ESDHC2_CLK: + return pcc_clock_get_rate(PER_CLK_USDHC1); + case MXC_DDR_CLK: + return scg_clk_get_rate(SCG_DDR_CLK); + default: + printf("Unsupported mxc_clock %d\n", clk); + break; + } + + return 0; +} + +void init_clk_usdhc(u32 index) +{ + switch (index) { + case 0: + /*Disable the clock before configure it */ + pcc_clock_enable(PER_CLK_USDHC0, false); + + /* 158MHz / 1 = 158MHz */ + pcc_clock_sel(PER_CLK_USDHC0, SCG_NIC1_CLK); + pcc_clock_div_config(PER_CLK_USDHC0, false, 1); + pcc_clock_enable(PER_CLK_USDHC0, true); + break; + case 1: + /*Disable the clock before configure it */ + pcc_clock_enable(PER_CLK_USDHC1, false); + + /* 158MHz / 1 = 158MHz */ + pcc_clock_sel(PER_CLK_USDHC1, SCG_NIC1_CLK); + pcc_clock_div_config(PER_CLK_USDHC1, false, 1); + pcc_clock_enable(PER_CLK_USDHC1, true); + break; + default: + printf("Invalid index for USDHC %d\n", index); + break; + } +} + +#ifdef CONFIG_MXC_OCOTP + +#define OCOTP_CTRL_PCC1_SLOT (38) +#define OCOTP_CTRL_HIGH4K_PCC1_SLOT (39) + +void enable_ocotp_clk(unsigned char enable) +{ + u32 val; + + /* + * Seems the OCOTP CLOCKs have been enabled at default, + * check its inuse flag + */ + + val = readl(PCC1_RBASE + 4 * OCOTP_CTRL_PCC1_SLOT); + if (!(val & PCC_INUSE_MASK)) + writel(PCC_CGC_MASK, (PCC1_RBASE + 4 * OCOTP_CTRL_PCC1_SLOT)); + + val = readl(PCC1_RBASE + 4 * OCOTP_CTRL_HIGH4K_PCC1_SLOT); + if (!(val & PCC_INUSE_MASK)) + writel(PCC_CGC_MASK, + (PCC1_RBASE + 4 * OCOTP_CTRL_HIGH4K_PCC1_SLOT)); +} +#endif + +void enable_usboh3_clk(unsigned char enable) +{ + if (enable) { + pcc_clock_enable(PER_CLK_USB0, false); + pcc_clock_sel(PER_CLK_USB0, SCG_NIC1_BUS_CLK); + pcc_clock_enable(PER_CLK_USB0, true); + +#ifdef CONFIG_USB_MAX_CONTROLLER_COUNT + if (CONFIG_USB_MAX_CONTROLLER_COUNT > 1) { + pcc_clock_enable(PER_CLK_USB1, false); + pcc_clock_sel(PER_CLK_USB1, SCG_NIC1_BUS_CLK); + pcc_clock_enable(PER_CLK_USB1, true); + } +#endif + + pcc_clock_enable(PER_CLK_USB_PHY, true); + pcc_clock_enable(PER_CLK_USB_PL301, true); + } else { + pcc_clock_enable(PER_CLK_USB0, false); + pcc_clock_enable(PER_CLK_USB1, false); + pcc_clock_enable(PER_CLK_USB_PHY, false); + pcc_clock_enable(PER_CLK_USB_PL301, false); + } +} + +static void lpuart_set_clk(uint32_t index, enum scg_clk clk) +{ + const enum pcc_clk lpuart_pcc_clks[] = { + PER_CLK_LPUART4, + PER_CLK_LPUART5, + PER_CLK_LPUART6, + PER_CLK_LPUART7, + }; + + if (index < 4 || index > 7) + return; + +#ifndef CONFIG_CLK_DEBUG + pcc_clock_enable(lpuart_pcc_clks[index - 4], false); +#endif + pcc_clock_sel(lpuart_pcc_clks[index - 4], clk); + pcc_clock_enable(lpuart_pcc_clks[index - 4], true); +} + +static void init_clk_lpuart(void) +{ + u32 index = 0, i; + + const u32 lpuart_array[] = { + LPUART0_RBASE, + LPUART1_RBASE, + LPUART2_RBASE, + LPUART3_RBASE, + LPUART4_RBASE, + LPUART5_RBASE, + LPUART6_RBASE, + LPUART7_RBASE, + }; + + for (i = 0; i < 8; i++) { + if (lpuart_array[i] == LPUART_BASE) { + index = i; + break; + } + } + + lpuart_set_clk(index, SCG_SOSC_DIV2_CLK); +} + +static void init_clk_rgpio2p(void) +{ + /*Enable RGPIO2P1 clock */ + pcc_clock_enable(PER_CLK_RGPIO2P1, true); + + /* + * Hard code to enable RGPIO2P0 clock since it is not + * in clock frame for A7 domain + */ + writel(PCC_CGC_MASK, (PCC0_RBASE + 0x3C)); +} + +/* Configure PLL/PFD freq */ +void clock_init(void) +{ + /* + * ROM has enabled clocks: + * A4 side: SIRC 16Mhz (DIV1-3 off), FIRC 48Mhz (DIV1-2 on), + * Non-LP-boot: SOSC, SPLL PFD0 (scs selected) + * A7 side: SPLL PFD0 (scs selected, 413Mhz), + * APLL PFD0 (352Mhz), DDRCLK, all NIC clocks + * A7 Plat0 (NIC0) = 176Mhz, Plat1 (NIC1) = 176Mhz, + * IP BUS (NIC1_BUS) = 58.6Mhz + * + * In u-boot: + * 1. Enable PFD1-3 of APLL for A7 side. Enable FIRC and DIVs. + * 2. Enable USB PLL + * 3. Init the clocks of peripherals used in u-boot bu + * without set rate interface.The clocks for these + * peripherals are enabled in this intialization. + * 4.Other peripherals with set clock rate interface + * does not be set in this function. + */ + + scg_a7_firc_init(); + + scg_a7_soscdiv_init(); + + scg_a7_init_core_clk(); + + /* APLL PFD1 = 270Mhz, PFD2=345.6Mhz, PFD3=800Mhz */ + scg_enable_pll_pfd(SCG_APLL_PFD1_CLK, 35); + scg_enable_pll_pfd(SCG_APLL_PFD2_CLK, 28); + scg_enable_pll_pfd(SCG_APLL_PFD3_CLK, 12); + + init_clk_lpuart(); + + init_clk_rgpio2p(); + + enable_usboh3_clk(1); +} + +#ifdef CONFIG_IMX_HAB +void hab_caam_clock_enable(unsigned char enable) +{ + if (enable) + pcc_clock_enable(PER_CLK_CAAM, true); + else + pcc_clock_enable(PER_CLK_CAAM, false); +} +#endif + +#ifndef CONFIG_SPL_BUILD +/* + * Dump some core clockes. + */ +int do_mx7_showclocks(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + + u32 freq; + freq = decode_pll(PLL_A7_SPLL); + printf("PLL_A7_SPLL %8d MHz\n", freq / 1000000); + + freq = decode_pll(PLL_A7_APLL); + printf("PLL_A7_APLL %8d MHz\n", freq / 1000000); + + freq = decode_pll(PLL_USB); + printf("PLL_USB %8d MHz\n", freq / 1000000); + + printf("\n"); + + printf("CORE %8d kHz\n", scg_clk_get_rate(SCG_CORE_CLK) / 1000); + printf("IPG %8d kHz\n", mxc_get_clock(MXC_IPG_CLK) / 1000); + printf("UART %8d kHz\n", mxc_get_clock(MXC_UART_CLK) / 1000); + printf("AHB %8d kHz\n", mxc_get_clock(MXC_AHB_CLK) / 1000); + printf("AXI %8d kHz\n", mxc_get_clock(MXC_AXI_CLK) / 1000); + printf("DDR %8d kHz\n", mxc_get_clock(MXC_DDR_CLK) / 1000); + printf("USDHC1 %8d kHz\n", mxc_get_clock(MXC_ESDHC_CLK) / 1000); + printf("USDHC2 %8d kHz\n", mxc_get_clock(MXC_ESDHC2_CLK) / 1000); + printf("I2C4 %8d kHz\n", mxc_get_clock(MXC_I2C_CLK) / 1000); + + scg_a7_info(); + + return 0; +} + +U_BOOT_CMD( + clocks, CONFIG_SYS_MAXARGS, 1, do_mx7_showclocks, + "display clocks", + "" +); +#endif diff --git a/roms/u-boot/arch/arm/mach-imx/mx7ulp/iomux.c b/roms/u-boot/arch/arm/mach-imx/mx7ulp/iomux.c new file mode 100644 index 000000000..05ddeed2a --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/mx7ulp/iomux.c @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2016 Freescale Semiconductor, Inc. + */ +#include <common.h> +#include <log.h> +#include <asm/io.h> +#include <asm/arch/imx-regs.h> +#include <asm/arch/iomux.h> + +static void *base = (void *)IOMUXC_BASE_ADDR; + +/* + * iomuxc0 base address. In imx7ulp-pins.h, + * the offsets of pins in iomuxc0 are from 0xD000, + * so we set the base address to (0x4103D000 - 0xD000 = 0x41030000) + */ +static void *base_mports = (void *)(AIPS0_BASE + 0x30000); + +/* + * configures a single pad in the iomuxer + */ +void mx7ulp_iomux_setup_pad(iomux_cfg_t pad) +{ + u32 mux_ctrl_ofs = (pad & MUX_CTRL_OFS_MASK) >> MUX_CTRL_OFS_SHIFT; + u32 mux_mode = (pad & MUX_MODE_MASK) >> MUX_MODE_SHIFT; + u32 sel_input_ofs = + (pad & MUX_SEL_INPUT_OFS_MASK) >> MUX_SEL_INPUT_OFS_SHIFT; + u32 sel_input = + (pad & MUX_SEL_INPUT_MASK) >> MUX_SEL_INPUT_SHIFT; + u32 pad_ctrl_ofs = mux_ctrl_ofs; + u32 pad_ctrl = (pad & MUX_PAD_CTRL_MASK) >> MUX_PAD_CTRL_SHIFT; + + debug("[PAD CFG] = 0x%16llX \r\n\tmux_ctl = 0x%X(0x%X) sel_input = 0x%X(0x%X) pad_ctrl = 0x%X(0x%X)\r\n", + pad, mux_ctrl_ofs, mux_mode, sel_input_ofs, sel_input, + pad_ctrl_ofs, pad_ctrl); + + if (mux_mode & IOMUX_CONFIG_MPORTS) { + mux_mode &= ~IOMUX_CONFIG_MPORTS; + base = base_mports; + } else { + base = (void *)IOMUXC_BASE_ADDR; + } + + __raw_writel(((mux_mode << IOMUXC_PCR_MUX_ALT_SHIFT) & + IOMUXC_PCR_MUX_ALT_MASK), base + mux_ctrl_ofs); + + if (sel_input_ofs) + __raw_writel((sel_input << IOMUXC_PSMI_IMUX_ALT_SHIFT), + base + sel_input_ofs); + + if (!(pad_ctrl & NO_PAD_CTRL)) + __raw_writel(((mux_mode << IOMUXC_PCR_MUX_ALT_SHIFT) & + IOMUXC_PCR_MUX_ALT_MASK) | + (pad_ctrl & (~IOMUXC_PCR_MUX_ALT_MASK)), + base + pad_ctrl_ofs); +} + +/* configures a list of pads within declared with IOMUX_PADS macro */ +void mx7ulp_iomux_setup_multiple_pads(iomux_cfg_t const *pad_list, + unsigned count) +{ + iomux_cfg_t const *p = pad_list; + int i; + + for (i = 0; i < count; i++) { + mx7ulp_iomux_setup_pad(*p); + p++; + } +} diff --git a/roms/u-boot/arch/arm/mach-imx/mx7ulp/pcc.c b/roms/u-boot/arch/arm/mach-imx/mx7ulp/pcc.c new file mode 100644 index 000000000..aa7ea86a4 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/mx7ulp/pcc.c @@ -0,0 +1,284 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2016 Freescale Semiconductor, Inc. + */ + +#include <common.h> +#include <div64.h> +#include <log.h> +#include <asm/io.h> +#include <errno.h> +#include <asm/arch/imx-regs.h> +#include <asm/arch/pcc.h> +#include <asm/arch/sys_proto.h> + +#define PCC_CLKSRC_TYPES 2 +#define PCC_CLKSRC_NUM 7 + +static enum scg_clk pcc_clksrc[PCC_CLKSRC_TYPES][PCC_CLKSRC_NUM] = { + { SCG_NIC1_BUS_CLK, + SCG_NIC1_CLK, + SCG_DDR_CLK, + SCG_APLL_PFD2_CLK, + SCG_APLL_PFD1_CLK, + SCG_APLL_PFD0_CLK, + USB_PLL_OUT, + }, + { SCG_SOSC_DIV2_CLK, /* SOSC BUS clock */ + MIPI_PLL_OUT, + SCG_FIRC_DIV2_CLK, /* FIRC BUS clock */ + SCG_ROSC_CLK, + SCG_NIC1_BUS_CLK, + SCG_NIC1_CLK, + SCG_APLL_PFD3_CLK, + }, +}; + +static struct pcc_entry pcc_arrays[] = { + {PCC2_RBASE, DMA1_PCC2_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV}, + {PCC2_RBASE, RGPIO1_PCC2_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV}, + {PCC2_RBASE, FLEXBUS0_PCC2_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV}, + {PCC2_RBASE, SEMA42_1_PCC2_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV}, + {PCC2_RBASE, DMA1_CH_MUX0_PCC2_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV}, + {PCC2_RBASE, SNVS_PCC2_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV}, + {PCC2_RBASE, CAAM_PCC2_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV}, + {PCC2_RBASE, LPTPM4_PCC2_SLOT, CLKSRC_PER_BUS, PCC_NO_DIV}, + {PCC2_RBASE, LPTPM5_PCC2_SLOT, CLKSRC_PER_BUS, PCC_NO_DIV}, + {PCC2_RBASE, LPIT1_PCC2_SLOT, CLKSRC_PER_BUS, PCC_NO_DIV}, + {PCC2_RBASE, LPSPI2_PCC2_SLOT, CLKSRC_PER_BUS, PCC_NO_DIV}, + {PCC2_RBASE, LPSPI3_PCC2_SLOT, CLKSRC_PER_BUS, PCC_NO_DIV}, + {PCC2_RBASE, LPI2C4_PCC2_SLOT, CLKSRC_PER_BUS, PCC_NO_DIV}, + {PCC2_RBASE, LPI2C5_PCC2_SLOT, CLKSRC_PER_BUS, PCC_NO_DIV}, + {PCC2_RBASE, LPUART4_PCC2_SLOT, CLKSRC_PER_BUS, PCC_NO_DIV}, + {PCC2_RBASE, LPUART5_PCC2_SLOT, CLKSRC_PER_BUS, PCC_NO_DIV}, + {PCC2_RBASE, FLEXIO1_PCC2_SLOT, CLKSRC_PER_BUS, PCC_NO_DIV}, + {PCC2_RBASE, USBOTG0_PCC2_SLOT, CLKSRC_PER_PLAT, PCC_HAS_DIV}, + {PCC2_RBASE, USBOTG1_PCC2_SLOT, CLKSRC_PER_PLAT, PCC_HAS_DIV}, + {PCC2_RBASE, USBPHY_PCC2_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV}, + {PCC2_RBASE, USB_PL301_PCC2_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV}, + {PCC2_RBASE, USDHC0_PCC2_SLOT, CLKSRC_PER_PLAT, PCC_HAS_DIV}, + {PCC2_RBASE, USDHC1_PCC2_SLOT, CLKSRC_PER_PLAT, PCC_HAS_DIV}, + {PCC2_RBASE, WDG1_PCC2_SLOT, CLKSRC_PER_BUS, PCC_HAS_DIV}, + {PCC2_RBASE, WDG2_PCC2_SLOT, CLKSRC_PER_BUS, PCC_HAS_DIV}, + + {PCC3_RBASE, LPTPM6_PCC3_SLOT, CLKSRC_PER_BUS, PCC_NO_DIV}, + {PCC3_RBASE, LPTPM7_PCC3_SLOT, CLKSRC_PER_BUS, PCC_NO_DIV}, + {PCC3_RBASE, LPI2C6_PCC3_SLOT, CLKSRC_PER_BUS, PCC_NO_DIV}, + {PCC3_RBASE, LPI2C7_PCC3_SLOT, CLKSRC_PER_BUS, PCC_NO_DIV}, + {PCC3_RBASE, LPUART6_PCC3_SLOT, CLKSRC_PER_BUS, PCC_NO_DIV}, + {PCC3_RBASE, LPUART7_PCC3_SLOT, CLKSRC_PER_BUS, PCC_NO_DIV}, + {PCC3_RBASE, VIU0_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV}, + {PCC3_RBASE, DSI0_PCC3_SLOT, CLKSRC_PER_BUS, PCC_HAS_DIV}, + {PCC3_RBASE, LCDIF0_PCC3_SLOT, CLKSRC_PER_PLAT, PCC_HAS_DIV}, + {PCC3_RBASE, MMDC0_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV}, + {PCC3_RBASE, PORTC_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV}, + {PCC3_RBASE, PORTD_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV}, + {PCC3_RBASE, PORTE_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV}, + {PCC3_RBASE, PORTF_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV}, + {PCC3_RBASE, GPU3D_PCC3_SLOT, CLKSRC_PER_PLAT, PCC_NO_DIV}, + {PCC3_RBASE, GPU2D_PCC3_SLOT, CLKSRC_PER_PLAT, PCC_NO_DIV}, +}; + +int pcc_clock_enable(enum pcc_clk clk, bool enable) +{ + u32 reg, val; + + if (clk >= ARRAY_SIZE(pcc_arrays)) + return -EINVAL; + + reg = pcc_arrays[clk].pcc_base + pcc_arrays[clk].pcc_slot * 4; + + val = readl(reg); + + clk_debug("pcc_clock_enable: clk %d, reg 0x%x, val 0x%x, enable %d\n", + clk, reg, val, enable); + + if (!(val & PCC_PR_MASK) || (val & PCC_INUSE_MASK)) + return -EPERM; + + if (enable) + val |= PCC_CGC_MASK; + else + val &= ~PCC_CGC_MASK; + + writel(val, reg); + + clk_debug("pcc_clock_enable: val 0x%x\n", val); + + return 0; +} + +/* The clock source select needs clock is disabled */ +int pcc_clock_sel(enum pcc_clk clk, enum scg_clk src) +{ + u32 reg, val, i, clksrc_type; + + if (clk >= ARRAY_SIZE(pcc_arrays)) + return -EINVAL; + + clksrc_type = pcc_arrays[clk].clksrc; + if (clksrc_type >= CLKSRC_NO_PCS) { + printf("No PCS field for the PCC %d, clksrc type %d\n", + clk, clksrc_type); + return -EPERM; + } + + for (i = 0; i < PCC_CLKSRC_NUM; i++) { + if (pcc_clksrc[clksrc_type][i] == src) { + /* Find the clock src, then set it to PCS */ + break; + } + } + + if (i == PCC_CLKSRC_NUM) { + printf("Not find the parent scg_clk in PCS of PCC %d, invalid scg_clk %d\n", clk, src); + return -EINVAL; + } + + reg = pcc_arrays[clk].pcc_base + pcc_arrays[clk].pcc_slot * 4; + + val = readl(reg); + + clk_debug("pcc_clock_sel: clk %d, reg 0x%x, val 0x%x, clksrc_type %d\n", + clk, reg, val, clksrc_type); + + if (!(val & PCC_PR_MASK) || (val & PCC_INUSE_MASK) || + (val & PCC_CGC_MASK)) { + printf("Not permit to select clock source val = 0x%x\n", val); + return -EPERM; + } + + val &= ~PCC_PCS_MASK; + val |= ((i + 1) << PCC_PCS_OFFSET); + + writel(val, reg); + + clk_debug("pcc_clock_sel: val 0x%x\n", val); + + return 0; +} + +int pcc_clock_div_config(enum pcc_clk clk, bool frac, u8 div) +{ + u32 reg, val; + + if (clk >= ARRAY_SIZE(pcc_arrays) || div > 8 || + (div == 1 && frac != 0)) + return -EINVAL; + + if (pcc_arrays[clk].div >= PCC_NO_DIV) { + printf("No DIV/FRAC field for the PCC %d\n", clk); + return -EPERM; + } + + reg = pcc_arrays[clk].pcc_base + pcc_arrays[clk].pcc_slot * 4; + + val = readl(reg); + + if (!(val & PCC_PR_MASK) || (val & PCC_INUSE_MASK) || + (val & PCC_CGC_MASK)) { + printf("Not permit to set div/frac val = 0x%x\n", val); + return -EPERM; + } + + if (frac) + val |= PCC_FRAC_MASK; + else + val &= ~PCC_FRAC_MASK; + + val &= ~PCC_PCD_MASK; + val |= (div - 1) & PCC_PCD_MASK; + + writel(val, reg); + + return 0; +} + +bool pcc_clock_is_enable(enum pcc_clk clk) +{ + u32 reg, val; + + if (clk >= ARRAY_SIZE(pcc_arrays)) + return -EINVAL; + + reg = pcc_arrays[clk].pcc_base + pcc_arrays[clk].pcc_slot * 4; + val = readl(reg); + + if ((val & PCC_INUSE_MASK) || (val & PCC_CGC_MASK)) + return true; + + return false; +} + +int pcc_clock_get_clksrc(enum pcc_clk clk, enum scg_clk *src) +{ + u32 reg, val, clksrc_type; + + if (clk >= ARRAY_SIZE(pcc_arrays)) + return -EINVAL; + + clksrc_type = pcc_arrays[clk].clksrc; + if (clksrc_type >= CLKSRC_NO_PCS) { + printf("No PCS field for the PCC %d, clksrc type %d\n", + clk, clksrc_type); + return -EPERM; + } + + reg = pcc_arrays[clk].pcc_base + pcc_arrays[clk].pcc_slot * 4; + + val = readl(reg); + + clk_debug("pcc_clock_get_clksrc: clk %d, reg 0x%x, val 0x%x, type %d\n", + clk, reg, val, clksrc_type); + + if (!(val & PCC_PR_MASK)) { + printf("This pcc slot is not present = 0x%x\n", val); + return -EPERM; + } + + val &= PCC_PCS_MASK; + val = (val >> PCC_PCS_OFFSET); + + if (!val) { + printf("Clock source is off\n"); + return -EIO; + } + + *src = pcc_clksrc[clksrc_type][val - 1]; + + clk_debug("pcc_clock_get_clksrc: parent scg clk %d\n", *src); + + return 0; +} + +u32 pcc_clock_get_rate(enum pcc_clk clk) +{ + u32 reg, val, rate, frac, div; + enum scg_clk parent; + int ret; + + ret = pcc_clock_get_clksrc(clk, &parent); + if (ret) + return 0; + + rate = scg_clk_get_rate(parent); + + clk_debug("pcc_clock_get_rate: parent rate %u\n", rate); + + if (pcc_arrays[clk].div == PCC_HAS_DIV) { + reg = pcc_arrays[clk].pcc_base + pcc_arrays[clk].pcc_slot * 4; + val = readl(reg); + + frac = (val & PCC_FRAC_MASK) >> PCC_FRAC_OFFSET; + div = (val & PCC_PCD_MASK) >> PCC_PCD_OFFSET; + + /* + * Theoretically don't have overflow in the calc, + * the rate won't exceed 2G + */ + rate = rate * (frac + 1) / (div + 1); + } + + clk_debug("pcc_clock_get_rate: rate %u\n", rate); + return rate; +} diff --git a/roms/u-boot/arch/arm/mach-imx/mx7ulp/scg.c b/roms/u-boot/arch/arm/mach-imx/mx7ulp/scg.c new file mode 100644 index 000000000..4c066557c --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/mx7ulp/scg.c @@ -0,0 +1,1083 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2016 Freescale Semiconductor, Inc. + */ + +#include <common.h> +#include <div64.h> +#include <log.h> +#include <asm/io.h> +#include <errno.h> +#include <asm/arch/imx-regs.h> +#include <asm/arch/pcc.h> +#include <asm/arch/sys_proto.h> +#include <linux/delay.h> + +scg_p scg1_regs = (scg_p)SCG1_RBASE; + +static u32 scg_src_get_rate(enum scg_clk clksrc) +{ + u32 reg; + + switch (clksrc) { + case SCG_SOSC_CLK: + reg = readl(&scg1_regs->sosccsr); + if (!(reg & SCG_SOSC_CSR_SOSCVLD_MASK)) + return 0; + + return 24000000; + case SCG_FIRC_CLK: + reg = readl(&scg1_regs->firccsr); + if (!(reg & SCG_FIRC_CSR_FIRCVLD_MASK)) + return 0; + + return 48000000; + case SCG_SIRC_CLK: + reg = readl(&scg1_regs->sirccsr); + if (!(reg & SCG_SIRC_CSR_SIRCVLD_MASK)) + return 0; + + return 16000000; + case SCG_ROSC_CLK: + reg = readl(&scg1_regs->rtccsr); + if (!(reg & SCG_ROSC_CSR_ROSCVLD_MASK)) + return 0; + + return 32768; + default: + break; + } + + return 0; +} + +static u32 scg_sircdiv_get_rate(enum scg_clk clk) +{ + u32 reg, val, rate; + u32 shift, mask; + + switch (clk) { + case SCG_SIRC_DIV1_CLK: + mask = SCG_SIRCDIV_DIV1_MASK; + shift = SCG_SIRCDIV_DIV1_SHIFT; + break; + case SCG_SIRC_DIV2_CLK: + mask = SCG_SIRCDIV_DIV2_MASK; + shift = SCG_SIRCDIV_DIV2_SHIFT; + break; + case SCG_SIRC_DIV3_CLK: + mask = SCG_SIRCDIV_DIV3_MASK; + shift = SCG_SIRCDIV_DIV3_SHIFT; + break; + default: + return 0; + } + + reg = readl(&scg1_regs->sirccsr); + if (!(reg & SCG_SIRC_CSR_SIRCVLD_MASK)) + return 0; + + reg = readl(&scg1_regs->sircdiv); + val = (reg & mask) >> shift; + + if (!val) /*clock disabled*/ + return 0; + + rate = scg_src_get_rate(SCG_SIRC_CLK); + rate = rate / (1 << (val - 1)); + + return rate; +} + +static u32 scg_fircdiv_get_rate(enum scg_clk clk) +{ + u32 reg, val, rate; + u32 shift, mask; + + switch (clk) { + case SCG_FIRC_DIV1_CLK: + mask = SCG_FIRCDIV_DIV1_MASK; + shift = SCG_FIRCDIV_DIV1_SHIFT; + break; + case SCG_FIRC_DIV2_CLK: + mask = SCG_FIRCDIV_DIV2_MASK; + shift = SCG_FIRCDIV_DIV2_SHIFT; + break; + case SCG_FIRC_DIV3_CLK: + mask = SCG_FIRCDIV_DIV3_MASK; + shift = SCG_FIRCDIV_DIV3_SHIFT; + break; + default: + return 0; + } + + reg = readl(&scg1_regs->firccsr); + if (!(reg & SCG_FIRC_CSR_FIRCVLD_MASK)) + return 0; + + reg = readl(&scg1_regs->fircdiv); + val = (reg & mask) >> shift; + + if (!val) /*clock disabled*/ + return 0; + + rate = scg_src_get_rate(SCG_FIRC_CLK); + rate = rate / (1 << (val - 1)); + + return rate; +} + +static u32 scg_soscdiv_get_rate(enum scg_clk clk) +{ + u32 reg, val, rate; + u32 shift, mask; + + switch (clk) { + case SCG_SOSC_DIV1_CLK: + mask = SCG_SOSCDIV_DIV1_MASK; + shift = SCG_SOSCDIV_DIV1_SHIFT; + break; + case SCG_SOSC_DIV2_CLK: + mask = SCG_SOSCDIV_DIV2_MASK; + shift = SCG_SOSCDIV_DIV2_SHIFT; + break; + case SCG_SOSC_DIV3_CLK: + mask = SCG_SOSCDIV_DIV3_MASK; + shift = SCG_SOSCDIV_DIV3_SHIFT; + break; + default: + return 0; + } + + reg = readl(&scg1_regs->sosccsr); + if (!(reg & SCG_SOSC_CSR_SOSCVLD_MASK)) + return 0; + + reg = readl(&scg1_regs->soscdiv); + val = (reg & mask) >> shift; + + if (!val) /*clock disabled*/ + return 0; + + rate = scg_src_get_rate(SCG_SOSC_CLK); + rate = rate / (1 << (val - 1)); + + return rate; +} + +static u32 scg_apll_pfd_get_rate(enum scg_clk clk) +{ + u32 reg, val, rate; + u32 shift, mask, gate, valid; + + switch (clk) { + case SCG_APLL_PFD0_CLK: + gate = SCG_PLL_PFD0_GATE_MASK; + valid = SCG_PLL_PFD0_VALID_MASK; + mask = SCG_PLL_PFD0_FRAC_MASK; + shift = SCG_PLL_PFD0_FRAC_SHIFT; + break; + case SCG_APLL_PFD1_CLK: + gate = SCG_PLL_PFD1_GATE_MASK; + valid = SCG_PLL_PFD1_VALID_MASK; + mask = SCG_PLL_PFD1_FRAC_MASK; + shift = SCG_PLL_PFD1_FRAC_SHIFT; + break; + case SCG_APLL_PFD2_CLK: + gate = SCG_PLL_PFD2_GATE_MASK; + valid = SCG_PLL_PFD2_VALID_MASK; + mask = SCG_PLL_PFD2_FRAC_MASK; + shift = SCG_PLL_PFD2_FRAC_SHIFT; + break; + case SCG_APLL_PFD3_CLK: + gate = SCG_PLL_PFD3_GATE_MASK; + valid = SCG_PLL_PFD3_VALID_MASK; + mask = SCG_PLL_PFD3_FRAC_MASK; + shift = SCG_PLL_PFD3_FRAC_SHIFT; + break; + default: + return 0; + } + + reg = readl(&scg1_regs->apllpfd); + if (reg & gate || !(reg & valid)) + return 0; + + clk_debug("scg_apll_pfd_get_rate reg 0x%x\n", reg); + + val = (reg & mask) >> shift; + rate = decode_pll(PLL_A7_APLL); + + rate = rate / val * 18; + + clk_debug("scg_apll_pfd_get_rate rate %u\n", rate); + + return rate; +} + +static u32 scg_spll_pfd_get_rate(enum scg_clk clk) +{ + u32 reg, val, rate; + u32 shift, mask, gate, valid; + + switch (clk) { + case SCG_SPLL_PFD0_CLK: + gate = SCG_PLL_PFD0_GATE_MASK; + valid = SCG_PLL_PFD0_VALID_MASK; + mask = SCG_PLL_PFD0_FRAC_MASK; + shift = SCG_PLL_PFD0_FRAC_SHIFT; + break; + case SCG_SPLL_PFD1_CLK: + gate = SCG_PLL_PFD1_GATE_MASK; + valid = SCG_PLL_PFD1_VALID_MASK; + mask = SCG_PLL_PFD1_FRAC_MASK; + shift = SCG_PLL_PFD1_FRAC_SHIFT; + break; + case SCG_SPLL_PFD2_CLK: + gate = SCG_PLL_PFD2_GATE_MASK; + valid = SCG_PLL_PFD2_VALID_MASK; + mask = SCG_PLL_PFD2_FRAC_MASK; + shift = SCG_PLL_PFD2_FRAC_SHIFT; + break; + case SCG_SPLL_PFD3_CLK: + gate = SCG_PLL_PFD3_GATE_MASK; + valid = SCG_PLL_PFD3_VALID_MASK; + mask = SCG_PLL_PFD3_FRAC_MASK; + shift = SCG_PLL_PFD3_FRAC_SHIFT; + break; + default: + return 0; + } + + reg = readl(&scg1_regs->spllpfd); + if (reg & gate || !(reg & valid)) + return 0; + + clk_debug("scg_spll_pfd_get_rate reg 0x%x\n", reg); + + val = (reg & mask) >> shift; + rate = decode_pll(PLL_A7_SPLL); + + rate = rate / val * 18; + + clk_debug("scg_spll_pfd_get_rate rate %u\n", rate); + + return rate; +} + +static u32 scg_apll_get_rate(void) +{ + u32 reg, val, rate; + + reg = readl(&scg1_regs->apllcfg); + val = (reg & SCG_PLL_CFG_PLLSEL_MASK) >> SCG_PLL_CFG_PLLSEL_SHIFT; + + if (!val) { + /* APLL clock after two dividers */ + rate = decode_pll(PLL_A7_APLL); + + val = (reg & SCG_PLL_CFG_POSTDIV1_MASK) >> + SCG_PLL_CFG_POSTDIV1_SHIFT; + rate = rate / (val + 1); + + val = (reg & SCG_PLL_CFG_POSTDIV2_MASK) >> + SCG_PLL_CFG_POSTDIV2_SHIFT; + rate = rate / (val + 1); + } else { + /* APLL PFD clock */ + val = (reg & SCG_PLL_CFG_PFDSEL_MASK) >> + SCG_PLL_CFG_PFDSEL_SHIFT; + rate = scg_apll_pfd_get_rate(SCG_APLL_PFD0_CLK + val); + } + + return rate; +} + +static u32 scg_spll_get_rate(void) +{ + u32 reg, val, rate; + + reg = readl(&scg1_regs->spllcfg); + val = (reg & SCG_PLL_CFG_PLLSEL_MASK) >> SCG_PLL_CFG_PLLSEL_SHIFT; + + clk_debug("scg_spll_get_rate reg 0x%x\n", reg); + + if (!val) { + /* APLL clock after two dividers */ + rate = decode_pll(PLL_A7_SPLL); + + val = (reg & SCG_PLL_CFG_POSTDIV1_MASK) >> + SCG_PLL_CFG_POSTDIV1_SHIFT; + rate = rate / (val + 1); + + val = (reg & SCG_PLL_CFG_POSTDIV2_MASK) >> + SCG_PLL_CFG_POSTDIV2_SHIFT; + rate = rate / (val + 1); + + clk_debug("scg_spll_get_rate SPLL %u\n", rate); + + } else { + /* APLL PFD clock */ + val = (reg & SCG_PLL_CFG_PFDSEL_MASK) >> + SCG_PLL_CFG_PFDSEL_SHIFT; + rate = scg_spll_pfd_get_rate(SCG_SPLL_PFD0_CLK + val); + + clk_debug("scg_spll_get_rate PFD %u\n", rate); + } + + return rate; +} + +static u32 scg_ddr_get_rate(void) +{ + u32 reg, val, rate, div; + + reg = readl(&scg1_regs->ddrccr); + val = (reg & SCG_DDRCCR_DDRCS_MASK) >> SCG_DDRCCR_DDRCS_SHIFT; + div = (reg & SCG_DDRCCR_DDRDIV_MASK) >> SCG_DDRCCR_DDRDIV_SHIFT; + + if (!div) + return 0; + + if (!val) { + reg = readl(&scg1_regs->apllcfg); + val = (reg & SCG_PLL_CFG_PFDSEL_MASK) >> + SCG_PLL_CFG_PFDSEL_SHIFT; + rate = scg_apll_pfd_get_rate(SCG_APLL_PFD0_CLK + val); + } else { + rate = decode_pll(PLL_USB); + } + + rate = rate / (1 << (div - 1)); + return rate; +} + +static u32 scg_nic_get_rate(enum scg_clk clk) +{ + u32 reg, val, rate, nic0_rate; + u32 shift, mask; + + reg = readl(&scg1_regs->niccsr); + val = (reg & SCG_NICCSR_NICCS_MASK) >> SCG_NICCSR_NICCS_SHIFT; + + clk_debug("scg_nic_get_rate niccsr 0x%x\n", reg); + + if (!val) + rate = scg_src_get_rate(SCG_FIRC_CLK); + else + rate = scg_ddr_get_rate(); + + clk_debug("scg_nic_get_rate parent rate %u\n", rate); + + val = (reg & SCG_NICCSR_NIC0DIV_MASK) >> SCG_NICCSR_NIC0DIV_SHIFT; + + rate = rate / (val + 1); + nic0_rate = rate; + + clk_debug("scg_nic_get_rate NIC0 rate %u\n", rate); + + switch (clk) { + case SCG_NIC0_CLK: + return rate; + case SCG_GPU_CLK: + mask = SCG_NICCSR_GPUDIV_MASK; + shift = SCG_NICCSR_GPUDIV_SHIFT; + break; + case SCG_NIC1_EXT_CLK: + case SCG_NIC1_BUS_CLK: + case SCG_NIC1_CLK: + mask = SCG_NICCSR_NIC1DIV_MASK; + shift = SCG_NICCSR_NIC1DIV_SHIFT; + break; + default: + return 0; + } + + val = (reg & mask) >> shift; + rate = rate / (val + 1); + + clk_debug("scg_nic_get_rate NIC1 rate %u\n", rate); + + switch (clk) { + case SCG_GPU_CLK: + case SCG_NIC1_CLK: + return rate; + case SCG_NIC1_EXT_CLK: + mask = SCG_NICCSR_NIC1EXTDIV_MASK; + shift = SCG_NICCSR_NIC1EXTDIV_SHIFT; + break; + case SCG_NIC1_BUS_CLK: + mask = SCG_NICCSR_NIC1BUSDIV_MASK; + shift = SCG_NICCSR_NIC1BUSDIV_SHIFT; + break; + default: + return 0; + } + + /* + * On RevB, the nic_bus and nic_ext dividers are parallel + * not chained with nic div + */ + if (soc_rev() >= CHIP_REV_2_0) + rate = nic0_rate; + + val = (reg & mask) >> shift; + rate = rate / (val + 1); + + clk_debug("scg_nic_get_rate NIC1 bus rate %u\n", rate); + return rate; +} + + +static enum scg_clk scg_scs_array[4] = { + SCG_SOSC_CLK, SCG_SIRC_CLK, SCG_FIRC_CLK, SCG_ROSC_CLK, +}; + +static u32 scg_sys_get_rate(enum scg_clk clk) +{ + u32 reg, val, rate; + + if (clk != SCG_CORE_CLK && clk != SCG_BUS_CLK) + return 0; + + reg = readl(&scg1_regs->csr); + val = (reg & SCG_CCR_SCS_MASK) >> SCG_CCR_SCS_SHIFT; + + clk_debug("scg_sys_get_rate reg 0x%x\n", reg); + + switch (val) { + case SCG_SCS_SYS_OSC: + case SCG_SCS_SLOW_IRC: + case SCG_SCS_FAST_IRC: + case SCG_SCS_RTC_OSC: + rate = scg_src_get_rate(scg_scs_array[val - 1]); + break; + case 5: + rate = scg_apll_get_rate(); + break; + case 6: + rate = scg_spll_get_rate(); + break; + default: + return 0; + } + + clk_debug("scg_sys_get_rate parent rate %u\n", rate); + + val = (reg & SCG_CCR_DIVCORE_MASK) >> SCG_CCR_DIVCORE_SHIFT; + + rate = rate / (val + 1); + + if (clk == SCG_BUS_CLK) { + val = (reg & SCG_CCR_DIVBUS_MASK) >> SCG_CCR_DIVBUS_SHIFT; + rate = rate / (val + 1); + } + + return rate; +} + +u32 decode_pll(enum pll_clocks pll) +{ + u32 reg, pre_div, infreq, mult; + u32 num, denom; + + /* + * Alought there are four choices for the bypass src, + * we choose OSC_24M which is the default set in ROM. + */ + switch (pll) { + case PLL_A7_SPLL: + reg = readl(&scg1_regs->spllcsr); + + if (!(reg & SCG_SPLL_CSR_SPLLVLD_MASK)) + return 0; + + reg = readl(&scg1_regs->spllcfg); + + pre_div = (reg & SCG_PLL_CFG_PREDIV_MASK) >> + SCG_PLL_CFG_PREDIV_SHIFT; + pre_div += 1; + + mult = (reg & SCG1_SPLL_CFG_MULT_MASK) >> + SCG_PLL_CFG_MULT_SHIFT; + + infreq = (reg & SCG_PLL_CFG_CLKSRC_MASK) >> + SCG_PLL_CFG_CLKSRC_SHIFT; + if (!infreq) + infreq = scg_src_get_rate(SCG_SOSC_CLK); + else + infreq = scg_src_get_rate(SCG_FIRC_CLK); + + num = readl(&scg1_regs->spllnum); + denom = readl(&scg1_regs->splldenom); + + infreq = infreq / pre_div; + + if (denom) + return infreq * mult + infreq * num / denom; + else + return infreq * mult; + + case PLL_A7_APLL: + reg = readl(&scg1_regs->apllcsr); + + if (!(reg & SCG_APLL_CSR_APLLVLD_MASK)) + return 0; + + reg = readl(&scg1_regs->apllcfg); + + pre_div = (reg & SCG_PLL_CFG_PREDIV_MASK) >> + SCG_PLL_CFG_PREDIV_SHIFT; + pre_div += 1; + + mult = (reg & SCG_APLL_CFG_MULT_MASK) >> + SCG_PLL_CFG_MULT_SHIFT; + + infreq = (reg & SCG_PLL_CFG_CLKSRC_MASK) >> + SCG_PLL_CFG_CLKSRC_SHIFT; + if (!infreq) + infreq = scg_src_get_rate(SCG_SOSC_CLK); + else + infreq = scg_src_get_rate(SCG_FIRC_CLK); + + num = readl(&scg1_regs->apllnum); + denom = readl(&scg1_regs->aplldenom); + + infreq = infreq / pre_div; + + if (denom) + return infreq * mult + infreq * num / denom; + else + return infreq * mult; + + case PLL_USB: + reg = readl(&scg1_regs->upllcsr); + + if (!(reg & SCG_UPLL_CSR_UPLLVLD_MASK)) + return 0; + + return 480000000u; + + case PLL_MIPI: + return 480000000u; + default: + printf("Unsupported pll clocks %d\n", pll); + break; + } + + return 0; +} + +u32 scg_clk_get_rate(enum scg_clk clk) +{ + switch (clk) { + case SCG_SIRC_DIV1_CLK: + case SCG_SIRC_DIV2_CLK: + case SCG_SIRC_DIV3_CLK: + return scg_sircdiv_get_rate(clk); + + case SCG_FIRC_DIV1_CLK: + case SCG_FIRC_DIV2_CLK: + case SCG_FIRC_DIV3_CLK: + return scg_fircdiv_get_rate(clk); + + case SCG_SOSC_DIV1_CLK: + case SCG_SOSC_DIV2_CLK: + case SCG_SOSC_DIV3_CLK: + return scg_soscdiv_get_rate(clk); + + case SCG_CORE_CLK: + case SCG_BUS_CLK: + return scg_sys_get_rate(clk); + + case SCG_SPLL_PFD0_CLK: + case SCG_SPLL_PFD1_CLK: + case SCG_SPLL_PFD2_CLK: + case SCG_SPLL_PFD3_CLK: + return scg_spll_pfd_get_rate(clk); + + case SCG_APLL_PFD0_CLK: + case SCG_APLL_PFD1_CLK: + case SCG_APLL_PFD2_CLK: + case SCG_APLL_PFD3_CLK: + return scg_apll_pfd_get_rate(clk); + + case SCG_DDR_CLK: + return scg_ddr_get_rate(); + + case SCG_NIC0_CLK: + case SCG_GPU_CLK: + case SCG_NIC1_CLK: + case SCG_NIC1_BUS_CLK: + case SCG_NIC1_EXT_CLK: + return scg_nic_get_rate(clk); + + case USB_PLL_OUT: + return decode_pll(PLL_USB); + + case MIPI_PLL_OUT: + return decode_pll(PLL_MIPI); + + case SCG_SOSC_CLK: + case SCG_FIRC_CLK: + case SCG_SIRC_CLK: + case SCG_ROSC_CLK: + return scg_src_get_rate(clk); + default: + return 0; + } +} + +int scg_enable_pll_pfd(enum scg_clk clk, u32 frac) +{ + u32 reg; + u32 shift, mask, gate, valid; + u32 addr; + + if (frac < 12 || frac > 35) + return -EINVAL; + + switch (clk) { + case SCG_SPLL_PFD0_CLK: + case SCG_APLL_PFD0_CLK: + gate = SCG_PLL_PFD0_GATE_MASK; + valid = SCG_PLL_PFD0_VALID_MASK; + mask = SCG_PLL_PFD0_FRAC_MASK; + shift = SCG_PLL_PFD0_FRAC_SHIFT; + + if (clk == SCG_SPLL_PFD0_CLK) + addr = (u32)(&scg1_regs->spllpfd); + else + addr = (u32)(&scg1_regs->apllpfd); + break; + case SCG_SPLL_PFD1_CLK: + case SCG_APLL_PFD1_CLK: + gate = SCG_PLL_PFD1_GATE_MASK; + valid = SCG_PLL_PFD1_VALID_MASK; + mask = SCG_PLL_PFD1_FRAC_MASK; + shift = SCG_PLL_PFD1_FRAC_SHIFT; + + if (clk == SCG_SPLL_PFD1_CLK) + addr = (u32)(&scg1_regs->spllpfd); + else + addr = (u32)(&scg1_regs->apllpfd); + break; + case SCG_SPLL_PFD2_CLK: + case SCG_APLL_PFD2_CLK: + gate = SCG_PLL_PFD2_GATE_MASK; + valid = SCG_PLL_PFD2_VALID_MASK; + mask = SCG_PLL_PFD2_FRAC_MASK; + shift = SCG_PLL_PFD2_FRAC_SHIFT; + + if (clk == SCG_SPLL_PFD2_CLK) + addr = (u32)(&scg1_regs->spllpfd); + else + addr = (u32)(&scg1_regs->apllpfd); + break; + case SCG_SPLL_PFD3_CLK: + case SCG_APLL_PFD3_CLK: + gate = SCG_PLL_PFD3_GATE_MASK; + valid = SCG_PLL_PFD3_VALID_MASK; + mask = SCG_PLL_PFD3_FRAC_MASK; + shift = SCG_PLL_PFD3_FRAC_SHIFT; + + if (clk == SCG_SPLL_PFD3_CLK) + addr = (u32)(&scg1_regs->spllpfd); + else + addr = (u32)(&scg1_regs->apllpfd); + break; + default: + return -EINVAL; + } + + /* Gate the PFD */ + reg = readl(addr); + reg |= gate; + writel(reg, addr); + + /* Write Frac divider */ + reg &= ~mask; + reg |= (frac << shift) & mask; + writel(reg, addr); + + /* + * Un-gate the PFD + * (Need un-gate before checking valid, not align with RM) + */ + reg &= ~gate; + writel(reg, addr); + + /* Wait for PFD clock being valid */ + do { + reg = readl(addr); + } while (!(reg & valid)); + + return 0; +} + +#define SIM_MISC_CTRL0_USB_PLL_EN_MASK (0x1 << 2) +int scg_enable_usb_pll(bool usb_control) +{ + u32 sosc_rate; + s32 timeout = 1000000; + u32 reg; + + struct usbphy_regs *usbphy = + (struct usbphy_regs *)USBPHY_RBASE; + + sosc_rate = scg_src_get_rate(SCG_SOSC_CLK); + if (!sosc_rate) + return -EPERM; + + reg = readl(SIM0_RBASE + 0x3C); + if (usb_control) + reg &= ~SIM_MISC_CTRL0_USB_PLL_EN_MASK; + else + reg |= SIM_MISC_CTRL0_USB_PLL_EN_MASK; + writel(reg, SIM0_RBASE + 0x3C); + + if (!(readl(&usbphy->usb1_pll_480_ctrl) & PLL_USB_LOCK_MASK)) { + writel(0x1c00000, &usbphy->usb1_pll_480_ctrl_clr); + + switch (sosc_rate) { + case 24000000: + writel(0xc00000, &usbphy->usb1_pll_480_ctrl_set); + break; + + case 30000000: + writel(0x800000, &usbphy->usb1_pll_480_ctrl_set); + break; + + case 19200000: + writel(0x1400000, &usbphy->usb1_pll_480_ctrl_set); + break; + + default: + writel(0xc00000, &usbphy->usb1_pll_480_ctrl_set); + break; + } + + /* Enable the regulator first */ + writel(PLL_USB_REG_ENABLE_MASK, + &usbphy->usb1_pll_480_ctrl_set); + + /* Wait at least 15us */ + udelay(15); + + /* Enable the power */ + writel(PLL_USB_PWR_MASK, &usbphy->usb1_pll_480_ctrl_set); + + /* Wait lock */ + while (timeout--) { + if (readl(&usbphy->usb1_pll_480_ctrl) & + PLL_USB_LOCK_MASK) + break; + } + + if (timeout <= 0) { + /* If timeout, we power down the pll */ + writel(PLL_USB_PWR_MASK, + &usbphy->usb1_pll_480_ctrl_clr); + return -ETIME; + } + } + + /* Clear the bypass */ + writel(PLL_USB_BYPASS_MASK, &usbphy->usb1_pll_480_ctrl_clr); + + /* Enable the PLL clock out to USB */ + writel((PLL_USB_EN_USB_CLKS_MASK | PLL_USB_ENABLE_MASK), + &usbphy->usb1_pll_480_ctrl_set); + + if (!usb_control) { + while (timeout--) { + if (readl(&scg1_regs->upllcsr) & + SCG_UPLL_CSR_UPLLVLD_MASK) + break; + } + + if (timeout <= 0) { + reg = readl(SIM0_RBASE + 0x3C); + reg &= ~SIM_MISC_CTRL0_USB_PLL_EN_MASK; + writel(reg, SIM0_RBASE + 0x3C); + return -ETIME; + } + } + + return 0; +} + + +/* A7 domain system clock source is SPLL */ +#define SCG1_RCCR_SCS_NUM ((SCG_SCS_SYS_PLL) << SCG_CCR_SCS_SHIFT) + +/* A7 Core clck = SPLL PFD0 / 1 = 500MHz / 1 = 500MHz */ +#define SCG1_RCCR_DIVCORE_NUM ((0x0) << SCG_CCR_DIVCORE_SHIFT) +#define SCG1_RCCR_CFG_MASK (SCG_CCR_SCS_MASK | SCG_CCR_DIVBUS_MASK) + +/* A7 Plat clck = A7 Core Clock / 2 = 250MHz / 1 = 250MHz */ +#define SCG1_RCCR_DIVBUS_NUM ((0x1) << SCG_CCR_DIVBUS_SHIFT) +#define SCG1_RCCR_CFG_NUM (SCG1_RCCR_SCS_NUM | SCG1_RCCR_DIVBUS_NUM) + +void scg_a7_rccr_init(void) +{ + u32 rccr_reg_val = 0; + + rccr_reg_val = readl(&scg1_regs->rccr); + + rccr_reg_val &= (~SCG1_RCCR_CFG_MASK); + rccr_reg_val |= (SCG1_RCCR_CFG_NUM); + + writel(rccr_reg_val, &scg1_regs->rccr); +} + +/* POSTDIV2 = 1 */ +#define SCG1_SPLL_CFG_POSTDIV2_NUM ((0x0) << SCG_PLL_CFG_POSTDIV2_SHIFT) +/* POSTDIV1 = 1 */ +#define SCG1_SPLL_CFG_POSTDIV1_NUM ((0x0) << SCG_PLL_CFG_POSTDIV1_SHIFT) + +/* MULT = 22 */ +#define SCG1_SPLL_CFG_MULT_NUM ((22) << SCG_PLL_CFG_MULT_SHIFT) + +/* PFD0 output clock selected */ +#define SCG1_SPLL_CFG_PFDSEL_NUM ((0) << SCG_PLL_CFG_PFDSEL_SHIFT) +/* PREDIV = 1 */ +#define SCG1_SPLL_CFG_PREDIV_NUM ((0x0) << SCG_PLL_CFG_PREDIV_SHIFT) +/* SPLL output clocks (including PFD outputs) selected */ +#define SCG1_SPLL_CFG_BYPASS_NUM ((0x0) << SCG_PLL_CFG_BYPASS_SHIFT) +/* SPLL PFD output clock selected */ +#define SCG1_SPLL_CFG_PLLSEL_NUM ((0x1) << SCG_PLL_CFG_PLLSEL_SHIFT) +/* Clock source is System OSC */ +#define SCG1_SPLL_CFG_CLKSRC_NUM ((0x0) << SCG_PLL_CFG_CLKSRC_SHIFT) +#define SCG1_SPLL_CFG_NUM_24M_OSC (SCG1_SPLL_CFG_POSTDIV2_NUM | \ + SCG1_SPLL_CFG_POSTDIV1_NUM | \ + (22 << SCG_PLL_CFG_MULT_SHIFT) | \ + SCG1_SPLL_CFG_PFDSEL_NUM | \ + SCG1_SPLL_CFG_PREDIV_NUM | \ + SCG1_SPLL_CFG_BYPASS_NUM | \ + SCG1_SPLL_CFG_PLLSEL_NUM | \ + SCG1_SPLL_CFG_CLKSRC_NUM) +/*413Mhz = A7 SPLL(528MHz) * 18/23 */ +#define SCG1_SPLL_PFD0_FRAC_NUM ((23) << SCG_PLL_PFD0_FRAC_SHIFT) + +void scg_a7_spll_init(void) +{ + u32 val = 0; + + /* Disable A7 System PLL */ + val = readl(&scg1_regs->spllcsr); + val &= ~SCG_SPLL_CSR_SPLLEN_MASK; + writel(val, &scg1_regs->spllcsr); + + /* + * Per block guide, + * "When changing PFD values, it is recommneded PFDx clock + * gets gated first by writing a value of 1 to PFDx_CLKGATE register, + * then program the new PFD value, then poll the PFDx_VALID + * flag to set before writing a value of 0 to PFDx_CLKGATE + * to ungate the PFDx clock and allow PFDx clock to run" + */ + + /* Gate off A7 SPLL PFD0 ~ PDF4 */ + val = readl(&scg1_regs->spllpfd); + val |= (SCG_PLL_PFD3_GATE_MASK | + SCG_PLL_PFD2_GATE_MASK | + SCG_PLL_PFD1_GATE_MASK | + SCG_PLL_PFD0_GATE_MASK); + writel(val, &scg1_regs->spllpfd); + + /* ================ A7 SPLL Configuration Start ============== */ + + /* Configure A7 System PLL */ + writel(SCG1_SPLL_CFG_NUM_24M_OSC, &scg1_regs->spllcfg); + + /* Enable A7 System PLL */ + val = readl(&scg1_regs->spllcsr); + val |= SCG_SPLL_CSR_SPLLEN_MASK; + writel(val, &scg1_regs->spllcsr); + + /* Wait for A7 SPLL clock ready */ + while (!(readl(&scg1_regs->spllcsr) & SCG_SPLL_CSR_SPLLVLD_MASK)) + ; + + /* Configure A7 SPLL PFD0 */ + val = readl(&scg1_regs->spllpfd); + val &= ~SCG_PLL_PFD0_FRAC_MASK; + val |= SCG1_SPLL_PFD0_FRAC_NUM; + writel(val, &scg1_regs->spllpfd); + + /* Un-gate A7 SPLL PFD0 */ + val = readl(&scg1_regs->spllpfd); + val &= ~SCG_PLL_PFD0_GATE_MASK; + writel(val, &scg1_regs->spllpfd); + + /* Wait for A7 SPLL PFD0 clock being valid */ + while (!(readl(&scg1_regs->spllpfd) & SCG_PLL_PFD0_VALID_MASK)) + ; + + /* ================ A7 SPLL Configuration End ============== */ +} + +/* DDR clock source is APLL PFD0 (396MHz) */ +#define SCG1_DDRCCR_DDRCS_NUM ((0x0) << SCG_DDRCCR_DDRCS_SHIFT) +/* DDR clock = APLL PFD0 / 1 = 396MHz / 1 = 396MHz */ +#define SCG1_DDRCCR_DDRDIV_NUM ((0x1) << SCG_DDRCCR_DDRDIV_SHIFT) +/* DDR clock = APLL PFD0 / 2 = 396MHz / 2 = 198MHz */ +#define SCG1_DDRCCR_DDRDIV_LF_NUM ((0x2) << SCG_DDRCCR_DDRDIV_SHIFT) +#define SCG1_DDRCCR_CFG_NUM (SCG1_DDRCCR_DDRCS_NUM | \ + SCG1_DDRCCR_DDRDIV_NUM) +#define SCG1_DDRCCR_CFG_LF_NUM (SCG1_DDRCCR_DDRCS_NUM | \ + SCG1_DDRCCR_DDRDIV_LF_NUM) +void scg_a7_ddrclk_init(void) +{ + writel(SCG1_DDRCCR_CFG_NUM, &scg1_regs->ddrccr); +} + +/* SCG1(A7) APLLCFG configurations */ +/* divide by 1 <<28 */ +#define SCG1_APLL_CFG_POSTDIV2_NUM ((0x0) << SCG_PLL_CFG_POSTDIV2_SHIFT) +/* divide by 1 <<24 */ +#define SCG1_APLL_CFG_POSTDIV1_NUM ((0x0) << SCG_PLL_CFG_POSTDIV1_SHIFT) +/* MULT is 22 <<16 */ +#define SCG1_APLL_CFG_MULT_NUM ((22) << SCG_PLL_CFG_MULT_SHIFT) +/* PFD0 output clock selected <<14 */ +#define SCG1_APLL_CFG_PFDSEL_NUM ((0) << SCG_PLL_CFG_PFDSEL_SHIFT) +/* PREDIV = 1 <<8 */ +#define SCG1_APLL_CFG_PREDIV_NUM ((0x0) << SCG_PLL_CFG_PREDIV_SHIFT) +/* APLL output clocks (including PFD outputs) selected <<2 */ +#define SCG1_APLL_CFG_BYPASS_NUM ((0x0) << SCG_PLL_CFG_BYPASS_SHIFT) +/* APLL PFD output clock selected <<1 */ +#define SCG1_APLL_CFG_PLLSEL_NUM ((0x0) << SCG_PLL_CFG_PLLSEL_SHIFT) +/* Clock source is System OSC <<0 */ +#define SCG1_APLL_CFG_CLKSRC_NUM ((0x0) << SCG_PLL_CFG_CLKSRC_SHIFT) + +/* SCG1(A7) FIRC DIV configurations */ +/* Disable FIRC DIV3 */ +#define SCG1_FIRCDIV_DIV3_NUM ((0x0) << SCG_FIRCDIV_DIV3_SHIFT) +/* FIRC DIV2 = 48MHz / 1 = 48MHz */ +#define SCG1_FIRCDIV_DIV2_NUM ((0x1) << SCG_FIRCDIV_DIV2_SHIFT) +/* Disable FIRC DIV1 */ +#define SCG1_FIRCDIV_DIV1_NUM ((0x0) << SCG_FIRCDIV_DIV1_SHIFT) + +void scg_a7_firc_init(void) +{ + /* Wait for FIRC clock ready */ + while (!(readl(&scg1_regs->firccsr) & SCG_FIRC_CSR_FIRCVLD_MASK)) + ; + + /* Configure A7 FIRC DIV1 ~ DIV3 */ + writel((SCG1_FIRCDIV_DIV3_NUM | + SCG1_FIRCDIV_DIV2_NUM | + SCG1_FIRCDIV_DIV1_NUM), &scg1_regs->fircdiv); +} + +/* SCG1(A7) NICCCR configurations */ +/* NIC clock source is DDR clock (396/198MHz) */ +#define SCG1_NICCCR_NICCS_NUM ((0x1) << SCG_NICCCR_NICCS_SHIFT) + +/* NIC0 clock = DDR Clock / 2 = 396MHz / 2 = 198MHz */ +#define SCG1_NICCCR_NIC0_DIV_NUM ((0x1) << SCG_NICCCR_NIC0_DIV_SHIFT) +/* NIC0 clock = DDR Clock / 1 = 198MHz / 1 = 198MHz */ +#define SCG1_NICCCR_NIC0_DIV_LF_NUM ((0x0) << SCG_NICCCR_NIC0_DIV_SHIFT) +/* NIC1 clock = NIC0 Clock / 1 = 198MHz / 2 = 198MHz */ +#define SCG1_NICCCR_NIC1_DIV_NUM ((0x0) << SCG_NICCCR_NIC1_DIV_SHIFT) +/* NIC1 bus clock = NIC1 Clock / 3 = 198MHz / 3 = 66MHz */ +#define SCG1_NICCCR_NIC1_DIVBUS_NUM ((0x2) << SCG_NICCCR_NIC1_DIVBUS_SHIFT) +#define SCG1_NICCCR_CFG_NUM (SCG1_NICCCR_NICCS_NUM | \ + SCG1_NICCCR_NIC0_DIV_NUM | \ + SCG1_NICCCR_NIC1_DIV_NUM | \ + SCG1_NICCCR_NIC1_DIVBUS_NUM) + +void scg_a7_nicclk_init(void) +{ + writel(SCG1_NICCCR_CFG_NUM, &scg1_regs->nicccr); +} + +/* SCG1(A7) FIRC DIV configurations */ +/* Enable FIRC DIV3 */ +#define SCG1_SOSCDIV_DIV3_NUM ((0x1) << SCG_SOSCDIV_DIV3_SHIFT) +/* FIRC DIV2 = 48MHz / 1 = 48MHz */ +#define SCG1_SOSCDIV_DIV2_NUM ((0x1) << SCG_SOSCDIV_DIV2_SHIFT) +/* Enable FIRC DIV1 */ +#define SCG1_SOSCDIV_DIV1_NUM ((0x1) << SCG_SOSCDIV_DIV1_SHIFT) + +void scg_a7_soscdiv_init(void) +{ + /* Wait for FIRC clock ready */ + while (!(readl(&scg1_regs->sosccsr) & SCG_SOSC_CSR_SOSCVLD_MASK)) + ; + + /* Configure A7 FIRC DIV1 ~ DIV3 */ + writel((SCG1_SOSCDIV_DIV3_NUM | SCG1_SOSCDIV_DIV2_NUM | + SCG1_SOSCDIV_DIV1_NUM), &scg1_regs->soscdiv); +} + +void scg_a7_sys_clk_sel(enum scg_sys_src clk) +{ + u32 rccr_reg_val = 0; + + clk_debug("%s: system clock selected as %s\n", "[SCG]", + clk == SCG_SCS_SYS_OSC ? "SYS_OSC" : + clk == SCG_SCS_SLOW_IRC ? "SLOW_IRC" : + clk == SCG_SCS_FAST_IRC ? "FAST_IRC" : + clk == SCG_SCS_RTC_OSC ? "RTC_OSC" : + clk == SCG_SCS_AUX_PLL ? "AUX_PLL" : + clk == SCG_SCS_SYS_PLL ? "SYS_PLL" : + clk == SCG_SCS_USBPHY_PLL ? "USBPHY_PLL" : + "Invalid source" + ); + + rccr_reg_val = readl(&scg1_regs->rccr); + rccr_reg_val &= ~SCG_CCR_SCS_MASK; + rccr_reg_val |= (clk << SCG_CCR_SCS_SHIFT); + writel(rccr_reg_val, &scg1_regs->rccr); +} + +void scg_a7_info(void) +{ + debug("SCG Version: 0x%x\n", readl(&scg1_regs->verid)); + debug("SCG Parameter: 0x%x\n", readl(&scg1_regs->param)); + debug("SCG RCCR Value: 0x%x\n", readl(&scg1_regs->rccr)); + debug("SCG Clock Status: 0x%x\n", readl(&scg1_regs->csr)); +} + +void scg_a7_init_core_clk(void) +{ + u32 val = 0; + + /* + * The normal target frequency for ULP B0 is 500Mhz, + * but ROM set it to 413Mhz, need to change SPLL PFD0 FRAC + */ + if (soc_rev() >= CHIP_REV_2_0) { + /* Switch RCCR SCG to SOSC, firstly check the SOSC is valid */ + if ((readl(&scg1_regs->sosccsr) & SCG_SOSC_CSR_SOSCVLD_MASK)) { + val = readl(&scg1_regs->rccr); + val &= (~SCG_CCR_SCS_MASK); + val |= ((SCG_SCS_SYS_OSC) << SCG_CCR_SCS_SHIFT); + writel(val, &scg1_regs->rccr); + + /* Switch the PLLS to SPLL clk */ + val = readl(&scg1_regs->spllcfg); + val &= ~SCG_PLL_CFG_PLLSEL_MASK; + writel(val, &scg1_regs->spllcfg); + + /* + * Re-configure PFD0 to 19, + * A7 SPLL(528MHz) * 18 / 19 = 500MHz + */ + scg_enable_pll_pfd(SCG_SPLL_PFD0_CLK, 19); + + /* Switch the PLLS to SPLL PFD0 */ + val = readl(&scg1_regs->spllcfg); + val |= SCG_PLL_CFG_PLLSEL_MASK; + writel(val, &scg1_regs->spllcfg); + + /* Set RCCR SCG to SPLL clk out */ + val = readl(&scg1_regs->rccr); + val &= (~SCG_CCR_SCS_MASK); + val |= ((SCG_SCS_SYS_PLL) << SCG_CCR_SCS_SHIFT); + writel(val, &scg1_regs->rccr); + } + } +} diff --git a/roms/u-boot/arch/arm/mach-imx/mx7ulp/soc.c b/roms/u-boot/arch/arm/mach-imx/mx7ulp/soc.c new file mode 100644 index 000000000..320f24dd2 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/mx7ulp/soc.c @@ -0,0 +1,365 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2016 Freescale Semiconductor, Inc. + */ + +#include <common.h> +#include <cpu_func.h> +#include <init.h> +#include <log.h> +#include <asm/io.h> +#include <asm/arch/clock.h> +#include <asm/arch/imx-regs.h> +#include <asm/arch/sys_proto.h> +#include <asm/mach-imx/boot_mode.h> +#include <asm/mach-imx/hab.h> +#include <linux/bitops.h> + +#define PMC0_BASE_ADDR 0x410a1000 +#define PMC0_CTRL 0x28 +#define PMC0_CTRL_LDOEN BIT(31) +#define PMC0_CTRL_LDOOKDIS BIT(30) +#define PMC0_CTRL_PMC1ON BIT(24) +#define PMC1_BASE_ADDR 0x40400000 +#define PMC1_RUN 0x8 +#define PMC1_STOP 0x10 +#define PMC1_VLPS 0x14 +#define PMC1_LDOVL_SHIFT 16 +#define PMC1_LDOVL_MASK (0x3f << PMC1_LDOVL_SHIFT) +#define PMC1_LDOVL_900 0x1e +#define PMC1_LDOVL_950 0x23 +#define PMC1_STATUS 0x20 +#define PMC1_STATUS_LDOVLF BIT(8) + +static char *get_reset_cause(char *); + +#if defined(CONFIG_IMX_HAB) +struct imx_sec_config_fuse_t const imx_sec_config_fuse = { + .bank = 29, + .word = 6, +}; +#endif + +#define ROM_VERSION_ADDR 0x80 +u32 get_cpu_rev(void) +{ + /* Check the ROM version for cpu revision */ + u32 rom_version = readl((void __iomem *)ROM_VERSION_ADDR); + + return (MXC_CPU_MX7ULP << 12) | (rom_version & 0xFF); +} + +#ifdef CONFIG_REVISION_TAG +u32 __weak get_board_rev(void) +{ + return get_cpu_rev(); +} +#endif + +enum bt_mode get_boot_mode(void) +{ + u32 bt0_cfg = 0; + + bt0_cfg = readl(CMC0_RBASE + 0x40); + bt0_cfg &= (BT0CFG_LPBOOT_MASK | BT0CFG_DUALBOOT_MASK); + + if (!(bt0_cfg & BT0CFG_LPBOOT_MASK)) { + /* No low power boot */ + if (bt0_cfg & BT0CFG_DUALBOOT_MASK) + return DUAL_BOOT; + else + return SINGLE_BOOT; + } + + return LOW_POWER_BOOT; +} + +int arch_cpu_init(void) +{ + return 0; +} + +#ifdef CONFIG_BOARD_POSTCLK_INIT +int board_postclk_init(void) +{ + return 0; +} +#endif + +#define UNLOCK_WORD0 0xC520 /* 1st unlock word */ +#define UNLOCK_WORD1 0xD928 /* 2nd unlock word */ +#define REFRESH_WORD0 0xA602 /* 1st refresh word */ +#define REFRESH_WORD1 0xB480 /* 2nd refresh word */ + +static void disable_wdog(u32 wdog_base) +{ + writel(UNLOCK_WORD0, (wdog_base + 0x04)); + writel(UNLOCK_WORD1, (wdog_base + 0x04)); + writel(0x0, (wdog_base + 0x0C)); /* Set WIN to 0 */ + writel(0x400, (wdog_base + 0x08)); /* Set timeout to default 0x400 */ + writel(0x120, (wdog_base + 0x00)); /* Disable it and set update */ + + writel(REFRESH_WORD0, (wdog_base + 0x04)); /* Refresh the CNT */ + writel(REFRESH_WORD1, (wdog_base + 0x04)); +} + +void init_wdog(void) +{ + /* + * ROM will configure WDOG1, disable it or enable it + * depending on FUSE. The update bit is set for reconfigurable. + * We have to use unlock sequence to reconfigure it. + * WDOG2 is not touched by ROM, so it will have default value + * which is enabled. We can directly configure it. + * To simplify the codes, we still use same reconfigure + * process as WDOG1. Because the update bit is not set for + * WDOG2, the unlock sequence won't take effect really. + * It actually directly configure the wdog. + * In this function, we will disable both WDOG1 and WDOG2, + * and set update bit for both. So that kernel can reconfigure them. + */ + disable_wdog(WDG1_RBASE); + disable_wdog(WDG2_RBASE); +} + +static bool ldo_mode_is_enabled(void) +{ + unsigned int reg; + + reg = readl(PMC0_BASE_ADDR + PMC0_CTRL); + if (reg & PMC0_CTRL_LDOEN) + return true; + else + return false; +} + +#if !defined(CONFIG_SPL) || (defined(CONFIG_SPL) && defined(CONFIG_SPL_BUILD)) +#if defined(CONFIG_LDO_ENABLED_MODE) +static void init_ldo_mode(void) +{ + unsigned int reg; + + if (ldo_mode_is_enabled()) + return; + + /* Set LDOOKDIS */ + setbits_le32(PMC0_BASE_ADDR + PMC0_CTRL, PMC0_CTRL_LDOOKDIS); + + /* Set LDOVL to 0.95V in PMC1_RUN */ + reg = readl(PMC1_BASE_ADDR + PMC1_RUN); + reg &= ~PMC1_LDOVL_MASK; + reg |= (PMC1_LDOVL_950 << PMC1_LDOVL_SHIFT); + writel(PMC1_BASE_ADDR + PMC1_RUN, reg); + + /* Wait for LDOVLF to be cleared */ + reg = readl(PMC1_BASE_ADDR + PMC1_STATUS); + while (reg & PMC1_STATUS_LDOVLF) + ; + + /* Set LDOVL to 0.95V in PMC1_STOP */ + reg = readl(PMC1_BASE_ADDR + PMC1_STOP); + reg &= ~PMC1_LDOVL_MASK; + reg |= (PMC1_LDOVL_950 << PMC1_LDOVL_SHIFT); + writel(PMC1_BASE_ADDR + PMC1_STOP, reg); + + /* Set LDOVL to 0.90V in PMC1_VLPS */ + reg = readl(PMC1_BASE_ADDR + PMC1_VLPS); + reg &= ~PMC1_LDOVL_MASK; + reg |= (PMC1_LDOVL_900 << PMC1_LDOVL_SHIFT); + writel(PMC1_BASE_ADDR + PMC1_VLPS, reg); + + /* Set LDOEN bit */ + setbits_le32(PMC0_BASE_ADDR + PMC0_CTRL, PMC0_CTRL_LDOEN); + + /* Set the PMC1ON bit */ + setbits_le32(PMC0_BASE_ADDR + PMC0_CTRL, PMC0_CTRL_PMC1ON); +} +#endif + +void s_init(void) +{ + /* Disable wdog */ + init_wdog(); + + /* clock configuration. */ + clock_init(); + + if (soc_rev() < CHIP_REV_2_0) { + /* enable dumb pmic */ + writel((readl(SNVS_LP_LPCR) | SNVS_LPCR_DPEN), SNVS_LP_LPCR); + } + +#if defined(CONFIG_LDO_ENABLED_MODE) + init_ldo_mode(); +#endif + return; +} +#endif + +#ifndef CONFIG_ULP_WATCHDOG +void reset_cpu(void) +{ + setbits_le32(SIM0_RBASE, SIM_SOPT1_A7_SW_RESET); + while (1) + ; +} +#endif + +#if defined(CONFIG_DISPLAY_CPUINFO) +const char *get_imx_type(u32 imxtype) +{ + return "7ULP"; +} + +int print_cpuinfo(void) +{ + u32 cpurev; + char cause[18]; + + cpurev = get_cpu_rev(); + + printf("CPU: Freescale i.MX%s rev%d.%d at %d MHz\n", + get_imx_type((cpurev & 0xFF000) >> 12), + (cpurev & 0x000F0) >> 4, (cpurev & 0x0000F) >> 0, + mxc_get_clock(MXC_ARM_CLK) / 1000000); + + printf("Reset cause: %s\n", get_reset_cause(cause)); + + printf("Boot mode: "); + switch (get_boot_mode()) { + case LOW_POWER_BOOT: + printf("Low power boot\n"); + break; + case DUAL_BOOT: + printf("Dual boot\n"); + break; + case SINGLE_BOOT: + default: + printf("Single boot\n"); + break; + } + + if (ldo_mode_is_enabled()) + printf("PMC1: LDO enabled mode\n"); + else + printf("PMC1: LDO bypass mode\n"); + + return 0; +} +#endif + +#define CMC_SRS_TAMPER (1 << 31) +#define CMC_SRS_SECURITY (1 << 30) +#define CMC_SRS_TZWDG (1 << 29) +#define CMC_SRS_JTAG_RST (1 << 28) +#define CMC_SRS_CORE1 (1 << 16) +#define CMC_SRS_LOCKUP (1 << 15) +#define CMC_SRS_SW (1 << 14) +#define CMC_SRS_WDG (1 << 13) +#define CMC_SRS_PIN_RESET (1 << 8) +#define CMC_SRS_WARM (1 << 4) +#define CMC_SRS_HVD (1 << 3) +#define CMC_SRS_LVD (1 << 2) +#define CMC_SRS_POR (1 << 1) +#define CMC_SRS_WUP (1 << 0) + +static u32 reset_cause = -1; + +static char *get_reset_cause(char *ret) +{ + u32 cause1, cause = 0, srs = 0; + u32 *reg_ssrs = (u32 *)(SRC_BASE_ADDR + 0x28); + u32 *reg_srs = (u32 *)(SRC_BASE_ADDR + 0x20); + + if (!ret) + return "null"; + + srs = readl(reg_srs); + cause1 = readl(reg_ssrs); + writel(cause1, reg_ssrs); + + reset_cause = cause1; + + cause = cause1 & (CMC_SRS_POR | CMC_SRS_WUP | CMC_SRS_WARM); + + switch (cause) { + case CMC_SRS_POR: + sprintf(ret, "%s", "POR"); + break; + case CMC_SRS_WUP: + sprintf(ret, "%s", "WUP"); + break; + case CMC_SRS_WARM: + cause = cause1 & (CMC_SRS_WDG | CMC_SRS_SW | + CMC_SRS_JTAG_RST); + switch (cause) { + case CMC_SRS_WDG: + sprintf(ret, "%s", "WARM-WDG"); + break; + case CMC_SRS_SW: + sprintf(ret, "%s", "WARM-SW"); + break; + case CMC_SRS_JTAG_RST: + sprintf(ret, "%s", "WARM-JTAG"); + break; + default: + sprintf(ret, "%s", "WARM-UNKN"); + break; + } + break; + default: + sprintf(ret, "%s-%X", "UNKN", cause1); + break; + } + + debug("[%X] SRS[%X] %X - ", cause1, srs, srs^cause1); + return ret; +} + +#ifdef CONFIG_ENV_IS_IN_MMC +__weak int board_mmc_get_env_dev(int devno) +{ + return CONFIG_SYS_MMC_ENV_DEV; +} + +int mmc_get_env_dev(void) +{ + int devno = 0; + u32 bt1_cfg = 0; + + /* If not boot from sd/mmc, use default value */ + if (get_boot_mode() == LOW_POWER_BOOT) + return CONFIG_SYS_MMC_ENV_DEV; + + bt1_cfg = readl(CMC1_RBASE + 0x40); + devno = (bt1_cfg >> 9) & 0x7; + + return board_mmc_get_env_dev(devno); +} +#endif + +enum boot_device get_boot_device(void) +{ + struct bootrom_sw_info **p = + (struct bootrom_sw_info **)ROM_SW_INFO_ADDR; + + enum boot_device boot_dev = SD1_BOOT; + u8 boot_type = (*p)->boot_dev_type; + u8 boot_instance = (*p)->boot_dev_instance; + + switch (boot_type) { + case BOOT_TYPE_SD: + boot_dev = boot_instance + SD1_BOOT; + break; + case BOOT_TYPE_MMC: + boot_dev = boot_instance + MMC1_BOOT; + break; + case BOOT_TYPE_USB: + boot_dev = USB_BOOT; + break; + default: + break; + } + + return boot_dev; +} diff --git a/roms/u-boot/arch/arm/mach-imx/mxs/Kconfig b/roms/u-boot/arch/arm/mach-imx/mxs/Kconfig new file mode 100644 index 000000000..9f48ffda4 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/mxs/Kconfig @@ -0,0 +1,61 @@ +if ARCH_MX23 + +config MX23 + bool + default y + +choice + prompt "MX23 board select" + optional + +config TARGET_MX23_OLINUXINO + bool "Support mx23_olinuxino" + select BOARD_EARLY_INIT_F + +config TARGET_MX23EVK + bool "Support mx23evk" + select BOARD_EARLY_INIT_F + +config TARGET_XFI3 + bool "Support xfi3" + +endchoice + +config SYS_SOC + default "mxs" + +source "board/olimex/mx23_olinuxino/Kconfig" +source "board/freescale/mx23evk/Kconfig" + +endif + +if ARCH_MX28 + +config MX28 + bool + default y + +choice + prompt "MX28 board select" + optional + +config TARGET_BG0900 + bool "Support bg0900" + +config TARGET_MX28EVK + bool "Support mx28evk" + select BOARD_EARLY_INIT_F + +config TARGET_XEA + bool "Support XEA" + +endchoice + +config SYS_SOC + default "mxs" + +source "board/freescale/mx28evk/Kconfig" +source "board/liebherr/xea/Kconfig" +source "board/ppcag/bg0900/Kconfig" + +endif diff --git a/roms/u-boot/arch/arm/mach-imx/priblob.c b/roms/u-boot/arch/arm/mach-imx/priblob.c new file mode 100644 index 000000000..e253eddfd --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/priblob.c @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2018 NXP + */ + +/* + * Boot command to get and set the PRIBLOB bitfield form the SCFGR register + * of the CAAM IP. It is recommended to set this bitfield to 3 once your + * encrypted boot image is ready, to prevent the generation of blobs usable + * to decrypt an encrypted boot image. + */ + +#include <asm/io.h> +#include <common.h> +#include <command.h> +#include "../drivers/crypto/fsl_caam_internal.h" + +int do_priblob_write(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + writel((readl(CAAM_SCFGR) & 0xFFFFFFFC) | 3, CAAM_SCFGR); + printf("New priblob setting = 0x%x\n", readl(CAAM_SCFGR) & 0x3); + + return 0; +} + +U_BOOT_CMD( + set_priblob_bitfield, 1, 0, do_priblob_write, + "Set the PRIBLOB bitfield to 3", + "<value>\n" + " - Write 3 in PRIBLOB bitfield of SCFGR regiter of CAAM IP.\n" + " Prevent the generation of blobs usable to decrypt an\n" + " encrypted boot image." +); diff --git a/roms/u-boot/arch/arm/mach-imx/rdc-sema.c b/roms/u-boot/arch/arm/mach-imx/rdc-sema.c new file mode 100644 index 000000000..e68367375 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/rdc-sema.c @@ -0,0 +1,182 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2016 Freescale Semiconductor, Inc. + */ +#include <common.h> +#include <asm/io.h> +#include <asm/arch/imx-regs.h> +#include <asm/mach-imx/rdc-sema.h> +#include <asm/arch/imx-rdc.h> +#include <linux/errno.h> + +/* + * Check if the RDC Semaphore is required for this peripheral. + */ +static inline int imx_rdc_check_sema_required(int per_id) +{ + struct rdc_regs *imx_rdc = (struct rdc_regs *)RDC_BASE_ADDR; + u32 reg; + + reg = readl(&imx_rdc->pdap[per_id]); + /* + * No semaphore: + * Intial value or this peripheral is assigned to only one domain + */ + if (!(reg & RDC_PDAP_SREQ_MASK)) + return -ENOENT; + + return 0; +} + +/* + * Check the peripheral read / write access permission on Domain [dom_id]. + */ +int imx_rdc_check_permission(int per_id, int dom_id) +{ + struct rdc_regs *imx_rdc = (struct rdc_regs *)RDC_BASE_ADDR; + u32 reg; + + reg = readl(&imx_rdc->pdap[per_id]); + if (!(reg & RDC_PDAP_DRW_MASK(dom_id))) + return -EACCES; /*No access*/ + + return 0; +} + +/* + * Lock up the RDC semaphore for this peripheral if semaphore is required. + */ +int imx_rdc_sema_lock(int per_id) +{ + struct rdc_sema_regs *imx_rdc_sema; + int ret; + u8 reg; + + ret = imx_rdc_check_sema_required(per_id); + if (ret) + return ret; + + if (per_id < SEMA_GATES_NUM) + imx_rdc_sema = (struct rdc_sema_regs *)SEMAPHORE1_BASE_ADDR; + else + imx_rdc_sema = (struct rdc_sema_regs *)SEMAPHORE2_BASE_ADDR; + + do { + writeb(RDC_SEMA_PROC_ID, + &imx_rdc_sema->gate[per_id % SEMA_GATES_NUM]); + reg = readb(&imx_rdc_sema->gate[per_id % SEMA_GATES_NUM]); + if ((reg & RDC_SEMA_GATE_GTFSM_MASK) == RDC_SEMA_PROC_ID) + break; /* Get the Semaphore*/ + } while (1); + + return 0; +} + +/* + * Unlock the RDC semaphore for this peripheral if main CPU is the + * semaphore owner. + */ +int imx_rdc_sema_unlock(int per_id) +{ + struct rdc_sema_regs *imx_rdc_sema; + int ret; + u8 reg; + + ret = imx_rdc_check_sema_required(per_id); + if (ret) + return ret; + + if (per_id < SEMA_GATES_NUM) + imx_rdc_sema = (struct rdc_sema_regs *)SEMAPHORE1_BASE_ADDR; + else + imx_rdc_sema = (struct rdc_sema_regs *)SEMAPHORE2_BASE_ADDR; + + reg = readb(&imx_rdc_sema->gate[per_id % SEMA_GATES_NUM]); + if ((reg & RDC_SEMA_GATE_GTFSM_MASK) != RDC_SEMA_PROC_ID) + return -EACCES; /*Not the semaphore owner */ + + writeb(0x0, &imx_rdc_sema->gate[per_id % SEMA_GATES_NUM]); + + return 0; +} + +/* + * Setup RDC setting for one peripheral + */ +int imx_rdc_setup_peri(rdc_peri_cfg_t p) +{ + struct rdc_regs *imx_rdc = (struct rdc_regs *)RDC_BASE_ADDR; + u32 reg = 0; + u32 share_count = 0; + u32 peri_id = p & RDC_PERI_MASK; + u32 domain = (p & RDC_DOMAIN_MASK) >> RDC_DOMAIN_SHIFT_BASE; + + /* No domain assigned */ + if (domain == 0) + return -EINVAL; + + reg |= domain; + + share_count = (domain & 0x3) + + ((domain >> 2) & 0x3) + + ((domain >> 4) & 0x3) + + ((domain >> 6) & 0x3); + + if (share_count > 0x3) + reg |= RDC_PDAP_SREQ_MASK; + + writel(reg, &imx_rdc->pdap[peri_id]); + + return 0; +} + +/* + * Setup RDC settings for multiple peripherals + */ +int imx_rdc_setup_peripherals(rdc_peri_cfg_t const *peripherals_list, + unsigned count) +{ + rdc_peri_cfg_t const *p = peripherals_list; + int i, ret; + + for (i = 0; i < count; i++) { + ret = imx_rdc_setup_peri(*p); + if (ret) + return ret; + p++; + } + + return 0; +} + +/* + * Setup RDC setting for one master + */ +int imx_rdc_setup_ma(rdc_ma_cfg_t p) +{ + struct rdc_regs *imx_rdc = (struct rdc_regs *)RDC_BASE_ADDR; + u32 master_id = (p & RDC_MASTER_MASK) >> RDC_MASTER_SHIFT; + u32 domain = (p & RDC_DOMAIN_MASK) >> RDC_DOMAIN_SHIFT_BASE; + + writel((domain & RDC_MDA_DID_MASK), &imx_rdc->mda[master_id]); + + return 0; +} + +/* + * Setup RDC settings for multiple masters + */ +int imx_rdc_setup_masters(rdc_ma_cfg_t const *masters_list, unsigned count) +{ + rdc_ma_cfg_t const *p = masters_list; + int i, ret; + + for (i = 0; i < count; i++) { + ret = imx_rdc_setup_ma(*p); + if (ret) + return ret; + p++; + } + + return 0; +} diff --git a/roms/u-boot/arch/arm/mach-imx/sata.c b/roms/u-boot/arch/arm/mach-imx/sata.c new file mode 100644 index 000000000..c4599aaf6 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/sata.c @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2011 Freescale Semiconductor, Inc. + */ + +#include <asm/mach-imx/iomux-v3.h> +#include <asm/arch/iomux.h> +#include <asm/io.h> +#include <asm/arch/clock.h> +#include <asm/arch/imx-regs.h> +#include <asm/arch/sys_proto.h> + +int setup_sata(void) +{ + struct iomuxc *const iomuxc_regs = (struct iomuxc *)IOMUXC_BASE_ADDR; + int ret; + + if (!is_mx6dq() && !is_mx6dqp()) + return 1; + + ret = enable_sata_clock(); + if (ret) + return ret; + + clrsetbits_le32(&iomuxc_regs->gpr[13], + IOMUXC_GPR13_SATA_MASK, + IOMUXC_GPR13_SATA_PHY_8_RXEQ_3P0DB + |IOMUXC_GPR13_SATA_PHY_7_SATA2M + |IOMUXC_GPR13_SATA_SPEED_3G + |(3<<IOMUXC_GPR13_SATA_PHY_6_SHIFT) + |IOMUXC_GPR13_SATA_SATA_PHY_5_SS_DISABLED + |IOMUXC_GPR13_SATA_SATA_PHY_4_ATTEN_9_16 + |IOMUXC_GPR13_SATA_PHY_3_TXBOOST_0P00_DB + |IOMUXC_GPR13_SATA_PHY_2_TX_1P104V + |IOMUXC_GPR13_SATA_PHY_1_SLOW); + + return 0; +} diff --git a/roms/u-boot/arch/arm/mach-imx/speed.c b/roms/u-boot/arch/arm/mach-imx/speed.c new file mode 100644 index 000000000..b729187ec --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/speed.c @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2000-2003 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * Copyright (C) 2004-2007 Freescale Semiconductor, Inc. + * TsiChung Liew (Tsi-Chung.Liew@freescale.com) + */ + +#include <common.h> +#include <clock_legacy.h> +#include <asm/arch/imx-regs.h> +#include <asm/arch/clock.h> +#include <asm/global_data.h> + +#ifdef CONFIG_FSL_ESDHC_IMX +DECLARE_GLOBAL_DATA_PTR; +#endif + +int get_clocks(void) +{ +#ifdef CONFIG_FSL_ESDHC_IMX +#ifdef CONFIG_FSL_USDHC +#if CONFIG_SYS_FSL_ESDHC_ADDR == USDHC2_BASE_ADDR + gd->arch.sdhc_clk = mxc_get_clock(MXC_ESDHC2_CLK); +#elif CONFIG_SYS_FSL_ESDHC_ADDR == USDHC3_BASE_ADDR + gd->arch.sdhc_clk = mxc_get_clock(MXC_ESDHC3_CLK); +#elif CONFIG_SYS_FSL_ESDHC_ADDR == USDHC4_BASE_ADDR + gd->arch.sdhc_clk = mxc_get_clock(MXC_ESDHC4_CLK); +#else + gd->arch.sdhc_clk = mxc_get_clock(MXC_ESDHC_CLK); +#endif +#else +#if CONFIG_SYS_FSL_ESDHC_ADDR == MMC_SDHC2_BASE_ADDR + gd->arch.sdhc_clk = mxc_get_clock(MXC_ESDHC2_CLK); +#elif CONFIG_SYS_FSL_ESDHC_ADDR == MMC_SDHC3_BASE_ADDR + gd->arch.sdhc_clk = mxc_get_clock(MXC_ESDHC3_CLK); +#elif CONFIG_SYS_FSL_ESDHC_ADDR == MMC_SDHC4_BASE_ADDR + gd->arch.sdhc_clk = mxc_get_clock(MXC_ESDHC4_CLK); +#else + gd->arch.sdhc_clk = mxc_get_clock(MXC_ESDHC_CLK); +#endif +#endif +#endif + return 0; +} diff --git a/roms/u-boot/arch/arm/mach-imx/spl.c b/roms/u-boot/arch/arm/mach-imx/spl.c new file mode 100644 index 000000000..36033d611 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/spl.c @@ -0,0 +1,347 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2014 Gateworks Corporation + * Copyright (C) 2011-2012 Freescale Semiconductor, Inc. + * + * Author: Tim Harvey <tharvey@gateworks.com> + */ + +#include <common.h> +#include <hang.h> +#include <init.h> +#include <log.h> +#include <asm/global_data.h> +#include <asm/io.h> +#include <asm/arch/imx-regs.h> +#include <asm/arch/sys_proto.h> +#include <asm/spl.h> +#include <spl.h> +#include <asm/mach-imx/hab.h> +#include <asm/mach-imx/boot_mode.h> +#include <g_dnl.h> +#include <linux/libfdt.h> + +DECLARE_GLOBAL_DATA_PTR; + +__weak int spl_board_boot_device(enum boot_device boot_dev_spl) +{ + return 0; +} + +#if defined(CONFIG_MX6) +/* determine boot device from SRC_SBMR1 (BOOT_CFG[4:1]) or SRC_GPR9 register */ +u32 spl_boot_device(void) +{ + unsigned int bmode = readl(&src_base->sbmr2); + u32 reg = imx6_src_get_boot_mode(); + + /* + * Check for BMODE if serial downloader is enabled + * BOOT_MODE - see IMX6DQRM Table 8-1 + */ + if (((bmode >> 24) & 0x03) == 0x01) /* Serial Downloader */ + return BOOT_DEVICE_BOARD; + + /* + * The above method does not detect that the boot ROM used + * serial downloader in case the boot ROM decided to use the + * serial downloader as a fall back (primary boot source failed). + * + * Infer that the boot ROM used the USB serial downloader by + * checking whether the USB PHY is currently active... This + * assumes that SPL did not (yet) initialize the USB PHY... + */ + if (is_usbotg_phy_active()) + return BOOT_DEVICE_BOARD; + + /* BOOT_CFG1[7:4] - see IMX6DQRM Table 8-8 */ + switch ((reg & IMX6_BMODE_MASK) >> IMX6_BMODE_SHIFT) { + /* EIM: See 8.5.1, Table 8-9 */ + case IMX6_BMODE_EMI: + /* BOOT_CFG1[3]: NOR/OneNAND Selection */ + switch ((reg & IMX6_BMODE_EMI_MASK) >> IMX6_BMODE_EMI_SHIFT) { + case IMX6_BMODE_ONENAND: + return BOOT_DEVICE_ONENAND; + case IMX6_BMODE_NOR: + return BOOT_DEVICE_NOR; + break; + } + /* Reserved: Used to force Serial Downloader */ + case IMX6_BMODE_RESERVED: + return BOOT_DEVICE_BOARD; + /* SATA: See 8.5.4, Table 8-20 */ +#if !defined(CONFIG_MX6UL) && !defined(CONFIG_MX6ULL) + case IMX6_BMODE_SATA: + return BOOT_DEVICE_SATA; +#endif + /* Serial ROM: See 8.5.5.1, Table 8-22 */ + case IMX6_BMODE_SERIAL_ROM: + /* BOOT_CFG4[2:0] */ + switch ((reg & IMX6_BMODE_SERIAL_ROM_MASK) >> + IMX6_BMODE_SERIAL_ROM_SHIFT) { + case IMX6_BMODE_ECSPI1: + case IMX6_BMODE_ECSPI2: + case IMX6_BMODE_ECSPI3: + case IMX6_BMODE_ECSPI4: + case IMX6_BMODE_ECSPI5: + return BOOT_DEVICE_SPI; + case IMX6_BMODE_I2C1: + case IMX6_BMODE_I2C2: + case IMX6_BMODE_I2C3: + return BOOT_DEVICE_I2C; + } + break; + /* SD/eSD: 8.5.3, Table 8-15 */ + case IMX6_BMODE_SD: + case IMX6_BMODE_ESD: + return BOOT_DEVICE_MMC1; + /* MMC/eMMC: 8.5.3 */ + case IMX6_BMODE_MMC: + case IMX6_BMODE_EMMC: + return BOOT_DEVICE_MMC1; + /* NAND Flash: 8.5.2, Table 8-10 */ + case IMX6_BMODE_NAND_MIN ... IMX6_BMODE_NAND_MAX: + return BOOT_DEVICE_NAND; +#if defined(CONFIG_MX6UL) || defined(CONFIG_MX6ULL) + /* QSPI boot */ + case IMX6_BMODE_QSPI: + return BOOT_DEVICE_SPI; +#endif + } + return BOOT_DEVICE_NONE; +} + +#elif defined(CONFIG_MX7) || defined(CONFIG_IMX8M) || defined(CONFIG_IMX8) +/* Translate iMX7/i.MX8M boot device to the SPL boot device enumeration */ +u32 spl_boot_device(void) +{ +#if defined(CONFIG_MX7) + unsigned int bmode = readl(&src_base->sbmr2); + + /* + * Check for BMODE if serial downloader is enabled + * BOOT_MODE - see IMX7DRM Table 6-24 + */ + if (((bmode >> 24) & 0x03) == 0x01) /* Serial Downloader */ + return BOOT_DEVICE_BOARD; + + /* + * The above method does not detect that the boot ROM used + * serial downloader in case the boot ROM decided to use the + * serial downloader as a fall back (primary boot source failed). + * + * Infer that the boot ROM used the USB serial downloader by + * checking whether the USB PHY is currently active... This + * assumes that SPL did not (yet) initialize the USB PHY... + */ + if (is_boot_from_usb()) + return BOOT_DEVICE_BOARD; +#endif + + enum boot_device boot_device_spl = get_boot_device(); + + if (IS_ENABLED(CONFIG_IMX8MM) || IS_ENABLED(CONFIG_IMX8MN) || + IS_ENABLED(CONFIG_IMX8MP)) + return spl_board_boot_device(boot_device_spl); + + switch (boot_device_spl) { +#if defined(CONFIG_MX7) + case SD1_BOOT: + case MMC1_BOOT: + case SD2_BOOT: + case MMC2_BOOT: + case SD3_BOOT: + case MMC3_BOOT: + return BOOT_DEVICE_MMC1; +#elif defined(CONFIG_IMX8) + case MMC1_BOOT: + return BOOT_DEVICE_MMC1; + case SD2_BOOT: + return BOOT_DEVICE_MMC2_2; + case SD3_BOOT: + return BOOT_DEVICE_MMC1; + case FLEXSPI_BOOT: + return BOOT_DEVICE_SPI; +#elif defined(CONFIG_IMX8M) + case SD1_BOOT: + case MMC1_BOOT: + return BOOT_DEVICE_MMC1; + case SD2_BOOT: + case MMC2_BOOT: + return BOOT_DEVICE_MMC2; +#endif + case NAND_BOOT: + return BOOT_DEVICE_NAND; + case SPI_NOR_BOOT: + return BOOT_DEVICE_SPI; + case QSPI_BOOT: + return BOOT_DEVICE_NOR; + case USB_BOOT: + return BOOT_DEVICE_USB; + default: + return BOOT_DEVICE_NONE; + } +} +#endif /* CONFIG_MX7 || CONFIG_IMX8M || CONFIG_IMX8 */ + +#ifdef CONFIG_SPL_USB_GADGET +int g_dnl_bind_fixup(struct usb_device_descriptor *dev, const char *name) +{ + put_unaligned(CONFIG_USB_GADGET_PRODUCT_NUM + 0xfff, &dev->idProduct); + + return 0; +} + +#define SDPV_BCD_DEVICE 0x500 +int g_dnl_get_board_bcd_device_number(int gcnum) +{ + return SDPV_BCD_DEVICE; +} +#endif + +#if defined(CONFIG_SPL_MMC_SUPPORT) +/* called from spl_mmc to see type of boot mode for storage (RAW or FAT) */ +u32 spl_mmc_boot_mode(const u32 boot_device) +{ +#if defined(CONFIG_MX7) || defined(CONFIG_IMX8M) || defined(CONFIG_IMX8) + switch (get_boot_device()) { + /* for MMC return either RAW or FAT mode */ + case SD1_BOOT: + case SD2_BOOT: + case SD3_BOOT: + if (IS_ENABLED(CONFIG_SPL_FS_FAT)) + return MMCSD_MODE_FS; + else + return MMCSD_MODE_RAW; + case MMC1_BOOT: + case MMC2_BOOT: + case MMC3_BOOT: + if (IS_ENABLED(CONFIG_SPL_FS_FAT)) + return MMCSD_MODE_FS; + else if (IS_ENABLED(CONFIG_SUPPORT_EMMC_BOOT)) + return MMCSD_MODE_EMMCBOOT; + else + return MMCSD_MODE_RAW; + default: + puts("spl: ERROR: unsupported device\n"); + hang(); + } +#else + switch (boot_device) { + /* for MMC return either RAW or FAT mode */ + case BOOT_DEVICE_MMC1: + case BOOT_DEVICE_MMC2: + case BOOT_DEVICE_MMC2_2: + if (IS_ENABLED(CONFIG_SPL_FS_FAT)) + return MMCSD_MODE_FS; + else if (IS_ENABLED(CONFIG_SUPPORT_EMMC_BOOT)) + return MMCSD_MODE_EMMCBOOT; + else + return MMCSD_MODE_RAW; + default: + puts("spl: ERROR: unsupported device\n"); + hang(); + } +#endif +} +#endif + +#if defined(CONFIG_IMX_HAB) + +/* + * +------------+ 0x0 (DDR_UIMAGE_START) - + * | Header | | + * +------------+ 0x40 | + * | | | + * | | | + * | | | + * | | | + * | Image Data | | + * . | | + * . | > Stuff to be authenticated ----+ + * . | | | + * | | | | + * | | | | + * +------------+ | | + * | | | | + * | Fill Data | | | + * | | | | + * +------------+ Align to ALIGN_SIZE | | + * | IVT | | | + * +------------+ + IVT_SIZE - | + * | | | + * | CSF DATA | <---------------------------------------------------------+ + * | | + * +------------+ + * | | + * | Fill Data | + * | | + * +------------+ + CSF_PAD_SIZE + */ + +__weak void __noreturn jump_to_image_no_args(struct spl_image_info *spl_image) +{ + typedef void __noreturn (*image_entry_noargs_t)(void); + uint32_t offset; + + image_entry_noargs_t image_entry = + (image_entry_noargs_t)(unsigned long)spl_image->entry_point; + + debug("image entry point: 0x%lX\n", spl_image->entry_point); + + if (spl_image->flags & SPL_FIT_FOUND) { + image_entry(); + } else { + /* + * HAB looks for the CSF at the end of the authenticated + * data therefore, we need to subtract the size of the + * CSF from the actual filesize + */ + offset = spl_image->size - CONFIG_CSF_SIZE; + if (!imx_hab_authenticate_image(spl_image->load_addr, + offset + IVT_SIZE + + CSF_PAD_SIZE, offset)) { + image_entry(); + } else { + panic("spl: ERROR: image authentication fail\n"); + } + } +} + +#if !defined(CONFIG_SPL_FIT_SIGNATURE) +ulong board_spl_fit_size_align(ulong size) +{ + /* + * HAB authenticate_image requests the IVT offset is + * aligned to 0x1000 + */ + + size = ALIGN(size, 0x1000); + size += CONFIG_CSF_SIZE; + + return size; +} + +void board_spl_fit_post_load(const void *fit) +{ + u32 offset = ALIGN(fdt_totalsize(fit), 0x1000); + + if (imx_hab_authenticate_image((uintptr_t)fit, + offset + IVT_SIZE + CSF_PAD_SIZE, + offset)) { + panic("spl: ERROR: image authentication unsuccessful\n"); + } +} +#endif + +#endif + +#if defined(CONFIG_MX6) && defined(CONFIG_SPL_OS_BOOT) +int dram_init_banksize(void) +{ + gd->bd->bi_dram[0].start = CONFIG_SYS_SDRAM_BASE; + gd->bd->bi_dram[0].size = imx_ddr_size(); + + return 0; +} +#endif diff --git a/roms/u-boot/arch/arm/mach-imx/spl_imx_romapi.c b/roms/u-boot/arch/arm/mach-imx/spl_imx_romapi.c new file mode 100644 index 000000000..d2085dabd --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/spl_imx_romapi.c @@ -0,0 +1,294 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2019 NXP + */ + +#include <common.h> +#include <errno.h> +#include <image.h> +#include <log.h> +#include <asm/global_data.h> +#include <linux/libfdt.h> +#include <spl.h> + +#include <asm/arch/sys_proto.h> + +DECLARE_GLOBAL_DATA_PTR; + +static int is_boot_from_stream_device(u32 boot) +{ + u32 interface; + + interface = boot >> 16; + if (interface >= BT_DEV_TYPE_USB) + return 1; + + if (interface == BT_DEV_TYPE_MMC && (boot & 1)) + return 1; + + return 0; +} + +static ulong spl_romapi_read_seekable(struct spl_load_info *load, + ulong sector, ulong count, + void *buf) +{ + u32 pagesize = *(u32 *)load->priv; + volatile gd_t *pgd = gd; + ulong byte = count * pagesize; + int ret; + u32 offset; + + offset = sector * pagesize; + + debug("ROM API load from 0x%x, size 0x%x\n", offset, (u32)byte); + + ret = g_rom_api->download_image(buf, offset, byte, + ((uintptr_t)buf) ^ offset ^ byte); + set_gd(pgd); + + if (ret == ROM_API_OKAY) + return count; + + printf("ROM API Failure when load 0x%x\n", offset); + + return 0; +} + +static int spl_romapi_load_image_seekable(struct spl_image_info *spl_image, + struct spl_boot_device *bootdev, + u32 rom_bt_dev) +{ + volatile gd_t *pgd = gd; + int ret; + u32 offset; + u32 pagesize, size; + struct image_header *header; + u32 image_offset; + + ret = g_rom_api->query_boot_infor(QUERY_IVT_OFF, &offset, + ((uintptr_t)&offset) ^ QUERY_IVT_OFF); + ret |= g_rom_api->query_boot_infor(QUERY_PAGE_SZ, &pagesize, + ((uintptr_t)&pagesize) ^ QUERY_PAGE_SZ); + ret |= g_rom_api->query_boot_infor(QUERY_IMG_OFF, &image_offset, + ((uintptr_t)&image_offset) ^ QUERY_IMG_OFF); + + set_gd(pgd); + + if (ret != ROM_API_OKAY) { + puts("ROMAPI: Failure query boot infor pagesize/offset\n"); + return -1; + } + + header = (struct image_header *)(CONFIG_SPL_IMX_ROMAPI_LOADADDR); + + printf("image offset 0x%x, pagesize 0x%x, ivt offset 0x%x\n", + image_offset, pagesize, offset); + + if (((rom_bt_dev >> 16) & 0xff) == BT_DEV_TYPE_FLEXSPINOR) + offset = CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_SECTOR * 512; + else + offset = image_offset + + CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_SECTOR * 512 - 0x8000; + + size = ALIGN(sizeof(struct image_header), pagesize); + ret = g_rom_api->download_image((u8 *)header, offset, size, + ((uintptr_t)header) ^ offset ^ size); + set_gd(pgd); + + if (ret != ROM_API_OKAY) { + printf("ROMAPI: download failure offset 0x%x size 0x%x\n", + offset, size); + return -1; + } + + if (IS_ENABLED(CONFIG_SPL_LOAD_FIT) && + image_get_magic(header) == FDT_MAGIC) { + struct spl_load_info load; + + memset(&load, 0, sizeof(load)); + load.bl_len = pagesize; + load.read = spl_romapi_read_seekable; + load.priv = &pagesize; + return spl_load_simple_fit(spl_image, &load, + offset / pagesize, header); + } else { + /* TODO */ + puts("Can't support legacy image\n"); + return -1; + } + + return 0; +} + +static ulong spl_ram_load_read(struct spl_load_info *load, ulong sector, + ulong count, void *buf) +{ + memcpy(buf, (void *)(sector), count); + + if (load->priv) { + ulong *p = (ulong *)load->priv; + ulong total = sector + count; + + if (total > *p) + *p = total; + } + + return count; +} + +static ulong get_fit_image_size(void *fit) +{ + struct spl_image_info spl_image; + struct spl_load_info spl_load_info; + ulong last = (ulong)fit; + + memset(&spl_load_info, 0, sizeof(spl_load_info)); + spl_load_info.bl_len = 1; + spl_load_info.read = spl_ram_load_read; + spl_load_info.priv = &last; + + spl_load_simple_fit(&spl_image, &spl_load_info, + (uintptr_t)fit, fit); + + return last - (ulong)fit; +} + +u8 *search_fit_header(u8 *p, int size) +{ + int i; + + for (i = 0; i < size; i += 4) + if (genimg_get_format(p + i) == IMAGE_FORMAT_FIT) + return p + i; + + return NULL; +} + +static int spl_romapi_load_image_stream(struct spl_image_info *spl_image, + struct spl_boot_device *bootdev) +{ + struct spl_load_info load; + volatile gd_t *pgd = gd; + u32 pagesize, pg; + int ret; + int i = 0; + u8 *p = (u8 *)CONFIG_SPL_IMX_ROMAPI_LOADADDR; + u8 *pfit = NULL; + int imagesize; + int total; + + ret = g_rom_api->query_boot_infor(QUERY_PAGE_SZ, &pagesize, + ((uintptr_t)&pagesize) ^ QUERY_PAGE_SZ); + set_gd(pgd); + + if (ret != ROM_API_OKAY) + puts("failure at query_boot_info\n"); + + pg = pagesize; + if (pg < 1024) + pg = 1024; + + for (i = 0; i < 640; i++) { + ret = g_rom_api->download_image(p, 0, pg, + ((uintptr_t)p) ^ pg); + set_gd(pgd); + + if (ret != ROM_API_OKAY) { + puts("Steam(USB) download failure\n"); + return -1; + } + + pfit = search_fit_header(p, pg); + p += pg; + + if (pfit) + break; + } + + if (!pfit) { + puts("Can't found uboot FIT image in 640K range \n"); + return -1; + } + + if (p - pfit < sizeof(struct fdt_header)) { + ret = g_rom_api->download_image(p, 0, pg, ((uintptr_t)p) ^ pg); + set_gd(pgd); + + if (ret != ROM_API_OKAY) { + puts("Steam(USB) download failure\n"); + return -1; + } + + p += pg; + } + + imagesize = fit_get_size(pfit); + printf("Find FIT header 0x&%p, size %d\n", pfit, imagesize); + + if (p - pfit < imagesize) { + imagesize -= p - pfit; + /*need pagesize hear after ROM fix USB problme*/ + imagesize += pg - 1; + imagesize /= pg; + imagesize *= pg; + + printf("Need continue download %d\n", imagesize); + + ret = g_rom_api->download_image(p, 0, imagesize, + ((uintptr_t)p) ^ imagesize); + set_gd(pgd); + + p += imagesize; + + if (ret != ROM_API_OKAY) { + printf("Failure download %d\n", imagesize); + return -1; + } + } + + total = get_fit_image_size(pfit); + total += 3; + total &= ~0x3; + + imagesize = total - (p - pfit); + + imagesize += pagesize - 1; + imagesize /= pagesize; + imagesize *= pagesize; + + printf("Download %d, total fit %d\n", imagesize, total); + + ret = g_rom_api->download_image(p, 0, imagesize, + ((uintptr_t)p) ^ imagesize); + if (ret != ROM_API_OKAY) + printf("ROM download failure %d\n", imagesize); + + memset(&load, 0, sizeof(load)); + load.bl_len = 1; + load.read = spl_ram_load_read; + + return spl_load_simple_fit(spl_image, &load, (ulong)pfit, pfit); +} + +int board_return_to_bootrom(struct spl_image_info *spl_image, + struct spl_boot_device *bootdev) +{ + volatile gd_t *pgd = gd; + int ret; + u32 boot; + + ret = g_rom_api->query_boot_infor(QUERY_BT_DEV, &boot, + ((uintptr_t)&boot) ^ QUERY_BT_DEV); + set_gd(pgd); + + if (ret != ROM_API_OKAY) { + puts("ROMAPI: failure at query_boot_info\n"); + return -1; + } + + if (is_boot_from_stream_device(boot)) + return spl_romapi_load_image_stream(spl_image, bootdev); + + return spl_romapi_load_image_seekable(spl_image, bootdev, boot); +} diff --git a/roms/u-boot/arch/arm/mach-imx/spl_qspi.cfg b/roms/u-boot/arch/arm/mach-imx/spl_qspi.cfg new file mode 100644 index 000000000..1e39ae2f0 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/spl_qspi.cfg @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2014, Compulab Ltd - http://compulab.co.il/ + */ + +#define __ASSEMBLY__ +#include <config.h> + +IMAGE_VERSION 2 +BOOT_FROM qspi + +/* + * Secure boot support + */ +#ifdef CONFIG_IMX_HAB +CSF CONFIG_CSF_SIZE +#endif diff --git a/roms/u-boot/arch/arm/mach-imx/spl_sd.cfg b/roms/u-boot/arch/arm/mach-imx/spl_sd.cfg new file mode 100644 index 000000000..dbaee8153 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/spl_sd.cfg @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2014, Compulab Ltd - http://compulab.co.il/ + */ + +#define __ASSEMBLY__ +#include <config.h> + +IMAGE_VERSION 2 +BOOT_FROM sd + +/* + * Secure boot support + */ +#ifdef CONFIG_IMX_HAB +CSF CONFIG_CSF_SIZE +#endif diff --git a/roms/u-boot/arch/arm/mach-imx/syscounter.c b/roms/u-boot/arch/arm/mach-imx/syscounter.c new file mode 100644 index 000000000..6dfed365d --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/syscounter.c @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2015 Freescale Semiconductor, Inc. + * + * The file use ls102xa/timer.c as a reference. + */ + +#include <common.h> +#include <init.h> +#include <time.h> +#include <asm/global_data.h> +#include <asm/io.h> +#include <div64.h> +#include <asm/arch/imx-regs.h> +#include <asm/arch/sys_proto.h> +#include <asm/mach-imx/syscounter.h> +#include <linux/delay.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* + * This function is intended for SHORT delays only. + * It will overflow at around 10 seconds @ 400MHz, + * or 20 seconds @ 200MHz. + */ +unsigned long usec2ticks(unsigned long usec) +{ + ulong ticks; + + if (usec < 1000) + ticks = ((usec * (get_tbclk()/1000)) + 500) / 1000; + else + ticks = ((usec / 10) * (get_tbclk() / 100000)); + + return ticks; +} + +static inline unsigned long long tick_to_time(unsigned long long tick) +{ + unsigned long freq; + + asm volatile("mrc p15, 0, %0, c14, c0, 0" : "=r" (freq)); + + tick *= CONFIG_SYS_HZ; + do_div(tick, freq); + + return tick; +} + +static inline unsigned long long us_to_tick(unsigned long long usec) +{ + unsigned long freq; + + asm volatile("mrc p15, 0, %0, c14, c0, 0" : "=r" (freq)); + + usec = usec * freq + 999999; + do_div(usec, 1000000); + + return usec; +} + +#ifndef CONFIG_SKIP_LOWLEVEL_INIT +int timer_init(void) +{ + struct sctr_regs *sctr = (struct sctr_regs *)SCTR_BASE_ADDR; + unsigned long val, freq; + + freq = CONFIG_SC_TIMER_CLK; + asm volatile("mcr p15, 0, %0, c14, c0, 0" : : "r" (freq)); + + writel(freq, &sctr->cntfid0); + + /* Enable system counter */ + val = readl(&sctr->cntcr); + val &= ~(SC_CNTCR_FREQ0 | SC_CNTCR_FREQ1); + val |= SC_CNTCR_FREQ0 | SC_CNTCR_ENABLE | SC_CNTCR_HDBG; + writel(val, &sctr->cntcr); + + gd->arch.tbl = 0; + gd->arch.tbu = 0; + + return 0; +} +#endif + +unsigned long long get_ticks(void) +{ + unsigned long long now; + + asm volatile("mrrc p15, 0, %Q0, %R0, c14" : "=r" (now)); + + gd->arch.tbl = (unsigned long)(now & 0xffffffff); + gd->arch.tbu = (unsigned long)(now >> 32); + + return now; +} + +ulong get_timer(ulong base) +{ + return tick_to_time(get_ticks()) - base; +} + +void __udelay(unsigned long usec) +{ + unsigned long long tmp; + ulong tmo; + + tmo = us_to_tick(usec); + tmp = get_ticks() + tmo; /* get current timestamp */ + + while (get_ticks() < tmp) /* loop till event */ + /*NOP*/; +} + +/* + * This function is derived from PowerPC code (timebase clock frequency). + * On ARM it returns the number of timer ticks per second. + */ +ulong get_tbclk(void) +{ + unsigned long freq; + + asm volatile("mrc p15, 0, %0, c14, c0, 0" : "=r" (freq)); + + return freq; +} diff --git a/roms/u-boot/arch/arm/mach-imx/timer.c b/roms/u-boot/arch/arm/mach-imx/timer.c new file mode 100644 index 000000000..fcd45f09f --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/timer.c @@ -0,0 +1,140 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2007 + * Sascha Hauer, Pengutronix + * + * (C) Copyright 2009 Freescale Semiconductor, Inc. + */ + +#include <common.h> +#include <init.h> +#include <time.h> +#include <asm/io.h> +#include <div64.h> +#include <asm/global_data.h> +#include <asm/arch/imx-regs.h> +#include <asm/arch/clock.h> +#include <asm/arch/sys_proto.h> + +/* General purpose timers registers */ +struct mxc_gpt { + unsigned int control; + unsigned int prescaler; + unsigned int status; + unsigned int nouse[6]; + unsigned int counter; +}; + +static struct mxc_gpt *cur_gpt = (struct mxc_gpt *)GPT1_BASE_ADDR; + +/* General purpose timers bitfields */ +#define GPTCR_SWR (1 << 15) /* Software reset */ +#define GPTCR_24MEN (1 << 10) /* Enable 24MHz clock input */ +#define GPTCR_FRR (1 << 9) /* Freerun / restart */ +#define GPTCR_CLKSOURCE_32 (4 << 6) /* Clock source 32khz */ +#define GPTCR_CLKSOURCE_OSC (5 << 6) /* Clock source OSC */ +#define GPTCR_CLKSOURCE_PRE (1 << 6) /* Clock source PRECLK */ +#define GPTCR_CLKSOURCE_MASK (0x7 << 6) +#define GPTCR_TEN 1 /* Timer enable */ + +#define GPTPR_PRESCALER24M_SHIFT 12 +#define GPTPR_PRESCALER24M_MASK (0xF << GPTPR_PRESCALER24M_SHIFT) + +DECLARE_GLOBAL_DATA_PTR; + +static inline int gpt_has_clk_source_osc(void) +{ + if (((is_mx6dq()) && (soc_rev() > CHIP_REV_1_0)) || + is_mx6dqp() || is_mx6sdl() || is_mx6sx() || is_mx6ul() || + is_mx6ull() || is_mx6sll() || is_mx7()) + return 1; + + return 0; +} + +static inline ulong gpt_get_clk(void) +{ +#ifdef CONFIG_MXC_GPT_HCLK + if (gpt_has_clk_source_osc()) + return MXC_HCLK >> 3; + else + return mxc_get_clock(MXC_IPG_PERCLK); +#else + return MXC_CLK32; +#endif +} + +int timer_init(void) +{ + int i; + + /* setup GP Timer 1 */ + __raw_writel(GPTCR_SWR, &cur_gpt->control); + + /* We have no udelay by now */ + for (i = 0; i < 100; i++) + __raw_writel(0, &cur_gpt->control); + + i = __raw_readl(&cur_gpt->control); + i &= ~GPTCR_CLKSOURCE_MASK; + +#ifdef CONFIG_MXC_GPT_HCLK + if (gpt_has_clk_source_osc()) { + i |= GPTCR_CLKSOURCE_OSC | GPTCR_TEN; + + /* + * For DL/S, SX, UL, ULL, SLL set 24Mhz OSC + * Enable bit and prescaler + */ + if (is_mx6sdl() || is_mx6sx() || is_mx6ul() || is_mx6ull() || + is_mx6sll() || is_mx7()) { + i |= GPTCR_24MEN; + + /* Produce 3Mhz clock */ + __raw_writel((7 << GPTPR_PRESCALER24M_SHIFT), + &cur_gpt->prescaler); + } + } else { + i |= GPTCR_CLKSOURCE_PRE | GPTCR_TEN; + } +#else + __raw_writel(0, &cur_gpt->prescaler); /* 32Khz */ + i |= GPTCR_CLKSOURCE_32 | GPTCR_TEN; +#endif + __raw_writel(i, &cur_gpt->control); + + gd->arch.tbl = __raw_readl(&cur_gpt->counter); + gd->arch.tbu = 0; + + return 0; +} + +unsigned long timer_read_counter(void) +{ + return __raw_readl(&cur_gpt->counter); /* current tick value */ +} + +/* + * This function is derived from PowerPC code (timebase clock frequency). + * On ARM it returns the number of timer ticks per second. + */ +ulong get_tbclk(void) +{ + return gpt_get_clk(); +} + +/* + * This function is intended for SHORT delays only. + * It will overflow at around 10 seconds @ 400MHz, + * or 20 seconds @ 200MHz. + */ +unsigned long usec2ticks(unsigned long _usec) +{ + unsigned long long usec = _usec; + + usec *= get_tbclk(); + usec += 999999; + do_div(usec, 1000000); + + return usec; +} diff --git a/roms/u-boot/arch/arm/mach-imx/video.c b/roms/u-boot/arch/arm/mach-imx/video.c new file mode 100644 index 000000000..1bc9b7cc7 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-imx/video.c @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include <common.h> +#include <env.h> +#include <linux/errno.h> +#include <asm/mach-imx/video.h> + +#ifdef CONFIG_IMX_HDMI +#include <asm/arch/mxc_hdmi.h> +#include <asm/io.h> + +int detect_hdmi(struct display_info_t const *dev) +{ + struct hdmi_regs *hdmi = (struct hdmi_regs *)HDMI_ARB_BASE_ADDR; + return readb(&hdmi->phy_stat0) & HDMI_DVI_STAT; +} +#endif + +int board_video_skip(void) +{ + int i; + int ret = 0; + char const *panel = env_get("panel"); + + if (!panel) { + for (i = 0; i < display_count; i++) { + struct display_info_t const *dev = displays+i; + if (dev->detect && dev->detect(dev)) { + panel = dev->mode.name; + printf("auto-detected panel %s\n", panel); + break; + } + } + if (!panel) { + panel = displays[0].mode.name; + printf("No panel detected: default to %s\n", panel); + i = 0; + } + } else { + for (i = 0; i < display_count; i++) { + if (!strcmp(panel, displays[i].mode.name)) + break; + } + } + + if (i < display_count) { + ret = ipuv3_fb_init(&displays[i].mode, displays[i].di ? 1 : 0, + displays[i].pixfmt); + if (!ret) { + if (displays[i].enable) + displays[i].enable(displays + i); + + printf("Display: %s (%ux%u)\n", + displays[i].mode.name, + displays[i].mode.xres, + displays[i].mode.yres); + +#ifdef CONFIG_IMX_HDMI + if (!strcmp(displays[i].mode.name, "HDMI")) + imx_enable_hdmi_phy(); +#endif + } else + printf("LCD %s cannot be configured: %d\n", + displays[i].mode.name, ret); + } else { + printf("unsupported panel %s\n", panel); + return -EINVAL; + } + + return ret; +} + +int ipu_displays_init(void) +{ + return board_video_skip(); +} |