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