Skip to content

Commit 0b83255

Browse files
krishnachaitanya-linuxZiyue Zhang
authored andcommitted
FROMLIST: PCI: Add support for PCIe WAKE# interrupt
According to the PCI Express specification (PCIe r7.0, Section 5.3.3.2), two link wakeup mechanisms are defined: Beacon and WAKE#. Beacon is a hardware-only mechanism and is invisible to software (PCIe r7.0, Section 4.2.7.8.1). This change adds support for the WAKE# mechanism in the PCI core. According to the PCIe specification, multiple WAKE# signals can exist in a system or each component in the hierarchy could share a single WAKE# signal. In configurations involving a PCIe switch, each downstream port (DSP) of the switch may be connected to a separate WAKE# line, allowing each endpoint to signal WAKE# independently. From figure 5.4 in sec 5.3.3.2, WAKE# can also be terminated at the switch itself. Such topologies are typically not described in Device Tree, therefore it is out of scope for this series. To support this, the WAKE# should be described in the device tree node of the endpoint/bridge. If all endpoints share a single WAKE# line, then each endpoint node shall describe the same WAKE# signal or a single WAKE# in the Root Port node. In pci_device_add(), PCI framework will search for the WAKE# in device node. Once found, register for the wake IRQ through dev_pm_set_dedicated_wake_irq() associates a wakeup IRQ with a device and requests it, but the PM core keeps the IRQ disabled by default. The IRQ is enabled by the PM core, only when the device is permitted to wake the system, i.e. during system suspend and after runtime suspend, and only when device wakeup is enabled. If the same WAKE# GPIO is described in multiple device tree nodes, only the first device that successfully registers the wake IRQ will succeed, while subsequent registrations may fail. This limitation does not affect functional correctness, since WAKE# is only used to bring the link to D0, and endpoint-specific wakeup handling is resolved later through PME detection (PME_EN is set in suspend path by PCI core by default). When the wake IRQ fires, the wakeirq handler invokes pm_runtime_resume() to bring the device back to an active power state, such as transitioning from D3cold to D0. Once the device is active and the link is usable, the endpoint may generate a PME, which is then handled by the PCI core through PME polling or the PCIe PME service driver to complete the wakeup of the endpoint. WAKE# is added in dts schema and merged based on below links. Link: https://lore.kernel.org/all/20250515090517.3506772-1-krishna.chundru@oss.qualcomm.com/ Link: devicetree-org/dt-schema#170 Reviewed-by: Linus Walleij <linus.walleij@linaro.org> Acked-by: Manivannan Sadhasivam <mani@kernel.org> Signed-off-by: Krishna Chaitanya Chundru <krishna.chundru@oss.qualcomm.com> Link: https://lore.kernel.org/all/20260624-wakeirq_support-v11-1-120fbfaebe59@oss.qualcomm.com/
1 parent bf15344 commit 0b83255

7 files changed

Lines changed: 99 additions & 0 deletions

File tree

drivers/pci/of.c

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#define pr_fmt(fmt) "PCI: OF: " fmt
88

99
#include <linux/cleanup.h>
10+
#include <linux/gpio/consumer.h>
1011
#include <linux/irqdomain.h>
1112
#include <linux/kernel.h>
1213
#include <linux/pci.h>
@@ -15,6 +16,7 @@
1516
#include <linux/of_address.h>
1617
#include <linux/of_pci.h>
1718
#include <linux/platform_device.h>
19+
#include <linux/pm_wakeirq.h>
1820
#include "pci.h"
1921

2022
#ifdef CONFIG_PCI
@@ -586,6 +588,79 @@ int of_irq_parse_and_map_pci(const struct pci_dev *dev, u8 slot, u8 pin)
586588
return irq_create_of_mapping(&oirq);
587589
}
588590
EXPORT_SYMBOL_GPL(of_irq_parse_and_map_pci);
591+
static void pci_configure_wake_irq(struct pci_dev *pdev, struct gpio_desc *wake)
592+
{
593+
int ret, wake_irq;
594+
595+
wake_irq = gpiod_to_irq(wake);
596+
if (wake_irq < 0) {
597+
pci_err(pdev, "Failed to get wake irq: %d\n", wake_irq);
598+
return;
599+
}
600+
601+
/*
602+
* dev_pm_set_dedicated_wake_irq() associates a wakeup IRQ with the
603+
* device and requests it, but the PM core keeps it disabled by default.
604+
* The IRQ is enabled only when the device is allowed to wake the system
605+
* (during system suspend and after runtime suspend), and only if device
606+
* wakeup is enabled.
607+
*
608+
* When the wake IRQ fires, the wakeirq handler invokes pm_runtime_resume()
609+
* to bring the device back to an active power state (e.g. from D3cold to D0).
610+
* Once the device is active and the link is usable, the endpoint may signal
611+
* a PME, which is then handled by the PCI core (either via PME polling or the
612+
* PCIe PME service driver) to wakeup particular endpoint.
613+
*/
614+
ret = dev_pm_set_dedicated_wake_irq(&pdev->dev, wake_irq);
615+
if (ret < 0) {
616+
pci_err(pdev, "Failed to set WAKE# IRQ: %d\n", ret);
617+
return;
618+
}
619+
620+
ret = irq_set_irq_type(wake_irq, IRQ_TYPE_LEVEL_LOW);
621+
if (ret < 0) {
622+
dev_pm_clear_wake_irq(&pdev->dev);
623+
pci_err(pdev, "Failed to set irq_type: %d\n", ret);
624+
return;
625+
}
626+
627+
device_init_wakeup(&pdev->dev, true);
628+
}
629+
630+
void pci_configure_of_wake_gpio(struct pci_dev *dev)
631+
{
632+
struct device_node *dn = pci_device_to_OF_node(dev);
633+
struct gpio_desc *gpio;
634+
635+
if (!dn)
636+
return;
637+
/*
638+
* fwnode_gpiod_get() may fail with -EBUSY (e.g. shared WAKE#), but the
639+
* actual WAKE# trigger from the device would still work and the host
640+
* controller driver will enable power to the topology.
641+
*
642+
* -EPROBE_DEFER cannot be propagated here since pci_device_add() has no
643+
* retry mechanism.
644+
*/
645+
gpio = fwnode_gpiod_get(of_fwnode_handle(dn), "wake", GPIOD_IN, NULL);
646+
if (!IS_ERR(gpio)) {
647+
dev->wake = gpio;
648+
pci_configure_wake_irq(dev, gpio);
649+
}
650+
}
651+
652+
void pci_remove_of_wake_gpio(struct pci_dev *dev)
653+
{
654+
struct device_node *dn = pci_device_to_OF_node(dev);
655+
656+
if (!dn)
657+
return;
658+
659+
device_init_wakeup(&dev->dev, false);
660+
dev_pm_clear_wake_irq(&dev->dev);
661+
gpiod_put(dev->wake);
662+
dev->wake = NULL;
663+
}
589664
#endif /* CONFIG_OF_IRQ */
590665

591666
static int pci_parse_request_of_pci_ranges(struct device *dev,

drivers/pci/pci.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include <linux/init.h>
1616
#include <linux/msi.h>
1717
#include <linux/of.h>
18+
#include <linux/of_pci.h>
1819
#include <linux/pci.h>
1920
#include <linux/pm.h>
2021
#include <linux/slab.h>
@@ -1128,6 +1129,16 @@ static inline bool platform_pci_bridge_d3(struct pci_dev *dev)
11281129
return acpi_pci_bridge_d3(dev);
11291130
}
11301131

1132+
void platform_pci_configure_wake(struct pci_dev *dev)
1133+
{
1134+
pci_configure_of_wake_gpio(dev);
1135+
}
1136+
1137+
void platform_pci_remove_wake(struct pci_dev *dev)
1138+
{
1139+
pci_remove_of_wake_gpio(dev);
1140+
}
1141+
11311142
/**
11321143
* pci_update_current_state - Read power state of given device and cache it
11331144
* @dev: PCI device to handle.

drivers/pci/pci.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,8 @@ void pci_msix_init(struct pci_dev *dev);
265265
bool pci_bridge_d3_possible(struct pci_dev *dev);
266266
void pci_bridge_d3_update(struct pci_dev *dev);
267267
int pci_bridge_wait_for_secondary_bus(struct pci_dev *dev, char *reset_type);
268+
void platform_pci_configure_wake(struct pci_dev *dev);
269+
void platform_pci_remove_wake(struct pci_dev *dev);
268270

269271
static inline bool pci_bus_rrs_vendor_id(u32 l)
270272
{

drivers/pci/probe.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2741,6 +2741,8 @@ void pci_device_add(struct pci_dev *dev, struct pci_bus *bus)
27412741
ret = device_add(&dev->dev);
27422742
WARN_ON(ret < 0);
27432743

2744+
platform_pci_configure_wake(dev);
2745+
27442746
pci_npem_create(dev);
27452747

27462748
pci_doe_sysfs_init(dev);

drivers/pci/remove.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ static void pci_destroy_dev(struct pci_dev *dev)
3535
if (pci_dev_test_and_set_removed(dev))
3636
return;
3737

38+
platform_pci_remove_wake(dev);
3839
pci_doe_sysfs_teardown(dev);
3940
pci_npem_remove(dev);
4041

include/linux/of_pci.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,18 @@ static inline void of_pci_check_probe_only(void) { }
3030

3131
#if IS_ENABLED(CONFIG_OF_IRQ)
3232
int of_irq_parse_and_map_pci(const struct pci_dev *dev, u8 slot, u8 pin);
33+
void pci_configure_of_wake_gpio(struct pci_dev *dev);
34+
void pci_remove_of_wake_gpio(struct pci_dev *dev);
3335
#else
3436
static inline int
3537
of_irq_parse_and_map_pci(const struct pci_dev *dev, u8 slot, u8 pin)
3638
{
3739
return 0;
3840
}
41+
42+
static inline void pci_configure_of_wake_gpio(struct pci_dev *dev) { }
43+
44+
static inline void pci_remove_of_wake_gpio(struct pci_dev *dev) { }
3945
#endif
4046

4147
#endif

include/linux/pci.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -558,6 +558,8 @@ struct pci_dev {
558558
/* These methods index pci_reset_fn_methods[] */
559559
u8 reset_methods[PCI_NUM_RESET_METHODS]; /* In priority order */
560560

561+
struct gpio_desc *wake; /* Holds WAKE# gpio */
562+
561563
#ifdef CONFIG_PCIE_TPH
562564
u16 tph_cap; /* TPH capability offset */
563565
u8 tph_mode; /* TPH mode */

0 commit comments

Comments
 (0)