diff options
-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); |