Merge pull request #961 from jeenu-arm/gic-600

Introduce ARM GIC-600 driver
This commit is contained in:
danh-arm 2017-06-05 14:41:31 +01:00 committed by GitHub
commit c66f4adee3
5 changed files with 183 additions and 56 deletions

View File

@ -638,6 +638,7 @@ map is explained in the [Firmware Design].
if `FVP_CLUSTER_COUNT` > 2.
* `FVP_USE_GIC_DRIVER` : Selects the GIC driver to be built. Options:
- `FVP_GIC600` : The GIC600 implementation of GICv3 is selected
- `FVP_GICV2` : The GICv2 only driver is selected
- `FVP_GICV3` : The GICv3 only driver is selected (default option)
- `FVP_GICV3_LEGACY`: The Legacy GICv3 driver is selected (deprecated)

119
drivers/arm/gic/v3/gic600.c Normal file
View File

@ -0,0 +1,119 @@
/*
* Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* Driver for GIC600-specific features. This driver only overrides APIs that are
* different to those generic ones in GICv3 driver.
*
* GIC600 supports independently power-gating redistributor interface.
*/
#include <arch_helpers.h>
#include <assert.h>
#include <gicv3.h>
#include "gicv3_private.h"
/* GIC600-specific register offsets */
#define GICR_PWRR 0x24
/* GICR_PWRR fields */
#define PWRR_RDPD_SHIFT 0
#define PWRR_RDGPD_SHIFT 2
#define PWRR_RDGPO_SHIFT 3
#define PWRR_RDGPD (1 << PWRR_RDGPD_SHIFT)
#define PWRR_RDGPO (1 << PWRR_RDGPO_SHIFT)
/* Values to write to GICR_PWRR register to power redistributor */
#define PWRR_ON (0 << PWRR_RDPD_SHIFT)
#define PWRR_OFF (1 << PWRR_RDPD_SHIFT)
/* Generic GICv3 resources */
extern const gicv3_driver_data_t *gicv3_driver_data;
/* GIC600-specific accessor functions */
static void gicr_write_pwrr(uintptr_t base, unsigned int val)
{
mmio_write_32(base + GICR_PWRR, val);
}
static uint32_t gicr_read_pwrr(uintptr_t base)
{
return mmio_read_32(base + GICR_PWRR);
}
static int gicr_group_powering_down(uint32_t pwrr)
{
/*
* Whether the redistributor group power down operation is in transit:
* i.e. it's intending to, but not finished yet.
*/
return ((pwrr & PWRR_RDGPD) && !(pwrr & PWRR_RDGPO));
}
static void gic600_pwr_on(uintptr_t base)
{
/* Power on redistributor */
gicr_write_pwrr(base, PWRR_ON);
/* Wait until the power on state is reflected */
while (gicr_read_pwrr(base) & PWRR_RDGPO)
;
}
static void gic600_pwr_off(uintptr_t base)
{
/* Power off redistributor */
gicr_write_pwrr(base, PWRR_OFF);
/*
* If this is the last man, turning this redistributor frame off will
* result in the group itself being powered off. In that case, wait as
* long as it's in transition, or has aborted the transition altogether
* for any reason.
*/
if (gicr_read_pwrr(base) & PWRR_RDGPD) {
while (gicr_group_powering_down(gicr_read_pwrr(base)))
;
}
}
/*
* Power off GIC600 redistributor
*/
void gicv3_rdistif_off(unsigned int proc_num)
{
uintptr_t gicr_base;
assert(gicv3_driver_data);
assert(proc_num < gicv3_driver_data->rdistif_num);
assert(gicv3_driver_data->rdistif_base_addrs);
gicr_base = gicv3_driver_data->rdistif_base_addrs[proc_num];
assert(gicr_base);
/* Attempt to power redistributor off */
gic600_pwr_off(gicr_base);
}
/*
* Power on GIC600 redistributor
*/
void gicv3_rdistif_on(unsigned int proc_num)
{
uintptr_t gicr_base;
assert(gicv3_driver_data);
assert(proc_num < gicv3_driver_data->rdistif_num);
assert(gicv3_driver_data->rdistif_base_addrs);
gicr_base = gicv3_driver_data->rdistif_base_addrs[proc_num];
assert(gicr_base);
/* Power redistributor on */
gic600_pwr_on(gicr_base);
}

View File

@ -8,12 +8,10 @@
#include <arch_helpers.h>
#include <assert.h>
#include <debug.h>
#include <gic_common.h>
#include <gicv3.h>
#include "../common/gic_common_private.h"
#include "gicv3_private.h"
static const gicv3_driver_data_t *driver_data;
const gicv3_driver_data_t *gicv3_driver_data;
static unsigned int gicv2_compat;
/*
@ -90,18 +88,20 @@ void gicv3_driver_init(const gicv3_driver_data_t *plat_driver_data)
plat_driver_data->gicr_base,
plat_driver_data->mpidr_to_core_pos);
driver_data = plat_driver_data;
gicv3_driver_data = plat_driver_data;
/*
* The GIC driver data is initialized by the primary CPU with caches
* enabled. When the secondary CPU boots up, it initializes the
* GICC/GICR interface with the caches disabled. Hence flush the
* driver_data to ensure coherency. This is not required if the
* driver data to ensure coherency. This is not required if the
* platform has HW_ASSISTED_COHERENCY enabled.
*/
#if !HW_ASSISTED_COHERENCY
flush_dcache_range((uintptr_t) &driver_data, sizeof(driver_data));
flush_dcache_range((uintptr_t) driver_data, sizeof(*driver_data));
flush_dcache_range((uintptr_t) &gicv3_driver_data,
sizeof(gicv3_driver_data));
flush_dcache_range((uintptr_t) gicv3_driver_data,
sizeof(*gicv3_driver_data));
#endif
INFO("GICv3 %s legacy support detected."
@ -117,10 +117,10 @@ void gicv3_distif_init(void)
{
unsigned int bitmap = 0;
assert(driver_data);
assert(driver_data->gicd_base);
assert(driver_data->g1s_interrupt_array ||
driver_data->g0_interrupt_array);
assert(gicv3_driver_data);
assert(gicv3_driver_data->gicd_base);
assert(gicv3_driver_data->g1s_interrupt_array ||
gicv3_driver_data->g0_interrupt_array);
assert(IS_IN_EL3());
@ -129,39 +129,39 @@ void gicv3_distif_init(void)
* the ARE_S bit. The Distributor might generate a system error
* otherwise.
*/
gicd_clr_ctlr(driver_data->gicd_base,
gicd_clr_ctlr(gicv3_driver_data->gicd_base,
CTLR_ENABLE_G0_BIT |
CTLR_ENABLE_G1S_BIT |
CTLR_ENABLE_G1NS_BIT,
RWP_TRUE);
/* Set the ARE_S and ARE_NS bit now that interrupts have been disabled */
gicd_set_ctlr(driver_data->gicd_base,
gicd_set_ctlr(gicv3_driver_data->gicd_base,
CTLR_ARE_S_BIT | CTLR_ARE_NS_BIT, RWP_TRUE);
/* Set the default attribute of all SPIs */
gicv3_spis_configure_defaults(driver_data->gicd_base);
gicv3_spis_configure_defaults(gicv3_driver_data->gicd_base);
/* Configure the G1S SPIs */
if (driver_data->g1s_interrupt_array) {
gicv3_secure_spis_configure(driver_data->gicd_base,
driver_data->g1s_interrupt_num,
driver_data->g1s_interrupt_array,
if (gicv3_driver_data->g1s_interrupt_array) {
gicv3_secure_spis_configure(gicv3_driver_data->gicd_base,
gicv3_driver_data->g1s_interrupt_num,
gicv3_driver_data->g1s_interrupt_array,
INTR_GROUP1S);
bitmap |= CTLR_ENABLE_G1S_BIT;
}
/* Configure the G0 SPIs */
if (driver_data->g0_interrupt_array) {
gicv3_secure_spis_configure(driver_data->gicd_base,
driver_data->g0_interrupt_num,
driver_data->g0_interrupt_array,
if (gicv3_driver_data->g0_interrupt_array) {
gicv3_secure_spis_configure(gicv3_driver_data->gicd_base,
gicv3_driver_data->g0_interrupt_num,
gicv3_driver_data->g0_interrupt_array,
INTR_GROUP0);
bitmap |= CTLR_ENABLE_G0_BIT;
}
/* Enable the secure SPIs now that they have been configured */
gicd_set_ctlr(driver_data->gicd_base, bitmap, RWP_TRUE);
gicd_set_ctlr(gicv3_driver_data->gicd_base, bitmap, RWP_TRUE);
}
/*******************************************************************************
@ -173,37 +173,37 @@ void gicv3_rdistif_init(unsigned int proc_num)
{
uintptr_t gicr_base;
assert(driver_data);
assert(proc_num < driver_data->rdistif_num);
assert(driver_data->rdistif_base_addrs);
assert(driver_data->gicd_base);
assert(gicd_read_ctlr(driver_data->gicd_base) & CTLR_ARE_S_BIT);
assert(driver_data->g1s_interrupt_array ||
driver_data->g0_interrupt_array);
assert(gicv3_driver_data);
assert(proc_num < gicv3_driver_data->rdistif_num);
assert(gicv3_driver_data->rdistif_base_addrs);
assert(gicv3_driver_data->gicd_base);
assert(gicd_read_ctlr(gicv3_driver_data->gicd_base) & CTLR_ARE_S_BIT);
assert(gicv3_driver_data->g1s_interrupt_array ||
gicv3_driver_data->g0_interrupt_array);
assert(IS_IN_EL3());
/* Power on redistributor */
gicv3_rdistif_on(proc_num);
gicr_base = driver_data->rdistif_base_addrs[proc_num];
gicr_base = gicv3_driver_data->rdistif_base_addrs[proc_num];
/* Set the default attribute of all SGIs and PPIs */
gicv3_ppi_sgi_configure_defaults(gicr_base);
/* Configure the G1S SGIs/PPIs */
if (driver_data->g1s_interrupt_array) {
if (gicv3_driver_data->g1s_interrupt_array) {
gicv3_secure_ppi_sgi_configure(gicr_base,
driver_data->g1s_interrupt_num,
driver_data->g1s_interrupt_array,
gicv3_driver_data->g1s_interrupt_num,
gicv3_driver_data->g1s_interrupt_array,
INTR_GROUP1S);
}
/* Configure the G0 SGIs/PPIs */
if (driver_data->g0_interrupt_array) {
if (gicv3_driver_data->g0_interrupt_array) {
gicv3_secure_ppi_sgi_configure(gicr_base,
driver_data->g0_interrupt_num,
driver_data->g0_interrupt_array,
gicv3_driver_data->g0_interrupt_num,
gicv3_driver_data->g0_interrupt_array,
INTR_GROUP0);
}
}
@ -231,13 +231,13 @@ void gicv3_cpuif_enable(unsigned int proc_num)
unsigned int scr_el3;
unsigned int icc_sre_el3;
assert(driver_data);
assert(proc_num < driver_data->rdistif_num);
assert(driver_data->rdistif_base_addrs);
assert(gicv3_driver_data);
assert(proc_num < gicv3_driver_data->rdistif_num);
assert(gicv3_driver_data->rdistif_base_addrs);
assert(IS_IN_EL3());
/* Mark the connected core as awake */
gicr_base = driver_data->rdistif_base_addrs[proc_num];
gicr_base = gicv3_driver_data->rdistif_base_addrs[proc_num];
gicv3_rdistif_mark_core_awake(gicr_base);
/* Disable the legacy interrupt bypass */
@ -291,9 +291,9 @@ void gicv3_cpuif_disable(unsigned int proc_num)
{
uintptr_t gicr_base;
assert(driver_data);
assert(proc_num < driver_data->rdistif_num);
assert(driver_data->rdistif_base_addrs);
assert(gicv3_driver_data);
assert(proc_num < gicv3_driver_data->rdistif_num);
assert(gicv3_driver_data->rdistif_base_addrs);
assert(IS_IN_EL3());
@ -314,7 +314,7 @@ void gicv3_cpuif_disable(unsigned int proc_num)
isb();
/* Mark the connected core as asleep */
gicr_base = driver_data->rdistif_base_addrs[proc_num];
gicr_base = gicv3_driver_data->rdistif_base_addrs[proc_num];
gicv3_rdistif_mark_core_asleep(gicr_base);
}
@ -371,25 +371,25 @@ unsigned int gicv3_get_interrupt_type(unsigned int id,
uintptr_t gicr_base;
assert(IS_IN_EL3());
assert(driver_data);
assert(gicv3_driver_data);
/* Ensure the parameters are valid */
assert(id < PENDING_G1S_INTID || id >= MIN_LPI_ID);
assert(proc_num < driver_data->rdistif_num);
assert(proc_num < gicv3_driver_data->rdistif_num);
/* All LPI interrupts are Group 1 non secure */
if (id >= MIN_LPI_ID)
return INTR_GROUP1NS;
if (id < MIN_SPI_ID) {
assert(driver_data->rdistif_base_addrs);
gicr_base = driver_data->rdistif_base_addrs[proc_num];
assert(gicv3_driver_data->rdistif_base_addrs);
gicr_base = gicv3_driver_data->rdistif_base_addrs[proc_num];
igroup = gicr_get_igroupr0(gicr_base, id);
grpmodr = gicr_get_igrpmodr0(gicr_base, id);
} else {
assert(driver_data->gicd_base);
igroup = gicd_get_igroupr(driver_data->gicd_base, id);
grpmodr = gicd_get_igrpmodr(driver_data->gicd_base, id);
assert(gicv3_driver_data->gicd_base);
igroup = gicd_get_igroupr(gicv3_driver_data->gicd_base, id);
grpmodr = gicd_get_igrpmodr(gicv3_driver_data->gicd_base, id);
}
/*

View File

@ -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
*/
@ -7,9 +7,11 @@
#ifndef __GICV3_PRIVATE_H__
#define __GICV3_PRIVATE_H__
#include <gic_common.h>
#include <gicv3.h>
#include <mmio.h>
#include <stdint.h>
#include "../common/gic_common_private.h"
/*******************************************************************************
* GICv3 private macro definitions

View File

@ -31,13 +31,18 @@ endif
$(eval $(call add_define,FVP_INTERCONNECT_DRIVER))
# Choose the GIC sources depending upon the how the FVP will be invoked
ifeq (${FVP_USE_GIC_DRIVER}, FVP_GICV3)
FVP_GIC_SOURCES := drivers/arm/gic/common/gic_common.c \
FVP_GICV3_SOURCES := drivers/arm/gic/common/gic_common.c \
drivers/arm/gic/v3/gicv3_main.c \
drivers/arm/gic/v3/gicv3_helpers.c \
plat/common/plat_gicv3.c \
plat/arm/common/arm_gicv3.c
# Choose the GIC sources depending upon the how the FVP will be invoked
ifeq (${FVP_USE_GIC_DRIVER}, FVP_GICV3)
FVP_GIC_SOURCES := ${FVP_GICV3_SOURCES}
else ifeq (${FVP_USE_GIC_DRIVER},FVP_GIC600)
FVP_GIC_SOURCES := ${FVP_GICV3_SOURCES} \
drivers/arm/gic/v3/gic600.c
else ifeq (${FVP_USE_GIC_DRIVER}, FVP_GICV2)
FVP_GIC_SOURCES := drivers/arm/gic/common/gic_common.c \
drivers/arm/gic/v2/gicv2_main.c \