@@ -1213,6 +1213,41 @@ static void pci_acpi_optimize_delay(struct pci_dev *pdev,
ACPI_FREE(obj);
}
+/* pci_acpi_evaluate_ltr_latency
+ *
+ * @dev - pci_dev for which to evaluate Latency Tolerance Reporting _DSM
+ */
+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 |
+ (elements[0].integer.value << PCI_LTR_SCALE_SHIFT));
+ dev->max_nosnoop_latency = (u16) (elements[3].integer.value |
+ (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;
@@ -561,11 +561,13 @@ pci_ers_result_t pcie_do_recovery(struct pci_dev *dev,
bool pcie_wait_for_link(struct pci_dev *pdev, bool active);
#ifdef CONFIG_PCIEASPM
+void pci_ltr_init(struct pci_dev *dev);
void pcie_aspm_init_link_state(struct pci_dev *pdev);
void pcie_aspm_exit_link_state(struct pci_dev *pdev);
void pcie_aspm_pm_state_change(struct pci_dev *pdev);
void pcie_aspm_powersave_config_link(struct pci_dev *pdev);
#else
+static inline void pci_ltr_init(struct pci_dev *pdev) { }
static inline void pcie_aspm_init_link_state(struct pci_dev *pdev) { }
static inline void pcie_aspm_exit_link_state(struct pci_dev *pdev) { }
static inline void pcie_aspm_pm_state_change(struct pci_dev *pdev) { }
@@ -680,11 +682,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
@@ -318,6 +318,77 @@ static void pci_lat_encode(u64 lat, u32 *scale, u32 *val)
else { *scale = 5; *val = (lat >> 25) & 0x3ff; }
}
+/**
+ * pci_lat_decode - Decode the latency to a value in ns
+ * @latency: latency register value
+ *
+ * @latency is in the format of the LTR Capability Max Snoop Latency and
+ * Max No-Snoop Latency registers (see PCIe r5.0, sec 7.8.1 and 6.18).
+ */
+static u64 pci_lat_decode(u16 latency)
+{
+ u16 scale = (latency & PCI_LTR_SCALE_MASK) >> 10;
+ u64 val = latency & PCI_LTR_VALUE_MASK;
+
+ switch (scale) {
+ case 0: return val << 0;
+ case 1: return val << 5;
+ case 2: return val << 10;
+ case 3: return val << 15;
+ case 4: return val << 20;
+ case 5: return val << 25;
+ }
+ return 0;
+}
+
+/**
+ * pci_ltr_init - Initialize Latency Tolerance Reporting capability of
+ * given PCI device
+ * @dev: PCI device
+ */
+void pci_ltr_init(struct pci_dev *dev)
+{
+ int ltr;
+ struct pci_dev *bridge;
+ u64 snoop = 0, nosnoop = 0;
+ u32 scale, val;
+ u16 snoop_enc, snoop_cur, nosnoop_enc, nosnoop_cur;
+
+ ltr = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_LTR);
+ if (!ltr)
+ return;
+
+ bridge = pci_upstream_bridge(dev);
+ while (bridge) {
+ snoop += pci_lat_decode(bridge->max_snoop_latency);
+ nosnoop += pci_lat_decode(bridge->max_nosnoop_latency);
+ bridge = pci_upstream_bridge(bridge);
+ }
+
+ pci_dbg(dev, "calculated Max Snoop Latency %lluns Max No-Snoop Latency %lluns\n",
+ snoop, nosnoop);
+
+ pci_lat_encode(snoop, &scale, &val);
+ snoop_enc = (scale << 10) | val;
+ pci_read_config_word(dev, ltr + PCI_LTR_MAX_SNOOP_LAT, &snoop_cur);
+ if (snoop_enc != snoop_cur) {
+ pci_info(dev, "setting Max Snoop Latency %lluns (was %lluns)\n",
+ snoop, pci_lat_decode(snoop_cur));
+ pci_write_config_word(dev, ltr + PCI_LTR_MAX_SNOOP_LAT,
+ snoop_enc);
+ }
+
+ pci_lat_encode(nosnoop, &scale, &val);
+ nosnoop_enc = (scale << 10) | val;
+ pci_read_config_word(dev, ltr + PCI_LTR_MAX_NOSNOOP_LAT, &nosnoop_cur);
+ if (nosnoop_enc != nosnoop_cur) {
+ pci_info(dev, "setting Max No-Snoop Latency %lluns (was %lluns)\n",
+ nosnoop, pci_lat_decode(nosnoop_cur));
+ pci_write_config_word(dev, ltr + PCI_LTR_MAX_NOSNOOP_LAT,
+ nosnoop_enc);
+ }
+}
+
/* Convert L0s latency encoding to ns */
static u32 calc_l0s_latency(u32 encoding)
{
@@ -2106,6 +2106,9 @@ static void pci_configure_ltr(struct pci_dev *dev)
if (!pci_is_pcie(dev))
return;
+ /* Read latency values (if any) from platform */
+ pci_acpi_evaluate_ltr_latency(dev);
+
pcie_capability_read_dword(dev, PCI_EXP_DEVCAP2, &cap);
if (!(cap & PCI_EXP_DEVCAP2_LTR))
return;
@@ -2400,6 +2403,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 */