summaryrefslogtreecommitdiff
path: root/drivers/pci/pcie/pme.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pci/pcie/pme.c')
-rw-r--r--drivers/pci/pcie/pme.c88
1 files changed, 41 insertions, 47 deletions
diff --git a/drivers/pci/pcie/pme.c b/drivers/pci/pcie/pme.c
index 0dbcf429089f..a2daebd9806c 100644
--- a/drivers/pci/pcie/pme.c
+++ b/drivers/pci/pcie/pme.c
@@ -7,6 +7,9 @@
* Copyright (C) 2009 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc.
*/
+#define dev_fmt(fmt) "PME: " fmt
+
+#include <linux/bitfield.h>
#include <linux/pci.h>
#include <linux/kernel.h>
#include <linux/errno.h>
@@ -194,14 +197,14 @@ static void pcie_pme_handle_request(struct pci_dev *port, u16 req_id)
* assuming that the PME was reported by a PCIe-PCI bridge that
* used devfn different from zero.
*/
- pci_dbg(port, "PME interrupt generated for non-existent device %02x:%02x.%d\n",
- busnr, PCI_SLOT(devfn), PCI_FUNC(devfn));
+ pci_info(port, "interrupt generated for non-existent device %02x:%02x.%d\n",
+ busnr, PCI_SLOT(devfn), PCI_FUNC(devfn));
found = pcie_pme_from_pci_bridge(bus, 0);
}
out:
if (!found)
- pci_dbg(port, "Spurious native PME interrupt!\n");
+ pci_info(port, "Spurious native interrupt!\n");
}
/**
@@ -222,7 +225,7 @@ static void pcie_pme_work_fn(struct work_struct *work)
break;
pcie_capability_read_dword(port, PCI_EXP_RTSTA, &rtsta);
- if (rtsta == (u32) ~0)
+ if (PCI_POSSIBLE_ERROR(rtsta))
break;
if (rtsta & PCI_EXP_RTSTA_PME) {
@@ -233,7 +236,8 @@ static void pcie_pme_work_fn(struct work_struct *work)
pcie_clear_root_pme_status(port);
spin_unlock_irq(&data->lock);
- pcie_pme_handle_request(port, rtsta & 0xffff);
+ pcie_pme_handle_request(port,
+ FIELD_GET(PCI_EXP_RTSTA_PME_RQ_ID, rtsta));
spin_lock_irq(&data->lock);
continue;
@@ -272,7 +276,7 @@ static irqreturn_t pcie_pme_irq(int irq, void *context)
spin_lock_irqsave(&data->lock, flags);
pcie_capability_read_dword(port, PCI_EXP_RTSTA, &rtsta);
- if (rtsta == (u32) ~0 || !(rtsta & PCI_EXP_RTSTA_PME)) {
+ if (PCI_POSSIBLE_ERROR(rtsta) || !(rtsta & PCI_EXP_RTSTA_PME)) {
spin_unlock_irqrestore(&data->lock, flags);
return IRQ_NONE;
}
@@ -308,7 +312,10 @@ static int pcie_pme_can_wakeup(struct pci_dev *dev, void *ign)
static void pcie_pme_mark_devices(struct pci_dev *port)
{
pcie_pme_can_wakeup(port, NULL);
- if (port->subordinate)
+
+ if (pci_pcie_type(port) == PCI_EXP_TYPE_RC_EC)
+ pcie_walk_rcec(port, pcie_pme_can_wakeup, NULL);
+ else if (port->subordinate)
pci_walk_bus(port->subordinate, pcie_pme_can_wakeup, NULL);
}
@@ -318,10 +325,16 @@ static void pcie_pme_mark_devices(struct pci_dev *port)
*/
static int pcie_pme_probe(struct pcie_device *srv)
{
- struct pci_dev *port;
+ struct pci_dev *port = srv->port;
struct pcie_pme_service_data *data;
+ int type = pci_pcie_type(port);
int ret;
+ /* Limit to Root Ports or Root Complex Event Collectors */
+ if (type != PCI_EXP_TYPE_RC_EC &&
+ type != PCI_EXP_TYPE_ROOT_PORT)
+ return -ENODEV;
+
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
@@ -331,7 +344,6 @@ static int pcie_pme_probe(struct pcie_device *srv)
data->srv = srv;
set_service_data(srv, data);
- port = srv->port;
pcie_pme_interrupt_enable(port, false);
pcie_clear_root_pme_status(port);
@@ -341,7 +353,7 @@ static int pcie_pme_probe(struct pcie_device *srv)
return ret;
}
- pci_info(port, "Signaling PME with IRQ %d\n", srv->irq);
+ pci_info(port, "Signaling with IRQ %d\n", srv->irq);
pcie_pme_mark_devices(port);
pcie_pme_interrupt_enable(port, true);
@@ -363,6 +375,16 @@ static bool pcie_pme_check_wakeup(struct pci_bus *bus)
return false;
}
+static void pcie_pme_disable_interrupt(struct pci_dev *port,
+ struct pcie_pme_service_data *data)
+{
+ spin_lock_irq(&data->lock);
+ pcie_pme_interrupt_enable(port, false);
+ pcie_clear_root_pme_status(port);
+ data->noirq = true;
+ spin_unlock_irq(&data->lock);
+}
+
/**
* pcie_pme_suspend - Suspend PCIe PME service device.
* @srv: PCIe service device to suspend.
@@ -387,11 +409,7 @@ static int pcie_pme_suspend(struct pcie_device *srv)
return 0;
}
- spin_lock_irq(&data->lock);
- pcie_pme_interrupt_enable(port, false);
- pcie_clear_root_pme_status(port);
- data->noirq = true;
- spin_unlock_irq(&data->lock);
+ pcie_pme_disable_interrupt(port, data);
synchronize_irq(srv->irq);
@@ -400,7 +418,7 @@ static int pcie_pme_suspend(struct pcie_device *srv)
/**
* pcie_pme_resume - Resume PCIe PME service device.
- * @srv - PCIe service device to resume.
+ * @srv: PCIe service device to resume.
*/
static int pcie_pme_resume(struct pcie_device *srv)
{
@@ -423,55 +441,31 @@ static int pcie_pme_resume(struct pcie_device *srv)
/**
* pcie_pme_remove - Prepare PCIe PME service device for removal.
- * @srv - PCIe service device to remove.
+ * @srv: PCIe service device to remove.
*/
static void pcie_pme_remove(struct pcie_device *srv)
{
- pcie_pme_suspend(srv);
- free_irq(srv->irq, srv);
- kfree(get_service_data(srv));
-}
-
-static int pcie_pme_runtime_suspend(struct pcie_device *srv)
-{
- struct pcie_pme_service_data *data = get_service_data(srv);
-
- spin_lock_irq(&data->lock);
- pcie_pme_interrupt_enable(srv->port, false);
- pcie_clear_root_pme_status(srv->port);
- data->noirq = true;
- spin_unlock_irq(&data->lock);
-
- return 0;
-}
-
-static int pcie_pme_runtime_resume(struct pcie_device *srv)
-{
struct pcie_pme_service_data *data = get_service_data(srv);
- spin_lock_irq(&data->lock);
- pcie_pme_interrupt_enable(srv->port, true);
- data->noirq = false;
- spin_unlock_irq(&data->lock);
-
- return 0;
+ pcie_pme_disable_interrupt(srv->port, data);
+ free_irq(srv->irq, srv);
+ cancel_work_sync(&data->work);
+ kfree(data);
}
static struct pcie_port_service_driver pcie_pme_driver = {
.name = "pcie_pme",
- .port_type = PCI_EXP_TYPE_ROOT_PORT,
+ .port_type = PCIE_ANY_PORT,
.service = PCIE_PORT_SERVICE_PME,
.probe = pcie_pme_probe,
.suspend = pcie_pme_suspend,
- .runtime_suspend = pcie_pme_runtime_suspend,
- .runtime_resume = pcie_pme_runtime_resume,
.resume = pcie_pme_resume,
.remove = pcie_pme_remove,
};
/**
- * pcie_pme_service_init - Register the PCIe PME service driver.
+ * pcie_pme_init - Register the PCIe PME service driver.
*/
int __init pcie_pme_init(void)
{