]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
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 | * | |
3ec829b6 | 6 | * Copyright (C) 1992 - 1997, 2000-2005 Silicon Graphics, Inc. All rights reserved. |
1da177e4 LT |
7 | */ |
8 | ||
9 | #include <linux/bootmem.h> | |
10 | #include <linux/nodemask.h> | |
11 | #include <asm/sn/types.h> | |
1da177e4 | 12 | #include <asm/sn/addrs.h> |
61d67f2e | 13 | #include <asm/sn/sn_feature_sets.h> |
1da177e4 | 14 | #include <asm/sn/geo.h> |
1da177e4 | 15 | #include <asm/sn/io.h> |
c13cf371 PB |
16 | #include <asm/sn/pcibr_provider.h> |
17 | #include <asm/sn/pcibus_provider_defs.h> | |
18 | #include <asm/sn/pcidev.h> | |
1da177e4 | 19 | #include <asm/sn/simulator.h> |
c13cf371 | 20 | #include <asm/sn/sn_sal.h> |
9c90bdde | 21 | #include <asm/sn/tioca_provider.h> |
c9221da9 | 22 | #include <asm/sn/tioce_provider.h> |
c13cf371 PB |
23 | #include "xtalk/hubdev.h" |
24 | #include "xtalk/xwidgetdev.h" | |
1da177e4 | 25 | |
8ed9b2c7 JS |
26 | |
27 | extern void sn_init_cpei_timer(void); | |
28 | extern void register_sn_procfs(void); | |
29 | ||
6f354b01 PB |
30 | static struct list_head sn_sysdata_list; |
31 | ||
32 | /* sysdata list struct */ | |
33 | struct sysdata_el { | |
34 | struct list_head entry; | |
35 | void *sysdata; | |
36 | }; | |
37 | ||
1da177e4 LT |
38 | struct slab_info { |
39 | struct hubdev_info hubdev; | |
40 | }; | |
41 | ||
42 | struct brick { | |
43 | moduleid_t id; /* Module ID of this module */ | |
44 | struct slab_info slab_info[MAX_SLABS + 1]; | |
45 | }; | |
46 | ||
8ed9b2c7 | 47 | int sn_ioif_inited; /* SN I/O infrastructure initialized? */ |
1da177e4 | 48 | |
e955d825 MM |
49 | struct sn_pcibus_provider *sn_pci_provider[PCIIO_ASIC_MAX_TYPES]; /* indexed by asic type */ |
50 | ||
8ed9b2c7 JS |
51 | static int max_segment_number; /* Default highest segment number */ |
52 | static int max_pcibus_number = 255; /* Default highest pci bus number */ | |
674c6479 | 53 | |
e955d825 MM |
54 | /* |
55 | * Hooks and struct for unsupported pci providers | |
56 | */ | |
57 | ||
58 | static dma_addr_t | |
59 | sn_default_pci_map(struct pci_dev *pdev, unsigned long paddr, size_t size) | |
60 | { | |
61 | return 0; | |
62 | } | |
63 | ||
64 | static void | |
65 | sn_default_pci_unmap(struct pci_dev *pdev, dma_addr_t addr, int direction) | |
66 | { | |
67 | return; | |
68 | } | |
69 | ||
70 | static void * | |
7c2a6c62 | 71 | sn_default_pci_bus_fixup(struct pcibus_bussoft *soft, struct pci_controller *controller) |
e955d825 MM |
72 | { |
73 | return NULL; | |
74 | } | |
75 | ||
76 | static struct sn_pcibus_provider sn_pci_default_provider = { | |
77 | .dma_map = sn_default_pci_map, | |
78 | .dma_map_consistent = sn_default_pci_map, | |
79 | .dma_unmap = sn_default_pci_unmap, | |
80 | .bus_fixup = sn_default_pci_bus_fixup, | |
81 | }; | |
82 | ||
1da177e4 | 83 | /* |
6d6e4200 PB |
84 | * Retrieve the DMA Flush List given nasid, widget, and device. |
85 | * This list is needed to implement the WAR - Flush DMA data on PIO Reads. | |
1da177e4 | 86 | */ |
6d6e4200 PB |
87 | static inline u64 |
88 | sal_get_device_dmaflush_list(u64 nasid, u64 widget_num, u64 device_num, | |
89 | u64 address) | |
1da177e4 | 90 | { |
1da177e4 LT |
91 | struct ia64_sal_retval ret_stuff; |
92 | ret_stuff.status = 0; | |
93 | ret_stuff.v0 = 0; | |
94 | ||
95 | SAL_CALL_NOLOCK(ret_stuff, | |
6d6e4200 PB |
96 | (u64) SN_SAL_IOIF_GET_DEVICE_DMAFLUSH_LIST, |
97 | (u64) nasid, (u64) widget_num, | |
98 | (u64) device_num, (u64) address, 0, 0, 0); | |
99 | return ret_stuff.status; | |
1da177e4 LT |
100 | } |
101 | ||
102 | /* | |
103 | * Retrieve the hub device info structure for the given nasid. | |
104 | */ | |
6d6e4200 | 105 | static inline u64 sal_get_hubdev_info(u64 handle, u64 address) |
1da177e4 | 106 | { |
1da177e4 LT |
107 | struct ia64_sal_retval ret_stuff; |
108 | ret_stuff.status = 0; | |
109 | ret_stuff.v0 = 0; | |
110 | ||
111 | SAL_CALL_NOLOCK(ret_stuff, | |
112 | (u64) SN_SAL_IOIF_GET_HUBDEV_INFO, | |
113 | (u64) handle, (u64) address, 0, 0, 0, 0, 0); | |
114 | return ret_stuff.v0; | |
115 | } | |
116 | ||
117 | /* | |
118 | * Retrieve the pci bus information given the bus number. | |
119 | */ | |
6d6e4200 | 120 | static inline u64 sal_get_pcibus_info(u64 segment, u64 busnum, u64 address) |
1da177e4 | 121 | { |
1da177e4 LT |
122 | struct ia64_sal_retval ret_stuff; |
123 | ret_stuff.status = 0; | |
124 | ret_stuff.v0 = 0; | |
125 | ||
126 | SAL_CALL_NOLOCK(ret_stuff, | |
127 | (u64) SN_SAL_IOIF_GET_PCIBUS_INFO, | |
128 | (u64) segment, (u64) busnum, (u64) address, 0, 0, 0, 0); | |
129 | return ret_stuff.v0; | |
130 | } | |
131 | ||
132 | /* | |
133 | * Retrieve the pci device information given the bus and device|function number. | |
134 | */ | |
6d6e4200 | 135 | static inline u64 |
53493dcf PB |
136 | sal_get_pcidev_info(u64 segment, u64 bus_number, u64 devfn, u64 pci_dev, |
137 | u64 sn_irq_info) | |
1da177e4 LT |
138 | { |
139 | struct ia64_sal_retval ret_stuff; | |
140 | ret_stuff.status = 0; | |
141 | ret_stuff.v0 = 0; | |
142 | ||
143 | SAL_CALL_NOLOCK(ret_stuff, | |
144 | (u64) SN_SAL_IOIF_GET_PCIDEV_INFO, | |
53493dcf | 145 | (u64) segment, (u64) bus_number, (u64) devfn, |
1da177e4 LT |
146 | (u64) pci_dev, |
147 | sn_irq_info, 0, 0); | |
148 | return ret_stuff.v0; | |
149 | } | |
150 | ||
3ec829b6 JK |
151 | /* |
152 | * sn_pcidev_info_get() - Retrieve the pcidev_info struct for the specified | |
153 | * device. | |
154 | */ | |
155 | inline struct pcidev_info * | |
156 | sn_pcidev_info_get(struct pci_dev *dev) | |
157 | { | |
158 | struct pcidev_info *pcidev; | |
159 | ||
160 | list_for_each_entry(pcidev, | |
161 | &(SN_PCI_CONTROLLER(dev)->pcidev_info), pdi_list) { | |
162 | if (pcidev->pdi_linux_pcidev == dev) { | |
163 | return pcidev; | |
164 | } | |
165 | } | |
166 | return NULL; | |
167 | } | |
168 | ||
5f7f5b0c PB |
169 | /* Older PROM flush WAR |
170 | * | |
171 | * 01/16/06 -- This war will be in place until a new official PROM is released. | |
172 | * Additionally note that the struct sn_flush_device_war also has to be | |
173 | * removed from arch/ia64/sn/include/xtalk/hubdev.h | |
174 | */ | |
175 | static u8 war_implemented = 0; | |
176 | ||
61d67f2e PB |
177 | static s64 sn_device_fixup_war(u64 nasid, u64 widget, int device, |
178 | struct sn_flush_device_common *common) | |
5f7f5b0c PB |
179 | { |
180 | struct sn_flush_device_war *war_list; | |
181 | struct sn_flush_device_war *dev_entry; | |
182 | struct ia64_sal_retval isrv = {0,0,0,0}; | |
183 | ||
184 | if (!war_implemented) { | |
185 | printk(KERN_WARNING "PROM version < 4.50 -- implementing old " | |
186 | "PROM flush WAR\n"); | |
187 | war_implemented = 1; | |
188 | } | |
189 | ||
190 | war_list = kzalloc(DEV_PER_WIDGET * sizeof(*war_list), GFP_KERNEL); | |
191 | if (!war_list) | |
192 | BUG(); | |
193 | ||
194 | SAL_CALL_NOLOCK(isrv, SN_SAL_IOIF_GET_WIDGET_DMAFLUSH_LIST, | |
195 | nasid, widget, __pa(war_list), 0, 0, 0 ,0); | |
196 | if (isrv.status) | |
197 | panic("sn_device_fixup_war failed: %s\n", | |
198 | ia64_sal_strerror(isrv.status)); | |
199 | ||
200 | dev_entry = war_list + device; | |
201 | memcpy(common,dev_entry, sizeof(*common)); | |
5f7f5b0c | 202 | kfree(war_list); |
61d67f2e PB |
203 | |
204 | return isrv.status; | |
5f7f5b0c PB |
205 | } |
206 | ||
1da177e4 | 207 | /* |
5f7f5b0c | 208 | * sn_fixup_ionodes() - This routine initializes the HUB data strcuture for |
1da177e4 LT |
209 | * each node in the system. |
210 | */ | |
2fcc3db0 | 211 | static void __init sn_fixup_ionodes(void) |
1da177e4 | 212 | { |
6d6e4200 PB |
213 | struct sn_flush_device_kernel *sn_flush_device_kernel; |
214 | struct sn_flush_device_kernel *dev_entry; | |
1da177e4 | 215 | struct hubdev_info *hubdev; |
6d6e4200 PB |
216 | u64 status; |
217 | u64 nasid; | |
8ed9b2c7 | 218 | int i, widget, device, size; |
1da177e4 | 219 | |
674c6479 CN |
220 | /* |
221 | * Get SGI Specific HUB chipset information. | |
222 | * Inform Prom that this kernel can support domain bus numbering. | |
223 | */ | |
24ee0a6d | 224 | for (i = 0; i < num_cnodes; i++) { |
1da177e4 LT |
225 | hubdev = (struct hubdev_info *)(NODEPDA(i)->pdinfo); |
226 | nasid = cnodeid_to_nasid(i); | |
674c6479 CN |
227 | hubdev->max_segment_number = 0xffffffff; |
228 | hubdev->max_pcibus_number = 0xff; | |
6d6e4200 | 229 | status = sal_get_hubdev_info(nasid, (u64) __pa(hubdev)); |
1da177e4 LT |
230 | if (status) |
231 | continue; | |
232 | ||
674c6479 CN |
233 | /* Save the largest Domain and pcibus numbers found. */ |
234 | if (hubdev->max_segment_number) { | |
235 | /* | |
236 | * Dealing with a Prom that supports segments. | |
237 | */ | |
238 | max_segment_number = hubdev->max_segment_number; | |
239 | max_pcibus_number = hubdev->max_pcibus_number; | |
240 | } | |
241 | ||
c0b12422 CN |
242 | /* Attach the error interrupt handlers */ |
243 | if (nasid & 1) | |
244 | ice_error_init(hubdev); | |
245 | else | |
246 | hub_error_init(hubdev); | |
247 | ||
1da177e4 LT |
248 | for (widget = 0; widget <= HUB_WIDGET_ID_MAX; widget++) |
249 | hubdev->hdi_xwidget_info[widget].xwi_hubinfo = hubdev; | |
250 | ||
251 | if (!hubdev->hdi_flush_nasid_list.widget_p) | |
252 | continue; | |
253 | ||
8ed9b2c7 JS |
254 | size = (HUB_WIDGET_ID_MAX + 1) * |
255 | sizeof(struct sn_flush_device_kernel *); | |
1da177e4 | 256 | hubdev->hdi_flush_nasid_list.widget_p = |
8ed9b2c7 JS |
257 | kzalloc(size, GFP_KERNEL); |
258 | if (!hubdev->hdi_flush_nasid_list.widget_p) | |
259 | BUG(); | |
1da177e4 LT |
260 | |
261 | for (widget = 0; widget <= HUB_WIDGET_ID_MAX; widget++) { | |
8ed9b2c7 JS |
262 | size = DEV_PER_WIDGET * |
263 | sizeof(struct sn_flush_device_kernel); | |
264 | sn_flush_device_kernel = kzalloc(size, GFP_KERNEL); | |
6d6e4200 PB |
265 | if (!sn_flush_device_kernel) |
266 | BUG(); | |
6d6e4200 PB |
267 | |
268 | dev_entry = sn_flush_device_kernel; | |
269 | for (device = 0; device < DEV_PER_WIDGET; | |
270 | device++,dev_entry++) { | |
8ed9b2c7 JS |
271 | size = sizeof(struct sn_flush_device_common); |
272 | dev_entry->common = kzalloc(size, GFP_KERNEL); | |
6d6e4200 PB |
273 | if (!dev_entry->common) |
274 | BUG(); | |
6d6e4200 | 275 | |
61d67f2e PB |
276 | if (sn_prom_feature_available( |
277 | PRF_DEVICE_FLUSH_LIST)) | |
278 | status = sal_get_device_dmaflush_list( | |
8ed9b2c7 JS |
279 | nasid, widget, device, |
280 | (u64)(dev_entry->common)); | |
61d67f2e PB |
281 | else |
282 | status = sn_device_fixup_war(nasid, | |
8ed9b2c7 JS |
283 | widget, device, |
284 | dev_entry->common); | |
61d67f2e PB |
285 | if (status != SALRET_OK) |
286 | panic("SAL call failed: %s\n", | |
287 | ia64_sal_strerror(status)); | |
6d6e4200 PB |
288 | |
289 | spin_lock_init(&dev_entry->sfdl_flush_lock); | |
1da177e4 LT |
290 | } |
291 | ||
6d6e4200 PB |
292 | if (sn_flush_device_kernel) |
293 | hubdev->hdi_flush_nasid_list.widget_p[widget] = | |
294 | sn_flush_device_kernel; | |
295 | } | |
1da177e4 | 296 | } |
1da177e4 LT |
297 | } |
298 | ||
3ec829b6 JK |
299 | /* |
300 | * sn_pci_window_fixup() - Create a pci_window for each device resource. | |
301 | * Until ACPI support is added, we need this code | |
302 | * to setup pci_windows for use by | |
303 | * pcibios_bus_to_resource(), | |
304 | * pcibios_resource_to_bus(), etc. | |
305 | */ | |
306 | static void | |
307 | sn_pci_window_fixup(struct pci_dev *dev, unsigned int count, | |
53493dcf | 308 | s64 * pci_addrs) |
3ec829b6 JK |
309 | { |
310 | struct pci_controller *controller = PCI_CONTROLLER(dev->bus); | |
311 | unsigned int i; | |
312 | unsigned int idx; | |
313 | unsigned int new_count; | |
314 | struct pci_window *new_window; | |
315 | ||
316 | if (count == 0) | |
317 | return; | |
318 | idx = controller->windows; | |
319 | new_count = controller->windows + count; | |
320 | new_window = kcalloc(new_count, sizeof(struct pci_window), GFP_KERNEL); | |
321 | if (new_window == NULL) | |
322 | BUG(); | |
323 | if (controller->window) { | |
324 | memcpy(new_window, controller->window, | |
325 | sizeof(struct pci_window) * controller->windows); | |
326 | kfree(controller->window); | |
327 | } | |
328 | ||
329 | /* Setup a pci_window for each device resource. */ | |
330 | for (i = 0; i <= PCI_ROM_RESOURCE; i++) { | |
331 | if (pci_addrs[i] == -1) | |
332 | continue; | |
333 | ||
334 | new_window[idx].offset = dev->resource[i].start - pci_addrs[i]; | |
335 | new_window[idx].resource = dev->resource[i]; | |
336 | idx++; | |
337 | } | |
338 | ||
339 | controller->windows = new_count; | |
340 | controller->window = new_window; | |
341 | } | |
342 | ||
6f354b01 PB |
343 | void sn_pci_unfixup_slot(struct pci_dev *dev) |
344 | { | |
345 | struct pci_dev *host_pci_dev = SN_PCIDEV_INFO(dev)->host_pci_dev; | |
346 | ||
347 | sn_irq_unfixup(dev); | |
348 | pci_dev_put(host_pci_dev); | |
349 | pci_dev_put(dev); | |
350 | } | |
351 | ||
1da177e4 LT |
352 | /* |
353 | * sn_pci_fixup_slot() - This routine sets up a slot's resources | |
354 | * consistent with the Linux PCI abstraction layer. Resources acquired | |
355 | * from our PCI provider include PIO maps to BAR space and interrupt | |
356 | * objects. | |
357 | */ | |
c13cf371 | 358 | void sn_pci_fixup_slot(struct pci_dev *dev) |
1da177e4 | 359 | { |
3ec829b6 | 360 | unsigned int count = 0; |
1da177e4 | 361 | int idx; |
674c6479 | 362 | int segment = pci_domain_nr(dev->bus); |
1da177e4 | 363 | int status = 0; |
e955d825 | 364 | struct pcibus_bussoft *bs; |
cb4cb2cb PB |
365 | struct pci_bus *host_pci_bus; |
366 | struct pci_dev *host_pci_dev; | |
3ec829b6 | 367 | struct pcidev_info *pcidev_info; |
53493dcf | 368 | s64 pci_addrs[PCI_ROM_RESOURCE + 1]; |
cb4cb2cb PB |
369 | struct sn_irq_info *sn_irq_info; |
370 | unsigned long size; | |
371 | unsigned int bus_no, devfn; | |
1da177e4 | 372 | |
6f354b01 | 373 | pci_dev_get(dev); /* for the sysdata pointer */ |
3ec829b6 | 374 | pcidev_info = kzalloc(sizeof(struct pcidev_info), GFP_KERNEL); |
8ed9b2c7 | 375 | if (!pcidev_info) |
1da177e4 | 376 | BUG(); /* Cannot afford to run out of memory */ |
1da177e4 | 377 | |
8ed9b2c7 JS |
378 | sn_irq_info = kzalloc(sizeof(struct sn_irq_info), GFP_KERNEL); |
379 | if (!sn_irq_info) | |
1da177e4 | 380 | BUG(); /* Cannot afford to run out of memory */ |
1da177e4 LT |
381 | |
382 | /* Call to retrieve pci device information needed by kernel. */ | |
383 | status = sal_get_pcidev_info((u64) segment, (u64) dev->bus->number, | |
384 | dev->devfn, | |
3ec829b6 | 385 | (u64) __pa(pcidev_info), |
1da177e4 LT |
386 | (u64) __pa(sn_irq_info)); |
387 | if (status) | |
cb4cb2cb | 388 | BUG(); /* Cannot get platform pci device information */ |
1da177e4 | 389 | |
3ec829b6 JK |
390 | /* Add pcidev_info to list in sn_pci_controller struct */ |
391 | list_add_tail(&pcidev_info->pdi_list, | |
392 | &(SN_PCI_CONTROLLER(dev->bus)->pcidev_info)); | |
393 | ||
1da177e4 LT |
394 | /* Copy over PIO Mapped Addresses */ |
395 | for (idx = 0; idx <= PCI_ROM_RESOURCE; idx++) { | |
396 | unsigned long start, end, addr; | |
397 | ||
3ec829b6 JK |
398 | if (!pcidev_info->pdi_pio_mapped_addr[idx]) { |
399 | pci_addrs[idx] = -1; | |
1da177e4 | 400 | continue; |
3ec829b6 | 401 | } |
1da177e4 LT |
402 | |
403 | start = dev->resource[idx].start; | |
404 | end = dev->resource[idx].end; | |
405 | size = end - start; | |
3ec829b6 JK |
406 | if (size == 0) { |
407 | pci_addrs[idx] = -1; | |
408 | continue; | |
409 | } | |
410 | pci_addrs[idx] = start; | |
411 | count++; | |
412 | addr = pcidev_info->pdi_pio_mapped_addr[idx]; | |
1da177e4 LT |
413 | addr = ((addr << 4) >> 4) | __IA64_UNCACHED_OFFSET; |
414 | dev->resource[idx].start = addr; | |
415 | dev->resource[idx].end = addr + size; | |
416 | if (dev->resource[idx].flags & IORESOURCE_IO) | |
417 | dev->resource[idx].parent = &ioport_resource; | |
418 | else | |
419 | dev->resource[idx].parent = &iomem_resource; | |
420 | } | |
3ec829b6 JK |
421 | /* Create a pci_window in the pci_controller struct for |
422 | * each device resource. | |
423 | */ | |
424 | if (count > 0) | |
425 | sn_pci_window_fixup(dev, count, pci_addrs); | |
1da177e4 | 426 | |
6f354b01 PB |
427 | /* |
428 | * Using the PROMs values for the PCI host bus, get the Linux | |
cb4cb2cb PB |
429 | * PCI host_pci_dev struct and set up host bus linkages |
430 | */ | |
431 | ||
3ec829b6 JK |
432 | bus_no = (pcidev_info->pdi_slot_host_handle >> 32) & 0xff; |
433 | devfn = pcidev_info->pdi_slot_host_handle & 0xffffffff; | |
674c6479 | 434 | host_pci_bus = pci_find_bus(segment, bus_no); |
cb4cb2cb PB |
435 | host_pci_dev = pci_get_slot(host_pci_bus, devfn); |
436 | ||
3ec829b6 JK |
437 | pcidev_info->host_pci_dev = host_pci_dev; |
438 | pcidev_info->pdi_linux_pcidev = dev; | |
439 | pcidev_info->pdi_host_pcidev_info = SN_PCIDEV_INFO(host_pci_dev); | |
cb4cb2cb | 440 | bs = SN_PCIBUS_BUSSOFT(dev->bus); |
3ec829b6 | 441 | pcidev_info->pdi_pcibus_info = bs; |
e955d825 MM |
442 | |
443 | if (bs && bs->bs_asic_type < PCIIO_ASIC_MAX_TYPES) { | |
444 | SN_PCIDEV_BUSPROVIDER(dev) = sn_pci_provider[bs->bs_asic_type]; | |
445 | } else { | |
446 | SN_PCIDEV_BUSPROVIDER(dev) = &sn_pci_default_provider; | |
447 | } | |
1da177e4 LT |
448 | |
449 | /* Only set up IRQ stuff if this device has a host bus context */ | |
e955d825 | 450 | if (bs && sn_irq_info->irq_irq) { |
3ec829b6 JK |
451 | pcidev_info->pdi_sn_irq_info = sn_irq_info; |
452 | dev->irq = pcidev_info->pdi_sn_irq_info->irq_irq; | |
1da177e4 | 453 | sn_irq_fixup(dev, sn_irq_info); |
cb4cb2cb | 454 | } else { |
3ec829b6 | 455 | pcidev_info->pdi_sn_irq_info = NULL; |
cb4cb2cb | 456 | kfree(sn_irq_info); |
1da177e4 | 457 | } |
a80dcc0b MM |
458 | |
459 | /* | |
460 | * MSI currently not supported on altix. Remove this when | |
461 | * the MSI abstraction patches are integrated into the kernel | |
462 | * (sometime after 2.6.16 releases) | |
463 | */ | |
464 | dev->no_msi = 1; | |
1da177e4 LT |
465 | } |
466 | ||
467 | /* | |
468 | * sn_pci_controller_fixup() - This routine sets up a bus's resources | |
469 | * consistent with the Linux PCI abstraction layer. | |
470 | */ | |
6f354b01 | 471 | void sn_pci_controller_fixup(int segment, int busnum, struct pci_bus *bus) |
1da177e4 | 472 | { |
8ed9b2c7 | 473 | int status; |
1da177e4 | 474 | int nasid, cnode; |
1da177e4 | 475 | struct pci_controller *controller; |
3ec829b6 | 476 | struct sn_pci_controller *sn_controller; |
1da177e4 LT |
477 | struct pcibus_bussoft *prom_bussoft_ptr; |
478 | struct hubdev_info *hubdev_info; | |
8ed9b2c7 | 479 | void *provider_soft; |
e955d825 | 480 | struct sn_pcibus_provider *provider; |
1da177e4 | 481 | |
6f354b01 PB |
482 | status = sal_get_pcibus_info((u64) segment, (u64) busnum, |
483 | (u64) ia64_tpa(&prom_bussoft_ptr)); | |
484 | if (status > 0) | |
485 | return; /*bus # does not exist */ | |
1da177e4 | 486 | prom_bussoft_ptr = __va(prom_bussoft_ptr); |
1da177e4 | 487 | |
3ec829b6 JK |
488 | /* Allocate a sn_pci_controller, which has a pci_controller struct |
489 | * as the first member. | |
490 | */ | |
491 | sn_controller = kzalloc(sizeof(struct sn_pci_controller), GFP_KERNEL); | |
492 | if (!sn_controller) | |
493 | BUG(); | |
494 | INIT_LIST_HEAD(&sn_controller->pcidev_info); | |
495 | controller = &sn_controller->pci_controller; | |
674c6479 | 496 | controller->segment = segment; |
6f354b01 | 497 | |
1da177e4 | 498 | if (bus == NULL) { |
6f354b01 PB |
499 | bus = pci_scan_bus(busnum, &pci_root_ops, controller); |
500 | if (bus == NULL) | |
c1ffb910 | 501 | goto error_return; /* error, or bus already scanned */ |
6f354b01 | 502 | bus->sysdata = NULL; |
1da177e4 LT |
503 | } |
504 | ||
6f354b01 PB |
505 | if (bus->sysdata) |
506 | goto error_return; /* sysdata already alloc'd */ | |
507 | ||
1da177e4 LT |
508 | /* |
509 | * Per-provider fixup. Copies the contents from prom to local | |
510 | * area and links SN_PCIBUS_BUSSOFT(). | |
1da177e4 LT |
511 | */ |
512 | ||
6f354b01 | 513 | if (prom_bussoft_ptr->bs_asic_type >= PCIIO_ASIC_MAX_TYPES) |
c1ffb910 | 514 | goto error_return; /* unsupported asic type */ |
6f354b01 PB |
515 | |
516 | if (prom_bussoft_ptr->bs_asic_type == PCIIO_ASIC_TYPE_PPB) | |
517 | goto error_return; /* no further fixup necessary */ | |
e955d825 MM |
518 | |
519 | provider = sn_pci_provider[prom_bussoft_ptr->bs_asic_type]; | |
6f354b01 | 520 | if (provider == NULL) |
c1ffb910 | 521 | goto error_return; /* no provider registerd for this asic */ |
e955d825 | 522 | |
c1ffb910 | 523 | bus->sysdata = controller; |
6f354b01 | 524 | if (provider->bus_fixup) |
7c2a6c62 | 525 | provider_soft = (*provider->bus_fixup) (prom_bussoft_ptr, controller); |
8ed9b2c7 JS |
526 | else |
527 | provider_soft = NULL; | |
1da177e4 | 528 | |
c1ffb910 PB |
529 | if (provider_soft == NULL) { |
530 | /* fixup failed or not applicable */ | |
531 | bus->sysdata = NULL; | |
532 | goto error_return; | |
533 | } | |
1da177e4 | 534 | |
3ec829b6 JK |
535 | /* |
536 | * Setup pci_windows for legacy IO and MEM space. | |
537 | * (Temporary until ACPI support is in place.) | |
538 | */ | |
539 | controller->window = kcalloc(2, sizeof(struct pci_window), GFP_KERNEL); | |
540 | if (controller->window == NULL) | |
541 | BUG(); | |
542 | controller->window[0].offset = prom_bussoft_ptr->bs_legacy_io; | |
543 | controller->window[0].resource.name = "legacy_io"; | |
544 | controller->window[0].resource.flags = IORESOURCE_IO; | |
545 | controller->window[0].resource.start = prom_bussoft_ptr->bs_legacy_io; | |
546 | controller->window[0].resource.end = | |
547 | controller->window[0].resource.start + 0xffff; | |
548 | controller->window[0].resource.parent = &ioport_resource; | |
549 | controller->window[1].offset = prom_bussoft_ptr->bs_legacy_mem; | |
550 | controller->window[1].resource.name = "legacy_mem"; | |
551 | controller->window[1].resource.flags = IORESOURCE_MEM; | |
552 | controller->window[1].resource.start = prom_bussoft_ptr->bs_legacy_mem; | |
553 | controller->window[1].resource.end = | |
554 | controller->window[1].resource.start + (1024 * 1024) - 1; | |
555 | controller->window[1].resource.parent = &iomem_resource; | |
556 | controller->windows = 2; | |
557 | ||
1da177e4 LT |
558 | /* |
559 | * Generic bus fixup goes here. Don't reference prom_bussoft_ptr | |
560 | * after this point. | |
561 | */ | |
562 | ||
1da177e4 | 563 | PCI_CONTROLLER(bus)->platform_data = provider_soft; |
1da177e4 LT |
564 | nasid = NASID_GET(SN_PCIBUS_BUSSOFT(bus)->bs_base); |
565 | cnode = nasid_to_cnodeid(nasid); | |
566 | hubdev_info = (struct hubdev_info *)(NODEPDA(cnode)->pdinfo); | |
567 | SN_PCIBUS_BUSSOFT(bus)->bs_xwidget_info = | |
568 | &(hubdev_info->hdi_xwidget_info[SN_PCIBUS_BUSSOFT(bus)->bs_xid]); | |
6f354b01 | 569 | |
7c2a6c62 CL |
570 | /* |
571 | * If the node information we obtained during the fixup phase is invalid | |
572 | * then set controller->node to -1 (undetermined) | |
573 | */ | |
574 | if (controller->node >= num_online_nodes()) { | |
575 | struct pcibus_bussoft *b = SN_PCIBUS_BUSSOFT(bus); | |
576 | ||
674c6479 | 577 | printk(KERN_WARNING "Device ASIC=%u XID=%u PBUSNUM=%u" |
7c2a6c62 CL |
578 | "L_IO=%lx L_MEM=%lx BASE=%lx\n", |
579 | b->bs_asic_type, b->bs_xid, b->bs_persist_busnum, | |
580 | b->bs_legacy_io, b->bs_legacy_mem, b->bs_base); | |
581 | printk(KERN_WARNING "on node %d but only %d nodes online." | |
582 | "Association set to undetermined.\n", | |
583 | controller->node, num_online_nodes()); | |
584 | controller->node = -1; | |
585 | } | |
6f354b01 PB |
586 | return; |
587 | ||
588 | error_return: | |
589 | ||
3ec829b6 | 590 | kfree(sn_controller); |
6f354b01 PB |
591 | return; |
592 | } | |
593 | ||
594 | void sn_bus_store_sysdata(struct pci_dev *dev) | |
595 | { | |
596 | struct sysdata_el *element; | |
597 | ||
f96cb1f0 | 598 | element = kzalloc(sizeof(struct sysdata_el), GFP_KERNEL); |
6f354b01 PB |
599 | if (!element) { |
600 | dev_dbg(dev, "%s: out of memory!\n", __FUNCTION__); | |
601 | return; | |
602 | } | |
3ec829b6 | 603 | element->sysdata = SN_PCIDEV_INFO(dev); |
6f354b01 PB |
604 | list_add(&element->entry, &sn_sysdata_list); |
605 | } | |
606 | ||
607 | void sn_bus_free_sysdata(void) | |
608 | { | |
609 | struct sysdata_el *element; | |
b6bb7618 | 610 | struct list_head *list, *safe; |
6f354b01 | 611 | |
b6bb7618 | 612 | list_for_each_safe(list, safe, &sn_sysdata_list) { |
6f354b01 PB |
613 | element = list_entry(list, struct sysdata_el, entry); |
614 | list_del(&element->entry); | |
8b34ff42 PB |
615 | list_del(&(((struct pcidev_info *) |
616 | (element->sysdata))->pdi_list)); | |
6f354b01 PB |
617 | kfree(element->sysdata); |
618 | kfree(element); | |
6f354b01 PB |
619 | } |
620 | return; | |
1da177e4 LT |
621 | } |
622 | ||
623 | /* | |
624 | * Ugly hack to get PCI setup until we have a proper ACPI namespace. | |
625 | */ | |
626 | ||
627 | #define PCI_BUSES_TO_SCAN 256 | |
628 | ||
629 | static int __init sn_pci_init(void) | |
630 | { | |
8ed9b2c7 | 631 | int i, j; |
1da177e4 | 632 | struct pci_dev *pci_dev = NULL; |
1da177e4 | 633 | |
71a5d027 | 634 | if (!ia64_platform_is("sn2") || IS_RUNNING_ON_FAKE_PROM()) |
1da177e4 LT |
635 | return 0; |
636 | ||
e955d825 MM |
637 | /* |
638 | * prime sn_pci_provider[]. Individial provider init routines will | |
639 | * override their respective default entries. | |
640 | */ | |
641 | ||
642 | for (i = 0; i < PCIIO_ASIC_MAX_TYPES; i++) | |
643 | sn_pci_provider[i] = &sn_pci_default_provider; | |
644 | ||
645 | pcibr_init_provider(); | |
9c90bdde | 646 | tioca_init_provider(); |
c9221da9 | 647 | tioce_init_provider(); |
e955d825 | 648 | |
1da177e4 LT |
649 | /* |
650 | * This is needed to avoid bounce limit checks in the blk layer | |
651 | */ | |
652 | ia64_max_iommu_merge_mask = ~PAGE_MASK; | |
653 | sn_fixup_ionodes(); | |
cb4cb2cb | 654 | sn_irq_lh_init(); |
6f354b01 | 655 | INIT_LIST_HEAD(&sn_sysdata_list); |
1da177e4 LT |
656 | sn_init_cpei_timer(); |
657 | ||
658 | #ifdef CONFIG_PROC_FS | |
659 | register_sn_procfs(); | |
660 | #endif | |
661 | ||
6f354b01 | 662 | /* busses are not known yet ... */ |
674c6479 CN |
663 | for (i = 0; i <= max_segment_number; i++) |
664 | for (j = 0; j <= max_pcibus_number; j++) | |
665 | sn_pci_controller_fixup(i, j, NULL); | |
1da177e4 LT |
666 | |
667 | /* | |
668 | * Generic Linux PCI Layer has created the pci_bus and pci_dev | |
669 | * structures - time for us to add our SN PLatform specific | |
670 | * information. | |
671 | */ | |
672 | ||
673 | while ((pci_dev = | |
6f354b01 | 674 | pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pci_dev)) != NULL) |
1da177e4 | 675 | sn_pci_fixup_slot(pci_dev); |
1da177e4 LT |
676 | |
677 | sn_ioif_inited = 1; /* sn I/O infrastructure now initialized */ | |
678 | ||
679 | return 0; | |
680 | } | |
681 | ||
682 | /* | |
683 | * hubdev_init_node() - Creates the HUB data structure and link them to it's | |
684 | * own NODE specific data area. | |
685 | */ | |
686 | void hubdev_init_node(nodepda_t * npda, cnodeid_t node) | |
687 | { | |
1da177e4 | 688 | struct hubdev_info *hubdev_info; |
8ed9b2c7 JS |
689 | int size; |
690 | pg_data_t *pg; | |
691 | ||
692 | size = sizeof(struct hubdev_info); | |
1da177e4 LT |
693 | |
694 | if (node >= num_online_nodes()) /* Headless/memless IO nodes */ | |
8ed9b2c7 | 695 | pg = NODE_DATA(0); |
1da177e4 | 696 | else |
8ed9b2c7 | 697 | pg = NODE_DATA(node); |
1da177e4 | 698 | |
8ed9b2c7 JS |
699 | hubdev_info = (struct hubdev_info *)alloc_bootmem_node(pg, size); |
700 | ||
701 | npda->pdinfo = (void *)hubdev_info; | |
1da177e4 LT |
702 | } |
703 | ||
704 | geoid_t | |
705 | cnodeid_get_geoid(cnodeid_t cnode) | |
706 | { | |
1da177e4 LT |
707 | struct hubdev_info *hubdev; |
708 | ||
709 | hubdev = (struct hubdev_info *)(NODEPDA(cnode)->pdinfo); | |
710 | return hubdev->hdi_geoid; | |
1da177e4 LT |
711 | } |
712 | ||
713 | subsys_initcall(sn_pci_init); | |
6f354b01 PB |
714 | EXPORT_SYMBOL(sn_pci_fixup_slot); |
715 | EXPORT_SYMBOL(sn_pci_unfixup_slot); | |
716 | EXPORT_SYMBOL(sn_pci_controller_fixup); | |
717 | EXPORT_SYMBOL(sn_bus_store_sysdata); | |
718 | EXPORT_SYMBOL(sn_bus_free_sysdata); |