Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v2.6.16-rc3 598 lines 15 kB view raw
1/* 2 * This file is subject to the terms and conditions of the GNU General Public 3 * License. See the file "COPYING" in the main directory of this archive 4 * for more details. 5 * 6 * Copyright (C) 2005 Silicon Graphics, Inc. All rights reserved. 7 * 8 * This work was based on the 2.4/2.6 kernel development by Dick Reigner. 9 * Work to add BIOS PROM support was completed by Mike Habeck. 10 */ 11 12#include <linux/init.h> 13#include <linux/kernel.h> 14#include <linux/module.h> 15#include <linux/pci.h> 16#include <linux/proc_fs.h> 17#include <linux/types.h> 18 19#include <asm/sn/addrs.h> 20#include <asm/sn/l1.h> 21#include <asm/sn/module.h> 22#include <asm/sn/pcibr_provider.h> 23#include <asm/sn/pcibus_provider_defs.h> 24#include <asm/sn/pcidev.h> 25#include <asm/sn/sn_sal.h> 26#include <asm/sn/types.h> 27 28#include "../pci.h" 29#include "pci_hotplug.h" 30 31MODULE_LICENSE("GPL"); 32MODULE_AUTHOR("SGI (prarit@sgi.com, dickie@sgi.com, habeck@sgi.com)"); 33MODULE_DESCRIPTION("SGI Altix Hot Plug PCI Controller Driver"); 34 35#define PCIIO_ASIC_TYPE_TIOCA 4 36#define PCI_SLOT_ALREADY_UP 2 /* slot already up */ 37#define PCI_SLOT_ALREADY_DOWN 3 /* slot already down */ 38#define PCI_L1_ERR 7 /* L1 console command error */ 39#define PCI_EMPTY_33MHZ 15 /* empty 33 MHz bus */ 40#define PCI_L1_QSIZE 128 /* our L1 message buffer size */ 41#define SN_MAX_HP_SLOTS 32 /* max hotplug slots */ 42#define SGI_HOTPLUG_PROM_REV 0x0430 /* Min. required PROM version */ 43#define SN_SLOT_NAME_SIZE 33 /* size of name string */ 44 45/* internal list head */ 46static struct list_head sn_hp_list; 47 48/* hotplug_slot struct's private pointer */ 49struct slot { 50 int device_num; 51 struct pci_bus *pci_bus; 52 /* this struct for glue internal only */ 53 struct hotplug_slot *hotplug_slot; 54 struct list_head hp_list; 55 char physical_path[SN_SLOT_NAME_SIZE]; 56}; 57 58struct pcibr_slot_enable_resp { 59 int resp_sub_errno; 60 char resp_l1_msg[PCI_L1_QSIZE + 1]; 61}; 62 63struct pcibr_slot_disable_resp { 64 int resp_sub_errno; 65 char resp_l1_msg[PCI_L1_QSIZE + 1]; 66}; 67 68enum sn_pci_req_e { 69 PCI_REQ_SLOT_ELIGIBLE, 70 PCI_REQ_SLOT_DISABLE 71}; 72 73static int enable_slot(struct hotplug_slot *slot); 74static int disable_slot(struct hotplug_slot *slot); 75static inline int get_power_status(struct hotplug_slot *slot, u8 *value); 76 77static struct hotplug_slot_ops sn_hotplug_slot_ops = { 78 .owner = THIS_MODULE, 79 .enable_slot = enable_slot, 80 .disable_slot = disable_slot, 81 .get_power_status = get_power_status, 82}; 83 84static DECLARE_MUTEX(sn_hotplug_sem); 85 86static ssize_t path_show (struct hotplug_slot *bss_hotplug_slot, 87 char *buf) 88{ 89 int retval = -ENOENT; 90 struct slot *slot = bss_hotplug_slot->private; 91 92 if (!slot) 93 return retval; 94 95 retval = sprintf (buf, "%s\n", slot->physical_path); 96 return retval; 97} 98 99static struct hotplug_slot_attribute sn_slot_path_attr = __ATTR_RO(path); 100 101static int sn_pci_slot_valid(struct pci_bus *pci_bus, int device) 102{ 103 struct pcibus_info *pcibus_info; 104 int bricktype; 105 int bus_num; 106 107 pcibus_info = SN_PCIBUS_BUSSOFT_INFO(pci_bus); 108 109 /* Check to see if this is a valid slot on 'pci_bus' */ 110 if (!(pcibus_info->pbi_valid_devices & (1 << device))) 111 return -EPERM; 112 113 bricktype = MODULE_GET_BTYPE(pcibus_info->pbi_moduleid); 114 bus_num = pcibus_info->pbi_buscommon.bs_persist_busnum & 0xf; 115 116 /* Do not allow hotplug operations on base I/O cards */ 117 if ((bricktype == L1_BRICKTYPE_IX || bricktype == L1_BRICKTYPE_IA) && 118 (bus_num == 1 && device != 1)) 119 return -EPERM; 120 121 return 1; 122} 123 124static int sn_pci_bus_valid(struct pci_bus *pci_bus) 125{ 126 struct pcibus_info *pcibus_info; 127 int asic_type; 128 int bricktype; 129 130 pcibus_info = SN_PCIBUS_BUSSOFT_INFO(pci_bus); 131 132 /* Don't register slots hanging off the TIOCA bus */ 133 asic_type = pcibus_info->pbi_buscommon.bs_asic_type; 134 if (asic_type == PCIIO_ASIC_TYPE_TIOCA) 135 return -EPERM; 136 137 /* Only register slots in I/O Bricks that support hotplug */ 138 bricktype = MODULE_GET_BTYPE(pcibus_info->pbi_moduleid); 139 switch (bricktype) { 140 case L1_BRICKTYPE_IX: 141 case L1_BRICKTYPE_PX: 142 case L1_BRICKTYPE_IA: 143 case L1_BRICKTYPE_PA: 144 return 1; 145 break; 146 default: 147 return -EPERM; 148 break; 149 } 150 151 return -EIO; 152} 153 154static int sn_hp_slot_private_alloc(struct hotplug_slot *bss_hotplug_slot, 155 struct pci_bus *pci_bus, int device) 156{ 157 struct pcibus_info *pcibus_info; 158 struct slot *slot; 159 160 pcibus_info = SN_PCIBUS_BUSSOFT_INFO(pci_bus); 161 162 slot = kzalloc(sizeof(*slot), GFP_KERNEL); 163 if (!slot) 164 return -ENOMEM; 165 bss_hotplug_slot->private = slot; 166 167 bss_hotplug_slot->name = kmalloc(SN_SLOT_NAME_SIZE, GFP_KERNEL); 168 if (!bss_hotplug_slot->name) { 169 kfree(bss_hotplug_slot->private); 170 return -ENOMEM; 171 } 172 173 slot->device_num = device; 174 slot->pci_bus = pci_bus; 175 sprintf(bss_hotplug_slot->name, "%04x:%02x:%02x", 176 pci_domain_nr(pci_bus), 177 ((int)pcibus_info->pbi_buscommon.bs_persist_busnum) & 0xf, 178 device + 1); 179 sprintf(slot->physical_path, "module_%c%c%c%c%.2d", 180 '0'+RACK_GET_CLASS(MODULE_GET_RACK(pcibus_info->pbi_moduleid)), 181 '0'+RACK_GET_GROUP(MODULE_GET_RACK(pcibus_info->pbi_moduleid)), 182 '0'+RACK_GET_NUM(MODULE_GET_RACK(pcibus_info->pbi_moduleid)), 183 MODULE_GET_BTCHAR(pcibus_info->pbi_moduleid), 184 MODULE_GET_BPOS(pcibus_info->pbi_moduleid)); 185 slot->hotplug_slot = bss_hotplug_slot; 186 list_add(&slot->hp_list, &sn_hp_list); 187 188 return 0; 189} 190 191static struct hotplug_slot * sn_hp_destroy(void) 192{ 193 struct slot *slot; 194 struct hotplug_slot *bss_hotplug_slot = NULL; 195 196 list_for_each_entry(slot, &sn_hp_list, hp_list) { 197 bss_hotplug_slot = slot->hotplug_slot; 198 list_del(&((struct slot *)bss_hotplug_slot->private)-> 199 hp_list); 200 sysfs_remove_file(&bss_hotplug_slot->kobj, 201 &sn_slot_path_attr.attr); 202 break; 203 } 204 return bss_hotplug_slot; 205} 206 207static void sn_bus_alloc_data(struct pci_dev *dev) 208{ 209 struct pci_bus *subordinate_bus; 210 struct pci_dev *child; 211 212 sn_pci_fixup_slot(dev); 213 214 /* Recursively sets up the sn_irq_info structs */ 215 if (dev->subordinate) { 216 subordinate_bus = dev->subordinate; 217 list_for_each_entry(child, &subordinate_bus->devices, bus_list) 218 sn_bus_alloc_data(child); 219 } 220} 221 222static void sn_bus_free_data(struct pci_dev *dev) 223{ 224 struct pci_bus *subordinate_bus; 225 struct pci_dev *child; 226 227 /* Recursively clean up sn_irq_info structs */ 228 if (dev->subordinate) { 229 subordinate_bus = dev->subordinate; 230 list_for_each_entry(child, &subordinate_bus->devices, bus_list) 231 sn_bus_free_data(child); 232 } 233 sn_pci_unfixup_slot(dev); 234} 235 236static int sn_slot_enable(struct hotplug_slot *bss_hotplug_slot, 237 int device_num) 238{ 239 struct slot *slot = bss_hotplug_slot->private; 240 struct pcibus_info *pcibus_info; 241 struct pcibr_slot_enable_resp resp; 242 int rc; 243 244 pcibus_info = SN_PCIBUS_BUSSOFT_INFO(slot->pci_bus); 245 246 /* 247 * Power-on and initialize the slot in the SN 248 * PCI infrastructure. 249 */ 250 rc = sal_pcibr_slot_enable(pcibus_info, device_num, &resp); 251 252 if (rc == PCI_SLOT_ALREADY_UP) { 253 dev_dbg(slot->pci_bus->self, "is already active\n"); 254 return 1; /* return 1 to user */ 255 } 256 257 if (rc == PCI_L1_ERR) { 258 dev_dbg(slot->pci_bus->self, 259 "L1 failure %d with message: %s", 260 resp.resp_sub_errno, resp.resp_l1_msg); 261 return -EPERM; 262 } 263 264 if (rc) { 265 dev_dbg(slot->pci_bus->self, 266 "insert failed with error %d sub-error %d\n", 267 rc, resp.resp_sub_errno); 268 return -EIO; 269 } 270 271 pcibus_info = SN_PCIBUS_BUSSOFT_INFO(slot->pci_bus); 272 pcibus_info->pbi_enabled_devices |= (1 << device_num); 273 274 return 0; 275} 276 277static int sn_slot_disable(struct hotplug_slot *bss_hotplug_slot, 278 int device_num, int action) 279{ 280 struct slot *slot = bss_hotplug_slot->private; 281 struct pcibus_info *pcibus_info; 282 struct pcibr_slot_disable_resp resp; 283 int rc; 284 285 pcibus_info = SN_PCIBUS_BUSSOFT_INFO(slot->pci_bus); 286 287 rc = sal_pcibr_slot_disable(pcibus_info, device_num, action, &resp); 288 289 if ((action == PCI_REQ_SLOT_ELIGIBLE) && 290 (rc == PCI_SLOT_ALREADY_DOWN)) { 291 dev_dbg(slot->pci_bus->self, "Slot %s already inactive\n"); 292 return 1; /* return 1 to user */ 293 } 294 295 if ((action == PCI_REQ_SLOT_ELIGIBLE) && (rc == PCI_EMPTY_33MHZ)) { 296 dev_dbg(slot->pci_bus->self, 297 "Cannot remove last 33MHz card\n"); 298 return -EPERM; 299 } 300 301 if ((action == PCI_REQ_SLOT_ELIGIBLE) && (rc == PCI_L1_ERR)) { 302 dev_dbg(slot->pci_bus->self, 303 "L1 failure %d with message \n%s\n", 304 resp.resp_sub_errno, resp.resp_l1_msg); 305 return -EPERM; 306 } 307 308 if ((action == PCI_REQ_SLOT_ELIGIBLE) && rc) { 309 dev_dbg(slot->pci_bus->self, 310 "remove failed with error %d sub-error %d\n", 311 rc, resp.resp_sub_errno); 312 return -EIO; 313 } 314 315 if ((action == PCI_REQ_SLOT_ELIGIBLE) && !rc) 316 return 0; 317 318 if ((action == PCI_REQ_SLOT_DISABLE) && !rc) { 319 pcibus_info = SN_PCIBUS_BUSSOFT_INFO(slot->pci_bus); 320 pcibus_info->pbi_enabled_devices &= ~(1 << device_num); 321 dev_dbg(slot->pci_bus->self, "remove successful\n"); 322 return 0; 323 } 324 325 if ((action == PCI_REQ_SLOT_DISABLE) && rc) { 326 dev_dbg(slot->pci_bus->self,"remove failed rc = %d\n", rc); 327 } 328 329 return rc; 330} 331 332static int enable_slot(struct hotplug_slot *bss_hotplug_slot) 333{ 334 struct slot *slot = bss_hotplug_slot->private; 335 struct pci_bus *new_bus = NULL; 336 struct pci_dev *dev; 337 int func, num_funcs; 338 int new_ppb = 0; 339 int rc; 340 341 /* Serialize the Linux PCI infrastructure */ 342 down(&sn_hotplug_sem); 343 344 /* 345 * Power-on and initialize the slot in the SN 346 * PCI infrastructure. 347 */ 348 rc = sn_slot_enable(bss_hotplug_slot, slot->device_num); 349 if (rc) { 350 up(&sn_hotplug_sem); 351 return rc; 352 } 353 354 num_funcs = pci_scan_slot(slot->pci_bus, 355 PCI_DEVFN(slot->device_num + 1, 0)); 356 if (!num_funcs) { 357 dev_dbg(slot->pci_bus->self, "no device in slot\n"); 358 up(&sn_hotplug_sem); 359 return -ENODEV; 360 } 361 362 sn_pci_controller_fixup(pci_domain_nr(slot->pci_bus), 363 slot->pci_bus->number, 364 slot->pci_bus); 365 /* 366 * Map SN resources for all functions on the card 367 * to the Linux PCI interface and tell the drivers 368 * about them. 369 */ 370 for (func = 0; func < num_funcs; func++) { 371 dev = pci_get_slot(slot->pci_bus, 372 PCI_DEVFN(slot->device_num + 1, 373 PCI_FUNC(func))); 374 if (dev) { 375 if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) { 376 unsigned char sec_bus; 377 pci_read_config_byte(dev, PCI_SECONDARY_BUS, 378 &sec_bus); 379 new_bus = pci_add_new_bus(dev->bus, dev, 380 sec_bus); 381 pci_scan_child_bus(new_bus); 382 sn_pci_controller_fixup(pci_domain_nr(new_bus), 383 new_bus->number, 384 new_bus); 385 new_ppb = 1; 386 } 387 sn_bus_alloc_data(dev); 388 pci_dev_put(dev); 389 } 390 } 391 392 /* Call the driver for the new device */ 393 pci_bus_add_devices(slot->pci_bus); 394 /* Call the drivers for the new devices subordinate to PPB */ 395 if (new_ppb) 396 pci_bus_add_devices(new_bus); 397 398 up(&sn_hotplug_sem); 399 400 if (rc == 0) 401 dev_dbg(slot->pci_bus->self, 402 "insert operation successful\n"); 403 else 404 dev_dbg(slot->pci_bus->self, 405 "insert operation failed rc = %d\n", rc); 406 407 return rc; 408} 409 410static int disable_slot(struct hotplug_slot *bss_hotplug_slot) 411{ 412 struct slot *slot = bss_hotplug_slot->private; 413 struct pci_dev *dev; 414 int func; 415 int rc; 416 417 /* Acquire update access to the bus */ 418 down(&sn_hotplug_sem); 419 420 /* is it okay to bring this slot down? */ 421 rc = sn_slot_disable(bss_hotplug_slot, slot->device_num, 422 PCI_REQ_SLOT_ELIGIBLE); 423 if (rc) 424 goto leaving; 425 426 /* Free the SN resources assigned to the Linux device.*/ 427 for (func = 0; func < 8; func++) { 428 dev = pci_get_slot(slot->pci_bus, 429 PCI_DEVFN(slot->device_num + 1, 430 PCI_FUNC(func))); 431 if (dev) { 432 /* 433 * Some drivers may use dma accesses during the 434 * driver remove function. We release the sysdata 435 * areas after the driver remove functions have 436 * been called. 437 */ 438 sn_bus_store_sysdata(dev); 439 sn_bus_free_data(dev); 440 pci_remove_bus_device(dev); 441 pci_dev_put(dev); 442 } 443 } 444 445 /* free the collected sysdata pointers */ 446 sn_bus_free_sysdata(); 447 448 /* Deactivate slot */ 449 rc = sn_slot_disable(bss_hotplug_slot, slot->device_num, 450 PCI_REQ_SLOT_DISABLE); 451 leaving: 452 /* Release the bus lock */ 453 up(&sn_hotplug_sem); 454 455 return rc; 456} 457 458static inline int get_power_status(struct hotplug_slot *bss_hotplug_slot, 459 u8 *value) 460{ 461 struct slot *slot = bss_hotplug_slot->private; 462 struct pcibus_info *pcibus_info; 463 464 pcibus_info = SN_PCIBUS_BUSSOFT_INFO(slot->pci_bus); 465 down(&sn_hotplug_sem); 466 *value = pcibus_info->pbi_enabled_devices & (1 << slot->device_num); 467 up(&sn_hotplug_sem); 468 return 0; 469} 470 471static void sn_release_slot(struct hotplug_slot *bss_hotplug_slot) 472{ 473 kfree(bss_hotplug_slot->info); 474 kfree(bss_hotplug_slot->name); 475 kfree(bss_hotplug_slot->private); 476 kfree(bss_hotplug_slot); 477} 478 479static int sn_hotplug_slot_register(struct pci_bus *pci_bus) 480{ 481 int device; 482 struct hotplug_slot *bss_hotplug_slot; 483 int rc = 0; 484 485 /* 486 * Currently only four devices are supported, 487 * in the future there maybe more -- up to 32. 488 */ 489 490 for (device = 0; device < SN_MAX_HP_SLOTS ; device++) { 491 if (sn_pci_slot_valid(pci_bus, device) != 1) 492 continue; 493 494 bss_hotplug_slot = kzalloc(sizeof(*bss_hotplug_slot), 495 GFP_KERNEL); 496 if (!bss_hotplug_slot) { 497 rc = -ENOMEM; 498 goto alloc_err; 499 } 500 501 bss_hotplug_slot->info = 502 kzalloc(sizeof(struct hotplug_slot_info), 503 GFP_KERNEL); 504 if (!bss_hotplug_slot->info) { 505 rc = -ENOMEM; 506 goto alloc_err; 507 } 508 509 if (sn_hp_slot_private_alloc(bss_hotplug_slot, 510 pci_bus, device)) { 511 rc = -ENOMEM; 512 goto alloc_err; 513 } 514 515 bss_hotplug_slot->ops = &sn_hotplug_slot_ops; 516 bss_hotplug_slot->release = &sn_release_slot; 517 518 rc = pci_hp_register(bss_hotplug_slot); 519 if (rc) 520 goto register_err; 521 522 rc = sysfs_create_file(&bss_hotplug_slot->kobj, 523 &sn_slot_path_attr.attr); 524 if (rc) 525 goto register_err; 526 } 527 dev_dbg(pci_bus->self, "Registered bus with hotplug\n"); 528 return rc; 529 530register_err: 531 dev_dbg(pci_bus->self, "bus failed to register with err = %d\n", 532 rc); 533 534alloc_err: 535 if (rc == -ENOMEM) 536 dev_dbg(pci_bus->self, "Memory allocation error\n"); 537 538 /* destroy THIS element */ 539 if (bss_hotplug_slot) 540 sn_release_slot(bss_hotplug_slot); 541 542 /* destroy anything else on the list */ 543 while ((bss_hotplug_slot = sn_hp_destroy())) 544 pci_hp_deregister(bss_hotplug_slot); 545 546 return rc; 547} 548 549static int sn_pci_hotplug_init(void) 550{ 551 struct pci_bus *pci_bus = NULL; 552 int rc; 553 int registered = 0; 554 555 if (sn_sal_rev() < SGI_HOTPLUG_PROM_REV) { 556 printk(KERN_ERR "%s: PROM version must be greater than 4.30\n", 557 __FUNCTION__); 558 return -EPERM; 559 } 560 561 INIT_LIST_HEAD(&sn_hp_list); 562 563 while ((pci_bus = pci_find_next_bus(pci_bus))) { 564 if (!pci_bus->sysdata) 565 continue; 566 567 rc = sn_pci_bus_valid(pci_bus); 568 if (rc != 1) { 569 dev_dbg(pci_bus->self, "not a valid hotplug bus\n"); 570 continue; 571 } 572 dev_dbg(pci_bus->self, "valid hotplug bus\n"); 573 574 rc = sn_hotplug_slot_register(pci_bus); 575 if (!rc) { 576 registered = 1; 577 } else { 578 registered = 0; 579 break; 580 } 581 } 582 583 return registered == 1 ? 0 : -ENODEV; 584} 585 586static void sn_pci_hotplug_exit(void) 587{ 588 struct hotplug_slot *bss_hotplug_slot; 589 590 while ((bss_hotplug_slot = sn_hp_destroy())) 591 pci_hp_deregister(bss_hotplug_slot); 592 593 if (!list_empty(&sn_hp_list)) 594 printk(KERN_ERR "%s: internal list is not empty\n", __FILE__); 595} 596 597module_init(sn_pci_hotplug_init); 598module_exit(sn_pci_hotplug_exit);