diff options
| author | Kenji Kaneshige <kaneshige.kenji@jp.fujitsu.com> | 2008-12-16 22:07:38 -0500 |
|---|---|---|
| committer | Jesse Barnes <jbarnes@virtuousgeek.org> | 2009-01-07 14:13:10 -0500 |
| commit | c9ffa5a586a97da4d552f89b8f39eea79a63a612 (patch) | |
| tree | 47e4a6de7df1c5d41bff61f8bfc81eb464b8d2c6 /drivers/pci/hotplug | |
| parent | 873392ca514f87eae39f53b6944caf85b1a047cb (diff) | |
PCI: pciehp: add ACPI based slot detection
There is a problem that some non hot-pluggable PCIe slots are detected
as hot-pluggable by pciehp on some platforms. The immediate cause of
this problem is that hot-plug capable bit in the Slot Capabilities
register is set even for non hot-pluggable slots on those platforms.
It seems a BIOS/hardware problem, but we need workaround about that.
Some of those platforms define hot-pluggable PCIe slots on ACPI
namespace properly, while hot-plug capable bit in the Slot
Capabilities register is set improperly. So using ACPI namespace
information in pciehp to detect PCIe hot-pluggable slots would be a
workaround.
This patch adds 'pciehp_detect_mode' module option. When 'acpi' is
specified, pciehp uses ACPI namespace information to detect PCIe
hot-pluggable slots.
Signed-off-by: Kenji Kaneshige <kaneshige.kenji@jp.fujitsu.com>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
Diffstat (limited to 'drivers/pci/hotplug')
| -rw-r--r-- | drivers/pci/hotplug/Makefile | 3 | ||||
| -rw-r--r-- | drivers/pci/hotplug/pciehp.h | 15 | ||||
| -rw-r--r-- | drivers/pci/hotplug/pciehp_acpi.c | 114 | ||||
| -rw-r--r-- | drivers/pci/hotplug/pciehp_core.c | 1 |
4 files changed, 132 insertions, 1 deletions
diff --git a/drivers/pci/hotplug/Makefile b/drivers/pci/hotplug/Makefile index 9bdbe1a6688f..e31fb91652ce 100644 --- a/drivers/pci/hotplug/Makefile +++ b/drivers/pci/hotplug/Makefile | |||
| @@ -55,6 +55,9 @@ pciehp-objs := pciehp_core.o \ | |||
| 55 | pciehp_ctrl.o \ | 55 | pciehp_ctrl.o \ |
| 56 | pciehp_pci.o \ | 56 | pciehp_pci.o \ |
| 57 | pciehp_hpc.o | 57 | pciehp_hpc.o |
| 58 | ifdef CONFIG_ACPI | ||
| 59 | pciehp-objs += pciehp_acpi.o | ||
| 60 | endif | ||
| 58 | 61 | ||
| 59 | shpchp-objs := shpchp_core.o \ | 62 | shpchp-objs := shpchp_core.o \ |
| 60 | shpchp_ctrl.o \ | 63 | shpchp_ctrl.o \ |
diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h index b2801a7ee37f..27fd18f019f8 100644 --- a/drivers/pci/hotplug/pciehp.h +++ b/drivers/pci/hotplug/pciehp.h | |||
| @@ -220,11 +220,23 @@ struct hpc_ops { | |||
| 220 | #include <acpi/actypes.h> | 220 | #include <acpi/actypes.h> |
| 221 | #include <linux/pci-acpi.h> | 221 | #include <linux/pci-acpi.h> |
| 222 | 222 | ||
| 223 | extern void __init pciehp_acpi_slot_detection_init(void); | ||
| 224 | extern int pciehp_acpi_slot_detection_check(struct pci_dev *dev); | ||
| 225 | |||
| 226 | static inline void pciehp_firmware_init(void) | ||
| 227 | { | ||
| 228 | pciehp_acpi_slot_detection_init(); | ||
| 229 | } | ||
| 230 | |||
| 223 | static inline int pciehp_get_hp_hw_control_from_firmware(struct pci_dev *dev) | 231 | static inline int pciehp_get_hp_hw_control_from_firmware(struct pci_dev *dev) |
| 224 | { | 232 | { |
| 233 | int retval; | ||
| 225 | u32 flags = (OSC_PCI_EXPRESS_NATIVE_HP_CONTROL | | 234 | u32 flags = (OSC_PCI_EXPRESS_NATIVE_HP_CONTROL | |
| 226 | OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL); | 235 | OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL); |
| 227 | return acpi_get_hp_hw_control_from_firmware(dev, flags); | 236 | retval = acpi_get_hp_hw_control_from_firmware(dev, flags); |
| 237 | if (retval) | ||
| 238 | return retval; | ||
| 239 | return pciehp_acpi_slot_detection_check(dev); | ||
| 228 | } | 240 | } |
| 229 | 241 | ||
| 230 | static inline int pciehp_get_hp_params_from_firmware(struct pci_dev *dev, | 242 | static inline int pciehp_get_hp_params_from_firmware(struct pci_dev *dev, |
| @@ -235,6 +247,7 @@ static inline int pciehp_get_hp_params_from_firmware(struct pci_dev *dev, | |||
| 235 | return 0; | 247 | return 0; |
| 236 | } | 248 | } |
| 237 | #else | 249 | #else |
| 250 | #define pciehp_firmware_init() do {} while (0) | ||
| 238 | #define pciehp_get_hp_hw_control_from_firmware(dev) 0 | 251 | #define pciehp_get_hp_hw_control_from_firmware(dev) 0 |
| 239 | #define pciehp_get_hp_params_from_firmware(dev, hpp) (-ENODEV) | 252 | #define pciehp_get_hp_params_from_firmware(dev, hpp) (-ENODEV) |
| 240 | #endif /* CONFIG_ACPI */ | 253 | #endif /* CONFIG_ACPI */ |
diff --git a/drivers/pci/hotplug/pciehp_acpi.c b/drivers/pci/hotplug/pciehp_acpi.c new file mode 100644 index 000000000000..0cd49b728045 --- /dev/null +++ b/drivers/pci/hotplug/pciehp_acpi.c | |||
| @@ -0,0 +1,114 @@ | |||
| 1 | /* | ||
| 2 | * ACPI related functions for PCI Express Hot Plug driver. | ||
| 3 | * | ||
| 4 | * Copyright (C) 2008 Kenji Kaneshige | ||
| 5 | * Copyright (C) 2008 Fujitsu Limited. | ||
| 6 | * | ||
| 7 | * All rights reserved. | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or modify | ||
| 10 | * it under the terms of the GNU General Public License as published by | ||
| 11 | * the Free Software Foundation; either version 2 of the License, or (at | ||
| 12 | * your option) any later version. | ||
| 13 | * | ||
| 14 | * This program is distributed in the hope that it will be useful, but | ||
| 15 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 16 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
| 17 | * NON INFRINGEMENT. See the GNU General Public License for more | ||
| 18 | * details. | ||
| 19 | * | ||
| 20 | * You should have received a copy of the GNU General Public License | ||
| 21 | * along with this program; if not, write to the Free Software | ||
| 22 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
| 23 | * | ||
| 24 | */ | ||
| 25 | |||
| 26 | #include <linux/acpi.h> | ||
| 27 | #include "pciehp.h" | ||
| 28 | |||
| 29 | #define PCIEHP_DETECT_PCIE (0) | ||
| 30 | #define PCIEHP_DETECT_ACPI (1) | ||
| 31 | #define PCIEHP_DETECT_DEFAULT PCIEHP_DETECT_PCIE | ||
| 32 | |||
| 33 | static int slot_detection_mode; | ||
| 34 | static char *pciehp_detect_mode; | ||
| 35 | module_param(pciehp_detect_mode, charp, 0444); | ||
| 36 | MODULE_PARM_DESC(pciehp_detect_mode, | ||
| 37 | "Slot detection mode: pcie, acpi\n" | ||
| 38 | " pcie - Use PCIe based slot detection (default)\n" | ||
| 39 | " acpi - Use ACPI for slot detection\n"); | ||
| 40 | |||
| 41 | static int is_ejectable(acpi_handle handle) | ||
| 42 | { | ||
| 43 | acpi_status status; | ||
| 44 | acpi_handle tmp; | ||
| 45 | unsigned long long removable; | ||
| 46 | status = acpi_get_handle(handle, "_ADR", &tmp); | ||
| 47 | if (ACPI_FAILURE(status)) | ||
| 48 | return 0; | ||
| 49 | status = acpi_get_handle(handle, "_EJ0", &tmp); | ||
| 50 | if (ACPI_SUCCESS(status)) | ||
| 51 | return 1; | ||
| 52 | status = acpi_evaluate_integer(handle, "_RMV", NULL, &removable); | ||
| 53 | if (ACPI_SUCCESS(status) && removable) | ||
| 54 | return 1; | ||
| 55 | return 0; | ||
| 56 | } | ||
| 57 | |||
| 58 | static acpi_status | ||
| 59 | check_hotplug(acpi_handle handle, u32 lvl, void *context, void **rv) | ||
| 60 | { | ||
| 61 | int *found = (int *)context; | ||
| 62 | if (is_ejectable(handle)) { | ||
| 63 | *found = 1; | ||
| 64 | return AE_CTRL_TERMINATE; | ||
| 65 | } | ||
| 66 | return AE_OK; | ||
| 67 | } | ||
| 68 | |||
| 69 | static int pciehp_detect_acpi_slot(struct pci_bus *pbus) | ||
| 70 | { | ||
| 71 | acpi_handle handle; | ||
| 72 | struct pci_dev *pdev = pbus->self; | ||
| 73 | int found = 0; | ||
| 74 | |||
| 75 | if (!pdev){ | ||
| 76 | int seg = pci_domain_nr(pbus), busnr = pbus->number; | ||
| 77 | handle = acpi_get_pci_rootbridge_handle(seg, busnr); | ||
| 78 | } else | ||
| 79 | handle = DEVICE_ACPI_HANDLE(&(pdev->dev)); | ||
| 80 | |||
| 81 | if (!handle) | ||
| 82 | return 0; | ||
| 83 | |||
| 84 | acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1, | ||
| 85 | check_hotplug, (void *)&found, NULL); | ||
| 86 | return found; | ||
| 87 | } | ||
| 88 | |||
| 89 | int pciehp_acpi_slot_detection_check(struct pci_dev *dev) | ||
| 90 | { | ||
| 91 | if (slot_detection_mode != PCIEHP_DETECT_ACPI) | ||
| 92 | return 0; | ||
| 93 | if (pciehp_detect_acpi_slot(dev->subordinate)) | ||
| 94 | return 0; | ||
| 95 | return -ENODEV; | ||
| 96 | } | ||
| 97 | |||
| 98 | static int __init parse_detect_mode(void) | ||
| 99 | { | ||
| 100 | if (!pciehp_detect_mode) | ||
| 101 | return PCIEHP_DETECT_DEFAULT; | ||
| 102 | if (!strcmp(pciehp_detect_mode, "pcie")) | ||
| 103 | return PCIEHP_DETECT_PCIE; | ||
| 104 | if (!strcmp(pciehp_detect_mode, "acpi")) | ||
| 105 | return PCIEHP_DETECT_ACPI; | ||
| 106 | warn("bad specifier '%s' for pciehp_detect_mode. Use default\n", | ||
| 107 | pciehp_detect_mode); | ||
| 108 | return PCIEHP_DETECT_DEFAULT; | ||
| 109 | } | ||
| 110 | |||
| 111 | void __init pciehp_acpi_slot_detection_init(void) | ||
| 112 | { | ||
| 113 | slot_detection_mode = parse_detect_mode(); | ||
| 114 | } | ||
diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c index 39cf248d24e3..5482d4ed8256 100644 --- a/drivers/pci/hotplug/pciehp_core.c +++ b/drivers/pci/hotplug/pciehp_core.c | |||
| @@ -522,6 +522,7 @@ static int __init pcied_init(void) | |||
| 522 | { | 522 | { |
| 523 | int retval = 0; | 523 | int retval = 0; |
| 524 | 524 | ||
| 525 | pciehp_firmware_init(); | ||
| 525 | retval = pcie_port_service_register(&hpdriver_portdrv); | 526 | retval = pcie_port_service_register(&hpdriver_portdrv); |
| 526 | dbg("pcie_port_service_register = %d\n", retval); | 527 | dbg("pcie_port_service_register = %d\n", retval); |
| 527 | info(DRIVER_DESC " version: " DRIVER_VERSION "\n"); | 528 | info(DRIVER_DESC " version: " DRIVER_VERSION "\n"); |
