From 464ce2bbaa95a95f773a639c47e14bdffa21d75a Mon Sep 17 00:00:00 2001 From: Soby Mathew Date: Wed, 7 Oct 2015 09:45:27 +0100 Subject: [PATCH] Add ARM GICv2 driver This patch adds a driver for ARM GICv2 systems, example GIC-400. Unlike the existing GIC driver in `include/drivers/arm/arm_gic.h`, this driver is optimised for GICv2 and does not support GICv3 systems in GICv2 compatibility mode. The driver interface has been implemented in `drivers/arm/gic/v2/gicv2_main.c`. The corresponding header is in `include/drivers/arm/gicv2.h`. Helper functions are implemented in `drivers/arm/gic/v2/gicv2_helpers.c` and are accessible through the `drivers/arm/gic/v2/gicv2_private.h` header. Change-Id: I09fffa4e621fb99ba3c01204839894816cd89a2a --- drivers/arm/gic/v2/gicv2_helpers.c | 230 ++++++++++++++++++++++++++ drivers/arm/gic/v2/gicv2_main.c | 254 +++++++++++++++++++++++++++++ drivers/arm/gic/v2/gicv2_private.h | 147 +++++++++++++++++ include/drivers/arm/gicv2.h | 165 +++++++++++++++++++ 4 files changed, 796 insertions(+) create mode 100644 drivers/arm/gic/v2/gicv2_helpers.c create mode 100644 drivers/arm/gic/v2/gicv2_main.c create mode 100644 drivers/arm/gic/v2/gicv2_private.h create mode 100644 include/drivers/arm/gicv2.h diff --git a/drivers/arm/gic/v2/gicv2_helpers.c b/drivers/arm/gic/v2/gicv2_helpers.c new file mode 100644 index 00000000..1f904c51 --- /dev/null +++ b/drivers/arm/gic/v2/gicv2_helpers.c @@ -0,0 +1,230 @@ +/* + * Copyright (c) 2015, 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 "gicv2_private.h" + +/* + * Accessor to read the GIC Distributor ITARGETSR corresponding to the + * interrupt `id`, 4 interrupt IDs at a time. + */ +unsigned int gicd_read_itargetsr(uintptr_t base, unsigned int id) +{ + unsigned n = id >> ITARGETSR_SHIFT; + return mmio_read_32(base + GICD_ITARGETSR + (n << 2)); +} + +/* + * Accessor to read the GIC Distributor CPENDSGIR corresponding to the + * interrupt `id`, 4 interrupt IDs at a time. + */ +unsigned int gicd_read_cpendsgir(uintptr_t base, unsigned int id) +{ + unsigned n = id >> CPENDSGIR_SHIFT; + return mmio_read_32(base + GICD_CPENDSGIR + (n << 2)); +} + +/* + * Accessor to read the GIC Distributor SPENDSGIR corresponding to the + * interrupt `id`, 4 interrupt IDs at a time. + */ +unsigned int gicd_read_spendsgir(uintptr_t base, unsigned int id) +{ + unsigned n = id >> SPENDSGIR_SHIFT; + return mmio_read_32(base + GICD_SPENDSGIR + (n << 2)); +} + +/* + * Accessor to write the GIC Distributor ITARGETSR corresponding to the + * interrupt `id`, 4 interrupt IDs at a time. + */ +void gicd_write_itargetsr(uintptr_t base, unsigned int id, unsigned int val) +{ + unsigned n = id >> ITARGETSR_SHIFT; + mmio_write_32(base + GICD_ITARGETSR + (n << 2), val); +} + +/* + * Accessor to write the GIC Distributor CPENDSGIR corresponding to the + * interrupt `id`, 4 interrupt IDs at a time. + */ +void gicd_write_cpendsgir(uintptr_t base, unsigned int id, unsigned int val) +{ + unsigned n = id >> CPENDSGIR_SHIFT; + mmio_write_32(base + GICD_CPENDSGIR + (n << 2), val); +} + +/* + * Accessor to write the GIC Distributor SPENDSGIR corresponding to the + * interrupt `id`, 4 interrupt IDs at a time. + */ +void gicd_write_spendsgir(uintptr_t base, unsigned int id, unsigned int val) +{ + unsigned n = id >> SPENDSGIR_SHIFT; + 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) +{ + unsigned byte_off = id & ((1 << ITARGETSR_SHIFT) - 1); + unsigned int reg_val = gicd_read_itargetsr(base, id); + + gicd_write_itargetsr(base, id, reg_val | (target << (byte_off << 3))); +} + +/******************************************************************************* + * Get the current CPU bit mask from GICD_ITARGETSR0 + ******************************************************************************/ +unsigned int gicv2_get_cpuif_id(uintptr_t base) +{ + unsigned int val; + + val = gicd_read_itargetsr(base, 0); + return val & GIC_TARGET_CPU_MASK; +} + +/******************************************************************************* + * Helper function to configure the default attributes of SPIs. + ******************************************************************************/ +void gicv2_spis_configure_defaults(uintptr_t gicd_base) +{ + unsigned int index, num_ints; + + num_ints = gicd_read_typer(gicd_base); + num_ints &= TYPER_IT_LINES_NO_MASK; + num_ints = (num_ints + 1) << 5; + + /* + * Treat all SPIs as G1NS by default. The number of interrupts is + * calculated as 32 * (IT_LINES + 1). We do 32 at a time. + */ + for (index = MIN_SPI_ID; index < num_ints; index += 32) + gicd_write_igroupr(gicd_base, index, ~0U); + + /* Setup the default SPI priorities doing four at a time */ + for (index = MIN_SPI_ID; index < num_ints; index += 4) + gicd_write_ipriorityr(gicd_base, + index, + GICD_IPRIORITYR_DEF_VAL); + + /* Treat all SPIs as level triggered by default, 16 at a time */ + for (index = MIN_SPI_ID; index < num_ints; index += 16) + gicd_write_icfgr(gicd_base, index, 0); +} + +/******************************************************************************* + * Helper function to configure secure G0 SPIs. + ******************************************************************************/ +void gicv2_secure_spis_configure(uintptr_t gicd_base, + unsigned int num_ints, + const unsigned int *sec_intr_list) +{ + unsigned int index, irq_num; + + /* If `num_ints` is not 0, ensure that `sec_intr_list` is not NULL */ + assert(num_ints ? (uintptr_t)sec_intr_list : 1); + + for (index = 0; index < num_ints; index++) { + irq_num = sec_intr_list[index]; + if (irq_num >= MIN_SPI_ID) { + /* Configure this interrupt as a secure interrupt */ + gicd_clr_igroupr(gicd_base, irq_num); + + /* Set the priority of this interrupt */ + gicd_write_ipriorityr(gicd_base, + irq_num, + GIC_HIGHEST_SEC_PRIORITY); + + /* Target the secure interrupts to primary CPU */ + gicd_set_itargetsr(gicd_base, irq_num, + gicv2_get_cpuif_id(gicd_base)); + + /* Enable this interrupt */ + gicd_set_isenabler(gicd_base, irq_num); + } + } + +} + +/******************************************************************************* + * Helper function to configure secure G0 SGIs and PPIs. + ******************************************************************************/ +void gicv2_secure_ppi_sgi_setup(uintptr_t gicd_base, + unsigned int num_ints, + const unsigned int *sec_intr_list) +{ + unsigned int index, irq_num, sec_ppi_sgi_mask = 0; + + /* If `num_ints` is not 0, ensure that `sec_intr_list` is not NULL */ + assert(num_ints ? (uintptr_t)sec_intr_list : 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 (index = 0; index < MIN_SPI_ID; index += 4) + gicd_write_ipriorityr(gicd_base, + index, + GICD_IPRIORITYR_DEF_VAL); + + for (index = 0; index < num_ints; index++) { + irq_num = sec_intr_list[index]; + if (irq_num < MIN_SPI_ID) { + /* We have an SGI or a PPI. They are Group0 at reset */ + sec_ppi_sgi_mask |= 1U << irq_num; + + /* Set the priority of this interrupt */ + gicd_write_ipriorityr(gicd_base, + irq_num, + GIC_HIGHEST_SEC_PRIORITY); + } + } + + /* + * 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); +} diff --git a/drivers/arm/gic/v2/gicv2_main.c b/drivers/arm/gic/v2/gicv2_main.c new file mode 100644 index 00000000..cf939261 --- /dev/null +++ b/drivers/arm/gic/v2/gicv2_main.c @@ -0,0 +1,254 @@ +/* + * Copyright (c) 2015, 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 "gicv2_private.h" + +static const gicv2_driver_data_t *driver_data; + +/******************************************************************************* + * 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 gicv2_cpuif_enable(void) +{ + unsigned int val; + + assert(driver_data); + assert(driver_data->gicc_base); + + /* + * Enable the Group 0 interrupts, FIQEn and disable Group 0/1 + * bypass. + */ + val = CTLR_ENABLE_G0_BIT | FIQ_EN_BIT | FIQ_BYP_DIS_GRP0; + val |= IRQ_BYP_DIS_GRP0 | FIQ_BYP_DIS_GRP1 | IRQ_BYP_DIS_GRP1; + + /* Program the idle priority in the PMR */ + gicc_write_pmr(driver_data->gicc_base, GIC_PRI_MASK); + gicc_write_ctlr(driver_data->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 gicv2_cpuif_disable(void) +{ + unsigned int val; + + assert(driver_data); + assert(driver_data->gicc_base); + + /* Disable secure, non-secure interrupts and disable their bypass */ + val = gicc_read_ctlr(driver_data->gicc_base); + val &= ~(CTLR_ENABLE_G0_BIT | CTLR_ENABLE_G1_BIT); + val |= FIQ_BYP_DIS_GRP1 | FIQ_BYP_DIS_GRP0; + val |= IRQ_BYP_DIS_GRP0 | IRQ_BYP_DIS_GRP1; + gicc_write_ctlr(driver_data->gicc_base, val); +} + +/******************************************************************************* + * Per cpu gic distributor setup which will be done by all cpus after a cold + * boot/hotplug. This marks out the secure SPIs and PPIs & enables them. + ******************************************************************************/ +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); +} + +/******************************************************************************* + * Global gic distributor init 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. + ******************************************************************************/ +void gicv2_distif_init(void) +{ + unsigned int ctlr; + + 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); + gicd_write_ctlr(driver_data->gicd_base, + ctlr & ~(CTLR_ENABLE_G0_BIT | CTLR_ENABLE_G1_BIT)); + + /* 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); + + /* Re-enable the secure SPIs now that they have been configured */ + gicd_write_ctlr(driver_data->gicd_base, ctlr | CTLR_ENABLE_G0_BIT); +} + +/******************************************************************************* + * Initialize the ARM GICv2 driver with the provided platform inputs + ******************************************************************************/ +void gicv2_driver_init(const gicv2_driver_data_t *plat_driver_data) +{ + unsigned int gic_version; + assert(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); + + /* Ensure that this is a GICv2 system */ + gic_version = gicd_read_pidr2(plat_driver_data->gicd_base); + gic_version = (gic_version >> PIDR2_ARCH_REV_SHIFT) + & PIDR2_ARCH_REV_MASK; + assert(gic_version == ARCH_REV_GICV2); + + driver_data = plat_driver_data; + + INFO("ARM GICv2 driver initialized\n"); +} + +/****************************************************************************** + * This function returns whether FIQ is enabled in the GIC CPU interface. + *****************************************************************************/ +unsigned int gicv2_is_fiq_enabled(void) +{ + unsigned int gicc_ctlr; + + assert(driver_data); + assert(driver_data->gicc_base); + + gicc_ctlr = gicc_read_ctlr(driver_data->gicc_base); + return (gicc_ctlr >> FIQ_EN_SHIFT) & 0x1; +} + +/******************************************************************************* + * This function returns the type of the highest priority pending interrupt at + * the GIC cpu interface. The return values can be one of the following : + * PENDING_G1_INTID : The interrupt type is non secure Group 1. + * 0 - 1019 : The interrupt type is secure Group 0. + * GIC_SPURIOUS_INTERRUPT : there is no pending interrupt with + * sufficient priority to be signaled + ******************************************************************************/ +unsigned int gicv2_get_pending_interrupt_type(void) +{ + assert(driver_data); + assert(driver_data->gicc_base); + + return gicc_read_hppir(driver_data->gicc_base) & INT_ID_MASK; +} + +/******************************************************************************* + * This function returns the id of the highest priority pending interrupt at + * the GIC cpu interface. GIC_SPURIOUS_INTERRUPT is returned when there is no + * interrupt pending. + ******************************************************************************/ +unsigned int gicv2_get_pending_interrupt_id(void) +{ + unsigned int id; + + assert(driver_data); + assert(driver_data->gicc_base); + + id = gicc_read_hppir(driver_data->gicc_base) & INT_ID_MASK; + + /* + * Find out which non-secure interrupt it is under the assumption that + * the GICC_CTLR.AckCtl bit is 0. + */ + if (id == PENDING_G1_INTID) + id = gicc_read_ahppir(driver_data->gicc_base) & INT_ID_MASK; + + return id; +} + +/******************************************************************************* + * This functions reads the GIC cpu interface Interrupt Acknowledge register + * to start handling the pending secure 0 interrupt. It returns the + * contents of the IAR. + ******************************************************************************/ +unsigned int gicv2_acknowledge_interrupt(void) +{ + assert(driver_data); + assert(driver_data->gicc_base); + + return gicc_read_IAR(driver_data->gicc_base); +} + +/******************************************************************************* + * This functions writes the GIC cpu interface End Of Interrupt register with + * the passed value to finish handling the active secure group 0 interrupt. + ******************************************************************************/ +void gicv2_end_of_interrupt(unsigned int id) +{ + assert(driver_data); + assert(driver_data->gicc_base); + + gicc_write_EOIR(driver_data->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 secure or group1 non secure. It returns zero for Group 0 secure and + * one for Group 1 non secure interrupt. + ******************************************************************************/ +unsigned int gicv2_get_interrupt_group(unsigned int id) +{ + assert(driver_data); + assert(driver_data->gicd_base); + + return gicd_get_igroupr(driver_data->gicd_base, id); +} diff --git a/drivers/arm/gic/v2/gicv2_private.h b/drivers/arm/gic/v2/gicv2_private.h new file mode 100644 index 00000000..6a1ec82b --- /dev/null +++ b/drivers/arm/gic/v2/gicv2_private.h @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2015, 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. + */ + +#ifndef __GICV2_PRIVATE_H__ +#define __GICV2_PRIVATE_H__ + +#include +#include +#include + +/******************************************************************************* + * Private function prototypes + ******************************************************************************/ +void gicv2_spis_configure_defaults(uintptr_t gicd_base); +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); +unsigned int gicv2_get_cpuif_id(uintptr_t base); + +/******************************************************************************* + * GIC Distributor interface accessors for reading entire registers + ******************************************************************************/ +static inline unsigned int gicd_read_pidr2(uintptr_t base) +{ + return mmio_read_32(base + GICD_PIDR2_GICV2); +} + +/******************************************************************************* + * GIC CPU interface accessors for reading entire registers + ******************************************************************************/ + +static inline unsigned int gicc_read_ctlr(uintptr_t base) +{ + return mmio_read_32(base + GICC_CTLR); +} + +static inline unsigned int gicc_read_pmr(uintptr_t base) +{ + return mmio_read_32(base + GICC_PMR); +} + +static inline unsigned int gicc_read_BPR(uintptr_t base) +{ + return mmio_read_32(base + GICC_BPR); +} + +static inline unsigned int gicc_read_IAR(uintptr_t base) +{ + return mmio_read_32(base + GICC_IAR); +} + +static inline unsigned int gicc_read_EOIR(uintptr_t base) +{ + return mmio_read_32(base + GICC_EOIR); +} + +static inline unsigned int gicc_read_hppir(uintptr_t base) +{ + return mmio_read_32(base + GICC_HPPIR); +} + +static inline unsigned int gicc_read_ahppir(uintptr_t base) +{ + return mmio_read_32(base + GICC_AHPPIR); +} + +static inline unsigned int gicc_read_dir(uintptr_t base) +{ + return mmio_read_32(base + GICC_DIR); +} + +static inline unsigned int gicc_read_iidr(uintptr_t base) +{ + return mmio_read_32(base + GICC_IIDR); +} + +/******************************************************************************* + * GIC CPU interface accessors for writing entire registers + ******************************************************************************/ + +static inline void gicc_write_ctlr(uintptr_t base, unsigned int val) +{ + mmio_write_32(base + GICC_CTLR, val); +} + +static inline void gicc_write_pmr(uintptr_t base, unsigned int val) +{ + mmio_write_32(base + GICC_PMR, val); +} + +static inline void gicc_write_BPR(uintptr_t base, unsigned int val) +{ + mmio_write_32(base + GICC_BPR, val); +} + + +static inline void gicc_write_IAR(uintptr_t base, unsigned int val) +{ + mmio_write_32(base + GICC_IAR, val); +} + +static inline void gicc_write_EOIR(uintptr_t base, unsigned int val) +{ + mmio_write_32(base + GICC_EOIR, val); +} + +static inline void gicc_write_hppir(uintptr_t base, unsigned int val) +{ + mmio_write_32(base + GICC_HPPIR, val); +} + +static inline void gicc_write_dir(uintptr_t base, unsigned int val) +{ + mmio_write_32(base + GICC_DIR, val); +} + +#endif /* __GICV2_PRIVATE_H__ */ diff --git a/include/drivers/arm/gicv2.h b/include/drivers/arm/gicv2.h new file mode 100644 index 00000000..88dc0153 --- /dev/null +++ b/include/drivers/arm/gicv2.h @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2015, 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. + */ + +#ifndef __GICV2_H__ +#define __GICV2_H__ + +/******************************************************************************* + * GICv2 miscellaneous definitions + ******************************************************************************/ +/* Interrupt IDs reported by the HPPIR and IAR registers */ +#define PENDING_G1_INTID 1022 + +/******************************************************************************* + * GICv2 specific Distributor interface register offsets and constants. + ******************************************************************************/ +#define GICD_ITARGETSR 0x800 +#define GICD_SGIR 0xF00 +#define GICD_CPENDSGIR 0xF10 +#define GICD_SPENDSGIR 0xF20 +#define GICD_PIDR2_GICV2 0xFE8 + +#define ITARGETSR_SHIFT 2 +#define GIC_TARGET_CPU_MASK 0xff + +#define CPENDSGIR_SHIFT 2 +#define SPENDSGIR_SHIFT CPENDSGIR_SHIFT + +/******************************************************************************* + * GICv2 specific CPU interface register offsets and constants. + ******************************************************************************/ +/* Physical CPU Interface registers */ +#define GICC_CTLR 0x0 +#define GICC_PMR 0x4 +#define GICC_BPR 0x8 +#define GICC_IAR 0xC +#define GICC_EOIR 0x10 +#define GICC_RPR 0x14 +#define GICC_HPPIR 0x18 +#define GICC_AHPPIR 0x28 +#define GICC_IIDR 0xFC +#define GICC_DIR 0x1000 +#define GICC_PRIODROP GICC_EOIR + +/* GICC_CTLR bit definitions */ +#define EOI_MODE_NS (1 << 10) +#define EOI_MODE_S (1 << 9) +#define IRQ_BYP_DIS_GRP1 (1 << 8) +#define FIQ_BYP_DIS_GRP1 (1 << 7) +#define IRQ_BYP_DIS_GRP0 (1 << 6) +#define FIQ_BYP_DIS_GRP0 (1 << 5) +#define CBPR (1 << 4) +#define FIQ_EN_SHIFT 3 +#define FIQ_EN_BIT (1 << FIQ_EN_SHIFT) +#define ACK_CTL (1 << 2) + +/* GICC_IIDR bit masks and shifts */ +#define GICC_IIDR_PID_SHIFT 20 +#define GICC_IIDR_ARCH_SHIFT 16 +#define GICC_IIDR_REV_SHIFT 12 +#define GICC_IIDR_IMP_SHIFT 0 + +#define GICC_IIDR_PID_MASK 0xfff +#define GICC_IIDR_ARCH_MASK 0xf +#define GICC_IIDR_REV_MASK 0xf +#define GICC_IIDR_IMP_MASK 0xfff + +/* HYP view virtual CPU Interface registers */ +#define GICH_CTL 0x0 +#define GICH_VTR 0x4 +#define GICH_ELRSR0 0x30 +#define GICH_ELRSR1 0x34 +#define GICH_APR0 0xF0 +#define GICH_LR_BASE 0x100 + +/* Virtual CPU Interface registers */ +#define GICV_CTL 0x0 +#define GICV_PRIMASK 0x4 +#define GICV_BP 0x8 +#define GICV_INTACK 0xC +#define GICV_EOI 0x10 +#define GICV_RUNNINGPRI 0x14 +#define GICV_HIGHESTPEND 0x18 +#define GICV_DEACTIVATE 0x1000 + +/* GICD_CTLR bit definitions */ +#define CTLR_ENABLE_G1_SHIFT 1 +#define CTLR_ENABLE_G1_MASK 0x1 +#define CTLR_ENABLE_G1_BIT (1 << CTLR_ENABLE_G1_SHIFT) + +/* Interrupt ID mask for HPPIR, AHPPIR, IAR and AIAR CPU Interface registers */ +#define INT_ID_MASK 0x3ff + +#ifndef __ASSEMBLY__ + +#include + +/******************************************************************************* + * This structure describes some of the implementation defined attributes of + * the GICv2 IP. It is used by the platform port to specify these attributes + * 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. + * + * 2. The 'gicc_base' field contains the base address of the CPU Interface + * programmer's view. + * + * 3. The 'g0_interrupt_array' field is a pointer to an array in which each + * entry corresponds to an ID of a Group 0 interrupt. + * + * 4. The 'g0_interrupt_num' field contains the number of entries in the + * 'g0_interrupt_array'. + ******************************************************************************/ +typedef struct gicv2_driver_data { + uintptr_t gicd_base; + uintptr_t gicc_base; + unsigned int g0_interrupt_num; + const unsigned int *g0_interrupt_array; +} gicv2_driver_data_t; + +/******************************************************************************* + * Function prototypes + ******************************************************************************/ +void gicv2_driver_init(const gicv2_driver_data_t *plat_driver_data); +void gicv2_distif_init(void); +void gicv2_pcpu_distif_init(void); +void gicv2_cpuif_enable(void); +void gicv2_cpuif_disable(void); +unsigned int gicv2_is_fiq_enabled(void); +unsigned int gicv2_get_pending_interrupt_type(void); +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); + +#endif /* __ASSEMBLY__ */ +#endif /* __GICV2_H__ */ -- 2.30.2