diff --git a/drivers/arm/gic/arm_gic.c b/drivers/arm/gic/arm_gic.c index 86aaa9a..58fbc89 100644 --- a/drivers/arm/gic/arm_gic.c +++ b/drivers/arm/gic/arm_gic.c @@ -40,6 +40,12 @@ #include #include +/* Value used to initialize Non-Secure IRQ priorities four at a time */ +#define GICD_IPRIORITYR_DEF_VAL \ + (GIC_HIGHEST_NS_PRIORITY | \ + (GIC_HIGHEST_NS_PRIORITY << 8) | \ + (GIC_HIGHEST_NS_PRIORITY << 16) | \ + (GIC_HIGHEST_NS_PRIORITY << 24)) static unsigned int g_gicc_base; static unsigned int g_gicd_base; @@ -216,8 +222,16 @@ unsigned int index, irq_num; assert(g_gicd_base); + + /* Mark all 32 SGI+PPI interrupts as Group 1 (non-secure) */ gicd_write_igroupr(g_gicd_base, 0, ~0); + /* Setup PPI priorities doing four at a time */ + for (index = 0; index < 32; index += 4) { + gicd_write_ipriorityr(g_gicd_base, index, + GICD_IPRIORITYR_DEF_VAL); + } + assert(g_irq_sec_ptr); for (index = 0; index < g_num_irqs; index++) { irq_num = g_irq_sec_ptr[index]; @@ -232,6 +246,17 @@ } /******************************************************************************* + * Get the current CPU bit mask from GICD_ITARGETSR0 + ******************************************************************************/ +static unsigned int arm_gic_get_cpuif_id(void) +{ + unsigned int val; + + val = gicd_read_itargetsr(g_gicd_base, 0); + return val & GIC_TARGET_CPU_MASK; +} + +/******************************************************************************* * Global gic distributor setup which will be done by the primary cpu after a * cold boot. It marks out the secure SPIs, PPIs & SGIs and enables them. It * then enables the secure GIC distributor interface. @@ -239,6 +264,7 @@ static void arm_gic_distif_setup(void) { unsigned int num_ints, ctlr, index, irq_num; + uint8_t target_cpu; /* Disable the distributor before going further */ assert(g_gicd_base); @@ -247,16 +273,24 @@ gicd_write_ctlr(g_gicd_base, ctlr); /* - * Mark out non-secure interrupts. Calculate number of - * IGROUPR registers to consider. Will be equal to the - * number of IT_LINES + * Mark out non-secure SPI interrupts. The number of interrupts is + * calculated as 32 * (IT_LINES + 1). We do 32 at a time. */ num_ints = gicd_read_typer(g_gicd_base) & IT_LINES_NO_MASK; - num_ints++; - for (index = 0; index < num_ints; index++) - gicd_write_igroupr(g_gicd_base, index << IGROUPR_SHIFT, ~0); + num_ints = (num_ints + 1) << 5; + for (index = MIN_SPI_ID; index < num_ints; index += 32) + gicd_write_igroupr(g_gicd_base, index, ~0); - /* Configure secure interrupts now */ + /* Setup SPI priorities doing four at a time */ + for (index = MIN_SPI_ID; index < num_ints; index += 4) { + gicd_write_ipriorityr(g_gicd_base, index, + GICD_IPRIORITYR_DEF_VAL); + } + + /* Read the target CPU mask */ + target_cpu = arm_gic_get_cpuif_id(); + + /* Configure SPI secure interrupts now */ assert(g_irq_sec_ptr); for (index = 0; index < g_num_irqs; index++) { irq_num = g_irq_sec_ptr[index]; @@ -265,11 +299,16 @@ gicd_clr_igroupr(g_gicd_base, irq_num); gicd_set_ipriorityr(g_gicd_base, irq_num, GIC_HIGHEST_SEC_PRIORITY); - gicd_set_itargetsr(g_gicd_base, irq_num, - platform_get_core_pos(read_mpidr())); + gicd_set_itargetsr(g_gicd_base, irq_num, target_cpu); gicd_set_isenabler(g_gicd_base, irq_num); } } + + /* + * Configure the SGI and PPI. This is done in a separated function + * because each CPU is responsible for initializing its own private + * interrupts. + */ arm_gic_pcpu_distif_setup(); gicd_write_ctlr(g_gicd_base, ctlr | ENABLE_GRP0); @@ -285,13 +324,22 @@ unsigned int num_irqs ) { + unsigned int val; + assert(gicc_base); assert(gicd_base); - assert(gicr_base); assert(irq_sec_ptr); + g_gicc_base = gicc_base; g_gicd_base = gicd_base; - g_gicr_base = gicr_base; + + val = gicc_read_iidr(g_gicc_base); + + if (((val >> GICC_IIDR_ARCH_SHIFT) & GICC_IIDR_ARCH_MASK) >= 3) { + assert(gicr_base); + g_gicr_base = gicr_base; + } + g_irq_sec_ptr = irq_sec_ptr; g_num_irqs = num_irqs; } diff --git a/drivers/arm/gic/gic_v2.c b/drivers/arm/gic/gic_v2.c index 27a39b9..41603a9 100644 --- a/drivers/arm/gic/gic_v2.c +++ b/drivers/arm/gic/gic_v2.c @@ -283,13 +283,12 @@ mmio_write_32(reg, reg_val); } -void gicd_set_itargetsr(unsigned int base, unsigned int id, unsigned int iface) +void gicd_set_itargetsr(unsigned int base, unsigned int id, unsigned int target) { unsigned byte_off = id & ((1 << ITARGETSR_SHIFT) - 1); unsigned int reg_val = gicd_read_itargetsr(base, id); - gicd_write_itargetsr(base, id, reg_val | - (1 << iface) << (byte_off << 3)); + gicd_write_itargetsr(base, id, reg_val | (target << (byte_off << 3))); } /******************************************************************************* diff --git a/include/drivers/arm/gic_v2.h b/include/drivers/arm/gic_v2.h index 4c6b0dc..a2d3eee 100644 --- a/include/drivers/arm/gic_v2.h +++ b/include/drivers/arm/gic_v2.h @@ -48,6 +48,7 @@ #define GIC_HIGHEST_NS_PRIORITY 128 #define GIC_LOWEST_NS_PRIORITY 254 /* 255 would disable an interrupt */ #define GIC_SPURIOUS_INTERRUPT 1023 +#define GIC_TARGET_CPU_MASK 0xff #define ENABLE_GRP0 (1 << 0) #define ENABLE_GRP1 (1 << 1) diff --git a/plat/juno/aarch64/juno_common.c b/plat/juno/aarch64/juno_common.c index 59bc7ed..8129b05 100644 --- a/plat/juno/aarch64/juno_common.c +++ b/plat/juno/aarch64/juno_common.c @@ -29,6 +29,7 @@ */ #include +#include #include #include #include @@ -113,6 +114,28 @@ }; #endif +/* Array of secure interrupts to be configured by the gic driver */ +const unsigned int irq_sec_array[] = { + IRQ_MHU, + IRQ_GPU_SMMU_0, + IRQ_GPU_SMMU_1, + IRQ_ETR_SMMU, + IRQ_TZC400, + IRQ_TZ_WDOG, + IRQ_SEC_PHY_TIMER, + IRQ_SEC_SGI_0, + IRQ_SEC_SGI_1, + IRQ_SEC_SGI_2, + IRQ_SEC_SGI_3, + IRQ_SEC_SGI_4, + IRQ_SEC_SGI_5, + IRQ_SEC_SGI_6, + IRQ_SEC_SGI_7 +}; + +const unsigned int num_sec_irqs = sizeof(irq_sec_array) / + sizeof(irq_sec_array[0]); + /******************************************************************************* * Macro generating the code for the function setting up the pagetables as per * the platform memory map & initialize the mmu, for the given exception level @@ -163,3 +186,8 @@ return counter_base_frequency; } + +void plat_gic_init(void) +{ + arm_gic_init(GICC_BASE, GICD_BASE, 0, irq_sec_array, num_sec_irqs); +} diff --git a/plat/juno/bl31_plat_setup.c b/plat/juno/bl31_plat_setup.c index 4a92d44..c450462 100644 --- a/plat/juno/bl31_plat_setup.c +++ b/plat/juno/bl31_plat_setup.c @@ -29,6 +29,7 @@ */ #include +#include #include #include #include @@ -151,7 +152,8 @@ mhu_secure_init(); /* Initialize the gic cpu and distributor interfaces */ - gic_setup(); + plat_gic_init(); + arm_gic_setup(); /* Enable and initialize the System level generic timer */ mmio_write_32(SYS_CNTCTL_BASE + CNTCR_OFF, CNTCR_FCREQ(0) | CNTCR_EN); diff --git a/plat/juno/juno_private.h b/plat/juno/juno_private.h index bb2548f..14d7af4 100644 --- a/plat/juno/juno_private.h +++ b/plat/juno/juno_private.h @@ -83,18 +83,7 @@ unsigned long plat_get_ns_image_entrypoint(void); unsigned long platform_get_stack(unsigned long mpidr); uint64_t plat_get_syscnt_freq(void); - -/* Declarations for plat_gic.c */ -uint32_t ic_get_pending_interrupt_id(void); -uint32_t ic_get_pending_interrupt_type(void); -uint32_t ic_acknowledge_interrupt(void); -uint32_t ic_get_interrupt_type(uint32_t id); -void ic_end_of_interrupt(uint32_t id); -void gic_cpuif_deactivate(unsigned int gicc_base); -void gic_cpuif_setup(unsigned int gicc_base); -void gic_pcpu_distif_setup(unsigned int gicd_base); -void gic_setup(void); -uint32_t plat_interrupt_type_to_line(uint32_t type, uint32_t security_state); +void plat_gic_init(void); /* Declarations for plat_topology.c */ int plat_setup_topology(void); diff --git a/plat/juno/plat_gic.c b/plat/juno/plat_gic.c deleted file mode 100644 index 9001519..0000000 --- a/plat/juno/plat_gic.c +++ /dev/null @@ -1,283 +0,0 @@ -/* - * Copyright (c) 2013-2014, ARM Limited and Contributors. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of ARM nor the names of its contributors may be used - * to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#include -#include -#include -#include -#include -#include -#include "juno_def.h" -#include "juno_private.h" - - -/* Value used to initialise Non-Secure irq priorities four at a time */ -#define DEFAULT_NS_PRIORITY_X4 \ - (GIC_HIGHEST_NS_PRIORITY | \ - (GIC_HIGHEST_NS_PRIORITY << 8) | \ - (GIC_HIGHEST_NS_PRIORITY << 16) | \ - (GIC_HIGHEST_NS_PRIORITY << 24)) - - -/******************************************************************************* - * 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. - ******************************************************************************/ -void gic_cpuif_setup(unsigned int gicc_base) -{ - unsigned int val; - - gicc_write_pmr(gicc_base, GIC_PRI_MASK); - - val = ENABLE_GRP0 | FIQ_EN; - val |= FIQ_BYP_DIS_GRP0 | IRQ_BYP_DIS_GRP0; - val |= FIQ_BYP_DIS_GRP1 | IRQ_BYP_DIS_GRP1; - gicc_write_ctlr(gicc_base, val); -} - -/******************************************************************************* - * Place the cpu interface in a state where it can never make a cpu exit wfi as - * as result of an asserted interrupt. This is critical for powering down a cpu - ******************************************************************************/ -void gic_cpuif_deactivate(unsigned int gicc_base) -{ - unsigned int val; - - /* Disable secure, non-secure interrupts and disable their bypass */ - val = gicc_read_ctlr(gicc_base); - val &= ~(ENABLE_GRP0 | ENABLE_GRP1); - val |= FIQ_BYP_DIS_GRP1 | FIQ_BYP_DIS_GRP0; - val |= IRQ_BYP_DIS_GRP0 | IRQ_BYP_DIS_GRP1; - gicc_write_ctlr(gicc_base, val); -} - -static void gic_set_secure(unsigned int gicd_base, unsigned id) -{ - /* Set interrupt as Group 0 */ - gicd_clr_igroupr(gicd_base, id); - - /* Set priority to max */ - gicd_set_ipriorityr(gicd_base, id, GIC_HIGHEST_SEC_PRIORITY); -} - -/******************************************************************************* - * Per cpu gic distributor setup which will be done by all cpus after a cold - * boot/hotplug. This marks out the secure interrupts & enables them. - ******************************************************************************/ -void gic_pcpu_distif_setup(unsigned int gicd_base) -{ - unsigned i; - - /* Mark all 32 PPI interrupts as Group 1 (non-secure) */ - mmio_write_32(gicd_base + GICD_IGROUPR, 0xffffffffu); - - /* Setup PPI priorities doing four at a time */ - for (i = 0; i < 32; i += 4) - mmio_write_32(gicd_base + GICD_IPRIORITYR + i, DEFAULT_NS_PRIORITY_X4); - - /* Configure those PPIs we want as secure, and enable them. */ - static const char sec_irq[] = { - IRQ_SEC_PHY_TIMER, - IRQ_SEC_SGI_0, - IRQ_SEC_SGI_1, - IRQ_SEC_SGI_2, - IRQ_SEC_SGI_3, - IRQ_SEC_SGI_4, - IRQ_SEC_SGI_5, - IRQ_SEC_SGI_6, - IRQ_SEC_SGI_7 - }; - for (i = 0; i < sizeof(sec_irq) / sizeof(sec_irq[0]); i++) { - gic_set_secure(gicd_base, sec_irq[i]); - gicd_set_isenabler(gicd_base, sec_irq[i]); - } -} - -/******************************************************************************* - * Global gic distributor setup which will be done by the primary cpu after a - * cold boot. It marks out the secure SPIs, PPIs & SGIs and enables them. It - * then enables the secure GIC distributor interface. - ******************************************************************************/ -static void gic_distif_setup(unsigned int gicd_base) -{ - unsigned int i, ctlr; - const unsigned int ITLinesNumber = - gicd_read_typer(gicd_base) & IT_LINES_NO_MASK; - - /* Disable the distributor before going further */ - ctlr = gicd_read_ctlr(gicd_base); - ctlr &= ~(ENABLE_GRP0 | ENABLE_GRP1); - gicd_write_ctlr(gicd_base, ctlr); - - /* Mark all lines of SPIs as Group 1 (non-secure) */ - for (i = 0; i < ITLinesNumber; i++) - mmio_write_32(gicd_base + GICD_IGROUPR + 4 + i * 4, 0xffffffffu); - - /* Setup SPI priorities doing four at a time */ - for (i = 0; i < ITLinesNumber * 32; i += 4) - mmio_write_32(gicd_base + GICD_IPRIORITYR + 32 + i, DEFAULT_NS_PRIORITY_X4); - - /* Configure the SPIs we want as secure */ - static const char sec_irq[] = { - IRQ_MHU, - IRQ_GPU_SMMU_0, - IRQ_GPU_SMMU_1, - IRQ_ETR_SMMU, - IRQ_TZC400, - IRQ_TZ_WDOG - }; - for (i = 0; i < sizeof(sec_irq) / sizeof(sec_irq[0]); i++) - gic_set_secure(gicd_base, sec_irq[i]); - - /* Route watchdog interrupt to this CPU and enable it. */ - gicd_set_itargetsr(gicd_base, IRQ_TZ_WDOG, - platform_get_core_pos(read_mpidr())); - gicd_set_isenabler(gicd_base, IRQ_TZ_WDOG); - - /* Now setup the PPIs */ - gic_pcpu_distif_setup(gicd_base); - - /* Enable Group 0 (secure) interrupts */ - gicd_write_ctlr(gicd_base, ctlr | ENABLE_GRP0); -} - -void gic_setup(void) -{ - gic_cpuif_setup(GICC_BASE); - gic_distif_setup(GICD_BASE); -} - -/******************************************************************************* - * An ARM processor signals interrupt exceptions through the IRQ and FIQ pins. - * The interrupt controller knows which pin/line it uses to signal a type of - * interrupt. The platform knows which interrupt controller type is being used - * in a particular security state e.g. with an ARM GIC, normal world could use - * the GICv2 features while the secure world could use GICv3 features and vice - * versa. - * This function is exported by the platform to let the interrupt management - * framework determine for a type of interrupt and security state, which line - * should be used in the SCR_EL3 to control its routing to EL3. The interrupt - * line is represented as the bit position of the IRQ or FIQ bit in the SCR_EL3. - ******************************************************************************/ -uint32_t plat_interrupt_type_to_line(uint32_t type, uint32_t security_state) -{ - assert(type == INTR_TYPE_S_EL1 || - type == INTR_TYPE_EL3 || - type == INTR_TYPE_NS); - - assert(sec_state_is_valid(security_state)); - - /* - * We ignore the security state parameter because Juno is GICv2 only - * so both normal and secure worlds are using ARM GICv2. - */ - return gicv2_interrupt_type_to_line(GICC_BASE, type); -} - -/******************************************************************************* - * This function returns the type of the highest priority pending interrupt at - * the GIC cpu interface. INTR_TYPE_INVAL is returned when there is no - * interrupt pending. - ******************************************************************************/ -uint32_t plat_ic_get_pending_interrupt_type(void) -{ - uint32_t id; - - id = gicc_read_hppir(GICC_BASE); - - /* Assume that all secure interrupts are S-EL1 interrupts */ - if (id < 1022) - return INTR_TYPE_S_EL1; - - if (id == GIC_SPURIOUS_INTERRUPT) - return INTR_TYPE_INVAL; - - return INTR_TYPE_NS; -} - -/******************************************************************************* - * This function returns the id of the highest priority pending interrupt at - * the GIC cpu interface. INTR_ID_UNAVAILABLE is returned when there is no - * interrupt pending. - ******************************************************************************/ -uint32_t plat_ic_get_pending_interrupt_id(void) -{ - uint32_t id; - - id = gicc_read_hppir(GICC_BASE); - - if (id < 1022) - return id; - - if (id == 1023) - return INTR_ID_UNAVAILABLE; - - /* - * Find out which non-secure interrupt it is under the assumption that - * the GICC_CTLR.AckCtl bit is 0. - */ - return gicc_read_ahppir(GICC_BASE); -} - -/******************************************************************************* - * This functions reads the GIC cpu interface Interrupt Acknowledge register - * to start handling the pending interrupt. It returns the contents of the IAR. - ******************************************************************************/ -uint32_t plat_ic_acknowledge_interrupt(void) -{ - return gicc_read_IAR(GICC_BASE); -} - -/******************************************************************************* - * This functions writes the GIC cpu interface End Of Interrupt register with - * the passed value to finish handling the active interrupt - ******************************************************************************/ -void plat_ic_end_of_interrupt(uint32_t id) -{ - gicc_write_EOIR(GICC_BASE, id); -} - -/******************************************************************************* - * This function returns the type of the interrupt id depending upon the group - * this interrupt has been configured under by the interrupt controller i.e. - * group0 or group1. - ******************************************************************************/ -uint32_t plat_ic_get_interrupt_type(uint32_t id) -{ - uint32_t group; - - group = gicd_get_igroupr(GICD_BASE, id); - - /* Assume that all secure interrupts are S-EL1 interrupts */ - if (group == GRP0) - return INTR_TYPE_S_EL1; - else - return INTR_TYPE_NS; -} diff --git a/plat/juno/plat_pm.c b/plat/juno/plat_pm.c index a3f6bdd..adf599f 100644 --- a/plat/juno/plat_pm.c +++ b/plat/juno/plat_pm.c @@ -30,6 +30,7 @@ #include #include +#include #include #include #include @@ -133,10 +134,10 @@ /* Enable the gic cpu interface */ - gic_cpuif_setup(GICC_BASE); + arm_gic_cpuif_setup(); /* Juno todo: Is this setup only needed after a cold boot? */ - gic_pcpu_distif_setup(GICD_BASE); + arm_gic_pcpu_distif_setup(); /* Clear the mailbox for this cpu. */ juno_program_mailbox(mpidr, 0); @@ -155,7 +156,7 @@ uint32_t cluster_state = scpi_power_on; /* Prevent interrupts from spuriously waking up this cpu */ - gic_cpuif_deactivate(GICC_BASE); + arm_gic_cpuif_deactivate(); /* Cluster is to be turned off, so disable coherency */ if (afflvl > MPIDR_AFFLVL0) { diff --git a/plat/juno/platform.mk b/plat/juno/platform.mk index 2800438..6ca219d 100644 --- a/plat/juno/platform.mk +++ b/plat/juno/platform.mk @@ -53,6 +53,7 @@ drivers/io/io_storage.c \ lib/aarch64/xlat_tables.c \ plat/common/aarch64/plat_common.c \ + plat/common/plat_gic.c \ plat/juno/plat_io_storage.c BL1_SOURCES += drivers/arm/cci400/cci400.c \ @@ -76,7 +77,9 @@ plat/juno/scpi.c BL31_SOURCES += drivers/arm/cci400/cci400.c \ + drivers/arm/gic/arm_gic.c \ drivers/arm/gic/gic_v2.c \ + drivers/arm/gic/gic_v3.c \ lib/cpus/aarch64/cortex_a53.S \ lib/cpus/aarch64/cortex_a57.S \ plat/common/aarch64/platform_mp_stack.S \ @@ -86,7 +89,6 @@ plat/juno/aarch64/juno_common.c \ plat/juno/plat_pm.c \ plat/juno/plat_topology.c \ - plat/juno/plat_gic.c \ plat/juno/scpi.c ifneq (${RESET_TO_BL31},0) diff --git a/plat/juno/tsp/tsp-juno.mk b/plat/juno/tsp/tsp-juno.mk index d0d29d7..4d56ea2 100644 --- a/plat/juno/tsp/tsp-juno.mk +++ b/plat/juno/tsp/tsp-juno.mk @@ -29,9 +29,9 @@ # # TSP source files specific to Juno platform -BL32_SOURCES += drivers/arm/gic/gic_v2.c \ +BL32_SOURCES += drivers/arm/gic/arm_gic.c \ + drivers/arm/gic/gic_v2.c \ plat/common/aarch64/platform_mp_stack.S \ plat/juno/aarch64/juno_common.c \ plat/juno/aarch64/plat_helpers.S \ - plat/juno/tsp/tsp_plat_setup.c \ - plat/juno/plat_gic.c + plat/juno/tsp/tsp_plat_setup.c diff --git a/plat/juno/tsp/tsp_plat_setup.c b/plat/juno/tsp/tsp_plat_setup.c index 2d4ab81..0a9d4cb 100644 --- a/plat/juno/tsp/tsp_plat_setup.c +++ b/plat/juno/tsp/tsp_plat_setup.c @@ -80,6 +80,7 @@ ******************************************************************************/ void tsp_platform_setup(void) { + plat_gic_init(); } /*******************************************************************************