Introduce ARM GIC-600 driver

ARM GIC-600 IP complies with ARM GICv3 architecture, but among others,
implements a power control register in the Redistributor frame. This
register must be programmed to mark the frame as powered on, before
accessing other registers in the frame. Rest of initialization sequence
remains the same.

The driver provides APIs for Redistributor power management, and
overrides those in the generic GICv3 driver. The driver data is shared
between generic GICv3 driver and that of GIC-600.

For FVP platform, the GIC-600 driver is chosen when FVP_USE_GIC_DRIVER
is set to FVP_GIC600. Also update user guide.

Change-Id: I321b2360728d69f6d4b0a747b2cfcc3fe5a20d67
Signed-off-by: Jeenu Viswambharan <jeenu.viswambharan@arm.com>
diff --git a/drivers/arm/gic/v3/gic600.c b/drivers/arm/gic/v3/gic600.c
new file mode 100644
index 0000000..4ea31ab
--- /dev/null
+++ b/drivers/arm/gic/v3/gic600.c
@@ -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);
+}