diff options
Diffstat (limited to 'arch/ia64/sn/kernel/io_init.c')
-rw-r--r-- | arch/ia64/sn/kernel/io_init.c | 411 |
1 files changed, 411 insertions, 0 deletions
diff --git a/arch/ia64/sn/kernel/io_init.c b/arch/ia64/sn/kernel/io_init.c new file mode 100644 index 00000000000..001880812b7 --- /dev/null +++ b/arch/ia64/sn/kernel/io_init.c | |||
@@ -0,0 +1,411 @@ | |||
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> | ||
12 | #include <asm/sn/sn_sal.h> | ||
13 | #include <asm/sn/addrs.h> | ||
14 | #include "pci/pcibus_provider_defs.h" | ||
15 | #include "pci/pcidev.h" | ||
16 | #include "pci/pcibr_provider.h" | ||
17 | #include "xtalk/xwidgetdev.h" | ||
18 | #include <asm/sn/geo.h> | ||
19 | #include "xtalk/hubdev.h" | ||
20 | #include <asm/sn/io.h> | ||
21 | #include <asm/sn/simulator.h> | ||
22 | |||
23 | char master_baseio_wid; | ||
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 | |||
37 | /* | ||
38 | * Retrieve the DMA Flush List given nasid. This list is needed | ||
39 | * to implement the WAR - Flush DMA data on PIO Reads. | ||
40 | */ | ||
41 | static inline uint64_t | ||
42 | sal_get_widget_dmaflush_list(u64 nasid, u64 widget_num, u64 address) | ||
43 | { | ||
44 | |||
45 | struct ia64_sal_retval ret_stuff; | ||
46 | ret_stuff.status = 0; | ||
47 | ret_stuff.v0 = 0; | ||
48 | |||
49 | SAL_CALL_NOLOCK(ret_stuff, | ||
50 | (u64) SN_SAL_IOIF_GET_WIDGET_DMAFLUSH_LIST, | ||
51 | (u64) nasid, (u64) widget_num, (u64) address, 0, 0, 0, | ||
52 | 0); | ||
53 | return ret_stuff.v0; | ||
54 | |||
55 | } | ||
56 | |||
57 | /* | ||
58 | * Retrieve the hub device info structure for the given nasid. | ||
59 | */ | ||
60 | static inline uint64_t sal_get_hubdev_info(u64 handle, u64 address) | ||
61 | { | ||
62 | |||
63 | struct ia64_sal_retval ret_stuff; | ||
64 | ret_stuff.status = 0; | ||
65 | ret_stuff.v0 = 0; | ||
66 | |||
67 | SAL_CALL_NOLOCK(ret_stuff, | ||
68 | (u64) SN_SAL_IOIF_GET_HUBDEV_INFO, | ||
69 | (u64) handle, (u64) address, 0, 0, 0, 0, 0); | ||
70 | return ret_stuff.v0; | ||
71 | } | ||
72 | |||
73 | /* | ||
74 | * Retrieve the pci bus information given the bus number. | ||
75 | */ | ||
76 | static inline uint64_t sal_get_pcibus_info(u64 segment, u64 busnum, u64 address) | ||
77 | { | ||
78 | |||
79 | struct ia64_sal_retval ret_stuff; | ||
80 | ret_stuff.status = 0; | ||
81 | ret_stuff.v0 = 0; | ||
82 | |||
83 | SAL_CALL_NOLOCK(ret_stuff, | ||
84 | (u64) SN_SAL_IOIF_GET_PCIBUS_INFO, | ||
85 | (u64) segment, (u64) busnum, (u64) address, 0, 0, 0, 0); | ||
86 | return ret_stuff.v0; | ||
87 | } | ||
88 | |||
89 | /* | ||
90 | * Retrieve the pci device information given the bus and device|function number. | ||
91 | */ | ||
92 | static inline uint64_t | ||
93 | sal_get_pcidev_info(u64 segment, u64 bus_number, u64 devfn, u64 pci_dev, | ||
94 | u64 sn_irq_info) | ||
95 | { | ||
96 | struct ia64_sal_retval ret_stuff; | ||
97 | ret_stuff.status = 0; | ||
98 | ret_stuff.v0 = 0; | ||
99 | |||
100 | SAL_CALL_NOLOCK(ret_stuff, | ||
101 | (u64) SN_SAL_IOIF_GET_PCIDEV_INFO, | ||
102 | (u64) segment, (u64) bus_number, (u64) devfn, | ||
103 | (u64) pci_dev, | ||
104 | sn_irq_info, 0, 0); | ||
105 | return ret_stuff.v0; | ||
106 | } | ||
107 | |||
108 | /* | ||
109 | * sn_alloc_pci_sysdata() - This routine allocates a pci controller | ||
110 | * which is expected as the pci_dev and pci_bus sysdata by the Linux | ||
111 | * PCI infrastructure. | ||
112 | */ | ||
113 | static inline struct pci_controller *sn_alloc_pci_sysdata(void) | ||
114 | { | ||
115 | struct pci_controller *pci_sysdata; | ||
116 | |||
117 | pci_sysdata = kmalloc(sizeof(*pci_sysdata), GFP_KERNEL); | ||
118 | if (!pci_sysdata) | ||
119 | BUG(); | ||
120 | |||
121 | memset(pci_sysdata, 0, sizeof(*pci_sysdata)); | ||
122 | return pci_sysdata; | ||
123 | } | ||
124 | |||
125 | /* | ||
126 | * sn_fixup_ionodes() - This routine initializes the HUB data strcuture for | ||
127 | * each node in the system. | ||
128 | */ | ||
129 | static void sn_fixup_ionodes(void) | ||
130 | { | ||
131 | |||
132 | struct sn_flush_device_list *sn_flush_device_list; | ||
133 | struct hubdev_info *hubdev; | ||
134 | uint64_t status; | ||
135 | uint64_t nasid; | ||
136 | int i, widget; | ||
137 | |||
138 | for (i = 0; i < numionodes; i++) { | ||
139 | hubdev = (struct hubdev_info *)(NODEPDA(i)->pdinfo); | ||
140 | nasid = cnodeid_to_nasid(i); | ||
141 | status = sal_get_hubdev_info(nasid, (uint64_t) __pa(hubdev)); | ||
142 | if (status) | ||
143 | continue; | ||
144 | |||
145 | for (widget = 0; widget <= HUB_WIDGET_ID_MAX; widget++) | ||
146 | hubdev->hdi_xwidget_info[widget].xwi_hubinfo = hubdev; | ||
147 | |||
148 | if (!hubdev->hdi_flush_nasid_list.widget_p) | ||
149 | continue; | ||
150 | |||
151 | hubdev->hdi_flush_nasid_list.widget_p = | ||
152 | kmalloc((HUB_WIDGET_ID_MAX + 1) * | ||
153 | sizeof(struct sn_flush_device_list *), GFP_KERNEL); | ||
154 | |||
155 | memset(hubdev->hdi_flush_nasid_list.widget_p, 0x0, | ||
156 | (HUB_WIDGET_ID_MAX + 1) * | ||
157 | sizeof(struct sn_flush_device_list *)); | ||
158 | |||
159 | for (widget = 0; widget <= HUB_WIDGET_ID_MAX; widget++) { | ||
160 | sn_flush_device_list = kmalloc(DEV_PER_WIDGET * | ||
161 | sizeof(struct | ||
162 | sn_flush_device_list), | ||
163 | GFP_KERNEL); | ||
164 | memset(sn_flush_device_list, 0x0, | ||
165 | DEV_PER_WIDGET * | ||
166 | sizeof(struct sn_flush_device_list)); | ||
167 | |||
168 | status = | ||
169 | sal_get_widget_dmaflush_list(nasid, widget, | ||
170 | (uint64_t) | ||
171 | __pa | ||
172 | (sn_flush_device_list)); | ||
173 | if (status) { | ||
174 | kfree(sn_flush_device_list); | ||
175 | continue; | ||
176 | } | ||
177 | |||
178 | hubdev->hdi_flush_nasid_list.widget_p[widget] = | ||
179 | sn_flush_device_list; | ||
180 | } | ||
181 | |||
182 | if (!(i & 1)) | ||
183 | hub_error_init(hubdev); | ||
184 | else | ||
185 | ice_error_init(hubdev); | ||
186 | } | ||
187 | |||
188 | } | ||
189 | |||
190 | /* | ||
191 | * sn_pci_fixup_slot() - This routine sets up a slot's resources | ||
192 | * consistent with the Linux PCI abstraction layer. Resources acquired | ||
193 | * from our PCI provider include PIO maps to BAR space and interrupt | ||
194 | * objects. | ||
195 | */ | ||
196 | static void sn_pci_fixup_slot(struct pci_dev *dev) | ||
197 | { | ||
198 | int idx; | ||
199 | int segment = 0; | ||
200 | uint64_t size; | ||
201 | struct sn_irq_info *sn_irq_info; | ||
202 | struct pci_dev *host_pci_dev; | ||
203 | int status = 0; | ||
204 | |||
205 | dev->sysdata = kmalloc(sizeof(struct pcidev_info), GFP_KERNEL); | ||
206 | if (SN_PCIDEV_INFO(dev) <= 0) | ||
207 | BUG(); /* Cannot afford to run out of memory */ | ||
208 | memset(SN_PCIDEV_INFO(dev), 0, sizeof(struct pcidev_info)); | ||
209 | |||
210 | sn_irq_info = kmalloc(sizeof(struct sn_irq_info), GFP_KERNEL); | ||
211 | if (sn_irq_info <= 0) | ||
212 | BUG(); /* Cannot afford to run out of memory */ | ||
213 | memset(sn_irq_info, 0, sizeof(struct sn_irq_info)); | ||
214 | |||
215 | /* Call to retrieve pci device information needed by kernel. */ | ||
216 | status = sal_get_pcidev_info((u64) segment, (u64) dev->bus->number, | ||
217 | dev->devfn, | ||
218 | (u64) __pa(SN_PCIDEV_INFO(dev)), | ||
219 | (u64) __pa(sn_irq_info)); | ||
220 | if (status) | ||
221 | BUG(); /* Cannot get platform pci device information information */ | ||
222 | |||
223 | /* Copy over PIO Mapped Addresses */ | ||
224 | for (idx = 0; idx <= PCI_ROM_RESOURCE; idx++) { | ||
225 | unsigned long start, end, addr; | ||
226 | |||
227 | if (!SN_PCIDEV_INFO(dev)->pdi_pio_mapped_addr[idx]) | ||
228 | continue; | ||
229 | |||
230 | start = dev->resource[idx].start; | ||
231 | end = dev->resource[idx].end; | ||
232 | size = end - start; | ||
233 | addr = SN_PCIDEV_INFO(dev)->pdi_pio_mapped_addr[idx]; | ||
234 | addr = ((addr << 4) >> 4) | __IA64_UNCACHED_OFFSET; | ||
235 | dev->resource[idx].start = addr; | ||
236 | dev->resource[idx].end = addr + size; | ||
237 | if (dev->resource[idx].flags & IORESOURCE_IO) | ||
238 | dev->resource[idx].parent = &ioport_resource; | ||
239 | else | ||
240 | dev->resource[idx].parent = &iomem_resource; | ||
241 | } | ||
242 | |||
243 | /* set up host bus linkages */ | ||
244 | host_pci_dev = | ||
245 | pci_find_slot(SN_PCIDEV_INFO(dev)->pdi_slot_host_handle >> 32, | ||
246 | SN_PCIDEV_INFO(dev)-> | ||
247 | pdi_slot_host_handle & 0xffffffff); | ||
248 | SN_PCIDEV_INFO(dev)->pdi_host_pcidev_info = | ||
249 | SN_PCIDEV_INFO(host_pci_dev); | ||
250 | SN_PCIDEV_INFO(dev)->pdi_linux_pcidev = dev; | ||
251 | SN_PCIDEV_INFO(dev)->pdi_pcibus_info = SN_PCIBUS_BUSSOFT(dev->bus); | ||
252 | |||
253 | /* Only set up IRQ stuff if this device has a host bus context */ | ||
254 | if (SN_PCIDEV_BUSSOFT(dev) && sn_irq_info->irq_irq) { | ||
255 | SN_PCIDEV_INFO(dev)->pdi_sn_irq_info = sn_irq_info; | ||
256 | dev->irq = SN_PCIDEV_INFO(dev)->pdi_sn_irq_info->irq_irq; | ||
257 | sn_irq_fixup(dev, sn_irq_info); | ||
258 | } | ||
259 | } | ||
260 | |||
261 | /* | ||
262 | * sn_pci_controller_fixup() - This routine sets up a bus's resources | ||
263 | * consistent with the Linux PCI abstraction layer. | ||
264 | */ | ||
265 | static void sn_pci_controller_fixup(int segment, int busnum) | ||
266 | { | ||
267 | int status = 0; | ||
268 | int nasid, cnode; | ||
269 | struct pci_bus *bus; | ||
270 | struct pci_controller *controller; | ||
271 | struct pcibus_bussoft *prom_bussoft_ptr; | ||
272 | struct hubdev_info *hubdev_info; | ||
273 | void *provider_soft; | ||
274 | |||
275 | status = | ||
276 | sal_get_pcibus_info((u64) segment, (u64) busnum, | ||
277 | (u64) ia64_tpa(&prom_bussoft_ptr)); | ||
278 | if (status > 0) { | ||
279 | return; /* bus # does not exist */ | ||
280 | } | ||
281 | |||
282 | prom_bussoft_ptr = __va(prom_bussoft_ptr); | ||
283 | controller = sn_alloc_pci_sysdata(); | ||
284 | /* controller non-zero is BUG'd in sn_alloc_pci_sysdata */ | ||
285 | |||
286 | bus = pci_scan_bus(busnum, &pci_root_ops, controller); | ||
287 | if (bus == NULL) { | ||
288 | return; /* error, or bus already scanned */ | ||
289 | } | ||
290 | |||
291 | /* | ||
292 | * Per-provider fixup. Copies the contents from prom to local | ||
293 | * area and links SN_PCIBUS_BUSSOFT(). | ||
294 | * | ||
295 | * Note: Provider is responsible for ensuring that prom_bussoft_ptr | ||
296 | * represents an asic-type that it can handle. | ||
297 | */ | ||
298 | |||
299 | if (prom_bussoft_ptr->bs_asic_type == PCIIO_ASIC_TYPE_PPB) { | ||
300 | return; /* no further fixup necessary */ | ||
301 | } | ||
302 | |||
303 | provider_soft = pcibr_bus_fixup(prom_bussoft_ptr); | ||
304 | if (provider_soft == NULL) { | ||
305 | return; /* fixup failed or not applicable */ | ||
306 | } | ||
307 | |||
308 | /* | ||
309 | * Generic bus fixup goes here. Don't reference prom_bussoft_ptr | ||
310 | * after this point. | ||
311 | */ | ||
312 | |||
313 | bus->sysdata = controller; | ||
314 | PCI_CONTROLLER(bus)->platform_data = provider_soft; | ||
315 | |||
316 | nasid = NASID_GET(SN_PCIBUS_BUSSOFT(bus)->bs_base); | ||
317 | cnode = nasid_to_cnodeid(nasid); | ||
318 | hubdev_info = (struct hubdev_info *)(NODEPDA(cnode)->pdinfo); | ||
319 | SN_PCIBUS_BUSSOFT(bus)->bs_xwidget_info = | ||
320 | &(hubdev_info->hdi_xwidget_info[SN_PCIBUS_BUSSOFT(bus)->bs_xid]); | ||
321 | } | ||
322 | |||
323 | /* | ||
324 | * Ugly hack to get PCI setup until we have a proper ACPI namespace. | ||
325 | */ | ||
326 | |||
327 | #define PCI_BUSES_TO_SCAN 256 | ||
328 | |||
329 | static int __init sn_pci_init(void) | ||
330 | { | ||
331 | int i = 0; | ||
332 | struct pci_dev *pci_dev = NULL; | ||
333 | extern void sn_init_cpei_timer(void); | ||
334 | #ifdef CONFIG_PROC_FS | ||
335 | extern void register_sn_procfs(void); | ||
336 | #endif | ||
337 | |||
338 | if (!ia64_platform_is("sn2") || IS_RUNNING_ON_SIMULATOR()) | ||
339 | return 0; | ||
340 | |||
341 | /* | ||
342 | * This is needed to avoid bounce limit checks in the blk layer | ||
343 | */ | ||
344 | ia64_max_iommu_merge_mask = ~PAGE_MASK; | ||
345 | sn_fixup_ionodes(); | ||
346 | sn_irq = kmalloc(sizeof(struct sn_irq_info *) * NR_IRQS, GFP_KERNEL); | ||
347 | if (sn_irq <= 0) | ||
348 | BUG(); /* Canno afford to run out of memory. */ | ||
349 | memset(sn_irq, 0, sizeof(struct sn_irq_info *) * NR_IRQS); | ||
350 | |||
351 | sn_init_cpei_timer(); | ||
352 | |||
353 | #ifdef CONFIG_PROC_FS | ||
354 | register_sn_procfs(); | ||
355 | #endif | ||
356 | |||
357 | for (i = 0; i < PCI_BUSES_TO_SCAN; i++) { | ||
358 | sn_pci_controller_fixup(0, i); | ||
359 | } | ||
360 | |||
361 | /* | ||
362 | * Generic Linux PCI Layer has created the pci_bus and pci_dev | ||
363 | * structures - time for us to add our SN PLatform specific | ||
364 | * information. | ||
365 | */ | ||
366 | |||
367 | while ((pci_dev = | ||
368 | pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pci_dev)) != NULL) { | ||
369 | sn_pci_fixup_slot(pci_dev); | ||
370 | } | ||
371 | |||
372 | sn_ioif_inited = 1; /* sn I/O infrastructure now initialized */ | ||
373 | |||
374 | return 0; | ||
375 | } | ||
376 | |||
377 | /* | ||
378 | * hubdev_init_node() - Creates the HUB data structure and link them to it's | ||
379 | * own NODE specific data area. | ||
380 | */ | ||
381 | void hubdev_init_node(nodepda_t * npda, cnodeid_t node) | ||
382 | { | ||
383 | |||
384 | struct hubdev_info *hubdev_info; | ||
385 | |||
386 | if (node >= num_online_nodes()) /* Headless/memless IO nodes */ | ||
387 | hubdev_info = | ||
388 | (struct hubdev_info *)alloc_bootmem_node(NODE_DATA(0), | ||
389 | sizeof(struct | ||
390 | hubdev_info)); | ||
391 | else | ||
392 | hubdev_info = | ||
393 | (struct hubdev_info *)alloc_bootmem_node(NODE_DATA(node), | ||
394 | sizeof(struct | ||
395 | hubdev_info)); | ||
396 | npda->pdinfo = (void *)hubdev_info; | ||
397 | |||
398 | } | ||
399 | |||
400 | geoid_t | ||
401 | cnodeid_get_geoid(cnodeid_t cnode) | ||
402 | { | ||
403 | |||
404 | struct hubdev_info *hubdev; | ||
405 | |||
406 | hubdev = (struct hubdev_info *)(NODEPDA(cnode)->pdinfo); | ||
407 | return hubdev->hdi_geoid; | ||
408 | |||
409 | } | ||
410 | |||
411 | subsys_initcall(sn_pci_init); | ||