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