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