/* * If this switch is set, MSI will not be used for PCIe PME signaling. This * causes the PCIe port driver to use INTx interrupts only, but it turns out * that using MSI for PCIe PME signaling doesn't play well with PCIe PME-based * wake-up from system sleep states.
*/ bool pcie_pme_msi_disabled;
/** * pcie_pme_interrupt_enable - Enable/disable PCIe PME interrupt generation. * @dev: PCIe root port or event collector. * @enable: Enable or disable the interrupt.
*/ void pcie_pme_interrupt_enable(struct pci_dev *dev, bool enable)
{ if (enable)
pcie_capability_set_word(dev, PCI_EXP_RTCTL,
PCI_EXP_RTCTL_PMEIE); else
pcie_capability_clear_word(dev, PCI_EXP_RTCTL,
PCI_EXP_RTCTL_PMEIE);
}
/** * pcie_pme_walk_bus - Scan a PCI bus for devices asserting PME#. * @bus: PCI bus to scan. * * Scan given PCI bus and all buses under it for devices asserting PME#.
*/ staticbool pcie_pme_walk_bus(struct pci_bus *bus)
{ struct pci_dev *dev; bool ret = false;
list_for_each_entry(dev, &bus->devices, bus_list) { /* Skip PCIe devices in case we started from a root port. */ if (!pci_is_pcie(dev) && pci_check_pme_status(dev)) { if (dev->pme_poll)
dev->pme_poll = false;
pci_wakeup_event(dev);
pm_request_resume(&dev->dev);
ret = true;
}
if (dev->subordinate && pcie_pme_walk_bus(dev->subordinate))
ret = true;
}
return ret;
}
/** * pcie_pme_from_pci_bridge - Check if PCIe-PCI bridge generated a PME. * @bus: Secondary bus of the bridge. * @devfn: Device/function number to check. * * PME from PCI devices under a PCIe-PCI bridge may be converted to an in-band * PCIe PME message. In such that case the bridge should use the Requester ID * of device/function number 0 on its secondary bus.
*/ staticbool pcie_pme_from_pci_bridge(struct pci_bus *bus, u8 devfn)
{ struct pci_dev *dev; bool found = false;
if (devfn) returnfalse;
dev = pci_dev_get(bus->self); if (!dev) returnfalse;
if (pci_is_pcie(dev) && pci_pcie_type(dev) == PCI_EXP_TYPE_PCI_BRIDGE) {
down_read(&pci_bus_sem); if (pcie_pme_walk_bus(bus))
found = true;
up_read(&pci_bus_sem);
}
pci_dev_put(dev); return found;
}
/** * pcie_pme_handle_request - Find device that generated PME and handle it. * @port: Root port or event collector that generated the PME interrupt. * @req_id: PCIe Requester ID of the device that generated the PME.
*/ staticvoid pcie_pme_handle_request(struct pci_dev *port, u16 req_id)
{
u8 busnr = req_id >> 8, devfn = req_id & 0xff; struct pci_bus *bus; struct pci_dev *dev; bool found = false;
/* First, check if the PME is from the root port itself. */ if (port->devfn == devfn && port->bus->number == busnr) { if (port->pme_poll)
port->pme_poll = false;
if (pci_check_pme_status(port)) {
pm_request_resume(&port->dev);
found = true;
} else { /* * Apparently, the root port generated the PME on behalf * of a non-PCIe device downstream. If this is done by * a root port, the Requester ID field in its status * register may contain either the root port's, or the * source device's information (PCI Express Base * Specification, Rev. 2.0, Section 6.1.9).
*/
down_read(&pci_bus_sem);
found = pcie_pme_walk_bus(port->subordinate);
up_read(&pci_bus_sem);
} goto out;
}
/* Second, find the bus the source device is on. */
bus = pci_find_bus(pci_domain_nr(port->bus), busnr); if (!bus) goto out;
/* Next, check if the PME is from a PCIe-PCI bridge. */
found = pcie_pme_from_pci_bridge(bus, devfn); if (found) goto out;
/* Finally, try to find the PME source on the bus. */
down_read(&pci_bus_sem);
list_for_each_entry(dev, &bus->devices, bus_list) {
pci_dev_get(dev); if (dev->devfn == devfn) {
found = true; break;
}
pci_dev_put(dev);
}
up_read(&pci_bus_sem);
if (found) { /* The device is there, but we have to check its PME status. */
found = pci_check_pme_status(dev); if (found) { if (dev->pme_poll)
dev->pme_poll = false;
pci_wakeup_event(dev);
pm_request_resume(&dev->dev);
}
pci_dev_put(dev);
} elseif (devfn) { /* * The device is not there, but we can still try to recover by * assuming that the PME was reported by a PCIe-PCI bridge that * used devfn different from zero.
*/
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_info(port, "Spurious native interrupt!\n");
}
/** * pcie_pme_work_fn - Work handler for PCIe PME interrupt. * @work: Work structure giving access to service data.
*/ staticvoid pcie_pme_work_fn(struct work_struct *work)
{ struct pcie_pme_service_data *data =
container_of(work, struct pcie_pme_service_data, work); struct pci_dev *port = data->srv->port;
u32 rtsta;
spin_lock_irq(&data->lock);
for (;;) { if (data->noirq) break;
pcie_capability_read_dword(port, PCI_EXP_RTSTA, &rtsta); if (PCI_POSSIBLE_ERROR(rtsta)) break;
if (rtsta & PCI_EXP_RTSTA_PME) { /* * Clear PME status of the port. If there are other * pending PMEs, the status will be set again.
*/
pcie_clear_root_pme_status(port);
/** * pcie_pme_mark_devices - Set the wakeup flag for devices below a port. * @port: PCIe root port or event collector to handle. * * For each device below given root port, including the port itself (or for each * root complex integrated endpoint if @port is a root complex event collector) * set the flag indicating that it can signal run-time wake-up events.
*/ staticvoid pcie_pme_mark_devices(struct pci_dev *port)
{
pcie_pme_can_wakeup(port, NULL);
/** * pcie_pme_probe - Initialize PCIe PME service for given root port. * @srv: PCIe service to initialize.
*/ staticint pcie_pme_probe(struct pcie_device *srv)
{ 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;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.