18cfab3cfSBjorn Helgaas // SPDX-License-Identifier: GPL-2.0
2271fb719SRafael J. Wysocki /*
3271fb719SRafael J. Wysocki * PCIe Native PME support
4271fb719SRafael J. Wysocki *
5271fb719SRafael J. Wysocki * Copyright (C) 2007 - 2009 Intel Corp
6271fb719SRafael J. Wysocki * Copyright (C) 2007 - 2009 Shaohua Li <shaohua.li@intel.com>
7271fb719SRafael J. Wysocki * Copyright (C) 2009 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc.
8271fb719SRafael J. Wysocki */
9271fb719SRafael J. Wysocki
1000ebf134SFrederick Lawler #define dev_fmt(fmt) "PME: " fmt
1100ebf134SFrederick Lawler
12271fb719SRafael J. Wysocki #include <linux/pci.h>
13271fb719SRafael J. Wysocki #include <linux/kernel.h>
14271fb719SRafael J. Wysocki #include <linux/errno.h>
15271fb719SRafael J. Wysocki #include <linux/slab.h>
16271fb719SRafael J. Wysocki #include <linux/init.h>
17271fb719SRafael J. Wysocki #include <linux/interrupt.h>
18271fb719SRafael J. Wysocki #include <linux/device.h>
19271fb719SRafael J. Wysocki #include <linux/pm_runtime.h>
20271fb719SRafael J. Wysocki
21271fb719SRafael J. Wysocki #include "../pci.h"
22271fb719SRafael J. Wysocki #include "portdrv.h"
23271fb719SRafael J. Wysocki
24271fb719SRafael J. Wysocki /*
25271fb719SRafael J. Wysocki * If this switch is set, MSI will not be used for PCIe PME signaling. This
26271fb719SRafael J. Wysocki * causes the PCIe port driver to use INTx interrupts only, but it turns out
27271fb719SRafael J. Wysocki * that using MSI for PCIe PME signaling doesn't play well with PCIe PME-based
28271fb719SRafael J. Wysocki * wake-up from system sleep states.
29271fb719SRafael J. Wysocki */
30271fb719SRafael J. Wysocki bool pcie_pme_msi_disabled;
31271fb719SRafael J. Wysocki
pcie_pme_setup(char * str)32271fb719SRafael J. Wysocki static int __init pcie_pme_setup(char *str)
33271fb719SRafael J. Wysocki {
34271fb719SRafael J. Wysocki if (!strncmp(str, "nomsi", 5))
35271fb719SRafael J. Wysocki pcie_pme_msi_disabled = true;
36271fb719SRafael J. Wysocki
37271fb719SRafael J. Wysocki return 1;
38271fb719SRafael J. Wysocki }
39271fb719SRafael J. Wysocki __setup("pcie_pme=", pcie_pme_setup);
40271fb719SRafael J. Wysocki
41271fb719SRafael J. Wysocki struct pcie_pme_service_data {
42271fb719SRafael J. Wysocki spinlock_t lock;
43271fb719SRafael J. Wysocki struct pcie_device *srv;
44271fb719SRafael J. Wysocki struct work_struct work;
45c7b5a4e6SRafael J. Wysocki bool noirq; /* If set, keep the PME interrupt disabled. */
46271fb719SRafael J. Wysocki };
47271fb719SRafael J. Wysocki
48271fb719SRafael J. Wysocki /**
49271fb719SRafael J. Wysocki * pcie_pme_interrupt_enable - Enable/disable PCIe PME interrupt generation.
50271fb719SRafael J. Wysocki * @dev: PCIe root port or event collector.
51271fb719SRafael J. Wysocki * @enable: Enable or disable the interrupt.
52271fb719SRafael J. Wysocki */
pcie_pme_interrupt_enable(struct pci_dev * dev,bool enable)53271fb719SRafael J. Wysocki void pcie_pme_interrupt_enable(struct pci_dev *dev, bool enable)
54271fb719SRafael J. Wysocki {
55271fb719SRafael J. Wysocki if (enable)
56263e54b9SJiang Liu pcie_capability_set_word(dev, PCI_EXP_RTCTL,
57263e54b9SJiang Liu PCI_EXP_RTCTL_PMEIE);
58271fb719SRafael J. Wysocki else
59263e54b9SJiang Liu pcie_capability_clear_word(dev, PCI_EXP_RTCTL,
60263e54b9SJiang Liu PCI_EXP_RTCTL_PMEIE);
61271fb719SRafael J. Wysocki }
62271fb719SRafael J. Wysocki
63271fb719SRafael J. Wysocki /**
64271fb719SRafael J. Wysocki * pcie_pme_walk_bus - Scan a PCI bus for devices asserting PME#.
65271fb719SRafael J. Wysocki * @bus: PCI bus to scan.
66271fb719SRafael J. Wysocki *
67271fb719SRafael J. Wysocki * Scan given PCI bus and all buses under it for devices asserting PME#.
68271fb719SRafael J. Wysocki */
pcie_pme_walk_bus(struct pci_bus * bus)69271fb719SRafael J. Wysocki static bool pcie_pme_walk_bus(struct pci_bus *bus)
70271fb719SRafael J. Wysocki {
71271fb719SRafael J. Wysocki struct pci_dev *dev;
72271fb719SRafael J. Wysocki bool ret = false;
73271fb719SRafael J. Wysocki
74271fb719SRafael J. Wysocki list_for_each_entry(dev, &bus->devices, bus_list) {
75271fb719SRafael J. Wysocki /* Skip PCIe devices in case we started from a root port. */
76271fb719SRafael J. Wysocki if (!pci_is_pcie(dev) && pci_check_pme_status(dev)) {
77379021d5SRafael J. Wysocki if (dev->pme_poll)
78379021d5SRafael J. Wysocki dev->pme_poll = false;
79379021d5SRafael J. Wysocki
80271fb719SRafael J. Wysocki pci_wakeup_event(dev);
810f953bf6SRafael J. Wysocki pm_request_resume(&dev->dev);
82271fb719SRafael J. Wysocki ret = true;
83271fb719SRafael J. Wysocki }
84271fb719SRafael J. Wysocki
85271fb719SRafael J. Wysocki if (dev->subordinate && pcie_pme_walk_bus(dev->subordinate))
86271fb719SRafael J. Wysocki ret = true;
87271fb719SRafael J. Wysocki }
88271fb719SRafael J. Wysocki
89271fb719SRafael J. Wysocki return ret;
90271fb719SRafael J. Wysocki }
91271fb719SRafael J. Wysocki
92271fb719SRafael J. Wysocki /**
93271fb719SRafael J. Wysocki * pcie_pme_from_pci_bridge - Check if PCIe-PCI bridge generated a PME.
94271fb719SRafael J. Wysocki * @bus: Secondary bus of the bridge.
95271fb719SRafael J. Wysocki * @devfn: Device/function number to check.
96271fb719SRafael J. Wysocki *
97271fb719SRafael J. Wysocki * PME from PCI devices under a PCIe-PCI bridge may be converted to an in-band
98271fb719SRafael J. Wysocki * PCIe PME message. In such that case the bridge should use the Requester ID
99271fb719SRafael J. Wysocki * of device/function number 0 on its secondary bus.
100271fb719SRafael J. Wysocki */
pcie_pme_from_pci_bridge(struct pci_bus * bus,u8 devfn)101271fb719SRafael J. Wysocki static bool pcie_pme_from_pci_bridge(struct pci_bus *bus, u8 devfn)
102271fb719SRafael J. Wysocki {
103271fb719SRafael J. Wysocki struct pci_dev *dev;
104271fb719SRafael J. Wysocki bool found = false;
105271fb719SRafael J. Wysocki
106271fb719SRafael J. Wysocki if (devfn)
107271fb719SRafael J. Wysocki return false;
108271fb719SRafael J. Wysocki
109271fb719SRafael J. Wysocki dev = pci_dev_get(bus->self);
110271fb719SRafael J. Wysocki if (!dev)
111271fb719SRafael J. Wysocki return false;
112271fb719SRafael J. Wysocki
11362f87c0eSYijing Wang if (pci_is_pcie(dev) && pci_pcie_type(dev) == PCI_EXP_TYPE_PCI_BRIDGE) {
114271fb719SRafael J. Wysocki down_read(&pci_bus_sem);
115271fb719SRafael J. Wysocki if (pcie_pme_walk_bus(bus))
116271fb719SRafael J. Wysocki found = true;
117271fb719SRafael J. Wysocki up_read(&pci_bus_sem);
118271fb719SRafael J. Wysocki }
119271fb719SRafael J. Wysocki
120271fb719SRafael J. Wysocki pci_dev_put(dev);
121271fb719SRafael J. Wysocki return found;
122271fb719SRafael J. Wysocki }
123271fb719SRafael J. Wysocki
124271fb719SRafael J. Wysocki /**
125271fb719SRafael J. Wysocki * pcie_pme_handle_request - Find device that generated PME and handle it.
126271fb719SRafael J. Wysocki * @port: Root port or event collector that generated the PME interrupt.
127271fb719SRafael J. Wysocki * @req_id: PCIe Requester ID of the device that generated the PME.
128271fb719SRafael J. Wysocki */
pcie_pme_handle_request(struct pci_dev * port,u16 req_id)129271fb719SRafael J. Wysocki static void pcie_pme_handle_request(struct pci_dev *port, u16 req_id)
130271fb719SRafael J. Wysocki {
131271fb719SRafael J. Wysocki u8 busnr = req_id >> 8, devfn = req_id & 0xff;
132271fb719SRafael J. Wysocki struct pci_bus *bus;
133271fb719SRafael J. Wysocki struct pci_dev *dev;
134271fb719SRafael J. Wysocki bool found = false;
135271fb719SRafael J. Wysocki
136271fb719SRafael J. Wysocki /* First, check if the PME is from the root port itself. */
137271fb719SRafael J. Wysocki if (port->devfn == devfn && port->bus->number == busnr) {
138379021d5SRafael J. Wysocki if (port->pme_poll)
139379021d5SRafael J. Wysocki port->pme_poll = false;
140379021d5SRafael J. Wysocki
141271fb719SRafael J. Wysocki if (pci_check_pme_status(port)) {
142271fb719SRafael J. Wysocki pm_request_resume(&port->dev);
143271fb719SRafael J. Wysocki found = true;
144271fb719SRafael J. Wysocki } else {
145271fb719SRafael J. Wysocki /*
146271fb719SRafael J. Wysocki * Apparently, the root port generated the PME on behalf
147271fb719SRafael J. Wysocki * of a non-PCIe device downstream. If this is done by
148271fb719SRafael J. Wysocki * a root port, the Requester ID field in its status
149271fb719SRafael J. Wysocki * register may contain either the root port's, or the
150271fb719SRafael J. Wysocki * source device's information (PCI Express Base
151271fb719SRafael J. Wysocki * Specification, Rev. 2.0, Section 6.1.9).
152271fb719SRafael J. Wysocki */
153271fb719SRafael J. Wysocki down_read(&pci_bus_sem);
154271fb719SRafael J. Wysocki found = pcie_pme_walk_bus(port->subordinate);
155271fb719SRafael J. Wysocki up_read(&pci_bus_sem);
156271fb719SRafael J. Wysocki }
157271fb719SRafael J. Wysocki goto out;
158271fb719SRafael J. Wysocki }
159271fb719SRafael J. Wysocki
160271fb719SRafael J. Wysocki /* Second, find the bus the source device is on. */
161271fb719SRafael J. Wysocki bus = pci_find_bus(pci_domain_nr(port->bus), busnr);
162271fb719SRafael J. Wysocki if (!bus)
163271fb719SRafael J. Wysocki goto out;
164271fb719SRafael J. Wysocki
165271fb719SRafael J. Wysocki /* Next, check if the PME is from a PCIe-PCI bridge. */
166271fb719SRafael J. Wysocki found = pcie_pme_from_pci_bridge(bus, devfn);
167271fb719SRafael J. Wysocki if (found)
168271fb719SRafael J. Wysocki goto out;
169271fb719SRafael J. Wysocki
170271fb719SRafael J. Wysocki /* Finally, try to find the PME source on the bus. */
171271fb719SRafael J. Wysocki down_read(&pci_bus_sem);
172271fb719SRafael J. Wysocki list_for_each_entry(dev, &bus->devices, bus_list) {
173271fb719SRafael J. Wysocki pci_dev_get(dev);
174271fb719SRafael J. Wysocki if (dev->devfn == devfn) {
175271fb719SRafael J. Wysocki found = true;
176271fb719SRafael J. Wysocki break;
177271fb719SRafael J. Wysocki }
178271fb719SRafael J. Wysocki pci_dev_put(dev);
179271fb719SRafael J. Wysocki }
180271fb719SRafael J. Wysocki up_read(&pci_bus_sem);
181271fb719SRafael J. Wysocki
182271fb719SRafael J. Wysocki if (found) {
183271fb719SRafael J. Wysocki /* The device is there, but we have to check its PME status. */
184271fb719SRafael J. Wysocki found = pci_check_pme_status(dev);
185271fb719SRafael J. Wysocki if (found) {
186379021d5SRafael J. Wysocki if (dev->pme_poll)
187379021d5SRafael J. Wysocki dev->pme_poll = false;
188379021d5SRafael J. Wysocki
189271fb719SRafael J. Wysocki pci_wakeup_event(dev);
1900f953bf6SRafael J. Wysocki pm_request_resume(&dev->dev);
191271fb719SRafael J. Wysocki }
192271fb719SRafael J. Wysocki pci_dev_put(dev);
193271fb719SRafael J. Wysocki } else if (devfn) {
194271fb719SRafael J. Wysocki /*
195271fb719SRafael J. Wysocki * The device is not there, but we can still try to recover by
196271fb719SRafael J. Wysocki * assuming that the PME was reported by a PCIe-PCI bridge that
197271fb719SRafael J. Wysocki * used devfn different from zero.
198271fb719SRafael J. Wysocki */
19900ebf134SFrederick Lawler pci_info(port, "interrupt generated for non-existent device %02x:%02x.%d\n",
200271fb719SRafael J. Wysocki busnr, PCI_SLOT(devfn), PCI_FUNC(devfn));
201271fb719SRafael J. Wysocki found = pcie_pme_from_pci_bridge(bus, 0);
202271fb719SRafael J. Wysocki }
203271fb719SRafael J. Wysocki
204271fb719SRafael J. Wysocki out:
205271fb719SRafael J. Wysocki if (!found)
20600ebf134SFrederick Lawler pci_info(port, "Spurious native interrupt!\n");
207271fb719SRafael J. Wysocki }
208271fb719SRafael J. Wysocki
209271fb719SRafael J. Wysocki /**
210271fb719SRafael J. Wysocki * pcie_pme_work_fn - Work handler for PCIe PME interrupt.
211271fb719SRafael J. Wysocki * @work: Work structure giving access to service data.
212271fb719SRafael J. Wysocki */
pcie_pme_work_fn(struct work_struct * work)213271fb719SRafael J. Wysocki static void pcie_pme_work_fn(struct work_struct *work)
214271fb719SRafael J. Wysocki {
215271fb719SRafael J. Wysocki struct pcie_pme_service_data *data =
216271fb719SRafael J. Wysocki container_of(work, struct pcie_pme_service_data, work);
217271fb719SRafael J. Wysocki struct pci_dev *port = data->srv->port;
218271fb719SRafael J. Wysocki u32 rtsta;
219271fb719SRafael J. Wysocki
220271fb719SRafael J. Wysocki spin_lock_irq(&data->lock);
221271fb719SRafael J. Wysocki
222271fb719SRafael J. Wysocki for (;;) {
223c7b5a4e6SRafael J. Wysocki if (data->noirq)
224271fb719SRafael J. Wysocki break;
225271fb719SRafael J. Wysocki
226263e54b9SJiang Liu pcie_capability_read_dword(port, PCI_EXP_RTSTA, &rtsta);
227*aa66ea10SNaveen Naidu if (PCI_POSSIBLE_ERROR(rtsta))
2283ad3f8ceSQiang break;
2293ad3f8ceSQiang
230271fb719SRafael J. Wysocki if (rtsta & PCI_EXP_RTSTA_PME) {
231271fb719SRafael J. Wysocki /*
232271fb719SRafael J. Wysocki * Clear PME status of the port. If there are other
233271fb719SRafael J. Wysocki * pending PMEs, the status will be set again.
234271fb719SRafael J. Wysocki */
235fe31e697SRafael J. Wysocki pcie_clear_root_pme_status(port);
236271fb719SRafael J. Wysocki
237271fb719SRafael J. Wysocki spin_unlock_irq(&data->lock);
238271fb719SRafael J. Wysocki pcie_pme_handle_request(port, rtsta & 0xffff);
239271fb719SRafael J. Wysocki spin_lock_irq(&data->lock);
240271fb719SRafael J. Wysocki
241271fb719SRafael J. Wysocki continue;
242271fb719SRafael J. Wysocki }
243271fb719SRafael J. Wysocki
244271fb719SRafael J. Wysocki /* No need to loop if there are no more PMEs pending. */
245271fb719SRafael J. Wysocki if (!(rtsta & PCI_EXP_RTSTA_PENDING))
246271fb719SRafael J. Wysocki break;
247271fb719SRafael J. Wysocki
248271fb719SRafael J. Wysocki spin_unlock_irq(&data->lock);
249271fb719SRafael J. Wysocki cpu_relax();
250271fb719SRafael J. Wysocki spin_lock_irq(&data->lock);
251271fb719SRafael J. Wysocki }
252271fb719SRafael J. Wysocki
253c7b5a4e6SRafael J. Wysocki if (!data->noirq)
254271fb719SRafael J. Wysocki pcie_pme_interrupt_enable(port, true);
255271fb719SRafael J. Wysocki
256271fb719SRafael J. Wysocki spin_unlock_irq(&data->lock);
257271fb719SRafael J. Wysocki }
258271fb719SRafael J. Wysocki
259271fb719SRafael J. Wysocki /**
260271fb719SRafael J. Wysocki * pcie_pme_irq - Interrupt handler for PCIe root port PME interrupt.
261271fb719SRafael J. Wysocki * @irq: Interrupt vector.
262271fb719SRafael J. Wysocki * @context: Interrupt context pointer.
263271fb719SRafael J. Wysocki */
pcie_pme_irq(int irq,void * context)264271fb719SRafael J. Wysocki static irqreturn_t pcie_pme_irq(int irq, void *context)
265271fb719SRafael J. Wysocki {
266271fb719SRafael J. Wysocki struct pci_dev *port;
267271fb719SRafael J. Wysocki struct pcie_pme_service_data *data;
268271fb719SRafael J. Wysocki u32 rtsta;
269271fb719SRafael J. Wysocki unsigned long flags;
270271fb719SRafael J. Wysocki
271271fb719SRafael J. Wysocki port = ((struct pcie_device *)context)->port;
272271fb719SRafael J. Wysocki data = get_service_data((struct pcie_device *)context);
273271fb719SRafael J. Wysocki
274271fb719SRafael J. Wysocki spin_lock_irqsave(&data->lock, flags);
275263e54b9SJiang Liu pcie_capability_read_dword(port, PCI_EXP_RTSTA, &rtsta);
276271fb719SRafael J. Wysocki
277*aa66ea10SNaveen Naidu if (PCI_POSSIBLE_ERROR(rtsta) || !(rtsta & PCI_EXP_RTSTA_PME)) {
278271fb719SRafael J. Wysocki spin_unlock_irqrestore(&data->lock, flags);
279271fb719SRafael J. Wysocki return IRQ_NONE;
280271fb719SRafael J. Wysocki }
281271fb719SRafael J. Wysocki
282271fb719SRafael J. Wysocki pcie_pme_interrupt_enable(port, false);
283271fb719SRafael J. Wysocki spin_unlock_irqrestore(&data->lock, flags);
284271fb719SRafael J. Wysocki
285271fb719SRafael J. Wysocki /* We don't use pm_wq, because it's freezable. */
286271fb719SRafael J. Wysocki schedule_work(&data->work);
287271fb719SRafael J. Wysocki
288271fb719SRafael J. Wysocki return IRQ_HANDLED;
289271fb719SRafael J. Wysocki }
290271fb719SRafael J. Wysocki
291271fb719SRafael J. Wysocki /**
2928370c2dcSRafael J. Wysocki * pcie_pme_can_wakeup - Set the wakeup capability flag.
293271fb719SRafael J. Wysocki * @dev: PCI device to handle.
294271fb719SRafael J. Wysocki * @ign: Ignored.
295271fb719SRafael J. Wysocki */
pcie_pme_can_wakeup(struct pci_dev * dev,void * ign)2968370c2dcSRafael J. Wysocki static int pcie_pme_can_wakeup(struct pci_dev *dev, void *ign)
297271fb719SRafael J. Wysocki {
298de3ef1ebSRafael J. Wysocki device_set_wakeup_capable(&dev->dev, true);
299271fb719SRafael J. Wysocki return 0;
300271fb719SRafael J. Wysocki }
301271fb719SRafael J. Wysocki
302271fb719SRafael J. Wysocki /**
3038370c2dcSRafael J. Wysocki * pcie_pme_mark_devices - Set the wakeup flag for devices below a port.
304271fb719SRafael J. Wysocki * @port: PCIe root port or event collector to handle.
305271fb719SRafael J. Wysocki *
306271fb719SRafael J. Wysocki * For each device below given root port, including the port itself (or for each
307271fb719SRafael J. Wysocki * root complex integrated endpoint if @port is a root complex event collector)
3088370c2dcSRafael J. Wysocki * set the flag indicating that it can signal run-time wake-up events.
309271fb719SRafael J. Wysocki */
pcie_pme_mark_devices(struct pci_dev * port)310271fb719SRafael J. Wysocki static void pcie_pme_mark_devices(struct pci_dev *port)
311271fb719SRafael J. Wysocki {
3128370c2dcSRafael J. Wysocki pcie_pme_can_wakeup(port, NULL);
3139a2f604fSSean V Kelley
3149a2f604fSSean V Kelley if (pci_pcie_type(port) == PCI_EXP_TYPE_RC_EC)
3159a2f604fSSean V Kelley pcie_walk_rcec(port, pcie_pme_can_wakeup, NULL);
3169a2f604fSSean V Kelley else if (port->subordinate)
3178370c2dcSRafael J. Wysocki pci_walk_bus(port->subordinate, pcie_pme_can_wakeup, NULL);
318271fb719SRafael J. Wysocki }
319271fb719SRafael J. Wysocki
320271fb719SRafael J. Wysocki /**
321271fb719SRafael J. Wysocki * pcie_pme_probe - Initialize PCIe PME service for given root port.
322271fb719SRafael J. Wysocki * @srv: PCIe service to initialize.
323271fb719SRafael J. Wysocki */
pcie_pme_probe(struct pcie_device * srv)324271fb719SRafael J. Wysocki static int pcie_pme_probe(struct pcie_device *srv)
325271fb719SRafael J. Wysocki {
3269a2f604fSSean V Kelley struct pci_dev *port = srv->port;
327271fb719SRafael J. Wysocki struct pcie_pme_service_data *data;
3289a2f604fSSean V Kelley int type = pci_pcie_type(port);
329271fb719SRafael J. Wysocki int ret;
330271fb719SRafael J. Wysocki
3319a2f604fSSean V Kelley /* Limit to Root Ports or Root Complex Event Collectors */
3329a2f604fSSean V Kelley if (type != PCI_EXP_TYPE_RC_EC &&
3339a2f604fSSean V Kelley type != PCI_EXP_TYPE_ROOT_PORT)
3349a2f604fSSean V Kelley return -ENODEV;
3359a2f604fSSean V Kelley
336271fb719SRafael J. Wysocki data = kzalloc(sizeof(*data), GFP_KERNEL);
337271fb719SRafael J. Wysocki if (!data)
338271fb719SRafael J. Wysocki return -ENOMEM;
339271fb719SRafael J. Wysocki
340271fb719SRafael J. Wysocki spin_lock_init(&data->lock);
341271fb719SRafael J. Wysocki INIT_WORK(&data->work, pcie_pme_work_fn);
342271fb719SRafael J. Wysocki data->srv = srv;
343271fb719SRafael J. Wysocki set_service_data(srv, data);
344271fb719SRafael J. Wysocki
345271fb719SRafael J. Wysocki pcie_pme_interrupt_enable(port, false);
346fe31e697SRafael J. Wysocki pcie_clear_root_pme_status(port);
347271fb719SRafael J. Wysocki
348271fb719SRafael J. Wysocki ret = request_irq(srv->irq, pcie_pme_irq, IRQF_SHARED, "PCIe PME", srv);
349271fb719SRafael J. Wysocki if (ret) {
350271fb719SRafael J. Wysocki kfree(data);
351a902d81aSBjorn Helgaas return ret;
352271fb719SRafael J. Wysocki }
353271fb719SRafael J. Wysocki
35400ebf134SFrederick Lawler pci_info(port, "Signaling with IRQ %d\n", srv->irq);
355a902d81aSBjorn Helgaas
356a902d81aSBjorn Helgaas pcie_pme_mark_devices(port);
357a902d81aSBjorn Helgaas pcie_pme_interrupt_enable(port, true);
358a902d81aSBjorn Helgaas return 0;
359271fb719SRafael J. Wysocki }
360271fb719SRafael J. Wysocki
pcie_pme_check_wakeup(struct pci_bus * bus)36176cde7e4SRafael J. Wysocki static bool pcie_pme_check_wakeup(struct pci_bus *bus)
36276cde7e4SRafael J. Wysocki {
36376cde7e4SRafael J. Wysocki struct pci_dev *dev;
36476cde7e4SRafael J. Wysocki
36576cde7e4SRafael J. Wysocki if (!bus)
36676cde7e4SRafael J. Wysocki return false;
36776cde7e4SRafael J. Wysocki
36876cde7e4SRafael J. Wysocki list_for_each_entry(dev, &bus->devices, bus_list)
36976cde7e4SRafael J. Wysocki if (device_may_wakeup(&dev->dev)
37076cde7e4SRafael J. Wysocki || pcie_pme_check_wakeup(dev->subordinate))
37176cde7e4SRafael J. Wysocki return true;
37276cde7e4SRafael J. Wysocki
37376cde7e4SRafael J. Wysocki return false;
37476cde7e4SRafael J. Wysocki }
37576cde7e4SRafael J. Wysocki
pcie_pme_disable_interrupt(struct pci_dev * port,struct pcie_pme_service_data * data)37695c80bc6SRafael J. Wysocki static void pcie_pme_disable_interrupt(struct pci_dev *port,
37795c80bc6SRafael J. Wysocki struct pcie_pme_service_data *data)
37895c80bc6SRafael J. Wysocki {
37995c80bc6SRafael J. Wysocki spin_lock_irq(&data->lock);
38095c80bc6SRafael J. Wysocki pcie_pme_interrupt_enable(port, false);
38195c80bc6SRafael J. Wysocki pcie_clear_root_pme_status(port);
38295c80bc6SRafael J. Wysocki data->noirq = true;
38395c80bc6SRafael J. Wysocki spin_unlock_irq(&data->lock);
38495c80bc6SRafael J. Wysocki }
38595c80bc6SRafael J. Wysocki
386271fb719SRafael J. Wysocki /**
387271fb719SRafael J. Wysocki * pcie_pme_suspend - Suspend PCIe PME service device.
388271fb719SRafael J. Wysocki * @srv: PCIe service device to suspend.
389271fb719SRafael J. Wysocki */
pcie_pme_suspend(struct pcie_device * srv)390271fb719SRafael J. Wysocki static int pcie_pme_suspend(struct pcie_device *srv)
391271fb719SRafael J. Wysocki {
392271fb719SRafael J. Wysocki struct pcie_pme_service_data *data = get_service_data(srv);
393271fb719SRafael J. Wysocki struct pci_dev *port = srv->port;
394c7b5a4e6SRafael J. Wysocki bool wakeup;
3955dfd7f9fSLucas Stach int ret;
396271fb719SRafael J. Wysocki
39776cde7e4SRafael J. Wysocki if (device_may_wakeup(&port->dev)) {
39876cde7e4SRafael J. Wysocki wakeup = true;
39976cde7e4SRafael J. Wysocki } else {
40076cde7e4SRafael J. Wysocki down_read(&pci_bus_sem);
40176cde7e4SRafael J. Wysocki wakeup = pcie_pme_check_wakeup(port->subordinate);
40276cde7e4SRafael J. Wysocki up_read(&pci_bus_sem);
40376cde7e4SRafael J. Wysocki }
40476cde7e4SRafael J. Wysocki if (wakeup) {
4055dfd7f9fSLucas Stach ret = enable_irq_wake(srv->irq);
406c7b5a4e6SRafael J. Wysocki if (!ret)
407c7b5a4e6SRafael J. Wysocki return 0;
4085dfd7f9fSLucas Stach }
409c7b5a4e6SRafael J. Wysocki
41095c80bc6SRafael J. Wysocki pcie_pme_disable_interrupt(port, data);
411271fb719SRafael J. Wysocki
412271fb719SRafael J. Wysocki synchronize_irq(srv->irq);
413271fb719SRafael J. Wysocki
414271fb719SRafael J. Wysocki return 0;
415271fb719SRafael J. Wysocki }
416271fb719SRafael J. Wysocki
417271fb719SRafael J. Wysocki /**
418271fb719SRafael J. Wysocki * pcie_pme_resume - Resume PCIe PME service device.
4195dda3ba6SJay Fang * @srv: PCIe service device to resume.
420271fb719SRafael J. Wysocki */
pcie_pme_resume(struct pcie_device * srv)421271fb719SRafael J. Wysocki static int pcie_pme_resume(struct pcie_device *srv)
422271fb719SRafael J. Wysocki {
423271fb719SRafael J. Wysocki struct pcie_pme_service_data *data = get_service_data(srv);
424271fb719SRafael J. Wysocki
425271fb719SRafael J. Wysocki spin_lock_irq(&data->lock);
426c7b5a4e6SRafael J. Wysocki if (data->noirq) {
42776cde7e4SRafael J. Wysocki struct pci_dev *port = srv->port;
42876cde7e4SRafael J. Wysocki
429fe31e697SRafael J. Wysocki pcie_clear_root_pme_status(port);
430271fb719SRafael J. Wysocki pcie_pme_interrupt_enable(port, true);
431c7b5a4e6SRafael J. Wysocki data->noirq = false;
43276cde7e4SRafael J. Wysocki } else {
43376cde7e4SRafael J. Wysocki disable_irq_wake(srv->irq);
43476cde7e4SRafael J. Wysocki }
435271fb719SRafael J. Wysocki spin_unlock_irq(&data->lock);
436271fb719SRafael J. Wysocki
437271fb719SRafael J. Wysocki return 0;
438271fb719SRafael J. Wysocki }
439271fb719SRafael J. Wysocki
440afe3e4d1SYinghai Lu /**
441afe3e4d1SYinghai Lu * pcie_pme_remove - Prepare PCIe PME service device for removal.
4425dda3ba6SJay Fang * @srv: PCIe service device to remove.
443afe3e4d1SYinghai Lu */
pcie_pme_remove(struct pcie_device * srv)444afe3e4d1SYinghai Lu static void pcie_pme_remove(struct pcie_device *srv)
445afe3e4d1SYinghai Lu {
44695c80bc6SRafael J. Wysocki struct pcie_pme_service_data *data = get_service_data(srv);
44795c80bc6SRafael J. Wysocki
44895c80bc6SRafael J. Wysocki pcie_pme_disable_interrupt(srv->port, data);
449afe3e4d1SYinghai Lu free_irq(srv->irq, srv);
4507cf58b79SSven Van Asbroeck cancel_work_sync(&data->work);
45195c80bc6SRafael J. Wysocki kfree(data);
452afe3e4d1SYinghai Lu }
453afe3e4d1SYinghai Lu
454271fb719SRafael J. Wysocki static struct pcie_port_service_driver pcie_pme_driver = {
455271fb719SRafael J. Wysocki .name = "pcie_pme",
4569a2f604fSSean V Kelley .port_type = PCIE_ANY_PORT,
457271fb719SRafael J. Wysocki .service = PCIE_PORT_SERVICE_PME,
458271fb719SRafael J. Wysocki
459271fb719SRafael J. Wysocki .probe = pcie_pme_probe,
460271fb719SRafael J. Wysocki .suspend = pcie_pme_suspend,
461271fb719SRafael J. Wysocki .resume = pcie_pme_resume,
462afe3e4d1SYinghai Lu .remove = pcie_pme_remove,
463271fb719SRafael J. Wysocki };
464271fb719SRafael J. Wysocki
465271fb719SRafael J. Wysocki /**
46643395d9eSKrzysztof Wilczyński * pcie_pme_init - Register the PCIe PME service driver.
467271fb719SRafael J. Wysocki */
pcie_pme_init(void)468c29de841SKeith Busch int __init pcie_pme_init(void)
469271fb719SRafael J. Wysocki {
470271fb719SRafael J. Wysocki return pcie_port_service_register(&pcie_pme_driver);
471271fb719SRafael J. Wysocki }
472