From: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>
To: "Rafael J. Wysocki" <rafael@kernel.org>, Lukas Wunner <lukas@wunner.de>
Cc: Bjorn Helgaas <bhelgaas@google.com>,
linux-pci@vger.kernel.org, linux-kernel@vger.kernel.org,
linux-pm@vger.kernel.org
Subject: Re: [PATCH v2] PCI: pciehp: Fix hotplug on Catlow Lake with unreliable PME status
Date: Mon, 9 Mar 2026 11:04:09 -0700 [thread overview]
Message-ID: <19dc5e60-2740-458f-9e0a-90d3982316f4@linux.intel.com> (raw)
In-Reply-To: <09db5a83-980c-432e-adbc-2582680c8cf9@linux.intel.com>
Hi Lukas/Rafael,
On 2/19/2026 1:54 PM, Kuppuswamy Sathyanarayanan wrote:
> Hi,
>
> On 2/19/2026 3:09 AM, Rafael J. Wysocki wrote:
>> On Thu, Feb 19, 2026 at 9:04 AM Lukas Wunner <lukas@wunner.de> wrote:
>>>
>>> On Wed, Feb 18, 2026 at 06:33:15PM +0100, Rafael J. Wysocki wrote:
>>>> First, keeping the ports in D0 may gate runtime PC10. Does it not?
>>>
>>> The Root Port in question is on the PCH. I'm not sure, does keeping a
>>> PCH Root Port in D0 also prevent PC10 entry or is that only the case
>>> for Root Ports on the CPU die/tile?
>>
>> If it is located in the PCH, it should not gate PC10 if in D0 at least
>> in theory, but it would be good to verify that.
>>
>> Of course, it will still gate S0ix entry through runtime idle, but
>> that's a bit moot if the platform is unable to enter S0ix through
>> runtime idle anyway for other reasons (which is quite likely), or if
>> the power difference between S0ix and PC10 is small.
>
> I will gather current PC10 and S0ix numbers. If there is a significant
> difference between the two power savings, I will implement the
> pme_is_broken() approach.
I have gathered the test results. S0ix is not a POR for this platform, so
I was unable to collect those numbers. The deepest package C-state supported
is PC6.
With pm_runtime_disable() keeping the port in D0, I observe approximately
10% reduction in PC6 residency. Given this power impact, I will proceed with
the pme_is_broken() approach suggested by Lukas, which allows the port to
enter D3hot while keeping hotplug interrupts enabled.
I will submit v3 with this implementation. Change log looks like below:
diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c
index 1e9158d7bac7..f854ef9551c3 100644
--- a/drivers/pci/hotplug/pciehp_core.c
+++ b/drivers/pci/hotplug/pciehp_core.c
@@ -260,13 +260,20 @@ static bool pme_is_native(struct pcie_device *dev)
return pcie_ports_native || host->native_pme;
}
+static bool pme_is_broken(struct pcie_device *pcie)
+{
+ struct pci_dev *pdev = pcie->port;
+
+ return !!(pdev->dev_flags & PCI_DEV_FLAGS_PME_UNRELIABLE);
+}
+
static void pciehp_disable_interrupt(struct pcie_device *dev)
{
/*
* Disable hotplug interrupt so that it does not trigger
* immediately when the downstream link goes down.
*/
- if (pme_is_native(dev))
+ if (pme_is_native(dev) && !pme_is_broken(dev))
pcie_disable_interrupt(get_service_data(dev));
}
@@ -318,7 +325,7 @@ static int pciehp_resume(struct pcie_device *dev)
{
struct controller *ctrl = get_service_data(dev);
- if (pme_is_native(dev))
+ if (pme_is_native(dev) && !pme_is_broken(dev))
pcie_enable_interrupt(ctrl);
pciehp_check_presence(ctrl);
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index 48946cca4be7..acaa328dff51 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -6380,3 +6380,51 @@ static void pci_mask_replay_timer_timeout(struct pci_dev *pdev)
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_GLI, 0x9750, pci_mask_replay_timer_timeout);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_GLI, 0x9755, pci_mask_replay_timer_timeout);
#endif
+
+/*
+ * When a PCIe port is in D3hot, the hotplug driver depends on PME
+ * to wake the port back to D0 and then process any hotplug-related
+ * state changes. On Intel Catlow Lake platforms, PCH PCIe root ports
+ * do not reliably update PME state during D3hot to D0 transitions.
+ *
+ * Mark affected ports with PCI_DEV_FLAGS_PME_UNRELIABLE to keep
+ * hotplug interrupts (HPIE) enabled during D3hot instead of relying on
+ * PME-based wakeup. This allows hotplug events to be delivered via
+ * direct interrupts while still permitting the port to enter D3hot for
@@ -6380,3 +6380,51 @@ static void pci_mask_replay_timer_timeout(struct pci_dev *pdev)
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_GLI, 0x9750, pci_mask_replay_timer_timeout);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_GLI, 0x9755, pci_mask_replay_timer_timeout);
#endif
+
+/*
+ * When a PCIe port is in D3hot, the hotplug driver depends on PME
+ * to wake the port back to D0 and then process any hotplug-related
+ * state changes. On Intel Catlow Lake platforms, PCH PCIe root ports
+ * do not reliably update PME state during D3hot to D0 transitions.
+ *
+ * Mark affected ports with PCI_DEV_FLAGS_PME_UNRELIABLE to keep
+ * hotplug interrupts (HPIE) enabled during D3hot instead of relying on
+ * PME-based wakeup. This allows hotplug events to be delivered via
+ * direct interrupts while still permitting the port to enter D3hot for
+ * power savings.
+ *
+ */
+static void quirk_intel_catlow_pcie_pme_unreliable(struct pci_dev *dev)
+{
+ dev->dev_flags |= PCI_DEV_FLAGS_PME_UNRELIABLE;
+ pci_info(dev, "Catlow PCH port: PME unreliable\n");
+}
+/* Apply quirk to Catlow Lake PCH root ports (0x7a30 - 0x7a4b) */
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x7a30, quirk_intel_catlow_pcie_pme_unreliable);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x7a31, quirk_intel_catlow_pcie_pme_unreliable);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x7a32, quirk_intel_catlow_pcie_pme_unreliable);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x7a33, quirk_intel_catlow_pcie_pme_unreliable);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x7a34, quirk_intel_catlow_pcie_pme_unreliable);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x7a35, quirk_intel_catlow_pcie_pme_unreliable);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x7a36, quirk_intel_catlow_pcie_pme_unreliable);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x7a37, quirk_intel_catlow_pcie_pme_unreliable);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x7a38, quirk_intel_catlow_pcie_pme_unreliable);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x7a39, quirk_intel_catlow_pcie_pme_unreliable);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x7a3a, quirk_intel_catlow_pcie_pme_unreliable);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x7a3b, quirk_intel_catlow_pcie_pme_unreliable);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x7a3c, quirk_intel_catlow_pcie_pme_unreliable);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x7a3d, quirk_intel_catlow_pcie_pme_unreliable);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x7a3e, quirk_intel_catlow_pcie_pme_unreliable);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x7a3f, quirk_intel_catlow_pcie_pme_unreliable);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x7a40, quirk_intel_catlow_pcie_pme_unreliable);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x7a41, quirk_intel_catlow_pcie_pme_unreliable);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x7a42, quirk_intel_catlow_pcie_pme_unreliable);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x7a43, quirk_intel_catlow_pcie_pme_unreliable);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x7a44, quirk_intel_catlow_pcie_pme_unreliable);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x7a45, quirk_intel_catlow_pcie_pme_unreliable);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x7a46, quirk_intel_catlow_pcie_pme_unreliable);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x7a47, quirk_intel_catlow_pcie_pme_unreliable);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x7a48, quirk_intel_catlow_pcie_pme_unreliable);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x7a49, quirk_intel_catlow_pcie_pme_unreliable);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x7a4a, quirk_intel_catlow_pcie_pme_unreliable);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x7a4b, quirk_intel_catlow_pcie_pme_unreliable);
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 1c270f1d5123..9761351c5d70 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -253,6 +253,8 @@ enum pci_dev_flags {
* integrated with the downstream devices and doesn't use real PCI.
*/
PCI_DEV_FLAGS_PCI_BRIDGE_NO_ALIAS = (__force pci_dev_flags_t) (1 << 14),
+ /* Device PME is broken or unreliable */
+ PCI_DEV_FLAGS_PME_UNRELIABLE = (__force pci_dev_flags_t) (1 << 15),
};
>
>
>>
>>> If this does cause a power regression, the pme_is_broken() approach
>>> suggested upthread might be a viable alternative. It'll allow the
>>> Root Port to go to D3hot but will keep interrupts enabled in the
>>> Slot Control register.
>>
>> Sounds reasonable to me.
>
> Sounds good.
>
>>
>
--
Sathyanarayanan Kuppuswamy
Linux Kernel Developer
prev parent reply other threads:[~2026-03-09 18:04 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
[not found] <20260213231428.613164-1-sathyanarayanan.kuppuswamy@linux.intel.com>
2026-02-17 16:54 ` [PATCH v2] PCI: pciehp: Fix hotplug on Catlow Lake with unreliable PME status Kuppuswamy Sathyanarayanan
2026-02-17 18:08 ` Rafael J. Wysocki
2026-02-18 16:27 ` Kuppuswamy Sathyanarayanan
2026-02-18 17:33 ` Rafael J. Wysocki
2026-02-19 8:04 ` Lukas Wunner
2026-02-19 11:09 ` Rafael J. Wysocki
2026-02-19 21:54 ` Kuppuswamy Sathyanarayanan
2026-03-09 18:04 ` Kuppuswamy Sathyanarayanan [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=19dc5e60-2740-458f-9e0a-90d3982316f4@linux.intel.com \
--to=sathyanarayanan.kuppuswamy@linux.intel.com \
--cc=bhelgaas@google.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-pci@vger.kernel.org \
--cc=linux-pm@vger.kernel.org \
--cc=lukas@wunner.de \
--cc=rafael@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox