Merge pull request #1130 from jeenu-arm/gic-patches
New GIC APIs and specifying interrupt propertes
diff --git a/Makefile b/Makefile
index a7d3a87..b32b417 100644
--- a/Makefile
+++ b/Makefile
@@ -449,6 +449,7 @@
$(eval $(call assert_boolean,ENABLE_SPE_FOR_LOWER_ELS))
$(eval $(call assert_boolean,ERROR_DEPRECATED))
$(eval $(call assert_boolean,GENERATE_COT))
+$(eval $(call assert_boolean,GICV2_G0_FOR_EL3))
$(eval $(call assert_boolean,HW_ASSISTED_COHERENCY))
$(eval $(call assert_boolean,LOAD_IMAGE_V2))
$(eval $(call assert_boolean,NS_TIMER_SWITCH))
@@ -486,6 +487,7 @@
$(eval $(call add_define,ENABLE_RUNTIME_INSTRUMENTATION))
$(eval $(call add_define,ENABLE_SPE_FOR_LOWER_ELS))
$(eval $(call add_define,ERROR_DEPRECATED))
+$(eval $(call add_define,GICV2_G0_FOR_EL3))
$(eval $(call add_define,HW_ASSISTED_COHERENCY))
$(eval $(call add_define,LOAD_IMAGE_V2))
$(eval $(call add_define,LOG_LEVEL))
diff --git a/docs/firmware-design.rst b/docs/firmware-design.rst
index e16e9b8..0790597 100644
--- a/docs/firmware-design.rst
+++ b/docs/firmware-design.rst
@@ -1167,6 +1167,56 @@
already been performed and act as appropriate. Possible courses of actions are,
e.g. skip the action the second time, or undo/redo it.
+Configuring secure interrupts
+-----------------------------
+
+The GIC driver is responsible for performing initial configuration of secure
+interrupts on the platform. To this end, the platform is expected to provide the
+GIC driver (either GICv2 or GICv3, as selected by the platform) with the
+interrupt configuration during the driver initialisation.
+
+There are two ways to specify secure interrupt configuration:
+
+#. Array of secure interrupt properties: In this scheme, in both GICv2 and GICv3
+ driver data structures, the ``interrupt_props`` member points to an array of
+ interrupt properties. Each element of the array specifies the interrupt
+ number and its configuration, viz. priority, group, configuration. Each
+ element of the array shall be populated by the macro ``INTR_PROP_DESC()``.
+ The macro takes the following arguments:
+
+ - 10-bit interrupt number,
+
+ - 8-bit interrupt priority,
+
+ - Interrupt type (one of ``INTR_TYPE_EL3``, ``INTR_TYPE_S_EL1``,
+ ``INTR_TYPE_NS``),
+
+ - Interrupt configuration (either ``GIC_INTR_CFG_LEVEL`` or
+ ``GIC_INTR_CFG_EDGE``).
+
+#. Array of secure interrupts: In this scheme, the GIC driver is provided an
+ array of secure interrupt numbers. The GIC driver, at the time of
+ initialisation, iterates through the array and assigns each interrupt
+ the appropriate group.
+
+ - For the GICv2 driver, in ``gicv2_driver_data`` structure, the
+ ``g0_interrupt_array`` member of the should point to the array of
+ interrupts to be assigned to *Group 0*, and the ``g0_interrupt_num``
+ member of the should be set to the number of interrupts in the array.
+
+ - For the GICv3 driver, in ``gicv3_driver_data`` structure:
+
+ - The ``g0_interrupt_array`` member of the should point to the array of
+ interrupts to be assigned to *Group 0*, and the ``g0_interrupt_num``
+ member of the should be set to the number of interrupts in the array.
+
+ - The ``g1s_interrupt_array`` member of the should point to the array of
+ interrupts to be assigned to *Group 1 Secure*, and the
+ ``g1s_interrupt_num`` member of the should be set to the number of
+ interrupts in the array.
+
+ **Note that this scheme is deprecated.**
+
CPU specific operations framework
---------------------------------
diff --git a/docs/platform-interrupt-controller-API.rst b/docs/platform-interrupt-controller-API.rst
new file mode 100644
index 0000000..795c085
--- /dev/null
+++ b/docs/platform-interrupt-controller-API.rst
@@ -0,0 +1,297 @@
+Platform Interrupt Controller API documentation
+===============================================
+
+.. section-numbering::
+ :suffix: .
+
+.. contents::
+
+This document lists the optional platform interrupt controller API that
+abstracts the runtime configuration and control of interrupt controller from the
+generic code. The mandatory APIs are described in the `porting guide`__.
+
+.. __: porting-guide.rst#interrupt-management-framework-in-bl31
+
+Function: unsigned int plat_ic_get_running_priority(void); [optional]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+ Argument : void
+ Return : unsigned int
+
+This API should return the priority of the interrupt the PE is currently
+servicing. This must be be called only after an interrupt has already been
+acknowledged via. ``plat_ic_acknowledge_interrupt``.
+
+In the case of ARM standard platforms using GIC, the *Running Priority Register*
+is read to determine the priority of the interrupt.
+
+Function: int plat_ic_is_spi(unsigned int id); [optional]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+ Argument : unsigned int
+ Return : int
+
+The API should return whether the interrupt ID (first parameter) is categorized
+as a Shared Peripheral Interrupt. Shared Peripheral Interrupts are typically
+associated to system-wide peripherals, and these interrupts can target any PE in
+the system.
+
+Function: int plat_ic_is_ppi(unsigned int id); [optional]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+ Argument : unsigned int
+ Return : int
+
+The API should return whether the interrupt ID (first parameter) is categorized
+as a Private Peripheral Interrupt. Private Peripheral Interrupts are typically
+associated with peripherals that are private to each PE. Interrupts from private
+peripherals target to that PE only.
+
+Function: int plat_ic_is_sgi(unsigned int id); [optional]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+ Argument : unsigned int
+ Return : int
+
+The API should return whether the interrupt ID (first parameter) is categorized
+as a Software Generated Interrupt. Software Generated Interrupts are raised by
+explicit programming by software, and are typically used in inter-PE
+communication. Secure SGIs are reserved for use by Secure world software.
+
+Function: unsigned int plat_ic_get_interrupt_active(unsigned int id); [optional]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+ Argument : unsigned int
+ Return : int
+
+This API should return the *active* status of the interrupt ID specified by the
+first parameter, ``id``.
+
+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.
+
+Function: void plat_ic_set_interrupt_priority(unsigned int id, unsigned int priority); [optional]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+ Argument : unsigned int
+ Argument : unsigned int
+ Return : void
+
+This API should set the priority of the interrupt specified by first parameter
+``id`` to the value set by the second parameter ``priority``.
+
+In case of ARM standard platforms using GIC, the implementation of the API
+writes to GIC *Priority Register* set interrupt priority.
+
+Function: int plat_ic_has_interrupt_type(unsigned int type); [optional]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+ Argument : unsigned int
+ Return : int
+
+This API should return whether the platform supports a given interrupt type. The
+parameter ``type`` shall be one of ``INTR_TYPE_EL3``, ``INTR_TYPE_S_EL1``, or
+``INTR_TYPE_NS``.
+
+In case of ARM standard platforms using GICv3, the implementation of the API
+returns ``1`` for all interrupt types.
+
+In case of ARM standard platforms using GICv2, the API always return ``1`` for
+``INTR_TYPE_NS``. Return value for other types depends on the value of build
+option ``GICV2_G0_FOR_EL3``:
+
+- For interrupt type ``INTR_TYPE_EL3``:
+
+ - When ``GICV2_G0_FOR_EL3`` is ``0``, it returns ``0``, indicating no support
+ for EL3 interrupts.
+
+ - When ``GICV2_G0_FOR_EL3`` is ``1``, it returns ``1``, indicating support for
+ EL3 interrupts.
+
+- For interrupt type ``INTR_TYPE_S_EL1``:
+
+ - When ``GICV2_G0_FOR_EL3`` is ``0``, it returns ``1``, indicating support for
+ Secure EL1 interrupts.
+
+ - When ``GICV2_G0_FOR_EL3`` is ``1``, it returns ``0``, indicating no support
+ for Secure EL1 interrupts.
+
+Function: void plat_ic_set_interrupt_type(unsigned int id, unsigned int type); [optional]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+ Argument : unsigned int
+ Argument : unsigned int
+ Return : void
+
+This API should set the interrupt specified by first parameter ``id`` to the
+type specified by second parameter ``type``. The ``type`` parameter can be
+one of:
+
+- ``INTR_TYPE_NS``: interrupt is meant to be consumed by the Non-secure world.
+
+- ``INTR_TYPE_S_EL1``: interrupt is meant to be consumed by Secure EL1.
+
+- ``INTR_TYPE_EL3``: interrupt is meant to be consumed by EL3.
+
+In case of ARM standard platforms using GIC, the implementation of the API
+writes to the GIC *Group Register* and *Group Modifier Register* (only GICv3) to
+assign the interrupt to the right group.
+
+For GICv3:
+
+- ``INTR_TYPE_NS`` maps to Group 1 interrupt.
+
+- ``INTR_TYPE_S_EL1`` maps to Secure Group 1 interrupt.
+
+- ``INTR_TYPE_EL3`` maps to Secure Group 0 interrupt.
+
+For GICv2:
+
+- ``INTR_TYPE_NS`` maps to Group 1 interrupt.
+
+- When the build option ``GICV2_G0_FOR_EL3`` is set to ``0`` (the default),
+ ``INTR_TYPE_S_EL1`` maps to Group 0. Otherwise, ``INTR_TYPE_EL3`` maps to
+ Group 0 interrupt.
+
+Function: void plat_ic_raise_el3_sgi(int sgi_num, u_register_t target); [optional]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+ Argument : int
+ Argument : u_register_t
+ Return : void
+
+This API should raise an EL3 SGI. The first parameter, ``sgi_num``, specifies
+the ID of the SGI. The second parameter, ``target``, must be the MPIDR of the
+target PE.
+
+In case of ARM standard platforms using GIC, the implementation of the API
+inserts barrier to make memory updates visible before raising SGI, then writes
+to appropriate *SGI Register* in order to raise the EL3 SGI.
+
+Function: void plat_ic_set_spi_routing(unsigned int id, unsigned int routing_mode, u_register_t mpidr); [optional]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+ Argument : unsigned int
+ Argument : unsigned int
+ Argument : u_register_t
+ Return : void
+
+This API should set the routing mode of Share Peripheral Interrupt (SPI)
+specified by first parameter ``id`` to that specified by the second parameter
+``routing_mode``.
+
+The ``routing_mode`` parameter can be one of:
+
+- ``INTR_ROUTING_MODE_ANY`` means the interrupt can be routed to any PE in the
+ system. The ``mpidr`` parameter is ignored in this case.
+
+- ``INTR_ROUTING_MODE_PE`` means the interrupt is routed to the PE whose MPIDR
+ value is specified by the parameter ``mpidr``.
+
+In case of ARM standard platforms using GIC, the implementation of the API
+writes to the GIC *Target Register* (GICv2) or *Route Register* (GICv3) to set
+the routing.
+
+Function: void plat_ic_set_interrupt_pending(unsigned int id); [optional]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+ Argument : unsigned int
+ Return : void
+
+This API should set the interrupt specified by first parameter ``id`` to
+*Pending*.
+
+In case of ARM standard platforms using GIC, the implementation of the API
+inserts barrier to make memory updates visible before setting interrupt pending,
+and writes to the GIC *Set Pending Register* to set the interrupt pending
+status.
+
+Function: void plat_ic_clear_interrupt_pending(unsigned int id); [optional]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+ Argument : unsigned int
+ Return : void
+
+This API should clear the *Pending* status of the interrupt specified by first
+parameter ``id``.
+
+In case of ARM standard platforms using GIC, the implementation of the API
+writes to the GIC *Clear Pending Register* to clear the interrupt pending
+status, and inserts barrier to make memory updates visible afterwards.
+
+Function: unsigned int plat_ic_set_priority_mask(unsigned int id); [optional]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+ Argument : unsigned int
+ Return : int
+
+This API should set the priority mask (first parameter) in the interrupt
+controller such that only interrupts of higher priority than the supplied one
+may be signalled to the PE. The API should return the current priority value
+that it's overwriting.
+
+In case of ARM standard platforms using GIC, the implementation of the API
+inserts to order memory updates before updating mask, then writes to the GIC
+*Priority Mask Register*, and make sure memory updates are visible before
+potential trigger due to mask update.
+
+----
+
+*Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.*
diff --git a/docs/porting-guide.rst b/docs/porting-guide.rst
index cd3bcda..6352bb9 100644
--- a/docs/porting-guide.rst
+++ b/docs/porting-guide.rst
@@ -2327,6 +2327,10 @@
GICv3 depending on the build flag ``FVP_USE_GIC_DRIVER`` (See FVP platform
specific build options in `User Guide`_ for more details).
+See also: `Interrupt Controller Abstraction APIs`__.
+
+.. __: platform-interrupt-controller-API.rst
+
Function : plat\_interrupt\_type\_to\_line() [mandatory]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -2674,7 +2678,7 @@
--------------
-*Copyright (c) 2013-2016, ARM Limited and Contributors. All rights reserved.*
+*Copyright (c) 2013-2017, ARM Limited and Contributors. All rights reserved.*
.. _Migration Guide: platform-migration-guide.rst
.. _include/plat/common/platform.h: ../include/plat/common/platform.h
diff --git a/docs/user-guide.rst b/docs/user-guide.rst
index 7c0a980..28483f2 100644
--- a/docs/user-guide.rst
+++ b/docs/user-guide.rst
@@ -386,6 +386,19 @@
images will include support for Trusted Board Boot, but the FIP and FWU\_FIP
will not include the corresponding certificates, causing a boot failure.
+- ``GICV2_G0_FOR_EL3``: Unlike GICv3, the GICv2 architecture doesn't have
+ inherent support for specific EL3 type interrupts. Setting this build option
+ to ``1`` assumes GICv2 *Group 0* interrupts are expected to target EL3, both
+ by `platform abstraction layer`__ and `Interrupt Management Framework`__.
+ This allows GICv2 platforms to enable features requiring EL3 interrupt type.
+ This also means that all GICv2 Group 0 interrupts are delivered to EL3, and
+ the Secure Payload interrupts needs to be synchronously handed over to Secure
+ EL1 for handling. The default value of this option is ``0``, which means the
+ Group 0 interrupts are assumed to be handled by Secure EL1.
+
+ .. __: `platform-interrupt-controller-API.rst`
+ .. __: `interrupt-framework-design.rst`
+
- ``HANDLE_EA_EL3_FIRST``: When defined External Aborts and SError Interrupts
will be always trapped in EL3 i.e. in BL31 at runtime.
diff --git a/drivers/arm/gic/common/gic_common.c b/drivers/arm/gic/common/gic_common.c
index 6535813..d523772 100644
--- a/drivers/arm/gic/common/gic_common.c
+++ b/drivers/arm/gic/common/gic_common.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
*/
@@ -273,6 +273,14 @@
gicd_write_icpendr(base, id, (1 << bit_num));
}
+unsigned int gicd_get_isactiver(uintptr_t base, unsigned int id)
+{
+ unsigned int bit_num = id & ((1 << ISACTIVER_SHIFT) - 1);
+ unsigned int reg_val = gicd_read_isactiver(base, id);
+
+ return (reg_val >> bit_num) & 0x1;
+}
+
void gicd_set_isactiver(uintptr_t base, unsigned int id)
{
unsigned bit_num = id & ((1 << ISACTIVER_SHIFT) - 1);
@@ -291,3 +299,15 @@
{
mmio_write_8(base + GICD_IPRIORITYR + id, pri & GIC_PRI_MASK);
}
+
+void gicd_set_icfgr(uintptr_t base, unsigned int id, unsigned int cfg)
+{
+ unsigned bit_num = id & ((1 << ICFGR_SHIFT) - 1);
+ uint32_t reg_val = gicd_read_icfgr(base, id);
+
+ /* Clear the field, and insert required configuration */
+ reg_val &= ~(GIC_CFG_MASK << bit_num);
+ reg_val |= ((cfg & GIC_CFG_MASK) << bit_num);
+
+ gicd_write_icfgr(base, id, reg_val);
+}
diff --git a/drivers/arm/gic/common/gic_common_private.h b/drivers/arm/gic/common/gic_common_private.h
index 2077cc4..2021f9a 100644
--- a/drivers/arm/gic/common/gic_common_private.h
+++ b/drivers/arm/gic/common/gic_common_private.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2016-2017, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
@@ -73,8 +73,10 @@
void gicd_set_icenabler(uintptr_t base, unsigned int id);
void gicd_set_ispendr(uintptr_t base, unsigned int id);
void gicd_set_icpendr(uintptr_t base, unsigned int id);
+unsigned int gicd_get_isactiver(uintptr_t base, unsigned int id);
void gicd_set_isactiver(uintptr_t base, unsigned int id);
void gicd_set_icactiver(uintptr_t base, unsigned int id);
void gicd_set_ipriorityr(uintptr_t base, unsigned int id, unsigned int pri);
+void gicd_set_icfgr(uintptr_t base, unsigned int id, unsigned int cfg);
#endif /* GIC_COMMON_PRIVATE_H_ */
diff --git a/drivers/arm/gic/v2/gicv2_helpers.c b/drivers/arm/gic/v2/gicv2_helpers.c
index 7cdbc27..0df50fb 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
*/
@@ -9,6 +9,7 @@
#include <assert.h>
#include <debug.h>
#include <gic_common.h>
+#include <interrupt_props.h>
#include "../common/gic_common_private.h"
#include "gicv2_private.h"
@@ -72,15 +73,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
******************************************************************************/
@@ -121,6 +113,7 @@
gicd_write_icfgr(gicd_base, index, 0);
}
+#if !ERROR_DEPRECATED
/*******************************************************************************
* Helper function to configure secure G0 SPIs.
******************************************************************************/
@@ -154,8 +147,50 @@
}
}
+#endif
/*******************************************************************************
+ * Helper function to configure properties of secure G0 SPIs.
+ ******************************************************************************/
+void gicv2_secure_spis_configure_props(uintptr_t gicd_base,
+ const interrupt_prop_t *interrupt_props,
+ unsigned int interrupt_props_num)
+{
+ unsigned int i;
+ const interrupt_prop_t *prop_desc;
+
+ /* Make sure there's a valid property array */
+ assert(interrupt_props_num != 0 ? (uintptr_t) interrupt_props : 1);
+
+ for (i = 0; i < interrupt_props_num; i++) {
+ prop_desc = &interrupt_props[i];
+
+ if (prop_desc->intr_num < MIN_SPI_ID)
+ continue;
+
+ /* Configure this interrupt as a secure interrupt */
+ assert(prop_desc->intr_grp == GICV2_INTR_GROUP0);
+ gicd_clr_igroupr(gicd_base, prop_desc->intr_num);
+
+ /* Set the priority of this interrupt */
+ gicd_set_ipriorityr(gicd_base, prop_desc->intr_num,
+ prop_desc->intr_pri);
+
+ /* Target the secure interrupts to primary CPU */
+ gicd_set_itargetsr(gicd_base, prop_desc->intr_num,
+ gicv2_get_cpuif_id(gicd_base));
+
+ /* Set interrupt configuration */
+ gicd_set_icfgr(gicd_base, prop_desc->intr_num,
+ prop_desc->intr_cfg);
+
+ /* Enable this interrupt */
+ gicd_set_isenabler(gicd_base, prop_desc->intr_num);
+ }
+}
+
+#if !ERROR_DEPRECATED
+/*******************************************************************************
* Helper function to configure secure G0 SGIs and PPIs.
******************************************************************************/
void gicv2_secure_ppi_sgi_setup(uintptr_t gicd_base,
@@ -202,3 +237,66 @@
/* Enable the Group 0 SGIs and PPIs */
gicd_write_isenabler(gicd_base, 0, sec_ppi_sgi_mask);
}
+#endif
+
+/*******************************************************************************
+ * Helper function to configure properties of secure G0 SGIs and PPIs.
+ ******************************************************************************/
+void gicv2_secure_ppi_sgi_setup_props(uintptr_t gicd_base,
+ const interrupt_prop_t *interrupt_props,
+ unsigned int interrupt_props_num)
+{
+ unsigned int i;
+ uint32_t sec_ppi_sgi_mask = 0;
+ const interrupt_prop_t *prop_desc;
+
+ /* Make sure there's a valid property array */
+ assert(interrupt_props_num != 0 ? (uintptr_t) interrupt_props : 1);
+
+ /*
+ * Disable all SGIs (imp. def.)/PPIs before configuring them. This is a
+ * more scalable approach as it avoids clearing the enable bits in the
+ * GICD_CTLR.
+ */
+ gicd_write_icenabler(gicd_base, 0, ~0);
+
+ /* Setup the default PPI/SGI priorities doing four at a time */
+ for (i = 0; i < MIN_SPI_ID; i += 4)
+ gicd_write_ipriorityr(gicd_base, i, GICD_IPRIORITYR_DEF_VAL);
+
+ for (i = 0; i < interrupt_props_num; i++) {
+ prop_desc = &interrupt_props[i];
+
+ if (prop_desc->intr_num >= MIN_SPI_ID)
+ continue;
+
+ /* Configure this interrupt as a secure interrupt */
+ assert(prop_desc->intr_grp == GICV2_INTR_GROUP0);
+
+ /*
+ * Set interrupt configuration for PPIs. Configuration for SGIs
+ * are ignored.
+ */
+ if ((prop_desc->intr_num >= MIN_PPI_ID) &&
+ (prop_desc->intr_num < MIN_SPI_ID)) {
+ gicd_set_icfgr(gicd_base, prop_desc->intr_num,
+ prop_desc->intr_cfg);
+ }
+
+ /* We have an SGI or a PPI. They are Group0 at reset */
+ sec_ppi_sgi_mask |= (1u << prop_desc->intr_num);
+
+ /* Set the priority of this interrupt */
+ gicd_set_ipriorityr(gicd_base, prop_desc->intr_num,
+ prop_desc->intr_pri);
+ }
+
+ /*
+ * Invert the bitmask to create a mask for non-secure PPIs and SGIs.
+ * Program the GICD_IGROUPR0 with this bit mask.
+ */
+ gicd_write_igroupr(gicd_base, 0, ~sec_ppi_sgi_mask);
+
+ /* Enable the Group 0 SGIs and PPIs */
+ gicd_write_isenabler(gicd_base, 0, sec_ppi_sgi_mask);
+}
diff --git a/drivers/arm/gic/v2/gicv2_main.c b/drivers/arm/gic/v2/gicv2_main.c
index deac927..25296a6 100644
--- a/drivers/arm/gic/v2/gicv2_main.c
+++ b/drivers/arm/gic/v2/gicv2_main.c
@@ -10,11 +10,20 @@
#include <debug.h>
#include <gic_common.h>
#include <gicv2.h>
+#include <interrupt_props.h>
+#include <spinlock.h>
#include "../common/gic_common_private.h"
#include "gicv2_private.h"
static const gicv2_driver_data_t *driver_data;
+/*
+ * Spinlock to guard registers needing read-modify-write. APIs protected by this
+ * spinlock are used either at boot time (when only a single CPU is active), or
+ * when the system is fully coherent.
+ */
+spinlock_t gic_lock;
+
/*******************************************************************************
* Enable secure interrupts and use FIQs to route them. Disable legacy bypass
* and set the priority mask register to allow all interrupts to trickle in.
@@ -65,11 +74,21 @@
{
assert(driver_data);
assert(driver_data->gicd_base);
- assert(driver_data->g0_interrupt_array);
- gicv2_secure_ppi_sgi_setup(driver_data->gicd_base,
- driver_data->g0_interrupt_num,
- driver_data->g0_interrupt_array);
+#if !ERROR_DEPRECATED
+ if (driver_data->interrupt_props != NULL) {
+#endif
+ gicv2_secure_ppi_sgi_setup_props(driver_data->gicd_base,
+ driver_data->interrupt_props,
+ driver_data->interrupt_props_num);
+#if !ERROR_DEPRECATED
+ } else {
+ assert(driver_data->g0_interrupt_array);
+ gicv2_secure_ppi_sgi_setup(driver_data->gicd_base,
+ driver_data->g0_interrupt_num,
+ driver_data->g0_interrupt_array);
+ }
+#endif
}
/*******************************************************************************
@@ -83,7 +102,6 @@
assert(driver_data);
assert(driver_data->gicd_base);
- assert(driver_data->g0_interrupt_array);
/* Disable the distributor before going further */
ctlr = gicd_read_ctlr(driver_data->gicd_base);
@@ -93,10 +111,22 @@
/* Set the default attribute of all SPIs */
gicv2_spis_configure_defaults(driver_data->gicd_base);
- /* Configure the G0 SPIs */
- gicv2_secure_spis_configure(driver_data->gicd_base,
- driver_data->g0_interrupt_num,
- driver_data->g0_interrupt_array);
+#if !ERROR_DEPRECATED
+ if (driver_data->interrupt_props != NULL) {
+#endif
+ gicv2_secure_spis_configure_props(driver_data->gicd_base,
+ driver_data->interrupt_props,
+ driver_data->interrupt_props_num);
+#if !ERROR_DEPRECATED
+ } else {
+ assert(driver_data->g0_interrupt_array);
+
+ /* Configure the G0 SPIs */
+ gicv2_secure_spis_configure(driver_data->gicd_base,
+ driver_data->g0_interrupt_num,
+ driver_data->g0_interrupt_array);
+ }
+#endif
/* Re-enable the secure SPIs now that they have been configured */
gicd_write_ctlr(driver_data->gicd_base, ctlr | CTLR_ENABLE_G0_BIT);
@@ -112,19 +142,26 @@
assert(plat_driver_data->gicd_base);
assert(plat_driver_data->gicc_base);
- /*
- * The platform should provide a list of atleast one type of
- * interrupts
- */
- assert(plat_driver_data->g0_interrupt_array);
+#if !ERROR_DEPRECATED
+ if (plat_driver_data->interrupt_props == NULL) {
+ /* Interrupt properties array size must be 0 */
+ assert(plat_driver_data->interrupt_props_num == 0);
- /*
- * If there are no interrupts of a particular type, then the number of
- * interrupts of that type should be 0 and vice-versa.
- */
- assert(plat_driver_data->g0_interrupt_array ?
- plat_driver_data->g0_interrupt_num :
- plat_driver_data->g0_interrupt_num == 0);
+ /* The platform should provide a list of secure interrupts */
+ assert(plat_driver_data->g0_interrupt_array);
+
+ /*
+ * If there are no interrupts of a particular type, then the
+ * number of interrupts of that type should be 0 and vice-versa.
+ */
+ assert(plat_driver_data->g0_interrupt_array ?
+ plat_driver_data->g0_interrupt_num :
+ plat_driver_data->g0_interrupt_num == 0);
+ }
+#else
+ assert(plat_driver_data->interrupt_props != NULL);
+ assert(plat_driver_data->interrupt_props_num > 0);
+#endif
/* Ensure that this is a GICv2 system */
gic_version = gicd_read_pidr2(plat_driver_data->gicd_base);
@@ -240,3 +277,255 @@
return gicd_get_igroupr(driver_data->gicd_base, id);
}
+
+/*******************************************************************************
+ * This function returns the priority of the interrupt the processor is
+ * currently servicing.
+ ******************************************************************************/
+unsigned int gicv2_get_running_priority(void)
+{
+ assert(driver_data);
+ assert(driver_data->gicc_base);
+
+ return gicc_read_rpr(driver_data->gicc_base);
+}
+
+/*******************************************************************************
+ * This function sets the GICv2 target mask pattern for the current PE. The PE
+ * target mask is used to translate linear PE index (returned by platform core
+ * position) to a bit mask used when targeting interrupts to a PE, viz. when
+ * raising SGIs and routing SPIs.
+ ******************************************************************************/
+void gicv2_set_pe_target_mask(unsigned int proc_num)
+{
+ assert(driver_data);
+ assert(driver_data->gicd_base);
+ assert(driver_data->target_masks);
+ assert(proc_num < GICV2_MAX_TARGET_PE);
+ assert(proc_num < driver_data->target_masks_num);
+
+ /* Return if the target mask is already populated */
+ if (driver_data->target_masks[proc_num])
+ return;
+
+ /* Read target register corresponding to this CPU */
+ driver_data->target_masks[proc_num] =
+ gicv2_get_cpuif_id(driver_data->gicd_base);
+}
+
+/*******************************************************************************
+ * This function returns the active status of the interrupt (either because the
+ * state is active, or active and pending).
+ ******************************************************************************/
+unsigned int gicv2_get_interrupt_active(unsigned int id)
+{
+ assert(driver_data);
+ assert(driver_data->gicd_base);
+ assert(id <= MAX_SPI_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();
+}
+
+/*******************************************************************************
+ * This function sets the interrupt priority as supplied for the given interrupt
+ * id.
+ ******************************************************************************/
+void gicv2_set_interrupt_priority(unsigned int id, unsigned int priority)
+{
+ assert(driver_data);
+ assert(driver_data->gicd_base);
+ assert(id <= MAX_SPI_ID);
+
+ gicd_set_ipriorityr(driver_data->gicd_base, id, priority);
+}
+
+/*******************************************************************************
+ * This function assigns group for the interrupt identified by id. The group can
+ * be any of GICV2_INTR_GROUP*
+ ******************************************************************************/
+void gicv2_set_interrupt_type(unsigned int id, unsigned int type)
+{
+ assert(driver_data);
+ assert(driver_data->gicd_base);
+ assert(id <= MAX_SPI_ID);
+
+ /* Serialize read-modify-write to Distributor registers */
+ spin_lock(&gic_lock);
+ switch (type) {
+ case GICV2_INTR_GROUP1:
+ gicd_set_igroupr(driver_data->gicd_base, id);
+ break;
+ case GICV2_INTR_GROUP0:
+ gicd_clr_igroupr(driver_data->gicd_base, id);
+ break;
+ default:
+ assert(0);
+ }
+ spin_unlock(&gic_lock);
+}
+
+/*******************************************************************************
+ * This function raises the specified SGI to requested targets.
+ *
+ * The proc_num parameter must be the linear index of the target PE in the
+ * system.
+ ******************************************************************************/
+void gicv2_raise_sgi(int sgi_num, int proc_num)
+{
+ unsigned int sgir_val, target;
+
+ assert(driver_data);
+ assert(proc_num < GICV2_MAX_TARGET_PE);
+ assert(driver_data->gicd_base);
+
+ /*
+ * Target masks array must have been supplied, and the core position
+ * should be valid.
+ */
+ assert(driver_data->target_masks);
+ assert(proc_num < driver_data->target_masks_num);
+
+ /* Don't raise SGI if the mask hasn't been populated */
+ target = driver_data->target_masks[proc_num];
+ assert(target != 0);
+
+ sgir_val = GICV2_SGIR_VALUE(SGIR_TGT_SPECIFIC, target, sgi_num);
+
+ /*
+ * Ensure that any shared variable updates depending on out of band
+ * interrupt trigger are observed before raising SGI.
+ */
+ 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);
+}
+
+/*******************************************************************************
+ * This function clears the pending status of an interrupt identified by id.
+ ******************************************************************************/
+void gicv2_clear_interrupt_pending(unsigned int id)
+{
+ assert(driver_data);
+ assert(driver_data->gicd_base);
+
+ /* SGIs can't be cleared pending */
+ assert(id >= MIN_PPI_ID);
+
+ /*
+ * Clear pending interrupt, and ensure that any shared variable updates
+ * depending on out of band interrupt trigger are observed afterwards.
+ */
+ gicd_set_icpendr(driver_data->gicd_base, id);
+ dsbishst();
+}
+
+/*******************************************************************************
+ * This function sets the pending status of an interrupt identified by id.
+ ******************************************************************************/
+void gicv2_set_interrupt_pending(unsigned int id)
+{
+ assert(driver_data);
+ assert(driver_data->gicd_base);
+
+ /* SGIs can't be cleared pending */
+ assert(id >= MIN_PPI_ID);
+
+ /*
+ * Ensure that any shared variable updates depending on out of band
+ * interrupt trigger are observed before setting interrupt pending.
+ */
+ dsbishst();
+ gicd_set_ispendr(driver_data->gicd_base, id);
+}
+
+/*******************************************************************************
+ * This function sets the PMR register with the supplied value. Returns the
+ * original PMR.
+ ******************************************************************************/
+unsigned int gicv2_set_pmr(unsigned int mask)
+{
+ unsigned int old_mask;
+
+ assert(driver_data);
+ assert(driver_data->gicc_base);
+
+ old_mask = gicc_read_pmr(driver_data->gicc_base);
+
+ /*
+ * Order memory updates w.r.t. PMR write, and ensure they're visible
+ * before potential out of band interrupt trigger because of PMR update.
+ */
+ dmbishst();
+ gicc_write_pmr(driver_data->gicc_base, mask);
+ dsbishst();
+
+ return old_mask;
+}
diff --git a/drivers/arm/gic/v2/gicv2_private.h b/drivers/arm/gic/v2/gicv2_private.h
index 91ab43a..25600de 100644
--- a/drivers/arm/gic/v2/gicv2_private.h
+++ b/drivers/arm/gic/v2/gicv2_private.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2015-2017, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
@@ -15,12 +15,20 @@
* Private function prototypes
******************************************************************************/
void gicv2_spis_configure_defaults(uintptr_t gicd_base);
+#if !ERROR_DEPRECATED
void gicv2_secure_spis_configure(uintptr_t gicd_base,
unsigned int num_ints,
const unsigned int *sec_intr_list);
void gicv2_secure_ppi_sgi_setup(uintptr_t gicd_base,
unsigned int num_ints,
const unsigned int *sec_intr_list);
+#endif
+void gicv2_secure_spis_configure_props(uintptr_t gicd_base,
+ const interrupt_prop_t *interrupt_props,
+ unsigned int interrupt_props_num);
+void gicv2_secure_ppi_sgi_setup_props(uintptr_t gicd_base,
+ const interrupt_prop_t *interrupt_props,
+ unsigned int interrupt_props_num);
unsigned int gicv2_get_cpuif_id(uintptr_t base);
/*******************************************************************************
@@ -32,6 +40,25 @@
}
/*******************************************************************************
+ * 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);
+}
+
+/*******************************************************************************
* GIC CPU interface accessors for reading entire registers
******************************************************************************/
@@ -80,6 +107,11 @@
return mmio_read_32(base + GICC_IIDR);
}
+static inline unsigned int gicc_read_rpr(uintptr_t base)
+{
+ return mmio_read_32(base + GICC_RPR);
+}
+
/*******************************************************************************
* GIC CPU interface accessors for writing entire registers
******************************************************************************/
diff --git a/drivers/arm/gic/v3/gicv3_helpers.c b/drivers/arm/gic/v3/gicv3_helpers.c
index 73ad060..2522695 100644
--- a/drivers/arm/gic/v3/gicv3_helpers.c
+++ b/drivers/arm/gic/v3/gicv3_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
*/
@@ -9,6 +9,7 @@
#include <assert.h>
#include <debug.h>
#include <gic_common.h>
+#include <interrupt_props.h>
#include "../common/gic_common_private.h"
#include "gicv3_private.h"
@@ -172,6 +173,51 @@
}
/*
+ * 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.
+ */
+unsigned int gicr_get_isactiver0(uintptr_t base, unsigned int id)
+{
+ unsigned bit_num = id & ((1 << ISACTIVER_SHIFT) - 1);
+ unsigned int reg_val = gicr_read_isactiver0(base);
+
+ return (reg_val >> bit_num) & 0x1;
+}
+
+/*
+ * Accessor to clear the bit corresponding to interrupt ID in GIC Re-distributor
+ * ICPENDRR0.
+ */
+void gicr_set_icpendr0(uintptr_t base, unsigned int id)
+{
+ unsigned bit_num = id & ((1 << ICPENDR_SHIFT) - 1);
+
+ gicr_write_icpendr0(base, (1 << bit_num));
+}
+
+/*
+ * Accessor to set the bit corresponding to interrupt ID in GIC Re-distributor
+ * ISPENDR0.
+ */
+void gicr_set_ispendr0(uintptr_t base, unsigned int id)
+{
+ unsigned bit_num = id & ((1 << ISPENDR_SHIFT) - 1);
+
+ gicr_write_ispendr0(base, (1 << bit_num));
+}
+
+/*
* Accessor to set the byte corresponding to interrupt ID
* in GIC Re-distributor IPRIORITYR.
*/
@@ -180,6 +226,38 @@
mmio_write_8(base + GICR_IPRIORITYR + id, pri & GIC_PRI_MASK);
}
+/*
+ * Accessor to set the bit fields corresponding to interrupt ID
+ * in GIC Re-distributor ICFGR0.
+ */
+void gicr_set_icfgr0(uintptr_t base, unsigned int id, unsigned int cfg)
+{
+ unsigned bit_num = id & ((1 << ICFGR_SHIFT) - 1);
+ uint32_t reg_val = gicr_read_icfgr0(base);
+
+ /* Clear the field, and insert required configuration */
+ reg_val &= ~(GIC_CFG_MASK << bit_num);
+ reg_val |= ((cfg & GIC_CFG_MASK) << bit_num);
+
+ gicr_write_icfgr0(base, reg_val);
+}
+
+/*
+ * Accessor to set the bit fields corresponding to interrupt ID
+ * in GIC Re-distributor ICFGR1.
+ */
+void gicr_set_icfgr1(uintptr_t base, unsigned int id, unsigned int cfg)
+{
+ unsigned bit_num = id & ((1 << ICFGR_SHIFT) - 1);
+ uint32_t reg_val = gicr_read_icfgr1(base);
+
+ /* Clear the field, and insert required configuration */
+ reg_val &= ~(GIC_CFG_MASK << bit_num);
+ reg_val |= ((cfg & GIC_CFG_MASK) << bit_num);
+
+ gicr_write_icfgr1(base, reg_val);
+}
+
/******************************************************************************
* This function marks the core as awake in the re-distributor and
* ensures that the interface is active.
@@ -287,6 +365,7 @@
gicd_write_icfgr(gicd_base, index, 0);
}
+#if !ERROR_DEPRECATED
/*******************************************************************************
* Helper function to configure secure G0 and G1S SPIs.
******************************************************************************/
@@ -333,6 +412,63 @@
}
}
+#endif
+
+/*******************************************************************************
+ * Helper function to configure properties of secure SPIs
+ ******************************************************************************/
+unsigned int gicv3_secure_spis_configure_props(uintptr_t gicd_base,
+ const interrupt_prop_t *interrupt_props,
+ unsigned int interrupt_props_num)
+{
+ unsigned int i;
+ const interrupt_prop_t *current_prop;
+ unsigned long long gic_affinity_val;
+ unsigned int ctlr_enable = 0;
+
+ /* Make sure there's a valid property array */
+ assert(interrupt_props != NULL);
+ assert(interrupt_props_num > 0);
+
+ for (i = 0; i < interrupt_props_num; i++) {
+ current_prop = &interrupt_props[i];
+
+ if (current_prop->intr_num < MIN_SPI_ID)
+ continue;
+
+ /* Configure this interrupt as a secure interrupt */
+ gicd_clr_igroupr(gicd_base, current_prop->intr_num);
+
+ /* Configure this interrupt as G0 or a G1S interrupt */
+ assert((current_prop->intr_grp == INTR_GROUP0) ||
+ (current_prop->intr_grp == INTR_GROUP1S));
+ if (current_prop->intr_grp == INTR_GROUP1S) {
+ gicd_set_igrpmodr(gicd_base, current_prop->intr_num);
+ ctlr_enable |= CTLR_ENABLE_G1S_BIT;
+ } else {
+ gicd_clr_igrpmodr(gicd_base, current_prop->intr_num);
+ ctlr_enable |= CTLR_ENABLE_G0_BIT;
+ }
+
+ /* Set interrupt configuration */
+ gicd_set_icfgr(gicd_base, current_prop->intr_num,
+ current_prop->intr_cfg);
+
+ /* Set the priority of this interrupt */
+ gicd_set_ipriorityr(gicd_base, current_prop->intr_num,
+ current_prop->intr_pri);
+
+ /* Target SPIs to the primary CPU */
+ gic_affinity_val = gicd_irouter_val_from_mpidr(read_mpidr(), 0);
+ gicd_write_irouter(gicd_base, current_prop->intr_num,
+ gic_affinity_val);
+
+ /* Enable this interrupt */
+ gicd_set_isenabler(gicd_base, current_prop->intr_num);
+ }
+
+ return ctlr_enable;
+}
/*******************************************************************************
* Helper function to configure the default attributes of SPIs.
@@ -362,6 +498,7 @@
gicr_write_icfgr1(gicr_base, 0);
}
+#if !ERROR_DEPRECATED
/*******************************************************************************
* Helper function to configure secure G0 and G1S SPIs.
******************************************************************************/
@@ -399,3 +536,54 @@
}
}
}
+#endif
+
+/*******************************************************************************
+ * Helper function to configure properties of secure G0 and G1S PPIs and SGIs.
+ ******************************************************************************/
+void gicv3_secure_ppi_sgi_configure_props(uintptr_t gicr_base,
+ const interrupt_prop_t *interrupt_props,
+ unsigned int interrupt_props_num)
+{
+ unsigned int i;
+ const interrupt_prop_t *current_prop;
+
+ /* Make sure there's a valid property array */
+ assert(interrupt_props != NULL);
+ assert(interrupt_props_num > 0);
+
+ for (i = 0; i < interrupt_props_num; i++) {
+ current_prop = &interrupt_props[i];
+
+ if (current_prop->intr_num >= MIN_SPI_ID)
+ continue;
+
+ /* Configure this interrupt as a secure interrupt */
+ gicr_clr_igroupr0(gicr_base, current_prop->intr_num);
+
+ /* Configure this interrupt as G0 or a G1S interrupt */
+ assert((current_prop->intr_grp == INTR_GROUP0) ||
+ (current_prop->intr_grp == INTR_GROUP1S));
+ if (current_prop->intr_grp == INTR_GROUP1S)
+ gicr_set_igrpmodr0(gicr_base, current_prop->intr_num);
+ else
+ gicr_clr_igrpmodr0(gicr_base, current_prop->intr_num);
+
+ /* Set the priority of this interrupt */
+ gicr_set_ipriorityr(gicr_base, current_prop->intr_num,
+ current_prop->intr_pri);
+
+ /*
+ * Set interrupt configuration for PPIs. Configuration for SGIs
+ * are ignored.
+ */
+ if ((current_prop->intr_num >= MIN_PPI_ID) &&
+ (current_prop->intr_num < MIN_SPI_ID)) {
+ gicr_set_icfgr1(gicr_base, current_prop->intr_num,
+ current_prop->intr_cfg);
+ }
+
+ /* Enable this interrupt */
+ gicr_set_isenabler0(gicr_base, current_prop->intr_num);
+ }
+}
diff --git a/drivers/arm/gic/v3/gicv3_main.c b/drivers/arm/gic/v3/gicv3_main.c
index 1a018d8..8c4f508 100644
--- a/drivers/arm/gic/v3/gicv3_main.c
+++ b/drivers/arm/gic/v3/gicv3_main.c
@@ -9,12 +9,21 @@
#include <assert.h>
#include <debug.h>
#include <gicv3.h>
+#include <interrupt_props.h>
+#include <spinlock.h>
#include "gicv3_private.h"
const gicv3_driver_data_t *gicv3_driver_data;
static unsigned int gicv2_compat;
/*
+ * Spinlock to guard registers needing read-modify-write. APIs protected by this
+ * spinlock are used either at boot time (when only a single CPU is active), or
+ * when the system is fully coherent.
+ */
+spinlock_t gic_lock;
+
+/*
* Redistributor power operations are weakly bound so that they can be
* overridden
*/
@@ -58,23 +67,33 @@
assert(IS_IN_EL3());
- /*
- * The platform should provide a list of at least one type of
- * interrupts
- */
- assert(plat_driver_data->g0_interrupt_array ||
- plat_driver_data->g1s_interrupt_array);
+#if !ERROR_DEPRECATED
+ if (plat_driver_data->interrupt_props == NULL) {
+ /* Interrupt properties array size must be 0 */
+ assert(plat_driver_data->interrupt_props_num == 0);
- /*
- * If there are no interrupts of a particular type, then the number of
- * interrupts of that type should be 0 and vice-versa.
- */
- assert(plat_driver_data->g0_interrupt_array ?
- plat_driver_data->g0_interrupt_num :
- plat_driver_data->g0_interrupt_num == 0);
- assert(plat_driver_data->g1s_interrupt_array ?
- plat_driver_data->g1s_interrupt_num :
- plat_driver_data->g1s_interrupt_num == 0);
+ /*
+ * The platform should provide a list of at least one type of
+ * interrupt.
+ */
+ assert(plat_driver_data->g0_interrupt_array ||
+ plat_driver_data->g1s_interrupt_array);
+
+ /*
+ * If there are no interrupts of a particular type, then the
+ * number of interrupts of that type should be 0 and vice-versa.
+ */
+ assert(plat_driver_data->g0_interrupt_array ?
+ plat_driver_data->g0_interrupt_num :
+ plat_driver_data->g0_interrupt_num == 0);
+ assert(plat_driver_data->g1s_interrupt_array ?
+ plat_driver_data->g1s_interrupt_num :
+ plat_driver_data->g1s_interrupt_num == 0);
+ }
+#else
+ assert(plat_driver_data->interrupt_props != NULL);
+ assert(plat_driver_data->interrupt_props_num > 0);
+#endif
/* Check for system register support */
#ifdef AARCH32
@@ -140,8 +159,6 @@
assert(gicv3_driver_data);
assert(gicv3_driver_data->gicd_base);
- assert(gicv3_driver_data->g1s_interrupt_array ||
- gicv3_driver_data->g0_interrupt_array);
assert(IS_IN_EL3());
@@ -163,23 +180,37 @@
/* Set the default attribute of all SPIs */
gicv3_spis_configure_defaults(gicv3_driver_data->gicd_base);
- /* Configure the G1S SPIs */
- if (gicv3_driver_data->g1s_interrupt_array) {
- gicv3_secure_spis_configure(gicv3_driver_data->gicd_base,
+#if !ERROR_DEPRECATED
+ if (gicv3_driver_data->interrupt_props != NULL) {
+#endif
+ bitmap = gicv3_secure_spis_configure_props(
+ gicv3_driver_data->gicd_base,
+ gicv3_driver_data->interrupt_props,
+ gicv3_driver_data->interrupt_props_num);
+#if !ERROR_DEPRECATED
+ } else {
+ assert(gicv3_driver_data->g1s_interrupt_array ||
+ gicv3_driver_data->g0_interrupt_array);
+
+ /* Configure the G1S SPIs */
+ if (gicv3_driver_data->g1s_interrupt_array) {
+ gicv3_secure_spis_configure(gicv3_driver_data->gicd_base,
gicv3_driver_data->g1s_interrupt_num,
gicv3_driver_data->g1s_interrupt_array,
INTR_GROUP1S);
- bitmap |= CTLR_ENABLE_G1S_BIT;
- }
+ bitmap |= CTLR_ENABLE_G1S_BIT;
+ }
- /* Configure the G0 SPIs */
- if (gicv3_driver_data->g0_interrupt_array) {
- gicv3_secure_spis_configure(gicv3_driver_data->gicd_base,
+ /* Configure the G0 SPIs */
+ if (gicv3_driver_data->g0_interrupt_array) {
+ gicv3_secure_spis_configure(gicv3_driver_data->gicd_base,
gicv3_driver_data->g0_interrupt_num,
gicv3_driver_data->g0_interrupt_array,
INTR_GROUP0);
- bitmap |= CTLR_ENABLE_G0_BIT;
+ bitmap |= CTLR_ENABLE_G0_BIT;
+ }
}
+#endif
/* Enable the secure SPIs now that they have been configured */
gicd_set_ctlr(gicv3_driver_data->gicd_base, bitmap, RWP_TRUE);
@@ -199,8 +230,6 @@
assert(gicv3_driver_data->rdistif_base_addrs);
assert(gicv3_driver_data->gicd_base);
assert(gicd_read_ctlr(gicv3_driver_data->gicd_base) & CTLR_ARE_S_BIT);
- assert(gicv3_driver_data->g1s_interrupt_array ||
- gicv3_driver_data->g0_interrupt_array);
assert(IS_IN_EL3());
@@ -212,21 +241,34 @@
/* Set the default attribute of all SGIs and PPIs */
gicv3_ppi_sgi_configure_defaults(gicr_base);
- /* Configure the G1S SGIs/PPIs */
- if (gicv3_driver_data->g1s_interrupt_array) {
- gicv3_secure_ppi_sgi_configure(gicr_base,
+#if !ERROR_DEPRECATED
+ if (gicv3_driver_data->interrupt_props != NULL) {
+#endif
+ gicv3_secure_ppi_sgi_configure_props(gicr_base,
+ gicv3_driver_data->interrupt_props,
+ gicv3_driver_data->interrupt_props_num);
+#if !ERROR_DEPRECATED
+ } else {
+ assert(gicv3_driver_data->g1s_interrupt_array ||
+ gicv3_driver_data->g0_interrupt_array);
+
+ /* Configure the G1S SGIs/PPIs */
+ if (gicv3_driver_data->g1s_interrupt_array) {
+ gicv3_secure_ppi_sgi_configure(gicr_base,
gicv3_driver_data->g1s_interrupt_num,
gicv3_driver_data->g1s_interrupt_array,
INTR_GROUP1S);
- }
+ }
- /* Configure the G0 SGIs/PPIs */
- if (gicv3_driver_data->g0_interrupt_array) {
- gicv3_secure_ppi_sgi_configure(gicr_base,
+ /* Configure the G0 SGIs/PPIs */
+ if (gicv3_driver_data->g0_interrupt_array) {
+ gicv3_secure_ppi_sgi_configure(gicr_base,
gicv3_driver_data->g0_interrupt_num,
gicv3_driver_data->g0_interrupt_array,
INTR_GROUP0);
+ }
}
+#endif
}
/*******************************************************************************
@@ -769,3 +811,337 @@
gicd_wait_for_pending_write(gicd_base);
}
+
+/*******************************************************************************
+ * This function gets the priority of the interrupt the processor is currently
+ * servicing.
+ ******************************************************************************/
+unsigned int gicv3_get_running_priority(void)
+{
+ return read_icc_rpr_el1();
+}
+
+/*******************************************************************************
+ * This function checks if the interrupt identified by id is active (whether the
+ * state is either active, or active and pending). The proc_num is used if the
+ * interrupt is SGI or PPI and programs the corresponding Redistributor
+ * interface.
+ ******************************************************************************/
+unsigned int gicv3_get_interrupt_active(unsigned int id, unsigned int proc_num)
+{
+ unsigned int value;
+
+ 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);
+
+ if (id < MIN_SPI_ID) {
+ /* For SGIs and PPIs */
+ value = gicr_get_isactiver0(
+ gicv3_driver_data->rdistif_base_addrs[proc_num], id);
+ } else {
+ value = gicd_get_isactiver(gicv3_driver_data->gicd_base, id);
+ }
+
+ 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();
+}
+
+/*******************************************************************************
+ * This function sets the interrupt priority as supplied for the given interrupt
+ * id.
+ ******************************************************************************/
+void gicv3_set_interrupt_priority(unsigned int id, unsigned int proc_num,
+ unsigned int priority)
+{
+ uintptr_t gicr_base;
+
+ 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);
+
+ if (id < MIN_SPI_ID) {
+ gicr_base = gicv3_driver_data->rdistif_base_addrs[proc_num];
+ gicr_set_ipriorityr(gicr_base, id, priority);
+ } else {
+ gicd_set_ipriorityr(gicv3_driver_data->gicd_base, id, priority);
+ }
+}
+
+/*******************************************************************************
+ * This function assigns group for the interrupt identified by id. The proc_num
+ * is used if the interrupt is SGI or PPI, and programs the corresponding
+ * Redistributor interface. The group can be any of GICV3_INTR_GROUP*
+ ******************************************************************************/
+void gicv3_set_interrupt_type(unsigned int id, unsigned int proc_num,
+ unsigned int type)
+{
+ unsigned int igroup = 0, grpmod = 0;
+ uintptr_t gicr_base;
+
+ 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);
+
+ switch (type) {
+ case INTR_GROUP1S:
+ igroup = 0;
+ grpmod = 1;
+ break;
+ case INTR_GROUP0:
+ igroup = 0;
+ grpmod = 0;
+ break;
+ case INTR_GROUP1NS:
+ igroup = 1;
+ grpmod = 0;
+ break;
+ default:
+ assert(0);
+ }
+
+ if (id < MIN_SPI_ID) {
+ gicr_base = gicv3_driver_data->rdistif_base_addrs[proc_num];
+ if (igroup)
+ gicr_set_igroupr0(gicr_base, id);
+ else
+ gicr_clr_igroupr0(gicr_base, id);
+
+ if (grpmod)
+ gicr_set_igrpmodr0(gicr_base, id);
+ else
+ gicr_clr_igrpmodr0(gicr_base, id);
+ } else {
+ /* Serialize read-modify-write to Distributor registers */
+ spin_lock(&gic_lock);
+ if (igroup)
+ gicd_set_igroupr(gicv3_driver_data->gicd_base, id);
+ else
+ gicd_clr_igroupr(gicv3_driver_data->gicd_base, id);
+
+ if (grpmod)
+ gicd_set_igrpmodr(gicv3_driver_data->gicd_base, id);
+ else
+ gicd_clr_igrpmodr(gicv3_driver_data->gicd_base, id);
+ spin_unlock(&gic_lock);
+ }
+}
+
+/*******************************************************************************
+ * This function raises the specified Secure Group 0 SGI.
+ *
+ * The target parameter must be a valid MPIDR in the system.
+ ******************************************************************************/
+void gicv3_raise_secure_g0_sgi(int sgi_num, u_register_t target)
+{
+ unsigned int tgt, aff3, aff2, aff1, aff0;
+ uint64_t sgi_val;
+
+ /* Verify interrupt number is in the SGI range */
+ assert((sgi_num >= MIN_SGI_ID) && (sgi_num < MIN_PPI_ID));
+
+ /* Extract affinity fields from target */
+ aff0 = MPIDR_AFFLVL0_VAL(target);
+ aff1 = MPIDR_AFFLVL1_VAL(target);
+ aff2 = MPIDR_AFFLVL2_VAL(target);
+ aff3 = MPIDR_AFFLVL3_VAL(target);
+
+ /*
+ * Make target list from affinity 0, and ensure GICv3 SGI can target
+ * this PE.
+ */
+ assert(aff0 < GICV3_MAX_SGI_TARGETS);
+ tgt = BIT(aff0);
+
+ /* Raise SGI to PE specified by its affinity */
+ sgi_val = GICV3_SGIR_VALUE(aff3, aff2, aff1, sgi_num, SGIR_IRM_TO_AFF,
+ tgt);
+
+ /*
+ * Ensure that any shared variable updates depending on out of band
+ * interrupt trigger are observed before raising SGI.
+ */
+ dsbishst();
+ 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();
+ }
+ }
+}
+
+/*******************************************************************************
+ * This function clears the pending status of an interrupt identified by id.
+ * The proc_num is used if the interrupt is SGI or PPI, and programs the
+ * corresponding Redistributor interface.
+ ******************************************************************************/
+void gicv3_clear_interrupt_pending(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);
+
+ /*
+ * Clear pending 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_icpendr0(gicv3_driver_data->rdistif_base_addrs[proc_num],
+ id);
+ } else {
+ gicd_set_icpendr(gicv3_driver_data->gicd_base, id);
+ }
+ dsbishst();
+}
+
+/*******************************************************************************
+ * This function sets the pending status of an interrupt identified by id.
+ * The proc_num is used if the interrupt is SGI or PPI and programs the
+ * corresponding Redistributor interface.
+ ******************************************************************************/
+void gicv3_set_interrupt_pending(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);
+
+ /*
+ * Ensure that any shared variable updates depending on out of band
+ * interrupt trigger are observed before setting interrupt pending.
+ */
+ dsbishst();
+ if (id < MIN_SPI_ID) {
+ /* For SGIs and PPIs */
+ gicr_set_ispendr0(gicv3_driver_data->rdistif_base_addrs[proc_num],
+ id);
+ } else {
+ gicd_set_ispendr(gicv3_driver_data->gicd_base, id);
+ }
+}
+
+/*******************************************************************************
+ * This function sets the PMR register with the supplied value. Returns the
+ * original PMR.
+ ******************************************************************************/
+unsigned int gicv3_set_pmr(unsigned int mask)
+{
+ unsigned int old_mask;
+
+ old_mask = read_icc_pmr_el1();
+
+ /*
+ * Order memory updates w.r.t. PMR write, and ensure they're visible
+ * before potential out of band interrupt trigger because of PMR update.
+ * PMR system register writes are self-synchronizing, so no ISB required
+ * thereafter.
+ */
+ dsbishst();
+ write_icc_pmr_el1(mask);
+
+ return old_mask;
+}
diff --git a/drivers/arm/gic/v3/gicv3_private.h b/drivers/arm/gic/v3/gicv3_private.h
index 59298ed..a5093d0 100644
--- a/drivers/arm/gic/v3/gicv3_private.h
+++ b/drivers/arm/gic/v3/gicv3_private.h
@@ -67,9 +67,13 @@
unsigned int gicd_get_igrpmodr(uintptr_t base, unsigned int id);
unsigned int gicr_get_igrpmodr0(uintptr_t base, unsigned int id);
unsigned int gicr_get_igroupr0(uintptr_t base, unsigned int id);
+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_ispendr0(uintptr_t base, unsigned int id);
+void gicr_set_icpendr0(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);
@@ -81,6 +85,7 @@
******************************************************************************/
void gicv3_spis_configure_defaults(uintptr_t gicd_base);
void gicv3_ppi_sgi_configure_defaults(uintptr_t gicr_base);
+#if !ERROR_DEPRECATED
void gicv3_secure_spis_configure(uintptr_t gicd_base,
unsigned int num_ints,
const unsigned int *sec_intr_list,
@@ -89,6 +94,13 @@
unsigned int num_ints,
const unsigned int *sec_intr_list,
unsigned int int_grp);
+#endif
+void gicv3_secure_ppi_sgi_configure_props(uintptr_t gicr_base,
+ const interrupt_prop_t *interrupt_props,
+ unsigned int interrupt_props_num);
+unsigned int gicv3_secure_spis_configure_props(uintptr_t gicd_base,
+ const interrupt_prop_t *interrupt_props,
+ unsigned int interrupt_props_num);
void gicv3_rdistif_base_addrs_probe(uintptr_t *rdistif_base_addrs,
unsigned int rdistif_num,
uintptr_t gicr_base,
@@ -219,6 +231,11 @@
return mmio_read_32(base + GICR_ISENABLER0);
}
+static inline void gicr_write_icpendr0(uintptr_t base, unsigned int val)
+{
+ mmio_write_32(base + GICR_ICPENDR0, val);
+}
+
static inline void gicr_write_isenabler0(uintptr_t base, unsigned int val)
{
mmio_write_32(base + GICR_ISENABLER0, val);
diff --git a/include/bl31/interrupt_mgmt.h b/include/bl31/interrupt_mgmt.h
index 9a6a7fa..cccad3a 100644
--- a/include/bl31/interrupt_mgmt.h
+++ b/include/bl31/interrupt_mgmt.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2014-2017, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
@@ -17,6 +17,11 @@
#define INTR_TYPE_NS U(2)
#define MAX_INTR_TYPES U(3)
#define INTR_TYPE_INVAL MAX_INTR_TYPES
+
+/* Interrupt routing modes */
+#define INTR_ROUTING_MODE_PE 0
+#define INTR_ROUTING_MODE_ANY 1
+
/*
* Constant passed to the interrupt handler in the 'id' field when the
* framework does not read the gic registers to determine the interrupt id.
@@ -93,6 +98,8 @@
#ifndef __ASSEMBLY__
+#include <stdint.h>
+
/* Prototype for defining a handler for an interrupt type */
typedef uint64_t (*interrupt_type_handler_t)(uint32_t id,
uint32_t flags,
diff --git a/include/common/interrupt_props.h b/include/common/interrupt_props.h
new file mode 100644
index 0000000..9786b40
--- /dev/null
+++ b/include/common/interrupt_props.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef __INTERRUPT_PROPS_H__
+#define __INTERRUPT_PROPS_H__
+
+#ifndef __ASSEMBLY__
+
+/* Create an interrupt property descriptor from various interrupt properties */
+#define INTR_PROP_DESC(num, pri, grp, cfg) \
+ { \
+ .intr_num = num, \
+ .intr_pri = pri, \
+ .intr_grp = grp, \
+ .intr_cfg = cfg, \
+ }
+
+typedef struct interrupt_prop {
+ unsigned int intr_num:10;
+ unsigned int intr_pri:8;
+ unsigned int intr_grp:2;
+ unsigned int intr_cfg:2;
+} interrupt_prop_t;
+
+#endif /* __ASSEMBLY__ */
+#endif /* __INTERRUPT_PROPS_H__ */
diff --git a/include/drivers/arm/gic_common.h b/include/drivers/arm/gic_common.h
index b9cae80..9e126a8 100644
--- a/include/drivers/arm/gic_common.h
+++ b/include/drivers/arm/gic_common.h
@@ -12,6 +12,7 @@
******************************************************************************/
/* Constants to categorise interrupts */
#define MIN_SGI_ID 0
+#define MIN_SEC_SGI_ID 8
#define MIN_PPI_ID 16
#define MIN_SPI_ID 32
#define MAX_SPI_ID 1019
@@ -22,9 +23,16 @@
/* Mask for the priority field common to all GIC interfaces */
#define GIC_PRI_MASK 0xff
+/* Mask for the configuration field common to all GIC interfaces */
+#define GIC_CFG_MASK 0x3
+
/* Constant to indicate a spurious interrupt in all GIC versions */
#define GIC_SPURIOUS_INTERRUPT 1023
+/* Interrupt configurations */
+#define GIC_INTR_CFG_LEVEL 0
+#define GIC_INTR_CFG_EDGE 1
+
/* Constants to categorise priorities */
#define GIC_HIGHEST_SEC_PRIORITY 0
#define GIC_LOWEST_SEC_PRIORITY 127
@@ -73,6 +81,7 @@
#define ISACTIVER_SHIFT 5
#define ICACTIVER_SHIFT ISACTIVER_SHIFT
#define IPRIORITYR_SHIFT 2
+#define ITARGETSR_SHIFT 2
#define ICFGR_SHIFT 4
#define NSACR_SHIFT 4
diff --git a/include/drivers/arm/gicv2.h b/include/drivers/arm/gicv2.h
index a788025..6e8322e 100644
--- a/include/drivers/arm/gicv2.h
+++ b/include/drivers/arm/gicv2.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2015-2017, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
@@ -10,9 +10,17 @@
/*******************************************************************************
* GICv2 miscellaneous definitions
******************************************************************************/
+
+/* Interrupt group definitions */
+#define GICV2_INTR_GROUP0 0
+#define GICV2_INTR_GROUP1 1
+
/* Interrupt IDs reported by the HPPIR and IAR registers */
#define PENDING_G1_INTID 1022
+/* GICv2 can only target up to 8 PEs */
+#define GICV2_MAX_TARGET_PE 8
+
/*******************************************************************************
* GICv2 specific Distributor interface register offsets and constants.
******************************************************************************/
@@ -28,6 +36,19 @@
#define CPENDSGIR_SHIFT 2
#define SPENDSGIR_SHIFT CPENDSGIR_SHIFT
+#define SGIR_TGTLSTFLT_SHIFT 24
+#define SGIR_TGTLSTFLT_MASK 0x3
+#define SGIR_TGTLST_SHIFT 16
+#define SGIR_TGTLST_MASK 0xff
+#define SGIR_INTID_MASK 0xf
+
+#define SGIR_TGT_SPECIFIC 0
+
+#define GICV2_SGIR_VALUE(tgt_lst_flt, tgt, intid) \
+ ((((tgt_lst_flt) & SGIR_TGTLSTFLT_MASK) << SGIR_TGTLSTFLT_SHIFT) | \
+ (((tgt) & SGIR_TGTLST_MASK) << SGIR_TGTLST_SHIFT) | \
+ ((intid) & SGIR_INTID_MASK))
+
/*******************************************************************************
* GICv2 specific CPU interface register offsets and constants.
******************************************************************************/
@@ -95,6 +116,7 @@
#ifndef __ASSEMBLY__
+#include <interrupt_props.h>
#include <stdint.h>
/*******************************************************************************
@@ -103,23 +125,43 @@
* in order to initialize the GICv2 driver. The attributes are described
* below.
*
- * 1. The 'gicd_base' field contains the base address of the Distributor
- * interface programmer's view.
+ * The 'gicd_base' field contains the base address of the Distributor interface
+ * programmer's view.
*
- * 2. The 'gicc_base' field contains the base address of the CPU Interface
- * programmer's view.
+ * The 'gicc_base' field contains the base address of the CPU Interface
+ * programmer's view.
*
- * 3. The 'g0_interrupt_array' field is a pointer to an array in which each
- * entry corresponds to an ID of a Group 0 interrupt.
+ * The 'g0_interrupt_array' field is a pointer to an array in which each entry
+ * corresponds to an ID of a Group 0 interrupt. This field is ignored when
+ * 'interrupt_props' field is used. This field is deprecated.
*
- * 4. The 'g0_interrupt_num' field contains the number of entries in the
- * 'g0_interrupt_array'.
+ * The 'g0_interrupt_num' field contains the number of entries in the
+ * 'g0_interrupt_array'. This field is ignored when 'interrupt_props' field is
+ * used. This field is deprecated.
+ *
+ * The 'target_masks' is a pointer to an array containing 'target_masks_num'
+ * elements. The GIC driver will populate the array with per-PE target mask to
+ * use to when targeting interrupts.
+ *
+ * The 'interrupt_props' field is a pointer to an array that enumerates secure
+ * interrupts and their properties. If this field is not NULL, both
+ * 'g0_interrupt_array' and 'g1s_interrupt_array' fields are ignored.
+ *
+ * The 'interrupt_props_num' field contains the number of entries in the
+ * 'interrupt_props' array. If this field is non-zero, 'g0_interrupt_num' is
+ * ignored.
******************************************************************************/
typedef struct gicv2_driver_data {
uintptr_t gicd_base;
uintptr_t gicc_base;
+#if !ERROR_DEPRECATED
unsigned int g0_interrupt_num;
const unsigned int *g0_interrupt_array;
+#endif
+ unsigned int *target_masks;
+ unsigned int target_masks_num;
+ const interrupt_prop_t *interrupt_props;
+ unsigned int interrupt_props_num;
} gicv2_driver_data_t;
/*******************************************************************************
@@ -136,6 +178,18 @@
unsigned int gicv2_acknowledge_interrupt(void);
void gicv2_end_of_interrupt(unsigned int id);
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);
+void gicv2_set_interrupt_priority(unsigned int id, unsigned int priority);
+void gicv2_set_interrupt_type(unsigned int id, unsigned int type);
+void gicv2_raise_sgi(int sgi_num, int proc_num);
+void gicv2_set_spi_routing(unsigned int id, int proc_num);
+void gicv2_set_interrupt_pending(unsigned int id);
+void gicv2_clear_interrupt_pending(unsigned int id);
+unsigned int gicv2_set_pmr(unsigned int mask);
#endif /* __ASSEMBLY__ */
#endif /* __GICV2_H__ */
diff --git a/include/drivers/arm/gicv3.h b/include/drivers/arm/gicv3.h
index c52fe48..b2e4d4c 100644
--- a/include/drivers/arm/gicv3.h
+++ b/include/drivers/arm/gicv3.h
@@ -7,8 +7,6 @@
#ifndef __GICV3_H__
#define __GICV3_H__
-#include "utils_def.h"
-
/*******************************************************************************
* GICv3 miscellaneous definitions
******************************************************************************/
@@ -24,6 +22,9 @@
/* Constant to categorize LPI interrupt */
#define MIN_LPI_ID 8192
+/* GICv3 can only target up to 16 PEs with SGI */
+#define GICV3_MAX_SGI_TARGETS 16
+
/*******************************************************************************
* GICv3 specific Distributor interface register offsets and constants.
******************************************************************************/
@@ -72,6 +73,9 @@
#define IROUTER_IRM_SHIFT 31
#define IROUTER_IRM_MASK 0x1
+#define GICV3_IRM_PE 0
+#define GICV3_IRM_ANY 1
+
#define NUM_OF_DIST_REGS 30
/*******************************************************************************
@@ -165,6 +169,27 @@
#define IAR1_EL1_INTID_SHIFT 0
#define IAR1_EL1_INTID_MASK 0xffffff
+/* ICC SGI macros */
+#define SGIR_TGT_MASK 0xffff
+#define SGIR_AFF1_SHIFT 16
+#define SGIR_INTID_SHIFT 24
+#define SGIR_INTID_MASK 0xf
+#define SGIR_AFF2_SHIFT 32
+#define SGIR_IRM_SHIFT 40
+#define SGIR_IRM_MASK 0x1
+#define SGIR_AFF3_SHIFT 48
+#define SGIR_AFF_MASK 0xf
+
+#define SGIR_IRM_TO_AFF 0
+
+#define GICV3_SGIR_VALUE(aff3, aff2, aff1, intid, irm, tgt) \
+ ((((uint64_t) (aff3) & SGIR_AFF_MASK) << SGIR_AFF3_SHIFT) | \
+ (((uint64_t) (irm) & SGIR_IRM_MASK) << SGIR_IRM_SHIFT) | \
+ (((uint64_t) (aff2) & SGIR_AFF_MASK) << SGIR_AFF2_SHIFT) | \
+ (((intid) & SGIR_INTID_MASK) << SGIR_INTID_SHIFT) | \
+ (((aff1) & SGIR_AFF_MASK) << SGIR_AFF1_SHIFT) | \
+ ((tgt) & SGIR_TGT_MASK))
+
/*****************************************************************************
* GICv3 ITS registers and constants
*****************************************************************************/
@@ -185,6 +210,7 @@
#ifndef __ASSEMBLY__
#include <gic_common.h>
+#include <interrupt_props.h>
#include <stdint.h>
#include <types.h>
#include <utils_def.h>
@@ -224,53 +250,70 @@
* GICv3 IP. It is used by the platform port to specify these attributes in order
* to initialise the GICV3 driver. The attributes are described below.
*
- * 1. The 'gicd_base' field contains the base address of the Distributor
- * interface programmer's view.
+ * The 'gicd_base' field contains the base address of the Distributor interface
+ * programmer's view.
*
- * 2. The 'gicr_base' field contains the base address of the Re-distributor
- * interface programmer's view.
+ * The 'gicr_base' field contains the base address of the Re-distributor
+ * interface programmer's view.
*
- * 3. The 'g0_interrupt_array' field is a ponter to an array in which each
- * entry corresponds to an ID of a Group 0 interrupt.
+ * The 'g0_interrupt_array' field is a pointer to an array in which each entry
+ * corresponds to an ID of a Group 0 interrupt. This field is ignored when
+ * 'interrupt_props' field is used. This field is deprecated.
*
- * 4. The 'g0_interrupt_num' field contains the number of entries in the
- * 'g0_interrupt_array'.
+ * The 'g0_interrupt_num' field contains the number of entries in the
+ * 'g0_interrupt_array'. This field is ignored when 'interrupt_props' field is
+ * used. This field is deprecated.
*
- * 5. The 'g1s_interrupt_array' field is a ponter to an array in which each
- * entry corresponds to an ID of a Group 1 interrupt.
+ * The 'g1s_interrupt_array' field is a pointer to an array in which each entry
+ * corresponds to an ID of a Group 1 interrupt. This field is ignored when
+ * 'interrupt_props' field is used. This field is deprecated.
*
- * 6. The 'g1s_interrupt_num' field contains the number of entries in the
- * 'g1s_interrupt_array'.
+ * The 'g1s_interrupt_num' field contains the number of entries in the
+ * 'g1s_interrupt_array'. This field must be 0 if 'interrupt_props' field is
+ * used. This field is ignored when 'interrupt_props' field is used. This field
+ * is deprecated.
*
- * 7. The 'rdistif_num' field contains the number of Redistributor interfaces
- * the GIC implements. This is equal to the number of CPUs or CPU interfaces
- * instantiated in the GIC.
+ * The 'interrupt_props' field is a pointer to an array that enumerates secure
+ * interrupts and their properties. If this field is not NULL, both
+ * 'g0_interrupt_array' and 'g1s_interrupt_array' fields are ignored.
*
- * 8. The 'rdistif_base_addrs' field is a pointer to an array that has an entry
- * for storing the base address of the Redistributor interface frame of each
- * CPU in the system. The size of the array = 'rdistif_num'. The base
- * addresses are detected during driver initialisation.
+ * The 'interrupt_props_num' field contains the number of entries in the
+ * 'interrupt_props' array. If this field is non-zero, both 'g0_interrupt_num'
+ * and 'g1s_interrupt_num' are ignored.
*
- * 9. The 'mpidr_to_core_pos' field is a pointer to a hash function which the
- * driver will use to convert an MPIDR value to a linear core index. This
- * index will be used for accessing the 'rdistif_base_addrs' array. This is
- * an optional field. A GICv3 implementation maps each MPIDR to a linear core
- * index as well. This mapping can be found by reading the "Affinity Value"
- * and "Processor Number" fields in the GICR_TYPER. It is IMP. DEF. if the
- * "Processor Numbers" are suitable to index into an array to access core
- * specific information. If this not the case, the platform port must provide
- * a hash function. Otherwise, the "Processor Number" field will be used to
- * access the array elements.
+ * The 'rdistif_num' field contains the number of Redistributor interfaces the
+ * GIC implements. This is equal to the number of CPUs or CPU interfaces
+ * instantiated in the GIC.
+ *
+ * The 'rdistif_base_addrs' field is a pointer to an array that has an entry for
+ * storing the base address of the Redistributor interface frame of each CPU in
+ * the system. The size of the array = 'rdistif_num'. The base addresses are
+ * detected during driver initialisation.
+ *
+ * The 'mpidr_to_core_pos' field is a pointer to a hash function which the
+ * driver will use to convert an MPIDR value to a linear core index. This index
+ * will be used for accessing the 'rdistif_base_addrs' array. This is an
+ * optional field. A GICv3 implementation maps each MPIDR to a linear core index
+ * as well. This mapping can be found by reading the "Affinity Value" and
+ * "Processor Number" fields in the GICR_TYPER. It is IMP. DEF. if the
+ * "Processor Numbers" are suitable to index into an array to access core
+ * specific information. If this not the case, the platform port must provide a
+ * hash function. Otherwise, the "Processor Number" field will be used to access
+ * the array elements.
******************************************************************************/
typedef unsigned int (*mpidr_hash_fn)(u_register_t mpidr);
typedef struct gicv3_driver_data {
uintptr_t gicd_base;
uintptr_t gicr_base;
+#if !ERROR_DEPRECATED
unsigned int g0_interrupt_num;
unsigned int g1s_interrupt_num;
const unsigned int *g0_interrupt_array;
const unsigned int *g1s_interrupt_array;
+#endif
+ const interrupt_prop_t *interrupt_props;
+ unsigned int interrupt_props_num;
unsigned int rdistif_num;
uintptr_t *rdistif_base_addrs;
mpidr_hash_fn mpidr_to_core_pos;
@@ -349,5 +392,20 @@
void gicv3_its_save_disable(uintptr_t gits_base, gicv3_its_ctx_t * const its_ctx);
void gicv3_its_restore(uintptr_t gits_base, const gicv3_its_ctx_t * const its_ctx);
+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);
+void gicv3_set_interrupt_priority(unsigned int id, unsigned int proc_num,
+ unsigned int priority);
+void gicv3_set_interrupt_type(unsigned int id, unsigned int proc_num,
+ unsigned int group);
+void gicv3_raise_secure_g0_sgi(int sgi_num, u_register_t target);
+void gicv3_set_spi_routing(unsigned int id, unsigned int irm,
+ u_register_t mpidr);
+void gicv3_set_interrupt_pending(unsigned int id, unsigned int proc_num);
+void gicv3_clear_interrupt_pending(unsigned int id, unsigned int proc_num);
+unsigned int gicv3_set_pmr(unsigned int mask);
+
#endif /* __ASSEMBLY__ */
#endif /* __GICV3_H__ */
diff --git a/include/lib/aarch32/arch.h b/include/lib/aarch32/arch.h
index 6c6d6a1..3846bec 100644
--- a/include/lib/aarch32/arch.h
+++ b/include/lib/aarch32/arch.h
@@ -44,6 +44,7 @@
(((mpidr) >> MPIDR_AFF1_SHIFT) & MPIDR_AFFLVL_MASK)
#define MPIDR_AFFLVL2_VAL(mpidr) \
(((mpidr) >> MPIDR_AFF2_SHIFT) & MPIDR_AFFLVL_MASK)
+#define MPIDR_AFFLVL3_VAL(mpidr) 0
/*
* The MPIDR_MAX_AFFLVL count starts from 0. Take care to
diff --git a/include/lib/aarch32/arch_helpers.h b/include/lib/aarch32/arch_helpers.h
index 5d31836..469e9b0 100644
--- a/include/lib/aarch32/arch_helpers.h
+++ b/include/lib/aarch32/arch_helpers.h
@@ -213,6 +213,7 @@
DEFINE_SYSOP_TYPE_FUNC(dsb, ish)
DEFINE_SYSOP_TYPE_FUNC(dsb, ishst)
DEFINE_SYSOP_TYPE_FUNC(dmb, ish)
+DEFINE_SYSOP_TYPE_FUNC(dmb, ishst)
DEFINE_SYSOP_FUNC(isb)
void __dead2 smc(uint32_t r0, uint32_t r1, uint32_t r2, uint32_t r3,
@@ -257,6 +258,7 @@
DEFINE_COPROCR_RW_FUNCS(icc_sre_el2, ICC_HSRE)
DEFINE_COPROCR_RW_FUNCS(icc_sre_el3, ICC_MSRE)
DEFINE_COPROCR_RW_FUNCS(icc_pmr_el1, ICC_PMR)
+DEFINE_COPROCR_RW_FUNCS(icc_rpr_el1, ICC_RPR)
DEFINE_COPROCR_RW_FUNCS(icc_igrpen1_el3, ICC_MGRPEN1)
DEFINE_COPROCR_RW_FUNCS(icc_igrpen0_el1, ICC_IGRPEN0)
DEFINE_COPROCR_RW_FUNCS(icc_hppir0_el1, ICC_HPPIR0)
@@ -265,6 +267,7 @@
DEFINE_COPROCR_RW_FUNCS(icc_iar1_el1, ICC_IAR1)
DEFINE_COPROCR_RW_FUNCS(icc_eoir0_el1, ICC_EOIR0)
DEFINE_COPROCR_RW_FUNCS(icc_eoir1_el1, ICC_EOIR1)
+DEFINE_COPROCR_RW_FUNCS_64(icc_sgi0r_el1, ICC_SGI0R_EL1_64)
DEFINE_COPROCR_RW_FUNCS(hdcr, HDCR)
DEFINE_COPROCR_RW_FUNCS(cnthp_ctl, CNTHP_CTL)
@@ -324,4 +327,7 @@
#define read_ctr_el0() read_ctr()
+#define write_icc_sgi0r_el1(_v) \
+ write64_icc_sgi0r_el1(_v)
+
#endif /* __ARCH_HELPERS_H__ */
diff --git a/include/lib/aarch64/arch.h b/include/lib/aarch64/arch.h
index 9cbe405..997e3a2 100644
--- a/include/lib/aarch64/arch.h
+++ b/include/lib/aarch64/arch.h
@@ -68,6 +68,7 @@
#define ICC_CTLR_EL1 S3_0_C12_C12_4
#define ICC_CTLR_EL3 S3_6_C12_C12_4
#define ICC_PMR_EL1 S3_0_C4_C6_0
+#define ICC_RPR_EL1 S3_0_C12_C11_3
#define ICC_IGRPEN1_EL3 S3_6_c12_c12_7
#define ICC_IGRPEN0_EL1 S3_0_c12_c12_6
#define ICC_HPPIR0_EL1 S3_0_c12_c8_2
@@ -76,6 +77,7 @@
#define ICC_IAR1_EL1 S3_0_c12_c12_0
#define ICC_EOIR0_EL1 S3_0_c12_c8_1
#define ICC_EOIR1_EL1 S3_0_c12_c12_1
+#define ICC_SGI0R_EL1 S3_0_c12_c11_7
/*******************************************************************************
* Generic timer memory mapped registers & offsets
diff --git a/include/lib/aarch64/arch_helpers.h b/include/lib/aarch64/arch_helpers.h
index 04b2e03..9c022ab 100644
--- a/include/lib/aarch64/arch_helpers.h
+++ b/include/lib/aarch64/arch_helpers.h
@@ -198,6 +198,7 @@
DEFINE_SYSOP_TYPE_FUNC(dsb, ish)
DEFINE_SYSOP_TYPE_FUNC(dsb, ishst)
DEFINE_SYSOP_TYPE_FUNC(dmb, ish)
+DEFINE_SYSOP_TYPE_FUNC(dmb, ishst)
DEFINE_SYSOP_FUNC(isb)
uint32_t get_afflvl_shift(uint32_t);
@@ -307,6 +308,7 @@
DEFINE_RENAME_SYSREG_RW_FUNCS(icc_sre_el2, ICC_SRE_EL2)
DEFINE_RENAME_SYSREG_RW_FUNCS(icc_sre_el3, ICC_SRE_EL3)
DEFINE_RENAME_SYSREG_RW_FUNCS(icc_pmr_el1, ICC_PMR_EL1)
+DEFINE_RENAME_SYSREG_READ_FUNC(icc_rpr_el1, ICC_RPR_EL1)
DEFINE_RENAME_SYSREG_RW_FUNCS(icc_igrpen1_el3, ICC_IGRPEN1_EL3)
DEFINE_RENAME_SYSREG_RW_FUNCS(icc_igrpen0_el1, ICC_IGRPEN0_EL1)
DEFINE_RENAME_SYSREG_READ_FUNC(icc_hppir0_el1, ICC_HPPIR0_EL1)
@@ -315,6 +317,7 @@
DEFINE_RENAME_SYSREG_READ_FUNC(icc_iar1_el1, ICC_IAR1_EL1)
DEFINE_RENAME_SYSREG_WRITE_FUNC(icc_eoir0_el1, ICC_EOIR0_EL1)
DEFINE_RENAME_SYSREG_WRITE_FUNC(icc_eoir1_el1, ICC_EOIR1_EL1)
+DEFINE_RENAME_SYSREG_WRITE_FUNC(icc_sgi0r_el1, ICC_SGI0R_EL1)
#define IS_IN_EL(x) \
diff --git a/include/plat/common/platform.h b/include/plat/common/platform.h
index e189f64..f03a399 100644
--- a/include/plat/common/platform.h
+++ b/include/plat/common/platform.h
@@ -70,6 +70,26 @@
uint32_t security_state);
/*******************************************************************************
+ * Optional interrupt management functions, depending on chosen EL3 components.
+ ******************************************************************************/
+unsigned int plat_ic_get_running_priority(void);
+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);
+int plat_ic_has_interrupt_type(unsigned int type);
+void plat_ic_set_interrupt_type(unsigned int id, unsigned int type);
+void plat_ic_set_interrupt_priority(unsigned int id, unsigned int priority);
+void plat_ic_raise_el3_sgi(int sgi_num, u_register_t target);
+void plat_ic_set_spi_routing(unsigned int id, unsigned int routing_mode,
+ u_register_t mpidr);
+void plat_ic_set_interrupt_pending(unsigned int id);
+void plat_ic_clear_interrupt_pending(unsigned int id);
+unsigned int plat_ic_set_priority_mask(unsigned int mask);
+
+/*******************************************************************************
* Optional common functions (may be overridden)
******************************************************************************/
uintptr_t plat_get_my_stack(void);
diff --git a/make_helpers/defaults.mk b/make_helpers/defaults.mk
index 8601046..412c3b7 100644
--- a/make_helpers/defaults.mk
+++ b/make_helpers/defaults.mk
@@ -77,6 +77,10 @@
# For Chain of Trust
GENERATE_COT := 0
+# Hint platform interrupt control layer that Group 0 interrupts are for EL3. By
+# default, they are for Secure EL1.
+GICV2_G0_FOR_EL3 := 0
+
# Whether system coherency is managed in hardware, without explicit software
# operations.
HW_ASSISTED_COHERENCY := 0
diff --git a/plat/arm/common/arm_gicv2.c b/plat/arm/common/arm_gicv2.c
index 521fa8c..6dd847b 100644
--- a/plat/arm/common/arm_gicv2.c
+++ b/plat/arm/common/arm_gicv2.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2015-2017, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
@@ -28,11 +28,15 @@
PLAT_ARM_G0_IRQS
};
+static unsigned int target_mask_array[PLATFORM_CORE_COUNT];
+
static const gicv2_driver_data_t arm_gic_data = {
.gicd_base = PLAT_ARM_GICD_BASE,
.gicc_base = PLAT_ARM_GICC_BASE,
.g0_interrupt_num = ARRAY_SIZE(g0_interrupt_array),
.g0_interrupt_array = g0_interrupt_array,
+ .target_masks = target_mask_array,
+ .target_masks_num = ARRAY_SIZE(target_mask_array),
};
/******************************************************************************
@@ -72,6 +76,7 @@
void plat_arm_gic_pcpu_init(void)
{
gicv2_pcpu_distif_init();
+ gicv2_set_pe_target_mask(plat_my_core_pos());
}
/******************************************************************************
diff --git a/plat/common/plat_gicv2.c b/plat/common/plat_gicv2.c
index 50a8181..05fabca 100644
--- a/plat/common/plat_gicv2.c
+++ b/plat/common/plat_gicv2.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2015-2017, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
@@ -7,6 +7,7 @@
#include <gic_common.h>
#include <gicv2.h>
#include <interrupt_mgmt.h>
+#include <platform.h>
/*
* The following platform GIC functions are weakly defined. They
@@ -20,6 +21,18 @@
#pragma weak plat_ic_end_of_interrupt
#pragma weak plat_interrupt_type_to_line
+#pragma weak plat_ic_get_running_priority
+#pragma weak plat_ic_is_spi
+#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
+#pragma weak plat_ic_set_interrupt_priority
+#pragma weak plat_ic_set_interrupt_type
+#pragma weak plat_ic_raise_el3_sgi
+#pragma weak plat_ic_set_spi_routing
+
/*
* This function returns the highest priority pending interrupt at
* the Interrupt controller
@@ -53,8 +66,13 @@
id = gicv2_get_pending_interrupt_type();
/* Assume that all secure interrupts are S-EL1 interrupts */
- if (id < PENDING_G1_INTID)
+ if (id < PENDING_G1_INTID) {
+#if GICV2_G0_FOR_EL3
+ return INTR_TYPE_EL3;
+#else
return INTR_TYPE_S_EL1;
+#endif
+ }
if (id == GIC_SPURIOUS_INTERRUPT)
return INTR_TYPE_INVAL;
@@ -83,7 +101,12 @@
type = gicv2_get_interrupt_group(id);
/* Assume that all secure interrupts are S-EL1 interrupts */
- return (type) ? INTR_TYPE_NS : INTR_TYPE_S_EL1;
+ return type == GICV2_INTR_GROUP1 ? INTR_TYPE_NS :
+#if GICV2_G0_FOR_EL3
+ INTR_TYPE_EL3;
+#else
+ INTR_TYPE_S_EL1;
+#endif
}
/*
@@ -122,3 +145,135 @@
return ((gicv2_is_fiq_enabled()) ? __builtin_ctz(SCR_FIQ_BIT) :
__builtin_ctz(SCR_IRQ_BIT));
}
+
+unsigned int plat_ic_get_running_priority(void)
+{
+ return gicv2_get_running_priority();
+}
+
+int plat_ic_is_spi(unsigned int id)
+{
+ return (id >= MIN_SPI_ID) && (id <= MAX_SPI_ID);
+}
+
+int plat_ic_is_ppi(unsigned int id)
+{
+ return (id >= MIN_PPI_ID) && (id < MIN_SPI_ID);
+}
+
+int plat_ic_is_sgi(unsigned int id)
+{
+ return (id >= MIN_SGI_ID) && (id < MIN_PPI_ID);
+}
+
+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);
+}
+
+void plat_ic_set_interrupt_priority(unsigned int id, unsigned int priority)
+{
+ gicv2_set_interrupt_priority(id, priority);
+}
+
+int plat_ic_has_interrupt_type(unsigned int type)
+{
+ switch (type) {
+#if GICV2_G0_FOR_EL3
+ case INTR_TYPE_EL3:
+#else
+ case INTR_TYPE_S_EL1:
+#endif
+ case INTR_TYPE_NS:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+void plat_ic_set_interrupt_type(unsigned int id, unsigned int type)
+{
+ int gicv2_type = 0;
+
+ /* Map canonical interrupt type to GICv2 type */
+ switch (type) {
+#if GICV2_G0_FOR_EL3
+ case INTR_TYPE_EL3:
+#else
+ case INTR_TYPE_S_EL1:
+#endif
+ gicv2_type = GICV2_INTR_GROUP0;
+ break;
+ case INTR_TYPE_NS:
+ gicv2_type = GICV2_INTR_GROUP1;
+ break;
+ default:
+ assert(0);
+ }
+
+ gicv2_set_interrupt_type(id, gicv2_type);
+}
+
+void plat_ic_raise_el3_sgi(int sgi_num, u_register_t target)
+{
+#if GICV2_G0_FOR_EL3
+ int id;
+
+ /* Target must be a valid MPIDR in the system */
+ id = plat_core_pos_by_mpidr(target);
+ assert(id >= 0);
+
+ /* Verify that this is a secure SGI */
+ assert(plat_ic_get_interrupt_type(sgi_num) == INTR_TYPE_EL3);
+
+ gicv2_raise_sgi(sgi_num, id);
+#else
+ assert(0);
+#endif
+}
+
+void plat_ic_set_spi_routing(unsigned int id, unsigned int routing_mode,
+ u_register_t mpidr)
+{
+ int proc_num = 0;
+
+ switch (routing_mode) {
+ case INTR_ROUTING_MODE_PE:
+ proc_num = plat_core_pos_by_mpidr(mpidr);
+ assert(proc_num >= 0);
+ break;
+ case INTR_ROUTING_MODE_ANY:
+ /* Bit mask selecting all 8 CPUs as candidates */
+ proc_num = -1;
+ break;
+ default:
+ assert(0);
+ }
+
+ gicv2_set_spi_routing(id, proc_num);
+}
+
+void plat_ic_set_interrupt_pending(unsigned int id)
+{
+ gicv2_set_interrupt_pending(id);
+}
+
+void plat_ic_clear_interrupt_pending(unsigned int id)
+{
+ gicv2_clear_interrupt_pending(id);
+}
+
+unsigned int plat_ic_set_priority_mask(unsigned int mask)
+{
+ return gicv2_set_pmr(mask);
+}
diff --git a/plat/common/plat_gicv3.c b/plat/common/plat_gicv3.c
index 030a1d9..52ceb6a 100644
--- a/plat/common/plat_gicv3.c
+++ b/plat/common/plat_gicv3.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
*/
@@ -26,6 +26,20 @@
#pragma weak plat_ic_end_of_interrupt
#pragma weak plat_interrupt_type_to_line
+#pragma weak plat_ic_get_running_priority
+#pragma weak plat_ic_is_spi
+#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
+#pragma weak plat_ic_set_interrupt_priority
+#pragma weak plat_ic_set_interrupt_type
+#pragma weak plat_ic_raise_el3_sgi
+#pragma weak plat_ic_set_spi_routing
+#pragma weak plat_ic_set_interrupt_pending
+#pragma weak plat_ic_clear_interrupt_pending
+
CASSERT((INTR_TYPE_S_EL1 == INTR_GROUP1S) &&
(INTR_TYPE_NS == INTR_GROUP1NS) &&
(INTR_TYPE_EL3 == INTR_GROUP0), assert_interrupt_type_mismatch);
@@ -155,6 +169,108 @@
return __builtin_ctz(SCR_FIQ_BIT);
}
}
+
+unsigned int plat_ic_get_running_priority(void)
+{
+ return gicv3_get_running_priority();
+}
+
+int plat_ic_is_spi(unsigned int id)
+{
+ return (id >= MIN_SPI_ID) && (id <= MAX_SPI_ID);
+}
+
+int plat_ic_is_ppi(unsigned int id)
+{
+ return (id >= MIN_PPI_ID) && (id < MIN_SPI_ID);
+}
+
+int plat_ic_is_sgi(unsigned int id)
+{
+ return (id >= MIN_SGI_ID) && (id < MIN_PPI_ID);
+}
+
+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());
+}
+
+void plat_ic_set_interrupt_priority(unsigned int id, unsigned int priority)
+{
+ gicv3_set_interrupt_priority(id, plat_my_core_pos(), priority);
+}
+
+int plat_ic_has_interrupt_type(unsigned int type)
+{
+ assert((type == INTR_TYPE_EL3) || (type == INTR_TYPE_S_EL1) ||
+ (type == INTR_TYPE_NS));
+ return 1;
+}
+
+void plat_ic_set_interrupt_type(unsigned int id, unsigned int type)
+{
+ gicv3_set_interrupt_type(id, plat_my_core_pos(), type);
+}
+
+void plat_ic_raise_el3_sgi(int sgi_num, u_register_t target)
+{
+ /* Target must be a valid MPIDR in the system */
+ assert(plat_core_pos_by_mpidr(target) >= 0);
+
+ /* Verify that this is a secure EL3 SGI */
+ assert(plat_ic_get_interrupt_type(sgi_num) == INTR_TYPE_EL3);
+
+ gicv3_raise_secure_g0_sgi(sgi_num, target);
+}
+
+void plat_ic_set_spi_routing(unsigned int id, unsigned int routing_mode,
+ u_register_t mpidr)
+{
+ unsigned int irm = 0;
+
+ switch (routing_mode) {
+ case INTR_ROUTING_MODE_PE:
+ assert(plat_core_pos_by_mpidr(mpidr) >= 0);
+ irm = GICV3_IRM_PE;
+ break;
+ case INTR_ROUTING_MODE_ANY:
+ irm = GICV3_IRM_ANY;
+ break;
+ default:
+ assert(0);
+ }
+
+ gicv3_set_spi_routing(id, irm, mpidr);
+}
+
+void plat_ic_set_interrupt_pending(unsigned int id)
+{
+ /* Disallow setting SGIs pending */
+ assert(id >= MIN_PPI_ID);
+ gicv3_set_interrupt_pending(id, plat_my_core_pos());
+}
+
+void plat_ic_clear_interrupt_pending(unsigned int id)
+{
+ /* Disallow setting SGIs pending */
+ assert(id >= MIN_PPI_ID);
+ gicv3_clear_interrupt_pending(id, plat_my_core_pos());
+}
+
+unsigned int plat_ic_set_priority_mask(unsigned int mask)
+{
+ return gicv3_set_pmr(mask);
+}
#endif
#ifdef IMAGE_BL32