summaryrefslogtreecommitdiff
path: root/drivers/pci
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pci')
-rw-r--r--drivers/pci/Kconfig12
-rw-r--r--drivers/pci/Makefile11
-rw-r--r--drivers/pci/access.c76
-rw-r--r--drivers/pci/bus.c22
-rw-r--r--drivers/pci/hotplug/Kconfig5
-rw-r--r--drivers/pci/hotplug/acpi_pcihp.c2
-rw-r--r--drivers/pci/hotplug/acpiphp.h11
-rw-r--r--drivers/pci/hotplug/acpiphp_core.c41
-rw-r--r--drivers/pci/hotplug/acpiphp_glue.c143
-rw-r--r--drivers/pci/hotplug/acpiphp_ibm.c1
-rw-r--r--drivers/pci/hotplug/cpci_hotplug_core.c4
-rw-r--r--drivers/pci/hotplug/cpci_hotplug_pci.c2
-rw-r--r--drivers/pci/hotplug/cpcihp_generic.c4
-rw-r--r--drivers/pci/hotplug/cpqphp.h3
-rw-r--r--drivers/pci/hotplug/cpqphp_core.c1
-rw-r--r--drivers/pci/hotplug/cpqphp_ctrl.c3
-rw-r--r--drivers/pci/hotplug/cpqphp_nvram.c1
-rw-r--r--drivers/pci/hotplug/cpqphp_pci.c1
-rw-r--r--drivers/pci/hotplug/cpqphp_sysfs.c3
-rw-r--r--drivers/pci/hotplug/fakephp.c31
-rw-r--r--drivers/pci/hotplug/ibmphp.h2
-rw-r--r--drivers/pci/hotplug/ibmphp_hpc.c2
-rw-r--r--drivers/pci/hotplug/ibmphp_pci.c4
-rw-r--r--drivers/pci/hotplug/pci_hotplug.h236
-rw-r--r--drivers/pci/hotplug/pci_hotplug_core.c168
-rw-r--r--drivers/pci/hotplug/pciehp.h11
-rw-r--r--drivers/pci/hotplug/pciehp_core.c13
-rw-r--r--drivers/pci/hotplug/pciehp_ctrl.c116
-rw-r--r--drivers/pci/hotplug/pciehp_hpc.c10
-rw-r--r--drivers/pci/hotplug/pcihp_skeleton.c11
-rw-r--r--drivers/pci/hotplug/rpadlpar_core.c2
-rw-r--r--drivers/pci/hotplug/rpadlpar_sysfs.c2
-rw-r--r--drivers/pci/hotplug/rpaphp.h2
-rw-r--r--drivers/pci/hotplug/rpaphp_core.c4
-rw-r--r--drivers/pci/hotplug/sgi_hotplug.c37
-rw-r--r--drivers/pci/hotplug/shpchp.h10
-rw-r--r--drivers/pci/hotplug/shpchp_core.c8
-rw-r--r--drivers/pci/hotplug/shpchp_ctrl.c19
-rw-r--r--drivers/pci/hotplug/shpchp_hpc.c88
-rw-r--r--drivers/pci/hotplug/shpchp_sysfs.c4
-rw-r--r--drivers/pci/htirq.c185
-rw-r--r--drivers/pci/msi-altix.c210
-rw-r--r--drivers/pci/msi-apic.c101
-rw-r--r--drivers/pci/msi.c1021
-rw-r--r--drivers/pci/msi.h118
-rw-r--r--drivers/pci/pci-acpi.c10
-rw-r--r--drivers/pci/pci-driver.c35
-rw-r--r--drivers/pci/pci-sysfs.c181
-rw-r--r--drivers/pci/pci.c173
-rw-r--r--drivers/pci/pci.h3
-rw-r--r--drivers/pci/pcie/Kconfig1
-rw-r--r--drivers/pci/pcie/Makefile3
-rw-r--r--drivers/pci/pcie/aer/Kconfig12
-rw-r--r--drivers/pci/pcie/aer/Makefile8
-rw-r--r--drivers/pci/pcie/aer/aerdrv.c345
-rw-r--r--drivers/pci/pcie/aer/aerdrv.h125
-rw-r--r--drivers/pci/pcie/aer/aerdrv_acpi.c68
-rw-r--r--drivers/pci/pcie/aer/aerdrv_core.c758
-rw-r--r--drivers/pci/pcie/aer/aerdrv_errprint.c248
-rw-r--r--drivers/pci/pcie/portdrv.h4
-rw-r--r--drivers/pci/pcie/portdrv_bus.c1
-rw-r--r--drivers/pci/pcie/portdrv_core.c10
-rw-r--r--drivers/pci/pcie/portdrv_pci.c211
-rw-r--r--drivers/pci/probe.c136
-rw-r--r--drivers/pci/proc.c8
-rw-r--r--drivers/pci/quirks.c285
-rw-r--r--drivers/pci/remove.c37
-rw-r--r--drivers/pci/rom.c15
-rw-r--r--drivers/pci/search.c72
-rw-r--r--drivers/pci/setup-bus.c11
70 files changed, 3565 insertions, 1956 deletions
diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig
index c27e782e6df9..f1dd81a1d592 100644
--- a/drivers/pci/Kconfig
+++ b/drivers/pci/Kconfig
@@ -27,14 +27,14 @@ config PCI_MULTITHREAD_PROBE
smaller speedup on single processor machines.
But it can also cause lots of bad things to happen. A number
- of PCI drivers can not properly handle running in this way,
+ of PCI drivers cannot properly handle running in this way,
some will just not work properly at all, while others might
decide to blow up power supplies with a huge load all at once,
so use this option at your own risk.
It is very unwise to use this option if you are not using a
boot process that can handle devices being created in any
- order. A program that can create persistant block and network
+ order. A program that can create persistent block and network
device names (like udev) is a good idea if you wish to use
this option.
@@ -52,3 +52,11 @@ config PCI_DEBUG
When in doubt, say N.
+config HT_IRQ
+ bool "Interrupts on hypertransport devices"
+ default y
+ depends on PCI && X86_LOCAL_APIC && X86_IO_APIC
+ help
+ This allows native hypertransport devices to use interrupts.
+
+ If unsure say Y.
diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
index f2d152b818f0..e3beb784406f 100644
--- a/drivers/pci/Makefile
+++ b/drivers/pci/Makefile
@@ -14,6 +14,12 @@ obj-$(CONFIG_HOTPLUG) += hotplug.o
# Build the PCI Hotplug drivers if we were asked to
obj-$(CONFIG_HOTPLUG_PCI) += hotplug/
+# Build the PCI MSI interrupt support
+obj-$(CONFIG_PCI_MSI) += msi.o
+
+# Build the Hypertransport interrupt support
+obj-$(CONFIG_HT_IRQ) += htirq.o
+
#
# Some architectures use the generic PCI setup functions
#
@@ -27,11 +33,6 @@ obj-$(CONFIG_PPC64) += setup-bus.o
obj-$(CONFIG_MIPS) += setup-bus.o setup-irq.o
obj-$(CONFIG_X86_VISWS) += setup-irq.o
-msiobj-y := msi.o msi-apic.o
-msiobj-$(CONFIG_IA64_GENERIC) += msi-altix.o
-msiobj-$(CONFIG_IA64_SGI_SN2) += msi-altix.o
-obj-$(CONFIG_PCI_MSI) += $(msiobj-y)
-
#
# ACPI Related PCI FW Functions
#
diff --git a/drivers/pci/access.c b/drivers/pci/access.c
index ea16805a153c..fc405f0165d9 100644
--- a/drivers/pci/access.c
+++ b/drivers/pci/access.c
@@ -1,6 +1,8 @@
#include <linux/pci.h>
#include <linux/module.h>
+#include <linux/sched.h>
#include <linux/ioport.h>
+#include <linux/wait.h>
#include "pci.h"
@@ -63,30 +65,42 @@ EXPORT_SYMBOL(pci_bus_write_config_byte);
EXPORT_SYMBOL(pci_bus_write_config_word);
EXPORT_SYMBOL(pci_bus_write_config_dword);
-static u32 pci_user_cached_config(struct pci_dev *dev, int pos)
-{
- u32 data;
+/*
+ * The following routines are to prevent the user from accessing PCI config
+ * space when it's unsafe to do so. Some devices require this during BIST and
+ * we're required to prevent it during D-state transitions.
+ *
+ * We have a bit per device to indicate it's blocked and a global wait queue
+ * for callers to sleep on until devices are unblocked.
+ */
+static DECLARE_WAIT_QUEUE_HEAD(pci_ucfg_wait);
- data = dev->saved_config_space[pos/sizeof(dev->saved_config_space[0])];
- data >>= (pos % sizeof(dev->saved_config_space[0])) * 8;
- return data;
+static noinline void pci_wait_ucfg(struct pci_dev *dev)
+{
+ DECLARE_WAITQUEUE(wait, current);
+
+ __add_wait_queue(&pci_ucfg_wait, &wait);
+ do {
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ spin_unlock_irq(&pci_lock);
+ schedule();
+ spin_lock_irq(&pci_lock);
+ } while (dev->block_ucfg_access);
+ __remove_wait_queue(&pci_ucfg_wait, &wait);
}
#define PCI_USER_READ_CONFIG(size,type) \
int pci_user_read_config_##size \
(struct pci_dev *dev, int pos, type *val) \
{ \
- unsigned long flags; \
int ret = 0; \
u32 data = -1; \
if (PCI_##size##_BAD) return PCIBIOS_BAD_REGISTER_NUMBER; \
- spin_lock_irqsave(&pci_lock, flags); \
- if (likely(!dev->block_ucfg_access)) \
- ret = dev->bus->ops->read(dev->bus, dev->devfn, \
+ spin_lock_irq(&pci_lock); \
+ if (unlikely(dev->block_ucfg_access)) pci_wait_ucfg(dev); \
+ ret = dev->bus->ops->read(dev->bus, dev->devfn, \
pos, sizeof(type), &data); \
- else if (pos < sizeof(dev->saved_config_space)) \
- data = pci_user_cached_config(dev, pos); \
- spin_unlock_irqrestore(&pci_lock, flags); \
+ spin_unlock_irq(&pci_lock); \
*val = (type)data; \
return ret; \
}
@@ -95,14 +109,13 @@ int pci_user_read_config_##size \
int pci_user_write_config_##size \
(struct pci_dev *dev, int pos, type val) \
{ \
- unsigned long flags; \
int ret = -EIO; \
if (PCI_##size##_BAD) return PCIBIOS_BAD_REGISTER_NUMBER; \
- spin_lock_irqsave(&pci_lock, flags); \
- if (likely(!dev->block_ucfg_access)) \
- ret = dev->bus->ops->write(dev->bus, dev->devfn, \
+ spin_lock_irq(&pci_lock); \
+ if (unlikely(dev->block_ucfg_access)) pci_wait_ucfg(dev); \
+ ret = dev->bus->ops->write(dev->bus, dev->devfn, \
pos, sizeof(type), val); \
- spin_unlock_irqrestore(&pci_lock, flags); \
+ spin_unlock_irq(&pci_lock); \
return ret; \
}
@@ -117,21 +130,23 @@ PCI_USER_WRITE_CONFIG(dword, u32)
* pci_block_user_cfg_access - Block userspace PCI config reads/writes
* @dev: pci device struct
*
- * This function blocks any userspace PCI config accesses from occurring.
- * When blocked, any writes will be bit bucketed and reads will return the
- * data saved using pci_save_state for the first 64 bytes of config
- * space and return 0xff for all other config reads.
- **/
+ * When user access is blocked, any reads or writes to config space will
+ * sleep until access is unblocked again. We don't allow nesting of
+ * block/unblock calls.
+ */
void pci_block_user_cfg_access(struct pci_dev *dev)
{
unsigned long flags;
+ int was_blocked;
- pci_save_state(dev);
-
- /* spinlock to synchronize with anyone reading config space now */
spin_lock_irqsave(&pci_lock, flags);
+ was_blocked = dev->block_ucfg_access;
dev->block_ucfg_access = 1;
spin_unlock_irqrestore(&pci_lock, flags);
+
+ /* If we BUG() inside the pci_lock, we're guaranteed to hose
+ * the machine */
+ BUG_ON(was_blocked);
}
EXPORT_SYMBOL_GPL(pci_block_user_cfg_access);
@@ -140,14 +155,19 @@ EXPORT_SYMBOL_GPL(pci_block_user_cfg_access);
* @dev: pci device struct
*
* This function allows userspace PCI config accesses to resume.
- **/
+ */
void pci_unblock_user_cfg_access(struct pci_dev *dev)
{
unsigned long flags;
- /* spinlock to synchronize with anyone reading saved config space */
spin_lock_irqsave(&pci_lock, flags);
+
+ /* This indicates a problem in the caller, but we don't need
+ * to kill them, unlike a double-block above. */
+ WARN_ON(!dev->block_ucfg_access);
+
dev->block_ucfg_access = 0;
+ wake_up_all(&pci_ucfg_wait);
spin_unlock_irqrestore(&pci_lock, flags);
}
EXPORT_SYMBOL_GPL(pci_unblock_user_cfg_access);
diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c
index 5f7db9d2436e..aadaa3c8096b 100644
--- a/drivers/pci/bus.c
+++ b/drivers/pci/bus.c
@@ -77,9 +77,12 @@ pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res,
* This adds a single pci device to the global
* device list and adds sysfs and procfs entries
*/
-void __devinit pci_bus_add_device(struct pci_dev *dev)
+int __devinit pci_bus_add_device(struct pci_dev *dev)
{
- device_add(&dev->dev);
+ int retval;
+ retval = device_add(&dev->dev);
+ if (retval)
+ return retval;
down_write(&pci_bus_sem);
list_add_tail(&dev->global_list, &pci_devices);
@@ -87,6 +90,7 @@ void __devinit pci_bus_add_device(struct pci_dev *dev)
pci_proc_attach_device(dev);
pci_create_sysfs_dev_files(dev);
+ return 0;
}
/**
@@ -104,6 +108,7 @@ void __devinit pci_bus_add_device(struct pci_dev *dev)
void __devinit pci_bus_add_devices(struct pci_bus *bus)
{
struct pci_dev *dev;
+ int retval;
list_for_each_entry(dev, &bus->devices, bus_list) {
/*
@@ -112,7 +117,9 @@ void __devinit pci_bus_add_devices(struct pci_bus *bus)
*/
if (!list_empty(&dev->global_list))
continue;
- pci_bus_add_device(dev);
+ retval = pci_bus_add_device(dev);
+ if (retval)
+ dev_err(&dev->dev, "Error adding device, continuing\n");
}
list_for_each_entry(dev, &bus->devices, bus_list) {
@@ -129,10 +136,13 @@ void __devinit pci_bus_add_devices(struct pci_bus *bus)
list_add_tail(&dev->subordinate->node,
&dev->bus->children);
up_write(&pci_bus_sem);
- }
+ }
pci_bus_add_devices(dev->subordinate);
-
- sysfs_create_link(&dev->subordinate->class_dev.kobj, &dev->dev.kobj, "bridge");
+ retval = sysfs_create_link(&dev->subordinate->class_dev.kobj,
+ &dev->dev.kobj, "bridge");
+ if (retval)
+ dev_err(&dev->dev, "Error creating sysfs "
+ "bridge symlink, continuing...\n");
}
}
}
diff --git a/drivers/pci/hotplug/Kconfig b/drivers/pci/hotplug/Kconfig
index 8a60f391ffcf..adce4204d87d 100644
--- a/drivers/pci/hotplug/Kconfig
+++ b/drivers/pci/hotplug/Kconfig
@@ -76,7 +76,8 @@ config HOTPLUG_PCI_IBM
config HOTPLUG_PCI_ACPI
tristate "ACPI PCI Hotplug driver"
- depends on (!ACPI_DOCK && ACPI && HOTPLUG_PCI) || (ACPI_DOCK && HOTPLUG_PCI)
+ depends on HOTPLUG_PCI
+ depends on (!ACPI_DOCK && ACPI) || (ACPI_DOCK)
help
Say Y here if you have a system that supports PCI Hotplug using
ACPI.
@@ -157,7 +158,7 @@ config HOTPLUG_PCI_RPA
tristate "RPA PCI Hotplug driver"
depends on HOTPLUG_PCI && PPC_PSERIES && PPC64 && !HOTPLUG_PCI_FAKE
help
- Say Y here if you have a a RPA system that supports PCI Hotplug.
+ Say Y here if you have a RPA system that supports PCI Hotplug.
To compile this driver as a module, choose M here: the
module will be called rpaphp.
diff --git a/drivers/pci/hotplug/acpi_pcihp.c b/drivers/pci/hotplug/acpi_pcihp.c
index 51cb9f817c22..270a33cc08f6 100644
--- a/drivers/pci/hotplug/acpi_pcihp.c
+++ b/drivers/pci/hotplug/acpi_pcihp.c
@@ -29,10 +29,10 @@
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/pci.h>
+#include <linux/pci_hotplug.h>
#include <acpi/acpi.h>
#include <acpi/acpi_bus.h>
#include <acpi/actypes.h>
-#include "pci_hotplug.h"
#define MY_NAME "acpi_pcihp"
diff --git a/drivers/pci/hotplug/acpiphp.h b/drivers/pci/hotplug/acpiphp.h
index be104eced34c..ddbadd95387e 100644
--- a/drivers/pci/hotplug/acpiphp.h
+++ b/drivers/pci/hotplug/acpiphp.h
@@ -38,7 +38,7 @@
#include <linux/acpi.h>
#include <linux/kobject.h> /* for KOBJ_NAME_LEN */
#include <linux/mutex.h>
-#include "pci_hotplug.h"
+#include <linux/pci_hotplug.h>
#define dbg(format, arg...) \
do { \
@@ -62,10 +62,10 @@ struct acpiphp_slot;
struct slot {
struct hotplug_slot *hotplug_slot;
struct acpiphp_slot *acpi_slot;
+ struct hotplug_slot_info info;
+ char name[SLOT_NAME_SIZE];
};
-
-
/**
* struct acpiphp_bridge - PCI bridge information
*
@@ -150,6 +150,11 @@ struct acpiphp_attention_info
struct module *owner;
};
+struct acpiphp_ioapic {
+ struct pci_dev *dev;
+ u32 gsi_base;
+ struct list_head list;
+};
/* PCI bus bridge HID */
#define ACPI_PCI_HOST_HID "PNP0A03"
diff --git a/drivers/pci/hotplug/acpiphp_core.c b/drivers/pci/hotplug/acpiphp_core.c
index e2fef60c2d06..40c79b03c7ef 100644
--- a/drivers/pci/hotplug/acpiphp_core.c
+++ b/drivers/pci/hotplug/acpiphp_core.c
@@ -37,10 +37,10 @@
#include <linux/kernel.h>
#include <linux/pci.h>
+#include <linux/pci_hotplug.h>
#include <linux/slab.h>
#include <linux/smp.h>
#include <linux/smp_lock.h>
-#include "pci_hotplug.h"
#include "acpiphp.h"
#define MY_NAME "acpiphp"
@@ -303,25 +303,15 @@ static int __init init_acpi(void)
/* read initial number of slots */
if (!retval) {
num_slots = acpiphp_get_num_slots();
- if (num_slots == 0)
+ if (num_slots == 0) {
+ acpiphp_glue_exit();
retval = -ENODEV;
+ }
}
return retval;
}
-
-/**
- * make_slot_name - make a slot name that appears in pcihpfs
- * @slot: slot to name
- *
- */
-static void make_slot_name(struct slot *slot)
-{
- snprintf(slot->hotplug_slot->name, SLOT_NAME_SIZE, "%u",
- slot->acpi_slot->sun);
-}
-
/**
* release_slot - free up the memory used by a slot
* @hotplug_slot: slot to free
@@ -332,8 +322,6 @@ static void release_slot(struct hotplug_slot *hotplug_slot)
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
- kfree(slot->hotplug_slot->info);
- kfree(slot->hotplug_slot->name);
kfree(slot->hotplug_slot);
kfree(slot);
}
@@ -342,26 +330,19 @@ static void release_slot(struct hotplug_slot *hotplug_slot)
int acpiphp_register_hotplug_slot(struct acpiphp_slot *acpiphp_slot)
{
struct slot *slot;
- struct hotplug_slot *hotplug_slot;
- struct hotplug_slot_info *hotplug_slot_info;
int retval = -ENOMEM;
slot = kzalloc(sizeof(*slot), GFP_KERNEL);
if (!slot)
goto error;
- slot->hotplug_slot = kzalloc(sizeof(*hotplug_slot), GFP_KERNEL);
+ slot->hotplug_slot = kzalloc(sizeof(*slot->hotplug_slot), GFP_KERNEL);
if (!slot->hotplug_slot)
goto error_slot;
- slot->hotplug_slot->info = kzalloc(sizeof(*hotplug_slot_info),
- GFP_KERNEL);
- if (!slot->hotplug_slot->info)
- goto error_hpslot;
+ slot->hotplug_slot->info = &slot->info;
- slot->hotplug_slot->name = kzalloc(SLOT_NAME_SIZE, GFP_KERNEL);
- if (!slot->hotplug_slot->name)
- goto error_info;
+ slot->hotplug_slot->name = slot->name;
slot->hotplug_slot->private = slot;
slot->hotplug_slot->release = &release_slot;
@@ -376,21 +357,17 @@ int acpiphp_register_hotplug_slot(struct acpiphp_slot *acpiphp_slot)
slot->hotplug_slot->info->cur_bus_speed = PCI_SPEED_UNKNOWN;
acpiphp_slot->slot = slot;
- make_slot_name(slot);
+ snprintf(slot->name, sizeof(slot->name), "%u", slot->acpi_slot->sun);
retval = pci_hp_register(slot->hotplug_slot);
if (retval) {
err("pci_hp_register failed with error %d\n", retval);
- goto error_name;
+ goto error_hpslot;
}
info("Slot [%s] registered\n", slot->hotplug_slot->name);
return 0;
-error_name:
- kfree(slot->hotplug_slot->name);
-error_info:
- kfree(slot->hotplug_slot->info);
error_hpslot:
kfree(slot->hotplug_slot);
error_slot:
diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c
index ae67a8f55ba1..0b9d0db1590a 100644
--- a/drivers/pci/hotplug/acpiphp_glue.c
+++ b/drivers/pci/hotplug/acpiphp_glue.c
@@ -45,14 +45,16 @@
#include <linux/kernel.h>
#include <linux/pci.h>
+#include <linux/pci_hotplug.h>
#include <linux/smp_lock.h>
#include <linux/mutex.h>
#include "../pci.h"
-#include "pci_hotplug.h"
#include "acpiphp.h"
static LIST_HEAD(bridge_list);
+static LIST_HEAD(ioapic_list);
+static DEFINE_SPINLOCK(ioapic_list_lock);
#define MY_NAME "acpiphp_glue"
@@ -797,6 +799,7 @@ ioapic_add(acpi_handle handle, u32 lvl, void *context, void **rv)
struct pci_dev *pdev;
u32 gsi_base;
u64 phys_addr;
+ struct acpiphp_ioapic *ioapic;
/* Evaluate _STA if present */
status = acpi_evaluate_integer(handle, "_STA", NULL, &sta);
@@ -811,41 +814,107 @@ ioapic_add(acpi_handle handle, u32 lvl, void *context, void **rv)
if (get_gsi_base(handle, &gsi_base))
return AE_OK;
+ ioapic = kmalloc(sizeof(*ioapic), GFP_KERNEL);
+ if (!ioapic)
+ return AE_NO_MEMORY;
+
pdev = get_apic_pci_info(handle);
if (!pdev)
- return AE_OK;
+ goto exit_kfree;
- if (pci_enable_device(pdev)) {
- pci_dev_put(pdev);
- return AE_OK;
- }
+ if (pci_enable_device(pdev))
+ goto exit_pci_dev_put;
pci_set_master(pdev);
- if (pci_request_region(pdev, 0, "I/O APIC(acpiphp)")) {
- pci_disable_device(pdev);
- pci_dev_put(pdev);
- return AE_OK;
- }
+ if (pci_request_region(pdev, 0, "I/O APIC(acpiphp)"))
+ goto exit_pci_disable_device;
phys_addr = pci_resource_start(pdev, 0);
- if (acpi_register_ioapic(handle, phys_addr, gsi_base)) {
- pci_release_region(pdev, 0);
- pci_disable_device(pdev);
- pci_dev_put(pdev);
+ if (acpi_register_ioapic(handle, phys_addr, gsi_base))
+ goto exit_pci_release_region;
+
+ ioapic->gsi_base = gsi_base;
+ ioapic->dev = pdev;
+ spin_lock(&ioapic_list_lock);
+ list_add_tail(&ioapic->list, &ioapic_list);
+ spin_unlock(&ioapic_list_lock);
+
+ return AE_OK;
+
+ exit_pci_release_region:
+ pci_release_region(pdev, 0);
+ exit_pci_disable_device:
+ pci_disable_device(pdev);
+ exit_pci_dev_put:
+ pci_dev_put(pdev);
+ exit_kfree:
+ kfree(ioapic);
+
+ return AE_OK;
+}
+
+static acpi_status
+ioapic_remove(acpi_handle handle, u32 lvl, void *context, void **rv)
+{
+ acpi_status status;
+ unsigned long sta;
+ acpi_handle tmp;
+ u32 gsi_base;
+ struct acpiphp_ioapic *pos, *n, *ioapic = NULL;
+
+ /* Evaluate _STA if present */
+ status = acpi_evaluate_integer(handle, "_STA", NULL, &sta);
+ if (ACPI_SUCCESS(status) && sta != ACPI_STA_ALL)
+ return AE_CTRL_DEPTH;
+
+ /* Scan only PCI bus scope */
+ status = acpi_get_handle(handle, "_HID", &tmp);
+ if (ACPI_SUCCESS(status))
+ return AE_CTRL_DEPTH;
+
+ if (get_gsi_base(handle, &gsi_base))
return AE_OK;
+
+ acpi_unregister_ioapic(handle, gsi_base);
+
+ spin_lock(&ioapic_list_lock);
+ list_for_each_entry_safe(pos, n, &ioapic_list, list) {
+ if (pos->gsi_base != gsi_base)
+ continue;
+ ioapic = pos;
+ list_del(&ioapic->list);
+ break;
}
+ spin_unlock(&ioapic_list_lock);
+
+ if (!ioapic)
+ return AE_OK;
+
+ pci_release_region(ioapic->dev, 0);
+ pci_disable_device(ioapic->dev);
+ pci_dev_put(ioapic->dev);
+ kfree(ioapic);
return AE_OK;
}
static int acpiphp_configure_ioapics(acpi_handle handle)
{
+ ioapic_add(handle, 0, NULL, NULL);
acpi_walk_namespace(ACPI_TYPE_DEVICE, handle,
ACPI_UINT32_MAX, ioapic_add, NULL, NULL);
return 0;
}
+static int acpiphp_unconfigure_ioapics(acpi_handle handle)
+{
+ ioapic_remove(handle, 0, NULL, NULL);
+ acpi_walk_namespace(ACPI_TYPE_DEVICE, handle,
+ ACPI_UINT32_MAX, ioapic_remove, NULL, NULL);
+ return 0;
+}
+
static int power_on_slot(struct acpiphp_slot *slot)
{
acpi_status status;
@@ -997,7 +1066,7 @@ acpiphp_bus_add_out:
* @handle: handle to acpi namespace
*
*/
-int acpiphp_bus_trim(acpi_handle handle)
+static int acpiphp_bus_trim(acpi_handle handle)
{
struct acpi_device *device;
int retval;
@@ -1074,10 +1143,11 @@ static int enable_device(struct acpiphp_slot *slot)
pci_bus_assign_resources(bus);
acpiphp_sanitize_bus(bus);
+ acpiphp_set_hpp_values(slot->bridge->handle, bus);
+ list_for_each_entry(func, &slot->funcs, sibling)
+ acpiphp_configure_ioapics(func->handle);
pci_enable_bridges(bus);
pci_bus_add_devices(bus);
- acpiphp_set_hpp_values(slot->bridge->handle, bus);
- acpiphp_configure_ioapics(slot->bridge->handle);
/* associate pci_dev to our representation */
list_for_each (l, &slot->funcs) {
@@ -1103,6 +1173,16 @@ static int enable_device(struct acpiphp_slot *slot)
return retval;
}
+static void disable_bridges(struct pci_bus *bus)
+{
+ struct pci_dev *dev;
+ list_for_each_entry(dev, &bus->devices, bus_list) {
+ if (dev->subordinate) {
+ disable_bridges(dev->subordinate);
+ pci_disable_device(dev);
+ }
+ }
+}
/**
* disable_device - disable a slot
@@ -1127,6 +1207,19 @@ static int disable_device(struct acpiphp_slot *slot)
func->bridge = NULL;
}
+ if (func->pci_dev) {
+ pci_stop_bus_device(func->pci_dev);
+ if (func->pci_dev->subordinate) {
+ disable_bridges(func->pci_dev->subordinate);
+ pci_disable_device(func->pci_dev);
+ }
+ }
+ }
+
+ list_for_each (l, &slot->funcs) {
+ func = list_entry(l, struct acpiphp_func, sibling);
+
+ acpiphp_unconfigure_ioapics(func->handle);
acpiphp_bus_trim(func->handle);
/* try to remove anyway.
* acpiphp_bus_add might have been failed */
@@ -1600,14 +1693,10 @@ void __exit acpiphp_glue_exit(void)
*/
int __init acpiphp_get_num_slots(void)
{
- struct list_head *node;
struct acpiphp_bridge *bridge;
- int num_slots;
-
- num_slots = 0;
+ int num_slots = 0;
- list_for_each (node, &bridge_list) {
- bridge = (struct acpiphp_bridge *)node;
+ list_for_each_entry (bridge, &bridge_list, list) {
dbg("Bus %04x:%02x has %d slot%s\n",
pci_domain_nr(bridge->pci_bus),
bridge->pci_bus->number, bridge->nr_slots,
@@ -1714,8 +1803,8 @@ u8 acpiphp_get_power_status(struct acpiphp_slot *slot)
/*
- * latch closed: 1
- * latch open: 0
+ * latch open: 1
+ * latch closed: 0
*/
u8 acpiphp_get_latch_status(struct acpiphp_slot *slot)
{
@@ -1723,7 +1812,7 @@ u8 acpiphp_get_latch_status(struct acpiphp_slot *slot)
sta = get_slot_status(slot);
- return (sta & ACPI_STA_SHOW_IN_UI) ? 1 : 0;
+ return (sta & ACPI_STA_SHOW_IN_UI) ? 0 : 1;
}
diff --git a/drivers/pci/hotplug/acpiphp_ibm.c b/drivers/pci/hotplug/acpiphp_ibm.c
index d0a07d9ab30c..bd40aee10e16 100644
--- a/drivers/pci/hotplug/acpiphp_ibm.c
+++ b/drivers/pci/hotplug/acpiphp_ibm.c
@@ -35,7 +35,6 @@
#include <linux/moduleparam.h>
#include "acpiphp.h"
-#include "pci_hotplug.h"
#define DRIVER_VERSION "1.0.1"
#define DRIVER_AUTHOR "Irene Zubarev <zubarev@us.ibm.com>, Vernon Mauery <vernux@us.ibm.com>"
diff --git a/drivers/pci/hotplug/cpci_hotplug_core.c b/drivers/pci/hotplug/cpci_hotplug_core.c
index d5df5871cfa2..684551559d44 100644
--- a/drivers/pci/hotplug/cpci_hotplug_core.c
+++ b/drivers/pci/hotplug/cpci_hotplug_core.c
@@ -29,12 +29,12 @@
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/pci.h>
+#include <linux/pci_hotplug.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/smp_lock.h>
#include <asm/atomic.h>
#include <linux/delay.h>
-#include "pci_hotplug.h"
#include "cpci_hotplug.h"
#define DRIVER_AUTHOR "Scott Murray <scottm@somanetworks.com>"
@@ -342,7 +342,7 @@ cpci_hp_unregister_bus(struct pci_bus *bus)
/* This is the interrupt mode interrupt handler */
static irqreturn_t
-cpci_hp_intr(int irq, void *data, struct pt_regs *regs)
+cpci_hp_intr(int irq, void *data)
{
dbg("entered cpci_hp_intr");
diff --git a/drivers/pci/hotplug/cpci_hotplug_pci.c b/drivers/pci/hotplug/cpci_hotplug_pci.c
index 4afcaffd031c..7b1beaad2752 100644
--- a/drivers/pci/hotplug/cpci_hotplug_pci.c
+++ b/drivers/pci/hotplug/cpci_hotplug_pci.c
@@ -26,9 +26,9 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/pci.h>
+#include <linux/pci_hotplug.h>
#include <linux/proc_fs.h>
#include "../pci.h"
-#include "pci_hotplug.h"
#include "cpci_hotplug.h"
#define MY_NAME "cpci_hotplug"
diff --git a/drivers/pci/hotplug/cpcihp_generic.c b/drivers/pci/hotplug/cpcihp_generic.c
index e847f0d6c7fe..f3852a6b74ea 100644
--- a/drivers/pci/hotplug/cpcihp_generic.c
+++ b/drivers/pci/hotplug/cpcihp_generic.c
@@ -84,7 +84,7 @@ static int __init validate_parameters(void)
if(!bridge) {
info("not configured, disabling.");
- return 1;
+ return -EINVAL;
}
str = bridge;
if(!*str)
@@ -147,7 +147,7 @@ static int __init cpcihp_generic_init(void)
info(DRIVER_DESC " version: " DRIVER_VERSION);
status = validate_parameters();
- if(status != 0)
+ if (status)
return status;
r = request_region(port, 1, "#ENUM hotswap signal register");
diff --git a/drivers/pci/hotplug/cpqphp.h b/drivers/pci/hotplug/cpqphp.h
index c74e9e37e76b..298ad7f3f4f4 100644
--- a/drivers/pci/hotplug/cpqphp.h
+++ b/drivers/pci/hotplug/cpqphp.h
@@ -28,7 +28,6 @@
#ifndef _CPQPHP_H
#define _CPQPHP_H
-#include "pci_hotplug.h"
#include <linux/interrupt.h>
#include <asm/io.h> /* for read? and write? functions */
#include <linux/delay.h> /* for delays */
@@ -409,7 +408,7 @@ extern void cpqhp_remove_debugfs_files (struct controller *ctrl);
/* controller functions */
extern void cpqhp_pushbutton_thread (unsigned long event_pointer);
-extern irqreturn_t cpqhp_ctrl_intr (int IRQ, void *data, struct pt_regs *regs);
+extern irqreturn_t cpqhp_ctrl_intr (int IRQ, void *data);
extern int cpqhp_find_available_resources (struct controller *ctrl, void __iomem *rom_start);
extern int cpqhp_event_start_thread (void);
extern void cpqhp_event_stop_thread (void);
diff --git a/drivers/pci/hotplug/cpqphp_core.c b/drivers/pci/hotplug/cpqphp_core.c
index 1fc259913b68..5617cfdadc5c 100644
--- a/drivers/pci/hotplug/cpqphp_core.c
+++ b/drivers/pci/hotplug/cpqphp_core.c
@@ -37,6 +37,7 @@
#include <linux/slab.h>
#include <linux/workqueue.h>
#include <linux/pci.h>
+#include <linux/pci_hotplug.h>
#include <linux/init.h>
#include <linux/interrupt.h>
diff --git a/drivers/pci/hotplug/cpqphp_ctrl.c b/drivers/pci/hotplug/cpqphp_ctrl.c
index ae2dd36efef2..79ff6b4de3a6 100644
--- a/drivers/pci/hotplug/cpqphp_ctrl.c
+++ b/drivers/pci/hotplug/cpqphp_ctrl.c
@@ -36,6 +36,7 @@
#include <linux/wait.h>
#include <linux/smp_lock.h>
#include <linux/pci.h>
+#include <linux/pci_hotplug.h>
#include "cpqphp.h"
static u32 configure_new_device(struct controller* ctrl, struct pci_func *func,
@@ -889,7 +890,7 @@ int cpqhp_resource_sort_and_combine(struct pci_resource **head)
}
-irqreturn_t cpqhp_ctrl_intr(int IRQ, void *data, struct pt_regs *regs)
+irqreturn_t cpqhp_ctrl_intr(int IRQ, void *data)
{
struct controller *ctrl = data;
u8 schedule_flag = 0;
diff --git a/drivers/pci/hotplug/cpqphp_nvram.c b/drivers/pci/hotplug/cpqphp_nvram.c
index cf0878917537..298a6cfd8406 100644
--- a/drivers/pci/hotplug/cpqphp_nvram.c
+++ b/drivers/pci/hotplug/cpqphp_nvram.c
@@ -33,6 +33,7 @@
#include <linux/slab.h>
#include <linux/workqueue.h>
#include <linux/pci.h>
+#include <linux/pci_hotplug.h>
#include <linux/init.h>
#include <asm/uaccess.h>
#include "cpqphp.h"
diff --git a/drivers/pci/hotplug/cpqphp_pci.c b/drivers/pci/hotplug/cpqphp_pci.c
index 0d9688952f4a..fc7c74d72595 100644
--- a/drivers/pci/hotplug/cpqphp_pci.c
+++ b/drivers/pci/hotplug/cpqphp_pci.c
@@ -33,6 +33,7 @@
#include <linux/workqueue.h>
#include <linux/proc_fs.h>
#include <linux/pci.h>
+#include <linux/pci_hotplug.h>
#include "../pci.h"
#include "cpqphp.h"
#include "cpqphp_nvram.h"
diff --git a/drivers/pci/hotplug/cpqphp_sysfs.c b/drivers/pci/hotplug/cpqphp_sysfs.c
index 8b3da007e859..634f74d919d3 100644
--- a/drivers/pci/hotplug/cpqphp_sysfs.c
+++ b/drivers/pci/hotplug/cpqphp_sysfs.c
@@ -32,6 +32,7 @@
#include <linux/proc_fs.h>
#include <linux/workqueue.h>
#include <linux/pci.h>
+#include <linux/pci_hotplug.h>
#include <linux/debugfs.h>
#include "cpqphp.h"
@@ -140,7 +141,7 @@ struct ctrl_dbg {
static int open(struct inode *inode, struct file *file)
{
- struct controller *ctrl = inode->u.generic_ip;
+ struct controller *ctrl = inode->i_private;
struct ctrl_dbg *dbg;
int retval = -ENOMEM;
diff --git a/drivers/pci/hotplug/fakephp.c b/drivers/pci/hotplug/fakephp.c
index dd2b762777c4..e27907c91d92 100644
--- a/drivers/pci/hotplug/fakephp.c
+++ b/drivers/pci/hotplug/fakephp.c
@@ -35,10 +35,10 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
+#include <linux/pci_hotplug.h>
#include <linux/init.h>
#include <linux/string.h>
#include <linux/slab.h>
-#include "pci_hotplug.h"
#include "../pci.h"
#if !defined(MODULE)
@@ -176,17 +176,25 @@ static void pci_rescan_slot(struct pci_dev *temp)
struct pci_bus *bus = temp->bus;
struct pci_dev *dev;
int func;
+ int retval;
u8 hdr_type;
+
if (!pci_read_config_byte(temp, PCI_HEADER_TYPE, &hdr_type)) {
temp->hdr_type = hdr_type & 0x7f;
- if (!pci_find_slot(bus->number, temp->devfn)) {
+ if ((dev = pci_get_slot(bus, temp->devfn)) != NULL)
+ pci_dev_put(dev);
+ else {
dev = pci_scan_single_device(bus, temp->devfn);
if (dev) {
dbg("New device on %s function %x:%x\n",
bus->name, temp->devfn >> 3,
temp->devfn & 7);
- pci_bus_add_device(dev);
- add_slot(dev);
+ retval = pci_bus_add_device(dev);
+ if (retval)
+ dev_err(&dev->dev, "error adding "
+ "device, continuing.\n");
+ else
+ add_slot(dev);
}
}
/* multifunction device? */
@@ -199,14 +207,20 @@ static void pci_rescan_slot(struct pci_dev *temp)
continue;
temp->hdr_type = hdr_type & 0x7f;
- if (!pci_find_slot(bus->number, temp->devfn)) {
+ if ((dev = pci_get_slot(bus, temp->devfn)) != NULL)
+ pci_dev_put(dev);
+ else {
dev = pci_scan_single_device(bus, temp->devfn);
if (dev) {
dbg("New device on %s function %x:%x\n",
bus->name, temp->devfn >> 3,
temp->devfn & 7);
- pci_bus_add_device(dev);
- add_slot(dev);
+ retval = pci_bus_add_device(dev);
+ if (retval)
+ dev_err(&dev->dev, "error adding "
+ "device, continuing.\n");
+ else
+ add_slot(dev);
}
}
}
@@ -295,7 +309,7 @@ static int disable_slot(struct hotplug_slot *slot)
/* search for subfunctions and disable them first */
if (!(dslot->dev->devfn & 7)) {
for (func = 1; func < 8; func++) {
- dev = pci_find_slot(dslot->dev->bus->number,
+ dev = pci_get_slot(dslot->dev->bus,
dslot->dev->devfn + func);
if (dev) {
hslot = get_slot_from_dev(dev);
@@ -305,6 +319,7 @@ static int disable_slot(struct hotplug_slot *slot)
err("Hotplug slot not found for subfunction of PCI device\n");
return -ENODEV;
}
+ pci_dev_put(dev);
} else
dbg("No device in slot found\n");
}
diff --git a/drivers/pci/hotplug/ibmphp.h b/drivers/pci/hotplug/ibmphp.h
index dba6d8ca9bda..612d96301509 100644
--- a/drivers/pci/hotplug/ibmphp.h
+++ b/drivers/pci/hotplug/ibmphp.h
@@ -30,7 +30,7 @@
*
*/
-#include "pci_hotplug.h"
+#include <linux/pci_hotplug.h>
extern int ibmphp_debug;
diff --git a/drivers/pci/hotplug/ibmphp_hpc.c b/drivers/pci/hotplug/ibmphp_hpc.c
index c3ac98a0a6a6..f55ac3885cb3 100644
--- a/drivers/pci/hotplug/ibmphp_hpc.c
+++ b/drivers/pci/hotplug/ibmphp_hpc.c
@@ -531,7 +531,7 @@ static u8 hpc_readcmdtoindex (u8 cmd, u8 index)
*
* Action: issue a READ command to HPC
*
-* Input: pslot - can not be NULL for READ_ALLSTAT
+* Input: pslot - cannot be NULL for READ_ALLSTAT
* pstatus - can be NULL for READ_ALLSTAT
*
* Return 0 or error codes
diff --git a/drivers/pci/hotplug/ibmphp_pci.c b/drivers/pci/hotplug/ibmphp_pci.c
index d87a9e3eaeeb..d8f05d7a3c72 100644
--- a/drivers/pci/hotplug/ibmphp_pci.c
+++ b/drivers/pci/hotplug/ibmphp_pci.c
@@ -1371,12 +1371,12 @@ static int unconfigure_boot_bridge (u8 busno, u8 device, u8 function)
}
bus = ibmphp_find_res_bus (sec_number);
- debug ("bus->busno is %x\n", bus->busno);
- debug ("sec_number is %x\n", sec_number);
if (!bus) {
err ("cannot find Bus structure for the bridged device\n");
return -EINVAL;
}
+ debug("bus->busno is %x\n", bus->busno);
+ debug("sec_number is %x\n", sec_number);
ibmphp_remove_bus (bus, busno);
diff --git a/drivers/pci/hotplug/pci_hotplug.h b/drivers/pci/hotplug/pci_hotplug.h
deleted file mode 100644
index e929b7c11429..000000000000
--- a/drivers/pci/hotplug/pci_hotplug.h
+++ /dev/null
@@ -1,236 +0,0 @@
-/*
- * PCI HotPlug Core Functions
- *
- * Copyright (C) 1995,2001 Compaq Computer Corporation
- * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
- * Copyright (C) 2001 IBM Corp.
- *
- * All rights reserved.
- *
- * 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, GOOD TITLE or
- * NON INFRINGEMENT. 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., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * Send feedback to <greg@kroah.com>
- *
- */
-#ifndef _PCI_HOTPLUG_H
-#define _PCI_HOTPLUG_H
-
-
-/* These values come from the PCI Hotplug Spec */
-enum pci_bus_speed {
- PCI_SPEED_33MHz = 0x00,
- PCI_SPEED_66MHz = 0x01,
- PCI_SPEED_66MHz_PCIX = 0x02,
- PCI_SPEED_100MHz_PCIX = 0x03,
- PCI_SPEED_133MHz_PCIX = 0x04,
- PCI_SPEED_66MHz_PCIX_ECC = 0x05,
- PCI_SPEED_100MHz_PCIX_ECC = 0x06,
- PCI_SPEED_133MHz_PCIX_ECC = 0x07,
- PCI_SPEED_66MHz_PCIX_266 = 0x09,
- PCI_SPEED_100MHz_PCIX_266 = 0x0a,
- PCI_SPEED_133MHz_PCIX_266 = 0x0b,
- PCI_SPEED_66MHz_PCIX_533 = 0x11,
- PCI_SPEED_100MHz_PCIX_533 = 0x12,
- PCI_SPEED_133MHz_PCIX_533 = 0x13,
- PCI_SPEED_UNKNOWN = 0xff,
-};
-
-/* These values come from the PCI Express Spec */
-enum pcie_link_width {
- PCIE_LNK_WIDTH_RESRV = 0x00,
- PCIE_LNK_X1 = 0x01,
- PCIE_LNK_X2 = 0x02,
- PCIE_LNK_X4 = 0x04,
- PCIE_LNK_X8 = 0x08,
- PCIE_LNK_X12 = 0x0C,
- PCIE_LNK_X16 = 0x10,
- PCIE_LNK_X32 = 0x20,
- PCIE_LNK_WIDTH_UNKNOWN = 0xFF,
-};
-
-enum pcie_link_speed {
- PCIE_2PT5GB = 0x14,
- PCIE_LNK_SPEED_UNKNOWN = 0xFF,
-};
-
-struct hotplug_slot;
-struct hotplug_slot_attribute {
- struct attribute attr;
- ssize_t (*show)(struct hotplug_slot *, char *);
- ssize_t (*store)(struct hotplug_slot *, const char *, size_t);
-};
-#define to_hotplug_attr(n) container_of(n, struct hotplug_slot_attribute, attr);
-
-/**
- * struct hotplug_slot_ops -the callbacks that the hotplug pci core can use
- * @owner: The module owner of this structure
- * @enable_slot: Called when the user wants to enable a specific pci slot
- * @disable_slot: Called when the user wants to disable a specific pci slot
- * @set_attention_status: Called to set the specific slot's attention LED to
- * the specified value
- * @hardware_test: Called to run a specified hardware test on the specified
- * slot.
- * @get_power_status: Called to get the current power status of a slot.
- * If this field is NULL, the value passed in the struct hotplug_slot_info
- * will be used when this value is requested by a user.
- * @get_attention_status: Called to get the current attention status of a slot.
- * If this field is NULL, the value passed in the struct hotplug_slot_info
- * will be used when this value is requested by a user.
- * @get_latch_status: Called to get the current latch status of a slot.
- * If this field is NULL, the value passed in the struct hotplug_slot_info
- * will be used when this value is requested by a user.
- * @get_adapter_status: Called to get see if an adapter is present in the slot or not.
- * If this field is NULL, the value passed in the struct hotplug_slot_info
- * will be used when this value is requested by a user.
- * @get_address: Called to get pci address of a slot.
- * If this field is NULL, the value passed in the struct hotplug_slot_info
- * will be used when this value is requested by a user.
- * @get_max_bus_speed: Called to get the max bus speed for a slot.
- * If this field is NULL, the value passed in the struct hotplug_slot_info
- * will be used when this value is requested by a user.
- * @get_cur_bus_speed: Called to get the current bus speed for a slot.
- * If this field is NULL, the value passed in the struct hotplug_slot_info
- * will be used when this value is requested by a user.
- *
- * The table of function pointers that is passed to the hotplug pci core by a
- * hotplug pci driver. These functions are called by the hotplug pci core when
- * the user wants to do something to a specific slot (query it for information,
- * set an LED, enable / disable power, etc.)
- */
-struct hotplug_slot_ops {
- struct module *owner;
- int (*enable_slot) (struct hotplug_slot *slot);
- int (*disable_slot) (struct hotplug_slot *slot);
- int (*set_attention_status) (struct hotplug_slot *slot, u8 value);
- int (*hardware_test) (struct hotplug_slot *slot, u32 value);
- int (*get_power_status) (struct hotplug_slot *slot, u8 *value);
- int (*get_attention_status) (struct hotplug_slot *slot, u8 *value);
- int (*get_latch_status) (struct hotplug_slot *slot, u8 *value);
- int (*get_adapter_status) (struct hotplug_slot *slot, u8 *value);
- int (*get_address) (struct hotplug_slot *slot, u32 *value);
- int (*get_max_bus_speed) (struct hotplug_slot *slot, enum pci_bus_speed *value);
- int (*get_cur_bus_speed) (struct hotplug_slot *slot, enum pci_bus_speed *value);
-};
-
-/**
- * struct hotplug_slot_info - used to notify the hotplug pci core of the state of the slot
- * @power: if power is enabled or not (1/0)
- * @attention_status: if the attention light is enabled or not (1/0)
- * @latch_status: if the latch (if any) is open or closed (1/0)
- * @adapter_present: if there is a pci board present in the slot or not (1/0)
- * @address: (domain << 16 | bus << 8 | dev)
- *
- * Used to notify the hotplug pci core of the status of a specific slot.
- */
-struct hotplug_slot_info {
- u8 power_status;
- u8 attention_status;
- u8 latch_status;
- u8 adapter_status;
- u32 address;
- enum pci_bus_speed max_bus_speed;
- enum pci_bus_speed cur_bus_speed;
-};
-
-/**
- * struct hotplug_slot - used to register a physical slot with the hotplug pci core
- * @name: the name of the slot being registered. This string must
- * be unique amoung slots registered on this system.
- * @ops: pointer to the &struct hotplug_slot_ops to be used for this slot
- * @info: pointer to the &struct hotplug_slot_info for the initial values for
- * this slot.
- * @release: called during pci_hp_deregister to free memory allocated in a
- * hotplug_slot structure.
- * @private: used by the hotplug pci controller driver to store whatever it
- * needs.
- */
-struct hotplug_slot {
- char *name;
- struct hotplug_slot_ops *ops;
- struct hotplug_slot_info *info;
- void (*release) (struct hotplug_slot *slot);
- void *private;
-
- /* Variables below this are for use only by the hotplug pci core. */
- struct list_head slot_list;
- struct kobject kobj;
-};
-#define to_hotplug_slot(n) container_of(n, struct hotplug_slot, kobj)
-
-extern int pci_hp_register (struct hotplug_slot *slot);
-extern int pci_hp_deregister (struct hotplug_slot *slot);
-extern int pci_hp_change_slot_info (struct hotplug_slot *slot,
- struct hotplug_slot_info *info);
-extern struct subsystem pci_hotplug_slots_subsys;
-
-/* PCI Setting Record (Type 0) */
-struct hpp_type0 {
- u32 revision;
- u8 cache_line_size;
- u8 latency_timer;
- u8 enable_serr;
- u8 enable_perr;
-};
-
-/* PCI-X Setting Record (Type 1) */
-struct hpp_type1 {
- u32 revision;
- u8 max_mem_read;
- u8 avg_max_split;
- u16 tot_max_split;
-};
-
-/* PCI Express Setting Record (Type 2) */
-struct hpp_type2 {
- u32 revision;
- u32 unc_err_mask_and;
- u32 unc_err_mask_or;
- u32 unc_err_sever_and;
- u32 unc_err_sever_or;
- u32 cor_err_mask_and;
- u32 cor_err_mask_or;
- u32 adv_err_cap_and;
- u32 adv_err_cap_or;
- u16 pci_exp_devctl_and;
- u16 pci_exp_devctl_or;
- u16 pci_exp_lnkctl_and;
- u16 pci_exp_lnkctl_or;
- u32 sec_unc_err_sever_and;
- u32 sec_unc_err_sever_or;
- u32 sec_unc_err_mask_and;
- u32 sec_unc_err_mask_or;
-};
-
-struct hotplug_params {
- struct hpp_type0 *t0; /* Type0: NULL if not available */
- struct hpp_type1 *t1; /* Type1: NULL if not available */
- struct hpp_type2 *t2; /* Type2: NULL if not available */
- struct hpp_type0 type0_data;
- struct hpp_type1 type1_data;
- struct hpp_type2 type2_data;
-};
-
-#ifdef CONFIG_ACPI
-#include <acpi/acpi.h>
-#include <acpi/acpi_bus.h>
-#include <acpi/actypes.h>
-extern acpi_status acpi_run_oshp(acpi_handle handle);
-extern acpi_status acpi_get_hp_params_from_firmware(struct pci_bus *bus,
- struct hotplug_params *hpp);
-int acpi_root_bridge(acpi_handle handle);
-#endif
-#endif
-
diff --git a/drivers/pci/hotplug/pci_hotplug_core.c b/drivers/pci/hotplug/pci_hotplug_core.c
index b7b378df89e3..f5d632e72323 100644
--- a/drivers/pci/hotplug/pci_hotplug_core.c
+++ b/drivers/pci/hotplug/pci_hotplug_core.c
@@ -21,9 +21,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
- * Send feedback to <greg@kroah.com>
- *
- * Filesystem portion based on work done by Pat Mochel on ddfs/driverfs
+ * Send feedback to <kristen.c.accardi@intel.com>
*
*/
@@ -32,6 +30,8 @@
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/list.h>
+#include <linux/kobject.h>
+#include <linux/sysfs.h>
#include <linux/pagemap.h>
#include <linux/slab.h>
#include <linux/smp_lock.h>
@@ -39,11 +39,8 @@
#include <linux/mount.h>
#include <linux/namei.h>
#include <linux/pci.h>
+#include <linux/pci_hotplug.h>
#include <asm/uaccess.h>
-#include <linux/kobject.h>
-#include <linux/sysfs.h>
-#include "pci_hotplug.h"
-
#define MY_NAME "pci_hotplug"
@@ -482,31 +479,95 @@ static int has_test_file (struct hotplug_slot *slot)
static int fs_add_slot (struct hotplug_slot *slot)
{
- if (has_power_file(slot) == 0)
- sysfs_create_file(&slot->kobj, &hotplug_slot_attr_power.attr);
+ int retval = 0;
- if (has_attention_file(slot) == 0)
- sysfs_create_file(&slot->kobj, &hotplug_slot_attr_attention.attr);
+ if (has_power_file(slot) == 0) {
+ retval = sysfs_create_file(&slot->kobj, &hotplug_slot_attr_power.attr);
+ if (retval)
+ goto exit_power;
+ }
- if (has_latch_file(slot) == 0)
- sysfs_create_file(&slot->kobj, &hotplug_slot_attr_latch.attr);
+ if (has_attention_file(slot) == 0) {
+ retval = sysfs_create_file(&slot->kobj,
+ &hotplug_slot_attr_attention.attr);
+ if (retval)
+ goto exit_attention;
+ }
- if (has_adapter_file(slot) == 0)
- sysfs_create_file(&slot->kobj, &hotplug_slot_attr_presence.attr);
+ if (has_latch_file(slot) == 0) {
+ retval = sysfs_create_file(&slot->kobj,
+ &hotplug_slot_attr_latch.attr);
+ if (retval)
+ goto exit_latch;
+ }
- if (has_address_file(slot) == 0)
- sysfs_create_file(&slot->kobj, &hotplug_slot_attr_address.attr);
+ if (has_adapter_file(slot) == 0) {
+ retval = sysfs_create_file(&slot->kobj,
+ &hotplug_slot_attr_presence.attr);
+ if (retval)
+ goto exit_adapter;
+ }
- if (has_max_bus_speed_file(slot) == 0)
- sysfs_create_file(&slot->kobj, &hotplug_slot_attr_max_bus_speed.attr);
+ if (has_address_file(slot) == 0) {
+ retval = sysfs_create_file(&slot->kobj,
+ &hotplug_slot_attr_address.attr);
+ if (retval)
+ goto exit_address;
+ }
+
+ if (has_max_bus_speed_file(slot) == 0) {
+ retval = sysfs_create_file(&slot->kobj,
+ &hotplug_slot_attr_max_bus_speed.attr);
+ if (retval)
+ goto exit_max_speed;
+ }
+
+ if (has_cur_bus_speed_file(slot) == 0) {
+ retval = sysfs_create_file(&slot->kobj,
+ &hotplug_slot_attr_cur_bus_speed.attr);
+ if (retval)
+ goto exit_cur_speed;
+ }
+
+ if (has_test_file(slot) == 0) {
+ retval = sysfs_create_file(&slot->kobj,
+ &hotplug_slot_attr_test.attr);
+ if (retval)
+ goto exit_test;
+ }
+ goto exit;
+
+exit_test:
if (has_cur_bus_speed_file(slot) == 0)
- sysfs_create_file(&slot->kobj, &hotplug_slot_attr_cur_bus_speed.attr);
+ sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_cur_bus_speed.attr);
- if (has_test_file(slot) == 0)
- sysfs_create_file(&slot->kobj, &hotplug_slot_attr_test.attr);
+exit_cur_speed:
+ if (has_max_bus_speed_file(slot) == 0)
+ sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_max_bus_speed.attr);
- return 0;
+exit_max_speed:
+ if (has_address_file(slot) == 0)
+ sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_address.attr);
+
+exit_address:
+ if (has_adapter_file(slot) == 0)
+ sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_presence.attr);
+
+exit_adapter:
+ if (has_latch_file(slot) == 0)
+ sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_latch.attr);
+
+exit_latch:
+ if (has_attention_file(slot) == 0)
+ sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_attention.attr);
+
+exit_attention:
+ if (has_power_file(slot) == 0)
+ sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_power.attr);
+exit_power:
+exit:
+ return retval;
}
static void fs_remove_slot (struct hotplug_slot *slot)
@@ -626,8 +687,11 @@ int pci_hp_deregister (struct hotplug_slot *slot)
*
* Returns 0 if successful, anything else for an error.
*/
-int pci_hp_change_slot_info (struct hotplug_slot *slot, struct hotplug_slot_info *info)
+int __must_check pci_hp_change_slot_info(struct hotplug_slot *slot,
+ struct hotplug_slot_info *info)
{
+ int retval;
+
if ((slot == NULL) || (info == NULL))
return -ENODEV;
@@ -636,32 +700,60 @@ int pci_hp_change_slot_info (struct hotplug_slot *slot, struct hotplug_slot_info
* for the files referring to the fields that have now changed.
*/
if ((has_power_file(slot) == 0) &&
- (slot->info->power_status != info->power_status))
- sysfs_update_file(&slot->kobj, &hotplug_slot_attr_power.attr);
+ (slot->info->power_status != info->power_status)) {
+ retval = sysfs_update_file(&slot->kobj,
+ &hotplug_slot_attr_power.attr);
+ if (retval)
+ return retval;
+ }
if ((has_attention_file(slot) == 0) &&
- (slot->info->attention_status != info->attention_status))
- sysfs_update_file(&slot->kobj, &hotplug_slot_attr_attention.attr);
+ (slot->info->attention_status != info->attention_status)) {
+ retval = sysfs_update_file(&slot->kobj,
+ &hotplug_slot_attr_attention.attr);
+ if (retval)
+ return retval;
+ }
if ((has_latch_file(slot) == 0) &&
- (slot->info->latch_status != info->latch_status))
- sysfs_update_file(&slot->kobj, &hotplug_slot_attr_latch.attr);
+ (slot->info->latch_status != info->latch_status)) {
+ retval = sysfs_update_file(&slot->kobj,
+ &hotplug_slot_attr_latch.attr);
+ if (retval)
+ return retval;
+ }
if ((has_adapter_file(slot) == 0) &&
- (slot->info->adapter_status != info->adapter_status))
- sysfs_update_file(&slot->kobj, &hotplug_slot_attr_presence.attr);
+ (slot->info->adapter_status != info->adapter_status)) {
+ retval = sysfs_update_file(&slot->kobj,
+ &hotplug_slot_attr_presence.attr);
+ if (retval)
+ return retval;
+ }
if ((has_address_file(slot) == 0) &&
- (slot->info->address != info->address))
- sysfs_update_file(&slot->kobj, &hotplug_slot_attr_address.attr);
+ (slot->info->address != info->address)) {
+ retval = sysfs_update_file(&slot->kobj,
+ &hotplug_slot_attr_address.attr);
+ if (retval)
+ return retval;
+ }
if ((has_max_bus_speed_file(slot) == 0) &&
- (slot->info->max_bus_speed != info->max_bus_speed))
- sysfs_update_file(&slot->kobj, &hotplug_slot_attr_max_bus_speed.attr);
+ (slot->info->max_bus_speed != info->max_bus_speed)) {
+ retval = sysfs_update_file(&slot->kobj,
+ &hotplug_slot_attr_max_bus_speed.attr);
+ if (retval)
+ return retval;
+ }
if ((has_cur_bus_speed_file(slot) == 0) &&
- (slot->info->cur_bus_speed != info->cur_bus_speed))
- sysfs_update_file(&slot->kobj, &hotplug_slot_attr_cur_bus_speed.attr);
+ (slot->info->cur_bus_speed != info->cur_bus_speed)) {
+ retval = sysfs_update_file(&slot->kobj,
+ &hotplug_slot_attr_cur_bus_speed.attr);
+ if (retval)
+ return retval;
+ }
memcpy (slot->info, info, sizeof (struct hotplug_slot_info));
diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h
index eaea9d36a1bb..4fb12fcda563 100644
--- a/drivers/pci/hotplug/pciehp.h
+++ b/drivers/pci/hotplug/pciehp.h
@@ -31,11 +31,11 @@
#include <linux/types.h>
#include <linux/pci.h>
+#include <linux/pci_hotplug.h>
#include <linux/delay.h>
#include <linux/sched.h> /* signal_pending() */
#include <linux/pcieport_if.h>
#include <linux/mutex.h>
-#include "pci_hotplug.h"
#define MY_NAME "pciehp"
@@ -92,6 +92,7 @@ struct php_ctlr_state_s {
struct controller {
struct controller *next;
struct mutex crit_sect; /* critical section mutex */
+ struct mutex ctrl_lock; /* controller lock */
struct php_ctlr_state_s *hpc_ctlr_handle; /* HPC controller handle */
int num_slots; /* Number of slots on ctlr */
int slot_num_inc; /* 1 or -1 */
@@ -166,10 +167,10 @@ struct controller {
* error Messages
*/
#define msg_initialization_err "Initialization failure, error=%d\n"
-#define msg_button_on "PCI slot #%d - powering on due to button press.\n"
-#define msg_button_off "PCI slot #%d - powering off due to button press.\n"
-#define msg_button_cancel "PCI slot #%d - action canceled due to button press.\n"
-#define msg_button_ignore "PCI slot #%d - button press ignored. (action in progress...)\n"
+#define msg_button_on "PCI slot #%s - powering on due to button press.\n"
+#define msg_button_off "PCI slot #%s - powering off due to button press.\n"
+#define msg_button_cancel "PCI slot #%s - action canceled due to button press.\n"
+#define msg_button_ignore "PCI slot #%s - button press ignored. (action in progress...)\n"
/* controller functions */
extern int pciehp_event_start_thread (void);
diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c
index c67b7c3f1ddf..f13f31323e85 100644
--- a/drivers/pci/hotplug/pciehp_core.c
+++ b/drivers/pci/hotplug/pciehp_core.c
@@ -448,7 +448,7 @@ static int pciehp_probe(struct pcie_device *dev, const struct pcie_port_service_
}
/* Wait for exclusive access to hardware */
- mutex_lock(&ctrl->crit_sect);
+ mutex_lock(&ctrl->ctrl_lock);
t_slot->hpc_ops->get_adapter_status(t_slot, &value); /* Check if slot is occupied */
@@ -456,7 +456,7 @@ static int pciehp_probe(struct pcie_device *dev, const struct pcie_port_service_
rc = t_slot->hpc_ops->power_off_slot(t_slot); /* Power off slot if not occupied*/
if (rc) {
/* Done with exclusive hardware access */
- mutex_unlock(&ctrl->crit_sect);
+ mutex_unlock(&ctrl->ctrl_lock);
goto err_out_free_ctrl_slot;
} else
/* Wait for the command to complete */
@@ -464,7 +464,7 @@ static int pciehp_probe(struct pcie_device *dev, const struct pcie_port_service_
}
/* Done with exclusive hardware access */
- mutex_unlock(&ctrl->crit_sect);
+ mutex_unlock(&ctrl->ctrl_lock);
return 0;
@@ -521,14 +521,9 @@ static void __exit unload_pciehpd(void)
}
-static int hpdriver_context = 0;
-
static void pciehp_remove (struct pcie_device *device)
{
- printk("%s ENTRY\n", __FUNCTION__);
- printk("%s -> Call free_irq for irq = %d\n",
- __FUNCTION__, device->irq);
- free_irq(device->irq, &hpdriver_context);
+ /* XXX - Needs to be adapted to device driver model */
}
#ifdef CONFIG_PM
diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c
index 33d198768356..372c63e35aa9 100644
--- a/drivers/pci/hotplug/pciehp_ctrl.c
+++ b/drivers/pci/hotplug/pciehp_ctrl.c
@@ -43,6 +43,11 @@ static int event_finished;
static unsigned long pushbutton_pending; /* = 0 */
static unsigned long surprise_rm_pending; /* = 0 */
+static inline char *slot_name(struct slot *p_slot)
+{
+ return p_slot->hotplug_slot->name;
+}
+
u8 pciehp_handle_attention_button(u8 hp_slot, void *inst_id)
{
struct controller *ctrl = (struct controller *) inst_id;
@@ -68,7 +73,7 @@ u8 pciehp_handle_attention_button(u8 hp_slot, void *inst_id)
/*
* Button pressed - See if need to TAKE ACTION!!!
*/
- info("Button pressed on Slot(%d)\n", ctrl->first_slot + hp_slot);
+ info("Button pressed on Slot(%s)\n", slot_name(p_slot));
taskInfo->event_type = INT_BUTTON_PRESS;
if ((p_slot->state == BLINKINGON_STATE)
@@ -78,7 +83,7 @@ u8 pciehp_handle_attention_button(u8 hp_slot, void *inst_id)
* or hot-remove
*/
taskInfo->event_type = INT_BUTTON_CANCEL;
- info("Button cancel on Slot(%d)\n", ctrl->first_slot + hp_slot);
+ info("Button cancel on Slot(%s)\n", slot_name(p_slot));
} else if ((p_slot->state == POWERON_STATE)
|| (p_slot->state == POWEROFF_STATE)) {
/* Ignore if the slot is on power-on or power-off state; this
@@ -86,7 +91,7 @@ u8 pciehp_handle_attention_button(u8 hp_slot, void *inst_id)
* hot-remove is undergoing
*/
taskInfo->event_type = INT_BUTTON_IGNORE;
- info("Button ignore on Slot(%d)\n", ctrl->first_slot + hp_slot);
+ info("Button ignore on Slot(%s)\n", slot_name(p_slot));
}
if (rc)
@@ -122,13 +127,13 @@ u8 pciehp_handle_switch_change(u8 hp_slot, void *inst_id)
/*
* Switch opened
*/
- info("Latch open on Slot(%d)\n", ctrl->first_slot + hp_slot);
+ info("Latch open on Slot(%s)\n", slot_name(p_slot));
taskInfo->event_type = INT_SWITCH_OPEN;
} else {
/*
* Switch closed
*/
- info("Latch close on Slot(%d)\n", ctrl->first_slot + hp_slot);
+ info("Latch close on Slot(%s)\n", slot_name(p_slot));
taskInfo->event_type = INT_SWITCH_CLOSE;
}
@@ -166,13 +171,13 @@ u8 pciehp_handle_presence_change(u8 hp_slot, void *inst_id)
/*
* Card Present
*/
- info("Card present on Slot(%d)\n", ctrl->first_slot + hp_slot);
+ info("Card present on Slot(%s)\n", slot_name(p_slot));
taskInfo->event_type = INT_PRESENCE_ON;
} else {
/*
* Not Present
*/
- info("Card not present on Slot(%d)\n", ctrl->first_slot + hp_slot);
+ info("Card not present on Slot(%s)\n", slot_name(p_slot));
taskInfo->event_type = INT_PRESENCE_OFF;
}
@@ -206,13 +211,13 @@ u8 pciehp_handle_power_fault(u8 hp_slot, void *inst_id)
/*
* power fault Cleared
*/
- info("Power fault cleared on Slot(%d)\n", ctrl->first_slot + hp_slot);
+ info("Power fault cleared on Slot(%s)\n", slot_name(p_slot));
taskInfo->event_type = INT_POWER_FAULT_CLEAR;
} else {
/*
* power fault
*/
- info("Power fault on Slot(%d)\n", ctrl->first_slot + hp_slot);
+ info("Power fault on Slot(%s)\n", slot_name(p_slot));
taskInfo->event_type = INT_POWER_FAULT;
info("power fault bit %x set\n", hp_slot);
}
@@ -229,13 +234,13 @@ u8 pciehp_handle_power_fault(u8 hp_slot, void *inst_id)
static void set_slot_off(struct controller *ctrl, struct slot * pslot)
{
/* Wait for exclusive access to hardware */
- mutex_lock(&ctrl->crit_sect);
+ mutex_lock(&ctrl->ctrl_lock);
/* turn off slot, turn on Amber LED, turn off Green LED if supported*/
if (POWER_CTRL(ctrl->ctrlcap)) {
if (pslot->hpc_ops->power_off_slot(pslot)) {
err("%s: Issue of Slot Power Off command failed\n", __FUNCTION__);
- mutex_unlock(&ctrl->crit_sect);
+ mutex_unlock(&ctrl->ctrl_lock);
return;
}
wait_for_ctrl_irq (ctrl);
@@ -249,14 +254,14 @@ static void set_slot_off(struct controller *ctrl, struct slot * pslot)
if (ATTN_LED(ctrl->ctrlcap)) {
if (pslot->hpc_ops->set_attention_status(pslot, 1)) {
err("%s: Issue of Set Attention Led command failed\n", __FUNCTION__);
- mutex_unlock(&ctrl->crit_sect);
+ mutex_unlock(&ctrl->ctrl_lock);
return;
}
wait_for_ctrl_irq (ctrl);
}
/* Done with exclusive hardware access */
- mutex_unlock(&ctrl->crit_sect);
+ mutex_unlock(&ctrl->ctrl_lock);
}
/**
@@ -279,13 +284,13 @@ static int board_added(struct slot *p_slot)
ctrl->slot_device_offset, hp_slot);
/* Wait for exclusive access to hardware */
- mutex_lock(&ctrl->crit_sect);
+ mutex_lock(&ctrl->ctrl_lock);
if (POWER_CTRL(ctrl->ctrlcap)) {
/* Power on slot */
rc = p_slot->hpc_ops->power_on_slot(p_slot);
if (rc) {
- mutex_unlock(&ctrl->crit_sect);
+ mutex_unlock(&ctrl->ctrl_lock);
return -1;
}
@@ -301,7 +306,7 @@ static int board_added(struct slot *p_slot)
}
/* Done with exclusive hardware access */
- mutex_unlock(&ctrl->crit_sect);
+ mutex_unlock(&ctrl->ctrl_lock);
/* Wait for ~1 second */
wait_for_ctrl_irq (ctrl);
@@ -335,7 +340,7 @@ static int board_added(struct slot *p_slot)
pci_fixup_device(pci_fixup_final, ctrl->pci_dev);
if (PWR_LED(ctrl->ctrlcap)) {
/* Wait for exclusive access to hardware */
- mutex_lock(&ctrl->crit_sect);
+ mutex_lock(&ctrl->ctrl_lock);
p_slot->hpc_ops->green_led_on(p_slot);
@@ -343,7 +348,7 @@ static int board_added(struct slot *p_slot)
wait_for_ctrl_irq (ctrl);
/* Done with exclusive hardware access */
- mutex_unlock(&ctrl->crit_sect);
+ mutex_unlock(&ctrl->ctrl_lock);
}
return 0;
@@ -375,14 +380,14 @@ static int remove_board(struct slot *p_slot)
dbg("In %s, hp_slot = %d\n", __FUNCTION__, hp_slot);
/* Wait for exclusive access to hardware */
- mutex_lock(&ctrl->crit_sect);
+ mutex_lock(&ctrl->ctrl_lock);
if (POWER_CTRL(ctrl->ctrlcap)) {
/* power off slot */
rc = p_slot->hpc_ops->power_off_slot(p_slot);
if (rc) {
err("%s: Issue of Slot Disable command failed\n", __FUNCTION__);
- mutex_unlock(&ctrl->crit_sect);
+ mutex_unlock(&ctrl->ctrl_lock);
return rc;
}
/* Wait for the command to complete */
@@ -398,7 +403,7 @@ static int remove_board(struct slot *p_slot)
}
/* Done with exclusive hardware access */
- mutex_unlock(&ctrl->crit_sect);
+ mutex_unlock(&ctrl->ctrl_lock);
return 0;
}
@@ -445,7 +450,7 @@ static void pciehp_pushbutton_thread(unsigned long slot)
if (pciehp_enable_slot(p_slot) && PWR_LED(p_slot->ctrl->ctrlcap)) {
/* Wait for exclusive access to hardware */
- mutex_lock(&p_slot->ctrl->crit_sect);
+ mutex_lock(&p_slot->ctrl->ctrl_lock);
p_slot->hpc_ops->green_led_off(p_slot);
@@ -453,7 +458,7 @@ static void pciehp_pushbutton_thread(unsigned long slot)
wait_for_ctrl_irq (p_slot->ctrl);
/* Done with exclusive hardware access */
- mutex_unlock(&p_slot->ctrl->crit_sect);
+ mutex_unlock(&p_slot->ctrl->ctrl_lock);
}
p_slot->state = STATIC_STATE;
}
@@ -495,7 +500,7 @@ static void pciehp_surprise_rm_thread(unsigned long slot)
if (pciehp_enable_slot(p_slot) && PWR_LED(p_slot->ctrl->ctrlcap)) {
/* Wait for exclusive access to hardware */
- mutex_lock(&p_slot->ctrl->crit_sect);
+ mutex_lock(&p_slot->ctrl->ctrl_lock);
p_slot->hpc_ops->green_led_off(p_slot);
@@ -503,7 +508,7 @@ static void pciehp_surprise_rm_thread(unsigned long slot)
wait_for_ctrl_irq (p_slot->ctrl);
/* Done with exclusive hardware access */
- mutex_unlock(&p_slot->ctrl->crit_sect);
+ mutex_unlock(&p_slot->ctrl->ctrl_lock);
}
p_slot->state = STATIC_STATE;
}
@@ -616,7 +621,7 @@ static void interrupt_event_handler(struct controller *ctrl)
switch (p_slot->state) {
case BLINKINGOFF_STATE:
/* Wait for exclusive access to hardware */
- mutex_lock(&ctrl->crit_sect);
+ mutex_lock(&ctrl->ctrl_lock);
if (PWR_LED(ctrl->ctrlcap)) {
p_slot->hpc_ops->green_led_on(p_slot);
@@ -630,11 +635,11 @@ static void interrupt_event_handler(struct controller *ctrl)
wait_for_ctrl_irq (ctrl);
}
/* Done with exclusive hardware access */
- mutex_unlock(&ctrl->crit_sect);
+ mutex_unlock(&ctrl->ctrl_lock);
break;
case BLINKINGON_STATE:
/* Wait for exclusive access to hardware */
- mutex_lock(&ctrl->crit_sect);
+ mutex_lock(&ctrl->ctrl_lock);
if (PWR_LED(ctrl->ctrlcap)) {
p_slot->hpc_ops->green_led_off(p_slot);
@@ -647,14 +652,14 @@ static void interrupt_event_handler(struct controller *ctrl)
wait_for_ctrl_irq (ctrl);
}
/* Done with exclusive hardware access */
- mutex_unlock(&ctrl->crit_sect);
+ mutex_unlock(&ctrl->ctrl_lock);
break;
default:
warn("Not a valid state\n");
return;
}
- info(msg_button_cancel, p_slot->number);
+ info(msg_button_cancel, slot_name(p_slot));
p_slot->state = STATIC_STATE;
}
/* ***********Button Pressed (No action on 1st press...) */
@@ -667,16 +672,16 @@ static void interrupt_event_handler(struct controller *ctrl)
/* slot is on */
dbg("slot is on\n");
p_slot->state = BLINKINGOFF_STATE;
- info(msg_button_off, p_slot->number);
+ info(msg_button_off, slot_name(p_slot));
} else {
/* slot is off */
dbg("slot is off\n");
p_slot->state = BLINKINGON_STATE;
- info(msg_button_on, p_slot->number);
+ info(msg_button_on, slot_name(p_slot));
}
/* Wait for exclusive access to hardware */
- mutex_lock(&ctrl->crit_sect);
+ mutex_lock(&ctrl->ctrl_lock);
/* blink green LED and turn off amber */
if (PWR_LED(ctrl->ctrlcap)) {
@@ -693,7 +698,7 @@ static void interrupt_event_handler(struct controller *ctrl)
}
/* Done with exclusive hardware access */
- mutex_unlock(&ctrl->crit_sect);
+ mutex_unlock(&ctrl->ctrl_lock);
init_timer(&p_slot->task_event);
p_slot->task_event.expires = jiffies + 5 * HZ; /* 5 second delay */
@@ -708,7 +713,7 @@ static void interrupt_event_handler(struct controller *ctrl)
if (POWER_CTRL(ctrl->ctrlcap)) {
dbg("power fault\n");
/* Wait for exclusive access to hardware */
- mutex_lock(&ctrl->crit_sect);
+ mutex_lock(&ctrl->ctrl_lock);
if (ATTN_LED(ctrl->ctrlcap)) {
p_slot->hpc_ops->set_attention_status(p_slot, 1);
@@ -721,7 +726,7 @@ static void interrupt_event_handler(struct controller *ctrl)
}
/* Done with exclusive hardware access */
- mutex_unlock(&ctrl->crit_sect);
+ mutex_unlock(&ctrl->ctrl_lock);
}
}
/***********SURPRISE REMOVAL********************/
@@ -760,28 +765,30 @@ int pciehp_enable_slot(struct slot *p_slot)
rc = p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus);
if (rc || !getstatus) {
- info("%s: no adapter on slot(%x)\n", __FUNCTION__, p_slot->number);
+ info("%s: no adapter on slot(%s)\n", __FUNCTION__,
+ slot_name(p_slot));
mutex_unlock(&p_slot->ctrl->crit_sect);
- return 1;
+ return -ENODEV;
}
if (MRL_SENS(p_slot->ctrl->ctrlcap)) {
rc = p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
if (rc || getstatus) {
- info("%s: latch open on slot(%x)\n", __FUNCTION__, p_slot->number);
+ info("%s: latch open on slot(%s)\n", __FUNCTION__,
+ slot_name(p_slot));
mutex_unlock(&p_slot->ctrl->crit_sect);
- return 1;
+ return -ENODEV;
}
}
if (POWER_CTRL(p_slot->ctrl->ctrlcap)) {
rc = p_slot->hpc_ops->get_power_status(p_slot, &getstatus);
if (rc || getstatus) {
- info("%s: already enabled on slot(%x)\n", __FUNCTION__, p_slot->number);
+ info("%s: already enabled on slot(%s)\n", __FUNCTION__,
+ slot_name(p_slot));
mutex_unlock(&p_slot->ctrl->crit_sect);
- return 1;
+ return -EINVAL;
}
}
- mutex_unlock(&p_slot->ctrl->crit_sect);
p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
@@ -790,9 +797,9 @@ int pciehp_enable_slot(struct slot *p_slot)
p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
}
- if (p_slot)
- update_slot_info(p_slot);
+ update_slot_info(p_slot);
+ mutex_unlock(&p_slot->ctrl->crit_sect);
return rc;
}
@@ -811,34 +818,37 @@ int pciehp_disable_slot(struct slot *p_slot)
if (!HP_SUPR_RM(p_slot->ctrl->ctrlcap)) {
ret = p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus);
if (ret || !getstatus) {
- info("%s: no adapter on slot(%x)\n", __FUNCTION__, p_slot->number);
+ info("%s: no adapter on slot(%s)\n", __FUNCTION__,
+ slot_name(p_slot));
mutex_unlock(&p_slot->ctrl->crit_sect);
- return 1;
+ return -ENODEV;
}
}
if (MRL_SENS(p_slot->ctrl->ctrlcap)) {
ret = p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
if (ret || getstatus) {
- info("%s: latch open on slot(%x)\n", __FUNCTION__, p_slot->number);
+ info("%s: latch open on slot(%s)\n", __FUNCTION__,
+ slot_name(p_slot));
mutex_unlock(&p_slot->ctrl->crit_sect);
- return 1;
+ return -ENODEV;
}
}
if (POWER_CTRL(p_slot->ctrl->ctrlcap)) {
ret = p_slot->hpc_ops->get_power_status(p_slot, &getstatus);
if (ret || !getstatus) {
- info("%s: already disabled slot(%x)\n", __FUNCTION__, p_slot->number);
+ info("%s: already disabled slot(%s)\n", __FUNCTION__,
+ slot_name(p_slot));
mutex_unlock(&p_slot->ctrl->crit_sect);
- return 1;
+ return -EINVAL;
}
}
- mutex_unlock(&p_slot->ctrl->crit_sect);
-
ret = remove_board(p_slot);
update_slot_info(p_slot);
+
+ mutex_unlock(&p_slot->ctrl->crit_sect);
return ret;
}
diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c
index 6ab3b6cd2b54..6d3f580f2666 100644
--- a/drivers/pci/hotplug/pciehp_hpc.c
+++ b/drivers/pci/hotplug/pciehp_hpc.c
@@ -222,7 +222,7 @@ static struct php_ctlr_state_s *php_ctlr_list_head; /* HPC state linked list */
static int ctlr_seq_num = 0; /* Controller sequence # */
static spinlock_t list_lock;
-static irqreturn_t pcie_isr(int IRQ, void *dev_id, struct pt_regs *regs);
+static irqreturn_t pcie_isr(int IRQ, void *dev_id);
static void start_int_poll_timer(struct php_ctlr_state_s *php_ctlr, int seconds);
@@ -239,7 +239,7 @@ static void int_poll_timeout(unsigned long lphp_ctlr)
}
/* Poll for interrupt events. regs == NULL => polling */
- pcie_isr( 0, (void *)php_ctlr, NULL );
+ pcie_isr( 0, (void *)php_ctlr );
init_timer(&php_ctlr->int_poll_timer);
@@ -718,8 +718,6 @@ static void hpc_release_ctlr(struct controller *ctrl)
if (php_ctlr->irq) {
free_irq(php_ctlr->irq, ctrl);
php_ctlr->irq = 0;
- if (!pcie_mch_quirk)
- pci_disable_msi(php_ctlr->pci_dev);
}
}
if (php_ctlr->pci_dev)
@@ -863,7 +861,7 @@ static int hpc_power_off_slot(struct slot * slot)
return retval;
}
-static irqreturn_t pcie_isr(int IRQ, void *dev_id, struct pt_regs *regs)
+static irqreturn_t pcie_isr(int IRQ, void *dev_id)
{
struct controller *ctrl = NULL;
struct php_ctlr_state_s *php_ctlr;
@@ -1402,6 +1400,8 @@ int pcie_init(struct controller * ctrl, struct pcie_device *dev)
pdev->subsystem_vendor, pdev->subsystem_device);
mutex_init(&ctrl->crit_sect);
+ mutex_init(&ctrl->ctrl_lock);
+
/* setup wait queue */
init_waitqueue_head(&ctrl->queue);
diff --git a/drivers/pci/hotplug/pcihp_skeleton.c b/drivers/pci/hotplug/pcihp_skeleton.c
index 8ad446605f75..50bcd3fe61da 100644
--- a/drivers/pci/hotplug/pcihp_skeleton.c
+++ b/drivers/pci/hotplug/pcihp_skeleton.c
@@ -1,5 +1,5 @@
/*
- * PCI Hot Plug Controller Skeleton Driver - 0.2
+ * PCI Hot Plug Controller Skeleton Driver - 0.3
*
* Copyright (C) 2001,2003 Greg Kroah-Hartman (greg@kroah.com)
* Copyright (C) 2001,2003 IBM Corp.
@@ -21,7 +21,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
- * This driver is to be used as a skeleton driver to be show how to interface
+ * This driver is to be used as a skeleton driver to show how to interface
* with the pci hotplug core easily.
*
* Send feedback to <greg@kroah.com>
@@ -33,8 +33,8 @@
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/pci.h>
+#include <linux/pci_hotplug.h>
#include <linux/init.h>
-#include "pci_hotplug.h"
#define SLOT_NAME_SIZE 10
struct slot {
@@ -58,8 +58,6 @@ static LIST_HEAD(slot_list);
#define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME , ## arg)
#define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME , ## arg)
-
-
/* local variables */
static int debug;
static int num_slots;
@@ -109,7 +107,6 @@ static int enable_slot(struct hotplug_slot *hotplug_slot)
return retval;
}
-
static int disable_slot(struct hotplug_slot *hotplug_slot)
{
struct slot *slot = hotplug_slot->private;
@@ -342,7 +339,7 @@ static int __init pcihp_skel_init(void)
info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
/*
* Do specific initialization stuff for your driver here
- * Like initializing your controller hardware (if any) and
+ * like initializing your controller hardware (if any) and
* determining the number of slots you have in the system
* right now.
*/
diff --git a/drivers/pci/hotplug/rpadlpar_core.c b/drivers/pci/hotplug/rpadlpar_core.c
index 46825fee3ae4..72383467a0d5 100644
--- a/drivers/pci/hotplug/rpadlpar_core.c
+++ b/drivers/pci/hotplug/rpadlpar_core.c
@@ -63,7 +63,7 @@ static struct device_node *find_php_slot_pci_node(char *drc_name,
char *type;
int rc;
- while ((np = of_find_node_by_type(np, "pci"))) {
+ while ((np = of_find_node_by_name(np, "pci"))) {
rc = rpaphp_get_drc_props(np, NULL, &name, &type, NULL);
if (rc == 0)
if (!strcmp(drc_name, name) && !strcmp(drc_type, type))
diff --git a/drivers/pci/hotplug/rpadlpar_sysfs.c b/drivers/pci/hotplug/rpadlpar_sysfs.c
index db69be85b458..6c5be3ff578c 100644
--- a/drivers/pci/hotplug/rpadlpar_sysfs.c
+++ b/drivers/pci/hotplug/rpadlpar_sysfs.c
@@ -14,7 +14,7 @@
*/
#include <linux/kobject.h>
#include <linux/string.h>
-#include "pci_hotplug.h"
+#include <linux/pci_hotplug.h>
#include "rpadlpar.h"
#define DLPAR_KOBJ_NAME "control"
diff --git a/drivers/pci/hotplug/rpaphp.h b/drivers/pci/hotplug/rpaphp.h
index 310b6186c0e5..2e7accf0f734 100644
--- a/drivers/pci/hotplug/rpaphp.h
+++ b/drivers/pci/hotplug/rpaphp.h
@@ -28,7 +28,7 @@
#define _PPC64PHP_H
#include <linux/pci.h>
-#include "pci_hotplug.h"
+#include <linux/pci_hotplug.h>
#define DR_INDICATOR 9002
#define DR_ENTITY_SENSE 9003
diff --git a/drivers/pci/hotplug/rpaphp_core.c b/drivers/pci/hotplug/rpaphp_core.c
index 7288a3eccfb3..71a2cb8baa4a 100644
--- a/drivers/pci/hotplug/rpaphp_core.c
+++ b/drivers/pci/hotplug/rpaphp_core.c
@@ -26,6 +26,7 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/pci.h>
+#include <linux/pci_hotplug.h>
#include <linux/slab.h>
#include <linux/smp.h>
#include <linux/smp_lock.h>
@@ -36,7 +37,6 @@
#include "../pci.h" /* for pci_add_new_bus */
/* and pci_do_scan_bus */
#include "rpaphp.h"
-#include "pci_hotplug.h"
int debug;
static struct semaphore rpaphp_sem;
@@ -356,7 +356,7 @@ static int __init rpaphp_init(void)
info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
init_MUTEX(&rpaphp_sem);
- while ((dn = of_find_node_by_type(dn, "pci")))
+ while ((dn = of_find_node_by_name(dn, "pci")))
rpaphp_add_slot(dn);
return 0;
diff --git a/drivers/pci/hotplug/sgi_hotplug.c b/drivers/pci/hotplug/sgi_hotplug.c
index f31d83c2c633..5d188c558386 100644
--- a/drivers/pci/hotplug/sgi_hotplug.c
+++ b/drivers/pci/hotplug/sgi_hotplug.c
@@ -13,6 +13,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
+#include <linux/pci_hotplug.h>
#include <linux/proc_fs.h>
#include <linux/types.h>
#include <linux/mutex.h>
@@ -29,7 +30,6 @@
#include <asm/sn/types.h>
#include "../pci.h"
-#include "pci_hotplug.h"
MODULE_LICENSE("GPL");
MODULE_AUTHOR("SGI (prarit@sgi.com, dickie@sgi.com, habeck@sgi.com)");
@@ -205,21 +205,6 @@ static struct hotplug_slot * sn_hp_destroy(void)
return bss_hotplug_slot;
}
-static void sn_bus_alloc_data(struct pci_dev *dev)
-{
- struct pci_bus *subordinate_bus;
- struct pci_dev *child;
-
- sn_pci_fixup_slot(dev);
-
- /* Recursively sets up the sn_irq_info structs */
- if (dev->subordinate) {
- subordinate_bus = dev->subordinate;
- list_for_each_entry(child, &subordinate_bus->devices, bus_list)
- sn_bus_alloc_data(child);
- }
-}
-
static void sn_bus_free_data(struct pci_dev *dev)
{
struct pci_bus *subordinate_bus;
@@ -337,6 +322,11 @@ static int sn_slot_disable(struct hotplug_slot *bss_hotplug_slot,
return rc;
}
+/*
+ * Power up and configure the slot via a SAL call to PROM.
+ * Scan slot (and any children), do any platform specific fixup,
+ * and find device driver.
+ */
static int enable_slot(struct hotplug_slot *bss_hotplug_slot)
{
struct slot *slot = bss_hotplug_slot->private;
@@ -345,6 +335,7 @@ static int enable_slot(struct hotplug_slot *bss_hotplug_slot)
int func, num_funcs;
int new_ppb = 0;
int rc;
+ void pcibios_fixup_device_resources(struct pci_dev *);
/* Serialize the Linux PCI infrastructure */
mutex_lock(&sn_hotplug_mutex);
@@ -367,9 +358,6 @@ static int enable_slot(struct hotplug_slot *bss_hotplug_slot)
return -ENODEV;
}
- sn_pci_controller_fixup(pci_domain_nr(slot->pci_bus),
- slot->pci_bus->number,
- slot->pci_bus);
/*
* Map SN resources for all functions on the card
* to the Linux PCI interface and tell the drivers
@@ -380,6 +368,13 @@ static int enable_slot(struct hotplug_slot *bss_hotplug_slot)
PCI_DEVFN(slot->device_num + 1,
PCI_FUNC(func)));
if (dev) {
+ /* Need to do slot fixup on PPB before fixup of children
+ * (PPB's pcidev_info needs to be in pcidev_info list
+ * before child's SN_PCIDEV_INFO() call to setup
+ * pdi_host_pcidev_info).
+ */
+ pcibios_fixup_device_resources(dev);
+ sn_pci_fixup_slot(dev);
if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
unsigned char sec_bus;
pci_read_config_byte(dev, PCI_SECONDARY_BUS,
@@ -387,12 +382,8 @@ static int enable_slot(struct hotplug_slot *bss_hotplug_slot)
new_bus = pci_add_new_bus(dev->bus, dev,
sec_bus);
pci_scan_child_bus(new_bus);
- sn_pci_controller_fixup(pci_domain_nr(new_bus),
- new_bus->number,
- new_bus);
new_ppb = 1;
}
- sn_bus_alloc_data(dev);
pci_dev_put(dev);
}
}
diff --git a/drivers/pci/hotplug/shpchp.h b/drivers/pci/hotplug/shpchp.h
index 7208b95c6ee7..50757695844f 100644
--- a/drivers/pci/hotplug/shpchp.h
+++ b/drivers/pci/hotplug/shpchp.h
@@ -31,12 +31,11 @@
#include <linux/types.h>
#include <linux/pci.h>
+#include <linux/pci_hotplug.h>
#include <linux/delay.h>
#include <linux/sched.h> /* signal_pending(), struct timer_list */
#include <linux/mutex.h>
-#include "pci_hotplug.h"
-
#if !defined(MODULE)
#define MY_NAME "shpchp"
#else
@@ -71,7 +70,7 @@ struct slot {
struct hotplug_slot *hotplug_slot;
struct list_head slot_list;
char name[SLOT_NAME_SIZE];
- struct work_struct work; /* work for button event */
+ struct delayed_work work; /* work for button event */
struct mutex lock;
};
@@ -103,7 +102,6 @@ struct controller {
u32 cap_offset;
unsigned long mmio_base;
unsigned long mmio_size;
- volatile int cmd_busy;
};
@@ -173,7 +171,7 @@ struct controller {
#define msg_button_cancel "PCI slot #%s - action canceled due to button press.\n"
/* sysfs functions for the hotplug controller info */
-extern void shpchp_create_ctrl_files (struct controller *ctrl);
+extern int __must_check shpchp_create_ctrl_files(struct controller *ctrl);
extern int shpchp_sysfs_enable_slot(struct slot *slot);
extern int shpchp_sysfs_disable_slot(struct slot *slot);
@@ -189,7 +187,7 @@ extern int shpchp_configure_device(struct slot *p_slot);
extern int shpchp_unconfigure_device(struct slot *p_slot);
extern void shpchp_remove_ctrl_files(struct controller *ctrl);
extern void cleanup_slots(struct controller *ctrl);
-extern void queue_pushbutton_work(void *data);
+extern void queue_pushbutton_work(struct work_struct *work);
#ifdef CONFIG_ACPI
diff --git a/drivers/pci/hotplug/shpchp_core.c b/drivers/pci/hotplug/shpchp_core.c
index a14e7de19846..4eac85b3d90e 100644
--- a/drivers/pci/hotplug/shpchp_core.c
+++ b/drivers/pci/hotplug/shpchp_core.c
@@ -159,7 +159,7 @@ static int init_slots(struct controller *ctrl)
goto error_info;
slot->number = sun;
- INIT_WORK(&slot->work, queue_pushbutton_work, slot);
+ INIT_DELAYED_WORK(&slot->work, queue_pushbutton_work);
/* register this slot with the hotplug pci core */
hotplug_slot->private = slot;
@@ -449,10 +449,14 @@ static int shpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
ctrl->speed = PCI_SPEED_33MHz;
}
- shpchp_create_ctrl_files(ctrl);
+ rc = shpchp_create_ctrl_files(ctrl);
+ if (rc)
+ goto err_cleanup_slots;
return 0;
+err_cleanup_slots:
+ cleanup_slots(ctrl);
err_out_release_ctlr:
ctrl->hpc_ops->release_ctlr(ctrl);
err_out_free_ctrl:
diff --git a/drivers/pci/hotplug/shpchp_ctrl.c b/drivers/pci/hotplug/shpchp_ctrl.c
index c39901dbff20..158ac7836096 100644
--- a/drivers/pci/hotplug/shpchp_ctrl.c
+++ b/drivers/pci/hotplug/shpchp_ctrl.c
@@ -36,7 +36,7 @@
#include "../pci.h"
#include "shpchp.h"
-static void interrupt_event_handler(void *data);
+static void interrupt_event_handler(struct work_struct *work);
static int shpchp_enable_slot(struct slot *p_slot);
static int shpchp_disable_slot(struct slot *p_slot);
@@ -50,7 +50,7 @@ static int queue_interrupt_event(struct slot *p_slot, u32 event_type)
info->event_type = event_type;
info->p_slot = p_slot;
- INIT_WORK(&info->work, interrupt_event_handler, info);
+ INIT_WORK(&info->work, interrupt_event_handler);
schedule_work(&info->work);
@@ -408,9 +408,10 @@ struct pushbutton_work_info {
* Handles all pending events and exits.
*
*/
-static void shpchp_pushbutton_thread(void *data)
+static void shpchp_pushbutton_thread(struct work_struct *work)
{
- struct pushbutton_work_info *info = data;
+ struct pushbutton_work_info *info =
+ container_of(work, struct pushbutton_work_info, work);
struct slot *p_slot = info->p_slot;
mutex_lock(&p_slot->lock);
@@ -436,9 +437,9 @@ static void shpchp_pushbutton_thread(void *data)
kfree(info);
}
-void queue_pushbutton_work(void *data)
+void queue_pushbutton_work(struct work_struct *work)
{
- struct slot *p_slot = data;
+ struct slot *p_slot = container_of(work, struct slot, work.work);
struct pushbutton_work_info *info;
info = kmalloc(sizeof(*info), GFP_KERNEL);
@@ -447,7 +448,7 @@ void queue_pushbutton_work(void *data)
return;
}
info->p_slot = p_slot;
- INIT_WORK(&info->work, shpchp_pushbutton_thread, info);
+ INIT_WORK(&info->work, shpchp_pushbutton_thread);
mutex_lock(&p_slot->lock);
switch (p_slot->state) {
@@ -541,9 +542,9 @@ static void handle_button_press_event(struct slot *p_slot)
}
}
-static void interrupt_event_handler(void *data)
+static void interrupt_event_handler(struct work_struct *work)
{
- struct event_info *info = data;
+ struct event_info *info = container_of(work, struct event_info, work);
struct slot *p_slot = info->p_slot;
mutex_lock(&p_slot->lock);
diff --git a/drivers/pci/hotplug/shpchp_hpc.c b/drivers/pci/hotplug/shpchp_hpc.c
index 0f9798df4704..83a5226ba9ed 100644
--- a/drivers/pci/hotplug/shpchp_hpc.c
+++ b/drivers/pci/hotplug/shpchp_hpc.c
@@ -218,7 +218,7 @@ static spinlock_t list_lock;
static atomic_t shpchp_num_controllers = ATOMIC_INIT(0);
-static irqreturn_t shpc_isr(int irq, void *dev_id, struct pt_regs *regs);
+static irqreturn_t shpc_isr(int irq, void *dev_id);
static void start_int_poll_timer(struct php_ctlr_state_s *php_ctlr, int sec);
static int hpc_check_cmd_status(struct controller *ctrl);
@@ -276,7 +276,7 @@ static void int_poll_timeout(unsigned long lphp_ctlr)
DBG_ENTER_ROUTINE
/* Poll for interrupt events. regs == NULL => polling */
- shpc_isr(0, php_ctlr->callback_instance_id, NULL);
+ shpc_isr(0, php_ctlr->callback_instance_id);
init_timer(&php_ctlr->int_poll_timer);
if (!shpchp_poll_time)
@@ -302,21 +302,51 @@ static void start_int_poll_timer(struct php_ctlr_state_s *php_ctlr, int sec)
add_timer(&php_ctlr->int_poll_timer);
}
+static inline int is_ctrl_busy(struct controller *ctrl)
+{
+ u16 cmd_status = shpc_readw(ctrl, CMD_STATUS);
+ return cmd_status & 0x1;
+}
+
+/*
+ * Returns 1 if SHPC finishes executing a command within 1 sec,
+ * otherwise returns 0.
+ */
+static inline int shpc_poll_ctrl_busy(struct controller *ctrl)
+{
+ int i;
+
+ if (!is_ctrl_busy(ctrl))
+ return 1;
+
+ /* Check every 0.1 sec for a total of 1 sec */
+ for (i = 0; i < 10; i++) {
+ msleep(100);
+ if (!is_ctrl_busy(ctrl))
+ return 1;
+ }
+
+ return 0;
+}
+
static inline int shpc_wait_cmd(struct controller *ctrl)
{
int retval = 0;
- unsigned int timeout_msec = shpchp_poll_mode ? 2000 : 1000;
- unsigned long timeout = msecs_to_jiffies(timeout_msec);
- int rc = wait_event_interruptible_timeout(ctrl->queue,
- !ctrl->cmd_busy, timeout);
- if (!rc) {
+ unsigned long timeout = msecs_to_jiffies(1000);
+ int rc;
+
+ if (shpchp_poll_mode)
+ rc = shpc_poll_ctrl_busy(ctrl);
+ else
+ rc = wait_event_interruptible_timeout(ctrl->queue,
+ !is_ctrl_busy(ctrl), timeout);
+ if (!rc && is_ctrl_busy(ctrl)) {
retval = -EIO;
- err("Command not completed in %d msec\n", timeout_msec);
+ err("Command not completed in 1000 msec\n");
} else if (rc < 0) {
retval = -EINTR;
info("Command was interrupted by a signal\n");
}
- ctrl->cmd_busy = 0;
return retval;
}
@@ -327,26 +357,15 @@ static int shpc_write_cmd(struct slot *slot, u8 t_slot, u8 cmd)
u16 cmd_status;
int retval = 0;
u16 temp_word;
- int i;
DBG_ENTER_ROUTINE
mutex_lock(&slot->ctrl->cmd_lock);
- for (i = 0; i < 10; i++) {
- cmd_status = shpc_readw(ctrl, CMD_STATUS);
-
- if (!(cmd_status & 0x1))
- break;
- /* Check every 0.1 sec for a total of 1 sec*/
- msleep(100);
- }
-
- cmd_status = shpc_readw(ctrl, CMD_STATUS);
-
- if (cmd_status & 0x1) {
+ if (!shpc_poll_ctrl_busy(ctrl)) {
/* After 1 sec and and the controller is still busy */
- err("%s : Controller is still busy after 1 sec.\n", __FUNCTION__);
+ err("%s : Controller is still busy after 1 sec.\n",
+ __FUNCTION__);
retval = -EBUSY;
goto out;
}
@@ -358,7 +377,6 @@ static int shpc_write_cmd(struct slot *slot, u8 t_slot, u8 cmd)
/* To make sure the Controller Busy bit is 0 before we send out the
* command.
*/
- slot->ctrl->cmd_busy = 1;
shpc_writew(ctrl, CMD, temp_word);
/*
@@ -870,7 +888,7 @@ static int hpc_set_bus_speed_mode(struct slot * slot, enum pci_bus_speed value)
return retval;
}
-static irqreturn_t shpc_isr(int irq, void *dev_id, struct pt_regs *regs)
+static irqreturn_t shpc_isr(int irq, void *dev_id)
{
struct controller *ctrl = (struct controller *)dev_id;
struct php_ctlr_state_s *php_ctlr = ctrl->hpc_ctlr_handle;
@@ -908,7 +926,6 @@ static irqreturn_t shpc_isr(int irq, void *dev_id, struct pt_regs *regs)
serr_int &= ~SERR_INTR_RSVDZ_MASK;
shpc_writel(ctrl, SERR_INTR_ENABLE, serr_int);
- ctrl->cmd_busy = 0;
wake_up_interruptible(&ctrl->queue);
}
@@ -1101,7 +1118,7 @@ int shpc_init(struct controller * ctrl, struct pci_dev * pdev)
{
struct php_ctlr_state_s *php_ctlr, *p;
void *instance_id = ctrl;
- int rc, num_slots = 0;
+ int rc = -1, num_slots = 0;
u8 hp_slot;
u32 shpc_base_offset;
u32 tempdword, slot_reg, slot_config;
@@ -1167,11 +1184,15 @@ int shpc_init(struct controller * ctrl, struct pci_dev * pdev)
info("HPC vendor_id %x device_id %x ss_vid %x ss_did %x\n", pdev->vendor, pdev->device, pdev->subsystem_vendor,
pdev->subsystem_device);
- if (pci_enable_device(pdev))
+ rc = pci_enable_device(pdev);
+ if (rc) {
+ err("%s: pci_enable_device failed\n", __FUNCTION__);
goto abort_free_ctlr;
+ }
if (!request_mem_region(ctrl->mmio_base, ctrl->mmio_size, MY_NAME)) {
err("%s: cannot reserve MMIO region\n", __FUNCTION__);
+ rc = -1;
goto abort_free_ctlr;
}
@@ -1180,6 +1201,7 @@ int shpc_init(struct controller * ctrl, struct pci_dev * pdev)
err("%s: cannot remap MMIO region %lx @ %lx\n", __FUNCTION__,
ctrl->mmio_size, ctrl->mmio_base);
release_mem_region(ctrl->mmio_base, ctrl->mmio_size);
+ rc = -1;
goto abort_free_ctlr;
}
dbg("%s: php_ctlr->creg %p\n", __FUNCTION__, php_ctlr->creg);
@@ -1282,8 +1304,10 @@ int shpc_init(struct controller * ctrl, struct pci_dev * pdev)
*/
if (atomic_add_return(1, &shpchp_num_controllers) == 1) {
shpchp_wq = create_singlethread_workqueue("shpchpd");
- if (!shpchp_wq)
- return -ENOMEM;
+ if (!shpchp_wq) {
+ rc = -ENOMEM;
+ goto abort_free_ctlr;
+ }
}
/*
@@ -1313,8 +1337,10 @@ int shpc_init(struct controller * ctrl, struct pci_dev * pdev)
/* We end up here for the many possible ways to fail this API. */
abort_free_ctlr:
+ if (php_ctlr->creg)
+ iounmap(php_ctlr->creg);
kfree(php_ctlr);
abort:
DBG_LEAVE_ROUTINE
- return -1;
+ return rc;
}
diff --git a/drivers/pci/hotplug/shpchp_sysfs.c b/drivers/pci/hotplug/shpchp_sysfs.c
index 620e1139e607..29fa9d26adae 100644
--- a/drivers/pci/hotplug/shpchp_sysfs.c
+++ b/drivers/pci/hotplug/shpchp_sysfs.c
@@ -91,9 +91,9 @@ static ssize_t show_ctrl (struct device *dev, struct device_attribute *attr, cha
}
static DEVICE_ATTR (ctrl, S_IRUGO, show_ctrl, NULL);
-void shpchp_create_ctrl_files (struct controller *ctrl)
+int __must_check shpchp_create_ctrl_files (struct controller *ctrl)
{
- device_create_file (&ctrl->pci_dev->dev, &dev_attr_ctrl);
+ return device_create_file (&ctrl->pci_dev->dev, &dev_attr_ctrl);
}
void shpchp_remove_ctrl_files(struct controller *ctrl)
diff --git a/drivers/pci/htirq.c b/drivers/pci/htirq.c
new file mode 100644
index 000000000000..0a8d1cce9fa0
--- /dev/null
+++ b/drivers/pci/htirq.c
@@ -0,0 +1,185 @@
+/*
+ * File: htirq.c
+ * Purpose: Hypertransport Interrupt Capability
+ *
+ * Copyright (C) 2006 Linux Networx
+ * Copyright (C) Eric Biederman <ebiederman@lnxi.com>
+ */
+
+#include <linux/irq.h>
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/gfp.h>
+#include <linux/htirq.h>
+
+/* Global ht irq lock.
+ *
+ * This is needed to serialize access to the data port in hypertransport
+ * irq capability.
+ *
+ * With multiple simultaneous hypertransport irq devices it might pay
+ * to make this more fine grained. But start with simple, stupid, and correct.
+ */
+static DEFINE_SPINLOCK(ht_irq_lock);
+
+struct ht_irq_cfg {
+ struct pci_dev *dev;
+ /* Update callback used to cope with buggy hardware */
+ ht_irq_update_t *update;
+ unsigned pos;
+ unsigned idx;
+ struct ht_irq_msg msg;
+};
+
+
+void write_ht_irq_msg(unsigned int irq, struct ht_irq_msg *msg)
+{
+ struct ht_irq_cfg *cfg = get_irq_data(irq);
+ unsigned long flags;
+ spin_lock_irqsave(&ht_irq_lock, flags);
+ if (cfg->msg.address_lo != msg->address_lo) {
+ pci_write_config_byte(cfg->dev, cfg->pos + 2, cfg->idx);
+ pci_write_config_dword(cfg->dev, cfg->pos + 4, msg->address_lo);
+ }
+ if (cfg->msg.address_hi != msg->address_hi) {
+ pci_write_config_byte(cfg->dev, cfg->pos + 2, cfg->idx + 1);
+ pci_write_config_dword(cfg->dev, cfg->pos + 4, msg->address_hi);
+ }
+ if (cfg->update)
+ cfg->update(cfg->dev, irq, msg);
+ spin_unlock_irqrestore(&ht_irq_lock, flags);
+ cfg->msg = *msg;
+}
+
+void fetch_ht_irq_msg(unsigned int irq, struct ht_irq_msg *msg)
+{
+ struct ht_irq_cfg *cfg = get_irq_data(irq);
+ *msg = cfg->msg;
+}
+
+void mask_ht_irq(unsigned int irq)
+{
+ struct ht_irq_cfg *cfg;
+ struct ht_irq_msg msg;
+
+ cfg = get_irq_data(irq);
+
+ msg = cfg->msg;
+ msg.address_lo |= 1;
+ write_ht_irq_msg(irq, &msg);
+}
+
+void unmask_ht_irq(unsigned int irq)
+{
+ struct ht_irq_cfg *cfg;
+ struct ht_irq_msg msg;
+
+ cfg = get_irq_data(irq);
+
+ msg = cfg->msg;
+ msg.address_lo &= ~1;
+ write_ht_irq_msg(irq, &msg);
+}
+
+/**
+ * __ht_create_irq - create an irq and attach it to a device.
+ * @dev: The hypertransport device to find the irq capability on.
+ * @idx: Which of the possible irqs to attach to.
+ * @update: Function to be called when changing the htirq message
+ *
+ * The irq number of the new irq or a negative error value is returned.
+ */
+int __ht_create_irq(struct pci_dev *dev, int idx, ht_irq_update_t *update)
+{
+ struct ht_irq_cfg *cfg;
+ unsigned long flags;
+ u32 data;
+ int max_irq;
+ int pos;
+ int irq;
+
+ pos = pci_find_capability(dev, PCI_CAP_ID_HT);
+ while (pos) {
+ u8 subtype;
+ pci_read_config_byte(dev, pos + 3, &subtype);
+ if (subtype == HT_CAPTYPE_IRQ)
+ break;
+ pos = pci_find_next_capability(dev, pos, PCI_CAP_ID_HT);
+ }
+ if (!pos)
+ return -EINVAL;
+
+ /* Verify the idx I want to use is in range */
+ spin_lock_irqsave(&ht_irq_lock, flags);
+ pci_write_config_byte(dev, pos + 2, 1);
+ pci_read_config_dword(dev, pos + 4, &data);
+ spin_unlock_irqrestore(&ht_irq_lock, flags);
+
+ max_irq = (data >> 16) & 0xff;
+ if ( idx > max_irq)
+ return -EINVAL;
+
+ cfg = kmalloc(sizeof(*cfg), GFP_KERNEL);
+ if (!cfg)
+ return -ENOMEM;
+
+ cfg->dev = dev;
+ cfg->update = update;
+ cfg->pos = pos;
+ cfg->idx = 0x10 + (idx * 2);
+ /* Initialize msg to a value that will never match the first write. */
+ cfg->msg.address_lo = 0xffffffff;
+ cfg->msg.address_hi = 0xffffffff;
+
+ irq = create_irq();
+ if (irq < 0) {
+ kfree(cfg);
+ return -EBUSY;
+ }
+ set_irq_data(irq, cfg);
+
+ if (arch_setup_ht_irq(irq, dev) < 0) {
+ ht_destroy_irq(irq);
+ return -EBUSY;
+ }
+
+ return irq;
+}
+
+/**
+ * ht_create_irq - create an irq and attach it to a device.
+ * @dev: The hypertransport device to find the irq capability on.
+ * @idx: Which of the possible irqs to attach to.
+ *
+ * ht_create_irq needs to be called for all hypertransport devices
+ * that generate irqs.
+ *
+ * The irq number of the new irq or a negative error value is returned.
+ */
+int ht_create_irq(struct pci_dev *dev, int idx)
+{
+ return __ht_create_irq(dev, idx, NULL);
+}
+
+/**
+ * ht_destroy_irq - destroy an irq created with ht_create_irq
+ *
+ * This reverses ht_create_irq removing the specified irq from
+ * existence. The irq should be free before this happens.
+ */
+void ht_destroy_irq(unsigned int irq)
+{
+ struct ht_irq_cfg *cfg;
+
+ cfg = get_irq_data(irq);
+ set_irq_chip(irq, NULL);
+ set_irq_data(irq, NULL);
+ destroy_irq(irq);
+
+ kfree(cfg);
+}
+
+EXPORT_SYMBOL(__ht_create_irq);
+EXPORT_SYMBOL(ht_create_irq);
+EXPORT_SYMBOL(ht_destroy_irq);
diff --git a/drivers/pci/msi-altix.c b/drivers/pci/msi-altix.c
deleted file mode 100644
index bed4183a5e39..000000000000
--- a/drivers/pci/msi-altix.c
+++ /dev/null
@@ -1,210 +0,0 @@
-/*
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file "COPYING" in the main directory of this archive
- * for more details.
- *
- * Copyright (C) 2006 Silicon Graphics, Inc. All Rights Reserved.
- */
-
-#include <linux/types.h>
-#include <linux/pci.h>
-#include <linux/cpumask.h>
-
-#include <asm/sn/addrs.h>
-#include <asm/sn/intr.h>
-#include <asm/sn/pcibus_provider_defs.h>
-#include <asm/sn/pcidev.h>
-#include <asm/sn/nodepda.h>
-
-#include "msi.h"
-
-struct sn_msi_info {
- u64 pci_addr;
- struct sn_irq_info *sn_irq_info;
-};
-
-static struct sn_msi_info *sn_msi_info;
-
-static void
-sn_msi_teardown(unsigned int vector)
-{
- nasid_t nasid;
- int widget;
- struct pci_dev *pdev;
- struct pcidev_info *sn_pdev;
- struct sn_irq_info *sn_irq_info;
- struct pcibus_bussoft *bussoft;
- struct sn_pcibus_provider *provider;
-
- sn_irq_info = sn_msi_info[vector].sn_irq_info;
- if (sn_irq_info == NULL || sn_irq_info->irq_int_bit >= 0)
- return;
-
- sn_pdev = (struct pcidev_info *)sn_irq_info->irq_pciioinfo;
- pdev = sn_pdev->pdi_linux_pcidev;
- provider = SN_PCIDEV_BUSPROVIDER(pdev);
-
- (*provider->dma_unmap)(pdev,
- sn_msi_info[vector].pci_addr,
- PCI_DMA_FROMDEVICE);
- sn_msi_info[vector].pci_addr = 0;
-
- bussoft = SN_PCIDEV_BUSSOFT(pdev);
- nasid = NASID_GET(bussoft->bs_base);
- widget = (nasid & 1) ?
- TIO_SWIN_WIDGETNUM(bussoft->bs_base) :
- SWIN_WIDGETNUM(bussoft->bs_base);
-
- sn_intr_free(nasid, widget, sn_irq_info);
- sn_msi_info[vector].sn_irq_info = NULL;
-
- return;
-}
-
-int
-sn_msi_setup(struct pci_dev *pdev, unsigned int vector,
- u32 *addr_hi, u32 *addr_lo, u32 *data)
-{
- int widget;
- int status;
- nasid_t nasid;
- u64 bus_addr;
- struct sn_irq_info *sn_irq_info;
- struct pcibus_bussoft *bussoft = SN_PCIDEV_BUSSOFT(pdev);
- struct sn_pcibus_provider *provider = SN_PCIDEV_BUSPROVIDER(pdev);
-
- if (bussoft == NULL)
- return -EINVAL;
-
- if (provider == NULL || provider->dma_map_consistent == NULL)
- return -EINVAL;
-
- /*
- * Set up the vector plumbing. Let the prom (via sn_intr_alloc)
- * decide which cpu to direct this msi at by default.
- */
-
- nasid = NASID_GET(bussoft->bs_base);
- widget = (nasid & 1) ?
- TIO_SWIN_WIDGETNUM(bussoft->bs_base) :
- SWIN_WIDGETNUM(bussoft->bs_base);
-
- sn_irq_info = kzalloc(sizeof(struct sn_irq_info), GFP_KERNEL);
- if (! sn_irq_info)
- return -ENOMEM;
-
- status = sn_intr_alloc(nasid, widget, sn_irq_info, vector, -1, -1);
- if (status) {
- kfree(sn_irq_info);
- return -ENOMEM;
- }
-
- sn_irq_info->irq_int_bit = -1; /* mark this as an MSI irq */
- sn_irq_fixup(pdev, sn_irq_info);
-
- /* Prom probably should fill these in, but doesn't ... */
- sn_irq_info->irq_bridge_type = bussoft->bs_asic_type;
- sn_irq_info->irq_bridge = (void *)bussoft->bs_base;
-
- /*
- * Map the xio address into bus space
- */
- bus_addr = (*provider->dma_map_consistent)(pdev,
- sn_irq_info->irq_xtalkaddr,
- sizeof(sn_irq_info->irq_xtalkaddr),
- SN_DMA_MSI|SN_DMA_ADDR_XIO);
- if (! bus_addr) {
- sn_intr_free(nasid, widget, sn_irq_info);
- kfree(sn_irq_info);
- return -ENOMEM;
- }
-
- sn_msi_info[vector].sn_irq_info = sn_irq_info;
- sn_msi_info[vector].pci_addr = bus_addr;
-
- *addr_hi = (u32)(bus_addr >> 32);
- *addr_lo = (u32)(bus_addr & 0x00000000ffffffff);
-
- /*
- * In the SN platform, bit 16 is a "send vector" bit which
- * must be present in order to move the vector through the system.
- */
- *data = 0x100 + (unsigned int)vector;
-
-#ifdef CONFIG_SMP
- set_irq_affinity_info((vector & 0xff), sn_irq_info->irq_cpuid, 0);
-#endif
-
- return 0;
-}
-
-static void
-sn_msi_target(unsigned int vector, unsigned int cpu,
- u32 *addr_hi, u32 *addr_lo)
-{
- int slice;
- nasid_t nasid;
- u64 bus_addr;
- struct pci_dev *pdev;
- struct pcidev_info *sn_pdev;
- struct sn_irq_info *sn_irq_info;
- struct sn_irq_info *new_irq_info;
- struct sn_pcibus_provider *provider;
-
- sn_irq_info = sn_msi_info[vector].sn_irq_info;
- if (sn_irq_info == NULL || sn_irq_info->irq_int_bit >= 0)
- return;
-
- /*
- * Release XIO resources for the old MSI PCI address
- */
-
- sn_pdev = (struct pcidev_info *)sn_irq_info->irq_pciioinfo;
- pdev = sn_pdev->pdi_linux_pcidev;
- provider = SN_PCIDEV_BUSPROVIDER(pdev);
-
- bus_addr = (u64)(*addr_hi) << 32 | (u64)(*addr_lo);
- (*provider->dma_unmap)(pdev, bus_addr, PCI_DMA_FROMDEVICE);
- sn_msi_info[vector].pci_addr = 0;
-
- nasid = cpuid_to_nasid(cpu);
- slice = cpuid_to_slice(cpu);
-
- new_irq_info = sn_retarget_vector(sn_irq_info, nasid, slice);
- sn_msi_info[vector].sn_irq_info = new_irq_info;
- if (new_irq_info == NULL)
- return;
-
- /*
- * Map the xio address into bus space
- */
-
- bus_addr = (*provider->dma_map_consistent)(pdev,
- new_irq_info->irq_xtalkaddr,
- sizeof(new_irq_info->irq_xtalkaddr),
- SN_DMA_MSI|SN_DMA_ADDR_XIO);
-
- sn_msi_info[vector].pci_addr = bus_addr;
- *addr_hi = (u32)(bus_addr >> 32);
- *addr_lo = (u32)(bus_addr & 0x00000000ffffffff);
-}
-
-struct msi_ops sn_msi_ops = {
- .setup = sn_msi_setup,
- .teardown = sn_msi_teardown,
-#ifdef CONFIG_SMP
- .target = sn_msi_target,
-#endif
-};
-
-int
-sn_msi_init(void)
-{
- sn_msi_info =
- kzalloc(sizeof(struct sn_msi_info) * NR_VECTORS, GFP_KERNEL);
- if (! sn_msi_info)
- return -ENOMEM;
-
- msi_register(&sn_msi_ops);
- return 0;
-}
diff --git a/drivers/pci/msi-apic.c b/drivers/pci/msi-apic.c
deleted file mode 100644
index 5ed798b319c7..000000000000
--- a/drivers/pci/msi-apic.c
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * MSI hooks for standard x86 apic
- */
-
-#include <linux/pci.h>
-#include <linux/irq.h>
-#include <asm/smp.h>
-
-#include "msi.h"
-
-/*
- * Shifts for APIC-based data
- */
-
-#define MSI_DATA_VECTOR_SHIFT 0
-#define MSI_DATA_VECTOR(v) (((u8)v) << MSI_DATA_VECTOR_SHIFT)
-
-#define MSI_DATA_DELIVERY_SHIFT 8
-#define MSI_DATA_DELIVERY_FIXED (0 << MSI_DATA_DELIVERY_SHIFT)
-#define MSI_DATA_DELIVERY_LOWPRI (1 << MSI_DATA_DELIVERY_SHIFT)
-
-#define MSI_DATA_LEVEL_SHIFT 14
-#define MSI_DATA_LEVEL_DEASSERT (0 << MSI_DATA_LEVEL_SHIFT)
-#define MSI_DATA_LEVEL_ASSERT (1 << MSI_DATA_LEVEL_SHIFT)
-
-#define MSI_DATA_TRIGGER_SHIFT 15
-#define MSI_DATA_TRIGGER_EDGE (0 << MSI_DATA_TRIGGER_SHIFT)
-#define MSI_DATA_TRIGGER_LEVEL (1 << MSI_DATA_TRIGGER_SHIFT)
-
-/*
- * Shift/mask fields for APIC-based bus address
- */
-
-#define MSI_ADDR_HEADER 0xfee00000
-
-#define MSI_ADDR_DESTID_MASK 0xfff0000f
-#define MSI_ADDR_DESTID_CPU(cpu) ((cpu) << MSI_TARGET_CPU_SHIFT)
-
-#define MSI_ADDR_DESTMODE_SHIFT 2
-#define MSI_ADDR_DESTMODE_PHYS (0 << MSI_ADDR_DESTMODE_SHIFT)
-#define MSI_ADDR_DESTMODE_LOGIC (1 << MSI_ADDR_DESTMODE_SHIFT)
-
-#define MSI_ADDR_REDIRECTION_SHIFT 3
-#define MSI_ADDR_REDIRECTION_CPU (0 << MSI_ADDR_REDIRECTION_SHIFT)
-#define MSI_ADDR_REDIRECTION_LOWPRI (1 << MSI_ADDR_REDIRECTION_SHIFT)
-
-
-static void
-msi_target_apic(unsigned int vector,
- unsigned int dest_cpu,
- u32 *address_hi, /* in/out */
- u32 *address_lo) /* in/out */
-{
- u32 addr = *address_lo;
-
- addr &= MSI_ADDR_DESTID_MASK;
- addr |= MSI_ADDR_DESTID_CPU(cpu_physical_id(dest_cpu));
-
- *address_lo = addr;
-}
-
-static int
-msi_setup_apic(struct pci_dev *pdev, /* unused in generic */
- unsigned int vector,
- u32 *address_hi,
- u32 *address_lo,
- u32 *data)
-{
- unsigned long dest_phys_id;
-
- dest_phys_id = cpu_physical_id(first_cpu(cpu_online_map));
-
- *address_hi = 0;
- *address_lo = MSI_ADDR_HEADER |
- MSI_ADDR_DESTMODE_PHYS |
- MSI_ADDR_REDIRECTION_CPU |
- MSI_ADDR_DESTID_CPU(dest_phys_id);
-
- *data = MSI_DATA_TRIGGER_EDGE |
- MSI_DATA_LEVEL_ASSERT |
- MSI_DATA_DELIVERY_FIXED |
- MSI_DATA_VECTOR(vector);
-
- return 0;
-}
-
-static void
-msi_teardown_apic(unsigned int vector)
-{
- return; /* no-op */
-}
-
-/*
- * Generic ops used on most IA archs/platforms. Set with msi_register()
- */
-
-struct msi_ops msi_apic_ops = {
- .setup = msi_setup_apic,
- .teardown = msi_teardown_apic,
- .target = msi_target_apic,
-};
diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c
index a83c1f5735d6..ed3f7e1a563c 100644
--- a/drivers/pci/msi.c
+++ b/drivers/pci/msi.c
@@ -6,6 +6,7 @@
* Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com)
*/
+#include <linux/err.h>
#include <linux/mm.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
@@ -14,6 +15,7 @@
#include <linux/smp_lock.h>
#include <linux/pci.h>
#include <linux/proc_fs.h>
+#include <linux/msi.h>
#include <asm/errno.h>
#include <asm/io.h>
@@ -24,63 +26,39 @@
static DEFINE_SPINLOCK(msi_lock);
static struct msi_desc* msi_desc[NR_IRQS] = { [0 ... NR_IRQS-1] = NULL };
-static kmem_cache_t* msi_cachep;
+static struct kmem_cache* msi_cachep;
static int pci_msi_enable = 1;
-static int last_alloc_vector;
-static int nr_released_vectors;
-static int nr_reserved_vectors = NR_HP_RESERVED_VECTORS;
-static int nr_msix_devices;
-
-#ifndef CONFIG_X86_IO_APIC
-int vector_irq[NR_VECTORS] = { [0 ... NR_VECTORS - 1] = -1};
-#endif
-
-static struct msi_ops *msi_ops;
-
-int
-msi_register(struct msi_ops *ops)
-{
- msi_ops = ops;
- return 0;
-}
-
-static void msi_cache_ctor(void *p, kmem_cache_t *cache, unsigned long flags)
-{
- memset(p, 0, sizeof(struct msi_desc));
-}
static int msi_cache_init(void)
{
- msi_cachep = kmem_cache_create("msi_cache",
- sizeof(struct msi_desc),
- 0, SLAB_HWCACHE_ALIGN, msi_cache_ctor, NULL);
+ msi_cachep = kmem_cache_create("msi_cache", sizeof(struct msi_desc),
+ 0, SLAB_HWCACHE_ALIGN, NULL, NULL);
if (!msi_cachep)
return -ENOMEM;
return 0;
}
-static void msi_set_mask_bit(unsigned int vector, int flag)
+static void msi_set_mask_bit(unsigned int irq, int flag)
{
struct msi_desc *entry;
- entry = (struct msi_desc *)msi_desc[vector];
- if (!entry || !entry->dev || !entry->mask_base)
- return;
+ entry = msi_desc[irq];
+ BUG_ON(!entry || !entry->dev);
switch (entry->msi_attrib.type) {
case PCI_CAP_ID_MSI:
- {
- int pos;
- u32 mask_bits;
-
- pos = (long)entry->mask_base;
- pci_read_config_dword(entry->dev, pos, &mask_bits);
- mask_bits &= ~(1);
- mask_bits |= flag;
- pci_write_config_dword(entry->dev, pos, mask_bits);
+ if (entry->msi_attrib.maskbit) {
+ int pos;
+ u32 mask_bits;
+
+ pos = (long)entry->mask_base;
+ pci_read_config_dword(entry->dev, pos, &mask_bits);
+ mask_bits &= ~(1);
+ mask_bits |= flag;
+ pci_write_config_dword(entry->dev, pos, mask_bits);
+ }
break;
- }
case PCI_CAP_ID_MSIX:
{
int offset = entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE +
@@ -89,261 +67,101 @@ static void msi_set_mask_bit(unsigned int vector, int flag)
break;
}
default:
+ BUG();
break;
}
}
-#ifdef CONFIG_SMP
-static void set_msi_affinity(unsigned int vector, cpumask_t cpu_mask)
+void read_msi_msg(unsigned int irq, struct msi_msg *msg)
{
- struct msi_desc *entry;
- u32 address_hi, address_lo;
- unsigned int irq = vector;
- unsigned int dest_cpu = first_cpu(cpu_mask);
-
- entry = (struct msi_desc *)msi_desc[vector];
- if (!entry || !entry->dev)
- return;
-
- switch (entry->msi_attrib.type) {
+ struct msi_desc *entry = get_irq_data(irq);
+ switch(entry->msi_attrib.type) {
case PCI_CAP_ID_MSI:
{
- int pos = pci_find_capability(entry->dev, PCI_CAP_ID_MSI);
-
- if (!pos)
- return;
-
- pci_read_config_dword(entry->dev, msi_upper_address_reg(pos),
- &address_hi);
- pci_read_config_dword(entry->dev, msi_lower_address_reg(pos),
- &address_lo);
-
- msi_ops->target(vector, dest_cpu, &address_hi, &address_lo);
-
- pci_write_config_dword(entry->dev, msi_upper_address_reg(pos),
- address_hi);
- pci_write_config_dword(entry->dev, msi_lower_address_reg(pos),
- address_lo);
- set_native_irq_info(irq, cpu_mask);
+ struct pci_dev *dev = entry->dev;
+ int pos = entry->msi_attrib.pos;
+ u16 data;
+
+ pci_read_config_dword(dev, msi_lower_address_reg(pos),
+ &msg->address_lo);
+ if (entry->msi_attrib.is_64) {
+ pci_read_config_dword(dev, msi_upper_address_reg(pos),
+ &msg->address_hi);
+ pci_read_config_word(dev, msi_data_reg(pos, 1), &data);
+ } else {
+ msg->address_hi = 0;
+ pci_read_config_word(dev, msi_data_reg(pos, 1), &data);
+ }
+ msg->data = data;
break;
}
case PCI_CAP_ID_MSIX:
{
- int offset_hi =
- entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE +
- PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET;
- int offset_lo =
- entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE +
- PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET;
-
- address_hi = readl(entry->mask_base + offset_hi);
- address_lo = readl(entry->mask_base + offset_lo);
+ void __iomem *base;
+ base = entry->mask_base +
+ entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE;
- msi_ops->target(vector, dest_cpu, &address_hi, &address_lo);
+ msg->address_lo = readl(base + PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET);
+ msg->address_hi = readl(base + PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET);
+ msg->data = readl(base + PCI_MSIX_ENTRY_DATA_OFFSET);
+ break;
+ }
+ default:
+ BUG();
+ }
+}
- writel(address_hi, entry->mask_base + offset_hi);
- writel(address_lo, entry->mask_base + offset_lo);
- set_native_irq_info(irq, cpu_mask);
+void write_msi_msg(unsigned int irq, struct msi_msg *msg)
+{
+ struct msi_desc *entry = get_irq_data(irq);
+ switch (entry->msi_attrib.type) {
+ case PCI_CAP_ID_MSI:
+ {
+ struct pci_dev *dev = entry->dev;
+ int pos = entry->msi_attrib.pos;
+
+ pci_write_config_dword(dev, msi_lower_address_reg(pos),
+ msg->address_lo);
+ if (entry->msi_attrib.is_64) {
+ pci_write_config_dword(dev, msi_upper_address_reg(pos),
+ msg->address_hi);
+ pci_write_config_word(dev, msi_data_reg(pos, 1),
+ msg->data);
+ } else {
+ pci_write_config_word(dev, msi_data_reg(pos, 0),
+ msg->data);
+ }
break;
}
- default:
+ case PCI_CAP_ID_MSIX:
+ {
+ void __iomem *base;
+ base = entry->mask_base +
+ entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE;
+
+ writel(msg->address_lo,
+ base + PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET);
+ writel(msg->address_hi,
+ base + PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET);
+ writel(msg->data, base + PCI_MSIX_ENTRY_DATA_OFFSET);
break;
}
-}
-#else
-#define set_msi_affinity NULL
-#endif /* CONFIG_SMP */
-
-static void mask_MSI_irq(unsigned int vector)
-{
- msi_set_mask_bit(vector, 1);
-}
-
-static void unmask_MSI_irq(unsigned int vector)
-{
- msi_set_mask_bit(vector, 0);
-}
-
-static unsigned int startup_msi_irq_wo_maskbit(unsigned int vector)
-{
- struct msi_desc *entry;
- unsigned long flags;
-
- spin_lock_irqsave(&msi_lock, flags);
- entry = msi_desc[vector];
- if (!entry || !entry->dev) {
- spin_unlock_irqrestore(&msi_lock, flags);
- return 0;
+ default:
+ BUG();
}
- entry->msi_attrib.state = 1; /* Mark it active */
- spin_unlock_irqrestore(&msi_lock, flags);
-
- return 0; /* never anything pending */
-}
-
-static unsigned int startup_msi_irq_w_maskbit(unsigned int vector)
-{
- startup_msi_irq_wo_maskbit(vector);
- unmask_MSI_irq(vector);
- return 0; /* never anything pending */
-}
-
-static void shutdown_msi_irq(unsigned int vector)
-{
- struct msi_desc *entry;
- unsigned long flags;
-
- spin_lock_irqsave(&msi_lock, flags);
- entry = msi_desc[vector];
- if (entry && entry->dev)
- entry->msi_attrib.state = 0; /* Mark it not active */
- spin_unlock_irqrestore(&msi_lock, flags);
-}
-
-static void end_msi_irq_wo_maskbit(unsigned int vector)
-{
- move_native_irq(vector);
- ack_APIC_irq();
-}
-
-static void end_msi_irq_w_maskbit(unsigned int vector)
-{
- move_native_irq(vector);
- unmask_MSI_irq(vector);
- ack_APIC_irq();
-}
-
-static void do_nothing(unsigned int vector)
-{
}
-/*
- * Interrupt Type for MSI-X PCI/PCI-X/PCI-Express Devices,
- * which implement the MSI-X Capability Structure.
- */
-static struct hw_interrupt_type msix_irq_type = {
- .typename = "PCI-MSI-X",
- .startup = startup_msi_irq_w_maskbit,
- .shutdown = shutdown_msi_irq,
- .enable = unmask_MSI_irq,
- .disable = mask_MSI_irq,
- .ack = mask_MSI_irq,
- .end = end_msi_irq_w_maskbit,
- .set_affinity = set_msi_affinity
-};
-
-/*
- * Interrupt Type for MSI PCI/PCI-X/PCI-Express Devices,
- * which implement the MSI Capability Structure with
- * Mask-and-Pending Bits.
- */
-static struct hw_interrupt_type msi_irq_w_maskbit_type = {
- .typename = "PCI-MSI",
- .startup = startup_msi_irq_w_maskbit,
- .shutdown = shutdown_msi_irq,
- .enable = unmask_MSI_irq,
- .disable = mask_MSI_irq,
- .ack = mask_MSI_irq,
- .end = end_msi_irq_w_maskbit,
- .set_affinity = set_msi_affinity
-};
-
-/*
- * Interrupt Type for MSI PCI/PCI-X/PCI-Express Devices,
- * which implement the MSI Capability Structure without
- * Mask-and-Pending Bits.
- */
-static struct hw_interrupt_type msi_irq_wo_maskbit_type = {
- .typename = "PCI-MSI",
- .startup = startup_msi_irq_wo_maskbit,
- .shutdown = shutdown_msi_irq,
- .enable = do_nothing,
- .disable = do_nothing,
- .ack = do_nothing,
- .end = end_msi_irq_wo_maskbit,
- .set_affinity = set_msi_affinity
-};
-
-static int msi_free_vector(struct pci_dev* dev, int vector, int reassign);
-static int assign_msi_vector(void)
+void mask_msi_irq(unsigned int irq)
{
- static int new_vector_avail = 1;
- int vector;
- unsigned long flags;
-
- /*
- * msi_lock is provided to ensure that successful allocation of MSI
- * vector is assigned unique among drivers.
- */
- spin_lock_irqsave(&msi_lock, flags);
-
- if (!new_vector_avail) {
- int free_vector = 0;
-
- /*
- * vector_irq[] = -1 indicates that this specific vector is:
- * - assigned for MSI (since MSI have no associated IRQ) or
- * - assigned for legacy if less than 16, or
- * - having no corresponding 1:1 vector-to-IOxAPIC IRQ mapping
- * vector_irq[] = 0 indicates that this vector, previously
- * assigned for MSI, is freed by hotplug removed operations.
- * This vector will be reused for any subsequent hotplug added
- * operations.
- * vector_irq[] > 0 indicates that this vector is assigned for
- * IOxAPIC IRQs. This vector and its value provides a 1-to-1
- * vector-to-IOxAPIC IRQ mapping.
- */
- for (vector = FIRST_DEVICE_VECTOR; vector < NR_IRQS; vector++) {
- if (vector_irq[vector] != 0)
- continue;
- free_vector = vector;
- if (!msi_desc[vector])
- break;
- else
- continue;
- }
- if (!free_vector) {
- spin_unlock_irqrestore(&msi_lock, flags);
- return -EBUSY;
- }
- vector_irq[free_vector] = -1;
- nr_released_vectors--;
- spin_unlock_irqrestore(&msi_lock, flags);
- if (msi_desc[free_vector] != NULL) {
- struct pci_dev *dev;
- int tail;
-
- /* free all linked vectors before re-assign */
- do {
- spin_lock_irqsave(&msi_lock, flags);
- dev = msi_desc[free_vector]->dev;
- tail = msi_desc[free_vector]->link.tail;
- spin_unlock_irqrestore(&msi_lock, flags);
- msi_free_vector(dev, tail, 1);
- } while (free_vector != tail);
- }
-
- return free_vector;
- }
- vector = assign_irq_vector(AUTO_ASSIGN);
- last_alloc_vector = vector;
- if (vector == LAST_DEVICE_VECTOR)
- new_vector_avail = 0;
-
- spin_unlock_irqrestore(&msi_lock, flags);
- return vector;
+ msi_set_mask_bit(irq, 1);
}
-static int get_new_vector(void)
+void unmask_msi_irq(unsigned int irq)
{
- int vector = assign_msi_vector();
-
- if (vector > 0)
- set_intr_gate(vector, interrupt[vector]);
-
- return vector;
+ msi_set_mask_bit(irq, 0);
}
+static int msi_free_irq(struct pci_dev* dev, int irq);
static int msi_init(void)
{
static int status = -ENOMEM;
@@ -358,22 +176,6 @@ static int msi_init(void)
return status;
}
- status = msi_arch_init();
- if (status < 0) {
- pci_msi_enable = 0;
- printk(KERN_WARNING
- "PCI: MSI arch init failed. MSI disabled.\n");
- return status;
- }
-
- if (! msi_ops) {
- printk(KERN_WARNING
- "PCI: MSI ops not registered. MSI disabled.\n");
- status = -EINVAL;
- return status;
- }
-
- last_alloc_vector = assign_irq_vector(AUTO_ASSIGN);
status = msi_cache_init();
if (status < 0) {
pci_msi_enable = 0;
@@ -381,61 +183,61 @@ static int msi_init(void)
return status;
}
- if (last_alloc_vector < 0) {
- pci_msi_enable = 0;
- printk(KERN_WARNING "PCI: No interrupt vectors available for MSI\n");
- status = -EBUSY;
- return status;
- }
- vector_irq[last_alloc_vector] = 0;
- nr_released_vectors++;
-
return status;
}
-static int get_msi_vector(struct pci_dev *dev)
-{
- return get_new_vector();
-}
-
static struct msi_desc* alloc_msi_entry(void)
{
struct msi_desc *entry;
- entry = kmem_cache_alloc(msi_cachep, SLAB_KERNEL);
+ entry = kmem_cache_zalloc(msi_cachep, GFP_KERNEL);
if (!entry)
return NULL;
- memset(entry, 0, sizeof(struct msi_desc));
entry->link.tail = entry->link.head = 0; /* single message */
entry->dev = NULL;
return entry;
}
-static void attach_msi_entry(struct msi_desc *entry, int vector)
+static void attach_msi_entry(struct msi_desc *entry, int irq)
{
unsigned long flags;
spin_lock_irqsave(&msi_lock, flags);
- msi_desc[vector] = entry;
+ msi_desc[irq] = entry;
spin_unlock_irqrestore(&msi_lock, flags);
}
-static void irq_handler_init(int cap_id, int pos, int mask)
+static int create_msi_irq(void)
{
- unsigned long flags;
+ struct msi_desc *entry;
+ int irq;
- spin_lock_irqsave(&irq_desc[pos].lock, flags);
- if (cap_id == PCI_CAP_ID_MSIX)
- irq_desc[pos].chip = &msix_irq_type;
- else {
- if (!mask)
- irq_desc[pos].chip = &msi_irq_wo_maskbit_type;
- else
- irq_desc[pos].chip = &msi_irq_w_maskbit_type;
+ entry = alloc_msi_entry();
+ if (!entry)
+ return -ENOMEM;
+
+ irq = create_irq();
+ if (irq < 0) {
+ kmem_cache_free(msi_cachep, entry);
+ return -EBUSY;
}
- spin_unlock_irqrestore(&irq_desc[pos].lock, flags);
+
+ set_irq_data(irq, entry);
+
+ return irq;
+}
+
+static void destroy_msi_irq(unsigned int irq)
+{
+ struct msi_desc *entry;
+
+ entry = get_irq_data(irq);
+ set_irq_chip(irq, NULL);
+ set_irq_data(irq, NULL);
+ destroy_irq(irq);
+ kmem_cache_free(msi_cachep, entry);
}
static void enable_msi_mode(struct pci_dev *dev, int pos, int type)
@@ -453,10 +255,8 @@ static void enable_msi_mode(struct pci_dev *dev, int pos, int type)
pci_write_config_word(dev, msi_control_reg(pos), control);
dev->msix_enabled = 1;
}
- if (pci_find_capability(dev, PCI_CAP_ID_EXP)) {
- /* PCI Express Endpoint device detected */
- pci_intx(dev, 0); /* disable intx */
- }
+
+ pci_intx(dev, 0); /* disable intx */
}
void disable_msi_mode(struct pci_dev *dev, int pos, int type)
@@ -474,27 +274,25 @@ void disable_msi_mode(struct pci_dev *dev, int pos, int type)
pci_write_config_word(dev, msi_control_reg(pos), control);
dev->msix_enabled = 0;
}
- if (pci_find_capability(dev, PCI_CAP_ID_EXP)) {
- /* PCI Express Endpoint device detected */
- pci_intx(dev, 1); /* enable intx */
- }
+
+ pci_intx(dev, 1); /* enable intx */
}
-static int msi_lookup_vector(struct pci_dev *dev, int type)
+static int msi_lookup_irq(struct pci_dev *dev, int type)
{
- int vector;
+ int irq;
unsigned long flags;
spin_lock_irqsave(&msi_lock, flags);
- for (vector = FIRST_DEVICE_VECTOR; vector < NR_IRQS; vector++) {
- if (!msi_desc[vector] || msi_desc[vector]->dev != dev ||
- msi_desc[vector]->msi_attrib.type != type ||
- msi_desc[vector]->msi_attrib.default_vector != dev->irq)
+ for (irq = 0; irq < NR_IRQS; irq++) {
+ if (!msi_desc[irq] || msi_desc[irq]->dev != dev ||
+ msi_desc[irq]->msi_attrib.type != type ||
+ msi_desc[irq]->msi_attrib.default_irq != dev->irq)
continue;
spin_unlock_irqrestore(&msi_lock, flags);
- /* This pre-assigned MSI vector for this device
- already exits. Override dev->irq with this vector */
- dev->irq = vector;
+ /* This pre-assigned MSI irq for this device
+ already exits. Override dev->irq with this irq */
+ dev->irq = irq;
return 0;
}
spin_unlock_irqrestore(&msi_lock, flags);
@@ -506,11 +304,6 @@ void pci_scan_msi_device(struct pci_dev *dev)
{
if (!dev)
return;
-
- if (pci_find_capability(dev, PCI_CAP_ID_MSIX) > 0)
- nr_msix_devices++;
- else if (pci_find_capability(dev, PCI_CAP_ID_MSI) > 0)
- nr_reserved_vectors++;
}
#ifdef CONFIG_PM
@@ -584,7 +377,7 @@ int pci_save_msix_state(struct pci_dev *dev)
{
int pos;
int temp;
- int vector, head, tail = 0;
+ int irq, head, tail = 0;
u16 control;
struct pci_cap_saved_state *save_state;
@@ -606,33 +399,20 @@ int pci_save_msix_state(struct pci_dev *dev)
/* save the table */
temp = dev->irq;
- if (msi_lookup_vector(dev, PCI_CAP_ID_MSIX)) {
+ if (msi_lookup_irq(dev, PCI_CAP_ID_MSIX)) {
kfree(save_state);
return -EINVAL;
}
- vector = head = dev->irq;
+ irq = head = dev->irq;
while (head != tail) {
- int j;
- void __iomem *base;
struct msi_desc *entry;
- entry = msi_desc[vector];
- base = entry->mask_base;
- j = entry->msi_attrib.entry_nr;
-
- entry->address_lo_save =
- readl(base + j * PCI_MSIX_ENTRY_SIZE +
- PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET);
- entry->address_hi_save =
- readl(base + j * PCI_MSIX_ENTRY_SIZE +
- PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET);
- entry->data_save =
- readl(base + j * PCI_MSIX_ENTRY_SIZE +
- PCI_MSIX_ENTRY_DATA_OFFSET);
-
- tail = msi_desc[vector]->link.tail;
- vector = tail;
+ entry = msi_desc[irq];
+ read_msi_msg(irq, &entry->msg_save);
+
+ tail = msi_desc[irq]->link.tail;
+ irq = tail;
}
dev->irq = temp;
@@ -645,9 +425,7 @@ void pci_restore_msix_state(struct pci_dev *dev)
{
u16 save;
int pos;
- int vector, head, tail = 0;
- void __iomem *base;
- int j;
+ int irq, head, tail = 0;
struct msi_desc *entry;
int temp;
struct pci_cap_saved_state *save_state;
@@ -665,26 +443,15 @@ void pci_restore_msix_state(struct pci_dev *dev)
/* route the table */
temp = dev->irq;
- if (msi_lookup_vector(dev, PCI_CAP_ID_MSIX))
+ if (msi_lookup_irq(dev, PCI_CAP_ID_MSIX))
return;
- vector = head = dev->irq;
+ irq = head = dev->irq;
while (head != tail) {
- entry = msi_desc[vector];
- base = entry->mask_base;
- j = entry->msi_attrib.entry_nr;
-
- writel(entry->address_lo_save,
- base + j * PCI_MSIX_ENTRY_SIZE +
- PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET);
- writel(entry->address_hi_save,
- base + j * PCI_MSIX_ENTRY_SIZE +
- PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET);
- writel(entry->data_save,
- base + j * PCI_MSIX_ENTRY_SIZE +
- PCI_MSIX_ENTRY_DATA_OFFSET);
-
- tail = msi_desc[vector]->link.tail;
- vector = tail;
+ entry = msi_desc[irq];
+ write_msi_msg(irq, &entry->msg_save);
+
+ tail = msi_desc[irq]->link.tail;
+ irq = tail;
}
dev->irq = temp;
@@ -693,104 +460,68 @@ void pci_restore_msix_state(struct pci_dev *dev)
}
#endif
-static int msi_register_init(struct pci_dev *dev, struct msi_desc *entry)
-{
- int status;
- u32 address_hi;
- u32 address_lo;
- u32 data;
- int pos, vector = dev->irq;
- u16 control;
-
- pos = pci_find_capability(dev, PCI_CAP_ID_MSI);
- pci_read_config_word(dev, msi_control_reg(pos), &control);
-
- /* Configure MSI capability structure */
- status = msi_ops->setup(dev, vector, &address_hi, &address_lo, &data);
- if (status < 0)
- return status;
-
- pci_write_config_dword(dev, msi_lower_address_reg(pos), address_lo);
- if (is_64bit_address(control)) {
- pci_write_config_dword(dev,
- msi_upper_address_reg(pos), address_hi);
- pci_write_config_word(dev,
- msi_data_reg(pos, 1), data);
- } else
- pci_write_config_word(dev,
- msi_data_reg(pos, 0), data);
- if (entry->msi_attrib.maskbit) {
- unsigned int maskbits, temp;
- /* All MSIs are unmasked by default, Mask them all */
- pci_read_config_dword(dev,
- msi_mask_bits_reg(pos, is_64bit_address(control)),
- &maskbits);
- temp = (1 << multi_msi_capable(control));
- temp = ((temp - 1) & ~temp);
- maskbits |= temp;
- pci_write_config_dword(dev,
- msi_mask_bits_reg(pos, is_64bit_address(control)),
- maskbits);
- }
-
- return 0;
-}
-
/**
* msi_capability_init - configure device's MSI capability structure
* @dev: pointer to the pci_dev data structure of MSI device function
*
* Setup the MSI capability structure of device function with a single
- * MSI vector, regardless of device function is capable of handling
+ * MSI irq, regardless of device function is capable of handling
* multiple messages. A return of zero indicates the successful setup
- * of an entry zero with the new MSI vector or non-zero for otherwise.
+ * of an entry zero with the new MSI irq or non-zero for otherwise.
**/
static int msi_capability_init(struct pci_dev *dev)
{
int status;
struct msi_desc *entry;
- int pos, vector;
+ int pos, irq;
u16 control;
pos = pci_find_capability(dev, PCI_CAP_ID_MSI);
pci_read_config_word(dev, msi_control_reg(pos), &control);
/* MSI Entry Initialization */
- entry = alloc_msi_entry();
- if (!entry)
- return -ENOMEM;
+ irq = create_msi_irq();
+ if (irq < 0)
+ return irq;
- vector = get_msi_vector(dev);
- if (vector < 0) {
- kmem_cache_free(msi_cachep, entry);
- return -EBUSY;
- }
- entry->link.head = vector;
- entry->link.tail = vector;
+ entry = get_irq_data(irq);
+ entry->link.head = irq;
+ entry->link.tail = irq;
entry->msi_attrib.type = PCI_CAP_ID_MSI;
- entry->msi_attrib.state = 0; /* Mark it not active */
+ entry->msi_attrib.is_64 = is_64bit_address(control);
entry->msi_attrib.entry_nr = 0;
entry->msi_attrib.maskbit = is_mask_bit_support(control);
- entry->msi_attrib.default_vector = dev->irq; /* Save IOAPIC IRQ */
- dev->irq = vector;
- entry->dev = dev;
+ entry->msi_attrib.default_irq = dev->irq; /* Save IOAPIC IRQ */
+ entry->msi_attrib.pos = pos;
if (is_mask_bit_support(control)) {
entry->mask_base = (void __iomem *)(long)msi_mask_bits_reg(pos,
is_64bit_address(control));
}
- /* Replace with MSI handler */
- irq_handler_init(PCI_CAP_ID_MSI, vector, entry->msi_attrib.maskbit);
+ entry->dev = dev;
+ if (entry->msi_attrib.maskbit) {
+ unsigned int maskbits, temp;
+ /* All MSIs are unmasked by default, Mask them all */
+ pci_read_config_dword(dev,
+ msi_mask_bits_reg(pos, is_64bit_address(control)),
+ &maskbits);
+ temp = (1 << multi_msi_capable(control));
+ temp = ((temp - 1) & ~temp);
+ maskbits |= temp;
+ pci_write_config_dword(dev,
+ msi_mask_bits_reg(pos, is_64bit_address(control)),
+ maskbits);
+ }
/* Configure MSI capability structure */
- status = msi_register_init(dev, entry);
- if (status != 0) {
- dev->irq = entry->msi_attrib.default_vector;
- kmem_cache_free(msi_cachep, entry);
+ status = arch_setup_msi_irq(irq, dev);
+ if (status < 0) {
+ destroy_msi_irq(irq);
return status;
}
- attach_msi_entry(entry, vector);
+ attach_msi_entry(entry, irq);
/* Set MSI enabled bits */
enable_msi_mode(dev, pos, PCI_CAP_ID_MSI);
+ dev->irq = irq;
return 0;
}
@@ -801,18 +532,15 @@ static int msi_capability_init(struct pci_dev *dev)
* @nvec: number of @entries
*
* Setup the MSI-X capability structure of device function with a
- * single MSI-X vector. A return of zero indicates the successful setup of
- * requested MSI-X entries with allocated vectors or non-zero for otherwise.
+ * single MSI-X irq. A return of zero indicates the successful setup of
+ * requested MSI-X entries with allocated irqs or non-zero for otherwise.
**/
static int msix_capability_init(struct pci_dev *dev,
struct msix_entry *entries, int nvec)
{
struct msi_desc *head = NULL, *tail = NULL, *entry = NULL;
- u32 address_hi;
- u32 address_lo;
- u32 data;
int status;
- int vector, pos, i, j, nr_entries, temp = 0;
+ int irq, pos, i, j, nr_entries, temp = 0;
unsigned long phys_addr;
u32 table_offset;
u16 control;
@@ -834,65 +562,56 @@ static int msix_capability_init(struct pci_dev *dev,
/* MSI-X Table Initialization */
for (i = 0; i < nvec; i++) {
- entry = alloc_msi_entry();
- if (!entry)
- break;
- vector = get_msi_vector(dev);
- if (vector < 0) {
- kmem_cache_free(msi_cachep, entry);
+ irq = create_msi_irq();
+ if (irq < 0)
break;
- }
+ entry = get_irq_data(irq);
j = entries[i].entry;
- entries[i].vector = vector;
+ entries[i].vector = irq;
entry->msi_attrib.type = PCI_CAP_ID_MSIX;
- entry->msi_attrib.state = 0; /* Mark it not active */
+ entry->msi_attrib.is_64 = 1;
entry->msi_attrib.entry_nr = j;
entry->msi_attrib.maskbit = 1;
- entry->msi_attrib.default_vector = dev->irq;
+ entry->msi_attrib.default_irq = dev->irq;
+ entry->msi_attrib.pos = pos;
entry->dev = dev;
entry->mask_base = base;
if (!head) {
- entry->link.head = vector;
- entry->link.tail = vector;
+ entry->link.head = irq;
+ entry->link.tail = irq;
head = entry;
} else {
entry->link.head = temp;
entry->link.tail = tail->link.tail;
- tail->link.tail = vector;
- head->link.head = vector;
+ tail->link.tail = irq;
+ head->link.head = irq;
}
- temp = vector;
+ temp = irq;
tail = entry;
- /* Replace with MSI-X handler */
- irq_handler_init(PCI_CAP_ID_MSIX, vector, 1);
/* Configure MSI-X capability structure */
- status = msi_ops->setup(dev, vector,
- &address_hi,
- &address_lo,
- &data);
- if (status < 0)
+ status = arch_setup_msi_irq(irq, dev);
+ if (status < 0) {
+ destroy_msi_irq(irq);
break;
+ }
- writel(address_lo,
- base + j * PCI_MSIX_ENTRY_SIZE +
- PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET);
- writel(address_hi,
- base + j * PCI_MSIX_ENTRY_SIZE +
- PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET);
- writel(data,
- base + j * PCI_MSIX_ENTRY_SIZE +
- PCI_MSIX_ENTRY_DATA_OFFSET);
- attach_msi_entry(entry, vector);
+ attach_msi_entry(entry, irq);
}
if (i != nvec) {
+ int avail = i - 1;
i--;
for (; i >= 0; i--) {
- vector = (entries + i)->vector;
- msi_free_vector(dev, vector, 0);
+ irq = (entries + i)->vector;
+ msi_free_irq(dev, irq);
(entries + i)->vector = 0;
}
- return -EBUSY;
+ /* If we had some success report the number of irqs
+ * we succeeded in setting up.
+ */
+ if (avail <= 0)
+ avail = -EBUSY;
+ return avail;
}
/* Set MSI-X enabled bits */
enable_msi_mode(dev, pos, PCI_CAP_ID_MSIX);
@@ -901,31 +620,51 @@ static int msix_capability_init(struct pci_dev *dev,
}
/**
- * pci_enable_msi - configure device's MSI capability structure
+ * pci_msi_supported - check whether MSI may be enabled on device
* @dev: pointer to the pci_dev data structure of MSI device function
*
- * Setup the MSI capability structure of device function with
- * a single MSI vector upon its software driver call to request for
- * MSI mode enabled on its hardware device function. A return of zero
- * indicates the successful setup of an entry zero with the new MSI
- * vector or non-zero for otherwise.
+ * Look at global flags, the device itself, and its parent busses
+ * to return 0 if MSI are supported for the device.
**/
-int pci_enable_msi(struct pci_dev* dev)
+static
+int pci_msi_supported(struct pci_dev * dev)
{
struct pci_bus *bus;
- int pos, temp, status = -EINVAL;
- u16 control;
-
- if (!pci_msi_enable || !dev)
- return status;
- if (dev->no_msi)
- return status;
+ /* MSI must be globally enabled and supported by the device */
+ if (!pci_msi_enable || !dev || dev->no_msi)
+ return -EINVAL;
+ /* Any bridge which does NOT route MSI transactions from it's
+ * secondary bus to it's primary bus must set NO_MSI flag on
+ * the secondary pci_bus.
+ * We expect only arch-specific PCI host bus controller driver
+ * or quirks for specific PCI bridges to be setting NO_MSI.
+ */
for (bus = dev->bus; bus; bus = bus->parent)
if (bus->bus_flags & PCI_BUS_FLAGS_NO_MSI)
return -EINVAL;
+ return 0;
+}
+
+/**
+ * pci_enable_msi - configure device's MSI capability structure
+ * @dev: pointer to the pci_dev data structure of MSI device function
+ *
+ * Setup the MSI capability structure of device function with
+ * a single MSI irq upon its software driver call to request for
+ * MSI mode enabled on its hardware device function. A return of zero
+ * indicates the successful setup of an entry zero with the new MSI
+ * irq or non-zero for otherwise.
+ **/
+int pci_enable_msi(struct pci_dev* dev)
+{
+ int pos, temp, status;
+
+ if (pci_msi_supported(dev) < 0)
+ return -EINVAL;
+
temp = dev->irq;
status = msi_init();
@@ -936,52 +675,25 @@ int pci_enable_msi(struct pci_dev* dev)
if (!pos)
return -EINVAL;
- if (!msi_lookup_vector(dev, PCI_CAP_ID_MSI)) {
- /* Lookup Sucess */
- unsigned long flags;
+ WARN_ON(!msi_lookup_irq(dev, PCI_CAP_ID_MSI));
- pci_read_config_word(dev, msi_control_reg(pos), &control);
- if (control & PCI_MSI_FLAGS_ENABLE)
- return 0; /* Already in MSI mode */
- spin_lock_irqsave(&msi_lock, flags);
- if (!vector_irq[dev->irq]) {
- msi_desc[dev->irq]->msi_attrib.state = 0;
- vector_irq[dev->irq] = -1;
- nr_released_vectors--;
- spin_unlock_irqrestore(&msi_lock, flags);
- status = msi_register_init(dev, msi_desc[dev->irq]);
- if (status == 0)
- enable_msi_mode(dev, pos, PCI_CAP_ID_MSI);
- return status;
- }
- spin_unlock_irqrestore(&msi_lock, flags);
- dev->irq = temp;
- }
- /* Check whether driver already requested for MSI-X vectors */
+ /* Check whether driver already requested for MSI-X irqs */
pos = pci_find_capability(dev, PCI_CAP_ID_MSIX);
- if (pos > 0 && !msi_lookup_vector(dev, PCI_CAP_ID_MSIX)) {
+ if (pos > 0 && !msi_lookup_irq(dev, PCI_CAP_ID_MSIX)) {
printk(KERN_INFO "PCI: %s: Can't enable MSI. "
- "Device already has MSI-X vectors assigned\n",
+ "Device already has MSI-X irq assigned\n",
pci_name(dev));
dev->irq = temp;
return -EINVAL;
}
status = msi_capability_init(dev);
- if (!status) {
- if (!pos)
- nr_reserved_vectors--; /* Only MSI capable */
- else if (nr_msix_devices > 0)
- nr_msix_devices--; /* Both MSI and MSI-X capable,
- but choose enabling MSI */
- }
-
return status;
}
void pci_disable_msi(struct pci_dev* dev)
{
struct msi_desc *entry;
- int pos, default_vector;
+ int pos, default_irq;
u16 control;
unsigned long flags;
@@ -998,41 +710,41 @@ void pci_disable_msi(struct pci_dev* dev)
if (!(control & PCI_MSI_FLAGS_ENABLE))
return;
+ disable_msi_mode(dev, pos, PCI_CAP_ID_MSI);
+
spin_lock_irqsave(&msi_lock, flags);
entry = msi_desc[dev->irq];
if (!entry || !entry->dev || entry->msi_attrib.type != PCI_CAP_ID_MSI) {
spin_unlock_irqrestore(&msi_lock, flags);
return;
}
- if (entry->msi_attrib.state) {
+ if (irq_has_action(dev->irq)) {
spin_unlock_irqrestore(&msi_lock, flags);
printk(KERN_WARNING "PCI: %s: pci_disable_msi() called without "
- "free_irq() on MSI vector %d\n",
+ "free_irq() on MSI irq %d\n",
pci_name(dev), dev->irq);
- BUG_ON(entry->msi_attrib.state > 0);
+ BUG_ON(irq_has_action(dev->irq));
} else {
- vector_irq[dev->irq] = 0; /* free it */
- nr_released_vectors++;
- default_vector = entry->msi_attrib.default_vector;
+ default_irq = entry->msi_attrib.default_irq;
spin_unlock_irqrestore(&msi_lock, flags);
- /* Restore dev->irq to its default pin-assertion vector */
- dev->irq = default_vector;
- disable_msi_mode(dev, pci_find_capability(dev, PCI_CAP_ID_MSI),
- PCI_CAP_ID_MSI);
+ msi_free_irq(dev, dev->irq);
+
+ /* Restore dev->irq to its default pin-assertion irq */
+ dev->irq = default_irq;
}
}
-static int msi_free_vector(struct pci_dev* dev, int vector, int reassign)
+static int msi_free_irq(struct pci_dev* dev, int irq)
{
struct msi_desc *entry;
int head, entry_nr, type;
void __iomem *base;
unsigned long flags;
- msi_ops->teardown(vector);
+ arch_teardown_msi_irq(irq);
spin_lock_irqsave(&msi_lock, flags);
- entry = msi_desc[vector];
+ entry = msi_desc[irq];
if (!entry || entry->dev != dev) {
spin_unlock_irqrestore(&msi_lock, flags);
return -EINVAL;
@@ -1044,112 +756,46 @@ static int msi_free_vector(struct pci_dev* dev, int vector, int reassign)
msi_desc[entry->link.head]->link.tail = entry->link.tail;
msi_desc[entry->link.tail]->link.head = entry->link.head;
entry->dev = NULL;
- if (!reassign) {
- vector_irq[vector] = 0;
- nr_released_vectors++;
- }
- msi_desc[vector] = NULL;
+ msi_desc[irq] = NULL;
spin_unlock_irqrestore(&msi_lock, flags);
- kmem_cache_free(msi_cachep, entry);
+ destroy_msi_irq(irq);
if (type == PCI_CAP_ID_MSIX) {
- if (!reassign)
- writel(1, base +
- entry_nr * PCI_MSIX_ENTRY_SIZE +
- PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET);
+ writel(1, base + entry_nr * PCI_MSIX_ENTRY_SIZE +
+ PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET);
- if (head == vector)
+ if (head == irq)
iounmap(base);
}
return 0;
}
-static int reroute_msix_table(int head, struct msix_entry *entries, int *nvec)
-{
- int vector = head, tail = 0;
- int i, j = 0, nr_entries = 0;
- void __iomem *base;
- unsigned long flags;
-
- spin_lock_irqsave(&msi_lock, flags);
- while (head != tail) {
- nr_entries++;
- tail = msi_desc[vector]->link.tail;
- if (entries[0].entry == msi_desc[vector]->msi_attrib.entry_nr)
- j = vector;
- vector = tail;
- }
- if (*nvec > nr_entries) {
- spin_unlock_irqrestore(&msi_lock, flags);
- *nvec = nr_entries;
- return -EINVAL;
- }
- vector = ((j > 0) ? j : head);
- for (i = 0; i < *nvec; i++) {
- j = msi_desc[vector]->msi_attrib.entry_nr;
- msi_desc[vector]->msi_attrib.state = 0; /* Mark it not active */
- vector_irq[vector] = -1; /* Mark it busy */
- nr_released_vectors--;
- entries[i].vector = vector;
- if (j != (entries + i)->entry) {
- base = msi_desc[vector]->mask_base;
- msi_desc[vector]->msi_attrib.entry_nr =
- (entries + i)->entry;
- writel( readl(base + j * PCI_MSIX_ENTRY_SIZE +
- PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET), base +
- (entries + i)->entry * PCI_MSIX_ENTRY_SIZE +
- PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET);
- writel( readl(base + j * PCI_MSIX_ENTRY_SIZE +
- PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET), base +
- (entries + i)->entry * PCI_MSIX_ENTRY_SIZE +
- PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET);
- writel( (readl(base + j * PCI_MSIX_ENTRY_SIZE +
- PCI_MSIX_ENTRY_DATA_OFFSET) & 0xff00) | vector,
- base + (entries+i)->entry*PCI_MSIX_ENTRY_SIZE +
- PCI_MSIX_ENTRY_DATA_OFFSET);
- }
- vector = msi_desc[vector]->link.tail;
- }
- spin_unlock_irqrestore(&msi_lock, flags);
-
- return 0;
-}
-
/**
* pci_enable_msix - configure device's MSI-X capability structure
* @dev: pointer to the pci_dev data structure of MSI-X device function
* @entries: pointer to an array of MSI-X entries
- * @nvec: number of MSI-X vectors requested for allocation by device driver
+ * @nvec: number of MSI-X irqs requested for allocation by device driver
*
* Setup the MSI-X capability structure of device function with the number
- * of requested vectors upon its software driver call to request for
+ * of requested irqs upon its software driver call to request for
* MSI-X mode enabled on its hardware device function. A return of zero
* indicates the successful configuration of MSI-X capability structure
- * with new allocated MSI-X vectors. A return of < 0 indicates a failure.
+ * with new allocated MSI-X irqs. A return of < 0 indicates a failure.
* Or a return of > 0 indicates that driver request is exceeding the number
- * of vectors available. Driver should use the returned value to re-send
+ * of irqs available. Driver should use the returned value to re-send
* its request.
**/
int pci_enable_msix(struct pci_dev* dev, struct msix_entry *entries, int nvec)
{
- struct pci_bus *bus;
- int status, pos, nr_entries, free_vectors;
+ int status, pos, nr_entries;
int i, j, temp;
u16 control;
- unsigned long flags;
- if (!pci_msi_enable || !dev || !entries)
+ if (!entries || pci_msi_supported(dev) < 0)
return -EINVAL;
- if (dev->no_msi)
- return -EINVAL;
-
- for (bus = dev->bus; bus; bus = bus->parent)
- if (bus->bus_flags & PCI_BUS_FLAGS_NO_MSI)
- return -EINVAL;
-
status = msi_init();
if (status < 0)
return status;
@@ -1159,9 +805,6 @@ int pci_enable_msix(struct pci_dev* dev, struct msix_entry *entries, int nvec)
return -EINVAL;
pci_read_config_word(dev, msi_control_reg(pos), &control);
- if (control & PCI_MSIX_FLAGS_ENABLE)
- return -EINVAL; /* Already in MSI-X mode */
-
nr_entries = multi_msix_capable(control);
if (nvec > nr_entries)
return -EINVAL;
@@ -1176,56 +819,18 @@ int pci_enable_msix(struct pci_dev* dev, struct msix_entry *entries, int nvec)
}
}
temp = dev->irq;
- if (!msi_lookup_vector(dev, PCI_CAP_ID_MSIX)) {
- /* Lookup Sucess */
- nr_entries = nvec;
- /* Reroute MSI-X table */
- if (reroute_msix_table(dev->irq, entries, &nr_entries)) {
- /* #requested > #previous-assigned */
- dev->irq = temp;
- return nr_entries;
- }
- dev->irq = temp;
- enable_msi_mode(dev, pos, PCI_CAP_ID_MSIX);
- return 0;
- }
- /* Check whether driver already requested for MSI vector */
+ WARN_ON(!msi_lookup_irq(dev, PCI_CAP_ID_MSIX));
+
+ /* Check whether driver already requested for MSI irq */
if (pci_find_capability(dev, PCI_CAP_ID_MSI) > 0 &&
- !msi_lookup_vector(dev, PCI_CAP_ID_MSI)) {
+ !msi_lookup_irq(dev, PCI_CAP_ID_MSI)) {
printk(KERN_INFO "PCI: %s: Can't enable MSI-X. "
- "Device already has an MSI vector assigned\n",
+ "Device already has an MSI irq assigned\n",
pci_name(dev));
dev->irq = temp;
return -EINVAL;
}
-
- spin_lock_irqsave(&msi_lock, flags);
- /*
- * msi_lock is provided to ensure that enough vectors resources are
- * available before granting.
- */
- free_vectors = pci_vector_resources(last_alloc_vector,
- nr_released_vectors);
- /* Ensure that each MSI/MSI-X device has one vector reserved by
- default to avoid any MSI-X driver to take all available
- resources */
- free_vectors -= nr_reserved_vectors;
- /* Find the average of free vectors among MSI-X devices */
- if (nr_msix_devices > 0)
- free_vectors /= nr_msix_devices;
- spin_unlock_irqrestore(&msi_lock, flags);
-
- if (nvec > free_vectors) {
- if (free_vectors > 0)
- return free_vectors;
- else
- return -EBUSY;
- }
-
status = msix_capability_init(dev, entries, nvec);
- if (!status && nr_msix_devices > 0)
- nr_msix_devices--;
-
return status;
}
@@ -1247,53 +852,47 @@ void pci_disable_msix(struct pci_dev* dev)
if (!(control & PCI_MSIX_FLAGS_ENABLE))
return;
+ disable_msi_mode(dev, pos, PCI_CAP_ID_MSIX);
+
temp = dev->irq;
- if (!msi_lookup_vector(dev, PCI_CAP_ID_MSIX)) {
- int state, vector, head, tail = 0, warning = 0;
+ if (!msi_lookup_irq(dev, PCI_CAP_ID_MSIX)) {
+ int irq, head, tail = 0, warning = 0;
unsigned long flags;
- vector = head = dev->irq;
- spin_lock_irqsave(&msi_lock, flags);
+ irq = head = dev->irq;
+ dev->irq = temp; /* Restore pin IRQ */
while (head != tail) {
- state = msi_desc[vector]->msi_attrib.state;
- if (state)
+ spin_lock_irqsave(&msi_lock, flags);
+ tail = msi_desc[irq]->link.tail;
+ spin_unlock_irqrestore(&msi_lock, flags);
+ if (irq_has_action(irq))
warning = 1;
- else {
- vector_irq[vector] = 0; /* free it */
- nr_released_vectors++;
- }
- tail = msi_desc[vector]->link.tail;
- vector = tail;
+ else if (irq != head) /* Release MSI-X irq */
+ msi_free_irq(dev, irq);
+ irq = tail;
}
- spin_unlock_irqrestore(&msi_lock, flags);
+ msi_free_irq(dev, irq);
if (warning) {
- dev->irq = temp;
printk(KERN_WARNING "PCI: %s: pci_disable_msix() called without "
- "free_irq() on all MSI-X vectors\n",
+ "free_irq() on all MSI-X irqs\n",
pci_name(dev));
BUG_ON(warning > 0);
- } else {
- dev->irq = temp;
- disable_msi_mode(dev,
- pci_find_capability(dev, PCI_CAP_ID_MSIX),
- PCI_CAP_ID_MSIX);
-
}
}
}
/**
- * msi_remove_pci_irq_vectors - reclaim MSI(X) vectors to unused state
+ * msi_remove_pci_irq_vectors - reclaim MSI(X) irqs to unused state
* @dev: pointer to the pci_dev data structure of MSI(X) device function
*
* Being called during hotplug remove, from which the device function
- * is hot-removed. All previous assigned MSI/MSI-X vectors, if
+ * is hot-removed. All previous assigned MSI/MSI-X irqs, if
* allocated for this device function, are reclaimed to unused state,
* which may be used later on.
**/
void msi_remove_pci_irq_vectors(struct pci_dev* dev)
{
- int state, pos, temp;
+ int pos, temp;
unsigned long flags;
if (!pci_msi_enable || !dev)
@@ -1301,42 +900,38 @@ void msi_remove_pci_irq_vectors(struct pci_dev* dev)
temp = dev->irq; /* Save IOAPIC IRQ */
pos = pci_find_capability(dev, PCI_CAP_ID_MSI);
- if (pos > 0 && !msi_lookup_vector(dev, PCI_CAP_ID_MSI)) {
- spin_lock_irqsave(&msi_lock, flags);
- state = msi_desc[dev->irq]->msi_attrib.state;
- spin_unlock_irqrestore(&msi_lock, flags);
- if (state) {
+ if (pos > 0 && !msi_lookup_irq(dev, PCI_CAP_ID_MSI)) {
+ if (irq_has_action(dev->irq)) {
printk(KERN_WARNING "PCI: %s: msi_remove_pci_irq_vectors() "
- "called without free_irq() on MSI vector %d\n",
+ "called without free_irq() on MSI irq %d\n",
pci_name(dev), dev->irq);
- BUG_ON(state > 0);
- } else /* Release MSI vector assigned to this device */
- msi_free_vector(dev, dev->irq, 0);
+ BUG_ON(irq_has_action(dev->irq));
+ } else /* Release MSI irq assigned to this device */
+ msi_free_irq(dev, dev->irq);
dev->irq = temp; /* Restore IOAPIC IRQ */
}
pos = pci_find_capability(dev, PCI_CAP_ID_MSIX);
- if (pos > 0 && !msi_lookup_vector(dev, PCI_CAP_ID_MSIX)) {
- int vector, head, tail = 0, warning = 0;
+ if (pos > 0 && !msi_lookup_irq(dev, PCI_CAP_ID_MSIX)) {
+ int irq, head, tail = 0, warning = 0;
void __iomem *base = NULL;
- vector = head = dev->irq;
+ irq = head = dev->irq;
while (head != tail) {
spin_lock_irqsave(&msi_lock, flags);
- state = msi_desc[vector]->msi_attrib.state;
- tail = msi_desc[vector]->link.tail;
- base = msi_desc[vector]->mask_base;
+ tail = msi_desc[irq]->link.tail;
+ base = msi_desc[irq]->mask_base;
spin_unlock_irqrestore(&msi_lock, flags);
- if (state)
+ if (irq_has_action(irq))
warning = 1;
- else if (vector != head) /* Release MSI-X vector */
- msi_free_vector(dev, vector, 0);
- vector = tail;
+ else if (irq != head) /* Release MSI-X irq */
+ msi_free_irq(dev, irq);
+ irq = tail;
}
- msi_free_vector(dev, vector, 0);
+ msi_free_irq(dev, irq);
if (warning) {
iounmap(base);
printk(KERN_WARNING "PCI: %s: msi_remove_pci_irq_vectors() "
- "called without free_irq() on all MSI-X vectors\n",
+ "called without free_irq() on all MSI-X irqs\n",
pci_name(dev));
BUG_ON(warning > 0);
}
diff --git a/drivers/pci/msi.h b/drivers/pci/msi.h
index 56951c39d3a3..3898f5237144 100644
--- a/drivers/pci/msi.h
+++ b/drivers/pci/msi.h
@@ -6,92 +6,6 @@
#ifndef MSI_H
#define MSI_H
-/*
- * MSI operation vector. Used by the msi core code (drivers/pci/msi.c)
- * to abstract platform-specific tasks relating to MSI address generation
- * and resource management.
- */
-struct msi_ops {
- /**
- * setup - generate an MSI bus address and data for a given vector
- * @pdev: PCI device context (in)
- * @vector: vector allocated by the msi core (in)
- * @addr_hi: upper 32 bits of PCI bus MSI address (out)
- * @addr_lo: lower 32 bits of PCI bus MSI address (out)
- * @data: MSI data payload (out)
- *
- * Description: The setup op is used to generate a PCI bus addres and
- * data which the msi core will program into the card MSI capability
- * registers. The setup routine is responsible for picking an initial
- * cpu to target the MSI at. The setup routine is responsible for
- * examining pdev to determine the MSI capabilities of the card and
- * generating a suitable address/data. The setup routine is
- * responsible for allocating and tracking any system resources it
- * needs to route the MSI to the cpu it picks, and for associating
- * those resources with the passed in vector.
- *
- * Returns 0 if the MSI address/data was successfully setup.
- **/
-
- int (*setup) (struct pci_dev *pdev, unsigned int vector,
- u32 *addr_hi, u32 *addr_lo, u32 *data);
-
- /**
- * teardown - release resources allocated by setup
- * @vector: vector context for resources (in)
- *
- * Description: The teardown op is used to release any resources
- * that were allocated in the setup routine associated with the passed
- * in vector.
- **/
-
- void (*teardown) (unsigned int vector);
-
- /**
- * target - retarget an MSI at a different cpu
- * @vector: vector context for resources (in)
- * @cpu: new cpu to direct vector at (in)
- * @addr_hi: new value of PCI bus upper 32 bits (in/out)
- * @addr_lo: new value of PCI bus lower 32 bits (in/out)
- *
- * Description: The target op is used to redirect an MSI vector
- * at a different cpu. addr_hi/addr_lo coming in are the existing
- * values that the MSI core has programmed into the card. The
- * target code is responsible for freeing any resources (if any)
- * associated with the old address, and generating a new PCI bus
- * addr_hi/addr_lo that will redirect the vector at the indicated cpu.
- **/
-
- void (*target) (unsigned int vector, unsigned int cpu,
- u32 *addr_hi, u32 *addr_lo);
-};
-
-extern int msi_register(struct msi_ops *ops);
-
-#include <asm/msi.h>
-
-/*
- * Assume the maximum number of hot plug slots supported by the system is about
- * ten. The worstcase is that each of these slots is hot-added with a device,
- * which has two MSI/MSI-X capable functions. To avoid any MSI-X driver, which
- * attempts to request all available vectors, NR_HP_RESERVED_VECTORS is defined
- * as below to ensure at least one message is assigned to each detected MSI/
- * MSI-X device function.
- */
-#define NR_HP_RESERVED_VECTORS 20
-
-extern int vector_irq[NR_VECTORS];
-extern void (*interrupt[NR_IRQS])(void);
-extern int pci_vector_resources(int last, int nr_released);
-
-/*
- * MSI-X Address Register
- */
-#define PCI_MSIX_FLAGS_QSIZE 0x7FF
-#define PCI_MSIX_FLAGS_ENABLE (1 << 15)
-#define PCI_MSIX_FLAGS_BIRMASK (7 << 0)
-#define PCI_MSIX_FLAGS_BITMASK (1 << 0)
-
#define PCI_MSIX_ENTRY_SIZE 16
#define PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET 0
#define PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET 4
@@ -110,8 +24,8 @@ extern int pci_vector_resources(int last, int nr_released);
(1 << ((control & PCI_MSI_FLAGS_QMASK) >> 1))
#define multi_msi_enable(control, num) \
control |= (((num >> 1) << 4) & PCI_MSI_FLAGS_QSIZE);
-#define is_64bit_address(control) (control & PCI_MSI_FLAGS_64BIT)
-#define is_mask_bit_support(control) (control & PCI_MSI_FLAGS_MASKBIT)
+#define is_64bit_address(control) (!!(control & PCI_MSI_FLAGS_64BIT))
+#define is_mask_bit_support(control) (!!(control & PCI_MSI_FLAGS_MASKBIT))
#define msi_enable(control, num) multi_msi_enable(control, num); \
control |= PCI_MSI_FLAGS_ENABLE
@@ -125,32 +39,4 @@ extern int pci_vector_resources(int last, int nr_released);
#define msix_mask(address) (address | PCI_MSIX_FLAGS_BITMASK)
#define msix_is_pending(address) (address & PCI_MSIX_FLAGS_PENDMASK)
-struct msi_desc {
- struct {
- __u8 type : 5; /* {0: unused, 5h:MSI, 11h:MSI-X} */
- __u8 maskbit : 1; /* mask-pending bit supported ? */
- __u8 state : 1; /* {0: free, 1: busy} */
- __u8 reserved: 1; /* reserved */
- __u8 entry_nr; /* specific enabled entry */
- __u8 default_vector; /* default pre-assigned vector */
- __u8 unused; /* formerly unused destination cpu*/
- }msi_attrib;
-
- struct {
- __u16 head;
- __u16 tail;
- }link;
-
- void __iomem *mask_base;
- struct pci_dev *dev;
-
-#ifdef CONFIG_PM
- /* PM save area for MSIX address/data */
-
- u32 address_hi_save;
- u32 address_lo_save;
- u32 data_save;
-#endif
-};
-
#endif /* MSI_H */
diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c
index bb7456c1dbac..a064f36a0805 100644
--- a/drivers/pci/pci-acpi.c
+++ b/drivers/pci/pci-acpi.c
@@ -36,6 +36,7 @@ acpi_query_osc (
struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL};
union acpi_object *out_obj;
u32 osc_dw0;
+ acpi_status *ret_status = (acpi_status *)retval;
/* Setting up input parameters */
@@ -56,6 +57,7 @@ acpi_query_osc (
if (ACPI_FAILURE (status)) {
printk(KERN_DEBUG
"Evaluate _OSC Set fails. Status = 0x%04x\n", status);
+ *ret_status = status;
return status;
}
out_obj = output.pointer;
@@ -90,6 +92,7 @@ acpi_query_osc (
query_osc_out:
kfree(output.pointer);
+ *ret_status = status;
return status;
}
@@ -166,6 +169,7 @@ run_osc_out:
acpi_status pci_osc_support_set(u32 flags)
{
u32 temp;
+ acpi_status retval;
if (!(flags & OSC_SUPPORT_MASKS)) {
return AE_TYPE;
@@ -179,9 +183,13 @@ acpi_status pci_osc_support_set(u32 flags)
acpi_get_devices ( PCI_ROOT_HID_STRING,
acpi_query_osc,
ctrlset_buf,
- NULL );
+ (void **) &retval );
ctrlset_buf[OSC_QUERY_TYPE] = !OSC_QUERY_ENABLE;
ctrlset_buf[OSC_CONTROL_TYPE] = temp;
+ if (ACPI_FAILURE(retval)) {
+ /* no osc support at all */
+ ctrlset_buf[OSC_SUPPORT_TYPE] = 0;
+ }
return AE_OK;
}
EXPORT_SYMBOL(pci_osc_support_set);
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c
index d8ace1f90dd2..e5ae3a0c13bb 100644
--- a/drivers/pci/pci-driver.c
+++ b/drivers/pci/pci-driver.c
@@ -56,6 +56,7 @@ store_new_id(struct device_driver *driver, const char *buf, size_t count)
subdevice=PCI_ANY_ID, class=0, class_mask=0;
unsigned long driver_data=0;
int fields=0;
+ int retval = 0;
fields = sscanf(buf, "%x %x %x %x %x %x %lux",
&vendor, &device, &subvendor, &subdevice,
@@ -82,10 +83,12 @@ store_new_id(struct device_driver *driver, const char *buf, size_t count)
spin_unlock(&pdrv->dynids.lock);
if (get_driver(&pdrv->driver)) {
- driver_attach(&pdrv->driver);
+ retval = driver_attach(&pdrv->driver);
put_driver(&pdrv->driver);
}
+ if (retval)
+ return retval;
return count;
}
static DRIVER_ATTR(new_id, S_IWUSR, NULL, store_new_id);
@@ -262,6 +265,13 @@ static int pci_device_remove(struct device * dev)
}
/*
+ * If the device is still on, set the power state as "unknown",
+ * since it might change by the next time we load the driver.
+ */
+ if (pci_dev->current_state == PCI_D0)
+ pci_dev->current_state = PCI_UNKNOWN;
+
+ /*
* We would love to complain here if pci_dev->is_enabled is set, that
* the driver should have called pci_disable_device(), but the
* unfortunate fact is there are too many odd BIOS and bridge setups
@@ -285,6 +295,12 @@ static int pci_device_suspend(struct device * dev, pm_message_t state)
suspend_report_result(drv->suspend, i);
} else {
pci_save_state(pci_dev);
+ /*
+ * mark its power state as "unknown", since we don't know if
+ * e.g. the BIOS will change its device state when we suspend.
+ */
+ if (pci_dev->current_state == PCI_D0)
+ pci_dev->current_state = PCI_UNKNOWN;
}
return i;
}
@@ -313,8 +329,8 @@ static int pci_default_resume(struct pci_dev *pci_dev)
/* restore the PCI config space */
pci_restore_state(pci_dev);
/* if the device was enabled before suspend, reenable */
- if (pci_dev->is_enabled)
- retval = pci_enable_device(pci_dev);
+ if (atomic_read(&pci_dev->enable_cnt))
+ retval = __pci_enable_device(pci_dev);
/* if the device was busmaster before the suspend, make it busmaster again */
if (pci_dev->is_busmaster)
pci_set_master(pci_dev);
@@ -418,16 +434,23 @@ int __pci_register_driver(struct pci_driver *drv, struct module *owner)
drv->driver.bus = &pci_bus_type;
drv->driver.owner = owner;
drv->driver.kobj.ktype = &pci_driver_kobj_type;
- drv->driver.multithread_probe = pci_multithread_probe;
+
+ if (pci_multithread_probe)
+ drv->driver.multithread_probe = pci_multithread_probe;
+ else
+ drv->driver.multithread_probe = drv->multithread_probe;
spin_lock_init(&drv->dynids.lock);
INIT_LIST_HEAD(&drv->dynids.list);
/* register with core */
error = driver_register(&drv->driver);
+ if (error)
+ return error;
- if (!error)
- error = pci_create_newid_file(drv);
+ error = pci_create_newid_file(drv);
+ if (error)
+ driver_unregister(&drv->driver);
return error;
}
diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c
index fdefa7dcd156..7a94076752d0 100644
--- a/drivers/pci/pci-sysfs.c
+++ b/drivers/pci/pci-sysfs.c
@@ -42,7 +42,6 @@ pci_config_attr(subsystem_vendor, "0x%04x\n");
pci_config_attr(subsystem_device, "0x%04x\n");
pci_config_attr(class, "0x%06x\n");
pci_config_attr(irq, "%u\n");
-pci_config_attr(is_enabled, "%u\n");
static ssize_t broken_parity_status_show(struct device *dev,
struct device_attribute *attr,
@@ -112,25 +111,78 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
(u8)(pci_dev->class >> 16), (u8)(pci_dev->class >> 8),
(u8)(pci_dev->class));
}
-static ssize_t
-is_enabled_store(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+
+static ssize_t is_enabled_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
{
+ ssize_t result = -EINVAL;
struct pci_dev *pdev = to_pci_dev(dev);
/* this can crash the machine when done on the "wrong" device */
if (!capable(CAP_SYS_ADMIN))
return count;
- if (*buf == '0')
- pci_disable_device(pdev);
+ if (*buf == '0') {
+ if (atomic_read(&pdev->enable_cnt) != 0)
+ pci_disable_device(pdev);
+ else
+ result = -EIO;
+ } else if (*buf == '1')
+ result = pci_enable_device(pdev);
+
+ return result < 0 ? result : count;
+}
- if (*buf == '1')
- pci_enable_device(pdev);
+static ssize_t is_enabled_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct pci_dev *pdev;
- return count;
+ pdev = to_pci_dev (dev);
+ return sprintf (buf, "%u\n", atomic_read(&pdev->enable_cnt));
}
+static ssize_t
+msi_bus_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+
+ if (!pdev->subordinate)
+ return 0;
+
+ return sprintf (buf, "%u\n",
+ !(pdev->subordinate->bus_flags & PCI_BUS_FLAGS_NO_MSI));
+}
+
+static ssize_t
+msi_bus_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+
+ /* bad things may happen if the no_msi flag is changed
+ * while some drivers are loaded */
+ if (!capable(CAP_SYS_ADMIN))
+ return count;
+
+ if (!pdev->subordinate)
+ return count;
+
+ if (*buf == '0') {
+ pdev->subordinate->bus_flags |= PCI_BUS_FLAGS_NO_MSI;
+ dev_warn(&pdev->dev, "forced subordinate bus to not support MSI,"
+ " bad things could happen.\n");
+ }
+
+ if (*buf == '1') {
+ pdev->subordinate->bus_flags &= ~PCI_BUS_FLAGS_NO_MSI;
+ dev_warn(&pdev->dev, "forced subordinate bus to support MSI,"
+ " bad things could happen.\n");
+ }
+
+ return count;
+}
struct device_attribute pci_dev_attrs[] = {
__ATTR_RO(resource),
@@ -145,6 +197,7 @@ struct device_attribute pci_dev_attrs[] = {
__ATTR(enable, 0600, is_enabled_show, is_enabled_store),
__ATTR(broken_parity_status,(S_IRUGO|S_IWUSR),
broken_parity_status_show,broken_parity_status_store),
+ __ATTR(msi_bus, 0644, msi_bus_show, msi_bus_store),
__ATTR_NULL,
};
@@ -385,15 +438,38 @@ pci_mmap_resource(struct kobject *kobj, struct bin_attribute *attr,
}
/**
+ * pci_remove_resource_files - cleanup resource files
+ * @dev: dev to cleanup
+ *
+ * If we created resource files for @dev, remove them from sysfs and
+ * free their resources.
+ */
+static void
+pci_remove_resource_files(struct pci_dev *pdev)
+{
+ int i;
+
+ for (i = 0; i < PCI_ROM_RESOURCE; i++) {
+ struct bin_attribute *res_attr;
+
+ res_attr = pdev->res_attr[i];
+ if (res_attr) {
+ sysfs_remove_bin_file(&pdev->dev.kobj, res_attr);
+ kfree(res_attr);
+ }
+ }
+}
+
+/**
* pci_create_resource_files - create resource files in sysfs for @dev
* @dev: dev in question
*
* Walk the resources in @dev creating files for each resource available.
*/
-static void
-pci_create_resource_files(struct pci_dev *pdev)
+static int pci_create_resource_files(struct pci_dev *pdev)
{
int i;
+ int retval;
/* Expose the PCI resources from this device as files */
for (i = 0; i < PCI_ROM_RESOURCE; i++) {
@@ -416,35 +492,19 @@ pci_create_resource_files(struct pci_dev *pdev)
res_attr->size = pci_resource_len(pdev, i);
res_attr->mmap = pci_mmap_resource;
res_attr->private = &pdev->resource[i];
- sysfs_create_bin_file(&pdev->dev.kobj, res_attr);
- }
- }
-}
-
-/**
- * pci_remove_resource_files - cleanup resource files
- * @dev: dev to cleanup
- *
- * If we created resource files for @dev, remove them from sysfs and
- * free their resources.
- */
-static void
-pci_remove_resource_files(struct pci_dev *pdev)
-{
- int i;
-
- for (i = 0; i < PCI_ROM_RESOURCE; i++) {
- struct bin_attribute *res_attr;
-
- res_attr = pdev->res_attr[i];
- if (res_attr) {
- sysfs_remove_bin_file(&pdev->dev.kobj, res_attr);
- kfree(res_attr);
+ retval = sysfs_create_bin_file(&pdev->dev.kobj, res_attr);
+ if (retval) {
+ pci_remove_resource_files(pdev);
+ return retval;
+ }
+ } else {
+ return -ENOMEM;
}
}
+ return 0;
}
#else /* !HAVE_PCI_MMAP */
-static inline void pci_create_resource_files(struct pci_dev *dev) { return; }
+static inline int pci_create_resource_files(struct pci_dev *dev) { return 0; }
static inline void pci_remove_resource_files(struct pci_dev *dev) { return; }
#endif /* HAVE_PCI_MMAP */
@@ -529,22 +589,27 @@ static struct bin_attribute pcie_config_attr = {
.write = pci_write_config,
};
-int pci_create_sysfs_dev_files (struct pci_dev *pdev)
+int __must_check pci_create_sysfs_dev_files (struct pci_dev *pdev)
{
+ struct bin_attribute *rom_attr = NULL;
+ int retval;
+
if (!sysfs_initialized)
return -EACCES;
if (pdev->cfg_size < 4096)
- sysfs_create_bin_file(&pdev->dev.kobj, &pci_config_attr);
+ retval = sysfs_create_bin_file(&pdev->dev.kobj, &pci_config_attr);
else
- sysfs_create_bin_file(&pdev->dev.kobj, &pcie_config_attr);
+ retval = sysfs_create_bin_file(&pdev->dev.kobj, &pcie_config_attr);
+ if (retval)
+ goto err;
- pci_create_resource_files(pdev);
+ retval = pci_create_resource_files(pdev);
+ if (retval)
+ goto err_bin_file;
/* If the device has a ROM, try to expose it in sysfs. */
if (pci_resource_len(pdev, PCI_ROM_RESOURCE)) {
- struct bin_attribute *rom_attr;
-
rom_attr = kzalloc(sizeof(*rom_attr), GFP_ATOMIC);
if (rom_attr) {
pdev->rom_attr = rom_attr;
@@ -554,13 +619,28 @@ int pci_create_sysfs_dev_files (struct pci_dev *pdev)
rom_attr->attr.owner = THIS_MODULE;
rom_attr->read = pci_read_rom;
rom_attr->write = pci_write_rom;
- sysfs_create_bin_file(&pdev->dev.kobj, rom_attr);
+ retval = sysfs_create_bin_file(&pdev->dev.kobj, rom_attr);
+ if (retval)
+ goto err_rom;
+ } else {
+ retval = -ENOMEM;
+ goto err_bin_file;
}
}
/* add platform-specific attributes */
pcibios_add_platform_entries(pdev);
-
+
return 0;
+
+err_rom:
+ kfree(rom_attr);
+err_bin_file:
+ if (pdev->cfg_size < 4096)
+ sysfs_remove_bin_file(&pdev->dev.kobj, &pci_config_attr);
+ else
+ sysfs_remove_bin_file(&pdev->dev.kobj, &pcie_config_attr);
+err:
+ return retval;
}
/**
@@ -571,6 +651,9 @@ int pci_create_sysfs_dev_files (struct pci_dev *pdev)
*/
void pci_remove_sysfs_dev_files(struct pci_dev *pdev)
{
+ if (!sysfs_initialized)
+ return;
+
if (pdev->cfg_size < 4096)
sysfs_remove_bin_file(&pdev->dev.kobj, &pci_config_attr);
else
@@ -589,10 +672,14 @@ void pci_remove_sysfs_dev_files(struct pci_dev *pdev)
static int __init pci_sysfs_init(void)
{
struct pci_dev *pdev = NULL;
-
+ int retval;
+
sysfs_initialized = 1;
- for_each_pci_dev(pdev)
- pci_create_sysfs_dev_files(pdev);
+ for_each_pci_dev(pdev) {
+ retval = pci_create_sysfs_dev_files(pdev);
+ if (retval)
+ return retval;
+ }
return 0;
}
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 590f4e6f505d..5a14b73cf3a1 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -445,6 +445,92 @@ pci_power_t pci_choose_state(struct pci_dev *dev, pm_message_t state)
EXPORT_SYMBOL(pci_choose_state);
+static int pci_save_pcie_state(struct pci_dev *dev)
+{
+ int pos, i = 0;
+ struct pci_cap_saved_state *save_state;
+ u16 *cap;
+
+ pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
+ if (pos <= 0)
+ return 0;
+
+ save_state = kzalloc(sizeof(*save_state) + sizeof(u16) * 4, GFP_KERNEL);
+ if (!save_state) {
+ dev_err(&dev->dev, "Out of memory in pci_save_pcie_state\n");
+ return -ENOMEM;
+ }
+ cap = (u16 *)&save_state->data[0];
+
+ pci_read_config_word(dev, pos + PCI_EXP_DEVCTL, &cap[i++]);
+ pci_read_config_word(dev, pos + PCI_EXP_LNKCTL, &cap[i++]);
+ pci_read_config_word(dev, pos + PCI_EXP_SLTCTL, &cap[i++]);
+ pci_read_config_word(dev, pos + PCI_EXP_RTCTL, &cap[i++]);
+ pci_add_saved_cap(dev, save_state);
+ return 0;
+}
+
+static void pci_restore_pcie_state(struct pci_dev *dev)
+{
+ int i = 0, pos;
+ struct pci_cap_saved_state *save_state;
+ u16 *cap;
+
+ save_state = pci_find_saved_cap(dev, PCI_CAP_ID_EXP);
+ pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
+ if (!save_state || pos <= 0)
+ return;
+ cap = (u16 *)&save_state->data[0];
+
+ pci_write_config_word(dev, pos + PCI_EXP_DEVCTL, cap[i++]);
+ pci_write_config_word(dev, pos + PCI_EXP_LNKCTL, cap[i++]);
+ pci_write_config_word(dev, pos + PCI_EXP_SLTCTL, cap[i++]);
+ pci_write_config_word(dev, pos + PCI_EXP_RTCTL, cap[i++]);
+ pci_remove_saved_cap(save_state);
+ kfree(save_state);
+}
+
+
+static int pci_save_pcix_state(struct pci_dev *dev)
+{
+ int pos, i = 0;
+ struct pci_cap_saved_state *save_state;
+ u16 *cap;
+
+ pos = pci_find_capability(dev, PCI_CAP_ID_PCIX);
+ if (pos <= 0)
+ return 0;
+
+ save_state = kzalloc(sizeof(*save_state) + sizeof(u16), GFP_KERNEL);
+ if (!save_state) {
+ dev_err(&dev->dev, "Out of memory in pci_save_pcie_state\n");
+ return -ENOMEM;
+ }
+ cap = (u16 *)&save_state->data[0];
+
+ pci_read_config_word(dev, pos + PCI_X_CMD, &cap[i++]);
+ pci_add_saved_cap(dev, save_state);
+ return 0;
+}
+
+static void pci_restore_pcix_state(struct pci_dev *dev)
+{
+ int i = 0, pos;
+ struct pci_cap_saved_state *save_state;
+ u16 *cap;
+
+ save_state = pci_find_saved_cap(dev, PCI_CAP_ID_PCIX);
+ pos = pci_find_capability(dev, PCI_CAP_ID_PCIX);
+ if (!save_state || pos <= 0)
+ return;
+ cap = (u16 *)&save_state->data[0];
+
+ pci_write_config_word(dev, pos + PCI_X_CMD, cap[i++]);
+ pci_remove_saved_cap(save_state);
+ kfree(save_state);
+}
+
+
/**
* pci_save_state - save the PCI configuration space of a device before suspending
* @dev: - PCI device that we're dealing with
@@ -460,6 +546,10 @@ pci_save_state(struct pci_dev *dev)
return i;
if ((i = pci_save_msix_state(dev)) != 0)
return i;
+ if ((i = pci_save_pcie_state(dev)) != 0)
+ return i;
+ if ((i = pci_save_pcix_state(dev)) != 0)
+ return i;
return 0;
}
@@ -473,6 +563,9 @@ pci_restore_state(struct pci_dev *dev)
int i;
int val;
+ /* PCI Express register must be restored first */
+ pci_restore_pcie_state(dev);
+
/*
* The Base Address register should be programmed before the command
* register(s)
@@ -488,6 +581,7 @@ pci_restore_state(struct pci_dev *dev)
dev->saved_config_space[i]);
}
}
+ pci_restore_pcix_state(dev);
pci_restore_msi_state(dev);
pci_restore_msix_state(dev);
return 0;
@@ -518,30 +612,51 @@ pci_enable_device_bars(struct pci_dev *dev, int bars)
}
/**
- * pci_enable_device - Initialize device before it's used by a driver.
+ * __pci_enable_device - Initialize device before it's used by a driver.
* @dev: PCI device to be initialized
*
* Initialize device before it's used by a driver. Ask low-level code
* to enable I/O and memory. Wake up the device if it was suspended.
* Beware, this function can fail.
+ *
+ * Note this function is a backend and is not supposed to be called by
+ * normal code, use pci_enable_device() instead.
*/
int
-pci_enable_device(struct pci_dev *dev)
+__pci_enable_device(struct pci_dev *dev)
{
int err;
- if (dev->is_enabled)
- return 0;
-
err = pci_enable_device_bars(dev, (1 << PCI_NUM_RESOURCES) - 1);
if (err)
return err;
pci_fixup_device(pci_fixup_enable, dev);
- dev->is_enabled = 1;
return 0;
}
/**
+ * pci_enable_device - Initialize device before it's used by a driver.
+ * @dev: PCI device to be initialized
+ *
+ * Initialize device before it's used by a driver. Ask low-level code
+ * to enable I/O and memory. Wake up the device if it was suspended.
+ * Beware, this function can fail.
+ *
+ * Note we don't actually enable the device many times if we call
+ * this function repeatedly (we just increment the count).
+ */
+int pci_enable_device(struct pci_dev *dev)
+{
+ int result;
+ if (atomic_add_return(1, &dev->enable_cnt) > 1)
+ return 0; /* already enabled */
+ result = __pci_enable_device(dev);
+ if (result < 0)
+ atomic_dec(&dev->enable_cnt);
+ return result;
+}
+
+/**
* pcibios_disable_device - disable arch specific PCI resources for device dev
* @dev: the PCI device to disable
*
@@ -557,12 +672,18 @@ void __attribute__ ((weak)) pcibios_disable_device (struct pci_dev *dev) {}
*
* Signal to the system that the PCI device is not in use by the system
* anymore. This only involves disabling PCI bus-mastering, if active.
+ *
+ * Note we don't actually disable the device until all callers of
+ * pci_device_enable() have called pci_device_disable().
*/
void
pci_disable_device(struct pci_dev *dev)
{
u16 pci_command;
+ if (atomic_sub_return(1, &dev->enable_cnt) != 0)
+ return;
+
if (dev->msi_enabled)
disable_msi_mode(dev, pci_find_capability(dev, PCI_CAP_ID_MSI),
PCI_CAP_ID_MSI);
@@ -578,7 +699,6 @@ pci_disable_device(struct pci_dev *dev)
dev->is_busmaster = 0;
pcibios_disable_device(dev);
- dev->is_enabled = 0;
}
/**
@@ -781,22 +901,38 @@ pci_set_master(struct pci_dev *dev)
pcibios_set_master(dev);
}
-#ifndef HAVE_ARCH_PCI_MWI
+#ifdef PCI_DISABLE_MWI
+int pci_set_mwi(struct pci_dev *dev)
+{
+ return 0;
+}
+
+void pci_clear_mwi(struct pci_dev *dev)
+{
+}
+
+#else
+
+#ifndef PCI_CACHE_LINE_BYTES
+#define PCI_CACHE_LINE_BYTES L1_CACHE_BYTES
+#endif
+
/* This can be overridden by arch code. */
-u8 pci_cache_line_size = L1_CACHE_BYTES >> 2;
+/* Don't forget this is measured in 32-bit words, not bytes */
+u8 pci_cache_line_size = PCI_CACHE_LINE_BYTES / 4;
/**
- * pci_generic_prep_mwi - helper function for pci_set_mwi
- * @dev: the PCI device for which MWI is enabled
+ * pci_set_cacheline_size - ensure the CACHE_LINE_SIZE register is programmed
+ * @dev: the PCI device for which MWI is to be enabled
*
- * Helper function for generic implementation of pcibios_prep_mwi
- * function. Originally copied from drivers/net/acenic.c.
+ * Helper function for pci_set_mwi.
+ * Originally copied from drivers/net/acenic.c.
* Copyright 1998-2001 by Jes Sorensen, <jes@trained-monkey.org>.
*
* RETURNS: An appropriate -ERRNO error value on error, or zero for success.
*/
static int
-pci_generic_prep_mwi(struct pci_dev *dev)
+pci_set_cacheline_size(struct pci_dev *dev)
{
u8 cacheline_size;
@@ -822,7 +958,6 @@ pci_generic_prep_mwi(struct pci_dev *dev)
return -EINVAL;
}
-#endif /* !HAVE_ARCH_PCI_MWI */
/**
* pci_set_mwi - enables memory-write-invalidate PCI transaction
@@ -840,12 +975,7 @@ pci_set_mwi(struct pci_dev *dev)
int rc;
u16 cmd;
-#ifdef HAVE_ARCH_PCI_MWI
- rc = pcibios_prep_mwi(dev);
-#else
- rc = pci_generic_prep_mwi(dev);
-#endif
-
+ rc = pci_set_cacheline_size(dev);
if (rc)
return rc;
@@ -876,6 +1006,7 @@ pci_clear_mwi(struct pci_dev *dev)
pci_write_config_word(dev, PCI_COMMAND, cmd);
}
}
+#endif /* ! PCI_DISABLE_MWI */
/**
* pci_intx - enables/disables PCI INTx for device dev
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index 08d58fc78ee1..398852f526a6 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -1,5 +1,6 @@
/* Functions internal to the PCI core code */
+extern int __must_check __pci_enable_device(struct pci_dev *);
extern int pci_uevent(struct device *dev, char **envp, int num_envp,
char *buffer, int buffer_size);
extern int pci_create_sysfs_dev_files(struct pci_dev *pdev);
@@ -42,7 +43,7 @@ extern void pci_remove_legacy_files(struct pci_bus *bus);
/* Lock for read/write access to pci device and bus lists */
extern struct rw_semaphore pci_bus_sem;
-#ifdef CONFIG_X86_IO_APIC
+#ifdef CONFIG_PCI_MSI
extern int pci_msi_quirk;
#else
#define pci_msi_quirk 0
diff --git a/drivers/pci/pcie/Kconfig b/drivers/pci/pcie/Kconfig
index 1012db8b8b2c..0ad92a8ad8b1 100644
--- a/drivers/pci/pcie/Kconfig
+++ b/drivers/pci/pcie/Kconfig
@@ -34,3 +34,4 @@ config HOTPLUG_PCI_PCIE_POLL_EVENT_MODE
When in doubt, say N.
+source "drivers/pci/pcie/aer/Kconfig"
diff --git a/drivers/pci/pcie/Makefile b/drivers/pci/pcie/Makefile
index 984fa87283e3..e00fb99acf44 100644
--- a/drivers/pci/pcie/Makefile
+++ b/drivers/pci/pcie/Makefile
@@ -5,3 +5,6 @@
pcieportdrv-y := portdrv_core.o portdrv_pci.o portdrv_bus.o
obj-$(CONFIG_PCIEPORTBUS) += pcieportdrv.o
+
+# Build PCI Express AER if needed
+obj-$(CONFIG_PCIEAER) += aer/
diff --git a/drivers/pci/pcie/aer/Kconfig b/drivers/pci/pcie/aer/Kconfig
new file mode 100644
index 000000000000..3f37a60a6438
--- /dev/null
+++ b/drivers/pci/pcie/aer/Kconfig
@@ -0,0 +1,12 @@
+#
+# PCI Express Root Port Device AER Configuration
+#
+
+config PCIEAER
+ boolean "Root Port Advanced Error Reporting support"
+ depends on PCIEPORTBUS && ACPI
+ default y
+ help
+ This enables PCI Express Root Port Advanced Error Reporting
+ (AER) driver support. Error reporting messages sent to Root
+ Port will be handled by PCI Express AER driver.
diff --git a/drivers/pci/pcie/aer/Makefile b/drivers/pci/pcie/aer/Makefile
new file mode 100644
index 000000000000..15a4f40d520b
--- /dev/null
+++ b/drivers/pci/pcie/aer/Makefile
@@ -0,0 +1,8 @@
+#
+# Makefile for PCI-Express Root Port Advanced Error Reporting Driver
+#
+
+obj-$(CONFIG_PCIEAER) += aerdriver.o
+
+aerdriver-objs := aerdrv_errprint.o aerdrv_core.o aerdrv.o aerdrv_acpi.o
+
diff --git a/drivers/pci/pcie/aer/aerdrv.c b/drivers/pci/pcie/aer/aerdrv.c
new file mode 100644
index 000000000000..55866b6b26fa
--- /dev/null
+++ b/drivers/pci/pcie/aer/aerdrv.c
@@ -0,0 +1,345 @@
+/*
+ * drivers/pci/pcie/aer/aerdrv.c
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * This file implements the AER root port service driver. The driver will
+ * register an irq handler. When root port triggers an AER interrupt, the irq
+ * handler will collect root port status and schedule a work.
+ *
+ * Copyright (C) 2006 Intel Corp.
+ * Tom Long Nguyen (tom.l.nguyen@intel.com)
+ * Zhang Yanmin (yanmin.zhang@intel.com)
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/pm.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/pcieport_if.h>
+
+#include "aerdrv.h"
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v1.0"
+#define DRIVER_AUTHOR "tom.l.nguyen@intel.com"
+#define DRIVER_DESC "Root Port Advanced Error Reporting Driver"
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+static int __devinit aer_probe (struct pcie_device *dev,
+ const struct pcie_port_service_id *id );
+static void aer_remove(struct pcie_device *dev);
+static int aer_suspend(struct pcie_device *dev, pm_message_t state)
+{return 0;}
+static int aer_resume(struct pcie_device *dev) {return 0;}
+static pci_ers_result_t aer_error_detected(struct pci_dev *dev,
+ enum pci_channel_state error);
+static void aer_error_resume(struct pci_dev *dev);
+static pci_ers_result_t aer_root_reset(struct pci_dev *dev);
+
+/*
+ * PCI Express bus's AER Root service driver data structure
+ */
+static struct pcie_port_service_id aer_id[] = {
+ {
+ .vendor = PCI_ANY_ID,
+ .device = PCI_ANY_ID,
+ .port_type = PCIE_RC_PORT,
+ .service_type = PCIE_PORT_SERVICE_AER,
+ },
+ { /* end: all zeroes */ }
+};
+
+static struct pci_error_handlers aer_error_handlers = {
+ .error_detected = aer_error_detected,
+ .resume = aer_error_resume,
+};
+
+static struct pcie_port_service_driver aerdrv = {
+ .name = "aer",
+ .id_table = &aer_id[0],
+
+ .probe = aer_probe,
+ .remove = aer_remove,
+
+ .suspend = aer_suspend,
+ .resume = aer_resume,
+
+ .err_handler = &aer_error_handlers,
+
+ .reset_link = aer_root_reset,
+};
+
+/**
+ * aer_irq - Root Port's ISR
+ * @irq: IRQ assigned to Root Port
+ * @context: pointer to Root Port data structure
+ *
+ * Invoked when Root Port detects AER messages.
+ **/
+static irqreturn_t aer_irq(int irq, void *context)
+{
+ unsigned int status, id;
+ struct pcie_device *pdev = (struct pcie_device *)context;
+ struct aer_rpc *rpc = get_service_data(pdev);
+ int next_prod_idx;
+ unsigned long flags;
+ int pos;
+
+ pos = pci_find_aer_capability(pdev->port);
+ /*
+ * Must lock access to Root Error Status Reg, Root Error ID Reg,
+ * and Root error producer/consumer index
+ */
+ spin_lock_irqsave(&rpc->e_lock, flags);
+
+ /* Read error status */
+ pci_read_config_dword(pdev->port, pos + PCI_ERR_ROOT_STATUS, &status);
+ if (!(status & ROOT_ERR_STATUS_MASKS)) {
+ spin_unlock_irqrestore(&rpc->e_lock, flags);
+ return IRQ_NONE;
+ }
+
+ /* Read error source and clear error status */
+ pci_read_config_dword(pdev->port, pos + PCI_ERR_ROOT_COR_SRC, &id);
+ pci_write_config_dword(pdev->port, pos + PCI_ERR_ROOT_STATUS, status);
+
+ /* Store error source for later DPC handler */
+ next_prod_idx = rpc->prod_idx + 1;
+ if (next_prod_idx == AER_ERROR_SOURCES_MAX)
+ next_prod_idx = 0;
+ if (next_prod_idx == rpc->cons_idx) {
+ /*
+ * Error Storm Condition - possibly the same error occurred.
+ * Drop the error.
+ */
+ spin_unlock_irqrestore(&rpc->e_lock, flags);
+ return IRQ_HANDLED;
+ }
+ rpc->e_sources[rpc->prod_idx].status = status;
+ rpc->e_sources[rpc->prod_idx].id = id;
+ rpc->prod_idx = next_prod_idx;
+ spin_unlock_irqrestore(&rpc->e_lock, flags);
+
+ /* Invoke DPC handler */
+ schedule_work(&rpc->dpc_handler);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * aer_alloc_rpc - allocate Root Port data structure
+ * @dev: pointer to the pcie_dev data structure
+ *
+ * Invoked when Root Port's AER service is loaded.
+ **/
+static struct aer_rpc* aer_alloc_rpc(struct pcie_device *dev)
+{
+ struct aer_rpc *rpc;
+
+ if (!(rpc = (struct aer_rpc *)kmalloc(sizeof(struct aer_rpc),
+ GFP_KERNEL)))
+ return NULL;
+
+ memset(rpc, 0, sizeof(struct aer_rpc));
+ /*
+ * Initialize Root lock access, e_lock, to Root Error Status Reg,
+ * Root Error ID Reg, and Root error producer/consumer index.
+ */
+ rpc->e_lock = SPIN_LOCK_UNLOCKED;
+
+ rpc->rpd = dev;
+ INIT_WORK(&rpc->dpc_handler, aer_isr);
+ rpc->prod_idx = rpc->cons_idx = 0;
+ mutex_init(&rpc->rpc_mutex);
+ init_waitqueue_head(&rpc->wait_release);
+
+ /* Use PCIE bus function to store rpc into PCIE device */
+ set_service_data(dev, rpc);
+
+ return rpc;
+}
+
+/**
+ * aer_remove - clean up resources
+ * @dev: pointer to the pcie_dev data structure
+ *
+ * Invoked when PCI Express bus unloads or AER probe fails.
+ **/
+static void aer_remove(struct pcie_device *dev)
+{
+ struct aer_rpc *rpc = get_service_data(dev);
+
+ if (rpc) {
+ /* If register interrupt service, it must be free. */
+ if (rpc->isr)
+ free_irq(dev->irq, dev);
+
+ wait_event(rpc->wait_release, rpc->prod_idx == rpc->cons_idx);
+
+ aer_delete_rootport(rpc);
+ set_service_data(dev, NULL);
+ }
+}
+
+/**
+ * aer_probe - initialize resources
+ * @dev: pointer to the pcie_dev data structure
+ * @id: pointer to the service id data structure
+ *
+ * Invoked when PCI Express bus loads AER service driver.
+ **/
+static int __devinit aer_probe (struct pcie_device *dev,
+ const struct pcie_port_service_id *id )
+{
+ int status;
+ struct aer_rpc *rpc;
+ struct device *device = &dev->device;
+
+ /* Init */
+ if ((status = aer_init(dev)))
+ return status;
+
+ /* Alloc rpc data structure */
+ if (!(rpc = aer_alloc_rpc(dev))) {
+ printk(KERN_DEBUG "%s: Alloc rpc fails on PCIE device[%s]\n",
+ __FUNCTION__, device->bus_id);
+ aer_remove(dev);
+ return -ENOMEM;
+ }
+
+ /* Request IRQ ISR */
+ if ((status = request_irq(dev->irq, aer_irq, SA_SHIRQ, "aerdrv",
+ dev))) {
+ printk(KERN_DEBUG "%s: Request ISR fails on PCIE device[%s]\n",
+ __FUNCTION__, device->bus_id);
+ aer_remove(dev);
+ return status;
+ }
+
+ rpc->isr = 1;
+
+ aer_enable_rootport(rpc);
+
+ return status;
+}
+
+/**
+ * aer_root_reset - reset link on Root Port
+ * @dev: pointer to Root Port's pci_dev data structure
+ *
+ * Invoked by Port Bus driver when performing link reset at Root Port.
+ **/
+static pci_ers_result_t aer_root_reset(struct pci_dev *dev)
+{
+ u16 p2p_ctrl;
+ u32 status;
+ int pos;
+
+ pos = pci_find_aer_capability(dev);
+
+ /* Disable Root's interrupt in response to error messages */
+ pci_write_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, 0);
+
+ /* Assert Secondary Bus Reset */
+ pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &p2p_ctrl);
+ p2p_ctrl |= PCI_CB_BRIDGE_CTL_CB_RESET;
+ pci_write_config_word(dev, PCI_BRIDGE_CONTROL, p2p_ctrl);
+
+ /* De-assert Secondary Bus Reset */
+ p2p_ctrl &= ~PCI_CB_BRIDGE_CTL_CB_RESET;
+ pci_write_config_word(dev, PCI_BRIDGE_CONTROL, p2p_ctrl);
+
+ /*
+ * System software must wait for at least 100ms from the end
+ * of a reset of one or more device before it is permitted
+ * to issue Configuration Requests to those devices.
+ */
+ msleep(200);
+ printk(KERN_DEBUG "Complete link reset at Root[%s]\n", dev->dev.bus_id);
+
+ /* Enable Root Port's interrupt in response to error messages */
+ pci_read_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, &status);
+ pci_write_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, status);
+ pci_write_config_dword(dev,
+ pos + PCI_ERR_ROOT_COMMAND,
+ ROOT_PORT_INTR_ON_MESG_MASK);
+
+ return PCI_ERS_RESULT_RECOVERED;
+}
+
+/**
+ * aer_error_detected - update severity status
+ * @dev: pointer to Root Port's pci_dev data structure
+ * @error: error severity being notified by port bus
+ *
+ * Invoked by Port Bus driver during error recovery.
+ **/
+static pci_ers_result_t aer_error_detected(struct pci_dev *dev,
+ enum pci_channel_state error)
+{
+ /* Root Port has no impact. Always recovers. */
+ return PCI_ERS_RESULT_CAN_RECOVER;
+}
+
+/**
+ * aer_error_resume - clean up corresponding error status bits
+ * @dev: pointer to Root Port's pci_dev data structure
+ *
+ * Invoked by Port Bus driver during nonfatal recovery.
+ **/
+static void aer_error_resume(struct pci_dev *dev)
+{
+ int pos;
+ u32 status, mask;
+ u16 reg16;
+
+ /* Clean up Root device status */
+ pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
+ pci_read_config_word(dev, pos + PCI_EXP_DEVSTA, &reg16);
+ pci_write_config_word(dev, pos + PCI_EXP_DEVSTA, reg16);
+
+ /* Clean AER Root Error Status */
+ pos = pci_find_aer_capability(dev);
+ pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, &status);
+ pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_SEVER, &mask);
+ if (dev->error_state == pci_channel_io_normal)
+ status &= ~mask; /* Clear corresponding nonfatal bits */
+ else
+ status &= mask; /* Clear corresponding fatal bits */
+ pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, status);
+}
+
+/**
+ * aer_service_init - register AER root service driver
+ *
+ * Invoked when AER root service driver is loaded.
+ **/
+static int __init aer_service_init(void)
+{
+ return pcie_port_service_register(&aerdrv);
+}
+
+/**
+ * aer_service_exit - unregister AER root service driver
+ *
+ * Invoked when AER root service driver is unloaded.
+ **/
+static void __exit aer_service_exit(void)
+{
+ pcie_port_service_unregister(&aerdrv);
+}
+
+module_init(aer_service_init);
+module_exit(aer_service_exit);
diff --git a/drivers/pci/pcie/aer/aerdrv.h b/drivers/pci/pcie/aer/aerdrv.h
new file mode 100644
index 000000000000..3c0a58f64dd8
--- /dev/null
+++ b/drivers/pci/pcie/aer/aerdrv.h
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2006 Intel Corp.
+ * Tom Long Nguyen (tom.l.nguyen@intel.com)
+ * Zhang Yanmin (yanmin.zhang@intel.com)
+ *
+ */
+
+#ifndef _AERDRV_H_
+#define _AERDRV_H_
+
+#include <linux/pcieport_if.h>
+#include <linux/aer.h>
+
+#define AER_NONFATAL 0
+#define AER_FATAL 1
+#define AER_CORRECTABLE 2
+#define AER_UNCORRECTABLE 4
+#define AER_ERROR_MASK 0x001fffff
+#define AER_ERROR(d) (d & AER_ERROR_MASK)
+
+#define OSC_METHOD_RUN_SUCCESS 0
+#define OSC_METHOD_NOT_SUPPORTED 1
+#define OSC_METHOD_RUN_FAILURE 2
+
+/* Root Error Status Register Bits */
+#define ROOT_ERR_STATUS_MASKS 0x0f
+
+#define SYSTEM_ERROR_INTR_ON_MESG_MASK (PCI_EXP_RTCTL_SECEE| \
+ PCI_EXP_RTCTL_SENFEE| \
+ PCI_EXP_RTCTL_SEFEE)
+#define ROOT_PORT_INTR_ON_MESG_MASK (PCI_ERR_ROOT_CMD_COR_EN| \
+ PCI_ERR_ROOT_CMD_NONFATAL_EN| \
+ PCI_ERR_ROOT_CMD_FATAL_EN)
+#define ERR_COR_ID(d) (d & 0xffff)
+#define ERR_UNCOR_ID(d) (d >> 16)
+
+#define AER_SUCCESS 0
+#define AER_UNSUCCESS 1
+#define AER_ERROR_SOURCES_MAX 100
+
+#define AER_LOG_TLP_MASKS (PCI_ERR_UNC_POISON_TLP| \
+ PCI_ERR_UNC_ECRC| \
+ PCI_ERR_UNC_UNSUP| \
+ PCI_ERR_UNC_COMP_ABORT| \
+ PCI_ERR_UNC_UNX_COMP| \
+ PCI_ERR_UNC_MALF_TLP)
+
+/* AER Error Info Flags */
+#define AER_TLP_HEADER_VALID_FLAG 0x00000001
+#define AER_MULTI_ERROR_VALID_FLAG 0x00000002
+
+#define ERR_CORRECTABLE_ERROR_MASK 0x000031c1
+#define ERR_UNCORRECTABLE_ERROR_MASK 0x001ff010
+
+struct header_log_regs {
+ unsigned int dw0;
+ unsigned int dw1;
+ unsigned int dw2;
+ unsigned int dw3;
+};
+
+struct aer_err_info {
+ int severity; /* 0:NONFATAL | 1:FATAL | 2:COR */
+ int flags;
+ unsigned int status; /* COR/UNCOR Error Status */
+ struct header_log_regs tlp; /* TLP Header */
+};
+
+struct aer_err_source {
+ unsigned int status;
+ unsigned int id;
+};
+
+struct aer_rpc {
+ struct pcie_device *rpd; /* Root Port device */
+ struct work_struct dpc_handler;
+ struct aer_err_source e_sources[AER_ERROR_SOURCES_MAX];
+ unsigned short prod_idx; /* Error Producer Index */
+ unsigned short cons_idx; /* Error Consumer Index */
+ int isr;
+ spinlock_t e_lock; /*
+ * Lock access to Error Status/ID Regs
+ * and error producer/consumer index
+ */
+ struct mutex rpc_mutex; /*
+ * only one thread could do
+ * recovery on the same
+ * root port hierachy
+ */
+ wait_queue_head_t wait_release;
+};
+
+struct aer_broadcast_data {
+ enum pci_channel_state state;
+ enum pci_ers_result result;
+};
+
+static inline pci_ers_result_t merge_result(enum pci_ers_result orig,
+ enum pci_ers_result new)
+{
+ switch (orig) {
+ case PCI_ERS_RESULT_CAN_RECOVER:
+ case PCI_ERS_RESULT_RECOVERED:
+ orig = new;
+ break;
+ case PCI_ERS_RESULT_DISCONNECT:
+ if (new == PCI_ERS_RESULT_NEED_RESET)
+ orig = new;
+ break;
+ default:
+ break;
+ }
+
+ return orig;
+}
+
+extern struct bus_type pcie_port_bus_type;
+extern void aer_enable_rootport(struct aer_rpc *rpc);
+extern void aer_delete_rootport(struct aer_rpc *rpc);
+extern int aer_init(struct pcie_device *dev);
+extern void aer_isr(struct work_struct *work);
+extern void aer_print_error(struct pci_dev *dev, struct aer_err_info *info);
+extern int aer_osc_setup(struct pci_dev *dev);
+
+#endif //_AERDRV_H_
diff --git a/drivers/pci/pcie/aer/aerdrv_acpi.c b/drivers/pci/pcie/aer/aerdrv_acpi.c
new file mode 100644
index 000000000000..fa68e89ebec9
--- /dev/null
+++ b/drivers/pci/pcie/aer/aerdrv_acpi.c
@@ -0,0 +1,68 @@
+/*
+ * Access ACPI _OSC method
+ *
+ * Copyright (C) 2006 Intel Corp.
+ * Tom Long Nguyen (tom.l.nguyen@intel.com)
+ * Zhang Yanmin (yanmin.zhang@intel.com)
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/pm.h>
+#include <linux/suspend.h>
+#include <linux/acpi.h>
+#include <linux/pci-acpi.h>
+#include <linux/delay.h>
+#include "aerdrv.h"
+
+/**
+ * aer_osc_setup - run ACPI _OSC method
+ *
+ * Return:
+ * Zero if success. Nonzero for 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 pci_dev *dev)
+{
+ int retval = OSC_METHOD_RUN_SUCCESS;
+ acpi_status status;
+ acpi_handle handle = DEVICE_ACPI_HANDLE(&dev->dev);
+ struct pci_dev *pdev = dev;
+ struct pci_bus *parent;
+
+ while (!handle) {
+ if (!pdev || !pdev->bus->parent)
+ break;
+ parent = pdev->bus->parent;
+ if (!parent->self)
+ /* Parent must be a host bridge */
+ handle = acpi_get_pci_rootbridge_handle(
+ pci_domain_nr(parent),
+ parent->number);
+ else
+ handle = DEVICE_ACPI_HANDLE(
+ &(parent->self->dev));
+ pdev = parent->self;
+ }
+
+ if (!handle)
+ return OSC_METHOD_NOT_SUPPORTED;
+
+ pci_osc_support_set(OSC_EXT_PCI_CONFIG_SUPPORT);
+ status = pci_osc_control_set(handle, OSC_PCI_EXPRESS_AER_CONTROL |
+ OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL);
+ if (ACPI_FAILURE(status)) {
+ if (status == AE_SUPPORT)
+ retval = OSC_METHOD_NOT_SUPPORTED;
+ else
+ retval = OSC_METHOD_RUN_FAILURE;
+ }
+
+ return retval;
+}
+
diff --git a/drivers/pci/pcie/aer/aerdrv_core.c b/drivers/pci/pcie/aer/aerdrv_core.c
new file mode 100644
index 000000000000..08e13033ced8
--- /dev/null
+++ b/drivers/pci/pcie/aer/aerdrv_core.c
@@ -0,0 +1,758 @@
+/*
+ * drivers/pci/pcie/aer/aerdrv_core.c
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * This file implements the core part of PCI-Express AER. When an pci-express
+ * error is delivered, an error message will be collected and printed to
+ * console, then, an error recovery procedure will be executed by following
+ * the pci error recovery rules.
+ *
+ * Copyright (C) 2006 Intel Corp.
+ * Tom Long Nguyen (tom.l.nguyen@intel.com)
+ * Zhang Yanmin (yanmin.zhang@intel.com)
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/pm.h>
+#include <linux/suspend.h>
+#include <linux/acpi.h>
+#include <linux/pci-acpi.h>
+#include <linux/delay.h>
+#include "aerdrv.h"
+
+static int forceload;
+module_param(forceload, bool, 0);
+
+#define PCI_CFG_SPACE_SIZE (0x100)
+int pci_find_aer_capability(struct pci_dev *dev)
+{
+ int pos;
+ u32 reg32 = 0;
+
+ /* Check if it's a pci-express device */
+ pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
+ if (!pos)
+ return 0;
+
+ /* Check if it supports pci-express AER */
+ pos = PCI_CFG_SPACE_SIZE;
+ while (pos) {
+ if (pci_read_config_dword(dev, pos, &reg32))
+ return 0;
+
+ /* some broken boards return ~0 */
+ if (reg32 == 0xffffffff)
+ return 0;
+
+ if (PCI_EXT_CAP_ID(reg32) == PCI_EXT_CAP_ID_ERR)
+ break;
+
+ pos = reg32 >> 20;
+ }
+
+ return pos;
+}
+
+int pci_enable_pcie_error_reporting(struct pci_dev *dev)
+{
+ u16 reg16 = 0;
+ int pos;
+
+ pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
+ if (!pos)
+ return -EIO;
+
+ pci_read_config_word(dev, pos+PCI_EXP_DEVCTL, &reg16);
+ reg16 = reg16 |
+ PCI_EXP_DEVCTL_CERE |
+ PCI_EXP_DEVCTL_NFERE |
+ PCI_EXP_DEVCTL_FERE |
+ PCI_EXP_DEVCTL_URRE;
+ pci_write_config_word(dev, pos+PCI_EXP_DEVCTL,
+ reg16);
+ return 0;
+}
+
+int pci_disable_pcie_error_reporting(struct pci_dev *dev)
+{
+ u16 reg16 = 0;
+ int pos;
+
+ pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
+ if (!pos)
+ return -EIO;
+
+ pci_read_config_word(dev, pos+PCI_EXP_DEVCTL, &reg16);
+ reg16 = reg16 & ~(PCI_EXP_DEVCTL_CERE |
+ PCI_EXP_DEVCTL_NFERE |
+ PCI_EXP_DEVCTL_FERE |
+ PCI_EXP_DEVCTL_URRE);
+ pci_write_config_word(dev, pos+PCI_EXP_DEVCTL,
+ reg16);
+ return 0;
+}
+
+int pci_cleanup_aer_uncorrect_error_status(struct pci_dev *dev)
+{
+ int pos;
+ u32 status, mask;
+
+ pos = pci_find_aer_capability(dev);
+ if (!pos)
+ return -EIO;
+
+ pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, &status);
+ pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_SEVER, &mask);
+ if (dev->error_state == pci_channel_io_normal)
+ status &= ~mask; /* Clear corresponding nonfatal bits */
+ else
+ status &= mask; /* Clear corresponding fatal bits */
+ pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, status);
+
+ return 0;
+}
+
+static int find_device_iter(struct device *device, void *data)
+{
+ struct pci_dev *dev;
+ u16 id = *(unsigned long *)data;
+ u8 secondary, subordinate, d_bus = id >> 8;
+
+ if (device->bus == &pci_bus_type) {
+ dev = to_pci_dev(device);
+ if (id == ((dev->bus->number << 8) | dev->devfn)) {
+ /*
+ * Device ID match
+ */
+ *(unsigned long*)data = (unsigned long)device;
+ return 1;
+ }
+
+ /*
+ * If device is P2P, check if it is an upstream?
+ */
+ if (dev->hdr_type & PCI_HEADER_TYPE_BRIDGE) {
+ pci_read_config_byte(dev, PCI_SECONDARY_BUS,
+ &secondary);
+ pci_read_config_byte(dev, PCI_SUBORDINATE_BUS,
+ &subordinate);
+ if (d_bus >= secondary && d_bus <= subordinate) {
+ *(unsigned long*)data = (unsigned long)device;
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * find_source_device - search through device hierarchy for source device
+ * @p_dev: pointer to Root Port pci_dev data structure
+ * @id: device ID of agent who sends an error message to this Root Port
+ *
+ * Invoked when error is detected at the Root Port.
+ **/
+static struct device* find_source_device(struct pci_dev *parent, u16 id)
+{
+ struct pci_dev *dev = parent;
+ struct device *device;
+ unsigned long device_addr;
+ int status;
+
+ /* Is Root Port an agent that sends error message? */
+ if (id == ((dev->bus->number << 8) | dev->devfn))
+ return &dev->dev;
+
+ do {
+ device_addr = id;
+ if ((status = device_for_each_child(&dev->dev,
+ &device_addr, find_device_iter))) {
+ device = (struct device*)device_addr;
+ dev = to_pci_dev(device);
+ if (id == ((dev->bus->number << 8) | dev->devfn))
+ return device;
+ }
+ }while (status);
+
+ return NULL;
+}
+
+static void report_error_detected(struct pci_dev *dev, void *data)
+{
+ pci_ers_result_t vote;
+ struct pci_error_handlers *err_handler;
+ struct aer_broadcast_data *result_data;
+ result_data = (struct aer_broadcast_data *) data;
+
+ dev->error_state = result_data->state;
+
+ if (!dev->driver ||
+ !dev->driver->err_handler ||
+ !dev->driver->err_handler->error_detected) {
+ if (result_data->state == pci_channel_io_frozen &&
+ !(dev->hdr_type & PCI_HEADER_TYPE_BRIDGE)) {
+ /*
+ * In case of fatal recovery, if one of down-
+ * stream device has no driver. We might be
+ * unable to recover because a later insmod
+ * of a driver for this device is unaware of
+ * its hw state.
+ */
+ printk(KERN_DEBUG "Device ID[%s] has %s\n",
+ dev->dev.bus_id, (dev->driver) ?
+ "no AER-aware driver" : "no driver");
+ }
+ return;
+ }
+
+ err_handler = dev->driver->err_handler;
+ vote = err_handler->error_detected(dev, result_data->state);
+ result_data->result = merge_result(result_data->result, vote);
+ return;
+}
+
+static void report_mmio_enabled(struct pci_dev *dev, void *data)
+{
+ pci_ers_result_t vote;
+ struct pci_error_handlers *err_handler;
+ struct aer_broadcast_data *result_data;
+ result_data = (struct aer_broadcast_data *) data;
+
+ if (!dev->driver ||
+ !dev->driver->err_handler ||
+ !dev->driver->err_handler->mmio_enabled)
+ return;
+
+ err_handler = dev->driver->err_handler;
+ vote = err_handler->mmio_enabled(dev);
+ result_data->result = merge_result(result_data->result, vote);
+ return;
+}
+
+static void report_slot_reset(struct pci_dev *dev, void *data)
+{
+ pci_ers_result_t vote;
+ struct pci_error_handlers *err_handler;
+ struct aer_broadcast_data *result_data;
+ result_data = (struct aer_broadcast_data *) data;
+
+ if (!dev->driver ||
+ !dev->driver->err_handler ||
+ !dev->driver->err_handler->slot_reset)
+ return;
+
+ err_handler = dev->driver->err_handler;
+ vote = err_handler->slot_reset(dev);
+ result_data->result = merge_result(result_data->result, vote);
+ return;
+}
+
+static void report_resume(struct pci_dev *dev, void *data)
+{
+ struct pci_error_handlers *err_handler;
+
+ dev->error_state = pci_channel_io_normal;
+
+ if (!dev->driver ||
+ !dev->driver->err_handler ||
+ !dev->driver->err_handler->slot_reset)
+ return;
+
+ err_handler = dev->driver->err_handler;
+ err_handler->resume(dev);
+ return;
+}
+
+/**
+ * broadcast_error_message - handle message broadcast to downstream drivers
+ * @device: pointer to from where in a hierarchy message is broadcasted down
+ * @api: callback to be broadcasted
+ * @state: error state
+ *
+ * Invoked during error recovery process. Once being invoked, the content
+ * of error severity will be broadcasted to all downstream drivers in a
+ * hierarchy in question.
+ **/
+static pci_ers_result_t broadcast_error_message(struct pci_dev *dev,
+ enum pci_channel_state state,
+ char *error_mesg,
+ void (*cb)(struct pci_dev *, void *))
+{
+ struct aer_broadcast_data result_data;
+
+ printk(KERN_DEBUG "Broadcast %s message\n", error_mesg);
+ result_data.state = state;
+ if (cb == report_error_detected)
+ result_data.result = PCI_ERS_RESULT_CAN_RECOVER;
+ else
+ result_data.result = PCI_ERS_RESULT_RECOVERED;
+
+ if (dev->hdr_type & PCI_HEADER_TYPE_BRIDGE) {
+ /*
+ * If the error is reported by a bridge, we think this error
+ * is related to the downstream link of the bridge, so we
+ * do error recovery on all subordinates of the bridge instead
+ * of the bridge and clear the error status of the bridge.
+ */
+ if (cb == report_error_detected)
+ dev->error_state = state;
+ pci_walk_bus(dev->subordinate, cb, &result_data);
+ if (cb == report_resume) {
+ pci_cleanup_aer_uncorrect_error_status(dev);
+ dev->error_state = pci_channel_io_normal;
+ }
+ }
+ else {
+ /*
+ * If the error is reported by an end point, we think this
+ * error is related to the upstream link of the end point.
+ */
+ pci_walk_bus(dev->bus, cb, &result_data);
+ }
+
+ return result_data.result;
+}
+
+struct find_aer_service_data {
+ struct pcie_port_service_driver *aer_driver;
+ int is_downstream;
+};
+
+static int find_aer_service_iter(struct device *device, void *data)
+{
+ struct device_driver *driver;
+ struct pcie_port_service_driver *service_driver;
+ struct pcie_device *pcie_dev;
+ struct find_aer_service_data *result;
+
+ result = (struct find_aer_service_data *) data;
+
+ if (device->bus == &pcie_port_bus_type) {
+ pcie_dev = to_pcie_device(device);
+ if (pcie_dev->id.port_type == PCIE_SW_DOWNSTREAM_PORT)
+ result->is_downstream = 1;
+
+ driver = device->driver;
+ if (driver) {
+ service_driver = to_service_driver(driver);
+ if (service_driver->id_table->service_type ==
+ PCIE_PORT_SERVICE_AER) {
+ result->aer_driver = service_driver;
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static void find_aer_service(struct pci_dev *dev,
+ struct find_aer_service_data *data)
+{
+ int retval;
+ retval = device_for_each_child(&dev->dev, data, find_aer_service_iter);
+}
+
+static pci_ers_result_t reset_link(struct pcie_device *aerdev,
+ struct pci_dev *dev)
+{
+ struct pci_dev *udev;
+ pci_ers_result_t status;
+ struct find_aer_service_data data;
+
+ if (dev->hdr_type & PCI_HEADER_TYPE_BRIDGE)
+ udev = dev;
+ else
+ udev= dev->bus->self;
+
+ data.is_downstream = 0;
+ data.aer_driver = NULL;
+ find_aer_service(udev, &data);
+
+ /*
+ * Use the aer driver of the error agent firstly.
+ * If it hasn't the aer driver, use the root port's
+ */
+ if (!data.aer_driver || !data.aer_driver->reset_link) {
+ if (data.is_downstream &&
+ aerdev->device.driver &&
+ to_service_driver(aerdev->device.driver)->reset_link) {
+ data.aer_driver =
+ to_service_driver(aerdev->device.driver);
+ } else {
+ printk(KERN_DEBUG "No link-reset support to Device ID"
+ "[%s]\n",
+ dev->dev.bus_id);
+ return PCI_ERS_RESULT_DISCONNECT;
+ }
+ }
+
+ status = data.aer_driver->reset_link(udev);
+ if (status != PCI_ERS_RESULT_RECOVERED) {
+ printk(KERN_DEBUG "Link reset at upstream Device ID"
+ "[%s] failed\n",
+ udev->dev.bus_id);
+ return PCI_ERS_RESULT_DISCONNECT;
+ }
+
+ return status;
+}
+
+/**
+ * do_recovery - handle nonfatal/fatal error recovery process
+ * @aerdev: pointer to a pcie_device data structure of root port
+ * @dev: pointer to a pci_dev data structure of agent detecting an error
+ * @severity: error severity type
+ *
+ * Invoked when an error is nonfatal/fatal. Once being invoked, broadcast
+ * error detected message to all downstream drivers within a hierarchy in
+ * question and return the returned code.
+ **/
+static pci_ers_result_t do_recovery(struct pcie_device *aerdev,
+ struct pci_dev *dev,
+ int severity)
+{
+ pci_ers_result_t status, result = PCI_ERS_RESULT_RECOVERED;
+ enum pci_channel_state state;
+
+ if (severity == AER_FATAL)
+ state = pci_channel_io_frozen;
+ else
+ state = pci_channel_io_normal;
+
+ status = broadcast_error_message(dev,
+ state,
+ "error_detected",
+ report_error_detected);
+
+ if (severity == AER_FATAL) {
+ result = reset_link(aerdev, dev);
+ if (result != PCI_ERS_RESULT_RECOVERED) {
+ /* TODO: Should panic here? */
+ return result;
+ }
+ }
+
+ if (status == PCI_ERS_RESULT_CAN_RECOVER)
+ status = broadcast_error_message(dev,
+ state,
+ "mmio_enabled",
+ report_mmio_enabled);
+
+ if (status == PCI_ERS_RESULT_NEED_RESET) {
+ /*
+ * TODO: Should call platform-specific
+ * functions to reset slot before calling
+ * drivers' slot_reset callbacks?
+ */
+ status = broadcast_error_message(dev,
+ state,
+ "slot_reset",
+ report_slot_reset);
+ }
+
+ if (status == PCI_ERS_RESULT_RECOVERED)
+ broadcast_error_message(dev,
+ state,
+ "resume",
+ report_resume);
+
+ return status;
+}
+
+/**
+ * handle_error_source - handle logging error into an event log
+ * @aerdev: pointer to pcie_device data structure of the root port
+ * @dev: pointer to pci_dev data structure of error source device
+ * @info: comprehensive error information
+ *
+ * Invoked when an error being detected by Root Port.
+ **/
+static void handle_error_source(struct pcie_device * aerdev,
+ struct pci_dev *dev,
+ struct aer_err_info info)
+{
+ pci_ers_result_t status = 0;
+ int pos;
+
+ if (info.severity == AER_CORRECTABLE) {
+ /*
+ * Correctable error does not need software intevention.
+ * No need to go through error recovery process.
+ */
+ pos = pci_find_aer_capability(dev);
+ if (pos)
+ pci_write_config_dword(dev, pos + PCI_ERR_COR_STATUS,
+ info.status);
+ } else {
+ status = do_recovery(aerdev, dev, info.severity);
+ if (status == PCI_ERS_RESULT_RECOVERED) {
+ printk(KERN_DEBUG "AER driver successfully recovered\n");
+ } else {
+ /* TODO: Should kernel panic here? */
+ printk(KERN_DEBUG "AER driver didn't recover\n");
+ }
+ }
+}
+
+/**
+ * aer_enable_rootport - enable Root Port's interrupts when receiving messages
+ * @rpc: pointer to a Root Port data structure
+ *
+ * Invoked when PCIE bus loads AER service driver.
+ **/
+void aer_enable_rootport(struct aer_rpc *rpc)
+{
+ struct pci_dev *pdev = rpc->rpd->port;
+ int pos, aer_pos;
+ u16 reg16;
+ u32 reg32;
+
+ pos = pci_find_capability(pdev, PCI_CAP_ID_EXP);
+ /* Clear PCIE Capability's Device Status */
+ pci_read_config_word(pdev, pos+PCI_EXP_DEVSTA, &reg16);
+ pci_write_config_word(pdev, pos+PCI_EXP_DEVSTA, reg16);
+
+ /* Disable system error generation in response to error messages */
+ pci_read_config_word(pdev, pos + PCI_EXP_RTCTL, &reg16);
+ reg16 &= ~(SYSTEM_ERROR_INTR_ON_MESG_MASK);
+ pci_write_config_word(pdev, pos + PCI_EXP_RTCTL, reg16);
+
+ aer_pos = pci_find_aer_capability(pdev);
+ /* Clear error status */
+ pci_read_config_dword(pdev, aer_pos + PCI_ERR_ROOT_STATUS, &reg32);
+ pci_write_config_dword(pdev, aer_pos + PCI_ERR_ROOT_STATUS, reg32);
+ pci_read_config_dword(pdev, aer_pos + PCI_ERR_COR_STATUS, &reg32);
+ pci_write_config_dword(pdev, aer_pos + PCI_ERR_COR_STATUS, reg32);
+ pci_read_config_dword(pdev, aer_pos + PCI_ERR_UNCOR_STATUS, &reg32);
+ pci_write_config_dword(pdev, aer_pos + PCI_ERR_UNCOR_STATUS, reg32);
+
+ /* Enable Root Port device reporting error itself */
+ pci_read_config_word(pdev, pos+PCI_EXP_DEVCTL, &reg16);
+ reg16 = reg16 |
+ PCI_EXP_DEVCTL_CERE |
+ PCI_EXP_DEVCTL_NFERE |
+ PCI_EXP_DEVCTL_FERE |
+ PCI_EXP_DEVCTL_URRE;
+ pci_write_config_word(pdev, pos+PCI_EXP_DEVCTL,
+ reg16);
+
+ /* Enable Root Port's interrupt in response to error messages */
+ pci_write_config_dword(pdev,
+ aer_pos + PCI_ERR_ROOT_COMMAND,
+ ROOT_PORT_INTR_ON_MESG_MASK);
+}
+
+/**
+ * disable_root_aer - disable Root Port's interrupts when receiving messages
+ * @rpc: pointer to a Root Port data structure
+ *
+ * Invoked when PCIE bus unloads AER service driver.
+ **/
+static void disable_root_aer(struct aer_rpc *rpc)
+{
+ struct pci_dev *pdev = rpc->rpd->port;
+ u32 reg32;
+ int pos;
+
+ pos = pci_find_aer_capability(pdev);
+ /* Disable Root's interrupt in response to error messages */
+ pci_write_config_dword(pdev, pos + PCI_ERR_ROOT_COMMAND, 0);
+
+ /* Clear Root's error status reg */
+ pci_read_config_dword(pdev, pos + PCI_ERR_ROOT_STATUS, &reg32);
+ pci_write_config_dword(pdev, pos + PCI_ERR_ROOT_STATUS, reg32);
+}
+
+/**
+ * get_e_source - retrieve an error source
+ * @rpc: pointer to the root port which holds an error
+ *
+ * Invoked by DPC handler to consume an error.
+ **/
+static struct aer_err_source* get_e_source(struct aer_rpc *rpc)
+{
+ struct aer_err_source *e_source;
+ unsigned long flags;
+
+ /* Lock access to Root error producer/consumer index */
+ spin_lock_irqsave(&rpc->e_lock, flags);
+ if (rpc->prod_idx == rpc->cons_idx) {
+ spin_unlock_irqrestore(&rpc->e_lock, flags);
+ return NULL;
+ }
+ e_source = &rpc->e_sources[rpc->cons_idx];
+ rpc->cons_idx++;
+ if (rpc->cons_idx == AER_ERROR_SOURCES_MAX)
+ rpc->cons_idx = 0;
+ spin_unlock_irqrestore(&rpc->e_lock, flags);
+
+ return e_source;
+}
+
+static int get_device_error_info(struct pci_dev *dev, struct aer_err_info *info)
+{
+ int pos;
+
+ pos = pci_find_aer_capability(dev);
+
+ /* The device might not support AER */
+ if (!pos)
+ return AER_SUCCESS;
+
+ if (info->severity == AER_CORRECTABLE) {
+ pci_read_config_dword(dev, pos + PCI_ERR_COR_STATUS,
+ &info->status);
+ if (!(info->status & ERR_CORRECTABLE_ERROR_MASK))
+ return AER_UNSUCCESS;
+ } else if (dev->hdr_type & PCI_HEADER_TYPE_BRIDGE ||
+ info->severity == AER_NONFATAL) {
+
+ /* Link is still healthy for IO reads */
+ pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS,
+ &info->status);
+ if (!(info->status & ERR_UNCORRECTABLE_ERROR_MASK))
+ return AER_UNSUCCESS;
+
+ if (info->status & AER_LOG_TLP_MASKS) {
+ info->flags |= AER_TLP_HEADER_VALID_FLAG;
+ pci_read_config_dword(dev,
+ pos + PCI_ERR_HEADER_LOG, &info->tlp.dw0);
+ pci_read_config_dword(dev,
+ pos + PCI_ERR_HEADER_LOG + 4, &info->tlp.dw1);
+ pci_read_config_dword(dev,
+ pos + PCI_ERR_HEADER_LOG + 8, &info->tlp.dw2);
+ pci_read_config_dword(dev,
+ pos + PCI_ERR_HEADER_LOG + 12, &info->tlp.dw3);
+ }
+ }
+
+ return AER_SUCCESS;
+}
+
+/**
+ * aer_isr_one_error - consume an error detected by root port
+ * @p_device: pointer to error root port service device
+ * @e_src: pointer to an error source
+ **/
+static void aer_isr_one_error(struct pcie_device *p_device,
+ struct aer_err_source *e_src)
+{
+ struct device *s_device;
+ struct aer_err_info e_info = {0, 0, 0,};
+ int i;
+ u16 id;
+
+ /*
+ * There is a possibility that both correctable error and
+ * uncorrectable error being logged. Report correctable error first.
+ */
+ for (i = 1; i & ROOT_ERR_STATUS_MASKS ; i <<= 2) {
+ if (i > 4)
+ break;
+ if (!(e_src->status & i))
+ continue;
+
+ /* Init comprehensive error information */
+ if (i & PCI_ERR_ROOT_COR_RCV) {
+ id = ERR_COR_ID(e_src->id);
+ e_info.severity = AER_CORRECTABLE;
+ } else {
+ id = ERR_UNCOR_ID(e_src->id);
+ e_info.severity = ((e_src->status >> 6) & 1);
+ }
+ if (e_src->status &
+ (PCI_ERR_ROOT_MULTI_COR_RCV |
+ PCI_ERR_ROOT_MULTI_UNCOR_RCV))
+ e_info.flags |= AER_MULTI_ERROR_VALID_FLAG;
+ if (!(s_device = find_source_device(p_device->port, id))) {
+ printk(KERN_DEBUG "%s->can't find device of ID%04x\n",
+ __FUNCTION__, id);
+ continue;
+ }
+ if (get_device_error_info(to_pci_dev(s_device), &e_info) ==
+ AER_SUCCESS) {
+ aer_print_error(to_pci_dev(s_device), &e_info);
+ handle_error_source(p_device,
+ to_pci_dev(s_device),
+ e_info);
+ }
+ }
+}
+
+/**
+ * aer_isr - consume errors detected by root port
+ * @work: definition of this work item
+ *
+ * Invoked, as DPC, when root port records new detected error
+ **/
+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;
+
+ mutex_lock(&rpc->rpc_mutex);
+ e_src = get_e_source(rpc);
+ while (e_src) {
+ aer_isr_one_error(p_device, e_src);
+ e_src = get_e_source(rpc);
+ }
+ mutex_unlock(&rpc->rpc_mutex);
+
+ wake_up(&rpc->wait_release);
+}
+
+/**
+ * aer_delete_rootport - disable root port aer and delete service data
+ * @rpc: pointer to a root port device being deleted
+ *
+ * Invoked when AER service unloaded on a specific Root Port
+ **/
+void aer_delete_rootport(struct aer_rpc *rpc)
+{
+ /* Disable root port AER itself */
+ disable_root_aer(rpc);
+
+ kfree(rpc);
+}
+
+/**
+ * aer_init - provide AER initialization
+ * @dev: pointer to AER pcie device
+ *
+ * Invoked when AER service driver is loaded.
+ **/
+int aer_init(struct pcie_device *dev)
+{
+ int status;
+
+ /* Run _OSC Method */
+ status = aer_osc_setup(dev->port);
+
+ if(status != OSC_METHOD_RUN_SUCCESS) {
+ printk(KERN_DEBUG "%s: AER service init fails - %s\n",
+ __FUNCTION__,
+ (status == OSC_METHOD_NOT_SUPPORTED) ?
+ "No ACPI _OSC support" : "Run ACPI _OSC fails");
+
+ if (!forceload)
+ return status;
+ }
+
+ return AER_SUCCESS;
+}
+
+EXPORT_SYMBOL_GPL(pci_find_aer_capability);
+EXPORT_SYMBOL_GPL(pci_enable_pcie_error_reporting);
+EXPORT_SYMBOL_GPL(pci_disable_pcie_error_reporting);
+EXPORT_SYMBOL_GPL(pci_cleanup_aer_uncorrect_error_status);
+
diff --git a/drivers/pci/pcie/aer/aerdrv_errprint.c b/drivers/pci/pcie/aer/aerdrv_errprint.c
new file mode 100644
index 000000000000..3933d4f30e8c
--- /dev/null
+++ b/drivers/pci/pcie/aer/aerdrv_errprint.c
@@ -0,0 +1,248 @@
+/*
+ * drivers/pci/pcie/aer/aerdrv_errprint.c
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Format error messages and print them to console.
+ *
+ * Copyright (C) 2006 Intel Corp.
+ * Tom Long Nguyen (tom.l.nguyen@intel.com)
+ * Zhang Yanmin (yanmin.zhang@intel.com)
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/pm.h>
+#include <linux/suspend.h>
+
+#include "aerdrv.h"
+
+#define AER_AGENT_RECEIVER 0
+#define AER_AGENT_REQUESTER 1
+#define AER_AGENT_COMPLETER 2
+#define AER_AGENT_TRANSMITTER 3
+
+#define AER_AGENT_REQUESTER_MASK (PCI_ERR_UNC_COMP_TIME| \
+ PCI_ERR_UNC_UNSUP)
+
+#define AER_AGENT_COMPLETER_MASK PCI_ERR_UNC_COMP_ABORT
+
+#define AER_AGENT_TRANSMITTER_MASK(t, e) (e & (PCI_ERR_COR_REP_ROLL| \
+ ((t == AER_CORRECTABLE) ? PCI_ERR_COR_REP_TIMER: 0)))
+
+#define AER_GET_AGENT(t, e) \
+ ((e & AER_AGENT_COMPLETER_MASK) ? AER_AGENT_COMPLETER : \
+ (e & AER_AGENT_REQUESTER_MASK) ? AER_AGENT_REQUESTER : \
+ (AER_AGENT_TRANSMITTER_MASK(t, e)) ? AER_AGENT_TRANSMITTER : \
+ AER_AGENT_RECEIVER)
+
+#define AER_PHYSICAL_LAYER_ERROR_MASK PCI_ERR_COR_RCVR
+#define AER_DATA_LINK_LAYER_ERROR_MASK(t, e) \
+ (PCI_ERR_UNC_DLP| \
+ PCI_ERR_COR_BAD_TLP| \
+ PCI_ERR_COR_BAD_DLLP| \
+ PCI_ERR_COR_REP_ROLL| \
+ ((t == AER_CORRECTABLE) ? \
+ PCI_ERR_COR_REP_TIMER: 0))
+
+#define AER_PHYSICAL_LAYER_ERROR 0
+#define AER_DATA_LINK_LAYER_ERROR 1
+#define AER_TRANSACTION_LAYER_ERROR 2
+
+#define AER_GET_LAYER_ERROR(t, e) \
+ ((e & AER_PHYSICAL_LAYER_ERROR_MASK) ? \
+ AER_PHYSICAL_LAYER_ERROR : \
+ (e & AER_DATA_LINK_LAYER_ERROR_MASK(t, e)) ? \
+ AER_DATA_LINK_LAYER_ERROR : \
+ AER_TRANSACTION_LAYER_ERROR)
+
+/*
+ * AER error strings
+ */
+static char* aer_error_severity_string[] = {
+ "Uncorrected (Non-Fatal)",
+ "Uncorrected (Fatal)",
+ "Corrected"
+};
+
+static char* aer_error_layer[] = {
+ "Physical Layer",
+ "Data Link Layer",
+ "Transaction Layer"
+};
+static char* aer_correctable_error_string[] = {
+ "Receiver Error ", /* Bit Position 0 */
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "Bad TLP ", /* Bit Position 6 */
+ "Bad DLLP ", /* Bit Position 7 */
+ "RELAY_NUM Rollover ", /* Bit Position 8 */
+ NULL,
+ NULL,
+ NULL,
+ "Replay Timer Timeout ", /* Bit Position 12 */
+ "Advisory Non-Fatal ", /* Bit Position 13 */
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+};
+
+static char* aer_uncorrectable_error_string[] = {
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "Data Link Protocol ", /* Bit Position 4 */
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "Poisoned TLP ", /* Bit Position 12 */
+ "Flow Control Protocol ", /* Bit Position 13 */
+ "Completion Timeout ", /* Bit Position 14 */
+ "Completer Abort ", /* Bit Position 15 */
+ "Unexpected Completion ", /* Bit Position 16 */
+ "Receiver Overflow ", /* Bit Position 17 */
+ "Malformed TLP ", /* Bit Position 18 */
+ "ECRC ", /* Bit Position 19 */
+ "Unsupported Request ", /* Bit Position 20 */
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+};
+
+static char* aer_agent_string[] = {
+ "Receiver ID",
+ "Requester ID",
+ "Completer ID",
+ "Transmitter ID"
+};
+
+static char * aer_get_error_source_name(int severity,
+ unsigned int status,
+ char errmsg_buff[])
+{
+ int i;
+ char * errmsg = NULL;
+
+ for (i = 0; i < 32; i++) {
+ if (!(status & (1 << i)))
+ continue;
+
+ if (severity == AER_CORRECTABLE)
+ errmsg = aer_correctable_error_string[i];
+ else
+ errmsg = aer_uncorrectable_error_string[i];
+
+ if (!errmsg) {
+ sprintf(errmsg_buff, "Unknown Error Bit %2d ", i);
+ errmsg = errmsg_buff;
+ }
+
+ break;
+ }
+
+ return errmsg;
+}
+
+static DEFINE_SPINLOCK(logbuf_lock);
+static char errmsg_buff[100];
+void aer_print_error(struct pci_dev *dev, struct aer_err_info *info)
+{
+ char * errmsg;
+ int err_layer, agent;
+ char * loglevel;
+
+ if (info->severity == AER_CORRECTABLE)
+ loglevel = KERN_WARNING;
+ else
+ loglevel = KERN_ERR;
+
+ printk("%s+------ PCI-Express Device Error ------+\n", loglevel);
+ printk("%sError Severity\t\t: %s\n", loglevel,
+ aer_error_severity_string[info->severity]);
+
+ if ( info->status == 0) {
+ printk("%sPCIE Bus Error type\t: (Unaccessible)\n", loglevel);
+ printk("%sUnaccessible Received\t: %s\n", loglevel,
+ info->flags & AER_MULTI_ERROR_VALID_FLAG ?
+ "Multiple" : "First");
+ printk("%sUnregistered Agent ID\t: %04x\n", loglevel,
+ (dev->bus->number << 8) | dev->devfn);
+ } else {
+ err_layer = AER_GET_LAYER_ERROR(info->severity, info->status);
+ printk("%sPCIE Bus Error type\t: %s\n", loglevel,
+ aer_error_layer[err_layer]);
+
+ spin_lock(&logbuf_lock);
+ errmsg = aer_get_error_source_name(info->severity,
+ info->status,
+ errmsg_buff);
+ printk("%s%s\t: %s\n", loglevel, errmsg,
+ info->flags & AER_MULTI_ERROR_VALID_FLAG ?
+ "Multiple" : "First");
+ spin_unlock(&logbuf_lock);
+
+ agent = AER_GET_AGENT(info->severity, info->status);
+ printk("%s%s\t\t: %04x\n", loglevel,
+ aer_agent_string[agent],
+ (dev->bus->number << 8) | dev->devfn);
+
+ printk("%sVendorID=%04xh, DeviceID=%04xh,"
+ " Bus=%02xh, Device=%02xh, Function=%02xh\n",
+ loglevel,
+ dev->vendor,
+ dev->device,
+ dev->bus->number,
+ PCI_SLOT(dev->devfn),
+ PCI_FUNC(dev->devfn));
+
+ if (info->flags & AER_TLP_HEADER_VALID_FLAG) {
+ unsigned char *tlp = (unsigned char *) &info->tlp;
+ printk("%sTLB Header:\n", loglevel);
+ printk("%s%02x%02x%02x%02x %02x%02x%02x%02x"
+ " %02x%02x%02x%02x %02x%02x%02x%02x\n",
+ loglevel,
+ *(tlp + 3), *(tlp + 2), *(tlp + 1), *tlp,
+ *(tlp + 7), *(tlp + 6), *(tlp + 5), *(tlp + 4),
+ *(tlp + 11), *(tlp + 10), *(tlp + 9),
+ *(tlp + 8), *(tlp + 15), *(tlp + 14),
+ *(tlp + 13), *(tlp + 12));
+ }
+ }
+}
+
diff --git a/drivers/pci/pcie/portdrv.h b/drivers/pci/pcie/portdrv.h
index 1d317d22ee89..3656e0349dd1 100644
--- a/drivers/pci/pcie/portdrv.h
+++ b/drivers/pci/pcie/portdrv.h
@@ -9,6 +9,8 @@
#ifndef _PORTDRV_H_
#define _PORTDRV_H_
+#include <linux/compiler.h>
+
#if !defined(PCI_CAP_ID_PME)
#define PCI_CAP_ID_PME 1
#endif
@@ -39,7 +41,7 @@ extern int pcie_port_device_suspend(struct pci_dev *dev, pm_message_t state);
extern int pcie_port_device_resume(struct pci_dev *dev);
#endif
extern void pcie_port_device_remove(struct pci_dev *dev);
-extern void pcie_port_bus_register(void);
+extern int __must_check pcie_port_bus_register(void);
extern void pcie_port_bus_unregister(void);
#endif /* _PORTDRV_H_ */
diff --git a/drivers/pci/pcie/portdrv_bus.c b/drivers/pci/pcie/portdrv_bus.c
index 3e84b501e6a4..3f0976868eda 100644
--- a/drivers/pci/pcie/portdrv_bus.c
+++ b/drivers/pci/pcie/portdrv_bus.c
@@ -24,6 +24,7 @@ struct bus_type pcie_port_bus_type = {
.suspend = pcie_port_bus_suspend,
.resume = pcie_port_bus_resume,
};
+EXPORT_SYMBOL_GPL(pcie_port_bus_type);
static int pcie_port_bus_match(struct device *dev, struct device_driver *drv)
{
diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c
index 55c662267868..b20a9b81dae2 100644
--- a/drivers/pci/pcie/portdrv_core.c
+++ b/drivers/pci/pcie/portdrv_core.c
@@ -339,8 +339,7 @@ static int suspend_iter(struct device *dev, void *data)
int pcie_port_device_suspend(struct pci_dev *dev, pm_message_t state)
{
- device_for_each_child(&dev->dev, &state, suspend_iter);
- return 0;
+ return device_for_each_child(&dev->dev, &state, suspend_iter);
}
static int resume_iter(struct device *dev, void *data)
@@ -358,8 +357,7 @@ static int resume_iter(struct device *dev, void *data)
int pcie_port_device_resume(struct pci_dev *dev)
{
- device_for_each_child(&dev->dev, NULL, resume_iter);
- return 0;
+ return device_for_each_child(&dev->dev, NULL, resume_iter);
}
#endif
@@ -402,9 +400,9 @@ void pcie_port_device_remove(struct pci_dev *dev)
pci_disable_msi(dev);
}
-void pcie_port_bus_register(void)
+int pcie_port_bus_register(void)
{
- bus_register(&pcie_port_bus_type);
+ return bus_register(&pcie_port_bus_type);
}
void pcie_port_bus_unregister(void)
diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c
index 478d0d28f7ad..b4da7954611e 100644
--- a/drivers/pci/pcie/portdrv_pci.c
+++ b/drivers/pci/pcie/portdrv_pci.c
@@ -14,8 +14,10 @@
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/pcieport_if.h>
+#include <linux/aer.h>
#include "portdrv.h"
+#include "aer/aerdrv.h"
/*
* Version Information
@@ -30,6 +32,43 @@ MODULE_LICENSE("GPL");
/* global data */
static const char device_name[] = "pcieport-driver";
+static int pcie_portdrv_save_config(struct pci_dev *dev)
+{
+ return pci_save_state(dev);
+}
+
+static int pcie_portdrv_restore_config(struct pci_dev *dev)
+{
+ int retval;
+
+ pci_restore_state(dev);
+ retval = pci_enable_device(dev);
+ if (retval)
+ return retval;
+ pci_set_master(dev);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int pcie_portdrv_suspend(struct pci_dev *dev, pm_message_t state)
+{
+ int ret = pcie_port_device_suspend(dev, state);
+
+ if (!ret)
+ ret = pcie_portdrv_save_config(dev);
+ return ret;
+}
+
+static int pcie_portdrv_resume(struct pci_dev *dev)
+{
+ pcie_portdrv_restore_config(dev);
+ return pcie_port_device_resume(dev);
+}
+#else
+#define pcie_portdrv_suspend NULL
+#define pcie_portdrv_resume NULL
+#endif
+
/*
* pcie_portdrv_probe - Probe PCI-Express port devices
* @dev: PCI-Express port device being probed
@@ -61,6 +100,10 @@ static int __devinit pcie_portdrv_probe (struct pci_dev *dev,
return -ENOMEM;
}
+ pcie_portdrv_save_config(dev);
+
+ pci_enable_pcie_error_reporting(dev);
+
return 0;
}
@@ -70,39 +113,151 @@ static void pcie_portdrv_remove (struct pci_dev *dev)
kfree(pci_get_drvdata(dev));
}
-#ifdef CONFIG_PM
-static int pcie_portdrv_save_config(struct pci_dev *dev)
+static int error_detected_iter(struct device *device, void *data)
{
- return pci_save_state(dev);
+ struct pcie_device *pcie_device;
+ struct pcie_port_service_driver *driver;
+ struct aer_broadcast_data *result_data;
+ pci_ers_result_t status;
+
+ result_data = (struct aer_broadcast_data *) data;
+
+ if (device->bus == &pcie_port_bus_type && device->driver) {
+ driver = to_service_driver(device->driver);
+ if (!driver ||
+ !driver->err_handler ||
+ !driver->err_handler->error_detected)
+ return 0;
+
+ pcie_device = to_pcie_device(device);
+
+ /* Forward error detected message to service drivers */
+ status = driver->err_handler->error_detected(
+ pcie_device->port,
+ result_data->state);
+ result_data->result =
+ merge_result(result_data->result, status);
+ }
+
+ return 0;
}
-static int pcie_portdrv_restore_config(struct pci_dev *dev)
+static pci_ers_result_t pcie_portdrv_error_detected(struct pci_dev *dev,
+ enum pci_channel_state error)
{
+ struct aer_broadcast_data result_data =
+ {error, PCI_ERS_RESULT_CAN_RECOVER};
int retval;
- pci_restore_state(dev);
- retval = pci_enable_device(dev);
- if (retval)
- return retval;
- pci_set_master(dev);
+ /* can not fail */
+ retval = device_for_each_child(&dev->dev, &result_data, error_detected_iter);
+
+ return result_data.result;
+}
+
+static int mmio_enabled_iter(struct device *device, void *data)
+{
+ struct pcie_device *pcie_device;
+ struct pcie_port_service_driver *driver;
+ pci_ers_result_t status, *result;
+
+ result = (pci_ers_result_t *) data;
+
+ if (device->bus == &pcie_port_bus_type && device->driver) {
+ driver = to_service_driver(device->driver);
+ if (driver &&
+ driver->err_handler &&
+ driver->err_handler->mmio_enabled) {
+ pcie_device = to_pcie_device(device);
+
+ /* Forward error message to service drivers */
+ status = driver->err_handler->mmio_enabled(
+ pcie_device->port);
+ *result = merge_result(*result, status);
+ }
+ }
+
return 0;
}
-static int pcie_portdrv_suspend (struct pci_dev *dev, pm_message_t state)
+static pci_ers_result_t pcie_portdrv_mmio_enabled(struct pci_dev *dev)
{
- int ret = pcie_port_device_suspend(dev, state);
+ pci_ers_result_t status = PCI_ERS_RESULT_RECOVERED;
+ int retval;
- if (!ret)
- ret = pcie_portdrv_save_config(dev);
- return ret;
+ /* get true return value from &status */
+ retval = device_for_each_child(&dev->dev, &status, mmio_enabled_iter);
+ return status;
}
-static int pcie_portdrv_resume (struct pci_dev *dev)
+static int slot_reset_iter(struct device *device, void *data)
{
- pcie_portdrv_restore_config(dev);
- return pcie_port_device_resume(dev);
+ struct pcie_device *pcie_device;
+ struct pcie_port_service_driver *driver;
+ pci_ers_result_t status, *result;
+
+ result = (pci_ers_result_t *) data;
+
+ if (device->bus == &pcie_port_bus_type && device->driver) {
+ driver = to_service_driver(device->driver);
+ if (driver &&
+ driver->err_handler &&
+ driver->err_handler->slot_reset) {
+ pcie_device = to_pcie_device(device);
+
+ /* Forward error message to service drivers */
+ status = driver->err_handler->slot_reset(
+ pcie_device->port);
+ *result = merge_result(*result, status);
+ }
+ }
+
+ return 0;
+}
+
+static pci_ers_result_t pcie_portdrv_slot_reset(struct pci_dev *dev)
+{
+ pci_ers_result_t status;
+ int retval;
+
+ /* If fatal, restore cfg space for possible link reset at upstream */
+ if (dev->error_state == pci_channel_io_frozen) {
+ pcie_portdrv_restore_config(dev);
+ pci_enable_pcie_error_reporting(dev);
+ }
+
+ /* get true return value from &status */
+ retval = device_for_each_child(&dev->dev, &status, slot_reset_iter);
+
+ return status;
+}
+
+static int resume_iter(struct device *device, void *data)
+{
+ struct pcie_device *pcie_device;
+ struct pcie_port_service_driver *driver;
+
+ if (device->bus == &pcie_port_bus_type && device->driver) {
+ driver = to_service_driver(device->driver);
+ if (driver &&
+ driver->err_handler &&
+ driver->err_handler->resume) {
+ pcie_device = to_pcie_device(device);
+
+ /* Forward error message to service drivers */
+ driver->err_handler->resume(pcie_device->port);
+ }
+ }
+
+ return 0;
+}
+
+static void pcie_portdrv_err_resume(struct pci_dev *dev)
+{
+ int retval;
+ /* nothing to do with error value, if it ever happens */
+ retval = device_for_each_child(&dev->dev, NULL, resume_iter);
}
-#endif
/*
* LINUX Device Driver Model
@@ -114,6 +269,13 @@ static const struct pci_device_id port_pci_ids[] = { {
};
MODULE_DEVICE_TABLE(pci, port_pci_ids);
+static struct pci_error_handlers pcie_portdrv_err_handler = {
+ .error_detected = pcie_portdrv_error_detected,
+ .mmio_enabled = pcie_portdrv_mmio_enabled,
+ .slot_reset = pcie_portdrv_slot_reset,
+ .resume = pcie_portdrv_err_resume,
+};
+
static struct pci_driver pcie_portdrv = {
.name = (char *)device_name,
.id_table = &port_pci_ids[0],
@@ -121,20 +283,25 @@ static struct pci_driver pcie_portdrv = {
.probe = pcie_portdrv_probe,
.remove = pcie_portdrv_remove,
-#ifdef CONFIG_PM
.suspend = pcie_portdrv_suspend,
.resume = pcie_portdrv_resume,
-#endif /* PM */
+
+ .err_handler = &pcie_portdrv_err_handler,
};
static int __init pcie_portdrv_init(void)
{
- int retval = 0;
+ int retval;
- pcie_port_bus_register();
+ retval = pcie_port_bus_register();
+ if (retval) {
+ printk(KERN_WARNING "PCIE: bus_register error: %d\n", retval);
+ goto out;
+ }
retval = pci_register_driver(&pcie_portdrv);
if (retval)
pcie_port_bus_unregister();
+ out:
return retval;
}
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index c5a58d1c6c1c..6a3c1e728900 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -339,6 +339,7 @@ pci_alloc_child_bus(struct pci_bus *parent, struct pci_dev *bridge, int busnr)
{
struct pci_bus *child;
int i;
+ int retval;
/*
* Allocate a new bus, and inherit stuff from the parent..
@@ -356,8 +357,13 @@ pci_alloc_child_bus(struct pci_bus *parent, struct pci_dev *bridge, int busnr)
child->class_dev.class = &pcibus_class;
sprintf(child->class_dev.class_id, "%04x:%02x", pci_domain_nr(child), busnr);
- class_device_register(&child->class_dev);
- class_device_create_file(&child->class_dev, &class_device_attr_cpuaffinity);
+ retval = class_device_register(&child->class_dev);
+ if (retval)
+ goto error_register;
+ retval = class_device_create_file(&child->class_dev,
+ &class_device_attr_cpuaffinity);
+ if (retval)
+ goto error_file_create;
/*
* Set up the primary, secondary and subordinate
@@ -375,6 +381,12 @@ pci_alloc_child_bus(struct pci_bus *parent, struct pci_dev *bridge, int busnr)
bridge->subordinate = child;
return child;
+
+error_file_create:
+ class_device_unregister(&child->class_dev);
+error_register:
+ kfree(child);
+ return NULL;
}
struct pci_bus * __devinit pci_add_new_bus(struct pci_bus *parent, struct pci_dev *dev, int busnr)
@@ -667,6 +679,33 @@ static int pci_setup_device(struct pci_dev * dev)
pci_read_bases(dev, 6, PCI_ROM_ADDRESS);
pci_read_config_word(dev, PCI_SUBSYSTEM_VENDOR_ID, &dev->subsystem_vendor);
pci_read_config_word(dev, PCI_SUBSYSTEM_ID, &dev->subsystem_device);
+
+ /*
+ * Do the ugly legacy mode stuff here rather than broken chip
+ * quirk code. Legacy mode ATA controllers have fixed
+ * addresses. These are not always echoed in BAR0-3, and
+ * BAR0-3 in a few cases contain junk!
+ */
+ if (class == PCI_CLASS_STORAGE_IDE) {
+ u8 progif;
+ pci_read_config_byte(dev, PCI_CLASS_PROG, &progif);
+ if ((progif & 1) == 0) {
+ dev->resource[0].start = 0x1F0;
+ dev->resource[0].end = 0x1F7;
+ dev->resource[0].flags = IORESOURCE_IO;
+ dev->resource[1].start = 0x3F6;
+ dev->resource[1].end = 0x3F6;
+ dev->resource[1].flags = IORESOURCE_IO;
+ }
+ if ((progif & 4) == 0) {
+ dev->resource[2].start = 0x170;
+ dev->resource[2].end = 0x177;
+ dev->resource[2].flags = IORESOURCE_IO;
+ dev->resource[3].start = 0x376;
+ dev->resource[3].end = 0x376;
+ dev->resource[3].flags = IORESOURCE_IO;
+ }
+ }
break;
case PCI_HEADER_TYPE_BRIDGE: /* bridge header */
@@ -834,6 +873,7 @@ void __devinit pci_device_add(struct pci_dev *dev, struct pci_bus *bus)
dev->dev.release = pci_release_dev;
pci_dev_get(dev);
+ set_dev_node(&dev->dev, pcibus_to_node(bus));
dev->dev.dma_mask = &dev->dma_mask;
dev->dev.coherent_dma_mask = 0xffffffffull;
@@ -1055,3 +1095,95 @@ EXPORT_SYMBOL(pci_scan_bridge);
EXPORT_SYMBOL(pci_scan_single_device);
EXPORT_SYMBOL_GPL(pci_scan_child_bus);
#endif
+
+static int __init pci_sort_bf_cmp(const struct pci_dev *a, const struct pci_dev *b)
+{
+ if (pci_domain_nr(a->bus) < pci_domain_nr(b->bus)) return -1;
+ else if (pci_domain_nr(a->bus) > pci_domain_nr(b->bus)) return 1;
+
+ if (a->bus->number < b->bus->number) return -1;
+ else if (a->bus->number > b->bus->number) return 1;
+
+ if (a->devfn < b->devfn) return -1;
+ else if (a->devfn > b->devfn) return 1;
+
+ return 0;
+}
+
+/*
+ * Yes, this forcably breaks the klist abstraction temporarily. It
+ * just wants to sort the klist, not change reference counts and
+ * take/drop locks rapidly in the process. It does all this while
+ * holding the lock for the list, so objects can't otherwise be
+ * added/removed while we're swizzling.
+ */
+static void __init pci_insertion_sort_klist(struct pci_dev *a, struct list_head *list)
+{
+ struct list_head *pos;
+ struct klist_node *n;
+ struct device *dev;
+ struct pci_dev *b;
+
+ list_for_each(pos, list) {
+ n = container_of(pos, struct klist_node, n_node);
+ dev = container_of(n, struct device, knode_bus);
+ b = to_pci_dev(dev);
+ if (pci_sort_bf_cmp(a, b) <= 0) {
+ list_move_tail(&a->dev.knode_bus.n_node, &b->dev.knode_bus.n_node);
+ return;
+ }
+ }
+ list_move_tail(&a->dev.knode_bus.n_node, list);
+}
+
+static void __init pci_sort_breadthfirst_klist(void)
+{
+ LIST_HEAD(sorted_devices);
+ struct list_head *pos, *tmp;
+ struct klist_node *n;
+ struct device *dev;
+ struct pci_dev *pdev;
+
+ spin_lock(&pci_bus_type.klist_devices.k_lock);
+ list_for_each_safe(pos, tmp, &pci_bus_type.klist_devices.k_list) {
+ n = container_of(pos, struct klist_node, n_node);
+ dev = container_of(n, struct device, knode_bus);
+ pdev = to_pci_dev(dev);
+ pci_insertion_sort_klist(pdev, &sorted_devices);
+ }
+ list_splice(&sorted_devices, &pci_bus_type.klist_devices.k_list);
+ spin_unlock(&pci_bus_type.klist_devices.k_lock);
+}
+
+static void __init pci_insertion_sort_devices(struct pci_dev *a, struct list_head *list)
+{
+ struct pci_dev *b;
+
+ list_for_each_entry(b, list, global_list) {
+ if (pci_sort_bf_cmp(a, b) <= 0) {
+ list_move_tail(&a->global_list, &b->global_list);
+ return;
+ }
+ }
+ list_move_tail(&a->global_list, list);
+}
+
+static void __init pci_sort_breadthfirst_devices(void)
+{
+ LIST_HEAD(sorted_devices);
+ struct pci_dev *dev, *tmp;
+
+ down_write(&pci_bus_sem);
+ list_for_each_entry_safe(dev, tmp, &pci_devices, global_list) {
+ pci_insertion_sort_devices(dev, &sorted_devices);
+ }
+ list_splice(&sorted_devices, &pci_devices);
+ up_write(&pci_bus_sem);
+}
+
+void __init pci_sort_breadthfirst(void)
+{
+ pci_sort_breadthfirst_devices();
+ pci_sort_breadthfirst_klist();
+}
+
diff --git a/drivers/pci/proc.c b/drivers/pci/proc.c
index 99cf33379769..4a6760a3b31f 100644
--- a/drivers/pci/proc.c
+++ b/drivers/pci/proc.c
@@ -23,7 +23,7 @@ static loff_t
proc_bus_pci_lseek(struct file *file, loff_t off, int whence)
{
loff_t new = -1;
- struct inode *inode = file->f_dentry->d_inode;
+ struct inode *inode = file->f_path.dentry->d_inode;
mutex_lock(&inode->i_mutex);
switch (whence) {
@@ -48,7 +48,7 @@ proc_bus_pci_lseek(struct file *file, loff_t off, int whence)
static ssize_t
proc_bus_pci_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
{
- const struct inode *ino = file->f_dentry->d_inode;
+ const struct inode *ino = file->f_path.dentry->d_inode;
const struct proc_dir_entry *dp = PDE(ino);
struct pci_dev *dev = dp->data;
unsigned int pos = *ppos;
@@ -130,7 +130,7 @@ proc_bus_pci_read(struct file *file, char __user *buf, size_t nbytes, loff_t *pp
static ssize_t
proc_bus_pci_write(struct file *file, const char __user *buf, size_t nbytes, loff_t *ppos)
{
- const struct inode *ino = file->f_dentry->d_inode;
+ const struct inode *ino = file->f_path.dentry->d_inode;
const struct proc_dir_entry *dp = PDE(ino);
struct pci_dev *dev = dp->data;
int pos = *ppos;
@@ -245,7 +245,7 @@ static int proc_bus_pci_ioctl(struct inode *inode, struct file *file, unsigned i
#ifdef HAVE_PCI_MMAP
static int proc_bus_pci_mmap(struct file *file, struct vm_area_struct *vma)
{
- struct inode *inode = file->f_dentry->d_inode;
+ struct inode *inode = file->f_path.dentry->d_inode;
const struct proc_dir_entry *dp = PDE(inode);
struct pci_dev *dev = dp->data;
struct pci_filp_private *fpriv = file->private_data;
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index def78a2a7c15..9ca9b9bf6160 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -93,8 +93,21 @@ static void __devinit quirk_nopcipci(struct pci_dev *dev)
pci_pci_problems |= PCIPCI_FAIL;
}
}
+
+static void __devinit quirk_nopciamd(struct pci_dev *dev)
+{
+ u8 rev;
+ pci_read_config_byte(dev, 0x08, &rev);
+ if (rev == 0x13) {
+ /* Erratum 24 */
+ printk(KERN_INFO "Chipset erratum: Disabling direct PCI/AGP transfers.\n");
+ pci_pci_problems |= PCIAGP_FAIL;
+ }
+}
+
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_5597, quirk_nopcipci );
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_496, quirk_nopcipci );
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8151_0, quirk_nopciamd );
/*
* Triton requires workarounds to be used by the drivers
@@ -440,6 +453,12 @@ static void __devinit quirk_ich6_lpc_acpi(struct pci_dev *dev)
}
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_0, quirk_ich6_lpc_acpi );
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_1, quirk_ich6_lpc_acpi );
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_0, quirk_ich6_lpc_acpi );
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_1, quirk_ich6_lpc_acpi );
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_31, quirk_ich6_lpc_acpi );
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH8_0, quirk_ich6_lpc_acpi );
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH8_2, quirk_ich6_lpc_acpi );
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH8_3, quirk_ich6_lpc_acpi );
/*
* VIA ACPI: One IO region pointed to by longword at
@@ -555,7 +574,7 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8237, quirk_via_vt
* is currently marked NoFix
*
* We have multiple reports of hangs with this chipset that went away with
- * noapic specified. For the moment we assume its the errata. We may be wrong
+ * noapic specified. For the moment we assume it's the erratum. We may be wrong
* of course. However the advice is demonstrably good even if so..
*/
static void __devinit quirk_amd_ioapic(struct pci_dev *dev)
@@ -564,7 +583,7 @@ static void __devinit quirk_amd_ioapic(struct pci_dev *dev)
pci_read_config_byte(dev, PCI_REVISION_ID, &rev);
if (rev >= 0x02) {
- printk(KERN_WARNING "I/O APIC: AMD Errata #22 may be present. In the event of instability try\n");
+ printk(KERN_WARNING "I/O APIC: AMD Erratum #22 may be present. In the event of instability try\n");
printk(KERN_WARNING " : booting with the \"noapic\" option.\n");
}
}
@@ -577,8 +596,6 @@ static void __init quirk_ioapic_rmw(struct pci_dev *dev)
}
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_SI, PCI_ANY_ID, quirk_ioapic_rmw );
-int pci_msi_quirk;
-
#define AMD8131_revA0 0x01
#define AMD8131_revB0 0x11
#define AMD8131_MISC 0x40
@@ -587,12 +604,6 @@ static void __init quirk_amd_8131_ioapic(struct pci_dev *dev)
{
unsigned char revid, tmp;
- if (dev->subordinate) {
- printk(KERN_WARNING "PCI: MSI quirk detected. "
- "PCI_BUS_FLAGS_NO_MSI set for subordinate bus.\n");
- dev->subordinate->bus_flags |= PCI_BUS_FLAGS_NO_MSI;
- }
-
if (nr_ioapics == 0)
return;
@@ -605,13 +616,6 @@ static void __init quirk_amd_8131_ioapic(struct pci_dev *dev)
}
}
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8131_BRIDGE, quirk_amd_8131_ioapic);
-
-static void __init quirk_svw_msi(struct pci_dev *dev)
-{
- pci_msi_quirk = 1;
- printk(KERN_WARNING "PCI: MSI quirk detected. pci_msi_quirk set.\n");
-}
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_GCNB_LE, quirk_svw_msi );
#endif /* CONFIG_X86_IO_APIC */
@@ -650,11 +654,43 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686_4, quirk_vi
* Some of the on-chip devices are actually '586 devices' so they are
* listed here.
*/
+
+static int via_irq_fixup_needed = -1;
+
+/*
+ * As some VIA hardware is available in PCI-card form, we need to restrict
+ * this quirk to VIA PCI hardware built onto VIA-based motherboards only.
+ * We try to locate a VIA southbridge before deciding whether the quirk
+ * should be applied.
+ */
+static const struct pci_device_id via_irq_fixup_tbl[] = {
+ {
+ .vendor = PCI_VENDOR_ID_VIA,
+ .device = PCI_ANY_ID,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .class = PCI_CLASS_BRIDGE_ISA << 8,
+ .class_mask = 0xffff00,
+ },
+ { 0, },
+};
+
static void quirk_via_irq(struct pci_dev *dev)
{
u8 irq, new_irq;
- new_irq = dev->irq & 0xf;
+ if (via_irq_fixup_needed == -1)
+ via_irq_fixup_needed = pci_dev_present(via_irq_fixup_tbl);
+
+ if (!via_irq_fixup_needed)
+ return;
+
+ new_irq = dev->irq;
+
+ /* Don't quirk interrupts outside the legacy IRQ range */
+ if (!new_irq || new_irq > 15)
+ return;
+
pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &irq);
if (new_irq != irq) {
printk(KERN_INFO "PCI: VIA IRQ fixup for %s, from %d to %d\n",
@@ -663,14 +699,7 @@ static void quirk_via_irq(struct pci_dev *dev)
pci_write_config_byte(dev, PCI_INTERRUPT_LINE, new_irq);
}
}
-DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_0, quirk_via_irq);
-DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_1, quirk_via_irq);
-DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_2, quirk_via_irq);
-DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_3, quirk_via_irq);
-DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8235_USB_2, quirk_via_irq);
-DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686, quirk_via_irq);
-DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686_4, quirk_via_irq);
-DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686_5, quirk_via_irq);
+DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_VIA, PCI_ANY_ID, quirk_via_irq);
/*
* VIA VT82C598 has its device ID settable and many BIOSes
@@ -685,33 +714,6 @@ static void __devinit quirk_vt82c598_id(struct pci_dev *dev)
}
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C597_0, quirk_vt82c598_id );
-#ifdef CONFIG_ACPI_SLEEP
-
-/*
- * Some VIA systems boot with the abnormal status flag set. This can cause
- * the BIOS to re-POST the system on resume rather than passing control
- * back to the OS. Clear the flag on boot
- */
-static void __devinit quirk_via_abnormal_poweroff(struct pci_dev *dev)
-{
- u32 reg;
-
- acpi_hw_register_read(ACPI_MTX_DO_NOT_LOCK, ACPI_REGISTER_PM1_STATUS,
- &reg);
-
- if (reg & 0x800) {
- printk("Clearing abnormal poweroff flag\n");
- acpi_hw_register_write(ACPI_MTX_DO_NOT_LOCK,
- ACPI_REGISTER_PM1_STATUS,
- (u16)0x800);
- }
-}
-
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8235, quirk_via_abnormal_poweroff);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8237, quirk_via_abnormal_poweroff);
-
-#endif
-
/*
* CardBus controllers have a legacy base address that enables them
* to respond as i82365 pcmcia controllers. We don't want them to
@@ -795,56 +797,6 @@ static void __init quirk_mediagx_master(struct pci_dev *dev)
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_PCI_MASTER, quirk_mediagx_master );
/*
- * As per PCI spec, ignore base address registers 0-3 of the IDE controllers
- * running in Compatible mode (bits 0 and 2 in the ProgIf for primary and
- * secondary channels respectively). If the device reports Compatible mode
- * but does use BAR0-3 for address decoding, we assume that firmware has
- * programmed these BARs with standard values (0x1f0,0x3f4 and 0x170,0x374).
- * Exceptions (if they exist) must be handled in chip/architecture specific
- * fixups.
- *
- * Note: for non x86 people. You may need an arch specific quirk to handle
- * moving IDE devices to native mode as well. Some plug in card devices power
- * up in compatible mode and assume the BIOS will adjust them.
- *
- * Q: should we load the 0x1f0,0x3f4 into the registers or zap them as
- * we do now ? We don't want is pci_enable_device to come along
- * and assign new resources. Both approaches work for that.
- */
-static void __devinit quirk_ide_bases(struct pci_dev *dev)
-{
- struct resource *res;
- int first_bar = 2, last_bar = 0;
-
- if ((dev->class >> 8) != PCI_CLASS_STORAGE_IDE)
- return;
-
- res = &dev->resource[0];
-
- /* primary channel: ProgIf bit 0, BAR0, BAR1 */
- if (!(dev->class & 1) && (res[0].flags || res[1].flags)) {
- res[0].start = res[0].end = res[0].flags = 0;
- res[1].start = res[1].end = res[1].flags = 0;
- first_bar = 0;
- last_bar = 1;
- }
-
- /* secondary channel: ProgIf bit 2, BAR2, BAR3 */
- if (!(dev->class & 4) && (res[2].flags || res[3].flags)) {
- res[2].start = res[2].end = res[2].flags = 0;
- res[3].start = res[3].end = res[3].flags = 0;
- last_bar = 3;
- }
-
- if (!last_bar)
- return;
-
- printk(KERN_INFO "PCI: Ignoring BAR%d-%d of IDE controller %s\n",
- first_bar, last_bar, pci_name(dev));
-}
-DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, quirk_ide_bases);
-
-/*
* Ensure C0 rev restreaming is off. This is normally done by
* the BIOS but in the odd case it is not the results are corruption
* hence the presence of a Linux check
@@ -878,11 +830,10 @@ static void __devinit quirk_svwks_csb5ide(struct pci_dev *pdev)
prog &= ~5;
pdev->class &= ~5;
pci_write_config_byte(pdev, PCI_CLASS_PROG, prog);
- /* need to re-assign BARs for compat mode */
- quirk_ide_bases(pdev);
+ /* PCI layer will sort out resources */
}
}
-DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB5IDE, quirk_svwks_csb5ide );
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB5IDE, quirk_svwks_csb5ide );
/*
* Intel 82801CAM ICH3-M datasheet says IDE modes must be the same
@@ -898,11 +849,9 @@ static void __init quirk_ide_samemode(struct pci_dev *pdev)
prog &= ~5;
pdev->class &= ~5;
pci_write_config_byte(pdev, PCI_CLASS_PROG, prog);
- /* need to re-assign BARs for compat mode */
- quirk_ide_bases(pdev);
}
}
-DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_10, quirk_ide_samemode);
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_10, quirk_ide_samemode);
/* This was originally an Alpha specific thing, but it really fits here.
* The i82375 PCI/EISA bridge appears as non-classified. Fix that.
@@ -1458,33 +1407,6 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x2609, quirk_intel_pcie_pm);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x260a, quirk_intel_pcie_pm);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x260b, quirk_intel_pcie_pm);
-/*
- * Fixup the cardbus bridges on the IBM Dock II docking station
- */
-static void __devinit quirk_ibm_dock2_cardbus(struct pci_dev *dev)
-{
- u32 val;
-
- /*
- * tie the 2 interrupt pins to INTA, and configure the
- * multifunction routing register to handle this.
- */
- if ((dev->subsystem_vendor == PCI_VENDOR_ID_IBM) &&
- (dev->subsystem_device == 0x0148)) {
- printk(KERN_INFO "PCI: Found IBM Dock II Cardbus Bridge "
- "applying quirk\n");
- pci_read_config_dword(dev, 0x8c, &val);
- val = ((val & 0xffffff00) | 0x1002);
- pci_write_config_dword(dev, 0x8c, val);
- pci_read_config_dword(dev, 0x80, &val);
- val = ((val & 0x00ffff00) | 0x2864c077);
- pci_write_config_dword(dev, 0x80, val);
- }
-}
-
-DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_1420,
- quirk_ibm_dock2_cardbus);
-
static void __devinit quirk_netmos(struct pci_dev *dev)
{
unsigned int num_parallel = (dev->subsystem_device & 0xf0) >> 4;
@@ -1590,7 +1512,6 @@ static void __devinit fixup_rev1_53c810(struct pci_dev* dev)
}
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_NCR, PCI_DEVICE_ID_NCR_53C810, fixup_rev1_53c810);
-
static void pci_do_fixups(struct pci_dev *dev, struct pci_fixup *f, struct pci_fixup *end)
{
while (f < end) {
@@ -1690,6 +1611,96 @@ static void __devinit quirk_nvidia_ck804_pcie_aer_ext_cap(struct pci_dev *dev)
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_CK804_PCIE,
quirk_nvidia_ck804_pcie_aer_ext_cap);
+#ifdef CONFIG_PCI_MSI
+/* To disable MSI globally */
+int pci_msi_quirk;
+
+/* The Serverworks PCI-X chipset does not support MSI. We cannot easily rely
+ * on setting PCI_BUS_FLAGS_NO_MSI in its bus flags because there are actually
+ * some other busses controlled by the chipset even if Linux is not aware of it.
+ * Instead of setting the flag on all busses in the machine, simply disable MSI
+ * globally.
+ */
+static void __init quirk_svw_msi(struct pci_dev *dev)
+{
+ pci_msi_quirk = 1;
+ printk(KERN_WARNING "PCI: MSI quirk detected. pci_msi_quirk set.\n");
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_GCNB_LE, quirk_svw_msi);
+
+/* Disable MSI on chipsets that are known to not support it */
+static void __devinit quirk_disable_msi(struct pci_dev *dev)
+{
+ if (dev->subordinate) {
+ printk(KERN_WARNING "PCI: MSI quirk detected. "
+ "PCI_BUS_FLAGS_NO_MSI set for %s subordinate bus.\n",
+ pci_name(dev));
+ dev->subordinate->bus_flags |= PCI_BUS_FLAGS_NO_MSI;
+ }
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8131_BRIDGE, quirk_disable_msi);
+
+/* Go through the list of Hypertransport capabilities and
+ * return 1 if a HT MSI capability is found and enabled */
+static int __devinit msi_ht_cap_enabled(struct pci_dev *dev)
+{
+ u8 pos;
+ int ttl;
+ for (pos = pci_find_capability(dev, PCI_CAP_ID_HT), ttl = 48;
+ pos && ttl;
+ pos = pci_find_next_capability(dev, pos, PCI_CAP_ID_HT), ttl--) {
+ u32 cap_hdr;
+ /* MSI mapping section according to Hypertransport spec */
+ if (pci_read_config_dword(dev, pos, &cap_hdr) == 0
+ && (cap_hdr & 0xf8000000) == 0xa8000000 /* MSI mapping */) {
+ printk(KERN_INFO "PCI: Found HT MSI mapping on %s with capability %s\n",
+ pci_name(dev), cap_hdr & 0x10000 ? "enabled" : "disabled");
+ return (cap_hdr & 0x10000) != 0; /* MSI mapping cap enabled */
+ }
+ }
+ return 0;
+}
+
+/* Check the hypertransport MSI mapping to know whether MSI is enabled or not */
+static void __devinit quirk_msi_ht_cap(struct pci_dev *dev)
+{
+ if (dev->subordinate && !msi_ht_cap_enabled(dev)) {
+ printk(KERN_WARNING "PCI: MSI quirk detected. "
+ "MSI disabled on chipset %s.\n",
+ pci_name(dev));
+ dev->subordinate->bus_flags |= PCI_BUS_FLAGS_NO_MSI;
+ }
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_HT2000_PCIE,
+ quirk_msi_ht_cap);
+
+/* The nVidia CK804 chipset may have 2 HT MSI mappings.
+ * MSI are supported if the MSI capability set in any of these mappings.
+ */
+static void __devinit quirk_nvidia_ck804_msi_ht_cap(struct pci_dev *dev)
+{
+ struct pci_dev *pdev;
+
+ if (!dev->subordinate)
+ return;
+
+ /* check HT MSI cap on this chipset and the root one.
+ * a single one having MSI is enough to be sure that MSI are supported.
+ */
+ pdev = pci_get_slot(dev->bus, 0);
+ if (dev->subordinate && !msi_ht_cap_enabled(dev)
+ && !msi_ht_cap_enabled(pdev)) {
+ printk(KERN_WARNING "PCI: MSI quirk detected. "
+ "MSI disabled on chipset %s.\n",
+ pci_name(dev));
+ dev->subordinate->bus_flags |= PCI_BUS_FLAGS_NO_MSI;
+ }
+ pci_dev_put(pdev);
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_CK804_PCIE,
+ quirk_nvidia_ck804_msi_ht_cap);
+#endif /* CONFIG_PCI_MSI */
+
EXPORT_SYMBOL(pcie_mch_quirk);
#ifdef CONFIG_HOTPLUG
EXPORT_SYMBOL(pci_fixup_device);
diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c
index 99ffbd478b29..430281b2e921 100644
--- a/drivers/pci/remove.c
+++ b/drivers/pci/remove.c
@@ -16,8 +16,11 @@ static void pci_free_resources(struct pci_dev *dev)
}
}
-static void pci_destroy_dev(struct pci_dev *dev)
+static void pci_stop_dev(struct pci_dev *dev)
{
+ if (!dev->global_list.next)
+ return;
+
if (!list_empty(&dev->global_list)) {
pci_proc_detach_device(dev);
pci_remove_sysfs_dev_files(dev);
@@ -27,6 +30,11 @@ static void pci_destroy_dev(struct pci_dev *dev)
dev->global_list.next = dev->global_list.prev = NULL;
up_write(&pci_bus_sem);
}
+}
+
+static void pci_destroy_dev(struct pci_dev *dev)
+{
+ pci_stop_dev(dev);
/* Remove the device from the device lists, and prevent any further
* list accesses from this device */
@@ -119,5 +127,32 @@ void pci_remove_behind_bridge(struct pci_dev *dev)
}
}
+static void pci_stop_bus_devices(struct pci_bus *bus)
+{
+ struct list_head *l, *n;
+
+ list_for_each_safe(l, n, &bus->devices) {
+ struct pci_dev *dev = pci_dev_b(l);
+ pci_stop_bus_device(dev);
+ }
+}
+
+/**
+ * pci_stop_bus_device - stop a PCI device and any children
+ * @dev: the device to stop
+ *
+ * Stop a PCI device (detach the driver, remove from the global list
+ * and so on). This also stop any subordinate buses and children in a
+ * depth-first manner.
+ */
+void pci_stop_bus_device(struct pci_dev *dev)
+{
+ if (dev->subordinate)
+ pci_stop_bus_devices(dev->subordinate);
+
+ pci_stop_dev(dev);
+}
+
EXPORT_SYMBOL(pci_remove_bus_device);
EXPORT_SYMBOL(pci_remove_behind_bridge);
+EXPORT_SYMBOL_GPL(pci_stop_bus_device);
diff --git a/drivers/pci/rom.c b/drivers/pci/rom.c
index f5ee7ce16fa6..d087e0817715 100644
--- a/drivers/pci/rom.c
+++ b/drivers/pci/rom.c
@@ -71,13 +71,18 @@ void __iomem *pci_map_rom(struct pci_dev *pdev, size_t *size)
void __iomem *image;
int last_image;
- /* IORESOURCE_ROM_SHADOW only set on x86 */
+ /*
+ * IORESOURCE_ROM_SHADOW set on x86, x86_64 and IA64 supports legacy
+ * memory map if the VGA enable bit of the Bridge Control register is
+ * set for embedded VGA.
+ */
if (res->flags & IORESOURCE_ROM_SHADOW) {
/* primary video rom always starts here */
start = (loff_t)0xC0000;
*size = 0x20000; /* cover C000:0 through E000:0 */
} else {
- if (res->flags & IORESOURCE_ROM_COPY) {
+ if (res->flags &
+ (IORESOURCE_ROM_COPY | IORESOURCE_ROM_BIOS_COPY)) {
*size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
return (void __iomem *)(unsigned long)
pci_resource_start(pdev, PCI_ROM_RESOURCE);
@@ -161,7 +166,8 @@ void __iomem *pci_map_rom_copy(struct pci_dev *pdev, size_t *size)
if (!rom)
return NULL;
- if (res->flags & (IORESOURCE_ROM_COPY | IORESOURCE_ROM_SHADOW))
+ if (res->flags & (IORESOURCE_ROM_COPY | IORESOURCE_ROM_SHADOW |
+ IORESOURCE_ROM_BIOS_COPY))
return rom;
res->start = (unsigned long)kmalloc(*size, GFP_KERNEL);
@@ -187,7 +193,7 @@ void pci_unmap_rom(struct pci_dev *pdev, void __iomem *rom)
{
struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
- if (res->flags & IORESOURCE_ROM_COPY)
+ if (res->flags & (IORESOURCE_ROM_COPY | IORESOURCE_ROM_BIOS_COPY))
return;
iounmap(rom);
@@ -211,6 +217,7 @@ void pci_remove_rom(struct pci_dev *pdev)
sysfs_remove_bin_file(&pdev->dev.kobj, pdev->rom_attr);
if (!(res->flags & (IORESOURCE_ROM_ENABLE |
IORESOURCE_ROM_SHADOW |
+ IORESOURCE_ROM_BIOS_COPY |
IORESOURCE_ROM_COPY)))
pci_disable_rom(pdev);
}
diff --git a/drivers/pci/search.c b/drivers/pci/search.c
index d529462d1b53..2f13eba5d5ae 100644
--- a/drivers/pci/search.c
+++ b/drivers/pci/search.c
@@ -140,6 +140,31 @@ struct pci_dev * pci_get_slot(struct pci_bus *bus, unsigned int devfn)
}
/**
+ * pci_get_bus_and_slot - locate PCI device from a given PCI slot
+ * @bus: number of PCI bus on which desired PCI device resides
+ * @devfn: encodes number of PCI slot in which the desired PCI
+ * device resides and the logical device number within that slot
+ * in case of multi-function devices.
+ *
+ * Given a PCI bus and slot/function number, the desired PCI device
+ * is located in system global list of PCI devices. If the device
+ * is found, a pointer to its data structure is returned. If no
+ * device is found, %NULL is returned. The returned device has its
+ * reference count bumped by one.
+ */
+
+struct pci_dev * pci_get_bus_and_slot(unsigned int bus, unsigned int devfn)
+{
+ struct pci_dev *dev = NULL;
+
+ while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) {
+ if (dev->bus->number == bus && dev->devfn == devfn)
+ return dev;
+ }
+ return NULL;
+}
+
+/**
* pci_find_subsys - begin or continue searching for a PCI device by vendor/subvendor/device/subdevice id
* @vendor: PCI vendor id to match, or %PCI_ANY_ID to match all vendor ids
* @device: PCI device id to match, or %PCI_ANY_ID to match all device ids
@@ -274,6 +299,45 @@ pci_get_device(unsigned int vendor, unsigned int device, struct pci_dev *from)
return pci_get_subsys(vendor, device, PCI_ANY_ID, PCI_ANY_ID, from);
}
+/**
+ * pci_get_device_reverse - begin or continue searching for a PCI device by vendor/device id
+ * @vendor: PCI vendor id to match, or %PCI_ANY_ID to match all vendor ids
+ * @device: PCI device id to match, or %PCI_ANY_ID to match all device ids
+ * @from: Previous PCI device found in search, or %NULL for new search.
+ *
+ * Iterates through the list of known PCI devices in the reverse order of
+ * pci_get_device.
+ * If a PCI device is found with a matching @vendor and @device, the reference
+ * count to the device is incremented and a pointer to its device structure
+ * is returned Otherwise, %NULL is returned. A new search is initiated by
+ * passing %NULL as the @from argument. Otherwise if @from is not %NULL,
+ * searches continue from next device on the global list. The reference
+ * count for @from is always decremented if it is not %NULL.
+ */
+struct pci_dev *
+pci_get_device_reverse(unsigned int vendor, unsigned int device, struct pci_dev *from)
+{
+ struct list_head *n;
+ struct pci_dev *dev;
+
+ WARN_ON(in_interrupt());
+ down_read(&pci_bus_sem);
+ n = from ? from->global_list.prev : pci_devices.prev;
+
+ while (n && (n != &pci_devices)) {
+ dev = pci_dev_g(n);
+ if ((vendor == PCI_ANY_ID || dev->vendor == vendor) &&
+ (device == PCI_ANY_ID || dev->device == device))
+ goto exit;
+ n = n->prev;
+ }
+ dev = NULL;
+exit:
+ dev = pci_dev_get(dev);
+ up_read(&pci_bus_sem);
+ pci_dev_put(from);
+ return dev;
+}
/**
* pci_find_device_reverse - begin or continue searching for a PCI device by vendor/device id
@@ -382,12 +446,16 @@ exit:
}
EXPORT_SYMBOL(pci_dev_present);
-EXPORT_SYMBOL(pci_find_bus);
-EXPORT_SYMBOL(pci_find_next_bus);
EXPORT_SYMBOL(pci_find_device);
EXPORT_SYMBOL(pci_find_device_reverse);
EXPORT_SYMBOL(pci_find_slot);
+/* For boot time work */
+EXPORT_SYMBOL(pci_find_bus);
+EXPORT_SYMBOL(pci_find_next_bus);
+/* For everyone */
EXPORT_SYMBOL(pci_get_device);
+EXPORT_SYMBOL(pci_get_device_reverse);
EXPORT_SYMBOL(pci_get_subsys);
EXPORT_SYMBOL(pci_get_slot);
+EXPORT_SYMBOL(pci_get_bus_and_slot);
EXPORT_SYMBOL(pci_get_class);
diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c
index 47c1071ad84e..8f7bcf56f149 100644
--- a/drivers/pci/setup-bus.c
+++ b/drivers/pci/setup-bus.c
@@ -57,10 +57,17 @@ pbus_assign_resources_sorted(struct pci_bus *bus)
/* Don't touch classless devices or host bridges or ioapics. */
if (class == PCI_CLASS_NOT_DEFINED ||
- class == PCI_CLASS_BRIDGE_HOST ||
- class == PCI_CLASS_SYSTEM_PIC)
+ class == PCI_CLASS_BRIDGE_HOST)
continue;
+ /* Don't touch ioapic devices already enabled by firmware */
+ if (class == PCI_CLASS_SYSTEM_PIC) {
+ u16 command;
+ pci_read_config_word(dev, PCI_COMMAND, &command);
+ if (command & (PCI_COMMAND_IO | PCI_COMMAND_MEMORY))
+ continue;
+ }
+
pdev_sort_resources(dev, &head);
}