mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-23 17:30:02 +00:00
![Rafael J. Wysocki](/assets/img/avatar_default.png)
Although the majority of PCI devices can generate PMEs that in principle may be used to wake up devices suspended at run time, platform support is generally necessary to convert PMEs into wake-up events that can be delivered to the kernel. If ACPI is used for this purpose, PME signals generated by a PCI device will trigger the ACPI GPE associated with the device to generate an ACPI wake-up event that we can set up a handler for, provided that everything is configured correctly. Unfortunately, the subset of PCI devices that have GPEs associated with them is quite limited. The devices without dedicated GPEs have to rely on the GPEs associated with other devices (in the majority of cases their upstream bridges and, possibly, the root bridge) to generate ACPI wake-up events in response to PME signals from them. Add ACPI platform support for PCI PME wake-up: o Add a framework making is possible to use ACPI system notify handlers for run-time PM. o Add new PCI platform callback ->run_wake() to struct pci_platform_pm_ops allowing us to enable/disable the platform to generate wake-up events for given device. Implemet this callback for the ACPI platform. o Define ACPI wake-up handlers for PCI devices and PCI root buses and make the PCI-ACPI binding code register wake-up notifiers for all PCI devices present in the ACPI tables. o Add function pci_dev_run_wake() which can be used by PCI drivers to check if given device is capable of generating wake-up events at run time. Developed in cooperation with Matthew Garrett <mjg@redhat.com>. Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl> Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
120 lines
3.1 KiB
C
120 lines
3.1 KiB
C
/*
|
|
* pci_bind.c - ACPI PCI Device Binding ($Revision: 2 $)
|
|
*
|
|
* Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
|
|
* Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
|
|
*
|
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or (at
|
|
* your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
|
*
|
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
*/
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/types.h>
|
|
#include <linux/pci.h>
|
|
#include <linux/pci-acpi.h>
|
|
#include <linux/acpi.h>
|
|
#include <linux/pm_runtime.h>
|
|
#include <acpi/acpi_bus.h>
|
|
#include <acpi/acpi_drivers.h>
|
|
|
|
#define _COMPONENT ACPI_PCI_COMPONENT
|
|
ACPI_MODULE_NAME("pci_bind");
|
|
|
|
static int acpi_pci_unbind(struct acpi_device *device)
|
|
{
|
|
struct pci_dev *dev;
|
|
|
|
dev = acpi_get_pci_dev(device->handle);
|
|
if (!dev)
|
|
goto out;
|
|
|
|
device_set_run_wake(&dev->dev, false);
|
|
pci_acpi_remove_pm_notifier(device);
|
|
|
|
if (!dev->subordinate)
|
|
goto out;
|
|
|
|
acpi_pci_irq_del_prt(dev->subordinate);
|
|
|
|
device->ops.bind = NULL;
|
|
device->ops.unbind = NULL;
|
|
|
|
out:
|
|
pci_dev_put(dev);
|
|
return 0;
|
|
}
|
|
|
|
static int acpi_pci_bind(struct acpi_device *device)
|
|
{
|
|
acpi_status status;
|
|
acpi_handle handle;
|
|
struct pci_bus *bus;
|
|
struct pci_dev *dev;
|
|
|
|
dev = acpi_get_pci_dev(device->handle);
|
|
if (!dev)
|
|
return 0;
|
|
|
|
pci_acpi_add_pm_notifier(device, dev);
|
|
if (device->wakeup.flags.run_wake)
|
|
device_set_run_wake(&dev->dev, true);
|
|
|
|
/*
|
|
* Install the 'bind' function to facilitate callbacks for
|
|
* children of the P2P bridge.
|
|
*/
|
|
if (dev->subordinate) {
|
|
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
|
|
"Device %04x:%02x:%02x.%d is a PCI bridge\n",
|
|
pci_domain_nr(dev->bus), dev->bus->number,
|
|
PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)));
|
|
device->ops.bind = acpi_pci_bind;
|
|
device->ops.unbind = acpi_pci_unbind;
|
|
}
|
|
|
|
/*
|
|
* Evaluate and parse _PRT, if exists. This code allows parsing of
|
|
* _PRT objects within the scope of non-bridge devices. Note that
|
|
* _PRTs within the scope of a PCI bridge assume the bridge's
|
|
* subordinate bus number.
|
|
*
|
|
* TBD: Can _PRTs exist within the scope of non-bridge PCI devices?
|
|
*/
|
|
status = acpi_get_handle(device->handle, METHOD_NAME__PRT, &handle);
|
|
if (ACPI_FAILURE(status))
|
|
goto out;
|
|
|
|
if (dev->subordinate)
|
|
bus = dev->subordinate;
|
|
else
|
|
bus = dev->bus;
|
|
|
|
acpi_pci_irq_add_prt(device->handle, bus);
|
|
|
|
out:
|
|
pci_dev_put(dev);
|
|
return 0;
|
|
}
|
|
|
|
int acpi_pci_bind_root(struct acpi_device *device)
|
|
{
|
|
device->ops.bind = acpi_pci_bind;
|
|
device->ops.unbind = acpi_pci_unbind;
|
|
|
|
return 0;
|
|
}
|