blob: afc0a53505f1cbafb3ee6544ea94c879984cbb0a [file] [log] [blame]
Jon Masone4650582006-06-26 13:58:14 +02001/*
2 * Derived from arch/powerpc/kernel/iommu.c
3 *
Muli Ben-Yehudaaa0a9f32006-07-10 17:06:15 +02004 * Copyright (C) IBM Corporation, 2006
Jon Masond8d2bed2006-10-05 18:47:21 +02005 * Copyright (C) 2006 Jon Mason <jdmason@kudzu.us>
Jon Masone4650582006-06-26 13:58:14 +02006 *
Jon Masond8d2bed2006-10-05 18:47:21 +02007 * Author: Jon Mason <jdmason@kudzu.us>
Muli Ben-Yehudaaa0a9f32006-07-10 17:06:15 +02008 * Author: Muli Ben-Yehuda <muli@il.ibm.com>
9
Jon Masone4650582006-06-26 13:58:14 +020010 * 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
Jon Masone4650582006-06-26 13:58:14 +020025#include <linux/kernel.h>
26#include <linux/init.h>
27#include <linux/types.h>
28#include <linux/slab.h>
29#include <linux/mm.h>
30#include <linux/spinlock.h>
31#include <linux/string.h>
32#include <linux/dma-mapping.h>
33#include <linux/init.h>
34#include <linux/bitops.h>
35#include <linux/pci_ids.h>
36#include <linux/pci.h>
37#include <linux/delay.h>
38#include <asm/proto.h>
39#include <asm/calgary.h>
40#include <asm/tce.h>
41#include <asm/pci-direct.h>
42#include <asm/system.h>
43#include <asm/dma.h>
Laurent Vivierb34e90b2006-12-07 02:14:06 +010044#include <asm/rio.h>
Jon Masone4650582006-06-26 13:58:14 +020045
46#define PCI_DEVICE_ID_IBM_CALGARY 0x02a1
47#define PCI_VENDOR_DEVICE_ID_CALGARY \
48 (PCI_VENDOR_ID_IBM | PCI_DEVICE_ID_IBM_CALGARY << 16)
49
50/* we need these for register space address calculation */
51#define START_ADDRESS 0xfe000000
52#define CHASSIS_BASE 0
53#define ONE_BASED_CHASSIS_NUM 1
54
55/* register offsets inside the host bridge space */
Muli Ben-Yehudacb01fc72006-10-22 00:41:15 +020056#define CALGARY_CONFIG_REG 0x0108
57#define PHB_CSR_OFFSET 0x0110 /* Channel Status */
Jon Masone4650582006-06-26 13:58:14 +020058#define PHB_PLSSR_OFFSET 0x0120
59#define PHB_CONFIG_RW_OFFSET 0x0160
60#define PHB_IOBASE_BAR_LOW 0x0170
61#define PHB_IOBASE_BAR_HIGH 0x0180
62#define PHB_MEM_1_LOW 0x0190
63#define PHB_MEM_1_HIGH 0x01A0
64#define PHB_IO_ADDR_SIZE 0x01B0
65#define PHB_MEM_1_SIZE 0x01C0
66#define PHB_MEM_ST_OFFSET 0x01D0
67#define PHB_AER_OFFSET 0x0200
68#define PHB_CONFIG_0_HIGH 0x0220
69#define PHB_CONFIG_0_LOW 0x0230
70#define PHB_CONFIG_0_END 0x0240
71#define PHB_MEM_2_LOW 0x02B0
72#define PHB_MEM_2_HIGH 0x02C0
73#define PHB_MEM_2_SIZE_HIGH 0x02D0
74#define PHB_MEM_2_SIZE_LOW 0x02E0
75#define PHB_DOSHOLE_OFFSET 0x08E0
76
77/* PHB_CONFIG_RW */
78#define PHB_TCE_ENABLE 0x20000000
79#define PHB_SLOT_DISABLE 0x1C000000
80#define PHB_DAC_DISABLE 0x01000000
81#define PHB_MEM2_ENABLE 0x00400000
82#define PHB_MCSR_ENABLE 0x00100000
83/* TAR (Table Address Register) */
84#define TAR_SW_BITS 0x0000ffffffff800fUL
85#define TAR_VALID 0x0000000000000008UL
86/* CSR (Channel/DMA Status Register) */
87#define CSR_AGENT_MASK 0xffe0ffff
Muli Ben-Yehudacb01fc72006-10-22 00:41:15 +020088/* CCR (Calgary Configuration Register) */
89#define CCR_2SEC_TIMEOUT 0x000000000000000EUL
Jon Masone4650582006-06-26 13:58:14 +020090
91#define MAX_NUM_OF_PHBS 8 /* how many PHBs in total? */
Jon Masond2105b12006-07-29 21:42:43 +020092#define MAX_NUM_CHASSIS 8 /* max number of chassis */
Muli Ben-Yehuda4ea8a5d2006-09-26 10:52:33 +020093/* MAX_PHB_BUS_NUM is the maximal possible dev->bus->number */
94#define MAX_PHB_BUS_NUM (MAX_NUM_OF_PHBS * MAX_NUM_CHASSIS * 2)
Jon Masone4650582006-06-26 13:58:14 +020095#define PHBS_PER_CALGARY 4
96
97/* register offsets in Calgary's internal register space */
98static const unsigned long tar_offsets[] = {
99 0x0580 /* TAR0 */,
100 0x0588 /* TAR1 */,
101 0x0590 /* TAR2 */,
102 0x0598 /* TAR3 */
103};
104
105static const unsigned long split_queue_offsets[] = {
106 0x4870 /* SPLIT QUEUE 0 */,
107 0x5870 /* SPLIT QUEUE 1 */,
108 0x6870 /* SPLIT QUEUE 2 */,
109 0x7870 /* SPLIT QUEUE 3 */
110};
111
112static const unsigned long phb_offsets[] = {
113 0x8000 /* PHB0 */,
114 0x9000 /* PHB1 */,
115 0xA000 /* PHB2 */,
116 0xB000 /* PHB3 */
117};
118
Laurent Vivierb34e90b2006-12-07 02:14:06 +0100119/* PHB debug registers */
120
121static const unsigned long phb_debug_offsets[] = {
122 0x4000 /* PHB 0 DEBUG */,
123 0x5000 /* PHB 1 DEBUG */,
124 0x6000 /* PHB 2 DEBUG */,
125 0x7000 /* PHB 3 DEBUG */
126};
127
128/*
129 * STUFF register for each debug PHB,
130 * byte 1 = start bus number, byte 2 = end bus number
131 */
132
133#define PHB_DEBUG_STUFF_OFFSET 0x0020
134
Jon Masone4650582006-06-26 13:58:14 +0200135unsigned int specified_table_size = TCE_TABLE_SIZE_UNSPECIFIED;
136static int translate_empty_slots __read_mostly = 0;
137static int calgary_detected __read_mostly = 0;
138
Laurent Vivierb34e90b2006-12-07 02:14:06 +0100139static struct rio_table_hdr *rio_table_hdr __initdata;
140static struct scal_detail *scal_devs[MAX_NUMNODES] __initdata;
141static struct rio_detail *rio_devs[MAX_NUMNODES*4] __initdata;
142
Muli Ben-Yehudaf38db652006-09-26 10:52:31 +0200143struct calgary_bus_info {
144 void *tce_space;
Muli Ben-Yehuda0577f142006-09-26 10:52:31 +0200145 unsigned char translation_disabled;
Muli Ben-Yehudaf38db652006-09-26 10:52:31 +0200146 signed char phbid;
Laurent Vivierb34e90b2006-12-07 02:14:06 +0100147 void __iomem *bbar;
Muli Ben-Yehudaf38db652006-09-26 10:52:31 +0200148};
149
150static struct calgary_bus_info bus_info[MAX_PHB_BUS_NUM] = { { NULL, 0, 0 }, };
Jon Masone4650582006-06-26 13:58:14 +0200151
152static void tce_cache_blast(struct iommu_table *tbl);
153
154/* enable this to stress test the chip's TCE cache */
155#ifdef CONFIG_IOMMU_DEBUG
Muli Ben-Yehudade684652006-09-26 10:52:33 +0200156int debugging __read_mostly = 1;
157
Muli Ben-Yehuda796e4392006-09-26 10:52:33 +0200158static inline unsigned long verify_bit_range(unsigned long* bitmap,
159 int expected, unsigned long start, unsigned long end)
160{
161 unsigned long idx = start;
162
163 BUG_ON(start >= end);
164
165 while (idx < end) {
166 if (!!test_bit(idx, bitmap) != expected)
167 return idx;
168 ++idx;
169 }
170
171 /* all bits have the expected value */
172 return ~0UL;
173}
Muli Ben-Yehudade684652006-09-26 10:52:33 +0200174#else /* debugging is disabled */
175int debugging __read_mostly = 0;
176
Muli Ben-Yehuda796e4392006-09-26 10:52:33 +0200177static inline unsigned long verify_bit_range(unsigned long* bitmap,
178 int expected, unsigned long start, unsigned long end)
179{
180 return ~0UL;
181}
Muli Ben-Yehudade684652006-09-26 10:52:33 +0200182#endif /* CONFIG_IOMMU_DEBUG */
Jon Masone4650582006-06-26 13:58:14 +0200183
184static inline unsigned int num_dma_pages(unsigned long dma, unsigned int dmalen)
185{
186 unsigned int npages;
187
188 npages = PAGE_ALIGN(dma + dmalen) - (dma & PAGE_MASK);
189 npages >>= PAGE_SHIFT;
190
191 return npages;
192}
193
194static inline int translate_phb(struct pci_dev* dev)
195{
Muli Ben-Yehudaf38db652006-09-26 10:52:31 +0200196 int disabled = bus_info[dev->bus->number].translation_disabled;
Jon Masone4650582006-06-26 13:58:14 +0200197 return !disabled;
198}
199
200static void iommu_range_reserve(struct iommu_table *tbl,
201 unsigned long start_addr, unsigned int npages)
202{
203 unsigned long index;
204 unsigned long end;
Muli Ben-Yehuda796e4392006-09-26 10:52:33 +0200205 unsigned long badbit;
Jon Masone4650582006-06-26 13:58:14 +0200206
207 index = start_addr >> PAGE_SHIFT;
208
209 /* bail out if we're asked to reserve a region we don't cover */
210 if (index >= tbl->it_size)
211 return;
212
213 end = index + npages;
214 if (end > tbl->it_size) /* don't go off the table */
215 end = tbl->it_size;
216
Muli Ben-Yehuda796e4392006-09-26 10:52:33 +0200217 badbit = verify_bit_range(tbl->it_map, 0, index, end);
218 if (badbit != ~0UL) {
219 if (printk_ratelimit())
Jon Masone4650582006-06-26 13:58:14 +0200220 printk(KERN_ERR "Calgary: entry already allocated at "
221 "0x%lx tbl %p dma 0x%lx npages %u\n",
Muli Ben-Yehuda796e4392006-09-26 10:52:33 +0200222 badbit, tbl, start_addr, npages);
Jon Masone4650582006-06-26 13:58:14 +0200223 }
Muli Ben-Yehuda796e4392006-09-26 10:52:33 +0200224
225 set_bit_string(tbl->it_map, index, npages);
Jon Masone4650582006-06-26 13:58:14 +0200226}
227
228static unsigned long iommu_range_alloc(struct iommu_table *tbl,
229 unsigned int npages)
230{
231 unsigned long offset;
232
233 BUG_ON(npages == 0);
234
235 offset = find_next_zero_string(tbl->it_map, tbl->it_hint,
236 tbl->it_size, npages);
237 if (offset == ~0UL) {
238 tce_cache_blast(tbl);
239 offset = find_next_zero_string(tbl->it_map, 0,
240 tbl->it_size, npages);
241 if (offset == ~0UL) {
242 printk(KERN_WARNING "Calgary: IOMMU full.\n");
243 if (panic_on_overflow)
244 panic("Calgary: fix the allocator.\n");
245 else
246 return bad_dma_address;
247 }
248 }
249
250 set_bit_string(tbl->it_map, offset, npages);
251 tbl->it_hint = offset + npages;
252 BUG_ON(tbl->it_hint > tbl->it_size);
253
254 return offset;
255}
256
257static dma_addr_t iommu_alloc(struct iommu_table *tbl, void *vaddr,
258 unsigned int npages, int direction)
259{
260 unsigned long entry, flags;
261 dma_addr_t ret = bad_dma_address;
262
263 spin_lock_irqsave(&tbl->it_lock, flags);
264
265 entry = iommu_range_alloc(tbl, npages);
266
267 if (unlikely(entry == bad_dma_address))
268 goto error;
269
270 /* set the return dma address */
271 ret = (entry << PAGE_SHIFT) | ((unsigned long)vaddr & ~PAGE_MASK);
272
273 /* put the TCEs in the HW table */
274 tce_build(tbl, entry, npages, (unsigned long)vaddr & PAGE_MASK,
275 direction);
276
277 spin_unlock_irqrestore(&tbl->it_lock, flags);
278
279 return ret;
280
281error:
282 spin_unlock_irqrestore(&tbl->it_lock, flags);
283 printk(KERN_WARNING "Calgary: failed to allocate %u pages in "
284 "iommu %p\n", npages, tbl);
285 return bad_dma_address;
286}
287
288static void __iommu_free(struct iommu_table *tbl, dma_addr_t dma_addr,
289 unsigned int npages)
290{
291 unsigned long entry;
Muli Ben-Yehuda796e4392006-09-26 10:52:33 +0200292 unsigned long badbit;
Jon Masone4650582006-06-26 13:58:14 +0200293
294 entry = dma_addr >> PAGE_SHIFT;
295
296 BUG_ON(entry + npages > tbl->it_size);
297
298 tce_free(tbl, entry, npages);
299
Muli Ben-Yehuda796e4392006-09-26 10:52:33 +0200300 badbit = verify_bit_range(tbl->it_map, 1, entry, entry + npages);
301 if (badbit != ~0UL) {
302 if (printk_ratelimit())
Jon Masone4650582006-06-26 13:58:14 +0200303 printk(KERN_ERR "Calgary: bit is off at 0x%lx "
304 "tbl %p dma 0x%Lx entry 0x%lx npages %u\n",
Muli Ben-Yehuda796e4392006-09-26 10:52:33 +0200305 badbit, tbl, dma_addr, entry, npages);
Jon Masone4650582006-06-26 13:58:14 +0200306 }
307
308 __clear_bit_string(tbl->it_map, entry, npages);
Jon Masone4650582006-06-26 13:58:14 +0200309}
310
311static void iommu_free(struct iommu_table *tbl, dma_addr_t dma_addr,
312 unsigned int npages)
313{
314 unsigned long flags;
315
316 spin_lock_irqsave(&tbl->it_lock, flags);
317
318 __iommu_free(tbl, dma_addr, npages);
319
320 spin_unlock_irqrestore(&tbl->it_lock, flags);
321}
322
323static void __calgary_unmap_sg(struct iommu_table *tbl,
324 struct scatterlist *sglist, int nelems, int direction)
325{
326 while (nelems--) {
327 unsigned int npages;
328 dma_addr_t dma = sglist->dma_address;
329 unsigned int dmalen = sglist->dma_length;
330
331 if (dmalen == 0)
332 break;
333
334 npages = num_dma_pages(dma, dmalen);
335 __iommu_free(tbl, dma, npages);
336 sglist++;
337 }
338}
339
340void calgary_unmap_sg(struct device *dev, struct scatterlist *sglist,
341 int nelems, int direction)
342{
343 unsigned long flags;
344 struct iommu_table *tbl = to_pci_dev(dev)->bus->self->sysdata;
345
346 if (!translate_phb(to_pci_dev(dev)))
347 return;
348
349 spin_lock_irqsave(&tbl->it_lock, flags);
350
351 __calgary_unmap_sg(tbl, sglist, nelems, direction);
352
353 spin_unlock_irqrestore(&tbl->it_lock, flags);
354}
355
356static int calgary_nontranslate_map_sg(struct device* dev,
357 struct scatterlist *sg, int nelems, int direction)
358{
359 int i;
360
361 for (i = 0; i < nelems; i++ ) {
362 struct scatterlist *s = &sg[i];
363 BUG_ON(!s->page);
364 s->dma_address = virt_to_bus(page_address(s->page) +s->offset);
365 s->dma_length = s->length;
366 }
367 return nelems;
368}
369
370int calgary_map_sg(struct device *dev, struct scatterlist *sg,
371 int nelems, int direction)
372{
373 struct iommu_table *tbl = to_pci_dev(dev)->bus->self->sysdata;
374 unsigned long flags;
375 unsigned long vaddr;
376 unsigned int npages;
377 unsigned long entry;
378 int i;
379
380 if (!translate_phb(to_pci_dev(dev)))
381 return calgary_nontranslate_map_sg(dev, sg, nelems, direction);
382
383 spin_lock_irqsave(&tbl->it_lock, flags);
384
385 for (i = 0; i < nelems; i++ ) {
386 struct scatterlist *s = &sg[i];
387 BUG_ON(!s->page);
388
389 vaddr = (unsigned long)page_address(s->page) + s->offset;
390 npages = num_dma_pages(vaddr, s->length);
391
392 entry = iommu_range_alloc(tbl, npages);
393 if (entry == bad_dma_address) {
394 /* makes sure unmap knows to stop */
395 s->dma_length = 0;
396 goto error;
397 }
398
399 s->dma_address = (entry << PAGE_SHIFT) | s->offset;
400
401 /* insert into HW table */
402 tce_build(tbl, entry, npages, vaddr & PAGE_MASK,
403 direction);
404
405 s->dma_length = s->length;
406 }
407
408 spin_unlock_irqrestore(&tbl->it_lock, flags);
409
410 return nelems;
411error:
412 __calgary_unmap_sg(tbl, sg, nelems, direction);
413 for (i = 0; i < nelems; i++) {
414 sg[i].dma_address = bad_dma_address;
415 sg[i].dma_length = 0;
416 }
417 spin_unlock_irqrestore(&tbl->it_lock, flags);
418 return 0;
419}
420
421dma_addr_t calgary_map_single(struct device *dev, void *vaddr,
422 size_t size, int direction)
423{
424 dma_addr_t dma_handle = bad_dma_address;
425 unsigned long uaddr;
426 unsigned int npages;
427 struct iommu_table *tbl = to_pci_dev(dev)->bus->self->sysdata;
428
429 uaddr = (unsigned long)vaddr;
430 npages = num_dma_pages(uaddr, size);
431
432 if (translate_phb(to_pci_dev(dev)))
433 dma_handle = iommu_alloc(tbl, vaddr, npages, direction);
434 else
435 dma_handle = virt_to_bus(vaddr);
436
437 return dma_handle;
438}
439
440void calgary_unmap_single(struct device *dev, dma_addr_t dma_handle,
441 size_t size, int direction)
442{
443 struct iommu_table *tbl = to_pci_dev(dev)->bus->self->sysdata;
444 unsigned int npages;
445
446 if (!translate_phb(to_pci_dev(dev)))
447 return;
448
449 npages = num_dma_pages(dma_handle, size);
450 iommu_free(tbl, dma_handle, npages);
451}
452
453void* calgary_alloc_coherent(struct device *dev, size_t size,
454 dma_addr_t *dma_handle, gfp_t flag)
455{
456 void *ret = NULL;
457 dma_addr_t mapping;
458 unsigned int npages, order;
459 struct iommu_table *tbl;
460
461 tbl = to_pci_dev(dev)->bus->self->sysdata;
462
463 size = PAGE_ALIGN(size); /* size rounded up to full pages */
464 npages = size >> PAGE_SHIFT;
465 order = get_order(size);
466
467 /* alloc enough pages (and possibly more) */
468 ret = (void *)__get_free_pages(flag, order);
469 if (!ret)
470 goto error;
471 memset(ret, 0, size);
472
473 if (translate_phb(to_pci_dev(dev))) {
474 /* set up tces to cover the allocated range */
475 mapping = iommu_alloc(tbl, ret, npages, DMA_BIDIRECTIONAL);
476 if (mapping == bad_dma_address)
477 goto free;
478
479 *dma_handle = mapping;
480 } else /* non translated slot */
481 *dma_handle = virt_to_bus(ret);
482
483 return ret;
484
485free:
486 free_pages((unsigned long)ret, get_order(size));
487 ret = NULL;
488error:
489 return ret;
490}
491
492static struct dma_mapping_ops calgary_dma_ops = {
493 .alloc_coherent = calgary_alloc_coherent,
494 .map_single = calgary_map_single,
495 .unmap_single = calgary_unmap_single,
496 .map_sg = calgary_map_sg,
497 .unmap_sg = calgary_unmap_sg,
498};
499
Laurent Vivierb34e90b2006-12-07 02:14:06 +0100500static inline void __iomem * busno_to_bbar(unsigned char num)
501{
502 return bus_info[num].bbar;
503}
504
Jon Masone4650582006-06-26 13:58:14 +0200505static inline int busno_to_phbid(unsigned char num)
506{
Muli Ben-Yehudaf38db652006-09-26 10:52:31 +0200507 return bus_info[num].phbid;
Jon Masone4650582006-06-26 13:58:14 +0200508}
509
510static inline unsigned long split_queue_offset(unsigned char num)
511{
512 size_t idx = busno_to_phbid(num);
513
514 return split_queue_offsets[idx];
515}
516
517static inline unsigned long tar_offset(unsigned char num)
518{
519 size_t idx = busno_to_phbid(num);
520
521 return tar_offsets[idx];
522}
523
524static inline unsigned long phb_offset(unsigned char num)
525{
526 size_t idx = busno_to_phbid(num);
527
528 return phb_offsets[idx];
529}
530
531static inline void __iomem* calgary_reg(void __iomem *bar, unsigned long offset)
532{
533 unsigned long target = ((unsigned long)bar) | offset;
534 return (void __iomem*)target;
535}
536
537static void 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 printk(KERN_WARNING "Calgary: PCI bus not quiesced, "
562 "continuing anyway\n");
563
564 /* invalidate TCE cache */
565 target = calgary_reg(bbar, tar_offset(tbl->it_busno));
566 writeq(tbl->tar_val, target);
567
568 /* enable arbitration */
569 target = calgary_reg(bbar, phb_offset(tbl->it_busno) | PHB_AER_OFFSET);
570 writel(aer, target);
571 (void)readl(target); /* flush */
572}
573
574static void __init calgary_reserve_mem_region(struct pci_dev *dev, u64 start,
575 u64 limit)
576{
577 unsigned int numpages;
578
579 limit = limit | 0xfffff;
580 limit++;
581
582 numpages = ((limit - start) >> PAGE_SHIFT);
583 iommu_range_reserve(dev->sysdata, start, numpages);
584}
585
586static void __init calgary_reserve_peripheral_mem_1(struct pci_dev *dev)
587{
588 void __iomem *target;
589 u64 low, high, sizelow;
590 u64 start, limit;
591 struct iommu_table *tbl = dev->sysdata;
592 unsigned char busnum = dev->bus->number;
593 void __iomem *bbar = tbl->bbar;
594
595 /* peripheral MEM_1 region */
596 target = calgary_reg(bbar, phb_offset(busnum) | PHB_MEM_1_LOW);
597 low = be32_to_cpu(readl(target));
598 target = calgary_reg(bbar, phb_offset(busnum) | PHB_MEM_1_HIGH);
599 high = be32_to_cpu(readl(target));
600 target = calgary_reg(bbar, phb_offset(busnum) | PHB_MEM_1_SIZE);
601 sizelow = be32_to_cpu(readl(target));
602
603 start = (high << 32) | low;
604 limit = sizelow;
605
606 calgary_reserve_mem_region(dev, start, limit);
607}
608
609static void __init calgary_reserve_peripheral_mem_2(struct pci_dev *dev)
610{
611 void __iomem *target;
612 u32 val32;
613 u64 low, high, sizelow, sizehigh;
614 u64 start, limit;
615 struct iommu_table *tbl = dev->sysdata;
616 unsigned char busnum = dev->bus->number;
617 void __iomem *bbar = tbl->bbar;
618
619 /* is it enabled? */
620 target = calgary_reg(bbar, phb_offset(busnum) | PHB_CONFIG_RW_OFFSET);
621 val32 = be32_to_cpu(readl(target));
622 if (!(val32 & PHB_MEM2_ENABLE))
623 return;
624
625 target = calgary_reg(bbar, phb_offset(busnum) | PHB_MEM_2_LOW);
626 low = be32_to_cpu(readl(target));
627 target = calgary_reg(bbar, phb_offset(busnum) | PHB_MEM_2_HIGH);
628 high = be32_to_cpu(readl(target));
629 target = calgary_reg(bbar, phb_offset(busnum) | PHB_MEM_2_SIZE_LOW);
630 sizelow = be32_to_cpu(readl(target));
631 target = calgary_reg(bbar, phb_offset(busnum) | PHB_MEM_2_SIZE_HIGH);
632 sizehigh = be32_to_cpu(readl(target));
633
634 start = (high << 32) | low;
635 limit = (sizehigh << 32) | sizelow;
636
637 calgary_reserve_mem_region(dev, start, limit);
638}
639
640/*
641 * some regions of the IO address space do not get translated, so we
642 * must not give devices IO addresses in those regions. The regions
643 * are the 640KB-1MB region and the two PCI peripheral memory holes.
644 * Reserve all of them in the IOMMU bitmap to avoid giving them out
645 * later.
646 */
647static void __init calgary_reserve_regions(struct pci_dev *dev)
648{
649 unsigned int npages;
650 void __iomem *bbar;
651 unsigned char busnum;
652 u64 start;
653 struct iommu_table *tbl = dev->sysdata;
654
655 bbar = tbl->bbar;
656 busnum = dev->bus->number;
657
658 /* reserve bad_dma_address in case it's a legal address */
659 iommu_range_reserve(tbl, bad_dma_address, 1);
660
661 /* avoid the BIOS/VGA first 640KB-1MB region */
662 start = (640 * 1024);
663 npages = ((1024 - 640) * 1024) >> PAGE_SHIFT;
664 iommu_range_reserve(tbl, start, npages);
665
666 /* reserve the two PCI peripheral memory regions in IO space */
667 calgary_reserve_peripheral_mem_1(dev);
668 calgary_reserve_peripheral_mem_2(dev);
669}
670
671static int __init calgary_setup_tar(struct pci_dev *dev, void __iomem *bbar)
672{
673 u64 val64;
674 u64 table_phys;
675 void __iomem *target;
676 int ret;
677 struct iommu_table *tbl;
678
679 /* build TCE tables for each PHB */
680 ret = build_tce_table(dev, bbar);
681 if (ret)
682 return ret;
683
Muli Ben-Yehudaf38db652006-09-26 10:52:31 +0200684 tbl = dev->sysdata;
685 tbl->it_base = (unsigned long)bus_info[dev->bus->number].tce_space;
686 tce_free(tbl, 0, tbl->it_size);
687
Jon Masone4650582006-06-26 13:58:14 +0200688 calgary_reserve_regions(dev);
689
690 /* set TARs for each PHB */
691 target = calgary_reg(bbar, tar_offset(dev->bus->number));
692 val64 = be64_to_cpu(readq(target));
693
694 /* zero out all TAR bits under sw control */
695 val64 &= ~TAR_SW_BITS;
696
697 tbl = dev->sysdata;
698 table_phys = (u64)__pa(tbl->it_base);
699 val64 |= table_phys;
700
701 BUG_ON(specified_table_size > TCE_TABLE_SIZE_8M);
702 val64 |= (u64) specified_table_size;
703
704 tbl->tar_val = cpu_to_be64(val64);
705 writeq(tbl->tar_val, target);
706 readq(target); /* flush */
707
708 return 0;
709}
710
Muli Ben-Yehudab8f4fe62006-09-26 10:52:31 +0200711static void __init calgary_free_bus(struct pci_dev *dev)
Jon Masone4650582006-06-26 13:58:14 +0200712{
713 u64 val64;
714 struct iommu_table *tbl = dev->sysdata;
715 void __iomem *target;
Muli Ben-Yehudab8f4fe62006-09-26 10:52:31 +0200716 unsigned int bitmapsz;
Jon Masone4650582006-06-26 13:58:14 +0200717
718 target = calgary_reg(tbl->bbar, tar_offset(dev->bus->number));
719 val64 = be64_to_cpu(readq(target));
720 val64 &= ~TAR_SW_BITS;
721 writeq(cpu_to_be64(val64), target);
722 readq(target); /* flush */
723
Muli Ben-Yehudab8f4fe62006-09-26 10:52:31 +0200724 bitmapsz = tbl->it_size / BITS_PER_BYTE;
725 free_pages((unsigned long)tbl->it_map, get_order(bitmapsz));
726 tbl->it_map = NULL;
727
Jon Masone4650582006-06-26 13:58:14 +0200728 kfree(tbl);
729 dev->sysdata = NULL;
Muli Ben-Yehudab8f4fe62006-09-26 10:52:31 +0200730
731 /* Can't free bootmem allocated memory after system is up :-( */
732 bus_info[dev->bus->number].tce_space = NULL;
Jon Masone4650582006-06-26 13:58:14 +0200733}
734
735static void calgary_watchdog(unsigned long data)
736{
737 struct pci_dev *dev = (struct pci_dev *)data;
738 struct iommu_table *tbl = dev->sysdata;
739 void __iomem *bbar = tbl->bbar;
740 u32 val32;
741 void __iomem *target;
742
743 target = calgary_reg(bbar, phb_offset(tbl->it_busno) | PHB_CSR_OFFSET);
744 val32 = be32_to_cpu(readl(target));
745
746 /* If no error, the agent ID in the CSR is not valid */
747 if (val32 & CSR_AGENT_MASK) {
Jon Mason70d666d2006-10-05 18:47:21 +0200748 printk(KERN_EMERG "calgary_watchdog: DMA error on PHB %#x, "
Jon Masone4650582006-06-26 13:58:14 +0200749 "CSR = %#x\n", dev->bus->number, val32);
750 writel(0, target);
751
752 /* Disable bus that caused the error */
753 target = calgary_reg(bbar, phb_offset(tbl->it_busno) |
754 PHB_CONFIG_RW_OFFSET);
755 val32 = be32_to_cpu(readl(target));
756 val32 |= PHB_SLOT_DISABLE;
757 writel(cpu_to_be32(val32), target);
758 readl(target); /* flush */
759 } else {
760 /* Reset the timer */
761 mod_timer(&tbl->watchdog_timer, jiffies + 2 * HZ);
762 }
763}
764
Muli Ben-Yehudacb01fc72006-10-22 00:41:15 +0200765static void __init calgary_increase_split_completion_timeout(void __iomem *bbar,
766 unsigned char busnum)
767{
768 u64 val64;
769 void __iomem *target;
Muli Ben-Yehuda58db8542006-12-07 02:14:06 +0100770 unsigned int phb_shift = ~0; /* silence gcc */
Muli Ben-Yehudacb01fc72006-10-22 00:41:15 +0200771 u64 mask;
772
773 switch (busno_to_phbid(busnum)) {
774 case 0: phb_shift = (63 - 19);
775 break;
776 case 1: phb_shift = (63 - 23);
777 break;
778 case 2: phb_shift = (63 - 27);
779 break;
780 case 3: phb_shift = (63 - 35);
781 break;
782 default:
783 BUG_ON(busno_to_phbid(busnum));
784 }
785
786 target = calgary_reg(bbar, CALGARY_CONFIG_REG);
787 val64 = be64_to_cpu(readq(target));
788
789 /* zero out this PHB's timer bits */
790 mask = ~(0xFUL << phb_shift);
791 val64 &= mask;
792 val64 |= (CCR_2SEC_TIMEOUT << phb_shift);
793 writeq(cpu_to_be64(val64), target);
794 readq(target); /* flush */
795}
796
Jon Masone4650582006-06-26 13:58:14 +0200797static void __init calgary_enable_translation(struct pci_dev *dev)
798{
799 u32 val32;
800 unsigned char busnum;
801 void __iomem *target;
802 void __iomem *bbar;
803 struct iommu_table *tbl;
804
805 busnum = dev->bus->number;
806 tbl = dev->sysdata;
807 bbar = tbl->bbar;
808
809 /* enable TCE in PHB Config Register */
810 target = calgary_reg(bbar, phb_offset(busnum) | PHB_CONFIG_RW_OFFSET);
811 val32 = be32_to_cpu(readl(target));
812 val32 |= PHB_TCE_ENABLE | PHB_DAC_DISABLE | PHB_MCSR_ENABLE;
813
Jon Mason70d666d2006-10-05 18:47:21 +0200814 printk(KERN_INFO "Calgary: enabling translation on PHB %#x\n", busnum);
Jon Masone4650582006-06-26 13:58:14 +0200815 printk(KERN_INFO "Calgary: errant DMAs will now be prevented on this "
816 "bus.\n");
817
818 writel(cpu_to_be32(val32), target);
819 readl(target); /* flush */
820
Muli Ben-Yehudacb01fc72006-10-22 00:41:15 +0200821 /*
822 * Give split completion a longer timeout on bus 1 for aic94xx
823 * http://bugzilla.kernel.org/show_bug.cgi?id=7180
824 */
825 if (busnum == 1)
826 calgary_increase_split_completion_timeout(bbar, busnum);
827
Jon Masone4650582006-06-26 13:58:14 +0200828 init_timer(&tbl->watchdog_timer);
829 tbl->watchdog_timer.function = &calgary_watchdog;
830 tbl->watchdog_timer.data = (unsigned long)dev;
831 mod_timer(&tbl->watchdog_timer, jiffies);
832}
833
834static void __init calgary_disable_translation(struct pci_dev *dev)
835{
836 u32 val32;
837 unsigned char busnum;
838 void __iomem *target;
839 void __iomem *bbar;
840 struct iommu_table *tbl;
841
842 busnum = dev->bus->number;
843 tbl = dev->sysdata;
844 bbar = tbl->bbar;
845
846 /* disable TCE in PHB Config Register */
847 target = calgary_reg(bbar, phb_offset(busnum) | PHB_CONFIG_RW_OFFSET);
848 val32 = be32_to_cpu(readl(target));
849 val32 &= ~(PHB_TCE_ENABLE | PHB_DAC_DISABLE | PHB_MCSR_ENABLE);
850
Jon Mason70d666d2006-10-05 18:47:21 +0200851 printk(KERN_INFO "Calgary: disabling translation on PHB %#x!\n", busnum);
Jon Masone4650582006-06-26 13:58:14 +0200852 writel(cpu_to_be32(val32), target);
853 readl(target); /* flush */
854
855 del_timer_sync(&tbl->watchdog_timer);
856}
857
Laurent Vivierb34e90b2006-12-07 02:14:06 +0100858static inline void __iomem * __init locate_register_space(struct pci_dev *dev)
Jon Masone4650582006-06-26 13:58:14 +0200859{
Laurent Vivierb34e90b2006-12-07 02:14:06 +0100860 return busno_to_bbar(dev->bus->number);
Jon Masone4650582006-06-26 13:58:14 +0200861}
862
Muli Ben-Yehudaa4fc5202006-09-26 10:52:31 +0200863static void __init calgary_init_one_nontraslated(struct pci_dev *dev)
Jon Masone4650582006-06-26 13:58:14 +0200864{
Muli Ben-Yehuda871b1702006-09-26 10:52:31 +0200865 pci_dev_get(dev);
Jon Masone4650582006-06-26 13:58:14 +0200866 dev->sysdata = NULL;
867 dev->bus->self = dev;
Jon Masone4650582006-06-26 13:58:14 +0200868}
869
870static int __init calgary_init_one(struct pci_dev *dev)
871{
Jon Masone4650582006-06-26 13:58:14 +0200872 void __iomem *bbar;
873 int ret;
874
Jon Masondedc9932006-10-05 18:47:21 +0200875 BUG_ON(dev->bus->number >= MAX_PHB_BUS_NUM);
876
Laurent Vivierb34e90b2006-12-07 02:14:06 +0100877 bbar = locate_register_space(dev);
Jon Masone4650582006-06-26 13:58:14 +0200878 if (!bbar) {
879 ret = -ENODATA;
880 goto done;
881 }
882
883 ret = calgary_setup_tar(dev, bbar);
884 if (ret)
885 goto iounmap;
886
Muli Ben-Yehuda871b1702006-09-26 10:52:31 +0200887 pci_dev_get(dev);
Jon Masone4650582006-06-26 13:58:14 +0200888 dev->bus->self = dev;
889 calgary_enable_translation(dev);
890
891 return 0;
892
893iounmap:
894 iounmap(bbar);
895done:
896 return ret;
897}
898
899static int __init calgary_init(void)
900{
Jon Masondedc9932006-10-05 18:47:21 +0200901 int ret = -ENODEV;
Jon Masone4650582006-06-26 13:58:14 +0200902 struct pci_dev *dev = NULL;
Laurent Vivierb34e90b2006-12-07 02:14:06 +0100903 int rio, phb, bus;
904 void __iomem *bbar;
905 void __iomem *target;
906 u8 start_bus, end_bus;
907 u32 val;
908
909 for (rio = 0; rio < rio_table_hdr->num_rio_dev; rio++) {
910
911 if ( (rio_devs[rio]->type != COMPAT_CALGARY) &&
912 (rio_devs[rio]->type != ALT_CALGARY) )
913 continue;
914
915 /* map entire 1MB of Calgary config space */
916 bbar = ioremap_nocache(rio_devs[rio]->BBAR, 1024 * 1024);
917
918 for (phb = 0; phb < PHBS_PER_CALGARY; phb++) {
919
920 target = calgary_reg(bbar, phb_debug_offsets[phb] |
921 PHB_DEBUG_STUFF_OFFSET);
922 val = be32_to_cpu(readl(target));
923 start_bus = (u8)((val & 0x00FF0000) >> 16);
924 end_bus = (u8)((val & 0x0000FF00) >> 8);
925 for (bus = start_bus; bus <= end_bus; bus++) {
926 bus_info[bus].bbar = bbar;
927 bus_info[bus].phbid = phb;
928 }
929 }
930 }
931
Jon Masone4650582006-06-26 13:58:14 +0200932
Jon Masondedc9932006-10-05 18:47:21 +0200933 do {
Jon Masone4650582006-06-26 13:58:14 +0200934 dev = pci_get_device(PCI_VENDOR_ID_IBM,
935 PCI_DEVICE_ID_IBM_CALGARY,
936 dev);
937 if (!dev)
938 break;
939 if (!translate_phb(dev)) {
940 calgary_init_one_nontraslated(dev);
941 continue;
942 }
Muli Ben-Yehuda871b1702006-09-26 10:52:31 +0200943 if (!bus_info[dev->bus->number].tce_space && !translate_empty_slots)
Jon Masone4650582006-06-26 13:58:14 +0200944 continue;
Muli Ben-Yehuda871b1702006-09-26 10:52:31 +0200945
Jon Masone4650582006-06-26 13:58:14 +0200946 ret = calgary_init_one(dev);
947 if (ret)
948 goto error;
Jon Masondedc9932006-10-05 18:47:21 +0200949 } while (1);
Jon Masone4650582006-06-26 13:58:14 +0200950
951 return ret;
952
953error:
Jon Masondedc9932006-10-05 18:47:21 +0200954 do {
Alan Cox7cd8b682006-12-07 02:14:03 +0100955 dev = pci_get_device_reverse(PCI_VENDOR_ID_IBM,
Jon Masone4650582006-06-26 13:58:14 +0200956 PCI_DEVICE_ID_IBM_CALGARY,
957 dev);
Muli Ben-Yehuda9f2dc462006-09-26 10:52:31 +0200958 if (!dev)
959 break;
Jon Masone4650582006-06-26 13:58:14 +0200960 if (!translate_phb(dev)) {
961 pci_dev_put(dev);
962 continue;
963 }
Muli Ben-Yehudaf38db652006-09-26 10:52:31 +0200964 if (!bus_info[dev->bus->number].tce_space && !translate_empty_slots)
Jon Masone4650582006-06-26 13:58:14 +0200965 continue;
Muli Ben-Yehuda871b1702006-09-26 10:52:31 +0200966
Jon Masone4650582006-06-26 13:58:14 +0200967 calgary_disable_translation(dev);
Muli Ben-Yehudab8f4fe62006-09-26 10:52:31 +0200968 calgary_free_bus(dev);
Muli Ben-Yehuda871b1702006-09-26 10:52:31 +0200969 pci_dev_put(dev); /* Undo calgary_init_one()'s pci_dev_get() */
Jon Masondedc9932006-10-05 18:47:21 +0200970 } while (1);
Jon Masone4650582006-06-26 13:58:14 +0200971
972 return ret;
973}
974
975static inline int __init determine_tce_table_size(u64 ram)
976{
977 int ret;
978
979 if (specified_table_size != TCE_TABLE_SIZE_UNSPECIFIED)
980 return specified_table_size;
981
982 /*
983 * Table sizes are from 0 to 7 (TCE_TABLE_SIZE_64K to
984 * TCE_TABLE_SIZE_8M). Table size 0 has 8K entries and each
985 * larger table size has twice as many entries, so shift the
986 * max ram address by 13 to divide by 8K and then look at the
987 * order of the result to choose between 0-7.
988 */
989 ret = get_order(ram >> 13);
990 if (ret > TCE_TABLE_SIZE_8M)
991 ret = TCE_TABLE_SIZE_8M;
992
993 return ret;
994}
995
Laurent Vivierb34e90b2006-12-07 02:14:06 +0100996static int __init build_detail_arrays(void)
997{
998 unsigned long ptr;
999 int i, scal_detail_size, rio_detail_size;
1000
1001 if (rio_table_hdr->num_scal_dev > MAX_NUMNODES){
1002 printk(KERN_WARNING
1003 "Calgary: MAX_NUMNODES too low! Defined as %d, "
1004 "but system has %d nodes.\n",
1005 MAX_NUMNODES, rio_table_hdr->num_scal_dev);
1006 return -ENODEV;
1007 }
1008
1009 switch (rio_table_hdr->version){
1010 default:
1011 printk(KERN_WARNING
1012 "Calgary: Invalid Rio Grande Table Version: %d\n",
1013 rio_table_hdr->version);
1014 return -ENODEV;
1015 case 2:
1016 scal_detail_size = 11;
1017 rio_detail_size = 13;
1018 break;
1019 case 3:
1020 scal_detail_size = 12;
1021 rio_detail_size = 15;
1022 break;
1023 }
1024
1025 ptr = ((unsigned long)rio_table_hdr) + 3;
1026 for (i = 0; i < rio_table_hdr->num_scal_dev;
1027 i++, ptr += scal_detail_size)
1028 scal_devs[i] = (struct scal_detail *)ptr;
1029
1030 for (i = 0; i < rio_table_hdr->num_rio_dev;
1031 i++, ptr += rio_detail_size)
1032 rio_devs[i] = (struct rio_detail *)ptr;
1033
1034 return 0;
1035}
1036
Jon Masone4650582006-06-26 13:58:14 +02001037void __init detect_calgary(void)
1038{
1039 u32 val;
Jon Masond2105b12006-07-29 21:42:43 +02001040 int bus;
Jon Masone4650582006-06-26 13:58:14 +02001041 void *tbl;
Jon Masond2105b12006-07-29 21:42:43 +02001042 int calgary_found = 0;
Laurent Vivierb34e90b2006-12-07 02:14:06 +01001043 unsigned long ptr;
1044 int offset;
Jon Masone4650582006-06-26 13:58:14 +02001045
1046 /*
1047 * if the user specified iommu=off or iommu=soft or we found
1048 * another HW IOMMU already, bail out.
1049 */
1050 if (swiotlb || no_iommu || iommu_detected)
1051 return;
1052
Andi Kleen0637a702006-09-26 10:52:41 +02001053 if (!early_pci_allowed())
1054 return;
1055
Laurent Vivierb34e90b2006-12-07 02:14:06 +01001056 ptr = (unsigned long)phys_to_virt(get_bios_ebda());
1057
1058 rio_table_hdr = NULL;
1059 offset = 0x180;
1060 while (offset) {
1061 /* The block id is stored in the 2nd word */
1062 if (*((unsigned short *)(ptr + offset + 2)) == 0x4752){
1063 /* set the pointer past the offset & block id */
1064 rio_table_hdr = (struct rio_table_hdr *)(ptr+offset+4);
1065 break;
1066 }
1067 /* The next offset is stored in the 1st word. 0 means no more */
1068 offset = *((unsigned short *)(ptr + offset));
1069 }
1070 if (!rio_table_hdr){
1071 printk(KERN_ERR "Calgary: Unable to locate "
1072 "Rio Grande Table in EBDA - bailing!\n");
1073 return;
1074 }
1075
1076 if (build_detail_arrays())
1077 return;
1078
Jon Masone4650582006-06-26 13:58:14 +02001079 specified_table_size = determine_tce_table_size(end_pfn * PAGE_SIZE);
1080
Jon Masond2105b12006-07-29 21:42:43 +02001081 for (bus = 0; bus < MAX_PHB_BUS_NUM; bus++) {
1082 int dev;
Muli Ben-Yehudaf38db652006-09-26 10:52:31 +02001083 struct calgary_bus_info *info = &bus_info[bus];
1084 info->phbid = -1;
Jon Masond2105b12006-07-29 21:42:43 +02001085
Jon Masone4650582006-06-26 13:58:14 +02001086 if (read_pci_config(bus, 0, 0, 0) != PCI_VENDOR_DEVICE_ID_CALGARY)
1087 continue;
Jon Masond2105b12006-07-29 21:42:43 +02001088
Muli Ben-Yehudaf38db652006-09-26 10:52:31 +02001089 if (info->translation_disabled)
Jon Masone4650582006-06-26 13:58:14 +02001090 continue;
Muli Ben-Yehudaf38db652006-09-26 10:52:31 +02001091
Jon Masone4650582006-06-26 13:58:14 +02001092 /*
Jon Masond2105b12006-07-29 21:42:43 +02001093 * Scan the slots of the PCI bus to see if there is a device present.
1094 * The parent bus will be the zero-ith device, so start at 1.
Jon Masone4650582006-06-26 13:58:14 +02001095 */
Jon Masond2105b12006-07-29 21:42:43 +02001096 for (dev = 1; dev < 8; dev++) {
1097 val = read_pci_config(bus, dev, 0, 0);
1098 if (val != 0xffffffff || translate_empty_slots) {
1099 tbl = alloc_tce_table();
1100 if (!tbl)
1101 goto cleanup;
Muli Ben-Yehudaf38db652006-09-26 10:52:31 +02001102 info->tce_space = tbl;
Jon Masond2105b12006-07-29 21:42:43 +02001103 calgary_found = 1;
1104 break;
1105 }
1106 }
Jon Masone4650582006-06-26 13:58:14 +02001107 }
1108
Jon Masond2105b12006-07-29 21:42:43 +02001109 if (calgary_found) {
Jon Masone4650582006-06-26 13:58:14 +02001110 iommu_detected = 1;
1111 calgary_detected = 1;
Muli Ben-Yehudade684652006-09-26 10:52:33 +02001112 printk(KERN_INFO "PCI-DMA: Calgary IOMMU detected.\n");
1113 printk(KERN_INFO "PCI-DMA: Calgary TCE table spec is %d, "
1114 "CONFIG_IOMMU_DEBUG is %s.\n", specified_table_size,
1115 debugging ? "enabled" : "disabled");
Jon Masone4650582006-06-26 13:58:14 +02001116 }
1117 return;
1118
1119cleanup:
Muli Ben-Yehudaf38db652006-09-26 10:52:31 +02001120 for (--bus; bus >= 0; --bus) {
1121 struct calgary_bus_info *info = &bus_info[bus];
1122
1123 if (info->tce_space)
1124 free_tce_table(info->tce_space);
1125 }
Jon Masone4650582006-06-26 13:58:14 +02001126}
1127
1128int __init calgary_iommu_init(void)
1129{
1130 int ret;
1131
1132 if (no_iommu || swiotlb)
1133 return -ENODEV;
1134
1135 if (!calgary_detected)
1136 return -ENODEV;
1137
1138 /* ok, we're trying to use Calgary - let's roll */
1139 printk(KERN_INFO "PCI-DMA: Using Calgary IOMMU\n");
1140
1141 ret = calgary_init();
1142 if (ret) {
1143 printk(KERN_ERR "PCI-DMA: Calgary init failed %d, "
1144 "falling back to no_iommu\n", ret);
1145 if (end_pfn > MAX_DMA32_PFN)
1146 printk(KERN_ERR "WARNING more than 4GB of memory, "
1147 "32bit PCI may malfunction.\n");
1148 return ret;
1149 }
1150
1151 force_iommu = 1;
1152 dma_ops = &calgary_dma_ops;
1153
1154 return 0;
1155}
1156
1157static int __init calgary_parse_options(char *p)
1158{
1159 unsigned int bridge;
1160 size_t len;
1161 char* endp;
1162
1163 while (*p) {
1164 if (!strncmp(p, "64k", 3))
1165 specified_table_size = TCE_TABLE_SIZE_64K;
1166 else if (!strncmp(p, "128k", 4))
1167 specified_table_size = TCE_TABLE_SIZE_128K;
1168 else if (!strncmp(p, "256k", 4))
1169 specified_table_size = TCE_TABLE_SIZE_256K;
1170 else if (!strncmp(p, "512k", 4))
1171 specified_table_size = TCE_TABLE_SIZE_512K;
1172 else if (!strncmp(p, "1M", 2))
1173 specified_table_size = TCE_TABLE_SIZE_1M;
1174 else if (!strncmp(p, "2M", 2))
1175 specified_table_size = TCE_TABLE_SIZE_2M;
1176 else if (!strncmp(p, "4M", 2))
1177 specified_table_size = TCE_TABLE_SIZE_4M;
1178 else if (!strncmp(p, "8M", 2))
1179 specified_table_size = TCE_TABLE_SIZE_8M;
1180
1181 len = strlen("translate_empty_slots");
1182 if (!strncmp(p, "translate_empty_slots", len))
1183 translate_empty_slots = 1;
1184
1185 len = strlen("disable");
1186 if (!strncmp(p, "disable", len)) {
1187 p += len;
1188 if (*p == '=')
1189 ++p;
1190 if (*p == '\0')
1191 break;
1192 bridge = simple_strtol(p, &endp, 0);
1193 if (p == endp)
1194 break;
1195
Jon Masond2105b12006-07-29 21:42:43 +02001196 if (bridge < MAX_PHB_BUS_NUM) {
Jon Masone4650582006-06-26 13:58:14 +02001197 printk(KERN_INFO "Calgary: disabling "
Jon Mason70d666d2006-10-05 18:47:21 +02001198 "translation for PHB %#x\n", bridge);
Muli Ben-Yehudaf38db652006-09-26 10:52:31 +02001199 bus_info[bridge].translation_disabled = 1;
Jon Masone4650582006-06-26 13:58:14 +02001200 }
1201 }
1202
1203 p = strpbrk(p, ",");
1204 if (!p)
1205 break;
1206
1207 p++; /* skip ',' */
1208 }
1209 return 1;
1210}
1211__setup("calgary=", calgary_parse_options);