diff options
-rw-r--r-- | drivers/pci/hotplug/shpchp.h | 7 | ||||
-rw-r--r-- | drivers/pci/hotplug/shpchp_core.c | 8 | ||||
-rw-r--r-- | drivers/pci/hotplug/shpchp_ctrl.c | 227 |
3 files changed, 181 insertions, 61 deletions
diff --git a/drivers/pci/hotplug/shpchp.h b/drivers/pci/hotplug/shpchp.h index 87db07cfebdf..dd449512cf68 100644 --- a/drivers/pci/hotplug/shpchp.h +++ b/drivers/pci/hotplug/shpchp.h | |||
@@ -72,6 +72,7 @@ struct slot { | |||
72 | struct list_head slot_list; | 72 | struct list_head slot_list; |
73 | char name[SLOT_NAME_SIZE]; | 73 | char name[SLOT_NAME_SIZE]; |
74 | struct work_struct work; /* work for button event */ | 74 | struct work_struct work; /* work for button event */ |
75 | struct mutex lock; | ||
75 | }; | 76 | }; |
76 | 77 | ||
77 | struct event_info { | 78 | struct event_info { |
@@ -181,8 +182,8 @@ struct hotplug_params { | |||
181 | /* sysfs functions for the hotplug controller info */ | 182 | /* sysfs functions for the hotplug controller info */ |
182 | extern void shpchp_create_ctrl_files (struct controller *ctrl); | 183 | extern void shpchp_create_ctrl_files (struct controller *ctrl); |
183 | 184 | ||
184 | extern int shpchp_enable_slot(struct slot *slot); | 185 | extern int shpchp_sysfs_enable_slot(struct slot *slot); |
185 | extern int shpchp_disable_slot(struct slot *slot); | 186 | extern int shpchp_sysfs_disable_slot(struct slot *slot); |
186 | 187 | ||
187 | extern u8 shpchp_handle_attention_button(u8 hp_slot, void *inst_id); | 188 | extern u8 shpchp_handle_attention_button(u8 hp_slot, void *inst_id); |
188 | extern u8 shpchp_handle_switch_change(u8 hp_slot, void *inst_id); | 189 | extern u8 shpchp_handle_switch_change(u8 hp_slot, void *inst_id); |
@@ -200,7 +201,7 @@ extern int shpchprm_get_physical_slot_number(struct controller *ctrl, | |||
200 | u32 *sun, u8 busnum, u8 devnum); | 201 | u32 *sun, u8 busnum, u8 devnum); |
201 | extern void shpchp_remove_ctrl_files(struct controller *ctrl); | 202 | extern void shpchp_remove_ctrl_files(struct controller *ctrl); |
202 | extern void cleanup_slots(struct controller *ctrl); | 203 | extern void cleanup_slots(struct controller *ctrl); |
203 | extern void shpchp_pushbutton_thread(void *data); | 204 | extern void queue_pushbutton_work(void *data); |
204 | 205 | ||
205 | /* Global variables */ | 206 | /* Global variables */ |
206 | extern struct list_head shpchp_ctrl_list; | 207 | extern struct list_head shpchp_ctrl_list; |
diff --git a/drivers/pci/hotplug/shpchp_core.c b/drivers/pci/hotplug/shpchp_core.c index 5de659d23d1a..fa60ae47d91d 100644 --- a/drivers/pci/hotplug/shpchp_core.c +++ b/drivers/pci/hotplug/shpchp_core.c | |||
@@ -136,13 +136,14 @@ static int init_slots(struct controller *ctrl) | |||
136 | slot->bus = ctrl->slot_bus; | 136 | slot->bus = ctrl->slot_bus; |
137 | slot->device = ctrl->slot_device_offset + i; | 137 | slot->device = ctrl->slot_device_offset + i; |
138 | slot->hpc_ops = ctrl->hpc_ops; | 138 | slot->hpc_ops = ctrl->hpc_ops; |
139 | mutex_init(&slot->lock); | ||
139 | 140 | ||
140 | if (shpchprm_get_physical_slot_number(ctrl, &sun, | 141 | if (shpchprm_get_physical_slot_number(ctrl, &sun, |
141 | slot->bus, slot->device)) | 142 | slot->bus, slot->device)) |
142 | goto error_info; | 143 | goto error_info; |
143 | 144 | ||
144 | slot->number = sun; | 145 | slot->number = sun; |
145 | INIT_WORK(&slot->work, shpchp_pushbutton_thread, slot); | 146 | INIT_WORK(&slot->work, queue_pushbutton_work, slot); |
146 | 147 | ||
147 | /* register this slot with the hotplug pci core */ | 148 | /* register this slot with the hotplug pci core */ |
148 | hotplug_slot->private = slot; | 149 | hotplug_slot->private = slot; |
@@ -188,6 +189,7 @@ void cleanup_slots(struct controller *ctrl) | |||
188 | slot = list_entry(tmp, struct slot, slot_list); | 189 | slot = list_entry(tmp, struct slot, slot_list); |
189 | list_del(&slot->slot_list); | 190 | list_del(&slot->slot_list); |
190 | cancel_delayed_work(&slot->work); | 191 | cancel_delayed_work(&slot->work); |
192 | flush_scheduled_work(); | ||
191 | flush_workqueue(shpchp_wq); | 193 | flush_workqueue(shpchp_wq); |
192 | pci_hp_deregister(slot->hotplug_slot); | 194 | pci_hp_deregister(slot->hotplug_slot); |
193 | } | 195 | } |
@@ -244,7 +246,7 @@ static int enable_slot (struct hotplug_slot *hotplug_slot) | |||
244 | 246 | ||
245 | dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); | 247 | dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); |
246 | 248 | ||
247 | return shpchp_enable_slot(slot); | 249 | return shpchp_sysfs_enable_slot(slot); |
248 | } | 250 | } |
249 | 251 | ||
250 | static int disable_slot (struct hotplug_slot *hotplug_slot) | 252 | static int disable_slot (struct hotplug_slot *hotplug_slot) |
@@ -253,7 +255,7 @@ static int disable_slot (struct hotplug_slot *hotplug_slot) | |||
253 | 255 | ||
254 | dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); | 256 | dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); |
255 | 257 | ||
256 | return shpchp_disable_slot(slot); | 258 | return shpchp_sysfs_disable_slot(slot); |
257 | } | 259 | } |
258 | 260 | ||
259 | static int get_power_status (struct hotplug_slot *hotplug_slot, u8 *value) | 261 | static int get_power_status (struct hotplug_slot *hotplug_slot, u8 *value) |
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 | } | ||