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 ***
_______________________________________________
[email protected] mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "[email protected]"

Reply via email to