Migrate bhndb(4) to the new bhnd_erom API.

Adds support for probing and initializing bhndb(4) bridge state using
the bhnd_erom API, ensuring that full bridge configuration is available
*prior* to actually attaching and enumerating the bhnd(4) child device,
allowing us to safely allocate bus-level agent/device resources during
bhnd(4) bus enumeration.

- Add a bhnd_erom_probe() method usable by bhndb(4). This is an analogue
  to the existing bhnd_erom_probe_static() method, and allows the bhndb
  bridge to discover the best available erom parser class prior to newbus
  probing of its children.
- Add support for supplying identification hints when probing erom
  devices. This is required on early EXTIF-only chipsets, where chip
  identification registers are not available.
- Migrate bhndb over to the new bhnd_erom API, using bhnd_core_info
  records rather than bridged bhnd(4) device_t references to determine
  the bridged chipsets' capability/bridge configuration.
- The bhndb parent (e.g. if_bwn) is now required to supply a hardware
  priority table to the bridge. The default table is currently sufficient
  for our supported devices.
- Drop the two-pass attach approach we used for compatibility with bhndb(4) in
  the bhnd(4) bus drivers, and instead perform bus enumeration immediately,
  and allocate bridged per-child bus-level resources during that enumeration.

Approved by:	adrian (mentor)
Differential Revision:	https://reviews.freebsd.org/D7768
This commit is contained in:
Landon J. Fuller 2016-09-04 00:58:19 +00:00
parent 3da0f3c9ae
commit 111d7cb2e3
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=305371
33 changed files with 1660 additions and 1040 deletions

View File

@ -48,6 +48,12 @@ __FBSDID("$FreeBSD$");
/* RID used when allocating EROM table */
#define BCMA_EROM_RID 0
static bhnd_erom_class_t *
bcma_get_erom_class(driver_t *driver)
{
return (&bcma_erom_parser);
}
int
bcma_probe(device_t dev)
{
@ -55,70 +61,25 @@ bcma_probe(device_t dev)
return (BUS_PROBE_DEFAULT);
}
/**
* Default bcma(4) bus driver implementation of DEVICE_ATTACH().
*
* This implementation initializes internal bcma(4) state and performs
* bus enumeration, and must be called by subclassing drivers in
* DEVICE_ATTACH() before any other bus methods.
*/
int
bcma_attach(device_t dev)
{
struct bcma_devinfo *dinfo;
device_t *devs, child;
int ndevs;
int error;
int error;
if ((error = device_get_children(dev, &devs, &ndevs)))
/* Enumerate children */
if ((error = bcma_add_children(dev))) {
device_delete_children(dev);
return (error);
/*
* Map our children's agent register block.
*/
for (int i = 0; i < ndevs; i++) {
bhnd_addr_t addr;
bhnd_size_t size;
rman_res_t r_start, r_count, r_end;
child = devs[i];
dinfo = device_get_ivars(child);
KASSERT(!device_is_suspended(child),
("bcma(4) stateful suspend handling requires that devices "
"not be suspended before bcma_attach()"));
/* Verify that the agent register block exists and is
* mappable */
if (bhnd_get_port_rid(child, BHND_PORT_AGENT, 0, 0) == -1)
continue;
/* Fetch the address of the agent register block */
error = bhnd_get_region_addr(child, BHND_PORT_AGENT, 0, 0,
&addr, &size);
if (error) {
device_printf(dev, "failed fetching agent register "
"block address for core %d\n", i);
goto cleanup;
}
/* Allocate the resource */
r_start = addr;
r_count = size;
r_end = r_start + r_count - 1;
dinfo->rid_agent = i + 1;
dinfo->res_agent = BHND_BUS_ALLOC_RESOURCE(dev, dev,
SYS_RES_MEMORY, &dinfo->rid_agent, r_start, r_end, r_count,
RF_ACTIVE);
if (dinfo->res_agent == NULL) {
device_printf(dev, "failed allocating agent register "
"block for core %d\n", i);
error = ENXIO;
goto cleanup;
}
}
cleanup:
free(devs, M_BHND);
if (error)
return (error);
return (bhnd_generic_attach(dev));
return (0);
}
int
@ -191,15 +152,6 @@ bcma_get_resource_list(device_t dev, device_t 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)
{
@ -516,8 +468,7 @@ bcma_add_children(device_t 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);
@ -528,17 +479,21 @@ bcma_add_children(device_t bus)
child = BUS_ADD_CHILD(bus, 0, NULL, -1);
if (child == NULL) {
error = ENXIO;
goto failed;
goto cleanup;
}
/* Initialize device ivars */
dinfo = device_get_ivars(child);
if ((error = bcma_init_dinfo(bus, dinfo, corecfg)))
goto failed;
goto cleanup;
/* The dinfo instance now owns the corecfg value */
corecfg = NULL;
/* Allocate device's agent registers, if any */
if ((error = bcma_dinfo_alloc_agent(bus, child, dinfo)))
goto cleanup;
/* If pins are floating or the hardware is otherwise
* unpopulated, the device shouldn't be used. */
if (bhnd_is_hw_disabled(child))
@ -548,16 +503,19 @@ bcma_add_children(device_t bus)
BHND_BUS_CHILD_ADDED(bus, child);
}
/* Hit EOF parsing cores? */
/* EOF while parsing cores is expected */
if (error == ENOENT)
error = 0;
failed:
cleanup:
bhnd_erom_free(erom);
if (corecfg != NULL)
bcma_free_corecfg(corecfg);
if (error)
device_delete_children(bus);
return (error);
}
@ -574,7 +532,7 @@ static device_method_t bcma_methods[] = {
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),

View File

@ -72,26 +72,21 @@ bcma_bhndb_probe(device_t dev)
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

View File

@ -58,7 +58,13 @@ __FBSDID("$FreeBSD$");
* marker.
*/
struct bcma_erom_io;
static const char *bcma_erom_entry_type_name (uint8_t entry);
static uint32_t bcma_eio_read4(struct bcma_erom_io *io,
bus_size_t offset);
static int bcma_erom_read32(struct bcma_erom *erom,
uint32_t *entry);
static int bcma_erom_skip32(struct bcma_erom *erom);
@ -72,8 +78,10 @@ static int bcma_erom_seek_next(struct bcma_erom *erom,
static int bcma_erom_region_to_port_type(struct bcma_erom *erom,
uint8_t region_type, bhnd_port_type *port_type);
static int bcma_erom_peek32(struct bcma_erom *erom,
uint32_t *entry);
static bus_size_t bcma_erom_tell(struct bcma_erom *erom);
static void bcma_erom_seek(struct bcma_erom *erom,
bus_size_t offset);
@ -96,9 +104,33 @@ 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, \
@ -106,7 +138,6 @@ static void bcma_erom_to_core_info(const struct bcma_erom_core *core,
} \
} while(0)
/** Return the type name for an EROM entry */
static const char *
bcma_erom_entry_type_name (uint8_t entry)
@ -123,47 +154,112 @@ bcma_erom_entry_type_name (uint8_t entry)
}
}
static int
bcma_erom_init(bhnd_erom_t *erom, device_t parent, int rid, bus_addr_t enum_addr)
/**
* 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)
{
struct bcma_erom *sc = (struct bcma_erom *)erom;
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, const struct bhnd_chipid *cid,
device_t parent, int rid)
{
struct bcma_erom *sc;
struct bhnd_resource *res;
sc = (struct bcma_erom *)erom;
sc->dev = parent;
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)
return (ENOMEM);
sc->start = BCMA_EROM_TABLE_START;
sc->offset = 0;
res = bhnd_alloc_resource(parent, SYS_RES_MEMORY, &rid, cid->enum_addr,
cid->enum_addr + BCMA_EROM_TABLE_SIZE - 1, BCMA_EROM_TABLE_SIZE,
RF_ACTIVE|RF_SHAREABLE);
if (res == NULL)
return (ENOMEM);
bcma_eio_init(&sc->io, res, rid, BCMA_EROM_TABLE_START);
return (0);
}
/* BCMA implementation of BHND_EROM_INIT_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_init_static(bhnd_erom_t *erom, const struct bhnd_chipid *cid,
bus_space_tag_t bst, bus_space_handle_t bsh)
{
uint32_t idreg, eaddr;
uint8_t chip_type;
struct bcma_erom *sc;
idreg = bus_space_read_4(bst, bsh, CHIPC_ID);
chip_type = CHIPC_GET_BITS(idreg, CHIPC_ID_BUS);
sc = (struct bcma_erom *)erom;
sc->dev = NULL;
sc->offset = 0;
/* Fetch EROM physical address */
if (!BHND_CHIPTYPE_HAS_EROM(chip_type))
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_common(struct bcma_erom_io *io, const struct bhnd_chipid *hint,
struct bhnd_chipid *cid)
{
uint32_t idreg, eromptr;
/* Hints aren't supported; all BCMA devices have a ChipCommon
* core */
if (hint != NULL)
return (EINVAL);
/* Confirm CHIPC_EROMPTR availability */
idreg = bcma_eio_read4(io, CHIPC_ID);
if (!BHND_CHIPTYPE_HAS_EROM(CHIPC_GET_BITS(idreg, CHIPC_ID_BUS)))
return (ENXIO);
eaddr = bus_space_read_4(bst, bsh, CHIPC_EROMPTR);
/* Fetch EROM address */
eromptr = bcma_eio_read4(io, CHIPC_EROMPTR);
/* Parse chip identifier */
*cid = bhnd_parse_chipid(idreg, eaddr);
*cid = bhnd_parse_chipid(idreg, eromptr);
/* Verify chip type */
switch (chip_type) {
switch (cid->chip_type) {
case BHND_CHIPTYPE_BCMA:
return (BUS_PROBE_DEFAULT);
@ -173,37 +269,44 @@ bcma_erom_probe_static(bhnd_erom_class_t *cls, bus_space_tag_t bst,
default:
return (ENXIO);
}
}
}
static int
bcma_erom_init_static(bhnd_erom_t *erom, bus_space_tag_t bst,
bus_space_handle_t bsh)
bcma_erom_probe(bhnd_erom_class_t *cls, struct bhnd_resource *res,
bus_size_t offset, const struct bhnd_chipid *hint, struct bhnd_chipid *cid)
{
struct bcma_erom *sc = (struct bcma_erom *)erom;
struct bcma_erom_io io;
sc->dev = NULL;
sc->rid = -1;
sc->res = NULL;
sc->bst = bst;
sc->bsh = bsh;
sc->start = BCMA_EROM_TABLE_START;
sc->offset = 0;
bcma_eio_init(&io, res, rman_get_rid(res->res),
offset + BCMA_EROM_TABLE_START);
return (0);
return (bcma_erom_probe_common(&io, hint, cid));
}
static int
bcma_erom_probe_static(bhnd_erom_class_t *cls, bus_space_tag_t bst,
bus_space_handle_t bsh, bus_addr_t paddr, const struct bhnd_chipid *hint,
struct bhnd_chipid *cid)
{
struct bcma_erom_io io;
bcma_eio_init_static(&io, bst, bsh, BCMA_EROM_TABLE_START);
return (bcma_erom_probe_common(&io, hint, cid));
}
static void
bcma_erom_fini(bhnd_erom_t *erom)
{
struct bcma_erom *sc = (struct bcma_erom *)erom;
if (sc->res != NULL) {
bhnd_release_resource(sc->dev, SYS_RES_MEMORY, sc->rid,
sc->res);
if (sc->io.res != NULL) {
bhnd_release_resource(sc->dev, SYS_RES_MEMORY, sc->io.rid,
sc->io.res);
sc->res = NULL;
sc->rid = -1;
sc->io.res = NULL;
sc->io.rid = -1;
}
}
@ -484,19 +587,12 @@ bcma_erom_seek(struct bcma_erom *erom, bus_size_t offset)
static int
bcma_erom_peek32(struct bcma_erom *erom, uint32_t *entry)
{
bus_size_t off;
if (erom->offset >= BCMA_EROM_TABLE_SIZE) {
if (erom->offset >= (BCMA_EROM_TABLE_SIZE - sizeof(uint32_t))) {
EROM_LOG(erom, "BCMA EROM table missing terminating EOF\n");
return (EINVAL);
}
off = erom->start + erom->offset;
if (erom->res != NULL)
*entry = bhnd_bus_read_4(erom->res, off);
else
*entry = bus_space_read_4(erom->bst, erom->bsh, off);
*entry = bcma_eio_read4(&erom->io, erom->offset);
return (0);
}
@ -1259,6 +1355,7 @@ bcma_erom_next_corecfg(struct bcma_erom *erom, struct bcma_corecfg **result)
}
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),

View File

@ -42,25 +42,6 @@ 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 */

View File

@ -93,10 +93,19 @@ 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 *

View File

@ -43,6 +43,10 @@ __FBSDID("$FreeBSD$");
#include "bcmavar.h"
/* Return the resource ID for a device's agent register allocation */
#define BCMA_AGENT_RID(_dinfo) \
(BCMA_AGENT_RID_BASE + BCMA_DINFO_COREIDX(_dinfo))
/**
* Allocate and initialize new core config structure.
*
@ -244,6 +248,63 @@ bcma_init_dinfo(device_t bus, struct bcma_devinfo *dinfo,
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.
*

View File

@ -45,6 +45,18 @@
* Internal definitions shared by bcma(4) driver implementations.
*/
/** Base resource ID for per-core agent register allocations */
#define BCMA_AGENT_RID_BASE 100
/**
* Return the device's core index.
*
* @param _dinfo The bcma_devinfo instance to query.
*/
#define BCMA_DINFO_COREIDX(_dinfo) \
((_dinfo)->corecfg->core_info.core_idx)
/** BCMA port identifier. */
typedef u_int bcma_pid_t;
#define BCMA_PID_MAX UINT_MAX /**< Maximum bcma_pid_t value */
@ -72,6 +84,8 @@ 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);
@ -147,7 +161,6 @@ struct bcma_devinfo {
/** 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_ */

View File

@ -39,6 +39,7 @@
#include "bhnd_ids.h"
#include "bhnd_types.h"
#include "bhnd_erom_types.h"
#include "bhnd_debug.h"
#include "bhnd_bus_if.h"
#include "bhnd_match.h"
@ -188,6 +189,12 @@ struct bhnd_resource {
* is MMIO accessible. */
};
/** Wrap the active resource @p _r in a bhnd_resource structure */
#define BHND_DIRECT_RESOURCE(_r) ((struct bhnd_resource) { \
.res = (_r), \
.direct = true, \
})
/**
* Device quirk table descriptor.
*/
@ -278,6 +285,13 @@ const struct bhnd_core_info *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);
@ -389,7 +403,16 @@ int bhnd_bus_generic_deactivate_resource (device_t dev,
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

View File

@ -29,6 +29,7 @@
#include <sys/rman.h>
#include <dev/bhnd/bhnd_types.h>
#include <dev/bhnd/bhnd_erom_types.h>
INTERFACE bhnd_bus;
@ -49,7 +50,13 @@ CODE {
#include <sys/systm.h>
#include <dev/bhnd/bhndvar.h>
static bhnd_erom_class_t *
bhnd_bus_null_get_erom_class(driver_t *driver)
{
return (NULL);
}
static struct bhnd_chipid *
bhnd_bus_null_get_chipid(device_t dev, device_t child)
{
@ -152,7 +159,7 @@ CODE {
static device_t
bhnd_bus_null_find_hostb_device(device_t dev)
{
panic("bhnd_bus_find_hostb_device unimplemented");
return (NULL);
}
static bool
@ -198,6 +205,15 @@ CODE {
}
/**
* 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.
*

View File

@ -45,15 +45,15 @@ __FBSDID("$FreeBSD$");
* be allocated.
* @param rid The resource ID to be used when allocating EROM
* resources.
* @param enum_addr The base address of the device enumeration table.
* @param cid The device's chip identifier.
*
* @retval non-NULL success
* @retval NULL if an error occured allocating or initializing the
* EROM parser.
*/
bhnd_erom_t *
bhnd_erom_alloc(bhnd_erom_class_t *cls, device_t parent, int rid,
bus_addr_t enum_addr)
bhnd_erom_alloc(bhnd_erom_class_t *cls, const struct bhnd_chipid *cid,
device_t parent, int rid)
{
bhnd_erom_t *erom;
int error;
@ -61,9 +61,9 @@ bhnd_erom_alloc(bhnd_erom_class_t *cls, device_t parent, int rid,
erom = (bhnd_erom_t *)kobj_create((kobj_class_t)cls, M_BHND,
M_WAITOK|M_ZERO);
if ((error = BHND_EROM_INIT(erom, parent, rid, enum_addr))) {
if ((error = BHND_EROM_INIT(erom, cid, parent, rid))) {
printf("error initializing %s parser at %#jx with "
"rid %d: %d\n", cls->name, (uintmax_t)enum_addr, rid,
"rid %d: %d\n", cls->name, (uintmax_t)cid->enum_addr, rid,
error);
kobj_delete((kobj_t)erom, M_BHND);
@ -86,6 +86,7 @@ bhnd_erom_alloc(bhnd_erom_class_t *cls, device_t parent, int rid,
* @param esize The total available number of bytes allocated for
* @p erom. If this is less than is required by @p cls,
* ENOMEM will be returned.
* @param cid The device's chip identifier.
* @param bst Bus space tag.
* @param bsh Bus space handle mapping the device enumeration
* space.
@ -97,7 +98,7 @@ bhnd_erom_alloc(bhnd_erom_class_t *cls, device_t parent, int rid,
*/
int
bhnd_erom_init_static(bhnd_erom_class_t *cls, bhnd_erom_t *erom, size_t esize,
bus_space_tag_t bst, bus_space_handle_t bsh)
const struct bhnd_chipid *cid, bus_space_tag_t bst, bus_space_handle_t bsh)
{
kobj_class_t kcls;
@ -109,7 +110,7 @@ bhnd_erom_init_static(bhnd_erom_class_t *cls, bhnd_erom_t *erom, size_t esize,
/* 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));
}
/**

View File

@ -42,11 +42,12 @@
#include "bhnd_erom_if.h"
bhnd_erom_t *bhnd_erom_alloc(bhnd_erom_class_t *cls,
device_t parent, int rid,
bus_addr_t enum_addr);
const struct bhnd_chipid *cid,
device_t parent, int rid);
int bhnd_erom_init_static(bhnd_erom_class_t *cls,
bhnd_erom_t *erom, size_t esize,
const struct bhnd_chipid *cid,
bus_space_tag_t bst,
bus_space_handle_t bsh);
@ -92,17 +93,50 @@ SET_DECLARE(bhnd_erom_class_set, bhnd_erom_class_t);
#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
@ -116,9 +150,10 @@ SET_DECLARE(bhnd_erom_class_set, bhnd_erom_class_t);
*/
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));
}
/**

View File

@ -43,17 +43,50 @@ INTERFACE bhnd_erom;
# 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
@ -66,51 +99,54 @@ INTERFACE bhnd_erom;
* 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;
};
/**

View File

@ -105,8 +105,9 @@ struct bhnd_core_match {
core_id:1,
core_rev:1,
core_class:1,
core_idx:1,
core_unit:1,
flags_unused:3;
flags_unused:2;
} match;
} m;
@ -114,6 +115,7 @@ struct bhnd_core_match {
uint16_t core_id; /**< required core ID */
struct bhnd_hwrev_match core_rev; /**< matching core revisions. */
bhnd_devclass_t core_class; /**< required bhnd class */
u_int core_idx; /**< required core index */
int core_unit; /**< required core unit */
};
@ -122,6 +124,7 @@ struct bhnd_core_match {
_BHND_COPY_MATCH_FIELD(_src, core_id), \
_BHND_COPY_MATCH_FIELD(_src, core_rev), \
_BHND_COPY_MATCH_FIELD(_src, core_class), \
_BHND_COPY_MATCH_FIELD(_src, core_idx), \
_BHND_COPY_MATCH_FIELD(_src, core_unit) \
#define BHND_MATCH_CORE_VENDOR(_v) _BHND_SET_MATCH_FIELD(core_vendor, _v)
@ -129,6 +132,7 @@ struct bhnd_core_match {
#define BHND_MATCH_CORE_REV(_rev) _BHND_SET_MATCH_FIELD(core_rev, \
BHND_ ## _rev)
#define BHND_MATCH_CORE_CLASS(_cls) _BHND_SET_MATCH_FIELD(core_class, _cls)
#define BHND_MATCH_CORE_IDX(_idx) _BHND_SET_MATCH_FIELD(core_idx, _idx)
#define BHND_MATCH_CORE_UNIT(_unit) _BHND_SET_MATCH_FIELD(core_unit, _unit)
/**
@ -255,6 +259,7 @@ struct bhnd_device_match {
core_id:1,
core_rev:1,
core_class:1,
core_idx:1,
core_unit:1,
chip_id:1,
chip_rev:1,
@ -263,7 +268,7 @@ struct bhnd_device_match {
board_type:1,
board_rev:1,
board_srom_rev:1,
flags_unused:2;
flags_unused:1;
} match;
} m;
@ -271,6 +276,7 @@ struct bhnd_device_match {
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 */

View File

@ -83,7 +83,7 @@ bhnd_nexus_read_chipid(device_t dev, struct bhnd_chipid *chipid)
static bool
bhnd_nexus_is_hw_disabled(device_t dev, device_t child)
{
return false;
return (false);
}
static bhnd_attach_type

View File

@ -485,6 +485,47 @@ bhnd_find_core(const struct bhnd_core_info *cores, u_int num_cores,
return bhnd_match_core(cores, num_cores, &md);
}
/**
* Create an equality match descriptor for @p core.
*
* @param core The core info to be matched on.
* @param desc On return, will be populated with a match descriptor for @p core.
*/
struct bhnd_core_match
bhnd_core_get_match_desc(const struct bhnd_core_info *core)
{
return ((struct bhnd_core_match) {
BHND_MATCH_CORE_VENDOR(core->vendor),
BHND_MATCH_CORE_ID(core->device),
BHND_MATCH_CORE_REV(HWREV_EQ(core->hwrev)),
BHND_MATCH_CORE_CLASS(bhnd_core_class(core)),
BHND_MATCH_CORE_IDX(core->core_idx),
BHND_MATCH_CORE_UNIT(core->unit)
});
}
/**
* Return true if the @p lhs is equal to @p rhs
*
* @param lhs The first bhnd core descriptor to compare.
* @param rhs The second bhnd core descriptor to compare.
*
* @retval true if @p lhs is equal to @p rhs
* @retval false if @p lhs is not equal to @p rhs
*/
bool
bhnd_cores_equal(const struct bhnd_core_info *lhs,
const struct bhnd_core_info *rhs)
{
struct bhnd_core_match md;
/* Use an equality match descriptor to perform the comparison */
md = bhnd_core_get_match_desc(rhs);
return (bhnd_core_matches(lhs, &md));
}
/**
* Return true if the @p core matches @p desc.
*
@ -511,6 +552,9 @@ bhnd_core_matches(const struct bhnd_core_info *core,
!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);

View File

@ -66,6 +66,22 @@ bhnd_bhndb_get_attach_type(device_t dev, device_t child)
return (BHND_ATTACH_ADAPTER);
}
static device_t
bhnd_bhndb_find_hostb_device(device_t dev)
{
struct bhnd_core_info core;
struct bhnd_core_match md;
int error;
/* Ask the bridge for the hostb core info */
if ((error = BHNDB_GET_HOSTB_CORE(device_get_parent(dev), dev, &core)))
return (NULL);
/* Find the corresponding bus device */
md = bhnd_core_get_match_desc(&core);
return (bhnd_match_child(dev, &md));
}
static bhnd_clksrc
bhnd_bhndb_pwrctl_get_clksrc(device_t dev, device_t child,
bhnd_clock clock)
@ -96,6 +112,7 @@ bhnd_bhndb_pwrctl_ungate_clock(device_t dev, device_t child,
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),

View File

@ -50,6 +50,8 @@ __FBSDID("$FreeBSD$");
#include <dev/bhnd/bhndvar.h>
#include <dev/bhnd/bhndreg.h>
#include <dev/bhnd/bhnd_erom.h>
#include <dev/bhnd/cores/chipc/chipcreg.h>
#include <dev/bhnd/nvram/bhnd_nvram.h>
@ -71,24 +73,31 @@ enum {
#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);
@ -111,6 +120,10 @@ static int bhndb_try_activate_resource(
int type, int rid, struct resource *r,
bool *indirect);
static inline struct bhndb_dw_alloc *bhndb_io_resource(struct bhndb_softc *sc,
bus_addr_t addr, bus_size_t size,
bus_size_t *offset);
/**
* Default bhndb(4) implementation of DEVICE_PROBE().
@ -183,26 +196,40 @@ bhndb_child_location_str(device_t dev, device_t child, char *buf,
}
/**
* Return true if @p devlist matches the @p hw specification.
* Return the bridge core info. Will panic if the bridge core info has not yet
* been populated during full bridge configuration.
*
* @param devlist A device table to match against.
* @param num_devs The number of devices in @p devlist.
* @param sc BHNDB device state.
*/
static struct bhnd_core_info *
bhndb_get_bridge_core(struct bhndb_softc *sc)
{
if (!sc->have_br_core)
panic("bridge not yet fully configured; no bridge core!");
return (&sc->bridge_core);
}
/**
* Return true if @p cores matches the @p hw specification.
*
* @param cores A device table to match against.
* @param ncores The number of cores in @p cores.
* @param hw The hardware description to be matched against.
*/
static bool
bhndb_hw_matches(device_t *devlist, int num_devs, const struct bhndb_hw *hw)
bhndb_hw_matches(struct bhnd_core_info *cores, u_int ncores,
const struct bhndb_hw *hw)
{
for (u_int i = 0; i < hw->num_hw_reqs; i++) {
const struct bhnd_core_match *match;
struct bhnd_core_info ci;
bool found;
match = &hw->hw_reqs[i];
found = false;
for (int d = 0; d < num_devs; d++) {
ci = bhnd_get_core_info(devlist[d]);
if (!bhnd_core_matches(&ci, match))
for (u_int d = 0; d < ncores; d++) {
if (!bhnd_core_matches(&cores[d], match))
continue;
found = true;
@ -217,20 +244,21 @@ bhndb_hw_matches(device_t *devlist, int num_devs, const struct bhndb_hw *hw)
}
/**
* Initialize the region maps and priority configuration in @p r using
* the provided priority @p table and the set of devices attached to
* the bridged @p bus_dev .
* Initialize the region maps and priority configuration in @p br using
* the priority @p table and the set of cores enumerated by @p erom.
*
* @param sc The bhndb device state.
* @param devs All devices enumerated on the bridged bhnd bus.
* @param ndevs The length of @p devs.
* @param br The resource state to be configured.
* @param erom EROM parser used to enumerate @p cores.
* @param cores All cores enumerated on the bridged bhnd bus.
* @param ncores The length of @p cores.
* @param table Hardware priority table to be used to determine the relative
* priorities of per-core port resources.
* @param r The resource state to be configured.
*/
static int
bhndb_initialize_region_cfg(struct bhndb_softc *sc, device_t *devs, int ndevs,
const struct bhndb_hw_priority *table, struct bhndb_resources *r)
bhndb_init_region_cfg(struct bhndb_softc *sc, bhnd_erom_t *erom,
struct bhndb_resources *br, struct bhnd_core_info *cores, u_int ncores,
const struct bhndb_hw_priority *table)
{
const struct bhndb_hw_priority *hp;
bhnd_addr_t addr;
@ -247,29 +275,40 @@ bhndb_initialize_region_cfg(struct bhndb_softc *sc, device_t *devs, int ndevs,
/*
* Register bridge regions covering all statically mapped ports.
*/
for (int i = 0; i < ndevs; i++) {
for (u_int i = 0; i < ncores; i++) {
const struct bhndb_regwin *regw;
device_t child;
struct bhnd_core_info *core;
struct bhnd_core_match md;
child = devs[i];
core = &cores[i];
md = bhnd_core_get_match_desc(core);
for (regw = r->cfg->register_windows;
for (regw = br->cfg->register_windows;
regw->win_type != BHNDB_REGWIN_T_INVALID; regw++)
{
/* Only core windows are supported */
if (regw->win_type != BHNDB_REGWIN_T_CORE)
continue;
/* Skip non-applicable register windows. */
if (!bhndb_regwin_matches_device(regw, child))
/* Skip non-matching cores. */
if (!bhndb_regwin_match_core(regw, core))
continue;
/* Fetch the base address of the mapped port. */
error = bhnd_get_region_addr(child,
regw->d.core.port_type, regw->d.core.port,
regw->d.core.region, &addr, &size);
if (error)
return (error);
/* Fetch the base address of the mapped port */
error = bhnd_erom_lookup_core_addr(erom, &md,
regw->d.core.port_type,
regw->d.core.port,
regw->d.core.region,
NULL,
&addr,
&size);
if (error) {
/* Skip non-applicable register windows */
if (error == ENOENT)
continue;
return (error);
}
/*
* Always defer to the register window's size.
@ -290,7 +329,7 @@ bhndb_initialize_region_cfg(struct bhndb_softc *sc, device_t *devs, int ndevs,
* The window priority for a statically mapped
* region is always HIGH.
*/
error = bhndb_add_resource_region(r, addr, size,
error = bhndb_add_resource_region(br, addr, size,
BHNDB_PRIORITY_HIGH, regw);
if (error)
return (error);
@ -301,22 +340,24 @@ bhndb_initialize_region_cfg(struct bhndb_softc *sc, device_t *devs, int ndevs,
* Perform priority accounting and register bridge regions for all
* ports defined in the priority table
*/
for (int i = 0; i < ndevs; i++) {
for (u_int i = 0; i < ncores; i++) {
struct bhndb_region *region;
device_t child;
struct bhnd_core_info *core;
struct bhnd_core_match md;
child = devs[i];
core = &cores[i];
md = bhnd_core_get_match_desc(core);
/*
* Skip priority accounting for cores that ...
*/
/* ... do not require bridge resources */
if (bhnd_is_hw_disabled(child) || !device_is_enabled(child))
if (BHNDB_BUS_IS_CORE_DISABLED(sc->parent_dev, sc->dev, core))
continue;
/* ... do not have a priority table entry */
hp = bhndb_hw_priority_find_device(table, child);
hp = bhndb_hw_priority_find_core(table, core);
if (hp == NULL)
continue;
@ -330,27 +371,26 @@ bhndb_initialize_region_cfg(struct bhndb_softc *sc, device_t *devs, int ndevs,
const struct bhndb_port_priority *pp;
pp = &hp->ports[i];
/* Skip ports not defined on this device */
if (!bhnd_is_region_valid(child, pp->type, pp->port,
pp->region))
{
continue;
}
/* Fetch the address+size of the mapped port. */
error = bhnd_get_region_addr(child, pp->type, pp->port,
pp->region, &addr, &size);
if (error)
return (error);
error = bhnd_erom_lookup_core_addr(erom, &md,
pp->type, pp->port, pp->region,
NULL, &addr, &size);
if (error) {
/* Skip ports not defined on this device */
if (error == ENOENT)
continue;
return (error);
}
/* Skip ports with an existing static mapping */
region = bhndb_find_resource_region(r, addr, size);
region = bhndb_find_resource_region(br, addr, size);
if (region != NULL && region->static_regwin != NULL)
continue;
/* Define a dynamic region for this port */
error = bhndb_add_resource_region(r, addr, size,
error = bhndb_add_resource_region(br, addr, size,
pp->priority, NULL);
if (error)
return (error);
@ -375,17 +415,17 @@ bhndb_initialize_region_cfg(struct bhndb_softc *sc, device_t *devs, int ndevs,
/* Determine the minimum priority at which we'll allocate direct
* register windows from our dynamic pool */
size_t prio_total = prio_low + prio_default + prio_high;
if (prio_total <= r->dwa_count) {
if (prio_total <= br->dwa_count) {
/* low+default+high priority regions get windows */
r->min_prio = BHNDB_PRIORITY_LOW;
br->min_prio = BHNDB_PRIORITY_LOW;
} else if (prio_default + prio_high <= r->dwa_count) {
} else if (prio_default + prio_high <= br->dwa_count) {
/* default+high priority regions get windows */
r->min_prio = BHNDB_PRIORITY_DEFAULT;
br->min_prio = BHNDB_PRIORITY_DEFAULT;
} else {
/* high priority regions get windows */
r->min_prio = BHNDB_PRIORITY_HIGH;
br->min_prio = BHNDB_PRIORITY_HIGH;
}
if (BHNDB_DEBUG(PRIO)) {
@ -393,10 +433,10 @@ bhndb_initialize_region_cfg(struct bhndb_softc *sc, device_t *devs, int ndevs,
const char *direct_msg, *type_msg;
bhndb_priority_t prio, prio_min;
prio_min = r->min_prio;
prio_min = br->min_prio;
device_printf(sc->dev, "min_prio: %d\n", prio_min);
STAILQ_FOREACH(region, &r->bus_regions, link) {
STAILQ_FOREACH(region, &br->bus_regions, link) {
prio = region->priority;
direct_msg = prio >= prio_min ? "direct" : "indirect";
@ -418,8 +458,8 @@ bhndb_initialize_region_cfg(struct bhndb_softc *sc, device_t *devs, int ndevs,
* Find a hardware specification for @p dev.
*
* @param sc The bhndb device state.
* @param devs All devices enumerated on the bridged bhnd bus.
* @param ndevs The length of @p devs.
* @param cores All cores enumerated on the bridged bhnd bus.
* @param ncores The length of @p cores.
* @param[out] hw On success, the matched hardware specification.
* with @p dev.
*
@ -427,15 +467,15 @@ bhndb_initialize_region_cfg(struct bhndb_softc *sc, device_t *devs, int ndevs,
* @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 */
@ -446,70 +486,16 @@ bhndb_find_hwspec(struct bhndb_softc *sc, device_t *devs, int ndevs,
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
@ -521,6 +507,7 @@ 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);
@ -530,30 +517,48 @@ bhndb_attach(device_t dev, bhnd_devclass_t 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);
@ -564,55 +569,265 @@ bhndb_attach(device_t dev, bhnd_devclass_t bridge_devclass)
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_hw *hw;
struct bhndb_resources *r;
device_t *devs;
device_t hostb;
int ndevs;
int error;
const struct bhndb_hwcfg *cfg;
struct bhndb_dw_alloc *dwa;
struct resource *res;
const struct bhndb_regwin *win;
sc = device_get_softc(dev);
hostb = NULL;
BHNDB_LOCK_ASSERT(sc, MA_OWNED);
/* 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);
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;
}
/* Find our host bridge device */
hostb = BHNDB_FIND_HOSTB_DEVICE(dev, child);
if (hostb == NULL) {
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;
u_int ncores;
int error;
erom = NULL;
cores = NULL;
br = NULL;
/* Allocate EROM parser instance */
erom = bhnd_erom_alloc(eromcls, &sc->chipid, sc->bus_dev, 0);
if (erom == NULL) {
device_printf(sc->dev, "failed to allocate device enumeration "
"table parser\n");
return (ENXIO);
}
/* Look for our host bridge core */
if ((error = bhndb_find_hostb_core(sc, erom, &sc->bridge_core))) {
device_printf(sc->dev, "no host bridge core found\n");
error = ENODEV;
goto cleanup;
} else {
sc->have_br_core = true;
}
/* Fetch the bridged device's core table */
if ((error = bhnd_erom_get_core_table(erom, &cores, &ncores))) {
device_printf(sc->dev, "error fetching core table: %d\n",
error);
goto cleanup;
}
/* Find our full register window configuration */
if ((error = bhndb_find_hwspec(sc, devs, ndevs, &hw))) {
if ((error = bhndb_find_hwspec(sc, cores, ncores, &hw))) {
device_printf(sc->dev, "unable to identify device, "
" using generic bridge resource definitions\n");
" using generic bridge resource definitions\n");
error = 0;
goto cleanup;
}
@ -620,34 +835,59 @@ bhndb_generic_init_full_config(device_t dev, device_t child,
if (bootverbose || BHNDB_DEBUG(PRIO))
device_printf(sc->dev, "%s resource configuration\n", hw->name);
/* Release existing resource state */
BHNDB_LOCK(sc);
bhndb_free_resources(sc->bus_res);
sc->bus_res = NULL;
BHNDB_UNLOCK(sc);
/* Allocate new resource state */
r = bhndb_alloc_resources(dev, sc->parent_dev, hw->cfg);
if (r == NULL) {
error = ENXIO;
/* Allocate new bridge resource state using the discovered hardware
* configuration */
br = bhndb_alloc_resources(sc->dev, sc->parent_dev, hw->cfg);
if (br == NULL) {
device_printf(sc->dev,
"failed to allocate new resource state\n");
error = ENOMEM;
goto cleanup;
}
/* Initialize our resource priority configuration */
error = bhndb_initialize_region_cfg(sc, devs, ndevs, hw_prio_table, r);
/* Populate our resource priority configuration */
hwprio = BHNDB_BUS_GET_HARDWARE_PRIO(sc->parent_dev, sc->dev);
error = bhndb_init_region_cfg(sc, erom, br, cores, ncores, hwprio);
if (error) {
bhndb_free_resources(r);
device_printf(sc->dev, "failed to initialize resource "
"priority configuration: %d\n", error);
goto cleanup;
}
/* Update our bridge state */
BHNDB_LOCK(sc);
sc->bus_res = r;
sc->hostb_dev = hostb;
BHNDB_UNLOCK(sc);
/* The EROM parser holds a reference to the resource state we're
* about to invalidate */
bhnd_erom_free_core_table(erom, cores);
bhnd_erom_free(erom);
cores = NULL;
erom = NULL;
/* Replace existing resource state */
bhndb_free_resources(sc->bus_res);
sc->bus_res = br;
/* Pointer is now owned by sc->bus_res */
br = NULL;
/* Re-allocate host resources */
if ((error = bhndb_alloc_host_resources(sc->bus_res))) {
device_printf(sc->dev, "failed to reallocate bridge host "
"resources: %d\n", error);
goto cleanup;
}
return (0);
cleanup:
free(devs, M_TEMP);
if (cores != NULL)
bhnd_erom_free_core_table(erom, cores);
if (erom != NULL)
bhnd_erom_free(erom);
if (br != NULL)
bhndb_free_resources(br);
return (error);
}
@ -928,21 +1168,17 @@ bhndb_get_chipid(device_t dev, device_t child)
/**
* Default implementation of BHNDB_IS_HW_DISABLED().
* Default implementation of BHND_BUS_IS_HW_DISABLED().
*/
static bool
bhndb_is_hw_disabled(device_t dev, device_t child) {
bhndb_is_hw_disabled(device_t dev, device_t child)
{
struct bhndb_softc *sc;
struct bhnd_core_info *bridge_core;
struct bhnd_core_info core;
sc = device_get_softc(dev);
/* Requestor must be attached to the bhnd bus */
if (device_get_parent(child) != sc->bus_dev) {
return (BHND_BUS_IS_HW_DISABLED(device_get_parent(dev), child));
}
/* Fetch core info */
core = bhnd_get_core_info(child);
/* Try to defer to the bhndb bus parent */
@ -951,78 +1187,27 @@ bhndb_is_hw_disabled(device_t dev, device_t child) {
/* 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);
}
/**
@ -1681,7 +1866,14 @@ bhndb_io_resource_slow(struct bhndb_softc *sc, bus_addr_t addr,
}
/**
* Find the bridge resource to be used for I/O requests.
* Return a borrowed reference to a bridge resource allocation record capable
* of handling bus I/O requests of @p size at @p addr.
*
* This will either return a reference to an existing allocation
* record mapping the requested space, or will configure and return a free
* allocation record.
*
* Will panic if a usable record cannot be found.
*
* @param sc Bridge driver state.
* @param addr The I/O target address.
@ -1961,8 +2153,7 @@ static device_method_t bhndb_methods[] = {
/* 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),

View File

@ -1,5 +1,5 @@
#-
# Copyright (c) 2015 Landon Fuller <landon@landonf.org>
# Copyright (c) 2015-2016 Landon Fuller <landonf@FreeBSD.org>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
@ -59,6 +59,12 @@ CODE {
{
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,
@ -108,6 +114,18 @@ METHOD const struct bhndb_hw * get_hardware_table {
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.

View File

@ -61,18 +61,12 @@ CODE {
{
panic("bhndb_populate_board_info unimplemented");
}
static int
bhndb_null_init_full_config(device_t dev, device_t child,
const struct bhndb_hw_priority *priority_table)
{
panic("bhndb_init_full_config unimplemented");
}
static device_t
bhndb_null_find_hostb_device(device_t dev, device_t child)
static int
bhndb_null_get_hostb_core(device_t dev, device_t child,
struct bhnd_core_info *core)
{
panic("bhndb_find_hostb_device unimplemented");
panic("bhndb_get_hostb_core unimplemented");
}
static void
@ -124,39 +118,23 @@ METHOD int populate_board_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

View File

@ -62,6 +62,8 @@ __FBSDID("$FreeBSD$");
#include "bhndb_pcivar.h"
#include "bhndb_private.h"
static int bhndb_pci_add_children(struct bhndb_pci_softc *sc);
static int bhndb_enable_pci_clocks(struct bhndb_pci_softc *sc);
static int bhndb_disable_pci_clocks(struct bhndb_pci_softc *sc);
@ -110,93 +112,119 @@ bhndb_pci_attach(device_t dev)
sc = device_get_softc(dev);
sc->dev = dev;
sc->parent = device_get_parent(dev);
/* 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, &reg) == 0)
sc->pci_devclass = BHND_DEVCLASS_PCIE;
/* Enable clocks (if supported by this hardware) */
if ((error = bhndb_enable_pci_clocks(sc)))
return (error);
/* Use siba(4)-compatible regwin handling until we know
* what kind of bus is attached */
sc->set_regwin = bhndb_pci_compat_setregwin;
/* 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. */
if ((error = bhndb_attach(dev, sc->pci_devclass)))
return (error);
if (pci_find_cap(sc->parent, PCIY_EXPRESS, &reg) == 0)
sc->pci_devclass = BHND_DEVCLASS_PCIE;
else
sc->pci_devclass = BHND_DEVCLASS_PCI;
/* If supported, switch to the faster regwin handling */
/* Enable clocks (if required by this hardware) */
if ((error = bhndb_enable_pci_clocks(sc)))
goto cleanup;
/* Perform bridge attach, fully initializing the bridge
* configuration. */
if ((error = bhndb_attach(dev, sc->pci_devclass)))
goto cleanup;
/* 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);
}
return (0);
}
static int
bhndb_pci_init_full_config(device_t dev, device_t child,
const struct bhndb_hw_priority *hw_prio_table)
{
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)))
return (error);
/* Enable PCI bus mastering */
pci_enable_busmaster(sc->parent);
/* Fix-up power on defaults for SROM-less devices. */
bhndb_init_sromless_pci_config(sc);
/* If SPROM is mapped directly into BAR0, add NVRAM device. */
/* 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_detach(device_t dev)
{
struct bhndb_pci_softc *sc;
int error;
sc = device_get_softc(dev);
/* Attempt to detach our children */
if ((error = bus_generic_detach(dev)))
return (error);
/* Perform generic bridge detach */
if ((error = bhndb_generic_detach(dev)))
return (error);
/* Disable clocks (if required by this hardware) */
if ((error = bhndb_disable_pci_clocks(sc)))
return (error);
/* Disable PCI bus mastering */
pci_disable_busmaster(sc->parent);
return (0);
}
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,
error = bus_set_resource(child, SYS_RES_MEMORY, 0,
bhndb_pci_sprom_addr(sc), nv_sz);
if (error) {
device_printf(dev,
device_printf(sc->dev,
"failed to register sprom resources\n");
return (error);
}
/* Attach the device */
if ((error = device_probe_and_attach(nv_dev))) {
device_printf(dev, "sprom attach failed\n");
return (error);
}
}
return (0);
@ -298,18 +326,27 @@ 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;
}
if (hostb_core.vendor != BHND_MFGID_BCM)
return;
switch (bhnd_get_device(sc->bhndb.hostb_dev)) {
switch (hostb_core.device) {
case BHND_COREID_PCI:
srom_offset = BHND_PCI_SRSH_PI_OFFSET;
break;
@ -342,7 +379,7 @@ bhndb_init_sromless_pci_config(struct bhndb_pci_softc *sc)
/* 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);
@ -383,28 +420,6 @@ bhndb_pci_suspend(device_t dev)
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)
@ -678,7 +693,6 @@ static device_method_t bhndb_pci_methods[] = {
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),

View File

@ -63,6 +63,9 @@ 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);
@ -126,13 +129,13 @@ const struct bhndb_regwin *bhndb_regwin_find_best(
bhnd_port_type port_type, u_int port,
u_int region, bus_size_t min_size);
bool bhndb_regwin_matches_device(
bool bhndb_regwin_match_core(
const struct bhndb_regwin *regw,
device_t dev);
struct bhnd_core_info *core);
const struct bhndb_hw_priority *bhndb_hw_priority_find_device(
const struct bhndb_hw_priority *bhndb_hw_priority_find_core(
const struct bhndb_hw_priority *table,
device_t device);
struct bhnd_core_info *core);
/**
@ -175,8 +178,9 @@ struct bhndb_resources {
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 */

View File

@ -196,6 +196,8 @@ 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];
@ -230,6 +232,8 @@ bhndb_find_regwin_resource(struct bhndb_resources *br,
{
const struct resource_spec *rspecs;
KASSERT(br->res_avail, ("no host resources allocated"));
rspecs = br->cfg->resource_specs;
for (u_int i = 0; rspecs[i].type != -1; i++) {
if (win->res.type != rspecs[i].type)
@ -250,11 +254,11 @@ bhndb_find_regwin_resource(struct bhndb_resources *br,
}
/**
* Allocate and initialize a new resource state structure, allocating
* bus resources from @p parent_dev according to @p cfg.
* Allocate and initialize a new resource state structure.
*
* @param dev The bridge device.
* @param parent_dev The parent device from which resources will be allocated.
* @param parent_dev The parent device from which host resources should be
* allocated.
* @param cfg The hardware configuration to be used.
*/
struct bhndb_resources *
@ -264,13 +268,10 @@ bhndb_alloc_resources(device_t dev, device_t parent_dev,
struct bhndb_resources *r;
const struct bhndb_regwin *win;
bus_size_t last_window_size;
size_t res_num;
int rnid;
int error;
bool free_parent_res;
bool free_ht_mem, free_br_mem;
free_parent_res = false;
free_ht_mem = false;
free_br_mem = false;
@ -315,62 +316,6 @@ bhndb_alloc_resources(device_t dev, device_t parent_dev,
goto failed;
}
/* Determine our bridge resource count from the hardware config. */
res_num = 0;
for (size_t i = 0; cfg->resource_specs[i].type != -1; i++)
res_num++;
/* Allocate space for a non-const copy of our resource_spec
* table; this will be updated with the RIDs assigned by
* bus_alloc_resources. */
r->res_spec = malloc(sizeof(r->res_spec[0]) * (res_num + 1), M_BHND,
M_NOWAIT);
if (r->res_spec == NULL)
goto failed;
/* Initialize and terminate the table */
for (size_t i = 0; i < res_num; i++)
r->res_spec[i] = cfg->resource_specs[i];
r->res_spec[res_num].type = -1;
/* Allocate space for our resource references */
r->res = malloc(sizeof(r->res[0]) * res_num, M_BHND, M_NOWAIT);
if (r->res == NULL)
goto failed;
/* Allocate resources */
error = bus_alloc_resources(r->parent_dev, r->res_spec, r->res);
if (error) {
device_printf(r->dev,
"could not allocate bridge resources on %s: %d\n",
device_get_nameunit(r->parent_dev), error);
goto failed;
} else {
free_parent_res = true;
}
/* Add allocated memory resources to our host memory resource manager */
for (u_int i = 0; r->res_spec[i].type != -1; i++) {
struct resource *res;
/* skip non-memory resources */
if (r->res_spec[i].type != SYS_RES_MEMORY)
continue;
/* add host resource to set of managed regions */
res = r->res[i];
error = rman_manage_region(&r->ht_mem_rman, rman_get_start(res),
rman_get_end(res));
if (error) {
device_printf(r->dev,
"could not register host memory region with "
"ht_mem_rman: %d\n", error);
goto failed;
}
}
/* Fetch the dynamic regwin count and verify that it does not exceed
* what is representable via our freelist bitstring. */
r->dwa_count = bhndb_regwin_count(cfg->register_windows,
@ -432,16 +377,109 @@ bhndb_alloc_resources(device_t dev, device_t parent_dev,
dwa->target = 0x0;
LIST_INIT(&dwa->refs);
rnid++;
}
return (r);
failed:
if (free_ht_mem)
rman_fini(&r->ht_mem_rman);
if (free_br_mem)
rman_fini(&r->br_mem_rman);
if (r->dw_alloc != NULL)
free(r->dw_alloc, M_BHND);
if (r->dwa_freelist != NULL)
free(r->dwa_freelist, M_BHND);
free(r, M_BHND);
return (NULL);
}
/**
* Allocate host resources required by @p br, and initialize
* internal BHNDB_ADDRSPACE_NATIVE resource manager state.
*
* @param br Resource state.
*/
int
bhndb_alloc_host_resources(struct bhndb_resources *br)
{
size_t res_num;
int error;
KASSERT(!br->res_avail, ("host resources already allocated"));
/* Determine our bridge resource count from the hardware config. */
res_num = 0;
for (size_t i = 0; br->cfg->resource_specs[i].type != -1; i++)
res_num++;
/* Allocate space for a non-const copy of our resource_spec
* table; this will be updated with the RIDs assigned by
* bus_alloc_resources. */
br->res_spec = malloc(sizeof(br->res_spec[0]) * (res_num + 1), M_BHND,
M_NOWAIT);
if (br->res_spec == NULL) {
error = ENOMEM;
goto failed;
}
/* Initialize and terminate the table */
for (size_t i = 0; i < res_num; i++)
br->res_spec[i] = br->cfg->resource_specs[i];
br->res_spec[res_num].type = -1;
/* Allocate space for our resource references */
br->res = malloc(sizeof(br->res[0]) * res_num, M_BHND, M_NOWAIT);
if (br->res == NULL) {
error = ENOMEM;
goto failed;
}
/* Allocate host resources */
error = bus_alloc_resources(br->parent_dev, br->res_spec, br->res);
if (error) {
device_printf(br->dev,
"could not allocate bridge resources on %s: %d\n",
device_get_nameunit(br->parent_dev), error);
goto failed;
} else {
br->res_avail = true;
}
/* Populate (and validate) parent resource references for all
* dynamic windows */
for (size_t i = 0; i < br->dwa_count; i++) {
struct bhndb_dw_alloc *dwa;
const struct bhndb_regwin *win;
dwa = &br->dw_alloc[i];
win = dwa->win;
/* Find and validate corresponding resource. */
dwa->parent_res = bhndb_find_regwin_resource(r, win);
if (dwa->parent_res == NULL)
dwa->parent_res = bhndb_find_regwin_resource(br, win);
if (dwa->parent_res == NULL) {
device_printf(br->dev, "no host resource found for %u "
"register window with offset %#jx and "
"size %#jx\n",
win->win_type,
(uintmax_t)win->win_offset,
(uintmax_t)win->win_size);
error = ENXIO;
goto failed;
}
if (rman_get_size(dwa->parent_res) < win->win_offset +
win->win_size)
{
device_printf(r->dev, "resource %d too small for "
device_printf(br->dev, "resource %d too small for "
"register window with offset %llx and size %llx\n",
rman_get_rid(dwa->parent_res),
(unsigned long long) win->win_offset,
@ -450,37 +488,41 @@ bhndb_alloc_resources(device_t dev, device_t parent_dev,
error = EINVAL;
goto failed;
}
rnid++;
}
return (r);
/* 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 (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);
}
/**
@ -510,7 +552,8 @@ bhndb_free_resources(struct bhndb_resources *br)
}
/* Release resources allocated through our parent. */
bus_release_resources(br->parent_dev, br->res_spec, br->res);
if (br->res_avail)
bus_release_resources(br->parent_dev, br->res_spec, br->res);
/* Clean up resource reservations */
for (size_t i = 0; i < br->dwa_count; i++) {
@ -533,8 +576,12 @@ bhndb_free_resources(struct bhndb_resources *br)
rman_fini(&br->br_mem_rman);
/* Free backing resource state structures */
free(br->res, M_BHND);
free(br->res_spec, M_BHND);
if (br->res != NULL)
free(br->res, M_BHND);
if (br->res_spec != NULL)
free(br->res_spec, M_BHND);
free(br->dw_alloc, M_BHND);
free(br->dwa_freelist, M_BHND);
}
@ -988,33 +1035,27 @@ bhndb_regwin_find_best(const struct bhndb_regwin *table,
}
/**
* 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);
@ -1022,22 +1063,19 @@ bhndb_regwin_matches_device(const struct bhndb_regwin *regw, device_t dev)
/**
* 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);
}

View File

@ -89,12 +89,12 @@ 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 */

View File

@ -25,12 +25,6 @@
#ifndef _BHND_CORES_CHIPC_CHIPCREG_H_
#define _BHND_CORES_CHIPC_CHIPCREG_H_
#define CHIPC_CHIPID_SIZE 0x100 /**< size of the register block
containing the chip
identification registers
required during bus
enumeration */
/** Evaluates to true if the given ChipCommon core revision supports
* the CHIPC_CORECTRL register */
#define CHIPC_HWREV_HAS_CORECTRL(hwrev) ((hwrev) >= 1)

View File

@ -44,6 +44,12 @@ __FBSDID("$FreeBSD$");
#include "sibareg.h"
#include "sibavar.h"
static bhnd_erom_class_t *
siba_get_erom_class(driver_t *driver)
{
return (&siba_erom_parser);
}
int
siba_probe(device_t dev)
{
@ -51,84 +57,29 @@ siba_probe(device_t dev)
return (BUS_PROBE_DEFAULT);
}
/**
* Default siba(4) bus driver implementation of DEVICE_ATTACH().
*
* This implementation initializes internal siba(4) state and performs
* bus enumeration, and must be called by subclassing drivers in
* DEVICE_ATTACH() before any other bus methods.
*/
int
siba_attach(device_t dev)
{
struct siba_devinfo *dinfo;
struct siba_softc *sc;
device_t *devs;
int ndevs;
int error;
sc = device_get_softc(dev);
sc->dev = dev;
/* Fetch references to the siba SIBA_CFG* blocks for all
* registered devices */
if ((error = device_get_children(dev, &devs, &ndevs)))
/* Enumerate children */
if ((error = siba_add_children(dev))) {
device_delete_children(dev);
return (error);
for (int i = 0; i < ndevs; i++) {
struct siba_addrspace *addrspace;
dinfo = device_get_ivars(devs[i]);
KASSERT(!device_is_suspended(devs[i]),
("siba(4) stateful suspend handling requires that devices "
"not be suspended before siba_attach()"));
/* Fetch the core register address space */
addrspace = siba_find_addrspace(dinfo, BHND_PORT_DEVICE, 0, 0);
if (addrspace == NULL) {
device_printf(dev,
"missing device registers for core %d\n", i);
error = ENXIO;
goto cleanup;
}
/*
* Map the per-core configuration blocks
*/
KASSERT(dinfo->core_id.num_cfg_blocks <= SIBA_MAX_CFG,
("config block count %u out of range",
dinfo->core_id.num_cfg_blocks));
for (u_int cfgidx = 0; cfgidx < dinfo->core_id.num_cfg_blocks;
cfgidx++)
{
rman_res_t r_start, r_count, r_end;
/* Determine the config block's address range; configuration
* blocks are allocated starting at SIBA_CFG0_OFFSET,
* growing downwards. */
r_start = addrspace->sa_base + SIBA_CFG0_OFFSET;
r_start -= cfgidx * SIBA_CFG_SIZE;
r_count = SIBA_CFG_SIZE;
r_end = r_start + r_count - 1;
/* Allocate the config resource */
dinfo->cfg_rid[cfgidx] = 0;
dinfo->cfg[cfgidx] = BHND_BUS_ALLOC_RESOURCE(dev, dev,
SYS_RES_MEMORY, &dinfo->cfg_rid[cfgidx], r_start,
r_end, r_count, RF_ACTIVE);
if (dinfo->cfg[cfgidx] == NULL) {
device_printf(dev, "failed allocating CFG_%u for "
"core %d\n", cfgidx, i);
error = ENXIO;
goto cleanup;
}
}
}
cleanup:
free(devs, M_BHND);
if (error)
return (error);
/* Delegate remainder to standard bhnd method implementation */
return (bhnd_generic_attach(dev));
return (0);
}
int
@ -213,15 +164,6 @@ siba_get_resource_list(device_t dev, device_t 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)
{
@ -492,6 +434,64 @@ siba_register_addrspaces(device_t dev, struct siba_devinfo *di,
return (0);
}
/**
* Map per-core configuration blocks for @p dinfo.
*
* @param dev The siba bus device.
* @param dinfo The device info instance on which to map all per-core
* configuration blocks.
*/
static int
siba_map_cfg_resources(device_t dev, struct siba_devinfo *dinfo)
{
struct siba_addrspace *addrspace;
rman_res_t r_start, r_count, r_end;
uint8_t num_cfg;
num_cfg = dinfo->core_id.num_cfg_blocks;
if (num_cfg > SIBA_MAX_CFG) {
device_printf(dev, "config block count %hhu out of range\n",
num_cfg);
return (ENXIO);
}
/* Fetch the core register address space */
addrspace = siba_find_addrspace(dinfo, BHND_PORT_DEVICE, 0, 0);
if (addrspace == NULL) {
device_printf(dev, "missing device registers\n");
return (ENXIO);
}
/*
* Map the per-core configuration blocks
*/
for (uint8_t i = 0; i < num_cfg; i++) {
/* Determine the config block's address range; configuration
* blocks are allocated starting at SIBA_CFG0_OFFSET,
* growing downwards. */
r_start = addrspace->sa_base + SIBA_CFG0_OFFSET;
r_start -= i * SIBA_CFG_SIZE;
r_count = SIBA_CFG_SIZE;
r_end = r_start + r_count - 1;
/* Allocate the config resource */
dinfo->cfg_rid[i] = SIBA_CFG_RID(dinfo, i);
dinfo->cfg[i] = BHND_BUS_ALLOC_RESOURCE(dev, dev,
SYS_RES_MEMORY, &dinfo->cfg_rid[i], r_start, r_end,
r_count, RF_ACTIVE);
if (dinfo->cfg[i] == NULL) {
device_printf(dev, "failed to allocate SIBA_CFG%hhu\n",
i);
return (ENXIO);
}
}
return (0);
}
static struct bhnd_devinfo *
siba_alloc_bhnd_dinfo(device_t dev)
{
@ -510,73 +510,22 @@ siba_free_bhnd_dinfo(device_t dev, struct bhnd_devinfo *dinfo)
* the bus.
*
* @param dev The siba bus device.
* @param chipid The chip identifier, if the device does not provide a
* ChipCommon core. Should o NULL otherwise.
*/
int
siba_add_children(device_t dev, const struct bhnd_chipid *chipid)
siba_add_children(device_t dev)
{
struct bhnd_chipid ccid;
struct bhnd_core_info *cores;
struct siba_devinfo *dinfo;
struct bhnd_resource *r;
int rid;
int error;
const struct bhnd_chipid *chipid;
struct bhnd_core_info *cores;
struct siba_devinfo *dinfo;
struct bhnd_resource *r;
int rid;
int error;
dinfo = NULL;
cores = NULL;
r = NULL;
/*
* Try to determine the number of device cores via the ChipCommon
* identification registers.
*
* A small number of very early devices do not include a ChipCommon
* core, in which case our caller must supply the chip identification
* information via a non-NULL chipid parameter.
*/
if (chipid == NULL) {
uint32_t idhigh, ccreg;
uint16_t vendor, device;
uint8_t ccrev;
/* Map the first core's register block. If the ChipCommon core
* exists, it will always be the first core. */
rid = 0;
r = bhnd_alloc_resource(dev, SYS_RES_MEMORY, &rid,
SIBA_CORE_ADDR(0), SIBA_CORE_SIZE,
SIBA_CORE_ADDR(0) + SIBA_CORE_SIZE - 1,
RF_ACTIVE);
/* Identify the core */
idhigh = bhnd_bus_read_4(r, SB0_REG_ABS(SIBA_CFG0_IDHIGH));
vendor = SIBA_REG_GET(idhigh, IDH_VENDOR);
device = SIBA_REG_GET(idhigh, IDH_DEVICE);
ccrev = SIBA_IDH_CORE_REV(idhigh);
if (vendor != OCP_VENDOR_BCM || device != BHND_COREID_CC) {
device_printf(dev,
"cannot identify device: no chipcommon core "
"found\n");
error = ENXIO;
goto cleanup;
}
/* Identify the chipset */
ccreg = bhnd_bus_read_4(r, CHIPC_ID);
ccid = bhnd_parse_chipid(ccreg, SIBA_ENUM_ADDR);
/* Fix up the core count */
error = bhnd_chipid_fixed_ncores(&ccid, ccrev, &ccid.ncores);
if (error) {
device_printf(dev, "unable to determine core count for "
"chipset 0x%hx\n", ccid.chip_id);
goto cleanup;
}
chipid = &ccid;
bhnd_release_resource(dev, SYS_RES_MEMORY, rid, r);
}
chipid = BHND_BUS_GET_CHIPID(dev, dev);
/* Allocate our temporary core table and enumerate all cores */
cores = malloc(sizeof(*cores) * chipid->ncores, M_BHND, M_NOWAIT);
@ -636,14 +585,19 @@ siba_add_children(device_t dev, const struct bhnd_chipid *chipid)
if ((error = siba_register_addrspaces(dev, dinfo, r)))
goto cleanup;
/* Release our resource covering the register blocks
* we're about to map */
bhnd_release_resource(dev, SYS_RES_MEMORY, rid, r);
r = NULL;
/* Map the core's config blocks */
if ((error = siba_map_cfg_resources(dev, dinfo)))
goto cleanup;
/* If pins are floating or the hardware is otherwise
* unpopulated, the device shouldn't be used. */
if (bhnd_is_hw_disabled(child))
device_disable(child);
/* Release our resource */
bhnd_release_resource(dev, SYS_RES_MEMORY, rid, r);
r = NULL;
/* Issue bus callback for fully initialized child. */
BHND_BUS_CHILD_ADDED(dev, child);
@ -673,7 +627,7 @@ static device_method_t siba_methods[] = {
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),

View File

@ -101,35 +101,28 @@ siba_bhndb_probe(device_t dev)
static int
siba_bhndb_attach(device_t dev)
{
struct siba_softc *sc;
const struct bhnd_chipid *chipid;
int error;
struct siba_softc *sc;
int error;
sc = device_get_softc(dev);
/* Enumerate our children. */
chipid = BHNDB_GET_CHIPID(device_get_parent(dev), dev);
if ((error = siba_add_children(dev, chipid)))
return (error);
/* Initialize full bridge configuration */
error = BHNDB_INIT_FULL_CONFIG(device_get_parent(dev), dev,
bhndb_siba_priority_table);
if (error)
return (error);
/* Ask our parent bridge to find the corresponding bridge core */
sc->hostb_dev = BHNDB_FIND_HOSTB_DEVICE(device_get_parent(dev), dev);
/* Call our superclass' implementation */
/* Perform initial attach and enumerate our children. */
if ((error = siba_attach(dev)))
return (error);
goto failed;
/* Apply attach/resume work-arounds */
/* Apply attach/resume workarounds before any child drivers attach */
if ((error = siba_bhndb_wars_hwup(sc)))
return (error);
goto failed;
/* Delegate remainder to standard bhnd method implementation */
if ((error = bhnd_generic_attach(dev)))
goto failed;
return (0);
failed:
device_delete_children(dev);
return (error);
}
static int
@ -222,11 +215,15 @@ 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 */
@ -256,10 +253,14 @@ siba_bhndb_wars_pcie_clear_d11_timeout(struct siba_softc *sc)
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) {

View File

@ -46,117 +46,110 @@ __FBSDID("$FreeBSD$");
#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;
/* Identify the chipcommon core */
ccid = siba_erom_parse_core_id(sc, 0, 0);
if (ccid.core_info.vendor != BHND_MFGID_BCM ||
ccid.core_info.device != BHND_COREID_CC)
{
EROM_LOG(sc,
"first core not chipcommon (vendor=%#hx, core=%#hx)\n",
ccid.core_info.vendor, ccid.core_info.device);
return (ENXIO);
}
/* Identify the chipset */
idreg = siba_erom_read_4(sc, 0, CHIPC_ID);
*cid = bhnd_parse_chipid(idreg, enum_addr);
/* Fix up the core count in-place */
return (bhnd_chipid_fixed_ncores(cid, ccid.core_info.hwrev,
&cid->ncores));
}
static int
siba_erom_init_common(struct siba_erom *sc)
{
struct bhnd_chipid cid;
int error;
/* There's always at least one core */
sc->ncores = 1;
/* Try using the provided hint. */
if (hint != NULL) {
struct siba_core_id sid;
/* Identify the chipset */
if ((error = siba_erom_read_chipid(sc, SIBA_ENUM_ADDR, &cid)))
return (error);
/* Validate bus type */
if (hint->chip_type != BHND_CHIPTYPE_SIBA)
return (ENXIO);
/* Verify the chip type */
if (cid.chip_type != BHND_CHIPTYPE_SIBA)
return (ENXIO);
/*
* Verify the first core's IDHIGH/IDLOW identification.
*
* The core must be a Broadcom core, but must *not* be
* a chipcommon core; those shouldn't be hinted.
*
* The first core on EXTIF-equipped devices varies, but on the
* BCM4710, it's a SDRAM core (0x803).
*/
sid = siba_eio_read_core_id(io, 0, 0);
if (sid.core_info.vendor != BHND_MFGID_BCM)
return (ENXIO);
if (sid.core_info.device == BHND_COREID_CC)
return (EINVAL);
*cid = *hint;
} else {
/* Validate bus type */
idreg = siba_eio_read_4(io, 0, CHIPC_ID);
if (CHIPC_GET_BITS(idreg, CHIPC_ID_BUS) != BHND_CHIPTYPE_SIBA)
return (ENXIO);
/* Identify the chipset */
if ((error = siba_eio_read_chipid(io, SIBA_ENUM_ADDR, cid)))
return (error);
/* Verify the chip type */
if (cid->chip_type != BHND_CHIPTYPE_SIBA)
return (ENXIO);
}
/*
* gcc hack: ensure bhnd_chipid.ncores cannot exceed SIBA_MAX_CORES
@ -165,91 +158,213 @@ siba_erom_init_common(struct siba_erom *sc)
* 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);
if (chip_type != BHND_CHIPTYPE_SIBA)
return (ENXIO);
/* 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)))
/* Initialize I/O context, assuming at least 1 core exists. */
if ((error = siba_eio_init_static(&io, bst, bsh, 0, 1)))
return (error);
/* 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);
if (error)
return (error);
return (BUS_PROBE_DEFAULT);
return (siba_erom_probe_common(&io, hint, cid));
}
/* SIBA implementation of BHND_EROM_INIT() */
static int
siba_erom_init_static(bhnd_erom_t *erom, bus_space_tag_t bst,
bus_space_handle_t bsh)
siba_erom_init(bhnd_erom_t *erom, const struct bhnd_chipid *cid,
device_t parent, int rid)
{
struct siba_erom *sc = (struct siba_erom *)erom;
struct siba_erom *sc;
struct bhnd_resource *res;
int error;
sc = (struct siba_erom *)erom;
sc->dev = NULL;
sc->rid = -1;
sc->res = NULL;
sc->bst = bst;
sc->bsh = bsh;
/* 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);
return (siba_erom_init_common(sc));
/* Initialize I/O context */
error = siba_eio_init(&sc->io, parent, res, rid, 0x0, cid->ncores);
if (error)
bhnd_release_resource(parent, SYS_RES_MEMORY, rid, res);
return (error);
}
/* SIBA implementation of BHND_EROM_INIT_STATIC() */
static int
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;
sc = (struct siba_erom *)erom;
/* 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)
@ -264,12 +379,12 @@ siba_erom_lookup_core(bhnd_erom_t *erom, const struct bhnd_core_match *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 */
@ -278,7 +393,7 @@ siba_erom_lookup_core(bhnd_erom_t *erom, const struct bhnd_core_match *desc,
/* Re-scan preceding cores to determine the unit number. */
for (u_int j = 0; j < i; j++) {
sid = siba_erom_parse_core_id(sc, i, 0);
sid = siba_eio_read_core_id(&sc->io, i, 0);
/* Bump the unit number? */
if (sid.core_info.vendor == ci.vendor &&
@ -319,7 +434,7 @@ siba_erom_lookup_core_addr(bhnd_erom_t *erom, const struct bhnd_core_match *desc
return (error);
/* Fetch full siba core ident */
sid = siba_erom_parse_core_id(sc, core.core_idx, core.unit);
sid = siba_eio_read_core_id(&sc->io, core.core_idx, core.unit);
/* Is port valid? */
if (!siba_is_port_valid(sid.num_addrspace, type, port))
@ -343,7 +458,7 @@ siba_erom_lookup_core_addr(bhnd_erom_t *erom, const struct bhnd_core_match *desc
}
/* Read and parse the address match register */
am = siba_erom_read_4(sc, core.core_idx, am_offset);
am = siba_eio_read_4(&sc->io, core.core_idx, am_offset);
if ((error = siba_parse_admatch(am, &am_addr, &am_size))) {
printf("failed to decode address match register value 0x%x\n",
@ -371,19 +486,19 @@ siba_erom_get_core_table(bhnd_erom_t *erom, struct bhnd_core_info **cores,
sc = (struct siba_erom *)erom;
/* Allocate our core array */
out = malloc(sizeof(*out) * sc->ncores, M_BHND, M_NOWAIT);
out = malloc(sizeof(*out) * sc->io.ncores, M_BHND, M_NOWAIT);
if (out == NULL)
return (ENOMEM);
*cores = out;
*num_cores = sc->ncores;
*num_cores = sc->io.ncores;
/* Enumerate all cores. */
for (u_int i = 0; i < sc->ncores; i++) {
for (u_int i = 0; i < sc->io.ncores; i++) {
struct siba_core_id sid;
/* Read the core info */
sid = siba_erom_parse_core_id(sc, i, 0);
sid = siba_eio_read_core_id(&sc->io, i, 0);
out[i] = sid.core_info;
/* Determine unit number */
@ -405,6 +520,7 @@ siba_erom_free_core_table(bhnd_erom_t *erom, struct bhnd_core_info *cores)
}
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),

View File

@ -84,18 +84,21 @@ siba_nexus_probe(device_t dev)
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 *

View File

@ -60,8 +60,7 @@ 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,
@ -100,6 +99,11 @@ int siba_parse_admatch(uint32_t am, uint32_t *addr,
#define SIBA_MAX_CFG SIBA_CFG_NUM_2_3 /**< maximum number of supported config
register blocks */
#define SIBA_CFG_RID_BASE 100 /**< base resource ID for SIBA_CFG* register allocations */
#define SIBA_CFG_RID(_dinfo, _cfg) \
(SIBA_CFG_RID_BASE + (_cfg) + \
(_dinfo->core_id.core_info.core_idx * SIBA_MAX_CFG))
/* Sonics/OCP address space mappings */
#define SIBA_CORE_ADDRSPACE 0 /**< Address space mapping the primary
device registers */
@ -155,7 +159,6 @@ struct siba_devinfo {
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_ */

View File

@ -1,5 +1,5 @@
/*-
* Copyright (c) 2015 Landon Fuller <landon@landonf.org>
* Copyright (c) 2015-2016 Landon Fuller <landonf@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -42,6 +42,7 @@ __FBSDID("$FreeBSD$");
#include <dev/pci/pcivar.h>
#include <dev/bhnd/bhndb/bhndb_pcivar.h>
#include <dev/bhnd/bhndb/bhndb_hwdata.h>
#include <dev/bhnd/bhndb/bhndb_pci_hwdata.h>
#include <dev/bhnd/bhnd_ids.h>
@ -115,12 +116,14 @@ static const struct bwn_pci_devcfg bwn_pci_devcfgs[] = {
{
.bridge_hwcfg = &bhndb_pci_siba_generic_hwcfg,
.bridge_hwtable = bhndb_pci_generic_hw_table,
.bridge_hwprio = bhndb_siba_priority_table,
.devices = siba_devices
},
/* BCMA devices */
{
.bridge_hwcfg = &bhndb_pci_bcma_generic_hwcfg,
.bridge_hwtable = bhndb_pci_generic_hw_table,
.bridge_hwprio = bhndb_bcma_priority_table,
.devices = bcma_devices
},
{ NULL, NULL, NULL }
@ -234,6 +237,13 @@ bwn_pci_get_bhndb_hwtable(device_t dev, device_t child)
return (sc->devcfg->bridge_hwtable);
}
static const struct bhndb_hw_priority *
bwn_pci_get_bhndb_hwprio(device_t dev, device_t child)
{
struct bwn_pci_softc *sc = device_get_softc(dev);
return (sc->devcfg->bridge_hwprio);
}
static bool
bwn_pci_is_core_disabled(device_t dev, device_t child,
struct bhnd_core_info *core)
@ -274,6 +284,7 @@ static device_method_t bwn_pci_methods[] = {
/* 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

View File

@ -1,5 +1,5 @@
/*-
* Copyright (c) 2015 Landon Fuller <landon@landonf.org>
* Copyright (c) 2015-2016 Landon Fuller <landonf@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -87,7 +87,8 @@ struct bwn_pci_device {
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_ */
#endif /* _IF_BWN_PCIVAR_H_ */

View File

@ -219,7 +219,8 @@ bcm_erom_probe_and_attach(bhnd_erom_class_t **erom_cls, kobj_ops_t erom_ops,
kobj_class_compile_static(cls, &kops);
/* Probe the bus address */
result = bhnd_erom_probe_static(cls, bst, bsh, bus_addr, &pcid);
result = bhnd_erom_probe_static(cls, bst, bsh, bus_addr, NULL,
&pcid);
/* Drop pointer to stack allocated ops table */
cls->ops = NULL;
@ -253,7 +254,7 @@ bcm_erom_probe_and_attach(bhnd_erom_class_t **erom_cls, kobj_ops_t 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);

View File

@ -9,6 +9,7 @@ SRCS= bhndb.c bhndb_subr.c bhndb_hwdata.c \
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