shpchp_ctrl.c revision e325e1f0783382298141c74737712637943c6063
1/* 2 * Standard 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 <linux/workqueue.h> 36#include "../pci.h" 37#include "shpchp.h" 38 39static void interrupt_event_handler(struct work_struct *work); 40static int shpchp_enable_slot(struct slot *p_slot); 41static int shpchp_disable_slot(struct slot *p_slot); 42 43static int queue_interrupt_event(struct slot *p_slot, u32 event_type) 44{ 45 struct event_info *info; 46 47 info = kmalloc(sizeof(*info), GFP_ATOMIC); 48 if (!info) 49 return -ENOMEM; 50 51 info->event_type = event_type; 52 info->p_slot = p_slot; 53 INIT_WORK(&info->work, interrupt_event_handler); 54 55 schedule_work(&info->work); 56 57 return 0; 58} 59 60u8 shpchp_handle_attention_button(u8 hp_slot, struct controller *ctrl) 61{ 62 struct slot *p_slot; 63 u32 event_type; 64 65 /* Attention Button Change */ 66 dbg("shpchp: Attention button interrupt received.\n"); 67 68 p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); 69 p_slot->hpc_ops->get_adapter_status(p_slot, &(p_slot->presence_save)); 70 71 /* 72 * Button pressed - See if need to TAKE ACTION!!! 73 */ 74 info("Button pressed on Slot(%s)\n", p_slot->name); 75 event_type = INT_BUTTON_PRESS; 76 77 queue_interrupt_event(p_slot, event_type); 78 79 return 0; 80 81} 82 83u8 shpchp_handle_switch_change(u8 hp_slot, struct controller *ctrl) 84{ 85 struct slot *p_slot; 86 u8 getstatus; 87 u32 event_type; 88 89 /* Switch Change */ 90 dbg("shpchp: Switch interrupt received.\n"); 91 92 p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); 93 p_slot->hpc_ops->get_adapter_status(p_slot, &(p_slot->presence_save)); 94 p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); 95 dbg("%s: Card present %x Power status %x\n", __FUNCTION__, 96 p_slot->presence_save, p_slot->pwr_save); 97 98 if (getstatus) { 99 /* 100 * Switch opened 101 */ 102 info("Latch open on Slot(%s)\n", p_slot->name); 103 event_type = INT_SWITCH_OPEN; 104 if (p_slot->pwr_save && p_slot->presence_save) { 105 event_type = INT_POWER_FAULT; 106 err("Surprise Removal of card\n"); 107 } 108 } else { 109 /* 110 * Switch closed 111 */ 112 info("Latch close on Slot(%s)\n", p_slot->name); 113 event_type = INT_SWITCH_CLOSE; 114 } 115 116 queue_interrupt_event(p_slot, event_type); 117 118 return 1; 119} 120 121u8 shpchp_handle_presence_change(u8 hp_slot, struct controller *ctrl) 122{ 123 struct slot *p_slot; 124 u32 event_type; 125 126 /* Presence Change */ 127 dbg("shpchp: Presence/Notify input change.\n"); 128 129 p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); 130 131 /* 132 * Save the presence state 133 */ 134 p_slot->hpc_ops->get_adapter_status(p_slot, &(p_slot->presence_save)); 135 if (p_slot->presence_save) { 136 /* 137 * Card Present 138 */ 139 info("Card present on Slot(%s)\n", p_slot->name); 140 event_type = INT_PRESENCE_ON; 141 } else { 142 /* 143 * Not Present 144 */ 145 info("Card not present on Slot(%s)\n", p_slot->name); 146 event_type = INT_PRESENCE_OFF; 147 } 148 149 queue_interrupt_event(p_slot, event_type); 150 151 return 1; 152} 153 154u8 shpchp_handle_power_fault(u8 hp_slot, struct controller *ctrl) 155{ 156 struct slot *p_slot; 157 u32 event_type; 158 159 /* Power fault */ 160 dbg("shpchp: Power fault interrupt received.\n"); 161 162 p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); 163 164 if ( !(p_slot->hpc_ops->query_power_fault(p_slot))) { 165 /* 166 * Power fault Cleared 167 */ 168 info("Power fault cleared on Slot(%s)\n", p_slot->name); 169 p_slot->status = 0x00; 170 event_type = INT_POWER_FAULT_CLEAR; 171 } else { 172 /* 173 * Power fault 174 */ 175 info("Power fault on Slot(%s)\n", p_slot->name); 176 event_type = INT_POWER_FAULT; 177 /* set power fault status for this board */ 178 p_slot->status = 0xFF; 179 info("power fault bit %x set\n", hp_slot); 180 } 181 182 queue_interrupt_event(p_slot, event_type); 183 184 return 1; 185} 186 187/* The following routines constitute the bulk of the 188 hotplug controller logic 189 */ 190static int change_bus_speed(struct controller *ctrl, struct slot *p_slot, 191 enum pci_bus_speed speed) 192{ 193 int rc = 0; 194 195 dbg("%s: change to speed %d\n", __FUNCTION__, speed); 196 if ((rc = p_slot->hpc_ops->set_bus_speed_mode(p_slot, speed))) { 197 err("%s: Issue of set bus speed mode command failed\n", 198 __FUNCTION__); 199 return WRONG_BUS_FREQUENCY; 200 } 201 return rc; 202} 203 204static int fix_bus_speed(struct controller *ctrl, struct slot *pslot, 205 u8 flag, enum pci_bus_speed asp, enum pci_bus_speed bsp, 206 enum pci_bus_speed msp) 207{ 208 int rc = 0; 209 210 /* 211 * If other slots on the same bus are occupied, we cannot 212 * change the bus speed. 213 */ 214 if (flag) { 215 if (asp < bsp) { 216 err("%s: speed of bus %x and adapter %x mismatch\n", 217 __FUNCTION__, bsp, asp); 218 rc = WRONG_BUS_FREQUENCY; 219 } 220 return rc; 221 } 222 223 if (asp < msp) { 224 if (bsp != asp) 225 rc = change_bus_speed(ctrl, pslot, asp); 226 } else { 227 if (bsp != msp) 228 rc = change_bus_speed(ctrl, pslot, msp); 229 } 230 return rc; 231} 232 233/** 234 * board_added - Called after a board has been added to the system. 235 * 236 * Turns power on for the board 237 * Configures board 238 * 239 */ 240static int board_added(struct slot *p_slot) 241{ 242 u8 hp_slot; 243 u8 slots_not_empty = 0; 244 int rc = 0; 245 enum pci_bus_speed asp, bsp, msp; 246 struct controller *ctrl = p_slot->ctrl; 247 248 hp_slot = p_slot->device - ctrl->slot_device_offset; 249 250 dbg("%s: p_slot->device, slot_offset, hp_slot = %d, %d ,%d\n", 251 __FUNCTION__, p_slot->device, 252 ctrl->slot_device_offset, hp_slot); 253 254 /* Power on slot without connecting to bus */ 255 rc = p_slot->hpc_ops->power_on_slot(p_slot); 256 if (rc) { 257 err("%s: Failed to power on slot\n", __FUNCTION__); 258 return -1; 259 } 260 261 if ((ctrl->pci_dev->vendor == 0x8086) && (ctrl->pci_dev->device == 0x0332)) { 262 if (slots_not_empty) 263 return WRONG_BUS_FREQUENCY; 264 265 if ((rc = p_slot->hpc_ops->set_bus_speed_mode(p_slot, PCI_SPEED_33MHz))) { 266 err("%s: Issue of set bus speed mode command failed\n", __FUNCTION__); 267 return WRONG_BUS_FREQUENCY; 268 } 269 270 /* turn on board, blink green LED, turn off Amber LED */ 271 if ((rc = p_slot->hpc_ops->slot_enable(p_slot))) { 272 err("%s: Issue of Slot Enable command failed\n", __FUNCTION__); 273 return rc; 274 } 275 } 276 277 rc = p_slot->hpc_ops->get_adapter_speed(p_slot, &asp); 278 if (rc) { 279 err("%s: Can't get adapter speed or bus mode mismatch\n", 280 __FUNCTION__); 281 return WRONG_BUS_FREQUENCY; 282 } 283 284 rc = p_slot->hpc_ops->get_cur_bus_speed(p_slot, &bsp); 285 if (rc) { 286 err("%s: Can't get bus operation speed\n", __FUNCTION__); 287 return WRONG_BUS_FREQUENCY; 288 } 289 290 rc = p_slot->hpc_ops->get_max_bus_speed(p_slot, &msp); 291 if (rc) { 292 err("%s: Can't get max bus operation speed\n", __FUNCTION__); 293 msp = bsp; 294 } 295 296 /* Check if there are other slots or devices on the same bus */ 297 if (!list_empty(&ctrl->pci_dev->subordinate->devices)) 298 slots_not_empty = 1; 299 300 dbg("%s: slots_not_empty %d, adapter_speed %d, bus_speed %d, " 301 "max_bus_speed %d\n", __FUNCTION__, slots_not_empty, asp, 302 bsp, msp); 303 304 rc = fix_bus_speed(ctrl, p_slot, slots_not_empty, asp, bsp, msp); 305 if (rc) 306 return rc; 307 308 /* turn on board, blink green LED, turn off Amber LED */ 309 if ((rc = p_slot->hpc_ops->slot_enable(p_slot))) { 310 err("%s: Issue of Slot Enable command failed\n", __FUNCTION__); 311 return rc; 312 } 313 314 /* Wait for ~1 second */ 315 msleep(1000); 316 317 dbg("%s: slot status = %x\n", __FUNCTION__, p_slot->status); 318 /* Check for a power fault */ 319 if (p_slot->status == 0xFF) { 320 /* power fault occurred, but it was benign */ 321 dbg("%s: power fault\n", __FUNCTION__); 322 rc = POWER_FAILURE; 323 p_slot->status = 0; 324 goto err_exit; 325 } 326 327 if (shpchp_configure_device(p_slot)) { 328 err("Cannot add device at 0x%x:0x%x\n", p_slot->bus, 329 p_slot->device); 330 goto err_exit; 331 } 332 333 p_slot->status = 0; 334 p_slot->is_a_board = 0x01; 335 p_slot->pwr_save = 1; 336 337 p_slot->hpc_ops->green_led_on(p_slot); 338 339 return 0; 340 341err_exit: 342 /* turn off slot, turn on Amber LED, turn off Green LED */ 343 rc = p_slot->hpc_ops->slot_disable(p_slot); 344 if (rc) { 345 err("%s: Issue of Slot Disable command failed\n", __FUNCTION__); 346 return rc; 347 } 348 349 return(rc); 350} 351 352 353/** 354 * remove_board - Turns off slot and LED's 355 * 356 */ 357static int remove_board(struct slot *p_slot) 358{ 359 struct controller *ctrl = p_slot->ctrl; 360 u8 hp_slot; 361 int rc; 362 363 if (shpchp_unconfigure_device(p_slot)) 364 return(1); 365 366 hp_slot = p_slot->device - ctrl->slot_device_offset; 367 p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); 368 369 dbg("In %s, hp_slot = %d\n", __FUNCTION__, hp_slot); 370 371 /* Change status to shutdown */ 372 if (p_slot->is_a_board) 373 p_slot->status = 0x01; 374 375 /* turn off slot, turn on Amber LED, turn off Green LED */ 376 rc = p_slot->hpc_ops->slot_disable(p_slot); 377 if (rc) { 378 err("%s: Issue of Slot Disable command failed\n", __FUNCTION__); 379 return rc; 380 } 381 382 rc = p_slot->hpc_ops->set_attention_status(p_slot, 0); 383 if (rc) { 384 err("%s: Issue of Set Attention command failed\n", __FUNCTION__); 385 return rc; 386 } 387 388 p_slot->pwr_save = 0; 389 p_slot->is_a_board = 0; 390 391 return 0; 392} 393 394 395struct pushbutton_work_info { 396 struct slot *p_slot; 397 struct work_struct work; 398}; 399 400/** 401 * shpchp_pushbutton_thread 402 * 403 * Scheduled procedure to handle blocking stuff for the pushbuttons 404 * Handles all pending events and exits. 405 * 406 */ 407static void shpchp_pushbutton_thread(struct work_struct *work) 408{ 409 struct pushbutton_work_info *info = 410 container_of(work, struct pushbutton_work_info, work); 411 struct slot *p_slot = info->p_slot; 412 413 mutex_lock(&p_slot->lock); 414 switch (p_slot->state) { 415 case POWEROFF_STATE: 416 mutex_unlock(&p_slot->lock); 417 shpchp_disable_slot(p_slot); 418 mutex_lock(&p_slot->lock); 419 p_slot->state = STATIC_STATE; 420 break; 421 case POWERON_STATE: 422 mutex_unlock(&p_slot->lock); 423 if (shpchp_enable_slot(p_slot)) 424 p_slot->hpc_ops->green_led_off(p_slot); 425 mutex_lock(&p_slot->lock); 426 p_slot->state = STATIC_STATE; 427 break; 428 default: 429 break; 430 } 431 mutex_unlock(&p_slot->lock); 432 433 kfree(info); 434} 435 436void shpchp_queue_pushbutton_work(struct work_struct *work) 437{ 438 struct slot *p_slot = container_of(work, struct slot, work.work); 439 struct pushbutton_work_info *info; 440 441 info = kmalloc(sizeof(*info), GFP_KERNEL); 442 if (!info) { 443 err("%s: Cannot allocate memory\n", __FUNCTION__); 444 return; 445 } 446 info->p_slot = p_slot; 447 INIT_WORK(&info->work, shpchp_pushbutton_thread); 448 449 mutex_lock(&p_slot->lock); 450 switch (p_slot->state) { 451 case BLINKINGOFF_STATE: 452 p_slot->state = POWEROFF_STATE; 453 break; 454 case BLINKINGON_STATE: 455 p_slot->state = POWERON_STATE; 456 break; 457 default: 458 goto out; 459 } 460 queue_work(shpchp_wq, &info->work); 461 out: 462 mutex_unlock(&p_slot->lock); 463} 464 465static int update_slot_info (struct slot *slot) 466{ 467 struct hotplug_slot_info *info; 468 int result; 469 470 info = kmalloc(sizeof(*info), GFP_KERNEL); 471 if (!info) 472 return -ENOMEM; 473 474 slot->hpc_ops->get_power_status(slot, &(info->power_status)); 475 slot->hpc_ops->get_attention_status(slot, &(info->attention_status)); 476 slot->hpc_ops->get_latch_status(slot, &(info->latch_status)); 477 slot->hpc_ops->get_adapter_status(slot, &(info->adapter_status)); 478 479 result = pci_hp_change_slot_info(slot->hotplug_slot, info); 480 kfree (info); 481 return result; 482} 483 484/* 485 * Note: This function must be called with slot->lock held 486 */ 487static void handle_button_press_event(struct slot *p_slot) 488{ 489 u8 getstatus; 490 491 switch (p_slot->state) { 492 case STATIC_STATE: 493 p_slot->hpc_ops->get_power_status(p_slot, &getstatus); 494 if (getstatus) { 495 p_slot->state = BLINKINGOFF_STATE; 496 info("PCI slot #%s - powering off due to button " 497 "press.\n", p_slot->name); 498 } else { 499 p_slot->state = BLINKINGON_STATE; 500 info("PCI slot #%s - powering on due to button " 501 "press.\n", p_slot->name); 502 } 503 /* blink green LED and turn off amber */ 504 p_slot->hpc_ops->green_led_blink(p_slot); 505 p_slot->hpc_ops->set_attention_status(p_slot, 0); 506 507 schedule_delayed_work(&p_slot->work, 5*HZ); 508 break; 509 case BLINKINGOFF_STATE: 510 case BLINKINGON_STATE: 511 /* 512 * Cancel if we are still blinking; this means that we 513 * press the attention again before the 5 sec. limit 514 * expires to cancel hot-add or hot-remove 515 */ 516 info("Button cancel on Slot(%s)\n", p_slot->name); 517 dbg("%s: button cancel\n", __FUNCTION__); 518 cancel_delayed_work(&p_slot->work); 519 if (p_slot->state == BLINKINGOFF_STATE) 520 p_slot->hpc_ops->green_led_on(p_slot); 521 else 522 p_slot->hpc_ops->green_led_off(p_slot); 523 p_slot->hpc_ops->set_attention_status(p_slot, 0); 524 info("PCI slot #%s - action canceled due to button press\n", 525 p_slot->name); 526 p_slot->state = STATIC_STATE; 527 break; 528 case POWEROFF_STATE: 529 case POWERON_STATE: 530 /* 531 * Ignore if the slot is on power-on or power-off state; 532 * this means that the previous attention button action 533 * to hot-add or hot-remove is undergoing 534 */ 535 info("Button ignore on Slot(%s)\n", p_slot->name); 536 update_slot_info(p_slot); 537 break; 538 default: 539 warn("Not a valid state\n"); 540 break; 541 } 542} 543 544static void interrupt_event_handler(struct work_struct *work) 545{ 546 struct event_info *info = container_of(work, struct event_info, work); 547 struct slot *p_slot = info->p_slot; 548 549 mutex_lock(&p_slot->lock); 550 switch (info->event_type) { 551 case INT_BUTTON_PRESS: 552 handle_button_press_event(p_slot); 553 break; 554 case INT_POWER_FAULT: 555 dbg("%s: power fault\n", __FUNCTION__); 556 p_slot->hpc_ops->set_attention_status(p_slot, 1); 557 p_slot->hpc_ops->green_led_off(p_slot); 558 break; 559 default: 560 update_slot_info(p_slot); 561 break; 562 } 563 mutex_unlock(&p_slot->lock); 564 565 kfree(info); 566} 567 568 569static int shpchp_enable_slot (struct slot *p_slot) 570{ 571 u8 getstatus = 0; 572 int rc, retval = -ENODEV; 573 574 /* Check to see if (latch closed, card present, power off) */ 575 mutex_lock(&p_slot->ctrl->crit_sect); 576 rc = p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus); 577 if (rc || !getstatus) { 578 info("No adapter on slot(%s)\n", p_slot->name); 579 goto out; 580 } 581 rc = p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); 582 if (rc || getstatus) { 583 info("Latch open on slot(%s)\n", p_slot->name); 584 goto out; 585 } 586 rc = p_slot->hpc_ops->get_power_status(p_slot, &getstatus); 587 if (rc || getstatus) { 588 info("Already enabled on slot(%s)\n", p_slot->name); 589 goto out; 590 } 591 592 p_slot->is_a_board = 1; 593 594 /* We have to save the presence info for these slots */ 595 p_slot->hpc_ops->get_adapter_status(p_slot, &(p_slot->presence_save)); 596 p_slot->hpc_ops->get_power_status(p_slot, &(p_slot->pwr_save)); 597 dbg("%s: p_slot->pwr_save %x\n", __FUNCTION__, p_slot->pwr_save); 598 p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); 599 600 if(((p_slot->ctrl->pci_dev->vendor == PCI_VENDOR_ID_AMD) || 601 (p_slot->ctrl->pci_dev->device == PCI_DEVICE_ID_AMD_POGO_7458)) 602 && p_slot->ctrl->num_slots == 1) { 603 /* handle amd pogo errata; this must be done before enable */ 604 amd_pogo_errata_save_misc_reg(p_slot); 605 retval = board_added(p_slot); 606 /* handle amd pogo errata; this must be done after enable */ 607 amd_pogo_errata_restore_misc_reg(p_slot); 608 } else 609 retval = board_added(p_slot); 610 611 if (retval) { 612 p_slot->hpc_ops->get_adapter_status(p_slot, 613 &(p_slot->presence_save)); 614 p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); 615 } 616 617 update_slot_info(p_slot); 618 out: 619 mutex_unlock(&p_slot->ctrl->crit_sect); 620 return retval; 621} 622 623 624static int shpchp_disable_slot (struct slot *p_slot) 625{ 626 u8 getstatus = 0; 627 int rc, retval = -ENODEV; 628 629 if (!p_slot->ctrl) 630 return -ENODEV; 631 632 /* Check to see if (latch closed, card present, power on) */ 633 mutex_lock(&p_slot->ctrl->crit_sect); 634 635 rc = p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus); 636 if (rc || !getstatus) { 637 info("No adapter on slot(%s)\n", p_slot->name); 638 goto out; 639 } 640 rc = p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); 641 if (rc || getstatus) { 642 info("Latch open on slot(%s)\n", p_slot->name); 643 goto out; 644 } 645 rc = p_slot->hpc_ops->get_power_status(p_slot, &getstatus); 646 if (rc || !getstatus) { 647 info("Already disabled slot(%s)\n", p_slot->name); 648 goto out; 649 } 650 651 retval = remove_board(p_slot); 652 update_slot_info(p_slot); 653 out: 654 mutex_unlock(&p_slot->ctrl->crit_sect); 655 return retval; 656} 657 658int shpchp_sysfs_enable_slot(struct slot *p_slot) 659{ 660 int retval = -ENODEV; 661 662 mutex_lock(&p_slot->lock); 663 switch (p_slot->state) { 664 case BLINKINGON_STATE: 665 cancel_delayed_work(&p_slot->work); 666 case STATIC_STATE: 667 p_slot->state = POWERON_STATE; 668 mutex_unlock(&p_slot->lock); 669 retval = shpchp_enable_slot(p_slot); 670 mutex_lock(&p_slot->lock); 671 p_slot->state = STATIC_STATE; 672 break; 673 case POWERON_STATE: 674 info("Slot %s is already in powering on state\n", 675 p_slot->name); 676 break; 677 case BLINKINGOFF_STATE: 678 case POWEROFF_STATE: 679 info("Already enabled on slot %s\n", p_slot->name); 680 break; 681 default: 682 err("Not a valid state on slot %s\n", p_slot->name); 683 break; 684 } 685 mutex_unlock(&p_slot->lock); 686 687 return retval; 688} 689 690int shpchp_sysfs_disable_slot(struct slot *p_slot) 691{ 692 int retval = -ENODEV; 693 694 mutex_lock(&p_slot->lock); 695 switch (p_slot->state) { 696 case BLINKINGOFF_STATE: 697 cancel_delayed_work(&p_slot->work); 698 case STATIC_STATE: 699 p_slot->state = POWEROFF_STATE; 700 mutex_unlock(&p_slot->lock); 701 retval = shpchp_disable_slot(p_slot); 702 mutex_lock(&p_slot->lock); 703 p_slot->state = STATIC_STATE; 704 break; 705 case POWEROFF_STATE: 706 info("Slot %s is already in powering off state\n", 707 p_slot->name); 708 break; 709 case BLINKINGON_STATE: 710 case POWERON_STATE: 711 info("Already disabled on slot %s\n", p_slot->name); 712 break; 713 default: 714 err("Not a valid state on slot %s\n", p_slot->name); 715 break; 716 } 717 mutex_unlock(&p_slot->lock); 718 719 return retval; 720} 721