Page MenuHomeFreeBSD

D12510.diff
No OneTemporary

D12510.diff

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, &reg) == 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

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)

Event Timeline