blob: 3943c9243200d4060c7e24446f24cce20be34a28 [file] [log] [blame]
/* 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();
}