Commit | Line | Data |
---|---|---|
736759ef | 1 | // SPDX-License-Identifier: GPL-2.0+ |
1da177e4 LT |
2 | /* |
3 | * ACPI PCI HotPlug glue functions to ACPI CA subsystem | |
4 | * | |
5 | * Copyright (C) 2002,2003 Takayoshi Kochi (t-kochi@bq.jp.nec.com) | |
6 | * Copyright (C) 2002 Hiroshi Aono (h-aono@ap.jp.nec.com) | |
7 | * Copyright (C) 2002,2003 NEC Corporation | |
3d0186bb | 8 | * Copyright (C) 2003-2005 Matthew Wilcox (willy@infradead.org) |
42f49a6a | 9 | * Copyright (C) 2003-2005 Hewlett Packard |
8e7561cf RS |
10 | * Copyright (C) 2005 Rajesh Shah (rajesh.shah@intel.com) |
11 | * Copyright (C) 2005 Intel Corporation | |
1da177e4 LT |
12 | * |
13 | * All rights reserved. | |
14 | * | |
998be20f | 15 | * Send feedback to <kristen.c.accardi@intel.com> |
1da177e4 LT |
16 | * |
17 | */ | |
18 | ||
42f49a6a RS |
19 | /* |
20 | * Lifetime rules for pci_dev: | |
42f49a6a RS |
21 | * - The one in acpiphp_bridge has its refcount elevated by pci_get_slot() |
22 | * when the bridge is scanned and it loses a refcount when the bridge | |
23 | * is removed. | |
5d4a4b25 | 24 | * - When a P2P bridge is present, we elevate the refcount on the subordinate |
b2105b9f | 25 | * bus. It loses the refcount when the driver unloads. |
42f49a6a RS |
26 | */ |
27 | ||
bd950799 LT |
28 | #define pr_fmt(fmt) "acpiphp_glue: " fmt |
29 | ||
1da177e4 LT |
30 | #include <linux/module.h> |
31 | ||
32 | #include <linux/kernel.h> | |
33 | #include <linux/pci.h> | |
7a54f25c | 34 | #include <linux/pci_hotplug.h> |
e8c331e9 | 35 | #include <linux/pci-acpi.h> |
4ebe3450 | 36 | #include <linux/pm_runtime.h> |
6aa4cdd0 | 37 | #include <linux/mutex.h> |
5a0e3ad6 | 38 | #include <linux/slab.h> |
6af8bef1 | 39 | #include <linux/acpi.h> |
1da177e4 LT |
40 | |
41 | #include "../pci.h" | |
1da177e4 LT |
42 | #include "acpiphp.h" |
43 | ||
44 | static LIST_HEAD(bridge_list); | |
3d54a316 | 45 | static DEFINE_MUTEX(bridge_mutex); |
1da177e4 | 46 | |
be27b3dc | 47 | static int acpiphp_hotplug_notify(struct acpi_device *adev, u32 type); |
edf5bf34 | 48 | static void acpiphp_post_dock_fixup(struct acpi_device *adev); |
8e5dce35 | 49 | static void acpiphp_sanitize_bus(struct pci_bus *bus); |
d3a1ebb0 | 50 | static void hotplug_event(u32 type, struct acpiphp_context *context); |
3d54a316 | 51 | static void free_bridge(struct kref *kref); |
8e5dce35 | 52 | |
cb7b8ced RW |
53 | /** |
54 | * acpiphp_init_context - Create hotplug context and grab a reference to it. | |
bbcbfc0e | 55 | * @adev: ACPI device object to create the context for. |
cb7b8ced | 56 | * |
e525506f | 57 | * Call under acpi_hp_context_lock. |
cb7b8ced | 58 | */ |
bbcbfc0e | 59 | static struct acpiphp_context *acpiphp_init_context(struct acpi_device *adev) |
cb7b8ced RW |
60 | { |
61 | struct acpiphp_context *context; | |
cb7b8ced RW |
62 | |
63 | context = kzalloc(sizeof(*context), GFP_KERNEL); | |
64 | if (!context) | |
65 | return NULL; | |
66 | ||
cb7b8ced | 67 | context->refcount = 1; |
ba574dc8 RW |
68 | context->hp.notify = acpiphp_hotplug_notify; |
69 | context->hp.fixup = acpiphp_post_dock_fixup; | |
70 | acpi_set_hp_context(adev, &context->hp); | |
cb7b8ced RW |
71 | return context; |
72 | } | |
73 | ||
74 | /** | |
75 | * acpiphp_get_context - Get hotplug context and grab a reference to it. | |
3c2cc7ff | 76 | * @adev: ACPI device object to get the context for. |
cb7b8ced | 77 | * |
e525506f | 78 | * Call under acpi_hp_context_lock. |
cb7b8ced | 79 | */ |
3c2cc7ff | 80 | static struct acpiphp_context *acpiphp_get_context(struct acpi_device *adev) |
cb7b8ced | 81 | { |
3c2cc7ff | 82 | struct acpiphp_context *context; |
cb7b8ced | 83 | |
3c2cc7ff RW |
84 | if (!adev->hp) |
85 | return NULL; | |
86 | ||
87 | context = to_acpiphp_context(adev->hp); | |
88 | context->refcount++; | |
cb7b8ced RW |
89 | return context; |
90 | } | |
91 | ||
92 | /** | |
93 | * acpiphp_put_context - Drop a reference to ACPI hotplug context. | |
bbcbfc0e | 94 | * @context: ACPI hotplug context to drop a reference to. |
cb7b8ced RW |
95 | * |
96 | * The context object is removed if there are no more references to it. | |
97 | * | |
e525506f | 98 | * Call under acpi_hp_context_lock. |
cb7b8ced RW |
99 | */ |
100 | static void acpiphp_put_context(struct acpiphp_context *context) | |
101 | { | |
102 | if (--context->refcount) | |
103 | return; | |
104 | ||
bd4674df | 105 | WARN_ON(context->bridge); |
3c2cc7ff | 106 | context->hp.self->hp = NULL; |
cb7b8ced RW |
107 | kfree(context); |
108 | } | |
109 | ||
3d54a316 JL |
110 | static inline void get_bridge(struct acpiphp_bridge *bridge) |
111 | { | |
112 | kref_get(&bridge->ref); | |
113 | } | |
114 | ||
115 | static inline void put_bridge(struct acpiphp_bridge *bridge) | |
116 | { | |
117 | kref_put(&bridge->ref, free_bridge); | |
118 | } | |
119 | ||
edf5bf34 RW |
120 | static struct acpiphp_context *acpiphp_grab_context(struct acpi_device *adev) |
121 | { | |
122 | struct acpiphp_context *context; | |
123 | ||
124 | acpi_lock_hp_context(); | |
dae68d7f | 125 | |
edf5bf34 | 126 | context = acpiphp_get_context(adev); |
dae68d7f RW |
127 | if (!context) |
128 | goto unlock; | |
129 | ||
130 | if (context->func.parent->is_going_away) { | |
131 | acpiphp_put_context(context); | |
132 | context = NULL; | |
133 | goto unlock; | |
edf5bf34 | 134 | } |
dae68d7f | 135 | |
edf5bf34 RW |
136 | get_bridge(context->func.parent); |
137 | acpiphp_put_context(context); | |
dae68d7f RW |
138 | |
139 | unlock: | |
edf5bf34 RW |
140 | acpi_unlock_hp_context(); |
141 | return context; | |
142 | } | |
143 | ||
144 | static void acpiphp_let_context_go(struct acpiphp_context *context) | |
145 | { | |
146 | put_bridge(context->func.parent); | |
147 | } | |
148 | ||
3d54a316 JL |
149 | static void free_bridge(struct kref *kref) |
150 | { | |
cb7b8ced | 151 | struct acpiphp_context *context; |
3d54a316 JL |
152 | struct acpiphp_bridge *bridge; |
153 | struct acpiphp_slot *slot, *next; | |
154 | struct acpiphp_func *func, *tmp; | |
155 | ||
e525506f | 156 | acpi_lock_hp_context(); |
cb7b8ced | 157 | |
3d54a316 JL |
158 | bridge = container_of(kref, struct acpiphp_bridge, ref); |
159 | ||
160 | list_for_each_entry_safe(slot, next, &bridge->slots, node) { | |
bd4674df RW |
161 | list_for_each_entry_safe(func, tmp, &slot->funcs, sibling) |
162 | acpiphp_put_context(func_to_context(func)); | |
163 | ||
3d54a316 JL |
164 | kfree(slot); |
165 | } | |
166 | ||
87831273 | 167 | context = bridge->context; |
bbd34fcd RW |
168 | /* Root bridges will not have hotplug context. */ |
169 | if (context) { | |
170 | /* Release the reference taken by acpiphp_enumerate_slots(). */ | |
bda46dbb | 171 | put_bridge(context->func.parent); |
bbd34fcd RW |
172 | context->bridge = NULL; |
173 | acpiphp_put_context(context); | |
174 | } | |
cb7b8ced | 175 | |
3d54a316 JL |
176 | put_device(&bridge->pci_bus->dev); |
177 | pci_dev_put(bridge->pci_dev); | |
178 | kfree(bridge); | |
cb7b8ced | 179 | |
e525506f | 180 | acpi_unlock_hp_context(); |
3d54a316 JL |
181 | } |
182 | ||
edf5bf34 RW |
183 | /** |
184 | * acpiphp_post_dock_fixup - Post-dock fixups for PCI devices. | |
185 | * @adev: ACPI device object corresponding to a PCI device. | |
4e8662bb | 186 | * |
edf5bf34 | 187 | * TBD - figure out a way to only call fixups for systems that require them. |
4e8662bb | 188 | */ |
edf5bf34 | 189 | static void acpiphp_post_dock_fixup(struct acpi_device *adev) |
4e8662bb | 190 | { |
edf5bf34 RW |
191 | struct acpiphp_context *context = acpiphp_grab_context(adev); |
192 | struct pci_bus *bus; | |
4e8662bb KA |
193 | u32 buses; |
194 | ||
edf5bf34 | 195 | if (!context) |
f09ce741 | 196 | return; |
4e8662bb | 197 | |
edf5bf34 RW |
198 | bus = context->func.slot->bus; |
199 | if (!bus->self) | |
200 | goto out; | |
201 | ||
4e8662bb KA |
202 | /* fixup bad _DCK function that rewrites |
203 | * secondary bridge on slot | |
204 | */ | |
edf5bf34 | 205 | pci_read_config_dword(bus->self, PCI_PRIMARY_BUS, &buses); |
4e8662bb | 206 | |
b918c62e | 207 | if (((buses >> 8) & 0xff) != bus->busn_res.start) { |
4e8662bb | 208 | buses = (buses & 0xff000000) |
2a9d3521 | 209 | | ((unsigned int)(bus->primary) << 0) |
b918c62e YL |
210 | | ((unsigned int)(bus->busn_res.start) << 8) |
211 | | ((unsigned int)(bus->busn_res.end) << 16); | |
4e8662bb KA |
212 | pci_write_config_dword(bus->self, PCI_PRIMARY_BUS, buses); |
213 | } | |
4e8662bb | 214 | |
edf5bf34 RW |
215 | out: |
216 | acpiphp_let_context_go(context); | |
af9d8adc | 217 | } |
4e8662bb | 218 | |
3799c5a0 RW |
219 | /** |
220 | * acpiphp_add_context - Add ACPIPHP context to an ACPI device object. | |
221 | * @handle: ACPI handle of the object to add a context to. | |
222 | * @lvl: Not used. | |
223 | * @data: The object's parent ACPIPHP bridge. | |
224 | * @rv: Not used. | |
225 | */ | |
226 | static acpi_status acpiphp_add_context(acpi_handle handle, u32 lvl, void *data, | |
227 | void **rv) | |
1da177e4 | 228 | { |
9a607a54 | 229 | struct acpi_device *adev = acpi_fetch_acpi_dev(handle); |
cb7b8ced RW |
230 | struct acpiphp_bridge *bridge = data; |
231 | struct acpiphp_context *context; | |
1da177e4 LT |
232 | struct acpiphp_slot *slot; |
233 | struct acpiphp_func *newfunc; | |
1da177e4 | 234 | acpi_status status = AE_OK; |
bbd34fcd RW |
235 | unsigned long long adr; |
236 | int device, function; | |
e8c331e9 | 237 | struct pci_bus *pbus = bridge->pci_bus; |
bbd34fcd | 238 | struct pci_dev *pdev = bridge->pci_dev; |
3b63aaa7 | 239 | u32 val; |
1da177e4 | 240 | |
9a607a54 RW |
241 | if (!adev) |
242 | return AE_OK; | |
243 | ||
dfb117b3 BH |
244 | status = acpi_evaluate_integer(handle, "_ADR", NULL, &adr); |
245 | if (ACPI_FAILURE(status)) { | |
f26ca1d6 TK |
246 | if (status != AE_NOT_FOUND) |
247 | acpi_handle_warn(handle, | |
248 | "can't evaluate _ADR (%#x)\n", status); | |
dfb117b3 BH |
249 | return AE_OK; |
250 | } | |
251 | ||
252 | device = (adr >> 16) & 0xffff; | |
253 | function = adr & 0xffff; | |
254 | ||
e525506f | 255 | acpi_lock_hp_context(); |
bbcbfc0e | 256 | context = acpiphp_init_context(adev); |
cb7b8ced | 257 | if (!context) { |
e525506f | 258 | acpi_unlock_hp_context(); |
cb7b8ced | 259 | acpi_handle_err(handle, "No hotplug context\n"); |
cb7b8ced RW |
260 | return AE_NOT_EXIST; |
261 | } | |
bd4674df | 262 | newfunc = &context->func; |
bd4674df | 263 | newfunc->function = function; |
bda46dbb | 264 | newfunc->parent = bridge; |
edf5bf34 | 265 | acpi_unlock_hp_context(); |
cb7b8ced | 266 | |
edf5bf34 RW |
267 | /* |
268 | * If this is a dock device, its _EJ0 should be executed by the dock | |
269 | * notify handler after calling _DCK. | |
270 | */ | |
271 | if (!is_dock_device(adev) && acpi_has_method(handle, "_EJ0")) | |
20416ea5 | 272 | newfunc->flags = FUNC_HAS_EJ0; |
1da177e4 | 273 | |
ecd046da | 274 | if (acpi_has_method(handle, "_STA")) |
1da177e4 LT |
275 | newfunc->flags |= FUNC_HAS_STA; |
276 | ||
1da177e4 | 277 | /* search for objects that share the same slot */ |
ad41dd9d | 278 | list_for_each_entry(slot, &bridge->slots, node) |
bbd34fcd | 279 | if (slot->device == device) |
ac372338 | 280 | goto slot_found; |
1da177e4 | 281 | |
ac372338 RW |
282 | slot = kzalloc(sizeof(struct acpiphp_slot), GFP_KERNEL); |
283 | if (!slot) { | |
e525506f | 284 | acpi_lock_hp_context(); |
146fc68a | 285 | acpiphp_put_context(context); |
e525506f | 286 | acpi_unlock_hp_context(); |
146fc68a | 287 | return AE_NO_MEMORY; |
ac372338 RW |
288 | } |
289 | ||
bda46dbb | 290 | slot->bus = bridge->pci_bus; |
ac372338 | 291 | slot->device = device; |
ac372338 | 292 | INIT_LIST_HEAD(&slot->funcs); |
ac372338 | 293 | |
ac372338 | 294 | list_add_tail(&slot->node, &bridge->slots); |
1da177e4 | 295 | |
cc6254e0 RW |
296 | /* |
297 | * Expose slots to user space for functions that have _EJ0 or _RMV or | |
298 | * are located in dock stations. Do not expose them for devices handled | |
84c8b58e MW |
299 | * by the native PCIe hotplug (PCIeHP) or standard PCI hotplug |
300 | * (SHPCHP), because that code is supposed to expose slots to user | |
301 | * space in those cases. | |
cc6254e0 | 302 | */ |
3b52b21f | 303 | if ((acpi_pci_check_ejectable(pbus, handle) || is_dock_device(adev)) |
84c8b58e | 304 | && !(pdev && hotplug_is_native(pdev))) { |
bbd34fcd RW |
305 | unsigned long long sun; |
306 | int retval; | |
ac372338 | 307 | |
bbd34fcd RW |
308 | bridge->nr_slots++; |
309 | status = acpi_evaluate_integer(handle, "_SUN", NULL, &sun); | |
310 | if (ACPI_FAILURE(status)) | |
311 | sun = bridge->nr_slots; | |
312 | ||
bd950799 | 313 | pr_debug("found ACPI PCI Hotplug slot %llu at PCI %04x:%02x:%02x\n", |
7342798d | 314 | sun, pci_domain_nr(pbus), pbus->number, device); |
bbd34fcd | 315 | |
7342798d | 316 | retval = acpiphp_register_hotplug_slot(slot, sun); |
bbd34fcd | 317 | if (retval) { |
1aaac071 | 318 | slot->slot = NULL; |
bbd34fcd RW |
319 | bridge->nr_slots--; |
320 | if (retval == -EBUSY) | |
227f0647 | 321 | pr_warn("Slot %llu already registered by another hotplug driver\n", sun); |
bbd34fcd | 322 | else |
227f0647 | 323 | pr_warn("acpiphp_register_hotplug_slot failed (err code = 0x%x)\n", retval); |
bbd34fcd RW |
324 | } |
325 | /* Even if the slot registration fails, we can still use it. */ | |
1da177e4 LT |
326 | } |
327 | ||
ac372338 | 328 | slot_found: |
1da177e4 LT |
329 | newfunc->slot = slot; |
330 | list_add_tail(&newfunc->sibling, &slot->funcs); | |
331 | ||
3b63aaa7 JL |
332 | if (pci_bus_read_dev_vendor_id(pbus, PCI_DEVFN(device, function), |
333 | &val, 60*1000)) | |
bc805a55 | 334 | slot->flags |= SLOT_ENABLED; |
1da177e4 | 335 | |
2e862c51 | 336 | return AE_OK; |
1da177e4 LT |
337 | } |
338 | ||
364d5094 | 339 | static void cleanup_bridge(struct acpiphp_bridge *bridge) |
1da177e4 | 340 | { |
3d54a316 JL |
341 | struct acpiphp_slot *slot; |
342 | struct acpiphp_func *func; | |
42f49a6a | 343 | |
3d54a316 JL |
344 | list_for_each_entry(slot, &bridge->slots, node) { |
345 | list_for_each_entry(func, &slot->funcs, sibling) { | |
1a699476 | 346 | struct acpi_device *adev = func_to_acpi_device(func); |
5a3bc573 | 347 | |
1a699476 | 348 | acpi_lock_hp_context(); |
be27b3dc | 349 | adev->hp->notify = NULL; |
edf5bf34 | 350 | adev->hp->fixup = NULL; |
1a699476 | 351 | acpi_unlock_hp_context(); |
42f49a6a | 352 | } |
9217a984 | 353 | slot->flags |= SLOT_IS_GOING_AWAY; |
1aaac071 RW |
354 | if (slot->slot) |
355 | acpiphp_unregister_hotplug_slot(slot); | |
42f49a6a RS |
356 | } |
357 | ||
3d54a316 | 358 | mutex_lock(&bridge_mutex); |
42f49a6a | 359 | list_del(&bridge->list); |
3d54a316 | 360 | mutex_unlock(&bridge_mutex); |
9217a984 | 361 | |
e525506f | 362 | acpi_lock_hp_context(); |
9217a984 | 363 | bridge->is_going_away = true; |
e525506f | 364 | acpi_unlock_hp_context(); |
1da177e4 LT |
365 | } |
366 | ||
15a1ae74 | 367 | /** |
26e6c66e | 368 | * acpiphp_max_busnr - return the highest reserved bus number under the given bus. |
15a1ae74 | 369 | * @bus: bus to start search with |
15a1ae74 KA |
370 | */ |
371 | static unsigned char acpiphp_max_busnr(struct pci_bus *bus) | |
372 | { | |
c6f0d5ad | 373 | struct pci_bus *tmp; |
15a1ae74 KA |
374 | unsigned char max, n; |
375 | ||
376 | /* | |
377 | * pci_bus_max_busnr will return the highest | |
378 | * reserved busnr for all these children. | |
379 | * that is equivalent to the bus->subordinate | |
380 | * value. We don't want to use the parent's | |
381 | * bus->subordinate value because it could have | |
382 | * padding in it. | |
383 | */ | |
b918c62e | 384 | max = bus->busn_res.start; |
15a1ae74 | 385 | |
c6f0d5ad YW |
386 | list_for_each_entry(tmp, &bus->children, node) { |
387 | n = pci_bus_max_busnr(tmp); | |
15a1ae74 KA |
388 | if (n > max) |
389 | max = n; | |
390 | } | |
391 | return max; | |
392 | } | |
393 | ||
d0607050 SL |
394 | static void acpiphp_set_acpi_region(struct acpiphp_slot *slot) |
395 | { | |
396 | struct acpiphp_func *func; | |
d0607050 SL |
397 | |
398 | list_for_each_entry(func, &slot->funcs, sibling) { | |
d0607050 | 399 | /* _REG is optional, we don't care about if there is failure */ |
6dd10c47 HG |
400 | acpi_evaluate_reg(func_to_handle(func), |
401 | ACPI_ADR_SPACE_PCI_CONFIG, | |
402 | ACPI_REG_CONNECT); | |
d0607050 SL |
403 | } |
404 | } | |
405 | ||
1f96a965 YL |
406 | static void check_hotplug_bridge(struct acpiphp_slot *slot, struct pci_dev *dev) |
407 | { | |
408 | struct acpiphp_func *func; | |
409 | ||
1f96a965 YL |
410 | /* quirk, or pcie could set it already */ |
411 | if (dev->is_hotplug_bridge) | |
412 | return; | |
413 | ||
c63a3be7 RW |
414 | /* |
415 | * In the PCIe case, only Root Ports and Downstream Ports are capable of | |
416 | * accommodating hotplug devices, so avoid marking Upstream Ports as | |
417 | * "hotplug bridges". | |
418 | */ | |
419 | if (pci_is_pcie(dev) && pci_pcie_type(dev) == PCI_EXP_TYPE_UPSTREAM) | |
420 | return; | |
421 | ||
1f96a965 YL |
422 | list_for_each_entry(func, &slot->funcs, sibling) { |
423 | if (PCI_FUNC(dev->devfn) == func->function) { | |
bbd34fcd | 424 | dev->is_hotplug_bridge = 1; |
1f96a965 YL |
425 | break; |
426 | } | |
427 | } | |
428 | } | |
3b63aaa7 | 429 | |
a47d8c8e RW |
430 | static int acpiphp_rescan_slot(struct acpiphp_slot *slot) |
431 | { | |
432 | struct acpiphp_func *func; | |
433 | ||
b6708fbf RW |
434 | list_for_each_entry(func, &slot->funcs, sibling) { |
435 | struct acpi_device *adev = func_to_acpi_device(func); | |
a47d8c8e | 436 | |
b6708fbf RW |
437 | acpi_bus_scan(adev->handle); |
438 | if (acpi_device_enumerated(adev)) | |
439 | acpi_device_set_power(adev, ACPI_STATE_D0); | |
440 | } | |
a47d8c8e RW |
441 | return pci_scan_slot(slot->bus, PCI_DEVFN(slot->device, 0)); |
442 | } | |
443 | ||
84c8b58e MW |
444 | static void acpiphp_native_scan_bridge(struct pci_dev *bridge) |
445 | { | |
446 | struct pci_bus *bus = bridge->subordinate; | |
447 | struct pci_dev *dev; | |
448 | int max; | |
449 | ||
450 | if (!bus) | |
451 | return; | |
452 | ||
453 | max = bus->busn_res.start; | |
454 | /* Scan already configured non-hotplug bridges */ | |
455 | for_each_pci_bridge(dev, bus) { | |
456 | if (!hotplug_is_native(dev)) | |
457 | max = pci_scan_bridge(bus, dev, max, 0); | |
458 | } | |
459 | ||
460 | /* Scan non-hotplug bridges that need to be reconfigured */ | |
461 | for_each_pci_bridge(dev, bus) { | |
77adf935 MW |
462 | if (hotplug_is_native(dev)) |
463 | continue; | |
464 | ||
465 | max = pci_scan_bridge(bus, dev, max, 1); | |
466 | if (dev->subordinate) { | |
467 | pcibios_resource_survey_bus(dev->subordinate); | |
468 | pci_bus_size_bridges(dev->subordinate); | |
469 | pci_bus_assign_resources(dev->subordinate); | |
470 | } | |
84c8b58e MW |
471 | } |
472 | } | |
473 | ||
1da177e4 | 474 | /** |
a1d0abce | 475 | * enable_slot - enable, configure a slot |
1da177e4 | 476 | * @slot: slot to be enabled |
f188b99f | 477 | * @bridge: true if enable is for the whole bridge (not a single slot) |
1da177e4 LT |
478 | * |
479 | * This function should be called per *physical slot*, | |
480 | * not per each slot object in ACPI namespace. | |
1da177e4 | 481 | */ |
f188b99f | 482 | static void enable_slot(struct acpiphp_slot *slot, bool bridge) |
1da177e4 | 483 | { |
1da177e4 | 484 | struct pci_dev *dev; |
bda46dbb | 485 | struct pci_bus *bus = slot->bus; |
1da177e4 | 486 | struct acpiphp_func *func; |
1da177e4 | 487 | |
f188b99f | 488 | if (bridge && bus->self && hotplug_is_native(bus->self)) { |
84c8b58e MW |
489 | /* |
490 | * If native hotplug is used, it will take care of hotplug | |
491 | * slot management and resource allocation for hotplug | |
492 | * bridges. However, ACPI hotplug may still be used for | |
493 | * non-hotplug bridges to bring in additional devices such | |
494 | * as a Thunderbolt host controller. | |
495 | */ | |
24a0c654 | 496 | for_each_pci_bridge(dev, bus) { |
84c8b58e MW |
497 | if (PCI_SLOT(dev->devfn) == slot->device) |
498 | acpiphp_native_scan_bridge(dev); | |
499 | } | |
84c8b58e | 500 | } else { |
cc22522f | 501 | LIST_HEAD(add_list); |
84c8b58e MW |
502 | int max, pass; |
503 | ||
504 | acpiphp_rescan_slot(slot); | |
505 | max = acpiphp_max_busnr(bus); | |
506 | for (pass = 0; pass < 2; pass++) { | |
507 | for_each_pci_bridge(dev, bus) { | |
508 | if (PCI_SLOT(dev->devfn) != slot->device) | |
509 | continue; | |
510 | ||
511 | max = pci_scan_bridge(bus, dev, max, pass); | |
512 | if (pass && dev->subordinate) { | |
513 | check_hotplug_bridge(slot, dev); | |
514 | pcibios_resource_survey_bus(dev->subordinate); | |
5df12742 BH |
515 | __pci_bus_size_bridges(dev->subordinate, |
516 | &add_list); | |
84c8b58e | 517 | } |
c64b5eea | 518 | } |
42f49a6a | 519 | } |
5df12742 | 520 | __pci_bus_assign_resources(bus, &add_list, NULL); |
1da177e4 | 521 | } |
2dc41281 | 522 | |
8e5dce35 | 523 | acpiphp_sanitize_bus(bus); |
81ee5732 | 524 | pcie_bus_configure_settings(bus); |
d0607050 | 525 | acpiphp_set_acpi_region(slot); |
69643e48 IC |
526 | |
527 | list_for_each_entry(dev, &bus->devices, bus_list) { | |
528 | /* Assume that newly added devices are powered on already. */ | |
44bda4b7 | 529 | if (!pci_dev_is_added(dev)) |
69643e48 IC |
530 | dev->current_state = PCI_D0; |
531 | } | |
532 | ||
42f49a6a RS |
533 | pci_bus_add_devices(bus); |
534 | ||
f382a086 | 535 | slot->flags |= SLOT_ENABLED; |
58c08628 | 536 | list_for_each_entry(func, &slot->funcs, sibling) { |
9d911d79 AC |
537 | dev = pci_get_slot(bus, PCI_DEVFN(slot->device, |
538 | func->function)); | |
f382a086 AK |
539 | if (!dev) { |
540 | /* Do not set SLOT_ENABLED flag if some funcs | |
541 | are not added. */ | |
9337a493 | 542 | slot->flags &= ~SLOT_ENABLED; |
551bcb75 | 543 | continue; |
f382a086 | 544 | } |
3bbfd319 | 545 | pci_dev_put(dev); |
1da177e4 | 546 | } |
1da177e4 LT |
547 | } |
548 | ||
1da177e4 | 549 | /** |
a1d0abce | 550 | * disable_slot - disable a slot |
26e6c66e | 551 | * @slot: ACPI PHP slot |
1da177e4 | 552 | */ |
a1d0abce | 553 | static void disable_slot(struct acpiphp_slot *slot) |
1da177e4 | 554 | { |
1c0c5443 RW |
555 | struct pci_bus *bus = slot->bus; |
556 | struct pci_dev *dev, *prev; | |
1da177e4 | 557 | struct acpiphp_func *func; |
551bcb75 | 558 | |
ce29ca3e | 559 | /* |
a1d0abce | 560 | * enable_slot() enumerates all functions in this device via |
ce29ca3e AK |
561 | * pci_scan_slot(), whether they have associated ACPI hotplug |
562 | * methods (_EJ0, etc.) or not. Therefore, we remove all functions | |
563 | * here. | |
564 | */ | |
1c0c5443 RW |
565 | list_for_each_entry_safe_reverse(dev, prev, &bus->devices, bus_list) |
566 | if (PCI_SLOT(dev->devfn) == slot->device) | |
567 | pci_stop_and_remove_bus_device(dev); | |
600812ec | 568 | |
5a3bc573 | 569 | list_for_each_entry(func, &slot->funcs, sibling) |
bbcbfc0e | 570 | acpi_bus_trim(func_to_acpi_device(func)); |
1da177e4 | 571 | |
9337a493 | 572 | slot->flags &= ~SLOT_ENABLED; |
1da177e4 LT |
573 | } |
574 | ||
f244d8b6 RW |
575 | static bool slot_no_hotplug(struct acpiphp_slot *slot) |
576 | { | |
b440bde7 BH |
577 | struct pci_bus *bus = slot->bus; |
578 | struct pci_dev *dev; | |
f244d8b6 | 579 | |
b440bde7 BH |
580 | list_for_each_entry(dev, &bus->devices, bus_list) { |
581 | if (PCI_SLOT(dev->devfn) == slot->device && dev->ignore_hotplug) | |
f244d8b6 | 582 | return true; |
b440bde7 | 583 | } |
f244d8b6 RW |
584 | return false; |
585 | } | |
1da177e4 LT |
586 | |
587 | /** | |
588 | * get_slot_status - get ACPI slot status | |
26e6c66e | 589 | * @slot: ACPI PHP slot |
1da177e4 | 590 | * |
26e6c66e RD |
591 | * If a slot has _STA for each function and if any one of them |
592 | * returned non-zero status, return it. | |
1da177e4 | 593 | * |
26e6c66e RD |
594 | * If a slot doesn't have _STA and if any one of its functions' |
595 | * configuration space is configured, return 0x0f as a _STA. | |
1da177e4 | 596 | * |
26e6c66e | 597 | * Otherwise return 0. |
1da177e4 LT |
598 | */ |
599 | static unsigned int get_slot_status(struct acpiphp_slot *slot) | |
600 | { | |
27663c58 | 601 | unsigned long long sta = 0; |
1da177e4 | 602 | struct acpiphp_func *func; |
13d3047c | 603 | u32 dvid; |
1da177e4 | 604 | |
58c08628 | 605 | list_for_each_entry(func, &slot->funcs, sibling) { |
1da177e4 | 606 | if (func->flags & FUNC_HAS_STA) { |
5a3bc573 RW |
607 | acpi_status status; |
608 | ||
609 | status = acpi_evaluate_integer(func_to_handle(func), | |
610 | "_STA", NULL, &sta); | |
1da177e4 LT |
611 | if (ACPI_SUCCESS(status) && sta) |
612 | break; | |
613 | } else { | |
13d3047c MW |
614 | if (pci_bus_read_dev_vendor_id(slot->bus, |
615 | PCI_DEVFN(slot->device, func->function), | |
616 | &dvid, 0)) { | |
1da177e4 LT |
617 | sta = ACPI_STA_ALL; |
618 | break; | |
619 | } | |
620 | } | |
621 | } | |
622 | ||
13d3047c MW |
623 | if (!sta) { |
624 | /* | |
625 | * Check for the slot itself since it may be that the | |
626 | * ACPI slot is a device below PCIe upstream port so in | |
627 | * that case it may not even be reachable yet. | |
628 | */ | |
629 | if (pci_bus_read_dev_vendor_id(slot->bus, | |
630 | PCI_DEVFN(slot->device, 0), &dvid, 0)) { | |
631 | sta = ACPI_STA_ALL; | |
632 | } | |
633 | } | |
634 | ||
1da177e4 LT |
635 | return (unsigned int)sta; |
636 | } | |
637 | ||
72820594 MW |
638 | static inline bool device_status_valid(unsigned int sta) |
639 | { | |
640 | /* | |
641 | * ACPI spec says that _STA may return bit 0 clear with bit 3 set | |
642 | * if the device is valid but does not require a device driver to be | |
643 | * loaded (Section 6.3.7 of ACPI 5.0A). | |
644 | */ | |
645 | unsigned int mask = ACPI_STA_DEVICE_ENABLED | ACPI_STA_DEVICE_FUNCTIONING; | |
646 | return (sta & mask) == mask; | |
647 | } | |
648 | ||
4ebe3450 RW |
649 | /** |
650 | * trim_stale_devices - remove PCI devices that are not responding. | |
651 | * @dev: PCI device to start walking the hierarchy from. | |
652 | */ | |
653 | static void trim_stale_devices(struct pci_dev *dev) | |
654 | { | |
4dc3082d | 655 | struct acpi_device *adev = ACPI_COMPANION(&dev->dev); |
4ebe3450 | 656 | struct pci_bus *bus = dev->subordinate; |
d41be346 | 657 | bool alive = dev->ignore_hotplug; |
4ebe3450 | 658 | |
4dc3082d | 659 | if (adev) { |
4ebe3450 RW |
660 | acpi_status status; |
661 | unsigned long long sta; | |
662 | ||
b2118d6a | 663 | status = acpi_evaluate_integer(adev->handle, "_STA", NULL, &sta); |
d41be346 | 664 | alive = alive || (ACPI_SUCCESS(status) && device_status_valid(sta)); |
4ebe3450 | 665 | } |
b8a62d54 RW |
666 | if (!alive) |
667 | alive = pci_device_is_present(dev); | |
4ebe3450 | 668 | |
4ebe3450 | 669 | if (!alive) { |
8f004f4a MW |
670 | pci_dev_set_disconnected(dev, NULL); |
671 | if (pci_has_subordinate(dev)) | |
672 | pci_walk_bus(dev->subordinate, pci_dev_set_disconnected, | |
673 | NULL); | |
674 | ||
4ebe3450 | 675 | pci_stop_and_remove_bus_device(dev); |
4dc3082d RW |
676 | if (adev) |
677 | acpi_bus_trim(adev); | |
4ebe3450 RW |
678 | } else if (bus) { |
679 | struct pci_dev *child, *tmp; | |
680 | ||
681 | /* The device is a bridge. so check the bus below it. */ | |
682 | pm_runtime_get_sync(&dev->dev); | |
2d7c1b77 | 683 | list_for_each_entry_safe_reverse(child, tmp, &bus->devices, bus_list) |
4ebe3450 RW |
684 | trim_stale_devices(child); |
685 | ||
686 | pm_runtime_put(&dev->dev); | |
687 | } | |
688 | } | |
689 | ||
1da177e4 LT |
690 | /** |
691 | * acpiphp_check_bridge - re-enumerate devices | |
26e6c66e | 692 | * @bridge: where to begin re-enumeration |
1da177e4 LT |
693 | * |
694 | * Iterate over all slots under this bridge and make sure that if a | |
695 | * card is present they are enabled, and if not they are disabled. | |
696 | */ | |
4ebe3450 | 697 | static void acpiphp_check_bridge(struct acpiphp_bridge *bridge) |
1da177e4 LT |
698 | { |
699 | struct acpiphp_slot *slot; | |
1da177e4 | 700 | |
9217a984 RW |
701 | /* Bail out if the bridge is going away. */ |
702 | if (bridge->is_going_away) | |
703 | return; | |
704 | ||
dd7fd3a8 RW |
705 | if (bridge->pci_dev) |
706 | pm_runtime_get_sync(&bridge->pci_dev->dev); | |
707 | ||
ad41dd9d | 708 | list_for_each_entry(slot, &bridge->slots, node) { |
4ebe3450 RW |
709 | struct pci_bus *bus = slot->bus; |
710 | struct pci_dev *dev, *tmp; | |
711 | ||
f244d8b6 RW |
712 | if (slot_no_hotplug(slot)) { |
713 | ; /* do nothing */ | |
72820594 | 714 | } else if (device_status_valid(get_slot_status(slot))) { |
4ebe3450 | 715 | /* remove stale devices if any */ |
2d7c1b77 RW |
716 | list_for_each_entry_safe_reverse(dev, tmp, |
717 | &bus->devices, bus_list) | |
4ebe3450 RW |
718 | if (PCI_SLOT(dev->devfn) == slot->device) |
719 | trim_stale_devices(dev); | |
720 | ||
721 | /* configure all functions */ | |
f188b99f | 722 | enable_slot(slot, true); |
1da177e4 | 723 | } else { |
a1d0abce | 724 | disable_slot(slot); |
1da177e4 LT |
725 | } |
726 | } | |
dd7fd3a8 RW |
727 | |
728 | if (bridge->pci_dev) | |
729 | pm_runtime_put(&bridge->pci_dev->dev); | |
1da177e4 LT |
730 | } |
731 | ||
8e7561cf RS |
732 | /* |
733 | * Remove devices for which we could not assign resources, call | |
734 | * arch specific code to fix-up the bus | |
735 | */ | |
736 | static void acpiphp_sanitize_bus(struct pci_bus *bus) | |
737 | { | |
d65eba6a | 738 | struct pci_dev *dev, *tmp; |
8e7561cf RS |
739 | int i; |
740 | unsigned long type_mask = IORESOURCE_IO | IORESOURCE_MEM; | |
741 | ||
2d7c1b77 | 742 | list_for_each_entry_safe_reverse(dev, tmp, &bus->devices, bus_list) { |
ff3ce480 | 743 | for (i = 0; i < PCI_BRIDGE_RESOURCES; i++) { |
8e7561cf RS |
744 | struct resource *res = &dev->resource[i]; |
745 | if ((res->flags & type_mask) && !res->start && | |
746 | res->end) { | |
747 | /* Could not assign a required resources | |
748 | * for this device, remove it */ | |
210647af | 749 | pci_stop_and_remove_bus_device(dev); |
8e7561cf RS |
750 | break; |
751 | } | |
752 | } | |
753 | } | |
754 | } | |
755 | ||
1da177e4 LT |
756 | /* |
757 | * ACPI event handlers | |
758 | */ | |
759 | ||
1f7c164b | 760 | void acpiphp_check_host_bridge(struct acpi_device *adev) |
3f327e39 | 761 | { |
86f5f3ca | 762 | struct acpiphp_bridge *bridge = NULL; |
3f327e39 | 763 | |
86f5f3ca RW |
764 | acpi_lock_hp_context(); |
765 | if (adev->hp) { | |
766 | bridge = to_acpiphp_root_context(adev->hp)->root_bridge; | |
767 | if (bridge) | |
768 | get_bridge(bridge); | |
769 | } | |
770 | acpi_unlock_hp_context(); | |
3f327e39 | 771 | if (bridge) { |
d42f5da2 RW |
772 | pci_lock_rescan_remove(); |
773 | ||
3f327e39 | 774 | acpiphp_check_bridge(bridge); |
d42f5da2 RW |
775 | |
776 | pci_unlock_rescan_remove(); | |
3f327e39 YL |
777 | put_bridge(bridge); |
778 | } | |
3f327e39 YL |
779 | } |
780 | ||
9217a984 RW |
781 | static int acpiphp_disable_and_eject_slot(struct acpiphp_slot *slot); |
782 | ||
d3a1ebb0 | 783 | static void hotplug_event(u32 type, struct acpiphp_context *context) |
1da177e4 | 784 | { |
3c2cc7ff | 785 | acpi_handle handle = context->hp.self->handle; |
bd4674df | 786 | struct acpiphp_func *func = &context->func; |
b75cece1 | 787 | struct acpiphp_slot *slot = func->slot; |
1da177e4 | 788 | struct acpiphp_bridge *bridge; |
6af8bef1 | 789 | |
e525506f | 790 | acpi_lock_hp_context(); |
c8ebcf1f | 791 | bridge = context->bridge; |
43e5c091 RW |
792 | if (bridge) |
793 | get_bridge(bridge); | |
1da177e4 | 794 | |
e525506f | 795 | acpi_unlock_hp_context(); |
3757b948 | 796 | |
f41b3261 | 797 | pci_lock_rescan_remove(); |
1da177e4 LT |
798 | |
799 | switch (type) { | |
800 | case ACPI_NOTIFY_BUS_CHECK: | |
801 | /* bus re-enumerate */ | |
1d4a5b61 | 802 | acpi_handle_debug(handle, "Bus check in %s()\n", __func__); |
b75cece1 | 803 | if (bridge) |
43e5c091 | 804 | acpiphp_check_bridge(bridge); |
b75cece1 | 805 | else if (!(slot->flags & SLOT_IS_GOING_AWAY)) |
f188b99f | 806 | enable_slot(slot, false); |
4ebe3450 | 807 | |
1da177e4 LT |
808 | break; |
809 | ||
810 | case ACPI_NOTIFY_DEVICE_CHECK: | |
811 | /* device check */ | |
1d4a5b61 | 812 | acpi_handle_debug(handle, "Device check in %s()\n", __func__); |
a47d8c8e | 813 | if (bridge) { |
43e5c091 | 814 | acpiphp_check_bridge(bridge); |
b75cece1 | 815 | } else if (!(slot->flags & SLOT_IS_GOING_AWAY)) { |
a47d8c8e RW |
816 | /* |
817 | * Check if anything has changed in the slot and rescan | |
818 | * from the parent if that's the case. | |
819 | */ | |
661b4064 | 820 | if (acpiphp_rescan_slot(slot)) |
a47d8c8e RW |
821 | acpiphp_check_bridge(func->parent); |
822 | } | |
1da177e4 LT |
823 | break; |
824 | ||
1da177e4 LT |
825 | case ACPI_NOTIFY_EJECT_REQUEST: |
826 | /* request device eject */ | |
1d4a5b61 | 827 | acpi_handle_debug(handle, "Eject request in %s()\n", __func__); |
b75cece1 | 828 | acpiphp_disable_and_eject_slot(slot); |
1da177e4 | 829 | break; |
1da177e4 | 830 | } |
6af8bef1 | 831 | |
f41b3261 | 832 | pci_unlock_rescan_remove(); |
43e5c091 RW |
833 | if (bridge) |
834 | put_bridge(bridge); | |
21a31013 RW |
835 | } |
836 | ||
be27b3dc | 837 | static int acpiphp_hotplug_notify(struct acpi_device *adev, u32 type) |
6af8bef1 | 838 | { |
87831273 | 839 | struct acpiphp_context *context; |
87831273 | 840 | |
edf5bf34 RW |
841 | context = acpiphp_grab_context(adev); |
842 | if (!context) | |
3c2cc7ff | 843 | return -ENODATA; |
1b360f44 | 844 | |
3c2cc7ff | 845 | hotplug_event(type, context); |
edf5bf34 | 846 | acpiphp_let_context_go(context); |
3c2cc7ff | 847 | return 0; |
8e7561cf | 848 | } |
1da177e4 | 849 | |
454481ad RW |
850 | /** |
851 | * acpiphp_enumerate_slots - Enumerate PCI slots for a given bus. | |
852 | * @bus: PCI bus to enumerate the slots for. | |
853 | * | |
854 | * A "slot" is an object associated with a PCI device number. All functions | |
855 | * (PCI devices) with the same bus and device number belong to the same slot. | |
1da177e4 | 856 | */ |
be1c9de9 | 857 | void acpiphp_enumerate_slots(struct pci_bus *bus) |
1da177e4 | 858 | { |
3b63aaa7 | 859 | struct acpiphp_bridge *bridge; |
bbcbfc0e | 860 | struct acpi_device *adev; |
2552002a RW |
861 | acpi_handle handle; |
862 | acpi_status status; | |
1da177e4 | 863 | |
3b63aaa7 JL |
864 | if (acpiphp_disabled) |
865 | return; | |
1da177e4 | 866 | |
bbcbfc0e RW |
867 | adev = ACPI_COMPANION(bus->bridge); |
868 | if (!adev) | |
3b63aaa7 | 869 | return; |
1da177e4 | 870 | |
bbcbfc0e | 871 | handle = adev->handle; |
3b63aaa7 | 872 | bridge = kzalloc(sizeof(struct acpiphp_bridge), GFP_KERNEL); |
c7abb235 | 873 | if (!bridge) |
3b63aaa7 | 874 | return; |
3b63aaa7 | 875 | |
ad41dd9d | 876 | INIT_LIST_HEAD(&bridge->slots); |
3d54a316 | 877 | kref_init(&bridge->ref); |
3b63aaa7 JL |
878 | bridge->pci_dev = pci_dev_get(bus->self); |
879 | bridge->pci_bus = bus; | |
880 | ||
881 | /* | |
882 | * Grab a ref to the subordinate PCI bus in case the bus is | |
883 | * removed via PCI core logical hotplug. The ref pins the bus | |
884 | * (which we access during module unload). | |
885 | */ | |
886 | get_device(&bus->dev); | |
887 | ||
882d18a7 RW |
888 | acpi_lock_hp_context(); |
889 | if (pci_is_root_bus(bridge->pci_bus)) { | |
890 | struct acpiphp_root_context *root_context; | |
891 | ||
892 | root_context = kzalloc(sizeof(*root_context), GFP_KERNEL); | |
893 | if (!root_context) | |
894 | goto err; | |
895 | ||
896 | root_context->root_bridge = bridge; | |
ba574dc8 | 897 | acpi_set_hp_context(adev, &root_context->hp); |
882d18a7 | 898 | } else { |
bbd34fcd RW |
899 | struct acpiphp_context *context; |
900 | ||
901 | /* | |
902 | * This bridge should have been registered as a hotplug function | |
fd3cfebe RW |
903 | * under its parent, so the context should be there, unless the |
904 | * parent is going to be handled by pciehp, in which case this | |
905 | * bridge is not interesting to us either. | |
bbd34fcd | 906 | */ |
3c2cc7ff | 907 | context = acpiphp_get_context(adev); |
882d18a7 RW |
908 | if (!context) |
909 | goto err; | |
910 | ||
bbd34fcd RW |
911 | bridge->context = context; |
912 | context->bridge = bridge; | |
913 | /* Get a reference to the parent bridge. */ | |
bda46dbb | 914 | get_bridge(context->func.parent); |
bbd34fcd | 915 | } |
882d18a7 | 916 | acpi_unlock_hp_context(); |
bbd34fcd | 917 | |
3799c5a0 | 918 | /* Must be added to the list prior to calling acpiphp_add_context(). */ |
2552002a RW |
919 | mutex_lock(&bridge_mutex); |
920 | list_add(&bridge->list, &bridge_list); | |
921 | mutex_unlock(&bridge_mutex); | |
922 | ||
923 | /* register all slot objects under this bridge */ | |
89373a55 | 924 | status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1, |
3799c5a0 | 925 | acpiphp_add_context, NULL, bridge, NULL); |
2552002a | 926 | if (ACPI_FAILURE(status)) { |
89373a55 | 927 | acpi_handle_err(handle, "failed to register slots\n"); |
bbd34fcd RW |
928 | cleanup_bridge(bridge); |
929 | put_bridge(bridge); | |
2552002a | 930 | } |
882d18a7 RW |
931 | return; |
932 | ||
933 | err: | |
934 | acpi_unlock_hp_context(); | |
935 | put_device(&bus->dev); | |
936 | pci_dev_put(bridge->pci_dev); | |
937 | kfree(bridge); | |
938 | } | |
939 | ||
902ee490 | 940 | static void acpiphp_drop_bridge(struct acpiphp_bridge *bridge) |
882d18a7 RW |
941 | { |
942 | if (pci_is_root_bus(bridge->pci_bus)) { | |
943 | struct acpiphp_root_context *root_context; | |
944 | struct acpi_device *adev; | |
945 | ||
946 | acpi_lock_hp_context(); | |
947 | adev = ACPI_COMPANION(bridge->pci_bus->bridge); | |
948 | root_context = to_acpiphp_root_context(adev->hp); | |
949 | adev->hp = NULL; | |
950 | acpi_unlock_hp_context(); | |
951 | kfree(root_context); | |
952 | } | |
953 | cleanup_bridge(bridge); | |
954 | put_bridge(bridge); | |
3b63aaa7 JL |
955 | } |
956 | ||
454481ad RW |
957 | /** |
958 | * acpiphp_remove_slots - Remove slot objects associated with a given bus. | |
959 | * @bus: PCI bus to remove the slot objects for. | |
960 | */ | |
3b63aaa7 | 961 | void acpiphp_remove_slots(struct pci_bus *bus) |
1da177e4 | 962 | { |
ff181e5a | 963 | struct acpiphp_bridge *bridge; |
3b63aaa7 JL |
964 | |
965 | if (acpiphp_disabled) | |
966 | return; | |
967 | ||
ff181e5a RW |
968 | mutex_lock(&bridge_mutex); |
969 | list_for_each_entry(bridge, &bridge_list, list) | |
3b63aaa7 | 970 | if (bridge->pci_bus == bus) { |
ff181e5a | 971 | mutex_unlock(&bridge_mutex); |
882d18a7 | 972 | acpiphp_drop_bridge(bridge); |
ff181e5a | 973 | return; |
3b63aaa7 | 974 | } |
ff181e5a RW |
975 | |
976 | mutex_unlock(&bridge_mutex); | |
1da177e4 LT |
977 | } |
978 | ||
1da177e4 LT |
979 | /** |
980 | * acpiphp_enable_slot - power on slot | |
26e6c66e | 981 | * @slot: ACPI PHP slot |
1da177e4 LT |
982 | */ |
983 | int acpiphp_enable_slot(struct acpiphp_slot *slot) | |
984 | { | |
9217a984 RW |
985 | pci_lock_rescan_remove(); |
986 | ||
2c3033a0 IY |
987 | if (slot->flags & SLOT_IS_GOING_AWAY) { |
988 | pci_unlock_rescan_remove(); | |
9217a984 | 989 | return -ENODEV; |
2c3033a0 | 990 | } |
9217a984 | 991 | |
bc805a55 | 992 | /* configure all functions */ |
55502ddb | 993 | if (!(slot->flags & SLOT_ENABLED)) |
f188b99f | 994 | enable_slot(slot, false); |
55502ddb | 995 | |
9217a984 | 996 | pci_unlock_rescan_remove(); |
a1d0abce | 997 | return 0; |
1da177e4 LT |
998 | } |
999 | ||
1da177e4 | 1000 | /** |
ad21d2d0 | 1001 | * acpiphp_disable_and_eject_slot - power off and eject slot |
26e6c66e | 1002 | * @slot: ACPI PHP slot |
1da177e4 | 1003 | */ |
9217a984 | 1004 | static int acpiphp_disable_and_eject_slot(struct acpiphp_slot *slot) |
1da177e4 | 1005 | { |
ad21d2d0 | 1006 | struct acpiphp_func *func; |
9217a984 RW |
1007 | |
1008 | if (slot->flags & SLOT_IS_GOING_AWAY) | |
1009 | return -ENODEV; | |
1da177e4 | 1010 | |
1da177e4 | 1011 | /* unconfigure all functions */ |
a1d0abce | 1012 | disable_slot(slot); |
1da177e4 | 1013 | |
ad21d2d0 MW |
1014 | list_for_each_entry(func, &slot->funcs, sibling) |
1015 | if (func->flags & FUNC_HAS_EJ0) { | |
1016 | acpi_handle handle = func_to_handle(func); | |
1017 | ||
1018 | if (ACPI_FAILURE(acpi_evaluate_ej0(handle))) | |
1019 | acpi_handle_err(handle, "_EJ0 failed\n"); | |
1020 | ||
1021 | break; | |
1022 | } | |
1023 | ||
9217a984 | 1024 | return 0; |
1da177e4 LT |
1025 | } |
1026 | ||
9217a984 RW |
1027 | int acpiphp_disable_slot(struct acpiphp_slot *slot) |
1028 | { | |
1029 | int ret; | |
1030 | ||
21369c77 RW |
1031 | /* |
1032 | * Acquire acpi_scan_lock to ensure that the execution of _EJ0 in | |
1033 | * acpiphp_disable_and_eject_slot() will be synchronized properly. | |
1034 | */ | |
1035 | acpi_scan_lock_acquire(); | |
9217a984 RW |
1036 | pci_lock_rescan_remove(); |
1037 | ret = acpiphp_disable_and_eject_slot(slot); | |
1038 | pci_unlock_rescan_remove(); | |
21369c77 | 1039 | acpi_scan_lock_release(); |
9217a984 RW |
1040 | return ret; |
1041 | } | |
1da177e4 LT |
1042 | |
1043 | /* | |
1044 | * slot enabled: 1 | |
1045 | * slot disabled: 0 | |
1046 | */ | |
1047 | u8 acpiphp_get_power_status(struct acpiphp_slot *slot) | |
1048 | { | |
bc805a55 | 1049 | return (slot->flags & SLOT_ENABLED); |
1da177e4 LT |
1050 | } |
1051 | ||
1da177e4 | 1052 | /* |
35ae61a0 MT |
1053 | * latch open: 1 |
1054 | * latch closed: 0 | |
1da177e4 LT |
1055 | */ |
1056 | u8 acpiphp_get_latch_status(struct acpiphp_slot *slot) | |
1057 | { | |
1ad3790a | 1058 | return !(get_slot_status(slot) & ACPI_STA_DEVICE_UI); |
1da177e4 LT |
1059 | } |
1060 | ||
1da177e4 LT |
1061 | /* |
1062 | * adapter presence : 1 | |
1063 | * absence : 0 | |
1064 | */ | |
1065 | u8 acpiphp_get_adapter_status(struct acpiphp_slot *slot) | |
1066 | { | |
1ad3790a | 1067 | return !!get_slot_status(slot); |
1da177e4 | 1068 | } |