From 1a11074be2e94736d12e49a9a562b0a54e8a423d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= Date: Fri, 15 Sep 2023 18:57:46 +0300 Subject: [PATCH 1/9] PCI: Add PCI_L1SS_CTL2 fields MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add L1 PM Substates Control 2 Register fields (PCI_L1SS_CTL2_*). Link: https://lore.kernel.org/r/20230915155752.84640-2-ilpo.jarvinen@linux.intel.com Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas --- include/uapi/linux/pci_regs.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/uapi/linux/pci_regs.h b/include/uapi/linux/pci_regs.h index e5f558d96493..3a7ea9581568 100644 --- a/include/uapi/linux/pci_regs.h +++ b/include/uapi/linux/pci_regs.h @@ -1088,6 +1088,8 @@ #define PCI_L1SS_CTL1_LTR_L12_TH_VALUE 0x03ff0000 /* LTR_L1.2_THRESHOLD_Value */ #define PCI_L1SS_CTL1_LTR_L12_TH_SCALE 0xe0000000 /* LTR_L1.2_THRESHOLD_Scale */ #define PCI_L1SS_CTL2 0x0c /* Control 2 Register */ +#define PCI_L1SS_CTL2_T_PWR_ON_SCALE 0x00000003 /* T_POWER_ON Scale */ +#define PCI_L1SS_CTL2_T_PWR_ON_VALUE 0x000000f8 /* T_POWER_ON Value */ /* Designated Vendor-Specific (DVSEC, PCI_EXT_CAP_ID_DVSEC) */ #define PCI_DVSEC_HEADER1 0x4 /* Designated Vendor-Specific Header1 */ From 52d92516cb2e04a9b1b8837079a561ca672c1d5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= Date: Fri, 15 Sep 2023 18:57:47 +0300 Subject: [PATCH 2/9] PCI/ASPM: Use FIELD_GET/PREP() to access PCIe capability fields MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace open-coded variants to access PCIe capability registers fields with FIELD_GET/PREP(). Link: https://lore.kernel.org/r/20230915155752.84640-3-ilpo.jarvinen@linux.intel.com Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/aspm.c | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index 1bf630059264..06f175d8dee5 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -7,6 +7,7 @@ * Copyright (C) Shaohua Li (shaohua.li@intel.com) */ +#include #include #include #include @@ -267,7 +268,7 @@ static void pcie_aspm_configure_common_clock(struct pcie_link_state *link) /* Convert L0s latency encoding to ns */ static u32 calc_l0s_latency(u32 lnkcap) { - u32 encoding = (lnkcap & PCI_EXP_LNKCAP_L0SEL) >> 12; + u32 encoding = FIELD_GET(PCI_EXP_LNKCAP_L0SEL, lnkcap); if (encoding == 0x7) return (5 * 1000); /* > 4us */ @@ -285,7 +286,7 @@ static u32 calc_l0s_acceptable(u32 encoding) /* Convert L1 latency encoding to ns */ static u32 calc_l1_latency(u32 lnkcap) { - u32 encoding = (lnkcap & PCI_EXP_LNKCAP_L1EL) >> 15; + u32 encoding = FIELD_GET(PCI_EXP_LNKCAP_L1EL, lnkcap); if (encoding == 0x7) return (65 * 1000); /* > 64us */ @@ -371,11 +372,11 @@ static void pcie_aspm_check_latency(struct pci_dev *endpoint) link = endpoint->bus->self->link_state; /* Calculate endpoint L0s acceptable latency */ - encoding = (endpoint->devcap & PCI_EXP_DEVCAP_L0S) >> 6; + encoding = FIELD_GET(PCI_EXP_DEVCAP_L0S, endpoint->devcap); acceptable_l0s = calc_l0s_acceptable(encoding); /* Calculate endpoint L1 acceptable latency */ - encoding = (endpoint->devcap & PCI_EXP_DEVCAP_L1) >> 9; + encoding = FIELD_GET(PCI_EXP_DEVCAP_L1, endpoint->devcap); acceptable_l1 = calc_l1_acceptable(encoding); while (link) { @@ -446,22 +447,24 @@ static void aspm_calc_l12_info(struct pcie_link_state *link, u32 pl1_2_enables, cl1_2_enables; /* Choose the greater of the two Port Common_Mode_Restore_Times */ - val1 = (parent_l1ss_cap & PCI_L1SS_CAP_CM_RESTORE_TIME) >> 8; - val2 = (child_l1ss_cap & PCI_L1SS_CAP_CM_RESTORE_TIME) >> 8; + val1 = FIELD_GET(PCI_L1SS_CAP_CM_RESTORE_TIME, parent_l1ss_cap); + val2 = FIELD_GET(PCI_L1SS_CAP_CM_RESTORE_TIME, child_l1ss_cap); t_common_mode = max(val1, val2); /* Choose the greater of the two Port T_POWER_ON times */ - val1 = (parent_l1ss_cap & PCI_L1SS_CAP_P_PWR_ON_VALUE) >> 19; - scale1 = (parent_l1ss_cap & PCI_L1SS_CAP_P_PWR_ON_SCALE) >> 16; - val2 = (child_l1ss_cap & PCI_L1SS_CAP_P_PWR_ON_VALUE) >> 19; - scale2 = (child_l1ss_cap & PCI_L1SS_CAP_P_PWR_ON_SCALE) >> 16; + val1 = FIELD_GET(PCI_L1SS_CAP_P_PWR_ON_VALUE, parent_l1ss_cap); + scale1 = FIELD_GET(PCI_L1SS_CAP_P_PWR_ON_SCALE, parent_l1ss_cap); + val2 = FIELD_GET(PCI_L1SS_CAP_P_PWR_ON_VALUE, child_l1ss_cap); + scale2 = FIELD_GET(PCI_L1SS_CAP_P_PWR_ON_SCALE, child_l1ss_cap); if (calc_l12_pwron(parent, scale1, val1) > calc_l12_pwron(child, scale2, val2)) { - ctl2 |= scale1 | (val1 << 3); + ctl2 |= FIELD_PREP(PCI_L1SS_CTL2_T_PWR_ON_SCALE, scale1) | + FIELD_PREP(PCI_L1SS_CTL2_T_PWR_ON_VALUE, val1); t_power_on = calc_l12_pwron(parent, scale1, val1); } else { - ctl2 |= scale2 | (val2 << 3); + ctl2 |= FIELD_PREP(PCI_L1SS_CTL2_T_PWR_ON_SCALE, scale2) | + FIELD_PREP(PCI_L1SS_CTL2_T_PWR_ON_VALUE, val2); t_power_on = calc_l12_pwron(child, scale2, val2); } @@ -477,7 +480,9 @@ static void aspm_calc_l12_info(struct pcie_link_state *link, */ l1_2_threshold = 2 + 4 + t_common_mode + t_power_on; encode_l12_threshold(l1_2_threshold, &scale, &value); - ctl1 |= t_common_mode << 8 | scale << 29 | value << 16; + ctl1 |= FIELD_PREP(PCI_L1SS_CTL1_CM_RESTORE_TIME, t_common_mode) | + FIELD_PREP(PCI_L1SS_CTL1_LTR_L12_TH_VALUE, value) | + FIELD_PREP(PCI_L1SS_CTL1_LTR_L12_TH_SCALE, scale); /* Some broken devices only support dword access to L1 SS */ pci_read_config_dword(parent, parent->l1ss + PCI_L1SS_CTL1, &pctl1); From 69bb38b77486b114b56b1174d74bb97d66045697 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= Date: Fri, 15 Sep 2023 18:57:48 +0300 Subject: [PATCH 3/9] PCI/ASPM: Return U32_MAX instead of bit magic construct MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of returning a bit obscure -1U, make code's intent of returning the maximum representable value more obvious by returning U32_MAX. Link: https://lore.kernel.org/r/20230915155752.84640-4-ilpo.jarvinen@linux.intel.com Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/aspm.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index 06f175d8dee5..4cd11ab27233 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -279,7 +280,7 @@ static u32 calc_l0s_latency(u32 lnkcap) static u32 calc_l0s_acceptable(u32 encoding) { if (encoding == 0x7) - return -1U; + return U32_MAX; return (64 << encoding); } @@ -297,7 +298,7 @@ static u32 calc_l1_latency(u32 lnkcap) static u32 calc_l1_acceptable(u32 encoding) { if (encoding == 0x7) - return -1U; + return U32_MAX; return (1000 << encoding); } From e13b72b819245027f960ce7a3735c4fe24fab024 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= Date: Fri, 15 Sep 2023 18:57:49 +0300 Subject: [PATCH 4/9] PCI/ASPM: Use time constants MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use defined constants to convert between time units. Link: https://lore.kernel.org/r/20230915155752.84640-5-ilpo.jarvinen@linux.intel.com Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/aspm.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index 4cd11ab27233..60135fc7281a 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -21,6 +21,8 @@ #include #include #include +#include + #include "../pci.h" #ifdef MODULE_PARAM_PREFIX @@ -272,7 +274,7 @@ static u32 calc_l0s_latency(u32 lnkcap) u32 encoding = FIELD_GET(PCI_EXP_LNKCAP_L0SEL, lnkcap); if (encoding == 0x7) - return (5 * 1000); /* > 4us */ + return 5 * NSEC_PER_USEC; /* > 4us */ return (64 << encoding); } @@ -290,8 +292,8 @@ static u32 calc_l1_latency(u32 lnkcap) u32 encoding = FIELD_GET(PCI_EXP_LNKCAP_L1EL, lnkcap); if (encoding == 0x7) - return (65 * 1000); /* > 64us */ - return (1000 << encoding); + return 65 * NSEC_PER_USEC; /* > 64us */ + return NSEC_PER_USEC << encoding; } /* Convert L1 acceptable latency encoding to ns */ @@ -299,7 +301,7 @@ static u32 calc_l1_acceptable(u32 encoding) { if (encoding == 0x7) return U32_MAX; - return (1000 << encoding); + return NSEC_PER_USEC << encoding; } /* Convert L1SS T_pwr encoding to usec */ @@ -327,7 +329,7 @@ static u32 calc_l12_pwron(struct pci_dev *pdev, u32 scale, u32 val) */ static void encode_l12_threshold(u32 threshold_us, u32 *scale, u32 *value) { - u64 threshold_ns = (u64) threshold_us * 1000; + u64 threshold_ns = (u64)threshold_us * NSEC_PER_USEC; /* * LTR_L1.2_THRESHOLD_Value ("value") is a 10-bit field with max @@ -419,7 +421,7 @@ static void pcie_aspm_check_latency(struct pci_dev *endpoint) if ((link->aspm_capable & ASPM_STATE_L1) && (latency + l1_switch_latency > acceptable_l1)) link->aspm_capable &= ~ASPM_STATE_L1; - l1_switch_latency += 1000; + l1_switch_latency += NSEC_PER_USEC; link = link->parent; } From 4ea9c414422ddd2b4aafa5d5ed6f61e1e0e85422 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= Date: Fri, 15 Sep 2023 18:57:50 +0300 Subject: [PATCH 5/9] PCI/ASPM: Use FIELD_MAX() instead of literals MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert 0x3ff literals in encode_l12_threshold() to FIELD_MAX(PCI_L1SS_CTL1_LTR_L12_TH_VALUE) that explains the purpose of the literal. Link: https://lore.kernel.org/r/20230915155752.84640-6-ilpo.jarvinen@linux.intel.com Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/aspm.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index 60135fc7281a..fac6c5a0be26 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -335,27 +335,27 @@ static void encode_l12_threshold(u32 threshold_us, u32 *scale, u32 *value) * LTR_L1.2_THRESHOLD_Value ("value") is a 10-bit field with max * value of 0x3ff. */ - if (threshold_ns <= 0x3ff * 1) { + if (threshold_ns <= 1 * FIELD_MAX(PCI_L1SS_CTL1_LTR_L12_TH_VALUE)) { *scale = 0; /* Value times 1ns */ *value = threshold_ns; - } else if (threshold_ns <= 0x3ff * 32) { + } else if (threshold_ns <= 32 * FIELD_MAX(PCI_L1SS_CTL1_LTR_L12_TH_VALUE)) { *scale = 1; /* Value times 32ns */ *value = roundup(threshold_ns, 32) / 32; - } else if (threshold_ns <= 0x3ff * 1024) { + } else if (threshold_ns <= 1024 * FIELD_MAX(PCI_L1SS_CTL1_LTR_L12_TH_VALUE)) { *scale = 2; /* Value times 1024ns */ *value = roundup(threshold_ns, 1024) / 1024; - } else if (threshold_ns <= 0x3ff * 32768) { + } else if (threshold_ns <= 32768 * FIELD_MAX(PCI_L1SS_CTL1_LTR_L12_TH_VALUE)) { *scale = 3; /* Value times 32768ns */ *value = roundup(threshold_ns, 32768) / 32768; - } else if (threshold_ns <= 0x3ff * 1048576) { + } else if (threshold_ns <= 1048576 * FIELD_MAX(PCI_L1SS_CTL1_LTR_L12_TH_VALUE)) { *scale = 4; /* Value times 1048576ns */ *value = roundup(threshold_ns, 1048576) / 1048576; - } else if (threshold_ns <= 0x3ff * (u64) 33554432) { + } else if (threshold_ns <= (u64)33554432 * FIELD_MAX(PCI_L1SS_CTL1_LTR_L12_TH_VALUE)) { *scale = 5; /* Value times 33554432ns */ *value = roundup(threshold_ns, 33554432) / 33554432; } else { *scale = 5; - *value = 0x3ff; /* Max representable value */ + *value = FIELD_MAX(PCI_L1SS_CTL1_LTR_L12_TH_VALUE); } } From 3c4f460480b0696e3cd0b019ba7915c57923961c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= Date: Fri, 15 Sep 2023 18:57:51 +0300 Subject: [PATCH 6/9] PCI/ASPM: Remove unnecessary includes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit aspm.c does not use anything from delay.h nor jiffies.h so remove the includes. Link: https://lore.kernel.org/r/20230915155752.84640-7-ilpo.jarvinen@linux.intel.com Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/aspm.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index fac6c5a0be26..fb25892d15b7 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -19,8 +19,6 @@ #include #include #include -#include -#include #include #include "../pci.h" From 3be31e95f3db18794139dcbdac1f370ee677f6ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= Date: Fri, 15 Sep 2023 18:57:52 +0300 Subject: [PATCH 7/9] PCI/ASPM: Convert printk() to pr_*() and add include MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert printk(KERN_INFO ...) to pr_info() and add the correct include for it. Link: https://lore.kernel.org/r/20230915155752.84640-8-ilpo.jarvinen@linux.intel.com Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/aspm.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index fb25892d15b7..855feaefd7f4 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -1367,10 +1368,10 @@ static int __init pcie_aspm_disable(char *str) aspm_policy = POLICY_DEFAULT; aspm_disabled = 1; aspm_support_enabled = false; - printk(KERN_INFO "PCIe ASPM is disabled\n"); + pr_info("PCIe ASPM is disabled\n"); } else if (!strcmp(str, "force")) { aspm_force = 1; - printk(KERN_INFO "PCIe ASPM is forcibly enabled\n"); + pr_info("PCIe ASPM is forcibly enabled\n"); } return 1; } From 3cb4f534bac010258b2688395c2f13459a932be9 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Wed, 11 Oct 2023 09:36:40 +0200 Subject: [PATCH 8/9] Revert "PCI/ASPM: Disable only ASPM_STATE_L1 when driver, disables L1" This reverts commit fb097dcd5a28c0a2325632405c76a66777a6bed9. After fb097dcd5a28 ("PCI/ASPM: Disable only ASPM_STATE_L1 when driver disables L1"), disabling L1 via pci_disable_link_state(PCIE_LINK_STATE_L1), then enabling one substate, e.g., L1.1, via sysfs actually enables *all* the substates. For example, r8169 disables L1 because of hardware issues on a number of systems, which implicitly disables the L1.1 and L1.2 substates. On some systems, L1 and L1.1 work fine, but L1.2 causes missed rx packets. Enabling L1.1 via the sysfs "aspm_l1_1" attribute unexpectedly enables L1.2 as well as L1.1. After fb097dcd5a28, pci_disable_link_state(PCIE_LINK_STATE_L1) adds only ASPM_L1 (but not any of the L1.x substates) to the "aspm_disable" mask: --- Before fb097dcd5a28 +++ After fb097dcd5a28 # r8169 disables L1: pci_disable_link_state(PCIE_LINK_STATE_L1) - disable |= ASPM_L1 | ASPM_L1_1 | ASPM_L1_2 | ... # disable L1, L1.x + disable |= ASPM_L1 # disable L1 only # write "1" to sysfs "aspm_l1_1" attribute: l1_1_aspm aspm_attr_store_common(state = ASPM_L1_1) disable &= ~ASPM_L1_1 # enable L1.1 if (state & (ASPM_L1_1 | ...)) # if enabling any substate disable &= ~ASPM_L1 # enable L1 # final state: - disable = ASPM_L1_2 | ... # L1, L1.1 enabled; L1.2 disabled + disable = 0 # L1, L1.1, L1.2 all enabled Enabling an L1.x substate removes the substate and L1 from the "aspm_disable" mask. After fb097dcd5a28, the substates were not added to the mask when disabling L1, so enabling one substate implicitly enables all of them. Revert fb097dcd5a28 so enabling one substate doesn't enable the others. Link: https://lore.kernel.org/r/c75931ac-7208-4200-9ca1-821629cf5e28@gmail.com Signed-off-by: Heiner Kallweit [bhelgaas: work through example in commit log] Signed-off-by: Bjorn Helgaas Cc: stable@vger.kernel.org --- drivers/pci/pcie/aspm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index 855feaefd7f4..dc203b8e5a63 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -1066,7 +1066,8 @@ static int __pci_disable_link_state(struct pci_dev *pdev, int state, bool sem) if (state & PCIE_LINK_STATE_L0S) link->aspm_disable |= ASPM_STATE_L0S; if (state & PCIE_LINK_STATE_L1) - link->aspm_disable |= ASPM_STATE_L1; + /* L1 PM substates require L1 */ + link->aspm_disable |= ASPM_STATE_L1 | ASPM_STATE_L1SS; if (state & PCIE_LINK_STATE_L1_1) link->aspm_disable |= ASPM_STATE_L1_1; if (state & PCIE_LINK_STATE_L1_2) From 8e37372ad0bea4c9b4712d9943f6ae96cff9491f Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Wed, 11 Oct 2023 09:46:45 +0200 Subject: [PATCH 9/9] PCI/ASPM: Fix L1 substate handling in aspm_attr_store_common() aspm_attr_store_common(), which handles sysfs control of ASPM, has the same problem as fb097dcd5a28 ("PCI/ASPM: Disable only ASPM_STATE_L1 when driver disables L1"): disabling L1 adds only ASPM_L1 (but not any of the L1.x substates) to the "aspm_disable" mask. Enabling one substate, e.g., L1.1, via sysfs removes ASPM_L1 from the disable mask. Since disabling L1 via sysfs doesn't add any of the substates to the disable mask, enabling L1.1 actually enables *all* the substates. In this scenario: - Write 0 to "l1_aspm" to disable L1 - Write 1 to "l1_1_aspm" to enable L1.1 the intention is to disable L1 and all L1.x substates, then enable just L1.1, but in fact, *all* L1.x substates are enabled. Fix this by explicitly disabling all the L1.x substates when disabling L1. Fixes: 72ea91afbfb0 ("PCI/ASPM: Add sysfs attributes for controlling ASPM link states") Link: https://lore.kernel.org/r/6ba7dd79-9cfe-4ed0-a002-d99cb842f361@gmail.com Signed-off-by: Heiner Kallweit [bhelgaas: commit log] Signed-off-by: Bjorn Helgaas Cc: stable@vger.kernel.org --- drivers/pci/pcie/aspm.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index dc203b8e5a63..99656d669f00 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -1255,6 +1255,8 @@ static ssize_t aspm_attr_store_common(struct device *dev, link->aspm_disable &= ~ASPM_STATE_L1; } else { link->aspm_disable |= state; + if (state & ASPM_STATE_L1) + link->aspm_disable |= ASPM_STATE_L1SS; } pcie_config_aspm_link(link, policy_to_aspm_state(link));