aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@g5.osdl.org>2006-09-27 08:09:15 -0700
committerLinus Torvalds <torvalds@g5.osdl.org>2006-09-27 08:09:15 -0700
commitff0972c26bbf209da6f6d244cce60e695df863f6 (patch)
tree79db41583bf3847139ace7a6d1eff0266ea63bc2
parenta09fc446fb6d541281d9559fe7215d7c0d3cc9ce (diff)
parentc9d86d76c1cdd76d67292ab75643db66573ca7dd (diff)
Merge master.kernel.org:/pub/scm/linux/kernel/git/gregkh/pci-2.6
* master.kernel.org:/pub/scm/linux/kernel/git/gregkh/pci-2.6: (28 commits) pciehp - fix wrong return value IA64: PCI: dont disable irq which is not enabled acpiphp: add support for ioapic hot-remove PCI: assign ioapic resource at hotplug acpiphp: disable bridges acpiphp: stop bus device before acpi_bus_trim PCI: add pci_stop_bus_device acpiphp: do not initialize existing ioapics acpiphp: initialize ioapics before starting devices acpiphp: set hpp values before starting devices PCI Hotplug: cleanup pcihp skeleton code. PCI: Restore PCI Express capability registers after PM event PCI: drivers/pci/hotplug/acpiphp_glue.c: make a function static PCI: Multiprobe sanitizer PCI: fix __must_check warnings PCI Hotplug: fix __must_check warnings SHPCHP: fix __must_check warnings PCI-Express AER implemetation: pcie_portdrv error handler PCI-Express AER implemetation: AER core and aerdriver PCI-Express AER implemetation: export pcie_port_bus_type ...
-rw-r--r--Documentation/pcieaer-howto.txt253
-rw-r--r--arch/ia64/pci/pci.c3
-rw-r--r--arch/powerpc/sysdev/mpic.c2
-rw-r--r--drivers/infiniband/hw/ipath/ipath_iba6110.c5
-rw-r--r--drivers/pci/bus.c22
-rw-r--r--drivers/pci/hotplug/acpiphp.h5
-rw-r--r--drivers/pci/hotplug/acpiphp_glue.c127
-rw-r--r--drivers/pci/hotplug/fakephp.c18
-rw-r--r--drivers/pci/hotplug/pci_hotplug.h4
-rw-r--r--drivers/pci/hotplug/pci_hotplug_core.c157
-rw-r--r--drivers/pci/hotplug/pciehp_ctrl.c12
-rw-r--r--drivers/pci/hotplug/pcihp_skeleton.c9
-rw-r--r--drivers/pci/hotplug/shpchp.h2
-rw-r--r--drivers/pci/hotplug/shpchp_core.c6
-rw-r--r--drivers/pci/hotplug/shpchp_sysfs.c4
-rw-r--r--drivers/pci/msi.c51
-rw-r--r--drivers/pci/pci-driver.c11
-rw-r--r--drivers/pci/pci-sysfs.c153
-rw-r--r--drivers/pci/pci.c50
-rw-r--r--drivers/pci/pci.h2
-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.c346
-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.h2
-rw-r--r--drivers/pci/pcie/portdrv_bus.c1
-rw-r--r--drivers/pci/pcie/portdrv_core.c11
-rw-r--r--drivers/pci/pcie/portdrv_pci.c211
-rw-r--r--drivers/pci/probe.c16
-rw-r--r--drivers/pci/quirks.c104
-rw-r--r--drivers/pci/remove.c37
-rw-r--r--drivers/pci/setup-bus.c13
-rw-r--r--include/linux/aer.h24
-rw-r--r--include/linux/pci.h5
-rw-r--r--include/linux/pci_ids.h1
-rw-r--r--include/linux/pci_regs.h2
-rw-r--r--include/linux/pcieport_if.h6
-rw-r--r--kernel/resource.c32
43 files changed, 2719 insertions, 211 deletions
diff --git a/Documentation/pcieaer-howto.txt b/Documentation/pcieaer-howto.txt
new file mode 100644
index 00000000000..16c251230c8
--- /dev/null
+++ b/Documentation/pcieaer-howto.txt
@@ -0,0 +1,253 @@
+ The PCI Express Advanced Error Reporting Driver Guide HOWTO
+ T. Long Nguyen <tom.l.nguyen@intel.com>
+ Yanmin Zhang <yanmin.zhang@intel.com>
+ 07/29/2006
+
+
+1. Overview
+
+1.1 About this guide
+
+This guide describes the basics of the PCI Express Advanced Error
+Reporting (AER) driver and provides information on how to use it, as
+well as how to enable the drivers of endpoint devices to conform with
+PCI Express AER driver.
+
+1.2 Copyright © Intel Corporation 2006.
+
+1.3 What is the PCI Express AER Driver?
+
+PCI Express error signaling can occur on the PCI Express link itself
+or on behalf of transactions initiated on the link. PCI Express
+defines two error reporting paradigms: the baseline capability and
+the Advanced Error Reporting capability. The baseline capability is
+required of all PCI Express components providing a minimum defined
+set of error reporting requirements. Advanced Error Reporting
+capability is implemented with a PCI Express advanced error reporting
+extended capability structure providing more robust error reporting.
+
+The PCI Express AER driver provides the infrastructure to support PCI
+Express Advanced Error Reporting capability. The PCI Express AER
+driver provides three basic functions:
+
+- Gathers the comprehensive error information if errors occurred.
+- Reports error to the users.
+- Performs error recovery actions.
+
+AER driver only attaches root ports which support PCI-Express AER
+capability.
+
+
+2. User Guide
+
+2.1 Include the PCI Express AER Root Driver into the Linux Kernel
+
+The PCI Express AER Root driver is a Root Port service driver attached
+to the PCI Express Port Bus driver. If a user wants to use it, the driver
+has to be compiled. Option CONFIG_PCIEAER supports this capability. It
+depends on CONFIG_PCIEPORTBUS, so pls. set CONFIG_PCIEPORTBUS=y and
+CONFIG_PCIEAER = y.
+
+2.2 Load PCI Express AER Root Driver
+There is a case where a system has AER support in BIOS. Enabling the AER
+Root driver and having AER support in BIOS may result unpredictable
+behavior. To avoid this conflict, a successful load of the AER Root driver
+requires ACPI _OSC support in the BIOS to allow the AER Root driver to
+request for native control of AER. See the PCI FW 3.0 Specification for
+details regarding OSC usage. Currently, lots of firmwares don't provide
+_OSC support while they use PCI Express. To support such firmwares,
+forceload, a parameter of type bool, could enable AER to continue to
+be initiated although firmwares have no _OSC support. To enable the
+walkaround, pls. add aerdriver.forceload=y to kernel boot parameter line
+when booting kernel. Note that forceload=n by default.
+
+2.3 AER error output
+When a PCI-E AER error is captured, an error message will be outputed to
+console. If it's a correctable error, it is outputed as a warning.
+Otherwise, it is printed as an error. So users could choose different
+log level to filter out correctable error messages.
+
+Below shows an example.
++------ PCI-Express Device Error -----+
+Error Severity : Uncorrected (Fatal)
+PCIE Bus Error type : Transaction Layer
+Unsupported Request : First
+Requester ID : 0500
+VendorID=8086h, DeviceID=0329h, Bus=05h, Device=00h, Function=00h
+TLB Header:
+04000001 00200a03 05010000 00050100
+
+In the example, 'Requester ID' means the ID of the device who sends
+the error message to root port. Pls. refer to pci express specs for
+other fields.
+
+
+3. Developer Guide
+
+To enable AER aware support requires a software driver to configure
+the AER capability structure within its device and to provide callbacks.
+
+To support AER better, developers need understand how AER does work
+firstly.
+
+PCI Express errors are classified into two types: correctable errors
+and uncorrectable errors. This classification is based on the impacts
+of those errors, which may result in degraded performance or function
+failure.
+
+Correctable errors pose no impacts on the functionality of the
+interface. The PCI Express protocol can recover without any software
+intervention or any loss of data. These errors are detected and
+corrected by hardware. Unlike correctable errors, uncorrectable
+errors impact functionality of the interface. Uncorrectable errors
+can cause a particular transaction or a particular PCI Express link
+to be unreliable. Depending on those error conditions, uncorrectable
+errors are further classified into non-fatal errors and fatal errors.
+Non-fatal errors cause the particular transaction to be unreliable,
+but the PCI Express link itself is fully functional. Fatal errors, on
+the other hand, cause the link to be unreliable.
+
+When AER is enabled, a PCI Express device will automatically send an
+error message to the PCIE root port above it when the device captures
+an error. The Root Port, upon receiving an error reporting message,
+internally processes and logs the error message in its PCI Express
+capability structure. Error information being logged includes storing
+the error reporting agent's requestor ID into the Error Source
+Identification Registers and setting the error bits of the Root Error
+Status Register accordingly. If AER error reporting is enabled in Root
+Error Command Register, the Root Port generates an interrupt if an
+error is detected.
+
+Note that the errors as described above are related to the PCI Express
+hierarchy and links. These errors do not include any device specific
+errors because device specific errors will still get sent directly to
+the device driver.
+
+3.1 Configure the AER capability structure
+
+AER aware drivers of PCI Express component need change the device
+control registers to enable AER. They also could change AER registers,
+including mask and severity registers. Helper function
+pci_enable_pcie_error_reporting could be used to enable AER. See
+section 3.3.
+
+3.2. Provide callbacks
+
+3.2.1 callback reset_link to reset pci express link
+
+This callback is used to reset the pci express physical link when a
+fatal error happens. The root port aer service driver provides a
+default reset_link function, but different upstream ports might
+have different specifications to reset pci express link, so all
+upstream ports should provide their own reset_link functions.
+
+In struct pcie_port_service_driver, a new pointer, reset_link, is
+added.
+
+pci_ers_result_t (*reset_link) (struct pci_dev *dev);
+
+Section 3.2.2.2 provides more detailed info on when to call
+reset_link.
+
+3.2.2 PCI error-recovery callbacks
+
+The PCI Express AER Root driver uses error callbacks to coordinate
+with downstream device drivers associated with a hierarchy in question
+when performing error recovery actions.
+
+Data struct pci_driver has a pointer, err_handler, to point to
+pci_error_handlers who consists of a couple of callback function
+pointers. AER driver follows the rules defined in
+pci-error-recovery.txt except pci express specific parts (e.g.
+reset_link). Pls. refer to pci-error-recovery.txt for detailed
+definitions of the callbacks.
+
+Below sections specify when to call the error callback functions.
+
+3.2.2.1 Correctable errors
+
+Correctable errors pose no impacts on the functionality of
+the interface. The PCI Express protocol can recover without any
+software intervention or any loss of data. These errors do not
+require any recovery actions. The AER driver clears the device's
+correctable error status register accordingly and logs these errors.
+
+3.2.2.2 Non-correctable (non-fatal and fatal) errors
+
+If an error message indicates a non-fatal error, performing link reset
+at upstream is not required. The AER driver calls error_detected(dev,
+pci_channel_io_normal) to all drivers associated within a hierarchy in
+question. for example,
+EndPoint<==>DownstreamPort B<==>UpstreamPort A<==>RootPort.
+If Upstream port A captures an AER error, the hierarchy consists of
+Downstream port B and EndPoint.
+
+A driver may return PCI_ERS_RESULT_CAN_RECOVER,
+PCI_ERS_RESULT_DISCONNECT, or PCI_ERS_RESULT_NEED_RESET, depending on
+whether it can recover or the AER driver calls mmio_enabled as next.
+
+If an error message indicates a fatal error, kernel will broadcast
+error_detected(dev, pci_channel_io_frozen) to all drivers within
+a hierarchy in question. Then, performing link reset at upstream is
+necessary. As different kinds of devices might use different approaches
+to reset link, AER port service driver is required to provide the
+function to reset link. Firstly, kernel looks for if the upstream
+component has an aer driver. If it has, kernel uses the reset_link
+callback of the aer driver. If the upstream component has no aer driver
+and the port is downstream port, we will use the aer driver of the
+root port who reports the AER error. As for upstream ports,
+they should provide their own aer service drivers with reset_link
+function. If error_detected returns PCI_ERS_RESULT_CAN_RECOVER and
+reset_link returns PCI_ERS_RESULT_RECOVERED, the error handling goes
+to mmio_enabled.
+
+3.3 helper functions
+
+3.3.1 int pci_find_aer_capability(struct pci_dev *dev);
+pci_find_aer_capability locates the PCI Express AER capability
+in the device configuration space. If the device doesn't support
+PCI-Express AER, the function returns 0.
+
+3.3.2 int pci_enable_pcie_error_reporting(struct pci_dev *dev);
+pci_enable_pcie_error_reporting enables the device to send error
+messages to root port when an error is detected. Note that devices
+don't enable the error reporting by default, so device drivers need
+call this function to enable it.
+
+3.3.3 int pci_disable_pcie_error_reporting(struct pci_dev *dev);
+pci_disable_pcie_error_reporting disables the device to send error
+messages to root port when an error is detected.
+
+3.3.4 int pci_cleanup_aer_uncorrect_error_status(struct pci_dev *dev);
+pci_cleanup_aer_uncorrect_error_status cleanups the uncorrectable
+error status register.
+
+3.4 Frequent Asked Questions
+
+Q: What happens if a PCI Express device driver does not provide an
+error recovery handler (pci_driver->err_handler is equal to NULL)?
+
+A: The devices attached with the driver won't be recovered. If the
+error is fatal, kernel will print out warning messages. Please refer
+to section 3 for more information.
+
+Q: What happens if an upstream port service driver does not provide
+callback reset_link?
+
+A: Fatal error recovery will fail if the errors are reported by the
+upstream ports who are attached by the service driver.
+
+Q: How does this infrastructure deal with driver that is not PCI
+Express aware?
+
+A: This infrastructure calls the error callback functions of the
+driver when an error happens. But if the driver is not aware of
+PCI Express, the device might not report its own errors to root
+port.
+
+Q: What modifications will that driver need to make it compatible
+with the PCI Express AER Root driver?
+
+A: It could call the helper functions to enable AER in devices and
+cleanup uncorrectable status register. Pls. refer to section 3.3.
+
diff --git a/arch/ia64/pci/pci.c b/arch/ia64/pci/pci.c
index 60b45e79f08..15c7c670da3 100644
--- a/arch/ia64/pci/pci.c
+++ b/arch/ia64/pci/pci.c
@@ -562,7 +562,8 @@ pcibios_enable_device (struct pci_dev *dev, int mask)
void
pcibios_disable_device (struct pci_dev *dev)
{
- acpi_pci_irq_disable(dev);
+ if (dev->is_enabled)
+ acpi_pci_irq_disable(dev);
}
void
diff --git a/arch/powerpc/sysdev/mpic.c b/arch/powerpc/sysdev/mpic.c
index b604926401f..723972bb5bd 100644
--- a/arch/powerpc/sysdev/mpic.c
+++ b/arch/powerpc/sysdev/mpic.c
@@ -339,7 +339,7 @@ static void __init mpic_scan_ht_pic(struct mpic *mpic, u8 __iomem *devbase,
for (pos = readb(devbase + PCI_CAPABILITY_LIST); pos != 0;
pos = readb(devbase + pos + PCI_CAP_LIST_NEXT)) {
u8 id = readb(devbase + pos + PCI_CAP_LIST_ID);
- if (id == PCI_CAP_ID_HT_IRQCONF) {
+ if (id == PCI_CAP_ID_HT) {
id = readb(devbase + pos + 3);
if (id == 0x80)
break;
diff --git a/drivers/infiniband/hw/ipath/ipath_iba6110.c b/drivers/infiniband/hw/ipath/ipath_iba6110.c
index bf2455a6d56..5c9b509e40e 100644
--- a/drivers/infiniband/hw/ipath/ipath_iba6110.c
+++ b/drivers/infiniband/hw/ipath/ipath_iba6110.c
@@ -742,7 +742,6 @@ static int ipath_setup_ht_reset(struct ipath_devdata *dd)
return 0;
}
-#define HT_CAPABILITY_ID 0x08 /* HT capabilities not defined in kernel */
#define HT_INTR_DISC_CONFIG 0x80 /* HT interrupt and discovery cap */
#define HT_INTR_REG_INDEX 2 /* intconfig requires indirect accesses */
@@ -973,7 +972,7 @@ static int ipath_setup_ht_config(struct ipath_devdata *dd,
* do this early, before we ever enable errors or hardware errors,
* mostly to avoid causing the chip to enter freeze mode.
*/
- pos = pci_find_capability(pdev, HT_CAPABILITY_ID);
+ pos = pci_find_capability(pdev, PCI_CAP_ID_HT);
if (!pos) {
ipath_dev_err(dd, "Couldn't find HyperTransport "
"capability; no interrupts\n");
@@ -996,7 +995,7 @@ static int ipath_setup_ht_config(struct ipath_devdata *dd,
else if (cap_type == HT_INTR_DISC_CONFIG)
ihandler = set_int_handler(dd, pdev, pos);
} while ((pos = pci_find_next_capability(pdev, pos,
- HT_CAPABILITY_ID)));
+ PCI_CAP_ID_HT)));
if (!ihandler) {
ipath_dev_err(dd, "Couldn't find interrupt handler in "
diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c
index 5f7db9d2436..aadaa3c8096 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/acpiphp.h b/drivers/pci/hotplug/acpiphp.h
index be104eced34..7fff07e877c 100644
--- a/drivers/pci/hotplug/acpiphp.h
+++ b/drivers/pci/hotplug/acpiphp.h
@@ -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_glue.c b/drivers/pci/hotplug/acpiphp_glue.c
index ae67a8f55ba..83e8e4412de 100644
--- a/drivers/pci/hotplug/acpiphp_glue.c
+++ b/drivers/pci/hotplug/acpiphp_glue.c
@@ -53,6 +53,8 @@
#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 */
diff --git a/drivers/pci/hotplug/fakephp.c b/drivers/pci/hotplug/fakephp.c
index dd2b762777c..05a4f0f9018 100644
--- a/drivers/pci/hotplug/fakephp.c
+++ b/drivers/pci/hotplug/fakephp.c
@@ -176,7 +176,9 @@ 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)) {
@@ -185,8 +187,12 @@ static void pci_rescan_slot(struct pci_dev *temp)
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? */
@@ -205,8 +211,12 @@ static void pci_rescan_slot(struct pci_dev *temp)
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);
}
}
}
diff --git a/drivers/pci/hotplug/pci_hotplug.h b/drivers/pci/hotplug/pci_hotplug.h
index e929b7c1142..772523dc386 100644
--- a/drivers/pci/hotplug/pci_hotplug.h
+++ b/drivers/pci/hotplug/pci_hotplug.h
@@ -172,8 +172,8 @@ struct hotplug_slot {
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 int __must_check 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) */
diff --git a/drivers/pci/hotplug/pci_hotplug_core.c b/drivers/pci/hotplug/pci_hotplug_core.c
index b7b378df89e..e2823ea9c4e 100644
--- a/drivers/pci/hotplug/pci_hotplug_core.c
+++ b/drivers/pci/hotplug/pci_hotplug_core.c
@@ -482,31 +482,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_attentio