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 * @level: Printk log level 102 * @pfx: String prefix 103 * 104 * Prints TLP Header and Prefix Log information held by @log. 105 */ 106 void pcie_print_tlp_log(const struct pci_dev *dev, 107 const struct pcie_tlp_log *log, const char *level, 108 const char *pfx) 109 { 110 /* EE_PREFIX_STR fits the extended DW space needed for the Flit mode */ 111 char buf[11 * PCIE_STD_MAX_TLP_HEADERLOG + 1]; 112 unsigned int i; 113 int len; 114 115 len = scnprintf(buf, sizeof(buf), "%#010x %#010x %#010x %#010x", 116 log->dw[0], log->dw[1], log->dw[2], log->dw[3]); 117 118 if (log->flit) { 119 for (i = PCIE_STD_NUM_TLP_HEADERLOG; i < log->header_len; i++) { 120 len += scnprintf(buf + len, sizeof(buf) - len, 121 " %#010x", log->dw[i]); 122 } 123 } else { 124 if (log->prefix[0]) 125 len += scnprintf(buf + len, sizeof(buf) - len, 126 EE_PREFIX_STR); 127 for (i = 0; i < ARRAY_SIZE(log->prefix); i++) { 128 if (!log->prefix[i]) 129 break; 130 len += scnprintf(buf + len, sizeof(buf) - len, 131 " %#010x", log->prefix[i]); 132 } 133 } 134 135 dev_printk(level, &dev->dev, "%sTLP Header%s: %s\n", pfx, 136 log->flit ? " (Flit)" : "", buf); 137 } 138