1 | /* |
2 | * Derived from arch/powerpc/kernel/iommu.c |
3 | * |
4 | * Copyright IBM Corporation, 2006-2007 |
5 | * Copyright (C) 2006 Jon Mason <jdmason@kudzu.us> |
6 | * |
7 | * Author: Jon Mason <jdmason@kudzu.us> |
8 | * Author: Muli Ben-Yehuda <muli@il.ibm.com> |
9 | |
10 | * This program is free software; you can redistribute it and/or modify |
11 | * it under the terms of the GNU General Public License as published by |
12 | * the Free Software Foundation; either version 2 of the License, or |
13 | * (at your option) any later version. |
14 | * |
15 | * This program is distributed in the hope that it will be useful, |
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
18 | * GNU General Public License for more details. |
19 | * |
20 | * You should have received a copy of the GNU General Public License |
21 | * along with this program; if not, write to the Free Software |
22 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
23 | */ |
24 | |
25 | #define pr_fmt(fmt) "Calgary: " fmt |
26 | |
27 | #include <linux/kernel.h> |
28 | #include <linux/init.h> |
29 | #include <linux/types.h> |
30 | #include <linux/slab.h> |
31 | #include <linux/mm.h> |
32 | #include <linux/spinlock.h> |
33 | #include <linux/string.h> |
34 | #include <linux/crash_dump.h> |
35 | #include <linux/dma-mapping.h> |
36 | #include <linux/dma-direct.h> |
37 | #include <linux/bitmap.h> |
38 | #include <linux/pci_ids.h> |
39 | #include <linux/pci.h> |
40 | #include <linux/delay.h> |
41 | #include <linux/scatterlist.h> |
42 | #include <linux/iommu-helper.h> |
43 | |
44 | #include <asm/iommu.h> |
45 | #include <asm/calgary.h> |
46 | #include <asm/tce.h> |
47 | #include <asm/pci-direct.h> |
48 | #include <asm/dma.h> |
49 | #include <asm/rio.h> |
50 | #include <asm/bios_ebda.h> |
51 | #include <asm/x86_init.h> |
52 | #include <asm/iommu_table.h> |
53 | |
54 | #ifdef CONFIG_CALGARY_IOMMU_ENABLED_BY_DEFAULT |
55 | int use_calgary __read_mostly = 1; |
56 | #else |
57 | int use_calgary __read_mostly = 0; |
58 | #endif /* CONFIG_CALGARY_DEFAULT_ENABLED */ |
59 | |
60 | #define PCI_DEVICE_ID_IBM_CALGARY 0x02a1 |
61 | #define PCI_DEVICE_ID_IBM_CALIOC2 0x0308 |
62 | |
63 | /* register offsets inside the host bridge space */ |
64 | #define CALGARY_CONFIG_REG 0x0108 |
65 | #define PHB_CSR_OFFSET 0x0110 /* Channel Status */ |
66 | #define PHB_PLSSR_OFFSET 0x0120 |
67 | #define PHB_CONFIG_RW_OFFSET 0x0160 |
68 | #define PHB_IOBASE_BAR_LOW 0x0170 |
69 | #define PHB_IOBASE_BAR_HIGH 0x0180 |
70 | #define PHB_MEM_1_LOW 0x0190 |
71 | #define PHB_MEM_1_HIGH 0x01A0 |
72 | #define PHB_IO_ADDR_SIZE 0x01B0 |
73 | #define PHB_MEM_1_SIZE 0x01C0 |
74 | #define PHB_MEM_ST_OFFSET 0x01D0 |
75 | #define PHB_AER_OFFSET 0x0200 |
76 | #define PHB_CONFIG_0_HIGH 0x0220 |
77 | #define PHB_CONFIG_0_LOW 0x0230 |
78 | #define PHB_CONFIG_0_END 0x0240 |
79 | #define PHB_MEM_2_LOW 0x02B0 |
80 | #define PHB_MEM_2_HIGH 0x02C0 |
81 | #define PHB_MEM_2_SIZE_HIGH 0x02D0 |
82 | #define PHB_MEM_2_SIZE_LOW 0x02E0 |
83 | #define PHB_DOSHOLE_OFFSET 0x08E0 |
84 | |
85 | /* CalIOC2 specific */ |
86 | #define PHB_SAVIOR_L2 0x0DB0 |
87 | #define PHB_PAGE_MIG_CTRL 0x0DA8 |
88 | #define PHB_PAGE_MIG_DEBUG 0x0DA0 |
89 | #define PHB_ROOT_COMPLEX_STATUS 0x0CB0 |
90 | |
91 | /* PHB_CONFIG_RW */ |
92 | #define PHB_TCE_ENABLE 0x20000000 |
93 | #define PHB_SLOT_DISABLE 0x1C000000 |
94 | #define PHB_DAC_DISABLE 0x01000000 |
95 | #define PHB_MEM2_ENABLE 0x00400000 |
96 | #define PHB_MCSR_ENABLE 0x00100000 |
97 | /* TAR (Table Address Register) */ |
98 | #define TAR_SW_BITS 0x0000ffffffff800fUL |
99 | #define TAR_VALID 0x0000000000000008UL |
100 | /* CSR (Channel/DMA Status Register) */ |
101 | #define CSR_AGENT_MASK 0xffe0ffff |
102 | /* CCR (Calgary Configuration Register) */ |
103 | #define CCR_2SEC_TIMEOUT 0x000000000000000EUL |
104 | /* PMCR/PMDR (Page Migration Control/Debug Registers */ |
105 | #define PMR_SOFTSTOP 0x80000000 |
106 | #define PMR_SOFTSTOPFAULT 0x40000000 |
107 | #define PMR_HARDSTOP 0x20000000 |
108 | |
109 | /* |
110 | * The maximum PHB bus number. |
111 | * x3950M2 (rare): 8 chassis, 48 PHBs per chassis = 384 |
112 | * x3950M2: 4 chassis, 48 PHBs per chassis = 192 |
113 | * x3950 (PCIE): 8 chassis, 32 PHBs per chassis = 256 |
114 | * x3950 (PCIX): 8 chassis, 16 PHBs per chassis = 128 |
115 | */ |
116 | #define MAX_PHB_BUS_NUM 256 |
117 | |
118 | #define PHBS_PER_CALGARY 4 |
119 | |
120 | /* register offsets in Calgary's internal register space */ |
121 | static const unsigned long tar_offsets[] = { |
122 | 0x0580 /* TAR0 */, |
123 | 0x0588 /* TAR1 */, |
124 | 0x0590 /* TAR2 */, |
125 | 0x0598 /* TAR3 */ |
126 | }; |
127 | |
128 | static const unsigned long split_queue_offsets[] = { |
129 | 0x4870 /* SPLIT QUEUE 0 */, |
130 | 0x5870 /* SPLIT QUEUE 1 */, |
131 | 0x6870 /* SPLIT QUEUE 2 */, |
132 | 0x7870 /* SPLIT QUEUE 3 */ |
133 | }; |
134 | |
135 | static const unsigned long phb_offsets[] = { |
136 | 0x8000 /* PHB0 */, |
137 | 0x9000 /* PHB1 */, |
138 | 0xA000 /* PHB2 */, |
139 | 0xB000 /* PHB3 */ |
140 | }; |
141 | |
142 | /* PHB debug registers */ |
143 | |
144 | static const unsigned long phb_debug_offsets[] = { |
145 | 0x4000 /* PHB 0 DEBUG */, |
146 | 0x5000 /* PHB 1 DEBUG */, |
147 | 0x6000 /* PHB 2 DEBUG */, |
148 | 0x7000 /* PHB 3 DEBUG */ |
149 | }; |
150 | |
151 | /* |
152 | * STUFF register for each debug PHB, |
153 | * byte 1 = start bus number, byte 2 = end bus number |
154 | */ |
155 | |
156 | #define PHB_DEBUG_STUFF_OFFSET 0x0020 |
157 | |
158 | unsigned int specified_table_size = TCE_TABLE_SIZE_UNSPECIFIED; |
159 | static int translate_empty_slots __read_mostly = 0; |
160 | static int calgary_detected __read_mostly = 0; |
161 | |
162 | static struct rio_table_hdr *rio_table_hdr __initdata; |
163 | static struct scal_detail *scal_devs[MAX_NUMNODES] __initdata; |
164 | static struct rio_detail *rio_devs[MAX_NUMNODES * 4] __initdata; |
165 | |
166 | struct calgary_bus_info { |
167 | void *tce_space; |
168 | unsigned char translation_disabled; |
169 | signed char phbid; |
170 | void __iomem *bbar; |
171 | }; |
172 | |
173 | static void calgary_handle_quirks(struct iommu_table *tbl, struct pci_dev *dev); |
174 | static void calgary_tce_cache_blast(struct iommu_table *tbl); |
175 | static void calgary_dump_error_regs(struct iommu_table *tbl); |
176 | static void calioc2_handle_quirks(struct iommu_table *tbl, struct pci_dev *dev); |
177 | static void calioc2_tce_cache_blast(struct iommu_table *tbl); |
178 | static void calioc2_dump_error_regs(struct iommu_table *tbl); |
179 | static void calgary_init_bitmap_from_tce_table(struct iommu_table *tbl); |
180 | static void get_tce_space_from_tar(void); |
181 | |
182 | static const struct cal_chipset_ops calgary_chip_ops = { |
183 | .handle_quirks = calgary_handle_quirks, |
184 | .tce_cache_blast = calgary_tce_cache_blast, |
185 | .dump_error_regs = calgary_dump_error_regs |
186 | }; |
187 | |
188 | static const struct cal_chipset_ops calioc2_chip_ops = { |
189 | .handle_quirks = calioc2_handle_quirks, |
190 | .tce_cache_blast = calioc2_tce_cache_blast, |
191 | .dump_error_regs = calioc2_dump_error_regs |
192 | }; |
193 | |
194 | static struct calgary_bus_info bus_info[MAX_PHB_BUS_NUM] = { { NULL, 0, 0 }, }; |
195 | |
196 | static inline int translation_enabled(struct iommu_table *tbl) |
197 | { |
198 | /* only PHBs with translation enabled have an IOMMU table */ |
199 | return (tbl != NULL); |
200 | } |
201 | |
202 | static void iommu_range_reserve(struct iommu_table *tbl, |
203 | unsigned long start_addr, unsigned int npages) |
204 | { |
205 | unsigned long index; |
206 | unsigned long end; |
207 | unsigned long flags; |
208 | |
209 | index = start_addr >> PAGE_SHIFT; |
210 | |
211 | /* bail out if we're asked to reserve a region we don't cover */ |
212 | if (index >= tbl->it_size) |
213 | return; |
214 | |
215 | end = index + npages; |
216 | if (end > tbl->it_size) /* don't go off the table */ |
217 | end = tbl->it_size; |
218 | |
219 | spin_lock_irqsave(&tbl->it_lock, flags); |
220 | |
221 | bitmap_set(tbl->it_map, index, npages); |
222 | |
223 | spin_unlock_irqrestore(&tbl->it_lock, flags); |
224 | } |
225 | |
226 | static unsigned long iommu_range_alloc(struct device *dev, |
227 | struct iommu_table *tbl, |
228 | unsigned int npages) |
229 | { |
230 | unsigned long flags; |
231 | unsigned long offset; |
232 | unsigned long boundary_size; |
233 | |
234 | boundary_size = ALIGN(dma_get_seg_boundary(dev) + 1, |
235 | PAGE_SIZE) >> PAGE_SHIFT; |
236 | |
237 | BUG_ON(npages == 0); |
238 | |
239 | spin_lock_irqsave(&tbl->it_lock, flags); |
240 | |
241 | offset = iommu_area_alloc(tbl->it_map, tbl->it_size, tbl->it_hint, |
242 | npages, 0, boundary_size, 0); |
243 | if (offset == ~0UL) { |
244 | tbl->chip_ops->tce_cache_blast(tbl); |
245 | |
246 | offset = iommu_area_alloc(tbl->it_map, tbl->it_size, 0, |
247 | npages, 0, boundary_size, 0); |
248 | if (offset == ~0UL) { |
249 | pr_warn("IOMMU full\n" ); |
250 | spin_unlock_irqrestore(&tbl->it_lock, flags); |
251 | if (panic_on_overflow) |
252 | panic("Calgary: fix the allocator.\n" ); |
253 | else |
254 | return DMA_MAPPING_ERROR; |
255 | } |
256 | } |
257 | |
258 | tbl->it_hint = offset + npages; |
259 | BUG_ON(tbl->it_hint > tbl->it_size); |
260 | |
261 | spin_unlock_irqrestore(&tbl->it_lock, flags); |
262 | |
263 | return offset; |
264 | } |
265 | |
266 | static dma_addr_t iommu_alloc(struct device *dev, struct iommu_table *tbl, |
267 | void *vaddr, unsigned int npages, int direction) |
268 | { |
269 | unsigned long entry; |
270 | dma_addr_t ret; |
271 | |
272 | entry = iommu_range_alloc(dev, tbl, npages); |
273 | if (unlikely(entry == DMA_MAPPING_ERROR)) { |
274 | pr_warn("failed to allocate %u pages in iommu %p\n" , |
275 | npages, tbl); |
276 | return DMA_MAPPING_ERROR; |
277 | } |
278 | |
279 | /* set the return dma address */ |
280 | ret = (entry << PAGE_SHIFT) | ((unsigned long)vaddr & ~PAGE_MASK); |
281 | |
282 | /* put the TCEs in the HW table */ |
283 | tce_build(tbl, entry, npages, (unsigned long)vaddr & PAGE_MASK, |
284 | direction); |
285 | return ret; |
286 | } |
287 | |
288 | static void iommu_free(struct iommu_table *tbl, dma_addr_t dma_addr, |
289 | unsigned int npages) |
290 | { |
291 | unsigned long entry; |
292 | unsigned long flags; |
293 | |
294 | /* were we called with bad_dma_address? */ |
295 | if (unlikely(dma_addr == DMA_MAPPING_ERROR)) { |
296 | WARN(1, KERN_ERR "Calgary: driver tried unmapping bad DMA " |
297 | "address 0x%Lx\n" , dma_addr); |
298 | return; |
299 | } |
300 | |
301 | entry = dma_addr >> PAGE_SHIFT; |
302 | |
303 | BUG_ON(entry + npages > tbl->it_size); |
304 | |
305 | tce_free(tbl, entry, npages); |
306 | |
307 | spin_lock_irqsave(&tbl->it_lock, flags); |
308 | |
309 | bitmap_clear(tbl->it_map, entry, npages); |
310 | |
311 | spin_unlock_irqrestore(&tbl->it_lock, flags); |
312 | } |
313 | |
314 | static inline struct iommu_table *find_iommu_table(struct device *dev) |
315 | { |
316 | struct pci_dev *pdev; |
317 | struct pci_bus *pbus; |
318 | struct iommu_table *tbl; |
319 | |
320 | pdev = to_pci_dev(dev); |
321 | |
322 | /* search up the device tree for an iommu */ |
323 | pbus = pdev->bus; |
324 | do { |
325 | tbl = pci_iommu(pbus); |
326 | if (tbl && tbl->it_busno == pbus->number) |
327 | break; |
328 | tbl = NULL; |
329 | pbus = pbus->parent; |
330 | } while (pbus); |
331 | |
332 | BUG_ON(tbl && (tbl->it_busno != pbus->number)); |
333 | |
334 | return tbl; |
335 | } |
336 | |
337 | static void calgary_unmap_sg(struct device *dev, struct scatterlist *sglist, |
338 | int nelems,enum dma_data_direction dir, |
339 | unsigned long attrs) |
340 | { |
341 | struct iommu_table *tbl = find_iommu_table(dev); |
342 | struct scatterlist *s; |
343 | int i; |
344 | |
345 | if (!translation_enabled(tbl)) |
346 | return; |
347 | |
348 | for_each_sg(sglist, s, nelems, i) { |
349 | unsigned int npages; |
350 | dma_addr_t dma = s->dma_address; |
351 | unsigned int dmalen = s->dma_length; |
352 | |
353 | if (dmalen == 0) |
354 | break; |
355 | |
356 | npages = iommu_num_pages(dma, dmalen, PAGE_SIZE); |
357 | iommu_free(tbl, dma, npages); |
358 | } |
359 | } |
360 | |
361 | static int calgary_map_sg(struct device *dev, struct scatterlist *sg, |
362 | int nelems, enum dma_data_direction dir, |
363 | unsigned long attrs) |
364 | { |
365 | struct iommu_table *tbl = find_iommu_table(dev); |
366 | struct scatterlist *s; |
367 | unsigned long vaddr; |
368 | unsigned int npages; |
369 | unsigned long entry; |
370 | int i; |
371 | |
372 | for_each_sg(sg, s, nelems, i) { |
373 | BUG_ON(!sg_page(s)); |
374 | |
375 | vaddr = (unsigned long) sg_virt(s); |
376 | npages = iommu_num_pages(vaddr, s->length, PAGE_SIZE); |
377 | |
378 | entry = iommu_range_alloc(dev, tbl, npages); |
379 | if (entry == DMA_MAPPING_ERROR) { |
380 | /* makes sure unmap knows to stop */ |
381 | s->dma_length = 0; |
382 | goto error; |
383 | } |
384 | |
385 | s->dma_address = (entry << PAGE_SHIFT) | s->offset; |
386 | |
387 | /* insert into HW table */ |
388 | tce_build(tbl, entry, npages, vaddr & PAGE_MASK, dir); |
389 | |
390 | s->dma_length = s->length; |
391 | } |
392 | |
393 | return nelems; |
394 | error: |
395 | calgary_unmap_sg(dev, sg, nelems, dir, 0); |
396 | for_each_sg(sg, s, nelems, i) { |
397 | sg->dma_address = DMA_MAPPING_ERROR; |
398 | sg->dma_length = 0; |
399 | } |
400 | return 0; |
401 | } |
402 | |
403 | static dma_addr_t calgary_map_page(struct device *dev, struct page *page, |
404 | unsigned long offset, size_t size, |
405 | enum dma_data_direction dir, |
406 | unsigned long attrs) |
407 | { |
408 | void *vaddr = page_address(page) + offset; |
409 | unsigned long uaddr; |
410 | unsigned int npages; |
411 | struct iommu_table *tbl = find_iommu_table(dev); |
412 | |
413 | uaddr = (unsigned long)vaddr; |
414 | npages = iommu_num_pages(uaddr, size, PAGE_SIZE); |
415 | |
416 | return iommu_alloc(dev, tbl, vaddr, npages, dir); |
417 | } |
418 | |
419 | static void calgary_unmap_page(struct device *dev, dma_addr_t dma_addr, |
420 | size_t size, enum dma_data_direction dir, |
421 | unsigned long attrs) |
422 | { |
423 | struct iommu_table *tbl = find_iommu_table(dev); |
424 | unsigned int npages; |
425 | |
426 | npages = iommu_num_pages(dma_addr, size, PAGE_SIZE); |
427 | iommu_free(tbl, dma_addr, npages); |
428 | } |
429 | |
430 | static void* calgary_alloc_coherent(struct device *dev, size_t size, |
431 | dma_addr_t *dma_handle, gfp_t flag, unsigned long attrs) |
432 | { |
433 | void *ret = NULL; |
434 | dma_addr_t mapping; |
435 | unsigned int npages, order; |
436 | struct iommu_table *tbl = find_iommu_table(dev); |
437 | |
438 | size = PAGE_ALIGN(size); /* size rounded up to full pages */ |
439 | npages = size >> PAGE_SHIFT; |
440 | order = get_order(size); |
441 | |
442 | /* alloc enough pages (and possibly more) */ |
443 | ret = (void *)__get_free_pages(flag, order); |
444 | if (!ret) |
445 | goto error; |
446 | memset(ret, 0, size); |
447 | |
448 | /* set up tces to cover the allocated range */ |
449 | mapping = iommu_alloc(dev, tbl, ret, npages, DMA_BIDIRECTIONAL); |
450 | if (mapping == DMA_MAPPING_ERROR) |
451 | goto free; |
452 | *dma_handle = mapping; |
453 | return ret; |
454 | free: |
455 | free_pages((unsigned long)ret, get_order(size)); |
456 | ret = NULL; |
457 | error: |
458 | return ret; |
459 | } |
460 | |
461 | static void calgary_free_coherent(struct device *dev, size_t size, |
462 | void *vaddr, dma_addr_t dma_handle, |
463 | unsigned long attrs) |
464 | { |
465 | unsigned int npages; |
466 | struct iommu_table *tbl = find_iommu_table(dev); |
467 | |
468 | size = PAGE_ALIGN(size); |
469 | npages = size >> PAGE_SHIFT; |
470 | |
471 | iommu_free(tbl, dma_handle, npages); |
472 | free_pages((unsigned long)vaddr, get_order(size)); |
473 | } |
474 | |
475 | static const struct dma_map_ops calgary_dma_ops = { |
476 | .alloc = calgary_alloc_coherent, |
477 | .free = calgary_free_coherent, |
478 | .map_sg = calgary_map_sg, |
479 | .unmap_sg = calgary_unmap_sg, |
480 | .map_page = calgary_map_page, |
481 | .unmap_page = calgary_unmap_page, |
482 | .dma_supported = dma_direct_supported, |
483 | }; |
484 | |
485 | static inline void __iomem * busno_to_bbar(unsigned char num) |
486 | { |
487 | return bus_info[num].bbar; |
488 | } |
489 | |
490 | static inline int busno_to_phbid(unsigned char num) |
491 | { |
492 | return bus_info[num].phbid; |
493 | } |
494 | |
495 | static inline unsigned long split_queue_offset(unsigned char num) |
496 | { |
497 | size_t idx = busno_to_phbid(num); |
498 | |
499 | return split_queue_offsets[idx]; |
500 | } |
501 | |
502 | static inline unsigned long tar_offset(unsigned char num) |
503 | { |
504 | size_t idx = busno_to_phbid(num); |
505 | |
506 | return tar_offsets[idx]; |
507 | } |
508 | |
509 | static inline unsigned long phb_offset(unsigned char num) |
510 | { |
511 | size_t idx = busno_to_phbid(num); |
512 | |
513 | return phb_offsets[idx]; |
514 | } |
515 | |
516 | static inline void __iomem* calgary_reg(void __iomem *bar, unsigned long offset) |
517 | { |
518 | unsigned long target = ((unsigned long)bar) | offset; |
519 | return (void __iomem*)target; |
520 | } |
521 | |
522 | static inline int is_calioc2(unsigned short device) |
523 | { |
524 | return (device == PCI_DEVICE_ID_IBM_CALIOC2); |
525 | } |
526 | |
527 | static inline int is_calgary(unsigned short device) |
528 | { |
529 | return (device == PCI_DEVICE_ID_IBM_CALGARY); |
530 | } |
531 | |
532 | static inline int is_cal_pci_dev(unsigned short device) |
533 | { |
534 | return (is_calgary(device) || is_calioc2(device)); |
535 | } |
536 | |
537 | static void calgary_tce_cache_blast(struct iommu_table *tbl) |
538 | { |
539 | u64 val; |
540 | u32 aer; |
541 | int i = 0; |
542 | void __iomem *bbar = tbl->bbar; |
543 | void __iomem *target; |
544 | |
545 | /* disable arbitration on the bus */ |
546 | target = calgary_reg(bbar, phb_offset(tbl->it_busno) | PHB_AER_OFFSET); |
547 | aer = readl(target); |
548 | writel(0, target); |
549 | |
550 | /* read plssr to ensure it got there */ |
551 | target = calgary_reg(bbar, phb_offset(tbl->it_busno) | PHB_PLSSR_OFFSET); |
552 | val = readl(target); |
553 | |
554 | /* poll split queues until all DMA activity is done */ |
555 | target = calgary_reg(bbar, split_queue_offset(tbl->it_busno)); |
556 | do { |
557 | val = readq(target); |
558 | i++; |
559 | } while ((val & 0xff) != 0xff && i < 100); |
560 | if (i == 100) |
561 | pr_warn("PCI bus not quiesced, continuing anyway\n" ); |
562 | |
563 | /* invalidate TCE cache */ |
564 | target = calgary_reg(bbar, tar_offset(tbl->it_busno)); |
565 | writeq(tbl->tar_val, target); |
566 | |
567 | /* enable arbitration */ |
568 | target = calgary_reg(bbar, phb_offset(tbl->it_busno) | PHB_AER_OFFSET); |
569 | writel(aer, target); |
570 | (void)readl(target); /* flush */ |
571 | } |
572 | |
573 | static void calioc2_tce_cache_blast(struct iommu_table *tbl) |
574 | { |
575 | void __iomem *bbar = tbl->bbar; |
576 | void __iomem *target; |
577 | u64 val64; |
578 | u32 val; |
579 | int i = 0; |
580 | int count = 1; |
581 | unsigned char bus = tbl->it_busno; |
582 | |
583 | begin: |
584 | printk(KERN_DEBUG "Calgary: CalIOC2 bus 0x%x entering tce cache blast " |
585 | "sequence - count %d\n" , bus, count); |
586 | |
587 | /* 1. using the Page Migration Control reg set SoftStop */ |
588 | target = calgary_reg(bbar, phb_offset(bus) | PHB_PAGE_MIG_CTRL); |
589 | val = be32_to_cpu(readl(target)); |
590 | printk(KERN_DEBUG "1a. read 0x%x [LE] from %p\n" , val, target); |
591 | val |= PMR_SOFTSTOP; |
592 | printk(KERN_DEBUG "1b. writing 0x%x [LE] to %p\n" , val, target); |
593 | writel(cpu_to_be32(val), target); |
594 | |
595 | /* 2. poll split queues until all DMA activity is done */ |
596 | printk(KERN_DEBUG "2a. starting to poll split queues\n" ); |
597 | target = calgary_reg(bbar, split_queue_offset(bus)); |
598 | do { |
599 | val64 = readq(target); |
600 | i++; |
601 | } while ((val64 & 0xff) != 0xff && i < 100); |
602 | if (i == 100) |
603 | pr_warn("CalIOC2: PCI bus not quiesced, continuing anyway\n" ); |
604 | |
605 | /* 3. poll Page Migration DEBUG for SoftStopFault */ |
606 | target = calgary_reg(bbar, phb_offset(bus) | PHB_PAGE_MIG_DEBUG); |
607 | val = be32_to_cpu(readl(target)); |
608 | printk(KERN_DEBUG "3. read 0x%x [LE] from %p\n" , val, target); |
609 | |
610 | /* 4. if SoftStopFault - goto (1) */ |
611 | if (val & PMR_SOFTSTOPFAULT) { |
612 | if (++count < 100) |
613 | goto begin; |
614 | else { |
615 | pr_warn("CalIOC2: too many SoftStopFaults, aborting TCE cache flush sequence!\n" ); |
616 | return; /* pray for the best */ |
617 | } |
618 | } |
619 | |
620 | /* 5. Slam into HardStop by reading PHB_PAGE_MIG_CTRL */ |
621 | target = calgary_reg(bbar, phb_offset(bus) | PHB_PAGE_MIG_CTRL); |
622 | printk(KERN_DEBUG "5a. slamming into HardStop by reading %p\n" , target); |
623 | val = be32_to_cpu(readl(target)); |
624 | printk(KERN_DEBUG "5b. read 0x%x [LE] from %p\n" , val, target); |
625 | target = calgary_reg(bbar, phb_offset(bus) | PHB_PAGE_MIG_DEBUG); |
626 | val = be32_to_cpu(readl(target)); |
627 | printk(KERN_DEBUG "5c. read 0x%x [LE] from %p (debug)\n" , val, target); |
628 | |
629 | /* 6. invalidate TCE cache */ |
630 | printk(KERN_DEBUG "6. invalidating TCE cache\n" ); |
631 | target = calgary_reg(bbar, tar_offset(bus)); |
632 | writeq(tbl->tar_val, target); |
633 | |
634 | /* 7. Re-read PMCR */ |
635 | printk(KERN_DEBUG "7a. Re-reading PMCR\n" ); |
636 | target = calgary_reg(bbar, phb_offset(bus) | PHB_PAGE_MIG_CTRL); |
637 | val = be32_to_cpu(readl(target)); |
638 | printk(KERN_DEBUG "7b. read 0x%x [LE] from %p\n" , val, target); |
639 | |
640 | /* 8. Remove HardStop */ |
641 | printk(KERN_DEBUG "8a. removing HardStop from PMCR\n" ); |
642 | target = calgary_reg(bbar, phb_offset(bus) | PHB_PAGE_MIG_CTRL); |
643 | val = 0; |
644 | printk(KERN_DEBUG "8b. writing 0x%x [LE] to %p\n" , val, target); |
645 | writel(cpu_to_be32(val), target); |
646 | val = be32_to_cpu(readl(target)); |
647 | printk(KERN_DEBUG "8c. read 0x%x [LE] from %p\n" , val, target); |
648 | } |
649 | |
650 | static void __init calgary_reserve_mem_region(struct pci_dev *dev, u64 start, |
651 | u64 limit) |
652 | { |
653 | unsigned int numpages; |
654 | |
655 | limit = limit | 0xfffff; |
656 | limit++; |
657 | |
658 | numpages = ((limit - start) >> PAGE_SHIFT); |
659 | iommu_range_reserve(pci_iommu(dev->bus), start, numpages); |
660 | } |
661 | |
662 | static void __init calgary_reserve_peripheral_mem_1(struct pci_dev *dev) |
663 | { |
664 | void __iomem *target; |
665 | u64 low, high, sizelow; |
666 | u64 start, limit; |
667 | struct iommu_table *tbl = pci_iommu(dev->bus); |
668 | unsigned char busnum = dev->bus->number; |
669 | void __iomem *bbar = tbl->bbar; |
670 | |
671 | /* peripheral MEM_1 region */ |
672 | target = calgary_reg(bbar, phb_offset(busnum) | PHB_MEM_1_LOW); |
673 | low = be32_to_cpu(readl(target)); |
674 | target = calgary_reg(bbar, phb_offset(busnum) | PHB_MEM_1_HIGH); |
675 | high = be32_to_cpu(readl(target)); |
676 | target = calgary_reg(bbar, phb_offset(busnum) | PHB_MEM_1_SIZE); |
677 | sizelow = be32_to_cpu(readl(target)); |
678 | |
679 | start = (high << 32) | low; |
680 | limit = sizelow; |
681 | |
682 | calgary_reserve_mem_region(dev, start, limit); |
683 | } |
684 | |
685 | static void __init calgary_reserve_peripheral_mem_2(struct pci_dev *dev) |
686 | { |
687 | void __iomem *target; |
688 | u32 val32; |
689 | u64 low, high, sizelow, sizehigh; |
690 | u64 start, limit; |
691 | struct iommu_table *tbl = pci_iommu(dev->bus); |
692 | unsigned char busnum = dev->bus->number; |
693 | void __iomem *bbar = tbl->bbar; |
694 | |
695 | /* is it enabled? */ |
696 | target = calgary_reg(bbar, phb_offset(busnum) | PHB_CONFIG_RW_OFFSET); |
697 | val32 = be32_to_cpu(readl(target)); |
698 | if (!(val32 & PHB_MEM2_ENABLE)) |
699 | return; |
700 | |
701 | target = calgary_reg(bbar, phb_offset(busnum) | PHB_MEM_2_LOW); |
702 | low = be32_to_cpu(readl(target)); |
703 | target = calgary_reg(bbar, phb_offset(busnum) | PHB_MEM_2_HIGH); |
704 | high = be32_to_cpu(readl(target)); |
705 | target = calgary_reg(bbar, phb_offset(busnum) | PHB_MEM_2_SIZE_LOW); |
706 | sizelow = be32_to_cpu(readl(target)); |
707 | target = calgary_reg(bbar, phb_offset(busnum) | PHB_MEM_2_SIZE_HIGH); |
708 | sizehigh = be32_to_cpu(readl(target)); |
709 | |
710 | start = (high << 32) | low; |
711 | limit = (sizehigh << 32) | sizelow; |
712 | |
713 | calgary_reserve_mem_region(dev, start, limit); |
714 | } |
715 | |
716 | /* |
717 | * some regions of the IO address space do not get translated, so we |
718 | * must not give devices IO addresses in those regions. The regions |
719 | * are the 640KB-1MB region and the two PCI peripheral memory holes. |
720 | * Reserve all of them in the IOMMU bitmap to avoid giving them out |
721 | * later. |
722 | */ |
723 | static void __init calgary_reserve_regions(struct pci_dev *dev) |
724 | { |
725 | unsigned int npages; |
726 | u64 start; |
727 | struct iommu_table *tbl = pci_iommu(dev->bus); |
728 | |
729 | /* avoid the BIOS/VGA first 640KB-1MB region */ |
730 | /* for CalIOC2 - avoid the entire first MB */ |
731 | if (is_calgary(dev->device)) { |
732 | start = (640 * 1024); |
733 | npages = ((1024 - 640) * 1024) >> PAGE_SHIFT; |
734 | } else { /* calioc2 */ |
735 | start = 0; |
736 | npages = (1 * 1024 * 1024) >> PAGE_SHIFT; |
737 | } |
738 | iommu_range_reserve(tbl, start, npages); |
739 | |
740 | /* reserve the two PCI peripheral memory regions in IO space */ |
741 | calgary_reserve_peripheral_mem_1(dev); |
742 | calgary_reserve_peripheral_mem_2(dev); |
743 | } |
744 | |
745 | static int __init calgary_setup_tar(struct pci_dev *dev, void __iomem *bbar) |
746 | { |
747 | u64 val64; |
748 | u64 table_phys; |
749 | void __iomem *target; |
750 | int ret; |
751 | struct iommu_table *tbl; |
752 | |
753 | /* build TCE tables for each PHB */ |
754 | ret = build_tce_table(dev, bbar); |
755 | if (ret) |
756 | return ret; |
757 | |
758 | tbl = pci_iommu(dev->bus); |
759 | tbl->it_base = (unsigned long)bus_info[dev->bus->number].tce_space; |
760 | |
761 | if (is_kdump_kernel()) |
762 | calgary_init_bitmap_from_tce_table(tbl); |
763 | else |
764 | tce_free(tbl, 0, tbl->it_size); |
765 | |
766 | if (is_calgary(dev->device)) |
767 | tbl->chip_ops = &calgary_chip_ops; |
768 | else if (is_calioc2(dev->device)) |
769 | tbl->chip_ops = &calioc2_chip_ops; |
770 | else |
771 | BUG(); |
772 | |
773 | calgary_reserve_regions(dev); |
774 | |
775 | /* set TARs for each PHB */ |
776 | target = calgary_reg(bbar, tar_offset(dev->bus->number)); |
777 | val64 = be64_to_cpu(readq(target)); |
778 | |
779 | /* zero out all TAR bits under sw control */ |
780 | val64 &= ~TAR_SW_BITS; |
781 | table_phys = (u64)__pa(tbl->it_base); |
782 | |
783 | val64 |= table_phys; |
784 | |
785 | BUG_ON(specified_table_size > TCE_TABLE_SIZE_8M); |
786 | val64 |= (u64) specified_table_size; |
787 | |
788 | tbl->tar_val = cpu_to_be64(val64); |
789 | |
790 | writeq(tbl->tar_val, target); |
791 | readq(target); /* flush */ |
792 | |
793 | return 0; |
794 | } |
795 | |
796 | static void __init calgary_free_bus(struct pci_dev *dev) |
797 | { |
798 | u64 val64; |
799 | struct iommu_table *tbl = pci_iommu(dev->bus); |
800 | void __iomem *target; |
801 | unsigned int bitmapsz; |
802 | |
803 | target = calgary_reg(tbl->bbar, tar_offset(dev->bus->number)); |
804 | val64 = be64_to_cpu(readq(target)); |
805 | val64 &= ~TAR_SW_BITS; |
806 | writeq(cpu_to_be64(val64), target); |
807 | readq(target); /* flush */ |
808 | |
809 | bitmapsz = tbl->it_size / BITS_PER_BYTE; |
810 | free_pages((unsigned long)tbl->it_map, get_order(bitmapsz)); |
811 | tbl->it_map = NULL; |
812 | |
813 | kfree(tbl); |
814 | |
815 | set_pci_iommu(dev->bus, NULL); |
816 | |
817 | /* Can't free bootmem allocated memory after system is up :-( */ |
818 | bus_info[dev->bus->number].tce_space = NULL; |
819 | } |
820 | |
821 | static void calgary_dump_error_regs(struct iommu_table *tbl) |
822 | { |
823 | void __iomem *bbar = tbl->bbar; |
824 | void __iomem *target; |
825 | u32 csr, plssr; |
826 | |
827 | target = calgary_reg(bbar, phb_offset(tbl->it_busno) | PHB_CSR_OFFSET); |
828 | csr = be32_to_cpu(readl(target)); |
829 | |
830 | target = calgary_reg(bbar, phb_offset(tbl->it_busno) | PHB_PLSSR_OFFSET); |
831 | plssr = be32_to_cpu(readl(target)); |
832 | |
833 | /* If no error, the agent ID in the CSR is not valid */ |
834 | pr_emerg("DMA error on Calgary PHB 0x%x, 0x%08x@CSR 0x%08x@PLSSR\n" , |
835 | tbl->it_busno, csr, plssr); |
836 | } |
837 | |
838 | static void calioc2_dump_error_regs(struct iommu_table *tbl) |
839 | { |
840 | void __iomem *bbar = tbl->bbar; |
841 | u32 csr, csmr, plssr, mck, rcstat; |
842 | void __iomem *target; |
843 | unsigned long phboff = phb_offset(tbl->it_busno); |
844 | unsigned long erroff; |
845 | u32 errregs[7]; |
846 | int i; |
847 | |
848 | /* dump CSR */ |
849 | target = calgary_reg(bbar, phboff | PHB_CSR_OFFSET); |
850 | csr = be32_to_cpu(readl(target)); |
851 | /* dump PLSSR */ |
852 | target = calgary_reg(bbar, phboff | PHB_PLSSR_OFFSET); |
853 | plssr = be32_to_cpu(readl(target)); |
854 | /* dump CSMR */ |
855 | target = calgary_reg(bbar, phboff | 0x290); |
856 | csmr = be32_to_cpu(readl(target)); |
857 | /* dump mck */ |
858 | target = calgary_reg(bbar, phboff | 0x800); |
859 | mck = be32_to_cpu(readl(target)); |
860 | |
861 | pr_emerg("DMA error on CalIOC2 PHB 0x%x\n" , tbl->it_busno); |
862 | |
863 | pr_emerg("0x%08x@CSR 0x%08x@PLSSR 0x%08x@CSMR 0x%08x@MCK\n" , |
864 | csr, plssr, csmr, mck); |
865 | |
866 | /* dump rest of error regs */ |
867 | pr_emerg("" ); |
868 | for (i = 0; i < ARRAY_SIZE(errregs); i++) { |
869 | /* err regs are at 0x810 - 0x870 */ |
870 | erroff = (0x810 + (i * 0x10)); |
871 | target = calgary_reg(bbar, phboff | erroff); |
872 | errregs[i] = be32_to_cpu(readl(target)); |
873 | pr_cont("0x%08x@0x%lx " , errregs[i], erroff); |
874 | } |
875 | pr_cont("\n" ); |
876 | |
877 | /* root complex status */ |
878 | target = calgary_reg(bbar, phboff | PHB_ROOT_COMPLEX_STATUS); |
879 | rcstat = be32_to_cpu(readl(target)); |
880 | printk(KERN_EMERG "Calgary: 0x%08x@0x%x\n" , rcstat, |
881 | PHB_ROOT_COMPLEX_STATUS); |
882 | } |
883 | |
884 | static void calgary_watchdog(struct timer_list *t) |
885 | { |
886 | struct iommu_table *tbl = from_timer(tbl, t, watchdog_timer); |
887 | void __iomem *bbar = tbl->bbar; |
888 | u32 val32; |
889 | void __iomem *target; |
890 | |
891 | target = calgary_reg(bbar, phb_offset(tbl->it_busno) | PHB_CSR_OFFSET); |
892 | val32 = be32_to_cpu(readl(target)); |
893 | |
894 | /* If no error, the agent ID in the CSR is not valid */ |
895 | if (val32 & CSR_AGENT_MASK) { |
896 | tbl->chip_ops->dump_error_regs(tbl); |
897 | |
898 | /* reset error */ |
899 | writel(0, target); |
900 | |
901 | /* Disable bus that caused the error */ |
902 | target = calgary_reg(bbar, phb_offset(tbl->it_busno) | |
903 | PHB_CONFIG_RW_OFFSET); |
904 | val32 = be32_to_cpu(readl(target)); |
905 | val32 |= PHB_SLOT_DISABLE; |
906 | writel(cpu_to_be32(val32), target); |
907 | readl(target); /* flush */ |
908 | } else { |
909 | /* Reset the timer */ |
910 | mod_timer(&tbl->watchdog_timer, jiffies + 2 * HZ); |
911 | } |
912 | } |
913 | |
914 | static void __init calgary_set_split_completion_timeout(void __iomem *bbar, |
915 | unsigned char busnum, unsigned long timeout) |
916 | { |
917 | u64 val64; |
918 | void __iomem *target; |
919 | unsigned int phb_shift = ~0; /* silence gcc */ |
920 | u64 mask; |
921 | |
922 | switch (busno_to_phbid(busnum)) { |
923 | case 0: phb_shift = (63 - 19); |
924 | break; |
925 | case 1: phb_shift = (63 - 23); |
926 | break; |
927 | case 2: phb_shift = (63 - 27); |
928 | break; |
929 | case 3: phb_shift = (63 - 35); |
930 | break; |
931 | default: |
932 | BUG_ON(busno_to_phbid(busnum)); |
933 | } |
934 | |
935 | target = calgary_reg(bbar, CALGARY_CONFIG_REG); |
936 | val64 = be64_to_cpu(readq(target)); |
937 | |
938 | /* zero out this PHB's timer bits */ |
939 | mask = ~(0xFUL << phb_shift); |
940 | val64 &= mask; |
941 | val64 |= (timeout << phb_shift); |
942 | writeq(cpu_to_be64(val64), target); |
943 | readq(target); /* flush */ |
944 | } |
945 | |
946 | static void __init calioc2_handle_quirks(struct iommu_table *tbl, struct pci_dev *dev) |
947 | { |
948 | unsigned char busnum = dev->bus->number; |
949 | void __iomem *bbar = tbl->bbar; |
950 | void __iomem *target; |
951 | u32 val; |
952 | |
953 | /* |
954 | * CalIOC2 designers recommend setting bit 8 in 0xnDB0 to 1 |
955 | */ |
956 | target = calgary_reg(bbar, phb_offset(busnum) | PHB_SAVIOR_L2); |
957 | val = cpu_to_be32(readl(target)); |
958 | val |= 0x00800000; |
959 | writel(cpu_to_be32(val), target); |
960 | } |
961 | |
962 | static void __init calgary_handle_quirks(struct iommu_table *tbl, struct pci_dev *dev) |
963 | { |
964 | unsigned char busnum = dev->bus->number; |
965 | |
966 | /* |
967 | * Give split completion a longer timeout on bus 1 for aic94xx |
968 | * http://bugzilla.kernel.org/show_bug.cgi?id=7180 |
969 | */ |
970 | if (is_calgary(dev->device) && (busnum == 1)) |
971 | calgary_set_split_completion_timeout(tbl->bbar, busnum, |
972 | CCR_2SEC_TIMEOUT); |
973 | } |
974 | |
975 | static void __init calgary_enable_translation(struct pci_dev *dev) |
976 | { |
977 | u32 val32; |
978 | unsigned char busnum; |
979 | void __iomem *target; |
980 | void __iomem *bbar; |
981 | struct iommu_table *tbl; |
982 | |
983 | busnum = dev->bus->number; |
984 | tbl = pci_iommu(dev->bus); |
985 | bbar = tbl->bbar; |
986 | |
987 | /* enable TCE in PHB Config Register */ |
988 | target = calgary_reg(bbar, phb_offset(busnum) | PHB_CONFIG_RW_OFFSET); |
989 | val32 = be32_to_cpu(readl(target)); |
990 | val32 |= PHB_TCE_ENABLE | PHB_DAC_DISABLE | PHB_MCSR_ENABLE; |
991 | |
992 | printk(KERN_INFO "Calgary: enabling translation on %s PHB %#x\n" , |
993 | (dev->device == PCI_DEVICE_ID_IBM_CALGARY) ? |
994 | "Calgary" : "CalIOC2" , busnum); |
995 | printk(KERN_INFO "Calgary: errant DMAs will now be prevented on this " |
996 | "bus.\n" ); |
997 | |
998 | writel(cpu_to_be32(val32), target); |
999 | readl(target); /* flush */ |
1000 | |
1001 | timer_setup(&tbl->watchdog_timer, calgary_watchdog, 0); |
1002 | mod_timer(&tbl->watchdog_timer, jiffies); |
1003 | } |
1004 | |
1005 | static void __init calgary_disable_translation(struct pci_dev *dev) |
1006 | { |
1007 | u32 val32; |
1008 | unsigned char busnum; |
1009 | void __iomem *target; |
1010 | void __iomem *bbar; |
1011 | struct iommu_table *tbl; |
1012 | |
1013 | busnum = dev->bus->number; |
1014 | tbl = pci_iommu(dev->bus); |
1015 | bbar = tbl->bbar; |
1016 | |
1017 | /* disable TCE in PHB Config Register */ |
1018 | target = calgary_reg(bbar, phb_offset(busnum) | PHB_CONFIG_RW_OFFSET); |
1019 | val32 = be32_to_cpu(readl(target)); |
1020 | val32 &= ~(PHB_TCE_ENABLE | PHB_DAC_DISABLE | PHB_MCSR_ENABLE); |
1021 | |
1022 | printk(KERN_INFO "Calgary: disabling translation on PHB %#x!\n" , busnum); |
1023 | writel(cpu_to_be32(val32), target); |
1024 | readl(target); /* flush */ |
1025 | |
1026 | del_timer_sync(&tbl->watchdog_timer); |
1027 | } |
1028 | |
1029 | static void __init calgary_init_one_nontraslated(struct pci_dev *dev) |
1030 | { |
1031 | pci_dev_get(dev); |
1032 | set_pci_iommu(dev->bus, NULL); |
1033 | |
1034 | /* is the device behind a bridge? */ |
1035 | if (dev->bus->parent) |
1036 | dev->bus->parent->self = dev; |
1037 | else |
1038 | dev->bus->self = dev; |
1039 | } |
1040 | |
1041 | static int __init calgary_init_one(struct pci_dev *dev) |
1042 | { |
1043 | void __iomem *bbar; |
1044 | struct iommu_table *tbl; |
1045 | int ret; |
1046 | |
1047 | bbar = busno_to_bbar(dev->bus->number); |
1048 | ret = calgary_setup_tar(dev, bbar); |
1049 | if (ret) |
1050 | goto done; |
1051 | |
1052 | pci_dev_get(dev); |
1053 | |
1054 | if (dev->bus->parent) { |
1055 | if (dev->bus->parent->self) |
1056 | printk(KERN_WARNING "Calgary: IEEEE, dev %p has " |
1057 | "bus->parent->self!\n" , dev); |
1058 | dev->bus->parent->self = dev; |
1059 | } else |
1060 | dev->bus->self = dev; |
1061 | |
1062 | tbl = pci_iommu(dev->bus); |
1063 | tbl->chip_ops->handle_quirks(tbl, dev); |
1064 | |
1065 | calgary_enable_translation(dev); |
1066 | |
1067 | return 0; |
1068 | |
1069 | done: |
1070 | return ret; |
1071 | } |
1072 | |
1073 | static int __init calgary_locate_bbars(void) |
1074 | { |
1075 | int ret; |
1076 | int rioidx, phb, bus; |
1077 | void __iomem *bbar; |
1078 | void __iomem *target; |
1079 | unsigned long offset; |
1080 | u8 start_bus, end_bus; |
1081 | u32 val; |
1082 | |
1083 | ret = -ENODATA; |
1084 | for (rioidx = 0; rioidx < rio_table_hdr->num_rio_dev; rioidx++) { |
1085 | struct rio_detail *rio = rio_devs[rioidx]; |
1086 | |
1087 | if ((rio->type != COMPAT_CALGARY) && (rio->type != ALT_CALGARY)) |
1088 | continue; |
1089 | |
1090 | /* map entire 1MB of Calgary config space */ |
1091 | bbar = ioremap_nocache(rio->BBAR, 1024 * 1024); |
1092 | if (!bbar) |
1093 | goto error; |
1094 | |
1095 | for (phb = 0; phb < PHBS_PER_CALGARY; phb++) { |
1096 | offset = phb_debug_offsets[phb] | PHB_DEBUG_STUFF_OFFSET; |
1097 | target = calgary_reg(bbar, offset); |
1098 | |
1099 | val = be32_to_cpu(readl(target)); |
1100 | |
1101 | start_bus = (u8)((val & 0x00FF0000) >> 16); |
1102 | end_bus = (u8)((val & 0x0000FF00) >> 8); |
1103 | |
1104 | if (end_bus) { |
1105 | for (bus = start_bus; bus <= end_bus; bus++) { |
1106 | bus_info[bus].bbar = bbar; |
1107 | bus_info[bus].phbid = phb; |
1108 | } |
1109 | } else { |
1110 | bus_info[start_bus].bbar = bbar; |
1111 | bus_info[start_bus].phbid = phb; |
1112 | } |
1113 | } |
1114 | } |
1115 | |
1116 | return 0; |
1117 | |
1118 | error: |
1119 | /* scan bus_info and iounmap any bbars we previously ioremap'd */ |
1120 | for (bus = 0; bus < ARRAY_SIZE(bus_info); bus++) |
1121 | if (bus_info[bus].bbar) |
1122 | iounmap(bus_info[bus].bbar); |
1123 | |
1124 | return ret; |
1125 | } |
1126 | |
1127 | static int __init calgary_init(void) |
1128 | { |
1129 | int ret; |
1130 | struct pci_dev *dev = NULL; |
1131 | struct calgary_bus_info *info; |
1132 | |
1133 | ret = calgary_locate_bbars(); |
1134 | if (ret) |
1135 | return ret; |
1136 | |
1137 | /* Purely for kdump kernel case */ |
1138 | if (is_kdump_kernel()) |
1139 | get_tce_space_from_tar(); |
1140 | |
1141 | do { |
1142 | dev = pci_get_device(PCI_VENDOR_ID_IBM, PCI_ANY_ID, dev); |
1143 | if (!dev) |
1144 | break; |
1145 | if (!is_cal_pci_dev(dev->device)) |
1146 | continue; |
1147 | |
1148 | info = &bus_info[dev->bus->number]; |
1149 | if (info->translation_disabled) { |
1150 | calgary_init_one_nontraslated(dev); |
1151 | continue; |
1152 | } |
1153 | |
1154 | if (!info->tce_space && !translate_empty_slots) |
1155 | continue; |
1156 | |
1157 | ret = calgary_init_one(dev); |
1158 | if (ret) |
1159 | goto error; |
1160 | } while (1); |
1161 | |
1162 | dev = NULL; |
1163 | for_each_pci_dev(dev) { |
1164 | struct iommu_table *tbl; |
1165 | |
1166 | tbl = find_iommu_table(&dev->dev); |
1167 | |
1168 | if (translation_enabled(tbl)) |
1169 | dev->dev.dma_ops = &calgary_dma_ops; |
1170 | } |
1171 | |
1172 | return ret; |
1173 | |
1174 | error: |
1175 | do { |
1176 | dev = pci_get_device(PCI_VENDOR_ID_IBM, PCI_ANY_ID, dev); |
1177 | if (!dev) |
1178 | break; |
1179 | if (!is_cal_pci_dev(dev->device)) |
1180 | continue; |
1181 | |
1182 | info = &bus_info[dev->bus->number]; |
1183 | if (info->translation_disabled) { |
1184 | pci_dev_put(dev); |
1185 | continue; |
1186 | } |
1187 | if (!info->tce_space && !translate_empty_slots) |
1188 | continue; |
1189 | |
1190 | calgary_disable_translation(dev); |
1191 | calgary_free_bus(dev); |
1192 | pci_dev_put(dev); /* Undo calgary_init_one()'s pci_dev_get() */ |
1193 | dev->dev.dma_ops = NULL; |
1194 | } while (1); |
1195 | |
1196 | return ret; |
1197 | } |
1198 | |
1199 | static inline int __init determine_tce_table_size(void) |
1200 | { |
1201 | int ret; |
1202 | |
1203 | if (specified_table_size != TCE_TABLE_SIZE_UNSPECIFIED) |
1204 | return specified_table_size; |
1205 | |
1206 | if (is_kdump_kernel() && saved_max_pfn) { |
1207 | /* |
1208 | * Table sizes are from 0 to 7 (TCE_TABLE_SIZE_64K to |
1209 | * TCE_TABLE_SIZE_8M). Table size 0 has 8K entries and each |
1210 | * larger table size has twice as many entries, so shift the |
1211 | * max ram address by 13 to divide by 8K and then look at the |
1212 | * order of the result to choose between 0-7. |
1213 | */ |
1214 | ret = get_order((saved_max_pfn * PAGE_SIZE) >> 13); |
1215 | if (ret > TCE_TABLE_SIZE_8M) |
1216 | ret = TCE_TABLE_SIZE_8M; |
1217 | } else { |
1218 | /* |
1219 | * Use 8M by default (suggested by Muli) if it's not |
1220 | * kdump kernel and saved_max_pfn isn't set. |
1221 | */ |
1222 | ret = TCE_TABLE_SIZE_8M; |
1223 | } |
1224 | |
1225 | return ret; |
1226 | } |
1227 | |
1228 | static int __init build_detail_arrays(void) |
1229 | { |
1230 | unsigned long ptr; |
1231 | unsigned numnodes, i; |
1232 | int scal_detail_size, rio_detail_size; |
1233 | |
1234 | numnodes = rio_table_hdr->num_scal_dev; |
1235 | if (numnodes > MAX_NUMNODES){ |
1236 | printk(KERN_WARNING |
1237 | "Calgary: MAX_NUMNODES too low! Defined as %d, " |
1238 | "but system has %d nodes.\n" , |
1239 | MAX_NUMNODES, numnodes); |
1240 | return -ENODEV; |
1241 | } |
1242 | |
1243 | switch (rio_table_hdr->version){ |
1244 | case 2: |
1245 | scal_detail_size = 11; |
1246 | rio_detail_size = 13; |
1247 | break; |
1248 | case 3: |
1249 | scal_detail_size = 12; |
1250 | rio_detail_size = 15; |
1251 | break; |
1252 | default: |
1253 | printk(KERN_WARNING |
1254 | "Calgary: Invalid Rio Grande Table Version: %d\n" , |
1255 | rio_table_hdr->version); |
1256 | return -EPROTO; |
1257 | } |
1258 | |
1259 | ptr = ((unsigned long)rio_table_hdr) + 3; |
1260 | for (i = 0; i < numnodes; i++, ptr += scal_detail_size) |
1261 | scal_devs[i] = (struct scal_detail *)ptr; |
1262 | |
1263 | for (i = 0; i < rio_table_hdr->num_rio_dev; |
1264 | i++, ptr += rio_detail_size) |
1265 | rio_devs[i] = (struct rio_detail *)ptr; |
1266 | |
1267 | return 0; |
1268 | } |
1269 | |
1270 | static int __init calgary_bus_has_devices(int bus, unsigned short pci_dev) |
1271 | { |
1272 | int dev; |
1273 | u32 val; |
1274 | |
1275 | if (pci_dev == PCI_DEVICE_ID_IBM_CALIOC2) { |
1276 | /* |
1277 | * FIXME: properly scan for devices across the |
1278 | * PCI-to-PCI bridge on every CalIOC2 port. |
1279 | */ |
1280 | return 1; |
1281 | } |
1282 | |
1283 | for (dev = 1; dev < 8; dev++) { |
1284 | val = read_pci_config(bus, dev, 0, 0); |
1285 | if (val != 0xffffffff) |
1286 | break; |
1287 | } |
1288 | return (val != 0xffffffff); |
1289 | } |
1290 | |
1291 | /* |
1292 | * calgary_init_bitmap_from_tce_table(): |
1293 | * Function for kdump case. In the second/kdump kernel initialize |
1294 | * the bitmap based on the tce table entries obtained from first kernel |
1295 | */ |
1296 | static void calgary_init_bitmap_from_tce_table(struct iommu_table *tbl) |
1297 | { |
1298 | u64 *tp; |
1299 | unsigned int index; |
1300 | tp = ((u64 *)tbl->it_base); |
1301 | for (index = 0 ; index < tbl->it_size; index++) { |
1302 | if (*tp != 0x0) |
1303 | set_bit(index, tbl->it_map); |
1304 | tp++; |
1305 | } |
1306 | } |
1307 | |
1308 | /* |
1309 | * get_tce_space_from_tar(): |
1310 | * Function for kdump case. Get the tce tables from first kernel |
1311 | * by reading the contents of the base address register of calgary iommu |
1312 | */ |
1313 | static void __init get_tce_space_from_tar(void) |
1314 | { |
1315 | int bus; |
1316 | void __iomem *target; |
1317 | unsigned long tce_space; |
1318 | |
1319 | for (bus = 0; bus < MAX_PHB_BUS_NUM; bus++) { |
1320 | struct calgary_bus_info *info = &bus_info[bus]; |
1321 | unsigned short pci_device; |
1322 | u32 val; |
1323 | |
1324 | val = read_pci_config(bus, 0, 0, 0); |
1325 | pci_device = (val & 0xFFFF0000) >> 16; |
1326 | |
1327 | if (!is_cal_pci_dev(pci_device)) |
1328 | continue; |
1329 | if (info->translation_disabled) |
1330 | continue; |
1331 | |
1332 | if (calgary_bus_has_devices(bus, pci_device) || |
1333 | translate_empty_slots) { |
1334 | target = calgary_reg(bus_info[bus].bbar, |
1335 | tar_offset(bus)); |
1336 | tce_space = be64_to_cpu(readq(target)); |
1337 | tce_space = tce_space & TAR_SW_BITS; |
1338 | |
1339 | tce_space = tce_space & (~specified_table_size); |
1340 | info->tce_space = (u64 *)__va(tce_space); |
1341 | } |
1342 | } |
1343 | return; |
1344 | } |
1345 | |
1346 | static int __init calgary_iommu_init(void) |
1347 | { |
1348 | int ret; |
1349 | |
1350 | /* ok, we're trying to use Calgary - let's roll */ |
1351 | printk(KERN_INFO "PCI-DMA: Using Calgary IOMMU\n" ); |
1352 | |
1353 | ret = calgary_init(); |
1354 | if (ret) { |
1355 | printk(KERN_ERR "PCI-DMA: Calgary init failed %d, " |
1356 | "falling back to no_iommu\n" , ret); |
1357 | return ret; |
1358 | } |
1359 | |
1360 | return 0; |
1361 | } |
1362 | |
1363 | int __init detect_calgary(void) |
1364 | { |
1365 | int bus; |
1366 | void *tbl; |
1367 | int calgary_found = 0; |
1368 | unsigned long ptr; |
1369 | unsigned int offset, prev_offset; |
1370 | int ret; |
1371 | |
1372 | /* |
1373 | * if the user specified iommu=off or iommu=soft or we found |
1374 | * another HW IOMMU already, bail out. |
1375 | */ |
1376 | if (no_iommu || iommu_detected) |
1377 | return -ENODEV; |
1378 | |
1379 | if (!use_calgary) |
1380 | return -ENODEV; |
1381 | |
1382 | if (!early_pci_allowed()) |
1383 | return -ENODEV; |
1384 | |
1385 | printk(KERN_DEBUG "Calgary: detecting Calgary via BIOS EBDA area\n" ); |
1386 | |
1387 | ptr = (unsigned long)phys_to_virt(get_bios_ebda()); |
1388 | |
1389 | rio_table_hdr = NULL; |
1390 | prev_offset = 0; |
1391 | offset = 0x180; |
1392 | /* |
1393 | * The next offset is stored in the 1st word. |
1394 | * Only parse up until the offset increases: |
1395 | */ |
1396 | while (offset > prev_offset) { |
1397 | /* The block id is stored in the 2nd word */ |
1398 | if (*((unsigned short *)(ptr + offset + 2)) == 0x4752){ |
1399 | /* set the pointer past the offset & block id */ |
1400 | rio_table_hdr = (struct rio_table_hdr *)(ptr + offset + 4); |
1401 | break; |
1402 | } |
1403 | prev_offset = offset; |
1404 | offset = *((unsigned short *)(ptr + offset)); |
1405 | } |
1406 | if (!rio_table_hdr) { |
1407 | printk(KERN_DEBUG "Calgary: Unable to locate Rio Grande table " |
1408 | "in EBDA - bailing!\n" ); |
1409 | return -ENODEV; |
1410 | } |
1411 | |
1412 | ret = build_detail_arrays(); |
1413 | if (ret) { |
1414 | printk(KERN_DEBUG "Calgary: build_detail_arrays ret %d\n" , ret); |
1415 | return -ENOMEM; |
1416 | } |
1417 | |
1418 | specified_table_size = determine_tce_table_size(); |
1419 | |
1420 | for (bus = 0; bus < MAX_PHB_BUS_NUM; bus++) { |
1421 | struct calgary_bus_info *info = &bus_info[bus]; |
1422 | unsigned short pci_device; |
1423 | u32 val; |
1424 | |
1425 | val = read_pci_config(bus, 0, 0, 0); |
1426 | pci_device = (val & 0xFFFF0000) >> 16; |
1427 | |
1428 | if (!is_cal_pci_dev(pci_device)) |
1429 | continue; |
1430 | |
1431 | if (info->translation_disabled) |
1432 | continue; |
1433 | |
1434 | if (calgary_bus_has_devices(bus, pci_device) || |
1435 | translate_empty_slots) { |
1436 | /* |
1437 | * If it is kdump kernel, find and use tce tables |
1438 | * from first kernel, else allocate tce tables here |
1439 | */ |
1440 | if (!is_kdump_kernel()) { |
1441 | tbl = alloc_tce_table(); |
1442 | if (!tbl) |
1443 | goto cleanup; |
1444 | info->tce_space = tbl; |
1445 | } |
1446 | calgary_found = 1; |
1447 | } |
1448 | } |
1449 | |
1450 | printk(KERN_DEBUG "Calgary: finished detection, Calgary %s\n" , |
1451 | calgary_found ? "found" : "not found" ); |
1452 | |
1453 | if (calgary_found) { |
1454 | iommu_detected = 1; |
1455 | calgary_detected = 1; |
1456 | printk(KERN_INFO "PCI-DMA: Calgary IOMMU detected.\n" ); |
1457 | printk(KERN_INFO "PCI-DMA: Calgary TCE table spec is %d\n" , |
1458 | specified_table_size); |
1459 | |
1460 | x86_init.iommu.iommu_init = calgary_iommu_init; |
1461 | } |
1462 | return calgary_found; |
1463 | |
1464 | cleanup: |
1465 | for (--bus; bus >= 0; --bus) { |
1466 | struct calgary_bus_info *info = &bus_info[bus]; |
1467 | |
1468 | if (info->tce_space) |
1469 | free_tce_table(info->tce_space); |
1470 | } |
1471 | return -ENOMEM; |
1472 | } |
1473 | |
1474 | static int __init calgary_parse_options(char *p) |
1475 | { |
1476 | unsigned int bridge; |
1477 | unsigned long val; |
1478 | size_t len; |
1479 | ssize_t ret; |
1480 | |
1481 | while (*p) { |
1482 | if (!strncmp(p, "64k" , 3)) |
1483 | specified_table_size = TCE_TABLE_SIZE_64K; |
1484 | else if (!strncmp(p, "128k" , 4)) |
1485 | specified_table_size = TCE_TABLE_SIZE_128K; |
1486 | else if (!strncmp(p, "256k" , 4)) |
1487 | specified_table_size = TCE_TABLE_SIZE_256K; |
1488 | else if (!strncmp(p, "512k" , 4)) |
1489 | specified_table_size = TCE_TABLE_SIZE_512K; |
1490 | else if (!strncmp(p, "1M" , 2)) |
1491 | specified_table_size = TCE_TABLE_SIZE_1M; |
1492 | else if (!strncmp(p, "2M" , 2)) |
1493 | specified_table_size = TCE_TABLE_SIZE_2M; |
1494 | else if (!strncmp(p, "4M" , 2)) |
1495 | specified_table_size = TCE_TABLE_SIZE_4M; |
1496 | else if (!strncmp(p, "8M" , 2)) |
1497 | specified_table_size = TCE_TABLE_SIZE_8M; |
1498 | |
1499 | len = strlen("translate_empty_slots" ); |
1500 | if (!strncmp(p, "translate_empty_slots" , len)) |
1501 | translate_empty_slots = 1; |
1502 | |
1503 | len = strlen("disable" ); |
1504 | if (!strncmp(p, "disable" , len)) { |
1505 | p += len; |
1506 | if (*p == '=') |
1507 | ++p; |
1508 | if (*p == '\0') |
1509 | break; |
1510 | ret = kstrtoul(p, 0, &val); |
1511 | if (ret) |
1512 | break; |
1513 | |
1514 | bridge = val; |
1515 | if (bridge < MAX_PHB_BUS_NUM) { |
1516 | printk(KERN_INFO "Calgary: disabling " |
1517 | "translation for PHB %#x\n" , bridge); |
1518 | bus_info[bridge].translation_disabled = 1; |
1519 | } |
1520 | } |
1521 | |
1522 | p = strpbrk(p, "," ); |
1523 | if (!p) |
1524 | break; |
1525 | |
1526 | p++; /* skip ',' */ |
1527 | } |
1528 | return 1; |
1529 | } |
1530 | __setup("calgary=" , calgary_parse_options); |
1531 | |
1532 | static void __init calgary_fixup_one_tce_space(struct pci_dev *dev) |
1533 | { |
1534 | struct iommu_table *tbl; |
1535 | unsigned int npages; |
1536 | int i; |
1537 | |
1538 | tbl = pci_iommu(dev->bus); |
1539 | |
1540 | for (i = 0; i < 4; i++) { |
1541 | struct resource *r = &dev->resource[PCI_BRIDGE_RESOURCES + i]; |
1542 | |
1543 | /* Don't give out TCEs that map MEM resources */ |
1544 | if (!(r->flags & IORESOURCE_MEM)) |
1545 | continue; |
1546 | |
1547 | /* 0-based? we reserve the whole 1st MB anyway */ |
1548 | if (!r->start) |
1549 | continue; |
1550 | |
1551 | /* cover the whole region */ |
1552 | npages = resource_size(r) >> PAGE_SHIFT; |
1553 | npages++; |
1554 | |
1555 | iommu_range_reserve(tbl, r->start, npages); |
1556 | } |
1557 | } |
1558 | |
1559 | static int __init calgary_fixup_tce_spaces(void) |
1560 | { |
1561 | struct pci_dev *dev = NULL; |
1562 | struct calgary_bus_info *info; |
1563 | |
1564 | if (no_iommu || swiotlb || !calgary_detected) |
1565 | return -ENODEV; |
1566 | |
1567 | printk(KERN_DEBUG "Calgary: fixing up tce spaces\n" ); |
1568 | |
1569 | do { |
1570 | dev = pci_get_device(PCI_VENDOR_ID_IBM, PCI_ANY_ID, dev); |
1571 | if (!dev) |
1572 | break; |
1573 | if (!is_cal_pci_dev(dev->device)) |
1574 | continue; |
1575 | |
1576 | info = &bus_info[dev->bus->number]; |
1577 | if (info->translation_disabled) |
1578 | continue; |
1579 | |
1580 | if (!info->tce_space) |
1581 | continue; |
1582 | |
1583 | calgary_fixup_one_tce_space(dev); |
1584 | |
1585 | } while (1); |
1586 | |
1587 | return 0; |
1588 | } |
1589 | |
1590 | /* |
1591 | * We need to be call after pcibios_assign_resources (fs_initcall level) |
1592 | * and before device_initcall. |
1593 | */ |
1594 | rootfs_initcall(calgary_fixup_tce_spaces); |
1595 | |
1596 | IOMMU_INIT_POST(detect_calgary); |
1597 | |