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