aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pci
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pci')
-rw-r--r--drivers/pci/Makefile4
-rw-r--r--drivers/pci/bus.c53
-rw-r--r--drivers/pci/dmar.c6
-rw-r--r--drivers/pci/hotplug/cpqphp_sysfs.c13
-rw-r--r--drivers/pci/hotplug/ibmphp_hpc.c4
-rw-r--r--drivers/pci/hotplug/pciehp.h2
-rw-r--r--drivers/pci/hotplug/pciehp_core.c18
-rw-r--r--drivers/pci/hotplug/pciehp_ctrl.c9
-rw-r--r--drivers/pci/hotplug/pciehp_hpc.c20
-rw-r--r--drivers/pci/hotplug/shpchp.h2
-rw-r--r--drivers/pci/hotplug/shpchp_core.c20
-rw-r--r--drivers/pci/hotplug/shpchp_ctrl.c7
-rw-r--r--drivers/pci/hotplug/shpchp_hpc.c26
-rw-r--r--drivers/pci/msi.h4
-rw-r--r--drivers/pci/pci.c79
-rw-r--r--drivers/pci/pci.h3
-rw-r--r--drivers/pci/pcie/aer/aer_inject.c1
-rw-r--r--drivers/pci/pcie/aer/aerdrv.c2
-rw-r--r--drivers/pci/pcie/aer/aerdrv.h3
-rw-r--r--drivers/pci/pcie/aer/aerdrv_acpi.c34
-rw-r--r--drivers/pci/pcie/aer/aerdrv_core.c2
-rw-r--r--drivers/pci/pcie/portdrv_acpi.c2
-rw-r--r--drivers/pci/probe.c4
-rw-r--r--drivers/pci/proc.c4
-rw-r--r--drivers/pci/quirks.c34
-rw-r--r--drivers/pci/setup-res.c2
26 files changed, 270 insertions, 88 deletions
diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
index d5e2705..f01e344 100644
--- a/drivers/pci/Makefile
+++ b/drivers/pci/Makefile
@@ -67,6 +67,4 @@ obj-$(CONFIG_PCI_STUB) += pci-stub.o
obj-$(CONFIG_XEN_PCIDEV_FRONTEND) += xen-pcifront.o
-ifeq ($(CONFIG_PCI_DEBUG),y)
-EXTRA_CFLAGS += -DDEBUG
-endif
+ccflags-$(CONFIG_PCI_DEBUG) := -DDEBUG
diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c
index 69546e9..5624db8 100644
--- a/drivers/pci/bus.c
+++ b/drivers/pci/bus.c
@@ -64,6 +64,49 @@ void pci_bus_remove_resources(struct pci_bus *bus)
}
}
+/*
+ * Find the highest-address bus resource below the cursor "res". If the
+ * cursor is NULL, return the highest resource.
+ */
+static struct resource *pci_bus_find_resource_prev(struct pci_bus *bus,
+ unsigned int type,
+ struct resource *res)
+{
+ struct resource *r, *prev = NULL;
+ int i;
+
+ pci_bus_for_each_resource(bus, r, i) {
+ if (!r)
+ continue;
+
+ if ((r->flags & IORESOURCE_TYPE_BITS) != type)
+ continue;
+
+ /* If this resource is at or past the cursor, skip it */
+ if (res) {
+ if (r == res)
+ continue;
+ if (r->end > res->end)
+ continue;
+ if (r->end == res->end && r->start > res->start)
+ continue;
+ }
+
+ if (!prev)
+ prev = r;
+
+ /*
+ * A small resource is higher than a large one that ends at
+ * the same address.
+ */
+ if (r->end > prev->end ||
+ (r->end == prev->end && r->start > prev->start))
+ prev = r;
+ }
+
+ return prev;
+}
+
/**
* pci_bus_alloc_resource - allocate a resource from a parent bus
* @bus: PCI bus
@@ -89,9 +132,10 @@ pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res,
resource_size_t),
void *alignf_data)
{
- int i, ret = -ENOMEM;
+ int ret = -ENOMEM;
struct resource *r;
resource_size_t max = -1;
+ unsigned int type = res->flags & IORESOURCE_TYPE_BITS;
type_mask |= IORESOURCE_IO | IORESOURCE_MEM;
@@ -99,10 +143,9 @@ pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res,
if (!(res->flags & IORESOURCE_MEM_64))
max = PCIBIOS_MAX_MEM_32;
- pci_bus_for_each_resource(bus, r, i) {
- if (!r)
- continue;
-
+ /* Look for space at highest addresses first */
+ r = pci_bus_find_resource_prev(bus, type, NULL);
+ for ( ; r; r = pci_bus_find_resource_prev(bus, type, r)) {
/* type_mask must match */
if ((res->flags ^ r->flags) & type_mask)
continue;
diff --git a/drivers/pci/dmar.c b/drivers/pci/dmar.c
index 3de3a43..0157708 100644
--- a/drivers/pci/dmar.c
+++ b/drivers/pci/dmar.c
@@ -36,6 +36,7 @@
#include <linux/tboot.h>
#include <linux/dmi.h>
#include <linux/slab.h>
+#include <asm/iommu_table.h>
#define PREFIX "DMAR: "
@@ -687,7 +688,7 @@ failed:
return 0;
}
-void __init detect_intel_iommu(void)
+int __init detect_intel_iommu(void)
{
int ret;
@@ -723,6 +724,8 @@ void __init detect_intel_iommu(void)
}
early_acpi_os_unmap_memory(dmar_tbl, dmar_tbl_size);
dmar_tbl = NULL;
+
+ return ret ? 1 : -ENODEV;
}
@@ -1455,3 +1458,4 @@ int __init dmar_ir_support(void)
return 0;
return dmar->flags & 0x1;
}
+IOMMU_INIT_POST(detect_intel_iommu);
diff --git a/drivers/pci/hotplug/cpqphp_sysfs.c b/drivers/pci/hotplug/cpqphp_sysfs.c
index 5621532..4cb3044 100644
--- a/drivers/pci/hotplug/cpqphp_sysfs.c
+++ b/drivers/pci/hotplug/cpqphp_sysfs.c
@@ -34,10 +34,11 @@
#include <linux/workqueue.h>
#include <linux/pci.h>
#include <linux/pci_hotplug.h>
-#include <linux/smp_lock.h>
+#include <linux/mutex.h>
#include <linux/debugfs.h>
#include "cpqphp.h"
+static DEFINE_MUTEX(cpqphp_mutex);
static int show_ctrl (struct controller *ctrl, char *buf)
{
char *out = buf;
@@ -147,7 +148,7 @@ static int open(struct inode *inode, struct file *file)
struct ctrl_dbg *dbg;
int retval = -ENOMEM;
- lock_kernel();
+ mutex_lock(&cpqphp_mutex);
dbg = kmalloc(sizeof(*dbg), GFP_KERNEL);
if (!dbg)
goto exit;
@@ -160,7 +161,7 @@ static int open(struct inode *inode, struct file *file)
file->private_data = dbg;
retval = 0;
exit:
- unlock_kernel();
+ mutex_unlock(&cpqphp_mutex);
return retval;
}
@@ -169,7 +170,7 @@ static loff_t lseek(struct file *file, loff_t off, int whence)
struct ctrl_dbg *dbg;
loff_t new = -1;
- lock_kernel();
+ mutex_lock(&cpqphp_mutex);
dbg = file->private_data;
switch (whence) {
@@ -181,10 +182,10 @@ static loff_t lseek(struct file *file, loff_t off, int whence)
break;
}
if (new < 0 || new > dbg->size) {
- unlock_kernel();
+ mutex_unlock(&cpqphp_mutex);
return -EINVAL;
}
- unlock_kernel();
+ mutex_unlock(&cpqphp_mutex);
return (file->f_pos = new);
}
diff --git a/drivers/pci/hotplug/ibmphp_hpc.c b/drivers/pci/hotplug/ibmphp_hpc.c
index 1aaf3f3..f59ed30 100644
--- a/drivers/pci/hotplug/ibmphp_hpc.c
+++ b/drivers/pci/hotplug/ibmphp_hpc.c
@@ -133,8 +133,8 @@ void __init ibmphp_hpc_initvars (void)
debug ("%s - Entry\n", __func__);
mutex_init(&sem_hpcaccess);
- init_MUTEX (&semOperations);
- init_MUTEX_LOCKED (&sem_exit);
+ sema_init(&semOperations, 1);
+ sema_init(&sem_exit, 0);
to_debug = 0;
debug ("%s - Exit\n", __func__);
diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h
index 73d5139..838f571 100644
--- a/drivers/pci/hotplug/pciehp.h
+++ b/drivers/pci/hotplug/pciehp.h
@@ -36,6 +36,7 @@
#include <linux/sched.h> /* signal_pending() */
#include <linux/pcieport_if.h>
#include <linux/mutex.h>
+#include <linux/workqueue.h>
#define MY_NAME "pciehp"
@@ -44,6 +45,7 @@ extern int pciehp_poll_time;
extern int pciehp_debug;
extern int pciehp_force;
extern struct workqueue_struct *pciehp_wq;
+extern struct workqueue_struct *pciehp_ordered_wq;
#define dbg(format, arg...) \
do { \
diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c
index aa5f3ff..7ac8358 100644
--- a/drivers/pci/hotplug/pciehp_core.c
+++ b/drivers/pci/hotplug/pciehp_core.c
@@ -43,6 +43,7 @@ int pciehp_poll_mode;
int pciehp_poll_time;
int pciehp_force;
struct workqueue_struct *pciehp_wq;
+struct workqueue_struct *pciehp_ordered_wq;
#define DRIVER_VERSION "0.4"
#define DRIVER_AUTHOR "Dan Zink <dan.zink@compaq.com>, Greg Kroah-Hartman <greg@kroah.com>, Dely Sy <dely.l.sy@intel.com>"
@@ -340,18 +341,33 @@ static int __init pcied_init(void)
{
int retval = 0;
+ pciehp_wq = alloc_workqueue("pciehp", 0, 0);
+ if (!pciehp_wq)
+ return -ENOMEM;
+
+ pciehp_ordered_wq = alloc_ordered_workqueue("pciehp_ordered", 0);
+ if (!pciehp_ordered_wq) {
+ destroy_workqueue(pciehp_wq);
+ return -ENOMEM;
+ }
+
pciehp_firmware_init();
retval = pcie_port_service_register(&hpdriver_portdrv);
dbg("pcie_port_service_register = %d\n", retval);
info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
- if (retval)
+ if (retval) {
+ destroy_workqueue(pciehp_ordered_wq);
+ destroy_workqueue(pciehp_wq);
dbg("Failure to register service\n");
+ }
return retval;
}
static void __exit pcied_cleanup(void)
{
dbg("unload_pciehpd()\n");
+ destroy_workqueue(pciehp_ordered_wq);
+ destroy_workqueue(pciehp_wq);
pcie_port_service_unregister(&hpdriver_portdrv);
info(DRIVER_DESC " version: " DRIVER_VERSION " unloaded\n");
}
diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c
index 8f58148..085dbb5 100644
--- a/drivers/pci/hotplug/pciehp_ctrl.c
+++ b/drivers/pci/hotplug/pciehp_ctrl.c
@@ -32,7 +32,6 @@
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/pci.h>
-#include <linux/workqueue.h>
#include "../pci.h"
#include "pciehp.h"
@@ -50,7 +49,7 @@ static int queue_interrupt_event(struct slot *p_slot, u32 event_type)
info->p_slot = p_slot;
INIT_WORK(&info->work, interrupt_event_handler);
- schedule_work(&info->work);
+ queue_work(pciehp_wq, &info->work);
return 0;
}
@@ -345,7 +344,7 @@ void pciehp_queue_pushbutton_work(struct work_struct *work)
kfree(info);
goto out;
}
- queue_work(pciehp_wq, &info->work);
+ queue_work(pciehp_ordered_wq, &info->work);
out:
mutex_unlock(&p_slot->lock);
}
@@ -378,7 +377,7 @@ static void handle_button_press_event(struct slot *p_slot)
if (ATTN_LED(ctrl))
pciehp_set_attention_status(p_slot, 0);
- schedule_delayed_work(&p_slot->work, 5*HZ);
+ queue_delayed_work(pciehp_wq, &p_slot->work, 5*HZ);
break;
case BLINKINGOFF_STATE:
case BLINKINGON_STATE:
@@ -440,7 +439,7 @@ static void handle_surprise_event(struct slot *p_slot)
else
p_slot->state = POWERON_STATE;
- queue_work(pciehp_wq, &info->work);
+ queue_work(pciehp_ordered_wq, &info->work);
}
static void interrupt_event_handler(struct work_struct *work)
diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c
index 0cd4204..50a23da 100644
--- a/drivers/pci/hotplug/pciehp_hpc.c
+++ b/drivers/pci/hotplug/pciehp_hpc.c
@@ -41,8 +41,6 @@
#include "../pci.h"
#include "pciehp.h"
-static atomic_t pciehp_num_controllers = ATOMIC_INIT(0);
-
static inline int pciehp_readw(struct controller *ctrl, int reg, u16 *value)
{
struct pci_dev *dev = ctrl->pcie->port;
@@ -805,8 +803,8 @@ static void pcie_cleanup_slot(struct controller *ctrl)
{
struct slot *slot = ctrl->slot;
cancel_delayed_work(&slot->work);
- flush_scheduled_work();
flush_workqueue(pciehp_wq);
+ flush_workqueue(pciehp_ordered_wq);
kfree(slot);
}
@@ -912,16 +910,6 @@ struct controller *pcie_init(struct pcie_device *dev)
/* Disable sotfware notification */
pcie_disable_notification(ctrl);
- /*
- * If this is the first controller to be initialized,
- * initialize the pciehp work queue
- */
- if (atomic_add_return(1, &pciehp_num_controllers) == 1) {
- pciehp_wq = create_singlethread_workqueue("pciehpd");
- if (!pciehp_wq)
- goto abort_ctrl;
- }
-
ctrl_info(ctrl, "HPC vendor_id %x device_id %x ss_vid %x ss_did %x\n",
pdev->vendor, pdev->device, pdev->subsystem_vendor,
pdev->subsystem_device);
@@ -941,11 +929,5 @@ void pciehp_release_ctrl(struct controller *ctrl)
{
pcie_shutdown_notification(ctrl);
pcie_cleanup_slot(ctrl);
- /*
- * If this is the last controller to be released, destroy the
- * pciehp work queue
- */
- if (atomic_dec_and_test(&pciehp_num_controllers))
- destroy_workqueue(pciehp_wq);
kfree(ctrl);
}
diff --git a/drivers/pci/hotplug/shpchp.h b/drivers/pci/hotplug/shpchp.h
index d2627e1..e0c90e6 100644
--- a/drivers/pci/hotplug/shpchp.h
+++ b/drivers/pci/hotplug/shpchp.h
@@ -35,6 +35,7 @@
#include <linux/delay.h>
#include <linux/sched.h> /* signal_pending(), struct timer_list */
#include <linux/mutex.h>
+#include <linux/workqueue.h>
#if !defined(MODULE)
#define MY_NAME "shpchp"
@@ -46,6 +47,7 @@ extern int shpchp_poll_mode;
extern int shpchp_poll_time;
extern int shpchp_debug;
extern struct workqueue_struct *shpchp_wq;
+extern struct workqueue_struct *shpchp_ordered_wq;
#define dbg(format, arg...) \
do { \
diff --git a/drivers/pci/hotplug/shpchp_core.c b/drivers/pci/hotplug/shpchp_core.c
index a7bd504..aca972b 100644
--- a/drivers/pci/hotplug/shpchp_core.c
+++ b/drivers/pci/hotplug/shpchp_core.c
@@ -33,7 +33,6 @@
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/pci.h>
-#include <linux/workqueue.h>
#include "shpchp.h"
/* Global variables */
@@ -41,6 +40,7 @@ int shpchp_debug;
int shpchp_poll_mode;
int shpchp_poll_time;
struct workqueue_struct *shpchp_wq;
+struct workqueue_struct *shpchp_ordered_wq;
#define DRIVER_VERSION "0.4"
#define DRIVER_AUTHOR "Dan Zink <dan.zink@compaq.com>, Greg Kroah-Hartman <greg@kroah.com>, Dely Sy <dely.l.sy@intel.com>"
@@ -174,8 +174,8 @@ void cleanup_slots(struct controller *ctrl)
slot = list_entry(tmp, struct slot, slot_list);
list_del(&slot->slot_list);
cancel_delayed_work(&slot->work);
- flush_scheduled_work();
flush_workqueue(shpchp_wq);
+ flush_workqueue(shpchp_ordered_wq);
pci_hp_deregister(slot->hotplug_slot);
}
}
@@ -360,9 +360,23 @@ static int __init shpcd_init(void)
{
int retval = 0;
+ shpchp_wq = alloc_ordered_workqueue("shpchp", 0);
+ if (!shpchp_wq)
+ return -ENOMEM;
+
+ shpchp_ordered_wq = alloc_ordered_workqueue("shpchp_ordered", 0);
+ if (!shpchp_ordered_wq) {
+ destroy_workqueue(shpchp_wq);
+ return -ENOMEM;
+ }
+
retval = pci_register_driver(&shpc_driver);
dbg("%s: pci_register_driver = %d\n", __func__, retval);
info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
+ if (retval) {
+ destroy_workqueue(shpchp_ordered_wq);
+ destroy_workqueue(shpchp_wq);
+ }
return retval;
}
@@ -370,6 +384,8 @@ static void __exit shpcd_cleanup(void)
{
dbg("unload_shpchpd()\n");
pci_unregister_driver(&shpc_driver);
+ destroy_workqueue(shpchp_ordered_wq);
+ destroy_workqueue(shpchp_wq);
info(DRIVER_DESC " version: " DRIVER_VERSION " unloaded\n");
}
diff --git a/drivers/pci/hotplug/shpchp_ctrl.c b/drivers/pci/hotplug/shpchp_ctrl.c
index 3387fbf..b00b09b 100644
--- a/drivers/pci/hotplug/shpchp_ctrl.c
+++ b/drivers/pci/hotplug/shpchp_ctrl.c
@@ -32,7 +32,6 @@
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/pci.h>
-#include <linux/workqueue.h>
#include "../pci.h"
#include "shpchp.h"
@@ -52,7 +51,7 @@ static int queue_interrupt_event(struct slot *p_slot, u32 event_type)
info->p_slot = p_slot;
INIT_WORK(&info->work, interrupt_event_handler);
- schedule_work(&info->work);
+ queue_work(shpchp_wq, &info->work);
return 0;
}
@@ -457,7 +456,7 @@ void shpchp_queue_pushbutton_work(struct work_struct *work)
kfree(info);
goto out;
}
- queue_work(shpchp_wq, &info->work);
+ queue_work(shpchp_ordered_wq, &info->work);
out:
mutex_unlock(&p_slot->lock);
}
@@ -505,7 +504,7 @@ static void handle_button_press_event(struct slot *p_slot)
p_slot->hpc_ops->green_led_blink(p_slot);
p_slot->hpc_ops->set_attention_status(p_slot, 0);
- schedule_delayed_work(&p_slot->work, 5*HZ);
+ queue_delayed_work(shpchp_wq, &p_slot->work, 5*HZ);
break;
case BLINKINGOFF_STATE:
case BLINKINGON_STATE:
diff --git a/drivers/pci/hotplug/shpchp_hpc.c b/drivers/pci/hotplug/shpchp_hpc.c
index d3985e7..36547f0 100644
--- a/drivers/pci/hotplug/shpchp_hpc.c
+++ b/drivers/pci/hotplug/shpchp_hpc.c
@@ -179,8 +179,6 @@
#define SLOT_EVENT_LATCH 0x2
#define SLOT_SERR_INT_MASK 0x3
-static atomic_t shpchp_num_controllers = ATOMIC_INIT(0);
-
static irqreturn_t shpc_isr(int irq, void *dev_id);
static void start_int_poll_timer(struct controller *ctrl, int sec);
static int hpc_check_cmd_status(struct controller *ctrl);
@@ -614,13 +612,6 @@ static void hpc_release_ctlr(struct controller *ctrl)
iounmap(ctrl->creg);
release_mem_region(ctrl->mmio_base, ctrl->mmio_size);
-
- /*
- * If this is the last controller to be released, destroy the
- * shpchpd work queue
- */
- if (atomic_dec_and_test(&shpchp_num_controllers))
- destroy_workqueue(shpchp_wq);
}
static int hpc_power_on_slot(struct slot * slot)
@@ -1077,9 +1068,8 @@ int shpc_init(struct controller *ctrl, struct pci_dev *pdev)
rc = request_irq(ctrl->pci_dev->irq, shpc_isr, IRQF_SHARED,
MY_NAME, (void *)ctrl);
- ctrl_dbg(ctrl, "request_irq %d for hpc%d (returns %d)\n",
- ctrl->pci_dev->irq,
- atomic_read(&shpchp_num_controllers), rc);
+ ctrl_dbg(ctrl, "request_irq %d (returns %d)\n",
+ ctrl->pci_dev->irq, rc);
if (rc) {
ctrl_err(ctrl, "Can't get irq %d for the hotplug "
"controller\n", ctrl->pci_dev->irq);
@@ -1092,18 +1082,6 @@ int shpc_init(struct controller *ctrl, struct pci_dev *pdev)
shpc_get_cur_bus_speed(ctrl);
/*
- * If this is the first controller to be initialized,
- * initialize the shpchpd work queue
- */
- if (atomic_add_return(1, &shpchp_num_controllers) == 1) {
- shpchp_wq = create_singlethread_workqueue("shpchpd");
- if (!shpchp_wq) {
- rc = -ENOMEM;
- goto abort_iounmap;
- }
- }
-
- /*
* Unmask all event interrupts of all slots
*/
for (hp_slot = 0; hp_slot < ctrl->num_slots; hp_slot++) {
diff --git a/drivers/pci/msi.h b/drivers/pci/msi.h
index de27c1c..feff3be 100644
--- a/drivers/pci/msi.h
+++ b/drivers/pci/msi.h
@@ -22,8 +22,8 @@
#define is_64bit_address(control) (!!(control & PCI_MSI_FLAGS_64BIT))
#define is_mask_bit_support(control) (!!(control & PCI_MSI_FLAGS_MASKBIT))
-#define msix_table_offset_reg(base) (base + 0x04)
-#define msix_pba_offset_reg(base) (base + 0x08)
+#define msix_table_offset_reg(base) (base + PCI_MSIX_TABLE)
+#define msix_pba_offset_reg(base) (base + PCI_MSIX_PBA)
#define msix_table_size(control) ((control & PCI_MSIX_FLAGS_QSIZE)+1)
#define multi_msix_capable(control) msix_table_size((control))
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 7fa3cbd..e98c810 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -38,6 +38,19 @@ EXPORT_SYMBOL(pci_pci_problems);
unsigned int pci_pm_d3_delay;
+static void pci_pme_list_scan(struct work_struct *work);
+
+static LIST_HEAD(pci_pme_list);
+static DEFINE_MUTEX(pci_pme_list_mutex);
+static DECLARE_DELAYED_WORK(pci_pme_work, pci_pme_list_scan);
+
+struct pci_pme_device {
+ struct list_head list;
+ struct pci_dev *dev;
+};
+
+#define PME_TIMEOUT 1000 /* How long between PME checks */
+
static void pci_dev_d3_sleep(struct pci_dev *dev)
{
unsigned int delay = dev->d3_delay;
@@ -1331,6 +1344,32 @@ bool pci_pme_capable(struct pci_dev *dev, pci_power_t state)
return !!(dev->pme_support & (1 << state));
}
+static void pci_pme_list_scan(struct work_struct *work)
+{
+ struct pci_pme_device *pme_dev;
+
+ mutex_lock(&pci_pme_list_mutex);
+ if (!list_empty(&pci_pme_list)) {
+ list_for_each_entry(pme_dev, &pci_pme_list, list)
+ pci_pme_wakeup(pme_dev->dev, NULL);
+ schedule_delayed_work(&pci_pme_work, msecs_to_jiffies(PME_TIMEOUT));
+ }
+ mutex_unlock(&pci_pme_list_mutex);
+}
+
+/**
+ * pci_external_pme - is a device an external PCI PME source?
+ * @dev: PCI device to check
+ *
+ */
+
+static bool pci_external_pme(struct pci_dev *dev)
+{
+ if (pci_is_pcie(dev) || dev->bus->number == 0)
+ return false;
+ return true;
+}
+
/**
* pci_pme_active - enable or disable PCI device's PME# function
* @dev: PCI device to handle.
@@ -1354,6 +1393,44 @@ void pci_pme_active(struct pci_dev *dev, bool enable)
pci_write_config_word(dev, dev->pm_cap + PCI_PM_CTRL, pmcsr);
+ /* PCI (as opposed to PCIe) PME requires that the device have
+ its PME# line hooked up correctly. Not all hardware vendors
+ do this, so the PME never gets delivered and the device
+ remains asleep. The easiest way around this is to
+ periodically walk the list of suspended devices and check
+ whether any have their PME flag set. The assumption is that
+ we'll wake up often enough anyway that this won't be a huge
+ hit, and the power savings from the devices will still be a
+ win. */
+
+ if (pci_external_pme(dev)) {
+ struct pci_pme_device *pme_dev;
+ if (enable) {
+ pme_dev = kmalloc(sizeof(struct pci_pme_device),
+ GFP_KERNEL);
+ if (!pme_dev)
+ goto out;
+ pme_dev->dev = dev;
+ mutex_lock(&pci_pme_list_mutex);
+ list_add(&pme_dev->list, &pci_pme_list);
+ if (list_is_singular(&pci_pme_list))
+ schedule_delayed_work(&pci_pme_work,
+ msecs_to_jiffies(PME_TIMEOUT));
+ mutex_unlock(&pci_pme_list_mutex);
+ } else {
+ mutex_lock(&pci_pme_list_mutex);
+ list_for_each_entry(pme_dev, &pci_pme_list, list) {
+ if (pme_dev->dev == dev) {
+ list_del(&pme_dev->list);
+ kfree(pme_dev);
+ break;
+ }
+ }
+ mutex_unlock(&pci_pme_list_mutex);
+ }
+ }
+
+out:
dev_printk(KERN_DEBUG, &dev->dev, "PME# %s\n",
enable ? "enabled" : "disabled");
}
@@ -2689,7 +2766,7 @@ int pcie_get_readrq(struct pci_dev *dev)
ret = pci_read_config_word(dev, cap + PCI_EXP_DEVCTL, &ctl);
if (!ret)
- ret = 128 << ((ctl & PCI_EXP_DEVCTL_READRQ) >> 12);
+ ret = 128 << ((ctl & PCI_EXP_DEVCTL_READRQ) >> 12);
return ret;
}
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index 6beb11b..f5c7c38 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -63,11 +63,8 @@ struct pci_platform_pm_ops {
extern int pci_set_platform_pm(struct pci_platform_pm_ops *ops);
extern void pci_update_current_state(struct pci_dev *dev, pci_power_t state);
extern void pci_disable_enabled_device(struct pci_dev *dev);
-extern bool pci_check_pme_status(struct pci_dev *dev);
extern int pci_finish_runtime_suspend(struct pci_dev *dev);
-extern void pci_wakeup_event(struct pci_dev *dev);
extern int __pci_pme_wakeup(struct pci_dev *dev, void *ign);
-extern void pci_pme_wakeup_bus(struct pci_bus *bus);
extern void pci_pm_init(struct pci_dev *dev);
extern void platform_pci_wakeup_init(struct pci_dev *dev);
extern void pci_allocate_cap_save_buffers(struct pci_dev *dev);
diff --git a/drivers/pci/pcie/aer/aer_inject.c b/drivers/pci/pcie/aer/aer_inject.c
index 9099246..b3cf622 100644
--- a/drivers/pci/pcie/aer/aer_inject.c
+++ b/drivers/pci/pcie/aer/aer_inject.c
@@ -472,6 +472,7 @@ static ssize_t aer_inject_write(struct file *filp, const char __user *ubuf,
static const struct file_operations aer_inject_fops = {
.write = aer_inject_write,
.owner = THIS_MODULE,
+ .llseek = noop_llseek,
};
static struct miscdevice aer_inject_device = {
diff --git a/drivers/pci/pcie/aer/aerdrv.c b/drivers/pci/pcie/aer/aerdrv.c
index f409948..2b2b650 100644
--- a/drivers/pci/pcie/aer/aerdrv.c
+++ b/drivers/pci/pcie/aer/aerdrv.c
@@ -416,7 +416,7 @@ static void aer_error_resume(struct pci_dev *dev)
*/
static int __init aer_service_init(void)
{
- if (!pci_aer_available())
+ if (!pci_aer_available() || aer_acpi_firmware_first())
return -ENXIO;
return pcie_port_service_register(&aerdriver);
}
diff --git a/drivers/pci/pcie/aer/aerdrv.h b/drivers/pci/pcie/aer/aerdrv.h
index 80c11d1..9656e30 100644
--- a/drivers/pci/pcie/aer/aerdrv.h
+++ b/drivers/pci/pcie/aer/aerdrv.h
@@ -132,6 +132,7 @@ static inline int aer_osc_setup(struct pcie_device *pciedev)
#ifdef CONFIG_ACPI_APEI
extern int pcie_aer_get_firmware_first(struct pci_dev *pci_dev);
+extern bool aer_acpi_firmware_first(void);
#else
static inline int pcie_aer_get_firmware_first(struct pci_dev *pci_dev)
{
@@ -139,6 +140,8 @@ static inline int pcie_aer_get_firmware_first(struct pci_dev *pci_dev)
return pci_dev->__aer_firmware_first;
return 0;
}
+
+static inline bool aer_acpi_firmware_first(void) { return false; }
#endif
static inline void pcie_aer_force_firmware_first(struct pci_dev *pci_dev,
diff --git a/drivers/pci/pcie/aer/aerdrv_acpi.c b/drivers/pci/pcie/aer/aerdrv_acpi.c
index 2bb9b89..275bf15 100644
--- a/drivers/pci/pcie/aer/aerdrv_acpi.c
+++ b/drivers/pci/pcie/aer/aerdrv_acpi.c
@@ -93,4 +93,38 @@ int pcie_aer_get_firmware_first(struct pci_dev *dev)
aer_set_firmware_first(dev);
return dev->__aer_firmware_first;
}
+
+static bool aer_firmware_first;
+
+static int aer_hest_parse_aff(struct acpi_hest_header *hest_hdr, void *data)
+{
+ struct acpi_hest_aer_common *p;
+
+ if (aer_firmware_first)
+ return 0;
+
+ switch (hest_hdr->type) {
+ case ACPI_HEST_TYPE_AER_ROOT_PORT:
+ case ACPI_HEST_TYPE_AER_ENDPOINT:
+ case ACPI_HEST_TYPE_AER_BRIDGE:
+ p = (struct acpi_hest_aer_common *)(hest_hdr + 1);
+ aer_firmware_first = !!(p->flags & ACPI_HEST_FIRMWARE_FIRST);
+ default:
+ return 0;
+ }
+}
+
+/**
+ * aer_acpi_firmware_first - Check if APEI should control AER.
+ */
+bool aer_acpi_firmware_first(void)
+{
+ static bool parsed = false;
+
+ if (!parsed) {
+ apei_hest_parse(aer_hest_parse_aff, NULL);
+ parsed = true;
+ }
+ return aer_firmware_first;
+}
#endif
diff --git a/drivers/pci/pcie/aer/aerdrv_core.c b/drivers/pci/pcie/aer/aerdrv_core.c
index 29e268f..43421fb 100644
--- a/drivers/pci/pcie/aer/aerdrv_core.c
+++ b/drivers/pci/pcie/aer/aerdrv_core.c
@@ -754,7 +754,7 @@ void aer_isr(struct work_struct *work)
{
struct aer_rpc *rpc = container_of(work, struct aer_rpc, dpc_handler);
struct pcie_device *p_device = rpc->rpd;
- struct aer_err_source e_src;
+ struct aer_err_source uninitialized_var(e_src);
mutex_lock(&rpc->rpc_mutex);
while (get_e_source(rpc, &e_src))
diff --git a/drivers/pci/pcie/portdrv_acpi.c b/drivers/pci/pcie/portdrv_acpi.c
index b7c4cb1..5982b6a 100644
--- a/drivers/pci/pcie/portdrv_acpi.c
+++ b/drivers/pci/pcie/portdrv_acpi.c
@@ -49,7 +49,7 @@ int pcie_port_acpi_setup(struct pci_dev *port, int *srv_mask)
| OSC_PCI_EXPRESS_PME_CONTROL;
if (pci_aer_available()) {
- if (pcie_aer_get_firmware_first(port))
+ if (aer_acpi_firmware_first())
dev_dbg(&port->dev, "PCIe errors handled by BIOS.\n");
else
flags |= OSC_PCI_EXPRESS_AER_CONTROL;
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index 12625d9..c84900d 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -961,8 +961,8 @@ int pci_setup_device(struct pci_dev *dev)
dev->class = class;
class >>= 8;
- dev_dbg(&dev->dev, "found [%04x:%04x] class %06x header type %02x\n",
- dev->vendor, dev->device, class, dev->hdr_type);
+ dev_printk(KERN_DEBUG, &dev->dev, "[%04x:%04x] type %d class %#08x\n",
+ dev->vendor, dev->device, dev->hdr_type, class);
/* need to have dev->class ready */
dev->cfg_size = pci_cfg_space_size(dev);
diff --git a/drivers/pci/proc.c b/drivers/pci/proc.c
index 01f0306..297b72c 100644
--- a/drivers/pci/proc.c
+++ b/drivers/pci/proc.c
@@ -212,8 +212,6 @@ static long proc_bus_pci_ioctl(struct file *file, unsigned int cmd,
#endif /* HAVE_PCI_MMAP */
int ret = 0;
- lock_kernel();
-
switch (cmd) {
case PCIIOC_CONTROLLER:
ret = pci_domain_nr(dev->bus);
@@ -242,7 +240,6 @@ static long proc_bus_pci_ioctl(struct file *file, unsigned int cmd,
break;
};
- unlock_kernel();
return ret;
}
@@ -306,6 +303,7 @@ static const struct file_operations proc_bus_pci_operations = {
.read = proc_bus_pci_read,
.write = proc_bus_pci_write,
.unlocked_ioctl = proc_bus_pci_ioctl,
+ .compat_ioctl = proc_bus_pci_ioctl,
#ifdef HAVE_PCI_MMAP
.open = proc_bus_pci_open,
.release = proc_bus_pci_release,
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index 857ae01..f5c63fe 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -226,6 +226,7 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82439TX, quir
* VIA Apollo KT133 needs PCI latency patch
* Made according to a windows driver based patch by George E. Breese
* see PCI Latency Adjust on http://www.viahardware.com/download/viatweak.shtm
+ * and http://www.georgebreese.com/net/software/#PCI
* Also see http://www.au-ja.org/review-kt133a-1-en.phtml for
* the info on which Mr Breese based his work.
*
@@ -1016,7 +1017,7 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_TOSHIBA, 0x605, quirk_transparent_bridge)
/*
* Common misconfiguration of the MediaGX/Geode PCI master that will
* reduce PCI bandwidth from 70MB/s to 25MB/s. See the GXM/GXLV/GX1
- * datasheets found at http://www.national.com/ds/GX for info on what
+ * datasheets found at http://www.national.com/analog for info on what
* these bits do. <christer@weinigel.se>
*/
static void quirk_mediagx_master(struct pci_dev *dev)
@@ -2296,6 +2297,37 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_NVIDIA,
PCI_DEVICE_ID_NVIDIA_NVENET_15,
nvenet_msi_disable);
+/*
+ * Some versions of the MCP55 bridge from nvidia have a legacy irq routing
+ * config register. This register controls the routing of legacy interrupts
+ * from devices that route through the MCP55. If this register is misprogramed
+ * interrupts are only sent to the bsp, unlike conventional systems where the
+ * irq is broadxast to all online cpus. Not having this register set
+ * properly prevents kdump from booting up properly, so lets make sure that
+ * we have it set correctly.
+ * Note this is an undocumented register.
+ */
+static void __devinit nvbridge_check_legacy_irq_routing(struct pci_dev *dev)
+{
+ u32 cfg;
+
+ pci_read_config_dword(dev, 0x74, &cfg);
+
+ if (cfg & ((1 << 2) | (1 << 15))) {
+ printk(KERN_INFO "Rewriting irq routing register on MCP55\n");
+ cfg &= ~((1 << 2) | (1 << 15));
+ pci_write_config_dword(dev, 0x74, cfg);
+ }
+}
+
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_NVIDIA,
+ PCI_DEVICE_ID_NVIDIA_MCP55_BRIDGE_V0,
+ nvbridge_check_legacy_irq_routing);
+
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_NVIDIA,
+ PCI_DEVICE_ID_NVIDIA_MCP55_BRIDGE_V4,
+ nvbridge_check_legacy_irq_routing);
+
static int __devinit ht_check_msi_mapping(struct pci_dev *dev)
{
int pos, ttl = 48;
diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c
index 2aaa131..bc0e6ee 100644
--- a/drivers/pci/setup-res.c
+++ b/drivers/pci/setup-res.c
@@ -85,7 +85,7 @@ void pci_update_resource(struct pci_dev *dev, int resno)
}
}
res->flags &= ~IORESOURCE_UNSET;
- dev_info(&dev->dev, "BAR %d: set to %pR (PCI address [%#llx-%#llx]\n",
+ dev_info(&dev->dev, "BAR %d: set to %pR (PCI address [%#llx-%#llx])\n",
resno, res, (unsigned long long)region.start,
(unsigned long long)region.end);
}