summaryrefslogtreecommitdiffstats
path: root/drivers/pci/pcie/pme/pcie_pme_acpi.c
blob: 83ab2287ae3f86bc501ddf64b2a7d1f09991e1f5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
/*
 * 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;
}