aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKenji Kaneshige <kaneshige.kenji@jp.fujitsu.com>2008-05-28 02:01:03 -0400
committerJesse Barnes <jbarnes@virtuousgeek.org>2008-06-10 13:59:53 -0400
commitac9c052d10d8d6f46a30cb46c0d6d753997c299f (patch)
tree15c252f9a0addcc944aa7e453bf68b174dfc38fe
parentd737bdc141f0f040171fffbb7f9e08a825b27aab (diff)
shpchp: check firmware before taking control
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>
-rw-r--r--drivers/pci/hotplug/acpi_pcihp.c85
-rw-r--r--drivers/pci/hotplug/pciehp.h10
-rw-r--r--drivers/pci/hotplug/pciehp_hpc.c69
-rw-r--r--drivers/pci/hotplug/shpchp.h14
-rw-r--r--drivers/pci/hotplug/shpchp_core.c15
-rw-r--r--drivers/pci/hotplug/shpchp_hpc.c1
-rw-r--r--include/linux/pci_hotplug.h2
7 files changed, 105 insertions, 91 deletions
diff --git a/drivers/pci/hotplug/acpi_pcihp.c b/drivers/pci/hotplug/acpi_pcihp.c
index f8c187a763b..93e37f0666a 100644
--- a/drivers/pci/hotplug/acpi_pcihp.c
+++ b/drivers/pci/hotplug/acpi_pcihp.c
@@ -30,6 +30,7 @@
30#include <linux/types.h> 30#include <linux/types.h>
31#include <linux/pci.h> 31#include <linux/pci.h>
32#include <linux/pci_hotplug.h> 32#include <linux/pci_hotplug.h>
33#include <linux/pci-acpi.h>
33#include <acpi/acpi.h> 34#include <acpi/acpi.h>
34#include <acpi/acpi_bus.h> 35#include <acpi/acpi_bus.h>
35#include <acpi/actypes.h> 36#include <acpi/actypes.h>
@@ -299,7 +300,7 @@ free_and_return:
299 * 300 *
300 * @handle - the handle of the hotplug controller. 301 * @handle - the handle of the hotplug controller.
301 */ 302 */
302acpi_status acpi_run_oshp(acpi_handle handle) 303static acpi_status acpi_run_oshp(acpi_handle handle)
303{ 304{
304 acpi_status status; 305 acpi_status status;
305 struct acpi_buffer string = { ACPI_ALLOCATE_BUFFER, NULL }; 306 struct acpi_buffer string = { ACPI_ALLOCATE_BUFFER, NULL };
@@ -322,9 +323,6 @@ acpi_status acpi_run_oshp(acpi_handle handle)
322 kfree(string.pointer); 323 kfree(string.pointer);
323 return status; 324 return status;
324} 325}
325EXPORT_SYMBOL_GPL(acpi_run_oshp);
326
327
328 326
329/* acpi_get_hp_params_from_firmware 327/* acpi_get_hp_params_from_firmware
330 * 328 *
@@ -374,6 +372,85 @@ acpi_status acpi_get_hp_params_from_firmware(struct pci_bus *bus,
374} 372}
375EXPORT_SYMBOL_GPL(acpi_get_hp_params_from_firmware); 373EXPORT_SYMBOL_GPL(acpi_get_hp_params_from_firmware);
376 374
375/**
376 * acpi_get_hp_hw_control_from_firmware
377 * @dev: the pci_dev of the bridge that has a hotplug controller
378 * @flags: requested control bits for _OSC
379 *
380 * Attempt to take hotplug control from firmware.
381 */
382int acpi_get_hp_hw_control_from_firmware(struct pci_dev *dev, u32 flags)
383{
384 acpi_status status;
385 acpi_handle chandle, handle = DEVICE_ACPI_HANDLE(&(dev->dev));
386 struct pci_dev *pdev = dev;
387 struct pci_bus *parent;
388 struct acpi_buffer string = { ACPI_ALLOCATE_BUFFER, NULL };
389
390 flags &= (OSC_PCI_EXPRESS_NATIVE_HP_CONTROL |
391 OSC_SHPC_NATIVE_HP_CONTROL |
392 OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL);
393 if (!flags) {
394 err("Invalid flags %u specified!\n", flags);
395 return -EINVAL;
396 }
397
398 /*
399 * Per PCI firmware specification, we should run the ACPI _OSC
400 * method to get control of hotplug hardware before using it. If
401 * an _OSC is missing, we look for an OSHP to do the same thing.
402 * To handle different BIOS behavior, we look for _OSC and OSHP
403 * within the scope of the hotplug controller and its parents,
404 * upto the host bridge under which this controller exists.
405 */
406 while (!handle) {
407 /*
408 * This hotplug controller was not listed in the ACPI name
409 * space at all. Try to get acpi handle of parent pci bus.
410 */
411 if (!pdev || !pdev->bus->parent)
412 break;
413 parent = pdev->bus->parent;
414 dbg("Could not find %s in acpi namespace, trying parent\n",
415 pci_name(pdev));
416 if (!parent->self)
417 /* Parent must be a host bridge */
418 handle = acpi_get_pci_rootbridge_handle(
419 pci_domain_nr(parent),
420 parent->number);
421 else
422 handle = DEVICE_ACPI_HANDLE(&(parent->self->dev));
423 pdev = parent->self;
424 }
425
426 while (handle) {
427 acpi_get_name(handle, ACPI_FULL_PATHNAME, &string);
428 dbg("Trying to get hotplug control for %s \n",
429 (char *)string.pointer);
430 status = pci_osc_control_set(handle, flags);
431 if (status == AE_NOT_FOUND)
432 status = acpi_run_oshp(handle);
433 if (ACPI_SUCCESS(status)) {
434 dbg("Gained control for hotplug HW for pci %s (%s)\n",
435 pci_name(dev), (char *)string.pointer);
436 kfree(string.pointer);
437 return 0;
438 }
439 if (acpi_root_bridge(handle))
440 break;
441 chandle = handle;
442 status = acpi_get_parent(chandle, &handle);
443 if (ACPI_FAILURE(status))
444 break;
445 }
446
447 dbg("Cannot get control of hotplug hardware for pci %s\n",
448 pci_name(dev));
449
450 kfree(string.pointer);
451 return -ENODEV;
452}
453EXPORT_SYMBOL(acpi_get_hp_hw_control_from_firmware);
377 454
378/* acpi_root_bridge - check to see if this acpi object is a root bridge 455/* acpi_root_bridge - check to see if this acpi object is a root bridge
379 * 456 *
diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h
index 084b73efacb..8492fab800c 100644
--- a/drivers/pci/hotplug/pciehp.h
+++ b/drivers/pci/hotplug/pciehp.h
@@ -202,9 +202,13 @@ struct hpc_ops {
202#include <acpi/actypes.h> 202#include <acpi/actypes.h>
203#include <linux/pci-acpi.h> 203#include <linux/pci-acpi.h>
204 204
205extern int pciehp_acpi_get_hp_hw_control_from_firmware(struct pci_dev *dev); 205static inline int pciehp_get_hp_hw_control_from_firmware(struct pci_dev *dev)
206#define pciehp_get_hp_hw_control_from_firmware(dev) \ 206{
207 pciehp_acpi_get_hp_hw_control_from_firmware(dev) 207 u32 flags = (OSC_PCI_EXPRESS_NATIVE_HP_CONTROL |
208 OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL);
209 return acpi_get_hp_hw_control_from_firmware(dev, flags);
210}
211
208static inline int pciehp_get_hp_params_from_firmware(struct pci_dev *dev, 212static inline int pciehp_get_hp_params_from_firmware(struct pci_dev *dev,
209 struct hotplug_params *hpp) 213 struct hotplug_params *hpp)
210{ 214{
diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c
index 7e3a3d17c33..a48021d85f2 100644
--- a/drivers/pci/hotplug/pciehp_hpc.c
+++ b/drivers/pci/hotplug/pciehp_hpc.c
@@ -1009,75 +1009,6 @@ static struct hpc_ops pciehp_hpc_ops = {
1009 .check_lnk_status = hpc_check_lnk_status, 1009 .check_lnk_status = hpc_check_lnk_status,
1010}; 1010};
1011 1011
1012#ifdef CONFIG_ACPI
1013int pciehp_acpi_get_hp_hw_control_from_firmware(struct pci_dev *dev)
1014{
1015 acpi_status status;
1016 acpi_handle chandle, handle = DEVICE_ACPI_HANDLE(&(dev->dev));
1017 struct pci_dev *pdev = dev;
1018 struct pci_bus *parent;
1019 struct acpi_buffer string = { ACPI_ALLOCATE_BUFFER, NULL };
1020
1021 /*
1022 * Per PCI firmware specification, we should run the ACPI _OSC
1023 * method to get control of hotplug hardware before using it.
1024 * If an _OSC is missing, we look for an OSHP to do the same thing.
1025 * To handle different BIOS behavior, we look for _OSC and OSHP
1026 * within the scope of the hotplug controller and its parents, upto
1027 * the host bridge under which this controller exists.
1028 */
1029 while (!handle) {
1030 /*
1031 * This hotplug controller was not listed in the ACPI name
1032 * space at all. Try to get acpi handle of parent pci bus.
1033 */
1034 if (!pdev || !pdev->bus->parent)
1035 break;
1036 parent = pdev->bus->parent;
1037 dbg("Could not find %s in acpi namespace, trying parent\n",
1038 pci_name(pdev));
1039 if (!parent->self)
1040 /* Parent must be a host bridge */
1041 handle = acpi_get_pci_rootbridge_handle(
1042 pci_domain_nr(parent),
1043 parent->number);
1044 else
1045 handle = DEVICE_ACPI_HANDLE(
1046 &(parent->self->dev));
1047 pdev = parent->self;
1048 }
1049
1050 while (handle) {
1051 acpi_get_name(handle, ACPI_FULL_PATHNAME, &string);
1052 dbg("Trying to get hotplug control for %s \n",
1053 (char *)string.pointer);
1054 status = pci_osc_control_set(handle,
1055 OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL |
1056 OSC_PCI_EXPRESS_NATIVE_HP_CONTROL);
1057 if (status == AE_NOT_FOUND)
1058 status = acpi_run_oshp(handle);
1059 if (ACPI_SUCCESS(status)) {
1060 dbg("Gained control for hotplug HW for pci %s (%s)\n",
1061 pci_name(dev), (char *)string.pointer);
1062 kfree(string.pointer);
1063 return 0;
1064 }
1065 if (acpi_root_bridge(handle))
1066 break;
1067 chandle = handle;
1068 status = acpi_get_parent(chandle, &handle);
1069 if (ACPI_FAILURE(status))
1070 break;
1071 }
1072
1073 dbg("Cannot get control of hotplug hardware for pci %s\n",
1074 pci_name(dev));
1075
1076 kfree(string.pointer);
1077 return -1;
1078}
1079#endif
1080
1081static int pcie_init_hardware_part1(struct controller *ctrl, 1012static int pcie_init_hardware_part1(struct controller *ctrl,
1082 struct pcie_device *dev) 1013 struct pcie_device *dev)
1083{ 1014{
diff --git a/drivers/pci/hotplug/shpchp.h b/drivers/pci/hotplug/shpchp.h
index f66e8d6315a..8a026f750de 100644
--- a/drivers/pci/hotplug/shpchp.h
+++ b/drivers/pci/hotplug/shpchp.h
@@ -170,6 +170,7 @@ extern void shpchp_queue_pushbutton_work(struct work_struct *work);
170extern int shpc_init( struct controller *ctrl, struct pci_dev *pdev); 170extern int shpc_init( struct controller *ctrl, struct pci_dev *pdev);
171 171
172#ifdef CONFIG_ACPI 172#ifdef CONFIG_ACPI
173#include <linux/pci-acpi.h>
173static inline int get_hp_params_from_firmware(struct pci_dev *dev, 174static inline int get_hp_params_from_firmware(struct pci_dev *dev,
174 struct hotplug_params *hpp) 175 struct hotplug_params *hpp)
175{ 176{
@@ -177,14 +178,15 @@ static inline int get_hp_params_from_firmware(struct pci_dev *dev,
177 return -ENODEV; 178 return -ENODEV;
178 return 0; 179 return 0;
179} 180}
180#define get_hp_hw_control_from_firmware(pdev) \ 181
181 do { \ 182static inline int get_hp_hw_control_from_firmware(struct pci_dev *dev)
182 if (DEVICE_ACPI_HANDLE(&(pdev->dev))) \ 183{
183 acpi_run_oshp(DEVICE_ACPI_HANDLE(&(pdev->dev)));\ 184 u32 flags = OSC_SHPC_NATIVE_HP_CONTROL;
184 } while (0) 185 return acpi_get_hp_hw_control_from_firmware(dev, flags);
186}
185#else 187#else
186#define get_hp_params_from_firmware(dev, hpp) (-ENODEV) 188#define get_hp_params_from_firmware(dev, hpp) (-ENODEV)
187#define get_hp_hw_control_from_firmware(dev) do { } while (0) 189#define get_hp_hw_control_from_firmware(dev) (0)
188#endif 190#endif
189 191
190struct ctrl_reg { 192struct ctrl_reg {
diff --git a/drivers/pci/hotplug/shpchp_core.c b/drivers/pci/hotplug/shpchp_core.c
index 0066b0be092..df41ecc4e7f 100644
--- a/drivers/pci/hotplug/shpchp_core.c
+++ b/drivers/pci/hotplug/shpchp_core.c
@@ -330,13 +330,14 @@ static int get_cur_bus_speed (struct hotplug_slot *hotplug_slot, enum pci_bus_sp
330 330
331static int is_shpc_capable(struct pci_dev *dev) 331static int is_shpc_capable(struct pci_dev *dev)
332{ 332{
333 if ((dev->vendor == PCI_VENDOR_ID_AMD) || (dev->device == 333 if ((dev->vendor == PCI_VENDOR_ID_AMD) || (dev->device ==
334 PCI_DEVICE_ID_AMD_GOLAM_7450)) 334 PCI_DEVICE_ID_AMD_GOLAM_7450))
335 return 1; 335 return 1;
336 if (pci_find_capability(dev, PCI_CAP_ID_SHPC)) 336 if (!pci_find_capability(dev, PCI_CAP_ID_SHPC))
337 return 1; 337 return 0;
338 338 if (get_hp_hw_control_from_firmware(dev))
339 return 0; 339 return 0;
340 return 1;
340} 341}
341 342
342static int shpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent) 343static int shpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
diff --git a/drivers/pci/hotplug/shpchp_hpc.c b/drivers/pci/hotplug/shpchp_hpc.c
index 7d770b2cd88..7a0bff364cd 100644
--- a/drivers/pci/hotplug/shpchp_hpc.c
+++ b/drivers/pci/hotplug/shpchp_hpc.c
@@ -1084,7 +1084,6 @@ int shpc_init(struct controller *ctrl, struct pci_dev *pdev)
1084 dbg("%s: HPC at b:d:f:irq=0x%x:%x:%x:%x\n", __func__, 1084 dbg("%s: HPC at b:d:f:irq=0x%x:%x:%x:%x\n", __func__,
1085 pdev->bus->number, PCI_SLOT(pdev->devfn), 1085 pdev->bus->number, PCI_SLOT(pdev->devfn),
1086 PCI_FUNC(pdev->devfn), pdev->irq); 1086 PCI_FUNC(pdev->devfn), pdev->irq);
1087 get_hp_hw_control_from_firmware(pdev);
1088 1087
1089 /* 1088 /*
1090 * If this is the first controller to be initialized, 1089 * If this is the first controller to be initialized,
diff --git a/include/linux/pci_hotplug.h b/include/linux/pci_hotplug.h
index 8f67e8f2a3c..dbdcd1ad3c6 100644
--- a/include/linux/pci_hotplug.h
+++ b/include/linux/pci_hotplug.h
@@ -227,9 +227,9 @@ struct hotplug_params {
227#include <acpi/acpi.h> 227#include <acpi/acpi.h>
228#include <acpi/acpi_bus.h> 228#include <acpi/acpi_bus.h>
229#include <acpi/actypes.h> 229#include <acpi/actypes.h>
230extern acpi_status acpi_run_oshp(acpi_handle handle);
231extern acpi_status acpi_get_hp_params_from_firmware(struct pci_bus *bus, 230extern acpi_status acpi_get_hp_params_from_firmware(struct pci_bus *bus,
232 struct hotplug_params *hpp); 231 struct hotplug_params *hpp);
232int acpi_get_hp_hw_control_from_firmware(struct pci_dev *dev, u32 flags);
233int acpi_root_bridge(acpi_handle handle); 233int acpi_root_bridge(acpi_handle handle);
234#endif 234#endif
235#endif 235#endif