mirror of
https://github.com/ARM-software/arm-trusted-firmware.git
synced 2025-04-15 00:54:22 +00:00
Merge pull request #1130 from jeenu-arm/gic-patches
New GIC APIs and specifying interrupt propertes
This commit is contained in:
commit
623c43774a
27 changed files with 1975 additions and 118 deletions
2
Makefile
2
Makefile
|
@ -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
|
||||
---------------------------------
|
||||
|
||||
|
|
297
docs/platform-interrupt-controller-API.rst
Normal file
297
docs/platform-interrupt-controller-API.rst
Normal file
|
@ -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.*
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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 @@ 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);
|
||||
}
|
||||
|
|
|
@ -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_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_ */
|
||||
|
|
|
@ -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 @@ 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);
|
||||
|
||||
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 @@ 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);
|
||||
|
||||
/* 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 @@ 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
|
||||
*/
|
||||
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 @@ 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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
/*******************************************************************************
|
||||
|
@ -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
|
||||
******************************************************************************/
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
@ -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,23 +67,33 @@ void gicv3_driver_init(const gicv3_driver_data_t *plat_driver_data)
|
|||
|
||||
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 @@ 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,23 +180,37 @@ void gicv3_distif_init(void)
|
|||
/* 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 @@ 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,21 +241,34 @@ void gicv3_rdistif_init(unsigned int proc_num)
|
|||
/* 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 @@ 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);
|
||||
|
|
|
@ -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,
|
||||
|
|
29
include/common/interrupt_props.h
Normal file
29
include/common/interrupt_props.h
Normal file
|
@ -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__ */
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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_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
|
||||
* 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_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__ */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -198,6 +198,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)
|
||||
|
||||
uint32_t get_afflvl_shift(uint32_t);
|
||||
|
@ -307,6 +308,7 @@ DEFINE_RENAME_SYSREG_RW_FUNCS(icc_sre_el1, ICC_SRE_EL1)
|
|||
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_iar0_el1, ICC_IAR0_EL1)
|
|||
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) \
|
||||
|
|
|
@ -69,6 +69,26 @@ void plat_ic_end_of_interrupt(uint32_t id);
|
|||
uint32_t plat_interrupt_type_to_line(uint32_t type,
|
||||
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)
|
||||
******************************************************************************/
|
||||
|
|
|
@ -77,6 +77,10 @@ FWU_FIP_NAME := fwu_fip.bin
|
|||
# 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
|
||||
|
|
|
@ -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 @@ static const unsigned int g0_interrupt_array[] = {
|
|||
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_cpuif_disable(void)
|
|||
void plat_arm_gic_pcpu_init(void)
|
||||
{
|
||||
gicv2_pcpu_distif_init();
|
||||
gicv2_set_pe_target_mask(plat_my_core_pos());
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
|
|
|
@ -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 @@ uint32_t plat_ic_get_pending_interrupt_type(void)
|
|||
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 @@ uint32_t plat_ic_get_interrupt_type(uint32_t id)
|
|||
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 @@ uint32_t plat_interrupt_type_to_line(uint32_t type,
|
|||
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);
|
||||
}
|
||||
|
|
|
@ -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 @@ uint32_t plat_interrupt_type_to_line(uint32_t type,
|
|||
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
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue