Mercurial > illumos > illumos-gate
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;