GIC: Add API to set interrupt routing

SPIs can be routed to either a specific PE, or to any one of all
available PEs.

API documentation updated.

Change-Id: I28675f634568aaf4ea1aa8aa7ebf25b419a963ed
Co-authored-by: Yousuf A <yousuf.sait@arm.com>
Signed-off-by: Jeenu Viswambharan <jeenu.viswambharan@arm.com>
diff --git a/drivers/arm/gic/v2/gicv2_helpers.c b/drivers/arm/gic/v2/gicv2_helpers.c
index 7cdbc27..2693076 100644
--- a/drivers/arm/gic/v2/gicv2_helpers.c
+++ b/drivers/arm/gic/v2/gicv2_helpers.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015-2016, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2015-2017, ARM Limited and Contributors. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -72,15 +72,6 @@
 	mmio_write_32(base + GICD_SPENDSGIR + (n << 2), val);
 }
 
-/*
- * Accessor to write the GIC Distributor ITARGETSR corresponding to the
- * interrupt `id`.
- */
-void gicd_set_itargetsr(uintptr_t base, unsigned int id, unsigned int target)
-{
-	mmio_write_8(base + GICD_ITARGETSR + id, target & GIC_TARGET_CPU_MASK);
-}
-
 /*******************************************************************************
  * Get the current CPU bit mask from GICD_ITARGETSR0
  ******************************************************************************/
diff --git a/drivers/arm/gic/v2/gicv2_main.c b/drivers/arm/gic/v2/gicv2_main.c
index b59e458..f0b902c 100644
--- a/drivers/arm/gic/v2/gicv2_main.c
+++ b/drivers/arm/gic/v2/gicv2_main.c
@@ -403,3 +403,38 @@
 	dsbishst();
 	gicd_write_sgir(driver_data->gicd_base, sgir_val);
 }
+
+/*******************************************************************************
+ * This function sets the interrupt routing for the given SPI interrupt id.
+ * The interrupt routing is specified in routing mode. The proc_num parameter is
+ * linear index of the PE to target SPI. When proc_num < 0, the SPI may target
+ * all PEs.
+ ******************************************************************************/
+void gicv2_set_spi_routing(unsigned int id, int proc_num)
+{
+	int target;
+
+	assert(driver_data);
+	assert(driver_data->gicd_base);
+
+	assert(id >= MIN_SPI_ID && id <= MAX_SPI_ID);
+
+	/*
+	 * Target masks array must have been supplied, and the core position
+	 * should be valid.
+	 */
+	assert(driver_data->target_masks);
+	assert(proc_num < GICV2_MAX_TARGET_PE);
+	assert(proc_num < driver_data->target_masks_num);
+
+	if (proc_num < 0) {
+		/* Target all PEs */
+		target = GIC_TARGET_CPU_MASK;
+	} else {
+		/* Don't route interrupt if the mask hasn't been populated */
+		target = driver_data->target_masks[proc_num];
+		assert(target != 0);
+	}
+
+	gicd_set_itargetsr(driver_data->gicd_base, id, target);
+}
diff --git a/drivers/arm/gic/v2/gicv2_private.h b/drivers/arm/gic/v2/gicv2_private.h
index 915fc3a..70f0597 100644
--- a/drivers/arm/gic/v2/gicv2_private.h
+++ b/drivers/arm/gic/v2/gicv2_private.h
@@ -34,6 +34,17 @@
 /*******************************************************************************
  * GIC Distributor interface accessors for writing entire registers
  ******************************************************************************/
+static inline unsigned int gicd_get_itargetsr(uintptr_t base, unsigned int id)
+{
+	return mmio_read_8(base + GICD_ITARGETSR + id);
+}
+
+static inline void gicd_set_itargetsr(uintptr_t base, unsigned int id,
+		unsigned int target)
+{
+	mmio_write_8(base + GICD_ITARGETSR + id, target & GIC_TARGET_CPU_MASK);
+}
+
 static inline void gicd_write_sgir(uintptr_t base, unsigned int val)
 {
 	mmio_write_32(base + GICD_SGIR, val);
diff --git a/drivers/arm/gic/v3/gicv3_main.c b/drivers/arm/gic/v3/gicv3_main.c
index a506732..c81ba95 100644
--- a/drivers/arm/gic/v3/gicv3_main.c
+++ b/drivers/arm/gic/v3/gicv3_main.c
@@ -999,3 +999,41 @@
 	write_icc_sgi0r_el1(sgi_val);
 	isb();
 }
+
+/*******************************************************************************
+ * This function sets the interrupt routing for the given SPI interrupt id.
+ * The interrupt routing is specified in routing mode and mpidr.
+ *
+ * The routing mode can be either of:
+ *  - GICV3_IRM_ANY
+ *  - GICV3_IRM_PE
+ *
+ * The mpidr is the affinity of the PE to which the interrupt will be routed,
+ * and is ignored for routing mode GICV3_IRM_ANY.
+ ******************************************************************************/
+void gicv3_set_spi_routing(unsigned int id, unsigned int irm, u_register_t mpidr)
+{
+	unsigned long long aff;
+	uint64_t router;
+
+	assert(gicv3_driver_data);
+	assert(gicv3_driver_data->gicd_base);
+
+	assert((irm == GICV3_IRM_ANY) || (irm == GICV3_IRM_PE));
+	assert(id >= MIN_SPI_ID && id <= MAX_SPI_ID);
+
+	aff = gicd_irouter_val_from_mpidr(mpidr, irm);
+	gicd_write_irouter(gicv3_driver_data->gicd_base, id, aff);
+
+	/*
+	 * In implementations that do not require 1 of N distribution of SPIs,
+	 * IRM might be RAZ/WI. Read back and verify IRM bit.
+	 */
+	if (irm == GICV3_IRM_ANY) {
+		router = gicd_read_irouter(gicv3_driver_data->gicd_base, id);
+		if (!((router >> IROUTER_IRM_SHIFT) & IROUTER_IRM_MASK)) {
+			ERROR("GICv3 implementation doesn't support routing ANY\n");
+			panic();
+		}
+	}
+}