diff options
-rw-r--r-- | usr/src/uts/common/io/pciex/pcie.c | 172 | ||||
-rw-r--r-- | usr/src/uts/common/sys/pcie.h | 26 | ||||
-rw-r--r-- | usr/src/uts/common/sys/pcie_impl.h | 31 |
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; /* |