| /* SPDX-License-Identifier: GPL-2.0+ */ |
| /* |
| * Copyright (c) 2017 Tuomas Tynkkynen |
| */ |
| |
| #include <cpu_func.h> |
| #include <dm.h> |
| #include <env.h> |
| #include <fdtdec.h> |
| #include <fdt_support.h> |
| #include <init.h> |
| #include <log.h> |
| #include <usb.h> |
| #include <asm/armv8/mmu.h> |
| |
| #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(); |
| } |