summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBjorn Helgaas <bhelgaas@google.com>2018-01-31 10:10:26 -0600
committerBjorn Helgaas <helgaas@kernel.org>2018-01-31 10:10:26 -0600
commit3ea8bc3326e1e88c0dc0b9bc8697d66eb69bf9a4 (patch)
treeca4337557d8ce1e849df3755d05d76f87e7b1f9e
parent86e99150c3ce4726d4f15c64f535b903a66fa76f (diff)
parent7d8e7d19b095ae70b1ca483ca36e7985a108abe5 (diff)
Merge branch 'pci/aspm' into next
* pci/aspm: PCI/ASPM: Unexport internal ASPM interfaces PCI/ASPM: Enable Latency Tolerance Reporting when supported PCI/ASPM: Calculate LTR_L1.2_THRESHOLD from device characteristics
-rw-r--r--drivers/pci/pci.h20
-rw-r--r--drivers/pci/pcie/aspm.c71
-rw-r--r--drivers/pci/probe.c33
-rw-r--r--include/linux/pci-aspm.h35
-rw-r--r--include/linux/pci.h2
5 files changed, 104 insertions, 57 deletions
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index f6b58b32a67c..e90009fff1a9 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -342,6 +342,26 @@ static inline resource_size_t pci_resource_alignment(struct pci_dev *dev,
void pci_enable_acs(struct pci_dev *dev);
+#ifdef CONFIG_PCIEASPM
+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 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) { }
+static inline void pcie_aspm_powersave_config_link(struct pci_dev *pdev) { }
+#endif
+
+#ifdef CONFIG_PCIEASPM_DEBUG
+void pcie_aspm_create_sysfs_dev_files(struct pci_dev *pdev);
+void pcie_aspm_remove_sysfs_dev_files(struct pci_dev *pdev);
+#else
+static inline void pcie_aspm_create_sysfs_dev_files(struct pci_dev *pdev) { }
+static inline void pcie_aspm_remove_sysfs_dev_files(struct pci_dev *pdev) { }
+#endif
+
#ifdef CONFIG_PCIE_PTM
void pci_ptm_init(struct pci_dev *dev);
#else
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)
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index 14e0ea1ff38b..3761b1303529 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -1875,6 +1875,38 @@ static void pci_configure_relaxed_ordering(struct pci_dev *dev)
}
}
+static void pci_configure_ltr(struct pci_dev *dev)
+{
+#ifdef CONFIG_PCIEASPM
+ u32 cap;
+ struct pci_dev *bridge;
+
+ if (!pci_is_pcie(dev))
+ return;
+
+ pcie_capability_read_dword(dev, PCI_EXP_DEVCAP2, &cap);
+ if (!(cap & PCI_EXP_DEVCAP2_LTR))
+ return;
+
+ /*
+ * Software must not enable LTR in an Endpoint unless the Root
+ * Complex and all intermediate Switches indicate support for LTR.
+ * PCIe r3.1, sec 6.18.
+ */
+ if (pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT)
+ dev->ltr_path = 1;
+ else {
+ bridge = pci_upstream_bridge(dev);
+ if (bridge && bridge->ltr_path)
+ dev->ltr_path = 1;
+ }
+
+ if (dev->ltr_path)
+ pcie_capability_set_word(dev, PCI_EXP_DEVCTL2,
+ PCI_EXP_DEVCTL2_LTR_EN);
+#endif
+}
+
static void pci_configure_device(struct pci_dev *dev)
{
struct hotplug_params hpp;
@@ -1883,6 +1915,7 @@ static void pci_configure_device(struct pci_dev *dev)
pci_configure_mps(dev);
pci_configure_extended_tags(dev, NULL);
pci_configure_relaxed_ordering(dev);
+ pci_configure_ltr(dev);
memset(&hpp, 0, sizeof(hpp));
ret = pci_get_hp_params(dev, &hpp);
diff --git a/include/linux/pci-aspm.h b/include/linux/pci-aspm.h
index 3cc06b059017..df28af5cef21 100644
--- a/include/linux/pci-aspm.h
+++ b/include/linux/pci-aspm.h
@@ -24,43 +24,12 @@
#define PCIE_LINK_STATE_CLKPM 4
#ifdef CONFIG_PCIEASPM
-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);
void pci_disable_link_state(struct pci_dev *pdev, int state);
void pci_disable_link_state_locked(struct pci_dev *pdev, int state);
void pcie_no_aspm(void);
#else
-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)
-{
-}
-static inline void pcie_aspm_powersave_config_link(struct pci_dev *pdev)
-{
-}
-static inline void pci_disable_link_state(struct pci_dev *pdev, int state)
-{
-}
-static inline void pcie_no_aspm(void)
-{
-}
+static inline void pci_disable_link_state(struct pci_dev *pdev, int state) { }
+static inline void pcie_no_aspm(void) { }
#endif
-#ifdef CONFIG_PCIEASPM_DEBUG /* this depends on CONFIG_PCIEASPM */
-void pcie_aspm_create_sysfs_dev_files(struct pci_dev *pdev);
-void pcie_aspm_remove_sysfs_dev_files(struct pci_dev *pdev);
-#else
-static inline void pcie_aspm_create_sysfs_dev_files(struct pci_dev *pdev)
-{
-}
-static inline void pcie_aspm_remove_sysfs_dev_files(struct pci_dev *pdev)
-{
-}
-#endif
#endif /* LINUX_ASPM_H */
diff --git a/include/linux/pci.h b/include/linux/pci.h
index c170c9250c8b..d75d30e55976 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -350,6 +350,8 @@ struct pci_dev {
#ifdef CONFIG_PCIEASPM
struct pcie_link_state *link_state; /* ASPM link state */
+ unsigned int ltr_path:1; /* Latency Tolerance Reporting
+ supported from root to here */
#endif
pci_channel_state_t error_state; /* current connectivity state */