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