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
This commit is contained in:
parent
b8bfab43ad
commit
eaa5fb4b80
@ -115,8 +115,8 @@ 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_erom_t *erom,
|
||||
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;
|
||||
@ -294,6 +297,18 @@ bhndb_init_region_cfg(struct bhndb_softc *sc, bhnd_erom_t *erom,
|
||||
return (error);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
@ -307,14 +322,25 @@ bhndb_init_region_cfg(struct bhndb_softc *sc, bhnd_erom_t *erom,
|
||||
*/
|
||||
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_erom_t *erom,
|
||||
* 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_erom_t *erom,
|
||||
}
|
||||
|
||||
/* 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_erom_t *erom,
|
||||
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 child,
|
||||
* 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_addr_t addr,
|
||||
*offset = dwa->win->win_offset;
|
||||
*offset += addr - dwa->target;
|
||||
|
||||
*stolen = false;
|
||||
return (dwa);
|
||||
}
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
/* not found */
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
@ -1649,9 +1696,8 @@ bhndb_io_resource_slow(struct bhndb_softc *sc, bus_addr_t addr,
|
||||
* 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_addr_t addr,
|
||||
* @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 addr, bus_size_t size,
|
||||
* 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 addr, bus_size_t size,
|
||||
|
||||
/* 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 addr, bus_size_t size,
|
||||
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 addr, bus_size_t size,
|
||||
("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 */
|
||||
|
@ -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). */
|
||||
@ -149,6 +150,26 @@ typedef enum {
|
||||
BHNDB_PRIORITY_HIGH = 3
|
||||
} 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.
|
||||
*/
|
||||
@ -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) */
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -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_table[] = {
|
||||
* 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_table[] = {
|
||||
),
|
||||
|
||||
BHNDB_HW_PRIORITY_TABLE_END
|
||||
};
|
||||
};
|
||||
|
@ -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, struct resource **res,
|
||||
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_size_t offset,
|
||||
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_size_t offset, u_int width)
|
||||
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 */
|
||||
|
@ -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
|
||||
},
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -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,10 +125,24 @@ 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 */
|
||||
|
@ -291,7 +291,10 @@ bhndb_alloc_resources(device_t dev, device_t parent_dev,
|
||||
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, int type,
|
||||
* @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 type,
|
||||
*/
|
||||
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, bhnd_addr_t addr,
|
||||
.addr = addr,
|
||||
.size = size,
|
||||
.priority = priority,
|
||||
.alloc_flags = alloc_flags,
|
||||
.static_regwin = static_regwin
|
||||
};
|
||||
|
||||
@ -1084,6 +1095,39 @@ bhndb_add_resource_region(struct bhndb_resources *br, bhnd_addr_t addr,
|
||||
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 *br,
|
||||
|
||||
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 */
|
||||
@ -1323,6 +1367,74 @@ bhndb_dw_set_addr(device_t dev, struct bhndb_resources *br,
|
||||
return (0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
@ -1380,18 +1492,24 @@ bhndb_regwin_find_type(const struct bhndb_regwin *table,
|
||||
* @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 *table, bhnd_devclass_t class,
|
||||
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 *table, bhnd_devclass_t class,
|
||||
* @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 *table, bhnd_devclass_t class,
|
||||
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);
|
||||
|
||||
@ -1499,3 +1631,42 @@ bhndb_hw_priority_find_core(const struct bhndb_hw_priority *table,
|
||||
/* 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 */
|
||||
return (NULL);
|
||||
}
|
||||
|
@ -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,
|
||||
|
Loading…
x
Reference in New Issue
Block a user