Index: head/sys/dev/bhnd/bcma/bcma.c =================================================================== --- head/sys/dev/bhnd/bcma/bcma.c +++ head/sys/dev/bhnd/bcma/bcma.c @@ -48,6 +48,12 @@ /* RID used when allocating EROM table */ #define BCMA_EROM_RID 0 +static bhnd_erom_class_t * +bcma_get_erom_class(driver_t *driver) +{ + return (&bcma_erom_parser); +} + int bcma_probe(device_t dev) { @@ -55,70 +61,25 @@ return (BUS_PROBE_DEFAULT); } +/** + * Default bcma(4) bus driver implementation of DEVICE_ATTACH(). + * + * This implementation initializes internal bcma(4) state and performs + * bus enumeration, and must be called by subclassing drivers in + * DEVICE_ATTACH() before any other bus methods. + */ int bcma_attach(device_t dev) { - struct bcma_devinfo *dinfo; - device_t *devs, child; - int ndevs; - int error; + int error; - - if ((error = device_get_children(dev, &devs, &ndevs))) + /* Enumerate children */ + if ((error = bcma_add_children(dev))) { + device_delete_children(dev); return (error); - - /* - * Map our children's agent register block. - */ - for (int i = 0; i < ndevs; i++) { - bhnd_addr_t addr; - bhnd_size_t size; - rman_res_t r_start, r_count, r_end; - - child = devs[i]; - dinfo = device_get_ivars(child); - - KASSERT(!device_is_suspended(child), - ("bcma(4) stateful suspend handling requires that devices " - "not be suspended before bcma_attach()")); - - /* Verify that the agent register block exists and is - * mappable */ - if (bhnd_get_port_rid(child, BHND_PORT_AGENT, 0, 0) == -1) - continue; - - /* 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(dev, "failed fetching agent register " - "block address for core %d\n", i); - goto cleanup; - } - - /* Allocate the resource */ - r_start = addr; - r_count = size; - r_end = r_start + r_count - 1; - - dinfo->rid_agent = i + 1; - dinfo->res_agent = BHND_BUS_ALLOC_RESOURCE(dev, dev, - SYS_RES_MEMORY, &dinfo->rid_agent, r_start, r_end, r_count, - RF_ACTIVE); - if (dinfo->res_agent == NULL) { - device_printf(dev, "failed allocating agent register " - "block for core %d\n", i); - error = ENXIO; - goto cleanup; - } } -cleanup: - free(devs, M_BHND); - if (error) - return (error); - - return (bhnd_generic_attach(dev)); + return (0); } int @@ -191,15 +152,6 @@ return (&dinfo->resources); } -static device_t -bcma_find_hostb_device(device_t dev) -{ - struct bcma_softc *sc = device_get_softc(dev); - - /* This is set (or not) by the concrete bcma driver subclass. */ - return (sc->hostb_dev); -} - static int bcma_reset_core(device_t dev, device_t child, uint16_t flags) { @@ -516,8 +468,7 @@ corecfg = NULL; /* Allocate our EROM parser */ - erom = bhnd_erom_alloc(&bcma_erom_parser, bus, BCMA_EROM_RID, - cid->enum_addr); + erom = bhnd_erom_alloc(&bcma_erom_parser, cid, bus, BCMA_EROM_RID); if (erom == NULL) return (ENODEV); @@ -528,17 +479,21 @@ child = BUS_ADD_CHILD(bus, 0, NULL, -1); if (child == NULL) { error = ENXIO; - goto failed; + goto cleanup; } /* Initialize device ivars */ dinfo = device_get_ivars(child); if ((error = bcma_init_dinfo(bus, dinfo, corecfg))) - goto failed; + 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; + /* If pins are floating or the hardware is otherwise * unpopulated, the device shouldn't be used. */ if (bhnd_is_hw_disabled(child)) @@ -548,16 +503,19 @@ BHND_BUS_CHILD_ADDED(bus, child); } - /* Hit EOF parsing cores? */ + /* EOF while parsing cores is expected */ if (error == ENOENT) error = 0; -failed: +cleanup: bhnd_erom_free(erom); if (corecfg != NULL) bcma_free_corecfg(corecfg); + if (error) + device_delete_children(bus); + return (error); } @@ -574,7 +532,7 @@ DEVMETHOD(bus_get_resource_list, bcma_get_resource_list), /* BHND interface */ - DEVMETHOD(bhnd_bus_find_hostb_device, bcma_find_hostb_device), + DEVMETHOD(bhnd_bus_get_erom_class, bcma_get_erom_class), DEVMETHOD(bhnd_bus_alloc_devinfo, bcma_alloc_bhnd_dinfo), DEVMETHOD(bhnd_bus_free_devinfo, bcma_free_bhnd_dinfo), DEVMETHOD(bhnd_bus_reset_core, bcma_reset_core), Index: head/sys/dev/bhnd/bcma/bcma_bhndb.c =================================================================== --- head/sys/dev/bhnd/bcma/bcma_bhndb.c +++ head/sys/dev/bhnd/bcma/bcma_bhndb.c @@ -72,26 +72,21 @@ static int bcma_bhndb_attach(device_t dev) { - struct bcma_softc *sc; - int error; - - sc = device_get_softc(dev); + int error; - /* Enumerate our children. */ - if ((error = bcma_add_children(dev))) - return (error); + /* Perform initial attach and enumerate our children. */ + if ((error = bcma_attach(dev))) + goto failed; + + /* Delegate remainder to standard bhnd method implementation */ + if ((error = bhnd_generic_attach(dev))) + goto failed; - /* Initialize full bridge configuration */ - error = BHNDB_INIT_FULL_CONFIG(device_get_parent(dev), dev, - bhndb_bcma_priority_table); - if (error) - return (error); - - /* Ask our parent bridge to find the corresponding bridge core */ - sc->hostb_dev = BHNDB_FIND_HOSTB_DEVICE(device_get_parent(dev), dev); + return (0); - /* Call our superclass' implementation */ - return (bcma_attach(dev)); +failed: + device_delete_children(dev); + return (error); } static int Index: head/sys/dev/bhnd/bcma/bcma_erom.c =================================================================== --- head/sys/dev/bhnd/bcma/bcma_erom.c +++ head/sys/dev/bhnd/bcma/bcma_erom.c @@ -58,7 +58,13 @@ * marker. */ +struct bcma_erom_io; + static const char *bcma_erom_entry_type_name (uint8_t entry); + +static uint32_t bcma_eio_read4(struct bcma_erom_io *io, + bus_size_t offset); + static int bcma_erom_read32(struct bcma_erom *erom, uint32_t *entry); static int bcma_erom_skip32(struct bcma_erom *erom); @@ -72,8 +78,10 @@ static int bcma_erom_region_to_port_type(struct bcma_erom *erom, uint8_t region_type, bhnd_port_type *port_type); + static int bcma_erom_peek32(struct bcma_erom *erom, uint32_t *entry); + static bus_size_t bcma_erom_tell(struct bcma_erom *erom); static void bcma_erom_seek(struct bcma_erom *erom, bus_size_t offset); @@ -96,9 +104,33 @@ u_int core_idx, int core_unit, struct bhnd_core_info *info); +/** + * BCMA EROM generic I/O context + */ +struct bcma_erom_io { + struct bhnd_resource *res; /**< memory resource, or NULL if initialized + with bus space tag and handle */ + int rid; /**< memory resource id, or -1 */ + + bus_space_tag_t bst; /**< bus space tag, if any */ + bus_space_handle_t bsh; /**< bus space handle, if any */ + + bus_size_t start; /**< base read offset */ +}; + +/** + * BCMA EROM per-instance state. + */ +struct bcma_erom { + struct bhnd_erom obj; + device_t dev; /**< parent device, or NULL if none. */ + struct bcma_erom_io io; /**< I/O context */ + bus_size_t offset; /**< current read offset */ +}; + #define EROM_LOG(erom, fmt, ...) do { \ if (erom->dev != NULL) { \ - device_printf(erom->dev, "erom[0x%llx]: " fmt, \ + device_printf(erom->dev, "erom[0x%llx]: " fmt, \ (unsigned long long) (erom->offset), ##__VA_ARGS__);\ } else { \ printf("erom[0x%llx]: " fmt, \ @@ -106,7 +138,6 @@ } \ } while(0) - /** Return the type name for an EROM entry */ static const char * bcma_erom_entry_type_name (uint8_t entry) @@ -123,47 +154,112 @@ } } + +/** + * Read a 32-bit value from an EROM I/O context. + * + * @param io EROM I/O context. + * @param offset Read offset. + */ +static uint32_t +bcma_eio_read4(struct bcma_erom_io *io, bus_size_t offset) +{ + bus_size_t read_off; + + read_off = io->start + offset; + if (io->res != NULL) + return (bhnd_bus_read_4(io->res, read_off)); + else + return (bus_space_read_4(io->bst, io->bsh, read_off)); +} + +/* Initialize bcma_erom resource I/O context */ +static void +bcma_eio_init(struct bcma_erom_io *io, struct bhnd_resource *res, int rid, + bus_size_t offset) +{ + io->res = res; + io->rid = rid; + io->start = offset; +} + +/* Initialize bcma_erom bus space I/O context */ +static void +bcma_eio_init_static(struct bcma_erom_io *io, bus_space_tag_t bst, + bus_space_handle_t bsh, bus_size_t offset) +{ + io->res = NULL; + io->rid = -1; + io->bst = bst; + io->bsh = bsh; + io->start = offset; +} + +/* BCMA implementation of BHND_EROM_INIT() */ static int -bcma_erom_init(bhnd_erom_t *erom, device_t parent, int rid, bus_addr_t enum_addr) +bcma_erom_init(bhnd_erom_t *erom, const struct bhnd_chipid *cid, + device_t parent, int rid) { - struct bcma_erom *sc = (struct bcma_erom *)erom; + struct bcma_erom *sc; + struct bhnd_resource *res; + sc = (struct bcma_erom *)erom; sc->dev = parent; + sc->offset = 0; + + res = bhnd_alloc_resource(parent, SYS_RES_MEMORY, &rid, cid->enum_addr, + cid->enum_addr + BCMA_EROM_TABLE_SIZE - 1, BCMA_EROM_TABLE_SIZE, + RF_ACTIVE|RF_SHAREABLE); - sc->rid = rid; - sc->res = bhnd_alloc_resource(parent, SYS_RES_MEMORY, &sc->rid, - enum_addr, enum_addr + BCMA_EROM_TABLE_SIZE - 1, - BCMA_EROM_TABLE_SIZE, RF_ACTIVE|RF_SHAREABLE); - if (sc->res == NULL) + if (res == NULL) return (ENOMEM); - - sc->start = BCMA_EROM_TABLE_START; + + bcma_eio_init(&sc->io, res, rid, BCMA_EROM_TABLE_START); + + return (0); +} + +/* BCMA implementation of BHND_EROM_INIT_STATIC() */ +static int +bcma_erom_init_static(bhnd_erom_t *erom, const struct bhnd_chipid *cid, + bus_space_tag_t bst, bus_space_handle_t bsh) +{ + struct bcma_erom *sc; + + sc = (struct bcma_erom *)erom; + sc->dev = NULL; sc->offset = 0; + bcma_eio_init_static(&sc->io, bst, bsh, BCMA_EROM_TABLE_START); + return (0); } +/* Common implementation of BHND_EROM_PROBE/BHND_EROM_PROBE_STATIC */ static int -bcma_erom_probe_static(bhnd_erom_class_t *cls, bus_space_tag_t bst, - bus_space_handle_t bsh, bus_addr_t paddr, struct bhnd_chipid *cid) +bcma_erom_probe_common(struct bcma_erom_io *io, const struct bhnd_chipid *hint, + struct bhnd_chipid *cid) { - uint32_t idreg, eaddr; - uint8_t chip_type; + uint32_t idreg, eromptr; - idreg = bus_space_read_4(bst, bsh, CHIPC_ID); - chip_type = CHIPC_GET_BITS(idreg, CHIPC_ID_BUS); + /* Hints aren't supported; all BCMA devices have a ChipCommon + * core */ + if (hint != NULL) + return (EINVAL); - /* Fetch EROM physical address */ - if (!BHND_CHIPTYPE_HAS_EROM(chip_type)) + /* Confirm CHIPC_EROMPTR availability */ + idreg = bcma_eio_read4(io, CHIPC_ID); + if (!BHND_CHIPTYPE_HAS_EROM(CHIPC_GET_BITS(idreg, CHIPC_ID_BUS))) return (ENXIO); - eaddr = bus_space_read_4(bst, bsh, CHIPC_EROMPTR); + /* Fetch EROM address */ + eromptr = bcma_eio_read4(io, CHIPC_EROMPTR); /* Parse chip identifier */ - *cid = bhnd_parse_chipid(idreg, eaddr); + *cid = bhnd_parse_chipid(idreg, eromptr); /* Verify chip type */ - switch (chip_type) { + switch (cid->chip_type) { case BHND_CHIPTYPE_BCMA: return (BUS_PROBE_DEFAULT); @@ -173,37 +269,44 @@ default: return (ENXIO); - } + } } static int -bcma_erom_init_static(bhnd_erom_t *erom, bus_space_tag_t bst, - bus_space_handle_t bsh) +bcma_erom_probe(bhnd_erom_class_t *cls, struct bhnd_resource *res, + bus_size_t offset, const struct bhnd_chipid *hint, struct bhnd_chipid *cid) { - struct bcma_erom *sc = (struct bcma_erom *)erom; + struct bcma_erom_io io; - sc->dev = NULL; - sc->rid = -1; - sc->res = NULL; - sc->bst = bst; - sc->bsh = bsh; - sc->start = BCMA_EROM_TABLE_START; - sc->offset = 0; + bcma_eio_init(&io, res, rman_get_rid(res->res), + offset + BCMA_EROM_TABLE_START); - return (0); + return (bcma_erom_probe_common(&io, hint, cid)); } +static int +bcma_erom_probe_static(bhnd_erom_class_t *cls, bus_space_tag_t bst, + bus_space_handle_t bsh, bus_addr_t paddr, const struct bhnd_chipid *hint, + struct bhnd_chipid *cid) +{ + struct bcma_erom_io io; + + bcma_eio_init_static(&io, bst, bsh, BCMA_EROM_TABLE_START); + return (bcma_erom_probe_common(&io, hint, cid)); +} + + static void bcma_erom_fini(bhnd_erom_t *erom) { struct bcma_erom *sc = (struct bcma_erom *)erom; - if (sc->res != NULL) { - bhnd_release_resource(sc->dev, SYS_RES_MEMORY, sc->rid, - sc->res); + if (sc->io.res != NULL) { + bhnd_release_resource(sc->dev, SYS_RES_MEMORY, sc->io.rid, + sc->io.res); - sc->res = NULL; - sc->rid = -1; + sc->io.res = NULL; + sc->io.rid = -1; } } @@ -484,19 +587,12 @@ static int bcma_erom_peek32(struct bcma_erom *erom, uint32_t *entry) { - bus_size_t off; - - if (erom->offset >= BCMA_EROM_TABLE_SIZE) { + if (erom->offset >= (BCMA_EROM_TABLE_SIZE - sizeof(uint32_t))) { EROM_LOG(erom, "BCMA EROM table missing terminating EOF\n"); return (EINVAL); } - - off = erom->start + erom->offset; - if (erom->res != NULL) - *entry = bhnd_bus_read_4(erom->res, off); - else - *entry = bus_space_read_4(erom->bst, erom->bsh, off); - + + *entry = bcma_eio_read4(&erom->io, erom->offset); return (0); } @@ -1259,6 +1355,7 @@ } static kobj_method_t bcma_erom_methods[] = { + KOBJMETHOD(bhnd_erom_probe, bcma_erom_probe), KOBJMETHOD(bhnd_erom_probe_static, bcma_erom_probe_static), KOBJMETHOD(bhnd_erom_init, bcma_erom_init), KOBJMETHOD(bhnd_erom_init_static, bcma_erom_init_static), Index: head/sys/dev/bhnd/bcma/bcma_eromvar.h =================================================================== --- head/sys/dev/bhnd/bcma/bcma_eromvar.h +++ head/sys/dev/bhnd/bcma/bcma_eromvar.h @@ -42,25 +42,6 @@ int bcma_erom_next_corecfg(struct bcma_erom *sc, struct bcma_corecfg **result); -/** - * BCMA EROM per-instance state. - */ -struct bcma_erom { - struct bhnd_erom obj; - device_t dev; /**< EROM parent device, or NULL - if none. */ - struct bhnd_resource *res; /**< EROM table resource, or - NULL if initialized with - bus space tag and handle */ - int rid; /**< EROM table rid, or -1 */ - - bus_space_tag_t bst; /**< EROM table bus space */ - bus_space_handle_t bsh; /**< EROM table bus handle */ - - bus_size_t start; /**< EROM table offset */ - bus_size_t offset; /**< current read offset */ -}; - /** EROM core descriptor. */ struct bcma_erom_core { uint16_t vendor; /**< core's designer */ Index: head/sys/dev/bhnd/bcma/bcma_nexus.c =================================================================== --- head/sys/dev/bhnd/bcma/bcma_nexus.c +++ head/sys/dev/bhnd/bcma/bcma_nexus.c @@ -93,10 +93,19 @@ { int error; - if ((error = bcma_add_children(dev))) - return (error); + /* Perform initial attach and enumerate our children. */ + if ((error = bcma_attach(dev))) + goto failed; + + /* Delegate remainder to standard bhnd method implementation */ + if ((error = bhnd_generic_attach(dev))) + goto failed; + + return (0); - return (bcma_attach(dev)); +failed: + device_delete_children(dev); + return (error); } static const struct bhnd_chipid * Index: head/sys/dev/bhnd/bcma/bcma_subr.c =================================================================== --- head/sys/dev/bhnd/bcma/bcma_subr.c +++ head/sys/dev/bhnd/bcma/bcma_subr.c @@ -43,6 +43,10 @@ #include "bcmavar.h" +/* Return the resource ID for a device's agent register allocation */ +#define BCMA_AGENT_RID(_dinfo) \ + (BCMA_AGENT_RID_BASE + BCMA_DINFO_COREIDX(_dinfo)) + /** * Allocate and initialize new core config structure. * @@ -244,6 +248,63 @@ 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; + + 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); + 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); +} + + /** * Deallocate the given device info structure and any associated resources. * Index: head/sys/dev/bhnd/bcma/bcmavar.h =================================================================== --- head/sys/dev/bhnd/bcma/bcmavar.h +++ head/sys/dev/bhnd/bcma/bcmavar.h @@ -45,6 +45,18 @@ * Internal definitions shared by bcma(4) driver implementations. */ +/** Base resource ID for per-core agent register allocations */ +#define BCMA_AGENT_RID_BASE 100 + +/** + * Return the device's core index. + * + * @param _dinfo The bcma_devinfo instance to query. + */ +#define BCMA_DINFO_COREIDX(_dinfo) \ + ((_dinfo)->corecfg->core_info.core_idx) + + /** BCMA port identifier. */ typedef u_int bcma_pid_t; #define BCMA_PID_MAX UINT_MAX /**< Maximum bcma_pid_t value */ @@ -72,6 +84,8 @@ int bcma_init_dinfo(device_t bus, 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, struct bcma_devinfo *dinfo); @@ -147,7 +161,6 @@ /** BMCA per-instance state */ struct bcma_softc { struct bhnd_softc bhnd_sc; /**< bhnd state */ - device_t hostb_dev; /**< host bridge core, or NULL */ }; #endif /* _BCMA_BCMAVAR_H_ */ Index: head/sys/dev/bhnd/bhnd.h =================================================================== --- head/sys/dev/bhnd/bhnd.h +++ head/sys/dev/bhnd/bhnd.h @@ -39,6 +39,7 @@ #include "bhnd_ids.h" #include "bhnd_types.h" +#include "bhnd_erom_types.h" #include "bhnd_debug.h" #include "bhnd_bus_if.h" #include "bhnd_match.h" @@ -188,6 +189,12 @@ * is MMIO accessible. */ }; +/** Wrap the active resource @p _r in a bhnd_resource structure */ +#define BHND_DIRECT_RESOURCE(_r) ((struct bhnd_resource) { \ + .res = (_r), \ + .direct = true, \ +}) + /** * Device quirk table descriptor. */ @@ -278,6 +285,13 @@ const struct bhnd_core_info *cores, u_int num_cores, bhnd_devclass_t class); +struct bhnd_core_match bhnd_core_get_match_desc( + const struct bhnd_core_info *core); + +bool bhnd_cores_equal( + const struct bhnd_core_info *lhs, + const struct bhnd_core_info *rhs); + bool bhnd_core_matches( const struct bhnd_core_info *core, const struct bhnd_core_match *desc); @@ -389,7 +403,16 @@ bhnd_attach_type bhnd_bus_generic_get_attach_type(device_t dev, device_t child); - +/** + * Return the bhnd(4) bus driver's device enumeration parser class + * + * @param driver A bhnd bus driver instance. + */ +static inline bhnd_erom_class_t * +bhnd_driver_get_erom_class(driver_t *driver) +{ + return (BHND_BUS_GET_EROM_CLASS(driver)); +} /** * Return the active host bridge core for the bhnd bus, if any, or NULL if Index: head/sys/dev/bhnd/bhnd_bus_if.m =================================================================== --- head/sys/dev/bhnd/bhnd_bus_if.m +++ head/sys/dev/bhnd/bhnd_bus_if.m @@ -29,6 +29,7 @@ #include #include +#include INTERFACE bhnd_bus; @@ -49,7 +50,13 @@ #include #include - + + static bhnd_erom_class_t * + bhnd_bus_null_get_erom_class(driver_t *driver) + { + return (NULL); + } + static struct bhnd_chipid * bhnd_bus_null_get_chipid(device_t dev, device_t child) { @@ -152,7 +159,7 @@ static device_t bhnd_bus_null_find_hostb_device(device_t dev) { - panic("bhnd_bus_find_hostb_device unimplemented"); + return (NULL); } static bool @@ -199,6 +206,15 @@ } /** + * Return the bhnd(4) bus driver's device enumeration parser class. + * + * @param driver The bhnd bus driver instance. + */ +STATICMETHOD bhnd_erom_class_t * get_erom_class { + driver_t *driver; +} DEFAULT bhnd_bus_null_get_erom_class; + +/** * Return the active host bridge core for the bhnd bus, if any. * * @param dev The bhnd bus device. Index: head/sys/dev/bhnd/bhnd_erom.h =================================================================== --- head/sys/dev/bhnd/bhnd_erom.h +++ head/sys/dev/bhnd/bhnd_erom.h @@ -42,11 +42,12 @@ #include "bhnd_erom_if.h" bhnd_erom_t *bhnd_erom_alloc(bhnd_erom_class_t *cls, - device_t parent, int rid, - bus_addr_t enum_addr); + const struct bhnd_chipid *cid, + device_t parent, int rid); int bhnd_erom_init_static(bhnd_erom_class_t *cls, bhnd_erom_t *erom, size_t esize, + const struct bhnd_chipid *cid, bus_space_tag_t bst, bus_space_handle_t bsh); @@ -94,15 +95,48 @@ /** * Probe to see if this device enumeration class supports the bhnd bus + * mapped by the given resource, returning a standard newbus device probe + * result (see BUS_PROBE_*) and the probed chip identification. + * + * @param cls The erom class to probe. + * @param res A resource mapping the first bus core (EXTIF or + * ChipCommon) + * @param offset Offset to the first bus core within @p res. + * @param hint Identification hint used to identify the device. If + * chipset supports standard chip identification registers + * within the first core, this parameter should be NULL. + * @param[out] cid On success, the probed chip identifier. + * + * @retval 0 if this is the only possible device enumeration + * parser for the probed bus. + * @retval negative if the probe succeeds, a negative value should be + * returned; the parser returning the highest negative + * value will be selected to handle device enumeration. + * @retval ENXIO If the bhnd bus type is not handled by this parser. + * @retval positive if an error occurs during probing, a regular unix error + * code should be returned. + */ +static inline int +bhnd_erom_probe(bhnd_erom_class_t *cls, struct bhnd_resource *res, + bus_size_t offset, const struct bhnd_chipid *hint, struct bhnd_chipid *cid) +{ + return (BHND_EROM_PROBE(cls, res, offset, hint, cid)); +} + +/** + * Probe to see if this device enumeration class supports the bhnd bus * mapped at the given bus space tag and handle, returning a standard * newbus device probe result (see BUS_PROBE_*) and the probed * chip identification. * - * @param cls The parser class to be probed. + * @param cls The erom class to probe. * @param bst Bus space tag. * @param bsh Bus space handle mapping the EXTIF or ChipCommon core. * @param paddr The physical address of the core mapped by @p bst and * @p bsh. + * @param hint Identification hint used to identify the device. If + * chipset supports standard chip identification registers + * within the first core, this parameter should be NULL. * @param[out] cid On success, the probed chip identifier. * * @retval 0 if this is the only possible device enumeration @@ -116,9 +150,10 @@ */ static inline int bhnd_erom_probe_static(bhnd_erom_class_t *cls, bus_space_tag_t bst, - bus_space_handle_t bsh, bus_addr_t paddr, struct bhnd_chipid *cid) + bus_space_handle_t bsh, bus_addr_t paddr, const struct bhnd_chipid *hint, + struct bhnd_chipid *cid) { - return (BHND_EROM_PROBE_STATIC(cls, bst, bsh, paddr, cid)); + return (BHND_EROM_PROBE_STATIC(cls, bst, bsh, paddr, hint, cid)); } /** Index: head/sys/dev/bhnd/bhnd_erom.c =================================================================== --- head/sys/dev/bhnd/bhnd_erom.c +++ head/sys/dev/bhnd/bhnd_erom.c @@ -45,15 +45,15 @@ * be allocated. * @param rid The resource ID to be used when allocating EROM * resources. - * @param enum_addr The base address of the device enumeration table. + * @param cid The device's chip identifier. * * @retval non-NULL success * @retval NULL if an error occured allocating or initializing the * EROM parser. */ bhnd_erom_t * -bhnd_erom_alloc(bhnd_erom_class_t *cls, device_t parent, int rid, - bus_addr_t enum_addr) +bhnd_erom_alloc(bhnd_erom_class_t *cls, const struct bhnd_chipid *cid, + device_t parent, int rid) { bhnd_erom_t *erom; int error; @@ -61,9 +61,9 @@ erom = (bhnd_erom_t *)kobj_create((kobj_class_t)cls, M_BHND, M_WAITOK|M_ZERO); - if ((error = BHND_EROM_INIT(erom, parent, rid, enum_addr))) { + if ((error = BHND_EROM_INIT(erom, cid, parent, rid))) { printf("error initializing %s parser at %#jx with " - "rid %d: %d\n", cls->name, (uintmax_t)enum_addr, rid, + "rid %d: %d\n", cls->name, (uintmax_t)cid->enum_addr, rid, error); kobj_delete((kobj_t)erom, M_BHND); @@ -86,6 +86,7 @@ * @param esize The total available number of bytes allocated for * @p erom. If this is less than is required by @p cls, * ENOMEM will be returned. + * @param cid The device's chip identifier. * @param bst Bus space tag. * @param bsh Bus space handle mapping the device enumeration * space. @@ -97,7 +98,7 @@ */ int bhnd_erom_init_static(bhnd_erom_class_t *cls, bhnd_erom_t *erom, size_t esize, - bus_space_tag_t bst, bus_space_handle_t bsh) + const struct bhnd_chipid *cid, bus_space_tag_t bst, bus_space_handle_t bsh) { kobj_class_t kcls; @@ -109,7 +110,7 @@ /* Perform instance initialization */ kobj_init_static((kobj_t)erom, kcls); - return (BHND_EROM_INIT_STATIC(erom, bst, bsh)); + return (BHND_EROM_INIT_STATIC(erom, cid, bst, bsh)); } /** Index: head/sys/dev/bhnd/bhnd_erom_if.m =================================================================== --- head/sys/dev/bhnd/bhnd_erom_if.m +++ head/sys/dev/bhnd/bhnd_erom_if.m @@ -45,15 +45,48 @@ /** * Probe to see if this device enumeration class supports the bhnd bus + * mapped by the given resource, returning a standard newbus device probe + * result (see BUS_PROBE_*) and the probed chip identification. + * + * @param cls The erom class to probe. + * @param res A resource mapping the first bus core. + * @param offset Offset to the first bus core within @p res. + * @param hint Hint used to identify the device. If chipset supports + * standard chip identification registers within the first + * core, this parameter should be NULL. + * @param[out] cid On success, the probed chip identifier. + * + * @retval 0 if this is the only possible device enumeration + * parser for the probed bus. + * @retval negative if the probe succeeds, a negative value should be + * returned; the parser returning the highest negative + * value will be selected to handle device enumeration. + * @retval ENXIO If the bhnd bus type is not handled by this parser. + * @retval positive if an error occurs during probing, a regular unix error + * code should be returned. + */ +STATICMETHOD int probe { + bhnd_erom_class_t *cls; + struct bhnd_resource *res; + bus_size_t offset; + const struct bhnd_chipid *hint; + struct bhnd_chipid *cid; +}; + +/** + * Probe to see if this device enumeration class supports the bhnd bus * mapped at the given bus space tag and handle, returning a standard * newbus device probe result (see BUS_PROBE_*) and the probed * chip identification. * - * @param cls The erom parse class to probe. + * @param cls The erom class to probe. * @param bst Bus space tag. - * @param bsh Bus space handle mapping the EXTIF or ChipCommon core. + * @param bsh Bus space handle mapping the first bus core. * @param paddr The physical address of the core mapped by @p bst and * @p bsh. + * @param hint Hint used to identify the device. If chipset supports + * standard chip identification registers within the first + * core, this parameter should be NULL. * @param[out] cid On success, the probed chip identifier. * * @retval 0 if this is the only possible device enumeration @@ -66,51 +99,54 @@ * code should be returned. */ STATICMETHOD int probe_static { - bhnd_erom_class_t *cls; - bus_space_tag_t bst; - bus_space_handle_t bsh; - bus_addr_t paddr; - struct bhnd_chipid *cid; + bhnd_erom_class_t *cls; + bus_space_tag_t bst; + bus_space_handle_t bsh; + bus_addr_t paddr; + const struct bhnd_chipid *hint; + struct bhnd_chipid *cid; }; /** * Initialize a device enumeration table parser. * * @param erom The erom parser to initialize. + * @param cid The device's chip identifier. * @param parent The parent device from which EROM resources should * be allocated. * @param rid The resource id to be used when allocating the * enumeration table. - * @param enum_addr The base address of the device enumeration table. * * @retval 0 success * @retval non-zero if an error occurs initializing the EROM parser, * a regular unix error code will be returned. */ METHOD int init { - bhnd_erom_t *erom; - device_t parent; - int rid; - bus_addr_t enum_addr; + bhnd_erom_t *erom; + const struct bhnd_chipid *cid; + device_t parent; + int rid; }; /** * Initialize an device enumeration table parser using the provided bus space * tag and handle. * - * @param erom The erom parser to initialize. - * @param bst Bus space tag. - * @param bsh bus space handle mapping the full bus enumeration - * space. + * @param erom The erom parser to initialize. + * @param cid The device's chip identifier. + * @param bst Bus space tag. + * @param bsh Bus space handle mapping the full bus enumeration + * space. * * @retval 0 success * @retval non-zero if an error occurs initializing the EROM parser, * a regular unix error code will be returned. */ METHOD int init_static { - bhnd_erom_t *erom; - bus_space_tag_t bst; - bus_space_handle_t bsh; + bhnd_erom_t *erom; + const struct bhnd_chipid *cid; + bus_space_tag_t bst; + bus_space_handle_t bsh; }; /** Index: head/sys/dev/bhnd/bhnd_match.h =================================================================== --- head/sys/dev/bhnd/bhnd_match.h +++ head/sys/dev/bhnd/bhnd_match.h @@ -105,8 +105,9 @@ core_id:1, core_rev:1, core_class:1, + core_idx:1, core_unit:1, - flags_unused:3; + flags_unused:2; } match; } m; @@ -114,6 +115,7 @@ uint16_t core_id; /**< required core ID */ struct bhnd_hwrev_match core_rev; /**< matching core revisions. */ bhnd_devclass_t core_class; /**< required bhnd class */ + u_int core_idx; /**< required core index */ int core_unit; /**< required core unit */ }; @@ -122,6 +124,7 @@ _BHND_COPY_MATCH_FIELD(_src, core_id), \ _BHND_COPY_MATCH_FIELD(_src, core_rev), \ _BHND_COPY_MATCH_FIELD(_src, core_class), \ + _BHND_COPY_MATCH_FIELD(_src, core_idx), \ _BHND_COPY_MATCH_FIELD(_src, core_unit) \ #define BHND_MATCH_CORE_VENDOR(_v) _BHND_SET_MATCH_FIELD(core_vendor, _v) @@ -129,6 +132,7 @@ #define BHND_MATCH_CORE_REV(_rev) _BHND_SET_MATCH_FIELD(core_rev, \ BHND_ ## _rev) #define BHND_MATCH_CORE_CLASS(_cls) _BHND_SET_MATCH_FIELD(core_class, _cls) +#define BHND_MATCH_CORE_IDX(_idx) _BHND_SET_MATCH_FIELD(core_idx, _idx) #define BHND_MATCH_CORE_UNIT(_unit) _BHND_SET_MATCH_FIELD(core_unit, _unit) /** @@ -255,6 +259,7 @@ core_id:1, core_rev:1, core_class:1, + core_idx:1, core_unit:1, chip_id:1, chip_rev:1, @@ -263,7 +268,7 @@ board_type:1, board_rev:1, board_srom_rev:1, - flags_unused:2; + flags_unused:1; } match; } m; @@ -271,6 +276,7 @@ uint16_t core_id; /**< required core ID */ struct bhnd_hwrev_match core_rev; /**< matching core revisions. */ bhnd_devclass_t core_class; /**< required bhnd class */ + u_int core_idx; /**< required core index */ int core_unit; /**< required core unit */ uint16_t chip_id; /**< required chip id */ Index: head/sys/dev/bhnd/bhnd_nexus.c =================================================================== --- head/sys/dev/bhnd/bhnd_nexus.c +++ head/sys/dev/bhnd/bhnd_nexus.c @@ -83,7 +83,7 @@ static bool bhnd_nexus_is_hw_disabled(device_t dev, device_t child) { - return false; + return (false); } static bhnd_attach_type Index: head/sys/dev/bhnd/bhnd_subr.c =================================================================== --- head/sys/dev/bhnd/bhnd_subr.c +++ head/sys/dev/bhnd/bhnd_subr.c @@ -485,6 +485,47 @@ return bhnd_match_core(cores, num_cores, &md); } + +/** + * Create an equality match descriptor for @p core. + * + * @param core The core info to be matched on. + * @param desc On return, will be populated with a match descriptor for @p core. + */ +struct bhnd_core_match +bhnd_core_get_match_desc(const struct bhnd_core_info *core) +{ + return ((struct bhnd_core_match) { + BHND_MATCH_CORE_VENDOR(core->vendor), + BHND_MATCH_CORE_ID(core->device), + BHND_MATCH_CORE_REV(HWREV_EQ(core->hwrev)), + BHND_MATCH_CORE_CLASS(bhnd_core_class(core)), + BHND_MATCH_CORE_IDX(core->core_idx), + BHND_MATCH_CORE_UNIT(core->unit) + }); +} + + +/** + * Return true if the @p lhs is equal to @p rhs + * + * @param lhs The first bhnd core descriptor to compare. + * @param rhs The second bhnd core descriptor to compare. + * + * @retval true if @p lhs is equal to @p rhs + * @retval false if @p lhs is not equal to @p rhs + */ +bool +bhnd_cores_equal(const struct bhnd_core_info *lhs, + const struct bhnd_core_info *rhs) +{ + struct bhnd_core_match md; + + /* Use an equality match descriptor to perform the comparison */ + md = bhnd_core_get_match_desc(rhs); + return (bhnd_core_matches(lhs, &md)); +} + /** * Return true if the @p core matches @p desc. * @@ -511,6 +552,9 @@ !bhnd_hwrev_matches(core->hwrev, &desc->core_rev)) return (false); + if (desc->m.match.core_idx && desc->core_idx != core->core_idx) + return (false); + if (desc->m.match.core_class && desc->core_class != bhnd_core_class(core)) return (false); Index: head/sys/dev/bhnd/bhndb/bhnd_bhndb.c =================================================================== --- head/sys/dev/bhnd/bhndb/bhnd_bhndb.c +++ head/sys/dev/bhnd/bhndb/bhnd_bhndb.c @@ -66,6 +66,22 @@ return (BHND_ATTACH_ADAPTER); } +static device_t +bhnd_bhndb_find_hostb_device(device_t dev) +{ + struct bhnd_core_info core; + struct bhnd_core_match md; + int error; + + /* Ask the bridge for the hostb core info */ + if ((error = BHNDB_GET_HOSTB_CORE(device_get_parent(dev), dev, &core))) + return (NULL); + + /* Find the corresponding bus device */ + md = bhnd_core_get_match_desc(&core); + return (bhnd_match_child(dev, &md)); +} + static bhnd_clksrc bhnd_bhndb_pwrctl_get_clksrc(device_t dev, device_t child, bhnd_clock clock) @@ -96,6 +112,7 @@ static device_method_t bhnd_bhndb_methods[] = { /* BHND interface */ DEVMETHOD(bhnd_bus_get_attach_type, bhnd_bhndb_get_attach_type), + 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_pwrctl_get_clksrc, bhnd_bhndb_pwrctl_get_clksrc), Index: head/sys/dev/bhnd/bhndb/bhndb.c =================================================================== --- head/sys/dev/bhnd/bhndb/bhndb.c +++ head/sys/dev/bhnd/bhndb/bhndb.c @@ -50,6 +50,8 @@ #include #include +#include + #include #include @@ -71,24 +73,31 @@ #define BHNDB_DEBUG(_type) (BHNDB_DEBUG_ ## _type & bhndb_debug) -static bool bhndb_hw_matches(device_t *devlist, - int num_devs, - const struct bhndb_hw *hw); - -static int bhndb_initialize_region_cfg( - struct bhndb_softc *sc, device_t *devs, - int ndevs, - const struct bhndb_hw_priority *table, - struct bhndb_resources *r); +static int bhndb_find_hostb_core(struct bhndb_softc *sc, + bhnd_erom_t *erom, + struct bhnd_core_info *core); + +static bhnd_erom_class_t *bhndb_probe_erom_class(struct bhndb_softc *sc, + struct bhnd_chipid *cid); + +static int bhndb_init_full_config(struct bhndb_softc *sc, + bhnd_erom_class_t *eromcls); + +static struct bhnd_core_info *bhndb_get_bridge_core(struct bhndb_softc *sc); + +static bool bhndb_hw_matches(struct bhnd_core_info *cores, + u_int ncores, const struct bhndb_hw *hw); + +static int bhndb_init_region_cfg(struct bhndb_softc *sc, + bhnd_erom_t *erom, + struct bhndb_resources *r, + struct bhnd_core_info *cores, u_int ncores, + const struct bhndb_hw_priority *table); static int bhndb_find_hwspec(struct bhndb_softc *sc, - device_t *devs, int ndevs, + struct bhnd_core_info *cores, u_int ncores, const struct bhndb_hw **hw); -static int bhndb_read_chipid(struct bhndb_softc *sc, - const struct bhndb_hwcfg *cfg, - struct bhnd_chipid *result); - bhndb_addrspace bhndb_get_addrspace(struct bhndb_softc *sc, device_t child); @@ -111,6 +120,10 @@ int type, int rid, struct resource *r, bool *indirect); +static inline struct bhndb_dw_alloc *bhndb_io_resource(struct bhndb_softc *sc, + bus_addr_t addr, bus_size_t size, + bus_size_t *offset); + /** * Default bhndb(4) implementation of DEVICE_PROBE(). @@ -183,26 +196,40 @@ } /** - * Return true if @p devlist matches the @p hw specification. + * Return the bridge core info. Will panic if the bridge core info has not yet + * been populated during full bridge configuration. * - * @param devlist A device table to match against. - * @param num_devs The number of devices in @p devlist. + * @param sc BHNDB device state. + */ +static struct bhnd_core_info * +bhndb_get_bridge_core(struct bhndb_softc *sc) +{ + if (!sc->have_br_core) + panic("bridge not yet fully configured; no bridge core!"); + + return (&sc->bridge_core); +} + +/** + * Return true if @p cores matches the @p hw specification. + * + * @param cores A device table to match against. + * @param ncores The number of cores in @p cores. * @param hw The hardware description to be matched against. */ static bool -bhndb_hw_matches(device_t *devlist, int num_devs, const struct bhndb_hw *hw) +bhndb_hw_matches(struct bhnd_core_info *cores, u_int ncores, + const struct bhndb_hw *hw) { for (u_int i = 0; i < hw->num_hw_reqs; i++) { const struct bhnd_core_match *match; - struct bhnd_core_info ci; bool found; match = &hw->hw_reqs[i]; found = false; - for (int d = 0; d < num_devs; d++) { - ci = bhnd_get_core_info(devlist[d]); - if (!bhnd_core_matches(&ci, match)) + for (u_int d = 0; d < ncores; d++) { + if (!bhnd_core_matches(&cores[d], match)) continue; found = true; @@ -217,20 +244,21 @@ } /** - * Initialize the region maps and priority configuration in @p r using - * the provided priority @p table and the set of devices attached to - * the bridged @p bus_dev . + * Initialize the region maps and priority configuration in @p br using + * the priority @p table and the set of cores enumerated by @p erom. * * @param sc The bhndb device state. - * @param devs All devices enumerated on the bridged bhnd bus. - * @param ndevs The length of @p devs. + * @param br The resource state to be configured. + * @param erom EROM parser used to enumerate @p cores. + * @param cores All cores enumerated on the bridged bhnd bus. + * @param ncores The length of @p cores. * @param table Hardware priority table to be used to determine the relative * priorities of per-core port resources. - * @param r The resource state to be configured. */ static int -bhndb_initialize_region_cfg(struct bhndb_softc *sc, device_t *devs, int ndevs, - const struct bhndb_hw_priority *table, struct bhndb_resources *r) +bhndb_init_region_cfg(struct bhndb_softc *sc, bhnd_erom_t *erom, + struct bhndb_resources *br, struct bhnd_core_info *cores, u_int ncores, + const struct bhndb_hw_priority *table) { const struct bhndb_hw_priority *hp; bhnd_addr_t addr; @@ -247,29 +275,40 @@ /* * Register bridge regions covering all statically mapped ports. */ - for (int i = 0; i < ndevs; i++) { + for (u_int i = 0; i < ncores; i++) { const struct bhndb_regwin *regw; - device_t child; + struct bhnd_core_info *core; + struct bhnd_core_match md; - child = devs[i]; + core = &cores[i]; + md = bhnd_core_get_match_desc(core); - for (regw = r->cfg->register_windows; + for (regw = br->cfg->register_windows; regw->win_type != BHNDB_REGWIN_T_INVALID; regw++) { /* Only core windows are supported */ if (regw->win_type != BHNDB_REGWIN_T_CORE) continue; - /* Skip non-applicable register windows. */ - if (!bhndb_regwin_matches_device(regw, child)) + /* Skip non-matching cores. */ + if (!bhndb_regwin_match_core(regw, core)) continue; - - /* Fetch the base address of the mapped port. */ - error = bhnd_get_region_addr(child, - regw->d.core.port_type, regw->d.core.port, - regw->d.core.region, &addr, &size); - if (error) - return (error); + + /* Fetch the base address of the mapped port */ + error = bhnd_erom_lookup_core_addr(erom, &md, + regw->d.core.port_type, + regw->d.core.port, + regw->d.core.region, + NULL, + &addr, + &size); + if (error) { + /* Skip non-applicable register windows */ + if (error == ENOENT) + continue; + + return (error); + } /* * Always defer to the register window's size. @@ -290,7 +329,7 @@ * The window priority for a statically mapped * region is always HIGH. */ - error = bhndb_add_resource_region(r, addr, size, + error = bhndb_add_resource_region(br, addr, size, BHNDB_PRIORITY_HIGH, regw); if (error) return (error); @@ -301,22 +340,24 @@ * Perform priority accounting and register bridge regions for all * ports defined in the priority table */ - for (int i = 0; i < ndevs; i++) { + for (u_int i = 0; i < ncores; i++) { struct bhndb_region *region; - device_t child; + struct bhnd_core_info *core; + struct bhnd_core_match md; - child = devs[i]; + core = &cores[i]; + md = bhnd_core_get_match_desc(core); /* * Skip priority accounting for cores that ... */ /* ... do not require bridge resources */ - if (bhnd_is_hw_disabled(child) || !device_is_enabled(child)) + if (BHNDB_BUS_IS_CORE_DISABLED(sc->parent_dev, sc->dev, core)) continue; /* ... do not have a priority table entry */ - hp = bhndb_hw_priority_find_device(table, child); + hp = bhndb_hw_priority_find_core(table, core); if (hp == NULL) continue; @@ -330,27 +371,26 @@ const struct bhndb_port_priority *pp; pp = &hp->ports[i]; - - /* Skip ports not defined on this device */ - if (!bhnd_is_region_valid(child, pp->type, pp->port, - pp->region)) - { - continue; - } /* Fetch the address+size of the mapped port. */ - error = bhnd_get_region_addr(child, pp->type, pp->port, - pp->region, &addr, &size); - if (error) - return (error); + error = bhnd_erom_lookup_core_addr(erom, &md, + pp->type, pp->port, pp->region, + NULL, &addr, &size); + if (error) { + /* Skip ports not defined on this device */ + if (error == ENOENT) + continue; + + return (error); + } /* Skip ports with an existing static mapping */ - region = bhndb_find_resource_region(r, addr, size); + region = bhndb_find_resource_region(br, addr, size); if (region != NULL && region->static_regwin != NULL) continue; /* Define a dynamic region for this port */ - error = bhndb_add_resource_region(r, addr, size, + error = bhndb_add_resource_region(br, addr, size, pp->priority, NULL); if (error) return (error); @@ -375,17 +415,17 @@ /* Determine the minimum priority at which we'll allocate direct * register windows from our dynamic pool */ size_t prio_total = prio_low + prio_default + prio_high; - if (prio_total <= r->dwa_count) { + if (prio_total <= br->dwa_count) { /* low+default+high priority regions get windows */ - r->min_prio = BHNDB_PRIORITY_LOW; + br->min_prio = BHNDB_PRIORITY_LOW; - } else if (prio_default + prio_high <= r->dwa_count) { + } else if (prio_default + prio_high <= br->dwa_count) { /* default+high priority regions get windows */ - r->min_prio = BHNDB_PRIORITY_DEFAULT; + br->min_prio = BHNDB_PRIORITY_DEFAULT; } else { /* high priority regions get windows */ - r->min_prio = BHNDB_PRIORITY_HIGH; + br->min_prio = BHNDB_PRIORITY_HIGH; } if (BHNDB_DEBUG(PRIO)) { @@ -393,10 +433,10 @@ const char *direct_msg, *type_msg; bhndb_priority_t prio, prio_min; - prio_min = r->min_prio; + prio_min = br->min_prio; device_printf(sc->dev, "min_prio: %d\n", prio_min); - STAILQ_FOREACH(region, &r->bus_regions, link) { + STAILQ_FOREACH(region, &br->bus_regions, link) { prio = region->priority; direct_msg = prio >= prio_min ? "direct" : "indirect"; @@ -418,8 +458,8 @@ * Find a hardware specification for @p dev. * * @param sc The bhndb device state. - * @param devs All devices enumerated on the bridged bhnd bus. - * @param ndevs The length of @p devs. + * @param cores All cores enumerated on the bridged bhnd bus. + * @param ncores The length of @p cores. * @param[out] hw On success, the matched hardware specification. * with @p dev. * @@ -427,15 +467,15 @@ * @retval non-zero if an error occurs fetching device info for comparison. */ static int -bhndb_find_hwspec(struct bhndb_softc *sc, device_t *devs, int ndevs, - const struct bhndb_hw **hw) +bhndb_find_hwspec(struct bhndb_softc *sc, struct bhnd_core_info *cores, + u_int ncores, const struct bhndb_hw **hw) { const struct bhndb_hw *next, *hw_table; /* Search for the first matching hardware config. */ hw_table = BHNDB_BUS_GET_HARDWARE_TABLE(sc->parent_dev, sc->dev); for (next = hw_table; next->hw_reqs != NULL; next++) { - if (!bhndb_hw_matches(devs, ndevs, next)) + if (!bhndb_hw_matches(cores, ncores, next)) continue; /* Found */ @@ -447,69 +487,15 @@ } /** - * Read the ChipCommon identification data for this device. - * - * @param sc bhndb device state. - * @param cfg The hardware configuration to use when mapping the ChipCommon - * registers. - * @param[out] result the chip identification data. - * - * @retval 0 success - * @retval non-zero if the ChipCommon identification data could not be read. - */ -static int -bhndb_read_chipid(struct bhndb_softc *sc, const struct bhndb_hwcfg *cfg, - struct bhnd_chipid *result) -{ - const struct bhnd_chipid *parent_cid; - const struct bhndb_regwin *cc_win; - struct resource_spec rs; - int error; - - /* Let our parent device override the discovery process */ - parent_cid = BHNDB_BUS_GET_CHIPID(sc->parent_dev, sc->dev); - if (parent_cid != NULL) { - *result = *parent_cid; - return (0); - } - - /* Find a register window we can use to map the first CHIPC_CHIPID_SIZE - * of ChipCommon registers. */ - cc_win = bhndb_regwin_find_best(cfg->register_windows, - BHND_DEVCLASS_CC, 0, BHND_PORT_DEVICE, 0, 0, CHIPC_CHIPID_SIZE); - if (cc_win == NULL) { - device_printf(sc->dev, "no chipcommon register window\n"); - return (0); - } - - /* We can assume a device without a static ChipCommon window uses the - * default ChipCommon address. */ - if (cc_win->win_type == BHNDB_REGWIN_T_DYN) { - error = BHNDB_SET_WINDOW_ADDR(sc->dev, cc_win, - BHND_DEFAULT_CHIPC_ADDR); - - if (error) { - device_printf(sc->dev, "failed to set chipcommon " - "register window\n"); - return (error); - } - } - - /* Let the default bhnd implemenation alloc/release the resource and - * perform the read */ - rs.type = cc_win->res.type; - rs.rid = cc_win->res.rid; - rs.flags = RF_ACTIVE; - - return (bhnd_read_chipid(sc->parent_dev, &rs, cc_win->win_offset, - result)); -} - -/** * Helper function that must be called by subclass bhndb(4) drivers * when implementing DEVICE_ATTACH() before calling any bhnd(4) or bhndb(4) * APIs on the bridge device. * + * This function will add a bridged bhnd(4) child device with a device order of + * BHND_PROBE_BUS. Any subclass bhndb(4) driver may use the BHND_PROBE_* + * priority bands to add additional devices that will be attached in + * their preferred order relative to the bridged bhnd(4) bus. + * * @param dev The bridge device to attach. * @param bridge_devclass The device class of the bridging core. This is used * to automatically detect the bridge core, and to disable additional bridge @@ -521,6 +507,7 @@ struct bhndb_devinfo *dinfo; struct bhndb_softc *sc; const struct bhndb_hwcfg *cfg; + bhnd_erom_class_t *eromcls; int error; sc = device_get_softc(dev); @@ -530,30 +517,48 @@ BHNDB_LOCK_INIT(sc); - /* Read our chip identification data */ - cfg = BHNDB_BUS_GET_GENERIC_HWCFG(sc->parent_dev, sc->dev); - if ((error = bhndb_read_chipid(sc, cfg, &sc->chipid))) - return (error); - /* Populate generic resource allocation state. */ + cfg = BHNDB_BUS_GET_GENERIC_HWCFG(sc->parent_dev, sc->dev); sc->bus_res = bhndb_alloc_resources(dev, sc->parent_dev, cfg); if (sc->bus_res == NULL) { return (ENXIO); } - /* Attach our bridged bus device */ - sc->bus_dev = BUS_ADD_CHILD(dev, 0, "bhnd", -1); + /* Allocate our host resources */ + if ((error = bhndb_alloc_host_resources(sc->bus_res))) + goto failed; + + /* Probe for a usable EROM class for our bridged bhnd(4) bus and + * populate our chip identifier. */ + BHNDB_LOCK(sc); + if ((eromcls = bhndb_probe_erom_class(sc, &sc->chipid)) == NULL) { + BHNDB_UNLOCK(sc); + + device_printf(sc->dev, "device enumeration unsupported; no " + "compatible driver found\n"); + return (ENXIO); + } + BHNDB_UNLOCK(sc); + + /* Add our bridged bus device */ + sc->bus_dev = BUS_ADD_CHILD(dev, BHND_PROBE_BUS, "bhnd", -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)); + /* Enumerate the bridged device and fully initialize our bridged + * resource configuration */ + if ((error = bhndb_init_full_config(sc, eromcls))) { + device_printf(sc->dev, "initializing full bridge " + "configuration failed: %d\n", error); + goto failed; + } + + return (0); failed: BHNDB_LOCK_DESTROY(sc); @@ -564,55 +569,265 @@ return (error); } + /** - * Default bhndb(4) implementation of BHNDB_INIT_FULL_CONFIG(). + * Return a borrowed reference to the host resource mapping at least + * BHND_DEFAULT_CORE_SIZE bytes at the first bus core, for use with + * bhnd_erom_probe(). + * + * This may return a borrowed reference to a bhndb_dw_alloc-managed + * resource; any additional resource mapping requests may invalidate this + * borrowed reference. + * + * @param sc BHNDB driver state. + * @param[out] offset On success, the offset within the returned resource + * at which the first bus core can be found. * - * This function provides the default bhndb implementation of - * BHNDB_INIT_FULL_CONFIG(), and must be called by any subclass driver - * overriding BHNDB_INIT_FULL_CONFIG(). - * - * As documented by BHNDB_INIT_FULL_CONFIG, this function performs final - * bridge configuration based on the hardware information enumerated by the - * child bus, and will reset all resource allocation state on the bridge. - * - * When calling this method: - * - Any bus resources previously allocated by @p child must be deallocated. - * - The @p child bus must have performed initial enumeration -- but not - * probe or attachment -- of its children. + * @retval non-NULL success. + * @retval NULL If no usable mapping could be found. */ -int -bhndb_generic_init_full_config(device_t dev, device_t child, - const struct bhndb_hw_priority *hw_prio_table) +static struct resource * +bhndb_erom_chipc_resource(struct bhndb_softc *sc, bus_size_t *offset) { - struct bhndb_softc *sc; - const struct bhndb_hw *hw; - struct bhndb_resources *r; - device_t *devs; - device_t hostb; - int ndevs; - int error; + const struct bhndb_hwcfg *cfg; + struct bhndb_dw_alloc *dwa; + struct resource *res; + const struct bhndb_regwin *win; - sc = device_get_softc(dev); - hostb = NULL; + BHNDB_LOCK_ASSERT(sc, MA_OWNED); + + cfg = sc->bus_res->cfg; + + /* Find a static register window mapping ChipCommon. */ + win = bhndb_regwin_find_core(cfg->register_windows, BHND_DEVCLASS_CC, + 0, BHND_PORT_DEVICE, 0, 0); + if (win != NULL) { + if (win->win_size < BHND_DEFAULT_CORE_SIZE) { + device_printf(sc->dev, + "chipcommon register window too small\n"); + return (NULL); + } + + res = bhndb_find_regwin_resource(sc->bus_res, win); + if (res == NULL) { + device_printf(sc->dev, + "chipcommon register window not allocated\n"); + return (NULL); + } + + *offset = win->win_offset; + return (res); + } + + /* We'll need to fetch and configure a dynamic window. We can assume a + * device without a static ChipCommon mapping uses the default siba(4) + * base address. */ + dwa = bhndb_io_resource(sc, BHND_DEFAULT_CHIPC_ADDR, + BHND_DEFAULT_CORE_SIZE, offset); + if (dwa != NULL) + return (dwa->parent_res); + + device_printf(sc->dev, "unable to map chipcommon registers; no usable " + "register window found\n"); + return (NULL); +} + +/** + * Probe all supported EROM classes, returning the best matching class + * (or NULL if not found), writing the probed chip identifier to @p cid. + * + * @param sc BHNDB driver state. + * @param cid On success, the bridged chipset's chip identifier. + */ +static bhnd_erom_class_t * +bhndb_probe_erom_class(struct bhndb_softc *sc, struct bhnd_chipid *cid) +{ + devclass_t bhndb_devclass; + const struct bhnd_chipid *hint; + struct resource *res; + bus_size_t res_offset; + driver_t **drivers; + int drv_count; + bhnd_erom_class_t *erom_cls; + int prio, result; + + BHNDB_LOCK_ASSERT(sc, MA_OWNED); + + erom_cls = NULL; + prio = 0; + + /* Let our parent device provide a chipid hint */ + hint = BHNDB_BUS_GET_CHIPID(sc->parent_dev, sc->dev); + + /* Fetch a borrowed reference to the resource mapping ChipCommon. */ + res = bhndb_erom_chipc_resource(sc, &res_offset); + if (res == NULL) + return (NULL); + + /* Fetch all available drivers */ + bhndb_devclass = device_get_devclass(sc->dev); + if (devclass_get_drivers(bhndb_devclass, &drivers, &drv_count) != 0) + return (NULL); + + /* Enumerate the drivers looking for the best available EROM class */ + for (int i = 0; i < drv_count; i++) { + struct bhnd_chipid pcid; + bhnd_erom_class_t *cls; + + cls = bhnd_driver_get_erom_class(drivers[i]); + if (cls == NULL) + continue; + + kobj_class_compile(cls); + + /* Probe the bus */ + result = bhnd_erom_probe(cls, &BHND_DIRECT_RESOURCE(res), + res_offset, hint, &pcid); + + /* The parser did not match if an error was returned */ + if (result > 0) + continue; + + /* Check for a new highest priority match */ + if (erom_cls == NULL || result > prio) { + prio = result; + + *cid = pcid; + erom_cls = cls; + } + + /* Terminate immediately on BUS_PROBE_SPECIFIC */ + if (result == BUS_PROBE_SPECIFIC) + break; + } + + return (erom_cls); +} + +/* ascending core index comparison used by bhndb_find_hostb_core() */ +static int +compare_core_index(const void *lhs, const void *rhs) +{ + u_int left = ((const struct bhnd_core_info *)lhs)->core_idx; + u_int right = ((const struct bhnd_core_info *)rhs)->core_idx; + + if (left < right) + return (-1); + else if (left > right) + return (1); + else + return (0); +} + +/** + * Search @p erom for the core serving as the bhnd host bridge. + * + * This function uses a heuristic valid on all known PCI/PCIe/PCMCIA-bridged + * bhnd(4) devices to determine the hostb core: + * + * - The core must have a Broadcom vendor ID. + * - The core devclass must match the bridge type. + * - The core must be the first device on the bus with the bridged device + * class. + * + * @param sc BHNDB device state. + * @param erom The device enumeration table parser to be used to fetch + * core info. + * @param[out] core If found, the matching core info. + * + * @retval 0 success + * @retval ENOENT not found + * @retval non-zero if an error occured fetching core info. + */ +static int +bhndb_find_hostb_core(struct bhndb_softc *sc, bhnd_erom_t *erom, + struct bhnd_core_info *core) +{ + struct bhnd_core_match md; + struct bhnd_core_info *cores; + u_int ncores; + int error; - /* Fetch the full set of bhnd-attached cores */ - if ((error = device_get_children(sc->bus_dev, &devs, &ndevs))) { - device_printf(sc->dev, "unable to get children\n"); + if ((error = bhnd_erom_get_core_table(erom, &cores, &ncores))) return (error); + + /* Set up a match descriptor for the required device class. */ + md = (struct bhnd_core_match) { + BHND_MATCH_CORE_CLASS(sc->bridge_class), + BHND_MATCH_CORE_UNIT(0) + }; + + /* Ensure the table is sorted by core index value, ascending; + * the host bridge must be the absolute first matching device on the + * bus. */ + qsort(cores, ncores, sizeof(*cores), compare_core_index); + + /* Find the hostb core */ + error = ENOENT; + for (u_int i = 0; i < ncores; i++) { + if (bhnd_core_matches(&cores[i], &md)) { + /* Found! */ + *core = cores[i]; + error = 0; + break; + } } - /* Find our host bridge device */ - hostb = BHNDB_FIND_HOSTB_DEVICE(dev, child); - if (hostb == NULL) { + /* Clean up */ + bhnd_erom_free_core_table(erom, cores); + + return (error); +} + +/** + * Identify the bridged device and perform final bridge resource configuration + * based on capabilities of the enumerated device. + * + * Any bridged resources allocated using the generic brige hardware + * configuration must be released prior to calling this function. + */ +static int +bhndb_init_full_config(struct bhndb_softc *sc, bhnd_erom_class_t *eromcls) +{ + struct bhnd_core_info *cores; + struct bhndb_resources *br; + const struct bhndb_hw_priority *hwprio; + bhnd_erom_t *erom; + const struct bhndb_hw *hw; + u_int ncores; + int error; + + erom = NULL; + cores = NULL; + br = NULL; + + /* Allocate EROM parser instance */ + erom = bhnd_erom_alloc(eromcls, &sc->chipid, sc->bus_dev, 0); + if (erom == NULL) { + device_printf(sc->dev, "failed to allocate device enumeration " + "table parser\n"); + return (ENXIO); + } + + /* Look for our host bridge core */ + if ((error = bhndb_find_hostb_core(sc, erom, &sc->bridge_core))) { device_printf(sc->dev, "no host bridge core found\n"); - error = ENODEV; + goto cleanup; + } else { + sc->have_br_core = true; + } + + /* Fetch the bridged device's core table */ + if ((error = bhnd_erom_get_core_table(erom, &cores, &ncores))) { + device_printf(sc->dev, "error fetching core table: %d\n", + error); goto cleanup; } /* Find our full register window configuration */ - if ((error = bhndb_find_hwspec(sc, devs, ndevs, &hw))) { + if ((error = bhndb_find_hwspec(sc, cores, ncores, &hw))) { device_printf(sc->dev, "unable to identify device, " - " using generic bridge resource definitions\n"); + " using generic bridge resource definitions\n"); error = 0; goto cleanup; } @@ -620,34 +835,59 @@ if (bootverbose || BHNDB_DEBUG(PRIO)) device_printf(sc->dev, "%s resource configuration\n", hw->name); - /* Release existing resource state */ - BHNDB_LOCK(sc); - bhndb_free_resources(sc->bus_res); - sc->bus_res = NULL; - BHNDB_UNLOCK(sc); - - /* Allocate new resource state */ - r = bhndb_alloc_resources(dev, sc->parent_dev, hw->cfg); - if (r == NULL) { - error = ENXIO; + /* Allocate new bridge resource state using the discovered hardware + * configuration */ + br = bhndb_alloc_resources(sc->dev, sc->parent_dev, hw->cfg); + if (br == NULL) { + device_printf(sc->dev, + "failed to allocate new resource state\n"); + error = ENOMEM; goto cleanup; } - /* Initialize our resource priority configuration */ - error = bhndb_initialize_region_cfg(sc, devs, ndevs, hw_prio_table, r); + /* Populate our resource priority configuration */ + hwprio = BHNDB_BUS_GET_HARDWARE_PRIO(sc->parent_dev, sc->dev); + error = bhndb_init_region_cfg(sc, erom, br, cores, ncores, hwprio); if (error) { - bhndb_free_resources(r); + device_printf(sc->dev, "failed to initialize resource " + "priority configuration: %d\n", error); goto cleanup; } - /* Update our bridge state */ - BHNDB_LOCK(sc); - sc->bus_res = r; - sc->hostb_dev = hostb; - BHNDB_UNLOCK(sc); + /* The EROM parser holds a reference to the resource state we're + * about to invalidate */ + bhnd_erom_free_core_table(erom, cores); + bhnd_erom_free(erom); + + cores = NULL; + erom = NULL; + + /* Replace existing resource state */ + bhndb_free_resources(sc->bus_res); + sc->bus_res = br; + + /* Pointer is now owned by sc->bus_res */ + br = NULL; + + /* Re-allocate host resources */ + if ((error = bhndb_alloc_host_resources(sc->bus_res))) { + device_printf(sc->dev, "failed to reallocate bridge host " + "resources: %d\n", error); + goto cleanup; + } + + return (0); cleanup: - free(devs, M_TEMP); + if (cores != NULL) + bhnd_erom_free_core_table(erom, cores); + + if (erom != NULL) + bhnd_erom_free(erom); + + if (br != NULL) + bhndb_free_resources(br); + return (error); } @@ -928,21 +1168,17 @@ /** - * Default implementation of BHNDB_IS_HW_DISABLED(). + * Default implementation of BHND_BUS_IS_HW_DISABLED(). */ static bool -bhndb_is_hw_disabled(device_t dev, device_t child) { +bhndb_is_hw_disabled(device_t dev, device_t child) +{ struct bhndb_softc *sc; + struct bhnd_core_info *bridge_core; struct bhnd_core_info core; sc = device_get_softc(dev); - /* Requestor must be attached to the bhnd bus */ - if (device_get_parent(child) != sc->bus_dev) { - return (BHND_BUS_IS_HW_DISABLED(device_get_parent(dev), child)); - } - - /* Fetch core info */ core = bhnd_get_core_info(child); /* Try to defer to the bhndb bus parent */ @@ -951,78 +1187,27 @@ /* Otherwise, we treat bridge-capable cores as unpopulated if they're * not the configured host bridge */ + bridge_core = bhndb_get_bridge_core(sc); if (BHND_DEVCLASS_SUPPORTS_HOSTB(bhnd_core_class(&core))) - return (BHNDB_FIND_HOSTB_DEVICE(dev, sc->bus_dev) != child); + return (!bhnd_cores_equal(&core, bridge_core)); - /* Otherwise, assume the core is populated */ + /* Assume the core is populated */ return (false); } -/* ascending core index comparison used by bhndb_find_hostb_device() */ -static int -compare_core_index(const void *lhs, const void *rhs) -{ - u_int left = bhnd_get_core_index(*(const device_t *) lhs); - u_int right = bhnd_get_core_index(*(const device_t *) rhs); - - if (left < right) - return (-1); - else if (left > right) - return (1); - else - return (0); -} - /** - * Default bhndb(4) implementation of BHND_BUS_FIND_HOSTB_DEVICE(). + * Default bhndb(4) implementation of BHNDB_GET_HOSTB_CORE(). * * This function uses a heuristic valid on all known PCI/PCIe/PCMCIA-bridged - * bhnd(4) devices to determine the hostb core: - * - * - The core must have a Broadcom vendor ID. - * - The core devclass must match the bridge type. - * - The core must be the first device on the bus with the bridged device - * class. - * - * @param dev The bhndb device - * @param child The requesting bhnd bus. + * bhnd(4) devices. */ -static device_t -bhndb_find_hostb_device(device_t dev, device_t child) +static int +bhndb_get_hostb_core(device_t dev, device_t child, struct bhnd_core_info *core) { - struct bhndb_softc *sc; - struct bhnd_device_match md; - device_t hostb_dev, *devlist; - int devcnt, error; - - sc = device_get_softc(dev); - - /* Set up a match descriptor for the required device class. */ - md = (struct bhnd_device_match) { - BHND_MATCH_CORE_CLASS(sc->bridge_class), - BHND_MATCH_CORE_UNIT(0) - }; - - /* Must be the absolute first matching device on the bus. */ - if ((error = device_get_children(child, &devlist, &devcnt))) - return (false); - - /* Sort by core index value, ascending */ - qsort(devlist, devcnt, sizeof(*devlist), compare_core_index); - - /* Find the hostb device */ - hostb_dev = NULL; - for (int i = 0; i < devcnt; i++) { - if (bhnd_device_matches(devlist[i], &md)) { - hostb_dev = devlist[i]; - break; - } - } - - /* Clean up */ - free(devlist, M_TEMP); + struct bhndb_softc *sc = device_get_softc(dev); - return (hostb_dev); + *core = *bhndb_get_bridge_core(sc); + return (0); } /** @@ -1681,7 +1866,14 @@ } /** - * Find the bridge resource to be used for I/O requests. + * Return a borrowed reference to a bridge resource allocation record capable + * of handling bus I/O requests of @p size at @p addr. + * + * This will either return a reference to an existing allocation + * record mapping the requested space, or will configure and return a free + * allocation record. + * + * Will panic if a usable record cannot be found. * * @param sc Bridge driver state. * @param addr The I/O target address. @@ -1961,8 +2153,7 @@ /* BHNDB interface */ DEVMETHOD(bhndb_get_chipid, bhndb_get_chipid), - DEVMETHOD(bhndb_init_full_config, bhndb_generic_init_full_config), - DEVMETHOD(bhndb_find_hostb_device, bhndb_find_hostb_device), + DEVMETHOD(bhndb_get_hostb_core, bhndb_get_hostb_core), DEVMETHOD(bhndb_suspend_resource, bhndb_suspend_resource), DEVMETHOD(bhndb_resume_resource, bhndb_resume_resource), Index: head/sys/dev/bhnd/bhndb/bhndb_bus_if.m =================================================================== --- head/sys/dev/bhnd/bhndb/bhndb_bus_if.m +++ head/sys/dev/bhnd/bhndb/bhndb_bus_if.m @@ -1,5 +1,5 @@ #- -# Copyright (c) 2015 Landon Fuller +# Copyright (c) 2015-2016 Landon Fuller # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -59,6 +59,12 @@ { panic("bhndb_get_hardware_table unimplemented"); } + + static const struct bhndb_hw_priority * + bhndb_null_get_hardware_prio(device_t dev, device_t child) + { + panic("bhndb_get_hardware_prio unimplemented"); + } static bool bhndb_null_is_core_disabled(device_t dev, device_t child, @@ -109,6 +115,18 @@ } DEFAULT bhndb_null_get_hardware_table; /** + * Return the hardware priority table to be used when allocating bridge + * resources. + * + * @param dev The parent device. + * @param child The attached bhndb device. + */ +METHOD const struct bhndb_hw_priority * get_hardware_prio { + device_t dev; + device_t child; +} DEFAULT bhndb_null_get_hardware_prio; + +/** * Return true if the hardware required by @p core is unpopulated or * otherwise unusable. * Index: head/sys/dev/bhnd/bhndb/bhndb_if.m =================================================================== --- head/sys/dev/bhnd/bhndb/bhndb_if.m +++ head/sys/dev/bhnd/bhndb/bhndb_if.m @@ -61,18 +61,12 @@ { panic("bhndb_populate_board_info unimplemented"); } - - static int - bhndb_null_init_full_config(device_t dev, device_t child, - const struct bhndb_hw_priority *priority_table) - { - panic("bhndb_init_full_config unimplemented"); - } - static device_t - bhndb_null_find_hostb_device(device_t dev, device_t child) + static int + bhndb_null_get_hostb_core(device_t dev, device_t child, + struct bhnd_core_info *core) { - panic("bhndb_find_hostb_device unimplemented"); + panic("bhndb_get_hostb_core unimplemented"); } static void @@ -124,39 +118,23 @@ } DEFAULT bhndb_null_populate_board_info; /** - * Perform final bridge hardware configuration after @p child has fully - * enumerated its children. + * Get the host bridge core info for the attached bhnd bus. * - * This must be called by any bhndb-attached bus device; this allows the - * bridge to perform final configuration based on the hardware information - * enumerated by the child bus. - * - * When calling this method: - * - Any bus resources previously allocated by @p child must be deallocated. - * - The @p child bus must have performed initial enumeration -- but not - * probe or attachment -- of its children. + * @param dev The bridge device. + * @param child The bhnd bus device attached to @p dev. + * @param[out] core Will be populated with the host bridge core info, if + * found. * - * @param dev The bridge device. - * @param child The bhnd bus device attached to @p dev. - * @param hw_priority The hardware priority table to be used when determining - * the bridge resource allocation strategy. - */ -METHOD int init_full_config { - device_t dev; - device_t child; - const struct bhndb_hw_priority *priority_table; -} DEFAULT bhndb_null_init_full_config; - -/** - * Locate the active host bridge core for the attached bhnd bus. - * - * @param dev The bridge device. - * @param child The bhnd bus device attached to @p dev. + * @retval 0 success + * @retval ENOENT No host bridge core found. + * @retval non-zero If locating the host bridge core otherwise fails, a + * regular UNIX error code should be returned. */ -METHOD device_t find_hostb_device { +METHOD int get_hostb_core { device_t dev; device_t child; -} DEFAULT bhndb_null_find_hostb_device; + struct bhnd_core_info *core; +} DEFAULT bhndb_null_get_hostb_core; /** * Mark a resource as 'suspended', gauranteeing to the bridge that no 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 @@ -62,6 +62,8 @@ #include "bhndb_pcivar.h" #include "bhndb_private.h" +static int bhndb_pci_add_children(struct bhndb_pci_softc *sc); + static int bhndb_enable_pci_clocks(struct bhndb_pci_softc *sc); static int bhndb_disable_pci_clocks(struct bhndb_pci_softc *sc); @@ -110,93 +112,119 @@ sc = device_get_softc(dev); sc->dev = dev; sc->parent = device_get_parent(dev); + sc->set_regwin = bhndb_pci_compat_setregwin; - /* Enable PCI bus mastering */ - pci_enable_busmaster(sc->parent); - - /* Determine our bridge device class */ - sc->pci_devclass = BHND_DEVCLASS_PCI; if (pci_find_cap(sc->parent, PCIY_EXPRESS, ®) == 0) sc->pci_devclass = BHND_DEVCLASS_PCIE; + else + sc->pci_devclass = BHND_DEVCLASS_PCI; - /* Enable clocks (if supported by this hardware) */ + /* Enable clocks (if required by this hardware) */ if ((error = bhndb_enable_pci_clocks(sc))) - return (error); - - /* Use siba(4)-compatible regwin handling until we know - * what kind of bus is attached */ - sc->set_regwin = bhndb_pci_compat_setregwin; + goto cleanup; - /* Perform full bridge attach. This should call back into our - * bhndb_pci_init_full_config() implementation once the bridged - * bhnd(4) bus has been enumerated, but before any devices have been - * probed or attached. */ + /* Perform bridge attach, fully initializing the bridge + * configuration. */ if ((error = bhndb_attach(dev, sc->pci_devclass))) - return (error); + goto cleanup; - /* If supported, switch to the faster regwin handling */ + /* If supported, switch to faster regwin handling */ if (sc->bhndb.chipid.chip_type != BHND_CHIPTYPE_SIBA) { atomic_store_rel_ptr((volatile void *) &sc->set_regwin, (uintptr_t) &bhndb_pci_fast_setregwin); } + /* Enable PCI bus mastering */ + pci_enable_busmaster(sc->parent); + + /* Fix-up power on defaults for SROM-less devices. */ + bhndb_init_sromless_pci_config(sc); + + /* Add any additional child devices */ + if ((error = bhndb_pci_add_children(sc))) + goto cleanup; + + /* Probe and attach our children */ + if ((error = bus_generic_attach(dev))) + goto cleanup; + return (0); + +cleanup: + device_delete_children(dev); + bhndb_disable_pci_clocks(sc); + pci_disable_busmaster(sc->parent); + + return (error); } static int -bhndb_pci_init_full_config(device_t dev, device_t child, - const struct bhndb_hw_priority *hw_prio_table) +bhndb_pci_detach(device_t dev) { struct bhndb_pci_softc *sc; - device_t nv_dev; - bus_size_t nv_sz; int error; sc = device_get_softc(dev); - /* Let our parent perform standard initialization first */ - if ((error = bhndb_generic_init_full_config(dev, child, hw_prio_table))) + /* Attempt to detach our children */ + if ((error = bus_generic_detach(dev))) return (error); - /* Fix-up power on defaults for SROM-less devices. */ - bhndb_init_sromless_pci_config(sc); + /* Perform generic bridge detach */ + if ((error = bhndb_generic_detach(dev))) + return (error); + + /* Disable clocks (if required by this hardware) */ + if ((error = bhndb_disable_pci_clocks(sc))) + return (error); + + /* Disable PCI bus mastering */ + pci_disable_busmaster(sc->parent); + + return (0); +} - /* If SPROM is mapped directly into BAR0, add NVRAM device. */ +static int +bhndb_pci_add_children(struct bhndb_pci_softc *sc) +{ + bus_size_t nv_sz; + int error; + + /** + * If SPROM is mapped directly into BAR0, add child NVRAM + * device. + */ nv_sz = bhndb_pci_sprom_size(sc); if (nv_sz > 0) { struct bhndb_devinfo *dinfo; - const char *dname; + device_t child; if (bootverbose) { - device_printf(dev, "found SPROM (%u bytes)\n", - (unsigned int) nv_sz); + device_printf(sc->dev, "found SPROM (%ju bytes)\n", + (uintmax_t)nv_sz); } - /* Add sprom device */ - dname = "bhnd_nvram"; - if ((nv_dev = BUS_ADD_CHILD(dev, 0, dname, -1)) == NULL) { - device_printf(dev, "failed to add sprom device\n"); + /* Add sprom device, ordered early enough to be available + * before the bridged bhnd(4) bus is attached. */ + child = BUS_ADD_CHILD(sc->dev, + BHND_PROBE_ROOT + BHND_PROBE_ORDER_EARLY, "bhnd_nvram", -1); + if (child == NULL) { + device_printf(sc->dev, "failed to add sprom device\n"); return (ENXIO); } /* Initialize device address space and resource covering the * BAR0 SPROM shadow. */ - dinfo = device_get_ivars(nv_dev); + dinfo = device_get_ivars(child); dinfo->addrspace = BHNDB_ADDRSPACE_NATIVE; - error = bus_set_resource(nv_dev, SYS_RES_MEMORY, 0, - bhndb_pci_sprom_addr(sc), nv_sz); + error = bus_set_resource(child, SYS_RES_MEMORY, 0, + bhndb_pci_sprom_addr(sc), nv_sz); if (error) { - device_printf(dev, + device_printf(sc->dev, "failed to register sprom resources\n"); return (error); } - - /* Attach the device */ - if ((error = device_probe_and_attach(nv_dev))) { - device_printf(dev, "sprom attach failed\n"); - return (error); - } } return (0); @@ -298,18 +326,27 @@ 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; u_int pci_cidx, sprom_cidx; uint16_t val; + int error; bres = sc->bhndb.bus_res; cfg = bres->cfg; - if (bhnd_get_vendor(sc->bhndb.hostb_dev) != BHND_MFGID_BCM) + /* 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"); + return; + } + + if (hostb_core.vendor != BHND_MFGID_BCM) return; - switch (bhnd_get_device(sc->bhndb.hostb_dev)) { + switch (hostb_core.device) { case BHND_COREID_PCI: srom_offset = BHND_PCI_SRSH_PI_OFFSET; break; @@ -342,7 +379,7 @@ /* If it doesn't match host bridge's core index, update the index * value */ - pci_cidx = bhnd_get_core_index(sc->bhndb.hostb_dev); + pci_cidx = hostb_core.core_idx; if (sprom_cidx != pci_cidx) { val &= ~BHND_PCI_SRSH_PI_MASK; val |= (pci_cidx << BHND_PCI_SRSH_PI_SHIFT); @@ -384,28 +421,6 @@ } static int -bhndb_pci_detach(device_t dev) -{ - struct bhndb_pci_softc *sc; - int error; - - sc = device_get_softc(dev); - - /* Disable clocks (if supported by this hardware) */ - if ((error = bhndb_disable_pci_clocks(sc))) - return (error); - - /* Perform detach */ - if ((error = bhndb_generic_detach(dev))) - return (error); - - /* Disable PCI bus mastering */ - pci_disable_busmaster(sc->parent); - - return (0); -} - -static int bhndb_pci_set_window_addr(device_t dev, const struct bhndb_regwin *rw, bhnd_addr_t addr) { @@ -678,7 +693,6 @@ DEVMETHOD(bhnd_bus_pwrctl_ungate_clock, bhndb_pci_pwrctl_ungate_clock), /* BHNDB interface */ - DEVMETHOD(bhndb_init_full_config, bhndb_pci_init_full_config), DEVMETHOD(bhndb_set_window_addr, bhndb_pci_set_window_addr), DEVMETHOD(bhndb_populate_board_info, bhndb_pci_populate_board_info), 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 @@ -63,6 +63,9 @@ device_t parent_dev, const struct bhndb_hwcfg *cfg); +int bhndb_alloc_host_resources( + struct bhndb_resources *br); + void bhndb_free_resources( struct bhndb_resources *br); @@ -126,13 +129,13 @@ bhnd_port_type port_type, u_int port, u_int region, bus_size_t min_size); -bool bhndb_regwin_matches_device( +bool bhndb_regwin_match_core( const struct bhndb_regwin *regw, - device_t dev); + struct bhnd_core_info *core); -const struct bhndb_hw_priority *bhndb_hw_priority_find_device( +const struct bhndb_hw_priority *bhndb_hw_priority_find_core( const struct bhndb_hw_priority *table, - device_t device); + struct bhnd_core_info *core); /** @@ -175,8 +178,9 @@ const struct bhndb_hwcfg *cfg; /**< hardware configuration */ device_t parent_dev; /**< parent device */ - struct resource_spec *res_spec; /**< parent bus resource specs */ - struct resource **res; /**< parent bus resources */ + struct resource_spec *res_spec; /**< parent bus resource specs, or NULL if not allocated */ + struct resource **res; /**< parent bus resources, or NULL if not allocated */ + bool res_avail; /**< if parent bus resources have been allocated */ struct rman ht_mem_rman; /**< host memory manager */ struct rman br_mem_rman; /**< bridged memory manager */ 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 @@ -196,6 +196,8 @@ bhndb_find_resource_range(struct bhndb_resources *br, rman_res_t start, rman_res_t count) { + KASSERT(br->res_avail, ("no host resources allocated")); + for (u_int i = 0; br->res_spec[i].type != -1; i++) { struct resource *r = br->res[i]; @@ -230,6 +232,8 @@ { const struct resource_spec *rspecs; + KASSERT(br->res_avail, ("no host resources allocated")); + rspecs = br->cfg->resource_specs; for (u_int i = 0; rspecs[i].type != -1; i++) { if (win->res.type != rspecs[i].type) @@ -250,11 +254,11 @@ } /** - * Allocate and initialize a new resource state structure, allocating - * bus resources from @p parent_dev according to @p cfg. + * Allocate and initialize a new resource state structure. * * @param dev The bridge device. - * @param parent_dev The parent device from which resources will be allocated. + * @param parent_dev The parent device from which host resources should be + * allocated. * @param cfg The hardware configuration to be used. */ struct bhndb_resources * @@ -264,13 +268,10 @@ struct bhndb_resources *r; const struct bhndb_regwin *win; bus_size_t last_window_size; - size_t res_num; 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; @@ -315,62 +316,6 @@ 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++) - res_num++; - - /* Allocate space for a non-const copy of our resource_spec - * table; this will be updated with the RIDs assigned by - * bus_alloc_resources. */ - r->res_spec = malloc(sizeof(r->res_spec[0]) * (res_num + 1), M_BHND, - M_NOWAIT); - if (r->res_spec == NULL) - goto failed; - - /* Initialize and terminate the table */ - for (size_t i = 0; i < res_num; i++) - r->res_spec[i] = cfg->resource_specs[i]; - - r->res_spec[res_num].type = -1; - - /* Allocate space for our resource references */ - r->res = malloc(sizeof(r->res[0]) * res_num, M_BHND, M_NOWAIT); - if (r->res == NULL) - goto failed; - - /* Allocate resources */ - error = bus_alloc_resources(r->parent_dev, r->res_spec, r->res); - if (error) { - device_printf(r->dev, - "could not allocate bridge resources on %s: %d\n", - device_get_nameunit(r->parent_dev), error); - goto failed; - } else { - 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 bitstring. */ r->dwa_count = bhndb_regwin_count(cfg->register_windows, @@ -432,16 +377,109 @@ dwa->target = 0x0; LIST_INIT(&dwa->refs); + rnid++; + } + + return (r); + +failed: + if (free_ht_mem) + rman_fini(&r->ht_mem_rman); + + if (free_br_mem) + rman_fini(&r->br_mem_rman); + + if (r->dw_alloc != NULL) + free(r->dw_alloc, M_BHND); + + if (r->dwa_freelist != NULL) + free(r->dwa_freelist, M_BHND); + + free(r, M_BHND); + + return (NULL); +} + +/** + * Allocate host resources required by @p br, and initialize + * internal BHNDB_ADDRSPACE_NATIVE resource manager state. + * + * @param br Resource state. + */ +int +bhndb_alloc_host_resources(struct bhndb_resources *br) +{ + size_t res_num; + int error; + + KASSERT(!br->res_avail, ("host resources already allocated")); + + /* Determine our bridge resource count from the hardware config. */ + res_num = 0; + for (size_t i = 0; br->cfg->resource_specs[i].type != -1; i++) + res_num++; + + /* Allocate space for a non-const copy of our resource_spec + * table; this will be updated with the RIDs assigned by + * bus_alloc_resources. */ + br->res_spec = malloc(sizeof(br->res_spec[0]) * (res_num + 1), M_BHND, + M_NOWAIT); + if (br->res_spec == NULL) { + error = ENOMEM; + goto failed; + } + + /* Initialize and terminate the table */ + for (size_t i = 0; i < res_num; i++) + br->res_spec[i] = br->cfg->resource_specs[i]; + + br->res_spec[res_num].type = -1; + + /* Allocate space for our resource references */ + br->res = malloc(sizeof(br->res[0]) * res_num, M_BHND, M_NOWAIT); + if (br->res == NULL) { + error = ENOMEM; + goto failed; + } + + /* Allocate host resources */ + error = bus_alloc_resources(br->parent_dev, br->res_spec, br->res); + if (error) { + device_printf(br->dev, + "could not allocate bridge resources on %s: %d\n", + device_get_nameunit(br->parent_dev), error); + goto failed; + } else { + br->res_avail = true; + } + + /* Populate (and validate) parent resource references for all + * dynamic windows */ + for (size_t i = 0; i < br->dwa_count; i++) { + struct bhndb_dw_alloc *dwa; + const struct bhndb_regwin *win; + + dwa = &br->dw_alloc[i]; + win = dwa->win; /* Find and validate corresponding resource. */ - dwa->parent_res = bhndb_find_regwin_resource(r, win); - if (dwa->parent_res == NULL) + dwa->parent_res = bhndb_find_regwin_resource(br, win); + if (dwa->parent_res == NULL) { + device_printf(br->dev, "no host resource found for %u " + "register window with offset %#jx and " + "size %#jx\n", + win->win_type, + (uintmax_t)win->win_offset, + (uintmax_t)win->win_size); + + error = ENXIO; goto failed; + } if (rman_get_size(dwa->parent_res) < win->win_offset + win->win_size) { - device_printf(r->dev, "resource %d too small for " + device_printf(br->dev, "resource %d too small for " "register window with offset %llx and size %llx\n", rman_get_rid(dwa->parent_res), (unsigned long long) win->win_offset, @@ -450,37 +488,41 @@ error = EINVAL; goto failed; } + } - rnid++; + /* Add allocated memory resources to our host memory resource manager */ + for (u_int i = 0; br->res_spec[i].type != -1; i++) { + struct resource *res; + + /* skip non-memory resources */ + if (br->res_spec[i].type != SYS_RES_MEMORY) + continue; + + /* add host resource to set of managed regions */ + res = br->res[i]; + error = rman_manage_region(&br->ht_mem_rman, + rman_get_start(res), rman_get_end(res)); + if (error) { + device_printf(br->dev, + "could not register host memory region with " + "ht_mem_rman: %d\n", error); + goto failed; + } } - return (r); + return (0); failed: - if (free_parent_res) - bus_release_resources(r->parent_dev, r->res_spec, r->res); + if (br->res_avail) + bus_release_resources(br->parent_dev, br->res_spec, br->res); - if (free_ht_mem) - rman_fini(&r->ht_mem_rman); - - if (free_br_mem) - rman_fini(&r->br_mem_rman); + if (br->res != NULL) + free(br->res, M_BHND); - if (r->res != NULL) - free(r->res, M_BHND); + if (br->res_spec != NULL) + free(br->res_spec, M_BHND); - if (r->res_spec != NULL) - free(r->res_spec, M_BHND); - - if (r->dw_alloc != NULL) - free(r->dw_alloc, M_BHND); - - if (r->dwa_freelist != NULL) - free(r->dwa_freelist, M_BHND); - - free (r, M_BHND); - - return (NULL); + return (error); } /** @@ -510,7 +552,8 @@ } /* Release resources allocated through our parent. */ - bus_release_resources(br->parent_dev, br->res_spec, br->res); + if (br->res_avail) + bus_release_resources(br->parent_dev, br->res_spec, br->res); /* Clean up resource reservations */ for (size_t i = 0; i < br->dwa_count; i++) { @@ -533,8 +576,12 @@ rman_fini(&br->br_mem_rman); /* Free backing resource state structures */ - free(br->res, M_BHND); - free(br->res_spec, M_BHND); + if (br->res != NULL) + free(br->res, M_BHND); + + if (br->res_spec != NULL) + free(br->res_spec, M_BHND); + free(br->dw_alloc, M_BHND); free(br->dwa_freelist, M_BHND); } @@ -988,33 +1035,27 @@ } /** - * Return true if @p regw defines a static port register window, and - * the mapped port is actually defined on @p dev. + * Return true if @p regw defines a BHNDB_REGWIN_T_CORE register window + * that matches against @p core. * * @param regw A register window to match against. - * @param dev A bhnd(4) bus device. + * @param core The bhnd(4) core info to match against @p regw. */ bool -bhndb_regwin_matches_device(const struct bhndb_regwin *regw, device_t dev) +bhndb_regwin_match_core(const struct bhndb_regwin *regw, + struct bhnd_core_info *core) { /* Only core windows are supported */ if (regw->win_type != BHNDB_REGWIN_T_CORE) return (false); /* Device class must match */ - if (bhnd_get_class(dev) != regw->d.core.class) + if (bhnd_core_class(core) != regw->d.core.class) return (false); /* Device unit must match */ - if (bhnd_get_core_unit(dev) != regw->d.core.unit) - return (false); - - /* The regwin port/region must be defined. */ - if (!bhnd_is_region_valid(dev, regw->d.core.port_type, regw->d.core.port, - regw->d.core.region)) - { + if (core->unit != regw->d.core.unit) return (false); - } /* Matches */ return (true); @@ -1022,22 +1063,19 @@ /** * Search for a core resource priority descriptor in @p table that matches - * @p device. + * @p core. * * @param table The table to search. - * @param device A bhnd(4) bus device. + * @param core The core to match against @p table. */ const struct bhndb_hw_priority * -bhndb_hw_priority_find_device(const struct bhndb_hw_priority *table, - device_t device) +bhndb_hw_priority_find_core(const struct bhndb_hw_priority *table, + struct bhnd_core_info *core) { const struct bhndb_hw_priority *hp; - struct bhnd_core_info ci; - - ci = bhnd_get_core_info(device); for (hp = table; hp->ports != NULL; hp++) { - if (bhnd_core_matches(&ci, &hp->match)) + if (bhnd_core_matches(core, &hp->match)) return (hp); } Index: head/sys/dev/bhnd/bhndb/bhndbvar.h =================================================================== --- head/sys/dev/bhnd/bhndb/bhndbvar.h +++ head/sys/dev/bhnd/bhndb/bhndbvar.h @@ -89,12 +89,12 @@ device_t dev; /**< bridge device */ struct bhnd_chipid chipid; /**< chip identification */ bhnd_devclass_t bridge_class; /**< bridge core type */ + struct bhnd_core_info bridge_core; /**< bridge core. not populated until + * full bridge config is initialized */ + bool have_br_core; /**< false if not yet available */ device_t parent_dev; /**< parent device */ device_t bus_dev; /**< child bhnd(4) bus */ - device_t hostb_dev; /**< child host bridge device, or NULL - if the @p bus_dev has not yet - called BHNDB_INIT_FULL_CONFIG() */ struct mtx sc_mtx; /**< resource lock. */ struct bhndb_resources *bus_res; /**< bus resource state */ Index: head/sys/dev/bhnd/cores/chipc/chipcreg.h =================================================================== --- head/sys/dev/bhnd/cores/chipc/chipcreg.h +++ head/sys/dev/bhnd/cores/chipc/chipcreg.h @@ -25,12 +25,6 @@ #ifndef _BHND_CORES_CHIPC_CHIPCREG_H_ #define _BHND_CORES_CHIPC_CHIPCREG_H_ -#define CHIPC_CHIPID_SIZE 0x100 /**< size of the register block - containing the chip - identification registers - required during bus - enumeration */ - /** Evaluates to true if the given ChipCommon core revision supports * the CHIPC_CORECTRL register */ #define CHIPC_HWREV_HAS_CORECTRL(hwrev) ((hwrev) >= 1) Index: head/sys/dev/bhnd/siba/siba.c =================================================================== --- head/sys/dev/bhnd/siba/siba.c +++ head/sys/dev/bhnd/siba/siba.c @@ -44,6 +44,12 @@ #include "sibareg.h" #include "sibavar.h" +static bhnd_erom_class_t * +siba_get_erom_class(driver_t *driver) +{ + return (&siba_erom_parser); +} + int siba_probe(device_t dev) { @@ -51,84 +57,29 @@ return (BUS_PROBE_DEFAULT); } +/** + * Default siba(4) bus driver implementation of DEVICE_ATTACH(). + * + * This implementation initializes internal siba(4) state and performs + * bus enumeration, and must be called by subclassing drivers in + * DEVICE_ATTACH() before any other bus methods. + */ int siba_attach(device_t dev) { - struct siba_devinfo *dinfo; struct siba_softc *sc; - device_t *devs; - int ndevs; int error; - + sc = device_get_softc(dev); sc->dev = dev; - /* Fetch references to the siba SIBA_CFG* blocks for all - * registered devices */ - if ((error = device_get_children(dev, &devs, &ndevs))) + /* Enumerate children */ + if ((error = siba_add_children(dev))) { + device_delete_children(dev); return (error); - - for (int i = 0; i < ndevs; i++) { - struct siba_addrspace *addrspace; - - dinfo = device_get_ivars(devs[i]); - - KASSERT(!device_is_suspended(devs[i]), - ("siba(4) stateful suspend handling requires that devices " - "not be suspended before siba_attach()")); - - /* Fetch the core register address space */ - addrspace = siba_find_addrspace(dinfo, BHND_PORT_DEVICE, 0, 0); - if (addrspace == NULL) { - device_printf(dev, - "missing device registers for core %d\n", i); - error = ENXIO; - goto cleanup; - } - - /* - * Map the per-core configuration blocks - */ - KASSERT(dinfo->core_id.num_cfg_blocks <= SIBA_MAX_CFG, - ("config block count %u out of range", - dinfo->core_id.num_cfg_blocks)); - - for (u_int cfgidx = 0; cfgidx < dinfo->core_id.num_cfg_blocks; - cfgidx++) - { - rman_res_t r_start, r_count, r_end; - - /* 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 -= cfgidx * SIBA_CFG_SIZE; - - r_count = SIBA_CFG_SIZE; - r_end = r_start + r_count - 1; - - /* Allocate the config resource */ - dinfo->cfg_rid[cfgidx] = 0; - dinfo->cfg[cfgidx] = BHND_BUS_ALLOC_RESOURCE(dev, dev, - SYS_RES_MEMORY, &dinfo->cfg_rid[cfgidx], r_start, - r_end, r_count, RF_ACTIVE); - - if (dinfo->cfg[cfgidx] == NULL) { - device_printf(dev, "failed allocating CFG_%u for " - "core %d\n", cfgidx, i); - error = ENXIO; - goto cleanup; - } - } } -cleanup: - free(devs, M_BHND); - if (error) - return (error); - - /* Delegate remainder to standard bhnd method implementation */ - return (bhnd_generic_attach(dev)); + return (0); } int @@ -213,15 +164,6 @@ return (&dinfo->resources); } -static device_t -siba_find_hostb_device(device_t dev) -{ - struct siba_softc *sc = device_get_softc(dev); - - /* This is set (or not) by the concrete siba driver subclass. */ - return (sc->hostb_dev); -} - static int siba_reset_core(device_t dev, device_t child, uint16_t flags) { @@ -492,6 +434,64 @@ return (0); } +/** + * Map per-core configuration blocks for @p dinfo. + * + * @param dev The siba bus device. + * @param dinfo The device info instance on which to map all per-core + * configuration blocks. + */ +static int +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; + + num_cfg = dinfo->core_id.num_cfg_blocks; + if (num_cfg > SIBA_MAX_CFG) { + device_printf(dev, "config block count %hhu out of range\n", + num_cfg); + return (ENXIO); + } + + /* Fetch the core register address space */ + addrspace = siba_find_addrspace(dinfo, BHND_PORT_DEVICE, 0, 0); + if (addrspace == NULL) { + device_printf(dev, "missing device registers\n"); + return (ENXIO); + } + + /* + * 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; + + r_count = SIBA_CFG_SIZE; + r_end = r_start + r_count - 1; + + /* Allocate the config resource */ + dinfo->cfg_rid[i] = SIBA_CFG_RID(dinfo, i); + dinfo->cfg[i] = BHND_BUS_ALLOC_RESOURCE(dev, dev, + SYS_RES_MEMORY, &dinfo->cfg_rid[i], r_start, r_end, + r_count, RF_ACTIVE); + + if (dinfo->cfg[i] == NULL) { + device_printf(dev, "failed to allocate SIBA_CFG%hhu\n", + i); + return (ENXIO); + } + } + + return (0); +} + + static struct bhnd_devinfo * siba_alloc_bhnd_dinfo(device_t dev) { @@ -510,73 +510,22 @@ * the bus. * * @param dev The siba bus device. - * @param chipid The chip identifier, if the device does not provide a - * ChipCommon core. Should o NULL otherwise. */ int -siba_add_children(device_t dev, const struct bhnd_chipid *chipid) +siba_add_children(device_t dev) { - struct bhnd_chipid ccid; - struct bhnd_core_info *cores; - struct siba_devinfo *dinfo; - struct bhnd_resource *r; - int rid; - int error; + const struct bhnd_chipid *chipid; + struct bhnd_core_info *cores; + struct siba_devinfo *dinfo; + struct bhnd_resource *r; + int rid; + int error; dinfo = NULL; cores = NULL; r = NULL; - - /* - * Try to determine the number of device cores via the ChipCommon - * identification registers. - * - * A small number of very early devices do not include a ChipCommon - * core, in which case our caller must supply the chip identification - * information via a non-NULL chipid parameter. - */ - if (chipid == NULL) { - uint32_t idhigh, ccreg; - uint16_t vendor, device; - uint8_t ccrev; - /* Map the first core's register block. If the ChipCommon core - * exists, it will always be the first core. */ - rid = 0; - r = bhnd_alloc_resource(dev, SYS_RES_MEMORY, &rid, - SIBA_CORE_ADDR(0), SIBA_CORE_SIZE, - SIBA_CORE_ADDR(0) + SIBA_CORE_SIZE - 1, - RF_ACTIVE); - - /* Identify the core */ - idhigh = bhnd_bus_read_4(r, SB0_REG_ABS(SIBA_CFG0_IDHIGH)); - vendor = SIBA_REG_GET(idhigh, IDH_VENDOR); - device = SIBA_REG_GET(idhigh, IDH_DEVICE); - ccrev = SIBA_IDH_CORE_REV(idhigh); - - if (vendor != OCP_VENDOR_BCM || device != BHND_COREID_CC) { - device_printf(dev, - "cannot identify device: no chipcommon core " - "found\n"); - error = ENXIO; - goto cleanup; - } - - /* Identify the chipset */ - ccreg = bhnd_bus_read_4(r, CHIPC_ID); - ccid = bhnd_parse_chipid(ccreg, SIBA_ENUM_ADDR); - - /* Fix up the core count */ - error = bhnd_chipid_fixed_ncores(&ccid, ccrev, &ccid.ncores); - if (error) { - device_printf(dev, "unable to determine core count for " - "chipset 0x%hx\n", ccid.chip_id); - goto cleanup; - } - - chipid = &ccid; - bhnd_release_resource(dev, SYS_RES_MEMORY, rid, r); - } + chipid = BHND_BUS_GET_CHIPID(dev, dev); /* Allocate our temporary core table and enumerate all cores */ cores = malloc(sizeof(*cores) * chipid->ncores, M_BHND, M_NOWAIT); @@ -636,14 +585,19 @@ if ((error = siba_register_addrspaces(dev, dinfo, r))) goto cleanup; + /* Release our resource covering the register blocks + * we're about to map */ + bhnd_release_resource(dev, SYS_RES_MEMORY, rid, r); + r = NULL; + + /* Map the core's config blocks */ + if ((error = siba_map_cfg_resources(dev, dinfo))) + goto cleanup; + /* If pins are floating or the hardware is otherwise * unpopulated, the device shouldn't be used. */ if (bhnd_is_hw_disabled(child)) device_disable(child); - - /* Release our resource */ - bhnd_release_resource(dev, SYS_RES_MEMORY, rid, r); - r = NULL; /* Issue bus callback for fully initialized child. */ BHND_BUS_CHILD_ADDED(dev, child); @@ -673,7 +627,7 @@ DEVMETHOD(bus_get_resource_list, siba_get_resource_list), /* BHND interface */ - DEVMETHOD(bhnd_bus_find_hostb_device, siba_find_hostb_device), + DEVMETHOD(bhnd_bus_get_erom_class, siba_get_erom_class), DEVMETHOD(bhnd_bus_alloc_devinfo, siba_alloc_bhnd_dinfo), DEVMETHOD(bhnd_bus_free_devinfo, siba_free_bhnd_dinfo), DEVMETHOD(bhnd_bus_reset_core, siba_reset_core), Index: head/sys/dev/bhnd/siba/siba_bhndb.c =================================================================== --- head/sys/dev/bhnd/siba/siba_bhndb.c +++ head/sys/dev/bhnd/siba/siba_bhndb.c @@ -101,35 +101,28 @@ static int siba_bhndb_attach(device_t dev) { - struct siba_softc *sc; - const struct bhnd_chipid *chipid; - int error; + struct siba_softc *sc; + int error; sc = device_get_softc(dev); - /* Enumerate our children. */ - chipid = BHNDB_GET_CHIPID(device_get_parent(dev), dev); - if ((error = siba_add_children(dev, chipid))) - return (error); - - /* Initialize full bridge configuration */ - error = BHNDB_INIT_FULL_CONFIG(device_get_parent(dev), dev, - bhndb_siba_priority_table); - if (error) - return (error); - - /* Ask our parent bridge to find the corresponding bridge core */ - sc->hostb_dev = BHNDB_FIND_HOSTB_DEVICE(device_get_parent(dev), dev); - - /* Call our superclass' implementation */ + /* Perform initial attach and enumerate our children. */ if ((error = siba_attach(dev))) - return (error); + goto failed; - /* Apply attach/resume work-arounds */ + /* Apply attach/resume workarounds before any child drivers attach */ if ((error = siba_bhndb_wars_hwup(sc))) - return (error); + goto failed; + + /* Delegate remainder to standard bhnd method implementation */ + if ((error = bhnd_generic_attach(dev))) + goto failed; return (0); + +failed: + device_delete_children(dev); + return (error); } static int @@ -222,11 +215,15 @@ siba_bhndb_wars_pcie_clear_d11_timeout(struct siba_softc *sc) { struct siba_devinfo *dinfo; + device_t hostb_dev; device_t d11; uint32_t imcfg; /* Only applies when bridged by PCIe */ - if (bhnd_get_class(sc->hostb_dev) != BHND_DEVCLASS_PCIE) + if ((hostb_dev = bhnd_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 */ @@ -256,10 +253,14 @@ static int siba_bhndb_wars_hwup(struct siba_softc *sc) { + device_t hostb_dev; uint32_t quirks; int error; - quirks = bhnd_device_quirks(sc->hostb_dev, bridge_devs, + if ((hostb_dev = bhnd_find_hostb_device(sc->dev)) == NULL) + return (ENXIO); + + quirks = bhnd_device_quirks(hostb_dev, bridge_devs, sizeof(bridge_devs[0])); if (quirks & SIBA_QUIRK_PCIE_D11_SB_TIMEOUT) { Index: head/sys/dev/bhnd/siba/siba_erom.c =================================================================== --- head/sys/dev/bhnd/siba/siba_erom.c +++ head/sys/dev/bhnd/siba/siba_erom.c @@ -46,117 +46,110 @@ #include "sibavar.h" struct siba_erom; +struct siba_erom_io; -static int siba_erom_init_static(bhnd_erom_t *erom, bus_space_tag_t bst, - bus_space_handle_t bsh); -static void siba_erom_fini(bhnd_erom_t *erom); - -static uint32_t siba_erom_read_4(struct siba_erom *sc, u_int core_idx, - bus_size_t offset); -static int siba_erom_read_chipid(struct siba_erom *sc, - bus_addr_t enum_addr, struct bhnd_chipid *cid); -struct siba_erom { - struct bhnd_erom obj; +static int siba_eio_init(struct siba_erom_io *io, + device_t parent, struct bhnd_resource *res, + int rid, bus_size_t offset, u_int ncores); + +static int siba_eio_init_static(struct siba_erom_io *io, + bus_space_tag_t bst, bus_space_handle_t bsh, + bus_size_t offset, u_int ncores); + +static uint32_t siba_eio_read_4(struct siba_erom_io *io, + u_int core_idx, bus_size_t offset); + +static struct siba_core_id siba_eio_read_core_id(struct siba_erom_io *io, + u_int core_idx, int unit); + +static int siba_eio_read_chipid(struct siba_erom_io *io, + bus_addr_t enum_addr, + struct bhnd_chipid *cid); + +/** + * SIBA EROM generic I/O context + */ +struct siba_erom_io { u_int ncores; /**< core count */ + bus_size_t offset; /**< base read offset */ /* resource state */ device_t dev; /**< parent dev to use for resource allocations, - or NULL if initialized with bst/bsh */ - struct bhnd_resource *res; /**< siba bus mapping, or NULL */ - int rid; /**< siba bus maping resource ID */ + or NULL if unavailable. */ + struct bhnd_resource *res; /**< memory resource, or NULL */ + int rid; /**< memory resource ID */ /* bus tag state */ - bus_space_tag_t bst; /**< chipc bus tag */ - bus_space_handle_t bsh; /**< chipc bus handle */ + bus_space_tag_t bst; /**< bus space tag */ + bus_space_handle_t bsh; /**< bus space handle */ +}; + +/** + * SIBA EROM per-instance state. + */ +struct siba_erom { + struct bhnd_erom obj; + struct siba_erom_io io; /**< i/o context */ }; -#define EROM_LOG(sc, fmt, ...) do { \ - if (sc->dev != NULL) { \ - device_printf(sc->dev, "%s: " fmt, __FUNCTION__, \ +#define EROM_LOG(io, fmt, ...) do { \ + if (io->dev != NULL) { \ + device_printf(io->dev, "%s: " fmt, __FUNCTION__, \ ##__VA_ARGS__); \ } else { \ printf("%s: " fmt, __FUNCTION__, ##__VA_ARGS__); \ } \ } while(0) -static uint32_t -siba_erom_read_4(struct siba_erom *sc, u_int core_idx, bus_size_t offset) -{ - bus_size_t core_offset; - - /* Sanity check core index and offset */ - if (core_idx >= sc->ncores) - panic("core index %u out of range (ncores=%u)", core_idx, - sc->ncores); - - if (offset > SIBA_CORE_SIZE - sizeof(uint32_t)) - panic("invalid core offset %#jx", (uintmax_t)offset); - - /* Perform read */ - core_offset = SIBA_CORE_OFFSET(core_idx) + offset; - if (sc->res != NULL) - return (bhnd_bus_read_4(sc->res, core_offset)); - else - return (bus_space_read_4(sc->bst, sc->bsh, core_offset)); -} - -/** Fetch and parse a siba core's identification registers */ -static struct siba_core_id -siba_erom_parse_core_id(struct siba_erom *sc, u_int core_idx, int unit) -{ - uint32_t idhigh, idlow; - - idhigh = siba_erom_read_4(sc, core_idx, SB0_REG_ABS(SIBA_CFG0_IDHIGH)); - idlow = siba_erom_read_4(sc, core_idx, SB0_REG_ABS(SIBA_CFG0_IDLOW)); - - return (siba_parse_core_id(idhigh, idlow, core_idx, unit)); -} - -/** Fetch and parse the chip identification register */ static int -siba_erom_read_chipid(struct siba_erom *sc, bus_addr_t enum_addr, +siba_erom_probe_common(struct siba_erom_io *io, const struct bhnd_chipid *hint, struct bhnd_chipid *cid) { - struct siba_core_id ccid; uint32_t idreg; - - /* Identify the chipcommon core */ - ccid = siba_erom_parse_core_id(sc, 0, 0); - if (ccid.core_info.vendor != BHND_MFGID_BCM || - ccid.core_info.device != BHND_COREID_CC) - { - EROM_LOG(sc, - "first core not chipcommon (vendor=%#hx, core=%#hx)\n", - ccid.core_info.vendor, ccid.core_info.device); - return (ENXIO); - } - - /* Identify the chipset */ - idreg = siba_erom_read_4(sc, 0, CHIPC_ID); - *cid = bhnd_parse_chipid(idreg, enum_addr); - - /* Fix up the core count in-place */ - return (bhnd_chipid_fixed_ncores(cid, ccid.core_info.hwrev, - &cid->ncores)); -} - -static int -siba_erom_init_common(struct siba_erom *sc) -{ - struct bhnd_chipid cid; int error; - /* There's always at least one core */ - sc->ncores = 1; - - /* Identify the chipset */ - if ((error = siba_erom_read_chipid(sc, SIBA_ENUM_ADDR, &cid))) - return (error); + /* Try using the provided hint. */ + if (hint != NULL) { + struct siba_core_id sid; - /* Verify the chip type */ - if (cid.chip_type != BHND_CHIPTYPE_SIBA) - return (ENXIO); + /* Validate bus type */ + if (hint->chip_type != BHND_CHIPTYPE_SIBA) + return (ENXIO); + + /* + * Verify the first core's IDHIGH/IDLOW identification. + * + * The core must be a Broadcom core, but must *not* be + * a chipcommon core; those shouldn't be hinted. + * + * The first core on EXTIF-equipped devices varies, but on the + * BCM4710, it's a SDRAM core (0x803). + */ + + sid = siba_eio_read_core_id(io, 0, 0); + + if (sid.core_info.vendor != BHND_MFGID_BCM) + return (ENXIO); + + if (sid.core_info.device == BHND_COREID_CC) + return (EINVAL); + + *cid = *hint; + } else { + /* Validate bus type */ + idreg = siba_eio_read_4(io, 0, CHIPC_ID); + if (CHIPC_GET_BITS(idreg, CHIPC_ID_BUS) != BHND_CHIPTYPE_SIBA) + return (ENXIO); + + /* Identify the chipset */ + if ((error = siba_eio_read_chipid(io, SIBA_ENUM_ADDR, cid))) + return (error); + + /* Verify the chip type */ + if (cid->chip_type != BHND_CHIPTYPE_SIBA) + return (ENXIO); + } /* * gcc hack: ensure bhnd_chipid.ncores cannot exceed SIBA_MAX_CORES @@ -165,89 +158,211 @@ * if (cid.ncores > SIBA_MAX_CORES) * return (EINVAL) */ - _Static_assert((2^sizeof(cid.ncores)) <= SIBA_MAX_CORES, + _Static_assert((2^sizeof(cid->ncores)) <= SIBA_MAX_CORES, "ncores could result in over-read of backing resource"); - /* Update our core count */ - sc->ncores = cid.ncores; - return (0); } +/* SIBA implementation of BHND_EROM_PROBE() */ static int -siba_erom_init(bhnd_erom_t *erom, device_t parent, int rid, - bus_addr_t enum_addr) +siba_erom_probe(bhnd_erom_class_t *cls, struct bhnd_resource *res, + bus_size_t offset, const struct bhnd_chipid *hint, + struct bhnd_chipid *cid) { - struct siba_erom *sc = (struct siba_erom *)erom; + struct siba_erom_io io; + int error, rid; - sc->dev = parent; - sc->rid = rid; + rid = rman_get_rid(res->res); - sc->res = bhnd_alloc_resource(sc->dev, SYS_RES_MEMORY, &sc->rid, - enum_addr, enum_addr + SIBA_ENUM_SIZE -1, SIBA_ENUM_SIZE, - RF_ACTIVE|RF_SHAREABLE); - if (sc->res == NULL) - return (ENOMEM); + /* Initialize I/O context, assuming at least 1 core exists. */ + if ((error = siba_eio_init(&io, NULL, res, rid, offset, 1))) + return (error); - return (siba_erom_init_common(sc)); + return (siba_erom_probe_common(&io, hint, cid)); } +/* SIBA implementation of BHND_EROM_PROBE_STATIC() */ static int siba_erom_probe_static(bhnd_erom_class_t *cls, bus_space_tag_t bst, - bus_space_handle_t bsh, bus_addr_t paddr, struct bhnd_chipid *cid) + bus_space_handle_t bsh, bus_addr_t paddr, const struct bhnd_chipid *hint, + struct bhnd_chipid *cid) { - struct siba_erom sc; - uint32_t idreg; - uint8_t chip_type; + struct siba_erom_io io; int error; - idreg = bus_space_read_4(bst, bsh, CHIPC_ID); - chip_type = CHIPC_GET_BITS(idreg, CHIPC_ID_BUS); + /* Initialize I/O context, assuming at least 1 core exists. */ + if ((error = siba_eio_init_static(&io, bst, bsh, 0, 1))) + return (error); - if (chip_type != BHND_CHIPTYPE_SIBA) - return (ENXIO); + return (siba_erom_probe_common(&io, hint, cid)); +} - /* Initialize a static EROM instance that we can use to fetch - * the chip identifier */ - if ((error = siba_erom_init_static((bhnd_erom_t *)&sc, bst, bsh))) - return (error); +/* SIBA implementation of BHND_EROM_INIT() */ +static int +siba_erom_init(bhnd_erom_t *erom, const struct bhnd_chipid *cid, + device_t parent, int rid) +{ + struct siba_erom *sc; + struct bhnd_resource *res; + int error; + + sc = (struct siba_erom *)erom; - /* Try to read the chip ID, clean up the static instance */ - error = siba_erom_read_chipid(&sc, paddr, cid); - siba_erom_fini((bhnd_erom_t *)&sc); + /* Allocate backing resource */ + res = bhnd_alloc_resource(parent, SYS_RES_MEMORY, &rid, + cid->enum_addr, cid->enum_addr + SIBA_ENUM_SIZE -1, SIBA_ENUM_SIZE, + RF_ACTIVE|RF_SHAREABLE); + if (res == NULL) + return (ENOMEM); + + /* Initialize I/O context */ + error = siba_eio_init(&sc->io, parent, res, rid, 0x0, cid->ncores); if (error) - return (error); + bhnd_release_resource(parent, SYS_RES_MEMORY, rid, res); - return (BUS_PROBE_DEFAULT); + return (error); } +/* SIBA implementation of BHND_EROM_INIT_STATIC() */ static int -siba_erom_init_static(bhnd_erom_t *erom, bus_space_tag_t bst, - bus_space_handle_t bsh) +siba_erom_init_static(bhnd_erom_t *erom, const struct bhnd_chipid *cid, + bus_space_tag_t bst, bus_space_handle_t bsh) { - struct siba_erom *sc = (struct siba_erom *)erom; - - sc->dev = NULL; - sc->rid = -1; - sc->res = NULL; - sc->bst = bst; - sc->bsh = bsh; + struct siba_erom *sc; + + sc = (struct siba_erom *)erom; - return (siba_erom_init_common(sc)); + /* Initialize I/O context */ + return (siba_eio_init_static(&sc->io, bst, bsh, 0x0, cid->ncores)); } +/* SIBA implementation of BHND_EROM_FINI() */ static void siba_erom_fini(bhnd_erom_t *erom) { struct siba_erom *sc = (struct siba_erom *)erom; - if (sc->res != NULL) { - bhnd_release_resource(sc->dev, SYS_RES_MEMORY, sc->rid, - sc->res); + if (sc->io.res != NULL) { + bhnd_release_resource(sc->io.dev, SYS_RES_MEMORY, sc->io.rid, + sc->io.res); + + sc->io.res = NULL; + sc->io.rid = -1; + } +} + +/* Initialize siba_erom resource I/O context */ +static int +siba_eio_init(struct siba_erom_io *io, device_t parent, + struct bhnd_resource *res, int rid, bus_size_t offset, u_int ncores) +{ + io->dev = parent; + io->res = res; + io->rid = rid; + io->offset = offset; + io->ncores = ncores; + + return (0); +} + +/* Initialize siba_erom bus space I/O context */ +static int +siba_eio_init_static(struct siba_erom_io *io, bus_space_tag_t bst, + bus_space_handle_t bsh, bus_size_t offset, u_int ncores) +{ + io->res = NULL; + io->rid = -1; + io->bst = bst; + io->bsh = bsh; + io->offset = offset; + io->ncores = ncores; + + return (0); +} + +/** + * Read a 32-bit value from @p offset relative to the base address of + * the given @p core_idx. + * + * @param io EROM I/O context. + * @param core_idx Core index. + * @param offset Core register offset. + */ +static uint32_t +siba_eio_read_4(struct siba_erom_io *io, u_int core_idx, bus_size_t offset) +{ + bus_size_t core_offset; + + /* Sanity check core index and offset */ + if (core_idx >= io->ncores) + panic("core index %u out of range (ncores=%u)", core_idx, + io->ncores); + + if (offset > SIBA_CORE_SIZE - sizeof(uint32_t)) + panic("invalid core offset %#jx", (uintmax_t)offset); + + /* Perform read */ + core_offset = io->offset + SIBA_CORE_OFFSET(core_idx) + offset; + if (io->res != NULL) + return (bhnd_bus_read_4(io->res, core_offset)); + else + return (bus_space_read_4(io->bst, io->bsh, core_offset)); +} + +/** + * Read and parse identification registers for the given @p core_index. + * + * @param io EROM I/O context. + * @param core_idx The core index. + * @param unit The caller-specified unit number to be included in the return + * value. + */ +static struct siba_core_id +siba_eio_read_core_id(struct siba_erom_io *io, u_int core_idx, int unit) +{ + uint32_t idhigh, idlow; + + idhigh = siba_eio_read_4(io, core_idx, SB0_REG_ABS(SIBA_CFG0_IDHIGH)); + idlow = siba_eio_read_4(io, core_idx, SB0_REG_ABS(SIBA_CFG0_IDLOW)); - sc->res = NULL; - sc->rid = -1; + return (siba_parse_core_id(idhigh, idlow, core_idx, unit)); +} + +/** + * Read and parse the chip identification register from the ChipCommon core. + * + * @param io EROM I/O context. + * @param enum_addr The physical address mapped by @p io. + * @param cid On success, the parsed chip identifier. + */ +static int +siba_eio_read_chipid(struct siba_erom_io *io, bus_addr_t enum_addr, + struct bhnd_chipid *cid) +{ + struct siba_core_id ccid; + uint32_t idreg; + + /* Identify the chipcommon core */ + ccid = siba_eio_read_core_id(io, 0, 0); + if (ccid.core_info.vendor != BHND_MFGID_BCM || + ccid.core_info.device != BHND_COREID_CC) + { + if (bootverbose) { + EROM_LOG(io, "first core not chipcommon " + "(vendor=%#hx, core=%#hx)\n", ccid.core_info.vendor, + ccid.core_info.device); + } + return (ENXIO); } + + /* Identify the chipset */ + idreg = siba_eio_read_4(io, 0, CHIPC_ID); + *cid = bhnd_parse_chipid(idreg, enum_addr); + + /* Fix up the core count in-place */ + return (bhnd_chipid_fixed_ncores(cid, ccid.core_info.hwrev, + &cid->ncores)); } static int @@ -264,12 +379,12 @@ imatch.m.match.core_unit = 0; /* Locate the first matching core */ - for (u_int i = 0; i < sc->ncores; i++) { + for (u_int i = 0; i < sc->io.ncores; i++) { struct siba_core_id sid; struct bhnd_core_info ci; /* Read the core info */ - sid = siba_erom_parse_core_id(sc, i, 0); + sid = siba_eio_read_core_id(&sc->io, i, 0); ci = sid.core_info; /* Check for initial match */ @@ -278,7 +393,7 @@ /* Re-scan preceding cores to determine the unit number. */ for (u_int j = 0; j < i; j++) { - sid = siba_erom_parse_core_id(sc, i, 0); + sid = siba_eio_read_core_id(&sc->io, i, 0); /* Bump the unit number? */ if (sid.core_info.vendor == ci.vendor && @@ -319,7 +434,7 @@ return (error); /* Fetch full siba core ident */ - sid = siba_erom_parse_core_id(sc, core.core_idx, core.unit); + 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)) @@ -343,7 +458,7 @@ } /* Read and parse the address match register */ - am = siba_erom_read_4(sc, core.core_idx, am_offset); + am = siba_eio_read_4(&sc->io, core.core_idx, am_offset); if ((error = siba_parse_admatch(am, &am_addr, &am_size))) { printf("failed to decode address match register value 0x%x\n", @@ -371,19 +486,19 @@ sc = (struct siba_erom *)erom; /* Allocate our core array */ - out = malloc(sizeof(*out) * sc->ncores, M_BHND, M_NOWAIT); + out = malloc(sizeof(*out) * sc->io.ncores, M_BHND, M_NOWAIT); if (out == NULL) return (ENOMEM); *cores = out; - *num_cores = sc->ncores; + *num_cores = sc->io.ncores; /* Enumerate all cores. */ - for (u_int i = 0; i < sc->ncores; i++) { + for (u_int i = 0; i < sc->io.ncores; i++) { struct siba_core_id sid; /* Read the core info */ - sid = siba_erom_parse_core_id(sc, i, 0); + sid = siba_eio_read_core_id(&sc->io, i, 0); out[i] = sid.core_info; /* Determine unit number */ @@ -405,6 +520,7 @@ } static kobj_method_t siba_erom_methods[] = { + KOBJMETHOD(bhnd_erom_probe, siba_erom_probe), KOBJMETHOD(bhnd_erom_probe_static, siba_erom_probe_static), KOBJMETHOD(bhnd_erom_init, siba_erom_init), KOBJMETHOD(bhnd_erom_init_static, siba_erom_init_static), Index: head/sys/dev/bhnd/siba/siba_nexus.c =================================================================== --- head/sys/dev/bhnd/siba/siba_nexus.c +++ head/sys/dev/bhnd/siba/siba_nexus.c @@ -84,18 +84,21 @@ static int siba_nexus_attach(device_t dev) { - struct siba_nexus_softc *sc; int error; - sc = device_get_softc(dev); + /* Perform initial attach and enumerate our children. */ + if ((error = siba_attach(dev))) + goto failed; + + /* Delegate remainder to standard bhnd method implementation */ + if ((error = bhnd_generic_attach(dev))) + goto failed; - /* Enumerate the bus. */ - if ((error = siba_add_children(dev, NULL))) { - device_printf(dev, "error %d enumerating children\n", error); - return (error); - } + return (0); - return (siba_attach(dev)); +failed: + device_delete_children(dev); + return (error); } static const struct bhnd_chipid * Index: head/sys/dev/bhnd/siba/sibavar.h =================================================================== --- head/sys/dev/bhnd/siba/sibavar.h +++ head/sys/dev/bhnd/siba/sibavar.h @@ -60,8 +60,7 @@ struct siba_core_id siba_parse_core_id(uint32_t idhigh, uint32_t idlow, u_int core_idx, int unit); -int siba_add_children(device_t bus, - const struct bhnd_chipid *chipid); +int siba_add_children(device_t bus); struct siba_devinfo *siba_alloc_dinfo(device_t dev); int siba_init_dinfo(device_t dev, @@ -100,6 +99,11 @@ #define SIBA_MAX_CFG SIBA_CFG_NUM_2_3 /**< maximum number of supported config register blocks */ +#define SIBA_CFG_RID_BASE 100 /**< base resource ID for SIBA_CFG* register allocations */ +#define SIBA_CFG_RID(_dinfo, _cfg) \ + (SIBA_CFG_RID_BASE + (_cfg) + \ + (_dinfo->core_id.core_info.core_idx * SIBA_MAX_CFG)) + /* Sonics/OCP address space mappings */ #define SIBA_CORE_ADDRSPACE 0 /**< Address space mapping the primary device registers */ @@ -155,7 +159,6 @@ struct siba_softc { struct bhnd_softc bhnd_sc; /**< bhnd state */ device_t dev; /**< siba device */ - device_t hostb_dev; /**< host bridge core, or NULL */ }; #endif /* _SIBA_SIBAVAR_H_ */ Index: head/sys/dev/bwn/if_bwn_pci.c =================================================================== --- head/sys/dev/bwn/if_bwn_pci.c +++ head/sys/dev/bwn/if_bwn_pci.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2015 Landon Fuller + * Copyright (c) 2015-2016 Landon Fuller * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -42,6 +42,7 @@ #include #include +#include #include #include @@ -115,12 +116,14 @@ { .bridge_hwcfg = &bhndb_pci_siba_generic_hwcfg, .bridge_hwtable = bhndb_pci_generic_hw_table, + .bridge_hwprio = bhndb_siba_priority_table, .devices = siba_devices }, /* BCMA devices */ { .bridge_hwcfg = &bhndb_pci_bcma_generic_hwcfg, .bridge_hwtable = bhndb_pci_generic_hw_table, + .bridge_hwprio = bhndb_bcma_priority_table, .devices = bcma_devices }, { NULL, NULL, NULL } @@ -234,6 +237,13 @@ return (sc->devcfg->bridge_hwtable); } +static const struct bhndb_hw_priority * +bwn_pci_get_bhndb_hwprio(device_t dev, device_t child) +{ + struct bwn_pci_softc *sc = device_get_softc(dev); + return (sc->devcfg->bridge_hwprio); +} + static bool bwn_pci_is_core_disabled(device_t dev, device_t child, struct bhnd_core_info *core) @@ -274,6 +284,7 @@ /* BHNDB_BUS Interface */ DEVMETHOD(bhndb_bus_get_generic_hwcfg, bwn_pci_get_generic_hwcfg), DEVMETHOD(bhndb_bus_get_hardware_table, bwn_pci_get_bhndb_hwtable), + DEVMETHOD(bhndb_bus_get_hardware_prio, bwn_pci_get_bhndb_hwprio), DEVMETHOD(bhndb_bus_is_core_disabled, bwn_pci_is_core_disabled), DEVMETHOD_END Index: head/sys/dev/bwn/if_bwn_pcivar.h =================================================================== --- head/sys/dev/bwn/if_bwn_pcivar.h +++ head/sys/dev/bwn/if_bwn_pcivar.h @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2015 Landon Fuller + * Copyright (c) 2015-2016 Landon Fuller * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -87,7 +87,8 @@ struct bwn_pci_devcfg { const struct bhndb_hwcfg *bridge_hwcfg; const struct bhndb_hw *bridge_hwtable; + const struct bhndb_hw_priority *bridge_hwprio; const struct bwn_pci_device *devices; }; -#endif /* _IF_BWN_PCIVAR_H_ */ \ No newline at end of file +#endif /* _IF_BWN_PCIVAR_H_ */ Index: head/sys/mips/broadcom/bcm_machdep.c =================================================================== --- head/sys/mips/broadcom/bcm_machdep.c +++ head/sys/mips/broadcom/bcm_machdep.c @@ -219,7 +219,8 @@ kobj_class_compile_static(cls, &kops); /* Probe the bus address */ - result = bhnd_erom_probe_static(cls, bst, bsh, bus_addr, &pcid); + result = bhnd_erom_probe_static(cls, bst, bsh, bus_addr, NULL, + &pcid); /* Drop pointer to stack allocated ops table */ cls->ops = NULL; @@ -253,7 +254,7 @@ /* ... and initialize the erom parser instance */ bsh = BCM_SOC_BSH(cid->enum_addr, 0); - error = bhnd_erom_init_static(*erom_cls, erom, esize, + error = bhnd_erom_init_static(*erom_cls, erom, esize, cid, mips_bus_space_generic, bsh); return (error); Index: head/sys/modules/bhnd/bhndb/Makefile =================================================================== --- head/sys/modules/bhnd/bhndb/Makefile +++ head/sys/modules/bhnd/bhndb/Makefile @@ -9,6 +9,7 @@ bhndb_if.c bhndb_if.h SRCS+= bhnd_bus_if.h \ bhnd_chipc_if.h \ + bhnd_erom_if.h \ bhnd_nvram_if.h SRCS+= device_if.h bus_if.h pci_if.h