]>
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 | * | |
6 | * Copyright (C) 1992 - 1997, 2000-2004 Silicon Graphics, Inc. All rights reserved. | |
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> |
1da177e4 | 13 | #include <asm/sn/geo.h> |
1da177e4 | 14 | #include <asm/sn/io.h> |
c13cf371 PB |
15 | #include <asm/sn/pcibr_provider.h> |
16 | #include <asm/sn/pcibus_provider_defs.h> | |
17 | #include <asm/sn/pcidev.h> | |
1da177e4 | 18 | #include <asm/sn/simulator.h> |
c13cf371 | 19 | #include <asm/sn/sn_sal.h> |
9c90bdde | 20 | #include <asm/sn/tioca_provider.h> |
c13cf371 PB |
21 | #include "xtalk/hubdev.h" |
22 | #include "xtalk/xwidgetdev.h" | |
1da177e4 | 23 | |
1da177e4 LT |
24 | nasid_t master_nasid = INVALID_NASID; /* Partition Master */ |
25 | ||
26 | struct slab_info { | |
27 | struct hubdev_info hubdev; | |
28 | }; | |
29 | ||
30 | struct brick { | |
31 | moduleid_t id; /* Module ID of this module */ | |
32 | struct slab_info slab_info[MAX_SLABS + 1]; | |
33 | }; | |
34 | ||
35 | int sn_ioif_inited = 0; /* SN I/O infrastructure initialized? */ | |
36 | ||
e955d825 MM |
37 | struct sn_pcibus_provider *sn_pci_provider[PCIIO_ASIC_MAX_TYPES]; /* indexed by asic type */ |
38 | ||
39 | /* | |
40 | * Hooks and struct for unsupported pci providers | |
41 | */ | |
42 | ||
43 | static dma_addr_t | |
44 | sn_default_pci_map(struct pci_dev *pdev, unsigned long paddr, size_t size) | |
45 | { | |
46 | return 0; | |
47 | } | |
48 | ||
49 | static void | |
50 | sn_default_pci_unmap(struct pci_dev *pdev, dma_addr_t addr, int direction) | |
51 | { | |
52 | return; | |
53 | } | |
54 | ||
55 | static void * | |
56 | sn_default_pci_bus_fixup(struct pcibus_bussoft *soft) | |
57 | { | |
58 | return NULL; | |
59 | } | |
60 | ||
61 | static struct sn_pcibus_provider sn_pci_default_provider = { | |
62 | .dma_map = sn_default_pci_map, | |
63 | .dma_map_consistent = sn_default_pci_map, | |
64 | .dma_unmap = sn_default_pci_unmap, | |
65 | .bus_fixup = sn_default_pci_bus_fixup, | |
66 | }; | |
67 | ||
1da177e4 LT |
68 | /* |
69 | * Retrieve the DMA Flush List given nasid. This list is needed | |
70 | * to implement the WAR - Flush DMA data on PIO Reads. | |
71 | */ | |
72 | static inline uint64_t | |
73 | sal_get_widget_dmaflush_list(u64 nasid, u64 widget_num, u64 address) | |
74 | { | |
75 | ||
76 | struct ia64_sal_retval ret_stuff; | |
77 | ret_stuff.status = 0; | |
78 | ret_stuff.v0 = 0; | |
79 | ||
80 | SAL_CALL_NOLOCK(ret_stuff, | |
81 | (u64) SN_SAL_IOIF_GET_WIDGET_DMAFLUSH_LIST, | |
82 | (u64) nasid, (u64) widget_num, (u64) address, 0, 0, 0, | |
83 | 0); | |
84 | return ret_stuff.v0; | |
85 | ||
86 | } | |
87 | ||
88 | /* | |
89 | * Retrieve the hub device info structure for the given nasid. | |
90 | */ | |
91 | static inline uint64_t sal_get_hubdev_info(u64 handle, u64 address) | |
92 | { | |
93 | ||
94 | struct ia64_sal_retval ret_stuff; | |
95 | ret_stuff.status = 0; | |
96 | ret_stuff.v0 = 0; | |
97 | ||
98 | SAL_CALL_NOLOCK(ret_stuff, | |
99 | (u64) SN_SAL_IOIF_GET_HUBDEV_INFO, | |
100 | (u64) handle, (u64) address, 0, 0, 0, 0, 0); | |
101 | return ret_stuff.v0; | |
102 | } | |
103 | ||
104 | /* | |
105 | * Retrieve the pci bus information given the bus number. | |
106 | */ | |
107 | static inline uint64_t sal_get_pcibus_info(u64 segment, u64 busnum, u64 address) | |
108 | { | |
109 | ||
110 | struct ia64_sal_retval ret_stuff; | |
111 | ret_stuff.status = 0; | |
112 | ret_stuff.v0 = 0; | |
113 | ||
114 | SAL_CALL_NOLOCK(ret_stuff, | |
115 | (u64) SN_SAL_IOIF_GET_PCIBUS_INFO, | |
116 | (u64) segment, (u64) busnum, (u64) address, 0, 0, 0, 0); | |
117 | return ret_stuff.v0; | |
118 | } | |
119 | ||
120 | /* | |
121 | * Retrieve the pci device information given the bus and device|function number. | |
122 | */ | |
123 | static inline uint64_t | |
124 | sal_get_pcidev_info(u64 segment, u64 bus_number, u64 devfn, u64 pci_dev, | |
125 | u64 sn_irq_info) | |
126 | { | |
127 | struct ia64_sal_retval ret_stuff; | |
128 | ret_stuff.status = 0; | |
129 | ret_stuff.v0 = 0; | |
130 | ||
131 | SAL_CALL_NOLOCK(ret_stuff, | |
132 | (u64) SN_SAL_IOIF_GET_PCIDEV_INFO, | |
133 | (u64) segment, (u64) bus_number, (u64) devfn, | |
134 | (u64) pci_dev, | |
135 | sn_irq_info, 0, 0); | |
136 | return ret_stuff.v0; | |
137 | } | |
138 | ||
139 | /* | |
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 | |
158 | * each node in the system. | |
159 | */ | |
160 | static void sn_fixup_ionodes(void) | |
161 | { | |
162 | ||
163 | struct sn_flush_device_list *sn_flush_device_list; | |
164 | struct hubdev_info *hubdev; | |
165 | uint64_t status; | |
166 | uint64_t nasid; | |
167 | int i, widget; | |
168 | ||
169 | for (i = 0; i < numionodes; i++) { | |
170 | hubdev = (struct hubdev_info *)(NODEPDA(i)->pdinfo); | |
171 | nasid = cnodeid_to_nasid(i); | |
172 | status = sal_get_hubdev_info(nasid, (uint64_t) __pa(hubdev)); | |
173 | if (status) | |
174 | continue; | |
175 | ||
c0b12422 CN |
176 | /* Attach the error interrupt handlers */ |
177 | if (nasid & 1) | |
178 | ice_error_init(hubdev); | |
179 | else | |
180 | hub_error_init(hubdev); | |
181 | ||
1da177e4 LT |
182 | for (widget = 0; widget <= HUB_WIDGET_ID_MAX; widget++) |
183 | hubdev->hdi_xwidget_info[widget].xwi_hubinfo = hubdev; | |
184 | ||
185 | if (!hubdev->hdi_flush_nasid_list.widget_p) | |
186 | continue; | |
187 | ||
188 | hubdev->hdi_flush_nasid_list.widget_p = | |
189 | kmalloc((HUB_WIDGET_ID_MAX + 1) * | |
190 | sizeof(struct sn_flush_device_list *), GFP_KERNEL); | |
191 | ||
192 | memset(hubdev->hdi_flush_nasid_list.widget_p, 0x0, | |
193 | (HUB_WIDGET_ID_MAX + 1) * | |
194 | sizeof(struct sn_flush_device_list *)); | |
195 | ||
196 | for (widget = 0; widget <= HUB_WIDGET_ID_MAX; widget++) { | |
197 | sn_flush_device_list = kmalloc(DEV_PER_WIDGET * | |
198 | sizeof(struct | |
199 | sn_flush_device_list), | |
200 | GFP_KERNEL); | |
201 | memset(sn_flush_device_list, 0x0, | |
202 | DEV_PER_WIDGET * | |
203 | sizeof(struct sn_flush_device_list)); | |
204 | ||
205 | status = | |
206 | sal_get_widget_dmaflush_list(nasid, widget, | |
207 | (uint64_t) | |
208 | __pa | |
209 | (sn_flush_device_list)); | |
210 | if (status) { | |
211 | kfree(sn_flush_device_list); | |
212 | continue; | |
213 | } | |
214 | ||
215 | hubdev->hdi_flush_nasid_list.widget_p[widget] = | |
216 | sn_flush_device_list; | |
217 | } | |
218 | ||
1da177e4 LT |
219 | } |
220 | ||
221 | } | |
222 | ||
223 | /* | |
224 | * sn_pci_fixup_slot() - This routine sets up a slot's resources | |
225 | * consistent with the Linux PCI abstraction layer. Resources acquired | |
226 | * from our PCI provider include PIO maps to BAR space and interrupt | |
227 | * objects. | |
228 | */ | |
c13cf371 | 229 | void sn_pci_fixup_slot(struct pci_dev *dev) |
1da177e4 LT |
230 | { |
231 | int idx; | |
232 | int segment = 0; | |
1da177e4 | 233 | int status = 0; |
e955d825 | 234 | struct pcibus_bussoft *bs; |
cb4cb2cb PB |
235 | struct pci_bus *host_pci_bus; |
236 | struct pci_dev *host_pci_dev; | |
237 | struct sn_irq_info *sn_irq_info; | |
238 | unsigned long size; | |
239 | unsigned int bus_no, devfn; | |
1da177e4 LT |
240 | |
241 | dev->sysdata = kmalloc(sizeof(struct pcidev_info), GFP_KERNEL); | |
242 | if (SN_PCIDEV_INFO(dev) <= 0) | |
243 | BUG(); /* Cannot afford to run out of memory */ | |
244 | memset(SN_PCIDEV_INFO(dev), 0, sizeof(struct pcidev_info)); | |
245 | ||
246 | sn_irq_info = kmalloc(sizeof(struct sn_irq_info), GFP_KERNEL); | |
247 | if (sn_irq_info <= 0) | |
248 | BUG(); /* Cannot afford to run out of memory */ | |
249 | memset(sn_irq_info, 0, sizeof(struct sn_irq_info)); | |
250 | ||
251 | /* Call to retrieve pci device information needed by kernel. */ | |
252 | status = sal_get_pcidev_info((u64) segment, (u64) dev->bus->number, | |
253 | dev->devfn, | |
254 | (u64) __pa(SN_PCIDEV_INFO(dev)), | |
255 | (u64) __pa(sn_irq_info)); | |
256 | if (status) | |
cb4cb2cb | 257 | BUG(); /* Cannot get platform pci device information */ |
1da177e4 LT |
258 | |
259 | /* Copy over PIO Mapped Addresses */ | |
260 | for (idx = 0; idx <= PCI_ROM_RESOURCE; idx++) { | |
261 | unsigned long start, end, addr; | |
262 | ||
263 | if (!SN_PCIDEV_INFO(dev)->pdi_pio_mapped_addr[idx]) | |
264 | continue; | |
265 | ||
266 | start = dev->resource[idx].start; | |
267 | end = dev->resource[idx].end; | |
268 | size = end - start; | |
269 | addr = SN_PCIDEV_INFO(dev)->pdi_pio_mapped_addr[idx]; | |
270 | addr = ((addr << 4) >> 4) | __IA64_UNCACHED_OFFSET; | |
271 | dev->resource[idx].start = addr; | |
272 | dev->resource[idx].end = addr + size; | |
273 | if (dev->resource[idx].flags & IORESOURCE_IO) | |
274 | dev->resource[idx].parent = &ioport_resource; | |
275 | else | |
276 | dev->resource[idx].parent = &iomem_resource; | |
277 | } | |
278 | ||
cb4cb2cb PB |
279 | /* Using the PROMs values for the PCI host bus, get the Linux |
280 | * PCI host_pci_dev struct and set up host bus linkages | |
281 | */ | |
282 | ||
283 | bus_no = SN_PCIDEV_INFO(dev)->pdi_slot_host_handle >> 32; | |
284 | devfn = SN_PCIDEV_INFO(dev)->pdi_slot_host_handle & 0xffffffff; | |
285 | host_pci_bus = pci_find_bus(pci_domain_nr(dev->bus), bus_no); | |
286 | host_pci_dev = pci_get_slot(host_pci_bus, devfn); | |
287 | ||
288 | SN_PCIDEV_INFO(dev)->host_pci_dev = host_pci_dev; | |
1da177e4 | 289 | SN_PCIDEV_INFO(dev)->pdi_host_pcidev_info = |
cb4cb2cb | 290 | SN_PCIDEV_INFO(host_pci_dev); |
1da177e4 | 291 | SN_PCIDEV_INFO(dev)->pdi_linux_pcidev = dev; |
cb4cb2cb | 292 | bs = SN_PCIBUS_BUSSOFT(dev->bus); |
e955d825 MM |
293 | SN_PCIDEV_INFO(dev)->pdi_pcibus_info = bs; |
294 | ||
295 | if (bs && bs->bs_asic_type < PCIIO_ASIC_MAX_TYPES) { | |
296 | SN_PCIDEV_BUSPROVIDER(dev) = sn_pci_provider[bs->bs_asic_type]; | |
297 | } else { | |
298 | SN_PCIDEV_BUSPROVIDER(dev) = &sn_pci_default_provider; | |
299 | } | |
1da177e4 LT |
300 | |
301 | /* Only set up IRQ stuff if this device has a host bus context */ | |
e955d825 | 302 | if (bs && sn_irq_info->irq_irq) { |
1da177e4 LT |
303 | SN_PCIDEV_INFO(dev)->pdi_sn_irq_info = sn_irq_info; |
304 | dev->irq = SN_PCIDEV_INFO(dev)->pdi_sn_irq_info->irq_irq; | |
305 | sn_irq_fixup(dev, sn_irq_info); | |
cb4cb2cb PB |
306 | } else { |
307 | SN_PCIDEV_INFO(dev)->pdi_sn_irq_info = NULL; | |
308 | kfree(sn_irq_info); | |
1da177e4 LT |
309 | } |
310 | } | |
311 | ||
312 | /* | |
313 | * sn_pci_controller_fixup() - This routine sets up a bus's resources | |
314 | * consistent with the Linux PCI abstraction layer. | |
315 | */ | |
316 | static void sn_pci_controller_fixup(int segment, int busnum) | |
317 | { | |
318 | int status = 0; | |
319 | int nasid, cnode; | |
320 | struct pci_bus *bus; | |
321 | struct pci_controller *controller; | |
322 | struct pcibus_bussoft *prom_bussoft_ptr; | |
323 | struct hubdev_info *hubdev_info; | |
324 | void *provider_soft; | |
e955d825 | 325 | struct sn_pcibus_provider *provider; |
1da177e4 LT |
326 | |
327 | status = | |
328 | sal_get_pcibus_info((u64) segment, (u64) busnum, | |
329 | (u64) ia64_tpa(&prom_bussoft_ptr)); | |
330 | if (status > 0) { | |
331 | return; /* bus # does not exist */ | |
332 | } | |
333 | ||
334 | 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 | ||
338 | bus = pci_scan_bus(busnum, &pci_root_ops, controller); | |
339 | if (bus == NULL) { | |
340 | return; /* error, or bus already scanned */ | |
341 | } | |
342 | ||
343 | /* | |
344 | * Per-provider fixup. Copies the contents from prom to local | |
345 | * area and links SN_PCIBUS_BUSSOFT(). | |
1da177e4 LT |
346 | */ |
347 | ||
e955d825 MM |
348 | if (prom_bussoft_ptr->bs_asic_type >= PCIIO_ASIC_MAX_TYPES) { |
349 | return; /* unsupported asic type */ | |
350 | } | |
351 | ||
352 | provider = sn_pci_provider[prom_bussoft_ptr->bs_asic_type]; | |
353 | if (provider == NULL) { | |
354 | return; /* no provider registerd for this asic */ | |
355 | } | |
356 | ||
357 | provider_soft = NULL; | |
358 | if (provider->bus_fixup) { | |
359 | provider_soft = (*provider->bus_fixup) (prom_bussoft_ptr); | |
1da177e4 LT |
360 | } |
361 | ||
1da177e4 LT |
362 | if (provider_soft == NULL) { |
363 | return; /* fixup failed or not applicable */ | |
364 | } | |
365 | ||
366 | /* | |
367 | * Generic bus fixup goes here. Don't reference prom_bussoft_ptr | |
368 | * after this point. | |
369 | */ | |
370 | ||
371 | bus->sysdata = controller; | |
372 | PCI_CONTROLLER(bus)->platform_data = provider_soft; | |
373 | ||
374 | nasid = NASID_GET(SN_PCIBUS_BUSSOFT(bus)->bs_base); | |
375 | cnode = nasid_to_cnodeid(nasid); | |
376 | hubdev_info = (struct hubdev_info *)(NODEPDA(cnode)->pdinfo); | |
377 | SN_PCIBUS_BUSSOFT(bus)->bs_xwidget_info = | |
378 | &(hubdev_info->hdi_xwidget_info[SN_PCIBUS_BUSSOFT(bus)->bs_xid]); | |
379 | } | |
380 | ||
381 | /* | |
382 | * Ugly hack to get PCI setup until we have a proper ACPI namespace. | |
383 | */ | |
384 | ||
385 | #define PCI_BUSES_TO_SCAN 256 | |
386 | ||
387 | static int __init sn_pci_init(void) | |
388 | { | |
389 | int i = 0; | |
390 | struct pci_dev *pci_dev = NULL; | |
391 | extern void sn_init_cpei_timer(void); | |
392 | #ifdef CONFIG_PROC_FS | |
393 | extern void register_sn_procfs(void); | |
394 | #endif | |
395 | ||
71a5d027 | 396 | if (!ia64_platform_is("sn2") || IS_RUNNING_ON_FAKE_PROM()) |
1da177e4 LT |
397 | return 0; |
398 | ||
e955d825 MM |
399 | /* |
400 | * prime sn_pci_provider[]. Individial provider init routines will | |
401 | * override their respective default entries. | |
402 | */ | |
403 | ||
404 | for (i = 0; i < PCIIO_ASIC_MAX_TYPES; i++) | |
405 | sn_pci_provider[i] = &sn_pci_default_provider; | |
406 | ||
407 | pcibr_init_provider(); | |
9c90bdde | 408 | tioca_init_provider(); |
e955d825 | 409 | |
1da177e4 LT |
410 | /* |
411 | * This is needed to avoid bounce limit checks in the blk layer | |
412 | */ | |
413 | ia64_max_iommu_merge_mask = ~PAGE_MASK; | |
414 | sn_fixup_ionodes(); | |
cb4cb2cb | 415 | sn_irq_lh_init(); |
1da177e4 LT |
416 | sn_init_cpei_timer(); |
417 | ||
418 | #ifdef CONFIG_PROC_FS | |
419 | register_sn_procfs(); | |
420 | #endif | |
421 | ||
422 | for (i = 0; i < PCI_BUSES_TO_SCAN; i++) { | |
423 | sn_pci_controller_fixup(0, i); | |
424 | } | |
425 | ||
426 | /* | |
427 | * Generic Linux PCI Layer has created the pci_bus and pci_dev | |
428 | * structures - time for us to add our SN PLatform specific | |
429 | * information. | |
430 | */ | |
431 | ||
432 | while ((pci_dev = | |
433 | pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pci_dev)) != NULL) { | |
434 | sn_pci_fixup_slot(pci_dev); | |
435 | } | |
436 | ||
437 | sn_ioif_inited = 1; /* sn I/O infrastructure now initialized */ | |
438 | ||
439 | return 0; | |
440 | } | |
441 | ||
442 | /* | |
443 | * hubdev_init_node() - Creates the HUB data structure and link them to it's | |
444 | * own NODE specific data area. | |
445 | */ | |
446 | void hubdev_init_node(nodepda_t * npda, cnodeid_t node) | |
447 | { | |
448 | ||
449 | struct hubdev_info *hubdev_info; | |
450 | ||
451 | if (node >= num_online_nodes()) /* Headless/memless IO nodes */ | |
452 | hubdev_info = | |
453 | (struct hubdev_info *)alloc_bootmem_node(NODE_DATA(0), | |
454 | sizeof(struct | |
455 | hubdev_info)); | |
456 | else | |
457 | hubdev_info = | |
458 | (struct hubdev_info *)alloc_bootmem_node(NODE_DATA(node), | |
459 | sizeof(struct | |
460 | hubdev_info)); | |
461 | npda->pdinfo = (void *)hubdev_info; | |
462 | ||
463 | } | |
464 | ||
465 | geoid_t | |
466 | cnodeid_get_geoid(cnodeid_t cnode) | |
467 | { | |
468 | ||
469 | struct hubdev_info *hubdev; | |
470 | ||
471 | hubdev = (struct hubdev_info *)(NODEPDA(cnode)->pdinfo); | |
472 | return hubdev->hdi_geoid; | |
473 | ||
474 | } | |
475 | ||
476 | subsys_initcall(sn_pci_init); |