interrupt_controller: gicv3: add support for LPIs
The LPI (Locality-specific Peripheral Interrupts) are edge-triggered
message-based interrupts that can use an Interrupt Translation
Service (ITS) to route an interrupt to a specific Redistributor and
connected PE.
This implement the necessary LPI support when an ITS is enabled.
The LPI states are stored in memory-backed tables.
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
diff --git a/drivers/interrupt_controller/intc_gicv3.c b/drivers/interrupt_controller/intc_gicv3.c
index 18de217..042d6c0 100644
--- a/drivers/interrupt_controller/intc_gicv3.c
+++ b/drivers/interrupt_controller/intc_gicv3.c
@@ -11,6 +11,8 @@
#include "intc_gic_common_priv.h"
#include "intc_gicv3_priv.h"
+#include <string.h>
+
/* Redistributor base addresses for each core */
mem_addr_t gic_rdists[CONFIG_MP_NUM_CPUS];
@@ -20,6 +22,20 @@
#define IGROUPR_VAL 0x0U
#endif
+/*
+ * We allocate memory for PROPBASE to cover 2 ^ lpi_id_bits LPIs to
+ * deal with (one configuration byte per interrupt). PENDBASE has to
+ * be 64kB aligned (one bit per LPI, plus 8192 bits for SPI/PPI/SGI).
+ */
+#define ITS_MAX_LPI_NRBITS 16 /* 64K LPIs */
+
+#define LPI_PROPBASE_SZ(nrbits) ROUND_UP(BIT(nrbits), KB(64))
+#define LPI_PENDBASE_SZ(nrbits) ROUND_UP(BIT(nrbits) / 8, KB(64))
+
+#ifdef CONFIG_GIC_V3_ITS
+static uintptr_t lpi_prop_table;
+#endif
+
static inline mem_addr_t gic_get_rdist(void)
{
return gic_rdists[arch_curr_cpu()->id];
@@ -48,9 +64,47 @@
return 0;
}
+#ifdef CONFIG_GIC_V3_ITS
+static void arm_gic_lpi_setup(unsigned int intid, bool enable)
+{
+ uint8_t *cfg = &((uint8_t *)lpi_prop_table)[intid - 8192];
+
+ if (enable) {
+ *cfg |= BIT(0);
+ } else {
+ *cfg &= ~BIT(0);
+ }
+
+ dsb();
+}
+
+static void arm_gic_lpi_set_priority(unsigned int intid, unsigned int prio)
+{
+ uint8_t *cfg = &((uint8_t *)lpi_prop_table)[intid - 8192];
+
+ *cfg &= 0xfc;
+ *cfg |= prio & 0xfc;
+
+ dsb();
+}
+
+static bool arm_gic_lpi_is_enabled(unsigned int intid)
+{
+ uint8_t *cfg = &((uint8_t *)lpi_prop_table)[intid - 8192];
+
+ return (*cfg & BIT(0));
+}
+#endif
+
void arm_gic_irq_set_priority(unsigned int intid,
unsigned int prio, uint32_t flags)
{
+#ifdef CONFIG_GIC_V3_ITS
+ if (intid >= 8192) {
+ arm_gic_lpi_set_priority(intid, prio);
+ return;
+ }
+#endif
uint32_t mask = BIT(intid & (GIC_NUM_INTR_PER_REG - 1));
uint32_t idx = intid / GIC_NUM_INTR_PER_REG;
uint32_t shift;
@@ -80,6 +134,12 @@
void arm_gic_irq_enable(unsigned int intid)
{
+#ifdef CONFIG_GIC_V3_ITS
+ if (intid >= 8192) {
+ arm_gic_lpi_setup(intid, true);
+ return;
+ }
+#endif
uint32_t mask = BIT(intid & (GIC_NUM_INTR_PER_REG - 1));
uint32_t idx = intid / GIC_NUM_INTR_PER_REG;
@@ -99,6 +159,12 @@
void arm_gic_irq_disable(unsigned int intid)
{
+#ifdef CONFIG_GIC_V3_ITS
+ if (intid >= 8192) {
+ arm_gic_lpi_setup(intid, false);
+ return;
+ }
+#endif
uint32_t mask = BIT(intid & (GIC_NUM_INTR_PER_REG - 1));
uint32_t idx = intid / GIC_NUM_INTR_PER_REG;
@@ -109,6 +175,11 @@
bool arm_gic_irq_is_enabled(unsigned int intid)
{
+#ifdef CONFIG_GIC_V3_ITS
+ if (intid >= 8192) {
+ return arm_gic_lpi_is_enabled(intid);
+ }
+#endif
uint32_t mask = BIT(intid & (GIC_NUM_INTR_PER_REG - 1));
uint32_t idx = intid / GIC_NUM_INTR_PER_REG;
uint32_t val;
@@ -184,6 +255,58 @@
;
}
+#ifdef CONFIG_GIC_V3_ITS
+/*
+ * Setup LPIs Configuration & Pending tables for redistributors
+ * LPI configuration is global, each redistributor has a pending table
+ */
+static void gicv3_rdist_setup_lpis(mem_addr_t rdist)
+{
+ unsigned int lpi_id_bits = MIN(GICD_TYPER_IDBITS(sys_read32(GICD_TYPER)),
+ ITS_MAX_LPI_NRBITS);
+ uintptr_t lpi_pend_table;
+ uint64_t reg;
+ uint32_t ctlr;
+
+ /* If not, alloc a common prop table for all redistributors */
+ if (!lpi_prop_table) {
+ lpi_prop_table = (uintptr_t)k_aligned_alloc(4 * 1024, LPI_PROPBASE_SZ(lpi_id_bits));
+ memset((void *)lpi_prop_table, 0, LPI_PROPBASE_SZ(lpi_id_bits));
+ }
+
+ lpi_pend_table = (uintptr_t)k_aligned_alloc(64 * 1024, LPI_PENDBASE_SZ(lpi_id_bits));
+ memset((void *)lpi_pend_table, 0, LPI_PENDBASE_SZ(lpi_id_bits));
+
+ ctlr = sys_read32(rdist + GICR_CTLR);
+ ctlr &= ~GICR_CTLR_ENABLE_LPIS;
+ sys_write32(ctlr, rdist + GICR_CTLR);
+
+ /* PROPBASE */
+ reg = (GIC_BASER_SHARE_INNER << GITR_PROPBASER_SHAREABILITY_SHIFT) |
+ (GIC_BASER_CACHE_RAWAWB << GITR_PROPBASER_INNER_CACHE_SHIFT) |
+ (lpi_prop_table & (GITR_PROPBASER_ADDR_MASK << GITR_PROPBASER_ADDR_SHIFT)) |
+ (GIC_BASER_CACHE_INNERLIKE << GITR_PROPBASER_OUTER_CACHE_SHIFT) |
+ ((lpi_id_bits - 1) & GITR_PROPBASER_ID_BITS_MASK);
+ sys_write64(reg, rdist + GICR_PROPBASER);
+ /* TOFIX: check SHAREABILITY validity */
+
+ /* PENDBASE */
+ reg = (GIC_BASER_SHARE_INNER << GITR_PENDBASER_SHAREABILITY_SHIFT) |
+ (GIC_BASER_CACHE_RAWAWB << GITR_PENDBASER_INNER_CACHE_SHIFT) |
+ (lpi_pend_table & (GITR_PENDBASER_ADDR_MASK << GITR_PENDBASER_ADDR_SHIFT)) |
+ (GIC_BASER_CACHE_INNERLIKE << GITR_PENDBASER_OUTER_CACHE_SHIFT) |
+ GITR_PENDBASER_PTZ;
+ sys_write64(reg, rdist + GICR_PENDBASER);
+ /* TOFIX: check SHAREABILITY validity */
+
+ ctlr = sys_read32(rdist + GICR_CTLR);
+ ctlr |= GICR_CTLR_ENABLE_LPIS;
+ sys_write32(ctlr, rdist + GICR_CTLR);
+
+ dsb();
+}
+#endif
+
/*
* Initialize the cpu interface. This should be called by each core.
*/
@@ -334,6 +457,11 @@
cpu = arch_curr_cpu()->id;
gic_rdists[cpu] = GIC_RDIST_BASE + MPIDR_TO_CORE(GET_MPIDR()) * 0x20000;
+#ifdef CONFIG_GIC_V3_ITS
+ /* Enable LPIs in Redistributor */
+ gicv3_rdist_setup_lpis(gic_get_rdist());
+#endif
+
gicv3_rdist_enable(gic_get_rdist());
gicv3_cpuif_init();
diff --git a/drivers/interrupt_controller/intc_gicv3_priv.h b/drivers/interrupt_controller/intc_gicv3_priv.h
index 31f08e7..c5ed758 100644
--- a/drivers/interrupt_controller/intc_gicv3_priv.h
+++ b/drivers/interrupt_controller/intc_gicv3_priv.h
@@ -10,6 +10,20 @@
#include <zephyr/types.h>
#include <device.h>
+/* Cache and Share ability for ITS & Redistributor LPI state tables */
+#define GIC_BASER_CACHE_NGNRNE 0x0UL /* Device-nGnRnE */
+#define GIC_BASER_CACHE_INNERLIKE 0x0UL /* Same as Inner Cacheability. */
+#define GIC_BASER_CACHE_NCACHEABLE 0x1UL /* Normal Outer Non-cacheable */
+#define GIC_BASER_CACHE_RAWT 0x2UL /* Normal Outer Cacheable Read-allocate, Write-through */
+#define GIC_BASER_CACHE_RAWB 0x3UL /* Normal Outer Cacheable Read-allocate, Write-back */
+#define GIC_BASER_CACHE_WAWT 0x4UL /* Normal Outer Cacheable Write-allocate, Write-through */
+#define GIC_BASER_CACHE_WAWB 0x5UL /* Normal Outer Cacheable Write-allocate, Write-back */
+#define GIC_BASER_CACHE_RAWAWT 0x6UL /* Normal Outer Cacheable Read-allocate, Write-allocate, Write-through */
+#define GIC_BASER_CACHE_RAWAWB 0x7UL /* Normal Outer Cacheable Read-allocate, Write-allocate, Write-back */
+#define GIC_BASER_SHARE_NO 0x0UL /* Non-shareable */
+#define GIC_BASER_SHARE_INNER 0x1UL /* Inner Shareable */
+#define GIC_BASER_SHARE_OUTER 0x2UL /* Outer Shareable */
+
/*
* GIC Register Interface Base Addresses
*/
@@ -25,6 +39,8 @@
#define GICR_TYPER 0x0008
#define GICR_STATUSR 0x0010
#define GICR_WAKER 0x0014
+#define GICR_PROPBASER 0x0070
+#define GICR_PENDBASER 0x0078
/* Register bit definations */
@@ -41,12 +57,35 @@
#define GICD_CTLR_RWP 31
/* GICR_CTLR */
+#define GICR_CTLR_ENABLE_LPIS BIT(0)
#define GICR_CTLR_RWP 3
/* GICR_WAKER */
#define GICR_WAKER_PS 1
#define GICR_WAKER_CA 2
+/* GICR_PROPBASER */
+#define GITR_PROPBASER_ID_BITS_MASK 0x1fUL
+#define GITR_PROPBASER_INNER_CACHE_SHIFT 7
+#define GITR_PROPBASER_INNER_CACHE_MASK 0x7UL
+#define GITR_PROPBASER_SHAREABILITY_SHIFT 10
+#define GITR_PROPBASER_SHAREABILITY_MASK 0x3UL
+#define GITR_PROPBASER_ADDR_SHIFT 12
+#define GITR_PROPBASER_ADDR_MASK 0xFFFFFFFFFFUL
+#define GITR_PROPBASER_OUTER_CACHE_SHIFT 56
+#define GITR_PROPBASER_OUTER_CACHE_MASK 0x7UL
+
+/* GICR_PENDBASER */
+#define GITR_PENDBASER_INNER_CACHE_SHIFT 7
+#define GITR_PENDBASER_INNER_CACHE_MASK 0x7UL
+#define GITR_PENDBASER_SHAREABILITY_SHIFT 10
+#define GITR_PENDBASER_SHAREABILITY_MASK 0x3UL
+#define GITR_PENDBASER_ADDR_SHIFT 16
+#define GITR_PENDBASER_ADDR_MASK 0xFFFFFFFFFUL
+#define GITR_PENDBASER_OUTER_CACHE_SHIFT 56
+#define GITR_PENDBASER_OUTER_CACHE_MASK 0x7UL
+#define GITR_PENDBASER_PTZ BIT64(62)
+
/* GITCD_IROUTER */
#define GIC_DIST_IROUTER 0x6000
#define IROUTER(base, n) (base + GIC_DIST_IROUTER + (n) * 8)
diff --git a/include/drivers/interrupt_controller/gic.h b/include/drivers/interrupt_controller/gic.h
index 5fb605f..a46bc6c 100644
--- a/include/drivers/interrupt_controller/gic.h
+++ b/include/drivers/interrupt_controller/gic.h
@@ -216,6 +216,9 @@
/* GICD_TYPER.ITLinesNumber 0:4 */
#define GICD_TYPER_ITLINESNUM_MASK 0x1f
+/* GICD_TYPER.IDbits */
+#define GICD_TYPER_IDBITS(typer) ((((typer) >> 19) & 0x1f) + 1)
+
/*
* Common Helper Constants
*/