]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
[PATCH] pciehp: clean-up how we request control of hotplug hardware
authorrajesh.shah@intel.com <rajesh.shah@intel.com>
Tue, 1 Nov 2005 00:20:12 +0000 (16:20 -0800)
committerGreg Kroah-Hartman <gregkh@suse.de>
Fri, 11 Nov 2005 00:09:15 +0000 (16:09 -0800)
This patch further tweaks how we request control of hotplug
controller hardware from BIOS. We first search the ACPI namespace
corresponding to a specific hotplug controller looking for an
_OSC or OSHP method. On failure, we successively move to the
ACPI parent object, till we hit the highest level host bridge
in the hierarchy. This allows for different types of BIOS's
which place the _OSC/OSHP methods at various places in the acpi
namespace, while still not encroaching on the namespace of
some other root level host bridge.

This patch also introduces a new load time option (pciehp_force)
that allows us to bypass all _OSC/OSHP checking. Not supporting
these methods seems to be be the most common ACPI firmware problem
we've run into. This will still _not_ allow the pciehp driver to
work correctly if the BIOS really doesn't support pciehp (i.e. if
it doesn't generate a hotplug interrupt). Use this option with
caution.  Some BIOS's may deliberately not build any _OSC/OSHP
methods to make sure it retains control the hotplug hardware.
Using the pciehp_force parameter for such systems can lead to
two separate entities trying to control the same hardware.

Signed-off-by: Rajesh Shah <rajesh.shah@intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/acpi/glue.c
drivers/pci/hotplug/pciehp.h
drivers/pci/hotplug/pciehp_core.c
drivers/pci/hotplug/pciehp_hpc.c
drivers/pci/hotplug/pciehprm_acpi.c

index 3937adf4e5e5c04bd9d940648e6dc1d628c9a4f1..aa993715d6445d40c6ca668616b8f203d35a6b2c 100644 (file)
@@ -203,6 +203,7 @@ acpi_handle acpi_get_pci_rootbridge_handle(unsigned int seg, unsigned int bus)
        acpi_get_devices(PCI_ROOT_HID_STRING, find_pci_rootbridge, &find, NULL);
        return find.handle;
 }
+EXPORT_SYMBOL_GPL(acpi_get_pci_rootbridge_handle);
 
 /* Get device's handler per its address under its parent */
 struct acpi_find_child {
index e1c2cea305f8e23f1a2a85124ed0c4c65f6027ce..e71f78318d6ed4feebe576fa721d8aaad5a63923 100644 (file)
@@ -40,6 +40,7 @@
 extern int pciehp_poll_mode;
 extern int pciehp_poll_time;
 extern int pciehp_debug;
+extern int pciehp_force;
 
 /*#define dbg(format, arg...) do { if (pciehp_debug) printk(KERN_DEBUG "%s: " format, MY_NAME , ## arg); } while (0)*/
 #define dbg(format, arg...) do { if (pciehp_debug) printk("%s: " format, MY_NAME , ## arg); } while (0)
index 0bfc37325923165bce1ecfcc11436c8730a3284d..8df7048603480427e8cff83517d76647cc715627 100644 (file)
@@ -39,6 +39,7 @@
 int pciehp_debug;
 int pciehp_poll_mode;
 int pciehp_poll_time;
+int pciehp_force;
 struct controller *pciehp_ctrl_list;
 
 #define DRIVER_VERSION "0.4"
@@ -52,9 +53,11 @@ MODULE_LICENSE("GPL");
 module_param(pciehp_debug, bool, 0644);
 module_param(pciehp_poll_mode, bool, 0644);
 module_param(pciehp_poll_time, int, 0644);
+module_param(pciehp_force, bool, 0644);
 MODULE_PARM_DESC(pciehp_debug, "Debugging mode enabled or not");
 MODULE_PARM_DESC(pciehp_poll_mode, "Using polling mechanism for hot-plug events or not");
 MODULE_PARM_DESC(pciehp_poll_time, "Polling mechanism frequency, in seconds");
+MODULE_PARM_DESC(pciehp_force, "Force pciehp, even if _OSC and OSHP are missing");
 
 #define PCIE_MODULE_NAME "pciehp"
 
index 5c812b858847018d9e9ddaa5d608da975b658a03..2d2539ba7303cc178591295fc9113f45960215c7 100644 (file)
@@ -1417,9 +1417,14 @@ int pcie_init(struct controller * ctrl, struct pcie_device *dev)
                goto abort_free_ctlr;
        }
        
-       rc = get_hp_hw_control_from_firmware(ctrl->pci_dev);
-       if (rc)
-               goto abort_free_ctlr;
+       if (pciehp_force) {
+               dbg("Bypassing BIOS check for pciehp use on %s\n",
+                               pci_name(ctrl->pci_dev));
+       } else {
+               rc = get_hp_hw_control_from_firmware(ctrl->pci_dev);
+               if (rc)
+                       goto abort_free_ctlr;
+       }
 
        /*  Add this HPC instance into the HPC list */
        spin_lock(&list_lock);
index 5acdae3d52b18bb98789908ad191209bdd319b1a..4d013f86b1a59462ff96431c2e22a46445f36659 100644 (file)
@@ -132,7 +132,7 @@ static acpi_status acpi_run_oshp(acpi_handle handle)
        /* run OSHP */
        status = acpi_evaluate_object(handle, METHOD_NAME_OSHP, NULL, NULL);
        if (ACPI_FAILURE(status)) {
-               err("%s:%s OSHP fails=0x%x\n", __FUNCTION__, path_name,
+               dbg("%s:%s OSHP fails=0x%x\n", __FUNCTION__, path_name,
                                status);
        } else {
                dbg("%s:%s OSHP passes\n", __FUNCTION__, path_name);
@@ -140,32 +140,92 @@ static acpi_status acpi_run_oshp(acpi_handle handle)
        return status;
 }
 
+static int is_root_bridge(acpi_handle handle)
+{
+       acpi_status status;
+       struct acpi_device_info *info;
+       struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
+       int i;
+
+       status = acpi_get_object_info(handle, &buffer);
+       if (ACPI_SUCCESS(status)) {
+               info = buffer.pointer;
+               if ((info->valid & ACPI_VALID_HID) &&
+                       !strcmp(PCI_ROOT_HID_STRING,
+                                       info->hardware_id.value)) {
+                       acpi_os_free(buffer.pointer);
+                       return 1;
+               }
+               if (info->valid & ACPI_VALID_CID) {
+                       for (i=0; i < info->compatibility_id.count; i++) {
+                               if (!strcmp(PCI_ROOT_HID_STRING,
+                                       info->compatibility_id.id[i].value)) {
+                                       acpi_os_free(buffer.pointer);
+                                       return 1;
+                               }
+                       }
+               }
+       }
+       return 0;
+}
+
 int get_hp_hw_control_from_firmware(struct pci_dev *dev)
 {
        acpi_status status;
-       acpi_handle handle = DEVICE_ACPI_HANDLE(&(dev->dev));
+       acpi_handle chandle, handle = DEVICE_ACPI_HANDLE(&(dev->dev));
+       struct pci_dev *pdev = dev;
+       u8 *path_name;
        /*
         * Per PCI firmware specification, we should run the ACPI _OSC
-        * method to get control of hotplug hardware before using it
+        * method to get control of hotplug hardware before using it.
+        * If an _OSC is missing, we look for an OSHP to do the same thing.
+        * To handle different BIOS behavior, we look for _OSC and OSHP
+        * within the scope of the hotplug controller and its parents, upto
+        * the host bridge under which this controller exists.
         */
-       status = pci_osc_control_set(handle,
-                       OSC_PCI_EXPRESS_NATIVE_HP_CONTROL);
-
-       /* Fixme: fail native hotplug if _OSC does not exist for root ports */
-       if (status == AE_NOT_FOUND) {
+       while (!handle) {
                /*
-                * Some older BIOS's don't support _OSC but support
-                * OSHP to do the same thing
+                * This hotplug controller was not listed in the ACPI name
+                * space at all. Try to get acpi handle of parent pci bus.
                 */
-               status = acpi_run_oshp(handle);
+               if (!pdev || !pdev->bus->parent)
+                       break;
+               dbg("Could not find %s in acpi namespace, trying parent\n",
+                               pci_name(pdev));
+               if (!pdev->bus->parent->self)
+                       /* Parent must be a host bridge */
+                       handle = acpi_get_pci_rootbridge_handle(
+                                       pci_domain_nr(pdev->bus->parent),
+                                       pdev->bus->parent->number);
+               else
+                       handle = DEVICE_ACPI_HANDLE(
+                                       &(pdev->bus->parent->self->dev));
+               pdev = pdev->bus->parent->self;
        }
-       if (ACPI_FAILURE(status)) {
-               err("Cannot get control of hotplug hardware\n");
-               return -1;
+
+       while (handle) {
+               path_name = acpi_path_name(handle);
+               dbg("Trying to get hotplug control for %s \n", path_name);
+               status = pci_osc_control_set(handle,
+                               OSC_PCI_EXPRESS_NATIVE_HP_CONTROL);
+               if (status == AE_NOT_FOUND)
+                       status = acpi_run_oshp(handle);
+               if (ACPI_SUCCESS(status)) {
+                       dbg("Gained control for hotplug HW for pci %s (%s)\n",
+                               pci_name(dev), path_name);
+                       return 0;
+               }
+               if (is_root_bridge(handle))
+                       break;
+               chandle = handle;
+               status = acpi_get_parent(chandle, &handle);
+               if (ACPI_FAILURE(status))
+                       break;
        }
 
-       dbg("Sucess getting control of hotplug hardware\n");
-       return 0;
+       err("Cannot get control of hotplug hardware for pci %s\n",
+                       pci_name(dev));
+       return -1;
 }
 
 void get_hp_params_from_firmware(struct pci_dev *dev,