From b2103ccbb67e3ef0f7a75d21c989f9614ddbcaca Mon Sep 17 00:00:00 2001 From: Rajat Jain Date: Mon, 2 Jan 2017 22:34:11 -0800 Subject: PCI/ASPM: Add support for L1 substates Add support for ASPM L1 substates. For details about L1 substates, see the PCIe r3.1 spec, which includes the ECN below in secs 5.5 and 7.33. Add macros for the 4 new L1 substates, and add a new ASPM "POWER_SUPERSAVE" policy that can be used to enable L1 substates on a system if desired. The new policy is in a sense, a superset of the existing POWERSAVE policy. The 4 policies are now: DEFAULT: Reads and uses whatever ASPM states BIOS enabled PERFORMANCE: Everything except L0 disabled. POWERSAVE: L0s and L1 enabled (but not L1 substates) POWER_SUPERSAVE: L0s + L1 + L1 substates also enabled [bhelgaas: add PCIe r3.1 spec reference] Link: https://pcisig.com/sites/default/files/specification_documents/ECN_L1_PM_Substates_with_CLKREQ_31_May_2013_Rev10a.pdf Signed-off-by: Rajat Jain Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/aspm.c | 39 +++++++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 10 deletions(-) (limited to 'drivers/pci/pcie/aspm.c') diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index 17ac1dce3286..a74fb3a8333f 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -30,8 +30,17 @@ #define ASPM_STATE_L0S_UP (1) /* Upstream direction L0s state */ #define ASPM_STATE_L0S_DW (2) /* Downstream direction L0s state */ #define ASPM_STATE_L1 (4) /* L1 state */ +#define ASPM_STATE_L1_1 (8) /* ASPM L1.1 state */ +#define ASPM_STATE_L1_2 (0x10) /* ASPM L1.2 state */ +#define ASPM_STATE_L1_1_PCIPM (0x20) /* PCI PM L1.1 state */ +#define ASPM_STATE_L1_2_PCIPM (0x40) /* PCI PM L1.2 state */ +#define ASPM_STATE_L1_SS_PCIPM (ASPM_STATE_L1_1_PCIPM | ASPM_STATE_L1_2_PCIPM) +#define ASPM_STATE_L1_2_MASK (ASPM_STATE_L1_2 | ASPM_STATE_L1_2_PCIPM) +#define ASPM_STATE_L1SS (ASPM_STATE_L1_1 | ASPM_STATE_L1_1_PCIPM |\ + ASPM_STATE_L1_2_MASK) #define ASPM_STATE_L0S (ASPM_STATE_L0S_UP | ASPM_STATE_L0S_DW) -#define ASPM_STATE_ALL (ASPM_STATE_L0S | ASPM_STATE_L1) +#define ASPM_STATE_ALL (ASPM_STATE_L0S | ASPM_STATE_L1 | \ + ASPM_STATE_L1SS) struct aspm_latency { u32 l0s; /* L0s latency (nsec) */ @@ -47,11 +56,11 @@ struct pcie_link_state { struct list_head link; /* node in parent's children list */ /* ASPM state */ - u32 aspm_support:3; /* Supported ASPM state */ - u32 aspm_enabled:3; /* Enabled ASPM state */ - u32 aspm_capable:3; /* Capable ASPM state with latency */ - u32 aspm_default:3; /* Default ASPM state by BIOS */ - u32 aspm_disable:3; /* Disabled ASPM state */ + u32 aspm_support:7; /* Supported ASPM state */ + u32 aspm_enabled:7; /* Enabled ASPM state */ + u32 aspm_capable:7; /* Capable ASPM state with latency */ + u32 aspm_default:7; /* Default ASPM state by BIOS */ + u32 aspm_disable:7; /* Disabled ASPM state */ /* Clock PM state */ u32 clkpm_capable:1; /* Clock PM capable? */ @@ -76,11 +85,14 @@ static LIST_HEAD(link_list); #define POLICY_DEFAULT 0 /* BIOS default setting */ #define POLICY_PERFORMANCE 1 /* high performance */ #define POLICY_POWERSAVE 2 /* high power saving */ +#define POLICY_POWER_SUPERSAVE 3 /* possibly even more power saving */ #ifdef CONFIG_PCIEASPM_PERFORMANCE static int aspm_policy = POLICY_PERFORMANCE; #elif defined CONFIG_PCIEASPM_POWERSAVE static int aspm_policy = POLICY_POWERSAVE; +#elif defined CONFIG_PCIEASPM_POWER_SUPERSAVE +static int aspm_policy = POLICY_POWER_SUPERSAVE; #else static int aspm_policy; #endif @@ -88,7 +100,8 @@ static int aspm_policy; static const char *policy_str[] = { [POLICY_DEFAULT] = "default", [POLICY_PERFORMANCE] = "performance", - [POLICY_POWERSAVE] = "powersave" + [POLICY_POWERSAVE] = "powersave", + [POLICY_POWER_SUPERSAVE] = "powersupersave" }; #define LINK_RETRAIN_TIMEOUT HZ @@ -101,6 +114,9 @@ static int policy_to_aspm_state(struct pcie_link_state *link) return 0; case POLICY_POWERSAVE: /* Enable ASPM L0s/L1 */ + return (ASPM_STATE_L0S | ASPM_STATE_L1); + case POLICY_POWER_SUPERSAVE: + /* Enable Everything */ return ASPM_STATE_ALL; case POLICY_DEFAULT: return link->aspm_default; @@ -115,7 +131,8 @@ static int policy_to_clkpm_state(struct pcie_link_state *link) /* Disable ASPM and Clock PM */ return 0; case POLICY_POWERSAVE: - /* Disable Clock PM */ + case POLICY_POWER_SUPERSAVE: + /* Enable Clock PM */ return 1; case POLICY_DEFAULT: return link->clkpm_default; @@ -612,7 +629,8 @@ void pcie_aspm_init_link_state(struct pci_dev *pdev) * the BIOS's expectation, we'll do so once pci_enable_device() is * called. */ - if (aspm_policy != POLICY_POWERSAVE) { + if (aspm_policy != POLICY_POWERSAVE && + aspm_policy != POLICY_POWER_SUPERSAVE) { pcie_config_aspm_path(link); pcie_set_clkpm(link, policy_to_clkpm_state(link)); } @@ -712,7 +730,8 @@ void pcie_aspm_powersave_config_link(struct pci_dev *pdev) if (aspm_disabled || !link) return; - if (aspm_policy != POLICY_POWERSAVE) + if (aspm_policy != POLICY_POWERSAVE && + aspm_policy != POLICY_POWER_SUPERSAVE) return; down_read(&pci_bus_sem); -- cgit From b5a0a9b59c8185aebcd9a717e2e6258b58c72c06 Mon Sep 17 00:00:00 2001 From: Rajat Jain Date: Mon, 2 Jan 2017 22:34:12 -0800 Subject: PCI/ASPM: Read and set up L1 substate capabilities The PCIe spec (r3.1, sec 7.33) says the L1 PM Substates Capability may be implemented only in function 0. Read the L1 substate capability structures of upstream and downstream components of the link and set it up in the device structure. [bhelgaas: add specific spec reference] Signed-off-by: Rajat Jain Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/aspm.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) (limited to 'drivers/pci/pcie/aspm.c') diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index a74fb3a8333f..1cd53203abbd 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -49,6 +49,7 @@ struct aspm_latency { struct pcie_link_state { struct pci_dev *pdev; /* Upstream component of the Link */ + struct pci_dev *downstream; /* Downstream component, function 0 */ struct pcie_link_state *root; /* pointer to the root port link */ struct pcie_link_state *parent; /* pointer to the parent Link state */ struct list_head sibling; /* node in link_list */ @@ -300,6 +301,12 @@ struct aspm_register_info { u32 enabled:2; u32 latency_encoding_l0s; u32 latency_encoding_l1; + + /* L1 substates */ + u32 l1ss_cap_ptr; + u32 l1ss_cap; + u32 l1ss_ctl1; + u32 l1ss_ctl2; }; static void pcie_get_aspm_reg(struct pci_dev *pdev, @@ -314,6 +321,22 @@ static void pcie_get_aspm_reg(struct pci_dev *pdev, info->latency_encoding_l1 = (reg32 & PCI_EXP_LNKCAP_L1EL) >> 15; pcie_capability_read_word(pdev, PCI_EXP_LNKCTL, ®16); info->enabled = reg16 & PCI_EXP_LNKCTL_ASPMC; + + /* Read L1 PM substate capabilities */ + info->l1ss_cap = info->l1ss_ctl1 = info->l1ss_ctl2 = 0; + info->l1ss_cap_ptr = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_L1SS); + if (!info->l1ss_cap_ptr) + return; + pci_read_config_dword(pdev, info->l1ss_cap_ptr + PCI_L1SS_CAP, + &info->l1ss_cap); + if (!(info->l1ss_cap & PCI_L1SS_CAP_L1_PM_SS)) { + info->l1ss_cap = 0; + return; + } + pci_read_config_dword(pdev, info->l1ss_cap_ptr + PCI_L1SS_CTL1, + &info->l1ss_ctl1); + pci_read_config_dword(pdev, info->l1ss_cap_ptr + PCI_L1SS_CTL2, + &info->l1ss_ctl2); } static void pcie_aspm_check_latency(struct pci_dev *endpoint) @@ -355,6 +378,20 @@ static void pcie_aspm_check_latency(struct pci_dev *endpoint) } } +/* + * The L1 PM substate capability is only implemented in function 0 in a + * multi function device. + */ +static struct pci_dev *pci_function_0(struct pci_bus *linkbus) +{ + struct pci_dev *child; + + list_for_each_entry(child, &linkbus->devices, bus_list) + if (PCI_FUNC(child->devfn) == 0) + return child; + return NULL; +} + static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist) { struct pci_dev *child, *parent = link->pdev; @@ -370,8 +407,9 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist) /* Get upstream/downstream components' register state */ pcie_get_aspm_reg(parent, &upreg); - child = list_entry(linkbus->devices.next, struct pci_dev, bus_list); + child = pci_function_0(linkbus); pcie_get_aspm_reg(child, &dwreg); + link->downstream = child; /* * If ASPM not supported, don't mess with the clocks and link, @@ -414,6 +452,25 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist) link->latency_up.l1 = calc_l1_latency(upreg.latency_encoding_l1); link->latency_dw.l1 = calc_l1_latency(dwreg.latency_encoding_l1); + /* Setup L1 substate */ + if (upreg.l1ss_cap & dwreg.l1ss_cap & PCI_L1SS_CAP_ASPM_L1_1) + link->aspm_support |= ASPM_STATE_L1_1; + if (upreg.l1ss_cap & dwreg.l1ss_cap & PCI_L1SS_CAP_ASPM_L1_2) + link->aspm_support |= ASPM_STATE_L1_2; + if (upreg.l1ss_cap & dwreg.l1ss_cap & PCI_L1SS_CAP_PCIPM_L1_1) + link->aspm_support |= ASPM_STATE_L1_1_PCIPM; + if (upreg.l1ss_cap & dwreg.l1ss_cap & PCI_L1SS_CAP_PCIPM_L1_2) + link->aspm_support |= ASPM_STATE_L1_2_PCIPM; + + if (upreg.l1ss_ctl1 & dwreg.l1ss_ctl1 & PCI_L1SS_CTL1_ASPM_L1_1) + link->aspm_enabled |= ASPM_STATE_L1_1; + if (upreg.l1ss_ctl1 & dwreg.l1ss_ctl1 & PCI_L1SS_CTL1_ASPM_L1_2) + link->aspm_enabled |= ASPM_STATE_L1_2; + if (upreg.l1ss_ctl1 & dwreg.l1ss_ctl1 & PCI_L1SS_CTL1_PCIPM_L1_1) + link->aspm_enabled |= ASPM_STATE_L1_1_PCIPM; + if (upreg.l1ss_ctl1 & dwreg.l1ss_ctl1 & PCI_L1SS_CTL1_PCIPM_L1_2) + link->aspm_enabled |= ASPM_STATE_L1_2_PCIPM; + /* Save default state */ link->aspm_default = link->aspm_enabled; -- cgit From f1f0366dd6be9624f7d355b72cc909ab821eb4c0 Mon Sep 17 00:00:00 2001 From: Rajat Jain Date: Mon, 2 Jan 2017 22:34:13 -0800 Subject: 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 Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/aspm.c | 79 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) (limited to 'drivers/pci/pcie/aspm.c') 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; -- cgit From aeda9adebab8b5bdd90576e3065a1f3f948279ad Mon Sep 17 00:00:00 2001 From: Rajat Jain Date: Mon, 2 Jan 2017 22:34:14 -0800 Subject: PCI/ASPM: Configure L1 substate settings Configure the L1 substate settings on the upstream and downstream devices, while taking care of the rules dictated by the PCIe spec. [bhelgaas: drop "inline"] Signed-off-by: Rajat Jain Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/aspm.c | 106 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 104 insertions(+), 2 deletions(-) (limited to 'drivers/pci/pcie/aspm.c') diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index b3451cb0472b..ceb2395c37ae 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -588,6 +588,92 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist) } } +static void pci_clear_and_set_dword(struct pci_dev *pdev, int pos, + u32 clear, u32 set) +{ + u32 val; + + pci_read_config_dword(pdev, pos, &val); + val &= ~clear; + val |= set; + pci_write_config_dword(pdev, pos, val); +} + +/* Configure the ASPM L1 substates */ +static void pcie_config_aspm_l1ss(struct pcie_link_state *link, u32 state) +{ + u32 val, enable_req; + struct pci_dev *child = link->downstream, *parent = link->pdev; + u32 up_cap_ptr = link->l1ss.up_cap_ptr; + u32 dw_cap_ptr = link->l1ss.dw_cap_ptr; + + enable_req = (link->aspm_enabled ^ state) & state; + + /* + * Here are the rules specified in the PCIe spec for enabling L1SS: + * - When enabling L1.x, enable bit at parent first, then at child + * - When disabling L1.x, disable bit at child first, then at parent + * - When enabling ASPM L1.x, need to disable L1 + * (at child followed by parent). + * - The ASPM/PCIPM L1.2 must be disabled while programming timing + * parameters + * + * To keep it simple, disable all L1SS bits first, and later enable + * what is needed. + */ + + /* Disable all L1 substates */ + pci_clear_and_set_dword(child, dw_cap_ptr + PCI_L1SS_CTL1, + PCI_L1SS_CTL1_L1SS_MASK, 0); + pci_clear_and_set_dword(parent, up_cap_ptr + PCI_L1SS_CTL1, + PCI_L1SS_CTL1_L1SS_MASK, 0); + /* + * If needed, disable L1, and it gets enabled later + * in pcie_config_aspm_link(). + */ + if (enable_req & (ASPM_STATE_L1_1 | ASPM_STATE_L1_2)) { + pcie_capability_clear_and_set_word(child, PCI_EXP_LNKCTL, + PCI_EXP_LNKCTL_ASPM_L1, 0); + pcie_capability_clear_and_set_word(parent, PCI_EXP_LNKCTL, + PCI_EXP_LNKCTL_ASPM_L1, 0); + } + + if (enable_req & ASPM_STATE_L1_2_MASK) { + + /* Program T_pwr_on in both ports */ + pci_write_config_dword(parent, up_cap_ptr + PCI_L1SS_CTL2, + link->l1ss.ctl2); + pci_write_config_dword(child, dw_cap_ptr + PCI_L1SS_CTL2, + link->l1ss.ctl2); + + /* Program T_cmn_mode in parent */ + pci_clear_and_set_dword(parent, up_cap_ptr + PCI_L1SS_CTL1, + 0xFF00, link->l1ss.ctl1); + + /* Program LTR L1.2 threshold in both ports */ + pci_clear_and_set_dword(parent, dw_cap_ptr + PCI_L1SS_CTL1, + 0xE3FF0000, link->l1ss.ctl1); + pci_clear_and_set_dword(child, dw_cap_ptr + PCI_L1SS_CTL1, + 0xE3FF0000, link->l1ss.ctl1); + } + + val = 0; + if (state & ASPM_STATE_L1_1) + val |= PCI_L1SS_CTL1_ASPM_L1_1; + if (state & ASPM_STATE_L1_2) + val |= PCI_L1SS_CTL1_ASPM_L1_2; + if (state & ASPM_STATE_L1_1_PCIPM) + val |= PCI_L1SS_CTL1_PCIPM_L1_1; + if (state & ASPM_STATE_L1_2_PCIPM) + val |= PCI_L1SS_CTL1_PCIPM_L1_2; + + /* Enable what we need to enable */ + pci_clear_and_set_dword(parent, up_cap_ptr + PCI_L1SS_CTL1, + PCI_L1SS_CAP_L1_PM_SS, val); + pci_clear_and_set_dword(child, dw_cap_ptr + PCI_L1SS_CTL1, + PCI_L1SS_CAP_L1_PM_SS, val); +} + static void pcie_config_aspm_dev(struct pci_dev *pdev, u32 val) { pcie_capability_clear_and_set_word(pdev, PCI_EXP_LNKCTL, @@ -597,11 +683,23 @@ static void pcie_config_aspm_dev(struct pci_dev *pdev, u32 val) static void pcie_config_aspm_link(struct pcie_link_state *link, u32 state) { u32 upstream = 0, dwstream = 0; - struct pci_dev *child, *parent = link->pdev; + struct pci_dev *child = link->downstream, *parent = link->pdev; struct pci_bus *linkbus = parent->subordinate; - /* Nothing to do if the link is already in the requested state */ + /* Enable only the states that were not explicitly disabled */ state &= (link->aspm_capable & ~link->aspm_disable); + + /* Can't enable any substates if L1 is not enabled */ + if (!(state & ASPM_STATE_L1)) + state &= ~ASPM_STATE_L1SS; + + /* Spec says both ports must be in D0 before enabling PCI PM substates*/ + if (parent->current_state != PCI_D0 || child->current_state != PCI_D0) { + state &= ~ASPM_STATE_L1_SS_PCIPM; + state |= (link->aspm_enabled & ASPM_STATE_L1_SS_PCIPM); + } + + /* Nothing to do if the link is already in the requested state */ if (link->aspm_enabled == state) return; /* Convert ASPM state to upstream/downstream ASPM register state */ @@ -613,6 +711,10 @@ static void pcie_config_aspm_link(struct pcie_link_state *link, u32 state) upstream |= PCI_EXP_LNKCTL_ASPM_L1; dwstream |= PCI_EXP_LNKCTL_ASPM_L1; } + + if (link->aspm_capable & ASPM_STATE_L1SS) + pcie_config_aspm_l1ss(link, state); + /* * Spec 2.0 suggests all functions should be configured the * same setting for ASPM. Enabling ASPM L1 should be done in -- cgit From a142f4d3e5c54db5e942fa6ee5f3dc0e8c83207b Mon Sep 17 00:00:00 2001 From: Rajat Jain Date: Mon, 2 Jan 2017 22:34:15 -0800 Subject: PCI/ASPM: Add comment about L1 substate latency 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 currently. I'd be glad to take up any suggestions). Signed-off-by: Rajat Jain Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/aspm.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers/pci/pcie/aspm.c') diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index ceb2395c37ae..a9bcd56e41ed 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) && -- cgit