linux-pci.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v6 0/2] PCI/ASPM: Add support for LTR _DSM
@ 2020-10-01 21:44 Bjorn Helgaas
  2020-10-01 21:44 ` [PATCH v6 1/2] PCI/ASPM: Rename encode_l12_threshold(), convert arg to ns Bjorn Helgaas
  2020-10-01 21:44 ` [PATCH v6 2/2] PCI/ASPM: Add support for LTR _DSM Bjorn Helgaas
  0 siblings, 2 replies; 4+ messages in thread
From: Bjorn Helgaas @ 2020-10-01 21:44 UTC (permalink / raw)
  To: Puranjay Mohan; +Cc: Shuah Khan, linux-pci, linux-kernel-mentees, Bjorn Helgaas

From: Bjorn Helgaas <bhelgaas@google.com>

This is a v6 of Puranjay's LTR _DSM support.  It should be functionally
equivalent, but I moved the bulk of the code from pci.c to aspm.c, since
it's only used for ASPM.  I also combined pci_ltr_encode() with the
existing encode_l12_threshold(), since they were doing essentially the same
thing.

I'll reply to the patches with a few more comments.

Bjorn Helgaas (1):
  PCI/ASPM: Rename encode_l12_threshold(), convert arg to ns

Puranjay Mohan (1):
  PCI/ASPM: Add support for LTR _DSM

 drivers/pci/pci-acpi.c   |  35 +++++++++++++
 drivers/pci/pci.h        |   4 ++
 drivers/pci/pcie/aspm.c  | 110 +++++++++++++++++++++++++++++----------
 drivers/pci/probe.c      |   4 ++
 include/linux/pci-acpi.h |   1 +
 include/linux/pci.h      |   2 +
 6 files changed, 129 insertions(+), 27 deletions(-)

-- 
2.25.1


^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH v6 1/2] PCI/ASPM: Rename encode_l12_threshold(), convert arg to ns
  2020-10-01 21:44 [PATCH v6 0/2] PCI/ASPM: Add support for LTR _DSM Bjorn Helgaas
@ 2020-10-01 21:44 ` Bjorn Helgaas
  2020-10-01 21:44 ` [PATCH v6 2/2] PCI/ASPM: Add support for LTR _DSM Bjorn Helgaas
  1 sibling, 0 replies; 4+ messages in thread
From: Bjorn Helgaas @ 2020-10-01 21:44 UTC (permalink / raw)
  To: Puranjay Mohan; +Cc: Shuah Khan, linux-pci, linux-kernel-mentees, Bjorn Helgaas

From: Bjorn Helgaas <bhelgaas@google.com>

Rename encode_l12_threshold() to pci_lat_encode() and convert its argument
from microseconds to nanoseconds so we can share it between
LTR_L1.2_THRESHOLD encoding and LTR Max Snoop/No-Snoop Latency encoding.

Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
---
 drivers/pci/pcie/aspm.c | 39 ++++++++++++---------------------------
 1 file changed, 12 insertions(+), 27 deletions(-)

diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c
index 253c30cc1967..beb6e2e4e5d2 100644
--- a/drivers/pci/pcie/aspm.c
+++ b/drivers/pci/pcie/aspm.c
@@ -307,6 +307,17 @@ static void pcie_aspm_configure_common_clock(struct pcie_link_state *link)
 	pcie_capability_write_word(parent, PCI_EXP_LNKCTL, parent_reg);
 }
 
+static void pci_lat_encode(u64 lat, u32 *scale, u32 *val)
+{
+	/* See PCIe r5.0, sec 7.8.3.3 and sec 6.18 */
+	     if (lat < 32)	 { *scale = 0; *val = (lat >>  0) & 0x3ff; }
+	else if (lat < 1024)	 { *scale = 1; *val = (lat >>  5) & 0x3ff; }
+	else if (lat < 32768)	 { *scale = 2; *val = (lat >> 10) & 0x3ff; }
+	else if (lat < 1048576)	 { *scale = 3; *val = (lat >> 15) & 0x3ff; }
+	else if (lat < 33554432) { *scale = 4; *val = (lat >> 20) & 0x3ff; }
+	else			 { *scale = 5; *val = (lat >> 25) & 0x3ff; }
+}
+
 /* Convert L0s latency encoding to ns */
 static u32 calc_l0s_latency(u32 encoding)
 {
@@ -354,32 +365,6 @@ static u32 calc_l1ss_pwron(struct pci_dev *pdev, u32 scale, u32 val)
 	return 0;
 }
 
-static void encode_l12_threshold(u32 threshold_us, u32 *scale, u32 *value)
-{
-	u32 threshold_ns = threshold_us * 1000;
-
-	/* See PCIe r3.1, sec 7.33.3 and sec 6.18 */
-	if (threshold_ns < 32) {
-		*scale = 0;
-		*value = threshold_ns;
-	} else if (threshold_ns < 1024) {
-		*scale = 1;
-		*value = threshold_ns >> 5;
-	} else if (threshold_ns < 32768) {
-		*scale = 2;
-		*value = threshold_ns >> 10;
-	} else if (threshold_ns < 1048576) {
-		*scale = 3;
-		*value = threshold_ns >> 15;
-	} else if (threshold_ns < 33554432) {
-		*scale = 4;
-		*value = threshold_ns >> 20;
-	} else {
-		*scale = 5;
-		*value = threshold_ns >> 25;
-	}
-}
-
 struct aspm_register_info {
 	u32 support:2;
 	u32 enabled:2;
@@ -539,7 +524,7 @@ static void aspm_calc_l1ss_info(struct pcie_link_state *link,
 	 * least 4us.
 	 */
 	l1_2_threshold = 2 + 4 + t_common_mode + t_power_on;
-	encode_l12_threshold(l1_2_threshold, &scale, &value);
+	pci_lat_encode(l1_2_threshold * 1000, &scale, &value);
 	link->l1ss.ctl1 |= t_common_mode << 8 | scale << 29 | value << 16;
 }
 
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH v6 2/2] PCI/ASPM: Add support for LTR _DSM
  2020-10-01 21:44 [PATCH v6 0/2] PCI/ASPM: Add support for LTR _DSM Bjorn Helgaas
  2020-10-01 21:44 ` [PATCH v6 1/2] PCI/ASPM: Rename encode_l12_threshold(), convert arg to ns Bjorn Helgaas
@ 2020-10-01 21:44 ` Bjorn Helgaas
  2020-10-01 22:17   ` [Linux-kernel-mentees] " Bjorn Helgaas
  1 sibling, 1 reply; 4+ messages in thread
From: Bjorn Helgaas @ 2020-10-01 21:44 UTC (permalink / raw)
  To: Puranjay Mohan; +Cc: Shuah Khan, linux-pci, linux-kernel-mentees, Bjorn Helgaas

From: Puranjay Mohan <puranjay12@gmail.com>

Latency Tolerance Reporting (LTR) is required for the ASPM L1.2 PM
substate.  Devices with Upstream Ports (Endpoints and Switches) may support
the optional LTR Capability.  When LTR is enabled, devices transmit LTR
messages containing Snoop and No-Snoop Latencies upstream.  The L1.2
substate may be entered if the most recent LTR values are greater than or
equal to the LTR_L1.2_THRESHOLD from the L1 PM Substates Control 1
register.

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.

Link: https://lore.kernel.org/r/20200926052917.20247-1-puranjay12@gmail.com
Signed-off-by: Puranjay Mohan <puranjay12@gmail.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
---
 drivers/pci/pci-acpi.c   | 35 ++++++++++++++++++++
 drivers/pci/pci.h        |  4 +++
 drivers/pci/pcie/aspm.c  | 71 ++++++++++++++++++++++++++++++++++++++++
 drivers/pci/probe.c      |  4 +++
 include/linux/pci-acpi.h |  1 +
 include/linux/pci.h      |  2 ++
 6 files changed, 117 insertions(+)

diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c
index d5869a03f748..dc1623de3475 100644
--- a/drivers/pci/pci-acpi.c
+++ b/drivers/pci/pci-acpi.c
@@ -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;
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index fa12f7cbc1a0..cc1cc6f09530 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -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
diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c
index beb6e2e4e5d2..0ca25628ba4a 100644
--- a/drivers/pci/pcie/aspm.c
+++ b/drivers/pci/pcie/aspm.c
@@ -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)
 {
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index 03d37128a24f..25ed03cd4757 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -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);
 
diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h
index 5ba475ca9078..e23236a4ff66 100644
--- a/include/linux/pci-acpi.h
+++ b/include/linux/pci-acpi.h
@@ -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
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 835530605c0d..af7558e3c331 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -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 */
 
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [Linux-kernel-mentees] [PATCH v6 2/2] PCI/ASPM: Add support for LTR _DSM
  2020-10-01 21:44 ` [PATCH v6 2/2] PCI/ASPM: Add support for LTR _DSM Bjorn Helgaas
@ 2020-10-01 22:17   ` Bjorn Helgaas
  0 siblings, 0 replies; 4+ messages in thread
From: Bjorn Helgaas @ 2020-10-01 22:17 UTC (permalink / raw)
  To: Puranjay Mohan; +Cc: Bjorn Helgaas, linux-pci, linux-kernel-mentees

On Thu, Oct 01, 2020 at 04:44:36PM -0500, Bjorn Helgaas wrote:
> From: Puranjay Mohan <puranjay12@gmail.com>
> 
> Latency Tolerance Reporting (LTR) is required for the ASPM L1.2 PM
> substate.  Devices with Upstream Ports (Endpoints and Switches) may support
> the optional LTR Capability.  When LTR is enabled, devices transmit LTR
> messages containing Snoop and No-Snoop Latencies upstream.  The L1.2
> substate may be entered if the most recent LTR values are greater than or
> equal to the LTR_L1.2_THRESHOLD from the L1 PM Substates Control 1
> register.
> 
> 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.

> --- a/drivers/pci/probe.c
> +++ b/drivers/pci/probe.c
> @@ -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 */

I don't think we're doing this in quite the right order.  If I
understand correctly, we have this:

  pci_device_add
    pci_configure_device
      pci_configure_ltr
        pci_acpi_evaluate_ltr_latency
          acpi_evaluate_dsm(DSM_PCI_LTR_MAX_LATENCY)
        pcie_capability_set_word(PCI_EXP_DEVCTL2_LTR_EN)  <-- enable LTR
    pci_init_capabilities
      pci_ltr_init
        pci_write_config_word(PCI_LTR_MAX_SNOOP_LAT)  <-- set LTR msg content

So we enable LTR messages before we set the latency values in the LTR
capability.  I think we need to set the values *first*.

>  	pcie_report_downtraining(dev);

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2020-10-01 22:17 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-10-01 21:44 [PATCH v6 0/2] PCI/ASPM: Add support for LTR _DSM Bjorn Helgaas
2020-10-01 21:44 ` [PATCH v6 1/2] PCI/ASPM: Rename encode_l12_threshold(), convert arg to ns Bjorn Helgaas
2020-10-01 21:44 ` [PATCH v6 2/2] PCI/ASPM: Add support for LTR _DSM Bjorn Helgaas
2020-10-01 22:17   ` [Linux-kernel-mentees] " Bjorn Helgaas

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).