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