diff options
Diffstat (limited to 'drivers/pci/hotplug/shpchp_ctrl.c')
-rw-r--r-- | drivers/pci/hotplug/shpchp_ctrl.c | 227 |
1 files changed, 172 insertions, 55 deletions
diff --git a/drivers/pci/hotplug/shpchp_ctrl.c b/drivers/pci/hotplug/shpchp_ctrl.c index 2411f3bd08da..10f3257b18a7 100644 --- a/drivers/pci/hotplug/shpchp_ctrl.c +++ b/drivers/pci/hotplug/shpchp_ctrl.c | |||
@@ -37,6 +37,8 @@ | |||
37 | #include "shpchp.h" | 37 | #include "shpchp.h" |
38 | 38 | ||
39 | static void interrupt_event_handler(void *data); | 39 | static void interrupt_event_handler(void *data); |
40 | static int shpchp_enable_slot(struct slot *p_slot); | ||
41 | static int shpchp_disable_slot(struct slot *p_slot); | ||
40 | 42 | ||
41 | static int queue_interrupt_event(struct slot *p_slot, u32 event_type) | 43 | static int queue_interrupt_event(struct slot *p_slot, u32 event_type) |
42 | { | 44 | { |
@@ -50,7 +52,7 @@ static int queue_interrupt_event(struct slot *p_slot, u32 event_type) | |||
50 | info->p_slot = p_slot; | 52 | info->p_slot = p_slot; |
51 | INIT_WORK(&info->work, interrupt_event_handler, info); | 53 | INIT_WORK(&info->work, interrupt_event_handler, info); |
52 | 54 | ||
53 | queue_work(shpchp_wq, &info->work); | 55 | schedule_work(&info->work); |
54 | 56 | ||
55 | return 0; | 57 | return 0; |
56 | } | 58 | } |
@@ -73,24 +75,6 @@ u8 shpchp_handle_attention_button(u8 hp_slot, void *inst_id) | |||
73 | info("Button pressed on Slot(%d)\n", ctrl->first_slot + hp_slot); | 75 | info("Button pressed on Slot(%d)\n", ctrl->first_slot + hp_slot); |
74 | event_type = INT_BUTTON_PRESS; | 76 | event_type = INT_BUTTON_PRESS; |
75 | 77 | ||
76 | if ((p_slot->state == BLINKINGON_STATE) | ||
77 | || (p_slot->state == BLINKINGOFF_STATE)) { | ||
78 | /* Cancel if we are still blinking; this means that we press the | ||
79 | * attention again before the 5 sec. limit expires to cancel hot-add | ||
80 | * or hot-remove | ||
81 | */ | ||
82 | event_type = INT_BUTTON_CANCEL; | ||
83 | info("Button cancel on Slot(%d)\n", ctrl->first_slot + hp_slot); | ||
84 | } else if ((p_slot->state == POWERON_STATE) | ||
85 | || (p_slot->state == POWEROFF_STATE)) { | ||
86 | /* Ignore if the slot is on power-on or power-off state; this | ||
87 | * means that the previous attention button action to hot-add or | ||
88 | * hot-remove is undergoing | ||
89 | */ | ||
90 | event_type = INT_BUTTON_IGNORE; | ||
91 | info("Button ignore on Slot(%d)\n", ctrl->first_slot + hp_slot); | ||
92 | } | ||
93 | |||
94 | queue_interrupt_event(p_slot, event_type); | 78 | queue_interrupt_event(p_slot, event_type); |
95 | 79 | ||
96 | return 0; | 80 | return 0; |
@@ -492,6 +476,11 @@ static int remove_board(struct slot *p_slot) | |||
492 | } | 476 | } |
493 | 477 | ||
494 | 478 | ||
479 | struct pushbutton_work_info { | ||
480 | struct slot *p_slot; | ||
481 | struct work_struct work; | ||
482 | }; | ||
483 | |||
495 | /** | 484 | /** |
496 | * shpchp_pushbutton_thread | 485 | * shpchp_pushbutton_thread |
497 | * | 486 | * |
@@ -499,22 +488,61 @@ static int remove_board(struct slot *p_slot) | |||
499 | * Handles all pending events and exits. | 488 | * Handles all pending events and exits. |
500 | * | 489 | * |
501 | */ | 490 | */ |
502 | void shpchp_pushbutton_thread(void *data) | 491 | static void shpchp_pushbutton_thread(void *data) |
503 | { | 492 | { |
504 | struct slot *p_slot = data; | 493 | struct pushbutton_work_info *info = data; |
505 | u8 getstatus; | 494 | struct slot *p_slot = info->p_slot; |
506 | 495 | ||
507 | p_slot->hpc_ops->get_power_status(p_slot, &getstatus); | 496 | mutex_lock(&p_slot->lock); |
508 | if (getstatus) { | 497 | switch (p_slot->state) { |
509 | p_slot->state = POWEROFF_STATE; | 498 | case POWEROFF_STATE: |
499 | mutex_unlock(&p_slot->lock); | ||
510 | shpchp_disable_slot(p_slot); | 500 | shpchp_disable_slot(p_slot); |
501 | mutex_lock(&p_slot->lock); | ||
511 | p_slot->state = STATIC_STATE; | 502 | p_slot->state = STATIC_STATE; |
512 | } else { | 503 | break; |
513 | p_slot->state = POWERON_STATE; | 504 | case POWERON_STATE: |
505 | mutex_unlock(&p_slot->lock); | ||
514 | if (shpchp_enable_slot(p_slot)) | 506 | if (shpchp_enable_slot(p_slot)) |
515 | p_slot->hpc_ops->green_led_off(p_slot); | 507 | p_slot->hpc_ops->green_led_off(p_slot); |
508 | mutex_lock(&p_slot->lock); | ||
516 | p_slot->state = STATIC_STATE; | 509 | p_slot->state = STATIC_STATE; |
510 | break; | ||
511 | default: | ||
512 | break; | ||
513 | } | ||
514 | mutex_unlock(&p_slot->lock); | ||
515 | |||
516 | kfree(info); | ||
517 | } | ||
518 | |||
519 | void queue_pushbutton_work(void *data) | ||
520 | { | ||
521 | struct slot *p_slot = data; | ||
522 | struct pushbutton_work_info *info; | ||
523 | |||
524 | info = kmalloc(sizeof(*info), GFP_KERNEL); | ||
525 | if (!info) { | ||
526 | err("%s: Cannot allocate memory\n", __FUNCTION__); | ||
527 | return; | ||
528 | } | ||
529 | info->p_slot = p_slot; | ||
530 | INIT_WORK(&info->work, shpchp_pushbutton_thread, info); | ||
531 | |||
532 | mutex_lock(&p_slot->lock); | ||
533 | switch (p_slot->state) { | ||
534 | case BLINKINGOFF_STATE: | ||
535 | p_slot->state = POWEROFF_STATE; | ||
536 | break; | ||
537 | case BLINKINGON_STATE: | ||
538 | p_slot->state = POWERON_STATE; | ||
539 | break; | ||
540 | default: | ||
541 | goto out; | ||
517 | } | 542 | } |
543 | queue_work(shpchp_wq, &info->work); | ||
544 | out: | ||
545 | mutex_unlock(&p_slot->lock); | ||
518 | } | 546 | } |
519 | 547 | ||
520 | static int update_slot_info (struct slot *slot) | 548 | static int update_slot_info (struct slot *slot) |
@@ -536,34 +564,15 @@ static int update_slot_info (struct slot *slot) | |||
536 | return result; | 564 | return result; |
537 | } | 565 | } |
538 | 566 | ||
539 | static void interrupt_event_handler(void *data) | 567 | /* |
568 | * Note: This function must be called with slot->lock held | ||
569 | */ | ||
570 | static void handle_button_press_event(struct slot *p_slot) | ||
540 | { | 571 | { |
541 | struct event_info *info = data; | ||
542 | struct slot *p_slot = info->p_slot; | ||
543 | u8 getstatus; | 572 | u8 getstatus; |
544 | 573 | ||
545 | switch (info->event_type) { | 574 | switch (p_slot->state) { |
546 | case INT_BUTTON_CANCEL: | 575 | case STATIC_STATE: |
547 | dbg("%s: button cancel\n", __FUNCTION__); | ||
548 | cancel_delayed_work(&p_slot->work); | ||
549 | switch (p_slot->state) { | ||
550 | case BLINKINGOFF_STATE: | ||
551 | p_slot->hpc_ops->green_led_on(p_slot); | ||
552 | p_slot->hpc_ops->set_attention_status(p_slot, 0); | ||
553 | break; | ||
554 | case BLINKINGON_STATE: | ||
555 | p_slot->hpc_ops->green_led_off(p_slot); | ||
556 | p_slot->hpc_ops->set_attention_status(p_slot, 0); | ||
557 | break; | ||
558 | default: | ||
559 | warn("Not a valid state\n"); | ||
560 | return; | ||
561 | } | ||
562 | info(msg_button_cancel, p_slot->number); | ||
563 | p_slot->state = STATIC_STATE; | ||
564 | break; | ||
565 | case INT_BUTTON_PRESS: | ||
566 | dbg("%s: Button pressed\n", __FUNCTION__); | ||
567 | p_slot->hpc_ops->get_power_status(p_slot, &getstatus); | 576 | p_slot->hpc_ops->get_power_status(p_slot, &getstatus); |
568 | if (getstatus) { | 577 | if (getstatus) { |
569 | p_slot->state = BLINKINGOFF_STATE; | 578 | p_slot->state = BLINKINGOFF_STATE; |
@@ -576,7 +585,51 @@ static void interrupt_event_handler(void *data) | |||
576 | p_slot->hpc_ops->green_led_blink(p_slot); | 585 | p_slot->hpc_ops->green_led_blink(p_slot); |
577 | p_slot->hpc_ops->set_attention_status(p_slot, 0); | 586 | p_slot->hpc_ops->set_attention_status(p_slot, 0); |
578 | 587 | ||
579 | queue_delayed_work(shpchp_wq, &p_slot->work, 5*HZ); | 588 | schedule_delayed_work(&p_slot->work, 5*HZ); |
589 | break; | ||
590 | case BLINKINGOFF_STATE: | ||
591 | case BLINKINGON_STATE: | ||
592 | /* | ||
593 | * Cancel if we are still blinking; this means that we | ||
594 | * press the attention again before the 5 sec. limit | ||
595 | * expires to cancel hot-add or hot-remove | ||
596 | */ | ||
597 | info("Button cancel on Slot(%s)\n", p_slot->name); | ||
598 | dbg("%s: button cancel\n", __FUNCTION__); | ||
599 | cancel_delayed_work(&p_slot->work); | ||
600 | if (p_slot->state == BLINKINGOFF_STATE) | ||
601 | p_slot->hpc_ops->green_led_on(p_slot); | ||
602 | else | ||
603 | p_slot->hpc_ops->green_led_off(p_slot); | ||
604 | p_slot->hpc_ops->set_attention_status(p_slot, 0); | ||
605 | info(msg_button_cancel, p_slot->number); | ||
606 | p_slot->state = STATIC_STATE; | ||
607 | break; | ||
608 | case POWEROFF_STATE: | ||
609 | case POWERON_STATE: | ||
610 | /* | ||
611 | * Ignore if the slot is on power-on or power-off state; | ||
612 | * this means that the previous attention button action | ||
613 | * to hot-add or hot-remove is undergoing | ||
614 | */ | ||
615 | info("Button ignore on Slot(%s)\n", p_slot->name); | ||
616 | update_slot_info(p_slot); | ||
617 | break; | ||
618 | default: | ||
619 | warn("Not a valid state\n"); | ||
620 | break; | ||
621 | } | ||
622 | } | ||
623 | |||
624 | static void interrupt_event_handler(void *data) | ||
625 | { | ||
626 | struct event_info *info = data; | ||
627 | struct slot *p_slot = info->p_slot; | ||
628 | |||
629 | mutex_lock(&p_slot->lock); | ||
630 | switch (info->event_type) { | ||
631 | case INT_BUTTON_PRESS: | ||
632 | handle_button_press_event(p_slot); | ||
580 | break; | 633 | break; |
581 | case INT_POWER_FAULT: | 634 | case INT_POWER_FAULT: |
582 | dbg("%s: power fault\n", __FUNCTION__); | 635 | dbg("%s: power fault\n", __FUNCTION__); |
@@ -587,12 +640,13 @@ static void interrupt_event_handler(void *data) | |||
587 | update_slot_info(p_slot); | 640 | update_slot_info(p_slot); |
588 | break; | 641 | break; |
589 | } | 642 | } |
643 | mutex_unlock(&p_slot->lock); | ||
590 | 644 | ||
591 | kfree(info); | 645 | kfree(info); |
592 | } | 646 | } |
593 | 647 | ||
594 | 648 | ||
595 | int shpchp_enable_slot (struct slot *p_slot) | 649 | static int shpchp_enable_slot (struct slot *p_slot) |
596 | { | 650 | { |
597 | u8 getstatus = 0; | 651 | u8 getstatus = 0; |
598 | int rc, retval = -ENODEV; | 652 | int rc, retval = -ENODEV; |
@@ -647,7 +701,7 @@ int shpchp_enable_slot (struct slot *p_slot) | |||
647 | } | 701 | } |
648 | 702 | ||
649 | 703 | ||
650 | int shpchp_disable_slot (struct slot *p_slot) | 704 | static int shpchp_disable_slot (struct slot *p_slot) |
651 | { | 705 | { |
652 | u8 getstatus = 0; | 706 | u8 getstatus = 0; |
653 | int rc, retval = -ENODEV; | 707 | int rc, retval = -ENODEV; |
@@ -681,3 +735,66 @@ int shpchp_disable_slot (struct slot *p_slot) | |||
681 | return retval; | 735 | return retval; |
682 | } | 736 | } |
683 | 737 | ||
738 | int shpchp_sysfs_enable_slot(struct slot *p_slot) | ||
739 | { | ||
740 | int retval = -ENODEV; | ||
741 | |||
742 | mutex_lock(&p_slot->lock); | ||
743 | switch (p_slot->state) { | ||
744 | case BLINKINGON_STATE: | ||
745 | cancel_delayed_work(&p_slot->work); | ||
746 | case STATIC_STATE: | ||
747 | p_slot->state = POWERON_STATE; | ||
748 | mutex_unlock(&p_slot->lock); | ||
749 | retval = shpchp_enable_slot(p_slot); | ||
750 | mutex_lock(&p_slot->lock); | ||
751 | p_slot->state = STATIC_STATE; | ||
752 | break; | ||
753 | case POWERON_STATE: | ||
754 | info("Slot %s is already in powering on state\n", | ||
755 | p_slot->name); | ||
756 | break; | ||
757 | case BLINKINGOFF_STATE: | ||
758 | case POWEROFF_STATE: | ||
759 | info("Already enabled on slot %s\n", p_slot->name); | ||
760 | break; | ||
761 | default: | ||
762 | err("Not a valid state on slot %s\n", p_slot->name); | ||
763 | break; | ||
764 | } | ||
765 | mutex_unlock(&p_slot->lock); | ||
766 | |||
767 | return retval; | ||
768 | } | ||
769 | |||
770 | int shpchp_sysfs_disable_slot(struct slot *p_slot) | ||
771 | { | ||
772 | int retval = -ENODEV; | ||
773 | |||
774 | mutex_lock(&p_slot->lock); | ||
775 | switch (p_slot->state) { | ||
776 | case BLINKINGOFF_STATE: | ||
777 | cancel_delayed_work(&p_slot->work); | ||
778 | case STATIC_STATE: | ||
779 | p_slot->state = POWEROFF_STATE; | ||
780 | mutex_unlock(&p_slot->lock); | ||
781 | retval = shpchp_disable_slot(p_slot); | ||
782 | mutex_lock(&p_slot->lock); | ||
783 | p_slot->state = STATIC_STATE; | ||
784 | break; | ||
785 | case POWEROFF_STATE: | ||
786 | info("Slot %s is already in powering off state\n", | ||
787 | p_slot->name); | ||
788 | break; | ||
789 | case BLINKINGON_STATE: | ||
790 | case POWERON_STATE: | ||
791 | info("Already disabled on slot %s\n", p_slot->name); | ||
792 | break; | ||
793 | default: | ||
794 | err("Not a valid state on slot %s\n", p_slot->name); | ||
795 | break; | ||
796 | } | ||
797 | mutex_unlock(&p_slot->lock); | ||
798 | |||
799 | return retval; | ||
800 | } | ||