aboutsummaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorVijayenthiran Subramaniam <vijayenthiran.subramaniam@arm.com>2019-09-16 17:05:08 +0530
committerVijayenthiran Subramaniam <vijayenthiran.subramaniam@arm.com>2019-11-11 23:40:23 +0530
commitfcc337cf49e8b3d5f08d905bf24265ca82711bd8 (patch)
tree5a699bd2e18d1270ea08cc927e30f185005b6607 /drivers
parent74c21244005f639812b0cc68f71e501f831d8f70 (diff)
downloadtrusted-firmware-a-fcc337cf49e8b3d5f08d905bf24265ca82711bd8.tar.gz
gic/gic600: add support for multichip configuration
Add support to configure GIC-600's multichip routing table registers. Introduce a new gic600 multichip structure in order to support platforms to pass their GIC-600 multichip information such as routing table owner, SPI blocks ownership. This driver is currently experimental and the driver api may change in the future. Change-Id: Id409d0bc07843e271ead3fc2f6e3cb38b317878d Signed-off-by: Vijayenthiran Subramaniam <vijayenthiran.subramaniam@arm.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/arm/gic/v3/gic600_multichip.c240
-rw-r--r--drivers/arm/gic/v3/gic600_multichip_private.h97
2 files changed, 337 insertions, 0 deletions
diff --git a/drivers/arm/gic/v3/gic600_multichip.c b/drivers/arm/gic/v3/gic600_multichip.c
new file mode 100644
index 0000000000..c62c3f5dd4
--- /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 <assert.h>
+#include <common/debug.h>
+
+#include <drivers/arm/gicv3.h>
+#include <drivers/arm/gic600_multichip.h>
+
+#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 0000000000..b0217b6d41
--- /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 <drivers/arm/gic600_multichip.h>
+
+#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 */