Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F152910918
D5763.id14661.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
97 KB
Referenced Files
None
Subscribers
None
D5763.id14661.diff
View Options
Index: sys/dev/bhnd/bhndb/bhndb_pci.c
===================================================================
--- sys/dev/bhnd/bhndb/bhndb_pci.c
+++ sys/dev/bhnd/bhndb/bhndb_pci.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2015 Landon Fuller <landon@landonf.org>
+ * Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -37,27 +37,11 @@
* bus (e.g. bcma or siba) via a Broadcom PCI core configured in end-point
* mode.
*
- * This driver handles all interactions with the PCI bridge core. On the
- * bridged bhnd bus, the PCI core device will be claimed by a simple
- * bhnd_hostb driver.
+ * This driver handles all host-level PCI interactions with a PCI/PCIe bridge
+ * core operating in endpoint mode. On the bridged bhnd bus, the PCI core
+ * device will be managed by a bhnd_pci_hostb driver.
*/
-// Quirk TODO
-// WARs for the following are not yet implemented:
-// - BHND_PCI_QUIRK_SBINTVEC
-// - BHND_PCIE_QUIRK_ASPM_OVR
-// - BHND_PCIE_QUIRK_SERDES_NOPLLDOWN
-// Quirks (and WARs) for the following are not yet defined:
-// - Power savings via MDIO BLK1/PWR_MGMT3 on PCIe hwrev 15-20, 21-22
-// - WOWL PME enable/disable
-// - 4360 PCIe SerDes Tx amplitude/deemphasis (vendor Apple, boards
-// BCM94360X51P2, BCM94360X51A).
-// - PCI latency timer (boards CB2_4321_BOARD, CB2_4321_AG_BOARD)
-// - Max SerDes TX drive strength (vendor Apple, pcie >= rev10,
-// board BCM94322X9)
-// - 700mV SerDes TX drive strength (chipid BCM4331, boards BCM94331X19,
-// BCM94331X28, BCM94331X29B, BCM94331X19C)
-
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/bus.h>
@@ -72,7 +56,6 @@
#include <dev/bhnd/bhnd.h>
#include <dev/bhnd/cores/pci/bhnd_pcireg.h>
-#include <dev/bhnd/cores/pci/mdio_pcievar.h>
#include "bhndb_pcireg.h"
#include "bhndb_pcivar.h"
@@ -86,128 +69,8 @@
static int bhndb_pci_fast_setregwin(struct bhndb_pci_softc *,
const struct bhndb_regwin *, bhnd_addr_t);
-static uint32_t bhndb_pcie_read_proto_reg(struct bhndb_pci_softc *sc,
- uint32_t addr);
-static void bhndb_pcie_write_proto_reg(struct bhndb_pci_softc *sc,
- uint32_t addr, uint32_t val);
-
static void bhndb_init_sromless_pci_config(struct bhndb_pci_softc *sc);
-static int bhndb_pci_wars_register_access(struct bhndb_pci_softc *sc);
-static int bhndb_pci_wars_early_once(struct bhndb_pci_softc *sc);
-static int bhndb_pci_wars_hwup(struct bhndb_pci_softc *sc);
-static int bhndb_pci_wars_hwdown(struct bhndb_pci_softc *sc);
-
-static uint32_t bhndb_pci_discover_quirks(struct bhndb_pci_softc *,
- const struct bhndb_pci_id *);
-
-static const struct bhndb_pci_id *bhndb_pci_find_core_id(
- struct bhnd_core_info *core);
-/*
- * Supported PCI bridge cores.
- *
- * This table defines quirks specific to core hwrev ranges; see also
- * bhndb_pci_discover_quirks() for additional quirk detection.
- */
-static const struct bhndb_pci_id bhndb_pci_ids[] = {
- /* PCI */
- { 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_HWREV_RANGE (0, 5),
- BHNDB_PCI_QUIRK_SBINTVEC },
-
- { BHND_HWREV_GTE (11),
- BHNDB_PCI_QUIRK_SBTOPCI2_READMULTI |
- BHNDB_PCI_QUIRK_CLKRUN_DSBL },
-
- BHND_DEVICE_QUIRK_END
- }
- },
-
- /* PCI Gen 1 */
- { BHND_COREID_PCIE, BHND_PCI_REGFMT_PCIE,
- (struct bhnd_device_quirk[]) {
- { BHND_HWREV_EQ (0),
- BHNDB_PCIE_QUIRK_SDR9_L0s_HANG },
-
- { BHND_HWREV_RANGE (0, 1),
- BHNDB_PCIE_QUIRK_UR_STATUS_FIX },
-
- { BHND_HWREV_EQ (1),
- BHNDB_PCIE_QUIRK_PCIPM_REQEN },
-
- { BHND_HWREV_RANGE (3, 5),
- BHNDB_PCIE_QUIRK_ASPM_OVR |
- BHNDB_PCIE_QUIRK_SDR9_POLARITY |
- BHNDB_PCIE_QUIRK_SDR9_NO_FREQRETRY },
-
- { BHND_HWREV_LTE (6),
- BHNDB_PCIE_QUIRK_L1_IDLE_THRESH },
-
- { BHND_HWREV_GTE (6),
- BHNDB_PCIE_QUIRK_SPROM_L23_PCI_RESET },
-
- { BHND_HWREV_EQ (7),
- BHNDB_PCIE_QUIRK_SERDES_NOPLLDOWN },
-
- { BHND_HWREV_GTE (8),
- BHNDB_PCIE_QUIRK_L1_TIMER_PERF },
-
- { BHND_HWREV_GTE (10),
- BHNDB_PCIE_QUIRK_SD_C22_EXTADDR },
-
- BHND_DEVICE_QUIRK_END
- }
- },
-
- { BHND_COREID_INVALID, BHND_PCI_REGFMT_PCI }
-};
-
-
-/* quirk flag convenience macros */
-#define BHNDB_PCI_QUIRK(_sc, _name) \
- ((_sc)->quirks & BHNDB_PCI_QUIRK_ ## _name)
-#define BHNDB_PCIE_QUIRK(_sc, _name) \
- ((_sc)->quirks & BHNDB_PCIE_QUIRK_ ## _name)
-
-#define BHNDB_PCI_ASSERT_QUIRK(_sc, name) \
- KASSERT(BHNDB_PCI_QUIRK((_sc), name), ("quirk " __STRING(_name) " not set"))
-#define BHNDB_PCIE_ASSERT_QUIRK(_sc, name) \
- KASSERT(BHNDB_PCIE_QUIRK((_sc), name), ("quirk " __STRING(_name) " not set"))
-
-
-/* bus_(read|write)_* convenience macros */
-#define BHNDB_PCI_READ_2(_sc, _reg) \
- bus_read_2((_sc)->mem_res, (_sc)->mem_off + (_reg))
-#define BHNDB_PCI_READ_4(_sc, _reg) \
- bus_read_4((_sc)->mem_res, (_sc)->mem_off + (_reg))
-
-#define BHNDB_PCI_WRITE_2(_sc, _reg, _val) \
- bus_write_2((_sc)->mem_res, (_sc)->mem_off + (_reg), (_val))
-#define BHNDB_PCI_WRITE_4(_sc, _reg, _val) \
- bus_write_4((_sc)->mem_res, (_sc)->mem_off + (_reg), (_val))
-
-
-/* BHNDB_PCI_REG_* convenience macros */
-#define BPCI_REG_EXTRACT(_rv, _a) BHND_PCI_REG_EXTRACT(_rv, BHND_ ## _a)
-#define BPCI_REG_INSERT(_rv, _a, _v) BHND_PCI_REG_INSERT(_rv, BHND_ ## _a, _v)
-
-#define BPCI_COMMON_REG_EXTRACT(_r, _a) \
- BHND_PCI_COMMON_REG_EXTRACT(sc->regfmt, _r, _a)
-
-#define BPCI_COMMON_REG_INSERT(_r, _a, _v) \
- BHND_PCI_COMMON_REG_INSERT(sc->regfmt, _r, _a, _v)
-
-#define BPCI_COMMON_REG(_name) \
- BHND_PCI_COMMON_REG(sc->regfmt, _name)
-
-#define BPCI_COMMON_REG_OFFSET(_base, _offset) \
- (BPCI_COMMON_REG(_base) + BPCI_COMMON_REG(_offset))
-
/**
* Default bhndb_pci implementation of device_probe().
*
@@ -250,14 +113,8 @@
if (pci_find_cap(device_get_parent(dev), PCIY_EXPRESS, ®) == 0)
sc->pci_devclass = BHND_DEVCLASS_PCIE;
- /* Determine the basic set of applicable quirks. This will be updated
- * in bhndb_pci_init_full_config() once the PCI device core has
- * been enumerated. */
- sc->quirks = bhndb_pci_discover_quirks(sc, NULL);
-
- /* Using the discovered quirks, apply any WARs required for basic
- * register access. */
- if ((error = bhndb_pci_wars_register_access(sc)))
+ /* Enable clocks (if supported by this hardware) */
+ if ((error = bhndb_enable_pci_clocks(sc)))
return (error);
/* Use siba(4)-compatible regwin handling until we know
@@ -280,438 +137,108 @@
return (0);
}
-/**
- * Initialize the full bridge configuration.
- *
- * This is called during the DEVICE_ATTACH() process by the bridged bhndb(4)
- * bus, prior to probe/attachment of child cores.
- *
- * At this point, we can introspect the enumerated cores, find our host
- * bridge device, and apply any bridge-level hardware workarounds required
- * for proper operation of the bridged device cores.
- */
static int
bhndb_pci_init_full_config(device_t dev, device_t child,
- const struct bhndb_hw_priority *prio_table)
+ const struct bhndb_hw_priority *hw_prio_table)
{
- struct bhnd_core_info core;
- const struct bhndb_pci_id *id;
- struct bhndb_pci_softc *sc;
- struct bhndb_region *pcir;
- bhnd_addr_t pcir_addr;
- bhnd_size_t pcir_size;
- int error;
+ struct bhndb_pci_softc *sc;
+ int error;
sc = device_get_softc(dev);
- /* Let bhndb perform full discovery and initialization of the
- * available register windows and bridge resources. */
- if ((error = bhndb_generic_init_full_config(dev, child, prio_table)))
- return (error);
-
- /*
- * Identify our PCI bridge core, its register family, and any
- * applicable hardware quirks.
- */
- KASSERT(sc->bhndb.hostb_dev,
- ("missing hostb device\n"));
-
- core = bhnd_get_core_info(sc->bhndb.hostb_dev);
- id = bhndb_pci_find_core_id(&core);
- if (id == NULL) {
- device_printf(dev, "%s %s hostb core is not recognized\n",
- bhnd_vendor_name(core.vendor), bhnd_core_name(&core));
- }
-
- sc->regfmt = id->regfmt;
-
- /* Now that we've identified the PCI bridge core, we can determine the
- * full set of device quirks */
- sc->quirks = bhndb_pci_discover_quirks(sc, id);
-
- /*
- * Determine and save a reference to the bhndb resource and offset
- * at which the bridge core's device registers are mapped.
- *
- * All known bhnd(4) hardware provides a fixed static mapping of
- * the PCI core's registers. If this changes in the future -- which
- * is unlikely -- this driver will need to be adjusted to use
- * dynamic register windows.
- */
-
- /* Find base address and size of the PCI core's register block. */
- error = bhnd_get_region_addr(sc->bhndb.hostb_dev, BHND_PORT_DEVICE, 0,
- 0, &pcir_addr, &pcir_size);
- if (error) {
- device_printf(dev,
- "failed to locate PCI core registers\n");
- return (error);
- }
-
- /* Find the bhndb_region that statically maps this block */
- pcir = bhndb_find_resource_region(sc->bhndb.bus_res, pcir_addr,
- pcir_size);
- if (pcir == NULL || pcir->static_regwin == NULL) {
- device_printf(dev,
- "missing static PCI core register window\n");
- return (ENXIO);
- }
-
- /* Save borrowed reference to the mapped PCI core registers */
- sc->mem_off = pcir->static_regwin->win_offset;
- sc->mem_res = bhndb_find_regwin_resource(sc->bhndb.bus_res,
- pcir->static_regwin);
- if (sc->mem_res == NULL || !(rman_get_flags(sc->mem_res) & RF_ACTIVE)) {
- device_printf(dev,
- "no active resource maps the PCI core register window\n");
- return (ENXIO);
- }
-
- /* Configure a direct bhnd_resource wrapper that we can pass to
- * bhnd_resource APIs */
- sc->bhnd_mem_res = (struct bhnd_resource) {
- .res = sc->mem_res,
- .direct = true
- };
-
- /*
- * Attach MMIO device (if this is a PCIe device), which is used for
- * access to the PCIe SerDes required by the quirk workarounds.
- */
- if (sc->pci_devclass == BHND_DEVCLASS_PCIE) {
- sc->mdio = BUS_ADD_CHILD(dev, 0,
- devclass_get_name(bhnd_mdio_pci_devclass), 0);
- if (sc->mdio == NULL)
- return (ENXIO);
-
- error = bus_set_resource(sc->mdio, SYS_RES_MEMORY, 0,
- rman_get_start(sc->mem_res) + sc->mem_off +
- BHND_PCIE_MDIO_CTL, sizeof(uint32_t)*2);
- if (error) {
- device_printf(dev, "failed to set MDIO resource\n");
- return (error);
- }
-
- if ((error = device_probe_and_attach(sc->mdio))) {
- device_printf(dev, "failed to attach MDIO device\n");
- return (error);
- }
- }
-
- /* Apply any early one-time quirk workarounds */
- if ((error = bhndb_pci_wars_early_once(sc)))
- return (error);
-
- /* Apply attach-time quirk workarounds, required before the bridged
- * bhnd(4) bus itself performs a full attach(). */
- if ((error = bhndb_pci_wars_hwup(sc)))
+ /* Let our parent perform standard initialization first */
+ if ((error = bhndb_generic_init_full_config(dev, child, hw_prio_table)))
return (error);
- return (0);
-}
-
-/**
- * Apply any hardware workarounds that must be executed prior to attempting
- * register access on the bridged chipset.
- *
- * This must be called very early in attach() or resume(), after the basic
- * set of applicable device quirks has been determined.
- */
-static int
-bhndb_pci_wars_register_access(struct bhndb_pci_softc *sc)
-{
- int error;
-
- if (BHNDB_PCI_QUIRK(sc, EXT_CLOCK_GATING)) {
- if ((error = bhndb_enable_pci_clocks(sc))) {
- device_printf(sc->dev, "failed to enable clocks\n");
- return (error);
- }
- }
-
- return (0);
-}
-
-/**
- * Apply any hardware work-arounds that must be executed exactly once, early in
- * the attach process.
- *
- * This must be called after core enumeration and discovery of all applicable
- * quirks, but prior to probe/attach of any cores, parsing of
- * SPROM, etc.
- */
-static int
-bhndb_pci_wars_early_once(struct bhndb_pci_softc *sc)
-{
- /* Determine correct polarity by observing the attach-time PCIe PHY
- * link status. This is used later to reset/force the SerDes
- * polarity */
- if (BHNDB_PCIE_QUIRK(sc, SDR9_POLARITY)) {
- uint32_t st;
- bool inv;
-
-
- st = bhndb_pcie_read_proto_reg(sc, BHND_PCIE_PLP_STATUSREG);
- inv = ((st & BHND_PCIE_PLP_POLARITY_INV) != 0);
- sc->sdr9_quirk_polarity.inv = inv;
- }
-
- return (0);
-}
-
-/**
- * Apply any hardware workarounds that are required upon attach or resume
- * of the bridge device.
- */
-static int
-bhndb_pci_wars_hwup(struct bhndb_pci_softc *sc)
-{
- /* Note that the order here matters; these work-arounds
- * should not be re-ordered without careful review of their
- * interdependencies */
-
- /* Fix up any PoR defaults on SROMless devices */
+ /* Fix-up power on defaults for SROM-less devices. */
bhndb_init_sromless_pci_config(sc);
- /* Enable PCI prefetch/burst/readmulti flags */
- if (BHNDB_PCI_QUIRK(sc, SBTOPCI2_PREF_BURST) ||
- BHNDB_PCI_QUIRK(sc, SBTOPCI2_READMULTI))
- {
- uint32_t sbp2;
- sbp2 = BHNDB_PCI_READ_4(sc, BHND_PCI_SBTOPCI2);
-
- if (BHNDB_PCI_QUIRK(sc, SBTOPCI2_PREF_BURST))
- sbp2 |= (BHND_PCI_SBTOPCI_PREF|BHND_PCI_SBTOPCI_BURST);
-
- if (BHNDB_PCI_QUIRK(sc, SBTOPCI2_READMULTI))
- sbp2 |= BHND_PCI_SBTOPCI_RC_READMULTI;
-
- BHNDB_PCI_WRITE_4(sc, BHND_PCI_SBTOPCI2, sbp2);
- }
-
- /* Disable PCI CLKRUN# */
- if (BHNDB_PCI_QUIRK(sc, CLKRUN_DSBL)) {
- uint32_t ctl;
-
- ctl = BHNDB_PCI_READ_4(sc, BHND_PCI_CLKRUN_CTL);
- ctl |= BHND_PCI_CLKRUN_DSBL;
- BHNDB_PCI_WRITE_4(sc, BHND_PCI_CLKRUN_CTL, ctl);
- }
-
- /* Enable TLP unmatched address handling work-around */
- if (BHNDB_PCIE_QUIRK(sc, UR_STATUS_FIX)) {
- uint32_t wrs;
- wrs = bhndb_pcie_read_proto_reg(sc, BHND_PCIE_TLP_WORKAROUNDSREG);
- wrs |= BHND_PCIE_TLP_WORKAROUND_URBIT;
- bhndb_pcie_write_proto_reg(sc, BHND_PCIE_TLP_WORKAROUNDSREG, wrs);
- }
-
- /* Adjust SerDes CDR tuning to ensure that CDR is stable before sending
- * data during L0s to L0 exit transitions. */
- if (BHNDB_PCIE_QUIRK(sc, SDR9_L0s_HANG)) {
- uint16_t sdv;
-
- /* Set RX track/acquire timers to 2.064us/40.96us */
- sdv = BPCI_REG_INSERT(0, PCIE_SDR9_RX_TIMER1_LKTRK, (2064/16));
- sdv = BPCI_REG_INSERT(sdv, PCIE_SDR9_RX_TIMER1_LKACQ,
- (40960/1024));
- MDIO_WRITEREG(sc->mdio, BHND_PCIE_PHY_SDR9_TXRX,
- BHND_PCIE_SDR9_RX_TIMER1, sdv);
-
- /* Apply CDR frequency workaround */
- sdv = BHND_PCIE_SDR9_RX_CDR_FREQ_OVR_EN;
- sdv = BPCI_REG_INSERT(sdv, PCIE_SDR9_RX_CDR_FREQ_OVR, 0x0);
- MDIO_WRITEREG(sc->mdio, BHND_PCIE_PHY_SDR9_TXRX,
- BHND_PCIE_SDR9_RX_CDR, sdv);
-
- /* Apply CDR BW tunings */
- sdv = 0;
- sdv = BPCI_REG_INSERT(sdv, PCIE_SDR9_RX_CDRBW_INTGTRK, 0x2);
- sdv = BPCI_REG_INSERT(sdv, PCIE_SDR9_RX_CDRBW_INTGACQ, 0x4);
- sdv = BPCI_REG_INSERT(sdv, PCIE_SDR9_RX_CDRBW_PROPTRK, 0x6);
- sdv = BPCI_REG_INSERT(sdv, PCIE_SDR9_RX_CDRBW_PROPACQ, 0x6);
- MDIO_WRITEREG(sc->mdio, BHND_PCIE_PHY_SDR9_TXRX,
- BHND_PCIE_SDR9_RX_CDRBW, sdv);
- }
-
- /* Force correct SerDes polarity */
- if (BHNDB_PCIE_QUIRK(sc, SDR9_POLARITY)) {
- uint16_t rxctl;
-
- rxctl = MDIO_READREG(sc->mdio, BHND_PCIE_PHY_SDR9_TXRX,
- BHND_PCIE_SDR9_RX_CTRL);
-
- rxctl |= BHND_PCIE_SDR9_RX_CTRL_FORCE;
- if (sc->sdr9_quirk_polarity.inv)
- rxctl |= BHND_PCIE_SDR9_RX_CTRL_POLARITY_INV;
- else
- rxctl &= ~BHND_PCIE_SDR9_RX_CTRL_POLARITY_INV;
-
- MDIO_WRITEREG(sc->mdio, BHND_PCIE_PHY_SDR9_TXRX,
- BHND_PCIE_SDR9_RX_CTRL, rxctl);
- }
-
- /* Disable startup retry on PLL frequency detection failure */
- if (BHNDB_PCIE_QUIRK(sc, SDR9_NO_FREQRETRY)) {
- uint16_t pctl;
-
- pctl = MDIO_READREG(sc->mdio, BHND_PCIE_PHY_SDR9_PLL,
- BHND_PCIE_SDR9_PLL_CTRL);
-
- pctl &= ~BHND_PCIE_SDR9_PLL_CTRL_FREQDET_EN;
- MDIO_WRITEREG(sc->mdio, BHND_PCIE_PHY_SDR9_PLL,
- BHND_PCIE_SDR9_PLL_CTRL, pctl);
- }
-
- /* Explicitly enable PCI-PM */
- if (BHNDB_PCIE_QUIRK(sc, PCIPM_REQEN)) {
- uint32_t lcreg;
- lcreg = bhndb_pcie_read_proto_reg(sc, BHND_PCIE_DLLP_LCREG);
- lcreg |= BHND_PCIE_DLLP_LCREG_PCIPM_EN;
- bhndb_pcie_write_proto_reg(sc, BHND_PCIE_DLLP_LCREG, lcreg);
- }
-
- /* Adjust L1 timer to fix slow L1->L0 transitions */
- if (BHNDB_PCIE_QUIRK(sc, L1_IDLE_THRESH)) {
- uint32_t pmt;
- pmt = bhndb_pcie_read_proto_reg(sc, BHND_PCIE_DLLP_PMTHRESHREG);
- pmt = BPCI_REG_INSERT(pmt, PCIE_L1THRESHOLDTIME,
- BHND_PCIE_L1THRESHOLD_WARVAL);
- bhndb_pcie_write_proto_reg(sc, BHND_PCIE_DLLP_PMTHRESHREG, pmt);
- }
-
- /* Extend L1 timer for better performance.
- * TODO: We could enable/disable this on demand for better power
- * savings if we tie this to HT clock request handling */
- if (BHNDB_PCIE_QUIRK(sc, L1_TIMER_PERF)) {
- uint32_t pmt;
- pmt = bhndb_pcie_read_proto_reg(sc, BHND_PCIE_DLLP_PMTHRESHREG);
- pmt |= BHND_PCIE_ASPMTIMER_EXTEND;
- bhndb_pcie_write_proto_reg(sc, BHND_PCIE_DLLP_PMTHRESHREG, pmt);
- }
-
- /* Enable L23READY_EXIT_NOPRST if not already set in SPROM. */
- if (BHNDB_PCIE_QUIRK(sc, SPROM_L23_PCI_RESET)) {
- bus_size_t reg;
- uint16_t cfg;
-
- /* Fetch the misc cfg flags from SPROM */
- reg = BHND_PCIE_SPROM_SHADOW + BHND_PCIE_SRSH_PCIE_MISC_CONFIG;
- cfg = BHNDB_PCI_READ_2(sc, reg);
-
- /* Write EXIT_NOPRST flag if not already set in SPROM */
- if (!(cfg & BHND_PCIE_SRSH_L23READY_EXIT_NOPRST)) {
- cfg |= BHND_PCIE_SRSH_L23READY_EXIT_NOPRST;
- BHNDB_PCI_WRITE_2(sc, reg, cfg);
- }
- }
-
- return (0);
-}
-
-/**
- * Apply any hardware workarounds that are required upon resume of the
- * bridge device.
- *
- * This must be called before any bridged bhnd(4) cores have been resumed.
- */
-static int
-bhndb_pci_wars_hwresume(struct bhndb_pci_softc *sc)
-{
- int error;
-
- /* Nothing is possible without register access */
- if ((error = bhndb_pci_wars_register_access(sc)))
- return (error);
-
- /* Apply the general hwup workarounds */
- return (bhndb_pci_wars_hwup(sc));
-}
-
-/**
- * Apply any hardware workarounds that are required upon detach or suspend
- * of the bridge device.
- */
-static int
-bhndb_pci_wars_hwdown(struct bhndb_pci_softc *sc)
-{
- int error;
-
- /* Reduce L1 timer for better power savings.
- * TODO: We could enable/disable this on demand for better power
- * savings if we tie this to HT clock request handling */
- if (BHNDB_PCIE_QUIRK(sc, L1_TIMER_PERF)) {
- uint32_t pmt;
- pmt = bhndb_pcie_read_proto_reg(sc, BHND_PCIE_DLLP_PMTHRESHREG);
- pmt &= ~BHND_PCIE_ASPMTIMER_EXTEND;
- bhndb_pcie_write_proto_reg(sc, BHND_PCIE_DLLP_PMTHRESHREG, pmt);
- }
-
- /* Disable clocks */
- if (BHNDB_PCI_QUIRK(sc, EXT_CLOCK_GATING)) {
- if ((error = bhndb_disable_pci_clocks(sc))) {
- device_printf(sc->dev, "failed to disable clocks\n");
- return (error);
- }
- }
-
return (0);
}
/*
* On devices without a SROM, the PCI(e) cores will be initialized with
- * their Power-on-Reset defaults; this can leave the the BAR0 PCI windows
- * potentially mapped to the wrong core index.
+ * their Power-on-Reset defaults; this can leave two of the BAR0 PCI windows
+ * mapped to the wrong core.
*
- * This function updates the PCI core's BAR0 PCI configuration to point at the
+ * This function updates the SROM shadow to point the BAR0 windows at the
* current PCI core.
*
- * Applies to all PCI/PCIe revisions. Must be applied before bus devices
- * are probed/attached or the SPROM is parsed.
+ * Applies to all PCI/PCIe revisions.
*/
static void
bhndb_init_sromless_pci_config(struct bhndb_pci_softc *sc)
{
- bus_size_t sprom_addr;
- u_int sprom_core_idx;
- u_int pci_core_idx;
- uint16_t val;
+ struct bhndb_resources *bres;
+ const struct bhndb_hwcfg *cfg;
+ const struct bhndb_regwin *win;
+ struct resource *core_regs;
+ bus_size_t srom_offset;
+ u_int pci_cidx, sprom_cidx;
+ uint16_t val;
+
+ bres = sc->bhndb.bus_res;
+ cfg = bres->cfg;
+
+ if (bhnd_get_vendor(sc->bhndb.hostb_dev) != BHND_MFGID_BCM)
+ return;
+
+ switch (bhnd_get_device(sc->bhndb.hostb_dev)) {
+ case BHND_COREID_PCI:
+ srom_offset = BHND_PCI_SRSH_PI_OFFSET;
+ break;
+ case BHND_COREID_PCIE:
+ srom_offset = BHND_PCIE_SRSH_PI_OFFSET;
+ break;
+ default:
+ device_printf(sc->dev, "unsupported PCI host bridge device\n");
+ return;
+ }
+
+ /* Locate the static register window mapping the PCI core */
+ win = bhndb_regwin_find_core(cfg->register_windows, sc->pci_devclass,
+ 0, BHND_PORT_DEVICE, 0, 0);
+ if (win == NULL) {
+ device_printf(sc->dev, "missing PCI core register window\n");
+ return;
+ }
+
+ /* Fetch the resource containing the register window */
+ core_regs = bhndb_find_regwin_resource(bres, win);
+ if (core_regs == NULL) {
+ device_printf(sc->dev, "missing PCI core register resource\n");
+ return;
+ }
/* Fetch the SPROM's configured core index */
- sprom_addr = BPCI_COMMON_REG_OFFSET(SPROM_SHADOW, SRSH_PI_OFFSET);
- val = BHNDB_PCI_READ_2(sc, sprom_addr);
+ val = bus_read_2(core_regs, win->win_offset + srom_offset);
+ sprom_cidx = (val & BHND_PCI_SRSH_PI_MASK) >> BHND_PCI_SRSH_PI_SHIFT;
/* If it doesn't match host bridge's core index, update the index
* value */
- sprom_core_idx = BPCI_COMMON_REG_EXTRACT(val, SRSH_PI);
- pci_core_idx = bhnd_get_core_index(sc->bhndb.hostb_dev);
-
- if (sprom_core_idx != pci_core_idx) {
- val = BPCI_COMMON_REG_INSERT(val, SRSH_PI, pci_core_idx);
- BHNDB_PCI_WRITE_2(sc, sprom_addr, val);
+ pci_cidx = bhnd_get_core_index(sc->bhndb.hostb_dev);
+ if (sprom_cidx != pci_cidx) {
+ val &= ~BHND_PCI_SRSH_PI_MASK;
+ val |= (pci_cidx << BHND_PCI_SRSH_PI_SHIFT);
+ bus_write_2(core_regs,
+ win->win_offset + srom_offset, val);
}
}
static int
-bhndb_pci_detach(device_t dev)
+bhndb_pci_resume(device_t dev)
{
struct bhndb_pci_softc *sc;
int error;
sc = device_get_softc(dev);
-
- if ((error = bhndb_generic_detach(dev)))
- return (error);
-
- /* Apply any hardware workarounds. This may disable the clock, and
- * thus must be called *after* any children have been detached. */
- if ((error = bhndb_pci_wars_hwdown(sc)))
+
+ /* Enable clocks (if supported by this hardware) */
+ if ((error = bhndb_enable_pci_clocks(sc)))
return (error);
- /* Disable PCI bus mastering */
- pci_disable_busmaster(device_get_parent(dev));
-
- return (0);
+ /* Perform resume */
+ return (bhndb_generic_resume(dev));
}
static int
@@ -721,35 +248,34 @@
int error;
sc = device_get_softc(dev);
-
- if ((error = bhndb_generic_suspend(dev)))
- return (error);
-
- /* Apply any hardware workarounds. This may disable the clock, and
- * thus must be called *after* any children have been suspended. */
- if ((error = bhndb_pci_wars_hwdown(sc)))
+
+ /* Disable clocks (if supported by this hardware) */
+ if ((error = bhndb_disable_pci_clocks(sc)))
return (error);
- return (0);
+ /* Perform suspend */
+ return (bhndb_generic_suspend(dev));
}
static int
-bhndb_pci_resume(device_t dev)
+bhndb_pci_detach(device_t dev)
{
struct bhndb_pci_softc *sc;
int error;
sc = device_get_softc(dev);
- /* Apply any resume workarounds; these may be required for bridged
- * device access, and thus must be called *before* any children are
- * resumed. */
- if ((error = bhndb_pci_wars_hwresume(sc)))
+ /* Disable clocks (if supported by this hardware) */
+ if ((error = bhndb_disable_pci_clocks(sc)))
return (error);
- if ((error = bhndb_generic_resume(dev)))
+ /* Perform detach */
+ if ((error = bhndb_generic_detach(dev)))
return (error);
+ /* Disable PCI bus mastering */
+ pci_disable_busmaster(device_get_parent(dev));
+
return (0);
}
@@ -826,54 +352,14 @@
return (0);
}
-
/**
- * Read a 32-bit PCIe TLP/DLLP/PLP protocol register.
+ * Enable externally managed clocks, if required.
*
- * @param sc The bhndb_pci driver state.
- * @param addr The protocol register offset.
- */
-static uint32_t
-bhndb_pcie_read_proto_reg(struct bhndb_pci_softc *sc, uint32_t addr)
-{
- uint32_t val;
-
- KASSERT(bhnd_get_class(sc->bhndb.hostb_dev) == BHND_DEVCLASS_PCIE,
- ("not a pcie device!"));
-
- BHNDB_LOCK(&sc->bhndb);
- BHNDB_PCI_WRITE_4(sc, BHND_PCIE_IND_ADDR, addr);
- val = BHNDB_PCI_READ_4(sc, BHND_PCIE_IND_DATA);
- BHNDB_UNLOCK(&sc->bhndb);
-
- return (val);
-}
-
-/**
- * Write a 32-bit PCIe TLP/DLLP/PLP protocol register value.
- *
- * @param sc The bhndb_pci driver state.
- * @param addr The protocol register offset.
- * @param val The value to write to @p addr.
- */
-static void
-bhndb_pcie_write_proto_reg(struct bhndb_pci_softc *sc, uint32_t addr,
- uint32_t val)
-{
- KASSERT(bhnd_get_class(sc->bhndb.hostb_dev) == BHND_DEVCLASS_PCIE,
- ("not a pcie device!"));
-
- BHNDB_LOCK(&sc->bhndb);
- BHNDB_PCI_WRITE_4(sc, BHND_PCIE_IND_ADDR, addr);
- BHNDB_PCI_WRITE_4(sc, BHND_PCIE_IND_DATA, val);
- BHNDB_UNLOCK(&sc->bhndb);
-}
-
-
-/**
- * Enable externally managed clocks.
- *
- * Quirk Required: EXT_CLOCK_GATING
+ * Some PCI chipsets (BCM4306, possibly others) chips do not support
+ * the idle low-power clock. Clocking must be bootstrapped at
+ * 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.
*/
@@ -885,7 +371,9 @@
uint32_t gpio_flags;
uint16_t pci_status;
- BHNDB_PCI_ASSERT_QUIRK(sc, EXT_CLOCK_GATING);
+ /* Only supported and required on PCI devices */
+ if (sc->pci_devclass != BHND_DEVCLASS_PCI)
+ return (0);
pci_parent = device_get_parent(sc->dev);
@@ -921,9 +409,7 @@
}
/**
- * Disable externally managed clocks.
- *
- * Quirk Required: EXT_CLOCK_GATING
+ * Disable externally managed clocks, if required.
*
* @param sc Bridge driver state.
*/
@@ -933,7 +419,9 @@
device_t parent_dev;
uint32_t gpio_out, gpio_en;
- BHNDB_PCI_ASSERT_QUIRK(sc, EXT_CLOCK_GATING);
+ /* Only supported and required on PCI devices */
+ if (sc->pci_devclass != BHND_DEVCLASS_PCI)
+ return (0);
parent_dev = device_get_parent(sc->dev);
@@ -957,111 +445,13 @@
return (0);
}
-
-/**
- * Find the identification table entry for a core descriptor.
- *
- * @param sc bhndb PCI driver state.
- */
-static const struct bhndb_pci_id *
-bhndb_pci_find_core_id(struct bhnd_core_info *core)
-{
- const struct bhndb_pci_id *id;
-
- for (id = bhndb_pci_ids; id->device != BHND_COREID_INVALID; id++) {
- if (core->vendor == BHND_MFGID_BCM &&
- core->device == id->device)
- return (id);
- }
-
- return (NULL);
-}
-
-/**
- * Return all quirks known to be applicable to the host bridge.
- *
- * If the PCI bridge core has not yet been identified, no core-specific
- * quirk flags will be returned. This function may be called again to
- * rediscover applicable quirks after the host bridge core has been
- * identified.
- *
- * @param sc bhndb PCI driver state.
- * @param id The host bridge core's identification table entry, or NULL
- * if the host bridge core has not yet been identified.
- *
- * @return Returns the set of quirks applicable to the current hardware.
- */
-static uint32_t
-bhndb_pci_discover_quirks(struct bhndb_pci_softc *sc,
- const struct bhndb_pci_id *id)
-{
- struct bhnd_device_quirk *qt;
- uint32_t quirks;
- uint8_t hwrev;
-
- quirks = BHNDB_PCI_QUIRK_NONE;
-
- /* Determine any device class-specific quirks */
- switch (sc->pci_devclass) {
- case BHND_DEVCLASS_PCI:
- /* All PCI devices require external clock gating */
- sc->quirks |= BHNDB_PCI_QUIRK_EXT_CLOCK_GATING;
- break;
- default:
- break;
- }
-
- // TODO: Additional quirk matching
-
- /* Determine any PCI core hwrev-specific device quirks */
- if (id != NULL) {
- hwrev = bhnd_get_hwrev(sc->bhndb.hostb_dev);
- for (qt = id->quirks; qt->quirks != 0; qt++) {
- if (bhnd_hwrev_matches(hwrev, &qt->hwrev))
- quirks |= qt->quirks;
- };
- }
-
-
- return (quirks);
-}
-
-/*
- * Support for attaching the PCIe-Gen1 MDIO driver to a parent bhndb PCIe
- * bridge device.
- */
-static int
-bhndb_mdio_pcie_probe(device_t dev)
-{
- device_quiet(dev);
- return (BUS_PROBE_NOWILDCARD);
-}
-
-static int
-bhndb_mdio_pcie_attach(device_t dev)
-{
- struct bhndb_pci_softc *psc;
- psc = device_get_softc(device_get_parent(dev));
- return (bhnd_mdio_pcie_attach(dev,
- &psc->bhnd_mem_res, -1,
- psc->mem_off + BHND_PCIE_MDIO_CTL,
- (psc->quirks & BHNDB_PCIE_QUIRK_SD_C22_EXTADDR) != 0));
-}
-
-static device_method_t bhnd_mdio_pcie_methods[] = {
- /* Device interface */
- DEVMETHOD(device_probe, bhndb_mdio_pcie_probe),
- DEVMETHOD(device_attach, bhndb_mdio_pcie_attach),
- DEVMETHOD_END
-};
-
static device_method_t bhndb_pci_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, bhndb_pci_probe),
DEVMETHOD(device_attach, bhndb_pci_attach),
- DEVMETHOD(device_detach, bhndb_pci_detach),
- DEVMETHOD(device_suspend, bhndb_pci_suspend),
DEVMETHOD(device_resume, bhndb_pci_resume),
+ DEVMETHOD(device_suspend, bhndb_pci_suspend),
+ DEVMETHOD(device_detach, bhndb_pci_detach),
/* BHNDB interface */
DEVMETHOD(bhndb_init_full_config, bhndb_pci_init_full_config),
@@ -1073,12 +463,6 @@
DEFINE_CLASS_1(bhndb, bhndb_pci_driver, bhndb_pci_methods,
sizeof(struct bhndb_pci_softc), bhndb_driver);
-DEFINE_CLASS_1(bhnd_mdio_pci, bhndb_mdio_pcie_driver, bhnd_mdio_pcie_methods,
- sizeof(struct bhnd_mdio_pcie_softc), bhnd_mdio_pcie_driver);
-
-DRIVER_MODULE(bhnd_mdio_pcie, bhndb, bhndb_mdio_pcie_driver,
- bhnd_mdio_pci_devclass, NULL, NULL);
-
MODULE_VERSION(bhndb_pci, 1);
MODULE_DEPEND(bhndb_pci, bhnd_pci, 1, 1, 1);
MODULE_DEPEND(bhndb_pci, pci, 1, 1, 1);
Index: sys/dev/bhnd/bhndb/bhndb_pcivar.h
===================================================================
--- sys/dev/bhnd/bhndb/bhndb_pcivar.h
+++ sys/dev/bhnd/bhndb/bhndb_pcivar.h
@@ -32,10 +32,6 @@
#ifndef _BHND_BHNDB_PCIVAR_H_
#define _BHND_BHNDB_PCIVAR_H_
-#include <sys/stdint.h>
-
-#include <dev/bhnd/cores/pci/bhnd_pcivar.h>
-
#include "bhndbvar.h"
/*
@@ -52,191 +48,11 @@
typedef int (*bhndb_pci_set_regwin_t)(struct bhndb_pci_softc *sc,
const struct bhndb_regwin *rw, bhnd_addr_t addr);
-
-/**
- * PCI bridge core identification table.
- */
-struct bhndb_pci_id {
- uint16_t device; /**< bhnd device ID */
- bhnd_pci_regfmt_t regfmt; /**< register format */
- struct bhnd_device_quirk *quirks; /**< quirks table */
-};
-
struct bhndb_pci_softc {
struct bhndb_softc bhndb; /**< parent softc */
device_t dev; /**< bridge device */
bhnd_devclass_t pci_devclass; /**< PCI core's devclass */
bhndb_pci_set_regwin_t set_regwin; /**< regwin handler */
-
- /*
- * Initialized in BHNDB_INIT_FULL_CONFIG()
- */
-
- device_t mdio; /**< PCIe MDIO device. NULL if not PCIe. */
- bhnd_pci_regfmt_t regfmt; /**< device register format */
-
- struct resource *mem_res; /**< pci core's registers (borrowed reference) */
- bus_size_t mem_off; /**< offset to the PCI core's registers within `mem_res` . */
-
- struct bhnd_resource bhnd_mem_res; /**< bhnd resource representation of mem_res.
- this is a simple 'direct' resource mapping */
-
- uint32_t quirks; /**< BHNDB_PCI(E)_QUIRK flags */
-
- /**
- * Driver state specific to BHNDB_PCIE_QUIRK_SDR9_POLARITY.
- */
- struct {
- /**
- * PCIe SerDes RX polarity.
- *
- * Initialized to the PCIe link's RX polarity
- * at attach time. This is used to restore the
- * correct polarity on resume */
- bool inv;
- } sdr9_quirk_polarity;
-};
-
-/*
- * PCI/PCIe-Gen1 endpoint-mode device quirks
- */
-enum {
- /** No quirks */
- BHNDB_PCI_QUIRK_NONE = 0,
-
- /**
- * BCM4306 chips (and possibly others) do not support the idle
- * low-power clock. Clocking must be bootstrapped at attach/resume by
- * directly adjusting GPIO registers exposed in the PCI config space,
- * and correspondingly, explicitly shutdown at detach/suspend.
- */
- BHNDB_PCI_QUIRK_EXT_CLOCK_GATING = (1<<1),
-
- /**
- * SBTOPCI_PREF and SBTOPCI_BURST must be set on the
- * SSB_PCICORE_SBTOPCI2 register.
- */
- BHNDB_PCI_QUIRK_SBTOPCI2_PREF_BURST = (1<<2),
-
- /**
- * SBTOPCI_RC_READMULTI must be set on the SSB_PCICORE_SBTOPCI2
- * register.
- */
- BHNDB_PCI_QUIRK_SBTOPCI2_READMULTI = (1<<3),
-
- /**
- * Interrupt masking is handled via the interconnect configuration
- * registers (SBINTVEC on siba), rather than the PCI_INT_MASK
- * config register.
- */
- BHNDB_PCI_QUIRK_SBINTVEC = (1<<4),
-
- /**
- * PCI CLKRUN# should be disabled on attach (via CLKRUN_DSBL).
- *
- * The purpose of this work-around is unclear; there is some
- * documentation regarding earlier Broadcom drivers supporting
- * a "force CLKRUN#" *enable* registry key for use on mobile
- * hardware.
- */
- BHNDB_PCI_QUIRK_CLKRUN_DSBL = (1<<5),
-
- /**
- * TLP workaround for unmatched address handling is required.
- *
- * This TLP workaround will enable setting of the PCIe UR status bit
- * on memory access to an unmatched address.
- */
- BHNDB_PCIE_QUIRK_UR_STATUS_FIX = (1<<6),
-
- /**
- * PCI-PM power management must be explicitly enabled via
- * the data link control register.
- */
- BHNDB_PCIE_QUIRK_PCIPM_REQEN = (1<<7),
-
- /**
- * Fix L0s to L0 exit transition on SerDes <= rev9 devices.
- *
- * On these devices, PCIe/SerDes symbol lock can be lost if the
- * reference clock has not fully stabilized during the L0s to L0
- * exit transition, triggering an internal reset of the chip.
- *
- * The SerDes RX CDR phase lock timers and proportional/integral
- * filters must be tweaked to ensure the CDR has fully stabilized
- * before asserting receive sequencer completion.
- */
- BHNDB_PCIE_QUIRK_SDR9_L0s_HANG = (1<<8),
-
- /**
- * The idle time for entering L1 low-power state must be
- * explicitly set (to 114ns) to fix slow L1->L0 transition issues.
- */
- BHNDB_PCIE_QUIRK_L1_IDLE_THRESH = (1<<9),
-
- /**
- * The ASPM L1 entry timer should be extended for better performance,
- * and restored for better power savings.
- */
- BHNDB_PCIE_QUIRK_L1_TIMER_PERF = (1<<10),
-
- /**
- * ASPM and ECPM settings must be overridden manually.
- *
- * The override behavior is controlled by the BHND_BFL2_PCIEWAR_OVR
- * flag. If this flag is set, ASPM/CLKREQ should be overridden as
- * enabled; otherwise, they should be overridden as disabled.
- *
- * Attach/Resume:
- * - Set SRSH_ASPM_ENB flag in the SPROM ASPM register.
- * - Set ASPM L0S/L1 in the PCIER_LINK_CTL register.
- * - Set SRSH_CLKREQ_ENB flag in the SPROM CLKREQ_REV5 register.
- * - Clear ECPM in the PCIER_LINK_CTL register.
- *
- * Detach/Suspend:
- * -
- * - When the device enters D3 state, or system enters S3/S4 state,
- * clear ASPM L1 in the PCIER_LINK_CTL register.
- */
- BHNDB_PCIE_QUIRK_ASPM_OVR = (1<<11),
-
- /**
- * Fix SerDes polarity on SerDes <= rev9 devices.
- *
- * The SerDes polarity must be saved at device attachment, and
- * restored on suspend/resume.
- */
- BHNDB_PCIE_QUIRK_SDR9_POLARITY = (1<<12),
-
- /**
- * The SerDes PLL override flag (CHIPCTRL_4321_PLL_DOWN) must be set on
- * the ChipCommon core on resume.
- */
- BHNDB_PCIE_QUIRK_SERDES_NOPLLDOWN = (1<<13),
-
- /**
- * On attach and resume, consult the SPROM to determine whether
- * the L2/L3-Ready w/o PCI RESET work-around must be applied.
- *
- * If L23READY_EXIT_NOPRST is not already set in the SPROM, set it
- */
- BHNDB_PCIE_QUIRK_SPROM_L23_PCI_RESET = (1<<14),
-
- /**
- * The PCIe SerDes supports non-standard extended MDIO register access.
- *
- * The PCIe SerDes supports access to extended MDIO registers via
- * a non-standard Clause 22 address extension mechanism.
- */
- BHNDB_PCIE_QUIRK_SD_C22_EXTADDR = (1<<15),
-
- /**
- * The PCIe SerDes PLL must be configured to not retry the startup
- * sequence upon frequency detection failure on SerDes <= rev9 devices
- *
- * The issue this workaround resolves has not be determined.
- */
- BHNDB_PCIE_QUIRK_SDR9_NO_FREQRETRY = (1<<16),
};
-#endif /* _BHND_BHNDB_PCIVAR_H_ */
\ No newline at end of file
+#endif /* _BHND_BHNDB_PCIVAR_H_ */
Index: sys/dev/bhnd/cores/pci/bhnd_pci.c
===================================================================
--- sys/dev/bhnd/cores/pci/bhnd_pci.c
+++ sys/dev/bhnd/cores/pci/bhnd_pci.c
@@ -31,22 +31,518 @@
__FBSDID("$FreeBSD$");
/*
- * Broadcom Common PCI Support.
+ * Broadcom Common PCI/PCIe Support.
*
- * This module provides common implementation shared across the PCI/PCIe
- * endpoint and root complex drivers.
+ * This base driver implementation is shared by the bhnd_pcib (root complex)
+ * and bhnd_pci_hostb (host bridge) drivers.
*/
#include <sys/param.h>
+#include <sys/malloc.h>
#include <sys/kernel.h>
+#include <sys/bus.h>
#include <sys/module.h>
+#include <sys/systm.h>
+#include <machine/bus.h>
+#include <sys/rman.h>
+#include <machine/resource.h>
+
+#include <dev/bhnd/bhnd.h>
+#include <dev/mdio/mdio.h>
+
+#include "bhnd_pcireg.h"
#include "bhnd_pcivar.h"
+static int bhnd_pcie_mdio_wait_idle(struct bhnd_pci_softc *sc);
+static int bhnd_pcie_mdio_ioctl(struct bhnd_pci_softc *sc, uint32_t cmd);
+static int bhnd_pcie_mdio_enable(struct bhnd_pci_softc *sc);
+static void bhnd_pcie_mdio_disable(struct bhnd_pci_softc *sc);
+static int bhnd_pcie_mdio_cmd_write(struct bhnd_pci_softc *sc,
+ uint32_t cmd);
+static int bhnd_pcie_mdio_cmd_read(struct bhnd_pci_softc *sc, uint32_t cmd,
+ uint16_t *data_read);
+
+static struct bhnd_device_quirk bhnd_pci_quirks[];
+static struct bhnd_device_quirk bhnd_pcie_quirks[];
+
+#define BHND_PCI_QUIRKS bhnd_pci_quirks
+#define BHND_PCIE_QUIRKS bhnd_pcie_quirks
+#define BHND_PCI_DEV(_core, _desc, ...) \
+ { BHND_DEVICE(_core, _desc, BHND_ ## _core ## _QUIRKS, \
+ ## __VA_ARGS__), BHND_PCI_REGFMT_ ## _core }
+
+static const struct bhnd_pci_device {
+ struct bhnd_device device;
+ bhnd_pci_regfmt_t regfmt; /**< register format */
+} bhnd_pci_devs[] = {
+ BHND_PCI_DEV(PCI, "Host-PCI bridge", BHND_DF_HOSTB),
+ BHND_PCI_DEV(PCI, "PCI-BHND bridge"),
+ BHND_PCI_DEV(PCIE, "PCIe-G1 Host-PCI bridge", BHND_DF_HOSTB),
+ BHND_PCI_DEV(PCIE, "PCIe-G1 PCI-BHND bridge"),
+
+ { BHND_DEVICE_END, 0 }
+};
+
+/* Device quirks tables */
+static struct bhnd_device_quirk bhnd_pci_quirks[] = { BHND_DEVICE_QUIRK_END };
+static struct bhnd_device_quirk bhnd_pcie_quirks[] = {
+ { BHND_HWREV_GTE (10), BHND_PCI_QUIRK_SD_C22_EXTADDR },
+ BHND_DEVICE_QUIRK_END
+};
+
+#define BHND_PCIE_MDIO_CTL_DELAY 10 /**< usec delay required between
+ * MDIO_CTL/MDIO_DATA accesses. */
+#define BHND_PCIE_MDIO_RETRY_DELAY 2000 /**< usec delay before retrying
+ * BHND_PCIE_MDIOCTL_DONE. */
+#define BHND_PCIE_MDIO_RETRY_COUNT 200 /**< number of times to loop waiting
+ * for BHND_PCIE_MDIOCTL_DONE. */
+
+#define BHND_PCI_READ_4(_sc, _reg) \
+ bhnd_bus_read_4((_sc)->mem_res, (_reg))
+#define BHND_PCI_WRITE_4(_sc, _reg, _val) \
+ bhnd_bus_write_4((_sc)->mem_res, (_reg), (_val))
+
+#define BHND_PCIE_ASSERT(sc) \
+ KASSERT(bhnd_get_class(sc->dev) == BHND_DEVCLASS_PCIE, \
+ ("not a pcie device!"));
+
+int
+bhnd_pci_generic_probe(device_t dev)
+{
+ const struct bhnd_device *id;
+
+ id = bhnd_device_lookup(dev, &bhnd_pci_devs[0].device,
+ sizeof(bhnd_pci_devs[0]));
+ if (id == NULL)
+ return (ENXIO);
+
+ bhnd_set_custom_core_desc(dev, id->desc);
+ return (BUS_PROBE_DEFAULT);
+}
+
+int
+bhnd_pci_generic_attach(device_t dev)
+{
+ struct bhnd_pci_softc *sc;
+ int error;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+ sc->quirks = bhnd_device_quirks(dev, &bhnd_pci_devs[0].device,
+ sizeof(bhnd_pci_devs[0]));
+
+ /* Allocate bus resources */
+ sc->mem_res = bhnd_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->mem_rid,
+ RF_ACTIVE);
+ if (sc->mem_res == NULL)
+ return (ENXIO);
+
+ BHND_PCI_LOCK_INIT(sc);
+
+ /* Probe and attach children */
+ if ((error = bus_generic_attach(dev)))
+ goto cleanup;
+
+ return (0);
+
+cleanup:
+ bhnd_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem_res);
+ BHND_PCI_LOCK_DESTROY(sc);
+
+ return (error);
+}
+
+int
+bhnd_pci_generic_detach(device_t dev)
+{
+ struct bhnd_pci_softc *sc;
+ int error;
+
+ sc = device_get_softc(dev);
+
+ if ((error = bus_generic_detach(dev)))
+ return (error);
+
+ bhnd_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem_res);
+
+ BHND_PCI_LOCK_DESTROY(sc);
+
+ return (0);
+}
+
+static struct resource_list *
+bhnd_pci_get_resource_list(device_t dev, device_t child)
+{
+ struct bhnd_pci_devinfo *dinfo;
+
+ if (device_get_parent(child) != dev)
+ return (NULL);
+
+ dinfo = device_get_ivars(child);
+ return (&dinfo->resources);
+}
+
+static device_t
+bhnd_pci_add_child(device_t dev, u_int order, const char *name, int unit)
+{
+ struct bhnd_pci_devinfo *dinfo;
+ device_t child;
+
+ child = device_add_child_ordered(dev, order, name, unit);
+ if (child == NULL)
+ return (NULL);
+
+ dinfo = malloc(sizeof(struct bhnd_pci_devinfo), M_DEVBUF, M_NOWAIT);
+ if (dinfo == NULL) {
+ device_delete_child(dev, child);
+ return (NULL);
+ }
+
+ resource_list_init(&dinfo->resources);
+
+ device_set_ivars(child, dinfo);
+ return (child);
+}
+
+static void
+bhnd_pci_child_deleted(device_t dev, device_t child)
+{
+ struct bhnd_pci_devinfo *dinfo;
+
+ if (device_get_parent(child) != dev)
+ return;
+
+ dinfo = device_get_ivars(child);
+ if (dinfo != NULL) {
+ resource_list_free(&dinfo->resources);
+ free(dinfo, M_DEVBUF);
+ }
+
+ device_set_ivars(child, NULL);
+}
+
+int
+bhnd_pci_generic_suspend(device_t dev)
+{
+ return (bus_generic_suspend(dev));
+}
+
+int
+bhnd_pci_generic_resume(device_t dev)
+{
+ return (bus_generic_resume(dev));
+}
+
+/**
+ * Read a 32-bit PCIe TLP/DLLP/PLP protocol register.
+ *
+ * @param sc The bhndb_pci driver state.
+ * @param addr The protocol register offset.
+ */
+uint32_t
+bhnd_pcie_read_proto_reg(struct bhnd_pci_softc *sc, uint32_t addr)
+{
+ uint32_t val;
+
+ BHND_PCIE_ASSERT(sc);
+
+ BHND_PCI_LOCK(sc);
+ BHND_PCI_WRITE_4(sc, BHND_PCIE_IND_ADDR, addr);
+ val = BHND_PCI_READ_4(sc, BHND_PCIE_IND_DATA);
+ BHND_PCI_UNLOCK(sc);
+
+ return (val);
+}
+
+/**
+ * Write a 32-bit PCIe TLP/DLLP/PLP protocol register value.
+ *
+ * @param sc The bhndb_pci driver state.
+ * @param addr The protocol register offset.
+ * @param val The value to write to @p addr.
+ */
+void
+bhnd_pcie_write_proto_reg(struct bhnd_pci_softc *sc, uint32_t addr,
+ uint32_t val)
+{
+ BHND_PCIE_ASSERT(sc);
+
+ BHND_PCI_LOCK(sc);
+ BHND_PCI_WRITE_4(sc, BHND_PCIE_IND_ADDR, addr);
+ BHND_PCI_WRITE_4(sc, BHND_PCIE_IND_DATA, val);
+ BHND_PCI_UNLOCK(sc);
+}
+
+/* Spin until the MDIO device reports itself as idle, or timeout is reached. */
+static int
+bhnd_pcie_mdio_wait_idle(struct bhnd_pci_softc *sc)
+{
+ uint32_t ctl;
+
+ /* Spin waiting for the BUSY flag to clear */
+ for (int i = 0; i < BHND_PCIE_MDIO_RETRY_COUNT; i++) {
+ ctl = BHND_PCI_READ_4(sc, BHND_PCIE_MDIO_CTL);
+ if ((ctl & BHND_PCIE_MDIOCTL_DONE))
+ return (0);
+
+ DELAY(BHND_PCIE_MDIO_RETRY_DELAY);
+ }
+
+ return (ETIMEDOUT);
+}
+
+
+/**
+ * Write an MDIO IOCTL and wait for completion.
+ */
+static int
+bhnd_pcie_mdio_ioctl(struct bhnd_pci_softc *sc, uint32_t cmd)
+{
+ BHND_PCI_LOCK_ASSERT(sc, MA_OWNED);
+
+ BHND_PCI_WRITE_4(sc, BHND_PCIE_MDIO_CTL, cmd);
+ DELAY(BHND_PCIE_MDIO_CTL_DELAY);
+ return (0);
+}
+
+/**
+ * Enable MDIO device
+ */
+static int
+bhnd_pcie_mdio_enable(struct bhnd_pci_softc *sc)
+{
+ uint32_t ctl;
+
+ BHND_PCIE_ASSERT(sc);
+
+ /* Enable MDIO clock and preamble mode */
+ ctl = BHND_PCIE_MDIOCTL_PREAM_EN|BHND_PCIE_MDIOCTL_DIVISOR_VAL;
+ return (bhnd_pcie_mdio_ioctl(sc, ctl));
+}
+
/**
- * PCIe MDIO interface device class
+ * Disable MDIO device.
*/
-devclass_t bhnd_mdio_pci_devclass;
+static void
+bhnd_pcie_mdio_disable(struct bhnd_pci_softc *sc)
+{
+ if (bhnd_pcie_mdio_ioctl(sc, 0))
+ device_printf(sc->dev, "failed to disable MDIO clock\n");
+}
+
+
+/**
+ * Issue a write command and wait for completion
+ */
+static int
+bhnd_pcie_mdio_cmd_write(struct bhnd_pci_softc *sc, uint32_t cmd)
+{
+ int error;
+
+ BHND_PCI_LOCK_ASSERT(sc, MA_OWNED);
+
+ cmd |= BHND_PCIE_MDIODATA_START|BHND_PCIE_MDIODATA_TA|BHND_PCIE_MDIODATA_CMD_WRITE;
+
+ BHND_PCI_WRITE_4(sc, BHND_PCIE_MDIO_DATA, cmd);
+ DELAY(BHND_PCIE_MDIO_CTL_DELAY);
+
+ if ((error = bhnd_pcie_mdio_wait_idle(sc)))
+ return (error);
+
+ return (0);
+}
+
+/**
+ * Issue an an MDIO read command, wait for completion, and return
+ * the result in @p data_read.
+ */
+static int
+bhnd_pcie_mdio_cmd_read(struct bhnd_pci_softc *sc, uint32_t cmd,
+ uint16_t *data_read)
+{
+ int error;
+
+ BHND_PCI_LOCK_ASSERT(sc, MA_OWNED);
+
+ cmd |= BHND_PCIE_MDIODATA_START|BHND_PCIE_MDIODATA_TA|BHND_PCIE_MDIODATA_CMD_READ;
+ BHND_PCI_WRITE_4(sc, BHND_PCIE_MDIO_DATA, cmd);
+ DELAY(BHND_PCIE_MDIO_CTL_DELAY);
+
+ if ((error = bhnd_pcie_mdio_wait_idle(sc)))
+ return (error);
+
+ *data_read = (BHND_PCI_READ_4(sc, BHND_PCIE_MDIO_DATA) &
+ BHND_PCIE_MDIODATA_DATA_MASK);
+ return (0);
+}
+
+
+int
+bhnd_pcie_mdio_read(struct bhnd_pci_softc *sc, int phy, int reg)
+{
+ uint32_t cmd;
+ uint16_t val;
+ int error;
+
+ /* Enable MDIO access */
+ BHND_PCI_LOCK(sc);
+ bhnd_pcie_mdio_enable(sc);
+
+ /* Issue the read */
+ cmd = BHND_PCIE_MDIODATA_ADDR(phy, reg);
+ error = bhnd_pcie_mdio_cmd_read(sc, cmd, &val);
+
+ /* Disable MDIO access */
+ bhnd_pcie_mdio_disable(sc);
+ BHND_PCI_UNLOCK(sc);
+
+ if (error)
+ return (~0U);
+
+ return (val);
+}
+
+int
+bhnd_pcie_mdio_write(struct bhnd_pci_softc *sc, int phy, int reg, int val)
+{
+ uint32_t cmd;
+ int error;
+
+ /* Enable MDIO access */
+ BHND_PCI_LOCK(sc);
+ bhnd_pcie_mdio_enable(sc);
+
+ /* Issue the write */
+ cmd = BHND_PCIE_MDIODATA_ADDR(phy, reg) | (val & BHND_PCIE_MDIODATA_DATA_MASK);
+ error = bhnd_pcie_mdio_cmd_write(sc, cmd);
+
+ /* Disable MDIO access */
+ bhnd_pcie_mdio_disable(sc);
+ BHND_PCI_UNLOCK(sc);
+
+ return (error);
+}
+
+int
+bhnd_pcie_mdio_read_ext(struct bhnd_pci_softc *sc, int phy, int devaddr,
+ int reg)
+{
+ uint32_t cmd;
+ uint16_t blk, val;
+ uint8_t blk_reg;
+ int error;
+
+ if (devaddr == MDIO_DEVADDR_NONE)
+ return (bhnd_pcie_mdio_read(sc, phy, reg));
+
+ /* Extended register access is only supported for the SerDes device,
+ * using the non-standard C22 extended address mechanism */
+ if (!(sc->quirks & BHND_PCI_QUIRK_SD_C22_EXTADDR))
+ return (~0U);
+ if (phy != BHND_PCIE_PHYADDR_SD || devaddr != BHND_PCIE_DEVAD_SD)
+ return (~0U);
+
+ /* Enable MDIO access */
+ BHND_PCI_LOCK(sc);
+ bhnd_pcie_mdio_enable(sc);
+
+ /* Determine the block and register values */
+ blk = (reg & BHND_PCIE_SD_ADDREXT_BLK_MASK);
+ blk_reg = (reg & BHND_PCIE_SD_ADDREXT_REG_MASK);
+
+ /* Write the block address to the address extension register */
+ cmd = BHND_PCIE_MDIODATA_ADDR(phy, BHND_PCIE_SD_ADDREXT) |
+ (blk & BHND_PCIE_MDIODATA_DATA_MASK);
+ if ((error = bhnd_pcie_mdio_cmd_write(sc, cmd)))
+ goto cleanup;
+
+ /* Issue the read */
+ cmd = BHND_PCIE_MDIODATA_ADDR(phy, blk_reg);
+ error = bhnd_pcie_mdio_cmd_read(sc, cmd, &val);
+
+cleanup:
+ bhnd_pcie_mdio_disable(sc);
+ BHND_PCI_UNLOCK(sc);
+
+ if (error)
+ return (~0U);
+
+ return (val);
+}
+
+int
+bhnd_pcie_mdio_write_ext(struct bhnd_pci_softc *sc, int phy, int devaddr,
+ int reg, int val)
+{
+ uint32_t cmd;
+ uint16_t blk;
+ uint8_t blk_reg;
+ int error;
+
+ if (devaddr == MDIO_DEVADDR_NONE)
+ return (bhnd_pcie_mdio_write(sc, phy, reg, val));
+
+ /* Extended register access is only supported for the SerDes device,
+ * using the non-standard C22 extended address mechanism */
+ if (!(sc->quirks & BHND_PCI_QUIRK_SD_C22_EXTADDR))
+ return (~0U);
+ if (phy != BHND_PCIE_PHYADDR_SD || devaddr != BHND_PCIE_DEVAD_SD)
+ return (~0U);
+
+ /* Enable MDIO access */
+ BHND_PCI_LOCK(sc);
+ bhnd_pcie_mdio_enable(sc);
+
+ /* Determine the block and register values */
+ blk = (reg & BHND_PCIE_SD_ADDREXT_BLK_MASK);
+ blk_reg = (reg & BHND_PCIE_SD_ADDREXT_REG_MASK);
+
+ /* Write the block address to the address extension register */
+ cmd = BHND_PCIE_MDIODATA_ADDR(phy, BHND_PCIE_SD_ADDREXT) |
+ (blk & BHND_PCIE_MDIODATA_DATA_MASK);
+ if ((error = bhnd_pcie_mdio_cmd_write(sc, cmd)))
+ goto cleanup;
+
+ /* Issue the write */
+ cmd = BHND_PCIE_MDIODATA_ADDR(phy, blk_reg) |
+ (val & BHND_PCIE_MDIODATA_DATA_MASK);
+ error = bhnd_pcie_mdio_cmd_write(sc, cmd);
+
+cleanup:
+ bhnd_pcie_mdio_disable(sc);
+ BHND_PCI_UNLOCK(sc);
+
+ return (error);
+}
+
+static device_method_t bhnd_pci_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, bhnd_pci_generic_probe),
+ DEVMETHOD(device_attach, bhnd_pci_generic_attach),
+ DEVMETHOD(device_detach, bhnd_pci_generic_detach),
+ DEVMETHOD(device_suspend, bhnd_pci_generic_suspend),
+ DEVMETHOD(device_resume, bhnd_pci_generic_resume),
+
+ /* Bus interface */
+ DEVMETHOD(bus_add_child, bhnd_pci_add_child),
+ DEVMETHOD(bus_child_deleted, bhnd_pci_child_deleted),
+ DEVMETHOD(bus_print_child, bus_generic_print_child),
+ DEVMETHOD(bus_get_resource_list, bhnd_pci_get_resource_list),
+ DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource),
+ DEVMETHOD(bus_set_resource, bus_generic_rl_set_resource),
+ DEVMETHOD(bus_delete_resource, bus_generic_rl_delete_resource),
+
+ DEVMETHOD(bus_alloc_resource, bus_generic_rl_alloc_resource),
+ DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
+ DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
+ DEVMETHOD(bus_adjust_resource, bus_generic_adjust_resource),
+ DEVMETHOD(bus_release_resource, bus_generic_rl_release_resource),
+
+ DEVMETHOD_END
+};
+
+DEFINE_CLASS_0(bhnd_pci, bhnd_pci_driver, bhnd_pci_methods, sizeof(struct bhnd_pci_softc));
MODULE_VERSION(bhnd_pci, 1);
MODULE_DEPEND(bhnd_pci, pci, 1, 1, 1);
Index: sys/dev/bhnd/cores/pci/bhnd_pci_hostb.c
===================================================================
--- sys/dev/bhnd/cores/pci/bhnd_pci_hostb.c
+++ sys/dev/bhnd/cores/pci/bhnd_pci_hostb.c
@@ -31,16 +31,23 @@
__FBSDID("$FreeBSD$");
/*
- * Broadcom PCI-BHND Host Bridge.
+ * Broadcom BHND PCI/PCIe-Gen1 PCI-Host Bridge.
*
- * This driver is used to "eat" PCI(e) cores operating in endpoint mode when
- * they're attached to a bhndb_pci driver on the host side.
+ * This driver handles all interactions with PCI bridge cores operating in
+ * endpoint mode.
+ *
+ * Host-level PCI operations are handled at the bhndb bridge level by the
+ * bhndb_pci driver.
*/
#include <sys/param.h>
#include <sys/kernel.h>
+
+#include <sys/malloc.h>
+
#include <sys/bus.h>
#include <sys/module.h>
+
#include <sys/systm.h>
#include <machine/bus.h>
@@ -49,69 +56,397 @@
#include <dev/bhnd/bhnd.h>
-struct bhnd_pci_hostb_softc {
+#include "bhnd_pcireg.h"
+#include "bhnd_pci_hostbvar.h"
+
+#define BHND_PCI_ASSERT_QUIRK(_sc, _name) \
+ KASSERT((_sc)->quirks & (_name), ("quirk " __STRING(_name) " not set"))
+
+#define BHND_PCI_DEV(_core, _quirks) \
+ BHND_DEVICE(_core, "", _quirks, BHND_DF_HOSTB)
+
+static const struct bhnd_device_quirk bhnd_pci_quirks[];
+static const struct bhnd_device_quirk bhnd_pcie_quirks[];
+
+static int bhnd_pci_wars_early_once(struct bhnd_pcihb_softc *sc);
+static int bhnd_pci_wars_hwup(struct bhnd_pcihb_softc *sc);
+static int bhnd_pci_wars_hwdown(struct bhnd_pcihb_softc *sc);
+
+/*
+ * device/quirk tables
+ */
+static const struct bhnd_device bhnd_pci_devs[] = {
+ BHND_PCI_DEV(PCI, bhnd_pci_quirks),
+ BHND_PCI_DEV(PCIE, bhnd_pcie_quirks),
+ BHND_DEVICE_END
};
+static const struct bhnd_device_quirk bhnd_pci_quirks[] = {
+ { BHND_HWREV_ANY, BHND_PCI_QUIRK_SBTOPCI2_PREF_BURST },
+ { BHND_HWREV_GTE(11), BHND_PCI_QUIRK_SBTOPCI2_READMULTI |
+ BHND_PCI_QUIRK_CLKRUN_DSBL },
+ BHND_DEVICE_QUIRK_END
+};
+
+static const struct bhnd_device_quirk bhnd_pcie_quirks[] = {
+ { BHND_HWREV_EQ (0), BHND_PCIE_QUIRK_SDR9_L0s_HANG },
+ { BHND_HWREV_RANGE (0, 1), BHND_PCIE_QUIRK_UR_STATUS_FIX },
+ { BHND_HWREV_EQ (1), BHND_PCIE_QUIRK_PCIPM_REQEN },
+
+ { BHND_HWREV_RANGE (3, 5), BHND_PCIE_QUIRK_ASPM_OVR |
+ BHND_PCIE_QUIRK_SDR9_POLARITY |
+ BHND_PCIE_QUIRK_SDR9_NO_FREQRETRY },
+
+ { BHND_HWREV_LTE (6), BHND_PCIE_QUIRK_L1_IDLE_THRESH },
+ { BHND_HWREV_GTE (6), BHND_PCIE_QUIRK_SPROM_L23_PCI_RESET },
+ { BHND_HWREV_EQ (7), BHND_PCIE_QUIRK_SERDES_NOPLLDOWN },
+ { BHND_HWREV_GTE (8), BHND_PCIE_QUIRK_L1_TIMER_PERF },
+ { BHND_HWREV_GTE (10), BHND_PCIE_QUIRK_SD_C22_EXTADDR },
+ BHND_DEVICE_QUIRK_END
+};
+
+// Quirk handling TODO
+// WARs for the following are not yet implemented:
+// - BHND_PCIE_QUIRK_ASPM_OVR
+// - BHND_PCIE_QUIRK_SERDES_NOPLLDOWN
+// Quirks (and WARs) for the following are not yet defined:
+// - Power savings via MDIO BLK1/PWR_MGMT3 on PCIe hwrev 15-20, 21-22
+// - WOWL PME enable/disable
+// - 4360 PCIe SerDes Tx amplitude/deemphasis (vendor Apple, boards
+// BCM94360X51P2, BCM94360X51A).
+// - PCI latency timer (boards CB2_4321_BOARD, CB2_4321_AG_BOARD)
+// - Max SerDes TX drive strength (vendor Apple, pcie >= rev10,
+// board BCM94322X9)
+// - 700mV SerDes TX drive strength (chipid BCM4331, boards BCM94331X19,
+// BCM94331X28, BCM94331X29B, BCM94331X19C)
+
+#define BHND_PCI_SOFTC(_sc) (&((_sc)->common))
+
+#define BHND_PCI_READ_2(_sc, _reg) \
+ bhnd_bus_read_2(BHND_PCI_SOFTC(_sc)->mem_res, (_reg))
+
+#define BHND_PCI_READ_4(_sc, _reg) \
+ bhnd_bus_read_4(BHND_PCI_SOFTC(_sc)->mem_res, (_reg))
+
+#define BHND_PCI_WRITE_2(_sc, _reg, _val) \
+ bhnd_bus_write_2(BHND_PCI_SOFTC(_sc)->mem_res, (_reg), (_val))
+
+#define BHND_PCI_WRITE_4(_sc, _reg, _val) \
+ bhnd_bus_write_4(BHND_PCI_SOFTC(_sc)->mem_res, (_reg), (_val))
+
+#define BHND_PCI_PROTO_READ_4(_sc, _reg) \
+ bhnd_pcie_read_proto_reg(BHND_PCI_SOFTC(_sc), (_reg))
+
+#define BHND_PCI_PROTO_WRITE_4(_sc, _reg, _val) \
+ bhnd_pcie_write_proto_reg(BHND_PCI_SOFTC(_sc), (_reg), (_val))
+
+#define BHND_PCI_MDIO_READ(_sc, _phy, _reg) \
+ bhnd_pcie_mdio_read(BHND_PCI_SOFTC(_sc), (_phy), (_reg))
+
+#define BHND_PCI_MDIO_WRITE(_sc, _phy, _reg, _val) \
+ bhnd_pcie_mdio_write(BHND_PCI_SOFTC(_sc), (_phy), (_reg), (_val))
+
+#define BPCI_REG_SET(_regv, _attr, _val) \
+ BHND_PCI_REG_SET((_regv), BHND_ ## _attr, (_val))
+
+#define BPCI_REG_GET(_regv, _attr) \
+ BHND_PCI_REG_GET((_regv), BHND_ ## _attr)
+
+#define BPCI_CMN_REG_SET(_regv, _attr, _val) \
+ BHND_PCI_CMN_REG_SET(BHND_PCI_SOFTC(_sc)->regfmt, (_regv), \
+ BHND_ ## _attr, (_val))
+
+#define BPCI_CMN_REG_GET(_regv, _attr) \
+ BHND_PCI_CMN_REG_GET(BHND_PCI_SOFTC(_sc)->regfmt, (_regv), \
+ BHND_ ## _attr)
+
static int
-bhnd_pci_hostb_probe(device_t dev)
+bhnd_pci_hostb_attach(device_t dev)
{
- /* Ignore non-PCI cores */
- switch (bhnd_get_class(dev)){
- case BHND_DEVCLASS_PCI:
- case BHND_DEVCLASS_PCIE:
- break;
- default:
- return (ENXIO);
+ struct bhnd_pcihb_softc *sc;
+ int error;
+
+ sc = device_get_softc(dev);
+ sc->quirks = bhnd_device_quirks(dev, bhnd_pci_devs,
+ sizeof(bhnd_pci_devs[0]));
+
+ if ((error = bhnd_pci_generic_attach(dev)))
+ return (error);
+
+ /* Apply early single-shot work-arounds */
+ if ((error = bhnd_pci_wars_early_once(sc))) {
+ bhnd_pci_generic_detach(dev);
+ return (error);
}
- /* Ignore PCI cores not in host bridge mode. */
- if (!bhnd_is_hostb_device(dev))
- return (ENXIO);
+ /* Apply attach/resume work-arounds */
+ if ((error = bhnd_pci_wars_hwup(sc))) {
+ bhnd_pci_generic_detach(dev);
+ return (error);
+ }
- bhnd_set_default_core_desc(dev);
- return (BUS_PROBE_DEFAULT);
+
+ return (0);
}
static int
-bhnd_pci_hostb_attach(device_t dev)
+bhnd_pci_hostb_detach(device_t dev)
{
- return (0);
+ struct bhnd_pcihb_softc *sc;
+ int error;
+
+ sc = device_get_softc(dev);
+
+ /* Apply suspend/detach work-arounds */
+ if ((error = bhnd_pci_wars_hwdown(sc)))
+ return (error);
+
+ return (bhnd_pci_generic_detach(dev));
}
static int
-bhnd_pci_hostb_detach(device_t dev)
+bhnd_pci_hostb_suspend(device_t dev)
{
+ struct bhnd_pcihb_softc *sc;
+ int error;
+
+ sc = device_get_softc(dev);
+
+ /* Apply suspend/detach work-arounds */
+ if ((error = bhnd_pci_wars_hwdown(sc)))
+ return (error);
+
+ return (bhnd_pci_generic_suspend(dev));
+}
+
+static int
+bhnd_pci_hostb_resume(device_t dev)
+{
+ struct bhnd_pcihb_softc *sc;
+ int error;
+
+ sc = device_get_softc(dev);
+
+ if ((error = bhnd_pci_generic_resume(dev)))
+ return (error);
+
+ /* Apply attach/resume work-arounds */
+ if ((error = bhnd_pci_wars_hwup(sc))) {
+ bhnd_pci_generic_detach(dev);
+ return (error);
+ }
+
return (0);
}
+/**
+ * Apply any hardware work-arounds that must be executed exactly once, early in
+ * the attach process.
+ *
+ * This must be called after core enumeration and discovery of all applicable
+ * quirks, but prior to probe/attach of any cores, parsing of
+ * SPROM, etc.
+ */
static int
-bhnd_pci_hostb_suspend(device_t dev)
+bhnd_pci_wars_early_once(struct bhnd_pcihb_softc *sc)
{
+ /* Determine correct polarity by observing the attach-time PCIe PHY
+ * link status. This is used later to reset/force the SerDes
+ * polarity */
+ if (sc->quirks & BHND_PCIE_QUIRK_SDR9_POLARITY) {
+ uint32_t st;
+ bool inv;
+
+
+ st = BHND_PCI_PROTO_READ_4(sc, BHND_PCIE_PLP_STATUSREG);
+ inv = ((st & BHND_PCIE_PLP_POLARITY_INV) != 0);
+ sc->sdr9_quirk_polarity.inv = inv;
+ }
+
return (0);
}
+/**
+ * Apply any hardware workarounds that are required upon attach or resume
+ * of the bridge device.
+ */
static int
-bhnd_pci_hostb_resume(device_t dev)
+bhnd_pci_wars_hwup(struct bhnd_pcihb_softc *sc)
{
+ /* Note that the order here matters; these work-arounds
+ * should not be re-ordered without careful review of their
+ * interdependencies */
+
+ /* Enable PCI prefetch/burst/readmulti flags */
+ if (sc->quirks & BHND_PCI_QUIRK_SBTOPCI2_PREF_BURST ||
+ sc->quirks & BHND_PCI_QUIRK_SBTOPCI2_READMULTI)
+ {
+ uint32_t sbp2;
+ sbp2 = BHND_PCI_READ_4(sc, BHND_PCI_SBTOPCI2);
+
+ if (sc->quirks & BHND_PCI_QUIRK_SBTOPCI2_PREF_BURST)
+ sbp2 |= (BHND_PCI_SBTOPCI_PREF|BHND_PCI_SBTOPCI_BURST);
+
+ if (sc->quirks & BHND_PCI_QUIRK_SBTOPCI2_READMULTI)
+ sbp2 |= BHND_PCI_SBTOPCI_RC_READMULTI;
+
+ BHND_PCI_WRITE_4(sc, BHND_PCI_SBTOPCI2, sbp2);
+ }
+
+ /* Disable PCI CLKRUN# */
+ if (sc->quirks & BHND_PCI_QUIRK_CLKRUN_DSBL) {
+ uint32_t ctl;
+
+ ctl = BHND_PCI_READ_4(sc, BHND_PCI_CLKRUN_CTL);
+ ctl |= BHND_PCI_CLKRUN_DSBL;
+ BHND_PCI_WRITE_4(sc, BHND_PCI_CLKRUN_CTL, ctl);
+ }
+
+ /* Enable TLP unmatched address handling work-around */
+ if (sc->quirks & BHND_PCIE_QUIRK_UR_STATUS_FIX) {
+ uint32_t wrs;
+ wrs = BHND_PCI_PROTO_READ_4(sc, BHND_PCIE_TLP_WORKAROUNDSREG);
+ wrs |= BHND_PCIE_TLP_WORKAROUND_URBIT;
+ BHND_PCI_PROTO_WRITE_4(sc, BHND_PCIE_TLP_WORKAROUNDSREG, wrs);
+ }
+
+ /* Adjust SerDes CDR tuning to ensure that CDR is stable before sending
+ * data during L0s to L0 exit transitions. */
+ if (sc->quirks & BHND_PCIE_QUIRK_SDR9_L0s_HANG) {
+ uint16_t sdv;
+
+ /* Set RX track/acquire timers to 2.064us/40.96us */
+ sdv = BPCI_REG_SET(0, PCIE_SDR9_RX_TIMER1_LKTRK, (2064/16));
+ sdv = BPCI_REG_SET(sdv, PCIE_SDR9_RX_TIMER1_LKACQ,
+ (40960/1024));
+ BHND_PCI_MDIO_WRITE(sc, BHND_PCIE_PHY_SDR9_TXRX,
+ BHND_PCIE_SDR9_RX_TIMER1, sdv);
+
+ /* Apply CDR frequency workaround */
+ sdv = BHND_PCIE_SDR9_RX_CDR_FREQ_OVR_EN;
+ sdv = BPCI_REG_SET(sdv, PCIE_SDR9_RX_CDR_FREQ_OVR, 0x0);
+ BHND_PCI_MDIO_WRITE(sc, BHND_PCIE_PHY_SDR9_TXRX,
+ BHND_PCIE_SDR9_RX_CDR, sdv);
+
+ /* Apply CDR BW tunings */
+ sdv = 0;
+ sdv = BPCI_REG_SET(sdv, PCIE_SDR9_RX_CDRBW_INTGTRK, 0x2);
+ sdv = BPCI_REG_SET(sdv, PCIE_SDR9_RX_CDRBW_INTGACQ, 0x4);
+ sdv = BPCI_REG_SET(sdv, PCIE_SDR9_RX_CDRBW_PROPTRK, 0x6);
+ sdv = BPCI_REG_SET(sdv, PCIE_SDR9_RX_CDRBW_PROPACQ, 0x6);
+ BHND_PCI_MDIO_WRITE(sc, BHND_PCIE_PHY_SDR9_TXRX,
+ BHND_PCIE_SDR9_RX_CDRBW, sdv);
+ }
+
+ /* Force correct SerDes polarity */
+ if (sc->quirks & BHND_PCIE_QUIRK_SDR9_POLARITY) {
+ uint16_t rxctl;
+
+ rxctl = BHND_PCI_MDIO_READ(sc, BHND_PCIE_PHY_SDR9_TXRX,
+ BHND_PCIE_SDR9_RX_CTRL);
+
+ rxctl |= BHND_PCIE_SDR9_RX_CTRL_FORCE;
+ if (sc->sdr9_quirk_polarity.inv)
+ rxctl |= BHND_PCIE_SDR9_RX_CTRL_POLARITY_INV;
+ else
+ rxctl &= ~BHND_PCIE_SDR9_RX_CTRL_POLARITY_INV;
+
+ BHND_PCI_MDIO_WRITE(sc, BHND_PCIE_PHY_SDR9_TXRX,
+ BHND_PCIE_SDR9_RX_CTRL, rxctl);
+ }
+
+ /* Disable startup retry on PLL frequency detection failure */
+ if (sc->quirks & BHND_PCIE_QUIRK_SDR9_NO_FREQRETRY) {
+ uint16_t pctl;
+
+ pctl = BHND_PCI_MDIO_READ(sc, BHND_PCIE_PHY_SDR9_PLL,
+ BHND_PCIE_SDR9_PLL_CTRL);
+
+ pctl &= ~BHND_PCIE_SDR9_PLL_CTRL_FREQDET_EN;
+ BHND_PCI_MDIO_WRITE(sc, BHND_PCIE_PHY_SDR9_PLL,
+ BHND_PCIE_SDR9_PLL_CTRL, pctl);
+ }
+
+ /* Explicitly enable PCI-PM */
+ if (sc->quirks & BHND_PCIE_QUIRK_PCIPM_REQEN) {
+ uint32_t lcreg;
+ lcreg = BHND_PCI_PROTO_READ_4(sc, BHND_PCIE_DLLP_LCREG);
+ lcreg |= BHND_PCIE_DLLP_LCREG_PCIPM_EN;
+ BHND_PCI_PROTO_WRITE_4(sc, BHND_PCIE_DLLP_LCREG, lcreg);
+ }
+
+ /* Adjust L1 timer to fix slow L1->L0 transitions */
+ if (sc->quirks & BHND_PCIE_QUIRK_L1_IDLE_THRESH) {
+ uint32_t pmt;
+ pmt = BHND_PCI_PROTO_READ_4(sc, BHND_PCIE_DLLP_PMTHRESHREG);
+ pmt = BPCI_REG_SET(pmt, PCIE_L1THRESHOLDTIME,
+ BHND_PCIE_L1THRESHOLD_WARVAL);
+ BHND_PCI_PROTO_WRITE_4(sc, BHND_PCIE_DLLP_PMTHRESHREG, pmt);
+ }
+
+ /* Extend L1 timer for better performance.
+ * TODO: We could enable/disable this on demand for better power
+ * savings if we tie this to HT clock request handling */
+ if (sc->quirks & BHND_PCIE_QUIRK_L1_TIMER_PERF) {
+ uint32_t pmt;
+ pmt = BHND_PCI_PROTO_READ_4(sc, BHND_PCIE_DLLP_PMTHRESHREG);
+ pmt |= BHND_PCIE_ASPMTIMER_EXTEND;
+ BHND_PCI_PROTO_WRITE_4(sc, BHND_PCIE_DLLP_PMTHRESHREG, pmt);
+ }
+
+ /* Enable L23READY_EXIT_NOPRST if not already set in SPROM. */
+ if (sc->quirks & BHND_PCIE_QUIRK_SPROM_L23_PCI_RESET) {
+ bus_size_t reg;
+ uint16_t cfg;
+
+ /* Fetch the misc cfg flags from SPROM */
+ reg = BHND_PCIE_SPROM_SHADOW + BHND_PCIE_SRSH_PCIE_MISC_CONFIG;
+ cfg = BHND_PCI_READ_2(sc, reg);
+
+ /* Write EXIT_NOPRST flag if not already set in SPROM */
+ if (!(cfg & BHND_PCIE_SRSH_L23READY_EXIT_NOPRST)) {
+ cfg |= BHND_PCIE_SRSH_L23READY_EXIT_NOPRST;
+ BHND_PCI_WRITE_2(sc, reg, cfg);
+ }
+ }
+
+ return (0);
+}
+
+/**
+ * Apply any hardware workarounds that are required upon detach or suspend
+ * of the bridge device.
+ */
+static int
+bhnd_pci_wars_hwdown(struct bhnd_pcihb_softc *sc)
+{
+ /* Reduce L1 timer for better power savings.
+ * TODO: We could enable/disable this on demand for better power
+ * savings if we tie this to HT clock request handling */
+ if (sc->quirks & BHND_PCIE_QUIRK_L1_TIMER_PERF) {
+ uint32_t pmt;
+ pmt = BHND_PCI_PROTO_READ_4(sc, BHND_PCIE_DLLP_PMTHRESHREG);
+ pmt &= ~BHND_PCIE_ASPMTIMER_EXTEND;
+ BHND_PCI_PROTO_WRITE_4(sc, BHND_PCIE_DLLP_PMTHRESHREG, pmt);
+ }
+
return (0);
}
static device_method_t bhnd_pci_hostb_methods[] = {
/* Device interface */
- DEVMETHOD(device_probe, bhnd_pci_hostb_probe),
- DEVMETHOD(device_attach, bhnd_pci_hostb_attach),
- DEVMETHOD(device_detach, bhnd_pci_hostb_detach),
- DEVMETHOD(device_suspend, bhnd_pci_hostb_suspend),
- DEVMETHOD(device_resume, bhnd_pci_hostb_resume),
+ DEVMETHOD(device_attach, bhnd_pci_hostb_attach),
+ DEVMETHOD(device_detach, bhnd_pci_hostb_detach),
+ DEVMETHOD(device_suspend, bhnd_pci_hostb_suspend),
+ DEVMETHOD(device_resume, bhnd_pci_hostb_resume),
DEVMETHOD_END
};
-DEFINE_CLASS_0(bhnd_pci_hostb, bhnd_pci_hostb_driver, bhnd_pci_hostb_methods,
- sizeof(struct bhnd_pci_hostb_softc));
+DEFINE_CLASS_1(bhnd_pci_hostb, bhnd_pci_hostb_driver, bhnd_pci_hostb_methods,
+ sizeof(struct bhnd_pcihb_softc), bhnd_pci_driver);
DRIVER_MODULE(bhnd_hostb, bhnd, bhnd_pci_hostb_driver, bhnd_hostb_devclass, 0, 0);
MODULE_VERSION(bhnd_pci_hostb, 1);
-MODULE_DEPEND(bhnd_pci_hostb, pci, 1, 1, 1);
MODULE_DEPEND(bhnd_pci_hostb, bhnd_pci, 1, 1, 1);
\ No newline at end of file
Index: sys/dev/bhnd/cores/pci/bhnd_pci_hostbvar.h
===================================================================
--- sys/dev/bhnd/cores/pci/bhnd_pci_hostbvar.h
+++ sys/dev/bhnd/cores/pci/bhnd_pci_hostbvar.h
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2015 Landon Fuller <landon@landonf.org>
+ * Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -29,107 +29,39 @@
* $FreeBSD$
*/
-#ifndef _BHND_BHNDB_PCIVAR_H_
-#define _BHND_BHNDB_PCIVAR_H_
-
-#include <sys/stdint.h>
-
-#include <dev/bhnd/cores/pci/bhnd_pcivar.h>
-
-#include "bhndbvar.h"
+#ifndef _BHND_CORES_PCI_BHND_PCI_HOSTBVAR_H_
+#define _BHND_CORES_PCI_BHND_PCI_HOSTBVAR_H_
/*
- * bhndb(4) PCI driver subclass.
+ * PCI/PCIe-Gen1 Host Bridge definitions.
*/
-DECLARE_CLASS(bhndb_pci_driver);
-
-struct bhndb_pci_softc;
-
-/*
- * An interconnect-specific function implementing BHNDB_SET_WINDOW_ADDR
- */
-typedef int (*bhndb_pci_set_regwin_t)(struct bhndb_pci_softc *sc,
- const struct bhndb_regwin *rw, bhnd_addr_t addr);
-
-
-/**
- * PCI bridge core identification table.
- */
-struct bhndb_pci_id {
- uint16_t device; /**< bhnd device ID */
- bhnd_pci_regfmt_t regfmt; /**< register format */
- struct bhnd_device_quirk *quirks; /**< quirks table */
-};
-
-struct bhndb_pci_softc {
- struct bhndb_softc bhndb; /**< parent softc */
- device_t dev; /**< bridge device */
- bhnd_devclass_t pci_devclass; /**< PCI core's devclass */
- bhndb_pci_set_regwin_t set_regwin; /**< regwin handler */
-
- /*
- * Initialized in BHNDB_INIT_FULL_CONFIG()
- */
-
- device_t mdio; /**< PCIe MDIO device. NULL if not PCIe. */
- bhnd_pci_regfmt_t regfmt; /**< device register format */
-
- struct resource *mem_res; /**< pci core's registers (borrowed reference) */
- bus_size_t mem_off; /**< offset to the PCI core's registers within `mem_res` . */
-
- struct bhnd_resource bhnd_mem_res; /**< bhnd resource representation of mem_res.
- this is a simple 'direct' resource mapping */
+#include <sys/param.h>
+#include <sys/bus.h>
- uint32_t quirks; /**< BHNDB_PCI(E)_QUIRK flags */
+#include "bhnd_pcivar.h"
- /**
- * Driver state specific to BHNDB_PCIE_QUIRK_SDR9_POLARITY.
- */
- struct {
- /**
- * PCIe SerDes RX polarity.
- *
- * Initialized to the PCIe link's RX polarity
- * at attach time. This is used to restore the
- * correct polarity on resume */
- bool inv;
- } sdr9_quirk_polarity;
-};
+DECLARE_CLASS(bhnd_pci_hostb_driver);
/*
* PCI/PCIe-Gen1 endpoint-mode device quirks
*/
enum {
/** No quirks */
- BHNDB_PCI_QUIRK_NONE = 0,
-
- /**
- * BCM4306 chips (and possibly others) do not support the idle
- * low-power clock. Clocking must be bootstrapped at attach/resume by
- * directly adjusting GPIO registers exposed in the PCI config space,
- * and correspondingly, explicitly shutdown at detach/suspend.
- */
- BHNDB_PCI_QUIRK_EXT_CLOCK_GATING = (1<<1),
-
+ BHND_PCI_QUIRK_NONE = 0,
+
/**
* SBTOPCI_PREF and SBTOPCI_BURST must be set on the
* SSB_PCICORE_SBTOPCI2 register.
*/
- BHNDB_PCI_QUIRK_SBTOPCI2_PREF_BURST = (1<<2),
+ BHND_PCI_QUIRK_SBTOPCI2_PREF_BURST = (1<<1),
+
/**
* SBTOPCI_RC_READMULTI must be set on the SSB_PCICORE_SBTOPCI2
* register.
*/
- BHNDB_PCI_QUIRK_SBTOPCI2_READMULTI = (1<<3),
-
- /**
- * Interrupt masking is handled via the interconnect configuration
- * registers (SBINTVEC on siba), rather than the PCI_INT_MASK
- * config register.
- */
- BHNDB_PCI_QUIRK_SBINTVEC = (1<<4),
+ BHND_PCI_QUIRK_SBTOPCI2_READMULTI = (1<<2),
/**
* PCI CLKRUN# should be disabled on attach (via CLKRUN_DSBL).
@@ -139,7 +71,7 @@
* a "force CLKRUN#" *enable* registry key for use on mobile
* hardware.
*/
- BHNDB_PCI_QUIRK_CLKRUN_DSBL = (1<<5),
+ BHND_PCI_QUIRK_CLKRUN_DSBL = (1<<3),
/**
* TLP workaround for unmatched address handling is required.
@@ -147,13 +79,13 @@
* This TLP workaround will enable setting of the PCIe UR status bit
* on memory access to an unmatched address.
*/
- BHNDB_PCIE_QUIRK_UR_STATUS_FIX = (1<<6),
+ BHND_PCIE_QUIRK_UR_STATUS_FIX = (1<<4),
/**
* PCI-PM power management must be explicitly enabled via
* the data link control register.
*/
- BHNDB_PCIE_QUIRK_PCIPM_REQEN = (1<<7),
+ BHND_PCIE_QUIRK_PCIPM_REQEN = (1<<5),
/**
* Fix L0s to L0 exit transition on SerDes <= rev9 devices.
@@ -166,19 +98,19 @@
* filters must be tweaked to ensure the CDR has fully stabilized
* before asserting receive sequencer completion.
*/
- BHNDB_PCIE_QUIRK_SDR9_L0s_HANG = (1<<8),
+ BHND_PCIE_QUIRK_SDR9_L0s_HANG = (1<<6),
/**
* The idle time for entering L1 low-power state must be
* explicitly set (to 114ns) to fix slow L1->L0 transition issues.
*/
- BHNDB_PCIE_QUIRK_L1_IDLE_THRESH = (1<<9),
+ BHND_PCIE_QUIRK_L1_IDLE_THRESH = (1<<7),
/**
* The ASPM L1 entry timer should be extended for better performance,
* and restored for better power savings.
*/
- BHNDB_PCIE_QUIRK_L1_TIMER_PERF = (1<<10),
+ BHND_PCIE_QUIRK_L1_TIMER_PERF = (1<<8),
/**
* ASPM and ECPM settings must be overridden manually.
@@ -198,7 +130,7 @@
* - When the device enters D3 state, or system enters S3/S4 state,
* clear ASPM L1 in the PCIER_LINK_CTL register.
*/
- BHNDB_PCIE_QUIRK_ASPM_OVR = (1<<11),
+ BHND_PCIE_QUIRK_ASPM_OVR = (1<<9),
/**
* Fix SerDes polarity on SerDes <= rev9 devices.
@@ -206,13 +138,13 @@
* The SerDes polarity must be saved at device attachment, and
* restored on suspend/resume.
*/
- BHNDB_PCIE_QUIRK_SDR9_POLARITY = (1<<12),
+ BHND_PCIE_QUIRK_SDR9_POLARITY = (1<<10),
/**
- * The SerDes PLL override flag (CHIPCTRL_4321_PLL_DOWN) must be set on
- * the ChipCommon core on resume.
+ * SerDes PLL down flag must be manually disabled (by ChipCommon) on
+ * resume.
*/
- BHNDB_PCIE_QUIRK_SERDES_NOPLLDOWN = (1<<13),
+ BHND_PCIE_QUIRK_SERDES_NOPLLDOWN = (1<<11),
/**
* On attach and resume, consult the SPROM to determine whether
@@ -220,7 +152,7 @@
*
* If L23READY_EXIT_NOPRST is not already set in the SPROM, set it
*/
- BHNDB_PCIE_QUIRK_SPROM_L23_PCI_RESET = (1<<14),
+ BHND_PCIE_QUIRK_SPROM_L23_PCI_RESET = (1<<12),
/**
* The PCIe SerDes supports non-standard extended MDIO register access.
@@ -228,7 +160,7 @@
* The PCIe SerDes supports access to extended MDIO registers via
* a non-standard Clause 22 address extension mechanism.
*/
- BHNDB_PCIE_QUIRK_SD_C22_EXTADDR = (1<<15),
+ BHND_PCIE_QUIRK_SD_C22_EXTADDR = (1<<13),
/**
* The PCIe SerDes PLL must be configured to not retry the startup
@@ -236,7 +168,27 @@
*
* The issue this workaround resolves has not be determined.
*/
- BHNDB_PCIE_QUIRK_SDR9_NO_FREQRETRY = (1<<16),
+ BHND_PCIE_QUIRK_SDR9_NO_FREQRETRY = (1<<14),
+};
+
+/**
+ * bhnd_pci_hostb driver instance state.
+ */
+struct bhnd_pcihb_softc {
+ struct bhnd_pci_softc common; /**< common bhnd_pci state */
+ uint32_t quirks; /**< hostb device quirks */
+
+ /** BHND_PCIE_QUIRK_SDR9_POLARITY state. */
+ struct {
+ /**
+ * PCIe SerDes RX polarity.
+ *
+ * Initialized to the PCIe link's RX polarity
+ * at attach time. This is used to restore the
+ * correct polarity on resume */
+ bool inv;
+ } sdr9_quirk_polarity;
};
-#endif /* _BHND_BHNDB_PCIVAR_H_ */
\ No newline at end of file
+
+#endif /* _BHND_CORES_PCI_BHND_PCI_HOSTBVAR_H_ */
\ No newline at end of file
Index: sys/dev/bhnd/cores/pci/bhnd_pcib.c
===================================================================
--- sys/dev/bhnd/cores/pci/bhnd_pcib.c
+++ sys/dev/bhnd/cores/pci/bhnd_pcib.c
@@ -31,10 +31,10 @@
__FBSDID("$FreeBSD$");
/*
- * Broadcom PCI-BHND Host Bridge.
+ * Broadcom PCI/PCIe-Gen1 Host-PCI bridge.
*
- * This driver is used to "eat" PCI(e) cores operating in endpoint mode when
- * they're attached to a bhndb_pci driver on the host side.
+ * This driver handles all interactions with PCI bridge cores operating in
+ * root complex mode.
*/
#include <sys/param.h>
@@ -49,69 +49,36 @@
#include <dev/bhnd/bhnd.h>
#include "bhnd_pcireg.h"
-
#include "bhnd_pcibvar.h"
-static const struct bhnd_pcib_device {
- uint16_t vendor;
- uint16_t device;
- const char *desc;
-} bhnd_pcib_devs[] = {
- { BHND_MFGID_BCM, BHND_COREID_PCI, "BHND Host-PCI bridge" },
- { BHND_MFGID_BCM, BHND_COREID_PCIE, "BHND Host-PCI bridge (PCIe Gen1)" },
- { BHND_MFGID_INVALID, BHND_COREID_INVALID, NULL }
-};
-
-static int
-bhnd_pcib_probe(device_t dev)
-{
- const struct bhnd_pcib_device *id;
-
- /* Ignore PCI cores configured in host bridge mode */
- if (bhnd_is_hostb_device(dev))
- return (ENXIO);
-
- for (id = bhnd_pcib_devs; id->device != BHND_COREID_INVALID; id++) {
- if (bhnd_get_vendor(dev) != id->vendor)
- continue;
-
- if (bhnd_get_device(dev) != id->device)
- continue;
-
- device_set_desc(dev, id->desc);
- return (BUS_PROBE_SPECIFIC);
- }
-
- return (ENXIO);
-}
-
static int
bhnd_pcib_attach(device_t dev)
{
- return (ENXIO);
+ // TODO
+ return (bhnd_pci_generic_attach(dev));
}
static int
bhnd_pcib_detach(device_t dev)
{
- return (ENXIO);
+ // TODO
+ return (bhnd_pci_generic_detach(dev));
}
static int
bhnd_pcib_suspend(device_t dev)
{
- return (ENXIO);
+ return (bhnd_pci_generic_suspend(dev));
}
static int
bhnd_pcib_resume(device_t dev)
{
- return (ENXIO);
+ return (bhnd_pci_generic_resume(dev));
}
static device_method_t bhnd_pcib_methods[] = {
/* Device interface */
- DEVMETHOD(device_probe, bhnd_pcib_probe),
DEVMETHOD(device_attach, bhnd_pcib_attach),
DEVMETHOD(device_detach, bhnd_pcib_detach),
DEVMETHOD(device_suspend, bhnd_pcib_suspend),
@@ -119,7 +86,7 @@
DEVMETHOD_END
};
-DEFINE_CLASS_0(bhnd_pcib, bhnd_pcib_driver, bhnd_pcib_methods, sizeof(struct bhnd_pcib_softc));
+DEFINE_CLASS_1(bhnd_pcib, bhnd_pcib_driver, bhnd_pcib_methods, sizeof(struct bhnd_pcib_softc), bhnd_pci_driver);
DRIVER_MODULE(bhnd_pcib, bhnd, bhnd_pcib_driver, bhnd_hostb_devclass, 0, 0);
MODULE_VERSION(bhnd_pcib, 1);
Index: sys/dev/bhnd/cores/pci/bhnd_pcibvar.h
===================================================================
--- sys/dev/bhnd/cores/pci/bhnd_pcibvar.h
+++ sys/dev/bhnd/cores/pci/bhnd_pcibvar.h
@@ -35,16 +35,8 @@
#include "bhnd_pcivar.h"
/* PCI bridge driver-specific state */
-#define BHND_PCIB_MAX_RES 2
-#define BHND_PCIB_MAX_RSPEC (BHND_PCIB_MAX_RES+1)
struct bhnd_pcib_softc {
- device_t dev; /**< pci device */
- struct bhnd_resource *core; /**< core registers. */
- bhnd_pci_regfmt_t regfmt; /**< device register format */
-
- struct resource_spec rspec[BHND_PCIB_MAX_RSPEC];
- struct bhnd_resource *res[BHND_PCIB_MAX_RES];
-
+ struct bhnd_pci_softc sc_common;
};
#endif /* _BHND_CORES_PCI_BHND_PCIBVAR_H_ */
\ No newline at end of file
Index: sys/dev/bhnd/cores/pci/bhnd_pcireg.h
===================================================================
--- sys/dev/bhnd/cores/pci/bhnd_pcireg.h
+++ sys/dev/bhnd/cores/pci/bhnd_pcireg.h
@@ -203,6 +203,31 @@
#define BHND_PCIE_CFG_OFFSET(f, r) \
((((f) & BHND_PCIE_CFG_ADDR_FUNC_MASK) << BHND_PCIE_CFG_ADDR_FUNC_SHIFT) | \
(((r) & BHND_PCIE_CFG_ADDR_FUNC_SHIFT) << BHND_PCIE_CFG_ADDR_REG_SHIFT))
+
+/* BHND_PCIE_MDIO_CTL control */
+#define BHND_PCIE_MDIOCTL_DIVISOR_MASK 0x7f /* clock divisor mask */
+#define BHND_PCIE_MDIOCTL_DIVISOR_VAL 0x2 /* default clock divisor */
+#define BHND_PCIE_MDIOCTL_PREAM_EN 0x80 /* enable preamble mode */
+#define BHND_PCIE_MDIOCTL_DONE 0x100 /* tranaction completed */
+
+/* PCIe BHND_PCIE_MDIO_DATA Data */
+#define BHND_PCIE_MDIODATA_PHYADDR_MASK 0x0f800000 /* phy addr */
+#define BHND_PCIE_MDIODATA_PHYADDR_SHIFT 23
+#define BHND_PCIE_MDIODATA_REGADDR_MASK 0x007c0000 /* reg/dev addr */
+#define BHND_PCIE_MDIODATA_REGADDR_SHIFT 18
+#define BHND_PCIE_MDIODATA_DATA_MASK 0x0000ffff /* data */
+
+#define BHND_PCIE_MDIODATA_TA 0x00020000 /* slave turnaround time */
+#define BHND_PCIE_MDIODATA_START 0x40000000 /* start of transaction */
+#define BHND_PCIE_MDIODATA_CMD_WRITE 0x10000000 /* write command */
+#define BHND_PCIE_MDIODATA_CMD_READ 0x20000000 /* read command */
+
+#define BHND_PCIE_MDIODATA_ADDR(_phyaddr, _regaddr) ( \
+ (((_phyaddr) << BHND_PCIE_MDIODATA_PHYADDR_SHIFT) & \
+ BHND_PCIE_MDIODATA_PHYADDR_MASK) | \
+ (((_regaddr) << BHND_PCIE_MDIODATA_REGADDR_SHIFT) & \
+ BHND_PCIE_MDIODATA_REGADDR_MASK) \
+)
/* PCIE protocol PHY diagnostic registers */
#define BHND_PCIE_PLP_MODEREG 0x200 /* Mode */
Index: sys/dev/bhnd/cores/pci/bhnd_pcivar.h
===================================================================
--- sys/dev/bhnd/cores/pci/bhnd_pcivar.h
+++ sys/dev/bhnd/cores/pci/bhnd_pcivar.h
@@ -39,18 +39,81 @@
* Shared PCI Bridge/PCI Host Bridge definitions.
*/
-extern devclass_t bhnd_mdio_pci_devclass;
+DECLARE_CLASS(bhnd_pci_driver);
+struct bhnd_pci_softc;
-/* Device register families. */
+int bhnd_pci_generic_probe(device_t dev);
+int bhnd_pci_generic_attach(device_t dev);
+int bhnd_pci_generic_detach(device_t dev);
+int bhnd_pci_generic_suspend(device_t dev);
+int bhnd_pci_generic_resume(device_t dev);
+
+uint32_t bhnd_pcie_read_proto_reg(struct bhnd_pci_softc *sc,
+ uint32_t addr);
+void bhnd_pcie_write_proto_reg(struct bhnd_pci_softc *sc,
+ uint32_t addr, uint32_t val);
+int bhnd_pcie_mdio_read(struct bhnd_pci_softc *sc, int phy,
+ int reg);
+int bhnd_pcie_mdio_write(struct bhnd_pci_softc *sc, int phy,
+ int reg, int val);
+int bhnd_pcie_mdio_read_ext(struct bhnd_pci_softc *sc, int phy,
+ int devaddr, int reg);
+int bhnd_pcie_mdio_write_ext(struct bhnd_pci_softc *sc, int phy,
+ int devaddr, int reg, int val);
+
+/** PCI register block layouts. */
typedef enum {
BHND_PCI_REGFMT_PCI = 0, /* PCI register definitions */
BHND_PCI_REGFMT_PCIE = 1, /* PCIe-Gen1 register definitions */
} bhnd_pci_regfmt_t;
-/* Common BHND_PCI_*_REG_(EXTRACT|INSERT) implementation */
-#define _BHND_PCI_REG_EXTRACT(_regval, _mask, _shift) \
+/** PCI (base driver) quirks */
+enum {
+ /**
+ * The PCIe SerDes requires use of a non-standard Clause 22
+ * address extension mechanism to access extended MDIO registers.
+ */
+ BHND_PCI_QUIRK_SD_C22_EXTADDR = (1<<0),
+};
+
+/**
+ * bhnd_pci child device info
+ */
+struct bhnd_pci_devinfo {
+ struct resource_list resources;
+};
+
+/*
+ * Generic PCI bridge/end-point driver state.
+ *
+ * Must be first member of all subclass softc structures.
+ */
+struct bhnd_pci_softc {
+ device_t dev; /**< pci device */
+ uint32_t quirks; /**< quirk flags */
+ bhnd_pci_regfmt_t regfmt; /**< register format */
+
+ struct mtx mtx; /**< state mutex used to protect
+ interdependent register
+ accesses. */
+
+ struct bhnd_resource *mem_res; /**< device register block. */
+ int mem_rid; /**< register block RID */
+};
+
+
+#define BHND_PCI_LOCK_INIT(sc) \
+ mtx_init(&(sc)->mtx, device_get_nameunit((sc)->dev), \
+ "BHND PCI driver lock", MTX_DEF)
+#define BHND_PCI_LOCK(sc) mtx_lock(&(sc)->mtx)
+#define BHND_PCI_UNLOCK(sc) mtx_unlock(&(sc)->mtx)
+#define BHND_PCI_LOCK_ASSERT(sc, what) mtx_assert(&(sc)->mtx, what)
+#define BHND_PCI_LOCK_DESTROY(sc) mtx_destroy(&(sc)->mtx)
+
+/* BHND_PCI_*_REG_(GET|SET) implementation */
+#define _BHND_PCI_REG_GET(_regval, _mask, _shift) \
((_regval & _mask) >> _shift)
-#define _BHND_PCI_REG_INSERT(_regval, _mask, _shift, _setval) \
+#define _BHND_PCI_REG_SET(_regval, _mask, _shift, _setval) \
(((_regval) & ~ _mask) | (((_setval) << _shift) & _mask))
/**
@@ -60,8 +123,8 @@
* @param _attr The register attribute name to which to append `_MASK`/`_SHIFT`
* suffixes.
*/
-#define BHND_PCI_REG_EXTRACT(_regv, _attr) \
- _BHND_PCI_REG_EXTRACT(_regv, _attr ## _MASK, _attr ## _SHIFT)
+#define BHND_PCI_REG_GET(_regv, _attr) \
+ _BHND_PCI_REG_GET(_regv, _attr ## _MASK, _attr ## _SHIFT)
/**
* Insert a value in @p _regv by applying _MASK and _SHIFT defines.
@@ -71,20 +134,20 @@
* suffixes.
* @param _val The value to be set in @p _regv.
*/
-#define BHND_PCI_REG_INSERT(_regv, _attr, _val) \
- _BHND_PCI_REG_INSERT(_regv, _attr ## _MASK, _attr ## _SHIFT, _val)
+#define BHND_PCI_REG_SET(_regv, _attr, _val) \
+ _BHND_PCI_REG_SET(_regv, _attr ## _MASK, _attr ## _SHIFT, _val)
/**
* Extract a value by applying _MASK and _SHIFT defines to the common
* PCI/PCIe register definition @p _regv
*
- * @param _regf The PCI core register format (BHNDB_PCI_REGFMT_*).
+ * @param _regf The PCI core register format (BHND_PCI_REGFMT_*).
* @param _regv The register value containing the desired attribute
* @param _attr The register attribute name to which to prepend the register
* definition prefix and append `_MASK`/`_SHIFT` suffixes.
*/
-#define BHND_PCI_COMMON_REG_EXTRACT(_regf, _regv, _attr) \
- _BHND_PCI_REG_EXTRACT(_regv, \
+#define BHND_PCI_CMN_REG_GET(_regf, _regv, _attr) \
+ _BHND_PCI_REG_GET(_regv, \
BHND_PCI_COMMON_REG((_regf), _attr ## _MASK), \
BHND_PCI_COMMON_REG((_regf), _attr ## _SHIFT))
@@ -92,14 +155,14 @@
* Insert a register value by applying _MASK and _SHIFT defines to the common
* PCI/PCIe register definition @p _regv
*
- * @param _regf The PCI core register format (BHNDB_PCI_REGFMT_*).
+ * @param _regf The PCI core register format (BHND_PCI_REGFMT_*).
* @param _regv The register value containing the desired attribute
* @param _attr The register attribute name to which to prepend the register
* definition prefix and append `_MASK`/`_SHIFT` suffixes.
* @param _val The value to bet set in @p _regv.
*/
-#define BHND_PCI_COMMON_REG_INSERT(_regf, _regv, _attr, _val) \
- _BHND_PCI_REG_INSERT(_regv, \
+#define BHND_PCI_CMN_REG_SET(_regf, _regv, _attr, _val) \
+ _BHND_PCI_REG_SET(_regv, \
BHND_PCI_COMMON_REG((_regf), _attr ## _MASK), \
BHND_PCI_COMMON_REG((_regf), _attr ## _SHIFT), \
_val)
@@ -114,12 +177,12 @@
* This should be optimized down to a constant value if the register constant
* is the same across the register definitions.
*
- * @param _regf The PCI core register format (BHNDB_PCI_REGFMT_*).
+ * @param _regf The PCI core register format (BHND_PCI_REGFMT_*).
* @param _name The base name of the register.
*/
#define BHND_PCI_COMMON_REG(_regf, _name) ( \
- (_regf) == BHND_PCI_REGFMT_PCI ? BHND_PCI_ ## _name : \
+ (_regf) == BHND_PCI_REGFMT_PCI ? BHND_PCI_ ## _name : \
BHND_PCIE_ ## _name \
)
#endif /* _BHND_CORES_PCI_BHND_PCIVAR_H_ */
\ No newline at end of file
Index: sys/dev/bhnd/cores/pci/mdio_pcie.c
===================================================================
--- sys/dev/bhnd/cores/pci/mdio_pcie.c
+++ /dev/null
@@ -1,384 +0,0 @@
-/*-
- * Copyright (c) 2015 Landon Fuller <landon@landonf.org>
- * All rights reserved.
- *
- * 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.
- *
- * NO WARRANTY
- * 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.
- */
-
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
-/*
- * MDIO Driver for PCIe-G1 Cores (All Revisions).
- *
- * The MDIO interface provides access to the PCIe SerDes management registers.
- */
-
-#include <sys/param.h>
-#include <sys/kernel.h>
-#include <sys/bus.h>
-#include <sys/module.h>
-#include <sys/systm.h>
-
-#include <machine/bus.h>
-#include <sys/rman.h>
-#include <machine/resource.h>
-
-#include <dev/bhnd/bhnd.h>
-
-#include "bhnd_pcireg.h"
-
-#include "mdio_pciereg.h"
-#include "mdio_pcievar.h"
-
-#define BHND_MDIO_CTL_DELAY 10 /**< usec delay required between
- * MDIO_CTL/MDIO_DATA accesses. */
-#define BHND_MDIO_RETRY_DELAY 2000 /**< usec delay before retrying
- * BHND_MDIOCTL_DONE. */
-#define BHND_MDIO_RETRY_COUNT 200 /**< number of times to loop waiting
- * for BHND_MDIOCTL_DONE. */
-
-#define BHND_MDIO_READ_4(_sc, _reg) \
- bhnd_bus_read_4((_sc)->mem_res, (_sc)->mem_off + (_reg))
-
-#define BHND_MDIO_WRITE_4(_sc, _reg, _val) \
- bhnd_bus_write_4((_sc)->mem_res, (_sc)->mem_off + (_reg), (_val))
-
-static int
-bhnd_mdio_pcie_probe(device_t dev)
-{
- device_set_desc(dev, "Broadcom PCIe-G1 MDIO");
- device_quiet(dev);
-
- return (BUS_PROBE_DEFAULT);
-}
-
-/**
- * Helper function that must be called by subclass BHND MDIO drivers
- * when implementing DEVICE_ATTACH().
- *
- * @param dev The bhnd_mdio device.
- * @param mem_res A memory resource containing the device resources; this
- * @param mem_rid The @p mem_res resource ID, or -1 if this is a borrowed
- * reference that the device should not assume ownership of.
- * @param offset The offset within @p mem_res at which the MMIO register
- * block is defined.
- * @param c22ext If true, the MDIO driver will automatically use the PCIe
- * SerDes' non-standard extended address mechanism when handling C45 register
- * accesses to the PCIe SerDes device (BHND_PCIE_PHYADDR_SD /
- * BHND_PCIE_DEVAD_SD).
- */
-int bhnd_mdio_pcie_attach(device_t dev, struct bhnd_resource *mem_res,
- int mem_rid, bus_size_t offset, bool c22ext)
-{
- struct bhnd_mdio_pcie_softc *sc = device_get_softc(dev);
-
- sc->dev = dev;
- sc->mem_res = mem_res;
- sc->mem_rid = mem_rid;
- sc->mem_off = offset;
- sc->c22ext = c22ext;
-
- BHND_MDIO_PCIE_LOCK_INIT(sc);
-
- return (bus_generic_attach(dev));
-}
-
-static int
-bhnd_mdio_pcie_detach(device_t dev)
-{
- struct bhnd_mdio_pcie_softc *sc = device_get_softc(dev);
-
- BHND_MDIO_PCIE_LOCK_DESTROY(sc);
-
- return (0);
-}
-
-/* Spin until the MDIO device reports itself as idle, or timeout is reached. */
-static int
-bhnd_mdio_pcie_wait_idle(struct bhnd_mdio_pcie_softc *sc)
-{
- uint32_t ctl;
-
- /* Spin waiting for the BUSY flag to clear */
- for (int i = 0; i < BHND_MDIO_RETRY_COUNT; i++) {
- ctl = BHND_MDIO_READ_4(sc, BHND_MDIO_CTL);
- if ((ctl & BHND_MDIOCTL_DONE))
- return (0);
-
- DELAY(BHND_MDIO_RETRY_DELAY);
- }
-
- return (ETIMEDOUT);
-}
-
-
-/**
- * Write an MDIO IOCTL and wait for completion.
- */
-static int
-bhnd_mdio_pcie_ioctl(struct bhnd_mdio_pcie_softc *sc, uint32_t cmd)
-{
- BHND_MDIO_PCIE_LOCK_ASSERT(sc, MA_OWNED);
-
- BHND_MDIO_WRITE_4(sc, BHND_MDIO_CTL, cmd);
- DELAY(BHND_MDIO_CTL_DELAY);
- return (0);
-}
-
-/**
- * Enable MDIO device
- */
-static int
-bhnd_mdio_pcie_enable(struct bhnd_mdio_pcie_softc *sc)
-{
- uint32_t ctl;
-
- /* Enable MDIO clock and preamble mode */
- ctl = BHND_MDIOCTL_PREAM_EN|BHND_MDIOCTL_DIVISOR_VAL;
- return (bhnd_mdio_pcie_ioctl(sc, ctl));
-}
-
-/**
- * Disable MDIO device.
- */
-static void
-bhnd_mdio_pcie_disable(struct bhnd_mdio_pcie_softc *sc)
-{
- if (bhnd_mdio_pcie_ioctl(sc, 0))
- device_printf(sc->dev, "failed to disable MDIO clock\n");
-}
-
-
-/**
- * Issue a write command and wait for completion
- */
-static int
-bhnd_mdio_pcie_cmd_write(struct bhnd_mdio_pcie_softc *sc, uint32_t cmd)
-{
- int error;
-
- BHND_MDIO_PCIE_LOCK_ASSERT(sc, MA_OWNED);
-
- cmd |= BHND_MDIODATA_START|BHND_MDIODATA_TA|BHND_MDIODATA_CMD_WRITE;
-
- BHND_MDIO_WRITE_4(sc, BHND_MDIO_DATA, cmd);
- DELAY(BHND_MDIO_CTL_DELAY);
-
- if ((error = bhnd_mdio_pcie_wait_idle(sc)))
- return (error);
-
- return (0);
-}
-
-/**
- * Issue an an MDIO read command, wait for completion, and return
- * the result in @p data_read.
- */
-static int
-bhnd_mdio_pcie_cmd_read(struct bhnd_mdio_pcie_softc *sc, uint32_t cmd,
- uint16_t *data_read)
-{
- int error;
-
- BHND_MDIO_PCIE_LOCK_ASSERT(sc, MA_OWNED);
-
- cmd |= BHND_MDIODATA_START|BHND_MDIODATA_TA|BHND_MDIODATA_CMD_READ;
- BHND_MDIO_WRITE_4(sc, BHND_MDIO_DATA, cmd);
- DELAY(BHND_MDIO_CTL_DELAY);
-
- if ((error = bhnd_mdio_pcie_wait_idle(sc)))
- return (error);
-
- *data_read = (BHND_MDIO_READ_4(sc, BHND_MDIO_DATA) &
- BHND_MDIODATA_DATA_MASK);
- return (0);
-}
-
-
-static int
-bhnd_mdio_pcie_read(device_t dev, int phy, int reg)
-{
- struct bhnd_mdio_pcie_softc *sc;
- uint32_t cmd;
- uint16_t val;
- int error;
-
- sc = device_get_softc(dev);
-
- /* Enable MDIO access */
- BHND_MDIO_PCIE_LOCK(sc);
- bhnd_mdio_pcie_enable(sc);
-
- /* Issue the read */
- cmd = BHND_MDIODATA_ADDR(phy, reg);
- error = bhnd_mdio_pcie_cmd_read(sc, cmd, &val);
-
- /* Disable MDIO access */
- bhnd_mdio_pcie_disable(sc);
- BHND_MDIO_PCIE_UNLOCK(sc);
-
- if (error)
- return (~0U);
-
- return (val);
-}
-
-static int
-bhnd_mdio_pcie_write(device_t dev, int phy, int reg, int val)
-{
- struct bhnd_mdio_pcie_softc *sc;
- uint32_t cmd;
- int error;
-
- sc = device_get_softc(dev);
-
- /* Enable MDIO access */
- BHND_MDIO_PCIE_LOCK(sc);
- bhnd_mdio_pcie_enable(sc);
-
- /* Issue the write */
- cmd = BHND_MDIODATA_ADDR(phy, reg) | (val & BHND_MDIODATA_DATA_MASK);
- error = bhnd_mdio_pcie_cmd_write(sc, cmd);
-
- /* Disable MDIO access */
- bhnd_mdio_pcie_disable(sc);
- BHND_MDIO_PCIE_UNLOCK(sc);
-
- return (error);
-}
-
-static int
-bhnd_mdio_pcie_read_ext(device_t dev, int phy, int devaddr, int reg)
-{
- struct bhnd_mdio_pcie_softc *sc;
- uint32_t cmd;
- uint16_t blk, val;
- uint8_t blk_reg;
- int error;
-
- if (devaddr == MDIO_DEVADDR_NONE)
- return (MDIO_READREG(dev, phy, reg));
-
- sc = device_get_softc(dev);
-
- /* Extended register access is only supported for the SerDes device,
- * using the non-standard C22 extended address mechanism */
- if (!sc->c22ext)
- return (~0U);
- if (phy != BHND_PCIE_PHYADDR_SD || devaddr != BHND_PCIE_DEVAD_SD)
- return (~0U);
-
- /* Enable MDIO access */
- BHND_MDIO_PCIE_LOCK(sc);
- bhnd_mdio_pcie_enable(sc);
-
- /* Determine the block and register values */
- blk = (reg & BHND_PCIE_SD_ADDREXT_BLK_MASK);
- blk_reg = (reg & BHND_PCIE_SD_ADDREXT_REG_MASK);
-
- /* Write the block address to the address extension register */
- cmd = BHND_MDIODATA_ADDR(phy, BHND_PCIE_SD_ADDREXT) |
- (blk & BHND_MDIODATA_DATA_MASK);
- if ((error = bhnd_mdio_pcie_cmd_write(sc, cmd)))
- goto cleanup;
-
- /* Issue the read */
- cmd = BHND_MDIODATA_ADDR(phy, blk_reg);
- error = bhnd_mdio_pcie_cmd_read(sc, cmd, &val);
-
-cleanup:
- bhnd_mdio_pcie_disable(sc);
- BHND_MDIO_PCIE_UNLOCK(sc);
-
- if (error)
- return (~0U);
-
- return (val);
-}
-
-static int
-bhnd_mdio_pcie_write_ext(device_t dev, int phy, int devaddr, int reg,
- int val)
-{
- struct bhnd_mdio_pcie_softc *sc;
- uint32_t cmd;
- uint16_t blk;
- uint8_t blk_reg;
- int error;
-
- if (devaddr == MDIO_DEVADDR_NONE)
- return (MDIO_READREG(dev, phy, reg));
-
- sc = device_get_softc(dev);
-
- /* Extended register access is only supported for the SerDes device,
- * using the non-standard C22 extended address mechanism */
- if (!sc->c22ext)
- return (~0U);
- if (phy != BHND_PCIE_PHYADDR_SD || devaddr != BHND_PCIE_DEVAD_SD)
- return (~0U);
-
- /* Enable MDIO access */
- BHND_MDIO_PCIE_LOCK(sc);
- bhnd_mdio_pcie_enable(sc);
-
- /* Determine the block and register values */
- blk = (reg & BHND_PCIE_SD_ADDREXT_BLK_MASK);
- blk_reg = (reg & BHND_PCIE_SD_ADDREXT_REG_MASK);
-
- /* Write the block address to the address extension register */
- cmd = BHND_MDIODATA_ADDR(phy, BHND_PCIE_SD_ADDREXT) |
- (blk & BHND_MDIODATA_DATA_MASK);
- if ((error = bhnd_mdio_pcie_cmd_write(sc, cmd)))
- goto cleanup;
-
- /* Issue the write */
- cmd = BHND_MDIODATA_ADDR(phy, blk_reg) |
- (val & BHND_MDIODATA_DATA_MASK);
- error = bhnd_mdio_pcie_cmd_write(sc, cmd);
-
-cleanup:
- bhnd_mdio_pcie_disable(sc);
- BHND_MDIO_PCIE_UNLOCK(sc);
-
- return (error);
-}
-
-static device_method_t bhnd_mdio_pcie_methods[] = {
- /* Device interface */
- DEVMETHOD(device_probe, bhnd_mdio_pcie_probe),
- DEVMETHOD(device_detach, bhnd_mdio_pcie_detach),
-
- /* MDIO interface */
- DEVMETHOD(mdio_readreg, bhnd_mdio_pcie_read),
- DEVMETHOD(mdio_writereg, bhnd_mdio_pcie_write),
- DEVMETHOD(mdio_readextreg, bhnd_mdio_pcie_read_ext),
- DEVMETHOD(mdio_writeextreg, bhnd_mdio_pcie_write_ext),
-
- DEVMETHOD_END
-};
-
-DEFINE_CLASS_0(bhnd_mdio_pcie, bhnd_mdio_pcie_driver, bhnd_mdio_pcie_methods, sizeof(struct bhnd_mdio_pcie_softc));
Index: sys/dev/bhnd/cores/pci/mdio_pciereg.h
===================================================================
--- sys/dev/bhnd/cores/pci/mdio_pciereg.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/*-
- * Copyright (c) 2015 Landon Fuller <landon@landonf.org>
- * Copyright (c) 2010 Broadcom Corporation
- * All rights reserved.
- *
- * This file is derived from the pcie_core.h header distributed with Broadcom's
- * initial brcm80211 Linux driver release, as contributed to the Linux staging
- * repository.
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
- * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
- * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
- * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- * $FreeBSD$
- */
-
-#ifndef _BHND_CORES_PCI_MDIO_PCIEREG_H_
-#define _BHND_CORES_PCI_MDIO_PCIEREG_H_
-
-/* MDIO register offsets */
-#define BHND_MDIO_CTL 0x0 /**< mdio control */
-#define BHND_MDIO_DATA 0x4 /**< mdio data */
-
-/* MDIO control */
-#define BHND_MDIOCTL_DIVISOR_MASK 0x7f /* clock divisor mask */
-#define BHND_MDIOCTL_DIVISOR_VAL 0x2 /* default clock divisor */
-#define BHND_MDIOCTL_PREAM_EN 0x80 /* enable preamble mode */
-#define BHND_MDIOCTL_DONE 0x100 /* tranaction completed */
-
-/* MDIO Data */
-#define BHND_MDIODATA_PHYADDR_MASK 0x0f800000 /* phy addr */
-#define BHND_MDIODATA_PHYADDR_SHIFT 23
-#define BHND_MDIODATA_REGADDR_MASK 0x007c0000 /* reg/dev addr */
-#define BHND_MDIODATA_REGADDR_SHIFT 18
-#define BHND_MDIODATA_DATA_MASK 0x0000ffff /* data */
-
-#define BHND_MDIODATA_TA 0x00020000 /* slave turnaround time */
-#define BHND_MDIODATA_START 0x40000000 /* start of transaction */
-#define BHND_MDIODATA_CMD_WRITE 0x10000000 /* write command */
-#define BHND_MDIODATA_CMD_READ 0x20000000 /* read command */
-
-#define BHND_MDIODATA_ADDR(_phyaddr, _regaddr) ( \
- (((_phyaddr) << BHND_MDIODATA_PHYADDR_SHIFT) & \
- BHND_MDIODATA_PHYADDR_MASK) | \
- (((_regaddr) << BHND_MDIODATA_REGADDR_SHIFT) & \
- BHND_MDIODATA_REGADDR_MASK) \
-)
-
-#endif /* _BHND_CORES_PCI_MDIO_PCIEREG_H_ */
Index: sys/dev/bhnd/cores/pci/mdio_pcievar.h
===================================================================
--- sys/dev/bhnd/cores/pci/mdio_pcievar.h
+++ /dev/null
@@ -1,69 +0,0 @@
-/*-
- * Copyright (c) 2015 Landon Fuller <landon@landonf.org>
- * All rights reserved.
- *
- * 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.
- *
- * NO WARRANTY
- * 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_CORES_PCI_MDIO_PCIEVAR_H_
-#define _BHND_CORES_PCI_MDIO_PCIEVAR_H_
-
-#include <dev/mdio/mdio.h>
-#include "mdio_if.h"
-
-DECLARE_CLASS(bhnd_mdio_pcie_driver);
-
-int bhnd_mdio_pcie_attach(device_t dev, struct bhnd_resource *mem_res,
- int mem_rid, bus_size_t offset, bool c22ext);
-
-struct bhnd_mdio_pcie_softc {
- device_t dev; /**< mdio device */
- struct mtx sc_mtx; /**< mdio register lock */
-
- struct bhnd_resource *mem_res; /**< parent pcie registers */
- int mem_rid; /**< MDIO register resID, or
- -1 if mem_res reference is
- borrowed. */
- bus_size_t mem_off; /**< mdio register offset */
-
- bool c22ext; /**< automatically rewrite C45
- register requests made
- to the PCIe SerDes slave
- to use its non-standard
- C22 address extension
- mechanism. */
-};
-
-#define BHND_MDIO_PCIE_LOCK_INIT(sc) \
- mtx_init(&(sc)->sc_mtx, device_get_nameunit((sc)->dev), \
- "bhnd_pci_mdio register lock", MTX_DEF)
-#define BHND_MDIO_PCIE_LOCK(sc) mtx_lock(&(sc)->sc_mtx)
-#define BHND_MDIO_PCIE_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx)
-#define BHND_MDIO_PCIE_LOCK_ASSERT(sc, what) mtx_assert(&(sc)->sc_mtx, what)
-#define BHND_MDIO_PCIE_LOCK_DESTROY(sc) mtx_destroy(&(sc)->sc_mtx)
-
-#endif /* _BHND_CORES_PCI_MDIO_PCIEVAR_H_ */
Index: sys/dev/bhnd/siba/siba_bhndb.c
===================================================================
--- sys/dev/bhnd/siba/siba_bhndb.c
+++ sys/dev/bhnd/siba/siba_bhndb.c
@@ -45,6 +45,17 @@
* Supports attachment of siba(4) bus devices via a bhndb bridge.
*/
+//
+// TODO: PCI rev < 6 interrupt handling
+//
+// On early PCI cores (rev < 6) interrupt masking is handled via interconnect
+// configuration registers (SBINTVEC), rather than the PCI_INT_MASK
+// config register.
+//
+// On those devices, we should handle interrupts locally using SBINTVEC, rather
+// than delegating to our parent bhndb device.
+//
+
static int
siba_bhndb_probe(device_t dev)
{
Index: sys/modules/bhnd/cores/bhnd_pci/Makefile
===================================================================
--- sys/modules/bhnd/cores/bhnd_pci/Makefile
+++ sys/modules/bhnd/cores/bhnd_pci/Makefile
@@ -3,9 +3,7 @@
.PATH: ${.CURDIR}/../../../../dev/bhnd/cores/pci
KMOD= bhnd_pci
-SRCS= bhnd_pci.c \
- mdio_pcie.c
-SRCS+= device_if.h bus_if.h bhnd_bus_if.h \
- mdio_if.h
+SRCS= bhnd_pci.c
+SRCS+= device_if.h bus_if.h bhnd_bus_if.h
.include <bsd.kmod.mk>
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sat, Apr 18, 11:45 PM (18 h, 27 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
31738496
Default Alt Text
D5763.id14661.diff (97 KB)
Attached To
Mode
D5763: Add a common bhnd_pci driver shared by both bhnd_pcib and bhnd_pci_hostb
Attached
Detach File
Event Timeline
Log In to Comment