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; |
3c2cc7ff | 83 | acpi_set_hp_context(adev, &context->hp, acpiphp_hotplug_event); |
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 | ||
1da177e4 | 258 | /* callback routine to register each ACPI PCI slot object */ |
cb7b8ced RW |
259 | static acpi_status register_slot(acpi_handle handle, u32 lvl, void *data, |
260 | void **rv) | |
1da177e4 | 261 | { |
cb7b8ced RW |
262 | struct acpiphp_bridge *bridge = data; |
263 | struct acpiphp_context *context; | |
bbcbfc0e | 264 | struct acpi_device *adev; |
1da177e4 LT |
265 | struct acpiphp_slot *slot; |
266 | struct acpiphp_func *newfunc; | |
1da177e4 | 267 | acpi_status status = AE_OK; |
bbd34fcd RW |
268 | unsigned long long adr; |
269 | int device, function; | |
e8c331e9 | 270 | struct pci_bus *pbus = bridge->pci_bus; |
bbd34fcd | 271 | struct pci_dev *pdev = bridge->pci_dev; |
3b63aaa7 | 272 | u32 val; |
1da177e4 | 273 | |
bbd34fcd | 274 | if (pdev && device_is_managed_by_native_pciehp(pdev)) |
1da177e4 LT |
275 | return AE_OK; |
276 | ||
dfb117b3 BH |
277 | status = acpi_evaluate_integer(handle, "_ADR", NULL, &adr); |
278 | if (ACPI_FAILURE(status)) { | |
f26ca1d6 TK |
279 | if (status != AE_NOT_FOUND) |
280 | acpi_handle_warn(handle, | |
281 | "can't evaluate _ADR (%#x)\n", status); | |
dfb117b3 BH |
282 | return AE_OK; |
283 | } | |
bbcbfc0e RW |
284 | if (acpi_bus_get_device(handle, &adev)) |
285 | return AE_OK; | |
dfb117b3 BH |
286 | |
287 | device = (adr >> 16) & 0xffff; | |
288 | function = adr & 0xffff; | |
289 | ||
e525506f | 290 | acpi_lock_hp_context(); |
bbcbfc0e | 291 | context = acpiphp_init_context(adev); |
cb7b8ced | 292 | if (!context) { |
e525506f | 293 | acpi_unlock_hp_context(); |
cb7b8ced | 294 | acpi_handle_err(handle, "No hotplug context\n"); |
cb7b8ced RW |
295 | return AE_NOT_EXIST; |
296 | } | |
bd4674df | 297 | newfunc = &context->func; |
bd4674df | 298 | newfunc->function = function; |
bda46dbb | 299 | newfunc->parent = bridge; |
cb7b8ced | 300 | |
ecd046da | 301 | if (acpi_has_method(handle, "_EJ0")) |
20416ea5 | 302 | newfunc->flags = FUNC_HAS_EJ0; |
1da177e4 | 303 | |
ecd046da | 304 | if (acpi_has_method(handle, "_STA")) |
1da177e4 LT |
305 | newfunc->flags |= FUNC_HAS_STA; |
306 | ||
1a699476 RW |
307 | /* |
308 | * Dock stations' notify handler should be used for dock devices instead | |
309 | * of the common one, so clear hp.event in their contexts. | |
310 | */ | |
ecd046da | 311 | if (acpi_has_method(handle, "_DCK")) |
1a699476 RW |
312 | context->hp.event = NULL; |
313 | ||
314 | acpi_unlock_hp_context(); | |
20416ea5 | 315 | |
1da177e4 | 316 | /* search for objects that share the same slot */ |
ad41dd9d | 317 | list_for_each_entry(slot, &bridge->slots, node) |
bbd34fcd | 318 | if (slot->device == device) |
ac372338 | 319 | goto slot_found; |
1da177e4 | 320 | |
ac372338 RW |
321 | slot = kzalloc(sizeof(struct acpiphp_slot), GFP_KERNEL); |
322 | if (!slot) { | |
e525506f | 323 | acpi_lock_hp_context(); |
146fc68a | 324 | acpiphp_put_context(context); |
e525506f | 325 | acpi_unlock_hp_context(); |
146fc68a | 326 | return AE_NO_MEMORY; |
ac372338 RW |
327 | } |
328 | ||
bda46dbb | 329 | slot->bus = bridge->pci_bus; |
ac372338 | 330 | slot->device = device; |
ac372338 | 331 | INIT_LIST_HEAD(&slot->funcs); |
ac372338 | 332 | |
ac372338 | 333 | list_add_tail(&slot->node, &bridge->slots); |
1da177e4 | 334 | |
f7625980 | 335 | /* Register slots for ejectable functions only. */ |
bbd34fcd RW |
336 | if (acpi_pci_check_ejectable(pbus, handle) || is_dock_device(handle)) { |
337 | unsigned long long sun; | |
338 | int retval; | |
ac372338 | 339 | |
bbd34fcd RW |
340 | bridge->nr_slots++; |
341 | status = acpi_evaluate_integer(handle, "_SUN", NULL, &sun); | |
342 | if (ACPI_FAILURE(status)) | |
343 | sun = bridge->nr_slots; | |
344 | ||
bd950799 | 345 | pr_debug("found ACPI PCI Hotplug slot %llu at PCI %04x:%02x:%02x\n", |
7342798d | 346 | sun, pci_domain_nr(pbus), pbus->number, device); |
bbd34fcd | 347 | |
7342798d | 348 | retval = acpiphp_register_hotplug_slot(slot, sun); |
bbd34fcd | 349 | if (retval) { |
1aaac071 | 350 | slot->slot = NULL; |
bbd34fcd RW |
351 | bridge->nr_slots--; |
352 | if (retval == -EBUSY) | |
bd950799 | 353 | pr_warn("Slot %llu already registered by another " |
7342798d | 354 | "hotplug driver\n", sun); |
bbd34fcd | 355 | else |
bd950799 | 356 | pr_warn("acpiphp_register_hotplug_slot failed " |
bbd34fcd RW |
357 | "(err code = 0x%x)\n", retval); |
358 | } | |
359 | /* Even if the slot registration fails, we can still use it. */ | |
1da177e4 LT |
360 | } |
361 | ||
ac372338 | 362 | slot_found: |
1da177e4 LT |
363 | newfunc->slot = slot; |
364 | list_add_tail(&newfunc->sibling, &slot->funcs); | |
365 | ||
3b63aaa7 JL |
366 | if (pci_bus_read_dev_vendor_id(pbus, PCI_DEVFN(device, function), |
367 | &val, 60*1000)) | |
bc805a55 | 368 | slot->flags |= SLOT_ENABLED; |
1da177e4 | 369 | |
4e8662bb KA |
370 | if (is_dock_device(handle)) { |
371 | /* we don't want to call this device's _EJ0 | |
372 | * because we want the dock notify handler | |
373 | * to call it after it calls _DCK | |
20416ea5 KA |
374 | */ |
375 | newfunc->flags &= ~FUNC_HAS_EJ0; | |
4e8662bb | 376 | if (register_hotplug_dock_device(handle, |
43e5c091 | 377 | &acpiphp_dock_ops, context, |
21a31013 | 378 | acpiphp_dock_init, acpiphp_dock_release)) |
bd950799 | 379 | pr_debug("failed to register dock device\n"); |
20416ea5 KA |
380 | } |
381 | ||
2e862c51 | 382 | return AE_OK; |
1da177e4 LT |
383 | } |
384 | ||
42f49a6a RS |
385 | static struct acpiphp_bridge *acpiphp_handle_to_bridge(acpi_handle handle) |
386 | { | |
3c2cc7ff | 387 | struct acpi_device *adev = acpi_bus_get_acpi_device(handle); |
ed13febf RW |
388 | struct acpiphp_context *context; |
389 | struct acpiphp_bridge *bridge = NULL; | |
58c08628 | 390 | |
3c2cc7ff RW |
391 | if (!adev) |
392 | return NULL; | |
393 | ||
e525506f | 394 | acpi_lock_hp_context(); |
3c2cc7ff | 395 | context = acpiphp_get_context(adev); |
ed13febf RW |
396 | if (context) { |
397 | bridge = context->bridge; | |
398 | if (bridge) | |
3d54a316 | 399 | get_bridge(bridge); |
42f49a6a | 400 | |
ed13febf RW |
401 | acpiphp_put_context(context); |
402 | } | |
e525506f | 403 | acpi_unlock_hp_context(); |
3c2cc7ff | 404 | acpi_bus_put_acpi_device(adev); |
ed13febf | 405 | return bridge; |
42f49a6a | 406 | } |
1da177e4 | 407 | |
364d5094 | 408 | static void cleanup_bridge(struct acpiphp_bridge *bridge) |
1da177e4 | 409 | { |
3d54a316 JL |
410 | struct acpiphp_slot *slot; |
411 | struct acpiphp_func *func; | |
42f49a6a | 412 | |
3d54a316 JL |
413 | list_for_each_entry(slot, &bridge->slots, node) { |
414 | list_for_each_entry(func, &slot->funcs, sibling) { | |
1a699476 | 415 | struct acpi_device *adev = func_to_acpi_device(func); |
5a3bc573 | 416 | |
1a699476 RW |
417 | if (is_dock_device(adev->handle)) |
418 | unregister_hotplug_dock_device(adev->handle); | |
5a3bc573 | 419 | |
1a699476 RW |
420 | acpi_lock_hp_context(); |
421 | adev->hp->event = NULL; | |
422 | acpi_unlock_hp_context(); | |
42f49a6a | 423 | } |
9217a984 | 424 | slot->flags |= SLOT_IS_GOING_AWAY; |
1aaac071 RW |
425 | if (slot->slot) |
426 | acpiphp_unregister_hotplug_slot(slot); | |
42f49a6a RS |
427 | } |
428 | ||
3d54a316 | 429 | mutex_lock(&bridge_mutex); |
42f49a6a | 430 | list_del(&bridge->list); |
3d54a316 | 431 | mutex_unlock(&bridge_mutex); |
9217a984 | 432 | |
e525506f | 433 | acpi_lock_hp_context(); |
9217a984 | 434 | bridge->is_going_away = true; |
e525506f | 435 | acpi_unlock_hp_context(); |
1da177e4 LT |
436 | } |
437 | ||
15a1ae74 | 438 | /** |
26e6c66e | 439 | * acpiphp_max_busnr - return the highest reserved bus number under the given bus. |
15a1ae74 | 440 | * @bus: bus to start search with |
15a1ae74 KA |
441 | */ |
442 | static unsigned char acpiphp_max_busnr(struct pci_bus *bus) | |
443 | { | |
444 | struct list_head *tmp; | |
445 | unsigned char max, n; | |
446 | ||
447 | /* | |
448 | * pci_bus_max_busnr will return the highest | |
449 | * reserved busnr for all these children. | |
450 | * that is equivalent to the bus->subordinate | |
451 | * value. We don't want to use the parent's | |
452 | * bus->subordinate value because it could have | |
453 | * padding in it. | |
454 | */ | |
b918c62e | 455 | max = bus->busn_res.start; |
15a1ae74 KA |
456 | |
457 | list_for_each(tmp, &bus->children) { | |
458 | n = pci_bus_max_busnr(pci_bus_b(tmp)); | |
459 | if (n > max) | |
460 | max = n; | |
461 | } | |
462 | return max; | |
463 | } | |
464 | ||
d0607050 SL |
465 | static void acpiphp_set_acpi_region(struct acpiphp_slot *slot) |
466 | { | |
467 | struct acpiphp_func *func; | |
468 | union acpi_object params[2]; | |
469 | struct acpi_object_list arg_list; | |
470 | ||
471 | list_for_each_entry(func, &slot->funcs, sibling) { | |
472 | arg_list.count = 2; | |
473 | arg_list.pointer = params; | |
474 | params[0].type = ACPI_TYPE_INTEGER; | |
475 | params[0].integer.value = ACPI_ADR_SPACE_PCI_CONFIG; | |
476 | params[1].type = ACPI_TYPE_INTEGER; | |
477 | params[1].integer.value = 1; | |
478 | /* _REG is optional, we don't care about if there is failure */ | |
5a3bc573 RW |
479 | acpi_evaluate_object(func_to_handle(func), "_REG", &arg_list, |
480 | NULL); | |
d0607050 SL |
481 | } |
482 | } | |
483 | ||
1f96a965 YL |
484 | static void check_hotplug_bridge(struct acpiphp_slot *slot, struct pci_dev *dev) |
485 | { | |
486 | struct acpiphp_func *func; | |
487 | ||
1f96a965 YL |
488 | /* quirk, or pcie could set it already */ |
489 | if (dev->is_hotplug_bridge) | |
490 | return; | |
491 | ||
1f96a965 YL |
492 | list_for_each_entry(func, &slot->funcs, sibling) { |
493 | if (PCI_FUNC(dev->devfn) == func->function) { | |
bbd34fcd | 494 | dev->is_hotplug_bridge = 1; |
1f96a965 YL |
495 | break; |
496 | } | |
497 | } | |
498 | } | |
3b63aaa7 | 499 | |
a47d8c8e RW |
500 | static int acpiphp_rescan_slot(struct acpiphp_slot *slot) |
501 | { | |
502 | struct acpiphp_func *func; | |
503 | ||
b6708fbf RW |
504 | list_for_each_entry(func, &slot->funcs, sibling) { |
505 | struct acpi_device *adev = func_to_acpi_device(func); | |
a47d8c8e | 506 | |
b6708fbf RW |
507 | acpi_bus_scan(adev->handle); |
508 | if (acpi_device_enumerated(adev)) | |
509 | acpi_device_set_power(adev, ACPI_STATE_D0); | |
510 | } | |
a47d8c8e RW |
511 | return pci_scan_slot(slot->bus, PCI_DEVFN(slot->device, 0)); |
512 | } | |
513 | ||
1da177e4 | 514 | /** |
a1d0abce | 515 | * enable_slot - enable, configure a slot |
1da177e4 LT |
516 | * @slot: slot to be enabled |
517 | * | |
518 | * This function should be called per *physical slot*, | |
519 | * not per each slot object in ACPI namespace. | |
1da177e4 | 520 | */ |
a1d0abce | 521 | static void __ref enable_slot(struct acpiphp_slot *slot) |
1da177e4 | 522 | { |
1da177e4 | 523 | struct pci_dev *dev; |
bda46dbb | 524 | struct pci_bus *bus = slot->bus; |
1da177e4 | 525 | struct acpiphp_func *func; |
b91182a6 | 526 | int max, pass; |
d66ecb72 | 527 | LIST_HEAD(add_list); |
1da177e4 | 528 | |
ab122590 | 529 | acpiphp_rescan_slot(slot); |
15a1ae74 | 530 | max = acpiphp_max_busnr(bus); |
42f49a6a RS |
531 | for (pass = 0; pass < 2; pass++) { |
532 | list_for_each_entry(dev, &bus->devices, bus_list) { | |
533 | if (PCI_SLOT(dev->devfn) != slot->device) | |
534 | continue; | |
a1d0abce | 535 | |
42f49a6a | 536 | if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE || |
c64b5eea | 537 | dev->hdr_type == PCI_HEADER_TYPE_CARDBUS) { |
42f49a6a | 538 | max = pci_scan_bridge(bus, dev, max, pass); |
1f96a965 YL |
539 | if (pass && dev->subordinate) { |
540 | check_hotplug_bridge(slot, dev); | |
d66ecb72 JL |
541 | pcibios_resource_survey_bus(dev->subordinate); |
542 | __pci_bus_size_bridges(dev->subordinate, | |
543 | &add_list); | |
1f96a965 | 544 | } |
c64b5eea | 545 | } |
42f49a6a | 546 | } |
1da177e4 | 547 | } |
d66ecb72 | 548 | __pci_bus_assign_resources(bus, &add_list, NULL); |
2dc41281 | 549 | |
8e5dce35 | 550 | acpiphp_sanitize_bus(bus); |
fca6825a | 551 | acpiphp_set_hpp_values(bus); |
d0607050 | 552 | acpiphp_set_acpi_region(slot); |
69643e48 IC |
553 | |
554 | list_for_each_entry(dev, &bus->devices, bus_list) { | |
555 | /* Assume that newly added devices are powered on already. */ | |
556 | if (!dev->is_added) | |
557 | dev->current_state = PCI_D0; | |
558 | } | |
559 | ||
42f49a6a RS |
560 | pci_bus_add_devices(bus); |
561 | ||
f382a086 | 562 | slot->flags |= SLOT_ENABLED; |
58c08628 | 563 | list_for_each_entry(func, &slot->funcs, sibling) { |
9d911d79 AC |
564 | dev = pci_get_slot(bus, PCI_DEVFN(slot->device, |
565 | func->function)); | |
f382a086 AK |
566 | if (!dev) { |
567 | /* Do not set SLOT_ENABLED flag if some funcs | |
568 | are not added. */ | |
569 | slot->flags &= (~SLOT_ENABLED); | |
551bcb75 | 570 | continue; |
f382a086 | 571 | } |
1da177e4 | 572 | } |
1da177e4 LT |
573 | } |
574 | ||
1da177e4 | 575 | /** |
a1d0abce | 576 | * disable_slot - disable a slot |
26e6c66e | 577 | * @slot: ACPI PHP slot |
1da177e4 | 578 | */ |
a1d0abce | 579 | static void disable_slot(struct acpiphp_slot *slot) |
1da177e4 | 580 | { |
1c0c5443 RW |
581 | struct pci_bus *bus = slot->bus; |
582 | struct pci_dev *dev, *prev; | |
1da177e4 | 583 | struct acpiphp_func *func; |
551bcb75 | 584 | |
ce29ca3e | 585 | /* |
a1d0abce | 586 | * enable_slot() enumerates all functions in this device via |
ce29ca3e AK |
587 | * pci_scan_slot(), whether they have associated ACPI hotplug |
588 | * methods (_EJ0, etc.) or not. Therefore, we remove all functions | |
589 | * here. | |
590 | */ | |
1c0c5443 RW |
591 | list_for_each_entry_safe_reverse(dev, prev, &bus->devices, bus_list) |
592 | if (PCI_SLOT(dev->devfn) == slot->device) | |
593 | pci_stop_and_remove_bus_device(dev); | |
600812ec | 594 | |
bbcbfc0e RW |
595 | list_for_each_entry(func, &slot->funcs, sibling) |
596 | acpi_bus_trim(func_to_acpi_device(func)); | |
1da177e4 LT |
597 | |
598 | slot->flags &= (~SLOT_ENABLED); | |
1da177e4 LT |
599 | } |
600 | ||
b2118d6a | 601 | static bool acpiphp_no_hotplug(struct acpi_device *adev) |
f244d8b6 | 602 | { |
f244d8b6 RW |
603 | return adev && adev->flags.no_hotplug; |
604 | } | |
605 | ||
606 | static bool slot_no_hotplug(struct acpiphp_slot *slot) | |
607 | { | |
608 | struct acpiphp_func *func; | |
609 | ||
bbcbfc0e RW |
610 | list_for_each_entry(func, &slot->funcs, sibling) |
611 | if (acpiphp_no_hotplug(func_to_acpi_device(func))) | |
b2118d6a | 612 | return true; |
bbcbfc0e | 613 | |
f244d8b6 RW |
614 | return false; |
615 | } | |
1da177e4 LT |
616 | |
617 | /** | |
618 | * get_slot_status - get ACPI slot status | |
26e6c66e | 619 | * @slot: ACPI PHP slot |
1da177e4 | 620 | * |
26e6c66e RD |
621 | * If a slot has _STA for each function and if any one of them |
622 | * returned non-zero status, return it. | |
1da177e4 | 623 | * |
26e6c66e RD |
624 | * If a slot doesn't have _STA and if any one of its functions' |
625 | * configuration space is configured, return 0x0f as a _STA. | |
1da177e4 | 626 | * |
26e6c66e | 627 | * Otherwise return 0. |
1da177e4 LT |
628 | */ |
629 | static unsigned int get_slot_status(struct acpiphp_slot *slot) | |
630 | { | |
27663c58 | 631 | unsigned long long sta = 0; |
1da177e4 LT |
632 | struct acpiphp_func *func; |
633 | ||
58c08628 | 634 | list_for_each_entry(func, &slot->funcs, sibling) { |
1da177e4 | 635 | if (func->flags & FUNC_HAS_STA) { |
5a3bc573 RW |
636 | acpi_status status; |
637 | ||
638 | status = acpi_evaluate_integer(func_to_handle(func), | |
639 | "_STA", NULL, &sta); | |
1da177e4 LT |
640 | if (ACPI_SUCCESS(status) && sta) |
641 | break; | |
642 | } else { | |
5a3bc573 RW |
643 | u32 dvid; |
644 | ||
bda46dbb | 645 | pci_bus_read_config_dword(slot->bus, |
1da177e4 LT |
646 | PCI_DEVFN(slot->device, |
647 | func->function), | |
648 | PCI_VENDOR_ID, &dvid); | |
649 | if (dvid != 0xffffffff) { | |
650 | sta = ACPI_STA_ALL; | |
651 | break; | |
652 | } | |
653 | } | |
654 | } | |
655 | ||
656 | return (unsigned int)sta; | |
657 | } | |
658 | ||
4ebe3450 RW |
659 | /** |
660 | * trim_stale_devices - remove PCI devices that are not responding. | |
661 | * @dev: PCI device to start walking the hierarchy from. | |
662 | */ | |
663 | static void trim_stale_devices(struct pci_dev *dev) | |
664 | { | |
4dc3082d | 665 | struct acpi_device *adev = ACPI_COMPANION(&dev->dev); |
4ebe3450 RW |
666 | struct pci_bus *bus = dev->subordinate; |
667 | bool alive = false; | |
668 | ||
4dc3082d | 669 | if (adev) { |
4ebe3450 RW |
670 | acpi_status status; |
671 | unsigned long long sta; | |
672 | ||
b2118d6a | 673 | status = acpi_evaluate_integer(adev->handle, "_STA", NULL, &sta); |
f244d8b6 | 674 | alive = (ACPI_SUCCESS(status) && sta == ACPI_STA_ALL) |
b2118d6a | 675 | || acpiphp_no_hotplug(adev); |
4ebe3450 RW |
676 | } |
677 | if (!alive) { | |
678 | u32 v; | |
679 | ||
680 | /* Check if the device responds. */ | |
681 | alive = pci_bus_read_dev_vendor_id(dev->bus, dev->devfn, &v, 0); | |
682 | } | |
683 | if (!alive) { | |
684 | pci_stop_and_remove_bus_device(dev); | |
4dc3082d RW |
685 | if (adev) |
686 | acpi_bus_trim(adev); | |
4ebe3450 RW |
687 | } else if (bus) { |
688 | struct pci_dev *child, *tmp; | |
689 | ||
690 | /* The device is a bridge. so check the bus below it. */ | |
691 | pm_runtime_get_sync(&dev->dev); | |
2d7c1b77 | 692 | list_for_each_entry_safe_reverse(child, tmp, &bus->devices, bus_list) |
4ebe3450 RW |
693 | trim_stale_devices(child); |
694 | ||
695 | pm_runtime_put(&dev->dev); | |
696 | } | |
697 | } | |
698 | ||
1da177e4 LT |
699 | /** |
700 | * acpiphp_check_bridge - re-enumerate devices | |
26e6c66e | 701 | * @bridge: where to begin re-enumeration |
1da177e4 LT |
702 | * |
703 | * Iterate over all slots under this bridge and make sure that if a | |
704 | * card is present they are enabled, and if not they are disabled. | |
705 | */ | |
4ebe3450 | 706 | static void acpiphp_check_bridge(struct acpiphp_bridge *bridge) |
1da177e4 LT |
707 | { |
708 | struct acpiphp_slot *slot; | |
1da177e4 | 709 | |
9217a984 RW |
710 | /* Bail out if the bridge is going away. */ |
711 | if (bridge->is_going_away) | |
712 | return; | |
713 | ||
ad41dd9d | 714 | list_for_each_entry(slot, &bridge->slots, node) { |
4ebe3450 RW |
715 | struct pci_bus *bus = slot->bus; |
716 | struct pci_dev *dev, *tmp; | |
717 | ||
f244d8b6 RW |
718 | if (slot_no_hotplug(slot)) { |
719 | ; /* do nothing */ | |
720 | } else if (get_slot_status(slot) == ACPI_STA_ALL) { | |
4ebe3450 | 721 | /* remove stale devices if any */ |
2d7c1b77 RW |
722 | list_for_each_entry_safe_reverse(dev, tmp, |
723 | &bus->devices, bus_list) | |
4ebe3450 RW |
724 | if (PCI_SLOT(dev->devfn) == slot->device) |
725 | trim_stale_devices(dev); | |
726 | ||
727 | /* configure all functions */ | |
a1d0abce | 728 | enable_slot(slot); |
1da177e4 | 729 | } else { |
a1d0abce | 730 | disable_slot(slot); |
1da177e4 LT |
731 | } |
732 | } | |
1da177e4 LT |
733 | } |
734 | ||
fca6825a | 735 | static void acpiphp_set_hpp_values(struct pci_bus *bus) |
8e7561cf | 736 | { |
8e7561cf RS |
737 | struct pci_dev *dev; |
738 | ||
e81995bb BH |
739 | list_for_each_entry(dev, &bus->devices, bus_list) |
740 | pci_configure_slot(dev); | |
8e7561cf RS |
741 | } |
742 | ||
743 | /* | |
744 | * Remove devices for which we could not assign resources, call | |
745 | * arch specific code to fix-up the bus | |
746 | */ | |
747 | static void acpiphp_sanitize_bus(struct pci_bus *bus) | |
748 | { | |
d65eba6a | 749 | struct pci_dev *dev, *tmp; |
8e7561cf RS |
750 | int i; |
751 | unsigned long type_mask = IORESOURCE_IO | IORESOURCE_MEM; | |
752 | ||
2d7c1b77 | 753 | list_for_each_entry_safe_reverse(dev, tmp, &bus->devices, bus_list) { |
8e7561cf RS |
754 | for (i=0; i<PCI_BRIDGE_RESOURCES; i++) { |
755 | struct resource *res = &dev->resource[i]; | |
756 | if ((res->flags & type_mask) && !res->start && | |
757 | res->end) { | |
758 | /* Could not assign a required resources | |
759 | * for this device, remove it */ | |
210647af | 760 | pci_stop_and_remove_bus_device(dev); |
8e7561cf RS |
761 | break; |
762 | } | |
763 | } | |
764 | } | |
765 | } | |
766 | ||
1da177e4 LT |
767 | /* |
768 | * ACPI event handlers | |
769 | */ | |
770 | ||
3f327e39 YL |
771 | void acpiphp_check_host_bridge(acpi_handle handle) |
772 | { | |
773 | struct acpiphp_bridge *bridge; | |
774 | ||
775 | bridge = acpiphp_handle_to_bridge(handle); | |
776 | if (bridge) { | |
d42f5da2 RW |
777 | pci_lock_rescan_remove(); |
778 | ||
3f327e39 | 779 | acpiphp_check_bridge(bridge); |
d42f5da2 RW |
780 | |
781 | pci_unlock_rescan_remove(); | |
3f327e39 YL |
782 | put_bridge(bridge); |
783 | } | |
3f327e39 YL |
784 | } |
785 | ||
9217a984 RW |
786 | static int acpiphp_disable_and_eject_slot(struct acpiphp_slot *slot); |
787 | ||
d3a1ebb0 | 788 | static void hotplug_event(u32 type, struct acpiphp_context *context) |
1da177e4 | 789 | { |
3c2cc7ff | 790 | acpi_handle handle = context->hp.self->handle; |
bd4674df | 791 | struct acpiphp_func *func = &context->func; |
b75cece1 | 792 | struct acpiphp_slot *slot = func->slot; |
1da177e4 | 793 | struct acpiphp_bridge *bridge; |
6af8bef1 | 794 | |
e525506f | 795 | acpi_lock_hp_context(); |
c8ebcf1f | 796 | bridge = context->bridge; |
43e5c091 RW |
797 | if (bridge) |
798 | get_bridge(bridge); | |
1da177e4 | 799 | |
e525506f | 800 | acpi_unlock_hp_context(); |
3757b948 | 801 | |
f41b3261 | 802 | pci_lock_rescan_remove(); |
1da177e4 LT |
803 | |
804 | switch (type) { | |
805 | case ACPI_NOTIFY_BUS_CHECK: | |
806 | /* bus re-enumerate */ | |
1d4a5b61 | 807 | acpi_handle_debug(handle, "Bus check in %s()\n", __func__); |
b75cece1 | 808 | if (bridge) |
43e5c091 | 809 | acpiphp_check_bridge(bridge); |
b75cece1 RW |
810 | else if (!(slot->flags & SLOT_IS_GOING_AWAY)) |
811 | enable_slot(slot); | |
4ebe3450 | 812 | |
1da177e4 LT |
813 | break; |
814 | ||
815 | case ACPI_NOTIFY_DEVICE_CHECK: | |
816 | /* device check */ | |
1d4a5b61 | 817 | acpi_handle_debug(handle, "Device check in %s()\n", __func__); |
a47d8c8e | 818 | if (bridge) { |
43e5c091 | 819 | acpiphp_check_bridge(bridge); |
b75cece1 | 820 | } else if (!(slot->flags & SLOT_IS_GOING_AWAY)) { |
a47d8c8e RW |
821 | /* |
822 | * Check if anything has changed in the slot and rescan | |
823 | * from the parent if that's the case. | |
824 | */ | |
661b4064 | 825 | if (acpiphp_rescan_slot(slot)) |
a47d8c8e RW |
826 | acpiphp_check_bridge(func->parent); |
827 | } | |
1da177e4 LT |
828 | break; |
829 | ||
1da177e4 LT |
830 | case ACPI_NOTIFY_EJECT_REQUEST: |
831 | /* request device eject */ | |
1d4a5b61 | 832 | acpi_handle_debug(handle, "Eject request in %s()\n", __func__); |
b75cece1 | 833 | acpiphp_disable_and_eject_slot(slot); |
1da177e4 | 834 | break; |
1da177e4 | 835 | } |
6af8bef1 | 836 | |
f41b3261 | 837 | pci_unlock_rescan_remove(); |
43e5c091 RW |
838 | if (bridge) |
839 | put_bridge(bridge); | |
21a31013 RW |
840 | } |
841 | ||
3c2cc7ff | 842 | static int acpiphp_hotplug_event(struct acpi_device *adev, u32 type) |
6af8bef1 | 843 | { |
87831273 | 844 | struct acpiphp_context *context; |
5c8d0e1d | 845 | |
e525506f | 846 | acpi_lock_hp_context(); |
3c2cc7ff RW |
847 | context = acpiphp_get_context(adev); |
848 | if (!context || context->func.parent->is_going_away) { | |
e525506f | 849 | acpi_unlock_hp_context(); |
3c2cc7ff | 850 | return -ENODATA; |
87831273 | 851 | } |
3c2cc7ff RW |
852 | get_bridge(context->func.parent); |
853 | acpiphp_put_context(context); | |
e525506f | 854 | acpi_unlock_hp_context(); |
e532e84e | 855 | |
3c2cc7ff RW |
856 | hotplug_event(type, context); |
857 | ||
858 | put_bridge(context->func.parent); | |
859 | return 0; | |
8e7561cf | 860 | } |
1da177e4 | 861 | |
454481ad RW |
862 | /** |
863 | * acpiphp_enumerate_slots - Enumerate PCI slots for a given bus. | |
864 | * @bus: PCI bus to enumerate the slots for. | |
865 | * | |
866 | * A "slot" is an object associated with a PCI device number. All functions | |
867 | * (PCI devices) with the same bus and device number belong to the same slot. | |
1da177e4 | 868 | */ |
be1c9de9 | 869 | void acpiphp_enumerate_slots(struct pci_bus *bus) |
1da177e4 | 870 | { |
3b63aaa7 | 871 | struct acpiphp_bridge *bridge; |
bbcbfc0e | 872 | struct acpi_device *adev; |
2552002a RW |
873 | acpi_handle handle; |
874 | acpi_status status; | |
1da177e4 | 875 | |
3b63aaa7 JL |
876 | if (acpiphp_disabled) |
877 | return; | |
1da177e4 | 878 | |
bbcbfc0e RW |
879 | adev = ACPI_COMPANION(bus->bridge); |
880 | if (!adev) | |
3b63aaa7 | 881 | return; |
1da177e4 | 882 | |
bbcbfc0e | 883 | handle = adev->handle; |
3b63aaa7 | 884 | bridge = kzalloc(sizeof(struct acpiphp_bridge), GFP_KERNEL); |
cb7b8ced RW |
885 | if (!bridge) { |
886 | acpi_handle_err(handle, "No memory for bridge object\n"); | |
3b63aaa7 JL |
887 | return; |
888 | } | |
889 | ||
ad41dd9d | 890 | INIT_LIST_HEAD(&bridge->slots); |
3d54a316 | 891 | kref_init(&bridge->ref); |
3b63aaa7 JL |
892 | bridge->pci_dev = pci_dev_get(bus->self); |
893 | bridge->pci_bus = bus; | |
894 | ||
895 | /* | |
896 | * Grab a ref to the subordinate PCI bus in case the bus is | |
897 | * removed via PCI core logical hotplug. The ref pins the bus | |
898 | * (which we access during module unload). | |
899 | */ | |
900 | get_device(&bus->dev); | |
901 | ||
bbd34fcd RW |
902 | if (!pci_is_root_bus(bridge->pci_bus)) { |
903 | struct acpiphp_context *context; | |
904 | ||
905 | /* | |
906 | * This bridge should have been registered as a hotplug function | |
fd3cfebe RW |
907 | * under its parent, so the context should be there, unless the |
908 | * parent is going to be handled by pciehp, in which case this | |
909 | * bridge is not interesting to us either. | |
bbd34fcd | 910 | */ |
e525506f | 911 | acpi_lock_hp_context(); |
3c2cc7ff | 912 | context = acpiphp_get_context(adev); |
fd3cfebe | 913 | if (!context) { |
e525506f | 914 | acpi_unlock_hp_context(); |
bbd34fcd | 915 | put_device(&bus->dev); |
5d449457 | 916 | pci_dev_put(bridge->pci_dev); |
bbd34fcd RW |
917 | kfree(bridge); |
918 | return; | |
919 | } | |
920 | bridge->context = context; | |
921 | context->bridge = bridge; | |
922 | /* Get a reference to the parent bridge. */ | |
bda46dbb | 923 | get_bridge(context->func.parent); |
e525506f | 924 | acpi_unlock_hp_context(); |
bbd34fcd RW |
925 | } |
926 | ||
2552002a RW |
927 | /* must be added to the list prior to calling register_slot */ |
928 | mutex_lock(&bridge_mutex); | |
929 | list_add(&bridge->list, &bridge_list); | |
930 | mutex_unlock(&bridge_mutex); | |
931 | ||
932 | /* register all slot objects under this bridge */ | |
89373a55 | 933 | status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1, |
2552002a RW |
934 | register_slot, NULL, bridge, NULL); |
935 | if (ACPI_FAILURE(status)) { | |
89373a55 | 936 | acpi_handle_err(handle, "failed to register slots\n"); |
bbd34fcd RW |
937 | cleanup_bridge(bridge); |
938 | put_bridge(bridge); | |
2552002a | 939 | } |
3b63aaa7 JL |
940 | } |
941 | ||
454481ad RW |
942 | /** |
943 | * acpiphp_remove_slots - Remove slot objects associated with a given bus. | |
944 | * @bus: PCI bus to remove the slot objects for. | |
945 | */ | |
3b63aaa7 | 946 | void acpiphp_remove_slots(struct pci_bus *bus) |
1da177e4 | 947 | { |
ff181e5a | 948 | struct acpiphp_bridge *bridge; |
3b63aaa7 JL |
949 | |
950 | if (acpiphp_disabled) | |
951 | return; | |
952 | ||
ff181e5a RW |
953 | mutex_lock(&bridge_mutex); |
954 | list_for_each_entry(bridge, &bridge_list, list) | |
3b63aaa7 | 955 | if (bridge->pci_bus == bus) { |
ff181e5a | 956 | mutex_unlock(&bridge_mutex); |
3b63aaa7 | 957 | cleanup_bridge(bridge); |
3d54a316 | 958 | put_bridge(bridge); |
ff181e5a | 959 | return; |
3b63aaa7 | 960 | } |
ff181e5a RW |
961 | |
962 | mutex_unlock(&bridge_mutex); | |
1da177e4 LT |
963 | } |
964 | ||
1da177e4 LT |
965 | /** |
966 | * acpiphp_enable_slot - power on slot | |
26e6c66e | 967 | * @slot: ACPI PHP slot |
1da177e4 LT |
968 | */ |
969 | int acpiphp_enable_slot(struct acpiphp_slot *slot) | |
970 | { | |
9217a984 RW |
971 | pci_lock_rescan_remove(); |
972 | ||
973 | if (slot->flags & SLOT_IS_GOING_AWAY) | |
974 | return -ENODEV; | |
975 | ||
bc805a55 | 976 | /* configure all functions */ |
55502ddb | 977 | if (!(slot->flags & SLOT_ENABLED)) |
a1d0abce | 978 | enable_slot(slot); |
55502ddb | 979 | |
9217a984 | 980 | pci_unlock_rescan_remove(); |
a1d0abce | 981 | return 0; |
1da177e4 LT |
982 | } |
983 | ||
1da177e4 | 984 | /** |
ad21d2d0 | 985 | * acpiphp_disable_and_eject_slot - power off and eject slot |
26e6c66e | 986 | * @slot: ACPI PHP slot |
1da177e4 | 987 | */ |
9217a984 | 988 | static int acpiphp_disable_and_eject_slot(struct acpiphp_slot *slot) |
1da177e4 | 989 | { |
ad21d2d0 | 990 | struct acpiphp_func *func; |
9217a984 RW |
991 | |
992 | if (slot->flags & SLOT_IS_GOING_AWAY) | |
993 | return -ENODEV; | |
1da177e4 | 994 | |
1da177e4 | 995 | /* unconfigure all functions */ |
a1d0abce | 996 | disable_slot(slot); |
1da177e4 | 997 | |
ad21d2d0 MW |
998 | list_for_each_entry(func, &slot->funcs, sibling) |
999 | if (func->flags & FUNC_HAS_EJ0) { | |
1000 | acpi_handle handle = func_to_handle(func); | |
1001 | ||
1002 | if (ACPI_FAILURE(acpi_evaluate_ej0(handle))) | |
1003 | acpi_handle_err(handle, "_EJ0 failed\n"); | |
1004 | ||
1005 | break; | |
1006 | } | |
1007 | ||
9217a984 | 1008 | return 0; |
1da177e4 LT |
1009 | } |
1010 | ||
9217a984 RW |
1011 | int acpiphp_disable_slot(struct acpiphp_slot *slot) |
1012 | { | |
1013 | int ret; | |
1014 | ||
1015 | pci_lock_rescan_remove(); | |
1016 | ret = acpiphp_disable_and_eject_slot(slot); | |
1017 | pci_unlock_rescan_remove(); | |
1018 | return ret; | |
1019 | } | |
1da177e4 LT |
1020 | |
1021 | /* | |
1022 | * slot enabled: 1 | |
1023 | * slot disabled: 0 | |
1024 | */ | |
1025 | u8 acpiphp_get_power_status(struct acpiphp_slot *slot) | |
1026 | { | |
bc805a55 | 1027 | return (slot->flags & SLOT_ENABLED); |
1da177e4 LT |
1028 | } |
1029 | ||
1da177e4 | 1030 | /* |
35ae61a0 MT |
1031 | * latch open: 1 |
1032 | * latch closed: 0 | |
1da177e4 LT |
1033 | */ |
1034 | u8 acpiphp_get_latch_status(struct acpiphp_slot *slot) | |
1035 | { | |
1ad3790a | 1036 | return !(get_slot_status(slot) & ACPI_STA_DEVICE_UI); |
1da177e4 LT |
1037 | } |
1038 | ||
1da177e4 LT |
1039 | /* |
1040 | * adapter presence : 1 | |
1041 | * absence : 0 | |
1042 | */ | |
1043 | u8 acpiphp_get_adapter_status(struct acpiphp_slot *slot) | |
1044 | { | |
1ad3790a | 1045 | return !!get_slot_status(slot); |
1da177e4 | 1046 | } |