summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--usr/src/uts/common/io/pciex/pcie.c172
-rw-r--r--usr/src/uts/common/sys/pcie.h26
-rw-r--r--usr/src/uts/common/sys/pcie_impl.h31
3 files changed, 225 insertions, 4 deletions
diff --git a/usr/src/uts/common/io/pciex/pcie.c b/usr/src/uts/common/io/pciex/pcie.c
index 95eaa5837f..611b62a656 100644
--- a/usr/src/uts/common/io/pciex/pcie.c
+++ b/usr/src/uts/common/io/pciex/pcie.c
@@ -938,6 +938,172 @@ pcie_rc_fini_bus(dev_info_t *dip)
}
/*
+ * We need to capture the supported, maximum, and current device speed and
+ * width. The way that this has been done has changed over time.
+ *
+ * Prior to PCIe Gen 3, there were only current and supported speed fields.
+ * These were found in the link status and link capabilities registers of the
+ * PCI express capability. With the change to PCIe Gen 3, the information in the
+ * link capabilities changed to the maximum value. The supported speeds vector
+ * was moved to the link capabilities 2 register.
+ *
+ * Now, a device may not implement some of these registers. To determine whether
+ * or not it's here, we have to do the following. First, we need to check the
+ * revision of the PCI express capability. The link capabilities 2 register did
+ * not exist prior to version 2 of this register.
+ */
+static void
+pcie_capture_speeds(pcie_bus_t *bus_p, pcie_req_id_t bdf, dev_info_t *rcdip)
+{
+ uint16_t vers, status;
+ uint32_t val, cap, cap2;
+
+ if (!PCIE_IS_PCIE(bus_p))
+ return;
+
+ vers = pci_cfgacc_get16(rcdip, bdf, bus_p->bus_pcie_off + PCIE_PCIECAP);
+ if (vers == PCI_EINVAL16)
+ return;
+ vers &= PCIE_PCIECAP_VER_MASK;
+
+ /*
+ * Verify the capability's version.
+ */
+ switch (vers) {
+ case PCIE_PCIECAP_VER_1_0:
+ cap2 = 0;
+ break;
+ case PCIE_PCIECAP_VER_2_0:
+ cap2 = pci_cfgacc_get32(rcdip, bdf, bus_p->bus_pcie_off +
+ PCIE_LINKCAP2);
+ if (cap2 == PCI_EINVAL32)
+ cap2 = 0;
+ break;
+ default:
+ /* Don't try and handle an unknown version */
+ return;
+ }
+
+ status = pci_cfgacc_get16(rcdip, bdf, bus_p->bus_pcie_off +
+ PCIE_LINKSTS);
+ cap = pci_cfgacc_get32(rcdip, bdf, bus_p->bus_pcie_off + PCIE_LINKCAP);
+ if (status == PCI_EINVAL16 || cap == PCI_EINVAL32)
+ return;
+
+ switch (status & PCIE_LINKSTS_SPEED_MASK) {
+ case PCIE_LINKSTS_SPEED_2_5:
+ bus_p->bus_cur_speed = PCIE_LINK_SPEED_2_5;
+ break;
+ case PCIE_LINKSTS_SPEED_5:
+ bus_p->bus_cur_speed = PCIE_LINK_SPEED_5;
+ break;
+ case PCIE_LINKSTS_SPEED_8:
+ bus_p->bus_cur_speed = PCIE_LINK_SPEED_8;
+ break;
+ default:
+ bus_p->bus_cur_speed = PCIE_LINK_SPEED_UNKNOWN;
+ break;
+ }
+
+ switch (status & PCIE_LINKSTS_NEG_WIDTH_MASK) {
+ case PCIE_LINKSTS_NEG_WIDTH_X1:
+ bus_p->bus_cur_width = PCIE_LINK_WIDTH_X1;
+ break;
+ case PCIE_LINKSTS_NEG_WIDTH_X2:
+ bus_p->bus_cur_width = PCIE_LINK_WIDTH_X2;
+ break;
+ case PCIE_LINKSTS_NEG_WIDTH_X4:
+ bus_p->bus_cur_width = PCIE_LINK_WIDTH_X4;
+ break;
+ case PCIE_LINKSTS_NEG_WIDTH_X8:
+ bus_p->bus_cur_width = PCIE_LINK_WIDTH_X8;
+ break;
+ case PCIE_LINKSTS_NEG_WIDTH_X12:
+ bus_p->bus_cur_width = PCIE_LINK_WIDTH_X12;
+ break;
+ case PCIE_LINKSTS_NEG_WIDTH_X16:
+ bus_p->bus_cur_width = PCIE_LINK_WIDTH_X16;
+ break;
+ case PCIE_LINKSTS_NEG_WIDTH_X32:
+ bus_p->bus_cur_width = PCIE_LINK_WIDTH_X32;
+ break;
+ default:
+ bus_p->bus_cur_width = PCIE_LINK_WIDTH_UNKNOWN;
+ break;
+ }
+
+ switch (cap & PCIE_LINKCAP_MAX_WIDTH_MASK) {
+ case PCIE_LINKCAP_MAX_WIDTH_X1:
+ bus_p->bus_max_width = PCIE_LINK_WIDTH_X1;
+ break;
+ case PCIE_LINKCAP_MAX_WIDTH_X2:
+ bus_p->bus_max_width = PCIE_LINK_WIDTH_X2;
+ break;
+ case PCIE_LINKCAP_MAX_WIDTH_X4:
+ bus_p->bus_max_width = PCIE_LINK_WIDTH_X4;
+ break;
+ case PCIE_LINKCAP_MAX_WIDTH_X8:
+ bus_p->bus_max_width = PCIE_LINK_WIDTH_X8;
+ break;
+ case PCIE_LINKCAP_MAX_WIDTH_X12:
+ bus_p->bus_max_width = PCIE_LINK_WIDTH_X12;
+ break;
+ case PCIE_LINKCAP_MAX_WIDTH_X16:
+ bus_p->bus_max_width = PCIE_LINK_WIDTH_X16;
+ break;
+ case PCIE_LINKCAP_MAX_WIDTH_X32:
+ bus_p->bus_max_width = PCIE_LINK_WIDTH_X32;
+ break;
+ default:
+ bus_p->bus_max_width = PCIE_LINK_WIDTH_UNKNOWN;
+ break;
+ }
+
+ /*
+ * If we have the Link Capabilities 2, then we can get the supported
+ * speeds from it and treat the bits in Link Capabilities 1 as the
+ * maximum. If we don't, then we need to follow the Implementation Note
+ * in the standard under Link Capabilities 2. Effectively, this means
+ * that if the value of 10b is set in Link Capabilities register, that
+ * it supports both 2.5 and 5 GT/s speeds.
+ */
+ if (cap2 != 0) {
+ if (cap2 & PCIE_LINKCAP2_SPEED_2_5)
+ bus_p->bus_sup_speed |= PCIE_LINK_SPEED_2_5;
+ if (cap2 & PCIE_LINKCAP2_SPEED_5)
+ bus_p->bus_sup_speed |= PCIE_LINK_SPEED_5;
+ if (cap2 & PCIE_LINKCAP2_SPEED_8)
+ bus_p->bus_sup_speed |= PCIE_LINK_SPEED_8;
+
+ switch (cap & PCIE_LINKCAP_MAX_SPEED_MASK) {
+ case PCIE_LINKCAP_MAX_SPEED_2_5:
+ bus_p->bus_max_speed = PCIE_LINK_SPEED_2_5;
+ break;
+ case PCIE_LINKCAP_MAX_SPEED_5:
+ bus_p->bus_max_speed = PCIE_LINK_SPEED_5;
+ break;
+ case PCIE_LINKCAP_MAX_SPEED_8:
+ bus_p->bus_max_speed = PCIE_LINK_SPEED_8;
+ break;
+ default:
+ bus_p->bus_max_speed = PCIE_LINK_SPEED_UNKNOWN;
+ break;
+ }
+ } else {
+ if (cap & PCIE_LINKCAP_MAX_SPEED_5) {
+ bus_p->bus_max_speed = PCIE_LINK_SPEED_5;
+ bus_p->bus_sup_speed = PCIE_LINK_SPEED_2_5 |
+ PCIE_LINK_SPEED_5;
+ }
+
+ if (cap & PCIE_LINKCAP_MAX_SPEED_2_5) {
+ bus_p->bus_max_speed = PCIE_LINK_SPEED_2_5;
+ bus_p->bus_sup_speed = PCIE_LINK_SPEED_2_5;
+ }
+ }
+}
+
+/*
* partially init pcie_bus_t for device (dip,bdf) for accessing pci
* config space
*
@@ -1134,6 +1300,10 @@ pcie_init_bus(dev_info_t *dip, pcie_req_id_t bdf, uint8_t flags)
}
}
+ /*
+ * Save and record speed information about the device.
+ */
+
caps_done:
/* save RP dip and RP bdf */
if (PCIE_IS_RP(bus_p)) {
@@ -1226,6 +1396,8 @@ initial_done:
pcie_init_plat(dip);
+ pcie_capture_speeds(bus_p, bdf, rcdip);
+
final_done:
PCIE_DBG("Add %s(dip 0x%p, bdf 0x%x, secbus 0x%x)\n",
diff --git a/usr/src/uts/common/sys/pcie.h b/usr/src/uts/common/sys/pcie.h
index 05b70a56fa..d1b633a040 100644
--- a/usr/src/uts/common/sys/pcie.h
+++ b/usr/src/uts/common/sys/pcie.h
@@ -22,6 +22,9 @@
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
+/*
+ * Copyright (c) 2017, Joyent, Inc.
+ */
#ifndef _SYS_PCIE_H
#define _SYS_PCIE_H
@@ -187,7 +190,14 @@ extern "C" {
/*
* Link Capability Register (4 bytes)
*/
-#define PCIE_LINKCAP_MAX_SPEED_2_5 0x1 /* 2.5 Gb/s Speed */
+#define PCIE_LINKCAP_MAX_SPEED_2_5 0x1 /* 2.5 GT/s Speed */
+/*
+ * In version 2 of PCI express, this indicated that both 5.0 GT/s and 2.5 GT/s
+ * speeds were supported. The use of this as the maximum link speed was added
+ * with PCIex v3.
+ */
+#define PCIE_LINKCAP_MAX_SPEED_5 0x2 /* 5.0 GT/s Speed */
+#define PCIE_LINKCAP_MAX_SPEED_8 0x3 /* 8.0 GT/s Speed */
#define PCIE_LINKCAP_MAX_SPEED_MASK 0xF /* Maximum Link Speed */
#define PCIE_LINKCAP_MAX_WIDTH_X1 0x010
#define PCIE_LINKCAP_MAX_WIDTH_X2 0x020
@@ -251,7 +261,9 @@ extern "C" {
/*
* Link Status Register (2 bytes)
*/
-#define PCIE_LINKSTS_SPEED_2_5 0x1 /* Link Speed */
+#define PCIE_LINKSTS_SPEED_2_5 0x1 /* 2.5 GT/s Link Speed */
+#define PCIE_LINKSTS_SPEED_5 0x2 /* 5.0 GT/s Link Speed */
+#define PCIE_LINKSTS_SPEED_8 0x3 /* 8.0 GT/s Link Speed */
#define PCIE_LINKSTS_SPEED_MASK 0xF /* Link Speed */
#define PCIE_LINKSTS_NEG_WIDTH_X1 0x010
@@ -405,8 +417,14 @@ extern "C" {
#define PCIE_DEVCTL2_END_END_TLP_PREFIX 0x8000
-
-
+/*
+ * Link Capability 2 Register (4 bytes)
+ */
+#define PCIE_LINKCAP2_SPEED_2_5 0x02
+#define PCIE_LINKCAP2_SPEED_5 0x04
+#define PCIE_LINKCAP2_SPEED_8 0x08
+#define PCIE_LINKCAP2_SPEED_MASK 0xfe
+#define PCIE_LINKCAP2_CROSSLINK 0x100
/*
* PCI-Express Enhanced Capabilities Link Entry Bit Offsets
diff --git a/usr/src/uts/common/sys/pcie_impl.h b/usr/src/uts/common/sys/pcie_impl.h
index 7f199a18ec..6bf9610573 100644
--- a/usr/src/uts/common/sys/pcie_impl.h
+++ b/usr/src/uts/common/sys/pcie_impl.h
@@ -282,6 +282,28 @@ typedef struct pf_root_fault {
typedef struct pf_data pf_data_t;
+typedef enum pcie_link_width {
+ PCIE_LINK_WIDTH_UNKNOWN,
+ PCIE_LINK_WIDTH_X1,
+ PCIE_LINK_WIDTH_X2,
+ PCIE_LINK_WIDTH_X4,
+ PCIE_LINK_WIDTH_X8,
+ PCIE_LINK_WIDTH_X12,
+ PCIE_LINK_WIDTH_X16,
+ PCIE_LINK_WIDTH_X32
+} pcie_link_width_t;
+
+/*
+ * Note, this member should always be treated as a bit field, as a device may
+ * support multiple speeds.
+ */
+typedef enum pcie_link_speed {
+ PCIE_LINK_SPEED_UNKNOWN = 0x00,
+ PCIE_LINK_SPEED_2_5 = 0x01,
+ PCIE_LINK_SPEED_5 = 0x02,
+ PCIE_LINK_SPEED_8 = 0x04
+} pcie_link_speed_t;
+
/*
* For hot plugged device, these data are init'ed during during probe
* For non-hotplugged device, these data are init'ed in pci_autoconfig (on x86),
@@ -336,6 +358,15 @@ typedef struct pcie_bus {
/* workaround for PCI/PCI-X devs behind PCIe2PCI Bridge */
pcie_req_id_t bus_pcie2pci_secbus;
+
+ /*
+ * Link speed specific fields.
+ */
+ pcie_link_width_t bus_max_width;
+ pcie_link_width_t bus_cur_width;
+ pcie_link_speed_t bus_sup_speed;
+ pcie_link_speed_t bus_max_speed;
+ pcie_link_speed_t bus_cur_speed;
} pcie_bus_t;
/*