changeset 12330:e4f9a0025b49

6896094 For Intel 41210 PCIe2PCI Bridge, MPS for F0 and F2 should be initialized at the same time
author Daniel Ice <Daniel.Ice@Sun.COM>
date Thu, 06 May 2010 19:17:59 -0700
parents edd9b6e1b428
children d1dfa74bf947
files usr/src/uts/common/io/pciex/pcie.c usr/src/uts/common/io/pciex/pcieb.c usr/src/uts/common/io/pciex/pcieb.h
diffstat 3 files changed, 112 insertions(+), 7 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/uts/common/io/pciex/pcie.c	Thu May 06 18:50:02 2010 -0700
+++ b/usr/src/uts/common/io/pciex/pcie.c	Thu May 06 19:17:59 2010 -0700
@@ -20,7 +20,7 @@
  */
 
 /*
- * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #include <sys/sysmacros.h>
@@ -594,7 +594,11 @@
 	if (PCIE_IS_PCIE(bus_p)) {
 		/* Setup PCIe device control register */
 		reg16 = PCIE_CAP_GET(16, bus_p, PCIE_DEVCTL);
-		tmp16 = pcie_devctl_default;
+		/* note: MPS/MRRS are initialized in pcie_initchild_mps() */
+		tmp16 = (reg16 & (PCIE_DEVCTL_MAX_READ_REQ_MASK |
+		    PCIE_DEVCTL_MAX_PAYLOAD_MASK)) |
+		    (pcie_devctl_default & ~(PCIE_DEVCTL_MAX_READ_REQ_MASK |
+		    PCIE_DEVCTL_MAX_PAYLOAD_MASK));
 		PCIE_CAP_PUT(16, bus_p, PCIE_DEVCTL, tmp16);
 		PCIE_DBG_CAP(cdip, bus_p, "DEVCTL", 16, PCIE_DEVCTL, reg16);
 
@@ -1784,11 +1788,18 @@
 		int suggested_mrrs, fabric_mps;
 		uint16_t device_mps, device_mps_cap, device_mrrs, dev_ctrl;
 
+		dev_ctrl = PCIE_CAP_GET(16, bus_p, PCIE_DEVCTL);
 		if ((fabric_mps = (PCIE_IS_RP(bus_p) ? bus_p :
-		    PCIE_DIP2BUS(pdip))->bus_mps) < 0)
+		    PCIE_DIP2BUS(pdip))->bus_mps) < 0) {
+			dev_ctrl = (dev_ctrl & ~(PCIE_DEVCTL_MAX_READ_REQ_MASK |
+			    PCIE_DEVCTL_MAX_PAYLOAD_MASK)) |
+			    (pcie_devctl_default &
+			    (PCIE_DEVCTL_MAX_READ_REQ_MASK |
+			    PCIE_DEVCTL_MAX_PAYLOAD_MASK));
+
+			PCIE_CAP_PUT(16, bus_p, PCIE_DEVCTL, dev_ctrl);
 			return (DDI_SUCCESS);
-
-		dev_ctrl = PCIE_CAP_GET(16, bus_p, PCIE_DEVCTL);
+		}
 
 		device_mps_cap = PCIE_CAP_GET(16, bus_p, PCIE_DEVCAP) &
 		    PCIE_DEVCAP_MAX_PAYLOAD_MASK;
--- a/usr/src/uts/common/io/pciex/pcieb.c	Thu May 06 18:50:02 2010 -0700
+++ b/usr/src/uts/common/io/pciex/pcieb.c	Thu May 06 19:17:59 2010 -0700
@@ -57,6 +57,7 @@
 
 /* panic flag */
 int pcieb_die = PF_ERR_FATAL_FLAGS;
+int pcieb_disable_41210_wkarnd = 0;
 
 /* flag to turn on MSI support */
 int pcieb_enable_msi = 1;
@@ -276,6 +277,85 @@
 	return (DDI_PROBE_SUCCESS);
 }
 
+/*
+ * This is a workaround for an undocumented HW erratum with the
+ * multi-function, F0 and F2, Intel 41210 PCIe-to-PCI bridge. When
+ * Fn (cdip) attaches, this workaround is called to initialize Fn's
+ * sibling (sdip) with MPS/MRRS if it isn't already configured.
+ * Doing so prevents a malformed TLP panic.
+ */
+static void
+pcieb_41210_mps_wkrnd(dev_info_t *cdip)
+{
+	dev_info_t *sdip;
+	ddi_acc_handle_t cfg_hdl;
+	uint16_t cdip_dev_ctrl, cdip_mrrs_mps;
+	pcie_bus_t *cdip_bus_p = PCIE_DIP2BUS(cdip);
+
+	/* Get cdip's MPS/MRRS already setup by pcie_initchild_mps() */
+	ASSERT(cdip_bus_p);
+	cdip_dev_ctrl  = PCIE_CAP_GET(16, cdip_bus_p, PCIE_DEVCTL);
+	cdip_mrrs_mps  = cdip_dev_ctrl &
+	    (PCIE_DEVCTL_MAX_READ_REQ_MASK | PCIE_DEVCTL_MAX_PAYLOAD_MASK);
+
+	/* Locate sdip and set its MPS/MRRS when applicable */
+	for (sdip = ddi_get_child(ddi_get_parent(cdip)); sdip;
+	    sdip = ddi_get_next_sibling(sdip)) {
+		uint16_t sdip_dev_ctrl, sdip_mrrs_mps, cap_ptr;
+		uint32_t bus_dev_ven_id;
+
+		if (sdip == cdip || pci_config_setup(sdip, &cfg_hdl)
+		    != DDI_SUCCESS)
+			continue;
+
+		/* must be an Intel 41210 bridge */
+		bus_dev_ven_id = pci_config_get32(cfg_hdl, PCI_CONF_VENID);
+		if (!PCIEB_IS_41210_BRIDGE(bus_dev_ven_id)) {
+			pci_config_teardown(&cfg_hdl);
+			continue;
+		}
+
+		if (PCI_CAP_LOCATE(cfg_hdl, PCI_CAP_ID_PCI_E, &cap_ptr)
+		    != DDI_SUCCESS) {
+			pci_config_teardown(&cfg_hdl);
+			continue;
+		}
+
+		/* get sdip's MPS/MRRS to compare to cdip's */
+		sdip_dev_ctrl = PCI_CAP_GET16(cfg_hdl, NULL, cap_ptr,
+		    PCIE_DEVCTL);
+		sdip_mrrs_mps = sdip_dev_ctrl &
+		    (PCIE_DEVCTL_MAX_READ_REQ_MASK |
+		    PCIE_DEVCTL_MAX_PAYLOAD_MASK);
+
+		/* if sdip already attached then its MPS/MRRS is configured */
+		if (i_ddi_devi_attached(sdip)) {
+			ASSERT(sdip_mrrs_mps == cdip_mrrs_mps);
+			pci_config_teardown(&cfg_hdl);
+			continue;
+		}
+
+		/* otherwise, update sdip's MPS/MRRS if different from cdip's */
+		if (sdip_mrrs_mps != cdip_mrrs_mps) {
+			sdip_dev_ctrl = (sdip_dev_ctrl &
+			    ~(PCIE_DEVCTL_MAX_READ_REQ_MASK |
+			    PCIE_DEVCTL_MAX_PAYLOAD_MASK)) | cdip_mrrs_mps;
+
+			PCI_CAP_PUT16(cfg_hdl, NULL, cap_ptr, PCIE_DEVCTL,
+			    sdip_dev_ctrl);
+		}
+
+		/*
+		 * note: sdip's bus_mps will be updated by
+		 * pcie_initchild_mps()
+		 */
+
+		pci_config_teardown(&cfg_hdl);
+
+		break;
+	}
+}
+
 static int
 pcieb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
 {
@@ -380,6 +460,11 @@
 	if (pcie_init(devi, NULL) != DDI_SUCCESS)
 		goto fail;
 
+	/* Intel PCIe-to-PCI 41210 bridge workaround -- if applicable */
+	if (pcieb_disable_41210_wkarnd == 0 &&
+	    PCIEB_IS_41210_BRIDGE(bus_p->bus_dev_ven_id))
+		pcieb_41210_mps_wkrnd(devi);
+
 	/*
 	 * Initialize interrupt handlers. Ignore return value.
 	 */
--- a/usr/src/uts/common/io/pciex/pcieb.h	Thu May 06 18:50:02 2010 -0700
+++ b/usr/src/uts/common/io/pciex/pcieb.h	Thu May 06 19:17:59 2010 -0700
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #ifndef _SYS_PCIEB_H
@@ -81,6 +80,16 @@
 #define	PCIEB_LADDR(lo, hi) (((uint16_t)(hi) << 16) | (uint16_t)(lo))
 #define	PCIEB_32bit_MEMADDR(addr) (PCIEB_LADDR(0, ((uint16_t)(addr) & 0xFFF0)))
 
+/*
+ * Intel 41210 PCIe-to-PCI Bridge has two Functions F0 and F2:
+ * VID: 0x8086
+ * DID: F0 = 0x340, F2 = 0x341
+ */
+#define	PCIEB_IS_41210_F0(bus_dev_ven_id) (bus_dev_ven_id == 0x3408086)
+#define	PCIEB_IS_41210_F2(bus_dev_ven_id) (bus_dev_ven_id == 0x3418086)
+#define	PCIEB_IS_41210_BRIDGE(bus_dev_ven_id) \
+	(PCIEB_IS_41210_F0(bus_dev_ven_id) || PCIEB_IS_41210_F2(bus_dev_ven_id))
+
 typedef struct {
 	dev_info_t		*pcieb_dip;