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 /arch | |
| 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>
Diffstat (limited to 'arch')
| -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 |
3 files changed, 129 insertions, 48 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); | ||
