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 | |
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')
-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"); |