diff --git a/common/fdt_fixup.c b/common/fdt_fixup.c index 46606fb6e..de02b46e8 100644 --- a/common/fdt_fixup.c +++ b/common/fdt_fixup.c @@ -398,6 +398,7 @@ int fdt_add_cpus_node(void *dtb, unsigned int afflv0, * fdt_adjust_gic_redist() - Adjust GICv3 redistributor size * @dtb: Pointer to the DT blob in memory * @nr_cores: Number of CPU cores on this system. + * @gicr_base: Base address of the first GICR frame, or ~0 if unchanged * @gicr_frame_size: Size of the GICR frame per core * * On a GICv3 compatible interrupt controller, the redistributor provides @@ -410,17 +411,19 @@ int fdt_add_cpus_node(void *dtb, unsigned int afflv0, * A GICv4 compatible redistributor uses four 64K pages per core, whereas GICs * without support for direct injection of virtual interrupts use two 64K pages. * The @gicr_frame_size parameter should be 262144 and 131072, respectively. + * Also optionally allow adjusting the GICR frame base address, when this is + * different due to ITS frames between distributor and redistributor. * * Return: 0 on success, negative error value otherwise. */ int fdt_adjust_gic_redist(void *dtb, unsigned int nr_cores, - unsigned int gicr_frame_size) + uintptr_t gicr_base, unsigned int gicr_frame_size) { int offset = fdt_node_offset_by_compatible(dtb, 0, "arm,gic-v3"); - uint64_t redist_size_64; - uint32_t redist_size_32; + uint64_t reg_64; + uint32_t reg_32; void *val; - int parent; + int parent, ret; int ac, sc; if (offset < 0) { @@ -437,13 +440,34 @@ int fdt_adjust_gic_redist(void *dtb, unsigned int nr_cores, return -EINVAL; } + if (gicr_base != INVALID_BASE_ADDR) { + if (ac == 1) { + reg_32 = cpu_to_fdt32(gicr_base); + val = ®_32; + } else { + reg_64 = cpu_to_fdt64(gicr_base); + val = ®_64; + } + /* + * The redistributor base address is the second address in + * the "reg" entry, so we have to skip one address and one + * size cell. + */ + ret = fdt_setprop_inplace_namelen_partial(dtb, offset, + "reg", 3, + (ac + sc) * 4, + val, ac * 4); + if (ret < 0) { + return ret; + } + } + if (sc == 1) { - redist_size_32 = cpu_to_fdt32(nr_cores * gicr_frame_size); - val = &redist_size_32; + reg_32 = cpu_to_fdt32(nr_cores * gicr_frame_size); + val = ®_32; } else { - redist_size_64 = cpu_to_fdt64(nr_cores * - (uint64_t)gicr_frame_size); - val = &redist_size_64; + reg_64 = cpu_to_fdt64(nr_cores * (uint64_t)gicr_frame_size); + val = ®_64; } /* diff --git a/common/fdt_wrappers.c b/common/fdt_wrappers.c index 64e01ea6d..cfa134243 100644 --- a/common/fdt_wrappers.c +++ b/common/fdt_wrappers.c @@ -35,7 +35,7 @@ int fdt_read_uint32_array(const void *dtb, int node, const char *prop_name, /* Access property and obtain its length (in bytes) */ prop = fdt_getprop(dtb, node, prop_name, &value_len); if (prop == NULL) { - WARN("Couldn't find property %s in dtb\n", prop_name); + VERBOSE("Couldn't find property %s in dtb\n", prop_name); return -FDT_ERR_NOTFOUND; } diff --git a/drivers/arm/gic/v3/gicv3_helpers.c b/drivers/arm/gic/v3/gicv3_helpers.c index d752013e2..753d995d7 100644 --- a/drivers/arm/gic/v3/gicv3_helpers.c +++ b/drivers/arm/gic/v3/gicv3_helpers.c @@ -393,3 +393,18 @@ unsigned int gicv3_rdistif_get_number_frames(const uintptr_t gicr_frame) return count; } + +unsigned int gicv3_get_component_partnum(const uintptr_t gic_frame) +{ + unsigned int part_id; + + /* + * The lower 8 bits of PIDR0, complemented by the lower 4 bits of + * PIDR1 contain a part number identifying the GIC component at a + * particular base address. + */ + part_id = mmio_read_32(gic_frame + GICD_PIDR0_GICV3) & 0xff; + part_id |= (mmio_read_32(gic_frame + GICD_PIDR1_GICV3) << 8) & 0xf00; + + return part_id; +} diff --git a/fdts/arm_fpga.dts b/fdts/arm_fpga.dts index b7b4f0e6a..b9435a241 100644 --- a/fdts/arm_fpga.dts +++ b/fdts/arm_fpga.dts @@ -40,7 +40,6 @@ timer { compatible = "arm,armv8-timer"; - clock-frequency = <10000000>; interrupts = , , , @@ -98,5 +97,12 @@ /* The GICR size will be adjusted at runtime to match the cores. */ <0x0 0x30040000 0x0 0x00020000>; /* GICR for one core */ interrupts = ; + + its: msi-controller@30040000 { + compatible = "arm,gic-v3-its"; + reg = <0x0 0x30040000 0x0 0x40000>; + #msi-cells = <1>; + msi-controller; + }; }; }; diff --git a/include/common/fdt_fixup.h b/include/common/fdt_fixup.h index 2e9d49d53..7a590b2dc 100644 --- a/include/common/fdt_fixup.h +++ b/include/common/fdt_fixup.h @@ -7,13 +7,15 @@ #ifndef FDT_FIXUP_H #define FDT_FIXUP_H +#define INVALID_BASE_ADDR ((uintptr_t)~0UL) + int dt_add_psci_node(void *fdt); int dt_add_psci_cpu_enable_methods(void *fdt); int fdt_add_reserved_memory(void *dtb, const char *node_name, uintptr_t base, size_t size); int fdt_add_cpus_node(void *dtb, unsigned int afflv0, unsigned int afflv1, unsigned int afflv2); -int fdt_adjust_gic_redist(void *dtb, unsigned int nr_cores, +int fdt_adjust_gic_redist(void *dtb, unsigned int nr_cores, uintptr_t gicr_base, unsigned int gicr_frame_size); #endif /* FDT_FIXUP_H */ diff --git a/include/drivers/arm/arm_gicv3_common.h b/include/drivers/arm/arm_gicv3_common.h index e5df31136..d1e93be67 100644 --- a/include/drivers/arm/arm_gicv3_common.h +++ b/include/drivers/arm/arm_gicv3_common.h @@ -21,4 +21,8 @@ #define IIDR_MODEL_ARM_GIC_600AE U(0x0300043b) #define IIDR_MODEL_ARM_GIC_700 U(0x0400043b) +#define PIDR_COMPONENT_ARM_DIST U(0x492) +#define PIDR_COMPONENT_ARM_REDIST U(0x493) +#define PIDR_COMPONENT_ARM_ITS U(0x494) + #endif /* ARM_GICV3_COMMON_H */ diff --git a/include/drivers/arm/gicv3.h b/include/drivers/arm/gicv3.h index fa8946b16..5efefb693 100644 --- a/include/drivers/arm/gicv3.h +++ b/include/drivers/arm/gicv3.h @@ -104,6 +104,8 @@ #define GICD_IROUTER U(0x6000) #define GICD_IROUTERE U(0x8000) +#define GICD_PIDR0_GICV3 U(0xffe0) +#define GICD_PIDR1_GICV3 U(0xffe4) #define GICD_PIDR2_GICV3 U(0xffe8) #define IGRPMODR_SHIFT 5 @@ -301,6 +303,8 @@ #define GITS_CTLR_ENABLED_BIT BIT_32(0) #define GITS_CTLR_QUIESCENT_BIT BIT_32(1) +#define GITS_TYPER_VSGI BIT_64(39) + #ifndef __ASSEMBLER__ #include @@ -324,6 +328,8 @@ static inline uintptr_t gicv3_redist_size(uint64_t typer_val) #endif } +unsigned int gicv3_get_component_partnum(const uintptr_t gic_frame); + static inline bool gicv3_is_intr_id_special_identifier(unsigned int id) { return (id >= PENDING_G1S_INTID) && (id <= GIC_SPURIOUS_INTERRUPT); diff --git a/plat/arm/board/arm_fpga/build_axf.ld.S b/plat/arm/board/arm_fpga/build_axf.ld.S index b4bc7d884..d8254e5b2 100644 --- a/plat/arm/board/arm_fpga/build_axf.ld.S +++ b/plat/arm/board/arm_fpga/build_axf.ld.S @@ -15,11 +15,11 @@ OUTPUT_FORMAT("elf64-littleaarch64") OUTPUT_ARCH(aarch64) -INPUT(./bl31/bl31.elf) INPUT(./rom_trampoline.o) INPUT(./kernel_trampoline.o) TARGET(binary) +INPUT(./bl31.bin) INPUT(./fdts/arm_fpga.dtb) ENTRY(_start) @@ -33,7 +33,7 @@ SECTIONS .bl31 (BL31_BASE): { ASSERT(. == ALIGN(PAGE_SIZE), "BL31_BASE is not page aligned"); - *bl31.elf(.text* .data* .rodata* ro* .bss*) + *bl31.bin } .dtb (FPGA_PRELOADED_DTB_BASE): { diff --git a/plat/arm/board/arm_fpga/fpga_bl31_setup.c b/plat/arm/board/arm_fpga/fpga_bl31_setup.c index 2b5ca4af7..e1b3abb28 100644 --- a/plat/arm/board/arm_fpga/fpga_bl31_setup.c +++ b/plat/arm/board/arm_fpga/fpga_bl31_setup.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include "fpga_private.h" @@ -20,6 +21,7 @@ #include static entry_point_info_t bl33_image_ep_info; +static unsigned int system_freq; volatile uint32_t secondary_core_spinlock; uintptr_t plat_get_ns_image_entrypoint(void) @@ -118,18 +120,187 @@ entry_point_info_t *bl31_plat_get_next_image_ep_info(uint32_t type) } } -unsigned int plat_get_syscnt_freq2(void) -{ - const void *fdt = (void *)(uintptr_t)FPGA_PRELOADED_DTB_BASE; - int node; +/* + * Even though we sell the FPGA UART as an SBSA variant, it is actually + * a full fledged PL011. So the baudrate divider registers exist. + */ +#ifndef UARTIBRD +#define UARTIBRD 0x024 +#define UARTFBRD 0x028 +#endif - node = fdt_node_offset_by_compatible(fdt, 0, "arm,armv8-timer"); - if (node < 0) { - return FPGA_DEFAULT_TIMER_FREQUENCY; +/* Round an integer to the closest multiple of a value. */ +static unsigned int round_multiple(unsigned int x, unsigned int multiple) +{ + if (multiple < 2) { + return x; } - return fdt_read_uint32_default(fdt, node, "clock-frequency", - FPGA_DEFAULT_TIMER_FREQUENCY); + return ((x + (multiple / 2 - 1)) / multiple) * multiple; +} + +#define PL011_FRAC_SHIFT 6 +#define FPGA_DEFAULT_BAUDRATE 38400 +#define PL011_OVERSAMPLING 16 +static unsigned int pl011_freq_from_divider(unsigned int divider) +{ + unsigned int freq; + + freq = divider * FPGA_DEFAULT_BAUDRATE * PL011_OVERSAMPLING; + + return freq >> PL011_FRAC_SHIFT; +} + +/* + * The FPGAs run most peripherals from one main clock, among them the CPUs, + * the arch timer, and the UART baud base clock. + * The SCP knows this frequency and programs the UART clock divider for a + * 38400 bps baudrate. Recalculate the base input clock from there. + */ +static unsigned int fpga_get_system_frequency(void) +{ + const void *fdt = (void *)(uintptr_t)FPGA_PRELOADED_DTB_BASE; + int node, err; + + /* + * If the arch timer DT node has an explicit clock-frequency property + * set, use that, to allow people overriding auto-detection. + */ + node = fdt_node_offset_by_compatible(fdt, 0, "arm,armv8-timer"); + if (node >= 0) { + uint32_t freq; + + err = fdt_read_uint32(fdt, node, "clock-frequency", &freq); + if (err >= 0) { + return freq; + } + } + + node = fdt_node_offset_by_compatible(fdt, 0, "arm,pl011"); + if (node >= 0) { + uintptr_t pl011_base; + unsigned int divider; + + err = fdt_get_reg_props_by_index(fdt, node, 0, + &pl011_base, NULL); + if (err >= 0) { + divider = mmio_read_32(pl011_base + UARTIBRD); + divider <<= PL011_FRAC_SHIFT; + divider += mmio_read_32(pl011_base + UARTFBRD); + + /* + * The result won't be exact, due to rounding errors, + * but the input frequency was a multiple of 250 KHz. + */ + return round_multiple(pl011_freq_from_divider(divider), + 250000); + } else { + WARN("Cannot read PL011 MMIO base\n"); + } + } else { + WARN("No PL011 DT node\n"); + } + + /* No PL011 DT node or calculation failed. */ + return FPGA_DEFAULT_TIMER_FREQUENCY; +} + +unsigned int plat_get_syscnt_freq2(void) +{ + if (system_freq == 0U) { + system_freq = fpga_get_system_frequency(); + } + + return system_freq; +} + +static void fpga_dtb_update_clock(void *fdt, unsigned int freq) +{ + uint32_t freq_dtb = fdt32_to_cpu(freq); + uint32_t phandle; + int node, err; + + node = fdt_node_offset_by_compatible(fdt, 0, "arm,pl011"); + if (node < 0) { + WARN("%s(): No PL011 DT node found\n", __func__); + + return; + } + + err = fdt_read_uint32(fdt, node, "clocks", &phandle); + if (err != 0) { + WARN("Cannot find clocks property\n"); + + return; + } + + node = fdt_node_offset_by_phandle(fdt, phandle); + if (node < 0) { + WARN("Cannot get phandle\n"); + + return; + } + + err = fdt_setprop_inplace(fdt, node, + "clock-frequency", + &freq_dtb, + sizeof(freq_dtb)); + if (err < 0) { + WARN("Could not update DT baud clock frequency\n"); + + return; + } +} + +#define CMDLINE_SIGNATURE "CMD:" + +static int fpga_dtb_set_commandline(void *fdt, const char *cmdline) +{ + int chosen; + const char *eol; + char nul = 0; + int slen, err; + + chosen = fdt_add_subnode(fdt, 0, "chosen"); + if (chosen == -FDT_ERR_EXISTS) { + chosen = fdt_path_offset(fdt, "/chosen"); + } + + if (chosen < 0) { + return chosen; + } + + /* + * There is most likely an EOL at the end of the + * command line, make sure we terminate the line there. + * We can't replace the EOL with a NUL byte in the + * source, as this is in read-only memory. So we first + * create the property without any termination, then + * append a single NUL byte. + */ + eol = strchr(cmdline, '\n'); + if (eol == NULL) { + eol = strchr(cmdline, 0); + } + /* Skip the signature and omit the EOL/NUL byte. */ + slen = eol - (cmdline + strlen(CMDLINE_SIGNATURE)); + /* + * Let's limit the size of the property, just in case + * we find the signature by accident. The Linux kernel + * limits to 4096 characters at most (in fact 2048 for + * arm64), so that sounds like a reasonable number. + */ + if (slen > 4095) { + slen = 4095; + } + + err = fdt_setprop(fdt, chosen, "bootargs", + cmdline + strlen(CMDLINE_SIGNATURE), slen); + if (err != 0) { + return err; + } + + return fdt_appendprop(fdt, chosen, "bootargs", &nul, 1); } static void fpga_prepare_dtb(void) @@ -151,55 +322,13 @@ static void fpga_prepare_dtb(void) } /* Check for the command line signature. */ - if (!strncmp(cmdline, "CMD:", 4)) { - int chosen; - - INFO("using command line at 0x%x\n", FPGA_PRELOADED_CMD_LINE); - - chosen = fdt_add_subnode(fdt, 0, "chosen"); - if (chosen == -FDT_ERR_EXISTS) { - chosen = fdt_path_offset(fdt, "/chosen"); - } - if (chosen < 0) { - ERROR("cannot find /chosen node: %d\n", chosen); + if (!strncmp(cmdline, CMDLINE_SIGNATURE, strlen(CMDLINE_SIGNATURE))) { + err = fpga_dtb_set_commandline(fdt, cmdline); + if (err == 0) { + INFO("using command line at 0x%x\n", + FPGA_PRELOADED_CMD_LINE); } else { - const char *eol; - char nul = 0; - int slen; - - /* - * There is most likely an EOL at the end of the - * command line, make sure we terminate the line there. - * We can't replace the EOL with a NUL byte in the - * source, as this is in read-only memory. So we first - * create the property without any termination, then - * append a single NUL byte. - */ - eol = strchr(cmdline, '\n'); - if (!eol) { - eol = strchr(cmdline, 0); - } - /* Skip the signature and omit the EOL/NUL byte. */ - slen = eol - (cmdline + 4); - - /* - * Let's limit the size of the property, just in case - * we find the signature by accident. The Linux kernel - * limits to 4096 characters at most (in fact 2048 for - * arm64), so that sounds like a reasonable number. - */ - if (slen > 4095) { - slen = 4095; - } - err = fdt_setprop(fdt, chosen, "bootargs", - cmdline + 4, slen); - if (!err) { - err = fdt_appendprop(fdt, chosen, "bootargs", - &nul, 1); - } - if (err) { - ERROR("Could not set command line: %d\n", err); - } + ERROR("failed to put command line into DTB: %d\n", err); } } @@ -224,6 +353,7 @@ static void fpga_prepare_dtb(void) INFO("Adjusting GICR DT region to cover %u cores\n", nr_cores); err = fdt_adjust_gic_redist(fdt, nr_cores, + fpga_get_redist_base(), fpga_get_redist_size()); if (err < 0) { ERROR("Error %d fixing up GIC DT node\n", err); @@ -231,6 +361,8 @@ static void fpga_prepare_dtb(void) } } + fpga_dtb_update_clock(fdt, system_freq); + /* Check whether we support the SPE PMU. Remove the DT node if not. */ if (!spe_supported()) { int node = fdt_node_offset_by_compatible(fdt, 0, @@ -241,6 +373,16 @@ static void fpga_prepare_dtb(void) } } + /* Check whether we have an ITS. Remove the DT node if not. */ + if (!fpga_has_its()) { + int node = fdt_node_offset_by_compatible(fdt, 0, + "arm,gic-v3-its"); + + if (node >= 0) { + fdt_del_node(fdt, node); + } + } + err = fdt_pack(fdt); if (err < 0) { ERROR("Failed to pack Device Tree at %p: error %d\n", fdt, err); diff --git a/plat/arm/board/arm_fpga/fpga_gicv3.c b/plat/arm/board/arm_fpga/fpga_gicv3.c index 4a97beb96..e06a9da56 100644 --- a/plat/arm/board/arm_fpga/fpga_gicv3.c +++ b/plat/arm/board/arm_fpga/fpga_gicv3.c @@ -1,13 +1,14 @@ /* - * Copyright (c) 2020, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2020-2021, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include #include -#include +#include #include +#include #include #include @@ -21,6 +22,7 @@ static const interrupt_prop_t fpga_interrupt_props[] = { }; static uintptr_t fpga_rdistif_base_addrs[PLATFORM_CORE_COUNT]; +static int nr_itses; static unsigned int fpga_mpidr_to_core_pos(unsigned long mpidr) { @@ -38,6 +40,8 @@ static gicv3_driver_data_t fpga_gicv3_driver_data = { void plat_fpga_gic_init(void) { const void *fdt = (void *)(uintptr_t)FPGA_PRELOADED_DTB_BASE; + uintptr_t gicr_base = 0U; + uint32_t iidr; int node, ret; node = fdt_node_offset_by_compatible(fdt, 0, "arm,gic-v3"); @@ -54,11 +58,66 @@ void plat_fpga_gic_init(void) return; } - ret = fdt_get_reg_props_by_index(fdt, node, 1, - &fpga_gicv3_driver_data.gicr_base, NULL); - if (ret < 0) { - WARN("Could not read GIC redistributor address from DT.\n"); - return; + iidr = mmio_read_32(fpga_gicv3_driver_data.gicd_base + GICD_IIDR); + if (((iidr & IIDR_MODEL_MASK) == IIDR_MODEL_ARM_GIC_600) || + ((iidr & IIDR_MODEL_MASK) == IIDR_MODEL_ARM_GIC_700)) { + unsigned int frame_id; + + /* + * According to the GIC TRMs, if there are any ITSes, they + * start four 64K pages after the distributor. After all + * the ITSes then follow the redistributors. + */ + gicr_base = fpga_gicv3_driver_data.gicd_base + (4U << 16); + + do { + uint64_t its_typer; + + /* Each GIC component can be identified by its ID. */ + frame_id = gicv3_get_component_partnum(gicr_base); + + if (frame_id == PIDR_COMPONENT_ARM_REDIST) { + INFO("Found %d ITSes, redistributors start at 0x%llx\n", + nr_itses, (unsigned long long)gicr_base); + break; + } + + if (frame_id != PIDR_COMPONENT_ARM_ITS) { + WARN("GICv3: found unexpected frame 0x%x\n", + frame_id); + gicr_base = 0U; + break; + } + + /* + * Found an ITS, now work out if it supports virtual + * SGIs (for direct guest injection). If yes, each + * ITS occupies four 64K pages, otherwise just two. + */ + its_typer = mmio_read_64(gicr_base + GITS_TYPER); + if ((its_typer & GITS_TYPER_VSGI) != 0U) { + gicr_base += 4U << 16; + } else { + gicr_base += 2U << 16; + } + nr_itses++; + } while (true); + } + + /* + * If this is not a GIC-600 or -700, or the autodetection above failed, + * use the base address from the device tree. + */ + if (gicr_base == 0U) { + ret = fdt_get_reg_props_by_index(fdt, node, 1, + &fpga_gicv3_driver_data.gicr_base, + NULL); + if (ret < 0) { + WARN("Could not read GIC redistributor address from DT.\n"); + return; + } + } else { + fpga_gicv3_driver_data.gicr_base = gicr_base; } gicv3_driver_init(&fpga_gicv3_driver_data); @@ -91,3 +150,13 @@ uintptr_t fpga_get_redist_size(void) return gicv3_redist_size(typer_val); } + +uintptr_t fpga_get_redist_base(void) +{ + return fpga_gicv3_driver_data.gicr_base; +} + +bool fpga_has_its(void) +{ + return nr_itses > 0; +} diff --git a/plat/arm/board/arm_fpga/fpga_private.h b/plat/arm/board/arm_fpga/fpga_private.h index cc809c4e4..84d651cea 100644 --- a/plat/arm/board/arm_fpga/fpga_private.h +++ b/plat/arm/board/arm_fpga/fpga_private.h @@ -26,6 +26,8 @@ void fpga_pwr_gic_off(void); unsigned int plat_fpga_calc_core_pos(uint32_t mpid); unsigned int fpga_get_nr_gic_cores(void); uintptr_t fpga_get_redist_size(void); +uintptr_t fpga_get_redist_base(void); +bool fpga_has_its(void); #endif /* __ASSEMBLER__ */