diff options
Diffstat (limited to 'arch/arm/mach-imx')
24 files changed, 3507 insertions, 68 deletions
diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig index 26bfc5ccc4..653463ab46 100644 --- a/arch/arm/mach-imx/Kconfig +++ b/arch/arm/mach-imx/Kconfig @@ -161,7 +161,7 @@ config DDRMC_VF610_CALIBRATION config SPL_IMX_ROMAPI_LOADADDR hex "Default load address to load image through ROM API" - depends on IMX8MN || IMX8MP + depends on IMX8MN || IMX8MP || IMX8ULP config IMX_DCD_ADDR hex "DCD Blocks location on the image" @@ -172,3 +172,16 @@ config IMX_DCD_ADDR 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. + +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.MX 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. diff --git a/arch/arm/mach-imx/Makefile b/arch/arm/mach-imx/Makefile index 82aa39dee7..0ef269563d 100644 --- a/arch/arm/mach-imx/Makefile +++ b/arch/arm/mach-imx/Makefile @@ -68,6 +68,10 @@ obj-$(CONFIG_CMD_DEKBLOB) += cmd_dek.o obj-$(CONFIG_CMD_NANDBCB) += cmd_nandbcb.o endif +ifeq ($(CONFIG_SPL_BUILD),y) +obj-$(CONFIG_SPL_LOAD_IMX_CONTAINER) += image-container.o parse-container.o +endif + PLUGIN = board/$(BOARDDIR)/plugin ifeq ($(CONFIG_USE_IMXIMG_PLUGIN),y) @@ -227,6 +231,7 @@ obj-$(CONFIG_MX5) += mx5/ obj-$(CONFIG_MX6) += mx6/ obj-$(CONFIG_MX7) += mx7/ obj-$(CONFIG_ARCH_MX7ULP) += mx7ulp/ +obj-$(CONFIG_ARCH_IMX8ULP) += imx8ulp/ obj-$(CONFIG_IMX8M) += imx8m/ obj-$(CONFIG_ARCH_IMX8) += imx8/ obj-$(CONFIG_ARCH_IMXRT) += imxrt/ diff --git a/arch/arm/mach-imx/cmd_dek.c b/arch/arm/mach-imx/cmd_dek.c index 1e3cfee473..89da89c51d 100644 --- a/arch/arm/mach-imx/cmd_dek.c +++ b/arch/arm/mach-imx/cmd_dek.c @@ -17,7 +17,7 @@ #include <tee.h> #ifdef CONFIG_IMX_SECO_DEK_ENCAP #include <asm/arch/sci/sci.h> -#include <asm/arch/image.h> +#include <asm/mach-imx/image.h> #endif #include <cpu_func.h> diff --git a/arch/arm/mach-imx/cmd_mfgprot.c b/arch/arm/mach-imx/cmd_mfgprot.c index 1e866b76c8..aed3b2f83d 100644 --- a/arch/arm/mach-imx/cmd_mfgprot.c +++ b/arch/arm/mach-imx/cmd_mfgprot.c @@ -29,7 +29,7 @@ DECLARE_GLOBAL_DATA_PTR; * 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[]) +static int do_mfgprot(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { u8 *m_ptr, *dgst_ptr, *c_ptr, *d_ptr, *dst_ptr; char *pubk, *sign, *sel; diff --git a/arch/arm/mach-imx/imx8/image.c b/arch/arm/mach-imx/image-container.c index 5abc0d3a39..68b30bcfc5 100644 --- a/arch/arm/mach-imx/imx8/image.c +++ b/arch/arm/mach-imx/image-container.c @@ -11,7 +11,7 @@ #include <mmc.h> #include <spi_flash.h> #include <nand.h> -#include <asm/arch/image.h> +#include <asm/mach-imx/image.h> #include <asm/arch/sys_proto.h> #include <asm/mach-imx/boot_mode.h> @@ -19,8 +19,9 @@ #define QSPI_DEV 1 #define NAND_DEV 2 #define QSPI_NOR_DEV 3 +#define ROM_API_DEV 4 -static int __get_container_size(ulong addr) +int get_container_size(ulong addr, u16 *header_length) { struct container_hdr *phdr; struct boot_img_t *img_entry; @@ -34,7 +35,9 @@ static int __get_container_size(ulong addr) return -EFAULT; } - max_offset = sizeof(struct container_hdr); + max_offset = phdr->length_lsb + (phdr->length_msb << 8); + if (header_length) + *header_length = max_offset; img_entry = (struct boot_img_t *)(addr + sizeof(struct container_hdr)); for (i = 0; i < phdr->num_images; i++) { @@ -60,7 +63,7 @@ static int __get_container_size(ulong addr) return max_offset; } -static int get_container_size(void *dev, int dev_type, unsigned long offset) +static int get_dev_container_size(void *dev, int dev_type, unsigned long offset, u16 *header_length) { u8 *buf = malloc(CONTAINER_HDR_ALIGNMENT); int ret = 0; @@ -115,7 +118,17 @@ static int get_container_size(void *dev, int dev_type, unsigned long offset) memcpy(buf, (const void *)offset, CONTAINER_HDR_ALIGNMENT); #endif - ret = __get_container_size((ulong)buf); +#ifdef CONFIG_SPL_BOOTROM_SUPPORT + if (dev_type == ROM_API_DEV) { + ret = spl_romapi_raw_seekable_read(offset, CONTAINER_HDR_ALIGNMENT, buf); + if (!ret) { + printf("Read container image from ROM API failed\n"); + return -EIO; + } + } +#endif + + ret = get_container_size((ulong)buf, header_length); free(buf); @@ -149,6 +162,8 @@ static unsigned long get_boot_device_offset(void *dev, int dev_type) offset = CONTAINER_HDR_NAND_OFFSET; } else if (dev_type == QSPI_NOR_DEV) { offset = CONTAINER_HDR_QSPI_OFFSET + 0x08000000; + } else if (dev_type == ROM_API_DEV) { + offset = (unsigned long)dev; } return offset; @@ -158,11 +173,12 @@ static int get_imageset_end(void *dev, int dev_type) { unsigned long offset1 = 0, offset2 = 0; int value_container[2]; + u16 hdr_length; offset1 = get_boot_device_offset(dev, dev_type); offset2 = CONTAINER_HDR_ALIGNMENT + offset1; - value_container[0] = get_container_size(dev, dev_type, offset1); + value_container[0] = get_dev_container_size(dev, dev_type, offset1, &hdr_length); if (value_container[0] < 0) { printf("Parse seco container failed %d\n", value_container[0]); return value_container[0]; @@ -170,7 +186,7 @@ static int get_imageset_end(void *dev, int dev_type) debug("seco container size 0x%x\n", value_container[0]); - value_container[1] = get_container_size(dev, dev_type, offset2); + value_container[1] = get_dev_container_size(dev, dev_type, offset2, &hdr_length); if (value_container[1] < 0) { debug("Parse scu container failed %d, only seco container\n", value_container[1]); @@ -247,3 +263,24 @@ unsigned long spl_nor_get_uboot_base(void) return end; } #endif + +#ifdef CONFIG_SPL_BOOTROM_SUPPORT +u32 __weak spl_arch_boot_image_offset(u32 image_offset, u32 rom_bt_dev) +{ + return image_offset; +} + +ulong spl_romapi_get_uboot_base(u32 image_offset, u32 rom_bt_dev) +{ + ulong end; + + image_offset = spl_arch_boot_image_offset(image_offset, rom_bt_dev); + + end = get_imageset_end((void *)(ulong)image_offset, ROM_API_DEV); + end = ROUND(end, SZ_1K); + + printf("Load image from 0x%lx by ROM_API\n", end); + + return end; +} +#endif diff --git a/arch/arm/mach-imx/imx8/Kconfig b/arch/arm/mach-imx/imx8/Kconfig index 71221d8d06..b43739e5c6 100644 --- a/arch/arm/mach-imx/imx8/Kconfig +++ b/arch/arm/mach-imx/imx8/Kconfig @@ -31,19 +31,6 @@ config IMX8QXP 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 diff --git a/arch/arm/mach-imx/imx8/Makefile b/arch/arm/mach-imx/imx8/Makefile index bbb41adbe4..4ca4c14bdd 100644 --- a/arch/arm/mach-imx/imx8/Makefile +++ b/arch/arm/mach-imx/imx8/Makefile @@ -8,7 +8,4 @@ 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/arch/arm/mach-imx/imx8/ahab.c b/arch/arm/mach-imx/imx8/ahab.c index 015267c8b2..5a4d39cdaa 100644 --- a/arch/arm/mach-imx/imx8/ahab.c +++ b/arch/arm/mach-imx/imx8/ahab.c @@ -13,7 +13,7 @@ #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 <asm/mach-imx/image.h> #include <console.h> #include <cpu_func.h> diff --git a/arch/arm/mach-imx/imx8ulp/Kconfig b/arch/arm/mach-imx/imx8ulp/Kconfig new file mode 100644 index 0000000000..963fc93d34 --- /dev/null +++ b/arch/arm/mach-imx/imx8ulp/Kconfig @@ -0,0 +1,23 @@ +if ARCH_IMX8ULP + +config IMX8ULP + bool + select ARMV8_SPL_EXCEPTION_VECTORS + +config SYS_SOC + default "imx8ulp" + +choice + prompt "i.MX8ULP board select" + optional + +config TARGET_IMX8ULP_EVK + bool "imx8ulp_evk" + select IMX8ULP + select SUPPORT_SPL + +endchoice + +source "board/freescale/imx8ulp_evk/Kconfig" + +endif diff --git a/arch/arm/mach-imx/imx8ulp/Makefile b/arch/arm/mach-imx/imx8ulp/Makefile new file mode 100644 index 0000000000..2c9938fcdf --- /dev/null +++ b/arch/arm/mach-imx/imx8ulp/Makefile @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright 2020 NXP +# + +obj-y += lowlevel_init.o +obj-y += soc.o clock.o iomux.o pcc.o cgc.o rdc.o + +ifeq ($(CONFIG_SPL_BUILD),y) +obj-y += upower/ +endif diff --git a/arch/arm/mach-imx/imx8ulp/cgc.c b/arch/arm/mach-imx/imx8ulp/cgc.c new file mode 100644 index 0000000000..7bfc3862cd --- /dev/null +++ b/arch/arm/mach-imx/imx8ulp/cgc.c @@ -0,0 +1,455 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2021 NXP + */ + +#include <common.h> +#include <div64.h> +#include <asm/io.h> +#include <errno.h> +#include <asm/arch/imx-regs.h> +#include <asm/arch/cgc.h> +#include <asm/arch/sys_proto.h> +#include <asm/global_data.h> +#include <linux/delay.h> + +DECLARE_GLOBAL_DATA_PTR; + +static struct cgc1_regs *cgc1_regs = (struct cgc1_regs *)0x292C0000UL; +static struct cgc2_regs *cgc2_regs = (struct cgc2_regs *)0x2da60000UL; + +void cgc1_soscdiv_init(void) +{ + /* Configure SOSC/FRO DIV1 ~ DIV3 */ + clrbits_le32(&cgc1_regs->soscdiv, BIT(7)); + clrbits_le32(&cgc1_regs->soscdiv, BIT(15)); + clrbits_le32(&cgc1_regs->soscdiv, BIT(23)); + clrbits_le32(&cgc1_regs->soscdiv, BIT(31)); + + clrbits_le32(&cgc1_regs->frodiv, BIT(7)); +} + +void cgc1_pll2_init(void) +{ + u32 reg; + + if (readl(&cgc1_regs->pll2csr) & BIT(23)) + clrbits_le32(&cgc1_regs->pll2csr, BIT(23)); + + /* Disable PLL2 */ + clrbits_le32(&cgc1_regs->pll2csr, BIT(0)); + mdelay(1); + + /* wait valid bit false */ + while ((readl(&cgc1_regs->pll2csr) & BIT(24))) + ; + + /* Select SOSC as source, freq = 31 * 24 =744mhz */ + reg = 31 << 16; + writel(reg, &cgc1_regs->pll2cfg); + + /* Enable PLL2 */ + setbits_le32(&cgc1_regs->pll2csr, BIT(0)); + + /* Wait for PLL2 clock ready */ + while (!(readl(&cgc1_regs->pll2csr) & BIT(24))) + ; +} + +static void cgc1_set_a35_clk(u32 clk_src, u32 div_core) +{ + u32 reg; + + /* ulock */ + if (readl(&cgc1_regs->ca35clk) & BIT(31)) + clrbits_le32(&cgc1_regs->ca35clk, BIT(31)); + + reg = readl(&cgc1_regs->ca35clk); + reg &= ~GENMASK(29, 21); + reg |= ((clk_src & 0x3) << 28); + reg |= (((div_core - 1) & 0x3f) << 21); + writel(reg, &cgc1_regs->ca35clk); + + while (!(readl(&cgc1_regs->ca35clk) & BIT(27))) + ; +} + +void cgc1_init_core_clk(void) +{ + u32 reg = readl(&cgc1_regs->ca35clk); + + /* if already selected to PLL2, switch to FRO firstly */ + if (((reg >> 28) & 0x3) == 0x1) + cgc1_set_a35_clk(0, 1); + + /* Set pll2 to 750Mhz for 1V */ + cgc1_pll2_init(); + + /* Set A35 clock to pll2 */ + cgc1_set_a35_clk(1, 1); +} + +void cgc1_enet_stamp_sel(u32 clk_src) +{ + writel((clk_src & 0x7) << 24, &cgc1_regs->enetstamp); +} + +void cgc1_pll3_init(void) +{ + /* Gate off VCO */ + setbits_le32(&cgc1_regs->pll3div_vco, BIT(7)); + + /* Disable PLL3 */ + clrbits_le32(&cgc1_regs->pll3csr, BIT(0)); + + /* Gate off PFDxDIV */ + setbits_le32(&cgc1_regs->pll3div_pfd0, BIT(7) | BIT(15) | BIT(23) | BIT(31)); + setbits_le32(&cgc1_regs->pll3div_pfd1, BIT(7) | BIT(15) | BIT(23) | BIT(31)); + + /* Gate off PFDx */ + setbits_le32(&cgc1_regs->pll3pfdcfg, BIT(7)); + setbits_le32(&cgc1_regs->pll3pfdcfg, BIT(15)); + setbits_le32(&cgc1_regs->pll3pfdcfg, BIT(23)); + setbits_le32(&cgc1_regs->pll3pfdcfg, BIT(31)); + + /* Select SOSC as source */ + clrbits_le32(&cgc1_regs->pll3cfg, BIT(0)); + + //setbits_le32(&cgc1_regs->pll3cfg, 22 << 16); + writel(22 << 16, &cgc1_regs->pll3cfg); + + writel(578, &cgc1_regs->pll3num); + writel(1000, &cgc1_regs->pll3denom); + + /* Enable PLL3 */ + setbits_le32(&cgc1_regs->pll3csr, BIT(0)); + + /* Wait for PLL3 clock ready */ + while (!(readl(&cgc1_regs->pll3csr) & BIT(24))) + ; + /* Gate on VCO */ + clrbits_le32(&cgc1_regs->pll3div_vco, BIT(7)); + + /* + * PFD0: 380MHz/396/396/328 + */ + clrbits_le32(&cgc1_regs->pll3pfdcfg, 0x3F); + setbits_le32(&cgc1_regs->pll3pfdcfg, 25 << 0); + clrbits_le32(&cgc1_regs->pll3pfdcfg, BIT(7)); + while (!(readl(&cgc1_regs->pll3pfdcfg) & BIT(6))) + ; + + clrbits_le32(&cgc1_regs->pll3pfdcfg, 0x3F << 8); + setbits_le32(&cgc1_regs->pll3pfdcfg, 24 << 8); + clrbits_le32(&cgc1_regs->pll3pfdcfg, BIT(15)); + while (!(readl(&cgc1_regs->pll3pfdcfg) & BIT(14))) + ; + + clrbits_le32(&cgc1_regs->pll3pfdcfg, 0x3F << 16); + setbits_le32(&cgc1_regs->pll3pfdcfg, 24 << 16); + clrbits_le32(&cgc1_regs->pll3pfdcfg, BIT(23)); + while (!(readl(&cgc1_regs->pll3pfdcfg) & BIT(22))) + ; + + clrbits_le32(&cgc1_regs->pll3pfdcfg, 0x3F << 24); + setbits_le32(&cgc1_regs->pll3pfdcfg, 29 << 24); + clrbits_le32(&cgc1_regs->pll3pfdcfg, BIT(31)); + while (!(readl(&cgc1_regs->pll3pfdcfg) & BIT(30))) + ; + + clrbits_le32(&cgc1_regs->pll3div_pfd0, BIT(7)); + clrbits_le32(&cgc1_regs->pll3div_pfd0, BIT(15)); + clrbits_le32(&cgc1_regs->pll3div_pfd0, BIT(23)); + clrbits_le32(&cgc1_regs->pll3div_pfd0, BIT(31)); + + clrbits_le32(&cgc1_regs->pll3div_pfd1, BIT(7)); + clrbits_le32(&cgc1_regs->pll3div_pfd1, BIT(15)); + clrbits_le32(&cgc1_regs->pll3div_pfd1, BIT(23)); + clrbits_le32(&cgc1_regs->pll3div_pfd1, BIT(31)); +} + +void cgc2_pll4_init(void) +{ + /* Disable PFD DIV and clear DIV */ + writel(0x80808080, &cgc2_regs->pll4div_pfd0); + writel(0x80808080, &cgc2_regs->pll4div_pfd1); + + /* Gate off and clear PFD */ + writel(0x80808080, &cgc2_regs->pll4pfdcfg); + + /* Disable PLL4 */ + writel(0x0, &cgc2_regs->pll4csr); + + /* Configure PLL4 to 528Mhz and clock source from SOSC */ + writel(22 << 16, &cgc2_regs->pll4cfg); + writel(0x1, &cgc2_regs->pll4csr); + + /* wait for PLL4 output valid */ + while (!(readl(&cgc2_regs->pll4csr) & BIT(24))) + ; + + /* Enable all 4 PFDs */ + setbits_le32(&cgc2_regs->pll4pfdcfg, 30 << 0); /* 316.8Mhz for NIC_LPAV */ + setbits_le32(&cgc2_regs->pll4pfdcfg, 18 << 8); + setbits_le32(&cgc2_regs->pll4pfdcfg, 12 << 16); + setbits_le32(&cgc2_regs->pll4pfdcfg, 24 << 24); + + clrbits_le32(&cgc2_regs->pll4pfdcfg, BIT(7) | BIT(15) | BIT(23) | BIT(31)); + + while ((readl(&cgc2_regs->pll4pfdcfg) & (BIT(30) | BIT(22) | BIT(14) | BIT(6))) + != (BIT(30) | BIT(22) | BIT(14) | BIT(6))) + ; + + /* Enable PFD DIV */ + clrbits_le32(&cgc2_regs->pll4div_pfd0, BIT(7) | BIT(15) | BIT(23) | BIT(31)); + clrbits_le32(&cgc2_regs->pll4div_pfd1, BIT(7) | BIT(15) | BIT(23) | BIT(31)); +} + +void cgc2_ddrclk_config(u32 src, u32 div) +{ + writel((src << 28) | (div << 21), &cgc2_regs->ddrclk); + /* wait for DDRCLK switching done */ + while (!(readl(&cgc2_regs->ddrclk) & BIT(27))) + ; +} + +u32 decode_pll(enum cgc1_clk pll) +{ + u32 reg, infreq, mult; + u32 num, denom; + + infreq = 24000000U; + /* + * Alought there are four choices for the bypass src, + * we choose SOSC 24M which is the default set in ROM. + * TODO: check more the comments + */ + switch (pll) { + case PLL2: + reg = readl(&cgc1_regs->pll2csr); + if (!(reg & BIT(24))) + return 0; + + reg = readl(&cgc1_regs->pll2cfg); + mult = (reg >> 16) & 0x7F; + denom = readl(&cgc1_regs->pll2denom) & 0x3FFFFFFF; + num = readl(&cgc1_regs->pll2num) & 0x3FFFFFFF; + + return (u64)infreq * mult + (u64)infreq * num / denom; + case PLL3: + reg = readl(&cgc1_regs->pll3csr); + if (!(reg & BIT(24))) + return 0; + + reg = readl(&cgc1_regs->pll3cfg); + mult = (reg >> 16) & 0x7F; + denom = readl(&cgc1_regs->pll3denom) & 0x3FFFFFFF; + num = readl(&cgc1_regs->pll3num) & 0x3FFFFFFF; + + return (u64)infreq * mult + (u64)infreq * num / denom; + default: + printf("Unsupported pll clocks %d\n", pll); + break; + } + + return 0; +} + +u32 cgc1_pll3_vcodiv_rate(void) +{ + u32 reg, gate, div; + + reg = readl(&cgc1_regs->pll3div_vco); + gate = BIT(7) & reg; + div = reg & 0x3F; + + return gate ? 0 : decode_pll(PLL3) / (div + 1); +} + +u32 cgc1_pll3_pfd_rate(enum cgc1_clk clk) +{ + u32 index, gate, vld, reg; + + switch (clk) { + case PLL3_PFD0: + index = 0; + break; + case PLL3_PFD1: + index = 1; + break; + case PLL3_PFD2: + index = 2; + break; + case PLL3_PFD3: + index = 3; + break; + default: + return 0; + } + + reg = readl(&cgc1_regs->pll3pfdcfg); + gate = reg & (BIT(7) << (index * 8)); + vld = reg & (BIT(6) << (index * 8)); + + if (gate || !vld) + return 0; + + return (u64)decode_pll(PLL3) * 18 / ((reg >> (index * 8)) & 0x3F); +} + +u32 cgc1_pll3_pfd_div(enum cgc1_clk clk) +{ + void __iomem *base; + u32 pfd, index, gate, reg; + + switch (clk) { + case PLL3_PFD0_DIV1: + base = &cgc1_regs->pll3div_pfd0; + pfd = PLL3_PFD0; + index = 0; + break; + case PLL3_PFD0_DIV2: + base = &cgc1_regs->pll3div_pfd0; + pfd = PLL3_PFD0; + index = 1; + break; + case PLL3_PFD1_DIV1: + base = &cgc1_regs->pll3div_pfd0; + pfd = PLL3_PFD1; + index = 2; + break; + case PLL3_PFD1_DIV2: + base = &cgc1_regs->pll3div_pfd0; + pfd = PLL3_PFD1; + index = 3; + break; + case PLL3_PFD2_DIV1: + base = &cgc1_regs->pll3div_pfd1; + pfd = PLL3_PFD2; + index = 0; + break; + case PLL3_PFD2_DIV2: + base = &cgc1_regs->pll3div_pfd1; + pfd = PLL3_PFD2; + index = 1; + break; + case PLL3_PFD3_DIV1: + base = &cgc1_regs->pll3div_pfd1; + pfd = PLL3_PFD3; + index = 2; + break; + case PLL3_PFD3_DIV2: + base = &cgc1_regs->pll3div_pfd1; + pfd = PLL3_PFD3; + index = 3; + break; + default: + return 0; + } + + reg = readl(base); + gate = reg & (BIT(7) << (index * 8)); + + if (gate) + return 0; + + return cgc1_pll3_pfd_rate(pfd) / (((reg >> (index * 8)) & 0x3F) + 1); +} + +u32 cgc1_sosc_div(enum cgc1_clk clk) +{ + u32 reg, gate, index; + + switch (clk) { + case SOSC: + return 24000000; + case SOSC_DIV1: + index = 0; + break; + case SOSC_DIV2: + index = 1; + break; + case SOSC_DIV3: + index = 2; + break; + default: + return 0; + } + + reg = readl(&cgc1_regs->soscdiv); + gate = reg & (BIT(7) << (index * 8)); + + if (gate) + return 0; + + return 24000000 / (((reg >> (index * 8)) & 0x3F) + 1); +} + +u32 cgc1_fro_div(enum cgc1_clk clk) +{ + u32 reg, gate, vld, index; + + switch (clk) { + case FRO: + return 192000000; + case FRO_DIV1: + index = 0; + break; + case FRO_DIV2: + index = 1; + break; + case FRO_DIV3: + index = 2; + break; + default: + return 0; + } + + reg = readl(&cgc1_regs->frodiv); + gate = reg & (BIT(7) << (index * 8)); + vld = reg & (BIT(6) << (index * 8)); + + if (gate || !vld) + return 0; + + return 24000000 / (((reg >> (index * 8)) & 0x3F) + 1); +} + +u32 cgc1_clk_get_rate(enum cgc1_clk clk) +{ + switch (clk) { + case SOSC: + case SOSC_DIV1: + case SOSC_DIV2: + case SOSC_DIV3: + return cgc1_sosc_div(clk); + case FRO: + case FRO_DIV1: + case FRO_DIV2: + case FRO_DIV3: + return cgc1_fro_div(clk); + case PLL2: + return decode_pll(PLL2); + case PLL3: + return decode_pll(PLL3); + case PLL3_VCODIV: + return cgc1_pll3_vcodiv_rate(); + case PLL3_PFD0: + case PLL3_PFD1: + case PLL3_PFD2: + case PLL3_PFD3: + return cgc1_pll3_pfd_rate(clk); + case PLL3_PFD0_DIV1: + case PLL3_PFD0_DIV2: + case PLL3_PFD1_DIV1: + case PLL3_PFD1_DIV2: + case PLL3_PFD2_DIV1: + case PLL3_PFD2_DIV2: + case PLL3_PFD3_DIV1: + case PLL3_PFD3_DIV2: + return cgc1_pll3_pfd_div(clk); + default: + printf("Unsupported cgc1 clock: %d\n", clk); + return 0; + } +} diff --git a/arch/arm/mach-imx/imx8ulp/clock.c b/arch/arm/mach-imx/imx8ulp/clock.c new file mode 100644 index 0000000000..ebbaad4106 --- /dev/null +++ b/arch/arm/mach-imx/imx8ulp/clock.c @@ -0,0 +1,397 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2020 NXP + */ + +#include <common.h> +#include <command.h> +#include <div64.h> +#include <asm/arch/imx-regs.h> +#include <asm/io.h> +#include <errno.h> +#include <asm/arch/clock.h> +#include <asm/arch/pcc.h> +#include <asm/arch/cgc.h> +#include <asm/arch/sys_proto.h> +#include <asm/global_data.h> +#include <linux/delay.h> + +DECLARE_GLOBAL_DATA_PTR; + +#define PLL_USB_EN_USB_CLKS_MASK (0x01 << 6) +#define PLL_USB_PWR_MASK (0x01 << 12) +#define PLL_USB_ENABLE_MASK (0x01 << 13) +#define PLL_USB_BYPASS_MASK (0x01 << 16) +#define PLL_USB_REG_ENABLE_MASK (0x01 << 21) +#define PLL_USB_DIV_SEL_MASK (0x07 << 22) +#define PLL_USB_LOCK_MASK (0x01 << 31) +#define PCC5_LPDDR4_ADDR 0x2da70108 + +static void lpuart_set_clk(u32 index, enum cgc1_clk clk) +{ + const u32 lpuart_pcc_slots[] = { + LPUART4_PCC3_SLOT, + LPUART5_PCC3_SLOT, + LPUART6_PCC4_SLOT, + LPUART7_PCC4_SLOT, + }; + + const u32 lpuart_pcc[] = { + 3, 3, 4, 4, + }; + + if (index > 3) + return; + + pcc_clock_enable(lpuart_pcc[index], lpuart_pcc_slots[index], false); + pcc_clock_sel(lpuart_pcc[index], lpuart_pcc_slots[index], clk); + pcc_clock_enable(lpuart_pcc[index], lpuart_pcc_slots[index], true); + + pcc_reset_peripheral(lpuart_pcc[index], lpuart_pcc_slots[index], false); +} + +static void init_clk_lpuart(void) +{ + u32 index = 0, i; + + const u32 lpuart_array[] = { + LPUART4_RBASE, + LPUART5_RBASE, + LPUART6_RBASE, + LPUART7_RBASE, + }; + + for (i = 0; i < 4; i++) { + if (lpuart_array[i] == LPUART_BASE) { + index = i; + break; + } + } + + lpuart_set_clk(index, SOSC_DIV2); +} + +void init_clk_fspi(int index) +{ + pcc_clock_enable(4, FLEXSPI2_PCC4_SLOT, false); + pcc_clock_sel(4, FLEXSPI2_PCC4_SLOT, PLL3_PFD2_DIV1); + pcc_clock_div_config(4, FLEXSPI2_PCC4_SLOT, false, 8); + pcc_clock_enable(4, FLEXSPI2_PCC4_SLOT, true); + pcc_reset_peripheral(4, FLEXSPI2_PCC4_SLOT, false); +} + +void setclkout_ddr(void) +{ + writel(0x12800000, 0x2DA60020); + writel(0xa00, 0x298C0000); /* PTD0 */ +} + +void ddrphy_pll_lock(void) +{ + writel(0x00011542, 0x2E065964); + writel(0x00011542, 0x2E06586C); + + writel(0x00000B01, 0x2E062000); + writel(0x00000B01, 0x2E060000); +} + +void init_clk_ddr(void) +{ + /* enable pll4 and ddrclk*/ + cgc2_pll4_init(); + cgc2_ddrclk_config(1, 1); + + /* enable ddr pcc */ + writel(0xd0000000, PCC5_LPDDR4_ADDR); + + /* for debug */ + /* setclkout_ddr(); */ +} + +int set_ddr_clk(u32 phy_freq_mhz) +{ + debug("%s %u\n", __func__, phy_freq_mhz); + + if (phy_freq_mhz == 48) { + writel(0x90000000, PCC5_LPDDR4_ADDR); /* disable ddr pcc */ + cgc2_ddrclk_config(2, 0); /* 24Mhz DDR clock */ + writel(0xd0000000, PCC5_LPDDR4_ADDR); /* enable ddr pcc */ + } else if (phy_freq_mhz == 384) { + writel(0x90000000, PCC5_LPDDR4_ADDR); /* disable ddr pcc */ + cgc2_ddrclk_config(0, 0); /* 192Mhz DDR clock */ + writel(0xd0000000, PCC5_LPDDR4_ADDR); /* enable ddr pcc */ + } else if (phy_freq_mhz == 528) { + writel(0x90000000, PCC5_LPDDR4_ADDR); /* disable ddr pcc */ + cgc2_ddrclk_config(4, 1); /* 264Mhz DDR clock */ + writel(0xd0000000, PCC5_LPDDR4_ADDR); /* enable ddr pcc */ + } else if (phy_freq_mhz == 264) { + writel(0x90000000, PCC5_LPDDR4_ADDR); /* disable ddr pcc */ + cgc2_ddrclk_config(4, 3); /* 132Mhz DDR clock */ + writel(0xd0000000, PCC5_LPDDR4_ADDR); /* enable ddr pcc */ + } else if (phy_freq_mhz == 192) { + writel(0x90000000, PCC5_LPDDR4_ADDR); /* disable ddr pcc */ + cgc2_ddrclk_config(0, 1); /* 96Mhz DDR clock */ + writel(0xd0000000, PCC5_LPDDR4_ADDR); /* enable ddr pcc */ + } else if (phy_freq_mhz == 96) { + writel(0x90000000, PCC5_LPDDR4_ADDR); /* disable ddr pcc */ + cgc2_ddrclk_config(0, 3); /* 48Mhz DDR clock */ + writel(0xd0000000, PCC5_LPDDR4_ADDR); /* enable ddr pcc */ + } else { + printf("ddr phy clk %uMhz is not supported\n", phy_freq_mhz); + return -EINVAL; + } + + return 0; +} + +void clock_init(void) +{ + cgc1_soscdiv_init(); + cgc1_init_core_clk(); + + init_clk_lpuart(); + + pcc_clock_enable(4, SDHC0_PCC4_SLOT, false); + pcc_clock_sel(4, SDHC0_PCC4_SLOT, PLL3_PFD1_DIV2); + pcc_clock_enable(4, SDHC0_PCC4_SLOT, true); + pcc_reset_peripheral(4, SDHC0_PCC4_SLOT, false); + + pcc_clock_enable(4, SDHC1_PCC4_SLOT, false); + pcc_clock_sel(4, SDHC1_PCC4_SLOT, PLL3_PFD2_DIV1); + pcc_clock_enable(4, SDHC1_PCC4_SLOT, true); + pcc_reset_peripheral(4, SDHC1_PCC4_SLOT, false); + + pcc_clock_enable(4, SDHC2_PCC4_SLOT, false); + pcc_clock_sel(4, SDHC2_PCC4_SLOT, PLL3_PFD2_DIV1); + pcc_clock_enable(4, SDHC2_PCC4_SLOT, true); + pcc_reset_peripheral(4, SDHC2_PCC4_SLOT, false); + + /* Enable upower mu1 clk */ + pcc_clock_enable(3, UPOWER_PCC3_SLOT, true); + + /* + * Enable clock division + * TODO: may not needed after ROM ready. + */ +} + +#if IS_ENABLED(CONFIG_SYS_I2C_IMX_LPI2C) +int enable_i2c_clk(unsigned char enable, u32 i2c_num) +{ + /* Set parent to FIRC DIV2 clock */ + const u32 lpi2c_pcc_clks[] = { + LPI2C4_PCC3_SLOT << 8 | 3, + LPI2C5_PCC3_SLOT << 8 | 3, + LPI2C6_PCC4_SLOT << 8 | 4, + LPI2C7_PCC4_SLOT << 8 | 4, + }; + + if (i2c_num < 4 || i2c_num > 7) + return -EINVAL; + + if (enable) { + pcc_clock_enable(lpi2c_pcc_clks[i2c_num - 4] & 0xff, + lpi2c_pcc_clks[i2c_num - 4] >> 8, false); + pcc_clock_sel(lpi2c_pcc_clks[i2c_num - 4] & 0xff, + lpi2c_pcc_clks[i2c_num - 4] >> 8, SOSC_DIV2); + pcc_clock_enable(lpi2c_pcc_clks[i2c_num - 4] & 0xff, + lpi2c_pcc_clks[i2c_num - 4] >> 8, true); + pcc_reset_peripheral(lpi2c_pcc_clks[i2c_num - 4] & 0xff, + lpi2c_pcc_clks[i2c_num - 4] >> 8, false); + } else { + pcc_clock_enable(lpi2c_pcc_clks[i2c_num - 4] & 0xff, + lpi2c_pcc_clks[i2c_num - 4] >> 8, false); + } + return 0; +} + +u32 imx_get_i2cclk(u32 i2c_num) +{ + const u32 lpi2c_pcc_clks[] = { + LPI2C4_PCC3_SLOT << 8 | 3, + LPI2C5_PCC3_SLOT << 8 | 3, + LPI2C6_PCC4_SLOT << 8 | 4, + LPI2C7_PCC4_SLOT << 8 | 4, + }; + + if (i2c_num < 4 || i2c_num > 7) + return 0; + + return pcc_clock_get_rate(lpi2c_pcc_clks[i2c_num - 4] & 0xff, + lpi2c_pcc_clks[i2c_num - 4] >> 8); +} +#endif + +void enable_usboh3_clk(unsigned char enable) +{ + if (enable) { + pcc_clock_enable(4, USB0_PCC4_SLOT, true); + pcc_clock_enable(4, USBPHY_PCC4_SLOT, true); + pcc_reset_peripheral(4, USB0_PCC4_SLOT, false); + pcc_reset_peripheral(4, USBPHY_PCC4_SLOT, false); + +#ifdef CONFIG_USB_MAX_CONTROLLER_COUNT + if (CONFIG_USB_MAX_CONTROLLER_COUNT > 1) { + pcc_clock_enable(4, USB1_PCC4_SLOT, true); + pcc_clock_enable(4, USB1PHY_PCC4_SLOT, true); + pcc_reset_peripheral(4, USB1_PCC4_SLOT, false); + pcc_reset_peripheral(4, USB1PHY_PCC4_SLOT, false); + } +#endif + + pcc_clock_enable(4, USB_XBAR_PCC4_SLOT, true); + } else { + pcc_clock_enable(4, USB0_PCC4_SLOT, false); + pcc_clock_enable(4, USB1_PCC4_SLOT, false); + pcc_clock_enable(4, USBPHY_PCC4_SLOT, false); + pcc_clock_enable(4, USB1PHY_PCC4_SLOT, false); + pcc_clock_enable(4, USB_XBAR_PCC4_SLOT, false); + } +} + +int enable_usb_pll(ulong usb_phy_base) +{ + u32 sosc_rate; + s32 timeout = 1000000; + + struct usbphy_regs *usbphy = + (struct usbphy_regs *)usb_phy_base; + + sosc_rate = cgc1_sosc_div(SOSC); + if (!sosc_rate) + return -EPERM; + + 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); + + return 0; +} + +u32 mxc_get_clock(enum mxc_clock clk) +{ + switch (clk) { + case MXC_ESDHC_CLK: + return pcc_clock_get_rate(4, SDHC0_PCC4_SLOT); + case MXC_ESDHC2_CLK: + return pcc_clock_get_rate(4, SDHC1_PCC4_SLOT); + case MXC_ESDHC3_CLK: + return pcc_clock_get_rate(4, SDHC2_PCC4_SLOT); + case MXC_ARM_CLK: + return cgc1_clk_get_rate(PLL2); + default: + return 0; + } +} + +u32 get_lpuart_clk(void) +{ + int index = 0; + + const u32 lpuart_array[] = { + LPUART4_RBASE, + LPUART5_RBASE, + LPUART6_RBASE, + LPUART7_RBASE, + }; + + const u32 lpuart_pcc_slots[] = { + LPUART4_PCC3_SLOT, + LPUART5_PCC3_SLOT, + LPUART6_PCC4_SLOT, + LPUART7_PCC4_SLOT, + }; + + const u32 lpuart_pcc[] = { + 3, 3, 4, 4, + }; + + for (index = 0; index < 4; index++) { + if (lpuart_array[index] == LPUART_BASE) + break; + } + + if (index > 3) + return 0; + + return pcc_clock_get_rate(lpuart_pcc[index], lpuart_pcc_slots[index]); +} + +#ifndef CONFIG_SPL_BUILD +/* + * Dump some core clockes. + */ +int do_mx8ulp_showclocks(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[]) +{ + printf("SDHC0 %8d MHz\n", pcc_clock_get_rate(4, SDHC0_PCC4_SLOT) / 1000000); + printf("SDHC1 %8d MHz\n", pcc_clock_get_rate(4, SDHC1_PCC4_SLOT) / 1000000); + printf("SDHC2 %8d MHz\n", pcc_clock_get_rate(4, SDHC2_PCC4_SLOT) / 1000000); + + printf("SOSC %8d MHz\n", cgc1_clk_get_rate(SOSC) / 1000000); + printf("FRO %8d MHz\n", cgc1_clk_get_rate(FRO) / 1000000); + printf("PLL2 %8d MHz\n", cgc1_clk_get_rate(PLL2) / 1000000); + printf("PLL3 %8d MHz\n", cgc1_clk_get_rate(PLL3) / 1000000); + printf("PLL3_VCODIV %8d MHz\n", cgc1_clk_get_rate(PLL3_VCODIV) / 1000000); + printf("PLL3_PFD0 %8d MHz\n", cgc1_clk_get_rate(PLL3_PFD0) / 1000000); + printf("PLL3_PFD1 %8d MHz\n", cgc1_clk_get_rate(PLL3_PFD1) / 1000000); + printf("PLL3_PFD2 %8d MHz\n", cgc1_clk_get_rate(PLL3_PFD2) / 1000000); + printf("PLL3_PFD3 %8d MHz\n", cgc1_clk_get_rate(PLL3_PFD3) / 1000000); + + return 0; +} + +U_BOOT_CMD( + clocks, CONFIG_SYS_MAXARGS, 1, do_mx8ulp_showclocks, + "display clocks", + "" +); +#endif diff --git a/arch/arm/mach-imx/imx8ulp/iomux.c b/arch/arm/mach-imx/imx8ulp/iomux.c new file mode 100644 index 0000000000..c6d20f5468 --- /dev/null +++ b/arch/arm/mach-imx/imx8ulp/iomux.c @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2020-2021 NXP + */ + +#include <common.h> +#include <asm/io.h> +#include <asm/arch/imx-regs.h> +#include <asm/arch/iomux.h> + +static void *base = (void *)IOMUXC_BASE_ADDR; +static void *base_mports = (void *)(0x280A1000); + +/* + * configures a single pad in the iomuxer + */ +void imx8ulp_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; + + 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 imx8ulp_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++) { + imx8ulp_iomux_setup_pad(*p); + p++; + } +} diff --git a/arch/arm/mach-imx/imx8ulp/lowlevel_init.S b/arch/arm/mach-imx/imx8ulp/lowlevel_init.S new file mode 100644 index 0000000000..791c26407c --- /dev/null +++ b/arch/arm/mach-imx/imx8ulp/lowlevel_init.S @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2020 NXP + */ + +#include <config.h> + +.align 8 +.global rom_pointer +rom_pointer: + .space 256 + +/* + * Routine: save_boot_params (called after reset from start.S) + */ + +.global save_boot_params +save_boot_params: +#ifndef CONFIG_SPL_BUILD + /* 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 +#endif + /* Returns */ + b save_boot_params_ret diff --git a/arch/arm/mach-imx/imx8ulp/pcc.c b/arch/arm/mach-imx/imx8ulp/pcc.c new file mode 100644 index 0000000000..711b685cd7 --- /dev/null +++ b/arch/arm/mach-imx/imx8ulp/pcc.c @@ -0,0 +1,449 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2021 NXP + */ + +#include <common.h> +#include <div64.h> +#include <asm/io.h> +#include <errno.h> +#include <asm/arch/imx-regs.h> +#include <asm/arch/pcc.h> +#include <asm/arch/cgc.h> +#include <asm/arch/sys_proto.h> + +#define cgc1_clk_TYPES 2 +#define cgc1_clk_NUM 8 + +static enum cgc1_clk pcc3_clksrc[][8] = { + { + }, + { DUMMY0_CLK, + LPOSC, + SOSC_DIV2, + FRO_DIV2, + XBAR_BUSCLK, + PLL3_PFD1_DIV1, + PLL3_PFD0_DIV2, + PLL3_PFD0_DIV1 + } +}; + +static enum cgc1_clk pcc4_clksrc[][8] = { + { + DUMMY0_CLK, + SOSC_DIV1, + FRO_DIV1, + PLL3_PFD3_DIV2, + PLL3_PFD3_DIV1, + PLL3_PFD2_DIV2, + PLL3_PFD2_DIV1, + PLL3_PFD1_DIV2 + }, + { + DUMMY0_CLK, + DUMMY1_CLK, + LPOSC, + SOSC_DIV2, + FRO_DIV2, + XBAR_BUSCLK, + PLL3_VCODIV, + PLL3_PFD0_DIV1 + } +}; + +static struct pcc_entry pcc3_arrays[] = { + {PCC3_RBASE, DMA1_MP_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, + {PCC3_RBASE, DMA1_CH0_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, + {PCC3_RBASE, DMA1_CH1_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, + {PCC3_RBASE, DMA1_CH2_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, + {PCC3_RBASE, DMA1_CH3_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, + {PCC3_RBASE, DMA1_CH4_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, + {PCC3_RBASE, DMA1_CH5_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, + {PCC3_RBASE, DMA1_CH6_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, + {PCC3_RBASE, DMA1_CH7_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, + {PCC3_RBASE, DMA1_CH8_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, + {PCC3_RBASE, DMA1_CH9_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, + {PCC3_RBASE, DMA1_CH10_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, + {PCC3_RBASE, DMA1_CH11_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, + {PCC3_RBASE, DMA1_CH12_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, + {PCC3_RBASE, DMA1_CH13_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, + {PCC3_RBASE, DMA1_CH14_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, + {PCC3_RBASE, DMA1_CH15_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, + {PCC3_RBASE, DMA1_CH16_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, + {PCC3_RBASE, DMA1_CH17_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, + {PCC3_RBASE, DMA1_CH18_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, + {PCC3_RBASE, DMA1_CH19_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, + {PCC3_RBASE, DMA1_CH20_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, + {PCC3_RBASE, DMA1_CH21_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, + {PCC3_RBASE, DMA1_CH22_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, + {PCC3_RBASE, DMA1_CH23_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, + {PCC3_RBASE, DMA1_CH24_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, + {PCC3_RBASE, DMA1_CH25_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, + {PCC3_RBASE, DMA1_CH26_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, + {PCC3_RBASE, DMA1_CH27_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, + {PCC3_RBASE, DMA1_CH28_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, + {PCC3_RBASE, DMA1_CH29_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, + {PCC3_RBASE, DMA1_CH30_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, + {PCC3_RBASE, DMA1_CH31_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, + {PCC3_RBASE, MU0_B_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, + {PCC3_RBASE, MU3_A_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, + {PCC3_RBASE, LLWU1_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, + {PCC3_RBASE, UPOWER_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, + {PCC3_RBASE, WDOG3_PCC3_SLOT, CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B}, + {PCC3_RBASE, WDOG4_PCC3_SLOT, CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B}, + {PCC3_RBASE, XRDC_MGR_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, + {PCC3_RBASE, SEMA42_1_PCC3_SLOT, CLKSRC_PER_BUS, PCC_NO_DIV, PCC_NO_RST_B}, + {PCC3_RBASE, ROMCP1_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, + {PCC3_RBASE, LPIT1_PCC3_SLOT, CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B}, + {PCC3_RBASE, TPM4_PCC3_SLOT, CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B}, + {PCC3_RBASE, TPM5_PCC3_SLOT, CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B}, + {PCC3_RBASE, FLEXIO1_PCC3_SLOT, CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B}, + {PCC3_RBASE, I3C2_PCC3_SLOT, CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B}, + {PCC3_RBASE, LPI2C4_PCC3_SLOT, CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B}, + {PCC3_RBASE, LPI2C5_PCC3_SLOT, CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B}, + {PCC3_RBASE, LPUART4_PCC3_SLOT, CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B}, + {PCC3_RBASE, LPUART5_PCC3_SLOT, CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B}, + {PCC3_RBASE, LPSPI4_PCC3_SLOT, CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B}, + {PCC3_RBASE, LPSPI5_PCC3_SLOT, CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B}, + {} +}; + +static struct pcc_entry pcc4_arrays[] = { + {PCC4_RBASE, FLEXSPI2_PCC4_SLOT, CLKSRC_PER_PLAT, PCC_HAS_DIV, PCC_HAS_RST_B }, + {PCC4_RBASE, TPM6_PCC4_SLOT, CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B }, + {PCC4_RBASE, TPM7_PCC4_SLOT, CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B }, + {PCC4_RBASE, LPI2C6_PCC4_SLOT, CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B }, + {PCC4_RBASE, LPI2C7_PCC4_SLOT, CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B }, + {PCC4_RBASE, LPUART6_PCC4_SLOT, CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B }, + {PCC4_RBASE, LPUART7_PCC4_SLOT, CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B }, + {PCC4_RBASE, SAI4_PCC4_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_HAS_RST_B }, + {PCC4_RBASE, SAI5_PCC4_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_HAS_RST_B }, + {PCC4_RBASE, PCTLE_PCC4_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B }, + {PCC4_RBASE, PCTLF_PCC4_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B }, + {PCC4_RBASE, SDHC0_PCC4_SLOT, CLKSRC_PER_PLAT, PCC_HAS_DIV, PCC_HAS_RST_B }, + {PCC4_RBASE, SDHC1_PCC4_SLOT, CLKSRC_PER_PLAT, PCC_HAS_DIV, PCC_HAS_RST_B }, + {PCC4_RBASE, SDHC2_PCC4_SLOT, CLKSRC_PER_PLAT, PCC_HAS_DIV, PCC_HAS_RST_B }, + {PCC4_RBASE, USB0_PCC4_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_HAS_RST_B }, + {PCC4_RBASE, USBPHY_PCC4_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_HAS_RST_B }, + {PCC4_RBASE, USB1_PCC4_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_HAS_RST_B }, + {PCC4_RBASE, USB1PHY_PCC4_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_HAS_RST_B }, + {PCC4_RBASE, USB_XBAR_PCC4_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B }, + {PCC4_RBASE, ENET_PCC4_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_HAS_RST_B }, + {PCC4_RBASE, SFA1_PCC4_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B }, + {PCC4_RBASE, RGPIOE_PCC4_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B }, + {PCC4_RBASE, RGPIOF_PCC4_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B }, + {} +}; + +static int find_pcc_entry(int pcc_controller, int pcc_clk_slot, struct pcc_entry **out) +{ + struct pcc_entry *pcc_array; + int index = 0; + + switch (pcc_controller) { + case 3: + pcc_array = pcc3_arrays; + *out = &pcc3_arrays[0]; + break; + case 4: + pcc_array = pcc4_arrays; + *out = &pcc4_arrays[0]; + break; + default: + printf("Not supported pcc_controller: %d\n", pcc_controller); + return -EINVAL; + } + + while (pcc_array->pcc_base) { + if (pcc_array->pcc_slot == pcc_clk_slot) + return index; + + pcc_array++; + index++; + } + + return -ENOENT; +} + +int pcc_clock_enable(int pcc_controller, int pcc_clk_slot, bool enable) +{ + u32 val; + void __iomem *reg; + int clk; + struct pcc_entry *pcc_array; + + clk = find_pcc_entry(pcc_controller, pcc_clk_slot, &pcc_array); + if (clk < 0) + return -EINVAL; + + reg = (void __iomem *)(uintptr_t)(pcc_array[clk].pcc_base + pcc_array[clk].pcc_slot * 4); + + val = readl(reg); + + debug("%s: clk %d, reg 0x%p, val 0x%x, enable %d\n", __func__, 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); + + debug("%s: val 0x%x\n", __func__, val); + + return 0; +} + +/* The clock source select needs clock is disabled */ +int pcc_clock_sel(int pcc_controller, int pcc_clk_slot, enum cgc1_clk src) +{ + u32 val, i, clksrc_type; + void __iomem *reg; + struct pcc_entry *pcc_array; + enum cgc1_clk *cgc1_clk_array; + int clk; + + clk = find_pcc_entry(pcc_controller, pcc_clk_slot, &pcc_array); + if (clk < 0) + return -EINVAL; + + reg = (void __iomem *)(uintptr_t)(pcc_array[clk].pcc_base + pcc_array[clk].pcc_slot * 4); + + clksrc_type = pcc_array[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; + } + + if (pcc_controller == 3) + cgc1_clk_array = pcc3_clksrc[clksrc_type]; + else + cgc1_clk_array = pcc4_clksrc[clksrc_type]; + + for (i = 0; i < cgc1_clk_NUM; i++) { + if (cgc1_clk_array[i] == src) { + /* Find the clock src, then set it to PCS */ + break; + } + } + + if (i == cgc1_clk_NUM) { + printf("No parent in PCS of PCC %d, invalid scg_clk %d\n", clk, src); + return -EINVAL; + } + + val = readl(reg); + + debug("%s: clk %d, reg 0x%p, val 0x%x, clksrc_type %d\n", + __func__, 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 << PCC_PCS_OFFSET; + + writel(val, reg); + + debug("%s: val 0x%x\n", __func__, val); + + return 0; +} + +int pcc_clock_div_config(int pcc_controller, int pcc_clk_slot, bool frac, u8 div) +{ + u32 val; + void __iomem *reg; + struct pcc_entry *pcc_array; + int clk; + + clk = find_pcc_entry(pcc_controller, pcc_clk_slot, &pcc_array); + if (clk < 0) + return -EINVAL; + + reg = (void __iomem *)(uintptr_t)(pcc_array[clk].pcc_base + pcc_array[clk].pcc_slot * 4); + + if (div > 8 || (div == 1 && frac != 0)) + return -EINVAL; + + if (pcc_array[clk].div >= PCC_NO_DIV) { + printf("No DIV/FRAC field for the PCC %d\n", clk); + return -EPERM; + } + + 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(int pcc_controller, int pcc_clk_slot) +{ + u32 val; + void __iomem *reg; + struct pcc_entry *pcc_array; + int clk; + + clk = find_pcc_entry(pcc_controller, pcc_clk_slot, &pcc_array); + if (clk < 0) + return -EINVAL; + + reg = (void __iomem *)(uintptr_t)(pcc_array[clk].pcc_base + pcc_array[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(int pcc_controller, int pcc_clk_slot, enum cgc1_clk *src) +{ + u32 val, clksrc_type; + void __iomem *reg; + struct pcc_entry *pcc_array; + int clk; + enum cgc1_clk *cgc1_clk_array; + + clk = find_pcc_entry(pcc_controller, pcc_clk_slot, &pcc_array); + if (clk < 0) + return -EINVAL; + + clksrc_type = pcc_array[clk].clksrc; + if (clksrc_type >= CLKSRC_NO_PCS) { + printf("No PCS field for the PCC %d, clksrc type %d\n", + pcc_clk_slot, clksrc_type); + return -EPERM; + } + + reg = (void __iomem *)(uintptr_t)(pcc_array[clk].pcc_base + pcc_array[clk].pcc_slot * 4); + + val = readl(reg); + + debug("%s: clk %d, reg 0x%p, val 0x%x, type %d\n", + __func__, pcc_clk_slot, 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; + } + + if (pcc_controller == 3) + cgc1_clk_array = pcc3_clksrc[clksrc_type]; + else + cgc1_clk_array = pcc4_clksrc[clksrc_type]; + + *src = cgc1_clk_array[val]; + + debug("%s: parent cgc1 clk %d\n", __func__, *src); + + return 0; +} + +int pcc_reset_peripheral(int pcc_controller, int pcc_clk_slot, bool reset) +{ + u32 val; + void __iomem *reg; + struct pcc_entry *pcc_array; + int clk; + + clk = find_pcc_entry(pcc_controller, pcc_clk_slot, &pcc_array); + if (clk < 0) + return -EINVAL; + + if (pcc_array[clk].rst_b == PCC_NO_RST_B) + return 0; + + reg = (void __iomem *)(uintptr_t)(pcc_array[clk].pcc_base + pcc_array[clk].pcc_slot * 4); + + val = readl(reg); + + debug("%s: clk %d, reg 0x%p, val 0x%x\n", __func__, pcc_clk_slot, reg, val); + + if (!(val & PCC_PR_MASK)) { + printf("This pcc slot is not present = 0x%x\n", val); + return -EPERM; + } + + if (reset) + val &= ~BIT(28); + else + val |= BIT(28); + + writel(val, reg); + + debug("%s: clk %d, reg 0x%p, val 0x%x\n", __func__, pcc_clk_slot, reg, val); + + return 0; +} + +u32 pcc_clock_get_rate(int pcc_controller, int pcc_clk_slot) +{ + u32 val, rate, frac, div; + void __iomem *reg; + enum cgc1_clk parent; + int ret; + int clk; + struct pcc_entry *pcc_array; + + clk = find_pcc_entry(pcc_controller, pcc_clk_slot, &pcc_array); + if (clk < 0) + return -EINVAL; + + ret = pcc_clock_get_clksrc(pcc_controller, pcc_clk_slot, &parent); + if (ret) + return 0; + + rate = cgc1_clk_get_rate(parent); + + debug("%s: parent rate %u\n", __func__, rate); + + if (pcc_array[clk].div == PCC_HAS_DIV) { + reg = (void __iomem *)(uintptr_t)(pcc_array[clk].pcc_base + + pcc_array[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); + } + + debug("%s: rate %u\n", __func__, rate); + return rate; +} diff --git a/arch/arm/mach-imx/imx8ulp/rdc.c b/arch/arm/mach-imx/imx8ulp/rdc.c new file mode 100644 index 0000000000..e2eca0633e --- /dev/null +++ b/arch/arm/mach-imx/imx8ulp/rdc.c @@ -0,0 +1,411 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2021 NXP + */ + +#include <common.h> +#include <asm/io.h> +#include <asm/types.h> +#include <asm/arch/imx-regs.h> +#include <asm/arch/sys_proto.h> +#include <asm/arch/mu_hal.h> +#include <asm/arch/s400_api.h> +#include <asm/arch/rdc.h> +#include <div64.h> + +#define XRDC_ADDR 0x292f0000 +#define MRC_OFFSET 0x2000 +#define MRC_STEP 0x200 + +#define SP(X) ((X) << 9) +#define SU(X) ((X) << 6) +#define NP(X) ((X) << 3) +#define NU(X) ((X) << 0) + +#define RWX 7 +#define RW 6 +#define R 4 +#define X 1 + +#define D7SEL_CODE (SP(RW) | SU(RW) | NP(RWX) | NU(RWX)) +#define D6SEL_CODE (SP(RW) | SU(RW) | NP(RWX)) +#define D5SEL_CODE (SP(RW) | SU(RWX)) +#define D4SEL_CODE SP(RWX) +#define D3SEL_CODE (SP(X) | SU(X) | NP(X) | NU(X)) +#define D0SEL_CODE 0 + +#define D7SEL_DAT (SP(RW) | SU(RW) | NP(RW) | NU(RW)) +#define D6SEL_DAT (SP(RW) | SU(RW) | NP(RW)) +#define D5SEL_DAT (SP(RW) | SU(RW) | NP(R) | NU(R)) +#define D4SEL_DAT (SP(RW) | SU(RW)) +#define D3SEL_DAT SP(RW) + +struct mbc_mem_dom { + u32 mem_glbcfg[4]; + u32 nse_blk_index; + u32 nse_blk_set; + u32 nse_blk_clr; + u32 nsr_blk_clr_all; + u32 memn_glbac[8]; + /* The upper only existed in the beginning of each MBC */ + u32 mem0_blk_cfg_w[64]; + u32 mem0_blk_nse_w[16]; + u32 mem1_blk_cfg_w[8]; + u32 mem1_blk_nse_w[2]; + u32 mem2_blk_cfg_w[8]; + u32 mem2_blk_nse_w[2]; + u32 mem3_blk_cfg_w[8]; + u32 mem3_blk_nse_w[2];/*0x1F0, 0x1F4 */ + u32 reserved[2]; +}; + +struct mrc_rgn_dom { + u32 mrc_glbcfg[4]; + u32 nse_rgn_indirect; + u32 nse_rgn_set; + u32 nse_rgn_clr; + u32 nse_rgn_clr_all; + u32 memn_glbac[8]; + /* The upper only existed in the beginning of each MRC */ + u32 rgn_desc_words[8][2]; /* 8 regions, 2 words per region */ + u32 reserved[16]; + u32 rgn_nse; + u32 reserved2[15]; +}; + +struct trdc { + u8 res0[0x1000]; + struct mbc_mem_dom mem_dom[4][8]; + struct mrc_rgn_dom mrc_dom[2][8]; +}; + +union dxsel_perm { + struct { + u8 dx; + u8 perm; + }; + + u32 dom_perm; +}; + +int xrdc_config_mrc_dx_perm(u32 mrc_con, u32 region, u32 dom, u32 dxsel) +{ + ulong w2_addr; + u32 val = 0; + + w2_addr = XRDC_ADDR + MRC_OFFSET + mrc_con * 0x200 + region * 0x20 + 0x8; + + val = (readl(w2_addr) & (~(7 << (3 * dom)))) | (dxsel << (3 * dom)); + writel(val, w2_addr); + + return 0; +} + +int xrdc_config_mrc_w0_w1(u32 mrc_con, u32 region, u32 w0, u32 size) +{ + ulong w0_addr, w1_addr; + + w0_addr = XRDC_ADDR + MRC_OFFSET + mrc_con * 0x200 + region * 0x20; + w1_addr = w0_addr + 4; + + if ((size % 32) != 0) + return -EINVAL; + + writel(w0 & ~0x1f, w0_addr); + writel(w0 + size - 1, w1_addr); + + return 0; +} + +int xrdc_config_mrc_w3_w4(u32 mrc_con, u32 region, u32 w3, u32 w4) +{ + ulong w3_addr = XRDC_ADDR + MRC_OFFSET + mrc_con * 0x200 + region * 0x20 + 0xC; + ulong w4_addr = w3_addr + 4; + + writel(w3, w3_addr); + writel(w4, w4_addr); + + return 0; +} + +int xrdc_config_pdac_openacc(u32 bridge, u32 index) +{ + ulong w0_addr; + u32 val; + + switch (bridge) { + case 3: + w0_addr = XRDC_ADDR + 0x1000 + 0x8 * index; + break; + case 4: + w0_addr = XRDC_ADDR + 0x1400 + 0x8 * index; + break; + case 5: + w0_addr = XRDC_ADDR + 0x1800 + 0x8 * index; + break; + default: + return -EINVAL; + } + writel(0xffffff, w0_addr); + + val = readl(w0_addr + 4); + writel(val | BIT(31), w0_addr + 4); + + return 0; +} + +int xrdc_config_pdac(u32 bridge, u32 index, u32 dom, u32 perm) +{ + ulong w0_addr; + u32 val; + + switch (bridge) { + case 3: + w0_addr = XRDC_ADDR + 0x1000 + 0x8 * index; + break; + case 4: + w0_addr = XRDC_ADDR + 0x1400 + 0x8 * index; + break; + case 5: + w0_addr = XRDC_ADDR + 0x1800 + 0x8 * index; + break; + default: + return -EINVAL; + } + val = readl(w0_addr); + writel((val & ~(0x7 << (dom * 3))) | (perm << (dom * 3)), w0_addr); + + val = readl(w0_addr + 4); + writel(val | BIT(31), w0_addr + 4); + + return 0; +} + +int release_rdc(enum rdc_type type) +{ + ulong s_mu_base = 0x27020000UL; + struct imx8ulp_s400_msg msg; + int ret; + u32 rdc_id = (type == RDC_XRDC) ? 0x78 : 0x74; + + msg.version = AHAB_VERSION; + msg.tag = AHAB_CMD_TAG; + msg.size = 2; + msg.command = AHAB_RELEASE_RDC_REQ_CID; + msg.data[0] = (rdc_id << 8) | 0x2; /* A35 XRDC */ + + mu_hal_init(s_mu_base); + mu_hal_sendmsg(s_mu_base, 0, *((u32 *)&msg)); + mu_hal_sendmsg(s_mu_base, 1, msg.data[0]); + + ret = mu_hal_receivemsg(s_mu_base, 0, (u32 *)&msg); + if (!ret) { + ret = mu_hal_receivemsg(s_mu_base, 1, &msg.data[0]); + if (!ret) { + if ((msg.data[0] & 0xff) == 0xd6) + return 0; + } + + return -EIO; + } + + return ret; +} + +void xrdc_mrc_region_set_access(int mrc_index, u32 addr, u32 access) +{ + ulong xrdc_base = 0x292f0000, off; + u32 mrgd[5]; + u8 mrcfg, j, region_num; + u8 dsel; + + mrcfg = readb(xrdc_base + 0x140 + mrc_index); + region_num = mrcfg & 0x1f; + + for (j = 0; j < region_num; j++) { + off = 0x2000 + mrc_index * 0x200 + j * 0x20; + + mrgd[0] = readl(xrdc_base + off); + mrgd[1] = readl(xrdc_base + off + 4); + mrgd[2] = readl(xrdc_base + off + 8); + mrgd[3] = readl(xrdc_base + off + 0xc); + mrgd[4] = readl(xrdc_base + off + 0x10); + + debug("MRC [%u][%u]\n", mrc_index, j); + debug("0x%x, 0x%x, 0x%x, 0x%x, 0x%x\n", + mrgd[0], mrgd[1], mrgd[2], mrgd[3], mrgd[4]); + + /* hit */ + if (addr >= mrgd[0] && addr <= mrgd[1]) { + /* find domain 7 DSEL */ + dsel = (mrgd[2] >> 21) & 0x7; + if (dsel == 1) { + mrgd[4] &= ~0xFFF; + mrgd[4] |= (access & 0xFFF); + } else if (dsel == 2) { + mrgd[4] &= ~0xFFF0000; + mrgd[4] |= ((access & 0xFFF) << 16); + } + + /* not handle other cases, since S400 only set ACCESS1 and 2 */ + writel(mrgd[4], xrdc_base + off + 0x10); + return; + } + } +} + +void xrdc_init_mda(void) +{ + ulong xrdc_base = XRDC_ADDR, off; + u32 i = 0; + + /* Set MDA3-5 for PXP, ENET, CAAM to DID 1*/ + for (i = 3; i <= 5; i++) { + off = 0x800 + i * 0x20; + writel(0x200000A1, xrdc_base + off); + writel(0xA00000A1, xrdc_base + off); + } + + /* Set MDA10 -15 to DID 3 for video */ + for (i = 10; i <= 15; i++) { + off = 0x800 + i * 0x20; + writel(0x200000A3, xrdc_base + off); + writel(0xA00000A3, xrdc_base + off); + } +} + +void xrdc_init_mrc(void) +{ + /* The MRC8 is for SRAM1 */ + xrdc_config_mrc_w0_w1(8, 0, 0x21000000, 0x10000); + /* Allow for all domains: So domain 2/3 (HIFI DSP/LPAV) is ok to access */ + xrdc_config_mrc_dx_perm(8, 0, 0, 1); + xrdc_config_mrc_dx_perm(8, 0, 1, 1); + xrdc_config_mrc_dx_perm(8, 0, 2, 1); + xrdc_config_mrc_dx_perm(8, 0, 3, 1); + xrdc_config_mrc_dx_perm(8, 0, 4, 1); + xrdc_config_mrc_dx_perm(8, 0, 5, 1); + xrdc_config_mrc_dx_perm(8, 0, 6, 1); + xrdc_config_mrc_dx_perm(8, 0, 7, 1); + xrdc_config_mrc_w3_w4(8, 0, 0x0, 0x80000FFF); + + /* The MRC6 is for video modules to ddr */ + xrdc_config_mrc_w0_w1(6, 0, 0x80000000, 0x80000000); + xrdc_config_mrc_dx_perm(6, 0, 3, 1); /* allow for domain 3 video */ + xrdc_config_mrc_w3_w4(6, 0, 0x0, 0x80000FFF); +} + +int trdc_mbc_set_access(u32 mbc_x, u32 dom_x, u32 mem_x, u32 blk_x, bool sec_access) +{ + struct trdc *trdc_base = (struct trdc *)0x28031000U; + struct mbc_mem_dom *mbc_dom; + u32 *cfg_w, *nse_w; + u32 index, offset, val; + + mbc_dom = &trdc_base->mem_dom[mbc_x][dom_x]; + + switch (mem_x) { + case 0: + cfg_w = &mbc_dom->mem0_blk_cfg_w[blk_x / 8]; + nse_w = &mbc_dom->mem0_blk_nse_w[blk_x / 32]; + break; + case 1: + cfg_w = &mbc_dom->mem1_blk_cfg_w[blk_x / 8]; + nse_w = &mbc_dom->mem1_blk_nse_w[blk_x / 32]; + break; + case 2: + cfg_w = &mbc_dom->mem2_blk_cfg_w[blk_x / 8]; + nse_w = &mbc_dom->mem2_blk_nse_w[blk_x / 32]; + break; + case 3: + cfg_w = &mbc_dom->mem3_blk_cfg_w[blk_x / 8]; + nse_w = &mbc_dom->mem3_blk_nse_w[blk_x / 32]; + break; + default: + return -EINVAL; + }; + + index = blk_x % 8; + offset = index * 4; + + val = readl((void __iomem *)cfg_w); + + val &= ~(0xFU << offset); + + /* MBC0-3 + * Global 0, 0x7777 secure pri/user read/write/execute, S400 has already set it. + * So select MBC0_MEMN_GLBAC0 + */ + if (sec_access) { + val |= (0x0 << offset); + writel(val, (void __iomem *)cfg_w); + } else { + val |= (0x8 << offset); /* nse bit set */ + writel(val, (void __iomem *)cfg_w); + } + + return 0; +} + +int trdc_mrc_region_set_access(u32 mrc_x, u32 dom_x, u32 addr_start, u32 addr_end, bool sec_access) +{ + struct trdc *trdc_base = (struct trdc *)0x28031000U; + struct mrc_rgn_dom *mrc_dom; + u32 *desc_w; + u32 start, end; + u32 i, free = 8; + bool vld, hit = false; + + mrc_dom = &trdc_base->mrc_dom[mrc_x][dom_x]; + + for (i = 0; i < 8; i++) { + desc_w = &mrc_dom->rgn_desc_words[i][0]; + + start = readl((void __iomem *)desc_w) & 0xfff; + end = readl((void __iomem *)(desc_w + 1)); + vld = end & 0x1; + end = end & 0xfff; + + if (start == 0 && end == 0 && !vld && free >= 8) + free = i; + + /* Check all the region descriptors, even overlap */ + if (addr_start >= end || addr_end <= start || !vld) + continue; + + /* MRC0,1 + * Global 0, 0x7777 secure pri/user read/write/execute, S400 has already set it. + * So select MRCx_MEMN_GLBAC0 + */ + if (sec_access) { + writel(start, (void __iomem *)desc_w); + writel(end | 0x1, (void __iomem *)(desc_w + 1)); + } else { + writel(start, (void __iomem *)desc_w); + writel((end | 0x1 | 0x10), (void __iomem *)(desc_w + 1)); + } + + if (addr_start >= start && addr_end <= end) + hit = true; + } + + if (!hit) { + if (free >= 8) + return -EFAULT; + + desc_w = &mrc_dom->rgn_desc_words[free][0]; + + addr_start &= ~0xfff; + addr_end &= ~0xfff; + + if (sec_access) { + writel(addr_start, (void __iomem *)desc_w); + writel(addr_end | 0x1, (void __iomem *)(desc_w + 1)); + } else { + writel(addr_start, (void __iomem *)desc_w); + writel((addr_end | 0x1 | 0x10), (void __iomem *)(desc_w + 1)); + } + } + + return 0; +} diff --git a/arch/arm/mach-imx/imx8ulp/soc.c b/arch/arm/mach-imx/imx8ulp/soc.c new file mode 100644 index 0000000000..1c33acc7dd --- /dev/null +++ b/arch/arm/mach-imx/imx8ulp/soc.c @@ -0,0 +1,545 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2021 NXP + */ + +#include <asm/io.h> +#include <asm/arch/clock.h> +#include <asm/arch/imx-regs.h> +#include <asm/arch/sys_proto.h> +#include <asm/armv8/mmu.h> +#include <asm/mach-imx/boot_mode.h> +#include <asm/global_data.h> +#include <efi_loader.h> +#include <spl.h> +#include <asm/arch/rdc.h> +#include <asm/arch/s400_api.h> +#include <asm/arch/mu_hal.h> +#include <cpu_func.h> +#include <asm/setup.h> +#include <dm.h> +#include <dm/device-internal.h> +#include <dm/lists.h> +#include <dm/uclass.h> +#include <dm/device.h> +#include <dm/uclass-internal.h> + +DECLARE_GLOBAL_DATA_PTR; + +struct rom_api *g_rom_api = (struct rom_api *)0x1980; + +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; +} + +bool is_usb_boot(void) +{ + return get_boot_device() == USB_BOOT; +} + +#ifdef CONFIG_ENV_IS_IN_MMC +__weak int board_mmc_get_env_dev(int devno) +{ + return devno; +} + +int mmc_get_env_dev(void) +{ + volatile gd_t *pgd = gd; + int ret; + u32 boot; + u16 boot_type; + u8 boot_instance; + + 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 CONFIG_SYS_MMC_ENV_DEV; + } + + boot_type = boot >> 16; + boot_instance = (boot >> 8) & 0xff; + + /* If not boot from sd/mmc, use default value */ + if (boot_type != BOOT_TYPE_SD && boot_type != BOOT_TYPE_MMC) + return env_get_ulong("mmcdev", 10, CONFIG_SYS_MMC_ENV_DEV); + + return board_mmc_get_env_dev(boot_instance); +} +#endif + +u32 get_cpu_rev(void) +{ + return (MXC_CPU_IMX8ULP << 12) | CHIP_REV_1_0; +} + +enum bt_mode get_boot_mode(void) +{ + u32 bt0_cfg = 0; + + bt0_cfg = readl(SIM_SEC_BASE_ADDR + 0x24); + 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; +} + +#define CMC_SRS_TAMPER BIT(31) +#define CMC_SRS_SECURITY BIT(30) +#define CMC_SRS_TZWDG BIT(29) +#define CMC_SRS_JTAG_RST BIT(28) +#define CMC_SRS_CORE1 BIT(16) +#define CMC_SRS_LOCKUP BIT(15) +#define CMC_SRS_SW BIT(14) +#define CMC_SRS_WDG BIT(13) +#define CMC_SRS_PIN_RESET BIT(8) +#define CMC_SRS_WARM BIT(4) +#define CMC_SRS_HVD BIT(3) +#define CMC_SRS_LVD BIT(2) +#define CMC_SRS_POR BIT(1) +#define CMC_SRS_WUP BIT(0) + +static char *get_reset_cause(char *ret) +{ + u32 cause1, cause = 0, srs = 0; + void __iomem *reg_ssrs = (void __iomem *)(CMC1_BASE_ADDR + 0x88); + void __iomem *reg_srs = (void __iomem *)(CMC1_BASE_ADDR + 0x80); + + if (!ret) + return "null"; + + srs = readl(reg_srs); + cause1 = readl(reg_ssrs); + + cause = srs & (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 = srs & (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", srs); + break; + } + + debug("[%X] SRS[%X] %X - ", cause1, srs, srs ^ cause1); + return ret; +} + +#if defined(CONFIG_DISPLAY_CPUINFO) +const char *get_imx_type(u32 imxtype) +{ + return "8ULP"; +} + +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; + } + + 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(void __iomem *wdog_base) +{ + u32 val_cs = readl(wdog_base + 0x00); + + if (!(val_cs & 0x80)) + return; + + dmb(); + __raw_writel(REFRESH_WORD0, (wdog_base + 0x04)); /* Refresh the CNT */ + __raw_writel(REFRESH_WORD1, (wdog_base + 0x04)); + dmb(); + + if (!(val_cs & 800)) { + dmb(); + __raw_writel(UNLOCK_WORD0, (wdog_base + 0x04)); + __raw_writel(UNLOCK_WORD1, (wdog_base + 0x04)); + dmb(); + + while (!(readl(wdog_base + 0x00) & 0x800)) + ; + } + 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 */ + + while (!(readl(wdog_base + 0x00) & 0x400)) + ; +} + +void init_wdog(void) +{ + disable_wdog((void __iomem *)WDG3_RBASE); +} + +static struct mm_region imx8ulp_arm64_mem_map[] = { + { + /* ROM */ + .virt = 0x0, + .phys = 0x0, + .size = 0x40000UL, + .attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) | + PTE_BLOCK_OUTER_SHARE + }, + { + /* FLEXSPI0 */ + .virt = 0x04000000, + .phys = 0x04000000, + .size = 0x08000000UL, + .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | + PTE_BLOCK_NON_SHARE | + PTE_BLOCK_PXN | PTE_BLOCK_UXN + }, + { + /* SSRAM (align with 2M) */ + .virt = 0x1FE00000UL, + .phys = 0x1FE00000UL, + .size = 0x400000UL, + .attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) | + PTE_BLOCK_OUTER_SHARE | + PTE_BLOCK_PXN | PTE_BLOCK_UXN + }, { + /* SRAM1 (align with 2M) */ + .virt = 0x21000000UL, + .phys = 0x21000000UL, + .size = 0x200000UL, + .attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) | + PTE_BLOCK_OUTER_SHARE | + PTE_BLOCK_PXN | PTE_BLOCK_UXN + }, { + /* SRAM0 (align with 2M) */ + .virt = 0x22000000UL, + .phys = 0x22000000UL, + .size = 0x200000UL, + .attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) | + PTE_BLOCK_OUTER_SHARE | + PTE_BLOCK_PXN | PTE_BLOCK_UXN + }, { + /* Peripherals */ + .virt = 0x27000000UL, + .phys = 0x27000000UL, + .size = 0x3000000UL, + .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | + PTE_BLOCK_NON_SHARE | + PTE_BLOCK_PXN | PTE_BLOCK_UXN + }, { + /* Peripherals */ + .virt = 0x2D000000UL, + .phys = 0x2D000000UL, + .size = 0x1600000UL, + .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | + PTE_BLOCK_NON_SHARE | + PTE_BLOCK_PXN | PTE_BLOCK_UXN + }, { + /* FLEXSPI1-2 */ + .virt = 0x40000000UL, + .phys = 0x40000000UL, + .size = 0x40000000UL, + .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | + PTE_BLOCK_NON_SHARE | + PTE_BLOCK_PXN | PTE_BLOCK_UXN + }, { + /* DRAM1 */ + .virt = 0x80000000UL, + .phys = 0x80000000UL, + .size = PHYS_SDRAM_SIZE, + .attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) | + PTE_BLOCK_OUTER_SHARE + }, { + /* + * empty entrie to split table entry 5 + * if needed when TEEs are used + */ + 0, + }, { + /* List terminator */ + 0, + } +}; + +struct mm_region *mem_map = imx8ulp_arm64_mem_map; + +/* simplify the page table size to enhance boot speed */ +#define MAX_PTE_ENTRIES 512 +#define MAX_MEM_MAP_REGIONS 16 +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; +} + +void enable_caches(void) +{ + /* TODO: add TEE memmap region */ + + icache_enable(); + dcache_enable(); +} + +int dram_init(void) +{ + gd->ram_size = PHYS_SDRAM_SIZE; + + return 0; +} + +#ifdef CONFIG_SERIAL_TAG +void get_board_serial(struct tag_serialnr *serialnr) +{ + u32 uid[4]; + u32 res; + int ret; + + ret = ahab_read_common_fuse(1, uid, 4, &res); + if (ret) + printf("ahab read fuse failed %d, 0x%x\n", ret, res); + else + printf("UID 0x%x,0x%x,0x%x,0x%x\n", uid[0], uid[1], uid[2], uid[3]); + + serialnr->low = uid[0]; + serialnr->high = uid[3]; +} +#endif + +static void set_core0_reset_vector(u32 entry) +{ + /* Update SIM1 DGO8 for reset vector base */ + writel(entry, SIM1_BASE_ADDR + 0x5c); + + /* set update bit */ + setbits_le32(SIM1_BASE_ADDR + 0x8, 0x1 << 24); + + /* polling the ack */ + while ((readl(SIM1_BASE_ADDR + 0x8) & (0x1 << 26)) == 0) + ; + + /* clear the update */ + clrbits_le32(SIM1_BASE_ADDR + 0x8, (0x1 << 24)); + + /* clear the ack by set 1 */ + setbits_le32(SIM1_BASE_ADDR + 0x8, (0x1 << 26)); +} + +static int trdc_set_access(void) +{ + /* + * TRDC mgr + 4 MBC + 2 MRC. + * S400 should already configure when release RDC + * A35 only map non-secure region for pbridge0 and 1, set sec_access to false + */ + trdc_mbc_set_access(2, 7, 0, 49, false); + trdc_mbc_set_access(2, 7, 0, 50, false); + trdc_mbc_set_access(2, 7, 0, 51, false); + trdc_mbc_set_access(2, 7, 0, 52, false); + trdc_mbc_set_access(2, 7, 0, 53, false); + trdc_mbc_set_access(2, 7, 0, 54, false); + + /* CGC0: PBridge0 slot 47 */ + trdc_mbc_set_access(2, 7, 0, 47, false); + + /* Iomuxc0: : PBridge1 slot 33 */ + trdc_mbc_set_access(2, 7, 1, 33, false); + + return 0; +} + +int arch_cpu_init(void) +{ + if (IS_ENABLED(CONFIG_SPL_BUILD)) { + /* Disable wdog */ + init_wdog(); + + if (get_boot_mode() == SINGLE_BOOT) { + release_rdc(RDC_TRDC); + trdc_set_access(); + /* LPAV to APD */ + setbits_le32(0x2802B044, BIT(7)); + /* GPU 2D/3D to APD */ + setbits_le32(0x2802B04C, BIT(1) | BIT(2)); + /* DCNANO and MIPI_DSI to APD */ + setbits_le32(0x2802B04C, BIT(1) | BIT(2) | BIT(3) | BIT(4)); + } + + /* release xrdc, then allow A35 to write SRAM2 */ + release_rdc(RDC_XRDC); + xrdc_mrc_region_set_access(2, CONFIG_SPL_TEXT_BASE, 0xE00); + + clock_init(); + } else { + /* reconfigure core0 reset vector to ROM */ + set_core0_reset_vector(0x1000); + } + + 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,imx8ulp-mu"); + + ret = uclass_get_device_by_of_offset(UCLASS_MISC, node, &devp); + if (ret) { + printf("could not get S400 mu %d\n", ret); + return ret; + } + + return 0; +} + +#if defined(CONFIG_SPL_BUILD) +__weak void __noreturn jump_to_image_no_args(struct spl_image_info *spl_image) +{ + debug("image entry point: 0x%lx\n", spl_image->entry_point); + + set_core0_reset_vector((u32)spl_image->entry_point); + + /* Enable the 512KB cache */ + setbits_le32(SIM1_BASE_ADDR + 0x30, (0x1 << 4)); + + /* reset core */ + setbits_le32(SIM1_BASE_ADDR + 0x30, (0x1 << 16)); + + while (1) + ; +} +#endif + +void imx_get_mac_from_fuse(int dev_id, unsigned char *mac) +{ + memset(mac, 0, 6); +} + +int (*card_emmc_is_boot_part_en)(void) = (void *)0x67cc; +u32 spl_arch_boot_image_offset(u32 image_offset, u32 rom_bt_dev) +{ + /* Hard code for eMMC image_offset on 8ULP ROM, need fix by ROM, temp workaround */ + if (((rom_bt_dev >> 16) & 0xff) == BT_DEV_TYPE_MMC && card_emmc_is_boot_part_en()) + image_offset = 0; + + return image_offset; +} diff --git a/arch/arm/mach-imx/imx8ulp/upower/Makefile b/arch/arm/mach-imx/imx8ulp/upower/Makefile new file mode 100644 index 0000000000..f8b5da2ad3 --- /dev/null +++ b/arch/arm/mach-imx/imx8ulp/upower/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright 2020 NXP +# + +obj-y += upower_api.o upower_hal.o diff --git a/arch/arm/mach-imx/imx8ulp/upower/upower_api.c b/arch/arm/mach-imx/imx8ulp/upower/upower_api.c new file mode 100644 index 0000000000..5e19b9861f --- /dev/null +++ b/arch/arm/mach-imx/imx8ulp/upower/upower_api.c @@ -0,0 +1,485 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright 2021 NXP + */ + +#include <linux/types.h> +#include <string.h> +#include <asm/arch/imx-regs.h> +#include <asm/io.h> +#include "upower_api.h" + +enum upwr_api_state api_state; +enum soc_domain pwr_domain; +void *sh_buffer[UPWR_SG_COUNT]; +struct upwr_code_vers fw_rom_version; +struct upwr_code_vers fw_ram_version; +u32 fw_launch_option; +u32 sg_busy; +struct mu_type *mu; +upwr_up_max_msg sg_rsp_msg[UPWR_SG_COUNT]; +upwr_callb user_callback[UPWR_SG_COUNT]; +UPWR_RX_CALLB_FUNC_T sgrp_callback[UPWR_SG_COUNT]; +u32 sg_rsp_siz[UPWR_SG_COUNT]; + +#define UPWR_MU_MSG_SIZE (2) +#define UPWR_SG_BUSY(sg) (sg_busy & (1 << (sg))) +#define UPWR_USR_CALLB(sg, cb) \ + do { \ + user_callback[sg] = cb; \ + } while (0) +#define UPWR_MSG_HDR(hdr, sg, fn) \ + (hdr).domain = (u32)pwr_domain; \ + (hdr).srvgrp = sg; \ + (hdr).function = fn + +static u32 upwr_ptr2offset(u64 ptr, enum upwr_sg sg, size_t siz, size_t offset, const void *vptr) +{ + if (ptr >= UPWR_DRAM_SHARED_BASE_ADDR && + ((ptr - UPWR_DRAM_SHARED_BASE_ADDR) < UPWR_DRAM_SHARED_SIZE)) { + return (u32)(ptr - UPWR_DRAM_SHARED_BASE_ADDR); + } + + /* pointer is outside the shared memory, copy the struct to buffer */ + memcpy(offset + (char *)sh_buffer[sg], (void *)vptr, siz); + + return (u32)((u64)sh_buffer[sg] + offset - UPWR_DRAM_SHARED_BASE_ADDR); +} + +enum upwr_req_status upwr_req_status(enum upwr_sg sg, u32 *sgfptr, enum upwr_resp *errptr, + int *retptr) +{ + enum upwr_req_status status; + + status = (sg_rsp_msg[sg].hdr.errcode == UPWR_RESP_OK) ? UPWR_REQ_OK : UPWR_REQ_ERR; + + return status; +} + +void upwr_copy2tr(struct mu_type *mu, const u32 *msg, u32 size) +{ + int i; + + for (i = size - 1; i > -1; i--) + writel(msg[i], &mu->tr[i]); +} + +int upwr_tx(const u32 *msg, u32 size) +{ + if (size > UPWR_MU_MSG_SIZE) + return -2; + if (!size) + return -2; + + if (readl(&mu->tsr) != UPWR_MU_TSR_EMPTY) + return -1; /* not all TE bits in 1: some data to send still */ + + upwr_copy2tr(mu, msg, size); + writel(1 << (size - 1), &mu->tcr); + + return 0; +} + +void upwr_srv_req(enum upwr_sg sg, u32 *msg, u32 size) +{ + sg_busy |= 1 << sg; + + upwr_tx(msg, size); +} + +int upwr_pwm_power_on(const u32 swton[], const u32 memon[], upwr_callb callb) +{ + upwr_pwm_pwron_msg txmsg; + u64 ptrval; /* needed for X86, ARM64 */ + size_t stsize = 0; + + if (api_state != UPWR_API_READY) + return -3; + if (UPWR_SG_BUSY(UPWR_SG_PWRMGMT)) + return -1; + + UPWR_USR_CALLB(UPWR_SG_PWRMGMT, callb); + + UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_PWRMGMT, UPWR_PWM_PWR_ON); + + if (!swton) + txmsg.ptrs.ptr0 = 0; /* NULL pointer -> 0 offset */ + else + txmsg.ptrs.ptr0 = upwr_ptr2offset(ptrval, UPWR_SG_PWRMGMT, + (stsize = UPWR_PMC_SWT_WORDS * 4), 0, swton); + + if (!memon) + txmsg.ptrs.ptr1 = 0; /* NULL pointer -> 0 offset */ + else + txmsg.ptrs.ptr1 = upwr_ptr2offset(ptrval, UPWR_SG_PWRMGMT, UPWR_PMC_MEM_WORDS * 4, + stsize, memon); + + upwr_srv_req(UPWR_SG_PWRMGMT, (u32 *)&txmsg, sizeof(txmsg) / 4); + + return 0; +} + +enum upwr_req_status upwr_poll_req_status(enum upwr_sg sg, u32 *sgfptr, + enum upwr_resp *errptr, int *retptr, + u32 attempts) +{ + u32 i; + enum upwr_req_status ret; + + if (!attempts) { + ret = UPWR_REQ_BUSY; + while (ret == UPWR_REQ_BUSY) + ret = upwr_req_status(sg, sgfptr, errptr, retptr); + return ret; + } + + for (i = 0; i < attempts; i++) { + ret = upwr_req_status(sg, sgfptr, errptr, retptr); + if (ret != UPWR_REQ_BUSY) + break; + } + + return ret; +} + +int upwr_xcp_i2c_access(u16 addr, int8_t data_size, uint8_t subaddr_size, u32 subaddr, + u32 wdata, const upwr_callb callb) +{ + u64 ptrval = (u64)sh_buffer[UPWR_SG_EXCEPT]; + struct upwr_i2c_access *i2c_acc_ptr = (struct upwr_i2c_access *)ptrval; + struct upwr_pointer_msg txmsg; + + if (api_state != UPWR_API_READY) + return -3; + if (UPWR_SG_BUSY(UPWR_SG_EXCEPT)) + return -1; + + UPWR_USR_CALLB(UPWR_SG_EXCEPT, callb); + + UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_EXCEPT, UPWR_XCP_I2C); + + i2c_acc_ptr->addr = addr; + i2c_acc_ptr->subaddr = subaddr; + i2c_acc_ptr->subaddr_size = subaddr_size; + i2c_acc_ptr->data = wdata; + i2c_acc_ptr->data_size = data_size; + + txmsg.ptr = upwr_ptr2offset(ptrval, + UPWR_SG_EXCEPT, + (size_t)sizeof(struct upwr_i2c_access), + 0, + i2c_acc_ptr); + + upwr_srv_req(UPWR_SG_EXCEPT, (u32 *)&txmsg, sizeof(txmsg) / 4); + + return 0; +} + +int upwr_xcp_set_ddr_retention(enum soc_domain domain, u32 enable, const upwr_callb callb) +{ + union upwr_down_1w_msg txmsg; + + if (api_state != UPWR_API_READY) + return -3; + if (UPWR_SG_BUSY(UPWR_SG_EXCEPT)) + return -1; + + UPWR_USR_CALLB(UPWR_SG_EXCEPT, callb); + + UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_EXCEPT, UPWR_XCP_SET_DDR_RETN); + txmsg.hdr.domain = (u32)domain; + txmsg.hdr.arg = (u32)enable; + + upwr_srv_req(UPWR_SG_EXCEPT, (u32 *)&txmsg, sizeof(txmsg) / 4); + + return 0; +} + +int upwr_rx(u32 *msg, u32 *size) +{ + u32 len = readl(&mu->rsr); + + len = (len == 0x0) ? 0 : + (len == 0x1) ? 1 : + #if UPWR_MU_MSG_SIZE > 1 + (len == 0x3) ? 2 : + #if UPWR_MU_MSG_SIZE > 2 + (len == 0x7) ? 3 : + #if UPWR_MU_MSG_SIZE > 3 + (len == 0xF) ? 4 : + #endif + #endif + #endif + 0xFFFFFFFF; /* something wrong */ + + if (len == 0xFFFFFFFF) + return -3; + + *size = len; + if (!len) + return -1; + + /* copy the received message to the rx queue, so the interrupts are cleared; */ + for (u32 i = 0; i < len; i++) + msg[i] = readl(&mu->rr[i]); + + return 0; +} + +void msg_copy(u32 *dest, u32 *src, u32 size) +{ + *dest = *src; + if (size > 1) + *(dest + 1) = *(src + 1); +} + +void upwr_mu_int_callback(void) +{ + enum upwr_sg sg; /* service group number */ + UPWR_RX_CALLB_FUNC_T sg_callb; /* service group callback */ + struct upwr_up_2w_msg rxmsg; + u32 size; /* in words */ + + if (upwr_rx((u32 *)&rxmsg, &size) < 0) { + UPWR_API_ASSERT(0); + return; + } + + sg = (enum upwr_sg)rxmsg.hdr.srvgrp; + + /* copy msg to the service group buffer */ + msg_copy((u32 *)&sg_rsp_msg[sg], (u32 *)&rxmsg, size); + sg_rsp_siz[sg] = size; + sg_busy &= ~(1 << sg); + + sg_callb = sgrp_callback[sg]; + if (!sg_callb) { + upwr_callb user_callb = user_callback[sg]; + + /* no service group callback; call the user callback if any */ + if (!user_callb) + goto done; /* no user callback */ + + /* make the user callback */ + user_callb(sg, rxmsg.hdr.function, (enum upwr_resp)rxmsg.hdr.errcode, + (int)(size == 2) ? rxmsg.word2 : rxmsg.hdr.ret); + goto done; + } + + /* finally make the group callback */ + sg_callb(); + /* don't uninstall the group callback, it's permanent */ +done: + if (rxmsg.hdr.errcode == UPWR_RESP_SHUTDOWN) /* shutdown error: */ + api_state = UPWR_API_INITLZED; +} + +void upwr_txrx_isr(void) +{ + if (readl(&mu->rsr)) + upwr_mu_int_callback(); +} + +void upwr_start_callb(void) +{ + switch (api_state) { + case UPWR_API_START_WAIT: + { + upwr_rdy_callb start_callb = (upwr_rdy_callb)user_callback[UPWR_SG_EXCEPT]; + + union upwr_ready_msg *msg = (union upwr_ready_msg *)&sg_rsp_msg[UPWR_SG_EXCEPT]; + + /* message sanity check */ + UPWR_API_ASSERT(msg->hdr.srvgrp == UPWR_SG_EXCEPT); + UPWR_API_ASSERT(msg->hdr.function == UPWR_XCP_START); + UPWR_API_ASSERT(msg->hdr.errcode == UPWR_RESP_OK); + + fw_ram_version.soc_id = fw_rom_version.soc_id; + fw_ram_version.vmajor = msg->args.vmajor; + fw_ram_version.vminor = msg->args.vminor; + fw_ram_version.vfixes = msg->args.vfixes; + + /* + * vmajor == vminor == vfixes == 0 indicates start error + * in this case, go back to the INITLZED state + */ + + if (fw_ram_version.vmajor || fw_ram_version.vminor || fw_ram_version.vfixes) { + api_state = UPWR_API_READY; + + /* initialization is over: uninstall the callbacks just in case */ + UPWR_USR_CALLB(UPWR_SG_EXCEPT, NULL); + sgrp_callback[UPWR_SG_EXCEPT] = NULL; + + if (!fw_launch_option) { + /* launched ROM firmware: RAM fw versions must be all 0s */ + fw_ram_version.vmajor = + fw_ram_version.vminor = + fw_ram_version.vfixes = 0; + } + } else { + api_state = UPWR_API_INITLZED; + } + + start_callb(msg->args.vmajor, msg->args.vminor, msg->args.vfixes); + } + break; + + default: + UPWR_API_ASSERT(0); + break; + } +} + +int upwr_init(enum soc_domain domain, struct mu_type *muptr) +{ + u32 dom_buffer_base = ((UPWR_API_BUFFER_ENDPLUS + UPWR_API_BUFFER_BASE) / 2); + union upwr_init_msg *msg = (union upwr_init_msg *)&sg_rsp_msg[UPWR_SG_EXCEPT]; + enum upwr_sg sg; /* service group number */ + u32 size; /* in words */ + int j; + + mu = muptr; + writel(0, &mu->tcr); + writel(0, &mu->rcr); + + api_state = UPWR_API_INIT_WAIT; + pwr_domain = domain; + sg_busy = 0; + + /* initialize the versions, in case they are polled */ + fw_rom_version.soc_id = + fw_rom_version.vmajor = + fw_rom_version.vminor = + fw_rom_version.vfixes = 0; + + fw_ram_version.soc_id = + fw_ram_version.vmajor = + fw_ram_version.vminor = + fw_ram_version.vfixes = 0; + + sh_buffer[UPWR_SG_EXCEPT] = (void *)(ulong)dom_buffer_base; + sh_buffer[UPWR_SG_PWRMGMT] = (void *)(ulong)(dom_buffer_base + + sizeof(union upwr_xcp_union)); + sh_buffer[UPWR_SG_DELAYM] = NULL; + sh_buffer[UPWR_SG_VOLTM] = NULL; + sh_buffer[UPWR_SG_CURRM] = NULL; + sh_buffer[UPWR_SG_TEMPM] = NULL; + sh_buffer[UPWR_SG_DIAG] = NULL; + /* (no buffers service groups other than xcp and pwm for now) */ + + for (j = 0; j < UPWR_SG_COUNT; j++) { + user_callback[j] = NULL; + /* service group Exception gets the initialization callbacks */ + sgrp_callback[j] = (j == UPWR_SG_EXCEPT) ? upwr_start_callb : NULL; + + /* response messages with an initial consistent content */ + sg_rsp_msg[j].hdr.errcode = UPWR_RESP_SHUTDOWN; + } + + if (readl(&mu->fsr) & BIT(0)) { + /* send a ping message down to get the ROM version back */ + upwr_xcp_ping_msg ping_msg; + + ping_msg.hdr.domain = pwr_domain; + ping_msg.hdr.srvgrp = UPWR_SG_EXCEPT; + ping_msg.hdr.function = UPWR_XCP_PING; + + if (readl(&mu->rsr) & BIT(0)) /* first clean any Rx message left over */ + upwr_rx((u32 *)msg, &size); + + while (readl(&mu->tsr) != UPWR_MU_TSR_EMPTY) + ; + + /* + * now send the ping message; + * do not use upwr_tx, which needs API initilized; + * just write to the MU TR register(s) + */ + setbits_le32(&mu->fcr, BIT(0)); /* flag urgency status */ + upwr_copy2tr(mu, (u32 *)&ping_msg, sizeof(ping_msg) / 4); + } + + do { + /* poll for the MU Rx status: wait for an init message, either + * 1st sent from uPower after reset or as a response to a ping + */ + while (!readl(&mu->rsr) & BIT(0)) + ; + + clrbits_le32(&mu->fcr, BIT(0)); + + if (upwr_rx((u32 *)msg, &size) < 0) + return -4; + + if (size != (sizeof(union upwr_init_msg) / 4)) { + if (readl(&mu->fsr) & BIT(0)) + continue; /* discard left over msg */ + else + return -4; + } + + sg = (enum upwr_sg)msg->hdr.srvgrp; + if (sg != UPWR_SG_EXCEPT) { + if (readl(&mu->fsr) & BIT(0)) + continue; + else + return -4; + } + + if ((enum upwr_xcp_f)msg->hdr.function != UPWR_XCP_INIT) { + if (readl(&mu->fsr) & BIT(0)) + continue; + else + return -4; + } + + break; + } while (true); + + fw_rom_version.soc_id = msg->args.soc; + fw_rom_version.vmajor = msg->args.vmajor; + fw_rom_version.vminor = msg->args.vminor; + fw_rom_version.vfixes = msg->args.vfixes; + + api_state = UPWR_API_INITLZED; + + return 0; +} /* upwr_init */ + +int upwr_start(u32 launchopt, const upwr_rdy_callb rdycallb) +{ + upwr_start_msg txmsg; + + if (api_state != UPWR_API_INITLZED) + return -3; + + UPWR_USR_CALLB(UPWR_SG_EXCEPT, (upwr_callb)rdycallb); + + UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_EXCEPT, UPWR_XCP_START); + + txmsg.hdr.arg = launchopt; + fw_launch_option = launchopt; + + if (upwr_tx((u32 *)&txmsg, sizeof(txmsg) / 4) < 0) { + /* catastrophic error, but is it possible to happen? */ + UPWR_API_ASSERT(0); + return -1; + } + + api_state = UPWR_API_START_WAIT; + + return 0; +} + +u32 upwr_rom_version(u32 *vmajor, u32 *vminor, u32 *vfixes) +{ + u32 soc; + + soc = fw_rom_version.soc_id; + *vmajor = fw_rom_version.vmajor; + *vminor = fw_rom_version.vminor; + *vfixes = fw_rom_version.vfixes; + + return soc; +} diff --git a/arch/arm/mach-imx/imx8ulp/upower/upower_api.h b/arch/arm/mach-imx/imx8ulp/upower/upower_api.h new file mode 100644 index 0000000000..5cd7802a3d --- /dev/null +++ b/arch/arm/mach-imx/imx8ulp/upower/upower_api.h @@ -0,0 +1,258 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Copyright 2020 NXP + */ + +enum soc_domain { + RTD_DOMAIN = 0, + APD_DOMAIN = 1, + UPWR_MAIN_DOMAINS, /* RTD, AVD */ + AVD_DOMAIN = UPWR_MAIN_DOMAINS, + UPWR_DOMAIN_COUNT, /* RTD, APD, AVD */ + PSD_DOMAIN = UPWR_DOMAIN_COUNT, + UPWR_ALL_DOMAINS /* RTD, APD, AVD, PSD */ +}; + +enum upwr_api_state { + UPWR_API_INIT_WAIT, /* waiting for ROM firmware initialization */ + UPWR_API_INITLZED, /* ROM firmware initialized */ + UPWR_API_START_WAIT, /* waiting for start services */ + UPWR_API_READY /* ready to receive service requests */ +}; + +enum upwr_sg { /* Service Groups in priority order, high to low */ + UPWR_SG_EXCEPT, /* 0 = exception */ + UPWR_SG_PWRMGMT, /* 1 = power management */ + UPWR_SG_DELAYM, /* 2 = delay measurement */ + UPWR_SG_VOLTM, /* 3 = voltage measurement */ + UPWR_SG_CURRM, /* 4 = current measurement */ + UPWR_SG_TEMPM, /* 5 = temperature measurement */ + UPWR_SG_DIAG, /* 6 = diagnostic */ + UPWR_SG_COUNT +}; + +enum upwr_xcp_f { /* Exception Functions */ + /* 0 = init msg (not a service request itself) */ + UPWR_XCP_INIT, + /* 0 = also ping request, since its response is an init msg */ + UPWR_XCP_PING = UPWR_XCP_INIT, + UPWR_XCP_START, /* 1 = service start: upwr_start (not a service request itself) */ + UPWR_XCP_SHUTDOWN, /* 2 = service shutdown: upwr_xcp_shutdown */ + UPWR_XCP_CONFIG, /* 3 = uPower configuration: upwr_xcp_config */ + UPWR_XCP_SW_ALARM, /* 4 = uPower software alarm: upwr_xcp_sw_alarm */ + UPWR_XCP_I2C, /* 5 = I2C access: upwr_xcp_i2c_access */ + UPWR_XCP_SPARE_6, /* 6 = spare */ + UPWR_XCP_SET_DDR_RETN, /* 7 = set/clear ddr retention */ + UPWR_XCP_SPARE_8, /* 8 = spare */ + UPWR_XCP_SPARE_9, /* 9 = spare */ + UPWR_XCP_SPARE_10, /* 10 = spare */ + UPWR_XCP_SPARE_11, /* 11 = spare */ + UPWR_XCP_SPARE_12, /* 12 = spare */ + UPWR_XCP_SPARE_13, /* 13 = spare */ + UPWR_XCP_SPARE_14, /* 14 = spare */ + UPWR_XCP_SPARE_15, /* 15 = spare */ + UPWR_XCP_F_COUNT +}; + +enum upwr_resp { /* response error codes */ + UPWR_RESP_OK = 0, /* no error */ + UPWR_RESP_SG_BUSY, /* service group is busy */ + UPWR_RESP_SHUTDOWN, /* services not up or shutting down */ + UPWR_RESP_BAD_REQ, /* invalid request */ + UPWR_RESP_BAD_STATE, /* system state doesn't allow perform the request */ + UPWR_RESP_UNINSTALLD, /* service or function not installed */ + UPWR_RESP_UNINSTALLED = + UPWR_RESP_UNINSTALLD, /* service or function not installed (alias) */ + UPWR_RESP_RESOURCE, /* resource not available */ + UPWR_RESP_TIMEOUT, /* service timeout */ + UPWR_RESP_COUNT +}; + +#define UPWR_SRVGROUP_BITS (4) +#define UPWR_FUNCTION_BITS (4) +#define UPWR_PWDOMAIN_BITS (4) +#define UPWR_HEADER_BITS (UPWR_SRVGROUP_BITS + UPWR_FUNCTION_BITS + UPWR_PWDOMAIN_BITS) +#define UPWR_ARG_BITS (32 - UPWR_HEADER_BITS) + +#define UPWR_DUAL_OFFSET_BITS ((UPWR_ARG_BITS + 32) >> 1) + +struct upwr_msg_hdr { + u32 domain :UPWR_PWDOMAIN_BITS; /* power domain */ + u32 srvgrp :UPWR_SRVGROUP_BITS; /* service group */ + u32 function :UPWR_FUNCTION_BITS; /* function */ + u32 arg :UPWR_ARG_BITS; /* function-specific argument */ +}; + +union upwr_down_1w_msg { + struct upwr_msg_hdr hdr; + u32 word; /* message first word */ +}; + +#define upwr_start_msg union upwr_down_1w_msg +#define upwr_xcp_ping_msg union upwr_down_1w_msg + +#define UPWR_RESP_ERR_BITS (4) +#define UPWR_RESP_HDR_BITS (UPWR_RESP_ERR_BITS + \ + UPWR_SRVGROUP_BITS + UPWR_FUNCTION_BITS) +#define UPWR_RESP_RET_BITS (32 - UPWR_RESP_HDR_BITS) + +struct upwr_resp_hdr { + u32 errcode :UPWR_RESP_ERR_BITS; + u32 srvgrp :UPWR_SRVGROUP_BITS; /* service group */ + u32 function:UPWR_FUNCTION_BITS; + u32 ret :UPWR_RESP_RET_BITS; /* return value, if any */ +}; + +struct upwr_up_2w_msg { + struct upwr_resp_hdr hdr; + u32 word2; /* message second word */ +}; + +#define upwr_up_max_msg struct upwr_up_2w_msg + +union upwr_2pointer_msg { + struct upwr_msg_hdr hdr; + struct { + u64:UPWR_HEADER_BITS; + u64 ptr0:UPWR_DUAL_OFFSET_BITS; + u64 ptr1:UPWR_DUAL_OFFSET_BITS; + } ptrs; +}; + +#define upwr_pwm_pwron_msg union upwr_2pointer_msg + +struct upwr_pointer_msg { + struct upwr_msg_hdr hdr; + u32 ptr; /* config struct offset */ +}; + +struct upwr_i2c_access { /* structure pointed by message upwr_xcp_i2c_msg */ + u16 addr; + s8 data_size; + u8 subaddr_size; + u32 subaddr; + u32 data; +}; + +enum upwr_req_status { + UPWR_REQ_OK, /* request succeeded */ + UPWR_REQ_ERR, /* request failed */ + UPWR_REQ_BUSY /* request execution ongoing */ +}; + +#define UPWR_SOC_BITS (7) +#define UPWR_VMINOR_BITS (4) +#define UPWR_VFIXES_BITS (4) +#define UPWR_VMAJOR_BITS \ + (32 - UPWR_HEADER_BITS - UPWR_SOC_BITS - UPWR_VMINOR_BITS - UPWR_VFIXES_BITS) +union upwr_init_msg { + struct upwr_resp_hdr hdr; + struct { + u32 rsv:UPWR_RESP_HDR_BITS; + u32 soc:UPWR_SOC_BITS; /* SoC identification */ + u32 vmajor:UPWR_VMAJOR_BITS; /* firmware major version */ + u32 vminor:UPWR_VMINOR_BITS; /* firmware minor version */ + u32 vfixes:UPWR_VFIXES_BITS; /* firmware fixes version */ + } args; +}; + +#define UPWR_RAM_VMINOR_BITS (7) +#define UPWR_RAM_VFIXES_BITS (6) +#define UPWR_RAM_VMAJOR_BITS (32 - UPWR_HEADER_BITS - UPWR_RAM_VFIXES_BITS - UPWR_RAM_VMINOR_BITS) + +union upwr_ready_msg { + struct upwr_resp_hdr hdr; + struct { + u32 rsv:UPWR_RESP_HDR_BITS; + u32 vmajor:UPWR_RAM_VMAJOR_BITS; /* RAM fw major version */ + u32 vminor:UPWR_RAM_VMINOR_BITS; /* RAM fw minor version */ + u32 vfixes:UPWR_RAM_VFIXES_BITS; /* RAM fw fixes version */ + } args; +}; + +struct upwr_reg_access_t { + u32 addr; + u32 data; + u32 mask; /* mask=0 commands read */ +}; + +union upwr_xcp_union { + struct upwr_reg_access_t reg_access; +}; + +enum { /* Power Management Functions */ + UPWR_PWM_REGCFG, /* 0 = regulator config: upwr_pwm_reg_config */ + UPWR_PWM_DEVMODE = UPWR_PWM_REGCFG, /* deprecated, for old compile */ + UPWR_PWM_VOLT, /* 1 = voltage change: upwr_pwm_chng_reg_voltage */ + UPWR_PWM_SWITCH, /* 2 = switch control: upwr_pwm_chng_switch_mem */ + UPWR_PWM_PWR_ON, /* 3 = switch/RAM/ROM power on: upwr_pwm_power_on */ + UPWR_PWM_PWR_OFF, /* 4 = switch/RAM/ROM power off: upwr_pwm_power_off */ + UPWR_PWM_RETAIN, /* 5 = retain memory array: upwr_pwm_mem_retain */ + UPWR_PWM_DOM_BIAS, /* 6 = Domain bias control: upwr_pwm_chng_dom_bias */ + UPWR_PWM_MEM_BIAS, /* 7 = Memory bias control: upwr_pwm_chng_mem_bias */ + UPWR_PWM_PMICCFG, /* 8 = PMIC configuration: upwr_pwm_pmic_config */ + UPWR_PWM_PMICMOD = UPWR_PWM_PMICCFG, /* deprecated, for old compile */ + UPWR_PWM_PES, /* 9 = Power Event Sequencer */ + UPWR_PWM_CONFIG, /* 10= apply power mode defined configuration */ + UPWR_PWM_CFGPTR, /* 11= configuration pointer */ + UPWR_PWM_DOM_PWRON, /* 12 = domain power on: upwr_pwm_dom_power_on */ + UPWR_PWM_BOOT, /* 13 = boot start: upwr_pwm_boot_start */ + UPWR_PWM_FREQ, /* 14 = domain frequency setup */ + UPWR_PWM_PARAM, /* 15 = power management parameters */ + UPWR_PWM_F_COUNT +}; + +#ifndef UPWR_PMC_SWT_WORDS +#define UPWR_PMC_SWT_WORDS (1) +#endif + +#ifndef UPWR_PMC_MEM_WORDS +#define UPWR_PMC_MEM_WORDS (2) +#endif + +#define UPWR_API_ASSERT(c) do { } while (0) + +struct upwr_code_vers { + u32 soc_id; + u32 vmajor; + u32 vminor; + u32 vfixes; +}; + +#define UPWR_MU_MSG_SIZE (2) + +#define UPWR_MU_TSR_EMPTY ((u32)((1 << UPWR_MU_MSG_SIZE) - 1)) + +#ifndef UPWR_DRAM_SHARED_BASE_ADDR +#define UPWR_DRAM_SHARED_BASE_ADDR (0x28330000) +#endif + +#ifndef UPWR_DRAM_SHARED_SIZE +#define UPWR_DRAM_SHARED_SIZE (2048) +#endif + +#define UPWR_DRAM_SHARED_ENDPLUS (UPWR_DRAM_SHARED_BASE_ADDR + UPWR_DRAM_SHARED_SIZE) + +#ifndef UPWR_API_BUFFER_BASE +#define UPWR_API_BUFFER_BASE (0x28330600) +#endif + +#ifndef UPWR_API_BUFFER_ENDPLUS +#define UPWR_API_BUFFER_ENDPLUS (UPWR_DRAM_SHARED_ENDPLUS - 64) +#endif + +typedef void (*upwr_rdy_callb)(u32 vmajor, u32 vminor, u32 vfixes); +typedef void (*upwr_callb)(enum upwr_sg sg, u32 func, enum upwr_resp errcode, int ret); +int upwr_init(enum soc_domain domain, struct mu_type *muptr); +int upwr_start(u32 launchopt, const upwr_rdy_callb rdycallb); +u32 upwr_rom_version(u32 *vmajor, u32 *vminor, u32 *vfixes); +typedef void (*UPWR_RX_CALLB_FUNC_T)(void); + +int upwr_xcp_set_ddr_retention(enum soc_domain domain, u32 enable, const upwr_callb callb); +int upwr_pwm_power_on(const u32 swton[], const u32 memon[], upwr_callb callb); +int upwr_xcp_i2c_access(u16 addr, s8 data_size, u8 subaddr_size, u32 subaddr, + u32 wdata, const upwr_callb callb); +enum upwr_req_status upwr_poll_req_status(enum upwr_sg sg, u32 *sgfptr, + enum upwr_resp *errptr, int *retptr, + u32 attempts); +void upwr_txrx_isr(void); diff --git a/arch/arm/mach-imx/imx8ulp/upower/upower_hal.c b/arch/arm/mach-imx/imx8ulp/upower/upower_hal.c new file mode 100644 index 0000000000..b6811d56c9 --- /dev/null +++ b/arch/arm/mach-imx/imx8ulp/upower/upower_hal.c @@ -0,0 +1,180 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright 2021 NXP + */ + +#include <log.h> +#include <asm/io.h> +#include <asm/arch/imx-regs.h> +#include <linux/delay.h> + +#include "upower_api.h" + +#define UPOWER_AP_MU1_ADDR 0x29280000 +static struct mu_type *muptr = (struct mu_type *)UPOWER_AP_MU1_ADDR; + +void upower_wait_resp(void) +{ + while (!(readl(&muptr->rsr) & BIT(0))) { + debug("%s: poll the mu:%x\n", __func__, readl(&muptr->rsr)); + udelay(100); + } + + upwr_txrx_isr(); +} + +u32 upower_status(int status) +{ + u32 ret = -1; + + switch (status) { + case 0: + debug("%s: finished successfully!\n", __func__); + ret = 0; + break; + case -1: + printf("%s: memory allocation or resource failed!\n", __func__); + break; + case -2: + printf("%s: invalid argument!\n", __func__); + break; + case -3: + printf("%s: called in an invalid API state!\n", __func__); + break; + default: + printf("%s: invalid return status\n", __func__); + break; + } + return ret; +} + +void user_upwr_rdy_callb(u32 soc, u32 vmajor, u32 vminor) +{ + printf("%s: soc=%x\n", __func__, soc); + printf("%s: RAM version:%d.%d\n", __func__, vmajor, vminor); +} + +int upower_pmic_i2c_write(u32 reg_addr, u32 reg_val) +{ + int ret, ret_val; + enum upwr_resp err_code; + + ret = upwr_xcp_i2c_access(0x32, 1, 1, reg_addr, reg_val, NULL); + if (ret) { + printf("pmic i2c write failed ret %d\n", ret); + return ret; + } + + upower_wait_resp(); + ret = upwr_poll_req_status(UPWR_SG_EXCEPT, NULL, &err_code, &ret_val, 1000); + if (ret != UPWR_REQ_OK) { + printf("i2c poll Failure %d, err_code %d, ret_val 0x%x\n", ret, err_code, ret_val); + return ret; + } + + debug("PMIC write reg[0x%x], val[0x%x]\n", reg_addr, reg_val); + + return 0; +} + +int upower_pmic_i2c_read(u32 reg_addr, u32 *reg_val) +{ + int ret, ret_val; + enum upwr_resp err_code; + + if (!reg_val) + return -1; + + ret = upwr_xcp_i2c_access(0x32, -1, 1, reg_addr, 0, NULL); + if (ret) { + printf("pmic i2c read failed ret %d\n", ret); + return ret; + } + + upower_wait_resp(); + ret = upwr_poll_req_status(UPWR_SG_EXCEPT, NULL, &err_code, &ret_val, 1000); + if (ret != UPWR_REQ_OK) { + printf("i2c poll Failure %d, err_code %d, ret_val 0x%x\n", ret, err_code, ret_val); + return ret; + } + + *reg_val = ret_val; + + debug("PMIC read reg[0x%x], val[0x%x]\n", reg_addr, *reg_val); + + return 0; +} + +int upower_init(void) +{ + u32 fw_major, fw_minor, fw_vfixes; + u32 soc_id; + int status; + + u32 swton; + u64 memon; + int ret, ret_val; + + do { + status = upwr_init(1, muptr); + if (upower_status(status)) { + printf("%s: upower init failure\n", __func__); + break; + } + + soc_id = upwr_rom_version(&fw_major, &fw_minor, &fw_vfixes); + if (!soc_id) { + printf("%s:, soc_id not initialized\n", __func__); + break; + } + + printf("%s: soc_id=%d\n", __func__, soc_id); + printf("%s: version:%d.%d.%d\n", __func__, fw_major, fw_minor, fw_vfixes); + + printf("%s: start uPower RAM service\n", __func__); + status = upwr_start(1, user_upwr_rdy_callb); + upower_wait_resp(); + if (upower_status(status)) { + printf("%s: upower init failure\n", __func__); + break; + } + } while (0); + + swton = 0xfff80; + ret = upwr_pwm_power_on(&swton, NULL, NULL); + if (ret) + printf("Turn on switches fail %d\n", ret); + else + printf("Turn on switches ok\n"); + upower_wait_resp(); + ret = upwr_poll_req_status(UPWR_SG_PWRMGMT, NULL, NULL, &ret_val, 1000); + if (ret != UPWR_REQ_OK) + printf("Failure %d\n", ret); + + memon = 0x3FFFFFFFFFFFFCUL; + ret = upwr_pwm_power_on(NULL, (const u32 *)&memon, NULL); + if (ret) + printf("Turn on memories fail %d\n", ret); + else + printf("Turn on memories ok\n"); + upower_wait_resp(); + ret = upwr_poll_req_status(UPWR_SG_PWRMGMT, NULL, NULL, &ret_val, 1000); + if (ret != UPWR_REQ_OK) + printf("Failure %d\n", ret); + + mdelay(1); + + ret = upwr_xcp_set_ddr_retention(APD_DOMAIN, 0, NULL); + if (ret) + printf("Clear DDR retention fail %d\n", ret); + else + printf("Clear DDR retention ok\n"); + + upower_wait_resp(); + + ret = upwr_poll_req_status(UPWR_SG_EXCEPT, NULL, NULL, &ret_val, 1000); + if (ret != UPWR_REQ_OK) + printf("Failure %d\n", ret); + + return 0; +} diff --git a/arch/arm/mach-imx/imx8/parse-container.c b/arch/arm/mach-imx/parse-container.c index 375098902f..039a4c7303 100644 --- a/arch/arm/mach-imx/imx8/parse-container.c +++ b/arch/arm/mach-imx/parse-container.c @@ -7,8 +7,10 @@ #include <errno.h> #include <log.h> #include <spl.h> -#include <asm/arch/image.h> +#include <asm/mach-imx/image.h> +#ifdef CONFIG_AHAB_BOOT #include <asm/arch/sci/sci.h> +#endif #define SEC_SECURE_RAM_BASE 0x31800000UL #define SEC_SECURE_RAM_END_BASE (SEC_SECURE_RAM_BASE + 0xFFFFUL) diff --git a/arch/arm/mach-imx/priblob.c b/arch/arm/mach-imx/priblob.c index e253eddfdc..9b92eae781 100644 --- a/arch/arm/mach-imx/priblob.c +++ b/arch/arm/mach-imx/priblob.c @@ -15,7 +15,7 @@ #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[]) +int do_priblob_write(struct cmd_tbl *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); diff --git a/arch/arm/mach-imx/spl_imx_romapi.c b/arch/arm/mach-imx/spl_imx_romapi.c index d2085dabd3..d827de375a 100644 --- a/arch/arm/mach-imx/spl_imx_romapi.c +++ b/arch/arm/mach-imx/spl_imx_romapi.c @@ -10,11 +10,44 @@ #include <asm/global_data.h> #include <linux/libfdt.h> #include <spl.h> - +#include <asm/mach-imx/image.h> #include <asm/arch/sys_proto.h> DECLARE_GLOBAL_DATA_PTR; +/* Caller need ensure the offset and size to align with page size */ +ulong spl_romapi_raw_seekable_read(u32 offset, u32 size, void *buf) +{ + volatile gd_t *pgd = gd; + int ret; + + debug("%s 0x%x, size 0x%x\n", __func__, offset, size); + + ret = g_rom_api->download_image(buf, offset, size, + ((uintptr_t)buf) ^ offset ^ size); + + set_gd(pgd); + + if (ret == ROM_API_OKAY) + return size; + + printf("%s Failure when load 0x%x, size 0x%x\n", __func__, offset, size); + + return 0; +} + +ulong __weak spl_romapi_get_uboot_base(u32 image_offset, u32 rom_bt_dev) +{ + u32 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; + + return offset; +} + static int is_boot_from_stream_device(u32 boot) { u32 interface; @@ -34,25 +67,12 @@ static ulong spl_romapi_read_seekable(struct spl_load_info *load, 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; + return spl_romapi_raw_seekable_read(offset, byte, buf) / pagesize; } static int spl_romapi_load_image_seekable(struct spl_image_info *spl_image, @@ -85,11 +105,7 @@ static int spl_romapi_load_image_seekable(struct spl_image_info *spl_image, 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; + offset = spl_romapi_get_uboot_base(image_offset, rom_bt_dev); size = ALIGN(sizeof(struct image_header), pagesize); ret = g_rom_api->download_image((u8 *)header, offset, size, @@ -102,16 +118,23 @@ static int spl_romapi_load_image_seekable(struct spl_image_info *spl_image, return -1; } - if (IS_ENABLED(CONFIG_SPL_LOAD_FIT) && - image_get_magic(header) == FDT_MAGIC) { + 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); + return spl_load_simple_fit(spl_image, &load, offset / pagesize, header); + } else if (IS_ENABLED(CONFIG_SPL_LOAD_IMX_CONTAINER)) { + struct spl_load_info load; + + memset(&load, 0, sizeof(load)); + load.bl_len = pagesize; + load.read = spl_romapi_read_seekable; + load.priv = &pagesize; + + ret = spl_load_imx_container(spl_image, &load, offset / pagesize); } else { /* TODO */ puts("Can't support legacy image\n"); @@ -154,7 +177,7 @@ static ulong get_fit_image_size(void *fit) return last - (ulong)fit; } -u8 *search_fit_header(u8 *p, int size) +static u8 *search_fit_header(u8 *p, int size) { int i; @@ -165,6 +188,71 @@ u8 *search_fit_header(u8 *p, int size) return NULL; } +static u8 *search_container_header(u8 *p, int size) +{ + int i = 0; + u8 *hdr; + + for (i = 0; i < size; i += 4) { + hdr = p + i; + if (*(hdr + 3) == 0x87 && *hdr == 0 && (*(hdr + 1) != 0 || *(hdr + 2) != 0)) + return p + i; + } + + return NULL; +} + +static u8 *search_img_header(u8 *p, int size) +{ + if (IS_ENABLED(CONFIG_SPL_LOAD_FIT)) + return search_fit_header(p, size); + else if (IS_ENABLED(CONFIG_SPL_LOAD_IMX_CONTAINER)) + return search_container_header(p, size); + + return NULL; +} + +static u32 img_header_size(void) +{ + if (IS_ENABLED(CONFIG_SPL_LOAD_FIT)) + return sizeof(struct fdt_header); + else if (IS_ENABLED(CONFIG_SPL_LOAD_IMX_CONTAINER)) + return sizeof(struct container_hdr); + + return 0; +} + +static int img_info_size(void *img_hdr) +{ +#ifdef CONFIG_SPL_LOAD_FIT + return fit_get_size(img_hdr); +#elif defined CONFIG_SPL_LOAD_IMX_CONTAINER + struct container_hdr *container = img_hdr; + + return (container->length_lsb + (container->length_msb << 8)); +#else + return 0; +#endif +} + +static int img_total_size(void *img_hdr) +{ + if (IS_ENABLED(CONFIG_SPL_LOAD_FIT)) { + return get_fit_image_size(img_hdr); + } else if (IS_ENABLED(CONFIG_SPL_LOAD_IMX_CONTAINER)) { + int total = get_container_size((ulong)img_hdr, NULL); + + if (total < 0) { + printf("invalid container image\n"); + return 0; + } + + return total; + } + + return 0; +} + static int spl_romapi_load_image_stream(struct spl_image_info *spl_image, struct spl_boot_device *bootdev) { @@ -174,7 +262,7 @@ static int spl_romapi_load_image_stream(struct spl_image_info *spl_image, int ret; int i = 0; u8 *p = (u8 *)CONFIG_SPL_IMX_ROMAPI_LOADADDR; - u8 *pfit = NULL; + u8 *phdr = NULL; int imagesize; int total; @@ -199,19 +287,19 @@ static int spl_romapi_load_image_stream(struct spl_image_info *spl_image, return -1; } - pfit = search_fit_header(p, pg); + phdr = search_img_header(p, pg); p += pg; - if (pfit) + if (phdr) break; } - if (!pfit) { - puts("Can't found uboot FIT image in 640K range \n"); + if (!phdr) { + puts("Can't found uboot image in 640K range\n"); return -1; } - if (p - pfit < sizeof(struct fdt_header)) { + if (p - phdr < img_header_size()) { ret = g_rom_api->download_image(p, 0, pg, ((uintptr_t)p) ^ pg); set_gd(pgd); @@ -223,11 +311,11 @@ static int spl_romapi_load_image_stream(struct spl_image_info *spl_image, p += pg; } - imagesize = fit_get_size(pfit); - printf("Find FIT header 0x&%p, size %d\n", pfit, imagesize); + imagesize = img_info_size(phdr); + printf("Find img info 0x&%p, size %d\n", phdr, imagesize); - if (p - pfit < imagesize) { - imagesize -= p - pfit; + if (p - phdr < imagesize) { + imagesize -= p - phdr; /*need pagesize hear after ROM fix USB problme*/ imagesize += pg - 1; imagesize /= pg; @@ -247,20 +335,21 @@ static int spl_romapi_load_image_stream(struct spl_image_info *spl_image, } } - total = get_fit_image_size(pfit); + total = img_total_size(phdr); total += 3; total &= ~0x3; - imagesize = total - (p - pfit); + imagesize = total - (p - phdr); imagesize += pagesize - 1; imagesize /= pagesize; imagesize *= pagesize; - printf("Download %d, total fit %d\n", imagesize, total); + printf("Download %d, Total size %d\n", imagesize, total); ret = g_rom_api->download_image(p, 0, imagesize, ((uintptr_t)p) ^ imagesize); + set_gd(pgd); if (ret != ROM_API_OKAY) printf("ROM download failure %d\n", imagesize); @@ -268,7 +357,12 @@ static int spl_romapi_load_image_stream(struct spl_image_info *spl_image, load.bl_len = 1; load.read = spl_ram_load_read; - return spl_load_simple_fit(spl_image, &load, (ulong)pfit, pfit); + if (IS_ENABLED(CONFIG_SPL_LOAD_FIT)) + return spl_load_simple_fit(spl_image, &load, (ulong)phdr, phdr); + else if (IS_ENABLED(CONFIG_SPL_LOAD_IMX_CONTAINER)) + return spl_load_imx_container(spl_image, &load, (ulong)phdr); + + return -1; } int board_return_to_bootrom(struct spl_image_info *spl_image, |