/* SPDX-License-Identifier: GPL-2.0+ */ /* * Copyright (c) 2017 Tuomas Tynkkynen */ #include #include #include #include #include #include #include #include #include #include "qemu-sbsa.h" /* Assigned in lowlevel_init.S * Push the variable into the .data section so that it * does not get cleared later. */ unsigned long __section(".data") fw_dtb_pointer; static struct mm_region qemu_sbsa_mem_map[] = { { /* Secure flash */ .virt = SBSA_SECURE_FLASH_BASE_ADDR, .phys = SBSA_SECURE_FLASH_BASE_ADDR, .size = SBSA_SECURE_FLASH_LENGTH, .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | PTE_BLOCK_INNER_SHARE | PTE_BLOCK_PXN | PTE_BLOCK_UXN }, { /* Flash */ .virt = SBSA_FLASH_BASE_ADDR, .phys = SBSA_FLASH_BASE_ADDR, .size = SBSA_FLASH_LENGTH, .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | PTE_BLOCK_INNER_SHARE }, { /* Lowmem peripherals */ .virt = SBSA_PERIPH_BASE_ADDR, .phys = SBSA_PERIPH_BASE_ADDR, .size = SBSA_PCIE_MMIO_BASE_ADDR - SBSA_PERIPH_BASE_ADDR, .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | PTE_BLOCK_NON_SHARE | PTE_BLOCK_PXN | PTE_BLOCK_UXN }, { /* 32-bit address PCIE MMIO space */ .virt = SBSA_PCIE_MMIO_BASE_ADDR, .phys = SBSA_PCIE_MMIO_BASE_ADDR, .size = SBSA_PCIE_MMIO_LENGTH, .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | PTE_BLOCK_NON_SHARE | PTE_BLOCK_PXN | PTE_BLOCK_UXN }, { /* PCI-E ECAM memory area */ .virt = SBSA_PCIE_ECAM_BASE_ADDR, .phys = SBSA_PCIE_ECAM_BASE_ADDR, .size = SBSA_PCIE_ECAM_LENGTH, .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | PTE_BLOCK_NON_SHARE | PTE_BLOCK_PXN | PTE_BLOCK_UXN }, { /* Highmem PCI-E MMIO memory area */ .virt = SBSA_PCIE_MMIO_HIGH_BASE_ADDR, .phys = SBSA_PCIE_MMIO_HIGH_BASE_ADDR, .size = SBSA_PCIE_MMIO_HIGH_LENGTH, .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | PTE_BLOCK_NON_SHARE | PTE_BLOCK_PXN | PTE_BLOCK_UXN }, { /* DRAM */ .virt = SBSA_MEM_BASE_ADDR, .phys = SBSA_MEM_BASE_ADDR, .size = 0x800000000000ULL, .attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) | PTE_BLOCK_INNER_SHARE }, { /* List terminator */ 0, } }; struct mm_region *mem_map = qemu_sbsa_mem_map; int board_late_init(void) { /* start usb so that usb keyboard can be used as input device */ if (CONFIG_IS_ENABLED(USB_KEYBOARD)) usb_init(); return 0; } int board_init(void) { return 0; } /** * dtb_dt_qemu - Return the address of the QEMU provided FDT. * * @return: Pointer to FDT or NULL on failure */ static void *dtb_dt_qemu(void) { /* FDT might be at start of DRAM */ if (fdt_magic(SBSA_MEM_BASE_ADDR) == FDT_MAGIC) return (void *)(u64)SBSA_MEM_BASE_ADDR; /* When ARM_LINUX_KERNEL_AS_BL33 is enabled in ATF, it's passed in x0 */ if (fw_dtb_pointer >= SBSA_MEM_BASE_ADDR && fdt_magic(fw_dtb_pointer) == FDT_MAGIC) { return (void *)fw_dtb_pointer; } return NULL; } /* * QEMU doesn't set compatible on cpus. * Add them to make sure the U-Boot driver properly bind. */ static int fdtdec_fix_cpus(void *fdt_blob) { int cpus_offset, off, ret; u64 mpidr, i = 0; cpus_offset = fdt_path_offset(fdt_blob, "/cpus"); if (cpus_offset < 0) { puts("couldn't find /cpus node\n"); return cpus_offset; } fdt_for_each_subnode(off, fdt_blob, cpus_offset) { if (strncmp(fdt_get_name(fdt_blob, off, NULL), "cpu@", 4)) continue; mpidr = 0; ret = smc_get_mpidr(i, &mpidr); if (ret) { log_warning("Failed to get MPIDR for processor %lld from SMC: %d\n", i, ret); mpidr = i; } ret = fdt_setprop_string(fdt_blob, off, "compatible", "arm,armv8"); if (ret < 0) return ret; ret = fdt_setprop_string(fdt_blob, off, "device_type", "cpu"); if (ret < 0) return ret; ret = fdt_setprop_u64(fdt_blob, off, "reg", mpidr); if (ret < 0) return ret; i++; } return 0; } /* * Update the GIC node when necessary and add optional ITS when it has a * non zero base-address. */ static int fdtdec_fix_gic(void *fdt) { u64 gic_dist_base = SBSA_GIC_DIST_BASE_ADDR; u64 gic_redist_base = SBSA_GIC_REDIST_BASE_ADDR; u64 gic_its_base = 0; int offs, ret; u64 reg[10]; /* Invoke SMC to get real base-address */ smc_get_gic_dist_base(&gic_dist_base); smc_get_gic_redist_base(&gic_redist_base); if ((gic_dist_base != SBSA_GIC_DIST_BASE_ADDR) || (gic_redist_base != SBSA_GIC_REDIST_BASE_ADDR)) { offs = fdt_path_offset(fdt, "/interrupt-controller"); if (offs < 0) { puts("couldn't find /interrupt-controller node\n"); return offs; } reg[0] = cpu_to_fdt64(gic_dist_base); reg[1] = cpu_to_fdt64((u64)SBSA_GIC_DIST_LENGTH); reg[2] = cpu_to_fdt64(gic_redist_base); reg[3] = cpu_to_fdt64((u64)SBSA_GIC_REDIST_LENGTH); reg[4] = cpu_to_fdt64(0); reg[5] = cpu_to_fdt64(0); reg[6] = cpu_to_fdt64(SBSA_GIC_HBASE_ADDR); reg[7] = cpu_to_fdt64((u64)SBSA_GIC_HBASE_LENGTH); reg[8] = cpu_to_fdt64(SBSA_GIC_VBASE_ADDR); reg[9] = cpu_to_fdt64((u64)SBSA_GIC_VBASE_LENGTH); ret = fdt_setprop_inplace(fdt, offs, "reg", reg, sizeof(reg)); } smc_get_gic_its_base(&gic_its_base); if (gic_its_base != 0) { offs = fdt_path_offset(fdt, "/its"); if (offs < 0) return offs; ret = fdt_setprop_string(fdt, offs, "status", "okay"); if (ret < 0) return ret; reg[0] = cpu_to_fdt64(gic_its_base); reg[1] = 0; ret = fdt_setprop(fdt, offs, "reg", reg, sizeof(u64) * 2); if (ret < 0) return ret; } return 0; } int fdtdec_board_setup(const void *fdt_blob) { void *qemu_fdt; int ret; /* * Locate the QEMU provided DTB that contains the CPUs and amount of DRAM. */ qemu_fdt = dtb_dt_qemu(); if (!qemu_fdt) { log_err("QEMU FDT not found\n"); return -ENODEV; } ret = fdt_increase_size((void *)fdt_blob, 1024 + fdt_totalsize(qemu_fdt)); if (ret) return -ENOMEM; /* * Merge the QEMU DTB as overlay into the U-Boot provided DTB. */ ret = fdt_overlay_apply_node((void *)fdt_blob, 0, qemu_fdt, 0); if (ret < 0) log_err("Failed to apply overlay: %d\n", ret); /* Fix QEMU nodes to make sure U-Boot drivers are properly working */ ret = fdtdec_fix_cpus((void *)fdt_blob); if (ret < 0) log_err("Failed to fix CPUs in FDT: %d\n", ret); ret = fdtdec_fix_gic((void *)fdt_blob); if (ret < 0) log_err("Failed to fix INTC in FDT: %d\n", ret); return 0; } int misc_init_r(void) { return env_set_hex("fdt_addr", (uintptr_t)gd->fdt_blob); } void reset_cpu(void) { } int dram_init(void) { return fdtdec_setup_mem_size_base(); }