@@ -1213,6 +1213,43 @@ static void pci_acpi_optimize_delay(struct pci_dev *pdev,
ACPI_FREE(obj);
}
+/* pci_acpi_evaluate_ltr_latency
+ *
+ * @dev - the pci_dev to evaluate and save latencies
+ */
+void pci_acpi_evaluate_ltr_latency(struct pci_dev *dev)
+{
+#ifdef CONFIG_PCIEASPM
+ struct acpi_device *adev;
+ union acpi_object *obj, *elements;
+
+ adev = ACPI_COMPANION(&dev->dev);
+ if (!adev && !pci_dev_is_added(dev))
+ adev = acpi_pci_find_companion(&dev->dev);
+ if (!adev)
+ return;
+
+ if (!acpi_check_dsm(adev->handle, &pci_acpi_dsm_guid, 2,
+ 1ULL << DSM_PCI_LTR_MAX_LATENCY))
+ return;
+ obj = acpi_evaluate_dsm(adev->handle, &pci_acpi_dsm_guid, 2,
+ DSM_PCI_LTR_MAX_LATENCY, NULL);
+ if (!obj)
+ return;
+
+ if (obj->type == ACPI_TYPE_PACKAGE && obj->package.count == 4) {
+ elements = obj->package.elements;
+ dev->max_snoop_latency = (u16)elements[1].integer.value |
+ ((u16)elements[0].integer.value <<
+ PCI_LTR_SCALE_SHIFT);
+ dev->max_nosnoop_latency = (u16)elements[3].integer.value |
+ ((u16)elements[2].integer.value <<
+ PCI_LTR_SCALE_SHIFT);
+ }
+ ACPI_FREE(obj);
+#endif
+}
+
static void pci_acpi_set_external_facing(struct pci_dev *dev)
{
u8 val;
@@ -3056,6 +3056,102 @@ void pci_pm_init(struct pci_dev *dev)
dev->imm_ready = 1;
}
+/**
+ * pci_ltr_decode - Decode the latency to a value in ns
+ * @latency: latency according to PCIe base specification 7.8.2.
+ */
+static u64 pci_ltr_decode(u16 latency)
+{
+ u16 scale = (latency & PCI_LTR_SCALE_MASK) >> 10;
+ u16 value = latency & PCI_LTR_VALUE_MASK;
+
+ switch (scale) {
+ case 0: return value;
+ case 1: return value * 32;
+ case 2: return value * 1024;
+ case 3: return value * 32768;
+ case 4: return value * 1048576;
+ case 5: return value * 33554432;
+ }
+ return 0;
+}
+
+/**
+ * pci_ltr_encode - Encode the value in ns to a 16 bit register value
+ * @val: latency according to PCIe base specification 7.8.2.
+ */
+static u16 pci_ltr_encode(u64 val)
+{
+ if (val <= 1UL * 0x3ff)
+ return (0 << 10) | (val & 0x3ff);
+ if (val <= 32UL * 0x3ff)
+ return (1 << 10) | ((val >> 5) & 0x3ff);
+ if (val <= 1024UL * 0x3ff)
+ return (2 << 10) | ((val >> 10) & 0x3ff);
+ if (val <= 32768UL * 0x3ff)
+ return (3 << 10) | ((val >> 15) & 0x3ff);
+ if (val <= 1048576UL * 0x3ff)
+ return (4 << 10) | ((val >> 20) & 0x3ff);
+ if (val <= 33554432UL * 0x3ff)
+ return (5 << 10) | ((val >> 25) & 0x3ff);
+ return 0;
+}
+
+/**
+ * pci_ltr_init - Initialize Latency Tolerance Information of given PCI device
+ * @dev: PCI device to handle.
+ */
+void pci_ltr_init(struct pci_dev *dev)
+{
+#ifdef CONFIG_PCIEASPM
+ struct pci_dev *bridge = dev;
+ int ltr;
+ u64 max_snoop = 0, max_nosnoop = 0;
+ u16 max_snoop_encoded, max_nosnoop_encoded;
+ u16 max_snoop_current, max_nosnoop_current;
+ u64 max_snoop_current_decoded, max_nosnoop_current_decoded;
+
+ ltr = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_LTR);
+ if (!ltr)
+ return;
+
+ bridge = pci_upstream_bridge(bridge);
+ while (bridge) {
+ max_snoop += pci_ltr_decode(bridge->max_snoop_latency);
+ max_nosnoop += pci_ltr_decode(bridge->max_nosnoop_latency);
+ bridge = pci_upstream_bridge(bridge);
+ }
+ max_snoop_encoded = pci_ltr_encode(max_snoop);
+ max_nosnoop_encoded = pci_ltr_encode(max_nosnoop);
+
+ pci_read_config_word(dev, ltr + PCI_LTR_MAX_SNOOP_LAT,
+ &max_snoop_current);
+ max_snoop_current_decoded = pci_ltr_decode(max_snoop_current);
+ pci_read_config_word(dev, ltr + PCI_LTR_MAX_NOSNOOP_LAT,
+ &max_nosnoop_current);
+ max_nosnoop_current_decoded = pci_ltr_decode(max_nosnoop_current);
+
+ pci_dbg(dev,
+ "Calculated Max Snoop Latency %lluns Max No-Snoop Latency %lluns\n",
+ max_snoop, max_nosnoop);
+
+ if (max_snoop != max_snoop_current_decoded) {
+ pci_info(dev, "Max Snoop Latency %lluns (was %lluns)\n",
+ max_snoop, max_snoop_current_decoded);
+ pci_write_config_word(dev,
+ ltr + PCI_LTR_MAX_SNOOP_LAT,
+ max_snoop_encoded);
+ }
+ if (max_nosnoop != max_nosnoop_current_decoded) {
+ pci_info(dev, "Max No-Snoop Latency %lluns (was %lluns)\n",
+ max_nosnoop, max_nosnoop_current_decoded);
+ pci_write_config_word(dev,
+ ltr + PCI_LTR_MAX_NOSNOOP_LAT,
+ max_nosnoop_encoded);
+ }
+#endif
+}
+
static unsigned long pci_ea_flags(struct pci_dev *dev, u8 prop)
{
unsigned long flags = IORESOURCE_PCI_FIXED | IORESOURCE_PCI_EA_BEI;
@@ -110,6 +110,7 @@ void pci_free_cap_save_buffers(struct pci_dev *dev);
bool pci_bridge_d3_possible(struct pci_dev *dev);
void pci_bridge_d3_update(struct pci_dev *dev);
void pci_bridge_wait_for_secondary_bus(struct pci_dev *dev);
+void pci_ltr_init(struct pci_dev *dev);
static inline void pci_wakeup_event(struct pci_dev *dev)
{
@@ -680,11 +681,13 @@ static inline int pci_aer_raw_clear_status(struct pci_dev *dev) { return -EINVAL
#ifdef CONFIG_ACPI
int pci_acpi_program_hp_params(struct pci_dev *dev);
+void pci_acpi_evaluate_ltr_latency(struct pci_dev *dev);
#else
static inline int pci_acpi_program_hp_params(struct pci_dev *dev)
{
return -ENODEV;
}
+static inline void pci_acpi_evaluate_ltr_latency(struct pci_dev *dev) {}
#endif
#ifdef CONFIG_PCIEASPM
@@ -2105,6 +2105,11 @@ static void pci_configure_ltr(struct pci_dev *dev)
if (!pci_is_pcie(dev))
return;
+ /*
+ * Read latency values from _DSM and save in pci_dev
+ * as a part of latency tolerance reporting mechanism.
+ */
+ pci_acpi_evaluate_ltr_latency(dev);
pcie_capability_read_dword(dev, PCI_EXP_DEVCAP2, &cap);
if (!(cap & PCI_EXP_DEVCAP2_LTR))
@@ -2400,6 +2405,7 @@ static void pci_init_capabilities(struct pci_dev *dev)
pci_ptm_init(dev); /* Precision Time Measurement */
pci_aer_init(dev); /* Advanced Error Reporting */
pci_dpc_init(dev); /* Downstream Port Containment */
+ pci_ltr_init(dev); /* Latency Tolerance Reporting */
pcie_report_downtraining(dev);
@@ -110,6 +110,7 @@ extern const guid_t pci_acpi_dsm_guid;
/* _DSM Definitions for PCI */
#define DSM_PCI_PRESERVE_BOOT_CONFIG 0x05
+#define DSM_PCI_LTR_MAX_LATENCY 0x06
#define DSM_PCI_DEVICE_NAME 0x07
#define DSM_PCI_POWER_ON_RESET_DELAY 0x08
#define DSM_PCI_DEVICE_READINESS_DURATIONS 0x09
@@ -380,6 +380,8 @@ struct pci_dev {
struct pcie_link_state *link_state; /* ASPM link state */
unsigned int ltr_path:1; /* Latency Tolerance Reporting
supported from root to here */
+ u16 max_snoop_latency; /* LTR Max Snoop latency */
+ u16 max_nosnoop_latency; /* LTR Max No-Snoop latency */
#endif
unsigned int eetlp_prefix_path:1; /* End-to-End TLP Prefix */
Add a new function pci_ltr_init() which will be called from pci_init_capabilities() to initialize every PCIe device's LTR values. Add code in probe.c to evaluate LTR _DSM and save the latencies in pci_dev. Signed-off-by: Puranjay Mohan <puranjay12@gmail.com> --- v5 - Add static keyword with pci_ltr_encode() and pci_ltr_decode() v4 - remove pci_upstream_bridge and use acpi_pci_find_companion() to get the handle for evaluating _DSM. Add acpi_check_dsm() Change the logging mechanism. v3 - change the algorithm to add latencies, add helper funtions for encoding and decoding the latencies. v2 - add an #ifdefin pci-acpi.c to fix the bug reported by test bot. v1 - based the patch on v5.9-rc1 so it applies correctly. --- drivers/pci/pci-acpi.c | 37 ++++++++++++++++ drivers/pci/pci.c | 96 ++++++++++++++++++++++++++++++++++++++++ drivers/pci/pci.h | 3 ++ drivers/pci/probe.c | 6 +++ include/linux/pci-acpi.h | 1 + include/linux/pci.h | 2 + 6 files changed, 145 insertions(+)