diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/pci/hotplug/pciehp_acpi.c | 80 |
1 files changed, 76 insertions, 4 deletions
diff --git a/drivers/pci/hotplug/pciehp_acpi.c b/drivers/pci/hotplug/pciehp_acpi.c index 0cd49b728045..88a5c57f2e5b 100644 --- a/drivers/pci/hotplug/pciehp_acpi.c +++ b/drivers/pci/hotplug/pciehp_acpi.c | |||
@@ -28,15 +28,18 @@ | |||
28 | 28 | ||
29 | #define PCIEHP_DETECT_PCIE (0) | 29 | #define PCIEHP_DETECT_PCIE (0) |
30 | #define PCIEHP_DETECT_ACPI (1) | 30 | #define PCIEHP_DETECT_ACPI (1) |
31 | #define PCIEHP_DETECT_DEFAULT PCIEHP_DETECT_PCIE | 31 | #define PCIEHP_DETECT_AUTO (2) |
32 | #define PCIEHP_DETECT_DEFAULT PCIEHP_DETECT_AUTO | ||
32 | 33 | ||
33 | static int slot_detection_mode; | 34 | static int slot_detection_mode; |
34 | static char *pciehp_detect_mode; | 35 | static char *pciehp_detect_mode; |
35 | module_param(pciehp_detect_mode, charp, 0444); | 36 | module_param(pciehp_detect_mode, charp, 0444); |
36 | MODULE_PARM_DESC(pciehp_detect_mode, | 37 | MODULE_PARM_DESC(pciehp_detect_mode, |
37 | "Slot detection mode: pcie, acpi\n" | 38 | "Slot detection mode: pcie, acpi, auto\n" |
38 | " pcie - Use PCIe based slot detection (default)\n" | 39 | " pcie - Use PCIe based slot detection\n" |
39 | " acpi - Use ACPI for slot detection\n"); | 40 | " acpi - Use ACPI for slot detection\n" |
41 | " auto(default) - Auto select mode. Use acpi option if duplicate\n" | ||
42 | " slot ids are found. Otherwise, use pcie option\n"); | ||
40 | 43 | ||
41 | static int is_ejectable(acpi_handle handle) | 44 | static int is_ejectable(acpi_handle handle) |
42 | { | 45 | { |
@@ -103,12 +106,81 @@ static int __init parse_detect_mode(void) | |||
103 | return PCIEHP_DETECT_PCIE; | 106 | return PCIEHP_DETECT_PCIE; |
104 | if (!strcmp(pciehp_detect_mode, "acpi")) | 107 | if (!strcmp(pciehp_detect_mode, "acpi")) |
105 | return PCIEHP_DETECT_ACPI; | 108 | return PCIEHP_DETECT_ACPI; |
109 | if (!strcmp(pciehp_detect_mode, "auto")) | ||
110 | return PCIEHP_DETECT_AUTO; | ||
106 | warn("bad specifier '%s' for pciehp_detect_mode. Use default\n", | 111 | warn("bad specifier '%s' for pciehp_detect_mode. Use default\n", |
107 | pciehp_detect_mode); | 112 | pciehp_detect_mode); |
108 | return PCIEHP_DETECT_DEFAULT; | 113 | return PCIEHP_DETECT_DEFAULT; |
109 | } | 114 | } |
110 | 115 | ||
116 | static struct pcie_port_service_id __initdata port_pci_ids[] = { | ||
117 | { | ||
118 | .vendor = PCI_ANY_ID, | ||
119 | .device = PCI_ANY_ID, | ||
120 | .port_type = PCIE_ANY_PORT, | ||
121 | .service_type = PCIE_PORT_SERVICE_HP, | ||
122 | .driver_data = 0, | ||
123 | }, { /* end: all zeroes */ } | ||
124 | }; | ||
125 | |||
126 | static int __initdata dup_slot_id; | ||
127 | static int __initdata acpi_slot_detected; | ||
128 | static struct list_head __initdata dummy_slots = LIST_HEAD_INIT(dummy_slots); | ||
129 | |||
130 | /* Dummy driver for dumplicate name detection */ | ||
131 | static int __init dummy_probe(struct pcie_device *dev, | ||
132 | const struct pcie_port_service_id *id) | ||
133 | { | ||
134 | int pos; | ||
135 | u32 slot_cap; | ||
136 | struct slot *slot, *tmp; | ||
137 | struct pci_dev *pdev = dev->port; | ||
138 | if (!(slot = kzalloc(sizeof(*slot), GFP_KERNEL))) | ||
139 | return -ENOMEM; | ||
140 | /* Note: pciehp_detect_mode != PCIEHP_DETECT_ACPI here */ | ||
141 | if (pciehp_get_hp_hw_control_from_firmware(pdev)) | ||
142 | return -ENODEV; | ||
143 | if (!(pos = pci_find_capability(pdev, PCI_CAP_ID_EXP))) | ||
144 | return -ENODEV; | ||
145 | pci_read_config_dword(pdev, pos + PCI_EXP_SLTCAP, &slot_cap); | ||
146 | slot->number = slot_cap >> 19; | ||
147 | list_for_each_entry(tmp, &dummy_slots, slot_list) { | ||
148 | if (tmp->number == slot->number) | ||
149 | dup_slot_id++; | ||
150 | } | ||
151 | list_add_tail(&slot->slot_list, &dummy_slots); | ||
152 | if (!acpi_slot_detected && pciehp_detect_acpi_slot(pdev->subordinate)) | ||
153 | acpi_slot_detected = 1; | ||
154 | return -ENODEV; /* dummy driver always returns error */ | ||
155 | } | ||
156 | |||
157 | static struct pcie_port_service_driver __initdata dummy_driver = { | ||
158 | .name = "pciehp_dummy", | ||
159 | .id_table = port_pci_ids, | ||
160 | .probe = dummy_probe, | ||
161 | }; | ||
162 | |||
163 | static int __init select_detection_mode(void) | ||
164 | { | ||
165 | struct slot *slot, *tmp; | ||
166 | pcie_port_service_register(&dummy_driver); | ||
167 | pcie_port_service_unregister(&dummy_driver); | ||
168 | list_for_each_entry_safe(slot, tmp, &dummy_slots, slot_list) { | ||
169 | list_del(&slot->slot_list); | ||
170 | kfree(slot); | ||
171 | } | ||
172 | if (acpi_slot_detected && dup_slot_id) | ||
173 | return PCIEHP_DETECT_ACPI; | ||
174 | return PCIEHP_DETECT_PCIE; | ||
175 | } | ||
176 | |||
111 | void __init pciehp_acpi_slot_detection_init(void) | 177 | void __init pciehp_acpi_slot_detection_init(void) |
112 | { | 178 | { |
113 | slot_detection_mode = parse_detect_mode(); | 179 | slot_detection_mode = parse_detect_mode(); |
180 | if (slot_detection_mode != PCIEHP_DETECT_AUTO) | ||
181 | goto out; | ||
182 | slot_detection_mode = select_detection_mode(); | ||
183 | out: | ||
184 | if (slot_detection_mode == PCIEHP_DETECT_ACPI) | ||
185 | info("Using ACPI for slot detection.\n"); | ||
114 | } | 186 | } |