diff options
author | Scott Murray <scottm@somanetworks.com> | 2005-05-09 17:31:50 -0400 |
---|---|---|
committer | Greg KH <gregkh@suse.de> | 2005-05-17 17:31:11 -0400 |
commit | 43b7d7cfb157b5c8c5cc0933f4e96fd81adc81ca (patch) | |
tree | 2af3b43ed8ee9468b1e0418c10275f33d23ced19 /drivers/pci/hotplug | |
parent | 8b245e45f34280ec61e3c8d643d4613b9e0eb7a4 (diff) |
[PATCH] PCI Hotplug: CPCI update
[PATCH] CPCI: update
I have finally done some work to update the CompactPCI hotplug driver to
fix some of the outstanding issues in 2.6:
- Added adapter and latch status ops so that those files will get created
by the current PCI hotplug core. This used to not be required, but
seems to be now after some of the sysfs rework in the core.
- Replaced slot list spinlock with a r/w semaphore to avoid any potential
issues with sleeping. This quiets all of the runtime warnings.
- Reworked interrupt driven hot extraction handling to remove need for a
polling operator for ENUM# status. There are a lot of boards that only
have an interrupt driven by ENUM#, so this lowers the bar to entry.
- Replaced pci_visit_dev usage with better use of the PCI core functions.
The new code is functionally equivalent to the previous code, but the
use of pci_enable_device on insert needs to be investigated further, as
I need to do some more testing to see if it is still necessary.
Signed-off-by: Scott Murray <scottm@somanetworks.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/pci/hotplug')
-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 | } |