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:
parent
8e35bf8319
commit
89294a783a
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=324071
@ -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;
|
||||
|
@ -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),
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
79
sys/dev/bhnd/bhnd_eromvar.h
Normal file
79
sys/dev/bhnd/bhnd_eromvar.h
Normal 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_ */
|
@ -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",
|
||||
|
@ -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, ®) == 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),
|
||||
|
@ -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 */
|
||||
|
@ -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 */
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -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 */
|
||||
|
@ -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_ */
|
@ -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);
|
||||
|
@ -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),
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user