diff options
Diffstat (limited to 'drivers/pci/hotplug/acpiphp_glue.c')
-rw-r--r-- | drivers/pci/hotplug/acpiphp_glue.c | 111 |
1 files changed, 84 insertions, 27 deletions
diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index d370f999782e..faf7eed5d963 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c | |||
@@ -116,6 +116,59 @@ is_ejectable_slot(acpi_handle handle, u32 lvl, void *context, void **rv) | |||
116 | } | 116 | } |
117 | } | 117 | } |
118 | 118 | ||
119 | /* callback routine to check for the existance of a pci dock device */ | ||
120 | static acpi_status | ||
121 | is_pci_dock_device(acpi_handle handle, u32 lvl, void *context, void **rv) | ||
122 | { | ||
123 | int *count = (int *)context; | ||
124 | |||
125 | if (is_dock_device(handle)) { | ||
126 | (*count)++; | ||
127 | return AE_CTRL_TERMINATE; | ||
128 | } else { | ||
129 | return AE_OK; | ||
130 | } | ||
131 | } | ||
132 | |||
133 | |||
134 | |||
135 | |||
136 | /* | ||
137 | * the _DCK method can do funny things... and sometimes not | ||
138 | * hah-hah funny. | ||
139 | * | ||
140 | * TBD - figure out a way to only call fixups for | ||
141 | * systems that require them. | ||
142 | */ | ||
143 | static int post_dock_fixups(struct notifier_block *nb, unsigned long val, | ||
144 | void *v) | ||
145 | { | ||
146 | struct acpiphp_func *func = container_of(nb, struct acpiphp_func, nb); | ||
147 | struct pci_bus *bus = func->slot->bridge->pci_bus; | ||
148 | u32 buses; | ||
149 | |||
150 | if (!bus->self) | ||
151 | return NOTIFY_OK; | ||
152 | |||
153 | /* fixup bad _DCK function that rewrites | ||
154 | * secondary bridge on slot | ||
155 | */ | ||
156 | pci_read_config_dword(bus->self, | ||
157 | PCI_PRIMARY_BUS, | ||
158 | &buses); | ||
159 | |||
160 | if (((buses >> 8) & 0xff) != bus->secondary) { | ||
161 | buses = (buses & 0xff000000) | ||
162 | | ((unsigned int)(bus->primary) << 0) | ||
163 | | ((unsigned int)(bus->secondary) << 8) | ||
164 | | ((unsigned int)(bus->subordinate) << 16); | ||
165 | pci_write_config_dword(bus->self, PCI_PRIMARY_BUS, buses); | ||
166 | } | ||
167 | return NOTIFY_OK; | ||
168 | } | ||
169 | |||
170 | |||
171 | |||
119 | 172 | ||
120 | /* callback routine to register each ACPI PCI slot object */ | 173 | /* callback routine to register each ACPI PCI slot object */ |
121 | static acpi_status | 174 | static acpi_status |
@@ -124,7 +177,6 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) | |||
124 | struct acpiphp_bridge *bridge = (struct acpiphp_bridge *)context; | 177 | struct acpiphp_bridge *bridge = (struct acpiphp_bridge *)context; |
125 | struct acpiphp_slot *slot; | 178 | struct acpiphp_slot *slot; |
126 | struct acpiphp_func *newfunc; | 179 | struct acpiphp_func *newfunc; |
127 | struct dependent_device *dd; | ||
128 | acpi_handle tmp; | 180 | acpi_handle tmp; |
129 | acpi_status status = AE_OK; | 181 | acpi_status status = AE_OK; |
130 | unsigned long adr, sun; | 182 | unsigned long adr, sun; |
@@ -137,7 +189,7 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) | |||
137 | 189 | ||
138 | status = acpi_get_handle(handle, "_EJ0", &tmp); | 190 | status = acpi_get_handle(handle, "_EJ0", &tmp); |
139 | 191 | ||
140 | if (ACPI_FAILURE(status) && !(is_dependent_device(handle))) | 192 | if (ACPI_FAILURE(status) && !(is_dock_device(handle))) |
141 | return AE_OK; | 193 | return AE_OK; |
142 | 194 | ||
143 | device = (adr >> 16) & 0xffff; | 195 | device = (adr >> 16) & 0xffff; |
@@ -162,18 +214,8 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) | |||
162 | if (ACPI_SUCCESS(acpi_get_handle(handle, "_PS3", &tmp))) | 214 | if (ACPI_SUCCESS(acpi_get_handle(handle, "_PS3", &tmp))) |
163 | newfunc->flags |= FUNC_HAS_PS3; | 215 | newfunc->flags |= FUNC_HAS_PS3; |
164 | 216 | ||
165 | if (ACPI_SUCCESS(acpi_get_handle(handle, "_DCK", &tmp))) { | 217 | if (ACPI_SUCCESS(acpi_get_handle(handle, "_DCK", &tmp))) |
166 | newfunc->flags |= FUNC_HAS_DCK; | 218 | newfunc->flags |= FUNC_HAS_DCK; |
167 | /* add to devices dependent on dock station, | ||
168 | * because this may actually be the dock bridge | ||
169 | */ | ||
170 | dd = alloc_dependent_device(handle); | ||
171 | if (!dd) | ||
172 | err("Can't allocate memory for " | ||
173 | "new dependent device!\n"); | ||
174 | else | ||
175 | add_dependent_device(dd); | ||
176 | } | ||
177 | 219 | ||
178 | status = acpi_evaluate_integer(handle, "_SUN", NULL, &sun); | 220 | status = acpi_evaluate_integer(handle, "_SUN", NULL, &sun); |
179 | if (ACPI_FAILURE(status)) | 221 | if (ACPI_FAILURE(status)) |
@@ -225,20 +267,23 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) | |||
225 | slot->flags |= (SLOT_ENABLED | SLOT_POWEREDON); | 267 | slot->flags |= (SLOT_ENABLED | SLOT_POWEREDON); |
226 | } | 268 | } |
227 | 269 | ||
228 | /* if this is a device dependent on a dock station, | 270 | if (is_dock_device(handle)) { |
229 | * associate the acpiphp_func to the dependent_device | 271 | /* we don't want to call this device's _EJ0 |
230 | * struct. | 272 | * because we want the dock notify handler |
231 | */ | 273 | * to call it after it calls _DCK |
232 | if ((dd = get_dependent_device(handle))) { | ||
233 | newfunc->flags |= FUNC_IS_DD; | ||
234 | /* | ||
235 | * we don't want any devices which is dependent | ||
236 | * on the dock to have it's _EJ0 method executed. | ||
237 | * because we need to run _DCK first. | ||
238 | */ | 274 | */ |
239 | newfunc->flags &= ~FUNC_HAS_EJ0; | 275 | newfunc->flags &= ~FUNC_HAS_EJ0; |
240 | dd->func = newfunc; | 276 | if (register_hotplug_dock_device(handle, |
241 | add_pci_dependent_device(dd); | 277 | handle_hotplug_event_func, newfunc)) |
278 | dbg("failed to register dock device\n"); | ||
279 | |||
280 | /* we need to be notified when dock events happen | ||
281 | * outside of the hotplug operation, since we may | ||
282 | * need to do fixups before we can hotplug. | ||
283 | */ | ||
284 | newfunc->nb.notifier_call = post_dock_fixups; | ||
285 | if (register_dock_notifier(&newfunc->nb)) | ||
286 | dbg("failed to register a dock notifier"); | ||
242 | } | 287 | } |
243 | 288 | ||
244 | /* install notify handler */ | 289 | /* install notify handler */ |
@@ -277,6 +322,15 @@ static int detect_ejectable_slots(acpi_handle *bridge_handle) | |||
277 | status = acpi_walk_namespace(ACPI_TYPE_DEVICE, bridge_handle, (u32)1, | 322 | status = acpi_walk_namespace(ACPI_TYPE_DEVICE, bridge_handle, (u32)1, |
278 | is_ejectable_slot, (void *)&count, NULL); | 323 | is_ejectable_slot, (void *)&count, NULL); |
279 | 324 | ||
325 | /* | ||
326 | * we also need to add this bridge if there is a dock bridge or | ||
327 | * other pci device on a dock station (removable) | ||
328 | */ | ||
329 | if (!count) | ||
330 | status = acpi_walk_namespace(ACPI_TYPE_DEVICE, bridge_handle, | ||
331 | (u32)1, is_pci_dock_device, (void *)&count, | ||
332 | NULL); | ||
333 | |||
280 | return count; | 334 | return count; |
281 | } | 335 | } |
282 | 336 | ||
@@ -487,8 +541,7 @@ find_p2p_bridge(acpi_handle handle, u32 lvl, void *context, void **rv) | |||
487 | goto out; | 541 | goto out; |
488 | 542 | ||
489 | /* check if this bridge has ejectable slots */ | 543 | /* check if this bridge has ejectable slots */ |
490 | if ((detect_ejectable_slots(handle) > 0) || | 544 | if ((detect_ejectable_slots(handle) > 0)) { |
491 | (detect_dependent_devices(handle) > 0)) { | ||
492 | dbg("found PCI-to-PCI bridge at PCI %s\n", pci_name(dev)); | 545 | dbg("found PCI-to-PCI bridge at PCI %s\n", pci_name(dev)); |
493 | add_p2p_bridge(handle, dev); | 546 | add_p2p_bridge(handle, dev); |
494 | } | 547 | } |
@@ -605,6 +658,10 @@ static void cleanup_bridge(struct acpiphp_bridge *bridge) | |||
605 | list_for_each_safe (list, tmp, &slot->funcs) { | 658 | list_for_each_safe (list, tmp, &slot->funcs) { |
606 | struct acpiphp_func *func; | 659 | struct acpiphp_func *func; |
607 | func = list_entry(list, struct acpiphp_func, sibling); | 660 | func = list_entry(list, struct acpiphp_func, sibling); |
661 | if (is_dock_device(func->handle)) { | ||
662 | unregister_hotplug_dock_device(func->handle); | ||
663 | unregister_dock_notifier(&func->nb); | ||
664 | } | ||
608 | if (!(func->flags & FUNC_HAS_DCK)) { | 665 | if (!(func->flags & FUNC_HAS_DCK)) { |
609 | status = acpi_remove_notify_handler(func->handle, | 666 | status = acpi_remove_notify_handler(func->handle, |
610 | ACPI_SYSTEM_NOTIFY, | 667 | ACPI_SYSTEM_NOTIFY, |