[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
This commit is contained in:
parent
a0fa43a851
commit
d8ac63dee1
@ -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,418 +137,126 @@ 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)))
|
||||
/* Let our parent perform standard initialization first */
|
||||
if ((error = bhndb_generic_init_full_config(dev, child, hw_prio_table)))
|
||||
return (error);
|
||||
|
||||
/*
|
||||
* Identify our PCI bridge core, its register family, and any
|
||||
* applicable hardware quirks.
|
||||
*/
|
||||
KASSERT(sc->bhndb.hostb_dev,
|
||||
("missing hostb device\n"));
|
||||
|
||||
core = bhnd_get_core_info(sc->bhndb.hostb_dev);
|
||||
id = bhndb_pci_find_core_id(&core);
|
||||
if (id == NULL) {
|
||||
device_printf(dev, "%s %s hostb core is not recognized\n",
|
||||
bhnd_vendor_name(core.vendor), bhnd_core_name(&core));
|
||||
}
|
||||
|
||||
sc->regfmt = id->regfmt;
|
||||
|
||||
/* Now that we've identified the PCI bridge core, we can determine the
|
||||
* full set of device quirks */
|
||||
sc->quirks = bhndb_pci_discover_quirks(sc, id);
|
||||
|
||||
/*
|
||||
* Determine and save a reference to the bhndb resource and offset
|
||||
* at which the bridge core's device registers are mapped.
|
||||
*
|
||||
* All known bhnd(4) hardware provides a fixed static mapping of
|
||||
* the PCI core's registers. If this changes in the future -- which
|
||||
* is unlikely -- this driver will need to be adjusted to use
|
||||
* dynamic register windows.
|
||||
*/
|
||||
|
||||
/* Find base address and size of the PCI core's register block. */
|
||||
error = bhnd_get_region_addr(sc->bhndb.hostb_dev, BHND_PORT_DEVICE, 0,
|
||||
0, &pcir_addr, &pcir_size);
|
||||
if (error) {
|
||||
device_printf(dev,
|
||||
"failed to locate PCI core registers\n");
|
||||
return (error);
|
||||
}
|
||||
|
||||
/* Find the bhndb_region that statically maps this block */
|
||||
pcir = bhndb_find_resource_region(sc->bhndb.bus_res, pcir_addr,
|
||||
pcir_size);
|
||||
if (pcir == NULL || pcir->static_regwin == NULL) {
|
||||
device_printf(dev,
|
||||
"missing static PCI core register window\n");
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
/* Save borrowed reference to the mapped PCI core registers */
|
||||
sc->mem_off = pcir->static_regwin->win_offset;
|
||||
sc->mem_res = bhndb_find_regwin_resource(sc->bhndb.bus_res,
|
||||
pcir->static_regwin);
|
||||
if (sc->mem_res == NULL || !(rman_get_flags(sc->mem_res) & RF_ACTIVE)) {
|
||||
device_printf(dev,
|
||||
"no active resource maps the PCI core register window\n");
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
/* Configure a direct bhnd_resource wrapper that we can pass to
|
||||
* bhnd_resource APIs */
|
||||
sc->bhnd_mem_res = (struct bhnd_resource) {
|
||||
.res = sc->mem_res,
|
||||
.direct = true
|
||||
};
|
||||
|
||||
/*
|
||||
* Attach MMIO device (if this is a PCIe device), which is used for
|
||||
* access to the PCIe SerDes required by the quirk workarounds.
|
||||
*/
|
||||
if (sc->pci_devclass == BHND_DEVCLASS_PCIE) {
|
||||
sc->mdio = BUS_ADD_CHILD(dev, 0,
|
||||
devclass_get_name(bhnd_mdio_pci_devclass), 0);
|
||||
if (sc->mdio == NULL)
|
||||
return (ENXIO);
|
||||
|
||||
error = bus_set_resource(sc->mdio, SYS_RES_MEMORY, 0,
|
||||
rman_get_start(sc->mem_res) + sc->mem_off +
|
||||
BHND_PCIE_MDIO_CTL, sizeof(uint32_t)*2);
|
||||
if (error) {
|
||||
device_printf(dev, "failed to set MDIO resource\n");
|
||||
return (error);
|
||||
}
|
||||
|
||||
if ((error = device_probe_and_attach(sc->mdio))) {
|
||||
device_printf(dev, "failed to attach MDIO device\n");
|
||||
return (error);
|
||||
}
|
||||
}
|
||||
|
||||
/* Apply any early one-time quirk workarounds */
|
||||
if ((error = bhndb_pci_wars_early_once(sc)))
|
||||
return (error);
|
||||
|
||||
/* Apply attach-time quirk workarounds, required before the bridged
|
||||
* bhnd(4) bus itself performs a full attach(). */
|
||||
if ((error = bhndb_pci_wars_hwup(sc)))
|
||||
return (error);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply any hardware workarounds that must be executed prior to attempting
|
||||
* register access on the bridged chipset.
|
||||
*
|
||||
* 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_resume(device_t dev)
|
||||
{
|
||||
struct bhndb_pci_softc *sc;
|
||||
int error;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
|
||||
/* Enable clocks (if supported by this hardware) */
|
||||
if ((error = bhndb_enable_pci_clocks(sc)))
|
||||
return (error);
|
||||
|
||||
/* Perform resume */
|
||||
return (bhndb_generic_resume(dev));
|
||||
}
|
||||
|
||||
static int
|
||||
bhndb_pci_suspend(device_t dev)
|
||||
{
|
||||
struct bhndb_pci_softc *sc;
|
||||
int error;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
|
||||
/* Disable clocks (if supported by this hardware) */
|
||||
if ((error = bhndb_disable_pci_clocks(sc)))
|
||||
return (error);
|
||||
|
||||
/* Perform suspend */
|
||||
return (bhndb_generic_suspend(dev));
|
||||
}
|
||||
|
||||
static int
|
||||
bhndb_pci_detach(device_t dev)
|
||||
{
|
||||
@ -700,12 +265,12 @@ bhndb_pci_detach(device_t dev)
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
|
||||
if ((error = bhndb_generic_detach(dev)))
|
||||
/* Disable clocks (if supported by this hardware) */
|
||||
if ((error = bhndb_disable_pci_clocks(sc)))
|
||||
return (error);
|
||||
|
||||
/* Apply any hardware workarounds. This may disable the clock, and
|
||||
* thus must be called *after* any children have been detached. */
|
||||
if ((error = bhndb_pci_wars_hwdown(sc)))
|
||||
/* Perform detach */
|
||||
if ((error = bhndb_generic_detach(dev)))
|
||||
return (error);
|
||||
|
||||
/* Disable PCI bus mastering */
|
||||
@ -714,45 +279,6 @@ bhndb_pci_detach(device_t dev)
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
bhndb_pci_suspend(device_t dev)
|
||||
{
|
||||
struct bhndb_pci_softc *sc;
|
||||
int error;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
|
||||
if ((error = bhndb_generic_suspend(dev)))
|
||||
return (error);
|
||||
|
||||
/* Apply any hardware workarounds. This may disable the clock, and
|
||||
* thus must be called *after* any children have been suspended. */
|
||||
if ((error = bhndb_pci_wars_hwdown(sc)))
|
||||
return (error);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
bhndb_pci_resume(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)))
|
||||
return (error);
|
||||
|
||||
if ((error = bhndb_generic_resume(dev)))
|
||||
return (error);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
bhndb_pci_set_window_addr(device_t dev, const struct bhndb_regwin *rw,
|
||||
bhnd_addr_t addr)
|
||||
@ -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);
|
||||
|
@ -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_ */
|
||||
#endif /* _BHND_BHNDB_PCIVAR_H_ */
|
||||
|
@ -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
|
||||
};
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
/**
|
||||
* PCIe MDIO interface device class
|
||||
* Read a 32-bit PCIe TLP/DLLP/PLP protocol register.
|
||||
*
|
||||
* @param sc The bhndb_pci driver state.
|
||||
* @param addr The protocol register offset.
|
||||
*/
|
||||
devclass_t bhnd_mdio_pci_devclass;
|
||||
uint32_t
|
||||
bhnd_pcie_read_proto_reg(struct bhnd_pci_softc *sc, uint32_t addr)
|
||||
{
|
||||
uint32_t val;
|
||||
|
||||
BHND_PCIE_ASSERT(sc);
|
||||
|
||||
BHND_PCI_LOCK(sc);
|
||||
BHND_PCI_WRITE_4(sc, BHND_PCIE_IND_ADDR, addr);
|
||||
val = BHND_PCI_READ_4(sc, BHND_PCIE_IND_DATA);
|
||||
BHND_PCI_UNLOCK(sc);
|
||||
|
||||
return (val);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a 32-bit PCIe TLP/DLLP/PLP protocol register value.
|
||||
*
|
||||
* @param sc The bhndb_pci driver state.
|
||||
* @param addr The protocol register offset.
|
||||
* @param val The value to write to @p addr.
|
||||
*/
|
||||
void
|
||||
bhnd_pcie_write_proto_reg(struct bhnd_pci_softc *sc, uint32_t addr,
|
||||
uint32_t val)
|
||||
{
|
||||
BHND_PCIE_ASSERT(sc);
|
||||
|
||||
BHND_PCI_LOCK(sc);
|
||||
BHND_PCI_WRITE_4(sc, BHND_PCIE_IND_ADDR, addr);
|
||||
BHND_PCI_WRITE_4(sc, BHND_PCIE_IND_DATA, val);
|
||||
BHND_PCI_UNLOCK(sc);
|
||||
}
|
||||
|
||||
/* Spin until the MDIO device reports itself as idle, or timeout is reached. */
|
||||
static int
|
||||
bhnd_pcie_mdio_wait_idle(struct bhnd_pci_softc *sc)
|
||||
{
|
||||
uint32_t ctl;
|
||||
|
||||
/* Spin waiting for the BUSY flag to clear */
|
||||
for (int i = 0; i < BHND_PCIE_MDIO_RETRY_COUNT; i++) {
|
||||
ctl = BHND_PCI_READ_4(sc, BHND_PCIE_MDIO_CTL);
|
||||
if ((ctl & BHND_PCIE_MDIOCTL_DONE))
|
||||
return (0);
|
||||
|
||||
DELAY(BHND_PCIE_MDIO_RETRY_DELAY);
|
||||
}
|
||||
|
||||
return (ETIMEDOUT);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Write an MDIO IOCTL and wait for completion.
|
||||
*/
|
||||
static int
|
||||
bhnd_pcie_mdio_ioctl(struct bhnd_pci_softc *sc, uint32_t cmd)
|
||||
{
|
||||
BHND_PCI_LOCK_ASSERT(sc, MA_OWNED);
|
||||
|
||||
BHND_PCI_WRITE_4(sc, BHND_PCIE_MDIO_CTL, cmd);
|
||||
DELAY(BHND_PCIE_MDIO_CTL_DELAY);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable MDIO device
|
||||
*/
|
||||
static int
|
||||
bhnd_pcie_mdio_enable(struct bhnd_pci_softc *sc)
|
||||
{
|
||||
uint32_t ctl;
|
||||
|
||||
BHND_PCIE_ASSERT(sc);
|
||||
|
||||
/* Enable MDIO clock and preamble mode */
|
||||
ctl = BHND_PCIE_MDIOCTL_PREAM_EN|BHND_PCIE_MDIOCTL_DIVISOR_VAL;
|
||||
return (bhnd_pcie_mdio_ioctl(sc, ctl));
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable MDIO device.
|
||||
*/
|
||||
static void
|
||||
bhnd_pcie_mdio_disable(struct bhnd_pci_softc *sc)
|
||||
{
|
||||
if (bhnd_pcie_mdio_ioctl(sc, 0))
|
||||
device_printf(sc->dev, "failed to disable MDIO clock\n");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Issue a write command and wait for completion
|
||||
*/
|
||||
static int
|
||||
bhnd_pcie_mdio_cmd_write(struct bhnd_pci_softc *sc, uint32_t cmd)
|
||||
{
|
||||
int error;
|
||||
|
||||
BHND_PCI_LOCK_ASSERT(sc, MA_OWNED);
|
||||
|
||||
cmd |= BHND_PCIE_MDIODATA_START|BHND_PCIE_MDIODATA_TA|BHND_PCIE_MDIODATA_CMD_WRITE;
|
||||
|
||||
BHND_PCI_WRITE_4(sc, BHND_PCIE_MDIO_DATA, cmd);
|
||||
DELAY(BHND_PCIE_MDIO_CTL_DELAY);
|
||||
|
||||
if ((error = bhnd_pcie_mdio_wait_idle(sc)))
|
||||
return (error);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Issue an an MDIO read command, wait for completion, and return
|
||||
* the result in @p data_read.
|
||||
*/
|
||||
static int
|
||||
bhnd_pcie_mdio_cmd_read(struct bhnd_pci_softc *sc, uint32_t cmd,
|
||||
uint16_t *data_read)
|
||||
{
|
||||
int error;
|
||||
|
||||
BHND_PCI_LOCK_ASSERT(sc, MA_OWNED);
|
||||
|
||||
cmd |= BHND_PCIE_MDIODATA_START|BHND_PCIE_MDIODATA_TA|BHND_PCIE_MDIODATA_CMD_READ;
|
||||
BHND_PCI_WRITE_4(sc, BHND_PCIE_MDIO_DATA, cmd);
|
||||
DELAY(BHND_PCIE_MDIO_CTL_DELAY);
|
||||
|
||||
if ((error = bhnd_pcie_mdio_wait_idle(sc)))
|
||||
return (error);
|
||||
|
||||
*data_read = (BHND_PCI_READ_4(sc, BHND_PCIE_MDIO_DATA) &
|
||||
BHND_PCIE_MDIODATA_DATA_MASK);
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
bhnd_pcie_mdio_read(struct bhnd_pci_softc *sc, int phy, int reg)
|
||||
{
|
||||
uint32_t cmd;
|
||||
uint16_t val;
|
||||
int error;
|
||||
|
||||
/* Enable MDIO access */
|
||||
BHND_PCI_LOCK(sc);
|
||||
bhnd_pcie_mdio_enable(sc);
|
||||
|
||||
/* Issue the read */
|
||||
cmd = BHND_PCIE_MDIODATA_ADDR(phy, reg);
|
||||
error = bhnd_pcie_mdio_cmd_read(sc, cmd, &val);
|
||||
|
||||
/* Disable MDIO access */
|
||||
bhnd_pcie_mdio_disable(sc);
|
||||
BHND_PCI_UNLOCK(sc);
|
||||
|
||||
if (error)
|
||||
return (~0U);
|
||||
|
||||
return (val);
|
||||
}
|
||||
|
||||
int
|
||||
bhnd_pcie_mdio_write(struct bhnd_pci_softc *sc, int phy, int reg, int val)
|
||||
{
|
||||
uint32_t cmd;
|
||||
int error;
|
||||
|
||||
/* Enable MDIO access */
|
||||
BHND_PCI_LOCK(sc);
|
||||
bhnd_pcie_mdio_enable(sc);
|
||||
|
||||
/* Issue the write */
|
||||
cmd = BHND_PCIE_MDIODATA_ADDR(phy, reg) | (val & BHND_PCIE_MDIODATA_DATA_MASK);
|
||||
error = bhnd_pcie_mdio_cmd_write(sc, cmd);
|
||||
|
||||
/* Disable MDIO access */
|
||||
bhnd_pcie_mdio_disable(sc);
|
||||
BHND_PCI_UNLOCK(sc);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
int
|
||||
bhnd_pcie_mdio_read_ext(struct bhnd_pci_softc *sc, int phy, int devaddr,
|
||||
int reg)
|
||||
{
|
||||
uint32_t cmd;
|
||||
uint16_t blk, val;
|
||||
uint8_t blk_reg;
|
||||
int error;
|
||||
|
||||
if (devaddr == MDIO_DEVADDR_NONE)
|
||||
return (bhnd_pcie_mdio_read(sc, phy, reg));
|
||||
|
||||
/* Extended register access is only supported for the SerDes device,
|
||||
* using the non-standard C22 extended address mechanism */
|
||||
if (!(sc->quirks & BHND_PCI_QUIRK_SD_C22_EXTADDR))
|
||||
return (~0U);
|
||||
if (phy != BHND_PCIE_PHYADDR_SD || devaddr != BHND_PCIE_DEVAD_SD)
|
||||
return (~0U);
|
||||
|
||||
/* Enable MDIO access */
|
||||
BHND_PCI_LOCK(sc);
|
||||
bhnd_pcie_mdio_enable(sc);
|
||||
|
||||
/* Determine the block and register values */
|
||||
blk = (reg & BHND_PCIE_SD_ADDREXT_BLK_MASK);
|
||||
blk_reg = (reg & BHND_PCIE_SD_ADDREXT_REG_MASK);
|
||||
|
||||
/* Write the block address to the address extension register */
|
||||
cmd = BHND_PCIE_MDIODATA_ADDR(phy, BHND_PCIE_SD_ADDREXT) |
|
||||
(blk & BHND_PCIE_MDIODATA_DATA_MASK);
|
||||
if ((error = bhnd_pcie_mdio_cmd_write(sc, cmd)))
|
||||
goto cleanup;
|
||||
|
||||
/* Issue the read */
|
||||
cmd = BHND_PCIE_MDIODATA_ADDR(phy, blk_reg);
|
||||
error = bhnd_pcie_mdio_cmd_read(sc, cmd, &val);
|
||||
|
||||
cleanup:
|
||||
bhnd_pcie_mdio_disable(sc);
|
||||
BHND_PCI_UNLOCK(sc);
|
||||
|
||||
if (error)
|
||||
return (~0U);
|
||||
|
||||
return (val);
|
||||
}
|
||||
|
||||
int
|
||||
bhnd_pcie_mdio_write_ext(struct bhnd_pci_softc *sc, int phy, int devaddr,
|
||||
int reg, int val)
|
||||
{
|
||||
uint32_t cmd;
|
||||
uint16_t blk;
|
||||
uint8_t blk_reg;
|
||||
int error;
|
||||
|
||||
if (devaddr == MDIO_DEVADDR_NONE)
|
||||
return (bhnd_pcie_mdio_write(sc, phy, reg, val));
|
||||
|
||||
/* Extended register access is only supported for the SerDes device,
|
||||
* using the non-standard C22 extended address mechanism */
|
||||
if (!(sc->quirks & BHND_PCI_QUIRK_SD_C22_EXTADDR))
|
||||
return (~0U);
|
||||
if (phy != BHND_PCIE_PHYADDR_SD || devaddr != BHND_PCIE_DEVAD_SD)
|
||||
return (~0U);
|
||||
|
||||
/* Enable MDIO access */
|
||||
BHND_PCI_LOCK(sc);
|
||||
bhnd_pcie_mdio_enable(sc);
|
||||
|
||||
/* Determine the block and register values */
|
||||
blk = (reg & BHND_PCIE_SD_ADDREXT_BLK_MASK);
|
||||
blk_reg = (reg & BHND_PCIE_SD_ADDREXT_REG_MASK);
|
||||
|
||||
/* Write the block address to the address extension register */
|
||||
cmd = BHND_PCIE_MDIODATA_ADDR(phy, BHND_PCIE_SD_ADDREXT) |
|
||||
(blk & BHND_PCIE_MDIODATA_DATA_MASK);
|
||||
if ((error = bhnd_pcie_mdio_cmd_write(sc, cmd)))
|
||||
goto cleanup;
|
||||
|
||||
/* Issue the write */
|
||||
cmd = BHND_PCIE_MDIODATA_ADDR(phy, blk_reg) |
|
||||
(val & BHND_PCIE_MDIODATA_DATA_MASK);
|
||||
error = bhnd_pcie_mdio_cmd_write(sc, cmd);
|
||||
|
||||
cleanup:
|
||||
bhnd_pcie_mdio_disable(sc);
|
||||
BHND_PCI_UNLOCK(sc);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
static device_method_t bhnd_pci_methods[] = {
|
||||
/* Device interface */
|
||||
DEVMETHOD(device_probe, bhnd_pci_generic_probe),
|
||||
DEVMETHOD(device_attach, bhnd_pci_generic_attach),
|
||||
DEVMETHOD(device_detach, bhnd_pci_generic_detach),
|
||||
DEVMETHOD(device_suspend, bhnd_pci_generic_suspend),
|
||||
DEVMETHOD(device_resume, bhnd_pci_generic_resume),
|
||||
|
||||
/* Bus interface */
|
||||
DEVMETHOD(bus_add_child, bhnd_pci_add_child),
|
||||
DEVMETHOD(bus_child_deleted, bhnd_pci_child_deleted),
|
||||
DEVMETHOD(bus_print_child, bus_generic_print_child),
|
||||
DEVMETHOD(bus_get_resource_list, bhnd_pci_get_resource_list),
|
||||
DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource),
|
||||
DEVMETHOD(bus_set_resource, bus_generic_rl_set_resource),
|
||||
DEVMETHOD(bus_delete_resource, bus_generic_rl_delete_resource),
|
||||
|
||||
DEVMETHOD(bus_alloc_resource, bus_generic_rl_alloc_resource),
|
||||
DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
|
||||
DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
|
||||
DEVMETHOD(bus_adjust_resource, bus_generic_adjust_resource),
|
||||
DEVMETHOD(bus_release_resource, bus_generic_rl_release_resource),
|
||||
|
||||
DEVMETHOD_END
|
||||
};
|
||||
|
||||
DEFINE_CLASS_0(bhnd_pci, bhnd_pci_driver, bhnd_pci_methods, sizeof(struct bhnd_pci_softc));
|
||||
|
||||
MODULE_VERSION(bhnd_pci, 1);
|
||||
MODULE_DEPEND(bhnd_pci, pci, 1, 1, 1);
|
||||
|
@ -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 int
|
||||
bhnd_pci_hostb_probe(device_t dev)
|
||||
{
|
||||
/* Ignore non-PCI cores */
|
||||
switch (bhnd_get_class(dev)){
|
||||
case BHND_DEVCLASS_PCI:
|
||||
case BHND_DEVCLASS_PCIE:
|
||||
break;
|
||||
default:
|
||||
return (ENXIO);
|
||||
}
|
||||
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
|
||||
};
|
||||
|
||||
/* Ignore PCI cores not in host bridge mode. */
|
||||
if (!bhnd_is_hostb_device(dev))
|
||||
return (ENXIO);
|
||||
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_set_default_core_desc(dev);
|
||||
return (BUS_PROBE_DEFAULT);
|
||||
}
|
||||
{ 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_attach(device_t dev)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
/* Apply attach/resume work-arounds */
|
||||
if ((error = bhnd_pci_wars_hwup(sc))) {
|
||||
bhnd_pci_generic_detach(dev);
|
||||
return (error);
|
||||
}
|
||||
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
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_suspend(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_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_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_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);
|
||||
MODULE_DEPEND(bhnd_pci_hostb, bhnd_pci, 1, 1, 1);
|
||||
|
194
sys/dev/bhnd/cores/pci/bhnd_pci_hostbvar.h
Normal file
194
sys/dev/bhnd/cores/pci/bhnd_pci_hostbvar.h
Normal file
@ -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_ */
|
@ -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);
|
||||
|
@ -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_ */
|
@ -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 */
|
||||
|
@ -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 \
|
||||
)
|
||||
|
||||
|
@ -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));
|
@ -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_ */
|
@ -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_ */
|
@ -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)
|
||||
{
|
||||
|
@ -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>
|
||||
|
Loading…
Reference in New Issue
Block a user