Index: head/sys/dev/bhnd/bcma/bcma.c =================================================================== --- head/sys/dev/bhnd/bcma/bcma.c (revision 305370) +++ head/sys/dev/bhnd/bcma/bcma.c (revision 305371) @@ -1,595 +1,553 @@ /*- * Copyright (c) 2015 Landon Fuller * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include "bcmavar.h" #include "bcma_eromreg.h" #include "bcma_eromvar.h" #include /* 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) { device_set_desc(dev, "BCMA BHND bus"); 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 bcma_detach(device_t dev) { return (bhnd_generic_detach(dev)); } static int bcma_read_ivar(device_t dev, device_t child, int index, uintptr_t *result) { const struct bcma_devinfo *dinfo; const struct bhnd_core_info *ci; dinfo = device_get_ivars(child); ci = &dinfo->corecfg->core_info; switch (index) { case BHND_IVAR_VENDOR: *result = ci->vendor; return (0); case BHND_IVAR_DEVICE: *result = ci->device; return (0); case BHND_IVAR_HWREV: *result = ci->hwrev; return (0); case BHND_IVAR_DEVICE_CLASS: *result = bhnd_core_class(ci); return (0); case BHND_IVAR_VENDOR_NAME: *result = (uintptr_t) bhnd_vendor_name(ci->vendor); return (0); case BHND_IVAR_DEVICE_NAME: *result = (uintptr_t) bhnd_core_name(ci); return (0); case BHND_IVAR_CORE_INDEX: *result = ci->core_idx; return (0); case BHND_IVAR_CORE_UNIT: *result = ci->unit; return (0); default: return (ENOENT); } } static int bcma_write_ivar(device_t dev, device_t child, int index, uintptr_t value) { switch (index) { case BHND_IVAR_VENDOR: case BHND_IVAR_DEVICE: case BHND_IVAR_HWREV: case BHND_IVAR_DEVICE_CLASS: case BHND_IVAR_VENDOR_NAME: case BHND_IVAR_DEVICE_NAME: case BHND_IVAR_CORE_INDEX: case BHND_IVAR_CORE_UNIT: return (EINVAL); default: return (ENOENT); } } static struct resource_list * bcma_get_resource_list(device_t dev, device_t child) { struct bcma_devinfo *dinfo = device_get_ivars(child); 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) { struct bcma_devinfo *dinfo; if (device_get_parent(child) != dev) BHND_BUS_RESET_CORE(device_get_parent(dev), child, flags); dinfo = device_get_ivars(child); /* Can't reset the core without access to the agent registers */ if (dinfo->res_agent == NULL) return (ENODEV); /* Start reset */ bhnd_bus_write_4(dinfo->res_agent, BHND_RESET_CF, BHND_RESET_CF_ENABLE); bhnd_bus_read_4(dinfo->res_agent, BHND_RESET_CF); DELAY(10); /* Disable clock */ bhnd_bus_write_4(dinfo->res_agent, BHND_CF, flags); bhnd_bus_read_4(dinfo->res_agent, BHND_CF); DELAY(10); /* Enable clocks & force clock gating */ bhnd_bus_write_4(dinfo->res_agent, BHND_CF, BHND_CF_CLOCK_EN | BHND_CF_FGC | flags); bhnd_bus_read_4(dinfo->res_agent, BHND_CF); DELAY(10); /* Complete reset */ bhnd_bus_write_4(dinfo->res_agent, BHND_RESET_CF, 0); bhnd_bus_read_4(dinfo->res_agent, BHND_RESET_CF); DELAY(10); /* Release force clock gating */ bhnd_bus_write_4(dinfo->res_agent, BHND_CF, BHND_CF_CLOCK_EN | flags); bhnd_bus_read_4(dinfo->res_agent, BHND_CF); DELAY(10); return (0); } static int bcma_suspend_core(device_t dev, device_t child) { struct bcma_devinfo *dinfo; if (device_get_parent(child) != dev) BHND_BUS_SUSPEND_CORE(device_get_parent(dev), child); dinfo = device_get_ivars(child); /* Can't suspend the core without access to the agent registers */ if (dinfo->res_agent == NULL) return (ENODEV); // TODO - perform suspend return (ENXIO); } static uint32_t bcma_read_config(device_t dev, device_t child, bus_size_t offset, u_int width) { struct bcma_devinfo *dinfo; struct bhnd_resource *r; /* Must be a directly attached child core */ if (device_get_parent(child) != dev) return (UINT32_MAX); /* Fetch the agent registers */ dinfo = device_get_ivars(child); if ((r = dinfo->res_agent) == NULL) return (UINT32_MAX); /* Verify bounds */ if (offset > rman_get_size(r->res)) return (UINT32_MAX); if (rman_get_size(r->res) - offset < width) return (UINT32_MAX); switch (width) { case 1: return (bhnd_bus_read_1(r, offset)); case 2: return (bhnd_bus_read_2(r, offset)); case 4: return (bhnd_bus_read_4(r, offset)); default: return (UINT32_MAX); } } static void bcma_write_config(device_t dev, device_t child, bus_size_t offset, uint32_t val, u_int width) { struct bcma_devinfo *dinfo; struct bhnd_resource *r; /* Must be a directly attached child core */ if (device_get_parent(child) != dev) return; /* Fetch the agent registers */ dinfo = device_get_ivars(child); if ((r = dinfo->res_agent) == NULL) return; /* Verify bounds */ if (offset > rman_get_size(r->res)) return; if (rman_get_size(r->res) - offset < width) return; switch (width) { case 1: bhnd_bus_write_1(r, offset, val); break; case 2: bhnd_bus_write_2(r, offset, val); break; case 4: bhnd_bus_write_4(r, offset, val); break; default: break; } } static u_int bcma_get_port_count(device_t dev, device_t child, bhnd_port_type type) { struct bcma_devinfo *dinfo; /* delegate non-bus-attached devices to our parent */ if (device_get_parent(child) != dev) return (BHND_BUS_GET_PORT_COUNT(device_get_parent(dev), child, type)); dinfo = device_get_ivars(child); switch (type) { case BHND_PORT_DEVICE: return (dinfo->corecfg->num_dev_ports); case BHND_PORT_BRIDGE: return (dinfo->corecfg->num_bridge_ports); case BHND_PORT_AGENT: return (dinfo->corecfg->num_wrapper_ports); default: device_printf(dev, "%s: unknown type (%d)\n", __func__, type); return (0); } } static u_int bcma_get_region_count(device_t dev, device_t child, bhnd_port_type type, u_int port_num) { struct bcma_devinfo *dinfo; struct bcma_sport_list *ports; struct bcma_sport *port; /* delegate non-bus-attached devices to our parent */ if (device_get_parent(child) != dev) return (BHND_BUS_GET_REGION_COUNT(device_get_parent(dev), child, type, port_num)); dinfo = device_get_ivars(child); ports = bcma_corecfg_get_port_list(dinfo->corecfg, type); STAILQ_FOREACH(port, ports, sp_link) { if (port->sp_num == port_num) return (port->sp_num_maps); } /* not found */ return (0); } static int bcma_get_port_rid(device_t dev, device_t child, bhnd_port_type port_type, u_int port_num, u_int region_num) { struct bcma_devinfo *dinfo; struct bcma_map *map; struct bcma_sport_list *ports; struct bcma_sport *port; dinfo = device_get_ivars(child); ports = bcma_corecfg_get_port_list(dinfo->corecfg, port_type); STAILQ_FOREACH(port, ports, sp_link) { if (port->sp_num != port_num) continue; STAILQ_FOREACH(map, &port->sp_maps, m_link) if (map->m_region_num == region_num) return map->m_rid; } return -1; } static int bcma_decode_port_rid(device_t dev, device_t child, int type, int rid, bhnd_port_type *port_type, u_int *port_num, u_int *region_num) { struct bcma_devinfo *dinfo; struct bcma_map *map; struct bcma_sport_list *ports; struct bcma_sport *port; dinfo = device_get_ivars(child); /* Ports are always memory mapped */ if (type != SYS_RES_MEMORY) return (EINVAL); /* Starting with the most likely device list, search all three port * lists */ bhnd_port_type types[] = { BHND_PORT_DEVICE, BHND_PORT_AGENT, BHND_PORT_BRIDGE }; for (int i = 0; i < nitems(types); i++) { ports = bcma_corecfg_get_port_list(dinfo->corecfg, types[i]); STAILQ_FOREACH(port, ports, sp_link) { STAILQ_FOREACH(map, &port->sp_maps, m_link) { if (map->m_rid != rid) continue; *port_type = port->sp_type; *port_num = port->sp_num; *region_num = map->m_region_num; return (0); } } } return (ENOENT); } static int bcma_get_region_addr(device_t dev, device_t child, bhnd_port_type port_type, u_int port_num, u_int region_num, bhnd_addr_t *addr, bhnd_size_t *size) { struct bcma_devinfo *dinfo; struct bcma_map *map; struct bcma_sport_list *ports; struct bcma_sport *port; dinfo = device_get_ivars(child); ports = bcma_corecfg_get_port_list(dinfo->corecfg, port_type); /* Search the port list */ STAILQ_FOREACH(port, ports, sp_link) { if (port->sp_num != port_num) continue; STAILQ_FOREACH(map, &port->sp_maps, m_link) { if (map->m_region_num != region_num) continue; /* Found! */ *addr = map->m_base; *size = map->m_size; return (0); } } return (ENOENT); } static struct bhnd_devinfo * bcma_alloc_bhnd_dinfo(device_t dev) { struct bcma_devinfo *dinfo = bcma_alloc_dinfo(dev); return ((struct bhnd_devinfo *)dinfo); } static void bcma_free_bhnd_dinfo(device_t dev, struct bhnd_devinfo *dinfo) { bcma_free_dinfo(dev, (struct bcma_devinfo *)dinfo); } /** * Scan the device enumeration ROM table, adding all valid discovered cores to * the bus. * * @param bus The bcma bus. */ int bcma_add_children(device_t bus) { bhnd_erom_t *erom; struct bcma_erom *bcma_erom; const struct bhnd_chipid *cid; struct bcma_corecfg *corecfg; struct bcma_devinfo *dinfo; device_t child; int error; cid = BHND_BUS_GET_CHIPID(bus, bus); 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); /* Add all cores. */ bcma_erom = (struct bcma_erom *)erom; while ((error = bcma_erom_next_corecfg(bcma_erom, &corecfg)) == 0) { /* Add the child device */ 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)) device_disable(child); /* Issue bus callback for fully initialized child. */ 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); } static device_method_t bcma_methods[] = { /* Device interface */ DEVMETHOD(device_probe, bcma_probe), DEVMETHOD(device_attach, bcma_attach), DEVMETHOD(device_detach, bcma_detach), /* Bus interface */ DEVMETHOD(bus_read_ivar, bcma_read_ivar), DEVMETHOD(bus_write_ivar, bcma_write_ivar), 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), DEVMETHOD(bhnd_bus_suspend_core, bcma_suspend_core), DEVMETHOD(bhnd_bus_read_config, bcma_read_config), DEVMETHOD(bhnd_bus_write_config, bcma_write_config), DEVMETHOD(bhnd_bus_get_port_count, bcma_get_port_count), DEVMETHOD(bhnd_bus_get_region_count, bcma_get_region_count), DEVMETHOD(bhnd_bus_get_port_rid, bcma_get_port_rid), DEVMETHOD(bhnd_bus_decode_port_rid, bcma_decode_port_rid), DEVMETHOD(bhnd_bus_get_region_addr, bcma_get_region_addr), DEVMETHOD_END }; DEFINE_CLASS_1(bhnd, bcma_driver, bcma_methods, sizeof(struct bcma_softc), bhnd_driver); MODULE_VERSION(bcma, 1); MODULE_DEPEND(bcma, bhnd, 1, 1, 1); Index: head/sys/dev/bhnd/bcma/bcma_bhndb.c =================================================================== --- head/sys/dev/bhnd/bcma/bcma_bhndb.c (revision 305370) +++ head/sys/dev/bhnd/bcma/bcma_bhndb.c (revision 305371) @@ -1,179 +1,174 @@ /*- * Copyright (c) 2015 Landon Fuller * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include "bcmavar.h" #include "bcma_eromreg.h" #include "bcma_eromvar.h" /* * Supports attachment of bcma(4) bus devices via a bhndb bridge. */ static int bcma_bhndb_probe(device_t dev) { const struct bhnd_chipid *cid; int error; /* Defer to default probe implementation */ if ((error = bcma_probe(dev)) > 0) return (error); /* Check bus type */ cid = BHNDB_GET_CHIPID(device_get_parent(dev), dev); if (cid->chip_type != BHND_CHIPTYPE_BCMA) return (ENXIO); /* Set device description */ bhnd_set_default_bus_desc(dev, cid); return (error); } static int bcma_bhndb_attach(device_t dev) { - struct bcma_softc *sc; - int error; + int error; - sc = device_get_softc(dev); + /* Perform initial attach and enumerate our children. */ + if ((error = bcma_attach(dev))) + goto failed; - /* Enumerate our children. */ - if ((error = bcma_add_children(dev))) - return (error); + /* 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); + return (0); - /* 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 */ - return (bcma_attach(dev)); +failed: + device_delete_children(dev); + return (error); } static int bcma_bhndb_suspend_child(device_t dev, device_t child) { struct bcma_devinfo *dinfo; int error; if (device_get_parent(child) != dev) BUS_SUSPEND_CHILD(device_get_parent(dev), child); if (device_is_suspended(child)) return (EBUSY); dinfo = device_get_ivars(child); /* Suspend the child */ if ((error = bhnd_generic_br_suspend_child(dev, child))) return (error); /* Suspend child's agent resource */ if (dinfo->res_agent != NULL) BHNDB_SUSPEND_RESOURCE(device_get_parent(dev), dev, SYS_RES_MEMORY, dinfo->res_agent->res); return (0); } static int bcma_bhndb_resume_child(device_t dev, device_t child) { struct bcma_devinfo *dinfo; int error; if (device_get_parent(child) != dev) BUS_SUSPEND_CHILD(device_get_parent(dev), child); if (!device_is_suspended(child)) return (EBUSY); dinfo = device_get_ivars(child); /* Resume child's agent resource */ if (dinfo->res_agent != NULL) { error = BHNDB_RESUME_RESOURCE(device_get_parent(dev), dev, SYS_RES_MEMORY, dinfo->res_agent->res); if (error) return (error); } /* Resume the child */ if ((error = bhnd_generic_br_resume_child(dev, child))) { /* On failure, re-suspend the agent resource */ if (dinfo->res_agent != NULL) { BHNDB_SUSPEND_RESOURCE(device_get_parent(dev), dev, SYS_RES_MEMORY, dinfo->res_agent->res); } return (error); } return (0); } static device_method_t bcma_bhndb_methods[] = { /* Device interface */ DEVMETHOD(device_probe, bcma_bhndb_probe), DEVMETHOD(device_attach, bcma_bhndb_attach), /* Bus interface */ DEVMETHOD(bus_suspend_child, bcma_bhndb_suspend_child), DEVMETHOD(bus_resume_child, bcma_bhndb_resume_child), DEVMETHOD_END }; DEFINE_CLASS_2(bhnd, bcma_bhndb_driver, bcma_bhndb_methods, sizeof(struct bcma_softc), bhnd_bhndb_driver, bcma_driver); DRIVER_MODULE(bcma_bhndb, bhndb, bcma_bhndb_driver, bhnd_devclass, NULL, NULL); MODULE_VERSION(bcma_bhndb, 1); MODULE_DEPEND(bcma_bhndb, bcma, 1, 1, 1); MODULE_DEPEND(bcma_bhndb, bhnd, 1, 1, 1); MODULE_DEPEND(bcma_bhndb, bhndb, 1, 1, 1); Index: head/sys/dev/bhnd/bcma/bcma_erom.c =================================================================== --- head/sys/dev/bhnd/bcma/bcma_erom.c (revision 305370) +++ head/sys/dev/bhnd/bcma/bcma_erom.c (revision 305371) @@ -1,1274 +1,1371 @@ /*- * Copyright (c) 2015 Landon Fuller * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include "bcma_eromreg.h" #include "bcma_eromvar.h" /* * BCMA Enumeration ROM (EROM) Table * * Provides auto-discovery of BCMA cores on Broadcom's HND SoC. * * The EROM core address can be found at BCMA_CC_EROM_ADDR within the * ChipCommon registers. The table itself is comprised of 32-bit * type-tagged entries, organized into an array of variable-length * core descriptor records. * * The final core descriptor is followed by a 32-bit BCMA_EROM_TABLE_EOF (0xF) * 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); static int bcma_erom_skip_core(struct bcma_erom *erom); static int bcma_erom_skip_mport(struct bcma_erom *erom); static int bcma_erom_skip_sport_region(struct bcma_erom *erom); static int bcma_erom_seek_next(struct bcma_erom *erom, uint8_t etype); 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); static void bcma_erom_reset(struct bcma_erom *erom); static int bcma_erom_seek_matching_core(struct bcma_erom *sc, const struct bhnd_core_match *desc, struct bhnd_core_info *core); static int bcma_erom_parse_core(struct bcma_erom *erom, struct bcma_erom_core *core); static int bcma_erom_parse_mport(struct bcma_erom *erom, struct bcma_erom_mport *mport); static int bcma_erom_parse_sport_region(struct bcma_erom *erom, struct bcma_erom_sport_region *region); static void bcma_erom_to_core_info(const struct bcma_erom_core *core, 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, \ (unsigned long long) (erom->offset), ##__VA_ARGS__);\ } \ } while(0) - /** Return the type name for an EROM entry */ static const char * bcma_erom_entry_type_name (uint8_t entry) { switch (BCMA_EROM_GET_ATTR(entry, ENTRY_TYPE)) { case BCMA_EROM_ENTRY_TYPE_CORE: return "core"; case BCMA_EROM_ENTRY_TYPE_MPORT: return "mport"; case BCMA_EROM_ENTRY_TYPE_REGION: return "region"; default: return "unknown"; } } + +/** + * 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; - 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) + 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); + + 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); case BHND_CHIPTYPE_BCMA_ALT: case BHND_CHIPTYPE_UBUS: return (BUS_PROBE_GENERIC); 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; } } static int bcma_erom_lookup_core(bhnd_erom_t *erom, const struct bhnd_core_match *desc, struct bhnd_core_info *core) { struct bcma_erom *sc = (struct bcma_erom *)erom; /* Search for the first matching core */ return (bcma_erom_seek_matching_core(sc, desc, core)); } static int bcma_erom_lookup_core_addr(bhnd_erom_t *erom, const struct bhnd_core_match *desc, bhnd_port_type port_type, u_int port_num, u_int region_num, struct bhnd_core_info *core, bhnd_addr_t *addr, bhnd_size_t *size) { struct bcma_erom *sc; struct bcma_erom_core ec; uint32_t entry; uint8_t region_port, region_type; bool found; int error; sc = (struct bcma_erom *)erom; /* Seek to the first matching core and provide the core info * to the caller */ if ((error = bcma_erom_seek_matching_core(sc, desc, core))) return (error); if ((error = bcma_erom_parse_core(sc, &ec))) return (error); /* Skip master ports */ for (u_long i = 0; i < ec.num_mport; i++) { if ((error = bcma_erom_skip_mport(sc))) return (error); } /* Seek to the region block for the given port type */ found = false; while (1) { bhnd_port_type p_type; uint8_t r_type; if ((error = bcma_erom_peek32(sc, &entry))) return (error); if (!BCMA_EROM_ENTRY_IS(entry, REGION)) return (ENOENT); /* Expected region type? */ r_type = BCMA_EROM_GET_ATTR(entry, REGION_TYPE); error = bcma_erom_region_to_port_type(sc, r_type, &p_type); if (error) return (error); if (p_type == port_type) { found = true; break; } /* Skip to next entry */ if ((error = bcma_erom_skip_sport_region(sc))) return (error); } if (!found) return (ENOENT); /* Found the appropriate port type block; now find the region records * for the given port number */ found = false; for (u_int i = 0; i <= port_num; i++) { bhnd_port_type p_type; if ((error = bcma_erom_peek32(sc, &entry))) return (error); if (!BCMA_EROM_ENTRY_IS(entry, REGION)) return (ENOENT); /* Fetch the type/port of the first region entry */ region_type = BCMA_EROM_GET_ATTR(entry, REGION_TYPE); region_port = BCMA_EROM_GET_ATTR(entry, REGION_PORT); /* Have we found the region entries for the desired port? */ if (i == port_num) { error = bcma_erom_region_to_port_type(sc, region_type, &p_type); if (error) return (error); if (p_type == port_type) found = true; break; } /* Otherwise, seek to next block of region records */ while (1) { uint8_t next_type, next_port; if ((error = bcma_erom_skip_sport_region(sc))) return (error); if ((error = bcma_erom_peek32(sc, &entry))) return (error); if (!BCMA_EROM_ENTRY_IS(entry, REGION)) return (ENOENT); next_type = BCMA_EROM_GET_ATTR(entry, REGION_TYPE); next_port = BCMA_EROM_GET_ATTR(entry, REGION_PORT); if (next_type != region_type || next_port != region_port) break; } } if (!found) return (ENOENT); /* Finally, search for the requested region number */ for (u_int i = 0; i <= region_num; i++) { struct bcma_erom_sport_region region; uint8_t next_port, next_type; if ((error = bcma_erom_peek32(sc, &entry))) return (error); if (!BCMA_EROM_ENTRY_IS(entry, REGION)) return (ENOENT); /* Check for the end of the region block */ next_type = BCMA_EROM_GET_ATTR(entry, REGION_TYPE); next_port = BCMA_EROM_GET_ATTR(entry, REGION_PORT); if (next_type != region_type || next_port != region_port) break; /* Parse the region */ if ((error = bcma_erom_parse_sport_region(sc, ®ion))) return (error); /* Is this our target region_num? */ if (i == region_num) { /* Found */ *addr = region.base_addr; *size = region.size; return (0); } } /* Not found */ return (ENOENT); }; static int bcma_erom_get_core_table(bhnd_erom_t *erom, struct bhnd_core_info **cores, u_int *num_cores) { struct bcma_erom *sc; struct bhnd_core_info *buffer; bus_size_t initial_offset; u_int count; int error; sc = (struct bcma_erom *)erom; buffer = NULL; initial_offset = bcma_erom_tell(sc); /* Determine the core count */ bcma_erom_reset(sc); for (count = 0, error = 0; !error; count++) { struct bcma_erom_core core; /* Seek to the first readable core entry */ error = bcma_erom_seek_next(sc, BCMA_EROM_ENTRY_TYPE_CORE); if (error == ENOENT) break; else if (error) goto cleanup; /* Read past the core descriptor */ if ((error = bcma_erom_parse_core(sc, &core))) goto cleanup; } /* Allocate our output buffer */ buffer = malloc(sizeof(struct bhnd_core_info) * count, M_BHND, M_NOWAIT); if (buffer == NULL) { error = ENOMEM; goto cleanup; } /* Parse all core descriptors */ bcma_erom_reset(sc); for (u_int i = 0; i < count; i++) { struct bcma_erom_core core; int unit; /* Parse the core */ error = bcma_erom_seek_next(sc, BCMA_EROM_ENTRY_TYPE_CORE); if (error) goto cleanup; error = bcma_erom_parse_core(sc, &core); if (error) goto cleanup; /* Determine the unit number */ unit = 0; for (u_int j = 0; j < i; j++) { if (buffer[i].vendor == buffer[j].vendor && buffer[i].device == buffer[j].device) unit++; } /* Convert to a bhnd info record */ bcma_erom_to_core_info(&core, i, unit, &buffer[i]); } cleanup: if (!error) { *cores = buffer; *num_cores = count; } else { if (buffer != NULL) free(buffer, M_BHND); } /* Restore the initial position */ bcma_erom_seek(sc, initial_offset); return (error); } static void bcma_erom_free_core_table(bhnd_erom_t *erom, struct bhnd_core_info *cores) { free(cores, M_BHND); } /** * Return the current read position. */ static bus_size_t bcma_erom_tell(struct bcma_erom *erom) { return (erom->offset); } /** * Seek to an absolute read position. */ static void bcma_erom_seek(struct bcma_erom *erom, bus_size_t offset) { erom->offset = offset; } /** * Read a 32-bit entry value from the EROM table without advancing the * read position. * * @param erom EROM read state. * @param entry Will contain the read result on success. * @retval 0 success * @retval ENOENT The end of the EROM table was reached. * @retval non-zero The read could not be completed. */ 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); } /** * Read a 32-bit entry value from the EROM table. * * @param erom EROM read state. * @param entry Will contain the read result on success. * @retval 0 success * @retval ENOENT The end of the EROM table was reached. * @retval non-zero The read could not be completed. */ static int bcma_erom_read32(struct bcma_erom *erom, uint32_t *entry) { int error; if ((error = bcma_erom_peek32(erom, entry)) == 0) erom->offset += 4; return (error); } /** * Read and discard 32-bit entry value from the EROM table. * * @param erom EROM read state. * @retval 0 success * @retval ENOENT The end of the EROM table was reached. * @retval non-zero The read could not be completed. */ static int bcma_erom_skip32(struct bcma_erom *erom) { uint32_t entry; return bcma_erom_read32(erom, &entry); } /** * Read and discard a core descriptor from the EROM table. * * @param erom EROM read state. * @retval 0 success * @retval ENOENT The end of the EROM table was reached. * @retval non-zero The read could not be completed. */ static int bcma_erom_skip_core(struct bcma_erom *erom) { struct bcma_erom_core core; return (bcma_erom_parse_core(erom, &core)); } /** * Read and discard a master port descriptor from the EROM table. * * @param erom EROM read state. * @retval 0 success * @retval ENOENT The end of the EROM table was reached. * @retval non-zero The read could not be completed. */ static int bcma_erom_skip_mport(struct bcma_erom *erom) { struct bcma_erom_mport mp; return (bcma_erom_parse_mport(erom, &mp)); } /** * Read and discard a port region descriptor from the EROM table. * * @param erom EROM read state. * @retval 0 success * @retval ENOENT The end of the EROM table was reached. * @retval non-zero The read could not be completed. */ static int bcma_erom_skip_sport_region(struct bcma_erom *erom) { struct bcma_erom_sport_region r; return (bcma_erom_parse_sport_region(erom, &r)); } /** * Seek to the next entry matching the given EROM entry type. * * @param erom EROM read state. * @param etype One of BCMA_EROM_ENTRY_TYPE_CORE, * BCMA_EROM_ENTRY_TYPE_MPORT, or BCMA_EROM_ENTRY_TYPE_REGION. * @retval 0 success * @retval ENOENT The end of the EROM table was reached. * @retval non-zero Reading or parsing the descriptor failed. */ static int bcma_erom_seek_next(struct bcma_erom *erom, uint8_t etype) { uint32_t entry; int error; /* Iterate until we hit an entry matching the requested type. */ while (!(error = bcma_erom_peek32(erom, &entry))) { /* Handle EOF */ if (entry == BCMA_EROM_TABLE_EOF) return (ENOENT); /* Invalid entry */ if (!BCMA_EROM_GET_ATTR(entry, ENTRY_ISVALID)) return (EINVAL); /* Entry type matches? */ if (BCMA_EROM_GET_ATTR(entry, ENTRY_TYPE) == etype) return (0); /* Skip non-matching entry types. */ switch (BCMA_EROM_GET_ATTR(entry, ENTRY_TYPE)) { case BCMA_EROM_ENTRY_TYPE_CORE: if ((error = bcma_erom_skip_core(erom))) return (error); break; case BCMA_EROM_ENTRY_TYPE_MPORT: if ((error = bcma_erom_skip_mport(erom))) return (error); break; case BCMA_EROM_ENTRY_TYPE_REGION: if ((error = bcma_erom_skip_sport_region(erom))) return (error); break; default: /* Unknown entry type! */ return (EINVAL); } } return (error); } /** * Return the read position to the start of the EROM table. * * @param erom EROM read state. */ static void bcma_erom_reset(struct bcma_erom *erom) { erom->offset = 0; } /** * Seek to the first core entry matching @p desc. * * @param erom EROM read state. * @param desc The core match descriptor. * @param[out] core On success, the matching core info. If the core info * is not desired, a NULL pointer may be provided. * @retval 0 success * @retval ENOENT The end of the EROM table was reached before @p index was * found. * @retval non-zero Reading or parsing failed. */ static int bcma_erom_seek_matching_core(struct bcma_erom *sc, const struct bhnd_core_match *desc, struct bhnd_core_info *core) { struct bhnd_core_match imatch; bus_size_t core_offset, next_offset; int error; /* Seek to table start. */ bcma_erom_reset(sc); /* We can't determine a core's unit number during the initial scan. */ imatch = *desc; imatch.m.match.core_unit = 0; /* Locate the first matching core */ for (u_int i = 0; i < UINT_MAX; i++) { struct bcma_erom_core ec; struct bhnd_core_info ci; /* Seek to the next core */ error = bcma_erom_seek_next(sc, BCMA_EROM_ENTRY_TYPE_CORE); if (error) return (error); /* Save the core offset */ core_offset = bcma_erom_tell(sc); /* Parse the core */ if ((error = bcma_erom_parse_core(sc, &ec))) return (error); bcma_erom_to_core_info(&ec, i, 0, &ci); /* Check for initial match */ if (!bhnd_core_matches(&ci, &imatch)) continue; /* Re-scan preceding cores to determine the unit number. */ next_offset = bcma_erom_tell(sc); bcma_erom_reset(sc); for (u_int j = 0; j < i; j++) { /* Parse the core */ error = bcma_erom_seek_next(sc, BCMA_EROM_ENTRY_TYPE_CORE); if (error) return (error); if ((error = bcma_erom_parse_core(sc, &ec))) return (error); /* Bump the unit number? */ if (ec.vendor == ci.vendor && ec.device == ci.device) ci.unit++; } /* Check for full match against now-valid unit number */ if (!bhnd_core_matches(&ci, desc)) { /* Reposition to allow reading the next core */ bcma_erom_seek(sc, next_offset); continue; } /* Found; seek to the core's initial offset and provide * the core info to the caller */ bcma_erom_seek(sc, core_offset); if (core != NULL) *core = ci; return (0); } /* Not found, or a parse error occured */ return (error); } /** * Read the next core descriptor from the EROM table. * * @param erom EROM read state. * @param[out] core On success, will be populated with the parsed core * descriptor data. * @retval 0 success * @retval ENOENT The end of the EROM table was reached. * @retval non-zero Reading or parsing the core descriptor failed. */ static int bcma_erom_parse_core(struct bcma_erom *erom, struct bcma_erom_core *core) { uint32_t entry; int error; /* Parse CoreDescA */ if ((error = bcma_erom_read32(erom, &entry))) return (error); /* Handle EOF */ if (entry == BCMA_EROM_TABLE_EOF) return (ENOENT); if (!BCMA_EROM_ENTRY_IS(entry, CORE)) { EROM_LOG(erom, "Unexpected EROM entry 0x%x (type=%s)\n", entry, bcma_erom_entry_type_name(entry)); return (EINVAL); } core->vendor = BCMA_EROM_GET_ATTR(entry, COREA_DESIGNER); core->device = BCMA_EROM_GET_ATTR(entry, COREA_ID); /* Parse CoreDescB */ if ((error = bcma_erom_read32(erom, &entry))) return (error); if (!BCMA_EROM_ENTRY_IS(entry, CORE)) { return (EINVAL); } core->rev = BCMA_EROM_GET_ATTR(entry, COREB_REV); core->num_mport = BCMA_EROM_GET_ATTR(entry, COREB_NUM_MP); core->num_dport = BCMA_EROM_GET_ATTR(entry, COREB_NUM_DP); core->num_mwrap = BCMA_EROM_GET_ATTR(entry, COREB_NUM_WMP); core->num_swrap = BCMA_EROM_GET_ATTR(entry, COREB_NUM_WSP); return (0); } /** * Read the next master port descriptor from the EROM table. * * @param erom EROM read state. * @param[out] mport On success, will be populated with the parsed * descriptor data. * @retval 0 success * @retval non-zero Reading or parsing the descriptor failed. */ static int bcma_erom_parse_mport(struct bcma_erom *erom, struct bcma_erom_mport *mport) { uint32_t entry; int error; /* Parse the master port descriptor */ if ((error = bcma_erom_read32(erom, &entry))) return (error); if (!BCMA_EROM_ENTRY_IS(entry, MPORT)) return (EINVAL); mport->port_vid = BCMA_EROM_GET_ATTR(entry, MPORT_ID); mport->port_num = BCMA_EROM_GET_ATTR(entry, MPORT_NUM); return (0); } /** * Read the next slave port region descriptor from the EROM table. * * @param erom EROM read state. * @param[out] mport On success, will be populated with the parsed * descriptor data. * @retval 0 success * @retval ENOENT The end of the region descriptor table was reached. * @retval non-zero Reading or parsing the descriptor failed. */ static int bcma_erom_parse_sport_region(struct bcma_erom *erom, struct bcma_erom_sport_region *region) { uint32_t entry; uint8_t size_type; int error; /* Peek at the region descriptor */ if (bcma_erom_peek32(erom, &entry)) return (EINVAL); /* A non-region entry signals the end of the region table */ if (!BCMA_EROM_ENTRY_IS(entry, REGION)) { return (ENOENT); } else { bcma_erom_skip32(erom); } region->base_addr = BCMA_EROM_GET_ATTR(entry, REGION_BASE); region->region_type = BCMA_EROM_GET_ATTR(entry, REGION_TYPE); region->region_port = BCMA_EROM_GET_ATTR(entry, REGION_PORT); size_type = BCMA_EROM_GET_ATTR(entry, REGION_SIZE); /* If region address is 64-bit, fetch the high bits. */ if (BCMA_EROM_GET_ATTR(entry, REGION_64BIT)) { if ((error = bcma_erom_read32(erom, &entry))) return (error); region->base_addr |= ((bhnd_addr_t) entry << 32); } /* Parse the region size; it's either encoded as the binary logarithm * of the number of 4K pages (i.e. log2 n), or its encoded as a * 32-bit/64-bit literal value directly following the current entry. */ if (size_type == BCMA_EROM_REGION_SIZE_OTHER) { if ((error = bcma_erom_read32(erom, &entry))) return (error); region->size = BCMA_EROM_GET_ATTR(entry, RSIZE_VAL); if (BCMA_EROM_GET_ATTR(entry, RSIZE_64BIT)) { if ((error = bcma_erom_read32(erom, &entry))) return (error); region->size |= ((bhnd_size_t) entry << 32); } } else { region->size = BCMA_EROM_REGION_SIZE_BASE << size_type; } /* Verify that addr+size does not overflow. */ if (region->size != 0 && BHND_ADDR_MAX - (region->size - 1) < region->base_addr) { EROM_LOG(erom, "%s%u: invalid address map %llx:%llx\n", bcma_erom_entry_type_name(region->region_type), region->region_port, (unsigned long long) region->base_addr, (unsigned long long) region->size); return (EINVAL); } return (0); } /** * Convert a bcma_erom_core record to its bhnd_core_info representation. * * @param core EROM core record to convert. * @param core_idx The core index of @p core. * @param core_unit The core unit of @p core. * @param[out] info The populated bhnd_core_info representation. */ static void bcma_erom_to_core_info(const struct bcma_erom_core *core, u_int core_idx, int core_unit, struct bhnd_core_info *info) { info->vendor = core->vendor; info->device = core->device; info->hwrev = core->rev; info->core_idx = core_idx; info->unit = core_unit; } /** * Map an EROM region type to its corresponding port type. * * @param region_type Region type value. * @param[out] port_type On success, the corresponding port type. */ static int bcma_erom_region_to_port_type(struct bcma_erom *erom, uint8_t region_type, bhnd_port_type *port_type) { switch (region_type) { case BCMA_EROM_REGION_TYPE_DEVICE: *port_type = BHND_PORT_DEVICE; return (0); case BCMA_EROM_REGION_TYPE_BRIDGE: *port_type = BHND_PORT_BRIDGE; return (0); case BCMA_EROM_REGION_TYPE_MWRAP: case BCMA_EROM_REGION_TYPE_SWRAP: *port_type = BHND_PORT_AGENT; return (0); default: EROM_LOG(erom, "unsupported region type %hhx\n", region_type); return (EINVAL); } } /** * Register all MMIO region descriptors for the given slave port. * * @param erom EROM read state. * @param corecfg Core info to be populated with the scanned port regions. * @param port_num Port index for which regions will be parsed. * @param region_type The region type to be parsed. * @param[out] offset The offset at which to perform parsing. On success, this * will be updated to point to the next EROM table entry. */ static int bcma_erom_corecfg_fill_port_regions(struct bcma_erom *erom, struct bcma_corecfg *corecfg, bcma_pid_t port_num, uint8_t region_type) { struct bcma_sport *sport; struct bcma_sport_list *sports; bus_size_t entry_offset; int error; bhnd_port_type port_type; error = 0; /* Determine the port type for this region type. */ error = bcma_erom_region_to_port_type(erom, region_type, &port_type); if (error) return (error); /* Fetch the list to be populated */ sports = bcma_corecfg_get_port_list(corecfg, port_type); /* Allocate a new port descriptor */ sport = bcma_alloc_sport(port_num, port_type); if (sport == NULL) return (ENOMEM); /* Read all address regions defined for this port */ for (bcma_rmid_t region_num = 0;; region_num++) { struct bcma_map *map; struct bcma_erom_sport_region spr; /* No valid port definition should come anywhere near * BCMA_RMID_MAX. */ if (region_num == BCMA_RMID_MAX) { EROM_LOG(erom, "core%u %s%u: region count reached " "upper limit of %u\n", corecfg->core_info.core_idx, bhnd_port_type_name(port_type), port_num, BCMA_RMID_MAX); error = EINVAL; goto cleanup; } /* Parse the next region entry. */ entry_offset = bcma_erom_tell(erom); error = bcma_erom_parse_sport_region(erom, &spr); if (error && error != ENOENT) { EROM_LOG(erom, "core%u %s%u.%u: invalid slave port " "address region\n", corecfg->core_info.core_idx, bhnd_port_type_name(port_type), port_num, region_num); goto cleanup; } /* ENOENT signals no further region entries */ if (error == ENOENT) { /* No further entries */ error = 0; break; } /* A region or type mismatch also signals no further region * entries */ if (spr.region_port != port_num || spr.region_type != region_type) { /* We don't want to consume this entry */ bcma_erom_seek(erom, entry_offset); error = 0; goto cleanup; } /* * Create the map entry. */ map = malloc(sizeof(struct bcma_map), M_BHND, M_NOWAIT); if (map == NULL) { error = ENOMEM; goto cleanup; } map->m_region_num = region_num; map->m_base = spr.base_addr; map->m_size = spr.size; map->m_rid = -1; /* Add the region map to the port */ STAILQ_INSERT_TAIL(&sport->sp_maps, map, m_link); sport->sp_num_maps++; } cleanup: /* Append the new port descriptor on success, or deallocate the * partially parsed descriptor on failure. */ if (error == 0) { STAILQ_INSERT_TAIL(sports, sport, sp_link); } else if (sport != NULL) { bcma_free_sport(sport); } return error; } /** * Parse the next core entry from the EROM table and produce a bcma_corecfg * to be owned by the caller. * * @param erom A bcma EROM instance. * @param[out] result On success, the core's device info. The caller inherits * ownership of this allocation. * * @return If successful, returns 0. If the end of the EROM table is hit, * ENOENT will be returned. On error, returns a non-zero error value. */ int bcma_erom_next_corecfg(struct bcma_erom *erom, struct bcma_corecfg **result) { struct bcma_corecfg *cfg; struct bcma_erom_core core; uint8_t first_region_type; bus_size_t initial_offset; u_int core_index; int core_unit; int error; cfg = NULL; initial_offset = bcma_erom_tell(erom); /* Parse the next core entry */ if ((error = bcma_erom_parse_core(erom, &core))) return (error); /* Determine the core's index and unit numbers */ bcma_erom_reset(erom); core_unit = 0; core_index = 0; for (; bcma_erom_tell(erom) != initial_offset; core_index++) { struct bcma_erom_core prev_core; /* Parse next core */ error = bcma_erom_seek_next(erom, BCMA_EROM_ENTRY_TYPE_CORE); if (error) return (error); if ((error = bcma_erom_parse_core(erom, &prev_core))) return (error); /* Is earlier unit? */ if (core.vendor == prev_core.vendor && core.device == prev_core.device) { core_unit++; } /* Seek to next core */ error = bcma_erom_seek_next(erom, BCMA_EROM_ENTRY_TYPE_CORE); if (error) return (error); } /* We already parsed the core descriptor */ if ((error = bcma_erom_skip_core(erom))) return (error); /* Allocate our corecfg */ cfg = bcma_alloc_corecfg(core_index, core_unit, core.vendor, core.device, core.rev); if (cfg == NULL) return (ENOMEM); /* These are 5-bit values in the EROM table, and should never be able * to overflow BCMA_PID_MAX. */ KASSERT(core.num_mport <= BCMA_PID_MAX, ("unsupported mport count")); KASSERT(core.num_dport <= BCMA_PID_MAX, ("unsupported dport count")); KASSERT(core.num_mwrap + core.num_swrap <= BCMA_PID_MAX, ("unsupported wport count")); if (bootverbose) { EROM_LOG(erom, "core%u: %s %s (cid=%hx, rev=%hu, unit=%d)\n", core_index, bhnd_vendor_name(core.vendor), bhnd_find_core_name(core.vendor, core.device), core.device, core.rev, core_unit); } cfg->num_master_ports = core.num_mport; cfg->num_dev_ports = 0; /* determined below */ cfg->num_bridge_ports = 0; /* determined blow */ cfg->num_wrapper_ports = core.num_mwrap + core.num_swrap; /* Parse Master Port Descriptors */ for (uint8_t i = 0; i < core.num_mport; i++) { struct bcma_mport *mport; struct bcma_erom_mport mpd; /* Parse the master port descriptor */ error = bcma_erom_parse_mport(erom, &mpd); if (error) goto failed; /* Initialize a new bus mport structure */ mport = malloc(sizeof(struct bcma_mport), M_BHND, M_NOWAIT); if (mport == NULL) { error = ENOMEM; goto failed; } mport->mp_vid = mpd.port_vid; mport->mp_num = mpd.port_num; /* Update dinfo */ STAILQ_INSERT_TAIL(&cfg->master_ports, mport, mp_link); } /* * Determine whether this is a bridge device; if so, we can * expect the first sequence of address region descriptors to * be of EROM_REGION_TYPE_BRIDGE instead of * BCMA_EROM_REGION_TYPE_DEVICE. * * It's unclear whether this is the correct mechanism by which we * should detect/handle bridge devices, but this approach matches * that of (some of) Broadcom's published drivers. */ if (core.num_dport > 0) { uint32_t entry; if ((error = bcma_erom_peek32(erom, &entry))) goto failed; if (BCMA_EROM_ENTRY_IS(entry, REGION) && BCMA_EROM_GET_ATTR(entry, REGION_TYPE) == BCMA_EROM_REGION_TYPE_BRIDGE) { first_region_type = BCMA_EROM_REGION_TYPE_BRIDGE; cfg->num_dev_ports = 0; cfg->num_bridge_ports = core.num_dport; } else { first_region_type = BCMA_EROM_REGION_TYPE_DEVICE; cfg->num_dev_ports = core.num_dport; cfg->num_bridge_ports = 0; } } /* Device/bridge port descriptors */ for (uint8_t sp_num = 0; sp_num < core.num_dport; sp_num++) { error = bcma_erom_corecfg_fill_port_regions(erom, cfg, sp_num, first_region_type); if (error) goto failed; } /* Wrapper (aka device management) descriptors (for master ports). */ for (uint8_t sp_num = 0; sp_num < core.num_mwrap; sp_num++) { error = bcma_erom_corecfg_fill_port_regions(erom, cfg, sp_num, BCMA_EROM_REGION_TYPE_MWRAP); if (error) goto failed; } /* Wrapper (aka device management) descriptors (for slave ports). */ for (uint8_t i = 0; i < core.num_swrap; i++) { /* Slave wrapper ports are not numbered distinctly from master * wrapper ports. */ /* * Broadcom DDR1/DDR2 Memory Controller * (cid=82e, rev=1, unit=0, d/mw/sw = 2/0/1 ) -> * bhnd0: erom[0xdc]: core6 agent0.0: mismatch got: 0x1 (0x2) * * ARM BP135 AMBA3 AXI to APB Bridge * (cid=135, rev=0, unit=0, d/mw/sw = 1/0/1 ) -> * bhnd0: erom[0x124]: core9 agent1.0: mismatch got: 0x0 (0x2) * * core.num_mwrap * ===> * (core.num_mwrap > 0) ? * core.num_mwrap : * ((core.vendor == BHND_MFGID_BCM) ? 1 : 0) */ uint8_t sp_num; sp_num = (core.num_mwrap > 0) ? core.num_mwrap : ((core.vendor == BHND_MFGID_BCM) ? 1 : 0) + i; error = bcma_erom_corecfg_fill_port_regions(erom, cfg, sp_num, BCMA_EROM_REGION_TYPE_SWRAP); if (error) goto failed; } *result = cfg; return (0); failed: if (cfg != NULL) bcma_free_corecfg(cfg); return error; } 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), KOBJMETHOD(bhnd_erom_fini, bcma_erom_fini), KOBJMETHOD(bhnd_erom_get_core_table, bcma_erom_get_core_table), KOBJMETHOD(bhnd_erom_free_core_table, bcma_erom_free_core_table), KOBJMETHOD(bhnd_erom_lookup_core, bcma_erom_lookup_core), KOBJMETHOD(bhnd_erom_lookup_core_addr, bcma_erom_lookup_core_addr), KOBJMETHOD_END }; BHND_EROM_DEFINE_CLASS(bcma_erom, bcma_erom_parser, bcma_erom_methods, sizeof(struct bcma_erom)); Index: head/sys/dev/bhnd/bcma/bcma_eromvar.h =================================================================== --- head/sys/dev/bhnd/bcma/bcma_eromvar.h (revision 305370) +++ head/sys/dev/bhnd/bcma/bcma_eromvar.h (revision 305371) @@ -1,93 +1,74 @@ /*- * Copyright (c) 2015 Landon Fuller * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. * * $FreeBSD$ */ #ifndef _BCMA_BCMA_EROMVAR_H_ #define _BCMA_BCMA_EROMVAR_H_ #include #include #include "bcmavar.h" struct bcma_erom; 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 */ uint16_t device; /**< core's device identifier */ uint16_t rev; /**< core's hardware revision */ u_long num_mport; /**< number of master port descriptors */ u_long num_dport; /**< number of slave port descriptors */ u_long num_mwrap; /**< number of master wrapper slave port descriptors */ u_long num_swrap; /**< number of slave wrapper slave port descriptors */ }; /** EROM master port descriptor. */ struct bcma_erom_mport { uint8_t port_num; /**< the port number (bus-unique) */ uint8_t port_vid; /**< the port VID. A single physical master port may have multiple VIDs; the canonical port address is composed of the port number + the port VID */ }; /** EROM slave port region descriptor. */ struct bcma_erom_sport_region { uint8_t region_port; /**< the slave port mapping this region */ uint8_t region_type; /**< the mapping port's type */ bhnd_addr_t base_addr; /**< region base address */ bhnd_addr_t size; /**< region size */ }; #endif /* _BCMA_BCMA_EROMVAR_H_ */ Index: head/sys/dev/bhnd/bcma/bcma_nexus.c =================================================================== --- head/sys/dev/bhnd/bcma/bcma_nexus.c (revision 305370) +++ head/sys/dev/bhnd/bcma/bcma_nexus.c (revision 305371) @@ -1,123 +1,132 @@ /*- * Copyright (c) 2016 Michael Zhilin * Copyright (c) 2015-2016 Landon Fuller * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. * * $FreeBSD$ */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include "bcmavar.h" #include "bcma_eromreg.h" /* * Supports bcma(4) attachment to a nexus bus. */ static int bcma_nexus_attach(device_t); static int bcma_nexus_probe(device_t); struct bcma_nexus_softc { struct bcma_softc parent_sc; struct bhnd_chipid bcma_cid; }; static int bcma_nexus_probe(device_t dev) { struct bcma_nexus_softc *sc; int error; sc = device_get_softc(dev); /* Read the ChipCommon info using the hints the kernel * was compiled with. */ if ((error = bhnd_nexus_read_chipid(dev, &sc->bcma_cid))) return (error); if (sc->bcma_cid.chip_type != BHND_CHIPTYPE_BCMA) return (ENXIO); if ((error = bcma_probe(dev)) > 0) { device_printf(dev, "error %d in probe\n", error); return (error); } /* Set device description */ bhnd_set_default_bus_desc(dev, &sc->bcma_cid); return (0); } static int bcma_nexus_attach(device_t dev) { int error; - if ((error = bcma_add_children(dev))) - return (error); + /* Perform initial attach and enumerate our children. */ + if ((error = bcma_attach(dev))) + goto failed; - return (bcma_attach(dev)); + /* 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 const struct bhnd_chipid * bcma_nexus_get_chipid(device_t dev, device_t child) { struct bcma_nexus_softc *sc = device_get_softc(dev); return (&sc->bcma_cid); } static device_method_t bcma_nexus_methods[] = { /* Device interface */ DEVMETHOD(device_probe, bcma_nexus_probe), DEVMETHOD(device_attach, bcma_nexus_attach), /* bhnd interface */ DEVMETHOD(bhnd_bus_get_chipid, bcma_nexus_get_chipid), DEVMETHOD_END }; DEFINE_CLASS_2(bhnd, bcma_nexus_driver, bcma_nexus_methods, sizeof(struct bcma_nexus_softc), bhnd_nexus_driver, bcma_driver); EARLY_DRIVER_MODULE(bcma_nexus, nexus, bcma_nexus_driver, bhnd_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); Index: head/sys/dev/bhnd/bcma/bcma_subr.c =================================================================== --- head/sys/dev/bhnd/bcma/bcma_subr.c (revision 305370) +++ head/sys/dev/bhnd/bcma/bcma_subr.c (revision 305371) @@ -1,309 +1,370 @@ /*- * Copyright (c) 2015 Landon Fuller * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #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. * * @param core_index Core index on the bus. * @param core_unit Core unit number. * @param vendor Core designer. * @param device Core identifier (e.g. part number). * @param hwrev Core revision. */ struct bcma_corecfg * bcma_alloc_corecfg(u_int core_index, int core_unit, uint16_t vendor, uint16_t device, uint8_t hwrev) { struct bcma_corecfg *cfg; cfg = malloc(sizeof(*cfg), M_BHND, M_NOWAIT); if (cfg == NULL) return NULL; cfg->core_info = (struct bhnd_core_info) { .vendor = vendor, .device = device, .hwrev = hwrev, .core_idx = core_index, .unit = core_unit }; STAILQ_INIT(&cfg->master_ports); cfg->num_master_ports = 0; STAILQ_INIT(&cfg->dev_ports); cfg->num_dev_ports = 0; STAILQ_INIT(&cfg->bridge_ports); cfg->num_bridge_ports = 0; STAILQ_INIT(&cfg->wrapper_ports); cfg->num_wrapper_ports = 0; return (cfg); } /** * Deallocate the given core config and any associated resources. * * @param corecfg Core info to be deallocated. */ void bcma_free_corecfg(struct bcma_corecfg *corecfg) { struct bcma_mport *mport, *mnext; struct bcma_sport *sport, *snext; STAILQ_FOREACH_SAFE(mport, &corecfg->master_ports, mp_link, mnext) { free(mport, M_BHND); } STAILQ_FOREACH_SAFE(sport, &corecfg->dev_ports, sp_link, snext) { bcma_free_sport(sport); } STAILQ_FOREACH_SAFE(sport, &corecfg->bridge_ports, sp_link, snext) { bcma_free_sport(sport); } STAILQ_FOREACH_SAFE(sport, &corecfg->wrapper_ports, sp_link, snext) { bcma_free_sport(sport); } free(corecfg, M_BHND); } /** * Return the @p cfg port list for @p type. * * @param cfg The core configuration. * @param type The requested port type. */ struct bcma_sport_list * bcma_corecfg_get_port_list(struct bcma_corecfg *cfg, bhnd_port_type type) { switch (type) { case BHND_PORT_DEVICE: return (&cfg->dev_ports); break; case BHND_PORT_BRIDGE: return (&cfg->bridge_ports); break; case BHND_PORT_AGENT: return (&cfg->wrapper_ports); break; default: return (NULL); } } /** * Populate the resource list and bcma_map RIDs using the maps defined on * @p ports. * * @param bus The requesting bus device. * @param dinfo The device info instance to be initialized. * @param ports The set of ports to be enumerated */ static void bcma_dinfo_init_resource_info(device_t bus, struct bcma_devinfo *dinfo, struct bcma_sport_list *ports) { struct bcma_map *map; struct bcma_sport *port; bhnd_addr_t end; STAILQ_FOREACH(port, ports, sp_link) { STAILQ_FOREACH(map, &port->sp_maps, m_link) { /* * Create the corresponding device resource list entry. * * We necessarily skip registration if the region's * device memory range is not representable via * rman_res_t. * * When rman_res_t is migrated to uintmax_t, any * range should be representable. */ end = map->m_base + map->m_size; if (map->m_base <= RM_MAX_END && end <= RM_MAX_END) { map->m_rid = resource_list_add_next( &dinfo->resources, SYS_RES_MEMORY, map->m_base, end, map->m_size); } else if (bootverbose) { device_printf(bus, "core%u %s%u.%u: region %llx-%llx extends " "beyond supported addressable range\n", dinfo->corecfg->core_info.core_idx, bhnd_port_type_name(port->sp_type), port->sp_num, map->m_region_num, (unsigned long long) map->m_base, (unsigned long long) end); } } } } /** * Allocate and return a new empty device info structure. * * @param bus The requesting bus device. * * @retval NULL if allocation failed. */ struct bcma_devinfo * bcma_alloc_dinfo(device_t bus) { struct bcma_devinfo *dinfo; dinfo = malloc(sizeof(struct bcma_devinfo), M_BHND, M_NOWAIT|M_ZERO); if (dinfo == NULL) return (NULL); dinfo->corecfg = NULL; dinfo->res_agent = NULL; dinfo->rid_agent = -1; resource_list_init(&dinfo->resources); return (dinfo); } /** * Initialize a device info structure previously allocated via * bcma_alloc_dinfo, assuming ownership of the provided core * configuration. * * @param bus The requesting bus device. * @param dinfo The device info instance. * @param corecfg Device core configuration; ownership of this value * will be assumed by @p dinfo. * * @retval 0 success * @retval non-zero initialization failed. */ int bcma_init_dinfo(device_t bus, struct bcma_devinfo *dinfo, struct bcma_corecfg *corecfg) { KASSERT(dinfo->corecfg == NULL, ("dinfo previously initialized")); /* Save core configuration value */ dinfo->corecfg = corecfg; /* The device ports must always be initialized first to ensure that * rid 0 maps to the first device port */ bcma_dinfo_init_resource_info(bus, dinfo, &corecfg->dev_ports); bcma_dinfo_init_resource_info(bus, dinfo, &corecfg->bridge_ports); bcma_dinfo_init_resource_info(bus, dinfo, &corecfg->wrapper_ports); return (0); } + + +/** + * Allocate the per-core agent register block for a device info structure + * previous initialized via bcma_init_dinfo(). + * + * If an agent0.0 region is not defined on @p dinfo, the device info + * agent resource is set to NULL and 0 is returned. + * + * @param bus The requesting bus device. + * @param child The bcma child device. + * @param dinfo The device info associated with @p child + * + * @retval 0 success + * @retval non-zero resource allocation failed. + */ +int +bcma_dinfo_alloc_agent(device_t bus, device_t child, struct bcma_devinfo *dinfo) +{ + bhnd_addr_t addr; + bhnd_size_t size; + rman_res_t r_start, r_count, r_end; + int error; + + 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. * * @param bus The requesting bus device. * @param dinfo Device info to be deallocated. */ void bcma_free_dinfo(device_t bus, struct bcma_devinfo *dinfo) { resource_list_free(&dinfo->resources); if (dinfo->corecfg != NULL) bcma_free_corecfg(dinfo->corecfg); /* Release agent resource, if any */ if (dinfo->res_agent != NULL) { bhnd_release_resource(bus, SYS_RES_MEMORY, dinfo->rid_agent, dinfo->res_agent); } free(dinfo, M_BHND); } /** * Allocate and initialize new slave port descriptor. * * @param port_num Per-core port number. * @param port_type Port type. */ struct bcma_sport * bcma_alloc_sport(bcma_pid_t port_num, bhnd_port_type port_type) { struct bcma_sport *sport; sport = malloc(sizeof(struct bcma_sport), M_BHND, M_NOWAIT); if (sport == NULL) return NULL; sport->sp_num = port_num; sport->sp_type = port_type; sport->sp_num_maps = 0; STAILQ_INIT(&sport->sp_maps); return sport; } /** * Deallocate all resources associated with the given port descriptor. * * @param sport Port descriptor to be deallocated. */ void bcma_free_sport(struct bcma_sport *sport) { struct bcma_map *map, *mapnext; STAILQ_FOREACH_SAFE(map, &sport->sp_maps, m_link, mapnext) { free(map, M_BHND); } free(sport, M_BHND); } Index: head/sys/dev/bhnd/bcma/bcmavar.h =================================================================== --- head/sys/dev/bhnd/bcma/bcmavar.h (revision 305370) +++ head/sys/dev/bhnd/bcma/bcmavar.h (revision 305371) @@ -1,153 +1,166 @@ /*- * Copyright (c) 2015 Landon Fuller * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. * * $FreeBSD$ */ #ifndef _BCMA_BCMAVAR_H_ #define _BCMA_BCMAVAR_H_ #include #include #include #include #include #include "bcma.h" /* * 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 */ /** BCMA per-port region map identifier. */ typedef u_int bcma_rmid_t; #define BCMA_RMID_MAX UINT_MAX /**< Maximum bcma_rmid_t value */ struct bcma_devinfo; struct bcma_corecfg; struct bcma_map; struct bcma_mport; struct bcma_sport; int bcma_probe(device_t dev); int bcma_attach(device_t dev); int bcma_detach(device_t dev); int bcma_add_children(device_t bus); struct bcma_sport_list *bcma_corecfg_get_port_list(struct bcma_corecfg *cfg, bhnd_port_type type); struct bcma_devinfo *bcma_alloc_dinfo(device_t bus); int bcma_init_dinfo(device_t bus, 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); struct bcma_corecfg *bcma_alloc_corecfg(u_int core_index, int core_unit, uint16_t vendor, uint16_t device, uint8_t hwrev); void bcma_free_corecfg(struct bcma_corecfg *corecfg); struct bcma_sport *bcma_alloc_sport(bcma_pid_t port_num, bhnd_port_type port_type); void bcma_free_sport(struct bcma_sport *sport); /** BCMA master port descriptor */ struct bcma_mport { bcma_pid_t mp_num; /**< AXI port identifier (bus-unique) */ bcma_pid_t mp_vid; /**< AXI master virtual ID (core-unique) */ STAILQ_ENTRY(bcma_mport) mp_link; }; /** BCMA memory region descriptor */ struct bcma_map { bcma_rmid_t m_region_num; /**< region identifier (port-unique). */ bhnd_addr_t m_base; /**< base address */ bhnd_size_t m_size; /**< size */ int m_rid; /**< bus resource id, or -1. */ STAILQ_ENTRY(bcma_map) m_link; }; /** BCMA slave port descriptor */ struct bcma_sport { bcma_pid_t sp_num; /**< slave port number (core-unique) */ bhnd_port_type sp_type; /**< port type */ u_long sp_num_maps; /**< number of regions mapped to this port */ STAILQ_HEAD(, bcma_map) sp_maps; STAILQ_ENTRY(bcma_sport) sp_link; }; STAILQ_HEAD(bcma_mport_list, bcma_mport); STAILQ_HEAD(bcma_sport_list, bcma_sport); /** BCMA IP core/block configuration */ struct bcma_corecfg { struct bhnd_core_info core_info; /**< standard core info */ u_long num_master_ports; /**< number of master port descriptors. */ struct bcma_mport_list master_ports; /**< master port descriptors */ u_long num_dev_ports; /**< number of device slave port descriptors. */ struct bcma_sport_list dev_ports; /**< device port descriptors */ u_long num_bridge_ports; /**< number of bridge slave port descriptors. */ struct bcma_sport_list bridge_ports; /**< bridge port descriptors */ u_long num_wrapper_ports; /**< number of wrapper slave port descriptors. */ struct bcma_sport_list wrapper_ports; /**< wrapper port descriptors */ }; /** * BCMA per-device info */ struct bcma_devinfo { struct bhnd_devinfo bhnd_dinfo; /**< superclass device info. */ struct resource_list resources; /**< Slave port memory regions. */ struct bcma_corecfg *corecfg; /**< IP core/block config */ struct bhnd_resource *res_agent; /**< Agent (wrapper) resource, or NULL. Not * all bcma(4) cores have or require an agent. */ int rid_agent; /**< Agent resource ID, or -1 */ }; /** 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 (revision 305370) +++ head/sys/dev/bhnd/bhnd.h (revision 305371) @@ -1,1203 +1,1226 @@ /*- * Copyright (c) 2015 Landon Fuller * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. * * $FreeBSD$ */ #ifndef _BHND_BHND_H_ #define _BHND_BHND_H_ #include #include #include #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" #include "nvram/bhnd_nvram.h" extern devclass_t bhnd_devclass; extern devclass_t bhnd_hostb_devclass; extern devclass_t bhnd_nvram_devclass; #define BHND_CHIPID_MAX_NAMELEN 32 /**< maximum buffer required for a bhnd_format_chip_id() */ /** * bhnd child instance variables */ enum bhnd_device_vars { BHND_IVAR_VENDOR, /**< Designer's JEP-106 manufacturer ID. */ BHND_IVAR_DEVICE, /**< Part number */ BHND_IVAR_HWREV, /**< Core revision */ BHND_IVAR_DEVICE_CLASS, /**< Core class (@sa bhnd_devclass_t) */ BHND_IVAR_VENDOR_NAME, /**< Core vendor name */ BHND_IVAR_DEVICE_NAME, /**< Core name */ BHND_IVAR_CORE_INDEX, /**< Bus-assigned core number */ BHND_IVAR_CORE_UNIT, /**< Bus-assigned core unit number, assigned sequentially (starting at 0) for each vendor/device pair. */ }; /** * bhnd device probe priority bands. */ enum { BHND_PROBE_ROOT = 0, /**< Nexus or host bridge */ BHND_PROBE_BUS = 1000, /**< Busses and bridges */ BHND_PROBE_CPU = 2000, /**< CPU devices */ BHND_PROBE_INTERRUPT = 3000, /**< Interrupt controllers. */ BHND_PROBE_TIMER = 4000, /**< Timers and clocks. */ BHND_PROBE_RESOURCE = 5000, /**< Resource discovery (including NVRAM/SPROM) */ BHND_PROBE_DEFAULT = 6000, /**< Default device priority */ }; /** * Constants defining fine grained ordering within a BHND_PROBE_* priority band. * * Example: * @code * BHND_PROBE_BUS + BHND_PROBE_ORDER_FIRST * @endcode */ enum { BHND_PROBE_ORDER_FIRST = 0, BHND_PROBE_ORDER_EARLY = 25, BHND_PROBE_ORDER_MIDDLE = 50, BHND_PROBE_ORDER_LATE = 75, BHND_PROBE_ORDER_LAST = 100 }; /* * Simplified accessors for bhnd device ivars */ #define BHND_ACCESSOR(var, ivar, type) \ __BUS_ACCESSOR(bhnd, var, BHND, ivar, type) BHND_ACCESSOR(vendor, VENDOR, uint16_t); BHND_ACCESSOR(device, DEVICE, uint16_t); BHND_ACCESSOR(hwrev, HWREV, uint8_t); BHND_ACCESSOR(class, DEVICE_CLASS, bhnd_devclass_t); BHND_ACCESSOR(vendor_name, VENDOR_NAME, const char *); BHND_ACCESSOR(device_name, DEVICE_NAME, const char *); BHND_ACCESSOR(core_index, CORE_INDEX, u_int); BHND_ACCESSOR(core_unit, CORE_UNIT, int); #undef BHND_ACCESSOR /** * A bhnd(4) board descriptor. */ struct bhnd_board_info { uint16_t board_vendor; /**< PCI-SIG vendor ID (even on non-PCI * devices). * * On PCI devices, this will generally * be the subsystem vendor ID, but the * value may be overridden in device * NVRAM. */ uint16_t board_type; /**< Board type (See BHND_BOARD_*) * * On PCI devices, this will generally * be the subsystem device ID, but the * value may be overridden in device * NVRAM. */ uint16_t board_rev; /**< Board revision. */ uint8_t board_srom_rev; /**< Board SROM format revision */ uint32_t board_flags; /**< Board flags (see BHND_BFL_*) */ uint32_t board_flags2; /**< Board flags 2 (see BHND_BFL2_*) */ uint32_t board_flags3; /**< Board flags 3 (see BHND_BFL3_*) */ }; /** * Chip Identification * * This is read from the ChipCommon ID register; on earlier bhnd(4) devices * where ChipCommon is unavailable, known values must be supplied. */ struct bhnd_chipid { uint16_t chip_id; /**< chip id (BHND_CHIPID_*) */ uint8_t chip_rev; /**< chip revision */ uint8_t chip_pkg; /**< chip package (BHND_PKGID_*) */ uint8_t chip_type; /**< chip type (BHND_CHIPTYPE_*) */ bhnd_addr_t enum_addr; /**< chip_type-specific enumeration * address; either the siba(4) base * core register block, or the bcma(4) * EROM core address. */ uint8_t ncores; /**< number of cores, if known. 0 if * not available. */ }; /** * A bhnd(4) core descriptor. */ struct bhnd_core_info { uint16_t vendor; /**< JEP-106 vendor (BHND_MFGID_*) */ uint16_t device; /**< device */ uint16_t hwrev; /**< hardware revision */ u_int core_idx; /**< bus-assigned core index */ int unit; /**< bus-assigned core unit */ }; /** * A bhnd(4) bus resource. * * This provides an abstract interface to per-core resources that may require * bus-level remapping of address windows prior to access. */ struct bhnd_resource { struct resource *res; /**< the system resource. */ bool direct; /**< false if the resource requires * bus window remapping before it * 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. */ struct bhnd_device_quirk { struct bhnd_device_match desc; /**< device match descriptor */ uint32_t quirks; /**< quirk flags */ }; #define BHND_CORE_QUIRK(_rev, _flags) \ {{ BHND_MATCH_CORE_REV(_rev) }, (_flags) } #define BHND_CHIP_QUIRK(_chip, _rev, _flags) \ {{ BHND_CHIP_IR(BCM ## _chip, _rev) }, (_flags) } #define BHND_PKG_QUIRK(_chip, _pkg, _flags) \ {{ BHND_CHIP_IP(BCM ## _chip, BCM ## _chip ## _pkg) }, (_flags) } #define BHND_BOARD_QUIRK(_board, _flags) \ {{ BHND_MATCH_BOARD_TYPE(_board) }, \ (_flags) } #define BHND_DEVICE_QUIRK_END { { BHND_MATCH_ANY }, 0 } #define BHND_DEVICE_QUIRK_IS_END(_q) \ (((_q)->desc.m.match_flags == 0) && (_q)->quirks == 0) enum { BHND_DF_ANY = 0, BHND_DF_HOSTB = (1<<0), /**< core is serving as the bus' host * bridge. implies BHND_DF_ADAPTER */ BHND_DF_SOC = (1<<1), /**< core is attached to a native bus (BHND_ATTACH_NATIVE) */ BHND_DF_ADAPTER = (1<<2), /**< core is attached to a bridged * adapter (BHND_ATTACH_ADAPTER) */ }; /** Device probe table descriptor */ struct bhnd_device { const struct bhnd_device_match core; /**< core match descriptor */ const char *desc; /**< device description, or NULL. */ const struct bhnd_device_quirk *quirks_table; /**< quirks table for this device, or NULL */ uint32_t device_flags; /**< required BHND_DF_* flags */ }; #define _BHND_DEVICE(_vendor, _device, _desc, _quirks, \ _flags, ...) \ { { BHND_MATCH_CORE(BHND_MFGID_ ## _vendor, \ BHND_COREID_ ## _device) }, _desc, _quirks, \ _flags } #define BHND_DEVICE(_vendor, _device, _desc, _quirks, ...) \ _BHND_DEVICE(_vendor, _device, _desc, _quirks, \ ## __VA_ARGS__, 0) #define BHND_DEVICE_END { { BHND_MATCH_ANY }, NULL, NULL, 0 } #define BHND_DEVICE_IS_END(_d) \ (BHND_MATCH_IS_ANY(&(_d)->core) && (_d)->desc == NULL) const char *bhnd_vendor_name(uint16_t vendor); const char *bhnd_port_type_name(bhnd_port_type port_type); const char *bhnd_nvram_src_name(bhnd_nvram_src nvram_src); const char *bhnd_find_core_name(uint16_t vendor, uint16_t device); bhnd_devclass_t bhnd_find_core_class(uint16_t vendor, uint16_t device); const char *bhnd_core_name(const struct bhnd_core_info *ci); bhnd_devclass_t bhnd_core_class(const struct bhnd_core_info *ci); int bhnd_format_chip_id(char *buffer, size_t size, uint16_t chip_id); device_t bhnd_match_child(device_t dev, const struct bhnd_core_match *desc); device_t bhnd_find_child(device_t dev, bhnd_devclass_t class, int unit); device_t bhnd_find_bridge_root(device_t dev, devclass_t bus_class); const struct bhnd_core_info *bhnd_match_core( const struct bhnd_core_info *cores, u_int num_cores, const struct bhnd_core_match *desc); const struct bhnd_core_info *bhnd_find_core( 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); bool bhnd_chip_matches( const struct bhnd_chipid *chipid, const struct bhnd_chip_match *desc); bool bhnd_board_matches( const struct bhnd_board_info *info, const struct bhnd_board_match *desc); bool bhnd_hwrev_matches(uint16_t hwrev, const struct bhnd_hwrev_match *desc); bool bhnd_device_matches(device_t dev, const struct bhnd_device_match *desc); const struct bhnd_device *bhnd_device_lookup(device_t dev, const struct bhnd_device *table, size_t entry_size); uint32_t bhnd_device_quirks(device_t dev, const struct bhnd_device *table, size_t entry_size); struct bhnd_core_info bhnd_get_core_info(device_t dev); int bhnd_alloc_resources(device_t dev, struct resource_spec *rs, struct bhnd_resource **res); void bhnd_release_resources(device_t dev, const struct resource_spec *rs, struct bhnd_resource **res); struct bhnd_chipid bhnd_parse_chipid(uint32_t idreg, bhnd_addr_t enum_addr); int bhnd_chipid_fixed_ncores( const struct bhnd_chipid *cid, uint16_t chipc_hwrev, uint8_t *ncores); int bhnd_read_chipid(device_t dev, struct resource_spec *rs, bus_size_t chipc_offset, struct bhnd_chipid *result); void bhnd_set_custom_core_desc(device_t dev, const char *name); void bhnd_set_default_core_desc(device_t dev); void bhnd_set_default_bus_desc(device_t dev, const struct bhnd_chipid *chip_id); int bhnd_nvram_getvar_str(device_t dev, const char *name, char *buf, size_t len, size_t *rlen); int bhnd_nvram_getvar_uint(device_t dev, const char *name, void *value, int width); int bhnd_nvram_getvar_uint8(device_t dev, const char *name, uint8_t *value); int bhnd_nvram_getvar_uint16(device_t dev, const char *name, uint16_t *value); int bhnd_nvram_getvar_uint32(device_t dev, const char *name, uint32_t *value); int bhnd_nvram_getvar_int(device_t dev, const char *name, void *value, int width); int bhnd_nvram_getvar_int8(device_t dev, const char *name, int8_t *value); int bhnd_nvram_getvar_int16(device_t dev, const char *name, int16_t *value); int bhnd_nvram_getvar_int32(device_t dev, const char *name, int32_t *value); int bhnd_nvram_getvar_array(device_t dev, const char *name, void *buf, size_t count, bhnd_nvram_type type); bool bhnd_bus_generic_is_hw_disabled(device_t dev, device_t child); bool bhnd_bus_generic_is_region_valid(device_t dev, device_t child, bhnd_port_type type, u_int port, u_int region); int bhnd_bus_generic_get_nvram_var(device_t dev, device_t child, const char *name, void *buf, size_t *size, bhnd_nvram_type type); const struct bhnd_chipid *bhnd_bus_generic_get_chipid(device_t dev, device_t child); int bhnd_bus_generic_read_board_info(device_t dev, device_t child, struct bhnd_board_info *info); struct bhnd_resource *bhnd_bus_generic_alloc_resource (device_t dev, device_t child, int type, int *rid, rman_res_t start, rman_res_t end, rman_res_t count, u_int flags); int bhnd_bus_generic_release_resource (device_t dev, device_t child, int type, int rid, struct bhnd_resource *r); int bhnd_bus_generic_activate_resource (device_t dev, device_t child, int type, int rid, struct bhnd_resource *r); int bhnd_bus_generic_deactivate_resource (device_t dev, device_t child, int type, int rid, struct bhnd_resource *r); bhnd_attach_type bhnd_bus_generic_get_attach_type(device_t dev, device_t child); - +/** + * 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 * not found. * * @param dev A bhnd bus device. */ static inline device_t bhnd_find_hostb_device(device_t dev) { return (BHND_BUS_FIND_HOSTB_DEVICE(dev)); } /** * Return true if the hardware components required by @p dev are known to be * unpopulated or otherwise unusable. * * In some cases, enumerated devices may have pins that are left floating, or * the hardware may otherwise be non-functional; this method allows a parent * device to explicitly specify if a successfully enumerated @p dev should * be disabled. * * @param dev A bhnd bus child device. */ static inline bool bhnd_is_hw_disabled(device_t dev) { return (BHND_BUS_IS_HW_DISABLED(device_get_parent(dev), dev)); } /** * Return the BHND chip identification info for the bhnd bus. * * @param dev A bhnd bus child device. */ static inline const struct bhnd_chipid * bhnd_get_chipid(device_t dev) { return (BHND_BUS_GET_CHIPID(device_get_parent(dev), dev)); }; /** * If supported by the chipset, return the clock source for the given clock. * * This function is only supported on early PWRCTL-equipped chipsets * that expose clock management via their host bridge interface. Currently, * this includes PCI (not PCIe) devices, with ChipCommon core revisions 0-9. * * @param dev A bhnd bus child device. * @param clock The clock for which a clock source will be returned. * * @retval bhnd_clksrc The clock source for @p clock. * @retval BHND_CLKSRC_UNKNOWN If @p clock is unsupported, or its * clock source is not known to the bus. */ static inline bhnd_clksrc bhnd_pwrctl_get_clksrc(device_t dev, bhnd_clock clock) { return (BHND_BUS_PWRCTL_GET_CLKSRC(device_get_parent(dev), dev, clock)); } /** * If supported by the chipset, gate @p clock * * This function is only supported on early PWRCTL-equipped chipsets * that expose clock management via their host bridge interface. Currently, * this includes PCI (not PCIe) devices, with ChipCommon core revisions 0-9. * * @param dev A bhnd bus child device. * @param clock The clock to be disabled. * * @retval 0 success * @retval ENODEV If bus-level clock source management is not supported. * @retval ENXIO If bus-level management of @p clock is not supported. */ static inline int bhnd_pwrctl_gate_clock(device_t dev, bhnd_clock clock) { return (BHND_BUS_PWRCTL_GATE_CLOCK(device_get_parent(dev), dev, clock)); } /** * If supported by the chipset, ungate @p clock * * This function is only supported on early PWRCTL-equipped chipsets * that expose clock management via their host bridge interface. Currently, * this includes PCI (not PCIe) devices, with ChipCommon core revisions 0-9. * * @param dev A bhnd bus child device. * @param clock The clock to be enabled. * * @retval 0 success * @retval ENODEV If bus-level clock source management is not supported. * @retval ENXIO If bus-level management of @p clock is not supported. */ static inline int bhnd_pwrctl_ungate_clock(device_t dev, bhnd_clock clock) { return (BHND_BUS_PWRCTL_UNGATE_CLOCK(device_get_parent(dev), dev, clock)); } /** * Return the BHND attachment type of the parent bhnd bus. * * @param dev A bhnd bus child device. * * @retval BHND_ATTACH_ADAPTER if the bus is resident on a bridged adapter, * such as a WiFi chipset. * @retval BHND_ATTACH_NATIVE if the bus provides hardware services (clock, * CPU, etc) to a directly attached native host. */ static inline bhnd_attach_type bhnd_get_attach_type (device_t dev) { return (BHND_BUS_GET_ATTACH_TYPE(device_get_parent(dev), dev)); } /** * Attempt to read the BHND board identification from the bhnd bus. * * This relies on NVRAM access, and will fail if a valid NVRAM device cannot * be found, or is not yet attached. * * @param dev The parent of @p child. * @param child The bhnd device requesting board info. * @param[out] info On success, will be populated with the bhnd(4) device's * board information. * * @retval 0 success * @retval ENODEV No valid NVRAM source could be found. * @retval non-zero If reading @p name otherwise fails, a regular unix * error code will be returned. */ static inline int bhnd_read_board_info(device_t dev, struct bhnd_board_info *info) { return (BHND_BUS_READ_BOARD_INFO(device_get_parent(dev), dev, info)); } /** * Allocate and enable per-core PMU request handling for @p child. * * The region containing the core's PMU register block (if any) must be * allocated via bus_alloc_resource(9) (or bhnd_alloc_resource) before * calling bhnd_alloc_pmu(), and must not be released until after * calling bhnd_release_pmu(). * * @param dev The parent of @p child. * @param child The requesting bhnd device. * * @retval 0 success * @retval non-zero If allocating PMU request state otherwise fails, a * regular unix error code will be returned. */ static inline int bhnd_alloc_pmu(device_t dev) { return (BHND_BUS_ALLOC_PMU(device_get_parent(dev), dev)); } /** * Release any per-core PMU resources allocated for @p child. Any outstanding * PMU requests are are discarded. * * @param dev The parent of @p child. * @param child The requesting bhnd device. * * @retval 0 success * @retval non-zero If releasing PMU request state otherwise fails, a * regular unix error code will be returned, and * the core state will be left unmodified. */ static inline int bhnd_release_pmu(device_t dev) { return (BHND_BUS_RELEASE_PMU(device_get_parent(dev), dev)); } /** * Request that @p clock (or faster) be routed to @p dev. * * A driver must ask the bhnd bus to allocate clock request state * via bhnd_alloc_pmu() before it can request clock resources. * * Request multiplexing is managed by the bus. * * @param dev The bhnd(4) device to which @p clock should be routed. * @param clock The requested clock source. * * @retval 0 success * @retval ENODEV If an unsupported clock was requested. * @retval ENXIO If the PMU has not been initialized or is otherwise unvailable. */ static inline int bhnd_request_clock(device_t dev, bhnd_clock clock) { return (BHND_BUS_REQUEST_CLOCK(device_get_parent(dev), dev, clock)); } /** * Request that @p clocks be powered on behalf of @p dev. * * This will power any clock sources (e.g. XTAL, PLL, etc) required for * @p clocks and wait until they are ready, discarding any previous * requests by @p dev. * * Request multiplexing is managed by the bus. * * A driver must ask the bhnd bus to allocate clock request state * via bhnd_alloc_pmu() before it can request clock resources. * * @param dev The requesting bhnd(4) device. * @param clocks The clock(s) to be enabled. * * @retval 0 success * @retval ENODEV If an unsupported clock was requested. * @retval ENXIO If the PMU has not been initialized or is otherwise unvailable. */ static inline int bhnd_enable_clocks(device_t dev, uint32_t clocks) { return (BHND_BUS_ENABLE_CLOCKS(device_get_parent(dev), dev, clocks)); } /** * Power up an external PMU-managed resource assigned to @p dev. * * A driver must ask the bhnd bus to allocate PMU request state * via bhnd_alloc_pmu() before it can request PMU resources. * * @param dev The requesting bhnd(4) device. * @param rsrc The core-specific external resource identifier. * * @retval 0 success * @retval ENODEV If the PMU does not support @p rsrc. * @retval ENXIO If the PMU has not been initialized or is otherwise unvailable. */ static inline int bhnd_request_ext_rsrc(device_t dev, u_int rsrc) { return (BHND_BUS_REQUEST_EXT_RSRC(device_get_parent(dev), dev, rsrc)); } /** * Power down an external PMU-managed resource assigned to @p dev. * * A driver must ask the bhnd bus to allocate PMU request state * via bhnd_alloc_pmu() before it can request PMU resources. * * @param dev The requesting bhnd(4) device. * @param rsrc The core-specific external resource identifier. * * @retval 0 success * @retval ENODEV If the PMU does not support @p rsrc. * @retval ENXIO If the PMU has not been initialized or is otherwise unvailable. */ static inline int bhnd_release_ext_rsrc(device_t dev, u_int rsrc) { return (BHND_BUS_RELEASE_EXT_RSRC(device_get_parent(dev), dev, rsrc)); } /** * Read @p width bytes at @p offset from the bus-specific agent/config * space of @p dev. * * @param dev The bhnd device for which @p offset should be read. * @param offset The offset to be read. * @param width The size of the access. Must be 1, 2 or 4 bytes. * * The exact behavior of this method is bus-specific. In the case of * bcma(4), this method provides access to the first agent port of @p child. * * @note Device drivers should only use this API for functionality * that is not available via another bhnd(4) function. */ static inline uint32_t bhnd_read_config(device_t dev, bus_size_t offset, u_int width) { return (BHND_BUS_READ_CONFIG(device_get_parent(dev), dev, offset, width)); } /** * Read @p width bytes at @p offset from the bus-specific agent/config * space of @p dev. * * @param dev The bhnd device for which @p offset should be read. * @param offset The offset to be written. * @param width The size of the access. Must be 1, 2 or 4 bytes. * * The exact behavior of this method is bus-specific. In the case of * bcma(4), this method provides access to the first agent port of @p child. * * @note Device drivers should only use this API for functionality * that is not available via another bhnd(4) function. */ static inline void bhnd_write_config(device_t dev, bus_size_t offset, uint32_t val, u_int width) { BHND_BUS_WRITE_CONFIG(device_get_parent(dev), dev, offset, val, width); } /** * Read an NVRAM variable, coerced to the requested @p type. * * @param dev A bhnd bus child device. * @param name The NVRAM variable name. * @param[out] buf A buffer large enough to hold @p len bytes. On * success, the requested value will be written to * this buffer. This argment may be NULL if * the value is not desired. * @param[in,out] len The maximum capacity of @p buf. On success, * will be set to the actual size of the requested * value. * @param type The desired data representation to be written * to @p buf. * * @retval 0 success * @retval ENOENT The requested variable was not found. * @retval ENODEV No valid NVRAM source could be found. * @retval ENOMEM If a buffer of @p size is too small to hold the * requested value. * @retval EOPNOTSUPP If the value cannot be coerced to @p type. * @retval ERANGE If value coercion would overflow @p type. * @retval non-zero If reading @p name otherwise fails, a regular unix * error code will be returned. */ static inline int bhnd_nvram_getvar(device_t dev, const char *name, void *buf, size_t *len, bhnd_nvram_type type) { return (BHND_BUS_GET_NVRAM_VAR(device_get_parent(dev), dev, name, buf, len, type)); } /** * Allocate a resource from a device's parent bhnd(4) bus. * * @param dev The device requesting resource ownership. * @param type The type of resource to allocate. This may be any type supported * by the standard bus APIs. * @param rid The bus-specific handle identifying the resource being allocated. * @param start The start address of the resource. * @param end The end address of the resource. * @param count The size of the resource. * @param flags The flags for the resource to be allocated. These may be any * values supported by the standard bus APIs. * * To request the resource's default addresses, pass @p start and * @p end values of @c 0 and @c ~0, respectively, and * a @p count of @c 1. * * @retval NULL The resource could not be allocated. * @retval resource The allocated resource. */ static inline struct bhnd_resource * bhnd_alloc_resource(device_t dev, int type, int *rid, rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) { return BHND_BUS_ALLOC_RESOURCE(device_get_parent(dev), dev, type, rid, start, end, count, flags); } /** * Allocate a resource from a device's parent bhnd(4) bus, using the * resource's default start, end, and count values. * * @param dev The device requesting resource ownership. * @param type The type of resource to allocate. This may be any type supported * by the standard bus APIs. * @param rid The bus-specific handle identifying the resource being allocated. * @param flags The flags for the resource to be allocated. These may be any * values supported by the standard bus APIs. * * @retval NULL The resource could not be allocated. * @retval resource The allocated resource. */ static inline struct bhnd_resource * bhnd_alloc_resource_any(device_t dev, int type, int *rid, u_int flags) { return bhnd_alloc_resource(dev, type, rid, 0, ~0, 1, flags); } /** * Activate a previously allocated bhnd resource. * * @param dev The device holding ownership of the allocated resource. * @param type The type of the resource. * @param rid The bus-specific handle identifying the resource. * @param r A pointer to the resource returned by bhnd_alloc_resource or * BHND_BUS_ALLOC_RESOURCE. * * @retval 0 success * @retval non-zero an error occurred while activating the resource. */ static inline int bhnd_activate_resource(device_t dev, int type, int rid, struct bhnd_resource *r) { return BHND_BUS_ACTIVATE_RESOURCE(device_get_parent(dev), dev, type, rid, r); } /** * Deactivate a previously activated bhnd resource. * * @param dev The device holding ownership of the activated resource. * @param type The type of the resource. * @param rid The bus-specific handle identifying the resource. * @param r A pointer to the resource returned by bhnd_alloc_resource or * BHND_BUS_ALLOC_RESOURCE. * * @retval 0 success * @retval non-zero an error occurred while activating the resource. */ static inline int bhnd_deactivate_resource(device_t dev, int type, int rid, struct bhnd_resource *r) { return BHND_BUS_DEACTIVATE_RESOURCE(device_get_parent(dev), dev, type, rid, r); } /** * Free a resource allocated by bhnd_alloc_resource(). * * @param dev The device holding ownership of the resource. * @param type The type of the resource. * @param rid The bus-specific handle identifying the resource. * @param r A pointer to the resource returned by bhnd_alloc_resource or * BHND_ALLOC_RESOURCE. * * @retval 0 success * @retval non-zero an error occurred while activating the resource. */ static inline int bhnd_release_resource(device_t dev, int type, int rid, struct bhnd_resource *r) { return BHND_BUS_RELEASE_RESOURCE(device_get_parent(dev), dev, type, rid, r); } /** * Return true if @p region_num is a valid region on @p port_num of * @p type attached to @p dev. * * @param dev A bhnd bus child device. * @param type The port type being queried. * @param port_num The port number being queried. * @param region_num The region number being queried. */ static inline bool bhnd_is_region_valid(device_t dev, bhnd_port_type type, u_int port_num, u_int region_num) { return (BHND_BUS_IS_REGION_VALID(device_get_parent(dev), dev, type, port_num, region_num)); } /** * Return the number of ports of type @p type attached to @p def. * * @param dev A bhnd bus child device. * @param type The port type being queried. */ static inline u_int bhnd_get_port_count(device_t dev, bhnd_port_type type) { return (BHND_BUS_GET_PORT_COUNT(device_get_parent(dev), dev, type)); } /** * Return the number of memory regions mapped to @p child @p port of * type @p type. * * @param dev A bhnd bus child device. * @param port The port number being queried. * @param type The port type being queried. */ static inline u_int bhnd_get_region_count(device_t dev, bhnd_port_type type, u_int port) { return (BHND_BUS_GET_REGION_COUNT(device_get_parent(dev), dev, type, port)); } /** * Return the resource-ID for a memory region on the given device port. * * @param dev A bhnd bus child device. * @param type The port type. * @param port The port identifier. * @param region The identifier of the memory region on @p port. * * @retval int The RID for the given @p port and @p region on @p device. * @retval -1 No such port/region found. */ static inline int bhnd_get_port_rid(device_t dev, bhnd_port_type type, u_int port, u_int region) { return BHND_BUS_GET_PORT_RID(device_get_parent(dev), dev, type, port, region); } /** * Decode a port / region pair on @p dev defined by @p rid. * * @param dev A bhnd bus child device. * @param type The resource type. * @param rid The resource identifier. * @param[out] port_type The decoded port type. * @param[out] port The decoded port identifier. * @param[out] region The decoded region identifier. * * @retval 0 success * @retval non-zero No matching port/region found. */ static inline int bhnd_decode_port_rid(device_t dev, int type, int rid, bhnd_port_type *port_type, u_int *port, u_int *region) { return BHND_BUS_DECODE_PORT_RID(device_get_parent(dev), dev, type, rid, port_type, port, region); } /** * Get the address and size of @p region on @p port. * * @param dev A bhnd bus child device. * @param port_type The port type. * @param port The port identifier. * @param region The identifier of the memory region on @p port. * @param[out] region_addr The region's base address. * @param[out] region_size The region's size. * * @retval 0 success * @retval non-zero No matching port/region found. */ static inline int bhnd_get_region_addr(device_t dev, bhnd_port_type port_type, u_int port, u_int region, bhnd_addr_t *region_addr, bhnd_size_t *region_size) { return BHND_BUS_GET_REGION_ADDR(device_get_parent(dev), dev, port_type, port, region, region_addr, region_size); } /* * bhnd bus-level equivalents of the bus_(read|write|set|barrier|...) * macros (compatible with bhnd_resource). * * Generated with bhnd/tools/bus_macro.sh */ #define bhnd_bus_barrier(r, o, l, f) \ ((r)->direct) ? \ bus_barrier((r)->res, (o), (l), (f)) : \ BHND_BUS_BARRIER( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (l), (f)) #define bhnd_bus_read_1(r, o) \ ((r)->direct) ? \ bus_read_1((r)->res, (o)) : \ BHND_BUS_READ_1( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o)) #define bhnd_bus_read_multi_1(r, o, d, c) \ ((r)->direct) ? \ bus_read_multi_1((r)->res, (o), (d), (c)) : \ BHND_BUS_READ_MULTI_1( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (d), (c)) #define bhnd_bus_read_region_1(r, o, d, c) \ ((r)->direct) ? \ bus_read_region_1((r)->res, (o), (d), (c)) : \ BHND_BUS_READ_REGION_1( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (d), (c)) #define bhnd_bus_write_1(r, o, v) \ ((r)->direct) ? \ bus_write_1((r)->res, (o), (v)) : \ BHND_BUS_WRITE_1( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (v)) #define bhnd_bus_write_multi_1(r, o, d, c) \ ((r)->direct) ? \ bus_write_multi_1((r)->res, (o), (d), (c)) : \ BHND_BUS_WRITE_MULTI_1( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (d), (c)) #define bhnd_bus_write_region_1(r, o, d, c) \ ((r)->direct) ? \ bus_write_region_1((r)->res, (o), (d), (c)) : \ BHND_BUS_WRITE_REGION_1( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (d), (c)) #define bhnd_bus_read_stream_1(r, o) \ ((r)->direct) ? \ bus_read_stream_1((r)->res, (o)) : \ BHND_BUS_READ_STREAM_1( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o)) #define bhnd_bus_read_multi_stream_1(r, o, d, c) \ ((r)->direct) ? \ bus_read_multi_stream_1((r)->res, (o), (d), (c)) : \ BHND_BUS_READ_MULTI_STREAM_1( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (d), (c)) #define bhnd_bus_read_region_stream_1(r, o, d, c) \ ((r)->direct) ? \ bus_read_region_stream_1((r)->res, (o), (d), (c)) : \ BHND_BUS_READ_REGION_STREAM_1( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (d), (c)) #define bhnd_bus_write_stream_1(r, o, v) \ ((r)->direct) ? \ bus_write_stream_1((r)->res, (o), (v)) : \ BHND_BUS_WRITE_STREAM_1( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (v)) #define bhnd_bus_write_multi_stream_1(r, o, d, c) \ ((r)->direct) ? \ bus_write_multi_stream_1((r)->res, (o), (d), (c)) : \ BHND_BUS_WRITE_MULTI_STREAM_1( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (d), (c)) #define bhnd_bus_write_region_stream_1(r, o, d, c) \ ((r)->direct) ? \ bus_write_region_stream_1((r)->res, (o), (d), (c)) : \ BHND_BUS_WRITE_REGION_STREAM_1( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (d), (c)) #define bhnd_bus_set_multi_1(r, o, v, c) \ ((r)->direct) ? \ bus_set_multi_1((r)->res, (o), (v), (c)) : \ BHND_BUS_SET_MULTI_1( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (v), (c)) #define bhnd_bus_set_region_1(r, o, v, c) \ ((r)->direct) ? \ bus_set_region_1((r)->res, (o), (v), (c)) : \ BHND_BUS_SET_REGION_1( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (v), (c)) #define bhnd_bus_read_2(r, o) \ ((r)->direct) ? \ bus_read_2((r)->res, (o)) : \ BHND_BUS_READ_2( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o)) #define bhnd_bus_read_multi_2(r, o, d, c) \ ((r)->direct) ? \ bus_read_multi_2((r)->res, (o), (d), (c)) : \ BHND_BUS_READ_MULTI_2( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (d), (c)) #define bhnd_bus_read_region_2(r, o, d, c) \ ((r)->direct) ? \ bus_read_region_2((r)->res, (o), (d), (c)) : \ BHND_BUS_READ_REGION_2( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (d), (c)) #define bhnd_bus_write_2(r, o, v) \ ((r)->direct) ? \ bus_write_2((r)->res, (o), (v)) : \ BHND_BUS_WRITE_2( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (v)) #define bhnd_bus_write_multi_2(r, o, d, c) \ ((r)->direct) ? \ bus_write_multi_2((r)->res, (o), (d), (c)) : \ BHND_BUS_WRITE_MULTI_2( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (d), (c)) #define bhnd_bus_write_region_2(r, o, d, c) \ ((r)->direct) ? \ bus_write_region_2((r)->res, (o), (d), (c)) : \ BHND_BUS_WRITE_REGION_2( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (d), (c)) #define bhnd_bus_read_stream_2(r, o) \ ((r)->direct) ? \ bus_read_stream_2((r)->res, (o)) : \ BHND_BUS_READ_STREAM_2( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o)) #define bhnd_bus_read_multi_stream_2(r, o, d, c) \ ((r)->direct) ? \ bus_read_multi_stream_2((r)->res, (o), (d), (c)) : \ BHND_BUS_READ_MULTI_STREAM_2( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (d), (c)) #define bhnd_bus_read_region_stream_2(r, o, d, c) \ ((r)->direct) ? \ bus_read_region_stream_2((r)->res, (o), (d), (c)) : \ BHND_BUS_READ_REGION_STREAM_2( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (d), (c)) #define bhnd_bus_write_stream_2(r, o, v) \ ((r)->direct) ? \ bus_write_stream_2((r)->res, (o), (v)) : \ BHND_BUS_WRITE_STREAM_2( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (v)) #define bhnd_bus_write_multi_stream_2(r, o, d, c) \ ((r)->direct) ? \ bus_write_multi_stream_2((r)->res, (o), (d), (c)) : \ BHND_BUS_WRITE_MULTI_STREAM_2( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (d), (c)) #define bhnd_bus_write_region_stream_2(r, o, d, c) \ ((r)->direct) ? \ bus_write_region_stream_2((r)->res, (o), (d), (c)) : \ BHND_BUS_WRITE_REGION_STREAM_2( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (d), (c)) #define bhnd_bus_set_multi_2(r, o, v, c) \ ((r)->direct) ? \ bus_set_multi_2((r)->res, (o), (v), (c)) : \ BHND_BUS_SET_MULTI_2( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (v), (c)) #define bhnd_bus_set_region_2(r, o, v, c) \ ((r)->direct) ? \ bus_set_region_2((r)->res, (o), (v), (c)) : \ BHND_BUS_SET_REGION_2( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (v), (c)) #define bhnd_bus_read_4(r, o) \ ((r)->direct) ? \ bus_read_4((r)->res, (o)) : \ BHND_BUS_READ_4( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o)) #define bhnd_bus_read_multi_4(r, o, d, c) \ ((r)->direct) ? \ bus_read_multi_4((r)->res, (o), (d), (c)) : \ BHND_BUS_READ_MULTI_4( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (d), (c)) #define bhnd_bus_read_region_4(r, o, d, c) \ ((r)->direct) ? \ bus_read_region_4((r)->res, (o), (d), (c)) : \ BHND_BUS_READ_REGION_4( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (d), (c)) #define bhnd_bus_write_4(r, o, v) \ ((r)->direct) ? \ bus_write_4((r)->res, (o), (v)) : \ BHND_BUS_WRITE_4( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (v)) #define bhnd_bus_write_multi_4(r, o, d, c) \ ((r)->direct) ? \ bus_write_multi_4((r)->res, (o), (d), (c)) : \ BHND_BUS_WRITE_MULTI_4( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (d), (c)) #define bhnd_bus_write_region_4(r, o, d, c) \ ((r)->direct) ? \ bus_write_region_4((r)->res, (o), (d), (c)) : \ BHND_BUS_WRITE_REGION_4( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (d), (c)) #define bhnd_bus_read_stream_4(r, o) \ ((r)->direct) ? \ bus_read_stream_4((r)->res, (o)) : \ BHND_BUS_READ_STREAM_4( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o)) #define bhnd_bus_read_multi_stream_4(r, o, d, c) \ ((r)->direct) ? \ bus_read_multi_stream_4((r)->res, (o), (d), (c)) : \ BHND_BUS_READ_MULTI_STREAM_4( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (d), (c)) #define bhnd_bus_read_region_stream_4(r, o, d, c) \ ((r)->direct) ? \ bus_read_region_stream_4((r)->res, (o), (d), (c)) : \ BHND_BUS_READ_REGION_STREAM_4( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (d), (c)) #define bhnd_bus_write_stream_4(r, o, v) \ ((r)->direct) ? \ bus_write_stream_4((r)->res, (o), (v)) : \ BHND_BUS_WRITE_STREAM_4( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (v)) #define bhnd_bus_write_multi_stream_4(r, o, d, c) \ ((r)->direct) ? \ bus_write_multi_stream_4((r)->res, (o), (d), (c)) : \ BHND_BUS_WRITE_MULTI_STREAM_4( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (d), (c)) #define bhnd_bus_write_region_stream_4(r, o, d, c) \ ((r)->direct) ? \ bus_write_region_stream_4((r)->res, (o), (d), (c)) : \ BHND_BUS_WRITE_REGION_STREAM_4( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (d), (c)) #define bhnd_bus_set_multi_4(r, o, v, c) \ ((r)->direct) ? \ bus_set_multi_4((r)->res, (o), (v), (c)) : \ BHND_BUS_SET_MULTI_4( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (v), (c)) #define bhnd_bus_set_region_4(r, o, v, c) \ ((r)->direct) ? \ bus_set_region_4((r)->res, (o), (v), (c)) : \ BHND_BUS_SET_REGION_4( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (v), (c)) #endif /* _BHND_BHND_H_ */ Index: head/sys/dev/bhnd/bhnd_bus_if.m =================================================================== --- head/sys/dev/bhnd/bhnd_bus_if.m (revision 305370) +++ head/sys/dev/bhnd/bhnd_bus_if.m (revision 305371) @@ -1,1238 +1,1254 @@ #- # Copyright (c) 2015 Landon Fuller # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, # INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE # USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # $FreeBSD$ #include #include #include #include +#include INTERFACE bhnd_bus; # # bhnd(4) bus interface # HEADER { /* forward declarations */ struct bhnd_board_info; struct bhnd_core_info; struct bhnd_chipid; struct bhnd_devinfo; struct bhnd_resource; } CODE { #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) { panic("bhnd_bus_get_chipid unimplemented"); } static bhnd_attach_type bhnd_bus_null_get_attach_type(device_t dev, device_t child) { panic("bhnd_bus_get_attach_type unimplemented"); } static bhnd_clksrc bhnd_bus_null_pwrctl_get_clksrc(device_t dev, device_t child, bhnd_clock clock) { return (BHND_CLKSRC_UNKNOWN); } static int bhnd_bus_null_pwrctl_gate_clock(device_t dev, device_t child, bhnd_clock clock) { return (ENODEV); } static int bhnd_bus_null_pwrctl_ungate_clock(device_t dev, device_t child, bhnd_clock clock) { return (ENODEV); } static int bhnd_bus_null_read_board_info(device_t dev, device_t child, struct bhnd_board_info *info) { panic("bhnd_bus_read_boardinfo unimplemented"); } static void bhnd_bus_null_child_added(device_t dev, device_t child) { } static int bhnd_bus_null_alloc_pmu(device_t dev, device_t child) { panic("bhnd_bus_alloc_pmu unimplemented"); } static int bhnd_bus_null_release_pmu(device_t dev, device_t child) { panic("bhnd_bus_release_pmu unimplemented"); } static int bhnd_bus_null_request_clock(device_t dev, device_t child, bhnd_clock clock) { panic("bhnd_bus_request_clock unimplemented"); } static int bhnd_bus_null_enable_clocks(device_t dev, device_t child, uint32_t clocks) { panic("bhnd_bus_enable_clocks unimplemented"); } static int bhnd_bus_null_request_ext_rsrc(device_t dev, device_t child, u_int rsrc) { panic("bhnd_bus_request_ext_rsrc unimplemented"); } static int bhnd_bus_null_release_ext_rsrc(device_t dev, device_t child, u_int rsrc) { panic("bhnd_bus_release_ext_rsrc unimplemented"); } static uint32_t bhnd_bus_null_read_config(device_t dev, device_t child, bus_size_t offset, u_int width) { panic("bhnd_bus_null_read_config unimplemented"); } static void bhnd_bus_null_write_config(device_t dev, device_t child, bus_size_t offset, uint32_t val, u_int width) { panic("bhnd_bus_null_write_config unimplemented"); } static device_t bhnd_bus_null_find_hostb_device(device_t dev) { - panic("bhnd_bus_find_hostb_device unimplemented"); + return (NULL); } static bool bhnd_bus_null_is_hw_disabled(device_t dev, device_t child) { panic("bhnd_bus_is_hw_disabled unimplemented"); } static int bhnd_bus_null_get_probe_order(device_t dev, device_t child) { panic("bhnd_bus_get_probe_order unimplemented"); } static int bhnd_bus_null_get_port_rid(device_t dev, device_t child, bhnd_port_type port_type, u_int port, u_int region) { return (-1); } static int bhnd_bus_null_decode_port_rid(device_t dev, device_t child, int type, int rid, bhnd_port_type *port_type, u_int *port, u_int *region) { return (ENOENT); } static int bhnd_bus_null_get_region_addr(device_t dev, device_t child, bhnd_port_type type, u_int port, u_int region, bhnd_addr_t *addr, bhnd_size_t *size) { return (ENOENT); } static int bhnd_bus_null_get_nvram_var(device_t dev, device_t child, const char *name, void *buf, size_t *size, bhnd_nvram_type type) { return (ENODEV); } } + +/** + * 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. * * @retval device_t if a hostb device exists * @retval NULL if no hostb device is found. */ METHOD device_t find_hostb_device { device_t dev; } DEFAULT bhnd_bus_null_find_hostb_device; /** * Return true if the hardware components required by @p child are unpopulated * or otherwise unusable. * * In some cases, enumerated devices may have pins that are left floating, or * the hardware may otherwise be non-functional; this method allows a parent * device to explicitly specify if a successfully enumerated @p child should * be disabled. * * @param dev The device whose child is being examined. * @param child The child device. */ METHOD bool is_hw_disabled { device_t dev; device_t child; } DEFAULT bhnd_bus_null_is_hw_disabled; /** * Return the probe (and attach) order for @p child. * * All devices on the bhnd(4) bus will be probed, attached, or resumed in * ascending order; they will be suspended, shutdown, and detached in * descending order. * * The following device methods will be dispatched in ascending probe order * by the bus: * * - DEVICE_PROBE() * - DEVICE_ATTACH() * - DEVICE_RESUME() * * The following device methods will be dispatched in descending probe order * by the bus: * * - DEVICE_SHUTDOWN() * - DEVICE_DETACH() * - DEVICE_SUSPEND() * * @param dev The device whose child is being examined. * @param child The child device. * * Refer to BHND_PROBE_* and BHND_PROBE_ORDER_* for the standard set of * priorities. */ METHOD int get_probe_order { device_t dev; device_t child; } DEFAULT bhnd_bus_null_get_probe_order; /** * Return the BHND chip identification for the parent bus. * * @param dev The device whose child is being examined. * @param child The child device. */ METHOD const struct bhnd_chipid * get_chipid { device_t dev; device_t child; } DEFAULT bhnd_bus_null_get_chipid; /** * Return the BHND attachment type of the parent bus. * * @param dev The device whose child is being examined. * @param child The child device. * * @retval BHND_ATTACH_ADAPTER if the bus is resident on a bridged adapter, * such as a WiFi chipset. * @retval BHND_ATTACH_NATIVE if the bus provides hardware services (clock, * CPU, etc) to a directly attached native host. */ METHOD bhnd_attach_type get_attach_type { device_t dev; device_t child; } DEFAULT bhnd_bus_null_get_attach_type; /** * Attempt to read the BHND board identification from the parent bus. * * This relies on NVRAM access, and will fail if a valid NVRAM device cannot * be found, or is not yet attached. * * @param dev The parent of @p child. * @param child The bhnd device requesting board info. * @param[out] info On success, will be populated with the bhnd(4) device's * board information. * * @retval 0 success * @retval ENODEV No valid NVRAM source could be found. * @retval non-zero If reading @p name otherwise fails, a regular unix * error code will be returned. */ METHOD int read_board_info { device_t dev; device_t child; struct bhnd_board_info *info; } DEFAULT bhnd_bus_null_read_board_info; /** * Allocate and zero-initialize a buffer suitably sized and aligned for a * bhnd_devinfo structure. * * @param dev The bhnd bus device. * * @retval non-NULL success * @retval NULL allocation failed */ METHOD struct bhnd_devinfo * alloc_devinfo { device_t dev; }; /** * Release memory previously allocated for @p devinfo. * * @param dev The bhnd bus device. * @param dinfo A devinfo buffer previously allocated via * BHND_BUS_ALLOC_DEVINFO(). */ METHOD void free_devinfo { device_t dev; struct bhnd_devinfo *dinfo; }; /** * Notify a bhnd bus that a child was added. * * This method must be called by concrete bhnd(4) driver impementations * after @p child's bus state is fully initialized. * * @param dev The bhnd bus whose child is being added. * @param child The child added to @p dev. */ METHOD void child_added { device_t dev; device_t child; } DEFAULT bhnd_bus_null_child_added; /** * Reset the device's hardware core. * * @param dev The parent of @p child. * @param child The device to be reset. * @param flags Device-specific core flags to be supplied on reset. * * @retval 0 success * @retval non-zero error */ METHOD int reset_core { device_t dev; device_t child; uint16_t flags; } /** * Suspend a device hardware core. * * @param dev The parent of @p child. * @param child The device to be reset. * * @retval 0 success * @retval non-zero error */ METHOD int suspend_core { device_t dev; device_t child; } /** * If supported by the chipset, return the clock source for the given clock. * * This function is only supported on early PWRCTL-equipped chipsets * that expose clock management via their host bridge interface. Currently, * this includes PCI (not PCIe) devices, with ChipCommon core revisions 0-9. * * @param dev The parent of @p child. * @param child The bhnd device requesting a clock source. * @param clock The clock for which a clock source will be returned. * * @retval bhnd_clksrc The clock source for @p clock. * @retval BHND_CLKSRC_UNKNOWN If @p clock is unsupported, or its * clock source is not known to the bus. */ METHOD bhnd_clksrc pwrctl_get_clksrc { device_t dev; device_t child; bhnd_clock clock; } DEFAULT bhnd_bus_null_pwrctl_get_clksrc; /** * If supported by the chipset, gate the clock source for @p clock * * This function is only supported on early PWRCTL-equipped chipsets * that expose clock management via their host bridge interface. Currently, * this includes PCI (not PCIe) devices, with ChipCommon core revisions 0-9. * * @param dev The parent of @p child. * @param child The bhnd device requesting clock gating. * @param clock The clock to be disabled. * * @retval 0 success * @retval ENODEV If bus-level clock source management is not supported. * @retval ENXIO If bus-level management of @p clock is not supported. */ METHOD int pwrctl_gate_clock { device_t dev; device_t child; bhnd_clock clock; } DEFAULT bhnd_bus_null_pwrctl_gate_clock; /** * If supported by the chipset, ungate the clock source for @p clock * * This function is only supported on early PWRCTL-equipped chipsets * that expose clock management via their host bridge interface. Currently, * this includes PCI (not PCIe) devices, with ChipCommon core revisions 0-9. * * @param dev The parent of @p child. * @param child The bhnd device requesting clock gating. * @param clock The clock to be enabled. * * @retval 0 success * @retval ENODEV If bus-level clock source management is not supported. * @retval ENXIO If bus-level management of @p clock is not supported. */ METHOD int pwrctl_ungate_clock { device_t dev; device_t child; bhnd_clock clock; } DEFAULT bhnd_bus_null_pwrctl_ungate_clock; /** * Allocate and enable per-core PMU request handling for @p child. * * The region containing the core's PMU register block (if any) must be * allocated via bus_alloc_resource(9) (or bhnd_alloc_resource) before * calling BHND_BUS_ALLOC_PMU(), and must not be released until after * calling BHND_BUS_RELEASE_PMU(). * * @param dev The parent of @p child. * @param child The requesting bhnd device. */ METHOD int alloc_pmu { device_t dev; device_t child; } DEFAULT bhnd_bus_null_alloc_pmu; /** * Release per-core PMU resources allocated for @p child. Any * outstanding PMU requests are discarded. * * @param dev The parent of @p child. * @param child The requesting bhnd device. */ METHOD int release_pmu { device_t dev; device_t child; } DEFAULT bhnd_bus_null_release_pmu; /** * Request that @p clock (or faster) be routed to @p child. * * A driver must ask the bhnd bus to allocate PMU request state * via BHND_BUS_ALLOC_PMU() before it can request clock resources. * * Request multiplexing is managed by the bus. * * @param dev The parent of @p child. * @param child The bhnd device requesting @p clock. * @param clock The requested clock source. * * @retval 0 success * @retval ENODEV If an unsupported clock was requested. * @retval ENXIO If the PMU has not been initialized or is otherwise unvailable. */ METHOD int request_clock { device_t dev; device_t child; bhnd_clock clock; } DEFAULT bhnd_bus_null_request_clock; /** * Request that @p clocks be powered on behalf of @p child. * * This will power on clock sources (e.g. XTAL, PLL, etc) required for * @p clocks and wait until they are ready, discarding any previous * requests by @p child. * * Request multiplexing is managed by the bus. * * A driver must ask the bhnd bus to allocate PMU request state * via BHND_BUS_ALLOC_PMU() before it can request clock resources. * * @param dev The parent of @p child. * @param child The bhnd device requesting @p clock. * @param clock The requested clock source. * * @retval 0 success * @retval ENODEV If an unsupported clock was requested. * @retval ENXIO If the PMU has not been initialized or is otherwise unvailable. */ METHOD int enable_clocks { device_t dev; device_t child; uint32_t clocks; } DEFAULT bhnd_bus_null_enable_clocks; /** * Power up an external PMU-managed resource assigned to @p child. * * A driver must ask the bhnd bus to allocate PMU request state * via BHND_BUS_ALLOC_PMU() before it can request PMU resources. * * @param dev The parent of @p child. * @param child The bhnd device requesting @p rsrc. * @param rsrc The core-specific external resource identifier. * * @retval 0 success * @retval ENODEV If the PMU does not support @p rsrc. * @retval ENXIO If the PMU has not been initialized or is otherwise unvailable. */ METHOD int request_ext_rsrc { device_t dev; device_t child; u_int rsrc; } DEFAULT bhnd_bus_null_request_ext_rsrc; /** * Power down an external PMU-managed resource assigned to @p child. * * A driver must ask the bhnd bus to allocate PMU request state * via BHND_BUS_ALLOC_PMU() before it can request PMU resources. * * @param dev The parent of @p child. * @param child The bhnd device requesting @p rsrc. * @param rsrc The core-specific external resource number. * * @retval 0 success * @retval ENODEV If the PMU does not support @p rsrc. * @retval ENXIO If the PMU has not been initialized or is otherwise unvailable. */ METHOD int release_ext_rsrc { device_t dev; device_t child; u_int rsrc; } DEFAULT bhnd_bus_null_release_ext_rsrc; /** * Read @p width bytes at @p offset from the bus-specific agent/config * space of @p child. * * @param dev The parent of @p child. * @param child The bhnd device for which @p offset should be read. * @param offset The offset to be read. * @param width The size of the access. Must be 1, 2 or 4 bytes. * * The exact behavior of this method is bus-specific. On a bcma(4) bus, this * method provides access to the first agent port of @p child; on a siba(4) bus, * this method provides access to the core's CFG0 register block. * * @note Device drivers should only use this API for functionality * that is not available via another bhnd(4) function. */ METHOD uint32_t read_config { device_t dev; device_t child; bus_size_t offset; u_int width; } DEFAULT bhnd_bus_null_read_config; /** * Read @p width bytes at @p offset from the bus-specific agent/config * space of @p child. * * @param dev The parent of @p child. * @param child The bhnd device for which @p offset should be read. * @param offset The offset to be written. * @param width The size of the access. Must be 1, 2 or 4 bytes. * * The exact behavior of this method is bus-specific. In the case of * bcma(4), this method provides access to the first agent port of @p child. * * @note Device drivers should only use this API for functionality * that is not available via another bhnd(4) function. */ METHOD void write_config { device_t dev; device_t child; bus_size_t offset; uint32_t val; u_int width; } DEFAULT bhnd_bus_null_write_config; /** * Allocate a bhnd resource. * * This method's semantics are functionally identical to the bus API of the same * name; refer to BUS_ALLOC_RESOURCE for complete documentation. */ METHOD struct bhnd_resource * alloc_resource { device_t dev; device_t child; int type; int *rid; rman_res_t start; rman_res_t end; rman_res_t count; u_int flags; } DEFAULT bhnd_bus_generic_alloc_resource; /** * Release a bhnd resource. * * This method's semantics are functionally identical to the bus API of the same * name; refer to BUS_RELEASE_RESOURCE for complete documentation. */ METHOD int release_resource { device_t dev; device_t child; int type; int rid; struct bhnd_resource *res; } DEFAULT bhnd_bus_generic_release_resource; /** * Activate a bhnd resource. * * This method's semantics are functionally identical to the bus API of the same * name; refer to BUS_ACTIVATE_RESOURCE for complete documentation. */ METHOD int activate_resource { device_t dev; device_t child; int type; int rid; struct bhnd_resource *r; } DEFAULT bhnd_bus_generic_activate_resource; /** * Deactivate a bhnd resource. * * This method's semantics are functionally identical to the bus API of the same * name; refer to BUS_DEACTIVATE_RESOURCE for complete documentation. */ METHOD int deactivate_resource { device_t dev; device_t child; int type; int rid; struct bhnd_resource *r; } DEFAULT bhnd_bus_generic_deactivate_resource; /** * Return true if @p region_num is a valid region on @p port_num of * @p type attached to @p child. * * @param dev The device whose child is being examined. * @param child The child device. * @param type The port type being queried. * @param port_num The port number being queried. * @param region_num The region number being queried. */ METHOD bool is_region_valid { device_t dev; device_t child; bhnd_port_type type; u_int port_num; u_int region_num; }; /** * Return the number of ports of type @p type attached to @p child. * * @param dev The device whose child is being examined. * @param child The child device. * @param type The port type being queried. */ METHOD u_int get_port_count { device_t dev; device_t child; bhnd_port_type type; }; /** * Return the number of memory regions mapped to @p child @p port of * type @p type. * * @param dev The device whose child is being examined. * @param child The child device. * @param port The port number being queried. * @param type The port type being queried. */ METHOD u_int get_region_count { device_t dev; device_t child; bhnd_port_type type; u_int port; }; /** * Return the SYS_RES_MEMORY resource-ID for a port/region pair attached to * @p child. * * @param dev The bus device. * @param child The bhnd child. * @param port_type The port type. * @param port_num The index of the child interconnect port. * @param region_num The index of the port-mapped address region. * * @retval -1 No such port/region found. */ METHOD int get_port_rid { device_t dev; device_t child; bhnd_port_type port_type; u_int port_num; u_int region_num; } DEFAULT bhnd_bus_null_get_port_rid; /** * Decode a port / region pair on @p child defined by @p type and @p rid. * * @param dev The bus device. * @param child The bhnd child. * @param type The resource type. * @param rid The resource ID. * @param[out] port_type The port's type. * @param[out] port The port identifier. * @param[out] region The identifier of the memory region on @p port. * * @retval 0 success * @retval non-zero No matching type/rid found. */ METHOD int decode_port_rid { device_t dev; device_t child; int type; int rid; bhnd_port_type *port_type; u_int *port; u_int *region; } DEFAULT bhnd_bus_null_decode_port_rid; /** * Get the address and size of @p region on @p port. * * @param dev The bus device. * @param child The bhnd child. * @param port_type The port type. * @param port The port identifier. * @param region The identifier of the memory region on @p port. * @param[out] region_addr The region's base address. * @param[out] region_size The region's size. * * @retval 0 success * @retval non-zero No matching port/region found. */ METHOD int get_region_addr { device_t dev; device_t child; bhnd_port_type port_type; u_int port; u_int region; bhnd_addr_t *region_addr; bhnd_size_t *region_size; } DEFAULT bhnd_bus_null_get_region_addr; /** * Read an NVRAM variable. * * It is the responsibility of the bus to delegate this request to * the appropriate NVRAM child device, or to a parent bus implementation. * * @param dev The bus device. * @param child The requesting device. * @param name The NVRAM variable name. * @param[out] buf On success, the requested value will be written * to this buffer. This argment may be NULL if * the value is not desired. * @param[in,out] size The capacity of @p buf. On success, will be set * to the actual size of the requested value. * @param type The data type to be written to @p buf. * * @retval 0 success * @retval ENOENT The requested variable was not found. * @retval ENOMEM If @p buf is non-NULL and a buffer of @p size is too * small to hold the requested value. * @retval ENODEV No valid NVRAM source could be found. * @retval EFTYPE If the @p name's data type cannot be coerced to @p type. * @retval ERANGE If value coercion would overflow @p type. * @retval non-zero If reading @p name otherwise fails, a regular unix * error code will be returned. */ METHOD int get_nvram_var { device_t dev; device_t child; const char *name; void *buf; size_t *size; bhnd_nvram_type type; } DEFAULT bhnd_bus_null_get_nvram_var; /** An implementation of bus_read_1() compatible with bhnd_resource */ METHOD uint8_t read_1 { device_t dev; device_t child; struct bhnd_resource *r; bus_size_t offset; } /** An implementation of bus_read_2() compatible with bhnd_resource */ METHOD uint16_t read_2 { device_t dev; device_t child; struct bhnd_resource *r; bus_size_t offset; } /** An implementation of bus_read_4() compatible with bhnd_resource */ METHOD uint32_t read_4 { device_t dev; device_t child; struct bhnd_resource *r; bus_size_t offset; } /** An implementation of bus_write_1() compatible with bhnd_resource */ METHOD void write_1 { device_t dev; device_t child; struct bhnd_resource *r; bus_size_t offset; uint8_t value; } /** An implementation of bus_write_2() compatible with bhnd_resource */ METHOD void write_2 { device_t dev; device_t child; struct bhnd_resource *r; bus_size_t offset; uint16_t value; } /** An implementation of bus_write_4() compatible with bhnd_resource */ METHOD void write_4 { device_t dev; device_t child; struct bhnd_resource *r; bus_size_t offset; uint32_t value; } /** An implementation of bus_read_stream_1() compatible with bhnd_resource */ METHOD uint8_t read_stream_1 { device_t dev; device_t child; struct bhnd_resource *r; bus_size_t offset; } /** An implementation of bus_read_stream_2() compatible with bhnd_resource */ METHOD uint16_t read_stream_2 { device_t dev; device_t child; struct bhnd_resource *r; bus_size_t offset; } /** An implementation of bus_read_stream_4() compatible with bhnd_resource */ METHOD uint32_t read_stream_4 { device_t dev; device_t child; struct bhnd_resource *r; bus_size_t offset; } /** An implementation of bus_write_stream_1() compatible with bhnd_resource */ METHOD void write_stream_1 { device_t dev; device_t child; struct bhnd_resource *r; bus_size_t offset; uint8_t value; } /** An implementation of bus_write_stream_2() compatible with bhnd_resource */ METHOD void write_stream_2 { device_t dev; device_t child; struct bhnd_resource *r; bus_size_t offset; uint16_t value; } /** An implementation of bus_write_stream_4() compatible with bhnd_resource */ METHOD void write_stream_4 { device_t dev; device_t child; struct bhnd_resource *r; bus_size_t offset; uint32_t value; } /** An implementation of bus_read_multi_1() compatible with bhnd_resource */ METHOD void read_multi_1 { device_t dev; device_t child; struct bhnd_resource *r; bus_size_t offset; uint8_t *datap; bus_size_t count; } /** An implementation of bus_read_multi_2() compatible with bhnd_resource */ METHOD void read_multi_2 { device_t dev; device_t child; struct bhnd_resource *r; bus_size_t offset; uint16_t *datap; bus_size_t count; } /** An implementation of bus_read_multi_4() compatible with bhnd_resource */ METHOD void read_multi_4 { device_t dev; device_t child; struct bhnd_resource *r; bus_size_t offset; uint32_t *datap; bus_size_t count; } /** An implementation of bus_write_multi_1() compatible with bhnd_resource */ METHOD void write_multi_1 { device_t dev; device_t child; struct bhnd_resource *r; bus_size_t offset; uint8_t *datap; bus_size_t count; } /** An implementation of bus_write_multi_2() compatible with bhnd_resource */ METHOD void write_multi_2 { device_t dev; device_t child; struct bhnd_resource *r; bus_size_t offset; uint16_t *datap; bus_size_t count; } /** An implementation of bus_write_multi_4() compatible with bhnd_resource */ METHOD void write_multi_4 { device_t dev; device_t child; struct bhnd_resource *r; bus_size_t offset; uint32_t *datap; bus_size_t count; } /** An implementation of bus_read_multi_stream_1() compatible * bhnd_resource */ METHOD void read_multi_stream_1 { device_t dev; device_t child; struct bhnd_resource *r; bus_size_t offset; uint8_t *datap; bus_size_t count; } /** An implementation of bus_read_multi_stream_2() compatible * bhnd_resource */ METHOD void read_multi_stream_2 { device_t dev; device_t child; struct bhnd_resource *r; bus_size_t offset; uint16_t *datap; bus_size_t count; } /** An implementation of bus_read_multi_stream_4() compatible * bhnd_resource */ METHOD void read_multi_stream_4 { device_t dev; device_t child; struct bhnd_resource *r; bus_size_t offset; uint32_t *datap; bus_size_t count; } /** An implementation of bus_write_multi_stream_1() compatible * bhnd_resource */ METHOD void write_multi_stream_1 { device_t dev; device_t child; struct bhnd_resource *r; bus_size_t offset; uint8_t *datap; bus_size_t count; } /** An implementation of bus_write_multi_stream_2() compatible with * bhnd_resource */ METHOD void write_multi_stream_2 { device_t dev; device_t child; struct bhnd_resource *r; bus_size_t offset; uint16_t *datap; bus_size_t count; } /** An implementation of bus_write_multi_stream_4() compatible with * bhnd_resource */ METHOD void write_multi_stream_4 { device_t dev; device_t child; struct bhnd_resource *r; bus_size_t offset; uint32_t *datap; bus_size_t count; } /** An implementation of bus_set_multi_1() compatible with bhnd_resource */ METHOD void set_multi_1 { device_t dev; device_t child; struct bhnd_resource *r; bus_size_t offset; uint8_t value; bus_size_t count; } /** An implementation of bus_set_multi_2() compatible with bhnd_resource */ METHOD void set_multi_2 { device_t dev; device_t child; struct bhnd_resource *r; bus_size_t offset; uint16_t value; bus_size_t count; } /** An implementation of bus_set_multi_4() compatible with bhnd_resource */ METHOD void set_multi_4 { device_t dev; device_t child; struct bhnd_resource *r; bus_size_t offset; uint32_t value; bus_size_t count; } /** An implementation of bus_set_region_1() compatible with bhnd_resource */ METHOD void set_region_1 { device_t dev; device_t child; struct bhnd_resource *r; bus_size_t offset; uint8_t value; bus_size_t count; } /** An implementation of bus_set_region_2() compatible with bhnd_resource */ METHOD void set_region_2 { device_t dev; device_t child; struct bhnd_resource *r; bus_size_t offset; uint16_t value; bus_size_t count; } /** An implementation of bus_set_region_4() compatible with bhnd_resource */ METHOD void set_region_4 { device_t dev; device_t child; struct bhnd_resource *r; bus_size_t offset; uint32_t value; bus_size_t count; } /** An implementation of bus_read_region_1() compatible with bhnd_resource */ METHOD void read_region_1 { device_t dev; device_t child; struct bhnd_resource *r; bus_size_t offset; uint8_t *datap; bus_size_t count; } /** An implementation of bus_read_region_2() compatible with bhnd_resource */ METHOD void read_region_2 { device_t dev; device_t child; struct bhnd_resource *r; bus_size_t offset; uint16_t *datap; bus_size_t count; } /** An implementation of bus_read_region_4() compatible with bhnd_resource */ METHOD void read_region_4 { device_t dev; device_t child; struct bhnd_resource *r; bus_size_t offset; uint32_t *datap; bus_size_t count; } /** An implementation of bus_read_region_stream_1() compatible with * bhnd_resource */ METHOD void read_region_stream_1 { device_t dev; device_t child; struct bhnd_resource *r; bus_size_t offset; uint8_t *datap; bus_size_t count; } /** An implementation of bus_read_region_stream_2() compatible with * bhnd_resource */ METHOD void read_region_stream_2 { device_t dev; device_t child; struct bhnd_resource *r; bus_size_t offset; uint16_t *datap; bus_size_t count; } /** An implementation of bus_read_region_stream_4() compatible with * bhnd_resource */ METHOD void read_region_stream_4 { device_t dev; device_t child; struct bhnd_resource *r; bus_size_t offset; uint32_t *datap; bus_size_t count; } /** An implementation of bus_write_region_1() compatible with bhnd_resource */ METHOD void write_region_1 { device_t dev; device_t child; struct bhnd_resource *r; bus_size_t offset; uint8_t *datap; bus_size_t count; } /** An implementation of bus_write_region_2() compatible with bhnd_resource */ METHOD void write_region_2 { device_t dev; device_t child; struct bhnd_resource *r; bus_size_t offset; uint16_t *datap; bus_size_t count; } /** An implementation of bus_write_region_4() compatible with bhnd_resource */ METHOD void write_region_4 { device_t dev; device_t child; struct bhnd_resource *r; bus_size_t offset; uint32_t *datap; bus_size_t count; } /** An implementation of bus_write_region_stream_1() compatible with * bhnd_resource */ METHOD void write_region_stream_1 { device_t dev; device_t child; struct bhnd_resource *r; bus_size_t offset; uint8_t *datap; bus_size_t count; } /** An implementation of bus_write_region_stream_2() compatible with * bhnd_resource */ METHOD void write_region_stream_2 { device_t dev; device_t child; struct bhnd_resource *r; bus_size_t offset; uint16_t *datap; bus_size_t count; } /** An implementation of bus_write_region_stream_4() compatible with * bhnd_resource */ METHOD void write_region_stream_4 { device_t dev; device_t child; struct bhnd_resource *r; bus_size_t offset; uint32_t *datap; bus_size_t count; } /** An implementation of bus_barrier() compatible with bhnd_resource */ METHOD void barrier { device_t dev; device_t child; struct bhnd_resource *r; bus_size_t offset; bus_size_t length; int flags; } Index: head/sys/dev/bhnd/bhnd_erom.c =================================================================== --- head/sys/dev/bhnd/bhnd_erom.c (revision 305370) +++ head/sys/dev/bhnd/bhnd_erom.c (revision 305371) @@ -1,140 +1,141 @@ /*- * Copyright (c) 2016 Landon Fuller * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include /** * Allocate and return a new device enumeration table parser. * * @param cls The parser class for which an instance will be * allocated. * @param parent The parent device from which EROM resources should * 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; 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); return (NULL); } return (erom); } /** * Perform static initialization of aa device enumeration table parser using * the provided bus space tag and handle. * * This may be used to initialize a caller-allocated erom instance state * during early boot, prior to malloc availability. * * @param cls The parser class for which an instance will be * allocated. * @param erom The erom parser instance to initialize. * @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. * * @retval 0 success * @retval ENOMEM if @p esize is smaller than required by @p cls. * @retval non-zero if an error occurs initializing the EROM parser, * a regular unix error code will be returned. */ 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; kcls = (kobj_class_t)cls; /* Verify allocation size */ if (kcls->size > esize) return (ENOMEM); /* 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)); } /** * Release any resources held by a @p erom parser previously * initialized via bhnd_erom_init_static(). * * @param erom An erom parser instance previously initialized via * bhnd_erom_init_static(). */ void bhnd_erom_fini_static(bhnd_erom_t *erom) { return (BHND_EROM_FINI(erom)); } /** * Release all resources held by a @p erom parser previously * allocated via bhnd_erom_alloc(). * * @param erom An erom parser instance previously allocated via * bhnd_erom_alloc(). */ void bhnd_erom_free(bhnd_erom_t *erom) { BHND_EROM_FINI(erom); kobj_delete((kobj_t)erom, M_BHND); } Index: head/sys/dev/bhnd/bhnd_erom.h =================================================================== --- head/sys/dev/bhnd/bhnd_erom.h (revision 305370) +++ head/sys/dev/bhnd/bhnd_erom.h (revision 305371) @@ -1,208 +1,243 @@ /*- * Copyright (c) 2015-2016 Landon Fuller * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. * * $FreeBSD$ */ #ifndef _BHND_EROM_BHND_EROM_H_ #define _BHND_EROM_BHND_EROM_H_ #include #include #include #include #include #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); void bhnd_erom_fini_static(bhnd_erom_t *erom); void bhnd_erom_free(bhnd_erom_t *erom); /** * Abstract bhnd_erom instance state. Must be first member of all subclass * instances. */ struct bhnd_erom { KOBJ_FIELDS; }; /** Number of additional bytes to reserve for statically allocated * bhnd_erom instances. */ #define BHND_EROM_STATIC_BYTES 64 /** * A bhnd_erom instance structure large enough to statically allocate * any known bhnd_erom subclass. * * The maximum size of subclasses is verified statically in * BHND_EROM_DEFINE_CLASS(), and at runtime in bhnd_erom_init_static(). */ struct bhnd_erom_static { struct bhnd_erom obj; uint8_t idata[BHND_EROM_STATIC_BYTES]; }; /** Registered EROM parser class instances. */ SET_DECLARE(bhnd_erom_class_set, bhnd_erom_class_t); #define BHND_EROM_DEFINE_CLASS(name, classvar, methods, size) \ DEFINE_CLASS_0(name, classvar, methods, size); \ BHND_EROM_CLASS_DEF(classvar); \ _Static_assert(size <= sizeof(struct bhnd_erom_static), \ "cannot statically allocate instance data; " \ "increase BHND_EROM_STATIC_BYTES"); #define BHND_EROM_CLASS_DEF(classvar) DATA_SET(bhnd_erom_class_set, classvar) /** * 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 * parser for the probed bus. * @retval negative if the probe succeeds, a negative value should be * returned; the parser returning the lowest 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_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)); } /** * Parse all cores descriptors in @p erom, returning the array in @p cores and * the count in @p num_cores. * * The memory allocated for the table must be freed via * bhnd_erom_free_core_table(). * * @param erom The erom parser to be queried. * @param[out] cores The table of parsed core descriptors. * @param[out] num_cores The number of core records in @p cores. * * @retval 0 success * @retval non-zero if an error occurs, a regular unix error code will * be returned. */ static inline int bhnd_erom_get_core_table(bhnd_erom_t *erom, struct bhnd_core_info **cores, u_int *num_cores) { return (BHND_EROM_GET_CORE_TABLE(erom, cores, num_cores)); } /** * Free any memory allocated in a previous call to BHND_EROM_GET_CORE_TABLE(). * * @param erom The erom parser instance. * @param cores A core table allocated by @p erom. */ static inline void bhnd_erom_free_core_table(bhnd_erom_t *erom, struct bhnd_core_info *cores) { return (BHND_EROM_FREE_CORE_TABLE(erom, cores)); }; /** * Locate the first core table entry in @p erom that matches @p desc. * * @param erom The erom parser to be queried. * @param desc A core match descriptor. * @param[out] core On success, the matching core info record. * * @retval 0 success * @retval ENOENT No core matching @p desc was found. * @retval non-zero Reading or parsing failed. */ static inline int bhnd_erom_lookup_core(bhnd_erom_t *erom, const struct bhnd_core_match *desc, struct bhnd_core_info *core) { return (BHND_EROM_LOOKUP_CORE(erom, desc, core)); } /** * Locate the first core table entry in @p erom that matches @p desc, * and return the specified port region's base address and size. * * If a core matching @p desc is not found, or the requested port region * is not mapped to the matching core, ENOENT is returned. * * @param erom The erom parser to be queried. * @param desc A core match descriptor. * @param type The port type to search for. * @param port The port to search for. * @param region The port region to search for. * @param[out] core If not NULL, will be populated with the matched core * info record on success. * @param[out] addr On success, the base address of the port region. * @param[out] size On success, the total size of the port region. * * @retval 0 success * @retval ENOENT No core matching @p desc was found. * @retval ENOENT No port region matching @p type, @p port, and @p region * was found. * @retval non-zero Reading or parsing failed. */ static inline int bhnd_erom_lookup_core_addr(bhnd_erom_t *erom, const struct bhnd_core_match *desc, bhnd_port_type type, u_int port, u_int region, struct bhnd_core_info *core, bhnd_addr_t *addr, bhnd_size_t *size) { return (BHND_EROM_LOOKUP_CORE_ADDR(erom, desc, type, port, region, core, addr, size)); }; #endif /* _BHND_EROM_BHND_EROM_H_ */ Index: head/sys/dev/bhnd/bhnd_erom_if.m =================================================================== --- head/sys/dev/bhnd/bhnd_erom_if.m (revision 305370) +++ head/sys/dev/bhnd/bhnd_erom_if.m (revision 305371) @@ -1,207 +1,243 @@ #- # Copyright (c) 2016 Landon Fuller # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, # INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE # USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # $FreeBSD$ #include #include #include #include #include #include #include INTERFACE bhnd_erom; # # bhnd(4) device enumeration. # # Provides a common parser interface to the incompatible device enumeration # tables used by bhnd(4) buses. # /** * 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 * 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_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; }; /** * Release all resources held by @p erom. * * @param erom An erom parser instance previously initialized via * BHND_EROM_INIT() or BHND_EROM_INIT_STATIC(). */ METHOD void fini { bhnd_erom_t *erom; }; /** * Parse all cores descriptors, returning the array in @p cores and the count * in @p num_cores. * * The memory allocated for the table must be freed via * BHND_EROM_FREE_CORE_TABLE(). * * @param erom The erom parser to be queried. * @param[out] cores The table of parsed core descriptors. * @param[out] num_cores The number of core records in @p cores. * * @retval 0 success * @retval non-zero if an error occurs, a regular unix error code will * be returned. */ METHOD int get_core_table { bhnd_erom_t *erom; struct bhnd_core_info **cores; u_int *num_cores; }; /** * Free any memory allocated in a previous call to BHND_EROM_GET_CORE_TABLE(). * * @param erom The erom parser instance. * @param cores A core table allocated by @p erom. */ METHOD void free_core_table { bhnd_erom_t *erom; struct bhnd_core_info *cores; }; /** * Locate the first core table entry in @p erom that matches @p desc. * * @param erom The erom parser to be queried. * @param desc A core match descriptor. * @param[out] core On success, the matching core info record. * * @retval 0 success * @retval ENOENT No core matching @p desc was found. * @retval non-zero Reading or parsing failed. */ METHOD int lookup_core { bhnd_erom_t *erom; const struct bhnd_core_match *desc; struct bhnd_core_info *core; }; /** * Locate the first core table entry in @p erom that matches @p desc, * and return the specified port region's base address and size. * * If a core matching @p desc is not found, or the requested port region * is not mapped to the matching core, ENOENT is returned. * * @param erom The erom parser to be queried. * @param desc A core match descriptor. * @param type The port type to search for. * @param port The port to search for. * @param region The port region to search for. * @param[out] core If not NULL, will be populated with the matched core * info record on success. * @param[out] addr On success, the base address of the port region. * @param[out] size On success, the total size of the port region. * * @retval 0 success * @retval ENOENT No core matching @p desc was found. * @retval ENOENT No port region matching @p type, @p port, and @p region * was found. * @retval non-zero Reading or parsing failed. */ METHOD int lookup_core_addr { bhnd_erom_t *erom; const struct bhnd_core_match *desc; bhnd_port_type type; u_int port; u_int region; struct bhnd_core_info *core; bhnd_addr_t *addr; bhnd_size_t *size; }; Index: head/sys/dev/bhnd/bhnd_match.h =================================================================== --- head/sys/dev/bhnd/bhnd_match.h (revision 305370) +++ head/sys/dev/bhnd/bhnd_match.h (revision 305371) @@ -1,291 +1,297 @@ /*- * Copyright (c) 2015-2016 Landon Fuller * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. * * $FreeBSD$ */ #ifndef _BHND_BHND_MATCH_H_ #define _BHND_BHND_MATCH_H_ #include "bhnd_types.h" /** * A hardware revision match descriptor. */ struct bhnd_hwrev_match { uint16_t start; /**< first revision, or BHND_HWREV_INVALID to match on any revision. */ uint16_t end; /**< last revision, or BHND_HWREV_INVALID to match on any revision. */ }; /* Copy match field @p _name from @p _src */ #define _BHND_COPY_MATCH_FIELD(_src, _name) \ .m.match._name = (_src)->m.match._name, \ ._name = (_src)->_name /* Set match field @p _name with @p _value */ #define _BHND_SET_MATCH_FIELD(_name, _value) \ .m.match._name = 1, ._name = _value /** * Wildcard hardware revision match descriptor. */ #define BHND_HWREV_ANY { BHND_HWREV_INVALID, BHND_HWREV_INVALID } #define BHND_HWREV_IS_ANY(_m) \ ((_m)->start == BHND_HWREV_INVALID && (_m)->end == BHND_HWREV_INVALID) /** * Hardware revision match descriptor for an inclusive range. * * @param _start The first applicable hardware revision. * @param _end The last applicable hardware revision, or BHND_HWREV_INVALID * to match on any revision. */ #define BHND_HWREV_RANGE(_start, _end) { _start, _end } /** * Hardware revision match descriptor for a single revision. * * @param _hwrev The hardware revision to match on. */ #define BHND_HWREV_EQ(_hwrev) BHND_HWREV_RANGE(_hwrev, _hwrev) /** * Hardware revision match descriptor for any revision equal to or greater * than @p _start. * * @param _start The first hardware revision to match on. */ #define BHND_HWREV_GTE(_start) BHND_HWREV_RANGE(_start, BHND_HWREV_INVALID) /** * Hardware revision match descriptor for any revision equal to or less * than @p _end. * * @param _end The last hardware revision to match on. */ #define BHND_HWREV_LTE(_end) BHND_HWREV_RANGE(0, _end) /** * A bhnd(4) core match descriptor. */ struct bhnd_core_match { /** Select fields to be matched */ union { uint8_t match_flags; struct { uint8_t core_vendor:1, core_id:1, core_rev:1, core_class:1, + core_idx:1, core_unit:1, - flags_unused:3; + flags_unused:2; } match; } m; uint16_t core_vendor; /**< required JEP106 device vendor */ 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 */ }; #define _BHND_CORE_MATCH_COPY(_src) \ _BHND_COPY_MATCH_FIELD(_src, core_vendor), \ _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) #define BHND_MATCH_CORE_ID(_id) _BHND_SET_MATCH_FIELD(core_id, _id) #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) /** * Match against the given @p _vendor and @p _id, */ #define BHND_MATCH_CORE(_vendor, _id) \ BHND_MATCH_CORE_VENDOR(_vendor), \ BHND_MATCH_CORE_ID(_id) /** * A bhnd(4) chip match descriptor. */ struct bhnd_chip_match { /** Select fields to be matched */ union { uint8_t match_flags; struct { uint8_t chip_id:1, chip_rev:1, chip_pkg:1, flags_unused:5; } match; } m; uint16_t chip_id; /**< required chip id */ struct bhnd_hwrev_match chip_rev; /**< matching chip revisions */ uint8_t chip_pkg; /**< required package */ }; #define _BHND_CHIP_MATCH_COPY(_src) \ _BHND_COPY_MATCH_FIELD(_src, chip_id), \ _BHND_COPY_MATCH_FIELD(_src, chip_rev), \ _BHND_COPY_MATCH_FIELD(_src, chip_pkg) \ /** Set the required chip ID within a bhnd match descriptor */ #define BHND_CHIP_ID(_cid) _BHND_SET_MATCH_FIELD(chip_id, \ BHND_CHIPID_ ## _cid) /** Set the required chip revision range within a bhnd match descriptor */ #define BHND_CHIP_REV(_rev) _BHND_SET_MATCH_FIELD(chip_rev, \ BHND_ ## _rev) /** Set the required package ID within a bhnd match descriptor */ #define BHND_CHIP_PKG(_pkg) _BHND_SET_MATCH_FIELD(chip_pkg, \ BHND_PKGID_ ## _pkg) /** Set the required chip and package ID within a bhnd match descriptor */ #define BHND_CHIP_IP(_cid, _pkg) \ BHND_CHIP_ID(_cid), BHND_CHIP_PKG(_pkg) /** Set the required chip ID, package ID, and revision within a bhnd_device_match * instance */ #define BHND_CHIP_IPR(_cid, _pkg, _rev) \ BHND_CHIP_ID(_cid), BHND_CHIP_PKG(_pkg), BHND_CHIP_REV(_rev) /** Set the required chip ID and revision within a bhnd_device_match * instance */ #define BHND_CHIP_IR(_cid, _rev) \ BHND_CHIP_ID(_cid), BHND_CHIP_REV(_rev) /** * A bhnd(4) board match descriptor. */ struct bhnd_board_match { /** Select fields to be matched */ union { uint8_t match_flags; struct { uint8_t board_vendor:1, board_type:1, board_rev:1, board_srom_rev:1, flags_unused:4; } match; } m; uint16_t board_vendor; /**< required board vendor */ uint16_t board_type; /**< required board type */ struct bhnd_hwrev_match board_rev; /**< matching board revisions */ struct bhnd_hwrev_match board_srom_rev; /**< matching board srom revisions */ }; #define _BHND_BOARD_MATCH_COPY(_src) \ _BHND_COPY_MATCH_FIELD(_src, board_vendor), \ _BHND_COPY_MATCH_FIELD(_src, board_type), \ _BHND_COPY_MATCH_FIELD(_src, board_rev), \ _BHND_COPY_MATCH_FIELD(_src, board_srom_rev) /** Set the required board vendor within a bhnd match descriptor */ #define BHND_MATCH_BOARD_VENDOR(_v) _BHND_SET_MATCH_FIELD(board_vendor, _v) /** Set the required board type within a bhnd match descriptor */ #define BHND_MATCH_BOARD_TYPE(_type) _BHND_SET_MATCH_FIELD(board_type, \ BHND_BOARD_ ## _type) /** Set the required SROM revision range within a bhnd match descriptor */ #define BHND_MATCH_SROMREV(_rev) _BHND_SET_MATCH_FIELD(board_srom_rev, \ BHND_HWREV_ ## _rev) /** Set the required board revision range within a bhnd match descriptor */ #define BHND_MATCH_BOARD_REV(_rev) _BHND_SET_MATCH_FIELD(board_rev, \ BHND_ ## _rev) /** Set the required board vendor and type within a bhnd match descriptor */ #define BHND_MATCH_BOARD(_vend, _type) \ BHND_MATCH_BOARD_VENDOR(_vend), BHND_MATCH_BOARD_TYPE(_type) /** * A bhnd(4) device match descriptor. * * @warning Matching on board attributes relies on NVRAM access, and will * fail if a valid NVRAM device cannot be found, or is not yet attached. */ struct bhnd_device_match { /** Select fields to be matched */ union { uint16_t match_flags; struct { uint16_t core_vendor:1, core_id:1, core_rev:1, core_class:1, + core_idx:1, core_unit:1, chip_id:1, chip_rev:1, chip_pkg:1, board_vendor:1, board_type:1, board_rev:1, board_srom_rev:1, - flags_unused:2; + flags_unused:1; } match; } m; uint16_t core_vendor; /**< required JEP106 device vendor */ 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 */ struct bhnd_hwrev_match chip_rev; /**< matching chip revisions */ uint8_t chip_pkg; /**< required package */ uint16_t board_vendor; /**< required board vendor */ uint16_t board_type; /**< required board type */ struct bhnd_hwrev_match board_rev; /**< matching board revisions */ struct bhnd_hwrev_match board_srom_rev; /**< matching board srom revisions */ }; /** Define a wildcard match requirement (matches on any device). */ #define BHND_MATCH_ANY .m.match_flags = 0 #define BHND_MATCH_IS_ANY(_m) \ ((_m)->m.match_flags == 0) #endif /* _BHND_BHND_MATCH_H_ */ Index: head/sys/dev/bhnd/bhnd_nexus.c =================================================================== --- head/sys/dev/bhnd/bhnd_nexus.c (revision 305370) +++ head/sys/dev/bhnd/bhnd_nexus.c (revision 305371) @@ -1,136 +1,136 @@ /*- * Copyright (c) 2015-2016 Landon Fuller * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. * * $FreeBSD$ */ #include __FBSDID("$FreeBSD$"); /* * bhnd(4) driver mix-in providing shared common methods for * bhnd bus devices attached via a root nexus. */ #include #include #include #include #include #include #include #include #include #include #include "bhnd_nexusvar.h" static const struct resource_spec bhnd_nexus_res_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, /* chipc registers */ { -1, 0, 0 } }; /** * Map ChipCommon's register block and read the chip identifier data. * * @param dev A bhnd_nexus device. * @param chipid On success, will be populated with the chip identifier. * @retval 0 success * @retval non-zero An error occurred reading the chip identifier.. */ int bhnd_nexus_read_chipid(device_t dev, struct bhnd_chipid *chipid) { struct resource_spec rspec[nitems(bhnd_nexus_res_spec)]; int error; memcpy(rspec, bhnd_nexus_res_spec, sizeof(rspec)); error = bhnd_read_chipid(dev, rspec, 0, chipid); if (error) device_printf(dev, "error %d reading chip ID\n", error); return (error); } static bool bhnd_nexus_is_hw_disabled(device_t dev, device_t child) { - return false; + return (false); } static bhnd_attach_type bhnd_nexus_get_attach_type(device_t dev, device_t child) { return (BHND_ATTACH_NATIVE); } static int bhnd_nexus_activate_resource(device_t dev, device_t child, int type, int rid, struct bhnd_resource *r) { int error; /* Always direct */ if ((error = bus_activate_resource(child, type, rid, r->res))) return (error); r->direct = true; return (0); } static int bhnd_nexus_deactivate_resource(device_t dev, device_t child, int type, int rid, struct bhnd_resource *r) { int error; /* Always direct */ KASSERT(r->direct, ("indirect resource delegated to bhnd_nexus\n")); if ((error = bus_deactivate_resource(child, type, rid, r->res))) return (error); r->direct = false; return (0); } static device_method_t bhnd_nexus_methods[] = { /* bhnd interface */ DEVMETHOD(bhnd_bus_activate_resource, bhnd_nexus_activate_resource), DEVMETHOD(bhnd_bus_deactivate_resource, bhnd_nexus_deactivate_resource), DEVMETHOD(bhnd_bus_is_hw_disabled, bhnd_nexus_is_hw_disabled), DEVMETHOD(bhnd_bus_get_attach_type, bhnd_nexus_get_attach_type), DEVMETHOD_END }; DEFINE_CLASS_0(bhnd, bhnd_nexus_driver, bhnd_nexus_methods, sizeof(struct bhnd_softc)); Index: head/sys/dev/bhnd/bhnd_subr.c =================================================================== --- head/sys/dev/bhnd/bhnd_subr.c (revision 305370) +++ head/sys/dev/bhnd/bhnd_subr.c (revision 305371) @@ -1,1634 +1,1678 @@ /*- * Copyright (c) 2015 Landon Fuller * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include "nvram/bhnd_nvram.h" #include "bhnd_chipc_if.h" #include "bhnd_nvram_if.h" #include "bhnd_nvram_map.h" #include "bhndreg.h" #include "bhndvar.h" /* BHND core device description table. */ static const struct bhnd_core_desc { uint16_t vendor; uint16_t device; bhnd_devclass_t class; const char *desc; } bhnd_core_descs[] = { #define BHND_CDESC(_mfg, _cid, _cls, _desc) \ { BHND_MFGID_ ## _mfg, BHND_COREID_ ## _cid, \ BHND_DEVCLASS_ ## _cls, _desc } BHND_CDESC(BCM, CC, CC, "ChipCommon I/O Controller"), BHND_CDESC(BCM, ILINE20, OTHER, "iLine20 HPNA"), BHND_CDESC(BCM, SRAM, RAM, "SRAM"), BHND_CDESC(BCM, SDRAM, RAM, "SDRAM"), BHND_CDESC(BCM, PCI, PCI, "PCI Bridge"), BHND_CDESC(BCM, MIPS, CPU, "MIPS Core"), BHND_CDESC(BCM, ENET, ENET_MAC, "Fast Ethernet MAC"), BHND_CDESC(BCM, CODEC, OTHER, "V.90 Modem Codec"), BHND_CDESC(BCM, USB, OTHER, "USB 1.1 Device/Host Controller"), BHND_CDESC(BCM, ADSL, OTHER, "ADSL Core"), BHND_CDESC(BCM, ILINE100, OTHER, "iLine100 HPNA"), BHND_CDESC(BCM, IPSEC, OTHER, "IPsec Accelerator"), BHND_CDESC(BCM, UTOPIA, OTHER, "UTOPIA ATM Core"), BHND_CDESC(BCM, PCMCIA, PCCARD, "PCMCIA Bridge"), BHND_CDESC(BCM, SOCRAM, RAM, "Internal Memory"), BHND_CDESC(BCM, MEMC, MEMC, "MEMC SDRAM Controller"), BHND_CDESC(BCM, OFDM, OTHER, "OFDM PHY"), BHND_CDESC(BCM, EXTIF, OTHER, "External Interface"), BHND_CDESC(BCM, D11, WLAN, "802.11 MAC/PHY/Radio"), BHND_CDESC(BCM, APHY, WLAN_PHY, "802.11a PHY"), BHND_CDESC(BCM, BPHY, WLAN_PHY, "802.11b PHY"), BHND_CDESC(BCM, GPHY, WLAN_PHY, "802.11g PHY"), BHND_CDESC(BCM, MIPS33, CPU, "MIPS3302 Core"), BHND_CDESC(BCM, USB11H, OTHER, "USB 1.1 Host Controller"), BHND_CDESC(BCM, USB11D, OTHER, "USB 1.1 Device Core"), BHND_CDESC(BCM, USB20H, OTHER, "USB 2.0 Host Controller"), BHND_CDESC(BCM, USB20D, OTHER, "USB 2.0 Device Core"), BHND_CDESC(BCM, SDIOH, OTHER, "SDIO Host Controller"), BHND_CDESC(BCM, ROBO, OTHER, "RoboSwitch"), BHND_CDESC(BCM, ATA100, OTHER, "Parallel ATA Controller"), BHND_CDESC(BCM, SATAXOR, OTHER, "SATA DMA/XOR Controller"), BHND_CDESC(BCM, GIGETH, ENET_MAC, "Gigabit Ethernet MAC"), BHND_CDESC(BCM, PCIE, PCIE, "PCIe Bridge"), BHND_CDESC(BCM, NPHY, WLAN_PHY, "802.11n 2x2 PHY"), BHND_CDESC(BCM, SRAMC, MEMC, "SRAM Controller"), BHND_CDESC(BCM, MINIMAC, OTHER, "MINI MAC/PHY"), BHND_CDESC(BCM, ARM11, CPU, "ARM1176 CPU"), BHND_CDESC(BCM, ARM7S, CPU, "ARM7TDMI-S CPU"), BHND_CDESC(BCM, LPPHY, WLAN_PHY, "802.11a/b/g PHY"), BHND_CDESC(BCM, PMU, PMU, "PMU"), BHND_CDESC(BCM, SSNPHY, WLAN_PHY, "802.11n Single-Stream PHY"), BHND_CDESC(BCM, SDIOD, OTHER, "SDIO Device Core"), BHND_CDESC(BCM, ARMCM3, CPU, "ARM Cortex-M3 CPU"), BHND_CDESC(BCM, HTPHY, WLAN_PHY, "802.11n 4x4 PHY"), BHND_CDESC(MIPS,MIPS74K, CPU, "MIPS74k CPU"), BHND_CDESC(BCM, GMAC, ENET_MAC, "Gigabit MAC core"), BHND_CDESC(BCM, DMEMC, MEMC, "DDR1/DDR2 Memory Controller"), BHND_CDESC(BCM, PCIERC, OTHER, "PCIe Root Complex"), BHND_CDESC(BCM, OCP, SOC_BRIDGE, "OCP to OCP Bridge"), BHND_CDESC(BCM, SC, OTHER, "Shared Common Core"), BHND_CDESC(BCM, AHB, SOC_BRIDGE, "OCP to AHB Bridge"), BHND_CDESC(BCM, SPIH, OTHER, "SPI Host Controller"), BHND_CDESC(BCM, I2S, OTHER, "I2S Digital Audio Interface"), BHND_CDESC(BCM, DMEMS, MEMC, "SDR/DDR1 Memory Controller"), BHND_CDESC(BCM, UBUS_SHIM, OTHER, "BCM6362/UBUS WLAN SHIM"), BHND_CDESC(BCM, PCIE2, PCIE, "PCIe Bridge (Gen2)"), BHND_CDESC(ARM, APB_BRIDGE, SOC_BRIDGE, "BP135 AMBA3 AXI to APB Bridge"), BHND_CDESC(ARM, PL301, SOC_ROUTER, "PL301 AMBA3 Interconnect"), BHND_CDESC(ARM, EROM, EROM, "PL366 Device Enumeration ROM"), BHND_CDESC(ARM, OOB_ROUTER, OTHER, "PL367 OOB Interrupt Router"), BHND_CDESC(ARM, AXI_UNMAPPED, OTHER, "Unmapped Address Ranges"), BHND_CDESC(BCM, 4706_CC, CC, "ChipCommon I/O Controller"), BHND_CDESC(BCM, NS_PCIE2, PCIE, "PCIe Bridge (Gen2)"), BHND_CDESC(BCM, NS_DMA, OTHER, "DMA engine"), BHND_CDESC(BCM, NS_SDIO, OTHER, "SDIO 3.0 Host Controller"), BHND_CDESC(BCM, NS_USB20H, OTHER, "USB 2.0 Host Controller"), BHND_CDESC(BCM, NS_USB30H, OTHER, "USB 3.0 Host Controller"), BHND_CDESC(BCM, NS_A9JTAG, OTHER, "ARM Cortex A9 JTAG Interface"), BHND_CDESC(BCM, NS_DDR23_MEMC, MEMC, "Denali DDR2/DD3 Memory Controller"), BHND_CDESC(BCM, NS_ROM, NVRAM, "System ROM"), BHND_CDESC(BCM, NS_NAND, NVRAM, "NAND Flash Controller"), BHND_CDESC(BCM, NS_QSPI, NVRAM, "QSPI Flash Controller"), BHND_CDESC(BCM, NS_CC_B, CC_B, "ChipCommon B Auxiliary I/O Controller"), BHND_CDESC(BCM, 4706_SOCRAM, RAM, "Internal Memory"), BHND_CDESC(BCM, IHOST_ARMCA9, CPU, "ARM Cortex A9 CPU"), BHND_CDESC(BCM, 4706_GMAC_CMN, ENET, "Gigabit MAC (Common)"), BHND_CDESC(BCM, 4706_GMAC, ENET_MAC, "Gigabit MAC"), BHND_CDESC(BCM, AMEMC, MEMC, "Denali DDR1/DDR2 Memory Controller"), #undef BHND_CDESC /* Derived from inspection of the BCM4331 cores that provide PrimeCell * IDs. Due to lack of documentation, the surmised device name/purpose * provided here may be incorrect. */ { BHND_MFGID_ARM, BHND_PRIMEID_EROM, BHND_DEVCLASS_OTHER, "PL364 Device Enumeration ROM" }, { BHND_MFGID_ARM, BHND_PRIMEID_SWRAP, BHND_DEVCLASS_OTHER, "PL368 Device Management Interface" }, { BHND_MFGID_ARM, BHND_PRIMEID_MWRAP, BHND_DEVCLASS_OTHER, "PL369 Device Management Interface" }, { 0, 0, 0, NULL } }; /** * Return the name for a given JEP106 manufacturer ID. * * @param vendor A JEP106 Manufacturer ID, including the non-standard ARM 4-bit * JEP106 continuation code. */ const char * bhnd_vendor_name(uint16_t vendor) { switch (vendor) { case BHND_MFGID_ARM: return "ARM"; case BHND_MFGID_BCM: return "Broadcom"; case BHND_MFGID_MIPS: return "MIPS"; default: return "unknown"; } } /** * Return the name of a port type. */ const char * bhnd_port_type_name(bhnd_port_type port_type) { switch (port_type) { case BHND_PORT_DEVICE: return ("device"); case BHND_PORT_BRIDGE: return ("bridge"); case BHND_PORT_AGENT: return ("agent"); default: return "unknown"; } } /** * Return the name of an NVRAM source. */ const char * bhnd_nvram_src_name(bhnd_nvram_src nvram_src) { switch (nvram_src) { case BHND_NVRAM_SRC_FLASH: return ("flash"); case BHND_NVRAM_SRC_OTP: return ("OTP"); case BHND_NVRAM_SRC_SPROM: return ("SPROM"); case BHND_NVRAM_SRC_UNKNOWN: return ("none"); default: return ("unknown"); } } static const struct bhnd_core_desc * bhnd_find_core_desc(uint16_t vendor, uint16_t device) { for (u_int i = 0; bhnd_core_descs[i].desc != NULL; i++) { if (bhnd_core_descs[i].vendor != vendor) continue; if (bhnd_core_descs[i].device != device) continue; return (&bhnd_core_descs[i]); } return (NULL); } /** * Return a human-readable name for a BHND core. * * @param vendor The core designer's JEDEC-106 Manufacturer ID * @param device The core identifier. */ const char * bhnd_find_core_name(uint16_t vendor, uint16_t device) { const struct bhnd_core_desc *desc; if ((desc = bhnd_find_core_desc(vendor, device)) == NULL) return ("unknown"); return desc->desc; } /** * Return the device class for a BHND core. * * @param vendor The core designer's JEDEC-106 Manufacturer ID * @param device The core identifier. */ bhnd_devclass_t bhnd_find_core_class(uint16_t vendor, uint16_t device) { const struct bhnd_core_desc *desc; if ((desc = bhnd_find_core_desc(vendor, device)) == NULL) return (BHND_DEVCLASS_OTHER); return desc->class; } /** * Return a human-readable name for a BHND core. * * @param ci The core's info record. */ const char * bhnd_core_name(const struct bhnd_core_info *ci) { return bhnd_find_core_name(ci->vendor, ci->device); } /** * Return the device class for a BHND core. * * @param ci The core's info record. */ bhnd_devclass_t bhnd_core_class(const struct bhnd_core_info *ci) { return bhnd_find_core_class(ci->vendor, ci->device); } /** * Write a human readable name representation of the given * BHND_CHIPID_* constant to @p buffer. * * @param buffer Output buffer, or NULL to compute the required size. * @param size Capacity of @p buffer, in bytes. * @param chip_id Chip ID to be formatted. * * @return Returns the required number of bytes on success, or a negative * integer on failure. No more than @p size-1 characters be written, with * the @p size'th set to '\0'. * * @sa BHND_CHIPID_MAX_NAMELEN */ int bhnd_format_chip_id(char *buffer, size_t size, uint16_t chip_id) { /* All hex formatted IDs are within the range of 0x4000-0x9C3F (40000-1) */ if (chip_id >= 0x4000 && chip_id <= 0x9C3F) return (snprintf(buffer, size, "BCM%hX", chip_id)); else return (snprintf(buffer, size, "BCM%hu", chip_id)); } /** * Initialize a core info record with data from from a bhnd-attached @p dev. * * @param dev A bhnd device. * @param core The record to be initialized. */ struct bhnd_core_info bhnd_get_core_info(device_t dev) { return (struct bhnd_core_info) { .vendor = bhnd_get_vendor(dev), .device = bhnd_get_device(dev), .hwrev = bhnd_get_hwrev(dev), .core_idx = bhnd_get_core_index(dev), .unit = bhnd_get_core_unit(dev) }; } /** * Find a @p class child device with @p unit on @p dev. * * @param parent The bhnd-compatible bus to be searched. * @param class The device class to match on. * @param unit The core unit number; specify -1 to return the first match * regardless of unit number. * * @retval device_t if a matching child device is found. * @retval NULL if no matching child device is found. */ device_t bhnd_find_child(device_t dev, bhnd_devclass_t class, int unit) { struct bhnd_core_match md = { BHND_MATCH_CORE_CLASS(class), BHND_MATCH_CORE_UNIT(unit) }; if (unit == -1) md.m.match.core_unit = 0; return bhnd_match_child(dev, &md); } /** * Find the first child device on @p dev that matches @p desc. * * @param parent The bhnd-compatible bus to be searched. * @param desc A match descriptor. * * @retval device_t if a matching child device is found. * @retval NULL if no matching child device is found. */ device_t bhnd_match_child(device_t dev, const struct bhnd_core_match *desc) { device_t *devlistp; device_t match; int devcnt; int error; error = device_get_children(dev, &devlistp, &devcnt); if (error != 0) return (NULL); match = NULL; for (int i = 0; i < devcnt; i++) { struct bhnd_core_info ci = bhnd_get_core_info(devlistp[i]); if (bhnd_core_matches(&ci, desc)) { match = devlistp[i]; goto done; } } done: free(devlistp, M_TEMP); return match; } /** * Walk up the bhnd device hierarchy to locate the root device * to which the bhndb bridge is attached. * * This can be used from within bhnd host bridge drivers to locate the * actual upstream host device. * * @param dev A bhnd device. * @param bus_class The expected bus (e.g. "pci") to which the bridge root * should be attached. * * @retval device_t if a matching parent device is found. * @retval NULL @p dev is not attached via a bhndb bus * @retval NULL no parent device is attached via @p bus_class. */ device_t bhnd_find_bridge_root(device_t dev, devclass_t bus_class) { devclass_t bhndb_class; device_t parent; KASSERT(device_get_devclass(device_get_parent(dev)) == bhnd_devclass, ("%s not a bhnd device", device_get_nameunit(dev))); bhndb_class = devclass_find("bhndb"); /* Walk the device tree until we hit a bridge */ parent = dev; while ((parent = device_get_parent(parent)) != NULL) { if (device_get_devclass(parent) == bhndb_class) break; } /* No bridge? */ if (parent == NULL) return (NULL); /* Search for a parent attached to the expected bus class */ while ((parent = device_get_parent(parent)) != NULL) { device_t bus; bus = device_get_parent(parent); if (bus != NULL && device_get_devclass(bus) == bus_class) return (parent); } /* Not found */ return (NULL); } /** * Find the first core in @p cores that matches @p desc. * * @param cores The table to search. * @param num_cores The length of @p cores. * @param desc A match descriptor. * * @retval bhnd_core_info if a matching core is found. * @retval NULL if no matching core is found. */ const struct bhnd_core_info * bhnd_match_core(const struct bhnd_core_info *cores, u_int num_cores, const struct bhnd_core_match *desc) { for (u_int i = 0; i < num_cores; i++) { if (bhnd_core_matches(&cores[i], desc)) return &cores[i]; } return (NULL); } /** * Find the first core in @p cores with the given @p class. * * @param cores The table to search. * @param num_cores The length of @p cores. * @param desc A match descriptor. * * @retval bhnd_core_info if a matching core is found. * @retval NULL if no matching core is found. */ const struct bhnd_core_info * bhnd_find_core(const struct bhnd_core_info *cores, u_int num_cores, bhnd_devclass_t class) { struct bhnd_core_match md = { BHND_MATCH_CORE_CLASS(class) }; 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. * * @param core A bhnd core descriptor. * @param desc A match descriptor to compare against @p core. * * @retval true if @p core matches @p match * @retval false if @p core does not match @p match. */ bool bhnd_core_matches(const struct bhnd_core_info *core, const struct bhnd_core_match *desc) { if (desc->m.match.core_vendor && desc->core_vendor != core->vendor) return (false); if (desc->m.match.core_id && desc->core_id != core->device) return (false); if (desc->m.match.core_unit && desc->core_unit != core->unit) return (false); if (desc->m.match.core_rev && !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); return true; } /** * Return true if the @p chip matches @p desc. * * @param chip A bhnd chip identifier. * @param desc A match descriptor to compare against @p chip. * * @retval true if @p chip matches @p match * @retval false if @p chip does not match @p match. */ bool bhnd_chip_matches(const struct bhnd_chipid *chip, const struct bhnd_chip_match *desc) { if (desc->m.match.chip_id && chip->chip_id != desc->chip_id) return (false); if (desc->m.match.chip_pkg && chip->chip_pkg != desc->chip_pkg) return (false); if (desc->m.match.chip_rev && !bhnd_hwrev_matches(chip->chip_rev, &desc->chip_rev)) return (false); return (true); } /** * Return true if the @p board matches @p desc. * * @param board The bhnd board info. * @param desc A match descriptor to compare against @p board. * * @retval true if @p chip matches @p match * @retval false if @p chip does not match @p match. */ bool bhnd_board_matches(const struct bhnd_board_info *board, const struct bhnd_board_match *desc) { if (desc->m.match.board_srom_rev && !bhnd_hwrev_matches(board->board_srom_rev, &desc->board_srom_rev)) return (false); if (desc->m.match.board_vendor && board->board_vendor != desc->board_vendor) return (false); if (desc->m.match.board_type && board->board_type != desc->board_type) return (false); if (desc->m.match.board_rev && !bhnd_hwrev_matches(board->board_rev, &desc->board_rev)) return (false); return (true); } /** * Return true if the @p hwrev matches @p desc. * * @param hwrev A bhnd hardware revision. * @param desc A match descriptor to compare against @p core. * * @retval true if @p hwrev matches @p match * @retval false if @p hwrev does not match @p match. */ bool bhnd_hwrev_matches(uint16_t hwrev, const struct bhnd_hwrev_match *desc) { if (desc->start != BHND_HWREV_INVALID && desc->start > hwrev) return false; if (desc->end != BHND_HWREV_INVALID && desc->end < hwrev) return false; return true; } /** * Return true if the @p dev matches @p desc. * * @param dev A bhnd device. * @param desc A match descriptor to compare against @p dev. * * @retval true if @p dev matches @p match * @retval false if @p dev does not match @p match. */ bool bhnd_device_matches(device_t dev, const struct bhnd_device_match *desc) { struct bhnd_core_info core; const struct bhnd_chipid *chip; struct bhnd_board_info board; device_t parent; int error; /* Construct individual match descriptors */ struct bhnd_core_match m_core = { _BHND_CORE_MATCH_COPY(desc) }; struct bhnd_chip_match m_chip = { _BHND_CHIP_MATCH_COPY(desc) }; struct bhnd_board_match m_board = { _BHND_BOARD_MATCH_COPY(desc) }; /* Fetch and match core info */ if (m_core.m.match_flags) { /* Only applicable to bhnd-attached cores */ parent = device_get_parent(dev); if (device_get_devclass(parent) != bhnd_devclass) { device_printf(dev, "attempting to match core " "attributes against non-core device\n"); return (false); } core = bhnd_get_core_info(dev); if (!bhnd_core_matches(&core, &m_core)) return (false); } /* Fetch and match chip info */ if (m_chip.m.match_flags) { chip = bhnd_get_chipid(dev); if (!bhnd_chip_matches(chip, &m_chip)) return (false); } /* Fetch and match board info. * * This is not available until after NVRAM is up; earlier device * matches should not include board requirements */ if (m_board.m.match_flags) { if ((error = bhnd_read_board_info(dev, &board))) { device_printf(dev, "failed to read required board info " "during device matching: %d\n", error); return (false); } if (!bhnd_board_matches(&board, &m_board)) return (false); } /* All matched */ return (true); } /** * Search @p table for an entry matching @p dev. * * @param dev A bhnd device to match against @p table. * @param table The device table to search. * @param entry_size The @p table entry size, in bytes. * * @retval bhnd_device the first matching device, if any. * @retval NULL if no matching device is found in @p table. */ const struct bhnd_device * bhnd_device_lookup(device_t dev, const struct bhnd_device *table, size_t entry_size) { const struct bhnd_device *entry; device_t hostb, parent; bhnd_attach_type attach_type; uint32_t dflags; parent = device_get_parent(dev); hostb = bhnd_find_hostb_device(parent); attach_type = bhnd_get_attach_type(dev); for (entry = table; !BHND_DEVICE_IS_END(entry); entry = (const struct bhnd_device *) ((const char *) entry + entry_size)) { /* match core info */ if (!bhnd_device_matches(dev, &entry->core)) continue; /* match device flags */ dflags = entry->device_flags; /* hostb implies BHND_ATTACH_ADAPTER requirement */ if (dflags & BHND_DF_HOSTB) dflags |= BHND_DF_ADAPTER; if (dflags & BHND_DF_ADAPTER) if (attach_type != BHND_ATTACH_ADAPTER) continue; if (dflags & BHND_DF_HOSTB) if (dev != hostb) continue; if (dflags & BHND_DF_SOC) if (attach_type != BHND_ATTACH_NATIVE) continue; /* device found */ return (entry); } /* not found */ return (NULL); } /** * Scan the device @p table for all quirk flags applicable to @p dev. * * @param dev A bhnd device to match against @p table. * @param table The device table to search. * * @return returns all matching quirk flags. */ uint32_t bhnd_device_quirks(device_t dev, const struct bhnd_device *table, size_t entry_size) { const struct bhnd_device *dent; const struct bhnd_device_quirk *qent, *qtable; uint32_t quirks; /* Locate the device entry */ if ((dent = bhnd_device_lookup(dev, table, entry_size)) == NULL) return (0); /* Quirks table is optional */ qtable = dent->quirks_table; if (qtable == NULL) return (0); /* Collect matching device quirk entries */ quirks = 0; for (qent = qtable; !BHND_DEVICE_QUIRK_IS_END(qent); qent++) { if (bhnd_device_matches(dev, &qent->desc)) quirks |= qent->quirks; } return (quirks); } /** * Allocate bhnd(4) resources defined in @p rs from a parent bus. * * @param dev The device requesting ownership of the resources. * @param rs A standard bus resource specification. This will be updated * with the allocated resource's RIDs. * @param res On success, the allocated bhnd resources. * * @retval 0 success * @retval non-zero if allocation of any non-RF_OPTIONAL resource fails, * all allocated resources will be released and a regular * unix error code will be returned. */ int bhnd_alloc_resources(device_t dev, struct resource_spec *rs, struct bhnd_resource **res) { /* Initialize output array */ for (u_int i = 0; rs[i].type != -1; i++) res[i] = NULL; for (u_int i = 0; rs[i].type != -1; i++) { res[i] = bhnd_alloc_resource_any(dev, rs[i].type, &rs[i].rid, rs[i].flags); /* Clean up all allocations on failure */ if (res[i] == NULL && !(rs[i].flags & RF_OPTIONAL)) { bhnd_release_resources(dev, rs, res); return (ENXIO); } } return (0); }; /** * Release bhnd(4) resources defined in @p rs from a parent bus. * * @param dev The device that owns the resources. * @param rs A standard bus resource specification previously initialized * by @p bhnd_alloc_resources. * @param res The bhnd resources to be released. */ void bhnd_release_resources(device_t dev, const struct resource_spec *rs, struct bhnd_resource **res) { for (u_int i = 0; rs[i].type != -1; i++) { if (res[i] == NULL) continue; bhnd_release_resource(dev, rs[i].type, rs[i].rid, res[i]); res[i] = NULL; } } /** * Parse the CHIPC_ID_* fields from the ChipCommon CHIPC_ID * register, returning its bhnd_chipid representation. * * @param idreg The CHIPC_ID register value. * @param enum_addr The enumeration address to include in the result. * * @warning * On early siba(4) devices, the ChipCommon core does not provide * a valid CHIPC_ID_NUMCORE field. On these ChipCommon revisions * (see CHIPC_NCORES_MIN_HWREV()), this function will parse and return * an invalid `ncores` value. */ struct bhnd_chipid bhnd_parse_chipid(uint32_t idreg, bhnd_addr_t enum_addr) { struct bhnd_chipid result; /* Fetch the basic chip info */ result.chip_id = CHIPC_GET_BITS(idreg, CHIPC_ID_CHIP); result.chip_pkg = CHIPC_GET_BITS(idreg, CHIPC_ID_PKG); result.chip_rev = CHIPC_GET_BITS(idreg, CHIPC_ID_REV); result.chip_type = CHIPC_GET_BITS(idreg, CHIPC_ID_BUS); result.ncores = CHIPC_GET_BITS(idreg, CHIPC_ID_NUMCORE); result.enum_addr = enum_addr; return (result); } /** * Determine the correct core count for a chip identification value that * may contain an invalid core count. * * On some early siba(4) devices (see CHIPC_NCORES_MIN_HWREV()), the ChipCommon * core does not provide a valid CHIPC_ID_NUMCORE field. * * @param cid The chip identification to be queried. * @param chipc_hwrev The hardware revision of the ChipCommon core from which * @p cid was parsed. * @param[out] ncores On success, will be set to the correct core count. * * @retval 0 If the core count is already correct, or was mapped to a * a correct value. * @retval EINVAL If the core count is incorrect, but the chip was not * recognized. */ int bhnd_chipid_fixed_ncores(const struct bhnd_chipid *cid, uint16_t chipc_hwrev, uint8_t *ncores) { /* bcma(4), and most siba(4) devices */ if (CHIPC_NCORES_MIN_HWREV(chipc_hwrev)) { *ncores = cid->ncores; return (0); } /* broken siba(4) chipsets */ switch (cid->chip_id) { case BHND_CHIPID_BCM4306: *ncores = 6; break; case BHND_CHIPID_BCM4704: *ncores = 9; break; case BHND_CHIPID_BCM5365: /* * BCM5365 does support ID_NUMCORE in at least * some of its revisions, but for unknown * reasons, Broadcom's drivers always exclude * the ChipCommon revision (0x5) used by BCM5365 * from the set of revisions supporting * ID_NUMCORE, and instead supply a fixed value. * * Presumably, at least some of these devices * shipped with a broken ID_NUMCORE value. */ *ncores = 7; break; default: return (EINVAL); } return (0); } /** * Allocate the resource defined by @p rs via @p dev, use it * to read the ChipCommon ID register relative to @p chipc_offset, * then release the resource. * * @param dev The device owning @p rs. * @param rs A resource spec that encompasses the ChipCommon register block. * @param chipc_offset The offset of the ChipCommon registers within @p rs. * @param[out] result the chip identification data. * * @retval 0 success * @retval non-zero if the ChipCommon identification data could not be read. */ int bhnd_read_chipid(device_t dev, struct resource_spec *rs, bus_size_t chipc_offset, struct bhnd_chipid *result) { struct resource *res; bhnd_addr_t enum_addr; uint32_t reg; uint8_t chip_type; int error, rid, rtype; rid = rs->rid; rtype = rs->type; error = 0; /* Allocate the ChipCommon window resource and fetch the chipid data */ res = bus_alloc_resource_any(dev, rtype, &rid, RF_ACTIVE); if (res == NULL) { device_printf(dev, "failed to allocate bhnd chipc resource\n"); return (ENXIO); } /* Fetch the basic chip info */ reg = bus_read_4(res, chipc_offset + CHIPC_ID); chip_type = CHIPC_GET_BITS(reg, CHIPC_ID_BUS); /* Fetch the EROMPTR */ if (BHND_CHIPTYPE_HAS_EROM(chip_type)) { enum_addr = bus_read_4(res, chipc_offset + CHIPC_EROMPTR); } else if (chip_type == BHND_CHIPTYPE_SIBA) { /* siba(4) uses the ChipCommon base address as the enumeration * address */ enum_addr = BHND_DEFAULT_CHIPC_ADDR; } else { device_printf(dev, "unknown chip type %hhu\n", chip_type); error = ENODEV; goto cleanup; } *result = bhnd_parse_chipid(reg, enum_addr); /* Fix the core count on early siba(4) devices */ if (chip_type == BHND_CHIPTYPE_SIBA) { uint32_t idh; uint16_t chipc_hwrev; /* * We need the ChipCommon revision to determine whether * the ncore field is valid. * * We can safely assume the siba IDHIGH register is mapped * within the chipc register block. */ idh = bus_read_4(res, SB0_REG_ABS(SIBA_CFG0_IDHIGH)); chipc_hwrev = SIBA_IDH_CORE_REV(idh); error = bhnd_chipid_fixed_ncores(result, chipc_hwrev, &result->ncores); if (error) goto cleanup; } cleanup: /* Clean up */ bus_release_resource(dev, rtype, rid, res); return (error); } /** * Read an NVRAM variable's NUL-terminated string value. * * @param dev A bhnd bus child device. * @param name The NVRAM variable name. * @param[out] buf A buffer large enough to hold @p len bytes. On * success, the NUL-terminated string value will be * written to this buffer. This argment may be NULL if * the value is not desired. * @param len The maximum capacity of @p buf. * @param[out] rlen On success, will be set to the actual size of * the requested value (including NUL termination). This * argment may be NULL if the size is not desired. * * @retval 0 success * @retval ENOENT The requested variable was not found. * @retval ENODEV No valid NVRAM source could be found. * @retval ENOMEM If @p buf is non-NULL and a buffer of @p len is too * small to hold the requested value. * @retval EFTYPE If the variable data cannot be coerced to a valid * string representation. * @retval ERANGE If value coercion would overflow @p type. * @retval non-zero If reading @p name otherwise fails, a regular unix * error code will be returned. */ int bhnd_nvram_getvar_str(device_t dev, const char *name, char *buf, size_t len, size_t *rlen) { size_t larg; int error; larg = len; error = bhnd_nvram_getvar(dev, name, buf, &larg, BHND_NVRAM_TYPE_CSTR); if (rlen != NULL) *rlen = larg; return (error); } /** * Read an NVRAM variable's unsigned integer value. * * @param dev A bhnd bus child device. * @param name The NVRAM variable name. * @param[out] value On success, the requested value will be written * to this pointer. * @param width The output integer type width (1, 2, or * 4 bytes). * * @retval 0 success * @retval ENOENT The requested variable was not found. * @retval ENODEV No valid NVRAM source could be found. * @retval EFTYPE If the variable data cannot be coerced to a * a valid unsigned integer representation. * @retval ERANGE If value coercion would overflow (or underflow) an * unsigned representation of the given @p width. * @retval non-zero If reading @p name otherwise fails, a regular unix * error code will be returned. */ int bhnd_nvram_getvar_uint(device_t dev, const char *name, void *value, int width) { bhnd_nvram_type type; size_t len; switch (width) { case 1: type = BHND_NVRAM_TYPE_UINT8; break; case 2: type = BHND_NVRAM_TYPE_UINT16; break; case 4: type = BHND_NVRAM_TYPE_UINT32; break; default: device_printf(dev, "unsupported NVRAM integer width: %d\n", width); return (EINVAL); } len = width; return (bhnd_nvram_getvar(dev, name, value, &len, type)); } /** * Read an NVRAM variable's unsigned 8-bit integer value. * * @param dev A bhnd bus child device. * @param name The NVRAM variable name. * @param[out] value On success, the requested value will be written * to this pointer. * * @retval 0 success * @retval ENOENT The requested variable was not found. * @retval ENODEV No valid NVRAM source could be found. * @retval EFTYPE If the variable data cannot be coerced to a * a valid unsigned integer representation. * @retval ERANGE If value coercion would overflow (or underflow) uint8_t. * @retval non-zero If reading @p name otherwise fails, a regular unix * error code will be returned. */ int bhnd_nvram_getvar_uint8(device_t dev, const char *name, uint8_t *value) { return (bhnd_nvram_getvar_uint(dev, name, value, sizeof(*value))); } /** * Read an NVRAM variable's unsigned 16-bit integer value. * * @param dev A bhnd bus child device. * @param name The NVRAM variable name. * @param[out] value On success, the requested value will be written * to this pointer. * * @retval 0 success * @retval ENOENT The requested variable was not found. * @retval ENODEV No valid NVRAM source could be found. * @retval EFTYPE If the variable data cannot be coerced to a * a valid unsigned integer representation. * @retval ERANGE If value coercion would overflow (or underflow) * uint16_t. * @retval non-zero If reading @p name otherwise fails, a regular unix * error code will be returned. */ int bhnd_nvram_getvar_uint16(device_t dev, const char *name, uint16_t *value) { return (bhnd_nvram_getvar_uint(dev, name, value, sizeof(*value))); } /** * Read an NVRAM variable's unsigned 32-bit integer value. * * @param dev A bhnd bus child device. * @param name The NVRAM variable name. * @param[out] value On success, the requested value will be written * to this pointer. * * @retval 0 success * @retval ENOENT The requested variable was not found. * @retval ENODEV No valid NVRAM source could be found. * @retval EFTYPE If the variable data cannot be coerced to a * a valid unsigned integer representation. * @retval ERANGE If value coercion would overflow (or underflow) * uint32_t. * @retval non-zero If reading @p name otherwise fails, a regular unix * error code will be returned. */ int bhnd_nvram_getvar_uint32(device_t dev, const char *name, uint32_t *value) { return (bhnd_nvram_getvar_uint(dev, name, value, sizeof(*value))); } /** * Read an NVRAM variable's signed integer value. * * @param dev A bhnd bus child device. * @param name The NVRAM variable name. * @param[out] value On success, the requested value will be written * to this pointer. * @param width The output integer type width (1, 2, or * 4 bytes). * * @retval 0 success * @retval ENOENT The requested variable was not found. * @retval ENODEV No valid NVRAM source could be found. * @retval EFTYPE If the variable data cannot be coerced to a * a valid integer representation. * @retval ERANGE If value coercion would overflow (or underflow) an * signed representation of the given @p width. * @retval non-zero If reading @p name otherwise fails, a regular unix * error code will be returned. */ int bhnd_nvram_getvar_int(device_t dev, const char *name, void *value, int width) { bhnd_nvram_type type; size_t len; switch (width) { case 1: type = BHND_NVRAM_TYPE_INT8; break; case 2: type = BHND_NVRAM_TYPE_INT16; break; case 4: type = BHND_NVRAM_TYPE_INT32; break; default: device_printf(dev, "unsupported NVRAM integer width: %d\n", width); return (EINVAL); } len = width; return (bhnd_nvram_getvar(dev, name, value, &len, type)); } /** * Read an NVRAM variable's signed 8-bit integer value. * * @param dev A bhnd bus child device. * @param name The NVRAM variable name. * @param[out] value On success, the requested value will be written * to this pointer. * * @retval 0 success * @retval ENOENT The requested variable was not found. * @retval ENODEV No valid NVRAM source could be found. * @retval EFTYPE If the variable data cannot be coerced to a * a valid integer representation. * @retval ERANGE If value coercion would overflow (or underflow) int8_t. * @retval non-zero If reading @p name otherwise fails, a regular unix * error code will be returned. */ int bhnd_nvram_getvar_int8(device_t dev, const char *name, int8_t *value) { return (bhnd_nvram_getvar_int(dev, name, value, sizeof(*value))); } /** * Read an NVRAM variable's signed 16-bit integer value. * * @param dev A bhnd bus child device. * @param name The NVRAM variable name. * @param[out] value On success, the requested value will be written * to this pointer. * * @retval 0 success * @retval ENOENT The requested variable was not found. * @retval ENODEV No valid NVRAM source could be found. * @retval EFTYPE If the variable data cannot be coerced to a * a valid integer representation. * @retval ERANGE If value coercion would overflow (or underflow) * int16_t. * @retval non-zero If reading @p name otherwise fails, a regular unix * error code will be returned. */ int bhnd_nvram_getvar_int16(device_t dev, const char *name, int16_t *value) { return (bhnd_nvram_getvar_int(dev, name, value, sizeof(*value))); } /** * Read an NVRAM variable's signed 32-bit integer value. * * @param dev A bhnd bus child device. * @param name The NVRAM variable name. * @param[out] value On success, the requested value will be written * to this pointer. * * @retval 0 success * @retval ENOENT The requested variable was not found. * @retval ENODEV No valid NVRAM source could be found. * @retval EFTYPE If the variable data cannot be coerced to a * a valid integer representation. * @retval ERANGE If value coercion would overflow (or underflow) * int32_t. * @retval non-zero If reading @p name otherwise fails, a regular unix * error code will be returned. */ int bhnd_nvram_getvar_int32(device_t dev, const char *name, int32_t *value) { return (bhnd_nvram_getvar_int(dev, name, value, sizeof(*value))); } /** * Read an NVRAM variable's array value. * * @param dev A bhnd bus child device. * @param name The NVRAM variable name. * @param[out] buf A buffer large enough to hold @p size bytes. * On success, the requested value will be written * to this buffer. * @param[in,out] size The required number of bytes to write to * @p buf. * @param type The desired array element data representation. * * @retval 0 success * @retval ENOENT The requested variable was not found. * @retval ENODEV No valid NVRAM source could be found. * @retval ENXIO If less than @p size bytes are available. * @retval ENOMEM If a buffer of @p size is too small to hold the * requested value. * @retval EFTYPE If the variable data cannot be coerced to a * a valid instance of @p type. * @retval ERANGE If value coercion would overflow (or underflow) a * representation of @p type. * @retval non-zero If reading @p name otherwise fails, a regular unix * error code will be returned. */ int bhnd_nvram_getvar_array(device_t dev, const char *name, void *buf, size_t size, bhnd_nvram_type type) { size_t nbytes; int error; /* Attempt read */ nbytes = size; if ((error = bhnd_nvram_getvar(dev, name, buf, &nbytes, type))) return (error); /* Verify that the expected number of bytes were fetched */ if (nbytes < size) return (ENXIO); return (0); } /** * Using the bhnd(4) bus-level core information and a custom core name, * populate @p dev's device description. * * @param dev A bhnd-bus attached device. * @param dev_name The core's name (e.g. "SDIO Device Core") */ void bhnd_set_custom_core_desc(device_t dev, const char *dev_name) { const char *vendor_name; char *desc; vendor_name = bhnd_get_vendor_name(dev); asprintf(&desc, M_BHND, "%s %s, rev %hhu", vendor_name, dev_name, bhnd_get_hwrev(dev)); if (desc != NULL) { device_set_desc_copy(dev, desc); free(desc, M_BHND); } else { device_set_desc(dev, dev_name); } } /** * Using the bhnd(4) bus-level core information, populate @p dev's device * description. * * @param dev A bhnd-bus attached device. */ void bhnd_set_default_core_desc(device_t dev) { bhnd_set_custom_core_desc(dev, bhnd_get_device_name(dev)); } /** * Using the bhnd @p chip_id, populate the bhnd(4) bus @p dev's device * description. * * @param dev A bhnd-bus attached device. */ void bhnd_set_default_bus_desc(device_t dev, const struct bhnd_chipid *chip_id) { const char *bus_name; char *desc; char chip_name[BHND_CHIPID_MAX_NAMELEN]; /* Determine chip type's bus name */ switch (chip_id->chip_type) { case BHND_CHIPTYPE_SIBA: bus_name = "SIBA bus"; break; case BHND_CHIPTYPE_BCMA: case BHND_CHIPTYPE_BCMA_ALT: bus_name = "BCMA bus"; break; case BHND_CHIPTYPE_UBUS: bus_name = "UBUS bus"; break; default: bus_name = "Unknown Type"; break; } /* Format chip name */ bhnd_format_chip_id(chip_name, sizeof(chip_name), chip_id->chip_id); /* Format and set device description */ asprintf(&desc, M_BHND, "%s %s", chip_name, bus_name); if (desc != NULL) { device_set_desc_copy(dev, desc); free(desc, M_BHND); } else { device_set_desc(dev, bus_name); } } /** * Helper function for implementing BHND_BUS_IS_HW_DISABLED(). * * If a parent device is available, this implementation delegates the * request to the BHND_BUS_IS_HW_DISABLED() method on the parent of @p dev. * * If no parent device is available (i.e. on a the bus root), the hardware * is assumed to be usable and false is returned. */ bool bhnd_bus_generic_is_hw_disabled(device_t dev, device_t child) { if (device_get_parent(dev) != NULL) return (BHND_BUS_IS_HW_DISABLED(device_get_parent(dev), child)); return (false); } /** * Helper function for implementing BHND_BUS_GET_CHIPID(). * * This implementation delegates the request to the BHND_BUS_GET_CHIPID() * method on the parent of @p dev. If no parent exists, the implementation * will panic. */ const struct bhnd_chipid * bhnd_bus_generic_get_chipid(device_t dev, device_t child) { if (device_get_parent(dev) != NULL) return (BHND_BUS_GET_CHIPID(device_get_parent(dev), child)); panic("missing BHND_BUS_GET_CHIPID()"); } /* nvram board_info population macros for bhnd_bus_generic_read_board_info() */ #define BHND_GV(_dest, _name) \ bhnd_nvram_getvar_uint(child, BHND_NVAR_ ## _name, &_dest, \ sizeof(_dest)) #define REQ_BHND_GV(_dest, _name) do { \ if ((error = BHND_GV(_dest, _name))) { \ device_printf(dev, \ "error reading " __STRING(_name) ": %d\n", error); \ return (error); \ } \ } while(0) #define OPT_BHND_GV(_dest, _name, _default) do { \ if ((error = BHND_GV(_dest, _name))) { \ if (error != ENOENT) { \ device_printf(dev, \ "error reading " \ __STRING(_name) ": %d\n", error); \ return (error); \ } \ _dest = _default; \ } \ } while(0) /** * Helper function for implementing BHND_BUS_READ_BOARDINFO(). * * This implementation populates @p info with information from NVRAM, * defaulting board_vendor and board_type fields to 0 if the * requested variables cannot be found. * * This behavior is correct for most SoCs, but must be overridden on * bridged (PCI, PCMCIA, etc) devices to produce a complete bhnd_board_info * result. */ int bhnd_bus_generic_read_board_info(device_t dev, device_t child, struct bhnd_board_info *info) { int error; OPT_BHND_GV(info->board_vendor, BOARDVENDOR, 0); OPT_BHND_GV(info->board_type, BOARDTYPE, 0); /* srom >= 2 */ REQ_BHND_GV(info->board_rev, BOARDREV); OPT_BHND_GV(info->board_srom_rev,SROMREV, 0); /* missing in some SoC NVRAM */ REQ_BHND_GV(info->board_flags, BOARDFLAGS); OPT_BHND_GV(info->board_flags2, BOARDFLAGS2, 0); /* srom >= 4 */ OPT_BHND_GV(info->board_flags3, BOARDFLAGS3, 0); /* srom >= 11 */ return (0); } #undef BHND_GV #undef BHND_GV_REQ #undef BHND_GV_OPT /** * Helper function for implementing BHND_BUS_GET_NVRAM_VAR(). * * This implementation searches @p dev for a usable NVRAM child device. * * If no usable child device is found on @p dev, the request is delegated to * the BHND_BUS_GET_NVRAM_VAR() method on the parent of @p dev. */ int bhnd_bus_generic_get_nvram_var(device_t dev, device_t child, const char *name, void *buf, size_t *size, bhnd_nvram_type type) { device_t nvram; device_t parent; /* Make sure we're holding Giant for newbus */ GIANT_REQUIRED; /* Look for a directly-attached NVRAM child */ if ((nvram = device_find_child(dev, "bhnd_nvram", -1)) != NULL) return BHND_NVRAM_GETVAR(nvram, name, buf, size, type); /* Try to delegate to parent */ if ((parent = device_get_parent(dev)) == NULL) return (ENODEV); return (BHND_BUS_GET_NVRAM_VAR(device_get_parent(dev), child, name, buf, size, type)); } /** * Helper function for implementing BHND_BUS_ALLOC_RESOURCE(). * * This implementation of BHND_BUS_ALLOC_RESOURCE() delegates allocation * of the underlying resource to BUS_ALLOC_RESOURCE(), and activation * to @p dev's BHND_BUS_ACTIVATE_RESOURCE(). */ struct bhnd_resource * bhnd_bus_generic_alloc_resource(device_t dev, device_t child, int type, int *rid, rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) { struct bhnd_resource *br; struct resource *res; int error; br = NULL; res = NULL; /* Allocate the real bus resource (without activating it) */ res = BUS_ALLOC_RESOURCE(dev, child, type, rid, start, end, count, (flags & ~RF_ACTIVE)); if (res == NULL) return (NULL); /* Allocate our bhnd resource wrapper. */ br = malloc(sizeof(struct bhnd_resource), M_BHND, M_NOWAIT); if (br == NULL) goto failed; br->direct = false; br->res = res; /* Attempt activation */ if (flags & RF_ACTIVE) { error = BHND_BUS_ACTIVATE_RESOURCE(dev, child, type, *rid, br); if (error) goto failed; } return (br); failed: if (res != NULL) BUS_RELEASE_RESOURCE(dev, child, type, *rid, res); free(br, M_BHND); return (NULL); } /** * Helper function for implementing BHND_BUS_RELEASE_RESOURCE(). * * This implementation of BHND_BUS_RELEASE_RESOURCE() delegates release of * the backing resource to BUS_RELEASE_RESOURCE(). */ int bhnd_bus_generic_release_resource(device_t dev, device_t child, int type, int rid, struct bhnd_resource *r) { int error; if ((error = BUS_RELEASE_RESOURCE(dev, child, type, rid, r->res))) return (error); free(r, M_BHND); return (0); } /** * Helper function for implementing BHND_BUS_ACTIVATE_RESOURCE(). * * This implementation of BHND_BUS_ACTIVATE_RESOURCE() first calls the * BHND_BUS_ACTIVATE_RESOURCE() method of the parent of @p dev. * * If this fails, and if @p dev is the direct parent of @p child, standard * resource activation is attempted via bus_activate_resource(). This enables * direct use of the bhnd(4) resource APIs on devices that may not be attached * to a parent bhnd bus or bridge. */ int bhnd_bus_generic_activate_resource(device_t dev, device_t child, int type, int rid, struct bhnd_resource *r) { int error; bool passthrough; passthrough = (device_get_parent(child) != dev); /* Try to delegate to the parent */ if (device_get_parent(dev) != NULL) { error = BHND_BUS_ACTIVATE_RESOURCE(device_get_parent(dev), child, type, rid, r); } else { error = ENODEV; } /* If bhnd(4) activation has failed and we're the child's direct * parent, try falling back on standard resource activation. */ if (error && !passthrough) { error = bus_activate_resource(child, type, rid, r->res); if (!error) r->direct = true; } return (error); }; /** * Helper function for implementing BHND_BUS_DEACTIVATE_RESOURCE(). * * This implementation of BHND_BUS_ACTIVATE_RESOURCE() simply calls the * BHND_BUS_ACTIVATE_RESOURCE() method of the parent of @p dev. */ int bhnd_bus_generic_deactivate_resource(device_t dev, device_t child, int type, int rid, struct bhnd_resource *r) { if (device_get_parent(dev) != NULL) return (BHND_BUS_DEACTIVATE_RESOURCE(device_get_parent(dev), child, type, rid, r)); return (EINVAL); }; Index: head/sys/dev/bhnd/bhndb/bhnd_bhndb.c =================================================================== --- head/sys/dev/bhnd/bhndb/bhnd_bhndb.c (revision 305370) +++ head/sys/dev/bhnd/bhndb/bhnd_bhndb.c (revision 305371) @@ -1,108 +1,125 @@ /*- * Copyright (c) 2015-2016 Landon Fuller * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include "bhndbvar.h" /* * bhnd(4) driver mix-in providing a shared common methods for * bhnd devices attached via a bhndb bridge. */ static int bhnd_bhndb_read_board_info(device_t dev, device_t child, struct bhnd_board_info *info) { int error; /* Initialize with NVRAM-derived values */ if ((error = bhnd_bus_generic_read_board_info(dev, child, info))) return (error); /* Let the bridge fill in any additional data */ return (BHNDB_POPULATE_BOARD_INFO(device_get_parent(dev), dev, info)); } static bhnd_attach_type bhnd_bhndb_get_attach_type(device_t dev, device_t child) { /* It's safe to assume that a bridged device is always an adapter */ 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) { /* Delegate to parent bridge */ return (BHND_BUS_PWRCTL_GET_CLKSRC(device_get_parent(dev), child, clock)); } static int bhnd_bhndb_pwrctl_gate_clock(device_t dev, device_t child, bhnd_clock clock) { /* Delegate to parent bridge */ return (BHND_BUS_PWRCTL_GATE_CLOCK(device_get_parent(dev), child, clock)); } static int bhnd_bhndb_pwrctl_ungate_clock(device_t dev, device_t child, bhnd_clock clock) { /* Delegate to parent bridge */ return (BHND_BUS_PWRCTL_UNGATE_CLOCK(device_get_parent(dev), child, clock)); } 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), DEVMETHOD(bhnd_bus_pwrctl_gate_clock, bhnd_bhndb_pwrctl_gate_clock), DEVMETHOD(bhnd_bus_pwrctl_ungate_clock, bhnd_bhndb_pwrctl_ungate_clock), DEVMETHOD_END }; DEFINE_CLASS_0(bhnd, bhnd_bhndb_driver, bhnd_bhndb_methods, 0); Index: head/sys/dev/bhnd/bhndb/bhndb.c =================================================================== --- head/sys/dev/bhnd/bhndb/bhndb.c (revision 305370) +++ head/sys/dev/bhnd/bhndb/bhndb.c (revision 305371) @@ -1,2034 +1,2225 @@ /*- * Copyright (c) 2015 Landon Fuller * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. */ #include __FBSDID("$FreeBSD$"); /* * Abstract BHND Bridge Device Driver * * Provides generic support for bridging from a parent bus (such as PCI) to * a BHND-compatible bus (e.g. bcma or siba). */ #include #include #include #include #include #include #include #include #include #include +#include + #include #include #include "bhnd_chipc_if.h" #include "bhnd_nvram_if.h" #include "bhndbvar.h" #include "bhndb_bus_if.h" #include "bhndb_hwdata.h" #include "bhndb_private.h" /* Debugging flags */ static u_long bhndb_debug = 0; TUNABLE_ULONG("hw.bhndb.debug", &bhndb_debug); enum { BHNDB_DEBUG_PRIO = 1 << 0, }; #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_find_hostb_core(struct bhndb_softc *sc, + bhnd_erom_t *erom, + struct bhnd_core_info *core); -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 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); static struct rman *bhndb_get_rman(struct bhndb_softc *sc, device_t child, int type); static int bhndb_init_child_resource(struct resource *r, struct resource *parent, bhnd_size_t offset, bhnd_size_t size); static int bhndb_activate_static_region( struct bhndb_softc *sc, struct bhndb_region *region, device_t child, int type, int rid, struct resource *r); static int bhndb_try_activate_resource( struct bhndb_softc *sc, device_t child, 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(). * * This function provides the default bhndb implementation of DEVICE_PROBE(), * and is compatible with bhndb(4) bridges attached via bhndb_attach_bridge(). */ int bhndb_generic_probe(device_t dev) { return (BUS_PROBE_NOWILDCARD); } static void bhndb_probe_nomatch(device_t dev, device_t child) { const char *name; name = device_get_name(child); if (name == NULL) name = "unknown device"; device_printf(dev, "<%s> (no driver attached)\n", name); } static int bhndb_print_child(device_t dev, device_t child) { struct bhndb_softc *sc; struct resource_list *rl; int retval = 0; sc = device_get_softc(dev); retval += bus_print_child_header(dev, child); rl = BUS_GET_RESOURCE_LIST(dev, child); if (rl != NULL) { retval += resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#jx"); retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%jd"); } retval += bus_print_child_domain(dev, child); retval += bus_print_child_footer(dev, child); return (retval); } static int bhndb_child_pnpinfo_str(device_t bus, device_t child, char *buf, size_t buflen) { *buf = '\0'; return (0); } static int bhndb_child_location_str(device_t dev, device_t child, char *buf, size_t buflen) { struct bhndb_softc *sc; sc = device_get_softc(dev); snprintf(buf, buflen, "base=0x%llx", (unsigned long long) sc->chipid.enum_addr); return (0); } /** - * 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; break; } if (!found) return (false); } return (true); } /** - * 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; bhnd_size_t size; size_t prio_low, prio_default, prio_high; int error; /* The number of port regions per priority band that must be accessible * via dynamic register windows */ prio_low = 0; prio_default = 0; prio_high = 0; /* * 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. * * If the port size is smaller than the window size, * this ensures that we fully utilize register windows * larger than the referenced port. * * If the port size is larger than the window size, this * ensures that we do not directly map the allocations * within the region to a too-small window. */ size = regw->win_size; /* * Add to the bus region list. * * 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); } } /* * 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; /* ... are explicitly disabled in the priority table. */ if (hp->priority == BHNDB_PRIORITY_NONE) continue; /* Determine the number of dynamic windows required and * register their bus_region entries. */ for (u_int i = 0; i < hp->num_ports; i++) { 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); /* Update port mapping counts */ switch (pp->priority) { case BHNDB_PRIORITY_NONE: break; case BHNDB_PRIORITY_LOW: prio_low++; break; case BHNDB_PRIORITY_DEFAULT: prio_default++; break; case BHNDB_PRIORITY_HIGH: prio_high++; break; } } } /* 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)) { struct bhndb_region *region; 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"; type_msg = region->static_regwin ? "static" : "dynamic"; device_printf(sc->dev, "region 0x%llx+0x%llx priority " "%u %s/%s\n", (unsigned long long) region->addr, (unsigned long long) region->size, region->priority, direct_msg, type_msg); } } return (0); } /** * 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. * * @retval 0 success * @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 */ *hw = next; return (0); } return (ENOENT); } /** - * 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 * cores (e.g. PCMCIA on a PCIe device). */ int bhndb_attach(device_t dev, bhnd_devclass_t bridge_devclass) { 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); sc->dev = dev; sc->parent_dev = device_get_parent(dev); sc->bridge_class = bridge_devclass; 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); if (sc->bus_res != NULL) bhndb_free_resources(sc->bus_res); 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 function provides the default bhndb implementation of - * BHNDB_INIT_FULL_CONFIG(), and must be called by any subclass driver - * overriding BHNDB_INIT_FULL_CONFIG(). + * This may return a borrowed reference to a bhndb_dw_alloc-managed + * resource; any additional resource mapping requests may invalidate this + * borrowed reference. * - * 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. + * @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. * - * 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_hwcfg *cfg; + struct bhndb_dw_alloc *dwa; + struct resource *res; + const struct bhndb_regwin *win; + + 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; + + 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; + } + } + + /* 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; - struct bhndb_resources *r; - device_t *devs; - device_t hostb; - int ndevs; + u_int ncores; int error; - sc = device_get_softc(dev); - hostb = NULL; + erom = NULL; + cores = NULL; + br = NULL; - /* 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"); - return (error); + /* 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); } - /* Find our host bridge device */ - hostb = BHNDB_FIND_HOSTB_DEVICE(dev, child); - if (hostb == NULL) { + /* 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; } 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); } /** * Default bhndb(4) implementation of DEVICE_DETACH(). * * This function detaches any child devices, and if successful, releases all * resources held by the bridge device. */ int bhndb_generic_detach(device_t dev) { struct bhndb_softc *sc; int error; sc = device_get_softc(dev); /* Detach children */ if ((error = bus_generic_detach(dev))) return (error); /* Clean up our driver state. */ bhndb_free_resources(sc->bus_res); BHNDB_LOCK_DESTROY(sc); return (0); } /** * Default bhndb(4) implementation of DEVICE_SUSPEND(). * * This function calls bus_generic_suspend() (or implements equivalent * behavior). */ int bhndb_generic_suspend(device_t dev) { return (bus_generic_suspend(dev)); } /** * Default bhndb(4) implementation of DEVICE_RESUME(). * * This function calls bus_generic_resume() (or implements equivalent * behavior). */ int bhndb_generic_resume(device_t dev) { struct bhndb_softc *sc; struct bhndb_resources *bus_res; struct bhndb_dw_alloc *dwa; int error; sc = device_get_softc(dev); bus_res = sc->bus_res; /* Guarantee that all in-use dynamic register windows are mapped to * their previously configured target address. */ BHNDB_LOCK(sc); for (size_t i = 0; i < bus_res->dwa_count; i++) { dwa = &bus_res->dw_alloc[i]; /* Skip regions that were not previously used */ if (bhndb_dw_is_free(bus_res, dwa) && dwa->target == 0x0) continue; /* Otherwise, ensure the register window is correct before * any children attempt MMIO */ error = BHNDB_SET_WINDOW_ADDR(dev, dwa->win, dwa->target); if (error) break; } BHNDB_UNLOCK(sc); /* Error restoring hardware state; children cannot be safely resumed */ if (error) { device_printf(dev, "Unable to restore hardware configuration; " "cannot resume: %d\n", error); return (error); } return (bus_generic_resume(dev)); } /** * Default implementation of BHNDB_SUSPEND_RESOURCE. */ static void bhndb_suspend_resource(device_t dev, device_t child, int type, struct resource *r) { struct bhndb_softc *sc; struct bhndb_dw_alloc *dwa; sc = device_get_softc(dev); // TODO: IRQs? if (type != SYS_RES_MEMORY) return; BHNDB_LOCK(sc); dwa = bhndb_dw_find_resource(sc->bus_res, r); if (dwa == NULL) { BHNDB_UNLOCK(sc); return; } if (BHNDB_DEBUG(PRIO)) device_printf(child, "suspend resource type=%d 0x%jx+0x%jx\n", type, rman_get_start(r), rman_get_size(r)); /* Release the resource's window reference */ bhndb_dw_release(sc->bus_res, dwa, r); BHNDB_UNLOCK(sc); } /** * Default implementation of BHNDB_RESUME_RESOURCE. */ static int bhndb_resume_resource(device_t dev, device_t child, int type, struct resource *r) { struct bhndb_softc *sc; sc = device_get_softc(dev); // TODO: IRQs? if (type != SYS_RES_MEMORY) return (0); /* Inactive resources don't require reallocation of bridge resources */ if (!(rman_get_flags(r) & RF_ACTIVE)) return (0); if (BHNDB_DEBUG(PRIO)) device_printf(child, "resume resource type=%d 0x%jx+0x%jx\n", type, rman_get_start(r), rman_get_size(r)); return (bhndb_try_activate_resource(sc, rman_get_device(r), type, rman_get_rid(r), r, NULL)); } /** * Default bhndb(4) implementation of BUS_READ_IVAR(). */ static int bhndb_read_ivar(device_t dev, device_t child, int index, uintptr_t *result) { return (ENOENT); } /** * Default bhndb(4) implementation of BUS_WRITE_IVAR(). */ static int bhndb_write_ivar(device_t dev, device_t child, int index, uintptr_t value) { return (ENOENT); } /** * Return the address space for the given @p child device. */ bhndb_addrspace bhndb_get_addrspace(struct bhndb_softc *sc, device_t child) { struct bhndb_devinfo *dinfo; device_t imd_dev; /* Find the directly attached parent of the requesting device */ imd_dev = child; while (imd_dev != NULL && device_get_parent(imd_dev) != sc->dev) imd_dev = device_get_parent(imd_dev); if (imd_dev == NULL) panic("bhndb address space request for non-child device %s\n", device_get_nameunit(child)); dinfo = device_get_ivars(imd_dev); return (dinfo->addrspace); } /** * Return the rman instance for a given resource @p type, if any. * * @param sc The bhndb device state. * @param child The requesting child. * @param type The resource type (e.g. SYS_RES_MEMORY, SYS_RES_IRQ, ...) */ static struct rman * bhndb_get_rman(struct bhndb_softc *sc, device_t child, int type) { switch (bhndb_get_addrspace(sc, child)) { case BHNDB_ADDRSPACE_NATIVE: switch (type) { case SYS_RES_MEMORY: return (&sc->bus_res->ht_mem_rman); case SYS_RES_IRQ: return (NULL); default: return (NULL); }; case BHNDB_ADDRSPACE_BRIDGED: switch (type) { case SYS_RES_MEMORY: return (&sc->bus_res->br_mem_rman); case SYS_RES_IRQ: // TODO // return &sc->irq_rman; return (NULL); default: return (NULL); }; } /* Quieten gcc */ return (NULL); } /** * Default implementation of BUS_ADD_CHILD() */ static device_t bhndb_add_child(device_t dev, u_int order, const char *name, int unit) { struct bhndb_devinfo *dinfo; device_t child; child = device_add_child_ordered(dev, order, name, unit); if (child == NULL) return (NULL); dinfo = malloc(sizeof(struct bhndb_devinfo), M_BHND, M_NOWAIT); if (dinfo == NULL) { device_delete_child(dev, child); return (NULL); } dinfo->addrspace = BHNDB_ADDRSPACE_NATIVE; resource_list_init(&dinfo->resources); device_set_ivars(child, dinfo); return (child); } /** * Default implementation of BUS_CHILD_DELETED(). */ static void bhndb_child_deleted(device_t dev, device_t child) { struct bhndb_devinfo *dinfo = device_get_ivars(child); if (dinfo != NULL) { resource_list_free(&dinfo->resources); free(dinfo, M_BHND); } device_set_ivars(child, NULL); } /** * Default implementation of BHNDB_GET_CHIPID(). */ static const struct bhnd_chipid * bhndb_get_chipid(device_t dev, device_t child) { struct bhndb_softc *sc = device_get_softc(dev); return (&sc->chipid); } /** - * 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 */ if (BHNDB_BUS_IS_CORE_DISABLED(sc->parent_dev, dev, &core)) return (true); /* 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; + struct bhndb_softc *sc = device_get_softc(dev); - 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); - - return (hostb_dev); + *core = *bhndb_get_bridge_core(sc); + return (0); } /** * Default bhndb(4) implementation of BUS_ALLOC_RESOURCE(). */ static struct resource * bhndb_alloc_resource(device_t dev, device_t child, int type, int *rid, rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) { struct bhndb_softc *sc; struct resource_list_entry *rle; struct resource *rv; struct rman *rm; int error; bool passthrough, isdefault; sc = device_get_softc(dev); passthrough = (device_get_parent(child) != dev); isdefault = RMAN_IS_DEFAULT_RANGE(start, end); rle = NULL; /* Populate defaults */ if (!passthrough && isdefault) { /* Fetch the resource list entry. */ rle = resource_list_find(BUS_GET_RESOURCE_LIST(dev, child), type, *rid); if (rle == NULL) { device_printf(dev, "default resource %#x type %d for child %s " "not found\n", *rid, type, device_get_nameunit(child)); return (NULL); } if (rle->res != NULL) { device_printf(dev, "resource entry %#x type %d for child %s is busy\n", *rid, type, device_get_nameunit(child)); return (NULL); } start = rle->start; end = rle->end; count = ulmax(count, rle->count); } /* Validate resource addresses */ if (start > end || count > ((end - start) + 1)) return (NULL); /* Fetch the resource manager */ rm = bhndb_get_rman(sc, child, type); if (rm == NULL) return (NULL); /* Make our reservation */ rv = rman_reserve_resource(rm, start, end, count, flags & ~RF_ACTIVE, child); if (rv == NULL) return (NULL); rman_set_rid(rv, *rid); /* Activate */ if (flags & RF_ACTIVE) { error = bus_activate_resource(child, type, *rid, rv); if (error) { device_printf(dev, "failed to activate entry %#x type %d for " "child %s: %d\n", *rid, type, device_get_nameunit(child), error); rman_release_resource(rv); return (NULL); } } /* Update child's resource list entry */ if (rle != NULL) { rle->res = rv; rle->start = rman_get_start(rv); rle->end = rman_get_end(rv); rle->count = rman_get_size(rv); } return (rv); } /** * Default bhndb(4) implementation of BUS_RELEASE_RESOURCE(). */ static int bhndb_release_resource(device_t dev, device_t child, int type, int rid, struct resource *r) { struct resource_list_entry *rle; bool passthrough; int error; passthrough = (device_get_parent(child) != dev); /* Deactivate resources */ if (rman_get_flags(r) & RF_ACTIVE) { error = BUS_DEACTIVATE_RESOURCE(dev, child, type, rid, r); if (error) return (error); } if ((error = rman_release_resource(r))) return (error); if (!passthrough) { /* Clean resource list entry */ rle = resource_list_find(BUS_GET_RESOURCE_LIST(dev, child), type, rid); if (rle != NULL) rle->res = NULL; } return (0); } /** * Default bhndb(4) implementation of BUS_ADJUST_RESOURCE(). */ static int bhndb_adjust_resource(device_t dev, device_t child, int type, struct resource *r, rman_res_t start, rman_res_t end) { struct bhndb_softc *sc; struct rman *rm; rman_res_t mstart, mend; int error; sc = device_get_softc(dev); error = 0; /* Verify basic constraints */ if (end <= start) return (EINVAL); /* Fetch resource manager */ rm = bhndb_get_rman(sc, child, type); if (rm == NULL) return (ENXIO); if (!rman_is_region_manager(r, rm)) return (ENXIO); BHNDB_LOCK(sc); /* If not active, allow any range permitted by the resource manager */ if (!(rman_get_flags(r) & RF_ACTIVE)) goto done; /* Otherwise, the range is limited to the existing register window * mapping */ error = bhndb_find_resource_limits(sc->bus_res, r, &mstart, &mend); if (error) goto done; if (start < mstart || end > mend) { error = EINVAL; goto done; } /* Fall through */ done: if (!error) error = rman_adjust_resource(r, start, end); BHNDB_UNLOCK(sc); return (error); } /** * Initialize child resource @p r with a virtual address, tag, and handle * copied from @p parent, adjusted to contain only the range defined by * @p offsize and @p size. * * @param r The register to be initialized. * @param parent The parent bus resource that fully contains the subregion. * @param offset The subregion offset within @p parent. * @param size The subregion size. * @p r. */ static int bhndb_init_child_resource(struct resource *r, struct resource *parent, bhnd_size_t offset, bhnd_size_t size) { bus_space_handle_t bh, child_bh; bus_space_tag_t bt; uintptr_t vaddr; int error; /* Fetch the parent resource's real bus values */ vaddr = (uintptr_t) rman_get_virtual(parent); bt = rman_get_bustag(parent); bh = rman_get_bushandle(parent); /* Configure child resource with window-adjusted real bus values */ vaddr += offset; error = bus_space_subregion(bt, bh, offset, size, &child_bh); if (error) return (error); rman_set_virtual(r, (void *) vaddr); rman_set_bustag(r, bt); rman_set_bushandle(r, child_bh); return (0); } /** * Attempt activation of a fixed register window mapping for @p child. * * @param sc BHNDB device state. * @param region The static region definition capable of mapping @p r. * @param child A child requesting resource activation. * @param type Resource type. * @param rid Resource identifier. * @param r Resource to be activated. * * @retval 0 if @p r was activated successfully * @retval ENOENT if no fixed register window was found. * @retval non-zero if @p r could not be activated. */ static int bhndb_activate_static_region(struct bhndb_softc *sc, struct bhndb_region *region, device_t child, int type, int rid, struct resource *r) { struct resource *bridge_res; const struct bhndb_regwin *win; bhnd_size_t parent_offset; rman_res_t r_start, r_size; int error; win = region->static_regwin; KASSERT(win != NULL && BHNDB_REGWIN_T_IS_STATIC(win->win_type), ("can't activate non-static region")); r_start = rman_get_start(r); r_size = rman_get_size(r); /* Find the corresponding bridge resource */ bridge_res = bhndb_find_regwin_resource(sc->bus_res, win); if (bridge_res == NULL) return (ENXIO); /* Calculate subregion offset within the parent resource */ parent_offset = r_start - region->addr; parent_offset += win->win_offset; /* Configure resource with its real bus values. */ error = bhndb_init_child_resource(r, bridge_res, parent_offset, r_size); if (error) return (error); /* Mark active */ if ((error = rman_activate_resource(r))) return (error); return (0); } /** * Attempt to allocate/retain a dynamic register window for @p r, returning * the retained window. * * @param sc The bhndb driver state. * @param r The resource for which a window will be retained. */ static struct bhndb_dw_alloc * bhndb_retain_dynamic_window(struct bhndb_softc *sc, struct resource *r) { struct bhndb_dw_alloc *dwa; rman_res_t r_start, r_size; int error; BHNDB_LOCK_ASSERT(sc, MA_OWNED); r_start = rman_get_start(r); r_size = rman_get_size(r); /* Look for an existing dynamic window we can reference */ dwa = bhndb_dw_find_mapping(sc->bus_res, r_start, r_size); if (dwa != NULL) { if (bhndb_dw_retain(sc->bus_res, dwa, r) == 0) return (dwa); return (NULL); } /* Otherwise, try to reserve a free window */ dwa = bhndb_dw_next_free(sc->bus_res); if (dwa == NULL) { /* No free windows */ return (NULL); } /* Window must be large enough to map the entire resource */ if (dwa->win->win_size < rman_get_size(r)) return (NULL); /* Set the window target */ error = bhndb_dw_set_addr(sc->dev, sc->bus_res, dwa, rman_get_start(r), rman_get_size(r)); if (error) { device_printf(sc->dev, "dynamic window initialization " "for 0x%llx-0x%llx failed: %d\n", (unsigned long long) r_start, (unsigned long long) r_start + r_size - 1, error); return (NULL); } /* Add our reservation */ if (bhndb_dw_retain(sc->bus_res, dwa, r)) return (NULL); return (dwa); } /** * Activate a resource using any viable static or dynamic register window. * * @param sc The bhndb driver state. * @param child The child holding ownership of @p r. * @param type The type of the resource to be activated. * @param rid The resource ID of @p r. * @param r The resource to be activated * @param[out] indirect On error and if not NULL, will be set to 'true' if * the caller should instead use an indirect resource mapping. * * @retval 0 success * @retval non-zero activation failed. */ static int bhndb_try_activate_resource(struct bhndb_softc *sc, device_t child, int type, int rid, struct resource *r, bool *indirect) { struct bhndb_region *region; struct bhndb_dw_alloc *dwa; bhndb_priority_t dw_priority; rman_res_t r_start, r_size; rman_res_t parent_offset; int error; BHNDB_LOCK_ASSERT(sc, MA_NOTOWNED); // TODO - IRQs if (type != SYS_RES_MEMORY) return (ENXIO); if (indirect) *indirect = false; r_start = rman_get_start(r); r_size = rman_get_size(r); /* Activate native addrspace resources using the host address space */ if (bhndb_get_addrspace(sc, child) == BHNDB_ADDRSPACE_NATIVE) { struct resource *parent; /* Find the bridge resource referenced by the child */ parent = bhndb_find_resource_range(sc->bus_res, r_start, r_size); if (parent == NULL) { device_printf(sc->dev, "host resource not found " "for 0x%llx-0x%llx\n", (unsigned long long) r_start, (unsigned long long) r_start + r_size - 1); return (ENOENT); } /* Initialize child resource with the real bus values */ error = bhndb_init_child_resource(r, parent, r_start - rman_get_start(parent), r_size); if (error) return (error); /* Try to activate child resource */ return (rman_activate_resource(r)); } /* Default to low priority */ dw_priority = BHNDB_PRIORITY_LOW; /* Look for a bus region matching the resource's address range */ region = bhndb_find_resource_region(sc->bus_res, r_start, r_size); if (region != NULL) dw_priority = region->priority; /* Prefer static mappings over consuming a dynamic windows. */ if (region && region->static_regwin) { error = bhndb_activate_static_region(sc, region, child, type, rid, r); if (error) device_printf(sc->dev, "static window allocation " "for 0x%llx-0x%llx failed\n", (unsigned long long) r_start, (unsigned long long) r_start + r_size - 1); return (error); } /* A dynamic window will be required; is this resource high enough * priority to be reserved a dynamic window? */ if (dw_priority < sc->bus_res->min_prio) { if (indirect) *indirect = true; return (ENOMEM); } /* Find and retain a usable window */ BHNDB_LOCK(sc); { dwa = bhndb_retain_dynamic_window(sc, r); } BHNDB_UNLOCK(sc); if (dwa == NULL) { if (indirect) *indirect = true; return (ENOMEM); } /* Configure resource with its real bus values. */ parent_offset = dwa->win->win_offset; parent_offset += r_start - dwa->target; error = bhndb_init_child_resource(r, dwa->parent_res, parent_offset, dwa->win->win_size); if (error) goto failed; /* Mark active */ if ((error = rman_activate_resource(r))) goto failed; return (0); failed: /* Release our region allocation. */ BHNDB_LOCK(sc); bhndb_dw_release(sc->bus_res, dwa, r); BHNDB_UNLOCK(sc); return (error); } /** * Default bhndb(4) implementation of BUS_ACTIVATE_RESOURCE(). * * Maps resource activation requests to a viable static or dynamic * register window, if any. */ static int bhndb_activate_resource(device_t dev, device_t child, int type, int rid, struct resource *r) { struct bhndb_softc *sc = device_get_softc(dev); return (bhndb_try_activate_resource(sc, child, type, rid, r, NULL)); } /** * Default bhndb(4) implementation of BUS_DEACTIVATE_RESOURCE(). */ static int bhndb_deactivate_resource(device_t dev, device_t child, int type, int rid, struct resource *r) { struct bhndb_dw_alloc *dwa; struct bhndb_softc *sc; struct rman *rm; int error; sc = device_get_softc(dev); if ((rm = bhndb_get_rman(sc, child, type)) == NULL) return (EINVAL); /* Mark inactive */ if ((error = rman_deactivate_resource(r))) return (error); /* Free any dynamic window allocation. */ if (bhndb_get_addrspace(sc, child) == BHNDB_ADDRSPACE_BRIDGED) { BHNDB_LOCK(sc); dwa = bhndb_dw_find_resource(sc->bus_res, r); if (dwa != NULL) bhndb_dw_release(sc->bus_res, dwa, r); BHNDB_UNLOCK(sc); } return (0); } /** * Default bhndb(4) implementation of BUS_GET_RESOURCE_LIST(). */ static struct resource_list * bhndb_get_resource_list(device_t dev, device_t child) { struct bhndb_devinfo *dinfo = device_get_ivars(child); return (&dinfo->resources); } /** * Default bhndb(4) implementation of BHND_BUS_ACTIVATE_RESOURCE(). * * For BHNDB_ADDRSPACE_NATIVE children, all resources may be assumed to * be activated by the bridge. * * For BHNDB_ADDRSPACE_BRIDGED children, attempts to activate a static register * window, a dynamic register window, or configures @p r as an indirect * resource -- in that order. */ static int bhndb_activate_bhnd_resource(device_t dev, device_t child, int type, int rid, struct bhnd_resource *r) { struct bhndb_softc *sc; struct bhndb_region *region; rman_res_t r_start, r_size; int error; bool indirect; KASSERT(!r->direct, ("direct flag set on inactive resource")); KASSERT(!(rman_get_flags(r->res) & RF_ACTIVE), ("RF_ACTIVE set on inactive resource")); sc = device_get_softc(dev); r_start = rman_get_start(r->res); r_size = rman_get_size(r->res); /* Verify bridged address range's resource priority, and skip direct * allocation if the priority is too low. */ if (bhndb_get_addrspace(sc, child) == BHNDB_ADDRSPACE_BRIDGED) { bhndb_priority_t r_prio; region = bhndb_find_resource_region(sc->bus_res, r_start, r_size); if (region != NULL) r_prio = region->priority; else r_prio = BHNDB_PRIORITY_NONE; /* If less than the minimum dynamic window priority, this * resource should always be indirect. */ if (r_prio < sc->bus_res->min_prio) return (0); } /* Attempt direct activation */ error = bhndb_try_activate_resource(sc, child, type, rid, r->res, &indirect); if (!error) { r->direct = true; } else if (indirect) { /* The request was valid, but no viable register window is * available; indirection must be employed. */ error = 0; r->direct = false; } if (BHNDB_DEBUG(PRIO) && bhndb_get_addrspace(sc, child) == BHNDB_ADDRSPACE_BRIDGED) { device_printf(child, "activated 0x%llx-0x%llx as %s " "resource\n", (unsigned long long) r_start, (unsigned long long) r_start + r_size - 1, r->direct ? "direct" : "indirect"); } return (error); }; /** * Default bhndb(4) implementation of BHND_BUS_DEACTIVATE_RESOURCE(). */ static int bhndb_deactivate_bhnd_resource(device_t dev, device_t child, int type, int rid, struct bhnd_resource *r) { int error; /* Indirect resources don't require activation */ if (!r->direct) return (0); KASSERT(rman_get_flags(r->res) & RF_ACTIVE, ("RF_ACTIVE not set on direct resource")); /* Perform deactivation */ error = bus_deactivate_resource(child, type, rid, r->res); if (!error) r->direct = false; return (error); }; /** * Slow path for bhndb_io_resource(). * * Iterates over the existing allocated dynamic windows looking for a viable * in-use region; the first matching region is returned. */ static struct bhndb_dw_alloc * bhndb_io_resource_slow(struct bhndb_softc *sc, bus_addr_t addr, bus_size_t size, bus_size_t *offset) { struct bhndb_resources *br; struct bhndb_dw_alloc *dwa; BHNDB_LOCK_ASSERT(sc, MA_OWNED); br = sc->bus_res; /* Search for an existing dynamic mapping of this address range. * Static regions are not searched, as a statically mapped * region would never be allocated as an indirect resource. */ for (size_t i = 0; i < br->dwa_count; i++) { const struct bhndb_regwin *win; dwa = &br->dw_alloc[i]; win = dwa->win; KASSERT(win->win_type == BHNDB_REGWIN_T_DYN, ("invalid register window type")); /* Verify the range */ if (addr < dwa->target) continue; if (addr + size > dwa->target + win->win_size) continue; /* Found */ *offset = dwa->win->win_offset; *offset += addr - dwa->target; return (dwa); } /* not found */ return (NULL); } /** - * 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. * @param size The size of the I/O operation to be performed at @p addr. * @param[out] offset The offset within the returned resource at which * to perform the I/O request. */ 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) { struct bhndb_resources *br; struct bhndb_dw_alloc *dwa; int error; BHNDB_LOCK_ASSERT(sc, MA_OWNED); br = sc->bus_res; /* Try to fetch a free window */ dwa = bhndb_dw_next_free(br); /* * If no dynamic windows are available, look for an existing * region that maps the target range. * * If none are found, this is a child driver bug -- our window * over-commit should only fail in the case where a child driver leaks * resources, or perform operations out-of-order. * * Broadcom HND chipsets are designed to not require register window * swapping during execution; as long as the child devices are * attached/detached correctly, using the hardware's required order * of operations, there should always be a window available for the * current operation. */ if (dwa == NULL) { dwa = bhndb_io_resource_slow(sc, addr, size, offset); if (dwa == NULL) { panic("register windows exhausted attempting to map " "0x%llx-0x%llx\n", (unsigned long long) addr, (unsigned long long) addr+size-1); } return (dwa); } /* Adjust the window if the I/O request won't fit in the current * target range. */ if (addr < dwa->target || addr > dwa->target + dwa->win->win_size || (dwa->target + dwa->win->win_size) - addr < size) { error = bhndb_dw_set_addr(sc->dev, sc->bus_res, dwa, addr, size); if (error) { panic("failed to set register window target mapping " "0x%llx-0x%llx\n", (unsigned long long) addr, (unsigned long long) addr+size-1); } } /* Calculate the offset and return */ *offset = (addr - dwa->target) + dwa->win->win_offset; return (dwa); } /* * BHND_BUS_(READ|WRITE_* implementations */ /* bhndb_bus_(read|write) common implementation */ #define BHNDB_IO_COMMON_SETUP(_io_size) \ struct bhndb_softc *sc; \ struct bhndb_dw_alloc *dwa; \ struct resource *io_res; \ bus_size_t io_offset; \ \ sc = device_get_softc(dev); \ \ BHNDB_LOCK(sc); \ dwa = bhndb_io_resource(sc, rman_get_start(r->res) + \ offset, _io_size, &io_offset); \ io_res = dwa->parent_res; \ \ KASSERT(!r->direct, \ ("bhnd_bus slow path used for direct resource")); \ \ KASSERT(rman_get_flags(io_res) & RF_ACTIVE, \ ("i/o resource is not active")); #define BHNDB_IO_COMMON_TEARDOWN() \ BHNDB_UNLOCK(sc); /* Defines a bhndb_bus_read_* method implementation */ #define BHNDB_IO_READ(_type, _name) \ static _type \ bhndb_bus_read_ ## _name (device_t dev, device_t child, \ struct bhnd_resource *r, bus_size_t offset) \ { \ _type v; \ BHNDB_IO_COMMON_SETUP(sizeof(_type)); \ v = bus_read_ ## _name (io_res, io_offset); \ BHNDB_IO_COMMON_TEARDOWN(); \ \ return (v); \ } /* Defines a bhndb_bus_write_* method implementation */ #define BHNDB_IO_WRITE(_type, _name) \ static void \ bhndb_bus_write_ ## _name (device_t dev, device_t child, \ struct bhnd_resource *r, bus_size_t offset, _type value) \ { \ BHNDB_IO_COMMON_SETUP(sizeof(_type)); \ bus_write_ ## _name (io_res, io_offset, value); \ BHNDB_IO_COMMON_TEARDOWN(); \ } /* Defines a bhndb_bus_(read|write|set)_(multi|region)_* method */ #define BHNDB_IO_MISC(_type, _ptr, _op, _size) \ static void \ bhndb_bus_ ## _op ## _ ## _size (device_t dev, \ device_t child, struct bhnd_resource *r, bus_size_t offset, \ _type _ptr datap, bus_size_t count) \ { \ BHNDB_IO_COMMON_SETUP(sizeof(_type) * count); \ bus_ ## _op ## _ ## _size (io_res, io_offset, \ datap, count); \ BHNDB_IO_COMMON_TEARDOWN(); \ } /* Defines a complete set of read/write methods */ #define BHNDB_IO_METHODS(_type, _size) \ BHNDB_IO_READ(_type, _size) \ BHNDB_IO_WRITE(_type, _size) \ \ BHNDB_IO_READ(_type, stream_ ## _size) \ BHNDB_IO_WRITE(_type, stream_ ## _size) \ \ BHNDB_IO_MISC(_type, *, read_multi, _size) \ BHNDB_IO_MISC(_type, *, write_multi, _size) \ \ BHNDB_IO_MISC(_type, *, read_multi_stream, _size) \ BHNDB_IO_MISC(_type, *, write_multi_stream, _size) \ \ BHNDB_IO_MISC(_type, , set_multi, _size) \ BHNDB_IO_MISC(_type, , set_region, _size) \ BHNDB_IO_MISC(_type, *, read_region, _size) \ BHNDB_IO_MISC(_type, *, write_region, _size) \ \ BHNDB_IO_MISC(_type, *, read_region_stream, _size) \ BHNDB_IO_MISC(_type, *, write_region_stream, _size) BHNDB_IO_METHODS(uint8_t, 1); BHNDB_IO_METHODS(uint16_t, 2); BHNDB_IO_METHODS(uint32_t, 4); /** * Default bhndb(4) implementation of BHND_BUS_BARRIER(). */ static void bhndb_bus_barrier(device_t dev, device_t child, struct bhnd_resource *r, bus_size_t offset, bus_size_t length, int flags) { BHNDB_IO_COMMON_SETUP(length); bus_barrier(io_res, io_offset + offset, length, flags); BHNDB_IO_COMMON_TEARDOWN(); } /** * Default bhndb(4) implementation of BUS_SETUP_INTR(). */ static int bhndb_setup_intr(device_t dev, device_t child, struct resource *r, int flags, driver_filter_t filter, driver_intr_t handler, void *arg, void **cookiep) { // TODO return (EOPNOTSUPP); } /** * Default bhndb(4) implementation of BUS_TEARDOWN_INTR(). */ static int bhndb_teardown_intr(device_t dev, device_t child, struct resource *r, void *cookie) { // TODO return (EOPNOTSUPP); } /** * Default bhndb(4) implementation of BUS_CONFIG_INTR(). */ static int bhndb_config_intr(device_t dev, int irq, enum intr_trigger trig, enum intr_polarity pol) { // TODO return (EOPNOTSUPP); } /** * Default bhndb(4) implementation of BUS_BIND_INTR(). */ static int bhndb_bind_intr(device_t dev, device_t child, struct resource *r, int cpu) { // TODO return (EOPNOTSUPP); } /** * Default bhndb(4) implementation of BUS_DESCRIBE_INTR(). */ static int bhndb_describe_intr(device_t dev, device_t child, struct resource *irq, void *cookie, const char *descr) { // TODO return (EOPNOTSUPP); } /** * Default bhndb(4) implementation of BUS_GET_DMA_TAG(). */ static bus_dma_tag_t bhndb_get_dma_tag(device_t dev, device_t child) { // TODO return (NULL); } static device_method_t bhndb_methods[] = { /* Device interface */ \ DEVMETHOD(device_probe, bhndb_generic_probe), DEVMETHOD(device_detach, bhndb_generic_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bhndb_generic_suspend), DEVMETHOD(device_resume, bhndb_generic_resume), /* Bus interface */ DEVMETHOD(bus_probe_nomatch, bhndb_probe_nomatch), DEVMETHOD(bus_print_child, bhndb_print_child), DEVMETHOD(bus_child_pnpinfo_str, bhndb_child_pnpinfo_str), DEVMETHOD(bus_child_location_str, bhndb_child_location_str), DEVMETHOD(bus_add_child, bhndb_add_child), DEVMETHOD(bus_child_deleted, bhndb_child_deleted), DEVMETHOD(bus_alloc_resource, bhndb_alloc_resource), DEVMETHOD(bus_release_resource, bhndb_release_resource), DEVMETHOD(bus_activate_resource, bhndb_activate_resource), DEVMETHOD(bus_deactivate_resource, bhndb_deactivate_resource), DEVMETHOD(bus_setup_intr, bhndb_setup_intr), DEVMETHOD(bus_teardown_intr, bhndb_teardown_intr), DEVMETHOD(bus_config_intr, bhndb_config_intr), DEVMETHOD(bus_bind_intr, bhndb_bind_intr), DEVMETHOD(bus_describe_intr, bhndb_describe_intr), DEVMETHOD(bus_get_dma_tag, bhndb_get_dma_tag), DEVMETHOD(bus_adjust_resource, bhndb_adjust_resource), DEVMETHOD(bus_set_resource, bus_generic_rl_set_resource), DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource), DEVMETHOD(bus_delete_resource, bus_generic_rl_delete_resource), DEVMETHOD(bus_get_resource_list, bhndb_get_resource_list), DEVMETHOD(bus_read_ivar, bhndb_read_ivar), DEVMETHOD(bus_write_ivar, bhndb_write_ivar), /* 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), /* BHND interface */ DEVMETHOD(bhnd_bus_is_hw_disabled, bhndb_is_hw_disabled), DEVMETHOD(bhnd_bus_get_chipid, bhndb_get_chipid), DEVMETHOD(bhnd_bus_activate_resource, bhndb_activate_bhnd_resource), DEVMETHOD(bhnd_bus_deactivate_resource, bhndb_deactivate_bhnd_resource), DEVMETHOD(bhnd_bus_get_nvram_var, bhnd_bus_generic_get_nvram_var), DEVMETHOD(bhnd_bus_read_1, bhndb_bus_read_1), DEVMETHOD(bhnd_bus_read_2, bhndb_bus_read_2), DEVMETHOD(bhnd_bus_read_4, bhndb_bus_read_4), DEVMETHOD(bhnd_bus_write_1, bhndb_bus_write_1), DEVMETHOD(bhnd_bus_write_2, bhndb_bus_write_2), DEVMETHOD(bhnd_bus_write_4, bhndb_bus_write_4), DEVMETHOD(bhnd_bus_read_stream_1, bhndb_bus_read_stream_1), DEVMETHOD(bhnd_bus_read_stream_2, bhndb_bus_read_stream_2), DEVMETHOD(bhnd_bus_read_stream_4, bhndb_bus_read_stream_4), DEVMETHOD(bhnd_bus_write_stream_1, bhndb_bus_write_stream_1), DEVMETHOD(bhnd_bus_write_stream_2, bhndb_bus_write_stream_2), DEVMETHOD(bhnd_bus_write_stream_4, bhndb_bus_write_stream_4), DEVMETHOD(bhnd_bus_read_multi_1, bhndb_bus_read_multi_1), DEVMETHOD(bhnd_bus_read_multi_2, bhndb_bus_read_multi_2), DEVMETHOD(bhnd_bus_read_multi_4, bhndb_bus_read_multi_4), DEVMETHOD(bhnd_bus_write_multi_1, bhndb_bus_write_multi_1), DEVMETHOD(bhnd_bus_write_multi_2, bhndb_bus_write_multi_2), DEVMETHOD(bhnd_bus_write_multi_4, bhndb_bus_write_multi_4), DEVMETHOD(bhnd_bus_read_multi_stream_1, bhndb_bus_read_multi_stream_1), DEVMETHOD(bhnd_bus_read_multi_stream_2, bhndb_bus_read_multi_stream_2), DEVMETHOD(bhnd_bus_read_multi_stream_4, bhndb_bus_read_multi_stream_4), DEVMETHOD(bhnd_bus_write_multi_stream_1,bhndb_bus_write_multi_stream_1), DEVMETHOD(bhnd_bus_write_multi_stream_2,bhndb_bus_write_multi_stream_2), DEVMETHOD(bhnd_bus_write_multi_stream_4,bhndb_bus_write_multi_stream_4), DEVMETHOD(bhnd_bus_set_multi_1, bhndb_bus_set_multi_1), DEVMETHOD(bhnd_bus_set_multi_2, bhndb_bus_set_multi_2), DEVMETHOD(bhnd_bus_set_multi_4, bhndb_bus_set_multi_4), DEVMETHOD(bhnd_bus_set_region_1, bhndb_bus_set_region_1), DEVMETHOD(bhnd_bus_set_region_2, bhndb_bus_set_region_2), DEVMETHOD(bhnd_bus_set_region_4, bhndb_bus_set_region_4), DEVMETHOD(bhnd_bus_read_region_1, bhndb_bus_read_region_1), DEVMETHOD(bhnd_bus_read_region_2, bhndb_bus_read_region_2), DEVMETHOD(bhnd_bus_read_region_4, bhndb_bus_read_region_4), DEVMETHOD(bhnd_bus_write_region_1, bhndb_bus_write_region_1), DEVMETHOD(bhnd_bus_write_region_2, bhndb_bus_write_region_2), DEVMETHOD(bhnd_bus_write_region_4, bhndb_bus_write_region_4), DEVMETHOD(bhnd_bus_read_region_stream_1,bhndb_bus_read_region_stream_1), DEVMETHOD(bhnd_bus_read_region_stream_2,bhndb_bus_read_region_stream_2), DEVMETHOD(bhnd_bus_read_region_stream_4,bhndb_bus_read_region_stream_4), DEVMETHOD(bhnd_bus_write_region_stream_1,bhndb_bus_write_region_stream_1), DEVMETHOD(bhnd_bus_write_region_stream_2,bhndb_bus_write_region_stream_2), DEVMETHOD(bhnd_bus_write_region_stream_4,bhndb_bus_write_region_stream_4), DEVMETHOD(bhnd_bus_barrier, bhndb_bus_barrier), DEVMETHOD_END }; devclass_t bhndb_devclass; DEFINE_CLASS_0(bhndb, bhndb_driver, bhndb_methods, sizeof(struct bhndb_softc)); MODULE_VERSION(bhndb, 1); MODULE_DEPEND(bhndb, bhnd, 1, 1, 1); Index: head/sys/dev/bhnd/bhndb/bhndb_bus_if.m =================================================================== --- head/sys/dev/bhnd/bhndb/bhndb_bus_if.m (revision 305370) +++ head/sys/dev/bhnd/bhndb/bhndb_bus_if.m (revision 305371) @@ -1,127 +1,145 @@ #- -# 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 # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, # INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE # USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # $FreeBSD$ #include #include # # Parent bus interface required by attached bhndb bridge devices. # INTERFACE bhndb_bus; HEADER { struct bhnd_core_info; struct bhndb_hwcfg; struct bhndb_hw; }; CODE { #include static const struct bhnd_chipid * bhndb_null_get_chipid(device_t dev, device_t child) { return (NULL); } static const struct bhndb_hwcfg * bhndb_null_get_generic_hwcfg(device_t dev, device_t child) { panic("bhndb_get_generic_hwcfg unimplemented"); } static const struct bhndb_hw * bhndb_null_get_hardware_table(device_t dev, device_t child) { 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, struct bhnd_core_info *core) { return (true); } } /** * Return a generic hardware configuration to be used by * the bhndb bridge device to enumerate attached devices. * * @param dev The parent device. * @param child The attached bhndb device. * * @retval bhndb_hwcfg The configuration to use for bus enumeration. */ METHOD const struct bhndb_hwcfg * get_generic_hwcfg { device_t dev; device_t child; } DEFAULT bhndb_null_get_generic_hwcfg; /** * Provide chip identification information to be used by a @p child during * device enumeration. * * May return NULL if the device includes a ChipCommon core. * * @param dev The parent device. * @param child The attached bhndb device. */ METHOD const struct bhnd_chipid * get_chipid { device_t dev; device_t child; } DEFAULT bhndb_null_get_chipid; /** * Return the hardware specification table to be used when identifying the * bridge's full hardware configuration. * * @param dev The parent device. * @param child The attached bhndb device. */ METHOD const struct bhndb_hw * get_hardware_table { device_t dev; device_t child; } 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. * * In some cases, the core's pins may be left floating, or the hardware * may otherwise be non-functional; this method allows the parent device * to explicitly specify whether @p core should be disabled. * * @param dev The parent device. * @param child The attached bhndb device. * @param core A core discovered on @p child. */ METHOD bool is_core_disabled { device_t dev; device_t child; struct bhnd_core_info *core; } DEFAULT bhndb_null_is_core_disabled; Index: head/sys/dev/bhnd/bhndb/bhndb_if.m =================================================================== --- head/sys/dev/bhnd/bhndb/bhndb_if.m (revision 305370) +++ head/sys/dev/bhnd/bhndb/bhndb_if.m (revision 305371) @@ -1,223 +1,201 @@ #- # Copyright (c) 2015 Landon Fuller # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, # INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE # USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # $FreeBSD$ #include #include #include #include #include #include # # bhndb bridge device interface. # INTERFACE bhndb; HEADER { struct bhndb_regwin; struct bhndb_hw; struct bhndb_hw_priority; } CODE { #include #include static const struct bhnd_chipid * bhndb_null_get_chipid(device_t dev, device_t child) { panic("bhndb_get_chipid unimplemented"); } static int bhndb_null_populate_board_info(device_t dev, device_t child, struct bhnd_board_info *info) { 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) + bhndb_null_get_hostb_core(device_t dev, device_t child, + struct bhnd_core_info *core) { - panic("bhndb_init_full_config unimplemented"); + panic("bhndb_get_hostb_core unimplemented"); } - static device_t - bhndb_null_find_hostb_device(device_t dev, device_t child) - { - panic("bhndb_find_hostb_device unimplemented"); - } - static void bhndb_null_suspend_resource(device_t dev, device_t child, int type, struct resource *r) { panic("bhndb_suspend_resource unimplemented"); } static int bhndb_null_resume_resource(device_t dev, device_t child, int type, struct resource *r) { panic("bhndb_resume_resource unimplemented"); } static int bhndb_null_set_window_addr(device_t dev, const struct bhndb_regwin *rw, bhnd_addr_t addr) { panic("bhndb_set_window_addr unimplemented"); } } /** * Return the chip identification information for @p child. * * @param dev The parent device of @p child. * @param child The bhndb-attached device. */ METHOD const struct bhnd_chipid * get_chipid { device_t dev; device_t child; } DEFAULT bhndb_null_get_chipid; /** * Populate @p info with board info known only to the bridge, * deferring to any existing initialized fields in @p info. * * @param dev The parent device of @p child. * @param child The bhndb-attached device. * @param[in,out] info A board info structure previously initialized with any * information available from NVRAM. */ METHOD int populate_board_info { device_t dev; device_t child; struct bhnd_board_info *info; } 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. + * @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. * - * 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 hw_priority The hardware priority table to be used when determining - * the bridge resource allocation strategy. + * @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 int init_full_config { +METHOD int get_hostb_core { 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. - */ -METHOD device_t find_hostb_device { - 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 * further use of the resource will be made until BHNDB_RESUME_RESOURCE() * is called. * * Bridge resources consumed by the reference may be released; these will * be reacquired if BHNDB_RESUME_RESOURCE() completes successfully. * * Requests to suspend a suspended resource will be ignored. * * @param dev The bridge device. * @param child The child device requesting resource suspension. This does * not need to be the owner of @p r. * @param type The resource type. * @param r The resource to be suspended. */ METHOD void suspend_resource { device_t dev; device_t child; int type; struct resource *r; } DEFAULT bhndb_null_suspend_resource; /** * Attempt to re-enable a resource previously suspended by * BHNDB_SUSPEND_RESOURCE(). * * Bridge resources required by the reference may not be available, in which * case an error will be returned and the resource mapped by @p r must not be * used in any capacity. * * Requests to resume a non-suspended resource will be ignored. * * @param dev The bridge device. * @param child The child device requesting resource suspension. This does * not need to be the owner of @p r. * @param type The resource type. * @param r The resource to be suspended. */ METHOD int resume_resource { device_t dev; device_t child; int type; struct resource *r; } DEFAULT bhndb_null_resume_resource; /** * Set a given register window's base address. * * @param dev The bridge device. * @param win The register window. * @param addr The address to be configured for @p win. * * @retval 0 success * @retval ENODEV The provided @p win is not memory-mapped on the bus or does * not support setting a base address. * @retval non-zero failure */ METHOD int set_window_addr { device_t dev; const struct bhndb_regwin *win; bhnd_addr_t addr; } DEFAULT bhndb_null_set_window_addr; Index: head/sys/dev/bhnd/bhndb/bhndb_pci.c =================================================================== --- head/sys/dev/bhnd/bhndb/bhndb_pci.c (revision 305370) +++ head/sys/dev/bhnd/bhndb/bhndb_pci.c (revision 305371) @@ -1,695 +1,709 @@ /*- * Copyright (c) 2015-2016 Landon Fuller * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. */ #include __FBSDID("$FreeBSD$"); /* * PCI-specific implementation for the BHNDB bridge driver. * * Provides support for bridging from a PCI parent bus to a BHND-compatible * bus (e.g. bcma or siba) via a Broadcom PCI core configured in end-point * mode. * * This driver handles all initial generic host-level PCI interactions with a * PCI/PCIe bridge core operating in endpoint mode. Once the bridged bhnd(4) * bus has been enumerated, this driver works in tandem with a core-specific * bhnd_pci_hostb driver to manage the PCI core. */ #include #include #include #include #include #include #include #include #include #include #include #include "bhndb_pcireg.h" #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); static int bhndb_pci_compat_setregwin(struct bhndb_pci_softc *, const struct bhndb_regwin *, bhnd_addr_t); static int bhndb_pci_fast_setregwin(struct bhndb_pci_softc *, const struct bhndb_regwin *, bhnd_addr_t); static void bhndb_init_sromless_pci_config( struct bhndb_pci_softc *sc); static bus_addr_t bhndb_pci_sprom_addr(struct bhndb_pci_softc *sc); static bus_size_t bhndb_pci_sprom_size(struct bhndb_pci_softc *sc); /** * Default bhndb_pci implementation of device_probe(). * * Verifies that the parent is a PCI/PCIe device. */ static int bhndb_pci_probe(device_t dev) { device_t parent; devclass_t parent_bus; devclass_t pci; /* Our parent must be a PCI/PCIe device. */ pci = devclass_find("pci"); parent = device_get_parent(dev); parent_bus = device_get_devclass(device_get_parent(parent)); if (parent_bus != pci) return (ENXIO); device_set_desc(dev, "PCI-BHND bridge"); return (BUS_PROBE_DEFAULT); } static int bhndb_pci_attach(device_t dev) { struct bhndb_pci_softc *sc; int error, reg; 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); + goto cleanup; - /* Use siba(4)-compatible regwin handling until we know - * what kind of bus is attached */ - sc->set_regwin = bhndb_pci_compat_setregwin; - - /* 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); - /* If SPROM is mapped directly into BAR0, add NVRAM device. */ + /* 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); +} + +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); } static const struct bhndb_regwin * bhndb_pci_sprom_regwin(struct bhndb_pci_softc *sc) { struct bhndb_resources *bres; const struct bhndb_hwcfg *cfg; const struct bhndb_regwin *sprom_win; bres = sc->bhndb.bus_res; cfg = bres->cfg; sprom_win = bhndb_regwin_find_type(cfg->register_windows, BHNDB_REGWIN_T_SPROM, BHNDB_PCI_V0_BAR0_SPROM_SIZE); return (sprom_win); } static bus_addr_t bhndb_pci_sprom_addr(struct bhndb_pci_softc *sc) { const struct bhndb_regwin *sprom_win; struct resource *r; /* Fetch the SPROM register window */ sprom_win = bhndb_pci_sprom_regwin(sc); KASSERT(sprom_win != NULL, ("requested sprom address on PCI_V2+")); /* Fetch the associated resource */ r = bhndb_find_regwin_resource(sc->bhndb.bus_res, sprom_win); KASSERT(r != NULL, ("missing resource for sprom window\n")); return (rman_get_start(r) + sprom_win->win_offset); } static bus_size_t bhndb_pci_sprom_size(struct bhndb_pci_softc *sc) { const struct bhndb_regwin *sprom_win; uint32_t sctl; bus_size_t sprom_sz; sprom_win = bhndb_pci_sprom_regwin(sc); /* PCI_V2 and later devices map SPROM/OTP via ChipCommon */ if (sprom_win == NULL) return (0); /* Determine SPROM size */ sctl = pci_read_config(sc->parent, BHNDB_PCI_SPROM_CONTROL, 4); if (sctl & BHNDB_PCI_SPROM_BLANK) return (0); switch (sctl & BHNDB_PCI_SPROM_SZ_MASK) { case BHNDB_PCI_SPROM_SZ_1KB: sprom_sz = (1 * 1024); break; case BHNDB_PCI_SPROM_SZ_4KB: sprom_sz = (4 * 1024); break; case BHNDB_PCI_SPROM_SZ_16KB: sprom_sz = (16 * 1024); break; case BHNDB_PCI_SPROM_SZ_RESERVED: default: device_printf(sc->dev, "invalid PCI sprom size 0x%x\n", sctl); return (0); } if (sprom_sz > sprom_win->win_size) { device_printf(sc->dev, "PCI sprom size (0x%x) overruns defined register window\n", sctl); return (0); } return (sprom_sz); } /* * On devices without a SROM, the PCI(e) cores will be initialized with * their Power-on-Reset defaults; this can leave two of the BAR0 PCI windows * mapped to the wrong core. * * This function updates the SROM shadow to point the BAR0 windows at the * current PCI core. * * Applies to all PCI/PCIe revisions. */ static void bhndb_init_sromless_pci_config(struct bhndb_pci_softc *sc) { struct bhndb_resources *bres; const struct bhndb_hwcfg *cfg; const struct bhndb_regwin *win; + struct bhnd_core_info hostb_core; struct resource *core_regs; bus_size_t srom_offset; 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; + } - switch (bhnd_get_device(sc->bhndb.hostb_dev)) { + if (hostb_core.vendor != BHND_MFGID_BCM) + return; + + switch (hostb_core.device) { case BHND_COREID_PCI: srom_offset = BHND_PCI_SRSH_PI_OFFSET; break; case BHND_COREID_PCIE: srom_offset = BHND_PCIE_SRSH_PI_OFFSET; break; default: device_printf(sc->dev, "unsupported PCI host bridge device\n"); return; } /* Locate the static register window mapping the PCI core */ win = bhndb_regwin_find_core(cfg->register_windows, sc->pci_devclass, 0, BHND_PORT_DEVICE, 0, 0); if (win == NULL) { device_printf(sc->dev, "missing PCI core register window\n"); return; } /* Fetch the resource containing the register window */ core_regs = bhndb_find_regwin_resource(bres, win); if (core_regs == NULL) { device_printf(sc->dev, "missing PCI core register resource\n"); return; } /* Fetch the SPROM's configured core index */ val = bus_read_2(core_regs, win->win_offset + srom_offset); sprom_cidx = (val & BHND_PCI_SRSH_PI_MASK) >> BHND_PCI_SRSH_PI_SHIFT; /* If it doesn't match host bridge's core index, update the index * value */ - pci_cidx = 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); bus_write_2(core_regs, win->win_offset + srom_offset, val); } } static int bhndb_pci_resume(device_t dev) { struct bhndb_pci_softc *sc; int error; sc = device_get_softc(dev); /* Enable clocks (if supported by this hardware) */ if ((error = bhndb_enable_pci_clocks(sc))) return (error); /* Perform resume */ return (bhndb_generic_resume(dev)); } static int bhndb_pci_suspend(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 suspend */ return (bhndb_generic_suspend(dev)); } 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) { struct bhndb_pci_softc *sc = device_get_softc(dev); return (sc->set_regwin(sc, rw, addr)); } /** * A siba(4) and bcma(4)-compatible bhndb_set_window_addr implementation. * * On siba(4) devices, it's possible that writing a PCI window register may * not succeed; it's necessary to immediately read the configuration register * and retry if not set to the desired value. * * This is not necessary on bcma(4) devices, but other than the overhead of * validating the register, there's no harm in performing the verification. */ static int bhndb_pci_compat_setregwin(struct bhndb_pci_softc *sc, const struct bhndb_regwin *rw, bhnd_addr_t addr) { int error; int reg; if (rw->win_type != BHNDB_REGWIN_T_DYN) return (ENODEV); reg = rw->d.dyn.cfg_offset; for (u_int i = 0; i < BHNDB_PCI_BARCTRL_WRITE_RETRY; i++) { if ((error = bhndb_pci_fast_setregwin(sc, rw, addr))) return (error); if (pci_read_config(sc->parent, reg, 4) == addr) return (0); DELAY(10); } /* Unable to set window */ return (ENODEV); } /** * A bcma(4)-only bhndb_set_window_addr implementation. */ static int bhndb_pci_fast_setregwin(struct bhndb_pci_softc *sc, const struct bhndb_regwin *rw, bhnd_addr_t addr) { /* The PCI bridge core only supports 32-bit addressing, regardless * of the bus' support for 64-bit addressing */ if (addr > UINT32_MAX) return (ERANGE); switch (rw->win_type) { case BHNDB_REGWIN_T_DYN: /* Addresses must be page aligned */ if (addr % rw->win_size != 0) return (EINVAL); pci_write_config(sc->parent, rw->d.dyn.cfg_offset, addr, 4); break; default: return (ENODEV); } return (0); } static int bhndb_pci_populate_board_info(device_t dev, device_t child, struct bhnd_board_info *info) { struct bhndb_pci_softc *sc; sc = device_get_softc(dev); /* * On a subset of Apple BCM4360 modules, always prefer the * PCI subdevice to the SPROM-supplied boardtype. * * TODO: * * Broadcom's own drivers implement this override, and then later use * the remapped BCM4360 board type to determine the required * board-specific workarounds. * * Without access to this hardware, it's unclear why this mapping * is done, and we must do the same. If we can survey the hardware * in question, it may be possible to replace this behavior with * explicit references to the SPROM-supplied boardtype(s) in our * quirk definitions. */ if (pci_get_subvendor(sc->parent) == PCI_VENDOR_APPLE) { switch (info->board_type) { case BHND_BOARD_BCM94360X29C: case BHND_BOARD_BCM94360X29CP2: case BHND_BOARD_BCM94360X51: case BHND_BOARD_BCM94360X51P2: info->board_type = 0; /* allow override below */ break; default: break; } } /* If NVRAM did not supply vendor/type info, provide the PCI * subvendor/subdevice values. */ if (info->board_vendor == 0) info->board_vendor = pci_get_subvendor(sc->parent); if (info->board_type == 0) info->board_type = pci_get_subdevice(sc->parent); return (0); } /** * Enable externally managed clocks, if required. * * Some PCI chipsets (BCM4306, possibly others) chips do not support * the idle low-power clock. Clocking must be bootstrapped at * attach/resume by directly adjusting GPIO registers exposed in the * PCI config space, and correspondingly, explicitly shutdown at * detach/suspend. * * @param sc Bridge driver state. */ static int bhndb_enable_pci_clocks(struct bhndb_pci_softc *sc) { uint32_t gpio_in, gpio_out, gpio_en; uint32_t gpio_flags; uint16_t pci_status; /* Only supported and required on PCI devices */ if (sc->pci_devclass != BHND_DEVCLASS_PCI) return (0); /* Read state of XTAL pin */ gpio_in = pci_read_config(sc->parent, BHNDB_PCI_GPIO_IN, 4); if (gpio_in & BHNDB_PCI_GPIO_XTAL_ON) return (0); /* already enabled */ /* Fetch current config */ gpio_out = pci_read_config(sc->parent, BHNDB_PCI_GPIO_OUT, 4); gpio_en = pci_read_config(sc->parent, BHNDB_PCI_GPIO_OUTEN, 4); /* Set PLL_OFF/XTAL_ON pins to HIGH and enable both pins */ gpio_flags = (BHNDB_PCI_GPIO_PLL_OFF|BHNDB_PCI_GPIO_XTAL_ON); gpio_out |= gpio_flags; gpio_en |= gpio_flags; pci_write_config(sc->parent, BHNDB_PCI_GPIO_OUT, gpio_out, 4); pci_write_config(sc->parent, BHNDB_PCI_GPIO_OUTEN, gpio_en, 4); DELAY(1000); /* Reset PLL_OFF */ gpio_out &= ~BHNDB_PCI_GPIO_PLL_OFF; pci_write_config(sc->parent, BHNDB_PCI_GPIO_OUT, gpio_out, 4); DELAY(5000); /* Clear any PCI 'sent target-abort' flag. */ pci_status = pci_read_config(sc->parent, PCIR_STATUS, 2); pci_status &= ~PCIM_STATUS_STABORT; pci_write_config(sc->parent, PCIR_STATUS, pci_status, 2); return (0); } /** * Disable externally managed clocks, if required. * * @param sc Bridge driver state. */ static int bhndb_disable_pci_clocks(struct bhndb_pci_softc *sc) { uint32_t gpio_out, gpio_en; /* Only supported and required on PCI devices */ if (sc->pci_devclass != BHND_DEVCLASS_PCI) return (0); /* Fetch current config */ gpio_out = pci_read_config(sc->parent, BHNDB_PCI_GPIO_OUT, 4); gpio_en = pci_read_config(sc->parent, BHNDB_PCI_GPIO_OUTEN, 4); /* Set PLL_OFF to HIGH, XTAL_ON to LOW. */ gpio_out &= ~BHNDB_PCI_GPIO_XTAL_ON; gpio_out |= BHNDB_PCI_GPIO_PLL_OFF; pci_write_config(sc->parent, BHNDB_PCI_GPIO_OUT, gpio_out, 4); /* Enable both output pins */ gpio_en |= (BHNDB_PCI_GPIO_PLL_OFF|BHNDB_PCI_GPIO_XTAL_ON); pci_write_config(sc->parent, BHNDB_PCI_GPIO_OUTEN, gpio_en, 4); return (0); } static bhnd_clksrc bhndb_pci_pwrctl_get_clksrc(device_t dev, device_t child, bhnd_clock clock) { struct bhndb_pci_softc *sc; uint32_t gpio_out; sc = device_get_softc(dev); /* Only supported on PCI devices */ if (sc->pci_devclass != BHND_DEVCLASS_PCI) return (ENODEV); /* Only ILP is supported */ if (clock != BHND_CLOCK_ILP) return (ENXIO); gpio_out = pci_read_config(sc->parent, BHNDB_PCI_GPIO_OUT, 4); if (gpio_out & BHNDB_PCI_GPIO_SCS) return (BHND_CLKSRC_PCI); else return (BHND_CLKSRC_XTAL); } static int bhndb_pci_pwrctl_gate_clock(device_t dev, device_t child, bhnd_clock clock) { struct bhndb_pci_softc *sc = device_get_softc(dev); /* Only supported on PCI devices */ if (sc->pci_devclass != BHND_DEVCLASS_PCI) return (ENODEV); /* Only HT is supported */ if (clock != BHND_CLOCK_HT) return (ENXIO); return (bhndb_disable_pci_clocks(sc)); } static int bhndb_pci_pwrctl_ungate_clock(device_t dev, device_t child, bhnd_clock clock) { struct bhndb_pci_softc *sc = device_get_softc(dev); /* Only supported on PCI devices */ if (sc->pci_devclass != BHND_DEVCLASS_PCI) return (ENODEV); /* Only HT is supported */ if (clock != BHND_CLOCK_HT) return (ENXIO); return (bhndb_enable_pci_clocks(sc)); } static device_method_t bhndb_pci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, bhndb_pci_probe), DEVMETHOD(device_attach, bhndb_pci_attach), DEVMETHOD(device_resume, bhndb_pci_resume), DEVMETHOD(device_suspend, bhndb_pci_suspend), DEVMETHOD(device_detach, bhndb_pci_detach), /* BHND interface */ DEVMETHOD(bhnd_bus_pwrctl_get_clksrc, bhndb_pci_pwrctl_get_clksrc), DEVMETHOD(bhnd_bus_pwrctl_gate_clock, bhndb_pci_pwrctl_gate_clock), DEVMETHOD(bhnd_bus_pwrctl_ungate_clock, bhndb_pci_pwrctl_ungate_clock), /* 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), DEVMETHOD_END }; DEFINE_CLASS_1(bhndb, bhndb_pci_driver, bhndb_pci_methods, sizeof(struct bhndb_pci_softc), bhndb_driver); MODULE_VERSION(bhndb_pci, 1); MODULE_DEPEND(bhndb_pci, bhnd_pci_hostb, 1, 1, 1); MODULE_DEPEND(bhndb_pci, pci, 1, 1, 1); MODULE_DEPEND(bhndb_pci, bhndb, 1, 1, 1); MODULE_DEPEND(bhndb_pci, bhnd, 1, 1, 1); Index: head/sys/dev/bhnd/bhndb/bhndb_private.h =================================================================== --- head/sys/dev/bhnd/bhndb/bhndb_private.h (revision 305370) +++ head/sys/dev/bhnd/bhndb/bhndb_private.h (revision 305371) @@ -1,256 +1,260 @@ /*- * Copyright (c) 2015 Landon Fuller * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. * * $FreeBSD$ */ #ifndef _BHND_BHNDB_PRIVATE_H_ #define _BHND_BHNDB_PRIVATE_H_ #include #include #include #include #include #include #include #include "bhndbvar.h" /* * Private bhndb(4) driver definitions. */ struct bhndb_dw_alloc; struct bhndb_region; struct bhndb_resources; struct resource *bhndb_find_resource_range( struct bhndb_resources *br, rman_res_t start, rman_res_t count); struct resource *bhndb_find_regwin_resource( struct bhndb_resources *br, const struct bhndb_regwin *win); struct bhndb_resources *bhndb_alloc_resources(device_t dev, 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); int bhndb_add_resource_region( struct bhndb_resources *br, bhnd_addr_t addr, bhnd_size_t size, bhndb_priority_t priority, const struct bhndb_regwin *static_regwin); int bhndb_find_resource_limits( struct bhndb_resources *br, struct resource *r, rman_res_t *start, rman_res_t *end); struct bhndb_region *bhndb_find_resource_region( struct bhndb_resources *br, bhnd_addr_t addr, bhnd_size_t size); struct bhndb_dw_alloc *bhndb_dw_find_resource( struct bhndb_resources *dr, struct resource *r); struct bhndb_dw_alloc *bhndb_dw_find_mapping( struct bhndb_resources *br, bhnd_addr_t addr, bhnd_size_t size); int bhndb_dw_retain( struct bhndb_resources *br, struct bhndb_dw_alloc *dwa, struct resource *res); void bhndb_dw_release( struct bhndb_resources *br, struct bhndb_dw_alloc *dwa, struct resource *res); int bhndb_dw_set_addr(device_t dev, struct bhndb_resources *br, struct bhndb_dw_alloc *dwa, bus_addr_t addr, bus_size_t size); size_t bhndb_regwin_count( const struct bhndb_regwin *table, bhndb_regwin_type_t type); const struct bhndb_regwin *bhndb_regwin_find_type( const struct bhndb_regwin *table, bhndb_regwin_type_t type, bus_size_t min_size); const struct bhndb_regwin *bhndb_regwin_find_core( const struct bhndb_regwin *table, bhnd_devclass_t class, int unit, bhnd_port_type port_type, u_int port, u_int region); const struct bhndb_regwin *bhndb_regwin_find_best( const struct bhndb_regwin *table, bhnd_devclass_t class, int unit, 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); /** * Dynamic register window allocation reference. */ struct bhndb_dw_rentry { struct resource *dw_res; /**< child resource */ LIST_ENTRY(bhndb_dw_rentry) dw_link; }; /** * A dynamic register window allocation record. */ struct bhndb_dw_alloc { const struct bhndb_regwin *win; /**< window definition */ struct resource *parent_res; /**< enclosing resource */ u_int rnid; /**< region identifier */ rman_res_t target; /**< the current window address, or 0x0 if unknown */ LIST_HEAD(, bhndb_dw_rentry) refs; /**< references */ }; /** * A bus address region description. */ struct bhndb_region { bhnd_addr_t addr; /**< start of mapped range */ bhnd_size_t size; /**< size of mapped range */ bhndb_priority_t priority; /**< direct resource allocation priority */ const struct bhndb_regwin *static_regwin; /**< fixed mapping regwin, if any */ STAILQ_ENTRY(bhndb_region) link; }; /** * BHNDB resource allocation state. */ struct bhndb_resources { device_t dev; /**< bridge device */ 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 */ STAILQ_HEAD(, bhndb_region) bus_regions; /**< bus region descriptors */ struct bhndb_dw_alloc *dw_alloc; /**< dynamic window allocation records */ size_t dwa_count; /**< number of dynamic windows available. */ bitstr_t *dwa_freelist; /**< dynamic window free list */ bhndb_priority_t min_prio; /**< minimum resource priority required to allocate a dynamic window */ }; /** * Returns true if the all dynamic windows are marked free, false * otherwise. * * @param br The resource state to check. */ static inline bool bhndb_dw_all_free(struct bhndb_resources *br) { int bit; bit_ffs(br->dwa_freelist, br->dwa_count, &bit); return (bit == -1); } /** * Find the next free dynamic window region in @p br. * * @param br The resource state to search. */ static inline struct bhndb_dw_alloc * bhndb_dw_next_free(struct bhndb_resources *br) { struct bhndb_dw_alloc *dw_free; int bit; bit_ffc(br->dwa_freelist, br->dwa_count, &bit); if (bit == -1) return (NULL); dw_free = &br->dw_alloc[bit]; KASSERT(LIST_EMPTY(&dw_free->refs), ("free list out of sync with refs")); return (dw_free); } /** * Returns true if a dynamic window allocation is marked as free. * * @param br The resource state owning @p dwa. * @param dwa The dynamic window allocation record to be checked. */ static inline bool bhndb_dw_is_free(struct bhndb_resources *br, struct bhndb_dw_alloc *dwa) { bool is_free = LIST_EMPTY(&dwa->refs); KASSERT(is_free == !bit_test(br->dwa_freelist, dwa->rnid), ("refs out of sync with free list")); return (is_free); } #define BHNDB_LOCK_INIT(sc) \ mtx_init(&(sc)->sc_mtx, device_get_nameunit((sc)->dev), \ "bhndb resource allocator lock", MTX_DEF) #define BHNDB_LOCK(sc) mtx_lock(&(sc)->sc_mtx) #define BHNDB_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx) #define BHNDB_LOCK_ASSERT(sc, what) mtx_assert(&(sc)->sc_mtx, what) #define BHNDB_LOCK_DESTROY(sc) mtx_destroy(&(sc)->sc_mtx) #endif /* _BHND_BHNDB_PRIVATE_H_ */ Index: head/sys/dev/bhnd/bhndb/bhndb_subr.c =================================================================== --- head/sys/dev/bhnd/bhndb/bhndb_subr.c (revision 305370) +++ head/sys/dev/bhnd/bhndb/bhndb_subr.c (revision 305371) @@ -1,1046 +1,1084 @@ /*- * Copyright (c) 2015 Landon Fuller * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include "bhndb_private.h" #include "bhndbvar.h" /** * Attach a BHND bridge device to @p parent. * * @param parent A parent PCI device. * @param[out] bhndb On success, the probed and attached bhndb bridge device. * @param unit The device unit number, or -1 to select the next available unit * number. * * @retval 0 success * @retval non-zero Failed to attach the bhndb device. */ int bhndb_attach_bridge(device_t parent, device_t *bhndb, int unit) { int error; *bhndb = device_add_child(parent, "bhndb", unit); if (*bhndb == NULL) return (ENXIO); if (!(error = device_probe_and_attach(*bhndb))) return (0); if ((device_delete_child(parent, *bhndb))) device_printf(parent, "failed to detach bhndb child\n"); return (error); } /* * Call BHNDB_SUSPEND_RESOURCE() for all resources in @p rl. */ static void bhndb_do_suspend_resources(device_t dev, struct resource_list *rl) { struct resource_list_entry *rle; /* Suspend all child resources. */ STAILQ_FOREACH(rle, rl, link) { /* Skip non-allocated resources */ if (rle->res == NULL) continue; BHNDB_SUSPEND_RESOURCE(device_get_parent(dev), dev, rle->type, rle->res); } } /** * Helper function for implementing BUS_RESUME_CHILD() on bridged * bhnd(4) buses. * * This implementation of BUS_RESUME_CHILD() uses BUS_GET_RESOURCE_LIST() * to find the child's resources and call BHNDB_SUSPEND_RESOURCE() for all * child resources, ensuring that the device's allocated bridge resources * will be available to other devices during bus resumption. * * Before suspending any resources, @p child is suspended by * calling bhnd_generic_suspend_child(). * * If @p child is not a direct child of @p dev, suspension is delegated to * the @p dev parent. */ int bhnd_generic_br_suspend_child(device_t dev, device_t child) { struct resource_list *rl; int error; if (device_get_parent(child) != dev) BUS_SUSPEND_CHILD(device_get_parent(dev), child); if (device_is_suspended(child)) return (EBUSY); /* Suspend the child device */ if ((error = bhnd_generic_suspend_child(dev, child))) return (error); /* Fetch the resource list. If none, there's nothing else to do */ rl = BUS_GET_RESOURCE_LIST(device_get_parent(child), child); if (rl == NULL) return (0); /* Suspend all child resources. */ bhndb_do_suspend_resources(dev, rl); return (0); } /** * Helper function for implementing BUS_RESUME_CHILD() on bridged * bhnd(4) bus devices. * * This implementation of BUS_RESUME_CHILD() uses BUS_GET_RESOURCE_LIST() * to find the child's resources and call BHNDB_RESUME_RESOURCE() for all * child resources, before delegating to bhnd_generic_resume_child(). * * If resource resumption fails, @p child will not be resumed. * * If @p child is not a direct child of @p dev, suspension is delegated to * the @p dev parent. */ int bhnd_generic_br_resume_child(device_t dev, device_t child) { struct resource_list *rl; struct resource_list_entry *rle; int error; if (device_get_parent(child) != dev) BUS_RESUME_CHILD(device_get_parent(dev), child); if (!device_is_suspended(child)) return (EBUSY); /* Fetch the resource list. If none, there's nothing else to do */ rl = BUS_GET_RESOURCE_LIST(device_get_parent(child), child); if (rl == NULL) return (bhnd_generic_resume_child(dev, child)); /* Resume all resources */ STAILQ_FOREACH(rle, rl, link) { /* Skip non-allocated resources */ if (rle->res == NULL) continue; error = BHNDB_RESUME_RESOURCE(device_get_parent(dev), dev, rle->type, rle->res); if (error) { /* Put all resources back into a suspend state */ bhndb_do_suspend_resources(dev, rl); return (error); } } /* Now that all resources are resumed, resume child */ if ((error = bhnd_generic_resume_child(dev, child))) { /* Put all resources back into a suspend state */ bhndb_do_suspend_resources(dev, rl); } return (error); } /** * Find a SYS_RES_MEMORY resource containing the given address range. * * @param br The bhndb resource state to search. * @param start The start address of the range to search for. * @param count The size of the range to search for. * * @retval resource the host resource containing the requested range. * @retval NULL if no resource containing the requested range can be found. */ struct resource * bhndb_find_resource_range(struct bhndb_resources *br, rman_res_t start, rman_res_t count) { + 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]; if (br->res_spec->type != SYS_RES_MEMORY) continue; /* Verify range */ if (rman_get_start(r) > start) continue; if (rman_get_end(r) < (start + count - 1)) continue; return (r); } return (NULL); } /** * Find the resource containing @p win. * * @param br The bhndb resource state to search. * @param win A register window. * * @retval resource the resource containing @p win. * @retval NULL if no resource containing @p win can be found. */ struct resource * bhndb_find_regwin_resource(struct bhndb_resources *br, const struct bhndb_regwin *win) { 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) continue; if (win->res.rid != rspecs[i].rid) continue; /* Found declared resource */ return (br->res[i]); } device_printf(br->dev, "missing regwin resource spec (type=%d, rid=%d)\n", win->res.type, win->res.rid); return (NULL); } /** - * 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 * bhndb_alloc_resources(device_t dev, device_t parent_dev, const struct bhndb_hwcfg *cfg) { 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; r = malloc(sizeof(*r), M_BHND, M_NOWAIT|M_ZERO); if (r == NULL) return (NULL); /* Basic initialization */ r->dev = dev; r->parent_dev = parent_dev; r->cfg = cfg; r->min_prio = BHNDB_PRIORITY_NONE; STAILQ_INIT(&r->bus_regions); /* Initialize host address space resource manager. */ r->ht_mem_rman.rm_start = 0; r->ht_mem_rman.rm_end = ~0; r->ht_mem_rman.rm_type = RMAN_ARRAY; r->ht_mem_rman.rm_descr = "BHNDB host memory"; if ((error = rman_init(&r->ht_mem_rman))) { device_printf(r->dev, "could not initialize ht_mem_rman\n"); goto failed; } free_ht_mem = true; /* Initialize resource manager for the bridged address space. */ r->br_mem_rman.rm_start = 0; r->br_mem_rman.rm_end = BUS_SPACE_MAXADDR_32BIT; r->br_mem_rman.rm_type = RMAN_ARRAY; r->br_mem_rman.rm_descr = "BHNDB bridged memory"; if ((error = rman_init(&r->br_mem_rman))) { device_printf(r->dev, "could not initialize br_mem_rman\n"); goto failed; } free_br_mem = true; error = rman_manage_region(&r->br_mem_rman, 0, BUS_SPACE_MAXADDR_32BIT); if (error) { device_printf(r->dev, "could not configure br_mem_rman\n"); goto failed; } - - /* Determine our bridge resource count from the hardware config. */ - res_num = 0; - for (size_t i = 0; cfg->resource_specs[i].type != -1; i++) - 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, BHNDB_REGWIN_T_DYN); if (r->dwa_count >= INT_MAX) { device_printf(r->dev, "max dynamic regwin count exceeded\n"); goto failed; } /* Allocate the dynamic window allocation table. */ r->dw_alloc = malloc(sizeof(r->dw_alloc[0]) * r->dwa_count, M_BHND, M_NOWAIT); if (r->dw_alloc == NULL) goto failed; /* Allocate the dynamic window allocation freelist */ r->dwa_freelist = bit_alloc(r->dwa_count, M_BHND, M_NOWAIT); if (r->dwa_freelist == NULL) goto failed; /* Initialize the dynamic window table */ rnid = 0; last_window_size = 0; for (win = cfg->register_windows; win->win_type != BHNDB_REGWIN_T_INVALID; win++) { struct bhndb_dw_alloc *dwa; /* Skip non-DYN windows */ if (win->win_type != BHNDB_REGWIN_T_DYN) continue; /* Validate the window size */ if (win->win_size == 0) { device_printf(r->dev, "ignoring zero-length dynamic " "register window\n"); continue; } else if (last_window_size == 0) { last_window_size = win->win_size; } else if (last_window_size != win->win_size) { /* * No existing hardware should trigger this. * * If you run into this in the future, the dynamic * window allocator and the resource priority system * will need to be extended to support multiple register * window allocation pools. */ device_printf(r->dev, "devices that vend multiple " "dynamic register window sizes are not currently " "supported\n"); goto failed; } dwa = &r->dw_alloc[rnid]; dwa->win = win; dwa->parent_res = NULL; dwa->rnid = rnid; 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, (unsigned long long) win->win_size); 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 (br->res != NULL) + free(br->res, M_BHND); - if (free_br_mem) - rman_fini(&r->br_mem_rman); + if (br->res_spec != NULL) + free(br->res_spec, M_BHND); - if (r->res != NULL) - free(r->res, 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); } /** * Deallocate the given bridge resource structure and any associated resources. * * @param br Resource state to be deallocated. */ void bhndb_free_resources(struct bhndb_resources *br) { struct bhndb_region *region, *r_next; struct bhndb_dw_alloc *dwa; struct bhndb_dw_rentry *dwr, *dwr_next; /* No window regions may still be held */ if (!bhndb_dw_all_free(br)) { for (int i = 0; i < br->dwa_count; i++) { dwa = &br->dw_alloc[i]; /* Skip free dynamic windows */ if (bhndb_dw_is_free(br, dwa)) continue; device_printf(br->dev, "leaked dynamic register window %d\n", dwa->rnid); } } /* 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++) { dwa = &br->dw_alloc[i]; LIST_FOREACH_SAFE(dwr, &dwa->refs, dw_link, dwr_next) { LIST_REMOVE(dwr, dw_link); free(dwr, M_BHND); } } /* Release bus regions */ STAILQ_FOREACH_SAFE(region, &br->bus_regions, link, r_next) { STAILQ_REMOVE(&br->bus_regions, region, bhndb_region, link); free(region, M_BHND); } /* Release our resource managers */ rman_fini(&br->ht_mem_rman); rman_fini(&br->br_mem_rman); /* Free backing resource state structures */ - free(br->res, M_BHND); - free(br->res_spec, M_BHND); + 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); } /** * Add a bus region entry to @p r for the given base @p addr and @p size. * * @param br The resource state to which the bus region entry will be added. * @param addr The base address of this region. * @param size The size of this region. * @param priority The resource priority to be assigned to allocations * made within this bus region. * @param static_regwin If available, a static register window mapping this * bus region entry. If not available, NULL. * * @retval 0 success * @retval non-zero if adding the bus region fails. */ int bhndb_add_resource_region(struct bhndb_resources *br, bhnd_addr_t addr, bhnd_size_t size, bhndb_priority_t priority, const struct bhndb_regwin *static_regwin) { struct bhndb_region *reg; /* Insert in the bus resource list */ reg = malloc(sizeof(*reg), M_BHND, M_NOWAIT); if (reg == NULL) return (ENOMEM); *reg = (struct bhndb_region) { .addr = addr, .size = size, .priority = priority, .static_regwin = static_regwin }; STAILQ_INSERT_HEAD(&br->bus_regions, reg, link); return (0); } /** * Find the maximum start and end limits of the register window mapping * resource @p r. * * If the memory range is not mapped by an existing dynamic or static register * window, ENOENT will be returned. * * @param br The resource state to search. * @param r The resource to search for in @p br. * @param addr The requested starting address. * @param size The requested size. * * @retval bhndb_region A region that fully contains the requested range. * @retval NULL If no mapping region can be found. */ int bhndb_find_resource_limits(struct bhndb_resources *br, struct resource *r, rman_res_t *start, rman_res_t *end) { struct bhndb_dw_alloc *dynamic; struct bhndb_region *sregion; /* Check for an enclosing dynamic register window */ if ((dynamic = bhndb_dw_find_resource(br, r))) { *start = dynamic->target; *end = dynamic->target + dynamic->win->win_size - 1; return (0); } /* Check for a static region */ sregion = bhndb_find_resource_region(br, rman_get_start(r), rman_get_size(r)); if (sregion != NULL && sregion->static_regwin != NULL) { *start = sregion->addr; *end = sregion->addr + sregion->size - 1; return (0); } /* Not found */ return (ENOENT); } /** * Find the bus region that maps @p size bytes at @p addr. * * @param br The resource state to search. * @param addr The requested starting address. * @param size The requested size. * * @retval bhndb_region A region that fully contains the requested range. * @retval NULL If no mapping region can be found. */ struct bhndb_region * bhndb_find_resource_region(struct bhndb_resources *br, bhnd_addr_t addr, bhnd_size_t size) { struct bhndb_region *region; STAILQ_FOREACH(region, &br->bus_regions, link) { /* Request must fit within the region's mapping */ if (addr < region->addr) continue; if (addr + size > region->addr + region->size) continue; return (region); } /* Not found */ return (NULL); } /** * Find the entry matching @p r in @p dwa's references, if any. * * @param dwa The dynamic window allocation to search * @param r The resource to search for in @p dwa. */ static struct bhndb_dw_rentry * bhndb_dw_find_resource_entry(struct bhndb_dw_alloc *dwa, struct resource *r) { struct bhndb_dw_rentry *rentry; LIST_FOREACH(rentry, &dwa->refs, dw_link) { struct resource *dw_res = rentry->dw_res; /* Match dev/rid/addr/size */ if (rman_get_device(dw_res) != rman_get_device(r) || rman_get_rid(dw_res) != rman_get_rid(r) || rman_get_start(dw_res) != rman_get_start(r) || rman_get_size(dw_res) != rman_get_size(r)) { continue; } /* Matching allocation found */ return (rentry); } return (NULL); } /** * Find the dynamic region allocated for @p r, if any. * * @param br The resource state to search. * @param r The resource to search for. * * @retval bhndb_dw_alloc The allocation record for @p r. * @retval NULL if no dynamic window is allocated for @p r. */ struct bhndb_dw_alloc * bhndb_dw_find_resource(struct bhndb_resources *br, struct resource *r) { struct bhndb_dw_alloc *dwa; for (size_t i = 0; i < br->dwa_count; i++) { dwa = &br->dw_alloc[i]; /* Skip free dynamic windows */ if (bhndb_dw_is_free(br, dwa)) continue; /* Matching allocation found? */ if (bhndb_dw_find_resource_entry(dwa, r) != NULL) return (dwa); } return (NULL); } /** * Find an existing dynamic window mapping @p size bytes * at @p addr. The window may or may not be free. * * @param br The resource state to search. * @param addr The requested starting address. * @param size The requested size. * * @retval bhndb_dw_alloc A window allocation that fully contains the requested * range. * @retval NULL If no mapping region can be found. */ struct bhndb_dw_alloc * bhndb_dw_find_mapping(struct bhndb_resources *br, bhnd_addr_t addr, bhnd_size_t size) { struct bhndb_dw_alloc *dwr; const struct bhndb_regwin *win; /* Search for an existing dynamic mapping of this address range. */ for (size_t i = 0; i < br->dwa_count; i++) { dwr = &br->dw_alloc[i]; win = dwr->win; /* Verify the range */ if (addr < dwr->target) continue; if (addr + size > dwr->target + win->win_size) continue; /* Found a usable mapping */ return (dwr); } /* not found */ return (NULL); } /** * Retain a reference to @p dwa for use by @p res. * * @param br The resource state owning @p dwa. * @param dwa The allocation record to be retained. * @param res The resource that will own a reference to @p dwa. * * @retval 0 success * @retval ENOMEM Failed to allocate a new reference structure. */ int bhndb_dw_retain(struct bhndb_resources *br, struct bhndb_dw_alloc *dwa, struct resource *res) { struct bhndb_dw_rentry *rentry; KASSERT(bhndb_dw_find_resource_entry(dwa, res) == NULL, ("double-retain of dynamic window for same resource")); /* Insert a reference entry; we use M_NOWAIT to allow use from * within a non-sleepable lock */ rentry = malloc(sizeof(*rentry), M_BHND, M_NOWAIT); if (rentry == NULL) return (ENOMEM); rentry->dw_res = res; LIST_INSERT_HEAD(&dwa->refs, rentry, dw_link); /* Update the free list */ bit_set(br->dwa_freelist, dwa->rnid); return (0); } /** * Release a reference to @p dwa previously retained by @p res. If the * reference count of @p dwa reaches zero, it will be added to the * free list. * * @param br The resource state owning @p dwa. * @param dwa The allocation record to be released. * @param res The resource that currently owns a reference to @p dwa. */ void bhndb_dw_release(struct bhndb_resources *br, struct bhndb_dw_alloc *dwa, struct resource *r) { struct bhndb_dw_rentry *rentry; /* Find the rentry */ rentry = bhndb_dw_find_resource_entry(dwa, r); KASSERT(rentry != NULL, ("over release of resource entry")); LIST_REMOVE(rentry, dw_link); free(rentry, M_BHND); /* If this was the last reference, update the free list */ if (LIST_EMPTY(&dwa->refs)) bit_clear(br->dwa_freelist, dwa->rnid); } /** * Attempt to set (or reset) the target address of @p dwa to map @p size bytes * at @p addr. * * This will apply any necessary window alignment and verify that * the window is capable of mapping the requested range prior to modifying * therecord. * * @param dev The device on which to issue the BHNDB_SET_WINDOW_ADDR() request. * @param br The resource state owning @p dwa. * @param dwa The allocation record to be configured. * @param addr The address to be mapped via @p dwa. * @param size The number of bytes to be mapped at @p addr. * * @retval 0 success * @retval non-zero no usable register window available. */ int bhndb_dw_set_addr(device_t dev, struct bhndb_resources *br, struct bhndb_dw_alloc *dwa, bus_addr_t addr, bus_size_t size) { const struct bhndb_regwin *rw; bus_addr_t offset; int error; rw = dwa->win; KASSERT(bhndb_dw_is_free(br, dwa), ("attempting to set the target address on an in-use window")); /* Page-align the target address */ offset = addr % rw->win_size; dwa->target = addr - offset; /* Verify that the window is large enough for the full target */ if (rw->win_size - offset < size) return (ENOMEM); /* Update the window target */ error = BHNDB_SET_WINDOW_ADDR(dev, dwa->win, dwa->target); if (error) { dwa->target = 0x0; return (error); } return (0); } /** * Return the count of @p type register windows in @p table. * * @param table The table to search. * @param type The required window type, or BHNDB_REGWIN_T_INVALID to * count all register window types. */ size_t bhndb_regwin_count(const struct bhndb_regwin *table, bhndb_regwin_type_t type) { const struct bhndb_regwin *rw; size_t count; count = 0; for (rw = table; rw->win_type != BHNDB_REGWIN_T_INVALID; rw++) { if (type == BHNDB_REGWIN_T_INVALID || rw->win_type == type) count++; } return (count); } /** * Search @p table for the first window with the given @p type. * * @param table The table to search. * @param type The required window type. * @param min_size The minimum window size. * * @retval bhndb_regwin The first matching window. * @retval NULL If no window of the requested type could be found. */ const struct bhndb_regwin * bhndb_regwin_find_type(const struct bhndb_regwin *table, bhndb_regwin_type_t type, bus_size_t min_size) { const struct bhndb_regwin *rw; for (rw = table; rw->win_type != BHNDB_REGWIN_T_INVALID; rw++) { if (rw->win_type == type && rw->win_size >= min_size) return (rw); } return (NULL); } /** * Search @p windows for the first matching core window. * * @param table The table to search. * @param class The required core class. * @param unit The required core unit, or -1. * @param port_type The required port type. * @param port The required port. * @param region The required region. * * @retval bhndb_regwin The first matching window. * @retval NULL If no matching window was found. */ const struct bhndb_regwin * bhndb_regwin_find_core(const struct bhndb_regwin *table, bhnd_devclass_t class, int unit, bhnd_port_type port_type, u_int port, u_int region) { const struct bhndb_regwin *rw; for (rw = table; rw->win_type != BHNDB_REGWIN_T_INVALID; rw++) { if (rw->win_type != BHNDB_REGWIN_T_CORE) continue; if (rw->d.core.class != class) continue; if (unit != -1 && rw->d.core.unit != unit) continue; if (rw->d.core.port_type != port_type) continue; if (rw->d.core.port != port) continue; if (rw->d.core.region != region) continue; return (rw); } return (NULL); } /** * Search @p windows for the best available window of at least @p min_size. * * Search order: * - BHND_REGWIN_T_CORE * - BHND_REGWIN_T_DYN * * @param table The table to search. * @param class The required core class. * @param unit The required core unit, or -1. * @param port_type The required port type. * @param port The required port. * @param region The required region. * @param min_size The minimum window size. * * @retval bhndb_regwin The first matching window. * @retval NULL If no matching window was found. */ const struct bhndb_regwin * bhndb_regwin_find_best(const struct bhndb_regwin *table, bhnd_devclass_t class, int unit, bhnd_port_type port_type, u_int port, u_int region, bus_size_t min_size) { const struct bhndb_regwin *rw; /* Prefer a fixed core mapping */ rw = bhndb_regwin_find_core(table, class, unit, port_type, port, region); if (rw != NULL) return (rw); /* Fall back on a generic dynamic window */ return (bhndb_regwin_find_type(table, BHNDB_REGWIN_T_DYN, min_size)); } /** - * 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) + if (core->unit != 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)) - { - return (false); - } /* Matches */ return (true); } /** * 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); } /* not found */ return (NULL); } Index: head/sys/dev/bhnd/bhndb/bhndbvar.h =================================================================== --- head/sys/dev/bhnd/bhndb/bhndbvar.h (revision 305370) +++ head/sys/dev/bhnd/bhndb/bhndbvar.h (revision 305371) @@ -1,103 +1,103 @@ /*- * Copyright (c) 2015 Landon Fuller * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. * * $FreeBSD$ */ #ifndef _BHND_BHNDBVAR_H_ #define _BHND_BHNDBVAR_H_ #include #include #include #include #include #include #include #include #include "bhndb.h" #include "bhndb_if.h" /* * Definitions shared by bhndb(4) driver implementations. */ DECLARE_CLASS(bhndb_driver); struct bhndb_resources; int bhndb_attach(device_t dev, bhnd_devclass_t bridge_devclass); int bhndb_generic_probe(device_t dev); int bhndb_generic_detach(device_t dev); int bhndb_generic_suspend(device_t dev); int bhndb_generic_resume(device_t dev); int bhndb_generic_init_full_config(device_t dev, device_t child, const struct bhndb_hw_priority *hw_prio_table); int bhnd_generic_br_suspend_child(device_t dev, device_t child); int bhnd_generic_br_resume_child(device_t dev, device_t child); /** * bhndb child address space. Children either operate in the bridged * SoC address space, or within the address space mapped to the host * device (e.g. the PCI BAR(s)). */ typedef enum { BHNDB_ADDRSPACE_BRIDGED, /**< bridged (SoC) address space */ BHNDB_ADDRSPACE_NATIVE /**< host address space */ } bhndb_addrspace; /** bhndb child instance state */ struct bhndb_devinfo { bhndb_addrspace addrspace; /**< child address space. */ struct resource_list resources; /**< child resources. */ }; /** * bhndb driver instance state. Must be first member of all subclass * softc structures. */ struct bhndb_softc { 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 */ }; #endif /* _BHND_BHNDBVAR_H_ */ Index: head/sys/dev/bhnd/cores/chipc/chipcreg.h =================================================================== --- head/sys/dev/bhnd/cores/chipc/chipcreg.h (revision 305370) +++ head/sys/dev/bhnd/cores/chipc/chipcreg.h (revision 305371) @@ -1,966 +1,960 @@ /*- * Copyright (c) 2015-2016 Landon Fuller * Copyright (c) 2010 Broadcom Corporation * All rights reserved. * * This file is derived from the sbchipc.h header distributed with * Broadcom's initial brcm80211 Linux driver release, as * contributed to the Linux staging repository. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $FreeBSD$ */ #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) /** Evaluates to true if the given ChipCommon core revision provides * the core count via the chip identification register. */ #define CHIPC_NCORES_MIN_HWREV(hwrev) ((hwrev) == 4 || (hwrev) >= 6) /** Evaluates to true if the given ChipCommon core revision supports * the CHIPC_CAPABILITIES_EXT register */ #define CHIPC_HWREV_HAS_CAP_EXT(hwrev) ((hwrev) >= 35) /** Evaluates to true if the chipcommon core (determined from the provided * @p _chipid (CHIPC_ID) register value) provides a pointer to the enumeration * table via CHIPC_EROMPTR */ #define CHIPC_HAS_EROMPTR(_chipid) \ (CHIPC_GET_BITS((_chipid), CHIPC_ID_BUS) != BHND_CHIPTYPE_SIBA) #define CHIPC_GET_FLAG(_value, _flag) (((_value) & _flag) != 0) #define CHIPC_GET_BITS(_value, _field) \ ((_value & _field ## _MASK) >> _field ## _SHIFT) #define CHIPC_ID 0x00 #define CHIPC_CAPABILITIES 0x04 #define CHIPC_CORECTRL 0x08 /* rev >= 1 */ #define CHIPC_BIST 0x0C #define CHIPC_OTPST 0x10 /**< otp status */ #define CHIPC_OTPCTRL 0x14 /**< otp control */ #define CHIPC_OTPPROG 0x18 #define CHIPC_OTPLAYOUT 0x1C /**< otp layout (IPX OTP) */ #define CHIPC_INTST 0x20 /**< interrupt status */ #define CHIPC_INTM 0x24 /**< interrupt mask */ #define CHIPC_CHIPCTRL 0x28 /**< chip control (rev >= 11) */ #define CHIPC_CHIPST 0x2C /**< chip status (rev >= 11) */ #define CHIPC_JTAGCMD 0x30 #define CHIPC_JTAGIR 0x34 #define CHIPC_JTAGDR 0x38 #define CHIPC_JTAGCTRL 0x3c #define CHIPC_SFLASH_BASE 0x40 #define CHIPC_SFLASH_SIZE 12 #define CHIPC_SFLASHCTRL 0x40 #define CHIPC_SFLASHADDR 0x44 #define CHIPC_SFLASHDATA 0x48 /* siba backplane configuration broadcast (siba-only) */ #define CHIPC_SBBCAST_ADDR 0x50 #define CHIPC_SBBCAST_DATA 0x54 #define CHIPC_GPIOPU 0x58 /**< pull-up mask (rev >= 20) */ #define CHIPC_GPIOPD 0x5C /**< pull down mask (rev >= 20) */ #define CHIPC_GPIOIN 0x60 #define CHIPC_GPIOOUT 0x64 #define CHIPC_GPIOOUTEN 0x68 #define CHIPC_GPIOCTRL 0x6C #define CHIPC_GPIOPOL 0x70 #define CHIPC_GPIOINTM 0x74 /**< gpio interrupt mask */ #define CHIPC_GPIOEVENT 0x78 /**< gpio event (rev >= 11) */ #define CHIPC_GPIOEVENT_INTM 0x7C /**< gpio event interrupt mask (rev >= 11) */ #define CHIPC_WATCHDOG 0x80 /**< watchdog timer */ #define CHIPC_GPIOEVENT_INTPOLARITY 0x84 /**< gpio even interrupt polarity (rev >= 11) */ #define CHIPC_GPIOTIMERVAL 0x88 /**< gpio-based LED duty cycle (rev >= 16) */ #define CHIPC_GPIOTIMEROUTMASK 0x8C /* clock control registers (non-PMU devices) */ #define CHIPC_CLKC_N 0x90 #define CHIPC_CLKC_SB 0x94 /* m0 (backplane) */ #define CHIPC_CLKC_PCI 0x98 /* m1 */ #define CHIPC_CLKC_M2 0x9C /* mii/uart/mipsref */ #define CHIPC_CLKC_M3 0xA0 /* cpu */ #define CHIPC_CLKDIV 0xA4 /* rev >= 3 */ #define CHIPC_GPIODEBUGSEL 0xA8 /* rev >= 28 */ #define CHIPC_CAPABILITIES_EXT 0xAC /* pll/slowclk clock control registers (rev >= 4) */ #define CHIPC_PLL_ON_DELAY 0xB0 /* rev >= 4 */ #define CHIPC_PLL_FREFSEL_DELAY 0xB4 /* rev >= 4 */ #define CHIPC_PLL_SLOWCLK_CTL 0xB8 /* "slowclock" (rev 6-9) */ /* "instaclock" clock control registers */ #define CHIPC_SYS_CLK_CTL 0xC0 /* "instaclock" (rev >= 10) */ #define CHIPC_SYS_CLK_ST_STRETCH 0xC4 /* state strech (?) rev >= 10 */ /* indirect backplane access (rev >= 10) */ #define CHIPC_BP_ADDRLOW 0xD0 #define CHIPC_BP_ADDRHIGH 0xD4 #define CHIPC_BP_DATA 0xD8 #define CHIPC_BP_INDACCESS 0xE0 /* SPI/I2C (rev >= 37) */ #define CHIPC_GSIO_CTRL 0xE4 #define CHIPC_GSIO_ADDR 0xE8 #define CHIPC_GSIO_DATA 0xEC /* More clock dividers (corerev >= 32) */ #define CHIPC_CLKDIV2 0xF0 #define CHIPC_EROMPTR 0xFC /**< 32-bit EROM base address * on BCMA devices */ /* ExtBus control registers (rev >= 3) */ #define CHIPC_PCMCIA_CFG 0x100 #define CHIPC_PCMCIA_MEMWAIT 0x104 #define CHIPC_PCMCIA_ATTRWAIT 0x108 #define CHIPC_PCMCIA_IOWAIT 0x10C #define CHIPC_IDE_CFG 0x110 #define CHIPC_IDE_MEMWAIT 0x114 #define CHIPC_IDE_ATTRWAIT 0x118 #define CHIPC_IDE_IOWAIT 0x11C #define CHIPC_PROG_CFG 0x120 #define CHIPC_PROG_WAITCOUNT 0x124 #define CHIPC_FLASH_CFG 0x128 #define CHIPC_FLASH_WAITCOUNT 0x12C #define CHIPC_SECI_CFG 0x130 #define CHIPC_SECI_ST 0x134 #define CHIPC_SECI_STM 0x138 #define CHIPC_SECI_RXNBC 0x13C /* Enhanced Coexistence Interface (ECI) registers (rev 21-34) */ #define CHIPC_ECI_OUTPUT 0x140 #define CHIPC_ECI_CTRL 0x144 #define CHIPC_ECI_INPUTLO 0x148 #define CHIPC_ECI_INPUTMI 0x14C #define CHIPC_ECI_INPUTHI 0x150 #define CHIPC_ECI_INPUTINTPOLARITYLO 0x154 #define CHIPC_ECI_INPUTINTPOLARITYMI 0x158 #define CHIPC_ECI_INPUTINTPOLARITYHI 0x15C #define CHIPC_ECI_INTMASKLO 0x160 #define CHIPC_ECI_INTMASKMI 0x164 #define CHIPC_ECI_INTMASKHI 0x168 #define CHIPC_ECI_EVENTLO 0x16C #define CHIPC_ECI_EVENTMI 0x170 #define CHIPC_ECI_EVENTHI 0x174 #define CHIPC_ECI_EVENTMASKLO 0x178 #define CHIPC_ECI_EVENTMASKMI 0x17C #define CHIPC_ECI_EVENTMASKHI 0x180 #define CHIPC_FLASHSTRCFG 0x18C /**< BCM4706 NAND flash config */ #define CHIPC_SPROM_CTRL 0x190 /**< SPROM interface (rev >= 32) */ #define CHIPC_SPROM_ADDR 0x194 #define CHIPC_SPROM_DATA 0x198 /* Clock control and hardware workarounds (corerev >= 20) */ #define CHIPC_CLK_CTL_ST 0x1E0 #define CHIPC_SPROM_HWWAR 0x19 #define CHIPC_UART_BASE 0x300 #define CHIPC_UART_SIZE 0x100 #define CHIPC_UART_MAX 3 /**< max UART blocks */ #define CHIPC_UART(_n) (CHIPC_UART_BASE + (CHIPC_UART_SIZE*_n)) /* PMU register block (rev >= 20) */ #define CHIPC_PMU_BASE 0x600 #define CHIPC_PMU_SIZE 0x70 #define CHIPC_SPROM_OTP 0x800 /* SPROM/OTP address space */ #define CHIPC_SPROM_OTP_SIZE 0x400 /** chipid */ #define CHIPC_ID_CHIP_MASK 0x0000FFFF /**< chip id */ #define CHIPC_ID_CHIP_SHIFT 0 #define CHIPC_ID_REV_MASK 0x000F0000 /**< chip revision */ #define CHIPC_ID_REV_SHIFT 16 #define CHIPC_ID_PKG_MASK 0x00F00000 /**< physical package ID */ #define CHIPC_ID_PKG_SHIFT 20 #define CHIPC_ID_NUMCORE_MASK 0x0F000000 /**< number of cores on chip (rev >= 4) */ #define CHIPC_ID_NUMCORE_SHIFT 24 #define CHIPC_ID_BUS_MASK 0xF0000000 /**< chip/interconnect type (BHND_CHIPTYPE_*) */ #define CHIPC_ID_BUS_SHIFT 28 /* capabilities */ #define CHIPC_CAP_NUM_UART_MASK 0x00000003 /* Number of UARTs (1-3) */ #define CHIPC_CAP_NUM_UART_SHIFT 0 #define CHIPC_CAP_MIPSEB 0x00000004 /* MIPS is in big-endian mode */ #define CHIPC_CAP_UCLKSEL_MASK 0x00000018 /* UARTs clock select */ #define CHIPC_CAP_UCLKSEL_SHIFT 3 #define CHIPC_CAP_UCLKSEL_UINTCLK 0x1 /* UARTs are driven by internal divided clock */ #define CHIPC_CAP_UARTGPIO 0x00000020 /* UARTs own GPIOs 15:12 */ #define CHIPC_CAP_EXTBUS_MASK 0x000000c0 /* External bus mask */ #define CHIPC_CAP_EXTBUS_SHIFT 6 #define CHIPC_CAP_EXTBUS_NONE 0x0 /* No ExtBus present */ #define CHIPC_CAP_EXTBUS_FULL 0x1 /* ExtBus: PCMCIA, IDE & Prog */ #define CHIPC_CAP_EXTBUS_PROG 0x2 /* ExtBus: ProgIf only */ #define CHIPC_CAP_FLASH_MASK 0x00000700 /* Type of flash */ #define CHIPC_CAP_FLASH_SHIFT 8 #define CHIPC_CAP_FLASH_NONE 0x0 /* No flash */ #define CHIPC_CAP_SFLASH_ST 0x1 /* ST serial flash */ #define CHIPC_CAP_SFLASH_AT 0x2 /* Atmel serial flash */ #define CHIPC_CAP_NFLASH 0x3 /* NAND flash */ #define CHIPC_CAP_PFLASH 0x7 /* Parallel flash */ #define CHIPC_CAP_PLL_MASK 0x00038000 /* Type of PLL */ #define CHIPC_CAP_PLL_SHIFT 15 #define CHIPC_CAP_PWR_CTL 0x00040000 /* Power/clock control */ #define CHIPC_CAP_OTP_SIZE_MASK 0x00380000 /* OTP Size (0 = none) */ #define CHIPC_CAP_OTP_SIZE_SHIFT 19 /* OTP Size shift */ #define CHIPC_CAP_OTP_SIZE_BASE 5 /* OTP Size base */ #define CHIPC_CAP_JTAGP 0x00400000 /* JTAG Master Present */ #define CHIPC_CAP_ROM 0x00800000 /* Internal boot rom active */ #define CHIPC_CAP_BKPLN64 0x08000000 /* 64-bit backplane */ #define CHIPC_CAP_PMU 0x10000000 /* PMU Present, rev >= 20 */ #define CHIPC_CAP_ECI 0x20000000 /* Enhanced Coexistence Interface */ #define CHIPC_CAP_SPROM 0x40000000 /* SPROM Present, rev >= 32 */ #define CHIPC_CAP_4706_NFLASH 0x80000000 /* NAND flash present, BCM4706 or chipc rev38 (BCM5357)? */ #define CHIPC_CAP2_SECI 0x00000001 /* SECI Present, rev >= 36 */ #define CHIPC_CAP2_GSIO 0x00000002 /* GSIO (spi/i2c) present, rev >= 37 */ #define CHIPC_CAP2_GCI 0x00000004 /* GCI present (rev >= ??) */ #define CHIPC_CAP2_AOB 0x00000040 /* Always on Bus present (rev >= 49) * * If set, PMU and GCI registers * are found in dedicated cores. * * This appears to be a lower power * APB bus, bridged via ARM APB IP. */ /* * ChipStatus (Common) */ /** ChipStatus CIS/OTP/SPROM values used to advertise OTP/SPROM availability in * chipcommon revs 11-31. */ enum { CHIPC_CST_DEFCIS_SEL = 0, /**< OTP is powered up, use default CIS, no SPROM */ CHIPC_CST_SPROM_SEL = 1, /**< OTP is powered up, SPROM is present */ CHIPC_CST_OTP_SEL = 2, /**< OTP is powered up, no SPROM */ CHIPC_CST_OTP_PWRDN = 3 /**< OTP is powered down, SPROM is present (rev <= 22 only) */ }; #define CHIPC_CST_SPROM_OTP_SEL_R22_MASK 0x00000003 /**< chipstatus OTP/SPROM SEL value (rev 22) */ #define CHIPC_CST_SPROM_OTP_SEL_R22_SHIFT 0 #define CHIPC_CST_SPROM_OTP_SEL_R23_MASK 0x000000c0 /**< chipstatus OTP/SPROM SEL value (revs 23-31) * * it is unknown whether this is supported on * any CC revs >= 32 that also vend CHIPC_CAP_* * constants for OTP/SPROM/NVRAM availability. */ #define CHIPC_CST_SPROM_OTP_SEL_R23_SHIFT 6 /* PLL type */ #define CHIPC_PLL_NONE 0x0 #define CHIPC_PLL_TYPE1 0x2 /* 48MHz base, 3 dividers */ #define CHIPC_PLL_TYPE2 0x4 /* 48MHz, 4 dividers */ #define CHIPC_PLL_TYPE3 0x6 /* 25MHz, 2 dividers */ #define CHIPC_PLL_TYPE4 0x8 /* 48MHz, 4 dividers */ #define CHIPC_PLL_TYPE5 0x3 /* 25MHz, 4 dividers */ #define CHIPC_PLL_TYPE6 0x5 /* 100/200 or 120/240 only */ #define CHIPC_PLL_TYPE7 0x7 /* 25MHz, 4 dividers */ /* dynamic clock control defines */ #define CHIPC_LPOMINFREQ 25000 /* low power oscillator min */ #define CHIPC_LPOMAXFREQ 43000 /* low power oscillator max */ #define CHIPC_XTALMINFREQ 19800000 /* 20 MHz - 1% */ #define CHIPC_XTALMAXFREQ 20200000 /* 20 MHz + 1% */ #define CHIPC_PCIMINFREQ 25000000 /* 25 MHz */ #define CHIPC_PCIMAXFREQ 34000000 /* 33 MHz + fudge */ #define CHIPC_ILP_DIV_5MHZ 0 /* ILP = 5 MHz */ #define CHIPC_ILP_DIV_1MHZ 4 /* ILP = 1 MHz */ /* Power Control Defines */ #define CHIPC_PLL_DELAY 150 /* us pll on delay */ #define CHIPC_FREF_DELAY 200 /* us fref change delay */ #define CHIPC_MIN_SLOW_CLK 32 /* us Slow clock period */ #define CHIPC_XTAL_ON_DELAY 1000 /* us crystal power-on delay */ /* corecontrol */ #define CHIPC_UARTCLKO 0x00000001 /* Drive UART with internal clock */ #define CHIPC_SE 0x00000002 /* sync clk out enable (corerev >= 3) */ #define CHIPC_UARTCLKEN 0x00000008 /* enable UART Clock (corerev > = 21 */ /* chipcontrol */ #define CHIPCTRL_4321A0_DEFAULT 0x3a4 #define CHIPCTRL_4321A1_DEFAULT 0x0a4 #define CHIPCTRL_4321_PLL_DOWN 0x800000 /* serdes PLL down override */ /* Fields in the otpstatus register in rev >= 21 */ #define CHIPC_OTPS_OL_MASK 0x000000ff #define CHIPC_OTPS_OL_MFG 0x00000001 /* manuf row is locked */ #define CHIPC_OTPS_OL_OR1 0x00000002 /* otp redundancy row 1 is locked */ #define CHIPC_OTPS_OL_OR2 0x00000004 /* otp redundancy row 2 is locked */ #define CHIPC_OTPS_OL_GU 0x00000008 /* general use region is locked */ #define CHIPC_OTPS_GUP_MASK 0x00000f00 #define CHIPC_OTPS_GUP_SHIFT 8 #define CHIPC_OTPS_GUP_HW 0x00000100 /* h/w subregion is programmed */ #define CHIPC_OTPS_GUP_SW 0x00000200 /* s/w subregion is programmed */ #define CHIPC_OTPS_GUP_CI 0x00000400 /* chipid/pkgopt subregion is programmed */ #define CHIPC_OTPS_GUP_FUSE 0x00000800 /* fuse subregion is programmed */ #define CHIPC_OTPS_READY 0x00001000 #define CHIPC_OTPS_RV(x) (1 << (16 + (x))) /* redundancy entry valid */ #define CHIPC_OTPS_RV_MASK 0x0fff0000 /* IPX OTP fields in the otpcontrol register */ #define CHIPC_OTPC_PROGSEL 0x00000001 #define CHIPC_OTPC_PCOUNT_MASK 0x0000000e #define CHIPC_OTPC_PCOUNT_SHIFT 1 #define CHIPC_OTPC_VSEL_MASK 0x000000f0 #define CHIPC_OTPC_VSEL_SHIFT 4 #define CHIPC_OTPC_TMM_MASK 0x00000700 #define CHIPC_OTPC_TMM_SHIFT 8 #define CHIPC_OTPC_ODM 0x00000800 #define CHIPC_OTPC_PROGEN 0x80000000 /* Fields in otpprog in IPX OTP and HND OTP */ #define CHIPC_OTPP_COL_MASK 0x000000ff #define CHIPC_OTPP_COL_SHIFT 0 #define CHIPC_OTPP_ROW_MASK 0x0000ff00 #define CHIPC_OTPP_ROW_SHIFT 8 #define CHIPC_OTPP_OC_MASK 0x0f000000 #define CHIPC_OTPP_OC_SHIFT 24 #define CHIPC_OTPP_READERR 0x10000000 #define CHIPC_OTPP_VALUE_MASK 0x20000000 #define CHIPC_OTPP_VALUE_SHIFT 29 #define CHIPC_OTPP_START_BUSY 0x80000000 #define CHIPC_OTPP_READ 0x40000000 /* HND OTP */ /* otplayout */ #define CHIPC_OTPL_SIZE_MASK 0x0000f000 /* rev >= 49 */ #define CHIPC_OTPL_SIZE_SHIFT 12 #define CHIPC_OTPL_GUP_MASK 0x00000FFF /* bit offset to general use region */ #define CHIPC_OTPL_GUP_SHIFT 0 #define CHIPC_OTPL_CISFORMAT_NEW 0x80000000 /* rev >= 36 */ /* Opcodes for OTPP_OC field */ #define CHIPC_OTPPOC_READ 0 #define CHIPC_OTPPOC_BIT_PROG 1 #define CHIPC_OTPPOC_VERIFY 3 #define CHIPC_OTPPOC_INIT 4 #define CHIPC_OTPPOC_SET 5 #define CHIPC_OTPPOC_RESET 6 #define CHIPC_OTPPOC_OCST 7 #define CHIPC_OTPPOC_ROW_LOCK 8 #define CHIPC_OTPPOC_PRESCN_TEST 9 /* Jtagm characteristics that appeared at a given corerev */ #define CHIPC_JTAGM_CREV_OLD 10 /* Old command set, 16bit max IR */ #define CHIPC_JTAGM_CREV_IRP 22 /* Able to do pause-ir */ #define CHIPC_JTAGM_CREV_RTI 28 /* Able to do return-to-idle */ /* jtagcmd */ #define CHIPC_JCMD_START 0x80000000 #define CHIPC_JCMD_BUSY 0x80000000 #define CHIPC_JCMD_STATE_MASK 0x60000000 #define CHIPC_JCMD_STATE_TLR 0x00000000 /* Test-logic-reset */ #define CHIPC_JCMD_STATE_PIR 0x20000000 /* Pause IR */ #define CHIPC_JCMD_STATE_PDR 0x40000000 /* Pause DR */ #define CHIPC_JCMD_STATE_RTI 0x60000000 /* Run-test-idle */ #define CHIPC_JCMD0_ACC_MASK 0x0000f000 #define CHIPC_JCMD0_ACC_IRDR 0x00000000 #define CHIPC_JCMD0_ACC_DR 0x00001000 #define CHIPC_JCMD0_ACC_IR 0x00002000 #define CHIPC_JCMD0_ACC_RESET 0x00003000 #define CHIPC_JCMD0_ACC_IRPDR 0x00004000 #define CHIPC_JCMD0_ACC_PDR 0x00005000 #define CHIPC_JCMD0_IRW_MASK 0x00000f00 #define CHIPC_JCMD_ACC_MASK 0x000f0000 /* Changes for corerev 11 */ #define CHIPC_JCMD_ACC_IRDR 0x00000000 #define CHIPC_JCMD_ACC_DR 0x00010000 #define CHIPC_JCMD_ACC_IR 0x00020000 #define CHIPC_JCMD_ACC_RESET 0x00030000 #define CHIPC_JCMD_ACC_IRPDR 0x00040000 #define CHIPC_JCMD_ACC_PDR 0x00050000 #define CHIPC_JCMD_ACC_PIR 0x00060000 #define CHIPC_JCMD_ACC_IRDR_I 0x00070000 /* rev 28: return to run-test-idle */ #define CHIPC_JCMD_ACC_DR_I 0x00080000 /* rev 28: return to run-test-idle */ #define CHIPC_JCMD_IRW_MASK 0x00001f00 #define CHIPC_JCMD_IRW_SHIFT 8 #define CHIPC_JCMD_DRW_MASK 0x0000003f /* jtagctrl */ #define CHIPC_JCTRL_FORCE_CLK 4 /* Force clock */ #define CHIPC_JCTRL_EXT_EN 2 /* Enable external targets */ #define CHIPC_JCTRL_EN 1 /* Enable Jtag master */ /* Fields in clkdiv */ #define CHIPC_CLKD_SFLASH 0x0f000000 #define CHIPC_CLKD_SFLASH_SHIFT 24 #define CHIPC_CLKD_OTP 0x000f0000 #define CHIPC_CLKD_OTP_SHIFT 16 #define CHIPC_CLKD_JTAG 0x00000f00 #define CHIPC_CLKD_JTAG_SHIFT 8 #define CHIPC_CLKD_UART 0x000000ff #define CHIPC_CLKD2_SPROM 0x00000003 /* intstatus/intmask */ #define CHIPC_CI_GPIO 0x00000001 /* gpio intr */ #define CHIPC_CI_EI 0x00000002 /* extif intr (corerev >= 3) */ #define CHIPC_CI_TEMP 0x00000004 /* temp. ctrl intr (corerev >= 15) */ #define CHIPC_CI_SIRQ 0x00000008 /* serial IRQ intr (corerev >= 15) */ #define CHIPC_CI_PMU 0x00000020 /* pmu intr (corerev >= 21) */ #define CHIPC_CI_UART 0x00000040 /* uart intr (corerev >= 21) */ #define CHIPC_CI_WDRESET 0x80000000 /* watchdog reset occurred */ /* slow_clk_ctl */ #define CHIPC_SCC_SS_MASK 0x00000007 /* slow clock source mask */ #define CHIPC_SCC_SS_LPO 0x00000000 /* source of slow clock is LPO */ #define CHIPC_SCC_SS_XTAL 0x00000001 /* source of slow clock is crystal */ #define CHIPC_SCC_SS_PCI 0x00000002 /* source of slow clock is PCI */ #define CHIPC_SCC_LF 0x00000200 /* LPOFreqSel, 1: 160Khz, 0: 32KHz */ #define CHIPC_SCC_LP 0x00000400 /* LPOPowerDown, 1: LPO is disabled, * 0: LPO is enabled */ #define CHIPC_SCC_FS 0x00000800 /* ForceSlowClk, 1: sb/cores running on slow clock, * 0: power logic control */ #define CHIPC_SCC_IP 0x00001000 /* IgnorePllOffReq, 1/0: power logic ignores/honors * PLL clock disable requests from core */ #define CHIPC_SCC_XC 0x00002000 /* XtalControlEn, 1/0: power logic does/doesn't * disable crystal when appropriate */ #define CHIPC_SCC_XP 0x00004000 /* XtalPU (RO), 1/0: crystal running/disabled */ #define CHIPC_SCC_CD_MASK 0xffff0000 /* ClockDivider (SlowClk = 1/(4+divisor)) */ #define CHIPC_SCC_CD_SHIFT 16 /* system_clk_ctl */ #define CHIPC_SYCC_IE 0x00000001 /* ILPen: Enable Idle Low Power */ #define CHIPC_SYCC_AE 0x00000002 /* ALPen: Enable Active Low Power */ #define CHIPC_SYCC_FP 0x00000004 /* ForcePLLOn */ #define CHIPC_SYCC_AR 0x00000008 /* Force ALP (or HT if ALPen is not set */ #define CHIPC_SYCC_HR 0x00000010 /* Force HT */ #define CHIPC_SYCC_CD_MASK 0xffff0000 /* ClkDiv (ILP = 1/(4 * (divisor + 1)) */ #define CHIPC_SYCC_CD_SHIFT 16 /* Indirect backplane access */ #define CHIPC_BPIA_BYTEEN 0x0000000f #define CHIPC_BPIA_SZ1 0x00000001 #define CHIPC_BPIA_SZ2 0x00000003 #define CHIPC_BPIA_SZ4 0x00000007 #define CHIPC_BPIA_SZ8 0x0000000f #define CHIPC_BPIA_WRITE 0x00000100 #define CHIPC_BPIA_START 0x00000200 #define CHIPC_BPIA_BUSY 0x00000200 #define CHIPC_BPIA_ERROR 0x00000400 /* pcmcia/prog/flash_config */ #define CHIPC_CF_EN 0x00000001 /* enable */ #define CHIPC_CF_EM_MASK 0x0000000e /* mode */ #define CHIPC_CF_EM_SHIFT 1 #define CHIPC_CF_EM_FLASH 0 /* flash/asynchronous mode */ #define CHIPC_CF_EM_SYNC 2 /* synchronous mode */ #define CHIPC_CF_EM_PCMCIA 4 /* pcmcia mode */ #define CHIPC_CF_DS 0x00000010 /* destsize: 0=8bit, 1=16bit */ #define CHIPC_CF_BS 0x00000020 /* byteswap */ #define CHIPC_CF_CD_MASK 0x000000c0 /* clock divider */ #define CHIPC_CF_CD_SHIFT 6 #define CHIPC_CF_CD_DIV2 0x00000000 /* backplane/2 */ #define CHIPC_CF_CD_DIV3 0x00000040 /* backplane/3 */ #define CHIPC_CF_CD_DIV4 0x00000080 /* backplane/4 */ #define CHIPC_CF_CE 0x00000100 /* clock enable */ #define CHIPC_CF_SB 0x00000200 /* size/bytestrobe (synch only) */ /* pcmcia_memwait */ #define CHIPC_PM_W0_MASK 0x0000003f /* waitcount0 */ #define CHIPC_PM_W1_MASK 0x00001f00 /* waitcount1 */ #define CHIPC_PM_W1_SHIFT 8 #define CHIPC_PM_W2_MASK 0x001f0000 /* waitcount2 */ #define CHIPC_PM_W2_SHIFT 16 #define CHIPC_PM_W3_MASK 0x1f000000 /* waitcount3 */ #define CHIPC_PM_W3_SHIFT 24 /* pcmcia_attrwait */ #define CHIPC_PA_W0_MASK 0x0000003f /* waitcount0 */ #define CHIPC_PA_W1_MASK 0x00001f00 /* waitcount1 */ #define CHIPC_PA_W1_SHIFT 8 #define CHIPC_PA_W2_MASK 0x001f0000 /* waitcount2 */ #define CHIPC_PA_W2_SHIFT 16 #define CHIPC_PA_W3_MASK 0x1f000000 /* waitcount3 */ #define CHIPC_PA_W3_SHIFT 24 /* pcmcia_iowait */ #define CHIPC_PI_W0_MASK 0x0000003f /* waitcount0 */ #define CHIPC_PI_W1_MASK 0x00001f00 /* waitcount1 */ #define CHIPC_PI_W1_SHIFT 8 #define CHIPC_PI_W2_MASK 0x001f0000 /* waitcount2 */ #define CHIPC_PI_W2_SHIFT 16 #define CHIPC_PI_W3_MASK 0x1f000000 /* waitcount3 */ #define CHIPC_PI_W3_SHIFT 24 /* prog_waitcount */ #define CHIPC_PW_W0_MASK 0x0000001f /* waitcount0 */ #define CHIPC_PW_W1_MASK 0x00001f00 /* waitcount1 */ #define CHIPC_PW_W1_SHIFT 8 #define CHIPC_PW_W2_MASK 0x001f0000 /* waitcount2 */ #define CHIPC_PW_W2_SHIFT 16 #define CHIPC_PW_W3_MASK 0x1f000000 /* waitcount3 */ #define CHIPC_PW_W3_SHIFT 24 #define CHIPC_PW_W0 0x0000000c #define CHIPC_PW_W1 0x00000a00 #define CHIPC_PW_W2 0x00020000 #define CHIPC_PW_W3 0x01000000 /* flash_waitcount */ #define CHIPC_FW_W0_MASK 0x0000003f /* waitcount0 */ #define CHIPC_FW_W1_MASK 0x00001f00 /* waitcount1 */ #define CHIPC_FW_W1_SHIFT 8 #define CHIPC_FW_W2_MASK 0x001f0000 /* waitcount2 */ #define CHIPC_FW_W2_SHIFT 16 #define CHIPC_FW_W3_MASK 0x1f000000 /* waitcount3 */ #define CHIPC_FW_W3_SHIFT 24 /* When SPROM support present, fields in spromcontrol */ #define CHIPC_SRC_START 0x80000000 #define CHIPC_SRC_BUSY 0x80000000 #define CHIPC_SRC_OPCODE 0x60000000 #define CHIPC_SRC_OP_READ 0x00000000 #define CHIPC_SRC_OP_WRITE 0x20000000 #define CHIPC_SRC_OP_WRDIS 0x40000000 #define CHIPC_SRC_OP_WREN 0x60000000 #define CHIPC_SRC_OTPSEL 0x00000010 #define CHIPC_SRC_LOCK 0x00000008 #define CHIPC_SRC_SIZE_MASK 0x00000006 #define CHIPC_SRC_SIZE_1K 0x00000000 #define CHIPC_SRC_SIZE_4K 0x00000002 #define CHIPC_SRC_SIZE_16K 0x00000004 #define CHIPC_SRC_SIZE_SHIFT 1 #define CHIPC_SRC_PRESENT 0x00000001 /* gpiotimerval */ #define CHIPC_GPIO_ONTIME_SHIFT 16 /* clockcontrol_n */ #define CHIPC_CN_N1_MASK 0x3f /* n1 control */ #define CHIPC_CN_N1_SHIFT 0 #define CHIPC_CN_N2_MASK 0x3f00 /* n2 control */ #define CHIPC_CN_N2_SHIFT 8 #define CHIPC_CN_PLLC_MASK 0xf0000 /* pll control */ #define CHIPC_CN_PLLC_SHIFT 16 /* clockcontrol_sb/pci/uart */ #define CHIPC_M1_MASK 0x3f /* m1 control */ #define CHIPC_M1_SHIFT 0 #define CHIPC_M2_MASK 0x3f00 /* m2 control */ #define CHIPC_M2_SHIFT 8 #define CHIPC_M3_MASK 0x3f0000 /* m3 control */ #define CHIPC_M3_SHIFT 16 #define CHIPC_MC_MASK 0x1f000000 /* mux control */ #define CHIPC_MC_SHIFT 24 /* N3M Clock control magic field values */ #define CHIPC_F6_2 0x02 /* A factor of 2 in */ #define CHIPC_F6_3 0x03 /* 6-bit fields like */ #define CHIPC_F6_4 0x05 /* N1, M1 or M3 */ #define CHIPC_F6_5 0x09 #define CHIPC_F6_6 0x11 #define CHIPC_F6_7 0x21 #define CHIPC_F5_BIAS 5 /* 5-bit fields get this added */ #define CHIPC_MC_BYPASS 0x08 #define CHIPC_MC_M1 0x04 #define CHIPC_MC_M1M2 0x02 #define CHIPC_MC_M1M2M3 0x01 #define CHIPC_MC_M1M3 0x11 /* Type 2 Clock control magic field values */ #define CHIPC_T2_BIAS 2 /* n1, n2, m1 & m3 bias */ #define CHIPC_T2M2_BIAS 3 /* m2 bias */ #define CHIPC_T2MC_M1BYP 1 #define CHIPC_T2MC_M2BYP 2 #define CHIPC_T2MC_M3BYP 4 /* Type 6 Clock control magic field values */ #define CHIPC_T6_MMASK 1 /* bits of interest in m */ #define CHIPC_T6_M0 120000000 /* sb clock for m = 0 */ #define CHIPC_T6_M1 100000000 /* sb clock for m = 1 */ #define CHIPC_SB2MIPS_T6(sb) (2 * (sb)) /* Common clock base */ #define CHIPC_CLOCK_BASE1 24000000 /* Half the clock freq */ #define CHIPC_CLOCK_BASE2 12500000 /* Alternate crystal on some PLLs */ /* Clock control values for 200MHz in 5350 */ #define CHIPC_CLKC_5350_N 0x0311 #define CHIPC_CLKC_5350_M 0x04020009 /* Bits in the ExtBus config registers */ #define CHIPC_CFG_EN 0x0001 /* Enable */ #define CHIPC_CFG_EM_MASK 0x000e /* Extif Mode */ #define CHIPC_CFG_EM_ASYNC 0x0000 /* Async/Parallel flash */ #define CHIPC_CFG_EM_SYNC 0x0002 /* Synchronous */ #define CHIPC_CFG_EM_PCMCIA 0x0004 /* PCMCIA */ #define CHIPC_CFG_EM_IDE 0x0006 /* IDE */ #define CHIPC_FLASH_CFG_DS 0x0010 /* Data size, 0=8bit, 1=16bit */ #define CHIPC_FLASH_CFG_CD_MASK 0x00e0 /* Sync: Clock divisor, rev >= 20 */ #define CHIPC_FLASH_CFG_CE 0x0100 /* Sync: Clock enable, rev >= 20 */ #define CHIPC_FLASH_CFG_SB 0x0200 /* Sync: Size/Bytestrobe, rev >= 20 */ #define CHIPC_FLASH_CFG_IS 0x0400 /* Extif Sync Clk Select, rev >= 20 */ /* ExtBus address space */ #define CHIPC_EB_BASE 0x1a000000 /* Chipc ExtBus base address */ #define CHIPC_EB_PCMCIA_MEM 0x1a000000 /* PCMCIA 0 memory base address */ #define CHIPC_EB_PCMCIA_IO 0x1a200000 /* PCMCIA 0 I/O base address */ #define CHIPC_EB_PCMCIA_CFG 0x1a400000 /* PCMCIA 0 config base address */ #define CHIPC_EB_IDE 0x1a800000 /* IDE memory base */ #define CHIPC_EB_PCMCIA1_MEM 0x1a800000 /* PCMCIA 1 memory base address */ #define CHIPC_EB_PCMCIA1_IO 0x1aa00000 /* PCMCIA 1 I/O base address */ #define CHIPC_EB_PCMCIA1_CFG 0x1ac00000 /* PCMCIA 1 config base address */ #define CHIPC_EB_PROGIF 0x1b000000 /* ProgIF Async/Sync base address */ /* Start/busy bit in flashcontrol */ #define CHIPC_SFLASH_OPCODE 0x000000ff #define CHIPC_SFLASH_ACTION 0x00000700 #define CHIPC_SFLASH_CS_ACTIVE 0x00001000 /* Chip Select Active, rev >= 20 */ #define CHIPC_SFLASH_START 0x80000000 #define CHIPC_SFLASH_BUSY SFLASH_START /* flashcontrol action codes */ #define CHIPC_SFLASH_ACT_OPONLY 0x0000 /* Issue opcode only */ #define CHIPC_SFLASH_ACT_OP1D 0x0100 /* opcode + 1 data byte */ #define CHIPC_SFLASH_ACT_OP3A 0x0200 /* opcode + 3 addr bytes */ #define CHIPC_SFLASH_ACT_OP3A1D 0x0300 /* opcode + 3 addr & 1 data bytes */ #define CHIPC_SFLASH_ACT_OP3A4D 0x0400 /* opcode + 3 addr & 4 data bytes */ #define CHIPC_SFLASH_ACT_OP3A4X4D 0x0500 /* opcode + 3 addr, 4 don't care & 4 data bytes */ #define CHIPC_SFLASH_ACT_OP3A1X4D 0x0700 /* opcode + 3 addr, 1 don't care & 4 data bytes */ /* flashcontrol action+opcodes for ST flashes */ #define CHIPC_SFLASH_ST_WREN 0x0006 /* Write Enable */ #define CHIPC_SFLASH_ST_WRDIS 0x0004 /* Write Disable */ #define CHIPC_SFLASH_ST_RDSR 0x0105 /* Read Status Register */ #define CHIPC_SFLASH_ST_WRSR 0x0101 /* Write Status Register */ #define CHIPC_SFLASH_ST_READ 0x0303 /* Read Data Bytes */ #define CHIPC_SFLASH_ST_PP 0x0302 /* Page Program */ #define CHIPC_SFLASH_ST_SE 0x02d8 /* Sector Erase */ #define CHIPC_SFLASH_ST_BE 0x00c7 /* Bulk Erase */ #define CHIPC_SFLASH_ST_DP 0x00b9 /* Deep Power-down */ #define CHIPC_SFLASH_ST_RES 0x03ab /* Read Electronic Signature */ #define CHIPC_SFLASH_ST_CSA 0x1000 /* Keep chip select asserted */ #define CHIPC_SFLASH_ST_SSE 0x0220 /* Sub-sector Erase */ /* Status register bits for ST flashes */ #define CHIPC_SFLASH_ST_WIP 0x01 /* Write In Progress */ #define CHIPC_SFLASH_ST_WEL 0x02 /* Write Enable Latch */ #define CHIPC_SFLASH_ST_BP_MASK 0x1c /* Block Protect */ #define CHIPC_SFLASH_ST_BP_SHIFT 2 #define CHIPC_SFLASH_ST_SRWD 0x80 /* Status Register Write Disable */ /* flashcontrol action+opcodes for Atmel flashes */ #define CHIPC_SFLASH_AT_READ 0x07e8 #define CHIPC_SFLASH_AT_PAGE_READ 0x07d2 #define CHIPC_SFLASH_AT_BUF1_READ #define CHIPC_SFLASH_AT_BUF2_READ #define CHIPC_SFLASH_AT_STATUS 0x01d7 #define CHIPC_SFLASH_AT_BUF1_WRITE 0x0384 #define CHIPC_SFLASH_AT_BUF2_WRITE 0x0387 #define CHIPC_SFLASH_AT_BUF1_ERASE_PROGRAM 0x0283 #define CHIPC_SFLASH_AT_BUF2_ERASE_PROGRAM 0x0286 #define CHIPC_SFLASH_AT_BUF1_PROGRAM 0x0288 #define CHIPC_SFLASH_AT_BUF2_PROGRAM 0x0289 #define CHIPC_SFLASH_AT_PAGE_ERASE 0x0281 #define CHIPC_SFLASH_AT_BLOCK_ERASE 0x0250 #define CHIPC_SFLASH_AT_BUF1_WRITE_ERASE_PROGRAM 0x0382 #define CHIPC_SFLASH_AT_BUF2_WRITE_ERASE_PROGRAM 0x0385 #define CHIPC_SFLASH_AT_BUF1_LOAD 0x0253 #define CHIPC_SFLASH_AT_BUF2_LOAD 0x0255 #define CHIPC_SFLASH_AT_BUF1_COMPARE 0x0260 #define CHIPC_SFLASH_AT_BUF2_COMPARE 0x0261 #define CHIPC_SFLASH_AT_BUF1_REPROGRAM 0x0258 #define CHIPC_SFLASH_AT_BUF2_REPROGRAM 0x0259 /* Status register bits for Atmel flashes */ #define CHIPC_SFLASH_AT_READY 0x80 #define CHIPC_SFLASH_AT_MISMATCH 0x40 #define CHIPC_SFLASH_AT_ID_MASK 0x38 #define CHIPC_SFLASH_AT_ID_SHIFT 3 /* * These are the UART port assignments, expressed as offsets from the base * register. These assignments should hold for any serial port based on * a 8250, 16450, or 16550(A). */ #define CHIPC_UART_RX 0 /* In: Receive buffer (DLAB=0) */ #define CHIPC_UART_TX 0 /* Out: Transmit buffer (DLAB=0) */ #define CHIPC_UART_DLL 0 /* Out: Divisor Latch Low (DLAB=1) */ #define CHIPC_UART_IER 1 /* In/Out: Interrupt Enable Register (DLAB=0) */ #define CHIPC_UART_DLM 1 /* Out: Divisor Latch High (DLAB=1) */ #define CHIPC_UART_IIR 2 /* In: Interrupt Identity Register */ #define CHIPC_UART_FCR 2 /* Out: FIFO Control Register */ #define CHIPC_UART_LCR 3 /* Out: Line Control Register */ #define CHIPC_UART_MCR 4 /* Out: Modem Control Register */ #define CHIPC_UART_LSR 5 /* In: Line Status Register */ #define CHIPC_UART_MSR 6 /* In: Modem Status Register */ #define CHIPC_UART_SCR 7 /* I/O: Scratch Register */ #define CHIPC_UART_LCR_DLAB 0x80 /* Divisor latch access bit */ #define CHIPC_UART_LCR_WLEN8 0x03 /* Word length: 8 bits */ #define CHIPC_UART_MCR_OUT2 0x08 /* MCR GPIO out 2 */ #define CHIPC_UART_MCR_LOOP 0x10 /* Enable loopback test mode */ #define CHIPC_UART_LSR_RX_FIFO 0x80 /* Receive FIFO error */ #define CHIPC_UART_LSR_TDHR 0x40 /* Data-hold-register empty */ #define CHIPC_UART_LSR_THRE 0x20 /* Transmit-hold-register empty */ #define CHIPC_UART_LSR_BREAK 0x10 /* Break interrupt */ #define CHIPC_UART_LSR_FRAMING 0x08 /* Framing error */ #define CHIPC_UART_LSR_PARITY 0x04 /* Parity error */ #define CHIPC_UART_LSR_OVERRUN 0x02 /* Overrun error */ #define CHIPC_UART_LSR_RXRDY 0x01 /* Receiver ready */ #define CHIPC_UART_FCR_FIFO_ENABLE 1 /* FIFO control register bit controlling FIFO enable/disable */ /* Interrupt Identity Register (IIR) bits */ #define CHIPC_UART_IIR_FIFO_MASK 0xc0 /* IIR FIFO disable/enabled mask */ #define CHIPC_UART_IIR_INT_MASK 0xf /* IIR interrupt ID source */ #define CHIPC_UART_IIR_MDM_CHG 0x0 /* Modem status changed */ #define CHIPC_UART_IIR_NOINT 0x1 /* No interrupt pending */ #define CHIPC_UART_IIR_THRE 0x2 /* THR empty */ #define CHIPC_UART_IIR_RCVD_DATA 0x4 /* Received data available */ #define CHIPC_UART_IIR_RCVR_STATUS 0x6 /* Receiver status */ #define CHIPC_UART_IIR_CHAR_TIME 0xc /* Character time */ /* Interrupt Enable Register (IER) bits */ #define CHIPC_UART_IER_EDSSI 8 /* enable modem status interrupt */ #define CHIPC_UART_IER_ELSI 4 /* enable receiver line status interrupt */ #define CHIPC_UART_IER_ETBEI 2 /* enable transmitter holding register empty interrupt */ #define CHIPC_UART_IER_ERBFI 1 /* enable data available interrupt */ /* 4325 chip-specific ChipStatus register bits */ #define CHIPC_CST4325_SPROM_OTP_SEL_MASK CHIPC_CST_SPROM_OTP_SEL_R22_MASK #define CHIPC_CST4325_SPROM_OTP_SEL_SHIFT CHIPC_CST_SPROM_OTP_SEL_R22_SHIFT #define CHIPC_CST4325_SDIO_USB_MODE_MASK 0x00000004 #define CHIPC_CST4325_SDIO_USB_MODE_SHIFT 2 #define CHIPC_CST4325_RCAL_VALID_MASK 0x00000008 #define CHIPC_CST4325_RCAL_VALID_SHIFT 3 #define CHIPC_CST4325_RCAL_VALUE_MASK 0x000001f0 #define CHIPC_CST4325_RCAL_VALUE_SHIFT 4 #define CHIPC_CST4325_PMUTOP_2B_MASK 0x00000200 /* 1 for 2b, 0 for to 2a */ #define CHIPC_CST4325_PMUTOP_2B_SHIFT 9 /* 4329 chip-specific ChipStatus register bits */ #define CHIPC_CST4329_SPROM_OTP_SEL_MASK CHIPC_CST_SPROM_OTP_SEL_R22_MASK #define CHIPC_CST4329_SPROM_OTP_SEL_SHIFT CHIPC_CST_SPROM_OTP_SEL_R22_SHIFT #define CHIPC_CST4329_SPI_SDIO_MODE_MASK 0x00000004 #define CHIPC_CST4329_SPI_SDIO_MODE_SHIFT 2 /* 4312 chip-specific ChipStatus register bits */ #define CHIPC_CST4312_SPROM_OTP_SEL_MASK CHIPC_CST_SPROM_OTP_SEL_R22_MASK #define CHIPC_CST4312_SPROM_OTP_SEL_SHIFT CHIPC_CST_SPROM_OTP_SEL_R22_SHIFT /* 4322 chip-specific ChipStatus register bits */ #define CHIPC_CST4322_XTAL_FREQ_20_40MHZ 0x00000020 #define CHIPC_CST4322_SPROM_OTP_SEL_MASK CHIPC_CST_SPROM_OTP_SEL_R23_MASK #define CHIPC_CST4322_SPROM_OTP_SEL_SHIFT CHIPC_CST_SPROM_OTP_SEL_R23_SHIFT #define CHIPC_CST4322_PCI_OR_USB 0x00000100 #define CHIPC_CST4322_BOOT_MASK 0x00000600 #define CHIPC_CST4322_BOOT_SHIFT 9 #define CHIPC_CST4322_BOOT_FROM_SRAM 0 /* boot from SRAM, ARM in reset */ #define CHIPC_CST4322_BOOT_FROM_ROM 1 /* boot from ROM */ #define CHIPC_CST4322_BOOT_FROM_FLASH 2 /* boot from FLASH */ #define CHIPC_CST4322_BOOT_FROM_INVALID 3 #define CHIPC_CST4322_ILP_DIV_EN 0x00000800 #define CHIPC_CST4322_FLASH_TYPE_MASK 0x00001000 #define CHIPC_CST4322_FLASH_TYPE_SHIFT 12 #define CHIPC_CST4322_FLASH_TYPE_SHIFT_ST 0 /* ST serial FLASH */ #define CHIPC_CST4322_FLASH_TYPE_SHIFT_ATMEL 1 /* ATMEL flash */ #define CHIPC_CST4322_ARM_TAP_SEL 0x00002000 #define CHIPC_CST4322_RES_INIT_MODE_MASK 0x0000c000 #define CHIPC_CST4322_RES_INIT_MODE_SHIFT 14 #define CHIPC_CST4322_RES_INIT_MODE_ILPAVAIL 0 /* resinitmode: ILP available */ #define CHIPC_CST4322_RES_INIT_MODE_ILPREQ 1 /* resinitmode: ILP request */ #define CHIPC_CST4322_RES_INIT_MODE_ALPAVAIL 2 /* resinitmode: ALP available */ #define CHIPC_CST4322_RES_INIT_MODE_HTAVAIL 3 /* resinitmode: HT available */ #define CHIPC_CST4322_PCIPLLCLK_GATING 0x00010000 #define CHIPC_CST4322_CLK_SWITCH_PCI_TO_ALP 0x00020000 #define CHIPC_CST4322_PCI_CARDBUS_MODE 0x00040000 /* 43236 Chip specific ChipStatus register bits */ #define CHIPC_CST43236_SFLASH_MASK 0x00000040 #define CHIPC_CST43236_OTP_SEL_MASK 0x00000080 #define CHIPC_CST43236_OTP_SEL_SHIFT 7 #define CHIPC_CST43236_HSIC_MASK 0x00000100 /* USB/HSIC */ #define CHIPC_CST43236_BP_CLK 0x00000200 /* 120/96Mbps */ #define CHIPC_CST43236_BOOT_MASK 0x00001800 #define CHIPC_CST43236_BOOT_SHIFT 11 #define CHIPC_CST43236_BOOT_FROM_SRAM 0 /* boot from SRAM, ARM in reset */ #define CHIPC_CST43236_BOOT_FROM_ROM 1 /* boot from ROM */ #define CHIPC_CST43236_BOOT_FROM_FLASH 2 /* boot from FLASH */ #define CHIPC_CST43236_BOOT_FROM_INVALID 3 /* 43237 Chip specific ChipStatus register bits */ #define CHIPC_CST43237_BP_CLK 0x00000200 /* 96/80Mbps */ /* 4331 Chip specific ChipStatus register bits */ #define CHIPC_CST4331_XTAL_FREQ 0x00000001 /* crystal frequency 20/40Mhz */ #define CHIPC_CST4331_SPROM_PRESENT 0x00000002 #define CHIPC_CST4331_OTP_PRESENT 0x00000004 #define CHIPC_CST4331_LDO_RF 0x00000008 #define CHIPC_CST4331_LDO_PAR 0x00000010 /* 4331 chip-specific CHIPCTRL register bits */ #define CHIPC_CCTRL4331_BT_COEXIST (1<<0) /* 0 disable */ #define CHIPC_CCTRL4331_SECI (1<<1) /* 0 SECI is disabled (JATG functional) */ #define CHIPC_CCTRL4331_EXT_LNA (1<<2) /* 0 disable */ #define CHIPC_CCTRL4331_SPROM_GPIO13_15 (1<<3) /* sprom/gpio13-15 mux */ #define CHIPC_CCTRL4331_EXTPA_EN (1<<4) /* 0 ext pa disable, 1 ext pa enabled */ #define CHIPC_CCTRL4331_GPIOCLK_ON_SPROMCS (1<<5) /* set drive out GPIO_CLK on sprom_cs pin */ #define CHIPC_CCTRL4331_PCIE_MDIO_ON_SPROMCS (1<<6) /* use sprom_cs pin as PCIE mdio interface */ #define CHIPC_CCTRL4331_EXTPA_ON_GPIO2_5 (1<<7) /* aband extpa will be at gpio2/5 and sprom_dout */ #define CHIPC_CCTRL4331_OVR_PIPEAUXCLKEN (1<<8) /* override core control on pipe_AuxClkEnable */ #define CHIPC_CCTRL4331_OVR_PIPEAUXPWRDOWN (1<<9) /* override core control on pipe_AuxPowerDown */ #define CHIPC_CCTRL4331_PCIE_AUXCLKEN (1<<10) /* pcie_auxclkenable */ #define CHIPC_CCTRL4331_PCIE_PIPE_PLLDOWN (1<<11) /* pcie_pipe_pllpowerdown */ #define CHIPC_CCTRL4331_EXTPA_EN2 (1<<12) /* 0 ext pa2 disable, 1 ext pa2 enabled */ #define CHIPC_CCTRL4331_BT_SHD0_ON_GPIO4 (1<<16) /* enable bt_shd0 at gpio4 */ #define CHIPC_CCTRL4331_BT_SHD1_ON_GPIO5 (1<<17) /* enable bt_shd1 at gpio5 */ /* 4315 chip-specific ChipStatus register bits */ #define CHIPC_CST4315_SPROM_OTP_SEL_MASK CHIPC_CST_SPROM_OTP_SEL_R22_MASK #define CHIPC_CST4315_SPROM_OTP_SEL_SHIFT CHIPC_CST_SPROM_OTP_SEL_R22_SHIFT #define CHIPC_CST4315_SDIO_MODE 0x00000004 /* gpio [8], sdio/usb mode */ #define CHIPC_CST4315_RCAL_VALID 0x00000008 #define CHIPC_CST4315_RCAL_VALUE_MASK 0x000001f0 #define CHIPC_CST4315_RCAL_VALUE_SHIFT 4 #define CHIPC_CST4315_PALDO_EXTPNP 0x00000200 /* PALDO is configured with external PNP */ #define CHIPC_CST4315_CBUCK_MODE_MASK 0x00000c00 #define CHIPC_CST4315_CBUCK_MODE_BURST 0x00000400 #define CHIPC_CST4315_CBUCK_MODE_LPBURST 0x00000c00 /* 4319 chip-specific ChipStatus register bits */ #define CHIPC_CST4319_SPI_CPULESSUSB 0x00000001 #define CHIPC_CST4319_SPI_CLK_POL 0x00000002 #define CHIPC_CST4319_SPI_CLK_PH 0x00000008 #define CHIPC_CST4319_SPROM_OTP_SEL_MASK CHIPC_CST_SPROM_OTP_SEL_R23_MASK /* gpio [7:6], SDIO CIS selection */ #define CHIPC_CST4319_SPROM_OTP_SEL_SHIFT CHIPC_CST_SPROM_OTP_SEL_R23_SHIFT #define CHIPC_CST4319_SDIO_USB_MODE 0x00000100 /* gpio [8], sdio/usb mode */ #define CHIPC_CST4319_REMAP_SEL_MASK 0x00000600 #define CHIPC_CST4319_ILPDIV_EN 0x00000800 #define CHIPC_CST4319_XTAL_PD_POL 0x00001000 #define CHIPC_CST4319_LPO_SEL 0x00002000 #define CHIPC_CST4319_RES_INIT_MODE 0x0000c000 #define CHIPC_CST4319_PALDO_EXTPNP 0x00010000 /* PALDO is configured with external PNP */ #define CHIPC_CST4319_CBUCK_MODE_MASK 0x00060000 #define CHIPC_CST4319_CBUCK_MODE_BURST 0x00020000 #define CHIPC_CST4319_CBUCK_MODE_LPBURST 0x00060000 #define CHIPC_CST4319_RCAL_VALID 0x01000000 #define CHIPC_CST4319_RCAL_VALUE_MASK 0x3e000000 #define CHIPC_CST4319_RCAL_VALUE_SHIFT 25 /* 4336 chip-specific ChipStatus register bits */ #define CHIPC_CST4336_SPI_MODE_MASK 0x00000001 #define CHIPC_CST4336_SPROM_PRESENT 0x00000002 #define CHIPC_CST4336_OTP_PRESENT 0x00000004 #define CHIPC_CST4336_ARMREMAP_0 0x00000008 #define CHIPC_CST4336_ILPDIV_EN_MASK 0x00000010 #define CHIPC_CST4336_ILPDIV_EN_SHIFT 4 #define CHIPC_CST4336_XTAL_PD_POL_MASK 0x00000020 #define CHIPC_CST4336_XTAL_PD_POL_SHIFT 5 #define CHIPC_CST4336_LPO_SEL_MASK 0x00000040 #define CHIPC_CST4336_LPO_SEL_SHIFT 6 #define CHIPC_CST4336_RES_INIT_MODE_MASK 0x00000180 #define CHIPC_CST4336_RES_INIT_MODE_SHIFT 7 #define CHIPC_CST4336_CBUCK_MODE_MASK 0x00000600 #define CHIPC_CST4336_CBUCK_MODE_SHIFT 9 /* 4330 chip-specific ChipStatus register bits */ #define CHIPC_CST4330_CHIPMODE_SDIOD(cs) (((cs) & 0x7) < 6) /* SDIO || gSPI */ #define CHIPC_CST4330_CHIPMODE_USB20D(cs) (((cs) & 0x7) >= 6) /* USB || USBDA */ #define CHIPC_CST4330_CHIPMODE_SDIO(cs) (((cs) & 0x4) == 0) /* SDIO */ #define CHIPC_CST4330_CHIPMODE_GSPI(cs) (((cs) & 0x6) == 4) /* gSPI */ #define CHIPC_CST4330_CHIPMODE_USB(cs) (((cs) & 0x7) == 6) /* USB packet-oriented */ #define CHIPC_CST4330_CHIPMODE_USBDA(cs) (((cs) & 0x7) == 7) /* USB Direct Access */ #define CHIPC_CST4330_OTP_PRESENT 0x00000010 #define CHIPC_CST4330_LPO_AUTODET_EN 0x00000020 #define CHIPC_CST4330_ARMREMAP_0 0x00000040 #define CHIPC_CST4330_SPROM_PRESENT 0x00000080 /* takes priority over OTP if both set */ #define CHIPC_CST4330_ILPDIV_EN 0x00000100 #define CHIPC_CST4330_LPO_SEL 0x00000200 #define CHIPC_CST4330_RES_INIT_MODE_SHIFT 10 #define CHIPC_CST4330_RES_INIT_MODE_MASK 0x00000c00 #define CHIPC_CST4330_CBUCK_MODE_SHIFT 12 #define CHIPC_CST4330_CBUCK_MODE_MASK 0x00003000 #define CHIPC_CST4330_CBUCK_POWER_OK 0x00004000 #define CHIPC_CST4330_BB_PLL_LOCKED 0x00008000 #define CHIPC_SOCDEVRAM_4330_BP_ADDR 0x1E000000 #define CHIPC_SOCDEVRAM_4330_ARM_ADDR 0x00800000 /* 4313 chip-specific ChipStatus register bits */ #define CHIPC_CST4313_SPROM_PRESENT 1 #define CHIPC_CST4313_OTP_PRESENT 2 #define CHIPC_CST4313_SPROM_OTP_SEL_MASK 0x00000002 #define CHIPC_CST4313_SPROM_OTP_SEL_SHIFT 0 /* 43228 chipstatus reg bits */ #define CHIPC_CST43228_ILP_DIV_EN 0x1 #define CHIPC_CST43228_OTP_PRESENT 0x2 #define CHIPC_CST43228_SERDES_REFCLK_PADSEL 0x4 #define CHIPC_CST43228_SDIO_MODE 0x8 #define CHIPC_CST43228_SDIO_OTP_PRESENT 0x10 #define CHIPC_CST43228_SDIO_RESET 0x20 /* * Register eci_inputlo bitfield values. * - BT packet type information bits [7:0] */ /* [3:0] - Task (link) type */ #define CHIPC_BT_ACL 0x00 #define CHIPC_BT_SCO 0x01 #define CHIPC_BT_eSCO 0x02 #define CHIPC_BT_A2DP 0x03 #define CHIPC_BT_SNIFF 0x04 #define CHIPC_BT_PAGE_SCAN 0x05 #define CHIPC_BT_INQUIRY_SCAN 0x06 #define CHIPC_BT_PAGE 0x07 #define CHIPC_BT_INQUIRY 0x08 #define CHIPC_BT_MSS 0x09 #define CHIPC_BT_PARK 0x0a #define CHIPC_BT_RSSISCAN 0x0b #define CHIPC_BT_MD_ACL 0x0c #define CHIPC_BT_MD_eSCO 0x0d #define CHIPC_BT_SCAN_WITH_SCO_LINK 0x0e #define CHIPC_BT_SCAN_WITHOUT_SCO_LINK 0x0f /* [7:4] = packet duration code */ /* [8] - Master / Slave */ #define CHIPC_BT_MASTER 0 #define CHIPC_BT_SLAVE 1 /* [11:9] - multi-level priority */ #define CHIPC_BT_LOWEST_PRIO 0x0 #define CHIPC_BT_HIGHEST_PRIO 0x3 #endif /* _BHND_CORES_CHIPC_CHIPCREG_H_ */ Index: head/sys/dev/bhnd/siba/siba.c =================================================================== --- head/sys/dev/bhnd/siba/siba.c (revision 305370) +++ head/sys/dev/bhnd/siba/siba.c (revision 305371) @@ -1,695 +1,649 @@ /*- * Copyright (c) 2015 Landon Fuller * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #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) { device_set_desc(dev, "SIBA BHND bus"); 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 siba_detach(device_t dev) { return (bhnd_generic_detach(dev)); } int siba_resume(device_t dev) { return (bhnd_generic_resume(dev)); } int siba_suspend(device_t dev) { return (bhnd_generic_suspend(dev)); } static int siba_read_ivar(device_t dev, device_t child, int index, uintptr_t *result) { const struct siba_devinfo *dinfo; const struct bhnd_core_info *cfg; dinfo = device_get_ivars(child); cfg = &dinfo->core_id.core_info; switch (index) { case BHND_IVAR_VENDOR: *result = cfg->vendor; return (0); case BHND_IVAR_DEVICE: *result = cfg->device; return (0); case BHND_IVAR_HWREV: *result = cfg->hwrev; return (0); case BHND_IVAR_DEVICE_CLASS: *result = bhnd_core_class(cfg); return (0); case BHND_IVAR_VENDOR_NAME: *result = (uintptr_t) bhnd_vendor_name(cfg->vendor); return (0); case BHND_IVAR_DEVICE_NAME: *result = (uintptr_t) bhnd_core_name(cfg); return (0); case BHND_IVAR_CORE_INDEX: *result = cfg->core_idx; return (0); case BHND_IVAR_CORE_UNIT: *result = cfg->unit; return (0); default: return (ENOENT); } } static int siba_write_ivar(device_t dev, device_t child, int index, uintptr_t value) { switch (index) { case BHND_IVAR_VENDOR: case BHND_IVAR_DEVICE: case BHND_IVAR_HWREV: case BHND_IVAR_DEVICE_CLASS: case BHND_IVAR_VENDOR_NAME: case BHND_IVAR_DEVICE_NAME: case BHND_IVAR_CORE_INDEX: case BHND_IVAR_CORE_UNIT: return (EINVAL); default: return (ENOENT); } } static struct resource_list * siba_get_resource_list(device_t dev, device_t child) { struct siba_devinfo *dinfo = device_get_ivars(child); 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) { struct siba_devinfo *dinfo; if (device_get_parent(child) != dev) BHND_BUS_RESET_CORE(device_get_parent(dev), child, flags); dinfo = device_get_ivars(child); /* Can't reset the core without access to the CFG0 registers */ if (dinfo->cfg[0] == NULL) return (ENODEV); // TODO - perform reset return (ENXIO); } static int siba_suspend_core(device_t dev, device_t child) { struct siba_devinfo *dinfo; if (device_get_parent(child) != dev) BHND_BUS_SUSPEND_CORE(device_get_parent(dev), child); dinfo = device_get_ivars(child); /* Can't suspend the core without access to the CFG0 registers */ if (dinfo->cfg[0] == NULL) return (ENODEV); // TODO - perform suspend return (ENXIO); } static uint32_t siba_read_config(device_t dev, device_t child, bus_size_t offset, u_int width) { struct siba_devinfo *dinfo; rman_res_t r_size; /* Must be directly attached */ if (device_get_parent(child) != dev) return (UINT32_MAX); /* CFG0 registers must be available */ dinfo = device_get_ivars(child); if (dinfo->cfg[0] == NULL) return (UINT32_MAX); /* Offset must fall within CFG0 */ r_size = rman_get_size(dinfo->cfg[0]->res); if (r_size < offset || r_size - offset < width) return (UINT32_MAX); switch (width) { case 1: return (bhnd_bus_read_1(dinfo->cfg[0], offset)); case 2: return (bhnd_bus_read_2(dinfo->cfg[0], offset)); case 4: return (bhnd_bus_read_4(dinfo->cfg[0], offset)); } /* Unsuported */ return (UINT32_MAX); } static void siba_write_config(device_t dev, device_t child, bus_size_t offset, uint32_t val, u_int width) { struct siba_devinfo *dinfo; rman_res_t r_size; /* Must be directly attached */ if (device_get_parent(child) != dev) return; /* CFG0 registers must be available */ dinfo = device_get_ivars(child); if (dinfo->cfg[0] == NULL) return; /* Offset must fall within CFG0 */ r_size = rman_get_size(dinfo->cfg[0]->res); if (r_size < offset || r_size - offset < width) return; switch (width) { case 1: bhnd_bus_write_1(dinfo->cfg[0], offset, val); case 2: bhnd_bus_write_2(dinfo->cfg[0], offset, val); case 4: bhnd_bus_write_4(dinfo->cfg[0], offset, val); } } static u_int siba_get_port_count(device_t dev, device_t child, bhnd_port_type type) { struct siba_devinfo *dinfo; /* delegate non-bus-attached devices to our parent */ if (device_get_parent(child) != dev) return (BHND_BUS_GET_PORT_COUNT(device_get_parent(dev), child, type)); dinfo = device_get_ivars(child); return (siba_addrspace_port_count(dinfo->core_id.num_addrspace)); } static u_int siba_get_region_count(device_t dev, device_t child, bhnd_port_type type, u_int port) { struct siba_devinfo *dinfo; /* delegate non-bus-attached devices to our parent */ if (device_get_parent(child) != dev) return (BHND_BUS_GET_REGION_COUNT(device_get_parent(dev), child, type, port)); dinfo = device_get_ivars(child); if (!siba_is_port_valid(dinfo->core_id.num_addrspace, type, port)) return (0); return (siba_addrspace_region_count(dinfo->core_id.num_addrspace, port)); } static int siba_get_port_rid(device_t dev, device_t child, bhnd_port_type port_type, u_int port_num, u_int region_num) { struct siba_devinfo *dinfo; struct siba_addrspace *addrspace; /* delegate non-bus-attached devices to our parent */ if (device_get_parent(child) != dev) return (BHND_BUS_GET_PORT_RID(device_get_parent(dev), child, port_type, port_num, region_num)); dinfo = device_get_ivars(child); addrspace = siba_find_addrspace(dinfo, port_type, port_num, region_num); if (addrspace == NULL) return (-1); return (addrspace->sa_rid); } static int siba_decode_port_rid(device_t dev, device_t child, int type, int rid, bhnd_port_type *port_type, u_int *port_num, u_int *region_num) { struct siba_devinfo *dinfo; /* delegate non-bus-attached devices to our parent */ if (device_get_parent(child) != dev) return (BHND_BUS_DECODE_PORT_RID(device_get_parent(dev), child, type, rid, port_type, port_num, region_num)); dinfo = device_get_ivars(child); /* Ports are always memory mapped */ if (type != SYS_RES_MEMORY) return (EINVAL); for (int i = 0; i < dinfo->core_id.num_addrspace; i++) { if (dinfo->addrspace[i].sa_rid != rid) continue; *port_type = BHND_PORT_DEVICE; *port_num = siba_addrspace_port(i); *region_num = siba_addrspace_region(i); return (0); } /* Not found */ return (ENOENT); } static int siba_get_region_addr(device_t dev, device_t child, bhnd_port_type port_type, u_int port_num, u_int region_num, bhnd_addr_t *addr, bhnd_size_t *size) { struct siba_devinfo *dinfo; struct siba_addrspace *addrspace; /* delegate non-bus-attached devices to our parent */ if (device_get_parent(child) != dev) { return (BHND_BUS_GET_REGION_ADDR(device_get_parent(dev), child, port_type, port_num, region_num, addr, size)); } dinfo = device_get_ivars(child); addrspace = siba_find_addrspace(dinfo, port_type, port_num, region_num); if (addrspace == NULL) return (ENOENT); *addr = addrspace->sa_base; *size = addrspace->sa_size - addrspace->sa_bus_reserved; return (0); } /** * Register all address space mappings for @p di. * * @param dev The siba bus device. * @param di The device info instance on which to register all address * space entries. * @param r A resource mapping the enumeration table block for @p di. */ static int siba_register_addrspaces(device_t dev, struct siba_devinfo *di, struct bhnd_resource *r) { struct siba_core_id *cid; uint32_t addr; uint32_t size; int error; cid = &di->core_id; /* Register the device address space entries */ for (uint8_t i = 0; i < di->core_id.num_addrspace; i++) { uint32_t adm; u_int adm_offset; uint32_t bus_reserved; /* Determine the register offset */ adm_offset = siba_admatch_offset(i); if (adm_offset == 0) { device_printf(dev, "addrspace %hhu is unsupported", i); return (ENODEV); } /* Fetch the address match register value */ adm = bhnd_bus_read_4(r, adm_offset); /* Parse the value */ if ((error = siba_parse_admatch(adm, &addr, &size))) { device_printf(dev, "failed to decode address " " match register value 0x%x\n", adm); return (error); } /* If this is the device's core/enumeration addrespace, * reserve the Sonics configuration register blocks for the * use of our bus. */ bus_reserved = 0; if (i == SIBA_CORE_ADDRSPACE) bus_reserved = cid->num_cfg_blocks * SIBA_CFG_SIZE; /* Append the region info */ error = siba_append_dinfo_region(di, i, addr, size, bus_reserved); if (error) return (error); } 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) { struct siba_devinfo *dinfo = siba_alloc_dinfo(dev); return ((struct bhnd_devinfo *)dinfo); } static void siba_free_bhnd_dinfo(device_t dev, struct bhnd_devinfo *dinfo) { siba_free_dinfo(dev, (struct siba_devinfo *)dinfo); } /** * Scan the core table and add all valid discovered cores to * 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); + chipid = BHND_BUS_GET_CHIPID(dev, dev); - /* 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); - } - /* Allocate our temporary core table and enumerate all cores */ cores = malloc(sizeof(*cores) * chipid->ncores, M_BHND, M_NOWAIT); if (cores == NULL) return (ENOMEM); /* Add all cores. */ for (u_int i = 0; i < chipid->ncores; i++) { struct siba_core_id cid; device_t child; uint32_t idhigh, idlow; rman_res_t r_count, r_end, r_start; /* Map the core's register block */ rid = 0; r_start = SIBA_CORE_ADDR(i); r_count = SIBA_CORE_SIZE; r_end = r_start + SIBA_CORE_SIZE - 1; r = bhnd_alloc_resource(dev, SYS_RES_MEMORY, &rid, r_start, r_end, r_count, RF_ACTIVE); if (r == NULL) { error = ENXIO; goto cleanup; } /* Add the child device */ child = BUS_ADD_CHILD(dev, 0, NULL, -1); if (child == NULL) { error = ENXIO; goto cleanup; } /* Read the core info */ idhigh = bhnd_bus_read_4(r, SB0_REG_ABS(SIBA_CFG0_IDHIGH)); idlow = bhnd_bus_read_4(r, SB0_REG_ABS(SIBA_CFG0_IDLOW)); cid = siba_parse_core_id(idhigh, idlow, i, 0); cores[i] = cid.core_info; /* Determine unit number */ for (u_int j = 0; j < i; j++) { if (cores[j].vendor == cores[i].vendor && cores[j].device == cores[i].device) cores[i].unit++; } /* Initialize per-device bus info */ if ((dinfo = device_get_ivars(child)) == NULL) { error = ENXIO; goto cleanup; } if ((error = siba_init_dinfo(dev, dinfo, &cid))) goto cleanup; /* Register the core's address space(s). */ 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); } cleanup: if (cores != NULL) free(cores, M_BHND); if (r != NULL) bhnd_release_resource(dev, SYS_RES_MEMORY, rid, r); return (error); } static device_method_t siba_methods[] = { /* Device interface */ DEVMETHOD(device_probe, siba_probe), DEVMETHOD(device_attach, siba_attach), DEVMETHOD(device_detach, siba_detach), DEVMETHOD(device_resume, siba_resume), DEVMETHOD(device_suspend, siba_suspend), /* Bus interface */ DEVMETHOD(bus_read_ivar, siba_read_ivar), DEVMETHOD(bus_write_ivar, siba_write_ivar), 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), DEVMETHOD(bhnd_bus_suspend_core, siba_suspend_core), DEVMETHOD(bhnd_bus_read_config, siba_read_config), DEVMETHOD(bhnd_bus_write_config, siba_write_config), DEVMETHOD(bhnd_bus_get_port_count, siba_get_port_count), DEVMETHOD(bhnd_bus_get_region_count, siba_get_region_count), DEVMETHOD(bhnd_bus_get_port_rid, siba_get_port_rid), DEVMETHOD(bhnd_bus_decode_port_rid, siba_decode_port_rid), DEVMETHOD(bhnd_bus_get_region_addr, siba_get_region_addr), DEVMETHOD_END }; DEFINE_CLASS_1(bhnd, siba_driver, siba_methods, sizeof(struct siba_softc), bhnd_driver); MODULE_VERSION(siba, 1); MODULE_DEPEND(siba, bhnd, 1, 1, 1); Index: head/sys/dev/bhnd/siba/siba_bhndb.c =================================================================== --- head/sys/dev/bhnd/siba/siba_bhndb.c (revision 305370) +++ head/sys/dev/bhnd/siba/siba_bhndb.c (revision 305371) @@ -1,295 +1,296 @@ /*- * Copyright (c) 2015 Landon Fuller * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include "sibareg.h" #include "sibavar.h" /* * Supports attachment of siba(4) bus devices via a bhndb bridge. */ // // TODO: PCI rev < 6 interrupt handling // // On early PCI cores (rev < 6) interrupt masking is handled via interconnect // configuration registers (SBINTVEC), rather than the PCI_INT_MASK // config register. // // On those devices, we should handle interrupts locally using SBINTVEC, rather // than delegating to our parent bhndb device. // static int siba_bhndb_wars_hwup(struct siba_softc *sc); /* Bridge-specific core device quirks */ enum { /** When PCIe-bridged, the D11 core's initiator request * timeout must be disabled to prevent D11 from entering a * RESP_TIMEOUT error state. */ SIBA_QUIRK_PCIE_D11_SB_TIMEOUT = (1<<0) }; static struct bhnd_device_quirk bridge_quirks[] = { BHND_CHIP_QUIRK(4311, HWREV_EQ(2), SIBA_QUIRK_PCIE_D11_SB_TIMEOUT), BHND_CHIP_QUIRK(4312, HWREV_EQ(0), SIBA_QUIRK_PCIE_D11_SB_TIMEOUT), }; static struct bhnd_device bridge_devs[] = { BHND_DEVICE(BCM, PCI, NULL, bridge_quirks), }; static int siba_bhndb_probe(device_t dev) { const struct bhnd_chipid *cid; int error; /* Defer to default probe implementation */ if ((error = siba_probe(dev)) > 0) return (error); /* Check bus type */ cid = BHNDB_GET_CHIPID(device_get_parent(dev), dev); if (cid->chip_type != BHND_CHIPTYPE_SIBA) return (ENXIO); /* Set device description */ bhnd_set_default_bus_desc(dev, cid); return (error); } 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 siba_bhndb_resume(device_t dev) { struct siba_softc *sc; int error; sc = device_get_softc(dev); /* Apply attach/resume work-arounds */ if ((error = siba_bhndb_wars_hwup(sc))) return (error); /* Call our superclass' implementation */ return (siba_resume(dev)); } /* Suspend all references to the device's cfg register blocks */ static void siba_bhndb_suspend_cfgblocks(device_t dev, struct siba_devinfo *dinfo) { for (u_int i = 0; i < dinfo->core_id.num_cfg_blocks; i++) { if (dinfo->cfg[i] == NULL) continue; BHNDB_SUSPEND_RESOURCE(device_get_parent(dev), dev, SYS_RES_MEMORY, dinfo->cfg[i]->res); } } static int siba_bhndb_suspend_child(device_t dev, device_t child) { struct siba_devinfo *dinfo; int error; if (device_get_parent(child) != dev) BUS_SUSPEND_CHILD(device_get_parent(dev), child); dinfo = device_get_ivars(child); /* Suspend the child */ if ((error = bhnd_generic_br_suspend_child(dev, child))) return (error); /* Suspend resource references to the child's config registers */ siba_bhndb_suspend_cfgblocks(dev, dinfo); return (0); } static int siba_bhndb_resume_child(device_t dev, device_t child) { struct siba_devinfo *dinfo; int error; if (device_get_parent(child) != dev) BUS_SUSPEND_CHILD(device_get_parent(dev), child); if (!device_is_suspended(child)) return (EBUSY); dinfo = device_get_ivars(child); /* Resume all resource references to the child's config registers */ for (u_int i = 0; i < dinfo->core_id.num_cfg_blocks; i++) { if (dinfo->cfg[i] == NULL) continue; error = BHNDB_RESUME_RESOURCE(device_get_parent(dev), dev, SYS_RES_MEMORY, dinfo->cfg[i]->res); if (error) { siba_bhndb_suspend_cfgblocks(dev, dinfo); return (error); } } /* Resume the child */ if ((error = bhnd_generic_br_resume_child(dev, child))) { siba_bhndb_suspend_cfgblocks(dev, dinfo); return (error); } return (0); } /* Work-around implementation for SIBA_QUIRK_PCIE_D11_SB_TIMEOUT */ static int 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 */ d11 = bhnd_match_child(sc->dev, &(struct bhnd_core_match) { BHND_MATCH_CORE(BHND_MFGID_BCM, BHND_COREID_D11), BHND_MATCH_CORE_UNIT(0) }); if (d11 == NULL) return (0); /* Clear initiator timeout in D11's CFG0 block */ dinfo = device_get_ivars(d11); KASSERT(dinfo->cfg[0] != NULL, ("missing core config mapping")); imcfg = bhnd_bus_read_4(dinfo->cfg[0], SIBA_CFG0_IMCONFIGLOW); imcfg &= ~SIBA_IMCL_RTO_MASK; bhnd_bus_write_4(dinfo->cfg[0], SIBA_CFG0_IMCONFIGLOW, imcfg); return (0); } /** * Apply any hardware workarounds that are required upon attach or resume * of the bus. */ 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) { if ((error = siba_bhndb_wars_pcie_clear_d11_timeout(sc))) return (error); } return (0); } static device_method_t siba_bhndb_methods[] = { /* Device interface */ DEVMETHOD(device_probe, siba_bhndb_probe), DEVMETHOD(device_attach, siba_bhndb_attach), DEVMETHOD(device_resume, siba_bhndb_resume), /* Bus interface */ DEVMETHOD(bus_suspend_child, siba_bhndb_suspend_child), DEVMETHOD(bus_resume_child, siba_bhndb_resume_child), DEVMETHOD_END }; DEFINE_CLASS_2(bhnd, siba_bhndb_driver, siba_bhndb_methods, sizeof(struct siba_softc), bhnd_bhndb_driver, siba_driver); DRIVER_MODULE(siba_bhndb, bhndb, siba_bhndb_driver, bhnd_devclass, NULL, NULL); MODULE_VERSION(siba_bhndb, 1); MODULE_DEPEND(siba_bhndb, siba, 1, 1, 1); MODULE_DEPEND(siba_bhndb, bhnd, 1, 1, 1); MODULE_DEPEND(siba_bhndb, bhndb, 1, 1, 1); Index: head/sys/dev/bhnd/siba/siba_erom.c =================================================================== --- head/sys/dev/bhnd/siba/siba_erom.c (revision 305370) +++ head/sys/dev/bhnd/siba/siba_erom.c (revision 305371) @@ -1,420 +1,536 @@ /*- * Copyright (c) 2015-2016 Landon Fuller * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include "sibareg.h" #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); +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); -struct siba_erom { - struct bhnd_erom obj; +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 */ }; -#define EROM_LOG(sc, fmt, ...) do { \ - if (sc->dev != NULL) { \ - device_printf(sc->dev, "%s: " fmt, __FUNCTION__, \ +/** + * SIBA EROM per-instance state. + */ +struct siba_erom { + struct bhnd_erom obj; + struct siba_erom_io io; /**< i/o context */ +}; + +#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; + int error; - /* 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); - } + /* Try using the provided hint. */ + if (hint != NULL) { + struct siba_core_id sid; - /* Identify the chipset */ - idreg = siba_erom_read_4(sc, 0, CHIPC_ID); - *cid = bhnd_parse_chipid(idreg, enum_addr); + /* Validate bus type */ + if (hint->chip_type != BHND_CHIPTYPE_SIBA) + return (ENXIO); - /* Fix up the core count in-place */ - return (bhnd_chipid_fixed_ncores(cid, ccid.core_info.hwrev, - &cid->ncores)); -} + /* + * 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). + */ -static int -siba_erom_init_common(struct siba_erom *sc) -{ - struct bhnd_chipid cid; - int error; + sid = siba_eio_read_core_id(io, 0, 0); - /* There's always at least one core */ - sc->ncores = 1; + if (sid.core_info.vendor != BHND_MFGID_BCM) + return (ENXIO); - /* Identify the chipset */ - if ((error = siba_erom_read_chipid(sc, SIBA_ENUM_ADDR, &cid))) - return (error); + if (sid.core_info.device == BHND_COREID_CC) + return (EINVAL); - /* Verify the chip type */ - if (cid.chip_type != BHND_CHIPTYPE_SIBA) - return (ENXIO); + *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 * without triggering build failure due to -Wtype-limits * * 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; + struct siba_erom *sc; + + sc = (struct siba_erom *)erom; - sc->dev = NULL; - sc->rid = -1; - sc->res = NULL; - sc->bst = bst; - sc->bsh = bsh; - - 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->res = NULL; - sc->rid = -1; + 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)); + + 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 siba_erom_lookup_core(bhnd_erom_t *erom, const struct bhnd_core_match *desc, struct bhnd_core_info *core) { struct siba_erom *sc; struct bhnd_core_match imatch; sc = (struct siba_erom *)erom; /* We can't determine a core's unit number during the initial scan. */ imatch = *desc; 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 */ if (!bhnd_core_matches(&ci, &imatch)) continue; /* 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 && sid.core_info.device == ci.device) ci.unit++; } /* Check for full match against now-valid unit number */ if (!bhnd_core_matches(&ci, desc)) continue; /* Matching core found */ *core = ci; return (0); } /* Not found */ return (ENOENT); } static int siba_erom_lookup_core_addr(bhnd_erom_t *erom, const struct bhnd_core_match *desc, bhnd_port_type type, u_int port, u_int region, struct bhnd_core_info *info, bhnd_addr_t *addr, bhnd_size_t *size) { struct siba_erom *sc; struct bhnd_core_info core; struct siba_core_id sid; uint32_t am, am_addr, am_size; u_int am_offset; u_int addrspace; int error; sc = (struct siba_erom *)erom; /* Locate the requested core */ if ((error = siba_erom_lookup_core(erom, desc, &core))) 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)) return (ENOENT); /* Is region valid? */ if (region >= siba_addrspace_region_count(sid.num_addrspace, port)) return (ENOENT); /* Map the bhnd port values to a siba addrspace index */ error = siba_addrspace_index(sid.num_addrspace, type, port, region, &addrspace); if (error) return (error); /* Determine the register offset */ am_offset = siba_admatch_offset(addrspace); if (am_offset == 0) { printf("addrspace %u is unsupported", addrspace); return (ENODEV); } /* 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", am); return (error); } if (info != NULL) *info = core; *addr = am_addr; *size = am_size; return (0); } /* BHND_EROM_GET_CORE_TABLE() */ static int siba_erom_get_core_table(bhnd_erom_t *erom, struct bhnd_core_info **cores, u_int *num_cores) { struct siba_erom *sc; struct bhnd_core_info *out; 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 */ for (u_int j = 0; j < i; j++) { if (out[j].vendor == out[i].vendor && out[j].device == out[i].device) out[i].unit++; } } return (0); } /* BHND_EROM_FREE_CORE_TABLE() */ static void siba_erom_free_core_table(bhnd_erom_t *erom, struct bhnd_core_info *cores) { free(cores, M_BHND); } 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), KOBJMETHOD(bhnd_erom_fini, siba_erom_fini), KOBJMETHOD(bhnd_erom_get_core_table, siba_erom_get_core_table), KOBJMETHOD(bhnd_erom_free_core_table, siba_erom_free_core_table), KOBJMETHOD(bhnd_erom_lookup_core, siba_erom_lookup_core), KOBJMETHOD(bhnd_erom_lookup_core_addr, siba_erom_lookup_core_addr), KOBJMETHOD_END }; BHND_EROM_DEFINE_CLASS(siba_erom, siba_erom_parser, siba_erom_methods, sizeof(struct siba_erom)); Index: head/sys/dev/bhnd/siba/siba_nexus.c =================================================================== --- head/sys/dev/bhnd/siba/siba_nexus.c (revision 305370) +++ head/sys/dev/bhnd/siba/siba_nexus.c (revision 305371) @@ -1,122 +1,125 @@ /*- * Copyright (c) 2015-2016 Landon Fuller * Copyright (c) 2007 Bruce M. Simpson. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include "sibavar.h" /* * Supports siba(4) attachment to a MIPS nexus bus. * * Derived from Bruce M. Simpson' original siba(4) driver. */ struct siba_nexus_softc { struct siba_softc parent_sc; struct bhnd_chipid siba_cid; }; static int siba_nexus_probe(device_t dev) { struct siba_nexus_softc *sc; int error; sc = device_get_softc(dev); /* Read the ChipCommon info using the hints the kernel * was compiled with. */ if ((error = bhnd_nexus_read_chipid(dev, &sc->siba_cid))) return (error); if (sc->siba_cid.chip_type != BHND_CHIPTYPE_SIBA) return (ENXIO); if ((error = siba_probe(dev)) > 0) { device_printf(dev, "error %d in probe\n", error); return (error); } /* Set device description */ bhnd_set_default_bus_desc(dev, &sc->siba_cid); return (0); } 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; - /* Enumerate the bus. */ - if ((error = siba_add_children(dev, NULL))) { - device_printf(dev, "error %d enumerating children\n", error); - return (error); - } + /* Delegate remainder to standard bhnd method implementation */ + if ((error = bhnd_generic_attach(dev))) + goto failed; - return (siba_attach(dev)); + return (0); + +failed: + device_delete_children(dev); + return (error); } static const struct bhnd_chipid * siba_nexus_get_chipid(device_t dev, device_t child) { struct siba_nexus_softc *sc = device_get_softc(dev); return (&sc->siba_cid); } static device_method_t siba_nexus_methods[] = { /* Device interface */ DEVMETHOD(device_probe, siba_nexus_probe), DEVMETHOD(device_attach, siba_nexus_attach), /* bhnd interface */ DEVMETHOD(bhnd_bus_get_chipid, siba_nexus_get_chipid), DEVMETHOD_END }; DEFINE_CLASS_2(bhnd, siba_nexus_driver, siba_nexus_methods, sizeof(struct siba_nexus_softc), bhnd_nexus_driver, siba_driver); EARLY_DRIVER_MODULE(siba_nexus, nexus, siba_nexus_driver, bhnd_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); Index: head/sys/dev/bhnd/siba/sibavar.h =================================================================== --- head/sys/dev/bhnd/siba/sibavar.h (revision 305370) +++ head/sys/dev/bhnd/siba/sibavar.h (revision 305371) @@ -1,161 +1,164 @@ /*- * Copyright (c) 2015 Landon Fuller * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. * * $FreeBSD$ */ #ifndef _SIBA_SIBAVAR_H_ #define _SIBA_SIBAVAR_H_ #include #include #include #include #include #include "siba.h" /* * Internal definitions shared by siba(4) driver implementations. */ struct siba_addrspace; struct siba_devinfo; struct siba_core_id; int siba_probe(device_t dev); int siba_attach(device_t dev); int siba_detach(device_t dev); int siba_resume(device_t dev); int siba_suspend(device_t dev); uint16_t siba_get_bhnd_mfgid(uint16_t ocp_vendor); 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, struct siba_devinfo *dinfo, const struct siba_core_id *core_id); void siba_free_dinfo(device_t dev, struct siba_devinfo *dinfo); u_int siba_addrspace_port_count(u_int num_addrspace); u_int siba_addrspace_region_count(u_int num_addrspace, u_int port); u_int siba_addrspace_port(u_int addrspace); u_int siba_addrspace_region(u_int addrspace); int siba_addrspace_index(u_int num_addrspace, bhnd_port_type type, u_int port, u_int region, u_int *addridx); bool siba_is_port_valid(u_int num_addrspace, bhnd_port_type type, u_int port); struct siba_addrspace *siba_find_addrspace(struct siba_devinfo *dinfo, bhnd_port_type type, u_int port, u_int region); int siba_append_dinfo_region(struct siba_devinfo *dinfo, uint8_t sid, uint32_t base, uint32_t size, uint32_t bus_reserved); u_int siba_admatch_offset(uint8_t addrspace); int siba_parse_admatch(uint32_t am, uint32_t *addr, uint32_t *size); /* Sonics configuration register blocks */ #define SIBA_CFG_NUM_2_2 1 /**< sonics <= 2.2 maps SIBA_CFG0. */ #define SIBA_CFG_NUM_2_3 2 /**< sonics <= 2.3 maps SIBA_CFG0 and SIBA_CFG1 */ #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 */ #define SIBA_MAX_ADDRSPACE 4 /**< Maximum number of Sonics/OCP * address space mappings for a * single core. */ /* bhnd(4) (port,region) representation of siba address space mappings */ #define SIBA_MAX_PORT 2 /**< maximum number of advertised * bhnd(4) ports */ /** siba(4) address space descriptor */ struct siba_addrspace { uint32_t sa_base; /**< base address */ uint32_t sa_size; /**< size */ int sa_rid; /**< bus resource id */ uint32_t sa_bus_reserved;/**< number of bytes at high end of * address space reserved for the bus */ }; /** * siba(4) per-core identification info. */ struct siba_core_id { struct bhnd_core_info core_info; /**< standard bhnd(4) core info */ uint16_t sonics_vendor; /**< OCP vendor identifier used to generate * the JEDEC-106 bhnd(4) vendor identifier. */ uint8_t sonics_rev; /**< sonics backplane revision code */ uint8_t num_addrspace; /**< number of address ranges mapped to this core. */ uint8_t num_cfg_blocks; /**< number of Sonics configuration register blocks mapped to the core's enumeration space */ }; /** * siba(4) per-device info */ struct siba_devinfo { struct bhnd_devinfo bhnd_dinfo; /**< superclass device info. */ struct resource_list resources; /**< per-core memory regions. */ struct siba_core_id core_id; /**< core identification info */ struct siba_addrspace addrspace[SIBA_MAX_ADDRSPACE]; /**< memory map descriptors */ struct bhnd_resource *cfg[SIBA_MAX_CFG]; /**< SIBA_CFG_* registers */ int cfg_rid[SIBA_MAX_CFG]; /**< SIBA_CFG_* resource IDs */ }; /** siba(4) per-instance state */ 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 (revision 305370) +++ head/sys/dev/bwn/if_bwn_pci.c (revision 305371) @@ -1,292 +1,303 @@ /*- - * 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 * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. */ #include __FBSDID("$FreeBSD$"); #include "opt_bwn.h" #include "opt_wlan.h" #include #include #include #include #include #include #include +#include #include #include #include "bhndb_bus_if.h" #include "if_bwn_pcivar.h" /* If non-zero, enable attachment of BWN_QUIRK_UNTESTED devices */ static int attach_untested = 0; TUNABLE_INT("hw.bwn_pci.attach_untested", &attach_untested); /* If non-zero, probe at a higher priority than the stable if_bwn driver. */ static int prefer_new_driver = 0; TUNABLE_INT("hw.bwn_pci.preferred", &prefer_new_driver); /* SIBA Devices */ static const struct bwn_pci_device siba_devices[] = { BWN_BCM_DEV(BCM4301, "BCM4301 802.11b", BWN_QUIRK_ENET_HW_UNPOPULATED), BWN_BCM_DEV(BCM4306, "BCM4306 802.11b/g", 0), BWN_BCM_DEV(BCM4306_D11G, "BCM4306 802.11g", 0), BWN_BCM_DEV(BCM4306_D11A, "BCM4306 802.11a", BWN_QUIRK_WLAN_DUALCORE), BWN_BCM_DEV(BCM4306_D11DUAL, "BCM4306 802.11a/b", BWN_QUIRK_WLAN_DUALCORE), BWN_BCM_DEV(BCM4306_D11G_ID2, "BCM4306 802.11g", 0), BWN_BCM_DEV(BCM4307, "BCM4307 802.11b", 0), BWN_BCM_DEV(BCM4311_D11G, "BCM4311 802.11b/g", 0), BWN_BCM_DEV(BCM4311_D11DUAL, "BCM4311 802.11a/b/g", 0), BWN_BCM_DEV(BCM4311_D11A, "BCM4311 802.11a", BWN_QUIRK_UNTESTED|BWN_QUIRK_WLAN_DUALCORE), BWN_BCM_DEV(BCM4318_D11G, "BCM4318 802.11b/g", 0), BWN_BCM_DEV(BCM4318_D11DUAL, "BCM4318 802.11a/b/g", 0), BWN_BCM_DEV(BCM4318_D11A, "BCM4318 802.11a", BWN_QUIRK_UNTESTED|BWN_QUIRK_WLAN_DUALCORE), BWN_BCM_DEV(BCM4321_D11N, "BCM4321 802.11n Dual-Band", 0), BWN_BCM_DEV(BCM4321_D11N2G, "BCM4321 802.11n 2GHz", 0), BWN_BCM_DEV(BCM4321_D11N2G, "BCM4321 802.11n 5GHz", BWN_QUIRK_UNTESTED), BWN_BCM_DEV(BCM4322_D11N, "BCM4322 802.11n Dual-Band", 0), BWN_BCM_DEV(BCM4322_D11N2G, "BCM4322 802.11n 2GHz", BWN_QUIRK_UNTESTED), BWN_BCM_DEV(BCM4322_D11N5G, "BCM4322 802.11n 5GHz", BWN_QUIRK_UNTESTED), BWN_BCM_DEV(BCM4328_D11G, "BCM4328/4312 802.11g", 0), { 0, 0, NULL, 0 } }; /** BCMA Devices */ static const struct bwn_pci_device bcma_devices[] = { BWN_BCM_DEV(BCM4331_D11N, "BCM4331 802.11n Dual-Band", 0), BWN_BCM_DEV(BCM4331_D11N2G, "BCM4331 802.11n 2GHz", 0), BWN_BCM_DEV(BCM4331_D11N5G, "BCM4331 802.11n 5GHz", 0), BWN_BCM_DEV(BCM43225_D11N2G, "BCM43225 802.11n 2GHz", 0), { 0, 0, NULL, 0} }; /** Device configuration table */ static const struct bwn_pci_devcfg bwn_pci_devcfgs[] = { /* SIBA devices */ { .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 } }; /** Search the device configuration table for an entry matching @p dev. */ static int bwn_pci_find_devcfg(device_t dev, const struct bwn_pci_devcfg **cfg, const struct bwn_pci_device **device) { const struct bwn_pci_devcfg *dvc; const struct bwn_pci_device *dv; for (dvc = bwn_pci_devcfgs; dvc->devices != NULL; dvc++) { for (dv = dvc->devices; dv->device != 0; dv++) { if (pci_get_vendor(dev) == dv->vendor && pci_get_device(dev) == dv->device) { if (cfg != NULL) *cfg = dvc; if (device != NULL) *device = dv; return (0); } } } return (ENOENT); } static int bwn_pci_probe(device_t dev) { const struct bwn_pci_device *ident; if (bwn_pci_find_devcfg(dev, NULL, &ident)) return (ENXIO); /* Skip untested devices */ if (ident->quirks & BWN_QUIRK_UNTESTED && !attach_untested) return (ENXIO); device_set_desc(dev, ident->desc); /* Until this driver is complete, require explicit opt-in before * superceding if_bwn/siba_bwn. */ if (prefer_new_driver) return (BUS_PROBE_DEFAULT+1); else return (BUS_PROBE_LOW_PRIORITY); // return (BUS_PROBE_DEFAULT); } static int bwn_pci_attach(device_t dev) { struct bwn_pci_softc *sc; const struct bwn_pci_device *ident; int error; sc = device_get_softc(dev); sc->dev = dev; /* Find our hardware config */ if (bwn_pci_find_devcfg(dev, &sc->devcfg, &ident)) return (ENXIO); /* Save quirk flags */ sc->quirks = ident->quirks; /* Attach bridge device */ if ((error = bhndb_attach_bridge(dev, &sc->bhndb_dev, -1))) return (ENXIO); /* Success */ return (0); } static int bwn_pci_detach(device_t dev) { return (bus_generic_detach(dev)); } static void bwn_pci_probe_nomatch(device_t dev, device_t child) { const char *name; name = device_get_name(child); if (name == NULL) name = "unknown device"; device_printf(dev, "<%s> (no driver attached)\n", name); } static const struct bhndb_hwcfg * bwn_pci_get_generic_hwcfg(device_t dev, device_t child) { struct bwn_pci_softc *sc = device_get_softc(dev); return (sc->devcfg->bridge_hwcfg); } static const struct bhndb_hw * bwn_pci_get_bhndb_hwtable(device_t dev, device_t child) { struct bwn_pci_softc *sc = device_get_softc(dev); 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) { struct bwn_pci_softc *sc; sc = device_get_softc(dev); switch (bhnd_core_class(core)) { case BHND_DEVCLASS_WLAN: if (core->unit > 0 && !(sc->quirks & BWN_QUIRK_WLAN_DUALCORE)) return (true); return (false); case BHND_DEVCLASS_ENET: case BHND_DEVCLASS_ENET_MAC: case BHND_DEVCLASS_ENET_PHY: return ((sc->quirks & BWN_QUIRK_ENET_HW_UNPOPULATED) != 0); default: return (false); } } static device_method_t bwn_pci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, bwn_pci_probe), DEVMETHOD(device_attach, bwn_pci_attach), DEVMETHOD(device_detach, bwn_pci_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), /* Bus interface */ DEVMETHOD(bus_probe_nomatch, bwn_pci_probe_nomatch), /* 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 }; static devclass_t bwn_pci_devclass; DEFINE_CLASS_0(bwn_pci, bwn_pci_driver, bwn_pci_methods, sizeof(struct bwn_pci_softc)); DRIVER_MODULE(bwn_pci, pci, bwn_pci_driver, bwn_pci_devclass, NULL, NULL); DRIVER_MODULE(bhndb, bwn_pci, bhndb_pci_driver, bhndb_devclass, NULL, NULL); MODULE_DEPEND(bwn_pci, bwn, 1, 1, 1); MODULE_DEPEND(bwn_pci, bhndb, 1, 1, 1); MODULE_DEPEND(bwn_pci, bhndb_pci, 1, 1, 1); MODULE_DEPEND(bwn_pci, bcma_bhndb, 1, 1, 1); MODULE_DEPEND(bwn_pci, siba_bhndb, 1, 1, 1); Index: head/sys/dev/bwn/if_bwn_pcivar.h =================================================================== --- head/sys/dev/bwn/if_bwn_pcivar.h (revision 305370) +++ head/sys/dev/bwn/if_bwn_pcivar.h (revision 305371) @@ -1,93 +1,94 @@ /*- - * 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 * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. * * $FreeBSD$ */ #ifndef _IF_BWN_PCIVAR_H_ #define _IF_BWN_PCIVAR_H_ struct bwn_pci_devcfg; /** bwn_pci per-instance state. */ struct bwn_pci_softc { device_t dev; /**< device */ device_t bhndb_dev; /**< bhnd bridge device */ const struct bwn_pci_devcfg *devcfg; /**< bwn device config */ uint32_t quirks; /**< quirk flags */ }; /* bwn device quirks */ enum { /** No quirks */ BWN_QUIRK_NONE = 0, /** * This model/revision has not been tested and may not work. */ BWN_QUIRK_UNTESTED = 1<<0, /** * Early dual-band devices did not support accessing multiple PHYs * from a single WLAN core, and instead used separate 2GHz and 5GHz * WLAN cores. * * However, not all cards with two WLAN cores are fully populated; * we must whitelist the boards on which a second WLAN core is actually * usable. */ BWN_QUIRK_WLAN_DUALCORE = 1<<1, /** * Some early devices shipped with unconnected ethernet cores; set * this quirk to treat these cores as unpopulated. */ BWN_QUIRK_ENET_HW_UNPOPULATED = 1<<2, }; /* PCI device descriptor */ struct bwn_pci_device { uint16_t vendor; uint16_t device; const char *desc; uint32_t quirks; }; #define BWN_BCM_DEV(_devid, _desc, _quirks) \ { PCI_VENDOR_BROADCOM, PCI_DEVID_ ## _devid, \ "Broadcom " _desc " Wireless", _quirks } /* Supported device table */ 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 (revision 305370) +++ head/sys/mips/broadcom/bcm_machdep.c (revision 305371) @@ -1,533 +1,534 @@ /*- * Copyright (c) 2007 Bruce M. Simpson. * Copyright (c) 2016 Michael Zhilin * Copyright (c) 2016 Landon Fuller * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_ddb.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "bcm_machdep.h" #include "bcm_mips_exts.h" #ifdef CFE #include #endif #if 0 #define BCM_TRACE(_fmt, ...) printf(_fmt, ##__VA_ARGS__) #else #define BCM_TRACE(_fmt, ...) #endif static int bcm_init_platform_data(struct bcm_platform *bp); static int bcm_find_core(struct bcm_platform *bp, uint16_t vendor, uint16_t device, int unit, struct bhnd_core_info *info, uintptr_t *addr); static int bcm_erom_probe_and_attach(bhnd_erom_class_t **erom_cls, kobj_ops_t erom_ops, bhnd_erom_t *erom, size_t esize, struct bhnd_chipid *cid); extern int *edata; extern int *end; static struct bcm_platform bcm_platform_data; static bool bcm_platform_data_avail = false; struct bcm_platform * bcm_get_platform(void) { if (!bcm_platform_data_avail) panic("platform data not available"); return (&bcm_platform_data); } static bus_addr_t bcm_get_bus_addr(void) { long maddr; if (resource_long_value("bhnd", 0, "maddr", &maddr) == 0) return ((u_long)maddr); return (BHND_DEFAULT_CHIPC_ADDR); } /** * Search the device enumeration table for a core matching @p vendor, * @p device, and @p unit. * * @param bp Platform state containing a valid EROM parser. * @param vendor The core's required vendor. * @param device The core's required device id. * @param unit The core's required unit number. * @param[out] info If non-NULL, will be populated with the core * info. * @param[out] addr If non-NULL, will be populated with the core's * physical register address. */ static int bcm_find_core(struct bcm_platform *bp, uint16_t vendor, uint16_t device, int unit, struct bhnd_core_info *info, uintptr_t *addr) { struct bhnd_core_match md; bhnd_addr_t b_addr; bhnd_size_t b_size; int error; md = (struct bhnd_core_match) { BHND_MATCH_CORE_VENDOR(vendor), BHND_MATCH_CORE_ID(BHND_COREID_CC), BHND_MATCH_CORE_UNIT(0) }; /* Fetch core info */ error = bhnd_erom_lookup_core_addr(&bp->erom.obj, &md, BHND_PORT_DEVICE, 0, 0, info, &b_addr, &b_size); if (error) return (error); if (addr != NULL && b_addr > UINTPTR_MAX) { BCM_ERR("core address %#jx overflows native address width\n", (uintmax_t)b_addr); return (ERANGE); } if (addr != NULL) *addr = b_addr; return (0); } /** * Probe and attach a bhnd_erom parser instance for the bhnd bus. * * @param[out] erom_cls The probed EROM class. * @param[out] erom_ops The storage to be used when compiling * @p erom_cls. * @param[out] erom The storage to be used when initializing the * static instance of @p erom_cls. * @param esize The total available number of bytes allocated * for @p erom. If this is less than is required * by @p erom_cls ENOMEM will be returned. * @param[out] cid On success, the probed chip identification. */ static int bcm_erom_probe_and_attach(bhnd_erom_class_t **erom_cls, kobj_ops_t erom_ops, bhnd_erom_t *erom, size_t esize, struct bhnd_chipid *cid) { bhnd_erom_class_t **clsp; bus_space_tag_t bst; bus_space_handle_t bsh; bus_addr_t bus_addr; int error, prio, result; bus_addr = bcm_get_bus_addr(); *erom_cls = NULL; prio = 0; bst = mips_bus_space_generic; bsh = BCM_SOC_BSH(bus_addr, 0); SET_FOREACH(clsp, bhnd_erom_class_set) { struct bhnd_chipid pcid; bhnd_erom_class_t *cls; struct kobj_ops kops; cls = *clsp; /* Compile the class' ops table */ 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; /* 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; } /* Valid EROM class probed? */ if (*erom_cls == NULL) { BCM_ERR("no erom parser found for root bus at %#jx\n", (uintmax_t)bus_addr); return (ENOENT); } /* Using the provided storage, recompile the erom class ... */ kobj_class_compile_static(*erom_cls, erom_ops); /* ... 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); } /** * Populate platform configuration data. */ static int bcm_init_platform_data(struct bcm_platform *bp) { bool aob, pmu; int error; /* Fetch CFE console handle (if any). Must be initialized before * any calls to printf/early_putc. */ #ifdef CFE if ((bp->cfe_console = cfe_getstdhandle(CFE_STDHANDLE_CONSOLE)) < 0) bp->cfe_console = -1; #endif /* Probe and attach device table provider, populating our * chip identification */ error = bcm_erom_probe_and_attach(&bp->erom_impl, &bp->erom_ops, &bp->erom.obj, sizeof(bp->erom), &bp->cid); if (error) { BCM_ERR("error attaching erom parser: %d\n", error); return (error); } /* Fetch chipcommon core info */ error = bcm_find_core(bp, BHND_MFGID_BCM, BHND_COREID_CC, 0, &bp->cc_id, &bp->cc_addr); if (error) { BCM_ERR("error locating chipc core: %d\n", error); return (error); } /* Fetch chipc capability flags */ bp->cc_caps = BCM_SOC_READ_4(bp->cc_addr, CHIPC_CAPABILITIES); bp->cc_caps_ext = 0x0; if (CHIPC_HWREV_HAS_CAP_EXT(bp->cc_id.hwrev)) bp->cc_caps_ext = BCM_CHIPC_READ_4(bp, CHIPC_CAPABILITIES_EXT); /* Fetch PMU info */ pmu = CHIPC_GET_FLAG(bp->cc_caps, CHIPC_CAP_PMU); aob = CHIPC_GET_FLAG(bp->cc_caps_ext, CHIPC_CAP2_AOB); if (pmu && aob) { /* PMU block mapped to a PMU core on the Always-on-Bus (aob) */ error = bcm_find_core(bp, BHND_MFGID_BCM, BHND_COREID_PMU, 0, &bp->pmu_id, &bp->pmu_addr); if (error) { BCM_ERR("error locating pmu core: %d\n", error); return (error); } } else if (pmu) { /* PMU block mapped to chipc */ bp->pmu_addr = bp->cc_addr; bp->pmu_id = bp->cc_id; } else { /* No PMU */ bp->pmu_addr = 0x0; memset(&bp->pmu_id, 0, sizeof(bp->pmu_id)); } /* Initialize PMU query state */ if (pmu) { error = bhnd_pmu_query_init(&bp->pmu, NULL, bp->cid, &bcm_pmu_soc_io, bp); if (error) { BCM_ERR("bhnd_pmu_query_init() failed: %d\n", error); return (error); } } bcm_platform_data_avail = true; return (0); } void platform_cpu_init() { /* Nothing special */ } static void mips_init(void) { int i, j; printf("entry: mips_init()\n"); #ifdef CFE /* * Query DRAM memory map from CFE. */ physmem = 0; for (i = 0; i < 10; i += 2) { int result; uint64_t addr, len, type; result = cfe_enummem(i / 2, 0, &addr, &len, &type); if (result < 0) { BCM_TRACE("There is no phys memory for: %d\n", i); phys_avail[i] = phys_avail[i + 1] = 0; break; } if (type != CFE_MI_AVAILABLE) { BCM_TRACE("phys memory is not available: %d\n", i); continue; } phys_avail[i] = addr; if (i == 0 && addr == 0) { /* * If this is the first physical memory segment probed * from CFE, omit the region at the start of physical * memory where the kernel has been loaded. */ phys_avail[i] += MIPS_KSEG0_TO_PHYS(kernel_kseg0_end); } BCM_TRACE("phys memory is available for: %d\n", i); BCM_TRACE(" => addr = %jx\n", addr); BCM_TRACE(" => len = %jd\n", len); phys_avail[i + 1] = addr + len; physmem += len; } BCM_TRACE("Total phys memory is : %ld\n", physmem); realmem = btoc(physmem); #endif for (j = 0; j < i; j++) dump_avail[j] = phys_avail[j]; physmem = realmem; init_param1(); init_param2(physmem); mips_cpu_init(); pmap_bootstrap(); mips_proc0_init(); mutex_init(); kdb_init(); #ifdef KDB if (boothowto & RB_KDB) kdb_enter(KDB_WHY_BOOTFLAGS, "Boot flags requested debugger"); #endif } void platform_reset(void) { struct bcm_platform *bp; bool bcm4785war; printf("bcm::platform_reset()\n"); intr_disable(); #ifdef CFE /* Fall back on CFE if reset requested during platform * data initialization */ if (!bcm_platform_data_avail) { cfe_exit(0, 0); while (1); } #endif bp = bcm_get_platform(); bcm4785war = false; /* Handle BCM4785-specific behavior */ if (bp->cid.chip_id == BHND_CHIPID_BCM4785) { bcm4785war = true; /* Switch to async mode */ bcm_mips_wr_pllcfg3(MIPS_BCMCFG_PLLCFG3_SM); } /* Set watchdog (PMU or ChipCommon) */ if (bp->pmu_addr != 0x0) { BCM_PMU_WRITE_4(bp, BHND_PMU_WATCHDOG, 1); } else BCM_CHIPC_WRITE_4(bp, CHIPC_WATCHDOG, 1); /* BCM4785 */ if (bcm4785war) { mips_sync(); __asm __volatile("wait"); } while (1); } void platform_start(__register_t a0, __register_t a1, __register_t a2, __register_t a3) { vm_offset_t kernend; uint64_t platform_counter_freq; int error; /* clear the BSS and SBSS segments */ kernend = (vm_offset_t)&end; memset(&edata, 0, kernend - (vm_offset_t)(&edata)); mips_postboot_fixup(); /* Initialize pcpu stuff */ mips_pcpu0_init(); #ifdef CFE /* * Initialize CFE firmware trampolines. This must be done * before any CFE APIs are called, including writing * to the CFE console. * * CFE passes the following values in registers: * a0: firmware handle * a2: firmware entry point * a3: entry point seal */ if (a3 == CFE_EPTSEAL) cfe_init(a0, a2); #endif /* Init BCM platform data */ if ((error = bcm_init_platform_data(&bcm_platform_data))) panic("bcm_init_platform_data() failed: %d", error); platform_counter_freq = bcm_get_cpufreq(bcm_get_platform()); /* CP0 ticks every two cycles */ mips_timer_early_init(platform_counter_freq / 2); cninit(); mips_init(); mips_timer_init_params(platform_counter_freq, 1); } /* * CFE-based EARLY_PRINTF support. To use, add the following to the kernel * config: * option EARLY_PRINTF * option CFE * device cfe */ #if defined(EARLY_PRINTF) && defined(CFE) static void bcm_cfe_eputc(int c) { unsigned char ch; int handle; ch = (unsigned char) c; /* bcm_get_platform() cannot be used here, as we may be called * from bcm_init_platform_data(). */ if ((handle = bcm_platform_data.cfe_console) < 0) return; if (ch == '\n') early_putc('\r'); while ((cfe_write(handle, &ch, 1)) == 0) continue; } early_putc_t *early_putc = bcm_cfe_eputc; #endif /* EARLY_PRINTF */ Index: head/sys/modules/bhnd/bhndb/Makefile =================================================================== --- head/sys/modules/bhnd/bhndb/Makefile (revision 305370) +++ head/sys/modules/bhnd/bhndb/Makefile (revision 305371) @@ -1,16 +1,17 @@ # $FreeBSD$ .PATH: ${.CURDIR}/../../../dev/bhnd/bhndb KMOD= bhndb SRCS= bhndb.c bhndb_subr.c bhndb_hwdata.c \ bhnd_bhndb.c \ bhndb_bus_if.c bhndb_bus_if.h \ 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 .include