mirror of
https://github.com/ARM-software/arm-trusted-firmware.git
synced 2025-04-16 17:44:19 +00:00

This patch adds support for Local Chip Addressing (LCA). In a multi-chip system, enablig LCA allows each GIC Distributor to maintain its own version of routing table. This feature is activated when the GICD_CFGID.LCA bit is set to 1. The existing `gic600_multichip_data` data structure did not account for the LCA feature. To support LCA: - `rt_owner_base` is replaced by `base_addrs[]`. This is required because each GICD in the system needs to be configured independently, and their base addresses must be passed to the driver. - `chip_addrs` is changed from 1D to 2D array to store the routing table for each chip's GICD. The entries in `chip_addrs` are configuration dependent, as the GIC specification does not enforce this. On a multi-chip platform with chip count N where LCA is enabled by default, the `gic600_multichip_data` structure should contain all copies of the routing table (N*N entries). On platforms where LCA is not supported, only the first sub-array with N entries is required. The function signature of `gic600_multichip_init` remains unchanged, but if the LCA feature is enabled, the driver will expect the routing table configuration in the described format. Change-Id: I8830c2cf90db6a0cae78e99914cd32c637284a2b Signed-off-by: Jerry Wang <Jerry.Wang4@arm.com>
428 lines
13 KiB
C
428 lines
13 KiB
C
/*
|
|
* Copyright (c) 2019-2024, Arm Limited. All rights reserved.
|
|
* Copyright (c) 2022-2023, NVIDIA Corporation. All rights reserved.
|
|
*
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*/
|
|
|
|
/*
|
|
* GIC-600 driver extension for multichip setup
|
|
*/
|
|
|
|
#include <assert.h>
|
|
|
|
#include <common/debug.h>
|
|
#include <drivers/arm/arm_gicv3_common.h>
|
|
#include <drivers/arm/gic600_multichip.h>
|
|
#include <drivers/arm/gicv3.h>
|
|
|
|
#include "../common/gic_common_private.h"
|
|
#include "gic600_multichip_private.h"
|
|
|
|
static struct gic600_multichip_data *plat_gic_multichip_data;
|
|
|
|
/*******************************************************************************
|
|
* Retrieve the address of the chip owner for a given SPI ID
|
|
******************************************************************************/
|
|
uintptr_t gic600_multichip_gicd_base_for_spi(uint32_t spi_id)
|
|
{
|
|
unsigned int i;
|
|
|
|
/* Find the multichip instance */
|
|
for (i = 0U; i < GIC600_MAX_MULTICHIP; i++) {
|
|
if ((spi_id <= plat_gic_multichip_data->spi_ids[i].spi_id_max) &&
|
|
(spi_id >= plat_gic_multichip_data->spi_ids[i].spi_id_min)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Ensure that plat_gic_multichip_data contains valid values */
|
|
assert(i < GIC600_MAX_MULTICHIP);
|
|
|
|
return plat_gic_multichip_data->spi_ids[i].gicd_base;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* 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-- == 0U) {
|
|
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 initiating 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;
|
|
unsigned int gicd_iidr_val = gicd_read_iidr(base);
|
|
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;
|
|
}
|
|
|
|
switch ((gicd_iidr_val & IIDR_MODEL_MASK)) {
|
|
case IIDR_MODEL_ARM_GIC_600:
|
|
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_GIC_600(chip_addr,
|
|
spi_block_min,
|
|
spi_blocks);
|
|
break;
|
|
case IIDR_MODEL_ARM_GIC_700:
|
|
/* Calculate the SPI_ID_MIN value for ESPI */
|
|
if (spi_id_min >= GIC700_ESPI_ID_MIN) {
|
|
spi_block_min = ESPI_BLOCK_MIN_VALUE(spi_id_min);
|
|
spi_block_min += SPI_BLOCKS_VALUE(GIC700_SPI_ID_MIN,
|
|
GIC700_SPI_ID_MAX);
|
|
} else {
|
|
spi_block_min = SPI_BLOCK_MIN_VALUE(spi_id_min);
|
|
}
|
|
|
|
/* Calculate the total number of blocks */
|
|
spi_blocks = SPI_BLOCKS_VALUE(spi_id_min, spi_id_max);
|
|
|
|
chipr_n_val = GICD_CHIPR_VALUE_GIC_700(chip_addr,
|
|
spi_block_min,
|
|
spi_blocks);
|
|
break;
|
|
default:
|
|
ERROR("Unsupported GIC model 0x%x for multichip setup.\n",
|
|
gicd_iidr_val);
|
|
panic();
|
|
break;
|
|
}
|
|
chipr_n_val |= 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 = 0U; i < multichip_data->chip_count; i++) {
|
|
spi_id_min = multichip_data->spi_ids[i].spi_id_min;
|
|
spi_id_max = multichip_data->spi_ids[i].spi_id_max;
|
|
|
|
if ((spi_id_min != 0U) || (spi_id_max != 0U)) {
|
|
|
|
/* 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;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* Validates the GIC-700 Multichip data structure passed by the platform.
|
|
******************************************************************************/
|
|
static void gic700_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 = 0U, multichip_espi_blocks = 0U;
|
|
|
|
assert(multichip_data != NULL);
|
|
|
|
if (multichip_data->chip_count > GIC600_MAX_MULTICHIP) {
|
|
ERROR("GIC-700 Multichip count (%u) should not exceed %u\n",
|
|
multichip_data->chip_count, GIC600_MAX_MULTICHIP);
|
|
panic();
|
|
}
|
|
|
|
for (i = 0U; i < multichip_data->chip_count; i++) {
|
|
spi_id_min = multichip_data->spi_ids[i].spi_id_min;
|
|
spi_id_max = multichip_data->spi_ids[i].spi_id_max;
|
|
|
|
if ((spi_id_min == 0U) || (spi_id_max == 0U)) {
|
|
continue;
|
|
}
|
|
|
|
/* MIN SPI ID check */
|
|
if ((spi_id_min < GIC700_SPI_ID_MIN) ||
|
|
((spi_id_min >= GIC700_SPI_ID_MAX) &&
|
|
(spi_id_min < GIC700_ESPI_ID_MIN))) {
|
|
ERROR("Invalid MIN SPI ID {%u} passed for "
|
|
"Chip %u\n", spi_id_min, i);
|
|
panic();
|
|
}
|
|
|
|
if ((spi_id_min > spi_id_max) ||
|
|
((spi_id_max - spi_id_min + 1) % 32 != 0)) {
|
|
ERROR("Unaligned SPI IDs {%u, %u} passed for "
|
|
"Chip %u\n", spi_id_min,
|
|
spi_id_max, i);
|
|
panic();
|
|
}
|
|
|
|
/* ESPI IDs range check */
|
|
if ((spi_id_min >= GIC700_ESPI_ID_MIN) &&
|
|
(spi_id_max > GIC700_ESPI_ID_MAX)) {
|
|
ERROR("Invalid ESPI IDs {%u, %u} passed for "
|
|
"Chip %u\n", spi_id_min,
|
|
spi_id_max, i);
|
|
panic();
|
|
|
|
}
|
|
|
|
/* SPI IDs range check */
|
|
if (((spi_id_min < GIC700_SPI_ID_MAX) &&
|
|
(spi_id_max > GIC700_SPI_ID_MAX))) {
|
|
ERROR("Invalid SPI IDs {%u, %u} passed for "
|
|
"Chip %u\n", spi_id_min,
|
|
spi_id_max, i);
|
|
panic();
|
|
}
|
|
|
|
/* SPI IDs overlap check */
|
|
if (spi_id_max < GIC700_SPI_ID_MAX) {
|
|
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;
|
|
}
|
|
|
|
/* ESPI IDs overlap check */
|
|
if (spi_id_max > GIC700_ESPI_ID_MIN) {
|
|
blocks_of_32 = BLOCKS_OF_32(spi_id_min - GIC700_ESPI_ID_MIN,
|
|
spi_id_max - GIC700_ESPI_ID_MIN);
|
|
if ((multichip_espi_blocks & blocks_of_32) != 0) {
|
|
ERROR("SPI IDs of Chip %u overlapping\n", i);
|
|
panic();
|
|
}
|
|
multichip_espi_blocks |= blocks_of_32;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* Initialize GIC-600 and GIC-700 Multichip operation in LCA mode by setting up
|
|
* the routing table first.
|
|
******************************************************************************/
|
|
static void gic600_multichip_lca_init(
|
|
struct gic600_multichip_data *multichip_data)
|
|
{
|
|
unsigned int i, j;
|
|
unsigned int rt_owner = multichip_data->rt_owner;
|
|
|
|
for (i = 0; i < multichip_data->chip_count; i++) {
|
|
for (j = 0; j < multichip_data->chip_count; j++) {
|
|
INFO("RT(LCA): CHIP%u -> CHIP%u 0x%lx\n", i, j,
|
|
multichip_data->chip_addrs[i][j]);
|
|
set_gicd_chipr_n(multichip_data->base_addrs[i], j,
|
|
multichip_data->chip_addrs[i][j],
|
|
multichip_data->spi_ids[j].spi_id_min,
|
|
multichip_data->spi_ids[j].spi_id_max);
|
|
}
|
|
}
|
|
|
|
/* Initialize the GICD which is marked as routing table owner last */
|
|
set_gicd_dchipr_rt_owner(multichip_data->base_addrs[rt_owner],
|
|
rt_owner);
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* Initialize GIC-600 and GIC-700 Multichip operation.
|
|
******************************************************************************/
|
|
void gic600_multichip_init(struct gic600_multichip_data *multichip_data)
|
|
{
|
|
unsigned int i;
|
|
unsigned int rt_owner = multichip_data->rt_owner;
|
|
uint32_t gicd_iidr_val =
|
|
gicd_read_iidr(multichip_data->base_addrs[rt_owner]);
|
|
|
|
if ((gicd_iidr_val & IIDR_MODEL_MASK) == IIDR_MODEL_ARM_GIC_600) {
|
|
gic600_multichip_validate_data(multichip_data);
|
|
}
|
|
|
|
if ((gicd_iidr_val & IIDR_MODEL_MASK) == IIDR_MODEL_ARM_GIC_700) {
|
|
gic700_multichip_validate_data(multichip_data);
|
|
}
|
|
|
|
/*
|
|
* 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->base_addrs[rt_owner]) &
|
|
(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->base_addrs[rt_owner]) &
|
|
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();
|
|
}
|
|
|
|
/* If LCA is not enabled */
|
|
if ((read_gicd_cfgid(multichip_data->base_addrs[rt_owner]) &
|
|
GICD_CFGID_LCA_BIT) == 0) {
|
|
/*
|
|
* Initialize the GICD which is marked as routing table
|
|
* owner first.
|
|
*/
|
|
set_gicd_dchipr_rt_owner(multichip_data->base_addrs[rt_owner],
|
|
rt_owner);
|
|
|
|
set_gicd_chipr_n(multichip_data->base_addrs[rt_owner], rt_owner,
|
|
multichip_data->chip_addrs[rt_owner][rt_owner],
|
|
multichip_data->spi_ids[rt_owner].spi_id_min,
|
|
multichip_data->spi_ids[rt_owner].spi_id_max);
|
|
|
|
for (i = 0; i < multichip_data->chip_count; i++) {
|
|
if (i == rt_owner)
|
|
continue;
|
|
set_gicd_chipr_n(
|
|
multichip_data->base_addrs[rt_owner], i,
|
|
multichip_data->chip_addrs[rt_owner][i],
|
|
multichip_data->spi_ids[i].spi_id_min,
|
|
multichip_data->spi_ids[i].spi_id_max);
|
|
}
|
|
} else {
|
|
/* If LCA is enabled */
|
|
INFO("GIC Local chip addressing is enabled\n");
|
|
gic600_multichip_lca_init(multichip_data);
|
|
}
|
|
|
|
plat_gic_multichip_data = multichip_data;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* Allow a way to query the status of the GIC600 multichip driver
|
|
******************************************************************************/
|
|
bool gic600_multichip_is_initialized(void)
|
|
{
|
|
return (plat_gic_multichip_data != NULL);
|
|
}
|