]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * PCI Express Hot Plug Controller Driver | |
3 | * | |
4 | * Copyright (C) 1995,2001 Compaq Computer Corporation | |
5 | * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) | |
6 | * Copyright (C) 2001 IBM Corp. | |
7 | * Copyright (C) 2003-2004 Intel Corporation | |
8 | * | |
9 | * All rights reserved. | |
10 | * | |
11 | * This program is free software; you can redistribute it and/or modify | |
12 | * it under the terms of the GNU General Public License as published by | |
13 | * the Free Software Foundation; either version 2 of the License, or (at | |
14 | * your option) any later version. | |
15 | * | |
16 | * This program is distributed in the hope that it will be useful, but | |
17 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
18 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | |
19 | * NON INFRINGEMENT. See the GNU General Public License for more | |
20 | * details. | |
21 | * | |
22 | * You should have received a copy of the GNU General Public License | |
23 | * along with this program; if not, write to the Free Software | |
24 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
25 | * | |
8cf4c195 | 26 | * Send feedback to <greg@kroah.com>, <kristen.c.accardi@intel.com> |
1da177e4 LT |
27 | * |
28 | */ | |
29 | ||
1da177e4 LT |
30 | #include <linux/module.h> |
31 | #include <linux/kernel.h> | |
32 | #include <linux/types.h> | |
1da177e4 LT |
33 | #include <linux/smp_lock.h> |
34 | #include <linux/pci.h> | |
35 | #include "../pci.h" | |
36 | #include "pciehp.h" | |
1da177e4 | 37 | |
1da177e4 LT |
38 | static void interrupt_event_handler(struct controller *ctrl); |
39 | ||
40 | static struct semaphore event_semaphore; /* mutex for process loop (up if something to process) */ | |
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 | ||
49ed2b49 KK |
46 | static inline char *slot_name(struct slot *p_slot) |
47 | { | |
48 | return p_slot->hotplug_slot->name; | |
49 | } | |
50 | ||
48fe3915 | 51 | u8 pciehp_handle_attention_button(u8 hp_slot, struct controller *ctrl) |
1da177e4 | 52 | { |
1da177e4 LT |
53 | struct slot *p_slot; |
54 | u8 rc = 0; | |
55 | u8 getstatus; | |
1da177e4 LT |
56 | struct event_info *taskInfo; |
57 | ||
58 | /* Attention Button Change */ | |
59 | dbg("pciehp: Attention button interrupt received.\n"); | |
60 | ||
1da177e4 LT |
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 | ||
1da177e4 LT |
65 | p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); |
66 | ||
ed6cbcf2 | 67 | ctrl->next_event = (ctrl->next_event + 1) % MAX_EVENTS; |
1da177e4 LT |
68 | taskInfo->hp_slot = hp_slot; |
69 | ||
70 | rc++; | |
71 | ||
72 | /* | |
73 | * Button pressed - See if need to TAKE ACTION!!! | |
74 | */ | |
49ed2b49 | 75 | info("Button pressed on Slot(%s)\n", slot_name(p_slot)); |
1da177e4 LT |
76 | taskInfo->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; | |
49ed2b49 | 85 | info("Button cancel on Slot(%s)\n", slot_name(p_slot)); |
1da177e4 LT |
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; | |
49ed2b49 | 93 | info("Button ignore on Slot(%s)\n", slot_name(p_slot)); |
1da177e4 LT |
94 | } |
95 | ||
96 | if (rc) | |
97 | up(&event_semaphore); /* signal event thread that new event is posted */ | |
98 | ||
99 | return 0; | |
100 | ||
101 | } | |
102 | ||
48fe3915 | 103 | u8 pciehp_handle_switch_change(u8 hp_slot, struct controller *ctrl) |
1da177e4 | 104 | { |
1da177e4 LT |
105 | struct slot *p_slot; |
106 | u8 rc = 0; | |
107 | u8 getstatus; | |
1da177e4 LT |
108 | struct event_info *taskInfo; |
109 | ||
110 | /* Switch Change */ | |
111 | dbg("pciehp: Switch interrupt received.\n"); | |
112 | ||
1da177e4 LT |
113 | /* This is the structure that tells the worker thread |
114 | * what to do | |
115 | */ | |
116 | taskInfo = &(ctrl->event_queue[ctrl->next_event]); | |
ed6cbcf2 | 117 | ctrl->next_event = (ctrl->next_event + 1) % MAX_EVENTS; |
1da177e4 LT |
118 | taskInfo->hp_slot = hp_slot; |
119 | ||
120 | rc++; | |
121 | p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); | |
1da177e4 LT |
122 | p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); |
123 | ||
124 | if (getstatus) { | |
125 | /* | |
126 | * Switch opened | |
127 | */ | |
49ed2b49 | 128 | info("Latch open on Slot(%s)\n", slot_name(p_slot)); |
1da177e4 LT |
129 | taskInfo->event_type = INT_SWITCH_OPEN; |
130 | } else { | |
131 | /* | |
132 | * Switch closed | |
133 | */ | |
49ed2b49 | 134 | info("Latch close on Slot(%s)\n", slot_name(p_slot)); |
1da177e4 LT |
135 | taskInfo->event_type = INT_SWITCH_CLOSE; |
136 | } | |
137 | ||
138 | if (rc) | |
139 | up(&event_semaphore); /* signal event thread that new event is posted */ | |
140 | ||
141 | return rc; | |
142 | } | |
143 | ||
48fe3915 | 144 | u8 pciehp_handle_presence_change(u8 hp_slot, struct controller *ctrl) |
1da177e4 | 145 | { |
1da177e4 | 146 | struct slot *p_slot; |
ed6cbcf2 | 147 | u8 presence_save, rc = 0; |
1da177e4 LT |
148 | struct event_info *taskInfo; |
149 | ||
150 | /* Presence Change */ | |
151 | dbg("pciehp: Presence/Notify input change.\n"); | |
152 | ||
1da177e4 LT |
153 | /* This is the structure that tells the worker thread |
154 | * what to do | |
155 | */ | |
156 | taskInfo = &(ctrl->event_queue[ctrl->next_event]); | |
ed6cbcf2 | 157 | ctrl->next_event = (ctrl->next_event + 1) % MAX_EVENTS; |
1da177e4 LT |
158 | taskInfo->hp_slot = hp_slot; |
159 | ||
160 | rc++; | |
161 | p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); | |
162 | ||
163 | /* Switch is open, assume a presence change | |
164 | * Save the presence state | |
165 | */ | |
ed6cbcf2 RS |
166 | p_slot->hpc_ops->get_adapter_status(p_slot, &presence_save); |
167 | if (presence_save) { | |
1da177e4 LT |
168 | /* |
169 | * Card Present | |
170 | */ | |
49ed2b49 | 171 | info("Card present on Slot(%s)\n", slot_name(p_slot)); |
1da177e4 LT |
172 | taskInfo->event_type = INT_PRESENCE_ON; |
173 | } else { | |
174 | /* | |
175 | * Not Present | |
176 | */ | |
49ed2b49 | 177 | info("Card not present on Slot(%s)\n", slot_name(p_slot)); |
1da177e4 LT |
178 | taskInfo->event_type = INT_PRESENCE_OFF; |
179 | } | |
180 | ||
181 | if (rc) | |
182 | up(&event_semaphore); /* signal event thread that new event is posted */ | |
183 | ||
184 | return rc; | |
185 | } | |
186 | ||
48fe3915 | 187 | u8 pciehp_handle_power_fault(u8 hp_slot, struct controller *ctrl) |
1da177e4 | 188 | { |
1da177e4 LT |
189 | struct slot *p_slot; |
190 | u8 rc = 0; | |
1da177e4 LT |
191 | struct event_info *taskInfo; |
192 | ||
193 | /* power fault */ | |
194 | dbg("pciehp: Power fault interrupt received.\n"); | |
195 | ||
1da177e4 LT |
196 | /* this is the structure that tells the worker thread |
197 | * what to do | |
198 | */ | |
199 | taskInfo = &(ctrl->event_queue[ctrl->next_event]); | |
ed6cbcf2 | 200 | ctrl->next_event = (ctrl->next_event + 1) % MAX_EVENTS; |
1da177e4 LT |
201 | taskInfo->hp_slot = hp_slot; |
202 | ||
203 | rc++; | |
204 | p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); | |
205 | ||
206 | if ( !(p_slot->hpc_ops->query_power_fault(p_slot))) { | |
207 | /* | |
208 | * power fault Cleared | |
209 | */ | |
49ed2b49 | 210 | info("Power fault cleared on Slot(%s)\n", slot_name(p_slot)); |
1da177e4 LT |
211 | taskInfo->event_type = INT_POWER_FAULT_CLEAR; |
212 | } else { | |
213 | /* | |
214 | * power fault | |
215 | */ | |
49ed2b49 | 216 | info("Power fault on Slot(%s)\n", slot_name(p_slot)); |
1da177e4 | 217 | taskInfo->event_type = INT_POWER_FAULT; |
1da177e4 LT |
218 | info("power fault bit %x set\n", hp_slot); |
219 | } | |
220 | if (rc) | |
221 | up(&event_semaphore); /* signal event thread that new event is posted */ | |
222 | ||
223 | return rc; | |
224 | } | |
225 | ||
1da177e4 LT |
226 | /* The following routines constitute the bulk of the |
227 | hotplug controller logic | |
228 | */ | |
229 | ||
230 | static void set_slot_off(struct controller *ctrl, struct slot * pslot) | |
231 | { | |
1da177e4 LT |
232 | /* turn off slot, turn on Amber LED, turn off Green LED if supported*/ |
233 | if (POWER_CTRL(ctrl->ctrlcap)) { | |
234 | if (pslot->hpc_ops->power_off_slot(pslot)) { | |
44ef4cef KK |
235 | err("%s: Issue of Slot Power Off command failed\n", |
236 | __FUNCTION__); | |
1da177e4 LT |
237 | return; |
238 | } | |
1da177e4 LT |
239 | } |
240 | ||
44ef4cef | 241 | if (PWR_LED(ctrl->ctrlcap)) |
1da177e4 | 242 | pslot->hpc_ops->green_led_off(pslot); |
1da177e4 | 243 | |
44ef4cef KK |
244 | if (ATTN_LED(ctrl->ctrlcap)) { |
245 | if (pslot->hpc_ops->set_attention_status(pslot, 1)) { | |
246 | err("%s: Issue of Set Attention Led command failed\n", | |
247 | __FUNCTION__); | |
1da177e4 LT |
248 | return; |
249 | } | |
1da177e4 | 250 | } |
1da177e4 LT |
251 | } |
252 | ||
253 | /** | |
254 | * board_added - Called after a board has been added to the system. | |
255 | * | |
256 | * Turns power on for the board | |
257 | * Configures board | |
258 | * | |
259 | */ | |
ed6cbcf2 | 260 | static int board_added(struct slot *p_slot) |
1da177e4 LT |
261 | { |
262 | u8 hp_slot; | |
44ef4cef | 263 | int retval = 0; |
ca22a5e4 | 264 | struct controller *ctrl = p_slot->ctrl; |
1da177e4 | 265 | |
ca22a5e4 | 266 | hp_slot = p_slot->device - ctrl->slot_device_offset; |
1da177e4 | 267 | |
1a9ed1bf RS |
268 | dbg("%s: slot device, slot offset, hp slot = %d, %d ,%d\n", |
269 | __FUNCTION__, p_slot->device, | |
270 | ctrl->slot_device_offset, hp_slot); | |
1da177e4 | 271 | |
1da177e4 LT |
272 | if (POWER_CTRL(ctrl->ctrlcap)) { |
273 | /* Power on slot */ | |
44ef4cef KK |
274 | retval = p_slot->hpc_ops->power_on_slot(p_slot); |
275 | if (retval) | |
276 | return retval; | |
1da177e4 LT |
277 | } |
278 | ||
44ef4cef | 279 | if (PWR_LED(ctrl->ctrlcap)) |
1da177e4 | 280 | p_slot->hpc_ops->green_led_blink(p_slot); |
1da177e4 LT |
281 | |
282 | /* Wait for ~1 second */ | |
44ef4cef | 283 | msleep(1000); |
1da177e4 | 284 | |
44ef4cef KK |
285 | /* Check link training status */ |
286 | retval = p_slot->hpc_ops->check_lnk_status(ctrl); | |
287 | if (retval) { | |
1da177e4 LT |
288 | err("%s: Failed to check link status\n", __FUNCTION__); |
289 | set_slot_off(ctrl, p_slot); | |
44ef4cef | 290 | return retval; |
1da177e4 LT |
291 | } |
292 | ||
1da177e4 | 293 | /* Check for a power fault */ |
5a49f203 RS |
294 | if (p_slot->hpc_ops->query_power_fault(p_slot)) { |
295 | dbg("%s: power fault detected\n", __FUNCTION__); | |
44ef4cef | 296 | retval = POWER_FAILURE; |
71b720c0 | 297 | goto err_exit; |
1da177e4 LT |
298 | } |
299 | ||
44ef4cef KK |
300 | retval = pciehp_configure_device(p_slot); |
301 | if (retval) { | |
71b720c0 | 302 | err("Cannot add device 0x%x:%x\n", p_slot->bus, |
44ef4cef | 303 | p_slot->device); |
71b720c0 RS |
304 | goto err_exit; |
305 | } | |
1da177e4 | 306 | |
71b720c0 RS |
307 | /* |
308 | * Some PCI Express root ports require fixup after hot-plug operation. | |
309 | */ | |
310 | if (pcie_mch_quirk) | |
311 | pci_fixup_device(pci_fixup_final, ctrl->pci_dev); | |
44ef4cef | 312 | if (PWR_LED(ctrl->ctrlcap)) |
71b720c0 | 313 | p_slot->hpc_ops->green_led_on(p_slot); |
44ef4cef | 314 | |
1da177e4 | 315 | return 0; |
71b720c0 RS |
316 | |
317 | err_exit: | |
318 | set_slot_off(ctrl, p_slot); | |
44ef4cef | 319 | return retval; |
1da177e4 LT |
320 | } |
321 | ||
1da177e4 LT |
322 | /** |
323 | * remove_board - Turns off slot and LED's | |
324 | * | |
325 | */ | |
ed6cbcf2 | 326 | static int remove_board(struct slot *p_slot) |
1da177e4 | 327 | { |
1da177e4 LT |
328 | u8 device; |
329 | u8 hp_slot; | |
44ef4cef | 330 | int retval = 0; |
ca22a5e4 | 331 | struct controller *ctrl = p_slot->ctrl; |
1da177e4 | 332 | |
44ef4cef KK |
333 | retval = pciehp_unconfigure_device(p_slot); |
334 | if (retval) | |
335 | return retval; | |
1da177e4 | 336 | |
ca22a5e4 | 337 | device = p_slot->device; |
ca22a5e4 | 338 | hp_slot = p_slot->device - ctrl->slot_device_offset; |
1da177e4 LT |
339 | p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); |
340 | ||
341 | dbg("In %s, hp_slot = %d\n", __FUNCTION__, hp_slot); | |
342 | ||
1da177e4 LT |
343 | if (POWER_CTRL(ctrl->ctrlcap)) { |
344 | /* power off slot */ | |
44ef4cef KK |
345 | retval = p_slot->hpc_ops->power_off_slot(p_slot); |
346 | if (retval) { | |
347 | err("%s: Issue of Slot Disable command failed\n", | |
348 | __FUNCTION__); | |
349 | return retval; | |
1da177e4 | 350 | } |
1da177e4 LT |
351 | } |
352 | ||
44ef4cef | 353 | if (PWR_LED(ctrl->ctrlcap)) |
1da177e4 LT |
354 | /* turn off Green LED */ |
355 | p_slot->hpc_ops->green_led_off(p_slot); | |
1da177e4 | 356 | |
1da177e4 LT |
357 | return 0; |
358 | } | |
359 | ||
360 | ||
361 | static void pushbutton_helper_thread(unsigned long data) | |
362 | { | |
363 | pushbutton_pending = data; | |
364 | ||
365 | up(&event_semaphore); | |
366 | } | |
367 | ||
368 | /** | |
369 | * pciehp_pushbutton_thread | |
370 | * | |
371 | * Scheduled procedure to handle blocking stuff for the pushbuttons | |
372 | * Handles all pending events and exits. | |
373 | * | |
374 | */ | |
375 | static void pciehp_pushbutton_thread(unsigned long slot) | |
376 | { | |
377 | struct slot *p_slot = (struct slot *) slot; | |
378 | u8 getstatus; | |
379 | ||
380 | pushbutton_pending = 0; | |
381 | ||
382 | if (!p_slot) { | |
383 | dbg("%s: Error! slot NULL\n", __FUNCTION__); | |
384 | return; | |
385 | } | |
386 | ||
387 | p_slot->hpc_ops->get_power_status(p_slot, &getstatus); | |
388 | if (getstatus) { | |
389 | p_slot->state = POWEROFF_STATE; | |
1a9ed1bf RS |
390 | dbg("%s: disabling bus:device(%x:%x)\n", __FUNCTION__, |
391 | p_slot->bus, p_slot->device); | |
1da177e4 LT |
392 | |
393 | pciehp_disable_slot(p_slot); | |
394 | p_slot->state = STATIC_STATE; | |
395 | } else { | |
396 | p_slot->state = POWERON_STATE; | |
1a9ed1bf RS |
397 | dbg("%s: adding bus:device(%x:%x)\n", __FUNCTION__, |
398 | p_slot->bus, p_slot->device); | |
1da177e4 | 399 | |
44ef4cef KK |
400 | if (pciehp_enable_slot(p_slot) && |
401 | PWR_LED(p_slot->ctrl->ctrlcap)) | |
1da177e4 LT |
402 | p_slot->hpc_ops->green_led_off(p_slot); |
403 | ||
1da177e4 LT |
404 | p_slot->state = STATIC_STATE; |
405 | } | |
406 | ||
407 | return; | |
408 | } | |
409 | ||
410 | /** | |
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 | { | |
419 | struct slot *p_slot = (struct slot *) slot; | |
420 | u8 getstatus; | |
421 | ||
422 | surprise_rm_pending = 0; | |
423 | ||
424 | if (!p_slot) { | |
425 | dbg("%s: Error! slot NULL\n", __FUNCTION__); | |
426 | return; | |
427 | } | |
428 | ||
429 | p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus); | |
430 | if (!getstatus) { | |
431 | p_slot->state = POWEROFF_STATE; | |
1a9ed1bf RS |
432 | dbg("%s: removing bus:device(%x:%x)\n", |
433 | __FUNCTION__, p_slot->bus, p_slot->device); | |
1da177e4 LT |
434 | |
435 | pciehp_disable_slot(p_slot); | |
436 | p_slot->state = STATIC_STATE; | |
437 | } else { | |
438 | p_slot->state = POWERON_STATE; | |
1a9ed1bf RS |
439 | dbg("%s: adding bus:device(%x:%x)\n", |
440 | __FUNCTION__, p_slot->bus, p_slot->device); | |
1da177e4 | 441 | |
44ef4cef KK |
442 | if (pciehp_enable_slot(p_slot) && |
443 | PWR_LED(p_slot->ctrl->ctrlcap)) | |
1da177e4 LT |
444 | p_slot->hpc_ops->green_led_off(p_slot); |
445 | ||
1da177e4 LT |
446 | p_slot->state = STATIC_STATE; |
447 | } | |
448 | ||
449 | return; | |
450 | } | |
451 | ||
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 | } | |
1da177e4 LT |
498 | return 0; |
499 | } | |
500 | ||
501 | ||
502 | void pciehp_event_stop_thread(void) | |
503 | { | |
504 | event_finished = 1; | |
1da177e4 | 505 | up(&event_semaphore); |
1da177e4 LT |
506 | down(&event_exit); |
507 | } | |
508 | ||
509 | ||
510 | static int update_slot_info(struct slot *slot) | |
511 | { | |
512 | struct hotplug_slot_info *info; | |
513 | /* char buffer[SLOT_NAME_SIZE]; */ | |
514 | int result; | |
515 | ||
516 | info = kmalloc(sizeof(struct hotplug_slot_info), GFP_KERNEL); | |
517 | if (!info) | |
518 | return -ENOMEM; | |
519 | ||
520 | /* make_slot_name (&buffer[0], SLOT_NAME_SIZE, slot); */ | |
521 | ||
522 | slot->hpc_ops->get_power_status(slot, &(info->power_status)); | |
523 | slot->hpc_ops->get_attention_status(slot, &(info->attention_status)); | |
524 | slot->hpc_ops->get_latch_status(slot, &(info->latch_status)); | |
525 | slot->hpc_ops->get_adapter_status(slot, &(info->adapter_status)); | |
526 | ||
527 | /* result = pci_hp_change_slot_info(buffer, info); */ | |
528 | result = pci_hp_change_slot_info(slot->hotplug_slot, info); | |
529 | kfree (info); | |
530 | return result; | |
531 | } | |
532 | ||
533 | static void interrupt_event_handler(struct controller *ctrl) | |
534 | { | |
535 | int loop = 0; | |
536 | int change = 1; | |
1da177e4 LT |
537 | u8 hp_slot; |
538 | u8 getstatus; | |
539 | struct slot *p_slot; | |
540 | ||
541 | while (change) { | |
542 | change = 0; | |
543 | ||
ed6cbcf2 | 544 | for (loop = 0; loop < MAX_EVENTS; loop++) { |
1da177e4 LT |
545 | if (ctrl->event_queue[loop].event_type != 0) { |
546 | hp_slot = ctrl->event_queue[loop].hp_slot; | |
547 | ||
1da177e4 LT |
548 | p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); |
549 | ||
1da177e4 LT |
550 | if (ctrl->event_queue[loop].event_type == INT_BUTTON_CANCEL) { |
551 | dbg("button cancel\n"); | |
552 | del_timer(&p_slot->task_event); | |
553 | ||
554 | switch (p_slot->state) { | |
555 | case BLINKINGOFF_STATE: | |
44ef4cef | 556 | if (PWR_LED(ctrl->ctrlcap)) |
1da177e4 | 557 | p_slot->hpc_ops->green_led_on(p_slot); |
1da177e4 | 558 | |
44ef4cef KK |
559 | if (ATTN_LED(ctrl->ctrlcap)) |
560 | p_slot->hpc_ops->set_attention_status(p_slot, 0); | |
1da177e4 LT |
561 | break; |
562 | case BLINKINGON_STATE: | |
44ef4cef | 563 | if (PWR_LED(ctrl->ctrlcap)) |
1da177e4 | 564 | p_slot->hpc_ops->green_led_off(p_slot); |
1da177e4 | 565 | |
44ef4cef KK |
566 | if (ATTN_LED(ctrl->ctrlcap)) |
567 | p_slot->hpc_ops->set_attention_status(p_slot, 0); | |
1da177e4 LT |
568 | break; |
569 | default: | |
570 | warn("Not a valid state\n"); | |
571 | return; | |
572 | } | |
15232ece | 573 | info("PCI slot #%s - action canceled due to button press.\n", slot_name(p_slot)); |
1da177e4 LT |
574 | p_slot->state = STATIC_STATE; |
575 | } | |
576 | /* ***********Button Pressed (No action on 1st press...) */ | |
577 | else if (ctrl->event_queue[loop].event_type == INT_BUTTON_PRESS) { | |
578 | ||
579 | if (ATTN_BUTTN(ctrl->ctrlcap)) { | |
580 | dbg("Button pressed\n"); | |
581 | p_slot->hpc_ops->get_power_status(p_slot, &getstatus); | |
582 | if (getstatus) { | |
583 | /* slot is on */ | |
584 | dbg("slot is on\n"); | |
585 | p_slot->state = BLINKINGOFF_STATE; | |
15232ece | 586 | info("PCI slot #%s - powering off due to button press.\n", slot_name(p_slot)); |
1da177e4 LT |
587 | } else { |
588 | /* slot is off */ | |
589 | dbg("slot is off\n"); | |
590 | p_slot->state = BLINKINGON_STATE; | |
15232ece | 591 | info("PCI slot #%s - powering on due to button press.\n", slot_name(p_slot)); |
1da177e4 LT |
592 | } |
593 | ||
1da177e4 | 594 | /* blink green LED and turn off amber */ |
44ef4cef | 595 | if (PWR_LED(ctrl->ctrlcap)) |
1da177e4 | 596 | p_slot->hpc_ops->green_led_blink(p_slot); |
1da177e4 | 597 | |
44ef4cef | 598 | if (ATTN_LED(ctrl->ctrlcap)) |
1da177e4 LT |
599 | p_slot->hpc_ops->set_attention_status(p_slot, 0); |
600 | ||
1da177e4 LT |
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 | ||
1da177e4 LT |
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"); | |
44ef4cef | 613 | if (ATTN_LED(ctrl->ctrlcap)) |
1da177e4 | 614 | p_slot->hpc_ops->set_attention_status(p_slot, 1); |
1da177e4 | 615 | |
44ef4cef | 616 | if (PWR_LED(ctrl->ctrlcap)) |
1da177e4 | 617 | p_slot->hpc_ops->green_led_off(p_slot); |
1da177e4 LT |
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 | } | |
643 | } | |
644 | ||
1da177e4 LT |
645 | int pciehp_enable_slot(struct slot *p_slot) |
646 | { | |
647 | u8 getstatus = 0; | |
648 | int rc; | |
1da177e4 LT |
649 | |
650 | /* Check to see if (latch closed, card present, power off) */ | |
6aa4cdd0 | 651 | mutex_lock(&p_slot->ctrl->crit_sect); |
1da177e4 LT |
652 | |
653 | rc = p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus); | |
654 | if (rc || !getstatus) { | |
49ed2b49 KK |
655 | info("%s: no adapter on slot(%s)\n", __FUNCTION__, |
656 | slot_name(p_slot)); | |
6aa4cdd0 | 657 | mutex_unlock(&p_slot->ctrl->crit_sect); |
c9d86d76 | 658 | return -ENODEV; |
1da177e4 LT |
659 | } |
660 | if (MRL_SENS(p_slot->ctrl->ctrlcap)) { | |
661 | rc = p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); | |
662 | if (rc || getstatus) { | |
49ed2b49 KK |
663 | info("%s: latch open on slot(%s)\n", __FUNCTION__, |
664 | slot_name(p_slot)); | |
6aa4cdd0 | 665 | mutex_unlock(&p_slot->ctrl->crit_sect); |
c9d86d76 | 666 | return -ENODEV; |
1da177e4 LT |
667 | } |
668 | } | |
669 | ||
670 | if (POWER_CTRL(p_slot->ctrl->ctrlcap)) { | |
671 | rc = p_slot->hpc_ops->get_power_status(p_slot, &getstatus); | |
672 | if (rc || getstatus) { | |
49ed2b49 KK |
673 | info("%s: already enabled on slot(%s)\n", __FUNCTION__, |
674 | slot_name(p_slot)); | |
6aa4cdd0 | 675 | mutex_unlock(&p_slot->ctrl->crit_sect); |
c9d86d76 | 676 | return -EINVAL; |
1da177e4 LT |
677 | } |
678 | } | |
1da177e4 | 679 | |
1da177e4 | 680 | p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); |
1da177e4 | 681 | |
ca22a5e4 | 682 | rc = board_added(p_slot); |
1da177e4 | 683 | if (rc) { |
1da177e4 | 684 | p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); |
1da177e4 LT |
685 | } |
686 | ||
9ef9977c | 687 | update_slot_info(p_slot); |
1da177e4 | 688 | |
dd5619cb | 689 | mutex_unlock(&p_slot->ctrl->crit_sect); |
1da177e4 LT |
690 | return rc; |
691 | } | |
692 | ||
693 | ||
694 | int pciehp_disable_slot(struct slot *p_slot) | |
695 | { | |
1da177e4 | 696 | u8 getstatus = 0; |
1da177e4 | 697 | int ret = 0; |
1da177e4 LT |
698 | |
699 | if (!p_slot->ctrl) | |
700 | return 1; | |
701 | ||
702 | /* Check to see if (latch closed, card present, power on) */ | |
6aa4cdd0 | 703 | mutex_lock(&p_slot->ctrl->crit_sect); |
1da177e4 LT |
704 | |
705 | if (!HP_SUPR_RM(p_slot->ctrl->ctrlcap)) { | |
706 | ret = p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus); | |
707 | if (ret || !getstatus) { | |
49ed2b49 KK |
708 | info("%s: no adapter on slot(%s)\n", __FUNCTION__, |
709 | slot_name(p_slot)); | |
6aa4cdd0 | 710 | mutex_unlock(&p_slot->ctrl->crit_sect); |
c9d86d76 | 711 | return -ENODEV; |
1da177e4 LT |
712 | } |
713 | } | |
714 | ||
715 | if (MRL_SENS(p_slot->ctrl->ctrlcap)) { | |
716 | ret = p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); | |
717 | if (ret || getstatus) { | |
49ed2b49 KK |
718 | info("%s: latch open on slot(%s)\n", __FUNCTION__, |
719 | slot_name(p_slot)); | |
6aa4cdd0 | 720 | mutex_unlock(&p_slot->ctrl->crit_sect); |
c9d86d76 | 721 | return -ENODEV; |
1da177e4 LT |
722 | } |
723 | } | |
724 | ||
725 | if (POWER_CTRL(p_slot->ctrl->ctrlcap)) { | |
726 | ret = p_slot->hpc_ops->get_power_status(p_slot, &getstatus); | |
727 | if (ret || !getstatus) { | |
49ed2b49 KK |
728 | info("%s: already disabled slot(%s)\n", __FUNCTION__, |
729 | slot_name(p_slot)); | |
6aa4cdd0 | 730 | mutex_unlock(&p_slot->ctrl->crit_sect); |
c9d86d76 | 731 | return -EINVAL; |
1da177e4 LT |
732 | } |
733 | } | |
734 | ||
ca22a5e4 RS |
735 | ret = remove_board(p_slot); |
736 | update_slot_info(p_slot); | |
dd5619cb KK |
737 | |
738 | mutex_unlock(&p_slot->ctrl->crit_sect); | |
ca22a5e4 | 739 | return ret; |
1da177e4 LT |
740 | } |
741 |