aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKenji Kaneshige <kaneshige.kenji@jp.fujitsu.com>2006-02-21 18:45:48 -0500
committerGreg Kroah-Hartman <gregkh@suse.de>2006-03-23 17:35:13 -0500
commita246fa4e9f0f1b5096a1cad0659d22fb10fb3732 (patch)
tree21490f4d1d2794a9ccd64529431fbbd8f4e234e7
parentf7391f5325ea744f0632f7ef39a90085162743ac (diff)
[PATCH] shpchp: Fix slot state handling
Current SHPCHP driver doesn't care about the confliction between hotplug operation via sysfs and hotplug operation via attention button. So if those ware conflicted, slot could be an unexpected state. This patch changes SHPCHP driver to handle slot state properly. With this patch, slot events are handled according to the current slot state as shown at the Table below. Table. Slot States and Event Handling ========================================================================= Slot State Event and Action ========================================================================= STATIC - Go to POWERON state if user initiates (Slot enabled, insertion request via sysfs Slot disabled) - Go to POWEROFF state if user initiates removal request via sysfs - Go to BLINKINGON state if user presses attention button when the slot is disabled - Go to BLINKINGOFF state if user presses attention button when the slot is enabled
-rw-r--r--drivers/pci/hotplug/shpchp.h7
-rw-r--r--drivers/pci/hotplug/shpchp_core.c8
-rw-r--r--drivers/pci/hotplug/shpchp_ctrl.c227
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
77struct event_info { 78struct 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 */
182extern void shpchp_create_ctrl_files (struct controller *ctrl); 183extern void shpchp_create_ctrl_files (struct controller *ctrl);
183 184
184extern int shpchp_enable_slot(struct slot *slot); 185extern int shpchp_sysfs_enable_slot(struct slot *slot);
185extern int shpchp_disable_slot(struct slot *slot); 186extern int shpchp_sysfs_disable_slot(struct slot *slot);
186 187
187extern u8 shpchp_handle_attention_button(u8 hp_slot, void *inst_id); 188extern u8 shpchp_handle_attention_button(u8 hp_slot, void *inst_id);
188extern u8 shpchp_handle_switch_change(u8 hp_slot, void *inst_id); 189extern 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);
201extern void shpchp_remove_ctrl_files(struct controller *ctrl); 202extern void shpchp_remove_ctrl_files(struct controller *ctrl);
202extern void cleanup_slots(struct controller *ctrl); 203extern void cleanup_slots(struct controller *ctrl);
203extern void shpchp_pushbutton_thread(void *data); 204extern void queue_pushbutton_work(void *data);
204 205
205/* Global variables */ 206/* Global variables */
206extern struct list_head shpchp_ctrl_list; 207extern 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
250static int disable_slot (struct hotplug_slot *hotplug_slot) 252static 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
259static int get_power_status (struct hotplug_slot *hotplug_slot, u8 *value) 261static 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
39static void interrupt_event_handler(void *data); 39static void interrupt_event_handler(void *data);
40static int shpchp_enable_slot(struct slot *p_slot);
41static int shpchp_disable_slot(struct slot *p_slot);
40 42
41static int queue_interrupt_event(struct slot *p_slot, u32 event_type) 43static 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
479struct 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 */
502void shpchp_pushbutton_thread(void *data) 491static 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
519void 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
520static int update_slot_info (struct slot *slot) 548static 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
539static void interrupt_event_handler(void *data) 567/*
568 * Note: This function must be called with slot->lock held
569 */
570static 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
624static 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
595int shpchp_enable_slot (struct slot *p_slot) 649static 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
650int shpchp_disable_slot (struct slot *p_slot) 704static 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
738int 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
770int 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}