diff options
| author | Prarit Bhargava <prarit@sgi.com> | 2005-07-06 18:29:53 -0400 |
|---|---|---|
| committer | Tony Luck <tony.luck@intel.com> | 2005-07-06 18:29:53 -0400 |
| commit | 6f354b014b51716166f13f68b29212d3c44ed2c4 (patch) | |
| tree | 396c09a5d519630a53652a1187bb85fceba82cee | |
| parent | 283c7f6ac6adb57e7dd13cdbc8d60b6ea4de6faf (diff) | |
[IA64] hotplug/ia64: SN Hotplug Driver - SN Hotplug Driver code
This patch is the SGI hotplug driver and additional changes required for
the driver. These modifications include changes to the SN io_init.c code
for memory management, the inclusion of new SAL calls to enable and disable
PCI slots, and a hotplug-style driver.
Signed-off-by: Prarit Bhargava <prarit@sgi.com>
Signed-off-by: Tony Luck <tony.luck@intel.com>
| -rw-r--r-- | arch/ia64/sn/kernel/io_init.c | 134 | ||||
| -rw-r--r-- | arch/ia64/sn/kernel/irq.c | 6 | ||||
| -rw-r--r-- | arch/ia64/sn/pci/pcibr/pcibr_provider.c | 37 | ||||
| -rw-r--r-- | drivers/pci/hotplug/Kconfig | 5 | ||||
| -rw-r--r-- | drivers/pci/hotplug/Makefile | 1 | ||||
| -rw-r--r-- | drivers/pci/hotplug/sgi_hotplug.c | 611 | ||||
| -rw-r--r-- | include/asm-ia64/sn/pcibr_provider.h | 4 | ||||
| -rw-r--r-- | include/asm-ia64/sn/pcidev.h | 6 |
8 files changed, 754 insertions, 50 deletions
diff --git a/arch/ia64/sn/kernel/io_init.c b/arch/ia64/sn/kernel/io_init.c index 041c4be02b2a..a67f39e448cb 100644 --- a/arch/ia64/sn/kernel/io_init.c +++ b/arch/ia64/sn/kernel/io_init.c | |||
| @@ -23,6 +23,14 @@ | |||
| 23 | 23 | ||
| 24 | nasid_t master_nasid = INVALID_NASID; /* Partition Master */ | 24 | nasid_t master_nasid = INVALID_NASID; /* Partition Master */ |
| 25 | 25 | ||
| 26 | static struct list_head sn_sysdata_list; | ||
| 27 | |||
| 28 | /* sysdata list struct */ | ||
| 29 | struct sysdata_el { | ||
| 30 | struct list_head entry; | ||
| 31 | void *sysdata; | ||
| 32 | }; | ||
| 33 | |||
| 26 | struct slab_info { | 34 | struct slab_info { |
| 27 | struct hubdev_info hubdev; | 35 | struct hubdev_info hubdev; |
| 28 | }; | 36 | }; |
| @@ -137,23 +145,6 @@ sal_get_pcidev_info(u64 segment, u64 bus_number, u64 devfn, u64 pci_dev, | |||
| 137 | } | 145 | } |
| 138 | 146 | ||
| 139 | /* | 147 | /* |
| 140 | * sn_alloc_pci_sysdata() - This routine allocates a pci controller | ||
| 141 | * which is expected as the pci_dev and pci_bus sysdata by the Linux | ||
| 142 | * PCI infrastructure. | ||
| 143 | */ | ||
| 144 | static inline struct pci_controller *sn_alloc_pci_sysdata(void) | ||
| 145 | { | ||
| 146 | struct pci_controller *pci_sysdata; | ||
| 147 | |||
| 148 | pci_sysdata = kmalloc(sizeof(*pci_sysdata), GFP_KERNEL); | ||
| 149 | if (!pci_sysdata) | ||
| 150 | BUG(); | ||
| 151 | |||
| 152 | memset(pci_sysdata, 0, sizeof(*pci_sysdata)); | ||
| 153 | return pci_sysdata; | ||
| 154 | } | ||
| 155 | |||
| 156 | /* | ||
| 157 | * sn_fixup_ionodes() - This routine initializes the HUB data strcuture for | 148 | * sn_fixup_ionodes() - This routine initializes the HUB data strcuture for |
| 158 | * each node in the system. | 149 | * each node in the system. |
| 159 | */ | 150 | */ |
| @@ -220,6 +211,15 @@ static void sn_fixup_ionodes(void) | |||
| 220 | 211 | ||
| 221 | } | 212 | } |
| 222 | 213 | ||
| 214 | void sn_pci_unfixup_slot(struct pci_dev *dev) | ||
| 215 | { | ||
| 216 | struct pci_dev *host_pci_dev = SN_PCIDEV_INFO(dev)->host_pci_dev; | ||
| 217 | |||
| 218 | sn_irq_unfixup(dev); | ||
| 219 | pci_dev_put(host_pci_dev); | ||
| 220 | pci_dev_put(dev); | ||
| 221 | } | ||
| 222 | |||
| 223 | /* | 223 | /* |
| 224 | * sn_pci_fixup_slot() - This routine sets up a slot's resources | 224 | * sn_pci_fixup_slot() - This routine sets up a slot's resources |
| 225 | * consistent with the Linux PCI abstraction layer. Resources acquired | 225 | * consistent with the Linux PCI abstraction layer. Resources acquired |
| @@ -238,6 +238,7 @@ void sn_pci_fixup_slot(struct pci_dev *dev) | |||
| 238 | unsigned long size; | 238 | unsigned long size; |
| 239 | unsigned int bus_no, devfn; | 239 | unsigned int bus_no, devfn; |
| 240 | 240 | ||
| 241 | pci_dev_get(dev); /* for the sysdata pointer */ | ||
| 241 | dev->sysdata = kmalloc(sizeof(struct pcidev_info), GFP_KERNEL); | 242 | dev->sysdata = kmalloc(sizeof(struct pcidev_info), GFP_KERNEL); |
| 242 | if (SN_PCIDEV_INFO(dev) <= 0) | 243 | if (SN_PCIDEV_INFO(dev) <= 0) |
| 243 | BUG(); /* Cannot afford to run out of memory */ | 244 | BUG(); /* Cannot afford to run out of memory */ |
| @@ -276,7 +277,8 @@ void sn_pci_fixup_slot(struct pci_dev *dev) | |||
| 276 | dev->resource[idx].parent = &iomem_resource; | 277 | dev->resource[idx].parent = &iomem_resource; |
| 277 | } | 278 | } |
| 278 | 279 | ||
| 279 | /* Using the PROMs values for the PCI host bus, get the Linux | 280 | /* |
| 281 | * Using the PROMs values for the PCI host bus, get the Linux | ||
| 280 | * PCI host_pci_dev struct and set up host bus linkages | 282 | * PCI host_pci_dev struct and set up host bus linkages |
| 281 | */ | 283 | */ |
| 282 | 284 | ||
| @@ -313,55 +315,57 @@ void sn_pci_fixup_slot(struct pci_dev *dev) | |||
| 313 | * sn_pci_controller_fixup() - This routine sets up a bus's resources | 315 | * sn_pci_controller_fixup() - This routine sets up a bus's resources |
| 314 | * consistent with the Linux PCI abstraction layer. | 316 | * consistent with the Linux PCI abstraction layer. |
| 315 | */ | 317 | */ |
| 316 | static void sn_pci_controller_fixup(int segment, int busnum) | 318 | void sn_pci_controller_fixup(int segment, int busnum, struct pci_bus *bus) |
| 317 | { | 319 | { |
| 318 | int status = 0; | 320 | int status = 0; |
| 319 | int nasid, cnode; | 321 | int nasid, cnode; |
| 320 | struct pci_bus *bus; | ||
| 321 | struct pci_controller *controller; | 322 | struct pci_controller *controller; |
| 322 | struct pcibus_bussoft *prom_bussoft_ptr; | 323 | struct pcibus_bussoft *prom_bussoft_ptr; |
| 323 | struct hubdev_info *hubdev_info; | 324 | struct hubdev_info *hubdev_info; |
| 324 | void *provider_soft; | 325 | void *provider_soft; |
| 325 | struct sn_pcibus_provider *provider; | 326 | struct sn_pcibus_provider *provider; |
| 326 | 327 | ||
| 327 | status = | 328 | status = sal_get_pcibus_info((u64) segment, (u64) busnum, |
| 328 | sal_get_pcibus_info((u64) segment, (u64) busnum, | 329 | (u64) ia64_tpa(&prom_bussoft_ptr)); |
| 329 | (u64) ia64_tpa(&prom_bussoft_ptr)); | 330 | if (status > 0) |
| 330 | if (status > 0) { | 331 | return; /*bus # does not exist */ |
| 331 | return; /* bus # does not exist */ | ||
| 332 | } | ||
| 333 | |||
| 334 | prom_bussoft_ptr = __va(prom_bussoft_ptr); | 332 | prom_bussoft_ptr = __va(prom_bussoft_ptr); |
| 335 | controller = sn_alloc_pci_sysdata(); | ||
| 336 | /* controller non-zero is BUG'd in sn_alloc_pci_sysdata */ | ||
| 337 | 333 | ||
| 338 | bus = pci_scan_bus(busnum, &pci_root_ops, controller); | 334 | controller = kcalloc(1,sizeof(struct pci_controller), GFP_KERNEL); |
| 335 | if (!controller) | ||
| 336 | BUG(); | ||
| 337 | |||
| 339 | if (bus == NULL) { | 338 | if (bus == NULL) { |
| 340 | return; /* error, or bus already scanned */ | 339 | bus = pci_scan_bus(busnum, &pci_root_ops, controller); |
| 340 | if (bus == NULL) | ||
| 341 | return; /* error, or bus already scanned */ | ||
| 342 | bus->sysdata = NULL; | ||
| 341 | } | 343 | } |
| 342 | 344 | ||
| 345 | if (bus->sysdata) | ||
| 346 | goto error_return; /* sysdata already alloc'd */ | ||
| 347 | |||
| 343 | /* | 348 | /* |
| 344 | * Per-provider fixup. Copies the contents from prom to local | 349 | * Per-provider fixup. Copies the contents from prom to local |
| 345 | * area and links SN_PCIBUS_BUSSOFT(). | 350 | * area and links SN_PCIBUS_BUSSOFT(). |
| 346 | */ | 351 | */ |
| 347 | 352 | ||
| 348 | if (prom_bussoft_ptr->bs_asic_type >= PCIIO_ASIC_MAX_TYPES) { | 353 | if (prom_bussoft_ptr->bs_asic_type >= PCIIO_ASIC_MAX_TYPES) |
| 349 | return; /* unsupported asic type */ | 354 | return; /* unsupported asic type */ |
| 350 | } | 355 | |
| 356 | if (prom_bussoft_ptr->bs_asic_type == PCIIO_ASIC_TYPE_PPB) | ||
| 357 | goto error_return; /* no further fixup necessary */ | ||
| 351 | 358 | ||
| 352 | provider = sn_pci_provider[prom_bussoft_ptr->bs_asic_type]; | 359 | provider = sn_pci_provider[prom_bussoft_ptr->bs_asic_type]; |
| 353 | if (provider == NULL) { | 360 | if (provider == NULL) |
| 354 | return; /* no provider registerd for this asic */ | 361 | return; /* no provider registerd for this asic */ |
| 355 | } | ||
| 356 | 362 | ||
| 357 | provider_soft = NULL; | 363 | provider_soft = NULL; |
| 358 | if (provider->bus_fixup) { | 364 | if (provider->bus_fixup) |
| 359 | provider_soft = (*provider->bus_fixup) (prom_bussoft_ptr); | 365 | provider_soft = (*provider->bus_fixup) (prom_bussoft_ptr); |
| 360 | } | ||
| 361 | 366 | ||
| 362 | if (provider_soft == NULL) { | 367 | if (provider_soft == NULL) |
| 363 | return; /* fixup failed or not applicable */ | 368 | return; /* fixup failed or not applicable */ |
| 364 | } | ||
| 365 | 369 | ||
| 366 | /* | 370 | /* |
| 367 | * Generic bus fixup goes here. Don't reference prom_bussoft_ptr | 371 | * Generic bus fixup goes here. Don't reference prom_bussoft_ptr |
| @@ -370,12 +374,47 @@ static void sn_pci_controller_fixup(int segment, int busnum) | |||
| 370 | 374 | ||
| 371 | bus->sysdata = controller; | 375 | bus->sysdata = controller; |
| 372 | PCI_CONTROLLER(bus)->platform_data = provider_soft; | 376 | PCI_CONTROLLER(bus)->platform_data = provider_soft; |
| 373 | |||
| 374 | nasid = NASID_GET(SN_PCIBUS_BUSSOFT(bus)->bs_base); | 377 | nasid = NASID_GET(SN_PCIBUS_BUSSOFT(bus)->bs_base); |
| 375 | cnode = nasid_to_cnodeid(nasid); | 378 | cnode = nasid_to_cnodeid(nasid); |
| 376 | hubdev_info = (struct hubdev_info *)(NODEPDA(cnode)->pdinfo); | 379 | hubdev_info = (struct hubdev_info *)(NODEPDA(cnode)->pdinfo); |
| 377 | SN_PCIBUS_BUSSOFT(bus)->bs_xwidget_info = | 380 | SN_PCIBUS_BUSSOFT(bus)->bs_xwidget_info = |
| 378 | &(hubdev_info->hdi_xwidget_info[SN_PCIBUS_BUSSOFT(bus)->bs_xid]); | 381 | &(hubdev_info->hdi_xwidget_info[SN_PCIBUS_BUSSOFT(bus)->bs_xid]); |
| 382 | |||
| 383 | return; | ||
| 384 | |||
| 385 | error_return: | ||
| 386 | |||
| 387 | kfree(controller); | ||
| 388 | return; | ||
| 389 | } | ||
| 390 | |||
| 391 | void sn_bus_store_sysdata(struct pci_dev *dev) | ||
| 392 | { | ||
| 393 | struct sysdata_el *element; | ||
| 394 | |||
| 395 | element = kcalloc(1, sizeof(struct sysdata_el), GFP_KERNEL); | ||
| 396 | if (!element) { | ||
| 397 | dev_dbg(dev, "%s: out of memory!\n", __FUNCTION__); | ||
| 398 | return; | ||
| 399 | } | ||
| 400 | element->sysdata = dev->sysdata; | ||
| 401 | list_add(&element->entry, &sn_sysdata_list); | ||
| 402 | } | ||
| 403 | |||
| 404 | void sn_bus_free_sysdata(void) | ||
| 405 | { | ||
| 406 | struct sysdata_el *element; | ||
| 407 | struct list_head *list; | ||
| 408 | |||
| 409 | sn_sysdata_free_start: | ||
| 410 | list_for_each(list, &sn_sysdata_list) { | ||
| 411 | element = list_entry(list, struct sysdata_el, entry); | ||
| 412 | list_del(&element->entry); | ||
| 413 | kfree(element->sysdata); | ||
| 414 | kfree(element); | ||
| 415 | goto sn_sysdata_free_start; | ||
| 416 | } | ||
| 417 | return; | ||
| 379 | } | 418 | } |
| 380 | 419 | ||
| 381 | /* | 420 | /* |
| @@ -413,15 +452,16 @@ static int __init sn_pci_init(void) | |||
| 413 | ia64_max_iommu_merge_mask = ~PAGE_MASK; | 452 | ia64_max_iommu_merge_mask = ~PAGE_MASK; |
| 414 | sn_fixup_ionodes(); | 453 | sn_fixup_ionodes(); |
| 415 | sn_irq_lh_init(); | 454 | sn_irq_lh_init(); |
| 455 | INIT_LIST_HEAD(&sn_sysdata_list); | ||
| 416 | sn_init_cpei_timer(); | 456 | sn_init_cpei_timer(); |
| 417 | 457 | ||
| 418 | #ifdef CONFIG_PROC_FS | 458 | #ifdef CONFIG_PROC_FS |
| 419 | register_sn_procfs(); | 459 | register_sn_procfs(); |
| 420 | #endif | 460 | #endif |
| 421 | 461 | ||
| 422 | for (i = 0; i < PCI_BUSES_TO_SCAN; i++) { | 462 | /* busses are not known yet ... */ |
| 423 | sn_pci_controller_fixup(0, i); | 463 | for (i = 0; i < PCI_BUSES_TO_SCAN; i++) |
| 424 | } | 464 | sn_pci_controller_fixup(0, i, NULL); |
| 425 | 465 | ||
| 426 | /* | 466 | /* |
| 427 | * Generic Linux PCI Layer has created the pci_bus and pci_dev | 467 | * Generic Linux PCI Layer has created the pci_bus and pci_dev |
| @@ -430,9 +470,8 @@ static int __init sn_pci_init(void) | |||
| 430 | */ | 470 | */ |
| 431 | 471 | ||
| 432 | while ((pci_dev = | 472 | while ((pci_dev = |
| 433 | pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pci_dev)) != NULL) { | 473 | pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pci_dev)) != NULL) |
| 434 | sn_pci_fixup_slot(pci_dev); | 474 | sn_pci_fixup_slot(pci_dev); |
| 435 | } | ||
| 436 | 475 | ||
| 437 | sn_ioif_inited = 1; /* sn I/O infrastructure now initialized */ | 476 | sn_ioif_inited = 1; /* sn I/O infrastructure now initialized */ |
| 438 | 477 | ||
| @@ -474,3 +513,8 @@ cnodeid_get_geoid(cnodeid_t cnode) | |||
| 474 | } | 513 | } |
| 475 | 514 | ||
| 476 | subsys_initcall(sn_pci_init); | 515 | subsys_initcall(sn_pci_init); |
| 516 | EXPORT_SYMBOL(sn_pci_fixup_slot); | ||
| 517 | EXPORT_SYMBOL(sn_pci_unfixup_slot); | ||
| 518 | EXPORT_SYMBOL(sn_pci_controller_fixup); | ||
| 519 | EXPORT_SYMBOL(sn_bus_store_sysdata); | ||
| 520 | EXPORT_SYMBOL(sn_bus_free_sysdata); | ||
diff --git a/arch/ia64/sn/kernel/irq.c b/arch/ia64/sn/kernel/irq.c index cf4dbf9645f1..84d276a14ecb 100644 --- a/arch/ia64/sn/kernel/irq.c +++ b/arch/ia64/sn/kernel/irq.c | |||
| @@ -284,7 +284,6 @@ void sn_irq_fixup(struct pci_dev *pci_dev, struct sn_irq_info *sn_irq_info) | |||
| 284 | int cpu = nasid_slice_to_cpuid(nasid, slice); | 284 | int cpu = nasid_slice_to_cpuid(nasid, slice); |
| 285 | 285 | ||
| 286 | pci_dev_get(pci_dev); | 286 | pci_dev_get(pci_dev); |
| 287 | |||
| 288 | sn_irq_info->irq_cpuid = cpu; | 287 | sn_irq_info->irq_cpuid = cpu; |
| 289 | sn_irq_info->irq_pciioinfo = SN_PCIDEV_INFO(pci_dev); | 288 | sn_irq_info->irq_pciioinfo = SN_PCIDEV_INFO(pci_dev); |
| 290 | 289 | ||
| @@ -305,15 +304,16 @@ void sn_irq_unfixup(struct pci_dev *pci_dev) | |||
| 305 | return; | 304 | return; |
| 306 | 305 | ||
| 307 | sn_irq_info = SN_PCIDEV_INFO(pci_dev)->pdi_sn_irq_info; | 306 | sn_irq_info = SN_PCIDEV_INFO(pci_dev)->pdi_sn_irq_info; |
| 308 | if (!sn_irq_info || !sn_irq_info->irq_irq) | 307 | if (!sn_irq_info || !sn_irq_info->irq_irq) { |
| 308 | kfree(sn_irq_info); | ||
| 309 | return; | 309 | return; |
| 310 | } | ||
| 310 | 311 | ||
| 311 | unregister_intr_pda(sn_irq_info); | 312 | unregister_intr_pda(sn_irq_info); |
| 312 | spin_lock(&sn_irq_info_lock); | 313 | spin_lock(&sn_irq_info_lock); |
| 313 | list_del_rcu(&sn_irq_info->list); | 314 | list_del_rcu(&sn_irq_info->list); |
| 314 | spin_unlock(&sn_irq_info_lock); | 315 | spin_unlock(&sn_irq_info_lock); |
| 315 | call_rcu(&sn_irq_info->rcu, sn_irq_info_free); | 316 | call_rcu(&sn_irq_info->rcu, sn_irq_info_free); |
| 316 | |||
| 317 | pci_dev_put(pci_dev); | 317 | pci_dev_put(pci_dev); |
| 318 | } | 318 | } |
| 319 | 319 | ||
diff --git a/arch/ia64/sn/pci/pcibr/pcibr_provider.c b/arch/ia64/sn/pci/pcibr/pcibr_provider.c index 9bc4de4a3ec0..9813da56d311 100644 --- a/arch/ia64/sn/pci/pcibr/pcibr_provider.c +++ b/arch/ia64/sn/pci/pcibr/pcibr_provider.c | |||
| @@ -18,6 +18,40 @@ | |||
| 18 | #include "xtalk/xwidgetdev.h" | 18 | #include "xtalk/xwidgetdev.h" |
| 19 | #include "xtalk/hubdev.h" | 19 | #include "xtalk/hubdev.h" |
| 20 | 20 | ||
| 21 | int | ||
| 22 | sal_pcibr_slot_enable(struct pcibus_info *soft, int device, void *resp) | ||
| 23 | { | ||
| 24 | struct ia64_sal_retval ret_stuff; | ||
| 25 | uint64_t busnum; | ||
| 26 | |||
| 27 | ret_stuff.status = 0; | ||
| 28 | ret_stuff.v0 = 0; | ||
| 29 | |||
| 30 | busnum = soft->pbi_buscommon.bs_persist_busnum; | ||
| 31 | SAL_CALL_NOLOCK(ret_stuff, (u64) SN_SAL_IOIF_SLOT_ENABLE, (u64) busnum, | ||
| 32 | (u64) device, (u64) resp, 0, 0, 0, 0); | ||
| 33 | |||
| 34 | return (int)ret_stuff.v0; | ||
| 35 | } | ||
| 36 | |||
| 37 | int | ||
| 38 | sal_pcibr_slot_disable(struct pcibus_info *soft, int device, int action, | ||
| 39 | void *resp) | ||
| 40 | { | ||
| 41 | struct ia64_sal_retval ret_stuff; | ||
| 42 | uint64_t busnum; | ||
| 43 | |||
| 44 | ret_stuff.status = 0; | ||
| 45 | ret_stuff.v0 = 0; | ||
| 46 | |||
| 47 | busnum = soft->pbi_buscommon.bs_persist_busnum; | ||
| 48 | SAL_CALL_NOLOCK(ret_stuff, (u64) SN_SAL_IOIF_SLOT_DISABLE, | ||
| 49 | (u64) busnum, (u64) device, (u64) action, | ||
| 50 | (u64) resp, 0, 0, 0); | ||
| 51 | |||
| 52 | return (int)ret_stuff.v0; | ||
| 53 | } | ||
| 54 | |||
| 21 | static int sal_pcibr_error_interrupt(struct pcibus_info *soft) | 55 | static int sal_pcibr_error_interrupt(struct pcibus_info *soft) |
| 22 | { | 56 | { |
| 23 | struct ia64_sal_retval ret_stuff; | 57 | struct ia64_sal_retval ret_stuff; |
| @@ -187,3 +221,6 @@ pcibr_init_provider(void) | |||
| 187 | 221 | ||
| 188 | return 0; | 222 | return 0; |
| 189 | } | 223 | } |
| 224 | |||
| 225 | EXPORT_SYMBOL_GPL(sal_pcibr_slot_enable); | ||
| 226 | EXPORT_SYMBOL_GPL(sal_pcibr_slot_disable); | ||
diff --git a/drivers/pci/hotplug/Kconfig b/drivers/pci/hotplug/Kconfig index 1a4d4ca2a4dc..9c4a39ee89b5 100644 --- a/drivers/pci/hotplug/Kconfig +++ b/drivers/pci/hotplug/Kconfig | |||
| @@ -187,9 +187,10 @@ config HOTPLUG_PCI_RPA_DLPAR | |||
| 187 | 187 | ||
| 188 | config HOTPLUG_PCI_SGI | 188 | config HOTPLUG_PCI_SGI |
| 189 | tristate "SGI PCI Hotplug Support" | 189 | tristate "SGI PCI Hotplug Support" |
| 190 | depends on HOTPLUG_PCI && IA64_SGI_SN2 | 190 | depends on HOTPLUG_PCI && (IA64_SGI_SN2 || IA64_GENERIC) |
| 191 | help | 191 | help |
| 192 | Say Y here if you have an SGI IA64 Altix system. | 192 | Say Y here if you want to use the SGI Altix Hotplug |
| 193 | Driver for PCI devices. | ||
| 193 | 194 | ||
| 194 | When in doubt, say N. | 195 | When in doubt, say N. |
| 195 | 196 | ||
diff --git a/drivers/pci/hotplug/Makefile b/drivers/pci/hotplug/Makefile index 3e632ff8c717..31a307004b94 100644 --- a/drivers/pci/hotplug/Makefile +++ b/drivers/pci/hotplug/Makefile | |||
| @@ -14,6 +14,7 @@ obj-$(CONFIG_HOTPLUG_PCI_PCIE) += pciehp.o | |||
| 14 | obj-$(CONFIG_HOTPLUG_PCI_SHPC) += shpchp.o | 14 | obj-$(CONFIG_HOTPLUG_PCI_SHPC) += shpchp.o |
| 15 | obj-$(CONFIG_HOTPLUG_PCI_RPA) += rpaphp.o | 15 | obj-$(CONFIG_HOTPLUG_PCI_RPA) += rpaphp.o |
| 16 | obj-$(CONFIG_HOTPLUG_PCI_RPA_DLPAR) += rpadlpar_io.o | 16 | obj-$(CONFIG_HOTPLUG_PCI_RPA_DLPAR) += rpadlpar_io.o |
| 17 | obj-$(CONFIG_HOTPLUG_PCI_SGI) += sgi_hotplug.o | ||
| 17 | 18 | ||
| 18 | pci_hotplug-objs := pci_hotplug_core.o | 19 | pci_hotplug-objs := pci_hotplug_core.o |
| 19 | 20 | ||
diff --git a/drivers/pci/hotplug/sgi_hotplug.c b/drivers/pci/hotplug/sgi_hotplug.c new file mode 100644 index 000000000000..323041fd41dc --- /dev/null +++ b/drivers/pci/hotplug/sgi_hotplug.c | |||
| @@ -0,0 +1,611 @@ | |||
| 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 | |||
| 31 | MODULE_LICENSE("GPL"); | ||
| 32 | MODULE_AUTHOR("SGI (prarit@sgi.com, dickie@sgi.com, habeck@sgi.com)"); | ||
| 33 | MODULE_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 number of hotplug slots */ | ||
| 42 | #define SGI_HOTPLUG_PROM_REV 0x0420 /* Min. required PROM version */ | ||
| 43 | |||
| 44 | /* internal list head */ | ||
| 45 | static struct list_head sn_hp_list; | ||
| 46 | |||
| 47 | /* hotplug_slot struct's private pointer */ | ||
| 48 | struct slot { | ||
| 49 | int device_num; | ||
| 50 | struct pci_bus *pci_bus; | ||
| 51 | /* this struct for glue internal only */ | ||
| 52 | struct hotplug_slot *hotplug_slot; | ||
| 53 | struct list_head hp_list; | ||
| 54 | }; | ||
| 55 | |||
| 56 | struct pcibr_slot_enable_resp { | ||
| 57 | int resp_sub_errno; | ||
| 58 | char resp_l1_msg[PCI_L1_QSIZE + 1]; | ||
| 59 | }; | ||
| 60 | |||
| 61 | struct pcibr_slot_disable_resp { | ||
| 62 | int resp_sub_errno; | ||
| 63 | char resp_l1_msg[PCI_L1_QSIZE + 1]; | ||
| 64 | }; | ||
| 65 | |||
| 66 | enum sn_pci_req_e { | ||
| 67 | PCI_REQ_SLOT_ELIGIBLE, | ||
| 68 | PCI_REQ_SLOT_DISABLE | ||
| 69 | }; | ||
| 70 | |||
| 71 | static int enable_slot(struct hotplug_slot *slot); | ||
| 72 | static int disable_slot(struct hotplug_slot *slot); | ||
| 73 | static int get_power_status(struct hotplug_slot *slot, u8 *value); | ||
| 74 | |||
| 75 | static struct hotplug_slot_ops sn_hotplug_slot_ops = { | ||
| 76 | .owner = THIS_MODULE, | ||
| 77 | .enable_slot = enable_slot, | ||
| 78 | .disable_slot = disable_slot, | ||
| 79 | .get_power_status = get_power_status, | ||
| 80 | }; | ||
| 81 | |||
| 82 | static DECLARE_MUTEX(sn_hotplug_sem); | ||
| 83 | |||
| 84 | static int sn_pci_slot_valid(struct pci_bus *pci_bus, int device) | ||
| 85 | { | ||
| 86 | struct pcibus_info *pcibus_info; | ||
| 87 | int bricktype; | ||
| 88 | int bus_num; | ||
| 89 | |||
| 90 | pcibus_info = SN_PCIBUS_BUSSOFT_INFO(pci_bus); | ||
| 91 | |||
| 92 | /* Check to see if this is a valid slot on 'pci_bus' */ | ||
| 93 | if (!(pcibus_info->pbi_valid_devices & (1 << device))) | ||
| 94 | return -EPERM; | ||
| 95 | |||
| 96 | bricktype = MODULE_GET_BTYPE(pcibus_info->pbi_moduleid); | ||
| 97 | bus_num = pcibus_info->pbi_buscommon.bs_persist_busnum & 0xf; | ||
| 98 | |||
| 99 | /* Do not allow hotplug operations on base I/O cards */ | ||
| 100 | if ((bricktype == L1_BRICKTYPE_IX || bricktype == L1_BRICKTYPE_IA) && | ||
| 101 | (bus_num == 1 && device != 1)) | ||
| 102 | return -EPERM; | ||
| 103 | |||
| 104 | return 1; | ||
| 105 | } | ||
| 106 | |||
| 107 | static int sn_pci_bus_valid(struct pci_bus *pci_bus) | ||
| 108 | { | ||
| 109 | struct pcibus_info *pcibus_info; | ||
| 110 | int asic_type; | ||
| 111 | int bricktype; | ||
| 112 | |||
| 113 | pcibus_info = SN_PCIBUS_BUSSOFT_INFO(pci_bus); | ||
| 114 | |||
| 115 | /* Don't register slots hanging off the TIOCA bus */ | ||
| 116 | asic_type = pcibus_info->pbi_buscommon.bs_asic_type; | ||
| 117 | if (asic_type == PCIIO_ASIC_TYPE_TIOCA) | ||
| 118 | return -EPERM; | ||
| 119 | |||
| 120 | /* Only register slots in I/O Bricks that support hotplug */ | ||
| 121 | bricktype = MODULE_GET_BTYPE(pcibus_info->pbi_moduleid); | ||
| 122 | switch (bricktype) { | ||
| 123 | case L1_BRICKTYPE_IX: | ||
| 124 | case L1_BRICKTYPE_PX: | ||
| 125 | case L1_BRICKTYPE_IA: | ||
| 126 | case L1_BRICKTYPE_PA: | ||
| 127 | return 1; | ||
| 128 | break; | ||
| 129 | default: | ||
| 130 | return -EPERM; | ||
| 131 | break; | ||
| 132 | } | ||
| 133 | |||
| 134 | return -EIO; | ||
| 135 | } | ||
| 136 | |||
| 137 | static int sn_hp_slot_private_alloc(struct hotplug_slot *bss_hotplug_slot, | ||
| 138 | struct pci_bus *pci_bus, int device) | ||
| 139 | { | ||
| 140 | struct pcibus_info *pcibus_info; | ||
| 141 | struct slot *slot; | ||
| 142 | |||
| 143 | pcibus_info = SN_PCIBUS_BUSSOFT_INFO(pci_bus); | ||
| 144 | |||
| 145 | bss_hotplug_slot->private = kcalloc(1, sizeof(struct slot), | ||
| 146 | GFP_KERNEL); | ||
| 147 | if (!bss_hotplug_slot->private) | ||
| 148 | return -ENOMEM; | ||
| 149 | slot = (struct slot *)bss_hotplug_slot->private; | ||
| 150 | |||
| 151 | bss_hotplug_slot->name = kmalloc(33, GFP_KERNEL); | ||
| 152 | if (!bss_hotplug_slot->name) { | ||
| 153 | kfree(bss_hotplug_slot->private); | ||
| 154 | return -ENOMEM; | ||
| 155 | } | ||
| 156 | |||
| 157 | slot->device_num = device; | ||
| 158 | slot->pci_bus = pci_bus; | ||
| 159 | |||
| 160 | sprintf(bss_hotplug_slot->name, "module_%c%c%c%c%.2d_b_%d_s_%d", | ||
| 161 | '0'+RACK_GET_CLASS(MODULE_GET_RACK(pcibus_info->pbi_moduleid)), | ||
| 162 | '0'+RACK_GET_GROUP(MODULE_GET_RACK(pcibus_info->pbi_moduleid)), | ||
| 163 | '0'+RACK_GET_NUM(MODULE_GET_RACK(pcibus_info->pbi_moduleid)), | ||
| 164 | MODULE_GET_BTCHAR(pcibus_info->pbi_moduleid), | ||
| 165 | MODULE_GET_BPOS(pcibus_info->pbi_moduleid), | ||
| 166 | ((int)pcibus_info->pbi_buscommon.bs_persist_busnum) & 0xf, | ||
| 167 | device + 1); | ||
| 168 | |||
| 169 | slot->hotplug_slot = bss_hotplug_slot; | ||
| 170 | list_add(&slot->hp_list, &sn_hp_list); | ||
| 171 | |||
| 172 | return 0; | ||
| 173 | } | ||
| 174 | |||
| 175 | static struct hotplug_slot * sn_hp_destroy(void) | ||
| 176 | { | ||
| 177 | struct slot *slot; | ||
| 178 | struct list_head *list; | ||
| 179 | struct hotplug_slot *bss_hotplug_slot = NULL; | ||
| 180 | |||
| 181 | list_for_each(list, &sn_hp_list) { | ||
| 182 | slot = list_entry(list, struct slot, hp_list); | ||
| 183 | bss_hotplug_slot = slot->hotplug_slot; | ||
| 184 | list_del(&((struct slot *)bss_hotplug_slot->private)-> | ||
| 185 | hp_list); | ||
| 186 | break; | ||
| 187 | } | ||
| 188 | return bss_hotplug_slot; | ||
| 189 | } | ||
| 190 | |||
| 191 | static void sn_bus_alloc_data(struct pci_dev *dev) | ||
| 192 | { | ||
| 193 | struct list_head *node; | ||
| 194 | struct pci_bus *subordinate_bus; | ||
| 195 | struct pci_dev *child; | ||
| 196 | |||
| 197 | sn_pci_fixup_slot(dev); | ||
| 198 | |||
| 199 | /* Recursively sets up the sn_irq_info structs */ | ||
| 200 | if (dev->subordinate) { | ||
| 201 | subordinate_bus = dev->subordinate; | ||
| 202 | list_for_each(node, &subordinate_bus->devices) { | ||
| 203 | child = list_entry(node, struct pci_dev, bus_list); | ||
| 204 | sn_bus_alloc_data(child); | ||
| 205 | } | ||
| 206 | } | ||
| 207 | } | ||
| 208 | |||
| 209 | static void sn_bus_free_data(struct pci_dev *dev) | ||
| 210 | { | ||
| 211 | struct list_head *node; | ||
| 212 | struct pci_bus *subordinate_bus; | ||
| 213 | struct pci_dev *child; | ||
| 214 | |||
| 215 | /* Recursively clean up sn_irq_info structs */ | ||
| 216 | if (dev->subordinate) { | ||
| 217 | subordinate_bus = dev->subordinate; | ||
| 218 | list_for_each(node, &subordinate_bus->devices) { | ||
| 219 | child = list_entry(node, struct pci_dev, bus_list); | ||
| 220 | sn_bus_free_data(child); | ||
| 221 | } | ||
| 222 | } | ||
| 223 | sn_pci_unfixup_slot(dev); | ||
| 224 | } | ||
| 225 | |||
| 226 | static u8 sn_power_status_get(struct hotplug_slot *bss_hotplug_slot) | ||
| 227 | { | ||
| 228 | struct slot *slot = (struct slot *)bss_hotplug_slot->private; | ||
| 229 | struct pcibus_info *pcibus_info; | ||
| 230 | u8 retval; | ||
| 231 | |||
| 232 | pcibus_info = SN_PCIBUS_BUSSOFT_INFO(slot->pci_bus); | ||
| 233 | retval = pcibus_info->pbi_enabled_devices & (1 << slot->device_num); | ||
| 234 | |||
| 235 | return retval ? 1 : 0; | ||
| 236 | } | ||
| 237 | |||
| 238 | static void sn_slot_mark_enable(struct hotplug_slot *bss_hotplug_slot, | ||
| 239 | int device_num) | ||
| 240 | { | ||
| 241 | struct slot *slot = (struct slot *)bss_hotplug_slot->private; | ||
| 242 | struct pcibus_info *pcibus_info; | ||
| 243 | |||
| 244 | pcibus_info = SN_PCIBUS_BUSSOFT_INFO(slot->pci_bus); | ||
| 245 | pcibus_info->pbi_enabled_devices |= (1 << device_num); | ||
| 246 | } | ||
| 247 | |||
| 248 | static void sn_slot_mark_disable(struct hotplug_slot *bss_hotplug_slot, | ||
| 249 | int device_num) | ||
| 250 | { | ||
| 251 | struct slot *slot = (struct slot *)bss_hotplug_slot->private; | ||
| 252 | struct pcibus_info *pcibus_info; | ||
| 253 | |||
| 254 | pcibus_info = SN_PCIBUS_BUSSOFT_INFO(slot->pci_bus); | ||
| 255 | pcibus_info->pbi_enabled_devices &= ~(1 << device_num); | ||
| 256 | } | ||
| 257 | |||
| 258 | static int sn_slot_enable(struct hotplug_slot *bss_hotplug_slot, | ||
| 259 | int device_num) | ||
| 260 | { | ||
| 261 | struct slot *slot = (struct slot *)bss_hotplug_slot->private; | ||
| 262 | struct pcibus_info *pcibus_info; | ||
| 263 | struct pcibr_slot_enable_resp resp; | ||
| 264 | int rc; | ||
| 265 | |||
| 266 | pcibus_info = SN_PCIBUS_BUSSOFT_INFO(slot->pci_bus); | ||
| 267 | |||
| 268 | /* | ||
| 269 | * Power-on and initialize the slot in the SN | ||
| 270 | * PCI infrastructure. | ||
| 271 | */ | ||
| 272 | rc = sal_pcibr_slot_enable(pcibus_info, device_num, &resp); | ||
| 273 | |||
| 274 | if (rc == PCI_SLOT_ALREADY_UP) { | ||
| 275 | dev_dbg(slot->pci_bus->self, "is already active\n"); | ||
| 276 | return -EPERM; | ||
| 277 | } | ||
| 278 | |||
| 279 | if (rc == PCI_L1_ERR) { | ||
| 280 | dev_dbg(slot->pci_bus->self, | ||
| 281 | "L1 failure %d with message: %s", | ||
| 282 | resp.resp_sub_errno, resp.resp_l1_msg); | ||
| 283 | return -EPERM; | ||
| 284 | } | ||
| 285 | |||
| 286 | if (rc) { | ||
| 287 | dev_dbg(slot->pci_bus->self, | ||
| 288 | "insert failed with error %d sub-error %d\n", | ||
| 289 | rc, resp.resp_sub_errno); | ||
| 290 | return -EIO; | ||
| 291 | } | ||
| 292 | |||
| 293 | sn_slot_mark_enable(bss_hotplug_slot, device_num); | ||
| 294 | |||
| 295 | return 0; | ||
| 296 | } | ||
| 297 | |||
| 298 | static int sn_slot_disable(struct hotplug_slot *bss_hotplug_slot, | ||
| 299 | int device_num, int action) | ||
| 300 | { | ||
| 301 | struct slot *slot = (struct slot *)bss_hotplug_slot->private; | ||
| 302 | struct pcibus_info *pcibus_info; | ||
| 303 | struct pcibr_slot_disable_resp resp; | ||
| 304 | int rc; | ||
| 305 | |||
| 306 | pcibus_info = SN_PCIBUS_BUSSOFT_INFO(slot->pci_bus); | ||
| 307 | |||
| 308 | rc = sal_pcibr_slot_disable(pcibus_info, device_num, action, &resp); | ||
| 309 | |||
| 310 | if (action == PCI_REQ_SLOT_ELIGIBLE && rc == PCI_SLOT_ALREADY_DOWN) { | ||
| 311 | dev_dbg(slot->pci_bus->self, "Slot %s already inactive\n"); | ||
| 312 | return -ENODEV; | ||
| 313 | } | ||
| 314 | |||
| 315 | if (action == PCI_REQ_SLOT_ELIGIBLE && rc == PCI_EMPTY_33MHZ) { | ||
| 316 | dev_dbg(slot->pci_bus->self, | ||
| 317 | "Cannot remove last 33MHz card\n"); | ||
| 318 | return -EPERM; | ||
| 319 | } | ||
| 320 | |||
| 321 | if (action == PCI_REQ_SLOT_ELIGIBLE && rc == PCI_L1_ERR) { | ||
| 322 | dev_dbg(slot->pci_bus->self, | ||
| 323 | "L1 failure %d with message \n%s\n", | ||
| 324 | resp.resp_sub_errno, resp.resp_l1_msg); | ||
| 325 | return -EPERM; | ||
| 326 | } | ||
| 327 | |||
| 328 | if (action == PCI_REQ_SLOT_ELIGIBLE && rc) { | ||
| 329 | dev_dbg(slot->pci_bus->self, | ||
| 330 | "remove failed with error %d sub-error %d\n", | ||
| 331 | rc, resp.resp_sub_errno); | ||
| 332 | return -EIO; | ||
| 333 | } | ||
| 334 | |||
| 335 | if (action == PCI_REQ_SLOT_ELIGIBLE && !rc) | ||
| 336 | return 0; | ||
| 337 | |||
| 338 | if (action == PCI_REQ_SLOT_DISABLE && !rc) { | ||
| 339 | sn_slot_mark_disable(bss_hotplug_slot, device_num); | ||
| 340 | dev_dbg(slot->pci_bus->self, "remove successful\n"); | ||
| 341 | return 0; | ||
| 342 | } | ||
| 343 | |||
| 344 | if (action == PCI_REQ_SLOT_DISABLE && rc) { | ||
| 345 | dev_dbg(slot->pci_bus->self,"remove failed rc = %d\n", rc); | ||
| 346 | return rc; | ||
| 347 | } | ||
| 348 | |||
| 349 | return rc; | ||
| 350 | } | ||
| 351 | |||
| 352 | static int enable_slot(struct hotplug_slot *bss_hotplug_slot) | ||
| 353 | { | ||
| 354 | struct slot *slot = (struct slot *)bss_hotplug_slot->private; | ||
| 355 | struct pci_bus *new_bus = NULL; | ||
| 356 | struct pci_dev *dev; | ||
| 357 | int func, num_funcs; | ||
| 358 | int new_ppb = 0; | ||
| 359 | int rc; | ||
| 360 | |||
| 361 | /* Serialize the Linux PCI infrastructure */ | ||
| 362 | down(&sn_hotplug_sem); | ||
| 363 | |||
| 364 | /* | ||
| 365 | * Power-on and initialize the slot in the SN | ||
| 366 | * PCI infrastructure. | ||
| 367 | */ | ||
| 368 | rc = sn_slot_enable(bss_hotplug_slot, slot->device_num); | ||
| 369 | if (rc) { | ||
| 370 | up(&sn_hotplug_sem); | ||
| 371 | return rc; | ||
| 372 | } | ||
| 373 | |||
| 374 | num_funcs = pci_scan_slot(slot->pci_bus, PCI_DEVFN(slot->device_num+1, | ||
| 375 | PCI_FUNC(0))); | ||
| 376 | if (!num_funcs) { | ||
| 377 | dev_dbg(slot->pci_bus->self, "no device in slot\n"); | ||
| 378 | up(&sn_hotplug_sem); | ||
| 379 | return -ENODEV; | ||
| 380 | } | ||
| 381 | |||
| 382 | sn_pci_controller_fixup(pci_domain_nr(slot->pci_bus), | ||
| 383 | slot->pci_bus->number, | ||
| 384 | slot->pci_bus); | ||
| 385 | /* | ||
| 386 | * Map SN resources for all functions on the card | ||
| 387 | * to the Linux PCI interface and tell the drivers | ||
| 388 | * about them. | ||
| 389 | */ | ||
| 390 | for (func = 0; func < num_funcs; func++) { | ||
| 391 | dev = pci_get_slot(slot->pci_bus, | ||
| 392 | PCI_DEVFN(slot->device_num + 1, | ||
| 393 | PCI_FUNC(func))); | ||
| 394 | |||
| 395 | |||
| 396 | if (dev) { | ||
| 397 | if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) { | ||
| 398 | unsigned char sec_bus; | ||
| 399 | pci_read_config_byte(dev, PCI_SECONDARY_BUS, | ||
| 400 | &sec_bus); | ||
| 401 | new_bus = pci_add_new_bus(dev->bus, dev, | ||
| 402 | sec_bus); | ||
| 403 | pci_scan_child_bus(new_bus); | ||
| 404 | sn_pci_controller_fixup(pci_domain_nr(new_bus), | ||
| 405 | new_bus->number, | ||
| 406 | new_bus); | ||
| 407 | new_ppb = 1; | ||
| 408 | } | ||
| 409 | sn_bus_alloc_data(dev); | ||
| 410 | pci_dev_put(dev); | ||
| 411 | } | ||
| 412 | } | ||
| 413 | |||
| 414 | /* Call the driver for the new device */ | ||
| 415 | pci_bus_add_devices(slot->pci_bus); | ||
| 416 | /* Call the drivers for the new devices subordinate to PPB */ | ||
| 417 | if (new_ppb) | ||
| 418 | pci_bus_add_devices(new_bus); | ||
| 419 | |||
| 420 | up(&sn_hotplug_sem); | ||
| 421 | |||
| 422 | if (rc == 0) | ||
| 423 | dev_dbg(slot->pci_bus->self, | ||
| 424 | "insert operation successful\n"); | ||
| 425 | else | ||
| 426 | dev_dbg(slot->pci_bus->self, | ||
| 427 | "insert operation failed rc = %d\n", rc); | ||
| 428 | |||
| 429 | return rc; | ||
| 430 | } | ||
| 431 | |||
| 432 | static int disable_slot(struct hotplug_slot *bss_hotplug_slot) | ||
| 433 | { | ||
| 434 | struct slot *slot = (struct slot *)bss_hotplug_slot->private; | ||
| 435 | struct pci_dev *dev; | ||
| 436 | int func; | ||
| 437 | int rc; | ||
| 438 | |||
| 439 | /* Acquire update access to the bus */ | ||
| 440 | down(&sn_hotplug_sem); | ||
| 441 | |||
| 442 | /* is it okay to bring this slot down? */ | ||
| 443 | rc = sn_slot_disable(bss_hotplug_slot, slot->device_num, | ||
| 444 | PCI_REQ_SLOT_ELIGIBLE); | ||
| 445 | if (rc) | ||
| 446 | goto leaving; | ||
| 447 | |||
| 448 | /* Free the SN resources assigned to the Linux device.*/ | ||
| 449 | for (func = 0; func < 8; func++) { | ||
| 450 | dev = pci_get_slot(slot->pci_bus, | ||
| 451 | PCI_DEVFN(slot->device_num+1, | ||
| 452 | PCI_FUNC(func))); | ||
| 453 | if (dev) { | ||
| 454 | /* | ||
| 455 | * Some drivers may use dma accesses during the | ||
| 456 | * driver remove function. We release the sysdata | ||
| 457 | * areas after the driver remove functions have | ||
| 458 | * been called. | ||
| 459 | */ | ||
| 460 | sn_bus_store_sysdata(dev); | ||
| 461 | sn_bus_free_data(dev); | ||
| 462 | pci_remove_bus_device(dev); | ||
| 463 | pci_dev_put(dev); | ||
| 464 | } | ||
| 465 | } | ||
| 466 | |||
| 467 | /* free the collected sysdata pointers */ | ||
| 468 | sn_bus_free_sysdata(); | ||
| 469 | |||
| 470 | /* Deactivate slot */ | ||
| 471 | rc = sn_slot_disable(bss_hotplug_slot, slot->device_num, | ||
| 472 | PCI_REQ_SLOT_DISABLE); | ||
| 473 | leaving: | ||
| 474 | /* Release the bus lock */ | ||
| 475 | up(&sn_hotplug_sem); | ||
| 476 | |||
| 477 | return rc; | ||
| 478 | } | ||
| 479 | |||
| 480 | static int get_power_status(struct hotplug_slot *bss_hotplug_slot, u8 *value) | ||
| 481 | { | ||
| 482 | down(&sn_hotplug_sem); | ||
| 483 | *value = sn_power_status_get(bss_hotplug_slot); | ||
| 484 | up(&sn_hotplug_sem); | ||
| 485 | return 0; | ||
| 486 | } | ||
| 487 | |||
| 488 | static void sn_release_slot(struct hotplug_slot *bss_hotplug_slot) | ||
| 489 | { | ||
| 490 | kfree(bss_hotplug_slot->info); | ||
| 491 | kfree(bss_hotplug_slot->name); | ||
| 492 | kfree(bss_hotplug_slot->private); | ||
| 493 | kfree(bss_hotplug_slot); | ||
| 494 | } | ||
| 495 | |||
| 496 | static int sn_hotplug_slot_register(struct pci_bus *pci_bus) | ||
| 497 | { | ||
| 498 | int device; | ||
| 499 | struct hotplug_slot *bss_hotplug_slot; | ||
| 500 | int rc = 0; | ||
| 501 | |||
| 502 | /* | ||
| 503 | * Currently only four devices are supported, | ||
| 504 | * in the future there maybe more -- up to 32. | ||
| 505 | */ | ||
| 506 | |||
| 507 | for (device = 0; device < SN_MAX_HP_SLOTS ; device++) { | ||
| 508 | if (sn_pci_slot_valid(pci_bus, device) != 1) | ||
| 509 | continue; | ||
| 510 | |||
| 511 | bss_hotplug_slot = kcalloc(1,sizeof(struct hotplug_slot), | ||
| 512 | GFP_KERNEL); | ||
| 513 | if (!bss_hotplug_slot) { | ||
| 514 | rc = -ENOMEM; | ||
| 515 | goto alloc_err; | ||
| 516 | } | ||
| 517 | |||
| 518 | bss_hotplug_slot->info = | ||
| 519 | kcalloc(1,sizeof(struct hotplug_slot_info), | ||
| 520 | GFP_KERNEL); | ||
| 521 | if (!bss_hotplug_slot->info) { | ||
| 522 | rc = -ENOMEM; | ||
| 523 | goto alloc_err; | ||
| 524 | } | ||
| 525 | |||
| 526 | if (sn_hp_slot_private_alloc(bss_hotplug_slot, | ||
| 527 | pci_bus, device)) { | ||
| 528 | rc = -ENOMEM; | ||
| 529 | goto alloc_err; | ||
| 530 | } | ||
| 531 | |||
| 532 | bss_hotplug_slot->ops = &sn_hotplug_slot_ops; | ||
| 533 | bss_hotplug_slot->release = &sn_release_slot; | ||
| 534 | |||
| 535 | rc = pci_hp_register(bss_hotplug_slot); | ||
| 536 | if (rc) | ||
| 537 | goto register_err; | ||
| 538 | } | ||
| 539 | dev_dbg(pci_bus->self, "Registered bus with hotplug\n"); | ||
| 540 | return rc; | ||
| 541 | |||
| 542 | register_err: | ||
| 543 | dev_dbg(pci_bus->self, "bus failed to register with err = %d\n", | ||
| 544 | rc); | ||
| 545 | |||
| 546 | alloc_err: | ||
| 547 | if (rc == -ENOMEM) | ||
| 548 | dev_dbg(pci_bus->self, "Memory allocation error\n"); | ||
| 549 | |||
| 550 | /* destroy THIS element */ | ||
| 551 | if (bss_hotplug_slot) | ||
| 552 | sn_release_slot(bss_hotplug_slot); | ||
| 553 | |||
| 554 | /* destroy anything else on the list */ | ||
| 555 | while ((bss_hotplug_slot = sn_hp_destroy())) | ||
| 556 | pci_hp_deregister(bss_hotplug_slot); | ||
| 557 | |||
| 558 | return rc; | ||
| 559 | } | ||
| 560 | |||
| 561 | static int sn_pci_hotplug_init(void) | ||
| 562 | { | ||
| 563 | struct pci_bus *pci_bus = NULL; | ||
| 564 | int rc; | ||
| 565 | int registered = 0; | ||
| 566 | |||
| 567 | INIT_LIST_HEAD(&sn_hp_list); | ||
| 568 | |||
| 569 | if (sn_sal_rev() < SGI_HOTPLUG_PROM_REV) { | ||
| 570 | printk(KERN_ERR "%s: PROM version must be greater than 4.05\n", | ||
| 571 | __FUNCTION__); | ||
| 572 | return -EPERM; | ||
| 573 | } | ||
| 574 | |||
| 575 | while ((pci_bus = pci_find_next_bus(pci_bus))) { | ||
| 576 | if (!pci_bus->sysdata) | ||
| 577 | continue; | ||
| 578 | |||
| 579 | rc = sn_pci_bus_valid(pci_bus); | ||
| 580 | if (rc != 1) { | ||
| 581 | dev_dbg(pci_bus->self, "not a valid hotplug bus\n"); | ||
| 582 | continue; | ||
| 583 | } | ||
| 584 | dev_dbg(pci_bus->self, "valid hotplug bus\n"); | ||
| 585 | |||
| 586 | rc = sn_hotplug_slot_register(pci_bus); | ||
| 587 | if (!rc) | ||
| 588 | registered = 1; | ||
| 589 | else { | ||
| 590 | registered = 0; | ||
| 591 | break; | ||
| 592 | } | ||
| 593 | } | ||
| 594 | |||
| 595 | return registered == 1 ? 0 : -ENODEV; | ||
| 596 | } | ||
| 597 | |||
| 598 | static void sn_pci_hotplug_exit(void) | ||
| 599 | { | ||
| 600 | struct hotplug_slot *bss_hotplug_slot; | ||
| 601 | |||
| 602 | while ((bss_hotplug_slot = sn_hp_destroy())) { | ||
| 603 | pci_hp_deregister(bss_hotplug_slot); | ||
| 604 | } | ||
| 605 | |||
| 606 | if (!list_empty(&sn_hp_list)) | ||
| 607 | printk(KERN_ERR "%s: internal list is not empty\n", __FILE__); | ||
| 608 | } | ||
| 609 | |||
| 610 | module_init(sn_pci_hotplug_init); | ||
| 611 | module_exit(sn_pci_hotplug_exit); | ||
diff --git a/include/asm-ia64/sn/pcibr_provider.h b/include/asm-ia64/sn/pcibr_provider.h index cbb4604c9349..2299c3ad2e33 100644 --- a/include/asm-ia64/sn/pcibr_provider.h +++ b/include/asm-ia64/sn/pcibr_provider.h | |||
| @@ -151,4 +151,8 @@ extern void pcibr_change_devices_irq(struct sn_irq_info *sn_irq_info); | |||
| 151 | extern int pcibr_ate_alloc(struct pcibus_info *, int); | 151 | extern int pcibr_ate_alloc(struct pcibus_info *, int); |
| 152 | extern void pcibr_ate_free(struct pcibus_info *, int); | 152 | extern void pcibr_ate_free(struct pcibus_info *, int); |
| 153 | extern void ate_write(struct pcibus_info *, int, int, uint64_t); | 153 | extern void ate_write(struct pcibus_info *, int, int, uint64_t); |
| 154 | extern int sal_pcibr_slot_enable(struct pcibus_info *soft, int device, | ||
| 155 | void *resp); | ||
| 156 | extern int sal_pcibr_slot_disable(struct pcibus_info *soft, int device, | ||
| 157 | int action, void *resp); | ||
| 154 | #endif | 158 | #endif |
diff --git a/include/asm-ia64/sn/pcidev.h b/include/asm-ia64/sn/pcidev.h index 9610fcc63545..49711d00ad04 100644 --- a/include/asm-ia64/sn/pcidev.h +++ b/include/asm-ia64/sn/pcidev.h | |||
| @@ -23,6 +23,8 @@ | |||
| 23 | #define SN_PCIBUS_BUSSOFT(pci_bus) \ | 23 | #define SN_PCIBUS_BUSSOFT(pci_bus) \ |
| 24 | ((struct pcibus_bussoft *)(PCI_CONTROLLER((pci_bus))->platform_data)) | 24 | ((struct pcibus_bussoft *)(PCI_CONTROLLER((pci_bus))->platform_data)) |
| 25 | 25 | ||
| 26 | #define SN_PCIBUS_BUSSOFT_INFO(pci_bus) \ | ||
| 27 | (struct pcibus_info *)((struct pcibus_bussoft *)(PCI_CONTROLLER((pci_bus))->platform_data)) | ||
| 26 | /* | 28 | /* |
| 27 | * Given a struct pci_dev, return the sn pcibus_bussoft struct. Note | 29 | * Given a struct pci_dev, return the sn pcibus_bussoft struct. Note |
| 28 | * that this is not equivalent to SN_PCIBUS_BUSSOFT(pci_dev->bus) due | 30 | * that this is not equivalent to SN_PCIBUS_BUSSOFT(pci_dev->bus) due |
| @@ -56,6 +58,10 @@ struct pcidev_info { | |||
| 56 | extern void sn_irq_fixup(struct pci_dev *pci_dev, | 58 | extern void sn_irq_fixup(struct pci_dev *pci_dev, |
| 57 | struct sn_irq_info *sn_irq_info); | 59 | struct sn_irq_info *sn_irq_info); |
| 58 | extern void sn_irq_unfixup(struct pci_dev *pci_dev); | 60 | extern void sn_irq_unfixup(struct pci_dev *pci_dev); |
| 61 | extern void sn_pci_controller_fixup(int segment, int busnum, | ||
| 62 | struct pci_bus *bus); | ||
| 63 | extern void sn_bus_store_sysdata(struct pci_dev *dev); | ||
| 64 | extern void sn_bus_free_sysdata(void); | ||
| 59 | extern void sn_pci_fixup_slot(struct pci_dev *dev); | 65 | extern void sn_pci_fixup_slot(struct pci_dev *dev); |
| 60 | extern void sn_pci_unfixup_slot(struct pci_dev *dev); | 66 | extern void sn_pci_unfixup_slot(struct pci_dev *dev); |
| 61 | extern void sn_irq_lh_init(void); | 67 | extern void sn_irq_lh_init(void); |
