Commit 28eb5f274a305bf3a13b2c80c4804d4515d05c64

Authored by Rafael J. Wysocki
Committed by Jesse Barnes
1 parent 75fb60f26b

PCI: PCIe: Ask BIOS for control of all native services at once

After commit 852972acff8f10f3a15679be2059bb94916cba5d (ACPI: Disable
ASPM if the platform won't provide _OSC control for PCIe) control of
the PCIe Capability Structure is unconditionally requested by
acpi_pci_root_add(), which in principle may cause problems to
happen in two ways.  First, the BIOS may refuse to give control of
the PCIe Capability Structure if it is not asked for any of the
_OSC features depending on it at the same time.  Second, the BIOS may
assume that control of the _OSC features depending on the PCIe
Capability Structure will be requested in the future and may behave
incorrectly if that doesn't happen.  For this reason, control of
the PCIe Capability Structure should always be requested along with
control of any other _OSC features that may depend on it (ie. PCIe
native PME, PCIe native hot-plug, PCIe AER).

Rework the PCIe port driver so that (1) it checks which native PCIe
port services can be enabled, according to the BIOS, and (2) it
requests control of all these services simultaneously.  In
particular, this causes pcie_portdrv_probe() to fail if the BIOS
refuses to grant control of the PCIe Capability Structure, which
means that no native PCIe port services can be enabled for the PCIe
Root Complex the given port belongs to.  If that happens, ASPM is
disabled to avoid problems with mishandling it by the part of the
PCIe hierarchy for which control of the PCIe Capability Structure
has not been received.

Make it possible to override this behavior using 'pcie_ports=native'
(use the PCIe native services regardless of the BIOS response to the
control request), or 'pcie_ports=compat' (do not use the PCIe native
services at all).

Accordingly, rework the existing PCIe port service drivers so that
they don't request control of the services directly.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>

Showing 17 changed files with 155 additions and 237 deletions Side-by-side Diff

Documentation/kernel-parameters.txt
... ... @@ -2043,18 +2043,17 @@
2043 2043 WARNING: Forcing ASPM on may cause system lockups.
2044 2044  
2045 2045 pcie_ports= [PCIE] PCIe ports handling:
  2046 + auto Ask the BIOS whether or not to use native PCIe services
  2047 + associated with PCIe ports (PME, hot-plug, AER). Use
  2048 + them only if that is allowed by the BIOS.
  2049 + native Use native PCIe services associated with PCIe ports
  2050 + unconditionally.
2046 2051 compat Treat PCIe ports as PCI-to-PCI bridges, disable the PCIe
2047 2052 ports driver.
2048 2053  
2049 2054 pcie_pme= [PCIE,PM] Native PCIe PME signaling options:
2050   - Format: {auto|force}[,nomsi]
2051   - auto Use native PCIe PME signaling if the BIOS allows the
2052   - kernel to control PCIe config registers of root ports.
2053   - force Use native PCIe PME signaling even if the BIOS refuses
2054   - to allow the kernel to control the relevant PCIe config
2055   - registers.
2056 2055 nomsi Do not use MSI for native PCIe PME signaling (this makes
2057   - all PCIe root ports use INTx for everything).
  2056 + all PCIe root ports use INTx for all services).
2058 2057  
2059 2058 pcmv= [HW,PCMCIA] BadgePAD 4
2060 2059  
drivers/acpi/pci_root.c
... ... @@ -33,7 +33,6 @@
33 33 #include <linux/pm_runtime.h>
34 34 #include <linux/pci.h>
35 35 #include <linux/pci-acpi.h>
36   -#include <linux/pci-aspm.h>
37 36 #include <linux/acpi.h>
38 37 #include <linux/slab.h>
39 38 #include <acpi/acpi_bus.h>
... ... @@ -567,14 +566,6 @@
567 566 flags |= OSC_MSI_SUPPORT;
568 567 if (flags != base_flags)
569 568 acpi_pci_osc_support(root, flags);
570   -
571   - flags = OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL;
572   - status = acpi_pci_osc_control_set(root->device->handle, &flags, flags);
573   -
574   - if (ACPI_FAILURE(status)) {
575   - printk(KERN_INFO "Unable to assume PCIe control: Disabling ASPM\n");
576   - pcie_no_aspm();
577   - }
578 569  
579 570 pci_acpi_add_bus_pm_notifier(device, root->bus);
580 571 if (device->wakeup.flags.run_wake)
drivers/pci/hotplug/acpi_pcihp.c
... ... @@ -338,9 +338,7 @@
338 338 acpi_handle chandle, handle;
339 339 struct acpi_buffer string = { ACPI_ALLOCATE_BUFFER, NULL };
340 340  
341   - flags &= (OSC_PCI_EXPRESS_NATIVE_HP_CONTROL |
342   - OSC_SHPC_NATIVE_HP_CONTROL |
343   - OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL);
  341 + flags &= OSC_SHPC_NATIVE_HP_CONTROL;
344 342 if (!flags) {
345 343 err("Invalid flags %u specified!\n", flags);
346 344 return -EINVAL;
drivers/pci/hotplug/pciehp.h
... ... @@ -176,20 +176,8 @@
176 176 {
177 177 pciehp_acpi_slot_detection_init();
178 178 }
179   -
180   -static inline int pciehp_get_hp_hw_control_from_firmware(struct pci_dev *dev)
181   -{
182   - int retval;
183   - u32 flags = (OSC_PCI_EXPRESS_NATIVE_HP_CONTROL |
184   - OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL);
185   - retval = acpi_get_hp_hw_control_from_firmware(dev, flags);
186   - if (retval)
187   - return retval;
188   - return pciehp_acpi_slot_detection_check(dev);
189   -}
190 179 #else
191 180 #define pciehp_firmware_init() do {} while (0)
192   -#define pciehp_get_hp_hw_control_from_firmware(dev) 0
193 181 #endif /* CONFIG_ACPI */
194 182 #endif /* _PCIEHP_H */
drivers/pci/hotplug/pciehp_acpi.c
... ... @@ -85,9 +85,7 @@
85 85 acpi_handle handle;
86 86 struct dummy_slot *slot, *tmp;
87 87 struct pci_dev *pdev = dev->port;
88   - /* Note: pciehp_detect_mode != PCIEHP_DETECT_ACPI here */
89   - if (pciehp_get_hp_hw_control_from_firmware(pdev))
90   - return -ENODEV;
  88 +
91 89 pos = pci_pcie_cap(pdev);
92 90 if (!pos)
93 91 return -ENODEV;
drivers/pci/hotplug/pciehp_core.c
... ... @@ -59,7 +59,7 @@
59 59 MODULE_PARM_DESC(pciehp_debug, "Debugging mode enabled or not");
60 60 MODULE_PARM_DESC(pciehp_poll_mode, "Using polling mechanism for hot-plug events or not");
61 61 MODULE_PARM_DESC(pciehp_poll_time, "Polling mechanism frequency, in seconds");
62   -MODULE_PARM_DESC(pciehp_force, "Force pciehp, even if _OSC and OSHP are missing");
  62 +MODULE_PARM_DESC(pciehp_force, "Force pciehp, even if OSHP is missing");
63 63  
64 64 #define PCIE_MODULE_NAME "pciehp"
65 65  
... ... @@ -235,7 +235,7 @@
235 235 dev_info(&dev->device,
236 236 "Bypassing BIOS check for pciehp use on %s\n",
237 237 pci_name(dev->port));
238   - else if (pciehp_get_hp_hw_control_from_firmware(dev->port))
  238 + else if (pciehp_acpi_slot_detection_check(dev->port))
239 239 goto err_out_none;
240 240  
241 241 ctrl = pcie_init(dev);
drivers/pci/pcie/Makefile
... ... @@ -6,6 +6,7 @@
6 6 obj-$(CONFIG_PCIEASPM) += aspm.o
7 7  
8 8 pcieportdrv-y := portdrv_core.o portdrv_pci.o portdrv_bus.o
  9 +pcieportdrv-$(CONFIG_ACPI) += portdrv_acpi.o
9 10  
10 11 obj-$(CONFIG_PCIEPORTBUS) += pcieportdrv.o
11 12  
drivers/pci/pcie/aer/aerdrv_acpi.c
... ... @@ -19,42 +19,6 @@
19 19 #include <acpi/apei.h>
20 20 #include "aerdrv.h"
21 21  
22   -/**
23   - * aer_osc_setup - run ACPI _OSC method
24   - * @pciedev: pcie_device which AER is being enabled on
25   - *
26   - * @return: Zero on success. Nonzero otherwise.
27   - *
28   - * Invoked when PCIe bus loads AER service driver. To avoid conflict with
29   - * BIOS AER support requires BIOS to yield AER control to OS native driver.
30   - **/
31   -int aer_osc_setup(struct pcie_device *pciedev)
32   -{
33   - acpi_status status = AE_NOT_FOUND;
34   - struct pci_dev *pdev = pciedev->port;
35   - acpi_handle handle = NULL;
36   -
37   - if (acpi_pci_disabled)
38   - return -1;
39   -
40   - handle = acpi_find_root_bridge_handle(pdev);
41   - if (handle) {
42   - u32 flags = OSC_PCI_EXPRESS_AER_CONTROL |
43   - OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL;
44   - status = acpi_pci_osc_control_set(handle, &flags, flags);
45   - }
46   -
47   - if (ACPI_FAILURE(status)) {
48   - dev_printk(KERN_DEBUG, &pciedev->device, "AER service couldn't "
49   - "init device: %s\n",
50   - (status == AE_SUPPORT || status == AE_NOT_FOUND) ?
51   - "no _OSC support" : "_OSC failed");
52   - return -1;
53   - }
54   -
55   - return 0;
56   -}
57   -
58 22 #ifdef CONFIG_ACPI_APEI
59 23 static inline int hest_match_pci(struct acpi_hest_aer_common *p,
60 24 struct pci_dev *pci)
drivers/pci/pcie/aer/aerdrv_core.c
... ... @@ -772,23 +772,11 @@
772 772 */
773 773 int aer_init(struct pcie_device *dev)
774 774 {
775   - if (pcie_aer_get_firmware_first(dev->port)) {
776   - dev_printk(KERN_DEBUG, &dev->device,
777   - "PCIe errors handled by platform firmware.\n");
778   - goto out;
779   - }
780   -
781   - if (aer_osc_setup(dev))
782   - goto out;
783   -
784   - return 0;
785   -out:
786 775 if (forceload) {
787 776 dev_printk(KERN_DEBUG, &dev->device,
788 777 "aerdrv forceload requested.\n");
789 778 pcie_aer_force_firmware_first(dev->port, 0);
790   - return 0;
791 779 }
792   - return -ENXIO;
  780 + return 0;
793 781 }
drivers/pci/pcie/pme/Makefile
... ... @@ -2,8 +2,5 @@
2 2 # Makefile for PCI-Express Root Port PME signaling driver
3 3 #
4 4  
5   -obj-$(CONFIG_PCIE_PME) += pmedriver.o
6   -
7   -pmedriver-objs := pcie_pme.o
8   -pmedriver-$(CONFIG_ACPI) += pcie_pme_acpi.o
  5 +obj-$(CONFIG_PCIE_PME) += pcie_pme.o
drivers/pci/pcie/pme/pcie_pme.c
... ... @@ -24,37 +24,12 @@
24 24 #include <linux/pm_runtime.h>
25 25  
26 26 #include "../../pci.h"
27   -#include "pcie_pme.h"
  27 +#include "../portdrv.h"
28 28  
29 29 #define PCI_EXP_RTSTA_PME 0x10000 /* PME status */
30 30 #define PCI_EXP_RTSTA_PENDING 0x20000 /* PME pending */
31 31  
32 32 /*
33   - * If set, this switch will prevent the PCIe root port PME service driver from
34   - * being registered. Consequently, the interrupt-based PCIe PME signaling will
35   - * not be used by any PCIe root ports in that case.
36   - */
37   -static bool pcie_pme_disabled = true;
38   -
39   -/*
40   - * The PCI Express Base Specification 2.0, Section 6.1.8, states the following:
41   - * "In order to maintain compatibility with non-PCI Express-aware system
42   - * software, system power management logic must be configured by firmware to use
43   - * the legacy mechanism of signaling PME by default. PCI Express-aware system
44   - * software must notify the firmware prior to enabling native, interrupt-based
45   - * PME signaling." However, if the platform doesn't provide us with a suitable
46   - * notification mechanism or the notification fails, it is not clear whether or
47   - * not we are supposed to use the interrupt-based PCIe PME signaling. The
48   - * switch below can be used to indicate the desired behaviour. When set, it
49   - * will make the kernel use the interrupt-based PCIe PME signaling regardless of
50   - * the platform notification status, although the kernel will attempt to notify
51   - * the platform anyway. When unset, it will prevent the kernel from using the
52   - * the interrupt-based PCIe PME signaling if the platform notification fails,
53   - * which is the default.
54   - */
55   -static bool pcie_pme_force_enable;
56   -
57   -/*
58 33 * If this switch is set, MSI will not be used for PCIe PME signaling. This
59 34 * causes the PCIe port driver to use INTx interrupts only, but it turns out
60 35 * that using MSI for PCIe PME signaling doesn't play well with PCIe PME-based
61 36  
62 37  
... ... @@ -64,38 +39,13 @@
64 39  
65 40 static int __init pcie_pme_setup(char *str)
66 41 {
67   - if (!strncmp(str, "auto", 4))
68   - pcie_pme_disabled = false;
69   - else if (!strncmp(str, "force", 5))
70   - pcie_pme_force_enable = true;
  42 + if (!strncmp(str, "nomsi", 5))
  43 + pcie_pme_msi_disabled = true;
71 44  
72   - str = strchr(str, ',');
73   - if (str) {
74   - str++;
75   - str += strspn(str, " \t");
76   - if (*str && !strcmp(str, "nomsi"))
77   - pcie_pme_msi_disabled = true;
78   - }
79   -
80 45 return 1;
81 46 }
82 47 __setup("pcie_pme=", pcie_pme_setup);
83 48  
84   -/**
85   - * pcie_pme_platform_setup - Ensure that the kernel controls the PCIe PME.
86   - * @srv: PCIe PME root port service to use for carrying out the check.
87   - *
88   - * Notify the platform that the native PCIe PME is going to be used and return
89   - * 'true' if the control of the PCIe PME registers has been acquired from the
90   - * platform.
91   - */
92   -static bool pcie_pme_platform_setup(struct pcie_device *srv)
93   -{
94   - if (!pcie_pme_platform_notify(srv))
95   - return true;
96   - return pcie_pme_force_enable;
97   -}
98   -
99 49 struct pcie_pme_service_data {
100 50 spinlock_t lock;
101 51 struct pcie_device *srv;
... ... @@ -108,7 +58,7 @@
108 58 * @dev: PCIe root port or event collector.
109 59 * @enable: Enable or disable the interrupt.
110 60 */
111   -static void pcie_pme_interrupt_enable(struct pci_dev *dev, bool enable)
  61 +void pcie_pme_interrupt_enable(struct pci_dev *dev, bool enable)
112 62 {
113 63 int rtctl_pos;
114 64 u16 rtctl;
... ... @@ -417,9 +367,6 @@
417 367 struct pcie_pme_service_data *data;
418 368 int ret;
419 369  
420   - if (!pcie_pme_platform_setup(srv))
421   - return -EACCES;
422   -
423 370 data = kzalloc(sizeof(*data), GFP_KERNEL);
424 371 if (!data)
425 372 return -ENOMEM;
... ... @@ -509,8 +456,7 @@
509 456 */
510 457 static int __init pcie_pme_service_init(void)
511 458 {
512   - return pcie_pme_disabled ?
513   - -ENODEV : pcie_port_service_register(&pcie_pme_driver);
  459 + return pcie_port_service_register(&pcie_pme_driver);
514 460 }
515 461  
516 462 module_init(pcie_pme_service_init);
drivers/pci/pcie/pme/pcie_pme.h
1   -/*
2   - * drivers/pci/pcie/pme/pcie_pme.h
3   - *
4   - * PCI Express Root Port PME signaling support
5   - *
6   - * Copyright (C) 2009 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc.
7   - */
8   -
9   -#ifndef _PCIE_PME_H_
10   -#define _PCIE_PME_H_
11   -
12   -struct pcie_device;
13   -
14   -#ifdef CONFIG_ACPI
15   -extern int pcie_pme_acpi_setup(struct pcie_device *srv);
16   -
17   -static inline int pcie_pme_platform_notify(struct pcie_device *srv)
18   -{
19   - return pcie_pme_acpi_setup(srv);
20   -}
21   -#else /* !CONFIG_ACPI */
22   -static inline int pcie_pme_platform_notify(struct pcie_device *srv)
23   -{
24   - return 0;
25   -}
26   -#endif /* !CONFIG_ACPI */
27   -
28   -#endif
drivers/pci/pcie/pme/pcie_pme_acpi.c
1   -/*
2   - * PCIe Native PME support, ACPI-related part
3   - *
4   - * Copyright (C) 2009 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc.
5   - *
6   - * This file is subject to the terms and conditions of the GNU General Public
7   - * License V2. See the file "COPYING" in the main directory of this archive
8   - * for more details.
9   - */
10   -
11   -#include <linux/pci.h>
12   -#include <linux/kernel.h>
13   -#include <linux/errno.h>
14   -#include <linux/acpi.h>
15   -#include <linux/pci-acpi.h>
16   -#include <linux/pcieport_if.h>
17   -
18   -/**
19   - * pcie_pme_acpi_setup - Request the ACPI BIOS to release control over PCIe PME.
20   - * @srv - PCIe PME service for a root port or event collector.
21   - *
22   - * Invoked when the PCIe bus type loads PCIe PME service driver. To avoid
23   - * conflict with the BIOS PCIe support requires the BIOS to yield PCIe PME
24   - * control to the kernel.
25   - */
26   -int pcie_pme_acpi_setup(struct pcie_device *srv)
27   -{
28   - acpi_status status = AE_NOT_FOUND;
29   - struct pci_dev *port = srv->port;
30   - acpi_handle handle;
31   - u32 flags;
32   - int error = 0;
33   -
34   - if (acpi_pci_disabled)
35   - return -ENOSYS;
36   -
37   - dev_info(&port->dev, "Requesting control of PCIe PME from ACPI BIOS\n");
38   -
39   - handle = acpi_find_root_bridge_handle(port);
40   - if (!handle)
41   - return -EINVAL;
42   -
43   - flags = OSC_PCI_EXPRESS_PME_CONTROL |
44   - OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL;
45   -
46   - status = acpi_pci_osc_control_set(handle, &flags, flags);
47   - if (ACPI_FAILURE(status)) {
48   - dev_info(&port->dev,
49   - "Failed to receive control of PCIe PME service: %s\n",
50   - (status == AE_SUPPORT || status == AE_NOT_FOUND) ?
51   - "no _OSC support" : "ACPI _OSC failed");
52   - error = -ENODEV;
53   - }
54   -
55   - return error;
56   -}
drivers/pci/pcie/portdrv.h
... ... @@ -21,6 +21,7 @@
21 21 #define get_descriptor_id(type, service) (((type - 4) << 4) | service)
22 22  
23 23 extern bool pcie_ports_disabled;
  24 +extern bool pcie_ports_auto;
24 25  
25 26 extern struct bus_type pcie_port_bus_type;
26 27 extern int pcie_port_device_register(struct pci_dev *dev);
... ... @@ -32,6 +33,8 @@
32 33 extern int __must_check pcie_port_bus_register(void);
33 34 extern void pcie_port_bus_unregister(void);
34 35  
  36 +struct pci_dev;
  37 +
35 38 #ifdef CONFIG_PCIE_PME
36 39 extern bool pcie_pme_msi_disabled;
37 40  
38 41  
39 42  
... ... @@ -44,10 +47,27 @@
44 47 {
45 48 return pcie_pme_msi_disabled;
46 49 }
  50 +
  51 +extern void pcie_pme_interrupt_enable(struct pci_dev *dev, bool enable);
47 52 #else /* !CONFIG_PCIE_PME */
48 53 static inline void pcie_pme_disable_msi(void) {}
49 54 static inline bool pcie_pme_no_msi(void) { return false; }
  55 +static inline void pcie_pme_interrupt_enable(struct pci_dev *dev, bool en) {}
50 56 #endif /* !CONFIG_PCIE_PME */
  57 +
  58 +#ifdef CONFIG_ACPI
  59 +extern int pcie_port_acpi_setup(struct pci_dev *port, int *mask);
  60 +
  61 +static inline int pcie_port_platform_notify(struct pci_dev *port, int *mask)
  62 +{
  63 + return pcie_port_acpi_setup(port, mask);
  64 +}
  65 +#else /* !CONFIG_ACPI */
  66 +static inline int pcie_port_platform_notify(struct pci_dev *port, int *mask)
  67 +{
  68 + return 0;
  69 +}
  70 +#endif /* !CONFIG_ACPI */
51 71  
52 72 #endif /* _PORTDRV_H_ */
drivers/pci/pcie/portdrv_acpi.c
  1 +/*
  2 + * PCIe Port Native Services Support, ACPI-Related Part
  3 + *
  4 + * Copyright (C) 2010 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc.
  5 + *
  6 + * This file is subject to the terms and conditions of the GNU General Public
  7 + * License V2. See the file "COPYING" in the main directory of this archive
  8 + * for more details.
  9 + */
  10 +
  11 +#include <linux/pci.h>
  12 +#include <linux/kernel.h>
  13 +#include <linux/errno.h>
  14 +#include <linux/acpi.h>
  15 +#include <linux/pci-acpi.h>
  16 +#include <linux/pcieport_if.h>
  17 +
  18 +#include "aer/aerdrv.h"
  19 +#include "../pci.h"
  20 +
  21 +/**
  22 + * pcie_port_acpi_setup - Request the BIOS to release control of PCIe services.
  23 + * @port: PCIe Port service for a root port or event collector.
  24 + * @srv_mask: Bit mask of services that can be enabled for @port.
  25 + *
  26 + * Invoked when @port is identified as a PCIe port device. To avoid conflicts
  27 + * with the BIOS PCIe port native services support requires the BIOS to yield
  28 + * control of these services to the kernel. The mask of services that the BIOS
  29 + * allows to be enabled for @port is written to @srv_mask.
  30 + *
  31 + * NOTE: It turns out that we cannot do that for individual port services
  32 + * separately, because that would make some systems work incorrectly.
  33 + */
  34 +int pcie_port_acpi_setup(struct pci_dev *port, int *srv_mask)
  35 +{
  36 + acpi_status status;
  37 + acpi_handle handle;
  38 + u32 flags;
  39 +
  40 + if (acpi_pci_disabled)
  41 + return 0;
  42 +
  43 + handle = acpi_find_root_bridge_handle(port);
  44 + if (!handle)
  45 + return -EINVAL;
  46 +
  47 + flags = OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL
  48 + | OSC_PCI_EXPRESS_NATIVE_HP_CONTROL
  49 + | OSC_PCI_EXPRESS_PME_CONTROL;
  50 +
  51 + if (pci_aer_available()) {
  52 + if (pcie_aer_get_firmware_first(port))
  53 + dev_dbg(&port->dev, "PCIe errors handled by BIOS.\n");
  54 + else
  55 + flags |= OSC_PCI_EXPRESS_AER_CONTROL;
  56 + }
  57 +
  58 + status = acpi_pci_osc_control_set(handle, &flags,
  59 + OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL);
  60 + if (ACPI_FAILURE(status)) {
  61 + dev_dbg(&port->dev, "ACPI _OSC request failed (code %d)\n",
  62 + status);
  63 + return -ENODEV;
  64 + }
  65 +
  66 + dev_info(&port->dev, "ACPI _OSC control granted for 0x%02x\n", flags);
  67 +
  68 + *srv_mask = PCIE_PORT_SERVICE_VC;
  69 + if (flags & OSC_PCI_EXPRESS_NATIVE_HP_CONTROL)
  70 + *srv_mask |= PCIE_PORT_SERVICE_HP;
  71 + if (flags & OSC_PCI_EXPRESS_PME_CONTROL)
  72 + *srv_mask |= PCIE_PORT_SERVICE_PME;
  73 + if (flags & OSC_PCI_EXPRESS_AER_CONTROL)
  74 + *srv_mask |= PCIE_PORT_SERVICE_AER;
  75 +
  76 + return 0;
  77 +}
drivers/pci/pcie/portdrv_core.c
... ... @@ -14,6 +14,8 @@
14 14 #include <linux/string.h>
15 15 #include <linux/slab.h>
16 16 #include <linux/pcieport_if.h>
  17 +#include <linux/aer.h>
  18 +#include <linux/pci-aspm.h>
17 19  
18 20 #include "../pci.h"
19 21 #include "portdrv.h"
20 22  
21 23  
22 24  
23 25  
... ... @@ -236,23 +238,40 @@
236 238 int services = 0, pos;
237 239 u16 reg16;
238 240 u32 reg32;
  241 + int cap_mask;
  242 + int err;
239 243  
  244 + err = pcie_port_platform_notify(dev, &cap_mask);
  245 + if (pcie_ports_auto) {
  246 + if (err) {
  247 + pcie_no_aspm();
  248 + return 0;
  249 + }
  250 + } else {
  251 + cap_mask = PCIE_PORT_SERVICE_PME | PCIE_PORT_SERVICE_HP
  252 + | PCIE_PORT_SERVICE_VC;
  253 + if (pci_aer_available())
  254 + cap_mask |= PCIE_PORT_SERVICE_AER;
  255 + }
  256 +
240 257 pos = pci_pcie_cap(dev);
241 258 pci_read_config_word(dev, pos + PCI_EXP_FLAGS, &reg16);
242 259 /* Hot-Plug Capable */
243   - if (reg16 & PCI_EXP_FLAGS_SLOT) {
  260 + if ((cap_mask & PCIE_PORT_SERVICE_HP) && (reg16 & PCI_EXP_FLAGS_SLOT)) {
244 261 pci_read_config_dword(dev, pos + PCI_EXP_SLTCAP, &reg32);
245 262 if (reg32 & PCI_EXP_SLTCAP_HPC)
246 263 services |= PCIE_PORT_SERVICE_HP;
247 264 }
248 265 /* AER capable */
249   - if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR))
  266 + if ((cap_mask & PCIE_PORT_SERVICE_AER)
  267 + && pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR))
250 268 services |= PCIE_PORT_SERVICE_AER;
251 269 /* VC support */
252 270 if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_VC))
253 271 services |= PCIE_PORT_SERVICE_VC;
254 272 /* Root ports are capable of generating PME too */
255   - if (dev->pcie_type == PCI_EXP_TYPE_ROOT_PORT)
  273 + if ((cap_mask & PCIE_PORT_SERVICE_PME)
  274 + && dev->pcie_type == PCI_EXP_TYPE_ROOT_PORT)
256 275 services |= PCIE_PORT_SERVICE_PME;
257 276  
258 277 return services;
drivers/pci/pcie/portdrv_pci.c
... ... @@ -15,6 +15,7 @@
15 15 #include <linux/pcieport_if.h>
16 16 #include <linux/aer.h>
17 17 #include <linux/dmi.h>
  18 +#include <linux/pci-aspm.h>
18 19  
19 20 #include "portdrv.h"
20 21 #include "aer/aerdrv.h"
21 22  
22 23  
... ... @@ -32,10 +33,23 @@
32 33 /* If this switch is set, PCIe port native services should not be enabled. */
33 34 bool pcie_ports_disabled;
34 35  
  36 +/*
  37 + * If this switch is set, ACPI _OSC will be used to determine whether or not to
  38 + * enable PCIe port native services.
  39 + */
  40 +bool pcie_ports_auto = true;
  41 +
35 42 static int __init pcie_port_setup(char *str)
36 43 {
37   - if (!strncmp(str, "compat", 6))
  44 + if (!strncmp(str, "compat", 6)) {
38 45 pcie_ports_disabled = true;
  46 + } else if (!strncmp(str, "native", 6)) {
  47 + pcie_ports_disabled = false;
  48 + pcie_ports_auto = false;
  49 + } else if (!strncmp(str, "auto", 4)) {
  50 + pcie_ports_disabled = false;
  51 + pcie_ports_auto = true;
  52 + }
39 53  
40 54 return 1;
41 55 }
42 56  
... ... @@ -313,8 +327,10 @@
313 327 {
314 328 int retval;
315 329  
316   - if (pcie_ports_disabled)
  330 + if (pcie_ports_disabled) {
  331 + pcie_no_aspm();
317 332 return -EACCES;
  333 + }
318 334  
319 335 dmi_check_system(pcie_portdrv_dmi_table);
320 336