Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

[PATCH 4/6] PCI/ASPM: Calculate and save the L1.2 timing parameters

19 views
Skip to first unread message

Rajat Jain

unread,
Jan 3, 2017, 1:40:05 AM1/3/17
to
Calculate and save the timing parameters that need to be programmed
if we need to enable L1.2 substates later.

We use the same logic (and a constant value for 1 of the
parameters) as used by Intel's coreboot:

https://www.coreboot.org/pipermail/coreboot-gerrit/2015-March/021134.html
https://review.coreboot.org/#/c/8832/

Signed-off-by: Rajat Jain <raj...@google.com>
---
drivers/pci/pcie/aspm.c | 79 +++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 79 insertions(+)

diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c
index 7a3ad85..a70afdf 100644
--- a/drivers/pci/pcie/aspm.c
+++ b/drivers/pci/pcie/aspm.c
@@ -42,6 +42,18 @@
#define ASPM_STATE_ALL (ASPM_STATE_L0S | ASPM_STATE_L1 | \
ASPM_STATE_L1SS)

+/*
+ * When L1 substates are enabled, the LTR L1.2 threshold is a timing parameter
+ * that decides whether L1.1 or L1.2 is entered (Refer PCIe spec for details).
+ * Not sure is there is a way to "calculate" this on the fly, but may be we
+ * could turn it into a parameter in future. This value has been taken from
+ * the following files from Intel's coreboot (which is the only code I found
+ * to have used this):
+ * https://www.coreboot.org/pipermail/coreboot-gerrit/2015-March/021134.html
+ * https://review.coreboot.org/#/c/8832/
+ */
+#define LTR_L1_2_THRESHOLD_BITS ((1 << 21) | (1 << 23) | (1 << 30))
+
struct aspm_latency {
u32 l0s; /* L0s latency (nsec) */
u32 l1; /* L1 latency (nsec) */
@@ -76,6 +88,14 @@ struct pcie_link_state {
* has one slot under it, so at most there are 8 functions.
*/
struct aspm_latency acceptable[8];
+
+ /* L1 PM Substate info */
+ struct {
+ u32 up_cap_ptr; /* L1SS cap ptr in upstream dev */
+ u32 dw_cap_ptr; /* L1SS cap ptr in downstream dev */
+ u32 ctl1; /* value to be programmed in ctl1 */
+ u32 ctl2; /* value to be programmed in ctl2 */
+ } l1ss;
};

static int aspm_disabled, aspm_force;
@@ -296,6 +316,22 @@ static u32 calc_l1_acceptable(u32 encoding)
return (1000 << encoding);
}

+/* Convert L1SS T_pwr encoding to usec */
+static u32 calc_l1ss_pwron(struct pci_dev *pdev, u32 scale, u32 val)
+{
+ switch (scale) {
+ case 0:
+ return val * 2;
+ case 1:
+ return val * 10;
+ case 2:
+ return val * 100;
+ }
+ dev_err(&pdev->dev, "%s: Invalid T_PwrOn scale: %u\n",
+ __func__, scale);
+ return 0;
+}
+
struct aspm_register_info {
u32 support:2;
u32 enabled:2;
@@ -392,6 +428,46 @@ static inline struct pci_dev *pci_function_0(struct pci_bus *linkbus)
return NULL;
}

+/* Calculate L1.2 PM substate timing parameters */
+static void aspm_calc_l1ss_info(struct pcie_link_state *link,
+ struct aspm_register_info *upreg,
+ struct aspm_register_info *dwreg)
+{
+ u32 val1, val2, scale1, scale2;
+
+ link->l1ss.up_cap_ptr = upreg->l1ss_cap_ptr;
+ link->l1ss.dw_cap_ptr = dwreg->l1ss_cap_ptr;
+ link->l1ss.ctl1 = link->l1ss.ctl2 = 0;
+
+ if (!(link->aspm_support & ASPM_STATE_L1_2_MASK))
+ return;
+
+ /* Choose the greater of the two T_cmn_mode_rstr_time */
+ val1 = (upreg->l1ss_cap >> 8) & 0xFF;
+ val2 = (upreg->l1ss_cap >> 8) & 0xFF;
+ if (val1 > val2)
+ link->l1ss.ctl1 |= val1 << 8;
+ else
+ link->l1ss.ctl1 |= val2 << 8;
+ /*
+ * We currently use LTR L1.2 threshold to be fixed constant picked from
+ * Intel's coreboot.
+ */
+ link->l1ss.ctl1 |= LTR_L1_2_THRESHOLD_BITS;
+
+ /* Choose the greater of the two T_pwr_on */
+ val1 = (upreg->l1ss_cap >> 19) & 0x1F;
+ scale1 = (upreg->l1ss_cap >> 16) & 0x03;
+ val2 = (dwreg->l1ss_cap >> 19) & 0x1F;
+ scale2 = (dwreg->l1ss_cap >> 16) & 0x03;
+
+ if (calc_l1ss_pwron(link->pdev, scale1, val1) >
+ calc_l1ss_pwron(link->downstream, scale2, val2))
+ link->l1ss.ctl2 |= scale1 | (val1 << 3);
+ else
+ link->l1ss.ctl2 |= scale2 | (val2 << 3);
+}
+
static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist)
{
struct pci_dev *child, *parent = link->pdev;
@@ -471,6 +547,9 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist)
if (upreg.l1ss_ctl1 & dwreg.l1ss_ctl1 & PCI_L1SS_CTL1_PCIPM_L1_2)
link->aspm_enabled |= ASPM_STATE_L1_2_PCIPM;

+ if (link->aspm_support & ASPM_STATE_L1SS)
+ aspm_calc_l1ss_info(link, &upreg, &dwreg);
+
/* Save default state */
link->aspm_default = link->aspm_enabled;

--
2.8.0.rc3.226.g39d4020

Rajat Jain

unread,
Jan 3, 2017, 1:40:05 AM1/3/17
to
Since the exit latencies for L1 substates are not advertised by
a device, it is not clear in spec how to do a L1 substate exit
latency check. We assume that the L1 exit latencies advertised
by a device include L1 substate latencies (and hence do not do any
check). If that is not true, we should do some sort of check here.

(I'm not clear about what that check should like currenlty. I'd be
glad to take up any suggestions).

Signed-off-by: Rajat Jain <raj...@google.com>
---
drivers/pci/pcie/aspm.c | 8 ++++++++
1 file changed, 8 insertions(+)

diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c
index 6735f38..cb5602c 100644
--- a/drivers/pci/pcie/aspm.c
+++ b/drivers/pci/pcie/aspm.c
@@ -403,6 +403,14 @@ static void pcie_aspm_check_latency(struct pci_dev *endpoint)
* Check L1 latency.
* Every switch on the path to root complex need 1
* more microsecond for L1. Spec doesn't mention L0s.
+ *
+ * The exit latencies for L1 substates are not advertised
+ * by a device. Since the spec also doesn't mention a way
+ * to determine max latencies introduced by enabling L1
+ * substates on the components, it is not clear how to do
+ * a L1 substate exit latency check. We assume that the
+ * L1 exit latencies advertised by a device include L1
+ * substate latencies (and hence do not do any check)
*/
latency = max_t(u32, link->latency_up.l1, link->latency_dw.l1);
if ((link->aspm_capable & ASPM_STATE_L1) &&
--
2.8.0.rc3.226.g39d4020

Rajat Jain

unread,
Jan 3, 2017, 1:40:05 AM1/3/17
to
This patchset adds the PCIe L1 PM substate support to the kernel.
The feature is described at:
https://pcisig.com/sites/default/files/specification_documents/ECN_L1_PM_Substates_with_CLKREQ_31_May_2013_Rev10a.pdf

Its all logically one patch (and may be some of them should be
squashed later) , but I've broken down into smaller patches for
ease of review.

This is currently rebased on top of Bjorn's master branch.

Rajat Jain (6):
PCI: Add L1 substate capability structure register definitions
PCI/ASPM: Introduce L1 substates and a Kconfig for it
PCI/ASPM: Read and setup L1 substate capabilities
PCI/ASPM: Calculate and save the L1.2 timing parameters
PCI/ASPM: Actually configure the L1 substate settings to the device
PCI/ASPM: Add comment about L1 substate latency

drivers/pci/pcie/Kconfig | 8 ++
drivers/pci/pcie/aspm.c | 291 ++++++++++++++++++++++++++++++++++++++++--
include/uapi/linux/pci_regs.h | 16 +++
3 files changed, 302 insertions(+), 13 deletions(-)

--
2.8.0.rc3.226.g39d4020

Rajat Jain

unread,
Jan 3, 2017, 1:40:11 AM1/3/17
to
These definitions will be used in subsequent patches.

Signed-off-by: Rajat Jain <raj...@google.com>
---
include/uapi/linux/pci_regs.h | 16 ++++++++++++++++
1 file changed, 16 insertions(+)

diff --git a/include/uapi/linux/pci_regs.h b/include/uapi/linux/pci_regs.h
index 174d114..f48d06e 100644
--- a/include/uapi/linux/pci_regs.h
+++ b/include/uapi/linux/pci_regs.h
@@ -682,6 +682,7 @@
#define PCI_EXT_CAP_ID_PMUX 0x1A /* Protocol Multiplexing */
#define PCI_EXT_CAP_ID_PASID 0x1B /* Process Address Space ID */
#define PCI_EXT_CAP_ID_DPC 0x1D /* Downstream Port Containment */
+#define PCI_EXT_CAP_ID_L1SS 0x1E /* L1 PM Substates */
#define PCI_EXT_CAP_ID_PTM 0x1F /* Precision Time Measurement */
#define PCI_EXT_CAP_ID_MAX PCI_EXT_CAP_ID_PTM

@@ -985,4 +986,19 @@
#define PCI_PTM_CTRL_ENABLE 0x00000001 /* PTM enable */
#define PCI_PTM_CTRL_ROOT 0x00000002 /* Root select */

+/* L1 PM Substates */
+#define PCI_L1SS_CAP 4 /* capability register */
+#define PCI_L1SS_CAP_PCIPM_L1_2 1 /* PCI PM L1.2 Support */
+#define PCI_L1SS_CAP_PCIPM_L1_1 2 /* PCI PM L1.1 Support */
+#define PCI_L1SS_CAP_ASPM_L1_2 4 /* ASPM L1.2 Support */
+#define PCI_L1SS_CAP_ASPM_L1_1 8 /* ASPM L1.1 Support */
+#define PCI_L1SS_CAP_L1_PM_SS 16 /* L1 PM Substates Support */
+#define PCI_L1SS_CTL1 8 /* Control Register 1 */
+#define PCI_L1SS_CTL1_PCIPM_L1_2 1 /* PCI PM L1.2 Enable */
+#define PCI_L1SS_CTL1_PCIPM_L1_1 2 /* PCI PM L1.1 Support */
+#define PCI_L1SS_CTL1_ASPM_L1_2 4 /* ASPM L1.2 Support */
+#define PCI_L1SS_CTL1_ASPM_L1_1 8 /* ASPM L1.1 Support */
+#define PCI_L1SS_CTL1_L1SS_MASK 0x0000000F
+#define PCI_L1SS_CTL2 0xC /* Control Register 2 */
+
#endif /* LINUX_PCI_REGS_H */
--
2.8.0.rc3.226.g39d4020

Bjorn Helgaas

unread,
Feb 14, 2017, 6:50:08 PM2/14/17
to
Applied to pci/aspm for v4.11, thanks, Rajat!
0 new messages