diff options
Diffstat (limited to 'drivers/pci/hotplug/pciehp_ctrl.c')
-rw-r--r-- | drivers/pci/hotplug/pciehp_ctrl.c | 616 |
1 files changed, 282 insertions, 334 deletions
diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c index 4283ef56dbd9..7f22caa70178 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 pciehp_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 | } | ||