mirror of
https://github.com/torvalds/linux.git
synced 2026-09-23 22:14:03 +02:00
Merge branch 'pci/aspm'
- Avoid L0s for Realtek RTS525A, where it causes an AER interrupt storm (Max Lee) - Program the same ASPM Control values for every function of multi-function devices, as recommended by the PCIe spec (Krishna Chaitanya Chundru) - Avoid ASPM L0s, L1, and L1 PM Substates based on 'aspm-no-l0s', 'aspm-no-l1' [1], and 'aspm-no-l1ss' DT properties (Krishna Chaitanya Chundru) * pci/aspm: PCI/ASPM: Mask ASPM states based on Devicetree properties PCI/ASPM: Disable/restore ASPM on every function for multi-function devices PCI/ASPM: Use pcie_capability_clear_and_set_word() for ASPM disable/restore PCI/ASPM: Avoid L0s for Realtek RTS525A
This commit is contained in:
commit
606866edbc
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user