aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/bhnd
diff options
context:
space:
mode:
authorLandon J. Fuller <landonf@FreeBSD.org>2017-11-21 23:15:20 +0000
committerLandon J. Fuller <landonf@FreeBSD.org>2017-11-21 23:15:20 +0000
commitcaeff9a3c2626660d3e080d4d3b35bc53ec4417f (patch)
treeda61f12c95a2dad1fc6d62a43b769e317eb5cc7f /sys/dev/bhnd
parent0a9cc964a1eae66f242631c1ef566560ab00aae2 (diff)
downloadsrc-caeff9a3c2626660d3e080d4d3b35bc53ec4417f.tar.gz
src-caeff9a3c2626660d3e080d4d3b35bc53ec4417f.zip
bhnd(4): implement MIPS and PCI(e) interrupt support
On BHND MIPS SoCs, this replaces the use of hard-coded MIPS IRQ#s in the common bhnd(4) core drivers; we now register an INTRNG child PIC that handles routing of backplane interrupt vectors via the MIPS core. On BHND PCI devices, backplane interrupt vectors are now routed to the PCI/PCIe host bridge core when bus_setup_intr() is called, where they are dispatched by the PCI core via a host interrupt (e.g. INTx/MSI). The bhndb(4) bridge driver tracks registered interrupt handlers for the bridged bhnd(4) devices and manages backplane interrupt routing, while delegating actual bus interrupt setup/teardown to the parent bus on behalf of the bridged cores. Approved by: adrian (mentor, implicit) Sponsored by: The FreeBSD Foundation Differential Revision: https://reviews.freebsd.org/D12518
Notes
Notes: svn path=/head/; revision=326079
Diffstat (limited to 'sys/dev/bhnd')
-rw-r--r--sys/dev/bhnd/bcma/bcma.c97
-rw-r--r--sys/dev/bhnd/bcma/bcma_subr.c298
-rw-r--r--sys/dev/bhnd/bcma/bcmavar.h41
-rw-r--r--sys/dev/bhnd/bhnd.c18
-rw-r--r--sys/dev/bhnd/bhnd.h63
-rw-r--r--sys/dev/bhnd/bhnd_bus_if.m223
-rw-r--r--sys/dev/bhnd/bhnd_ids.h8
-rw-r--r--sys/dev/bhnd/bhnd_match.h39
-rw-r--r--sys/dev/bhnd/bhnd_subr.c14
-rw-r--r--sys/dev/bhnd/bhndb/bhnd_bhndb.c48
-rw-r--r--sys/dev/bhnd/bhndb/bhndb.c341
-rw-r--r--sys/dev/bhnd/bhndb/bhndb_if.m50
-rw-r--r--sys/dev/bhnd/bhndb/bhndb_pci.c484
-rw-r--r--sys/dev/bhnd/bhndb/bhndb_pcireg.h1
-rw-r--r--sys/dev/bhnd/bhndb/bhndb_pcivar.h88
-rw-r--r--sys/dev/bhnd/bhndb/bhndb_private.h39
-rw-r--r--sys/dev/bhnd/bhndb/bhndb_subr.c309
-rw-r--r--sys/dev/bhnd/bhndb/bhndbvar.h18
-rw-r--r--sys/dev/bhnd/bhndvar.h6
-rw-r--r--sys/dev/bhnd/cores/chipc/chipc.c61
-rw-r--r--sys/dev/bhnd/cores/chipc/chipc_private.h13
-rw-r--r--sys/dev/bhnd/cores/chipc/chipc_subr.c74
-rw-r--r--sys/dev/bhnd/cores/chipc/chipcvar.h8
-rw-r--r--sys/dev/bhnd/cores/pci/bhnd_pci_hostb.c4
-rw-r--r--sys/dev/bhnd/cores/usb/bhnd_usb.c153
-rw-r--r--sys/dev/bhnd/cores/usb/bhnd_usbvar.h3
-rw-r--r--sys/dev/bhnd/siba/siba.c236
-rw-r--r--sys/dev/bhnd/siba/siba_bhndb.c95
-rw-r--r--sys/dev/bhnd/siba/siba_erom.c61
-rw-r--r--sys/dev/bhnd/siba/siba_subr.c262
-rw-r--r--sys/dev/bhnd/siba/sibareg.h11
-rw-r--r--sys/dev/bhnd/siba/sibavar.h67
32 files changed, 2465 insertions, 768 deletions
diff --git a/sys/dev/bhnd/bcma/bcma.c b/sys/dev/bhnd/bcma/bcma.c
index 1915d173dcce..59f944786ddf 100644
--- a/sys/dev/bhnd/bcma/bcma.c
+++ b/sys/dev/bhnd/bcma/bcma.c
@@ -1,7 +1,11 @@
/*-
- * Copyright (c) 2015 Landon Fuller <landon@landonf.org>
+ * Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org>
+ * Copyright (c) 2017 The FreeBSD Foundation
* All rights reserved.
*
+ * Portions of this software were developed by Landon Fuller
+ * under sponsorship from the FreeBSD Foundation.
+ *
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@@ -124,7 +128,7 @@ bcma_child_deleted(device_t dev, device_t child)
/* Free bcma device info */
if ((dinfo = device_get_ivars(child)) != NULL)
- bcma_free_dinfo(dev, dinfo);
+ bcma_free_dinfo(dev, child, dinfo);
device_set_ivars(child, NULL);
}
@@ -613,66 +617,46 @@ bcma_get_region_addr(device_t dev, device_t child, bhnd_port_type port_type,
/**
* Default bcma(4) bus driver implementation of BHND_BUS_GET_INTR_COUNT().
- *
- * This implementation consults @p child's agent register block,
- * returning the number of interrupt output lines routed to @p child.
*/
-int
+u_int
bcma_get_intr_count(device_t dev, device_t child)
{
- struct bcma_devinfo *dinfo;
- uint32_t dmpcfg, oobw;
-
- dinfo = device_get_ivars(child);
-
- /* Agent block must be mapped */
- if (dinfo->res_agent == NULL)
- return (0);
+ struct bcma_devinfo *dinfo;
- /* Agent must support OOB */
- dmpcfg = bhnd_bus_read_4(dinfo->res_agent, BCMA_DMP_CONFIG);
- if (!BCMA_DMP_GET_FLAG(dmpcfg, BCMA_DMP_CFG_OOB))
- return (0);
+ /* delegate non-bus-attached devices to our parent */
+ if (device_get_parent(child) != dev)
+ return (BHND_BUS_GET_INTR_COUNT(device_get_parent(dev), child));
- /* Return OOB width as interrupt count */
- oobw = bhnd_bus_read_4(dinfo->res_agent,
- BCMA_DMP_OOB_OUTWIDTH(BCMA_OOB_BANK_INTR));
- if (oobw > BCMA_OOB_NUM_SEL) {
- device_printf(dev, "ignoring invalid OOBOUTWIDTH for core %u: "
- "%#x\n", BCMA_DINFO_COREIDX(dinfo), oobw);
- return (0);
- }
-
- return (oobw);
+ dinfo = device_get_ivars(child);
+ return (dinfo->num_intrs);
}
/**
- * Default bcma(4) bus driver implementation of BHND_BUS_GET_CORE_IVEC().
- *
- * This implementation consults @p child's agent register block,
- * returning the interrupt output line routed to @p child, at OOB selector
- * @p intr.
+ * Default bcma(4) bus driver implementation of BHND_BUS_GET_INTR_IVEC().
*/
int
-bcma_get_core_ivec(device_t dev, device_t child, u_int intr, uint32_t *ivec)
+bcma_get_intr_ivec(device_t dev, device_t child, u_int intr, u_int *ivec)
{
struct bcma_devinfo *dinfo;
- uint32_t oobsel;
+ struct bcma_intr *desc;
- dinfo = device_get_ivars(child);
+ /* delegate non-bus-attached devices to our parent */
+ if (device_get_parent(child) != dev) {
+ return (BHND_BUS_GET_INTR_IVEC(device_get_parent(dev), child,
+ intr, ivec));
+ }
- /* Interrupt ID must be valid. */
- if (intr >= bcma_get_intr_count(dev, child))
- return (ENXIO);
+ dinfo = device_get_ivars(child);
- /* Fetch OOBSEL busline value */
- KASSERT(dinfo->res_agent != NULL, ("missing agent registers"));
- oobsel = bhnd_bus_read_4(dinfo->res_agent, BCMA_DMP_OOBSELOUT(
- BCMA_OOB_BANK_INTR, intr));
- *ivec = (oobsel >> BCMA_DMP_OOBSEL_SHIFT(intr)) &
- BCMA_DMP_OOBSEL_BUSLINE_MASK;
+ STAILQ_FOREACH(desc, &dinfo->intrs, i_link) {
+ if (desc->i_sel == intr) {
+ *ivec = desc->i_busline;
+ return (0);
+ }
+ }
- return (0);
+ /* Not found */
+ return (ENXIO);
}
/**
@@ -707,8 +691,6 @@ bcma_add_children(device_t bus)
/* Add all cores. */
bcma_erom = (struct bcma_erom *)erom;
while ((error = bcma_erom_next_corecfg(bcma_erom, &corecfg)) == 0) {
- int nintr;
-
/* Add the child device */
child = BUS_ADD_CHILD(bus, 0, NULL, -1);
if (child == NULL) {
@@ -718,27 +700,12 @@ bcma_add_children(device_t bus)
/* Initialize device ivars */
dinfo = device_get_ivars(child);
- if ((error = bcma_init_dinfo(bus, dinfo, corecfg)))
+ if ((error = bcma_init_dinfo(bus, child, dinfo, corecfg)))
goto cleanup;
/* The dinfo instance now owns the corecfg value */
corecfg = NULL;
- /* Allocate device's agent registers, if any */
- if ((error = bcma_dinfo_alloc_agent(bus, child, dinfo)))
- goto cleanup;
-
- /* Assign interrupts */
- nintr = bhnd_get_intr_count(child);
- for (int rid = 0; rid < nintr; rid++) {
- error = BHND_BUS_ASSIGN_INTR(bus, child, rid);
- if (error) {
- device_printf(bus, "failed to assign interrupt "
- "%d to core %u: %d\n", rid,
- BCMA_DINFO_COREIDX(dinfo), error);
- }
- }
-
/* If pins are floating or the hardware is otherwise
* unpopulated, the device shouldn't be used. */
if (bhnd_is_hw_disabled(child))
@@ -794,7 +761,7 @@ static device_method_t bcma_methods[] = {
DEVMETHOD(bhnd_bus_decode_port_rid, bcma_decode_port_rid),
DEVMETHOD(bhnd_bus_get_region_addr, bcma_get_region_addr),
DEVMETHOD(bhnd_bus_get_intr_count, bcma_get_intr_count),
- DEVMETHOD(bhnd_bus_get_core_ivec, bcma_get_core_ivec),
+ DEVMETHOD(bhnd_bus_get_intr_ivec, bcma_get_intr_ivec),
DEVMETHOD_END
};
diff --git a/sys/dev/bhnd/bcma/bcma_subr.c b/sys/dev/bhnd/bcma/bcma_subr.c
index ff69e5f7e147..8a9670c9a13d 100644
--- a/sys/dev/bhnd/bcma/bcma_subr.c
+++ b/sys/dev/bhnd/bcma/bcma_subr.c
@@ -1,7 +1,11 @@
/*-
- * Copyright (c) 2015 Landon Fuller <landon@landonf.org>
+ * Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org>
+ * Copyright (c) 2017 The FreeBSD Foundation
* All rights reserved.
*
+ * Portions of this software were developed by Landon Fuller
+ * under sponsorship from the FreeBSD Foundation.
+ *
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@@ -154,7 +158,7 @@ bcma_corecfg_get_port_list(struct bcma_corecfg *cfg, bhnd_port_type type)
* @param ports The set of ports to be enumerated
*/
static void
-bcma_dinfo_init_resource_info(device_t bus, struct bcma_devinfo *dinfo,
+bcma_dinfo_init_port_resource_info(device_t bus, struct bcma_devinfo *dinfo,
struct bcma_sport_list *ports)
{
struct bcma_map *map;
@@ -193,6 +197,126 @@ bcma_dinfo_init_resource_info(device_t bus, struct bcma_devinfo *dinfo,
}
+
+/**
+ * Allocate the per-core agent register block for a device info structure.
+ *
+ * If an agent0.0 region is not defined on @p dinfo, the device info
+ * agent resource is set to NULL and 0 is returned.
+ *
+ * @param bus The requesting bus device.
+ * @param child The bcma child device.
+ * @param dinfo The device info associated with @p child
+ *
+ * @retval 0 success
+ * @retval non-zero resource allocation failed.
+ */
+static int
+bcma_dinfo_init_agent(device_t bus, device_t child, struct bcma_devinfo *dinfo)
+{
+ bhnd_addr_t addr;
+ bhnd_size_t size;
+ rman_res_t r_start, r_count, r_end;
+ int error;
+
+ KASSERT(dinfo->res_agent == NULL, ("double allocation of agent"));
+
+ /* Verify that the agent register block exists and is
+ * mappable */
+ if (bhnd_get_port_rid(child, BHND_PORT_AGENT, 0, 0) == -1)
+ return (0); /* nothing to do */
+
+ /* Fetch the address of the agent register block */
+ error = bhnd_get_region_addr(child, BHND_PORT_AGENT, 0, 0,
+ &addr, &size);
+ if (error) {
+ device_printf(bus, "failed fetching agent register block "
+ "address for core %u\n", BCMA_DINFO_COREIDX(dinfo));
+ return (error);
+ }
+
+ /* Allocate the resource */
+ r_start = addr;
+ r_count = size;
+ r_end = r_start + r_count - 1;
+
+ dinfo->rid_agent = BCMA_AGENT_RID(dinfo);
+ dinfo->res_agent = BHND_BUS_ALLOC_RESOURCE(bus, bus, SYS_RES_MEMORY,
+ &dinfo->rid_agent, r_start, r_end, r_count, RF_ACTIVE|RF_SHAREABLE);
+ if (dinfo->res_agent == NULL) {
+ device_printf(bus, "failed allocating agent register block for "
+ "core %u\n", BCMA_DINFO_COREIDX(dinfo));
+ return (ENXIO);
+ }
+
+ return (0);
+}
+
+/**
+ * Populate the list of interrupts for a device info structure
+ * previously initialized via bcma_dinfo_alloc_agent().
+ *
+ * If an agent0.0 region is not mapped on @p dinfo, the OOB interrupt bank is
+ * assumed to be unavailable and 0 is returned.
+ *
+ * @param bus The requesting bus device.
+ * @param dinfo The device info instance to be initialized.
+ */
+static int
+bcma_dinfo_init_intrs(device_t bus, device_t child,
+ struct bcma_devinfo *dinfo)
+{
+ uint32_t dmpcfg, oobw;
+
+ /* Agent block must be mapped */
+ if (dinfo->res_agent == NULL)
+ return (0);
+
+ /* Agent must support OOB */
+ dmpcfg = bhnd_bus_read_4(dinfo->res_agent, BCMA_DMP_CONFIG);
+ if (!BCMA_DMP_GET_FLAG(dmpcfg, BCMA_DMP_CFG_OOB))
+ return (0);
+
+ /* Fetch width of the OOB interrupt bank */
+ oobw = bhnd_bus_read_4(dinfo->res_agent,
+ BCMA_DMP_OOB_OUTWIDTH(BCMA_OOB_BANK_INTR));
+ if (oobw > BCMA_OOB_NUM_SEL) {
+ device_printf(bus, "ignoring invalid OOBOUTWIDTH for core %u: "
+ "%#x\n", BCMA_DINFO_COREIDX(dinfo), oobw);
+ return (0);
+ }
+
+ /* Fetch OOBSEL busline values and populate list of interrupt
+ * descriptors */
+ for (uint32_t sel = 0; sel < oobw; sel++) {
+ struct bcma_intr *intr;
+ uint32_t selout;
+ uint8_t line;
+
+ if (dinfo->num_intrs == UINT_MAX)
+ return (ENOMEM);
+
+ selout = bhnd_bus_read_4(dinfo->res_agent, BCMA_DMP_OOBSELOUT(
+ BCMA_OOB_BANK_INTR, sel));
+
+ line = (selout >> BCMA_DMP_OOBSEL_SHIFT(sel)) &
+ BCMA_DMP_OOBSEL_BUSLINE_MASK;
+
+ intr = bcma_alloc_intr(BCMA_OOB_BANK_INTR, sel, line);
+ if (intr == NULL) {
+ device_printf(bus, "failed allocating interrupt "
+ "descriptor %#x for core %u\n", sel,
+ BCMA_DINFO_COREIDX(dinfo));
+ return (ENOMEM);
+ }
+
+ STAILQ_INSERT_HEAD(&dinfo->intrs, intr, i_link);
+ dinfo->num_intrs++;
+ }
+
+ return (0);
+}
+
/**
* Allocate and return a new empty device info structure.
*
@@ -213,6 +337,9 @@ bcma_alloc_dinfo(device_t bus)
dinfo->res_agent = NULL;
dinfo->rid_agent = -1;
+ STAILQ_INIT(&dinfo->intrs);
+ dinfo->num_intrs = 0;
+
resource_list_init(&dinfo->resources);
return (dinfo);
@@ -224,7 +351,8 @@ bcma_alloc_dinfo(device_t bus)
* configuration.
*
* @param bus The requesting bus device.
- * @param dinfo The device info instance.
+ * @param child The bcma child device.
+ * @param dinfo The device info associated with @p child
* @param corecfg Device core configuration; ownership of this value
* will be assumed by @p dinfo.
*
@@ -232,9 +360,12 @@ bcma_alloc_dinfo(device_t bus)
* @retval non-zero initialization failed.
*/
int
-bcma_init_dinfo(device_t bus, struct bcma_devinfo *dinfo,
+bcma_init_dinfo(device_t bus, device_t child, struct bcma_devinfo *dinfo,
struct bcma_corecfg *corecfg)
{
+ struct bcma_intr *intr;
+ int error;
+
KASSERT(dinfo->corecfg == NULL, ("dinfo previously initialized"));
/* Save core configuration value */
@@ -242,71 +373,52 @@ bcma_init_dinfo(device_t bus, struct bcma_devinfo *dinfo,
/* The device ports must always be initialized first to ensure that
* rid 0 maps to the first device port */
- bcma_dinfo_init_resource_info(bus, dinfo, &corecfg->dev_ports);
-
- bcma_dinfo_init_resource_info(bus, dinfo, &corecfg->bridge_ports);
- bcma_dinfo_init_resource_info(bus, dinfo, &corecfg->wrapper_ports);
-
- return (0);
-}
-
-
-/**
- * Allocate the per-core agent register block for a device info structure
- * previous initialized via bcma_init_dinfo().
- *
- * If an agent0.0 region is not defined on @p dinfo, the device info
- * agent resource is set to NULL and 0 is returned.
- *
- * @param bus The requesting bus device.
- * @param child The bcma child device.
- * @param dinfo The device info associated with @p child
- *
- * @retval 0 success
- * @retval non-zero resource allocation failed.
- */
-int
-bcma_dinfo_alloc_agent(device_t bus, device_t child, struct bcma_devinfo *dinfo)
-{
- bhnd_addr_t addr;
- bhnd_size_t size;
- rman_res_t r_start, r_count, r_end;
- int error;
+ bcma_dinfo_init_port_resource_info(bus, dinfo, &corecfg->dev_ports);
+ bcma_dinfo_init_port_resource_info(bus, dinfo, &corecfg->bridge_ports);
+ bcma_dinfo_init_port_resource_info(bus, dinfo, &corecfg->wrapper_ports);
+
+ /* Now that we've defined the port resources, we can map the device's
+ * agent registers (if any) */
+ if ((error = bcma_dinfo_init_agent(bus, child, dinfo)))
+ goto failed;
+
+ /* With agent registers mapped, we can populate the device's interrupt
+ * descriptors */
+ if ((error = bcma_dinfo_init_intrs(bus, child, dinfo)))
+ goto failed;
+
+ /* Finally, map the interrupt descriptors */
+ STAILQ_FOREACH(intr, &dinfo->intrs, i_link) {
+ /* Already mapped? */
+ if (intr->i_mapped)
+ continue;
+
+ /* Map the interrupt */
+ error = BHND_BUS_MAP_INTR(bus, child, intr->i_sel,
+ &intr->i_irq);
+ if (error) {
+ device_printf(bus, "failed mapping interrupt line %u "
+ "for core %u: %d\n", intr->i_sel,
+ BCMA_DINFO_COREIDX(dinfo), error);
+ goto failed;
+ }
- KASSERT(dinfo->res_agent == NULL, ("double allocation of agent"));
+ intr->i_mapped = true;
- /* Verify that the agent register block exists and is
- * mappable */
- if (bhnd_get_port_rid(child, BHND_PORT_AGENT, 0, 0) == -1)
- return (0); /* nothing to do */
-
- /* Fetch the address of the agent register block */
- error = bhnd_get_region_addr(child, BHND_PORT_AGENT, 0, 0,
- &addr, &size);
- if (error) {
- device_printf(bus, "failed fetching agent register block "
- "address for core %u\n", BCMA_DINFO_COREIDX(dinfo));
- return (error);
+ /* Add to resource list */
+ intr->i_rid = resource_list_add_next(&dinfo->resources,
+ SYS_RES_IRQ, intr->i_irq, intr->i_irq, 1);
}
- /* Allocate the resource */
- r_start = addr;
- r_count = size;
- r_end = r_start + r_count - 1;
+ return (0);
- dinfo->rid_agent = BCMA_AGENT_RID(dinfo);
- dinfo->res_agent = BHND_BUS_ALLOC_RESOURCE(bus, bus, SYS_RES_MEMORY,
- &dinfo->rid_agent, r_start, r_end, r_count, RF_ACTIVE);
- if (dinfo->res_agent == NULL) {
- device_printf(bus, "failed allocating agent register block for "
- "core %u\n", BCMA_DINFO_COREIDX(dinfo));
- return (ENXIO);
- }
+failed:
+ /* Owned by the caller on failure */
+ dinfo->corecfg = NULL;
- return (0);
+ return (error);
}
-
/**
* Deallocate the given device info structure and any associated resources.
*
@@ -314,8 +426,10 @@ bcma_dinfo_alloc_agent(device_t bus, device_t child, struct bcma_devinfo *dinfo)
* @param dinfo Device info to be deallocated.
*/
void
-bcma_free_dinfo(device_t bus, struct bcma_devinfo *dinfo)
+bcma_free_dinfo(device_t bus, device_t child, struct bcma_devinfo *dinfo)
{
+ struct bcma_intr *intr, *inext;
+
resource_list_free(&dinfo->resources);
if (dinfo->corecfg != NULL)
@@ -327,11 +441,71 @@ bcma_free_dinfo(device_t bus, struct bcma_devinfo *dinfo)
dinfo->res_agent);
}
+ /* Clean up interrupt descriptors */
+ STAILQ_FOREACH_SAFE(intr, &dinfo->intrs, i_link, inext) {
+ STAILQ_REMOVE(&dinfo->intrs, intr, bcma_intr, i_link);
+
+ /* Release our IRQ mapping */
+ if (intr->i_mapped) {
+ BHND_BUS_UNMAP_INTR(bus, child, intr->i_irq);
+ intr->i_mapped = false;
+ }
+
+ bcma_free_intr(intr);
+ }
+
free(dinfo, M_BHND);
}
/**
+ * Allocate and initialize a new interrupt descriptor.
+ *
+ * @param bank OOB bank.
+ * @param sel OOB selector.
+ * @param line OOB bus line.
+ */
+struct bcma_intr *
+bcma_alloc_intr(uint8_t bank, uint8_t sel, uint8_t line)
+{
+ struct bcma_intr *intr;
+
+ if (bank >= BCMA_OOB_NUM_BANKS)
+ return (NULL);
+
+ if (sel >= BCMA_OOB_NUM_SEL)
+ return (NULL);
+
+ if (line >= BCMA_OOB_NUM_BUSLINES)
+ return (NULL);
+
+ intr = malloc(sizeof(*intr), M_BHND, M_NOWAIT);
+ if (intr == NULL)
+ return (NULL);
+
+ intr->i_bank = bank;
+ intr->i_sel = sel;
+ intr->i_busline = line;
+ intr->i_mapped = false;
+ intr->i_irq = 0;
+
+ return (intr);
+}
+
+/**
+ * Deallocate all resources associated with the given interrupt descriptor.
+ *
+ * @param intr Interrupt descriptor to be deallocated.
+ */
+void
+bcma_free_intr(struct bcma_intr *intr)
+{
+ KASSERT(!intr->i_mapped, ("interrupt %u still mapped", intr->i_sel));
+
+ free(intr, M_BHND);
+}
+
+/**
* Allocate and initialize new slave port descriptor.
*
* @param port_num Per-core port number.
diff --git a/sys/dev/bhnd/bcma/bcmavar.h b/sys/dev/bhnd/bcma/bcmavar.h
index 1cdabcd6a81d..dba976841917 100644
--- a/sys/dev/bhnd/bcma/bcmavar.h
+++ b/sys/dev/bhnd/bcma/bcmavar.h
@@ -1,7 +1,11 @@
/*-
- * Copyright (c) 2015 Landon Fuller <landon@landonf.org>
+ * Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org>
+ * Copyright (c) 2017 The FreeBSD Foundation
* All rights reserved.
*
+ * Portions of this software were developed by Landon Fuller
+ * under sponsorship from the FreeBSD Foundation.
+ *
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@@ -67,6 +71,7 @@ typedef u_int bcma_rmid_t;
struct bcma_devinfo;
struct bcma_corecfg;
+struct bcma_intr;
struct bcma_map;
struct bcma_mport;
struct bcma_sport;
@@ -74,8 +79,8 @@ struct bcma_sport;
int bcma_probe(device_t dev);
int bcma_attach(device_t dev);
int bcma_detach(device_t dev);
-int bcma_get_intr_count(device_t dev, device_t child);
-int bcma_get_core_ivec(device_t dev, device_t child,
+u_int bcma_get_intr_count(device_t dev, device_t child);
+int bcma_get_intr_ivec(device_t dev, device_t child,
u_int intr, uint32_t *ivec);
int bcma_add_children(device_t bus);
@@ -84,18 +89,20 @@ struct bcma_sport_list *bcma_corecfg_get_port_list(struct bcma_corecfg *cfg,
bhnd_port_type type);
struct bcma_devinfo *bcma_alloc_dinfo(device_t bus);
-int bcma_init_dinfo(device_t bus,
+int bcma_init_dinfo(device_t bus, device_t child,
struct bcma_devinfo *dinfo,
struct bcma_corecfg *corecfg);
-int bcma_dinfo_alloc_agent(device_t bus, device_t child,
- struct bcma_devinfo *dinfo);
-void bcma_free_dinfo(device_t bus,
+void bcma_free_dinfo(device_t bus, device_t child,
struct bcma_devinfo *dinfo);
struct bcma_corecfg *bcma_alloc_corecfg(u_int core_index, int core_unit,
uint16_t vendor, uint16_t device, uint8_t hwrev);
void bcma_free_corecfg(struct bcma_corecfg *corecfg);
+struct bcma_intr *bcma_alloc_intr(uint8_t bank, uint8_t sel,
+ uint8_t line);
+void bcma_free_intr(struct bcma_intr *intr);
+
struct bcma_sport *bcma_alloc_sport(bcma_pid_t port_num, bhnd_port_type port_type);
void bcma_free_sport(struct bcma_sport *sport);
@@ -121,6 +128,18 @@ struct bcma_map {
STAILQ_ENTRY(bcma_map) m_link;
};
+/** BCMA interrupt descriptor */
+struct bcma_intr {
+ uint8_t i_bank; /**< OOB bank (see BCMA_OOB_BANK[A-D]) */
+ uint8_t i_sel; /**< OOB selector (0-7) */
+ uint8_t i_busline; /**< OOB bus line assigned to this selector */
+ bool i_mapped; /**< if an irq has been mapped for this selector */
+ int i_rid; /**< bus resource id, or -1 */
+ rman_res_t i_irq; /**< the mapped bus irq, if any */
+
+ STAILQ_ENTRY(bcma_intr) i_link;
+};
+
/** BCMA slave port descriptor */
struct bcma_sport {
bcma_pid_t sp_num; /**< slave port number (core-unique) */
@@ -131,8 +150,9 @@ struct bcma_sport {
STAILQ_ENTRY(bcma_sport) sp_link;
};
-STAILQ_HEAD(bcma_mport_list, bcma_mport);
-STAILQ_HEAD(bcma_sport_list, bcma_sport);
+STAILQ_HEAD(bcma_mport_list, bcma_mport);
+STAILQ_HEAD(bcma_intr_list, bcma_intr);
+STAILQ_HEAD(bcma_sport_list, bcma_sport);
/** BCMA IP core/block configuration */
struct bcma_corecfg {
@@ -162,6 +182,9 @@ struct bcma_devinfo {
* all bcma(4) cores have or require an agent. */
int rid_agent; /**< Agent resource ID, or -1 */
+ u_int num_intrs; /**< number of interrupt descriptors. */
+ struct bcma_intr_list intrs; /**< interrupt descriptors */
+
struct bhnd_core_pmu_info *pmu_info; /**< Bus-managed PMU state, or NULL */
};
diff --git a/sys/dev/bhnd/bhnd.c b/sys/dev/bhnd/bhnd.c
index f4c1ea29cf81..1a3b0246fea4 100644
--- a/sys/dev/bhnd/bhnd.c
+++ b/sys/dev/bhnd/bhnd.c
@@ -814,6 +814,22 @@ bhnd_generic_resume_child(device_t dev, device_t child)
return bus_generic_resume_child(dev, child);
}
+
+/**
+ * Default bhnd(4) bus driver implementation of BUS_SETUP_INTR().
+ *
+ * This implementation of BUS_SETUP_INTR() will delegate interrupt setup
+ * to the parent of @p dev, if any.
+ */
+int
+bhnd_generic_setup_intr(device_t dev, device_t child, struct resource *irq,
+ int flags, driver_filter_t *filter, driver_intr_t *intr, void *arg,
+ void **cookiep)
+{
+ return (bus_generic_setup_intr(dev, child, irq, flags, filter, intr,
+ arg, cookiep));
+}
+
/*
* Delegate all indirect I/O to the parent device. When inherited by
* non-bridged bus implementations, resources will never be marked as
@@ -917,7 +933,7 @@ static device_method_t bhnd_methods[] = {
DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
- DEVMETHOD(bus_setup_intr, bus_generic_setup_intr),
+ DEVMETHOD(bus_setup_intr, bhnd_generic_setup_intr),
DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr),
DEVMETHOD(bus_config_intr, bus_generic_config_intr),
DEVMETHOD(bus_bind_intr, bus_generic_bind_intr),
diff --git a/sys/dev/bhnd/bhnd.h b/sys/dev/bhnd/bhnd.h
index 33e34344968a..6c80f3d144a0 100644
--- a/sys/dev/bhnd/bhnd.h
+++ b/sys/dev/bhnd/bhnd.h
@@ -250,10 +250,10 @@ struct bhnd_device_quirk {
{{ BHND_MATCH_CORE_REV(_rev) }, (_flags) }
#define BHND_CHIP_QUIRK(_chip, _rev, _flags) \
- {{ BHND_CHIP_IR(BCM ## _chip, _rev) }, (_flags) }
+ {{ BHND_MATCH_CHIP_IR(BCM ## _chip, _rev) }, (_flags) }
#define BHND_PKG_QUIRK(_chip, _pkg, _flags) \
- {{ BHND_CHIP_IP(BCM ## _chip, BCM ## _chip ## _pkg) }, (_flags) }
+ {{ BHND_MATCH_CHIP_IP(BCM ## _chip, BCM ## _chip ## _pkg) }, (_flags) }
#define BHND_BOARD_QUIRK(_board, _flags) \
{{ BHND_MATCH_BOARD_TYPE(_board) }, \
@@ -528,8 +528,8 @@ int bhnd_bus_generic_activate_resource (device_t dev,
int bhnd_bus_generic_deactivate_resource (device_t dev,
device_t child, int type, int rid,
struct bhnd_resource *r);
-bhnd_attach_type bhnd_bus_generic_get_attach_type(device_t dev,
- device_t child);
+uintptr_t bhnd_bus_generic_get_intr_domain(device_t dev,
+ device_t child, bool self);
/**
* Return the bhnd(4) bus driver's device enumeration parser class
@@ -865,25 +865,22 @@ bhnd_read_board_info(device_t dev, struct bhnd_board_info *info)
}
/**
- * Return the number of interrupts to be assigned to @p child via
- * BHND_BUS_ASSIGN_INTR().
+ * Return the number of interrupt lines assigned to @p dev.
*
* @param dev A bhnd bus child device.
*/
-static inline int
+static inline u_int
bhnd_get_intr_count(device_t dev)
{
return (BHND_BUS_GET_INTR_COUNT(device_get_parent(dev), dev));
}
/**
- * Return the backplane interrupt vector corresponding to @p dev's given
- * @p intr number.
+ * Get the backplane interrupt vector of the @p intr line attached to @p dev.
*
* @param dev A bhnd bus child device.
- * @param intr The interrupt number being queried. This is equivalent to the
- * bus resource ID for the interrupt.
- * @param[out] ivec On success, the assigned hardware interrupt vector be
+ * @param intr The index of the interrupt line being queried.
+ * @param[out] ivec On success, the assigned hardware interrupt vector will be
* written to this pointer.
*
* On bcma(4) devices, this returns the OOB bus line assigned to the
@@ -893,17 +890,51 @@ bhnd_get_intr_count(device_t dev)
* to the interrupt.
*
* @retval 0 success
- * @retval ENXIO If @p intr exceeds the number of interrupts available
- * to @p child.
+ * @retval ENXIO If @p intr exceeds the number of interrupt lines
+ * assigned to @p child.
*/
static inline int
-bhnd_get_core_ivec(device_t dev, u_int intr, uint32_t *ivec)
+bhnd_get_intr_ivec(device_t dev, u_int intr, u_int *ivec)
{
- return (BHND_BUS_GET_CORE_IVEC(device_get_parent(dev), dev, intr,
+ return (BHND_BUS_GET_INTR_IVEC(device_get_parent(dev), dev, intr,
ivec));
}
/**
+ * Map the given @p intr to an IRQ number; until unmapped, this IRQ may be used
+ * to allocate a resource of type SYS_RES_IRQ.
+ *
+ * On success, the caller assumes ownership of the interrupt mapping, and
+ * is responsible for releasing the mapping via bhnd_unmap_intr().
+ *
+ * @param dev The requesting device.
+ * @param intr The interrupt being mapped.
+ * @param[out] irq On success, the bus interrupt value mapped for @p intr.
+ *
+ * @retval 0 If an interrupt was assigned.
+ * @retval non-zero If mapping an interrupt otherwise fails, a regular
+ * unix error code will be returned.
+ */
+static inline int
+bhnd_map_intr(device_t dev, u_int intr, rman_res_t *irq)
+{
+ return (BHND_BUS_MAP_INTR(device_get_parent(dev), dev, intr, irq));
+}
+
+/**
+ * Unmap an bus interrupt previously mapped via bhnd_map_intr().
+ *
+ * @param dev The requesting device.
+ * @param intr The interrupt number being unmapped. This is equivalent to the
+ * bus resource ID for the interrupt.
+ */
+static inline void
+bhnd_unmap_intr(device_t dev, rman_res_t irq)
+{
+ return (BHND_BUS_UNMAP_INTR(device_get_parent(dev), dev, irq));
+}
+
+/**
* Allocate and enable per-core PMU request handling for @p child.
*
* The region containing the core's PMU register block (if any) must be
diff --git a/sys/dev/bhnd/bhnd_bus_if.m b/sys/dev/bhnd/bhnd_bus_if.m
index 28b513959e6e..2123d9cb1749 100644
--- a/sys/dev/bhnd/bhnd_bus_if.m
+++ b/sys/dev/bhnd/bhnd_bus_if.m
@@ -141,25 +141,6 @@ CODE {
panic("bhnd_bus_read_boardinfo unimplemented");
}
- static int
- bhnd_bus_null_get_intr_count(device_t dev, device_t child)
- {
- panic("bhnd_bus_get_intr_count unimplemented");
- }
-
- static int
- bhnd_bus_null_assign_intr(device_t dev, device_t child, int rid)
- {
- panic("bhnd_bus_assign_intr unimplemented");
- }
-
- static int
- bhnd_bus_null_get_core_ivec(device_t dev, device_t child, u_int intr,
- uint32_t *ivec)
- {
- panic("bhnd_bus_get_core_ivec unimplemented");
- }
-
static void
bhnd_bus_null_child_added(device_t dev, device_t child)
{
@@ -243,6 +224,39 @@ CODE {
panic("bhnd_bus_get_probe_order unimplemented");
}
+ static uintptr_t
+ bhnd_bus_null_get_intr_domain(device_t dev, device_t child, bool self)
+ {
+ /* Unsupported */
+ return (0);
+ }
+
+ static u_int
+ bhnd_bus_null_get_intr_count(device_t dev, device_t child)
+ {
+ return (0);
+ }
+
+ static int
+ bhnd_bus_null_get_intr_ivec(device_t dev, device_t child, u_int intr,
+ u_int *ivec)
+ {
+ panic("bhnd_bus_get_intr_ivec unimplemented");
+ }
+
+ static int
+ bhnd_bus_null_map_intr(device_t dev, device_t child, u_int intr,
+ rman_res_t *irq)
+ {
+ panic("bhnd_bus_map_intr unimplemented");
+ }
+
+ static int
+ bhnd_bus_null_unmap_intr(device_t dev, device_t child, rman_res_t irq)
+ {
+ panic("bhnd_bus_unmap_intr unimplemented");
+ }
+
static int
bhnd_bus_null_get_port_rid(device_t dev, device_t child,
bhnd_port_type port_type, u_int port, u_int region)
@@ -488,77 +502,6 @@ METHOD int read_board_info {
} DEFAULT bhnd_bus_null_read_board_info;
/**
- * Return the number of interrupts to be assigned to @p child via
- * BHND_BUS_ASSIGN_INTR().
- *
- * @param dev The bhnd bus parent of @p child.
- * @param child The bhnd device for which a count should be returned.
- *
- * @retval 0 If no interrupts should be assigned.
- * @retval non-zero The count of interrupt resource IDs to be
- * assigned, starting at rid 0.
- */
-METHOD int get_intr_count {
- device_t dev;
- device_t child;
-} DEFAULT bhnd_bus_null_get_intr_count;
-
-/**
- * Assign an interrupt to @p child via bus_set_resource().
- *
- * The default bus implementation of this method should assign backplane
- * interrupt values to @p child.
- *
- * Bridge-attached bus implementations may instead override standard
- * interconnect IRQ assignment, providing IRQs inherited from the parent bus.
- *
- * TODO: Once we can depend on INTRNG, investigate replacing this with a
- * bridge-level interrupt controller.
- *
- * @param dev The bhnd bus parent of @p child.
- * @param child The bhnd device to which an interrupt should be assigned.
- * @param rid The interrupt resource ID to be assigned.
- *
- * @retval 0 If an interrupt was assigned.
- * @retval non-zero If assigning an interrupt otherwise fails, a regular
- * unix error code will be returned.
- */
-METHOD int assign_intr {
- device_t dev;
- device_t child;
- int rid;
-} DEFAULT bhnd_bus_null_assign_intr;
-
-/**
- * Return the backplane interrupt vector corresponding to @p child's given
- * @p intr number.
- *
- * @param dev The bhnd bus parent of @p child.
- * @param child The bhnd device for which the assigned interrupt vector should
- * be queried.
- * @param intr The interrupt number being queried. This is equivalent to the
- * bus resource ID for the interrupt.
- * @param[out] ivec On success, the assigned hardware interrupt vector be
- * written to this pointer.
- *
- * On bcma(4) devices, this returns the OOB bus line assigned to the
- * interrupt.
- *
- * On siba(4) devices, this returns the target OCP slave flag number assigned
- * to the interrupt.
- *
- * @retval 0 success
- * @retval ENXIO If @p intr exceeds the number of interrupts available
- * to @p child.
- */
-METHOD int get_core_ivec {
- device_t dev;
- device_t child;
- u_int intr;
- uint32_t *ivec;
-} DEFAULT bhnd_bus_null_get_core_ivec;
-
-/**
* Notify a bhnd bus that a child was added.
*
* This method must be called by concrete bhnd(4) driver impementations
@@ -998,6 +941,106 @@ METHOD int deactivate_resource {
} DEFAULT bhnd_bus_generic_deactivate_resource;
/**
+ * Return the interrupt domain.
+ *
+ * This globally unique value may be used as the interrupt controller 'xref'
+ * on targets that support INTRNG.
+ *
+ * @param dev The device whose child is being examined.
+ * @param child The child device.
+ * @parem self If true, return @p child's interrupt domain, rather than the
+ * domain in which @p child resides.
+ *
+ * On Non-OFW targets, this should either return:
+ * - The pointer address of a device that can uniquely identify @p child's
+ * interrupt domain (e.g., the bhnd bus' device_t address), or
+ * - 0 if unsupported by the bus.
+ *
+ * On OFW (including FDT) targets, this should return the @p child's iparent
+ * property's xref if @p self is false, the child's own node xref value if
+ * @p self is true, or 0 if no interrupt parent is found.
+ */
+METHOD uintptr_t get_intr_domain {
+ device_t dev;
+ device_t child;
+ bool self;
+} DEFAULT bhnd_bus_null_get_intr_domain;
+
+/**
+ * Return the number of interrupt lines assigned to @p child.
+ *
+ * @param dev The bhnd device whose child is being examined.
+ * @param child The child device.
+ */
+METHOD u_int get_intr_count {
+ device_t dev;
+ device_t child;
+} DEFAULT bhnd_bus_null_get_intr_count;
+
+/**
+ * Get the backplane interrupt vector of the @p intr line attached to @p child.
+ *
+ * @param dev The device whose child is being examined.
+ * @param child The child device.
+ * @param intr The index of the interrupt line being queried.
+ * @param[out] ivec On success, the assigned hardware interrupt vector will be
+ * written to this pointer.
+ *
+ * On bcma(4) devices, this returns the OOB bus line assigned to the
+ * interrupt.
+ *
+ * On siba(4) devices, this returns the target OCP slave flag number assigned
+ * to the interrupt.
+ *
+ * @retval 0 success
+ * @retval ENXIO If @p intr exceeds the number of interrupt lines
+ * assigned to @p child.
+ */
+METHOD int get_intr_ivec {
+ device_t dev;
+ device_t child;
+ u_int intr;
+ u_int *ivec;
+} DEFAULT bhnd_bus_null_get_intr_ivec;
+
+/**
+ * Map the given @p intr to an IRQ number; until unmapped, this IRQ may be used
+ * to allocate a resource of type SYS_RES_IRQ.
+ *
+ * On success, the caller assumes ownership of the interrupt mapping, and
+ * is responsible for releasing the mapping via BHND_BUS_UNMAP_INTR().
+ *
+ * @param dev The bhnd bus device.
+ * @param child The requesting child device.
+ * @param intr The interrupt being mapped.
+ * @param[out] irq On success, the bus interrupt value mapped for @p intr.
+ *
+ * @retval 0 If an interrupt was assigned.
+ * @retval non-zero If mapping an interrupt otherwise fails, a regular
+ * unix error code will be returned.
+ */
+METHOD int map_intr {
+ device_t dev;
+ device_t child;
+ u_int intr;
+ rman_res_t *irq;
+} DEFAULT bhnd_bus_null_map_intr;
+
+/**
+ * Unmap an bus interrupt previously mapped via BHND_BUS_MAP_INTR().
+ *
+ * @param dev The bhnd bus device.
+ * @param child The requesting child device.
+ * @param intr The interrupt number being unmapped. This is equivalent to the
+ * bus resource ID for the interrupt.
+ */
+METHOD void unmap_intr {
+ device_t dev;
+ device_t child;
+ rman_res_t irq;
+} DEFAULT bhnd_bus_null_unmap_intr;
+
+/**
* Return true if @p region_num is a valid region on @p port_num of
* @p type attached to @p child.
*
diff --git a/sys/dev/bhnd/bhnd_ids.h b/sys/dev/bhnd/bhnd_ids.h
index ac5cba9a8117..df16b12eb679 100644
--- a/sys/dev/bhnd/bhnd_ids.h
+++ b/sys/dev/bhnd/bhnd_ids.h
@@ -535,12 +535,16 @@
#define BHND_CHIPTYPE_UBUS 2 /**< ubus interconnect found in bcm63xx devices */
#define BHND_CHIPTYPE_BCMA_ALT 3 /**< bcma(4) interconnect */
-/** Evaluates to true if @p _type uses a BCMA EROM table */
-#define BHND_CHIPTYPE_HAS_EROM(_type) \
+/** Evaluates to true if @p _type is a BCMA or BCMA-compatible interconenct */
+#define BHND_CHIPTYPE_IS_BCMA_COMPATIBLE(_type) \
((_type) == BHND_CHIPTYPE_BCMA || \
(_type) == BHND_CHIPTYPE_BCMA_ALT || \
(_type) == BHND_CHIPTYPE_UBUS)
+/** Evaluates to true if @p _type uses a BCMA EROM table */
+#define BHND_CHIPTYPE_HAS_EROM(_type) \
+ BHND_CHIPTYPE_IS_BCMA_COMPATIBLE(_type)
+
/* Boardflags */
#define BHND_BFL_BTC2WIRE 0x00000001 /* old 2wire Bluetooth coexistence, OBSOLETE */
#define BHND_BFL_BTCOEX 0x00000001 /* Board supports BTCOEX */
diff --git a/sys/dev/bhnd/bhnd_match.h b/sys/dev/bhnd/bhnd_match.h
index c5b7d0125a01..3090929af790 100644
--- a/sys/dev/bhnd/bhnd_match.h
+++ b/sys/dev/bhnd/bhnd_match.h
@@ -154,7 +154,8 @@ struct bhnd_chip_match {
chip_id:1,
chip_rev:1,
chip_pkg:1,
- flags_unused:5;
+ chip_type:1,
+ flags_unused:4;
} match;
} m;
@@ -162,38 +163,46 @@ struct bhnd_chip_match {
uint16_t chip_id; /**< required chip id */
struct bhnd_hwrev_match chip_rev; /**< matching chip revisions */
uint8_t chip_pkg; /**< required package */
+ uint8_t chip_type; /**< required chip type (BHND_CHIPTYPE_*) */
};
#define _BHND_CHIP_MATCH_COPY(_src) \
_BHND_COPY_MATCH_FIELD(_src, chip_id), \
_BHND_COPY_MATCH_FIELD(_src, chip_rev), \
- _BHND_COPY_MATCH_FIELD(_src, chip_pkg) \
+ _BHND_COPY_MATCH_FIELD(_src, chip_pkg), \
+ _BHND_COPY_MATCH_FIELD(_src, chip_type),\
/** Set the required chip ID within a bhnd match descriptor */
-#define BHND_CHIP_ID(_cid) _BHND_SET_MATCH_FIELD(chip_id, \
+#define BHND_MATCH_CHIP_ID(_cid) _BHND_SET_MATCH_FIELD(chip_id, \
BHND_CHIPID_ ## _cid)
/** Set the required chip revision range within a bhnd match descriptor */
-#define BHND_CHIP_REV(_rev) _BHND_SET_MATCH_FIELD(chip_rev, \
+#define BHND_MATCH_CHIP_REV(_rev) _BHND_SET_MATCH_FIELD(chip_rev, \
BHND_ ## _rev)
/** Set the required package ID within a bhnd match descriptor */
-#define BHND_CHIP_PKG(_pkg) _BHND_SET_MATCH_FIELD(chip_pkg, \
+#define BHND_MATCH_CHIP_PKG(_pkg) _BHND_SET_MATCH_FIELD(chip_pkg, \
BHND_PKGID_ ## _pkg)
+/** Set the required chip type within a bhnd match descriptor */
+#define BHND_MATCH_CHIP_TYPE(_type) _BHND_SET_MATCH_FIELD(chip_type, \
+ BHND_CHIPTYPE_ ## _type)
+
/** Set the required chip and package ID within a bhnd match descriptor */
-#define BHND_CHIP_IP(_cid, _pkg) \
- BHND_CHIP_ID(_cid), BHND_CHIP_PKG(_pkg)
+#define BHND_MATCH_CHIP_IP(_cid, _pkg) \
+ BHND_MATCH_CHIP_ID(_cid), BHND_MATCH_CHIP_PKG(_pkg)
/** Set the required chip ID, package ID, and revision within a bhnd_device_match
* instance */
-#define BHND_CHIP_IPR(_cid, _pkg, _rev) \
- BHND_CHIP_ID(_cid), BHND_CHIP_PKG(_pkg), BHND_CHIP_REV(_rev)
+#define BHND_MATCH_CHIP_IPR(_cid, _pkg, _rev) \
+ BHND_MATCH_CHIP_ID(_cid), \
+ BHND_MATCH_CHIP_PKG(_pkg), \
+ BHND_MATCH_CHIP_REV(_rev)
/** Set the required chip ID and revision within a bhnd_device_match
* instance */
-#define BHND_CHIP_IR(_cid, _rev) \
- BHND_CHIP_ID(_cid), BHND_CHIP_REV(_rev)
+#define BHND_MATCH_CHIP_IR(_cid, _rev) \
+ BHND_MATCH_CHIP_ID(_cid), BHND_MATCH_CHIP_REV(_rev)
/**
* A bhnd(4) board match descriptor.
@@ -252,9 +261,9 @@ struct bhnd_board_match {
struct bhnd_device_match {
/** Select fields to be matched */
union {
- uint16_t match_flags;
+ uint32_t match_flags;
struct {
- uint16_t
+ uint32_t
core_vendor:1,
core_id:1,
core_rev:1,
@@ -264,11 +273,12 @@ struct bhnd_device_match {
chip_id:1,
chip_rev:1,
chip_pkg:1,
+ chip_type:1,
board_vendor:1,
board_type:1,
board_rev:1,
board_srom_rev:1,
- flags_unused:1;
+ flags_unused:16;
} match;
} m;
@@ -282,6 +292,7 @@ struct bhnd_device_match {
uint16_t chip_id; /**< required chip id */
struct bhnd_hwrev_match chip_rev; /**< matching chip revisions */
uint8_t chip_pkg; /**< required package */
+ uint8_t chip_type; /**< required chip type (BHND_CHIPTYPE_*) */
uint16_t board_vendor; /**< required board vendor */
uint16_t board_type; /**< required board type */
diff --git a/sys/dev/bhnd/bhnd_subr.c b/sys/dev/bhnd/bhnd_subr.c
index 992b887288fa..41472dc4fd93 100644
--- a/sys/dev/bhnd/bhnd_subr.c
+++ b/sys/dev/bhnd/bhnd_subr.c
@@ -738,6 +738,9 @@ bhnd_chip_matches(const struct bhnd_chipid *chip,
!bhnd_hwrev_matches(chip->chip_rev, &desc->chip_rev))
return (false);
+ if (desc->m.match.chip_type && chip->chip_type != desc->chip_type)
+ return (false);
+
return (true);
}
@@ -2317,3 +2320,14 @@ bhnd_bus_generic_deactivate_resource(device_t dev, device_t child,
return (EINVAL);
}
+/**
+ * Helper function for implementing BHND_BUS_GET_INTR_DOMAIN().
+ *
+ * This implementation simply returns the address of nearest bhnd(4) bus,
+ * which may be @p dev; this behavior may be incompatible with FDT/OFW targets.
+ */
+uintptr_t
+bhnd_bus_generic_get_intr_domain(device_t dev, device_t child, bool self)
+{
+ return ((uintptr_t)dev);
+} \ No newline at end of file
diff --git a/sys/dev/bhnd/bhndb/bhnd_bhndb.c b/sys/dev/bhnd/bhndb/bhnd_bhndb.c
index fdd45ef39819..8b93050ee7ab 100644
--- a/sys/dev/bhnd/bhndb/bhnd_bhndb.c
+++ b/sys/dev/bhnd/bhndb/bhnd_bhndb.c
@@ -98,10 +98,17 @@ bhnd_bhndb_find_hostb_device(device_t dev)
}
static int
-bhnd_bhndb_assign_intr(device_t dev, device_t child, int rid)
+bhnd_bhndb_map_intr(device_t dev, device_t child, u_int intr, rman_res_t *irq)
{
/* Delegate to parent bridge */
- return (BHND_BUS_ASSIGN_INTR(device_get_parent(dev), child, rid));
+ return (BHND_BUS_MAP_INTR(device_get_parent(dev), child, intr, irq));
+}
+
+static void
+bhnd_bhndb_unmap_intr(device_t dev, device_t child, rman_res_t irq)
+{
+ /* Delegate to parent bridge */
+ return (BHND_BUS_UNMAP_INTR(device_get_parent(dev), child, irq));
}
static bhnd_clksrc
@@ -131,13 +138,48 @@ bhnd_bhndb_pwrctl_ungate_clock(device_t dev, device_t child,
clock));
}
+static int
+bhnd_bhndb_setup_intr(device_t dev, device_t child, struct resource *irq,
+ int flags, driver_filter_t *filter, driver_intr_t *intr, void *arg,
+ void **cookiep)
+{
+ device_t core, bus;
+ int error;
+
+ /* Find the actual bus-attached child core */
+ core = child;
+ while ((bus = device_get_parent(core)) != NULL) {
+ if (bus == dev)
+ break;
+
+ core = bus;
+ }
+
+ KASSERT(core != NULL, ("%s is not a child of %s",
+ device_get_nameunit(child), device_get_nameunit(dev)));
+
+ /* Ask our bridge to enable interrupt routing for the child core */
+ error = BHNDB_ROUTE_INTERRUPTS(device_get_parent(dev), core);
+ if (error)
+ return (error);
+
+ /* Delegate actual interrupt setup to the default bhnd bus
+ * implementation */
+ return (bhnd_generic_setup_intr(dev, child, irq, flags, filter, intr,
+ arg, cookiep));
+}
+
static device_method_t bhnd_bhndb_methods[] = {
+ /* Bus interface */
+ DEVMETHOD(bus_setup_intr, bhnd_bhndb_setup_intr),
+
/* BHND interface */
DEVMETHOD(bhnd_bus_get_attach_type, bhnd_bhndb_get_attach_type),
DEVMETHOD(bhnd_bus_is_hw_disabled, bhnd_bhndb_is_hw_disabled),
DEVMETHOD(bhnd_bus_find_hostb_device, bhnd_bhndb_find_hostb_device),
DEVMETHOD(bhnd_bus_read_board_info, bhnd_bhndb_read_board_info),
- DEVMETHOD(bhnd_bus_assign_intr, bhnd_bhndb_assign_intr),
+ DEVMETHOD(bhnd_bus_map_intr, bhnd_bhndb_map_intr),
+ DEVMETHOD(bhnd_bus_unmap_intr, bhnd_bhndb_unmap_intr),
DEVMETHOD(bhnd_bus_pwrctl_get_clksrc, bhnd_bhndb_pwrctl_get_clksrc),
DEVMETHOD(bhnd_bus_pwrctl_gate_clock, bhnd_bhndb_pwrctl_gate_clock),
diff --git a/sys/dev/bhnd/bhndb/bhndb.c b/sys/dev/bhnd/bhndb/bhndb.c
index 92de9f174bed..e3529f5bf186 100644
--- a/sys/dev/bhnd/bhndb/bhndb.c
+++ b/sys/dev/bhnd/bhndb/bhndb.c
@@ -804,7 +804,7 @@ bhndb_get_rman(struct bhndb_softc *sc, device_t child, int type)
case SYS_RES_MEMORY:
return (&sc->bus_res->br_mem_rman);
case SYS_RES_IRQ:
- return (NULL);
+ return (&sc->bus_res->br_irq_rman);
default:
return (NULL);
}
@@ -1088,9 +1088,9 @@ bhndb_adjust_resource(device_t dev, device_t child, int type,
if (!(rman_get_flags(r) & RF_ACTIVE))
goto done;
- /* Otherwise, the range is limited to the existing register window
- * mapping */
- error = bhndb_find_resource_limits(sc->bus_res, r, &mstart, &mend);
+ /* Otherwise, the range is limited by the bridged resource mapping */
+ error = bhndb_find_resource_limits(sc->bus_res, type, r, &mstart,
+ &mend);
if (error)
goto done;
@@ -1285,12 +1285,25 @@ bhndb_try_activate_resource(struct bhndb_softc *sc, device_t child, int type,
BHNDB_LOCK_ASSERT(sc, MA_NOTOWNED);
- /* Only MMIO resources can be mapped via register windows */
- if (type != SYS_RES_MEMORY)
- return (ENXIO);
-
- if (indirect)
+ if (indirect != NULL)
*indirect = false;
+
+ switch (type) {
+ case SYS_RES_IRQ:
+ /* IRQ resources are always directly mapped */
+ return (rman_activate_resource(r));
+
+ case SYS_RES_MEMORY:
+ /* Handled below */
+ break;
+
+ default:
+ device_printf(sc->dev, "unsupported resource type %d\n", type);
+ return (ENXIO);
+ }
+
+ /* Only MMIO resources can be mapped via register windows */
+ KASSERT(type == SYS_RES_MEMORY, ("invalid type: %d", type));
r_start = rman_get_start(r);
r_size = rman_get_size(r);
@@ -1386,9 +1399,6 @@ failed:
/**
* Default bhndb(4) implementation of BUS_ACTIVATE_RESOURCE().
- *
- * Maps resource activation requests to a viable static or dynamic
- * register window, if any.
*/
static int
bhndb_activate_resource(device_t dev, device_t child, int type, int rid,
@@ -1432,16 +1442,27 @@ bhndb_deactivate_resource(device_t dev, device_t child, int type,
if ((error = rman_deactivate_resource(r)))
return (error);
- /* Free any dynamic window allocation. */
- if (bhndb_get_addrspace(sc, child) == BHNDB_ADDRSPACE_BRIDGED) {
- BHNDB_LOCK(sc);
- dwa = bhndb_dw_find_resource(sc->bus_res, r);
- if (dwa != NULL)
- bhndb_dw_release(sc->bus_res, dwa, r);
- BHNDB_UNLOCK(sc);
- }
+ switch (type) {
+ case SYS_RES_IRQ:
+ /* No bridge-level state to be freed */
+ return (0);
- return (0);
+ case SYS_RES_MEMORY:
+ /* Free any dynamic window allocation. */
+ if (bhndb_get_addrspace(sc, child) == BHNDB_ADDRSPACE_BRIDGED) {
+ BHNDB_LOCK(sc);
+ dwa = bhndb_dw_find_resource(sc->bus_res, r);
+ if (dwa != NULL)
+ bhndb_dw_release(sc->bus_res, dwa, r);
+ BHNDB_UNLOCK(sc);
+ }
+
+ return (0);
+
+ default:
+ device_printf(dev, "unsupported resource type %d\n", type);
+ return (ENXIO);
+ }
}
/**
@@ -1457,12 +1478,15 @@ bhndb_get_resource_list(device_t dev, device_t child)
/**
* Default bhndb(4) implementation of BHND_BUS_ACTIVATE_RESOURCE().
*
- * For BHNDB_ADDRSPACE_NATIVE children, all resources may be assumed to
- * be activated by the bridge.
+ * For BHNDB_ADDRSPACE_NATIVE children, all resources are activated as direct
+ * resources via BUS_ACTIVATE_RESOURCE().
*
- * For BHNDB_ADDRSPACE_BRIDGED children, attempts to activate a static register
- * window, a dynamic register window, or configures @p r as an indirect
- * resource -- in that order.
+ * For BHNDB_ADDRSPACE_BRIDGED children, the resource priority is determined,
+ * and if possible, the resource is activated as a direct resource. For example,
+ * depending on resource priority and bridge resource availability, this
+ * function will attempt to activate SYS_RES_MEMORY resources using either a
+ * static register window, a dynamic register window, or it will configure @p r
+ * as an indirect resource -- in that order.
*/
static int
bhndb_activate_bhnd_resource(device_t dev, device_t child,
@@ -1470,6 +1494,7 @@ bhndb_activate_bhnd_resource(device_t dev, device_t child,
{
struct bhndb_softc *sc;
struct bhndb_region *region;
+ bhndb_priority_t r_prio;
rman_res_t r_start, r_size;
int error;
bool indirect;
@@ -1494,22 +1519,34 @@ bhndb_activate_bhnd_resource(device_t dev, device_t child,
r_start = rman_get_start(r->res);
r_size = rman_get_size(r->res);
- /* Verify bridged address range's resource priority, and skip direct
+ /* Determine the resource priority of bridged resources, and skip direct
* allocation if the priority is too low. */
if (bhndb_get_addrspace(sc, child) == BHNDB_ADDRSPACE_BRIDGED) {
- bhndb_priority_t r_prio;
-
- region = bhndb_find_resource_region(sc->bus_res, r_start,
- r_size);
- if (region != NULL)
- r_prio = region->priority;
- else
- r_prio = BHNDB_PRIORITY_NONE;
-
- /* If less than the minimum dynamic window priority, this
- * resource should always be indirect. */
- if (r_prio < sc->bus_res->min_prio)
- return (0);
+ switch (type) {
+ case SYS_RES_IRQ:
+ /* IRQ resources are always direct */
+ break;
+
+ case SYS_RES_MEMORY:
+ region = bhndb_find_resource_region(sc->bus_res,
+ r_start, r_size);
+ if (region != NULL)
+ r_prio = region->priority;
+ else
+ r_prio = BHNDB_PRIORITY_NONE;
+
+ /* If less than the minimum dynamic window priority,
+ * this resource should always be indirect. */
+ if (r_prio < sc->bus_res->min_prio)
+ return (0);
+
+ break;
+
+ default:
+ device_printf(dev, "unsupported resource type %d\n",
+ type);
+ return (ENXIO);
+ }
}
/* Attempt direct activation */
@@ -1792,6 +1829,217 @@ bhndb_bus_barrier(device_t dev, device_t child, struct bhnd_resource *r,
}
/**
+ * Default bhndb(4) implementation of BHND_MAP_INTR().
+ */
+static int
+bhndb_bhnd_map_intr(device_t dev, device_t child, u_int intr, rman_res_t *irq)
+{
+ struct bhndb_softc *sc;
+ u_int ivec;
+ int error;
+
+ sc = device_get_softc(dev);
+
+ /* Is the intr valid? */
+ if (intr >= bhnd_get_intr_count(child))
+ return (EINVAL);
+
+ /* Fetch the interrupt vector */
+ if ((error = bhnd_get_intr_ivec(child, intr, &ivec)))
+ return (error);
+
+ /* Map directly to the actual backplane interrupt vector */
+ *irq = ivec;
+
+ return (0);
+}
+
+/**
+ * Default bhndb(4) implementation of BHND_UNMAP_INTR().
+ */
+static void
+bhndb_bhnd_unmap_intr(device_t dev, device_t child, rman_res_t irq)
+{
+ /* No state to clean up */
+}
+
+/**
+ * Default bhndb(4) implementation of BUS_SETUP_INTR().
+ */
+static int
+bhndb_setup_intr(device_t dev, device_t child, struct resource *r,
+ int flags, driver_filter_t filter, driver_intr_t handler, void *arg,
+ void **cookiep)
+{
+ struct bhndb_softc *sc;
+ struct bhndb_intr_isrc *isrc;
+ struct bhndb_intr_handler *ih;
+ int error;
+
+ sc = device_get_softc(dev);
+
+ /* Fetch the isrc */
+ if ((error = BHNDB_MAP_INTR_ISRC(dev, r, &isrc))) {
+ device_printf(dev, "failed to fetch isrc: %d\n", error);
+ return (error);
+ }
+
+ /* Allocate new ihandler entry */
+ ih = bhndb_alloc_intr_handler(child, r, isrc);
+ if (ih == NULL)
+ return (ENOMEM);
+
+ /* Perform actual interrupt setup via the host isrc */
+ error = bus_setup_intr(isrc->is_owner, isrc->is_res, flags, filter,
+ handler, arg, &ih->ih_cookiep);
+ if (error) {
+ bhndb_free_intr_handler(ih);
+ return (error);
+ }
+
+ /* Add to our interrupt handler list */
+ BHNDB_LOCK(sc);
+ bhndb_register_intr_handler(sc->bus_res, ih);
+ BHNDB_UNLOCK(sc);
+
+ /* Provide the interrupt handler entry as our cookiep value */
+ *cookiep = ih;
+ return (0);
+}
+
+/**
+ * Default bhndb(4) implementation of BUS_TEARDOWN_INTR().
+ */
+static int
+bhndb_teardown_intr(device_t dev, device_t child, struct resource *r,
+ void *cookiep)
+{
+ struct bhndb_softc *sc;
+ struct bhndb_intr_handler *ih;
+ struct bhndb_intr_isrc *isrc;
+ int error;
+
+ sc = device_get_softc(dev);
+
+ /* Locate and claim ownership of the interrupt handler entry */
+ BHNDB_LOCK(sc);
+
+ ih = bhndb_find_intr_handler(sc->bus_res, cookiep);
+ if (ih == NULL) {
+ panic("%s requested teardown of invalid cookiep %p",
+ device_get_nameunit(child), cookiep);
+ }
+
+ bhndb_deregister_intr_handler(sc->bus_res, ih);
+
+ BHNDB_UNLOCK(sc);
+
+ /* Perform actual interrupt teardown via the host isrc */
+ isrc = ih->ih_isrc;
+ error = bus_teardown_intr(isrc->is_owner, isrc->is_res, ih->ih_cookiep);
+ if (error) {
+ /* If teardown fails, we need to reinsert the handler entry
+ * to allow later teardown */
+ BHNDB_LOCK(sc);
+ bhndb_register_intr_handler(sc->bus_res, ih);
+ BHNDB_UNLOCK(sc);
+
+ return (error);
+ }
+
+ /* Free the entry */
+ bhndb_free_intr_handler(ih);
+ return (0);
+}
+
+/**
+ * Default bhndb(4) implementation of BUS_BIND_INTR().
+ */
+static int
+bhndb_bind_intr(device_t dev, device_t child, struct resource *irq, int cpu)
+{
+ struct bhndb_softc *sc;
+ struct bhndb_intr_handler *ih;
+ struct bhndb_intr_isrc *isrc;
+
+ sc = device_get_softc(dev);
+ isrc = NULL;
+
+ /* Fetch the isrc corresponding to the child IRQ resource */
+ BHNDB_LOCK(sc);
+ STAILQ_FOREACH(ih, &sc->bus_res->bus_intrs, ih_link) {
+ if (ih->ih_res == irq) {
+ isrc = ih->ih_isrc;
+ break;
+ }
+ }
+ BHNDB_UNLOCK(sc);
+
+ if (isrc == NULL) {
+ panic("%s requested bind of invalid irq %#jx-%#jx",
+ device_get_nameunit(child), rman_get_start(irq),
+ rman_get_end(irq));
+ }
+
+ /* Perform actual bind via the host isrc */
+ return (bus_bind_intr(isrc->is_owner, isrc->is_res, cpu));
+}
+
+/**
+ * Default bhndb(4) implementation of BUS_DESCRIBE_INTR().
+ */
+static int
+bhndb_describe_intr(device_t dev, device_t child, struct resource *irq,
+ void *cookie, const char *descr)
+{
+ struct bhndb_softc *sc;
+ struct bhndb_intr_handler *ih;
+ struct bhndb_intr_isrc *isrc;
+
+ sc = device_get_softc(dev);
+
+ /* Locate the interrupt handler entry; the caller owns the handler
+ * reference, and thus our entry is guaranteed to remain valid after
+ * we drop out lock below. */
+ BHNDB_LOCK(sc);
+
+ ih = bhndb_find_intr_handler(sc->bus_res, cookie);
+ if (ih == NULL) {
+ panic("%s requested invalid cookiep %p",
+ device_get_nameunit(child), cookie);
+ }
+
+ isrc = ih->ih_isrc;
+
+ BHNDB_UNLOCK(sc);
+
+ /* Perform the actual request via the host isrc */
+ return (BUS_DESCRIBE_INTR(device_get_parent(isrc->is_owner),
+ isrc->is_owner, isrc->is_res, ih->ih_cookiep, descr));
+}
+
+/**
+ * Default bhndb(4) implementation of BUS_CONFIG_INTR().
+ */
+static int
+bhndb_config_intr(device_t dev, int irq, enum intr_trigger trig,
+ enum intr_polarity pol)
+{
+ /* Unsupported */
+ return (ENXIO);
+}
+
+/**
+ * Default bhndb(4) implementation of BUS_REMAP_INTR().
+ */
+static int
+bhndb_remap_intr(device_t dev, device_t child, u_int irq)
+{
+ /* Unsupported */
+ return (ENXIO);
+}
+
+/**
* Default bhndb(4) implementation of BUS_GET_DMA_TAG().
*/
static bus_dma_tag_t
@@ -1822,11 +2070,12 @@ static device_method_t bhndb_methods[] = {
DEVMETHOD(bus_activate_resource, bhndb_activate_resource),
DEVMETHOD(bus_deactivate_resource, bhndb_deactivate_resource),
- DEVMETHOD(bus_setup_intr, bus_generic_setup_intr),
- DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr),
- DEVMETHOD(bus_config_intr, bus_generic_config_intr),
- DEVMETHOD(bus_bind_intr, bus_generic_bind_intr),
- DEVMETHOD(bus_describe_intr, bus_generic_describe_intr),
+ DEVMETHOD(bus_setup_intr, bhndb_setup_intr),
+ DEVMETHOD(bus_teardown_intr, bhndb_teardown_intr),
+ DEVMETHOD(bus_config_intr, bhndb_config_intr),
+ DEVMETHOD(bus_bind_intr, bhndb_bind_intr),
+ DEVMETHOD(bus_describe_intr, bhndb_describe_intr),
+ DEVMETHOD(bus_remap_intr, bhndb_remap_intr),
DEVMETHOD(bus_get_dma_tag, bhndb_get_dma_tag),
@@ -1851,6 +2100,8 @@ static device_method_t bhndb_methods[] = {
DEVMETHOD(bhnd_bus_activate_resource, bhndb_activate_bhnd_resource),
DEVMETHOD(bhnd_bus_deactivate_resource, bhndb_deactivate_bhnd_resource),
DEVMETHOD(bhnd_bus_get_nvram_var, bhnd_bus_generic_get_nvram_var),
+ DEVMETHOD(bhnd_bus_map_intr, bhndb_bhnd_map_intr),
+ DEVMETHOD(bhnd_bus_unmap_intr, bhndb_bhnd_unmap_intr),
DEVMETHOD(bhnd_bus_get_service_registry,bhndb_get_service_registry),
DEVMETHOD(bhnd_bus_register_provider, bhnd_bus_generic_sr_register_provider),
diff --git a/sys/dev/bhnd/bhndb/bhndb_if.m b/sys/dev/bhnd/bhndb/bhndb_if.m
index 3f9ebd237b2c..e16f560eafd3 100644
--- a/sys/dev/bhnd/bhndb/bhndb_if.m
+++ b/sys/dev/bhnd/bhndb/bhndb_if.m
@@ -1,7 +1,11 @@
#-
-# Copyright (c) 2015 Landon Fuller <landon@landonf.org>
+# Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org>
+# Copyright (c) 2017 The FreeBSD Foundation
# All rights reserved.
#
+# Portions of this software were developed by Landon Fuller
+# under sponsorship from the FreeBSD Foundation.
+#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
@@ -40,6 +44,7 @@
INTERFACE bhndb;
HEADER {
+ struct bhndb_intr_isrc;
struct bhndb_regwin;
struct bhndb_hw;
struct bhndb_hw_priority;
@@ -91,11 +96,24 @@ CODE {
}
static int
+ bhndb_null_route_interrupts(device_t dev, device_t child)
+ {
+ panic("bhndb_route_interrupts unimplemented");
+ }
+
+ static int
bhndb_null_set_window_addr(device_t dev,
const struct bhndb_regwin *rw, bhnd_addr_t addr)
{
panic("bhndb_set_window_addr unimplemented");
}
+
+ static int
+ bhndb_null_map_intr_isrc(device_t dev, struct resource *irq,
+ struct bhndb_intr_isrc **isrc)
+ {
+ panic("bhndb_map_intr_isrc unimplemented");
+ }
}
/**
@@ -208,6 +226,17 @@ METHOD int resume_resource {
} DEFAULT bhndb_null_resume_resource;
/**
+ * Enable bridge-level interrupt routing for @p child.
+ *
+ * @param dev The bridge device.
+ * @param child The bhnd child device for which interrupts should be routed.
+ */
+METHOD int route_interrupts {
+ device_t dev;
+ device_t child;
+} DEFAULT bhndb_null_route_interrupts;
+
+/**
* Set a given register window's base address.
*
* @param dev The bridge device.
@@ -224,3 +253,22 @@ METHOD int set_window_addr {
const struct bhndb_regwin *win;
bhnd_addr_t addr;
} DEFAULT bhndb_null_set_window_addr;
+
+/**
+ * Map a bridged interrupt resource to its corresponding host interrupt source,
+ * if any.
+ *
+ * @param dev The bridge device.
+ * @param irq The bridged interrupt resource.
+ * @param[out] isrc The host interrupt source to which the bridged interrupt
+ * is routed.
+ *
+ * @retval 0 success
+ * @retval non-zero if mapping @p irq otherwise fails, a regular unix error code
+ * will be returned.
+ */
+METHOD int map_intr_isrc {
+ device_t dev;
+ struct resource *irq;
+ struct bhndb_intr_isrc **isrc;
+} DEFAULT bhndb_null_map_intr_isrc;
diff --git a/sys/dev/bhnd/bhndb/bhndb_pci.c b/sys/dev/bhnd/bhndb/bhndb_pci.c
index 3fe38628e3c4..5c40c3202d23 100644
--- a/sys/dev/bhnd/bhndb/bhndb_pci.c
+++ b/sys/dev/bhnd/bhndb/bhndb_pci.c
@@ -64,6 +64,8 @@ __FBSDID("$FreeBSD$");
#include <dev/bhnd/bhnd_erom.h>
#include <dev/bhnd/bhnd_eromvar.h>
+#include <dev/bhnd/siba/sibareg.h>
+
#include <dev/bhnd/cores/pci/bhnd_pcireg.h>
#include "bhndb_pcireg.h"
@@ -72,13 +74,15 @@ __FBSDID("$FreeBSD$");
struct bhndb_pci_eio;
-static int bhndb_pci_init_msi(struct bhndb_pci_softc *sc);
+static int bhndb_pci_alloc_msi(struct bhndb_pci_softc *sc,
+ int *msi_count);
static int bhndb_pci_read_core_table(device_t dev,
struct bhnd_chipid *chipid,
struct bhnd_core_info **cores, u_int *ncores,
bhnd_erom_class_t **eromcls);
static int bhndb_pci_add_children(struct bhndb_pci_softc *sc);
+static bhnd_devclass_t bhndb_expected_pci_devclass(device_t dev);
static bool bhndb_is_pcie_attached(device_t dev);
static int bhndb_enable_pci_clocks(device_t dev);
@@ -90,6 +94,11 @@ static int bhndb_pci_compat_setregwin(device_t dev,
static int bhndb_pci_fast_setregwin(device_t dev, device_t pci_dev,
const struct bhndb_regwin *, bhnd_addr_t);
+static void bhndb_pci_write_core(struct bhndb_pci_softc *sc,
+ bus_size_t offset, uint32_t value, u_int width);
+static uint32_t bhndb_pci_read_core(struct bhndb_pci_softc *sc,
+ bus_size_t offset, u_int width);
+
static void bhndb_init_sromless_pci_config(
struct bhndb_pci_softc *sc);
@@ -106,7 +115,18 @@ static uint32_t bhndb_pci_eio_read(struct bhnd_erom_io *eio,
#define BHNDB_PCI_MSI_COUNT 1
-/* bhndb_pci erom I/O implementation */
+static struct bhndb_pci_quirk bhndb_pci_quirks[];
+static struct bhndb_pci_quirk bhndb_pcie_quirks[];
+static struct bhndb_pci_quirk bhndb_pcie2_quirks[];
+
+static struct bhndb_pci_core bhndb_pci_cores[] = {
+ BHNDB_PCI_CORE(PCI, BHND_PCI_SRSH_PI_OFFSET, bhndb_pci_quirks),
+ BHNDB_PCI_CORE(PCIE, BHND_PCIE_SRSH_PI_OFFSET, bhndb_pcie_quirks),
+ BHNDB_PCI_CORE(PCIE2, BHND_PCIE_SRSH_PI_OFFSET, bhndb_pcie2_quirks),
+ BHNDB_PCI_CORE_END
+};
+
+/* bhndb_pci erom I/O instance state */
struct bhndb_pci_eio {
struct bhnd_erom_io eio;
device_t dev; /**< bridge device */
@@ -120,6 +140,83 @@ struct bhndb_pci_eio {
bhnd_size_t size; /**< mapped size */
};
+static struct bhndb_pci_quirk bhndb_pci_quirks[] = {
+ /* Backplane interrupt flags must be routed via siba-specific
+ * SIBA_CFG0_INTVEC configuration register; the BHNDB_PCI_INT_MASK
+ * PCI configuration register is unsupported. */
+ {{ BHND_MATCH_CHIP_TYPE (SIBA) },
+ { BHND_MATCH_CORE_REV (HWREV_LTE(5)) },
+ BHNDB_PCI_QUIRK_SIBA_INTVEC },
+
+ /* All PCI core revisions require the SRSH work-around */
+ BHNDB_PCI_QUIRK(HWREV_ANY, BHNDB_PCI_QUIRK_SRSH_WAR),
+ BHNDB_PCI_QUIRK_END
+};
+
+static struct bhndb_pci_quirk bhndb_pcie_quirks[] = {
+ /* All PCIe-G1 core revisions require the SRSH work-around */
+ BHNDB_PCI_QUIRK(HWREV_ANY, BHNDB_PCI_QUIRK_SRSH_WAR),
+ BHNDB_PCI_QUIRK_END
+};
+
+static struct bhndb_pci_quirk bhndb_pcie2_quirks[] = {
+ /* All PCIe-G2 core revisions require the SRSH work-around */
+ BHNDB_PCI_QUIRK(HWREV_ANY, BHNDB_PCI_QUIRK_SRSH_WAR),
+ BHNDB_PCI_QUIRK_END
+};
+
+
+/**
+ * Return the device table entry for @p ci, or NULL if none.
+ */
+static struct bhndb_pci_core *
+bhndb_pci_find_core(struct bhnd_core_info *ci)
+{
+ for (size_t i = 0; !BHNDB_PCI_IS_CORE_END(&bhndb_pci_cores[i]); i++) {
+ struct bhndb_pci_core *entry = &bhndb_pci_cores[i];
+
+ if (bhnd_core_matches(ci, &entry->match))
+ return (entry);
+ }
+
+ return (NULL);
+}
+
+/**
+ * Return all quirk flags for the given @p cid and @p ci.
+ */
+static uint32_t
+bhndb_pci_get_core_quirks(struct bhnd_chipid *cid, struct bhnd_core_info *ci)
+{
+ struct bhndb_pci_core *entry;
+ struct bhndb_pci_quirk *qtable;
+ uint32_t quirks;
+
+ quirks = 0;
+
+ /* No core entry? */
+ if ((entry = bhndb_pci_find_core(ci)) == NULL)
+ return (quirks);
+
+ /* No quirks? */
+ if ((qtable = entry->quirks) == NULL)
+ return (quirks);
+
+ for (size_t i = 0; !BHNDB_PCI_IS_QUIRK_END(&qtable[i]); i++) {
+ struct bhndb_pci_quirk *q = &qtable[i];
+
+ if (!bhnd_chip_matches(cid, &q->chip_desc))
+ continue;
+
+ if (!bhnd_core_matches(ci, &q->core_desc))
+ continue;
+
+ quirks |= q->quirks;
+ }
+
+ return (quirks);
+}
+
/**
* Default bhndb_pci implementation of device_probe().
*
@@ -128,9 +225,16 @@ struct bhndb_pci_eio {
static int
bhndb_pci_probe(device_t dev)
{
- device_t parent;
- devclass_t parent_bus;
- devclass_t pci;
+ struct bhnd_chipid cid;
+ struct bhnd_core_info *cores, hostb_core;
+ struct bhndb_pci_core *entry;
+ bhnd_devclass_t hostb_devclass;
+ u_int ncores;
+ device_t parent;
+ devclass_t parent_bus, pci;
+ int error;
+
+ cores = NULL;
/* Our parent must be a PCI/PCIe device. */
pci = devclass_find("pci");
@@ -140,35 +244,67 @@ bhndb_pci_probe(device_t dev)
if (parent_bus != pci)
return (ENXIO);
+ /* Enable clocks */
+ if ((error = bhndb_enable_pci_clocks(dev)))
+ return (error);
+
+ /* Identify the chip and enumerate the bridged cores */
+ error = bhndb_pci_read_core_table(dev, &cid, &cores, &ncores, NULL);
+ if (error)
+ goto cleanup;
+
+ /* Search our core table for the host bridge core */
+ hostb_devclass = bhndb_expected_pci_devclass(dev);
+ error = bhndb_find_hostb_core(cores, ncores, hostb_devclass,
+ &hostb_core);
+ if (error)
+ goto cleanup;
+
+ /* Look for a matching core table entry */
+ if ((entry = bhndb_pci_find_core(&hostb_core)) == NULL) {
+ error = ENXIO;
+ goto cleanup;
+ }
+
device_set_desc(dev, "PCI-BHND bridge");
- return (BUS_PROBE_DEFAULT);
+ /* fall-through */
+ error = BUS_PROBE_DEFAULT;
+
+cleanup:
+ bhndb_disable_pci_clocks(dev);
+ if (cores != NULL)
+ free(cores, M_BHND);
+
+ return (error);
}
-/* Configure MSI interrupts */
+/**
+ * Attempt to allocate MSI interrupts, returning the count in @p msi_count
+ * on success.
+ */
static int
-bhndb_pci_init_msi(struct bhndb_pci_softc *sc)
+bhndb_pci_alloc_msi(struct bhndb_pci_softc *sc, int *msi_count)
{
- int error;
+ int error, count;
/* Is MSI available? */
if (pci_msi_count(sc->parent) < BHNDB_PCI_MSI_COUNT)
return (ENXIO);
/* Allocate expected message count */
- sc->intr.msi_count = BHNDB_PCI_MSI_COUNT;
- if ((error = pci_alloc_msi(sc->parent, &sc->intr.msi_count))) {
+ count = BHNDB_PCI_MSI_COUNT;
+ if ((error = pci_alloc_msi(sc->parent, &count))) {
device_printf(sc->dev, "failed to allocate MSI interrupts: "
"%d\n", error);
+
return (error);
}
- if (sc->intr.msi_count < BHNDB_PCI_MSI_COUNT)
+ if (count < BHNDB_PCI_MSI_COUNT)
return (ENXIO);
- /* MSI uses resource IDs starting at 1 */
- sc->intr.intr_rid = 1;
-
+ *msi_count = count;
return (0);
}
@@ -180,34 +316,46 @@ bhndb_pci_attach(device_t dev)
struct bhnd_core_info *cores, hostb_core;
bhnd_erom_class_t *erom_class;
u_int ncores;
- int error, reg;
+ int irq_rid;
+ int error;
sc = device_get_softc(dev);
sc->dev = dev;
sc->parent = device_get_parent(dev);
+ sc->pci_devclass = bhndb_expected_pci_devclass(dev);
+ sc->pci_quirks = 0;
sc->set_regwin = NULL;
+ BHNDB_PCI_LOCK_INIT(sc);
+
cores = NULL;
/* Enable PCI bus mastering */
pci_enable_busmaster(sc->parent);
/* Set up PCI interrupt handling */
- if (bhndb_pci_init_msi(sc) == 0) {
+ if (bhndb_pci_alloc_msi(sc, &sc->msi_count) == 0) {
+ /* MSI uses resource IDs starting at 1 */
+ irq_rid = 1;
+
device_printf(dev, "Using MSI interrupts on %s\n",
device_get_nameunit(sc->parent));
} else {
+ sc->msi_count = 0;
+ irq_rid = 0;
+
device_printf(dev, "Using INTx interrupts on %s\n",
device_get_nameunit(sc->parent));
- sc->intr.intr_rid = 0;
}
- /* Determine our bridge device class */
- sc->pci_devclass = BHND_DEVCLASS_PCI;
- if (pci_find_cap(sc->parent, PCIY_EXPRESS, &reg) == 0)
- sc->pci_devclass = BHND_DEVCLASS_PCIE;
- else
- sc->pci_devclass = BHND_DEVCLASS_PCI;
+ sc->isrc = bhndb_alloc_intr_isrc(sc->parent, irq_rid, 0, RM_MAX_END, 1,
+ RF_SHAREABLE | RF_ACTIVE);
+ if (sc->isrc == NULL) {
+ device_printf(sc->dev, "failed to allocate interrupt "
+ "resource\n");
+ error = ENXIO;
+ goto cleanup;
+ }
/* Enable clocks (if required by this hardware) */
if ((error = bhndb_enable_pci_clocks(sc->dev)))
@@ -226,12 +374,14 @@ bhndb_pci_attach(device_t dev)
sc->set_regwin = bhndb_pci_fast_setregwin;
}
- /* Determine our host bridge core */
+ /* Determine our host bridge core and populate our quirk flags */
error = bhndb_find_hostb_core(cores, ncores, sc->pci_devclass,
&hostb_core);
if (error)
goto cleanup;
+ sc->pci_quirks = bhndb_pci_get_core_quirks(&cid, &hostb_core);
+
/* Perform bridge attach */
error = bhndb_attach(dev, &cid, cores, ncores, &hostb_core, erom_class);
if (error)
@@ -256,7 +406,10 @@ cleanup:
device_delete_children(dev);
bhndb_disable_pci_clocks(sc->dev);
- if (sc->intr.msi_count > 0)
+ if (sc->isrc != NULL)
+ bhndb_free_intr_isrc(sc->isrc);
+
+ if (sc->msi_count > 0)
pci_release_msi(dev);
if (cores != NULL)
@@ -264,6 +417,8 @@ cleanup:
pci_disable_busmaster(sc->parent);
+ BHNDB_PCI_LOCK_DESTROY(sc);
+
return (error);
}
@@ -287,13 +442,18 @@ bhndb_pci_detach(device_t dev)
if ((error = bhndb_disable_pci_clocks(sc->dev)))
return (error);
+ /* Free our interrupt resources */
+ bhndb_free_intr_isrc(sc->isrc);
+
/* Release MSI interrupts */
- if (sc->intr.msi_count > 0)
+ if (sc->msi_count > 0)
pci_release_msi(dev);
/* Disable PCI bus mastering */
pci_disable_busmaster(sc->parent);
+ BHNDB_PCI_LOCK_DESTROY(sc);
+
return (0);
}
@@ -541,6 +701,112 @@ bhndb_pci_sprom_size(struct bhndb_pci_softc *sc)
return (sprom_sz);
}
+/**
+ * Return the host resource providing a static mapping of the PCI core's
+ * registers.
+ *
+ * @param sc bhndb PCI driver state.
+ * @param[out] res On success, the host resource containing our PCI
+ * core's register window.
+ * @param[out] offset On success, the offset of the PCI core registers within
+ * @p res.
+ *
+ * @retval 0 success
+ * @retval ENXIO if a valid static register window mapping the PCI core
+ * registers is not available.
+ */
+static int
+bhndb_pci_get_core_regs(struct bhndb_pci_softc *sc, struct resource **res,
+ bus_size_t *offset)
+{
+ const struct bhndb_regwin *win;
+ struct resource *r;
+
+ /* Locate the static register window mapping the PCI core */
+ win = bhndb_regwin_find_core(sc->bhndb.bus_res->cfg->register_windows,
+ sc->pci_devclass, 0, BHND_PORT_DEVICE, 0, 0);
+ if (win == NULL) {
+ device_printf(sc->dev, "missing PCI core register window\n");
+ return (ENXIO);
+ }
+
+ /* Fetch the resource containing the register window */
+ r = bhndb_host_resource_for_regwin(sc->bhndb.bus_res->res, win);
+ if (r == NULL) {
+ device_printf(sc->dev, "missing PCI core register resource\n");
+ return (ENXIO);
+ }
+
+ *res = r;
+ *offset = win->win_offset;
+
+ return (0);
+}
+
+/**
+ * Write a 1, 2, or 4 byte data item to the PCI core's registers at @p offset.
+ *
+ * @param sc bhndb PCI driver state.
+ * @param offset register write offset.
+ * @param value value to be written.
+ * @param width item width (1, 2, or 4 bytes).
+ */
+static void
+bhndb_pci_write_core(struct bhndb_pci_softc *sc, bus_size_t offset,
+ uint32_t value, u_int width)
+{
+ struct resource *r;
+ bus_size_t r_offset;
+ int error;
+
+ if ((error = bhndb_pci_get_core_regs(sc, &r, &r_offset)))
+ panic("no PCI core registers: %d", error);
+
+ switch (width) {
+ case 1:
+ bus_write_1(r, r_offset + offset, value);
+ break;
+ case 2:
+ bus_write_2(r, r_offset + offset, value);
+ break;
+ case 4:
+ bus_write_4(r, r_offset + offset, value);
+ break;
+ default:
+ panic("invalid width: %u", width);
+ }
+}
+
+/**
+ * Read a 1, 2, or 4 byte data item from the PCI core's registers
+ * at @p offset.
+ *
+ * @param sc bhndb PCI driver state.
+ * @param offset register read offset.
+ * @param width item width (1, 2, or 4 bytes).
+ */
+static uint32_t
+bhndb_pci_read_core(struct bhndb_pci_softc *sc, bus_size_t offset, u_int width)
+{
+ struct resource *r;
+ bus_size_t r_offset;
+ int error;
+
+ if ((error = bhndb_pci_get_core_regs(sc, &r, &r_offset)))
+ panic("no PCI core registers: %d", error);
+
+ switch (width) {
+ case 1:
+ return (bus_read_1(r, r_offset + offset));
+ case 2:
+ return (bus_read_2(r, r_offset + offset));
+ case 4:
+ return (bus_read_4(r, r_offset + offset));
+ default:
+ panic("invalid width: %u", width);
+ }
+}
+
/*
* On devices without a SROM, the PCI(e) cores will be initialized with
* their Power-on-Reset defaults; this can leave two of the BAR0 PCI windows
@@ -554,68 +820,31 @@ bhndb_pci_sprom_size(struct bhndb_pci_softc *sc)
static void
bhndb_init_sromless_pci_config(struct bhndb_pci_softc *sc)
{
- struct bhndb_resources *bres;
- const struct bhndb_hwcfg *cfg;
- const struct bhndb_regwin *win;
- struct bhnd_core_info hostb_core;
- struct resource *core_regs;
- bus_size_t srom_offset;
+ const struct bhndb_pci_core *pci_core;
+ bus_size_t srsh_offset;
u_int pci_cidx, sprom_cidx;
uint16_t val;
- int error;
-
- bres = sc->bhndb.bus_res;
- cfg = bres->cfg;
- /* Find our hostb core */
- error = BHNDB_GET_HOSTB_CORE(sc->dev, sc->bhndb.bus_dev, &hostb_core);
- if (error) {
- device_printf(sc->dev, "no host bridge device found\n");
+ if ((sc->pci_quirks & BHNDB_PCI_QUIRK_SRSH_WAR) == 0)
return;
- }
- if (hostb_core.vendor != BHND_MFGID_BCM)
- return;
-
- switch (hostb_core.device) {
- case BHND_COREID_PCI:
- srom_offset = BHND_PCI_SRSH_PI_OFFSET;
- break;
- case BHND_COREID_PCIE:
- srom_offset = BHND_PCIE_SRSH_PI_OFFSET;
- break;
- default:
- device_printf(sc->dev, "unsupported PCI host bridge device\n");
- return;
- }
-
- /* Locate the static register window mapping the PCI core */
- win = bhndb_regwin_find_core(cfg->register_windows, sc->pci_devclass,
- 0, BHND_PORT_DEVICE, 0, 0);
- if (win == NULL) {
- device_printf(sc->dev, "missing PCI core register window\n");
- return;
- }
+ /* Determine the correct register offset for our PCI core */
+ pci_core = bhndb_pci_find_core(&sc->bhndb.bridge_core);
+ KASSERT(pci_core != NULL, ("missing core table entry"));
- /* Fetch the resource containing the register window */
- core_regs = bhndb_host_resource_for_regwin(bres->res, win);
- if (core_regs == NULL) {
- device_printf(sc->dev, "missing PCI core register resource\n");
- return;
- }
+ srsh_offset = pci_core->srsh_offset;
/* Fetch the SPROM's configured core index */
- val = bus_read_2(core_regs, win->win_offset + srom_offset);
+ val = bhndb_pci_read_core(sc, srsh_offset, sizeof(val));
sprom_cidx = (val & BHND_PCI_SRSH_PI_MASK) >> BHND_PCI_SRSH_PI_SHIFT;
/* If it doesn't match host bridge's core index, update the index
* value */
- pci_cidx = hostb_core.core_idx;
+ pci_cidx = sc->bhndb.bridge_core.core_idx;
if (sprom_cidx != pci_cidx) {
val &= ~BHND_PCI_SRSH_PI_MASK;
val |= (pci_cidx << BHND_PCI_SRSH_PI_SHIFT);
- bus_write_2(core_regs,
- win->win_offset + srom_offset, val);
+ bhndb_pci_write_core(sc, srsh_offset, val, sizeof(val));
}
}
@@ -770,7 +999,22 @@ bhndb_pci_populate_board_info(device_t dev, device_t child,
}
/**
- * Return true if the bridge device @p bhndb is attached via PCIe,
+ * Examine the bridge device @p dev and return the expected host bridge
+ * device class.
+ *
+ * @param dev The bhndb bridge device
+ */
+static bhnd_devclass_t
+bhndb_expected_pci_devclass(device_t dev)
+{
+ if (bhndb_is_pcie_attached(dev))
+ return (BHND_DEVCLASS_PCIE);
+ else
+ return (BHND_DEVCLASS_PCI);
+}
+
+/**
+ * Return true if the bridge device @p dev is attached via PCIe,
* false otherwise.
*
* @param dev The bhndb bridge device
@@ -939,27 +1183,85 @@ bhndb_pci_pwrctl_ungate_clock(device_t dev, device_t child,
return (bhndb_enable_pci_clocks(sc->dev));
}
+/**
+ * BHNDB_MAP_INTR_ISRC()
+ */
+static int
+bhndb_pci_map_intr_isrc(device_t dev, struct resource *irq,
+ struct bhndb_intr_isrc **isrc)
+{
+ struct bhndb_pci_softc *sc = device_get_softc(dev);
+
+ /* There's only one bridged interrupt to choose from */
+ *isrc = sc->isrc;
+ return (0);
+}
+
+/* siba-specific implementation of BHNDB_ROUTE_INTERRUPTS() */
+static int
+bhndb_pci_route_siba_interrupts(struct bhndb_pci_softc *sc, device_t child)
+{
+ uint32_t sbintvec;
+ u_int ivec;
+ int error;
+
+ KASSERT(sc->pci_quirks & BHNDB_PCI_QUIRK_SIBA_INTVEC,
+ ("route_siba_interrupts not supported by this hardware"));
+
+ /* Fetch the sbflag# for the child */
+ if ((error = bhnd_get_intr_ivec(child, 0, &ivec)))
+ return (error);
+
+ if (ivec > (sizeof(sbintvec)*8) - 1 /* aka '31' */) {
+ /* This should never be an issue in practice */
+ device_printf(sc->dev, "cannot route interrupts to high "
+ "sbflag# %u\n", ivec);
+ return (ENXIO);
+ }
+
+ BHNDB_PCI_LOCK(sc);
+
+ sbintvec = bhndb_pci_read_core(sc, SB0_REG_ABS(SIBA_CFG0_INTVEC), 4);
+ sbintvec |= (1 << ivec);
+ bhndb_pci_write_core(sc, SB0_REG_ABS(SIBA_CFG0_INTVEC), sbintvec, 4);
+
+ BHNDB_PCI_UNLOCK(sc);
+
+ return (0);
+}
+
+/* BHNDB_ROUTE_INTERRUPTS() */
static int
-bhndb_pci_assign_intr(device_t dev, device_t child, int rid)
+bhndb_pci_route_interrupts(device_t dev, device_t child)
{
struct bhndb_pci_softc *sc;
- rman_res_t start, count;
- int error;
+ struct bhnd_core_info core;
+ uint32_t core_bit;
+ uint32_t intmask;
sc = device_get_softc(dev);
- /* Is the rid valid? */
- if (rid >= bhnd_get_intr_count(child))
- return (EINVAL);
-
- /* Fetch our common PCI interrupt's start/count. */
- error = bus_get_resource(sc->parent, SYS_RES_IRQ, sc->intr.intr_rid,
- &start, &count);
- if (error)
- return (error);
+ if (sc->pci_quirks & BHNDB_PCI_QUIRK_SIBA_INTVEC)
+ return (bhndb_pci_route_siba_interrupts(sc, child));
- /* Add to child's resource list */
- return (bus_set_resource(child, SYS_RES_IRQ, rid, start, count));
+ core = bhnd_get_core_info(child);
+ if (core.core_idx > BHNDB_PCI_SBIM_COREIDX_MAX) {
+ /* This should never be an issue in practice */
+ device_printf(dev, "cannot route interrupts to high core "
+ "index %u\n", core.core_idx);
+ return (ENXIO);
+ }
+
+ BHNDB_PCI_LOCK(sc);
+
+ core_bit = (1<<core.core_idx) << BHNDB_PCI_SBIM_SHIFT;
+ intmask = pci_read_config(sc->parent, BHNDB_PCI_INT_MASK, 4);
+ intmask |= core_bit;
+ pci_write_config(sc->parent, BHNDB_PCI_INT_MASK, intmask, 4);
+
+ BHNDB_PCI_UNLOCK(sc);
+
+ return (0);
}
/**
@@ -1156,8 +1458,6 @@ static device_method_t bhndb_pci_methods[] = {
DEVMETHOD(device_detach, bhndb_pci_detach),
/* BHND interface */
- DEVMETHOD(bhnd_bus_assign_intr, bhndb_pci_assign_intr),
-
DEVMETHOD(bhnd_bus_pwrctl_get_clksrc, bhndb_pci_pwrctl_get_clksrc),
DEVMETHOD(bhnd_bus_pwrctl_gate_clock, bhndb_pci_pwrctl_gate_clock),
DEVMETHOD(bhnd_bus_pwrctl_ungate_clock, bhndb_pci_pwrctl_ungate_clock),
@@ -1165,6 +1465,8 @@ static device_method_t bhndb_pci_methods[] = {
/* BHNDB interface */
DEVMETHOD(bhndb_set_window_addr, bhndb_pci_set_window_addr),
DEVMETHOD(bhndb_populate_board_info, bhndb_pci_populate_board_info),
+ DEVMETHOD(bhndb_map_intr_isrc, bhndb_pci_map_intr_isrc),
+ DEVMETHOD(bhndb_route_interrupts, bhndb_pci_route_interrupts),
DEVMETHOD_END
};
diff --git a/sys/dev/bhnd/bhndb/bhndb_pcireg.h b/sys/dev/bhnd/bhndb/bhndb_pcireg.h
index e398cc4c99f1..bf7c8b428e92 100644
--- a/sys/dev/bhnd/bhndb/bhndb_pcireg.h
+++ b/sys/dev/bhnd/bhndb/bhndb_pcireg.h
@@ -184,6 +184,7 @@
/* BHNDB_PCI_INT_MASK */
#define BHNDB_PCI_SBIM_SHIFT 8 /* backplane core interrupt mask bits offset */
+#define BHNDB_PCI_SBIM_COREIDX_MAX 15 /**< maximum representible core index (in 16 bit field) */
#define BHNDB_PCI_SBIM_MASK 0xff00 /* backplane core interrupt mask */
#define BHNDB_PCI_SBIM_MASK_SERR 0x4 /* backplane SBErr interrupt mask */
diff --git a/sys/dev/bhnd/bhndb/bhndb_pcivar.h b/sys/dev/bhnd/bhndb/bhndb_pcivar.h
index c00ac326c798..ac7bf4648103 100644
--- a/sys/dev/bhnd/bhndb/bhndb_pcivar.h
+++ b/sys/dev/bhnd/bhndb/bhndb_pcivar.h
@@ -1,7 +1,11 @@
/*-
- * Copyright (c) 2015 Landon Fuller <landon@landonf.org>
+ * Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org>
+ * Copyright (c) 2017 The FreeBSD Foundation
* All rights reserved.
*
+ * Portions of this software were developed by Landon Fuller
+ * under sponsorship from the FreeBSD Foundation.
+ *
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@@ -48,20 +52,82 @@ struct bhndb_pci_softc;
typedef int (*bhndb_pci_set_regwin_t)(device_t dev, device_t pci_dev,
const struct bhndb_regwin *rw, bhnd_addr_t addr);
-/* bhndb_pci interrupt state */
-struct bhndb_pci_intr {
- int msi_count; /**< MSI count, or 0 */
- int intr_rid; /**< interrupt resource ID.*/
+/**
+ * PCI/PCIe bridge-level device quirks
+ */
+enum {
+ /** No quirks */
+ BHNDB_PCI_QUIRK_NONE = 0,
+
+ /**
+ * The core requires fixup of the BAR0 SROM shadow to point at the
+ * current PCI core.
+ */
+ BHNDB_PCI_QUIRK_SRSH_WAR = (1<<0),
+
+ /**
+ * The PCI (rev <= 5) core does not provide interrupt status/mask
+ * registers; these siba-only devices require routing backplane
+ * interrupt flags via the SIBA_CFG0_INTVEC register.
+ */
+ BHNDB_PCI_QUIRK_SIBA_INTVEC = (1<<1),
};
+/** bhndb_pci quirk table entry */
+struct bhndb_pci_quirk {
+ struct bhnd_chip_match chip_desc; /**< chip match descriptor */
+ struct bhnd_core_match core_desc; /**< core match descriptor */
+ uint32_t quirks; /**< quirk flags */
+};
+
+#define BHNDB_PCI_QUIRK(_rev, _flags) { \
+ { BHND_MATCH_ANY }, \
+ { BHND_MATCH_CORE_REV(_rev) }, \
+ _flags, \
+}
+
+#define BHNDB_PCI_QUIRK_END \
+ { { BHND_MATCH_ANY }, { BHND_MATCH_ANY }, 0 }
+
+#define BHNDB_PCI_IS_QUIRK_END(_q) \
+ (BHND_MATCH_IS_ANY(&(_q)->core_desc) && \
+ BHND_MATCH_IS_ANY(&(_q)->chip_desc) && \
+ (_q)->quirks == 0)
+
+/** bhndb_pci core table entry */
+struct bhndb_pci_core {
+ struct bhnd_core_match match; /**< core match descriptor */
+ bus_size_t srsh_offset; /**< offset to SRSH_PI register, if any */
+ struct bhndb_pci_quirk *quirks; /**< quirk table */
+};
+
+#define BHNDB_PCI_CORE(_device, _srsh, _quirks) { \
+ { BHND_MATCH_CORE(BHND_MFGID_BCM, BHND_COREID_ ## _device) }, \
+ _srsh, \
+ _quirks \
+}
+#define BHNDB_PCI_CORE_END { { BHND_MATCH_ANY }, 0, NULL }
+#define BHNDB_PCI_IS_CORE_END(_c) BHND_MATCH_IS_ANY(&(_c)->match)
+
struct bhndb_pci_softc {
- struct bhndb_softc bhndb; /**< parent softc */
- device_t dev; /**< bridge device */
- device_t parent; /**< parent PCI device */
- bhnd_devclass_t pci_devclass; /**< PCI core's devclass */
- struct bhndb_pci_intr intr; /**< PCI interrupt config */
+ struct bhndb_softc bhndb; /**< parent softc */
+ device_t dev; /**< bridge device */
+ device_t parent; /**< parent PCI device */
+ bhnd_devclass_t pci_devclass; /**< PCI core's devclass */
+ uint32_t pci_quirks; /**< PCI bridge-level quirks */
+ int msi_count; /**< MSI count, or 0 */
+ struct bhndb_intr_isrc *isrc; /**< host interrupt source */
- bhndb_pci_set_regwin_t set_regwin; /**< regwin handler */
+ struct mtx mtx;
+ bhndb_pci_set_regwin_t set_regwin; /**< regwin handler */
};
+#define BHNDB_PCI_LOCK_INIT(sc) \
+ mtx_init(&(sc)->mtx, device_get_nameunit((sc)->dev), \
+ "bhndb_pc state", MTX_DEF)
+#define BHNDB_PCI_LOCK(sc) mtx_lock(&(sc)->mtx)
+#define BHNDB_PCI_UNLOCK(sc) mtx_unlock(&(sc)->mtx)
+#define BHNDB_PCI_LOCK_ASSERT(sc, what) mtx_assert(&(sc)->mtx, what)
+#define BHNDB_PCI_LOCK_DESTROY(sc) mtx_destroy(&(sc)->mtx)
+
#endif /* _BHND_BHNDB_PCIVAR_H_ */
diff --git a/sys/dev/bhnd/bhndb/bhndb_private.h b/sys/dev/bhnd/bhndb/bhndb_private.h
index bb4f31a76708..206ee400e419 100644
--- a/sys/dev/bhnd/bhndb/bhndb_private.h
+++ b/sys/dev/bhnd/bhndb/bhndb_private.h
@@ -1,10 +1,14 @@
/*-
* Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org>
+ * Copyright (c) 2017 The FreeBSD Foundation
* All rights reserved.
*
* Portions of this software were developed by Landon Fuller
* under sponsorship from the FreeBSD Foundation.
*
+ * Portions of this software were developed by Landon Fuller
+ * under sponsorship from the FreeBSD Foundation.
+ *
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@@ -51,6 +55,7 @@
*/
struct bhndb_dw_alloc;
+struct bhndb_intr_handler;
struct bhndb_region;
struct bhndb_resources;
@@ -68,10 +73,26 @@ int bhndb_add_resource_region(
const struct bhndb_regwin *static_regwin);
int bhndb_find_resource_limits(
- struct bhndb_resources *br,
+ struct bhndb_resources *br, int type,
struct resource *r, rman_res_t *start,
rman_res_t *end);
+struct bhndb_intr_handler *bhndb_alloc_intr_handler(device_t owner,
+ struct resource *r,
+ struct bhndb_intr_isrc *isrc);
+void bhndb_free_intr_handler(
+ struct bhndb_intr_handler *ih);
+
+void bhndb_register_intr_handler(
+ struct bhndb_resources *br,
+ struct bhndb_intr_handler *ih);
+void bhndb_deregister_intr_handler(
+ struct bhndb_resources *br,
+ struct bhndb_intr_handler *ih);
+struct bhndb_intr_handler *bhndb_find_intr_handler(
+ struct bhndb_resources *br,
+ void *cookiep);
+
struct bhndb_region *bhndb_find_resource_region(
struct bhndb_resources *br,
bhnd_addr_t addr, bhnd_size_t size);
@@ -137,6 +158,19 @@ struct bhndb_region {
};
/**
+ * Attached interrupt handler state
+ */
+struct bhndb_intr_handler {
+ device_t ih_owner; /**< child device */
+ struct resource *ih_res; /**< child resource */
+ void *ih_cookiep; /**< hostb-assigned cookiep, or NULL if bus_setup_intr() incomplete. */
+ struct bhndb_intr_isrc *ih_isrc; /**< host interrupt source routing the child's interrupt */
+ bool ih_active; /**< handler has been registered via bhndb_register_intr_handler */
+
+ STAILQ_ENTRY(bhndb_intr_handler) ih_link;
+};
+
+/**
* BHNDB resource allocation state.
*/
struct bhndb_resources {
@@ -147,6 +181,7 @@ struct bhndb_resources {
struct rman ht_mem_rman; /**< host memory manager */
struct rman br_mem_rman; /**< bridged memory manager */
+ struct rman br_irq_rman; /**< bridged irq manager */
STAILQ_HEAD(, bhndb_region) bus_regions; /**< bus region descriptors */
@@ -155,6 +190,8 @@ struct bhndb_resources {
bitstr_t *dwa_freelist; /**< dynamic window free list */
bhndb_priority_t min_prio; /**< minimum resource priority required to
allocate a dynamic window */
+
+ STAILQ_HEAD(,bhndb_intr_handler) bus_intrs; /**< attached child interrupt handlers */
};
/**
diff --git a/sys/dev/bhnd/bhndb/bhndb_subr.c b/sys/dev/bhnd/bhndb/bhndb_subr.c
index 55ca48e51082..76629fabb814 100644
--- a/sys/dev/bhnd/bhndb/bhndb_subr.c
+++ b/sys/dev/bhnd/bhndb/bhndb_subr.c
@@ -270,10 +270,11 @@ bhndb_alloc_resources(device_t dev, device_t parent_dev,
bus_size_t last_window_size;
int rnid;
int error;
- bool free_ht_mem, free_br_mem;
+ bool free_ht_mem, free_br_mem, free_br_irq;
free_ht_mem = false;
free_br_mem = false;
+ free_br_irq = false;
r = malloc(sizeof(*r), M_BHND, M_NOWAIT|M_ZERO);
if (r == NULL)
@@ -285,6 +286,7 @@ bhndb_alloc_resources(device_t dev, device_t parent_dev,
r->res = NULL;
r->min_prio = BHNDB_PRIORITY_NONE;
STAILQ_INIT(&r->bus_regions);
+ STAILQ_INIT(&r->bus_intrs);
/* Initialize host address space resource manager. */
r->ht_mem_rman.rm_start = 0;
@@ -316,6 +318,25 @@ bhndb_alloc_resources(device_t dev, device_t parent_dev,
goto failed;
}
+
+ /* Initialize resource manager for the bridged interrupt controller. */
+ r->br_irq_rman.rm_start = 0;
+ r->br_irq_rman.rm_end = RM_MAX_END;
+ r->br_irq_rman.rm_type = RMAN_ARRAY;
+ r->br_irq_rman.rm_descr = "BHNDB bridged interrupts";
+
+ if ((error = rman_init(&r->br_irq_rman))) {
+ device_printf(r->dev, "could not initialize br_irq_rman\n");
+ goto failed;
+ }
+ free_br_irq = true;
+
+ error = rman_manage_region(&r->br_irq_rman, 0, RM_MAX_END);
+ if (error) {
+ device_printf(r->dev, "could not configure br_irq_rman\n");
+ goto failed;
+ }
+
/* Fetch the dynamic regwin count and verify that it does not exceed
* what is representable via our freelist bitstring. */
r->dwa_count = bhndb_regwin_count(cfg->register_windows,
@@ -455,6 +476,9 @@ failed:
if (free_br_mem)
rman_fini(&r->br_mem_rman);
+ if (free_br_irq)
+ rman_fini(&r->br_irq_rman);
+
if (r->dw_alloc != NULL)
free(r->dw_alloc, M_BHND);
@@ -477,9 +501,14 @@ failed:
void
bhndb_free_resources(struct bhndb_resources *br)
{
- struct bhndb_region *region, *r_next;
- struct bhndb_dw_alloc *dwa;
- struct bhndb_dw_rentry *dwr, *dwr_next;
+ struct bhndb_region *region, *r_next;
+ struct bhndb_dw_alloc *dwa;
+ struct bhndb_dw_rentry *dwr, *dwr_next;
+ struct bhndb_intr_handler *ih;
+ bool leaked_regions, leaked_intrs;
+
+ leaked_regions = false;
+ leaked_intrs = false;
/* No window regions may still be held */
if (!bhndb_dw_all_free(br)) {
@@ -492,9 +521,21 @@ bhndb_free_resources(struct bhndb_resources *br)
device_printf(br->dev,
"leaked dynamic register window %d\n", dwa->rnid);
+ leaked_regions = true;
}
}
+ /* There should be no interrupt handlers still registered */
+ STAILQ_FOREACH(ih, &br->bus_intrs, ih_link) {
+ device_printf(br->dev, "interrupt handler leaked %p\n",
+ ih->ih_cookiep);
+ }
+
+ if (leaked_intrs || leaked_regions) {
+ panic("leaked%s%s", leaked_intrs ? " active interrupts" : "",
+ leaked_regions ? " active register windows" : "");
+ }
+
/* Release host resources allocated through our parent. */
if (br->res != NULL)
bhndb_release_host_resources(br->res);
@@ -518,6 +559,7 @@ bhndb_free_resources(struct bhndb_resources *br)
/* Release our resource managers */
rman_fini(&br->ht_mem_rman);
rman_fini(&br->br_mem_rman);
+ rman_fini(&br->br_irq_rman);
free(br->dw_alloc, M_BHND);
free(br->dwa_freelist, M_BHND);
@@ -667,6 +709,222 @@ bhndb_find_hostb_core(struct bhnd_core_info *cores, u_int ncores,
}
/**
+ * Allocate a host interrupt source and its backing SYS_RES_IRQ host resource.
+ *
+ * @param owner The device to be used to allocate a SYS_RES_IRQ
+ * resource with @p rid.
+ * @param rid The resource ID of the IRQ to be allocated.
+ * @param start The start value to be passed to bus_alloc_resource().
+ * @param end The end value to be passed to bus_alloc_resource().
+ * @param count The count to be passed to bus_alloc_resource().
+ * @param flags The flags to be passed to bus_alloc_resource().
+ *
+ * @retval non-NULL success
+ * @retval NULL if allocation fails.
+ */
+struct bhndb_intr_isrc *
+bhndb_alloc_intr_isrc(device_t owner, int rid, rman_res_t start, rman_res_t end,
+ rman_res_t count, u_int flags)
+{
+ struct bhndb_intr_isrc *isrc;
+
+ isrc = malloc(sizeof(*isrc), M_BHND, M_NOWAIT);
+ if (isrc == NULL)
+ return (NULL);
+
+ isrc->is_owner = owner;
+ isrc->is_rid = rid;
+ isrc->is_res = bus_alloc_resource(owner, SYS_RES_IRQ, &isrc->is_rid,
+ start, end, count, flags);
+ if (isrc->is_res == NULL) {
+ free(isrc, M_BHND);
+ return (NULL);
+ }
+
+ return (isrc);
+}
+
+/**
+ * Free a host interrupt source and its backing host resource.
+ *
+ * @param isrc The interrupt source to be freed.
+ */
+void
+bhndb_free_intr_isrc(struct bhndb_intr_isrc *isrc)
+{
+ bus_release_resource(isrc->is_owner, SYS_RES_IRQ, isrc->is_rid,
+ isrc->is_res);
+ free(isrc, M_BHND);
+}
+
+/**
+ * Allocate and initialize a new interrupt handler entry.
+ *
+ * @param owner The child device that owns this entry.
+ * @param r The child's interrupt resource.
+ * @param isrc The isrc mapped for this entry.
+ *
+ * @retval non-NULL success
+ * @retval NULL if allocation fails.
+ */
+struct bhndb_intr_handler *
+bhndb_alloc_intr_handler(device_t owner, struct resource *r,
+ struct bhndb_intr_isrc *isrc)
+{
+ struct bhndb_intr_handler *ih;
+
+ ih = malloc(sizeof(*ih), M_BHND, M_NOWAIT | M_ZERO);
+ ih->ih_owner = owner;
+ ih->ih_res = r;
+ ih->ih_isrc = isrc;
+ ih->ih_cookiep = NULL;
+ ih->ih_active = false;
+
+ return (ih);
+}
+
+/**
+ * Free an interrupt handler entry.
+ *
+ * @param br The resource state owning @p ih.
+ * @param ih The interrupt handler entry to be removed.
+ */
+void
+bhndb_free_intr_handler(struct bhndb_intr_handler *ih)
+{
+ KASSERT(!ih->ih_active, ("free of active interrupt handler %p",
+ ih->ih_cookiep));
+
+ free(ih, M_BHND);
+}
+
+/**
+ * Add an active interrupt handler to the given resource state.
+ *
+ * @param br The resource state to be modified.
+ * @param ih The interrupt handler entry to be added.
+ */
+void
+bhndb_register_intr_handler(struct bhndb_resources *br,
+ struct bhndb_intr_handler *ih)
+{
+ KASSERT(!ih->ih_active, ("duplicate registration of interrupt "
+ "handler %p", ih->ih_cookiep));
+ KASSERT(ih->ih_cookiep != NULL, ("missing cookiep"));
+
+ ih->ih_active = true;
+ STAILQ_INSERT_HEAD(&br->bus_intrs, ih, ih_link);
+}
+
+/**
+ * Remove an interrupt handler from the given resource state.
+ *
+ * @param br The resource state containing @p ih.
+ * @param ih The interrupt handler entry to be removed.
+ */
+void
+bhndb_deregister_intr_handler(struct bhndb_resources *br,
+ struct bhndb_intr_handler *ih)
+{
+ KASSERT(!ih->ih_active, ("duplicate deregistration of interrupt "
+ "handler %p", ih->ih_cookiep));
+
+ KASSERT(bhndb_find_intr_handler(br, ih) == ih,
+ ("unknown interrupt handler %p", ih));
+
+ STAILQ_REMOVE(&br->bus_intrs, ih, bhndb_intr_handler, ih_link);
+ ih->ih_active = false;
+}
+
+/**
+ * Return the interrupt handler entry corresponding to @p cookiep, or NULL
+ * if no entry is found.
+ *
+ * @param br The resource state to search for the given @p cookiep.
+ * @param cookiep The interrupt handler's bus-assigned cookiep value.
+ */
+struct bhndb_intr_handler *
+bhndb_find_intr_handler(struct bhndb_resources *br, void *cookiep)
+{
+ struct bhndb_intr_handler *ih;
+
+ STAILQ_FOREACH(ih, &br->bus_intrs, ih_link) {
+ if (ih == cookiep)
+ return (ih);
+ }
+
+ /* Not found */
+ return (NULL);
+}
+
+/**
+ * Find the maximum start and end limits of the bridged resource @p r.
+ *
+ * If the resource is not currently mapped by the bridge, ENOENT will be
+ * returned.
+ *
+ * @param br The resource state to search.
+ * @param type The resource type (see SYS_RES_*).
+ * @param r The resource to search for in @p br.
+ * @param[out] start On success, the minimum supported start address.
+ * @param[out] end On success, the maximum supported end address.
+ *
+ * @retval 0 success
+ * @retval ENOENT no active mapping found for @p r of @p type
+ */
+int
+bhndb_find_resource_limits(struct bhndb_resources *br, int type,
+ struct resource *r, rman_res_t *start, rman_res_t *end)
+{
+ struct bhndb_dw_alloc *dynamic;
+ struct bhndb_region *sregion;
+ struct bhndb_intr_handler *ih;
+
+ switch (type) {
+ case SYS_RES_IRQ:
+ /* Is this one of ours? */
+ STAILQ_FOREACH(ih, &br->bus_intrs, ih_link) {
+ if (ih->ih_res == r)
+ continue;
+
+ /* We don't support adjusting IRQ resource limits */
+ *start = rman_get_start(r);
+ *end = rman_get_end(r);
+ return (0);
+ }
+
+ /* Not found */
+ return (ENOENT);
+
+ case SYS_RES_MEMORY: {
+ /* Check for an enclosing dynamic register window */
+ if ((dynamic = bhndb_dw_find_resource(br, r))) {
+ *start = dynamic->target;
+ *end = dynamic->target + dynamic->win->win_size - 1;
+ return (0);
+ }
+
+ /* Check for a static region */
+ sregion = bhndb_find_resource_region(br, rman_get_start(r),
+ rman_get_size(r));
+ if (sregion != NULL && sregion->static_regwin != NULL) {
+ *start = sregion->addr;
+ *end = sregion->addr + sregion->size - 1;
+
+ return (0);
+ }
+
+ /* Not found */
+ return (ENOENT);
+ }
+
+ default:
+ device_printf(br->dev, "unknown resource type: %d\n", type);
+ return (ENOENT);
+ }
+}
+
+/**
* Add a bus region entry to @p r for the given base @p addr and @p size.
*
* @param br The resource state to which the bus region entry will be added.
@@ -706,49 +964,6 @@ bhndb_add_resource_region(struct bhndb_resources *br, bhnd_addr_t addr,
/**
- * Find the maximum start and end limits of the register window mapping
- * resource @p r.
- *
- * If the memory range is not mapped by an existing dynamic or static register
- * window, ENOENT will be returned.
- *
- * @param br The resource state to search.
- * @param r The resource to search for in @p br.
- * @param addr The requested starting address.
- * @param size The requested size.
- *
- * @retval bhndb_region A region that fully contains the requested range.
- * @retval NULL If no mapping region can be found.
- */
-int
-bhndb_find_resource_limits(struct bhndb_resources *br, struct resource *r,
- rman_res_t *start, rman_res_t *end)
-{
- struct bhndb_dw_alloc *dynamic;
- struct bhndb_region *sregion;
-
- /* Check for an enclosing dynamic register window */
- if ((dynamic = bhndb_dw_find_resource(br, r))) {
- *start = dynamic->target;
- *end = dynamic->target + dynamic->win->win_size - 1;
- return (0);
- }
-
- /* Check for a static region */
- sregion = bhndb_find_resource_region(br, rman_get_start(r),
- rman_get_size(r));
- if (sregion != NULL && sregion->static_regwin != NULL) {
- *start = sregion->addr;
- *end = sregion->addr + sregion->size - 1;
-
- return (0);
- }
-
- /* Not found */
- return (ENOENT);
-}
-
-/**
* Find the bus region that maps @p size bytes at @p addr.
*
* @param br The resource state to search.
diff --git a/sys/dev/bhnd/bhndb/bhndbvar.h b/sys/dev/bhnd/bhndb/bhndbvar.h
index b0a6a4be16d1..7f9eafabb624 100644
--- a/sys/dev/bhnd/bhndb/bhndbvar.h
+++ b/sys/dev/bhnd/bhndb/bhndbvar.h
@@ -55,6 +55,8 @@
DECLARE_CLASS(bhndb_driver);
+/* forward declarations */
+struct bhndb_intr_isrc;
struct bhndb_resources;
struct bhndb_host_resources;
@@ -82,6 +84,12 @@ int bhndb_find_hostb_core(
bhnd_devclass_t bridge_devclass,
struct bhnd_core_info *core);
+struct bhndb_intr_isrc *bhndb_alloc_intr_isrc(device_t owner, int rid,
+ rman_res_t start, rman_res_t end,
+ rman_res_t count, u_int flags);
+void bhndb_free_intr_isrc(
+ struct bhndb_intr_isrc *isrc);
+
int bhndb_alloc_host_resources(device_t dev,
const struct bhndb_hwcfg *hwcfg,
struct bhndb_host_resources **resources);
@@ -137,6 +145,15 @@ struct bhndb_devinfo {
};
/**
+ * Host interrupt source to which bridged interrupts may be routed.
+ */
+struct bhndb_intr_isrc {
+ device_t is_owner; /**< host device (e.g. the pci device). */
+ struct resource *is_res; /**< irq resource */
+ int is_rid; /**< irq resource ID */
+};
+
+/**
* Host resources allocated for a bridge hardware configuration.
*/
struct bhndb_host_resources {
@@ -162,6 +179,7 @@ struct bhndb_softc {
struct mtx sc_mtx; /**< resource lock. */
struct bhndb_resources *bus_res; /**< bus resource state */
+ STAILQ_HEAD(,bhndb_intr_handler) bus_intrs; /**< attached child interrupt handlers */
};
#endif /* _BHND_BHNDBVAR_H_ */
diff --git a/sys/dev/bhnd/bhndvar.h b/sys/dev/bhnd/bhndvar.h
index 6bbbb796ebba..777a96b46167 100644
--- a/sys/dev/bhnd/bhndvar.h
+++ b/sys/dev/bhnd/bhndvar.h
@@ -86,6 +86,12 @@ int bhnd_generic_suspend_child(device_t dev,
int bhnd_generic_resume_child(device_t dev,
device_t child);
+int bhnd_generic_setup_intr(device_t dev,
+ device_t child, struct resource *irq,
+ int flags, driver_filter_t *filter,
+ driver_intr_t *intr, void *arg,
+ void **cookiep);
+
int bhnd_generic_get_nvram_var(device_t dev,
device_t child, const char *name,
void *buf, size_t *size,
diff --git a/sys/dev/bhnd/cores/chipc/chipc.c b/sys/dev/bhnd/cores/chipc/chipc.c
index 31a5a2d3780c..cead2afbf33f 100644
--- a/sys/dev/bhnd/cores/chipc/chipc.c
+++ b/sys/dev/bhnd/cores/chipc/chipc.c
@@ -112,9 +112,6 @@ static struct bhnd_device_quirk chipc_quirks[] = {
BHND_DEVICE_QUIRK_END
};
-// FIXME: IRQ shouldn't be hard-coded
-#define CHIPC_MIPS_IRQ 2
-
static int chipc_add_children(struct chipc_softc *sc);
static bhnd_nvram_src chipc_find_nvram_src(struct chipc_softc *sc,
@@ -274,10 +271,13 @@ chipc_add_children(struct chipc_softc *sc)
}
/* Both OTP and external SPROM are mapped at CHIPC_SPROM_OTP */
- error = chipc_set_resource(sc, child, SYS_RES_MEMORY, 0,
- CHIPC_SPROM_OTP, CHIPC_SPROM_OTP_SIZE, 0, 0);
- if (error)
+ error = chipc_set_mem_resource(sc, child, 0, CHIPC_SPROM_OTP,
+ CHIPC_SPROM_OTP_SIZE, 0, 0);
+ if (error) {
+ device_printf(sc->dev, "failed to set OTP memory "
+ "resource: %d\n", error);
return (error);
+ }
}
/*
@@ -300,6 +300,11 @@ chipc_add_children(struct chipc_softc *sc)
/* UARTs */
for (u_int i = 0; i < min(sc->caps.num_uarts, CHIPC_UART_MAX); i++) {
+ int irq_rid, mem_rid;
+
+ irq_rid = 0;
+ mem_rid = 0;
+
child = BUS_ADD_CHILD(sc->dev, 0, "uart", -1);
if (child == NULL) {
device_printf(sc->dev, "failed to add uart%u\n", i);
@@ -307,24 +312,28 @@ chipc_add_children(struct chipc_softc *sc)
}
/* Shared IRQ */
- error = bus_set_resource(child, SYS_RES_IRQ, 0, CHIPC_MIPS_IRQ,
- 1);
+ error = chipc_set_irq_resource(sc, child, irq_rid, 0);
if (error) {
device_printf(sc->dev, "failed to set uart%u irq %u\n",
- i, CHIPC_MIPS_IRQ);
+ i, 0);
return (error);
}
/* UART registers are mapped sequentially */
- error = chipc_set_resource(sc, child, SYS_RES_MEMORY, 0,
+ error = chipc_set_mem_resource(sc, child, mem_rid,
CHIPC_UART(i), CHIPC_UART_SIZE, 0, 0);
- if (error)
+ if (error) {
+ device_printf(sc->dev, "failed to set uart%u memory "
+ "resource: %d\n", i, error);
return (error);
+ }
}
/* Flash */
flash_bus = chipc_flash_bus_name(sc->caps.flash_type);
if (flash_bus != NULL) {
+ int rid;
+
child = BUS_ADD_CHILD(sc->dev, 0, flash_bus, -1);
if (child == NULL) {
device_printf(sc->dev, "failed to add %s device\n",
@@ -333,16 +342,24 @@ chipc_add_children(struct chipc_softc *sc)
}
/* flash memory mapping */
- error = chipc_set_resource(sc, child, SYS_RES_MEMORY, 0,
- 0, RM_MAX_END, 1, 1);
- if (error)
+ rid = 0;
+ error = chipc_set_mem_resource(sc, child, rid, 0, RM_MAX_END, 1,
+ 1);
+ if (error) {
+ device_printf(sc->dev, "failed to set flash memory "
+ "resource %d: %d\n", rid, error);
return (error);
+ }
/* flashctrl registers */
- error = chipc_set_resource(sc, child, SYS_RES_MEMORY, 1,
+ rid++;
+ error = chipc_set_mem_resource(sc, child, rid,
CHIPC_SFLASH_BASE, CHIPC_SFLASH_SIZE, 0, 0);
- if (error)
+ if (error) {
+ device_printf(sc->dev, "failed to set flash memory "
+ "resource %d: %d\n", rid, error);
return (error);
+ }
}
return (0);
@@ -592,6 +609,7 @@ chipc_add_child(device_t dev, u_int order, const char *name, int unit)
}
resource_list_init(&dinfo->resources);
+ dinfo->irq_mapped = false;
device_set_ivars(child, dinfo);
return (child);
@@ -603,7 +621,15 @@ chipc_child_deleted(device_t dev, device_t child)
struct chipc_devinfo *dinfo = device_get_ivars(child);
if (dinfo != NULL) {
+ /* Free the child's resource list */
resource_list_free(&dinfo->resources);
+
+ /* Unmap the child's IRQ */
+ if (dinfo->irq_mapped) {
+ bhnd_unmap_intr(dev, dinfo->irq);
+ dinfo->irq_mapped = false;
+ }
+
free(dinfo, M_BHND);
}
@@ -731,8 +757,7 @@ chipc_get_rman(struct chipc_softc *sc, int type)
return (&sc->mem_rman);
case SYS_RES_IRQ:
- /* IRQs can be used with RF_SHAREABLE, so we don't perform
- * any local proxying of resource requests. */
+ /* We delegate IRQ resource management to the parent bus */
return (NULL);
default:
diff --git a/sys/dev/bhnd/cores/chipc/chipc_private.h b/sys/dev/bhnd/cores/chipc/chipc_private.h
index 06d4e2b1633e..a3820ce56dce 100644
--- a/sys/dev/bhnd/cores/chipc/chipc_private.h
+++ b/sys/dev/bhnd/cores/chipc/chipc_private.h
@@ -1,7 +1,11 @@
/*-
* Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org>
+ * Copyright (c) 2017 The FreeBSD Foundation
* All rights reserved.
*
+ * Portions of this software were developed by Landon Fuller
+ * under sponsorship from the FreeBSD Foundation.
+ *
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@@ -55,10 +59,11 @@ int chipc_init_child_resource(struct resource *r,
struct resource *parent,
bhnd_size_t offset, bhnd_size_t size);
-int chipc_set_resource(struct chipc_softc *sc,
- device_t child, int type, int rid,
- rman_res_t start, rman_res_t count, u_int port,
- u_int region);
+int chipc_set_irq_resource(struct chipc_softc *sc,
+ device_t child, int rid, u_int intr);
+int chipc_set_mem_resource(struct chipc_softc *sc,
+ device_t child, int rid, rman_res_t start,
+ rman_res_t count, u_int port, u_int region);
struct chipc_region *chipc_alloc_region(struct chipc_softc *sc,
bhnd_port_type type, u_int port,
diff --git a/sys/dev/bhnd/cores/chipc/chipc_subr.c b/sys/dev/bhnd/cores/chipc/chipc_subr.c
index e7a8b0df504e..156feb16dd07 100644
--- a/sys/dev/bhnd/cores/chipc/chipc_subr.c
+++ b/sys/dev/bhnd/cores/chipc/chipc_subr.c
@@ -162,22 +162,69 @@ chipc_init_child_resource(struct resource *r,
}
/**
- * Associate a resource with a given resource ID, relative to the given
- * port and region.
- *
- * This function behaves identically to bus_set_resource() for all resource
- * types other than SYS_RES_MEMORY.
+ * Map an interrupt line to an IRQ, and then register a corresponding SYS_RES_IRQ
+ * with @p child's resource list.
+ *
+ * @param sc chipc driver state.
+ * @param child The device to set the resource on.
+ * @param rid The resource ID.
+ * @param intr The interrupt line to be mapped.
+ * @param count The length of the resource.
+ * @param port The mapping port number (ignored if not SYS_RES_MEMORY).
+ * @param region The mapping region number (ignored if not SYS_RES_MEMORY).
+ */
+int
+chipc_set_irq_resource(struct chipc_softc *sc, device_t child, int rid,
+ u_int intr)
+{
+ struct chipc_devinfo *dinfo;
+ int error;
+
+ KASSERT(device_get_parent(child) == sc->dev, ("not a direct child"));
+ dinfo = device_get_ivars(child);
+
+ /* We currently only support a single IRQ mapping */
+ if (dinfo->irq_mapped) {
+ device_printf(sc->dev, "irq already mapped for child\n");
+ return (ENOMEM);
+ }
+
+ /* Map the IRQ */
+ if ((error = bhnd_map_intr(sc->dev, intr, &dinfo->irq))) {
+ device_printf(sc->dev, "failed to map intr %u: %d\n", intr,
+ error);
+ return (error);
+ }
+
+ dinfo->irq_mapped = true;
+
+ /* Add to child's resource list */
+ error = bus_set_resource(child, SYS_RES_IRQ, rid, dinfo->irq, 1);
+ if (error) {
+ device_printf(sc->dev, "failed to set child irq resource %d to "
+ "%ju: %d\n", rid, dinfo->irq, error);
+
+ bhnd_unmap_intr(sc->dev, dinfo->irq);
+ return (error);
+ }
+
+ return (0);
+}
+
+
+/**
+ * Add a SYS_RES_MEMORY resource with a given resource ID, relative to the
+ * given port and region, to @p child's resource list.
*
- * For SYS_RES_MEMORY resources, the specified @p region's address and size
- * will be fetched from the bhnd(4) bus, and bus_set_resource() will be called
- * with @p start added the region's actual base address.
+ * The specified @p region's address and size will be fetched from the bhnd(4)
+ * bus, and bus_set_resource() will be called with @p start added the region's
+ * actual base address.
*
* To use the default region values for @p start and @p count, specify
* a @p start value of 0ul, and an end value of RMAN_MAX_END
*
* @param sc chipc driver state.
* @param child The device to set the resource on.
- * @param type The resource type.
* @param rid The resource ID.
* @param start The resource start address (if SYS_RES_MEMORY, this is
* relative to @p region's base address).
@@ -186,7 +233,7 @@ chipc_init_child_resource(struct resource *r,
* @param region The mapping region number (ignored if not SYS_RES_MEMORY).
*/
int
-chipc_set_resource(struct chipc_softc *sc, device_t child, int type, int rid,
+chipc_set_mem_resource(struct chipc_softc *sc, device_t child, int rid,
rman_res_t start, rman_res_t count, u_int port, u_int region)
{
bhnd_addr_t region_addr;
@@ -194,9 +241,7 @@ chipc_set_resource(struct chipc_softc *sc, device_t child, int type, int rid,
bool isdefault;
int error;
- if (type != SYS_RES_MEMORY)
- return (bus_set_resource(child, type, rid, start, count));
-
+ KASSERT(device_get_parent(child) == sc->dev, ("not a direct child"));
isdefault = RMAN_IS_DEFAULT_RANGE(start, count);
/* Fetch region address and size */
@@ -224,7 +269,8 @@ chipc_set_resource(struct chipc_softc *sc, device_t child, int type, int rid,
return (ERANGE);
}
- return (bus_set_resource(child, type, rid, region_addr + start, count));
+ return (bus_set_resource(child, SYS_RES_MEMORY, rid,
+ region_addr + start, count));
}
diff --git a/sys/dev/bhnd/cores/chipc/chipcvar.h b/sys/dev/bhnd/cores/chipc/chipcvar.h
index b5f2a3700651..ce1836d83dd9 100644
--- a/sys/dev/bhnd/cores/chipc/chipcvar.h
+++ b/sys/dev/bhnd/cores/chipc/chipcvar.h
@@ -1,7 +1,11 @@
/*-
- * Copyright (c) 2015 Landon Fuller <landon@landonf.org>
+ * Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org>
+ * Copyright (c) 2017 The FreeBSD Foundation
* All rights reserved.
*
+ * Portions of this software were developed by Landon Fuller
+ * under sponsorship from the FreeBSD Foundation.
+ *
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@@ -138,6 +142,8 @@ enum {
*/
struct chipc_devinfo {
struct resource_list resources; /**< child resources */
+ rman_res_t irq; /**< child IRQ, if mapped */
+ bool irq_mapped; /**< true if IRQ mapped, false otherwise */
};
/**
diff --git a/sys/dev/bhnd/cores/pci/bhnd_pci_hostb.c b/sys/dev/bhnd/cores/pci/bhnd_pci_hostb.c
index 5d5657a1cf26..565ac51074ff 100644
--- a/sys/dev/bhnd/cores/pci/bhnd_pci_hostb.c
+++ b/sys/dev/bhnd/cores/pci/bhnd_pci_hostb.c
@@ -139,13 +139,13 @@ static const struct bhnd_device_quirk bhnd_pcie_quirks[] = {
BHND_PCIE_QUIRK_BFL2_PCIEWAR_EN },
/* Apple BCM4322 boards that require 700mV SerDes TX drive strength. */
- {{ BHND_CHIP_ID(BCM4322),
+ {{ BHND_MATCH_CHIP_ID(BCM4322),
BHND_MATCH_BOARD(PCI_VENDOR_APPLE, BCM94322X9), },
BHND_PCIE_QUIRK_SERDES_TXDRV_700MV },
/* Apple BCM4331 board-specific quirks */
#define BHND_A4331_QUIRK(_board, ...) \
- {{ BHND_CHIP_ID(BCM4331), \
+ {{ BHND_MATCH_CHIP_ID(BCM4331), \
BHND_MATCH_BOARD(PCI_VENDOR_APPLE, _board) }, __VA_ARGS__ }
BHND_A4331_QUIRK(BCM94331X19, BHND_PCIE_QUIRK_SERDES_TXDRV_MAX |
diff --git a/sys/dev/bhnd/cores/usb/bhnd_usb.c b/sys/dev/bhnd/cores/usb/bhnd_usb.c
index 0036b99ce917..c6fd4cc58c36 100644
--- a/sys/dev/bhnd/cores/usb/bhnd_usb.c
+++ b/sys/dev/bhnd/cores/usb/bhnd_usb.c
@@ -137,18 +137,6 @@ bhnd_usb_attach(device_t dev)
panic("%s: sc->mem_rman", __func__);
}
- sc->irq_rman.rm_start = sc->sc_irqn;
- sc->irq_rman.rm_end = sc->sc_irqn;
- sc->irq_rman.rm_type = RMAN_ARRAY;
- sc->irq_rman.rm_descr = "BHND USB core IRQ";
- /*
- * BHND USB share same IRQ between OHCI and EHCI
- */
- if (rman_init(&sc->irq_rman) != 0 ||
- rman_manage_region(&sc->irq_rman, sc->irq_rman.rm_start,
- sc->irq_rman.rm_end) != 0)
- panic("%s: failed to set up IRQ rman", __func__);
-
/* TODO: macros for registers */
bus_write_4(sc->sc_mem, 0x200, 0x7ff);
DELAY(100);
@@ -254,17 +242,19 @@ bhnd_usb_alloc_resource(device_t bus, device_t child, int type, int *rid,
struct resource *rv;
struct resource_list *rl;
struct resource_list_entry *rle;
- int isdefault, needactivate;
+ int passthrough, isdefault, needactivate;
struct bhnd_usb_softc *sc = device_get_softc(bus);
isdefault = RMAN_IS_DEFAULT_RANGE(start,end);
+ passthrough = (device_get_parent(child) != bus);
needactivate = flags & RF_ACTIVE;
- rl = BUS_GET_RESOURCE_LIST(bus, child);
rle = NULL;
- if (isdefault) {
+ if (!passthrough && isdefault) {
BHND_INFO_DEV(bus, "trying allocate def %d - %d for %s", type,
*rid, device_get_nameunit(child) );
+
+ rl = BUS_GET_RESOURCE_LIST(bus, child);
rle = resource_list_find(rl, type, *rid);
if (rle == NULL)
return (NULL);
@@ -303,32 +293,11 @@ bhnd_usb_alloc_resource(device_t bus, device_t child, int type, int *rid,
return (rv);
}
- if (type == SYS_RES_IRQ) {
-
- rv = rman_reserve_resource(&sc->irq_rman, start, end, count,
- flags, child);
- if (rv == NULL) {
- BHND_ERROR_DEV(bus, "could not reserve resource");
- return (0);
- }
-
- rman_set_rid(rv, *rid);
-
- if (needactivate &&
- bus_activate_resource(child, type, *rid, rv)) {
- BHND_ERROR_DEV(bus, "could not activate resource");
- rman_release_resource(rv);
- return (0);
- }
-
- return (rv);
- }
-
/*
* Pass the request to the parent.
*/
- return (resource_list_alloc(rl, bus, child, type, rid,
- start, end, count, flags));
+ return (bus_generic_rl_alloc_resource(bus, child, type, rid, start, end,
+ count, flags));
}
static struct resource_list *
@@ -345,17 +314,38 @@ static int
bhnd_usb_release_resource(device_t dev, device_t child, int type,
int rid, struct resource *r)
{
- struct resource_list *rl;
+ struct bhnd_usb_softc *sc;
struct resource_list_entry *rle;
+ bool passthrough;
+ int error;
- rl = bhnd_usb_get_reslist(dev, child);
- if (rl == NULL)
- return (EINVAL);
- rle = resource_list_find(rl, type, rid);
- if (rle == NULL)
- return (EINVAL);
- rman_release_resource(r);
- rle->res = NULL;
+ sc = device_get_softc(dev);
+ passthrough = (device_get_parent(child) != dev);
+
+ /* Delegate to our parent device's bus if the requested resource type
+ * isn't handled locally. */
+ if (type != SYS_RES_MEMORY) {
+ return (bus_generic_rl_release_resource(dev, child, type, rid,
+ r));
+ }
+
+ /* Deactivate resources */
+ if (rman_get_flags(r) & RF_ACTIVE) {
+ error = BUS_DEACTIVATE_RESOURCE(dev, child, type, rid, r);
+ if (error)
+ return (error);
+ }
+
+ if ((error = rman_release_resource(r)))
+ return (error);
+
+ if (!passthrough) {
+ /* Clean resource list entry */
+ rle = resource_list_find(BUS_GET_RESOURCE_LIST(dev, child),
+ type, rid);
+ if (rle != NULL)
+ rle->res = NULL;
+ }
return (0);
}
@@ -400,56 +390,84 @@ bhnd_usb_add_child(device_t dev, u_int order, const char *name, int unit)
struct bhnd_usb_softc *sc;
struct bhnd_usb_devinfo *sdi;
device_t child;
+ int error;
sc = device_get_softc(dev);
- child = device_add_child_ordered(dev, order, name, unit);
-
- if (child == NULL)
- return (NULL);
sdi = malloc(sizeof(struct bhnd_usb_devinfo), M_DEVBUF, M_NOWAIT|M_ZERO);
if (sdi == NULL)
return (NULL);
+ resource_list_init(&sdi->sdi_rl);
+ sdi->sdi_irq_mapped = false;
+
if (strncmp(name, "ohci", 4) == 0)
{
sdi->sdi_maddr = sc->sc_maddr + 0x000;
sdi->sdi_msize = 0x200;
- sdi->sdi_irq = sc->sc_irqn;
- BHND_INFO_DEV(dev, "ohci: irq=%d maddr=0x%jx", sdi->sdi_irq,
- sdi->sdi_maddr);
}
else if (strncmp(name, "ehci", 4) == 0)
{
sdi->sdi_maddr = sc->sc_maddr + 0x000;
sdi->sdi_msize = 0x1000;
- sdi->sdi_irq = sc->sc_irqn;
- BHND_INFO_DEV(dev, "ehci: irq=%d maddr=0x%jx", sdi->sdi_irq,
- sdi->sdi_maddr);
}
else
{
panic("Unknown subdevice");
- /* Unknown subdevice */
- sdi->sdi_maddr = 1;
- sdi->sdi_msize = 1;
- sdi->sdi_irq = 1;
}
- resource_list_init(&sdi->sdi_rl);
+ /* Map the child's IRQ */
+ if ((error = bhnd_map_intr(dev, 0, &sdi->sdi_irq))) {
+ BHND_ERROR_DEV(dev, "could not map %s interrupt: %d", name,
+ error);
+ goto failed;
+ }
+ sdi->sdi_irq_mapped = true;
+
+ BHND_INFO_DEV(dev, "%s: irq=%ju maddr=0x%jx", name, sdi->sdi_irq,
+ sdi->sdi_maddr);
/*
- * Determine memory window on bus and irq if one is needed.
+ * Add memory window and irq to child's resource list.
*/
- resource_list_add(&sdi->sdi_rl, SYS_RES_MEMORY, 0,
- sdi->sdi_maddr, sdi->sdi_maddr + sdi->sdi_msize - 1, sdi->sdi_msize);
+ resource_list_add(&sdi->sdi_rl, SYS_RES_MEMORY, 0, sdi->sdi_maddr,
+ sdi->sdi_maddr + sdi->sdi_msize - 1, sdi->sdi_msize);
+
+ resource_list_add(&sdi->sdi_rl, SYS_RES_IRQ, 0, sdi->sdi_irq,
+ sdi->sdi_irq, 1);
- resource_list_add(&sdi->sdi_rl, SYS_RES_IRQ, 0,
- sdi->sdi_irq, sdi->sdi_irq, 1);
+ child = device_add_child_ordered(dev, order, name, unit);
+ if (child == NULL) {
+ BHND_ERROR_DEV(dev, "could not add %s", name);
+ goto failed;
+ }
device_set_ivars(child, sdi);
return (child);
+failed:
+ if (sdi->sdi_irq_mapped)
+ bhnd_unmap_intr(dev, sdi->sdi_irq);
+
+ resource_list_free(&sdi->sdi_rl);
+
+ free(sdi, M_DEVBUF);
+ return (NULL);
+}
+
+static void
+bhnd_usb_child_deleted(device_t dev, device_t child)
+{
+ struct bhnd_usb_devinfo *dinfo;
+
+ if ((dinfo = device_get_ivars(child)) == NULL)
+ return;
+
+ if (dinfo->sdi_irq_mapped)
+ bhnd_unmap_intr(dev, dinfo->sdi_irq);
+
+ resource_list_free(&dinfo->sdi_rl);
+ free(dinfo, M_DEVBUF);
}
static device_method_t bhnd_usb_methods[] = {
@@ -459,6 +477,7 @@ static device_method_t bhnd_usb_methods[] = {
/* Bus interface */
DEVMETHOD(bus_add_child, bhnd_usb_add_child),
+ DEVMETHOD(bus_child_deleted, bhnd_usb_child_deleted),
DEVMETHOD(bus_alloc_resource, bhnd_usb_alloc_resource),
DEVMETHOD(bus_get_resource_list, bhnd_usb_get_reslist),
DEVMETHOD(bus_print_child, bhnd_usb_print_child),
diff --git a/sys/dev/bhnd/cores/usb/bhnd_usbvar.h b/sys/dev/bhnd/cores/usb/bhnd_usbvar.h
index de078b5bb833..63ad0dcc1be0 100644
--- a/sys/dev/bhnd/cores/usb/bhnd_usbvar.h
+++ b/sys/dev/bhnd/cores/usb/bhnd_usbvar.h
@@ -50,7 +50,8 @@ struct bhnd_usb_softc {
struct bhnd_usb_devinfo {
struct resource_list sdi_rl;
uint8_t sdi_unit; /* core index on bus */
- uint8_t sdi_irq;
+ rman_res_t sdi_irq; /**< child IRQ, if mapped */
+ bool sdi_irq_mapped; /**< true if IRQ mapped, false otherwise */
char sdi_name[8];
rman_res_t sdi_maddr;
rman_res_t sdi_msize;
diff --git a/sys/dev/bhnd/siba/siba.c b/sys/dev/bhnd/siba/siba.c
index d7be266b4680..2266d9d49e04 100644
--- a/sys/dev/bhnd/siba/siba.c
+++ b/sys/dev/bhnd/siba/siba.c
@@ -1,7 +1,11 @@
/*-
- * Copyright (c) 2015 Landon Fuller <landon@landonf.org>
+ * Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org>
+ * Copyright (c) 2017 The FreeBSD Foundation
* All rights reserved.
*
+ * Portions of this software were developed by Landon Fuller
+ * under sponsorship from the FreeBSD Foundation.
+ *
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@@ -214,7 +218,7 @@ siba_write_ioctl(device_t dev, device_t child, uint16_t value, uint16_t mask)
/* Fetch CFG0 mapping */
dinfo = device_get_ivars(child);
- if ((r = dinfo->cfg[0]) == NULL)
+ if ((r = dinfo->cfg_res[0]) == NULL)
return (ENODEV);
/* Mask and set TMSTATELOW core flag bits */
@@ -266,7 +270,7 @@ siba_reset_hw(device_t dev, device_t child, uint16_t ioctl)
dinfo = device_get_ivars(child);
/* Can't suspend the core without access to the CFG0 registers */
- if ((r = dinfo->cfg[0]) == NULL)
+ if ((r = dinfo->cfg_res[0]) == NULL)
return (ENODEV);
/* We require exclusive control over BHND_IOCTL_CLK_EN and
@@ -338,7 +342,7 @@ siba_suspend_hw(device_t dev, device_t child)
pm = dinfo->pmu_info;
/* Can't suspend the core without access to the CFG0 registers */
- if ((r = dinfo->cfg[0]) == NULL)
+ if ((r = dinfo->cfg_res[0]) == NULL)
return (ENODEV);
/* Already in RESET? */
@@ -433,23 +437,26 @@ siba_read_config(device_t dev, device_t child, bus_size_t offset, void *value,
/* CFG0 registers must be available */
dinfo = device_get_ivars(child);
- if (dinfo->cfg[0] == NULL)
+ if (dinfo->cfg_res[0] == NULL)
return (ENODEV);
/* Offset must fall within CFG0 */
- r_size = rman_get_size(dinfo->cfg[0]->res);
+ r_size = rman_get_size(dinfo->cfg_res[0]->res);
if (r_size < offset || r_size - offset < width)
return (EFAULT);
switch (width) {
case 1:
- *((uint8_t *)value) = bhnd_bus_read_1(dinfo->cfg[0], offset);
+ *((uint8_t *)value) = bhnd_bus_read_1(dinfo->cfg_res[0],
+ offset);
return (0);
case 2:
- *((uint16_t *)value) = bhnd_bus_read_2(dinfo->cfg[0], offset);
+ *((uint16_t *)value) = bhnd_bus_read_2(dinfo->cfg_res[0],
+ offset);
return (0);
case 4:
- *((uint32_t *)value) = bhnd_bus_read_4(dinfo->cfg[0], offset);
+ *((uint32_t *)value) = bhnd_bus_read_4(dinfo->cfg_res[0],
+ offset);
return (0);
default:
return (EINVAL);
@@ -470,7 +477,7 @@ siba_write_config(device_t dev, device_t child, bus_size_t offset,
/* CFG0 registers must be available */
dinfo = device_get_ivars(child);
- if ((r = dinfo->cfg[0]) == NULL)
+ if ((r = dinfo->cfg_res[0]) == NULL)
return (ENODEV);
/* Offset must fall within CFG0 */
@@ -504,7 +511,7 @@ siba_get_port_count(device_t dev, device_t child, bhnd_port_type type)
type));
dinfo = device_get_ivars(child);
- return (siba_addrspace_port_count(dinfo->core_id.num_addrspace));
+ return (siba_port_count(&dinfo->core_id, type));
}
static u_int
@@ -519,11 +526,7 @@ siba_get_region_count(device_t dev, device_t child, bhnd_port_type type,
type, port));
dinfo = device_get_ivars(child);
- if (!siba_is_port_valid(dinfo->core_id.num_addrspace, type, port))
- return (0);
-
- return (siba_addrspace_region_count(dinfo->core_id.num_addrspace,
- port));
+ return (siba_port_region_count(&dinfo->core_id, type, port));
}
static int
@@ -532,6 +535,7 @@ siba_get_port_rid(device_t dev, device_t child, bhnd_port_type port_type,
{
struct siba_devinfo *dinfo;
struct siba_addrspace *addrspace;
+ struct siba_cfg_block *cfg;
/* delegate non-bus-attached devices to our parent */
if (device_get_parent(child) != dev)
@@ -539,11 +543,19 @@ siba_get_port_rid(device_t dev, device_t child, bhnd_port_type port_type,
port_type, port_num, region_num));
dinfo = device_get_ivars(child);
+
+ /* Look for a matching addrspace entry */
addrspace = siba_find_addrspace(dinfo, port_type, port_num, region_num);
- if (addrspace == NULL)
- return (-1);
+ if (addrspace != NULL)
+ return (addrspace->sa_rid);
- return (addrspace->sa_rid);
+ /* Try the config blocks */
+ cfg = siba_find_cfg_block(dinfo, port_type, port_num, region_num);
+ if (cfg != NULL)
+ return (cfg->cb_rid);
+
+ /* Not found */
+ return (-1);
}
static int
@@ -563,13 +575,25 @@ siba_decode_port_rid(device_t dev, device_t child, int type, int rid,
if (type != SYS_RES_MEMORY)
return (EINVAL);
- for (int i = 0; i < dinfo->core_id.num_addrspace; i++) {
+ /* Look for a matching addrspace entry */
+ for (u_int i = 0; i < dinfo->core_id.num_addrspace; i++) {
if (dinfo->addrspace[i].sa_rid != rid)
continue;
*port_type = BHND_PORT_DEVICE;
- *port_num = siba_addrspace_port(i);
- *region_num = siba_addrspace_region(i);
+ *port_num = siba_addrspace_device_port(i);
+ *region_num = siba_addrspace_device_region(i);
+ return (0);
+ }
+
+ /* Try the config blocks */
+ for (u_int i = 0; i < dinfo->core_id.num_cfg_blocks; i++) {
+ if (dinfo->cfg[i].cb_rid != rid)
+ continue;
+
+ *port_type = BHND_PORT_AGENT;
+ *port_num = siba_cfg_agent_port(i);
+ *region_num = siba_cfg_agent_region(i);
return (0);
}
@@ -583,6 +607,7 @@ siba_get_region_addr(device_t dev, device_t child, bhnd_port_type port_type,
{
struct siba_devinfo *dinfo;
struct siba_addrspace *addrspace;
+ struct siba_cfg_block *cfg;
/* delegate non-bus-attached devices to our parent */
if (device_get_parent(child) != dev) {
@@ -591,67 +616,72 @@ siba_get_region_addr(device_t dev, device_t child, bhnd_port_type port_type,
}
dinfo = device_get_ivars(child);
+
+ /* Look for a matching addrspace */
addrspace = siba_find_addrspace(dinfo, port_type, port_num, region_num);
- if (addrspace == NULL)
- return (ENOENT);
+ if (addrspace != NULL) {
+ *addr = addrspace->sa_base;
+ *size = addrspace->sa_size - addrspace->sa_bus_reserved;
+ return (0);
+ }
- *addr = addrspace->sa_base;
- *size = addrspace->sa_size - addrspace->sa_bus_reserved;
- return (0);
+ /* Look for a matching cfg block */
+ cfg = siba_find_cfg_block(dinfo, port_type, port_num, region_num);
+ if (cfg != NULL) {
+ *addr = cfg->cb_base;
+ *size = cfg->cb_size;
+ return (0);
+ }
+
+ /* Not found */
+ return (ENOENT);
}
/**
* Default siba(4) bus driver implementation of BHND_BUS_GET_INTR_COUNT().
- *
- * This implementation consults @p child's configuration block mapping,
- * returning SIBA_CORE_NUM_INTR if a valid CFG0 block is mapped.
*/
-int
+u_int
siba_get_intr_count(device_t dev, device_t child)
{
- struct siba_devinfo *dinfo;
+ struct siba_devinfo *dinfo;
/* delegate non-bus-attached devices to our parent */
if (device_get_parent(child) != dev)
return (BHND_BUS_GET_INTR_COUNT(device_get_parent(dev), child));
dinfo = device_get_ivars(child);
-
- /* We can get/set interrupt sbflags on any core with a valid cfg0
- * block; whether the core actually makes use of it is another matter
- * entirely */
- if (dinfo->cfg[0] == NULL)
+ if (!dinfo->intr_en) {
+ /* No interrupts */
return (0);
-
- return (SIBA_CORE_NUM_INTR);
+ } else {
+ /* One assigned interrupt */
+ return (1);
+ }
}
/**
- * Default siba(4) bus driver implementation of BHND_BUS_GET_CORE_IVEC().
- *
- * This implementation consults @p child's CFG0 register block,
- * returning the interrupt flag assigned to @p child.
+ * Default siba(4) bus driver implementation of BHND_BUS_GET_INTR_IVEC().
*/
int
-siba_get_core_ivec(device_t dev, device_t child, u_int intr, uint32_t *ivec)
+siba_get_intr_ivec(device_t dev, device_t child, u_int intr, u_int *ivec)
{
struct siba_devinfo *dinfo;
- uint32_t tpsflag;
/* delegate non-bus-attached devices to our parent */
if (device_get_parent(child) != dev)
- return (BHND_BUS_GET_CORE_IVEC(device_get_parent(dev), child,
+ return (BHND_BUS_GET_INTR_IVEC(device_get_parent(dev), child,
intr, ivec));
/* Must be a valid interrupt ID */
if (intr >= siba_get_intr_count(dev, child))
return (ENXIO);
- /* Fetch sbflag number */
+ KASSERT(intr == 0, ("invalid ivec %u", intr));
+
dinfo = device_get_ivars(child);
- tpsflag = bhnd_bus_read_4(dinfo->cfg[0], SIBA_CFG0_TPSFLAG);
- *ivec = SIBA_REG_GET(tpsflag, TPS_NUM0);
+ KASSERT(dinfo->intr_en, ("core does not have an interrupt assigned"));
+ *ivec = dinfo->intr.flag;
return (0);
}
@@ -715,6 +745,55 @@ siba_register_addrspaces(device_t dev, struct siba_devinfo *di,
return (0);
}
+
+/**
+ * Register all interrupt descriptors for @p dinfo. Must be called after
+ * configuration blocks have been mapped.
+ *
+ * @param dev The siba bus device.
+ * @param child The siba child device.
+ * @param dinfo The device info instance on which to register all interrupt
+ * descriptor entries.
+ * @param r A resource mapping the enumeration table block for @p di.
+ */
+static int
+siba_register_interrupts(device_t dev, device_t child,
+ struct siba_devinfo *dinfo, struct bhnd_resource *r)
+{
+ uint32_t tpsflag;
+ int error;
+
+ /* Is backplane interrupt distribution enabled for this core? */
+ tpsflag = bhnd_bus_read_4(r, SB0_REG_ABS(SIBA_CFG0_TPSFLAG));
+ if ((tpsflag & SIBA_TPS_F0EN0) == 0) {
+ dinfo->intr_en = false;
+ return (0);
+ }
+
+ /* Have one interrupt */
+ dinfo->intr_en = true;
+ dinfo->intr.flag = SIBA_REG_GET(tpsflag, TPS_NUM0);
+ dinfo->intr.mapped = false;
+ dinfo->intr.irq = 0;
+ dinfo->intr.rid = -1;
+
+ /* Map the interrupt */
+ error = BHND_BUS_MAP_INTR(dev, child, 0 /* single intr is always 0 */,
+ &dinfo->intr.irq);
+ if (error) {
+ device_printf(dev, "failed mapping interrupt line for core %u: "
+ "%d\n", dinfo->core_id.core_info.core_idx, error);
+ return (error);
+ }
+ dinfo->intr.mapped = true;
+
+ /* Update the resource list */
+ dinfo->intr.rid = resource_list_add_next(&dinfo->resources, SYS_RES_IRQ,
+ dinfo->intr.irq, dinfo->intr.irq, 1);
+
+ return (0);
+}
+
/**
* Map per-core configuration blocks for @p dinfo.
*
@@ -728,6 +807,7 @@ siba_map_cfg_resources(device_t dev, struct siba_devinfo *dinfo)
struct siba_addrspace *addrspace;
rman_res_t r_start, r_count, r_end;
uint8_t num_cfg;
+ int rid;
num_cfg = dinfo->core_id.num_cfg_blocks;
if (num_cfg > SIBA_MAX_CFG) {
@@ -747,22 +827,28 @@ siba_map_cfg_resources(device_t dev, struct siba_devinfo *dinfo)
* Map the per-core configuration blocks
*/
for (uint8_t i = 0; i < num_cfg; i++) {
- /* Determine the config block's address range; configuration
- * blocks are allocated starting at SIBA_CFG0_OFFSET,
- * growing downwards. */
- r_start = addrspace->sa_base + SIBA_CFG0_OFFSET;
- r_start -= i * SIBA_CFG_SIZE;
-
+ /* Add to child's resource list */
+ r_start = addrspace->sa_base + SIBA_CFG_OFFSET(i);
r_count = SIBA_CFG_SIZE;
r_end = r_start + r_count - 1;
- /* Allocate the config resource */
+ rid = resource_list_add_next(&dinfo->resources, SYS_RES_MEMORY,
+ r_start, r_end, r_count);
+
+ /* Initialize config block descriptor */
+ dinfo->cfg[i] = ((struct siba_cfg_block) {
+ .cb_base = r_start,
+ .cb_size = SIBA_CFG_SIZE,
+ .cb_rid = rid
+ });
+
+ /* Map the config resource for bus-level access */
dinfo->cfg_rid[i] = SIBA_CFG_RID(dinfo, i);
- dinfo->cfg[i] = BHND_BUS_ALLOC_RESOURCE(dev, dev,
+ dinfo->cfg_res[i] = BHND_BUS_ALLOC_RESOURCE(dev, dev,
SYS_RES_MEMORY, &dinfo->cfg_rid[i], r_start, r_end,
- r_count, RF_ACTIVE);
+ r_count, RF_ACTIVE|RF_SHAREABLE);
- if (dinfo->cfg[i] == NULL) {
+ if (dinfo->cfg_res[i] == NULL) {
device_printf(dev, "failed to allocate SIBA_CFG%hhu\n",
i);
return (ENXIO);
@@ -805,7 +891,7 @@ siba_child_deleted(device_t dev, device_t child)
/* Free siba device info */
if ((dinfo = device_get_ivars(child)) != NULL)
- siba_free_dinfo(dev, dinfo);
+ siba_free_dinfo(dev, child, dinfo);
device_set_ivars(child, NULL);
}
@@ -846,6 +932,7 @@ siba_add_children(device_t dev)
*/
for (u_int i = 0; i < chipid->ncores; i++) {
struct siba_devinfo *dinfo;
+ device_t child;
uint32_t idhigh, idlow;
rman_res_t r_count, r_end, r_start;
@@ -878,14 +965,16 @@ siba_add_children(device_t dev)
}
/* Add the child device */
- children[i] = BUS_ADD_CHILD(dev, 0, NULL, -1);
- if (children[i] == NULL) {
+ child = BUS_ADD_CHILD(dev, 0, NULL, -1);
+ if (child == NULL) {
error = ENXIO;
goto failed;
}
+ children[i] = child;
+
/* Initialize per-device bus info */
- if ((dinfo = device_get_ivars(children[i])) == NULL) {
+ if ((dinfo = device_get_ivars(child)) == NULL) {
error = ENXIO;
goto failed;
}
@@ -897,14 +986,18 @@ siba_add_children(device_t dev)
if ((error = siba_register_addrspaces(dev, dinfo, r)))
goto failed;
+ /* Register the core's interrupts */
+ if ((error = siba_register_interrupts(dev, child, dinfo, r)))
+ goto failed;
+
/* Unmap the core's register block */
bhnd_release_resource(dev, SYS_RES_MEMORY, rid, r);
r = NULL;
/* If pins are floating or the hardware is otherwise
* unpopulated, the device shouldn't be used. */
- if (bhnd_is_hw_disabled(children[i]))
- device_disable(children[i]);
+ if (bhnd_is_hw_disabled(child))
+ device_disable(child);
}
/* Map all valid core's config register blocks and perform interrupt
@@ -912,7 +1005,6 @@ siba_add_children(device_t dev)
for (u_int i = 0; i < chipid->ncores; i++) {
struct siba_devinfo *dinfo;
device_t child;
- int nintr;
child = children[i];
@@ -926,16 +1018,6 @@ siba_add_children(device_t dev)
if ((error = siba_map_cfg_resources(dev, dinfo)))
goto failed;
- /* Assign interrupts */
- nintr = bhnd_get_intr_count(child);
- for (int rid = 0; rid < nintr; rid++) {
- error = BHND_BUS_ASSIGN_INTR(dev, child, rid);
- if (error) {
- device_printf(dev, "failed to assign interrupt "
- "%d to core %u: %d\n", rid, i, error);
- }
- }
-
/* Issue bus callback for fully initialized child. */
BHND_BUS_CHILD_ADDED(dev, child);
}
@@ -993,7 +1075,7 @@ static device_method_t siba_methods[] = {
DEVMETHOD(bhnd_bus_decode_port_rid, siba_decode_port_rid),
DEVMETHOD(bhnd_bus_get_region_addr, siba_get_region_addr),
DEVMETHOD(bhnd_bus_get_intr_count, siba_get_intr_count),
- DEVMETHOD(bhnd_bus_get_core_ivec, siba_get_core_ivec),
+ DEVMETHOD(bhnd_bus_get_intr_ivec, siba_get_intr_ivec),
DEVMETHOD_END
};
diff --git a/sys/dev/bhnd/siba/siba_bhndb.c b/sys/dev/bhnd/siba/siba_bhndb.c
index 13082edc2347..452fec94b7b1 100644
--- a/sys/dev/bhnd/siba/siba_bhndb.c
+++ b/sys/dev/bhnd/siba/siba_bhndb.c
@@ -51,20 +51,17 @@ __FBSDID("$FreeBSD$");
* Supports attachment of siba(4) bus devices via a bhndb bridge.
*/
-//
-// TODO: PCI rev < 6 interrupt handling
-//
-// On early PCI cores (rev < 6) interrupt masking is handled via interconnect
-// configuration registers (SBINTVEC), rather than the PCI_INT_MASK
-// config register.
-//
-// On those devices, we should handle interrupts locally using SBINTVEC, rather
-// than delegating to our parent bhndb device.
-//
-
-static int siba_bhndb_wars_hwup(struct siba_softc *sc);
-
-/* Bridge-specific core device quirks */
+struct siba_bhndb_softc;
+
+static int siba_bhndb_wars_hwup(struct siba_bhndb_softc *sc);
+
+/* siba_bhndb per-instance state */
+struct siba_bhndb_softc {
+ struct siba_softc siba; /**< common siba per-instance state */
+ uint32_t quirks; /**< bus-level quirks */
+};
+
+/* siba_bhndb quirks */
enum {
/** When PCIe-bridged, the D11 core's initiator request
* timeout must be disabled to prevent D11 from entering a
@@ -72,14 +69,22 @@ enum {
SIBA_QUIRK_PCIE_D11_SB_TIMEOUT = (1<<0)
};
-static struct bhnd_device_quirk bridge_quirks[] = {
- BHND_CHIP_QUIRK(4311, HWREV_EQ(2), SIBA_QUIRK_PCIE_D11_SB_TIMEOUT),
- BHND_CHIP_QUIRK(4312, HWREV_EQ(0), SIBA_QUIRK_PCIE_D11_SB_TIMEOUT),
+/* Bus-level quirks when bridged via a PCI host bridge core */
+static struct bhnd_device_quirk pci_bridge_quirks[] = {
BHND_DEVICE_QUIRK_END
};
+/* Bus-level quirks when bridged via a PCIe host bridge core */
+static struct bhnd_device_quirk pcie_bridge_quirks[] = {
+ BHND_CHIP_QUIRK (4311, HWREV_EQ(2), SIBA_QUIRK_PCIE_D11_SB_TIMEOUT),
+ BHND_CHIP_QUIRK (4312, HWREV_EQ(0), SIBA_QUIRK_PCIE_D11_SB_TIMEOUT),
+ BHND_DEVICE_QUIRK_END
+};
+
+/* Bus-level quirks specific to a particular host bridge core */
static struct bhnd_device bridge_devs[] = {
- BHND_DEVICE(BCM, PCI, NULL, bridge_quirks),
+ BHND_DEVICE(BCM, PCI, NULL, pci_bridge_quirks),
+ BHND_DEVICE(BCM, PCIE, NULL, pcie_bridge_quirks),
BHND_DEVICE_END
};
@@ -107,15 +112,23 @@ siba_bhndb_probe(device_t dev)
static int
siba_bhndb_attach(device_t dev)
{
- struct siba_softc *sc;
+ struct siba_bhndb_softc *sc;
+ device_t hostb;
int error;
sc = device_get_softc(dev);
+ sc->quirks = 0;
/* Perform initial attach and enumerate our children. */
if ((error = siba_attach(dev)))
goto failed;
+ /* Fetch bus-level quirks required by the host bridge core */
+ if ((hostb = bhnd_bus_find_hostb_device(dev)) != NULL) {
+ sc->quirks |= bhnd_device_quirks(hostb, bridge_devs,
+ sizeof(bridge_devs[0]));
+ }
+
/* Apply attach/resume workarounds before any child drivers attach */
if ((error = siba_bhndb_wars_hwup(sc)))
goto failed;
@@ -134,7 +147,7 @@ failed:
static int
siba_bhndb_resume(device_t dev)
{
- struct siba_softc *sc;
+ struct siba_bhndb_softc *sc;
int error;
sc = device_get_softc(dev);
@@ -151,11 +164,11 @@ siba_bhndb_resume(device_t dev)
static void
siba_bhndb_suspend_cfgblocks(device_t dev, struct siba_devinfo *dinfo) {
for (u_int i = 0; i < dinfo->core_id.num_cfg_blocks; i++) {
- if (dinfo->cfg[i] == NULL)
+ if (dinfo->cfg_res[i] == NULL)
continue;
BHNDB_SUSPEND_RESOURCE(device_get_parent(dev), dev,
- SYS_RES_MEMORY, dinfo->cfg[i]->res);
+ SYS_RES_MEMORY, dinfo->cfg_res[i]->res);
}
}
@@ -196,11 +209,11 @@ siba_bhndb_resume_child(device_t dev, device_t child)
/* Resume all resource references to the child's config registers */
for (u_int i = 0; i < dinfo->core_id.num_cfg_blocks; i++) {
- if (dinfo->cfg[i] == NULL)
+ if (dinfo->cfg_res[i] == NULL)
continue;
error = BHNDB_RESUME_RESOURCE(device_get_parent(dev), dev,
- SYS_RES_MEMORY, dinfo->cfg[i]->res);
+ SYS_RES_MEMORY, dinfo->cfg_res[i]->res);
if (error) {
siba_bhndb_suspend_cfgblocks(dev, dinfo);
return (error);
@@ -218,22 +231,14 @@ siba_bhndb_resume_child(device_t dev, device_t child)
/* Work-around implementation for SIBA_QUIRK_PCIE_D11_SB_TIMEOUT */
static int
-siba_bhndb_wars_pcie_clear_d11_timeout(struct siba_softc *sc)
+siba_bhndb_wars_pcie_clear_d11_timeout(struct siba_bhndb_softc *sc)
{
struct siba_devinfo *dinfo;
- device_t hostb_dev;
device_t d11;
uint32_t imcfg;
- /* Only applies when bridged by PCIe */
- if ((hostb_dev = bhnd_bus_find_hostb_device(sc->dev)) == NULL)
- return (ENXIO);
-
- if (bhnd_get_class(hostb_dev) != BHND_DEVCLASS_PCIE)
- return (0);
-
- /* Only applies if there's a D11 core */
- d11 = bhnd_bus_match_child(sc->dev, &(struct bhnd_core_match) {
+ /* Only applicable if there's a D11 core */
+ d11 = bhnd_bus_match_child(sc->siba.dev, &(struct bhnd_core_match) {
BHND_MATCH_CORE(BHND_MFGID_BCM, BHND_COREID_D11),
BHND_MATCH_CORE_UNIT(0)
});
@@ -242,12 +247,12 @@ siba_bhndb_wars_pcie_clear_d11_timeout(struct siba_softc *sc)
/* Clear initiator timeout in D11's CFG0 block */
dinfo = device_get_ivars(d11);
- KASSERT(dinfo->cfg[0] != NULL, ("missing core config mapping"));
+ KASSERT(dinfo->cfg_res[0] != NULL, ("missing core config mapping"));
- imcfg = bhnd_bus_read_4(dinfo->cfg[0], SIBA_CFG0_IMCONFIGLOW);
+ imcfg = bhnd_bus_read_4(dinfo->cfg_res[0], SIBA_CFG0_IMCONFIGLOW);
imcfg &= ~SIBA_IMCL_RTO_MASK;
- bhnd_bus_write_4(dinfo->cfg[0], SIBA_CFG0_IMCONFIGLOW, imcfg);
+ bhnd_bus_write_4(dinfo->cfg_res[0], SIBA_CFG0_IMCONFIGLOW, imcfg);
return (0);
}
@@ -257,19 +262,11 @@ siba_bhndb_wars_pcie_clear_d11_timeout(struct siba_softc *sc)
* of the bus.
*/
static int
-siba_bhndb_wars_hwup(struct siba_softc *sc)
+siba_bhndb_wars_hwup(struct siba_bhndb_softc *sc)
{
- device_t hostb_dev;
- uint32_t quirks;
- int error;
-
- if ((hostb_dev = bhnd_bus_find_hostb_device(sc->dev)) == NULL)
- return (ENXIO);
-
- quirks = bhnd_device_quirks(hostb_dev, bridge_devs,
- sizeof(bridge_devs[0]));
+ int error;
- if (quirks & SIBA_QUIRK_PCIE_D11_SB_TIMEOUT) {
+ if (sc->quirks & SIBA_QUIRK_PCIE_D11_SB_TIMEOUT) {
if ((error = siba_bhndb_wars_pcie_clear_d11_timeout(sc)))
return (error);
}
diff --git a/sys/dev/bhnd/siba/siba_erom.c b/sys/dev/bhnd/siba/siba_erom.c
index 1d7c0af62612..ac529da72ce7 100644
--- a/sys/dev/bhnd/siba/siba_erom.c
+++ b/sys/dev/bhnd/siba/siba_erom.c
@@ -334,7 +334,8 @@ siba_erom_lookup_core_addr(bhnd_erom_t *erom, const struct bhnd_core_match *desc
struct siba_core_id sid;
uint32_t am, am_addr, am_size;
u_int am_offset;
- u_int addrspace;
+ u_int addrspace, cfg;
+
int error;
sc = (struct siba_erom *)erom;
@@ -347,16 +348,64 @@ siba_erom_lookup_core_addr(bhnd_erom_t *erom, const struct bhnd_core_match *desc
sid = siba_eio_read_core_id(&sc->io, core.core_idx, core.unit);
/* Is port valid? */
- if (!siba_is_port_valid(sid.num_addrspace, type, port))
+ if (!siba_is_port_valid(&sid, type, port))
return (ENOENT);
/* Is region valid? */
- if (region >= siba_addrspace_region_count(sid.num_addrspace, port))
+ if (region >= siba_port_region_count(&sid, type, port))
return (ENOENT);
- /* Map the bhnd port values to a siba addrspace index */
- error = siba_addrspace_index(sid.num_addrspace, type, port, region,
- &addrspace);
+ /* Is this a siba configuration region? If so, this is mapped to an
+ * offset within the device0.0 port */
+ error = siba_cfg_index(&sid, type, port, region, &cfg);
+ if (!error) {
+ bhnd_addr_t region_addr;
+ bhnd_addr_t region_size;
+ bhnd_size_t cfg_offset, cfg_size;
+
+ cfg_offset = SIBA_CFG_OFFSET(cfg);
+ cfg_size = SIBA_CFG_SIZE;
+
+ /* Fetch the device0.0 addr/size */
+ error = siba_erom_lookup_core_addr(erom, desc, BHND_PORT_DEVICE,
+ 0, 0, NULL, &region_addr, &region_size);
+ if (error)
+ return (error);
+
+ /* Verify that our offset fits within the region */
+ if (region_size < cfg_size) {
+ printf("%s%u.%u offset %ju exceeds %s0.0 size %ju\n",
+ bhnd_port_type_name(type), port, region, cfg_offset,
+ bhnd_port_type_name(BHND_PORT_DEVICE), region_size);
+
+ return (ENXIO);
+ }
+
+ if (BHND_ADDR_MAX - region_addr < cfg_offset) {
+ printf("%s%u.%u offset %ju would overflow %s0.0 addr "
+ "%ju\n", bhnd_port_type_name(type), port, region,
+ cfg_offset, bhnd_port_type_name(BHND_PORT_DEVICE),
+ region_addr);
+
+ return (ENXIO);
+ }
+
+ if (info != NULL)
+ *info = core;
+
+ *addr = region_addr + cfg_offset;
+ *size = cfg_size;
+ return (0);
+ }
+
+ /*
+ * Otherwise, must be a device port.
+ *
+ * Map the bhnd device port to a siba addrspace index. Unlike siba(4)
+ * bus drivers, we do not exclude the siba(4) configuration blocks from
+ * the first device port.
+ */
+ error = siba_addrspace_index(&sid, type, port, region, &addrspace);
if (error)
return (error);
diff --git a/sys/dev/bhnd/siba/siba_subr.c b/sys/dev/bhnd/siba/siba_subr.c
index 52a5e019cc38..bd69fc7dcb71 100644
--- a/sys/dev/bhnd/siba/siba_subr.c
+++ b/sys/dev/bhnd/siba/siba_subr.c
@@ -1,7 +1,11 @@
/*-
- * Copyright (c) 2015 Landon Fuller <landon@landonf.org>
+ * Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org>
+ * Copyright (c) 2017 The FreeBSD Foundation
* All rights reserved.
*
+ * Portions of this software were developed by Landon Fuller
+ * under sponsorship from the FreeBSD Foundation.
+ *
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@@ -122,12 +126,19 @@ siba_alloc_dinfo(device_t bus)
return NULL;
for (u_int i = 0; i < nitems(dinfo->cfg); i++) {
- dinfo->cfg[i] = NULL;
+ dinfo->cfg[i] = ((struct siba_cfg_block){
+ .cb_base = 0,
+ .cb_size = 0,
+ .cb_rid = -1,
+ });
+ dinfo->cfg_res[i] = NULL;
dinfo->cfg_rid[i] = -1;
}
resource_list_init(&dinfo->resources);
+ dinfo->intr_en = false;
+
return dinfo;
}
@@ -151,12 +162,13 @@ siba_init_dinfo(device_t dev, struct siba_devinfo *dinfo,
}
/**
- * Map an addrspace index to its corresponding bhnd(4) port number.
+ * Map an addrspace index to its corresponding bhnd(4) BHND_PORT_DEVICE port
+ * number.
*
* @param addrspace Address space index.
*/
u_int
-siba_addrspace_port(u_int addrspace)
+siba_addrspace_device_port(u_int addrspace)
{
/* The first addrspace is always mapped to device0; the remainder
* are mapped to device1 */
@@ -167,12 +179,13 @@ siba_addrspace_port(u_int addrspace)
}
/**
- * Map an addrspace index to its corresponding bhnd(4) region number.
+ * Map an addrspace index to its corresponding bhnd(4) BHND_PORT_DEVICE port
+ * region number.
*
* @param addrspace Address space index.
*/
u_int
-siba_addrspace_region(u_int addrspace)
+siba_addrspace_device_region(u_int addrspace)
{
/* The first addrspace is always mapped to device0.0; the remainder
* are mapped to device1.0 + (n - 1) */
@@ -183,63 +196,194 @@ siba_addrspace_region(u_int addrspace)
}
/**
+ * Map an config block index to its corresponding bhnd(4) BHND_PORT_AGENT port
+ * number.
+ *
+ * @param cfg Config block index.
+ */
+u_int
+siba_cfg_agent_port(u_int cfg)
+{
+ /* Always agent0 */
+ return (0);
+}
+
+/**
+ * Map an config block index to its corresponding bhnd(4) BHND_PORT_AGENT port
+ * region number.
+ *
+ * @param cfg Config block index.
+ */
+u_int
+siba_cfg_agent_region(u_int cfg)
+{
+ /* Always agent0.<idx> */
+ return (cfg);
+}
+
+/**
* Return the number of bhnd(4) ports to advertise for the given
- * @p num_addrspace.
+ * @p core_id and @p port_type.
+ *
+ * Refer to the siba_addrspace_index() and siba_cfg_index() functions for
+ * information on siba's mapping of bhnd(4) port and region identifiers.
*
- * @param num_addrspace The number of siba address spaces.
+ * @param core_id The siba core info.
+ * @param port_type The bhnd(4) port type.
*/
u_int
-siba_addrspace_port_count(u_int num_addrspace)
+siba_port_count(struct siba_core_id *core_id, bhnd_port_type port_type)
+{
+ switch (port_type) {
+ case BHND_PORT_DEVICE:
+ /* 0, 1, or 2 ports */
+ return (min(core_id->num_addrspace, 2));
+
+ case BHND_PORT_AGENT:
+ /* One agent port maps all configuration blocks */
+ if (core_id->num_cfg_blocks > 0)
+ return (1);
+
+ /* Do not advertise an agent port if there are no configuration
+ * register blocks */
+ return (0);
+
+ default:
+ return (0);
+ }
+}
+
+/**
+ * Return true if @p port of @p port_type is defined by @p core_id, false
+ * otherwise.
+ *
+ * @param core_id The siba core info.
+ * @param port_type The bhnd(4) port type.
+ * @param port The bhnd(4) port number.
+ */
+bool
+siba_is_port_valid(struct siba_core_id *core_id, bhnd_port_type port_type,
+ u_int port)
{
- /* 0, 1, or 2 ports */
- return min(num_addrspace, 2);
+ /* Verify the index against the port count */
+ if (siba_port_count(core_id, port_type) <= port)
+ return (false);
+
+ return (true);
}
/**
- * Return the number of bhnd(4) regions to advertise on @p port
- * given the provided @p num_addrspace address space count.
+ * Return the number of bhnd(4) regions to advertise for @p core_id on the
+ * @p port of @p port_type.
*
- * @param num_addrspace The number of core-mapped siba(4) Sonics/OCP address
- * spaces.
+ * @param core_id The siba core info.
+ * @param port_type The bhnd(4) port type.
*/
u_int
-siba_addrspace_region_count(u_int num_addrspace, u_int port)
+siba_port_region_count(struct siba_core_id *core_id, bhnd_port_type port_type,
+ u_int port)
{
- /* The first address space, if any, is mapped to device0.0 */
- if (port == 0)
- return (min(num_addrspace, 1));
+ /* The port must exist */
+ if (!siba_is_port_valid(core_id, port_type, port))
+ return (0);
+
+ switch (port_type) {
+ case BHND_PORT_DEVICE:
+ /* The first address space, if any, is mapped to device0.0 */
+ if (port == 0)
+ return (min(core_id->num_addrspace, 1));
+
+ /* All remaining address spaces are mapped to device0.(n - 1) */
+ if (port == 1 && core_id->num_addrspace >= 2)
+ return (core_id->num_addrspace - 1);
+
+ break;
+
+ case BHND_PORT_AGENT:
+ /* All config blocks are mapped to a single port */
+ if (port == 0)
+ return (core_id->num_cfg_blocks);
+
+ break;
+
+ default:
+ break;
+ }
- /* All remaining address spaces are mapped to device0.(n - 1) */
- if (port == 1 && num_addrspace >= 2)
- return (num_addrspace - 1);
+ /* Validated above */
+ panic("siba_is_port_valid() returned true for unknown %s.%u port",
+ bhnd_port_type_name(port_type), port);
- /* No region mapping */
+}
+
+/**
+ * Map a bhnd(4) type/port/region triplet to its associated config block index,
+ * if any.
+ *
+ * We map config registers to port/region identifiers as follows:
+ *
+ * [port].[region] [cfg register block]
+ * agent0.0 0
+ * agent0.1 1
+ *
+ * @param num_addrspace The number of available siba address spaces.
+ * @param port_type The bhnd(4) port type.
+ * @param port The bhnd(4) port number.
+ * @param region The bhnd(4) port region.
+ * @param addridx On success, the corresponding addrspace index.
+ *
+ * @retval 0 success
+ * @retval ENOENT if the given type/port/region cannot be mapped to a
+ * siba config register block.
+ */
+int
+siba_cfg_index(struct siba_core_id *core_id, bhnd_port_type port_type,
+ u_int port, u_int region, u_int *cfgidx)
+{
+ /* Config blocks are mapped to agent ports */
+ if (port_type != BHND_PORT_AGENT)
+ return (ENOENT);
+
+ /* Port must be valid */
+ if (!siba_is_port_valid(core_id, port_type, port))
+ return (ENOENT);
+
+ if (region >= core_id->num_cfg_blocks)
+ return (ENOENT);
+
+ if (region >= SIBA_MAX_CFG)
+ return (ENOENT);
+
+ /* Found */
+ *cfgidx = region;
return (0);
}
/**
- * Return true if @p port is defined given an address space count
- * of @p num_addrspace, false otherwise.
+ * Map an bhnd(4) type/port/region triplet to its associated config block
+ * entry, if any.
*
- * Refer to the siba_find_addrspace() function for information on siba's
- * mapping of bhnd(4) port and region identifiers.
+ * The only supported port type is BHND_PORT_DEVICE.
*
- * @param num_addrspace The number of address spaces to verify the port against.
+ * @param dinfo The device info to search for a matching address space.
* @param type The bhnd(4) port type.
* @param port The bhnd(4) port number.
+ * @param region The bhnd(4) port region.
*/
-bool
-siba_is_port_valid(u_int num_addrspace, bhnd_port_type type, u_int port)
+struct siba_cfg_block *
+siba_find_cfg_block(struct siba_devinfo *dinfo, bhnd_port_type type, u_int port,
+ u_int region)
{
- /* Only device ports are supported */
- if (type != BHND_PORT_DEVICE)
- return (false);
+ u_int cfgidx;
+ int error;
- /* Verify the index against the port count */
- if (siba_addrspace_port_count(num_addrspace) <= port)
- return (false);
+ /* Map to addrspace index */
+ error = siba_cfg_index(&dinfo->core_id, type, port, region, &cfgidx);
+ if (error)
+ return (NULL);
- return (true);
+ /* Found */
+ return (&dinfo->cfg[cfgidx]);
}
/**
@@ -255,10 +399,8 @@ siba_is_port_valid(u_int num_addrspace, bhnd_port_type type, u_int port)
* device1.1 2
* device1.2 3
*
- * The only supported port type is BHND_PORT_DEVICE.
- *
- * @param num_addrspace The number of available siba address spaces.
- * @param type The bhnd(4) port type.
+ * @param core_id The siba core info.
+ * @param port_type The bhnd(4) port type.
* @param port The bhnd(4) port number.
* @param region The bhnd(4) port region.
* @param addridx On success, the corresponding addrspace index.
@@ -268,12 +410,17 @@ siba_is_port_valid(u_int num_addrspace, bhnd_port_type type, u_int port)
* siba address space.
*/
int
-siba_addrspace_index(u_int num_addrspace, bhnd_port_type type, u_int port,
- u_int region, u_int *addridx)
+siba_addrspace_index(struct siba_core_id *core_id, bhnd_port_type port_type,
+ u_int port, u_int region, u_int *addridx)
{
u_int idx;
- if (!siba_is_port_valid(num_addrspace, type, port))
+ /* Address spaces are always device ports */
+ if (port_type != BHND_PORT_DEVICE)
+ return (ENOENT);
+
+ /* Port must be valid */
+ if (!siba_is_port_valid(core_id, port_type, port))
return (ENOENT);
if (port == 0)
@@ -283,7 +430,7 @@ siba_addrspace_index(u_int num_addrspace, bhnd_port_type type, u_int port,
else
return (ENOENT);
- if (idx >= num_addrspace)
+ if (idx >= core_id->num_addrspace)
return (ENOENT);
/* Found */
@@ -310,8 +457,8 @@ siba_find_addrspace(struct siba_devinfo *dinfo, bhnd_port_type type, u_int port,
int error;
/* Map to addrspace index */
- error = siba_addrspace_index(dinfo->core_id.num_addrspace, type, port,
- region, &addridx);
+ error = siba_addrspace_index(&dinfo->core_id, type, port, region,
+ &addridx);
if (error)
return (NULL);
@@ -377,25 +524,32 @@ siba_append_dinfo_region(struct siba_devinfo *dinfo, uint8_t addridx,
* Deallocate the given device info structure and any associated resources.
*
* @param dev The requesting bus device.
- * @param dinfo Device info to be deallocated.
+ * @param child The siba child device.
+ * @param dinfo Device info associated with @p child to be deallocated.
*/
void
-siba_free_dinfo(device_t dev, struct siba_devinfo *dinfo)
+siba_free_dinfo(device_t dev, device_t child, struct siba_devinfo *dinfo)
{
resource_list_free(&dinfo->resources);
/* Free all mapped configuration blocks */
for (u_int i = 0; i < nitems(dinfo->cfg); i++) {
- if (dinfo->cfg[i] == NULL)
+ if (dinfo->cfg_res[i] == NULL)
continue;
bhnd_release_resource(dev, SYS_RES_MEMORY, dinfo->cfg_rid[i],
- dinfo->cfg[i]);
+ dinfo->cfg_res[i]);
- dinfo->cfg[i] = NULL;
+ dinfo->cfg_res[i] = NULL;
dinfo->cfg_rid[i] = -1;
}
+ /* Unmap the core's interrupt */
+ if (dinfo->intr_en && dinfo->intr.mapped) {
+ BHND_BUS_UNMAP_INTR(dev, child, dinfo->intr.irq);
+ dinfo->intr.mapped = false;
+ }
+
free(dinfo, M_BHND);
}
@@ -490,7 +644,7 @@ siba_write_target_state(device_t dev, struct siba_devinfo *dinfo,
uint32_t rval;
/* Must have a CFG0 block */
- if ((r = dinfo->cfg[0]) == NULL)
+ if ((r = dinfo->cfg_res[0]) == NULL)
return (ENODEV);
/* Verify the register offset falls within CFG register block */
@@ -535,7 +689,7 @@ siba_wait_target_busy(device_t dev, struct siba_devinfo *dinfo, int usec)
struct bhnd_resource *r;
uint32_t ts_high;
- if ((r = dinfo->cfg[0]) == NULL)
+ if ((r = dinfo->cfg_res[0]) == NULL)
return (ENODEV);
for (int i = 0; i < usec; i += 10) {
diff --git a/sys/dev/bhnd/siba/sibareg.h b/sys/dev/bhnd/siba/sibareg.h
index 60771f982da2..db5831233d54 100644
--- a/sys/dev/bhnd/siba/sibareg.h
+++ b/sys/dev/bhnd/siba/sibareg.h
@@ -48,7 +48,7 @@
#define SIBA_ENUM_ADDR BHND_DEFAULT_CHIPC_ADDR /**< enumeration space */
#define SIBA_ENUM_SIZE 0x00100000 /**< size of the enumeration space */
#define SIBA_CORE_SIZE BHND_DEFAULT_CORE_SIZE /**< per-core register block size */
-#define SIBA_CORE_NUM_INTR 1 /**< number of per-core interrupt lines */
+#define SIBA_MAX_INTR 32 /**< maximum number of backplane interrupt vectors */
#define SIBA_MAX_CORES \
(SIBA_ENUM_SIZE/SIBA_CORE_SIZE) /**< Maximum number of cores */
@@ -70,9 +70,13 @@
#define SIBA_CFG0_OFFSET 0xf00 /**< first configuration block */
#define SIBA_CFG1_OFFSET 0xe00 /**< second configuration block (sonics >= 2.3) */
-
#define SIBA_CFG_SIZE 0x100 /**< cfg register block size */
+/* Return the SIBA_CORE_ADDR-relative offset for the given siba configuration
+ * register block; configuration blocks are allocated starting at
+ * SIBA_CFG0_OFFSET, growing downwards. */
+#define SIBA_CFG_OFFSET(_n) (SIBA_CFG0_OFFSET - ((_n) * SIBA_CFG_SIZE))
+
/* Return the SIBA_CORE_ADDR-relative offset for a SIBA_CFG* register. */
#define SB0_REG_ABS(off) ((off) + SIBA_CFG0_OFFSET)
#define SB1_REG_ABS(off) ((off) + SIBA_CFG1_OFFSET)
@@ -118,6 +122,9 @@
#define SIBA_IPS_INT4_MASK 0x3f000000 /* which sbflags get routed to mips interrupt 4 */
#define SIBA_IPS_INT4_SHIFT 24
+#define SIBA_IPS_INT_SHIFT(_i) ((_i - 1) * 8)
+#define SIBA_IPS_INT_MASK(_i) (SIBA_IPS_INT1_MASK << SIBA_IPS_INT_SHIFT(_i))
+
/* sbtpsflag */
#define SIBA_TPS_NUM0_MASK 0x3f /* interrupt sbFlag # generated by this core */
#define SIBA_TPS_NUM0_SHIFT 0
diff --git a/sys/dev/bhnd/siba/sibavar.h b/sys/dev/bhnd/siba/sibavar.h
index 27364660e8fb..35a5d6ffa1d7 100644
--- a/sys/dev/bhnd/siba/sibavar.h
+++ b/sys/dev/bhnd/siba/sibavar.h
@@ -1,7 +1,11 @@
/*-
- * Copyright (c) 2015 Landon Fuller <landon@landonf.org>
+ * Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org>
+ * Copyright (c) 2017 The FreeBSD Foundation
* All rights reserved.
*
+ * Portions of this software were developed by Landon Fuller
+ * under sponsorship from the FreeBSD Foundation.
+ *
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@@ -46,6 +50,7 @@
*/
struct siba_addrspace;
+struct siba_cfg_block;
struct siba_devinfo;
struct siba_core_id;
@@ -54,9 +59,9 @@ int siba_attach(device_t dev);
int siba_detach(device_t dev);
int siba_resume(device_t dev);
int siba_suspend(device_t dev);
-int siba_get_intr_count(device_t dev, device_t child);
-int siba_get_core_ivec(device_t dev, device_t child,
- u_int intr, uint32_t *ivec);
+u_int siba_get_intr_count(device_t dev, device_t child);
+int siba_get_intr_ivec(device_t dev, device_t child,
+ u_int intr, u_int *ivec);
uint16_t siba_get_bhnd_mfgid(uint16_t ocp_vendor);
@@ -69,24 +74,38 @@ struct siba_devinfo *siba_alloc_dinfo(device_t dev);
int siba_init_dinfo(device_t dev,
struct siba_devinfo *dinfo,
const struct siba_core_id *core_id);
-void siba_free_dinfo(device_t dev,
+void siba_free_dinfo(device_t dev, device_t child,
struct siba_devinfo *dinfo);
-u_int siba_addrspace_port_count(u_int num_addrspace);
-u_int siba_addrspace_region_count(u_int num_addrspace,
- u_int port);
+u_int siba_port_count(struct siba_core_id *core_id,
+ bhnd_port_type port_type);
+bool siba_is_port_valid(struct siba_core_id *core_id,
+ bhnd_port_type port_type, u_int port);
+
+u_int siba_port_region_count(
+ struct siba_core_id *core_id,
+ bhnd_port_type port_type, u_int port);
+
+int siba_cfg_index(struct siba_core_id *core_id,
+ bhnd_port_type type, u_int port, u_int region,
+ u_int *cfgidx);
-u_int siba_addrspace_port(u_int addrspace);
-u_int siba_addrspace_region(u_int addrspace);
-int siba_addrspace_index(u_int num_addrspace,
+int siba_addrspace_index(struct siba_core_id *core_id,
bhnd_port_type type, u_int port, u_int region,
u_int *addridx);
-bool siba_is_port_valid(u_int num_addrspace,
- bhnd_port_type type, u_int port);
+
+u_int siba_addrspace_device_port(u_int addrspace);
+u_int siba_addrspace_device_region(u_int addrspace);
+
+u_int siba_cfg_agent_port(u_int cfg);
+u_int siba_cfg_agent_region(u_int cfg);
struct siba_addrspace *siba_find_addrspace(struct siba_devinfo *dinfo,
bhnd_port_type type, u_int port, u_int region);
+struct siba_cfg_block *siba_find_cfg_block(struct siba_devinfo *dinfo,
+ bhnd_port_type type, u_int port, u_int region);
+
int siba_append_dinfo_region(struct siba_devinfo *dinfo,
uint8_t sid, uint32_t base, uint32_t size,
uint32_t bus_reserved);
@@ -134,6 +153,21 @@ struct siba_addrspace {
* address space reserved for the bus */
};
+/** siba(4) config block descriptor */
+struct siba_cfg_block {
+ uint32_t cb_base; /**< base address */
+ uint32_t cb_size; /**< size */
+ int cb_rid; /**< bus resource id */
+};
+
+/** siba(4) backplane interrupt flag descriptor */
+struct siba_intr {
+ u_int flag; /**< backplane flag # */
+ bool mapped; /**< if an irq has been mapped */
+ int rid; /**< bus resource id, or -1 if unassigned */
+ rman_res_t irq; /**< the mapped bus irq, if any */
+};
+
/**
* siba(4) per-core identification info.
*/
@@ -156,9 +190,12 @@ struct siba_devinfo {
struct resource_list resources; /**< per-core memory regions. */
struct siba_core_id core_id; /**< core identification info */
struct siba_addrspace addrspace[SIBA_MAX_ADDRSPACE]; /**< memory map descriptors */
+ struct siba_cfg_block cfg[SIBA_MAX_CFG]; /**< config block descriptors */
+ struct siba_intr intr; /**< interrupt flag descriptor, if any */
+ bool intr_en; /**< if true, core has an assigned interrupt flag */
- struct bhnd_resource *cfg[SIBA_MAX_CFG]; /**< SIBA_CFG_* registers */
- int cfg_rid[SIBA_MAX_CFG]; /**< SIBA_CFG_* resource IDs */
+ struct bhnd_resource *cfg_res[SIBA_MAX_CFG]; /**< bus-mapped config block registers */
+ int cfg_rid[SIBA_MAX_CFG]; /**< bus-mapped config block resource IDs */
struct bhnd_core_pmu_info *pmu_info; /**< Bus-managed PMU state, or NULL */
};