c7f486567c
PCIe native PME detection mechanism is based on interrupts generated by root ports or event collectors every time a PCIe device sends a PME message upstream. Once a PME message has been sent by an endpoint device and received by its root port (or event collector in the case of root complex integrated endpoints), the Requester ID from the message header is registered in the root port's Root Status register. At the same time, the PME Status bit of the Root Status register is set to indicate that there's a PME to handle. If PCIe PME interrupt is enabled for the root port, it generates an interrupt once the PME Status has been set. After receiving the interrupt, the kernel can identify the PCIe device that generated the PME using the Requester ID from the root port's Root Status register. [For details, see PCI Express Base Specification, Rev. 2.0.] Implement a driver for the PCIe PME root port service working in accordance with the above description. Based on a patch from Shaohua Li <shaohua.li@intel.com>. Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl> Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
54 lines
1.5 KiB
C
54 lines
1.5 KiB
C
/*
|
|
* PCIe Native PME support, ACPI-related part
|
|
*
|
|
* Copyright (C) 2009 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc.
|
|
*
|
|
* This file is subject to the terms and conditions of the GNU General Public
|
|
* License V2. See the file "COPYING" in the main directory of this archive
|
|
* for more details.
|
|
*/
|
|
|
|
#include <linux/pci.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/acpi.h>
|
|
#include <linux/pci-acpi.h>
|
|
#include <linux/pcieport_if.h>
|
|
|
|
/**
|
|
* pcie_pme_acpi_setup - Request the ACPI BIOS to release control over PCIe PME.
|
|
* @srv - PCIe PME service for a root port or event collector.
|
|
*
|
|
* Invoked when the PCIe bus type loads PCIe PME service driver. To avoid
|
|
* conflict with the BIOS PCIe support requires the BIOS to yield PCIe PME
|
|
* control to the kernel.
|
|
*/
|
|
int pcie_pme_acpi_setup(struct pcie_device *srv)
|
|
{
|
|
acpi_status status = AE_NOT_FOUND;
|
|
struct pci_dev *port = srv->port;
|
|
acpi_handle handle;
|
|
int error = 0;
|
|
|
|
if (acpi_pci_disabled)
|
|
return -ENOSYS;
|
|
|
|
dev_info(&port->dev, "Requesting control of PCIe PME from ACPI BIOS\n");
|
|
|
|
handle = acpi_find_root_bridge_handle(port);
|
|
if (!handle)
|
|
return -EINVAL;
|
|
|
|
status = acpi_pci_osc_control_set(handle,
|
|
OSC_PCI_EXPRESS_PME_CONTROL |
|
|
OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL);
|
|
if (ACPI_FAILURE(status)) {
|
|
dev_info(&port->dev,
|
|
"Failed to receive control of PCIe PME service: %s\n",
|
|
(status == AE_SUPPORT || status == AE_NOT_FOUND) ?
|
|
"no _OSC support" : "ACPI _OSC failed");
|
|
error = -ENODEV;
|
|
}
|
|
|
|
return error;
|
|
}
|