Merge branch 'drm-patches' of master.kernel.org:/pub/scm/linux/kernel/git/airlied...
[linux-2.6] / drivers / pci / hotplug / pciehp_ctrl.c
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  *
26  * Send feedback to <greg@kroah.com>, <kristen.c.accardi@intel.com>
27  *
28  */
29
30 #include <linux/module.h>
31 #include <linux/kernel.h>
32 #include <linux/types.h>
33 #include <linux/smp_lock.h>
34 #include <linux/pci.h>
35 #include "../pci.h"
36 #include "pciehp.h"
37
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
46 static inline char *slot_name(struct slot *p_slot)
47 {
48         return p_slot->hotplug_slot->name;
49 }
50
51 u8 pciehp_handle_attention_button(u8 hp_slot, struct controller *ctrl)
52 {
53         struct slot *p_slot;
54         u8 rc = 0;
55         u8 getstatus;
56         struct event_info *taskInfo;
57
58         /* Attention Button Change */
59         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
70         rc++;
71
72         /*
73          *  Button pressed - See if need to TAKE ACTION!!!
74          */
75         info("Button pressed on Slot(%s)\n", slot_name(p_slot));
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;
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
96         if (rc)
97                 up(&event_semaphore);   /* signal event thread that new event is posted */
98
99         return 0;
100
101 }
102
103 u8 pciehp_handle_switch_change(u8 hp_slot, struct controller *ctrl)
104 {
105         struct slot *p_slot;
106         u8 rc = 0;
107         u8 getstatus;
108         struct event_info *taskInfo;
109
110         /* Switch Change */
111         dbg("pciehp:  Switch interrupt received.\n");
112
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);
122         p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
123
124         if (getstatus) {
125                 /*
126                  * Switch opened
127                  */
128                 info("Latch open on Slot(%s)\n", slot_name(p_slot));
129                 taskInfo->event_type = INT_SWITCH_OPEN;
130         } else {
131                 /*
132                  *  Switch closed
133                  */
134                 info("Latch close on Slot(%s)\n", slot_name(p_slot));
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
144 u8 pciehp_handle_presence_change(u8 hp_slot, struct controller *ctrl)
145 {
146         struct slot *p_slot;
147         u8 presence_save, rc = 0;
148         struct event_info *taskInfo;
149
150         /* Presence Change */
151         dbg("pciehp:  Presence/Notify input change.\n");
152
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);
162
163         /* Switch is open, assume a presence change
164          * Save the presence state
165          */
166         p_slot->hpc_ops->get_adapter_status(p_slot, &presence_save);
167         if (presence_save) {
168                 /*
169                  * Card Present
170                  */
171                 info("Card present on Slot(%s)\n", slot_name(p_slot));
172                 taskInfo->event_type = INT_PRESENCE_ON;
173         } else {
174                 /*
175                  * Not Present
176                  */
177                 info("Card not present on Slot(%s)\n", slot_name(p_slot));
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
187 u8 pciehp_handle_power_fault(u8 hp_slot, struct controller *ctrl)
188 {
189         struct slot *p_slot;
190         u8 rc = 0;
191         struct event_info *taskInfo;
192
193         /* power fault */
194         dbg("pciehp:  Power fault interrupt received.\n");
195
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);
205
206         if ( !(p_slot->hpc_ops->query_power_fault(p_slot))) {
207                 /*
208                  * power fault Cleared
209                  */
210                 info("Power fault cleared on Slot(%s)\n", slot_name(p_slot));
211                 taskInfo->event_type = INT_POWER_FAULT_CLEAR;
212         } else {
213                 /*
214                  *   power fault
215                  */
216                 info("Power fault on Slot(%s)\n", slot_name(p_slot));
217                 taskInfo->event_type = INT_POWER_FAULT;
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
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 {
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)) {   
235                         err("%s: Issue of Slot Power Off command failed\n",
236                             __FUNCTION__);
237                         return;
238                 }
239         }
240
241         if (PWR_LED(ctrl->ctrlcap))
242                 pslot->hpc_ops->green_led_off(pslot);   
243
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__);
248                         return;
249                 }
250         }
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  */
260 static int board_added(struct slot *p_slot)
261 {
262         u8 hp_slot;
263         int retval = 0;
264         struct controller *ctrl = p_slot->ctrl;
265
266         hp_slot = p_slot->device - ctrl->slot_device_offset;
267
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);
271
272         if (POWER_CTRL(ctrl->ctrlcap)) {
273                 /* Power on slot */
274                 retval = p_slot->hpc_ops->power_on_slot(p_slot);
275                 if (retval)
276                         return retval;
277         }
278         
279         if (PWR_LED(ctrl->ctrlcap))
280                 p_slot->hpc_ops->green_led_blink(p_slot);
281
282         /* Wait for ~1 second */
283         msleep(1000);
284
285         /* Check link training status */
286         retval = p_slot->hpc_ops->check_lnk_status(ctrl);
287         if (retval) {
288                 err("%s: Failed to check link status\n", __FUNCTION__);
289                 set_slot_off(ctrl, p_slot);
290                 return retval;
291         }
292
293         /* Check for a power fault */
294         if (p_slot->hpc_ops->query_power_fault(p_slot)) {
295                 dbg("%s: power fault detected\n", __FUNCTION__);
296                 retval = POWER_FAILURE;
297                 goto err_exit;
298         }
299
300         retval = pciehp_configure_device(p_slot);
301         if (retval) {
302                 err("Cannot add device 0x%x:%x\n", p_slot->bus,
303                     p_slot->device);
304                 goto err_exit;
305         }
306
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);
312         if (PWR_LED(ctrl->ctrlcap))
313                 p_slot->hpc_ops->green_led_on(p_slot);
314
315         return 0;
316
317 err_exit:
318         set_slot_off(ctrl, p_slot);
319         return retval;
320 }
321
322 /**
323  * remove_board - Turns off slot and LED's
324  *
325  */
326 static int remove_board(struct slot *p_slot)
327 {
328         u8 device;
329         u8 hp_slot;
330         int retval = 0;
331         struct controller *ctrl = p_slot->ctrl;
332
333         retval = pciehp_unconfigure_device(p_slot);
334         if (retval)
335                 return retval;
336
337         device = p_slot->device;
338         hp_slot = p_slot->device - ctrl->slot_device_offset;
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
343         if (POWER_CTRL(ctrl->ctrlcap)) {
344                 /* power off slot */
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;
350                 }
351         }
352
353         if (PWR_LED(ctrl->ctrlcap))
354                 /* turn off Green LED */
355                 p_slot->hpc_ops->green_led_off(p_slot);
356
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;
390                 dbg("%s: disabling bus:device(%x:%x)\n", __FUNCTION__,
391                                 p_slot->bus, p_slot->device);
392
393                 pciehp_disable_slot(p_slot);
394                 p_slot->state = STATIC_STATE;
395         } else {
396                 p_slot->state = POWERON_STATE;
397                 dbg("%s: adding bus:device(%x:%x)\n", __FUNCTION__,
398                                 p_slot->bus, p_slot->device);
399
400                 if (pciehp_enable_slot(p_slot) &&
401                     PWR_LED(p_slot->ctrl->ctrlcap))
402                         p_slot->hpc_ops->green_led_off(p_slot);
403
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;
432                 dbg("%s: removing bus:device(%x:%x)\n",
433                                 __FUNCTION__, p_slot->bus, p_slot->device);
434
435                 pciehp_disable_slot(p_slot);
436                 p_slot->state = STATIC_STATE;
437         } else {
438                 p_slot->state = POWERON_STATE;
439                 dbg("%s: adding bus:device(%x:%x)\n",
440                                 __FUNCTION__, p_slot->bus, p_slot->device);
441
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         }
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         }
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)
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;
537         u8 hp_slot;
538         u8 getstatus;
539         struct slot *p_slot;
540
541         while (change) {
542                 change = 0;
543
544                 for (loop = 0; loop < MAX_EVENTS; loop++) {
545                         if (ctrl->event_queue[loop].event_type != 0) {
546                                 hp_slot = ctrl->event_queue[loop].hp_slot;
547
548                                 p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
549
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:
556                                                 if (PWR_LED(ctrl->ctrlcap))
557                                                         p_slot->hpc_ops->green_led_on(p_slot);
558
559                                                 if (ATTN_LED(ctrl->ctrlcap))
560                                                         p_slot->hpc_ops->set_attention_status(p_slot, 0);
561                                                 break;
562                                         case BLINKINGON_STATE:
563                                                 if (PWR_LED(ctrl->ctrlcap))
564                                                         p_slot->hpc_ops->green_led_off(p_slot);
565
566                                                 if (ATTN_LED(ctrl->ctrlcap))
567                                                         p_slot->hpc_ops->set_attention_status(p_slot, 0);
568                                                 break;
569                                         default:
570                                                 warn("Not a valid state\n");
571                                                 return;
572                                         }
573                                         info("PCI slot #%s - action canceled due to button press.\n", slot_name(p_slot));
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;
586                                                         info("PCI slot #%s - powering off due to button press.\n", slot_name(p_slot));
587                                                 } else {
588                                                         /* slot is off */
589                                                         dbg("slot is off\n");
590                                                         p_slot->state = BLINKINGON_STATE;
591                                                         info("PCI slot #%s - powering on due to button press.\n", slot_name(p_slot));
592                                                 }
593
594                                                 /* blink green LED and turn off amber */
595                                                 if (PWR_LED(ctrl->ctrlcap))
596                                                         p_slot->hpc_ops->green_led_blink(p_slot);
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         }
643 }
644
645 int pciehp_enable_slot(struct slot *p_slot)
646 {
647         u8 getstatus = 0;
648         int rc;
649
650         /* Check to see if (latch closed, card present, power off) */
651         mutex_lock(&p_slot->ctrl->crit_sect);
652
653         rc = p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus);
654         if (rc || !getstatus) {
655                 info("%s: no adapter on slot(%s)\n", __FUNCTION__,
656                      slot_name(p_slot));
657                 mutex_unlock(&p_slot->ctrl->crit_sect);
658                 return -ENODEV;
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) {
663                         info("%s: latch open on slot(%s)\n", __FUNCTION__,
664                              slot_name(p_slot));
665                         mutex_unlock(&p_slot->ctrl->crit_sect);
666                         return -ENODEV;
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) {
673                         info("%s: already enabled on slot(%s)\n", __FUNCTION__,
674                              slot_name(p_slot));
675                         mutex_unlock(&p_slot->ctrl->crit_sect);
676                         return -EINVAL;
677                 }
678         }
679
680         p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
681
682         rc = board_added(p_slot);
683         if (rc) {
684                 p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
685         }
686
687         update_slot_info(p_slot);
688
689         mutex_unlock(&p_slot->ctrl->crit_sect);
690         return rc;
691 }
692
693
694 int pciehp_disable_slot(struct slot *p_slot)
695 {
696         u8 getstatus = 0;
697         int ret = 0;
698
699         if (!p_slot->ctrl)
700                 return 1;
701
702         /* Check to see if (latch closed, card present, power on) */
703         mutex_lock(&p_slot->ctrl->crit_sect);
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) {
708                         info("%s: no adapter on slot(%s)\n", __FUNCTION__,
709                              slot_name(p_slot));
710                         mutex_unlock(&p_slot->ctrl->crit_sect);
711                         return -ENODEV;
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) {
718                         info("%s: latch open on slot(%s)\n", __FUNCTION__,
719                              slot_name(p_slot));
720                         mutex_unlock(&p_slot->ctrl->crit_sect);
721                         return -ENODEV;
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) {
728                         info("%s: already disabled slot(%s)\n", __FUNCTION__,
729                              slot_name(p_slot));
730                         mutex_unlock(&p_slot->ctrl->crit_sect);
731                         return -EINVAL;
732                 }
733         }
734
735         ret = remove_board(p_slot);
736         update_slot_info(p_slot);
737
738         mutex_unlock(&p_slot->ctrl->crit_sect);
739         return ret;
740 }
741