shpchp: check firmware before taking control
authorKenji Kaneshige <kaneshige.kenji@jp.fujitsu.com>
Wed, 28 May 2008 06:01:03 +0000 (15:01 +0900)
committerJesse Barnes <jbarnes@virtuousgeek.org>
Tue, 10 Jun 2008 17:59:53 +0000 (10:59 -0700)
Fix the following problems of shpchp driver about getting hotplug
control from firmware.

  - The shpchp driver must not control the hotplug controller if it
    fails to get control from the firmware. But current shpchp
    controls the hotplug controller regardless the result, because it
    doesn't check the return value of get_hp_hw_control_from_firmware().

  - Current shpchp driver doesn't support _OSC.

The pciehp driver already have the code for evaluating _OSC and OSHP
and shpchp and pciehp can share it. So this patch move that code from
pciehp to acpi_pcihp.c.

Signed-off-by: Kenji Kaneshige <kaneshige.kenji@jp.fujitsu.com>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
drivers/pci/hotplug/acpi_pcihp.c
drivers/pci/hotplug/pciehp.h
drivers/pci/hotplug/pciehp_hpc.c
drivers/pci/hotplug/shpchp.h
drivers/pci/hotplug/shpchp_core.c
drivers/pci/hotplug/shpchp_hpc.c
include/linux/pci_hotplug.h

index f8c187a763bdcac12323191836079ea9943dde3e..93e37f0666ab5e2786484a775f107bbffd9b3ecd 100644 (file)
@@ -30,6 +30,7 @@
 #include <linux/types.h>
 #include <linux/pci.h>
 #include <linux/pci_hotplug.h>
+#include <linux/pci-acpi.h>
 #include <acpi/acpi.h>
 #include <acpi/acpi_bus.h>
 #include <acpi/actypes.h>
@@ -299,7 +300,7 @@ free_and_return:
  *
  * @handle - the handle of the hotplug controller.
  */
-acpi_status acpi_run_oshp(acpi_handle handle)
+static acpi_status acpi_run_oshp(acpi_handle handle)
 {
        acpi_status             status;
        struct acpi_buffer      string = { ACPI_ALLOCATE_BUFFER, NULL };
@@ -322,9 +323,6 @@ acpi_status acpi_run_oshp(acpi_handle handle)
        kfree(string.pointer);
        return status;
 }
-EXPORT_SYMBOL_GPL(acpi_run_oshp);
-
-
 
 /* acpi_get_hp_params_from_firmware
  *
@@ -374,6 +372,85 @@ acpi_status acpi_get_hp_params_from_firmware(struct pci_bus *bus,
 }
 EXPORT_SYMBOL_GPL(acpi_get_hp_params_from_firmware);
 
+/**
+ * acpi_get_hp_hw_control_from_firmware
+ * @dev: the pci_dev of the bridge that has a hotplug controller
+ * @flags: requested control bits for _OSC
+ *
+ * Attempt to take hotplug control from firmware.
+ */
+int acpi_get_hp_hw_control_from_firmware(struct pci_dev *dev, u32 flags)
+{
+       acpi_status status;
+       acpi_handle chandle, handle = DEVICE_ACPI_HANDLE(&(dev->dev));
+       struct pci_dev *pdev = dev;
+       struct pci_bus *parent;
+       struct acpi_buffer string = { ACPI_ALLOCATE_BUFFER, NULL };
+
+       flags &= (OSC_PCI_EXPRESS_NATIVE_HP_CONTROL |
+                 OSC_SHPC_NATIVE_HP_CONTROL |
+                 OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL);
+       if (!flags) {
+               err("Invalid flags %u specified!\n", flags);
+               return -EINVAL;
+       }
+
+       /*
+        * Per PCI firmware specification, we should run the ACPI _OSC
+        * 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.
+        */
+       while (!handle) {
+               /*
+                * This hotplug controller was not listed in the ACPI name
+                * space at all. Try to get acpi handle of parent pci bus.
+                */
+               if (!pdev || !pdev->bus->parent)
+                       break;
+               parent = pdev->bus->parent;
+               dbg("Could not find %s in acpi namespace, trying parent\n",
+                   pci_name(pdev));
+               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;
+       }
+
+       while (handle) {
+               acpi_get_name(handle, ACPI_FULL_PATHNAME, &string);
+               dbg("Trying to get hotplug control for %s \n",
+                   (char *)string.pointer);
+               status = pci_osc_control_set(handle, flags);
+               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), (char *)string.pointer);
+                       kfree(string.pointer);
+                       return 0;
+               }
+               if (acpi_root_bridge(handle))
+                       break;
+               chandle = handle;
+               status = acpi_get_parent(chandle, &handle);
+               if (ACPI_FAILURE(status))
+                       break;
+       }
+
+       dbg("Cannot get control of hotplug hardware for pci %s\n",
+           pci_name(dev));
+
+       kfree(string.pointer);
+       return -ENODEV;
+}
+EXPORT_SYMBOL(acpi_get_hp_hw_control_from_firmware);
 
 /* acpi_root_bridge - check to see if this acpi object is a root bridge
  *
index 084b73efacb30e5d88280e6c4510e349aff26826..8492fab800ccbc82e2d47d7647aaeb8964baa32e 100644 (file)
@@ -202,9 +202,13 @@ struct hpc_ops {
 #include <acpi/actypes.h>
 #include <linux/pci-acpi.h>
 
-extern int pciehp_acpi_get_hp_hw_control_from_firmware(struct pci_dev *dev);
-#define pciehp_get_hp_hw_control_from_firmware(dev)                    \
-       pciehp_acpi_get_hp_hw_control_from_firmware(dev)
+static inline int pciehp_get_hp_hw_control_from_firmware(struct pci_dev *dev)
+{
+       u32 flags = (OSC_PCI_EXPRESS_NATIVE_HP_CONTROL |
+                    OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL);
+       return acpi_get_hp_hw_control_from_firmware(dev, flags);
+}
+
 static inline int pciehp_get_hp_params_from_firmware(struct pci_dev *dev,
                        struct hotplug_params *hpp)
 {
index 7e3a3d17c334124a8ed81bf66e11bbffc5bf8450..a48021d85f22910477d19f79ca42278ebf955b1d 100644 (file)
@@ -1009,75 +1009,6 @@ static struct hpc_ops pciehp_hpc_ops = {
        .check_lnk_status               = hpc_check_lnk_status,
 };
 
-#ifdef CONFIG_ACPI
-int pciehp_acpi_get_hp_hw_control_from_firmware(struct pci_dev *dev)
-{
-       acpi_status status;
-       acpi_handle chandle, handle = DEVICE_ACPI_HANDLE(&(dev->dev));
-       struct pci_dev *pdev = dev;
-       struct pci_bus *parent;
-       struct acpi_buffer string = { ACPI_ALLOCATE_BUFFER, NULL };
-
-       /*
-        * Per PCI firmware specification, we should run the ACPI _OSC
-        * 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.
-        */
-       while (!handle) {
-               /*
-                * This hotplug controller was not listed in the ACPI name
-                * space at all. Try to get acpi handle of parent pci bus.
-                */
-               if (!pdev || !pdev->bus->parent)
-                       break;
-               parent = pdev->bus->parent;
-               dbg("Could not find %s in acpi namespace, trying parent\n",
-                               pci_name(pdev));
-               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;
-       }
-
-       while (handle) {
-               acpi_get_name(handle, ACPI_FULL_PATHNAME, &string);
-               dbg("Trying to get hotplug control for %s \n",
-                       (char *)string.pointer);
-               status = pci_osc_control_set(handle,
-                               OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL |
-                               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), (char *)string.pointer);
-                       kfree(string.pointer);
-                       return 0;
-               }
-               if (acpi_root_bridge(handle))
-                       break;
-               chandle = handle;
-               status = acpi_get_parent(chandle, &handle);
-               if (ACPI_FAILURE(status))
-                       break;
-       }
-
-       dbg("Cannot get control of hotplug hardware for pci %s\n",
-                       pci_name(dev));
-
-       kfree(string.pointer);
-       return -1;
-}
-#endif
-
 static int pcie_init_hardware_part1(struct controller *ctrl,
                                    struct pcie_device *dev)
 {
index f66e8d6315abecab94d94aaffe2cf7d133a34d34..8a026f750deb65f64831f50a603c32d761e6c5ff 100644 (file)
@@ -170,6 +170,7 @@ extern void shpchp_queue_pushbutton_work(struct work_struct *work);
 extern int shpc_init( struct controller *ctrl, struct pci_dev *pdev);
 
 #ifdef CONFIG_ACPI
+#include <linux/pci-acpi.h>
 static inline int get_hp_params_from_firmware(struct pci_dev *dev,
                                              struct hotplug_params *hpp)
 {
@@ -177,14 +178,15 @@ static inline int get_hp_params_from_firmware(struct pci_dev *dev,
                        return -ENODEV;
        return 0;
 }
-#define get_hp_hw_control_from_firmware(pdev)                          \
-       do {                                                            \
-               if (DEVICE_ACPI_HANDLE(&(pdev->dev)))                   \
-                       acpi_run_oshp(DEVICE_ACPI_HANDLE(&(pdev->dev)));\
-       } while (0)
+
+static inline int get_hp_hw_control_from_firmware(struct pci_dev *dev)
+{
+       u32 flags = OSC_SHPC_NATIVE_HP_CONTROL;
+       return acpi_get_hp_hw_control_from_firmware(dev, flags);
+}
 #else
 #define get_hp_params_from_firmware(dev, hpp) (-ENODEV)
-#define get_hp_hw_control_from_firmware(dev) do { } while (0)
+#define get_hp_hw_control_from_firmware(dev) (0)
 #endif
 
 struct ctrl_reg {
index 0066b0be09281f44fa8318892fb32ed395d01210..df41ecc4e7fcb7788cb7ea870a94b155a599b128 100644 (file)
@@ -330,13 +330,14 @@ static int get_cur_bus_speed (struct hotplug_slot *hotplug_slot, enum pci_bus_sp
 
 static int is_shpc_capable(struct pci_dev *dev)
 {
-       if ((dev->vendor == PCI_VENDOR_ID_AMD) || (dev->device ==
-                               PCI_DEVICE_ID_AMD_GOLAM_7450))
-               return 1;
-       if (pci_find_capability(dev, PCI_CAP_ID_SHPC))
-               return 1;
-
-       return 0;
+       if ((dev->vendor == PCI_VENDOR_ID_AMD) || (dev->device ==
+                                               PCI_DEVICE_ID_AMD_GOLAM_7450))
+               return 1;
+       if (!pci_find_capability(dev, PCI_CAP_ID_SHPC))
+               return 0;
+       if (get_hp_hw_control_from_firmware(dev))
+               return 0;
+       return 1;
 }
 
 static int shpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
index 7d770b2cd889a961f2b9c46226b2e7fe59894fb9..7a0bff364cd4f816cf6475b0c80237f53b73f805 100644 (file)
@@ -1084,7 +1084,6 @@ int shpc_init(struct controller *ctrl, struct pci_dev *pdev)
        dbg("%s: HPC at b:d:f:irq=0x%x:%x:%x:%x\n", __func__,
                        pdev->bus->number, PCI_SLOT(pdev->devfn),
                        PCI_FUNC(pdev->devfn), pdev->irq);
-       get_hp_hw_control_from_firmware(pdev);
 
        /*
         * If this is the first controller to be initialized,
index 8f67e8f2a3cc0c0e24d32ebd77cde24bb94281a5..dbdcd1ad3c6a6fa21d009f8383feb4b0784915d7 100644 (file)
@@ -227,9 +227,9 @@ struct hotplug_params {
 #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_get_hp_hw_control_from_firmware(struct pci_dev *dev, u32 flags);
 int acpi_root_bridge(acpi_handle handle);
 #endif
 #endif