2025-01-14 19:08:34 +02:00
|
|
|
// SPDX-License-Identifier: GPL-2.0
|
|
|
|
/*
|
|
|
|
* PCIe TLP Log handling
|
|
|
|
*
|
|
|
|
* Copyright (C) 2024 Intel Corporation
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/aer.h>
|
2025-01-14 19:08:40 +02:00
|
|
|
#include <linux/array_size.h>
|
2025-02-07 18:18:36 +02:00
|
|
|
#include <linux/bitfield.h>
|
2025-01-14 19:08:34 +02:00
|
|
|
#include <linux/pci.h>
|
|
|
|
#include <linux/string.h>
|
|
|
|
|
|
|
|
#include "../pci.h"
|
|
|
|
|
2025-01-14 19:08:39 +02:00
|
|
|
/**
|
|
|
|
* aer_tlp_log_len - Calculate AER Capability TLP Header/Prefix Log length
|
|
|
|
* @dev: PCIe device
|
|
|
|
* @aercc: AER Capabilities and Control register value
|
|
|
|
*
|
|
|
|
* Return: TLP Header/Prefix Log length
|
|
|
|
*/
|
|
|
|
unsigned int aer_tlp_log_len(struct pci_dev *dev, u32 aercc)
|
|
|
|
{
|
2025-02-07 18:18:36 +02:00
|
|
|
if (aercc & PCI_ERR_CAP_TLP_LOG_FLIT)
|
|
|
|
return FIELD_GET(PCI_ERR_CAP_TLP_LOG_SIZE, aercc);
|
|
|
|
|
2025-01-14 19:08:39 +02:00
|
|
|
return PCIE_STD_NUM_TLP_HEADERLOG +
|
|
|
|
((aercc & PCI_ERR_CAP_PREFIX_LOG_PRESENT) ?
|
|
|
|
dev->eetlp_prefix_max : 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CONFIG_PCIE_DPC
|
|
|
|
/**
|
|
|
|
* dpc_tlp_log_len - Calculate DPC RP PIO TLP Header/Prefix Log length
|
|
|
|
* @dev: PCIe device
|
|
|
|
*
|
|
|
|
* Return: TLP Header/Prefix Log length
|
|
|
|
*/
|
|
|
|
unsigned int dpc_tlp_log_len(struct pci_dev *dev)
|
|
|
|
{
|
|
|
|
/* Remove ImpSpec Log register from the count */
|
|
|
|
if (dev->dpc_rp_log_size >= PCIE_STD_NUM_TLP_HEADERLOG + 1)
|
|
|
|
return dev->dpc_rp_log_size - 1;
|
|
|
|
|
|
|
|
return dev->dpc_rp_log_size;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2025-01-14 19:08:34 +02:00
|
|
|
/**
|
|
|
|
* pcie_read_tlp_log - read TLP Header Log
|
|
|
|
* @dev: PCIe device
|
|
|
|
* @where: PCI Config offset of TLP Header Log
|
2025-01-14 19:08:39 +02:00
|
|
|
* @where2: PCI Config offset of TLP Prefix Log
|
|
|
|
* @tlp_len: TLP Log length (Header Log + TLP Prefix Log in DWORDs)
|
2025-02-07 18:18:36 +02:00
|
|
|
* @flit: TLP Logged in Flit mode
|
2025-01-14 19:08:36 +02:00
|
|
|
* @log: TLP Log structure to fill
|
2025-01-14 19:08:34 +02:00
|
|
|
*
|
2025-01-14 19:08:36 +02:00
|
|
|
* Fill @log from TLP Header Log registers, e.g., AER or DPC.
|
2025-01-14 19:08:34 +02:00
|
|
|
*
|
|
|
|
* Return: 0 on success and filled TLP Log structure, <0 on error.
|
|
|
|
*/
|
2025-01-14 19:08:39 +02:00
|
|
|
int pcie_read_tlp_log(struct pci_dev *dev, int where, int where2,
|
2025-02-07 18:18:36 +02:00
|
|
|
unsigned int tlp_len, bool flit, struct pcie_tlp_log *log)
|
2025-01-14 19:08:34 +02:00
|
|
|
{
|
2025-01-14 19:08:37 +02:00
|
|
|
unsigned int i;
|
2025-01-14 19:08:39 +02:00
|
|
|
int off, ret;
|
2025-02-07 18:18:36 +02:00
|
|
|
|
|
|
|
if (tlp_len > ARRAY_SIZE(log->dw))
|
|
|
|
tlp_len = ARRAY_SIZE(log->dw);
|
2025-01-14 19:08:34 +02:00
|
|
|
|
2025-01-14 19:08:36 +02:00
|
|
|
memset(log, 0, sizeof(*log));
|
2025-01-14 19:08:34 +02:00
|
|
|
|
2025-01-14 19:08:39 +02:00
|
|
|
for (i = 0; i < tlp_len; i++) {
|
2025-02-07 18:18:36 +02:00
|
|
|
if (i < PCIE_STD_NUM_TLP_HEADERLOG)
|
2025-01-14 19:08:39 +02:00
|
|
|
off = where + i * 4;
|
2025-02-07 18:18:36 +02:00
|
|
|
else
|
2025-01-14 19:08:39 +02:00
|
|
|
off = where2 + (i - PCIE_STD_NUM_TLP_HEADERLOG) * 4;
|
|
|
|
|
2025-02-07 18:18:36 +02:00
|
|
|
ret = pci_read_config_dword(dev, off, &log->dw[i]);
|
2025-01-14 19:08:34 +02:00
|
|
|
if (ret)
|
|
|
|
return pcibios_err_to_errno(ret);
|
|
|
|
}
|
|
|
|
|
2025-02-07 18:18:36 +02:00
|
|
|
/*
|
|
|
|
* Hard-code non-Flit mode to 4 DWORDs, for now. The exact length
|
|
|
|
* can only be known if the TLP is parsed.
|
|
|
|
*/
|
|
|
|
log->header_len = flit ? tlp_len : 4;
|
|
|
|
log->flit = flit;
|
|
|
|
|
2025-01-14 19:08:34 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2025-01-14 19:08:40 +02:00
|
|
|
|
|
|
|
#define EE_PREFIX_STR " E-E Prefixes:"
|
|
|
|
|
|
|
|
/**
|
|
|
|
* pcie_print_tlp_log - Print TLP Header / Prefix Log contents
|
|
|
|
* @dev: PCIe device
|
|
|
|
* @log: TLP Log structure
|
2025-05-22 18:21:19 -05:00
|
|
|
* @level: Printk log level
|
2025-01-14 19:08:40 +02:00
|
|
|
* @pfx: String prefix
|
|
|
|
*
|
|
|
|
* Prints TLP Header and Prefix Log information held by @log.
|
|
|
|
*/
|
|
|
|
void pcie_print_tlp_log(const struct pci_dev *dev,
|
2025-05-22 18:21:19 -05:00
|
|
|
const struct pcie_tlp_log *log, const char *level,
|
|
|
|
const char *pfx)
|
2025-01-14 19:08:40 +02:00
|
|
|
{
|
2025-02-07 18:18:36 +02:00
|
|
|
/* EE_PREFIX_STR fits the extended DW space needed for the Flit mode */
|
|
|
|
char buf[11 * PCIE_STD_MAX_TLP_HEADERLOG + 1];
|
2025-01-14 19:08:40 +02:00
|
|
|
unsigned int i;
|
|
|
|
int len;
|
|
|
|
|
|
|
|
len = scnprintf(buf, sizeof(buf), "%#010x %#010x %#010x %#010x",
|
|
|
|
log->dw[0], log->dw[1], log->dw[2], log->dw[3]);
|
|
|
|
|
2025-02-07 18:18:36 +02:00
|
|
|
if (log->flit) {
|
|
|
|
for (i = PCIE_STD_NUM_TLP_HEADERLOG; i < log->header_len; i++) {
|
|
|
|
len += scnprintf(buf + len, sizeof(buf) - len,
|
|
|
|
" %#010x", log->dw[i]);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (log->prefix[0])
|
|
|
|
len += scnprintf(buf + len, sizeof(buf) - len,
|
|
|
|
EE_PREFIX_STR);
|
|
|
|
for (i = 0; i < ARRAY_SIZE(log->prefix); i++) {
|
|
|
|
if (!log->prefix[i])
|
|
|
|
break;
|
|
|
|
len += scnprintf(buf + len, sizeof(buf) - len,
|
|
|
|
" %#010x", log->prefix[i]);
|
|
|
|
}
|
2025-01-14 19:08:40 +02:00
|
|
|
}
|
|
|
|
|
2025-05-22 18:21:19 -05:00
|
|
|
dev_printk(level, &dev->dev, "%sTLP Header%s: %s\n", pfx,
|
2025-02-07 18:18:36 +02:00
|
|
|
log->flit ? " (Flit)" : "", buf);
|
2025-01-14 19:08:40 +02:00
|
|
|
}
|