diff options
author | André Przywara <andre.przywara@arm.com> | 2021-11-06 02:32:00 +0100 |
---|---|---|
committer | TrustedFirmware Code Review <review@review.trustedfirmware.org> | 2021-11-06 02:32:00 +0100 |
commit | 683bb4d7bdfd42e6e026902c43797f132b2a75d5 (patch) | |
tree | 6c42c87e6f69c19b53e3219db5e56f26d1df2415 /plat | |
parent | 25d7dafb2c61b682dcb77a74baed2ae50f056910 (diff) | |
parent | 422b44fb56db7ca8b1a2f9f706733d7d4c2fdeb1 (diff) | |
download | arm-trusted-firmware-683bb4d7bdfd42e6e026902c43797f132b2a75d5.tar.gz |
Merge changes from topic "arm_fpga_auto" into integration
* changes:
feat(arm_fpga): write UART baud base clock frequency into DTB
feat(arm_fpga): query PL011 to learn system frequency
refactor(arm_fpga): move command line code into separate function
fix(fdt): avoid output on missing DT property
feat(arm_fpga): add ITS autodetection
feat(arm_fpga): determine GICR base by probing
feat(gicv3): introduce GIC component identification
feat(libfdt): also allow changing base address
fix(arm_fpga): avoid re-linking from executable ELF file
Diffstat (limited to 'plat')
-rw-r--r-- | plat/arm/board/arm_fpga/build_axf.ld.S | 4 | ||||
-rw-r--r-- | plat/arm/board/arm_fpga/fpga_bl31_setup.c | 248 | ||||
-rw-r--r-- | plat/arm/board/arm_fpga/fpga_gicv3.c | 83 | ||||
-rw-r--r-- | plat/arm/board/arm_fpga/fpga_private.h | 2 |
4 files changed, 275 insertions, 62 deletions
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 <drivers/delay_timer.h> #include <drivers/generic_delay_timer.h> #include <lib/extensions/spe.h> +#include <lib/mmio.h> #include <libfdt.h> #include "fpga_private.h" @@ -20,6 +21,7 @@ #include <platform_def.h> 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) +/* + * 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 + +/* 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 ((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; + 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) { - return FPGA_DEFAULT_TIMER_FREQUENCY; + 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; } - return fdt_read_uint32_default(fdt, node, "clock-frequency", - FPGA_DEFAULT_TIMER_FREQUENCY); + 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 <common/debug.h> #include <common/fdt_wrappers.h> -#include <drivers/arm/gicv3.h> +#include <drivers/arm/arm_gicv3_common.h> #include <drivers/arm/gic_common.h> +#include <drivers/arm/gicv3.h> #include <lib/mmio.h> #include <libfdt.h> @@ -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__ */ |