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