svn commit: r326297 - head/sys/dev/bhnd/bhndb
Landon J. Fuller
landonf at FreeBSD.org
Tue Nov 28 00:12:16 UTC 2017
Author: landonf
Date: Tue Nov 28 00:12:14 2017
New Revision: 326297
URL: https://svnweb.freebsd.org/changeset/base/326297
Log:
bhndb(4): Implement bridge support for the BCM4312 and other PCI_V0 chipsets.
Very early (PCI_V0) Broadcom PCI Wi-Fi chipsets have a few quirks when
compared to later PCI(e) core revisions:
- The standard static BAR0 mapping of the PCI core registers is discontiguous,
with siba's cfg0 register block mapped distinctly from the other core
registers.
- No dedicated ChipCommon register mapping is provided; instead, the
single configurable register window must be used to access both
ChipCommon and D11 core registers. The D11 core's operational semantics
guarantee the safety of -- after disabling interrupts -- borrowing
the single dynamic register window to perform the few ChipCommon
operations required by a driver.
To support these early PCI devices:
- Allow defining multiple discontiguous BHNDB_REGWIN_T_CORE register
windows that map a single port/region, and producing bridged resource
allocations backed by those discontiguous windows.
- Support stealing existing register window allocations to fulfill indirect
bhnd(4) bus I/O requests within address ranges tagged with
BHNDB_ALLOC_FULFILL_ON_OVERCOMMIT.
- Fix an inverted test of bhndb_is_pcie_attached() that disabled
PCI-only clock bring-up required by these devices.
Approved by: adrian (mentor, implicit)
Sponsored by: The FreeBSD Foundation
Modified:
head/sys/dev/bhnd/bhndb/bhndb.c
head/sys/dev/bhnd/bhndb/bhndb.h
head/sys/dev/bhnd/bhndb/bhndb_hwdata.c
head/sys/dev/bhnd/bhndb/bhndb_pci.c
head/sys/dev/bhnd/bhndb/bhndb_pci_hwdata.c
head/sys/dev/bhnd/bhndb/bhndb_pcireg.h
head/sys/dev/bhnd/bhndb/bhndb_private.h
head/sys/dev/bhnd/bhndb/bhndb_subr.c
head/sys/dev/bhnd/bhndb/bhndbvar.h
Modified: head/sys/dev/bhnd/bhndb/bhndb.c
==============================================================================
--- head/sys/dev/bhnd/bhndb/bhndb.c Mon Nov 27 23:48:21 2017 (r326296)
+++ head/sys/dev/bhnd/bhndb/bhndb.c Tue Nov 28 00:12:14 2017 (r326297)
@@ -115,9 +115,9 @@ static int bhndb_try_activate_resource(
static inline struct bhndb_dw_alloc *bhndb_io_resource(struct bhndb_softc *sc,
bus_addr_t addr, bus_size_t size,
- bus_size_t *offset);
+ bus_size_t *offset, bool *stolen,
+ bus_addr_t *restore);
-
/**
* Default bhndb(4) implementation of DEVICE_PROBE().
*
@@ -270,6 +270,9 @@ bhndb_init_region_cfg(struct bhndb_softc *sc, bhnd_ero
for (regw = br->cfg->register_windows;
regw->win_type != BHNDB_REGWIN_T_INVALID; regw++)
{
+ const struct bhndb_port_priority *pp;
+ uint32_t alloc_flags;
+
/* Only core windows are supported */
if (regw->win_type != BHNDB_REGWIN_T_CORE)
continue;
@@ -295,6 +298,18 @@ bhndb_init_region_cfg(struct bhndb_softc *sc, bhnd_ero
}
/*
+ * Apply the register window's region offset, if any.
+ */
+ if (regw->d.core.offset > size) {
+ device_printf(sc->dev, "invalid register "
+ "window offset %#jx for region %#jx+%#jx\n",
+ regw->d.core.offset, addr, size);
+ return (EINVAL);
+ }
+
+ addr += regw->d.core.offset;
+
+ /*
* Always defer to the register window's size.
*
* If the port size is smaller than the window size,
@@ -307,14 +322,25 @@ bhndb_init_region_cfg(struct bhndb_softc *sc, bhnd_ero
*/
size = regw->win_size;
+ /* Fetch allocation flags from the corresponding port
+ * priority entry, if any */
+ pp = bhndb_hw_priorty_find_port(table, core,
+ regw->d.core.port_type, regw->d.core.port,
+ regw->d.core.region);
+ if (pp != NULL) {
+ alloc_flags = pp->alloc_flags;
+ } else {
+ alloc_flags = 0;
+ }
+
/*
* Add to the bus region list.
*
- * The window priority for a statically mapped
- * region is always HIGH.
+ * The window priority for a statically mapped region is
+ * always HIGH.
*/
error = bhndb_add_resource_region(br, addr, size,
- BHNDB_PRIORITY_HIGH, regw);
+ BHNDB_PRIORITY_HIGH, 0, regw);
if (error)
return (error);
}
@@ -325,7 +351,6 @@ bhndb_init_region_cfg(struct bhndb_softc *sc, bhnd_ero
* ports defined in the priority table
*/
for (u_int i = 0; i < ncores; i++) {
- struct bhndb_region *region;
struct bhnd_core_info *core;
struct bhnd_core_match md;
@@ -369,13 +394,12 @@ bhndb_init_region_cfg(struct bhndb_softc *sc, bhnd_ero
}
/* Skip ports with an existing static mapping */
- region = bhndb_find_resource_region(br, addr, size);
- if (region != NULL && region->static_regwin != NULL)
+ if (bhndb_has_static_region_mapping(br, addr, size))
continue;
/* Define a dynamic region for this port */
error = bhndb_add_resource_region(br, addr, size,
- pp->priority, NULL);
+ pp->priority, pp->alloc_flags, NULL);
if (error)
return (error);
@@ -416,22 +440,29 @@ bhndb_init_region_cfg(struct bhndb_softc *sc, bhnd_ero
struct bhndb_region *region;
const char *direct_msg, *type_msg;
bhndb_priority_t prio, prio_min;
+ uint32_t flags;
prio_min = br->min_prio;
device_printf(sc->dev, "min_prio: %d\n", prio_min);
STAILQ_FOREACH(region, &br->bus_regions, link) {
prio = region->priority;
+ flags = region->alloc_flags;
direct_msg = prio >= prio_min ? "direct" : "indirect";
type_msg = region->static_regwin ? "static" : "dynamic";
device_printf(sc->dev, "region 0x%llx+0x%llx priority "
- "%u %s/%s\n",
+ "%u %s/%s",
(unsigned long long) region->addr,
(unsigned long long) region->size,
region->priority,
direct_msg, type_msg);
+
+ if (flags & BHNDB_ALLOC_FULFILL_ON_OVERCOMMIT)
+ printf(" [overcommit]\n");
+ else
+ printf("\n");
}
}
@@ -1605,11 +1636,12 @@ bhndb_deactivate_bhnd_resource(device_t dev, device_t
* in-use region; the first matching region is returned.
*/
static struct bhndb_dw_alloc *
-bhndb_io_resource_slow(struct bhndb_softc *sc, bus_addr_t addr,
- bus_size_t size, bus_size_t *offset)
+bhndb_io_resource_slow(struct bhndb_softc *sc, bus_addr_t addr, bus_size_t size,
+ bus_size_t *offset, bool *stolen, bus_addr_t *restore)
{
struct bhndb_resources *br;
struct bhndb_dw_alloc *dwa;
+ struct bhndb_region *region;
BHNDB_LOCK_ASSERT(sc, MA_OWNED);
@@ -1638,10 +1670,25 @@ bhndb_io_resource_slow(struct bhndb_softc *sc, bus_add
*offset = dwa->win->win_offset;
*offset += addr - dwa->target;
+ *stolen = false;
return (dwa);
}
- /* not found */
+ /* No existing dynamic mapping found. We'll need to check for a defined
+ * region to determine whether we can fulfill this request by
+ * stealing from an existing allocated register window */
+ region = bhndb_find_resource_region(br, addr, size);
+ if (region == NULL)
+ return (NULL);
+
+ if ((region->alloc_flags & BHNDB_ALLOC_FULFILL_ON_OVERCOMMIT) == 0)
+ return (NULL);
+
+ if ((dwa = bhndb_dw_steal(br, restore)) != NULL) {
+ *stolen = true;
+ return (dwa);
+ }
+
return (NULL);
}
@@ -1649,9 +1696,8 @@ bhndb_io_resource_slow(struct bhndb_softc *sc, bus_add
* Return a borrowed reference to a bridge resource allocation record capable
* of handling bus I/O requests of @p size at @p addr.
*
- * This will either return a reference to an existing allocation
- * record mapping the requested space, or will configure and return a free
- * allocation record.
+ * This will either return a reference to an existing allocation record mapping
+ * the requested space, or will configure and return a free allocation record.
*
* Will panic if a usable record cannot be found.
*
@@ -1660,10 +1706,16 @@ bhndb_io_resource_slow(struct bhndb_softc *sc, bus_add
* @param size The size of the I/O operation to be performed at @p addr.
* @param[out] offset The offset within the returned resource at which
* to perform the I/O request.
+ * @param[out] stolen Set to true if the allocation record was stolen to fulfill
+ * this request. If a stolen allocation record is returned,
+ * bhndb_io_resource_restore() must be called upon completion of the bus I/O
+ * request.
+ * @param[out] restore If the allocation record was stolen, this will be set
+ * to the target that must be restored.
*/
static inline struct bhndb_dw_alloc *
bhndb_io_resource(struct bhndb_softc *sc, bus_addr_t addr, bus_size_t size,
- bus_size_t *offset)
+ bus_size_t *offset, bool *stolen, bus_addr_t *restore)
{
struct bhndb_resources *br;
struct bhndb_dw_alloc *dwa;
@@ -1691,7 +1743,8 @@ bhndb_io_resource(struct bhndb_softc *sc, bus_addr_t a
* current operation.
*/
if (dwa == NULL) {
- dwa = bhndb_io_resource_slow(sc, addr, size, offset);
+ dwa = bhndb_io_resource_slow(sc, addr, size, offset, stolen,
+ restore);
if (dwa == NULL) {
panic("register windows exhausted attempting to map "
"0x%llx-0x%llx\n",
@@ -1720,6 +1773,7 @@ bhndb_io_resource(struct bhndb_softc *sc, bus_addr_t a
/* Calculate the offset and return */
*offset = (addr - dwa->target) + dwa->win->win_offset;
+ *stolen = false;
return (dwa);
}
@@ -1733,12 +1787,14 @@ bhndb_io_resource(struct bhndb_softc *sc, bus_addr_t a
struct bhndb_dw_alloc *dwa; \
struct resource *io_res; \
bus_size_t io_offset; \
+ bus_addr_t restore; \
+ bool stolen; \
\
sc = device_get_softc(dev); \
\
BHNDB_LOCK(sc); \
dwa = bhndb_io_resource(sc, rman_get_start(r->res) + \
- offset, _io_size, &io_offset); \
+ offset, _io_size, &io_offset, &stolen, &restore); \
io_res = dwa->parent_res; \
\
KASSERT(!r->direct, \
@@ -1748,6 +1804,10 @@ bhndb_io_resource(struct bhndb_softc *sc, bus_addr_t a
("i/o resource is not active"));
#define BHNDB_IO_COMMON_TEARDOWN() \
+ if (stolen) { \
+ bhndb_dw_return_stolen(sc->dev, sc->bus_res, \
+ dwa, restore); \
+ } \
BHNDB_UNLOCK(sc);
/* Defines a bhndb_bus_read_* method implementation */
Modified: head/sys/dev/bhnd/bhndb/bhndb.h
==============================================================================
--- head/sys/dev/bhnd/bhndb/bhndb.h Mon Nov 27 23:48:21 2017 (r326296)
+++ head/sys/dev/bhnd/bhndb/bhndb.h Tue Nov 28 00:12:14 2017 (r326297)
@@ -90,6 +90,7 @@ struct bhndb_regwin {
bhnd_port_type port_type; /**< mapped port type */
u_int port; /**< mapped port number */
u_int region; /**< mapped region number */
+ bhnd_size_t offset; /**< mapped offset within the region */
} core;
/** SPROM register window (BHNDB_REGWIN_T_SPROM). */
@@ -150,6 +151,26 @@ typedef enum {
} bhndb_priority_t;
/**
+ * bhndb resource allocation flags.
+ */
+enum bhndb_alloc_flags {
+ /**
+ * If resource overcommit prevents fulfilling a request for this
+ * resource, an in-use resource should be be borrowed to fulfill the
+ * request.
+ *
+ * The only known use case is to support accessing the ChipCommon core
+ * during Wi-Fi driver operation on early PCI Wi-Fi devices
+ * (PCI_V0, SSB) that do not provide a dedicated ChipCommon register
+ * window mapping; on such devices, device and firmware semantics
+ * guarantee the safety of -- after disabling interrupts -- borrowing
+ * the single dynamic register window that's been assigned to the D11
+ * core to perform the few ChipCommon operations required by the driver.
+ */
+ BHNDB_ALLOC_FULFILL_ON_OVERCOMMIT = (1<<0),
+};
+
+/**
* Port resource priority descriptor.
*/
struct bhndb_port_priority {
@@ -157,6 +178,7 @@ struct bhndb_port_priority {
u_int port; /**< port */
u_int region; /**< region */
bhndb_priority_t priority; /**< port priority */
+ uint32_t alloc_flags; /**< port allocation flags (@see bhndb_alloc_flags) */
};
/**
Modified: head/sys/dev/bhnd/bhndb/bhndb_hwdata.c
==============================================================================
--- head/sys/dev/bhnd/bhndb/bhndb_hwdata.c Mon Nov 27 23:48:21 2017 (r326296)
+++ head/sys/dev/bhnd/bhndb/bhndb_hwdata.c Tue Nov 28 00:12:14 2017 (r326297)
@@ -76,19 +76,24 @@ __FBSDID("$FreeBSD$");
BHNDB_PORTS(__VA_ARGS__) \
}
-/* Define a port priority record for the type/port/region
- * triplet. */
-#define BHNDB_PORT_PRIO(_type, _port, _region, _priority) { \
+/* Define a port priority record for the type/port/region triplet, optionally
+ * specifying port allocation flags as the final argument */
+#define BHNDB_PORT_PRIO(_type, _port, _region, _priority, ...) \
+ _BHNDB_PORT_PRIO(_type, _port, _region, _priority, ## __VA_ARGS__, 0)
+
+#define _BHNDB_PORT_PRIO(_type, _port, _region, _priority, _flags, ...) \
+{ \
.type = (BHND_PORT_ ## _type), \
.port = _port, \
.region = _region, \
- .priority = (BHNDB_PRIORITY_ ## _priority) \
+ .priority = (BHNDB_PRIORITY_ ## _priority), \
+ .alloc_flags = (_flags) \
}
/* Define a port priority record for the default (_type, 0, 0) type/port/region
* triplet. */
-#define BHNDB_PORT0_PRIO(_type, _priority) \
- BHNDB_PORT_PRIO(_type, 0, 0, _priority)
+#define BHNDB_PORT0_PRIO(_type, _priority, ...) \
+ BHNDB_PORT_PRIO(_type, 0, 0, _priority, ## __VA_ARGS__, 0)
/**
* Generic resource priority configuration usable with all currently supported
@@ -170,10 +175,14 @@ const struct bhndb_hw_priority bhndb_siba_priority_tab
* Agent ports are marked as 'NONE' on siba(4) devices, as they
* will be fully mappable via register windows shared with the
* device0.0 port.
+ *
+ * To support early PCI_V0 devices, we enable FULFILL_ON_OVERCOMMIT for
+ * ChipCommon.
*/
BHNDB_CLASS_PRIO(CC, LOW,
/* Device Block */
- BHNDB_PORT_PRIO(DEVICE, 0, 0, LOW)
+ BHNDB_PORT_PRIO(DEVICE, 0, 0, LOW,
+ BHNDB_ALLOC_FULFILL_ON_OVERCOMMIT)
),
BHNDB_CLASS_PRIO(PMU, LOW,
@@ -193,4 +202,4 @@ const struct bhndb_hw_priority bhndb_siba_priority_tab
),
BHNDB_HW_PRIORITY_TABLE_END
-};
\ No newline at end of file
+};
Modified: head/sys/dev/bhnd/bhndb/bhndb_pci.c
==============================================================================
--- head/sys/dev/bhnd/bhndb/bhndb_pci.c Mon Nov 27 23:48:21 2017 (r326296)
+++ head/sys/dev/bhnd/bhndb/bhndb_pci.c Tue Nov 28 00:12:14 2017 (r326297)
@@ -707,26 +707,28 @@ bhndb_pci_sprom_size(struct bhndb_pci_softc *sc)
* Return the host resource providing a static mapping of the PCI core's
* registers.
*
- * @param sc bhndb PCI driver state.
- * @param[out] res On success, the host resource containing our PCI
- * core's register window.
- * @param[out] offset On success, the offset of the PCI core registers within
- * @p res.
+ * @param sc bhndb PCI driver state.
+ * @param offset The required readable offset within the PCI core
+ * register block.
+ * @param size The required readable size at @p offset.
+ * @param[out] res On success, the host resource containing our PCI
+ * core's register window.
+ * @param[out] res_offset On success, the @p offset relative to @p res.
*
* @retval 0 success
* @retval ENXIO if a valid static register window mapping the PCI core
* registers is not available.
*/
static int
-bhndb_pci_get_core_regs(struct bhndb_pci_softc *sc, struct resource **res,
- bus_size_t *offset)
+bhndb_pci_get_core_regs(struct bhndb_pci_softc *sc, bus_size_t offset,
+ bus_size_t size, struct resource **res, bus_size_t *res_offset)
{
const struct bhndb_regwin *win;
struct resource *r;
- /* Locate the static register window mapping the PCI core */
+ /* Locate the static register window mapping the requested offset */
win = bhndb_regwin_find_core(sc->bhndb.bus_res->cfg->register_windows,
- sc->pci_devclass, 0, BHND_PORT_DEVICE, 0, 0);
+ sc->pci_devclass, 0, BHND_PORT_DEVICE, 0, 0, offset, size);
if (win == NULL) {
device_printf(sc->dev, "missing PCI core register window\n");
return (ENXIO);
@@ -739,8 +741,11 @@ bhndb_pci_get_core_regs(struct bhndb_pci_softc *sc, st
return (ENXIO);
}
+ KASSERT(offset >= win->d.core.offset, ("offset %#jx outside of "
+ "register window", (uintmax_t)offset));
+
*res = r;
- *offset = win->win_offset;
+ *res_offset = win->win_offset + (offset - win->d.core.offset);
return (0);
}
@@ -761,18 +766,21 @@ bhndb_pci_write_core(struct bhndb_pci_softc *sc, bus_s
bus_size_t r_offset;
int error;
- if ((error = bhndb_pci_get_core_regs(sc, &r, &r_offset)))
- panic("no PCI core registers: %d", error);
+ error = bhndb_pci_get_core_regs(sc, offset, width, &r, &r_offset);
+ if (error) {
+ panic("no PCI register window mapping %#jx+%#x: %d",
+ (uintmax_t)offset, width, error);
+ }
switch (width) {
case 1:
- bus_write_1(r, r_offset + offset, value);
+ bus_write_1(r, r_offset, value);
break;
case 2:
- bus_write_2(r, r_offset + offset, value);
+ bus_write_2(r, r_offset, value);
break;
case 4:
- bus_write_4(r, r_offset + offset, value);
+ bus_write_4(r, r_offset, value);
break;
default:
panic("invalid width: %u", width);
@@ -794,16 +802,19 @@ bhndb_pci_read_core(struct bhndb_pci_softc *sc, bus_si
bus_size_t r_offset;
int error;
- if ((error = bhndb_pci_get_core_regs(sc, &r, &r_offset)))
- panic("no PCI core registers: %d", error);
+ error = bhndb_pci_get_core_regs(sc, offset, width, &r, &r_offset);
+ if (error) {
+ panic("no PCI register window mapping %#jx+%#x: %d",
+ (uintmax_t)offset, width, error);
+ }
switch (width) {
case 1:
- return (bus_read_1(r, r_offset + offset));
+ return (bus_read_1(r, r_offset));
case 2:
- return (bus_read_2(r, r_offset + offset));
+ return (bus_read_2(r, r_offset));
case 4:
- return (bus_read_4(r, r_offset + offset));
+ return (bus_read_4(r, r_offset));
default:
panic("invalid width: %u", width);
}
@@ -1057,7 +1068,7 @@ bhndb_enable_pci_clocks(device_t dev)
pci_dev = device_get_parent(dev);
/* Only supported and required on PCI devices */
- if (!bhndb_is_pcie_attached(dev))
+ if (bhndb_is_pcie_attached(dev))
return (0);
/* Read state of XTAL pin */
Modified: head/sys/dev/bhnd/bhndb/bhndb_pci_hwdata.c
==============================================================================
--- head/sys/dev/bhnd/bhndb/bhndb_pci_hwdata.c Mon Nov 27 23:48:21 2017 (r326296)
+++ head/sys/dev/bhnd/bhndb/bhndb_pci_hwdata.c Tue Nov 28 00:12:14 2017 (r326297)
@@ -312,7 +312,13 @@ static const struct bhndb_hwcfg bhndb_pci_hwcfg_v0 = {
.res = { SYS_RES_MEMORY, PCIR_BAR(0) }
},
- /* bar0+0x1800: pci core registers */
+ /*
+ * bar0+0x1800: pci core registers.
+ *
+ * Does not include the SSB CFG registers found at the end of
+ * the 4K core register block; these are mapped non-contigiously
+ * by the next entry.
+ */
{
.win_type = BHNDB_REGWIN_T_CORE,
.win_offset = BHNDB_PCI_V0_BAR0_PCIREG_OFFSET,
@@ -322,10 +328,27 @@ static const struct bhndb_hwcfg bhndb_pci_hwcfg_v0 = {
.unit = 0,
.port = 0,
.region = 0,
+ .port_type = BHND_PORT_DEVICE,
+ },
+ .res = { SYS_RES_MEMORY, PCIR_BAR(0) }
+ },
+
+ /* bar0+0x1E00: pci core (SSB CFG registers) */
+ {
+ .win_type = BHNDB_REGWIN_T_CORE,
+ .win_offset = BHNDB_PCI_V0_BAR0_PCISB_OFFSET ,
+ .win_size = BHNDB_PCI_V0_BAR0_PCISB_SIZE,
+ .d.core = {
+ .class = BHND_DEVCLASS_PCI,
+ .unit = 0,
+ .port = 0,
+ .region = 0,
+ .offset = BHNDB_PCI_V0_BAR0_PCISB_COREOFF,
.port_type = BHND_PORT_DEVICE
},
.res = { SYS_RES_MEMORY, PCIR_BAR(0) }
},
+
BHNDB_REGWIN_TABLE_END
},
Modified: head/sys/dev/bhnd/bhndb/bhndb_pcireg.h
==============================================================================
--- head/sys/dev/bhnd/bhndb/bhndb_pcireg.h Mon Nov 27 23:48:21 2017 (r326296)
+++ head/sys/dev/bhnd/bhndb/bhndb_pcireg.h Tue Nov 28 00:12:14 2017 (r326297)
@@ -40,7 +40,8 @@
* [offset+ size] type description
* [0x0000+0x1000] dynamic mapped backplane address space (window 0).
* [0x1000+0x0800] fixed SPROM shadow
- * [0x1800+0x0800] fixed pci core registers
+ * [0x1800+0x0E00] fixed pci core device registers
+ * [0x1E00+0x0200] fixed pci core siba config registers
*
* == PCI_V1 ==
* Applies to:
@@ -133,8 +134,11 @@
#define BHNDB_PCI_V0_BAR0_WIN0_SIZE 0x1000
#define BHNDB_PCI_V0_BAR0_SPROM_OFFSET 0x1000 /* bar0 + 4K accesses sprom shadow (in pci core) */
#define BHNDB_PCI_V0_BAR0_SPROM_SIZE 0x0800
-#define BHNDB_PCI_V0_BAR0_PCIREG_OFFSET 0x1800 /* bar0 + 6K accesses pci core registers */
-#define BHNDB_PCI_V0_BAR0_PCIREG_SIZE 0x0800
+#define BHNDB_PCI_V0_BAR0_PCIREG_OFFSET 0x1800 /* bar0 + 6K accesses pci core registers (not including SSB CFG registers) */
+#define BHNDB_PCI_V0_BAR0_PCIREG_SIZE 0x0E00
+#define BHNDB_PCI_V0_BAR0_PCISB_OFFSET 0x1E00 /* bar0 + 7.5K accesses pci core's SSB CFG register blocks */
+#define BHNDB_PCI_V0_BAR0_PCISB_SIZE 0x0200
+#define BHNDB_PCI_V0_BAR0_PCISB_COREOFF 0xE00 /* mapped offset relative to the core base address */
/* PCI_V1 */
#define BHNDB_PCI_V1_BAR0_WIN0_CONTROL 0x80 /* backplane address space accessed by BAR0/WIN0 */
Modified: head/sys/dev/bhnd/bhndb/bhndb_private.h
==============================================================================
--- head/sys/dev/bhnd/bhndb/bhndb_private.h Mon Nov 27 23:48:21 2017 (r326296)
+++ head/sys/dev/bhnd/bhndb/bhndb_private.h Tue Nov 28 00:12:14 2017 (r326297)
@@ -70,6 +70,7 @@ int bhndb_add_resource_region(
struct bhndb_resources *br,
bhnd_addr_t addr, bhnd_size_t size,
bhndb_priority_t priority,
+ uint32_t alloc_flags,
const struct bhndb_regwin *static_regwin);
int bhndb_find_resource_limits(
@@ -93,6 +94,10 @@ struct bhndb_intr_handler *bhndb_find_intr_handler(
struct bhndb_resources *br,
void *cookiep);
+bool bhndb_has_static_region_mapping(
+ struct bhndb_resources *br,
+ bhnd_addr_t addr, bhnd_size_t size);
+
struct bhndb_region *bhndb_find_resource_region(
struct bhndb_resources *br,
bhnd_addr_t addr, bhnd_size_t size);
@@ -120,11 +125,25 @@ int bhndb_dw_set_addr(device_t dev,
struct bhndb_dw_alloc *dwa,
bus_addr_t addr, bus_size_t size);
+struct bhndb_dw_alloc *bhndb_dw_steal(struct bhndb_resources *br,
+ bus_addr_t *saved);
+
+void bhndb_dw_return_stolen(device_t dev,
+ struct bhndb_resources *br,
+ struct bhndb_dw_alloc *dwa,
+ bus_addr_t saved);
+
const struct bhndb_hw_priority *bhndb_hw_priority_find_core(
const struct bhndb_hw_priority *table,
struct bhnd_core_info *core);
+const struct bhndb_port_priority *bhndb_hw_priorty_find_port(
+ const struct bhndb_hw_priority *table,
+ struct bhnd_core_info *core,
+ bhnd_port_type port_type, u_int port,
+ u_int region);
+
/**
* Dynamic register window allocation reference.
*/
@@ -152,6 +171,7 @@ struct bhndb_region {
bhnd_addr_t addr; /**< start of mapped range */
bhnd_size_t size; /**< size of mapped range */
bhndb_priority_t priority; /**< direct resource allocation priority */
+ uint32_t alloc_flags; /**< resource allocation flags (@see bhndb_alloc_flags) */
const struct bhndb_regwin *static_regwin; /**< fixed mapping regwin, if any */
STAILQ_ENTRY(bhndb_region) link;
@@ -185,6 +205,7 @@ struct bhndb_resources {
STAILQ_HEAD(, bhndb_region) bus_regions; /**< bus region descriptors */
+ struct mtx dw_steal_mtx; /**< spinlock must be held when stealing a dynamic window allocation */
struct bhndb_dw_alloc *dw_alloc; /**< dynamic window allocation records */
size_t dwa_count; /**< number of dynamic windows available. */
bitstr_t *dwa_freelist; /**< dynamic window free list */
Modified: head/sys/dev/bhnd/bhndb/bhndb_subr.c
==============================================================================
--- head/sys/dev/bhnd/bhndb/bhndb_subr.c Mon Nov 27 23:48:21 2017 (r326296)
+++ head/sys/dev/bhnd/bhndb/bhndb_subr.c Tue Nov 28 00:12:14 2017 (r326297)
@@ -291,7 +291,10 @@ bhndb_alloc_resources(device_t dev, device_t parent_de
r->min_prio = BHNDB_PRIORITY_NONE;
STAILQ_INIT(&r->bus_regions);
STAILQ_INIT(&r->bus_intrs);
-
+
+ mtx_init(&r->dw_steal_mtx, device_get_nameunit(dev),
+ "bhndb dwa_steal lock", MTX_SPIN);
+
/* Initialize host address space resource manager. */
r->ht_mem_rman.rm_start = 0;
r->ht_mem_rman.rm_end = ~0;
@@ -492,6 +495,8 @@ failed:
if (r->res != NULL)
bhndb_release_host_resources(r->res);
+ mtx_destroy(&r->dw_steal_mtx);
+
free(r, M_BHND);
return (NULL);
@@ -626,6 +631,10 @@ bhndb_free_resources(struct bhndb_resources *br)
free(br->dw_alloc, M_BHND);
free(br->dwa_freelist, M_BHND);
+
+ mtx_destroy(&br->dw_steal_mtx);
+
+ free(br, M_BHND);
}
/**
@@ -1054,6 +1063,7 @@ bhndb_find_resource_limits(struct bhndb_resources *br,
* @param size The size of this region.
* @param priority The resource priority to be assigned to allocations
* made within this bus region.
+ * @param alloc_flags resource allocation flags (@see bhndb_alloc_flags)
* @param static_regwin If available, a static register window mapping this
* bus region entry. If not available, NULL.
*
@@ -1062,7 +1072,7 @@ bhndb_find_resource_limits(struct bhndb_resources *br,
*/
int
bhndb_add_resource_region(struct bhndb_resources *br, bhnd_addr_t addr,
- bhnd_size_t size, bhndb_priority_t priority,
+ bhnd_size_t size, bhndb_priority_t priority, uint32_t alloc_flags,
const struct bhndb_regwin *static_regwin)
{
struct bhndb_region *reg;
@@ -1076,6 +1086,7 @@ bhndb_add_resource_region(struct bhndb_resources *br,
.addr = addr,
.size = size,
.priority = priority,
+ .alloc_flags = alloc_flags,
.static_regwin = static_regwin
};
@@ -1084,7 +1095,40 @@ bhndb_add_resource_region(struct bhndb_resources *br,
return (0);
}
+/**
+ * Return true if a mapping of @p size bytes at @p addr is provided by either
+ * one contiguous bus region, or by multiple discontiguous regions.
+ *
+ * @param br The resource state to query.
+ * @param addr The requested starting address.
+ * @param size The requested size.
+ */
+bool
+bhndb_has_static_region_mapping(struct bhndb_resources *br,
+ bhnd_addr_t addr, bhnd_size_t size)
+{
+ struct bhndb_region *region;
+ bhnd_addr_t r_addr;
+ r_addr = addr;
+ while ((region = bhndb_find_resource_region(br, r_addr, 1)) != NULL) {
+ /* Must be backed by a static register window */
+ if (region->static_regwin == NULL)
+ return (false);
+
+ /* Adjust the search offset */
+ r_addr += region->size;
+
+ /* Have we traversed a complete (if discontiguous) mapping? */
+ if (r_addr == addr + size)
+ return (true);
+
+ }
+
+ /* No complete mapping found */
+ return (false);
+}
+
/**
* Find the bus region that maps @p size bytes at @p addr.
*
@@ -1302,7 +1346,7 @@ bhndb_dw_set_addr(device_t dev, struct bhndb_resources
rw = dwa->win;
- KASSERT(bhndb_dw_is_free(br, dwa),
+ KASSERT(bhndb_dw_is_free(br, dwa) || mtx_owned(&br->dw_steal_mtx),
("attempting to set the target address on an in-use window"));
/* Page-align the target address */
@@ -1324,6 +1368,74 @@ bhndb_dw_set_addr(device_t dev, struct bhndb_resources
}
/**
+ * Steal an in-use allocation record from @p br, returning the record's current
+ * target in @p saved on success.
+ *
+ * This function acquires a mutex and disables interrupts; callers should
+ * avoid holding a stolen window longer than required to issue an I/O
+ * request.
+ *
+ * A successful call to bhndb_dw_steal() must be balanced with a call to
+ * bhndb_dw_return_stolen().
+ *
+ * @param br The resource state from which a window should be stolen.
+ * @param saved The stolen window's saved target address.
+ *
+ * @retval non-NULL success
+ * @retval NULL no dynamic window regions are defined.
+ */
+struct bhndb_dw_alloc *
+bhndb_dw_steal(struct bhndb_resources *br, bus_addr_t *saved)
+{
+ struct bhndb_dw_alloc *dw_stolen;
+
+ KASSERT(bhndb_dw_next_free(br) == NULL,
+ ("attempting to steal an in-use window while free windows remain"));
+
+ /* Nothing to steal from? */
+ if (br->dwa_count == 0)
+ return (NULL);
+
+ /*
+ * Acquire our steal spinlock; this will be released in
+ * bhndb_dw_return_stolen().
+ *
+ * Acquiring also disables interrupts, which is required when one is
+ * stealing an in-use existing register window.
+ */
+ mtx_lock_spin(&br->dw_steal_mtx);
+
+ dw_stolen = &br->dw_alloc[0];
+ *saved = dw_stolen->target;
+ return (dw_stolen);
+}
+
+/**
+ * Return an allocation record previously stolen using bhndb_dw_steal().
+ *
+ * @param dev The device on which to issue a BHNDB_SET_WINDOW_ADDR() request.
+ * @param br The resource state owning @p dwa.
+ * @param dwa The allocation record to be returned.
+ * @param saved The original target address provided by bhndb_dw_steal().
+ */
+void
+bhndb_dw_return_stolen(device_t dev, struct bhndb_resources *br,
+ struct bhndb_dw_alloc *dwa, bus_addr_t saved)
+{
+ int error;
+
+ mtx_assert(&br->dw_steal_mtx, MA_OWNED);
+
+ error = bhndb_dw_set_addr(dev, br, dwa, saved, 0);
+ if (error) {
+ panic("failed to restore register window target %#jx: %d\n",
+ (uintmax_t)saved, error);
+ }
+
+ mtx_unlock_spin(&br->dw_steal_mtx);
+}
+
+/**
* Return the count of @p type register windows in @p table.
*
* @param table The table to search.
@@ -1380,18 +1492,24 @@ bhndb_regwin_find_type(const struct bhndb_regwin *tabl
* @param port_type The required port type.
* @param port The required port.
* @param region The required region.
+ * @param offset The required readable core register block offset.
+ * @param min_size The required minimum readable size at @p offset.
*
* @retval bhndb_regwin The first matching window.
* @retval NULL If no matching window was found.
*/
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)
+ int unit, bhnd_port_type port_type, u_int port, u_int region,
+ bus_size_t offset, bus_size_t min_size)
{
const struct bhndb_regwin *rw;
-
+
for (rw = table; rw->win_type != BHNDB_REGWIN_T_INVALID; rw++)
{
+ bus_size_t rw_offset;
+
+ /* Match on core, port, and region attributes */
if (rw->win_type != BHNDB_REGWIN_T_CORE)
continue;
@@ -1410,6 +1528,19 @@ bhndb_regwin_find_core(const struct bhndb_regwin *tabl
if (rw->d.core.region != region)
continue;
+ /* Verify that the requested range is mapped within
+ * this register window */
+ if (rw->d.core.offset > offset)
+ continue;
+
+ rw_offset = offset - rw->d.core.offset;
+
+ if (rw->win_size < rw_offset)
+ continue;
+
+ if (rw->win_size - rw_offset < min_size)
+ continue;
+
return (rw);
}
@@ -1429,7 +1560,8 @@ bhndb_regwin_find_core(const struct bhndb_regwin *tabl
* @param port_type The required port type.
* @param port The required port.
* @param region The required region.
- * @param min_size The minimum window size.
+ * @param offset The required readable core register block offset.
+ * @param min_size The required minimum readable size at @p offset.
*
* @retval bhndb_regwin The first matching window.
* @retval NULL If no matching window was found.
@@ -1437,13 +1569,13 @@ bhndb_regwin_find_core(const struct bhndb_regwin *tabl
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)
+ u_int region, bus_size_t offset, bus_size_t min_size)
{
const struct bhndb_regwin *rw;
/* Prefer a fixed core mapping */
rw = bhndb_regwin_find_core(table, class, unit, port_type,
- port, region);
+ port, region, offset, min_size);
if (rw != NULL)
return (rw);
@@ -1494,6 +1626,45 @@ bhndb_hw_priority_find_core(const struct bhndb_hw_prio
for (hp = table; hp->ports != NULL; hp++) {
if (bhnd_core_matches(core, &hp->match))
return (hp);
+ }
+
+ /* not found */
+ return (NULL);
+}
+
+
+/**
+ * Search for a port resource priority descriptor in @p table.
+ *
+ * @param table The table to search.
+ * @param core The core to match against @p table.
+ * @param port_type The required port type.
+ * @param port The required port.
+ * @param region The required region.
+ */
+const struct bhndb_port_priority *
+bhndb_hw_priorty_find_port(const struct bhndb_hw_priority *table,
+ struct bhnd_core_info *core, bhnd_port_type port_type, u_int port,
+ u_int region)
+{
+ const struct bhndb_hw_priority *hp;
+
+ if ((hp = bhndb_hw_priority_find_core(table, core)) == NULL)
+ return (NULL);
+
+ for (u_int i = 0; i < hp->num_ports; i++) {
+ const struct bhndb_port_priority *pp = &hp->ports[i];
+
+ if (pp->type != port_type)
+ continue;
+
+ if (pp->port != port)
+ continue;
+
+ if (pp->region != region)
+ continue;
+
+ return (pp);
}
/* not found */
Modified: head/sys/dev/bhnd/bhndb/bhndbvar.h
==============================================================================
--- head/sys/dev/bhnd/bhndb/bhndbvar.h Mon Nov 27 23:48:21 2017 (r326296)
+++ head/sys/dev/bhnd/bhndb/bhndbvar.h Tue Nov 28 00:12:14 2017 (r326297)
@@ -118,13 +118,15 @@ 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);
+ u_int region, bus_size_t offset,
+ bus_size_t min_size);
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);
+ u_int region, bus_size_t offset,
+ bus_size_t min_size);
bool bhndb_regwin_match_core(
const struct bhndb_regwin *regw,
More information about the svn-src-all
mailing list