xref: /openbmc/linux/drivers/pci/pcie/pme.c (revision 03ab8e6297acd1bc0eedaa050e2a1635c576fd11)
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