| 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) 2006 Silicon Graphics, Inc. All rights reserved. |
| 7 | */ |
| 8 | |
| 9 | #include <linux/memblock.h> |
| 10 | #include <linux/export.h> |
| 11 | #include <linux/slab.h> |
| 12 | #include <asm/sn/types.h> |
| 13 | #include <asm/sn/addrs.h> |
| 14 | #include <asm/sn/sn_feature_sets.h> |
| 15 | #include <asm/sn/geo.h> |
| 16 | #include <asm/sn/io.h> |
| 17 | #include <asm/sn/l1.h> |
| 18 | #include <asm/sn/module.h> |
| 19 | #include <asm/sn/pcibr_provider.h> |
| 20 | #include <asm/sn/pcibus_provider_defs.h> |
| 21 | #include <asm/sn/pcidev.h> |
| 22 | #include <asm/sn/simulator.h> |
| 23 | #include <asm/sn/sn_sal.h> |
| 24 | #include <asm/sn/tioca_provider.h> |
| 25 | #include <asm/sn/tioce_provider.h> |
| 26 | #include "xtalk/hubdev.h" |
| 27 | #include "xtalk/xwidgetdev.h" |
| 28 | #include <linux/acpi.h> |
| 29 | #include <asm/sn/sn2/sn_hwperf.h> |
| 30 | #include <asm/sn/acpi.h> |
| 31 | |
| 32 | extern void sn_init_cpei_timer(void); |
| 33 | extern void register_sn_procfs(void); |
| 34 | extern void sn_io_acpi_init(void); |
| 35 | extern void sn_io_init(void); |
| 36 | |
| 37 | |
| 38 | static struct list_head sn_sysdata_list; |
| 39 | |
| 40 | /* sysdata list struct */ |
| 41 | struct sysdata_el { |
| 42 | struct list_head entry; |
| 43 | void *sysdata; |
| 44 | }; |
| 45 | |
| 46 | int sn_ioif_inited; /* SN I/O infrastructure initialized? */ |
| 47 | |
| 48 | int sn_acpi_rev; /* SN ACPI revision */ |
| 49 | EXPORT_SYMBOL_GPL(sn_acpi_rev); |
| 50 | |
| 51 | struct sn_pcibus_provider *sn_pci_provider[PCIIO_ASIC_MAX_TYPES]; /* indexed by asic type */ |
| 52 | |
| 53 | /* |
| 54 | * Hooks and struct for unsupported pci providers |
| 55 | */ |
| 56 | |
| 57 | static dma_addr_t |
| 58 | sn_default_pci_map(struct pci_dev *pdev, unsigned long paddr, size_t size, int type) |
| 59 | { |
| 60 | return 0; |
| 61 | } |
| 62 | |
| 63 | static void |
| 64 | sn_default_pci_unmap(struct pci_dev *pdev, dma_addr_t addr, int direction) |
| 65 | { |
| 66 | return; |
| 67 | } |
| 68 | |
| 69 | static void * |
| 70 | sn_default_pci_bus_fixup(struct pcibus_bussoft *soft, struct pci_controller *controller) |
| 71 | { |
| 72 | return NULL; |
| 73 | } |
| 74 | |
| 75 | static struct sn_pcibus_provider sn_pci_default_provider = { |
| 76 | .dma_map = sn_default_pci_map, |
| 77 | .dma_map_consistent = sn_default_pci_map, |
| 78 | .dma_unmap = sn_default_pci_unmap, |
| 79 | .bus_fixup = sn_default_pci_bus_fixup, |
| 80 | }; |
| 81 | |
| 82 | /* |
| 83 | * Retrieve the DMA Flush List given nasid, widget, and device. |
| 84 | * This list is needed to implement the WAR - Flush DMA data on PIO Reads. |
| 85 | */ |
| 86 | static inline u64 |
| 87 | sal_get_device_dmaflush_list(u64 nasid, u64 widget_num, u64 device_num, |
| 88 | u64 address) |
| 89 | { |
| 90 | struct ia64_sal_retval ret_stuff; |
| 91 | ret_stuff.status = 0; |
| 92 | ret_stuff.v0 = 0; |
| 93 | |
| 94 | SAL_CALL_NOLOCK(ret_stuff, |
| 95 | (u64) SN_SAL_IOIF_GET_DEVICE_DMAFLUSH_LIST, |
| 96 | (u64) nasid, (u64) widget_num, |
| 97 | (u64) device_num, (u64) address, 0, 0, 0); |
| 98 | return ret_stuff.status; |
| 99 | } |
| 100 | |
| 101 | /* |
| 102 | * sn_pcidev_info_get() - Retrieve the pcidev_info struct for the specified |
| 103 | * device. |
| 104 | */ |
| 105 | inline struct pcidev_info * |
| 106 | sn_pcidev_info_get(struct pci_dev *dev) |
| 107 | { |
| 108 | struct pcidev_info *pcidev; |
| 109 | |
| 110 | list_for_each_entry(pcidev, |
| 111 | &(SN_PLATFORM_DATA(dev)->pcidev_info), pdi_list) { |
| 112 | if (pcidev->pdi_linux_pcidev == dev) |
| 113 | return pcidev; |
| 114 | } |
| 115 | return NULL; |
| 116 | } |
| 117 | |
| 118 | /* Older PROM flush WAR |
| 119 | * |
| 120 | * 01/16/06 -- This war will be in place until a new official PROM is released. |
| 121 | * Additionally note that the struct sn_flush_device_war also has to be |
| 122 | * removed from arch/ia64/sn/include/xtalk/hubdev.h |
| 123 | */ |
| 124 | |
| 125 | static s64 sn_device_fixup_war(u64 nasid, u64 widget, int device, |
| 126 | struct sn_flush_device_common *common) |
| 127 | { |
| 128 | struct sn_flush_device_war *war_list; |
| 129 | struct sn_flush_device_war *dev_entry; |
| 130 | struct ia64_sal_retval isrv = {0,0,0,0}; |
| 131 | |
| 132 | printk_once(KERN_WARNING |
| 133 | "PROM version < 4.50 -- implementing old PROM flush WAR\n"); |
| 134 | |
| 135 | war_list = kcalloc(DEV_PER_WIDGET, sizeof(*war_list), GFP_KERNEL); |
| 136 | BUG_ON(!war_list); |
| 137 | |
| 138 | SAL_CALL_NOLOCK(isrv, SN_SAL_IOIF_GET_WIDGET_DMAFLUSH_LIST, |
| 139 | nasid, widget, __pa(war_list), 0, 0, 0 ,0); |
| 140 | if (isrv.status) |
| 141 | panic("sn_device_fixup_war failed: %s\n", |
| 142 | ia64_sal_strerror(isrv.status)); |
| 143 | |
| 144 | dev_entry = war_list + device; |
| 145 | memcpy(common,dev_entry, sizeof(*common)); |
| 146 | kfree(war_list); |
| 147 | |
| 148 | return isrv.status; |
| 149 | } |
| 150 | |
| 151 | /* |
| 152 | * sn_common_hubdev_init() - This routine is called to initialize the HUB data |
| 153 | * structure for each node in the system. |
| 154 | */ |
| 155 | void __init |
| 156 | sn_common_hubdev_init(struct hubdev_info *hubdev) |
| 157 | { |
| 158 | |
| 159 | struct sn_flush_device_kernel *sn_flush_device_kernel; |
| 160 | struct sn_flush_device_kernel *dev_entry; |
| 161 | s64 status; |
| 162 | int widget, device, size; |
| 163 | |
| 164 | /* Attach the error interrupt handlers */ |
| 165 | if (hubdev->hdi_nasid & 1) /* If TIO */ |
| 166 | ice_error_init(hubdev); |
| 167 | else |
| 168 | hub_error_init(hubdev); |
| 169 | |
| 170 | for (widget = 0; widget <= HUB_WIDGET_ID_MAX; widget++) |
| 171 | hubdev->hdi_xwidget_info[widget].xwi_hubinfo = hubdev; |
| 172 | |
| 173 | if (!hubdev->hdi_flush_nasid_list.widget_p) |
| 174 | return; |
| 175 | |
| 176 | size = (HUB_WIDGET_ID_MAX + 1) * |
| 177 | sizeof(struct sn_flush_device_kernel *); |
| 178 | hubdev->hdi_flush_nasid_list.widget_p = |
| 179 | kzalloc(size, GFP_KERNEL); |
| 180 | BUG_ON(!hubdev->hdi_flush_nasid_list.widget_p); |
| 181 | |
| 182 | for (widget = 0; widget <= HUB_WIDGET_ID_MAX; widget++) { |
| 183 | size = DEV_PER_WIDGET * |
| 184 | sizeof(struct sn_flush_device_kernel); |
| 185 | sn_flush_device_kernel = kzalloc(size, GFP_KERNEL); |
| 186 | BUG_ON(!sn_flush_device_kernel); |
| 187 | |
| 188 | dev_entry = sn_flush_device_kernel; |
| 189 | for (device = 0; device < DEV_PER_WIDGET; |
| 190 | device++, dev_entry++) { |
| 191 | size = sizeof(struct sn_flush_device_common); |
| 192 | dev_entry->common = kzalloc(size, GFP_KERNEL); |
| 193 | BUG_ON(!dev_entry->common); |
| 194 | if (sn_prom_feature_available(PRF_DEVICE_FLUSH_LIST)) |
| 195 | status = sal_get_device_dmaflush_list( |
| 196 | hubdev->hdi_nasid, widget, device, |
| 197 | (u64)(dev_entry->common)); |
| 198 | else |
| 199 | status = sn_device_fixup_war(hubdev->hdi_nasid, |
| 200 | widget, device, |
| 201 | dev_entry->common); |
| 202 | if (status != SALRET_OK) |
| 203 | panic("SAL call failed: %s\n", |
| 204 | ia64_sal_strerror(status)); |
| 205 | |
| 206 | spin_lock_init(&dev_entry->sfdl_flush_lock); |
| 207 | } |
| 208 | |
| 209 | if (sn_flush_device_kernel) |
| 210 | hubdev->hdi_flush_nasid_list.widget_p[widget] = |
| 211 | sn_flush_device_kernel; |
| 212 | } |
| 213 | } |
| 214 | |
| 215 | void sn_pci_unfixup_slot(struct pci_dev *dev) |
| 216 | { |
| 217 | struct pci_dev *host_pci_dev = SN_PCIDEV_INFO(dev)->host_pci_dev; |
| 218 | |
| 219 | sn_irq_unfixup(dev); |
| 220 | pci_dev_put(host_pci_dev); |
| 221 | pci_dev_put(dev); |
| 222 | } |
| 223 | |
| 224 | /* |
| 225 | * sn_pci_fixup_slot() |
| 226 | */ |
| 227 | void sn_pci_fixup_slot(struct pci_dev *dev, struct pcidev_info *pcidev_info, |
| 228 | struct sn_irq_info *sn_irq_info) |
| 229 | { |
| 230 | int segment = pci_domain_nr(dev->bus); |
| 231 | struct pcibus_bussoft *bs; |
| 232 | struct pci_dev *host_pci_dev; |
| 233 | unsigned int bus_no, devfn; |
| 234 | |
| 235 | pci_dev_get(dev); /* for the sysdata pointer */ |
| 236 | |
| 237 | /* Add pcidev_info to list in pci_controller.platform_data */ |
| 238 | list_add_tail(&pcidev_info->pdi_list, |
| 239 | &(SN_PLATFORM_DATA(dev->bus)->pcidev_info)); |
| 240 | /* |
| 241 | * Using the PROMs values for the PCI host bus, get the Linux |
| 242 | * PCI host_pci_dev struct and set up host bus linkages |
| 243 | */ |
| 244 | |
| 245 | bus_no = (pcidev_info->pdi_slot_host_handle >> 32) & 0xff; |
| 246 | devfn = pcidev_info->pdi_slot_host_handle & 0xffffffff; |
| 247 | host_pci_dev = pci_get_domain_bus_and_slot(segment, bus_no, devfn); |
| 248 | |
| 249 | pcidev_info->host_pci_dev = host_pci_dev; |
| 250 | pcidev_info->pdi_linux_pcidev = dev; |
| 251 | pcidev_info->pdi_host_pcidev_info = SN_PCIDEV_INFO(host_pci_dev); |
| 252 | bs = SN_PCIBUS_BUSSOFT(dev->bus); |
| 253 | pcidev_info->pdi_pcibus_info = bs; |
| 254 | |
| 255 | if (bs && bs->bs_asic_type < PCIIO_ASIC_MAX_TYPES) { |
| 256 | SN_PCIDEV_BUSPROVIDER(dev) = sn_pci_provider[bs->bs_asic_type]; |
| 257 | } else { |
| 258 | SN_PCIDEV_BUSPROVIDER(dev) = &sn_pci_default_provider; |
| 259 | } |
| 260 | |
| 261 | /* Only set up IRQ stuff if this device has a host bus context */ |
| 262 | if (bs && sn_irq_info->irq_irq) { |
| 263 | pcidev_info->pdi_sn_irq_info = sn_irq_info; |
| 264 | dev->irq = pcidev_info->pdi_sn_irq_info->irq_irq; |
| 265 | sn_irq_fixup(dev, sn_irq_info); |
| 266 | } else { |
| 267 | pcidev_info->pdi_sn_irq_info = NULL; |
| 268 | kfree(sn_irq_info); |
| 269 | } |
| 270 | } |
| 271 | |
| 272 | /* |
| 273 | * sn_common_bus_fixup - Perform platform specific bus fixup. |
| 274 | * Execute the ASIC specific fixup routine |
| 275 | * for this bus. |
| 276 | */ |
| 277 | void |
| 278 | sn_common_bus_fixup(struct pci_bus *bus, |
| 279 | struct pcibus_bussoft *prom_bussoft_ptr) |
| 280 | { |
| 281 | int cnode; |
| 282 | struct pci_controller *controller; |
| 283 | struct hubdev_info *hubdev_info; |
| 284 | int nasid; |
| 285 | void *provider_soft; |
| 286 | struct sn_pcibus_provider *provider; |
| 287 | struct sn_platform_data *sn_platform_data; |
| 288 | |
| 289 | controller = PCI_CONTROLLER(bus); |
| 290 | /* |
| 291 | * Per-provider fixup. Copies the bus soft structure from prom |
| 292 | * to local area and links SN_PCIBUS_BUSSOFT(). |
| 293 | */ |
| 294 | |
| 295 | if (prom_bussoft_ptr->bs_asic_type >= PCIIO_ASIC_MAX_TYPES) { |
| 296 | printk(KERN_WARNING "sn_common_bus_fixup: Unsupported asic type, %d", |
| 297 | prom_bussoft_ptr->bs_asic_type); |
| 298 | return; |
| 299 | } |
| 300 | |
| 301 | if (prom_bussoft_ptr->bs_asic_type == PCIIO_ASIC_TYPE_PPB) |
| 302 | return; /* no further fixup necessary */ |
| 303 | |
| 304 | provider = sn_pci_provider[prom_bussoft_ptr->bs_asic_type]; |
| 305 | if (provider == NULL) |
| 306 | panic("sn_common_bus_fixup: No provider registered for this asic type, %d", |
| 307 | prom_bussoft_ptr->bs_asic_type); |
| 308 | |
| 309 | if (provider->bus_fixup) |
| 310 | provider_soft = (*provider->bus_fixup) (prom_bussoft_ptr, |
| 311 | controller); |
| 312 | else |
| 313 | provider_soft = NULL; |
| 314 | |
| 315 | /* |
| 316 | * Generic bus fixup goes here. Don't reference prom_bussoft_ptr |
| 317 | * after this point. |
| 318 | */ |
| 319 | controller->platform_data = kzalloc(sizeof(struct sn_platform_data), |
| 320 | GFP_KERNEL); |
| 321 | BUG_ON(controller->platform_data == NULL); |
| 322 | sn_platform_data = |
| 323 | (struct sn_platform_data *) controller->platform_data; |
| 324 | sn_platform_data->provider_soft = provider_soft; |
| 325 | INIT_LIST_HEAD(&((struct sn_platform_data *) |
| 326 | controller->platform_data)->pcidev_info); |
| 327 | nasid = NASID_GET(SN_PCIBUS_BUSSOFT(bus)->bs_base); |
| 328 | cnode = nasid_to_cnodeid(nasid); |
| 329 | hubdev_info = (struct hubdev_info *)(NODEPDA(cnode)->pdinfo); |
| 330 | SN_PCIBUS_BUSSOFT(bus)->bs_xwidget_info = |
| 331 | &(hubdev_info->hdi_xwidget_info[SN_PCIBUS_BUSSOFT(bus)->bs_xid]); |
| 332 | |
| 333 | /* |
| 334 | * If the node information we obtained during the fixup phase is |
| 335 | * invalid then set controller->node to -1 (undetermined) |
| 336 | */ |
| 337 | if (controller->node >= num_online_nodes()) { |
| 338 | struct pcibus_bussoft *b = SN_PCIBUS_BUSSOFT(bus); |
| 339 | |
| 340 | printk(KERN_WARNING "Device ASIC=%u XID=%u PBUSNUM=%u " |
| 341 | "L_IO=%llx L_MEM=%llx BASE=%llx\n", |
| 342 | b->bs_asic_type, b->bs_xid, b->bs_persist_busnum, |
| 343 | b->bs_legacy_io, b->bs_legacy_mem, b->bs_base); |
| 344 | printk(KERN_WARNING "on node %d but only %d nodes online." |
| 345 | "Association set to undetermined.\n", |
| 346 | controller->node, num_online_nodes()); |
| 347 | controller->node = -1; |
| 348 | } |
| 349 | } |
| 350 | |
| 351 | void sn_bus_store_sysdata(struct pci_dev *dev) |
| 352 | { |
| 353 | struct sysdata_el *element; |
| 354 | |
| 355 | element = kzalloc(sizeof(struct sysdata_el), GFP_KERNEL); |
| 356 | if (!element) { |
| 357 | dev_dbg(&dev->dev, "%s: out of memory!\n", __func__); |
| 358 | return; |
| 359 | } |
| 360 | element->sysdata = SN_PCIDEV_INFO(dev); |
| 361 | list_add(&element->entry, &sn_sysdata_list); |
| 362 | } |
| 363 | |
| 364 | void sn_bus_free_sysdata(void) |
| 365 | { |
| 366 | struct sysdata_el *element; |
| 367 | struct list_head *list, *safe; |
| 368 | |
| 369 | list_for_each_safe(list, safe, &sn_sysdata_list) { |
| 370 | element = list_entry(list, struct sysdata_el, entry); |
| 371 | list_del(&element->entry); |
| 372 | list_del(&(((struct pcidev_info *) |
| 373 | (element->sysdata))->pdi_list)); |
| 374 | kfree(element->sysdata); |
| 375 | kfree(element); |
| 376 | } |
| 377 | return; |
| 378 | } |
| 379 | |
| 380 | /* |
| 381 | * hubdev_init_node() - Creates the HUB data structure and link them to it's |
| 382 | * own NODE specific data area. |
| 383 | */ |
| 384 | void __init hubdev_init_node(nodepda_t * npda, cnodeid_t node) |
| 385 | { |
| 386 | struct hubdev_info *hubdev_info; |
| 387 | int size; |
| 388 | |
| 389 | size = sizeof(struct hubdev_info); |
| 390 | |
| 391 | if (node >= num_online_nodes()) /* Headless/memless IO nodes */ |
| 392 | node = 0; |
| 393 | |
| 394 | hubdev_info = (struct hubdev_info *)memblock_alloc_node(size, |
| 395 | SMP_CACHE_BYTES, |
| 396 | node); |
| 397 | |
| 398 | npda->pdinfo = (void *)hubdev_info; |
| 399 | } |
| 400 | |
| 401 | geoid_t |
| 402 | cnodeid_get_geoid(cnodeid_t cnode) |
| 403 | { |
| 404 | struct hubdev_info *hubdev; |
| 405 | |
| 406 | hubdev = (struct hubdev_info *)(NODEPDA(cnode)->pdinfo); |
| 407 | return hubdev->hdi_geoid; |
| 408 | } |
| 409 | |
| 410 | void sn_generate_path(struct pci_bus *pci_bus, char *address) |
| 411 | { |
| 412 | nasid_t nasid; |
| 413 | cnodeid_t cnode; |
| 414 | geoid_t geoid; |
| 415 | moduleid_t moduleid; |
| 416 | u16 bricktype; |
| 417 | |
| 418 | nasid = NASID_GET(SN_PCIBUS_BUSSOFT(pci_bus)->bs_base); |
| 419 | cnode = nasid_to_cnodeid(nasid); |
| 420 | geoid = cnodeid_get_geoid(cnode); |
| 421 | moduleid = geo_module(geoid); |
| 422 | |
| 423 | sprintf(address, "module_%c%c%c%c%.2d", |
| 424 | '0'+RACK_GET_CLASS(MODULE_GET_RACK(moduleid)), |
| 425 | '0'+RACK_GET_GROUP(MODULE_GET_RACK(moduleid)), |
| 426 | '0'+RACK_GET_NUM(MODULE_GET_RACK(moduleid)), |
| 427 | MODULE_GET_BTCHAR(moduleid), MODULE_GET_BPOS(moduleid)); |
| 428 | |
| 429 | /* Tollhouse requires slot id to be displayed */ |
| 430 | bricktype = MODULE_GET_BTYPE(moduleid); |
| 431 | if ((bricktype == L1_BRICKTYPE_191010) || |
| 432 | (bricktype == L1_BRICKTYPE_1932)) |
| 433 | sprintf(address + strlen(address), "^%d", |
| 434 | geo_slot(geoid)); |
| 435 | } |
| 436 | |
| 437 | void sn_pci_fixup_bus(struct pci_bus *bus) |
| 438 | { |
| 439 | |
| 440 | if (SN_ACPI_BASE_SUPPORT()) |
| 441 | sn_acpi_bus_fixup(bus); |
| 442 | else |
| 443 | sn_bus_fixup(bus); |
| 444 | } |
| 445 | |
| 446 | /* |
| 447 | * sn_io_early_init - Perform early IO (and some non-IO) initialization. |
| 448 | * In particular, setup the sn_pci_provider[] array. |
| 449 | * This needs to be done prior to any bus scanning |
| 450 | * (acpi_scan_init()) in the ACPI case, as the SN |
| 451 | * bus fixup code will reference the array. |
| 452 | */ |
| 453 | static int __init |
| 454 | sn_io_early_init(void) |
| 455 | { |
| 456 | int i; |
| 457 | |
| 458 | if (!ia64_platform_is("sn2") || IS_RUNNING_ON_FAKE_PROM()) |
| 459 | return 0; |
| 460 | |
| 461 | /* we set the acpi revision to that of the DSDT table OEM rev. */ |
| 462 | { |
| 463 | struct acpi_table_header *header = NULL; |
| 464 | |
| 465 | acpi_get_table(ACPI_SIG_DSDT, 1, &header); |
| 466 | BUG_ON(header == NULL); |
| 467 | sn_acpi_rev = header->oem_revision; |
| 468 | } |
| 469 | |
| 470 | /* |
| 471 | * prime sn_pci_provider[]. Individual provider init routines will |
| 472 | * override their respective default entries. |
| 473 | */ |
| 474 | |
| 475 | for (i = 0; i < PCIIO_ASIC_MAX_TYPES; i++) |
| 476 | sn_pci_provider[i] = &sn_pci_default_provider; |
| 477 | |
| 478 | pcibr_init_provider(); |
| 479 | tioca_init_provider(); |
| 480 | tioce_init_provider(); |
| 481 | |
| 482 | sn_irq_lh_init(); |
| 483 | INIT_LIST_HEAD(&sn_sysdata_list); |
| 484 | sn_init_cpei_timer(); |
| 485 | |
| 486 | #ifdef CONFIG_PROC_FS |
| 487 | register_sn_procfs(); |
| 488 | #endif |
| 489 | |
| 490 | { |
| 491 | struct acpi_table_header *header; |
| 492 | (void)acpi_get_table(ACPI_SIG_DSDT, 1, &header); |
| 493 | printk(KERN_INFO "ACPI DSDT OEM Rev 0x%x\n", |
| 494 | header->oem_revision); |
| 495 | } |
| 496 | if (SN_ACPI_BASE_SUPPORT()) |
| 497 | sn_io_acpi_init(); |
| 498 | else |
| 499 | sn_io_init(); |
| 500 | return 0; |
| 501 | } |
| 502 | |
| 503 | arch_initcall(sn_io_early_init); |
| 504 | |
| 505 | /* |
| 506 | * sn_io_late_init() - Perform any final platform specific IO initialization. |
| 507 | */ |
| 508 | |
| 509 | int __init |
| 510 | sn_io_late_init(void) |
| 511 | { |
| 512 | struct pci_bus *bus; |
| 513 | struct pcibus_bussoft *bussoft; |
| 514 | cnodeid_t cnode; |
| 515 | nasid_t nasid; |
| 516 | cnodeid_t near_cnode; |
| 517 | |
| 518 | if (!ia64_platform_is("sn2") || IS_RUNNING_ON_FAKE_PROM()) |
| 519 | return 0; |
| 520 | |
| 521 | /* |
| 522 | * Setup closest node in pci_controller->node for |
| 523 | * PIC, TIOCP, TIOCE (TIOCA does it during bus fixup using |
| 524 | * info from the PROM). |
| 525 | */ |
| 526 | bus = NULL; |
| 527 | while ((bus = pci_find_next_bus(bus)) != NULL) { |
| 528 | bussoft = SN_PCIBUS_BUSSOFT(bus); |
| 529 | nasid = NASID_GET(bussoft->bs_base); |
| 530 | cnode = nasid_to_cnodeid(nasid); |
| 531 | if ((bussoft->bs_asic_type == PCIIO_ASIC_TYPE_TIOCP) || |
| 532 | (bussoft->bs_asic_type == PCIIO_ASIC_TYPE_TIOCE) || |
| 533 | (bussoft->bs_asic_type == PCIIO_ASIC_TYPE_PIC)) { |
| 534 | /* PCI Bridge: find nearest node with CPUs */ |
| 535 | int e = sn_hwperf_get_nearest_node(cnode, NULL, |
| 536 | &near_cnode); |
| 537 | if (e < 0) { |
| 538 | near_cnode = (cnodeid_t)-1; /* use any node */ |
| 539 | printk(KERN_WARNING "sn_io_late_init: failed " |
| 540 | "to find near node with CPUs for " |
| 541 | "node %d, err=%d\n", cnode, e); |
| 542 | } |
| 543 | PCI_CONTROLLER(bus)->node = near_cnode; |
| 544 | } |
| 545 | } |
| 546 | |
| 547 | sn_ioif_inited = 1; /* SN I/O infrastructure now initialized */ |
| 548 | |
| 549 | return 0; |
| 550 | } |
| 551 | |
| 552 | fs_initcall(sn_io_late_init); |
| 553 | |
| 554 | EXPORT_SYMBOL(sn_pci_unfixup_slot); |
| 555 | EXPORT_SYMBOL(sn_bus_store_sysdata); |
| 556 | EXPORT_SYMBOL(sn_bus_free_sysdata); |
| 557 | EXPORT_SYMBOL(sn_generate_path); |
| 558 | |