[bhnd] Standardize bhnd device tables and quirk matching.

This add a bhnd device table mechanism that standardizes matching of
devices on the bhnd(4) bus, discovery of device quirk flags, and should
be pluggable into the new PNPINFO machinery.

Submitted by:	Landon Fuller <landonf@landonf.org>
Differential Revision:	https://reviews.freebsd.org/D5759
This commit is contained in:
Adrian Chadd 2016-04-19 15:56:39 +00:00
parent 386fb140a6
commit 36e4410ab0
6 changed files with 244 additions and 132 deletions

View File

@ -167,7 +167,41 @@ struct bhnd_hwrev_match {
/**
* Wildcard hardware revision match descriptor.
*/
#define BHND_HWREV_MATCH_ANY { BHND_HWREV_INVALID, BHND_HWREV_INVALID }
#define BHND_HWREV_ANY { BHND_HWREV_INVALID, BHND_HWREV_INVALID }
#define BHND_HWREV_IS_ANY(_m) \
((_m)->start == BHND_HWREV_INVALID && (_m)->end == BHND_HWREV_INVALID)
/**
* Hardware revision match descriptor for an inclusive range.
*
* @param _start The first applicable hardware revision.
* @param _end The last applicable hardware revision, or BHND_HWREV_INVALID
* to match on any revision.
*/
#define BHND_HWREV_RANGE(_start, _end) { _start, _end }
/**
* Hardware revision match descriptor for a single revision.
*
* @param _hwrev The hardware revision to match on.
*/
#define BHND_HWREV_EQ(_hwrev) BHND_HWREV_RANGE(_hwrev, _hwrev)
/**
* Hardware revision match descriptor for any revision equal to or greater
* than @p _start.
*
* @param _start The first hardware revision to match on.
*/
#define BHND_HWREV_GTE(_start) BHND_HWREV_RANGE(_start, BHND_HWREV_INVALID)
/**
* Hardware revision match descriptor for any revision equal to or less
* than @p _end.
*
* @param _end The last hardware revision to match on.
*/
#define BHND_HWREV_LTE(_end) BHND_HWREV_RANGE(0, _end)
/** A core match descriptor. */
@ -179,60 +213,58 @@ struct bhnd_core_match {
int unit; /**< required core unit, or -1 */
};
/**
* Core match descriptor matching against the given @p _vendor, @p _device,
* and @p _hwrev match descriptors.
*/
#define BHND_CORE_MATCH(_vendor, _device, _hwrev) \
{ _vendor, _device, _hwrev, BHND_DEVCLASS_INVALID, -1 }
/**
* Wildcard core match descriptor.
*/
#define BHND_CORE_MATCH_ANY \
{ \
.vendor = BHND_MFGID_INVALID, \
.device = BHND_COREID_INVALID, \
.hwrev = BHND_HWREV_ANY, \
.class = BHND_DEVCLASS_INVALID, \
.unit = -1 \
}
/**
* Revision-specific hardware quirk descriptor.
*
* Defines a set of quirk flags applicable to a range of hardware
* revisions.
* Device quirk table descriptor.
*/
struct bhnd_device_quirk {
struct bhnd_hwrev_match hwrev; /**< applicable hardware revisions */
uint32_t quirks; /**< applicable quirk flags */
uint32_t quirks; /**< quirk flags */
};
#define BHND_DEVICE_QUIRK_END { BHND_HWREV_ANY, 0 }
#define BHND_DEVICE_QUIRK_IS_END(_q) \
(BHND_HWREV_IS_ANY(&(_q)->hwrev) && (_q)->quirks == 0)
enum {
BHND_DF_ANY = 0,
BHND_DF_HOSTB = (1<<0) /**< core is serving as the bus'
* host bridge */
};
/**
* Define a bhnd_device_quirk over a range of hardware revisions.
*
* @param _start The first applicable hardware revision.
* @param _end The last applicable hardware revision, or BHND_HWREV_INVALID
* to match on any revision.
* @param _quirks Quirk flags applicable to this revision range.
*/
#define BHND_QUIRK_HWREV_RANGE(_start, _end, _quirks) \
{ .hwrev = { _start, _end }, .quirks = _quirks }
/** Device probe table descriptor */
struct bhnd_device {
const struct bhnd_core_match core; /**< core match descriptor */
const char *desc; /**< device description, or NULL. */
const struct bhnd_device_quirk *quirks_table; /**< quirks table for this device, or NULL */
uint32_t device_flags; /**< required BHND_DF_* flags */
};
/**
* Define a bhnd_device_quirk for a specific hardware revision.
*
* @param _hwrev The hardware revision to match on.
* @param _quirks Quirk flags applicable to this revision.
*/
#define BHND_QUIRK_HWREV_EQ(_hwrev, _quirks) \
BHND_QUIRK_HWREV_RANGE(_hwrev, _hwrev, _quirks)
#define _BHND_DEVICE(_device, _desc, _quirks, _flags, ...) \
{ BHND_CORE_MATCH(BHND_MFGID_BCM, BHND_COREID_ ## _device, \
BHND_HWREV_ANY), _desc, _quirks, _flags }
/**
* Define a bhnd_device_quirk for any hardware revision equal or greater
* than @p _start.
*
* @param _start The first hardware revision to match on.
* @param _quirks Quirk flags applicable to this revision.
*/
#define BHND_QUIRK_HWREV_GTE(_start, _quirks) \
BHND_QUIRK_HWREV_RANGE(_start, BHND_HWREV_INVALID, _quirks)
#define BHND_DEVICE(_device, _desc, _quirks, ...) \
_BHND_DEVICE(_device, _desc, _quirks, ## __VA_ARGS__, 0)
/**
* Define a bhnd_device_quirk for any hardware revision equal or less
* than @p _end.
*
* @param _end The last hardware revision to match on.
* @param _quirks Quirk flags applicable to this revision.
*/
#define BHND_QUIRK_HWREV_LTE(_end, _quirks) \
BHND_QUIRK_HWREV_RANGE(0, _end, _quirks)
/** Mark the end of a bhnd_device_quirk table. */
#define BHND_QUIRK_HWREV_END { BHND_HWREV_MATCH_ANY, 0 }
#define BHND_DEVICE_END { BHND_CORE_MATCH_ANY, NULL, NULL, 0 }
const char *bhnd_vendor_name(uint16_t vendor);
const char *bhnd_port_type_name(bhnd_port_type port_type);
@ -271,6 +303,14 @@ bool bhnd_hwrev_matches(uint16_t hwrev,
bool bhnd_device_matches(device_t dev,
const struct bhnd_core_match *desc);
const struct bhnd_device *bhnd_device_lookup(device_t dev,
const struct bhnd_device *table,
size_t entry_size);
uint32_t bhnd_device_quirks(device_t dev,
const struct bhnd_device *table,
size_t entry_size);
struct bhnd_core_info bhnd_get_core_info(device_t dev);
@ -290,9 +330,9 @@ int bhnd_read_chipid(device_t dev,
bus_size_t chipc_offset,
struct bhnd_chipid *result);
void bhnd_set_generic_core_desc(device_t dev);
void bhnd_set_custom_core_desc(device_t dev,
const char *name);
void bhnd_set_default_core_desc(device_t dev);
bool bhnd_bus_generic_is_hostb_device(device_t dev,

View File

@ -469,6 +469,85 @@ bhnd_device_matches(device_t dev, const struct bhnd_core_match *desc)
return bhnd_core_matches(&ci, desc);
}
/**
* Search @p table for an entry matching @p dev.
*
* @param dev A bhnd device to match against @p table.
* @param table The device table to search.
* @param entry_size The @p table entry size, in bytes.
*
* @retval bhnd_device the first matching device, if any.
* @retval NULL if no matching device is found in @p table.
*/
const struct bhnd_device *
bhnd_device_lookup(device_t dev, const struct bhnd_device *table,
size_t entry_size)
{
const struct bhnd_device *entry;
for (entry = table; entry->desc != NULL; entry =
(const struct bhnd_device *) ((const char *) entry + entry_size))
{
/* match core info */
if (!bhnd_device_matches(dev, &entry->core))
continue;
/* match device flags */
if (entry->device_flags & BHND_DF_HOSTB) {
if (!bhnd_is_hostb_device(dev))
continue;
}
/* device found */
return (entry);
}
/* not found */
return (NULL);
}
/**
* Scan @p table for all quirk flags applicable to @p dev.
*
* @param dev A bhnd device to match against @p table.
* @param table The device table to search.
* @param entry_size The @p table entry size, in bytes.
*
* @return returns all matching quirk flags.
*/
uint32_t
bhnd_device_quirks(device_t dev, const struct bhnd_device *table,
size_t entry_size)
{
const struct bhnd_device *dent;
const struct bhnd_device_quirk *qtable, *qent;
uint32_t quirks;
uint16_t hwrev;
hwrev = bhnd_get_hwrev(dev);
quirks = 0;
/* Find the quirk table */
if ((dent = bhnd_device_lookup(dev, table, entry_size)) == NULL) {
/* This is almost certainly a (caller) implementation bug */
device_printf(dev, "quirk lookup did not match any device\n");
return (0);
}
/* Quirks aren't a mandatory field */
if ((qtable = dent->quirks_table) == NULL)
return (0);
/* Collect matching quirk entries */
for (qent = qtable; !BHND_DEVICE_QUIRK_IS_END(qent); qent++) {
if (bhnd_hwrev_matches(hwrev, &qent->hwrev))
quirks |= qent->quirks;
}
return (quirks);
}
/**
* Allocate bhnd(4) resources defined in @p rs from a parent bus.
*
@ -619,25 +698,21 @@ cleanup:
}
/**
* Using the bhnd(4) bus-level core information, populate @p dev's device
* description.
* Using the bhnd(4) bus-level core information and a custom core name,
* populate @p dev's device description.
*
* @param dev A bhnd-bus attached device.
* @param dev_name The core's name (e.g. "SDIO Device Core")
*/
void
bhnd_set_generic_core_desc(device_t dev)
bhnd_set_custom_core_desc(device_t dev, const char *dev_name)
{
const char *dev_name;
const char *vendor_name;
char *desc;
vendor_name = bhnd_get_vendor_name(dev);
dev_name = bhnd_get_device_name(dev);
asprintf(&desc, M_BHND, "%s %s, rev %hhu",
bhnd_get_vendor_name(dev),
bhnd_get_device_name(dev),
bhnd_get_hwrev(dev));
asprintf(&desc, M_BHND, "%s %s, rev %hhu", vendor_name, dev_name,
bhnd_get_hwrev(dev));
if (desc != NULL) {
device_set_desc_copy(dev, desc);
@ -647,6 +722,18 @@ bhnd_set_generic_core_desc(device_t dev)
}
}
/**
* Using the bhnd(4) bus-level core information, populate @p dev's device
* description.
*
* @param dev A bhnd-bus attached device.
*/
void
bhnd_set_default_core_desc(device_t dev)
{
bhnd_set_custom_core_desc(dev, bhnd_get_device_name(dev));
}
/**
* Helper function for implementing BHND_BUS_IS_HOSTB_DEVICE().
*

View File

@ -111,56 +111,60 @@ static const struct bhndb_pci_id *bhndb_pci_find_core_id(
*/
static const struct bhndb_pci_id bhndb_pci_ids[] = {
/* PCI */
BHNDB_PCI_ID(PCI,
BHND_QUIRK_HWREV_GTE (0,
BHNDB_PCI_QUIRK_EXT_CLOCK_GATING |
BHNDB_PCI_QUIRK_SBTOPCI2_PREF_BURST),
{ BHND_COREID_PCI, BHND_PCI_REGFMT_PCI,
(struct bhnd_device_quirk[]) {
{ BHND_HWREV_GTE (0),
BHNDB_PCI_QUIRK_EXT_CLOCK_GATING |
BHNDB_PCI_QUIRK_SBTOPCI2_PREF_BURST },
BHND_QUIRK_HWREV_RANGE (0, 5,
BHNDB_PCI_QUIRK_SBINTVEC),
{ BHND_HWREV_RANGE (0, 5),
BHNDB_PCI_QUIRK_SBINTVEC },
BHND_QUIRK_HWREV_GTE (11,
BHNDB_PCI_QUIRK_SBTOPCI2_READMULTI |
BHNDB_PCI_QUIRK_CLKRUN_DSBL),
{ BHND_HWREV_GTE (11),
BHNDB_PCI_QUIRK_SBTOPCI2_READMULTI |
BHNDB_PCI_QUIRK_CLKRUN_DSBL },
BHND_QUIRK_HWREV_END
),
BHND_DEVICE_QUIRK_END
}
},
/* PCI Gen 1 */
BHNDB_PCI_ID(PCIE,
BHND_QUIRK_HWREV_EQ (0,
BHNDB_PCIE_QUIRK_SDR9_L0s_HANG),
{ BHND_COREID_PCIE, BHND_PCI_REGFMT_PCIE,
(struct bhnd_device_quirk[]) {
{ BHND_HWREV_EQ (0),
BHNDB_PCIE_QUIRK_SDR9_L0s_HANG },
BHND_QUIRK_HWREV_RANGE (0, 1,
BHNDB_PCIE_QUIRK_UR_STATUS_FIX),
{ BHND_HWREV_RANGE (0, 1),
BHNDB_PCIE_QUIRK_UR_STATUS_FIX },
BHND_QUIRK_HWREV_EQ (1,
BHNDB_PCIE_QUIRK_PCIPM_REQEN),
{ BHND_HWREV_EQ (1),
BHNDB_PCIE_QUIRK_PCIPM_REQEN },
BHND_QUIRK_HWREV_RANGE (3, 5,
BHNDB_PCIE_QUIRK_ASPM_OVR |
BHNDB_PCIE_QUIRK_SDR9_POLARITY |
BHNDB_PCIE_QUIRK_SDR9_NO_FREQRETRY),
{ BHND_HWREV_RANGE (3, 5),
BHNDB_PCIE_QUIRK_ASPM_OVR |
BHNDB_PCIE_QUIRK_SDR9_POLARITY |
BHNDB_PCIE_QUIRK_SDR9_NO_FREQRETRY },
BHND_QUIRK_HWREV_LTE (6,
BHNDB_PCIE_QUIRK_L1_IDLE_THRESH),
{ BHND_HWREV_LTE (6),
BHNDB_PCIE_QUIRK_L1_IDLE_THRESH },
BHND_QUIRK_HWREV_GTE (6,
BHNDB_PCIE_QUIRK_SPROM_L23_PCI_RESET),
{ BHND_HWREV_GTE (6),
BHNDB_PCIE_QUIRK_SPROM_L23_PCI_RESET },
BHND_QUIRK_HWREV_EQ (7,
BHNDB_PCIE_QUIRK_SERDES_NOPLLDOWN),
{ BHND_HWREV_EQ (7),
BHNDB_PCIE_QUIRK_SERDES_NOPLLDOWN },
BHND_QUIRK_HWREV_GTE (8,
BHNDB_PCIE_QUIRK_L1_TIMER_PERF),
{ BHND_HWREV_GTE (8),
BHNDB_PCIE_QUIRK_L1_TIMER_PERF },
BHND_QUIRK_HWREV_GTE (10,
BHNDB_PCIE_QUIRK_SD_C22_EXTADDR),
{ BHND_HWREV_GTE (10),
BHNDB_PCIE_QUIRK_SD_C22_EXTADDR },
BHND_QUIRK_HWREV_END
),
BHND_DEVICE_QUIRK_END
}
},
{ BHND_COREID_INVALID, BHND_PCI_REGFMT_PCI, NULL }
{ BHND_COREID_INVALID, BHND_PCI_REGFMT_PCI }
};

View File

@ -97,15 +97,6 @@ struct bhndb_pci_softc {
} sdr9_quirk_polarity;
};
/* Declare a bhndb_pci_id entry */
#define BHNDB_PCI_ID(_device, _desc, ...) { \
BHND_COREID_ ## _device, \
BHND_PCI_REGFMT_ ## _device, \
(struct bhnd_device_quirk[]) { \
__VA_ARGS__ \
} \
}
/*
* PCI/PCIe-Gen1 endpoint-mode device quirks
*/

View File

@ -62,21 +62,22 @@ static const struct resource_spec chipc_rspec[CHIPC_MAX_RSPEC] = {
{ -1, -1, 0 }
};
static struct bhnd_device_quirk chipc_quirks[];
/* Supported device identifiers */
static const struct chipc_device {
uint16_t device;
} chipc_devices[] = {
{ BHND_COREID_CC },
{ BHND_COREID_INVALID }
static const struct bhnd_device chipc_devices[] = {
BHND_DEVICE(CC, "", chipc_quirks),
BHND_DEVICE_END
};
/* Device quirks table */
static struct bhnd_device_quirk chipc_quirks[] = {
BHND_QUIRK_HWREV_RANGE (0, 21, CHIPC_QUIRK_ALWAYS_HAS_SPROM),
BHND_QUIRK_HWREV_EQ (22, CHIPC_QUIRK_SPROM_CHECK_CST_R22),
BHND_QUIRK_HWREV_RANGE (23, 31, CHIPC_QUIRK_SPROM_CHECK_CST_R23),
BHND_QUIRK_HWREV_GTE (35, CHIPC_QUIRK_SUPPORTS_NFLASH),
BHND_QUIRK_HWREV_END
{ BHND_HWREV_RANGE (0, 21), CHIPC_QUIRK_ALWAYS_HAS_SPROM },
{ BHND_HWREV_EQ (22), CHIPC_QUIRK_SPROM_CHECK_CST_R22 },
{ BHND_HWREV_RANGE (23, 31), CHIPC_QUIRK_SPROM_CHECK_CST_R23 },
{ BHND_HWREV_GTE (35), CHIPC_QUIRK_SUPPORTS_NFLASH },
BHND_DEVICE_QUIRK_END
};
/* quirk and capability flag convenience macros */
@ -95,25 +96,19 @@ static struct bhnd_device_quirk chipc_quirks[] = {
static int
chipc_probe(device_t dev)
{
const struct chipc_device *id;
const struct bhnd_device *id;
for (id = chipc_devices; id->device != BHND_COREID_INVALID; id++)
{
if (bhnd_get_vendor(dev) == BHND_MFGID_BCM &&
bhnd_get_device(dev) == id->device)
{
bhnd_set_generic_core_desc(dev);
return (BUS_PROBE_DEFAULT);
}
}
id = bhnd_device_lookup(dev, chipc_devices, sizeof(chipc_devices[0]));
if (id == NULL)
return (ENXIO);
return (ENXIO);
bhnd_set_default_core_desc(dev);
return (BUS_PROBE_DEFAULT);
}
static int
chipc_attach(device_t dev)
{
struct bhnd_device_quirk *dq;
struct chipc_softc *sc;
bhnd_addr_t enum_addr;
uint32_t ccid_reg;
@ -122,6 +117,8 @@ chipc_attach(device_t dev)
sc = device_get_softc(dev);
sc->dev = dev;
sc->quirks = bhnd_device_quirks(dev, chipc_devices,
sizeof(chipc_devices[0]));
/* Allocate bus resources */
memcpy(sc->rspec, chipc_rspec, sizeof(sc->rspec));
@ -155,13 +152,6 @@ chipc_attach(device_t dev)
sc->caps = bhnd_bus_read_4(sc->core, CHIPC_CAPABILITIES);
sc->cst = bhnd_bus_read_4(sc->core, CHIPC_CHIPST);
/* Populate the set of applicable quirk flags */
sc->quirks = 0;
for (dq = chipc_quirks; dq->quirks != 0; dq++) {
if (bhnd_hwrev_matches(bhnd_get_hwrev(dev), &dq->hwrev))
sc->quirks |= dq->quirks;
}
// TODO
switch (bhnd_chipc_nvram_src(dev)) {
case BHND_NVRAM_SRC_CIS:

View File

@ -68,7 +68,7 @@ bhnd_pci_hostb_probe(device_t dev)
if (!bhnd_is_hostb_device(dev))
return (ENXIO);
bhnd_set_generic_core_desc(dev);
bhnd_set_default_core_desc(dev);
return (BUS_PROBE_DEFAULT);
}