Index: head/sys/dev/bhnd/bhndb/bhndb_pci.c =================================================================== --- head/sys/dev/bhnd/bhndb/bhndb_pci.c (revision 298478) +++ head/sys/dev/bhnd/bhndb/bhndb_pci.c (revision 298479) @@ -1,1085 +1,469 @@ /*- - * Copyright (c) 2015 Landon Fuller + * Copyright (c) 2015-2016 Landon Fuller * 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 __FBSDID("$FreeBSD$"); /* * PCI-specific implementation for the BHNDB bridge driver. * * Provides support for bridging from a PCI parent bus to a BHND-compatible * 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 #include #include #include #include #include #include #include #include #include #include -#include #include "bhndb_pcireg.h" #include "bhndb_pcivar.h" #include "bhndb_private.h" static int bhndb_enable_pci_clocks(struct bhndb_pci_softc *sc); static int bhndb_disable_pci_clocks(struct bhndb_pci_softc *sc); static int bhndb_pci_compat_setregwin(struct bhndb_pci_softc *, const struct bhndb_regwin *, bhnd_addr_t); static int bhndb_pci_fast_setregwin(struct bhndb_pci_softc *, const struct bhndb_regwin *, bhnd_addr_t); -static 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(). * * Verifies that the parent is a PCI/PCIe device. */ static int bhndb_pci_probe(device_t dev) { device_t parent; devclass_t parent_bus; devclass_t pci; /* Our parent must be a PCI/PCIe device. */ pci = devclass_find("pci"); parent = device_get_parent(dev); parent_bus = device_get_devclass(device_get_parent(parent)); if (parent_bus != pci) return (ENXIO); device_set_desc(dev, "PCI-BHND bridge"); return (BUS_PROBE_DEFAULT); } static int bhndb_pci_attach(device_t dev) { struct bhndb_pci_softc *sc; int error, reg; sc = device_get_softc(dev); sc->dev = dev; /* Enable PCI bus mastering */ pci_enable_busmaster(device_get_parent(dev)); /* Determine our bridge device class */ sc->pci_devclass = BHND_DEVCLASS_PCI; 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 * what kind of bus is attached */ sc->set_regwin = bhndb_pci_compat_setregwin; /* Perform full bridge attach. This should call back into our * bhndb_pci_init_full_config() implementation once the bridged * bhnd(4) bus has been enumerated, but before any devices have been * probed or attached. */ if ((error = bhndb_attach(dev, sc->pci_devclass))) return (error); /* If supported, switch to the faster regwin handling */ if (sc->bhndb.chipid.chip_type != BHND_CHIPTYPE_SIBA) { atomic_store_rel_ptr((volatile void *) &sc->set_regwin, (uintptr_t) &bhndb_pci_fast_setregwin); } 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))) + /* Let our parent perform standard initialization first */ + if ((error = bhndb_generic_init_full_config(dev, child, hw_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")); + /* Fix-up power on defaults for SROM-less devices. */ + bhndb_init_sromless_pci_config(sc); - 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))) - return (error); - return (0); } -/** - * Apply any hardware workarounds that must be executed prior to attempting - * register access on the bridged chipset. +/* + * On devices without a SROM, the PCI(e) cores will be initialized with + * their Power-on-Reset defaults; this can leave two of the BAR0 PCI windows + * mapped to the wrong core. * - * 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 function updates the SROM shadow to point the BAR0 windows at the + * current PCI core. * - * 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. + * Applies to all PCI/PCIe revisions. */ -static int -bhndb_pci_wars_early_once(struct bhndb_pci_softc *sc) +static void +bhndb_init_sromless_pci_config(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; + 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; - 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; - } + if (bhnd_get_vendor(sc->bhndb.hostb_dev) != BHND_MFGID_BCM) + return; - 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 */ - 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); + 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; } - /* 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); + /* 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; } - - /* 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); + /* 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; } - /* 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. - * - * This function updates the PCI core's BAR0 PCI configuration to point 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. - */ -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; - /* 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))) + + /* Enable clocks (if supported by this hardware) */ + if ((error = bhndb_enable_pci_clocks(sc))) 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))) - return (error); - - /* Disable PCI bus mastering */ - pci_disable_busmaster(device_get_parent(dev)); - - return (0); + /* Perform resume */ + return (bhndb_generic_resume(dev)); } static int bhndb_pci_suspend(device_t dev) { struct bhndb_pci_softc *sc; int error; sc = device_get_softc(dev); - - if ((error = bhndb_generic_suspend(dev))) + + /* Disable clocks (if supported by this hardware) */ + if ((error = bhndb_disable_pci_clocks(sc))) 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))) - 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); } static int bhndb_pci_set_window_addr(device_t dev, const struct bhndb_regwin *rw, bhnd_addr_t addr) { struct bhndb_pci_softc *sc = device_get_softc(dev); return (sc->set_regwin(sc, rw, addr)); } /** * A siba(4) and bcma(4)-compatible bhndb_set_window_addr implementation. * * On siba(4) devices, it's possible that writing a PCI window register may * not succeed; it's necessary to immediately read the configuration register * and retry if not set to the desired value. * * This is not necessary on bcma(4) devices, but other than the overhead of * validating the register, there's no harm in performing the verification. */ static int bhndb_pci_compat_setregwin(struct bhndb_pci_softc *sc, const struct bhndb_regwin *rw, bhnd_addr_t addr) { device_t parent; int error; parent = sc->bhndb.parent_dev; if (rw->win_type != BHNDB_REGWIN_T_DYN) return (ENODEV); for (u_int i = 0; i < BHNDB_PCI_BARCTRL_WRITE_RETRY; i++) { if ((error = bhndb_pci_fast_setregwin(sc, rw, addr))) return (error); if (pci_read_config(parent, rw->dyn.cfg_offset, 4) == addr) return (0); DELAY(10); } /* Unable to set window */ return (ENODEV); } /** * A bcma(4)-only bhndb_set_window_addr implementation. */ static int bhndb_pci_fast_setregwin(struct bhndb_pci_softc *sc, const struct bhndb_regwin *rw, bhnd_addr_t addr) { device_t parent = sc->bhndb.parent_dev; /* The PCI bridge core only supports 32-bit addressing, regardless * of the bus' support for 64-bit addressing */ if (addr > UINT32_MAX) return (ERANGE); switch (rw->win_type) { case BHNDB_REGWIN_T_DYN: /* Addresses must be page aligned */ if (addr % rw->win_size != 0) return (EINVAL); pci_write_config(parent, rw->dyn.cfg_offset, addr, 4); break; default: return (ENODEV); } 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. + * 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 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 - * * @param sc Bridge driver state. */ static int bhndb_enable_pci_clocks(struct bhndb_pci_softc *sc) { device_t pci_parent; uint32_t gpio_in, gpio_out, gpio_en; 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); /* Read state of XTAL pin */ gpio_in = pci_read_config(pci_parent, BHNDB_PCI_GPIO_IN, 4); if (gpio_in & BHNDB_PCI_GPIO_XTAL_ON) return (0); /* already enabled */ /* Fetch current config */ gpio_out = pci_read_config(pci_parent, BHNDB_PCI_GPIO_OUT, 4); gpio_en = pci_read_config(pci_parent, BHNDB_PCI_GPIO_OUTEN, 4); /* Set PLL_OFF/XTAL_ON pins to HIGH and enable both pins */ gpio_flags = (BHNDB_PCI_GPIO_PLL_OFF|BHNDB_PCI_GPIO_XTAL_ON); gpio_out |= gpio_flags; gpio_en |= gpio_flags; pci_write_config(pci_parent, BHNDB_PCI_GPIO_OUT, gpio_out, 4); pci_write_config(pci_parent, BHNDB_PCI_GPIO_OUTEN, gpio_en, 4); DELAY(1000); /* Reset PLL_OFF */ gpio_out &= ~BHNDB_PCI_GPIO_PLL_OFF; pci_write_config(pci_parent, BHNDB_PCI_GPIO_OUT, gpio_out, 4); DELAY(5000); /* Clear any PCI 'sent target-abort' flag. */ pci_status = pci_read_config(pci_parent, PCIR_STATUS, 2); pci_status &= ~PCIM_STATUS_STABORT; pci_write_config(pci_parent, PCIR_STATUS, pci_status, 2); return (0); } /** - * Disable externally managed clocks. + * Disable externally managed clocks, if required. * - * Quirk Required: EXT_CLOCK_GATING - * * @param sc Bridge driver state. */ static int bhndb_disable_pci_clocks(struct bhndb_pci_softc *sc) { 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); // TODO: Check board flags for BFL2_XTALBUFOUTEN? // TODO: Check PCI core revision? // TODO: Switch to 'slow' clock? /* Fetch current config */ gpio_out = pci_read_config(parent_dev, BHNDB_PCI_GPIO_OUT, 4); gpio_en = pci_read_config(parent_dev, BHNDB_PCI_GPIO_OUTEN, 4); /* Set PLL_OFF to HIGH, XTAL_ON to LOW. */ gpio_out &= ~BHNDB_PCI_GPIO_XTAL_ON; gpio_out |= BHNDB_PCI_GPIO_PLL_OFF; pci_write_config(parent_dev, BHNDB_PCI_GPIO_OUT, gpio_out, 4); /* Enable both output pins */ gpio_en |= (BHNDB_PCI_GPIO_PLL_OFF|BHNDB_PCI_GPIO_XTAL_ON); pci_write_config(parent_dev, BHNDB_PCI_GPIO_OUTEN, gpio_en, 4); 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), DEVMETHOD(bhndb_set_window_addr, bhndb_pci_set_window_addr), DEVMETHOD_END }; 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); MODULE_DEPEND(bhndb_pci, bhndb, 1, 1, 1); Index: head/sys/dev/bhnd/bhndb/bhndb_pcivar.h =================================================================== --- head/sys/dev/bhnd/bhndb/bhndb_pcivar.h (revision 298478) +++ head/sys/dev/bhnd/bhndb/bhndb_pcivar.h (revision 298479) @@ -1,242 +1,58 @@ /*- * Copyright (c) 2015 Landon Fuller * 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_BHNDB_PCIVAR_H_ #define _BHND_BHNDB_PCIVAR_H_ -#include - -#include - #include "bhndbvar.h" /* * bhndb(4) PCI driver subclass. */ 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 */ - - 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: head/sys/dev/bhnd/cores/chipc/chipc.c =================================================================== --- head/sys/dev/bhnd/cores/chipc/chipc.c (revision 298478) +++ head/sys/dev/bhnd/cores/chipc/chipc.c (revision 298479) @@ -1,309 +1,309 @@ /*- * Copyright (c) 2015 Landon Fuller * 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 __FBSDID("$FreeBSD$"); /* * Broadcom ChipCommon driver. * * With the exception of some very early chipsets, the ChipCommon core * has been included in all HND SoCs and chipsets based on the siba(4) * and bcma(4) interconnects, providing a common interface to chipset * identification, bus enumeration, UARTs, clocks, watchdog interrupts, GPIO, * flash, etc. */ #include #include #include #include #include #include #include #include #include #include "chipcreg.h" #include "chipcvar.h" devclass_t bhnd_chipc_devclass; /**< bhnd(4) chipcommon device class */ static const struct resource_spec chipc_rspec[CHIPC_MAX_RSPEC] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { -1, -1, 0 } }; static struct bhnd_device_quirk chipc_quirks[]; /* Supported device identifiers */ static const struct bhnd_device chipc_devices[] = { - BHND_DEVICE(CC, "", chipc_quirks), + BHND_DEVICE(CC, NULL, chipc_quirks), BHND_DEVICE_END }; /* Device quirks table */ static struct bhnd_device_quirk chipc_quirks[] = { { BHND_HWREV_RANGE (0, 21), CHIPC_QUIRK_ALWAYS_HAS_SPROM }, { BHND_HWREV_EQ (22), CHIPC_QUIRK_SPROM_CHECK_CST_R22 }, { BHND_HWREV_RANGE (23, 31), CHIPC_QUIRK_SPROM_CHECK_CST_R23 }, { BHND_HWREV_GTE (35), CHIPC_QUIRK_SUPPORTS_NFLASH }, BHND_DEVICE_QUIRK_END }; /* quirk and capability flag convenience macros */ #define CHIPC_QUIRK(_sc, _name) \ ((_sc)->quirks & CHIPC_QUIRK_ ## _name) #define CHIPC_CAP(_sc, _name) \ ((_sc)->caps & CHIPC_ ## _name) #define CHIPC_ASSERT_QUIRK(_sc, name) \ KASSERT(CHIPC_QUIRK((_sc), name), ("quirk " __STRING(_name) " not set")) #define CHIPC_ASSERT_CAP(_sc, name) \ KASSERT(CHIPC_CAP((_sc), name), ("capability " __STRING(_name) " not set")) static int chipc_probe(device_t dev) { const struct bhnd_device *id; id = bhnd_device_lookup(dev, chipc_devices, sizeof(chipc_devices[0])); if (id == NULL) return (ENXIO); bhnd_set_default_core_desc(dev); return (BUS_PROBE_DEFAULT); } static int chipc_attach(device_t dev) { struct chipc_softc *sc; bhnd_addr_t enum_addr; uint32_t ccid_reg; uint8_t chip_type; int error; sc = device_get_softc(dev); sc->dev = dev; sc->quirks = bhnd_device_quirks(dev, chipc_devices, sizeof(chipc_devices[0])); /* Allocate bus resources */ memcpy(sc->rspec, chipc_rspec, sizeof(sc->rspec)); if ((error = bhnd_alloc_resources(dev, sc->rspec, sc->res))) return (error); sc->core = sc->res[0]; /* Fetch our chipset identification data */ ccid_reg = bhnd_bus_read_4(sc->core, CHIPC_ID); chip_type = CHIPC_GET_ATTR(ccid_reg, ID_BUS); switch (chip_type) { case BHND_CHIPTYPE_SIBA: /* enumeration space starts at the ChipCommon register base. */ enum_addr = rman_get_start(sc->core->res); break; case BHND_CHIPTYPE_BCMA: case BHND_CHIPTYPE_BCMA_ALT: enum_addr = bhnd_bus_read_4(sc->core, CHIPC_EROMPTR); break; default: device_printf(dev, "unsupported chip type %hhu\n", chip_type); error = ENODEV; goto cleanup; } sc->ccid = bhnd_parse_chipid(ccid_reg, enum_addr); /* Fetch capability and status register values */ sc->caps = bhnd_bus_read_4(sc->core, CHIPC_CAPABILITIES); sc->cst = bhnd_bus_read_4(sc->core, CHIPC_CHIPST); // TODO switch (bhnd_chipc_nvram_src(dev)) { case BHND_NVRAM_SRC_CIS: device_printf(dev, "NVRAM source: CIS\n"); break; case BHND_NVRAM_SRC_SPROM: device_printf(dev, "NVRAM source: SPROM\n"); break; case BHND_NVRAM_SRC_OTP: device_printf(dev, "NVRAM source: OTP\n"); break; case BHND_NVRAM_SRC_NFLASH: device_printf(dev, "NVRAM source: NFLASH\n"); break; case BHND_NVRAM_SRC_NONE: device_printf(dev, "NVRAM source: NONE\n"); break; } return (0); cleanup: bhnd_release_resources(dev, sc->rspec, sc->res); return (error); } static int chipc_detach(device_t dev) { struct chipc_softc *sc; sc = device_get_softc(dev); bhnd_release_resources(dev, sc->rspec, sc->res); return (0); } static int chipc_suspend(device_t dev) { return (0); } static int chipc_resume(device_t dev) { return (0); } /** * Use device-specific ChipStatus flags to determine the preferred NVRAM * data source. */ static bhnd_nvram_src_t chipc_nvram_src_chipst(struct chipc_softc *sc) { uint8_t nvram_sel; CHIPC_ASSERT_QUIRK(sc, SPROM_CHECK_CHIPST); if (CHIPC_QUIRK(sc, SPROM_CHECK_CST_R22)) { // TODO: On these devices, the official driver code always // assumes SPROM availability if CHIPC_CST_OTP_SEL is not // set; we must review against the actual behavior of our // BCM4312 hardware nvram_sel = CHIPC_GET_ATTR(sc->cst, CST_SPROM_OTP_SEL_R22); } else if (CHIPC_QUIRK(sc, SPROM_CHECK_CST_R23)) { nvram_sel = CHIPC_GET_ATTR(sc->cst, CST_SPROM_OTP_SEL_R23); } else { panic("invalid CST OTP/SPROM chipc quirk flags"); } device_printf(sc->dev, "querying chipst for 0x%x, 0x%x\n", sc->ccid.chip_id, sc->cst); switch (nvram_sel) { case CHIPC_CST_DEFCIS_SEL: return (BHND_NVRAM_SRC_CIS); case CHIPC_CST_SPROM_SEL: case CHIPC_CST_OTP_PWRDN: return (BHND_NVRAM_SRC_SPROM); case CHIPC_CST_OTP_SEL: return (BHND_NVRAM_SRC_OTP); default: device_printf(sc->dev, "unrecognized OTP/SPROM type 0x%hhx", nvram_sel); return (BHND_NVRAM_SRC_NONE); } } /** * Determine the preferred NVRAM data source. */ static bhnd_nvram_src_t chipc_nvram_src(device_t dev) { struct chipc_softc *sc; uint32_t srom_ctrl; sc = device_get_softc(dev); /* Very early devices always included a SPROM */ if (CHIPC_QUIRK(sc, ALWAYS_HAS_SPROM)) return (BHND_NVRAM_SRC_SPROM); /* Most other early devices require checking ChipStatus flags */ if (CHIPC_QUIRK(sc, SPROM_CHECK_CHIPST)) return (chipc_nvram_src_chipst(sc)); /* * Later chipset revisions standardized the NVRAM capability flags and * register interfaces. * * We check for hardware presence in order of precedence. For example, * SPROM is is always used in preference to internal OTP if found. */ if (CHIPC_CAP(sc, CAP_SPROM)) { srom_ctrl = bhnd_bus_read_4(sc->core, CHIPC_SPROM_CTRL); if (srom_ctrl & CHIPC_SRC_PRESENT) return (BHND_NVRAM_SRC_SPROM); } /* Check for OTP */ if (CHIPC_CAP(sc, CAP_OTP_SIZE)) return (BHND_NVRAM_SRC_OTP); /* * Finally, Northstar chipsets (and possibly other chipsets?) support * external NAND flash. */ if (CHIPC_QUIRK(sc, SUPPORTS_NFLASH) && CHIPC_CAP(sc, CAP_NFLASH)) return (BHND_NVRAM_SRC_NFLASH); /* No NVRAM hardware capability declared */ return (BHND_NVRAM_SRC_NONE); } static device_method_t chipc_methods[] = { /* Device interface */ DEVMETHOD(device_probe, chipc_probe), DEVMETHOD(device_attach, chipc_attach), DEVMETHOD(device_detach, chipc_detach), DEVMETHOD(device_suspend, chipc_suspend), DEVMETHOD(device_resume, chipc_resume), /* ChipCommon interface */ DEVMETHOD(bhnd_chipc_nvram_src, chipc_nvram_src), DEVMETHOD_END }; DEFINE_CLASS_0(bhnd_chipc, chipc_driver, chipc_methods, sizeof(struct chipc_softc)); DRIVER_MODULE(bhnd_chipc, bhnd, chipc_driver, bhnd_chipc_devclass, 0, 0); MODULE_VERSION(bhnd_chipc, 1); Index: head/sys/dev/bhnd/cores/pci/mdio_pcie.c =================================================================== --- head/sys/dev/bhnd/cores/pci/mdio_pcie.c (revision 298478) +++ head/sys/dev/bhnd/cores/pci/mdio_pcie.c (nonexistent) @@ -1,384 +0,0 @@ -/*- - * Copyright (c) 2015 Landon Fuller - * 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 -__FBSDID("$FreeBSD$"); - -/* - * MDIO Driver for PCIe-G1 Cores (All Revisions). - * - * The MDIO interface provides access to the PCIe SerDes management registers. - */ - -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -#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)); Property changes on: head/sys/dev/bhnd/cores/pci/mdio_pcie.c ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: head/sys/dev/bhnd/cores/pci/mdio_pcievar.h =================================================================== --- head/sys/dev/bhnd/cores/pci/mdio_pcievar.h (revision 298478) +++ head/sys/dev/bhnd/cores/pci/mdio_pcievar.h (nonexistent) @@ -1,69 +0,0 @@ -/*- - * Copyright (c) 2015 Landon Fuller - * 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 -#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_ */ Property changes on: head/sys/dev/bhnd/cores/pci/mdio_pcievar.h ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: head/sys/dev/bhnd/cores/pci/mdio_pciereg.h =================================================================== --- head/sys/dev/bhnd/cores/pci/mdio_pciereg.h (revision 298478) +++ head/sys/dev/bhnd/cores/pci/mdio_pciereg.h (nonexistent) @@ -1,57 +0,0 @@ -/*- - * Copyright (c) 2015 Landon Fuller - * 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_ */ Property changes on: head/sys/dev/bhnd/cores/pci/mdio_pciereg.h ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: head/sys/dev/bhnd/cores/pci/bhnd_pci.c =================================================================== --- head/sys/dev/bhnd/cores/pci/bhnd_pci.c (revision 298478) +++ head/sys/dev/bhnd/cores/pci/bhnd_pci.c (revision 298479) @@ -1,52 +1,548 @@ /*- * Copyright (c) 2015 Landon Fuller * 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 __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 +#include #include +#include #include +#include +#include +#include +#include + +#include +#include + +#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)); +} + /** - * PCIe MDIO interface device class + * Read a 32-bit PCIe TLP/DLLP/PLP protocol register. + * + * @param sc The bhndb_pci driver state. + * @param addr The protocol register offset. */ -devclass_t bhnd_mdio_pci_devclass; +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)); +} + +/** + * Disable MDIO device. + */ +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: head/sys/dev/bhnd/cores/pci/bhnd_pci_hostb.c =================================================================== --- head/sys/dev/bhnd/cores/pci/bhnd_pci_hostb.c (revision 298478) +++ head/sys/dev/bhnd/cores/pci/bhnd_pci_hostb.c (revision 298479) @@ -1,117 +1,452 @@ /*- * Copyright (c) 2015 Landon Fuller * 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 __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 #include + +#include + #include #include + #include #include #include #include #include -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 +MODULE_DEPEND(bhnd_pci_hostb, bhnd_pci, 1, 1, 1); Index: head/sys/dev/bhnd/cores/pci/bhnd_pci_hostbvar.h =================================================================== --- head/sys/dev/bhnd/cores/pci/bhnd_pci_hostbvar.h (nonexistent) +++ head/sys/dev/bhnd/cores/pci/bhnd_pci_hostbvar.h (revision 298479) @@ -0,0 +1,194 @@ +/*- + * Copyright (c) 2015-2016 Landon Fuller + * 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_BHND_PCI_HOSTBVAR_H_ +#define _BHND_CORES_PCI_BHND_PCI_HOSTBVAR_H_ + +/* + * PCI/PCIe-Gen1 Host Bridge definitions. + */ + +#include +#include + +#include "bhnd_pcivar.h" + +DECLARE_CLASS(bhnd_pci_hostb_driver); + +/* + * PCI/PCIe-Gen1 endpoint-mode device quirks + */ +enum { + /** No quirks */ + BHND_PCI_QUIRK_NONE = 0, + + /** + * SBTOPCI_PREF and SBTOPCI_BURST must be set on the + * SSB_PCICORE_SBTOPCI2 register. + */ + BHND_PCI_QUIRK_SBTOPCI2_PREF_BURST = (1<<1), + + + /** + * SBTOPCI_RC_READMULTI must be set on the SSB_PCICORE_SBTOPCI2 + * register. + */ + BHND_PCI_QUIRK_SBTOPCI2_READMULTI = (1<<2), + + /** + * 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. + */ + BHND_PCI_QUIRK_CLKRUN_DSBL = (1<<3), + + /** + * 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. + */ + BHND_PCIE_QUIRK_UR_STATUS_FIX = (1<<4), + + /** + * PCI-PM power management must be explicitly enabled via + * the data link control register. + */ + BHND_PCIE_QUIRK_PCIPM_REQEN = (1<<5), + + /** + * 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. + */ + 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. + */ + 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. + */ + BHND_PCIE_QUIRK_L1_TIMER_PERF = (1<<8), + + /** + * 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. + */ + BHND_PCIE_QUIRK_ASPM_OVR = (1<<9), + + /** + * Fix SerDes polarity on SerDes <= rev9 devices. + * + * The SerDes polarity must be saved at device attachment, and + * restored on suspend/resume. + */ + BHND_PCIE_QUIRK_SDR9_POLARITY = (1<<10), + + /** + * SerDes PLL down flag must be manually disabled (by ChipCommon) on + * resume. + */ + BHND_PCIE_QUIRK_SERDES_NOPLLDOWN = (1<<11), + + /** + * 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 + */ + BHND_PCIE_QUIRK_SPROM_L23_PCI_RESET = (1<<12), + + /** + * 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. + */ + BHND_PCIE_QUIRK_SD_C22_EXTADDR = (1<<13), + + /** + * 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. + */ + 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_CORES_PCI_BHND_PCI_HOSTBVAR_H_ */ \ No newline at end of file Property changes on: head/sys/dev/bhnd/cores/pci/bhnd_pci_hostbvar.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/dev/bhnd/cores/pci/bhnd_pcib.c =================================================================== --- head/sys/dev/bhnd/cores/pci/bhnd_pcib.c (revision 298478) +++ head/sys/dev/bhnd/cores/pci/bhnd_pcib.c (revision 298479) @@ -1,127 +1,94 @@ /*- * Copyright (c) 2015 Landon Fuller * 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 __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 #include #include #include #include #include #include #include #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), DEVMETHOD(device_resume, bhnd_pcib_resume), 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); MODULE_DEPEND(bhnd_pcib, pci, 1, 1, 1); MODULE_DEPEND(bhnd_pcib, bhnd_pci_mdio, 1, 1, 1); Index: head/sys/dev/bhnd/cores/pci/bhnd_pcibvar.h =================================================================== --- head/sys/dev/bhnd/cores/pci/bhnd_pcibvar.h (revision 298478) +++ head/sys/dev/bhnd/cores/pci/bhnd_pcibvar.h (revision 298479) @@ -1,50 +1,42 @@ /*- * Copyright (c) 2015 Landon Fuller * 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_BHND_PCIBVAR_H_ #define _BHND_CORES_PCI_BHND_PCIBVAR_H_ #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: head/sys/dev/bhnd/cores/pci/bhnd_pcireg.h =================================================================== --- head/sys/dev/bhnd/cores/pci/bhnd_pcireg.h (revision 298478) +++ head/sys/dev/bhnd/cores/pci/bhnd_pcireg.h (revision 298479) @@ -1,387 +1,412 @@ /*- * Copyright (c) 2015 Landon Fuller * Copyright (c) 2010 Broadcom Corporation * All rights reserved. * * This file is derived from the hndsoc.h, pci_core.h, and pcie_core.h headers * 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_BHND_PCIREG_H_ #define _BHND_CORES_PCI_BHND_PCIREG_H_ /* * PCI/PCIe-Gen1 DMA Constants */ #define BHND_PCI_DMA32_TRANSLATION 0x40000000 /* Client Mode sb2pcitranslation2 (1 GB) */ #define BHND_PCI_DMA32_SZ 0x40000000 /* Client Mode sb2pcitranslation2 size in bytes */ #define BHND_PCIE_DMA32_TRANSLATION BHND_PCI_DMA32_TRANSLATION #define BHND_PCIE_DMA32_SZ BHND_PCI_DMA32_SZ #define BHND_PCIE_DMA64_L32 0x00000000 /**< 64-bit client mode sb2pcitranslation2 (2 ZettaBytes, low 32 bits) */ #define BHND_PCIE_DMA64_H32 0x80000000 /**< 64-bit client mode sb2pcitranslation2 (2 ZettaBytes, high 32 bits) */ /* * PCI Core Registers */ #define BHND_PCI_CTL 0x000 /**< PCI core control*/ #define BHND_PCI_ARB_CTL 0x010 /**< PCI arbiter control */ #define BHND_PCI_CLKRUN_CTL 0x014 /**< PCI clckrun control (>= rev11) */ #define BHND_PCI_INTR_STATUS 0x020 /**< Interrupt status */ #define BHND_PCI_INTR_MASK 0x024 /**< Interrupt mask */ #define BHND_PCI_SBTOPCI_MBOX 0x028 /**< Sonics to PCI mailbox */ #define BHND_PCI_BCAST_ADDR 0x050 /**< Sonics broadcast address (pci) */ #define BHND_PCI_BCAST_DATA 0x054 /**< Sonics broadcast data (pci) */ #define BHND_PCI_GPIO_IN 0x060 /**< GPIO input (>= rev2) */ #define BHND_PCI_GPIO_OUT 0x064 /**< GPIO output (>= rev2) */ #define BHND_PCI_GPIO_EN 0x068 /**< GPIO output enable (>= rev2) */ #define BHND_PCI_GPIO_CTL 0x06C /**< GPIO control (>= rev2) */ #define BHND_PCI_SBTOPCI0 0x100 /**< Sonics to PCI translation 0 */ #define BHND_PCI_SBTOPCI1 0x104 /**< Sonics to PCI translation 1 */ #define BHND_PCI_SBTOPCI2 0x108 /**< Sonics to PCI translation 2 */ #define BHND_PCI_FUNC0_CFG 0x400 /**< PCI function 0 cfg space (>= rev8) */ #define BHND_PCI_FUNC1_CFG 0x500 /**< PCI function 1 cfg space (>= rev8) */ #define BHND_PCI_FUNC2_CFG 0x600 /**< PCI function 2 cfg space (>= rev8) */ #define BHND_PCI_FUNC3_CFG 0x700 /**< PCI function 3 cfg space (>= rev8) */ #define BHND_PCI_SPROM_SHADOW 0x800 /**< PCI SPROM shadow */ /* BHND_PCI_CTL */ #define BHND_PCI_CTL_RST_OE 0x01 /* When set, drives PCI_RESET out to pin */ #define BHND_PCI_CTL_RST 0x02 /* Value driven out to pin */ #define BHND_PCI_CTL_CLK_OE 0x04 /* When set, drives clock as gated by PCI_CLK out to pin */ #define BHND_PCI_CTL_CLK 0x08 /* Gate for clock driven out to pin */ /* BHND_PCI_ARB_CTL */ #define BHND_PCI_ARB_INT 0x01 /* When set, use an internal arbiter */ #define BHND_PCI_ARB_EXT 0x02 /* When set, use an external arbiter */ /* BHND_PCI_ARB_CTL - ParkID (>= rev8) */ #define BHND_PCI_ARB_PARKID_MASK 0x1c /* Selects which agent is parked on an idle bus */ #define BHND_PCI_ARB_PARKID_SHIFT 2 #define BHND_PCI_ARB_PARKID_EXT0 0 /* External master 0 */ #define BHND_PCI_ARB_PARKID_EXT1 1 /* External master 1 */ #define BHND_PCI_ARB_PARKID_EXT2 2 /* External master 2 */ #define BHND_PCI_ARB_PARKID_EXT3 3 /* External master 3 (rev >= 11) */ #define BHND_PCI_ARB_PARKID_INT_r10 3 /* Internal master (rev < 11) */ #define BHND_PCI_ARB_PARKID_INT_r11 4 /* Internal master (rev >= 11) */ #define BHND_PCI_ARB_PARKID_LAST_r10 4 /* Last active master (rev < 11) */ #define BHND_PCI_ARB_PARKID_LAST_r11 5 /* Last active master (rev >= 11) */ /* BHND_PCI_CLKRUN_CTL */ #define BHND_PCI_CLKRUN_DSBL 0x8000 /* Bit 15 forceClkrun */ /* BHND_PCI_INTR_STATUS / BHND_PCI_INTR_MASK */ #define BHND_PCI_INTR_A 0x01 /* PCI INTA# is asserted */ #define BHND_PCI_INTR_B 0x02 /* PCI INTB# is asserted */ #define BHND_PCI_INTR_SERR 0x04 /* PCI SERR# has been asserted (write one to clear) */ #define BHND_PCI_INTR_PERR 0x08 /* PCI PERR# has been asserted (write one to clear) */ /* BHND_PCI_SBTOPCI_MBOX * (General) PCI/SB mailbox interrupts, two bits per pci function */ #define BHND_PCI_SBTOPCI_MBOX_F0_0 0x100 /* function 0, int 0 */ #define BHND_PCI_SBTOPCI_MBOX_F0_1 0x200 /* function 0, int 1 */ #define BHND_PCI_SBTOPCI_MBOX_F1_0 0x400 /* function 1, int 0 */ #define BHND_PCI_SBTOPCI_MBOX_F1_1 0x800 /* function 1, int 1 */ #define BHND_PCI_SBTOPCI_MBOX_F2_0 0x1000 /* function 2, int 0 */ #define BHND_PCI_SBTOPCI_MBOX_F2_1 0x2000 /* function 2, int 1 */ #define BHND_PCI_SBTOPCI_MBOX_F3_0 0x4000 /* function 3, int 0 */ #define BHND_PCI_SBTOPCI_MBOX_F3_1 0x8000 /* function 3, int 1 */ /* BHND_PCI_BCAST_ADDR */ #define BHNC_PCI_BCAST_ADDR_MASK 0xFF /* Broadcast register address */ /* Sonics to PCI translation types */ #define BHND_PCI_SBTOPCI0_MASK 0xfc000000 #define BHND_PCI_SBTOPCI1_MASK 0xfc000000 #define BHND_PCI_SBTOPCI2_MASK 0xc0000000 /* Access type bits (0:1) */ #define BHND_PCI_SBTOPCI_MEM 0 #define BHND_PCI_SBTOPCI_IO 1 #define BHND_PCI_SBTOPCI_CFG0 2 #define BHND_PCI_SBTOPCI_CFG1 3 #define BHND_PCI_SBTOPCI_PREF 0x4 /* prefetch enable */ #define BHND_PCI_SBTOPCI_BURST 0x8 /* burst enable */ #define BHND_PCI_SBTOPCI_RC_MASK 0x30 /* read command (>= rev11) */ #define BHND_PCI_SBTOPCI_RC_READ 0x00 /* memory read */ #define BHND_PCI_SBTOPCI_RC_READLINE 0x10 /* memory read line */ #define BHND_PCI_SBTOPCI_RC_READMULTI 0x20 /* memory read multiple */ /* PCI core index in SROM shadow area */ #define BHND_PCI_SRSH_PI_OFFSET 0 /* first word */ #define BHND_PCI_SRSH_PI_MASK 0xf000 /* bit 15:12 */ #define BHND_PCI_SRSH_PI_SHIFT 12 /* bit 15:12 */ /* * PCIe-Gen1 Core Registers */ #define BHND_PCIE_CTL BHND_PCI_CTL /**< PCI core control*/ #define BHND_PCIE_BIST_STATUS 0x00C /**< BIST status */ #define BHND_PCIE_GPIO_SEL 0x010 /**< GPIO select */ #define BHND_PCIE_GPIO_OUT_EN 0x014 /**< GPIO output enable */ #define BHND_PCIE_INTR_STATUS BHND_PCI_INTR_STATUS /**< Interrupt status */ #define BHND_PCIE_INTR_MASK BHND_PCI_INTR_MASK /**< Interrupt mask */ #define BHND_PCIE_SBTOPCI_MBOX BHND_PCI_SBTOPCI_MBOX /**< Sonics to PCI mailbox */ #define BHND_PCIE_SBTOPCI0 BHND_PCI_SBTOPCI0 /**< Sonics to PCI translation 0 */ #define BHND_PCIE_SBTOPCI1 BHND_PCI_SBTOPCI1 /**< Sonics to PCI translation 1 */ #define BHND_PCIE_SBTOPCI2 BHND_PCI_SBTOPCI2 /**< Sonics to PCI translation 2 */ /* indirect pci config space access */ #define BHND_PCIE_CFG_ADDR 0x120 /**< pcie config space address */ #define BHND_PCIE_CFG_DATA 0x124 /**< pcie config space data */ /* mdio register access */ #define BHND_PCIE_MDIO_CTL 0x128 /**< mdio control */ #define BHND_PCIE_MDIO_DATA 0x12C /**< mdio data */ /* indirect protocol phy/dllp/tlp register access */ #define BHND_PCIE_IND_ADDR 0x130 /**< internal protocol register address */ #define BHND_PCIE_IND_DATA 0x134 /**< internal protocol register data */ #define BHND_PCIE_CLKREQEN_CTL 0x138 /**< clkreq rdma control */ #define BHND_PCIE_FUNC0_CFG BHND_PCI_FUNC0_CFG /**< PCI function 0 cfg space */ #define BHND_PCIE_FUNC1_CFG BHND_PCI_FUNC1_CFG /**< PCI function 1 cfg space */ #define BHND_PCIE_FUNC2_CFG BHND_PCI_FUNC2_CFG /**< PCI function 2 cfg space */ #define BHND_PCIE_FUNC3_CFG BHND_PCI_FUNC3_CFG /**< PCI function 3 cfg space */ #define BHND_PCIE_SPROM_SHADOW BHND_PCI_SPROM_SHADOW /**< PCI SPROM shadow */ /* BHND_PCIE_CTL */ #define BHND_PCIE_CTL_RST_OE BHND_PCI_CTL_RST_OE /* When set, drives PCI_RESET out to pin */ #define BHND_PCIE_CTL_RST BHND_PCI_CTL_RST_OE /* Value driven out to pin */ /* BHND_PCI_INTR_STATUS / BHND_PCI_INTR_MASK */ #define BHND_PCIE_INTR_A BHND_PCI_INTR_A /* PCIE INTA message is received */ #define BHND_PCIE_INTR_B BHND_PCI_INTR_B /* PCIE INTB message is received */ #define BHND_PCIE_INTR_FATAL 0x04 /* PCIE INTFATAL message is received */ #define BHND_PCIE_INTR_NFATAL 0x08 /* PCIE INTNONFATAL message is received */ #define BHND_PCIE_INTR_CORR 0x10 /* PCIE INTCORR message is received */ #define BHND_PCIE_INTR_PME 0x20 /* PCIE INTPME message is received */ /* SB to PCIE translation masks */ #define BHND_PCIE_SBTOPCI0_MASK BHND_PCI_SBTOPCI0_MASK #define BHND_PCIE_SBTOPCI1_MASK BHND_PCI_SBTOPCI1_MASK #define BHND_PCIE_SBTOPCI2_MASK BHND_PCI_SBTOPCI2_MASK /* Access type bits (0:1) */ #define BHND_PCIE_SBTOPCI_MEM BHND_PCI_SBTOPCI_MEM #define BHND_PCIE_SBTOPCI_IO BHND_PCI_SBTOPCI_IO #define BHND_PCIE_SBTOPCI_CFG0 BHND_PCI_SBTOPCI_CFG0 #define BHND_PCIE_SBTOPCI_CFG1 BHND_PCI_SBTOPCI_CFG1 #define BHND_PCIE_SBTOPCI_PREF BHND_PCI_SBTOPCI_PREF /* prefetch enable */ #define BHND_PCIE_SBTOPCI_BURST BHND_PCI_SBTOPCI_BURST /* burst enable */ /* BHND_PCIE_CFG_ADDR / BHND_PCIE_CFG_DATA */ #define BHND_PCIE_CFG_ADDR_FUNC_MASK 0x7000 #define BHND_PCIE_CFG_ADDR_FUNC_SHIFT 12 #define BHND_PCIE_CFG_ADDR_REG_MASK 0x0FFF #define BHND_PCIE_CFG_ADDR_REG_SHIFT 0 #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 */ #define BHND_PCIE_PLP_STATUSREG 0x204 /* Status */ #define BHND_PCIE_PLP_LTSSMCTRLREG 0x208 /* LTSSM control */ #define BHND_PCIE_PLP_LTLINKNUMREG 0x20c /* Link Training Link number */ #define BHND_PCIE_PLP_LTLANENUMREG 0x210 /* Link Training Lane number */ #define BHND_PCIE_PLP_LTNFTSREG 0x214 /* Link Training N_FTS */ #define BHND_PCIE_PLP_ATTNREG 0x218 /* Attention */ #define BHND_PCIE_PLP_ATTNMASKREG 0x21C /* Attention Mask */ #define BHND_PCIE_PLP_RXERRCTR 0x220 /* Rx Error */ #define BHND_PCIE_PLP_RXFRMERRCTR 0x224 /* Rx Framing Error */ #define BHND_PCIE_PLP_RXERRTHRESHREG 0x228 /* Rx Error threshold */ #define BHND_PCIE_PLP_TESTCTRLREG 0x22C /* Test Control reg */ #define BHND_PCIE_PLP_SERDESCTRLOVRDREG 0x230 /* SERDES Control Override */ #define BHND_PCIE_PLP_TIMINGOVRDREG 0x234 /* Timing param override */ #define BHND_PCIE_PLP_RXTXSMDIAGREG 0x238 /* RXTX State Machine Diag */ #define BHND_PCIE_PLP_LTSSMDIAGREG 0x23C /* LTSSM State Machine Diag */ /* PCIE protocol DLLP diagnostic registers */ #define BHND_PCIE_DLLP_LCREG 0x100 /* Link Control */ #define BHND_PCIE_DLLP_LCREG_PCIPM_EN 0x40 /* Enable PCI-PM power management */ #define BHND_PCIE_DLLP_LSREG 0x104 /* Link Status */ #define BHND_PCIE_DLLP_LAREG 0x108 /* Link Attention */ #define BHND_PCIE_DLLP_LAMASKREG 0x10C /* Link Attention Mask */ #define BHND_PCIE_DLLP_NEXTTXSEQNUMREG 0x110 /* Next Tx Seq Num */ #define BHND_PCIE_DLLP_ACKEDTXSEQNUMREG 0x114 /* Acked Tx Seq Num */ #define BHND_PCIE_DLLP_PURGEDTXSEQNUMREG 0x118 /* Purged Tx Seq Num */ #define BHND_PCIE_DLLP_RXSEQNUMREG 0x11C /* Rx Sequence Number */ #define BHND_PCIE_DLLP_LRREG 0x120 /* Link Replay */ #define BHND_PCIE_DLLP_LACKTOREG 0x124 /* Link Ack Timeout */ #define BHND_PCIE_DLLP_PMTHRESHREG 0x128 /* Power Management Threshold */ #define BHND_PCIE_L0THRESHOLDTIME_MASK 0xFF00 /* bits 0 - 7 */ #define BHND_PCIE_L1THRESHOLDTIME_MASK 0xFF00 /* bits 8 - 15 */ #define BHND_PCIE_L1THRESHOLDTIME_SHIFT 8 /* PCIE_L1THRESHOLDTIME_SHIFT */ #define BHND_PCIE_L1THRESHOLD_WARVAL 0x72 /* WAR value */ #define BHND_PCIE_ASPMTIMER_EXTEND 0x1000000 /* > rev7: enable extend ASPM timer */ #define BHND_PCIE_DLLP_RTRYWPREG 0x12C /* Retry buffer write ptr */ #define BHND_PCIE_DLLP_RTRYRPREG 0x130 /* Retry buffer Read ptr */ #define BHND_PCIE_DLLP_RTRYPPREG 0x134 /* Retry buffer Purged ptr */ #define BHND_PCIE_DLLP_RTRRWREG 0x138 /* Retry buffer Read/Write */ #define BHND_PCIE_DLLP_ECTHRESHREG 0x13C /* Error Count Threshold */ #define BHND_PCIE_DLLP_TLPERRCTRREG 0x140 /* TLP Error Counter */ #define BHND_PCIE_DLLP_ERRCTRREG 0x144 /* Error Counter */ #define BHND_PCIE_DLLP_NAKRXCTRREG 0x148 /* NAK Received Counter */ #define BHND_PCIE_DLLP_TESTREG 0x14C /* Test */ #define BHND_PCIE_DLLP_PKTBIST 0x150 /* Packet BIST */ #define BHND_PCIE_DLLP_PCIE11 0x154 /* DLLP PCIE 1.1 reg */ #define BHND_PCIE_DLLP_LSREG_LINKUP (1 << 16) /* PCIE protocol TLP diagnostic registers */ #define BHND_PCIE_TLP_CONFIGREG 0x000 /* Configuration */ #define BHND_PCIE_TLP_WORKAROUNDSREG 0x004 /* TLP Workarounds */ #define BHND_PCIE_TLP_WORKAROUND_URBIT 0x8 /* If enabled, UR status bit is set * on memory access of an unmatched * address */ #define BHND_PCIE_TLP_WRDMAUPPER 0x010 /* Write DMA Upper Address */ #define BHND_PCIE_TLP_WRDMALOWER 0x014 /* Write DMA Lower Address */ #define BHND_PCIE_TLP_WRDMAREQ_LBEREG 0x018 /* Write DMA Len/ByteEn Req */ #define BHND_PCIE_TLP_RDDMAUPPER 0x01C /* Read DMA Upper Address */ #define BHND_PCIE_TLP_RDDMALOWER 0x020 /* Read DMA Lower Address */ #define BHND_PCIE_TLP_RDDMALENREG 0x024 /* Read DMA Len Req */ #define BHND_PCIE_TLP_MSIDMAUPPER 0x028 /* MSI DMA Upper Address */ #define BHND_PCIE_TLP_MSIDMALOWER 0x02C /* MSI DMA Lower Address */ #define BHND_PCIE_TLP_MSIDMALENREG 0x030 /* MSI DMA Len Req */ #define BHND_PCIE_TLP_SLVREQLENREG 0x034 /* Slave Request Len */ #define BHND_PCIE_TLP_FCINPUTSREQ 0x038 /* Flow Control Inputs */ #define BHND_PCIE_TLP_TXSMGRSREQ 0x03C /* Tx StateMachine and Gated Req */ #define BHND_PCIE_TLP_ADRACKCNTARBLEN 0x040 /* Address Ack XferCnt and ARB Len */ #define BHND_PCIE_TLP_DMACPLHDR0 0x044 /* DMA Completion Hdr 0 */ #define BHND_PCIE_TLP_DMACPLHDR1 0x048 /* DMA Completion Hdr 1 */ #define BHND_PCIE_TLP_DMACPLHDR2 0x04C /* DMA Completion Hdr 2 */ #define BHND_PCIE_TLP_DMACPLMISC0 0x050 /* DMA Completion Misc0 */ #define BHND_PCIE_TLP_DMACPLMISC1 0x054 /* DMA Completion Misc1 */ #define BHND_PCIE_TLP_DMACPLMISC2 0x058 /* DMA Completion Misc2 */ #define BHND_PCIE_TLP_SPTCTRLLEN 0x05C /* Split Controller Req len */ #define BHND_PCIE_TLP_SPTCTRLMSIC0 0x060 /* Split Controller Misc 0 */ #define BHND_PCIE_TLP_SPTCTRLMSIC1 0x064 /* Split Controller Misc 1 */ #define BHND_PCIE_TLP_BUSDEVFUNC 0x068 /* Bus/Device/Func */ #define BHND_PCIE_TLP_RESETCTR 0x06C /* Reset Counter */ #define BHND_PCIE_TLP_RTRYBUF 0x070 /* Retry Buffer value */ #define BHND_PCIE_TLP_TGTDEBUG1 0x074 /* Target Debug Reg1 */ #define BHND_PCIE_TLP_TGTDEBUG2 0x078 /* Target Debug Reg2 */ #define BHND_PCIE_TLP_TGTDEBUG3 0x07C /* Target Debug Reg3 */ #define BHND_PCIE_TLP_TGTDEBUG4 0x080 /* Target Debug Reg4 */ /* * PCIe-G1 SerDes MDIO Registers (>= rev10) */ #define BHND_PCIE_PHYADDR_SD 0x0 /* serdes PHY address */ #define BHND_PCIE_DEVAD_SD 0x1 /* serdes pseudo-devad (PMA) recognized by the bhnd_mdio_pcie driver */ #define BHND_PCIE_SD_ADDREXT 0x1F /* serdes address extension register */ #define BHND_PCIE_SD_ADDREXT_BLK_MASK 0xFFF0 /* register block mask */ #define BHND_PCIE_SD_ADDREXT_REG_MASK 0x000F /* register address mask */ #define BHND_PCIE_SD_REGS_IEEE0 0x0000 /* IEEE0 AN CTRL block */ #define BHND_PCIE_SD_REGS_IEEE1 0x0010 /* IEEE1 AN ADV block */ #define BHND_PCIE_SD_REGS_BLK0 0x8000 /* ??? */ #define BHND_PCIE_SD_REGS_BLK1 0x8010 /* ??? */ #define BHND_PCIE_SD_REGS_BLK2 0x8020 /* ??? */ #define BHND_PCIE_SD_REGS_BLK3 0x8030 /* ??? */ #define BHND_PCIE_SD_REGS_BLK4 0x8040 /* ??? */ #define BHND_PCIE_SD_REGS_TXPLL 0x8080 /* TXPLL register block */ #define BHND_PCIE_SD_REGS_TXCTRL0 0x8200 /* ??? */ #define BHND_PCIE_SD_REGS_SERDESID 0x8310 /* ??? */ #define BHND_PCIE_SD_REGS_RXCTRL0 0x8400 /* ??? */ /* * PCIe-G1 SerDes-R9 MDIO Registers (<= rev9) * * These register definitions appear to match those provided in the * "PCI Express SerDes Registers" section of the BCM5761 Ethernet Controller * Programmer's Reference Guide. */ #define BHND_PCIE_PHY_SDR9_PLL 0x1C /* SerDes PLL PHY Address*/ #define BHND_PCIE_SDR9_PLL_CTRL 0x17 /* PLL control reg */ #define BHND_PCIE_SDR9_PLL_CTRL_FREQDET_EN 0x4000 /* bit 14 is FREQDET on */ #define BHND_PCIE_PHY_SDR9_TXRX 0x0F /* SerDes RX/TX PHY Address */ #define BHND_PCIE_SDR9_RX_CTRL 0x11 /* RX ctrl register */ #define BHND_PCIE_SDR9_RX_CTRL_FORCE 0x80 /* rxpolarity_force */ #define BHND_PCIE_SDR9_RX_CTRL_POLARITY_INV 0x40 /* rxpolarity_value (if set, inverse polarity) */ #define BHND_PCIE_SDR9_RX_CDR 0x16 /* RX CDR ctrl register */ #define BHND_PCIE_SDR9_RX_CDR_FREQ_OVR_EN 0x0100 /* freq_override_en flag */ #define BHND_PCIE_SDR9_RX_CDR_FREQ_OVR_MASK 0x00FF /* freq_override_val */ #define BHND_PCIE_SDR9_RX_CDR_FREQ_OVR_SHIFT 0 #define BHND_PCIE_SDR9_RX_CDRBW 0x17 /* RX CDR bandwidth (PLL tuning) */ #define BHND_PCIE_SDR9_RX_CDRBW_INTGTRK_MASK 0x7000 /* integral loop bandwidth (phase tracking mode) */ #define BHND_PCIE_SDR9_RX_CDRBW_INTGTRK_SHIFT 11 #define BHND_PCIE_SDR9_RX_CDRBW_INTGACQ_MASK 0x0700 /* integral loop bandwidth (phase acquisition mode) */ #define BHND_PCIE_SDR9_RX_CDRBW_INTGACQ_SHIFT 8 #define BHND_PCIE_SDR9_RX_CDRBW_PROPTRK_MASK 0x0070 /* proportional loop bandwidth (phase tracking mode) */ #define BHND_PCIE_SDR9_RX_CDRBW_PROPTRK_SHIFT 4 #define BHND_PCIE_SDR9_RX_CDRBW_PROPACQ_MASK 0x0007 /* proportional loop bandwidth (phase acquisition mode) */ #define BHND_PCIE_SDR9_RX_CDRBW_PROPACQ_SHIFT 0 #define BHND_PCIE_SDR9_RX_TIMER1 0x12 /* timer1 register */ #define BHND_PCIE_SDR9_RX_TIMER1_LKTRK_MASK 0xFF00 /* phase tracking delay before asserting RX seq completion (in 16ns units) */ #define BHND_PCIE_SDR9_RX_TIMER1_LKTRK_SHIFT 8 #define BHND_PCIE_SDR9_RX_TIMER1_LKACQ_MASK 0x00FF /* phase acquisition mode time (in 1024ns units) */ #define BHND_PCIE_SDR9_RX_TIMER1_LKACQ_SHIFT 0 /* SPROM offsets */ #define BHND_PCIE_SRSH_PI_OFFSET BHND_PCI_SRSH_PI_OFFSET /**< PCI core index in SROM shadow area */ #define BHND_PCIE_SRSH_PI_MASK BHND_PCI_SRSH_PI_MASK #define BHND_PCIE_SRSH_PI_SHIFT BHND_PCI_SRSH_PI_SHIFT #define BHND_PCIE_SRSH_ASPM_OFFSET 8 /* word 4 */ #define BHND_PCIE_SRSH_ASPM_ENB 0x18 /* bit 3, 4 */ #define BHND_PCIE_SRSH_ASPM_L1_ENB 0x10 /* bit 4 */ #define BHND_PCIE_SRSH_ASPM_L0s_ENB 0x8 /* bit 3 */ #define BHND_PCIE_SRSH_PCIE_MISC_CONFIG 10 /* word 5 */ #define BHND_PCIE_SRSH_L23READY_EXIT_NOPRST 0x8000 /* bit 15 */ #define BHND_PCIE_SRSH_CLKREQ_OFFSET_REV5 40 /* word 20 for srom rev <= 5 */ #define BHND_PCIE_SRSH_CLKREQ_OFFSET_REV8 104 /* word 52 for srom rev 8 */ #define BHND_PCIE_SRSH_CLKREQ_ENB 0x0800 /* bit 11 */ #define BHND_PCIE_SRSH_BD_OFFSET 12 /* word 6 */ #define BHND_PCIE_SRSH_AUTOINIT_OFFSET 36 /* auto initialization enable */ /* Linkcontrol reg offset in PCIE Cap */ #define BHND_PCIE_CAP_LINKCTRL_OFFSET 16 /* linkctrl offset in pcie cap */ #define BHND_PCIE_CAP_LCREG_ASPML0s 0x01 /* ASPM L0s in linkctrl */ #define BHND_PCIE_CAP_LCREG_ASPML1 0x02 /* ASPM L1 in linkctrl */ #define BHND_PCIE_CLKREQ_ENAB 0x100 /* CLKREQ Enab in linkctrl */ #define BHND_PCIE_ASPM_ENAB 3 /* ASPM L0s & L1 in linkctrl */ #define BHND_PCIE_ASPM_L1_ENAB 2 /* ASPM L0s & L1 in linkctrl */ #define BHND_PCIE_ASPM_L0s_ENAB 1 /* ASPM L0s & L1 in linkctrl */ #define BHND_PCIE_ASPM_DISAB 0 /* ASPM L0s & L1 in linkctrl */ /* Status reg PCIE_PLP_STATUSREG */ #define BHND_PCIE_PLP_POLARITY_INV 0x10 /* lane polarity is inverted */ #endif /* _BHND_CORES_PCI_BHND_PCIREG_H_ */ Index: head/sys/dev/bhnd/cores/pci/bhnd_pcivar.h =================================================================== --- head/sys/dev/bhnd/cores/pci/bhnd_pcivar.h (revision 298478) +++ head/sys/dev/bhnd/cores/pci/bhnd_pcivar.h (revision 298479) @@ -1,125 +1,188 @@ /*- * Copyright (c) 2015 Landon Fuller * 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_BHND_PCIVAR_H_ #define _BHND_CORES_PCI_BHND_PCIVAR_H_ #include #include /* * 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)) /** * Extract a register value by applying _MASK and _SHIFT defines. * * @param _regv The register value containing the desired attribute * @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. * * @param _regv The current register value. * @param _attr The register attribute name to which to append `_MASK`/`_SHIFT` * 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)) /** * 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) /** * Evaluates to the offset of a common PCI/PCIe register definition. * * This will trigger a compile-time error if the register is not defined * for all supported PCI/PCIe cores. * * 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: head/sys/dev/bhnd/siba/siba_bhndb.c =================================================================== --- head/sys/dev/bhnd/siba/siba_bhndb.c (revision 298478) +++ head/sys/dev/bhnd/siba/siba_bhndb.c (revision 298479) @@ -1,171 +1,182 @@ /*- * Copyright (c) 2015 Landon Fuller * 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include "sibavar.h" /* * 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) { const struct bhnd_chipid *cid; /* Check bus type */ cid = BHNDB_GET_CHIPID(device_get_parent(dev), dev); if (cid->chip_type != BHND_CHIPTYPE_SIBA) return (ENXIO); /* Delegate to default probe implementation */ return (siba_probe(dev)); } static int siba_bhndb_attach(device_t dev) { const struct bhnd_chipid *chipid; int error; /* Enumerate our children. */ chipid = BHNDB_GET_CHIPID(device_get_parent(dev), dev); if ((error = siba_add_children(dev, chipid))) return (error); /* Initialize full bridge configuration */ error = BHNDB_INIT_FULL_CONFIG(device_get_parent(dev), dev, bhndb_siba_priority_table); if (error) return (error); /* Call our superclass' implementation */ return (siba_attach(dev)); } /* Suspend all references to the device's cfg register blocks */ static void siba_bhndb_suspend_cfgblocks(device_t dev, struct siba_devinfo *dinfo) { for (u_int i = 0; i < dinfo->core_id.num_cfg_blocks; i++) { if (dinfo->cfg[i] == NULL) continue; BHNDB_SUSPEND_RESOURCE(device_get_parent(dev), dev, SYS_RES_MEMORY, dinfo->cfg[i]->res); } } static int siba_bhndb_suspend_child(device_t dev, device_t child) { struct siba_devinfo *dinfo; int error; if (device_get_parent(child) != dev) BUS_SUSPEND_CHILD(device_get_parent(dev), child); dinfo = device_get_ivars(child); /* Suspend the child */ if ((error = bhnd_generic_br_suspend_child(dev, child))) return (error); /* Suspend resource references to the child's config registers */ siba_bhndb_suspend_cfgblocks(dev, dinfo); return (0); } static int siba_bhndb_resume_child(device_t dev, device_t child) { struct siba_devinfo *dinfo; int error; if (device_get_parent(child) != dev) BUS_SUSPEND_CHILD(device_get_parent(dev), child); if (!device_is_suspended(child)) return (EBUSY); dinfo = device_get_ivars(child); /* Resume all resource references to the child's config registers */ for (u_int i = 0; i < dinfo->core_id.num_cfg_blocks; i++) { if (dinfo->cfg[i] == NULL) continue; error = BHNDB_RESUME_RESOURCE(device_get_parent(dev), dev, SYS_RES_MEMORY, dinfo->cfg[i]->res); if (error) { siba_bhndb_suspend_cfgblocks(dev, dinfo); return (error); } } /* Resume the child */ if ((error = bhnd_generic_br_resume_child(dev, child))) { siba_bhndb_suspend_cfgblocks(dev, dinfo); return (error); } return (0); } static device_method_t siba_bhndb_methods[] = { /* Device interface */ DEVMETHOD(device_probe, siba_bhndb_probe), DEVMETHOD(device_attach, siba_bhndb_attach), /* Bus interface */ DEVMETHOD(bus_suspend_child, siba_bhndb_suspend_child), DEVMETHOD(bus_resume_child, siba_bhndb_resume_child), DEVMETHOD_END }; DEFINE_CLASS_1(bhnd, siba_bhndb_driver, siba_bhndb_methods, sizeof(struct siba_softc), siba_driver); DRIVER_MODULE(siba_bhndb, bhndb, siba_bhndb_driver, bhnd_devclass, NULL, NULL); MODULE_VERSION(siba_bhndb, 1); MODULE_DEPEND(siba_bhndb, siba, 1, 1, 1); MODULE_DEPEND(siba_bhndb, bhndb, 1, 1, 1); \ No newline at end of file Index: head/sys/modules/bhnd/cores/bhnd_pci/Makefile =================================================================== --- head/sys/modules/bhnd/cores/bhnd_pci/Makefile (revision 298478) +++ head/sys/modules/bhnd/cores/bhnd_pci/Makefile (revision 298479) @@ -1,11 +1,9 @@ # $FreeBSD$ .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