diff options
Diffstat (limited to 'drivers/pci/hotplug/acpiphp_glue.c')
-rw-r--r-- | drivers/pci/hotplug/acpiphp_glue.c | 1344 |
1 files changed, 1344 insertions, 0 deletions
diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c new file mode 100644 index 000000000000..e7f41294f811 --- /dev/null +++ b/drivers/pci/hotplug/acpiphp_glue.c | |||
@@ -0,0 +1,1344 @@ | |||
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 | ||
7 | * | ||
8 | * All rights reserved. | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License as published by | ||
12 | * the Free Software Foundation; either version 2 of the License, or (at | ||
13 | * your option) any later version. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, but | ||
16 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
18 | * NON INFRINGEMENT. See the GNU General Public License for more | ||
19 | * details. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public License | ||
22 | * along with this program; if not, write to the Free Software | ||
23 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
24 | * | ||
25 | * Send feedback to <t-kochi@bq.jp.nec.com> | ||
26 | * | ||
27 | */ | ||
28 | |||
29 | #include <linux/init.h> | ||
30 | #include <linux/module.h> | ||
31 | |||
32 | #include <linux/kernel.h> | ||
33 | #include <linux/pci.h> | ||
34 | #include <linux/smp_lock.h> | ||
35 | #include <asm/semaphore.h> | ||
36 | |||
37 | #include "../pci.h" | ||
38 | #include "pci_hotplug.h" | ||
39 | #include "acpiphp.h" | ||
40 | |||
41 | static LIST_HEAD(bridge_list); | ||
42 | |||
43 | #define MY_NAME "acpiphp_glue" | ||
44 | |||
45 | static void handle_hotplug_event_bridge (acpi_handle, u32, void *); | ||
46 | static void handle_hotplug_event_func (acpi_handle, u32, void *); | ||
47 | |||
48 | /* | ||
49 | * initialization & terminatation routines | ||
50 | */ | ||
51 | |||
52 | /** | ||
53 | * is_ejectable - determine if a slot is ejectable | ||
54 | * @handle: handle to acpi namespace | ||
55 | * | ||
56 | * Ejectable slot should satisfy at least these conditions: | ||
57 | * | ||
58 | * 1. has _ADR method | ||
59 | * 2. has _EJ0 method | ||
60 | * | ||
61 | * optionally | ||
62 | * | ||
63 | * 1. has _STA method | ||
64 | * 2. has _PS0 method | ||
65 | * 3. has _PS3 method | ||
66 | * 4. .. | ||
67 | * | ||
68 | */ | ||
69 | static int is_ejectable(acpi_handle handle) | ||
70 | { | ||
71 | acpi_status status; | ||
72 | acpi_handle tmp; | ||
73 | |||
74 | status = acpi_get_handle(handle, "_ADR", &tmp); | ||
75 | if (ACPI_FAILURE(status)) { | ||
76 | return 0; | ||
77 | } | ||
78 | |||
79 | status = acpi_get_handle(handle, "_EJ0", &tmp); | ||
80 | if (ACPI_FAILURE(status)) { | ||
81 | return 0; | ||
82 | } | ||
83 | |||
84 | return 1; | ||
85 | } | ||
86 | |||
87 | |||
88 | /* callback routine to check the existence of ejectable slots */ | ||
89 | static acpi_status | ||
90 | is_ejectable_slot(acpi_handle handle, u32 lvl, void *context, void **rv) | ||
91 | { | ||
92 | int *count = (int *)context; | ||
93 | |||
94 | if (is_ejectable(handle)) { | ||
95 | (*count)++; | ||
96 | /* only one ejectable slot is enough */ | ||
97 | return AE_CTRL_TERMINATE; | ||
98 | } else { | ||
99 | return AE_OK; | ||
100 | } | ||
101 | } | ||
102 | |||
103 | |||
104 | /* callback routine to register each ACPI PCI slot object */ | ||
105 | static acpi_status | ||
106 | register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) | ||
107 | { | ||
108 | struct acpiphp_bridge *bridge = (struct acpiphp_bridge *)context; | ||
109 | struct acpiphp_slot *slot; | ||
110 | struct acpiphp_func *newfunc; | ||
111 | acpi_handle tmp; | ||
112 | acpi_status status = AE_OK; | ||
113 | unsigned long adr, sun; | ||
114 | int device, function; | ||
115 | static int num_slots = 0; /* XXX if we support I/O node hotplug... */ | ||
116 | |||
117 | status = acpi_evaluate_integer(handle, "_ADR", NULL, &adr); | ||
118 | |||
119 | if (ACPI_FAILURE(status)) | ||
120 | return AE_OK; | ||
121 | |||
122 | status = acpi_get_handle(handle, "_EJ0", &tmp); | ||
123 | |||
124 | if (ACPI_FAILURE(status)) | ||
125 | return AE_OK; | ||
126 | |||
127 | device = (adr >> 16) & 0xffff; | ||
128 | function = adr & 0xffff; | ||
129 | |||
130 | newfunc = kmalloc(sizeof(struct acpiphp_func), GFP_KERNEL); | ||
131 | if (!newfunc) | ||
132 | return AE_NO_MEMORY; | ||
133 | memset(newfunc, 0, sizeof(struct acpiphp_func)); | ||
134 | |||
135 | INIT_LIST_HEAD(&newfunc->sibling); | ||
136 | newfunc->handle = handle; | ||
137 | newfunc->function = function; | ||
138 | newfunc->flags = FUNC_HAS_EJ0; | ||
139 | |||
140 | if (ACPI_SUCCESS(acpi_get_handle(handle, "_STA", &tmp))) | ||
141 | newfunc->flags |= FUNC_HAS_STA; | ||
142 | |||
143 | if (ACPI_SUCCESS(acpi_get_handle(handle, "_PS0", &tmp))) | ||
144 | newfunc->flags |= FUNC_HAS_PS0; | ||
145 | |||
146 | if (ACPI_SUCCESS(acpi_get_handle(handle, "_PS3", &tmp))) | ||
147 | newfunc->flags |= FUNC_HAS_PS3; | ||
148 | |||
149 | status = acpi_evaluate_integer(handle, "_SUN", NULL, &sun); | ||
150 | if (ACPI_FAILURE(status)) | ||
151 | sun = -1; | ||
152 | |||
153 | /* search for objects that share the same slot */ | ||
154 | for (slot = bridge->slots; slot; slot = slot->next) | ||
155 | if (slot->device == device) { | ||
156 | if (slot->sun != sun) | ||
157 | warn("sibling found, but _SUN doesn't match!\n"); | ||
158 | break; | ||
159 | } | ||
160 | |||
161 | if (!slot) { | ||
162 | slot = kmalloc(sizeof(struct acpiphp_slot), GFP_KERNEL); | ||
163 | if (!slot) { | ||
164 | kfree(newfunc); | ||
165 | return AE_NO_MEMORY; | ||
166 | } | ||
167 | |||
168 | memset(slot, 0, sizeof(struct acpiphp_slot)); | ||
169 | slot->bridge = bridge; | ||
170 | slot->id = num_slots++; | ||
171 | slot->device = device; | ||
172 | slot->sun = sun; | ||
173 | INIT_LIST_HEAD(&slot->funcs); | ||
174 | init_MUTEX(&slot->crit_sect); | ||
175 | |||
176 | slot->next = bridge->slots; | ||
177 | bridge->slots = slot; | ||
178 | |||
179 | bridge->nr_slots++; | ||
180 | |||
181 | dbg("found ACPI PCI Hotplug slot at PCI %02x:%02x Slot:%d\n", | ||
182 | slot->bridge->bus, slot->device, slot->sun); | ||
183 | } | ||
184 | |||
185 | newfunc->slot = slot; | ||
186 | list_add_tail(&newfunc->sibling, &slot->funcs); | ||
187 | |||
188 | /* associate corresponding pci_dev */ | ||
189 | newfunc->pci_dev = pci_find_slot(bridge->bus, | ||
190 | PCI_DEVFN(device, function)); | ||
191 | if (newfunc->pci_dev) { | ||
192 | if (acpiphp_init_func_resource(newfunc) < 0) { | ||
193 | kfree(newfunc); | ||
194 | return AE_ERROR; | ||
195 | } | ||
196 | slot->flags |= (SLOT_ENABLED | SLOT_POWEREDON); | ||
197 | } | ||
198 | |||
199 | /* install notify handler */ | ||
200 | status = acpi_install_notify_handler(handle, | ||
201 | ACPI_SYSTEM_NOTIFY, | ||
202 | handle_hotplug_event_func, | ||
203 | newfunc); | ||
204 | |||
205 | if (ACPI_FAILURE(status)) { | ||
206 | err("failed to register interrupt notify handler\n"); | ||
207 | return status; | ||
208 | } | ||
209 | |||
210 | return AE_OK; | ||
211 | } | ||
212 | |||
213 | |||
214 | /* see if it's worth looking at this bridge */ | ||
215 | static int detect_ejectable_slots(acpi_handle *bridge_handle) | ||
216 | { | ||
217 | acpi_status status; | ||
218 | int count; | ||
219 | |||
220 | count = 0; | ||
221 | |||
222 | /* only check slots defined directly below bridge object */ | ||
223 | status = acpi_walk_namespace(ACPI_TYPE_DEVICE, bridge_handle, (u32)1, | ||
224 | is_ejectable_slot, (void *)&count, NULL); | ||
225 | |||
226 | return count; | ||
227 | } | ||
228 | |||
229 | |||
230 | /* decode ACPI _CRS data and convert into our internal resource list | ||
231 | * TBD: _TRA, etc. | ||
232 | */ | ||
233 | static acpi_status | ||
234 | decode_acpi_resource(struct acpi_resource *resource, void *context) | ||
235 | { | ||
236 | struct acpiphp_bridge *bridge = (struct acpiphp_bridge *) context; | ||
237 | struct acpi_resource_address64 address; | ||
238 | struct pci_resource *res; | ||
239 | |||
240 | if (resource->id != ACPI_RSTYPE_ADDRESS16 && | ||
241 | resource->id != ACPI_RSTYPE_ADDRESS32 && | ||
242 | resource->id != ACPI_RSTYPE_ADDRESS64) | ||
243 | return AE_OK; | ||
244 | |||
245 | acpi_resource_to_address64(resource, &address); | ||
246 | |||
247 | if (address.producer_consumer == ACPI_PRODUCER && address.address_length > 0) { | ||
248 | dbg("resource type: %d: 0x%llx - 0x%llx\n", address.resource_type, | ||
249 | (unsigned long long)address.min_address_range, | ||
250 | (unsigned long long)address.max_address_range); | ||
251 | res = acpiphp_make_resource(address.min_address_range, | ||
252 | address.address_length); | ||
253 | if (!res) { | ||
254 | err("out of memory\n"); | ||
255 | return AE_OK; | ||
256 | } | ||
257 | |||
258 | switch (address.resource_type) { | ||
259 | case ACPI_MEMORY_RANGE: | ||
260 | if (address.attribute.memory.cache_attribute == ACPI_PREFETCHABLE_MEMORY) { | ||
261 | res->next = bridge->p_mem_head; | ||
262 | bridge->p_mem_head = res; | ||
263 | } else { | ||
264 | res->next = bridge->mem_head; | ||
265 | bridge->mem_head = res; | ||
266 | } | ||
267 | break; | ||
268 | case ACPI_IO_RANGE: | ||
269 | res->next = bridge->io_head; | ||
270 | bridge->io_head = res; | ||
271 | break; | ||
272 | case ACPI_BUS_NUMBER_RANGE: | ||
273 | res->next = bridge->bus_head; | ||
274 | bridge->bus_head = res; | ||
275 | break; | ||
276 | default: | ||
277 | /* invalid type */ | ||
278 | kfree(res); | ||
279 | break; | ||
280 | } | ||
281 | } | ||
282 | |||
283 | return AE_OK; | ||
284 | } | ||
285 | |||
286 | /* decode ACPI 2.0 _HPP hot plug parameters */ | ||
287 | static void decode_hpp(struct acpiphp_bridge *bridge) | ||
288 | { | ||
289 | acpi_status status; | ||
290 | struct acpi_buffer buffer = { .length = ACPI_ALLOCATE_BUFFER, | ||
291 | .pointer = NULL}; | ||
292 | union acpi_object *package; | ||
293 | int i; | ||
294 | |||
295 | /* default numbers */ | ||
296 | bridge->hpp.cache_line_size = 0x10; | ||
297 | bridge->hpp.latency_timer = 0x40; | ||
298 | bridge->hpp.enable_SERR = 0; | ||
299 | bridge->hpp.enable_PERR = 0; | ||
300 | |||
301 | status = acpi_evaluate_object(bridge->handle, "_HPP", NULL, &buffer); | ||
302 | |||
303 | if (ACPI_FAILURE(status)) { | ||
304 | dbg("_HPP evaluation failed\n"); | ||
305 | return; | ||
306 | } | ||
307 | |||
308 | package = (union acpi_object *) buffer.pointer; | ||
309 | |||
310 | if (!package || package->type != ACPI_TYPE_PACKAGE || | ||
311 | package->package.count != 4 || !package->package.elements) { | ||
312 | err("invalid _HPP object; ignoring\n"); | ||
313 | goto err_exit; | ||
314 | } | ||
315 | |||
316 | for (i = 0; i < 4; i++) { | ||
317 | if (package->package.elements[i].type != ACPI_TYPE_INTEGER) { | ||
318 | err("invalid _HPP parameter type; ignoring\n"); | ||
319 | goto err_exit; | ||
320 | } | ||
321 | } | ||
322 | |||
323 | bridge->hpp.cache_line_size = package->package.elements[0].integer.value; | ||
324 | bridge->hpp.latency_timer = package->package.elements[1].integer.value; | ||
325 | bridge->hpp.enable_SERR = package->package.elements[2].integer.value; | ||
326 | bridge->hpp.enable_PERR = package->package.elements[3].integer.value; | ||
327 | |||
328 | dbg("_HPP parameter = (%02x, %02x, %02x, %02x)\n", | ||
329 | bridge->hpp.cache_line_size, | ||
330 | bridge->hpp.latency_timer, | ||
331 | bridge->hpp.enable_SERR, | ||
332 | bridge->hpp.enable_PERR); | ||
333 | |||
334 | bridge->flags |= BRIDGE_HAS_HPP; | ||
335 | |||
336 | err_exit: | ||
337 | kfree(buffer.pointer); | ||
338 | } | ||
339 | |||
340 | |||
341 | /* initialize miscellaneous stuff for both root and PCI-to-PCI bridge */ | ||
342 | static void init_bridge_misc(struct acpiphp_bridge *bridge) | ||
343 | { | ||
344 | acpi_status status; | ||
345 | |||
346 | /* decode ACPI 2.0 _HPP (hot plug parameters) */ | ||
347 | decode_hpp(bridge); | ||
348 | |||
349 | /* subtract all resources already allocated */ | ||
350 | acpiphp_detect_pci_resource(bridge); | ||
351 | |||
352 | /* register all slot objects under this bridge */ | ||
353 | status = acpi_walk_namespace(ACPI_TYPE_DEVICE, bridge->handle, (u32)1, | ||
354 | register_slot, bridge, NULL); | ||
355 | |||
356 | /* install notify handler */ | ||
357 | status = acpi_install_notify_handler(bridge->handle, | ||
358 | ACPI_SYSTEM_NOTIFY, | ||
359 | handle_hotplug_event_bridge, | ||
360 | bridge); | ||
361 | |||
362 | if (ACPI_FAILURE(status)) { | ||
363 | err("failed to register interrupt notify handler\n"); | ||
364 | } | ||
365 | |||
366 | list_add(&bridge->list, &bridge_list); | ||
367 | |||
368 | dbg("Bridge resource:\n"); | ||
369 | acpiphp_dump_resource(bridge); | ||
370 | } | ||
371 | |||
372 | |||
373 | /* allocate and initialize host bridge data structure */ | ||
374 | static void add_host_bridge(acpi_handle *handle, int seg, int bus) | ||
375 | { | ||
376 | acpi_status status; | ||
377 | struct acpiphp_bridge *bridge; | ||
378 | |||
379 | bridge = kmalloc(sizeof(struct acpiphp_bridge), GFP_KERNEL); | ||
380 | if (bridge == NULL) | ||
381 | return; | ||
382 | |||
383 | memset(bridge, 0, sizeof(struct acpiphp_bridge)); | ||
384 | |||
385 | bridge->type = BRIDGE_TYPE_HOST; | ||
386 | bridge->handle = handle; | ||
387 | bridge->seg = seg; | ||
388 | bridge->bus = bus; | ||
389 | |||
390 | bridge->pci_bus = pci_find_bus(seg, bus); | ||
391 | |||
392 | spin_lock_init(&bridge->res_lock); | ||
393 | |||
394 | /* to be overridden when we decode _CRS */ | ||
395 | bridge->sub = bridge->bus; | ||
396 | |||
397 | /* decode resources */ | ||
398 | |||
399 | status = acpi_walk_resources(handle, METHOD_NAME__CRS, | ||
400 | decode_acpi_resource, bridge); | ||
401 | |||
402 | if (ACPI_FAILURE(status)) { | ||
403 | err("failed to decode bridge resources\n"); | ||
404 | kfree(bridge); | ||
405 | return; | ||
406 | } | ||
407 | |||
408 | acpiphp_resource_sort_and_combine(&bridge->io_head); | ||
409 | acpiphp_resource_sort_and_combine(&bridge->mem_head); | ||
410 | acpiphp_resource_sort_and_combine(&bridge->p_mem_head); | ||
411 | acpiphp_resource_sort_and_combine(&bridge->bus_head); | ||
412 | |||
413 | dbg("ACPI _CRS resource:\n"); | ||
414 | acpiphp_dump_resource(bridge); | ||
415 | |||
416 | if (bridge->bus_head) { | ||
417 | bridge->bus = bridge->bus_head->base; | ||
418 | bridge->sub = bridge->bus_head->base + bridge->bus_head->length - 1; | ||
419 | } | ||
420 | |||
421 | init_bridge_misc(bridge); | ||
422 | } | ||
423 | |||
424 | |||
425 | /* allocate and initialize PCI-to-PCI bridge data structure */ | ||
426 | static void add_p2p_bridge(acpi_handle *handle, int seg, int bus, int dev, int fn) | ||
427 | { | ||
428 | struct acpiphp_bridge *bridge; | ||
429 | u8 tmp8; | ||
430 | u16 tmp16; | ||
431 | u64 base64, limit64; | ||
432 | u32 base, limit, base32u, limit32u; | ||
433 | |||
434 | bridge = kmalloc(sizeof(struct acpiphp_bridge), GFP_KERNEL); | ||
435 | if (bridge == NULL) { | ||
436 | err("out of memory\n"); | ||
437 | return; | ||
438 | } | ||
439 | |||
440 | memset(bridge, 0, sizeof(struct acpiphp_bridge)); | ||
441 | |||
442 | bridge->type = BRIDGE_TYPE_P2P; | ||
443 | bridge->handle = handle; | ||
444 | bridge->seg = seg; | ||
445 | |||
446 | bridge->pci_dev = pci_find_slot(bus, PCI_DEVFN(dev, fn)); | ||
447 | if (!bridge->pci_dev) { | ||
448 | err("Can't get pci_dev\n"); | ||
449 | kfree(bridge); | ||
450 | return; | ||
451 | } | ||
452 | |||
453 | bridge->pci_bus = bridge->pci_dev->subordinate; | ||
454 | if (!bridge->pci_bus) { | ||
455 | err("This is not a PCI-to-PCI bridge!\n"); | ||
456 | kfree(bridge); | ||
457 | return; | ||
458 | } | ||
459 | |||
460 | spin_lock_init(&bridge->res_lock); | ||
461 | |||
462 | bridge->bus = bridge->pci_bus->number; | ||
463 | bridge->sub = bridge->pci_bus->subordinate; | ||
464 | |||
465 | /* | ||
466 | * decode resources under this P2P bridge | ||
467 | */ | ||
468 | |||
469 | /* I/O resources */ | ||
470 | pci_read_config_byte(bridge->pci_dev, PCI_IO_BASE, &tmp8); | ||
471 | base = tmp8; | ||
472 | pci_read_config_byte(bridge->pci_dev, PCI_IO_LIMIT, &tmp8); | ||
473 | limit = tmp8; | ||
474 | |||
475 | switch (base & PCI_IO_RANGE_TYPE_MASK) { | ||
476 | case PCI_IO_RANGE_TYPE_16: | ||
477 | base = (base << 8) & 0xf000; | ||
478 | limit = ((limit << 8) & 0xf000) + 0xfff; | ||
479 | bridge->io_head = acpiphp_make_resource((u64)base, limit - base + 1); | ||
480 | if (!bridge->io_head) { | ||
481 | err("out of memory\n"); | ||
482 | kfree(bridge); | ||
483 | return; | ||
484 | } | ||
485 | dbg("16bit I/O range: %04x-%04x\n", | ||
486 | (u32)bridge->io_head->base, | ||
487 | (u32)(bridge->io_head->base + bridge->io_head->length - 1)); | ||
488 | break; | ||
489 | case PCI_IO_RANGE_TYPE_32: | ||
490 | pci_read_config_word(bridge->pci_dev, PCI_IO_BASE_UPPER16, &tmp16); | ||
491 | base = ((u32)tmp16 << 16) | ((base << 8) & 0xf000); | ||
492 | pci_read_config_word(bridge->pci_dev, PCI_IO_LIMIT_UPPER16, &tmp16); | ||
493 | limit = (((u32)tmp16 << 16) | ((limit << 8) & 0xf000)) + 0xfff; | ||
494 | bridge->io_head = acpiphp_make_resource((u64)base, limit - base + 1); | ||
495 | if (!bridge->io_head) { | ||
496 | err("out of memory\n"); | ||
497 | kfree(bridge); | ||
498 | return; | ||
499 | } | ||
500 | dbg("32bit I/O range: %08x-%08x\n", | ||
501 | (u32)bridge->io_head->base, | ||
502 | (u32)(bridge->io_head->base + bridge->io_head->length - 1)); | ||
503 | break; | ||
504 | case 0x0f: | ||
505 | dbg("I/O space unsupported\n"); | ||
506 | break; | ||
507 | default: | ||
508 | warn("Unknown I/O range type\n"); | ||
509 | } | ||
510 | |||
511 | /* Memory resources (mandatory for P2P bridge) */ | ||
512 | pci_read_config_word(bridge->pci_dev, PCI_MEMORY_BASE, &tmp16); | ||
513 | base = (tmp16 & 0xfff0) << 16; | ||
514 | pci_read_config_word(bridge->pci_dev, PCI_MEMORY_LIMIT, &tmp16); | ||
515 | limit = ((tmp16 & 0xfff0) << 16) | 0xfffff; | ||
516 | bridge->mem_head = acpiphp_make_resource((u64)base, limit - base + 1); | ||
517 | if (!bridge->mem_head) { | ||
518 | err("out of memory\n"); | ||
519 | kfree(bridge); | ||
520 | return; | ||
521 | } | ||
522 | dbg("32bit Memory range: %08x-%08x\n", | ||
523 | (u32)bridge->mem_head->base, | ||
524 | (u32)(bridge->mem_head->base + bridge->mem_head->length-1)); | ||
525 | |||
526 | /* Prefetchable Memory resources (optional) */ | ||
527 | pci_read_config_word(bridge->pci_dev, PCI_PREF_MEMORY_BASE, &tmp16); | ||
528 | base = tmp16; | ||
529 | pci_read_config_word(bridge->pci_dev, PCI_PREF_MEMORY_LIMIT, &tmp16); | ||
530 | limit = tmp16; | ||
531 | |||
532 | switch (base & PCI_MEMORY_RANGE_TYPE_MASK) { | ||
533 | case PCI_PREF_RANGE_TYPE_32: | ||
534 | base = (base & 0xfff0) << 16; | ||
535 | limit = ((limit & 0xfff0) << 16) | 0xfffff; | ||
536 | bridge->p_mem_head = acpiphp_make_resource((u64)base, limit - base + 1); | ||
537 | if (!bridge->p_mem_head) { | ||
538 | err("out of memory\n"); | ||
539 | kfree(bridge); | ||
540 | return; | ||
541 | } | ||
542 | dbg("32bit Prefetchable memory range: %08x-%08x\n", | ||
543 | (u32)bridge->p_mem_head->base, | ||
544 | (u32)(bridge->p_mem_head->base + bridge->p_mem_head->length - 1)); | ||
545 | break; | ||
546 | case PCI_PREF_RANGE_TYPE_64: | ||
547 | pci_read_config_dword(bridge->pci_dev, PCI_PREF_BASE_UPPER32, &base32u); | ||
548 | pci_read_config_dword(bridge->pci_dev, PCI_PREF_LIMIT_UPPER32, &limit32u); | ||
549 | base64 = ((u64)base32u << 32) | ((base & 0xfff0) << 16); | ||
550 | limit64 = (((u64)limit32u << 32) | ((limit & 0xfff0) << 16)) + 0xfffff; | ||
551 | |||
552 | bridge->p_mem_head = acpiphp_make_resource(base64, limit64 - base64 + 1); | ||
553 | if (!bridge->p_mem_head) { | ||
554 | err("out of memory\n"); | ||
555 | kfree(bridge); | ||
556 | return; | ||
557 | } | ||
558 | dbg("64bit Prefetchable memory range: %08x%08x-%08x%08x\n", | ||
559 | (u32)(bridge->p_mem_head->base >> 32), | ||
560 | (u32)(bridge->p_mem_head->base & 0xffffffff), | ||
561 | (u32)((bridge->p_mem_head->base + bridge->p_mem_head->length - 1) >> 32), | ||
562 | (u32)((bridge->p_mem_head->base + bridge->p_mem_head->length - 1) & 0xffffffff)); | ||
563 | break; | ||
564 | case 0x0f: | ||
565 | break; | ||
566 | default: | ||
567 | warn("Unknown prefetchale memory type\n"); | ||
568 | } | ||
569 | |||
570 | init_bridge_misc(bridge); | ||
571 | } | ||
572 | |||
573 | |||
574 | /* callback routine to find P2P bridges */ | ||
575 | static acpi_status | ||
576 | find_p2p_bridge(acpi_handle handle, u32 lvl, void *context, void **rv) | ||
577 | { | ||
578 | acpi_status status; | ||
579 | acpi_handle dummy_handle; | ||
580 | unsigned long *segbus = context; | ||
581 | unsigned long tmp; | ||
582 | int seg, bus, device, function; | ||
583 | struct pci_dev *dev; | ||
584 | |||
585 | /* get PCI address */ | ||
586 | seg = (*segbus >> 8) & 0xff; | ||
587 | bus = *segbus & 0xff; | ||
588 | |||
589 | status = acpi_get_handle(handle, "_ADR", &dummy_handle); | ||
590 | if (ACPI_FAILURE(status)) | ||
591 | return AE_OK; /* continue */ | ||
592 | |||
593 | status = acpi_evaluate_integer(handle, "_ADR", NULL, &tmp); | ||
594 | if (ACPI_FAILURE(status)) { | ||
595 | dbg("%s: _ADR evaluation failure\n", __FUNCTION__); | ||
596 | return AE_OK; | ||
597 | } | ||
598 | |||
599 | device = (tmp >> 16) & 0xffff; | ||
600 | function = tmp & 0xffff; | ||
601 | |||
602 | dev = pci_find_slot(bus, PCI_DEVFN(device, function)); | ||
603 | |||
604 | if (!dev) | ||
605 | return AE_OK; | ||
606 | |||
607 | if (!dev->subordinate) | ||
608 | return AE_OK; | ||
609 | |||
610 | /* check if this bridge has ejectable slots */ | ||
611 | if (detect_ejectable_slots(handle) > 0) { | ||
612 | dbg("found PCI-to-PCI bridge at PCI %s\n", pci_name(dev)); | ||
613 | add_p2p_bridge(handle, seg, bus, device, function); | ||
614 | } | ||
615 | |||
616 | return AE_OK; | ||
617 | } | ||
618 | |||
619 | |||
620 | /* find hot-pluggable slots, and then find P2P bridge */ | ||
621 | static int add_bridge(acpi_handle handle) | ||
622 | { | ||
623 | acpi_status status; | ||
624 | unsigned long tmp; | ||
625 | int seg, bus; | ||
626 | acpi_handle dummy_handle; | ||
627 | |||
628 | /* if the bridge doesn't have _STA, we assume it is always there */ | ||
629 | status = acpi_get_handle(handle, "_STA", &dummy_handle); | ||
630 | if (ACPI_SUCCESS(status)) { | ||
631 | status = acpi_evaluate_integer(handle, "_STA", NULL, &tmp); | ||
632 | if (ACPI_FAILURE(status)) { | ||
633 | dbg("%s: _STA evaluation failure\n", __FUNCTION__); | ||
634 | return 0; | ||
635 | } | ||
636 | if ((tmp & ACPI_STA_FUNCTIONING) == 0) | ||
637 | /* don't register this object */ | ||
638 | return 0; | ||
639 | } | ||
640 | |||
641 | /* get PCI segment number */ | ||
642 | status = acpi_evaluate_integer(handle, "_SEG", NULL, &tmp); | ||
643 | |||
644 | seg = ACPI_SUCCESS(status) ? tmp : 0; | ||
645 | |||
646 | /* get PCI bus number */ | ||
647 | status = acpi_evaluate_integer(handle, "_BBN", NULL, &tmp); | ||
648 | |||
649 | if (ACPI_SUCCESS(status)) { | ||
650 | bus = tmp; | ||
651 | } else { | ||
652 | warn("can't get bus number, assuming 0\n"); | ||
653 | bus = 0; | ||
654 | } | ||
655 | |||
656 | /* check if this bridge has ejectable slots */ | ||
657 | if (detect_ejectable_slots(handle) > 0) { | ||
658 | dbg("found PCI host-bus bridge with hot-pluggable slots\n"); | ||
659 | add_host_bridge(handle, seg, bus); | ||
660 | return 0; | ||
661 | } | ||
662 | |||
663 | tmp = seg << 8 | bus; | ||
664 | |||
665 | /* search P2P bridges under this host bridge */ | ||
666 | status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1, | ||
667 | find_p2p_bridge, &tmp, NULL); | ||
668 | |||
669 | if (ACPI_FAILURE(status)) | ||
670 | warn("find_p2p_bridge faied (error code = 0x%x)\n",status); | ||
671 | |||
672 | return 0; | ||
673 | } | ||
674 | |||
675 | |||
676 | static void remove_bridge(acpi_handle handle) | ||
677 | { | ||
678 | /* No-op for now .. */ | ||
679 | } | ||
680 | |||
681 | |||
682 | static int power_on_slot(struct acpiphp_slot *slot) | ||
683 | { | ||
684 | acpi_status status; | ||
685 | struct acpiphp_func *func; | ||
686 | struct list_head *l; | ||
687 | int retval = 0; | ||
688 | |||
689 | /* if already enabled, just skip */ | ||
690 | if (slot->flags & SLOT_POWEREDON) | ||
691 | goto err_exit; | ||
692 | |||
693 | list_for_each (l, &slot->funcs) { | ||
694 | func = list_entry(l, struct acpiphp_func, sibling); | ||
695 | |||
696 | if (func->flags & FUNC_HAS_PS0) { | ||
697 | dbg("%s: executing _PS0\n", __FUNCTION__); | ||
698 | status = acpi_evaluate_object(func->handle, "_PS0", NULL, NULL); | ||
699 | if (ACPI_FAILURE(status)) { | ||
700 | warn("%s: _PS0 failed\n", __FUNCTION__); | ||
701 | retval = -1; | ||
702 | goto err_exit; | ||
703 | } else | ||
704 | break; | ||
705 | } | ||
706 | } | ||
707 | |||
708 | /* TBD: evaluate _STA to check if the slot is enabled */ | ||
709 | |||
710 | slot->flags |= SLOT_POWEREDON; | ||
711 | |||
712 | err_exit: | ||
713 | return retval; | ||
714 | } | ||
715 | |||
716 | |||
717 | static int power_off_slot(struct acpiphp_slot *slot) | ||
718 | { | ||
719 | acpi_status status; | ||
720 | struct acpiphp_func *func; | ||
721 | struct list_head *l; | ||
722 | struct acpi_object_list arg_list; | ||
723 | union acpi_object arg; | ||
724 | |||
725 | int retval = 0; | ||
726 | |||
727 | /* if already disabled, just skip */ | ||
728 | if ((slot->flags & SLOT_POWEREDON) == 0) | ||
729 | goto err_exit; | ||
730 | |||
731 | list_for_each (l, &slot->funcs) { | ||
732 | func = list_entry(l, struct acpiphp_func, sibling); | ||
733 | |||
734 | if (func->pci_dev && (func->flags & FUNC_HAS_PS3)) { | ||
735 | status = acpi_evaluate_object(func->handle, "_PS3", NULL, NULL); | ||
736 | if (ACPI_FAILURE(status)) { | ||
737 | warn("%s: _PS3 failed\n", __FUNCTION__); | ||
738 | retval = -1; | ||
739 | goto err_exit; | ||
740 | } else | ||
741 | break; | ||
742 | } | ||
743 | } | ||
744 | |||
745 | list_for_each (l, &slot->funcs) { | ||
746 | func = list_entry(l, struct acpiphp_func, sibling); | ||
747 | |||
748 | /* We don't want to call _EJ0 on non-existing functions. */ | ||
749 | if (func->pci_dev && (func->flags & FUNC_HAS_EJ0)) { | ||
750 | /* _EJ0 method take one argument */ | ||
751 | arg_list.count = 1; | ||
752 | arg_list.pointer = &arg; | ||
753 | arg.type = ACPI_TYPE_INTEGER; | ||
754 | arg.integer.value = 1; | ||
755 | |||
756 | status = acpi_evaluate_object(func->handle, "_EJ0", &arg_list, NULL); | ||
757 | if (ACPI_FAILURE(status)) { | ||
758 | warn("%s: _EJ0 failed\n", __FUNCTION__); | ||
759 | retval = -1; | ||
760 | goto err_exit; | ||
761 | } else | ||
762 | break; | ||
763 | } | ||
764 | } | ||
765 | |||
766 | /* TBD: evaluate _STA to check if the slot is disabled */ | ||
767 | |||
768 | slot->flags &= (~SLOT_POWEREDON); | ||
769 | |||
770 | err_exit: | ||
771 | return retval; | ||
772 | } | ||
773 | |||
774 | |||
775 | /** | ||
776 | * enable_device - enable, configure a slot | ||
777 | * @slot: slot to be enabled | ||
778 | * | ||
779 | * This function should be called per *physical slot*, | ||
780 | * not per each slot object in ACPI namespace. | ||
781 | * | ||
782 | */ | ||
783 | static int enable_device(struct acpiphp_slot *slot) | ||
784 | { | ||
785 | u8 bus; | ||
786 | struct pci_dev *dev; | ||
787 | struct pci_bus *child; | ||
788 | struct list_head *l; | ||
789 | struct acpiphp_func *func; | ||
790 | int retval = 0; | ||
791 | int num; | ||
792 | |||
793 | if (slot->flags & SLOT_ENABLED) | ||
794 | goto err_exit; | ||
795 | |||
796 | /* sanity check: dev should be NULL when hot-plugged in */ | ||
797 | dev = pci_find_slot(slot->bridge->bus, PCI_DEVFN(slot->device, 0)); | ||
798 | if (dev) { | ||
799 | /* This case shouldn't happen */ | ||
800 | err("pci_dev structure already exists.\n"); | ||
801 | retval = -1; | ||
802 | goto err_exit; | ||
803 | } | ||
804 | |||
805 | /* allocate resources to device */ | ||
806 | retval = acpiphp_configure_slot(slot); | ||
807 | if (retval) | ||
808 | goto err_exit; | ||
809 | |||
810 | /* returned `dev' is the *first function* only! */ | ||
811 | num = pci_scan_slot(slot->bridge->pci_bus, PCI_DEVFN(slot->device, 0)); | ||
812 | if (num) | ||
813 | pci_bus_add_devices(slot->bridge->pci_bus); | ||
814 | dev = pci_find_slot(slot->bridge->bus, PCI_DEVFN(slot->device, 0)); | ||
815 | |||
816 | if (!dev) { | ||
817 | err("No new device found\n"); | ||
818 | retval = -1; | ||
819 | goto err_exit; | ||
820 | } | ||
821 | |||
822 | if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) { | ||
823 | pci_read_config_byte(dev, PCI_SECONDARY_BUS, &bus); | ||
824 | child = (struct pci_bus*) pci_add_new_bus(dev->bus, dev, bus); | ||
825 | pci_do_scan_bus(child); | ||
826 | } | ||
827 | |||
828 | /* associate pci_dev to our representation */ | ||
829 | list_for_each (l, &slot->funcs) { | ||
830 | func = list_entry(l, struct acpiphp_func, sibling); | ||
831 | |||
832 | func->pci_dev = pci_find_slot(slot->bridge->bus, | ||
833 | PCI_DEVFN(slot->device, | ||
834 | func->function)); | ||
835 | if (!func->pci_dev) | ||
836 | continue; | ||
837 | |||
838 | /* configure device */ | ||
839 | retval = acpiphp_configure_function(func); | ||
840 | if (retval) | ||
841 | goto err_exit; | ||
842 | } | ||
843 | |||
844 | slot->flags |= SLOT_ENABLED; | ||
845 | |||
846 | dbg("Available resources:\n"); | ||
847 | acpiphp_dump_resource(slot->bridge); | ||
848 | |||
849 | err_exit: | ||
850 | return retval; | ||
851 | } | ||
852 | |||
853 | |||
854 | /** | ||
855 | * disable_device - disable a slot | ||
856 | */ | ||
857 | static int disable_device(struct acpiphp_slot *slot) | ||
858 | { | ||
859 | int retval = 0; | ||
860 | struct acpiphp_func *func; | ||
861 | struct list_head *l; | ||
862 | |||
863 | /* is this slot already disabled? */ | ||
864 | if (!(slot->flags & SLOT_ENABLED)) | ||
865 | goto err_exit; | ||
866 | |||
867 | list_for_each (l, &slot->funcs) { | ||
868 | func = list_entry(l, struct acpiphp_func, sibling); | ||
869 | |||
870 | if (func->pci_dev) | ||
871 | acpiphp_unconfigure_function(func); | ||
872 | } | ||
873 | |||
874 | slot->flags &= (~SLOT_ENABLED); | ||
875 | |||
876 | err_exit: | ||
877 | return retval; | ||
878 | } | ||
879 | |||
880 | |||
881 | /** | ||
882 | * get_slot_status - get ACPI slot status | ||
883 | * | ||
884 | * if a slot has _STA for each function and if any one of them | ||
885 | * returned non-zero status, return it | ||
886 | * | ||
887 | * if a slot doesn't have _STA and if any one of its functions' | ||
888 | * configuration space is configured, return 0x0f as a _STA | ||
889 | * | ||
890 | * otherwise return 0 | ||
891 | */ | ||
892 | static unsigned int get_slot_status(struct acpiphp_slot *slot) | ||
893 | { | ||
894 | acpi_status status; | ||
895 | unsigned long sta = 0; | ||
896 | u32 dvid; | ||
897 | struct list_head *l; | ||
898 | struct acpiphp_func *func; | ||
899 | |||
900 | list_for_each (l, &slot->funcs) { | ||
901 | func = list_entry(l, struct acpiphp_func, sibling); | ||
902 | |||
903 | if (func->flags & FUNC_HAS_STA) { | ||
904 | status = acpi_evaluate_integer(func->handle, "_STA", NULL, &sta); | ||
905 | if (ACPI_SUCCESS(status) && sta) | ||
906 | break; | ||
907 | } else { | ||
908 | pci_bus_read_config_dword(slot->bridge->pci_bus, | ||
909 | PCI_DEVFN(slot->device, | ||
910 | func->function), | ||
911 | PCI_VENDOR_ID, &dvid); | ||
912 | if (dvid != 0xffffffff) { | ||
913 | sta = ACPI_STA_ALL; | ||
914 | break; | ||
915 | } | ||
916 | } | ||
917 | } | ||
918 | |||
919 | return (unsigned int)sta; | ||
920 | } | ||
921 | |||
922 | /** | ||
923 | * acpiphp_check_bridge - re-enumerate devices | ||
924 | * | ||
925 | * Iterate over all slots under this bridge and make sure that if a | ||
926 | * card is present they are enabled, and if not they are disabled. | ||
927 | */ | ||
928 | static int acpiphp_check_bridge(struct acpiphp_bridge *bridge) | ||
929 | { | ||
930 | struct acpiphp_slot *slot; | ||
931 | int retval = 0; | ||
932 | int enabled, disabled; | ||
933 | |||
934 | enabled = disabled = 0; | ||
935 | |||
936 | for (slot = bridge->slots; slot; slot = slot->next) { | ||
937 | unsigned int status = get_slot_status(slot); | ||
938 | if (slot->flags & SLOT_ENABLED) { | ||
939 | if (status == ACPI_STA_ALL) | ||
940 | continue; | ||
941 | retval = acpiphp_disable_slot(slot); | ||
942 | if (retval) { | ||
943 | err("Error occurred in disabling\n"); | ||
944 | goto err_exit; | ||
945 | } | ||
946 | disabled++; | ||
947 | } else { | ||
948 | if (status != ACPI_STA_ALL) | ||
949 | continue; | ||
950 | retval = acpiphp_enable_slot(slot); | ||
951 | if (retval) { | ||
952 | err("Error occurred in enabling\n"); | ||
953 | goto err_exit; | ||
954 | } | ||
955 | enabled++; | ||
956 | } | ||
957 | } | ||
958 | |||
959 | dbg("%s: %d enabled, %d disabled\n", __FUNCTION__, enabled, disabled); | ||
960 | |||
961 | err_exit: | ||
962 | return retval; | ||
963 | } | ||
964 | |||
965 | /* | ||
966 | * ACPI event handlers | ||
967 | */ | ||
968 | |||
969 | /** | ||
970 | * handle_hotplug_event_bridge - handle ACPI event on bridges | ||
971 | * | ||
972 | * @handle: Notify()'ed acpi_handle | ||
973 | * @type: Notify code | ||
974 | * @context: pointer to acpiphp_bridge structure | ||
975 | * | ||
976 | * handles ACPI event notification on {host,p2p} bridges | ||
977 | * | ||
978 | */ | ||
979 | static void handle_hotplug_event_bridge(acpi_handle handle, u32 type, void *context) | ||
980 | { | ||
981 | struct acpiphp_bridge *bridge; | ||
982 | char objname[64]; | ||
983 | struct acpi_buffer buffer = { .length = sizeof(objname), | ||
984 | .pointer = objname }; | ||
985 | |||
986 | bridge = (struct acpiphp_bridge *)context; | ||
987 | |||
988 | acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); | ||
989 | |||
990 | switch (type) { | ||
991 | case ACPI_NOTIFY_BUS_CHECK: | ||
992 | /* bus re-enumerate */ | ||
993 | dbg("%s: Bus check notify on %s\n", __FUNCTION__, objname); | ||
994 | acpiphp_check_bridge(bridge); | ||
995 | break; | ||
996 | |||
997 | case ACPI_NOTIFY_DEVICE_CHECK: | ||
998 | /* device check */ | ||
999 | dbg("%s: Device check notify on %s\n", __FUNCTION__, objname); | ||
1000 | acpiphp_check_bridge(bridge); | ||
1001 | break; | ||
1002 | |||
1003 | case ACPI_NOTIFY_DEVICE_WAKE: | ||
1004 | /* wake event */ | ||
1005 | dbg("%s: Device wake notify on %s\n", __FUNCTION__, objname); | ||
1006 | break; | ||
1007 | |||
1008 | case ACPI_NOTIFY_EJECT_REQUEST: | ||
1009 | /* request device eject */ | ||
1010 | dbg("%s: Device eject notify on %s\n", __FUNCTION__, objname); | ||
1011 | break; | ||
1012 | |||
1013 | case ACPI_NOTIFY_FREQUENCY_MISMATCH: | ||
1014 | printk(KERN_ERR "Device %s cannot be configured due" | ||
1015 | " to a frequency mismatch\n", objname); | ||
1016 | break; | ||
1017 | |||
1018 | case ACPI_NOTIFY_BUS_MODE_MISMATCH: | ||
1019 | printk(KERN_ERR "Device %s cannot be configured due" | ||
1020 | " to a bus mode mismatch\n", objname); | ||
1021 | break; | ||
1022 | |||
1023 | case ACPI_NOTIFY_POWER_FAULT: | ||
1024 | printk(KERN_ERR "Device %s has suffered a power fault\n", | ||
1025 | objname); | ||
1026 | break; | ||
1027 | |||
1028 | default: | ||
1029 | warn("notify_handler: unknown event type 0x%x for %s\n", type, objname); | ||
1030 | break; | ||
1031 | } | ||
1032 | } | ||
1033 | |||
1034 | |||
1035 | /** | ||
1036 | * handle_hotplug_event_func - handle ACPI event on functions (i.e. slots) | ||
1037 | * | ||
1038 | * @handle: Notify()'ed acpi_handle | ||
1039 | * @type: Notify code | ||
1040 | * @context: pointer to acpiphp_func structure | ||
1041 | * | ||
1042 | * handles ACPI event notification on slots | ||
1043 | * | ||
1044 | */ | ||
1045 | static void handle_hotplug_event_func(acpi_handle handle, u32 type, void *context) | ||
1046 | { | ||
1047 | struct acpiphp_func *func; | ||
1048 | char objname[64]; | ||
1049 | struct acpi_buffer buffer = { .length = sizeof(objname), | ||
1050 | .pointer = objname }; | ||
1051 | |||
1052 | acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); | ||
1053 | |||
1054 | func = (struct acpiphp_func *)context; | ||
1055 | |||
1056 | switch (type) { | ||
1057 | case ACPI_NOTIFY_BUS_CHECK: | ||
1058 | /* bus re-enumerate */ | ||
1059 | dbg("%s: Bus check notify on %s\n", __FUNCTION__, objname); | ||
1060 | acpiphp_enable_slot(func->slot); | ||
1061 | break; | ||
1062 | |||
1063 | case ACPI_NOTIFY_DEVICE_CHECK: | ||
1064 | /* device check : re-enumerate from parent bus */ | ||
1065 | dbg("%s: Device check notify on %s\n", __FUNCTION__, objname); | ||
1066 | acpiphp_check_bridge(func->slot->bridge); | ||
1067 | break; | ||
1068 | |||
1069 | case ACPI_NOTIFY_DEVICE_WAKE: | ||
1070 | /* wake event */ | ||
1071 | dbg("%s: Device wake notify on %s\n", __FUNCTION__, objname); | ||
1072 | break; | ||
1073 | |||
1074 | case ACPI_NOTIFY_EJECT_REQUEST: | ||
1075 | /* request device eject */ | ||
1076 | dbg("%s: Device eject notify on %s\n", __FUNCTION__, objname); | ||
1077 | acpiphp_disable_slot(func->slot); | ||
1078 | break; | ||
1079 | |||
1080 | default: | ||
1081 | warn("notify_handler: unknown event type 0x%x for %s\n", type, objname); | ||
1082 | break; | ||
1083 | } | ||
1084 | } | ||
1085 | |||
1086 | |||
1087 | static struct acpi_pci_driver acpi_pci_hp_driver = { | ||
1088 | .add = add_bridge, | ||
1089 | .remove = remove_bridge, | ||
1090 | }; | ||
1091 | |||
1092 | /** | ||
1093 | * acpiphp_glue_init - initializes all PCI hotplug - ACPI glue data structures | ||
1094 | * | ||
1095 | */ | ||
1096 | int __init acpiphp_glue_init(void) | ||
1097 | { | ||
1098 | int num; | ||
1099 | |||
1100 | if (list_empty(&pci_root_buses)) | ||
1101 | return -1; | ||
1102 | |||
1103 | num = acpi_pci_register_driver(&acpi_pci_hp_driver); | ||
1104 | |||
1105 | if (num <= 0) | ||
1106 | return -1; | ||
1107 | |||
1108 | return 0; | ||
1109 | } | ||
1110 | |||
1111 | |||
1112 | /** | ||
1113 | * acpiphp_glue_exit - terminates all PCI hotplug - ACPI glue data structures | ||
1114 | * | ||
1115 | * This function frees all data allocated in acpiphp_glue_init() | ||
1116 | */ | ||
1117 | void __exit acpiphp_glue_exit(void) | ||
1118 | { | ||
1119 | struct list_head *l1, *l2, *n1, *n2; | ||
1120 | struct acpiphp_bridge *bridge; | ||
1121 | struct acpiphp_slot *slot, *next; | ||
1122 | struct acpiphp_func *func; | ||
1123 | acpi_status status; | ||
1124 | |||
1125 | list_for_each_safe (l1, n1, &bridge_list) { | ||
1126 | bridge = (struct acpiphp_bridge *)l1; | ||
1127 | slot = bridge->slots; | ||
1128 | while (slot) { | ||
1129 | next = slot->next; | ||
1130 | list_for_each_safe (l2, n2, &slot->funcs) { | ||
1131 | func = list_entry(l2, struct acpiphp_func, sibling); | ||
1132 | acpiphp_free_resource(&func->io_head); | ||
1133 | acpiphp_free_resource(&func->mem_head); | ||
1134 | acpiphp_free_resource(&func->p_mem_head); | ||
1135 | acpiphp_free_resource(&func->bus_head); | ||
1136 | status = acpi_remove_notify_handler(func->handle, | ||
1137 | ACPI_SYSTEM_NOTIFY, | ||
1138 | handle_hotplug_event_func); | ||
1139 | if (ACPI_FAILURE(status)) | ||
1140 | err("failed to remove notify handler\n"); | ||
1141 | kfree(func); | ||
1142 | } | ||
1143 | kfree(slot); | ||
1144 | slot = next; | ||
1145 | } | ||
1146 | status = acpi_remove_notify_handler(bridge->handle, ACPI_SYSTEM_NOTIFY, | ||
1147 | handle_hotplug_event_bridge); | ||
1148 | if (ACPI_FAILURE(status)) | ||
1149 | err("failed to remove notify handler\n"); | ||
1150 | |||
1151 | acpiphp_free_resource(&bridge->io_head); | ||
1152 | acpiphp_free_resource(&bridge->mem_head); | ||
1153 | acpiphp_free_resource(&bridge->p_mem_head); | ||
1154 | acpiphp_free_resource(&bridge->bus_head); | ||
1155 | |||
1156 | kfree(bridge); | ||
1157 | } | ||
1158 | |||
1159 | acpi_pci_unregister_driver(&acpi_pci_hp_driver); | ||
1160 | } | ||
1161 | |||
1162 | |||
1163 | /** | ||
1164 | * acpiphp_get_num_slots - count number of slots in a system | ||
1165 | */ | ||
1166 | int __init acpiphp_get_num_slots(void) | ||
1167 | { | ||
1168 | struct list_head *node; | ||
1169 | struct acpiphp_bridge *bridge; | ||
1170 | int num_slots; | ||
1171 | |||
1172 | num_slots = 0; | ||
1173 | |||
1174 | list_for_each (node, &bridge_list) { | ||
1175 | bridge = (struct acpiphp_bridge *)node; | ||
1176 | dbg("Bus%d %dslot(s)\n", bridge->bus, bridge->nr_slots); | ||
1177 | num_slots += bridge->nr_slots; | ||
1178 | } | ||
1179 | |||
1180 | dbg("Total %dslots\n", num_slots); | ||
1181 | return num_slots; | ||
1182 | } | ||
1183 | |||
1184 | |||
1185 | #if 0 | ||
1186 | /** | ||
1187 | * acpiphp_for_each_slot - call function for each slot | ||
1188 | * @fn: callback function | ||
1189 | * @data: context to be passed to callback function | ||
1190 | * | ||
1191 | */ | ||
1192 | static int acpiphp_for_each_slot(acpiphp_callback fn, void *data) | ||
1193 | { | ||
1194 | struct list_head *node; | ||
1195 | struct acpiphp_bridge *bridge; | ||
1196 | struct acpiphp_slot *slot; | ||
1197 | int retval = 0; | ||
1198 | |||
1199 | list_for_each (node, &bridge_list) { | ||
1200 | bridge = (struct acpiphp_bridge *)node; | ||
1201 | for (slot = bridge->slots; slot; slot = slot->next) { | ||
1202 | retval = fn(slot, data); | ||
1203 | if (!retval) | ||
1204 | goto err_exit; | ||
1205 | } | ||
1206 | } | ||
1207 | |||
1208 | err_exit: | ||
1209 | return retval; | ||
1210 | } | ||
1211 | #endif | ||
1212 | |||
1213 | /* search matching slot from id */ | ||
1214 | struct acpiphp_slot *get_slot_from_id(int id) | ||
1215 | { | ||
1216 | struct list_head *node; | ||
1217 | struct acpiphp_bridge *bridge; | ||
1218 | struct acpiphp_slot *slot; | ||
1219 | |||
1220 | list_for_each (node, &bridge_list) { | ||
1221 | bridge = (struct acpiphp_bridge *)node; | ||
1222 | for (slot = bridge->slots; slot; slot = slot->next) | ||
1223 | if (slot->id == id) | ||
1224 | return slot; | ||
1225 | } | ||
1226 | |||
1227 | /* should never happen! */ | ||
1228 | err("%s: no object for id %d\n", __FUNCTION__, id); | ||
1229 | WARN_ON(1); | ||
1230 | return NULL; | ||
1231 | } | ||
1232 | |||
1233 | |||
1234 | /** | ||
1235 | * acpiphp_enable_slot - power on slot | ||
1236 | */ | ||
1237 | int acpiphp_enable_slot(struct acpiphp_slot *slot) | ||
1238 | { | ||
1239 | int retval; | ||
1240 | |||
1241 | down(&slot->crit_sect); | ||
1242 | |||
1243 | /* wake up all functions */ | ||
1244 | retval = power_on_slot(slot); | ||
1245 | if (retval) | ||
1246 | goto err_exit; | ||
1247 | |||
1248 | if (get_slot_status(slot) == ACPI_STA_ALL) | ||
1249 | /* configure all functions */ | ||
1250 | retval = enable_device(slot); | ||
1251 | |||
1252 | err_exit: | ||
1253 | up(&slot->crit_sect); | ||
1254 | return retval; | ||
1255 | } | ||
1256 | |||
1257 | |||
1258 | /** | ||
1259 | * acpiphp_disable_slot - power off slot | ||
1260 | */ | ||
1261 | int acpiphp_disable_slot(struct acpiphp_slot *slot) | ||
1262 | { | ||
1263 | int retval = 0; | ||
1264 | |||
1265 | down(&slot->crit_sect); | ||
1266 | |||
1267 | /* unconfigure all functions */ | ||
1268 | retval = disable_device(slot); | ||
1269 | if (retval) | ||
1270 | goto err_exit; | ||
1271 | |||
1272 | /* power off all functions */ | ||
1273 | retval = power_off_slot(slot); | ||
1274 | if (retval) | ||
1275 | goto err_exit; | ||
1276 | |||
1277 | acpiphp_resource_sort_and_combine(&slot->bridge->io_head); | ||
1278 | acpiphp_resource_sort_and_combine(&slot->bridge->mem_head); | ||
1279 | acpiphp_resource_sort_and_combine(&slot->bridge->p_mem_head); | ||
1280 | acpiphp_resource_sort_and_combine(&slot->bridge->bus_head); | ||
1281 | dbg("Available resources:\n"); | ||
1282 | acpiphp_dump_resource(slot->bridge); | ||
1283 | |||
1284 | err_exit: | ||
1285 | up(&slot->crit_sect); | ||
1286 | return retval; | ||
1287 | } | ||
1288 | |||
1289 | |||
1290 | /* | ||
1291 | * slot enabled: 1 | ||
1292 | * slot disabled: 0 | ||
1293 | */ | ||
1294 | u8 acpiphp_get_power_status(struct acpiphp_slot *slot) | ||
1295 | { | ||
1296 | unsigned int sta; | ||
1297 | |||
1298 | sta = get_slot_status(slot); | ||
1299 | |||
1300 | return (sta & ACPI_STA_ENABLED) ? 1 : 0; | ||
1301 | } | ||
1302 | |||
1303 | |||
1304 | /* | ||
1305 | * latch closed: 1 | ||
1306 | * latch open: 0 | ||
1307 | */ | ||
1308 | u8 acpiphp_get_latch_status(struct acpiphp_slot *slot) | ||
1309 | { | ||
1310 | unsigned int sta; | ||
1311 | |||
1312 | sta = get_slot_status(slot); | ||
1313 | |||
1314 | return (sta & ACPI_STA_SHOW_IN_UI) ? 1 : 0; | ||
1315 | } | ||
1316 | |||
1317 | |||
1318 | /* | ||
1319 | * adapter presence : 1 | ||
1320 | * absence : 0 | ||
1321 | */ | ||
1322 | u8 acpiphp_get_adapter_status(struct acpiphp_slot *slot) | ||
1323 | { | ||
1324 | unsigned int sta; | ||
1325 | |||
1326 | sta = get_slot_status(slot); | ||
1327 | |||
1328 | return (sta == 0) ? 0 : 1; | ||
1329 | } | ||
1330 | |||
1331 | |||
1332 | /* | ||
1333 | * pci address (seg/bus/dev) | ||
1334 | */ | ||
1335 | u32 acpiphp_get_address(struct acpiphp_slot *slot) | ||
1336 | { | ||
1337 | u32 address; | ||
1338 | |||
1339 | address = ((slot->bridge->seg) << 16) | | ||
1340 | ((slot->bridge->bus) << 8) | | ||
1341 | slot->device; | ||
1342 | |||
1343 | return address; | ||
1344 | } | ||