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 | } | ||
