1 /* 2 * PCI HotPlug Controller Core 3 * 4 * Copyright (C) 2001-2002 Greg Kroah-Hartman (greg@kroah.com) 5 * Copyright (C) 2001-2002 IBM Corp. 6 * 7 * All rights reserved. 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License as published by 11 * the Free Software Foundation; either version 2 of the License, or (at 12 * your option) any later version. 13 * 14 * This program is distributed in the hope that it will be useful, but 15 * WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or 17 * NON INFRINGEMENT. See the GNU General Public License for more 18 * details. 19 * 20 * You should have received a copy of the GNU General Public License 21 * along with this program; if not, write to the Free Software 22 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 23 * 24 * Send feedback to <kristen.c.accardi@intel.com> 25 * 26 */ 27 28 #include <linux/module.h> 29 #include <linux/moduleparam.h> 30 #include <linux/kernel.h> 31 #include <linux/types.h> 32 #include <linux/list.h> 33 #include <linux/kobject.h> 34 #include <linux/sysfs.h> 35 #include <linux/pagemap.h> 36 #include <linux/slab.h> 37 #include <linux/init.h> 38 #include <linux/mount.h> 39 #include <linux/namei.h> 40 #include <linux/mutex.h> 41 #include <linux/pci.h> 42 #include <linux/pci_hotplug.h> 43 #include <asm/uaccess.h> 44 #include "../pci.h" 45 46 #define MY_NAME "pci_hotplug" 47 48 #define dbg(fmt, arg...) do { if (debug) printk(KERN_DEBUG "%s: %s: " fmt , MY_NAME , __func__ , ## arg); } while (0) 49 #define err(format, arg...) printk(KERN_ERR "%s: " format , MY_NAME , ## arg) 50 #define info(format, arg...) printk(KERN_INFO "%s: " format , MY_NAME , ## arg) 51 #define warn(format, arg...) printk(KERN_WARNING "%s: " format , MY_NAME , ## arg) 52 53 54 /* local variables */ 55 static int debug; 56 57 #define DRIVER_VERSION "0.5" 58 #define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>, Scott Murray <scottm@somanetworks.com>" 59 #define DRIVER_DESC "PCI Hot Plug PCI Core" 60 61 62 ////////////////////////////////////////////////////////////////// 63 64 static LIST_HEAD(pci_hotplug_slot_list); 65 static DEFINE_MUTEX(pci_hp_mutex); 66 67 /* these strings match up with the values in pci_bus_speed */ 68 static char *pci_bus_speed_strings[] = { 69 "33 MHz PCI", /* 0x00 */ 70 "66 MHz PCI", /* 0x01 */ 71 "66 MHz PCIX", /* 0x02 */ 72 "100 MHz PCIX", /* 0x03 */ 73 "133 MHz PCIX", /* 0x04 */ 74 NULL, /* 0x05 */ 75 NULL, /* 0x06 */ 76 NULL, /* 0x07 */ 77 NULL, /* 0x08 */ 78 "66 MHz PCIX 266", /* 0x09 */ 79 "100 MHz PCIX 266", /* 0x0a */ 80 "133 MHz PCIX 266", /* 0x0b */ 81 NULL, /* 0x0c */ 82 NULL, /* 0x0d */ 83 NULL, /* 0x0e */ 84 NULL, /* 0x0f */ 85 NULL, /* 0x10 */ 86 "66 MHz PCIX 533", /* 0x11 */ 87 "100 MHz PCIX 533", /* 0x12 */ 88 "133 MHz PCIX 533", /* 0x13 */ 89 "2.5 GT/s PCI-E", /* 0x14 */ 90 "5.0 GT/s PCI-E", /* 0x15 */ 91 }; 92 93 #ifdef CONFIG_HOTPLUG_PCI_CPCI 94 extern int cpci_hotplug_init(int debug); 95 extern void cpci_hotplug_exit(void); 96 #else 97 static inline int cpci_hotplug_init(int debug) { return 0; } 98 static inline void cpci_hotplug_exit(void) { } 99 #endif 100 101 /* Weee, fun with macros... */ 102 #define GET_STATUS(name,type) \ 103 static int get_##name (struct hotplug_slot *slot, type *value) \ 104 { \ 105 struct hotplug_slot_ops *ops = slot->ops; \ 106 int retval = 0; \ 107 if (!try_module_get(ops->owner)) \ 108 return -ENODEV; \ 109 if (ops->get_##name) \ 110 retval = ops->get_##name(slot, value); \ 111 else \ 112 *value = slot->info->name; \ 113 module_put(ops->owner); \ 114 return retval; \ 115 } 116 117 GET_STATUS(power_status, u8) 118 GET_STATUS(attention_status, u8) 119 GET_STATUS(latch_status, u8) 120 GET_STATUS(adapter_status, u8) 121 GET_STATUS(max_bus_speed, enum pci_bus_speed) 122 GET_STATUS(cur_bus_speed, enum pci_bus_speed) 123 124 static ssize_t power_read_file(struct pci_slot *slot, char *buf) 125 { 126 int retval; 127 u8 value; 128 129 retval = get_power_status(slot->hotplug, &value); 130 if (retval) 131 goto exit; 132 retval = sprintf (buf, "%d\n", value); 133 exit: 134 return retval; 135 } 136 137 static ssize_t power_write_file(struct pci_slot *pci_slot, const char *buf, 138 size_t count) 139 { 140 struct hotplug_slot *slot = pci_slot->hotplug; 141 unsigned long lpower; 142 u8 power; 143 int retval = 0; 144 145 lpower = simple_strtoul (buf, NULL, 10); 146 power = (u8)(lpower & 0xff); 147 dbg ("power = %d\n", power); 148 149 if (!try_module_get(slot->ops->owner)) { 150 retval = -ENODEV; 151 goto exit; 152 } 153 switch (power) { 154 case 0: 155 if (slot->ops->disable_slot) 156 retval = slot->ops->disable_slot(slot); 157 break; 158 159 case 1: 160 if (slot->ops->enable_slot) 161 retval = slot->ops->enable_slot(slot); 162 break; 163 164 default: 165 err ("Illegal value specified for power\n"); 166 retval = -EINVAL; 167 } 168 module_put(slot->ops->owner); 169 170 exit: 171 if (retval) 172 return retval; 173 return count; 174 } 175 176 static struct pci_slot_attribute hotplug_slot_attr_power = { 177 .attr = {.name = "power", .mode = S_IFREG | S_IRUGO | S_IWUSR}, 178 .show = power_read_file, 179 .store = power_write_file 180 }; 181 182 static ssize_t attention_read_file(struct pci_slot *slot, char *buf) 183 { 184 int retval; 185 u8 value; 186 187 retval = get_attention_status(slot->hotplug, &value); 188 if (retval) 189 goto exit; 190 retval = sprintf(buf, "%d\n", value); 191 192 exit: 193 return retval; 194 } 195 196 static ssize_t attention_write_file(struct pci_slot *slot, const char *buf, 197 size_t count) 198 { 199 struct hotplug_slot_ops *ops = slot->hotplug->ops; 200 unsigned long lattention; 201 u8 attention; 202 int retval = 0; 203 204 lattention = simple_strtoul (buf, NULL, 10); 205 attention = (u8)(lattention & 0xff); 206 dbg (" - attention = %d\n", attention); 207 208 if (!try_module_get(ops->owner)) { 209 retval = -ENODEV; 210 goto exit; 211 } 212 if (ops->set_attention_status) 213 retval = ops->set_attention_status(slot->hotplug, attention); 214 module_put(ops->owner); 215 216 exit: 217 if (retval) 218 return retval; 219 return count; 220 } 221 222 static struct pci_slot_attribute hotplug_slot_attr_attention = { 223 .attr = {.name = "attention", .mode = S_IFREG | S_IRUGO | S_IWUSR}, 224 .show = attention_read_file, 225 .store = attention_write_file 226 }; 227 228 static ssize_t latch_read_file(struct pci_slot *slot, char *buf) 229 { 230 int retval; 231 u8 value; 232 233 retval = get_latch_status(slot->hotplug, &value); 234 if (retval) 235 goto exit; 236 retval = sprintf (buf, "%d\n", value); 237 238 exit: 239 return retval; 240 } 241 242 static struct pci_slot_attribute hotplug_slot_attr_latch = { 243 .attr = {.name = "latch", .mode = S_IFREG | S_IRUGO}, 244 .show = latch_read_file, 245 }; 246 247 static ssize_t presence_read_file(struct pci_slot *slot, char *buf) 248 { 249 int retval; 250 u8 value; 251 252 retval = get_adapter_status(slot->hotplug, &value); 253 if (retval) 254 goto exit; 255 retval = sprintf (buf, "%d\n", value); 256 257 exit: 258 return retval; 259 } 260 261 static struct pci_slot_attribute hotplug_slot_attr_presence = { 262 .attr = {.name = "adapter", .mode = S_IFREG | S_IRUGO}, 263 .show = presence_read_file, 264 }; 265 266 static char *unknown_speed = "Unknown bus speed"; 267 268 static ssize_t max_bus_speed_read_file(struct pci_slot *slot, char *buf) 269 { 270 char *speed_string; 271 int retval; 272 enum pci_bus_speed value; 273 274 retval = get_max_bus_speed(slot->hotplug, &value); 275 if (retval) 276 goto exit; 277 278 if (value == PCI_SPEED_UNKNOWN) 279 speed_string = unknown_speed; 280 else 281 speed_string = pci_bus_speed_strings[value]; 282 283 retval = sprintf (buf, "%s\n", speed_string); 284 285 exit: 286 return retval; 287 } 288 289 static struct pci_slot_attribute hotplug_slot_attr_max_bus_speed = { 290 .attr = {.name = "max_bus_speed", .mode = S_IFREG | S_IRUGO}, 291 .show = max_bus_speed_read_file, 292 }; 293 294 static ssize_t cur_bus_speed_read_file(struct pci_slot *slot, char *buf) 295 { 296 char *speed_string; 297 int retval; 298 enum pci_bus_speed value; 299 300 retval = get_cur_bus_speed(slot->hotplug, &value); 301 if (retval) 302 goto exit; 303 304 if (value == PCI_SPEED_UNKNOWN) 305 speed_string = unknown_speed; 306 else 307 speed_string = pci_bus_speed_strings[value]; 308 309 retval = sprintf (buf, "%s\n", speed_string); 310 311 exit: 312 return retval; 313 } 314 315 static struct pci_slot_attribute hotplug_slot_attr_cur_bus_speed = { 316 .attr = {.name = "cur_bus_speed", .mode = S_IFREG | S_IRUGO}, 317 .show = cur_bus_speed_read_file, 318 }; 319 320 static ssize_t test_write_file(struct pci_slot *pci_slot, const char *buf, 321 size_t count) 322 { 323 struct hotplug_slot *slot = pci_slot->hotplug; 324 unsigned long ltest; 325 u32 test; 326 int retval = 0; 327 328 ltest = simple_strtoul (buf, NULL, 10); 329 test = (u32)(ltest & 0xffffffff); 330 dbg ("test = %d\n", test); 331 332 if (!try_module_get(slot->ops->owner)) { 333 retval = -ENODEV; 334 goto exit; 335 } 336 if (slot->ops->hardware_test) 337 retval = slot->ops->hardware_test(slot, test); 338 module_put(slot->ops->owner); 339 340 exit: 341 if (retval) 342 return retval; 343 return count; 344 } 345 346 static struct pci_slot_attribute hotplug_slot_attr_test = { 347 .attr = {.name = "test", .mode = S_IFREG | S_IRUGO | S_IWUSR}, 348 .store = test_write_file 349 }; 350 351 static bool has_power_file(struct pci_slot *pci_slot) 352 { 353 struct hotplug_slot *slot = pci_slot->hotplug; 354 if ((!slot) || (!slot->ops)) 355 return false; 356 if ((slot->ops->enable_slot) || 357 (slot->ops->disable_slot) || 358 (slot->ops->get_power_status)) 359 return true; 360 return false; 361 } 362 363 static bool has_attention_file(struct pci_slot *pci_slot) 364 { 365 struct hotplug_slot *slot = pci_slot->hotplug; 366 if ((!slot) || (!slot->ops)) 367 return false; 368 if ((slot->ops->set_attention_status) || 369 (slot->ops->get_attention_status)) 370 return true; 371 return false; 372 } 373 374 static bool has_latch_file(struct pci_slot *pci_slot) 375 { 376 struct hotplug_slot *slot = pci_slot->hotplug; 377 if ((!slot) || (!slot->ops)) 378 return false; 379 if (slot->ops->get_latch_status) 380 return true; 381 return false; 382 } 383 384 static bool has_adapter_file(struct pci_slot *pci_slot) 385 { 386 struct hotplug_slot *slot = pci_slot->hotplug; 387 if ((!slot) || (!slot->ops)) 388 return false; 389 if (slot->ops->get_adapter_status) 390 return true; 391 return false; 392 } 393 394 static bool has_max_bus_speed_file(struct pci_slot *pci_slot) 395 { 396 struct hotplug_slot *slot = pci_slot->hotplug; 397 if ((!slot) || (!slot->ops)) 398 return false; 399 if (slot->ops->get_max_bus_speed) 400 return true; 401 return false; 402 } 403 404 static bool has_cur_bus_speed_file(struct pci_slot *pci_slot) 405 { 406 struct hotplug_slot *slot = pci_slot->hotplug; 407 if ((!slot) || (!slot->ops)) 408 return false; 409 if (slot->ops->get_cur_bus_speed) 410 return true; 411 return false; 412 } 413 414 static bool has_test_file(struct pci_slot *pci_slot) 415 { 416 struct hotplug_slot *slot = pci_slot->hotplug; 417 if ((!slot) || (!slot->ops)) 418 return false; 419 if (slot->ops->hardware_test) 420 return true; 421 return false; 422 } 423 424 static int fs_add_slot(struct pci_slot *slot) 425 { 426 int retval = 0; 427 428 /* Create symbolic link to the hotplug driver module */ 429 pci_hp_create_module_link(slot); 430 431 if (has_power_file(slot)) { 432 retval = sysfs_create_file(&slot->kobj, 433 &hotplug_slot_attr_power.attr); 434 if (retval) 435 goto exit_power; 436 } 437 438 if (has_attention_file(slot)) { 439 retval = sysfs_create_file(&slot->kobj, 440 &hotplug_slot_attr_attention.attr); 441 if (retval) 442 goto exit_attention; 443 } 444 445 if (has_latch_file(slot)) { 446 retval = sysfs_create_file(&slot->kobj, 447 &hotplug_slot_attr_latch.attr); 448 if (retval) 449 goto exit_latch; 450 } 451 452 if (has_adapter_file(slot)) { 453 retval = sysfs_create_file(&slot->kobj, 454 &hotplug_slot_attr_presence.attr); 455 if (retval) 456 goto exit_adapter; 457 } 458 459 if (has_max_bus_speed_file(slot)) { 460 retval = sysfs_create_file(&slot->kobj, 461 &hotplug_slot_attr_max_bus_speed.attr); 462 if (retval) 463 goto exit_max_speed; 464 } 465 466 if (has_cur_bus_speed_file(slot)) { 467 retval = sysfs_create_file(&slot->kobj, 468 &hotplug_slot_attr_cur_bus_speed.attr); 469 if (retval) 470 goto exit_cur_speed; 471 } 472 473 if (has_test_file(slot)) { 474 retval = sysfs_create_file(&slot->kobj, 475 &hotplug_slot_attr_test.attr); 476 if (retval) 477 goto exit_test; 478 } 479 480 goto exit; 481 482 exit_test: 483 if (has_cur_bus_speed_file(slot)) 484 sysfs_remove_file(&slot->kobj, 485 &hotplug_slot_attr_cur_bus_speed.attr); 486 exit_cur_speed: 487 if (has_max_bus_speed_file(slot)) 488 sysfs_remove_file(&slot->kobj, 489 &hotplug_slot_attr_max_bus_speed.attr); 490 exit_max_speed: 491 if (has_adapter_file(slot)) 492 sysfs_remove_file(&slot->kobj, 493 &hotplug_slot_attr_presence.attr); 494 exit_adapter: 495 if (has_latch_file(slot)) 496 sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_latch.attr); 497 exit_latch: 498 if (has_attention_file(slot)) 499 sysfs_remove_file(&slot->kobj, 500 &hotplug_slot_attr_attention.attr); 501 exit_attention: 502 if (has_power_file(slot)) 503 sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_power.attr); 504 exit_power: 505 pci_hp_remove_module_link(slot); 506 exit: 507 return retval; 508 } 509 510 static void fs_remove_slot(struct pci_slot *slot) 511 { 512 if (has_power_file(slot)) 513 sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_power.attr); 514 515 if (has_attention_file(slot)) 516 sysfs_remove_file(&slot->kobj, 517 &hotplug_slot_attr_attention.attr); 518 519 if (has_latch_file(slot)) 520 sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_latch.attr); 521 522 if (has_adapter_file(slot)) 523 sysfs_remove_file(&slot->kobj, 524 &hotplug_slot_attr_presence.attr); 525 526 if (has_max_bus_speed_file(slot)) 527 sysfs_remove_file(&slot->kobj, 528 &hotplug_slot_attr_max_bus_speed.attr); 529 530 if (has_cur_bus_speed_file(slot)) 531 sysfs_remove_file(&slot->kobj, 532 &hotplug_slot_attr_cur_bus_speed.attr); 533 534 if (has_test_file(slot)) 535 sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_test.attr); 536 537 pci_hp_remove_module_link(slot); 538 } 539 540 static struct hotplug_slot *get_slot_from_name (const char *name) 541 { 542 struct hotplug_slot *slot; 543 struct list_head *tmp; 544 545 list_for_each (tmp, &pci_hotplug_slot_list) { 546 slot = list_entry (tmp, struct hotplug_slot, slot_list); 547 if (strcmp(hotplug_slot_name(slot), name) == 0) 548 return slot; 549 } 550 return NULL; 551 } 552 553 /** 554 * __pci_hp_register - register a hotplug_slot with the PCI hotplug subsystem 555 * @bus: bus this slot is on 556 * @slot: pointer to the &struct hotplug_slot to register 557 * @devnr: device number 558 * @name: name registered with kobject core 559 * @owner: caller module owner 560 * @mod_name: caller module name 561 * 562 * Registers a hotplug slot with the pci hotplug subsystem, which will allow 563 * userspace interaction to the slot. 564 * 565 * Returns 0 if successful, anything else for an error. 566 */ 567 int __pci_hp_register(struct hotplug_slot *slot, struct pci_bus *bus, 568 int devnr, const char *name, 569 struct module *owner, const char *mod_name) 570 { 571 int result; 572 struct pci_slot *pci_slot; 573 574 if (slot == NULL) 575 return -ENODEV; 576 if ((slot->info == NULL) || (slot->ops == NULL)) 577 return -EINVAL; 578 if (slot->release == NULL) { 579 dbg("Why are you trying to register a hotplug slot " 580 "without a proper release function?\n"); 581 return -EINVAL; 582 } 583 584 slot->ops->owner = owner; 585 slot->ops->mod_name = mod_name; 586 587 mutex_lock(&pci_hp_mutex); 588 /* 589 * No problems if we call this interface from both ACPI_PCI_SLOT 590 * driver and call it here again. If we've already created the 591 * pci_slot, the interface will simply bump the refcount. 592 */ 593 pci_slot = pci_create_slot(bus, devnr, name, slot); 594 if (IS_ERR(pci_slot)) { 595 result = PTR_ERR(pci_slot); 596 goto out; 597 } 598 599 slot->pci_slot = pci_slot; 600 pci_slot->hotplug = slot; 601 602 list_add(&slot->slot_list, &pci_hotplug_slot_list); 603 604 result = fs_add_slot(pci_slot); 605 kobject_uevent(&pci_slot->kobj, KOBJ_ADD); 606 dbg("Added slot %s to the list\n", name); 607 out: 608 mutex_unlock(&pci_hp_mutex); 609 return result; 610 } 611 612 /** 613 * pci_hp_deregister - deregister a hotplug_slot with the PCI hotplug subsystem 614 * @hotplug: pointer to the &struct hotplug_slot to deregister 615 * 616 * The @slot must have been registered with the pci hotplug subsystem 617 * previously with a call to pci_hp_register(). 618 * 619 * Returns 0 if successful, anything else for an error. 620 */ 621 int pci_hp_deregister(struct hotplug_slot *hotplug) 622 { 623 struct hotplug_slot *temp; 624 struct pci_slot *slot; 625 626 if (!hotplug) 627 return -ENODEV; 628 629 mutex_lock(&pci_hp_mutex); 630 temp = get_slot_from_name(hotplug_slot_name(hotplug)); 631 if (temp != hotplug) { 632 mutex_unlock(&pci_hp_mutex); 633 return -ENODEV; 634 } 635 636 list_del(&hotplug->slot_list); 637 638 slot = hotplug->pci_slot; 639 fs_remove_slot(slot); 640 dbg("Removed slot %s from the list\n", hotplug_slot_name(hotplug)); 641 642 hotplug->release(hotplug); 643 slot->hotplug = NULL; 644 pci_destroy_slot(slot); 645 mutex_unlock(&pci_hp_mutex); 646 647 return 0; 648 } 649 650 /** 651 * pci_hp_change_slot_info - changes the slot's information structure in the core 652 * @hotplug: pointer to the slot whose info has changed 653 * @info: pointer to the info copy into the slot's info structure 654 * 655 * @slot must have been registered with the pci 656 * hotplug subsystem previously with a call to pci_hp_register(). 657 * 658 * Returns 0 if successful, anything else for an error. 659 */ 660 int __must_check pci_hp_change_slot_info(struct hotplug_slot *hotplug, 661 struct hotplug_slot_info *info) 662 { 663 struct pci_slot *slot; 664 if (!hotplug || !info) 665 return -ENODEV; 666 slot = hotplug->pci_slot; 667 668 memcpy(hotplug->info, info, sizeof(struct hotplug_slot_info)); 669 670 return 0; 671 } 672 673 static int __init pci_hotplug_init (void) 674 { 675 int result; 676 677 result = cpci_hotplug_init(debug); 678 if (result) { 679 err ("cpci_hotplug_init with error %d\n", result); 680 goto err_cpci; 681 } 682 683 info (DRIVER_DESC " version: " DRIVER_VERSION "\n"); 684 685 err_cpci: 686 return result; 687 } 688 689 static void __exit pci_hotplug_exit (void) 690 { 691 cpci_hotplug_exit(); 692 } 693 694 module_init(pci_hotplug_init); 695 module_exit(pci_hotplug_exit); 696 697 MODULE_AUTHOR(DRIVER_AUTHOR); 698 MODULE_DESCRIPTION(DRIVER_DESC); 699 MODULE_LICENSE("GPL"); 700 module_param(debug, bool, 0644); 701 MODULE_PARM_DESC(debug, "Debugging mode enabled or not"); 702 703 EXPORT_SYMBOL_GPL(__pci_hp_register); 704 EXPORT_SYMBOL_GPL(pci_hp_deregister); 705 EXPORT_SYMBOL_GPL(pci_hp_change_slot_info); 706