svn commit: r282867 - in head/sys: arm64/arm64 arm64/include conf

Zbigniew Bodek zbb at FreeBSD.org
Wed May 13 18:57:05 UTC 2015


Author: zbb
Date: Wed May 13 18:57:03 2015
New Revision: 282867
URL: https://svnweb.freebsd.org/changeset/base/282867

Log:
  Add support for ARM GICv3 interrupt controller used in some ARM64 chips
  
  GICv3 allows to distribute interrupts to more than 8 cores served by
  the previous GIC revisions. GICv3 introduces additional logic in form
  of Re-Distributors associated with particular CPUs to determine
  the highest priority interrupts and manage PPIs and LPIs
  (Locality-specific Peripheral Interrupts). Interrupts routing is
  based on CPUs' affinity numbers. CPU interface was changed to be
  accessible via CPU System Registers and this is the preferred
  (and supported) method in this driver.
  
  Obtained from: Semihalf
  Reviewed by:   andrew, emaste, ian, imp
  Sponsored by:  The FreeBSD Foundation

Added:
  head/sys/arm64/arm64/gic_v3.c   (contents, props changed)
  head/sys/arm64/arm64/gic_v3_fdt.c   (contents, props changed)
  head/sys/arm64/arm64/gic_v3_reg.h   (contents, props changed)
  head/sys/arm64/arm64/gic_v3_var.h   (contents, props changed)
Modified:
  head/sys/arm64/arm64/locore.S
  head/sys/arm64/include/armreg.h
  head/sys/conf/files.arm64

Added: head/sys/arm64/arm64/gic_v3.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/arm64/arm64/gic_v3.c	Wed May 13 18:57:03 2015	(r282867)
@@ -0,0 +1,580 @@
+/*-
+ * Copyright (c) 2015 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Semihalf under
+ * the sponsorship of the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/ktr.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+#include <sys/pcpu.h>
+#include <sys/proc.h>
+#include <sys/cpuset.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <machine/bus.h>
+#include <machine/intr.h>
+
+#include "pic_if.h"
+
+#include "gic_v3_reg.h"
+#include "gic_v3_var.h"
+
+/* Device and PIC methods */
+static void gic_v3_dispatch(device_t, struct trapframe *);
+static void gic_v3_eoi(device_t, u_int);
+static void gic_v3_mask_irq(device_t, u_int);
+static void gic_v3_unmask_irq(device_t, u_int);
+
+static device_method_t gic_v3_methods[] = {
+	/* Device interface */
+	DEVMETHOD(device_detach,	gic_v3_detach),
+
+	/* PIC interface */
+	DEVMETHOD(pic_dispatch,		gic_v3_dispatch),
+	DEVMETHOD(pic_eoi,		gic_v3_eoi),
+	DEVMETHOD(pic_mask,		gic_v3_mask_irq),
+	DEVMETHOD(pic_unmask,		gic_v3_unmask_irq),
+
+	/* End */
+	DEVMETHOD_END
+};
+
+DEFINE_CLASS_0(gic_v3, gic_v3_driver, gic_v3_methods,
+    sizeof(struct gic_v3_softc));
+
+/*
+ * Driver-specific definitions.
+ */
+MALLOC_DEFINE(M_GIC_V3, "GICv3", GIC_V3_DEVSTR);
+
+/*
+ * Helper functions and definitions.
+ */
+/* Destination registers, either Distributor or Re-Distributor */
+enum gic_v3_xdist {
+	DIST = 0,
+	REDIST,
+};
+
+/* Helper routines starting with gic_v3_ */
+static int gic_v3_dist_init(struct gic_v3_softc *);
+static int gic_v3_redist_find(struct gic_v3_softc *);
+static int gic_v3_redist_init(struct gic_v3_softc *);
+static int gic_v3_cpu_init(struct gic_v3_softc *);
+static void gic_v3_wait_for_rwp(struct gic_v3_softc *, enum gic_v3_xdist);
+
+/* A sequence of init functions for primary (boot) CPU */
+typedef int (*gic_v3_initseq_t) (struct gic_v3_softc *);
+/* Primary CPU initialization sequence */
+static gic_v3_initseq_t gic_v3_primary_init[] = {
+	gic_v3_dist_init,
+	gic_v3_redist_init,
+	gic_v3_cpu_init,
+	NULL
+};
+
+/*
+ * Device interface.
+ */
+int
+gic_v3_attach(device_t dev)
+{
+	struct gic_v3_softc *sc;
+	gic_v3_initseq_t *init_func;
+	uint32_t typer;
+	int rid;
+	int err;
+	size_t i;
+
+	sc = device_get_softc(dev);
+	sc->gic_registered = FALSE;
+	sc->dev = dev;
+	err = 0;
+
+	/* Initialize mutex */
+	mtx_init(&sc->gic_mtx, "GICv3 lock", NULL, MTX_SPIN);
+
+	/*
+	 * Allocate array of struct resource.
+	 * One entry for Distributor and all remaining for Re-Distributor.
+	 */
+	sc->gic_res = malloc(
+	    sizeof(sc->gic_res) * (sc->gic_redists.nregions + 1),
+	    M_GIC_V3, M_WAITOK);
+
+	/* Now allocate corresponding resources */
+	for (i = 0, rid = 0; i < (sc->gic_redists.nregions + 1); i++, rid++) {
+		sc->gic_res[rid] = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
+		    &rid, RF_ACTIVE);
+		if (sc->gic_res[rid] == NULL)
+			return (ENXIO);
+	}
+
+	/*
+	 * Distributor interface
+	 */
+	sc->gic_dist = sc->gic_res[0];
+
+	/*
+	 * Re-Dristributor interface
+	 */
+	/* Allocate space under region descriptions */
+	sc->gic_redists.regions = malloc(
+	    sizeof(*sc->gic_redists.regions) * sc->gic_redists.nregions,
+	    M_GIC_V3, M_WAITOK);
+
+	/* Fill-up bus_space information for each region. */
+	for (i = 0, rid = 1; i < sc->gic_redists.nregions; i++, rid++)
+		sc->gic_redists.regions[i] = sc->gic_res[rid];
+
+	/* Get the number of supported SPI interrupts */
+	typer = gic_d_read(sc, 4, GICD_TYPER);
+	sc->gic_nirqs = GICD_TYPER_I_NUM(typer);
+	if (sc->gic_nirqs > GIC_I_NUM_MAX)
+		sc->gic_nirqs = GIC_I_NUM_MAX;
+
+	/* Get the number of supported interrupt identifier bits */
+	sc->gic_idbits = GICD_TYPER_IDBITS(typer);
+
+	if (bootverbose) {
+		device_printf(dev, "SPIs: %u, IDs: %u\n",
+		    sc->gic_nirqs, (1 << sc->gic_idbits) - 1);
+	}
+
+	/* Train init sequence for boot CPU */
+	for (init_func = gic_v3_primary_init; *init_func != NULL; init_func++) {
+		err = (*init_func)(sc);
+		if (err != 0)
+			return (err);
+	}
+	/*
+	 * Full success.
+	 * Now register PIC to the interrupts handling layer.
+	 */
+	arm_register_root_pic(dev, sc->gic_nirqs);
+	sc->gic_registered = TRUE;
+
+	return (0);
+}
+
+int
+gic_v3_detach(device_t dev)
+{
+	struct gic_v3_softc *sc;
+	size_t i;
+	int rid;
+
+	sc = device_get_softc(dev);
+
+	if (device_is_attached(dev)) {
+		/*
+		 * XXX: We should probably deregister PIC
+		 */
+		if (sc->gic_registered)
+			panic("Trying to detach registered PIC");
+	}
+	for (rid = 0; rid < (sc->gic_redists.nregions + 1); rid++)
+		bus_release_resource(dev, SYS_RES_MEMORY, rid, sc->gic_res[rid]);
+
+	for (i = 0; i < MAXCPU; i++)
+		free(sc->gic_redists.pcpu[i], M_GIC_V3);
+
+	free(sc->gic_res, M_GIC_V3);
+	free(sc->gic_redists.regions, M_GIC_V3);
+
+	return (0);
+}
+
+/*
+ * PIC interface.
+ */
+static void
+gic_v3_dispatch(device_t dev, struct trapframe *frame)
+{
+	uint64_t active_irq;
+
+	while (1) {
+		active_irq = gic_icc_read(IAR1);
+
+		if (__predict_false(active_irq == ICC_IAR1_EL1_SPUR))
+			break;
+
+		if (__predict_true((active_irq >= GIC_FIRST_PPI &&
+		    active_irq <= GIC_LAST_SPI))) {
+			arm_dispatch_intr(active_irq, frame);
+			continue;
+		}
+
+		if (active_irq <= GIC_LAST_SGI || active_irq >= GIC_FIRST_LPI) {
+			/*
+			 * TODO: Implement proper SGI/LPI handling.
+			 *       Mask it if such is received for some reason.
+			 */
+			device_printf(dev,
+			    "Received unsupported interrupt type: %s\n",
+			    active_irq >= GIC_FIRST_LPI ? "LPI" : "SGI");
+			PIC_MASK(dev, active_irq);
+		}
+	}
+}
+
+static void
+gic_v3_eoi(device_t dev, u_int irq)
+{
+
+	gic_icc_write(EOIR1, (uint64_t)irq);
+}
+
+static void
+gic_v3_mask_irq(device_t dev, u_int irq)
+{
+	struct gic_v3_softc *sc;
+
+	sc = device_get_softc(dev);
+
+	if (irq >= GIC_FIRST_PPI && irq <= GIC_LAST_PPI) { /* PPIs in corresponding Re-Distributor */
+		gic_r_write(sc, 4,
+		    GICR_SGI_BASE_SIZE + GICD_ICENABLER(irq), GICD_I_MASK(irq));
+		gic_v3_wait_for_rwp(sc, REDIST);
+	} else if (irq >= GIC_FIRST_SPI && irq <= GIC_LAST_SPI) { /* SPIs in distributor */
+		gic_r_write(sc, 4, GICD_ICENABLER(irq), GICD_I_MASK(irq));
+		gic_v3_wait_for_rwp(sc, DIST);
+	} else
+		panic("%s: Unsupported IRQ number %u", __func__, irq);
+}
+
+static void
+gic_v3_unmask_irq(device_t dev, u_int irq)
+{
+	struct gic_v3_softc *sc;
+
+	sc = device_get_softc(dev);
+
+	if (irq >= GIC_FIRST_PPI && irq <= GIC_LAST_PPI) { /* PPIs in corresponding Re-Distributor */
+		gic_r_write(sc, 4,
+		    GICR_SGI_BASE_SIZE + GICD_ISENABLER(irq), GICD_I_MASK(irq));
+		gic_v3_wait_for_rwp(sc, REDIST);
+	} else if (irq >= GIC_FIRST_SPI && irq <= GIC_LAST_SPI) { /* SPIs in distributor */
+		gic_d_write(sc, 4, GICD_ISENABLER(irq), GICD_I_MASK(irq));
+		gic_v3_wait_for_rwp(sc, DIST);
+	} else
+		panic("%s: Unsupported IRQ number %u", __func__, irq);
+}
+
+/*
+ * Helper routines
+ */
+static void
+gic_v3_wait_for_rwp(struct gic_v3_softc *sc, enum gic_v3_xdist xdist)
+{
+	struct resource *res;
+	u_int cpuid;
+	size_t us_left = 1000000;
+
+	cpuid = PCPU_GET(cpuid);
+
+	switch (xdist) {
+	case DIST:
+		res = sc->gic_dist;
+		break;
+	case REDIST:
+		res = sc->gic_redists.pcpu[cpuid];
+		break;
+	default:
+		KASSERT(0, ("%s: Attempt to wait for unknown RWP", __func__));
+		return;
+	}
+
+	while ((bus_read_4(res, GICD_CTLR) & GICD_CTLR_RWP) != 0) {
+		DELAY(1);
+		if (us_left-- == 0)
+			panic("GICD Register write pending for too long");
+	}
+}
+
+/* CPU interface. */
+static __inline void
+gic_v3_cpu_priority(uint64_t mask)
+{
+
+	/* Set prority mask */
+	gic_icc_write(PMR, mask & ICC_PMR_EL1_PRIO_MASK);
+}
+
+static int
+gic_v3_cpu_enable_sre(struct gic_v3_softc *sc)
+{
+	uint64_t sre;
+	u_int cpuid;
+
+	cpuid = PCPU_GET(cpuid);
+	/*
+	 * Set the SRE bit to enable access to GIC CPU interface
+	 * via system registers.
+	 */
+	sre = READ_SPECIALREG(icc_sre_el1);
+	sre |= ICC_SRE_EL1_SRE;
+	WRITE_SPECIALREG(icc_sre_el1, sre);
+	isb();
+	/*
+	 * Now ensure that the bit is set.
+	 */
+	sre = READ_SPECIALREG(icc_sre_el1);
+	if ((sre & ICC_SRE_EL1_SRE) == 0) {
+		/* We are done. This was disabled in EL2 */
+		device_printf(sc->dev, "ERROR: CPU%u cannot enable CPU interface "
+		    "via system registers\n", cpuid);
+		return (ENXIO);
+	} else if (bootverbose) {
+		device_printf(sc->dev,
+		    "CPU%u enabled CPU interface via system registers\n",
+		    cpuid);
+	}
+
+	return (0);
+}
+
+static int
+gic_v3_cpu_init(struct gic_v3_softc *sc)
+{
+	int err;
+
+	/* Enable access to CPU interface via system registers */
+	err = gic_v3_cpu_enable_sre(sc);
+	if (err != 0)
+		return (err);
+	/* Priority mask to minimum - accept all interrupts */
+	gic_v3_cpu_priority(GIC_PRIORITY_MIN);
+	/* Disable EOI mode */
+	gic_icc_clear(CTLR, ICC_CTLR_EL1_EOIMODE);
+	/* Enable group 1 (insecure) interrups */
+	gic_icc_set(IGRPEN1, ICC_IGRPEN0_EL1_EN);
+
+	return (0);
+}
+
+/* Distributor */
+static int
+gic_v3_dist_init(struct gic_v3_softc *sc)
+{
+	uint64_t aff;
+	u_int i;
+
+	/*
+	 * 1. Disable the Distributor
+	 */
+	gic_d_write(sc, 4, GICD_CTLR, 0);
+	gic_v3_wait_for_rwp(sc, DIST);
+
+	/*
+	 * 2. Configure the Distributor
+	 */
+	/* Set all global interrupts to be level triggered, active low. */
+	for (i = GIC_FIRST_SPI; i < sc->gic_nirqs; i += GICD_I_PER_ICFGRn)
+		gic_d_write(sc, 4, GICD_ICFGR(i), 0x00000000);
+
+	/* Set priority to all shared interrupts */
+	for (i = GIC_FIRST_SPI;
+	    i < sc->gic_nirqs; i += GICD_I_PER_IPRIORITYn) {
+		/* Set highest priority */
+		gic_d_write(sc, 4, GICD_IPRIORITYR(i), GIC_PRIORITY_MAX);
+	}
+
+	/*
+	 * Disable all interrupts. Leave PPI and SGIs as they are enabled in
+	 * Re-Distributor registers.
+	 */
+	for (i = GIC_FIRST_SPI; i < sc->gic_nirqs; i += GICD_I_PER_ISENABLERn)
+		gic_d_write(sc, 4, GICD_ICENABLER(i), 0xFFFFFFFF);
+
+	gic_v3_wait_for_rwp(sc, DIST);
+
+	/*
+	 * 3. Enable Distributor
+	 */
+	/* Enable Distributor with ARE, Group 1 */
+	gic_d_write(sc, 4, GICD_CTLR, GICD_CTLR_ARE_NS | GICD_CTLR_G1A |
+	    GICD_CTLR_G1);
+
+	/*
+	 * 4. Route all interrupts to boot CPU.
+	 */
+	aff = CPU_AFFINITY(PCPU_GET(cpuid));
+	for (i = GIC_FIRST_SPI; i < sc->gic_nirqs; i++)
+		gic_d_write(sc, 4, GICD_IROUTER(i), aff);
+
+	return (0);
+}
+
+/* Re-Distributor */
+static int
+gic_v3_redist_find(struct gic_v3_softc *sc)
+{
+	struct resource r_res;
+	bus_space_handle_t r_bsh;
+	uint64_t aff;
+	uint64_t typer;
+	uint32_t pidr2;
+	u_int cpuid;
+	size_t i;
+
+	cpuid = PCPU_GET(cpuid);
+
+	/* Allocate struct resource for this CPU's Re-Distributor registers */
+	sc->gic_redists.pcpu[cpuid] =
+	    malloc(sizeof(*sc->gic_redists.pcpu[0]), M_GIC_V3, M_WAITOK);
+
+	aff = CPU_AFFINITY(cpuid);
+	/* Affinity in format for comparison with typer */
+	aff = (CPU_AFF3(aff) << 24) | (CPU_AFF2(aff) << 16) |
+	    (CPU_AFF1(aff) << 8) | CPU_AFF0(aff);
+
+	if (bootverbose) {
+		device_printf(sc->dev,
+		    "Start searching for Re-Distributor\n");
+	}
+	/* Iterate through Re-Distributor regions */
+	for (i = 0; i < sc->gic_redists.nregions; i++) {
+		/* Take a copy of the region's resource */
+		r_res = *sc->gic_redists.regions[i];
+		r_bsh = rman_get_bushandle(&r_res);
+
+		pidr2 = bus_read_4(&r_res, GICR_PIDR2);
+		switch (pidr2 & GICR_PIDR2_ARCH_MASK) {
+		case GICR_PIDR2_ARCH_GICv3: /* fall through */
+		case GICR_PIDR2_ARCH_GICv4:
+			break;
+		default:
+			device_printf(sc->dev,
+			    "No Re-Distributor found for CPU%u\n", cpuid);
+			free(sc->gic_redists.pcpu[cpuid], M_GIC_V3);
+			return (ENODEV);
+		}
+
+		do {
+			typer = bus_read_8(&r_res, GICR_TYPER);
+			if ((typer >> GICR_TYPER_AFF_SHIFT) == aff) {
+				KASSERT(sc->gic_redists.pcpu[cpuid] != NULL,
+				    ("Invalid pointer to per-CPU redistributor"));
+				/* Copy res contents to its final destination */
+				*sc->gic_redists.pcpu[cpuid] = r_res;
+				if (bootverbose) {
+					device_printf(sc->dev,
+					    "CPU%u Re-Distributor has been found\n",
+					    cpuid);
+				}
+				return (0);
+			}
+
+			r_bsh += (GICR_RD_BASE_SIZE + GICR_SGI_BASE_SIZE);
+			if ((typer & GICR_TYPER_VLPIS) != 0) {
+				r_bsh +=
+				    (GICR_VLPI_BASE_SIZE + GICR_RESERVED_SIZE);
+			}
+
+			rman_set_bushandle(&r_res, r_bsh);
+		} while ((typer & GICR_TYPER_LAST) == 0);
+	}
+
+	free(sc->gic_redists.pcpu[cpuid], M_GIC_V3);
+	device_printf(sc->dev, "No Re-Distributor found for CPU%u\n", cpuid);
+	return (ENXIO);
+}
+
+static int
+gic_v3_redist_wake(struct gic_v3_softc *sc)
+{
+	uint32_t waker;
+	size_t us_left = 1000000;
+
+	waker = gic_r_read(sc, 4, GICR_WAKER);
+	/* Wake up Re-Distributor for this CPU */
+	waker &= ~GICR_WAKER_PS;
+	gic_r_write(sc, 4, GICR_WAKER, waker);
+	/*
+	 * When clearing ProcessorSleep bit it is required to wait for
+	 * ChildrenAsleep to become zero following the processor power-on.
+	 */
+	while ((gic_r_read(sc, 4, GICR_WAKER) & GICR_WAKER_CA) != 0) {
+		DELAY(1);
+		if (us_left-- == 0) {
+			panic("Could not wake Re-Distributor for CPU%u",
+			    PCPU_GET(cpuid));
+		}
+	}
+
+	if (bootverbose) {
+		device_printf(sc->dev, "CPU%u Re-Distributor woke up\n",
+		    PCPU_GET(cpuid));
+	}
+
+	return (0);
+}
+
+static int
+gic_v3_redist_init(struct gic_v3_softc *sc)
+{
+	int err;
+	size_t i;
+
+	err = gic_v3_redist_find(sc);
+	if (err != 0)
+		return (err);
+
+	err = gic_v3_redist_wake(sc);
+	if (err != 0)
+		return (err);
+
+	/* Disable SPIs */
+	gic_r_write(sc, 4, GICR_SGI_BASE_SIZE + GICR_ICENABLER0,
+	    GICR_I_ENABLER_PPI_MASK);
+	/* Enable SGIs */
+	gic_r_write(sc, 4, GICR_SGI_BASE_SIZE + GICR_ISENABLER0,
+	    GICR_I_ENABLER_SGI_MASK);
+
+	/* Set priority for SGIs and PPIs */
+	for (i = 0; i <= GIC_LAST_PPI; i += GICR_I_PER_IPRIORITYn) {
+		gic_r_write(sc, 4, GICR_SGI_BASE_SIZE + GICD_IPRIORITYR(i),
+		    GIC_PRIORITY_MAX);
+	}
+
+	gic_v3_wait_for_rwp(sc, REDIST);
+
+	return (0);
+}

Added: head/sys/arm64/arm64/gic_v3_fdt.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/arm64/arm64/gic_v3_fdt.c	Wed May 13 18:57:03 2015	(r282867)
@@ -0,0 +1,124 @@
+/*-
+ * Copyright (c) 2015 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Semihalf under
+ * the sponsorship of the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include "pic_if.h"
+
+#include "gic_v3_reg.h"
+#include "gic_v3_var.h"
+
+/*
+ * FDT glue.
+ */
+static int gic_v3_fdt_probe(device_t);
+static int gic_v3_fdt_attach(device_t);
+
+static device_method_t gic_v3_fdt_methods[] = {
+	/* Device interface */
+	DEVMETHOD(device_probe,		gic_v3_fdt_probe),
+	DEVMETHOD(device_attach,	gic_v3_fdt_attach),
+
+	/* End */
+	DEVMETHOD_END
+};
+
+DEFINE_CLASS_1(gic_v3, gic_v3_fdt_driver, gic_v3_fdt_methods,
+    sizeof(struct gic_v3_softc), gic_v3_driver);
+
+static devclass_t gic_v3_fdt_devclass;
+
+EARLY_DRIVER_MODULE(gic_v3, simplebus, gic_v3_fdt_driver, gic_v3_fdt_devclass,
+    0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
+EARLY_DRIVER_MODULE(gic_v3, ofwbus, gic_v3_fdt_driver, gic_v3_fdt_devclass,
+    0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
+
+/*
+ * Device interface.
+ */
+static int
+gic_v3_fdt_probe(device_t dev)
+{
+
+	if (!ofw_bus_status_okay(dev))
+		return (ENXIO);
+
+	if (!ofw_bus_is_compatible(dev, "arm,gic-v3"))
+		return (ENXIO);
+
+	device_set_desc(dev, GIC_V3_DEVSTR);
+	return (BUS_PROBE_DEFAULT);
+}
+
+static int
+gic_v3_fdt_attach(device_t dev)
+{
+	struct gic_v3_softc *sc;
+	pcell_t redist_regions;
+	int err;
+
+	sc = device_get_softc(dev);
+	sc->dev = dev;
+
+	/*
+	 * Recover number of the Re-Distributor regions.
+	 */
+	if (OF_getencprop(ofw_bus_get_node(dev), "#redistributor-regions",
+	    &redist_regions, sizeof(redist_regions)) <= 0)
+		sc->gic_redists.nregions = 1;
+	else
+		sc->gic_redists.nregions = redist_regions;
+
+	err = gic_v3_attach(dev);
+	if (err)
+		goto error;
+
+	return (err);
+
+error:
+	if (bootverbose) {
+		device_printf(dev,
+		    "Failed to attach. Error %d\n", err);
+	}
+	/* Failure so free resources */
+	gic_v3_detach(dev);
+
+	return (err);
+}

Added: head/sys/arm64/arm64/gic_v3_reg.h
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/arm64/arm64/gic_v3_reg.h	Wed May 13 18:57:03 2015	(r282867)
@@ -0,0 +1,185 @@
+/*-
+ * Copyright (c) 2015 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Semihalf under
+ * the sponsorship of the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _GIC_V3_REG_H_
+#define	_GIC_V3_REG_H_
+
+/*
+ * Maximum number of interrupts
+ * supported by GIC (including SGIs, PPIs and SPIs)
+ */
+#define	GIC_I_NUM_MAX		(1020)
+/*
+ * Priority MAX/MIN values
+ */
+#define	GIC_PRIORITY_MAX	(0x00UL)
+/* Upper value is determined by LPI max priority */
+#define	GIC_PRIORITY_MIN	(0xFCUL)
+
+/* Numbers for software generated interrupts */
+#define	GIC_FIRST_SGI		(0)
+#define	GIC_LAST_SGI		(15)
+/* Numbers for private peripheral interrupts */
+#define	GIC_FIRST_PPI		(16)
+#define	GIC_LAST_PPI		(31)
+/* Numbers for spared peripheral interrupts */
+#define	GIC_FIRST_SPI		(32)
+#define	GIC_LAST_SPI		(1019)
+/* Numbers for local peripheral interrupts */
+#define	GIC_FIRST_LPI		(8192)
+
+/*
+ * Registers (v2/v3)
+ */
+#define	GICD_CTLR		(0x0000)
+#define	GICD_CTLR_G1		(1 << 0)
+#define	GICD_CTLR_G1A		(1 << 1)
+#define	GICD_CTLR_ARE_NS	(1 << 4)
+#define	GICD_CTLR_RWP		(1 << 31)
+
+#define	GICD_TYPER		(0x0004)
+#define		GICD_TYPER_IDBITS(n)	((((n) >> 19) & 0x1F) + 1)
+#define		GICD_TYPER_I_NUM(n)	((((n) & 0xF1) + 1) * 32)
+
+#define	GICD_ISENABLER(n)	(0x0100 + (((n) >> 5) * 4))
+#define		GICD_I_PER_ISENABLERn	(32)
+
+#define	GICD_ICENABLER(n)	(0x0180 + (((n) >> 5) * 4))
+#define	GICD_IPRIORITYR(n)	(0x0400 + (((n) >> 2) * 4))
+#define		GICD_I_PER_IPRIORITYn	(4)
+
+#define	GICD_I_MASK(n)		(1 << ((n) % 32))
+
+#define	GICD_ICFGR(n)		(0x0C00 + (((n) >> 4) * 4))
+/* First bit is a polarity bit (0 - low, 1 - high) */
+#define		GICD_ICFGR_POL_LOW	(0 << 0)
+#define		GICD_ICFGR_POL_HIGH	(1 << 0)
+#define		GICD_ICFGR_POL_MASK	(0x1)
+/* Second bit is a trigger bit (0 - level, 1 - edge) */
+#define		GICD_ICFGR_TRIG_LVL	(0 << 1)
+#define		GICD_ICFGR_TRIG_EDGE	(1 << 1)
+#define		GICD_ICFGR_TRIG_MASK	(0x2)
+
+#define		GICD_I_PER_ICFGRn	(16)
+
+/*
+ * Registers (v3)
+ */
+#define	GICD_IROUTER(n)		(0x6000 + ((n) * 8))
+#define	GICD_PIDR2		(0xFFE8)
+
+#define	GICR_PIDR2_ARCH_MASK	(0xF0)
+#define	GICR_PIDR2_ARCH_GICv3	(0x30)
+#define	GICR_PIDR2_ARCH_GICv4	(0x40)
+
+/* Redistributor registers */
+#define	GICR_PIDR2		GICD_PIDR2
+
+#define	GICR_TYPER		(0x0008)
+#define	GICR_TYPER_VLPIS	(1 << 1)
+#define	GICR_TYPER_LAST		(1 << 4)
+#define	GICR_TYPER_AFF_SHIFT	(32)
+
+#define	GICR_WAKER		(0x0014)
+#define	GICR_WAKER_PS		(1 << 1) /* Processor sleep */
+#define	GICR_WAKER_CA		(1 << 2) /* Children asleep */
+
+/* Re-distributor registers for SGIs and PPIs */
+#define	GICR_RD_BASE_SIZE	PAGE_SIZE_64K
+#define	GICR_SGI_BASE_SIZE	PAGE_SIZE_64K
+#define	GICR_VLPI_BASE_SIZE	PAGE_SIZE_64K
+#define	GICR_RESERVED_SIZE	PAGE_SIZE_64K
+
+#define	GICR_ISENABLER0				(0x0100)
+#define	GICR_ICENABLER0				(0x0180)
+#define		GICR_I_ENABLER_SGI_MASK		(0x0000FFFF)
+#define		GICR_I_ENABLER_PPI_MASK		(0xFFFF0000)
+
+#define		GICR_I_PER_IPRIORITYn		(GICD_I_PER_IPRIORITYn)
+
+/*
+ * CPU interface
+ */
+
+/*
+ * Registers list (ICC_xyz_EL1):
+ *
+ * PMR     - Priority Mask Register
+ *		* interrupts of priority higher than specified
+ *		  in this mask will be signalled to the CPU.
+ *		  (0xff - lowest possible prio., 0x00 - highest prio.)
+ *
+ * CTLR    - Control Register
+ *		* controls behavior of the CPU interface and displays
+ *		  implemented features.
+ *
+ * IGRPEN1 - Interrupt Group 1 Enable Register
+ *
+ * IAR1    - Interrupt Acknowledge Register Group 1
+ *		* contains number of the highest priority pending
+ *		  interrupt from the Group 1.
+ *
+ * EOIR1   - End of Interrupt Register Group 1
+ *		* Writes inform CPU interface about completed Group 1
+ *		  interrupts processing.
+ */
+
+#define	gic_icc_write(reg, val)					\
+do {								\
+	WRITE_SPECIALREG(ICC_ ##reg ##_EL1, val);		\
+	isb();							\
+} while (0)
+
+#define	gic_icc_read(reg)					\
+({								\
+	uint64_t val;						\
+								\
+	val = READ_SPECIALREG(ICC_ ##reg ##_EL1);		\
+	(val);							\
+})
+
+#define	gic_icc_set(reg, mask)					\
+do {								\
+	uint64_t val;						\
+	val = gic_icc_read(reg);				\
+	val |= (mask);						\
+	gic_icc_write(reg, val);				\
+} while (0)
+
+#define	gic_icc_clear(reg, mask)				\
+do {								\
+	uint64_t val;						\
+	val = gic_icc_read(reg);				\
+	val &= ~(mask);						\
+	gic_icc_write(reg, val);				\
+} while (0)
+
+#endif /* _GIC_V3_REG_H_ */

Added: head/sys/arm64/arm64/gic_v3_var.h
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/arm64/arm64/gic_v3_var.h	Wed May 13 18:57:03 2015	(r282867)
@@ -0,0 +1,106 @@
+/*-
+ * Copyright (c) 2015 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Semihalf under
+ * the sponsorship of the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _GIC_V3_VAR_H_
+#define _GIC_V3_VAR_H_
+
+#define	GIC_V3_DEVSTR	"ARM Generic Interrupt Controller v3.0"
+
+DECLARE_CLASS(gic_v3_driver);
+
+struct gic_redists {
+	/*
+	 * Re-Distributor region description.
+	 * We will have few of those depending
+	 * on the #redistributor-regions property in FDT.
+	 */
+	struct resource **	regions;
+	/* Number of Re-Distributor regions */
+	u_int			nregions;
+	/* Per-CPU Re-Distributor handler */
+	struct resource *	pcpu[MAXCPU];
+};
+
+struct gic_v3_softc {
+	device_t		dev;
+	struct resource **	gic_res;
+	struct mtx		gic_mtx;
+	/* Distributor */
+	struct resource *	gic_dist;
+	/* Re-Distributors */
+	struct gic_redists	gic_redists;
+
+	u_int			gic_nirqs;
+	u_int			gic_idbits;
+
+	boolean_t		gic_registered;
+};
+
+MALLOC_DECLARE(M_GIC_V3);
+
+/* Device methods */
+int gic_v3_attach(device_t dev);
+int gic_v3_detach(device_t dev);
+
+/*
+ * GIC Distributor accessors.
+ * Notice that only GIC sofc can be passed.
+ */
+#define	gic_d_read(sc, len, reg)		\
+({						\
+	bus_read_##len(sc->gic_dist, reg);	\
+})
+
+#define	gic_d_write(sc, len, reg, val)		\
+({						\
+	bus_write_##len(sc->gic_dist, reg, val);\
+})
+
+/* GIC Re-Distributor accessors (per-CPU) */
+#define	gic_r_read(sc, len, reg)		\
+({						\
+	u_int cpu = PCPU_GET(cpuid);		\
+						\
+	bus_read_##len(				\
+	    sc->gic_redists.pcpu[cpu],		\
+	    reg);				\
+})
+
+#define	gic_r_write(sc, len, reg, val)		\
+({						\
+	u_int cpu = PCPU_GET(cpuid);		\

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***


More information about the svn-src-head mailing list