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 742