All of lore.kernel.org
 help / color / mirror / Atom feed
From: Pranay Samala <pranay.samala@intel.com>
To: igt-dev@lists.freedesktop.org
Cc: karthik.b.s@intel.com, sameer.lattannavar@intel.com,
	pranay.samala@intel.com
Subject: [PATCH i-g-t 2/7] lib/igt_pm: Add PCI PME capability and D state accessors
Date: Mon,  7 Sep 2026 19:52:54 +0530	[thread overview]
Message-ID: <20260907142259.750528-3-pranay.samala@intel.com> (raw)
In-Reply-To: <20260907142259.750528-1-pranay.samala@intel.com>

Add four helpers that read the PCI Power Management capability.

igt_pm_pci_pme_supported() says whether the device can send a PME from a
given D state, by testing one bit of the PME_Support field.
The state matters. Intel graphics devices support PME in D3hot but not in
D3cold.

igt_pm_pci_pme_enabled() reads PME_En, which says whether the device is
armed to send a PME. Its kerneldoc notes that a driver may arm PME from
its own runtime suspend hook rather than leaving it to the PCI/PM core,
and that xe does so subject to device_may_wakeup(), i.e. subject to
power/wakeup.

igt_pm_pci_pme_status() reads PME_Status, which says whether the device has
a PME pending. Because the bit is write-1-to-clear and nothing but software
clears it, a test that expected a wakeup and did not get one can use this
to tell a device that never signalled from one whose signal was never
delivered.

igt_pm_pci_get_d_state() reads the PowerState field. Its kerneldoc points
out that PMCS can only ever report D0-D3hot, and that it cannot be used to
detect D3cold: config space here is reached through sysfs, where
pci_config_pm_runtime_get() resumes a device in D3cold before the read, so
such a device reports D0.

Assisted-by: GitHub_Copilot:claude-opus-5
Signed-off-by: Pranay Samala <pranay.samala@intel.com>
---
 lib/igt_pm.c | 162 +++++++++++++++++++++++++++++++++++++++++++++++++++
 lib/igt_pm.h |   5 ++
 2 files changed, 167 insertions(+)

diff --git a/lib/igt_pm.c b/lib/igt_pm.c
index 7905deb8d..ddc1de01c 100644
--- a/lib/igt_pm.c
+++ b/lib/igt_pm.c
@@ -1524,6 +1524,168 @@ bool igt_has_pci_pm_capability(struct pci_device *pci_dev)
 	return (offset > 0);
 }
 
+/*
+ * Read a 16 bit register of the PCI Power Management capability, at @reg_offset
+ * from the start of the capability. Returns false if the device has no PM
+ * capability, the config space read failed, or the device did not respond.
+ *
+ * Config space reads do not resume a device suspended into D3hot, so these are
+ * safe to use while the device is runtime suspended. They are not a way to
+ * observe D3cold though: the read goes through sysfs, where
+ * pci_config_pm_runtime_get() resumes a device in D3cold first, so such a
+ * device answers as D0 rather than dropping off the bus.
+ */
+static bool igt_pm_read_pci_pm_reg(struct pci_device *pci_dev, int reg_offset,
+				   uint16_t *val)
+{
+	int offset;
+
+	offset = find_pci_cap_offset(pci_dev, PCI_PM_CAP_ID);
+	if (offset <= 0)
+		return false;
+
+	if (pci_device_cfg_read_u16(pci_dev, val, offset + reg_offset))
+		return false;
+
+	/*
+	 * A device that has gone away reads back as all ones, which would
+	 * otherwise decode as a valid register value: PMCS 0xffff means
+	 * PowerState = D3hot with PME_En set. Reject it.
+	 */
+	return *val != 0xffff;
+}
+
+/**
+ * igt_pm_pci_pme_supported:
+ * @pci_dev: PCI device struct
+ * @state: D state to query PME support for
+ *
+ * Reads the PME_Support field (bits [15:11]) of the PCI Power Management
+ * Capabilities (PMC) register and reports whether the device is able to
+ * generate a Power Management Event from @state.
+ *
+ * Note that PME support is per D state: Intel graphics devices typically
+ * advertise PME support in D3hot but not in D3cold, so a state agnostic check
+ * is not sufficient to decide whether PME based signalling can be used.
+ *
+ * Returns: true if the device can generate a PME from @state, false otherwise.
+ */
+bool igt_pm_pci_pme_supported(struct pci_device *pci_dev,
+			      enum igt_acpi_d_state state)
+{
+	uint16_t pmc;
+	int bit;
+
+	switch (state) {
+	case IGT_ACPI_D0:
+		bit = 0;
+		break;
+	case IGT_ACPI_D1:
+		bit = 1;
+		break;
+	case IGT_ACPI_D2:
+		bit = 2;
+		break;
+	case IGT_ACPI_D3Hot:
+		bit = 3;
+		break;
+	case IGT_ACPI_D3Cold:
+		bit = 4;
+		break;
+	default:
+		igt_debug("Invalid D state %d for PME support query\n", state);
+		return false;
+	}
+
+	if (!igt_pm_read_pci_pm_reg(pci_dev, PCI_PM_PMC_OFFSET, &pmc))
+		return false;
+
+	igt_debug("PCI '%04x:%02x:%02x.%01x' PMC = 0x%04x, PME_Support = 0x%02x\n",
+		  pci_dev->domain, pci_dev->bus, pci_dev->dev, pci_dev->func, pmc,
+		  (pmc & PCI_PM_PMC_PME_SUPPORT_MASK) >> PCI_PM_PMC_PME_SUPPORT_SHIFT);
+
+	return !!(pmc & (1 << (PCI_PM_PMC_PME_SUPPORT_SHIFT + bit)));
+}
+
+/**
+ * igt_pm_pci_pme_enabled:
+ * @pci_dev: PCI device struct
+ *
+ * Reads PME_En (bit 8) of the PCI Power Management Control/Status (PMCS)
+ * register, i.e. whether the device is armed to generate PMEs.
+ *
+ * The PCI/PM core sets this on suspend if the device can generate a PME from the
+ * state it is suspending into, but a driver may also arm PME itself from its
+ * runtime suspend hook, in which case the driver's own conditions apply. xe does
+ * exactly that, and requires the device_may_wakeup() policy behind power/wakeup,
+ * so on xe this bit follows power/wakeup even for a runtime suspend. See
+ * igt_pm_set_wakeup_enabled().
+ *
+ * Returns: true if the device is armed to generate PMEs, false otherwise.
+ */
+bool igt_pm_pci_pme_enabled(struct pci_device *pci_dev)
+{
+	uint16_t pmcs;
+
+	if (!igt_pm_read_pci_pm_reg(pci_dev, PCI_PM_PMCS_OFFSET, &pmcs))
+		return false;
+
+	return !!(pmcs & PCI_PM_PMCS_PME_EN);
+}
+
+/**
+ * igt_pm_pci_pme_status:
+ * @pci_dev: PCI device struct
+ *
+ * Reads PME_Status (bit 15) of the PCI Power Management Control/Status (PMCS)
+ * register, i.e. whether the device has a PME pending.
+ *
+ * The bit is write-1-to-clear and the device does not clear it itself. It stays
+ * set from the moment the device signals a PME until software acknowledges it,
+ * which for a runtime resume is pci_pme_wakeup() on the way back to D0. So
+ * finding it still set on a device that is still suspended means the device did
+ * signal but the platform never delivered the PME, whereas finding it clear
+ * means the device never signalled at all.
+ *
+ * Returns: true if the device has a PME pending, false otherwise.
+ */
+bool igt_pm_pci_pme_status(struct pci_device *pci_dev)
+{
+	uint16_t pmcs;
+
+	if (!igt_pm_read_pci_pm_reg(pci_dev, PCI_PM_PMCS_OFFSET, &pmcs))
+		return false;
+
+	return !!(pmcs & PCI_PM_PMCS_PME_STATUS);
+}
+
+/**
+ * igt_pm_pci_get_d_state:
+ * @pci_dev: PCI device struct
+ *
+ * Reads the PowerState field (bits [1:0]) of the PCI Power Management
+ * Control/Status (PMCS) register.
+ *
+ * PMCS can only express D0-D3hot, and it is no help in detecting D3cold either:
+ * a device in D3cold is resumed by the config space read itself and so reports
+ * D0. A runtime suspended device that reports D3hot here is genuinely in D3hot.
+ *
+ * Returns: the D state the device reports, or IGT_ACPI_UNKNOWN_STATE if the
+ * device has no PM capability or the read failed.
+ */
+enum igt_acpi_d_state igt_pm_pci_get_d_state(struct pci_device *pci_dev)
+{
+	static const enum igt_acpi_d_state d_states[] = {
+		IGT_ACPI_D0, IGT_ACPI_D1, IGT_ACPI_D2, IGT_ACPI_D3Hot,
+	};
+	uint16_t pmcs;
+
+	if (!igt_pm_read_pci_pm_reg(pci_dev, PCI_PM_PMCS_OFFSET, &pmcs))
+		return IGT_ACPI_UNKNOWN_STATE;
+
+	return d_states[pmcs & PCI_PM_PMCS_PSTATE_MASK];
+}
+
 /**
  * igt_pm_dpms_toggle:
  * @output: igt output for which DPMS toggle has to be performed
diff --git a/lib/igt_pm.h b/lib/igt_pm.h
index cd9dceb1e..2784e97be 100644
--- a/lib/igt_pm.h
+++ b/lib/igt_pm.h
@@ -105,6 +105,11 @@ uint64_t igt_pm_get_runtime_active_time(struct pci_device *pci_dev);
 int igt_pm_get_runtime_usage(struct pci_device *pci_dev);
 void igt_pm_ignore_slpc_efficient_freq(int i915, int gtfd, bool val);
 bool igt_has_pci_pm_capability(struct pci_device *pci_dev);
+bool igt_pm_pci_pme_supported(struct pci_device *pci_dev,
+			      enum igt_acpi_d_state state);
+bool igt_pm_pci_pme_enabled(struct pci_device *pci_dev);
+bool igt_pm_pci_pme_status(struct pci_device *pci_dev);
+enum igt_acpi_d_state igt_pm_pci_get_d_state(struct pci_device *pci_dev);
 void igt_pm_dpms_toggle(igt_output_t *output);
 uint32_t igt_get_dc_counter(const char *dc_data);
 bool igt_support_dc6(int debugfs_fd);
-- 
2.53.0


  parent reply	other threads:[~2026-09-07 14:13 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-07 14:22 [PATCH i-g-t 0/7] Validate PM_PME signalling on display hotplug Pranay Samala
2026-09-07 14:22 ` [PATCH i-g-t 1/7] lib/igt_pci: Add PCI Power Management capability register layout Pranay Samala
2026-09-07 14:22 ` Pranay Samala [this message]
2026-09-07 14:22 ` [PATCH i-g-t 3/7] lib/igt_pm: Factor out power attribute path construction Pranay Samala
2026-09-07 14:22 ` [PATCH i-g-t 4/7] lib/igt_pm: Add power/wakeup accessors Pranay Samala
2026-09-07 14:22 ` [PATCH i-g-t 5/7] lib/igt_pm: Add power/wakeup_active_count accessor Pranay Samala
2026-09-07 14:22 ` [PATCH i-g-t 6/7] lib/igt_pm: Add drm_kms_helper.poll save/restore helpers Pranay Samala
2026-09-07 14:22 ` [PATCH i-g-t 7/7] tests/chamelium/kms_chamelium_hpd: Add HPD from runtime suspended D3hot Pranay Samala
2026-09-16 15:40   ` Govindapillai, Vinod
2026-09-07 19:49 ` ✓ Xe.CI.BAT: success for Validate PM_PME signalling on display hotplug (rev2) Patchwork
2026-09-07 20:02 ` ✓ i915.CI.BAT: " Patchwork
2026-09-08  0:17 ` ✗ Xe.CI.FULL: failure " Patchwork
2026-09-08  6:28 ` ✗ i915.CI.Full: " Patchwork

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=20260907142259.750528-3-pranay.samala@intel.com \
    --to=pranay.samala@intel.com \
    --cc=igt-dev@lists.freedesktop.org \
    --cc=karthik.b.s@intel.com \
    --cc=sameer.lattannavar@intel.com \
    /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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.