diff options
author | Kenji Kaneshige <kaneshige.kenji@jp.fujitsu.com> | 2007-03-06 18:02:26 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2007-05-02 22:02:34 -0400 |
commit | 5d386e1ac4025b4bcc6bad6811e771cb76064dfe (patch) | |
tree | d673b9686ed60aa43c1c0f43abc8bc54d19b6649 | |
parent | f7bdd12d234d9064bd0aa1b5610508959120a9b4 (diff) |
pciehp: Event handling rework
The event handler of PCIEHP driver is unnecessarily very complex. In
addition, current event handler can only a fixed number of events at
the same time, and some of events would be lost if several number of
events happened at the same time.
This patch simplify the event handler using 'work queue', and it also
fix the above-mentioned issue.
Signed-off-by: Kenji Kaneshige <kaneshige.kenji@jp.fujitsu.com>
Signed-off-by: Kristen Carlson Accardi <kristen.c.accardi@intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r-- | drivers/pci/hotplug/pciehp.h | 16 | ||||
-rw-r--r-- | drivers/pci/hotplug/pciehp_core.c | 44 | ||||
-rw-r--r-- | drivers/pci/hotplug/pciehp_ctrl.c | 616 | ||||
-rw-r--r-- | drivers/pci/hotplug/pciehp_hpc.c | 34 |
4 files changed, 321 insertions, 389 deletions
diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h index d19fcae8a7c0..c98e27128cc4 100644 --- a/drivers/pci/hotplug/pciehp.h +++ b/drivers/pci/hotplug/pciehp.h | |||
@@ -43,6 +43,7 @@ extern int pciehp_poll_mode; | |||
43 | extern int pciehp_poll_time; | 43 | extern int pciehp_poll_time; |
44 | extern int pciehp_debug; | 44 | extern int pciehp_debug; |
45 | extern int pciehp_force; | 45 | extern int pciehp_force; |
46 | extern struct workqueue_struct *pciehp_wq; | ||
46 | 47 | ||
47 | #define dbg(format, arg...) \ | 48 | #define dbg(format, arg...) \ |
48 | do { \ | 49 | do { \ |
@@ -70,14 +71,16 @@ struct slot { | |||
70 | struct list_head slot_list; | 71 | struct list_head slot_list; |
71 | char name[SLOT_NAME_SIZE]; | 72 | char name[SLOT_NAME_SIZE]; |
72 | unsigned long last_emi_toggle; | 73 | unsigned long last_emi_toggle; |
74 | struct delayed_work work; /* work for button event */ | ||
75 | struct mutex lock; | ||
73 | }; | 76 | }; |
74 | 77 | ||
75 | struct event_info { | 78 | struct event_info { |
76 | u32 event_type; | 79 | u32 event_type; |
77 | u8 hp_slot; | 80 | struct slot *p_slot; |
81 | struct work_struct work; | ||
78 | }; | 82 | }; |
79 | 83 | ||
80 | #define MAX_EVENTS 10 | ||
81 | struct controller { | 84 | struct controller { |
82 | struct controller *next; | 85 | struct controller *next; |
83 | struct mutex crit_sect; /* critical section mutex */ | 86 | struct mutex crit_sect; /* critical section mutex */ |
@@ -86,11 +89,9 @@ struct controller { | |||
86 | int slot_num_inc; /* 1 or -1 */ | 89 | int slot_num_inc; /* 1 or -1 */ |
87 | struct pci_dev *pci_dev; | 90 | struct pci_dev *pci_dev; |
88 | struct list_head slot_list; | 91 | struct list_head slot_list; |
89 | struct event_info event_queue[MAX_EVENTS]; | ||
90 | struct slot *slot; | 92 | struct slot *slot; |
91 | struct hpc_ops *hpc_ops; | 93 | struct hpc_ops *hpc_ops; |
92 | wait_queue_head_t queue; /* sleep & wake process */ | 94 | wait_queue_head_t queue; /* sleep & wake process */ |
93 | u8 next_event; | ||
94 | u8 bus; | 95 | u8 bus; |
95 | u8 device; | 96 | u8 device; |
96 | u8 function; | 97 | u8 function; |
@@ -149,16 +150,15 @@ struct controller { | |||
149 | #define HP_SUPR_RM(cap) (cap & HP_SUPR_RM_SUP) | 150 | #define HP_SUPR_RM(cap) (cap & HP_SUPR_RM_SUP) |
150 | #define EMI(cap) (cap & EMI_PRSN) | 151 | #define EMI(cap) (cap & EMI_PRSN) |
151 | 152 | ||
152 | extern int pciehp_event_start_thread(void); | 153 | extern int pciehp_sysfs_enable_slot(struct slot *slot); |
153 | extern void pciehp_event_stop_thread(void); | 154 | extern int pciehp_sysfs_disable_slot(struct slot *slot); |
154 | extern int pciehp_enable_slot(struct slot *slot); | ||
155 | extern int pciehp_disable_slot(struct slot *slot); | ||
156 | extern u8 pciehp_handle_attention_button(u8 hp_slot, struct controller *ctrl); | 155 | extern u8 pciehp_handle_attention_button(u8 hp_slot, struct controller *ctrl); |
157 | extern u8 pciehp_handle_switch_change(u8 hp_slot, struct controller *ctrl); | 156 | extern u8 pciehp_handle_switch_change(u8 hp_slot, struct controller *ctrl); |
158 | extern u8 pciehp_handle_presence_change(u8 hp_slot, struct controller *ctrl); | 157 | extern u8 pciehp_handle_presence_change(u8 hp_slot, struct controller *ctrl); |
159 | extern u8 pciehp_handle_power_fault(u8 hp_slot, struct controller *ctrl); | 158 | extern u8 pciehp_handle_power_fault(u8 hp_slot, struct controller *ctrl); |
160 | extern int pciehp_configure_device(struct slot *p_slot); | 159 | extern int pciehp_configure_device(struct slot *p_slot); |
161 | extern int pciehp_unconfigure_device(struct slot *p_slot); | 160 | extern int pciehp_unconfigure_device(struct slot *p_slot); |
161 | extern void queue_pushbutton_work(struct work_struct *work); | ||
162 | int pcie_init(struct controller *ctrl, struct pcie_device *dev); | 162 | int pcie_init(struct controller *ctrl, struct pcie_device *dev); |
163 | 163 | ||
164 | /* Global variables */ | 164 | /* Global variables */ |
diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c index a92eda6e02f6..749227ecdb04 100644 --- a/drivers/pci/hotplug/pciehp_core.c +++ b/drivers/pci/hotplug/pciehp_core.c | |||
@@ -41,6 +41,7 @@ int pciehp_debug; | |||
41 | int pciehp_poll_mode; | 41 | int pciehp_poll_mode; |
42 | int pciehp_poll_time; | 42 | int pciehp_poll_time; |
43 | int pciehp_force; | 43 | int pciehp_force; |
44 | struct workqueue_struct *pciehp_wq; | ||
44 | struct controller *pciehp_ctrl_list; | 45 | struct controller *pciehp_ctrl_list; |
45 | 46 | ||
46 | #define DRIVER_VERSION "0.4" | 47 | #define DRIVER_VERSION "0.4" |
@@ -62,7 +63,6 @@ MODULE_PARM_DESC(pciehp_force, "Force pciehp, even if _OSC and OSHP are missing" | |||
62 | 63 | ||
63 | #define PCIE_MODULE_NAME "pciehp" | 64 | #define PCIE_MODULE_NAME "pciehp" |
64 | 65 | ||
65 | static int pcie_start_thread (void); | ||
66 | static int set_attention_status (struct hotplug_slot *slot, u8 value); | 66 | static int set_attention_status (struct hotplug_slot *slot, u8 value); |
67 | static int enable_slot (struct hotplug_slot *slot); | 67 | static int enable_slot (struct hotplug_slot *slot); |
68 | static int disable_slot (struct hotplug_slot *slot); | 68 | static int disable_slot (struct hotplug_slot *slot); |
@@ -229,6 +229,8 @@ static int init_slots(struct controller *ctrl) | |||
229 | slot->device = ctrl->slot_device_offset + i; | 229 | slot->device = ctrl->slot_device_offset + i; |
230 | slot->hpc_ops = ctrl->hpc_ops; | 230 | slot->hpc_ops = ctrl->hpc_ops; |
231 | slot->number = ctrl->first_slot; | 231 | slot->number = ctrl->first_slot; |
232 | mutex_init(&slot->lock); | ||
233 | INIT_DELAYED_WORK(&slot->work, queue_pushbutton_work); | ||
232 | 234 | ||
233 | /* register this slot with the hotplug pci core */ | 235 | /* register this slot with the hotplug pci core */ |
234 | hotplug_slot->private = slot; | 236 | hotplug_slot->private = slot; |
@@ -286,6 +288,9 @@ static void cleanup_slots(struct controller *ctrl) | |||
286 | if (EMI(ctrl->ctrlcap)) | 288 | if (EMI(ctrl->ctrlcap)) |
287 | sysfs_remove_file(&slot->hotplug_slot->kobj, | 289 | sysfs_remove_file(&slot->hotplug_slot->kobj, |
288 | &hotplug_slot_attr_lock.attr); | 290 | &hotplug_slot_attr_lock.attr); |
291 | cancel_delayed_work(&slot->work); | ||
292 | flush_scheduled_work(); | ||
293 | flush_workqueue(pciehp_wq); | ||
289 | pci_hp_deregister(slot->hotplug_slot); | 294 | pci_hp_deregister(slot->hotplug_slot); |
290 | } | 295 | } |
291 | } | 296 | } |
@@ -314,7 +319,7 @@ static int enable_slot(struct hotplug_slot *hotplug_slot) | |||
314 | 319 | ||
315 | dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); | 320 | dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); |
316 | 321 | ||
317 | return pciehp_enable_slot(slot); | 322 | return pciehp_sysfs_enable_slot(slot); |
318 | } | 323 | } |
319 | 324 | ||
320 | 325 | ||
@@ -324,7 +329,7 @@ static int disable_slot(struct hotplug_slot *hotplug_slot) | |||
324 | 329 | ||
325 | dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); | 330 | dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); |
326 | 331 | ||
327 | return pciehp_disable_slot(slot); | 332 | return pciehp_sysfs_disable_slot(slot); |
328 | } | 333 | } |
329 | 334 | ||
330 | static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value) | 335 | static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value) |
@@ -466,9 +471,6 @@ static int pciehp_probe(struct pcie_device *dev, const struct pcie_port_service_ | |||
466 | 471 | ||
467 | t_slot = pciehp_find_slot(ctrl, ctrl->slot_device_offset); | 472 | t_slot = pciehp_find_slot(ctrl, ctrl->slot_device_offset); |
468 | 473 | ||
469 | /* Finish setting up the hot plug ctrl device */ | ||
470 | ctrl->next_event = 0; | ||
471 | |||
472 | if (!pciehp_ctrl_list) { | 474 | if (!pciehp_ctrl_list) { |
473 | pciehp_ctrl_list = ctrl; | 475 | pciehp_ctrl_list = ctrl; |
474 | ctrl->next = NULL; | 476 | ctrl->next = NULL; |
@@ -496,22 +498,6 @@ err_out_none: | |||
496 | return -ENODEV; | 498 | return -ENODEV; |
497 | } | 499 | } |
498 | 500 | ||
499 | |||
500 | static int pcie_start_thread(void) | ||
501 | { | ||
502 | int retval = 0; | ||
503 | |||
504 | dbg("Initialize + Start the notification/polling mechanism \n"); | ||
505 | |||
506 | retval = pciehp_event_start_thread(); | ||
507 | if (retval) { | ||
508 | dbg("pciehp_event_start_thread() failed\n"); | ||
509 | return retval; | ||
510 | } | ||
511 | |||
512 | return retval; | ||
513 | } | ||
514 | |||
515 | static void __exit unload_pciehpd(void) | 501 | static void __exit unload_pciehpd(void) |
516 | { | 502 | { |
517 | struct controller *ctrl; | 503 | struct controller *ctrl; |
@@ -529,10 +515,6 @@ static void __exit unload_pciehpd(void) | |||
529 | 515 | ||
530 | kfree(tctrl); | 516 | kfree(tctrl); |
531 | } | 517 | } |
532 | |||
533 | /* Stop the notification mechanism */ | ||
534 | pciehp_event_stop_thread(); | ||
535 | |||
536 | } | 518 | } |
537 | 519 | ||
538 | static void pciehp_remove (struct pcie_device *device) | 520 | static void pciehp_remove (struct pcie_device *device) |
@@ -585,21 +567,11 @@ static int __init pcied_init(void) | |||
585 | pciehp_poll_mode = 1; | 567 | pciehp_poll_mode = 1; |
586 | #endif | 568 | #endif |
587 | 569 | ||
588 | retval = pcie_start_thread(); | ||
589 | if (retval) | ||
590 | goto error_hpc_init; | ||
591 | |||
592 | retval = pcie_port_service_register(&hpdriver_portdrv); | 570 | retval = pcie_port_service_register(&hpdriver_portdrv); |
593 | dbg("pcie_port_service_register = %d\n", retval); | 571 | dbg("pcie_port_service_register = %d\n", retval); |
594 | info(DRIVER_DESC " version: " DRIVER_VERSION "\n"); | 572 | info(DRIVER_DESC " version: " DRIVER_VERSION "\n"); |
595 | if (retval) | 573 | if (retval) |
596 | dbg("%s: Failure to register service\n", __FUNCTION__); | 574 | dbg("%s: Failure to register service\n", __FUNCTION__); |
597 | |||
598 | error_hpc_init: | ||
599 | if (retval) { | ||
600 | pciehp_event_stop_thread(); | ||
601 | }; | ||
602 | |||
603 | return retval; | 575 | return retval; |
604 | } | 576 | } |
605 | 577 | ||
diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c index 4283ef56dbd9..91441e5ae631 100644 --- a/drivers/pci/hotplug/pciehp_ctrl.c +++ b/drivers/pci/hotplug/pciehp_ctrl.c | |||
@@ -32,92 +32,61 @@ | |||
32 | #include <linux/types.h> | 32 | #include <linux/types.h> |
33 | #include <linux/smp_lock.h> | 33 | #include <linux/smp_lock.h> |
34 | #include <linux/pci.h> | 34 | #include <linux/pci.h> |
35 | #include <linux/workqueue.h> | ||
35 | #include "../pci.h" | 36 | #include "../pci.h" |
36 | #include "pciehp.h" | 37 | #include "pciehp.h" |
37 | 38 | ||
38 | static void interrupt_event_handler(struct controller *ctrl); | 39 | static void interrupt_event_handler(struct work_struct *work); |
40 | static int pciehp_enable_slot(struct slot *p_slot); | ||
41 | static int pciehp_disable_slot(struct slot *p_slot); | ||
39 | 42 | ||
40 | static struct semaphore event_semaphore; /* mutex for process loop (up if something to process) */ | 43 | static int queue_interrupt_event(struct slot *p_slot, u32 event_type) |
41 | static struct semaphore event_exit; /* guard ensure thread has exited before calling it quits */ | ||
42 | static int event_finished; | ||
43 | static unsigned long pushbutton_pending; /* = 0 */ | ||
44 | static unsigned long surprise_rm_pending; /* = 0 */ | ||
45 | |||
46 | static inline char *slot_name(struct slot *p_slot) | ||
47 | { | 44 | { |
48 | return p_slot->hotplug_slot->name; | 45 | struct event_info *info; |
46 | |||
47 | info = kmalloc(sizeof(*info), GFP_ATOMIC); | ||
48 | if (!info) | ||
49 | return -ENOMEM; | ||
50 | |||
51 | info->event_type = event_type; | ||
52 | info->p_slot = p_slot; | ||
53 | INIT_WORK(&info->work, interrupt_event_handler); | ||
54 | |||
55 | schedule_work(&info->work); | ||
56 | |||
57 | return 0; | ||
49 | } | 58 | } |
50 | 59 | ||
51 | u8 pciehp_handle_attention_button(u8 hp_slot, struct controller *ctrl) | 60 | u8 pciehp_handle_attention_button(u8 hp_slot, struct controller *ctrl) |
52 | { | 61 | { |
53 | struct slot *p_slot; | 62 | struct slot *p_slot; |
54 | u8 rc = 0; | 63 | u32 event_type; |
55 | u8 getstatus; | ||
56 | struct event_info *taskInfo; | ||
57 | 64 | ||
58 | /* Attention Button Change */ | 65 | /* Attention Button Change */ |
59 | dbg("pciehp: Attention button interrupt received.\n"); | 66 | dbg("pciehp: Attention button interrupt received.\n"); |
60 | |||
61 | /* This is the structure that tells the worker thread what to do */ | ||
62 | taskInfo = &(ctrl->event_queue[ctrl->next_event]); | ||
63 | p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); | ||
64 | |||
65 | p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); | ||
66 | |||
67 | ctrl->next_event = (ctrl->next_event + 1) % MAX_EVENTS; | ||
68 | taskInfo->hp_slot = hp_slot; | ||
69 | 67 | ||
70 | rc++; | 68 | p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); |
71 | 69 | ||
72 | /* | 70 | /* |
73 | * Button pressed - See if need to TAKE ACTION!!! | 71 | * Button pressed - See if need to TAKE ACTION!!! |
74 | */ | 72 | */ |
75 | info("Button pressed on Slot(%s)\n", slot_name(p_slot)); | 73 | info("Button pressed on Slot(%s)\n", p_slot->name); |
76 | taskInfo->event_type = INT_BUTTON_PRESS; | 74 | event_type = INT_BUTTON_PRESS; |
77 | |||
78 | if ((p_slot->state == BLINKINGON_STATE) | ||
79 | || (p_slot->state == BLINKINGOFF_STATE)) { | ||
80 | /* Cancel if we are still blinking; this means that we press the | ||
81 | * attention again before the 5 sec. limit expires to cancel hot-add | ||
82 | * or hot-remove | ||
83 | */ | ||
84 | taskInfo->event_type = INT_BUTTON_CANCEL; | ||
85 | info("Button cancel on Slot(%s)\n", slot_name(p_slot)); | ||
86 | } else if ((p_slot->state == POWERON_STATE) | ||
87 | || (p_slot->state == POWEROFF_STATE)) { | ||
88 | /* Ignore if the slot is on power-on or power-off state; this | ||
89 | * means that the previous attention button action to hot-add or | ||
90 | * hot-remove is undergoing | ||
91 | */ | ||
92 | taskInfo->event_type = INT_BUTTON_IGNORE; | ||
93 | info("Button ignore on Slot(%s)\n", slot_name(p_slot)); | ||
94 | } | ||
95 | 75 | ||
96 | if (rc) | 76 | queue_interrupt_event(p_slot, event_type); |
97 | up(&event_semaphore); /* signal event thread that new event is posted */ | ||
98 | 77 | ||
99 | return 0; | 78 | return 0; |
100 | |||
101 | } | 79 | } |
102 | 80 | ||
103 | u8 pciehp_handle_switch_change(u8 hp_slot, struct controller *ctrl) | 81 | u8 pciehp_handle_switch_change(u8 hp_slot, struct controller *ctrl) |
104 | { | 82 | { |
105 | struct slot *p_slot; | 83 | struct slot *p_slot; |
106 | u8 rc = 0; | ||
107 | u8 getstatus; | 84 | u8 getstatus; |
108 | struct event_info *taskInfo; | 85 | u32 event_type; |
109 | 86 | ||
110 | /* Switch Change */ | 87 | /* Switch Change */ |
111 | dbg("pciehp: Switch interrupt received.\n"); | 88 | dbg("pciehp: Switch interrupt received.\n"); |
112 | 89 | ||
113 | /* This is the structure that tells the worker thread | ||
114 | * what to do | ||
115 | */ | ||
116 | taskInfo = &(ctrl->event_queue[ctrl->next_event]); | ||
117 | ctrl->next_event = (ctrl->next_event + 1) % MAX_EVENTS; | ||
118 | taskInfo->hp_slot = hp_slot; | ||
119 | |||
120 | rc++; | ||
121 | p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); | 90 | p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); |
122 | p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); | 91 | p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); |
123 | 92 | ||
@@ -125,39 +94,30 @@ u8 pciehp_handle_switch_change(u8 hp_slot, struct controller *ctrl) | |||
125 | /* | 94 | /* |
126 | * Switch opened | 95 | * Switch opened |
127 | */ | 96 | */ |
128 | info("Latch open on Slot(%s)\n", slot_name(p_slot)); | 97 | info("Latch open on Slot(%s)\n", p_slot->name); |
129 | taskInfo->event_type = INT_SWITCH_OPEN; | 98 | event_type = INT_SWITCH_OPEN; |
130 | } else { | 99 | } else { |
131 | /* | 100 | /* |
132 | * Switch closed | 101 | * Switch closed |
133 | */ | 102 | */ |
134 | info("Latch close on Slot(%s)\n", slot_name(p_slot)); | 103 | info("Latch close on Slot(%s)\n", p_slot->name); |
135 | taskInfo->event_type = INT_SWITCH_CLOSE; | 104 | event_type = INT_SWITCH_CLOSE; |
136 | } | 105 | } |
137 | 106 | ||
138 | if (rc) | 107 | queue_interrupt_event(p_slot, event_type); |
139 | up(&event_semaphore); /* signal event thread that new event is posted */ | ||
140 | 108 | ||
141 | return rc; | 109 | return 1; |
142 | } | 110 | } |
143 | 111 | ||
144 | u8 pciehp_handle_presence_change(u8 hp_slot, struct controller *ctrl) | 112 | u8 pciehp_handle_presence_change(u8 hp_slot, struct controller *ctrl) |
145 | { | 113 | { |
146 | struct slot *p_slot; | 114 | struct slot *p_slot; |
147 | u8 presence_save, rc = 0; | 115 | u32 event_type; |
148 | struct event_info *taskInfo; | 116 | u8 presence_save; |
149 | 117 | ||
150 | /* Presence Change */ | 118 | /* Presence Change */ |
151 | dbg("pciehp: Presence/Notify input change.\n"); | 119 | dbg("pciehp: Presence/Notify input change.\n"); |
152 | 120 | ||
153 | /* This is the structure that tells the worker thread | ||
154 | * what to do | ||
155 | */ | ||
156 | taskInfo = &(ctrl->event_queue[ctrl->next_event]); | ||
157 | ctrl->next_event = (ctrl->next_event + 1) % MAX_EVENTS; | ||
158 | taskInfo->hp_slot = hp_slot; | ||
159 | |||
160 | rc++; | ||
161 | p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); | 121 | p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); |
162 | 122 | ||
163 | /* Switch is open, assume a presence change | 123 | /* Switch is open, assume a presence change |
@@ -168,59 +128,49 @@ u8 pciehp_handle_presence_change(u8 hp_slot, struct controller *ctrl) | |||
168 | /* | 128 | /* |
169 | * Card Present | 129 | * Card Present |
170 | */ | 130 | */ |
171 | info("Card present on Slot(%s)\n", slot_name(p_slot)); | 131 | info("Card present on Slot(%s)\n", p_slot->name); |
172 | taskInfo->event_type = INT_PRESENCE_ON; | 132 | event_type = INT_PRESENCE_ON; |
173 | } else { | 133 | } else { |
174 | /* | 134 | /* |
175 | * Not Present | 135 | * Not Present |
176 | */ | 136 | */ |
177 | info("Card not present on Slot(%s)\n", slot_name(p_slot)); | 137 | info("Card not present on Slot(%s)\n", p_slot->name); |
178 | taskInfo->event_type = INT_PRESENCE_OFF; | 138 | event_type = INT_PRESENCE_OFF; |
179 | } | 139 | } |
180 | 140 | ||
181 | if (rc) | 141 | queue_interrupt_event(p_slot, event_type); |
182 | up(&event_semaphore); /* signal event thread that new event is posted */ | ||
183 | 142 | ||
184 | return rc; | 143 | return 1; |
185 | } | 144 | } |
186 | 145 | ||
187 | u8 pciehp_handle_power_fault(u8 hp_slot, struct controller *ctrl) | 146 | u8 pciehp_handle_power_fault(u8 hp_slot, struct controller *ctrl) |
188 | { | 147 | { |
189 | struct slot *p_slot; | 148 | struct slot *p_slot; |
190 | u8 rc = 0; | 149 | u32 event_type; |
191 | struct event_info *taskInfo; | ||
192 | 150 | ||
193 | /* power fault */ | 151 | /* power fault */ |
194 | dbg("pciehp: Power fault interrupt received.\n"); | 152 | dbg("pciehp: Power fault interrupt received.\n"); |
195 | 153 | ||
196 | /* this is the structure that tells the worker thread | ||
197 | * what to do | ||
198 | */ | ||
199 | taskInfo = &(ctrl->event_queue[ctrl->next_event]); | ||
200 | ctrl->next_event = (ctrl->next_event + 1) % MAX_EVENTS; | ||
201 | taskInfo->hp_slot = hp_slot; | ||
202 | |||
203 | rc++; | ||
204 | p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); | 154 | p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); |
205 | 155 | ||
206 | if ( !(p_slot->hpc_ops->query_power_fault(p_slot))) { | 156 | if ( !(p_slot->hpc_ops->query_power_fault(p_slot))) { |
207 | /* | 157 | /* |
208 | * power fault Cleared | 158 | * power fault Cleared |
209 | */ | 159 | */ |
210 | info("Power fault cleared on Slot(%s)\n", slot_name(p_slot)); | 160 | info("Power fault cleared on Slot(%s)\n", p_slot->name); |
211 | taskInfo->event_type = INT_POWER_FAULT_CLEAR; | 161 | event_type = INT_POWER_FAULT_CLEAR; |
212 | } else { | 162 | } else { |
213 | /* | 163 | /* |
214 | * power fault | 164 | * power fault |
215 | */ | 165 | */ |
216 | info("Power fault on Slot(%s)\n", slot_name(p_slot)); | 166 | info("Power fault on Slot(%s)\n", p_slot->name); |
217 | taskInfo->event_type = INT_POWER_FAULT; | 167 | event_type = INT_POWER_FAULT; |
218 | info("power fault bit %x set\n", hp_slot); | 168 | info("power fault bit %x set\n", hp_slot); |
219 | } | 169 | } |
220 | if (rc) | ||
221 | up(&event_semaphore); /* signal event thread that new event is posted */ | ||
222 | 170 | ||
223 | return rc; | 171 | queue_interrupt_event(p_slot, event_type); |
172 | |||
173 | return 1; | ||
224 | } | 174 | } |
225 | 175 | ||
226 | /* The following routines constitute the bulk of the | 176 | /* The following routines constitute the bulk of the |
@@ -357,13 +307,10 @@ static int remove_board(struct slot *p_slot) | |||
357 | return 0; | 307 | return 0; |
358 | } | 308 | } |
359 | 309 | ||
360 | 310 | struct power_work_info { | |
361 | static void pushbutton_helper_thread(unsigned long data) | 311 | struct slot *p_slot; |
362 | { | 312 | struct work_struct work; |
363 | pushbutton_pending = data; | 313 | }; |
364 | |||
365 | up(&event_semaphore); | ||
366 | } | ||
367 | 314 | ||
368 | /** | 315 | /** |
369 | * pciehp_pushbutton_thread | 316 | * pciehp_pushbutton_thread |
@@ -372,276 +319,214 @@ static void pushbutton_helper_thread(unsigned long data) | |||
372 | * Handles all pending events and exits. | 319 | * Handles all pending events and exits. |
373 | * | 320 | * |
374 | */ | 321 | */ |
375 | static void pciehp_pushbutton_thread(unsigned long slot) | 322 | static void pciehp_power_thread(struct work_struct *work) |
376 | { | 323 | { |
377 | struct slot *p_slot = (struct slot *) slot; | 324 | struct power_work_info *info = |
378 | u8 getstatus; | 325 | container_of(work, struct power_work_info, work); |
379 | 326 | struct slot *p_slot = info->p_slot; | |
380 | pushbutton_pending = 0; | 327 | |
381 | 328 | mutex_lock(&p_slot->lock); | |
382 | if (!p_slot) { | 329 | switch (p_slot->state) { |
383 | dbg("%s: Error! slot NULL\n", __FUNCTION__); | 330 | case POWEROFF_STATE: |
384 | return; | 331 | mutex_unlock(&p_slot->lock); |
385 | } | 332 | dbg("%s: disabling bus:device(%x:%x)\n", |
386 | 333 | __FUNCTION__, p_slot->bus, p_slot->device); | |
387 | p_slot->hpc_ops->get_power_status(p_slot, &getstatus); | ||
388 | if (getstatus) { | ||
389 | p_slot->state = POWEROFF_STATE; | ||
390 | dbg("%s: disabling bus:device(%x:%x)\n", __FUNCTION__, | ||
391 | p_slot->bus, p_slot->device); | ||
392 | |||
393 | pciehp_disable_slot(p_slot); | 334 | pciehp_disable_slot(p_slot); |
335 | mutex_lock(&p_slot->lock); | ||
394 | p_slot->state = STATIC_STATE; | 336 | p_slot->state = STATIC_STATE; |
395 | } else { | 337 | break; |
396 | p_slot->state = POWERON_STATE; | 338 | case POWERON_STATE: |
397 | dbg("%s: adding bus:device(%x:%x)\n", __FUNCTION__, | 339 | mutex_unlock(&p_slot->lock); |
398 | p_slot->bus, p_slot->device); | ||
399 | |||
400 | if (pciehp_enable_slot(p_slot) && | 340 | if (pciehp_enable_slot(p_slot) && |
401 | PWR_LED(p_slot->ctrl->ctrlcap)) | 341 | PWR_LED(p_slot->ctrl->ctrlcap)) |
402 | p_slot->hpc_ops->green_led_off(p_slot); | 342 | p_slot->hpc_ops->green_led_off(p_slot); |
403 | 343 | mutex_lock(&p_slot->lock); | |
404 | p_slot->state = STATIC_STATE; | 344 | p_slot->state = STATIC_STATE; |
345 | break; | ||
346 | default: | ||
347 | break; | ||
405 | } | 348 | } |
349 | mutex_unlock(&p_slot->lock); | ||
406 | 350 | ||
407 | return; | 351 | kfree(info); |
408 | } | 352 | } |
409 | 353 | ||
410 | /** | 354 | void queue_pushbutton_work(struct work_struct *work) |
411 | * pciehp_surprise_rm_thread | ||
412 | * | ||
413 | * Scheduled procedure to handle blocking stuff for the surprise removal | ||
414 | * Handles all pending events and exits. | ||
415 | * | ||
416 | */ | ||
417 | static void pciehp_surprise_rm_thread(unsigned long slot) | ||
418 | { | 355 | { |
419 | struct slot *p_slot = (struct slot *) slot; | 356 | struct slot *p_slot = container_of(work, struct slot, work.work); |
420 | u8 getstatus; | 357 | struct power_work_info *info; |
421 | |||
422 | surprise_rm_pending = 0; | ||
423 | 358 | ||
424 | if (!p_slot) { | 359 | info = kmalloc(sizeof(*info), GFP_KERNEL); |
425 | dbg("%s: Error! slot NULL\n", __FUNCTION__); | 360 | if (!info) { |
361 | err("%s: Cannot allocate memory\n", __FUNCTION__); | ||
426 | return; | 362 | return; |
427 | } | 363 | } |
364 | info->p_slot = p_slot; | ||
365 | INIT_WORK(&info->work, pciehp_power_thread); | ||
428 | 366 | ||
429 | p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus); | 367 | mutex_lock(&p_slot->lock); |
430 | if (!getstatus) { | 368 | switch (p_slot->state) { |
369 | case BLINKINGOFF_STATE: | ||
431 | p_slot->state = POWEROFF_STATE; | 370 | p_slot->state = POWEROFF_STATE; |
432 | dbg("%s: removing bus:device(%x:%x)\n", | 371 | break; |
433 | __FUNCTION__, p_slot->bus, p_slot->device); | 372 | case BLINKINGON_STATE: |
434 | |||
435 | pciehp_disable_slot(p_slot); | ||
436 | p_slot->state = STATIC_STATE; | ||
437 | } else { | ||
438 | p_slot->state = POWERON_STATE; | 373 | p_slot->state = POWERON_STATE; |
439 | dbg("%s: adding bus:device(%x:%x)\n", | 374 | break; |
440 | __FUNCTION__, p_slot->bus, p_slot->device); | 375 | default: |
441 | 376 | goto out; | |
442 | if (pciehp_enable_slot(p_slot) && | ||
443 | PWR_LED(p_slot->ctrl->ctrlcap)) | ||
444 | p_slot->hpc_ops->green_led_off(p_slot); | ||
445 | |||
446 | p_slot->state = STATIC_STATE; | ||
447 | } | 377 | } |
448 | 378 | queue_work(pciehp_wq, &info->work); | |
449 | return; | 379 | out: |
380 | mutex_unlock(&p_slot->lock); | ||
450 | } | 381 | } |
451 | 382 | ||
452 | |||
453 | |||
454 | /* this is the main worker thread */ | ||
455 | static int event_thread(void* data) | ||
456 | { | ||
457 | struct controller *ctrl; | ||
458 | lock_kernel(); | ||
459 | daemonize("pciehpd_event"); | ||
460 | |||
461 | unlock_kernel(); | ||
462 | |||
463 | while (1) { | ||
464 | dbg("!!!!event_thread sleeping\n"); | ||
465 | down_interruptible (&event_semaphore); | ||
466 | dbg("event_thread woken finished = %d\n", event_finished); | ||
467 | if (event_finished || signal_pending(current)) | ||
468 | break; | ||
469 | /* Do stuff here */ | ||
470 | if (pushbutton_pending) | ||
471 | pciehp_pushbutton_thread(pushbutton_pending); | ||
472 | else if (surprise_rm_pending) | ||
473 | pciehp_surprise_rm_thread(surprise_rm_pending); | ||
474 | else | ||
475 | for (ctrl = pciehp_ctrl_list; ctrl; ctrl=ctrl->next) | ||
476 | interrupt_event_handler(ctrl); | ||
477 | } | ||
478 | dbg("event_thread signals exit\n"); | ||
479 | up(&event_exit); | ||
480 | return 0; | ||
481 | } | ||
482 | |||
483 | int pciehp_event_start_thread(void) | ||
484 | { | ||
485 | int pid; | ||
486 | |||
487 | /* initialize our semaphores */ | ||
488 | init_MUTEX_LOCKED(&event_exit); | ||
489 | event_finished=0; | ||
490 | |||
491 | init_MUTEX_LOCKED(&event_semaphore); | ||
492 | pid = kernel_thread(event_thread, NULL, 0); | ||
493 | |||
494 | if (pid < 0) { | ||
495 | err ("Can't start up our event thread\n"); | ||
496 | return -1; | ||
497 | } | ||
498 | return 0; | ||
499 | } | ||
500 | |||
501 | |||
502 | void pciehp_event_stop_thread(void) | ||
503 | { | ||
504 | event_finished = 1; | ||
505 | up(&event_semaphore); | ||
506 | down(&event_exit); | ||
507 | } | ||
508 | |||
509 | |||
510 | static int update_slot_info(struct slot *slot) | 383 | static int update_slot_info(struct slot *slot) |
511 | { | 384 | { |
512 | struct hotplug_slot_info *info; | 385 | struct hotplug_slot_info *info; |
513 | /* char buffer[SLOT_NAME_SIZE]; */ | ||
514 | int result; | 386 | int result; |
515 | 387 | ||
516 | info = kmalloc(sizeof(struct hotplug_slot_info), GFP_KERNEL); | 388 | info = kmalloc(sizeof(*info), GFP_KERNEL); |
517 | if (!info) | 389 | if (!info) |
518 | return -ENOMEM; | 390 | return -ENOMEM; |
519 | 391 | ||
520 | /* make_slot_name (&buffer[0], SLOT_NAME_SIZE, slot); */ | ||
521 | |||
522 | slot->hpc_ops->get_power_status(slot, &(info->power_status)); | 392 | slot->hpc_ops->get_power_status(slot, &(info->power_status)); |
523 | slot->hpc_ops->get_attention_status(slot, &(info->attention_status)); | 393 | slot->hpc_ops->get_attention_status(slot, &(info->attention_status)); |
524 | slot->hpc_ops->get_latch_status(slot, &(info->latch_status)); | 394 | slot->hpc_ops->get_latch_status(slot, &(info->latch_status)); |
525 | slot->hpc_ops->get_adapter_status(slot, &(info->adapter_status)); | 395 | slot->hpc_ops->get_adapter_status(slot, &(info->adapter_status)); |
526 | 396 | ||
527 | /* result = pci_hp_change_slot_info(buffer, info); */ | ||
528 | result = pci_hp_change_slot_info(slot->hotplug_slot, info); | 397 | result = pci_hp_change_slot_info(slot->hotplug_slot, info); |
529 | kfree (info); | 398 | kfree (info); |
530 | return result; | 399 | return result; |
531 | } | 400 | } |
532 | 401 | ||
533 | static void interrupt_event_handler(struct controller *ctrl) | 402 | /* |
403 | * Note: This function must be called with slot->lock held | ||
404 | */ | ||
405 | static void handle_button_press_event(struct slot *p_slot) | ||
534 | { | 406 | { |
535 | int loop = 0; | 407 | struct controller *ctrl = p_slot->ctrl; |
536 | int change = 1; | ||
537 | u8 hp_slot; | ||
538 | u8 getstatus; | 408 | u8 getstatus; |
539 | struct slot *p_slot; | ||
540 | 409 | ||
541 | while (change) { | 410 | switch (p_slot->state) { |
542 | change = 0; | 411 | case STATIC_STATE: |
543 | 412 | p_slot->hpc_ops->get_power_status(p_slot, &getstatus); | |
544 | for (loop = 0; loop < MAX_EVENTS; loop++) { | 413 | if (getstatus) { |
545 | if (ctrl->event_queue[loop].event_type != 0) { | 414 | p_slot->state = BLINKINGOFF_STATE; |
546 | hp_slot = ctrl->event_queue[loop].hp_slot; | 415 | info("PCI slot #%s - powering off due to button " |
547 | 416 | "press.\n", p_slot->name); | |
548 | p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); | 417 | } else { |
549 | 418 | p_slot->state = BLINKINGON_STATE; | |
550 | if (ctrl->event_queue[loop].event_type == INT_BUTTON_CANCEL) { | 419 | info("PCI slot #%s - powering on due to button " |
551 | dbg("button cancel\n"); | 420 | "press.\n", p_slot->name); |
552 | del_timer(&p_slot->task_event); | 421 | } |
553 | 422 | /* blink green LED and turn off amber */ | |
554 | switch (p_slot->state) { | 423 | if (PWR_LED(ctrl->ctrlcap)) |
555 | case BLINKINGOFF_STATE: | 424 | p_slot->hpc_ops->green_led_blink(p_slot); |
556 | if (PWR_LED(ctrl->ctrlcap)) | 425 | if (ATTN_LED(ctrl->ctrlcap)) |
557 | p_slot->hpc_ops->green_led_on(p_slot); | 426 | p_slot->hpc_ops->set_attention_status(p_slot, 0); |
558 | 427 | ||
559 | if (ATTN_LED(ctrl->ctrlcap)) | 428 | schedule_delayed_work(&p_slot->work, 5*HZ); |
560 | p_slot->hpc_ops->set_attention_status(p_slot, 0); | 429 | break; |
561 | break; | 430 | case BLINKINGOFF_STATE: |
562 | case BLINKINGON_STATE: | 431 | case BLINKINGON_STATE: |
563 | if (PWR_LED(ctrl->ctrlcap)) | 432 | /* |
564 | p_slot->hpc_ops->green_led_off(p_slot); | 433 | * Cancel if we are still blinking; this means that we |
565 | 434 | * press the attention again before the 5 sec. limit | |
566 | if (ATTN_LED(ctrl->ctrlcap)) | 435 | * expires to cancel hot-add or hot-remove |
567 | p_slot->hpc_ops->set_attention_status(p_slot, 0); | 436 | */ |
568 | break; | 437 | info("Button cancel on Slot(%s)\n", p_slot->name); |
569 | default: | 438 | dbg("%s: button cancel\n", __FUNCTION__); |
570 | warn("Not a valid state\n"); | 439 | cancel_delayed_work(&p_slot->work); |
571 | return; | 440 | if (p_slot->state == BLINKINGOFF_STATE) { |
572 | } | 441 | if (PWR_LED(ctrl->ctrlcap)) |
573 | info("PCI slot #%s - action canceled due to button press.\n", slot_name(p_slot)); | 442 | p_slot->hpc_ops->green_led_on(p_slot); |
574 | p_slot->state = STATIC_STATE; | 443 | } else { |
575 | } | 444 | if (PWR_LED(ctrl->ctrlcap)) |
576 | /* ***********Button Pressed (No action on 1st press...) */ | 445 | p_slot->hpc_ops->green_led_off(p_slot); |
577 | else if (ctrl->event_queue[loop].event_type == INT_BUTTON_PRESS) { | 446 | } |
578 | 447 | if (ATTN_LED(ctrl->ctrlcap)) | |
579 | if (ATTN_BUTTN(ctrl->ctrlcap)) { | 448 | p_slot->hpc_ops->set_attention_status(p_slot, 0); |
580 | dbg("Button pressed\n"); | 449 | info("PCI slot #%s - action canceled due to button press\n", |
581 | p_slot->hpc_ops->get_power_status(p_slot, &getstatus); | 450 | p_slot->name); |
582 | if (getstatus) { | 451 | p_slot->state = STATIC_STATE; |
583 | /* slot is on */ | 452 | break; |
584 | dbg("slot is on\n"); | 453 | case POWEROFF_STATE: |
585 | p_slot->state = BLINKINGOFF_STATE; | 454 | case POWERON_STATE: |
586 | info("PCI slot #%s - powering off due to button press.\n", slot_name(p_slot)); | 455 | /* |
587 | } else { | 456 | * Ignore if the slot is on power-on or power-off state; |
588 | /* slot is off */ | 457 | * this means that the previous attention button action |
589 | dbg("slot is off\n"); | 458 | * to hot-add or hot-remove is undergoing |
590 | p_slot->state = BLINKINGON_STATE; | 459 | */ |
591 | info("PCI slot #%s - powering on due to button press.\n", slot_name(p_slot)); | 460 | info("Button ignore on Slot(%s)\n", p_slot->name); |
592 | } | 461 | update_slot_info(p_slot); |
593 | 462 | break; | |
594 | /* blink green LED and turn off amber */ | 463 | default: |
595 | if (PWR_LED(ctrl->ctrlcap)) | 464 | warn("Not a valid state\n"); |
596 | p_slot->hpc_ops->green_led_blink(p_slot); | 465 | break; |
597 | |||
598 | if (ATTN_LED(ctrl->ctrlcap)) | ||
599 | p_slot->hpc_ops->set_attention_status(p_slot, 0); | ||
600 | |||
601 | init_timer(&p_slot->task_event); | ||
602 | p_slot->task_event.expires = jiffies + 5 * HZ; /* 5 second delay */ | ||
603 | p_slot->task_event.function = (void (*)(unsigned long)) pushbutton_helper_thread; | ||
604 | p_slot->task_event.data = (unsigned long) p_slot; | ||
605 | |||
606 | add_timer(&p_slot->task_event); | ||
607 | } | ||
608 | } | ||
609 | /***********POWER FAULT********************/ | ||
610 | else if (ctrl->event_queue[loop].event_type == INT_POWER_FAULT) { | ||
611 | if (POWER_CTRL(ctrl->ctrlcap)) { | ||
612 | dbg("power fault\n"); | ||
613 | if (ATTN_LED(ctrl->ctrlcap)) | ||
614 | p_slot->hpc_ops->set_attention_status(p_slot, 1); | ||
615 | |||
616 | if (PWR_LED(ctrl->ctrlcap)) | ||
617 | p_slot->hpc_ops->green_led_off(p_slot); | ||
618 | } | ||
619 | } | ||
620 | /***********SURPRISE REMOVAL********************/ | ||
621 | else if ((ctrl->event_queue[loop].event_type == INT_PRESENCE_ON) || | ||
622 | (ctrl->event_queue[loop].event_type == INT_PRESENCE_OFF)) { | ||
623 | if (HP_SUPR_RM(ctrl->ctrlcap)) { | ||
624 | dbg("Surprise Removal\n"); | ||
625 | if (p_slot) { | ||
626 | surprise_rm_pending = (unsigned long) p_slot; | ||
627 | up(&event_semaphore); | ||
628 | update_slot_info(p_slot); | ||
629 | } | ||
630 | } | ||
631 | } else { | ||
632 | /* refresh notification */ | ||
633 | if (p_slot) | ||
634 | update_slot_info(p_slot); | ||
635 | } | ||
636 | |||
637 | ctrl->event_queue[loop].event_type = 0; | ||
638 | |||
639 | change = 1; | ||
640 | } | ||
641 | } /* End of FOR loop */ | ||
642 | } | 466 | } |
643 | } | 467 | } |
644 | 468 | ||
469 | /* | ||
470 | * Note: This function must be called with slot->lock held | ||
471 | */ | ||
472 | static void handle_surprise_event(struct slot *p_slot) | ||
473 | { | ||
474 | u8 getstatus; | ||
475 | struct power_work_info *info; | ||
476 | |||
477 | info = kmalloc(sizeof(*info), GFP_KERNEL); | ||
478 | if (!info) { | ||
479 | err("%s: Cannot allocate memory\n", __FUNCTION__); | ||
480 | return; | ||
481 | } | ||
482 | info->p_slot = p_slot; | ||
483 | INIT_WORK(&info->work, pciehp_power_thread); | ||
484 | |||
485 | p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus); | ||
486 | if (!getstatus) | ||
487 | p_slot->state = POWEROFF_STATE; | ||
488 | else | ||
489 | p_slot->state = POWERON_STATE; | ||
490 | |||
491 | queue_work(pciehp_wq, &info->work); | ||
492 | } | ||
493 | |||
494 | static void interrupt_event_handler(struct work_struct *work) | ||
495 | { | ||
496 | struct event_info *info = container_of(work, struct event_info, work); | ||
497 | struct slot *p_slot = info->p_slot; | ||
498 | struct controller *ctrl = p_slot->ctrl; | ||
499 | |||
500 | mutex_lock(&p_slot->lock); | ||
501 | switch (info->event_type) { | ||
502 | case INT_BUTTON_PRESS: | ||
503 | handle_button_press_event(p_slot); | ||
504 | break; | ||
505 | case INT_POWER_FAULT: | ||
506 | if (!POWER_CTRL(ctrl->ctrlcap)) | ||
507 | break; | ||
508 | if (ATTN_LED(ctrl->ctrlcap)) | ||
509 | p_slot->hpc_ops->set_attention_status(p_slot, 1); | ||
510 | if (PWR_LED(ctrl->ctrlcap)) | ||
511 | p_slot->hpc_ops->green_led_off(p_slot); | ||
512 | break; | ||
513 | case INT_PRESENCE_ON: | ||
514 | case INT_PRESENCE_OFF: | ||
515 | if (!HP_SUPR_RM(ctrl->ctrlcap)) | ||
516 | break; | ||
517 | dbg("Surprise Removal\n"); | ||
518 | update_slot_info(p_slot); | ||
519 | handle_surprise_event(p_slot); | ||
520 | break; | ||
521 | default: | ||
522 | update_slot_info(p_slot); | ||
523 | break; | ||
524 | } | ||
525 | mutex_unlock(&p_slot->lock); | ||
526 | |||
527 | kfree(info); | ||
528 | } | ||
529 | |||
645 | int pciehp_enable_slot(struct slot *p_slot) | 530 | int pciehp_enable_slot(struct slot *p_slot) |
646 | { | 531 | { |
647 | u8 getstatus = 0; | 532 | u8 getstatus = 0; |
@@ -653,7 +538,7 @@ int pciehp_enable_slot(struct slot *p_slot) | |||
653 | rc = p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus); | 538 | rc = p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus); |
654 | if (rc || !getstatus) { | 539 | if (rc || !getstatus) { |
655 | info("%s: no adapter on slot(%s)\n", __FUNCTION__, | 540 | info("%s: no adapter on slot(%s)\n", __FUNCTION__, |
656 | slot_name(p_slot)); | 541 | p_slot->name); |
657 | mutex_unlock(&p_slot->ctrl->crit_sect); | 542 | mutex_unlock(&p_slot->ctrl->crit_sect); |
658 | return -ENODEV; | 543 | return -ENODEV; |
659 | } | 544 | } |
@@ -661,7 +546,7 @@ int pciehp_enable_slot(struct slot *p_slot) | |||
661 | rc = p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); | 546 | rc = p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); |
662 | if (rc || getstatus) { | 547 | if (rc || getstatus) { |
663 | info("%s: latch open on slot(%s)\n", __FUNCTION__, | 548 | info("%s: latch open on slot(%s)\n", __FUNCTION__, |
664 | slot_name(p_slot)); | 549 | p_slot->name); |
665 | mutex_unlock(&p_slot->ctrl->crit_sect); | 550 | mutex_unlock(&p_slot->ctrl->crit_sect); |
666 | return -ENODEV; | 551 | return -ENODEV; |
667 | } | 552 | } |
@@ -671,7 +556,7 @@ int pciehp_enable_slot(struct slot *p_slot) | |||
671 | rc = p_slot->hpc_ops->get_power_status(p_slot, &getstatus); | 556 | rc = p_slot->hpc_ops->get_power_status(p_slot, &getstatus); |
672 | if (rc || getstatus) { | 557 | if (rc || getstatus) { |
673 | info("%s: already enabled on slot(%s)\n", __FUNCTION__, | 558 | info("%s: already enabled on slot(%s)\n", __FUNCTION__, |
674 | slot_name(p_slot)); | 559 | p_slot->name); |
675 | mutex_unlock(&p_slot->ctrl->crit_sect); | 560 | mutex_unlock(&p_slot->ctrl->crit_sect); |
676 | return -EINVAL; | 561 | return -EINVAL; |
677 | } | 562 | } |
@@ -706,7 +591,7 @@ int pciehp_disable_slot(struct slot *p_slot) | |||
706 | ret = p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus); | 591 | ret = p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus); |
707 | if (ret || !getstatus) { | 592 | if (ret || !getstatus) { |
708 | info("%s: no adapter on slot(%s)\n", __FUNCTION__, | 593 | info("%s: no adapter on slot(%s)\n", __FUNCTION__, |
709 | slot_name(p_slot)); | 594 | p_slot->name); |
710 | mutex_unlock(&p_slot->ctrl->crit_sect); | 595 | mutex_unlock(&p_slot->ctrl->crit_sect); |
711 | return -ENODEV; | 596 | return -ENODEV; |
712 | } | 597 | } |
@@ -716,7 +601,7 @@ int pciehp_disable_slot(struct slot *p_slot) | |||
716 | ret = p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); | 601 | ret = p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); |
717 | if (ret || getstatus) { | 602 | if (ret || getstatus) { |
718 | info("%s: latch open on slot(%s)\n", __FUNCTION__, | 603 | info("%s: latch open on slot(%s)\n", __FUNCTION__, |
719 | slot_name(p_slot)); | 604 | p_slot->name); |
720 | mutex_unlock(&p_slot->ctrl->crit_sect); | 605 | mutex_unlock(&p_slot->ctrl->crit_sect); |
721 | return -ENODEV; | 606 | return -ENODEV; |
722 | } | 607 | } |
@@ -726,7 +611,7 @@ int pciehp_disable_slot(struct slot *p_slot) | |||
726 | ret = p_slot->hpc_ops->get_power_status(p_slot, &getstatus); | 611 | ret = p_slot->hpc_ops->get_power_status(p_slot, &getstatus); |
727 | if (ret || !getstatus) { | 612 | if (ret || !getstatus) { |
728 | info("%s: already disabled slot(%s)\n", __FUNCTION__, | 613 | info("%s: already disabled slot(%s)\n", __FUNCTION__, |
729 | slot_name(p_slot)); | 614 | p_slot->name); |
730 | mutex_unlock(&p_slot->ctrl->crit_sect); | 615 | mutex_unlock(&p_slot->ctrl->crit_sect); |
731 | return -EINVAL; | 616 | return -EINVAL; |
732 | } | 617 | } |
@@ -739,3 +624,66 @@ int pciehp_disable_slot(struct slot *p_slot) | |||
739 | return ret; | 624 | return ret; |
740 | } | 625 | } |
741 | 626 | ||
627 | int pciehp_sysfs_enable_slot(struct slot *p_slot) | ||
628 | { | ||
629 | int retval = -ENODEV; | ||
630 | |||
631 | mutex_lock(&p_slot->lock); | ||
632 | switch (p_slot->state) { | ||
633 | case BLINKINGON_STATE: | ||
634 | cancel_delayed_work(&p_slot->work); | ||
635 | case STATIC_STATE: | ||
636 | p_slot->state = POWERON_STATE; | ||
637 | mutex_unlock(&p_slot->lock); | ||
638 | retval = pciehp_enable_slot(p_slot); | ||
639 | mutex_lock(&p_slot->lock); | ||
640 | p_slot->state = STATIC_STATE; | ||
641 | break; | ||
642 | case POWERON_STATE: | ||
643 | info("Slot %s is already in powering on state\n", | ||
644 | p_slot->name); | ||
645 | break; | ||
646 | case BLINKINGOFF_STATE: | ||
647 | case POWEROFF_STATE: | ||
648 | info("Already enabled on slot %s\n", p_slot->name); | ||
649 | break; | ||
650 | default: | ||
651 | err("Not a valid state on slot %s\n", p_slot->name); | ||
652 | break; | ||
653 | } | ||
654 | mutex_unlock(&p_slot->lock); | ||
655 | |||
656 | return retval; | ||
657 | } | ||
658 | |||
659 | int pciehp_sysfs_disable_slot(struct slot *p_slot) | ||
660 | { | ||
661 | int retval = -ENODEV; | ||
662 | |||
663 | mutex_lock(&p_slot->lock); | ||
664 | switch (p_slot->state) { | ||
665 | case BLINKINGOFF_STATE: | ||
666 | cancel_delayed_work(&p_slot->work); | ||
667 | case STATIC_STATE: | ||
668 | p_slot->state = POWEROFF_STATE; | ||
669 | mutex_unlock(&p_slot->lock); | ||
670 | retval = pciehp_disable_slot(p_slot); | ||
671 | mutex_lock(&p_slot->lock); | ||
672 | p_slot->state = STATIC_STATE; | ||
673 | break; | ||
674 | case POWEROFF_STATE: | ||
675 | info("Slot %s is already in powering off state\n", | ||
676 | p_slot->name); | ||
677 | break; | ||
678 | case BLINKINGON_STATE: | ||
679 | case POWERON_STATE: | ||
680 | info("Already disabled on slot %s\n", p_slot->name); | ||
681 | break; | ||
682 | default: | ||
683 | err("Not a valid state on slot %s\n", p_slot->name); | ||
684 | break; | ||
685 | } | ||
686 | mutex_unlock(&p_slot->lock); | ||
687 | |||
688 | return retval; | ||
689 | } | ||
diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index fbc64aa2dd68..9aac6a87eb53 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c | |||
@@ -71,6 +71,8 @@ | |||
71 | #define DBG_LEAVE_ROUTINE | 71 | #define DBG_LEAVE_ROUTINE |
72 | #endif /* DEBUG */ | 72 | #endif /* DEBUG */ |
73 | 73 | ||
74 | static atomic_t pciehp_num_controllers = ATOMIC_INIT(0); | ||
75 | |||
74 | struct ctrl_reg { | 76 | struct ctrl_reg { |
75 | u8 cap_id; | 77 | u8 cap_id; |
76 | u8 nxt_ptr; | 78 | u8 nxt_ptr; |
@@ -219,10 +221,7 @@ static inline int pciehp_writel(struct controller *ctrl, int reg, u32 value) | |||
219 | #define EMI_STATE 0x0080 | 221 | #define EMI_STATE 0x0080 |
220 | #define EMI_STATUS_BIT 7 | 222 | #define EMI_STATUS_BIT 7 |
221 | 223 | ||
222 | static spinlock_t hpc_event_lock; | ||
223 | |||
224 | DEFINE_DBG_BUFFER /* Debug string buffer for entire HPC defined here */ | 224 | DEFINE_DBG_BUFFER /* Debug string buffer for entire HPC defined here */ |
225 | static int ctlr_seq_num = 0; /* Controller sequence # */ | ||
226 | 225 | ||
227 | static irqreturn_t pcie_isr(int irq, void *dev_id); | 226 | static irqreturn_t pcie_isr(int irq, void *dev_id); |
228 | static void start_int_poll_timer(struct controller *ctrl, int sec); | 227 | static void start_int_poll_timer(struct controller *ctrl, int sec); |
@@ -656,6 +655,13 @@ static void hpc_release_ctlr(struct controller *ctrl) | |||
656 | else | 655 | else |
657 | free_irq(ctrl->pci_dev->irq, ctrl); | 656 | free_irq(ctrl->pci_dev->irq, ctrl); |
658 | 657 | ||
658 | /* | ||
659 | * If this is the last controller to be released, destroy the | ||
660 | * pciehp work queue | ||
661 | */ | ||
662 | if (atomic_dec_and_test(&pciehp_num_controllers)) | ||
663 | destroy_workqueue(pciehp_wq); | ||
664 | |||
659 | DBG_LEAVE_ROUTINE | 665 | DBG_LEAVE_ROUTINE |
660 | } | 666 | } |
661 | 667 | ||
@@ -1152,7 +1158,6 @@ int pciehp_acpi_get_hp_hw_control_from_firmware(struct pci_dev *dev) | |||
1152 | int pcie_init(struct controller * ctrl, struct pcie_device *dev) | 1158 | int pcie_init(struct controller * ctrl, struct pcie_device *dev) |
1153 | { | 1159 | { |
1154 | int rc; | 1160 | int rc; |
1155 | static int first = 1; | ||
1156 | u16 temp_word; | 1161 | u16 temp_word; |
1157 | u16 cap_reg; | 1162 | u16 cap_reg; |
1158 | u16 intr_enable = 0; | 1163 | u16 intr_enable = 0; |
@@ -1221,11 +1226,6 @@ int pcie_init(struct controller * ctrl, struct pcie_device *dev) | |||
1221 | dbg("%s: SLOTCTRL offset %x slot_ctrl %x\n", | 1226 | dbg("%s: SLOTCTRL offset %x slot_ctrl %x\n", |
1222 | __FUNCTION__, ctrl->cap_base + SLOTCTRL, slot_ctrl); | 1227 | __FUNCTION__, ctrl->cap_base + SLOTCTRL, slot_ctrl); |
1223 | 1228 | ||
1224 | if (first) { | ||
1225 | spin_lock_init(&hpc_event_lock); | ||
1226 | first = 0; | ||
1227 | } | ||
1228 | |||
1229 | for ( rc = 0; rc < DEVICE_COUNT_RESOURCE; rc++) | 1229 | for ( rc = 0; rc < DEVICE_COUNT_RESOURCE; rc++) |
1230 | if (pci_resource_len(pdev, rc) > 0) | 1230 | if (pci_resource_len(pdev, rc) > 0) |
1231 | dbg("pci resource[%d] start=0x%llx(len=0x%llx)\n", rc, | 1231 | dbg("pci resource[%d] start=0x%llx(len=0x%llx)\n", rc, |
@@ -1286,7 +1286,8 @@ int pcie_init(struct controller * ctrl, struct pcie_device *dev) | |||
1286 | rc = request_irq(ctrl->pci_dev->irq, pcie_isr, IRQF_SHARED, | 1286 | rc = request_irq(ctrl->pci_dev->irq, pcie_isr, IRQF_SHARED, |
1287 | MY_NAME, (void *)ctrl); | 1287 | MY_NAME, (void *)ctrl); |
1288 | dbg("%s: request_irq %d for hpc%d (returns %d)\n", | 1288 | dbg("%s: request_irq %d for hpc%d (returns %d)\n", |
1289 | __FUNCTION__, ctrl->pci_dev->irq, ctlr_seq_num, rc); | 1289 | __FUNCTION__, ctrl->pci_dev->irq, |
1290 | atomic_read(&pciehp_num_controllers), rc); | ||
1290 | if (rc) { | 1291 | if (rc) { |
1291 | err("Can't get irq %d for the hotplug controller\n", | 1292 | err("Can't get irq %d for the hotplug controller\n", |
1292 | ctrl->pci_dev->irq); | 1293 | ctrl->pci_dev->irq); |
@@ -1296,6 +1297,18 @@ int pcie_init(struct controller * ctrl, struct pcie_device *dev) | |||
1296 | dbg("pciehp ctrl b:d:f:irq=0x%x:%x:%x:%x\n", pdev->bus->number, | 1297 | dbg("pciehp ctrl b:d:f:irq=0x%x:%x:%x:%x\n", pdev->bus->number, |
1297 | PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), dev->irq); | 1298 | PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), dev->irq); |
1298 | 1299 | ||
1300 | /* | ||
1301 | * If this is the first controller to be initialized, | ||
1302 | * initialize the pciehp work queue | ||
1303 | */ | ||
1304 | if (atomic_add_return(1, &pciehp_num_controllers) == 1) { | ||
1305 | pciehp_wq = create_singlethread_workqueue("pciehpd"); | ||
1306 | if (!pciehp_wq) { | ||
1307 | rc = -ENOMEM; | ||
1308 | goto abort_free_irq; | ||
1309 | } | ||
1310 | } | ||
1311 | |||
1299 | rc = pciehp_readw(ctrl, SLOTCTRL, &temp_word); | 1312 | rc = pciehp_readw(ctrl, SLOTCTRL, &temp_word); |
1300 | if (rc) { | 1313 | if (rc) { |
1301 | err("%s: Cannot read SLOTCTRL register\n", __FUNCTION__); | 1314 | err("%s: Cannot read SLOTCTRL register\n", __FUNCTION__); |
@@ -1349,7 +1362,6 @@ int pcie_init(struct controller * ctrl, struct pcie_device *dev) | |||
1349 | goto abort_disable_intr; | 1362 | goto abort_disable_intr; |
1350 | } | 1363 | } |
1351 | 1364 | ||
1352 | ctlr_seq_num++; | ||
1353 | ctrl->hpc_ops = &pciehp_hpc_ops; | 1365 | ctrl->hpc_ops = &pciehp_hpc_ops; |
1354 | 1366 | ||
1355 | DBG_LEAVE_ROUTINE | 1367 | DBG_LEAVE_ROUTINE |