svn commit: r285213 - in head/sys: arm64/arm64 arm64/include conf
Zbigniew Bodek
zbb at FreeBSD.org
Mon Jul 6 18:27:43 UTC 2015
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 ***
More information about the svn-src-all
mailing list