Author: zbb
Date: Mon Jul  6 18:27:41 2015
New Revision: 285213
URL: https://svnweb.freebsd.org/changeset/base/285213

Log:
  Introduce ITS support for ARM64
  
  Add ARM ITS (Interrupt Translation Services) support required
  to bring-up message signalled interrupts on some ARM64 platforms.
  
  Obtained from: Semihalf
  Sponsored by:  The FreeBSD Foundation

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

Modified: head/sys/arm64/arm64/gic_v3.c
==============================================================================
--- head/sys/arm64/arm64/gic_v3.c       Mon Jul  6 18:27:18 2015        
(r285212)
+++ head/sys/arm64/arm64/gic_v3.c       Mon Jul  6 18:27:41 2015        
(r285213)
@@ -236,19 +236,18 @@ gic_v3_dispatch(device_t dev, struct tra
                        break;
 
                if (__predict_true((active_irq >= GIC_FIRST_PPI &&
-                   active_irq <= GIC_LAST_SPI))) {
+                   active_irq <= GIC_LAST_SPI) || active_irq >= 
GIC_FIRST_LPI)) {
                        arm_dispatch_intr(active_irq, frame);
                        continue;
                }
 
-               if (active_irq <= GIC_LAST_SGI || active_irq >= GIC_FIRST_LPI) {
+               if (active_irq <= GIC_LAST_SGI) {
                        /*
-                        * TODO: Implement proper SGI/LPI handling.
+                        * TODO: Implement proper SGI 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");
+                           "Received unsupported interrupt type: SGI\n");
                        PIC_MASK(dev, active_irq);
                }
        }
@@ -275,6 +274,8 @@ gic_v3_mask_irq(device_t dev, u_int irq)
        } 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 if (irq >= GIC_FIRST_LPI) { /* LPIs */
+               lpi_mask_irq(dev, irq);
        } else
                panic("%s: Unsupported IRQ number %u", __func__, irq);
 }
@@ -293,6 +294,8 @@ gic_v3_unmask_irq(device_t dev, u_int ir
        } 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 if (irq >= GIC_FIRST_LPI) { /* LPIs */
+               lpi_unmask_irq(dev, irq);
        } else
                panic("%s: Unsupported IRQ number %u", __func__, irq);
 }

Modified: head/sys/arm64/arm64/gic_v3_fdt.c
==============================================================================
--- head/sys/arm64/arm64/gic_v3_fdt.c   Mon Jul  6 18:27:18 2015        
(r285212)
+++ head/sys/arm64/arm64/gic_v3_fdt.c   Mon Jul  6 18:27:41 2015        
(r285213)
@@ -35,6 +35,8 @@ __FBSDID("$FreeBSD$");
 #include <sys/kernel.h>
 #include <sys/module.h>
 
+#include <machine/resource.h>
+
 #include <dev/fdt/fdt_common.h>
 #include <dev/ofw/openfirm.h>
 #include <dev/ofw/ofw_bus.h>
@@ -51,11 +53,27 @@ __FBSDID("$FreeBSD$");
 static int gic_v3_fdt_probe(device_t);
 static int gic_v3_fdt_attach(device_t);
 
+static struct resource *gic_v3_ofw_bus_alloc_res(device_t, device_t, int, int 
*,
+    u_long, u_long, u_long, u_int);
+static const struct ofw_bus_devinfo *gic_v3_ofw_get_devinfo(device_t, 
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),
 
+       /* Bus interface */
+       DEVMETHOD(bus_alloc_resource,           gic_v3_ofw_bus_alloc_res),
+       DEVMETHOD(bus_activate_resource,        bus_generic_activate_resource),
+
+       /* ofw_bus interface */
+       DEVMETHOD(ofw_bus_get_devinfo,  gic_v3_ofw_get_devinfo),
+       DEVMETHOD(ofw_bus_get_compat,   ofw_bus_gen_get_compat),
+       DEVMETHOD(ofw_bus_get_model,    ofw_bus_gen_get_model),
+       DEVMETHOD(ofw_bus_get_name,     ofw_bus_gen_get_name),
+       DEVMETHOD(ofw_bus_get_node,     ofw_bus_gen_get_node),
+       DEVMETHOD(ofw_bus_get_type,     ofw_bus_gen_get_type),
+
        /* End */
        DEVMETHOD_END
 };
@@ -71,6 +89,11 @@ EARLY_DRIVER_MODULE(gic_v3, ofwbus, gic_
     0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
 
 /*
+ * Helper functions declarations.
+ */
+static int gic_v3_ofw_bus_attach(device_t);
+
+/*
  * Device interface.
  */
 static int
@@ -109,6 +132,17 @@ gic_v3_fdt_attach(device_t dev)
        err = gic_v3_attach(dev);
        if (err)
                goto error;
+       /*
+        * Try to register ITS to this GIC.
+        * GIC will act as a bus in that case.
+        * Failure here will not affect main GIC functionality.
+        */
+       if (gic_v3_ofw_bus_attach(dev) != 0) {
+               if (bootverbose) {
+                       device_printf(dev,
+                           "Failed to attach ITS to this GIC\n");
+               }
+       }
 
        return (err);
 
@@ -122,3 +156,155 @@ error:
 
        return (err);
 }
+
+/* OFW bus interface */
+struct gic_v3_ofw_devinfo {
+       struct ofw_bus_devinfo  di_dinfo;
+       struct resource_list    di_rl;
+};
+
+static const struct ofw_bus_devinfo *
+gic_v3_ofw_get_devinfo(device_t bus __unused, device_t child)
+{
+       struct gic_v3_ofw_devinfo *di;
+
+       di = device_get_ivars(child);
+       return (&di->di_dinfo);
+}
+
+static struct resource *
+gic_v3_ofw_bus_alloc_res(device_t bus, device_t child, int type, int *rid,
+    u_long start, u_long end, u_long count, u_int flags)
+{
+       struct gic_v3_ofw_devinfo *di;
+       struct resource_list_entry *rle;
+       int ranges_len;
+
+       if ((start == 0UL) && (end == ~0UL)) {
+               if ((di = device_get_ivars(child)) == NULL)
+                       return (NULL);
+               if (type != SYS_RES_MEMORY)
+                       return (NULL);
+
+               /* Find defaults for this rid */
+               rle = resource_list_find(&di->di_rl, type, *rid);
+               if (rle == NULL)
+                       return (NULL);
+
+               start = rle->start;
+               end = rle->end;
+               count = rle->count;
+       }
+       /*
+        * XXX: No ranges remap!
+        *      Absolute address is expected.
+        */
+       if (ofw_bus_has_prop(bus, "ranges")) {
+               ranges_len = OF_getproplen(ofw_bus_get_node(bus), "ranges");
+               if (ranges_len != 0) {
+                       if (bootverbose) {
+                               device_printf(child,
+                                   "Ranges remap not supported\n");
+                       }
+                       return (NULL);
+               }
+       }
+       return (bus_generic_alloc_resource(bus, child, type, rid, start, end,
+           count, flags));
+}
+
+/* Helper functions */
+
+/*
+ * Bus capability support for GICv3.
+ * Collects and configures device informations and finally
+ * adds ITS device as a child of GICv3 in Newbus hierarchy.
+ */
+static int
+gic_v3_ofw_bus_attach(device_t dev)
+{
+       struct gic_v3_ofw_devinfo *di;
+       device_t child;
+       phandle_t parent, node;
+       pcell_t addr_cells, size_cells;
+
+       parent = ofw_bus_get_node(dev);
+       if (parent > 0) {
+               addr_cells = 2;
+               OF_getencprop(parent, "#address-cells", &addr_cells,
+                   sizeof(addr_cells));
+               size_cells = 2;
+               OF_getencprop(parent, "#size-cells", &size_cells,
+                   sizeof(size_cells));
+               /* Iterate through all GIC subordinates */
+               for (node = OF_child(parent); node > 0; node = OF_peer(node)) {
+                       /* Allocate and populate devinfo. */
+                       di = malloc(sizeof(*di), M_GIC_V3, M_WAITOK | M_ZERO);
+                       if (ofw_bus_gen_setup_devinfo(&di->di_dinfo, node)) {
+                               if (bootverbose) {
+                                       device_printf(dev,
+                                           "Could not set up devinfo for 
ITS\n");
+                               }
+                               free(di, M_GIC_V3);
+                               continue;
+                       }
+
+                       /* Initialize and populate resource list. */
+                       resource_list_init(&di->di_rl);
+                       ofw_bus_reg_to_rl(dev, node, addr_cells, size_cells,
+                           &di->di_rl);
+
+                       /* Should not have any interrupts, so don't add any */
+
+                       /* Add newbus device for this FDT node */
+                       child = device_add_child(dev, NULL, -1);
+                       if (!child) {
+                               if (bootverbose) {
+                                       device_printf(dev,
+                                           "Could not add child: %s\n",
+                                           di->di_dinfo.obd_name);
+                               }
+                               resource_list_free(&di->di_rl);
+                               ofw_bus_gen_destroy_devinfo(&di->di_dinfo);
+                               free(di, M_GIC_V3);
+                               continue;
+                       }
+
+                       device_set_ivars(child, di);
+               }
+       }
+
+       return (bus_generic_attach(dev));
+}
+
+static int gic_v3_its_fdt_probe(device_t dev);
+
+static device_method_t gic_v3_its_fdt_methods[] = {
+       /* Device interface */
+       DEVMETHOD(device_probe,         gic_v3_its_fdt_probe),
+
+       /* End */
+       DEVMETHOD_END
+};
+
+DEFINE_CLASS_1(gic_v3_its, gic_v3_its_fdt_driver, gic_v3_its_fdt_methods,
+    sizeof(struct gic_v3_its_softc), gic_v3_its_driver);
+
+static devclass_t gic_v3_its_fdt_devclass;
+
+EARLY_DRIVER_MODULE(gic_v3_its, gic_v3, gic_v3_its_fdt_driver,
+    gic_v3_its_fdt_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
+
+static int
+gic_v3_its_fdt_probe(device_t dev)
+{
+
+       if (!ofw_bus_status_okay(dev))
+               return (ENXIO);
+
+       if (!ofw_bus_is_compatible(dev, GIC_V3_ITS_COMPSTR))
+               return (ENXIO);
+
+       device_set_desc(dev, GIC_V3_ITS_DEVSTR);
+       return (BUS_PROBE_DEFAULT);
+}

Added: head/sys/arm64/arm64/gic_v3_its.c
==============================================================================
--- /dev/null   00:00:00 1970   (empty, because file is newly added)
+++ head/sys/arm64/arm64/gic_v3_its.c   Mon Jul  6 18:27:41 2015        
(r285213)
@@ -0,0 +1,1448 @@
+/*-
+ * 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/bitset.h>
+#include <sys/bitstring.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+#include <sys/pciio.h>
+#include <sys/pcpu.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+
+#include <dev/pci/pcivar.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <machine/bus.h>
+#include <machine/cpu.h>
+#include <machine/cpufunc.h>
+#include <machine/intr.h>
+
+#include "gic_v3_reg.h"
+#include "gic_v3_var.h"
+
+#include "pic_if.h"
+
+/* Device and PIC methods */
+static int gic_v3_its_attach(device_t);
+
+static device_method_t gic_v3_its_methods[] = {
+       /* Device interface */
+       DEVMETHOD(device_attach,        gic_v3_its_attach),
+       /*
+        * PIC interface
+        */
+       /* MSI-X */
+       DEVMETHOD(pic_alloc_msix,       gic_v3_its_alloc_msix),
+       DEVMETHOD(pic_map_msix,         gic_v3_its_map_msix),
+       /* MSI */
+       DEVMETHOD(pic_alloc_msi,        gic_v3_its_alloc_msi),
+       DEVMETHOD(pic_map_msi,          gic_v3_its_map_msix),
+
+       /* End */
+       DEVMETHOD_END
+};
+
+DEFINE_CLASS_0(gic_v3_its, gic_v3_its_driver, gic_v3_its_methods,
+    sizeof(struct gic_v3_its_softc));
+
+MALLOC_DEFINE(M_GIC_V3_ITS, "GICv3 ITS", GIC_V3_ITS_DEVSTR);
+
+static int its_alloc_tables(struct gic_v3_its_softc *);
+static void its_free_tables(struct gic_v3_its_softc *);
+static void its_init_commandq(struct gic_v3_its_softc *);
+static int its_init_cpu(struct gic_v3_its_softc *);
+static void its_init_cpu_collection(struct gic_v3_its_softc *);
+
+static int its_cmd_send(struct gic_v3_its_softc *, struct its_cmd_desc *);
+
+static void its_cmd_mapc(struct gic_v3_its_softc *, struct its_col *, uint8_t);
+static void its_cmd_mapvi(struct gic_v3_its_softc *, struct its_dev *, 
uint32_t,
+    uint32_t);
+static void its_cmd_mapi(struct gic_v3_its_softc *, struct its_dev *, 
uint32_t);
+static void its_cmd_inv(struct gic_v3_its_softc *, struct its_dev *, uint32_t);
+static void its_cmd_invall(struct gic_v3_its_softc *, struct its_col *);
+
+static void lpi_init_conftable(struct gic_v3_its_softc *);
+static void lpi_bitmap_init(struct gic_v3_its_softc *);
+static void lpi_init_cpu(struct gic_v3_its_softc *);
+static int lpi_config_cpu(struct gic_v3_its_softc *);
+
+const char *its_ptab_cache[] = {
+       [GITS_BASER_CACHE_NCNB] = "(NC,NB)",
+       [GITS_BASER_CACHE_NC] = "(NC)",
+       [GITS_BASER_CACHE_RAWT] = "(RA,WT)",
+       [GITS_BASER_CACHE_RAWB] = "(RA,WB)",
+       [GITS_BASER_CACHE_WAWT] = "(WA,WT)",
+       [GITS_BASER_CACHE_WAWB] = "(WA,WB)",
+       [GITS_BASER_CACHE_RAWAWT] = "(RAWA,WT)",
+       [GITS_BASER_CACHE_RAWAWB] = "(RAWA,WB)",
+};
+
+const char *its_ptab_share[] = {
+       [GITS_BASER_SHARE_NS] = "none",
+       [GITS_BASER_SHARE_IS] = "inner",
+       [GITS_BASER_SHARE_OS] = "outer",
+       [GITS_BASER_SHARE_RES] = "none",
+};
+
+const char *its_ptab_type[] = {
+       [GITS_BASER_TYPE_UNIMPL] = "Unimplemented",
+       [GITS_BASER_TYPE_DEV] = "Devices",
+       [GITS_BASER_TYPE_VP] = "Virtual Processors",
+       [GITS_BASER_TYPE_PP] = "Physical Processors",
+       [GITS_BASER_TYPE_IC] = "Interrupt Collections",
+       [GITS_BASER_TYPE_RES5] = "Reserved (5)",
+       [GITS_BASER_TYPE_RES6] = "Reserved (6)",
+       [GITS_BASER_TYPE_RES7] = "Reserved (7)",
+};
+
+static struct gic_v3_its_softc *its_sc;
+
+#define        gic_its_read(sc, len, reg)              \
+    bus_read_##len(&sc->its_res[0], reg)
+
+#define        gic_its_write(sc, len, reg, val)        \
+    bus_write_##len(&sc->its_res[0], reg, val)
+
+static int
+gic_v3_its_attach(device_t dev)
+{
+       struct gic_v3_its_softc *sc;
+       uint64_t gits_tmp;
+       uint32_t gits_pidr2;
+       int rid;
+       int ret;
+
+       sc = device_get_softc(dev);
+
+       /*
+        * Initialize sleep & spin mutex for ITS
+        */
+       /* Protects ITS device list and assigned LPIs bitmaps. */
+       mtx_init(&sc->its_mtx, "ITS sleep lock", NULL, MTX_DEF);
+       /* Protects access to ITS command circular buffer. */
+       mtx_init(&sc->its_spin_mtx, "ITS spin lock", NULL, MTX_SPIN);
+
+       rid = 0;
+       sc->its_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+           RF_ACTIVE);
+       if (sc->its_res == NULL) {
+               device_printf(dev, "Could not allocate memory\n");
+               return (ENXIO);
+       }
+
+       sc->dev = dev;
+
+       gits_pidr2 = gic_its_read(sc, 4, GITS_PIDR2);
+       switch (gits_pidr2 & GITS_PIDR2_ARCH_MASK) {
+       case GITS_PIDR2_ARCH_GICv3: /* fall through */
+       case GITS_PIDR2_ARCH_GICv4:
+               if (bootverbose) {
+                       device_printf(dev, "ITS found. Architecture rev. %u\n",
+                           (u_int)(gits_pidr2 & GITS_PIDR2_ARCH_MASK) >> 4);
+               }
+               break;
+       default:
+               device_printf(dev, "No ITS found in the system\n");
+               gic_v3_its_detach(dev);
+               return (ENODEV);
+       }
+
+       /* 1. Initialize commands queue */
+       its_init_commandq(sc);
+
+       /* 2. Provide memory for any private ITS tables */
+       ret = its_alloc_tables(sc);
+       if (ret != 0) {
+               gic_v3_its_detach(dev);
+               return (ret);
+       }
+
+       /* 3. Allocate collections. One per-CPU */
+       sc->its_cols = malloc(sizeof(*sc->its_cols) * MAXCPU,
+           M_GIC_V3_ITS, (M_WAITOK | M_ZERO));
+
+       /* 4. Enable ITS in GITS_CTLR */
+       gits_tmp = gic_its_read(sc, 4, GITS_CTLR);
+       gic_its_write(sc, 4, GITS_CTLR, gits_tmp | GITS_CTLR_EN);
+
+       /* 5. Initialize LPIs configuration table */
+       lpi_init_conftable(sc);
+
+       /* 6. LPIs bitmap init */
+       lpi_bitmap_init(sc);
+
+       /* 7. CPU init */
+       (void)its_init_cpu(sc);
+
+       /* 8. Init ITS devices list */
+       TAILQ_INIT(&sc->its_dev_list);
+
+       arm_register_msi_pic(dev);
+
+       /*
+        * XXX ARM64TODO: We need to have ITS software context
+        * when being called by the interrupt code (mask/unmask).
+        * This may be used only when one ITS is present in
+        * the system and eventually should be removed.
+        */
+       KASSERT(its_sc == NULL,
+           ("Trying to assign its_sc that is already set"));
+       its_sc = sc;
+
+       return (0);
+}
+
+/* Will not detach but use it for convenience */
+int
+gic_v3_its_detach(device_t dev)
+{
+       device_t parent;
+       struct gic_v3_softc *gic_sc;
+       struct gic_v3_its_softc *sc;
+       u_int cpuid;
+       int rid = 0;
+
+       sc = device_get_softc(dev);
+       cpuid = PCPU_GET(cpuid);
+
+       /* Release what's possible */
+
+       /* Command queue */
+       if ((void *)sc->its_cmdq_base != NULL) {
+               contigfree((void *)sc->its_cmdq_base,
+                   ITS_CMDQ_SIZE, M_GIC_V3_ITS);
+       }
+       /* ITTs */
+       its_free_tables(sc);
+       /* Collections */
+       free(sc->its_cols, M_GIC_V3_ITS);
+       /* LPI config table */
+       parent = device_get_parent(sc->dev);
+       gic_sc = device_get_softc(parent);
+       if ((void *)gic_sc->gic_redists.lpis.conf_base != NULL) {
+               contigfree((void *)gic_sc->gic_redists.lpis.conf_base,
+                   LPI_CONFTAB_SIZE, M_GIC_V3_ITS);
+       }
+       if ((void *)gic_sc->gic_redists.lpis.pend_base[cpuid] != NULL) {
+               contigfree((void *)gic_sc->gic_redists.lpis.pend_base[cpuid],
+                   roundup2(LPI_PENDTAB_SIZE, PAGE_SIZE_64K), M_GIC_V3_ITS);
+       }
+
+       /* Resource... */
+       bus_release_resource(dev, SYS_RES_MEMORY, rid, sc->its_res);
+
+       /* XXX ARM64TODO: Reset global pointer to ITS software context */
+       its_sc = NULL;
+
+       return (0);
+}
+
+static int
+its_alloc_tables(struct gic_v3_its_softc *sc)
+{
+       uint64_t gits_baser, gits_tmp;
+       uint64_t type, esize, cache, share, psz;
+       uint64_t gits_typer;
+       size_t page_size, npages, nitspages, nidents, tn;
+       size_t its_tbl_size;
+       vm_offset_t ptab_vaddr;
+       vm_paddr_t ptab_paddr;
+       boolean_t first = TRUE;
+
+       page_size = PAGE_SIZE_64K;
+
+       /* Read features first */
+       gits_typer = gic_its_read(sc, 8, GITS_TYPER);
+
+       for (tn = 0; tn < GITS_BASER_NUM; tn++) {
+               gits_baser = gic_its_read(sc, 8, GITS_BASER(tn));
+               type = GITS_BASER_TYPE(gits_baser);
+               /* Get the Table Entry size */
+               esize = GITS_BASER_ESIZE(gits_baser);
+
+               switch (type) {
+               case GITS_BASER_TYPE_UNIMPL: /* fall through */
+               case GITS_BASER_TYPE_RES5:
+               case GITS_BASER_TYPE_RES6:
+               case GITS_BASER_TYPE_RES7:
+                       continue;
+               case GITS_BASER_TYPE_DEV:
+                       nidents = (1 << GITS_TYPER_DEVB(gits_typer));
+                       its_tbl_size = esize * nidents;
+                       its_tbl_size = roundup2(its_tbl_size, page_size);
+                       npages = howmany(its_tbl_size, PAGE_SIZE);
+                       break;
+               default:
+                       npages = howmany(page_size, PAGE_SIZE);
+                       break;
+               }
+
+               /* Allocate required space */
+               ptab_vaddr = (vm_offset_t)contigmalloc(npages * PAGE_SIZE,
+                   M_GIC_V3_ITS, (M_WAITOK | M_ZERO), 0, ~0UL, PAGE_SIZE, 0);
+
+               sc->its_ptabs[tn].ptab_vaddr = ptab_vaddr;
+               sc->its_ptabs[tn].ptab_pgsz = PAGE_SIZE;
+               sc->its_ptabs[tn].ptab_npages = npages;
+
+               ptab_paddr = vtophys(ptab_vaddr);
+               KASSERT((ptab_paddr & GITS_BASER_PA_MASK) == ptab_paddr,
+                   ("%s: Unaligned PA for Interrupt Translation Table",
+                   device_get_name(sc->dev)));
+
+               /* Set defaults: WAWB, IS */
+               cache = GITS_BASER_CACHE_WAWB;
+               share = GITS_BASER_SHARE_IS;
+
+               for (;;) {
+                       nitspages = howmany(its_tbl_size, page_size);
+
+                       switch (page_size) {
+                       case PAGE_SIZE:         /* 4KB */
+                               psz = GITS_BASER_PSZ_4K;
+                               break;
+                       case PAGE_SIZE_16K:     /* 16KB */
+                               psz = GITS_BASER_PSZ_4K;
+                               break;
+                       case PAGE_SIZE_64K:     /* 64KB */
+                               psz = GITS_BASER_PSZ_64K;
+                               break;
+                       default:
+                               device_printf(sc->dev,
+                                   "Unsupported page size: %zuKB\n",
+                                   (page_size / 1024));
+                               its_free_tables(sc);
+                               return (ENXIO);
+                       }
+
+                       /* Clear fields under modification first */
+                       gits_baser &= ~(GITS_BASER_VALID |
+                           GITS_BASER_CACHE_MASK | GITS_BASER_TYPE_MASK |
+                           GITS_BASER_ESIZE_MASK | GITS_BASER_PA_MASK |
+                           GITS_BASER_SHARE_MASK | GITS_BASER_PSZ_MASK |
+                           GITS_BASER_SIZE_MASK);
+                       /* Construct register value */
+                       gits_baser |=
+                           (type << GITS_BASER_TYPE_SHIFT) |
+                           ((esize - 1) << GITS_BASER_ESIZE_SHIFT) |
+                           (cache << GITS_BASER_CACHE_SHIFT) |
+                           (share << GITS_BASER_SHARE_SHIFT) |
+                           (psz << GITS_BASER_PSZ_SHIFT) |
+                           ptab_paddr | (nitspages - 1) |
+                           GITS_BASER_VALID;
+
+                       gic_its_write(sc, 8, GITS_BASER(tn), gits_baser);
+                       /*
+                        * Verify.
+                        * Depending on implementation we may encounter
+                        * shareability and page size mismatch.
+                        */
+                       gits_tmp = gic_its_read(sc, 8, GITS_BASER(tn));
+                       if (((gits_tmp ^ gits_baser) & GITS_BASER_SHARE_MASK) 
!= 0) {
+                               share = gits_tmp & GITS_BASER_SHARE_MASK;
+                               share >>= GITS_BASER_SHARE_SHIFT;
+                               continue;
+                       }
+
+                       if (((gits_tmp ^ gits_baser) & GITS_BASER_PSZ_MASK) != 
0) {
+                               switch (page_size) {
+                               case PAGE_SIZE_16K:
+                                       /* Drop to 4KB page */
+                                       page_size = PAGE_SIZE;
+                                       continue;
+                               case PAGE_SIZE_64K:
+                                       /* Drop to 16KB page */
+                                       page_size = PAGE_SIZE_16K;
+                                       continue;
+                               }
+                       }
+                       /*
+                        * All possible adjustments should
+                        * be applied by now so just break the loop.
+                        */
+                       break;
+               }
+               /*
+                * Do not compare Cacheability field since
+                * it is implementation defined.
+                */
+               gits_tmp &= ~GITS_BASER_CACHE_MASK;
+               gits_baser &= ~GITS_BASER_CACHE_MASK;
+
+               if (gits_tmp != gits_baser) {
+                       device_printf(sc->dev,
+                           "Could not allocate ITS tables\n");
+                       its_free_tables(sc);
+                       return (ENXIO);
+               }
+
+               if (bootverbose) {
+                       if (first) {
+                               device_printf(sc->dev,
+                                   "Allocated ITS private tables:\n");
+                               first = FALSE;
+                       }
+                       device_printf(sc->dev,
+                           "\tPTAB%zu for %s: PA 0x%lx,"
+                           " %lu entries,"
+                           " cache policy %s, %s shareable,"
+                           " page size %zuKB\n",
+                           tn, its_ptab_type[type], ptab_paddr,
+                           (page_size * nitspages) / esize,
+                           its_ptab_cache[cache], its_ptab_share[share],
+                           page_size / 1024);
+               }
+       }
+
+       return (0);
+}
+
+static void
+its_free_tables(struct gic_v3_its_softc *sc)
+{
+       vm_offset_t ptab_vaddr;
+       size_t size;
+       size_t tn;
+
+       for (tn = 0; tn < GITS_BASER_NUM; tn++) {
+               ptab_vaddr = sc->its_ptabs[tn].ptab_vaddr;
+               if (ptab_vaddr == 0)
+                       continue;
+               size = sc->its_ptabs[tn].ptab_pgsz;
+               size *= sc->its_ptabs[tn].ptab_npages;
+
+               if ((void *)ptab_vaddr != NULL)
+                       contigfree((void *)ptab_vaddr, size, M_GIC_V3_ITS);
+
+               /* Clear the table description */
+               memset(&sc->its_ptabs[tn], 0, sizeof(sc->its_ptabs[tn]));
+       }
+}
+
+static void
+its_init_commandq(struct gic_v3_its_softc *sc)
+{
+       uint64_t gits_cbaser, gits_tmp;
+       uint64_t cache, share;
+       vm_paddr_t cmdq_paddr;
+       device_t dev;
+
+       dev = sc->dev;
+       /* Allocate memory for command queue */
+       sc->its_cmdq_base = contigmalloc(ITS_CMDQ_SIZE, M_GIC_V3_ITS,
+           (M_WAITOK | M_ZERO), 0, ~0UL, ITS_CMDQ_SIZE, 0);
+       /* Set command queue write pointer (command queue empty) */
+       sc->its_cmdq_write = sc->its_cmdq_base;
+
+       /* Save command queue pointer and attributes */
+       cmdq_paddr = vtophys(sc->its_cmdq_base);
+
+       /* Set defaults: Normal Inner WAWB, IS */
+       cache = GITS_CBASER_CACHE_NIWAWB;
+       share = GITS_CBASER_SHARE_IS;
+
+       gits_cbaser = (cmdq_paddr |
+           (cache << GITS_CBASER_CACHE_SHIFT) |
+           (share << GITS_CBASER_SHARE_SHIFT) |
+           /* Number of 4KB pages - 1 */
+           ((ITS_CMDQ_SIZE / PAGE_SIZE) - 1) |
+           /* Valid bit */
+           GITS_CBASER_VALID);
+
+       gic_its_write(sc, 8, GITS_CBASER, gits_cbaser);
+       gits_tmp = gic_its_read(sc, 8, GITS_CBASER);
+
+       if (((gits_tmp ^ gits_cbaser) & GITS_CBASER_SHARE_MASK) != 0) {
+               if (bootverbose) {
+                       device_printf(dev,
+                           "Will use cache flushing for commands queue\n");
+               }
+               /* Command queue needs cache flushing */
+               sc->its_flags |= ITS_FLAGS_CMDQ_FLUSH;
+       }
+
+       gic_its_write(sc, 8, GITS_CWRITER, 0x0);
+}
+
+static int
+its_init_cpu(struct gic_v3_its_softc *sc)
+{
+       device_t parent;
+       struct gic_v3_softc *gic_sc;
+
+       /*
+        * Check for LPIs support on this Re-Distributor.
+        */
+       parent = device_get_parent(sc->dev);
+       gic_sc = device_get_softc(parent);
+       if ((gic_r_read(gic_sc, 4, GICR_TYPER) & GICR_TYPER_PLPIS) == 0) {
+               if (bootverbose) {
+                       device_printf(sc->dev,
+                           "LPIs not supported on CPU%u\n", PCPU_GET(cpuid));
+               }
+               return (ENXIO);
+       }
+
+       /* Initialize LPIs for this CPU */
+       lpi_init_cpu(sc);
+
+       /* Initialize collections */
+       its_init_cpu_collection(sc);
+
+       return (0);
+}
+
+static void
+its_init_cpu_collection(struct gic_v3_its_softc *sc)
+{
+       device_t parent;
+       struct gic_v3_softc *gic_sc;
+       uint64_t typer;
+       uint64_t target;
+       vm_offset_t redist_base;
+       u_int cpuid;
+
+       cpuid = PCPU_GET(cpuid);
+       parent = device_get_parent(sc->dev);
+       gic_sc = device_get_softc(parent);
+
+       typer = gic_its_read(sc, 8, GITS_TYPER);
+       if ((typer & GITS_TYPER_PTA) != 0) {
+               redist_base =
+                   rman_get_bushandle(gic_sc->gic_redists.pcpu[cpuid]);
+               /*
+                * Target Address correspond to the base physical
+                * address of Re-Distributors.
+                */
+               target = vtophys(redist_base);
+       } else {
+               /* Target Address correspond to unique processor numbers */
+               typer = gic_r_read(gic_sc, 8, GICR_TYPER);
+               target = GICR_TYPER_CPUNUM(typer);
+       }
+
+       sc->its_cols[cpuid].col_target = target;
+       sc->its_cols[cpuid].col_id = cpuid;
+
+       its_cmd_mapc(sc, &sc->its_cols[cpuid], 1);
+       its_cmd_invall(sc, &sc->its_cols[cpuid]);
+}
+
+static void
+lpi_init_conftable(struct gic_v3_its_softc *sc)
+{
+       device_t parent;
+       struct gic_v3_softc *gic_sc;
+       vm_offset_t conf_base;
+       uint8_t prio_default;
+
+       parent = device_get_parent(sc->dev);
+       gic_sc = device_get_softc(parent);
+       /*
+        * LPI Configuration Table settings.
+        * Notice that Configuration Table is shared among all
+        * Re-Distributors, so this is going to be created just once.
+        */
+       conf_base = (vm_offset_t)contigmalloc(LPI_CONFTAB_SIZE,
+           M_GIC_V3_ITS, (M_WAITOK | M_ZERO), 0, ~0UL, PAGE_SIZE_64K, 0);
+
+       if (bootverbose) {
+               device_printf(sc->dev,
+                   "LPI Configuration Table at PA: 0x%lx\n",
+                   vtophys(conf_base));
+       }
+
+       /*
+        * Let the default priority be aligned with all other
+        * interrupts assuming that each interrupt is assigned
+        * MAX priority at startup. MAX priority on the other
+        * hand cannot be higher than 0xFC for LPIs.
+        */
+       prio_default = GIC_PRIORITY_MAX;
+
+       /* Write each settings byte to LPI configuration table */
+       memset((void *)conf_base,
+           (prio_default & LPI_CONF_PRIO_MASK) | LPI_CONF_GROUP1,
+           LPI_CONFTAB_SIZE);
+
+       cpu_dcache_wb_range((vm_offset_t)conf_base, roundup2(LPI_CONFTAB_SIZE,
+           PAGE_SIZE_64K));
+
+       gic_sc->gic_redists.lpis.conf_base = conf_base;
+}
+
+static void
+lpi_init_cpu(struct gic_v3_its_softc *sc)
+{
+       device_t parent;
+       struct gic_v3_softc *gic_sc;
+       vm_offset_t pend_base;
+       u_int cpuid;
+
+       parent = device_get_parent(sc->dev);
+       gic_sc = device_get_softc(parent);
+
+       /*
+        * LPI Pending Table settings.
+        * This has to be done for each Re-Distributor, hence for each CPU.
+        */
+       cpuid = PCPU_GET(cpuid);
+
+       pend_base = (vm_offset_t)contigmalloc(
+           roundup2(LPI_PENDTAB_SIZE, PAGE_SIZE_64K), M_GIC_V3_ITS,
+           (M_WAITOK | M_ZERO), 0, ~0UL, PAGE_SIZE_64K, 0);
+
+       /* Clean D-cache so that ITS can see zeroed pages */
+       cpu_dcache_wb_range((vm_offset_t)pend_base,
+           roundup2(LPI_PENDTAB_SIZE, PAGE_SIZE_64K));
+
+       if (bootverbose) {
+               device_printf(sc->dev,
+                   "LPI Pending Table for CPU%u at PA: 0x%lx\n",
+                   cpuid, vtophys(pend_base));
+       }
+
+       gic_sc->gic_redists.lpis.pend_base[cpuid] = pend_base;
+
+       lpi_config_cpu(sc);
+}
+
+static int
+lpi_config_cpu(struct gic_v3_its_softc *sc)
+{
+       device_t parent;
+       struct gic_v3_softc *gic_sc;
+       vm_offset_t conf_base, pend_base;
+       uint64_t gicr_xbaser, gicr_temp;
+       uint64_t cache, share, idbits;
+       uint32_t gicr_ctlr;
+       u_int cpuid;
+
+       parent = device_get_parent(sc->dev);
+       gic_sc = device_get_softc(parent);
+       cpuid = PCPU_GET(cpuid);
+
+       conf_base = gic_sc->gic_redists.lpis.conf_base;
+       pend_base = gic_sc->gic_redists.lpis.pend_base[cpuid];
+
+       /* Disable LPIs */
+       gicr_ctlr = gic_r_read(gic_sc, 4, GICR_CTLR);
+       gicr_ctlr &= ~GICR_CTLR_LPI_ENABLE;
+       gic_r_write(gic_sc, 4, GICR_CTLR, gicr_ctlr);
+       /* Perform full system barrier */
+       dsb(sy);
+
+       /*
+        * Set GICR_PROPBASER
+        */
+
+       /*
+        * Find out how many bits do we need for LPI identifiers.
+        * Remark 1.: Even though we have (LPI_CONFTAB_SIZE / 8) LPIs
+        *            the notified LPI ID still starts from 8192
+        *            (GIC_FIRST_LPI).
+        * Remark 2.: This could be done on compilation time but there
+        *            seems to be no sufficient macro.
+        */
+       idbits = flsl(LPI_CONFTAB_SIZE + GIC_FIRST_LPI) - 1;
+
+       /* Set defaults: Normal Inner WAWB, IS */
+       cache = GICR_PROPBASER_CACHE_NIWAWB;
+       share = GICR_PROPBASER_SHARE_IS;
+
+       gicr_xbaser = vtophys(conf_base) |
+           ((idbits - 1) & GICR_PROPBASER_IDBITS_MASK) |
+           (cache << GICR_PROPBASER_CACHE_SHIFT) |
+           (share << GICR_PROPBASER_SHARE_SHIFT);
+
+       gic_r_write(gic_sc, 8, GICR_PROPBASER, gicr_xbaser);
+       gicr_temp = gic_r_read(gic_sc, 8, GICR_PROPBASER);
+
+       if (((gicr_xbaser ^ gicr_temp) & GICR_PROPBASER_SHARE_MASK) != 0) {
+               if (bootverbose) {
+                       device_printf(sc->dev,
+                           "Will use cache flushing for LPI "
+                           "Configuration Table\n");
+               }
+               gic_sc->gic_redists.lpis.flags |= LPI_FLAGS_CONF_FLUSH;
+       }
+
+       /*
+        * Set GICR_PENDBASER
+        */
+
+       /* Set defaults: Normal Inner WAWB, IS */
+       cache = GICR_PENDBASER_CACHE_NIWAWB;
+       share = GICR_PENDBASER_SHARE_IS;
+
+       gicr_xbaser = vtophys(pend_base) |
+           (cache << GICR_PENDBASER_CACHE_SHIFT) |
+           (share << GICR_PENDBASER_SHARE_SHIFT);
+
+       gic_r_write(gic_sc, 8, GICR_PENDBASER, gicr_xbaser);
+
+       /* Enable LPIs */
+       gicr_ctlr = gic_r_read(gic_sc, 4, GICR_CTLR);
+       gicr_ctlr |= GICR_CTLR_LPI_ENABLE;
+       gic_r_write(gic_sc, 4, GICR_CTLR, gicr_ctlr);
+
+       dsb(sy);
+

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to