]>
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 | ||
30 | #include <linux/config.h> | |
31 | #include <linux/module.h> | |
32 | #include <linux/kernel.h> | |
33 | #include <linux/types.h> | |
34 | #include <linux/slab.h> | |
35 | #include <linux/workqueue.h> | |
36 | #include <linux/interrupt.h> | |
37 | #include <linux/delay.h> | |
38 | #include <linux/wait.h> | |
39 | #include <linux/smp_lock.h> | |
40 | #include <linux/pci.h> | |
41 | #include "../pci.h" | |
42 | #include "pciehp.h" | |
1da177e4 | 43 | |
1da177e4 LT |
44 | static void interrupt_event_handler(struct controller *ctrl); |
45 | ||
46 | static struct semaphore event_semaphore; /* mutex for process loop (up if something to process) */ | |
47 | static struct semaphore event_exit; /* guard ensure thread has exited before calling it quits */ | |
48 | static int event_finished; | |
49 | static unsigned long pushbutton_pending; /* = 0 */ | |
50 | static unsigned long surprise_rm_pending; /* = 0 */ | |
51 | ||
52 | u8 pciehp_handle_attention_button(u8 hp_slot, void *inst_id) | |
53 | { | |
54 | struct controller *ctrl = (struct controller *) inst_id; | |
55 | struct slot *p_slot; | |
56 | u8 rc = 0; | |
57 | u8 getstatus; | |
58 | struct pci_func *func; | |
59 | struct event_info *taskInfo; | |
60 | ||
61 | /* Attention Button Change */ | |
62 | dbg("pciehp: Attention button interrupt received.\n"); | |
63 | ||
64 | func = pciehp_slot_find(ctrl->slot_bus, (hp_slot + ctrl->slot_device_offset), 0); | |
65 | ||
66 | /* This is the structure that tells the worker thread what to do */ | |
67 | taskInfo = &(ctrl->event_queue[ctrl->next_event]); | |
68 | p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); | |
69 | ||
70 | p_slot->hpc_ops->get_adapter_status(p_slot, &(func->presence_save)); | |
71 | p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); | |
72 | ||
73 | ctrl->next_event = (ctrl->next_event + 1) % 10; | |
74 | taskInfo->hp_slot = hp_slot; | |
75 | ||
76 | rc++; | |
77 | ||
78 | /* | |
79 | * Button pressed - See if need to TAKE ACTION!!! | |
80 | */ | |
81 | info("Button pressed on Slot(%d)\n", ctrl->first_slot + hp_slot); | |
82 | taskInfo->event_type = INT_BUTTON_PRESS; | |
83 | ||
84 | if ((p_slot->state == BLINKINGON_STATE) | |
85 | || (p_slot->state == BLINKINGOFF_STATE)) { | |
86 | /* Cancel if we are still blinking; this means that we press the | |
87 | * attention again before the 5 sec. limit expires to cancel hot-add | |
88 | * or hot-remove | |
89 | */ | |
90 | taskInfo->event_type = INT_BUTTON_CANCEL; | |
91 | info("Button cancel on Slot(%d)\n", ctrl->first_slot + hp_slot); | |
92 | } else if ((p_slot->state == POWERON_STATE) | |
93 | || (p_slot->state == POWEROFF_STATE)) { | |
94 | /* Ignore if the slot is on power-on or power-off state; this | |
95 | * means that the previous attention button action to hot-add or | |
96 | * hot-remove is undergoing | |
97 | */ | |
98 | taskInfo->event_type = INT_BUTTON_IGNORE; | |
99 | info("Button ignore on Slot(%d)\n", ctrl->first_slot + hp_slot); | |
100 | } | |
101 | ||
102 | if (rc) | |
103 | up(&event_semaphore); /* signal event thread that new event is posted */ | |
104 | ||
105 | return 0; | |
106 | ||
107 | } | |
108 | ||
109 | u8 pciehp_handle_switch_change(u8 hp_slot, void *inst_id) | |
110 | { | |
111 | struct controller *ctrl = (struct controller *) inst_id; | |
112 | struct slot *p_slot; | |
113 | u8 rc = 0; | |
114 | u8 getstatus; | |
115 | struct pci_func *func; | |
116 | struct event_info *taskInfo; | |
117 | ||
118 | /* Switch Change */ | |
119 | dbg("pciehp: Switch interrupt received.\n"); | |
120 | ||
121 | func = pciehp_slot_find(ctrl->slot_bus, (hp_slot + ctrl->slot_device_offset), 0); | |
122 | ||
123 | /* This is the structure that tells the worker thread | |
124 | * what to do | |
125 | */ | |
126 | taskInfo = &(ctrl->event_queue[ctrl->next_event]); | |
127 | ctrl->next_event = (ctrl->next_event + 1) % 10; | |
128 | taskInfo->hp_slot = hp_slot; | |
129 | ||
130 | rc++; | |
131 | p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); | |
132 | p_slot->hpc_ops->get_adapter_status(p_slot, &(func->presence_save)); | |
133 | p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); | |
134 | ||
135 | if (getstatus) { | |
136 | /* | |
137 | * Switch opened | |
138 | */ | |
139 | info("Latch open on Slot(%d)\n", ctrl->first_slot + hp_slot); | |
140 | func->switch_save = 0; | |
141 | taskInfo->event_type = INT_SWITCH_OPEN; | |
142 | } else { | |
143 | /* | |
144 | * Switch closed | |
145 | */ | |
146 | info("Latch close on Slot(%d)\n", ctrl->first_slot + hp_slot); | |
147 | func->switch_save = 0x10; | |
148 | taskInfo->event_type = INT_SWITCH_CLOSE; | |
149 | } | |
150 | ||
151 | if (rc) | |
152 | up(&event_semaphore); /* signal event thread that new event is posted */ | |
153 | ||
154 | return rc; | |
155 | } | |
156 | ||
157 | u8 pciehp_handle_presence_change(u8 hp_slot, void *inst_id) | |
158 | { | |
159 | struct controller *ctrl = (struct controller *) inst_id; | |
160 | struct slot *p_slot; | |
161 | u8 rc = 0; | |
162 | struct pci_func *func; | |
163 | struct event_info *taskInfo; | |
164 | ||
165 | /* Presence Change */ | |
166 | dbg("pciehp: Presence/Notify input change.\n"); | |
167 | ||
168 | func = pciehp_slot_find(ctrl->slot_bus, (hp_slot + ctrl->slot_device_offset), 0); | |
169 | ||
170 | /* This is the structure that tells the worker thread | |
171 | * what to do | |
172 | */ | |
173 | taskInfo = &(ctrl->event_queue[ctrl->next_event]); | |
174 | ctrl->next_event = (ctrl->next_event + 1) % 10; | |
175 | taskInfo->hp_slot = hp_slot; | |
176 | ||
177 | rc++; | |
178 | p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); | |
179 | ||
180 | /* Switch is open, assume a presence change | |
181 | * Save the presence state | |
182 | */ | |
183 | p_slot->hpc_ops->get_adapter_status(p_slot, &(func->presence_save)); | |
184 | if (func->presence_save) { | |
185 | /* | |
186 | * Card Present | |
187 | */ | |
188 | info("Card present on Slot(%d)\n", ctrl->first_slot + hp_slot); | |
189 | taskInfo->event_type = INT_PRESENCE_ON; | |
190 | } else { | |
191 | /* | |
192 | * Not Present | |
193 | */ | |
194 | info("Card not present on Slot(%d)\n", ctrl->first_slot + hp_slot); | |
195 | taskInfo->event_type = INT_PRESENCE_OFF; | |
196 | } | |
197 | ||
198 | if (rc) | |
199 | up(&event_semaphore); /* signal event thread that new event is posted */ | |
200 | ||
201 | return rc; | |
202 | } | |
203 | ||
204 | u8 pciehp_handle_power_fault(u8 hp_slot, void *inst_id) | |
205 | { | |
206 | struct controller *ctrl = (struct controller *) inst_id; | |
207 | struct slot *p_slot; | |
208 | u8 rc = 0; | |
209 | struct pci_func *func; | |
210 | struct event_info *taskInfo; | |
211 | ||
212 | /* power fault */ | |
213 | dbg("pciehp: Power fault interrupt received.\n"); | |
214 | ||
215 | func = pciehp_slot_find(ctrl->slot_bus, (hp_slot + ctrl->slot_device_offset), 0); | |
216 | ||
217 | /* this is the structure that tells the worker thread | |
218 | * what to do | |
219 | */ | |
220 | taskInfo = &(ctrl->event_queue[ctrl->next_event]); | |
221 | ctrl->next_event = (ctrl->next_event + 1) % 10; | |
222 | taskInfo->hp_slot = hp_slot; | |
223 | ||
224 | rc++; | |
225 | p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); | |
226 | ||
227 | if ( !(p_slot->hpc_ops->query_power_fault(p_slot))) { | |
228 | /* | |
229 | * power fault Cleared | |
230 | */ | |
231 | info("Power fault cleared on Slot(%d)\n", ctrl->first_slot + hp_slot); | |
232 | func->status = 0x00; | |
233 | taskInfo->event_type = INT_POWER_FAULT_CLEAR; | |
234 | } else { | |
235 | /* | |
236 | * power fault | |
237 | */ | |
238 | info("Power fault on Slot(%d)\n", ctrl->first_slot + hp_slot); | |
239 | taskInfo->event_type = INT_POWER_FAULT; | |
240 | /* set power fault status for this board */ | |
241 | func->status = 0xFF; | |
242 | info("power fault bit %x set\n", hp_slot); | |
243 | } | |
244 | if (rc) | |
245 | up(&event_semaphore); /* signal event thread that new event is posted */ | |
246 | ||
247 | return rc; | |
248 | } | |
249 | ||
1da177e4 LT |
250 | /** |
251 | * pciehp_slot_create - Creates a node and adds it to the proper bus. | |
252 | * @busnumber - bus where new node is to be located | |
253 | * | |
254 | * Returns pointer to the new node or NULL if unsuccessful | |
255 | */ | |
256 | struct pci_func *pciehp_slot_create(u8 busnumber) | |
257 | { | |
258 | struct pci_func *new_slot; | |
259 | struct pci_func *next; | |
260 | dbg("%s: busnumber %x\n", __FUNCTION__, busnumber); | |
261 | new_slot = kmalloc(sizeof(struct pci_func), GFP_KERNEL); | |
262 | ||
263 | if (new_slot == NULL) | |
264 | return new_slot; | |
265 | ||
266 | memset(new_slot, 0, sizeof(struct pci_func)); | |
267 | ||
268 | new_slot->next = NULL; | |
269 | new_slot->configured = 1; | |
270 | ||
271 | if (pciehp_slot_list[busnumber] == NULL) { | |
272 | pciehp_slot_list[busnumber] = new_slot; | |
273 | } else { | |
274 | next = pciehp_slot_list[busnumber]; | |
275 | while (next->next != NULL) | |
276 | next = next->next; | |
277 | next->next = new_slot; | |
278 | } | |
279 | return new_slot; | |
280 | } | |
281 | ||
282 | ||
283 | /** | |
284 | * slot_remove - Removes a node from the linked list of slots. | |
285 | * @old_slot: slot to remove | |
286 | * | |
287 | * Returns 0 if successful, !0 otherwise. | |
288 | */ | |
289 | static int slot_remove(struct pci_func * old_slot) | |
290 | { | |
291 | struct pci_func *next; | |
292 | ||
293 | if (old_slot == NULL) | |
294 | return 1; | |
295 | ||
296 | next = pciehp_slot_list[old_slot->bus]; | |
297 | ||
298 | if (next == NULL) | |
299 | return 1; | |
300 | ||
301 | if (next == old_slot) { | |
302 | pciehp_slot_list[old_slot->bus] = old_slot->next; | |
1da177e4 LT |
303 | kfree(old_slot); |
304 | return 0; | |
305 | } | |
306 | ||
307 | while ((next->next != old_slot) && (next->next != NULL)) { | |
308 | next = next->next; | |
309 | } | |
310 | ||
311 | if (next->next == old_slot) { | |
312 | next->next = old_slot->next; | |
1da177e4 LT |
313 | kfree(old_slot); |
314 | return 0; | |
315 | } else | |
316 | return 2; | |
317 | } | |
318 | ||
319 | ||
320 | /** | |
321 | * bridge_slot_remove - Removes a node from the linked list of slots. | |
322 | * @bridge: bridge to remove | |
323 | * | |
324 | * Returns 0 if successful, !0 otherwise. | |
325 | */ | |
326 | static int bridge_slot_remove(struct pci_func *bridge) | |
327 | { | |
328 | u8 subordinateBus, secondaryBus; | |
329 | u8 tempBus; | |
330 | struct pci_func *next; | |
331 | ||
332 | if (bridge == NULL) | |
333 | return 1; | |
334 | ||
335 | secondaryBus = (bridge->config_space[0x06] >> 8) & 0xFF; | |
336 | subordinateBus = (bridge->config_space[0x06] >> 16) & 0xFF; | |
337 | ||
338 | for (tempBus = secondaryBus; tempBus <= subordinateBus; tempBus++) { | |
339 | next = pciehp_slot_list[tempBus]; | |
340 | ||
341 | while (!slot_remove(next)) { | |
342 | next = pciehp_slot_list[tempBus]; | |
343 | } | |
344 | } | |
345 | ||
346 | next = pciehp_slot_list[bridge->bus]; | |
347 | ||
348 | if (next == NULL) { | |
349 | return 1; | |
350 | } | |
351 | ||
352 | if (next == bridge) { | |
353 | pciehp_slot_list[bridge->bus] = bridge->next; | |
354 | kfree(bridge); | |
355 | return 0; | |
356 | } | |
357 | ||
358 | while ((next->next != bridge) && (next->next != NULL)) { | |
359 | next = next->next; | |
360 | } | |
361 | ||
362 | if (next->next == bridge) { | |
363 | next->next = bridge->next; | |
364 | kfree(bridge); | |
365 | return 0; | |
366 | } else | |
367 | return 2; | |
368 | } | |
369 | ||
370 | ||
371 | /** | |
372 | * pciehp_slot_find - Looks for a node by bus, and device, multiple functions accessed | |
373 | * @bus: bus to find | |
374 | * @device: device to find | |
375 | * @index: is 0 for first function found, 1 for the second... | |
376 | * | |
377 | * Returns pointer to the node if successful, %NULL otherwise. | |
378 | */ | |
379 | struct pci_func *pciehp_slot_find(u8 bus, u8 device, u8 index) | |
380 | { | |
381 | int found = -1; | |
382 | struct pci_func *func; | |
383 | ||
384 | func = pciehp_slot_list[bus]; | |
385 | dbg("%s: bus %x device %x index %x\n", | |
386 | __FUNCTION__, bus, device, index); | |
387 | if (func != NULL) { | |
388 | dbg("%s: func-> bus %x device %x function %x pci_dev %p\n", | |
389 | __FUNCTION__, func->bus, func->device, func->function, | |
390 | func->pci_dev); | |
391 | } else | |
392 | dbg("%s: func == NULL\n", __FUNCTION__); | |
393 | ||
394 | if ((func == NULL) || ((func->device == device) && (index == 0))) | |
395 | return func; | |
396 | ||
397 | if (func->device == device) | |
398 | found++; | |
399 | ||
400 | while (func->next != NULL) { | |
401 | func = func->next; | |
402 | ||
403 | dbg("%s: In while loop, func-> bus %x device %x function %x pci_dev %p\n", | |
404 | __FUNCTION__, func->bus, func->device, func->function, | |
405 | func->pci_dev); | |
406 | if (func->device == device) | |
407 | found++; | |
408 | dbg("%s: while loop, found %d, index %d\n", __FUNCTION__, | |
409 | found, index); | |
410 | ||
411 | if ((found == index) || (func->function == index)) { | |
412 | dbg("%s: Found bus %x dev %x func %x\n", __FUNCTION__, | |
413 | func->bus, func->device, func->function); | |
414 | return func; | |
415 | } | |
416 | } | |
417 | ||
418 | return NULL; | |
419 | } | |
420 | ||
421 | static int is_bridge(struct pci_func * func) | |
422 | { | |
423 | /* Check the header type */ | |
424 | if (((func->config_space[0x03] >> 16) & 0xFF) == 0x01) | |
425 | return 1; | |
426 | else | |
427 | return 0; | |
428 | } | |
429 | ||
430 | ||
431 | /* The following routines constitute the bulk of the | |
432 | hotplug controller logic | |
433 | */ | |
434 | ||
435 | static void set_slot_off(struct controller *ctrl, struct slot * pslot) | |
436 | { | |
437 | /* Wait for exclusive access to hardware */ | |
438 | down(&ctrl->crit_sect); | |
439 | ||
440 | /* turn off slot, turn on Amber LED, turn off Green LED if supported*/ | |
441 | if (POWER_CTRL(ctrl->ctrlcap)) { | |
442 | if (pslot->hpc_ops->power_off_slot(pslot)) { | |
443 | err("%s: Issue of Slot Power Off command failed\n", __FUNCTION__); | |
444 | up(&ctrl->crit_sect); | |
445 | return; | |
446 | } | |
447 | wait_for_ctrl_irq (ctrl); | |
448 | } | |
449 | ||
450 | if (PWR_LED(ctrl->ctrlcap)) { | |
451 | pslot->hpc_ops->green_led_off(pslot); | |
452 | wait_for_ctrl_irq (ctrl); | |
453 | } | |
454 | ||
455 | if (ATTN_LED(ctrl->ctrlcap)) { | |
456 | if (pslot->hpc_ops->set_attention_status(pslot, 1)) { | |
457 | err("%s: Issue of Set Attention Led command failed\n", __FUNCTION__); | |
458 | up(&ctrl->crit_sect); | |
459 | return; | |
460 | } | |
461 | wait_for_ctrl_irq (ctrl); | |
462 | } | |
463 | ||
464 | /* Done with exclusive hardware access */ | |
465 | up(&ctrl->crit_sect); | |
466 | } | |
467 | ||
468 | /** | |
469 | * board_added - Called after a board has been added to the system. | |
470 | * | |
471 | * Turns power on for the board | |
472 | * Configures board | |
473 | * | |
474 | */ | |
475 | static u32 board_added(struct pci_func * func, struct controller * ctrl) | |
476 | { | |
477 | u8 hp_slot; | |
1da177e4 LT |
478 | u32 temp_register = 0xFFFFFFFF; |
479 | u32 rc = 0; | |
1da177e4 | 480 | struct slot *p_slot; |
1da177e4 LT |
481 | |
482 | p_slot = pciehp_find_slot(ctrl, func->device); | |
483 | hp_slot = func->device - ctrl->slot_device_offset; | |
484 | ||
485 | dbg("%s: func->device, slot_offset, hp_slot = %d, %d ,%d\n", __FUNCTION__, func->device, ctrl->slot_device_offset, hp_slot); | |
486 | ||
487 | /* Wait for exclusive access to hardware */ | |
488 | down(&ctrl->crit_sect); | |
489 | ||
490 | if (POWER_CTRL(ctrl->ctrlcap)) { | |
491 | /* Power on slot */ | |
492 | rc = p_slot->hpc_ops->power_on_slot(p_slot); | |
493 | if (rc) { | |
494 | up(&ctrl->crit_sect); | |
495 | return -1; | |
496 | } | |
497 | ||
498 | /* Wait for the command to complete */ | |
499 | wait_for_ctrl_irq (ctrl); | |
500 | } | |
501 | ||
502 | if (PWR_LED(ctrl->ctrlcap)) { | |
503 | p_slot->hpc_ops->green_led_blink(p_slot); | |
504 | ||
505 | /* Wait for the command to complete */ | |
506 | wait_for_ctrl_irq (ctrl); | |
507 | } | |
508 | ||
509 | /* Done with exclusive hardware access */ | |
510 | up(&ctrl->crit_sect); | |
511 | ||
512 | /* Wait for ~1 second */ | |
513 | dbg("%s: before long_delay\n", __FUNCTION__); | |
514 | wait_for_ctrl_irq (ctrl); | |
515 | dbg("%s: afterlong_delay\n", __FUNCTION__); | |
516 | ||
517 | /* Check link training status */ | |
518 | rc = p_slot->hpc_ops->check_lnk_status(ctrl); | |
519 | if (rc) { | |
520 | err("%s: Failed to check link status\n", __FUNCTION__); | |
521 | set_slot_off(ctrl, p_slot); | |
522 | return rc; | |
523 | } | |
524 | ||
525 | dbg("%s: func status = %x\n", __FUNCTION__, func->status); | |
526 | ||
527 | /* Check for a power fault */ | |
528 | if (func->status == 0xFF) { | |
529 | /* power fault occurred, but it was benign */ | |
530 | temp_register = 0xFFFFFFFF; | |
531 | dbg("%s: temp register set to %x by power fault\n", __FUNCTION__, temp_register); | |
532 | rc = POWER_FAILURE; | |
533 | func->status = 0; | |
71b720c0 | 534 | goto err_exit; |
1da177e4 LT |
535 | } |
536 | ||
71b720c0 RS |
537 | rc = pciehp_configure_device(p_slot); |
538 | if (rc) { | |
539 | err("Cannot add device 0x%x:%x\n", p_slot->bus, | |
540 | p_slot->device); | |
541 | goto err_exit; | |
542 | } | |
1da177e4 | 543 | |
71b720c0 RS |
544 | pciehp_save_slot_config(ctrl, func); |
545 | func->status = 0; | |
546 | func->switch_save = 0x10; | |
547 | func->is_a_board = 0x01; | |
1da177e4 | 548 | |
71b720c0 RS |
549 | /* |
550 | * Some PCI Express root ports require fixup after hot-plug operation. | |
551 | */ | |
552 | if (pcie_mch_quirk) | |
553 | pci_fixup_device(pci_fixup_final, ctrl->pci_dev); | |
554 | if (PWR_LED(ctrl->ctrlcap)) { | |
555 | /* Wait for exclusive access to hardware */ | |
556 | down(&ctrl->crit_sect); | |
1da177e4 | 557 | |
71b720c0 | 558 | p_slot->hpc_ops->green_led_on(p_slot); |
1da177e4 | 559 | |
71b720c0 RS |
560 | /* Wait for the command to complete */ |
561 | wait_for_ctrl_irq (ctrl); | |
1da177e4 | 562 | |
71b720c0 RS |
563 | /* Done with exclusive hardware access */ |
564 | up(&ctrl->crit_sect); | |
565 | } | |
1da177e4 | 566 | return 0; |
71b720c0 RS |
567 | |
568 | err_exit: | |
569 | set_slot_off(ctrl, p_slot); | |
570 | return -1; | |
1da177e4 LT |
571 | } |
572 | ||
573 | ||
574 | /** | |
575 | * remove_board - Turns off slot and LED's | |
576 | * | |
577 | */ | |
578 | static u32 remove_board(struct pci_func *func, struct controller *ctrl) | |
579 | { | |
1da177e4 LT |
580 | u8 device; |
581 | u8 hp_slot; | |
582 | u32 rc; | |
1da177e4 LT |
583 | struct slot *p_slot; |
584 | ||
585 | if (func == NULL) | |
586 | return 1; | |
587 | ||
588 | if (pciehp_unconfigure_device(func)) | |
589 | return 1; | |
590 | ||
591 | device = func->device; | |
592 | ||
593 | hp_slot = func->device - ctrl->slot_device_offset; | |
594 | p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); | |
595 | ||
596 | dbg("In %s, hp_slot = %d\n", __FUNCTION__, hp_slot); | |
597 | ||
1da177e4 LT |
598 | /* Change status to shutdown */ |
599 | if (func->is_a_board) | |
600 | func->status = 0x01; | |
601 | func->configured = 0; | |
602 | ||
603 | /* Wait for exclusive access to hardware */ | |
604 | down(&ctrl->crit_sect); | |
605 | ||
606 | if (POWER_CTRL(ctrl->ctrlcap)) { | |
607 | /* power off slot */ | |
608 | rc = p_slot->hpc_ops->power_off_slot(p_slot); | |
609 | if (rc) { | |
610 | err("%s: Issue of Slot Disable command failed\n", __FUNCTION__); | |
611 | up(&ctrl->crit_sect); | |
612 | return rc; | |
613 | } | |
614 | /* Wait for the command to complete */ | |
615 | wait_for_ctrl_irq (ctrl); | |
616 | } | |
617 | ||
618 | if (PWR_LED(ctrl->ctrlcap)) { | |
619 | /* turn off Green LED */ | |
620 | p_slot->hpc_ops->green_led_off(p_slot); | |
621 | ||
622 | /* Wait for the command to complete */ | |
623 | wait_for_ctrl_irq (ctrl); | |
624 | } | |
625 | ||
626 | /* Done with exclusive hardware access */ | |
627 | up(&ctrl->crit_sect); | |
628 | ||
629 | if (ctrl->add_support) { | |
630 | while (func) { | |
1da177e4 LT |
631 | if (is_bridge(func)) { |
632 | dbg("PCI Bridge Hot-Remove s:b:d:f(%02x:%02x:%02x:%02x)\n", | |
633 | ctrl->seg, func->bus, func->device, func->function); | |
634 | bridge_slot_remove(func); | |
635 | } else { | |
636 | dbg("PCI Function Hot-Remove s:b:d:f(%02x:%02x:%02x:%02x)\n", | |
637 | ctrl->seg, func->bus, func->device, func->function); | |
638 | slot_remove(func); | |
639 | } | |
640 | ||
641 | func = pciehp_slot_find(ctrl->slot_bus, device, 0); | |
642 | } | |
643 | ||
644 | /* Setup slot structure with entry for empty slot */ | |
645 | func = pciehp_slot_create(ctrl->slot_bus); | |
646 | ||
647 | if (func == NULL) { | |
648 | return 1; | |
649 | } | |
650 | ||
651 | func->bus = ctrl->slot_bus; | |
652 | func->device = device; | |
653 | func->function = 0; | |
654 | func->configured = 0; | |
655 | func->switch_save = 0x10; | |
656 | func->is_a_board = 0; | |
657 | } | |
658 | ||
659 | return 0; | |
660 | } | |
661 | ||
662 | ||
663 | static void pushbutton_helper_thread(unsigned long data) | |
664 | { | |
665 | pushbutton_pending = data; | |
666 | ||
667 | up(&event_semaphore); | |
668 | } | |
669 | ||
670 | /** | |
671 | * pciehp_pushbutton_thread | |
672 | * | |
673 | * Scheduled procedure to handle blocking stuff for the pushbuttons | |
674 | * Handles all pending events and exits. | |
675 | * | |
676 | */ | |
677 | static void pciehp_pushbutton_thread(unsigned long slot) | |
678 | { | |
679 | struct slot *p_slot = (struct slot *) slot; | |
680 | u8 getstatus; | |
681 | ||
682 | pushbutton_pending = 0; | |
683 | ||
684 | if (!p_slot) { | |
685 | dbg("%s: Error! slot NULL\n", __FUNCTION__); | |
686 | return; | |
687 | } | |
688 | ||
689 | p_slot->hpc_ops->get_power_status(p_slot, &getstatus); | |
690 | if (getstatus) { | |
691 | p_slot->state = POWEROFF_STATE; | |
692 | dbg("In power_down_board, b:d(%x:%x)\n", p_slot->bus, p_slot->device); | |
693 | ||
694 | pciehp_disable_slot(p_slot); | |
695 | p_slot->state = STATIC_STATE; | |
696 | } else { | |
697 | p_slot->state = POWERON_STATE; | |
698 | dbg("In add_board, b:d(%x:%x)\n", p_slot->bus, p_slot->device); | |
699 | ||
700 | if (pciehp_enable_slot(p_slot) && PWR_LED(p_slot->ctrl->ctrlcap)) { | |
701 | /* Wait for exclusive access to hardware */ | |
702 | down(&p_slot->ctrl->crit_sect); | |
703 | ||
704 | p_slot->hpc_ops->green_led_off(p_slot); | |
705 | ||
706 | /* Wait for the command to complete */ | |
707 | wait_for_ctrl_irq (p_slot->ctrl); | |
708 | ||
709 | /* Done with exclusive hardware access */ | |
710 | up(&p_slot->ctrl->crit_sect); | |
711 | } | |
712 | p_slot->state = STATIC_STATE; | |
713 | } | |
714 | ||
715 | return; | |
716 | } | |
717 | ||
718 | /** | |
719 | * pciehp_surprise_rm_thread | |
720 | * | |
721 | * Scheduled procedure to handle blocking stuff for the surprise removal | |
722 | * Handles all pending events and exits. | |
723 | * | |
724 | */ | |
725 | static void pciehp_surprise_rm_thread(unsigned long slot) | |
726 | { | |
727 | struct slot *p_slot = (struct slot *) slot; | |
728 | u8 getstatus; | |
729 | ||
730 | surprise_rm_pending = 0; | |
731 | ||
732 | if (!p_slot) { | |
733 | dbg("%s: Error! slot NULL\n", __FUNCTION__); | |
734 | return; | |
735 | } | |
736 | ||
737 | p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus); | |
738 | if (!getstatus) { | |
739 | p_slot->state = POWEROFF_STATE; | |
740 | dbg("In removing board, b:d(%x:%x)\n", p_slot->bus, p_slot->device); | |
741 | ||
742 | pciehp_disable_slot(p_slot); | |
743 | p_slot->state = STATIC_STATE; | |
744 | } else { | |
745 | p_slot->state = POWERON_STATE; | |
746 | dbg("In add_board, b:d(%x:%x)\n", p_slot->bus, p_slot->device); | |
747 | ||
748 | if (pciehp_enable_slot(p_slot) && PWR_LED(p_slot->ctrl->ctrlcap)) { | |
749 | /* Wait for exclusive access to hardware */ | |
750 | down(&p_slot->ctrl->crit_sect); | |
751 | ||
752 | p_slot->hpc_ops->green_led_off(p_slot); | |
753 | ||
754 | /* Wait for the command to complete */ | |
755 | wait_for_ctrl_irq (p_slot->ctrl); | |
756 | ||
757 | /* Done with exclusive hardware access */ | |
758 | up(&p_slot->ctrl->crit_sect); | |
759 | } | |
760 | p_slot->state = STATIC_STATE; | |
761 | } | |
762 | ||
763 | return; | |
764 | } | |
765 | ||
766 | ||
767 | ||
768 | /* this is the main worker thread */ | |
769 | static int event_thread(void* data) | |
770 | { | |
771 | struct controller *ctrl; | |
772 | lock_kernel(); | |
773 | daemonize("pciehpd_event"); | |
774 | ||
775 | unlock_kernel(); | |
776 | ||
777 | while (1) { | |
778 | dbg("!!!!event_thread sleeping\n"); | |
779 | down_interruptible (&event_semaphore); | |
780 | dbg("event_thread woken finished = %d\n", event_finished); | |
781 | if (event_finished || signal_pending(current)) | |
782 | break; | |
783 | /* Do stuff here */ | |
784 | if (pushbutton_pending) | |
785 | pciehp_pushbutton_thread(pushbutton_pending); | |
786 | else if (surprise_rm_pending) | |
787 | pciehp_surprise_rm_thread(surprise_rm_pending); | |
788 | else | |
789 | for (ctrl = pciehp_ctrl_list; ctrl; ctrl=ctrl->next) | |
790 | interrupt_event_handler(ctrl); | |
791 | } | |
792 | dbg("event_thread signals exit\n"); | |
793 | up(&event_exit); | |
794 | return 0; | |
795 | } | |
796 | ||
797 | int pciehp_event_start_thread(void) | |
798 | { | |
799 | int pid; | |
800 | ||
801 | /* initialize our semaphores */ | |
802 | init_MUTEX_LOCKED(&event_exit); | |
803 | event_finished=0; | |
804 | ||
805 | init_MUTEX_LOCKED(&event_semaphore); | |
806 | pid = kernel_thread(event_thread, NULL, 0); | |
807 | ||
808 | if (pid < 0) { | |
809 | err ("Can't start up our event thread\n"); | |
810 | return -1; | |
811 | } | |
812 | dbg("Our event thread pid = %d\n", pid); | |
813 | return 0; | |
814 | } | |
815 | ||
816 | ||
817 | void pciehp_event_stop_thread(void) | |
818 | { | |
819 | event_finished = 1; | |
820 | dbg("event_thread finish command given\n"); | |
821 | up(&event_semaphore); | |
822 | dbg("wait for event_thread to exit\n"); | |
823 | down(&event_exit); | |
824 | } | |
825 | ||
826 | ||
827 | static int update_slot_info(struct slot *slot) | |
828 | { | |
829 | struct hotplug_slot_info *info; | |
830 | /* char buffer[SLOT_NAME_SIZE]; */ | |
831 | int result; | |
832 | ||
833 | info = kmalloc(sizeof(struct hotplug_slot_info), GFP_KERNEL); | |
834 | if (!info) | |
835 | return -ENOMEM; | |
836 | ||
837 | /* make_slot_name (&buffer[0], SLOT_NAME_SIZE, slot); */ | |
838 | ||
839 | slot->hpc_ops->get_power_status(slot, &(info->power_status)); | |
840 | slot->hpc_ops->get_attention_status(slot, &(info->attention_status)); | |
841 | slot->hpc_ops->get_latch_status(slot, &(info->latch_status)); | |
842 | slot->hpc_ops->get_adapter_status(slot, &(info->adapter_status)); | |
843 | ||
844 | /* result = pci_hp_change_slot_info(buffer, info); */ | |
845 | result = pci_hp_change_slot_info(slot->hotplug_slot, info); | |
846 | kfree (info); | |
847 | return result; | |
848 | } | |
849 | ||
850 | static void interrupt_event_handler(struct controller *ctrl) | |
851 | { | |
852 | int loop = 0; | |
853 | int change = 1; | |
854 | struct pci_func *func; | |
855 | u8 hp_slot; | |
856 | u8 getstatus; | |
857 | struct slot *p_slot; | |
858 | ||
859 | while (change) { | |
860 | change = 0; | |
861 | ||
862 | for (loop = 0; loop < 10; loop++) { | |
863 | if (ctrl->event_queue[loop].event_type != 0) { | |
864 | hp_slot = ctrl->event_queue[loop].hp_slot; | |
865 | ||
866 | func = pciehp_slot_find(ctrl->slot_bus, (hp_slot + ctrl->slot_device_offset), 0); | |
867 | ||
868 | p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); | |
869 | ||
870 | dbg("hp_slot %d, func %p, p_slot %p\n", hp_slot, func, p_slot); | |
871 | ||
872 | if (ctrl->event_queue[loop].event_type == INT_BUTTON_CANCEL) { | |
873 | dbg("button cancel\n"); | |
874 | del_timer(&p_slot->task_event); | |
875 | ||
876 | switch (p_slot->state) { | |
877 | case BLINKINGOFF_STATE: | |
878 | /* Wait for exclusive access to hardware */ | |
879 | down(&ctrl->crit_sect); | |
880 | ||
881 | if (PWR_LED(ctrl->ctrlcap)) { | |
882 | p_slot->hpc_ops->green_led_on(p_slot); | |
883 | /* Wait for the command to complete */ | |
884 | wait_for_ctrl_irq (ctrl); | |
885 | } | |
886 | if (ATTN_LED(ctrl->ctrlcap)) { | |
887 | p_slot->hpc_ops->set_attention_status(p_slot, 0); | |
888 | ||
889 | /* Wait for the command to complete */ | |
890 | wait_for_ctrl_irq (ctrl); | |
891 | } | |
892 | /* Done with exclusive hardware access */ | |
893 | up(&ctrl->crit_sect); | |
894 | break; | |
895 | case BLINKINGON_STATE: | |
896 | /* Wait for exclusive access to hardware */ | |
897 | down(&ctrl->crit_sect); | |
898 | ||
899 | if (PWR_LED(ctrl->ctrlcap)) { | |
900 | p_slot->hpc_ops->green_led_off(p_slot); | |
901 | /* Wait for the command to complete */ | |
902 | wait_for_ctrl_irq (ctrl); | |
903 | } | |
904 | if (ATTN_LED(ctrl->ctrlcap)){ | |
905 | p_slot->hpc_ops->set_attention_status(p_slot, 0); | |
906 | /* Wait for the command to complete */ | |
907 | wait_for_ctrl_irq (ctrl); | |
908 | } | |
909 | /* Done with exclusive hardware access */ | |
910 | up(&ctrl->crit_sect); | |
911 | ||
912 | break; | |
913 | default: | |
914 | warn("Not a valid state\n"); | |
915 | return; | |
916 | } | |
917 | info(msg_button_cancel, p_slot->number); | |
918 | p_slot->state = STATIC_STATE; | |
919 | } | |
920 | /* ***********Button Pressed (No action on 1st press...) */ | |
921 | else if (ctrl->event_queue[loop].event_type == INT_BUTTON_PRESS) { | |
922 | ||
923 | if (ATTN_BUTTN(ctrl->ctrlcap)) { | |
924 | dbg("Button pressed\n"); | |
925 | p_slot->hpc_ops->get_power_status(p_slot, &getstatus); | |
926 | if (getstatus) { | |
927 | /* slot is on */ | |
928 | dbg("slot is on\n"); | |
929 | p_slot->state = BLINKINGOFF_STATE; | |
930 | info(msg_button_off, p_slot->number); | |
931 | } else { | |
932 | /* slot is off */ | |
933 | dbg("slot is off\n"); | |
934 | p_slot->state = BLINKINGON_STATE; | |
935 | info(msg_button_on, p_slot->number); | |
936 | } | |
937 | ||
938 | /* Wait for exclusive access to hardware */ | |
939 | down(&ctrl->crit_sect); | |
940 | ||
941 | /* blink green LED and turn off amber */ | |
942 | if (PWR_LED(ctrl->ctrlcap)) { | |
943 | p_slot->hpc_ops->green_led_blink(p_slot); | |
944 | /* Wait for the command to complete */ | |
945 | wait_for_ctrl_irq (ctrl); | |
946 | } | |
947 | ||
948 | if (ATTN_LED(ctrl->ctrlcap)) { | |
949 | p_slot->hpc_ops->set_attention_status(p_slot, 0); | |
950 | ||
951 | /* Wait for the command to complete */ | |
952 | wait_for_ctrl_irq (ctrl); | |
953 | } | |
954 | ||
955 | /* Done with exclusive hardware access */ | |
956 | up(&ctrl->crit_sect); | |
957 | ||
958 | init_timer(&p_slot->task_event); | |
959 | p_slot->task_event.expires = jiffies + 5 * HZ; /* 5 second delay */ | |
960 | p_slot->task_event.function = (void (*)(unsigned long)) pushbutton_helper_thread; | |
961 | p_slot->task_event.data = (unsigned long) p_slot; | |
962 | ||
963 | dbg("add_timer p_slot = %p\n", (void *) p_slot); | |
964 | add_timer(&p_slot->task_event); | |
965 | } | |
966 | } | |
967 | /***********POWER FAULT********************/ | |
968 | else if (ctrl->event_queue[loop].event_type == INT_POWER_FAULT) { | |
969 | if (POWER_CTRL(ctrl->ctrlcap)) { | |
970 | dbg("power fault\n"); | |
971 | /* Wait for exclusive access to hardware */ | |
972 | down(&ctrl->crit_sect); | |
973 | ||
974 | if (ATTN_LED(ctrl->ctrlcap)) { | |
975 | p_slot->hpc_ops->set_attention_status(p_slot, 1); | |
976 | wait_for_ctrl_irq (ctrl); | |
977 | } | |
978 | ||
979 | if (PWR_LED(ctrl->ctrlcap)) { | |
980 | p_slot->hpc_ops->green_led_off(p_slot); | |
981 | wait_for_ctrl_irq (ctrl); | |
982 | } | |
983 | ||
984 | /* Done with exclusive hardware access */ | |
985 | up(&ctrl->crit_sect); | |
986 | } | |
987 | } | |
988 | /***********SURPRISE REMOVAL********************/ | |
989 | else if ((ctrl->event_queue[loop].event_type == INT_PRESENCE_ON) || | |
990 | (ctrl->event_queue[loop].event_type == INT_PRESENCE_OFF)) { | |
991 | if (HP_SUPR_RM(ctrl->ctrlcap)) { | |
992 | dbg("Surprise Removal\n"); | |
993 | if (p_slot) { | |
994 | surprise_rm_pending = (unsigned long) p_slot; | |
995 | up(&event_semaphore); | |
996 | update_slot_info(p_slot); | |
997 | } | |
998 | } | |
999 | } else { | |
1000 | /* refresh notification */ | |
1001 | if (p_slot) | |
1002 | update_slot_info(p_slot); | |
1003 | } | |
1004 | ||
1005 | ctrl->event_queue[loop].event_type = 0; | |
1006 | ||
1007 | change = 1; | |
1008 | } | |
1009 | } /* End of FOR loop */ | |
1010 | } | |
1011 | } | |
1012 | ||
1013 | ||
1014 | int pciehp_enable_slot(struct slot *p_slot) | |
1015 | { | |
1016 | u8 getstatus = 0; | |
1017 | int rc; | |
1018 | struct pci_func *func; | |
1019 | ||
1020 | func = pciehp_slot_find(p_slot->bus, p_slot->device, 0); | |
1021 | if (!func) { | |
1022 | dbg("%s: Error! slot NULL\n", __FUNCTION__); | |
1023 | return 1; | |
1024 | } | |
1025 | ||
1026 | /* Check to see if (latch closed, card present, power off) */ | |
1027 | down(&p_slot->ctrl->crit_sect); | |
1028 | ||
1029 | rc = p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus); | |
1030 | if (rc || !getstatus) { | |
1031 | info("%s: no adapter on slot(%x)\n", __FUNCTION__, p_slot->number); | |
1032 | up(&p_slot->ctrl->crit_sect); | |
1033 | return 1; | |
1034 | } | |
1035 | if (MRL_SENS(p_slot->ctrl->ctrlcap)) { | |
1036 | rc = p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); | |
1037 | if (rc || getstatus) { | |
1038 | info("%s: latch open on slot(%x)\n", __FUNCTION__, p_slot->number); | |
1039 | up(&p_slot->ctrl->crit_sect); | |
1040 | return 1; | |
1041 | } | |
1042 | } | |
1043 | ||
1044 | if (POWER_CTRL(p_slot->ctrl->ctrlcap)) { | |
1045 | rc = p_slot->hpc_ops->get_power_status(p_slot, &getstatus); | |
1046 | if (rc || getstatus) { | |
1047 | info("%s: already enabled on slot(%x)\n", __FUNCTION__, p_slot->number); | |
1048 | up(&p_slot->ctrl->crit_sect); | |
1049 | return 1; | |
1050 | } | |
1051 | } | |
1052 | up(&p_slot->ctrl->crit_sect); | |
1053 | ||
1054 | slot_remove(func); | |
1055 | ||
1056 | func = pciehp_slot_create(p_slot->bus); | |
1057 | if (func == NULL) | |
1058 | return 1; | |
1059 | ||
1060 | func->bus = p_slot->bus; | |
1061 | func->device = p_slot->device; | |
1062 | func->function = 0; | |
1063 | func->configured = 0; | |
1064 | func->is_a_board = 1; | |
1065 | ||
1066 | /* We have to save the presence info for these slots */ | |
1067 | p_slot->hpc_ops->get_adapter_status(p_slot, &(func->presence_save)); | |
1068 | p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); | |
1069 | func->switch_save = !getstatus? 0x10:0; | |
1070 | ||
1071 | rc = board_added(func, p_slot->ctrl); | |
1072 | if (rc) { | |
1073 | if (is_bridge(func)) | |
1074 | bridge_slot_remove(func); | |
1075 | else | |
1076 | slot_remove(func); | |
1077 | ||
1078 | /* Setup slot structure with entry for empty slot */ | |
1079 | func = pciehp_slot_create(p_slot->bus); | |
1080 | if (func == NULL) | |
1081 | return 1; /* Out of memory */ | |
1082 | ||
1083 | func->bus = p_slot->bus; | |
1084 | func->device = p_slot->device; | |
1085 | func->function = 0; | |
1086 | func->configured = 0; | |
1087 | func->is_a_board = 1; | |
1088 | ||
1089 | /* We have to save the presence info for these slots */ | |
1090 | p_slot->hpc_ops->get_adapter_status(p_slot, &(func->presence_save)); | |
1091 | p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); | |
1092 | func->switch_save = !getstatus? 0x10:0; | |
1093 | } | |
1094 | ||
1095 | if (p_slot) | |
1096 | update_slot_info(p_slot); | |
1097 | ||
1098 | return rc; | |
1099 | } | |
1100 | ||
1101 | ||
1102 | int pciehp_disable_slot(struct slot *p_slot) | |
1103 | { | |
1104 | u8 class_code, header_type, BCR; | |
1105 | u8 index = 0; | |
1106 | u8 getstatus = 0; | |
1107 | u32 rc = 0; | |
1108 | int ret = 0; | |
1109 | unsigned int devfn; | |
1110 | struct pci_bus *pci_bus = p_slot->ctrl->pci_dev->subordinate; | |
1111 | struct pci_func *func; | |
1112 | ||
1113 | if (!p_slot->ctrl) | |
1114 | return 1; | |
1115 | ||
1116 | /* Check to see if (latch closed, card present, power on) */ | |
1117 | down(&p_slot->ctrl->crit_sect); | |
1118 | ||
1119 | if (!HP_SUPR_RM(p_slot->ctrl->ctrlcap)) { | |
1120 | ret = p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus); | |
1121 | if (ret || !getstatus) { | |
1122 | info("%s: no adapter on slot(%x)\n", __FUNCTION__, p_slot->number); | |
1123 | up(&p_slot->ctrl->crit_sect); | |
1124 | return 1; | |
1125 | } | |
1126 | } | |
1127 | ||
1128 | if (MRL_SENS(p_slot->ctrl->ctrlcap)) { | |
1129 | ret = p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); | |
1130 | if (ret || getstatus) { | |
1131 | info("%s: latch open on slot(%x)\n", __FUNCTION__, p_slot->number); | |
1132 | up(&p_slot->ctrl->crit_sect); | |
1133 | return 1; | |
1134 | } | |
1135 | } | |
1136 | ||
1137 | if (POWER_CTRL(p_slot->ctrl->ctrlcap)) { | |
1138 | ret = p_slot->hpc_ops->get_power_status(p_slot, &getstatus); | |
1139 | if (ret || !getstatus) { | |
1140 | info("%s: already disabled slot(%x)\n", __FUNCTION__, p_slot->number); | |
1141 | up(&p_slot->ctrl->crit_sect); | |
1142 | return 1; | |
1143 | } | |
1144 | } | |
1145 | ||
1146 | up(&p_slot->ctrl->crit_sect); | |
1147 | ||
1148 | func = pciehp_slot_find(p_slot->bus, p_slot->device, index++); | |
1149 | ||
1150 | /* Make sure there are no video controllers here | |
1151 | * for all func of p_slot | |
1152 | */ | |
1153 | while (func && !rc) { | |
1154 | pci_bus->number = func->bus; | |
1155 | devfn = PCI_DEVFN(func->device, func->function); | |
1156 | ||
1157 | /* Check the Class Code */ | |
1158 | rc = pci_bus_read_config_byte (pci_bus, devfn, 0x0B, &class_code); | |
1159 | if (rc) | |
1160 | return rc; | |
1161 | ||
1162 | if (class_code == PCI_BASE_CLASS_DISPLAY) { | |
1163 | /* Display/Video adapter (not supported) */ | |
1164 | rc = REMOVE_NOT_SUPPORTED; | |
1165 | } else { | |
1166 | /* See if it's a bridge */ | |
1167 | rc = pci_bus_read_config_byte (pci_bus, devfn, PCI_HEADER_TYPE, &header_type); | |
1168 | if (rc) | |
1169 | return rc; | |
1170 | ||
1171 | /* If it's a bridge, check the VGA Enable bit */ | |
1172 | if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { | |
1173 | rc = pci_bus_read_config_byte (pci_bus, devfn, PCI_BRIDGE_CONTROL, &BCR); | |
1174 | if (rc) | |
1175 | return rc; | |
1176 | ||
1177 | /* If the VGA Enable bit is set, remove isn't supported */ | |
1178 | if (BCR & PCI_BRIDGE_CTL_VGA) { | |
1179 | rc = REMOVE_NOT_SUPPORTED; | |
1180 | } | |
1181 | } | |
1182 | } | |
1183 | ||
1184 | func = pciehp_slot_find(p_slot->bus, p_slot->device, index++); | |
1185 | } | |
1186 | ||
1187 | func = pciehp_slot_find(p_slot->bus, p_slot->device, 0); | |
1188 | if ((func != NULL) && !rc) { | |
1189 | rc = remove_board(func, p_slot->ctrl); | |
1190 | } else if (!rc) | |
1191 | rc = 1; | |
1192 | ||
1193 | if (p_slot) | |
1194 | update_slot_info(p_slot); | |
1195 | ||
1196 | return rc; | |
1197 | } | |
1198 |