Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F104183397
D2377.id5256.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
28 KB
Referenced Files
None
Subscribers
None
D2377.id5256.diff
View Options
Index: sys/arm64/arm64/gic_v3.c
===================================================================
--- /dev/null
+++ sys/arm64/arm64/gic_v3.c
@@ -0,0 +1,564 @@
+/*-
+ * 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"
+
+/*
+ * Driver-specific definitions.
+ */
+MALLOC_DEFINE(M_GIC_V3, "GICv3", GIC_V3_DEVSTR);
+
+devclass_t gic_v3_devclass;
+
+/*
+ * 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->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) {
+ err = ENXIO;
+ goto error;
+ }
+ }
+
+ /*
+ * 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 = typer & 0x1F;
+ sc->gic_nirqs = (sc->gic_nirqs + 1) * 32;
+ if (sc->gic_nirqs > 1020)
+ sc->gic_nirqs = 1020;
+
+ /* 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)
+ goto error;
+ }
+ /*
+ * Full success.
+ * Now register PIC to the interrupts handling layer.
+ */
+ arm_register_root_pic(dev, sc->gic_nirqs);
+
+error:
+ return (err);
+}
+
+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
+ */
+ }
+ for (i = 0, rid = 0; i < (sc->gic_redists.nregions + 1); i++, 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.
+ */
+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);
+ }
+ }
+}
+
+void
+gic_v3_eoi(device_t dev, u_int irq)
+{
+
+ gic_icc_write(EOIR1, (uint64_t)irq);
+}
+
+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);
+}
+
+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) {
+ device_printf(sc->dev,
+ "GICD Register write pending for too long");
+ return;
+ }
+ }
+}
+
+/* CPU interface. */
+static __inline void
+gic_v3_cpu_priority(uint64_t mask)
+{
+
+ /* Set prority mask */
+ gic_icc_write(PMR, mask & 0xFFUL);
+}
+
+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)) {
+ /* 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)
+ 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_EOI);
+ /* 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 += 16)
+ gic_d_write(sc, 4, GICD_ICFGR(i >> 4), 0x00000000);
+
+ /* Set priority to all shared interrupts */
+ for (i = GIC_FIRST_SPI; i < sc->gic_nirqs; i += 4) {
+ /* 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 += 32)
+ 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 >> 32) == 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) {
+ 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) {
+ device_printf(sc->dev,
+ "Could not wake Re-Distributor for CPU%u",
+ PCPU_GET(cpuid));
+ return (ENXIO);
+ }
+ }
+
+ 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)
+ goto error;
+
+ err = gic_v3_redist_wake(sc);
+ if (err)
+ goto error;
+
+ /* 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 += 4) {
+ gic_r_write(sc, 4, GICR_SGI_BASE_SIZE + GICD_IPRIORITYR(i),
+ GIC_PRIORITY_MAX);
+ }
+
+ gic_v3_wait_for_rwp(sc, REDIST);
+
+error:
+ return (err);
+}
Index: sys/arm64/arm64/gic_v3_fdt.c
===================================================================
--- /dev/null
+++ sys/arm64/arm64/gic_v3_fdt.c
@@ -0,0 +1,156 @@
+/*-
+ * 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/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 <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.
+ */
+struct gic_v3_ofw_devinfo {
+ struct ofw_bus_devinfo di_dinfo;
+ struct resource_list di_rl;
+};
+
+static int gic_v3_fdt_probe(device_t);
+static int gic_v3_fdt_attach(device_t);
+static int gic_v3_fdt_detach(device_t);
+
+static const struct ofw_bus_devinfo *
+gic_v3_ofw_get_devinfo(device_t __unused, device_t);
+
+static device_method_t gic_v3_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, gic_v3_fdt_probe),
+ DEVMETHOD(device_attach, gic_v3_fdt_attach),
+ DEVMETHOD(device_detach, gic_v3_fdt_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
+};
+
+static driver_t gic_v3_driver = {
+ "gic",
+ gic_v3_methods,
+ sizeof(struct gic_v3_softc),
+};
+
+EARLY_DRIVER_MODULE(gic_v3, simplebus, gic_v3_driver, gic_v3_devclass, 0, 0,
+ BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
+EARLY_DRIVER_MODULE(gic_v3, ofwbus, gic_v3_driver, gic_v3_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_fdt_detach(dev);
+
+ return (err);
+}
+
+static int
+gic_v3_fdt_detach(device_t dev)
+{
+
+ return (gic_v3_detach(dev));
+}
Index: sys/arm64/arm64/gic_v3_reg.h
===================================================================
--- /dev/null
+++ sys/arm64/arm64/gic_v3_reg.h
@@ -0,0 +1,168 @@
+/*-
+ * 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.
+ */
+
+#ifndef _GIC_V3_REG_H_
+#define _GIC_V3_REG_H_
+
+/*
+ * 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(x) ((((x) >> 19) & 0x1F) + 1)
+
+#define GICD_ISENABLER(n) (0x0100 + (((n) >> 5) * 4))
+#define GICD_ICENABLER(n) (0x0180 + (((n) >> 5) * 4))
+#define GICD_IPRIORITYR(n) (0x0400 + (((n) >> 2) * 4))
+#define GICD_I_MASK(n) (1 << ((n) % 32))
+
+#define GICD_ICFGR(n) (0x0C00 + ((n) * 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
+
+/*
+ * 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_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)
+
+/*
+ * 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_ */
Index: sys/arm64/arm64/gic_v3_var.h
===================================================================
--- /dev/null
+++ sys/arm64/arm64/gic_v3_var.h
@@ -0,0 +1,107 @@
+/*-
+ * 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.
+ */
+
+#ifndef _GIC_V3_VAR_H_
+#define _GIC_V3_VAR_H_
+
+#define GIC_V3_DEVSTR "ARM Generic Interrupt Controller v3.0"
+
+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;
+};
+
+extern devclass_t gic_v3_devclass;
+
+MALLOC_DECLARE(M_GIC_V3);
+
+/* Device and PIC methods */
+int gic_v3_attach(device_t dev);
+int gic_v3_detach(device_t dev);
+
+void gic_v3_dispatch(device_t, struct trapframe *);
+void gic_v3_eoi(device_t, u_int);
+void gic_v3_mask_irq(device_t, u_int);
+void gic_v3_unmask_irq(device_t, u_int);
+
+/*
+ * 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); \
+ \
+ bus_write_##len( \
+ sc->gic_redists.pcpu[cpu], \
+ reg, val); \
+})
+
+#endif /* _GIC_V3_VAR_H_ */
Index: sys/arm64/arm64/locore.S
===================================================================
--- sys/arm64/arm64/locore.S
+++ sys/arm64/arm64/locore.S
@@ -187,6 +187,18 @@
mov x2, #(PSR_F | PSR_I | PSR_A | PSR_D | PSR_M_EL1h)
msr spsr_el2, x2
+ /* Configure GICv3 CPU interface */
+ mrs x2, id_aa64pfr0_el1
+ ubfx x2, x2, #24, #4 /* GIC field */
+ cmp x2, #0x1 /* 0001 - SR CPU IF supported */
+ b.ne 2f
+
+ mrs x2, icc_sre_el2
+ orr x2, x2, #ICC_SRE_EL2_EN /* Enable access from insecure EL1 */
+ msr icc_sre_el2, x2
+ isb
+2:
+
/* Set the address to return to our return address */
msr elr_el2, x30
Index: sys/arm64/include/armreg.h
===================================================================
--- sys/arm64/include/armreg.h
+++ sys/arm64/include/armreg.h
@@ -96,6 +96,22 @@
#define EXCP_WATCHPT_EL1 0x35 /* Watchpoint, from same EL */
#define EXCP_BRK 0x3c /* Breakpoint */
+/* ICC_SRE_EL2 */
+#define ICC_SRE_EL2_EN (1U << 3)
+
+/* ICC_SRE_EL1 */
+#define ICC_SRE_EL1_SRE (1U << 0)
+
+/* ICC_CTLR_EL1 */
+#define ICC_CTLR_EL1_EOI (1U << 1)
+
+/* ICC_IGRPEN0_EL1 */
+#define ICC_IGRPEN0_EL1_EN (1U << 0)
+
+/* ICC_IAR1_EL1 */
+#define ICC_IAR1_EL1_SPUR (0x03ff)
+
+
/* ID_AA64PFR0_EL1 */
#define ID_AA64PFR0_EL0_MASK (0xf << 0)
#define ID_AA64PFR0_EL1_MASK (0xf << 4)
Index: sys/conf/files.arm64
===================================================================
--- sys/conf/files.arm64
+++ sys/conf/files.arm64
@@ -18,6 +18,7 @@
arm64/arm64/elf_machdep.c standard
arm64/arm64/exception.S standard
arm64/arm64/gic.c standard
+arm64/arm64/gic_v3.c standard
arm64/arm64/identcpu.c standard
arm64/arm64/intr_machdep.c standard
arm64/arm64/in_cksum.c optional inet | inet6
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Thu, Dec 5, 11:51 AM (10 h, 37 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
15054288
Default Alt Text
D2377.id5256.diff (28 KB)
Attached To
Mode
D2377: Introduce ARM GICv3 support
Attached
Detach File
Event Timeline
Log In to Comment