Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 17 May 2016 06:52:54 +0000 (UTC)
From:      Adrian Chadd <adrian@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r300015 - in head/sys: conf dev/bhnd dev/bhnd/bhndb dev/bhnd/cores/chipc dev/bhnd/cores/pci dev/bhnd/cores/pcie2 dev/bhnd/siba modules/bhnd/cores/bhnd_pci modules/bhnd/cores/bhnd_pci_ho...
Message-ID:  <201605170652.u4H6qsmQ024900@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: adrian
Date: Tue May 17 06:52:53 2016
New Revision: 300015
URL: https://svnweb.freebsd.org/changeset/base/300015

Log:
  [bhnd] Finish bhnd(4) PCI/PCIe-G1 hostb support.
  
  Now that we've got access to SPROM and can access board identification,
  this implements all known remaining hardware work-arounds for the bhnd(4)
  PCI and PCIe-G1 cores operating endpoint mode.
  
  Additionally, this adds an initial set of skeleton PCIe-G2 hostb and pcib
  drivers, required by fullmac and newer softmac devices.
  
  Submitted by:	Landon Fuller <landonf@landonf.org>
  Differential Revision:	https://reviews.freebsd.org/D6377

Added:
  head/sys/dev/bhnd/cores/pcie2/
  head/sys/dev/bhnd/cores/pcie2/bhnd_pcie2.c   (contents, props changed)
  head/sys/dev/bhnd/cores/pcie2/bhnd_pcie2_hostb.c   (contents, props changed)
  head/sys/dev/bhnd/cores/pcie2/bhnd_pcie2_hostbvar.h   (contents, props changed)
  head/sys/dev/bhnd/cores/pcie2/bhnd_pcie2_reg.h   (contents, props changed)
  head/sys/dev/bhnd/cores/pcie2/bhnd_pcie2_var.h   (contents, props changed)
  head/sys/dev/bhnd/cores/pcie2/bhnd_pcie2b.c   (contents, props changed)
  head/sys/dev/bhnd/cores/pcie2/bhnd_pcie2b_var.h   (contents, props changed)
Modified:
  head/sys/conf/files
  head/sys/dev/bhnd/bhnd.h
  head/sys/dev/bhnd/bhnd_ids.h
  head/sys/dev/bhnd/bhnd_subr.c
  head/sys/dev/bhnd/bhndb/bhndb_pci.c
  head/sys/dev/bhnd/bhndb/bhndb_pcireg.h
  head/sys/dev/bhnd/cores/chipc/bhnd_chipc_if.m
  head/sys/dev/bhnd/cores/chipc/chipc.c
  head/sys/dev/bhnd/cores/pci/bhnd_pci.c
  head/sys/dev/bhnd/cores/pci/bhnd_pci_hostb.c
  head/sys/dev/bhnd/cores/pci/bhnd_pci_hostbvar.h
  head/sys/dev/bhnd/cores/pci/bhnd_pcib.c
  head/sys/dev/bhnd/cores/pci/bhnd_pcireg.h
  head/sys/dev/bhnd/siba/siba.c
  head/sys/dev/bhnd/siba/siba_bhndb.c
  head/sys/dev/bhnd/siba/sibavar.h
  head/sys/modules/bhnd/cores/bhnd_pci/Makefile
  head/sys/modules/bhnd/cores/bhnd_pci_hostb/Makefile
  head/sys/modules/bhnd/cores/bhnd_pcib/Makefile

Modified: head/sys/conf/files
==============================================================================
--- head/sys/conf/files	Tue May 17 06:45:25 2016	(r300014)
+++ head/sys/conf/files	Tue May 17 06:52:53 2016	(r300015)
@@ -1142,6 +1142,9 @@ dev/bhnd/cores/chipc/bhnd_chipc_if.m	opt
 dev/bhnd/cores/pci/bhnd_pci.c		optional bhndbus pci | bhnd pci
 dev/bhnd/cores/pci/bhnd_pci_hostb.c	optional bhndbus pci | bhndb pci
 dev/bhnd/cores/pci/bhnd_pcib.c		optional bhnd_pcib bhnd pci
+dev/bhnd/cores/pcie2/bhnd_pcie2.c	optional bhndbus pci | bhnd pci
+dev/bhnd/cores/pcie2/bhnd_pcie2_hostb.c	optional bhndbus pci | bhndb pci
+dev/bhnd/cores/pcie2/bhnd_pcie2b.c	optional bhnd_pcie2b bhnd pci
 dev/bhnd/nvram/bhnd_nvram_if.m		optional bhndbus | bhnd
 dev/bhnd/nvram/bhnd_sprom.c		optional bhndbus | bhnd
 dev/bhnd/nvram/nvram_subr.c		optional bhndbus | bhnd

Modified: head/sys/dev/bhnd/bhnd.h
==============================================================================
--- head/sys/dev/bhnd/bhnd.h	Tue May 17 06:45:25 2016	(r300014)
+++ head/sys/dev/bhnd/bhnd.h	Tue May 17 06:52:53 2016	(r300015)
@@ -318,8 +318,8 @@ struct bhnd_chip_match {
 	.match_bvendor = 1, .board_vendor = _vend
 
 /** Set the required board type within a bhnd_chip_match instance */
-#define	BHND_CHIP_BT(_btype)		\
-	.match_btype = 1, .board_type = BHND_BOARD_BCM ## _btype
+#define	BHND_CHIP_BTYPE(_btype)		\
+	.match_btype = 1, .board_type = BHND_BOARD_ ## _btype
 
 /** Set the required SROM revision range within a bhnd_chip_match instance */
 #define	BHND_CHIP_SROMREV(_rev)		\
@@ -331,7 +331,7 @@ struct bhnd_chip_match {
 
 /** Set the required board vendor and type within a bhnd_chip_match instance */
 #define	BHND_CHIP_BVT(_vend, _type)	\
-	BHND_CHIP_BVEND(_vend), BHND_CHIP_BTYPE(_type)
+	BHND_CHIP_BVENDOR(_vend), BHND_CHIP_BTYPE(_type)
 
 /** Set the required board vendor, type, and revision within a bhnd_chip_match
  *  instance */
@@ -429,6 +429,9 @@ device_t			 bhnd_match_child(device_t de
 device_t			 bhnd_find_child(device_t dev,
 				     bhnd_devclass_t class, int unit);
 
+device_t			 bhnd_find_bridge_root(device_t dev,
+				     devclass_t bus_class);
+
 const struct bhnd_core_info	*bhnd_match_core(
 				     const struct bhnd_core_info *cores,
 				     u_int num_cores,

Modified: head/sys/dev/bhnd/bhnd_ids.h
==============================================================================
--- head/sys/dev/bhnd/bhnd_ids.h	Tue May 17 06:45:25 2016	(r300014)
+++ head/sys/dev/bhnd/bhnd_ids.h	Tue May 17 06:52:53 2016	(r300015)
@@ -26,8 +26,6 @@
 #ifndef _BHND_BHND_IDS_H_
 #define _BHND_BHND_IDS_H_
 
-
-
 /*
  * JEDEC JEP-106 Core Vendor IDs
  * 
@@ -851,12 +849,12 @@
 #define	BHND_BOARD_BU4785		0x0478
 
 /* 4321 boards */
-#define	BHND_BOARD_BU4321		0x046b
-#define	BHND_BOARD_BU4321E		0x047c
-#define	BHND_BOARD_MP4321		0x046c
-#define	BHND_BOARD_CB2_4321		0x046d
-#define	BHND_BOARD_CB2_4321_AG		0x0066
-#define	BHND_BOARD_MC4321		0x046e
+#define	BHND_BOARD_BCM4321BU		0x046b
+#define	BHND_BOARD_BCM4321BUE		0x047c
+#define	BHND_BOARD_BCM4321MP		0x046c
+#define	BHND_BOARD_BCM4321CB2		0x046d
+#define	BHND_BOARD_BCM4321CB2_AG	0x0066
+#define	BHND_BOARD_BCM4321MC		0x046e
 
 /* 4328 boards */
 #define	BHND_BOARD_BU4328		0x0481

Modified: head/sys/dev/bhnd/bhnd_subr.c
==============================================================================
--- head/sys/dev/bhnd/bhnd_subr.c	Tue May 17 06:45:25 2016	(r300014)
+++ head/sys/dev/bhnd/bhnd_subr.c	Tue May 17 06:52:53 2016	(r300015)
@@ -350,6 +350,56 @@ done:
 }
 
 /**
+ * Walk up the bhnd device hierarchy to locate the root device
+ * to which the bhndb bridge is attached.
+ * 
+ * This can be used from within bhnd host bridge drivers to locate the
+ * actual upstream host device.
+ * 
+ * @param dev A bhnd device.
+ * @param bus_class The expected bus (e.g. "pci") to which the bridge root
+ * should be attached.
+ * 
+ * @retval device_t if a matching parent device is found.
+ * @retval NULL @p dev is not attached via a bhndb bus
+ * @retval NULL no parent device is attached via @p bus_class.
+ */
+device_t
+bhnd_find_bridge_root(device_t dev, devclass_t bus_class)
+{
+	devclass_t	bhndb_class;
+	device_t	parent;
+
+	KASSERT(device_get_devclass(device_get_parent(dev)) == bhnd_devclass,
+	   ("%s not a bhnd device", device_get_nameunit(dev)));
+
+	bhndb_class = devclass_find("bhndb");
+
+	/* Walk the device tree until we hit a bridge */
+	parent = dev;
+	while ((parent = device_get_parent(parent)) != NULL) {
+		if (device_get_devclass(parent) == bhndb_class)
+			break;
+	}
+
+	/* No bridge? */
+	if (parent == NULL)
+		return (NULL);
+
+	/* Search for a parent attached to the expected bus class */
+	while ((parent = device_get_parent(parent)) != NULL) {
+		device_t bus;
+
+		bus = device_get_parent(parent);
+		if (bus != NULL && device_get_devclass(bus) == bus_class)
+			return (parent);
+	}
+
+	/* Not found */
+	return (NULL);
+}
+
+/**
  * Find the first core in @p cores that matches @p desc.
  * 
  * @param cores The table to search.

Modified: head/sys/dev/bhnd/bhndb/bhndb_pci.c
==============================================================================
--- head/sys/dev/bhnd/bhndb/bhndb_pci.c	Tue May 17 06:45:25 2016	(r300014)
+++ head/sys/dev/bhnd/bhndb/bhndb_pci.c	Tue May 17 06:52:53 2016	(r300015)
@@ -37,9 +37,10 @@ __FBSDID("$FreeBSD$");
  * bus (e.g. bcma or siba) via a Broadcom PCI core configured in end-point
  * mode.
  * 
- * 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.
+ * This driver handles all initial generic host-level PCI interactions with a
+ * PCI/PCIe bridge core operating in endpoint mode. Once the bridged bhnd(4)
+ * bus has been enumerated, this driver works in tandem with a core-specific
+ * bhnd_pci_hostb driver to manage the PCI core.
  */
 
 #include <sys/param.h>
@@ -482,6 +483,35 @@ bhndb_pci_populate_board_info(device_t d
 
 	sc = device_get_softc(dev);
 
+	/* 
+	 * On a subset of Apple BCM4360 modules, always prefer the
+	 * PCI subdevice to the SPROM-supplied boardtype.
+	 * 
+	 * TODO:
+	 * 
+	 * Broadcom's own drivers implement this override, and then later use
+	 * the remapped BCM4360 board type to determine the required
+	 * board-specific workarounds.
+	 * 
+	 * Without access to this hardware, it's unclear why this mapping
+	 * is done, and we must do the same. If we can survey the hardware
+	 * in question, it may be possible to replace this behavior with
+	 * explicit references to the SPROM-supplied boardtype(s) in our
+	 * quirk definitions.
+	 */
+	if (pci_get_subvendor(sc->parent) == PCI_VENDOR_APPLE) {
+		switch (info->board_type) {
+		case BHND_BOARD_BCM94360X29C:
+		case BHND_BOARD_BCM94360X29CP2:
+		case BHND_BOARD_BCM94360X51:
+		case BHND_BOARD_BCM94360X51P2:
+			info->board_type = 0;	/* allow override below */
+			break;
+		default:
+			break;
+		}
+	}
+
 	/* If NVRAM did not supply vendor/type info, provide the PCI
 	 * subvendor/subdevice values. */
 	if (info->board_vendor == 0)
@@ -560,10 +590,6 @@ bhndb_disable_pci_clocks(struct bhndb_pc
 	if (sc->pci_devclass != BHND_DEVCLASS_PCI)
 		return (0);
 
-	// TODO: Check board flags for BFL2_XTALBUFOUTEN?
-	// TODO: Check PCI core revision?
-	// TODO: Switch to 'slow' clock?
-
 	/* Fetch current config */
 	gpio_out = pci_read_config(sc->parent, BHNDB_PCI_GPIO_OUT, 4);
 	gpio_en = pci_read_config(sc->parent, BHNDB_PCI_GPIO_OUTEN, 4);
@@ -601,6 +627,7 @@ DEFINE_CLASS_1(bhndb, bhndb_pci_driver, 
 
 MODULE_VERSION(bhndb_pci, 1);
 MODULE_DEPEND(bhndb_pci, bhnd_pci_hostb, 1, 1, 1);
+MODULE_DEPEND(bhndb_pci, bhnd_pcie2_hostb, 1, 1, 1);
 MODULE_DEPEND(bhndb_pci, pci, 1, 1, 1);
 MODULE_DEPEND(bhndb_pci, bhndb, 1, 1, 1);
 MODULE_DEPEND(bhndb_pci, bhnd, 1, 1, 1);

Modified: head/sys/dev/bhnd/bhndb/bhndb_pcireg.h
==============================================================================
--- head/sys/dev/bhnd/bhndb/bhndb_pcireg.h	Tue May 17 06:45:25 2016	(r300014)
+++ head/sys/dev/bhnd/bhndb/bhndb_pcireg.h	Tue May 17 06:52:53 2016	(r300015)
@@ -29,13 +29,13 @@
  * 
  * = MAJOR CORE REVISIONS =
  * 
- * There have been four revisions to the BAR0/BAR1 memory mappings used
+ * There have been four revisions to the BAR0 memory mappings used
  * in BHND PCI/PCIE bridge cores:
  * 
  * == PCI_V0 ==
  * Applies to:
  * -  PCI (cid=0x804, revision <= 12)
- * BAR size: 8KB
+ * BAR0 size: 8KB
  * Address Map:
  *	[offset+  size]	type	description
  * 	[0x0000+0x1000]	dynamic mapped backplane address space (window 0).
@@ -46,7 +46,7 @@
  * Applies to:
  * -  PCI (cid=0x804, revision >= 13)
  * -  PCIE (cid=0x820) with ChipCommon (revision <= 31)
- * BAR size: 16KB
+ * BAR0 size: 16KB
  * Address Map:
  *	[offset+  size]	type	description
  *	[0x0000+0x1000]	dynamic	mapped backplane address space (window 0).
@@ -57,7 +57,7 @@
  * == PCI_V2 ==
  * Applies to:
  * - PCIE (cid=0x820) with ChipCommon (revision >= 32)
- * BAR size: 16KB
+ * BAR0 size: 16KB
  * Address Map:
  *	[offset+  size]	type	description
  *	[0x0000+0x1000]	dynamic	mapped backplane address space (window 0).
@@ -68,7 +68,7 @@
  * == PCI_V3 ==
  * Applies to:
  * - PCIE Gen 2 (cid=0x83c)
- * BAR size: 32KB?
+ * BAR0 size: 32KB
  * Address Map:
  *	[offset+  size]	type	description
  *	[0x0000+0x1000]	dynamic	mapped backplane address space (window 0).
@@ -76,6 +76,12 @@
  *	[0x2000+0x1000]	fixed	pci/pcie core registers
  *	[0x3000+0x1000]	fixed	chipcommon core registers
  *	[???]
+ * BAR1 size: varies
+ * Address Map:
+ *	[offset+  size]	type	description
+ *	[0x0000+0x????]	fixed	ARM tightly-coupled memory (TCM).
+ *				While fullmac chipsets provided a fixed
+ *				4KB mapping, newer devices will vary.
  * 
  * = MINOR CORE REVISIONS =
  * 
@@ -86,28 +92,6 @@
  * == PCI/PCIE Cores Revision >= 14 ==
  * - Mapped the clock CSR into the PCI config space. Refer to
  *   BHND_PCI_CLK_CTL_ST
- * 
- * = Hardware Bugs =
- * == BAR1 ==
- * 
- * The BHND PCI(e) cores hypothetically support an additional memory mapping
- * of the backplane address space via BAR1, but this appears to be subject
- * to a hardware bug in which BAR1 is initially configured with a 4 byte
- * length.
- * 
- * A work-around for this bug may be possible by writing to the PCI core's
- * BAR1 config register (0x4e0), but this requires further research -- I've
- * found three sources for information on the BAR1 PCI core configuration that
- * may be relevant:
- * 	- The QLogix NetXTreme 10GB PCIe NIC seems to use the same PCIE
- * 	  core IP block as is used in other BHND devices. The bxe(4) driver
- * 	  contains example initialization code and register constants
- * 	  that may apply (e.g. GRC_BAR2_CONFIG/PCI_CONFIG_2_BAR2_SIZE).
- * 	- The publicly available Broadcom BCM440X data sheet (440X-PG02-R)
- * 	  appears to (partially) document a Broadcom PCI(e) core that has a
- * 	  seemingly compatible programming model.
- * 	- The Android bcmdhd driver sources include a possible work-around
- *	  implementation (writing to 0x4e0) in dhd_pcie.c
  */
 
 /* Common PCI/PCIE Config Registers */
@@ -181,12 +165,11 @@
 #define	BHNDB_PCI_V2_BAR0_CCREGS_OFFSET	0x3000	/* bar0 + 12K accesses chipc core registers */
 #define	BHNDB_PCI_V2_BAR0_CCREGS_SIZE	0x1000
 
-/* PCI_V3 */
+/* PCI_V3 (PCIe-G2) */
 #define	BHNDB_PCI_V3_BAR0_WIN0_CONTROL	0x80	/* backplane address space accessed by BAR0/WIN0 */
-#define	BHNDB_PCI_V3_BAR1_WIN0_CONTROL	0x84	/* backplane address space accessed by BAR1/WIN0. */
 #define BHNDB_PCI_V3_BAR0_WIN1_CONTROL	0x70	/* backplane address space accessed by BAR0/WIN1 */
 
-#define	BHNDB_PCI_V3_BAR0_SIZE		0x8000	/* 32KB BAR0 (?) */
+#define	BHNDB_PCI_V3_BAR0_SIZE		0x8000	/* 32KB BAR0 */
 #define	BHNDB_PCI_V3_BAR0_WIN0_OFFSET	0x0	/* bar0 + 0x0 accesses configurable 4K region of backplane address space */
 #define	BHNDB_PCI_V3_BAR0_WIN0_SIZE	0x1000
 #define	BHNDB_PCI_V3_BAR0_WIN1_OFFSET	0x1000	/* bar0 + 4K accesses second 4K window */

Modified: head/sys/dev/bhnd/cores/chipc/bhnd_chipc_if.m
==============================================================================
--- head/sys/dev/bhnd/cores/chipc/bhnd_chipc_if.m	Tue May 17 06:45:25 2016	(r300014)
+++ head/sys/dev/bhnd/cores/chipc/bhnd_chipc_if.m	Tue May 17 06:52:53 2016	(r300015)
@@ -43,4 +43,23 @@ INTERFACE bhnd_chipc;
  */
 METHOD bhnd_nvram_src_t nvram_src {
 	device_t dev;
-}
\ No newline at end of file
+}
+
+/**
+ * Write @p value with @p mask directly to the chipctrl register.
+ *
+ * @param dev A bhnd(4) ChipCommon device.
+ * @param value The value to write.
+ * @param mask The mask of bits to be written from @p value.
+ *
+ * Drivers should only use function for functionality that is not
+ * available via another bhnd_chipc() function.
+ *
+ * Currently, the only known valid use-case is in implementing a hardware
+ * work-around for the BCM4321 PCIe rev7 core revision.
+ */
+METHOD void write_chipctrl {
+	device_t dev;
+	uint32_t value;
+	uint32_t mask;
+}

Modified: head/sys/dev/bhnd/cores/chipc/chipc.c
==============================================================================
--- head/sys/dev/bhnd/cores/chipc/chipc.c	Tue May 17 06:45:25 2016	(r300014)
+++ head/sys/dev/bhnd/cores/chipc/chipc.c	Tue May 17 06:52:53 2016	(r300015)
@@ -489,20 +489,38 @@ chipc_nvram_setvar(device_t dev, const c
 	return (ENODEV);
 }
 
+static void
+chipc_write_chipctrl(device_t dev, uint32_t value, uint32_t mask)
+{
+	struct chipc_softc	*sc;
+	uint32_t		 cctrl;
+
+	sc = device_get_softc(dev);
+
+	CHIPC_LOCK(sc);
+
+	cctrl = bhnd_bus_read_4(sc->core, CHIPC_CHIPCTRL);
+	cctrl = (cctrl & ~mask) | (value | mask);
+	bhnd_bus_write_4(sc->core, CHIPC_CHIPCTRL, cctrl);
+
+	CHIPC_UNLOCK(sc);
+}
+
 static device_method_t chipc_methods[] = {
 	/* Device interface */
-	DEVMETHOD(device_probe,		chipc_probe),
-	DEVMETHOD(device_attach,	chipc_attach),
-	DEVMETHOD(device_detach,	chipc_detach),
-	DEVMETHOD(device_suspend,	chipc_suspend),
-	DEVMETHOD(device_resume,	chipc_resume),
+	DEVMETHOD(device_probe,			chipc_probe),
+	DEVMETHOD(device_attach,		chipc_attach),
+	DEVMETHOD(device_detach,		chipc_detach),
+	DEVMETHOD(device_suspend,		chipc_suspend),
+	DEVMETHOD(device_resume,		chipc_resume),
 	
 	/* ChipCommon interface */
-	DEVMETHOD(bhnd_chipc_nvram_src,	chipc_nvram_src),
+	DEVMETHOD(bhnd_chipc_nvram_src,		chipc_nvram_src),
+	DEVMETHOD(bhnd_chipc_write_chipctrl,	chipc_write_chipctrl),
 
 	/* NVRAM interface */
-	DEVMETHOD(bhnd_nvram_getvar,	chipc_nvram_getvar),
-	DEVMETHOD(bhnd_nvram_setvar,	chipc_nvram_setvar),
+	DEVMETHOD(bhnd_nvram_getvar,		chipc_nvram_getvar),
+	DEVMETHOD(bhnd_nvram_setvar,		chipc_nvram_setvar),
 
 	DEVMETHOD_END
 };

Modified: head/sys/dev/bhnd/cores/pci/bhnd_pci.c
==============================================================================
--- head/sys/dev/bhnd/cores/pci/bhnd_pci.c	Tue May 17 06:45:25 2016	(r300014)
+++ head/sys/dev/bhnd/cores/pci/bhnd_pci.c	Tue May 17 06:52:53 2016	(r300015)
@@ -429,8 +429,7 @@ bhnd_pcie_mdio_read_ext(struct bhnd_pci_
     int reg)
 {
 	uint32_t	cmd;
-	uint16_t	blk, val;
-	uint8_t		blk_reg;
+	uint16_t	val;
 	int		error;
 
 	if (devaddr == MDIO_DEVADDR_NONE)
@@ -438,27 +437,23 @@ bhnd_pcie_mdio_read_ext(struct bhnd_pci_
 
 	/* 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))
+	if (!(sc->quirks & BHND_PCI_QUIRK_SD_C22_EXTADDR) ||
+	    phy != BHND_PCIE_PHYADDR_SD)
+	{
 		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);
+	cmd = BHND_PCIE_MDIODATA_ADDR(phy, BHND_PCIE_SD_ADDREXT) | devaddr;
 	if ((error = bhnd_pcie_mdio_cmd_write(sc, cmd)))
 		goto cleanup;
 
 	/* Issue the read */
-	cmd = BHND_PCIE_MDIODATA_ADDR(phy, blk_reg);
+	cmd = BHND_PCIE_MDIODATA_ADDR(phy, reg);
 	error = bhnd_pcie_mdio_cmd_read(sc, cmd, &val);
 
 cleanup:
@@ -476,8 +471,6 @@ bhnd_pcie_mdio_write_ext(struct bhnd_pci
     int reg, int val)
 {	
 	uint32_t	cmd;
-	uint16_t	blk;
-	uint8_t		blk_reg;
 	int		error;
 
 	if (devaddr == MDIO_DEVADDR_NONE)
@@ -485,27 +478,23 @@ bhnd_pcie_mdio_write_ext(struct bhnd_pci
 
 	/* 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))
+	if (!(sc->quirks & BHND_PCI_QUIRK_SD_C22_EXTADDR) ||
+	    phy != BHND_PCIE_PHYADDR_SD)
+	{
 		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);
+	cmd = BHND_PCIE_MDIODATA_ADDR(phy, BHND_PCIE_SD_ADDREXT) | devaddr;
 	if ((error = bhnd_pcie_mdio_cmd_write(sc, cmd)))
 		goto cleanup;
 
 	/* Issue the write */
-	cmd = BHND_PCIE_MDIODATA_ADDR(phy, blk_reg) |
+	cmd = BHND_PCIE_MDIODATA_ADDR(phy, reg) |
 	    (val & BHND_PCIE_MDIODATA_DATA_MASK);
 	error = bhnd_pcie_mdio_cmd_write(sc, cmd);
 

Modified: head/sys/dev/bhnd/cores/pci/bhnd_pci_hostb.c
==============================================================================
--- head/sys/dev/bhnd/cores/pci/bhnd_pci_hostb.c	Tue May 17 06:45:25 2016	(r300014)
+++ head/sys/dev/bhnd/cores/pci/bhnd_pci_hostb.c	Tue May 17 06:52:53 2016	(r300015)
@@ -56,28 +56,43 @@ __FBSDID("$FreeBSD$");
 
 #include <dev/bhnd/bhnd.h>
 
-#include "bhnd_pcireg.h"
-#include "bhnd_pci_hostbvar.h"
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
 
-#define	BHND_PCI_ASSERT_QUIRK(_sc, _name)	\
-    KASSERT((_sc)->quirks & (_name), ("quirk " __STRING(_name) " not set"))
+#include <dev/bhnd/cores/chipc/chipc.h>
+#include <dev/bhnd/cores/chipc/chipcreg.h>
 
-#define	BHND_PCI_DEV(_core, _quirks, _chip_quirks)		\
-	BHND_DEVICE(_core, "", _quirks, _chip_quirks, BHND_DF_HOSTB)
+#include "bhnd_pcireg.h"
+#include "bhnd_pci_hostbvar.h"
 
 static const struct bhnd_device_quirk bhnd_pci_quirks[];
 static const struct bhnd_device_quirk bhnd_pcie_quirks[];
+static const struct bhnd_chip_quirk bhnd_pci_chip_quirks[];
 static const struct bhnd_chip_quirk bhnd_pcie_chip_quirks[];
 
+/* Device driver work-around variations */
+typedef enum {
+	BHND_PCI_WAR_ATTACH,	/**< apply attach workarounds */
+	BHND_PCI_WAR_RESUME,	/**< apply resume workarounds */
+	BHND_PCI_WAR_SUSPEND,	/**< apply suspend workarounds */
+	BHND_PCI_WAR_DETACH	/**< apply detach workarounds */
+} bhnd_pci_war_state;
+
 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);
+static int	bhnd_pci_wars_hwup(struct bhnd_pcihb_softc *sc,
+		    bhnd_pci_war_state state);
+static int	bhnd_pci_wars_hwdown(struct bhnd_pcihb_softc *sc,
+		    bhnd_pci_war_state state);
 
 /*
  * device/quirk tables
  */
+
+#define	BHND_PCI_DEV(_core, _quirks, _chip_quirks)		\
+	BHND_DEVICE(_core, "", _quirks, _chip_quirks, BHND_DF_HOSTB)
+
 static const struct bhnd_device bhnd_pci_devs[] = {
-	BHND_PCI_DEV(PCI,	bhnd_pci_quirks,	NULL),
+	BHND_PCI_DEV(PCI,	bhnd_pci_quirks,	bhnd_pci_chip_quirks),
 	BHND_PCI_DEV(PCIE,	bhnd_pcie_quirks,	bhnd_pcie_chip_quirks),
 	BHND_DEVICE_END
 };
@@ -89,12 +104,22 @@ static const struct bhnd_device_quirk bh
 	BHND_DEVICE_QUIRK_END
 };
 
+static const struct bhnd_chip_quirk bhnd_pci_chip_quirks[] = {
+	/* BCM4321CB2 boards that require 960ns latency timer override */
+	{{ BHND_CHIP_BTYPE(BCM4321CB2) },
+		BHND_PCI_QUIRK_960NS_LATTIM_OVR },
+	{{ BHND_CHIP_BTYPE(BCM4321CB2_AG) },
+		BHND_PCI_QUIRK_960NS_LATTIM_OVR },
+
+	BHND_CHIP_QUIRK_END
+};
+
 static const struct bhnd_device_quirk bhnd_pcie_quirks[] = {
 	{ BHND_HWREV_EQ		(0),	BHND_PCIE_QUIRK_SDR9_L0s_HANG },
-	{ BHND_HWREV_RANGE	(0, 1),	BHND_PCIE_QUIRK_UR_STATUS_FIX },
+	{ BHND_HWREV_RANGE	(0,1),	BHND_PCIE_QUIRK_UR_STATUS_FIX },
 	{ BHND_HWREV_EQ		(1),	BHND_PCIE_QUIRK_PCIPM_REQEN },
 
-	{ BHND_HWREV_RANGE	(3, 5),	BHND_PCIE_QUIRK_ASPM_OVR |
+	{ BHND_HWREV_RANGE	(3,5),	BHND_PCIE_QUIRK_ASPM_OVR |
 					BHND_PCIE_QUIRK_SDR9_POLARITY |
 					BHND_PCIE_QUIRK_SDR9_NO_FREQRETRY },
 
@@ -102,37 +127,50 @@ static const struct bhnd_device_quirk bh
 	{ 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_HWREV_LTE	(17),	BHND_PCIE_QUIRK_MAX_MRRS_128 },
+
 	BHND_DEVICE_QUIRK_END
 };
 
 static const struct bhnd_chip_quirk bhnd_pcie_chip_quirks[] = {
 	/* Apple boards on which BHND_BFL2_PCIEWAR_OVR should be assumed
 	 * to be set. */
-	{{ BHND_CHIP_BVENDOR		(PCI_VENDOR_APPLE),
-	   BHND_CHIP_SROMREV		(HWREV_EQ(4)),
-	   BHND_CHIP_BREV		(HWREV_LTE(0x71)) },
-	   BHND_PCIE_QUIRK_BFL2_PCIEWAR_EN },
+	{{ BHND_CHIP_BVENDOR	(PCI_VENDOR_APPLE),
+	   BHND_CHIP_SROMREV	(HWREV_EQ(4)),
+	   BHND_CHIP_BREV	(HWREV_LTE(0x71)) },
+		BHND_PCIE_QUIRK_BFL2_PCIEWAR_EN		},
+
+	/* Apple BCM4322 boards that require 700mV SerDes TX drive strength. */
+	{{ BHND_CHIP_BVT	(PCI_VENDOR_APPLE,	BCM94322X9)	},
+		BHND_PCIE_QUIRK_SERDES_TXDRV_700MV	},
+
+	/* Apple BCM4331 board-specific quirks */
+#define	BHND_APPLE_4331_QUIRK(_board, ...)				\
+	{{ BHND_CHIP_ID		(4331),					\
+	   BHND_CHIP_BVT	(PCI_VENDOR_APPLE,	_board), },	\
+		__VA_ARGS__ }
+
+	BHND_APPLE_4331_QUIRK(BCM94331X19,
+	    BHND_PCIE_QUIRK_SERDES_TXDRV_MAX|BHND_PCIE_QUIRK_DEFAULT_MRRS_512),
+
+	BHND_APPLE_4331_QUIRK(BCM94331X28,
+	    BHND_PCIE_QUIRK_SERDES_TXDRV_MAX|BHND_PCIE_QUIRK_DEFAULT_MRRS_512),
+	BHND_APPLE_4331_QUIRK(BCM94331X28B, BHND_PCIE_QUIRK_DEFAULT_MRRS_512),
+	  
+	BHND_APPLE_4331_QUIRK(BCM94331X29B,
+	    BHND_PCIE_QUIRK_SERDES_TXDRV_MAX|BHND_PCIE_QUIRK_DEFAULT_MRRS_512),
+
+	BHND_APPLE_4331_QUIRK(BCM94331X19C,
+	    BHND_PCIE_QUIRK_SERDES_TXDRV_MAX|BHND_PCIE_QUIRK_DEFAULT_MRRS_512),
+	    
+	BHND_APPLE_4331_QUIRK(BCM94331X29D, BHND_PCIE_QUIRK_DEFAULT_MRRS_512),
+	BHND_APPLE_4331_QUIRK(BCM94331X33, BHND_PCIE_QUIRK_DEFAULT_MRRS_512),
+#undef BHND_APPLE_4331_QUIRK
 
 	BHND_CHIP_QUIRK_END
 };
 
-// Quirk handling TODO
-// WARs for the following are not yet implemented:
-// - BHND_PCIE_QUIRK_ASPM_OVR
-// - BHND_PCIE_QUIRK_BFL2_PCIEWAR_EN
-// - 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)		\
@@ -159,6 +197,13 @@ static const struct bhnd_chip_quirk bhnd
 #define	BHND_PCI_MDIO_WRITE(_sc, _phy, _reg, _val)		\
 	bhnd_pcie_mdio_write(BHND_PCI_SOFTC(_sc), (_phy), (_reg), (_val))
 
+#define	BHND_PCI_MDIO_READ_EXT(_sc, _phy, _devaddr, _reg)		\
+	bhnd_pcie_mdio_read_ext(BHND_PCI_SOFTC(_sc), (_phy), (_devaddr), (_reg))
+
+#define	BHND_PCI_MDIO_WRITE_EXT(_sc, _phy, _devaddr, _reg, _val)	\
+	bhnd_pcie_mdio_write_ext(BHND_PCI_SOFTC(_sc), (_phy),		\
+	    (_devaddr), (_reg), (_val))
+
 #define	BPCI_REG_SET(_regv, _attr, _val)	\
 	BHND_PCI_REG_SET((_regv), BHND_ ## _attr, (_val))
 
@@ -180,26 +225,34 @@ bhnd_pci_hostb_attach(device_t dev)
 	int			 error;
 
 	sc = device_get_softc(dev);
+	sc->dev = dev;
 	sc->quirks = bhnd_device_quirks(dev, bhnd_pci_devs,
 	    sizeof(bhnd_pci_devs[0]));
 
+	/* Find the host PCI bridge device */
+	sc->pci_dev = bhnd_find_bridge_root(dev, devclass_find("pci"));
+	if (sc->pci_dev == NULL) {
+		device_printf(dev, "parent pci bridge device not found\n");
+		return (ENXIO);
+	}
+
+	/* Common setup */
 	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);
-	}
+	if ((error = bhnd_pci_wars_early_once(sc)))
+		goto failed;
 
 	/* Apply attach/resume work-arounds */
-	if ((error = bhnd_pci_wars_hwup(sc))) {
-		bhnd_pci_generic_detach(dev);
-		return (error);
-	}
-
+	if ((error = bhnd_pci_wars_hwup(sc, BHND_PCI_WAR_ATTACH)))
+		goto failed;
 
 	return (0);
+	
+failed:
+	bhnd_pci_generic_detach(dev);
+	return (error);
 }
 
 static int
@@ -211,7 +264,7 @@ bhnd_pci_hostb_detach(device_t dev)
 	sc = device_get_softc(dev);
 
 	/* Apply suspend/detach work-arounds */
-	if ((error = bhnd_pci_wars_hwdown(sc)))
+	if ((error = bhnd_pci_wars_hwdown(sc, BHND_PCI_WAR_DETACH)))
 		return (error);
 
 	return (bhnd_pci_generic_detach(dev));
@@ -226,7 +279,7 @@ bhnd_pci_hostb_suspend(device_t dev)
 	sc = device_get_softc(dev);
 
 	/* Apply suspend/detach work-arounds */
-	if ((error = bhnd_pci_wars_hwdown(sc)))
+	if ((error = bhnd_pci_wars_hwdown(sc, BHND_PCI_WAR_SUSPEND)))
 		return (error);
 
 	return (bhnd_pci_generic_suspend(dev));
@@ -244,7 +297,7 @@ bhnd_pci_hostb_resume(device_t dev)
 		return (error);
 
 	/* Apply attach/resume work-arounds */
-	if ((error = bhnd_pci_wars_hwup(sc))) {
+	if ((error = bhnd_pci_wars_hwup(sc, BHND_PCI_WAR_RESUME))) {
 		bhnd_pci_generic_detach(dev);
 		return (error);
 	}
@@ -263,6 +316,36 @@ bhnd_pci_hostb_resume(device_t dev)
 static int
 bhnd_pci_wars_early_once(struct bhnd_pcihb_softc *sc)
 {
+	int error;
+
+	/* Set PCI latency timer */
+	if (sc->quirks & BHND_PCI_QUIRK_960NS_LATTIM_OVR) {
+		pci_write_config(sc->pci_dev, PCIR_LATTIMER, 0x20 /* 960ns */,
+		    1); 
+	}
+
+	/* Determine whether ASPM/CLKREQ should be forced on, or forced off. */
+	if (sc->quirks & BHND_PCIE_QUIRK_ASPM_OVR) {
+		struct bhnd_board_info	board;
+		bool			aspm_en;
+
+		/* Fetch board info */
+		if ((error = bhnd_read_board_info(sc->dev, &board)))
+			return (error);
+		
+		/* Check board flags */
+		aspm_en = true;
+		if (board.board_flags2 & BHND_BFL2_PCIEWAR_OVR)
+			aspm_en = false;
+
+		/* Early Apple devices did not (but should have) set
+		 * BHND_BFL2_PCIEWAR_OVR in SPROM. */
+		if (sc->quirks & BHND_PCIE_QUIRK_BFL2_PCIEWAR_EN)
+			aspm_en = false;
+
+		sc->aspm_quirk_override.aspm_en = aspm_en;
+	}
+
 	/* Determine correct polarity by observing the attach-time PCIe PHY
 	 * link status. This is used later to reset/force the SerDes
 	 * polarity */
@@ -270,12 +353,23 @@ bhnd_pci_wars_early_once(struct bhnd_pci
 		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;
 	}
 
+	/* Override maximum read request size */
+	if (bhnd_get_class(sc->dev) == BHND_DEVCLASS_PCIE) {
+		int	msize;
+
+		msize = 128; /* compatible with all PCIe-G1 core revisions */
+		if (sc->quirks & BHND_PCIE_QUIRK_DEFAULT_MRRS_512)
+			msize = 512;
+
+		if (pci_set_max_read_req(sc->pci_dev, msize) == 0)
+			panic("set mrrs on non-PCIe device");
+	}
+
 	return (0);
 }
 
@@ -284,7 +378,7 @@ bhnd_pci_wars_early_once(struct bhnd_pci
  * of the bridge device.
  */
 static int
-bhnd_pci_wars_hwup(struct bhnd_pcihb_softc *sc)
+bhnd_pci_wars_hwup(struct bhnd_pcihb_softc *sc, bhnd_pci_war_state state)
 {
 	/* Note that the order here matters; these work-arounds
 	 * should not be re-ordered without careful review of their
@@ -407,6 +501,47 @@ bhnd_pci_wars_hwup(struct bhnd_pcihb_sof
 		BHND_PCI_PROTO_WRITE_4(sc, BHND_PCIE_DLLP_PMTHRESHREG, pmt);
 	}
 
+	/* Override ASPM/ECPM settings in SPROM shadow and PCIER_LINK_CTL */
+	if (sc->quirks & BHND_PCIE_QUIRK_ASPM_OVR) {
+		bus_size_t	reg;
+		uint16_t	cfg;
+
+		/* Set ASPM L1/L0s flags in SPROM shadow */
+		reg = BHND_PCIE_SPROM_SHADOW + BHND_PCIE_SRSH_ASPM_OFFSET;
+		cfg = BHND_PCI_READ_2(sc, reg);
+
+		if (sc->aspm_quirk_override.aspm_en)
+			cfg |= BHND_PCIE_SRSH_ASPM_ENB;
+		else
+			cfg &= ~BHND_PCIE_SRSH_ASPM_ENB;
+		
+		BHND_PCI_WRITE_2(sc, reg, cfg);
+
+
+		/* Set ASPM/ECPM (CLKREQ) flags in PCIe link control register */
+		cfg = pcie_read_config(sc->pci_dev, PCIER_LINK_CTL, 2);
+
+		if (sc->aspm_quirk_override.aspm_en)
+			cfg |= PCIEM_LINK_CTL_ASPMC;
+		else
+			cfg &= ~PCIEM_LINK_CTL_ASPMC;
+
+		cfg &= ~PCIEM_LINK_CTL_ECPM;		/* CLKREQ# */
+
+		pcie_write_config(sc->pci_dev, PCIER_LINK_CTL, cfg, 2); 
+
+		/* Set CLKREQ (ECPM) flags in SPROM shadow */
+		reg = BHND_PCIE_SPROM_SHADOW + BHND_PCIE_SRSH_CLKREQ_OFFSET_R5;
+		cfg = BHND_PCI_READ_2(sc, reg);
+		
+		if (sc->aspm_quirk_override.aspm_en)
+			cfg |= BHND_PCIE_SRSH_CLKREQ_ENB;
+		else
+			cfg &= ~BHND_PCIE_SRSH_CLKREQ_ENB;
+
+		BHND_PCI_WRITE_2(sc, reg, cfg);
+	}
+
 	/* Enable L23READY_EXIT_NOPRST if not already set in SPROM. */
 	if (sc->quirks & BHND_PCIE_QUIRK_SPROM_L23_PCI_RESET) {
 		bus_size_t	reg;
@@ -423,6 +558,54 @@ bhnd_pci_wars_hwup(struct bhnd_pcihb_sof
 		}
 	}
 
+	/* Disable SerDes PLL down */
+	if (sc->quirks & BHND_PCIE_QUIRK_SERDES_NOPLLDOWN) {
+		device_t	bhnd, chipc;
+		bus_size_t	reg;
+		
+		bhnd = device_get_parent(sc->dev);
+		chipc = bhnd_find_child(bhnd, BHND_DEVCLASS_CC, 0);
+		KASSERT(chipc != NULL, ("missing chipcommon device"));
+
+		/* Write SerDes PLL disable flag to the ChipCommon core */
+		BHND_CHIPC_WRITE_CHIPCTRL(chipc, CHIPCTRL_4321_PLL_DOWN,
+		    CHIPCTRL_4321_PLL_DOWN);
+
+		/* Clear SPROM shadow backdoor register */
+		reg = BHND_PCIE_SPROM_SHADOW + BHND_PCIE_SRSH_BD_OFFSET;
+		BHND_PCI_WRITE_2(sc, reg, 0);
+	}
+
+	/* Adjust TX drive strength and pre-emphasis coefficient */
+	if (sc->quirks & BHND_PCIE_QUIRK_SERDES_TXDRV_ADJUST) {
+		uint16_t txdrv;
+
+		/* Fetch current TX driver parameters */
+		txdrv = BHND_PCI_MDIO_READ_EXT(sc, BHND_PCIE_PHYADDR_SD,
+		    BHND_PCIE_SD_REGS_TX0, BHND_PCIE_SD_TX_DRIVER);
+
+		/* Set 700mV drive strength */
+		if (sc->quirks & BHND_PCIE_QUIRK_SERDES_TXDRV_700MV) {
+			txdrv = BPCI_REG_SET(txdrv, PCIE_SD_TX_DRIVER_P2_COEFF,
+			    BHND_PCIE_APPLE_TX_P2_COEFF_700MV);
+
+			txdrv = BPCI_REG_SET(txdrv, PCIE_SD_TX_DRIVER_IDRIVER,
+			    BHND_PCIE_APPLE_TX_IDRIVER_700MV);
+		}
+
+		/* ... or, set max drive strength */
+		if (sc->quirks & BHND_PCIE_QUIRK_SERDES_TXDRV_MAX) {
+			txdrv = BPCI_REG_SET(txdrv, PCIE_SD_TX_DRIVER_P2_COEFF,
+			    BHND_PCIE_APPLE_TX_P2_COEFF_MAX);
+			
+			txdrv = BPCI_REG_SET(txdrv, PCIE_SD_TX_DRIVER_IDRIVER,
+			    BHND_PCIE_APPLE_TX_IDRIVER_MAX);
+		}
+
+		BHND_PCI_MDIO_WRITE_EXT(sc, BHND_PCIE_PHYADDR_SD,
+		    BHND_PCIE_SD_REGS_TX0, BHND_PCIE_SD_TX_DRIVER, txdrv);
+	}
+
 	return (0);
 }
 
@@ -431,8 +614,8 @@ bhnd_pci_wars_hwup(struct bhnd_pcihb_sof
  * of the bridge device.
  */
 static int
-bhnd_pci_wars_hwdown(struct bhnd_pcihb_softc *sc)
-{	
+bhnd_pci_wars_hwdown(struct bhnd_pcihb_softc *sc, bhnd_pci_war_state state)
+{
 	/* 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 */
@@ -443,6 +626,19 @@ bhnd_pci_wars_hwdown(struct bhnd_pcihb_s
 		BHND_PCI_PROTO_WRITE_4(sc, BHND_PCIE_DLLP_PMTHRESHREG, pmt);
 	}
 
+	/* Enable CLKREQ (ECPM). If suspending, also disable ASPM L1 entry */
+	if (sc->quirks & BHND_PCIE_QUIRK_ASPM_OVR) {
+		uint16_t	lcreg;
+
+		lcreg = pcie_read_config(sc->pci_dev, PCIER_LINK_CTL, 2);
+
+		lcreg |= PCIEM_LINK_CTL_ECPM;	/* CLKREQ# */
+		if (state == BHND_PCI_WAR_SUSPEND)
+			lcreg &= ~PCIEM_LINK_CTL_ASPMC_L1;
+
+		pcie_write_config(sc->pci_dev, PCIER_LINK_CTL, lcreg, 2);
+	}
+
 	return (0);
 }
 
@@ -456,10 +652,9 @@ static device_method_t bhnd_pci_hostb_me
 	DEVMETHOD_END
 };
 
-DEFINE_CLASS_1(bhnd_pci_hostb, bhnd_pci_hostb_driver, bhnd_pci_hostb_methods, 
+DEFINE_CLASS_1(bhnd_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);
+DRIVER_MODULE(bhnd_pci_hostb, bhnd, bhnd_pci_hostb_driver, bhnd_hostb_devclass, 0, 0);
 
 MODULE_VERSION(bhnd_pci_hostb, 1);
 MODULE_DEPEND(bhnd_pci_hostb, bhnd, 1, 1, 1);

Modified: head/sys/dev/bhnd/cores/pci/bhnd_pci_hostbvar.h
==============================================================================
--- head/sys/dev/bhnd/cores/pci/bhnd_pci_hostbvar.h	Tue May 17 06:45:25 2016	(r300014)
+++ head/sys/dev/bhnd/cores/pci/bhnd_pci_hostbvar.h	Tue May 17 06:52:53 2016	(r300015)
@@ -43,7 +43,7 @@
 
 DECLARE_CLASS(bhnd_pci_hostb_driver);
 
-/* 
+/**
  * PCI/PCIe-Gen1 endpoint-mode device quirks
  */
 enum {
@@ -56,7 +56,6 @@ enum {
 	 */
 	BHND_PCI_QUIRK_SBTOPCI2_PREF_BURST	= (1<<1),
 
-
 	/**
 	 * SBTOPCI_RC_READMULTI must be set on the SSB_PCICORE_SBTOPCI2
 	 * register.
@@ -74,18 +73,24 @@ enum {
 	BHND_PCI_QUIRK_CLKRUN_DSBL		= (1<<3),
 
 	/**
+	 * On PCI-attached BCM4321CB* boards, the PCI latency timer must be set
+	 * to 960ns on initial attach.
+	 */
+	BHND_PCI_QUIRK_960NS_LATTIM_OVR		= (1<<4),
+
+	/**
 	 * 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),
+	BHND_PCIE_QUIRK_UR_STATUS_FIX		= (1<<5),
 
 	/**
 	 * PCI-PM power management must be explicitly enabled via
 	 * the data link control register.
 	 */
-	BHND_PCIE_QUIRK_PCIPM_REQEN		= (1<<5),
+	BHND_PCIE_QUIRK_PCIPM_REQEN		= (1<<6),
 
 	/**
 	 * Fix L0s to L0 exit transition on SerDes <= rev9 devices.
@@ -98,46 +103,50 @@ enum {
 	 * filters must be tweaked to ensure the CDR has fully stabilized
 	 * before asserting receive sequencer completion.
 	 */
-	BHND_PCIE_QUIRK_SDR9_L0s_HANG		= (1<<6),
+	BHND_PCIE_QUIRK_SDR9_L0s_HANG		= (1<<7),
 
 	/**
 	 * 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),
+	BHND_PCIE_QUIRK_L1_IDLE_THRESH		= (1<<8),
 	
 	/**
 	 * 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),
+	BHND_PCIE_QUIRK_L1_TIMER_PERF		= (1<<9),
 
 	/**
 	 * ASPM and ECPM settings must be overridden manually.
+	 * Applies to 4311B0/4321B1 chipset revisions.
 	 * 
 	 * The override behavior is controlled by the BHND_BFL2_PCIEWAR_OVR
-	 * flag. If this flag is set, ASPM/CLKREQ should be overridden as

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201605170652.u4H6qsmQ024900>