Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jbarnes/pci-2.6
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jbarnes/pci-2.6: PCI: bus speed strings should be const PCI hotplug: Fix build with CONFIG_ACPI unset PCI: PCIe: Remove the port driver module exit routine PCI: PCIe: Move PCIe PME code to the pcie directory PCI: PCIe: Disable PCIe port services during port initialization PCI: PCIe: Ask BIOS for control of all native services at once ACPI/PCI: Negotiate _OSC control bits before requesting them ACPI/PCI: Do not preserve _OSC control bits returned by a query ACPI/PCI: Make acpi_pci_query_osc() return control bits ACPI/PCI: Reorder checks in acpi_pci_osc_control_set() PCI: PCIe: Introduce commad line switch for disabling port services PCI: PCIe AER: Introduce pci_aer_available() x86/PCI: only define pci_domain_nr if PCI and PCI_DOMAINS are set PCI: provide stub pci_domain_nr function for !CONFIG_PCI configs
This commit is contained in:
commit
d56557af19
24 changed files with 282 additions and 292 deletions
|
@ -1974,15 +1974,18 @@ and is between 256 and 4096 characters. It is defined in the file
|
|||
force Enable ASPM even on devices that claim not to support it.
|
||||
WARNING: Forcing ASPM on may cause system lockups.
|
||||
|
||||
pcie_ports= [PCIE] PCIe ports handling:
|
||||
auto Ask the BIOS whether or not to use native PCIe services
|
||||
associated with PCIe ports (PME, hot-plug, AER). Use
|
||||
them only if that is allowed by the BIOS.
|
||||
native Use native PCIe services associated with PCIe ports
|
||||
unconditionally.
|
||||
compat Treat PCIe ports as PCI-to-PCI bridges, disable the PCIe
|
||||
ports driver.
|
||||
|
||||
pcie_pme= [PCIE,PM] Native PCIe PME signaling options:
|
||||
Format: {auto|force}[,nomsi]
|
||||
auto Use native PCIe PME signaling if the BIOS allows the
|
||||
kernel to control PCIe config registers of root ports.
|
||||
force Use native PCIe PME signaling even if the BIOS refuses
|
||||
to allow the kernel to control the relevant PCIe config
|
||||
registers.
|
||||
nomsi Do not use MSI for native PCIe PME signaling (this makes
|
||||
all PCIe root ports use INTx for everything).
|
||||
all PCIe root ports use INTx for all services).
|
||||
|
||||
pcmv= [HW,PCMCIA] BadgePAD 4
|
||||
|
||||
|
|
|
@ -27,6 +27,9 @@ extern struct pci_bus *pci_scan_bus_on_node(int busno, struct pci_ops *ops,
|
|||
int node);
|
||||
extern struct pci_bus *pci_scan_bus_with_sysdata(int busno);
|
||||
|
||||
#ifdef CONFIG_PCI
|
||||
|
||||
#ifdef CONFIG_PCI_DOMAINS
|
||||
static inline int pci_domain_nr(struct pci_bus *bus)
|
||||
{
|
||||
struct pci_sysdata *sd = bus->sysdata;
|
||||
|
@ -37,13 +40,12 @@ static inline int pci_proc_domain(struct pci_bus *bus)
|
|||
{
|
||||
return pci_domain_nr(bus);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* Can be used to override the logic in pci_scan_bus for skipping
|
||||
already-configured bus numbers - to be used for buggy BIOSes
|
||||
or architectures with incomplete PCI setup by the loader */
|
||||
|
||||
#ifdef CONFIG_PCI
|
||||
extern unsigned int pcibios_assign_all_busses(void);
|
||||
extern int pci_legacy_init(void);
|
||||
# ifdef CONFIG_ACPI
|
||||
|
|
|
@ -33,7 +33,6 @@
|
|||
#include <linux/pm_runtime.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pci-acpi.h>
|
||||
#include <linux/pci-aspm.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/slab.h>
|
||||
#include <acpi/acpi_bus.h>
|
||||
|
@ -226,22 +225,31 @@ static acpi_status acpi_pci_run_osc(acpi_handle handle,
|
|||
return status;
|
||||
}
|
||||
|
||||
static acpi_status acpi_pci_query_osc(struct acpi_pci_root *root, u32 flags)
|
||||
static acpi_status acpi_pci_query_osc(struct acpi_pci_root *root,
|
||||
u32 support,
|
||||
u32 *control)
|
||||
{
|
||||
acpi_status status;
|
||||
u32 support_set, result, capbuf[3];
|
||||
u32 result, capbuf[3];
|
||||
|
||||
support &= OSC_PCI_SUPPORT_MASKS;
|
||||
support |= root->osc_support_set;
|
||||
|
||||
/* do _OSC query for all possible controls */
|
||||
support_set = root->osc_support_set | (flags & OSC_PCI_SUPPORT_MASKS);
|
||||
capbuf[OSC_QUERY_TYPE] = OSC_QUERY_ENABLE;
|
||||
capbuf[OSC_SUPPORT_TYPE] = support_set;
|
||||
capbuf[OSC_CONTROL_TYPE] = OSC_PCI_CONTROL_MASKS;
|
||||
capbuf[OSC_SUPPORT_TYPE] = support;
|
||||
if (control) {
|
||||
*control &= OSC_PCI_CONTROL_MASKS;
|
||||
capbuf[OSC_CONTROL_TYPE] = *control | root->osc_control_set;
|
||||
} else {
|
||||
/* Run _OSC query for all possible controls. */
|
||||
capbuf[OSC_CONTROL_TYPE] = OSC_PCI_CONTROL_MASKS;
|
||||
}
|
||||
|
||||
status = acpi_pci_run_osc(root->device->handle, capbuf, &result);
|
||||
if (ACPI_SUCCESS(status)) {
|
||||
root->osc_support_set = support_set;
|
||||
root->osc_control_qry = result;
|
||||
root->osc_queried = 1;
|
||||
root->osc_support_set = support;
|
||||
if (control)
|
||||
*control = result;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
@ -255,7 +263,7 @@ static acpi_status acpi_pci_osc_support(struct acpi_pci_root *root, u32 flags)
|
|||
if (ACPI_FAILURE(status))
|
||||
return status;
|
||||
mutex_lock(&osc_lock);
|
||||
status = acpi_pci_query_osc(root, flags);
|
||||
status = acpi_pci_query_osc(root, flags, NULL);
|
||||
mutex_unlock(&osc_lock);
|
||||
return status;
|
||||
}
|
||||
|
@ -365,55 +373,70 @@ struct pci_dev *acpi_get_pci_dev(acpi_handle handle)
|
|||
EXPORT_SYMBOL_GPL(acpi_get_pci_dev);
|
||||
|
||||
/**
|
||||
* acpi_pci_osc_control_set - commit requested control to Firmware
|
||||
* @handle: acpi_handle for the target ACPI object
|
||||
* @flags: driver's requested control bits
|
||||
* acpi_pci_osc_control_set - Request control of PCI root _OSC features.
|
||||
* @handle: ACPI handle of a PCI root bridge (or PCIe Root Complex).
|
||||
* @mask: Mask of _OSC bits to request control of, place to store control mask.
|
||||
* @req: Mask of _OSC bits the control of is essential to the caller.
|
||||
*
|
||||
* Attempt to take control from Firmware on requested control bits.
|
||||
* Run _OSC query for @mask and if that is successful, compare the returned
|
||||
* mask of control bits with @req. If all of the @req bits are set in the
|
||||
* returned mask, run _OSC request for it.
|
||||
*
|
||||
* The variable at the @mask address may be modified regardless of whether or
|
||||
* not the function returns success. On success it will contain the mask of
|
||||
* _OSC bits the BIOS has granted control of, but its contents are meaningless
|
||||
* on failure.
|
||||
**/
|
||||
acpi_status acpi_pci_osc_control_set(acpi_handle handle, u32 flags)
|
||||
acpi_status acpi_pci_osc_control_set(acpi_handle handle, u32 *mask, u32 req)
|
||||
{
|
||||
acpi_status status;
|
||||
u32 control_req, result, capbuf[3];
|
||||
acpi_handle tmp;
|
||||
struct acpi_pci_root *root;
|
||||
acpi_status status;
|
||||
u32 ctrl, capbuf[3];
|
||||
acpi_handle tmp;
|
||||
|
||||
status = acpi_get_handle(handle, "_OSC", &tmp);
|
||||
if (ACPI_FAILURE(status))
|
||||
return status;
|
||||
if (!mask)
|
||||
return AE_BAD_PARAMETER;
|
||||
|
||||
control_req = (flags & OSC_PCI_CONTROL_MASKS);
|
||||
if (!control_req)
|
||||
ctrl = *mask & OSC_PCI_CONTROL_MASKS;
|
||||
if ((ctrl & req) != req)
|
||||
return AE_TYPE;
|
||||
|
||||
root = acpi_pci_find_root(handle);
|
||||
if (!root)
|
||||
return AE_NOT_EXIST;
|
||||
|
||||
status = acpi_get_handle(handle, "_OSC", &tmp);
|
||||
if (ACPI_FAILURE(status))
|
||||
return status;
|
||||
|
||||
mutex_lock(&osc_lock);
|
||||
|
||||
*mask = ctrl | root->osc_control_set;
|
||||
/* No need to evaluate _OSC if the control was already granted. */
|
||||
if ((root->osc_control_set & control_req) == control_req)
|
||||
if ((root->osc_control_set & ctrl) == ctrl)
|
||||
goto out;
|
||||
|
||||
/* Need to query controls first before requesting them */
|
||||
if (!root->osc_queried) {
|
||||
status = acpi_pci_query_osc(root, root->osc_support_set);
|
||||
/* Need to check the available controls bits before requesting them. */
|
||||
while (*mask) {
|
||||
status = acpi_pci_query_osc(root, root->osc_support_set, mask);
|
||||
if (ACPI_FAILURE(status))
|
||||
goto out;
|
||||
if (ctrl == *mask)
|
||||
break;
|
||||
ctrl = *mask;
|
||||
}
|
||||
if ((root->osc_control_qry & control_req) != control_req) {
|
||||
printk(KERN_DEBUG
|
||||
"Firmware did not grant requested _OSC control\n");
|
||||
|
||||
if ((ctrl & req) != req) {
|
||||
status = AE_SUPPORT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
capbuf[OSC_QUERY_TYPE] = 0;
|
||||
capbuf[OSC_SUPPORT_TYPE] = root->osc_support_set;
|
||||
capbuf[OSC_CONTROL_TYPE] = root->osc_control_set | control_req;
|
||||
status = acpi_pci_run_osc(handle, capbuf, &result);
|
||||
capbuf[OSC_CONTROL_TYPE] = ctrl;
|
||||
status = acpi_pci_run_osc(handle, capbuf, mask);
|
||||
if (ACPI_SUCCESS(status))
|
||||
root->osc_control_set = result;
|
||||
root->osc_control_set = *mask;
|
||||
out:
|
||||
mutex_unlock(&osc_lock);
|
||||
return status;
|
||||
|
@ -544,14 +567,6 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device)
|
|||
if (flags != base_flags)
|
||||
acpi_pci_osc_support(root, flags);
|
||||
|
||||
status = acpi_pci_osc_control_set(root->device->handle,
|
||||
OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL);
|
||||
|
||||
if (ACPI_FAILURE(status)) {
|
||||
printk(KERN_INFO "Unable to assume PCIe control: Disabling ASPM\n");
|
||||
pcie_no_aspm();
|
||||
}
|
||||
|
||||
pci_acpi_add_bus_pm_notifier(device, root->bus);
|
||||
if (device->wakeup.flags.run_wake)
|
||||
device_set_run_wake(root->bus->bridge, true);
|
||||
|
|
|
@ -338,9 +338,7 @@ int acpi_get_hp_hw_control_from_firmware(struct pci_dev *pdev, u32 flags)
|
|||
acpi_handle chandle, handle;
|
||||
struct acpi_buffer string = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
|
||||
flags &= (OSC_PCI_EXPRESS_NATIVE_HP_CONTROL |
|
||||
OSC_SHPC_NATIVE_HP_CONTROL |
|
||||
OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL);
|
||||
flags &= OSC_SHPC_NATIVE_HP_CONTROL;
|
||||
if (!flags) {
|
||||
err("Invalid flags %u specified!\n", flags);
|
||||
return -EINVAL;
|
||||
|
@ -360,7 +358,7 @@ int acpi_get_hp_hw_control_from_firmware(struct pci_dev *pdev, u32 flags)
|
|||
acpi_get_name(handle, ACPI_FULL_PATHNAME, &string);
|
||||
dbg("Trying to get hotplug control for %s\n",
|
||||
(char *)string.pointer);
|
||||
status = acpi_pci_osc_control_set(handle, flags);
|
||||
status = acpi_pci_osc_control_set(handle, &flags, flags);
|
||||
if (ACPI_SUCCESS(status))
|
||||
goto got_one;
|
||||
if (status == AE_SUPPORT)
|
||||
|
|
|
@ -176,19 +176,11 @@ static inline void pciehp_firmware_init(void)
|
|||
{
|
||||
pciehp_acpi_slot_detection_init();
|
||||
}
|
||||
|
||||
static inline int pciehp_get_hp_hw_control_from_firmware(struct pci_dev *dev)
|
||||
{
|
||||
int retval;
|
||||
u32 flags = (OSC_PCI_EXPRESS_NATIVE_HP_CONTROL |
|
||||
OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL);
|
||||
retval = acpi_get_hp_hw_control_from_firmware(dev, flags);
|
||||
if (retval)
|
||||
return retval;
|
||||
return pciehp_acpi_slot_detection_check(dev);
|
||||
}
|
||||
#else
|
||||
#define pciehp_firmware_init() do {} while (0)
|
||||
#define pciehp_get_hp_hw_control_from_firmware(dev) 0
|
||||
static inline int pciehp_acpi_slot_detection_check(struct pci_dev *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_ACPI */
|
||||
#endif /* _PCIEHP_H */
|
||||
|
|
|
@ -85,9 +85,7 @@ static int __init dummy_probe(struct pcie_device *dev)
|
|||
acpi_handle handle;
|
||||
struct dummy_slot *slot, *tmp;
|
||||
struct pci_dev *pdev = dev->port;
|
||||
/* Note: pciehp_detect_mode != PCIEHP_DETECT_ACPI here */
|
||||
if (pciehp_get_hp_hw_control_from_firmware(pdev))
|
||||
return -ENODEV;
|
||||
|
||||
pos = pci_pcie_cap(pdev);
|
||||
if (!pos)
|
||||
return -ENODEV;
|
||||
|
|
|
@ -59,7 +59,7 @@ module_param(pciehp_force, bool, 0644);
|
|||
MODULE_PARM_DESC(pciehp_debug, "Debugging mode enabled or not");
|
||||
MODULE_PARM_DESC(pciehp_poll_mode, "Using polling mechanism for hot-plug events or not");
|
||||
MODULE_PARM_DESC(pciehp_poll_time, "Polling mechanism frequency, in seconds");
|
||||
MODULE_PARM_DESC(pciehp_force, "Force pciehp, even if _OSC and OSHP are missing");
|
||||
MODULE_PARM_DESC(pciehp_force, "Force pciehp, even if OSHP is missing");
|
||||
|
||||
#define PCIE_MODULE_NAME "pciehp"
|
||||
|
||||
|
@ -235,7 +235,7 @@ static int pciehp_probe(struct pcie_device *dev)
|
|||
dev_info(&dev->device,
|
||||
"Bypassing BIOS check for pciehp use on %s\n",
|
||||
pci_name(dev->port));
|
||||
else if (pciehp_get_hp_hw_control_from_firmware(dev->port))
|
||||
else if (pciehp_acpi_slot_detection_check(dev->port))
|
||||
goto err_out_none;
|
||||
|
||||
ctrl = pcie_init(dev);
|
||||
|
|
|
@ -140,8 +140,10 @@ static inline void pci_msi_init_pci_dev(struct pci_dev *dev) { }
|
|||
|
||||
#ifdef CONFIG_PCIEAER
|
||||
void pci_no_aer(void);
|
||||
bool pci_aer_available(void);
|
||||
#else
|
||||
static inline void pci_no_aer(void) { }
|
||||
static inline bool pci_aer_available(void) { return false; }
|
||||
#endif
|
||||
|
||||
static inline int pci_no_d1d2(struct pci_dev *dev)
|
||||
|
|
|
@ -6,10 +6,11 @@
|
|||
obj-$(CONFIG_PCIEASPM) += aspm.o
|
||||
|
||||
pcieportdrv-y := portdrv_core.o portdrv_pci.o portdrv_bus.o
|
||||
pcieportdrv-$(CONFIG_ACPI) += portdrv_acpi.o
|
||||
|
||||
obj-$(CONFIG_PCIEPORTBUS) += pcieportdrv.o
|
||||
|
||||
# Build PCI Express AER if needed
|
||||
obj-$(CONFIG_PCIEAER) += aer/
|
||||
|
||||
obj-$(CONFIG_PCIE_PME) += pme/
|
||||
obj-$(CONFIG_PCIE_PME) += pme.o
|
||||
|
|
|
@ -72,6 +72,11 @@ void pci_no_aer(void)
|
|||
pcie_aer_disable = 1; /* has priority over 'forceload' */
|
||||
}
|
||||
|
||||
bool pci_aer_available(void)
|
||||
{
|
||||
return !pcie_aer_disable && pci_msi_enabled();
|
||||
}
|
||||
|
||||
static int set_device_error_reporting(struct pci_dev *dev, void *data)
|
||||
{
|
||||
bool enable = *((bool *)data);
|
||||
|
@ -411,9 +416,7 @@ static void aer_error_resume(struct pci_dev *dev)
|
|||
*/
|
||||
static int __init aer_service_init(void)
|
||||
{
|
||||
if (pcie_aer_disable)
|
||||
return -ENXIO;
|
||||
if (!pci_msi_enabled())
|
||||
if (!pci_aer_available())
|
||||
return -ENXIO;
|
||||
return pcie_port_service_register(&aerdriver);
|
||||
}
|
||||
|
|
|
@ -19,42 +19,6 @@
|
|||
#include <acpi/apei.h>
|
||||
#include "aerdrv.h"
|
||||
|
||||
/**
|
||||
* aer_osc_setup - run ACPI _OSC method
|
||||
* @pciedev: pcie_device which AER is being enabled on
|
||||
*
|
||||
* @return: Zero on success. Nonzero otherwise.
|
||||
*
|
||||
* Invoked when PCIe bus loads AER service driver. To avoid conflict with
|
||||
* BIOS AER support requires BIOS to yield AER control to OS native driver.
|
||||
**/
|
||||
int aer_osc_setup(struct pcie_device *pciedev)
|
||||
{
|
||||
acpi_status status = AE_NOT_FOUND;
|
||||
struct pci_dev *pdev = pciedev->port;
|
||||
acpi_handle handle = NULL;
|
||||
|
||||
if (acpi_pci_disabled)
|
||||
return -1;
|
||||
|
||||
handle = acpi_find_root_bridge_handle(pdev);
|
||||
if (handle) {
|
||||
status = acpi_pci_osc_control_set(handle,
|
||||
OSC_PCI_EXPRESS_AER_CONTROL |
|
||||
OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL);
|
||||
}
|
||||
|
||||
if (ACPI_FAILURE(status)) {
|
||||
dev_printk(KERN_DEBUG, &pciedev->device, "AER service couldn't "
|
||||
"init device: %s\n",
|
||||
(status == AE_SUPPORT || status == AE_NOT_FOUND) ?
|
||||
"no _OSC support" : "_OSC failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ACPI_APEI
|
||||
static inline int hest_match_pci(struct acpi_hest_aer_common *p,
|
||||
struct pci_dev *pci)
|
||||
|
|
|
@ -772,22 +772,10 @@ void aer_isr(struct work_struct *work)
|
|||
*/
|
||||
int aer_init(struct pcie_device *dev)
|
||||
{
|
||||
if (pcie_aer_get_firmware_first(dev->port)) {
|
||||
dev_printk(KERN_DEBUG, &dev->device,
|
||||
"PCIe errors handled by platform firmware.\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (aer_osc_setup(dev))
|
||||
goto out;
|
||||
|
||||
return 0;
|
||||
out:
|
||||
if (forceload) {
|
||||
dev_printk(KERN_DEBUG, &dev->device,
|
||||
"aerdrv forceload requested.\n");
|
||||
pcie_aer_force_firmware_first(dev->port, 0);
|
||||
return 0;
|
||||
}
|
||||
return -ENXIO;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -23,37 +23,12 @@
|
|||
#include <linux/pci-acpi.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include "../../pci.h"
|
||||
#include "pcie_pme.h"
|
||||
#include "../pci.h"
|
||||
#include "portdrv.h"
|
||||
|
||||
#define PCI_EXP_RTSTA_PME 0x10000 /* PME status */
|
||||
#define PCI_EXP_RTSTA_PENDING 0x20000 /* PME pending */
|
||||
|
||||
/*
|
||||
* If set, this switch will prevent the PCIe root port PME service driver from
|
||||
* being registered. Consequently, the interrupt-based PCIe PME signaling will
|
||||
* not be used by any PCIe root ports in that case.
|
||||
*/
|
||||
static bool pcie_pme_disabled = true;
|
||||
|
||||
/*
|
||||
* The PCI Express Base Specification 2.0, Section 6.1.8, states the following:
|
||||
* "In order to maintain compatibility with non-PCI Express-aware system
|
||||
* software, system power management logic must be configured by firmware to use
|
||||
* the legacy mechanism of signaling PME by default. PCI Express-aware system
|
||||
* software must notify the firmware prior to enabling native, interrupt-based
|
||||
* PME signaling." However, if the platform doesn't provide us with a suitable
|
||||
* notification mechanism or the notification fails, it is not clear whether or
|
||||
* not we are supposed to use the interrupt-based PCIe PME signaling. The
|
||||
* switch below can be used to indicate the desired behaviour. When set, it
|
||||
* will make the kernel use the interrupt-based PCIe PME signaling regardless of
|
||||
* the platform notification status, although the kernel will attempt to notify
|
||||
* the platform anyway. When unset, it will prevent the kernel from using the
|
||||
* the interrupt-based PCIe PME signaling if the platform notification fails,
|
||||
* which is the default.
|
||||
*/
|
||||
static bool pcie_pme_force_enable;
|
||||
|
||||
/*
|
||||
* 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
|
||||
|
@ -64,38 +39,13 @@ bool pcie_pme_msi_disabled;
|
|||
|
||||
static int __init pcie_pme_setup(char *str)
|
||||
{
|
||||
if (!strncmp(str, "auto", 4))
|
||||
pcie_pme_disabled = false;
|
||||
else if (!strncmp(str, "force", 5))
|
||||
pcie_pme_force_enable = true;
|
||||
|
||||
str = strchr(str, ',');
|
||||
if (str) {
|
||||
str++;
|
||||
str += strspn(str, " \t");
|
||||
if (*str && !strcmp(str, "nomsi"))
|
||||
pcie_pme_msi_disabled = true;
|
||||
}
|
||||
if (!strncmp(str, "nomsi", 5))
|
||||
pcie_pme_msi_disabled = true;
|
||||
|
||||
return 1;
|
||||
}
|
||||
__setup("pcie_pme=", pcie_pme_setup);
|
||||
|
||||
/**
|
||||
* pcie_pme_platform_setup - Ensure that the kernel controls the PCIe PME.
|
||||
* @srv: PCIe PME root port service to use for carrying out the check.
|
||||
*
|
||||
* Notify the platform that the native PCIe PME is going to be used and return
|
||||
* 'true' if the control of the PCIe PME registers has been acquired from the
|
||||
* platform.
|
||||
*/
|
||||
static bool pcie_pme_platform_setup(struct pcie_device *srv)
|
||||
{
|
||||
if (!pcie_pme_platform_notify(srv))
|
||||
return true;
|
||||
return pcie_pme_force_enable;
|
||||
}
|
||||
|
||||
struct pcie_pme_service_data {
|
||||
spinlock_t lock;
|
||||
struct pcie_device *srv;
|
||||
|
@ -108,7 +58,7 @@ struct pcie_pme_service_data {
|
|||
* @dev: PCIe root port or event collector.
|
||||
* @enable: Enable or disable the interrupt.
|
||||
*/
|
||||
static void pcie_pme_interrupt_enable(struct pci_dev *dev, bool enable)
|
||||
void pcie_pme_interrupt_enable(struct pci_dev *dev, bool enable)
|
||||
{
|
||||
int rtctl_pos;
|
||||
u16 rtctl;
|
||||
|
@ -417,9 +367,6 @@ static int pcie_pme_probe(struct pcie_device *srv)
|
|||
struct pcie_pme_service_data *data;
|
||||
int ret;
|
||||
|
||||
if (!pcie_pme_platform_setup(srv))
|
||||
return -EACCES;
|
||||
|
||||
data = kzalloc(sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
@ -509,8 +456,7 @@ static struct pcie_port_service_driver pcie_pme_driver = {
|
|||
*/
|
||||
static int __init pcie_pme_service_init(void)
|
||||
{
|
||||
return pcie_pme_disabled ?
|
||||
-ENODEV : pcie_port_service_register(&pcie_pme_driver);
|
||||
return pcie_port_service_register(&pcie_pme_driver);
|
||||
}
|
||||
|
||||
module_init(pcie_pme_service_init);
|
|
@ -1,8 +0,0 @@
|
|||
#
|
||||
# Makefile for PCI-Express Root Port PME signaling driver
|
||||
#
|
||||
|
||||
obj-$(CONFIG_PCIE_PME) += pmedriver.o
|
||||
|
||||
pmedriver-objs := pcie_pme.o
|
||||
pmedriver-$(CONFIG_ACPI) += pcie_pme_acpi.o
|
|
@ -1,28 +0,0 @@
|
|||
/*
|
||||
* drivers/pci/pcie/pme/pcie_pme.h
|
||||
*
|
||||
* PCI Express Root Port PME signaling support
|
||||
*
|
||||
* Copyright (C) 2009 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc.
|
||||
*/
|
||||
|
||||
#ifndef _PCIE_PME_H_
|
||||
#define _PCIE_PME_H_
|
||||
|
||||
struct pcie_device;
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
extern int pcie_pme_acpi_setup(struct pcie_device *srv);
|
||||
|
||||
static inline int pcie_pme_platform_notify(struct pcie_device *srv)
|
||||
{
|
||||
return pcie_pme_acpi_setup(srv);
|
||||
}
|
||||
#else /* !CONFIG_ACPI */
|
||||
static inline int pcie_pme_platform_notify(struct pcie_device *srv)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif /* !CONFIG_ACPI */
|
||||
|
||||
#endif
|
|
@ -1,54 +0,0 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
|
@ -20,6 +20,9 @@
|
|||
|
||||
#define get_descriptor_id(type, service) (((type - 4) << 4) | service)
|
||||
|
||||
extern bool pcie_ports_disabled;
|
||||
extern bool pcie_ports_auto;
|
||||
|
||||
extern struct bus_type pcie_port_bus_type;
|
||||
extern int pcie_port_device_register(struct pci_dev *dev);
|
||||
#ifdef CONFIG_PM
|
||||
|
@ -30,6 +33,8 @@ extern void pcie_port_device_remove(struct pci_dev *dev);
|
|||
extern int __must_check pcie_port_bus_register(void);
|
||||
extern void pcie_port_bus_unregister(void);
|
||||
|
||||
struct pci_dev;
|
||||
|
||||
#ifdef CONFIG_PCIE_PME
|
||||
extern bool pcie_pme_msi_disabled;
|
||||
|
||||
|
@ -42,9 +47,26 @@ static inline bool pcie_pme_no_msi(void)
|
|||
{
|
||||
return pcie_pme_msi_disabled;
|
||||
}
|
||||
|
||||
extern void pcie_pme_interrupt_enable(struct pci_dev *dev, bool enable);
|
||||
#else /* !CONFIG_PCIE_PME */
|
||||
static inline void pcie_pme_disable_msi(void) {}
|
||||
static inline bool pcie_pme_no_msi(void) { return false; }
|
||||
static inline void pcie_pme_interrupt_enable(struct pci_dev *dev, bool en) {}
|
||||
#endif /* !CONFIG_PCIE_PME */
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
extern int pcie_port_acpi_setup(struct pci_dev *port, int *mask);
|
||||
|
||||
static inline int pcie_port_platform_notify(struct pci_dev *port, int *mask)
|
||||
{
|
||||
return pcie_port_acpi_setup(port, mask);
|
||||
}
|
||||
#else /* !CONFIG_ACPI */
|
||||
static inline int pcie_port_platform_notify(struct pci_dev *port, int *mask)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif /* !CONFIG_ACPI */
|
||||
|
||||
#endif /* _PORTDRV_H_ */
|
||||
|
|
77
drivers/pci/pcie/portdrv_acpi.c
Normal file
77
drivers/pci/pcie/portdrv_acpi.c
Normal file
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* PCIe Port Native Services Support, ACPI-Related Part
|
||||
*
|
||||
* Copyright (C) 2010 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>
|
||||
|
||||
#include "aer/aerdrv.h"
|
||||
#include "../pci.h"
|
||||
|
||||
/**
|
||||
* pcie_port_acpi_setup - Request the BIOS to release control of PCIe services.
|
||||
* @port: PCIe Port service for a root port or event collector.
|
||||
* @srv_mask: Bit mask of services that can be enabled for @port.
|
||||
*
|
||||
* Invoked when @port is identified as a PCIe port device. To avoid conflicts
|
||||
* with the BIOS PCIe port native services support requires the BIOS to yield
|
||||
* control of these services to the kernel. The mask of services that the BIOS
|
||||
* allows to be enabled for @port is written to @srv_mask.
|
||||
*
|
||||
* NOTE: It turns out that we cannot do that for individual port services
|
||||
* separately, because that would make some systems work incorrectly.
|
||||
*/
|
||||
int pcie_port_acpi_setup(struct pci_dev *port, int *srv_mask)
|
||||
{
|
||||
acpi_status status;
|
||||
acpi_handle handle;
|
||||
u32 flags;
|
||||
|
||||
if (acpi_pci_disabled)
|
||||
return 0;
|
||||
|
||||
handle = acpi_find_root_bridge_handle(port);
|
||||
if (!handle)
|
||||
return -EINVAL;
|
||||
|
||||
flags = OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL
|
||||
| OSC_PCI_EXPRESS_NATIVE_HP_CONTROL
|
||||
| OSC_PCI_EXPRESS_PME_CONTROL;
|
||||
|
||||
if (pci_aer_available()) {
|
||||
if (pcie_aer_get_firmware_first(port))
|
||||
dev_dbg(&port->dev, "PCIe errors handled by BIOS.\n");
|
||||
else
|
||||
flags |= OSC_PCI_EXPRESS_AER_CONTROL;
|
||||
}
|
||||
|
||||
status = acpi_pci_osc_control_set(handle, &flags,
|
||||
OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
dev_dbg(&port->dev, "ACPI _OSC request failed (code %d)\n",
|
||||
status);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
dev_info(&port->dev, "ACPI _OSC control granted for 0x%02x\n", flags);
|
||||
|
||||
*srv_mask = PCIE_PORT_SERVICE_VC;
|
||||
if (flags & OSC_PCI_EXPRESS_NATIVE_HP_CONTROL)
|
||||
*srv_mask |= PCIE_PORT_SERVICE_HP;
|
||||
if (flags & OSC_PCI_EXPRESS_PME_CONTROL)
|
||||
*srv_mask |= PCIE_PORT_SERVICE_PME;
|
||||
if (flags & OSC_PCI_EXPRESS_AER_CONTROL)
|
||||
*srv_mask |= PCIE_PORT_SERVICE_AER;
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -14,6 +14,8 @@
|
|||
#include <linux/string.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pcieport_if.h>
|
||||
#include <linux/aer.h>
|
||||
#include <linux/pci-aspm.h>
|
||||
|
||||
#include "../pci.h"
|
||||
#include "portdrv.h"
|
||||
|
@ -236,24 +238,64 @@ static int get_port_device_capability(struct pci_dev *dev)
|
|||
int services = 0, pos;
|
||||
u16 reg16;
|
||||
u32 reg32;
|
||||
int cap_mask;
|
||||
int err;
|
||||
|
||||
err = pcie_port_platform_notify(dev, &cap_mask);
|
||||
if (pcie_ports_auto) {
|
||||
if (err) {
|
||||
pcie_no_aspm();
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
cap_mask = PCIE_PORT_SERVICE_PME | PCIE_PORT_SERVICE_HP
|
||||
| PCIE_PORT_SERVICE_VC;
|
||||
if (pci_aer_available())
|
||||
cap_mask |= PCIE_PORT_SERVICE_AER;
|
||||
}
|
||||
|
||||
pos = pci_pcie_cap(dev);
|
||||
pci_read_config_word(dev, pos + PCI_EXP_FLAGS, ®16);
|
||||
/* Hot-Plug Capable */
|
||||
if (reg16 & PCI_EXP_FLAGS_SLOT) {
|
||||
if ((cap_mask & PCIE_PORT_SERVICE_HP) && (reg16 & PCI_EXP_FLAGS_SLOT)) {
|
||||
pci_read_config_dword(dev, pos + PCI_EXP_SLTCAP, ®32);
|
||||
if (reg32 & PCI_EXP_SLTCAP_HPC)
|
||||
if (reg32 & PCI_EXP_SLTCAP_HPC) {
|
||||
services |= PCIE_PORT_SERVICE_HP;
|
||||
/*
|
||||
* Disable hot-plug interrupts in case they have been
|
||||
* enabled by the BIOS and the hot-plug service driver
|
||||
* is not loaded.
|
||||
*/
|
||||
pos += PCI_EXP_SLTCTL;
|
||||
pci_read_config_word(dev, pos, ®16);
|
||||
reg16 &= ~(PCI_EXP_SLTCTL_CCIE | PCI_EXP_SLTCTL_HPIE);
|
||||
pci_write_config_word(dev, pos, reg16);
|
||||
}
|
||||
}
|
||||
/* AER capable */
|
||||
if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR))
|
||||
if ((cap_mask & PCIE_PORT_SERVICE_AER)
|
||||
&& pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR)) {
|
||||
services |= PCIE_PORT_SERVICE_AER;
|
||||
/*
|
||||
* Disable AER on this port in case it's been enabled by the
|
||||
* BIOS (the AER service driver will enable it when necessary).
|
||||
*/
|
||||
pci_disable_pcie_error_reporting(dev);
|
||||
}
|
||||
/* VC support */
|
||||
if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_VC))
|
||||
services |= PCIE_PORT_SERVICE_VC;
|
||||
/* Root ports are capable of generating PME too */
|
||||
if (dev->pcie_type == PCI_EXP_TYPE_ROOT_PORT)
|
||||
if ((cap_mask & PCIE_PORT_SERVICE_PME)
|
||||
&& dev->pcie_type == PCI_EXP_TYPE_ROOT_PORT) {
|
||||
services |= PCIE_PORT_SERVICE_PME;
|
||||
/*
|
||||
* Disable PME interrupt on this port in case it's been enabled
|
||||
* by the BIOS (the PME service driver will enable it when
|
||||
* necessary).
|
||||
*/
|
||||
pcie_pme_interrupt_enable(dev, false);
|
||||
}
|
||||
|
||||
return services;
|
||||
}
|
||||
|
@ -494,6 +536,9 @@ static void pcie_port_shutdown_service(struct device *dev) {}
|
|||
*/
|
||||
int pcie_port_service_register(struct pcie_port_service_driver *new)
|
||||
{
|
||||
if (pcie_ports_disabled)
|
||||
return -ENODEV;
|
||||
|
||||
new->driver.name = (char *)new->name;
|
||||
new->driver.bus = &pcie_port_bus_type;
|
||||
new->driver.probe = pcie_port_probe_service;
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <linux/pcieport_if.h>
|
||||
#include <linux/aer.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/pci-aspm.h>
|
||||
|
||||
#include "portdrv.h"
|
||||
#include "aer/aerdrv.h"
|
||||
|
@ -29,6 +30,31 @@ MODULE_AUTHOR(DRIVER_AUTHOR);
|
|||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
/* If this switch is set, PCIe port native services should not be enabled. */
|
||||
bool pcie_ports_disabled;
|
||||
|
||||
/*
|
||||
* If this switch is set, ACPI _OSC will be used to determine whether or not to
|
||||
* enable PCIe port native services.
|
||||
*/
|
||||
bool pcie_ports_auto = true;
|
||||
|
||||
static int __init pcie_port_setup(char *str)
|
||||
{
|
||||
if (!strncmp(str, "compat", 6)) {
|
||||
pcie_ports_disabled = true;
|
||||
} else if (!strncmp(str, "native", 6)) {
|
||||
pcie_ports_disabled = false;
|
||||
pcie_ports_auto = false;
|
||||
} else if (!strncmp(str, "auto", 4)) {
|
||||
pcie_ports_disabled = false;
|
||||
pcie_ports_auto = true;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
__setup("pcie_ports=", pcie_port_setup);
|
||||
|
||||
/* global data */
|
||||
|
||||
static int pcie_portdrv_restore_config(struct pci_dev *dev)
|
||||
|
@ -301,6 +327,11 @@ static int __init pcie_portdrv_init(void)
|
|||
{
|
||||
int retval;
|
||||
|
||||
if (pcie_ports_disabled) {
|
||||
pcie_no_aspm();
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
dmi_check_system(pcie_portdrv_dmi_table);
|
||||
|
||||
retval = pcie_port_bus_register();
|
||||
|
@ -315,11 +346,4 @@ static int __init pcie_portdrv_init(void)
|
|||
return retval;
|
||||
}
|
||||
|
||||
static void __exit pcie_portdrv_exit(void)
|
||||
{
|
||||
pci_unregister_driver(&pcie_portdriver);
|
||||
pcie_port_bus_unregister();
|
||||
}
|
||||
|
||||
module_init(pcie_portdrv_init);
|
||||
module_exit(pcie_portdrv_exit);
|
||||
|
|
|
@ -49,7 +49,7 @@ static ssize_t address_read_file(struct pci_slot *slot, char *buf)
|
|||
}
|
||||
|
||||
/* these strings match up with the values in pci_bus_speed */
|
||||
static char *pci_bus_speed_strings[] = {
|
||||
static const char *pci_bus_speed_strings[] = {
|
||||
"33 MHz PCI", /* 0x00 */
|
||||
"66 MHz PCI", /* 0x01 */
|
||||
"66 MHz PCI-X", /* 0x02 */
|
||||
|
|
|
@ -377,9 +377,6 @@ struct acpi_pci_root {
|
|||
|
||||
u32 osc_support_set; /* _OSC state of support bits */
|
||||
u32 osc_control_set; /* _OSC state of control bits */
|
||||
u32 osc_control_qry; /* the latest _OSC query result */
|
||||
|
||||
u32 osc_queried:1; /* has _OSC control been queried? */
|
||||
};
|
||||
|
||||
/* helper */
|
||||
|
|
|
@ -304,8 +304,8 @@ acpi_status acpi_run_osc(acpi_handle handle, struct acpi_osc_context *context);
|
|||
OSC_PCI_EXPRESS_PME_CONTROL | \
|
||||
OSC_PCI_EXPRESS_AER_CONTROL | \
|
||||
OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL)
|
||||
|
||||
extern acpi_status acpi_pci_osc_control_set(acpi_handle handle, u32 flags);
|
||||
extern acpi_status acpi_pci_osc_control_set(acpi_handle handle,
|
||||
u32 *mask, u32 req);
|
||||
extern void acpi_early_init(void);
|
||||
|
||||
#else /* !CONFIG_ACPI */
|
||||
|
|
|
@ -1214,6 +1214,9 @@ static inline struct pci_dev *pci_get_bus_and_slot(unsigned int bus,
|
|||
unsigned int devfn)
|
||||
{ return NULL; }
|
||||
|
||||
static inline int pci_domain_nr(struct pci_bus *bus)
|
||||
{ return 0; }
|
||||
|
||||
#define dev_is_pci(d) (false)
|
||||
#define dev_is_pf(d) (false)
|
||||
#define dev_num_vf(d) (0)
|
||||
|
|
Loading…
Reference in a new issue