| // SPDX-License-Identifier: GPL-2.0+ |
| /* |
| * Copyright 2017-2019 NXP |
| */ |
| |
| #include <common.h> |
| #include <clk.h> |
| #include <cpu.h> |
| #include <dm.h> |
| #include <dm/device-internal.h> |
| #include <dm/lists.h> |
| #include <dm/uclass.h> |
| #include <errno.h> |
| #include <asm/arch/sci/sci.h> |
| #include <power-domain.h> |
| #include <dm/device.h> |
| #include <dm/uclass-internal.h> |
| #include <thermal.h> |
| #include <elf.h> |
| #include <asm/arch/sid.h> |
| #include <asm/arch/sys_proto.h> |
| #include <asm/arch-imx/cpu.h> |
| #include <asm/armv8/cpu.h> |
| #include <asm/armv8/mmu.h> |
| #include <asm/setup.h> |
| #include <asm/mach-imx/boot_mode.h> |
| #include <asm/arch/video_common.h> |
| #include <linux/libfdt.h> |
| #include <fdt_support.h> |
| #include <fdtdec.h> |
| #include <generated/version_autogenerated.h> |
| #include <asm/mach-imx/imx_vservice.h> |
| #include <asm/arch/power-domain.h> |
| #include <spl.h> |
| |
| 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; |
| }; |
| |
| #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) |
| { |
| #if defined(CONFIG_SPL_BUILD) && defined(CONFIG_RECOVER_SPL_DATA_SECTION) |
| save_restore_data(); |
| #endif |
| |
| return 0; |
| } |
| |
| int arch_cpu_init_dm(void) |
| { |
| struct udevice *devp; |
| int node, ret; |
| |
| node = fdt_node_offset_by_compatible(gd->fdt_blob, -1, "fsl,imx8-mu"); |
| |
| ret = uclass_get_device_by_of_offset(UCLASS_MISC, node, &devp); |
| if (ret) { |
| printf("could not get scu %d\n", ret); |
| return ret; |
| } |
| |
| struct pass_over_info_t *pass_over; |
| |
| if (is_soc_rev(CHIP_REV_A)) { |
| pass_over = get_pass_over_info(); |
| if (pass_over && pass_over->g_ap_mu == 0) { |
| /* |
| * When ap_mu is 0, means the U-Boot booted |
| * from first container |
| */ |
| sc_misc_boot_status(-1, SC_MISC_BOOT_STATUS_SUCCESS); |
| } |
| } |
| |
| #ifdef CONFIG_IMX_SMMU |
| int sciErr; |
| sciErr = sc_pm_set_resource_power_mode(-1, SC_R_SMMU, |
| SC_PM_PW_MODE_ON); |
| if (sciErr) |
| return 0; |
| #endif |
| |
| return 0; |
| } |
| |
| #ifdef CONFIG_IMX_BOOTAUX |
| |
| #ifdef CONFIG_IMX8QM |
| int arch_auxiliary_core_up(u32 core_id, ulong boot_private_data) |
| { |
| sc_rsrc_t core_rsrc, mu_rsrc; |
| sc_faddr_t tcml_addr; |
| u32 tcm_size = SZ_256K; /* TCML + TCMU */ |
| ulong addr; |
| |
| |
| 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(-1, core_rsrc, SC_PM_PW_MODE_ON) != SC_ERR_NONE) |
| return -EIO; |
| |
| if (sc_pm_set_resource_power_mode(-1, 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(-1, 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_rsrc_t core_rsrc, mu_rsrc = -1; |
| sc_faddr_t aux_core_ram; |
| u32 size; |
| ulong addr; |
| |
| 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(-1, core_rsrc, SC_PM_PW_MODE_ON) != SC_ERR_NONE) |
| return -EIO; |
| |
| if (mu_rsrc != -1) { |
| if (sc_pm_set_resource_power_mode(-1, 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(-1, core_rsrc, SC_PM_CLK_PER, true, false) != SC_ERR_NONE) { |
| printf("Error enable clock\n"); |
| return -EIO; |
| } |
| |
| 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; |
| } |
| } |
| } |
| |
| 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(-1, 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; |
| |
| 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(-1, 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) |
| { |
| bool owned; |
| |
| owned = sc_rm_is_resource_owned(-1, rsrc_id); |
| |
| return owned; |
| } |
| |
| #ifdef CONFIG_IMX_SMMU |
| struct smmu_sid dev_sids[] = { |
| }; |
| |
| int imx8_config_smmu_sid(struct smmu_sid *dev_sids, int size) |
| { |
| int i; |
| int sciErr = 0; |
| |
| if ((dev_sids == NULL) || (size <= 0)) |
| return 0; |
| |
| 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(-1, |
| dev_sids[i].rsrc, |
| dev_sids[i].sid); |
| if (sciErr) { |
| printf("set master sid error\n"); |
| return sciErr; |
| } |
| } |
| |
| return 0; |
| } |
| #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 |
| } |
| |
| #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) |
| { |
| 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; |
| |
| /* Get SCFW build and commit id */ |
| sc_misc_build_info(-1, &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(-1, &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 = get_boot_device(); |
| |
| puts("Boot: "); |
| switch (bt_dev) { |
| case SD1_BOOT: |
| puts("SD0\n"); |
| break; |
| case SD2_BOOT: |
| puts("SD1\n"); |
| break; |
| case SD3_BOOT: |
| puts("SD2\n"); |
| break; |
| case MMC1_BOOT: |
| puts("MMC0\n"); |
| break; |
| case MMC2_BOOT: |
| puts("MMC1\n"); |
| break; |
| case MMC3_BOOT: |
| puts("MMC2\n"); |
| break; |
| case FLEXSPI_BOOT: |
| puts("FLEXSPI\n"); |
| break; |
| case SATA_BOOT: |
| puts("SATA\n"); |
| break; |
| case NAND_BOOT: |
| puts("NAND\n"); |
| break; |
| case USB_BOOT: |
| puts("USB\n"); |
| break; |
| default: |
| printf("Unknown device %u\n", bt_dev); |
| break; |
| } |
| |
| return 0; |
| } |
| |
| enum boot_device get_boot_device(void) |
| { |
| enum boot_device boot_dev = SD1_BOOT; |
| |
| sc_rsrc_t dev_rsrc; |
| |
| sc_misc_get_boot_dev(-1, &dev_rsrc); |
| |
| switch (dev_rsrc) { |
| case SC_R_SDHC_0: |
| boot_dev = MMC1_BOOT; |
| break; |
| case SC_R_SDHC_1: |
| boot_dev = SD2_BOOT; |
| break; |
| case SC_R_SDHC_2: |
| boot_dev = SD3_BOOT; |
| break; |
| case SC_R_NAND: |
| boot_dev = NAND_BOOT; |
| break; |
| case SC_R_FSPI_0: |
| boot_dev = FLEXSPI_BOOT; |
| break; |
| case SC_R_SATA_0: |
| boot_dev = SATA_BOOT; |
| break; |
| case SC_R_USB_0: |
| case SC_R_USB_1: |
| case SC_R_USB_2: |
| boot_dev = USB_BOOT; |
| break; |
| default: |
| break; |
| } |
| |
| return boot_dev; |
| } |
| |
| bool is_usb_boot(void) |
| { |
| return get_boot_device() == USB_BOOT; |
| } |
| |
| #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; |
| uint32_t val1 = 0, val2 = 0; |
| uint32_t word1, word2; |
| |
| word1 = FUSE_UNIQUE_ID_WORD0; |
| word2 = FUSE_UNIQUE_ID_WORD1; |
| |
| err = sc_misc_otp_fuse_read(-1, word1, &val1); |
| if (err != SC_ERR_NONE) { |
| printf("%s fuse %d read error: %d\n", __func__,word1, err); |
| return; |
| } |
| |
| err = sc_misc_otp_fuse_read(-1, word2, &val2); |
| if (err != SC_ERR_NONE) { |
| printf("%s fuse %d read error: %d\n", __func__, word2, err); |
| return; |
| } |
| serialnr->low = val1; |
| serialnr->high = val2; |
| } |
| #endif /*CONFIG_SERIAL_TAG*/ |
| |
| __weak int board_mmc_get_env_dev(int devno) |
| { |
| return devno; |
| } |
| |
| int mmc_get_env_dev(void) |
| { |
| sc_rsrc_t dev_rsrc; |
| int devno; |
| |
| sc_misc_get_boot_dev(-1, &dev_rsrc); |
| |
| switch (dev_rsrc) { |
| case SC_R_SDHC_0: |
| devno = 0; |
| break; |
| case SC_R_SDHC_1: |
| devno = 1; |
| break; |
| case SC_R_SDHC_2: |
| devno = 2; |
| break; |
| default: |
| /* If not boot from sd/mmc, use default value */ |
| return 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) |
| { |
| int err; |
| |
| if (!check_owned_resource(rsrc)) { |
| printf("%s rsrc[%d] not owned\n", __func__, rsrc); |
| return -1; |
| } |
| err = sc_rm_set_master_sid(-1, rsrc, sid); |
| debug("set_master_sid rsrc=%d sid=0x%x err=%d\n", rsrc, sid, err); |
| if (err != SC_ERR_NONE) { |
| 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 |
| static int ft_add_optee_node(void *fdt, bd_t *bd) |
| { |
| const char *path, *subpath; |
| int offs; |
| |
| /* |
| * No TEE space allocated indicating no TEE running, so no |
| * need to add optee node in dts |
| */ |
| if (!rom_pointer[1]) |
| return 0; |
| |
| offs = fdt_increase_size(fdt, 512); |
| if (offs) { |
| printf("No Space for dtb\n"); |
| return 1; |
| } |
| |
| path = "/firmware"; |
| offs = fdt_path_offset(fdt, path); |
| if (offs < 0) { |
| path = "/"; |
| offs = fdt_path_offset(fdt, path); |
| |
| if (offs < 0) { |
| printf("Could not find root node.\n"); |
| return 1; |
| } |
| |
| subpath = "firmware"; |
| offs = fdt_add_subnode(fdt, offs, subpath); |
| if (offs < 0) { |
| printf("Could not create %s node.\n", subpath); |
| } |
| } |
| |
| subpath = "optee"; |
| offs = fdt_add_subnode(fdt, offs, subpath); |
| if (offs < 0) { |
| printf("Could not create %s node.\n", subpath); |
| } |
| |
| fdt_setprop_string(fdt, offs, "compatible", "linaro,optee-tz"); |
| fdt_setprop_string(fdt, offs, "method", "smc"); |
| |
| return 0; |
| } |
| |
| 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 |
| |
| update_fdt_with_owned_resources(blob); |
| |
| 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_faddr_t start, end; |
| int ret; |
| bool owned; |
| |
| owned = sc_rm_is_memreg_owned(-1, mr); |
| if (owned) { |
| ret = sc_rm_get_memreg_info(-1, mr, &start, &end); |
| if (ret) { |
| printf("Memreg get info failed, %d\n", ret); |
| return -EINVAL; |
| } |
| debug("0x%llx -- 0x%llx\n", start, end); |
| *addr_start = start; |
| *addr_end = end; |
| |
| return 0; |
| } |
| |
| return -EINVAL; |
| } |
| |
| phys_size_t get_effective_memsize(void) |
| { |
| sc_rm_mr_t mr; |
| sc_faddr_t start, end, end1, start_aligned; |
| int err; |
| |
| end1 = (sc_faddr_t)PHYS_SDRAM_1 + PHYS_SDRAM_1_SIZE; |
| |
| for (mr = 0; mr < 64; mr++) { |
| err = get_owned_memreg(mr, &start, &end); |
| if (!err) { |
| start_aligned = roundup(start, MEMSTART_ALIGNMENT); |
| /* Too small memory region, not use it */ |
| if (start_aligned > end) |
| continue; |
| |
| /* Find the memory region runs the U-Boot */ |
| if (start >= PHYS_SDRAM_1 && start <= end1 && |
| (start <= CONFIG_SYS_TEXT_BASE && |
| end >= CONFIG_SYS_TEXT_BASE)) { |
| if ((end + 1) <= ((sc_faddr_t)PHYS_SDRAM_1 + |
| PHYS_SDRAM_1_SIZE)) |
| return (end - PHYS_SDRAM_1 + 1); |
| else |
| return PHYS_SDRAM_1_SIZE; |
| } |
| } |
| } |
| |
| return PHYS_SDRAM_1_SIZE; |
| } |
| |
| int dram_init(void) |
| { |
| sc_rm_mr_t mr; |
| sc_faddr_t start, end, end1, end2; |
| int err; |
| |
| end1 = (sc_faddr_t)PHYS_SDRAM_1 + PHYS_SDRAM_1_SIZE; |
| end2 = (sc_faddr_t)PHYS_SDRAM_2 + PHYS_SDRAM_2_SIZE; |
| for (mr = 0; mr < 64; mr++) { |
| err = get_owned_memreg(mr, &start, &end); |
| if (!err) { |
| start = roundup(start, MEMSTART_ALIGNMENT); |
| /* Too small memory region, not use it */ |
| if (start > end) |
| continue; |
| |
| if (start >= PHYS_SDRAM_1 && start <= end1) { |
| if ((end + 1) <= end1) |
| gd->ram_size += end - start + 1; |
| else |
| gd->ram_size += end1 - start; |
| } else if (start >= PHYS_SDRAM_2 && start <= end2) { |
| if ((end + 1) <= end2) |
| gd->ram_size += end - start + 1; |
| else |
| gd->ram_size += end2 - start; |
| } |
| } |
| } |
| |
| /* If error, set to the default value */ |
| if (!gd->ram_size) { |
| gd->ram_size = PHYS_SDRAM_1_SIZE; |
| gd->ram_size += PHYS_SDRAM_2_SIZE; |
| } |
| return 0; |
| } |
| |
| static void dram_bank_sort(int current_bank) |
| { |
| phys_addr_t start; |
| phys_size_t size; |
| |
| while (current_bank > 0) { |
| if (gd->bd->bi_dram[current_bank - 1].start > |
| gd->bd->bi_dram[current_bank].start) { |
| start = gd->bd->bi_dram[current_bank - 1].start; |
| size = gd->bd->bi_dram[current_bank - 1].size; |
| |
| gd->bd->bi_dram[current_bank - 1].start = |
| gd->bd->bi_dram[current_bank].start; |
| gd->bd->bi_dram[current_bank - 1].size = |
| gd->bd->bi_dram[current_bank].size; |
| |
| gd->bd->bi_dram[current_bank].start = start; |
| gd->bd->bi_dram[current_bank].size = size; |
| } |
| current_bank--; |
| } |
| } |
| |
| int dram_init_banksize(void) |
| { |
| sc_rm_mr_t mr; |
| sc_faddr_t start, end, end1, end2; |
| int i = 0; |
| int err; |
| |
| end1 = (sc_faddr_t)PHYS_SDRAM_1 + PHYS_SDRAM_1_SIZE; |
| end2 = (sc_faddr_t)PHYS_SDRAM_2 + PHYS_SDRAM_2_SIZE; |
| |
| for (mr = 0; mr < 64 && i < CONFIG_NR_DRAM_BANKS; mr++) { |
| err = get_owned_memreg(mr, &start, &end); |
| if (!err) { |
| start = roundup(start, MEMSTART_ALIGNMENT); |
| if (start > end) /* Small memory region, no use it */ |
| continue; |
| |
| if (start >= PHYS_SDRAM_1 && start <= end1) { |
| gd->bd->bi_dram[i].start = start; |
| |
| if ((end + 1) <= end1) |
| gd->bd->bi_dram[i].size = |
| end - start + 1; |
| else |
| gd->bd->bi_dram[i].size = end1 - start; |
| |
| dram_bank_sort(i); |
| i++; |
| } else if (start >= PHYS_SDRAM_2 && start <= end2) { |
| gd->bd->bi_dram[i].start = start; |
| |
| if ((end + 1) <= end2) |
| gd->bd->bi_dram[i].size = |
| end - start + 1; |
| else |
| gd->bd->bi_dram[i].size = end2 - start; |
| |
| dram_bank_sort(i); |
| i++; |
| } |
| } |
| } |
| |
| /* If error, set to the default value */ |
| if (!i) { |
| gd->bd->bi_dram[0].start = PHYS_SDRAM_1; |
| gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE; |
| gd->bd->bi_dram[1].start = PHYS_SDRAM_2; |
| gd->bd->bi_dram[1].size = PHYS_SDRAM_2_SIZE; |
| } |
| |
| return 0; |
| } |
| |
| static u64 get_block_attrs(sc_faddr_t addr_start) |
| { |
| u64 attr = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | PTE_BLOCK_NON_SHARE | |
| PTE_BLOCK_PXN | PTE_BLOCK_UXN; |
| |
| if ((addr_start >= PHYS_SDRAM_1 && |
| addr_start <= ((sc_faddr_t)PHYS_SDRAM_1 + PHYS_SDRAM_1_SIZE)) || |
| (addr_start >= PHYS_SDRAM_2 && |
| addr_start <= ((sc_faddr_t)PHYS_SDRAM_2 + PHYS_SDRAM_2_SIZE))) |
| #ifdef CONFIG_IMX_TRUSTY_OS |
| return (PTE_BLOCK_MEMTYPE(MT_NORMAL) | PTE_BLOCK_INNER_SHARE); |
| #else |
| return (PTE_BLOCK_MEMTYPE(MT_NORMAL) | PTE_BLOCK_OUTER_SHARE); |
| #endif |
| |
| return attr; |
| } |
| |
| static u64 get_block_size(sc_faddr_t addr_start, sc_faddr_t addr_end) |
| { |
| sc_faddr_t end1, end2; |
| |
| end1 = (sc_faddr_t)PHYS_SDRAM_1 + PHYS_SDRAM_1_SIZE; |
| end2 = (sc_faddr_t)PHYS_SDRAM_2 + PHYS_SDRAM_2_SIZE; |
| |
| if (addr_start >= PHYS_SDRAM_1 && addr_start <= end1) { |
| if ((addr_end + 1) > end1) |
| return end1 - addr_start; |
| } else if (addr_start >= PHYS_SDRAM_2 && addr_start <= end2) { |
| if ((addr_end + 1) > end2) |
| return end2 - addr_start; |
| } |
| |
| return (addr_end - addr_start + 1); |
| } |
| |
| #define MAX_PTE_ENTRIES 512 |
| #define MAX_MEM_MAP_REGIONS 16 |
| |
| static struct mm_region imx8_mem_map[MAX_MEM_MAP_REGIONS]; |
| struct mm_region *mem_map = imx8_mem_map; |
| |
| void enable_caches(void) |
| { |
| sc_rm_mr_t mr; |
| sc_faddr_t start, end; |
| int err, i; |
| |
| /* Create map for registers access from 0x1c000000 to 0x80000000*/ |
| imx8_mem_map[0].virt = 0x1c000000UL; |
| imx8_mem_map[0].phys = 0x1c000000UL; |
| imx8_mem_map[0].size = 0x64000000UL; |
| imx8_mem_map[0].attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | |
| PTE_BLOCK_NON_SHARE | PTE_BLOCK_PXN | PTE_BLOCK_UXN; |
| |
| i = 1; |
| |
| #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 { |
| puts("Error, need more MEM MAP REGIONS reserved\n"); |
| icache_enable(); |
| return; |
| } |
| |
| for (i = 0; i < MAX_MEM_MAP_REGIONS; i++) { |
| debug("[%d] vir = 0x%llx phys = 0x%llx size = 0x%llx attrs = 0x%llx\n", |
| i, imx8_mem_map[i].virt, imx8_mem_map[i].phys, |
| imx8_mem_map[i].size, imx8_mem_map[i].attrs); |
| } |
| |
| icache_enable(); |
| dcache_enable(); |
| } |
| |
| #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 |
| |
| #if defined(CONFIG_IMX8QM) |
| #define FUSE_MAC0_WORD0 452 |
| #define FUSE_MAC0_WORD1 453 |
| #define FUSE_MAC1_WORD0 454 |
| #define FUSE_MAC1_WORD1 455 |
| #elif defined(CONFIG_IMX8QXP) |
| #define FUSE_MAC0_WORD0 708 |
| #define FUSE_MAC0_WORD1 709 |
| #define FUSE_MAC1_WORD0 710 |
| #define FUSE_MAC1_WORD1 711 |
| #endif |
| |
| void imx_get_mac_from_fuse(int dev_id, unsigned char *mac) |
| { |
| u32 word[2], val[2] = {}; |
| int i, ret; |
| |
| if (dev_id == 0) { |
| word[0] = FUSE_MAC0_WORD0; |
| word[1] = FUSE_MAC0_WORD1; |
| } else { |
| word[0] = FUSE_MAC1_WORD0; |
| word[1] = FUSE_MAC1_WORD1; |
| } |
| |
| for (i = 0; i < 2; i++) { |
| ret = sc_misc_otp_fuse_read(-1, word[i], &val[i]); |
| if (ret < 0) |
| goto err; |
| } |
| |
| mac[0] = val[0]; |
| mac[1] = val[0] >> 8; |
| mac[2] = val[0] >> 16; |
| mac[3] = val[0] >> 24; |
| mac[4] = val[1]; |
| mac[5] = val[1] >> 8; |
| |
| debug("%s: MAC%d: %02x.%02x.%02x.%02x.%02x.%02x\n", |
| __func__, dev_id, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); |
| return; |
| err: |
| printf("%s: fuse %d, err: %d\n", __func__, word[i], ret); |
| } |
| |
| u32 get_cpu_rev(void) |
| { |
| u32 id = 0, rev = 0; |
| int ret; |
| |
| ret = sc_misc_get_control(-1, SC_R_SYSTEM, SC_C_ID, &id); |
| if (ret) |
| return 0; |
| |
| rev = (id >> 5) & 0xf; |
| id = (id & 0x1f) + MXC_SOC_IMX8; /* Dummy ID for chip */ |
| |
| return (id << 12) | rev; |
| } |
| |
| void 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; |
| } |
| } |
| |
| #if CONFIG_IS_ENABLED(CPU) |
| struct cpu_imx_platdata { |
| const char *name; |
| const char *rev; |
| const char *type; |
| u32 cpurev; |
| u32 freq_mhz; |
| u32 mpidr; |
| }; |
| |
| const char *get_imx8_type(u32 imxtype) |
| { |
| switch (imxtype) { |
| case MXC_CPU_IMX8QM: |
| return "QM"; /* i.MX8 Quad MAX */ |
| case MXC_CPU_IMX8QXP: |
| case MXC_CPU_IMX8QXP_A0: |
| return "QXP"; |
| default: |
| return "??"; |
| } |
| } |
| |
| const char *get_imx8_rev(u32 rev) |
| { |
| switch (rev) { |
| case CHIP_REV_A: |
| return "A"; |
| case CHIP_REV_B: |
| return "B"; |
| default: |
| return "?"; |
| } |
| } |
| |
| const char *get_core_name(struct udevice *dev) |
| { |
| const void *blob = gd->fdt_blob; |
| int node = dev_of_offset(dev); |
| |
| if (!fdt_node_check_compatible(blob, node, "arm,cortex-a35")) |
| return "A35"; |
| else if (!fdt_node_check_compatible(blob, node, "arm,cortex-a53")) |
| return "A53"; |
| else if (!fdt_node_check_compatible(blob, node, "arm,cortex-a72")) |
| return "A72"; |
| else |
| return "?"; |
| } |
| |
| int cpu_imx_get_desc(struct udevice *dev, char *buf, int size) |
| { |
| struct cpu_imx_platdata *plat = dev_get_platdata(dev); |
| int len; |
| |
| if (size < 100) |
| return -ENOSPC; |
| |
| len = snprintf(buf, size, "NXP i.MX8%s Rev%s %s at %u MHz", |
| plat->type, plat->rev, plat->name, plat->freq_mhz); |
| |
| #if defined(CONFIG_IMX_SC_THERMAL) |
| struct udevice *thermal_dev; |
| int cpu_tmp, ret; |
| |
| if (!strcmp(plat->name, "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) |
| len += snprintf(buf + len, size," at %dC", cpu_tmp); |
| else |
| len += snprintf(buf + len, size," - invalid sensor data"); |
| } else { |
| len += snprintf(buf + len, size, " - invalid sensor device"); |
| } |
| #endif |
| |
| len += snprintf(buf + len, size, "\n"); |
| |
| return 0; |
| } |
| |
| static int cpu_imx_get_info(struct udevice *dev, struct cpu_info *info) |
| { |
| struct cpu_imx_platdata *plat = dev_get_platdata(dev); |
| |
| info->cpu_freq = plat->freq_mhz * 1000; |
| info->features = BIT(CPU_FEAT_L1_CACHE) | BIT(CPU_FEAT_MMU); |
| return 0; |
| } |
| |
| static int cpu_imx_get_count(struct udevice *dev) |
| { |
| if (is_imx8qxp()) |
| return 4; |
| else |
| return 6; |
| } |
| |
| static int cpu_imx_get_vendor(struct udevice *dev, char *buf, int size) |
| { |
| snprintf(buf, size, "NXP"); |
| return 0; |
| } |
| |
| static bool cpu_imx_is_current(struct udevice *dev) |
| { |
| struct cpu_imx_platdata *plat = dev_get_platdata(dev); |
| |
| if (plat->mpidr == (read_mpidr() & 0xffff)) |
| return true; |
| |
| return false; |
| } |
| |
| static const struct cpu_ops cpu_imx8_ops = { |
| .get_desc = cpu_imx_get_desc, |
| .get_info = cpu_imx_get_info, |
| .get_count = cpu_imx_get_count, |
| .get_vendor = cpu_imx_get_vendor, |
| .is_current_cpu = cpu_imx_is_current, |
| }; |
| |
| static const struct udevice_id cpu_imx8_ids[] = { |
| { .compatible = "arm,cortex-a35" }, |
| { .compatible = "arm,cortex-a53" }, |
| { .compatible = "arm,cortex-a72" }, |
| { } |
| }; |
| |
| static int imx8_cpu_probe(struct udevice *dev) |
| { |
| struct cpu_imx_platdata *plat = dev_get_platdata(dev); |
| struct clk cpu_clk; |
| u32 cpurev; |
| int ret; |
| |
| cpurev = get_cpu_rev(); |
| plat->cpurev = cpurev; |
| plat->name = get_core_name(dev); |
| plat->rev = get_imx8_rev(cpurev & 0xFFF); |
| plat->type = get_imx8_type((cpurev & 0xFF000) >> 12); |
| |
| plat->mpidr = dev_read_addr(dev); |
| if (plat->mpidr == FDT_ADDR_T_NONE) { |
| printf("%s: Failed to get CPU reg property\n", __func__); |
| return -EINVAL; |
| } |
| |
| ret = clk_get_by_index(dev, 0, &cpu_clk); |
| if (ret) { |
| debug("%s: Failed to get CPU clk: %d\n", __func__, ret); |
| return 0; |
| } |
| |
| plat->freq_mhz = clk_get_rate(&cpu_clk) / 1000000; |
| return 0; |
| } |
| |
| U_BOOT_DRIVER(cpu_imx8_drv) = { |
| .name = "imx8x_cpu", |
| .id = UCLASS_CPU, |
| .of_match = cpu_imx8_ids, |
| .ops = &cpu_imx8_ops, |
| .probe = imx8_cpu_probe, |
| .platdata_auto_alloc_size = sizeof(struct cpu_imx_platdata), |
| .flags = DM_FLAG_PRE_RELOC, |
| }; |
| #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); |
| } |
| } |
| } |
| } |
| |
| 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]; |
| int err; |
| |
| err = sc_rm_get_resource_owner(-1, 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(-1, m4_parts[0])) |
| return true; |
| |
| if (is_imx8qm()) { |
| err = sc_rm_get_resource_owner(-1, SC_R_M4_1_PID0, &m4_parts[1]); |
| if (err != 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(-1, 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]; |
| int err; |
| struct ofnode_phandle_args args; |
| sc_rsrc_t resource_id; |
| sc_rm_pt_t resource_part; |
| |
| /* 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(-1, 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(-1, 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(-1, 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; |
| } |
| } |