Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F168564252
D12510.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
111 KB
Referenced Files
None
Subscribers
None
D12510.diff
View Options
Index: head/sys/dev/bhnd/bcma/bcma.c
===================================================================
--- head/sys/dev/bhnd/bcma/bcma.c
+++ head/sys/dev/bhnd/bcma/bcma.c
@@ -686,6 +686,7 @@
{
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 @@
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;
Index: head/sys/dev/bhnd/bcma/bcma_erom.c
===================================================================
--- head/sys/dev/bhnd/bcma/bcma_erom.c
+++ head/sys/dev/bhnd/bcma/bcma_erom.c
@@ -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 @@
* 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);
@@ -105,37 +104,18 @@
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 @@
}
}
-
-/**
- * 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;
+ uint32_t idreg, eromptr;
- 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;
-
/* 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 @@
}
}
-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 @@
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 @@
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),
Index: head/sys/dev/bhnd/bhnd_erom.h
===================================================================
--- head/sys/dev/bhnd/bhnd_erom.h
+++ head/sys/dev/bhnd/bhnd_erom.h
@@ -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,20 +45,39 @@
#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
* instances.
@@ -92,19 +115,18 @@
#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 @@
* 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));
}
/**
Index: head/sys/dev/bhnd/bhnd_erom.c
===================================================================
--- head/sys/dev/bhnd/bhnd_erom.c
+++ head/sys/dev/bhnd/bhnd_erom.c
@@ -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 @@
*/
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 @@
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 @@
}
/**
- * 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 @@
* @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 @@
*/
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 @@
/* 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));
}
/**
@@ -138,4 +243,244 @@
{
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);
+ }
}
Index: head/sys/dev/bhnd/bhnd_erom_if.m
===================================================================
--- head/sys/dev/bhnd/bhnd_erom_if.m
+++ head/sys/dev/bhnd/bhnd_erom_if.m
@@ -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,51 +47,25 @@
# tables used by bhnd(4) buses.
#
-/**
- * Probe to see if this device enumeration class supports the bhnd bus
- * mapped by the given resource, returning a standard newbus device probe
- * result (see BUS_PROBE_*) and the probed chip identification.
- *
- * @param cls The erom class to probe.
- * @param res A resource mapping the first bus core.
- * @param offset Offset to the first bus core within @p res.
- * @param hint Hint used to identify the device. If chipset supports
- * standard chip identification registers within the first
- * core, this parameter should be NULL.
- * @param[out] cid On success, the probed chip identifier.
- *
- * @retval 0 if this is the only possible device enumeration
- * parser for the probed bus.
- * @retval negative if the probe succeeds, a negative value should be
- * returned; the parser returning the highest negative
- * value will be selected to handle device enumeration.
- * @retval ENXIO If the bhnd bus type is not handled by this parser.
- * @retval positive if an error occurs during probing, a regular unix error
- * code should be returned.
- */
-STATICMETHOD int probe {
- bhnd_erom_class_t *cls;
- struct bhnd_resource *res;
- bus_size_t offset;
- const struct bhnd_chipid *hint;
- struct bhnd_chipid *cid;
+HEADER {
+ /* forward declarations */
+ struct bhnd_erom_io;
};
/**
- * 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.
+ * 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 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.
+ * @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.
@@ -98,11 +76,9 @@
* @retval positive if an error occurs during probing, a regular unix error
* code should be returned.
*/
-STATICMETHOD int probe_static {
+STATICMETHOD int probe {
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 @@
*
* @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 @@
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;
};
/**
Index: head/sys/dev/bhnd/bhnd_eromvar.h
===================================================================
--- head/sys/dev/bhnd/bhnd_eromvar.h
+++ head/sys/dev/bhnd/bhnd_eromvar.h
@@ -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_ */
Index: head/sys/dev/bhnd/bhndb/bhndb.c
===================================================================
--- head/sys/dev/bhnd/bhndb/bhndb.c
+++ head/sys/dev/bhnd/bhndb/bhndb.c
@@ -77,18 +77,6 @@
#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);
@@ -201,21 +189,6 @@
}
/**
- * 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.
*
* @param sc BHNDB device state.
@@ -507,52 +480,67 @@
* 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;
}
- BHNDB_UNLOCK(sc);
+ 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;
+ }
+
/* Add our bridged bus device */
sc->bus_dev = BUS_ADD_CHILD(dev, BHND_PROBE_BUS, "bhnd", -1);
if (sc->bus_dev == NULL) {
@@ -563,345 +551,41 @@
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;
}
- return (0);
-
-failed:
- BHNDB_LOCK_DESTROY(sc);
-
- 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);
+ 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 cleanup;
+ goto failed;
}
- /* The EROM parser holds a reference to the resource state we're
- * about to invalidate */
- bhnd_erom_free_core_table(erom, cores);
+ /* Free our erom instance */
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);
+failed:
+ BHNDB_LOCK_DESTROY(sc);
+ if (sc->bus_res != NULL)
+ bhndb_free_resources(sc->bus_res);
+
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 @@
struct bhnd_core_info *core)
{
struct bhndb_softc *sc;
- struct bhnd_core_info *bridge_core;
sc = device_get_softc(dev);
@@ -1200,9 +883,8 @@
/* 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 @@
{
struct bhndb_softc *sc = device_get_softc(dev);
- *core = *bhndb_get_bridge_core(sc);
+ *core = sc->bridge_core;
return (0);
}
@@ -1498,7 +1180,7 @@
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 @@
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",
Index: head/sys/dev/bhnd/bhndb/bhndb_pci.c
===================================================================
--- head/sys/dev/bhnd/bhndb/bhndb_pci.c
+++ head/sys/dev/bhnd/bhndb/bhndb_pci.c
@@ -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,23 +59,36 @@
#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>
#include "bhndb_pcireg.h"
#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 *,
+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 int bhndb_pci_fast_setregwin(struct bhndb_pci_softc *,
- const struct bhndb_regwin *, bhnd_addr_t);
static void bhndb_init_sromless_pci_config(
struct bhndb_pci_softc *sc);
@@ -79,8 +96,30 @@
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 @@
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,23 +210,33 @@
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 @@
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 @@
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,7 +297,125 @@
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)
{
bus_size_t nv_sz;
@@ -309,7 +488,7 @@
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 @@
}
/* 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 @@
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 @@
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 @@
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 @@
* 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 @@
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 @@
* 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 @@
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);
@@ -591,6 +770,23 @@
}
/**
+ * 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.
*
* Some PCI chipsets (BCM4306, possibly others) chips do not support
@@ -598,77 +794,89 @@
* 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 @@
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 @@
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 @@
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
@@ -752,6 +960,191 @@
/* Add to child's resource list */
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[] = {
Index: head/sys/dev/bhnd/bhndb/bhndb_pcivar.h
===================================================================
--- head/sys/dev/bhnd/bhndb/bhndb_pcivar.h
+++ head/sys/dev/bhnd/bhndb/bhndb_pcivar.h
@@ -45,7 +45,7 @@
/*
* 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 */
Index: head/sys/dev/bhnd/bhndb/bhndb_private.h
===================================================================
--- head/sys/dev/bhnd/bhndb/bhndb_private.h
+++ head/sys/dev/bhnd/bhndb/bhndb_private.h
@@ -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_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 @@
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 @@
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 */
Index: head/sys/dev/bhnd/bhndb/bhndb_subr.c
===================================================================
--- head/sys/dev/bhnd/bhndb/bhndb_subr.c
+++ head/sys/dev/bhnd/bhndb/bhndb_subr.c
@@ -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 @@
}
/**
- * 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 @@
* @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 @@
}
/**
- * 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 @@
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 @@
/* 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,92 +380,28 @@
rnid++;
}
- return (r);
-
-failed:
- if (free_ht_mem)
- rman_fini(&r->ht_mem_rman);
-
- if (free_br_mem)
- rman_fini(&r->br_mem_rman);
-
- if (r->dw_alloc != NULL)
- free(r->dw_alloc, M_BHND);
-
- if (r->dwa_freelist != NULL)
- free(r->dwa_freelist, M_BHND);
-
- free(r, M_BHND);
-
- return (NULL);
-}
-
-/**
- * Allocate host resources required by @p br, and initialize
- * internal BHNDB_ADDRSPACE_NATIVE resource manager state.
- *
- * @param br Resource state.
- */
-int
-bhndb_alloc_host_resources(struct bhndb_resources *br)
-{
- size_t res_num;
- int error;
-
- KASSERT(!br->res_avail, ("host resources already allocated"));
-
- /* Determine our bridge resource count from the hardware config. */
- res_num = 0;
- for (size_t i = 0; br->cfg->resource_specs[i].type != -1; i++)
- res_num++;
-
- /* Allocate space for a non-const copy of our resource_spec
- * table; this will be updated with the RIDs assigned by
- * bus_alloc_resources. */
- br->res_spec = malloc(sizeof(br->res_spec[0]) * (res_num + 1), M_BHND,
- M_NOWAIT);
- if (br->res_spec == NULL) {
- error = ENOMEM;
- goto failed;
- }
-
- /* Initialize and terminate the table */
- for (size_t i = 0; i < res_num; i++)
- br->res_spec[i] = br->cfg->resource_specs[i];
-
- br->res_spec[res_num].type = -1;
-
- /* Allocate space for our resource references */
- br->res = malloc(sizeof(br->res[0]) * res_num, M_BHND, M_NOWAIT);
- if (br->res == NULL) {
- error = ENOMEM;
- goto failed;
- }
-
/* Allocate host resources */
- error = bus_alloc_resources(br->parent_dev, br->res_spec, br->res);
+ error = bhndb_alloc_host_resources(parent_dev, r->cfg, &r->res);
if (error) {
- device_printf(br->dev,
- "could not allocate bridge resources on %s: %d\n",
- device_get_nameunit(br->parent_dev), error);
+ device_printf(r->dev,
+ "could not allocate host resources on %s: %d\n",
+ device_get_nameunit(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++) {
+ for (size_t i = 0; i < r->dwa_count; i++) {
struct bhndb_dw_alloc *dwa;
const struct bhndb_regwin *win;
- dwa = &br->dw_alloc[i];
+ dwa = &r->dw_alloc[i];
win = dwa->win;
/* Find and validate corresponding resource. */
- dwa->parent_res = bhndb_find_regwin_resource(br, win);
+ dwa->parent_res = bhndb_host_resource_for_regwin(r->res, win);
if (dwa->parent_res == NULL) {
- device_printf(br->dev, "no host resource found for %u "
+ device_printf(r->dev, "no host resource found for %u "
"register window with offset %#jx and "
"size %#jx\n",
win->win_type,
@@ -479,7 +415,7 @@
if (rman_get_size(dwa->parent_res) < win->win_offset +
win->win_size)
{
- device_printf(br->dev, "resource %d too small for "
+ 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,
@@ -491,38 +427,46 @@
}
/* Add allocated memory resources to our host memory resource manager */
- for (u_int i = 0; br->res_spec[i].type != -1; i++) {
+ for (u_int i = 0; r->res->resource_specs[i].type != -1; i++) {
struct resource *res;
/* skip non-memory resources */
- if (br->res_spec[i].type != SYS_RES_MEMORY)
+ if (r->res->resource_specs[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,
+ 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(br->dev,
+ device_printf(r->dev,
"could not register host memory region with "
"ht_mem_rman: %d\n", error);
goto failed;
}
}
- return (0);
+ return (r);
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 (free_ht_mem)
+ rman_fini(&r->ht_mem_rman);
- if (br->res_spec != NULL)
- free(br->res_spec, M_BHND);
+ if (free_br_mem)
+ rman_fini(&r->br_mem_rman);
- return (error);
+ if (r->dw_alloc != NULL)
+ free(r->dw_alloc, M_BHND);
+
+ 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);
}
/**
@@ -551,9 +495,9 @@
}
}
- /* 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,15 +519,151 @@
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);
}
/**
Index: head/sys/dev/bhnd/bhndb/bhndbvar.h
===================================================================
--- head/sys/dev/bhnd/bhndb/bhndbvar.h
+++ head/sys/dev/bhnd/bhndb/bhndbvar.h
@@ -56,19 +56,70 @@
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
* SoC address space, or within the address space mapped to the host
@@ -86,16 +137,23 @@
};
/**
+ * 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.
*/
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 */
Index: head/sys/dev/bhnd/bhndreg.h
===================================================================
--- head/sys/dev/bhnd/bhndreg.h
+++ head/sys/dev/bhnd/bhndreg.h
@@ -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_ */
\ No newline at end of file
Index: head/sys/dev/bhnd/siba/siba.c
===================================================================
--- head/sys/dev/bhnd/siba/siba.c
+++ head/sys/dev/bhnd/siba/siba.c
@@ -820,30 +820,34 @@
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 @@
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 @@
}
}
- /* 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);
Index: head/sys/dev/bhnd/siba/siba_erom.c
===================================================================
--- head/sys/dev/bhnd/siba/siba_erom.c
+++ head/sys/dev/bhnd/siba/siba_erom.c
@@ -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,13 +54,8 @@
static int siba_eio_init(struct siba_erom_io *io,
- device_t parent, struct bhnd_resource *res,
- int rid, bus_size_t offset, u_int ncores);
+ struct bhnd_erom_io *eio, u_int ncores);
-static int siba_eio_init_static(struct siba_erom_io *io,
- bus_space_tag_t bst, bus_space_handle_t bsh,
- bus_size_t offset, u_int ncores);
-
static uint32_t siba_eio_read_4(struct siba_erom_io *io,
u_int core_idx, bus_size_t offset);
@@ -71,18 +70,9 @@
* 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_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 @@
* 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 @@
*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,123 +155,47 @@
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);
+ return (siba_eio_init(&sc->io, eio, cid->ncores));
}
-/* SIBA implementation of BHND_EROM_INIT_STATIC() */
-static int
-siba_erom_init_static(bhnd_erom_t *erom, const struct bhnd_chipid *cid,
- bus_space_tag_t bst, bus_space_handle_t bsh)
-{
- struct siba_erom *sc;
-
- sc = (struct siba_erom *)erom;
-
- /* Initialize I/O context */
- return (siba_eio_init_static(&sc->io, bst, bsh, 0x0, cid->ncores));
-}
-
/* SIBA implementation of BHND_EROM_FINI() */
static void
siba_erom_fini(bhnd_erom_t *erom)
{
struct siba_erom *sc = (struct siba_erom *)erom;
- if (sc->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);
-}
-
/**
* Read a 32-bit value from @p offset relative to the base address of
* the given @p core_idx.
@@ -292,8 +207,6 @@
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 @@
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 @@
/* 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 @@
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),
Index: head/sys/mips/broadcom/bcm_machdep.h
===================================================================
--- head/sys/mips/broadcom/bcm_machdep.h
+++ head/sys/mips/broadcom/bcm_machdep.h
@@ -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 @@
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;
Index: head/sys/mips/broadcom/bcm_machdep.c
===================================================================
--- head/sys/mips/broadcom/bcm_machdep.c
+++ head/sys/mips/broadcom/bcm_machdep.c
@@ -77,6 +77,7 @@
#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_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 @@
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,25 +254,31 @@
* @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;
bhnd_erom_class_t *cls;
@@ -272,8 +290,7 @@
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 @@
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 @@
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,9 +335,15 @@
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
* any calls to printf/early_putc. */
@@ -339,10 +361,21 @@
/* 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);
}
Index: head/sys/modules/bhnd/bhndb_pci/Makefile
===================================================================
--- head/sys/modules/bhnd/bhndb_pci/Makefile
+++ head/sys/modules/bhnd/bhndb_pci/Makefile
@@ -6,6 +6,7 @@
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
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sun, Aug 30, 1:22 AM (5 h, 7 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
37548766
Default Alt Text
D12510.diff (111 KB)
Attached To
Mode
D12510: Add support for supplying bus I/O callbacks when initializing an EROM parser.
Attached
Detach File
Event Timeline
Log In to Comment