diff options
Diffstat (limited to 'drivers/arm/gic/v3/gic600.c')
-rw-r--r-- | drivers/arm/gic/v3/gic600.c | 126 |
1 files changed, 126 insertions, 0 deletions
diff --git a/drivers/arm/gic/v3/gic600.c b/drivers/arm/gic/v3/gic600.c new file mode 100644 index 00000000..eb4fc548 --- /dev/null +++ b/drivers/arm/gic/v3/gic600.c @@ -0,0 +1,126 @@ +/* + * 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) + +/* 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))) + ; + } +} + +void gicv3_distif_pre_save(unsigned int proc_num) +{ + arm_gicv3_distif_pre_save(proc_num); +} + +void gicv3_distif_post_restore(unsigned int proc_num) +{ + arm_gicv3_distif_post_restore(proc_num); +} + +/* + * 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); +} |