diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/pci/hotplug/cpci_hotplug_pci.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/pci/hotplug/cpci_hotplug_pci.c')
-rw-r--r-- | drivers/pci/hotplug/cpci_hotplug_pci.c | 661 |
1 files changed, 661 insertions, 0 deletions
diff --git a/drivers/pci/hotplug/cpci_hotplug_pci.c b/drivers/pci/hotplug/cpci_hotplug_pci.c new file mode 100644 index 000000000000..2e969616f298 --- /dev/null +++ b/drivers/pci/hotplug/cpci_hotplug_pci.c | |||
@@ -0,0 +1,661 @@ | |||
1 | /* | ||
2 | * CompactPCI Hot Plug Driver PCI functions | ||
3 | * | ||
4 | * Copyright (C) 2002 by SOMA Networks, Inc. | ||
5 | * | ||
6 | * All rights reserved. | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or (at | ||
11 | * your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, but | ||
14 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
16 | * NON INFRINGEMENT. See the GNU General Public License for more | ||
17 | * details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License | ||
20 | * along with this program; if not, write to the Free Software | ||
21 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
22 | * | ||
23 | * Send feedback to <scottm@somanetworks.com> | ||
24 | */ | ||
25 | |||
26 | #include <linux/config.h> | ||
27 | #include <linux/module.h> | ||
28 | #include <linux/kernel.h> | ||
29 | #include <linux/pci.h> | ||
30 | #include <linux/proc_fs.h> | ||
31 | #include "../pci.h" | ||
32 | #include "pci_hotplug.h" | ||
33 | #include "cpci_hotplug.h" | ||
34 | |||
35 | #if !defined(MODULE) | ||
36 | #define MY_NAME "cpci_hotplug" | ||
37 | #else | ||
38 | #define MY_NAME THIS_MODULE->name | ||
39 | #endif | ||
40 | |||
41 | extern int cpci_debug; | ||
42 | |||
43 | #define dbg(format, arg...) \ | ||
44 | do { \ | ||
45 | if(cpci_debug) \ | ||
46 | printk (KERN_DEBUG "%s: " format "\n", \ | ||
47 | MY_NAME , ## arg); \ | ||
48 | } while(0) | ||
49 | #define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME , ## arg) | ||
50 | #define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME , ## arg) | ||
51 | #define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME , ## arg) | ||
52 | |||
53 | #define ROUND_UP(x, a) (((x) + (a) - 1) & ~((a) - 1)) | ||
54 | |||
55 | |||
56 | u8 cpci_get_attention_status(struct slot* slot) | ||
57 | { | ||
58 | int hs_cap; | ||
59 | u16 hs_csr; | ||
60 | |||
61 | hs_cap = pci_bus_find_capability(slot->bus, | ||
62 | slot->devfn, | ||
63 | PCI_CAP_ID_CHSWP); | ||
64 | if(!hs_cap) { | ||
65 | return 0; | ||
66 | } | ||
67 | |||
68 | if(pci_bus_read_config_word(slot->bus, | ||
69 | slot->devfn, | ||
70 | hs_cap + 2, | ||
71 | &hs_csr)) { | ||
72 | return 0; | ||
73 | } | ||
74 | return hs_csr & 0x0008 ? 1 : 0; | ||
75 | } | ||
76 | |||
77 | int cpci_set_attention_status(struct slot* slot, int status) | ||
78 | { | ||
79 | int hs_cap; | ||
80 | u16 hs_csr; | ||
81 | |||
82 | hs_cap = pci_bus_find_capability(slot->bus, | ||
83 | slot->devfn, | ||
84 | PCI_CAP_ID_CHSWP); | ||
85 | if(!hs_cap) { | ||
86 | return 0; | ||
87 | } | ||
88 | |||
89 | if(pci_bus_read_config_word(slot->bus, | ||
90 | slot->devfn, | ||
91 | hs_cap + 2, | ||
92 | &hs_csr)) { | ||
93 | return 0; | ||
94 | } | ||
95 | if(status) { | ||
96 | hs_csr |= HS_CSR_LOO; | ||
97 | } else { | ||
98 | hs_csr &= ~HS_CSR_LOO; | ||
99 | } | ||
100 | if(pci_bus_write_config_word(slot->bus, | ||
101 | slot->devfn, | ||
102 | hs_cap + 2, | ||
103 | hs_csr)) { | ||
104 | return 0; | ||
105 | } | ||
106 | return 1; | ||
107 | } | ||
108 | |||
109 | u16 cpci_get_hs_csr(struct slot* slot) | ||
110 | { | ||
111 | int hs_cap; | ||
112 | u16 hs_csr; | ||
113 | |||
114 | hs_cap = pci_bus_find_capability(slot->bus, | ||
115 | slot->devfn, | ||
116 | PCI_CAP_ID_CHSWP); | ||
117 | if(!hs_cap) { | ||
118 | return 0xFFFF; | ||
119 | } | ||
120 | |||
121 | if(pci_bus_read_config_word(slot->bus, | ||
122 | slot->devfn, | ||
123 | hs_cap + 2, | ||
124 | &hs_csr)) { | ||
125 | return 0xFFFF; | ||
126 | } | ||
127 | return hs_csr; | ||
128 | } | ||
129 | |||
130 | #if 0 | ||
131 | u16 cpci_set_hs_csr(struct slot* slot, u16 hs_csr) | ||
132 | { | ||
133 | int hs_cap; | ||
134 | u16 new_hs_csr; | ||
135 | |||
136 | hs_cap = pci_bus_find_capability(slot->bus, | ||
137 | slot->devfn, | ||
138 | PCI_CAP_ID_CHSWP); | ||
139 | if(!hs_cap) { | ||
140 | return 0xFFFF; | ||
141 | } | ||
142 | |||
143 | /* Write out the new value */ | ||
144 | if(pci_bus_write_config_word(slot->bus, | ||
145 | slot->devfn, | ||
146 | hs_cap + 2, | ||
147 | hs_csr)) { | ||
148 | return 0xFFFF; | ||
149 | } | ||
150 | |||
151 | /* Read back what we just wrote out */ | ||
152 | if(pci_bus_read_config_word(slot->bus, | ||
153 | slot->devfn, | ||
154 | hs_cap + 2, | ||
155 | &new_hs_csr)) { | ||
156 | return 0xFFFF; | ||
157 | } | ||
158 | return new_hs_csr; | ||
159 | } | ||
160 | #endif | ||
161 | |||
162 | int cpci_check_and_clear_ins(struct slot* slot) | ||
163 | { | ||
164 | int hs_cap; | ||
165 | u16 hs_csr; | ||
166 | int ins = 0; | ||
167 | |||
168 | hs_cap = pci_bus_find_capability(slot->bus, | ||
169 | slot->devfn, | ||
170 | PCI_CAP_ID_CHSWP); | ||
171 | if(!hs_cap) { | ||
172 | return 0; | ||
173 | } | ||
174 | if(pci_bus_read_config_word(slot->bus, | ||
175 | slot->devfn, | ||
176 | hs_cap + 2, | ||
177 | &hs_csr)) { | ||
178 | return 0; | ||
179 | } | ||
180 | if(hs_csr & HS_CSR_INS) { | ||
181 | /* Clear INS (by setting it) */ | ||
182 | if(pci_bus_write_config_word(slot->bus, | ||
183 | slot->devfn, | ||
184 | hs_cap + 2, | ||
185 | hs_csr)) { | ||
186 | ins = 0; | ||
187 | } | ||
188 | ins = 1; | ||
189 | } | ||
190 | return ins; | ||
191 | } | ||
192 | |||
193 | int cpci_check_ext(struct slot* slot) | ||
194 | { | ||
195 | int hs_cap; | ||
196 | u16 hs_csr; | ||
197 | int ext = 0; | ||
198 | |||
199 | hs_cap = pci_bus_find_capability(slot->bus, | ||
200 | slot->devfn, | ||
201 | PCI_CAP_ID_CHSWP); | ||
202 | if(!hs_cap) { | ||
203 | return 0; | ||
204 | } | ||
205 | if(pci_bus_read_config_word(slot->bus, | ||
206 | slot->devfn, | ||
207 | hs_cap + 2, | ||
208 | &hs_csr)) { | ||
209 | return 0; | ||
210 | } | ||
211 | if(hs_csr & HS_CSR_EXT) { | ||
212 | ext = 1; | ||
213 | } | ||
214 | return ext; | ||
215 | } | ||
216 | |||
217 | int cpci_clear_ext(struct slot* slot) | ||
218 | { | ||
219 | int hs_cap; | ||
220 | u16 hs_csr; | ||
221 | |||
222 | hs_cap = pci_bus_find_capability(slot->bus, | ||
223 | slot->devfn, | ||
224 | PCI_CAP_ID_CHSWP); | ||
225 | if(!hs_cap) { | ||
226 | return -ENODEV; | ||
227 | } | ||
228 | if(pci_bus_read_config_word(slot->bus, | ||
229 | slot->devfn, | ||
230 | hs_cap + 2, | ||
231 | &hs_csr)) { | ||
232 | return -ENODEV; | ||
233 | } | ||
234 | if(hs_csr & HS_CSR_EXT) { | ||
235 | /* Clear EXT (by setting it) */ | ||
236 | if(pci_bus_write_config_word(slot->bus, | ||
237 | slot->devfn, | ||
238 | hs_cap + 2, | ||
239 | hs_csr)) { | ||
240 | return -ENODEV; | ||
241 | } | ||
242 | } | ||
243 | return 0; | ||
244 | } | ||
245 | |||
246 | int cpci_led_on(struct slot* slot) | ||
247 | { | ||
248 | int hs_cap; | ||
249 | u16 hs_csr; | ||
250 | |||
251 | hs_cap = pci_bus_find_capability(slot->bus, | ||
252 | slot->devfn, | ||
253 | PCI_CAP_ID_CHSWP); | ||
254 | if(!hs_cap) { | ||
255 | return -ENODEV; | ||
256 | } | ||
257 | if(pci_bus_read_config_word(slot->bus, | ||
258 | slot->devfn, | ||
259 | hs_cap + 2, | ||
260 | &hs_csr)) { | ||
261 | return -ENODEV; | ||
262 | } | ||
263 | if((hs_csr & HS_CSR_LOO) != HS_CSR_LOO) { | ||
264 | /* Set LOO */ | ||
265 | hs_csr |= HS_CSR_LOO; | ||
266 | if(pci_bus_write_config_word(slot->bus, | ||
267 | slot->devfn, | ||
268 | hs_cap + 2, | ||
269 | hs_csr)) { | ||
270 | err("Could not set LOO for slot %s", | ||
271 | slot->hotplug_slot->name); | ||
272 | return -ENODEV; | ||
273 | } | ||
274 | } | ||
275 | return 0; | ||
276 | } | ||
277 | |||
278 | int cpci_led_off(struct slot* slot) | ||
279 | { | ||
280 | int hs_cap; | ||
281 | u16 hs_csr; | ||
282 | |||
283 | hs_cap = pci_bus_find_capability(slot->bus, | ||
284 | slot->devfn, | ||
285 | PCI_CAP_ID_CHSWP); | ||
286 | if(!hs_cap) { | ||
287 | return -ENODEV; | ||
288 | } | ||
289 | if(pci_bus_read_config_word(slot->bus, | ||
290 | slot->devfn, | ||
291 | hs_cap + 2, | ||
292 | &hs_csr)) { | ||
293 | return -ENODEV; | ||
294 | } | ||
295 | if(hs_csr & HS_CSR_LOO) { | ||
296 | /* Clear LOO */ | ||
297 | hs_csr &= ~HS_CSR_LOO; | ||
298 | if(pci_bus_write_config_word(slot->bus, | ||
299 | slot->devfn, | ||
300 | hs_cap + 2, | ||
301 | hs_csr)) { | ||
302 | err("Could not clear LOO for slot %s", | ||
303 | slot->hotplug_slot->name); | ||
304 | return -ENODEV; | ||
305 | } | ||
306 | } | ||
307 | return 0; | ||
308 | } | ||
309 | |||
310 | |||
311 | /* | ||
312 | * Device configuration functions | ||
313 | */ | ||
314 | |||
315 | static int cpci_configure_dev(struct pci_bus *bus, struct pci_dev *dev) | ||
316 | { | ||
317 | u8 irq_pin; | ||
318 | int r; | ||
319 | |||
320 | dbg("%s - enter", __FUNCTION__); | ||
321 | |||
322 | /* NOTE: device already setup from prior scan */ | ||
323 | |||
324 | /* FIXME: How would we know if we need to enable the expansion ROM? */ | ||
325 | pci_write_config_word(dev, PCI_ROM_ADDRESS, 0x00L); | ||
326 | |||
327 | /* Assign resources */ | ||
328 | dbg("assigning resources for %02x:%02x.%x", | ||
329 | dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); | ||
330 | for (r = 0; r < 6; r++) { | ||
331 | struct resource *res = dev->resource + r; | ||
332 | if(res->flags) | ||
333 | pci_assign_resource(dev, r); | ||
334 | } | ||
335 | dbg("finished assigning resources for %02x:%02x.%x", | ||
336 | dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); | ||
337 | |||
338 | /* Does this function have an interrupt at all? */ | ||
339 | dbg("checking for function interrupt"); | ||
340 | pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &irq_pin); | ||
341 | if(irq_pin) { | ||
342 | dbg("function uses interrupt pin %d", irq_pin); | ||
343 | } | ||
344 | |||
345 | /* | ||
346 | * Need to explicitly set irq field to 0 so that it'll get assigned | ||
347 | * by the pcibios platform dependent code called by pci_enable_device. | ||
348 | */ | ||
349 | dev->irq = 0; | ||
350 | |||
351 | dbg("enabling device"); | ||
352 | pci_enable_device(dev); /* XXX check return */ | ||
353 | dbg("now dev->irq = %d", dev->irq); | ||
354 | if(irq_pin && dev->irq) { | ||
355 | pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq); | ||
356 | } | ||
357 | |||
358 | /* Can't use pci_insert_device at the moment, do it manually for now */ | ||
359 | pci_proc_attach_device(dev); | ||
360 | dbg("notifying drivers"); | ||
361 | //pci_announce_device_to_drivers(dev); | ||
362 | dbg("%s - exit", __FUNCTION__); | ||
363 | return 0; | ||
364 | } | ||
365 | |||
366 | static int cpci_configure_bridge(struct pci_bus* bus, struct pci_dev* dev) | ||
367 | { | ||
368 | int rc; | ||
369 | struct pci_bus* child; | ||
370 | struct resource* r; | ||
371 | u8 max, n; | ||
372 | u16 command; | ||
373 | |||
374 | dbg("%s - enter", __FUNCTION__); | ||
375 | |||
376 | /* Do basic bridge initialization */ | ||
377 | rc = pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0x40); | ||
378 | if(rc) { | ||
379 | printk(KERN_ERR "%s - write of PCI_LATENCY_TIMER failed\n", __FUNCTION__); | ||
380 | } | ||
381 | rc = pci_write_config_byte(dev, PCI_SEC_LATENCY_TIMER, 0x40); | ||
382 | if(rc) { | ||
383 | printk(KERN_ERR "%s - write of PCI_SEC_LATENCY_TIMER failed\n", __FUNCTION__); | ||
384 | } | ||
385 | rc = pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, L1_CACHE_BYTES / 4); | ||
386 | if(rc) { | ||
387 | printk(KERN_ERR "%s - write of PCI_CACHE_LINE_SIZE failed\n", __FUNCTION__); | ||
388 | } | ||
389 | |||
390 | /* | ||
391 | * Set parent bridge's subordinate field so that configuration space | ||
392 | * access will work in pci_scan_bridge and friends. | ||
393 | */ | ||
394 | max = pci_max_busnr(); | ||
395 | bus->subordinate = max + 1; | ||
396 | pci_write_config_byte(bus->self, PCI_SUBORDINATE_BUS, max + 1); | ||
397 | |||
398 | /* Scan behind bridge */ | ||
399 | n = pci_scan_bridge(bus, dev, max, 2); | ||
400 | child = pci_find_bus(0, max + 1); | ||
401 | if (!child) | ||
402 | return -ENODEV; | ||
403 | pci_proc_attach_bus(child); | ||
404 | |||
405 | /* | ||
406 | * Update parent bridge's subordinate field if there were more bridges | ||
407 | * behind the bridge that was scanned. | ||
408 | */ | ||
409 | if(n > max) { | ||
410 | bus->subordinate = n; | ||
411 | pci_write_config_byte(bus->self, PCI_SUBORDINATE_BUS, n); | ||
412 | } | ||
413 | |||
414 | /* | ||
415 | * Update the bridge resources of the bridge to accommodate devices | ||
416 | * behind it. | ||
417 | */ | ||
418 | pci_bus_size_bridges(child); | ||
419 | pci_bus_assign_resources(child); | ||
420 | |||
421 | /* Enable resource mapping via command register */ | ||
422 | command = PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE | PCI_COMMAND_PARITY | PCI_COMMAND_SERR; | ||
423 | r = child->resource[0]; | ||
424 | if(r && r->start) { | ||
425 | command |= PCI_COMMAND_IO; | ||
426 | } | ||
427 | r = child->resource[1]; | ||
428 | if(r && r->start) { | ||
429 | command |= PCI_COMMAND_MEMORY; | ||
430 | } | ||
431 | r = child->resource[2]; | ||
432 | if(r && r->start) { | ||
433 | command |= PCI_COMMAND_MEMORY; | ||
434 | } | ||
435 | rc = pci_write_config_word(dev, PCI_COMMAND, command); | ||
436 | if(rc) { | ||
437 | err("Error setting command register"); | ||
438 | return rc; | ||
439 | } | ||
440 | |||
441 | /* Set bridge control register */ | ||
442 | command = PCI_BRIDGE_CTL_PARITY | PCI_BRIDGE_CTL_SERR | PCI_BRIDGE_CTL_NO_ISA; | ||
443 | rc = pci_write_config_word(dev, PCI_BRIDGE_CONTROL, command); | ||
444 | if(rc) { | ||
445 | err("Error setting bridge control register"); | ||
446 | return rc; | ||
447 | } | ||
448 | dbg("%s - exit", __FUNCTION__); | ||
449 | return 0; | ||
450 | } | ||
451 | |||
452 | static int configure_visit_pci_dev(struct pci_dev_wrapped *wrapped_dev, | ||
453 | struct pci_bus_wrapped *wrapped_bus) | ||
454 | { | ||
455 | int rc; | ||
456 | struct pci_dev *dev = wrapped_dev->dev; | ||
457 | struct pci_bus *bus = wrapped_bus->bus; | ||
458 | struct slot* slot; | ||
459 | |||
460 | dbg("%s - enter", __FUNCTION__); | ||
461 | |||
462 | /* | ||
463 | * We need to fix up the hotplug representation with the Linux | ||
464 | * representation. | ||
465 | */ | ||
466 | if(wrapped_dev->data) { | ||
467 | slot = (struct slot*) wrapped_dev->data; | ||
468 | slot->dev = dev; | ||
469 | } | ||
470 | |||
471 | /* If it's a bridge, scan behind it for devices */ | ||
472 | if(dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) { | ||
473 | rc = cpci_configure_bridge(bus, dev); | ||
474 | if(rc) | ||
475 | return rc; | ||
476 | } | ||
477 | |||
478 | /* Actually configure device */ | ||
479 | if(dev) { | ||
480 | rc = cpci_configure_dev(bus, dev); | ||
481 | if(rc) | ||
482 | return rc; | ||
483 | } | ||
484 | dbg("%s - exit", __FUNCTION__); | ||
485 | return 0; | ||
486 | } | ||
487 | |||
488 | static int unconfigure_visit_pci_dev_phase2(struct pci_dev_wrapped *wrapped_dev, | ||
489 | struct pci_bus_wrapped *wrapped_bus) | ||
490 | { | ||
491 | struct pci_dev *dev = wrapped_dev->dev; | ||
492 | struct slot* slot; | ||
493 | |||
494 | dbg("%s - enter", __FUNCTION__); | ||
495 | if(!dev) | ||
496 | return -ENODEV; | ||
497 | |||
498 | /* Remove the Linux representation */ | ||
499 | if(pci_remove_device_safe(dev)) { | ||
500 | err("Could not remove device\n"); | ||
501 | return -1; | ||
502 | } | ||
503 | |||
504 | /* | ||
505 | * Now remove the hotplug representation. | ||
506 | */ | ||
507 | if(wrapped_dev->data) { | ||
508 | slot = (struct slot*) wrapped_dev->data; | ||
509 | slot->dev = NULL; | ||
510 | } else { | ||
511 | dbg("No hotplug representation for %02x:%02x.%x", | ||
512 | dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); | ||
513 | } | ||
514 | dbg("%s - exit", __FUNCTION__); | ||
515 | return 0; | ||
516 | } | ||
517 | |||
518 | static int unconfigure_visit_pci_bus_phase2(struct pci_bus_wrapped *wrapped_bus, | ||
519 | struct pci_dev_wrapped *wrapped_dev) | ||
520 | { | ||
521 | struct pci_bus *bus = wrapped_bus->bus; | ||
522 | struct pci_bus *parent = bus->self->bus; | ||
523 | |||
524 | dbg("%s - enter", __FUNCTION__); | ||
525 | |||
526 | /* The cleanup code for proc entries regarding buses should be in the kernel... */ | ||
527 | if(bus->procdir) | ||
528 | dbg("detach_pci_bus %s", bus->procdir->name); | ||
529 | pci_proc_detach_bus(bus); | ||
530 | |||
531 | /* The cleanup code should live in the kernel... */ | ||
532 | bus->self->subordinate = NULL; | ||
533 | |||
534 | /* unlink from parent bus */ | ||
535 | list_del(&bus->node); | ||
536 | |||
537 | /* Now, remove */ | ||
538 | if(bus) | ||
539 | kfree(bus); | ||
540 | |||
541 | /* Update parent's subordinate field */ | ||
542 | if(parent) { | ||
543 | u8 n = pci_bus_max_busnr(parent); | ||
544 | if(n < parent->subordinate) { | ||
545 | parent->subordinate = n; | ||
546 | pci_write_config_byte(parent->self, PCI_SUBORDINATE_BUS, n); | ||
547 | } | ||
548 | } | ||
549 | dbg("%s - exit", __FUNCTION__); | ||
550 | return 0; | ||
551 | } | ||
552 | |||
553 | static struct pci_visit configure_functions = { | ||
554 | .visit_pci_dev = configure_visit_pci_dev, | ||
555 | }; | ||
556 | |||
557 | static struct pci_visit unconfigure_functions_phase2 = { | ||
558 | .post_visit_pci_bus = unconfigure_visit_pci_bus_phase2, | ||
559 | .post_visit_pci_dev = unconfigure_visit_pci_dev_phase2 | ||
560 | }; | ||
561 | |||
562 | |||
563 | int cpci_configure_slot(struct slot* slot) | ||
564 | { | ||
565 | int rc = 0; | ||
566 | |||
567 | dbg("%s - enter", __FUNCTION__); | ||
568 | |||
569 | if(slot->dev == NULL) { | ||
570 | dbg("pci_dev null, finding %02x:%02x:%x", | ||
571 | slot->bus->number, PCI_SLOT(slot->devfn), PCI_FUNC(slot->devfn)); | ||
572 | slot->dev = pci_find_slot(slot->bus->number, slot->devfn); | ||
573 | } | ||
574 | |||
575 | /* Still NULL? Well then scan for it! */ | ||
576 | if(slot->dev == NULL) { | ||
577 | int n; | ||
578 | dbg("pci_dev still null"); | ||
579 | |||
580 | /* | ||
581 | * This will generate pci_dev structures for all functions, but | ||
582 | * we will only call this case when lookup fails. | ||
583 | */ | ||
584 | n = pci_scan_slot(slot->bus, slot->devfn); | ||
585 | dbg("%s: pci_scan_slot returned %d", __FUNCTION__, n); | ||
586 | if(n > 0) | ||
587 | pci_bus_add_devices(slot->bus); | ||
588 | slot->dev = pci_find_slot(slot->bus->number, slot->devfn); | ||
589 | if(slot->dev == NULL) { | ||
590 | err("Could not find PCI device for slot %02x", slot->number); | ||
591 | return 0; | ||
592 | } | ||
593 | } | ||
594 | dbg("slot->dev = %p", slot->dev); | ||
595 | if(slot->dev) { | ||
596 | struct pci_dev *dev; | ||
597 | struct pci_dev_wrapped wrapped_dev; | ||
598 | struct pci_bus_wrapped wrapped_bus; | ||
599 | int i; | ||
600 | |||
601 | memset(&wrapped_dev, 0, sizeof (struct pci_dev_wrapped)); | ||
602 | memset(&wrapped_bus, 0, sizeof (struct pci_bus_wrapped)); | ||
603 | |||
604 | for (i = 0; i < 8; i++) { | ||
605 | dev = pci_find_slot(slot->bus->number, | ||
606 | PCI_DEVFN(PCI_SLOT(slot->dev->devfn), i)); | ||
607 | if(!dev) | ||
608 | continue; | ||
609 | wrapped_dev.dev = dev; | ||
610 | wrapped_bus.bus = slot->dev->bus; | ||
611 | if(i) | ||
612 | wrapped_dev.data = NULL; | ||
613 | else | ||
614 | wrapped_dev.data = (void*) slot; | ||
615 | rc = pci_visit_dev(&configure_functions, &wrapped_dev, &wrapped_bus); | ||
616 | } | ||
617 | } | ||
618 | |||
619 | dbg("%s - exit, rc = %d", __FUNCTION__, rc); | ||
620 | return rc; | ||
621 | } | ||
622 | |||
623 | int cpci_unconfigure_slot(struct slot* slot) | ||
624 | { | ||
625 | int rc = 0; | ||
626 | int i; | ||
627 | struct pci_dev_wrapped wrapped_dev; | ||
628 | struct pci_bus_wrapped wrapped_bus; | ||
629 | struct pci_dev *dev; | ||
630 | |||
631 | dbg("%s - enter", __FUNCTION__); | ||
632 | |||
633 | if(!slot->dev) { | ||
634 | err("No device for slot %02x\n", slot->number); | ||
635 | return -ENODEV; | ||
636 | } | ||
637 | |||
638 | memset(&wrapped_dev, 0, sizeof (struct pci_dev_wrapped)); | ||
639 | memset(&wrapped_bus, 0, sizeof (struct pci_bus_wrapped)); | ||
640 | |||
641 | for (i = 0; i < 8; i++) { | ||
642 | dev = pci_find_slot(slot->bus->number, | ||
643 | PCI_DEVFN(PCI_SLOT(slot->devfn), i)); | ||
644 | if(dev) { | ||
645 | wrapped_dev.dev = dev; | ||
646 | wrapped_bus.bus = dev->bus; | ||
647 | if(i) | ||
648 | wrapped_dev.data = NULL; | ||
649 | else | ||
650 | wrapped_dev.data = (void*) slot; | ||
651 | dbg("%s - unconfigure phase 2", __FUNCTION__); | ||
652 | rc = pci_visit_dev(&unconfigure_functions_phase2, | ||
653 | &wrapped_dev, | ||
654 | &wrapped_bus); | ||
655 | if(rc) | ||
656 | break; | ||
657 | } | ||
658 | } | ||
659 | dbg("%s - exit, rc = %d", __FUNCTION__, rc); | ||
660 | return rc; | ||
661 | } | ||