* [PATCH] PCI/ASPM: Allow controller drivers to override default ASPM and CLKPM link state
@ 2025-07-20 19:01 David E. Box
2025-07-21 8:23 ` Manivannan Sadhasivam
0 siblings, 1 reply; 10+ messages in thread
From: David E. Box @ 2025-07-20 19:01 UTC (permalink / raw)
To: rafael, bhelgaas, vicamo.yang, kenny, ilpo.jarvinen, nirmal.patel,
mani
Cc: David E. Box, linux-pm, linux-pci, linux-kernel
Synthetic PCIe hierarchies, such as those created by Intel VMD, are not
visible to firmware and do not receive BIOS-provided default ASPM and CLKPM
configuration. As a result, devices behind such domains operate without
proper power management, regardless of platform intent.
To address this, allow controller drivers to supply an override for the
default link state by setting aspm_dflt_link_state for their associated
pci_host_bridge. During link initialization, if this field is non-zero,
ASPM and CLKPM defaults are derived from its value instead of being taken
from BIOS.
This mechanism enables drivers like VMD to achieve platform-aligned power
savings by statically defining the expected link configuration at
enumeration time, without relying on runtime calls such as
pci_enable_link_state(), which are ineffective when ASPM is disabled
globally.
This approach avoids per-controller hacks in ASPM core logic and provides a
general mechanism for domains that require explicit control over link power
state defaults.
Link: https://lore.kernel.org/linux-pm/0b166ece-eeec-ba5d-2212-50d995611cef@panix.com
Signed-off-by: David E. Box <david.e.box@linux.intel.com>
---
Changes from RFC:
-- Rename field to aspm_dflt_link_state since it stores
PCIE_LINK_STATE_XXX flags, not a policy enum.
-- Move the field to struct pci_host_bridge since it's being applied to
the entire host bridge per Mani's suggestion.
-- During testing noticed that clkpm remained disabled and this was
also handled by the formerly used pci_enable_link_state(). Add a
check in pcie_clkpm_cap_init() as well to enable clkpm during init.
drivers/pci/controller/vmd.c | 12 +++++++++---
drivers/pci/pcie/aspm.c | 13 +++++++++++--
include/linux/pci.h | 4 ++++
3 files changed, 24 insertions(+), 5 deletions(-)
diff --git a/drivers/pci/controller/vmd.c b/drivers/pci/controller/vmd.c
index 8df064b62a2f..6f0de95c87fd 100644
--- a/drivers/pci/controller/vmd.c
+++ b/drivers/pci/controller/vmd.c
@@ -730,7 +730,7 @@ static void vmd_copy_host_bridge_flags(struct pci_host_bridge *root_bridge,
}
/*
- * Enable ASPM and LTR settings on devices that aren't configured by BIOS.
+ * Enable LTR settings on devices that aren't configured by BIOS.
*/
static int vmd_pm_enable_quirk(struct pci_dev *pdev, void *userdata)
{
@@ -770,7 +770,6 @@ static int vmd_pm_enable_quirk(struct pci_dev *pdev, void *userdata)
* PCIe r6.0, sec 5.5.4.
*/
pci_set_power_state_locked(pdev, PCI_D0);
- pci_enable_link_state_locked(pdev, PCIE_LINK_STATE_ALL);
return 0;
}
@@ -785,6 +784,7 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features)
resource_size_t membar2_offset = 0x2000;
struct pci_bus *child;
struct pci_dev *dev;
+ struct pci_host_bridge *vmd_host_bridge;
int ret;
/*
@@ -911,8 +911,14 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features)
return -ENODEV;
}
+ vmd_host_bridge = to_pci_host_bridge(vmd->bus->bridge);
+
+#ifdef CONFIG_PCIEASPM
+ vmd_host_bridge->aspm_dflt_link_state = PCIE_LINK_STATE_ALL;
+#endif
+
vmd_copy_host_bridge_flags(pci_find_host_bridge(vmd->dev->bus),
- to_pci_host_bridge(vmd->bus->bridge));
+ vmd_host_bridge);
vmd_attach_resources(vmd);
if (vmd->irq_domain)
diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c
index 29fcb0689a91..6f5b34b172f9 100644
--- a/drivers/pci/pcie/aspm.c
+++ b/drivers/pci/pcie/aspm.c
@@ -380,6 +380,7 @@ static void pcie_clkpm_cap_init(struct pcie_link_state *link, int blacklist)
u16 reg16;
struct pci_dev *child;
struct pci_bus *linkbus = link->pdev->subordinate;
+ struct pci_host_bridge *host = pci_find_host_bridge(link->pdev->bus);
/* All functions should have the same cap and state, take the worst */
list_for_each_entry(child, &linkbus->devices, bus_list) {
@@ -394,7 +395,10 @@ static void pcie_clkpm_cap_init(struct pcie_link_state *link, int blacklist)
enabled = 0;
}
link->clkpm_enabled = enabled;
- link->clkpm_default = enabled;
+ if (host && host->aspm_dflt_link_state & PCIE_LINK_STATE_CLKPM)
+ link->clkpm_default = 1;
+ else
+ link->clkpm_default = enabled;
link->clkpm_capable = capable;
link->clkpm_disable = blacklist ? 1 : 0;
}
@@ -794,6 +798,7 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist)
u32 parent_lnkcap, child_lnkcap;
u16 parent_lnkctl, child_lnkctl;
struct pci_bus *linkbus = parent->subordinate;
+ struct pci_host_bridge *host;
if (blacklist) {
/* Set enabled/disable so that we will disable ASPM later */
@@ -866,7 +871,11 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist)
}
/* Save default state */
- link->aspm_default = link->aspm_enabled;
+ host = pci_find_host_bridge(parent->bus);
+ if (host && host->aspm_dflt_link_state)
+ link->aspm_default = host->aspm_dflt_link_state;
+ else
+ link->aspm_default = link->aspm_enabled;
/* Setup initial capable state. Will be updated later */
link->aspm_capable = link->aspm_support;
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 05e68f35f392..930028bf52b4 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -614,6 +614,10 @@ struct pci_host_bridge {
unsigned int size_windows:1; /* Enable root bus sizing */
unsigned int msi_domain:1; /* Bridge wants MSI domain */
+#ifdef CONFIG_PCIEASPM
+ unsigned int aspm_dflt_link_state; /* Controller provided link state */
+#endif
+
/* Resource alignment requirements */
resource_size_t (*align_resource)(struct pci_dev *dev,
const struct resource *res,
base-commit: d0b3b7b22dfa1f4b515fd3a295b3fd958f9e81af
--
2.43.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH] PCI/ASPM: Allow controller drivers to override default ASPM and CLKPM link state
2025-07-20 19:01 [PATCH] PCI/ASPM: Allow controller drivers to override default ASPM and CLKPM link state David E. Box
@ 2025-07-21 8:23 ` Manivannan Sadhasivam
2025-07-23 11:54 ` Rafael J. Wysocki
0 siblings, 1 reply; 10+ messages in thread
From: Manivannan Sadhasivam @ 2025-07-21 8:23 UTC (permalink / raw)
To: David E. Box
Cc: rafael, bhelgaas, vicamo.yang, kenny, ilpo.jarvinen, nirmal.patel,
linux-pm, linux-pci, linux-kernel
On Sun, Jul 20, 2025 at 12:01:37PM GMT, David E. Box wrote:
> Synthetic PCIe hierarchies, such as those created by Intel VMD, are not
> visible to firmware and do not receive BIOS-provided default ASPM and CLKPM
> configuration. As a result, devices behind such domains operate without
> proper power management, regardless of platform intent.
>
> To address this, allow controller drivers to supply an override for the
> default link state by setting aspm_dflt_link_state for their associated
> pci_host_bridge. During link initialization, if this field is non-zero,
> ASPM and CLKPM defaults are derived from its value instead of being taken
> from BIOS.
>
> This mechanism enables drivers like VMD to achieve platform-aligned power
> savings by statically defining the expected link configuration at
> enumeration time, without relying on runtime calls such as
> pci_enable_link_state(), which are ineffective when ASPM is disabled
> globally.
>
> This approach avoids per-controller hacks in ASPM core logic and provides a
> general mechanism for domains that require explicit control over link power
> state defaults.
>
> Link: https://lore.kernel.org/linux-pm/0b166ece-eeec-ba5d-2212-50d995611cef@panix.com
> Signed-off-by: David E. Box <david.e.box@linux.intel.com>
> ---
>
> Changes from RFC:
>
> -- Rename field to aspm_dflt_link_state since it stores
> PCIE_LINK_STATE_XXX flags, not a policy enum.
> -- Move the field to struct pci_host_bridge since it's being applied to
> the entire host bridge per Mani's suggestion.
> -- During testing noticed that clkpm remained disabled and this was
> also handled by the formerly used pci_enable_link_state(). Add a
> check in pcie_clkpm_cap_init() as well to enable clkpm during init.
>
> drivers/pci/controller/vmd.c | 12 +++++++++---
> drivers/pci/pcie/aspm.c | 13 +++++++++++--
> include/linux/pci.h | 4 ++++
> 3 files changed, 24 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/pci/controller/vmd.c b/drivers/pci/controller/vmd.c
> index 8df064b62a2f..6f0de95c87fd 100644
> --- a/drivers/pci/controller/vmd.c
> +++ b/drivers/pci/controller/vmd.c
> @@ -730,7 +730,7 @@ static void vmd_copy_host_bridge_flags(struct pci_host_bridge *root_bridge,
> }
>
> /*
> - * Enable ASPM and LTR settings on devices that aren't configured by BIOS.
> + * Enable LTR settings on devices that aren't configured by BIOS.
> */
> static int vmd_pm_enable_quirk(struct pci_dev *pdev, void *userdata)
> {
> @@ -770,7 +770,6 @@ static int vmd_pm_enable_quirk(struct pci_dev *pdev, void *userdata)
> * PCIe r6.0, sec 5.5.4.
> */
> pci_set_power_state_locked(pdev, PCI_D0);
This call becomes useless now.
> - pci_enable_link_state_locked(pdev, PCIE_LINK_STATE_ALL);
> return 0;
> }
>
> @@ -785,6 +784,7 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features)
> resource_size_t membar2_offset = 0x2000;
> struct pci_bus *child;
> struct pci_dev *dev;
> + struct pci_host_bridge *vmd_host_bridge;
> int ret;
>
> /*
> @@ -911,8 +911,14 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features)
> return -ENODEV;
> }
>
> + vmd_host_bridge = to_pci_host_bridge(vmd->bus->bridge);
> +
> +#ifdef CONFIG_PCIEASPM
> + vmd_host_bridge->aspm_dflt_link_state = PCIE_LINK_STATE_ALL;
> +#endif
I think it is better to provide an API that accepts the link state. We can
provide a stub if CONFIG_PCIEASPM is not selected. This will avoid the ifdef
clutter in the callers. Like:
void pci_set_default_link_state(struct pci_host_bridge *host_bridge,
unsigned int state)
{
#ifdef CONFIG_PCIEASPM
host_bridge->aspm_default_link_state = state;
#endif
}
Or you can stub the entire function to align with other ASPM APIs.
One more thought: Since this API is only going to be called by the host bridge
drivers, we can place it in drivers/pci/controller/pci-host-common.c and name it
as pci_host_common_set_default_link_state().
> +
> vmd_copy_host_bridge_flags(pci_find_host_bridge(vmd->dev->bus),
> - to_pci_host_bridge(vmd->bus->bridge));
> + vmd_host_bridge);
>
> vmd_attach_resources(vmd);
> if (vmd->irq_domain)
> diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c
> index 29fcb0689a91..6f5b34b172f9 100644
> --- a/drivers/pci/pcie/aspm.c
> +++ b/drivers/pci/pcie/aspm.c
> @@ -380,6 +380,7 @@ static void pcie_clkpm_cap_init(struct pcie_link_state *link, int blacklist)
> u16 reg16;
> struct pci_dev *child;
> struct pci_bus *linkbus = link->pdev->subordinate;
> + struct pci_host_bridge *host = pci_find_host_bridge(link->pdev->bus);
>
> /* All functions should have the same cap and state, take the worst */
> list_for_each_entry(child, &linkbus->devices, bus_list) {
> @@ -394,7 +395,10 @@ static void pcie_clkpm_cap_init(struct pcie_link_state *link, int blacklist)
> enabled = 0;
> }
> link->clkpm_enabled = enabled;
> - link->clkpm_default = enabled;
> + if (host && host->aspm_dflt_link_state & PCIE_LINK_STATE_CLKPM)
> + link->clkpm_default = 1;
> + else
> + link->clkpm_default = enabled;
> link->clkpm_capable = capable;
> link->clkpm_disable = blacklist ? 1 : 0;
> }
> @@ -794,6 +798,7 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist)
> u32 parent_lnkcap, child_lnkcap;
> u16 parent_lnkctl, child_lnkctl;
> struct pci_bus *linkbus = parent->subordinate;
> + struct pci_host_bridge *host;
>
> if (blacklist) {
> /* Set enabled/disable so that we will disable ASPM later */
> @@ -866,7 +871,11 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist)
> }
>
> /* Save default state */
> - link->aspm_default = link->aspm_enabled;
> + host = pci_find_host_bridge(parent->bus);
You can initialize 'host' while defining it.
Also, please add a comment on why we are doing this. The inline comment for the
member is not elaborate enough:
/*
* Use the default link state provided by the Host Bridge driver if
* available. If the BIOS is not able to provide default ASPM link
* state for some reason, the Host Bridge driver could do.
*/
> + if (host && host->aspm_dflt_link_state)
> + link->aspm_default = host->aspm_dflt_link_state;
> + else
> + link->aspm_default = link->aspm_enabled;
>
> /* Setup initial capable state. Will be updated later */
> link->aspm_capable = link->aspm_support;
> diff --git a/include/linux/pci.h b/include/linux/pci.h
> index 05e68f35f392..930028bf52b4 100644
> --- a/include/linux/pci.h
> +++ b/include/linux/pci.h
> @@ -614,6 +614,10 @@ struct pci_host_bridge {
> unsigned int size_windows:1; /* Enable root bus sizing */
> unsigned int msi_domain:1; /* Bridge wants MSI domain */
>
> +#ifdef CONFIG_PCIEASPM
> + unsigned int aspm_dflt_link_state; /* Controller provided link state */
/* Controller provided default link state */
Nit: Please expand 'default' as 'dflt' is not a commonly used acronym for
'default'.
- Mani
--
மணிவண்ணன் சதாசிவம்
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] PCI/ASPM: Allow controller drivers to override default ASPM and CLKPM link state
2025-07-21 8:23 ` Manivannan Sadhasivam
@ 2025-07-23 11:54 ` Rafael J. Wysocki
2025-07-23 11:56 ` Rafael J. Wysocki
2025-07-23 21:27 ` David Box
0 siblings, 2 replies; 10+ messages in thread
From: Rafael J. Wysocki @ 2025-07-23 11:54 UTC (permalink / raw)
To: Manivannan Sadhasivam, David E. Box
Cc: bhelgaas, vicamo.yang, kenny, ilpo.jarvinen, nirmal.patel,
linux-pm, linux-pci, linux-kernel
On Mon, Jul 21, 2025 at 10:24 AM Manivannan Sadhasivam <mani@kernel.org> wrote:
>
> On Sun, Jul 20, 2025 at 12:01:37PM GMT, David E. Box wrote:
> > Synthetic PCIe hierarchies, such as those created by Intel VMD, are not
> > visible to firmware and do not receive BIOS-provided default ASPM and CLKPM
> > configuration. As a result, devices behind such domains operate without
> > proper power management, regardless of platform intent.
> >
> > To address this, allow controller drivers to supply an override for the
> > default link state by setting aspm_dflt_link_state for their associated
> > pci_host_bridge. During link initialization, if this field is non-zero,
> > ASPM and CLKPM defaults are derived from its value instead of being taken
> > from BIOS.
> >
> > This mechanism enables drivers like VMD to achieve platform-aligned power
> > savings by statically defining the expected link configuration at
> > enumeration time, without relying on runtime calls such as
> > pci_enable_link_state(), which are ineffective when ASPM is disabled
> > globally.
> >
> > This approach avoids per-controller hacks in ASPM core logic and provides a
> > general mechanism for domains that require explicit control over link power
> > state defaults.
> >
> > Link: https://lore.kernel.org/linux-pm/0b166ece-eeec-ba5d-2212-50d995611cef@panix.com
> > Signed-off-by: David E. Box <david.e.box@linux.intel.com>
> > ---
> >
> > Changes from RFC:
> >
> > -- Rename field to aspm_dflt_link_state since it stores
> > PCIE_LINK_STATE_XXX flags, not a policy enum.
> > -- Move the field to struct pci_host_bridge since it's being applied to
> > the entire host bridge per Mani's suggestion.
> > -- During testing noticed that clkpm remained disabled and this was
> > also handled by the formerly used pci_enable_link_state(). Add a
> > check in pcie_clkpm_cap_init() as well to enable clkpm during init.
> >
> > drivers/pci/controller/vmd.c | 12 +++++++++---
> > drivers/pci/pcie/aspm.c | 13 +++++++++++--
> > include/linux/pci.h | 4 ++++
> > 3 files changed, 24 insertions(+), 5 deletions(-)
> >
> > diff --git a/drivers/pci/controller/vmd.c b/drivers/pci/controller/vmd.c
> > index 8df064b62a2f..6f0de95c87fd 100644
> > --- a/drivers/pci/controller/vmd.c
> > +++ b/drivers/pci/controller/vmd.c
> > @@ -730,7 +730,7 @@ static void vmd_copy_host_bridge_flags(struct pci_host_bridge *root_bridge,
> > }
> >
> > /*
> > - * Enable ASPM and LTR settings on devices that aren't configured by BIOS.
> > + * Enable LTR settings on devices that aren't configured by BIOS.
> > */
> > static int vmd_pm_enable_quirk(struct pci_dev *pdev, void *userdata)
> > {
> > @@ -770,7 +770,6 @@ static int vmd_pm_enable_quirk(struct pci_dev *pdev, void *userdata)
> > * PCIe r6.0, sec 5.5.4.
> > */
> > pci_set_power_state_locked(pdev, PCI_D0);
>
> This call becomes useless now.
>
> > - pci_enable_link_state_locked(pdev, PCIE_LINK_STATE_ALL);
> > return 0;
> > }
> >
> > @@ -785,6 +784,7 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features)
> > resource_size_t membar2_offset = 0x2000;
> > struct pci_bus *child;
> > struct pci_dev *dev;
> > + struct pci_host_bridge *vmd_host_bridge;
> > int ret;
> >
> > /*
> > @@ -911,8 +911,14 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features)
> > return -ENODEV;
> > }
> >
> > + vmd_host_bridge = to_pci_host_bridge(vmd->bus->bridge);
> > +
> > +#ifdef CONFIG_PCIEASPM
> > + vmd_host_bridge->aspm_dflt_link_state = PCIE_LINK_STATE_ALL;
> > +#endif
>
> I think it is better to provide an API that accepts the link state. We can
> provide a stub if CONFIG_PCIEASPM is not selected. This will avoid the ifdef
> clutter in the callers. Like:
>
> void pci_set_default_link_state(struct pci_host_bridge *host_bridge,
> unsigned int state)
> {
> #ifdef CONFIG_PCIEASPM
> host_bridge->aspm_default_link_state = state;
> #endif
> }
>
> Or you can stub the entire function to align with other ASPM APIs.
>
> One more thought: Since this API is only going to be called by the host bridge
> drivers, we can place it in drivers/pci/controller/pci-host-common.c and name it
> as pci_host_common_set_default_link_state().
I agree with the above except for the new function name. I'd call it
pci_host_set_default_pcie_link_state()
> > +
> > vmd_copy_host_bridge_flags(pci_find_host_bridge(vmd->dev->bus),
> > - to_pci_host_bridge(vmd->bus->bridge));
> > + vmd_host_bridge);
> >
> > vmd_attach_resources(vmd);
> > if (vmd->irq_domain)
> > diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c
> > index 29fcb0689a91..6f5b34b172f9 100644
> > --- a/drivers/pci/pcie/aspm.c
> > +++ b/drivers/pci/pcie/aspm.c
> > @@ -380,6 +380,7 @@ static void pcie_clkpm_cap_init(struct pcie_link_state *link, int blacklist)
> > u16 reg16;
> > struct pci_dev *child;
> > struct pci_bus *linkbus = link->pdev->subordinate;
> > + struct pci_host_bridge *host = pci_find_host_bridge(link->pdev->bus);
> >
> > /* All functions should have the same cap and state, take the worst */
> > list_for_each_entry(child, &linkbus->devices, bus_list) {
> > @@ -394,7 +395,10 @@ static void pcie_clkpm_cap_init(struct pcie_link_state *link, int blacklist)
> > enabled = 0;
> > }
> > link->clkpm_enabled = enabled;
> > - link->clkpm_default = enabled;
> > + if (host && host->aspm_dflt_link_state & PCIE_LINK_STATE_CLKPM)
> > + link->clkpm_default = 1;
> > + else
> > + link->clkpm_default = enabled;
> > link->clkpm_capable = capable;
> > link->clkpm_disable = blacklist ? 1 : 0;
> > }
> > @@ -794,6 +798,7 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist)
> > u32 parent_lnkcap, child_lnkcap;
> > u16 parent_lnkctl, child_lnkctl;
> > struct pci_bus *linkbus = parent->subordinate;
> > + struct pci_host_bridge *host;
> >
> > if (blacklist) {
> > /* Set enabled/disable so that we will disable ASPM later */
> > @@ -866,7 +871,11 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist)
> > }
> >
> > /* Save default state */
> > - link->aspm_default = link->aspm_enabled;
> > + host = pci_find_host_bridge(parent->bus);
>
> You can initialize 'host' while defining it.
>
> Also, please add a comment on why we are doing this. The inline comment for the
> member is not elaborate enough:
>
> /*
> * Use the default link state provided by the Host Bridge driver if
> * available. If the BIOS is not able to provide default ASPM link
> * state for some reason, the Host Bridge driver could do.
> */
>
> > + if (host && host->aspm_dflt_link_state)
> > + link->aspm_default = host->aspm_dflt_link_state;
> > + else
> > + link->aspm_default = link->aspm_enabled;
Or
link->aspm_default = pci_host_get_default_pcie_link_state(parent);
if (link->aspm_default)
link->aspm_default = link->aspm_enabled;
and make pci_host_get_default_pcie_link_state() return 0 on failures.
Then you can put all of the relevant information into the
pci_host_get_default_pcie_link_state() kerneldoc comment.
> >
> > /* Setup initial capable state. Will be updated later */
> > link->aspm_capable = link->aspm_support;
> > diff --git a/include/linux/pci.h b/include/linux/pci.h
> > index 05e68f35f392..930028bf52b4 100644
> > --- a/include/linux/pci.h
> > +++ b/include/linux/pci.h
> > @@ -614,6 +614,10 @@ struct pci_host_bridge {
> > unsigned int size_windows:1; /* Enable root bus sizing */
> > unsigned int msi_domain:1; /* Bridge wants MSI domain */
> >
> > +#ifdef CONFIG_PCIEASPM
> > + unsigned int aspm_dflt_link_state; /* Controller provided link state */
>
> /* Controller provided default link state */
>
>
> Nit: Please expand 'default' as 'dflt' is not a commonly used acronym for
> 'default'.
I agree.
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] PCI/ASPM: Allow controller drivers to override default ASPM and CLKPM link state
2025-07-23 11:54 ` Rafael J. Wysocki
@ 2025-07-23 11:56 ` Rafael J. Wysocki
2025-07-23 21:27 ` David Box
1 sibling, 0 replies; 10+ messages in thread
From: Rafael J. Wysocki @ 2025-07-23 11:56 UTC (permalink / raw)
To: Manivannan Sadhasivam, David E. Box
Cc: bhelgaas, vicamo.yang, kenny, ilpo.jarvinen, nirmal.patel,
linux-pm, linux-pci, linux-kernel
On Wed, Jul 23, 2025 at 1:54 PM Rafael J. Wysocki <rafael@kernel.org> wrote:
>
> On Mon, Jul 21, 2025 at 10:24 AM Manivannan Sadhasivam <mani@kernel.org> wrote:
> >
> > On Sun, Jul 20, 2025 at 12:01:37PM GMT, David E. Box wrote:
> > > Synthetic PCIe hierarchies, such as those created by Intel VMD, are not
> > > visible to firmware and do not receive BIOS-provided default ASPM and CLKPM
> > > configuration. As a result, devices behind such domains operate without
> > > proper power management, regardless of platform intent.
> > >
> > > To address this, allow controller drivers to supply an override for the
> > > default link state by setting aspm_dflt_link_state for their associated
> > > pci_host_bridge. During link initialization, if this field is non-zero,
> > > ASPM and CLKPM defaults are derived from its value instead of being taken
> > > from BIOS.
> > >
> > > This mechanism enables drivers like VMD to achieve platform-aligned power
> > > savings by statically defining the expected link configuration at
> > > enumeration time, without relying on runtime calls such as
> > > pci_enable_link_state(), which are ineffective when ASPM is disabled
> > > globally.
> > >
> > > This approach avoids per-controller hacks in ASPM core logic and provides a
> > > general mechanism for domains that require explicit control over link power
> > > state defaults.
> > >
> > > Link: https://lore.kernel.org/linux-pm/0b166ece-eeec-ba5d-2212-50d995611cef@panix.com
> > > Signed-off-by: David E. Box <david.e.box@linux.intel.com>
> > > ---
> > >
> > > Changes from RFC:
> > >
> > > -- Rename field to aspm_dflt_link_state since it stores
> > > PCIE_LINK_STATE_XXX flags, not a policy enum.
> > > -- Move the field to struct pci_host_bridge since it's being applied to
> > > the entire host bridge per Mani's suggestion.
> > > -- During testing noticed that clkpm remained disabled and this was
> > > also handled by the formerly used pci_enable_link_state(). Add a
> > > check in pcie_clkpm_cap_init() as well to enable clkpm during init.
> > >
> > > drivers/pci/controller/vmd.c | 12 +++++++++---
> > > drivers/pci/pcie/aspm.c | 13 +++++++++++--
> > > include/linux/pci.h | 4 ++++
> > > 3 files changed, 24 insertions(+), 5 deletions(-)
> > >
> > > diff --git a/drivers/pci/controller/vmd.c b/drivers/pci/controller/vmd.c
> > > index 8df064b62a2f..6f0de95c87fd 100644
> > > --- a/drivers/pci/controller/vmd.c
> > > +++ b/drivers/pci/controller/vmd.c
> > > @@ -730,7 +730,7 @@ static void vmd_copy_host_bridge_flags(struct pci_host_bridge *root_bridge,
> > > }
> > >
> > > /*
> > > - * Enable ASPM and LTR settings on devices that aren't configured by BIOS.
> > > + * Enable LTR settings on devices that aren't configured by BIOS.
> > > */
> > > static int vmd_pm_enable_quirk(struct pci_dev *pdev, void *userdata)
> > > {
> > > @@ -770,7 +770,6 @@ static int vmd_pm_enable_quirk(struct pci_dev *pdev, void *userdata)
> > > * PCIe r6.0, sec 5.5.4.
> > > */
> > > pci_set_power_state_locked(pdev, PCI_D0);
> >
> > This call becomes useless now.
> >
> > > - pci_enable_link_state_locked(pdev, PCIE_LINK_STATE_ALL);
> > > return 0;
> > > }
> > >
> > > @@ -785,6 +784,7 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features)
> > > resource_size_t membar2_offset = 0x2000;
> > > struct pci_bus *child;
> > > struct pci_dev *dev;
> > > + struct pci_host_bridge *vmd_host_bridge;
> > > int ret;
> > >
> > > /*
> > > @@ -911,8 +911,14 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features)
> > > return -ENODEV;
> > > }
> > >
> > > + vmd_host_bridge = to_pci_host_bridge(vmd->bus->bridge);
> > > +
> > > +#ifdef CONFIG_PCIEASPM
> > > + vmd_host_bridge->aspm_dflt_link_state = PCIE_LINK_STATE_ALL;
> > > +#endif
> >
> > I think it is better to provide an API that accepts the link state. We can
> > provide a stub if CONFIG_PCIEASPM is not selected. This will avoid the ifdef
> > clutter in the callers. Like:
> >
> > void pci_set_default_link_state(struct pci_host_bridge *host_bridge,
> > unsigned int state)
> > {
> > #ifdef CONFIG_PCIEASPM
> > host_bridge->aspm_default_link_state = state;
> > #endif
> > }
> >
> > Or you can stub the entire function to align with other ASPM APIs.
> >
> > One more thought: Since this API is only going to be called by the host bridge
> > drivers, we can place it in drivers/pci/controller/pci-host-common.c and name it
> > as pci_host_common_set_default_link_state().
>
> I agree with the above except for the new function name. I'd call it
> pci_host_set_default_pcie_link_state()
>
> > > +
> > > vmd_copy_host_bridge_flags(pci_find_host_bridge(vmd->dev->bus),
> > > - to_pci_host_bridge(vmd->bus->bridge));
> > > + vmd_host_bridge);
> > >
> > > vmd_attach_resources(vmd);
> > > if (vmd->irq_domain)
> > > diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c
> > > index 29fcb0689a91..6f5b34b172f9 100644
> > > --- a/drivers/pci/pcie/aspm.c
> > > +++ b/drivers/pci/pcie/aspm.c
> > > @@ -380,6 +380,7 @@ static void pcie_clkpm_cap_init(struct pcie_link_state *link, int blacklist)
> > > u16 reg16;
> > > struct pci_dev *child;
> > > struct pci_bus *linkbus = link->pdev->subordinate;
> > > + struct pci_host_bridge *host = pci_find_host_bridge(link->pdev->bus);
> > >
> > > /* All functions should have the same cap and state, take the worst */
> > > list_for_each_entry(child, &linkbus->devices, bus_list) {
> > > @@ -394,7 +395,10 @@ static void pcie_clkpm_cap_init(struct pcie_link_state *link, int blacklist)
> > > enabled = 0;
> > > }
> > > link->clkpm_enabled = enabled;
> > > - link->clkpm_default = enabled;
> > > + if (host && host->aspm_dflt_link_state & PCIE_LINK_STATE_CLKPM)
> > > + link->clkpm_default = 1;
> > > + else
> > > + link->clkpm_default = enabled;
> > > link->clkpm_capable = capable;
> > > link->clkpm_disable = blacklist ? 1 : 0;
> > > }
> > > @@ -794,6 +798,7 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist)
> > > u32 parent_lnkcap, child_lnkcap;
> > > u16 parent_lnkctl, child_lnkctl;
> > > struct pci_bus *linkbus = parent->subordinate;
> > > + struct pci_host_bridge *host;
> > >
> > > if (blacklist) {
> > > /* Set enabled/disable so that we will disable ASPM later */
> > > @@ -866,7 +871,11 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist)
> > > }
> > >
> > > /* Save default state */
> > > - link->aspm_default = link->aspm_enabled;
> > > + host = pci_find_host_bridge(parent->bus);
> >
> > You can initialize 'host' while defining it.
> >
> > Also, please add a comment on why we are doing this. The inline comment for the
> > member is not elaborate enough:
> >
> > /*
> > * Use the default link state provided by the Host Bridge driver if
> > * available. If the BIOS is not able to provide default ASPM link
> > * state for some reason, the Host Bridge driver could do.
> > */
> >
> > > + if (host && host->aspm_dflt_link_state)
> > > + link->aspm_default = host->aspm_dflt_link_state;
> > > + else
> > > + link->aspm_default = link->aspm_enabled;
>
> Or
>
> link->aspm_default = pci_host_get_default_pcie_link_state(parent);
> if (link->aspm_default)
I omitted the ! above by mistake.
> link->aspm_default = link->aspm_enabled;
So it should be
link->aspm_default = pci_host_get_default_pcie_link_state(parent);
if (!link->aspm_default)
link->aspm_default = link->aspm_enabled;
> and make pci_host_get_default_pcie_link_state() return 0 on failures.
>
> Then you can put all of the relevant information into the
> pci_host_get_default_pcie_link_state() kerneldoc comment.
>
> > >
> > > /* Setup initial capable state. Will be updated later */
> > > link->aspm_capable = link->aspm_support;
> > > diff --git a/include/linux/pci.h b/include/linux/pci.h
> > > index 05e68f35f392..930028bf52b4 100644
> > > --- a/include/linux/pci.h
> > > +++ b/include/linux/pci.h
> > > @@ -614,6 +614,10 @@ struct pci_host_bridge {
> > > unsigned int size_windows:1; /* Enable root bus sizing */
> > > unsigned int msi_domain:1; /* Bridge wants MSI domain */
> > >
> > > +#ifdef CONFIG_PCIEASPM
> > > + unsigned int aspm_dflt_link_state; /* Controller provided link state */
> >
> > /* Controller provided default link state */
> >
> >
> > Nit: Please expand 'default' as 'dflt' is not a commonly used acronym for
> > 'default'.
>
> I agree.
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] PCI/ASPM: Allow controller drivers to override default ASPM and CLKPM link state
2025-07-23 11:54 ` Rafael J. Wysocki
2025-07-23 11:56 ` Rafael J. Wysocki
@ 2025-07-23 21:27 ` David Box
2025-07-24 9:58 ` Rafael J. Wysocki
1 sibling, 1 reply; 10+ messages in thread
From: David Box @ 2025-07-23 21:27 UTC (permalink / raw)
To: Rafael J. Wysocki
Cc: Manivannan Sadhasivam, bhelgaas, vicamo.yang, kenny,
ilpo.jarvinen, nirmal.patel, linux-pm, linux-pci, linux-kernel
On Wed, Jul 23, 2025 at 01:54:41PM +0200, Rafael J. Wysocki wrote:
> On Mon, Jul 21, 2025 at 10:24 AM Manivannan Sadhasivam <mani@kernel.org> wrote:
> >
> > On Sun, Jul 20, 2025 at 12:01:37PM GMT, David E. Box wrote:
> > > Synthetic PCIe hierarchies, such as those created by Intel VMD, are not
> > > visible to firmware and do not receive BIOS-provided default ASPM and CLKPM
> > > configuration. As a result, devices behind such domains operate without
> > > proper power management, regardless of platform intent.
> > >
> > > To address this, allow controller drivers to supply an override for the
> > > default link state by setting aspm_dflt_link_state for their associated
> > > pci_host_bridge. During link initialization, if this field is non-zero,
> > > ASPM and CLKPM defaults are derived from its value instead of being taken
> > > from BIOS.
> > >
> > > This mechanism enables drivers like VMD to achieve platform-aligned power
> > > savings by statically defining the expected link configuration at
> > > enumeration time, without relying on runtime calls such as
> > > pci_enable_link_state(), which are ineffective when ASPM is disabled
> > > globally.
> > >
> > > This approach avoids per-controller hacks in ASPM core logic and provides a
> > > general mechanism for domains that require explicit control over link power
> > > state defaults.
> > >
> > > Link: https://lore.kernel.org/linux-pm/0b166ece-eeec-ba5d-2212-50d995611cef@panix.com
> > > Signed-off-by: David E. Box <david.e.box@linux.intel.com>
> > > ---
> > >
> > > Changes from RFC:
> > >
> > > -- Rename field to aspm_dflt_link_state since it stores
> > > PCIE_LINK_STATE_XXX flags, not a policy enum.
> > > -- Move the field to struct pci_host_bridge since it's being applied to
> > > the entire host bridge per Mani's suggestion.
> > > -- During testing noticed that clkpm remained disabled and this was
> > > also handled by the formerly used pci_enable_link_state(). Add a
> > > check in pcie_clkpm_cap_init() as well to enable clkpm during init.
> > >
> > > drivers/pci/controller/vmd.c | 12 +++++++++---
> > > drivers/pci/pcie/aspm.c | 13 +++++++++++--
> > > include/linux/pci.h | 4 ++++
> > > 3 files changed, 24 insertions(+), 5 deletions(-)
> > >
> > > diff --git a/drivers/pci/controller/vmd.c b/drivers/pci/controller/vmd.c
> > > index 8df064b62a2f..6f0de95c87fd 100644
> > > --- a/drivers/pci/controller/vmd.c
> > > +++ b/drivers/pci/controller/vmd.c
> > > @@ -730,7 +730,7 @@ static void vmd_copy_host_bridge_flags(struct pci_host_bridge *root_bridge,
> > > }
> > >
> > > /*
> > > - * Enable ASPM and LTR settings on devices that aren't configured by BIOS.
> > > + * Enable LTR settings on devices that aren't configured by BIOS.
> > > */
> > > static int vmd_pm_enable_quirk(struct pci_dev *pdev, void *userdata)
> > > {
> > > @@ -770,7 +770,6 @@ static int vmd_pm_enable_quirk(struct pci_dev *pdev, void *userdata)
> > > * PCIe r6.0, sec 5.5.4.
> > > */
> > > pci_set_power_state_locked(pdev, PCI_D0);
> >
> > This call becomes useless now.
Missed this. I'll remove it.
> >
> > > - pci_enable_link_state_locked(pdev, PCIE_LINK_STATE_ALL);
> > > return 0;
> > > }
> > >
> > > @@ -785,6 +784,7 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features)
> > > resource_size_t membar2_offset = 0x2000;
> > > struct pci_bus *child;
> > > struct pci_dev *dev;
> > > + struct pci_host_bridge *vmd_host_bridge;
> > > int ret;
> > >
> > > /*
> > > @@ -911,8 +911,14 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features)
> > > return -ENODEV;
> > > }
> > >
> > > + vmd_host_bridge = to_pci_host_bridge(vmd->bus->bridge);
> > > +
> > > +#ifdef CONFIG_PCIEASPM
> > > + vmd_host_bridge->aspm_dflt_link_state = PCIE_LINK_STATE_ALL;
> > > +#endif
> >
> > I think it is better to provide an API that accepts the link state. We can
> > provide a stub if CONFIG_PCIEASPM is not selected. This will avoid the ifdef
> > clutter in the callers. Like:
> >
> > void pci_set_default_link_state(struct pci_host_bridge *host_bridge,
> > unsigned int state)
> > {
> > #ifdef CONFIG_PCIEASPM
> > host_bridge->aspm_default_link_state = state;
> > #endif
> > }
> >
> > Or you can stub the entire function to align with other ASPM APIs.
> >
> > One more thought: Since this API is only going to be called by the host bridge
> > drivers, we can place it in drivers/pci/controller/pci-host-common.c and name it
> > as pci_host_common_set_default_link_state().
This would require VMD to select PCI_HOST_COMMON just to set one field in a
common struct. Seems heavy-handed. Thoughts? Also, with this and dropping the D0
call, I'll split the VMD cleanup into a separate patch again.
>
> I agree with the above except for the new function name. I'd call it
> pci_host_set_default_pcie_link_state()
Sounds good.
>
> > > +
> > > vmd_copy_host_bridge_flags(pci_find_host_bridge(vmd->dev->bus),
> > > - to_pci_host_bridge(vmd->bus->bridge));
> > > + vmd_host_bridge);
> > >
> > > vmd_attach_resources(vmd);
> > > if (vmd->irq_domain)
> > > diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c
> > > index 29fcb0689a91..6f5b34b172f9 100644
> > > --- a/drivers/pci/pcie/aspm.c
> > > +++ b/drivers/pci/pcie/aspm.c
> > > @@ -380,6 +380,7 @@ static void pcie_clkpm_cap_init(struct pcie_link_state *link, int blacklist)
> > > u16 reg16;
> > > struct pci_dev *child;
> > > struct pci_bus *linkbus = link->pdev->subordinate;
> > > + struct pci_host_bridge *host = pci_find_host_bridge(link->pdev->bus);
> > >
> > > /* All functions should have the same cap and state, take the worst */
> > > list_for_each_entry(child, &linkbus->devices, bus_list) {
> > > @@ -394,7 +395,10 @@ static void pcie_clkpm_cap_init(struct pcie_link_state *link, int blacklist)
> > > enabled = 0;
> > > }
> > > link->clkpm_enabled = enabled;
> > > - link->clkpm_default = enabled;
> > > + if (host && host->aspm_dflt_link_state & PCIE_LINK_STATE_CLKPM)
> > > + link->clkpm_default = 1;
> > > + else
> > > + link->clkpm_default = enabled;
> > > link->clkpm_capable = capable;
> > > link->clkpm_disable = blacklist ? 1 : 0;
> > > }
> > > @@ -794,6 +798,7 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist)
> > > u32 parent_lnkcap, child_lnkcap;
> > > u16 parent_lnkctl, child_lnkctl;
> > > struct pci_bus *linkbus = parent->subordinate;
> > > + struct pci_host_bridge *host;
> > >
> > > if (blacklist) {
> > > /* Set enabled/disable so that we will disable ASPM later */
> > > @@ -866,7 +871,11 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist)
> > > }
> > >
> > > /* Save default state */
> > > - link->aspm_default = link->aspm_enabled;
> > > + host = pci_find_host_bridge(parent->bus);
> >
> > You can initialize 'host' while defining it.
> >
> > Also, please add a comment on why we are doing this. The inline comment for the
> > member is not elaborate enough:
> >
> > /*
> > * Use the default link state provided by the Host Bridge driver if
> > * available. If the BIOS is not able to provide default ASPM link
> > * state for some reason, the Host Bridge driver could do.
> > */
> >
> > > + if (host && host->aspm_dflt_link_state)
> > > + link->aspm_default = host->aspm_dflt_link_state;
> > > + else
> > > + link->aspm_default = link->aspm_enabled;
>
> Or
>
> link->aspm_default = pci_host_get_default_pcie_link_state(parent);
> if (link->aspm_default)
> link->aspm_default = link->aspm_enabled;
>
> and make pci_host_get_default_pcie_link_state() return 0 on failures.
>
> Then you can put all of the relevant information into the
> pci_host_get_default_pcie_link_state() kerneldoc comment.
Sure. I'll add get/set APIs (plus the !) and put the comment there.
>
> > >
> > > /* Setup initial capable state. Will be updated later */
> > > link->aspm_capable = link->aspm_support;
> > > diff --git a/include/linux/pci.h b/include/linux/pci.h
> > > index 05e68f35f392..930028bf52b4 100644
> > > --- a/include/linux/pci.h
> > > +++ b/include/linux/pci.h
> > > @@ -614,6 +614,10 @@ struct pci_host_bridge {
> > > unsigned int size_windows:1; /* Enable root bus sizing */
> > > unsigned int msi_domain:1; /* Bridge wants MSI domain */
> > >
> > > +#ifdef CONFIG_PCIEASPM
> > > + unsigned int aspm_dflt_link_state; /* Controller provided link state */
> >
> > /* Controller provided default link state */
> >
> >
> > Nit: Please expand 'default' as 'dflt' is not a commonly used acronym for
> > 'default'.
>
> I agree.
Will do.
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] PCI/ASPM: Allow controller drivers to override default ASPM and CLKPM link state
2025-07-23 21:27 ` David Box
@ 2025-07-24 9:58 ` Rafael J. Wysocki
2025-07-24 16:48 ` Manivannan Sadhasivam
0 siblings, 1 reply; 10+ messages in thread
From: Rafael J. Wysocki @ 2025-07-24 9:58 UTC (permalink / raw)
To: David Box
Cc: Rafael J. Wysocki, Manivannan Sadhasivam, bhelgaas, vicamo.yang,
kenny, ilpo.jarvinen, nirmal.patel, linux-pm, linux-pci,
linux-kernel
On Wed, Jul 23, 2025 at 11:27 PM David Box <david.e.box@linux.intel.com> wrote:
>
> On Wed, Jul 23, 2025 at 01:54:41PM +0200, Rafael J. Wysocki wrote:
> > On Mon, Jul 21, 2025 at 10:24 AM Manivannan Sadhasivam <mani@kernel.org> wrote:
> > >
> > > On Sun, Jul 20, 2025 at 12:01:37PM GMT, David E. Box wrote:
> > > > Synthetic PCIe hierarchies, such as those created by Intel VMD, are not
> > > > visible to firmware and do not receive BIOS-provided default ASPM and CLKPM
> > > > configuration. As a result, devices behind such domains operate without
> > > > proper power management, regardless of platform intent.
> > > >
> > > > To address this, allow controller drivers to supply an override for the
> > > > default link state by setting aspm_dflt_link_state for their associated
> > > > pci_host_bridge. During link initialization, if this field is non-zero,
> > > > ASPM and CLKPM defaults are derived from its value instead of being taken
> > > > from BIOS.
> > > >
> > > > This mechanism enables drivers like VMD to achieve platform-aligned power
> > > > savings by statically defining the expected link configuration at
> > > > enumeration time, without relying on runtime calls such as
> > > > pci_enable_link_state(), which are ineffective when ASPM is disabled
> > > > globally.
> > > >
> > > > This approach avoids per-controller hacks in ASPM core logic and provides a
> > > > general mechanism for domains that require explicit control over link power
> > > > state defaults.
> > > >
> > > > Link: https://lore.kernel.org/linux-pm/0b166ece-eeec-ba5d-2212-50d995611cef@panix.com
> > > > Signed-off-by: David E. Box <david.e.box@linux.intel.com>
> > > > ---
> > > >
> > > > Changes from RFC:
> > > >
> > > > -- Rename field to aspm_dflt_link_state since it stores
> > > > PCIE_LINK_STATE_XXX flags, not a policy enum.
> > > > -- Move the field to struct pci_host_bridge since it's being applied to
> > > > the entire host bridge per Mani's suggestion.
> > > > -- During testing noticed that clkpm remained disabled and this was
> > > > also handled by the formerly used pci_enable_link_state(). Add a
> > > > check in pcie_clkpm_cap_init() as well to enable clkpm during init.
> > > >
> > > > drivers/pci/controller/vmd.c | 12 +++++++++---
> > > > drivers/pci/pcie/aspm.c | 13 +++++++++++--
> > > > include/linux/pci.h | 4 ++++
> > > > 3 files changed, 24 insertions(+), 5 deletions(-)
> > > >
> > > > diff --git a/drivers/pci/controller/vmd.c b/drivers/pci/controller/vmd.c
> > > > index 8df064b62a2f..6f0de95c87fd 100644
> > > > --- a/drivers/pci/controller/vmd.c
> > > > +++ b/drivers/pci/controller/vmd.c
> > > > @@ -730,7 +730,7 @@ static void vmd_copy_host_bridge_flags(struct pci_host_bridge *root_bridge,
> > > > }
> > > >
> > > > /*
> > > > - * Enable ASPM and LTR settings on devices that aren't configured by BIOS.
> > > > + * Enable LTR settings on devices that aren't configured by BIOS.
> > > > */
> > > > static int vmd_pm_enable_quirk(struct pci_dev *pdev, void *userdata)
> > > > {
> > > > @@ -770,7 +770,6 @@ static int vmd_pm_enable_quirk(struct pci_dev *pdev, void *userdata)
> > > > * PCIe r6.0, sec 5.5.4.
> > > > */
> > > > pci_set_power_state_locked(pdev, PCI_D0);
> > >
> > > This call becomes useless now.
>
> Missed this. I'll remove it.
>
> > >
> > > > - pci_enable_link_state_locked(pdev, PCIE_LINK_STATE_ALL);
> > > > return 0;
> > > > }
> > > >
> > > > @@ -785,6 +784,7 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features)
> > > > resource_size_t membar2_offset = 0x2000;
> > > > struct pci_bus *child;
> > > > struct pci_dev *dev;
> > > > + struct pci_host_bridge *vmd_host_bridge;
> > > > int ret;
> > > >
> > > > /*
> > > > @@ -911,8 +911,14 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features)
> > > > return -ENODEV;
> > > > }
> > > >
> > > > + vmd_host_bridge = to_pci_host_bridge(vmd->bus->bridge);
> > > > +
> > > > +#ifdef CONFIG_PCIEASPM
> > > > + vmd_host_bridge->aspm_dflt_link_state = PCIE_LINK_STATE_ALL;
> > > > +#endif
> > >
> > > I think it is better to provide an API that accepts the link state. We can
> > > provide a stub if CONFIG_PCIEASPM is not selected. This will avoid the ifdef
> > > clutter in the callers. Like:
> > >
> > > void pci_set_default_link_state(struct pci_host_bridge *host_bridge,
> > > unsigned int state)
> > > {
> > > #ifdef CONFIG_PCIEASPM
> > > host_bridge->aspm_default_link_state = state;
> > > #endif
> > > }
> > >
> > > Or you can stub the entire function to align with other ASPM APIs.
> > >
> > > One more thought: Since this API is only going to be called by the host bridge
> > > drivers, we can place it in drivers/pci/controller/pci-host-common.c and name it
> > > as pci_host_common_set_default_link_state().
>
> This would require VMD to select PCI_HOST_COMMON just to set one field in a
> common struct. Seems heavy-handed. Thoughts? Also, with this and dropping the D0
> call, I'll split the VMD cleanup into a separate patch again.
So maybe define a __weak pci_host_set_default_pcie_link_state() doing
nothing in the ASPM core and let VMD override it with its own
implementation?
> >
> > I agree with the above except for the new function name. I'd call it
> > pci_host_set_default_pcie_link_state()
>
> Sounds good.
>
> >
> > > > +
> > > > vmd_copy_host_bridge_flags(pci_find_host_bridge(vmd->dev->bus),
> > > > - to_pci_host_bridge(vmd->bus->bridge));
> > > > + vmd_host_bridge);
> > > >
> > > > vmd_attach_resources(vmd);
> > > > if (vmd->irq_domain)
> > > > diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c
> > > > index 29fcb0689a91..6f5b34b172f9 100644
> > > > --- a/drivers/pci/pcie/aspm.c
> > > > +++ b/drivers/pci/pcie/aspm.c
> > > > @@ -380,6 +380,7 @@ static void pcie_clkpm_cap_init(struct pcie_link_state *link, int blacklist)
> > > > u16 reg16;
> > > > struct pci_dev *child;
> > > > struct pci_bus *linkbus = link->pdev->subordinate;
> > > > + struct pci_host_bridge *host = pci_find_host_bridge(link->pdev->bus);
> > > >
> > > > /* All functions should have the same cap and state, take the worst */
> > > > list_for_each_entry(child, &linkbus->devices, bus_list) {
> > > > @@ -394,7 +395,10 @@ static void pcie_clkpm_cap_init(struct pcie_link_state *link, int blacklist)
> > > > enabled = 0;
> > > > }
> > > > link->clkpm_enabled = enabled;
> > > > - link->clkpm_default = enabled;
> > > > + if (host && host->aspm_dflt_link_state & PCIE_LINK_STATE_CLKPM)
> > > > + link->clkpm_default = 1;
> > > > + else
> > > > + link->clkpm_default = enabled;
> > > > link->clkpm_capable = capable;
> > > > link->clkpm_disable = blacklist ? 1 : 0;
> > > > }
> > > > @@ -794,6 +798,7 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist)
> > > > u32 parent_lnkcap, child_lnkcap;
> > > > u16 parent_lnkctl, child_lnkctl;
> > > > struct pci_bus *linkbus = parent->subordinate;
> > > > + struct pci_host_bridge *host;
> > > >
> > > > if (blacklist) {
> > > > /* Set enabled/disable so that we will disable ASPM later */
> > > > @@ -866,7 +871,11 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist)
> > > > }
> > > >
> > > > /* Save default state */
> > > > - link->aspm_default = link->aspm_enabled;
> > > > + host = pci_find_host_bridge(parent->bus);
> > >
> > > You can initialize 'host' while defining it.
> > >
> > > Also, please add a comment on why we are doing this. The inline comment for the
> > > member is not elaborate enough:
> > >
> > > /*
> > > * Use the default link state provided by the Host Bridge driver if
> > > * available. If the BIOS is not able to provide default ASPM link
> > > * state for some reason, the Host Bridge driver could do.
> > > */
> > >
> > > > + if (host && host->aspm_dflt_link_state)
> > > > + link->aspm_default = host->aspm_dflt_link_state;
> > > > + else
> > > > + link->aspm_default = link->aspm_enabled;
> >
> > Or
> >
> > link->aspm_default = pci_host_get_default_pcie_link_state(parent);
> > if (link->aspm_default)
> > link->aspm_default = link->aspm_enabled;
> >
> > and make pci_host_get_default_pcie_link_state() return 0 on failures.
> >
> > Then you can put all of the relevant information into the
> > pci_host_get_default_pcie_link_state() kerneldoc comment.
>
> Sure. I'll add get/set APIs (plus the !) and put the comment there.
Sounds good!
> > > >
> > > > /* Setup initial capable state. Will be updated later */
> > > > link->aspm_capable = link->aspm_support;
> > > > diff --git a/include/linux/pci.h b/include/linux/pci.h
> > > > index 05e68f35f392..930028bf52b4 100644
> > > > --- a/include/linux/pci.h
> > > > +++ b/include/linux/pci.h
> > > > @@ -614,6 +614,10 @@ struct pci_host_bridge {
> > > > unsigned int size_windows:1; /* Enable root bus sizing */
> > > > unsigned int msi_domain:1; /* Bridge wants MSI domain */
> > > >
> > > > +#ifdef CONFIG_PCIEASPM
> > > > + unsigned int aspm_dflt_link_state; /* Controller provided link state */
> > >
> > > /* Controller provided default link state */
> > >
> > >
> > > Nit: Please expand 'default' as 'dflt' is not a commonly used acronym for
> > > 'default'.
> >
> > I agree.
>
> Will do.
Thanks!
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] PCI/ASPM: Allow controller drivers to override default ASPM and CLKPM link state
2025-07-24 9:58 ` Rafael J. Wysocki
@ 2025-07-24 16:48 ` Manivannan Sadhasivam
2025-07-24 19:08 ` David Box
0 siblings, 1 reply; 10+ messages in thread
From: Manivannan Sadhasivam @ 2025-07-24 16:48 UTC (permalink / raw)
To: Rafael J. Wysocki
Cc: David Box, bhelgaas, vicamo.yang, kenny, ilpo.jarvinen,
nirmal.patel, linux-pm, linux-pci, linux-kernel
On Thu, Jul 24, 2025 at 11:58:40AM GMT, Rafael J. Wysocki wrote:
> On Wed, Jul 23, 2025 at 11:27 PM David Box <david.e.box@linux.intel.com> wrote:
> >
> > On Wed, Jul 23, 2025 at 01:54:41PM +0200, Rafael J. Wysocki wrote:
> > > On Mon, Jul 21, 2025 at 10:24 AM Manivannan Sadhasivam <mani@kernel.org> wrote:
> > > >
> > > > On Sun, Jul 20, 2025 at 12:01:37PM GMT, David E. Box wrote:
> > > > > Synthetic PCIe hierarchies, such as those created by Intel VMD, are not
> > > > > visible to firmware and do not receive BIOS-provided default ASPM and CLKPM
> > > > > configuration. As a result, devices behind such domains operate without
> > > > > proper power management, regardless of platform intent.
> > > > >
> > > > > To address this, allow controller drivers to supply an override for the
> > > > > default link state by setting aspm_dflt_link_state for their associated
> > > > > pci_host_bridge. During link initialization, if this field is non-zero,
> > > > > ASPM and CLKPM defaults are derived from its value instead of being taken
> > > > > from BIOS.
> > > > >
> > > > > This mechanism enables drivers like VMD to achieve platform-aligned power
> > > > > savings by statically defining the expected link configuration at
> > > > > enumeration time, without relying on runtime calls such as
> > > > > pci_enable_link_state(), which are ineffective when ASPM is disabled
> > > > > globally.
> > > > >
> > > > > This approach avoids per-controller hacks in ASPM core logic and provides a
> > > > > general mechanism for domains that require explicit control over link power
> > > > > state defaults.
> > > > >
> > > > > Link: https://lore.kernel.org/linux-pm/0b166ece-eeec-ba5d-2212-50d995611cef@panix.com
> > > > > Signed-off-by: David E. Box <david.e.box@linux.intel.com>
> > > > > ---
> > > > >
> > > > > Changes from RFC:
> > > > >
> > > > > -- Rename field to aspm_dflt_link_state since it stores
> > > > > PCIE_LINK_STATE_XXX flags, not a policy enum.
> > > > > -- Move the field to struct pci_host_bridge since it's being applied to
> > > > > the entire host bridge per Mani's suggestion.
> > > > > -- During testing noticed that clkpm remained disabled and this was
> > > > > also handled by the formerly used pci_enable_link_state(). Add a
> > > > > check in pcie_clkpm_cap_init() as well to enable clkpm during init.
> > > > >
> > > > > drivers/pci/controller/vmd.c | 12 +++++++++---
> > > > > drivers/pci/pcie/aspm.c | 13 +++++++++++--
> > > > > include/linux/pci.h | 4 ++++
> > > > > 3 files changed, 24 insertions(+), 5 deletions(-)
> > > > >
> > > > > diff --git a/drivers/pci/controller/vmd.c b/drivers/pci/controller/vmd.c
> > > > > index 8df064b62a2f..6f0de95c87fd 100644
> > > > > --- a/drivers/pci/controller/vmd.c
> > > > > +++ b/drivers/pci/controller/vmd.c
> > > > > @@ -730,7 +730,7 @@ static void vmd_copy_host_bridge_flags(struct pci_host_bridge *root_bridge,
> > > > > }
> > > > >
> > > > > /*
> > > > > - * Enable ASPM and LTR settings on devices that aren't configured by BIOS.
> > > > > + * Enable LTR settings on devices that aren't configured by BIOS.
> > > > > */
> > > > > static int vmd_pm_enable_quirk(struct pci_dev *pdev, void *userdata)
> > > > > {
> > > > > @@ -770,7 +770,6 @@ static int vmd_pm_enable_quirk(struct pci_dev *pdev, void *userdata)
> > > > > * PCIe r6.0, sec 5.5.4.
> > > > > */
> > > > > pci_set_power_state_locked(pdev, PCI_D0);
> > > >
> > > > This call becomes useless now.
> >
> > Missed this. I'll remove it.
> >
> > > >
> > > > > - pci_enable_link_state_locked(pdev, PCIE_LINK_STATE_ALL);
> > > > > return 0;
> > > > > }
> > > > >
> > > > > @@ -785,6 +784,7 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features)
> > > > > resource_size_t membar2_offset = 0x2000;
> > > > > struct pci_bus *child;
> > > > > struct pci_dev *dev;
> > > > > + struct pci_host_bridge *vmd_host_bridge;
> > > > > int ret;
> > > > >
> > > > > /*
> > > > > @@ -911,8 +911,14 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features)
> > > > > return -ENODEV;
> > > > > }
> > > > >
> > > > > + vmd_host_bridge = to_pci_host_bridge(vmd->bus->bridge);
> > > > > +
> > > > > +#ifdef CONFIG_PCIEASPM
> > > > > + vmd_host_bridge->aspm_dflt_link_state = PCIE_LINK_STATE_ALL;
> > > > > +#endif
> > > >
> > > > I think it is better to provide an API that accepts the link state. We can
> > > > provide a stub if CONFIG_PCIEASPM is not selected. This will avoid the ifdef
> > > > clutter in the callers. Like:
> > > >
> > > > void pci_set_default_link_state(struct pci_host_bridge *host_bridge,
> > > > unsigned int state)
> > > > {
> > > > #ifdef CONFIG_PCIEASPM
> > > > host_bridge->aspm_default_link_state = state;
> > > > #endif
> > > > }
> > > >
> > > > Or you can stub the entire function to align with other ASPM APIs.
> > > >
> > > > One more thought: Since this API is only going to be called by the host bridge
> > > > drivers, we can place it in drivers/pci/controller/pci-host-common.c and name it
> > > > as pci_host_common_set_default_link_state().
> >
> > This would require VMD to select PCI_HOST_COMMON just to set one field in a
> > common struct. Seems heavy-handed. Thoughts? Also, with this and dropping the D0
> > call, I'll split the VMD cleanup into a separate patch again.
>
> So maybe define a __weak pci_host_set_default_pcie_link_state() doing
> nothing in the ASPM core and let VMD override it with its own
> implementation?
>
No. There are other controller drivers (like pcie-qcom) going to use this API.
So please move it to the pci-host-common library as it should be.
> > >
> > > I agree with the above except for the new function name. I'd call it
> > > pci_host_set_default_pcie_link_state()
> >
Ok, looks good to me.
- Mani
--
மணிவண்ணன் சதாசிவம்
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] PCI/ASPM: Allow controller drivers to override default ASPM and CLKPM link state
2025-07-24 16:48 ` Manivannan Sadhasivam
@ 2025-07-24 19:08 ` David Box
2025-07-24 19:12 ` Rafael J. Wysocki
2025-08-10 11:06 ` Manivannan Sadhasivam
0 siblings, 2 replies; 10+ messages in thread
From: David Box @ 2025-07-24 19:08 UTC (permalink / raw)
To: Manivannan Sadhasivam
Cc: Rafael J. Wysocki, bhelgaas, vicamo.yang, kenny, ilpo.jarvinen,
nirmal.patel, linux-pm, linux-pci, linux-kernel
On Thu, Jul 24, 2025 at 10:18:47PM +0530, Manivannan Sadhasivam wrote:
> On Thu, Jul 24, 2025 at 11:58:40AM GMT, Rafael J. Wysocki wrote:
> > On Wed, Jul 23, 2025 at 11:27 PM David Box <david.e.box@linux.intel.com> wrote:
> > >
> > > On Wed, Jul 23, 2025 at 01:54:41PM +0200, Rafael J. Wysocki wrote:
> > > > On Mon, Jul 21, 2025 at 10:24 AM Manivannan Sadhasivam <mani@kernel.org> wrote:
> > > > >
> > > > > On Sun, Jul 20, 2025 at 12:01:37PM GMT, David E. Box wrote:
> > > > > > Synthetic PCIe hierarchies, such as those created by Intel VMD, are not
> > > > > > visible to firmware and do not receive BIOS-provided default ASPM and CLKPM
> > > > > > configuration. As a result, devices behind such domains operate without
> > > > > > proper power management, regardless of platform intent.
> > > > > >
> > > > > > To address this, allow controller drivers to supply an override for the
> > > > > > default link state by setting aspm_dflt_link_state for their associated
> > > > > > pci_host_bridge. During link initialization, if this field is non-zero,
> > > > > > ASPM and CLKPM defaults are derived from its value instead of being taken
> > > > > > from BIOS.
> > > > > >
> > > > > > This mechanism enables drivers like VMD to achieve platform-aligned power
> > > > > > savings by statically defining the expected link configuration at
> > > > > > enumeration time, without relying on runtime calls such as
> > > > > > pci_enable_link_state(), which are ineffective when ASPM is disabled
> > > > > > globally.
> > > > > >
> > > > > > This approach avoids per-controller hacks in ASPM core logic and provides a
> > > > > > general mechanism for domains that require explicit control over link power
> > > > > > state defaults.
> > > > > >
> > > > > > Link: https://lore.kernel.org/linux-pm/0b166ece-eeec-ba5d-2212-50d995611cef@panix.com
> > > > > > Signed-off-by: David E. Box <david.e.box@linux.intel.com>
> > > > > > ---
> > > > > >
> > > > > > Changes from RFC:
> > > > > >
> > > > > > -- Rename field to aspm_dflt_link_state since it stores
> > > > > > PCIE_LINK_STATE_XXX flags, not a policy enum.
> > > > > > -- Move the field to struct pci_host_bridge since it's being applied to
> > > > > > the entire host bridge per Mani's suggestion.
> > > > > > -- During testing noticed that clkpm remained disabled and this was
> > > > > > also handled by the formerly used pci_enable_link_state(). Add a
> > > > > > check in pcie_clkpm_cap_init() as well to enable clkpm during init.
> > > > > >
> > > > > > drivers/pci/controller/vmd.c | 12 +++++++++---
> > > > > > drivers/pci/pcie/aspm.c | 13 +++++++++++--
> > > > > > include/linux/pci.h | 4 ++++
> > > > > > 3 files changed, 24 insertions(+), 5 deletions(-)
> > > > > >
> > > > > > diff --git a/drivers/pci/controller/vmd.c b/drivers/pci/controller/vmd.c
> > > > > > index 8df064b62a2f..6f0de95c87fd 100644
> > > > > > --- a/drivers/pci/controller/vmd.c
> > > > > > +++ b/drivers/pci/controller/vmd.c
> > > > > > @@ -730,7 +730,7 @@ static void vmd_copy_host_bridge_flags(struct pci_host_bridge *root_bridge,
> > > > > > }
> > > > > >
> > > > > > /*
> > > > > > - * Enable ASPM and LTR settings on devices that aren't configured by BIOS.
> > > > > > + * Enable LTR settings on devices that aren't configured by BIOS.
> > > > > > */
> > > > > > static int vmd_pm_enable_quirk(struct pci_dev *pdev, void *userdata)
> > > > > > {
> > > > > > @@ -770,7 +770,6 @@ static int vmd_pm_enable_quirk(struct pci_dev *pdev, void *userdata)
> > > > > > * PCIe r6.0, sec 5.5.4.
> > > > > > */
> > > > > > pci_set_power_state_locked(pdev, PCI_D0);
> > > > >
> > > > > This call becomes useless now.
> > >
> > > Missed this. I'll remove it.
> > >
> > > > >
> > > > > > - pci_enable_link_state_locked(pdev, PCIE_LINK_STATE_ALL);
> > > > > > return 0;
> > > > > > }
> > > > > >
> > > > > > @@ -785,6 +784,7 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features)
> > > > > > resource_size_t membar2_offset = 0x2000;
> > > > > > struct pci_bus *child;
> > > > > > struct pci_dev *dev;
> > > > > > + struct pci_host_bridge *vmd_host_bridge;
> > > > > > int ret;
> > > > > >
> > > > > > /*
> > > > > > @@ -911,8 +911,14 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features)
> > > > > > return -ENODEV;
> > > > > > }
> > > > > >
> > > > > > + vmd_host_bridge = to_pci_host_bridge(vmd->bus->bridge);
> > > > > > +
> > > > > > +#ifdef CONFIG_PCIEASPM
> > > > > > + vmd_host_bridge->aspm_dflt_link_state = PCIE_LINK_STATE_ALL;
> > > > > > +#endif
> > > > >
> > > > > I think it is better to provide an API that accepts the link state. We can
> > > > > provide a stub if CONFIG_PCIEASPM is not selected. This will avoid the ifdef
> > > > > clutter in the callers. Like:
> > > > >
> > > > > void pci_set_default_link_state(struct pci_host_bridge *host_bridge,
> > > > > unsigned int state)
> > > > > {
> > > > > #ifdef CONFIG_PCIEASPM
> > > > > host_bridge->aspm_default_link_state = state;
> > > > > #endif
> > > > > }
> > > > >
> > > > > Or you can stub the entire function to align with other ASPM APIs.
> > > > >
> > > > > One more thought: Since this API is only going to be called by the host bridge
> > > > > drivers, we can place it in drivers/pci/controller/pci-host-common.c and name it
> > > > > as pci_host_common_set_default_link_state().
> > >
> > > This would require VMD to select PCI_HOST_COMMON just to set one field in a
> > > common struct. Seems heavy-handed. Thoughts? Also, with this and dropping the D0
> > > call, I'll split the VMD cleanup into a separate patch again.
> >
> > So maybe define a __weak pci_host_set_default_pcie_link_state() doing
> > nothing in the ASPM core and let VMD override it with its own
> > implementation?
> >
>
> No. There are other controller drivers (like pcie-qcom) going to use this API.
> So please move it to the pci-host-common library as it should be.
I was going to suggest that it could simply stay in aspm.c.
pci_enable_link_state_() is there and currently only used by controllers as
well.
David
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] PCI/ASPM: Allow controller drivers to override default ASPM and CLKPM link state
2025-07-24 19:08 ` David Box
@ 2025-07-24 19:12 ` Rafael J. Wysocki
2025-08-10 11:06 ` Manivannan Sadhasivam
1 sibling, 0 replies; 10+ messages in thread
From: Rafael J. Wysocki @ 2025-07-24 19:12 UTC (permalink / raw)
To: David Box
Cc: Manivannan Sadhasivam, Rafael J. Wysocki, bhelgaas, vicamo.yang,
kenny, ilpo.jarvinen, nirmal.patel, linux-pm, linux-pci,
linux-kernel
On Thu, Jul 24, 2025 at 9:08 PM David Box <david.e.box@linux.intel.com> wrote:
>
> On Thu, Jul 24, 2025 at 10:18:47PM +0530, Manivannan Sadhasivam wrote:
> > On Thu, Jul 24, 2025 at 11:58:40AM GMT, Rafael J. Wysocki wrote:
> > > On Wed, Jul 23, 2025 at 11:27 PM David Box <david.e.box@linux.intel.com> wrote:
> > > >
> > > > On Wed, Jul 23, 2025 at 01:54:41PM +0200, Rafael J. Wysocki wrote:
> > > > > On Mon, Jul 21, 2025 at 10:24 AM Manivannan Sadhasivam <mani@kernel.org> wrote:
> > > > > >
> > > > > > On Sun, Jul 20, 2025 at 12:01:37PM GMT, David E. Box wrote:
> > > > > > > Synthetic PCIe hierarchies, such as those created by Intel VMD, are not
> > > > > > > visible to firmware and do not receive BIOS-provided default ASPM and CLKPM
> > > > > > > configuration. As a result, devices behind such domains operate without
> > > > > > > proper power management, regardless of platform intent.
> > > > > > >
> > > > > > > To address this, allow controller drivers to supply an override for the
> > > > > > > default link state by setting aspm_dflt_link_state for their associated
> > > > > > > pci_host_bridge. During link initialization, if this field is non-zero,
> > > > > > > ASPM and CLKPM defaults are derived from its value instead of being taken
> > > > > > > from BIOS.
> > > > > > >
> > > > > > > This mechanism enables drivers like VMD to achieve platform-aligned power
> > > > > > > savings by statically defining the expected link configuration at
> > > > > > > enumeration time, without relying on runtime calls such as
> > > > > > > pci_enable_link_state(), which are ineffective when ASPM is disabled
> > > > > > > globally.
> > > > > > >
> > > > > > > This approach avoids per-controller hacks in ASPM core logic and provides a
> > > > > > > general mechanism for domains that require explicit control over link power
> > > > > > > state defaults.
> > > > > > >
> > > > > > > Link: https://lore.kernel.org/linux-pm/0b166ece-eeec-ba5d-2212-50d995611cef@panix.com
> > > > > > > Signed-off-by: David E. Box <david.e.box@linux.intel.com>
> > > > > > > ---
> > > > > > >
> > > > > > > Changes from RFC:
> > > > > > >
> > > > > > > -- Rename field to aspm_dflt_link_state since it stores
> > > > > > > PCIE_LINK_STATE_XXX flags, not a policy enum.
> > > > > > > -- Move the field to struct pci_host_bridge since it's being applied to
> > > > > > > the entire host bridge per Mani's suggestion.
> > > > > > > -- During testing noticed that clkpm remained disabled and this was
> > > > > > > also handled by the formerly used pci_enable_link_state(). Add a
> > > > > > > check in pcie_clkpm_cap_init() as well to enable clkpm during init.
> > > > > > >
> > > > > > > drivers/pci/controller/vmd.c | 12 +++++++++---
> > > > > > > drivers/pci/pcie/aspm.c | 13 +++++++++++--
> > > > > > > include/linux/pci.h | 4 ++++
> > > > > > > 3 files changed, 24 insertions(+), 5 deletions(-)
> > > > > > >
> > > > > > > diff --git a/drivers/pci/controller/vmd.c b/drivers/pci/controller/vmd.c
> > > > > > > index 8df064b62a2f..6f0de95c87fd 100644
> > > > > > > --- a/drivers/pci/controller/vmd.c
> > > > > > > +++ b/drivers/pci/controller/vmd.c
> > > > > > > @@ -730,7 +730,7 @@ static void vmd_copy_host_bridge_flags(struct pci_host_bridge *root_bridge,
> > > > > > > }
> > > > > > >
> > > > > > > /*
> > > > > > > - * Enable ASPM and LTR settings on devices that aren't configured by BIOS.
> > > > > > > + * Enable LTR settings on devices that aren't configured by BIOS.
> > > > > > > */
> > > > > > > static int vmd_pm_enable_quirk(struct pci_dev *pdev, void *userdata)
> > > > > > > {
> > > > > > > @@ -770,7 +770,6 @@ static int vmd_pm_enable_quirk(struct pci_dev *pdev, void *userdata)
> > > > > > > * PCIe r6.0, sec 5.5.4.
> > > > > > > */
> > > > > > > pci_set_power_state_locked(pdev, PCI_D0);
> > > > > >
> > > > > > This call becomes useless now.
> > > >
> > > > Missed this. I'll remove it.
> > > >
> > > > > >
> > > > > > > - pci_enable_link_state_locked(pdev, PCIE_LINK_STATE_ALL);
> > > > > > > return 0;
> > > > > > > }
> > > > > > >
> > > > > > > @@ -785,6 +784,7 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features)
> > > > > > > resource_size_t membar2_offset = 0x2000;
> > > > > > > struct pci_bus *child;
> > > > > > > struct pci_dev *dev;
> > > > > > > + struct pci_host_bridge *vmd_host_bridge;
> > > > > > > int ret;
> > > > > > >
> > > > > > > /*
> > > > > > > @@ -911,8 +911,14 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features)
> > > > > > > return -ENODEV;
> > > > > > > }
> > > > > > >
> > > > > > > + vmd_host_bridge = to_pci_host_bridge(vmd->bus->bridge);
> > > > > > > +
> > > > > > > +#ifdef CONFIG_PCIEASPM
> > > > > > > + vmd_host_bridge->aspm_dflt_link_state = PCIE_LINK_STATE_ALL;
> > > > > > > +#endif
> > > > > >
> > > > > > I think it is better to provide an API that accepts the link state. We can
> > > > > > provide a stub if CONFIG_PCIEASPM is not selected. This will avoid the ifdef
> > > > > > clutter in the callers. Like:
> > > > > >
> > > > > > void pci_set_default_link_state(struct pci_host_bridge *host_bridge,
> > > > > > unsigned int state)
> > > > > > {
> > > > > > #ifdef CONFIG_PCIEASPM
> > > > > > host_bridge->aspm_default_link_state = state;
> > > > > > #endif
> > > > > > }
> > > > > >
> > > > > > Or you can stub the entire function to align with other ASPM APIs.
> > > > > >
> > > > > > One more thought: Since this API is only going to be called by the host bridge
> > > > > > drivers, we can place it in drivers/pci/controller/pci-host-common.c and name it
> > > > > > as pci_host_common_set_default_link_state().
> > > >
> > > > This would require VMD to select PCI_HOST_COMMON just to set one field in a
> > > > common struct. Seems heavy-handed. Thoughts? Also, with this and dropping the D0
> > > > call, I'll split the VMD cleanup into a separate patch again.
> > >
> > > So maybe define a __weak pci_host_set_default_pcie_link_state() doing
> > > nothing in the ASPM core and let VMD override it with its own
> > > implementation?
> > >
> >
> > No. There are other controller drivers (like pcie-qcom) going to use this API.
> > So please move it to the pci-host-common library as it should be.
>
> I was going to suggest that it could simply stay in aspm.c.
> pci_enable_link_state_() is there and currently only used by controllers as
> well.
This sounds reasonable to me.
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] PCI/ASPM: Allow controller drivers to override default ASPM and CLKPM link state
2025-07-24 19:08 ` David Box
2025-07-24 19:12 ` Rafael J. Wysocki
@ 2025-08-10 11:06 ` Manivannan Sadhasivam
1 sibling, 0 replies; 10+ messages in thread
From: Manivannan Sadhasivam @ 2025-08-10 11:06 UTC (permalink / raw)
To: David Box
Cc: Rafael J. Wysocki, bhelgaas, vicamo.yang, kenny, ilpo.jarvinen,
nirmal.patel, linux-pm, linux-pci, linux-kernel
On Thu, Jul 24, 2025 at 12:08:03PM GMT, David Box wrote:
> On Thu, Jul 24, 2025 at 10:18:47PM +0530, Manivannan Sadhasivam wrote:
> > On Thu, Jul 24, 2025 at 11:58:40AM GMT, Rafael J. Wysocki wrote:
> > > On Wed, Jul 23, 2025 at 11:27 PM David Box <david.e.box@linux.intel.com> wrote:
> > > >
> > > > On Wed, Jul 23, 2025 at 01:54:41PM +0200, Rafael J. Wysocki wrote:
> > > > > On Mon, Jul 21, 2025 at 10:24 AM Manivannan Sadhasivam <mani@kernel.org> wrote:
> > > > > >
> > > > > > On Sun, Jul 20, 2025 at 12:01:37PM GMT, David E. Box wrote:
> > > > > > > Synthetic PCIe hierarchies, such as those created by Intel VMD, are not
> > > > > > > visible to firmware and do not receive BIOS-provided default ASPM and CLKPM
> > > > > > > configuration. As a result, devices behind such domains operate without
> > > > > > > proper power management, regardless of platform intent.
> > > > > > >
> > > > > > > To address this, allow controller drivers to supply an override for the
> > > > > > > default link state by setting aspm_dflt_link_state for their associated
> > > > > > > pci_host_bridge. During link initialization, if this field is non-zero,
> > > > > > > ASPM and CLKPM defaults are derived from its value instead of being taken
> > > > > > > from BIOS.
> > > > > > >
> > > > > > > This mechanism enables drivers like VMD to achieve platform-aligned power
> > > > > > > savings by statically defining the expected link configuration at
> > > > > > > enumeration time, without relying on runtime calls such as
> > > > > > > pci_enable_link_state(), which are ineffective when ASPM is disabled
> > > > > > > globally.
> > > > > > >
> > > > > > > This approach avoids per-controller hacks in ASPM core logic and provides a
> > > > > > > general mechanism for domains that require explicit control over link power
> > > > > > > state defaults.
> > > > > > >
> > > > > > > Link: https://lore.kernel.org/linux-pm/0b166ece-eeec-ba5d-2212-50d995611cef@panix.com
> > > > > > > Signed-off-by: David E. Box <david.e.box@linux.intel.com>
> > > > > > > ---
> > > > > > >
> > > > > > > Changes from RFC:
> > > > > > >
> > > > > > > -- Rename field to aspm_dflt_link_state since it stores
> > > > > > > PCIE_LINK_STATE_XXX flags, not a policy enum.
> > > > > > > -- Move the field to struct pci_host_bridge since it's being applied to
> > > > > > > the entire host bridge per Mani's suggestion.
> > > > > > > -- During testing noticed that clkpm remained disabled and this was
> > > > > > > also handled by the formerly used pci_enable_link_state(). Add a
> > > > > > > check in pcie_clkpm_cap_init() as well to enable clkpm during init.
> > > > > > >
> > > > > > > drivers/pci/controller/vmd.c | 12 +++++++++---
> > > > > > > drivers/pci/pcie/aspm.c | 13 +++++++++++--
> > > > > > > include/linux/pci.h | 4 ++++
> > > > > > > 3 files changed, 24 insertions(+), 5 deletions(-)
> > > > > > >
> > > > > > > diff --git a/drivers/pci/controller/vmd.c b/drivers/pci/controller/vmd.c
> > > > > > > index 8df064b62a2f..6f0de95c87fd 100644
> > > > > > > --- a/drivers/pci/controller/vmd.c
> > > > > > > +++ b/drivers/pci/controller/vmd.c
> > > > > > > @@ -730,7 +730,7 @@ static void vmd_copy_host_bridge_flags(struct pci_host_bridge *root_bridge,
> > > > > > > }
> > > > > > >
> > > > > > > /*
> > > > > > > - * Enable ASPM and LTR settings on devices that aren't configured by BIOS.
> > > > > > > + * Enable LTR settings on devices that aren't configured by BIOS.
> > > > > > > */
> > > > > > > static int vmd_pm_enable_quirk(struct pci_dev *pdev, void *userdata)
> > > > > > > {
> > > > > > > @@ -770,7 +770,6 @@ static int vmd_pm_enable_quirk(struct pci_dev *pdev, void *userdata)
> > > > > > > * PCIe r6.0, sec 5.5.4.
> > > > > > > */
> > > > > > > pci_set_power_state_locked(pdev, PCI_D0);
> > > > > >
> > > > > > This call becomes useless now.
> > > >
> > > > Missed this. I'll remove it.
> > > >
> > > > > >
> > > > > > > - pci_enable_link_state_locked(pdev, PCIE_LINK_STATE_ALL);
> > > > > > > return 0;
> > > > > > > }
> > > > > > >
> > > > > > > @@ -785,6 +784,7 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features)
> > > > > > > resource_size_t membar2_offset = 0x2000;
> > > > > > > struct pci_bus *child;
> > > > > > > struct pci_dev *dev;
> > > > > > > + struct pci_host_bridge *vmd_host_bridge;
> > > > > > > int ret;
> > > > > > >
> > > > > > > /*
> > > > > > > @@ -911,8 +911,14 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features)
> > > > > > > return -ENODEV;
> > > > > > > }
> > > > > > >
> > > > > > > + vmd_host_bridge = to_pci_host_bridge(vmd->bus->bridge);
> > > > > > > +
> > > > > > > +#ifdef CONFIG_PCIEASPM
> > > > > > > + vmd_host_bridge->aspm_dflt_link_state = PCIE_LINK_STATE_ALL;
> > > > > > > +#endif
> > > > > >
> > > > > > I think it is better to provide an API that accepts the link state. We can
> > > > > > provide a stub if CONFIG_PCIEASPM is not selected. This will avoid the ifdef
> > > > > > clutter in the callers. Like:
> > > > > >
> > > > > > void pci_set_default_link_state(struct pci_host_bridge *host_bridge,
> > > > > > unsigned int state)
> > > > > > {
> > > > > > #ifdef CONFIG_PCIEASPM
> > > > > > host_bridge->aspm_default_link_state = state;
> > > > > > #endif
> > > > > > }
> > > > > >
> > > > > > Or you can stub the entire function to align with other ASPM APIs.
> > > > > >
> > > > > > One more thought: Since this API is only going to be called by the host bridge
> > > > > > drivers, we can place it in drivers/pci/controller/pci-host-common.c and name it
> > > > > > as pci_host_common_set_default_link_state().
> > > >
> > > > This would require VMD to select PCI_HOST_COMMON just to set one field in a
> > > > common struct. Seems heavy-handed. Thoughts? Also, with this and dropping the D0
> > > > call, I'll split the VMD cleanup into a separate patch again.
> > >
> > > So maybe define a __weak pci_host_set_default_pcie_link_state() doing
> > > nothing in the ASPM core and let VMD override it with its own
> > > implementation?
> > >
> >
> > No. There are other controller drivers (like pcie-qcom) going to use this API.
> > So please move it to the pci-host-common library as it should be.
>
> I was going to suggest that it could simply stay in aspm.c.
> pci_enable_link_state_() is there and currently only used by controllers as
> well.
>
OK!
- Mani
--
மணிவண்ணன் சதாசிவம்
^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2025-08-10 11:06 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-07-20 19:01 [PATCH] PCI/ASPM: Allow controller drivers to override default ASPM and CLKPM link state David E. Box
2025-07-21 8:23 ` Manivannan Sadhasivam
2025-07-23 11:54 ` Rafael J. Wysocki
2025-07-23 11:56 ` Rafael J. Wysocki
2025-07-23 21:27 ` David Box
2025-07-24 9:58 ` Rafael J. Wysocki
2025-07-24 16:48 ` Manivannan Sadhasivam
2025-07-24 19:08 ` David Box
2025-07-24 19:12 ` Rafael J. Wysocki
2025-08-10 11:06 ` Manivannan Sadhasivam
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).