/* * Copyright 2017-2019 NXP * * SPDX-License-Identifier: GPL-2.0+ * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include DECLARE_GLOBAL_DATA_PTR; struct edma_ch_map { sc_rsrc_t ch_start_rsrc; u32 ch_start_regs; u32 ch_num; const char* node_path; }; u32 get_cpu_rev(void) { sc_ipc_t ipcHndl; uint32_t id = 0, rev = 0; sc_err_t err; ipcHndl = gd->arch.ipc_channel_handle; err = sc_misc_get_control(ipcHndl, SC_R_SYSTEM, SC_C_ID, &id); if (err != SC_ERR_NONE) return 0; rev = (id >> 5) & 0xf; id = (id & 0x1f) + MXC_SOC_IMX8; /* Dummy ID for chip */ return (id << 12) | rev; } #ifdef CONFIG_DISPLAY_CPUINFO const char *get_imx8_type(u32 imxtype) { switch (imxtype) { case MXC_CPU_IMX8QM: return "8QM"; /* i.MX8 Quad MAX */ case MXC_CPU_IMX8QXP: return "8QXP"; /* i.MX8 Quad XP */ case MXC_CPU_IMX8DX: return "8DX"; /* i.MX8 Dual X */ default: return "??"; } } const char *get_imx8_rev(u32 rev) { switch (rev) { case CHIP_REV_A: return "A"; case CHIP_REV_B: return "B"; case CHIP_REV_C: return "C"; default: return "?"; } } const char *get_core_name(void) { if (is_cortex_a53()) return "A53"; else if (is_cortex_a35()) return "A35"; else if (is_cortex_a72()) return "A72"; else return "?"; } int print_cpuinfo(void) { u32 cpurev; cpurev = get_cpu_rev(); printf("CPU: Freescale i.MX%s rev%s %s at %d MHz", get_imx8_type((cpurev & 0xFF000) >> 12), get_imx8_rev((cpurev & 0xFFF)), get_core_name(), mxc_get_clock(MXC_ARM_CLK) / 1000000); #if defined(CONFIG_IMX_SC_THERMAL) struct udevice *thermal_dev; int cpu_tmp, ret; if (is_imx8qm() && is_cortex_a72()) ret = uclass_get_device_by_name(UCLASS_THERMAL, "cpu-thermal1", &thermal_dev); else ret = uclass_get_device_by_name(UCLASS_THERMAL, "cpu-thermal0", &thermal_dev); if (!ret) { ret = thermal_get_temp(thermal_dev, &cpu_tmp); if (!ret) printf(" at %dC", cpu_tmp); else debug(" - invalid sensor data"); } else { debug(" - invalid sensor device"); } #endif printf("\n"); return 0; } #endif #define BT_PASSOVER_TAG (0x504F) struct pass_over_info_t *get_pass_over_info(void) { struct pass_over_info_t *p = (struct pass_over_info_t *)PASS_OVER_INFO_ADDR; if (p->barker != BT_PASSOVER_TAG || p->len != sizeof(struct pass_over_info_t)) return NULL; return p; } #if defined(CONFIG_SPL_BUILD) && defined(CONFIG_RECOVER_SPL_DATA_SECTION) char __data_save_start[0] __attribute__((section(".__data_save_start"))); char __data_save_end[0] __attribute__((section(".__data_save_end"))); u32 cold_reboot_flag = 1; static void save_restore_data(void) { u32 data_size = __data_save_end - __data_save_start; if (cold_reboot_flag == 1) { /* Save data section to data_save section */ memcpy(__data_save_start, __data_save_start - data_size, data_size); } else { /* Restore the data_save section to data section */ memcpy(__data_save_start - data_size, __data_save_start, data_size); } cold_reboot_flag++; } #endif int arch_cpu_init(void) { sc_ipc_t ipcHndl = 0; sc_err_t sciErr = 0; struct pass_over_info_t *pass_over; #if defined(CONFIG_SPL_BUILD) && defined(CONFIG_RECOVER_SPL_DATA_SECTION) save_restore_data(); #endif gd->arch.ipc_channel_handle = 0; /* Open IPC channel */ sciErr = sc_ipc_open(&ipcHndl, SC_IPC_CH); if (sciErr != SC_ERR_NONE) return -EPERM; gd->arch.ipc_channel_handle = ipcHndl; /* Dual bootloader feature will require CAAM access, but JR0 and JR1 will be * assigned to seco for imx8, use JR3 instead. */ #if defined(CONFIG_SPL_BUILD) && defined(CONFIG_DUAL_BOOTLOADER) if (sc_pm_set_resource_power_mode(ipcHndl, SC_R_CAAM_JR3, SC_PM_PW_MODE_ON) != SC_ERR_NONE) return -EPERM; if (sc_pm_set_resource_power_mode(ipcHndl, SC_R_CAAM_JR3_OUT, SC_PM_PW_MODE_ON) != SC_ERR_NONE) return -EPERM; #endif if (IS_ENABLED(CONFIG_XEN)) return 0; if (is_soc_rev(CHIP_REV_A)) { pass_over = get_pass_over_info(); if (pass_over && pass_over->g_ap_mu == 0) { /* When ap_mu is 0, means the u-boot is boot from first container */ sc_misc_boot_status(ipcHndl, SC_MISC_BOOT_STATUS_SUCCESS); } } #ifdef CONFIG_IMX_SMMU sciErr = sc_pm_set_resource_power_mode(ipcHndl, SC_R_SMMU, SC_PM_PW_MODE_ON); if (sciErr != SC_ERR_NONE) return 0; #endif return 0; } u32 cpu_mask(void) { #ifdef CONFIG_IMX8QM return 0x3f; #else return 0xf; /*For IMX8QXP*/ #endif } #define CCI400_DVM_MESSAGE_REQ_EN 0x00000002 #define CCI400_SNOOP_REQ_EN 0x00000001 #define CHANGE_PENDING_BIT (1 << 0) int imx8qm_wake_seconday_cores(void) { #ifdef CONFIG_ARMV8_MULTIENTRY sc_ipc_t ipcHndl; u64 *table = get_spin_tbl_addr(); /* Clear spin table so that secondary processors * observe the correct value after waking up from wfe. */ memset(table, 0, CONFIG_MAX_CPUS*SPIN_TABLE_ELEM_SIZE); flush_dcache_range((unsigned long)table, (unsigned long)table + (CONFIG_MAX_CPUS*SPIN_TABLE_ELEM_SIZE)); /* Open IPC channel */ if (sc_ipc_open(&ipcHndl, SC_IPC_CH) != SC_ERR_NONE) return -EIO; __raw_writel(0xc, 0x52090000); __raw_writel(1, 0x52090008); /* IPC to pwr up and boot other cores */ if (sc_pm_set_resource_power_mode(ipcHndl, SC_R_A53_1, SC_PM_PW_MODE_ON) != SC_ERR_NONE) return -EIO; if (sc_pm_cpu_start(ipcHndl, SC_R_A53_1, true, 0x80000000) != SC_ERR_NONE) return -EIO; if (sc_pm_set_resource_power_mode(ipcHndl, SC_R_A53_2, SC_PM_PW_MODE_ON) != SC_ERR_NONE) return -EIO; if (sc_pm_cpu_start(ipcHndl, SC_R_A53_2, true, 0x80000000) != SC_ERR_NONE) return -EIO; if (sc_pm_set_resource_power_mode(ipcHndl, SC_R_A53_3, SC_PM_PW_MODE_ON) != SC_ERR_NONE) return -EIO; if (sc_pm_cpu_start(ipcHndl, SC_R_A53_3, true, 0x80000000) != SC_ERR_NONE) return -EIO; /* Enable snoop and dvm msg requests for a53 port on CCI slave interface 3 */ __raw_writel(CCI400_DVM_MESSAGE_REQ_EN | CCI400_SNOOP_REQ_EN, 0x52094000); while (__raw_readl(0x5209000c) & CHANGE_PENDING_BIT) ; /* Pwr up cluster 1 and boot core 0*/ if (sc_pm_set_resource_power_mode(ipcHndl, SC_R_A72, SC_PM_PW_MODE_ON) != SC_ERR_NONE) return -EIO; if (sc_pm_set_resource_power_mode(ipcHndl, SC_R_A72_0, SC_PM_PW_MODE_ON) != SC_ERR_NONE) return -EIO; if (sc_pm_cpu_start(ipcHndl, SC_R_A72_0, true, 0x80000000) != SC_ERR_NONE) return -EIO; /* IPC to pwr up and boot core 1 */ if (sc_pm_set_resource_power_mode(ipcHndl, SC_R_A72_1, SC_PM_PW_MODE_ON) != SC_ERR_NONE) return -EIO; if (sc_pm_cpu_start(ipcHndl, SC_R_A72_1, true, 0x80000000) != SC_ERR_NONE) return -EIO; /* Enable snoop and dvm msg requests for a72 port on CCI slave interface 4 */ __raw_writel(CCI400_DVM_MESSAGE_REQ_EN | CCI400_SNOOP_REQ_EN, 0x52095000); while (__raw_readl(0x5209000c) & CHANGE_PENDING_BIT) ; #endif return 0; } int imx8qxp_wake_secondary_cores(void) { #ifdef CONFIG_ARMV8_MULTIENTRY sc_ipc_t ipcHndl; u64 *table = get_spin_tbl_addr(); /* Clear spin table so that secondary processors * observe the correct value after waking up from wfe. */ memset(table, 0, CONFIG_MAX_CPUS*SPIN_TABLE_ELEM_SIZE); flush_dcache_range((unsigned long)table, (unsigned long)table + (CONFIG_MAX_CPUS*SPIN_TABLE_ELEM_SIZE)); /* Open IPC channel */ if (sc_ipc_open(&ipcHndl, SC_IPC_CH) != SC_ERR_NONE) return -EIO; /* IPC to pwr up and boot other cores */ if (sc_pm_set_resource_power_mode(ipcHndl, SC_R_A35_1, SC_PM_PW_MODE_ON) != SC_ERR_NONE) return -EIO; if (sc_pm_cpu_start(ipcHndl, SC_R_A35_1, true, 0x80000000) != SC_ERR_NONE) return -EIO; if (sc_pm_set_resource_power_mode(ipcHndl, SC_R_A35_2, SC_PM_PW_MODE_ON) != SC_ERR_NONE) return -EIO; if (sc_pm_cpu_start(ipcHndl, SC_R_A35_2, true, 0x80000000) != SC_ERR_NONE) return -EIO; if (sc_pm_set_resource_power_mode(ipcHndl, SC_R_A35_3, SC_PM_PW_MODE_ON) != SC_ERR_NONE) return -EIO; if (sc_pm_cpu_start(ipcHndl, SC_R_A35_3, true, 0x80000000) != SC_ERR_NONE) return -EIO; #endif return 0; } #if defined(CONFIG_IMX8QM) #define FUSE_MAC0_WORD0 452 #define FUSE_MAC0_WORD1 453 #define FUSE_MAC1_WORD0 454 #define FUSE_MAC1_WORD1 455 #elif defined(CONFIG_IMX8QXP) #define FUSE_MAC0_WORD0 708 #define FUSE_MAC0_WORD1 709 #define FUSE_MAC1_WORD0 710 #define FUSE_MAC1_WORD1 711 #endif void imx_get_mac_from_fuse(int dev_id, unsigned char *mac) { sc_err_t err; sc_ipc_t ipc; uint32_t val1 = 0, val2 = 0; uint32_t word1, word2; ipc = gd->arch.ipc_channel_handle; if (dev_id == 0) { word1 = FUSE_MAC0_WORD0; word2 = FUSE_MAC0_WORD1; } else { word1 = FUSE_MAC1_WORD0; word2 = FUSE_MAC1_WORD1; } err = sc_misc_otp_fuse_read(ipc, word1, &val1); if (err != SC_ERR_NONE) { printf("%s fuse %d read error: %d\n", __func__, word1, err); return; } err = sc_misc_otp_fuse_read(ipc, word2, &val2); if (err != SC_ERR_NONE) { printf("%s fuse %d read error: %d\n", __func__, word2, err); return; } mac[0] = val1; mac[1] = val1 >> 8; mac[2] = val1 >> 16; mac[3] = val1 >> 24; mac[4] = val2; mac[5] = val2 >> 8; } #ifdef CONFIG_IMX_BOOTAUX #ifdef CONFIG_IMX8QM int arch_auxiliary_core_up(u32 core_id, ulong boot_private_data) { sc_ipc_t ipcHndl; sc_rsrc_t core_rsrc, mu_rsrc; sc_faddr_t tcml_addr; u32 tcm_size = SZ_256K; /* TCML + TCMU */ ulong addr; ipcHndl = gd->arch.ipc_channel_handle; switch (core_id) { case 0: core_rsrc = SC_R_M4_0_PID0; tcml_addr = 0x34FE0000; mu_rsrc = SC_R_M4_0_MU_1A; break; case 1: core_rsrc = SC_R_M4_1_PID0; tcml_addr = 0x38FE0000; mu_rsrc = SC_R_M4_1_MU_1A; break; default: printf("Not support this core boot up, ID:%u\n", core_id); return -EINVAL; } addr = (sc_faddr_t)boot_private_data; if (addr >= tcml_addr && addr <= tcml_addr + tcm_size) { printf("Wrong image address 0x%lx, should not in TCML\n", addr); return -EINVAL; } printf("Power on M4 and MU\n"); if (sc_pm_set_resource_power_mode(ipcHndl, core_rsrc, SC_PM_PW_MODE_ON) != SC_ERR_NONE) return -EIO; if (sc_pm_set_resource_power_mode(ipcHndl, mu_rsrc, SC_PM_PW_MODE_ON) != SC_ERR_NONE) return -EIO; printf("Copy M4 image from 0x%lx to TCML 0x%lx\n", addr, (ulong)tcml_addr); if (addr != tcml_addr) memcpy((void *)tcml_addr, (void *)addr, tcm_size); printf("Start M4 %u\n", core_id); if (sc_pm_cpu_start(ipcHndl, core_rsrc, true, tcml_addr) != SC_ERR_NONE) return -EIO; printf("bootaux complete\n"); return 0; } #endif #ifdef CONFIG_IMX8QXP static unsigned long load_elf_image_shdr(unsigned long addr) { Elf32_Ehdr *ehdr; /* Elf header structure pointer */ Elf32_Shdr *shdr; /* Section header structure pointer */ unsigned char *strtab = 0; /* String table pointer */ unsigned char *image; /* Binary image pointer */ int i; /* Loop counter */ ehdr = (Elf32_Ehdr *)addr; /* Find the section header string table for output info */ shdr = (Elf32_Shdr *)(addr + ehdr->e_shoff + (ehdr->e_shstrndx * sizeof(Elf32_Shdr))); if (shdr->sh_type == SHT_STRTAB) strtab = (unsigned char *)(addr + shdr->sh_offset); /* Load each appropriate section */ for (i = 0; i < ehdr->e_shnum; ++i) { shdr = (Elf32_Shdr *)(addr + ehdr->e_shoff + (i * sizeof(Elf32_Shdr))); if (!(shdr->sh_flags & SHF_ALLOC) || shdr->sh_addr == 0 || shdr->sh_size == 0) { continue; } if (strtab) { debug("%sing %s @ 0x%08lx (%ld bytes)\n", (shdr->sh_type == SHT_NOBITS) ? "Clear" : "Load", &strtab[shdr->sh_name], (unsigned long)shdr->sh_addr, (long)shdr->sh_size); } if (shdr->sh_type == SHT_NOBITS) { memset((void *)(uintptr_t)shdr->sh_addr, 0, shdr->sh_size); } else { image = (unsigned char *)addr + shdr->sh_offset; memcpy((void *)(uintptr_t)shdr->sh_addr, (const void *)image, shdr->sh_size); } flush_cache(shdr->sh_addr, shdr->sh_size); } return ehdr->e_entry; } int arch_auxiliary_core_up(u32 core_id, ulong boot_private_data) { sc_ipc_t ipcHndl; sc_rsrc_t core_rsrc, mu_rsrc = -1; sc_faddr_t aux_core_ram; u32 size; ulong addr; ipcHndl = gd->arch.ipc_channel_handle; switch (core_id) { case 0: core_rsrc = SC_R_M4_0_PID0; aux_core_ram = 0x34FE0000; mu_rsrc = SC_R_M4_0_MU_1A; size = SZ_256K; break; case 1: core_rsrc = SC_R_DSP; aux_core_ram = 0x596f8000; size = SZ_2K; break; default: printf("Not support this core boot up, ID:%u\n", core_id); return -EINVAL; } addr = (sc_faddr_t)boot_private_data; if (addr >= aux_core_ram && addr <= aux_core_ram + size) { printf("Wrong image address 0x%lx, should not in aux core ram\n", addr); return -EINVAL; } printf("Power on aux core %d\n", core_id); if (sc_pm_set_resource_power_mode(ipcHndl, core_rsrc, SC_PM_PW_MODE_ON) != SC_ERR_NONE) return -EIO; if (mu_rsrc != -1) { if (sc_pm_set_resource_power_mode(ipcHndl, mu_rsrc, SC_PM_PW_MODE_ON) != SC_ERR_NONE) return -EIO; } if (core_id == 1) { struct power_domain pd; if (sc_pm_clock_enable(ipcHndl, core_rsrc, SC_PM_CLK_PER, true, false) != SC_ERR_NONE) { printf("Error enable clock\n"); return -EIO; } LPCG_AllClockOn(AUD_DSP_LPCG); if (!power_domain_lookup_name("audio_sai0", &pd)) { if (power_domain_on(&pd)) { printf("Error power on SAI0\n"); return -EIO; } } if (!power_domain_lookup_name("audio_ocram", &pd)) { if (power_domain_on(&pd)) { printf("Error power on HIFI RAM\n"); return -EIO; } } LPCG_AllClockOn(AUD_OCRAM_LPCG); LPCG_AllClockOn(AUD_SAI_0_LPCG); } printf("Copy image from 0x%lx to 0x%lx\n", addr, (ulong)aux_core_ram); if (core_id == 0) { /* M4 use bin file */ memcpy((void *)aux_core_ram, (void *)addr, size); } else { /* HIFI use elf file */ if (!valid_elf_image(addr)) return -1; addr = load_elf_image_shdr(addr); } printf("Start %s\n", core_id == 0 ? "M4" : "HIFI"); if (sc_pm_cpu_start(ipcHndl, core_rsrc, true, aux_core_ram) != SC_ERR_NONE) return -EIO; printf("bootaux complete\n"); return 0; } #endif int arch_auxiliary_core_check_up(u32 core_id) { sc_rsrc_t core_rsrc; sc_pm_power_mode_t power_mode; sc_ipc_t ipcHndl; ipcHndl = gd->arch.ipc_channel_handle; switch (core_id) { case 0: core_rsrc = SC_R_M4_0_PID0; break; #ifdef CONFIG_IMX8QM case 1: core_rsrc = SC_R_M4_1_PID0; break; #endif default: printf("Not support this core, ID:%u\n", core_id); return 0; } if (sc_pm_get_resource_power_mode(ipcHndl, core_rsrc, &power_mode) != SC_ERR_NONE) return 0; if (power_mode != SC_PM_PW_MODE_OFF) return 1; return 0; } #endif static bool check_owned_resource(sc_rsrc_t rsrc_id) { sc_ipc_t ipcHndl = 0; bool owned; ipcHndl = gd->arch.ipc_channel_handle; owned = sc_rm_is_resource_owned(ipcHndl, rsrc_id); return owned; } #ifdef CONFIG_IMX_SMMU struct smmu_sid dev_sids[] = { }; sc_err_t imx8_config_smmu_sid(struct smmu_sid *dev_sids, int size) { int i; sc_err_t sciErr = SC_ERR_NONE; if ((dev_sids == NULL) || (size <= 0)) return SC_ERR_NONE; for (i = 0; i < size; i++) { if (!check_owned_resource(dev_sids[i].rsrc)) { printf("%s rsrc[%d] not owned\n", __func__, dev_sids[i].rsrc); continue; } sciErr = sc_rm_set_master_sid(gd->arch.ipc_channel_handle, dev_sids[i].rsrc, dev_sids[i].sid); if (sciErr != SC_ERR_NONE) { printf("set master sid error\n"); return sciErr; } } return SC_ERR_NONE; } #endif void arch_preboot_os(void) { #if defined(CONFIG_VIDEO_IMXDPUV1) imxdpuv1_fb_disable(); #endif #ifdef CONFIG_IMX_SMMU imx8_config_smmu_sid(dev_sids, ARRAY_SIZE(dev_sids)); #endif } enum boot_device get_boot_device(void) { enum boot_device boot_dev = SD1_BOOT; sc_ipc_t ipcHndl = 0; sc_rsrc_t dev_rsrc; /* Note we only support android in EMMC SDHC0 */ if (IS_ENABLED(CONFIG_XEN)) return MMC1_BOOT; ipcHndl = gd->arch.ipc_channel_handle; sc_misc_get_boot_dev(ipcHndl, &dev_rsrc); switch (dev_rsrc) { case SC_R_SDHC_0: boot_dev = MMC1_BOOT; break; case SC_R_SDHC_1: boot_dev = SD2_BOOT; break; case SC_R_SDHC_2: boot_dev = SD3_BOOT; break; case SC_R_NAND: boot_dev = NAND_BOOT; break; case SC_R_FSPI_0: boot_dev = FLEXSPI_BOOT; break; case SC_R_SATA_0: boot_dev = SATA_BOOT; break; case SC_R_USB_0: case SC_R_USB_1: case SC_R_USB_2: boot_dev = USB_BOOT; break; default: break; } return boot_dev; } bool is_usb_boot(void) { return get_boot_device() == USB_BOOT; } #if defined(CONFIG_ARCH_MISC_INIT) #define FSL_SIP_BUILDINFO 0xC2000003 #define FSL_SIP_BUILDINFO_GET_COMMITHASH 0x00 extern uint32_t _end_ofs; static void set_buildinfo_to_env(uint32_t scfw, uint32_t secofw, char *mkimage, char *atf) { if (!mkimage || !atf) return; env_set("commit_mkimage", mkimage); env_set("commit_atf", atf); env_set_hex("commit_scfw", (ulong)scfw); env_set_hex("commit_secofw", (ulong)secofw); } static void acquire_buildinfo(void) { sc_ipc_t ipc; uint32_t sc_build = 0, sc_commit = 0; uint32_t seco_build = 0, seco_commit = 0; char *mkimage_commit, *temp; uint64_t atf_commit = 0; ipc = gd->arch.ipc_channel_handle; /* Get SCFW build and commit id */ sc_misc_build_info(ipc, &sc_build, &sc_commit); if (sc_build == 0) { debug("SCFW does not support build info\n"); sc_commit = 0; /* Display 0 when the build info is not supported*/ } /* Get SECO FW build and commit id */ sc_seco_build_info(ipc, &seco_build, &seco_commit); if (seco_build == 0) { debug("SECO FW does not support build info\n"); seco_commit = 0; /* Display 0 when the build info is not supported*/ } /* Get imx-mkimage commit id. * The imx-mkimage puts the commit hash behind the end of u-boot.bin */ mkimage_commit = (char *)(ulong)(CONFIG_SYS_TEXT_BASE + _end_ofs + fdt_totalsize(gd->fdt_blob)); temp = mkimage_commit + 8; *temp = '\0'; if (strlen(mkimage_commit) == 0) { debug("IMX-MKIMAGE does not support build info\n"); mkimage_commit = "0"; /* Display 0 */ } /* Get ARM Trusted Firmware commit id */ atf_commit = call_imx_sip(FSL_SIP_BUILDINFO, FSL_SIP_BUILDINFO_GET_COMMITHASH, 0, 0, 0); if (atf_commit == 0xffffffff) { debug("ATF does not support build info\n"); atf_commit = 0x30; /* Display 0, 0 ascii is 0x30 */ } /* Set all to env */ set_buildinfo_to_env(sc_commit, seco_commit, mkimage_commit, (char *)&atf_commit); printf("\n BuildInfo: \n - SCFW %08x, SECO-FW %08x, IMX-MKIMAGE %s, ATF %s\n - %s \n\n", sc_commit, seco_commit, mkimage_commit, (char *)&atf_commit, U_BOOT_VERSION); } int arch_misc_init(void) { acquire_buildinfo(); return 0; } #endif int print_bootinfo(void) { enum boot_device bt_dev; bt_dev = get_boot_device(); puts("Boot: "); switch (bt_dev) { case SD1_BOOT: puts("SD0\n"); break; case SD2_BOOT: puts("SD1\n"); break; case SD3_BOOT: puts("SD2\n"); break; case MMC1_BOOT: puts("MMC0\n"); break; case MMC2_BOOT: puts("MMC1\n"); break; case MMC3_BOOT: puts("MMC2\n"); break; case FLEXSPI_BOOT: puts("FLEXSPI\n"); break; case SATA_BOOT: puts("SATA\n"); break; case NAND_BOOT: puts("NAND\n"); break; case USB_BOOT: puts("USB\n"); break; default: printf("Unknown device %u\n", bt_dev); break; } return 0; } #ifdef CONFIG_SERIAL_TAG #define FUSE_UNIQUE_ID_WORD0 16 #define FUSE_UNIQUE_ID_WORD1 17 void get_board_serial(struct tag_serialnr *serialnr) { sc_err_t err; sc_ipc_t ipc; uint32_t val1 = 0, val2 = 0; uint32_t word1, word2; ipc = gd->arch.ipc_channel_handle; word1 = FUSE_UNIQUE_ID_WORD0; word2 = FUSE_UNIQUE_ID_WORD1; err = sc_misc_otp_fuse_read(ipc, word1, &val1); if (err != SC_ERR_NONE) { printf("%s fuse %d read error: %d\n", __func__,word1, err); return; } err = sc_misc_otp_fuse_read(ipc, word2, &val2); if (err != SC_ERR_NONE) { printf("%s fuse %d read error: %d\n", __func__, word2, err); return; } serialnr->low = val1; serialnr->high = val2; } #endif /*CONFIG_SERIAL_TAG*/ __weak int board_mmc_get_env_dev(int devno) { return CONFIG_SYS_MMC_ENV_DEV; } int mmc_get_env_dev(void) { sc_ipc_t ipcHndl = 0; sc_rsrc_t dev_rsrc; int devno; ipcHndl = gd->arch.ipc_channel_handle; sc_misc_get_boot_dev(ipcHndl, &dev_rsrc); switch(dev_rsrc) { case SC_R_SDHC_0: devno = 0; break; case SC_R_SDHC_1: devno = 1; break; case SC_R_SDHC_2: devno = 2; break; default: /* If not boot from sd/mmc, use default value */ return env_get_ulong("mmcdev", 10, CONFIG_SYS_MMC_ENV_DEV); } return board_mmc_get_env_dev(devno); } static bool check_owned_resources_in_pd_tree(void *blob, int nodeoff, unsigned int *unowned_rsrc) { unsigned int rsrc_id; int phplen; const fdt32_t *php; /* Search the ancestors nodes in current SS power-domain tree, * if all ancestors' resources are owned, we can enable the node, * otherwise any ancestor is not owned, we should disable the node. */ do { php = fdt_getprop(blob, nodeoff, "power-domains", &phplen); if (!php) { debug(" - ignoring no power-domains\n"); break; } if (phplen != 4) { printf("ignoring %s power-domains of unexpected length %d\n", fdt_get_name(blob, nodeoff, NULL), phplen); break; } nodeoff = fdt_node_offset_by_phandle(blob, fdt32_to_cpu(*php)); rsrc_id = fdtdec_get_uint(blob, nodeoff, "reg", 0); if (rsrc_id == SC_R_NONE) { debug("%s's power domain use SC_R_NONE\n", fdt_get_name(blob, nodeoff, NULL)); break; } debug("power-domains node 0x%x, resource id %u\n", nodeoff, rsrc_id); if (!check_owned_resource(rsrc_id)) { if (unowned_rsrc != NULL) *unowned_rsrc = rsrc_id; return false; } } while (fdt_node_check_compatible(blob, nodeoff, "nxp,imx8-pd")); return true; } static int disable_fdt_node(void *blob, int nodeoffset) { int rc, ret; const char *status = "disabled"; do { rc = fdt_setprop(blob, nodeoffset, "status", status, strlen(status) + 1); if (rc) { if (rc == -FDT_ERR_NOSPACE) { ret = fdt_increase_size(blob, 512); if (ret) return ret; } } } while (rc == -FDT_ERR_NOSPACE); return rc; } static void fdt_edma_debug_int_array(u32 *array, int count, u32 stride) { #ifdef DEBUG int i; for (i = 0; i < count; i++) { printf("0x%x ", array[i]); if (i % stride == stride - 1) printf("\n"); } printf("\n"); #endif } static void fdt_edma_debug_stringlist(const char *stringlist, int length) { #ifdef DEBUG int i = 0, len; while (i < length) { printf("%s\n", stringlist); len = strlen(stringlist) + 1; i += len; stringlist += len; } printf("\n"); #endif } static void fdt_edma_swap_int_array(u32 *array, int count) { int i; for (i = 0; i < count; i++) { array[i] = cpu_to_fdt32(array[i]); } } static int fdt_edma_update_int_array(u32 *array, int count, u32 *new_array, u32 stride, int *remove_array, int remove_count) { int i = 0, j, curr = 0, new_cnt = 0; do { if (remove_count && curr == remove_array[i]) { i++; remove_count--; array += stride; } else { for (j = 0; j< stride; j++) { *new_array = *array; new_array++; array++; } new_cnt+= j; } curr++; } while ((curr * stride) < count); return new_cnt; } static int fdt_edma_update_stringlist(const char *stringlist, int stringlist_count, char *newlist, int *remove_array, int remove_count) { int i = 0, curr = 0, new_len = 0; int length; debug("fdt_edma_update_stringlist, remove_cnt %d\n", remove_count); do { if (remove_count && curr == remove_array[i]) { debug("remove %s at %d\n", stringlist, remove_array[i]); length = strlen(stringlist) + 1; stringlist += length; i++; remove_count--; } else { length = strlen(stringlist) + 1; strcpy(newlist, stringlist); debug("copy %s, %s, curr %d, len %d\n", newlist, stringlist, curr, length); stringlist += length; newlist += length; new_len += length; } curr++; } while (curr < stringlist_count); return new_len; } static int fdt_edma_get_channel_id(u32 *regs, int index, struct edma_ch_map *edma) { u32 ch_reg = regs[(index << 2) + 1]; u32 ch_reg_size = regs[(index << 2) + 3]; int ch_id = (ch_reg - edma->ch_start_regs) / ch_reg_size; if (ch_id >= edma->ch_num) return -1; return ch_id; } static void update_fdt_edma_nodes(void *blob) { struct edma_ch_map edma_qm[] = { { SC_R_DMA_0_CH0, 0x5a200000, 32, "/dma-controller@5a1f0000"}, { SC_R_DMA_1_CH0, 0x5aa00000, 32, "/dma-controller@5a9f0000"}, { SC_R_DMA_2_CH0, 0x59200000, 5, "/dma-controller@591F0000"}, { SC_R_DMA_2_CH5, 0x59250000, 27, "/dma-controller@591F0000"}, { SC_R_DMA_3_CH0, 0x59a00000, 32, "/dma-controller@599F0000"}, }; struct edma_ch_map edma_qxp[] = { { SC_R_DMA_0_CH0, 0x59200000, 32, "/dma-controller@591F0000"}, { SC_R_DMA_1_CH0, 0x59a00000, 32, "/dma-controller@599F0000"}, { SC_R_DMA_2_CH0, 0x5a200000, 5, "/dma-controller@5a1f0000"}, { SC_R_DMA_2_CH5, 0x5a250000, 27, "/dma-controller@5a1f0000"}, { SC_R_DMA_3_CH0, 0x5aa00000, 32, "/dma-controller@5a9f0000"}, }; u32 i, j, edma_size; int nodeoff, ret; struct edma_ch_map *edma_array; if (is_imx8qm()) { edma_array = edma_qm; edma_size = ARRAY_SIZE(edma_qm); } else { edma_array = edma_qxp; edma_size = ARRAY_SIZE(edma_qxp); } for (i = 0; i < edma_size; i++, edma_array++) { u32 regs[128]; u32 interrupts[96]; u32 dma_channels; int regs_count, interrupts_count, int_names_count; const char *list; int list_len, newlist_len; int remove[32]; int remove_cnt = 0; char * newlist; nodeoff = fdt_path_offset(blob, edma_array->node_path); if (nodeoff < 0) continue; /* Not found, skip it */ printf("%s, %d\n", edma_array->node_path, nodeoff); regs_count = fdtdec_get_int_array_count(blob, nodeoff, "reg", regs, 128); debug("regs_count %d\n", regs_count); if (regs_count < 0) continue; interrupts_count = fdtdec_get_int_array_count(blob, nodeoff, "interrupts", interrupts, 96); debug("interrupts_count %d\n", interrupts_count); if (interrupts_count < 0) continue; dma_channels = fdtdec_get_uint(blob, nodeoff, "dma-channels", 0); if (dma_channels == 0) continue; list = fdt_getprop(blob, nodeoff, "interrupt-names", &list_len); if (!list) continue; int_names_count = fdt_stringlist_count(blob, nodeoff, "interrupt-names"); fdt_edma_debug_int_array(regs, regs_count, 4); fdt_edma_debug_int_array(interrupts, interrupts_count, 3); fdt_edma_debug_stringlist(list, list_len); for (j = 0; j < (regs_count >> 2); j++) { int ch_id = fdt_edma_get_channel_id(regs, j, edma_array); if (ch_id < 0) continue; if (!check_owned_resource(edma_array->ch_start_rsrc + ch_id)) { printf("remove edma items %d\n", j); dma_channels--; remove[remove_cnt] = j; remove_cnt++; } } if (remove_cnt > 0) { u32 new_regs[128]; u32 new_interrupts[96]; regs_count = fdt_edma_update_int_array(regs, regs_count, new_regs, 4, remove, remove_cnt); interrupts_count = fdt_edma_update_int_array(interrupts, interrupts_count, new_interrupts, 3, remove, remove_cnt); fdt_edma_debug_int_array(new_regs, regs_count, 4); fdt_edma_debug_int_array(new_interrupts, interrupts_count, 3); fdt_edma_swap_int_array(new_regs, regs_count); fdt_edma_swap_int_array(new_interrupts, interrupts_count); /* malloc a new string list */ newlist = (char *)malloc(list_len); if (!newlist) { printf("malloc new string list failed, len=%d\n", list_len); continue; } newlist_len = fdt_edma_update_stringlist(list, int_names_count, newlist, remove, remove_cnt); fdt_edma_debug_stringlist(newlist, newlist_len); ret = fdt_setprop(blob, nodeoff, "reg", new_regs, regs_count * sizeof(u32)); if (ret) printf("fdt_setprop regs error %d\n", ret); ret = fdt_setprop(blob, nodeoff, "interrupts", new_interrupts, interrupts_count * sizeof(u32)); if (ret) printf("fdt_setprop interrupts error %d\n", ret); ret = fdt_setprop_u32(blob, nodeoff, "dma-channels", dma_channels); if (ret) printf("fdt_setprop_u32 dma-channels error %d\n", ret); ret = fdt_setprop(blob, nodeoff, "interrupt-names", newlist, newlist_len); if (ret) printf("fdt_setprop interrupt-names error %d\n", ret); free(newlist); } } } static void update_fdt_with_owned_resources(void *blob) { /* Traverses the fdt nodes, * check its power domain and use the resource id in the power domain * for checking whether it is owned by current partition */ int offset = 0, next_off; int depth = 0, next_depth; unsigned int rsrc_id; int rc; for (offset = fdt_next_node(blob, offset, &depth); offset > 0; offset = fdt_next_node(blob, offset, &depth)) { debug("Node name: %s, depth %d\n", fdt_get_name(blob, offset, NULL), depth); if (!fdtdec_get_is_enabled(blob, offset)) { debug(" - ignoring disabled device\n"); continue; } if (!fdt_node_check_compatible(blob, offset, "nxp,imx8-pd")) { /* Skip to next depth=1 node*/ next_off = offset; next_depth = depth; do { offset = next_off; depth = next_depth; next_off = fdt_next_node(blob, offset, &next_depth); if (next_off < 0 || next_depth < 1) break; debug("PD name: %s, offset %d, depth %d\n", fdt_get_name(blob, next_off, NULL), next_off, next_depth); } while (next_depth > 1); continue; } if (!check_owned_resources_in_pd_tree(blob, offset, &rsrc_id)) { /* If the resource is not owned, disable it in FDT */ rc = disable_fdt_node(blob, offset); if (!rc) printf("Disable %s, resource id %u not owned\n", fdt_get_name(blob, offset, NULL), rsrc_id); else printf("Unable to disable %s, err=%s\n", fdt_get_name(blob, offset, NULL), fdt_strerror(rc)); } } } #ifdef CONFIG_IMX_SMMU static int get_srsc_from_fdt_node_power_domain(void *blob, int device_offset) { const fdt32_t *prop; int pdnode_offset; prop = fdt_getprop(blob, device_offset, "power-domains", NULL); if (!prop) { debug("node %s has no power-domains\n", fdt_get_name(blob, device_offset, NULL)); return -ENOENT; } pdnode_offset = fdt_node_offset_by_phandle(blob, fdt32_to_cpu(*prop)); if (pdnode_offset < 0) { pr_err("failed to fetch node %s power-domain", fdt_get_name(blob, device_offset, NULL)); return pdnode_offset; } return fdtdec_get_uint(blob, pdnode_offset, "reg", -ENOENT); } static int config_smmu_resource_sid(int rsrc, int sid) { sc_err_t err; if (!check_owned_resource(rsrc)) { printf("%s rsrc[%d] not owned\n", __func__, rsrc); return -1; } err = sc_rm_set_master_sid(gd->arch.ipc_channel_handle, rsrc, sid); debug("set_master_sid rsrc=%d sid=0x%x err=%d\n", rsrc, sid, err); if (err != SC_ERR_NONE) { pr_err("fail set_master_sid rsrc=%d sid=0x%x err=%d\n", rsrc, sid, err); return -EINVAL; } return 0; } static int config_smmu_fdt_device_sid(void *blob, int device_offset, int sid) { int rsrc; int proplen; const fdt32_t *prop; const char *name = fdt_get_name(blob, device_offset, NULL); prop = fdt_getprop(blob, device_offset, "fsl,sc_rsrc_id", &proplen); if (prop) { int i; debug("configure node %s sid 0x%x for %d resources\n", name, sid, (int)(proplen / sizeof(fdt32_t))); for (i = 0; i < proplen / sizeof(fdt32_t); ++i) { config_smmu_resource_sid(fdt32_to_cpu(prop[i]), sid); } return 0; } rsrc = get_srsc_from_fdt_node_power_domain(blob, device_offset); debug("configure node %s sid 0x%x rsrc=%d\n", name, sid, rsrc); if (rsrc < 0) { debug("failed to determine SC_R_* for node %s\n", name); return rsrc; } return config_smmu_resource_sid(rsrc, sid); } /* assign master sid based on iommu properties in fdt */ static int config_smmu_fdt(void *blob) { int offset, proplen, i; const fdt32_t *prop; const char *name; /* Legacy smmu bindings, still used by xen. */ offset = fdt_node_offset_by_compatible(blob, 0, "arm,mmu-500"); if (offset > 0 && (prop = fdt_getprop(blob, offset, "mmu-masters", &proplen))) { debug("found legacy mmu-masters property\n"); for (i = 0; i < proplen / 8; ++i) { uint32_t phandle = fdt32_to_cpu(prop[2 * i]); int sid = fdt32_to_cpu(prop[2 * i + 1]); int device_offset; device_offset = fdt_node_offset_by_phandle(blob, phandle); if (device_offset < 0) { pr_err("Failed to fetch device reference from mmu_masters: %d", device_offset); continue; } config_smmu_fdt_device_sid(blob, device_offset, sid); } /* Ignore new bindings if old bindings found, just like linux. */ return 0; } /* Generic smmu bindings */ offset = 0; while ((offset = fdt_next_node(blob, offset, NULL)) > 0) { name = fdt_get_name(blob, offset, NULL); prop = fdt_getprop(blob, offset, "iommus", &proplen); if (!prop) continue; debug("node %s iommus proplen %d\n", name, proplen); if (proplen == 12) { int sid = fdt32_to_cpu(prop[1]); config_smmu_fdt_device_sid(blob, offset, sid); } else if (proplen != 4) { debug("node %s ignore unexpected iommus proplen=%d\n", name, proplen); } } return 0; } #endif #ifdef CONFIG_OF_SYSTEM_SETUP int ft_system_setup(void *blob, bd_t *bd) { #if (CONFIG_BOOTAUX_RESERVED_MEM_SIZE != 0x00) int off; off = fdt_add_mem_rsv(blob, CONFIG_BOOTAUX_RESERVED_MEM_BASE, CONFIG_BOOTAUX_RESERVED_MEM_SIZE); if (off < 0) printf("Failed to reserve memory for bootaux: %s\n", fdt_strerror(off)); #endif #ifndef CONFIG_SKIP_RESOURCE_CHECING update_fdt_with_owned_resources(blob); #endif update_fdt_edma_nodes(blob); #ifdef CONFIG_IMX_SMMU config_smmu_fdt(blob); #endif ft_add_optee_node(blob, bd); return 0; } #endif #define MEMSTART_ALIGNMENT SZ_2M /* Align the memory start with 2MB */ static int get_owned_memreg(sc_rm_mr_t mr, sc_faddr_t *addr_start, sc_faddr_t *addr_end) { sc_ipc_t ipcHndl = 0; sc_err_t sciErr = 0; bool owned; sc_faddr_t start, end; ipcHndl = gd->arch.ipc_channel_handle; if (ipcHndl) { owned = sc_rm_is_memreg_owned(ipcHndl, mr); if (owned) { sciErr = sc_rm_get_memreg_info(ipcHndl, mr, &start, &end); if (sciErr) { printf("Memreg get info failed, %d\n", sciErr); return -EINVAL; } else { debug("0x%llx -- 0x%llx\n", start, end); *addr_start = start; *addr_end = end; return 0; } } } return -EINVAL; } __weak void board_mem_get_layout(uint64_t *phys_sdram_1_start, uint64_t *phys_sdram_1_size, uint64_t *phys_sdram_2_start, uint64_t *phys_sdram_2_size) { *phys_sdram_1_start = PHYS_SDRAM_1; *phys_sdram_1_size = PHYS_SDRAM_1_SIZE; *phys_sdram_2_start = PHYS_SDRAM_2; *phys_sdram_2_size = PHYS_SDRAM_2_SIZE; } phys_size_t get_effective_memsize(void) { sc_rm_mr_t mr; sc_faddr_t start, end, start_aligned; uint64_t phys_sdram_1_start, phys_sdram_1_size; uint64_t phys_sdram_2_start, phys_sdram_2_size; int err; board_mem_get_layout(&phys_sdram_1_start, &phys_sdram_1_size, &phys_sdram_2_start, &phys_sdram_2_size); if (IS_ENABLED(CONFIG_XEN)) return phys_sdram_1_size; for (mr = 0; mr < 64; mr++) { err = get_owned_memreg(mr, &start, &end); if (!err) { start_aligned = roundup(start, MEMSTART_ALIGNMENT); if (start_aligned > end) /* Too small memory region, not use it */ continue; /* Find the memory region runs the u-boot */ if (start >= phys_sdram_1_start && start <= ((sc_faddr_t)phys_sdram_1_start + phys_sdram_1_size) && (start <= CONFIG_SYS_TEXT_BASE && CONFIG_SYS_TEXT_BASE <= end)){ if ((end + 1) <= ((sc_faddr_t)phys_sdram_1_start + phys_sdram_1_size)) return (end - phys_sdram_1_start + 1); else return phys_sdram_1_size; } } } return phys_sdram_1_size; } int dram_init(void) { sc_rm_mr_t mr; sc_faddr_t start, end; uint64_t phys_sdram_1_start, phys_sdram_1_size; uint64_t phys_sdram_2_start, phys_sdram_2_size; int err; board_mem_get_layout(&phys_sdram_1_start, &phys_sdram_1_size, &phys_sdram_2_start, &phys_sdram_2_size); if (IS_ENABLED(CONFIG_XEN)) { gd->ram_size = phys_sdram_1_size; gd->ram_size += phys_sdram_2_size; return 0; } for (mr = 0; mr < 64; mr++) { err = get_owned_memreg(mr, &start, &end); if (!err) { start = roundup(start, MEMSTART_ALIGNMENT); if (start > end) /* Too small memory region, not use it */ continue; if (start >= phys_sdram_1_start && start <= ((sc_faddr_t)phys_sdram_1_start + phys_sdram_1_size)) { if ((end + 1) <= ((sc_faddr_t)phys_sdram_1_start + phys_sdram_1_size)) gd->ram_size += end - start + 1; else gd->ram_size += ((sc_faddr_t)phys_sdram_1_start + phys_sdram_1_size) - start; } else if (start >= phys_sdram_2_start && start <= ((sc_faddr_t)phys_sdram_2_start + phys_sdram_2_size)) { if ((end + 1) <= ((sc_faddr_t)phys_sdram_2_start + phys_sdram_2_size)) gd->ram_size += end - start + 1; else gd->ram_size += ((sc_faddr_t)phys_sdram_2_start + phys_sdram_2_size) - start; } } } /* If error, set to the default value */ if (!gd->ram_size) { gd->ram_size = phys_sdram_1_size; gd->ram_size += phys_sdram_2_size; } return 0; } static void dram_bank_sort(int current_bank) { phys_addr_t start; phys_size_t size; while (current_bank > 0) { if (gd->bd->bi_dram[current_bank - 1].start > gd->bd->bi_dram[current_bank].start) { start = gd->bd->bi_dram[current_bank - 1].start; size = gd->bd->bi_dram[current_bank - 1].size; gd->bd->bi_dram[current_bank - 1].start = gd->bd->bi_dram[current_bank].start; gd->bd->bi_dram[current_bank - 1].size = gd->bd->bi_dram[current_bank].size; gd->bd->bi_dram[current_bank].start = start; gd->bd->bi_dram[current_bank].size = size; } current_bank--; } } int dram_init_banksize(void) { sc_rm_mr_t mr; sc_faddr_t start, end; uint64_t phys_sdram_1_start, phys_sdram_1_size; uint64_t phys_sdram_2_start, phys_sdram_2_size; int i = 0; int err; board_mem_get_layout(&phys_sdram_1_start, &phys_sdram_1_size, &phys_sdram_2_start, &phys_sdram_2_size); if (IS_ENABLED(CONFIG_XEN)) { gd->bd->bi_dram[0].start = phys_sdram_1_start; gd->bd->bi_dram[0].size = phys_sdram_1_size; gd->bd->bi_dram[1].start = phys_sdram_2_start; gd->bd->bi_dram[1].size = phys_sdram_2_size; return 0; } for (mr = 0; mr < 64 && i < CONFIG_NR_DRAM_BANKS; mr++) { err = get_owned_memreg(mr, &start, &end); if (!err) { start = roundup(start, MEMSTART_ALIGNMENT); if (start > end) /* Too small memory region, not use it */ continue; if (start >= phys_sdram_1_start && start <= ((sc_faddr_t)phys_sdram_1_start + phys_sdram_1_size)) { gd->bd->bi_dram[i].start = start; if ((end + 1) <= ((sc_faddr_t)phys_sdram_1_start + phys_sdram_1_size)) gd->bd->bi_dram[i].size = end - start + 1; else gd->bd->bi_dram[i].size = ((sc_faddr_t)phys_sdram_1_start + phys_sdram_1_size) - start; dram_bank_sort(i); i++; } else if (start >= phys_sdram_2_start && start <= ((sc_faddr_t)phys_sdram_2_start + phys_sdram_2_size)) { gd->bd->bi_dram[i].start = start; if ((end + 1) <= ((sc_faddr_t)phys_sdram_2_start + phys_sdram_2_size)) gd->bd->bi_dram[i].size = end - start + 1; else gd->bd->bi_dram[i].size = ((sc_faddr_t)phys_sdram_2_start + phys_sdram_2_size) - start; dram_bank_sort(i); i++; } } } /* If error, set to the default value */ if (!i) { gd->bd->bi_dram[0].start = phys_sdram_1_start; gd->bd->bi_dram[0].size = phys_sdram_1_size; gd->bd->bi_dram[1].start = phys_sdram_2_start; gd->bd->bi_dram[1].size = phys_sdram_2_size; } return 0; } static u64 get_block_attrs(sc_faddr_t addr_start) { uint64_t phys_sdram_1_start, phys_sdram_1_size; uint64_t phys_sdram_2_start, phys_sdram_2_size; board_mem_get_layout(&phys_sdram_1_start, &phys_sdram_1_size, &phys_sdram_2_start, &phys_sdram_2_size); #ifdef CONFIG_IMX_TRUSTY_OS return (PTE_BLOCK_MEMTYPE(MT_NORMAL) | PTE_BLOCK_INNER_SHARE); #else if ((addr_start >= phys_sdram_1_start && addr_start <= ((sc_faddr_t)phys_sdram_1_start + phys_sdram_1_size)) || (addr_start >= phys_sdram_2_start && addr_start <= ((sc_faddr_t)phys_sdram_2_start + phys_sdram_2_size))) return (PTE_BLOCK_MEMTYPE(MT_NORMAL) | PTE_BLOCK_OUTER_SHARE); #endif return (PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | PTE_BLOCK_NON_SHARE | PTE_BLOCK_PXN | PTE_BLOCK_UXN); } static u64 get_block_size(sc_faddr_t addr_start, sc_faddr_t addr_end) { uint64_t phys_sdram_1_start, phys_sdram_1_size; uint64_t phys_sdram_2_start, phys_sdram_2_size; board_mem_get_layout(&phys_sdram_1_start, &phys_sdram_1_size, &phys_sdram_2_start, &phys_sdram_2_size); if (addr_start >= phys_sdram_1_start && addr_start <= ((sc_faddr_t)phys_sdram_1_start + phys_sdram_1_size)) { if ((addr_end + 1) > ((sc_faddr_t)phys_sdram_1_start + phys_sdram_1_size)) return ((sc_faddr_t)phys_sdram_1_start + phys_sdram_1_size) - addr_start; } else if (addr_start >= phys_sdram_2_start && addr_start <= ((sc_faddr_t)phys_sdram_2_start + phys_sdram_2_size)) { if ((addr_end + 1) > ((sc_faddr_t)phys_sdram_2_start + phys_sdram_2_size)) return ((sc_faddr_t)phys_sdram_2_start + phys_sdram_2_size) - addr_start; } return (addr_end - addr_start + 1); } #define MAX_PTE_ENTRIES 512 #define MAX_MEM_MAP_REGIONS 16 static struct mm_region imx8_mem_map[MAX_MEM_MAP_REGIONS]; struct mm_region *mem_map = imx8_mem_map; void enable_caches(void) { sc_rm_mr_t mr; sc_faddr_t start, end; int err, i; if (IS_ENABLED(CONFIG_XEN)) { imx8_mem_map[0].virt = 0x00000000UL; imx8_mem_map[0].phys = 0x00000000UL; imx8_mem_map[0].size = 0x39000000UL; imx8_mem_map[0].attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | PTE_BLOCK_NON_SHARE | PTE_BLOCK_PXN | PTE_BLOCK_UXN; imx8_mem_map[1].virt = 0x39000000UL; imx8_mem_map[1].phys = 0x39000000UL; imx8_mem_map[1].size = 0x01000000UL; imx8_mem_map[1].attrs = (PTE_BLOCK_MEMTYPE(MT_NORMAL) | PTE_BLOCK_INNER_SHARE); imx8_mem_map[2].virt = 0x40000000UL; imx8_mem_map[2].phys = 0x40000000UL; imx8_mem_map[2].size = 0x40000000UL; imx8_mem_map[2].attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | PTE_BLOCK_NON_SHARE | PTE_BLOCK_PXN | PTE_BLOCK_UXN; imx8_mem_map[3].virt = 0x80000000UL; imx8_mem_map[3].phys = 0x80000000UL; imx8_mem_map[3].size = 0x80000000UL; imx8_mem_map[3].attrs = (PTE_BLOCK_MEMTYPE(MT_NORMAL) | PTE_BLOCK_INNER_SHARE); imx8_mem_map[4].virt = 0x100000000UL; imx8_mem_map[4].phys = 0x100000000UL; imx8_mem_map[4].size = 0x100000000UL; imx8_mem_map[4].attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | PTE_BLOCK_NON_SHARE | PTE_BLOCK_PXN | PTE_BLOCK_UXN; icache_enable(); dcache_enable(); return; } /* Create map for registers access from 0x1c000000 to 0x80000000*/ imx8_mem_map[0].virt = 0x1c000000UL; imx8_mem_map[0].phys = 0x1c000000UL; imx8_mem_map[0].size = 0x64000000UL; imx8_mem_map[0].attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | PTE_BLOCK_NON_SHARE | PTE_BLOCK_PXN | PTE_BLOCK_UXN; i = 1; #ifdef CONFIG_IMX_VSERVICE_SHARED_BUFFER imx8_mem_map[i].virt = CONFIG_IMX_VSERVICE_SHARED_BUFFER; imx8_mem_map[i].phys = CONFIG_IMX_VSERVICE_SHARED_BUFFER; imx8_mem_map[i].size = CONFIG_IMX_VSERVICE_SHARED_BUFFER_SIZE; imx8_mem_map[i].attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | PTE_BLOCK_NON_SHARE | PTE_BLOCK_PXN | PTE_BLOCK_UXN; i++; #endif for (mr = 0; mr < 64 && i < MAX_MEM_MAP_REGIONS; mr++) { err = get_owned_memreg(mr, &start, &end); if (!err) { imx8_mem_map[i].virt = start; imx8_mem_map[i].phys = start; imx8_mem_map[i].size = get_block_size(start, end); imx8_mem_map[i].attrs = get_block_attrs(start); i++; } } if (i < MAX_MEM_MAP_REGIONS) { imx8_mem_map[i].size = 0; imx8_mem_map[i].attrs = 0; } else { printf("Error, need more MEM MAP REGIONS reserved\n"); icache_enable(); return; } for (i = 0;i < MAX_MEM_MAP_REGIONS;i++) { debug("[%d] vir = 0x%llx phys = 0x%llx size = 0x%llx attrs = 0x%llx\n", i, imx8_mem_map[i].virt, imx8_mem_map[i].phys, imx8_mem_map[i].size, imx8_mem_map[i].attrs); } icache_enable(); dcache_enable(); } #ifndef CONFIG_SYS_DCACHE_OFF u64 get_page_table_size(void) { u64 one_pt = MAX_PTE_ENTRIES * sizeof(u64); u64 size = 0; /* For each memory region, the max table size: 2 level 3 tables + 2 level 2 tables + 1 level 1 table*/ size = (2 + 2 + 1) * one_pt * MAX_MEM_MAP_REGIONS + one_pt; /* * We need to duplicate our page table once to have an emergency pt to * resort to when splitting page tables later on */ size *= 2; /* * We may need to split page tables later on if dcache settings change, * so reserve up to 4 (random pick) page tables for that. */ size += one_pt * 4; return size; } #endif static bool check_device_power_off(struct udevice *dev, const char* permanent_on_devices[], int size) { int i; for (i = 0; i < size; i++) { if (!strcmp(dev->name, permanent_on_devices[i])) return false; } return true; } void power_off_pd_devices(const char* permanent_on_devices[], int size) { struct udevice *dev; struct power_domain pd; for (uclass_find_first_device(UCLASS_POWER_DOMAIN, &dev); dev; uclass_find_next_device(&dev)) { if (device_active(dev)) { /* Power off active pd devices except the permanent power on devices */ if (check_device_power_off(dev, permanent_on_devices, size)) { pd.dev = dev; power_domain_off(&pd); } } } } void disconnect_from_pc(void) { int ret; struct power_domain pd; if (!power_domain_lookup_name("conn_usb0", &pd)) { ret = power_domain_on(&pd); if (ret) { printf("conn_usb0 Power up failed! (error = %d)\n", ret); return; } writel(0x0, USB_BASE_ADDR + 0x140); ret = power_domain_off(&pd); if (ret) { printf("conn_usb0 Power off failed! (error = %d)\n", ret); return; } } else { printf("conn_usb0 finding failed!\n"); return; } } bool check_owned_udevice(struct udevice *dev) { int ret; sc_rsrc_t resource_id; struct ofnode_phandle_args args; /* Get the resource id from its power-domain */ ret = dev_read_phandle_with_args(dev, "power-domains", "#power-domain-cells", 0, 0, &args); if (ret) { printf("no power-domains found\n"); return false; } /* Get the owner partition for resource*/ resource_id = (sc_rsrc_t)ofnode_read_u32_default(args.node, "reg", SC_R_NONE); if (resource_id == SC_R_NONE) { printf("Can't find the resource id for udev %s\n", dev->name); return false; } debug("udev %s, resource id %d\n", dev->name, resource_id); return check_owned_resource(resource_id); } bool check_m4_parts_boot(void) { sc_rm_pt_t m4_parts[2]; sc_ipc_t ipc; sc_err_t err; ipc = gd->arch.ipc_channel_handle; err = sc_rm_get_resource_owner(ipc, SC_R_M4_0_PID0, &m4_parts[0]); if (err != SC_ERR_NONE) { printf("%s get resource [%d] owner error: %d\n", __func__, SC_R_M4_0_PID0, err); return false; } if (sc_pm_is_partition_started(ipc, m4_parts[0])) return true; if (is_imx8qm()) { err = sc_rm_get_resource_owner(ipc, SC_R_M4_1_PID0, &m4_parts[1]); if (err != SC_ERR_NONE) { printf("%s get resource [%d] owner error: %d\n", __func__, SC_R_M4_1_PID0, err); return false; } if (sc_pm_is_partition_started(ipc, m4_parts[1])) return true; } return false; } #ifdef CONFIG_IMX_VSERVICE struct udevice * board_imx_vservice_find_mu(struct udevice *dev) { int ret; const char *m4_mu_name[2] = { "mu@5d230000", "mu@5d240000" }; struct udevice *m4_mu[2]; sc_rm_pt_t m4_parts[2]; sc_ipc_t ipc; sc_err_t err; struct ofnode_phandle_args args; sc_rsrc_t resource_id; sc_rm_pt_t resource_part; ipc = gd->arch.ipc_channel_handle; /* Get the resource id from its power-domain */ ret = dev_read_phandle_with_args(dev, "power-domains", "#power-domain-cells", 0, 0, &args); if (ret) { printf("Can't find the power-domains property for udev %s\n", dev->name); return NULL; } /* Get the owner partition for resource*/ resource_id = (sc_rsrc_t)ofnode_read_u32_default(args.node, "reg", SC_R_NONE); if (resource_id == SC_R_NONE) { printf("Can't find the resource id for udev %s\n", dev->name); return NULL; } err = sc_rm_get_resource_owner(ipc, resource_id, &resource_part); if (err != SC_ERR_NONE) { printf("%s get resource [%d] owner error: %d\n", __func__, resource_id, err); return NULL; } debug("udev %s, resource id %d, resource part %d\n", dev->name, resource_id, resource_part); /* MU8 for communication between M4_0 and u-boot, MU9 for M4_1 and u-boot */ err = sc_rm_get_resource_owner(ipc, SC_R_M4_0_PID0, &m4_parts[0]); if (err != SC_ERR_NONE) { printf("%s get resource [%d] owner error: %d\n", __func__, SC_R_M4_0_PID0, err); return NULL; } ret = uclass_find_device_by_name(UCLASS_MISC, m4_mu_name[0], &m4_mu[0]); if (!ret) { /* If the i2c is in m4_0 partition, return the mu8 */ if (resource_part == m4_parts[0]) return m4_mu[0]; } if (is_imx8qm()) { err = sc_rm_get_resource_owner(ipc, SC_R_M4_1_PID0, &m4_parts[1]); if (err != SC_ERR_NONE) { printf("%s get resource [%d] owner error: %d\n", __func__, SC_R_M4_1_PID0, err); return NULL; } ret = uclass_find_device_by_name(UCLASS_MISC, m4_mu_name[1], &m4_mu[1]); if (!ret) { /* If the i2c is in m4_1 partition, return the mu9 */ if (resource_part == m4_parts[1]) return m4_mu[1]; } } return NULL; } void * board_imx_vservice_get_buffer(struct imx_vservice_channel *node, u32 size) { const char *m4_mu_name[2] = { "mu@5d230000", "mu@5d240000" }; /* Each MU ownes 1M buffer */ if (size <= 0x100000) { if (!strcmp(node->mu_dev->name, m4_mu_name[0])) return (void * )CONFIG_IMX_VSERVICE_SHARED_BUFFER; else if (!strcmp(node->mu_dev->name, m4_mu_name[1])) return (void * )(CONFIG_IMX_VSERVICE_SHARED_BUFFER + 0x100000); else return NULL; } return NULL; } #endif /* imx8qxp i2c1 has lots of devices may used by both M4 and A core * If A core partition does not own the resource, we will start * virtual i2c driver. Otherwise use local i2c driver. */ int board_imx_virt_i2c_bind(struct udevice *dev) { if (check_owned_udevice(dev)) return -ENODEV; return 0; } int board_imx_lpi2c_bind(struct udevice *dev) { if (check_owned_udevice(dev)) return 0; return -ENODEV; } void board_boot_order(u32 *spl_boot_list) { spl_boot_list[0] = spl_boot_device(); if (spl_boot_list[0] == BOOT_DEVICE_SPI) { /* Check whether we own the flexspi0, if not, use NOR boot */ if (!check_owned_resource(SC_R_FSPI_0)) spl_boot_list[0] = BOOT_DEVICE_NOR; } }