diff --git a/docs/platform-interrupt-controller-API.rst b/docs/platform-interrupt-controller-API.rst index 74291bc..df4139e 100644 --- a/docs/platform-interrupt-controller-API.rst +++ b/docs/platform-interrupt-controller-API.rst @@ -217,6 +217,32 @@ 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. + ---- *Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.* diff --git a/drivers/arm/gic/v2/gicv2_helpers.c b/drivers/arm/gic/v2/gicv2_helpers.c index 7cdbc27..2693076 100644 --- a/drivers/arm/gic/v2/gicv2_helpers.c +++ b/drivers/arm/gic/v2/gicv2_helpers.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2016, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2017, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -72,15 +72,6 @@ mmio_write_32(base + GICD_SPENDSGIR + (n << 2), val); } -/* - * Accessor to write the GIC Distributor ITARGETSR corresponding to the - * interrupt `id`. - */ -void gicd_set_itargetsr(uintptr_t base, unsigned int id, unsigned int target) -{ - mmio_write_8(base + GICD_ITARGETSR + id, target & GIC_TARGET_CPU_MASK); -} - /******************************************************************************* * Get the current CPU bit mask from GICD_ITARGETSR0 ******************************************************************************/ diff --git a/drivers/arm/gic/v2/gicv2_main.c b/drivers/arm/gic/v2/gicv2_main.c index b59e458..f0b902c 100644 --- a/drivers/arm/gic/v2/gicv2_main.c +++ b/drivers/arm/gic/v2/gicv2_main.c @@ -403,3 +403,38 @@ 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); +} diff --git a/drivers/arm/gic/v2/gicv2_private.h b/drivers/arm/gic/v2/gicv2_private.h index 915fc3a..70f0597 100644 --- a/drivers/arm/gic/v2/gicv2_private.h +++ b/drivers/arm/gic/v2/gicv2_private.h @@ -34,6 +34,17 @@ /******************************************************************************* * 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); diff --git a/drivers/arm/gic/v3/gicv3_main.c b/drivers/arm/gic/v3/gicv3_main.c index a506732..c81ba95 100644 --- a/drivers/arm/gic/v3/gicv3_main.c +++ b/drivers/arm/gic/v3/gicv3_main.c @@ -999,3 +999,41 @@ 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(); + } + } +} diff --git a/include/bl31/interrupt_mgmt.h b/include/bl31/interrupt_mgmt.h index 9a6a7fa..4f5a601 100644 --- a/include/bl31/interrupt_mgmt.h +++ b/include/bl31/interrupt_mgmt.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2014-2017, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -17,6 +17,11 @@ #define INTR_TYPE_NS U(2) #define MAX_INTR_TYPES U(3) #define INTR_TYPE_INVAL MAX_INTR_TYPES + +/* Interrupt routing modes */ +#define INTR_ROUTING_MODE_PE 0 +#define INTR_ROUTING_MODE_ANY 1 + /* * Constant passed to the interrupt handler in the 'id' field when the * framework does not read the gic registers to determine the interrupt id. diff --git a/include/drivers/arm/gic_common.h b/include/drivers/arm/gic_common.h index f4e2c52..14979cc 100644 --- a/include/drivers/arm/gic_common.h +++ b/include/drivers/arm/gic_common.h @@ -74,6 +74,7 @@ #define ISACTIVER_SHIFT 5 #define ICACTIVER_SHIFT ISACTIVER_SHIFT #define IPRIORITYR_SHIFT 2 +#define ITARGETSR_SHIFT 2 #define ICFGR_SHIFT 4 #define NSACR_SHIFT 4 diff --git a/include/drivers/arm/gicv2.h b/include/drivers/arm/gicv2.h index c9bcbb6..6d224c3 100644 --- a/include/drivers/arm/gicv2.h +++ b/include/drivers/arm/gicv2.h @@ -171,6 +171,7 @@ 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); #endif /* __ASSEMBLY__ */ #endif /* __GICV2_H__ */ diff --git a/include/drivers/arm/gicv3.h b/include/drivers/arm/gicv3.h index bf294f1..4f7cd8a 100644 --- a/include/drivers/arm/gicv3.h +++ b/include/drivers/arm/gicv3.h @@ -75,6 +75,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 /******************************************************************************* @@ -382,6 +385,8 @@ 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); #endif /* __ASSEMBLY__ */ #endif /* __GICV3_H__ */ diff --git a/include/plat/common/platform.h b/include/plat/common/platform.h index b4e33d9..5c4e058 100644 --- a/include/plat/common/platform.h +++ b/include/plat/common/platform.h @@ -83,6 +83,8 @@ void plat_ic_set_interrupt_type(unsigned int id, unsigned int type); void plat_ic_set_interrupt_priority(unsigned int id, unsigned int priority); void plat_ic_raise_el3_sgi(int sgi_num, u_register_t target); +void plat_ic_set_spi_routing(unsigned int id, unsigned int routing_mode, + u_register_t mpidr); /******************************************************************************* * Optional common functions (may be overridden) diff --git a/plat/common/plat_gicv2.c b/plat/common/plat_gicv2.c index 5df9c79..c30d872 100644 --- a/plat/common/plat_gicv2.c +++ b/plat/common/plat_gicv2.c @@ -31,6 +31,7 @@ #pragma weak plat_ic_set_interrupt_priority #pragma weak plat_ic_set_interrupt_type #pragma weak plat_ic_raise_el3_sgi +#pragma weak plat_ic_set_spi_routing /* * This function returns the highest priority pending interrupt at @@ -240,3 +241,24 @@ assert(0); #endif } + +void plat_ic_set_spi_routing(unsigned int id, unsigned int routing_mode, + u_register_t mpidr) +{ + int proc_num = 0; + + switch (routing_mode) { + case INTR_ROUTING_MODE_PE: + proc_num = plat_core_pos_by_mpidr(mpidr); + assert(proc_num >= 0); + break; + case INTR_ROUTING_MODE_ANY: + /* Bit mask selecting all 8 CPUs as candidates */ + proc_num = -1; + break; + default: + assert(0); + } + + gicv2_set_spi_routing(id, proc_num); +} diff --git a/plat/common/plat_gicv3.c b/plat/common/plat_gicv3.c index 819ca45..7b3ea33 100644 --- a/plat/common/plat_gicv3.c +++ b/plat/common/plat_gicv3.c @@ -36,6 +36,7 @@ #pragma weak plat_ic_set_interrupt_priority #pragma weak plat_ic_set_interrupt_type #pragma weak plat_ic_raise_el3_sgi +#pragma weak plat_ic_set_spi_routing CASSERT((INTR_TYPE_S_EL1 == INTR_GROUP1S) && (INTR_TYPE_NS == INTR_GROUP1NS) && @@ -229,6 +230,26 @@ gicv3_raise_secure_g0_sgi(sgi_num, target); } + +void plat_ic_set_spi_routing(unsigned int id, unsigned int routing_mode, + u_register_t mpidr) +{ + unsigned int irm = 0; + + switch (routing_mode) { + case INTR_ROUTING_MODE_PE: + assert(plat_core_pos_by_mpidr(mpidr) >= 0); + irm = GICV3_IRM_PE; + break; + case INTR_ROUTING_MODE_ANY: + irm = GICV3_IRM_ANY; + break; + default: + assert(0); + } + + gicv3_set_spi_routing(id, irm, mpidr); +} #endif #ifdef IMAGE_BL32