blob: f91427cada65d5d6c4085e21f40747fbbee47e5e [file] [log] [blame]
/*
* Copyright (c) 2018, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <arch.h>
#include <arch_helpers.h>
#include <assert.h>
#include <drivers/arm/arm_gic.h>
#include <drivers/arm/gic_common.h>
#include <drivers/arm/gic_v2.h>
#include <mmio.h>
#include <platform.h>
/*
* Data structure to store the GIC per CPU context before entering
* system suspend. Only the GIC context of first 32 interrupts (SGIs and PPIs)
* will be saved. The GIC SPI context needs to be restored by the respective
* drivers. The GICC_PMR is not saved here as it will be reinitialized during
* GIC restore.
*/
struct gicv2_pcpu_ctx {
unsigned int gicc_ctlr;
unsigned int gicd_isenabler0;
unsigned int gicd_ipriorityr[NUM_PCPU_INTR >> IPRIORITYR_SHIFT];
unsigned int gicd_icfgr;
};
static struct gicv2_pcpu_ctx pcpu_gic_ctx[PLATFORM_CORE_COUNT];
static uintptr_t gicc_base_addr;
static uintptr_t gicd_base_addr;
static unsigned int gic_cpu_id[PLATFORM_CORE_COUNT] = {UINT32_MAX};
/* Helper function to convert core pos to gic id */
static unsigned int core_pos_to_gic_id(unsigned int core_pos)
{
assert(gic_cpu_id[core_pos] != UINT32_MAX);
return gic_cpu_id[core_pos];
}
/*******************************************************************************
* GIC Distributor interface accessors for reading entire registers
******************************************************************************/
uint8_t gicd_read_itargetsr_byte(unsigned int base, unsigned int interrupt_id)
{
return mmio_read_8(base + GICD_ITARGETSR + interrupt_id);
}
unsigned int gicd_read_itargetsr(unsigned int base, unsigned int interrupt_id)
{
unsigned n = interrupt_id >> ITARGETSR_SHIFT;
return mmio_read_32(base + GICD_ITARGETSR + (n << 2));
}
unsigned int gicd_read_cpendsgir(unsigned int base, unsigned int interrupt_id)
{
unsigned n = interrupt_id >> CPENDSGIR_SHIFT;
return mmio_read_32(base + GICD_CPENDSGIR + (n << 2));
}
unsigned int gicd_read_spendsgir(unsigned int base, unsigned int interrupt_id)
{
unsigned n = interrupt_id >> SPENDSGIR_SHIFT;
return mmio_read_32(base + GICD_SPENDSGIR + (n << 2));
}
/*******************************************************************************
* GIC Distributor interface accessors for writing entire registers
******************************************************************************/
void gicd_write_itargetsr(unsigned int base,
unsigned int interrupt_id, unsigned int val)
{
unsigned n = interrupt_id >> ITARGETSR_SHIFT;
mmio_write_32(base + GICD_ITARGETSR + (n << 2), val);
}
void gicd_write_itargetsr_byte(unsigned int base,
unsigned int interrupt_id, unsigned int val)
{
mmio_write_8(base + GICD_ITARGETSR + interrupt_id, val);
}
void gicd_write_cpendsgir(unsigned int base,
unsigned int interrupt_id, unsigned int val)
{
unsigned n = interrupt_id >> CPENDSGIR_SHIFT;
mmio_write_32(base + GICD_CPENDSGIR + (n << 2), val);
}
void gicd_write_spendsgir(unsigned int base,
unsigned int interrupt_id, unsigned int val)
{
unsigned n = interrupt_id >> SPENDSGIR_SHIFT;
mmio_write_32(base + GICD_SPENDSGIR + (n << 2), val);
}
/*******************************************************************************
* GIC Distributor interface accessors for individual interrupt manipulation
******************************************************************************/
void gicd_set_itargetsr(unsigned int base,
unsigned int interrupt_id, unsigned int iface)
{
mmio_write_8(base + GICD_ITARGETSR + interrupt_id, (1 << iface));
}
/******************************************************************************
* GICv2 public driver API
*****************************************************************************/
void gicv2_enable_cpuif(void)
{
unsigned int gicc_ctlr;
assert(gicc_base_addr);
/* Enable the GICC and disable bypass */
gicc_ctlr = GICC_CTLR_ENABLE | FIQ_BYP_DIS_GRP1
| IRQ_BYP_DIS_GRP1;
gicc_write_ctlr(gicc_base_addr, gicc_ctlr);
}
void gicv2_probe_gic_cpu_id(void)
{
unsigned int gicd_itargets_val, core_pos;
assert(gicd_base_addr);
core_pos = platform_get_core_pos(read_mpidr_el1());
gicd_itargets_val = gicd_read_itargetsr(gicd_base_addr, 0);
/* In a uniprocessor implementation the GICD_ITARGETSRs are RAZ/WI. */
if (PLATFORM_CORE_COUNT > 1)
assert(gicd_itargets_val);
/* Convert the bit pos returned by read of ITARGETSR0 to GIC CPU ID */
gic_cpu_id[core_pos] = __builtin_ctz(gicd_itargets_val);
}
void gicv2_setup_cpuif(void)
{
assert(gicc_base_addr);
/* Set the priority mask register to allow all interrupts to trickle in */
gicc_write_pmr(gicc_base_addr, GIC_PRI_MASK);
gicv2_enable_cpuif();
}
void gicv2_disable_cpuif(void)
{
unsigned int gicc_ctlr;
assert(gicc_base_addr);
/* Disable non-secure interrupts and disable their bypass */
gicc_ctlr = gicc_read_ctlr(gicc_base_addr);
gicc_ctlr &= ~GICC_CTLR_ENABLE;
gicc_ctlr |= FIQ_BYP_DIS_GRP1 | IRQ_BYP_DIS_GRP1;
gicc_write_ctlr(gicc_base_addr, gicc_ctlr);
}
void gicv2_save_cpuif_context(void)
{
unsigned int core_pos = platform_get_core_pos(read_mpidr_el1());
assert(gicc_base_addr);
pcpu_gic_ctx[core_pos].gicc_ctlr =
gicc_read_ctlr(gicc_base_addr);
}
void gicv2_restore_cpuif_context(void)
{
unsigned int core_pos = platform_get_core_pos(read_mpidr_el1());
assert(gicc_base_addr);
/* The GICC_PMR is never modified, hence we initialize this register */
gicc_write_pmr(gicc_base_addr, GIC_PRI_MASK);
gicc_write_ctlr(gicc_base_addr,
pcpu_gic_ctx[core_pos].gicc_ctlr);
}
void gicv2_setup_distif(void)
{
unsigned int gicd_ctlr;
assert(gicd_base_addr);
/* Enable the forwarding of interrupts to CPU interface */
gicd_ctlr = gicd_read_ctlr(gicd_base_addr);
gicd_ctlr |= GICD_CTLR_ENABLE;
gicd_write_ctlr(gicd_base_addr, gicd_ctlr);
}
/* Save the per-cpu GICD ISENABLER, IPRIORITYR and ICFGR registers */
void gicv2_save_sgi_ppi_context(void)
{
unsigned int i;
unsigned int core_pos = platform_get_core_pos(read_mpidr_el1());
assert(gicd_base_addr);
pcpu_gic_ctx[core_pos].gicd_isenabler0 =
gicd_read_isenabler(gicd_base_addr, 0);
/* Read the ipriority registers, 4 at a time */
for (i = 0; i < (NUM_PCPU_INTR >> IPRIORITYR_SHIFT); i++)
pcpu_gic_ctx[core_pos].gicd_ipriorityr[i] =
gicd_read_ipriorityr(gicd_base_addr, i << IPRIORITYR_SHIFT);
pcpu_gic_ctx[core_pos].gicd_icfgr =
gicd_read_icfgr(gicd_base_addr, MIN_PPI_ID);
}
/* Restore the per-cpu GICD ISENABLER, IPRIORITYR and ICFGR registers */
void gicv2_restore_sgi_ppi_context(void)
{
unsigned int i;
unsigned int core_pos = platform_get_core_pos(read_mpidr_el1());
assert(gicd_base_addr);
/* Write the ipriority registers, 4 at a time */
for (i = 0; i < (NUM_PCPU_INTR >> IPRIORITYR_SHIFT); i++)
gicd_write_ipriorityr(gicd_base_addr, i << IPRIORITYR_SHIFT,
pcpu_gic_ctx[core_pos].gicd_ipriorityr[i]);
gicd_write_icfgr(gicd_base_addr, MIN_PPI_ID,
pcpu_gic_ctx[core_pos].gicd_icfgr);
gicd_write_isenabler(gicd_base_addr, 0,
pcpu_gic_ctx[core_pos].gicd_isenabler0);
}
unsigned int gicv2_gicd_get_ipriorityr(unsigned int interrupt_id)
{
assert(gicd_base_addr);
assert(IS_VALID_INTR_ID(interrupt_id));
return gicd_get_ipriorityr(gicd_base_addr, interrupt_id);
}
void gicv2_gicd_set_ipriorityr(unsigned int interrupt_id,
unsigned int priority)
{
assert(gicd_base_addr);
assert(IS_VALID_INTR_ID(interrupt_id));
gicd_set_ipriorityr(gicd_base_addr, interrupt_id, priority);
}
void gicv2_send_sgi(unsigned int sgi_id, unsigned int core_pos)
{
unsigned int sgir_val;
assert(gicd_base_addr);
assert(IS_SGI(sgi_id));
sgir_val = sgi_id << GICD_SGIR_INTID_SHIFT;
sgir_val |= (1 << core_pos_to_gic_id(core_pos)) << GICD_SGIR_CPUTL_SHIFT;
gicd_write_sgir(gicd_base_addr, sgir_val);
}
void gicv2_set_itargetsr(unsigned int num, unsigned int core_pos)
{
unsigned int gic_cpu_id;
assert(gicd_base_addr);
assert(IS_SPI(num));
gic_cpu_id = core_pos_to_gic_id(core_pos);
gicd_set_itargetsr(gicd_base_addr, num, gic_cpu_id);
}
uint8_t gicv2_read_itargetsr_value(unsigned int num)
{
return gicd_read_itargetsr_byte(gicd_base_addr, num);
}
void gicv2_set_itargetsr_value(unsigned int num, unsigned int val)
{
assert(gicd_base_addr);
assert(IS_SPI(num));
gicd_write_itargetsr_byte(gicd_base_addr, num, val);
assert(gicv2_read_itargetsr_value(num) == val);
}
unsigned int gicv2_gicd_get_isenabler(unsigned int num)
{
assert(gicd_base_addr);
assert(IS_VALID_INTR_ID(num));
return gicd_get_isenabler(gicd_base_addr, num);
}
void gicv2_gicd_set_isenabler(unsigned int num)
{
assert(gicd_base_addr);
assert(IS_VALID_INTR_ID(num));
gicd_set_isenabler(gicd_base_addr, num);
}
void gicv2_gicd_set_icenabler(unsigned int num)
{
assert(gicd_base_addr);
assert(IS_VALID_INTR_ID(num));
gicd_set_icenabler(gicd_base_addr, num);
}
unsigned int gicv2_gicc_read_iar(void)
{
assert(gicc_base_addr);
return gicc_read_iar(gicc_base_addr);
}
unsigned int gicv2_gicd_get_ispendr(unsigned int interrupt_id)
{
unsigned int ispendr;
unsigned int bit_pos;
assert(gicd_base_addr);
assert(IS_VALID_INTR_ID(interrupt_id));
ispendr = gicd_read_ispendr(gicd_base_addr, interrupt_id);
bit_pos = interrupt_id % (1 << ISPENDR_SHIFT);
return !!(ispendr & (1 << bit_pos));
}
void gicv2_gicd_set_ispendr(unsigned int interrupt_id)
{
assert(gicd_base_addr);
assert(IS_PPI(interrupt_id) || IS_SPI(interrupt_id));
gicd_set_ispendr(gicd_base_addr, interrupt_id);
}
void gicv2_gicd_set_icpendr(unsigned int interrupt_id)
{
assert(gicd_base_addr);
assert(IS_PPI(interrupt_id) || IS_SPI(interrupt_id));
gicd_set_icpendr(gicd_base_addr, interrupt_id);
}
void gicv2_gicc_write_eoir(unsigned int val)
{
assert(gicc_base_addr);
gicc_write_eoir(gicc_base_addr, val);
}
void gicv2_init(uintptr_t gicc_base,
uintptr_t gicd_base)
{
assert(gicc_base);
assert(gicd_base);
/* Assert that this is a GICv2 system */
assert(!is_gicv3_mode());
gicc_base_addr = gicc_base;
gicd_base_addr = gicd_base;
}