diff options
author | Rajat Jain <rajatja@google.com> | 2017-01-02 22:34:13 -0800 |
---|---|---|
committer | Bjorn Helgaas <bhelgaas@google.com> | 2017-02-14 17:44:25 -0600 |
commit | f1f0366dd6be9624f7d355b72cc909ab821eb4c0 (patch) | |
tree | 272dd86f5011286fc17097b6279f6c4918f4d7c9 | |
parent | b5a0a9b59c8185aebcd9a717e2e6258b58c72c06 (diff) |
PCI/ASPM: Calculate and save the L1.2 timing parameters
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 <rajatja@google.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
-rw-r--r-- | drivers/pci/pcie/aspm.c | 79 |
1 files changed, 79 insertions, 0 deletions
diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index 1cd53203abbd..b3451cb0472b 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 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) */ @@ -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 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; |