Commit 623c4377 authored by davidcunado-arm's avatar davidcunado-arm Committed by GitHub
Browse files

Merge pull request #1130 from jeenu-arm/gic-patches

New GIC APIs and specifying interrupt propertes
parents 6de7c00c c639e8eb
......@@ -449,6 +449,7 @@ $(eval $(call assert_boolean,ENABLE_RUNTIME_INSTRUMENTATION))
$(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_PSCI_STAT))
$(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))
......
......@@ -1167,6 +1167,56 @@ In other words, the reset handler should be able to detect whether an action has
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
---------------------------------
......
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.*
......@@ -2327,6 +2327,10 @@ Standard layer to use GICv2 and the FVP can be configured to use either GICv2 or
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 @@ amount of open resources per driver.
--------------
*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
......
......@@ -386,6 +386,19 @@ Common build options
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.
......
/*
* 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 @@ void gicd_set_icpendr(uintptr_t base, unsigned int id)
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 @@ void gicd_set_ipriorityr(uintptr_t base, unsigned int id, unsigned int pri)
{
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);
}
/*
* 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_isenabler(uintptr_t base, unsigned int id);
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_ */
/*
* 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 @@ void gicd_write_spendsgir(uintptr_t base, unsigned int id, unsigned int val)
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 @@ void gicv2_spis_configure_defaults(uintptr_t gicd_base)
gicd_write_icfgr(gicd_base, index, 0);
}
#if !ERROR_DEPRECATED
/*******************************************************************************
* Helper function to configure secure G0 SPIs.
******************************************************************************/
......@@ -154,7 +147,49 @@ void gicv2_secure_spis_configure(uintptr_t gicd_base,
}
}
#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.
******************************************************************************/
......@@ -202,3 +237,66 @@ void gicv2_secure_ppi_sgi_setup(uintptr_t gicd_base,
/* 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);
}
......@@ -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 @@ void gicv2_pcpu_distif_init(void)
{
assert(driver_data);
assert(driver_data->gicd_base);
assert(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 @@ void gicv2_distif_init(void)
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 @@ void gicv2_distif_init(void)
/* Set the default attribute of all SPIs */
gicv2_spis_configure_defaults(driver_data->gicd_base);
#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 @@ void gicv2_driver_init(const gicv2_driver_data_t *plat_driver_data)
assert(plat_driver_data->gicd_base);
assert(plat_driver_data->gicc_base);
/*
* The platform should provide a list of atleast one type of
* interrupts
*/
#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);
/* 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.
* 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 @@ unsigned int gicv2_get_interrupt_group(unsigned int id)
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;
}
/*
* 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);
/*******************************************************************************
......@@ -31,6 +39,25 @@ static inline unsigned int gicd_read_pidr2(uintptr_t base)
return mmio_read_32(base + GICD_PIDR2_GICV2);
}
/*******************************************************************************
* 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 @@ static inline unsigned int gicc_read_iidr(uintptr_t base)
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
******************************************************************************/
......
/*
* 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"
......@@ -171,6 +172,51 @@ void gicr_set_isenabler0(uintptr_t base, unsigned int id)
gicr_write_isenabler0(base, (1 << bit_num));
}
/*
* Accessor to set the bit corresponding to interrupt ID in GIC Re-distributor
* ICENABLER0.
*/
void gicr_set_icenabler0(uintptr_t base, unsigned int id)
{
unsigned bit_num = id & ((1 << ICENABLER_SHIFT) - 1);
gicr_write_icenabler0(base, (1 << bit_num));
}
/*
* Accessor to set the bit corresponding to interrupt ID in GIC Re-distributor
* ISACTIVER0.
*/
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 @@ void gicr_set_ipriorityr(uintptr_t base, unsigned int id, unsigned int pri)
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 @@ void gicv3_spis_configure_defaults(uintptr_t gicd_base)
gicd_write_icfgr(gicd_base, index, 0);
}
#if !ERROR_DEPRECATED
/*******************************************************************************
* Helper function to configure secure G0 and G1S SPIs.
******************************************************************************/
......@@ -333,6 +412,63 @@ void gicv3_secure_spis_configure(uintptr_t gicd_base,
}
}
#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 @@ void gicv3_ppi_sgi_configure_defaults(uintptr_t gicr_base)
gicr_write_icfgr1(gicr_base, 0);
}
#if !ERROR_DEPRECATED
/*******************************************************************************
* Helper function to configure secure G0 and G1S SPIs.
******************************************************************************/
......@@ -399,3 +536,54 @@ void gicv3_secure_ppi_sgi_configure(uintptr_t gicr_base,
}
}
}
#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);
}
}
......@@ -9,11 +9,20 @@
#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,16 +67,21 @@ void gicv3_driver_init(const gicv3_driver_data_t *plat_driver_data)
assert(IS_IN_EL3());
#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);
/*
* The platform should provide a list of at least one type of
* interrupts
* 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.
* 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 :
......@@ -75,6 +89,11 @@ void gicv3_driver_init(const gicv3_driver_data_t *plat_driver_data)
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 @@ void gicv3_distif_init(void)
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,6 +180,18 @@ void gicv3_distif_init(void)
/* Set the default attribute of all SPIs */
gicv3_spis_configure_defaults(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,
......@@ -180,6 +209,8 @@ void gicv3_distif_init(void)
INTR_GROUP0);
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 @@ void gicv3_rdistif_init(unsigned int proc_num)
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,6 +241,17 @@ void gicv3_rdistif_init(unsigned int proc_num)
/* Set the default attribute of all SGIs and PPIs */
gicv3_ppi_sgi_configure_defaults(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,
......@@ -227,6 +267,8 @@ void gicv3_rdistif_init(unsigned int proc_num)
gicv3_driver_data->g0_interrupt_array,
INTR_GROUP0);
}
}
#endif
}
/*******************************************************************************
......@@ -769,3 +811,337 @@ void gicv3_distif_init_restore(const gicv3_dist_ctx_t * const dist_ctx)
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;
}
......@@ -67,9 +67,13 @@ void gicr_write_ipriorityr(uintptr_t base, unsigned int id, unsigned int val);
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 gicr_set_ipriorityr(uintptr_t base, unsigned int id, unsigned int pri);
******************************************************************************/
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 @@ void gicv3_secure_ppi_sgi_configure(uintptr_t gicr_base,
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 @@ static inline unsigned int gicr_read_isenabler0(uintptr_t base)
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);
......
/*
* 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,
......
/*
* 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__ */
......@@ -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
......
/*
* 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
* 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.
*
* 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.
*
* 4. The 'g0_interrupt_num' field contains the number of entries in the
* 'g0_interrupt_array'.
* 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_get_pending_interrupt_id(void);
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__ */
......@@ -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
* 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.
*
* 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.
*
* 4. The 'g0_interrupt_num' field contains the number of entries in the
* 'g0_interrupt_array'.
* 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.
*
* 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_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.
*
* 6. The 'g1s_interrupt_num' field contains the number of entries in the
* 'g1s_interrupt_array'.
* 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.
*
* 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
* 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.
*
* 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.
*
* 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 '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.
*
* 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
* 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.
* 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_rdistif_save(unsigned int proc_num, gicv3_redist_ctx_t * const rdist_
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__ */
......@@ -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
......
......@@ -213,6 +213,7 @@ DEFINE_SYSOP_TYPE_FUNC(dmb, ld)
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_el1, ICC_SRE)
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_iar0_el1, ICC_IAR0)
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_DCOP_PARAM_FUNC(cvac, DCCMVAC)
#define read_ctr_el0() read_ctr()
#define write_icc_sgi0r_el1(_v) \
write64_icc_sgi0r_el1(_v)
#endif /* __ARCH_HELPERS_H__ */
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment