svn commit: r298276 - head/sys/dev/bhnd/bhndb
Adrian Chadd
adrian at FreeBSD.org
Tue Apr 19 15:52:57 UTC 2016
Author: adrian
Date: Tue Apr 19 15:52:55 2016
New Revision: 298276
URL: https://svnweb.freebsd.org/changeset/base/298276
Log:
[bhnd] Add support for specifying the address space used by bhndb children
This adds support for specifying the address space used by a bridge child;
this will either be the bridged SoC address space, or the host address space
required by children that map non SoC-address ranges from the PCI BAR.
This is necessary to support SROM/OTP child devices that live directly
beneath the bhndb device and require access to host resources, instead
of the standard behavior of delegating access to the bridged SoC address
space.
Submitted by: Landon Fuller <landonf at landonf.org>
Differential Revision: https://reviews.freebsd.org/D5757
Modified:
head/sys/dev/bhnd/bhndb/bhndb.c
head/sys/dev/bhnd/bhndb/bhndb_pci.c
head/sys/dev/bhnd/bhndb/bhndb_private.h
head/sys/dev/bhnd/bhndb/bhndb_subr.c
head/sys/dev/bhnd/bhndb/bhndbvar.h
Modified: head/sys/dev/bhnd/bhndb/bhndb.c
==============================================================================
--- head/sys/dev/bhnd/bhndb/bhndb.c Tue Apr 19 15:46:21 2016 (r298275)
+++ head/sys/dev/bhnd/bhndb/bhndb.c Tue Apr 19 15:52:55 2016 (r298276)
@@ -85,8 +85,11 @@ static int bhndb_read_chipid(struct b
const struct bhndb_hwcfg *cfg,
struct bhnd_chipid *result);
+bhndb_addrspace bhndb_get_addrspace(struct bhndb_softc *sc,
+ device_t child);
+
static struct rman *bhndb_get_rman(struct bhndb_softc *sc,
- int type);
+ device_t child, int type);
static int bhndb_init_child_resource(struct resource *r,
struct resource *parent,
@@ -509,6 +512,7 @@ bhndb_read_chipid(struct bhndb_softc *sc
int
bhndb_attach(device_t dev, bhnd_devclass_t bridge_devclass)
{
+ struct bhndb_devinfo *dinfo;
struct bhndb_softc *sc;
const struct bhndb_hwcfg *cfg;
int error;
@@ -525,45 +529,30 @@ bhndb_attach(device_t dev, bhnd_devclass
if ((error = bhndb_read_chipid(sc, cfg, &sc->chipid)))
return (error);
- /* Set up a resource manager for the device's address space. */
- sc->mem_rman.rm_start = 0;
- sc->mem_rman.rm_end = BUS_SPACE_MAXADDR_32BIT;
- sc->mem_rman.rm_type = RMAN_ARRAY;
- sc->mem_rman.rm_descr = "BHND I/O memory addresses";
-
- if ((error = rman_init(&sc->mem_rman))) {
- device_printf(dev, "could not initialize mem_rman\n");
- return (error);
- }
-
- error = rman_manage_region(&sc->mem_rman, 0, BUS_SPACE_MAXADDR_32BIT);
- if (error) {
- device_printf(dev, "could not configure mem_rman\n");
- goto failed;
- }
-
- /* Initialize basic resource allocation state. */
+ /* Populate generic resource allocation state. */
sc->bus_res = bhndb_alloc_resources(dev, sc->parent_dev, cfg);
if (sc->bus_res == NULL) {
- error = ENXIO;
- goto failed;
+ return (ENXIO);
}
/* Attach our bridged bus device */
- sc->bus_dev = device_add_child(dev, devclass_get_name(bhnd_devclass),
+ sc->bus_dev = BUS_ADD_CHILD(dev, 0, devclass_get_name(bhnd_devclass),
-1);
if (sc->bus_dev == NULL) {
error = ENXIO;
goto failed;
}
+ /* Configure address space */
+ dinfo = device_get_ivars(sc->bus_dev);
+ dinfo->addrspace = BHNDB_ADDRSPACE_BRIDGED;
+
+ /* Finish attach */
return (bus_generic_attach(dev));
failed:
BHNDB_LOCK_DESTROY(sc);
- rman_fini(&sc->mem_rman);
-
if (sc->bus_res != NULL)
bhndb_free_resources(sc->bus_res);
@@ -680,7 +669,6 @@ bhndb_generic_detach(device_t dev)
return (error);
/* Clean up our driver state. */
- rman_fini(&sc->mem_rman);
bhndb_free_resources(sc->bus_res);
BHNDB_LOCK_DESTROY(sc);
@@ -826,24 +814,60 @@ bhndb_write_ivar(device_t dev, device_t
}
/**
+ * Return the address space for the given @p child device.
+ */
+bhndb_addrspace
+bhndb_get_addrspace(struct bhndb_softc *sc, device_t child)
+{
+ struct bhndb_devinfo *dinfo;
+ device_t imd_dev;
+
+ /* Find the directly attached parent of the requesting device */
+ imd_dev = child;
+ while (imd_dev != NULL && device_get_parent(imd_dev) != sc->dev)
+ imd_dev = device_get_parent(imd_dev);
+
+ if (imd_dev == NULL)
+ panic("bhndb address space request for non-child device %s\n",
+ device_get_nameunit(child));
+
+ dinfo = device_get_ivars(imd_dev);
+ return (dinfo->addrspace);
+}
+
+/**
* Return the rman instance for a given resource @p type, if any.
*
* @param sc The bhndb device state.
+ * @param child The requesting child.
* @param type The resource type (e.g. SYS_RES_MEMORY, SYS_RES_IRQ, ...)
*/
static struct rman *
-bhndb_get_rman(struct bhndb_softc *sc, int type)
-{
- switch (type) {
- case SYS_RES_MEMORY:
- return &sc->mem_rman;
- case SYS_RES_IRQ:
- // TODO
- // return &sc->irq_rman;
- return (NULL);
- default:
- return (NULL);
- };
+bhndb_get_rman(struct bhndb_softc *sc, device_t child, int type)
+{
+ switch (bhndb_get_addrspace(sc, child)) {
+ case BHNDB_ADDRSPACE_NATIVE:
+ switch (type) {
+ case SYS_RES_MEMORY:
+ return (&sc->bus_res->ht_mem_rman);
+ case SYS_RES_IRQ:
+ return (NULL);
+ default:
+ return (NULL);
+ };
+
+ case BHNDB_ADDRSPACE_BRIDGED:
+ switch (type) {
+ case SYS_RES_MEMORY:
+ return (&sc->bus_res->br_mem_rman);
+ case SYS_RES_IRQ:
+ // TODO
+ // return &sc->irq_rman;
+ return (NULL);
+ default:
+ return (NULL);
+ };
+ }
}
/**
@@ -865,6 +889,7 @@ bhndb_add_child(device_t dev, u_int orde
return (NULL);
}
+ dinfo->addrspace = BHNDB_ADDRSPACE_NATIVE;
resource_list_init(&dinfo->resources);
device_set_ivars(child, dinfo);
@@ -1062,7 +1087,7 @@ bhndb_alloc_resource(device_t dev, devic
return (NULL);
/* Fetch the resource manager */
- rm = bhndb_get_rman(sc, type);
+ rm = bhndb_get_rman(sc, child, type);
if (rm == NULL)
return (NULL);
@@ -1080,8 +1105,8 @@ bhndb_alloc_resource(device_t dev, devic
if (error) {
device_printf(dev,
"failed to activate entry %#x type %d for "
- "child %s\n",
- *rid, type, device_get_nameunit(child));
+ "child %s: %d\n",
+ *rid, type, device_get_nameunit(child), error);
rman_release_resource(rv);
@@ -1137,7 +1162,7 @@ bhndb_adjust_resource(device_t dev, devi
error = 0;
/* Fetch resource manager */
- rm = bhndb_get_rman(sc, type);
+ rm = bhndb_get_rman(sc, child, type);
if (rm == NULL)
return (ENXIO);
@@ -1159,7 +1184,8 @@ bhndb_adjust_resource(device_t dev, devi
/**
* Initialize child resource @p r with a virtual address, tag, and handle
- * copied from @p parent, adjusted to contain only the range defined by @p win.
+ * copied from @p parent, adjusted to contain only the range defined by
+ * @p offsize and @p size.
*
* @param r The register to be initialized.
* @param parent The parent bus resource that fully contains the subregion.
@@ -1171,7 +1197,6 @@ static int
bhndb_init_child_resource(struct resource *r,
struct resource *parent, bhnd_size_t offset, bhnd_size_t size)
{
-
bus_space_handle_t bh, child_bh;
bus_space_tag_t bt;
uintptr_t vaddr;
@@ -1335,13 +1360,39 @@ bhndb_try_activate_resource(struct bhndb
if (indirect)
*indirect = false;
+
+ r_start = rman_get_start(r);
+ r_size = rman_get_size(r);
+
+ /* Activate native addrspace resources using the host address space */
+ if (bhndb_get_addrspace(sc, child) == BHNDB_ADDRSPACE_NATIVE) {
+ struct resource *parent;
+
+ /* Find the bridge resource referenced by the child */
+ parent = bhndb_find_resource_range(sc->bus_res, r_start,
+ r_size);
+ if (parent == NULL) {
+ device_printf(sc->dev, "host resource not found "
+ "for 0x%llx-0x%llx\n",
+ (unsigned long long) r_start,
+ (unsigned long long) r_start + r_size - 1);
+ return (ENOENT);
+ }
+
+ /* Initialize child resource with the real bus values */
+ error = bhndb_init_child_resource(r, parent,
+ r_start - rman_get_start(parent), r_size);
+ if (error)
+ return (error);
+
+ /* Try to activate child resource */
+ return (rman_activate_resource(r));
+ }
/* Default to low priority */
dw_priority = BHNDB_PRIORITY_LOW;
/* Look for a bus region matching the resource's address range */
- r_start = rman_get_start(r);
- r_size = rman_get_size(r);
region = bhndb_find_resource_region(sc->bus_res, r_start, r_size);
if (region != NULL)
dw_priority = region->priority;
@@ -1431,7 +1482,7 @@ bhndb_deactivate_resource(device_t dev,
sc = device_get_softc(dev);
- if ((rm = bhndb_get_rman(sc, type)) == NULL)
+ if ((rm = bhndb_get_rman(sc, child, type)) == NULL)
return (EINVAL);
/* Mark inactive */
@@ -1439,11 +1490,13 @@ bhndb_deactivate_resource(device_t dev,
return (error);
/* Free any dynamic window allocation. */
- 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);
+ 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);
}
@@ -1516,9 +1569,13 @@ bhndb_release_bhnd_resource(device_t dev
/**
* Default bhndb(4) implementation of BHND_BUS_ACTIVATE_RESOURCE().
+ *
+ * For BHNDB_ADDRSPACE_NATIVE children, all resources may be assumed to
+ * be actived by the bridge.
*
- * 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, attempts to activate a static register
+ * window, a dynamic register window, or configures @p r as an indirect
+ * resource -- in that order.
*/
static int
bhndb_activate_bhnd_resource(device_t dev, device_t child,
@@ -1526,7 +1583,6 @@ bhndb_activate_bhnd_resource(device_t de
{
struct bhndb_softc *sc;
struct bhndb_region *region;
- bhndb_priority_t r_prio;
rman_res_t r_start, r_size;
int error;
bool indirect;
@@ -1539,19 +1595,25 @@ bhndb_activate_bhnd_resource(device_t de
sc = device_get_softc(dev);
- /* Fetch the address range's resource priority */
r_start = rman_get_start(r->res);
r_size = rman_get_size(r->res);
- r_prio = BHNDB_PRIORITY_NONE;
- region = bhndb_find_resource_region(sc->bus_res, r_start, r_size);
- if (region != NULL)
- r_prio = region->priority;
-
- /* If less than the minimum dynamic window priority, this
- * resource should always be indirect. */
- if (r_prio < sc->bus_res->min_prio)
- return (0);
+ /* Verify bridged address range's resource priority, 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);
+ }
/* Attempt direct activation */
error = bhndb_try_activate_resource(sc, child, type, rid, r->res,
@@ -1565,7 +1627,9 @@ bhndb_activate_bhnd_resource(device_t de
r->direct = false;
}
- if (BHNDB_DEBUG(PRIO)) {
+ if (BHNDB_DEBUG(PRIO) &&
+ bhndb_get_addrspace(sc, child) == BHNDB_ADDRSPACE_BRIDGED)
+ {
device_printf(child, "activated 0x%llx-0x%llx as %s "
"resource\n",
(unsigned long long) r_start,
Modified: head/sys/dev/bhnd/bhndb/bhndb_pci.c
==============================================================================
--- head/sys/dev/bhnd/bhndb/bhndb_pci.c Tue Apr 19 15:46:21 2016 (r298275)
+++ head/sys/dev/bhnd/bhndb/bhndb_pci.c Tue Apr 19 15:52:55 2016 (r298276)
@@ -375,10 +375,18 @@ bhndb_pci_init_full_config(device_t dev,
* access to the PCIe SerDes required by the quirk workarounds.
*/
if (sc->pci_devclass == BHND_DEVCLASS_PCIE) {
- sc->mdio = device_add_child(dev,
+ sc->mdio = BUS_ADD_CHILD(dev, 0,
devclass_get_name(bhnd_mdio_pci_devclass), 0);
if (sc->mdio == NULL)
return (ENXIO);
+
+ error = bus_set_resource(sc->mdio, SYS_RES_MEMORY, 0,
+ rman_get_start(sc->mem_res) + sc->mem_off +
+ BHND_PCIE_MDIO_CTL, sizeof(uint32_t)*2);
+ if (error) {
+ device_printf(dev, "failed to set MDIO resource\n");
+ return (error);
+ }
if ((error = device_probe_and_attach(sc->mdio))) {
device_printf(dev, "failed to attach MDIO device\n");
@@ -1021,25 +1029,6 @@ bhndb_pci_discover_quirks(struct bhndb_p
static int
bhndb_mdio_pcie_probe(device_t dev)
{
- struct bhndb_softc *psc;
- device_t parent;
-
- /* Parent must be a bhndb_pcie instance */
- parent = device_get_parent(dev);
- if (device_get_driver(parent) != &bhndb_pci_driver)
- return (ENXIO);
-
- /* Parent must have PCIe-Gen1 hostb device */
- psc = device_get_softc(parent);
- if (psc->hostb_dev == NULL)
- return (ENXIO);
-
- if (bhnd_get_vendor(psc->hostb_dev) != BHND_MFGID_BCM ||
- bhnd_get_device(psc->hostb_dev) != BHND_COREID_PCIE)
- {
- return (ENXIO);
- }
-
device_quiet(dev);
return (BUS_PROBE_NOWILDCARD);
}
@@ -1048,15 +1037,11 @@ static int
bhndb_mdio_pcie_attach(device_t dev)
{
struct bhndb_pci_softc *psc;
-
psc = device_get_softc(device_get_parent(dev));
-
return (bhnd_mdio_pcie_attach(dev,
&psc->bhnd_mem_res, -1,
psc->mem_off + BHND_PCIE_MDIO_CTL,
(psc->quirks & BHNDB_PCIE_QUIRK_SD_C22_EXTADDR) != 0));
-
- return (ENXIO);
}
static device_method_t bhnd_mdio_pcie_methods[] = {
Modified: head/sys/dev/bhnd/bhndb/bhndb_private.h
==============================================================================
--- head/sys/dev/bhnd/bhndb/bhndb_private.h Tue Apr 19 15:46:21 2016 (r298275)
+++ head/sys/dev/bhnd/bhndb/bhndb_private.h Tue Apr 19 15:52:55 2016 (r298276)
@@ -50,6 +50,10 @@ struct bhndb_dw_alloc;
struct bhndb_region;
struct bhndb_resources;
+struct resource *bhndb_find_resource_range(
+ struct bhndb_resources *br,
+ rman_res_t start, rman_res_t count);
+
struct resource *bhndb_find_regwin_resource(
struct bhndb_resources *br,
const struct bhndb_regwin *win);
@@ -167,6 +171,9 @@ struct bhndb_resources {
device_t parent_dev; /**< parent device */
struct resource_spec *res_spec; /**< parent bus resource specs */
struct resource **res; /**< parent bus resources */
+
+ struct rman ht_mem_rman; /**< host memory manager */
+ struct rman br_mem_rman; /**< bridged memory manager */
STAILQ_HEAD(, bhndb_region) bus_regions; /**< bus region descriptors */
Modified: head/sys/dev/bhnd/bhndb/bhndb_subr.c
==============================================================================
--- head/sys/dev/bhnd/bhndb/bhndb_subr.c Tue Apr 19 15:46:21 2016 (r298275)
+++ head/sys/dev/bhnd/bhndb/bhndb_subr.c Tue Apr 19 15:52:55 2016 (r298276)
@@ -183,6 +183,39 @@ bhnd_generic_br_resume_child(device_t de
}
/**
+ * Find a SYS_RES_MEMORY resource containing the given address range.
+ *
+ * @param br The bhndb resource state to search.
+ * @param start The start address of the range to search for.
+ * @param count The size of the range to search for.
+ *
+ * @retval resource the host resource containing the requested range.
+ * @retval NULL if no resource containing the requested range can be found.
+ */
+struct resource *
+bhndb_find_resource_range(struct bhndb_resources *br, rman_res_t start,
+ rman_res_t count)
+{
+ for (u_int i = 0; br->res_spec[i].type != -1; i++) {
+ struct resource *r = br->res[i];
+
+ if (br->res_spec->type != SYS_RES_MEMORY)
+ continue;
+
+ /* Verify range */
+ if (rman_get_start(r) > start)
+ continue;
+
+ if (rman_get_end(r) < (start + count - 1))
+ continue;
+
+ return (r);
+ }
+
+ return (NULL);
+}
+
+/**
* Find the resource containing @p win.
*
* @param br The bhndb resource state to search.
@@ -235,8 +268,11 @@ bhndb_alloc_resources(device_t dev, devi
u_int rnid;
int error;
bool free_parent_res;
+ bool free_ht_mem, free_br_mem;
free_parent_res = false;
+ free_ht_mem = false;
+ free_br_mem = false;
r = malloc(sizeof(*r), M_BHND, M_NOWAIT|M_ZERO);
if (r == NULL)
@@ -249,6 +285,37 @@ bhndb_alloc_resources(device_t dev, devi
r->min_prio = BHNDB_PRIORITY_NONE;
STAILQ_INIT(&r->bus_regions);
+ /* Initialize host address space resource manager. */
+ r->ht_mem_rman.rm_start = 0;
+ r->ht_mem_rman.rm_end = ~0;
+ r->ht_mem_rman.rm_type = RMAN_ARRAY;
+ r->ht_mem_rman.rm_descr = "BHNDB host memory";
+ if ((error = rman_init(&r->ht_mem_rman))) {
+ device_printf(r->dev, "could not initialize ht_mem_rman\n");
+ goto failed;
+ }
+ free_ht_mem = true;
+
+
+ /* Initialize resource manager for the bridged address space. */
+ r->br_mem_rman.rm_start = 0;
+ r->br_mem_rman.rm_end = BUS_SPACE_MAXADDR_32BIT;
+ r->br_mem_rman.rm_type = RMAN_ARRAY;
+ r->br_mem_rman.rm_descr = "BHNDB bridged memory";
+
+ if ((error = rman_init(&r->br_mem_rman))) {
+ device_printf(r->dev, "could not initialize br_mem_rman\n");
+ goto failed;
+ }
+ free_br_mem = true;
+
+ error = rman_manage_region(&r->br_mem_rman, 0, BUS_SPACE_MAXADDR_32BIT);
+ if (error) {
+ device_printf(r->dev, "could not configure br_mem_rman\n");
+ goto failed;
+ }
+
+
/* Determine our bridge resource count from the hardware config. */
res_num = 0;
for (size_t i = 0; cfg->resource_specs[i].type != -1; i++)
@@ -284,6 +351,26 @@ bhndb_alloc_resources(device_t dev, devi
free_parent_res = true;
}
+ /* Add allocated memory resources to our host memory resource manager */
+ for (u_int i = 0; r->res_spec[i].type != -1; i++) {
+ struct resource *res;
+
+ /* skip non-memory resources */
+ if (r->res_spec[i].type != SYS_RES_MEMORY)
+ continue;
+
+ /* add host resource to set of managed regions */
+ res = r->res[i];
+ error = rman_manage_region(&r->ht_mem_rman, rman_get_start(res),
+ rman_get_end(res));
+ if (error) {
+ device_printf(r->dev,
+ "could not register host memory region with "
+ "ht_mem_rman: %d\n", error);
+ goto failed;
+ }
+ }
+
/* Fetch the dynamic regwin count and verify that it does not exceed
* what is representable via our freelist bitmask. */
r->dwa_count = bhndb_regwin_count(cfg->register_windows,
@@ -371,6 +458,12 @@ bhndb_alloc_resources(device_t dev, devi
failed:
if (free_parent_res)
bus_release_resources(r->parent_dev, r->res_spec, r->res);
+
+ if (free_ht_mem)
+ rman_fini(&r->ht_mem_rman);
+
+ if (free_br_mem)
+ rman_fini(&r->br_mem_rman);
if (r->res != NULL)
free(r->res, M_BHND);
@@ -423,6 +516,10 @@ bhndb_free_resources(struct bhndb_resour
free(region, M_BHND);
}
+ /* Release our resource managers */
+ rman_fini(&br->ht_mem_rman);
+ rman_fini(&br->br_mem_rman);
+
/* Free backing resource state structures */
free(br->res, M_BHND);
free(br->res_spec, M_BHND);
Modified: head/sys/dev/bhnd/bhndb/bhndbvar.h
==============================================================================
--- head/sys/dev/bhnd/bhndb/bhndbvar.h Tue Apr 19 15:46:21 2016 (r298275)
+++ head/sys/dev/bhnd/bhndb/bhndbvar.h Tue Apr 19 15:52:55 2016 (r298276)
@@ -65,9 +65,20 @@ int bhndb_generic_init_full_config(devic
int bhnd_generic_br_suspend_child(device_t dev, device_t child);
int bhnd_generic_br_resume_child(device_t dev, device_t child);
+/**
+ * bhndb child address space. Children either operate in the bridged
+ * SoC address space, or within the address space mapped to the host
+ * device (e.g. the PCI BAR(s)).
+ */
+typedef enum {
+ BHNDB_ADDRSPACE_BRIDGED, /**< bridged (SoC) address space */
+ BHNDB_ADDRSPACE_NATIVE /**< host address space */
+} bhndb_addrspace;
+
/** bhndb child instance state */
struct bhndb_devinfo {
- struct resource_list resources; /**< child resources. */
+ bhndb_addrspace addrspace; /**< child address space. */
+ struct resource_list resources; /**< child resources. */
};
/**
@@ -85,9 +96,7 @@ struct bhndb_softc {
if the @p bus_dev has not yet
called BHNDB_INIT_FULL_CONFIG() */
- struct rman mem_rman; /**< bridged bus memory manager */
struct mtx sc_mtx; /**< resource lock. */
-
struct bhndb_resources *bus_res; /**< bus resource state */
};
More information about the svn-src-head
mailing list