Skip to content

Commit f3ac2ff

Browse files
Mani-Sadhasivambjorn-helgaas
authored andcommitted
PCI/ASPM: Enable all ClockPM and ASPM states for devicetree platforms
So far, the PCI subsystem has honored the ASPM and Clock PM states set by the BIOS (through LNKCTL) during device initialization, if it relies on the default state selected using: * Kconfig: CONFIG_PCIEASPM_DEFAULT=y, or * cmdline: "pcie_aspm=off", or * FADT: ACPI_FADT_NO_ASPM This was done conservatively to avoid issues with the buggy devices that advertise ASPM capabilities, but behave erratically if the ASPM states are enabled. So the PCI subsystem ended up trusting the BIOS to enable only the ASPM states that were known to work for the devices. But this turned out to be a problem for devicetree platforms, especially the ARM based devicetree platforms powering Embedded and *some* Compute devices as they tend to run without any standard BIOS. So the ASPM states on these platforms were left disabled during boot and the PCI subsystem never bothered to enable them, unless the user has forcefully enabled the ASPM states through Kconfig, cmdline, and sysfs or the device drivers themselves, enabling the ASPM states through pci_enable_link_state() APIs. This caused runtime power issues on those platforms. So a couple of approaches were tried to mitigate this BIOS dependency without user intervention by enabling the ASPM states in the PCI controller drivers after device enumeration, and overriding the ASPM/Clock PM states by the PCI controller drivers through an API before enumeration. But it has been concluded that none of these mitigations should really be required and the PCI subsystem should enable the ASPM states advertised by the devices without relying on BIOS or the PCI controller drivers. If any device is found to be misbehaving after enabling ASPM states that they advertised, then those devices should be quirked to disable the problematic ASPM/Clock PM states. In an effort to do so, start by overriding the ASPM and Clock PM states set by the BIOS for devicetree platforms first. Separate helper functions are introduced to override the BIOS set states by enabling all of them if of_have_populated_dt() returns true. To aid debugging, print the overridden ASPM and Clock PM states as well. In the future, these helpers could be extended to allow other platforms like VMD, newer ACPI systems with a cutoff year etc... to follow the path. Link: https://lore.kernel.org/linux-pci/20250828204345.GA958461@bhelgaas Suggested-by: Bjorn Helgaas <helgaas@kernel.org> Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com> [bhelgaas: tweak comments and dmesg logs] Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> Link: https://patch.msgid.link/20250922-pci-dt-aspm-v2-1-2a65cf84e326@oss.qualcomm.com
1 parent 8f5ae30 commit f3ac2ff

1 file changed

Lines changed: 43 additions & 2 deletions

File tree

drivers/pci/pcie/aspm.c

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include <linux/math.h>
1616
#include <linux/module.h>
1717
#include <linux/moduleparam.h>
18+
#include <linux/of.h>
1819
#include <linux/pci.h>
1920
#include <linux/pci_regs.h>
2021
#include <linux/errno.h>
@@ -235,13 +236,15 @@ struct pcie_link_state {
235236
u32 aspm_support:7; /* Supported ASPM state */
236237
u32 aspm_enabled:7; /* Enabled ASPM state */
237238
u32 aspm_capable:7; /* Capable ASPM state with latency */
238-
u32 aspm_default:7; /* Default ASPM state by BIOS */
239+
u32 aspm_default:7; /* Default ASPM state by BIOS or
240+
override */
239241
u32 aspm_disable:7; /* Disabled ASPM state */
240242

241243
/* Clock PM state */
242244
u32 clkpm_capable:1; /* Clock PM capable? */
243245
u32 clkpm_enabled:1; /* Current Clock PM state */
244-
u32 clkpm_default:1; /* Default Clock PM state by BIOS */
246+
u32 clkpm_default:1; /* Default Clock PM state by BIOS or
247+
override */
245248
u32 clkpm_disable:1; /* Clock PM disabled */
246249
};
247250

@@ -373,6 +376,18 @@ static void pcie_set_clkpm(struct pcie_link_state *link, int enable)
373376
pcie_set_clkpm_nocheck(link, enable);
374377
}
375378

379+
static void pcie_clkpm_override_default_link_state(struct pcie_link_state *link,
380+
int enabled)
381+
{
382+
struct pci_dev *pdev = link->downstream;
383+
384+
/* For devicetree platforms, enable ClockPM by default */
385+
if (of_have_populated_dt() && !enabled) {
386+
link->clkpm_default = 1;
387+
pci_info(pdev, "ASPM: DT platform, enabling ClockPM\n");
388+
}
389+
}
390+
376391
static void pcie_clkpm_cap_init(struct pcie_link_state *link, int blacklist)
377392
{
378393
int capable = 1, enabled = 1;
@@ -395,6 +410,7 @@ static void pcie_clkpm_cap_init(struct pcie_link_state *link, int blacklist)
395410
}
396411
link->clkpm_enabled = enabled;
397412
link->clkpm_default = enabled;
413+
pcie_clkpm_override_default_link_state(link, enabled);
398414
link->clkpm_capable = capable;
399415
link->clkpm_disable = blacklist ? 1 : 0;
400416
}
@@ -788,6 +804,29 @@ static void aspm_l1ss_init(struct pcie_link_state *link)
788804
aspm_calc_l12_info(link, parent_l1ss_cap, child_l1ss_cap);
789805
}
790806

807+
#define FLAG(x, y, d) (((x) & (PCIE_LINK_STATE_##y)) ? d : "")
808+
809+
static void pcie_aspm_override_default_link_state(struct pcie_link_state *link)
810+
{
811+
struct pci_dev *pdev = link->downstream;
812+
u32 override;
813+
814+
/* For devicetree platforms, enable all ASPM states by default */
815+
if (of_have_populated_dt()) {
816+
link->aspm_default = PCIE_LINK_STATE_ASPM_ALL;
817+
override = link->aspm_default & ~link->aspm_enabled;
818+
if (override)
819+
pci_info(pdev, "ASPM: DT platform, enabling%s%s%s%s%s%s%s\n",
820+
FLAG(override, L0S_UP, " L0s-up"),
821+
FLAG(override, L0S_DW, " L0s-dw"),
822+
FLAG(override, L1, " L1"),
823+
FLAG(override, L1_1, " ASPM-L1.1"),
824+
FLAG(override, L1_2, " ASPM-L1.2"),
825+
FLAG(override, L1_1_PCIPM, " PCI-PM-L1.1"),
826+
FLAG(override, L1_2_PCIPM, " PCI-PM-L1.2"));
827+
}
828+
}
829+
791830
static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist)
792831
{
793832
struct pci_dev *child = link->downstream, *parent = link->pdev;
@@ -868,6 +907,8 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist)
868907
/* Save default state */
869908
link->aspm_default = link->aspm_enabled;
870909

910+
pcie_aspm_override_default_link_state(link);
911+
871912
/* Setup initial capable state. Will be updated later */
872913
link->aspm_capable = link->aspm_support;
873914

0 commit comments

Comments
 (0)