Skip to content

Commit f492edb

Browse files
debox1Lorenzo Pieralisi
authored andcommitted
PCI: vmd: Add quirk to configure PCIe ASPM and LTR
PCIe ports reserved for VMD use are not visible to BIOS and therefore not configured to enable PCIe ASPM or LTR values (which BIOS will configure if they are not set). Lack of this programming results in high power consumption on laptops as reported in bugzilla. For affected products use pci_enable_link_state to set the allowed link states for devices on the root ports. Also set the LTR value to the maximum value needed for the SoC. This is a workaround for products from Rocket Lake through Alder Lake. Raptor Lake, the latest product at this time, has already implemented LTR configuring in BIOS. Future products will move ASPM configuration back to BIOS as well. As this solution is intended for laptops, support is not added for hotplug or for devices downstream of a switch on the root port. Link: https://bugzilla.kernel.org/show_bug.cgi?id=212355 Link: https://bugzilla.kernel.org/show_bug.cgi?id=215063 Link: https://bugzilla.kernel.org/show_bug.cgi?id=213717 Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Michael Bottini <[email protected]> Signed-off-by: David E. Box <[email protected]> Signed-off-by: Lorenzo Pieralisi <[email protected]> Reviewed-by: Jon Derrick <[email protected]> Reviewed-by: Nirmal Patel <[email protected]> Reviewed-by: Kuppuswamy Sathyanarayanan <[email protected]>
1 parent 14d2079 commit f492edb

File tree

1 file changed

+54
-1
lines changed
  • drivers/pci/controller

1 file changed

+54
-1
lines changed

drivers/pci/controller/vmd.c

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,11 +66,22 @@ enum vmd_features {
6666
* interrupt handling.
6767
*/
6868
VMD_FEAT_CAN_BYPASS_MSI_REMAP = (1 << 4),
69+
70+
/*
71+
* Enable ASPM on the PCIE root ports and set the default LTR of the
72+
* storage devices on platforms where these values are not configured by
73+
* BIOS. This is needed for laptops, which require these settings for
74+
* proper power management of the SoC.
75+
*/
76+
VMD_FEAT_BIOS_PM_QUIRK = (1 << 5),
6977
};
7078

79+
#define VMD_BIOS_PM_QUIRK_LTR 0x1003 /* 3145728 ns */
80+
7181
#define VMD_FEATS_CLIENT (VMD_FEAT_HAS_MEMBAR_SHADOW_VSCAP | \
7282
VMD_FEAT_HAS_BUS_RESTRICTIONS | \
73-
VMD_FEAT_OFFSET_FIRST_VECTOR)
83+
VMD_FEAT_OFFSET_FIRST_VECTOR | \
84+
VMD_FEAT_BIOS_PM_QUIRK)
7485

7586
static DEFINE_IDA(vmd_instance_ida);
7687

@@ -713,6 +724,46 @@ static void vmd_copy_host_bridge_flags(struct pci_host_bridge *root_bridge,
713724
vmd_bridge->native_dpc = root_bridge->native_dpc;
714725
}
715726

727+
/*
728+
* Enable ASPM and LTR settings on devices that aren't configured by BIOS.
729+
*/
730+
static int vmd_pm_enable_quirk(struct pci_dev *pdev, void *userdata)
731+
{
732+
unsigned long features = *(unsigned long *)userdata;
733+
u16 ltr = VMD_BIOS_PM_QUIRK_LTR;
734+
u32 ltr_reg;
735+
int pos;
736+
737+
if (!(features & VMD_FEAT_BIOS_PM_QUIRK))
738+
return 0;
739+
740+
pci_enable_link_state(pdev, PCIE_LINK_STATE_ALL);
741+
742+
pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_LTR);
743+
if (!pos)
744+
return 0;
745+
746+
/*
747+
* Skip if the max snoop LTR is non-zero, indicating BIOS has set it
748+
* so the LTR quirk is not needed.
749+
*/
750+
pci_read_config_dword(pdev, pos + PCI_LTR_MAX_SNOOP_LAT, &ltr_reg);
751+
if (!!(ltr_reg & (PCI_LTR_VALUE_MASK | PCI_LTR_SCALE_MASK)))
752+
return 0;
753+
754+
/*
755+
* Set the default values to the maximum required by the platform to
756+
* allow the deepest power management savings. Write as a DWORD where
757+
* the lower word is the max snoop latency and the upper word is the
758+
* max non-snoop latency.
759+
*/
760+
ltr_reg = (ltr << 16) | ltr;
761+
pci_write_config_dword(pdev, pos + PCI_LTR_MAX_SNOOP_LAT, ltr_reg);
762+
pci_info(pdev, "VMD: Default LTR value set by driver\n");
763+
764+
return 0;
765+
}
766+
716767
static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features)
717768
{
718769
struct pci_sysdata *sd = &vmd->sysdata;
@@ -885,6 +936,8 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features)
885936

886937
pci_assign_unassigned_bus_resources(vmd->bus);
887938

939+
pci_walk_bus(vmd->bus, vmd_pm_enable_quirk, &features);
940+
888941
/*
889942
* VMD root buses are virtual and don't return true on pci_is_pcie()
890943
* and will fail pcie_bus_configure_settings() early. It can instead be

0 commit comments

Comments
 (0)