summaryrefslogtreecommitdiffstats
path: root/drivers/pci/pcie/aspm.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pci/pcie/aspm.c')
-rw-r--r--drivers/pci/pcie/aspm.c71
1 files changed, 47 insertions, 24 deletions
diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c
index 9783e10da3a9..3b9b4d50cd98 100644
--- a/drivers/pci/pcie/aspm.c
+++ b/drivers/pci/pcie/aspm.c
@@ -43,18 +43,6 @@
#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 maybe 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) */
@@ -333,6 +321,32 @@ 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)
+{
+ u64 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;
@@ -443,6 +457,7 @@ static void aspm_calc_l1ss_info(struct pcie_link_state *link,
struct aspm_register_info *dwreg)
{
u32 val1, val2, scale1, scale2;
+ u32 t_common_mode, t_power_on, l1_2_threshold, scale, value;
link->l1ss.up_cap_ptr = upreg->l1ss_cap_ptr;
link->l1ss.dw_cap_ptr = dwreg->l1ss_cap_ptr;
@@ -454,16 +469,7 @@ static void aspm_calc_l1ss_info(struct pcie_link_state *link,
/* Choose the greater of the two Port Common_Mode_Restore_Times */
val1 = (upreg->l1ss_cap & PCI_L1SS_CAP_CM_RESTORE_TIME) >> 8;
val2 = (dwreg->l1ss_cap & PCI_L1SS_CAP_CM_RESTORE_TIME) >> 8;
- 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;
+ t_common_mode = max(val1, val2);
/* Choose the greater of the two Port T_POWER_ON times */
val1 = (upreg->l1ss_cap & PCI_L1SS_CAP_P_PWR_ON_VALUE) >> 19;
@@ -472,10 +478,27 @@ static void aspm_calc_l1ss_info(struct pcie_link_state *link,
scale2 = (dwreg->l1ss_cap & PCI_L1SS_CAP_P_PWR_ON_SCALE) >> 16;
if (calc_l1ss_pwron(link->pdev, scale1, val1) >
- calc_l1ss_pwron(link->downstream, scale2, val2))
+ calc_l1ss_pwron(link->downstream, scale2, val2)) {
link->l1ss.ctl2 |= scale1 | (val1 << 3);
- else
+ t_power_on = calc_l1ss_pwron(link->pdev, scale1, val1);
+ } else {
link->l1ss.ctl2 |= scale2 | (val2 << 3);
+ t_power_on = calc_l1ss_pwron(link->downstream, scale2, val2);
+ }
+
+ /*
+ * Set LTR_L1.2_THRESHOLD to the time required to transition the
+ * Link from L0 to L1.2 and back to L0 so we enter L1.2 only if
+ * downstream devices report (via LTR) that they can tolerate at
+ * least that much latency.
+ *
+ * Based on PCIe r3.1, sec 5.5.3.3.1, Figures 5-16 and 5-17, and
+ * Table 5-11. T(POWER_OFF) is at most 2us and T(L1.2) is at
+ * least 4us.
+ */
+ l1_2_threshold = 2 + 4 + t_common_mode + t_power_on;
+ encode_l12_threshold(l1_2_threshold, &scale, &value);
+ link->l1ss.ctl1 |= t_common_mode << 8 | scale << 29 | value << 16;
}
static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist)
OpenPOWER on IntegriCloud