feat(gicv5): initialise the IRS
Do IRS initialisation that's only accessible from the EL3 interrupt
domain. Relies on the platform to provide SPI domain assignments and
trigger modes as well as to map the config frame in device nGnRnE
memory. All wires will default to NS and the platform may override this.
Change-Id: Icbd43503753cd76fd3d80ed47eba6926494bc323
Co-developed-by: Sascha Bischoff <sascha.bischoff@arm.com>
Signed-off-by: Boyan Karatotev <boyan.karatotev@arm.com>
diff --git a/drivers/arm/gicv5/gicv5_main.c b/drivers/arm/gicv5/gicv5_main.c
index e2b3089..3b66186 100644
--- a/drivers/arm/gicv5/gicv5_main.c
+++ b/drivers/arm/gicv5/gicv5_main.c
@@ -4,6 +4,7 @@
* SPDX-License-Identifier: BSD-3-Clause
*/
+#include <assert.h>
#include <cdefs.h>
#include <arch.h>
@@ -14,8 +15,51 @@
#include <common/debug.h>
#include <drivers/arm/gicv5.h>
+static void irs_configure_wire(uintptr_t base_addr, uint32_t wire, uint8_t domain)
+{
+ write_irs_spi_selr(base_addr, wire);
+ WAIT_FOR_VIDLE_IRS_SPI_STATUSR(base_addr);
+
+ write_irs_spi_domainr(base_addr, domain);
+ WAIT_FOR_VIDLE_IRS_SPI_STATUSR(base_addr);
+}
+
+static void irs_enable(const struct gicv5_irs *config)
+{
+ uint32_t spi_base, spi_range;
+ uintptr_t base_addr = config->el3_config_frame;
+
+ spi_base = EXTRACT(IRS_IDR7_SPI_BASE, read_irs_idr7(base_addr));
+ spi_range = EXTRACT(IRS_IDR6_SPI_IRS_RANGE, read_irs_idr6(base_addr));
+
+ if (spi_range == 0U) {
+ assert(config->num_spis == 0U);
+ }
+
+ /* default all wires to the NS domain */
+ for (uint32_t i = spi_base; i < spi_base + spi_range; i++) {
+ irs_configure_wire(base_addr, i, INTDMN_NS);
+ }
+
+ for (uint32_t i = 0U; i < config->num_spis; i++) {
+ assert((config->spis[i].id >= spi_base) &&
+ (config->spis[i].id < spi_base + spi_range));
+
+ irs_configure_wire(base_addr, config->spis[i].id, config->spis[i].domain);
+
+ /* don't (can't) configure TM of wires for other domains */
+ if (config->spis[i].domain == INTDMN_EL3) {
+ write_irs_spi_cfgr(base_addr, config->spis[i].tm);
+ WAIT_FOR_VIDLE_IRS_SPI_STATUSR(base_addr);
+ }
+ }
+}
+
void __init gicv5_driver_init(void)
{
+ for (size_t i = 0U; i < plat_gicv5_driver_data.num_irss; i++) {
+ irs_enable(&plat_gicv5_driver_data.irss[i]);
+ }
}
/*
diff --git a/include/drivers/arm/gicv5.h b/include/drivers/arm/gicv5.h
index caee11c..838ae37 100644
--- a/include/drivers/arm/gicv5.h
+++ b/include/drivers/arm/gicv5.h
@@ -10,6 +10,8 @@
#ifndef __ASSEMBLER__
#include <stdbool.h>
#include <stdint.h>
+
+#include <lib/mmio.h>
#endif
#include <lib/utils_def.h>
@@ -42,6 +44,15 @@
#define PPI_DB_RL 1
#define PPI_DB_S 0
+/* IRS register fields */
+#define IRS_IDR6_SPI_IRS_RANGE_SHIFT 0
+#define IRS_IDR6_SPI_IRS_RANGE_WIDTH 24
+#define IRS_IDR7_SPI_BASE_SHIFT 0
+#define IRS_IDR7_SPI_BASE_WIDTH 24
+
+#define IRS_SPI_STATUSR_IDLE_BIT BIT(0)
+#define IRS_SPI_STATUSR_V_BIT BIT(1)
+
#ifndef __ASSEMBLER__
#define _PPI_FIELD_SHIFT(_REG, _ppi_id) \
@@ -52,7 +63,66 @@
_var |= (uint64_t)_value << _PPI_FIELD_SHIFT(DOMAINR, _ppi_id);\
} while (false)
+
+#define DEFINE_GICV5_MMIO_WRITE_FUNC(_name, _offset) \
+static inline void write_##_name(uintptr_t base, uint32_t val) \
+{ \
+ mmio_write_32(base + _offset, val); \
+}
+
+#define DEFINE_GICV5_MMIO_READ_FUNC(_name, _offset) \
+static inline uint32_t read_##_name(uintptr_t base) \
+{ \
+ return mmio_read_32(base + _offset); \
+}
+
+#define DEFINE_GICV5_MMIO_RW_FUNCS(_name, _offset) \
+ DEFINE_GICV5_MMIO_READ_FUNC(_name, _offset) \
+ DEFINE_GICV5_MMIO_WRITE_FUNC(_name, _offset)
+
+DEFINE_GICV5_MMIO_READ_FUNC(irs_idr6, 0x0018)
+DEFINE_GICV5_MMIO_READ_FUNC(irs_idr7, 0x001c)
+DEFINE_GICV5_MMIO_RW_FUNCS( irs_spi_selr, 0x0108)
+DEFINE_GICV5_MMIO_RW_FUNCS( irs_spi_domainr, 0x010c)
+DEFINE_GICV5_MMIO_RW_FUNCS( irs_spi_cfgr, 0x0114)
+DEFINE_GICV5_MMIO_READ_FUNC(irs_spi_statusr, 0x0118)
+
+#define WAIT_FOR_IDLE(base, reg, reg_up) \
+ do { \
+ while ((read_##reg(base) & reg_up##_IDLE_BIT) == 0U) {} \
+ } while (0)
+
+/* wait for IDLE but also check the V bit was set */
+#define WAIT_FOR_VIDLE(base, reg, reg_up) \
+ do { \
+ uint32_t val; \
+ while (((val = read_##reg(base)) & reg_up##_IDLE_BIT) == 0U) {} \
+ assert((val & reg##_V_BIT) != 0U); \
+ } while (0)
+
+#define WAIT_FOR_VIDLE_IRS_SPI_STATUSR(base) \
+ WAIT_FOR_IDLE(base, irs_spi_statusr, IRS_SPI_STATUSR)
+
+struct gicv5_wire_props {
+ /* continuous wire ID as seen by the attached component */
+ uint32_t id;
+ /* use the INTDMN_XYZ macros */
+ uint8_t domain:2;
+ /* use the TM_XYZ (eg. TM_EDGE) macros */
+ uint8_t tm:1;
+};
+
+/* to describe every IRS in the system */
+struct gicv5_irs {
+ /* mapped device nGnRnE by the platform*/
+ uintptr_t el3_config_frame;
+ struct gicv5_wire_props *spis;
+ uint32_t num_spis;
+};
+
struct gicv5_driver_data {
+ struct gicv5_irs *irss;
+ uint32_t num_irss;
};
extern const struct gicv5_driver_data plat_gicv5_driver_data;