diff --git a/drivers/arm/gic/v3/gic600_multichip.c b/drivers/arm/gic/v3/gic600_multichip.c new file mode 100644 index 0000000..c62c3f5 --- /dev/null +++ b/drivers/arm/gic/v3/gic600_multichip.c @@ -0,0 +1,240 @@ +/* + * Copyright (c) 2019, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * GIC-600 driver extension for multichip setup + */ + +#include +#include + +#include +#include + +#include "gic600_multichip_private.h" +#include "../common/gic_common_private.h" + +#warning "GIC-600 Multichip driver is currently experimental and the API may change in future." + +/******************************************************************************* + * GIC-600 multichip operation related helper functions + ******************************************************************************/ +static void gicd_dchipr_wait_for_power_update_progress(uintptr_t base) +{ + unsigned int retry = GICD_PUP_UPDATE_RETRIES; + + while ((read_gicd_dchipr(base) & GICD_DCHIPR_PUP_BIT) != 0U) { + if (retry-- == 0) { + ERROR("GIC-600 connection to Routing Table Owner timed " + "out\n"); + panic(); + } + } +} + +/******************************************************************************* + * Sets up the routing table owner. + ******************************************************************************/ +static void set_gicd_dchipr_rt_owner(uintptr_t base, unsigned int rt_owner) +{ + /* + * Ensure that Group enables in GICD_CTLR are disabled and no pending + * register writes to GICD_CTLR. + */ + if ((gicd_read_ctlr(base) & + (CTLR_ENABLE_G0_BIT | CTLR_ENABLE_G1S_BIT | + CTLR_ENABLE_G1NS_BIT | GICD_CTLR_RWP_BIT)) != 0) { + ERROR("GICD_CTLR group interrupts are either enabled or have " + "pending writes. Cannot set RT owner.\n"); + panic(); + } + + /* Poll till PUP is zero before intiating write */ + gicd_dchipr_wait_for_power_update_progress(base); + + write_gicd_dchipr(base, read_gicd_dchipr(base) | + (rt_owner << GICD_DCHIPR_RT_OWNER_SHIFT)); + + /* Poll till PUP is zero to ensure write is complete */ + gicd_dchipr_wait_for_power_update_progress(base); +} + +/******************************************************************************* + * Configures the Chip Register to make connections to GICDs on + * a multichip platform. + ******************************************************************************/ +static void set_gicd_chipr_n(uintptr_t base, + unsigned int chip_id, + uint64_t chip_addr, + unsigned int spi_id_min, + unsigned int spi_id_max) +{ + unsigned int spi_block_min, spi_blocks; + uint64_t chipr_n_val; + + /* + * Ensure that group enables in GICD_CTLR are disabled and no pending + * register writes to GICD_CTLR. + */ + if ((gicd_read_ctlr(base) & + (CTLR_ENABLE_G0_BIT | CTLR_ENABLE_G1S_BIT | + CTLR_ENABLE_G1NS_BIT | GICD_CTLR_RWP_BIT)) != 0) { + ERROR("GICD_CTLR group interrupts are either enabled or have " + "pending writes. Cannot set CHIPR register.\n"); + panic(); + } + + /* + * spi_id_min and spi_id_max of value 0 is used to intidicate that the + * chip doesn't own any SPI block. Re-assign min and max values as SPI + * id starts from 32. + */ + if (spi_id_min == 0 && spi_id_max == 0) { + spi_id_min = GIC600_SPI_ID_MIN; + spi_id_max = GIC600_SPI_ID_MIN; + } + + spi_block_min = SPI_BLOCK_MIN_VALUE(spi_id_min); + spi_blocks = SPI_BLOCKS_VALUE(spi_id_min, spi_id_max); + + chipr_n_val = (GICD_CHIPR_VALUE(chip_addr, spi_block_min, spi_blocks)) | + GICD_CHIPRx_SOCKET_STATE; + + /* + * Wait for DCHIPR.PUP to be zero before commencing writes to + * GICD_CHIPRx. + */ + gicd_dchipr_wait_for_power_update_progress(base); + + /* + * Assign chip addr, spi min block, number of spi blocks and bring chip + * online by setting SocketState. + */ + write_gicd_chipr_n(base, chip_id, chipr_n_val); + + /* + * Poll until DCHIP.PUP is zero to verify connection to rt_owner chip + * is complete. + */ + gicd_dchipr_wait_for_power_update_progress(base); + + /* + * Ensure that write to GICD_CHIPRx is successful and the chip_n came + * online. + */ + if (read_gicd_chipr_n(base, chip_id) != chipr_n_val) { + ERROR("GICD_CHIPR%u write failed\n", chip_id); + panic(); + } + + /* Ensure that chip is in consistent state */ + if (((read_gicd_chipsr(base) & GICD_CHIPSR_RTS_MASK) >> + GICD_CHIPSR_RTS_SHIFT) != + GICD_CHIPSR_RTS_STATE_CONSISTENT) { + ERROR("Chip %u routing table is not in consistent state\n", + chip_id); + panic(); + } +} + +/******************************************************************************* + * Validates the GIC-600 Multichip data structure passed by the platform. + ******************************************************************************/ +static void gic600_multichip_validate_data( + struct gic600_multichip_data *multichip_data) +{ + unsigned int i, spi_id_min, spi_id_max, blocks_of_32; + unsigned int multichip_spi_blocks = 0; + + assert(multichip_data != NULL); + + if (multichip_data->chip_count > GIC600_MAX_MULTICHIP) { + ERROR("GIC-600 Multichip count should not exceed %d\n", + GIC600_MAX_MULTICHIP); + panic(); + } + + for (i = 0; i < multichip_data->chip_count; i++) { + spi_id_min = multichip_data->spi_ids[i][SPI_MIN_INDEX]; + spi_id_max = multichip_data->spi_ids[i][SPI_MAX_INDEX]; + + if ((spi_id_min != 0) || (spi_id_max != 0)) { + + /* SPI IDs range check */ + if (!(spi_id_min >= GIC600_SPI_ID_MIN) || + !(spi_id_max < GIC600_SPI_ID_MAX) || + !(spi_id_min <= spi_id_max) || + !((spi_id_max - spi_id_min + 1) % 32 == 0)) { + ERROR("Invalid SPI IDs {%u, %u} passed for " + "Chip %u\n", spi_id_min, + spi_id_max, i); + panic(); + } + + /* SPI IDs overlap check */ + blocks_of_32 = BLOCKS_OF_32(spi_id_min, spi_id_max); + if ((multichip_spi_blocks & blocks_of_32) != 0) { + ERROR("SPI IDs of Chip %u overlapping\n", i); + panic(); + } + multichip_spi_blocks |= blocks_of_32; + } + } +} + +/******************************************************************************* + * Intialize GIC-600 Multichip operation. + ******************************************************************************/ +void gic600_multichip_init(struct gic600_multichip_data *multichip_data) +{ + unsigned int i; + + gic600_multichip_validate_data(multichip_data); + + INFO("GIC-600 Multichip driver is experimental\n"); + + /* + * Ensure that G0/G1S/G1NS interrupts are disabled. This also ensures + * that GIC-600 Multichip configuration is done first. + */ + if ((gicd_read_ctlr(multichip_data->rt_owner_base) & + (CTLR_ENABLE_G0_BIT | CTLR_ENABLE_G1S_BIT | + CTLR_ENABLE_G1NS_BIT | GICD_CTLR_RWP_BIT)) != 0) { + ERROR("GICD_CTLR group interrupts are either enabled or have " + "pending writes.\n"); + panic(); + } + + /* Ensure that the routing table owner is in disconnected state */ + if (((read_gicd_chipsr(multichip_data->rt_owner_base) & + GICD_CHIPSR_RTS_MASK) >> GICD_CHIPSR_RTS_SHIFT) != + GICD_CHIPSR_RTS_STATE_DISCONNECTED) { + ERROR("GIC-600 routing table owner is not in disconnected " + "state to begin multichip configuration\n"); + panic(); + } + + /* Initialize the GICD which is marked as routing table owner first */ + set_gicd_dchipr_rt_owner(multichip_data->rt_owner_base, + multichip_data->rt_owner); + + set_gicd_chipr_n(multichip_data->rt_owner_base, multichip_data->rt_owner, + multichip_data->chip_addrs[multichip_data->rt_owner], + multichip_data-> + spi_ids[multichip_data->rt_owner][SPI_MIN_INDEX], + multichip_data-> + spi_ids[multichip_data->rt_owner][SPI_MAX_INDEX]); + + for (i = 0; i < multichip_data->chip_count; i++) { + if (i == multichip_data->rt_owner) + continue; + + set_gicd_chipr_n(multichip_data->rt_owner_base, i, + multichip_data->chip_addrs[i], + multichip_data->spi_ids[i][SPI_MIN_INDEX], + multichip_data->spi_ids[i][SPI_MAX_INDEX]); + } +} diff --git a/drivers/arm/gic/v3/gic600_multichip_private.h b/drivers/arm/gic/v3/gic600_multichip_private.h new file mode 100644 index 0000000..b0217b6 --- /dev/null +++ b/drivers/arm/gic/v3/gic600_multichip_private.h @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2019, ARM Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef GIC600_MULTICHIP_PRIVATE_H +#define GIC600_MULTICHIP_PRIVATE_H + +#include + +#include "gicv3_private.h" + +/* GIC600 GICD multichip related offsets */ +#define GICD_CHIPSR U(0xC000) +#define GICD_DCHIPR U(0xC004) +#define GICD_CHIPR U(0xC008) + +/* GIC600 GICD multichip related masks */ +#define GICD_CHIPRx_PUP_BIT BIT_64(1) +#define GICD_CHIPRx_SOCKET_STATE BIT_64(0) +#define GICD_DCHIPR_PUP_BIT BIT_32(0) +#define GICD_CHIPSR_RTS_MASK (BIT_32(4) | BIT_32(5)) + +/* GIC600 GICD multichip related shifts */ +#define GICD_CHIPRx_ADDR_SHIFT 16 +#define GICD_CHIPRx_SPI_BLOCK_MIN_SHIFT 10 +#define GICD_CHIPRx_SPI_BLOCKS_SHIFT 5 +#define GICD_CHIPSR_RTS_SHIFT 4 +#define GICD_DCHIPR_RT_OWNER_SHIFT 4 + +#define GICD_CHIPSR_RTS_STATE_DISCONNECTED U(0) +#define GICD_CHIPSR_RTS_STATE_UPDATING U(1) +#define GICD_CHIPSR_RTS_STATE_CONSISTENT U(2) + +/* SPI interrupt id minimum and maximum range */ +#define GIC600_SPI_ID_MIN 32 +#define GIC600_SPI_ID_MAX 960 + +/* Number of retries for PUP update */ +#define GICD_PUP_UPDATE_RETRIES 10000 + +#define SPI_MIN_INDEX 0 +#define SPI_MAX_INDEX 1 + +#define SPI_BLOCK_MIN_VALUE(spi_id_min) \ + (((spi_id_min) - GIC600_SPI_ID_MIN) / \ + GIC600_SPI_ID_MIN) +#define SPI_BLOCKS_VALUE(spi_id_min, spi_id_max) \ + (((spi_id_max) - (spi_id_min) + 1) / \ + GIC600_SPI_ID_MIN) +#define GICD_CHIPR_VALUE(chip_addr, spi_block_min, spi_blocks) \ + (((chip_addr) << GICD_CHIPRx_ADDR_SHIFT) | \ + ((spi_block_min) << GICD_CHIPRx_SPI_BLOCK_MIN_SHIFT) | \ + ((spi_blocks) << GICD_CHIPRx_SPI_BLOCKS_SHIFT)) + +/* + * Multichip data assertion macros + */ +/* Set bits from 0 to ((spi_id_max + 1) / 32) */ +#define SPI_BLOCKS_TILL_MAX(spi_id_max) ((1 << (((spi_id_max) + 1) >> 5)) - 1) +/* Set bits from 0 to (spi_id_min / 32) */ +#define SPI_BLOCKS_TILL_MIN(spi_id_min) ((1 << ((spi_id_min) >> 5)) - 1) +/* Set bits from (spi_id_min / 32) to ((spi_id_max + 1) / 32) */ +#define BLOCKS_OF_32(spi_id_min, spi_id_max) \ + SPI_BLOCKS_TILL_MAX(spi_id_max) ^ \ + SPI_BLOCKS_TILL_MIN(spi_id_min) + +/******************************************************************************* + * GIC-600 multichip operation related helper functions + ******************************************************************************/ +static inline uint32_t read_gicd_dchipr(uintptr_t base) +{ + return mmio_read_32(base + GICD_DCHIPR); +} + +static inline uint64_t read_gicd_chipr_n(uintptr_t base, uint8_t n) +{ + return mmio_read_64(base + (GICD_CHIPR + (8U * n))); +} + +static inline uint32_t read_gicd_chipsr(uintptr_t base) +{ + return mmio_read_32(base + GICD_CHIPSR); +} + +static inline void write_gicd_dchipr(uintptr_t base, uint32_t val) +{ + mmio_write_32(base + GICD_DCHIPR, val); +} + +static inline void write_gicd_chipr_n(uintptr_t base, uint8_t n, uint64_t val) +{ + mmio_write_64(base + (GICD_CHIPR + (8U * n)), val); +} + +#endif /* GIC600_MULTICHIP_PRIVATE_H */ diff --git a/include/drivers/arm/gic600_multichip.h b/include/drivers/arm/gic600_multichip.h new file mode 100644 index 0000000..bda406b --- /dev/null +++ b/include/drivers/arm/gic600_multichip.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2019, ARM Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef GIC600_MULTICHIP_H +#define GIC600_MULTICHIP_H + +#include + +/* + * GIC-600 microarchitecture supports coherent multichip environments containing + * up to 16 chips. + */ +#define GIC600_MAX_MULTICHIP 16 + +/* SPI IDs array consist of min and max ids */ +#define GIC600_SPI_IDS_SIZE 2 + +/******************************************************************************* + * GIC-600 multichip data structure describes platform specific attributes + * related to GIC-600 multichip. Platform port is expected to define these + * attributes to initialize the multichip related registers and create + * successful connections between the GIC-600s in a multichip system. + * + * The 'rt_owner_base' field contains the base address of the GIC Distributor + * which owns the routing table. + * + * The 'rt_owner' field contains the chip number which owns the routing table. + * Chip number or chip_id starts from 0. + * + * The 'chip_count' field contains the total number of chips in a multichip + * system. This should match the number of entries in 'chip_addrs' and 'spi_ids' + * fields. + * + * The 'chip_addrs' field contains array of chip addresses. These addresses are + * implementation specific values. + * + * The 'spi_ids' field contains array of minimum and maximum SPI interrupt ids + * that each chip owns. Note that SPI interrupt ids can range from 32 to 960 and + * it should be group of 32 (i.e., SPI minimum and (SPI maximum + 1) should be + * a multiple of 32). If a chip doesn't own any SPI interrupts a value of {0, 0} + * should be passed. + ******************************************************************************/ +struct gic600_multichip_data { + uintptr_t rt_owner_base; + unsigned int rt_owner; + unsigned int chip_count; + uint64_t chip_addrs[GIC600_MAX_MULTICHIP]; + unsigned int spi_ids[GIC600_MAX_MULTICHIP][GIC600_SPI_IDS_SIZE]; +}; + +void gic600_multichip_init(struct gic600_multichip_data *multichip_data); +#endif /* GIC600_MULTICHIP_H */ diff --git a/include/plat/arm/common/plat_arm.h b/include/plat/arm/common/plat_arm.h index 07a46c5..c00a041 100644 --- a/include/plat/arm/common/plat_arm.h +++ b/include/plat/arm/common/plat_arm.h @@ -254,6 +254,11 @@ int plat_arm_bl1_fwu_needed(void); __dead2 void plat_arm_error_handler(int err); +/* + * Optional function in ARM standard platforms + */ +void plat_arm_override_gicr_frames(const uintptr_t *plat_gicr_frames); + #if ARM_PLAT_MT unsigned int plat_arm_get_cpu_pe_count(u_register_t mpidr); #endif diff --git a/plat/arm/common/arm_gicv3.c b/plat/arm/common/arm_gicv3.c index fef5376..cfc5359 100644 --- a/plat/arm/common/arm_gicv3.c +++ b/plat/arm/common/arm_gicv3.c @@ -28,6 +28,15 @@ /* The GICv3 driver only needs to be initialized in EL3 */ static uintptr_t rdistif_base_addrs[PLATFORM_CORE_COUNT]; +/* Default GICR base address to be used for GICR probe. */ +static const uintptr_t gicr_base_addrs[2] = { + PLAT_ARM_GICR_BASE, /* GICR Base address of the primary CPU */ + 0U /* Zero Termination */ +}; + +/* List of zero terminated GICR frame addresses which CPUs will probe */ +static const uintptr_t *gicr_frames = gicr_base_addrs; + static const interrupt_prop_t arm_interrupt_props[] = { PLAT_ARM_G1S_IRQ_PROPS(INTR_GROUP1S), PLAT_ARM_G0_IRQ_PROPS(INTR_GROUP0) @@ -76,6 +85,18 @@ .mpidr_to_core_pos = arm_gicv3_mpidr_hash }; +/* + * By default, gicr_frames will be pointing to gicr_base_addrs. If + * the platform supports a non-contiguous GICR frames (GICR frames located + * at uneven offset), plat_arm_override_gicr_frames function can be used by + * such platform to override the gicr_frames. + */ +void plat_arm_override_gicr_frames(const uintptr_t *plat_gicr_frames) +{ + assert(plat_gicr_frames != NULL); + gicr_frames = plat_gicr_frames; +} + void __init plat_arm_gic_driver_init(void) { /* @@ -88,7 +109,7 @@ (defined(__aarch64__) && defined(IMAGE_BL31)) gicv3_driver_init(&arm_gic_data); - if (gicv3_rdistif_probe(PLAT_ARM_GICR_BASE) == -1) { + if (gicv3_rdistif_probe(gicr_base_addrs[0]) == -1) { ERROR("No GICR base frame found for Primary CPU\n"); panic(); } @@ -124,14 +145,23 @@ /****************************************************************************** * ARM common helper function to iterate over all GICR frames and discover the * corresponding per-cpu redistributor frame as well as initialize the - * corresponding interface in GICv3. At the moment, Arm platforms do not have - * non-contiguous GICR frames. + * corresponding interface in GICv3. *****************************************************************************/ void plat_arm_gic_pcpu_init(void) { int result; + const uintptr_t *plat_gicr_frames = gicr_frames; - result = gicv3_rdistif_probe(PLAT_ARM_GICR_BASE); + do { + result = gicv3_rdistif_probe(*plat_gicr_frames); + + /* If the probe is successful, no need to proceed further */ + if (result == 0) + break; + + plat_gicr_frames++; + } while (*plat_gicr_frames != 0U); + if (result == -1) { ERROR("No GICR base frame found for CPU 0x%lx\n", read_mpidr()); panic();