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