From 979225f4eed00d631bb57ebd09068edd91b8df7b Mon Sep 17 00:00:00 2001
From: Jeenu Viswambharan <jeenu.viswambharan@arm.com>
Date: Fri, 22 Sep 2017 08:32:09 +0100
Subject: [PATCH] GIC: Add APIs to enable and disable interrupt

API documentation updated.

Change-Id: Ice7511f8df5356851001d2f7dc2a46cfe318f9ba
Co-authored-by: Yousuf A <yousuf.sait@arm.com>
Signed-off-by: Jeenu Viswambharan <jeenu.viswambharan@arm.com>
---
 docs/platform-interrupt-controller-API.rst | 30 ++++++++++
 drivers/arm/gic/v2/gicv2_main.c            | 34 ++++++++++++
 drivers/arm/gic/v3/gicv3_helpers.c         | 11 ++++
 drivers/arm/gic/v3/gicv3_main.c            | 64 ++++++++++++++++++++++
 drivers/arm/gic/v3/gicv3_private.h         |  1 +
 include/drivers/arm/gicv2.h                |  2 +
 include/drivers/arm/gicv3.h                |  2 +
 include/plat/common/platform.h             |  2 +
 plat/common/plat_gicv2.c                   | 12 ++++
 plat/common/plat_gicv3.c                   | 12 ++++
 10 files changed, 170 insertions(+)

diff --git a/docs/platform-interrupt-controller-API.rst b/docs/platform-interrupt-controller-API.rst
index d0a2ad276..bea1a64b5 100644
--- a/docs/platform-interrupt-controller-API.rst
+++ b/docs/platform-interrupt-controller-API.rst
@@ -81,6 +81,36 @@ In case of ARM standard platforms using GIC, the implementation of the API reads
 the GIC *Set Active Register* to read and return the active status of the
 interrupt.
 
+Function: void plat_ic_enable_interrupt(unsigned int id); [optional]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+    Argument : unsigned int
+    Return   : void
+
+This API should enable the interrupt ID specified by the first parameter,
+``id``. PEs in the system are expected to receive only enabled interrupts.
+
+In case of ARM standard platforms using GIC, the implementation of the API
+inserts barrier to make memory updates visible before enabling interrupt, and
+then writes to GIC *Set Enable Register* to enable the interrupt.
+
+Function: void plat_ic_disable_interrupt(unsigned int id); [optional]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+    Argument : unsigned int
+    Return   : void
+
+This API should disable the interrupt ID specified by the first parameter,
+``id``. PEs in the system are not expected to receive disabled interrupts.
+
+In case of ARM standard platforms using GIC, the implementation of the API
+writes to GIC *Clear Enable Register* to disable the interrupt, and inserts
+barrier to make memory updates visible afterwards.
+
 ----
 
 *Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.*
diff --git a/drivers/arm/gic/v2/gicv2_main.c b/drivers/arm/gic/v2/gicv2_main.c
index e33353a88..e0058ad13 100644
--- a/drivers/arm/gic/v2/gicv2_main.c
+++ b/drivers/arm/gic/v2/gicv2_main.c
@@ -288,3 +288,37 @@ unsigned int gicv2_get_interrupt_active(unsigned int id)
 
 	return gicd_get_isactiver(driver_data->gicd_base, id);
 }
+
+/*******************************************************************************
+ * This function enables the interrupt identified by id.
+ ******************************************************************************/
+void gicv2_enable_interrupt(unsigned int id)
+{
+	assert(driver_data);
+	assert(driver_data->gicd_base);
+	assert(id <= MAX_SPI_ID);
+
+	/*
+	 * Ensure that any shared variable updates depending on out of band
+	 * interrupt trigger are observed before enabling interrupt.
+	 */
+	dsbishst();
+	gicd_set_isenabler(driver_data->gicd_base, id);
+}
+
+/*******************************************************************************
+ * This function disables the interrupt identified by id.
+ ******************************************************************************/
+void gicv2_disable_interrupt(unsigned int id)
+{
+	assert(driver_data);
+	assert(driver_data->gicd_base);
+	assert(id <= MAX_SPI_ID);
+
+	/*
+	 * Disable interrupt, and ensure that any shared variable updates
+	 * depending on out of band interrupt trigger are observed afterwards.
+	 */
+	gicd_set_icenabler(driver_data->gicd_base, id);
+	dsbishst();
+}
diff --git a/drivers/arm/gic/v3/gicv3_helpers.c b/drivers/arm/gic/v3/gicv3_helpers.c
index 81d50ad45..ee874f92f 100644
--- a/drivers/arm/gic/v3/gicv3_helpers.c
+++ b/drivers/arm/gic/v3/gicv3_helpers.c
@@ -171,6 +171,17 @@ void gicr_set_isenabler0(uintptr_t base, unsigned int id)
 	gicr_write_isenabler0(base, (1 << bit_num));
 }
 
+/*
+ * Accessor to set the bit corresponding to interrupt ID in GIC Re-distributor
+ * ICENABLER0.
+ */
+void gicr_set_icenabler0(uintptr_t base, unsigned int id)
+{
+	unsigned bit_num = id & ((1 << ICENABLER_SHIFT) - 1);
+
+	gicr_write_icenabler0(base, (1 << bit_num));
+}
+
 /*
  * Accessor to set the bit corresponding to interrupt ID in GIC Re-distributor
  * ISACTIVER0.
diff --git a/drivers/arm/gic/v3/gicv3_main.c b/drivers/arm/gic/v3/gicv3_main.c
index 08cf0957d..b3231993e 100644
--- a/drivers/arm/gic/v3/gicv3_main.c
+++ b/drivers/arm/gic/v3/gicv3_main.c
@@ -805,3 +805,67 @@ unsigned int gicv3_get_interrupt_active(unsigned int id, unsigned int proc_num)
 
 	return value;
 }
+
+/*******************************************************************************
+ * This function enables the interrupt identified by id. The proc_num
+ * is used if the interrupt is SGI or PPI, and programs the corresponding
+ * Redistributor interface.
+ ******************************************************************************/
+void gicv3_enable_interrupt(unsigned int id, unsigned int proc_num)
+{
+	assert(gicv3_driver_data);
+	assert(gicv3_driver_data->gicd_base);
+	assert(proc_num < gicv3_driver_data->rdistif_num);
+	assert(gicv3_driver_data->rdistif_base_addrs);
+	assert(id <= MAX_SPI_ID);
+
+	/*
+	 * Ensure that any shared variable updates depending on out of band
+	 * interrupt trigger are observed before enabling interrupt.
+	 */
+	dsbishst();
+	if (id < MIN_SPI_ID) {
+		/* For SGIs and PPIs */
+		gicr_set_isenabler0(
+				gicv3_driver_data->rdistif_base_addrs[proc_num],
+				id);
+	} else {
+		gicd_set_isenabler(gicv3_driver_data->gicd_base, id);
+	}
+}
+
+/*******************************************************************************
+ * This function disables the interrupt identified by id. The proc_num
+ * is used if the interrupt is SGI or PPI, and programs the corresponding
+ * Redistributor interface.
+ ******************************************************************************/
+void gicv3_disable_interrupt(unsigned int id, unsigned int proc_num)
+{
+	assert(gicv3_driver_data);
+	assert(gicv3_driver_data->gicd_base);
+	assert(proc_num < gicv3_driver_data->rdistif_num);
+	assert(gicv3_driver_data->rdistif_base_addrs);
+	assert(id <= MAX_SPI_ID);
+
+	/*
+	 * Disable interrupt, and ensure that any shared variable updates
+	 * depending on out of band interrupt trigger are observed afterwards.
+	 */
+	if (id < MIN_SPI_ID) {
+		/* For SGIs and PPIs */
+		gicr_set_icenabler0(
+				gicv3_driver_data->rdistif_base_addrs[proc_num],
+				id);
+
+		/* Write to clear enable requires waiting for pending writes */
+		gicr_wait_for_pending_write(
+				gicv3_driver_data->rdistif_base_addrs[proc_num]);
+	} else {
+		gicd_set_icenabler(gicv3_driver_data->gicd_base, id);
+
+		/* Write to clear enable requires waiting for pending writes */
+		gicd_wait_for_pending_write(gicv3_driver_data->gicd_base);
+	}
+
+	dsbishst();
+}
diff --git a/drivers/arm/gic/v3/gicv3_private.h b/drivers/arm/gic/v3/gicv3_private.h
index bb8ad9ae8..19fce2e73 100644
--- a/drivers/arm/gic/v3/gicv3_private.h
+++ b/drivers/arm/gic/v3/gicv3_private.h
@@ -71,6 +71,7 @@ unsigned int gicr_get_isactiver0(uintptr_t base, unsigned int id);
 void gicd_set_igrpmodr(uintptr_t base, unsigned int id);
 void gicr_set_igrpmodr0(uintptr_t base, unsigned int id);
 void gicr_set_isenabler0(uintptr_t base, unsigned int id);
+void gicr_set_icenabler0(uintptr_t base, unsigned int id);
 void gicr_set_igroupr0(uintptr_t base, unsigned int id);
 void gicd_clr_igrpmodr(uintptr_t base, unsigned int id);
 void gicr_clr_igrpmodr0(uintptr_t base, unsigned int id);
diff --git a/include/drivers/arm/gicv2.h b/include/drivers/arm/gicv2.h
index 2b4612637..0af1a25e2 100644
--- a/include/drivers/arm/gicv2.h
+++ b/include/drivers/arm/gicv2.h
@@ -148,6 +148,8 @@ unsigned int gicv2_get_interrupt_group(unsigned int id);
 unsigned int gicv2_get_running_priority(void);
 void gicv2_set_pe_target_mask(unsigned int proc_num);
 unsigned int gicv2_get_interrupt_active(unsigned int id);
+void gicv2_enable_interrupt(unsigned int id);
+void gicv2_disable_interrupt(unsigned int id);
 
 #endif /* __ASSEMBLY__ */
 #endif /* __GICV2_H__ */
diff --git a/include/drivers/arm/gicv3.h b/include/drivers/arm/gicv3.h
index ec272ea7c..6e6a47b97 100644
--- a/include/drivers/arm/gicv3.h
+++ b/include/drivers/arm/gicv3.h
@@ -351,6 +351,8 @@ void gicv3_its_restore(uintptr_t gits_base, const gicv3_its_ctx_t * const its_ct
 
 unsigned int gicv3_get_running_priority(void);
 unsigned int gicv3_get_interrupt_active(unsigned int id, unsigned int proc_num);
+void gicv3_enable_interrupt(unsigned int id, unsigned int proc_num);
+void gicv3_disable_interrupt(unsigned int id, unsigned int proc_num);
 
 #endif /* __ASSEMBLY__ */
 #endif /* __GICV3_H__ */
diff --git a/include/plat/common/platform.h b/include/plat/common/platform.h
index e4fb6a9f9..671aa61e8 100644
--- a/include/plat/common/platform.h
+++ b/include/plat/common/platform.h
@@ -77,6 +77,8 @@ int plat_ic_is_spi(unsigned int id);
 int plat_ic_is_ppi(unsigned int id);
 int plat_ic_is_sgi(unsigned int id);
 unsigned int plat_ic_get_interrupt_active(unsigned int id);
+void plat_ic_disable_interrupt(unsigned int id);
+void plat_ic_enable_interrupt(unsigned int id);
 
 /*******************************************************************************
  * Optional common functions (may be overridden)
diff --git a/plat/common/plat_gicv2.c b/plat/common/plat_gicv2.c
index 5a92b66b9..2591805de 100644
--- a/plat/common/plat_gicv2.c
+++ b/plat/common/plat_gicv2.c
@@ -25,6 +25,8 @@
 #pragma weak plat_ic_is_ppi
 #pragma weak plat_ic_is_sgi
 #pragma weak plat_ic_get_interrupt_active
+#pragma weak plat_ic_enable_interrupt
+#pragma weak plat_ic_disable_interrupt
 
 /*
  * This function returns the highest priority pending interrupt at
@@ -153,3 +155,13 @@ unsigned int plat_ic_get_interrupt_active(unsigned int id)
 {
 	return gicv2_get_interrupt_active(id);
 }
+
+void plat_ic_enable_interrupt(unsigned int id)
+{
+	gicv2_enable_interrupt(id);
+}
+
+void plat_ic_disable_interrupt(unsigned int id)
+{
+	gicv2_disable_interrupt(id);
+}
diff --git a/plat/common/plat_gicv3.c b/plat/common/plat_gicv3.c
index abb3f1d16..2a1f0f5c5 100644
--- a/plat/common/plat_gicv3.c
+++ b/plat/common/plat_gicv3.c
@@ -31,6 +31,8 @@
 #pragma weak plat_ic_is_ppi
 #pragma weak plat_ic_is_sgi
 #pragma weak plat_ic_get_interrupt_active
+#pragma weak plat_ic_enable_interrupt
+#pragma weak plat_ic_disable_interrupt
 
 CASSERT((INTR_TYPE_S_EL1 == INTR_GROUP1S) &&
 	(INTR_TYPE_NS == INTR_GROUP1NS) &&
@@ -186,6 +188,16 @@ unsigned int plat_ic_get_interrupt_active(unsigned int id)
 {
 	return gicv3_get_interrupt_active(id, plat_my_core_pos());
 }
+
+void plat_ic_enable_interrupt(unsigned int id)
+{
+	gicv3_enable_interrupt(id, plat_my_core_pos());
+}
+
+void plat_ic_disable_interrupt(unsigned int id)
+{
+	gicv3_disable_interrupt(id, plat_my_core_pos());
+}
 #endif
 #ifdef IMAGE_BL32
 
-- 
GitLab