shpchp_ctrl.c revision 99ff124d1673a1e3f19061ebc82634608d1119ed
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(void *data); 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, info); 54 55 schedule_work(&info->work); 56 57 return 0; 58} 59 60u8 shpchp_handle_attention_button(u8 hp_slot, void *inst_id) 61{ 62 struct controller *ctrl = (struct controller *) inst_id; 63 struct slot *p_slot; 64 u32 event_type; 65 66 /* Attention Button Change */ 67 dbg("shpchp: Attention button interrupt received.\n"); 68 69 p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); 70 p_slot->hpc_ops->get_adapter_status(p_slot, &(p_slot->presence_save)); 71 72 /* 73 * Button pressed - See if need to TAKE ACTION!!! 74 */ 75 info("Button pressed on Slot(%s)\n", p_slot->name); 76 event_type = INT_BUTTON_PRESS; 77 78 queue_interrupt_event(p_slot, event_type); 79 80 return 0; 81 82} 83 84u8 shpchp_handle_switch_change(u8 hp_slot, void *inst_id) 85{ 86 struct controller *ctrl = (struct controller *) inst_id; 87 struct slot *p_slot; 88 u8 getstatus; 89 u32 event_type; 90 91 /* Switch Change */ 92 dbg("shpchp: Switch interrupt received.\n"); 93 94 p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); 95 p_slot->hpc_ops->get_adapter_status(p_slot, &(p_slot->presence_save)); 96 p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); 97 dbg("%s: Card present %x Power status %x\n", __FUNCTION__, 98 p_slot->presence_save, p_slot->pwr_save); 99 100 if (getstatus) { 101 /* 102 * Switch opened 103 */ 104 info("Latch open on Slot(%s)\n", p_slot->name); 105 event_type = INT_SWITCH_OPEN; 106 if (p_slot->pwr_save && p_slot->presence_save) { 107 event_type = INT_POWER_FAULT; 108 err("Surprise Removal of card\n"); 109 } 110 } else { 111 /* 112 * Switch closed 113 */ 114 info("Latch close on Slot(%s)\n", p_slot->name); 115 event_type = INT_SWITCH_CLOSE; 116 } 117 118 queue_interrupt_event(p_slot, event_type); 119 120 return 1; 121} 122 123u8 shpchp_handle_presence_change(u8 hp_slot, void *inst_id) 124{ 125 struct controller *ctrl = (struct controller *) inst_id; 126 struct slot *p_slot; 127 u32 event_type; 128 129 /* Presence Change */ 130 dbg("shpchp: Presence/Notify input change.\n"); 131 132 p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); 133 134 /* 135 * Save the presence state 136 */ 137 p_slot->hpc_ops->get_adapter_status(p_slot, &(p_slot->presence_save)); 138 if (p_slot->presence_save) { 139 /* 140 * Card Present 141 */ 142 info("Card present on Slot(%s)\n", p_slot->name); 143 event_type = INT_PRESENCE_ON; 144 } else { 145 /* 146 * Not Present 147 */ 148 info("Card not present on Slot(%s)\n", p_slot->name); 149 event_type = INT_PRESENCE_OFF; 150 } 151 152 queue_interrupt_event(p_slot, event_type); 153 154 return 1; 155} 156 157u8 shpchp_handle_power_fault(u8 hp_slot, void *inst_id) 158{ 159 struct controller *ctrl = (struct controller *) inst_id; 160 struct slot *p_slot; 161 u32 event_type; 162 163 /* Power fault */ 164 dbg("shpchp: Power fault interrupt received.\n"); 165 166 p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); 167 168 if ( !(p_slot->hpc_ops->query_power_fault(p_slot))) { 169 /* 170 * Power fault Cleared 171 */ 172 info("Power fault cleared on Slot(%s)\n", p_slot->name); 173 p_slot->status = 0x00; 174 event_type = INT_POWER_FAULT_CLEAR; 175 } else { 176 /* 177 * Power fault 178 */ 179 info("Power fault on Slot(%s)\n", p_slot->name); 180 event_type = INT_POWER_FAULT; 181 /* set power fault status for this board */ 182 p_slot->status = 0xFF; 183 info("power fault bit %x set\n", hp_slot); 184 } 185 186 queue_interrupt_event(p_slot, event_type); 187 188 return 1; 189} 190 191/* The following routines constitute the bulk of the 192 hotplug controller logic 193 */ 194static int change_bus_speed(struct controller *ctrl, struct slot *p_slot, 195 enum pci_bus_speed speed) 196{ 197 int rc = 0; 198 199 dbg("%s: change to speed %d\n", __FUNCTION__, speed); 200 if ((rc = p_slot->hpc_ops->set_bus_speed_mode(p_slot, speed))) { 201 err("%s: Issue of set bus speed mode command failed\n", 202 __FUNCTION__); 203 return WRONG_BUS_FREQUENCY; 204 } 205 return rc; 206} 207 208static int fix_bus_speed(struct controller *ctrl, struct slot *pslot, 209 u8 flag, enum pci_bus_speed asp, enum pci_bus_speed bsp, 210 enum pci_bus_speed msp) 211{ 212 int rc = 0; 213 214 /* 215 * If other slots on the same bus are occupied, we cannot 216 * change the bus speed. 217 */ 218 if (flag) { 219 if (asp < bsp) { 220 err("%s: speed of bus %x and adapter %x mismatch\n", 221 __FUNCTION__, bsp, asp); 222 rc = WRONG_BUS_FREQUENCY; 223 } 224 return rc; 225 } 226 227 if (asp < msp) { 228 if (bsp != asp) 229 rc = change_bus_speed(ctrl, pslot, asp); 230 } else { 231 if (bsp != msp) 232 rc = change_bus_speed(ctrl, pslot, msp); 233 } 234 return rc; 235} 236 237/** 238 * board_added - Called after a board has been added to the system. 239 * 240 * Turns power on for the board 241 * Configures board 242 * 243 */ 244static int board_added(struct slot *p_slot) 245{ 246 u8 hp_slot; 247 u8 slots_not_empty = 0; 248 int rc = 0; 249 enum pci_bus_speed asp, bsp, msp; 250 struct controller *ctrl = p_slot->ctrl; 251 252 hp_slot = p_slot->device - ctrl->slot_device_offset; 253 254 dbg("%s: p_slot->device, slot_offset, hp_slot = %d, %d ,%d\n", 255 __FUNCTION__, p_slot->device, 256 ctrl->slot_device_offset, hp_slot); 257 258 /* Power on slot without connecting to bus */ 259 rc = p_slot->hpc_ops->power_on_slot(p_slot); 260 if (rc) { 261 err("%s: Failed to power on slot\n", __FUNCTION__); 262 return -1; 263 } 264 265 if ((ctrl->pci_dev->vendor == 0x8086) && (ctrl->pci_dev->device == 0x0332)) { 266 if (slots_not_empty) 267 return WRONG_BUS_FREQUENCY; 268 269 if ((rc = p_slot->hpc_ops->set_bus_speed_mode(p_slot, PCI_SPEED_33MHz))) { 270 err("%s: Issue of set bus speed mode command failed\n", __FUNCTION__); 271 return WRONG_BUS_FREQUENCY; 272 } 273 274 /* turn on board, blink green LED, turn off Amber LED */ 275 if ((rc = p_slot->hpc_ops->slot_enable(p_slot))) { 276 err("%s: Issue of Slot Enable command failed\n", __FUNCTION__); 277 return rc; 278 } 279 } 280 281 rc = p_slot->hpc_ops->get_adapter_speed(p_slot, &asp); 282 if (rc) { 283 err("%s: Can't get adapter speed or bus mode mismatch\n", 284 __FUNCTION__); 285 return WRONG_BUS_FREQUENCY; 286 } 287 288 rc = p_slot->hpc_ops->get_cur_bus_speed(p_slot, &bsp); 289 if (rc) { 290 err("%s: Can't get bus operation speed\n", __FUNCTION__); 291 return WRONG_BUS_FREQUENCY; 292 } 293 294 rc = p_slot->hpc_ops->get_max_bus_speed(p_slot, &msp); 295 if (rc) { 296 err("%s: Can't get max bus operation speed\n", __FUNCTION__); 297 msp = bsp; 298 } 299 300 /* Check if there are other slots or devices on the same bus */ 301 if (!list_empty(&ctrl->pci_dev->subordinate->devices)) 302 slots_not_empty = 1; 303 304 dbg("%s: slots_not_empty %d, adapter_speed %d, bus_speed %d, " 305 "max_bus_speed %d\n", __FUNCTION__, slots_not_empty, asp, 306 bsp, msp); 307 308 rc = fix_bus_speed(ctrl, p_slot, slots_not_empty, asp, bsp, msp); 309 if (rc) 310 return rc; 311 312 /* turn on board, blink green LED, turn off Amber LED */ 313 if ((rc = p_slot->hpc_ops->slot_enable(p_slot))) { 314 err("%s: Issue of Slot Enable command failed\n", __FUNCTION__); 315 return rc; 316 } 317 318 /* Wait for ~1 second */ 319 msleep(1000); 320 321 dbg("%s: slot status = %x\n", __FUNCTION__, p_slot->status); 322 /* Check for a power fault */ 323 if (p_slot->status == 0xFF) { 324 /* power fault occurred, but it was benign */ 325 dbg("%s: power fault\n", __FUNCTION__); 326 rc = POWER_FAILURE; 327 p_slot->status = 0; 328 goto err_exit; 329 } 330 331 if (shpchp_configure_device(p_slot)) { 332 err("Cannot add device at 0x%x:0x%x\n", p_slot->bus, 333 p_slot->device); 334 goto err_exit; 335 } 336 337 p_slot->status = 0; 338 p_slot->is_a_board = 0x01; 339 p_slot->pwr_save = 1; 340 341 p_slot->hpc_ops->green_led_on(p_slot); 342 343 return 0; 344 345err_exit: 346 /* turn off slot, turn on Amber LED, turn off Green LED */ 347 rc = p_slot->hpc_ops->slot_disable(p_slot); 348 if (rc) { 349 err("%s: Issue of Slot Disable command failed\n", __FUNCTION__); 350 return rc; 351 } 352 353 return(rc); 354} 355 356 357/** 358 * remove_board - Turns off slot and LED's 359 * 360 */ 361static int remove_board(struct slot *p_slot) 362{ 363 struct controller *ctrl = p_slot->ctrl; 364 u8 hp_slot; 365 int rc; 366 367 if (shpchp_unconfigure_device(p_slot)) 368 return(1); 369 370 hp_slot = p_slot->device - ctrl->slot_device_offset; 371 p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); 372 373 dbg("In %s, hp_slot = %d\n", __FUNCTION__, hp_slot); 374 375 /* Change status to shutdown */ 376 if (p_slot->is_a_board) 377 p_slot->status = 0x01; 378 379 /* turn off slot, turn on Amber LED, turn off Green LED */ 380 rc = p_slot->hpc_ops->slot_disable(p_slot); 381 if (rc) { 382 err("%s: Issue of Slot Disable command failed\n", __FUNCTION__); 383 return rc; 384 } 385 386 rc = p_slot->hpc_ops->set_attention_status(p_slot, 0); 387 if (rc) { 388 err("%s: Issue of Set Attention command failed\n", __FUNCTION__); 389 return rc; 390 } 391 392 p_slot->pwr_save = 0; 393 p_slot->is_a_board = 0; 394 395 return 0; 396} 397 398 399struct pushbutton_work_info { 400 struct slot *p_slot; 401 struct work_struct work; 402}; 403 404/** 405 * shpchp_pushbutton_thread 406 * 407 * Scheduled procedure to handle blocking stuff for the pushbuttons 408 * Handles all pending events and exits. 409 * 410 */ 411static void shpchp_pushbutton_thread(void *data) 412{ 413 struct pushbutton_work_info *info = data; 414 struct slot *p_slot = info->p_slot; 415 416 mutex_lock(&p_slot->lock); 417 switch (p_slot->state) { 418 case POWEROFF_STATE: 419 mutex_unlock(&p_slot->lock); 420 shpchp_disable_slot(p_slot); 421 mutex_lock(&p_slot->lock); 422 p_slot->state = STATIC_STATE; 423 break; 424 case POWERON_STATE: 425 mutex_unlock(&p_slot->lock); 426 if (shpchp_enable_slot(p_slot)) 427 p_slot->hpc_ops->green_led_off(p_slot); 428 mutex_lock(&p_slot->lock); 429 p_slot->state = STATIC_STATE; 430 break; 431 default: 432 break; 433 } 434 mutex_unlock(&p_slot->lock); 435 436 kfree(info); 437} 438 439void queue_pushbutton_work(void *data) 440{ 441 struct slot *p_slot = data; 442 struct pushbutton_work_info *info; 443 444 info = kmalloc(sizeof(*info), GFP_KERNEL); 445 if (!info) { 446 err("%s: Cannot allocate memory\n", __FUNCTION__); 447 return; 448 } 449 info->p_slot = p_slot; 450 INIT_WORK(&info->work, shpchp_pushbutton_thread, info); 451 452 mutex_lock(&p_slot->lock); 453 switch (p_slot->state) { 454 case BLINKINGOFF_STATE: 455 p_slot->state = POWEROFF_STATE; 456 break; 457 case BLINKINGON_STATE: 458 p_slot->state = POWERON_STATE; 459 break; 460 default: 461 goto out; 462 } 463 queue_work(shpchp_wq, &info->work); 464 out: 465 mutex_unlock(&p_slot->lock); 466} 467 468static int update_slot_info (struct slot *slot) 469{ 470 struct hotplug_slot_info *info; 471 int result; 472 473 info = kmalloc(sizeof(*info), GFP_KERNEL); 474 if (!info) 475 return -ENOMEM; 476 477 slot->hpc_ops->get_power_status(slot, &(info->power_status)); 478 slot->hpc_ops->get_attention_status(slot, &(info->attention_status)); 479 slot->hpc_ops->get_latch_status(slot, &(info->latch_status)); 480 slot->hpc_ops->get_adapter_status(slot, &(info->adapter_status)); 481 482 result = pci_hp_change_slot_info(slot->hotplug_slot, info); 483 kfree (info); 484 return result; 485} 486 487/* 488 * Note: This function must be called with slot->lock held 489 */ 490static void handle_button_press_event(struct slot *p_slot) 491{ 492 u8 getstatus; 493 494 switch (p_slot->state) { 495 case STATIC_STATE: 496 p_slot->hpc_ops->get_power_status(p_slot, &getstatus); 497 if (getstatus) { 498 p_slot->state = BLINKINGOFF_STATE; 499 info(msg_button_off, p_slot->name); 500 } else { 501 p_slot->state = BLINKINGON_STATE; 502 info(msg_button_on, p_slot->name); 503 } 504 /* blink green LED and turn off amber */ 505 p_slot->hpc_ops->green_led_blink(p_slot); 506 p_slot->hpc_ops->set_attention_status(p_slot, 0); 507 508 schedule_delayed_work(&p_slot->work, 5*HZ); 509 break; 510 case BLINKINGOFF_STATE: 511 case BLINKINGON_STATE: 512 /* 513 * Cancel if we are still blinking; this means that we 514 * press the attention again before the 5 sec. limit 515 * expires to cancel hot-add or hot-remove 516 */ 517 info("Button cancel on Slot(%s)\n", p_slot->name); 518 dbg("%s: button cancel\n", __FUNCTION__); 519 cancel_delayed_work(&p_slot->work); 520 if (p_slot->state == BLINKINGOFF_STATE) 521 p_slot->hpc_ops->green_led_on(p_slot); 522 else 523 p_slot->hpc_ops->green_led_off(p_slot); 524 p_slot->hpc_ops->set_attention_status(p_slot, 0); 525 info(msg_button_cancel, 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(void *data) 545{ 546 struct event_info *info = data; 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