diff --git a/docs/platform-interrupt-controller-API.rst b/docs/platform-interrupt-controller-API.rst index df4139e..d5c1673 100644 --- a/docs/platform-interrupt-controller-API.rst +++ b/docs/platform-interrupt-controller-API.rst @@ -243,6 +243,37 @@ 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. + ---- *Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.* diff --git a/drivers/arm/gic/v2/gicv2_main.c b/drivers/arm/gic/v2/gicv2_main.c index f0b902c..eab4c3b 100644 --- a/drivers/arm/gic/v2/gicv2_main.c +++ b/drivers/arm/gic/v2/gicv2_main.c @@ -438,3 +438,41 @@ 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); +} diff --git a/drivers/arm/gic/v3/gicv3_helpers.c b/drivers/arm/gic/v3/gicv3_helpers.c index ee874f9..3abc6a5 100644 --- a/drivers/arm/gic/v3/gicv3_helpers.c +++ b/drivers/arm/gic/v3/gicv3_helpers.c @@ -195,6 +195,28 @@ } /* + * 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. */ diff --git a/drivers/arm/gic/v3/gicv3_main.c b/drivers/arm/gic/v3/gicv3_main.c index c81ba95..43dd77f 100644 --- a/drivers/arm/gic/v3/gicv3_main.c +++ b/drivers/arm/gic/v3/gicv3_main.c @@ -1037,3 +1037,55 @@ } } } + +/******************************************************************************* + * 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); + } +} diff --git a/drivers/arm/gic/v3/gicv3_private.h b/drivers/arm/gic/v3/gicv3_private.h index 19fce2e..43529c5 100644 --- a/drivers/arm/gic/v3/gicv3_private.h +++ b/drivers/arm/gic/v3/gicv3_private.h @@ -72,6 +72,8 @@ 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); @@ -221,6 +223,11 @@ return mmio_read_32(base + GICR_ISENABLER0); } +static inline void gicr_write_icpendr0(uintptr_t base, unsigned int val) +{ + mmio_write_32(base + GICR_ICPENDR0, val); +} + static inline void gicr_write_isenabler0(uintptr_t base, unsigned int val) { mmio_write_32(base + GICR_ISENABLER0, val); diff --git a/include/drivers/arm/gicv2.h b/include/drivers/arm/gicv2.h index 6d224c3..bc2822e 100644 --- a/include/drivers/arm/gicv2.h +++ b/include/drivers/arm/gicv2.h @@ -172,6 +172,8 @@ 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); #endif /* __ASSEMBLY__ */ #endif /* __GICV2_H__ */ diff --git a/include/drivers/arm/gicv3.h b/include/drivers/arm/gicv3.h index 4f7cd8a..09d6d80 100644 --- a/include/drivers/arm/gicv3.h +++ b/include/drivers/arm/gicv3.h @@ -387,6 +387,8 @@ 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); #endif /* __ASSEMBLY__ */ #endif /* __GICV3_H__ */ diff --git a/include/plat/common/platform.h b/include/plat/common/platform.h index 5c4e058..ab5d68e 100644 --- a/include/plat/common/platform.h +++ b/include/plat/common/platform.h @@ -85,6 +85,8 @@ 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); /******************************************************************************* * Optional common functions (may be overridden) diff --git a/plat/common/plat_gicv2.c b/plat/common/plat_gicv2.c index c30d872..6460494 100644 --- a/plat/common/plat_gicv2.c +++ b/plat/common/plat_gicv2.c @@ -262,3 +262,13 @@ 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); +} diff --git a/plat/common/plat_gicv3.c b/plat/common/plat_gicv3.c index 7b3ea33..e5bf014 100644 --- a/plat/common/plat_gicv3.c +++ b/plat/common/plat_gicv3.c @@ -37,6 +37,8 @@ #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) && @@ -250,6 +252,20 @@ 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()); +} #endif #ifdef IMAGE_BL32