diff options
Diffstat (limited to 'drivers/pci')
-rw-r--r-- | drivers/pci/hotplug/cpci_hotplug.h | 2 | ||||
-rw-r--r-- | drivers/pci/hotplug/cpci_hotplug_core.c | 169 | ||||
-rw-r--r-- | drivers/pci/hotplug/cpci_hotplug_pci.c | 352 |
3 files changed, 112 insertions, 411 deletions
diff --git a/drivers/pci/hotplug/cpci_hotplug.h b/drivers/pci/hotplug/cpci_hotplug.h index 3ddd75937a40..d9769b30be9a 100644 --- a/drivers/pci/hotplug/cpci_hotplug.h +++ b/drivers/pci/hotplug/cpci_hotplug.h | |||
@@ -31,7 +31,7 @@ | |||
31 | #include <linux/types.h> | 31 | #include <linux/types.h> |
32 | #include <linux/pci.h> | 32 | #include <linux/pci.h> |
33 | 33 | ||
34 | /* PICMG 2.12 R2.0 HS CSR bits: */ | 34 | /* PICMG 2.1 R2.0 HS CSR bits: */ |
35 | #define HS_CSR_INS 0x0080 | 35 | #define HS_CSR_INS 0x0080 |
36 | #define HS_CSR_EXT 0x0040 | 36 | #define HS_CSR_EXT 0x0040 |
37 | #define HS_CSR_PI 0x0030 | 37 | #define HS_CSR_PI 0x0030 |
diff --git a/drivers/pci/hotplug/cpci_hotplug_core.c b/drivers/pci/hotplug/cpci_hotplug_core.c index ed243605dc7b..9e9dab7fe86a 100644 --- a/drivers/pci/hotplug/cpci_hotplug_core.c +++ b/drivers/pci/hotplug/cpci_hotplug_core.c | |||
@@ -33,11 +33,11 @@ | |||
33 | #include <linux/init.h> | 33 | #include <linux/init.h> |
34 | #include <linux/interrupt.h> | 34 | #include <linux/interrupt.h> |
35 | #include <linux/smp_lock.h> | 35 | #include <linux/smp_lock.h> |
36 | #include <asm/atomic.h> | ||
36 | #include <linux/delay.h> | 37 | #include <linux/delay.h> |
37 | #include "pci_hotplug.h" | 38 | #include "pci_hotplug.h" |
38 | #include "cpci_hotplug.h" | 39 | #include "cpci_hotplug.h" |
39 | 40 | ||
40 | #define DRIVER_VERSION "0.2" | ||
41 | #define DRIVER_AUTHOR "Scott Murray <scottm@somanetworks.com>" | 41 | #define DRIVER_AUTHOR "Scott Murray <scottm@somanetworks.com>" |
42 | #define DRIVER_DESC "CompactPCI Hot Plug Core" | 42 | #define DRIVER_DESC "CompactPCI Hot Plug Core" |
43 | 43 | ||
@@ -54,9 +54,10 @@ | |||
54 | #define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME , ## arg) | 54 | #define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME , ## arg) |
55 | 55 | ||
56 | /* local variables */ | 56 | /* local variables */ |
57 | static spinlock_t list_lock; | 57 | static DECLARE_RWSEM(list_rwsem); |
58 | static LIST_HEAD(slot_list); | 58 | static LIST_HEAD(slot_list); |
59 | static int slots; | 59 | static int slots; |
60 | static atomic_t extracting; | ||
60 | int cpci_debug; | 61 | int cpci_debug; |
61 | static struct cpci_hp_controller *controller; | 62 | static struct cpci_hp_controller *controller; |
62 | static struct semaphore event_semaphore; /* mutex for process loop (up if something to process) */ | 63 | static struct semaphore event_semaphore; /* mutex for process loop (up if something to process) */ |
@@ -68,6 +69,8 @@ static int disable_slot(struct hotplug_slot *slot); | |||
68 | static int set_attention_status(struct hotplug_slot *slot, u8 value); | 69 | static int set_attention_status(struct hotplug_slot *slot, u8 value); |
69 | static int get_power_status(struct hotplug_slot *slot, u8 * value); | 70 | static int get_power_status(struct hotplug_slot *slot, u8 * value); |
70 | static int get_attention_status(struct hotplug_slot *slot, u8 * value); | 71 | static int get_attention_status(struct hotplug_slot *slot, u8 * value); |
72 | static int get_adapter_status(struct hotplug_slot *slot, u8 * value); | ||
73 | static int get_latch_status(struct hotplug_slot *slot, u8 * value); | ||
71 | 74 | ||
72 | static struct hotplug_slot_ops cpci_hotplug_slot_ops = { | 75 | static struct hotplug_slot_ops cpci_hotplug_slot_ops = { |
73 | .owner = THIS_MODULE, | 76 | .owner = THIS_MODULE, |
@@ -76,6 +79,8 @@ static struct hotplug_slot_ops cpci_hotplug_slot_ops = { | |||
76 | .set_attention_status = set_attention_status, | 79 | .set_attention_status = set_attention_status, |
77 | .get_power_status = get_power_status, | 80 | .get_power_status = get_power_status, |
78 | .get_attention_status = get_attention_status, | 81 | .get_attention_status = get_attention_status, |
82 | .get_adapter_status = get_adapter_status, | ||
83 | .get_latch_status = get_latch_status, | ||
79 | }; | 84 | }; |
80 | 85 | ||
81 | static int | 86 | static int |
@@ -148,8 +153,10 @@ disable_slot(struct hotplug_slot *hotplug_slot) | |||
148 | warn("failure to update adapter file"); | 153 | warn("failure to update adapter file"); |
149 | } | 154 | } |
150 | 155 | ||
151 | slot->extracting = 0; | 156 | if(slot->extracting) { |
152 | 157 | slot->extracting = 0; | |
158 | atomic_dec(&extracting); | ||
159 | } | ||
153 | return retval; | 160 | return retval; |
154 | } | 161 | } |
155 | 162 | ||
@@ -188,6 +195,20 @@ set_attention_status(struct hotplug_slot *hotplug_slot, u8 status) | |||
188 | return cpci_set_attention_status(hotplug_slot->private, status); | 195 | return cpci_set_attention_status(hotplug_slot->private, status); |
189 | } | 196 | } |
190 | 197 | ||
198 | static int | ||
199 | get_adapter_status(struct hotplug_slot *hotplug_slot, u8 * value) | ||
200 | { | ||
201 | *value = hotplug_slot->info->adapter_status; | ||
202 | return 0; | ||
203 | } | ||
204 | |||
205 | static int | ||
206 | get_latch_status(struct hotplug_slot *hotplug_slot, u8 * value) | ||
207 | { | ||
208 | *value = hotplug_slot->info->latch_status; | ||
209 | return 0; | ||
210 | } | ||
211 | |||
191 | static void release_slot(struct hotplug_slot *hotplug_slot) | 212 | static void release_slot(struct hotplug_slot *hotplug_slot) |
192 | { | 213 | { |
193 | struct slot *slot = hotplug_slot->private; | 214 | struct slot *slot = hotplug_slot->private; |
@@ -273,10 +294,10 @@ cpci_hp_register_bus(struct pci_bus *bus, u8 first, u8 last) | |||
273 | } | 294 | } |
274 | 295 | ||
275 | /* Add slot to our internal list */ | 296 | /* Add slot to our internal list */ |
276 | spin_lock(&list_lock); | 297 | down_write(&list_rwsem); |
277 | list_add(&slot->slot_list, &slot_list); | 298 | list_add(&slot->slot_list, &slot_list); |
278 | slots++; | 299 | slots++; |
279 | spin_unlock(&list_lock); | 300 | up_write(&list_rwsem); |
280 | } | 301 | } |
281 | return 0; | 302 | return 0; |
282 | error_name: | 303 | error_name: |
@@ -299,9 +320,9 @@ cpci_hp_unregister_bus(struct pci_bus *bus) | |||
299 | struct list_head *next; | 320 | struct list_head *next; |
300 | int status; | 321 | int status; |
301 | 322 | ||
302 | spin_lock(&list_lock); | 323 | down_write(&list_rwsem); |
303 | if(!slots) { | 324 | if(!slots) { |
304 | spin_unlock(&list_lock); | 325 | up_write(&list_rwsem); |
305 | return -1; | 326 | return -1; |
306 | } | 327 | } |
307 | list_for_each_safe(tmp, next, &slot_list) { | 328 | list_for_each_safe(tmp, next, &slot_list) { |
@@ -319,7 +340,7 @@ cpci_hp_unregister_bus(struct pci_bus *bus) | |||
319 | slots--; | 340 | slots--; |
320 | } | 341 | } |
321 | } | 342 | } |
322 | spin_unlock(&list_lock); | 343 | up_write(&list_rwsem); |
323 | return 0; | 344 | return 0; |
324 | } | 345 | } |
325 | 346 | ||
@@ -347,7 +368,7 @@ cpci_hp_intr(int irq, void *data, struct pt_regs *regs) | |||
347 | } | 368 | } |
348 | 369 | ||
349 | /* | 370 | /* |
350 | * According to PICMG 2.12 R2.0, section 6.3.2, upon | 371 | * According to PICMG 2.1 R2.0, section 6.3.2, upon |
351 | * initialization, the system driver shall clear the | 372 | * initialization, the system driver shall clear the |
352 | * INS bits of the cold-inserted devices. | 373 | * INS bits of the cold-inserted devices. |
353 | */ | 374 | */ |
@@ -359,9 +380,9 @@ init_slots(void) | |||
359 | struct pci_dev* dev; | 380 | struct pci_dev* dev; |
360 | 381 | ||
361 | dbg("%s - enter", __FUNCTION__); | 382 | dbg("%s - enter", __FUNCTION__); |
362 | spin_lock(&list_lock); | 383 | down_read(&list_rwsem); |
363 | if(!slots) { | 384 | if(!slots) { |
364 | spin_unlock(&list_lock); | 385 | up_read(&list_rwsem); |
365 | return -1; | 386 | return -1; |
366 | } | 387 | } |
367 | list_for_each(tmp, &slot_list) { | 388 | list_for_each(tmp, &slot_list) { |
@@ -386,7 +407,7 @@ init_slots(void) | |||
386 | } | 407 | } |
387 | } | 408 | } |
388 | } | 409 | } |
389 | spin_unlock(&list_lock); | 410 | up_read(&list_rwsem); |
390 | dbg("%s - exit", __FUNCTION__); | 411 | dbg("%s - exit", __FUNCTION__); |
391 | return 0; | 412 | return 0; |
392 | } | 413 | } |
@@ -398,10 +419,11 @@ check_slots(void) | |||
398 | struct list_head *tmp; | 419 | struct list_head *tmp; |
399 | int extracted; | 420 | int extracted; |
400 | int inserted; | 421 | int inserted; |
422 | u16 hs_csr; | ||
401 | 423 | ||
402 | spin_lock(&list_lock); | 424 | down_read(&list_rwsem); |
403 | if(!slots) { | 425 | if(!slots) { |
404 | spin_unlock(&list_lock); | 426 | up_read(&list_rwsem); |
405 | err("no slots registered, shutting down"); | 427 | err("no slots registered, shutting down"); |
406 | return -1; | 428 | return -1; |
407 | } | 429 | } |
@@ -411,8 +433,6 @@ check_slots(void) | |||
411 | dbg("%s - looking at slot %s", | 433 | dbg("%s - looking at slot %s", |
412 | __FUNCTION__, slot->hotplug_slot->name); | 434 | __FUNCTION__, slot->hotplug_slot->name); |
413 | if(cpci_check_and_clear_ins(slot)) { | 435 | if(cpci_check_and_clear_ins(slot)) { |
414 | u16 hs_csr; | ||
415 | |||
416 | /* Some broken hardware (e.g. PLX 9054AB) asserts ENUM# twice... */ | 436 | /* Some broken hardware (e.g. PLX 9054AB) asserts ENUM# twice... */ |
417 | if(slot->dev) { | 437 | if(slot->dev) { |
418 | warn("slot %s already inserted", slot->hotplug_slot->name); | 438 | warn("slot %s already inserted", slot->hotplug_slot->name); |
@@ -462,8 +482,6 @@ check_slots(void) | |||
462 | 482 | ||
463 | inserted++; | 483 | inserted++; |
464 | } else if(cpci_check_ext(slot)) { | 484 | } else if(cpci_check_ext(slot)) { |
465 | u16 hs_csr; | ||
466 | |||
467 | /* Process extraction request */ | 485 | /* Process extraction request */ |
468 | dbg("%s - slot %s extracted", | 486 | dbg("%s - slot %s extracted", |
469 | __FUNCTION__, slot->hotplug_slot->name); | 487 | __FUNCTION__, slot->hotplug_slot->name); |
@@ -476,20 +494,40 @@ check_slots(void) | |||
476 | if(!slot->extracting) { | 494 | if(!slot->extracting) { |
477 | if(update_latch_status(slot->hotplug_slot, 0)) { | 495 | if(update_latch_status(slot->hotplug_slot, 0)) { |
478 | warn("failure to update latch file"); | 496 | warn("failure to update latch file"); |
497 | |||
479 | } | 498 | } |
499 | atomic_inc(&extracting); | ||
480 | slot->extracting = 1; | 500 | slot->extracting = 1; |
481 | } | 501 | } |
482 | extracted++; | 502 | extracted++; |
503 | } else if(slot->extracting) { | ||
504 | hs_csr = cpci_get_hs_csr(slot); | ||
505 | if(hs_csr == 0xffff) { | ||
506 | /* | ||
507 | * Hmmm, we're likely hosed at this point, should we | ||
508 | * bother trying to tell the driver or not? | ||
509 | */ | ||
510 | err("card in slot %s was improperly removed", | ||
511 | slot->hotplug_slot->name); | ||
512 | if(update_adapter_status(slot->hotplug_slot, 0)) { | ||
513 | warn("failure to update adapter file"); | ||
514 | } | ||
515 | slot->extracting = 0; | ||
516 | atomic_dec(&extracting); | ||
517 | } | ||
483 | } | 518 | } |
484 | } | 519 | } |
485 | spin_unlock(&list_lock); | 520 | up_read(&list_rwsem); |
521 | dbg("inserted=%d, extracted=%d, extracting=%d", | ||
522 | inserted, extracted, atomic_read(&extracting)); | ||
486 | if(inserted || extracted) { | 523 | if(inserted || extracted) { |
487 | return extracted; | 524 | return extracted; |
488 | } | 525 | } |
489 | else { | 526 | else if(!atomic_read(&extracting)) { |
490 | err("cannot find ENUM# source, shutting down"); | 527 | err("cannot find ENUM# source, shutting down"); |
491 | return -1; | 528 | return -1; |
492 | } | 529 | } |
530 | return 0; | ||
493 | } | 531 | } |
494 | 532 | ||
495 | /* This is the interrupt mode worker thread body */ | 533 | /* This is the interrupt mode worker thread body */ |
@@ -497,8 +535,6 @@ static int | |||
497 | event_thread(void *data) | 535 | event_thread(void *data) |
498 | { | 536 | { |
499 | int rc; | 537 | int rc; |
500 | struct slot *slot; | ||
501 | struct list_head *tmp; | ||
502 | 538 | ||
503 | lock_kernel(); | 539 | lock_kernel(); |
504 | daemonize("cpci_hp_eventd"); | 540 | daemonize("cpci_hp_eventd"); |
@@ -512,39 +548,22 @@ event_thread(void *data) | |||
512 | thread_finished); | 548 | thread_finished); |
513 | if(thread_finished || signal_pending(current)) | 549 | if(thread_finished || signal_pending(current)) |
514 | break; | 550 | break; |
515 | while(controller->ops->query_enum()) { | 551 | do { |
516 | rc = check_slots(); | 552 | rc = check_slots(); |
517 | if (rc > 0) | 553 | if (rc > 0) { |
518 | /* Give userspace a chance to handle extraction */ | 554 | /* Give userspace a chance to handle extraction */ |
519 | msleep(500); | 555 | msleep(500); |
520 | else if (rc < 0) { | 556 | } else if (rc < 0) { |
521 | dbg("%s - error checking slots", __FUNCTION__); | 557 | dbg("%s - error checking slots", __FUNCTION__); |
522 | thread_finished = 1; | 558 | thread_finished = 1; |
523 | break; | 559 | break; |
524 | } | 560 | } |
525 | } | 561 | } while(atomic_read(&extracting) != 0); |
526 | /* Check for someone yanking out a board */ | ||
527 | list_for_each(tmp, &slot_list) { | ||
528 | slot = list_entry(tmp, struct slot, slot_list); | ||
529 | if(slot->extracting) { | ||
530 | /* | ||
531 | * Hmmm, we're likely hosed at this point, should we | ||
532 | * bother trying to tell the driver or not? | ||
533 | */ | ||
534 | err("card in slot %s was improperly removed", | ||
535 | slot->hotplug_slot->name); | ||
536 | if(update_adapter_status(slot->hotplug_slot, 0)) { | ||
537 | warn("failure to update adapter file"); | ||
538 | } | ||
539 | slot->extracting = 0; | ||
540 | } | ||
541 | } | ||
542 | 562 | ||
543 | /* Re-enable ENUM# interrupt */ | 563 | /* Re-enable ENUM# interrupt */ |
544 | dbg("%s - re-enabling irq", __FUNCTION__); | 564 | dbg("%s - re-enabling irq", __FUNCTION__); |
545 | controller->ops->enable_irq(); | 565 | controller->ops->enable_irq(); |
546 | } | 566 | } |
547 | |||
548 | dbg("%s - event thread signals exit", __FUNCTION__); | 567 | dbg("%s - event thread signals exit", __FUNCTION__); |
549 | up(&thread_exit); | 568 | up(&thread_exit); |
550 | return 0; | 569 | return 0; |
@@ -555,8 +574,6 @@ static int | |||
555 | poll_thread(void *data) | 574 | poll_thread(void *data) |
556 | { | 575 | { |
557 | int rc; | 576 | int rc; |
558 | struct slot *slot; | ||
559 | struct list_head *tmp; | ||
560 | 577 | ||
561 | lock_kernel(); | 578 | lock_kernel(); |
562 | daemonize("cpci_hp_polld"); | 579 | daemonize("cpci_hp_polld"); |
@@ -565,35 +582,19 @@ poll_thread(void *data) | |||
565 | while(1) { | 582 | while(1) { |
566 | if(thread_finished || signal_pending(current)) | 583 | if(thread_finished || signal_pending(current)) |
567 | break; | 584 | break; |
568 | 585 | if(controller->ops->query_enum()) { | |
569 | while(controller->ops->query_enum()) { | 586 | do { |
570 | rc = check_slots(); | 587 | rc = check_slots(); |
571 | if(rc > 0) | 588 | if(rc > 0) { |
572 | /* Give userspace a chance to handle extraction */ | 589 | /* Give userspace a chance to handle extraction */ |
573 | msleep(500); | 590 | msleep(500); |
574 | else if (rc < 0) { | 591 | } else if(rc < 0) { |
575 | dbg("%s - error checking slots", __FUNCTION__); | 592 | dbg("%s - error checking slots", __FUNCTION__); |
576 | thread_finished = 1; | 593 | thread_finished = 1; |
577 | break; | 594 | break; |
578 | } | ||
579 | } | ||
580 | /* Check for someone yanking out a board */ | ||
581 | list_for_each(tmp, &slot_list) { | ||
582 | slot = list_entry(tmp, struct slot, slot_list); | ||
583 | if(slot->extracting) { | ||
584 | /* | ||
585 | * Hmmm, we're likely hosed at this point, should we | ||
586 | * bother trying to tell the driver or not? | ||
587 | */ | ||
588 | err("card in slot %s was improperly removed", | ||
589 | slot->hotplug_slot->name); | ||
590 | if(update_adapter_status(slot->hotplug_slot, 0)) { | ||
591 | warn("failure to update adapter file"); | ||
592 | } | 595 | } |
593 | slot->extracting = 0; | 596 | } while(atomic_read(&extracting) != 0); |
594 | } | ||
595 | } | 597 | } |
596 | |||
597 | msleep(100); | 598 | msleep(100); |
598 | } | 599 | } |
599 | dbg("poll thread signals exit"); | 600 | dbg("poll thread signals exit"); |
@@ -667,6 +668,9 @@ cpci_hp_unregister_controller(struct cpci_hp_controller *old_controller) | |||
667 | int status = 0; | 668 | int status = 0; |
668 | 669 | ||
669 | if(controller) { | 670 | if(controller) { |
671 | if(atomic_read(&extracting) != 0) { | ||
672 | return -EBUSY; | ||
673 | } | ||
670 | if(!thread_finished) { | 674 | if(!thread_finished) { |
671 | cpci_stop_thread(); | 675 | cpci_stop_thread(); |
672 | } | 676 | } |
@@ -691,12 +695,12 @@ cpci_hp_start(void) | |||
691 | return -ENODEV; | 695 | return -ENODEV; |
692 | } | 696 | } |
693 | 697 | ||
694 | spin_lock(&list_lock); | 698 | down_read(&list_rwsem); |
695 | if(!slots) { | 699 | if(list_empty(&slot_list)) { |
696 | spin_unlock(&list_lock); | 700 | up_read(&list_rwsem); |
697 | return -ENODEV; | 701 | return -ENODEV; |
698 | } | 702 | } |
699 | spin_unlock(&list_lock); | 703 | up_read(&list_rwsem); |
700 | 704 | ||
701 | if(first) { | 705 | if(first) { |
702 | status = init_slots(); | 706 | status = init_slots(); |
@@ -727,7 +731,9 @@ cpci_hp_stop(void) | |||
727 | if(!controller) { | 731 | if(!controller) { |
728 | return -ENODEV; | 732 | return -ENODEV; |
729 | } | 733 | } |
730 | 734 | if(atomic_read(&extracting) != 0) { | |
735 | return -EBUSY; | ||
736 | } | ||
731 | if(controller->irq) { | 737 | if(controller->irq) { |
732 | /* Stop enum interrupt processing */ | 738 | /* Stop enum interrupt processing */ |
733 | dbg("%s - disabling irq", __FUNCTION__); | 739 | dbg("%s - disabling irq", __FUNCTION__); |
@@ -747,7 +753,7 @@ cleanup_slots(void) | |||
747 | * Unregister all of our slots with the pci_hotplug subsystem, | 753 | * Unregister all of our slots with the pci_hotplug subsystem, |
748 | * and free up all memory that we had allocated. | 754 | * and free up all memory that we had allocated. |
749 | */ | 755 | */ |
750 | spin_lock(&list_lock); | 756 | down_write(&list_rwsem); |
751 | if(!slots) { | 757 | if(!slots) { |
752 | goto null_cleanup; | 758 | goto null_cleanup; |
753 | } | 759 | } |
@@ -761,17 +767,14 @@ cleanup_slots(void) | |||
761 | kfree(slot); | 767 | kfree(slot); |
762 | } | 768 | } |
763 | null_cleanup: | 769 | null_cleanup: |
764 | spin_unlock(&list_lock); | 770 | up_write(&list_rwsem); |
765 | return; | 771 | return; |
766 | } | 772 | } |
767 | 773 | ||
768 | int __init | 774 | int __init |
769 | cpci_hotplug_init(int debug) | 775 | cpci_hotplug_init(int debug) |
770 | { | 776 | { |
771 | spin_lock_init(&list_lock); | ||
772 | cpci_debug = debug; | 777 | cpci_debug = debug; |
773 | |||
774 | info(DRIVER_DESC " version: " DRIVER_VERSION); | ||
775 | return 0; | 778 | return 0; |
776 | } | 779 | } |
777 | 780 | ||
diff --git a/drivers/pci/hotplug/cpci_hotplug_pci.c b/drivers/pci/hotplug/cpci_hotplug_pci.c index 2e969616f298..69eb4fc54f2f 100644 --- a/drivers/pci/hotplug/cpci_hotplug_pci.c +++ b/drivers/pci/hotplug/cpci_hotplug_pci.c | |||
@@ -32,11 +32,7 @@ | |||
32 | #include "pci_hotplug.h" | 32 | #include "pci_hotplug.h" |
33 | #include "cpci_hotplug.h" | 33 | #include "cpci_hotplug.h" |
34 | 34 | ||
35 | #if !defined(MODULE) | ||
36 | #define MY_NAME "cpci_hotplug" | 35 | #define MY_NAME "cpci_hotplug" |
37 | #else | ||
38 | #define MY_NAME THIS_MODULE->name | ||
39 | #endif | ||
40 | 36 | ||
41 | extern int cpci_debug; | 37 | extern int cpci_debug; |
42 | 38 | ||
@@ -127,38 +123,6 @@ u16 cpci_get_hs_csr(struct slot* slot) | |||
127 | return hs_csr; | 123 | return hs_csr; |
128 | } | 124 | } |
129 | 125 | ||
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) | 126 | int cpci_check_and_clear_ins(struct slot* slot) |
163 | { | 127 | { |
164 | int hs_cap; | 128 | int hs_cap; |
@@ -261,7 +225,6 @@ int cpci_led_on(struct slot* slot) | |||
261 | return -ENODEV; | 225 | return -ENODEV; |
262 | } | 226 | } |
263 | if((hs_csr & HS_CSR_LOO) != HS_CSR_LOO) { | 227 | if((hs_csr & HS_CSR_LOO) != HS_CSR_LOO) { |
264 | /* Set LOO */ | ||
265 | hs_csr |= HS_CSR_LOO; | 228 | hs_csr |= HS_CSR_LOO; |
266 | if(pci_bus_write_config_word(slot->bus, | 229 | if(pci_bus_write_config_word(slot->bus, |
267 | slot->devfn, | 230 | slot->devfn, |
@@ -293,7 +256,6 @@ int cpci_led_off(struct slot* slot) | |||
293 | return -ENODEV; | 256 | return -ENODEV; |
294 | } | 257 | } |
295 | if(hs_csr & HS_CSR_LOO) { | 258 | if(hs_csr & HS_CSR_LOO) { |
296 | /* Clear LOO */ | ||
297 | hs_csr &= ~HS_CSR_LOO; | 259 | hs_csr &= ~HS_CSR_LOO; |
298 | if(pci_bus_write_config_word(slot->bus, | 260 | if(pci_bus_write_config_word(slot->bus, |
299 | slot->devfn, | 261 | slot->devfn, |
@@ -312,257 +274,23 @@ int cpci_led_off(struct slot* slot) | |||
312 | * Device configuration functions | 274 | * Device configuration functions |
313 | */ | 275 | */ |
314 | 276 | ||
315 | static int cpci_configure_dev(struct pci_bus *bus, struct pci_dev *dev) | 277 | static void cpci_enable_device(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 | { | 278 | { |
368 | int rc; | 279 | struct pci_bus *bus; |
369 | struct pci_bus* child; | ||
370 | struct resource* r; | ||
371 | u8 max, n; | ||
372 | u16 command; | ||
373 | |||
374 | dbg("%s - enter", __FUNCTION__); | ||
375 | 280 | ||
376 | /* Do basic bridge initialization */ | 281 | pci_enable_device(dev); |
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) { | 282 | if(dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) { |
473 | rc = cpci_configure_bridge(bus, dev); | 283 | bus = dev->subordinate; |
474 | if(rc) | 284 | list_for_each_entry(dev, &bus->devices, bus_list) { |
475 | return rc; | 285 | cpci_enable_device(dev); |
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 | } | 286 | } |
548 | } | 287 | } |
549 | dbg("%s - exit", __FUNCTION__); | ||
550 | return 0; | ||
551 | } | 288 | } |
552 | 289 | ||
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) | 290 | int cpci_configure_slot(struct slot* slot) |
564 | { | 291 | { |
565 | int rc = 0; | 292 | unsigned char busnr; |
293 | struct pci_bus *child; | ||
566 | 294 | ||
567 | dbg("%s - enter", __FUNCTION__); | 295 | dbg("%s - enter", __FUNCTION__); |
568 | 296 | ||
@@ -588,74 +316,44 @@ int cpci_configure_slot(struct slot* slot) | |||
588 | slot->dev = pci_find_slot(slot->bus->number, slot->devfn); | 316 | slot->dev = pci_find_slot(slot->bus->number, slot->devfn); |
589 | if(slot->dev == NULL) { | 317 | if(slot->dev == NULL) { |
590 | err("Could not find PCI device for slot %02x", slot->number); | 318 | err("Could not find PCI device for slot %02x", slot->number); |
591 | return 0; | 319 | return 1; |
592 | } | 320 | } |
593 | } | 321 | } |
594 | dbg("slot->dev = %p", slot->dev); | 322 | |
595 | if(slot->dev) { | 323 | if (slot->dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) { |
596 | struct pci_dev *dev; | 324 | pci_read_config_byte(slot->dev, PCI_SECONDARY_BUS, &busnr); |
597 | struct pci_dev_wrapped wrapped_dev; | 325 | child = pci_add_new_bus(slot->dev->bus, slot->dev, busnr); |
598 | struct pci_bus_wrapped wrapped_bus; | 326 | pci_do_scan_bus(child); |
599 | int i; | 327 | pci_bus_size_bridges(child); |
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 | } | 328 | } |
618 | 329 | ||
619 | dbg("%s - exit, rc = %d", __FUNCTION__, rc); | 330 | pci_bus_assign_resources(slot->dev->bus); |
620 | return rc; | 331 | |
332 | cpci_enable_device(slot->dev); | ||
333 | |||
334 | dbg("%s - exit", __FUNCTION__); | ||
335 | return 0; | ||
621 | } | 336 | } |
622 | 337 | ||
623 | int cpci_unconfigure_slot(struct slot* slot) | 338 | int cpci_unconfigure_slot(struct slot* slot) |
624 | { | 339 | { |
625 | int rc = 0; | ||
626 | int i; | 340 | int i; |
627 | struct pci_dev_wrapped wrapped_dev; | ||
628 | struct pci_bus_wrapped wrapped_bus; | ||
629 | struct pci_dev *dev; | 341 | struct pci_dev *dev; |
630 | 342 | ||
631 | dbg("%s - enter", __FUNCTION__); | 343 | dbg("%s - enter", __FUNCTION__); |
632 | |||
633 | if(!slot->dev) { | 344 | if(!slot->dev) { |
634 | err("No device for slot %02x\n", slot->number); | 345 | err("No device for slot %02x\n", slot->number); |
635 | return -ENODEV; | 346 | return -ENODEV; |
636 | } | 347 | } |
637 | 348 | ||
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++) { | 349 | for (i = 0; i < 8; i++) { |
642 | dev = pci_find_slot(slot->bus->number, | 350 | dev = pci_find_slot(slot->bus->number, |
643 | PCI_DEVFN(PCI_SLOT(slot->devfn), i)); | 351 | PCI_DEVFN(PCI_SLOT(slot->devfn), i)); |
644 | if(dev) { | 352 | if(dev) { |
645 | wrapped_dev.dev = dev; | 353 | pci_remove_bus_device(dev); |
646 | wrapped_bus.bus = dev->bus; | 354 | slot->dev = NULL; |
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 | } | 355 | } |
658 | } | 356 | } |
659 | dbg("%s - exit, rc = %d", __FUNCTION__, rc); | 357 | dbg("%s - exit", __FUNCTION__); |
660 | return rc; | 358 | return 0; |
661 | } | 359 | } |