1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * PCIe TLP Log handling 4 * 5 * Copyright (C) 2024 Intel Corporation 6 */ 7 8 #include <linux/aer.h> 9 #include <linux/array_size.h> 10 #include <linux/bitfield.h> 11 #include <linux/pci.h> 12 #include <linux/string.h> 13 14 #include "../pci.h" 15 16 /** 17 * aer_tlp_log_len - Calculate AER Capability TLP Header/Prefix Log length 18 * @dev: PCIe device 19 * @aercc: AER Capabilities and Control register value 20 * 21 * Return: TLP Header/Prefix Log length 22 */ 23 unsigned int aer_tlp_log_len(struct pci_dev *dev, u32 aercc) 24 { 25 if (aercc & PCI_ERR_CAP_TLP_LOG_FLIT) 26 return FIELD_GET(PCI_ERR_CAP_TLP_LOG_SIZE, aercc); 27 28 return PCIE_STD_NUM_TLP_HEADERLOG + 29 ((aercc & PCI_ERR_CAP_PREFIX_LOG_PRESENT) ? 30 dev->eetlp_prefix_max : 0); 31 } 32 33 #ifdef CONFIG_PCIE_DPC 34 /** 35 * dpc_tlp_log_len - Calculate DPC RP PIO TLP Header/Prefix Log length 36 * @dev: PCIe device 37 * 38 * Return: TLP Header/Prefix Log length 39 */ 40 unsigned int dpc_tlp_log_len(struct pci_dev *dev) 41 { 42 /* Remove ImpSpec Log register from the count */ 43 if (dev->dpc_rp_log_size >= PCIE_STD_NUM_TLP_HEADERLOG + 1) 44 return dev->dpc_rp_log_size - 1; 45 46 return dev->dpc_rp_log_size; 47 } 48 #endif 49 50 /** 51 * pcie_read_tlp_log - read TLP Header Log 52 * @dev: PCIe device 53 * @where: PCI Config offset of TLP Header Log 54 * @where2: PCI Config offset of TLP Prefix Log 55 * @tlp_len: TLP Log length (Header Log + TLP Prefix Log in DWORDs) 56 * @flit: TLP Logged in Flit mode 57 * @log: TLP Log structure to fill 58 * 59 * Fill @log from TLP Header Log registers, e.g., AER or DPC. 60 * 61 * Return: 0 on success and filled TLP Log structure, <0 on error. 62 */ 63 int pcie_read_tlp_log(struct pci_dev *dev, int where, int where2, 64 unsigned int tlp_len, bool flit, struct pcie_tlp_log *log) 65 { 66 unsigned int i; 67 int off, ret; 68 69 if (tlp_len > ARRAY_SIZE(log->dw)) 70 tlp_len = ARRAY_SIZE(log->dw); 71 72 memset(log, 0, sizeof(*log)); 73 74 for (i = 0; i < tlp_len; i++) { 75 if (i < PCIE_STD_NUM_TLP_HEADERLOG) 76 off = where + i * 4; 77 else 78 off = where2 + (i - PCIE_STD_NUM_TLP_HEADERLOG) * 4; 79 80 ret = pci_read_config_dword(dev, off, &log->dw[i]); 81 if (ret) 82 return pcibios_err_to_errno(ret); 83 } 84 85 /* 86 * Hard-code non-Flit mode to 4 DWORDs, for now. The exact length 87 * can only be known if the TLP is parsed. 88 */ 89 log->header_len = flit ? tlp_len : 4; 90 log->flit = flit; 91 92 return 0; 93 } 94 95 #define EE_PREFIX_STR " E-E Prefixes:" 96 97 /** 98 * pcie_print_tlp_log - Print TLP Header / Prefix Log contents 99 * @dev: PCIe device 100 * @log: TLP Log structure 101 * @pfx: String prefix 102 * 103 * Prints TLP Header and Prefix Log information held by @log. 104 */ 105 void pcie_print_tlp_log(const struct pci_dev *dev, 106 const struct pcie_tlp_log *log, const char *pfx) 107 { 108 /* EE_PREFIX_STR fits the extended DW space needed for the Flit mode */ 109 char buf[11 * PCIE_STD_MAX_TLP_HEADERLOG + 1]; 110 unsigned int i; 111 int len; 112 113 len = scnprintf(buf, sizeof(buf), "%#010x %#010x %#010x %#010x", 114 log->dw[0], log->dw[1], log->dw[2], log->dw[3]); 115 116 if (log->flit) { 117 for (i = PCIE_STD_NUM_TLP_HEADERLOG; i < log->header_len; i++) { 118 len += scnprintf(buf + len, sizeof(buf) - len, 119 " %#010x", log->dw[i]); 120 } 121 } else { 122 if (log->prefix[0]) 123 len += scnprintf(buf + len, sizeof(buf) - len, 124 EE_PREFIX_STR); 125 for (i = 0; i < ARRAY_SIZE(log->prefix); i++) { 126 if (!log->prefix[i]) 127 break; 128 len += scnprintf(buf + len, sizeof(buf) - len, 129 " %#010x", log->prefix[i]); 130 } 131 } 132 133 pci_err(dev, "%sTLP Header%s: %s\n", pfx, 134 log->flit ? " (Flit)" : "", buf); 135 } 136