diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index 172783e7f519..95ac34a34bd5 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -839,116 +839,6 @@ static void aspm_l1ss_init(struct pcie_link_state *link) #define FLAG(x, y, d) (((x) & (PCIE_LINK_STATE_##y)) ? d : "") -static void pcie_aspm_override_default_link_state(struct pcie_link_state *link) -{ - struct pci_dev *pdev = link->downstream; - u32 override; - - /* For devicetree platforms, enable L0s and L1 by default */ - if (of_have_populated_dt()) { - if (link->aspm_support & PCIE_LINK_STATE_L0S) - link->aspm_default |= PCIE_LINK_STATE_L0S; - if (link->aspm_support & PCIE_LINK_STATE_L1) - link->aspm_default |= PCIE_LINK_STATE_L1; - override = link->aspm_default & ~link->aspm_enabled; - if (override) - pci_info(pdev, "ASPM: default states%s%s\n", - FLAG(override, L0S, " L0s"), - FLAG(override, L1, " L1")); - } -} - -static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist) -{ - struct pci_dev *child = link->downstream, *parent = link->pdev; - u16 parent_lnkctl, child_lnkctl; - struct pci_bus *linkbus = parent->subordinate; - - if (blacklist) { - /* Set enabled/disable so that we will disable ASPM later */ - link->aspm_enabled = PCIE_LINK_STATE_ASPM_ALL; - link->aspm_disable = PCIE_LINK_STATE_ASPM_ALL; - return; - } - - /* - * If ASPM not supported, don't mess with the clocks and link, - * bail out now. - */ - if (!(parent->aspm_l0s_support && child->aspm_l0s_support) && - !(parent->aspm_l1_support && child->aspm_l1_support)) - return; - - /* Configure common clock before checking latencies */ - pcie_aspm_configure_common_clock(link); - - /* - * Re-read upstream/downstream components' register state after - * clock configuration. L0s & L1 exit latencies in the otherwise - * read-only Link Capabilities may change depending on common clock - * configuration (PCIe r5.0, sec 7.5.3.6). - */ - pcie_capability_read_word(parent, PCI_EXP_LNKCTL, &parent_lnkctl); - pcie_capability_read_word(child, PCI_EXP_LNKCTL, &child_lnkctl); - - /* Disable L0s/L1 before updating L1SS config */ - if (FIELD_GET(PCI_EXP_LNKCTL_ASPMC, child_lnkctl) || - FIELD_GET(PCI_EXP_LNKCTL_ASPMC, parent_lnkctl)) { - pcie_capability_write_word(child, PCI_EXP_LNKCTL, - child_lnkctl & ~PCI_EXP_LNKCTL_ASPMC); - pcie_capability_write_word(parent, PCI_EXP_LNKCTL, - parent_lnkctl & ~PCI_EXP_LNKCTL_ASPMC); - } - - /* - * Setup L0s state - * - * Note that we must not enable L0s in either direction on a - * given link unless components on both sides of the link each - * support L0s. - */ - if (parent->aspm_l0s_support && child->aspm_l0s_support) - link->aspm_support |= PCIE_LINK_STATE_L0S; - - if (child_lnkctl & PCI_EXP_LNKCTL_ASPM_L0S) - link->aspm_enabled |= PCIE_LINK_STATE_L0S_UP; - if (parent_lnkctl & PCI_EXP_LNKCTL_ASPM_L0S) - link->aspm_enabled |= PCIE_LINK_STATE_L0S_DW; - - /* Setup L1 state */ - if (parent->aspm_l1_support && child->aspm_l1_support) - link->aspm_support |= PCIE_LINK_STATE_L1; - - if (parent_lnkctl & child_lnkctl & PCI_EXP_LNKCTL_ASPM_L1) - link->aspm_enabled |= PCIE_LINK_STATE_L1; - - aspm_l1ss_init(link); - - /* Restore L0s/L1 if they were enabled */ - if (FIELD_GET(PCI_EXP_LNKCTL_ASPMC, child_lnkctl) || - FIELD_GET(PCI_EXP_LNKCTL_ASPMC, parent_lnkctl)) { - pcie_capability_write_word(parent, PCI_EXP_LNKCTL, parent_lnkctl); - pcie_capability_write_word(child, PCI_EXP_LNKCTL, child_lnkctl); - } - - /* Save default state */ - link->aspm_default = link->aspm_enabled; - - pcie_aspm_override_default_link_state(link); - - /* Setup initial capable state. Will be updated later */ - link->aspm_capable = link->aspm_support; - - /* Get and check endpoint acceptable latencies */ - list_for_each_entry(child, &linkbus->devices, bus_list) { - if (pci_pcie_type(child) != PCI_EXP_TYPE_ENDPOINT && - pci_pcie_type(child) != PCI_EXP_TYPE_LEG_END) - continue; - - pcie_aspm_check_latency(child); - } -} - /* Configure the ASPM L1 substates. Caller must disable L1 first. */ static void pcie_config_aspm_l1ss(struct pcie_link_state *link, u32 state) { @@ -985,6 +875,171 @@ static void pcie_config_aspm_l1ss(struct pcie_link_state *link, u32 state) PCI_L1SS_CTL1_L1SS_MASK, val); } +static bool pcie_link_has_aspm_override(const struct pcie_link_state *link, + const char *aspm) +{ + return (device_property_present(&link->pdev->dev, aspm) || + device_property_present(&link->downstream->dev, aspm)); +} + +static void pcie_aspm_override_default_link_state(struct pcie_link_state *link) +{ + struct pci_dev *pdev = link->downstream; + u32 override; + + /* For devicetree platforms, enable L0s and L1 by default */ + if (of_have_populated_dt()) { + bool no_l0s = pcie_link_has_aspm_override(link, "aspm-no-l0s"); + bool no_l1 = pcie_link_has_aspm_override(link, "aspm-no-l1"); + bool no_l1ss = pcie_link_has_aspm_override(link, "aspm-no-l1ss"); + + if (no_l0s) { + link->aspm_support &= ~PCIE_LINK_STATE_L0S; + link->aspm_default &= ~PCIE_LINK_STATE_L0S; + link->aspm_enabled &= ~PCIE_LINK_STATE_L0S; + } + + /* + * Clear L1SS in hardware before updating aspm_support. Once + * aspm_capable is derived from aspm_support, pcie_config_aspm_link() + * skips pcie_config_aspm_l1ss() entirely via the aspm_capable guard, + * leaving firmware-enabled L1SS substates active in hardware. + * This applies equally when disabling L1 (which implies L1SS). + */ + if ((no_l1 || no_l1ss) && (link->aspm_enabled & PCIE_LINK_STATE_L1SS)) + pcie_config_aspm_l1ss(link, 0); + + if (no_l1) { + link->aspm_support &= ~(PCIE_LINK_STATE_L1 | PCIE_LINK_STATE_L1SS); + link->aspm_default &= ~(PCIE_LINK_STATE_L1 | PCIE_LINK_STATE_L1SS); + link->aspm_enabled &= ~(PCIE_LINK_STATE_L1 | PCIE_LINK_STATE_L1SS); + } else if (no_l1ss) { + link->aspm_support &= ~PCIE_LINK_STATE_L1SS; + link->aspm_default &= ~PCIE_LINK_STATE_L1SS; + link->aspm_enabled &= ~PCIE_LINK_STATE_L1SS; + } + + if (link->aspm_support & PCIE_LINK_STATE_L0S) + link->aspm_default |= PCIE_LINK_STATE_L0S; + if (link->aspm_support & PCIE_LINK_STATE_L1) + link->aspm_default |= PCIE_LINK_STATE_L1; + override = link->aspm_default & ~link->aspm_enabled; + if (override) + pci_info(pdev, "ASPM: default states%s%s\n", + FLAG(override, L0S, " L0s"), + FLAG(override, L1, " L1")); + } +} + +static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist) +{ + struct pci_dev *child = link->downstream, *parent = link->pdev; + struct pci_dev *fn; + u16 parent_lnkctl, child_lnkctl; + struct pci_bus *linkbus = parent->subordinate; + + if (blacklist) { + /* Set enabled/disable so that we will disable ASPM later */ + link->aspm_enabled = PCIE_LINK_STATE_ASPM_ALL; + link->aspm_disable = PCIE_LINK_STATE_ASPM_ALL; + return; + } + + /* + * If ASPM not supported, don't mess with the clocks and link, + * bail out now. + */ + if (!(parent->aspm_l0s_support && child->aspm_l0s_support) && + !(parent->aspm_l1_support && child->aspm_l1_support)) + return; + + /* Configure common clock before checking latencies */ + pcie_aspm_configure_common_clock(link); + + /* + * Re-read upstream/downstream components' register state after + * clock configuration. L0s & L1 exit latencies in the otherwise + * read-only Link Capabilities may change depending on common clock + * configuration (PCIe r5.0, sec 7.5.3.6). + */ + pcie_capability_read_word(parent, PCI_EXP_LNKCTL, &parent_lnkctl); + pcie_capability_read_word(child, PCI_EXP_LNKCTL, &child_lnkctl); + + /* Disable L0s/L1 before updating L1SS config */ + if (FIELD_GET(PCI_EXP_LNKCTL_ASPMC, child_lnkctl) || + FIELD_GET(PCI_EXP_LNKCTL_ASPMC, parent_lnkctl)) { + list_for_each_entry(fn, &linkbus->devices, bus_list) + pcie_capability_clear_and_set_word(fn, PCI_EXP_LNKCTL, + PCI_EXP_LNKCTL_ASPMC, 0); + pcie_capability_clear_and_set_word(parent, PCI_EXP_LNKCTL, + PCI_EXP_LNKCTL_ASPMC, 0); + } + + /* + * Setup L0s state + * + * Note that we must not enable L0s in either direction on a + * given link unless components on both sides of the link each + * support L0s. + */ + if (parent->aspm_l0s_support && child->aspm_l0s_support) + link->aspm_support |= PCIE_LINK_STATE_L0S; + + if (child_lnkctl & PCI_EXP_LNKCTL_ASPM_L0S) + link->aspm_enabled |= PCIE_LINK_STATE_L0S_UP; + if (parent_lnkctl & PCI_EXP_LNKCTL_ASPM_L0S) + link->aspm_enabled |= PCIE_LINK_STATE_L0S_DW; + + /* Setup L1 state */ + if (parent->aspm_l1_support && child->aspm_l1_support) + link->aspm_support |= PCIE_LINK_STATE_L1; + + if (parent_lnkctl & child_lnkctl & PCI_EXP_LNKCTL_ASPM_L1) + link->aspm_enabled |= PCIE_LINK_STATE_L1; + + aspm_l1ss_init(link); + + /* Save default state */ + link->aspm_default = link->aspm_enabled; + + pcie_aspm_override_default_link_state(link); + + /* + * Restore L0s/L1 if they were enabled, but don't restore any + * state a Devicetree override just disabled in aspm_support above. + */ + if (FIELD_GET(PCI_EXP_LNKCTL_ASPMC, child_lnkctl) || + FIELD_GET(PCI_EXP_LNKCTL_ASPMC, parent_lnkctl)) { + if (!(link->aspm_support & PCIE_LINK_STATE_L0S)) { + child_lnkctl &= ~PCI_EXP_LNKCTL_ASPM_L0S; + parent_lnkctl &= ~PCI_EXP_LNKCTL_ASPM_L0S; + } + if (!(link->aspm_support & PCIE_LINK_STATE_L1)) { + child_lnkctl &= ~PCI_EXP_LNKCTL_ASPM_L1; + parent_lnkctl &= ~PCI_EXP_LNKCTL_ASPM_L1; + } + pcie_capability_clear_and_set_word(parent, PCI_EXP_LNKCTL, + PCI_EXP_LNKCTL_ASPMC, + parent_lnkctl & PCI_EXP_LNKCTL_ASPMC); + list_for_each_entry(fn, &linkbus->devices, bus_list) + pcie_capability_clear_and_set_word(fn, PCI_EXP_LNKCTL, + PCI_EXP_LNKCTL_ASPMC, + child_lnkctl & PCI_EXP_LNKCTL_ASPMC); + } + + /* Setup initial capable state. Will be updated later */ + link->aspm_capable = link->aspm_support; + + /* Get and check endpoint acceptable latencies */ + list_for_each_entry(child, &linkbus->devices, bus_list) { + if (pci_pcie_type(child) != PCI_EXP_TYPE_ENDPOINT && + pci_pcie_type(child) != PCI_EXP_TYPE_LEG_END) + continue; + + pcie_aspm_check_latency(child); + } +} + static void pcie_config_aspm_dev(struct pci_dev *pdev, u32 val) { pcie_capability_clear_and_set_word(pdev, PCI_EXP_LNKCTL, diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index b09f27f7846f..06c014964251 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -2507,6 +2507,9 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x10f1, quirk_disable_aspm_l0s); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x10f4, quirk_disable_aspm_l0s); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1508, quirk_disable_aspm_l0s); +/* Realtek RTS525A generates a Replay Timer Timeout storm when L0s is enabled. */ +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_REALTEK, 0x525a, quirk_disable_aspm_l0s); + static void quirk_disable_aspm_l0s_l1(struct pci_dev *dev) { pcie_aspm_remove_cap(dev,