bhnd: Add support for supplying bus I/O callbacks when initializing an EROM

parser.

This allows us to use the EROM parser API in cases where the standard bus
space I/O APIs are unsuitable. In particular, this will allow us to parse
the device enumeration table directly from bhndb(4) drivers, prior to
full attach and configuration of the bridge.

Approved by:	adrian (mentor)
Sponsored by:	The FreeBSD Foundation
Differential Revision:	https://reviews.freebsd.org/D12510
This commit is contained in:
Landon J. Fuller 2017-09-27 19:48:34 +00:00
parent 8e35bf8319
commit 89294a783a
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=324071
18 changed files with 1551 additions and 1132 deletions

View File

@ -686,6 +686,7 @@ bcma_add_children(device_t bus)
{
bhnd_erom_t *erom;
struct bcma_erom *bcma_erom;
struct bhnd_erom_io *eio;
const struct bhnd_chipid *cid;
struct bcma_corecfg *corecfg;
struct bcma_devinfo *dinfo;
@ -696,9 +697,12 @@ bcma_add_children(device_t bus)
corecfg = NULL;
/* Allocate our EROM parser */
erom = bhnd_erom_alloc(&bcma_erom_parser, cid, bus, BCMA_EROM_RID);
if (erom == NULL)
eio = bhnd_erom_iores_new(bus, BCMA_EROM_RID);
erom = bhnd_erom_alloc(&bcma_erom_parser, cid, eio);
if (erom == NULL) {
bhnd_erom_io_fini(eio);
return (ENODEV);
}
/* Add all cores. */
bcma_erom = (struct bcma_erom *)erom;

View File

@ -1,7 +1,11 @@
/*-
* Copyright (c) 2015 Landon Fuller <landon@landonf.org>
* Copyright (c) 2015-2017 Landon Fuller <landonf@landonf.org>
* Copyright (c) 2017 The FreeBSD Foundation
* All rights reserved.
*
* Portions of this software were developed by Landon Fuller
* under sponsorship from the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@ -58,13 +62,8 @@ __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);
@ -104,38 +103,19 @@ 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 */
struct bhnd_erom obj;
device_t dev; /**< parent device, or NULL if none. */
struct bhnd_erom_io *eio; /**< bus I/O callbacks */
bhnd_size_t offset; /**< current read offset */
};
#define EROM_LOG(erom, fmt, ...) do { \
if (erom->dev != NULL) { \
device_printf(erom->dev, "erom[0x%llx]: " fmt, \
(unsigned long long) (erom->offset), ##__VA_ARGS__);\
} else { \
printf("erom[0x%llx]: " fmt, \
(unsigned long long) (erom->offset), ##__VA_ARGS__);\
} \
#define EROM_LOG(erom, fmt, ...) do { \
printf("%s erom[0x%llx]: " fmt, __FUNCTION__, \
(unsigned long long)(erom->offset), ##__VA_ARGS__); \
} while(0)
/** Return the type name for an EROM entry */
@ -154,106 +134,52 @@ bcma_erom_entry_type_name (uint8_t entry)
}
}
/**
* Read a 32-bit value from an EROM I/O context.
*
* @param io EROM I/O context.
* @param offset Read offset.
*/
static uint32_t
bcma_eio_read4(struct bcma_erom_io *io, bus_size_t offset)
{
bus_size_t read_off;
read_off = io->start + offset;
if (io->res != NULL)
return (bhnd_bus_read_4(io->res, read_off));
else
return (bus_space_read_4(io->bst, io->bsh, read_off));
}
/* Initialize bcma_erom resource I/O context */
static void
bcma_eio_init(struct bcma_erom_io *io, struct bhnd_resource *res, int rid,
bus_size_t offset)
{
io->res = res;
io->rid = rid;
io->start = offset;
}
/* Initialize bcma_erom bus space I/O context */
static void
bcma_eio_init_static(struct bcma_erom_io *io, bus_space_tag_t bst,
bus_space_handle_t bsh, bus_size_t offset)
{
io->res = NULL;
io->rid = -1;
io->bst = bst;
io->bsh = bsh;
io->start = offset;
}
/* BCMA implementation of BHND_EROM_INIT() */
static int
bcma_erom_init(bhnd_erom_t *erom, const struct bhnd_chipid *cid,
device_t parent, int rid)
struct bhnd_erom_io *eio)
{
struct bcma_erom *sc;
struct bhnd_resource *res;
bhnd_addr_t table_addr;
int error;
sc = (struct bcma_erom *)erom;
sc->dev = parent;
sc->eio = eio;
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);
/* Determine erom table address */
if (BHND_ADDR_MAX - BCMA_EROM_TABLE_START < cid->enum_addr)
return (ENXIO); /* would overflow */
if (res == NULL)
return (ENOMEM);
table_addr = cid->enum_addr + BCMA_EROM_TABLE_START;
bcma_eio_init(&sc->io, res, rid, BCMA_EROM_TABLE_START);
/* Try to map the erom table */
error = bhnd_erom_io_map(sc->eio, table_addr, BCMA_EROM_TABLE_SIZE);
if (error)
return (error);
return (0);
}
/* BCMA implementation of BHND_EROM_INIT_STATIC() */
/* BCMA implementation of BHND_EROM_PROBE() */
static int
bcma_erom_init_static(bhnd_erom_t *erom, const struct bhnd_chipid *cid,
bus_space_tag_t bst, bus_space_handle_t bsh)
bcma_erom_probe(bhnd_erom_class_t *cls, struct bhnd_erom_io *eio,
const struct bhnd_chipid *hint, struct bhnd_chipid *cid)
{
struct bcma_erom *sc;
sc = (struct bcma_erom *)erom;
sc->dev = NULL;
sc->offset = 0;
bcma_eio_init_static(&sc->io, bst, bsh, BCMA_EROM_TABLE_START);
return (0);
}
/* Common implementation of BHND_EROM_PROBE/BHND_EROM_PROBE_STATIC */
static int
bcma_erom_probe_common(struct bcma_erom_io *io, const struct bhnd_chipid *hint,
struct bhnd_chipid *cid)
{
uint32_t idreg, eromptr;
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);
/* Confirm CHIPC_EROMPTR availability */
idreg = bhnd_erom_io_read(eio, CHIPC_ID, 4);
if (!BHND_CHIPTYPE_HAS_EROM(CHIPC_GET_BITS(idreg, CHIPC_ID_BUS)))
return (ENXIO);
/* Fetch EROM address */
eromptr = bcma_eio_read4(io, CHIPC_EROMPTR);
eromptr = bhnd_erom_io_read(eio, CHIPC_EROMPTR, 4);
/* Parse chip identifier */
*cid = bhnd_parse_chipid(idreg, eromptr);
@ -272,42 +198,12 @@ bcma_erom_probe_common(struct bcma_erom_io *io, const struct bhnd_chipid *hint,
}
}
static int
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_io io;
bcma_eio_init(&io, res, rman_get_rid(res->res),
offset + BCMA_EROM_TABLE_START);
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->io.res != NULL) {
bhnd_release_resource(sc->dev, SYS_RES_MEMORY, sc->io.rid,
sc->io.res);
sc->io.res = NULL;
sc->io.rid = -1;
}
bhnd_erom_io_fini(sc->eio);
}
static int
@ -591,8 +487,8 @@ bcma_erom_peek32(struct bcma_erom *erom, uint32_t *entry)
EROM_LOG(erom, "BCMA EROM table missing terminating EOF\n");
return (EINVAL);
}
*entry = bcma_eio_read4(&erom->io, erom->offset);
*entry = bhnd_erom_io_read(erom->eio, erom->offset, 4);
return (0);
}
@ -1520,9 +1416,7 @@ bcma_erom_dump(bhnd_erom_t *erom)
static kobj_method_t bcma_erom_methods[] = {
KOBJMETHOD(bhnd_erom_probe, bcma_erom_probe),
KOBJMETHOD(bhnd_erom_probe_static, bcma_erom_probe_static),
KOBJMETHOD(bhnd_erom_init, bcma_erom_init),
KOBJMETHOD(bhnd_erom_init_static, bcma_erom_init_static),
KOBJMETHOD(bhnd_erom_fini, bcma_erom_fini),
KOBJMETHOD(bhnd_erom_get_core_table, bcma_erom_get_core_table),
KOBJMETHOD(bhnd_erom_free_core_table, bcma_erom_free_core_table),

View File

@ -1,7 +1,11 @@
/*-
* Copyright (c) 2016 Landon Fuller <landonf@FreeBSD.org>
* Copyright (c) 2017 The FreeBSD Foundation
* All rights reserved.
*
* Portions of this software were developed by Landon Fuller
* under sponsorship from the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@ -31,20 +35,124 @@
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/bus.h>
#include <sys/kobj.h>
#include <machine/bus.h>
#include <sys/rman.h>
#include <machine/resource.h>
#include <dev/bhnd/bhndvar.h>
#include <dev/bhnd/bhnd_erom.h>
#include <dev/bhnd/bhnd_eromvar.h>
static int bhnd_erom_iores_map(struct bhnd_erom_io *eio, bhnd_addr_t addr,
bhnd_size_t size);
static uint32_t bhnd_erom_iores_read(struct bhnd_erom_io *eio,
bhnd_size_t offset, u_int width);
static void bhnd_erom_iores_fini(struct bhnd_erom_io *eio);
static int bhnd_erom_iobus_map(struct bhnd_erom_io *eio, bhnd_addr_t addr,
bhnd_size_t size);
static uint32_t bhnd_erom_iobus_read(struct bhnd_erom_io *eio,
bhnd_size_t offset, u_int width);
/**
* An implementation of bhnd_erom_io that manages mappings via
* bhnd_alloc_resource() and bhnd_release_resource().
*/
struct bhnd_erom_iores {
struct bhnd_erom_io eio;
device_t owner; /**< device from which we'll allocate resources */
int owner_rid; /**< rid to use when allocating new mappings */
struct bhnd_resource *mapped; /**< current mapping, or NULL */
int mapped_rid; /**< resource ID of current mapping, or -1 */
};
/**
* Fetch the device enumeration parser class from all bhnd(4)-compatible drivers
* registered for @p bus_devclass, probe @p eio for supporting parser classes,
* and return the best available supporting enumeration parser class.
*
* @param bus_devclass The bus device class to be queried for
* bhnd(4)-compatible drivers.
* @param eio An erom bus I/O instance, configured with a
* mapping of the first bus core.
* @param hint Identification hint used to identify the device.
* If the 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 non-NULL on success, the best available EROM class.
* @retval NULL if no erom class returned a successful probe result for
* @p eio.
*/
bhnd_erom_class_t *
bhnd_erom_probe_driver_classes(devclass_t bus_devclass,
struct bhnd_erom_io *eio, const struct bhnd_chipid *hint,
struct bhnd_chipid *cid)
{
driver_t **drivers;
int drv_count;
bhnd_erom_class_t *erom_cls;
int error, prio, result;
erom_cls = NULL;
prio = 0;
/* Fetch all available drivers */
error = devclass_get_drivers(bus_devclass, &drivers, &drv_count);
if (error) {
printf("error fetching bhnd(4) drivers for %s: %d\n",
devclass_get_name(bus_devclass), error);
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;
/* The default implementation of BHND_BUS_GET_EROM_CLASS()
* returns NULL if unimplemented; this should always be safe
* to call on arbitrary drivers */
cls = bhnd_driver_get_erom_class(drivers[i]);
if (cls == NULL)
continue;
kobj_class_compile(cls);
/* Probe the bus */
result = bhnd_erom_probe(cls, eio, hint, &pcid);
/* The parser did not match if an error was returned */
if (result > 0)
continue;
/* Check for a new highest priority match */
if (erom_cls == NULL || result > prio) {
prio = result;
*cid = pcid;
erom_cls = cls;
}
/* Terminate immediately on BUS_PROBE_SPECIFIC */
if (result == BUS_PROBE_SPECIFIC)
break;
}
return (erom_cls);
}
/**
* Allocate and return a new device enumeration table parser.
*
* @param cls The parser class for which an instance will be
* allocated.
* @param parent The parent device from which EROM resources should
* be allocated.
* @param rid The resource ID to be used when allocating EROM
* resources.
* @param eio The bus I/O callbacks to use when reading the device
* enumeration table.
* @param cid The device's chip identifier.
*
* @retval non-NULL success
@ -53,7 +161,7 @@ __FBSDID("$FreeBSD$");
*/
bhnd_erom_t *
bhnd_erom_alloc(bhnd_erom_class_t *cls, const struct bhnd_chipid *cid,
device_t parent, int rid)
struct bhnd_erom_io *eio)
{
bhnd_erom_t *erom;
int error;
@ -61,10 +169,9 @@ bhnd_erom_alloc(bhnd_erom_class_t *cls, const struct bhnd_chipid *cid,
erom = (bhnd_erom_t *)kobj_create((kobj_class_t)cls, M_BHND,
M_WAITOK|M_ZERO);
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)cid->enum_addr, rid,
error);
if ((error = BHND_EROM_INIT(erom, cid, eio))) {
printf("error initializing %s parser at %#jx: %d\n", cls->name,
(uintmax_t)cid->enum_addr, error);
kobj_delete((kobj_t)erom, M_BHND);
return (NULL);
@ -74,8 +181,7 @@ bhnd_erom_alloc(bhnd_erom_class_t *cls, const struct bhnd_chipid *cid,
}
/**
* Perform static initialization of aa device enumeration table parser using
* the provided bus space tag and handle.
* Perform static initialization of a device enumeration table parser.
*
* This may be used to initialize a caller-allocated erom instance state
* during early boot, prior to malloc availability.
@ -87,9 +193,8 @@ bhnd_erom_alloc(bhnd_erom_class_t *cls, const struct bhnd_chipid *cid,
* @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.
* @param eio The bus I/O callbacks to use when reading the device
* enumeration table.
*
* @retval 0 success
* @retval ENOMEM if @p esize is smaller than required by @p cls.
@ -98,7 +203,7 @@ bhnd_erom_alloc(bhnd_erom_class_t *cls, const struct bhnd_chipid *cid,
*/
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)
const struct bhnd_chipid *cid, struct bhnd_erom_io *eio)
{
kobj_class_t kcls;
@ -110,7 +215,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, cid, bst, bsh));
return (BHND_EROM_INIT(erom, cid, eio));
}
/**
@ -139,3 +244,243 @@ bhnd_erom_free(bhnd_erom_t *erom)
BHND_EROM_FINI(erom);
kobj_delete((kobj_t)erom, M_BHND);
}
/**
* Attempt to map @p size bytes at @p addr, replacing any existing
* @p eio mapping.
*
* @param eio I/O instance state.
* @param addr The address to be mapped.
* @param size The number of bytes to be mapped at @p addr.
*
* @retval 0 success
* @retval non-zero if mapping @p addr otherwise fails, a regular
* unix error code should be returned.
*/
int
bhnd_erom_io_map(struct bhnd_erom_io *eio, bhnd_addr_t addr, bhnd_size_t size)
{
return (eio->map(eio, addr, size));
}
/**
* Read a 1, 2, or 4 byte data item from @p eio, at the given @p offset
* relative to @p eio's current mapping.
*
* @param eio erom I/O callbacks
* @param offset read offset.
* @param width item width (1, 2, or 4 bytes).
*/
uint32_t
bhnd_erom_io_read(struct bhnd_erom_io *eio, bhnd_size_t offset, u_int width)
{
return (eio->read(eio, offset, width));
}
/**
* Free all resources held by @p eio.
*/
void
bhnd_erom_io_fini(struct bhnd_erom_io *eio)
{
if (eio->fini != NULL)
return (eio->fini(eio));
}
/**
* Allocate, initialize, and return a new I/O instance that will perform
* mapping by allocating SYS_RES_MEMORY resources from @p dev using @p rid.
*
* @param dev The device to pass to bhnd_alloc_resource() and
* bhnd_release_resource() functions.
* @param rid The resource ID to be used when allocating memory resources.
*/
struct bhnd_erom_io *
bhnd_erom_iores_new(device_t dev, int rid)
{
struct bhnd_erom_iores *iores;
iores = malloc(sizeof(*iores), M_BHND, M_WAITOK | M_ZERO);
iores->eio.map = bhnd_erom_iores_map;
iores->eio.read = bhnd_erom_iores_read;
iores->eio.fini = bhnd_erom_iores_fini;
iores->owner = dev;
iores->owner_rid = rid;
iores->mapped = NULL;
iores->mapped_rid = -1;
return (&iores->eio);
}
static int
bhnd_erom_iores_map(struct bhnd_erom_io *eio, bhnd_addr_t addr,
bhnd_size_t size)
{
struct bhnd_erom_iores *iores;
iores = (struct bhnd_erom_iores *)eio;
/* Sanity check the addr/size */
if (size == 0)
return (EINVAL);
if (BHND_ADDR_MAX - size < addr)
return (EINVAL); /* would overflow */
/* Check for an existing mapping */
if (iores->mapped) {
/* If already mapped, nothing else to do */
if (rman_get_start(iores->mapped->res) == addr &&
rman_get_size(iores->mapped->res) == size)
{
return (0);
}
/* Otherwise, we need to drop the existing mapping */
bhnd_release_resource(iores->owner, SYS_RES_MEMORY,
iores->mapped_rid, iores->mapped);
iores->mapped = NULL;
iores->mapped_rid = -1;
}
/* Try to allocate the new mapping */
iores->mapped_rid = iores->owner_rid;
iores->mapped = bhnd_alloc_resource(iores->owner, SYS_RES_MEMORY,
&iores->mapped_rid, addr, addr+size-1, size,
RF_ACTIVE|RF_SHAREABLE);
if (iores->mapped == NULL) {
iores->mapped_rid = -1;
return (ENXIO);
}
return (0);
}
static uint32_t
bhnd_erom_iores_read(struct bhnd_erom_io *eio, bhnd_size_t offset, u_int width)
{
struct bhnd_erom_iores *iores = (struct bhnd_erom_iores *)eio;
if (iores->mapped == NULL)
panic("read with invalid mapping");
switch (width) {
case 1:
return (bhnd_bus_read_1(iores->mapped, offset));
case 2:
return (bhnd_bus_read_2(iores->mapped, offset));
case 4:
return (bhnd_bus_read_4(iores->mapped, offset));
default:
panic("invalid width %u", width);
}
}
static void
bhnd_erom_iores_fini(struct bhnd_erom_io *eio)
{
struct bhnd_erom_iores *iores = (struct bhnd_erom_iores *)eio;
/* Release any mapping */
if (iores->mapped) {
bhnd_release_resource(iores->owner, SYS_RES_MEMORY,
iores->mapped_rid, iores->mapped);
iores->mapped = NULL;
iores->mapped_rid = -1;
}
free(eio, M_BHND);
}
/**
* Initialize an I/O instance that will perform mapping directly from the
* given bus space tag and handle.
*
* @param addr The base address mapped by @p bsh.
* @param size The total size mapped by @p bsh.
* @param bst Bus space tag for @p bsh.
* @param bsh Bus space handle mapping the full bus enumeration space.
*
* @retval 0 success
* @retval non-zero if initializing @p iobus otherwise fails, a regular
* unix error code will be returned.
*/
int
bhnd_erom_iobus_init(struct bhnd_erom_iobus *iobus, bhnd_addr_t addr,
bhnd_size_t size, bus_space_tag_t bst, bus_space_handle_t bsh)
{
iobus->eio.map = bhnd_erom_iobus_map;
iobus->eio.read = bhnd_erom_iobus_read;
iobus->eio.fini = NULL;
iobus->addr = addr;
iobus->size = size;
iobus->bst = bst;
iobus->bsh = bsh;
iobus->mapped = false;
return (0);
}
static int
bhnd_erom_iobus_map(struct bhnd_erom_io *eio, bhnd_addr_t addr,
bhnd_size_t size)
{
struct bhnd_erom_iobus *iobus = (struct bhnd_erom_iobus *)eio;
/* Sanity check the addr/size */
if (size == 0)
return (EINVAL);
/* addr+size must not overflow */
if (BHND_ADDR_MAX - size < addr)
return (EINVAL);
/* addr/size must fit within our bus tag's mapping */
if (addr < iobus->addr || size > iobus->size)
return (ENXIO);
if (iobus->size - (addr - iobus->addr) < size)
return (ENXIO);
/* The new addr offset and size must be representible as a bus_size_t */
if ((addr - iobus->addr) > BUS_SPACE_MAXSIZE)
return (ENXIO);
if (size > BUS_SPACE_MAXSIZE)
return (ENXIO);
iobus->offset = addr - iobus->addr;
iobus->limit = size;
iobus->mapped = true;
return (0);
}
static uint32_t
bhnd_erom_iobus_read(struct bhnd_erom_io *eio, bhnd_size_t offset, u_int width)
{
struct bhnd_erom_iobus *iobus = (struct bhnd_erom_iobus *)eio;
if (!iobus->mapped)
panic("no active mapping");
if (iobus->limit < width || iobus->limit - width < offset)
panic("invalid offset %#jx", offset);
switch (width) {
case 1:
return (bus_space_read_1(iobus->bst, iobus->bsh,
iobus->offset + offset));
case 2:
return (bus_space_read_2(iobus->bst, iobus->bsh,
iobus->offset + offset));
case 4:
return (bus_space_read_4(iobus->bst, iobus->bsh,
iobus->offset + offset));
default:
panic("invalid width %u", width);
}
}

View File

@ -1,7 +1,11 @@
/*-
* Copyright (c) 2015-2016 Landon Fuller <landonf@FreeBSD.org>
* Copyright (c) 2015-2017 Landon Fuller <landonf@FreeBSD.org>
* Copyright (c) 2017 The FreeBSD Foundation
* All rights reserved.
*
* Portions of this software were developed by Landon Fuller
* under sponsorship from the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@ -41,19 +45,38 @@
#include "bhnd_erom_if.h"
bhnd_erom_t *bhnd_erom_alloc(bhnd_erom_class_t *cls,
const struct bhnd_chipid *cid,
device_t parent, int rid);
/* forward declarations */
struct bhnd_erom_io;
struct bhnd_erom_iobus;
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);
bhnd_erom_class_t *bhnd_erom_probe_driver_classes(devclass_t bus_devclass,
struct bhnd_erom_io *eio,
const struct bhnd_chipid *hint,
struct bhnd_chipid *cid);
void bhnd_erom_fini_static(bhnd_erom_t *erom);
bhnd_erom_t *bhnd_erom_alloc(bhnd_erom_class_t *cls,
const struct bhnd_chipid *cid,
struct bhnd_erom_io *eio);
void bhnd_erom_free(bhnd_erom_t *erom);
int bhnd_erom_init_static(bhnd_erom_class_t *cls,
bhnd_erom_t *erom, size_t esize,
const struct bhnd_chipid *cid,
struct bhnd_erom_io *eio);
void bhnd_erom_fini_static(bhnd_erom_t *erom);
void bhnd_erom_free(bhnd_erom_t *erom);
struct bhnd_erom_io *bhnd_erom_iores_new(device_t dev, int rid);
int bhnd_erom_iobus_init(struct bhnd_erom_iobus *iobus,
bhnd_addr_t addr, bhnd_size_t size,
bus_space_tag_t bst, bus_space_handle_t bsh);
int bhnd_erom_io_map(struct bhnd_erom_io *eio,
bhnd_addr_t addr, bhnd_size_t size);
uint32_t bhnd_erom_io_read(struct bhnd_erom_io *eio,
bhnd_size_t offset, u_int width);
void bhnd_erom_io_fini(struct bhnd_erom_io *eio);
/**
* Abstract bhnd_erom instance state. Must be first member of all subclass
@ -92,19 +115,18 @@ 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.
* mapped by @p eio, 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 eio A bus I/O instance, configured with a mapping of the
* first bus core.
* @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
@ -117,43 +139,10 @@ SET_DECLARE(bhnd_erom_class_set, bhnd_erom_class_t);
* 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)
bhnd_erom_probe(bhnd_erom_class_t *cls, struct bhnd_erom_io *eio,
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 erom class to probe.
* @param bst Bus space tag.
* @param bsh Bus space handle mapping the EXTIF or ChipCommon core.
* @param paddr The physical address of the core mapped by @p bst and
* @p bsh.
* @param hint Identification hint used to identify the device. If
* chipset supports standard chip identification registers
* within the first core, this parameter should be NULL.
* @param[out] cid On success, the probed chip identifier.
*
* @retval 0 if this is the only possible device enumeration
* parser for the probed bus.
* @retval negative if the probe succeeds, a negative value should be
* returned; the parser returning the lowest value will
* be selected to handle device enumeration.
* @retval ENXIO If the bhnd bus type is not handled by this parser.
* @retval positive if an error occurs during probing, a regular unix error
* code should be returned.
*/
static inline int
bhnd_erom_probe_static(bhnd_erom_class_t *cls, bus_space_tag_t bst,
bus_space_handle_t bsh, bus_addr_t paddr, const struct bhnd_chipid *hint,
struct bhnd_chipid *cid)
{
return (BHND_EROM_PROBE_STATIC(cls, bst, bsh, paddr, hint, cid));
return (BHND_EROM_PROBE(cls, eio, hint, cid));
}
/**

View File

@ -1,7 +1,11 @@
#-
# Copyright (c) 2016 Landon Fuller <landon@landonf.org>
# Copyright (c) 2016-2017 Landon Fuller <landon@landonf.org>
# Copyright (c) 2017 The FreeBSD Foundation
# All rights reserved.
#
# Portions of this software were developed by Landon Fuller
# under sponsorship from the FreeBSD Foundation.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
@ -43,18 +47,25 @@ INTERFACE bhnd_erom;
# tables used by bhnd(4) buses.
#
HEADER {
/* forward declarations */
struct bhnd_erom_io;
};
/**
* 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.
* Probe to see if this device enumeration class supports the bhnd bus at
* @p addr, 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.
* @param cls The erom class to probe.
* @param eio A bus I/O instance, configured with a mapping of
* the first bus core.
* @param base_addr Address of the first bus core.
* @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.
@ -67,42 +78,7 @@ INTERFACE bhnd_erom;
*/
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 class to probe.
* @param bst Bus space tag.
* @param bsh Bus space handle mapping the first bus core.
* @param paddr The physical address of the core mapped by @p bst and
* @p bsh.
* @param hint Hint used to identify the device. If chipset supports
* standard chip identification registers within the first
* core, this parameter should be NULL.
* @param[out] cid On success, the probed chip identifier.
*
* @retval 0 if this is the only possible device enumeration
* parser for the probed bus.
* @retval negative if the probe succeeds, a negative value should be
* returned; the parser returning the highest negative
* value will be selected to handle device enumeration.
* @retval ENXIO If the bhnd bus type is not handled by this parser.
* @retval positive if an error occurs during probing, a regular unix error
* code should be returned.
*/
STATICMETHOD int probe_static {
bhnd_erom_class_t *cls;
bus_space_tag_t bst;
bus_space_handle_t bsh;
bus_addr_t paddr;
struct bhnd_erom_io *eio;
const struct bhnd_chipid *hint;
struct bhnd_chipid *cid;
};
@ -112,11 +88,9 @@ STATICMETHOD int probe_static {
*
* @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 eio The bus I/O instance to use when reading the device
* enumeration table. On success, the erom parser assumes
* ownership of this instance.
* @retval 0 success
* @retval non-zero if an error occurs initializing the EROM parser,
* a regular unix error code will be returned.
@ -124,29 +98,7 @@ STATICMETHOD int probe_static {
METHOD int init {
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 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;
const struct bhnd_chipid *cid;
bus_space_tag_t bst;
bus_space_handle_t bsh;
struct bhnd_erom_io *eio;
};
/**

View File

@ -0,0 +1,79 @@
/*-
* Copyright (c) 2017 The FreeBSD Foundation
* All rights reserved.
*
* This software was developed by Landon Fuller under sponsorship from
* the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer,
* without modification.
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
* similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
* redistribution must be conditioned upon including a substantially
* similar Disclaimer requirement for further binary redistribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGES.
*
* $FreeBSD$
*/
#ifndef _BHND_EROM_BHND_EROMVAR_H_
#define _BHND_EROM_BHND_EROMVAR_H_
#include <sys/param.h>
#include "bhnd_erom.h"
/* forward declarations */
struct bhnd_erom_io;
struct bhnd_erom_iobus;
/** @see bhnd_erom_io_map() */
typedef int (bhnd_erom_io_map_t)(struct bhnd_erom_io *eio,
bhnd_addr_t addr, bhnd_size_t size);
/** @see bhnd_erom_io_read() */
typedef uint32_t (bhnd_erom_io_read_t)(struct bhnd_erom_io *eio,
bhnd_size_t offset, u_int width);
/** @see bhnd_erom_io_fini() */
typedef void (bhnd_erom_io_fini_t)(struct bhnd_erom_io *eio);
/**
* Abstract EROM bus I/O support.
*/
struct bhnd_erom_io {
bhnd_erom_io_map_t *map; /**< @see bhnd_erom_io_map() */
bhnd_erom_io_read_t *read; /**< @see bhnd_erom_io_read() */
bhnd_erom_io_fini_t *fini; /**< @see bhnd_erom_io_fini(). May be NULL */
};
/**
* EROM bus handle/tag I/O instance state.
*/
struct bhnd_erom_iobus {
struct bhnd_erom_io eio;
bhnd_addr_t addr; /**< the address of @p bsh */
bhnd_size_t size; /**< the size of @p bsh */
bus_space_tag_t bst; /**< bus space tag */
bus_space_handle_t bsh; /**< bus space handle mapping the full enumeration space */
bool mapped; /**< if a mapping is active */
bus_size_t offset; /**< the current mapped offset within bsh */
bus_size_t limit; /**< the current mapped size relative to offset */
};
#endif /* _BHND_EROM_BHND_EROMVAR_H_ */

View File

@ -77,18 +77,6 @@ enum {
#define BHNDB_DEBUG(_type) (BHNDB_DEBUG_ ## _type & bhndb_debug)
static int bhndb_find_hostb_core(struct bhndb_softc *sc,
bhnd_erom_t *erom,
struct bhnd_core_info *core);
static bhnd_erom_class_t *bhndb_probe_erom_class(struct bhndb_softc *sc,
struct bhnd_chipid *cid);
static int bhndb_init_full_config(struct bhndb_softc *sc,
bhnd_erom_class_t *eromcls);
static struct bhnd_core_info *bhndb_get_bridge_core(struct bhndb_softc *sc);
static bool bhndb_hw_matches(struct bhndb_softc *sc,
struct bhnd_core_info *cores, u_int ncores,
const struct bhndb_hw *hw);
@ -200,21 +188,6 @@ bhndb_child_location_str(device_t dev, device_t child, char *buf,
return (0);
}
/**
* Return the bridge core info. Will panic if the bridge core info has not yet
* been populated during full bridge configuration.
*
* @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.
*
@ -507,51 +480,66 @@ bhndb_find_hwspec(struct bhndb_softc *sc, struct bhnd_core_info *cores,
* priority bands to add additional devices that will be attached in
* their preferred order relative to the bridged bhnd(4) bus.
*
* @param dev The bridge device to attach.
* @param bridge_devclass The device class of the bridging core. This is used
* to automatically detect the bridge core, and to disable additional bridge
* cores (e.g. PCMCIA on a PCIe device).
* @param dev The bridge device to attach.
* @param cid The bridged device's chip identification.
* @param cores The bridged device's core table.
* @param ncores The number of cores in @p cores.
* @param bridge_core Core info for the bhnd(4) core serving as the host
* bridge.
* @param erom_class An erom parser class that may be used to parse
* the bridged device's device enumeration table.
*/
int
bhndb_attach(device_t dev, bhnd_devclass_t bridge_devclass)
bhndb_attach(device_t dev, struct bhnd_chipid *cid,
struct bhnd_core_info *cores, u_int ncores,
struct bhnd_core_info *bridge_core, bhnd_erom_class_t *erom_class)
{
struct bhndb_devinfo *dinfo;
struct bhndb_softc *sc;
const struct bhndb_hwcfg *cfg;
bhnd_erom_class_t *eromcls;
const struct bhndb_hw *hw;
const struct bhndb_hwcfg *hwcfg;
const struct bhndb_hw_priority *hwprio;
struct bhnd_erom_io *eio;
bhnd_erom_t *erom;
int error;
sc = device_get_softc(dev);
sc->dev = dev;
sc->parent_dev = device_get_parent(dev);
sc->bridge_class = bridge_devclass;
sc->bridge_core = *bridge_core;
sc->chipid = *cid;
if ((error = bhnd_service_registry_init(&sc->services)))
return (error);
BHNDB_LOCK_INIT(sc);
/* 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)
goto failed;
/* Allocate our host resources */
if ((error = bhndb_alloc_host_resources(sc->bus_res)))
goto failed;
erom = NULL;
/* 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);
/* Find a matching bridge hardware configuration */
if ((error = bhndb_find_hwspec(sc, cores, ncores, &hw))) {
device_printf(sc->dev, "unable to identify device, "
" using generic bridge resource definitions\n");
device_printf(sc->dev, "device enumeration unsupported; no "
"compatible driver found\n");
return (ENXIO);
hwcfg = BHNDB_BUS_GET_GENERIC_HWCFG(sc->parent_dev, dev);
hw = NULL;
} else {
hwcfg = hw->cfg;
}
if (hw != NULL && (bootverbose || BHNDB_DEBUG(PRIO))) {
device_printf(sc->dev, "%s resource configuration\n", hw->name);
}
/* Allocate bridge resource state using the discovered hardware
* configuration */
sc->bus_res = bhndb_alloc_resources(sc->dev, sc->parent_dev, hwcfg);
if (sc->bus_res == NULL) {
device_printf(sc->dev, "failed to allocate bridge resource "
"state\n");
error = ENOMEM;
goto failed;
}
BHNDB_UNLOCK(sc);
/* Add our bridged bus device */
sc->bus_dev = BUS_ADD_CHILD(dev, BHND_PROBE_BUS, "bhnd", -1);
@ -563,14 +551,29 @@ bhndb_attach(device_t dev, bhnd_devclass_t bridge_devclass)
dinfo = device_get_ivars(sc->bus_dev);
dinfo->addrspace = BHNDB_ADDRSPACE_BRIDGED;
/* 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);
/* We can now use bhndb to perform bridging of SYS_RES_MEMORY resources;
* we use this to instantiate an erom parser instance */
eio = bhnd_erom_iores_new(sc->bus_dev, 0);
if ((erom = bhnd_erom_alloc(erom_class, cid, eio)) == NULL) {
bhnd_erom_io_fini(eio);
error = ENXIO;
goto failed;
}
/* Populate our resource priority configuration */
hwprio = BHNDB_BUS_GET_HARDWARE_PRIO(sc->parent_dev, sc->dev);
error = bhndb_init_region_cfg(sc, erom, sc->bus_res, cores, ncores,
hwprio);
if (error) {
device_printf(sc->dev, "failed to initialize resource "
"priority configuration: %d\n", error);
goto failed;
}
/* Free our erom instance */
bhnd_erom_free(erom);
erom = NULL;
return (0);
failed:
@ -579,329 +582,10 @@ bhndb_attach(device_t dev, bhnd_devclass_t bridge_devclass)
if (sc->bus_res != NULL)
bhndb_free_resources(sc->bus_res);
bhnd_service_registry_fini(&sc->services);
return (error);
}
/**
* Return a borrowed reference to the host resource mapping at least
* BHND_DEFAULT_CORE_SIZE bytes at the first bus core, for use with
* bhnd_erom_probe().
*
* This may return a borrowed reference to a bhndb_dw_alloc-managed
* resource; any additional resource mapping requests may invalidate this
* borrowed reference.
*
* @param sc BHNDB driver state.
* @param[out] offset On success, the offset within the returned resource
* at which the first bus core can be found.
*
* @retval non-NULL success.
* @retval NULL If no usable mapping could be found.
*/
static struct resource *
bhndb_erom_chipc_resource(struct bhndb_softc *sc, bus_size_t *offset)
{
const struct bhndb_hwcfg *cfg;
struct bhndb_dw_alloc *dwa;
struct resource *res;
const struct bhndb_regwin *win;
BHNDB_LOCK_ASSERT(sc, MA_OWNED);
cfg = sc->bus_res->cfg;
/* Find a static register window mapping ChipCommon. */
win = bhndb_regwin_find_core(cfg->register_windows, BHND_DEVCLASS_CC,
0, BHND_PORT_DEVICE, 0, 0);
if (win != NULL) {
if (win->win_size < BHND_DEFAULT_CORE_SIZE) {
device_printf(sc->dev,
"chipcommon register window too small\n");
return (NULL);
}
res = bhndb_find_regwin_resource(sc->bus_res, win);
if (res == NULL) {
device_printf(sc->dev,
"chipcommon register window not allocated\n");
return (NULL);
}
*offset = win->win_offset;
return (res);
}
/* We'll need to fetch and configure a dynamic window. We can assume a
* device without a static ChipCommon mapping uses the default siba(4)
* base address. */
dwa = bhndb_io_resource(sc, BHND_DEFAULT_CHIPC_ADDR,
BHND_DEFAULT_CORE_SIZE, offset);
if (dwa != NULL)
return (dwa->parent_res);
device_printf(sc->dev, "unable to map chipcommon registers; no usable "
"register window found\n");
return (NULL);
}
/**
* Probe all supported EROM classes, returning the best matching class
* (or NULL if not found), writing the probed chip identifier to @p cid.
*
* @param sc BHNDB driver state.
* @param cid On success, the bridged chipset's chip identifier.
*/
static bhnd_erom_class_t *
bhndb_probe_erom_class(struct bhndb_softc *sc, struct bhnd_chipid *cid)
{
devclass_t bhndb_devclass;
const struct bhnd_chipid *hint;
struct resource *res;
bus_size_t res_offset;
driver_t **drivers;
int drv_count;
bhnd_erom_class_t *erom_cls;
int prio, result;
BHNDB_LOCK_ASSERT(sc, MA_OWNED);
erom_cls = NULL;
prio = 0;
/* Let our parent device provide a chipid hint */
hint = BHNDB_BUS_GET_CHIPID(sc->parent_dev, sc->dev);
/* Fetch a borrowed reference to the resource mapping ChipCommon. */
res = bhndb_erom_chipc_resource(sc, &res_offset);
if (res == NULL)
return (NULL);
/* Fetch all available drivers */
bhndb_devclass = device_get_devclass(sc->dev);
if (devclass_get_drivers(bhndb_devclass, &drivers, &drv_count) != 0)
return (NULL);
/* Enumerate the drivers looking for the best available EROM class */
for (int i = 0; i < drv_count; i++) {
struct bhnd_chipid pcid;
bhnd_erom_class_t *cls;
cls = bhnd_driver_get_erom_class(drivers[i]);
if (cls == NULL)
continue;
kobj_class_compile(cls);
/* Probe the bus */
result = bhnd_erom_probe(cls, &BHND_DIRECT_RESOURCE(res),
res_offset, hint, &pcid);
/* The parser did not match if an error was returned */
if (result > 0)
continue;
/* Check for a new highest priority match */
if (erom_cls == NULL || result > prio) {
prio = result;
*cid = pcid;
erom_cls = cls;
}
/* Terminate immediately on BUS_PROBE_SPECIFIC */
if (result == BUS_PROBE_SPECIFIC)
break;
}
return (erom_cls);
}
/* ascending core index comparison used by bhndb_find_hostb_core() */
static int
compare_core_index(const void *lhs, const void *rhs)
{
u_int left = ((const struct bhnd_core_info *)lhs)->core_idx;
u_int right = ((const struct bhnd_core_info *)rhs)->core_idx;
if (left < right)
return (-1);
else if (left > right)
return (1);
else
return (0);
}
/**
* Search @p erom for the core serving as the bhnd host bridge.
*
* This function uses a heuristic valid on all known PCI/PCIe/PCMCIA-bridged
* bhnd(4) devices to determine the hostb core:
*
* - The core must have a Broadcom vendor ID.
* - The core devclass must match the bridge type.
* - The core must be the first device on the bus with the bridged device
* class.
*
* @param sc BHNDB device state.
* @param erom The device enumeration table parser to be used to fetch
* core info.
* @param[out] core If found, the matching core info.
*
* @retval 0 success
* @retval ENOENT not found
* @retval non-zero if an error occured fetching core info.
*/
static int
bhndb_find_hostb_core(struct bhndb_softc *sc, bhnd_erom_t *erom,
struct bhnd_core_info *core)
{
struct bhnd_core_match md;
struct bhnd_core_info *cores;
u_int ncores;
int error;
if ((error = bhnd_erom_get_core_table(erom, &cores, &ncores)))
return (error);
/* Set up a match descriptor for the required device class. */
md = (struct bhnd_core_match) {
BHND_MATCH_CORE_CLASS(sc->bridge_class),
BHND_MATCH_CORE_UNIT(0)
};
/* Ensure the table is sorted by core index value, ascending;
* the host bridge must be the absolute first matching device on the
* bus. */
qsort(cores, ncores, sizeof(*cores), compare_core_index);
/* Find the hostb core */
error = ENOENT;
for (u_int i = 0; i < ncores; i++) {
if (bhnd_core_matches(&cores[i], &md)) {
/* Found! */
*core = cores[i];
error = 0;
break;
}
}
/* Clean up */
bhnd_erom_free_core_table(erom, cores);
return (error);
}
/**
* Identify the bridged device and perform final bridge resource configuration
* based on capabilities of the enumerated device.
*
* Any bridged resources allocated using the generic brige hardware
* configuration must be released prior to calling this function.
*/
static int
bhndb_init_full_config(struct bhndb_softc *sc, bhnd_erom_class_t *eromcls)
{
struct bhnd_core_info *cores;
struct bhndb_resources *br;
const struct bhndb_hw_priority *hwprio;
bhnd_erom_t *erom;
const struct bhndb_hw *hw;
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");
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, cores, ncores, &hw))) {
device_printf(sc->dev, "unable to identify device, "
" using generic bridge resource definitions\n");
error = 0;
goto cleanup;
}
if (bootverbose || BHNDB_DEBUG(PRIO))
device_printf(sc->dev, "%s resource configuration\n", hw->name);
/* 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;
}
/* 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) {
device_printf(sc->dev, "failed to initialize resource "
"priority configuration: %d\n", error);
goto cleanup;
}
/* 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:
if (cores != NULL)
bhnd_erom_free_core_table(erom, cores);
if (erom != NULL)
bhnd_erom_free(erom);
if (br != NULL)
bhndb_free_resources(br);
bhnd_service_registry_fini(&sc->services);
return (error);
}
@ -1190,7 +874,6 @@ bhndb_is_core_disabled(device_t dev, device_t child,
struct bhnd_core_info *core)
{
struct bhndb_softc *sc;
struct bhnd_core_info *bridge_core;
sc = device_get_softc(dev);
@ -1200,9 +883,8 @@ bhndb_is_core_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 (!bhnd_cores_equal(core, bridge_core));
return (!bhnd_cores_equal(core, &sc->bridge_core));
/* Assume the core is populated */
return (false);
@ -1219,7 +901,7 @@ bhndb_get_hostb_core(device_t dev, device_t child, struct bhnd_core_info *core)
{
struct bhndb_softc *sc = device_get_softc(dev);
*core = *bhndb_get_bridge_core(sc);
*core = sc->bridge_core;
return (0);
}
@ -1498,7 +1180,7 @@ bhndb_activate_static_region(struct bhndb_softc *sc,
r_size = rman_get_size(r);
/* Find the corresponding bridge resource */
bridge_res = bhndb_find_regwin_resource(sc->bus_res, win);
bridge_res = bhndb_host_resource_for_regwin(sc->bus_res->res, win);
if (bridge_res == NULL)
return (ENXIO);
@ -1618,8 +1300,8 @@ bhndb_try_activate_resource(struct bhndb_softc *sc, device_t child, int type,
struct resource *parent;
/* Find the bridge resource referenced by the child */
parent = bhndb_find_resource_range(sc->bus_res, r_start,
r_size);
parent = bhndb_host_resource_for_range(sc->bus_res->res,
type, r_start, r_size);
if (parent == NULL) {
device_printf(sc->dev, "host resource not found "
"for 0x%llx-0x%llx\n",

View File

@ -1,7 +1,11 @@
/*-
* Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org>
* Copyright (c) 2017 The FreeBSD Foundation
* All rights reserved.
*
* Portions of this software were developed by Landon Fuller
* under sponsorship from the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@ -55,6 +59,10 @@ __FBSDID("$FreeBSD$");
#include <dev/pci/pcivar.h>
#include <dev/bhnd/bhnd.h>
#include <dev/bhnd/bhndreg.h>
#include <dev/bhnd/bhnd_erom.h>
#include <dev/bhnd/bhnd_eromvar.h>
#include <dev/bhnd/cores/pci/bhnd_pcireg.h>
@ -62,15 +70,24 @@ __FBSDID("$FreeBSD$");
#include "bhndb_pcivar.h"
#include "bhndb_private.h"
struct bhndb_pci_eio;
static int bhndb_pci_init_msi(struct bhndb_pci_softc *sc);
static int bhndb_pci_read_core_table(device_t dev,
struct bhnd_chipid *chipid,
struct bhnd_core_info **cores, u_int *ncores,
bhnd_erom_class_t **eromcls);
static int bhndb_pci_add_children(struct bhndb_pci_softc *sc);
static int bhndb_enable_pci_clocks(struct bhndb_pci_softc *sc);
static int bhndb_disable_pci_clocks(struct bhndb_pci_softc *sc);
static bool bhndb_is_pcie_attached(device_t dev);
static int bhndb_pci_compat_setregwin(struct bhndb_pci_softc *,
const struct bhndb_regwin *, bhnd_addr_t);
static int bhndb_pci_fast_setregwin(struct bhndb_pci_softc *,
static int bhndb_enable_pci_clocks(device_t dev);
static int bhndb_disable_pci_clocks(device_t dev);
static int bhndb_pci_compat_setregwin(device_t dev,
device_t pci_dev, const struct bhndb_regwin *,
bhnd_addr_t);
static int bhndb_pci_fast_setregwin(device_t dev, device_t pci_dev,
const struct bhndb_regwin *, bhnd_addr_t);
static void bhndb_init_sromless_pci_config(
@ -79,8 +96,30 @@ static void bhndb_init_sromless_pci_config(
static bus_addr_t bhndb_pci_sprom_addr(struct bhndb_pci_softc *sc);
static bus_size_t bhndb_pci_sprom_size(struct bhndb_pci_softc *sc);
static int bhndb_pci_eio_init(struct bhndb_pci_eio *pio,
device_t dev, device_t pci_dev,
struct bhndb_host_resources *hr);
static int bhndb_pci_eio_map(struct bhnd_erom_io *eio,
bhnd_addr_t addr, bhnd_size_t size);
static uint32_t bhndb_pci_eio_read(struct bhnd_erom_io *eio,
bhnd_size_t offset, u_int width);
#define BHNDB_PCI_MSI_COUNT 1
/* bhndb_pci erom I/O implementation */
struct bhndb_pci_eio {
struct bhnd_erom_io eio;
device_t dev; /**< bridge device */
device_t pci_dev; /**< parent PCI device */
struct bhndb_host_resources *hr; /**< borrowed reference to host resources */
const struct bhndb_regwin *win; /**< mapped register window, or NULL */
struct resource *res; /**< resource containing the register window, or NULL if no window mapped */
bhnd_addr_t res_target; /**< current target address (if mapped) */
bool mapped; /**< true if a valid mapping exists, false otherwise */
bhnd_addr_t addr; /**< mapped address */
bhnd_size_t size; /**< mapped size */
};
/**
* Default bhndb_pci implementation of device_probe().
*
@ -137,17 +176,23 @@ static int
bhndb_pci_attach(device_t dev)
{
struct bhndb_pci_softc *sc;
struct bhnd_chipid cid;
struct bhnd_core_info *cores, hostb_core;
bhnd_erom_class_t *erom_class;
u_int ncores;
int error, reg;
sc = device_get_softc(dev);
sc->dev = dev;
sc->parent = device_get_parent(dev);
sc->set_regwin = bhndb_pci_compat_setregwin;
sc->set_regwin = NULL;
cores = NULL;
/* Enable PCI bus mastering */
pci_enable_busmaster(sc->parent);
/* Set up interrupt handling */
/* Set up PCI interrupt handling */
if (bhndb_pci_init_msi(sc) == 0) {
device_printf(dev, "Using MSI interrupts on %s\n",
device_get_nameunit(sc->parent));
@ -165,22 +210,32 @@ bhndb_pci_attach(device_t dev)
sc->pci_devclass = BHND_DEVCLASS_PCI;
/* Enable clocks (if required by this hardware) */
if ((error = bhndb_enable_pci_clocks(sc)))
if ((error = bhndb_enable_pci_clocks(sc->dev)))
goto cleanup;
/* Perform bridge attach, fully initializing the bridge
* configuration. */
if ((error = bhndb_attach(dev, sc->pci_devclass)))
/* Identify the chip and enumerate the bridged cores */
error = bhndb_pci_read_core_table(dev, &cid, &cores, &ncores,
&erom_class);
if (error)
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);
/* Select the appropriate register window handler */
if (cid.chip_type == BHND_CHIPTYPE_SIBA) {
sc->set_regwin = bhndb_pci_compat_setregwin;
} else {
sc->set_regwin = bhndb_pci_fast_setregwin;
}
/* Enable PCI bus mastering */
pci_enable_busmaster(sc->parent);
/* Determine our host bridge core */
error = bhndb_find_hostb_core(cores, ncores, sc->pci_devclass,
&hostb_core);
if (error)
goto cleanup;
/* Perform bridge attach */
error = bhndb_attach(dev, &cid, cores, ncores, &hostb_core, erom_class);
if (error)
goto cleanup;
/* Fix-up power on defaults for SROM-less devices. */
bhndb_init_sromless_pci_config(sc);
@ -193,14 +248,20 @@ bhndb_pci_attach(device_t dev)
if ((error = bus_generic_attach(dev)))
goto cleanup;
free(cores, M_BHND);
return (0);
cleanup:
device_delete_children(dev);
bhndb_disable_pci_clocks(sc);
bhndb_disable_pci_clocks(sc->dev);
if (sc->intr.msi_count > 0)
pci_release_msi(dev);
if (cores != NULL)
free(cores, M_BHND);
pci_disable_busmaster(sc->parent);
return (error);
@ -223,7 +284,7 @@ bhndb_pci_detach(device_t dev)
return (error);
/* Disable clocks (if required by this hardware) */
if ((error = bhndb_disable_pci_clocks(sc)))
if ((error = bhndb_disable_pci_clocks(sc->dev)))
return (error);
/* Release MSI interrupts */
@ -236,6 +297,124 @@ bhndb_pci_detach(device_t dev)
return (0);
}
/**
* Use the generic PCI bridge hardware configuration to enumerate the bridged
* bhnd(4) bus' core table.
*
* @note This function may be safely called prior to device attach, (e.g.
* from DEVICE_PROBE).
* @note This function requires exclusive ownership over allocating and
* configuring host bridge resources, and should only be called prior to
* completion of device attach and full configuration of the bridge.
*
* @param dev The bhndb_pci bridge device.
* @param[out] chipid On success, the parsed chip identification.
* @param[out] cores On success, the enumerated core table. The
* caller is responsible for freeing this table via
* bhndb_pci_free_core_table().
* @param[out] ncores On success, the number of cores found in
* @p cores.
* @param[out] eromcls On success, a pointer to the erom class used to
* parse the device enumeration table. This
* argument may be NULL if the class is not
* desired.
*
* @retval 0 success
* @retval non-zero if enumerating the bridged bhnd(4) bus fails, a regular
* unix error code will be returned.
*/
static int
bhndb_pci_read_core_table(device_t dev, struct bhnd_chipid *chipid,
struct bhnd_core_info **cores, u_int *ncores,
bhnd_erom_class_t **eromcls)
{
const struct bhndb_hwcfg *cfg;
struct bhndb_host_resources *hr;
struct bhndb_pci_eio pio;
struct bhnd_core_info *erom_cores;
const struct bhnd_chipid *hint;
struct bhnd_chipid cid;
bhnd_erom_class_t *erom_class;
bhnd_erom_t *erom;
device_t parent_dev;
u_int erom_ncores;
int error;
parent_dev = device_get_parent(dev);
erom = NULL;
erom_cores = NULL;
/* Fetch our chipid hint (if any) and generic hardware configuration */
cfg = BHNDB_BUS_GET_GENERIC_HWCFG(parent_dev, dev);
hint = BHNDB_BUS_GET_CHIPID(parent_dev, dev);
/* Allocate our host resources */
if ((error = bhndb_alloc_host_resources(parent_dev, cfg, &hr)))
return (error);
/* Initialize our erom I/O state */
if ((error = bhndb_pci_eio_init(&pio, dev, parent_dev, hr)))
goto failed;
/* Map the first bus core from our bridged bhnd(4) bus */
error = bhndb_pci_eio_map(&pio.eio, BHND_DEFAULT_CHIPC_ADDR,
BHND_DEFAULT_CORE_SIZE);
if (error)
goto failed;
/* Probe for a usable EROM class, and read the chip identifier */
erom_class = bhnd_erom_probe_driver_classes(device_get_devclass(dev),
&pio.eio, hint, &cid);
if (erom_class == NULL) {
device_printf(dev, "device enumeration unsupported; no "
"compatible driver found\n");
error = ENXIO;
goto failed;
}
/* Allocate EROM parser */
if ((erom = bhnd_erom_alloc(erom_class, &cid, &pio.eio)) == NULL) {
device_printf(dev, "failed to allocate device enumeration "
"table parser\n");
error = ENXIO;
goto failed;
}
/* Read the full core table */
error = bhnd_erom_get_core_table(erom, &erom_cores, &erom_ncores);
if (error) {
device_printf(dev, "error fetching core table: %d\n", error);
goto failed;
}
/* Provide the results to our caller */
*cores = malloc(sizeof(erom_cores[0]) * erom_ncores, M_BHND, M_WAITOK);
memcpy(*cores, erom_cores, sizeof(erom_cores[0]) * erom_ncores);
*ncores = erom_ncores;
*chipid = cid;
if (eromcls != NULL)
*eromcls = erom_class;
/* Clean up */
bhnd_erom_free_core_table(erom, erom_cores);
bhnd_erom_free(erom);
bhndb_release_host_resources(hr);
return (0);
failed:
if (erom_cores != NULL)
bhnd_erom_free_core_table(erom, erom_cores);
if (erom != NULL)
bhnd_erom_free(erom);
bhndb_release_host_resources(hr);
return (error);
}
static int
bhndb_pci_add_children(struct bhndb_pci_softc *sc)
{
@ -309,7 +488,7 @@ bhndb_pci_sprom_addr(struct bhndb_pci_softc *sc)
KASSERT(sprom_win != NULL, ("requested sprom address on PCI_V2+"));
/* Fetch the associated resource */
r = bhndb_find_regwin_resource(sc->bhndb.bus_res, sprom_win);
r = bhndb_host_resource_for_regwin(sc->bhndb.bus_res->res, sprom_win);
KASSERT(r != NULL, ("missing resource for sprom window\n"));
return (rman_get_start(r) + sprom_win->win_offset);
@ -419,7 +598,7 @@ bhndb_init_sromless_pci_config(struct bhndb_pci_softc *sc)
}
/* Fetch the resource containing the register window */
core_regs = bhndb_find_regwin_resource(bres, win);
core_regs = bhndb_host_resource_for_regwin(bres->res, win);
if (core_regs == NULL) {
device_printf(sc->dev, "missing PCI core register resource\n");
return;
@ -449,7 +628,7 @@ bhndb_pci_resume(device_t dev)
sc = device_get_softc(dev);
/* Enable clocks (if supported by this hardware) */
if ((error = bhndb_enable_pci_clocks(sc)))
if ((error = bhndb_enable_pci_clocks(sc->dev)))
return (error);
/* Perform resume */
@ -465,7 +644,7 @@ bhndb_pci_suspend(device_t dev)
sc = device_get_softc(dev);
/* Disable clocks (if supported by this hardware) */
if ((error = bhndb_disable_pci_clocks(sc)))
if ((error = bhndb_disable_pci_clocks(sc->dev)))
return (error);
/* Perform suspend */
@ -477,7 +656,7 @@ bhndb_pci_set_window_addr(device_t dev, const struct bhndb_regwin *rw,
bhnd_addr_t addr)
{
struct bhndb_pci_softc *sc = device_get_softc(dev);
return (sc->set_regwin(sc, rw, addr));
return (sc->set_regwin(sc->dev, sc->parent, rw, addr));
}
/**
@ -491,7 +670,7 @@ bhndb_pci_set_window_addr(device_t dev, const struct bhndb_regwin *rw,
* validating the register, there's no harm in performing the verification.
*/
static int
bhndb_pci_compat_setregwin(struct bhndb_pci_softc *sc,
bhndb_pci_compat_setregwin(device_t dev, device_t pci_dev,
const struct bhndb_regwin *rw, bhnd_addr_t addr)
{
int error;
@ -502,10 +681,10 @@ bhndb_pci_compat_setregwin(struct bhndb_pci_softc *sc,
reg = rw->d.dyn.cfg_offset;
for (u_int i = 0; i < BHNDB_PCI_BARCTRL_WRITE_RETRY; i++) {
if ((error = bhndb_pci_fast_setregwin(sc, rw, addr)))
if ((error = bhndb_pci_fast_setregwin(dev, pci_dev, rw, addr)))
return (error);
if (pci_read_config(sc->parent, reg, 4) == addr)
if (pci_read_config(pci_dev, reg, 4) == addr)
return (0);
DELAY(10);
@ -519,7 +698,7 @@ bhndb_pci_compat_setregwin(struct bhndb_pci_softc *sc,
* A bcma(4)-only bhndb_set_window_addr implementation.
*/
static int
bhndb_pci_fast_setregwin(struct bhndb_pci_softc *sc,
bhndb_pci_fast_setregwin(device_t dev, device_t pci_dev,
const struct bhndb_regwin *rw, bhnd_addr_t addr)
{
/* The PCI bridge core only supports 32-bit addressing, regardless
@ -533,7 +712,7 @@ bhndb_pci_fast_setregwin(struct bhndb_pci_softc *sc,
if (addr % rw->win_size != 0)
return (EINVAL);
pci_write_config(sc->parent, rw->d.dyn.cfg_offset, addr, 4);
pci_write_config(pci_dev, rw->d.dyn.cfg_offset, addr, 4);
break;
default:
return (ENODEV);
@ -590,6 +769,23 @@ bhndb_pci_populate_board_info(device_t dev, device_t child,
return (0);
}
/**
* Return true if the bridge device @p bhndb is attached via PCIe,
* false otherwise.
*
* @param dev The bhndb bridge device
*/
static bool
bhndb_is_pcie_attached(device_t dev)
{
int reg;
if (pci_find_cap(device_get_parent(dev), PCIY_EXPRESS, &reg) == 0)
return (true);
return (false);
}
/**
* Enable externally managed clocks, if required.
*
@ -598,77 +794,89 @@ bhndb_pci_populate_board_info(device_t dev, device_t child,
* attach/resume by directly adjusting GPIO registers exposed in the
* PCI config space, and correspondingly, explicitly shutdown at
* detach/suspend.
*
* @param sc Bridge driver state.
*
* @note This function may be safely called prior to device attach, (e.g.
* from DEVICE_PROBE).
*
* @param dev The bhndb bridge device
*/
static int
bhndb_enable_pci_clocks(struct bhndb_pci_softc *sc)
bhndb_enable_pci_clocks(device_t dev)
{
device_t pci_dev;
uint32_t gpio_in, gpio_out, gpio_en;
uint32_t gpio_flags;
uint16_t pci_status;
pci_dev = device_get_parent(dev);
/* Only supported and required on PCI devices */
if (sc->pci_devclass != BHND_DEVCLASS_PCI)
if (!bhndb_is_pcie_attached(dev))
return (0);
/* Read state of XTAL pin */
gpio_in = pci_read_config(sc->parent, BHNDB_PCI_GPIO_IN, 4);
gpio_in = pci_read_config(pci_dev, BHNDB_PCI_GPIO_IN, 4);
if (gpio_in & BHNDB_PCI_GPIO_XTAL_ON)
return (0); /* already enabled */
/* Fetch current config */
gpio_out = pci_read_config(sc->parent, BHNDB_PCI_GPIO_OUT, 4);
gpio_en = pci_read_config(sc->parent, BHNDB_PCI_GPIO_OUTEN, 4);
gpio_out = pci_read_config(pci_dev, BHNDB_PCI_GPIO_OUT, 4);
gpio_en = pci_read_config(pci_dev, BHNDB_PCI_GPIO_OUTEN, 4);
/* Set PLL_OFF/XTAL_ON pins to HIGH and enable both pins */
gpio_flags = (BHNDB_PCI_GPIO_PLL_OFF|BHNDB_PCI_GPIO_XTAL_ON);
gpio_out |= gpio_flags;
gpio_en |= gpio_flags;
pci_write_config(sc->parent, BHNDB_PCI_GPIO_OUT, gpio_out, 4);
pci_write_config(sc->parent, BHNDB_PCI_GPIO_OUTEN, gpio_en, 4);
pci_write_config(pci_dev, BHNDB_PCI_GPIO_OUT, gpio_out, 4);
pci_write_config(pci_dev, BHNDB_PCI_GPIO_OUTEN, gpio_en, 4);
DELAY(1000);
/* Reset PLL_OFF */
gpio_out &= ~BHNDB_PCI_GPIO_PLL_OFF;
pci_write_config(sc->parent, BHNDB_PCI_GPIO_OUT, gpio_out, 4);
pci_write_config(pci_dev, BHNDB_PCI_GPIO_OUT, gpio_out, 4);
DELAY(5000);
/* Clear any PCI 'sent target-abort' flag. */
pci_status = pci_read_config(sc->parent, PCIR_STATUS, 2);
pci_status = pci_read_config(pci_dev, PCIR_STATUS, 2);
pci_status &= ~PCIM_STATUS_STABORT;
pci_write_config(sc->parent, PCIR_STATUS, pci_status, 2);
pci_write_config(pci_dev, PCIR_STATUS, pci_status, 2);
return (0);
}
/**
* Disable externally managed clocks, if required.
*
* @param sc Bridge driver state.
*
* This function may be safely called prior to device attach, (e.g.
* from DEVICE_PROBE).
*
* @param dev The bhndb bridge device
*/
static int
bhndb_disable_pci_clocks(struct bhndb_pci_softc *sc)
bhndb_disable_pci_clocks(device_t dev)
{
device_t pci_dev;
uint32_t gpio_out, gpio_en;
pci_dev = device_get_parent(dev);
/* Only supported and required on PCI devices */
if (sc->pci_devclass != BHND_DEVCLASS_PCI)
if (bhndb_is_pcie_attached(dev))
return (0);
/* Fetch current config */
gpio_out = pci_read_config(sc->parent, BHNDB_PCI_GPIO_OUT, 4);
gpio_en = pci_read_config(sc->parent, BHNDB_PCI_GPIO_OUTEN, 4);
gpio_out = pci_read_config(pci_dev, BHNDB_PCI_GPIO_OUT, 4);
gpio_en = pci_read_config(pci_dev, BHNDB_PCI_GPIO_OUTEN, 4);
/* Set PLL_OFF to HIGH, XTAL_ON to LOW. */
gpio_out &= ~BHNDB_PCI_GPIO_XTAL_ON;
gpio_out |= BHNDB_PCI_GPIO_PLL_OFF;
pci_write_config(sc->parent, BHNDB_PCI_GPIO_OUT, gpio_out, 4);
pci_write_config(pci_dev, BHNDB_PCI_GPIO_OUT, gpio_out, 4);
/* Enable both output pins */
gpio_en |= (BHNDB_PCI_GPIO_PLL_OFF|BHNDB_PCI_GPIO_XTAL_ON);
pci_write_config(sc->parent, BHNDB_PCI_GPIO_OUTEN, gpio_en, 4);
pci_write_config(pci_dev, BHNDB_PCI_GPIO_OUTEN, gpio_en, 4);
return (0);
}
@ -683,7 +891,7 @@ bhndb_pci_pwrctl_get_clksrc(device_t dev, device_t child,
sc = device_get_softc(dev);
/* Only supported on PCI devices */
if (sc->pci_devclass != BHND_DEVCLASS_PCI)
if (bhndb_is_pcie_attached(sc->dev))
return (ENODEV);
/* Only ILP is supported */
@ -704,14 +912,14 @@ bhndb_pci_pwrctl_gate_clock(device_t dev, device_t child,
struct bhndb_pci_softc *sc = device_get_softc(dev);
/* Only supported on PCI devices */
if (sc->pci_devclass != BHND_DEVCLASS_PCI)
if (bhndb_is_pcie_attached(sc->dev))
return (ENODEV);
/* Only HT is supported */
if (clock != BHND_CLOCK_HT)
return (ENXIO);
return (bhndb_disable_pci_clocks(sc));
return (bhndb_disable_pci_clocks(sc->dev));
}
static int
@ -721,14 +929,14 @@ bhndb_pci_pwrctl_ungate_clock(device_t dev, device_t child,
struct bhndb_pci_softc *sc = device_get_softc(dev);
/* Only supported on PCI devices */
if (sc->pci_devclass != BHND_DEVCLASS_PCI)
if (bhndb_is_pcie_attached(sc->dev))
return (ENODEV);
/* Only HT is supported */
if (clock != BHND_CLOCK_HT)
return (ENXIO);
return (bhndb_enable_pci_clocks(sc));
return (bhndb_enable_pci_clocks(sc->dev));
}
static int
@ -754,6 +962,191 @@ bhndb_pci_assign_intr(device_t dev, device_t child, int rid)
return (bus_set_resource(child, SYS_RES_IRQ, rid, start, count));
}
/**
* Initialize a new bhndb PCI bridge EROM I/O instance. This EROM I/O
* implementation supports mapping of the device enumeration table via the
* @p hr host resources.
*
* @param pio The instance to be initialized.
* @param dev The bridge device.
* @param pci_dev The bridge's parent PCI device.
* @param hr The host resources to be used to map the device
* enumeration table.
*/
static int
bhndb_pci_eio_init(struct bhndb_pci_eio *pio, device_t dev, device_t pci_dev,
struct bhndb_host_resources *hr)
{
memset(&pio->eio, sizeof(pio->eio), 0);
pio->eio.map = bhndb_pci_eio_map;
pio->eio.read = bhndb_pci_eio_read;
pio->eio.fini = NULL;
pio->dev = dev;
pio->pci_dev = pci_dev;
pio->hr = hr;
pio->win = NULL;
pio->res = NULL;
return (0);
}
/**
* Attempt to adjust the dynamic register window backing @p pio to permit
* reading @p size bytes at @p addr.
*
* If @p addr or @p size fall outside the existing mapped range, or if
* @p pio is not backed by a dynamic register window, ENXIO will be returned.
*
* @param pio The bhndb PCI erom I/O state to be modified.
* @param addr The address to be include
*/
static int
bhndb_pci_eio_adjust_mapping(struct bhndb_pci_eio *pio, bhnd_addr_t addr,
bhnd_size_t size)
{
bhnd_addr_t target;
bhnd_size_t offset;
int error;
KASSERT(pio->win != NULL, ("missing register window"));
KASSERT(pio->res != NULL, ("missing regwin resource"));
KASSERT(pio->win->win_type == BHNDB_REGWIN_T_DYN,
("unexpected window type %d", pio->win->win_type));
/* The requested subrange must fall within the total mapped range */
if (addr < pio->addr || (addr - pio->addr) > pio->size ||
size > pio->size || (addr - pio->addr) - pio->size < size)
{
return (ENXIO);
}
/* Do we already have a useable mapping? */
if (addr >= pio->res_target &&
addr <= pio->res_target + pio->win->win_size &&
(pio->res_target + pio->win->win_size) - addr >= size)
{
return (0);
}
/* Page-align the target address */
offset = addr % pio->win->win_size;
target = addr - offset;
/* Configure the register window */
error = bhndb_pci_compat_setregwin(pio->dev, pio->pci_dev, pio->win,
target);
if (error) {
device_printf(pio->dev, "failed to configure dynamic register "
"window: %d\n", error);
return (error);
}
pio->res_target = target;
return (0);
}
/* bhnd_erom_io_map() implementation */
static int
bhndb_pci_eio_map(struct bhnd_erom_io *eio, bhnd_addr_t addr,
bhnd_size_t size)
{
struct bhndb_pci_eio *pio;
const struct bhndb_regwin *regwin;
struct resource *r;
bhnd_addr_t target;
bhnd_size_t offset;
int error;
pio = (struct bhndb_pci_eio *)eio;
/* Locate a useable dynamic register window */
regwin = bhndb_regwin_find_type(pio->hr->cfg->register_windows,
BHNDB_REGWIN_T_DYN, MIN(size, BHND_DEFAULT_CORE_SIZE));
if (regwin == NULL) {
device_printf(pio->dev, "unable to map %#jx+%#jx; no "
"usable dynamic register window found\n", addr, size);
return (ENXIO);
}
/* Locate the host resource mapping our register window */
if ((r = bhndb_host_resource_for_regwin(pio->hr, regwin)) == NULL) {
device_printf(pio->dev, "unable to map %#jx+%#jx; no "
"usable register resource found\n", addr, size);
return (ENXIO);
}
/* Page-align the target address */
offset = addr % regwin->win_size;
target = addr - offset;
/* Configure the register window */
error = bhndb_pci_compat_setregwin(pio->dev, pio->pci_dev, regwin,
target);
if (error) {
device_printf(pio->dev, "failed to configure dynamic register "
"window: %d\n", error);
return (error);
}
/* Update our mapping state */
pio->win = regwin;
pio->res = r;
pio->addr = addr;
pio->size = size;
pio->res_target = target;
return (0);
}
/* bhnd_erom_io_read() implementation */
static uint32_t
bhndb_pci_eio_read(struct bhnd_erom_io *eio, bhnd_size_t offset, u_int width)
{
struct bhndb_pci_eio *pio;
bhnd_addr_t addr;
bus_size_t res_offset;
int error;
pio = (struct bhndb_pci_eio *)eio;
/* Calculate absolute address */
if (BHND_SIZE_MAX - offset < pio->addr) {
device_printf(pio->dev, "invalid offset %#jx+%#jx\n", pio->addr,
offset);
return (UINT32_MAX);
}
addr = pio->addr + offset;
/* Adjust the mapping for our read */
if ((error = bhndb_pci_eio_adjust_mapping(pio, addr, width))) {
device_printf(pio->dev, "failed to adjust register mapping: "
"%d\n", error);
return (UINT32_MAX);
}
KASSERT(pio->res_target <= addr, ("invalid mapping (%#jx vs. %#jx)",
pio->res_target, addr));
/* Determine the actual read offset within our register window
* resource */
res_offset = (addr - pio->res_target) + pio->win->win_offset;
/* Perform our read */
switch (width) {
case 1:
return (bus_read_1(pio->res, res_offset));
case 2:
return (bus_read_2(pio->res, res_offset));
case 4:
return (bus_read_4(pio->res, res_offset));
default:
panic("unsupported width: %u", width);
}
}
static device_method_t bhndb_pci_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, bhndb_pci_probe),

View File

@ -45,7 +45,7 @@ struct bhndb_pci_softc;
/*
* An interconnect-specific function implementing BHNDB_SET_WINDOW_ADDR
*/
typedef int (*bhndb_pci_set_regwin_t)(struct bhndb_pci_softc *sc,
typedef int (*bhndb_pci_set_regwin_t)(device_t dev, device_t pci_dev,
const struct bhndb_regwin *rw, bhnd_addr_t addr);
/* bhndb_pci interrupt state */

View File

@ -1,7 +1,10 @@
/*-
* Copyright (c) 2015 Landon Fuller <landon@landonf.org>
* Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org>
* All rights reserved.
*
* Portions of this software were developed by Landon Fuller
* under sponsorship from the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@ -51,21 +54,10 @@ struct bhndb_dw_alloc;
struct bhndb_region;
struct bhndb_resources;
struct resource *bhndb_find_resource_range(
struct bhndb_resources *br,
rman_res_t start, rman_res_t count);
struct resource *bhndb_find_regwin_resource(
struct bhndb_resources *br,
const struct bhndb_regwin *win);
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);
@ -107,32 +99,6 @@ int bhndb_dw_set_addr(device_t dev,
struct bhndb_dw_alloc *dwa,
bus_addr_t addr, bus_size_t size);
size_t bhndb_regwin_count(
const struct bhndb_regwin *table,
bhndb_regwin_type_t type);
const struct bhndb_regwin *bhndb_regwin_find_type(
const struct bhndb_regwin *table,
bhndb_regwin_type_t type,
bus_size_t min_size);
const struct bhndb_regwin *bhndb_regwin_find_core(
const struct bhndb_regwin *table,
bhnd_devclass_t class, int unit,
bhnd_port_type port_type, u_int port,
u_int region);
const struct bhndb_regwin *bhndb_regwin_find_best(
const struct bhndb_regwin *table,
bhnd_devclass_t class, int unit,
bhnd_port_type port_type, u_int port,
u_int region, bus_size_t min_size);
bool bhndb_regwin_match_core(
const struct bhndb_regwin *regw,
struct bhnd_core_info *core);
const struct bhndb_hw_priority *bhndb_hw_priority_find_core(
const struct bhndb_hw_priority *table,
struct bhnd_core_info *core);
@ -177,10 +143,7 @@ struct bhndb_resources {
device_t dev; /**< bridge device */
const struct bhndb_hwcfg *cfg; /**< hardware configuration */
device_t parent_dev; /**< parent device */
struct resource_spec *res_spec; /**< parent bus resource specs, 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 bhndb_host_resources *res; /**< host resources, or NULL if not allocated */
struct rman ht_mem_rman; /**< host memory manager */
struct rman br_mem_rman; /**< bridged memory manager */

View File

@ -1,7 +1,11 @@
/*-
* Copyright (c) 2015 Landon Fuller <landon@landonf.org>
* Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org>
* Copyright (c) 2017 The FreeBSD Foundation
* All rights reserved.
*
* Portions of this software were developed by Landon Fuller
* under sponsorship from the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@ -183,9 +187,10 @@ bhnd_generic_br_resume_child(device_t dev, device_t child)
}
/**
* Find a SYS_RES_MEMORY resource containing the given address range.
* Find a host resource of @p type that maps the given range.
*
* @param br The bhndb resource state to search.
* @param hr The resource state to search.
* @param type The resource type to search for (see SYS_RES_*).
* @param start The start address of the range to search for.
* @param count The size of the range to search for.
*
@ -193,15 +198,13 @@ bhnd_generic_br_resume_child(device_t dev, device_t child)
* @retval NULL if no resource containing the requested range can be found.
*/
struct resource *
bhndb_find_resource_range(struct bhndb_resources *br, rman_res_t start,
rman_res_t count)
bhndb_host_resource_for_range(struct bhndb_host_resources *hr, int type,
rman_res_t start, rman_res_t count)
{
KASSERT(br->res_avail, ("no host resources allocated"));
for (u_int i = 0; hr->resource_specs[i].type != -1; i++) {
struct resource *r = hr->resources[i];
for (u_int i = 0; br->res_spec[i].type != -1; i++) {
struct resource *r = br->res[i];
if (br->res_spec->type != SYS_RES_MEMORY)
if (hr->resource_specs[i].type != type)
continue;
/* Verify range */
@ -218,23 +221,21 @@ bhndb_find_resource_range(struct bhndb_resources *br, rman_res_t start,
}
/**
* Find the resource containing @p win.
* Find a host resource of that matches the given register window definition.
*
* @param br The bhndb resource state to search.
* @param win A register window.
* @param hr The resource state to search.
* @param win A register window definition.
*
* @retval resource the resource containing @p win.
* @retval NULL if no resource containing @p win can be found.
* @retval resource the host resource corresponding to @p win.
* @retval NULL if no resource corresponding to @p win can be found.
*/
struct resource *
bhndb_find_regwin_resource(struct bhndb_resources *br,
bhndb_host_resource_for_regwin(struct bhndb_host_resources *hr,
const struct bhndb_regwin *win)
{
const struct resource_spec *rspecs;
KASSERT(br->res_avail, ("no host resources allocated"));
rspecs = br->cfg->resource_specs;
rspecs = hr->resource_specs;
for (u_int i = 0; rspecs[i].type != -1; i++) {
if (win->res.type != rspecs[i].type)
continue;
@ -243,12 +244,11 @@ bhndb_find_regwin_resource(struct bhndb_resources *br,
continue;
/* Found declared resource */
return (br->res[i]);
return (hr->resources[i]);
}
device_printf(br->dev,
"missing regwin resource spec (type=%d, rid=%d)\n",
win->res.type, win->res.rid);
device_printf(hr->owner, "missing regwin resource spec "
"(type=%d, rid=%d)\n", win->res.type, win->res.rid);
return (NULL);
}
@ -281,8 +281,8 @@ bhndb_alloc_resources(device_t dev, device_t parent_dev,
/* Basic initialization */
r->dev = dev;
r->parent_dev = parent_dev;
r->cfg = cfg;
r->res = NULL;
r->min_prio = BHNDB_PRIORITY_NONE;
STAILQ_INIT(&r->bus_regions);
@ -380,6 +380,72 @@ bhndb_alloc_resources(device_t dev, device_t parent_dev,
rnid++;
}
/* Allocate host resources */
error = bhndb_alloc_host_resources(parent_dev, r->cfg, &r->res);
if (error) {
device_printf(r->dev,
"could not allocate host resources on %s: %d\n",
device_get_nameunit(parent_dev), error);
goto failed;
}
/* Populate (and validate) parent resource references for all
* dynamic windows */
for (size_t i = 0; i < r->dwa_count; i++) {
struct bhndb_dw_alloc *dwa;
const struct bhndb_regwin *win;
dwa = &r->dw_alloc[i];
win = dwa->win;
/* Find and validate corresponding resource. */
dwa->parent_res = bhndb_host_resource_for_regwin(r->res, win);
if (dwa->parent_res == NULL) {
device_printf(r->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 "
"register window with offset %llx and size %llx\n",
rman_get_rid(dwa->parent_res),
(unsigned long long) win->win_offset,
(unsigned long long) win->win_size);
error = EINVAL;
goto failed;
}
}
/* Add allocated memory resources to our host memory resource manager */
for (u_int i = 0; r->res->resource_specs[i].type != -1; i++) {
struct resource *res;
/* skip non-memory resources */
if (r->res->resource_specs[i].type != SYS_RES_MEMORY)
continue;
/* add host resource to set of managed regions */
res = r->res->resources[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;
}
}
return (r);
failed:
@ -395,136 +461,14 @@ bhndb_alloc_resources(device_t dev, device_t parent_dev,
if (r->dwa_freelist != NULL)
free(r->dwa_freelist, M_BHND);
if (r->res != NULL)
bhndb_release_host_resources(r->res);
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(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(br->dev, "resource %d too small for "
"register window with offset %llx and size %llx\n",
rman_get_rid(dwa->parent_res),
(unsigned long long) win->win_offset,
(unsigned long long) win->win_size);
error = EINVAL;
goto failed;
}
}
/* 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 (br->res_avail)
bus_release_resources(br->parent_dev, br->res_spec, br->res);
if (br->res != NULL)
free(br->res, M_BHND);
if (br->res_spec != NULL)
free(br->res_spec, M_BHND);
return (error);
}
/**
* Deallocate the given bridge resource structure and any associated resources.
*
@ -551,9 +495,9 @@ bhndb_free_resources(struct bhndb_resources *br)
}
}
/* Release resources allocated through our parent. */
if (br->res_avail)
bus_release_resources(br->parent_dev, br->res_spec, br->res);
/* Release host resources allocated through our parent. */
if (br->res != NULL)
bhndb_release_host_resources(br->res);
/* Clean up resource reservations */
for (size_t i = 0; i < br->dwa_count; i++) {
@ -575,17 +519,153 @@ bhndb_free_resources(struct bhndb_resources *br)
rman_fini(&br->ht_mem_rman);
rman_fini(&br->br_mem_rman);
/* Free backing resource state structures */
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);
}
/**
* Allocate host bus resources defined by @p hwcfg.
*
* On success, the caller assumes ownership of the allocated host resources,
* which must be freed via bhndb_release_host_resources().
*
* @param dev The device to be used when allocating resources
* (e.g. via bus_alloc_resources()).
* @param hwcfg The hardware configuration defining the host
* resources to be allocated
* @param[out] resources On success, the allocated host resources.
*/
int
bhndb_alloc_host_resources(device_t dev, const struct bhndb_hwcfg *hwcfg,
struct bhndb_host_resources **resources)
{
struct bhndb_host_resources *hr;
size_t nres;
int error;
hr = malloc(sizeof(*hr), M_BHND, M_WAITOK);
hr->owner = dev;
hr->cfg = hwcfg;
hr->resource_specs = NULL;
hr->resources = NULL;
/* Determine our bridge resource count from the hardware config. */
nres = 0;
for (size_t i = 0; hwcfg->resource_specs[i].type != -1; i++)
nres++;
/* Allocate space for a non-const copy of our resource_spec
* table; this will be updated with the RIDs assigned by
* bus_alloc_resources. */
hr->resource_specs = malloc(sizeof(hr->resource_specs[0]) * (nres + 1),
M_BHND, M_WAITOK);
/* Initialize and terminate the table */
for (size_t i = 0; i < nres; i++)
hr->resource_specs[i] = hwcfg->resource_specs[i];
hr->resource_specs[nres].type = -1;
/* Allocate space for our resource references */
hr->resources = malloc(sizeof(hr->resources[0]) * nres, M_BHND,
M_WAITOK);
/* Allocate host resources */
error = bus_alloc_resources(hr->owner, hr->resource_specs,
hr->resources);
if (error) {
device_printf(dev, "could not allocate bridge resources via "
"%s: %d\n", device_get_nameunit(dev), error);
goto failed;
}
*resources = hr;
return (0);
failed:
if (hr->resource_specs != NULL)
free(hr->resource_specs, M_BHND);
if (hr->resources != NULL)
free(hr->resources, M_BHND);
free(hr, M_BHND);
return (error);
}
/**
* Deallocate a set of bridge host resources.
*
* @param hr The resources to be freed.
*/
void
bhndb_release_host_resources(struct bhndb_host_resources *hr)
{
bus_release_resources(hr->owner, hr->resource_specs, hr->resources);
free(hr->resources, M_BHND);
free(hr->resource_specs, M_BHND);
free(hr, M_BHND);
}
/**
* Search @p cores 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 cores The core table to search.
* @param ncores The number of cores in @p cores.
* @param bridge_devclass The expected device class of the bridge core.
* @param[out] core If found, the matching host bridge core info.
*
* @retval 0 success
* @retval ENOENT not found
*/
int
bhndb_find_hostb_core(struct bhnd_core_info *cores, u_int ncores,
bhnd_devclass_t bridge_devclass, struct bhnd_core_info *core)
{
struct bhnd_core_match md;
struct bhnd_core_info *match;
u_int match_core_idx;
/* Set up a match descriptor for the required device class. */
md = (struct bhnd_core_match) {
BHND_MATCH_CORE_CLASS(bridge_devclass),
BHND_MATCH_CORE_UNIT(0)
};
/* Find the matching core with the lowest core index */
match = NULL;
match_core_idx = UINT_MAX;
for (u_int i = 0; i < ncores; i++) {
if (!bhnd_core_matches(&cores[i], &md))
continue;
/* Lower core indices take precedence */
if (match != NULL && match_core_idx < match->core_idx)
continue;
match = &cores[i];
match_core_idx = match->core_idx;
}
if (match == NULL)
return (ENOENT);
*core = *match;
return (0);
}
/**
* Add a bus region entry to @p r for the given base @p addr and @p size.
*

View File

@ -56,18 +56,69 @@
DECLARE_CLASS(bhndb_driver);
struct bhndb_resources;
struct bhndb_host_resources;
int bhndb_attach(device_t dev, bhnd_devclass_t bridge_devclass);
int bhndb_attach(device_t dev,
struct bhnd_chipid *cid,
struct bhnd_core_info *cores, u_int ncores,
struct bhnd_core_info *bridge_core,
bhnd_erom_class_t *erom_class);
int bhndb_generic_probe(device_t dev);
int bhndb_generic_detach(device_t dev);
int bhndb_generic_suspend(device_t dev);
int bhndb_generic_resume(device_t dev);
int bhndb_generic_init_full_config(device_t dev, device_t child,
const struct bhndb_hw_priority *hw_prio_table);
int bhndb_generic_probe(device_t dev);
int bhndb_generic_detach(device_t dev);
int bhndb_generic_suspend(device_t dev);
int bhndb_generic_resume(device_t dev);
int bhndb_generic_init_full_config(device_t dev,
device_t child,
const struct bhndb_hw_priority *hw_prio_table);
int bhnd_generic_br_suspend_child(device_t dev, device_t child);
int bhnd_generic_br_resume_child(device_t dev, device_t child);
int bhnd_generic_br_suspend_child(device_t dev,
device_t child);
int bhnd_generic_br_resume_child(device_t dev,
device_t child);
int bhndb_find_hostb_core(
struct bhnd_core_info *cores, u_int ncores,
bhnd_devclass_t bridge_devclass,
struct bhnd_core_info *core);
int bhndb_alloc_host_resources(device_t dev,
const struct bhndb_hwcfg *hwcfg,
struct bhndb_host_resources **resources);
void bhndb_release_host_resources(
struct bhndb_host_resources *resources);
struct resource *bhndb_host_resource_for_range(
struct bhndb_host_resources *resources,
int type, rman_res_t start,
rman_res_t count);
struct resource *bhndb_host_resource_for_regwin(
struct bhndb_host_resources *resources,
const struct bhndb_regwin *win);
size_t bhndb_regwin_count(
const struct bhndb_regwin *table,
bhndb_regwin_type_t type);
const struct bhndb_regwin *bhndb_regwin_find_type(
const struct bhndb_regwin *table,
bhndb_regwin_type_t type,
bus_size_t min_size);
const struct bhndb_regwin *bhndb_regwin_find_core(
const struct bhndb_regwin *table,
bhnd_devclass_t class, int unit,
bhnd_port_type port_type, u_int port,
u_int region);
const struct bhndb_regwin *bhndb_regwin_find_best(
const struct bhndb_regwin *table,
bhnd_devclass_t class, int unit,
bhnd_port_type port_type, u_int port,
u_int region, bus_size_t min_size);
bool bhndb_regwin_match_core(
const struct bhndb_regwin *regw,
struct bhnd_core_info *core);
/**
* bhndb child address space. Children either operate in the bridged
@ -85,6 +136,16 @@ struct bhndb_devinfo {
struct resource_list resources; /**< child resources. */
};
/**
* Host resources allocated for a bridge hardware configuration.
*/
struct bhndb_host_resources {
device_t owner; /**< device owning the allocated resources */
const struct bhndb_hwcfg *cfg; /**< bridge hardware configuration */
struct resource_spec *resource_specs; /**< resource specification table */
struct resource **resources; /**< allocated resource table */
};
/**
* bhndb driver instance state. Must be first member of all subclass
* softc structures.
@ -92,10 +153,7 @@ struct bhndb_devinfo {
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 */
struct bhnd_core_info bridge_core; /**< bridge core info */
device_t parent_dev; /**< parent device */
device_t bus_dev; /**< child bhnd(4) bus */

View File

@ -34,9 +34,9 @@
/**
* The default address at which the ChipCommon core is mapped on all siba(4)
* devices, and most bcma(4) devices.
* devices, and most (all?) bcma(4) devices.
*/
#define BHND_DEFAULT_CHIPC_ADDR 0x18000000
#define BHND_DEFAULT_CHIPC_ADDR 0x18000000
/**
* The standard size of a primary BHND_PORT_DEVICE or BHND_PORT_AGENT
@ -44,5 +44,9 @@
*/
#define BHND_DEFAULT_CORE_SIZE 0x1000
/**
* The standard size of the siba(4) and bcma(4) enumeration space.
*/
#define BHND_DEFAULT_ENUM_SIZE 0x00100000
#endif /* _BHND_BHNDREG_H_ */

View File

@ -820,30 +820,34 @@ int
siba_add_children(device_t dev)
{
const struct bhnd_chipid *chipid;
struct bhnd_core_info *cores;
struct siba_devinfo *dinfo;
struct siba_core_id *cores;
struct bhnd_resource *r;
device_t *children;
int rid;
int error;
dinfo = NULL;
cores = NULL;
r = NULL;
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);
if (cores == NULL)
return (ENOMEM);
/* Allocate our temporary core and device table */
cores = malloc(sizeof(*cores) * chipid->ncores, M_BHND, M_WAITOK);
children = malloc(sizeof(*children) * chipid->ncores, M_BHND,
M_WAITOK | M_ZERO);
/* Add all cores. */
/*
* Add child devices for all discovered cores.
*
* On bridged devices, we'll exhaust our available register windows if
* we map config blocks on unpopulated/disabled cores. To avoid this, we
* defer mapping of the per-core siba(4) config blocks until all cores
* have been enumerated and otherwise configured.
*/
for (u_int i = 0; i < chipid->ncores; i++) {
struct siba_core_id cid;
device_t child;
struct siba_devinfo *dinfo;
uint32_t idhigh, idlow;
rman_res_t r_count, r_end, r_start;
int nintr;
/* Map the core's register block */
rid = 0;
@ -854,51 +858,73 @@ siba_add_children(device_t dev)
r_end, r_count, RF_ACTIVE);
if (r == NULL) {
error = ENXIO;
goto cleanup;
goto failed;
}
/* Add the child device */
child = BUS_ADD_CHILD(dev, 0, NULL, -1);
if (child == NULL) {
error = ENXIO;
goto cleanup;
}
/* Read the core info */
idhigh = bhnd_bus_read_4(r, SB0_REG_ABS(SIBA_CFG0_IDHIGH));
idlow = bhnd_bus_read_4(r, SB0_REG_ABS(SIBA_CFG0_IDLOW));
cid = siba_parse_core_id(idhigh, idlow, i, 0);
cores[i] = cid.core_info;
cores[i] = siba_parse_core_id(idhigh, idlow, i, 0);
/* Determine unit number */
/* Determine and set unit number */
for (u_int j = 0; j < i; j++) {
if (cores[j].vendor == cores[i].vendor &&
cores[j].device == cores[i].device)
cores[i].unit++;
struct bhnd_core_info *cur = &cores[i].core_info;
struct bhnd_core_info *prev = &cores[j].core_info;
if (prev->vendor == cur->vendor &&
prev->device == cur->device)
cur->unit++;
}
/* Add the child device */
children[i] = BUS_ADD_CHILD(dev, 0, NULL, -1);
if (children[i] == NULL) {
error = ENXIO;
goto failed;
}
/* Initialize per-device bus info */
if ((dinfo = device_get_ivars(child)) == NULL) {
if ((dinfo = device_get_ivars(children[i])) == NULL) {
error = ENXIO;
goto cleanup;
goto failed;
}
if ((error = siba_init_dinfo(dev, dinfo, &cid)))
goto cleanup;
if ((error = siba_init_dinfo(dev, dinfo, &cores[i])))
goto failed;
/* Register the core's address space(s). */
if ((error = siba_register_addrspaces(dev, dinfo, r)))
goto cleanup;
goto failed;
/* Release our resource covering the register blocks
* we're about to map */
/* Unmap the core's register block */
bhnd_release_resource(dev, SYS_RES_MEMORY, rid, r);
r = NULL;
/* If pins are floating or the hardware is otherwise
* unpopulated, the device shouldn't be used. */
if (bhnd_is_hw_disabled(children[i]))
device_disable(children[i]);
}
/* Map all valid core's config register blocks and perform interrupt
* assignment */
for (u_int i = 0; i < chipid->ncores; i++) {
struct siba_devinfo *dinfo;
device_t child;
int nintr;
child = children[i];
/* Skip if core is disabled */
if (bhnd_is_hw_disabled(child))
continue;
dinfo = device_get_ivars(child);
/* Map the core's config blocks */
if ((error = siba_map_cfg_resources(dev, dinfo)))
goto cleanup;
goto failed;
/* Assign interrupts */
nintr = bhnd_get_intr_count(child);
@ -910,18 +936,25 @@ siba_add_children(device_t dev)
}
}
/* If pins are floating or the hardware is otherwise
* unpopulated, the device shouldn't be used. */
if (bhnd_is_hw_disabled(child))
device_disable(child);
/* Issue bus callback for fully initialized child. */
BHND_BUS_CHILD_ADDED(dev, child);
}
cleanup:
if (cores != NULL)
free(cores, M_BHND);
free(cores, M_BHND);
free(children, M_BHND);
return (0);
failed:
for (u_int i = 0; i < chipid->ncores; i++) {
if (children[i] == NULL)
continue;
device_delete_child(dev, children[i]);
}
free(cores, M_BHND);
free(children, M_BHND);
if (r != NULL)
bhnd_release_resource(dev, SYS_RES_MEMORY, rid, r);

View File

@ -1,7 +1,11 @@
/*-
* Copyright (c) 2015-2016 Landon Fuller <landonf@FreeBSD.org>
* Copyright (c) 2017 The FreeBSD Foundation
* All rights reserved.
*
* Portions of this software were developed by Landon Fuller
* under sponsorship from the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@ -50,12 +54,7 @@ struct siba_erom_io;
static int siba_eio_init(struct siba_erom_io *io,
device_t parent, struct bhnd_resource *res,
int rid, bus_size_t offset, u_int ncores);
static int siba_eio_init_static(struct siba_erom_io *io,
bus_space_tag_t bst, bus_space_handle_t bsh,
bus_size_t offset, u_int ncores);
struct bhnd_erom_io *eio, u_int ncores);
static uint32_t siba_eio_read_4(struct siba_erom_io *io,
u_int core_idx, bus_size_t offset);
@ -71,18 +70,9 @@ static int siba_eio_read_chipid(struct siba_erom_io *io,
* SIBA EROM generic I/O context
*/
struct siba_erom_io {
struct bhnd_erom_io *eio; /**< erom I/O callbacks */
bhnd_addr_t base_addr; /**< address of first core */
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 unavailable. */
struct bhnd_resource *res; /**< memory resource, or NULL */
int rid; /**< memory resource ID */
/* bus tag state */
bus_space_tag_t bst; /**< bus space tag */
bus_space_handle_t bsh; /**< bus space handle */
};
/**
@ -93,22 +83,23 @@ struct siba_erom {
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__); \
} \
#define EROM_LOG(io, fmt, ...) do { \
printf("%s: " fmt, __FUNCTION__, ##__VA_ARGS__); \
} while(0)
/* SIBA implementation of BHND_EROM_PROBE() */
static int
siba_erom_probe_common(struct siba_erom_io *io, const struct bhnd_chipid *hint,
struct bhnd_chipid *cid)
siba_erom_probe(bhnd_erom_class_t *cls, struct bhnd_erom_io *eio,
const struct bhnd_chipid *hint, struct bhnd_chipid *cid)
{
struct siba_erom_io io;
uint32_t idreg;
int error;
/* Initialize I/O context, assuming at least the first core is mapped */
if ((error = siba_eio_init(&io, eio, 1)))
return (error);
/* Try using the provided hint. */
if (hint != NULL) {
struct siba_core_id sid;
@ -127,7 +118,7 @@ siba_erom_probe_common(struct siba_erom_io *io, const struct bhnd_chipid *hint,
* BCM4710, it's a SDRAM core (0x803).
*/
sid = siba_eio_read_core_id(io, 0, 0);
sid = siba_eio_read_core_id(&io, 0, 0);
if (sid.core_info.vendor != BHND_MFGID_BCM)
return (ENXIO);
@ -138,12 +129,12 @@ siba_erom_probe_common(struct siba_erom_io *io, const struct bhnd_chipid *hint,
*cid = *hint;
} else {
/* Validate bus type */
idreg = siba_eio_read_4(io, 0, CHIPC_ID);
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)))
if ((error = siba_eio_read_chipid(&io, SIBA_ENUM_ADDR, cid)))
return (error);
/* Verify the chip type */
@ -164,77 +155,27 @@ siba_erom_probe_common(struct siba_erom_io *io, const struct bhnd_chipid *hint,
return (0);
}
/* SIBA implementation of BHND_EROM_PROBE() */
static int
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_io io;
int error, rid;
rid = rman_get_rid(res->res);
/* 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_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, const struct bhnd_chipid *hint,
struct bhnd_chipid *cid)
{
struct siba_erom_io io;
int error;
/* Initialize I/O context, assuming at least 1 core exists. */
if ((error = siba_eio_init_static(&io, bst, bsh, 0, 1)))
return (error);
return (siba_erom_probe_common(&io, hint, cid));
}
/* SIBA implementation of BHND_EROM_INIT() */
static int
siba_erom_init(bhnd_erom_t *erom, const struct bhnd_chipid *cid,
device_t parent, int rid)
struct bhnd_erom_io *eio)
{
struct siba_erom *sc;
struct bhnd_resource *res;
int error;
sc = (struct siba_erom *)erom;
/* 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);
/* Attempt to map the full core enumeration space */
error = bhnd_erom_io_map(eio, cid->enum_addr,
cid->ncores * SIBA_CORE_SIZE);
if (error) {
printf("%s: failed to map %u cores: %d\n", __FUNCTION__,
cid->ncores, error);
return (error);
}
/* 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));
return (siba_eio_init(&sc->io, eio, cid->ncores));
}
/* SIBA implementation of BHND_EROM_FINI() */
@ -243,41 +184,15 @@ siba_erom_fini(bhnd_erom_t *erom)
{
struct siba_erom *sc = (struct siba_erom *)erom;
if (sc->io.res != NULL) {
bhnd_release_resource(sc->io.dev, SYS_RES_MEMORY, sc->io.rid,
sc->io.res);
sc->io.res = NULL;
sc->io.rid = -1;
}
bhnd_erom_io_fini(sc->io.eio);
}
/* 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)
siba_eio_init(struct siba_erom_io *io, struct bhnd_erom_io *eio, u_int ncores)
{
io->dev = parent;
io->res = res;
io->rid = rid;
io->offset = offset;
io->eio = eio;
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);
}
@ -292,8 +207,6 @@ siba_eio_init_static(struct siba_erom_io *io, bus_space_tag_t bst,
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,
@ -303,11 +216,8 @@ siba_eio_read_4(struct siba_erom_io *io, u_int core_idx, bus_size_t offset)
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));
return (bhnd_erom_io_read(io->eio, SIBA_CORE_OFFSET(core_idx) + offset,
4));
}
/**
@ -393,7 +303,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_eio_read_core_id(&sc->io, i, 0);
sid = siba_eio_read_core_id(&sc->io, j, 0);
/* Bump the unit number? */
if (sid.core_info.vendor == ci.vendor &&
@ -580,9 +490,7 @@ siba_erom_dump(bhnd_erom_t *erom)
static kobj_method_t siba_erom_methods[] = {
KOBJMETHOD(bhnd_erom_probe, siba_erom_probe),
KOBJMETHOD(bhnd_erom_probe_static, siba_erom_probe_static),
KOBJMETHOD(bhnd_erom_init, siba_erom_init),
KOBJMETHOD(bhnd_erom_init_static, siba_erom_init_static),
KOBJMETHOD(bhnd_erom_fini, siba_erom_fini),
KOBJMETHOD(bhnd_erom_get_core_table, siba_erom_get_core_table),
KOBJMETHOD(bhnd_erom_free_core_table, siba_erom_free_core_table),

View File

@ -77,6 +77,7 @@ __FBSDID("$FreeBSD$");
#include <dev/bhnd/bhnd.h>
#include <dev/bhnd/bhndreg.h>
#include <dev/bhnd/bhnd_eromvar.h>
#include <dev/bhnd/bcma/bcma_eromvar.h>
@ -108,7 +109,7 @@ static int bcm_find_core(struct bcm_platform *bp,
static int bcm_erom_probe_and_attach(bhnd_erom_class_t **erom_cls,
kobj_ops_t erom_ops, bhnd_erom_t *erom, size_t esize,
struct bhnd_chipid *cid);
struct bhnd_erom_io *eio, struct bhnd_chipid *cid);
extern int *edata;
extern int *end;
@ -149,6 +150,17 @@ bcm_get_bus_addr(void)
return (BHND_DEFAULT_CHIPC_ADDR);
}
static bus_size_t
bcm_get_bus_size(void)
{
long msize;
if (resource_long_value("bhnd", 0, "msize", &msize) == 0)
return ((u_long)msize);
return (BHND_DEFAULT_ENUM_SIZE);
}
/**
* Search the device enumeration table for a core matching @p descs,
*
@ -242,24 +254,30 @@ bcm_get_nvram(struct bcm_platform *bp, const char *name, void *buf, size_t *len,
* @param esize The total available number of bytes allocated
* for @p erom. If this is less than is required
* by @p erom_cls ENOMEM will be returned.
* @param eio EROM I/O callbacks to be used.
* @param[out] cid On success, the probed chip identification.
*/
static int
bcm_erom_probe_and_attach(bhnd_erom_class_t **erom_cls, kobj_ops_t erom_ops,
bhnd_erom_t *erom, size_t esize, struct bhnd_chipid *cid)
bhnd_erom_t *erom, size_t esize, struct bhnd_erom_io *eio,
struct bhnd_chipid *cid)
{
bhnd_erom_class_t **clsp;
bus_space_tag_t bst;
bus_space_handle_t bsh;
bus_addr_t bus_addr;
int error, prio, result;
bus_addr = bcm_get_bus_addr();
*erom_cls = NULL;
prio = 0;
bst = mips_bus_space_generic;
bsh = BCM_SOC_BSH(bus_addr, 0);
/* Map our first bus core for the erom probe */
bus_addr = bcm_get_bus_addr();
if ((error = bhnd_erom_io_map(eio, bus_addr, BHND_DEFAULT_CORE_SIZE))) {
BCM_ERR("failed to map first core at %#jx+%#jx: %d\n",
(uintmax_t)bus_addr, (uintmax_t)BHND_DEFAULT_CORE_SIZE,
error);
return (error);
}
SET_FOREACH(clsp, bhnd_erom_class_set) {
struct bhnd_chipid pcid;
@ -272,8 +290,7 @@ 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, NULL,
&pcid);
result = bhnd_erom_probe(cls, eio, NULL, &pcid);
/* Drop pointer to stack allocated ops table */
cls->ops = NULL;
@ -299,6 +316,7 @@ bcm_erom_probe_and_attach(bhnd_erom_class_t **erom_cls, kobj_ops_t erom_ops,
if (*erom_cls == NULL) {
BCM_ERR("no erom parser found for root bus at %#jx\n",
(uintmax_t)bus_addr);
return (ENOENT);
}
@ -306,9 +324,7 @@ bcm_erom_probe_and_attach(bhnd_erom_class_t **erom_cls, kobj_ops_t erom_ops,
kobj_class_compile_static(*erom_cls, erom_ops);
/* ... and initialize the erom parser instance */
bsh = BCM_SOC_BSH(cid->enum_addr, 0);
error = bhnd_erom_init_static(*erom_cls, erom, esize, cid,
mips_bus_space_generic, bsh);
error = bhnd_erom_init_static(*erom_cls, erom, esize, cid, eio);
return (error);
}
@ -319,8 +335,14 @@ bcm_erom_probe_and_attach(bhnd_erom_class_t **erom_cls, kobj_ops_t erom_ops,
static int
bcm_init_platform_data(struct bcm_platform *bp)
{
bool aob, pmu;
int error;
bus_addr_t bus_addr, bus_size;
bus_space_tag_t erom_bst;
bus_space_handle_t erom_bsh;
bool aob, pmu;
int error;
bus_addr = bcm_get_bus_addr();
bus_size = bcm_get_bus_size();
#ifdef CFE
/* Fetch CFE console handle (if any). Must be initialized before
@ -339,10 +361,21 @@ bcm_init_platform_data(struct bcm_platform *bp)
/* Probe and attach device table provider, populating our
* chip identification */
erom_bst = mips_bus_space_generic;
erom_bsh = BCM_SOC_BSH(bus_addr, 0);
error = bhnd_erom_iobus_init(&bp->erom_io, bus_addr, bus_size, erom_bst,
erom_bsh);
if (error) {
BCM_ERR("failed to initialize erom I/O callbacks: %d\n", error);
return (error);
}
error = bcm_erom_probe_and_attach(&bp->erom_impl, &bp->erom_ops,
&bp->erom.obj, sizeof(bp->erom), &bp->cid);
&bp->erom.obj, sizeof(bp->erom), &bp->erom_io.eio, &bp->cid);
if (error) {
BCM_ERR("error attaching erom parser: %d\n", error);
bhnd_erom_io_fini(&bp->erom_io.eio);
return (error);
}

View File

@ -40,7 +40,7 @@
#include <machine/cpuregs.h>
#include <dev/bhnd/bhnd.h>
#include <dev/bhnd/bhnd_erom.h>
#include <dev/bhnd/bhnd_eromvar.h>
#include <dev/bhnd/cores/pmu/bhnd_pmuvar.h>
@ -66,6 +66,7 @@ struct bcm_platform {
bhnd_erom_class_t *erom_impl; /**< erom parser class */
struct kobj_ops erom_ops; /**< compiled kobj opcache */
struct bhnd_erom_iobus erom_io; /**< erom I/O callbacks */
union {
bhnd_erom_static_t data;
bhnd_erom_t obj;

View File

@ -6,6 +6,7 @@ KMOD= bhndb_pci
SRCS= bhndb_pci.c bhndb_pci_hwdata.c \
bhndb_pci_sprom.c
SRCS+= bhnd_bus_if.h bhndb_bus_if.h bhndb_if.h
SRCS+= bhnd_erom_if.h
SRCS+= bhnd_nvram_if.h
SRCS+= device_if.h bus_if.h pci_if.h