Index: head/sys/dev/bhnd/bhndb/bhndb.c =================================================================== --- head/sys/dev/bhnd/bhndb/bhndb.c +++ head/sys/dev/bhnd/bhndb/bhndb.c @@ -85,8 +85,11 @@ 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 @@ 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 @@ 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 @@ 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 @@ } /** + * 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 @@ return (NULL); } + dinfo->addrspace = BHNDB_ADDRSPACE_NATIVE; resource_list_init(&dinfo->resources); device_set_ivars(child, dinfo); @@ -1062,7 +1087,7 @@ 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 @@ 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 @@ 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 @@ /** * 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ /** * 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 @@ { 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 @@ 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 @@ 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, Index: head/sys/dev/bhnd/bhndb/bhndb_pci.c =================================================================== --- head/sys/dev/bhnd/bhndb/bhndb_pci.c +++ head/sys/dev/bhnd/bhndb/bhndb_pci.c @@ -375,10 +375,18 @@ * 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 @@ 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 @@ 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[] = { Index: head/sys/dev/bhnd/bhndb/bhndb_private.h =================================================================== --- head/sys/dev/bhnd/bhndb/bhndb_private.h +++ head/sys/dev/bhnd/bhndb/bhndb_private.h @@ -50,6 +50,10 @@ 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 @@ 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 */ Index: head/sys/dev/bhnd/bhndb/bhndb_subr.c =================================================================== --- head/sys/dev/bhnd/bhndb/bhndb_subr.c +++ head/sys/dev/bhnd/bhndb/bhndb_subr.c @@ -183,6 +183,39 @@ } /** + * 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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); Index: head/sys/dev/bhnd/bhndb/bhndbvar.h =================================================================== --- head/sys/dev/bhnd/bhndb/bhndbvar.h +++ head/sys/dev/bhnd/bhndb/bhndbvar.h @@ -65,9 +65,20 @@ 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 @@ 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 */ };