Author: andrew
Date: Fri Apr 15 14:19:25 2016
New Revision: 298051
URL: https://svnweb.freebsd.org/changeset/base/298051

Log:
  Add initial GICv2m support to the arm GIC driver. This will be used to
  support MSI and MSI-X interrupts, however intrng needs updates before this
  can happen.
  
  For now we just attach the driver until the MSI API is ready.
  
  Obtained from:        ABT Systems Ltd
  Sponsored by: The FreeBSD Foundation
  Differential Revision:        https://reviews.freebsd.org/D5950

Modified:
  head/sys/arm/arm/gic.c

Modified: head/sys/arm/arm/gic.c
==============================================================================
--- head/sys/arm/arm/gic.c      Fri Apr 15 13:34:59 2016        (r298050)
+++ head/sys/arm/arm/gic.c      Fri Apr 15 14:19:25 2016        (r298051)
@@ -134,6 +134,19 @@ u_int sgi_first_unused = GIC_FIRST_SGI;
 #endif
 #endif
 
+#ifdef ARM_INTRNG
+struct arm_gic_range {
+       uint64_t bus;
+       uint64_t host;
+       uint64_t size;
+};
+
+struct arm_gic_devinfo {
+       struct ofw_bus_devinfo  obdinfo;
+       struct resource_list    rl;
+};
+#endif
+
 struct arm_gic_softc {
        device_t                gic_dev;
 #ifdef ARM_INTRNG
@@ -151,6 +164,14 @@ struct arm_gic_softc {
 #ifdef GIC_DEBUG_SPURIOUS
        uint32_t                last_irq[MAXCPU];
 #endif
+
+#ifdef ARM_INTRNG
+       /* FDT child data */
+       pcell_t                 addr_cells;
+       pcell_t                 size_cells;
+       int                     nranges;
+       struct arm_gic_range *  ranges;
+#endif
 };
 
 #ifdef ARM_INTRNG
@@ -195,6 +216,7 @@ static struct ofw_compat_data compat_dat
        {"arm,cortex-a7-gic",   true},
        {"arm,arm11mp-gic",     true},
        {"brcm,brahma-b15-gic", true},
+       {"qcom,msm-qgic2",      true},
        {NULL,                  false}
 };
 
@@ -437,6 +459,107 @@ arm_gic_register_isrcs(struct arm_gic_so
        sc->nirqs = num;
        return (0);
 }
+
+static int
+arm_gic_fill_ranges(phandle_t node, struct arm_gic_softc *sc)
+{
+       pcell_t host_cells;
+       cell_t *base_ranges;
+       ssize_t nbase_ranges;
+       int i, j, k;
+
+       host_cells = 1;
+       OF_getencprop(OF_parent(node), "#address-cells", &host_cells,
+           sizeof(host_cells));
+       sc->addr_cells = 2;
+       OF_getencprop(node, "#address-cells", &sc->addr_cells,
+           sizeof(sc->addr_cells));
+       sc->size_cells = 2;
+       OF_getencprop(node, "#size-cells", &sc->size_cells,
+           sizeof(sc->size_cells));
+
+       nbase_ranges = OF_getproplen(node, "ranges");
+       if (nbase_ranges < 0)
+               return (-1);
+       sc->nranges = nbase_ranges / sizeof(cell_t) /
+           (sc->addr_cells + host_cells + sc->size_cells);
+       if (sc->nranges == 0)
+               return (0);
+
+       sc->ranges = malloc(sc->nranges * sizeof(sc->ranges[0]),
+           M_DEVBUF, M_WAITOK);
+       base_ranges = malloc(nbase_ranges, M_DEVBUF, M_WAITOK);
+       OF_getencprop(node, "ranges", base_ranges, nbase_ranges);
+
+       for (i = 0, j = 0; i < sc->nranges; i++) {
+               sc->ranges[i].bus = 0;
+               for (k = 0; k < sc->addr_cells; k++) {
+                       sc->ranges[i].bus <<= 32;
+                       sc->ranges[i].bus |= base_ranges[j++];
+               }
+               sc->ranges[i].host = 0;
+               for (k = 0; k < host_cells; k++) {
+                       sc->ranges[i].host <<= 32;
+                       sc->ranges[i].host |= base_ranges[j++];
+               }
+               sc->ranges[i].size = 0;
+               for (k = 0; k < sc->size_cells; k++) {
+                       sc->ranges[i].size <<= 32;
+                       sc->ranges[i].size |= base_ranges[j++];
+               }
+       }
+
+       free(base_ranges, M_DEVBUF);
+       return (sc->nranges);
+}
+
+static bool
+arm_gic_add_children(device_t dev)
+{
+       struct arm_gic_softc *sc;
+       struct arm_gic_devinfo *dinfo;
+       phandle_t child, node;
+       device_t cdev;
+
+       sc = device_get_softc(dev);
+       node = ofw_bus_get_node(dev);
+
+       /* If we have no children don't probe for them */
+       child = OF_child(node);
+       if (child == 0)
+               return (false);
+
+       if (arm_gic_fill_ranges(node, sc) < 0) {
+               device_printf(dev, "Have a child, but no ranges\n");
+               return (false);
+       }
+
+       for (; child != 0; child = OF_peer(child)) {
+               dinfo = malloc(sizeof(*dinfo), M_DEVBUF, M_WAITOK | M_ZERO);
+
+               if (ofw_bus_gen_setup_devinfo(&dinfo->obdinfo, child) != 0) {
+                       free(dinfo, M_DEVBUF);
+                       continue;
+               }
+
+               resource_list_init(&dinfo->rl);
+               ofw_bus_reg_to_rl(dev, child, sc->addr_cells,
+                   sc->size_cells, &dinfo->rl);
+
+               cdev = device_add_child(dev, NULL, -1);
+               if (cdev == NULL) {
+                       device_printf(dev, "<%s>: device_add_child failed\n",
+                           dinfo->obdinfo.obd_name);
+                       resource_list_free(&dinfo->rl);
+                       ofw_bus_gen_destroy_devinfo(&dinfo->obdinfo);
+                       free(dinfo, M_DEVBUF);
+                       continue;
+               }
+               device_set_ivars(cdev, dinfo);
+       }
+
+       return (true);
+}
 #endif
 
 static int
@@ -578,6 +701,13 @@ arm_gic_attach(device_t dev)
        }
 
        OF_device_register_xref(xref, dev);
+
+       /* If we have children probe and attach them */
+       if (arm_gic_add_children(dev)) {
+               bus_generic_probe(dev);
+               return (bus_generic_attach(dev));
+       }
+
        return (0);
 
 cleanup:
@@ -592,6 +722,75 @@ cleanup:
 }
 
 #ifdef ARM_INTRNG
+static struct resource *
+arm_gic_alloc_resource(device_t bus, device_t child, int type, int *rid,
+    rman_res_t start, rman_res_t end, rman_res_t count, u_int flags)
+{
+       struct arm_gic_softc *sc;
+       struct arm_gic_devinfo *di;
+       struct resource_list_entry *rle;
+       int j;
+
+       KASSERT(type == SYS_RES_MEMORY, ("Invalid resoure type %x", type));
+
+       sc = device_get_softc(bus);
+
+       /*
+        * Request for the default allocation with a given rid: use resource
+        * list stored in the local device info.
+        */
+       if (RMAN_IS_DEFAULT_RANGE(start, end)) {
+               if ((di = device_get_ivars(child)) == NULL)
+                       return (NULL);
+
+               if (type == SYS_RES_IOPORT)
+                       type = SYS_RES_MEMORY;
+
+               rle = resource_list_find(&di->rl, type, *rid);
+               if (rle == NULL) {
+                       if (bootverbose)
+                               device_printf(bus, "no default resources for "
+                                   "rid = %d, type = %d\n", *rid, type);
+                       return (NULL);
+               }
+               start = rle->start;
+               end = rle->end;
+               count = rle->count;
+       }
+
+       /* Remap through ranges property */
+       for (j = 0; j < sc->nranges; j++) {
+               if (start >= sc->ranges[j].bus && end <
+                   sc->ranges[j].bus + sc->ranges[j].size) {
+                       start -= sc->ranges[j].bus;
+                       start += sc->ranges[j].host;
+                       end -= sc->ranges[j].bus;
+                       end += sc->ranges[j].host;
+                       break;
+               }
+       }
+       if (j == sc->nranges && sc->nranges != 0) {
+               if (bootverbose)
+                       device_printf(bus, "Could not map resource "
+                           "%#jx-%#jx\n", (uintmax_t)start, (uintmax_t)end);
+
+               return (NULL);
+       }
+
+       return (bus_generic_alloc_resource(bus, child, type, rid, start, end,
+           count, flags));
+}
+
+static const struct ofw_bus_devinfo *
+arm_gic_ofw_get_devinfo(device_t bus __unused, device_t child)
+{
+       struct arm_gic_devinfo *di;
+
+       di = device_get_ivars(child);
+
+       return (&di->obdinfo);
+}
+
 static int
 arm_gic_intr(void *arg)
 {
@@ -1231,7 +1430,22 @@ static device_method_t arm_gic_methods[]
        /* Device interface */
        DEVMETHOD(device_probe,         arm_gic_probe),
        DEVMETHOD(device_attach,        arm_gic_attach),
+
 #ifdef ARM_INTRNG
+       /* Bus interface */
+       DEVMETHOD(bus_add_child,        bus_generic_add_child),
+       DEVMETHOD(bus_alloc_resource,   arm_gic_alloc_resource),
+       DEVMETHOD(bus_release_resource, bus_generic_release_resource),
+       DEVMETHOD(bus_activate_resource,bus_generic_activate_resource),
+
+       /* ofw_bus interface */
+       DEVMETHOD(ofw_bus_get_devinfo,  arm_gic_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),
+
        /* Interrupt controller interface */
        DEVMETHOD(pic_disable_intr,     arm_gic_disable_intr),
        DEVMETHOD(pic_enable_intr,      arm_gic_enable_intr),
@@ -1263,3 +1477,89 @@ EARLY_DRIVER_MODULE(gic, simplebus, arm_
     BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
 EARLY_DRIVER_MODULE(gic, ofwbus, arm_gic_driver, arm_gic_devclass, 0, 0,
     BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
+
+#ifdef ARM_INTRNG
+/*
+ * GICv2m support -- the GICv2 MSI/MSI-X controller.
+ */
+
+#define        GICV2M_MSI_TYPER        0x008
+#define         MSI_TYPER_SPI_BASE(x)  (((x) >> 16) & 0x3ff)
+#define         MSI_TYPER_SPI_COUNT(x) (((x) >> 0) & 0x3ff)
+#define        GICv2M_MSI_SETSPI_NS    0x040
+#define        GICV2M_MSI_IIDR         0xFCC
+
+struct arm_gicv2m_softc {
+       struct resource *sc_mem;
+       struct mtx      sc_mutex;
+       u_int           sc_spi_start;
+       u_int           sc_spi_count;
+       u_int           sc_spi_offset;
+};
+
+static struct ofw_compat_data gicv2m_compat_data[] = {
+       {"arm,gic-v2m-frame",   true},
+       {NULL,                  false}
+};
+
+static int
+arm_gicv2m_probe(device_t dev)
+{
+
+       if (!ofw_bus_status_okay(dev))
+               return (ENXIO);
+
+       if (!ofw_bus_search_compatible(dev, gicv2m_compat_data)->ocd_data)
+               return (ENXIO);
+
+       device_set_desc(dev, "ARM Generic Interrupt Controller MSI/MSIX");
+       return (BUS_PROBE_DEFAULT);
+}
+
+static int
+arm_gicv2m_attach(device_t dev)
+{
+       struct arm_gicv2m_softc *sc;
+       uint32_t typer;
+       int rid;
+
+       sc = device_get_softc(dev);
+
+       rid = 0;
+       sc->sc_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+           RF_ACTIVE);
+       if (sc->sc_mem == NULL) {
+               device_printf(dev, "Unable to allocate resources\n");
+               return (ENXIO);
+       }
+
+       typer = bus_read_4(sc->sc_mem, GICV2M_MSI_TYPER);
+       sc->sc_spi_start = MSI_TYPER_SPI_BASE(typer);
+       sc->sc_spi_count = MSI_TYPER_SPI_COUNT(typer);
+
+       mtx_init(&sc->sc_mutex, "GICv2m lock", "", MTX_DEF);
+
+       if (bootverbose)
+               device_printf(dev, "using spi %u to %u\n", sc->sc_spi_start,
+                   sc->sc_spi_start + sc->sc_spi_count - 1);
+
+       return (0);
+}
+
+static device_method_t arm_gicv2m_methods[] = {
+       /* Device interface */
+       DEVMETHOD(device_probe,         arm_gicv2m_probe),
+       DEVMETHOD(device_attach,        arm_gicv2m_attach),
+
+       /* End */
+       DEVMETHOD_END
+};
+
+DEFINE_CLASS_0(gicv2m, arm_gicv2m_driver, arm_gicv2m_methods,
+    sizeof(struct arm_gicv2m_softc));
+
+static devclass_t arm_gicv2m_devclass;
+
+EARLY_DRIVER_MODULE(gicv2m, gic, arm_gicv2m_driver,
+    arm_gicv2m_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
+#endif
_______________________________________________
svn-src-head@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to