summaryrefslogtreecommitdiffstats
path: root/drivers/pci
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2008-02-02 14:29:33 +1100
committerLinus Torvalds <torvalds@linux-foundation.org>2008-02-02 14:29:33 +1100
commit215e871aaa3d94540121a3809d80d0c5e5686e4f (patch)
tree0ed6469c5ad04db8cfa0edb58c676d5155df20cd /drivers/pci
parentb6cf160c4b788a31f6a4017a469b956ca77febf4 (diff)
parentfd7d1ced29e5beb88c9068801da7a362606d8273 (diff)
Merge git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/pci-2.6
* git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/pci-2.6: (64 commits) PCI: make pci_bus a struct device PCI: fix codingstyle issues in include/linux/pci.h PCI: fix codingstyle issues in drivers/pci/pci.h PCI: PCIE ASPM support PCI: Fix fakephp deadlock PCI: modify SB700 SATA MSI quirk PCI: Run ACPI _OSC method on root bridges only PCI ACPI: AER driver should only register PCIe devices with _OSC PCI ACPI: Added a function to register _OSC with only PCIe devices. PCI: constify function pointer tables PCI: Convert drivers/pci/proc.c to use unlocked_ioctl pciehp: block new requests from the device before power off pciehp: workaround against Bad DLLP during power off pciehp: wait for 1000ms before LED operation after power off PCI: Remove pci_enable_device_bars() from documentation PCI: Remove pci_enable_device_bars() PCI: Remove users of pci_enable_device_bars() PCI: Add pci_enable_device_{io,mem} intefaces PCI: avoid save the same type of cap multiple times PCI: correctly initialize a structure for pcie_save_pcix_state() ...
Diffstat (limited to 'drivers/pci')
-rw-r--r--drivers/pci/bus.c18
-rw-r--r--drivers/pci/dmar.c20
-rw-r--r--drivers/pci/hotplug/Kconfig4
-rw-r--r--drivers/pci/hotplug/Makefile4
-rw-r--r--drivers/pci/hotplug/acpiphp.h1
-rw-r--r--drivers/pci/hotplug/acpiphp_glue.c5
-rw-r--r--drivers/pci/hotplug/fakephp.c39
-rw-r--r--drivers/pci/hotplug/ibmphp_core.c11
-rw-r--r--drivers/pci/hotplug/pci_hotplug_core.c4
-rw-r--r--drivers/pci/hotplug/pciehp.h9
-rw-r--r--drivers/pci/hotplug/pciehp_core.c33
-rw-r--r--drivers/pci/hotplug/pciehp_ctrl.c27
-rw-r--r--drivers/pci/hotplug/pciehp_hpc.c314
-rw-r--r--drivers/pci/hotplug/pciehp_pci.c43
-rw-r--r--drivers/pci/hotplug/rpaphp.h1
-rw-r--r--drivers/pci/hotplug/rpaphp_pci.c14
-rw-r--r--drivers/pci/hotplug/rpaphp_slot.c47
-rw-r--r--drivers/pci/hotplug/shpchp_hpc.c2
-rw-r--r--drivers/pci/intel-iommu.c2
-rw-r--r--drivers/pci/msi.c94
-rw-r--r--drivers/pci/pci-acpi.c7
-rw-r--r--drivers/pci/pci-driver.c4
-rw-r--r--drivers/pci/pci-sysfs.c11
-rw-r--r--drivers/pci/pci.c93
-rw-r--r--drivers/pci/pci.h16
-rw-r--r--drivers/pci/pcie/Kconfig20
-rw-r--r--drivers/pci/pcie/Makefile3
-rw-r--r--drivers/pci/pcie/aer/aerdrv_acpi.c24
-rw-r--r--drivers/pci/pcie/aspm.c802
-rw-r--r--drivers/pci/pcie/portdrv_core.c5
-rw-r--r--drivers/pci/probe.c80
-rw-r--r--drivers/pci/proc.c17
-rw-r--r--drivers/pci/quirks.c483
-rw-r--r--drivers/pci/remove.c10
-rw-r--r--drivers/pci/rom.c6
-rw-r--r--drivers/pci/setup-bus.c64
-rw-r--r--drivers/pci/setup-res.c7
-rw-r--r--drivers/pci/syscall.c5
38 files changed, 1714 insertions, 635 deletions
diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c
index 9e5ea074ad20..ef5a6a245f5f 100644
--- a/drivers/pci/bus.c
+++ b/drivers/pci/bus.c
@@ -108,6 +108,7 @@ int pci_bus_add_device(struct pci_dev *dev)
void pci_bus_add_devices(struct pci_bus *bus)
{
struct pci_dev *dev;
+ struct pci_bus *child_bus;
int retval;
list_for_each_entry(dev, &bus->devices, bus_list) {
@@ -138,11 +139,19 @@ void pci_bus_add_devices(struct pci_bus *bus)
up_write(&pci_bus_sem);
}
pci_bus_add_devices(dev->subordinate);
- retval = sysfs_create_link(&dev->subordinate->class_dev.kobj,
- &dev->dev.kobj, "bridge");
+
+ /* register the bus with sysfs as the parent is now
+ * properly registered. */
+ child_bus = dev->subordinate;
+ child_bus->dev.parent = child_bus->bridge;
+ retval = device_register(&child_bus->dev);
+ if (!retval)
+ retval = device_create_file(&child_bus->dev,
+ &dev_attr_cpuaffinity);
if (retval)
- dev_err(&dev->dev, "Error creating sysfs "
- "bridge symlink, continuing...\n");
+ dev_err(&dev->dev, "Error registering pci_bus"
+ " device bridge symlink,"
+ " continuing...\n");
}
}
}
@@ -204,7 +213,6 @@ void pci_walk_bus(struct pci_bus *top, void (*cb)(struct pci_dev *, void *),
}
up_read(&pci_bus_sem);
}
-EXPORT_SYMBOL_GPL(pci_walk_bus);
EXPORT_SYMBOL(pci_bus_alloc_resource);
EXPORT_SYMBOL_GPL(pci_bus_add_device);
diff --git a/drivers/pci/dmar.c b/drivers/pci/dmar.c
index 5dfdfdac92e1..91b2dc956be5 100644
--- a/drivers/pci/dmar.c
+++ b/drivers/pci/dmar.c
@@ -25,6 +25,7 @@
#include <linux/pci.h>
#include <linux/dmar.h>
+#include "iova.h"
#undef PREFIX
#define PREFIX "DMAR:"
@@ -263,8 +264,8 @@ parse_dmar_table(void)
if (!dmar)
return -ENODEV;
- if (!dmar->width) {
- printk (KERN_WARNING PREFIX "Zero: Invalid DMAR haw\n");
+ if (dmar->width < PAGE_SHIFT_4K - 1) {
+ printk(KERN_WARNING PREFIX "Invalid DMAR haw\n");
return -EINVAL;
}
@@ -301,11 +302,24 @@ parse_dmar_table(void)
int __init dmar_table_init(void)
{
- parse_dmar_table();
+ int ret;
+
+ ret = parse_dmar_table();
+ if (ret) {
+ printk(KERN_INFO PREFIX "parse DMAR table failure.\n");
+ return ret;
+ }
+
if (list_empty(&dmar_drhd_units)) {
printk(KERN_INFO PREFIX "No DMAR devices found\n");
return -ENODEV;
}
+
+ if (list_empty(&dmar_rmrr_units)) {
+ printk(KERN_INFO PREFIX "No RMRR found\n");
+ return -ENODEV;
+ }
+
return 0;
}
diff --git a/drivers/pci/hotplug/Kconfig b/drivers/pci/hotplug/Kconfig
index a64449d489d6..2cdd8326f136 100644
--- a/drivers/pci/hotplug/Kconfig
+++ b/drivers/pci/hotplug/Kconfig
@@ -3,8 +3,8 @@
#
menuconfig HOTPLUG_PCI
- tristate "Support for PCI Hotplug (EXPERIMENTAL)"
- depends on PCI && EXPERIMENTAL && HOTPLUG
+ tristate "Support for PCI Hotplug"
+ depends on PCI && HOTPLUG
---help---
Say Y here if you have a motherboard with a PCI Hotplug controller.
This allows you to add and remove PCI cards while the machine is
diff --git a/drivers/pci/hotplug/Makefile b/drivers/pci/hotplug/Makefile
index 34a1891191fd..9bdbe1a6688f 100644
--- a/drivers/pci/hotplug/Makefile
+++ b/drivers/pci/hotplug/Makefile
@@ -3,7 +3,6 @@
#
obj-$(CONFIG_HOTPLUG_PCI) += pci_hotplug.o
-obj-$(CONFIG_HOTPLUG_PCI_FAKE) += fakephp.o
obj-$(CONFIG_HOTPLUG_PCI_COMPAQ) += cpqphp.o
obj-$(CONFIG_HOTPLUG_PCI_IBM) += ibmphp.o
obj-$(CONFIG_HOTPLUG_PCI_ACPI) += acpiphp.o
@@ -16,6 +15,9 @@ obj-$(CONFIG_HOTPLUG_PCI_RPA) += rpaphp.o
obj-$(CONFIG_HOTPLUG_PCI_RPA_DLPAR) += rpadlpar_io.o
obj-$(CONFIG_HOTPLUG_PCI_SGI) += sgi_hotplug.o
+# Link this last so it doesn't claim devices that have a real hotplug driver
+obj-$(CONFIG_HOTPLUG_PCI_FAKE) += fakephp.o
+
pci_hotplug-objs := pci_hotplug_core.o
ifdef CONFIG_HOTPLUG_PCI_CPCI
diff --git a/drivers/pci/hotplug/acpiphp.h b/drivers/pci/hotplug/acpiphp.h
index 1ef417cca2db..7a29164d4b32 100644
--- a/drivers/pci/hotplug/acpiphp.h
+++ b/drivers/pci/hotplug/acpiphp.h
@@ -113,7 +113,6 @@ struct acpiphp_slot {
u8 device; /* pci device# */
u32 sun; /* ACPI _SUN (slot unique number) */
- u32 slotno; /* slot number relative to bridge */
u32 flags; /* see below */
};
diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c
index ff1b1c71291a..cf22f9e01e00 100644
--- a/drivers/pci/hotplug/acpiphp_glue.c
+++ b/drivers/pci/hotplug/acpiphp_glue.c
@@ -102,7 +102,7 @@ static int is_ejectable(acpi_handle handle)
}
-/* callback routine to check the existence of ejectable slots */
+/* callback routine to check for the existence of ejectable slots */
static acpi_status
is_ejectable_slot(acpi_handle handle, u32 lvl, void *context, void **rv)
{
@@ -117,7 +117,7 @@ is_ejectable_slot(acpi_handle handle, u32 lvl, void *context, void **rv)
}
}
-/* callback routine to check for the existance of a pci dock device */
+/* callback routine to check for the existence of a pci dock device */
static acpi_status
is_pci_dock_device(acpi_handle handle, u32 lvl, void *context, void **rv)
{
@@ -1528,7 +1528,6 @@ check_sub_bridges(acpi_handle handle, u32 lvl, void *context, void **rv)
acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer);
dbg("%s: re-enumerating slots under %s\n",
__FUNCTION__, objname);
- acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer);
acpiphp_check_bridge(bridge);
}
return AE_OK ;
diff --git a/drivers/pci/hotplug/fakephp.c b/drivers/pci/hotplug/fakephp.c
index d7a293e3faf5..94b640146d44 100644
--- a/drivers/pci/hotplug/fakephp.c
+++ b/drivers/pci/hotplug/fakephp.c
@@ -39,6 +39,7 @@
#include <linux/init.h>
#include <linux/string.h>
#include <linux/slab.h>
+#include <linux/workqueue.h>
#include "../pci.h"
#if !defined(MODULE)
@@ -63,10 +64,16 @@ struct dummy_slot {
struct list_head node;
struct hotplug_slot *slot;
struct pci_dev *dev;
+ struct work_struct remove_work;
+ unsigned long removed;
};
static int debug;
static LIST_HEAD(slot_list);
+static struct workqueue_struct *dummyphp_wq;
+
+static void pci_rescan_worker(struct work_struct *work);
+static DECLARE_WORK(pci_rescan_work, pci_rescan_worker);
static int enable_slot (struct hotplug_slot *slot);
static int disable_slot (struct hotplug_slot *slot);
@@ -109,7 +116,7 @@ static int add_slot(struct pci_dev *dev)
slot->name = &dev->dev.bus_id[0];
dbg("slot->name = %s\n", slot->name);
- dslot = kmalloc(sizeof(struct dummy_slot), GFP_KERNEL);
+ dslot = kzalloc(sizeof(struct dummy_slot), GFP_KERNEL);
if (!dslot)
goto error_info;
@@ -164,6 +171,14 @@ static void remove_slot(struct dummy_slot *dslot)
err("Problem unregistering a slot %s\n", dslot->slot->name);
}
+/* called from the single-threaded workqueue handler to remove a slot */
+static void remove_slot_worker(struct work_struct *work)
+{
+ struct dummy_slot *dslot =
+ container_of(work, struct dummy_slot, remove_work);
+ remove_slot(dslot);
+}
+
/**
* pci_rescan_slot - Rescan slot
* @temp: Device template. Should be set: bus and devfn.
@@ -267,11 +282,17 @@ static inline void pci_rescan(void) {
pci_rescan_buses(&pci_root_buses);
}
+/* called from the single-threaded workqueue handler to rescan all pci buses */
+static void pci_rescan_worker(struct work_struct *work)
+{
+ pci_rescan();
+}
static int enable_slot(struct hotplug_slot *hotplug_slot)
{
/* mis-use enable_slot for rescanning of the pci bus */
- pci_rescan();
+ cancel_work_sync(&pci_rescan_work);
+ queue_work(dummyphp_wq, &pci_rescan_work);
return -ENODEV;
}
@@ -306,6 +327,10 @@ static int disable_slot(struct hotplug_slot *slot)
err("Can't remove PCI devices with other PCI devices behind it yet.\n");
return -ENODEV;
}
+ if (test_and_set_bit(0, &dslot->removed)) {
+ dbg("Slot already scheduled for removal\n");
+ return -ENODEV;
+ }
/* search for subfunctions and disable them first */
if (!(dslot->dev->devfn & 7)) {
for (func = 1; func < 8; func++) {
@@ -328,8 +353,9 @@ static int disable_slot(struct hotplug_slot *slot)
/* remove the device from the pci core */
pci_remove_bus_device(dslot->dev);
- /* blow away this sysfs entry and other parts. */
- remove_slot(dslot);
+ /* queue work item to blow away this sysfs entry and other parts. */
+ INIT_WORK(&dslot->remove_work, remove_slot_worker);
+ queue_work(dummyphp_wq, &dslot->remove_work);
return 0;
}
@@ -340,6 +366,7 @@ static void cleanup_slots (void)
struct list_head *next;
struct dummy_slot *dslot;
+ destroy_workqueue(dummyphp_wq);
list_for_each_safe (tmp, next, &slot_list) {
dslot = list_entry (tmp, struct dummy_slot, node);
remove_slot(dslot);
@@ -351,6 +378,10 @@ static int __init dummyphp_init(void)
{
info(DRIVER_DESC "\n");
+ dummyphp_wq = create_singlethread_workqueue(MY_NAME);
+ if (!dummyphp_wq)
+ return -ENOMEM;
+
return pci_scan_buses();
}
diff --git a/drivers/pci/hotplug/ibmphp_core.c b/drivers/pci/hotplug/ibmphp_core.c
index a90c28d0c69d..87b6b8b280e6 100644
--- a/drivers/pci/hotplug/ibmphp_core.c
+++ b/drivers/pci/hotplug/ibmphp_core.c
@@ -761,10 +761,13 @@ static void ibm_unconfigure_device(struct pci_func *func)
debug("func->device << 3 | 0x0 = %x\n", func->device << 3 | 0x0);
for (j = 0; j < 0x08; j++) {
- temp = pci_find_slot(func->busno, (func->device << 3) | j);
- if (temp)
+ temp = pci_get_bus_and_slot(func->busno, (func->device << 3) | j);
+ if (temp) {
pci_remove_bus_device(temp);
+ pci_dev_put(temp);
+ }
}
+ pci_dev_put(func->dev);
}
/*
@@ -823,7 +826,7 @@ static int ibm_configure_device(struct pci_func *func)
if (!(bus_structure_fixup(func->busno)))
flag = 1;
if (func->dev == NULL)
- func->dev = pci_find_slot(func->busno,
+ func->dev = pci_get_bus_and_slot(func->busno,
PCI_DEVFN(func->device, func->function));
if (func->dev == NULL) {
@@ -836,7 +839,7 @@ static int ibm_configure_device(struct pci_func *func)
if (num)
pci_bus_add_devices(bus);
- func->dev = pci_find_slot(func->busno,
+ func->dev = pci_get_bus_and_slot(func->busno,
PCI_DEVFN(func->device, func->function));
if (func->dev == NULL) {
err("ERROR... : pci_dev still NULL\n");
diff --git a/drivers/pci/hotplug/pci_hotplug_core.c b/drivers/pci/hotplug/pci_hotplug_core.c
index 47bb0e1ff3fa..dd59a050260f 100644
--- a/drivers/pci/hotplug/pci_hotplug_core.c
+++ b/drivers/pci/hotplug/pci_hotplug_core.c
@@ -137,7 +137,7 @@ static int get_##name (struct hotplug_slot *slot, type *value) \
int retval = 0; \
if (try_module_get(ops->owner)) { \
if (ops->get_##name) \
- retval = ops->get_##name (slot, value); \
+ retval = ops->get_##name(slot, value); \
else \
*value = slot->info->name; \
module_put(ops->owner); \
@@ -625,7 +625,7 @@ int pci_hp_register (struct hotplug_slot *slot)
if ((slot->info == NULL) || (slot->ops == NULL))
return -EINVAL;
if (slot->release == NULL) {
- dbg("Why are you trying to register a hotplug slot"
+ dbg("Why are you trying to register a hotplug slot "
"without a proper release function?\n");
return -EINVAL;
}
diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h
index 7959c222dc24..ca656b27a500 100644
--- a/drivers/pci/hotplug/pciehp.h
+++ b/drivers/pci/hotplug/pciehp.h
@@ -82,24 +82,18 @@ struct event_info {
};
struct controller {
- struct controller *next;
struct mutex crit_sect; /* critical section mutex */
struct mutex ctrl_lock; /* controller lock */
int num_slots; /* Number of slots on ctlr */
int slot_num_inc; /* 1 or -1 */
struct pci_dev *pci_dev;
struct list_head slot_list;
- struct slot *slot;
struct hpc_ops *hpc_ops;
wait_queue_head_t queue; /* sleep & wake process */
- u8 bus;
- u8 device;
- u8 function;
u8 slot_device_offset;
u32 first_slot; /* First physical slot number */ /* PCIE only has 1 slot */
u8 slot_bus; /* Bus where the slots handled by this controller sit */
u8 ctrlcap;
- u16 vendor_id;
u8 cap_base;
struct timer_list poll_timer;
volatile int cmd_busy;
@@ -161,6 +155,9 @@ extern int pciehp_configure_device(struct slot *p_slot);
extern int pciehp_unconfigure_device(struct slot *p_slot);
extern void pciehp_queue_pushbutton_work(struct work_struct *work);
int pcie_init(struct controller *ctrl, struct pcie_device *dev);
+int pciehp_enable_slot(struct slot *p_slot);
+int pciehp_disable_slot(struct slot *p_slot);
+int pcie_init_hardware_part2(struct controller *ctrl, struct pcie_device *dev);
static inline struct slot *pciehp_find_slot(struct controller *ctrl, u8 device)
{
diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c
index 6462ac3b405f..7f4836b8e71e 100644
--- a/drivers/pci/hotplug/pciehp_core.c
+++ b/drivers/pci/hotplug/pciehp_core.c
@@ -453,13 +453,9 @@ static int pciehp_probe(struct pcie_device *dev, const struct pcie_port_service_
pci_set_drvdata(pdev, ctrl);
- ctrl->bus = pdev->bus->number; /* ctrl bus */
- ctrl->slot_bus = pdev->subordinate->number; /* bus controlled by this HPC */
-
- ctrl->device = PCI_SLOT(pdev->devfn);
- ctrl->function = PCI_FUNC(pdev->devfn);
- dbg("%s: ctrl bus=0x%x, device=%x, function=%x, irq=%x\n", __FUNCTION__,
- ctrl->bus, ctrl->device, ctrl->function, pdev->irq);
+ dbg("%s: ctrl bus=0x%x, device=%x, function=%x, irq=%x\n",
+ __FUNCTION__, pdev->bus->number, PCI_SLOT(pdev->devfn),
+ PCI_FUNC(pdev->devfn), pdev->irq);
/* Setup the slot information structures */
rc = init_slots(ctrl);
@@ -471,6 +467,11 @@ static int pciehp_probe(struct pcie_device *dev, const struct pcie_port_service_
t_slot = pciehp_find_slot(ctrl, ctrl->slot_device_offset);
t_slot->hpc_ops->get_adapter_status(t_slot, &value); /* Check if slot is occupied */
+ if (value) {
+ rc = pciehp_enable_slot(t_slot);
+ if (rc) /* -ENODEV: shouldn't happen, but deal with it */
+ value = 0;
+ }
if ((POWER_CTRL(ctrl->ctrlcap)) && !value) {
rc = t_slot->hpc_ops->power_off_slot(t_slot); /* Power off slot if not occupied*/
if (rc)
@@ -509,6 +510,24 @@ static int pciehp_suspend (struct pcie_device *dev, pm_message_t state)
static int pciehp_resume (struct pcie_device *dev)
{
printk("%s ENTRY\n", __FUNCTION__);
+ if (pciehp_force) {
+ struct pci_dev *pdev = dev->port;
+ struct controller *ctrl = pci_get_drvdata(pdev);
+ struct slot *t_slot;
+ u8 status;
+
+ /* reinitialize the chipset's event detection logic */
+ pcie_init_hardware_part2(ctrl, dev);
+
+ t_slot = pciehp_find_slot(ctrl, ctrl->slot_device_offset);
+
+ /* Check if slot is occupied */
+ t_slot->hpc_ops->get_adapter_status(t_slot, &status);
+ if (status)
+ pciehp_enable_slot(t_slot);
+ else
+ pciehp_disable_slot(t_slot);
+ }
return 0;
}
#endif
diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c
index f1e0966cee95..b23061c56115 100644
--- a/drivers/pci/hotplug/pciehp_ctrl.c
+++ b/drivers/pci/hotplug/pciehp_ctrl.c
@@ -37,8 +37,6 @@
#include "pciehp.h"
static void interrupt_event_handler(struct work_struct *work);
-static int pciehp_enable_slot(struct slot *p_slot);
-static int pciehp_disable_slot(struct slot *p_slot);
static int queue_interrupt_event(struct slot *p_slot, u32 event_type)
{
@@ -197,12 +195,6 @@ static void set_slot_off(struct controller *ctrl, struct slot * pslot)
__FUNCTION__);
return;
}
- /*
- * After turning power off, we must wait for at least
- * 1 second before taking any action that relies on
- * power having been removed from the slot/adapter.
- */
- msleep(1000);
}
}
@@ -215,15 +207,12 @@ static void set_slot_off(struct controller *ctrl, struct slot * pslot)
*/
static int board_added(struct slot *p_slot)
{
- u8 hp_slot;
int retval = 0;
struct controller *ctrl = p_slot->ctrl;
- hp_slot = p_slot->device - ctrl->slot_device_offset;
-
dbg("%s: slot device, slot offset, hp slot = %d, %d ,%d\n",
__FUNCTION__, p_slot->device,
- ctrl->slot_device_offset, hp_slot);
+ ctrl->slot_device_offset, p_slot->hp_slot);
if (POWER_CTRL(ctrl->ctrlcap)) {
/* Power on slot */
@@ -281,8 +270,6 @@ err_exit:
*/
static int remove_board(struct slot *p_slot)
{
- u8 device;
- u8 hp_slot;
int retval = 0;
struct controller *ctrl = p_slot->ctrl;
@@ -290,11 +277,7 @@ static int remove_board(struct slot *p_slot)
if (retval)
return retval;
- device = p_slot->device;
- hp_slot = p_slot->device - ctrl->slot_device_offset;
- p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
-
- dbg("In %s, hp_slot = %d\n", __FUNCTION__, hp_slot);
+ dbg("In %s, hp_slot = %d\n", __FUNCTION__, p_slot->hp_slot);
if (POWER_CTRL(ctrl->ctrlcap)) {
/* power off slot */
@@ -621,12 +604,6 @@ int pciehp_disable_slot(struct slot *p_slot)
mutex_unlock(&p_slot->ctrl->crit_sect);
return -EINVAL;
}
- /*
- * After turning power off, we must wait for at least
- * 1 second before taking any action that relies on
- * power having been removed from the slot/adapter.
- */
- msleep(1000);
}
ret = remove_board(p_slot);
diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c
index 06d025b8b13f..6eba9b2cfb90 100644
--- a/drivers/pci/hotplug/pciehp_hpc.c
+++ b/drivers/pci/hotplug/pciehp_hpc.c
@@ -636,15 +636,57 @@ static int hpc_power_on_slot(struct slot * slot)
return retval;
}
+static inline int pcie_mask_bad_dllp(struct controller *ctrl)
+{
+ struct pci_dev *dev = ctrl->pci_dev;
+ int pos;
+ u32 reg;
+
+ pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
+ if (!pos)
+ return 0;
+ pci_read_config_dword(dev, pos + PCI_ERR_COR_MASK, &reg);
+ if (reg & PCI_ERR_COR_BAD_DLLP)
+ return 0;
+ reg |= PCI_ERR_COR_BAD_DLLP;
+ pci_write_config_dword(dev, pos + PCI_ERR_COR_MASK, reg);
+ return 1;
+}
+
+static inline void pcie_unmask_bad_dllp(struct controller *ctrl)
+{
+ struct pci_dev *dev = ctrl->pci_dev;
+ u32 reg;
+ int pos;
+
+ pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
+ if (!pos)
+ return;
+ pci_read_config_dword(dev, pos + PCI_ERR_COR_MASK, &reg);
+ if (!(reg & PCI_ERR_COR_BAD_DLLP))
+ return;
+ reg &= ~PCI_ERR_COR_BAD_DLLP;
+ pci_write_config_dword(dev, pos + PCI_ERR_COR_MASK, reg);
+}
+
static int hpc_power_off_slot(struct slot * slot)
{
struct controller *ctrl = slot->ctrl;
u16 slot_cmd;
u16 cmd_mask;
int retval = 0;
+ int changed;
dbg("%s: slot->hp_slot %x\n", __FUNCTION__, slot->hp_slot);
+ /*
+ * Set Bad DLLP Mask bit in Correctable Error Mask
+ * Register. This is the workaround against Bad DLLP error
+ * that sometimes happens during turning power off the slot
+ * which conforms to PCI Express 1.0a spec.
+ */
+ changed = pcie_mask_bad_dllp(ctrl);
+
slot_cmd = POWER_OFF;
cmd_mask = PWR_CTRL;
/*
@@ -674,6 +716,16 @@ static int hpc_power_off_slot(struct slot * slot)
dbg("%s: SLOTCTRL %x write cmd %x\n",
__FUNCTION__, ctrl->cap_base + SLOTCTRL, slot_cmd);
+ /*
+ * After turning power off, we must wait for at least 1 second
+ * before taking any action that relies on power having been
+ * removed from the slot/adapter.
+ */
+ msleep(1000);
+
+ if (changed)
+ pcie_unmask_bad_dllp(ctrl);
+
return retval;
}
@@ -1067,13 +1119,143 @@ int pciehp_acpi_get_hp_hw_control_from_firmware(struct pci_dev *dev)
}
#endif
-int pcie_init(struct controller * ctrl, struct pcie_device *dev)
+static int pcie_init_hardware_part1(struct controller *ctrl,
+ struct pcie_device *dev)
+{
+ int rc;
+ u16 temp_word;
+ u32 slot_cap;
+ u16 slot_status;
+
+ rc = pciehp_readl(ctrl, SLOTCAP, &slot_cap);
+ if (rc) {
+ err("%s: Cannot read SLOTCAP register\n", __FUNCTION__);
+ return -1;
+ }
+
+ /* Mask Hot-plug Interrupt Enable */
+ rc = pciehp_readw(ctrl, SLOTCTRL, &temp_word);
+ if (rc) {
+ err("%s: Cannot read SLOTCTRL register\n", __FUNCTION__);
+ return -1;
+ }
+
+ dbg("%s: SLOTCTRL %x value read %x\n",
+ __FUNCTION__, ctrl->cap_base + SLOTCTRL, temp_word);
+ temp_word = (temp_word & ~HP_INTR_ENABLE & ~CMD_CMPL_INTR_ENABLE) |
+ 0x00;
+
+ rc = pciehp_writew(ctrl, SLOTCTRL, temp_word);
+ if (rc) {
+ err("%s: Cannot write to SLOTCTRL register\n", __FUNCTION__);
+ return -1;
+ }
+
+ rc = pciehp_readw(ctrl, SLOTSTATUS, &slot_status);
+ if (rc) {
+ err("%s: Cannot read SLOTSTATUS register\n", __FUNCTION__);
+ return -1;
+ }
+
+ temp_word = 0x1F; /* Clear all events */
+ rc = pciehp_writew(ctrl, SLOTSTATUS, temp_word);
+ if (rc) {
+ err("%s: Cannot write to SLOTSTATUS register\n", __FUNCTION__);
+ return -1;
+ }
+ return 0;
+}
+
+int pcie_init_hardware_part2(struct controller *ctrl, struct pcie_device *dev)
{
int rc;
u16 temp_word;
- u16 cap_reg;
u16 intr_enable = 0;
u32 slot_cap;
+ u16 slot_status;
+
+ rc = pciehp_readw(ctrl, SLOTCTRL, &temp_word);
+ if (rc) {
+ err("%s: Cannot read SLOTCTRL register\n", __FUNCTION__);
+ goto abort;
+ }
+
+ intr_enable = intr_enable | PRSN_DETECT_ENABLE;
+
+ rc = pciehp_readl(ctrl, SLOTCAP, &slot_cap);
+ if (rc) {
+ err("%s: Cannot read SLOTCAP register\n", __FUNCTION__);
+ goto abort;
+ }
+
+ if (ATTN_BUTTN(slot_cap))
+ intr_enable = intr_enable | ATTN_BUTTN_ENABLE;
+
+ if (POWER_CTRL(slot_cap))
+ intr_enable = intr_enable | PWR_FAULT_DETECT_ENABLE;
+
+ if (MRL_SENS(slot_cap))
+ intr_enable = intr_enable | MRL_DETECT_ENABLE;
+
+ temp_word = (temp_word & ~intr_enable) | intr_enable;
+
+ if (pciehp_poll_mode) {
+ temp_word = (temp_word & ~HP_INTR_ENABLE) | 0x0;
+ } else {
+ temp_word = (temp_word & ~HP_INTR_ENABLE) | HP_INTR_ENABLE;
+ }
+
+ /*
+ * Unmask Hot-plug Interrupt Enable for the interrupt
+ * notification mechanism case.
+ */
+ rc = pciehp_writew(ctrl, SLOTCTRL, temp_word);
+ if (rc) {
+ err("%s: Cannot write to SLOTCTRL register\n", __FUNCTION__);
+ goto abort;
+ }
+ rc = pciehp_readw(ctrl, SLOTSTATUS, &slot_status);
+ if (rc) {
+ err("%s: Cannot read SLOTSTATUS register\n", __FUNCTION__);
+ goto abort_disable_intr;
+ }
+
+ temp_word = 0x1F; /* Clear all events */
+ rc = pciehp_writew(ctrl, SLOTSTATUS, temp_word);
+ if (rc) {
+ err("%s: Cannot write to SLOTSTATUS register\n", __FUNCTION__);
+ goto abort_disable_intr;
+ }
+
+ if (pciehp_force) {
+ dbg("Bypassing BIOS check for pciehp use on %s\n",
+ pci_name(ctrl->pci_dev));
+ } else {
+ rc = pciehp_get_hp_hw_control_from_firmware(ctrl->pci_dev);
+ if (rc)
+ goto abort_disable_intr;
+ }
+
+ return 0;
+
+ /* We end up here for the many possible ways to fail this API. */
+abort_disable_intr:
+ rc = pciehp_readw(ctrl, SLOTCTRL, &temp_word);
+ if (!rc) {
+ temp_word &= ~(intr_enable | HP_INTR_ENABLE);
+ rc = pciehp_writew(ctrl, SLOTCTRL, temp_word);
+ }
+ if (rc)
+ err("%s : disabling interrupts failed\n", __FUNCTION__);
+abort:
+ return -1;
+}
+
+int pcie_init(struct controller *ctrl, struct pcie_device *dev)
+{
+ int rc;
+ u16 cap_reg;
+ u32 slot_cap;
int cap_base;
u16 slot_status, slot_ctrl;
struct pci_dev *pdev;
@@ -1084,9 +1266,10 @@ int pcie_init(struct controller * ctrl, struct pcie_device *dev)
dbg("%s: hotplug controller vendor id 0x%x device id 0x%x\n",
__FUNCTION__, pdev->vendor, pdev->device);
- if ((cap_base = pci_find_capability(pdev, PCI_CAP_ID_EXP)) == 0) {
+ cap_base = pci_find_capability(pdev, PCI_CAP_ID_EXP);
+ if (cap_base == 0) {
dbg("%s: Can't find PCI_CAP_ID_EXP (0x10)\n", __FUNCTION__);
- goto abort_free_ctlr;
+ goto abort;
}
ctrl->cap_base = cap_b