Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
adam.huang
Arm Trusted Firmware
Commits
623c4377
Commit
623c4377
authored
Oct 21, 2017
by
davidcunado-arm
Committed by
GitHub
Oct 21, 2017
Browse files
Merge pull request #1130 from jeenu-arm/gic-patches
New GIC APIs and specifying interrupt propertes
parents
6de7c00c
c639e8eb
Changes
27
Hide whitespace changes
Inline
Side-by-side
Makefile
View file @
623c4377
...
...
@@ -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))
...
...
docs/firmware-design.rst
View file @
623c4377
...
...
@@ -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
---------------------------------
...
...
docs/platform-interrupt-controller-API.rst
0 → 100644
View file @
623c4377
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.*
docs/porting-guide.rst
View file @
623c4377
...
...
@@ -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-201
6
, ARM Limited and Contributors. All rights reserved.*
*Copyright (c) 2013-201
7
, ARM Limited and Contributors. All rights reserved.*
.. _Migration Guide: platform-migration-guide.rst
.. _include/plat/common/platform.h: ../include/plat/common/platform.h
...
...
docs/user-guide.rst
View file @
623c4377
...
...
@@ -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.
...
...
drivers/arm/gic/common/gic_common.c
View file @
623c4377
/*
* Copyright (c) 2015-201
6
, ARM Limited and Contributors. All rights reserved.
* Copyright (c) 2015-201
7
, 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
);
}
drivers/arm/gic/common/gic_common_private.h
View file @
623c4377
/*
* 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_ */
drivers/arm/gic/v2/gicv2_helpers.c
View file @
623c4377
/*
* Copyright (c) 2015-201
6
, ARM Limited and Contributors. All rights reserved.
* Copyright (c) 2015-201
7
, 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
);
}
drivers/arm/gic/v2/gicv2_main.c
View file @
623c4377
...
...
@@ -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 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
);
#if !ERROR_DEPRECATED
if
(
plat_driver_data
->
interrupt_props
==
NULL
)
{
/* Interrupt properties array size must be 0 */
assert
(
plat_driver_data
->
interrupt_props_num
==
0
);
/* The platform should provide a list of secure interrupts */
assert
(
plat_driver_data
->
g0_interrupt_array
);
/*
* If there are no interrupts of a particular type, then the
* number of interrupts of that type should be 0 and vice-versa.
*/
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
;
}
drivers/arm/gic/v2/gicv2_private.h
View file @
623c4377
/*
* 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
******************************************************************************/
...
...
drivers/arm/gic/v3/gicv3_helpers.c
View file @
623c4377
/*
* Copyright (c) 2015-201
6
, ARM Limited and Contributors. All rights reserved.
* Copyright (c) 2015-201
7
, 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
);
}
}
drivers/arm/gic/v3/gicv3_main.c
View file @
623c4377
...
...
@@ -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 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
);
#if !ERROR_DEPRECATED
if
(
plat_driver_data
->
interrupt_props
==
NULL
)
{
/* Interrupt properties array size must be 0 */
assert
(
plat_driver_data
->
interrupt_props_num
==
0
);
/*
* The platform should provide a list of at least one type of
* 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
;
}
drivers/arm/gic/v3/gicv3_private.h
View file @
623c4377
...
...
@@ -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
);
...
...
include/bl31/interrupt_mgmt.h
View file @
623c4377
/*
* 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
,
...
...
include/common/interrupt_props.h
0 → 100644
View file @
623c4377
/*
* 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__ */
include/drivers/arm/gic_common.h
View file @
623c4377
...
...
@@ -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
...
...
include/drivers/arm/gicv2.h
View file @
623c4377
/*
* 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.
*
* The 'gicc_base' field contains the base address of the CPU Interface
* programmer's view.
*
* The 'g0_interrupt_array' field is a pointer to an array in which each entry
* corresponds to an ID of a Group 0 interrupt. This field is ignored when
* 'interrupt_props' field is used. This field is deprecated.
*
* The 'g0_interrupt_num' field contains the number of entries in the
* 'g0_interrupt_array'. This field is ignored when 'interrupt_props' field is
* used. This field is deprecated.
*
* 2. The 'gicc_base' field contains the base address of the CPU Interface
* programmer's view.
* 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.
*
* 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 'interrupt_props' field is a pointer to an array that enumerates secure
* interrupts and their properties. If this field is not NULL, both
* 'g0_interrupt_array' and 'g1s_interrupt_array' fields are ignored.
*
* 4. The 'g0_interrupt_num' field contains the number of entries in the
* 'g0_interrupt_array'.
* The 'interrupt_props_num' field contains the number of entries in the
* 'interrupt_props' array. If this field is non-zero, 'g0_interrupt_num' is
* ignored.
******************************************************************************/
typedef
struct
gicv2_driver_data
{
uintptr_t
gicd_base
;
uintptr_t
gicc_base
;
#if !ERROR_DEPRECATED
unsigned
int
g0_interrupt_num
;
const
unsigned
int
*
g0_interrupt_array
;
#endif
unsigned
int
*
target_masks
;
unsigned
int
target_masks_num
;
const
interrupt_prop_t
*
interrupt_props
;
unsigned
int
interrupt_props_num
;
}
gicv2_driver_data_t
;
/*******************************************************************************
...
...
@@ -136,6 +178,18 @@ unsigned int gicv2_get_pending_interrupt_id(void);
unsigned
int
gicv2_acknowledge_interrupt
(
void
);
void
gicv2_end_of_interrupt
(
unsigned
int
id
);
unsigned
int
gicv2_get_interrupt_group
(
unsigned
int
id
);
unsigned
int
gicv2_get_running_priority
(
void
);
void
gicv2_set_pe_target_mask
(
unsigned
int
proc_num
);
unsigned
int
gicv2_get_interrupt_active
(
unsigned
int
id
);
void
gicv2_enable_interrupt
(
unsigned
int
id
);
void
gicv2_disable_interrupt
(
unsigned
int
id
);
void
gicv2_set_interrupt_priority
(
unsigned
int
id
,
unsigned
int
priority
);
void
gicv2_set_interrupt_type
(
unsigned
int
id
,
unsigned
int
type
);
void
gicv2_raise_sgi
(
int
sgi_num
,
int
proc_num
);
void
gicv2_set_spi_routing
(
unsigned
int
id
,
int
proc_num
);
void
gicv2_set_interrupt_pending
(
unsigned
int
id
);
void
gicv2_clear_interrupt_pending
(
unsigned
int
id
);
unsigned
int
gicv2_set_pmr
(
unsigned
int
mask
);
#endif
/* __ASSEMBLY__ */
#endif
/* __GICV2_H__ */
include/drivers/arm/gicv3.h
View file @
623c4377
...
...
@@ -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
num
b
er
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 e
numer
ates 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__ */
include/lib/aarch32/arch.h
View file @
623c4377
...
...
@@ -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
...
...
include/lib/aarch32/arch_helpers.h
View file @
623c4377
...
...
@@ -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__ */
Prev
1
2
Next
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment