Merge branch 'pci/wake'

- Add support for PCIe WAKE# interrupt when described via DT (Krishna
  Chaitanya Chundru)

* pci/wake:
  PCI: Add support for PCIe WAKE# interrupt
This commit is contained in:
Bjorn Helgaas
2026-08-21 16:40:38 -05:00
7 changed files with 103 additions and 0 deletions

View File

@@ -7,6 +7,7 @@
#define pr_fmt(fmt) "PCI: OF: " fmt
#include <linux/cleanup.h>
#include <linux/gpio/consumer.h>
#include <linux/irqdomain.h>
#include <linux/kernel.h>
#include <linux/pci.h>
@@ -15,6 +16,7 @@
#include <linux/of_address.h>
#include <linux/of_pci.h>
#include <linux/platform_device.h>
#include <linux/pm_wakeirq.h>
#include "pci.h"
#ifdef CONFIG_PCI
@@ -586,6 +588,83 @@ int of_irq_parse_and_map_pci(const struct pci_dev *dev, u8 slot, u8 pin)
return irq_create_of_mapping(&oirq);
}
EXPORT_SYMBOL_GPL(of_irq_parse_and_map_pci);
static void pci_configure_wake_irq(struct pci_dev *pdev, struct gpio_desc *wake)
{
int ret, wake_irq, irq_type;
wake_irq = gpiod_to_irq(wake);
if (wake_irq < 0) {
pci_err(pdev, "Failed to get wake IRQ: %d\n", wake_irq);
return;
}
/*
* dev_pm_set_dedicated_wake_irq() associates a wakeup IRQ with the
* device and requests it, but the PM core keeps it disabled by
* default. The IRQ is enabled only when the device is allowed to
* wake the system (during system suspend and after runtime
* suspend), and only if device wakeup is enabled.
*
* When the wake IRQ fires, the wakeirq handler invokes
* pm_runtime_resume() to bring the device back to an active power
* state (e.g. from D3cold to D0). Once the device is active and
* the link is usable, the endpoint may signal a PME, which is then
* handled by the PCI core (either via PME polling or the PCIe PME
* service driver) to wakeup the particular endpoint.
*/
ret = dev_pm_set_dedicated_wake_irq(&pdev->dev, wake_irq);
if (ret < 0) {
pci_err(pdev, "Failed to set WAKE# IRQ: %d\n", ret);
return;
}
irq_type = gpiod_is_active_low(wake) ? IRQ_TYPE_LEVEL_LOW :
IRQ_TYPE_LEVEL_HIGH;
ret = irq_set_irq_type(wake_irq, irq_type);
if (ret < 0) {
dev_pm_clear_wake_irq(&pdev->dev);
pci_err(pdev, "Failed to set irq_type: %d\n", ret);
return;
}
device_init_wakeup(&pdev->dev, true);
}
void pci_configure_of_wake_gpio(struct pci_dev *dev)
{
struct device_node *dn = pci_device_to_OF_node(dev);
struct gpio_desc *gpio;
if (!dn && !dev->wake)
return;
/*
* fwnode_gpiod_get() may fail with -EBUSY (e.g. shared WAKE#), but
* the actual WAKE# trigger from the device would still work and
* the host controller driver will enable power to the topology.
*
* -EPROBE_DEFER cannot be propagated here since pci_device_add()
* has no retry mechanism.
*/
gpio = fwnode_gpiod_get(of_fwnode_handle(dn), "wake", GPIOD_IN, NULL);
if (!IS_ERR(gpio)) {
dev->wake = gpio;
pci_configure_wake_irq(dev, gpio);
}
}
void pci_remove_of_wake_gpio(struct pci_dev *dev)
{
struct device_node *dn = pci_device_to_OF_node(dev);
if (!dn)
return;
device_init_wakeup(&dev->dev, false);
dev_pm_clear_wake_irq(&dev->dev);
gpiod_put(dev->wake);
dev->wake = NULL;
}
#endif /* CONFIG_OF_IRQ */
static int pci_parse_request_of_pci_ranges(struct device *dev,

View File

@@ -17,6 +17,7 @@
#include <linux/lockdep.h>
#include <linux/msi.h>
#include <linux/of.h>
#include <linux/of_pci.h>
#include <linux/pci.h>
#include <linux/pm.h>
#include <linux/slab.h>
@@ -1114,6 +1115,16 @@ static inline bool platform_pci_bridge_d3(struct pci_dev *dev)
return acpi_pci_bridge_d3(dev);
}
void platform_pci_configure_wake(struct pci_dev *dev)
{
pci_configure_of_wake_gpio(dev);
}
void platform_pci_remove_wake(struct pci_dev *dev)
{
pci_remove_of_wake_gpio(dev);
}
/**
* pci_update_current_state - Read power state of given device and cache it
* @dev: PCI device to handle.

View File

@@ -285,6 +285,8 @@ void pci_msix_init(struct pci_dev *dev);
bool pci_bridge_d3_possible(struct pci_dev *dev);
void pci_bridge_d3_update(struct pci_dev *dev);
int pci_bridge_wait_for_secondary_bus(struct pci_dev *dev, char *reset_type);
void platform_pci_configure_wake(struct pci_dev *dev);
void platform_pci_remove_wake(struct pci_dev *dev);
static inline bool pci_bus_rrs_vendor_id(u32 l)
{

View File

@@ -2749,6 +2749,8 @@ void pci_device_add(struct pci_dev *dev, struct pci_bus *bus)
pci_init_capabilities(dev);
platform_pci_configure_wake(dev);
/*
* Add the device to our list of discovered devices
* and the bus list for fixup functions, etc.

View File

@@ -34,6 +34,7 @@ static void pci_destroy_dev(struct pci_dev *dev)
if (pci_dev_test_and_set_removed(dev))
return;
platform_pci_remove_wake(dev);
pci_doe_sysfs_teardown(dev);
pci_npem_remove(dev);

View File

@@ -30,12 +30,18 @@ static inline void of_pci_check_probe_only(void) { }
#if IS_ENABLED(CONFIG_OF_IRQ)
int of_irq_parse_and_map_pci(const struct pci_dev *dev, u8 slot, u8 pin);
void pci_configure_of_wake_gpio(struct pci_dev *dev);
void pci_remove_of_wake_gpio(struct pci_dev *dev);
#else
static inline int
of_irq_parse_and_map_pci(const struct pci_dev *dev, u8 slot, u8 pin)
{
return 0;
}
static inline void pci_configure_of_wake_gpio(struct pci_dev *dev) { }
static inline void pci_remove_of_wake_gpio(struct pci_dev *dev) { }
#endif
#endif

View File

@@ -587,6 +587,8 @@ struct pci_dev {
/* These methods index pci_reset_fn_methods[] */
u8 reset_methods[PCI_NUM_RESET_METHODS]; /* In priority order */
struct gpio_desc *wake; /* WAKE# GPIO */
#ifdef CONFIG_PCIE_TPH
u16 tph_cap; /* TPH capability offset */
u8 tph_mode; /* TPH mode */