The CCIX Base Specification 1.0, www.ccixconsortium.org describes the PCI DVSEC based configuration space for CCIX elements and topology. This patch: 1. Introduces into ls-ecap the ability to call parsers for the Designated Vendor-Specific Extended Capablity (DVSEC) elements. These may be defined by a particular vendor, or as in this case, a consoritium with appropriate unique ID issued by the PCI SIG. 2. Parsing of CCIX Transport Layer DVSEC. This is straight forward for devices supporting only PCI defined speeds, with a lot more information on devices that also support the 20GT/s and 25GT/s extended speeds defined in the CCIX spec. 3. Parsing of CCIX Protocol Layer DVSEC. This defines the system topology along with agent specific elements. * Common Device Capabilities and Control * Address and ID routing tables. * Protocol Agent Capabilities and Controls. * CCIX Protocol Layer Port Capabilities and Controls. * CCIX Protocol Layer Link Capabilities and Controls. * Protocol Error Controls and Records as needed for RAS handling. The support is fairly complete but for this initial version some minor elements are not yet reported. Signed-off-by: Jonathan Cameron <Jonathan.Cameron@xxxxxxxxxx> --- Makefile | 2 +- lib/header.h | 2 + ls-ccix.c | 1364 ++++++++++++++++++++++++++++++++++++++++++++++++++ ls-ecaps.c | 28 +- lspci.h | 4 + 5 files changed, 1398 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index ff51be1..17c9cb2 100644 --- a/Makefile +++ b/Makefile @@ -69,7 +69,7 @@ force: lib/config.h lib/config.mk: cd lib && ./configure -lspci: lspci.o ls-vpd.o ls-caps.o ls-caps-vendor.o ls-ecaps.o ls-kernel.o ls-tree.o ls-map.o common.o lib/$(PCILIB) +lspci: lspci.o ls-vpd.o ls-caps.o ls-caps-vendor.o ls-ecaps.o ls-ccix.o ls-kernel.o ls-tree.o ls-map.o common.o lib/$(PCILIB) setpci: setpci.o common.o lib/$(PCILIB) LSPCIINC=lspci.h pciutils.h $(PCIINC) diff --git a/lib/header.h b/lib/header.h index bfdcc80..c790821 100644 --- a/lib/header.h +++ b/lib/header.h @@ -1354,6 +1354,8 @@ #define PCI_VENDOR_ID_INTEL 0x8086 #define PCI_VENDOR_ID_COMPAQ 0x0e11 +/* Note that this is a PCISIG Issued Unique Value rather than a Vendor ID */ +#define PCI_VENDOR_ID_CCIX 0x1e2c /* I/O resource flags, compatible with <include/linux/ioport.h> */ diff --git a/ls-ccix.c b/ls-ccix.c new file mode 100644 index 0000000..c9f156e --- /dev/null +++ b/ls-ccix.c @@ -0,0 +1,1364 @@ +/* + * The PCI Utilities -- Show CCIX Capabilities + * + * Copyright (c) 2018 Jonathan Cameron <Jonathan.Cameron@xxxxxxxxxx> + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#include <stdio.h> +#include <string.h> + +#include "lspci.h" + +#define ARRAY_SIZE(x) sizeof(x)/sizeof(*(x)) + +struct ccix_state { + int portagg_capable:1; + int sw_portal_capable:1; + int forwarding:1; + u32 dev_error_log_offset; + u32 idm_table_offset; + u32 sr_table_offset; + u32 hsam_table_offset; + u32 hsam_table_size; + u32 rsam_table_offset; + u32 rsam_table_size; + u32 links; + int ra_found, ra_ctl_found; + u32 ha_num_ids[64]; + u32 ha_num_pools[64]; + int ha_found, ha_ctl_found; + u32 sa_num_pools[64]; + int sa_found, sa_ctl_found; + u32 psamnum; + u32 port_error_offset; + u32 link_error_offset; + u32 sa_error_offset[64]; + u32 ra_error_offset[64]; + u32 ha_error_offset[64]; +}; + +/* Where is not always aligned, hence use byte reads */ +static void +ccix_vendor_error(struct device *d, int where) +{ + u8 buff, buff2; + u16 len; + + buff = get_conf_byte(d, where); + buff2 = get_conf_byte(d, where + 1); + len = (u16)buff2 << 8 | buff; + printf("\t\t\t\t\t\tVenSpecLen:\t%u\n", len); +} + +/* + * Note used both for mem pool definition in which it + * is only 3 bits and for memory errors in which it is + * a byte with vendor defined upper half. + */ +static const char * ccix_sub_mem_types[8] = { + "NotSpecified", "SRAM", "DDR", "NVDIMM-F", + "NVDIMM-N", "HBM", "FLASH", "Unknown" +}; + +static const char * ccix_operation_type[11] = { + "GenericUndetermined", "GenericRead", "GenericWrite", + "DataRead", "DataWrite", "InstructionFetch", "Prefetch", + "Eviction", "Snooping", "Snooped", "Management", +}; + +static void +ccix_memory_error(struct device *d, int where) +{ + static const char * memory_type[] = { + "Other/NonSpec", "ROM", "Volatile", "NonVolatile", "Device", + }; + static const char * memory_error_type[] = { + "Unknown", "NoError", "SingleBitECC", "MultiBitECC", + "SingleSymbolChipKillECC", "MultiSymbolChipKillECC", + "MasterAbort", "TargetAbort", "ParityError", "WatchDog", + "InvalidAddr", "MirrorBroken", "MemorySparing", "Scrub", + "PhysicalMemMapOutEvent", + }; + static const char * memory_operation[] = { + "Generic", "Read", "Write", NULL, "Scrub", + }; + u32 buff, valbits; + u8 value; + + valbits = get_conf_long(d, where); + buff = get_conf_long(d, where + 0x4); + + printf("\t\t\t\t\tMemErr:\t"); + if ((buff & 0xff) == 255) + printf("Internal\n"); + else + printf("FRU:\t\t%u\n", buff & 0xff); + + /* + * Note we ignore the error message length as it provides no + * useful information not conveyed in other ways. + */ + printf("\t\t\t\t\t\tMemType:\t"); + buff = get_conf_long(d, where + 0x8); + if (valbits & 0x01) { + value = buff & 0xff; + if (value >= 0x80) + printf("Vendor 0x%x\n", value); + else + printf("%s\n", value >= ARRAY_SIZE(memory_type) ? + "Unknown": memory_type[value]); + } else + printf("Unspecifcied\n"); + + printf("\t\t\t\t\t\tOperation:\t"); + if (valbits & 0x02) { + value = (buff >> 8) & 0xff; + switch (value) { + case 0 ... 2: + case 4: + printf("%s\n", memory_operation[value]); + break; + default: printf("Unknown\n"); break; + } + } else + printf("Unspecified\n"); + + printf("\t\t\t\t\t\tErrorType:\t"); + if (valbits & 0x04) { + value = (buff >> 16) & 0xff; + printf("%s\n", value >= ARRAY_SIZE(memory_error_type) ? + "Unknown" : memory_error_type[value]); + } else + printf("Unspecified\n"); + + printf("\t\t\t\t\t\tChan:\t\t"); + if (valbits & 0x08) + printf("%u\n", (buff >> 24) & 0xff); + else + printf("Unspecified\n"); + + buff = get_conf_long(d, where + 0xc); + printf("\t\t\t\t\t\tModule:\t\t"); + if (valbits & 0x1000) + printf("%u\n", buff & 0xffff); + else + printf("Unspecified\n"); + + printf("\t\t\t\t\t\tBank:\t\t"); + if (valbits & 0x10) + printf("%u\n", (buff >> 16) & 0xffff); + else + printf("Unspecified\n"); + + printf("\t\t\t\t\t\tDevice:\t\t"); + if (valbits & 0x20) + printf("%u\n", get_conf_long(d, where + 0x10)); + else + printf("Unspecified\n"); + + printf("\t\t\t\t\t\tRow:\t\t"); + if (valbits & 0x40) + printf("%u\n", get_conf_long(d, where + 0x14)); + else + printf("Unspecified\n"); + + printf("\t\t\t\t\t\tColumn:\t\t"); + if (valbits & 0x80) + printf("%u\n", get_conf_long(d, where + 0x18)); + else + printf("Unspecified\n"); + + printf("\t\t\t\t\t\tRank:\t\t"); + if (valbits & 0x100) + printf("%u\n", get_conf_long(d, where + 0x1c)); + else + printf("Unspecified\n"); + + buff = get_conf_long(d, where + 0x20); + printf("\t\t\t\t\t\tBitPos:\t\t"); + if (valbits & 0x200) + printf("%u\n", buff & 0xff); + else + printf("Unspecified\n"); + + printf("\t\t\t\t\t\tChipID:\t\t"); + if (valbits & 0x400) + printf("%u\n", (buff >> 8) & 0xff); + else + printf("Unspecified\n"); + + printf("\t\t\t\t\t\tMemPoolType:\t"); + if (valbits & 0x2000) { + value = (buff >> 16) & 0xff; + if (value >= 0x80) + printf("Vendor 0x%x\n", value); + else { + printf("%s\n", value >= ARRAY_SIZE(ccix_sub_mem_types) ? + "Unknown": ccix_sub_mem_types[value]); + } + } else + printf("Unspecified\n"); + + if (valbits & 0x800) + ccix_vendor_error(d, where + 0x23); +} + +static void +ccix_cache_error(struct device *d, int where) +{ + u32 buff, buff2, valbits; + u8 value; + static const char * ccix_cache_type[4] = { + "Instruction", "Data", "Unified", "SnoopFilterDirectory" + }; + static const char * ccix_cache_error_type[6] = { + "Data", "Tag", "Timeout", "Hang", "DataLost", "InvalidAddr", + }; + + valbits = get_conf_long(d, where); + buff = get_conf_long(d, where + 0x4); + printf("\t\t\t\t\tCacheErr:\n"); + printf("\t\t\t\t\t\tCacheType:\t"); + if (valbits & 0x01) { + value = (buff >> 16) & 0xff; + printf("%s\n", value >= ARRAY_SIZE(ccix_cache_type) ? + "Unknown": ccix_cache_type[value]); + } else + printf("Unspecified\n"); + + printf("\t\t\t\t\t\tOperationType:\t"); + if (valbits & 0x2) { + value = (buff >> 24) & 0xff; + printf("%s\n", value >= ARRAY_SIZE(ccix_operation_type) ? + "Unknown" : ccix_operation_type[value]); + } else + printf("Unspecified\n"); + buff = get_conf_long(d, where + 0x8); + printf("\t\t\t\t\t\tCacheError:\t"); + if (valbits & 0x4) { + value = buff & 0xff; + printf("%s\n", value >= ARRAY_SIZE(ccix_cache_error_type) ? + "Unknown" : ccix_cache_error_type[value]); + } else + printf("Unspecified\n"); + + printf("\t\t\t\t\t\tLevel:\t\t"); + if (valbits & 0x8) + printf("%u\n", (buff >> 8) & 0xff); + else + printf("Unspecified\n"); + + buff2 = get_conf_long(d, where + 0xc); + printf("\t\t\t\t\t\tSet:\t\t"); + if (valbits & 0x10) + printf("%u\n", (buff >> 16) & 0xffff | ((buff2 & 0xffff) << 16)); + else + printf("Unspecified\n"); + + buff = buff2; + buff2 = get_conf_long(d, where + 0x10); + printf("\t\t\t\t\t\tWay:\t\t"); + if (valbits & 0x20) + printf("%u\n", (buff >> 16) & 0xffff | ((buff2 & 0xffff) << 16)); + else + printf("Unspecified\n"); + + buff = buff2; + printf("\t\t\t\t\t\tInstanceID:\t"); + if (valbits & 0x40) + printf("%u\n", (buff >> 16) & 0xff); + else + printf("Unspecified\n"); + + if (valbits & 0x80) + ccix_vendor_error(d, where + 0x14); +} +static void +ccix_atc_error(struct device *d, int where) +{ + u32 buff, valbits; + u8 value; + + valbits = get_conf_long(d, where); + buff = get_conf_long(d, where + 0x4); + printf("\t\t\t\t\tATCErr:\n"); + printf("\t\t\t\t\t\tOperationType:\t"); + if (valbits & 0x1) { + value = (buff >> 16) & 0xff; + switch (value) { + case 0 ... 10: + printf("%s\n", ccix_operation_type[value]); + break; + default: + printf("Unknown\n"); + break; + } + } else + printf("Unspecified\n"); + + printf("\t\t\t\t\t\tInstanceID:\t"); + if (valbits & 0x02) + printf("%u\n", buff >> 24); + else + printf("Unspecified\n"); + + if (valbits & 0x04) + ccix_vendor_error(d, where + 0x0c); +} + +static void +ccix_port_error(struct device *d, int where) +{ + static const char * port_op_type[] = { + "Command", "Read", "Write" + }; + static const char * port_error_type[8] = { + "Generic", "BusParrity", "NoBDF", "AddrInval", + "IDInval", "Timeout", "Hang", "EgressBlk" + }; + u32 buff, valbits; + u8 value; + + buff = get_conf_long(d, where); + valbits = buff & 0xf; + buff = get_conf_long(d, where + 0x4); + printf("\t\t\t\t\tPortErr:\n"); + printf("\t\t\t\t\t\tOperation:\t"); + if (valbits & 0x1) { + value = (buff >> 16) & 0xff; + printf("%s\n", value >= ARRAY_SIZE(port_op_type) ? + "Unknown" : port_op_type[value]); + } else { + printf("Unspecified\n"); + } + + printf("\t\t\t\t\t\tPortErrType:\t"); + if (valbits & 0x2) { + value = (buff >> 24) & 0xff; + printf("%s\n", value >= ARRAY_SIZE(port_error_type) ? + "Unknown" : port_error_type[value]); + } else { + printf("Unspecified\n"); + } + + /* We could report the raw CCIX message header if present */ + printf("\t\t\t\t\t\tCCIX Message:\t"); + if (valbits & 0x04) + printf("Present\n"); + else + printf("Unspecified\n"); + + if (valbits & 0x08) + ccix_vendor_error(d, where + 0x28); +} + +static void +ccix_link_error(struct device *d, int where) +{ + u32 buff, valbits; + u8 value; + const char * link_op_types[3] = { + "Command", "Read", "Write", + }; + const char * link_error_types[5] = { + "Generic", "CreditUnderflow", "CreditOverflow", + "UnusableCreditReceived", "CreditTimeout" + }; + + valbits = get_conf_long(d, where); + buff = get_conf_long(d, where + 0x4); + printf("\t\t\t\t\tLinkErr:\n"); + printf("\t\t\t\t\t\tOperationType:\t"); + if (valbits & 0x1) { + value = (buff >> 16) & 0xff; + printf("%s\n", value >= ARRAY_SIZE(link_op_types) ? + "Unknown" : link_op_types[value]); + } else + printf("Unspecified\n"); + + printf("\t\t\t\t\t\tErrorType:\t"); + if (valbits & 0x02) { + value = buff >> 24; + printf("%s\n", value >= ARRAY_SIZE(link_error_types) ? + "Unknown" : link_error_types[value]); + } else + printf("Unspecified\n"); + + buff = get_conf_long(d, where + 0x8); + printf("\t\t\t\t\t\tLinkID:\t\t"); + if (valbits & 0x04) + printf("%u\n", buff & 0xff); + else + printf("Unspecified\n"); + + printf("\t\t\t\t\t\tLinkCreditType:\t"); + if (valbits & 0x08) + printf("%u\n", (buff >> 8) & 0xff); + else + printf("Unspecified\n"); + + printf("\t\t\t\t\t\tCCIX Message:\t"); + if (valbits & 0x10) + printf("Present\n"); + else + printf("Unspecified\n"); + + if (valbits & 0x20) + ccix_vendor_error(d, where + 0x2c); +} + +static void +ccix_agent_error(struct device *d, int where) +{ + u32 valbits; + + valbits = get_conf_long(d, where); + printf("\t\t\t\t\tInterErr:\n"); + if (valbits & 0x01) + ccix_vendor_error(d, where + 0x8); +} + +/* Does it make sense to do these in lspci? */ +static void +ccix_error_log(struct device *d, int where) +{ + u32 buff, buff2; + u8 value; + u8 error_type; + int vendor_specific; + + static const char * ccix_element[5] = { + "RA", "HA", "SA", "Port", "Link" + }; + + buff = get_conf_long(d, where); + + printf("\t\t\t\tPER [%03x]:\tLogVersion: %u, ME%c ", + where, buff & 0xff, FLAG(buff, 0x8000)); + buff = get_conf_long(d, where + 0x4); + printf("SevUE%c SevNoComm%c SevDegraded%c SevDeferred%c\n", + FLAG(buff, 0x10000), FLAG(buff, 0x20000), + FLAG(buff, 0x40000), FLAG(buff, 0x80000)); + printf("\t\t\t\t\tComponent:\t"); + value = (buff >> 12) & 0xf; + printf("%s\n", value >= ARRAY_SIZE(ccix_element) ? + "Unknown" : ccix_element[value]); + error_type = (buff >> 24) & 0x7; + vendor_specific = (buff >> 31) & 0x1; + printf("\t\t\t\t\tAddress:\t"); + if ((buff >> 30) & 0x1) { + buff = get_conf_long(d, where + 0x8); + buff2 = get_conf_long(d, where + 0xc); + printf("[0x%016lx], ", (u64)buff2 << 32 | (buff & 0xfffffffc)); + buff = get_conf_long(d, where + 0x10); + printf("MaskLen: %u\n", buff & 0x3f); + } else + printf("NotValid\n"); + + if (!vendor_specific) { + switch(error_type) { + case 0: + ccix_memory_error(d, where + 0x14); + break; + case 1: + ccix_cache_error(d, where + 0x14); + break; + case 2: + ccix_atc_error(d, where + 0x14); + break; + case 3: + ccix_port_error(d, where + 0x14); + break; + case 4: + ccix_link_error(d, where + 0x14); + break; + case 5: + ccix_agent_error(d, where + 0x14); + break; + default: + printf("\t\t\t\t\tUnknownErr:\n"); + break; + } + } else { + printf("\t\t\t\t\tVendorSpecific\n"); + } +} + +static void +ccix_mem_pool(struct device *d, int where, int i) +{ + u32 buff, buff2; + u64 pool_size; + static const char * ccix_mem_types[8] = { + "Other", "Expansion", "Hole", "ROM", + "Volatile", "Non-Volatile", "Device", "Unknown" + }; + static const char * ccix_mem_attr[8] = { + "Device", "Unknown", "Unknown", "Unknown", + "Non-Cacheable", "Cacheable", "Unknown", "Unknown" + }; + static const char * ccix_mem_ext_attr[8] = { + "System", "Private", "Unknown", "Unknown", + "Unknown", "Unknown", "Unknown", "Unknown" + }; + + buff = get_conf_long(d, where); + printf("\t\t\t\tPool #%u:\tReady%c ", i, FLAG(buff, 0x1)); + printf("%s/%s/%s/%s\n", + ccix_mem_types[(buff >> 1) & 0x7], + ccix_sub_mem_types[(buff >> 4) & 0x7], + ccix_mem_attr[(buff >> 8) & 0x7], + ccix_mem_ext_attr[(buff >> 11) & 0x7]); + printf("\t\t\t\t\t\tNon4GBAligned%c\n", FLAG(buff, 0x80)); + + buff2 = get_conf_long(d, where + 0x4); + pool_size = (((u64)buff2 << 16 | ((buff >> 16) & 0xffff)) + 1) * 65536; + printf("\t\t\t\t\t\tSize: "); + if (pool_size & 0x3ff == 0) + printf("%lu B", pool_size); + else { + if (pool_size & 0xfffff) + printf("%lu kB", pool_size >> 10); + else { + if (pool_size & 0x3fffffff) + printf("%lu MB", pool_size >> 20); + else { + if (pool_size & 0xffffffffff) + printf("%lu GB", pool_size >> 30); + else + printf("%lu TB", pool_size >> 40); + } + } + } + printf("\n"); +} + +static void +ccix_bat(struct device *d, int where, int i) +{ + u32 buff; + + buff = get_conf_long(d, where); + printf("\t\t\t\tPool #%u:\tValid%c ", i, FLAG(buff, 0x1)); + buff = get_conf_long(d, where + 0x4); + printf("BaseAddr: [0x%016lx]\n", (u64)buff << 32); +} + +static void +ccix_error_ctl_stat(struct device *d, int where, int *error) +{ + u32 buff; + + buff = get_conf_long(d, where); + printf("\t\t\t\tErrCtlSta1:\tCurrent: "); + switch (buff & 0x3) { + case 0: + printf("No error, "); + *error = 0; + break; + case 1: + printf("Error pending, "); + *error = 1; + break; + case 2: + printf("Invalid, "); + *error = 0; + break; + case 3: + printf("Error pending / PER Sent, "); + *error = 1; + break; + } + printf("LogDisable%c PERMsgDisable%c\n", FLAG(buff, 0x2), FLAG(buff, 0x4)); + buff = get_conf_long(d, where + 0x4); + printf("\t\t\t\tErrCtlSta2:\tSevLogMask: 0x%02x, SevPERMsgMask: 0x%02x\n", + buff & 0x3f, (buff >> 8) & 0x3f); + printf("\t\t\t\tTypeMaskR/L:\tMem%c/%c Cache%c/%c ATC%c/%c Port%c/%c Agent%c/%c\n", + FLAG(buff, 0x0010000), FLAG(buff, 0x0020000), + FLAG(buff, 0x0040000), FLAG(buff, 0x0080000), + FLAG(buff, 0x0100000), FLAG(buff, 0x0200000), + FLAG(buff, 0x0400000), FLAG(buff, 0x0800000), + FLAG(buff, 0x1000000), FLAG(buff, 0x2000000)); +} + +static void +ccix_sa_cap(struct device *d, int where, struct ccix_state *state) +{ + u32 buff; + unsigned i; + + buff = get_conf_long(d, where + 0x4); + state->sa_num_pools[state->sa_found] = (buff >> 4) & 0x3f; + printf("\t\t\t\tSACapStat:\tDiscRdyStat%c MemPoolRdy%c NumMemPool: %u\n", + FLAG(buff, 0x1), FLAG(buff, 0x80000000), (buff >> 4) & 0x3f); + state->sa_error_offset[state->sa_found] = get_conf_long(d, where + 0x8) >> 20; + for (i = 0; i < state->sa_num_pools[state->sa_found]; i++) + ccix_mem_pool(d, where + 0x0c + i * 8, i); + + state->sa_found++; +} + +static void +ccix_sa_ctl(struct device *d, int where, struct ccix_state *state) +{ + u32 buff; + unsigned i; + int num_sbat, error; + + buff = get_conf_long(d, where + 0x4); + num_sbat = (buff >> 4) & 0x3f; + printf("\t\t\t\tSACtl:\tEnable%c SAID: %02u, NumSBATEnable: %02u\n", + FLAG(buff, 0x1), (buff >> 26) & 0x3f, num_sbat); + + ccix_error_ctl_stat(d, where + 0x8, &error); + if (error && state->sa_error_offset[state->sa_ctl_found]) + ccix_error_log(d, state->sa_error_offset[state->sa_ctl_found]); + + for (i = 0; i < state->sa_num_pools[state->sa_ctl_found]; i++) + ccix_bat(d, where + 0x10 + 8 * i, i); + state->sa_ctl_found++; +} + +static void +ccix_ra_cap(struct device *d, int where, struct ccix_state *state) +{ + u32 buff; + + buff = get_conf_long(d, where + 0x4); + printf("\t\t\t\tRACapStat:\tDiscRdyStat%c CacheFlushStat%c\n", + FLAG(buff, 0x1), FLAG(buff, 0x80000000)); + state->ra_error_offset[state->ra_found] = get_conf_long(d, where + 0x8) >> 20; + + state->ra_found++; +} + +static void +ccix_ra_ctl(struct device *d, int where, struct ccix_state *state UNUSED) +{ + u32 buff; + int error; + + buff = get_conf_long(d, where + 0x4); + printf("\t\t\t\tRACtl:\tID: %02u, Enable%c SnpRespEnable%c CacheFlush%c CacheEnable%c\n", + (buff >> 26) & 0x3f, FLAG(buff, 0x1), FLAG(buff, 0x2), + FLAG(buff, 0x4000), FLAG(buff, 0x8000)); + + ccix_error_ctl_stat(d, where + 0x8, &error); + if (error && state->ra_error_offset[state->ra_ctl_found]) + ccix_error_log(d, state->ra_error_offset[state->ra_ctl_found]); + + state->ra_ctl_found++; +} + +static void +ccix_ha_cap(struct device *d, int where, struct ccix_state *state) +{ + u32 buff; + unsigned i; + + buff = get_conf_long(d, where + 0x4); + state->ha_num_ids[state->ha_found] = ((buff >> 1) & 0x3f) + 1; + + printf("\t\t\t\tHACapStat:\tDiscRdyStat%c MemPoolRdy%c MemExp%c NumHAID: %u, NumMemPool: %u\n", + FLAG(buff, 0x1), FLAG(buff, 0x80000000), FLAG(buff, 0x10000), + ((buff >> 1) & 0x3f) + 1, (buff >> 8) & 0x3f); + state->ha_num_pools[state->ha_found] = (buff >> 8) & 0x3f; + + state->ha_error_offset[state->ha_found] = get_conf_long(d, where + 0x8) >> 20; + + for (i = 0; i < state->ha_num_pools[state->ha_found]; i++) + ccix_mem_pool(d, where + 0xc + i * 8, i); + + state->ha_found++; +} + +static void +ccix_ha_ctl(struct device *d, int where, struct ccix_state *state) +{ + u32 buff, buff2; + int error; + unsigned i, j; + unsigned hbat_start = 0x18 + ((state->ha_num_ids[state->ha_ctl_found] - 1) / 4 + 1) * 4; + + buff = get_conf_long(d, where + 0x4); + printf("\t\t\t\tHACtl:\tEnable%c MemExpEnable%c HBatEnabled: %u\n", FLAG(buff, 0x1), + FLAG(buff, 0x10000), (buff >> 8) & 0x3f); + + buff = get_conf_long(d, where + 0x8); + buff2 = get_conf_long(d, where + 0xc); + printf("\t\t\t\tRAIDV:\t0x%016lx\n", ((u64)buff2 << 32) | buff); + ccix_error_ctl_stat(d, where + 0x10, &error); + + if (error && state->ha_error_offset[state->ha_ctl_found]) + ccix_error_log(d, state->ha_error_offset[state->ha_ctl_found]); + + for (i = 0; i <= (state->ha_num_ids[state->ha_ctl_found] - 1) / 4; i++) { + buff = get_conf_long(d, where + 0x18 + i * 4); + for(j = 0; j < 4; j++) { + if (i * 4 + j < state->ha_num_ids[state->ha_ctl_found]) + printf("\t\t\t\tHA #%02u:\tID: %02u\n", i * 4 + j, + (buff >> (8 * j) & 0x3f)); + } + } + + for (i = 0; i < state->ha_num_pools[state->ha_ctl_found]; i++) + ccix_bat(d, where + hbat_start + 8 * i, i); + + state->ha_ctl_found++; +} + +static void +ccix_link_cap(struct device *d, int where, + struct ccix_state *state UNUSED) +{ + u32 buff; + + buff = get_conf_long(d, where + 0x4); + printf("\t\t\t\tLinkCap:\tRdy%c SharedCredits%c MsgPack%c NoCompAck%c ", + FLAG(buff, 0x1), FLAG(buff, 0x2), FLAG(buff, 0x4), FLAG(buff, 0x8)); + printf("MaxPktSize: "); + switch (buff >> 7 & 0x7) { + case 0: printf("128B "); break; + case 1: printf("256B "); break; + case 2: printf("512B "); break; + default: printf("Unknown "); break; + }; + printf("\n"); + + buff = get_conf_long(d, where + 0x8); + printf("\t\t\t\tLinkSendCap:\tMaxMemReq: %u, MaxSnpReq: %u, MaxDatReq: %u\n", + buff & 0x3f, (buff >> 10) & 0x3f, (buff >> 20) & 0x3f); + + buff = get_conf_long(d, where + 0xc); + printf("\t\t\t\tLinkRcvCap:\tMaxMemReq: %u, MaxSnpReq: %u, MaxDatReq: %u\n", + buff & 0x3f, (buff >> 10) & 0x3f, (buff >> 20) & 0x3f); + + buff = get_conf_long(d, where + 0x10); + printf("\t\t\t\tMiscCap:\tMaxMiscReqSend: %u, MaxMiscReqRcv %u\n", + buff & 0x3f, (buff >> 10) & 0x3f); + state->link_error_offset = get_conf_long(d, where + 0x14) >> 20; +} + +static void +ccix_link_ctl(struct device *d, int where, struct ccix_state *state) +{ + u32 buff, buff2; + unsigned int i; + int error; + u16 BDF; + int link_len = state->forwarding ? 8 * 4 : 6 * 4; + + for (i = 0; i < state->links; i++) { + printf("\t\t\t\tLink#%02u [%x]\n", i, where + i * link_len + 0x4); + + buff = get_conf_long(d, where + i * link_len + 0x4); + printf("\t\t\t\tLinkCtl:\tEnable%c CreditSnd%c MsgPack%c NoCmpAck%c ", + FLAG(buff, 0x1), FLAG(buff, 0x2), FLAG(buff, 0x4), FLAG(buff, 0x40)); + printf("MaxPktSize: "); + switch (buff >> 7 & 0x7) { + case 0: printf("128B "); break; + case 1: printf("256B "); break; + case 2: printf("512B "); break; + default: printf("Unknown "); break; + }; + printf("\n"); + printf("\t\t\t\t\t\t"); + if ((buff >> 10) & 0x1) + printf("HA-to-SA\n"); + else + printf("RA-to-HA\n"); + + buff = get_conf_long(d, where + i * link_len + 0x8); + /* Print misc credits along with others */ + buff2 = get_conf_long(d, where + i * link_len + 0x10); + printf("\t\t\t\tMaxCredit:\tMem: %04u, Snoop: %04u, Data %04u, Misc %04u\n", + buff & 0x3ff, (buff >> 10) & 0x3ff, (buff >> 20) & 0x3ff, + buff2 & 0x3ff); + + buff = get_conf_long(d, where + i * link_len + 0xc); + printf("\t\t\t\tMinCredit:\tMem: %04u, Snoop: %04u, Data %04u, Misc %04u\n", + buff & 0x3ff, (buff >> 10) & 0x3ff, (buff >> 20) & 0x3ff, + (buff2 >> 10) & 0x3ff); + + if (state->forwarding) { + buff = get_conf_long(d, where + i * link_len + 0x20); + buff2 = get_conf_long(d, where + i * link_len + 0x24); + printf("\t\t\t\tBcastFwd:\t0x%016lx\n", (u64)buff2 << 32 | buff); + } + + /* + * Transport ID map - comes after the control block, but eiaer to follow here + * where it is associated with the link's other information + */ + buff = get_conf_long(d, where + state->links * link_len + 0x4 + 4 * i); + BDF = buff & 0xffff; + printf("\t\t\t\tDestBDF:\t%02x:%02x.%d\n", + BDF >> 8, (BDF >> 3) & 0x1f, BDF & 0x7); + + ccix_error_ctl_stat(d, where + i * link_len + 0x14, &error); + if (error && state->link_error_offset) + ccix_error_log(d, state->link_error_offset); + } +} + +static void +ccix_port_cap(struct device *d, int where, struct ccix_state *state) +{ + u32 buff; + + buff = get_conf_long(d, where + 0x4); + printf("\t\t\t\tPortCap:\t"); + printf("Port #%u Rdy%c OptTLP%c P2PForward%c ", buff >> 27, + FLAG(buff, 0x1), FLAG(buff, 0x2), FLAG(buff, 0x10)); + state->links = (buff >> 7) & 0x3f; + state->forwarding = (buff >> 4); + printf("Links: %u ", state->links); + state->psamnum = (buff >> 13) & 0x3f; + printf("PSAMNum: %u ", state->psamnum); + printf("\n"); + + buff = get_conf_long(d, where + 0x8); + printf("\t\t\t\tPortCap2:\tAgg Mask 0x%x\n", buff & 0xff); + + buff = get_conf_long(d, where + 0xc); + printf("\t\t\t\tPortCap3:\tFW Mask 0x%x\n", buff & 0xff); + + buff = get_conf_long(d, where + 0x10); + state->port_error_offset = buff >> 20; +} + +static void +ccix_port_ctl(struct device *d, int where, + struct ccix_state *state UNUSED) +{ + u32 buff; + u32 psamnum; + u32 i; + int error = 0; + u16 BDF; + + buff = get_conf_long(d, where + 0x4); + psamnum = (buff >> 13) & 0x3f; + printf("\t\t\t\tPortCntrl:\tEnable%c OptTLP%c LinksEnabled: %u, PSAMNum: %u\n", + FLAG(buff, 0x1), FLAG(buff, 0x2), (buff >> 7) & 0x3f, psamnum); + + ccix_error_ctl_stat(d, where + 0x8, &error); + buff = get_conf_long(d, where + 0x10); + BDF = buff & 0xffff; + printf("\t\t\t\tSourceTransportID: %02x:%02x.%d\n", + BDF >> 8, (BDF >> 3) & 0x1f, BDF & 0x7); + + for (i = 0; i < state->psamnum; i++) { + buff = get_conf_long(d, where + 0x14 + 12 * i); + printf("\t\t\t\tPSamEntry%02u: Valid%c Link%02u ", i, + FLAG(buff, 0x1), (buff >> 9) & 0x3f); + if (buff & (1 << 15)) + printf("HA-to-SA "); + else + printf("RA-to-HA "); + printf("[0x%016lx-0x%016lx]\n", + (uint64_t)get_conf_long(d, where + 0x14 + 12 * i + 4) << 32, + (uint64_t)get_conf_long(d, where + 0x14 + 12 * i + 8) << 32); + } + if (error && state->port_error_offset) + ccix_error_log(d, state->port_error_offset); +} + +static void +ccix_common_cap(struct device *d, int where, struct ccix_state *state) +{ + u32 buff; + int mesh_capable = 0; + u64 sw_portal_size; + + buff = get_conf_long(d, where + 0x4); + printf("\t\t\t\tCommonCap:\t"); + printf("DevID: %u ", (buff >> 24) & 0xff); + printf("StructVer: %u ", (buff >> 22) & 0x3); + if (buff & 0x1) { + printf("MultiPort+ "); + if (buff & 0x4) { + printf("Mesh+ "); + mesh_capable = 1; + } + else + printf("Mesh- "); + } else + printf("DevMultiPort- "); + if (buff & 0x2) { + printf("PrimaryPort\n"); + + buff = get_conf_long(d, where + 0x8); + printf("\t\t\t\tCommonCap2:\tRdy%c PartialCache%c PortAgg%c 128B%c MultiHop%c SamAlign%c ", + FLAG(buff, 0x1), FLAG(buff, 0x2), FLAG(buff, 0x4), + FLAG(buff, 0x8), FLAG(buff, 0x80), FLAG(buff, 0x200)); + state->portagg_capable = (buff >> 3) & 0x1; + if (buff & (1 << 8)) { + printf("SWPort+ "); + state->sw_portal_capable = 1; + } + else + printf("SWPort- "); + printf("\n"); + + printf("\t\t\t\t\t\tAddrWidth: "); + switch ((buff >> 4) & 0x7) { + case 0x0: printf("48 bit, "); break; + case 0x1: printf("52 bit, "); break; + case 0x2: printf("56 bit, "); break; + case 0x3: printf("60 bit, "); break; + case 0x4: printf("64 bit, "); break; + default: printf("unknown, "); break; + } + printf("DataRdyTime: %u * 32^%u\n", + (buff >> 19) & 0x3f, (buff >> 28) & 0x7); + if (state->sw_portal_capable) { + buff = get_conf_long(d, where + 0x20 + mesh_capable ? 4 : 0); + sw_portal_size = (u64)buff * 65636;; + printf("\t\t\t\t\t\tSWPortalSize: "); + if (sw_portal_size & 0x3ff) + printf("%lu B", sw_portal_size); + else { + if (sw_portal_size & 0xfffff) + printf("%lu kB", sw_portal_size >> 10); + else { + if (sw_portal_size & 0x3fffffff) + printf("%lu MB", sw_portal_size >> 20); + else { + if (sw_portal_size & 0xffffffffff) + printf("%lu GB", sw_portal_size >> 30); + else + printf("%lu TB", sw_portal_size >> 40); + } + } + } + printf("\n"); + } + state->dev_error_log_offset = get_conf_long(d, where + 0x10) >> 20; + state->idm_table_offset = get_conf_long(d, where + 0x14) >> 20; + buff = get_conf_long(d, where + 0x18); + state->rsam_table_offset = buff >> 20; + state->rsam_table_size = buff & 0xfff; + buff = get_conf_long(d, where + 0x1c); + state->hsam_table_offset = buff >> 20; + state->hsam_table_size = buff & 0xfff; + if (mesh_capable) + state->sr_table_offset = get_conf_long(d, where + 0x20) >> 20; + } + else + printf("SecondaryPort\n"); + + if (state->dev_error_log_offset) + ccix_error_log(d, state->dev_error_log_offset); +} + +static void +ccix_common_ctl(struct device *d, int where, struct ccix_state *state) +{ + u32 buff, buff2; + int offset = 0; + + buff = get_conf_long(d, where + 0x4); + printf("\t\t\t\tCommCtl1:\tDeviceEnable%c PrimaryPortEnable%c Mesh%c PortAgg%c\n", + FLAG(buff, 0x1), FLAG(buff, 0x2), FLAG(buff, 0x4), FLAG(buff, 0x10)); + printf("\t\t\t\t\t\tIDMTableValid%c RSAMTableValid%c HSAMTableValid%c SWPort%c\n", + FLAG(buff, 0x20), FLAG(buff, 0x40), + FLAG(buff, 0x80), FLAG(buff, 0x100)); + printf("\t\t\t\t\t\tErrorAgent: %u, DevID: %u\n", + (buff >> 16) & 0x3f, (buff >> 24) & 0xff); + + buff = get_conf_long(d, where + 0x8); + printf("\t\t\t\tCommCtl2:\tPartialCache%c 128B%c ", + FLAG(buff, 0x2), FLAG(buff, 0x8)); + printf("AddrWidth: "); + switch ((buff >> 4) & 0x7) { + case 0x0: printf("48 bit\n"); break; + case 0x1: printf("52 bit\n"); break; + case 0x2: printf("56 bit\n"); break; + case 0x3: printf("60 bit\n"); break; + case 0x4: printf("64 bit\n"); break; + default: printf("unknown\n "); break; + } + + buff = get_conf_long(d, where + 0xc); + printf("\t\t\t\tDevErrCtl:\tEnable%c\n", FLAG(buff, 0x1)); + if ((state->forwarding && !state->ha_found && + !state->ra_found && state->portagg_capable) || + (!state->forwarding && state->portagg_capable && + state->ha_found && !state->ra_found)) { + buff = get_conf_long(d, where + 0x10); + buff2 = get_conf_long(d, where + 0x14); + printf("\t\t\t\tSnpRqHM:\tHashMask: 0x%016lx\n", + ((u64)buff2 << 32) | (buff & 0xffffffc0)); + + offset += 8; + } + if (state->sw_portal_capable) { + buff = get_conf_long(d, where + 0x10 + offset); + printf("\t\t\t\tSWPortal:\tBaseAddress = [0x%016lx]\n", + (u64)buff << 32); + } +} + +static void +ccix_sam_entry(struct device *d, unsigned int index, int where) +{ + u32 buff, buff2; + buff = get_conf_long(d, where); + printf("\t\t\t\t#%02u: Enable%c ", index, FLAG(buff, 0x1)); + if (buff & 0x2) { + printf("Port#%02u ", (buff >> 4) & 0xf); + printf("NumAgg: %02u\t", ((buff >> 10) & 0xf) + 1); + } else + printf("Local\t\t"); + + buff = get_conf_long(d, where + 0x4); + buff2 = get_conf_long(d, where + 0x8); + printf("[0x%016lx:0x%016lx]\n", ((u64)buff) << 32, ((u64)buff2) << 32); +} + +static void +ccix_protocol_dvsec(struct device *d, int where, int len) +{ + struct ccix_handler { + const char * name; + void (*cap_handler)(struct device *d, int where, struct ccix_state *state); + void (*ctl_handler)(struct device *d, int where, struct ccix_state *state); + }; + static const struct ccix_handler ccix_handlers[11] = { + [0x0] = { "General", NULL, NULL, }, + [0x1] = { "Unexpcted TL DVSEC", NULL, NULL, }, + [0x2] = { "Unexpected PRL DVSEC", NULL, NULL, }, + [0x3] = { "Common", ccix_common_cap, ccix_common_ctl, }, + [0x4] = { "Port", ccix_port_cap, ccix_port_ctl, }, + [0x5] = { "Link", ccix_link_cap, ccix_link_ctl, }, + [0x6] = { "Home Agent", ccix_ha_cap, ccix_ha_ctl, }, + [0x7] = { "Unknown", NULL, NULL, }, + [0x8] = { "Request Agent", ccix_ra_cap, ccix_ra_ctl, }, + [0x9] = { "Unknown", NULL, NULL, }, + [0xa] = { "Slave Agent", ccix_sa_cap, ccix_sa_ctl, }, + }; + + int i; + u32 buff, buff2; + u32 guid[4]; + u32 cap_stat_offset; + u32 cnt_offset; + u32 sam_entry_offset; + u16 element; + u16 guid_offset; + struct ccix_state state = {}; + + printf("Protocol %x %x>\n", where, len); + + buff = get_conf_long(d, where + 0x8); + guid_offset = buff >> 20; + if (!config_fetch(d, where + 0xc, len - 0xc)) + return; + + buff = get_conf_long(d, where + 0xc); + cap_stat_offset = (buff >> 20) & 0xfff; + + buff = get_conf_long(d, where + 16); + cnt_offset = (buff >> 20) & 0xfff; + + while (cap_stat_offset) { + buff = get_conf_long(d, cap_stat_offset); + element = buff & 0xffff; + printf("\t\t\tCCIX Cap [%x v%u] ", cap_stat_offset, (buff >> 16) & 0xf); + if (element >= sizeof(ccix_handlers) / sizeof(ccix_handlers[0])) { + printf("Unknown\n"); + } else { + printf("%s\n", ccix_handlers[buff & 0xffff].name); + if (ccix_handlers[buff & 0xffff].cap_handler) + ccix_handlers[buff & 0xffff].cap_handler(d, cap_stat_offset, &state); + cap_stat_offset = buff >> 20; + } + } + + while (cnt_offset) { + buff = get_conf_long(d, cnt_offset); + printf("\t\t\tCCIX Ctl [%x] ", cnt_offset); + element = buff & 0xfff; + if (element >= sizeof(ccix_handlers) / sizeof(ccix_handlers[0])) { + printf("Unknown\n"); + } else { + printf("%s\n", ccix_handlers[buff & 0xffff].name); + if (ccix_handlers[buff & 0xffff].ctl_handler) + ccix_handlers[buff & 0xffff].ctl_handler(d, cnt_offset, &state); + } + cnt_offset = buff >> 20; + } + + if (guid_offset) { + buff = get_conf_long(d, guid_offset + 16); + printf("\t\t\tCCIX CCID OVERRIDE [%x v%d]\n", guid_offset, buff & 0xf); + guid[0] = get_conf_long(d, guid_offset); + guid[1] = get_conf_long(d, guid_offset + 4); + guid[2] = get_conf_long(d, guid_offset + 8); + guid[3] = get_conf_long(d, guid_offset + 12); + + if ((guid[0] != 0xc3cb993b) || + (guid[1] != 0x02c4436f) || + (guid[2] != 0x9b68d271) || + (guid[3] != 0xf2e8ca31)) { + printf("\t\t\t\t Invalid GUID\n"); + } else { + printf("\t\t\t\t OverrideCCID:\t%04x\n", buff >> 16); + } + } + + if (state.idm_table_offset) { + printf("\t\t\tCCIX IDM Table [%x]\n", state.idm_table_offset); + /* Only print valid entries */ + for (i = 0; i < 64; i++) { + buff = get_conf_long(d, state.idm_table_offset + i * 4); + if ((buff & 0x1) == 0) + continue; + printf("\t\t\t\t#%02u: ", i); + if (buff & 0x02) { + printf("Port#%02u ", (buff >> 4) & 0xf); + if ((buff >> 10) & 0xf) + printf("NumAgg: %02u, ", ((buff >> 10) & 0xf) + 1); + printf("Link#%02u", (buff >> 15) & 0x3f); + } else { + printf("Local"); + } + printf("\n"); + } + } + if (state.sr_table_offset) { + printf("\t\t\tCCIX SR Table [%x]\n", state.sr_table_offset); + /* Only print valid entries */ + for (i = 0; i < 64; i++) { + buff = get_conf_long(d, state.sr_table_offset + i * 4); + if ((buff & 0x1) == 0) + continue; + printf("\t\t\t\t#%02u: ", i); + if (buff & 0x02) { + printf("Port#%02u ", (buff >> 4) & 0xf); + if ((buff >> 10) & 0xf) + printf("NumAgg: %02u, ", ((buff >> 10) & 0xf) + 1); + printf("Link#%02u", (buff >> 15) & 0x3f); + } else { + printf("Local"); + } + printf("\n"); + } + } + + if (state.rsam_table_offset) { + printf("\t\t\tCCIX RSAM Table [%x]\n", state.rsam_table_offset); + /* relies on the hash mask only have 2 DW if present*/ + for (sam_entry_offset = 0; sam_entry_offset < state.rsam_table_size; + sam_entry_offset += 0xc) + ccix_sam_entry(d, sam_entry_offset / 12, + state.rsam_table_offset + sam_entry_offset); + /* Potential Hash mask here */ + if (state.portagg_capable) { + buff = get_conf_long(d, state.rsam_table_offset + state.rsam_table_size - 0x8); + buff2 = get_conf_long(d, state.rsam_table_offset + state.rsam_table_size - 0x4); + printf("\t\t\t\tHashMask: 0x%016lx\n", + ((u64)buff2 << 32) | (buff & 0xffffffc0)); + } + } + + if (state.hsam_table_offset) { + printf("\t\t\tCCIX HSAM Table [%x]\n", state.hsam_table_offset); + for (sam_entry_offset = 0; sam_entry_offset < state.hsam_table_size; + sam_entry_offset += 0xc) + ccix_sam_entry(d, sam_entry_offset / 12, + state.hsam_table_offset + sam_entry_offset); + /* Potential Hash mask here */ + if (state.portagg_capable) { + buff = get_conf_long(d, state.hsam_table_offset + state.hsam_table_size - 0x8); + buff2 = get_conf_long(d, state.hsam_table_offset + state.hsam_table_size - 0x4); + printf("\t\t\t\tHashMask: 0x%016lx\n", + ((u64)buff2 << 32) | (buff & 0xffffffc0)); + } + } +} + +static void +ccix_transport_dvsec(struct device *d, int where, int len) +{ + u32 buff; + int i, j; + + static const char *cal_times[8] = { + "10us", "50us", "100us", "500us", + "1ms", "5ms", "10ms", "50ms"}; + + static const char *quick_eq_times[] = { + "<NotSupp>", + "8ms/16ms", + "24ms/32ms", + "50ms/58ms", + "100ms/108ms", + "200ms/208ms", + }; + + static const char *ext_eq_phase_timeout[] = { + "24 ms / 32 ms ", + "50 ms / 58 ms ", + "100 ms / 108 ms ", + "200 ms / 208 ms ", + "400 ms / 408 ms ", + "600 ms / 608 ms ", + }; + + static const char *reach[4] = { + "SR", "LR", "SR/LR", "Unknown", + }; + + printf("Transport %x>\n", where); + /* CCIX v1.0 specification has a fixed length Transport DVSEC with 17DW */ + if (len < 17 * 4) { + printf("\t\t\t\t<incomplete>\n"); + return; + } + if (!config_fetch(d, where + 0xc, len - 0xc)) { + printf("\t\t\t<ureadable>\n"); + return; + } + buff = get_conf_long(d, where + 0x8) >> 16; + printf("\t\t\tTranCap:\t"); + if (buff & 1) { + printf("ESM+ %s RecalOnrC%c CalTime: %s QuickEqTime: %s\n", + reach[(buff >> 1) & 0x3], FLAG(buff, 0x8), + cal_times[(buff >> 4) & 0x7], + ((buff >> 8) & 0x7) >= ARRAY_SIZE(quick_eq_times) ? + "Unknown" : quick_eq_times[(buff >> 8) & 0x7]); + + buff = get_conf_long(d, where + 0xc); + printf("\t\t\tESMRateCap:\t"); + if (buff & (1 << 0)) + printf("2.5 GT/s "); + if (buff & (1 << 1)) + printf("5 GT/s "); + if (buff & (1 << 2)) + printf("8 GT/s "); + if (buff & (1 << 5)) + printf("16 GT/s "); + if (buff & (1 << 9)) + printf("20 GT/s "); + if (buff & (1 << 14)) + printf("25 GT/s "); + printf("\n"); + + buff = get_conf_long(d, where + 0x14); + printf("\t\t\tESMStatus:\t"); + switch(buff & 0x7f) { + case 0: printf("Inactive "); break; + case 1: printf("2.5 GT/s "); break; + case 2: printf("5 GT/s "); break; + case 3: printf("8 GT/s "); break; + case 6: printf("16 GT/s "); break; + case 10: printf("20 GT/s "); break; + case 15: printf("25 GT/s "); break; + default: + printf("Unknown "); + break; + } + printf("Cal%c\n", FLAG(buff, 0x80)); + + buff = get_conf_long(d, where + 0x18); + printf("\t\t\tESMCtl:\t\tESM0: "); + switch(buff & 0x7f) { + case 0: printf("No speed "); break; + case 3: printf("8 GT/s "); break; + case 6: printf("16 GT/s "); break; + default: + printf("Unknown "); + break; + } + printf("ESM1: "); + switch((buff >> 8) & 0x7f) { + case 0: printf("No speed "); break; + case 6: printf("16 GT/s "); break; + case 10: printf("20 GT/s "); break; + case 15: printf("25 GT/s "); break; + default: + printf("Unknown "); + break; + } + printf("ESM%c ESMCompliance%c ", FLAG(buff, 0x8000), FLAG(buff, 0x80000)); + if (buff & (1 << 24)) + printf("LR\n"); + else + printf("SR\n"); + printf("\t\t\t\t\tExtEqPhase2TimeOut: %s ExtEqPhase3TimeOut: %s\n", + ext_eq_phase_timeout[(buff >> 16) & 0x7], + ext_eq_phase_timeout[(buff >> 20) & 0x7]); + printf("\t\t\t\t\tQuickEqTimeout: %s", + (buff >> 26) & 0x7 > ARRAY_SIZE(quick_eq_times) ? + "Unknown" : quick_eq_times[(buff >> 26) & 0x7]); + + /* Equivalent values in Secondary PCI Express Ext Cap */ + printf("\n\t\t\tESMEqCtl 20GT/s:\t"); + for (i = 0; i < 4; i++) { + buff = get_conf_long(d, where + 0x1c + i * 4); + for (j = 0; j < 4; j++) { + if (i | j) + printf("\t\t\t\t\t\t"); + printf("Lane #%02u: ", i * 4 + j); + printf("Trans Presets US: 0x%x DS: 0x%x\n", + (buff >> (8 * j)) & 0x7, + (buff >> (8 *j + 4)) & 0x7); + } + } + printf("\n\t\t\tESMEqCtl 25GT/s:\t"); + for (i = 0; i < 4; i++) { + buff = get_conf_long(d, where + 0x2c + i * 4); + for (j = 0; j < 4; j++) { + if (i | j) + printf("\t\t\t\t\t\t"); + printf("Lane #%02u: Trans Presets US: 0x%x DS: 0x%x\n", + i * 4 + j, (buff >> (8 * j)) & 0x7, + (buff >> (8 *j + 4)) & 0x7); + } + } + } + else + printf("ESM- \n"); + + buff = get_conf_long(d, where + 0x3c); + printf("\t\t\tTLCap:\t\tOptTLP%c VCResCapInd: %u\n", + FLAG(buff, 0x1), (buff >> 8) & 0x7); + + buff = get_conf_long(d, where + 0x40); + printf("\t\t\tTLCtl:\t\tOptTLP%c LengthCheck%c\n", + FLAG(buff, 0x1), FLAG(buff, 0x2)); +} + +void +cap_ccix(struct device *d, int where) +{ + u32 buff; + u32 len; + + /* First two DW have already been fetched to identify the DVSEC VID */ + if (!config_fetch(d, where + 0x8, 4)) { + printf("\t\t<unreadable>\n"); + return; + } + + printf("\t\t<CCIX "); + buff = get_conf_long(d, where + 0x4); + len = buff >> 20; + + buff = get_conf_long(d, where + 0x8); + switch(buff & 0xFF) { + case 1: + ccix_transport_dvsec(d, where, len); + break; + case 2: + ccix_protocol_dvsec(d, where, len); + break; + default: + printf("\t\t\t<UNKNOWN>\n"); + break; + } +} diff --git a/ls-ecaps.c b/ls-ecaps.c index 4417cd9..85c6084 100644 --- a/ls-ecaps.c +++ b/ls-ecaps.c @@ -797,6 +797,32 @@ cap_ptm(struct device *d, int where) } } +static void +cap_dvsec(struct device *d, int where) +{ + u32 buff; + + printf("Designated Vendor-Specific <>\n"); + + if (verbose < 2) + return; + + if (!config_fetch(d, where + 4, 4)) { + printf("\t\t<unreadable>\n"); + return; + } + buff = get_conf_long(d, where + 4); + printf("\t\tVendor:%4x Version:%u\n", buff & 0xFFFF, (buff >> 16) & 0xF); + switch (buff & 0xffff) { + case PCI_VENDOR_ID_CCIX: + cap_ccix(d, where); + break; + default: + printf("\t\t<UNKNOWN VENDOR %4x %4x>\n", buff & 0xFFFF, PCI_VENDOR_ID_CCIX ); + break; + } +} + void show_ext_caps(struct device *d, int type) { @@ -924,7 +950,7 @@ show_ext_caps(struct device *d, int type) printf("Readiness Time Reporting <?>\n"); break; case PCI_EXT_CAP_ID_DVSEC: - printf("Designated Vendor-Specific <?>\n"); + cap_dvsec(d, where); break; case PCI_EXT_CAP_ID_VF_REBAR: printf("VF Resizable BAR <?>\n"); diff --git a/lspci.h b/lspci.h index fefee52..94efebd 100644 --- a/lspci.h +++ b/lspci.h @@ -75,6 +75,10 @@ void show_caps(struct device *d, int where); void show_ext_caps(struct device *d, int type); +/* ls-ccix.c */ + +void cap_ccix(struct device *d, int where); + /* ls-caps-vendor.c */ void show_vendor_caps(struct device *d, int where, int cap); -- 2.20.1