Blame view

arch/powerpc/sysdev/dart_iommu.c 11.8 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
  /*
1beb6a7d6   Benjamin Herrenschmidt   [PATCH] powerpc: ...
2
   * arch/powerpc/sysdev/dart_iommu.c
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
3
   *
91f14480a   Olof Johansson   [PATCH] powerpc: ...
4
   * Copyright (C) 2004 Olof Johansson <olof@lixom.net>, IBM Corporation
1beb6a7d6   Benjamin Herrenschmidt   [PATCH] powerpc: ...
5
6
   * Copyright (C) 2005 Benjamin Herrenschmidt <benh@kernel.crashing.org>,
   *                    IBM Corporation
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
7
8
9
   *
   * Based on pSeries_iommu.c:
   * Copyright (C) 2001 Mike Corrigan & Dave Engebretsen, IBM Corporation
91f14480a   Olof Johansson   [PATCH] powerpc: ...
10
   * Copyright (C) 2004 Olof Johansson <olof@lixom.net>, IBM Corporation
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
11
   *
1beb6a7d6   Benjamin Herrenschmidt   [PATCH] powerpc: ...
12
13
   * Dynamic DMA mapping support, Apple U3, U4 & IBM CPC925 "DART" iommu.
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
14
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
15
16
17
18
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
   * the Free Software Foundation; either version 2 of the License, or
   * (at your option) any later version.
1beb6a7d6   Benjamin Herrenschmidt   [PATCH] powerpc: ...
19
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
20
21
22
23
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
1beb6a7d6   Benjamin Herrenschmidt   [PATCH] powerpc: ...
24
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
25
26
27
28
   * You should have received a copy of the GNU General Public License
   * along with this program; if not, write to the Free Software
   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
29
30
  #include <linux/init.h>
  #include <linux/types.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
31
32
33
34
35
36
  #include <linux/mm.h>
  #include <linux/spinlock.h>
  #include <linux/string.h>
  #include <linux/pci.h>
  #include <linux/dma-mapping.h>
  #include <linux/vmalloc.h>
7e11580b3   Johannes Berg   [POWERPC] DART io...
37
  #include <linux/suspend.h>
95f72d1ed   Yinghai Lu   lmb: rename to me...
38
  #include <linux/memblock.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
39
  #include <linux/gfp.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
40
41
  #include <asm/io.h>
  #include <asm/prom.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
42
43
44
45
46
  #include <asm/iommu.h>
  #include <asm/pci-bridge.h>
  #include <asm/machdep.h>
  #include <asm/abs_addr.h>
  #include <asm/cacheflush.h>
d387899f3   Stephen Rothwell   powerpc: Move iSe...
47
  #include <asm/ppc-pci.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
48

9933f299d   David Gibson   [PATCH] powerpc: ...
49
  #include "dart.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
50
51
52
53
54
55
  /* Physical base address and size of the DART table */
  unsigned long dart_tablebase; /* exported to htab_initialize */
  static unsigned long dart_tablesize;
  
  /* Virtual base address of the DART table */
  static u32 *dart_vbase;
7e11580b3   Johannes Berg   [POWERPC] DART io...
56
57
58
  #ifdef CONFIG_PM
  static u32 *dart_copy;
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
59
60
  
  /* Mapped base address for the dart */
6fa2ffe90   Al Viro   [PATCH] fix iomem...
61
  static unsigned int __iomem *dart;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
62
63
64
  
  /* Dummy val that entries are set to when unused */
  static unsigned int dart_emptyval;
1beb6a7d6   Benjamin Herrenschmidt   [PATCH] powerpc: ...
65
66
  static struct iommu_table iommu_table_dart;
  static int iommu_table_dart_inited;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
67
  static int dart_dirty;
1beb6a7d6   Benjamin Herrenschmidt   [PATCH] powerpc: ...
68
  static int dart_is_u4;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
69

8fb07c044   Benjamin Herrenschmidt   powerpc/dart_iomm...
70
  #define DART_U4_BYPASS_BASE	0x8000000000ull
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
71
72
73
74
75
  #define DBG(...)
  
  static inline void dart_tlb_invalidate_all(void)
  {
  	unsigned long l = 0;
1beb6a7d6   Benjamin Herrenschmidt   [PATCH] powerpc: ...
76
  	unsigned int reg, inv_bit;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
77
78
79
80
81
82
83
84
85
86
  	unsigned long limit;
  
  	DBG("dart: flush
  ");
  
  	/* To invalidate the DART, set the DARTCNTL_FLUSHTLB bit in the
  	 * control register and wait for it to clear.
  	 *
  	 * Gotcha: Sometimes, the DART won't detect that the bit gets
  	 * set. If so, clear it and set it again.
1beb6a7d6   Benjamin Herrenschmidt   [PATCH] powerpc: ...
87
  	 */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
88
89
  
  	limit = 0;
1beb6a7d6   Benjamin Herrenschmidt   [PATCH] powerpc: ...
90
  	inv_bit = dart_is_u4 ? DART_CNTL_U4_FLUSHTLB : DART_CNTL_U3_FLUSHTLB;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
91
  retry:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
92
  	l = 0;
1beb6a7d6   Benjamin Herrenschmidt   [PATCH] powerpc: ...
93
94
95
96
97
  	reg = DART_IN(DART_CNTL);
  	reg |= inv_bit;
  	DART_OUT(DART_CNTL, reg);
  
  	while ((DART_IN(DART_CNTL) & inv_bit) && l < (1L << limit))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
98
  		l++;
1beb6a7d6   Benjamin Herrenschmidt   [PATCH] powerpc: ...
99
  	if (l == (1L << limit)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
100
101
  		if (limit < 4) {
  			limit++;
feb76c7b2   Olof Johansson   [POWERPC] U4 DART...
102
103
  			reg = DART_IN(DART_CNTL);
  			reg &= ~inv_bit;
1beb6a7d6   Benjamin Herrenschmidt   [PATCH] powerpc: ...
104
  			DART_OUT(DART_CNTL, reg);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
105
106
  			goto retry;
  		} else
1beb6a7d6   Benjamin Herrenschmidt   [PATCH] powerpc: ...
107
  			panic("DART: TLB did not flush after waiting a long "
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
108
109
110
  			      "time. Buggy U3 ?");
  	}
  }
feb76c7b2   Olof Johansson   [POWERPC] U4 DART...
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
  static inline void dart_tlb_invalidate_one(unsigned long bus_rpn)
  {
  	unsigned int reg;
  	unsigned int l, limit;
  
  	reg = DART_CNTL_U4_ENABLE | DART_CNTL_U4_IONE |
  		(bus_rpn & DART_CNTL_U4_IONE_MASK);
  	DART_OUT(DART_CNTL, reg);
  
  	limit = 0;
  wait_more:
  	l = 0;
  	while ((DART_IN(DART_CNTL) & DART_CNTL_U4_IONE) && l < (1L << limit)) {
  		rmb();
  		l++;
  	}
  
  	if (l == (1L << limit)) {
  		if (limit < 4) {
  			limit++;
  			goto wait_more;
  		} else
  			panic("DART: TLB did not flush after waiting a long "
  			      "time. Buggy U4 ?");
  	}
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
137
138
  static void dart_flush(struct iommu_table *tbl)
  {
eeac5c142   Benjamin Herrenschmidt   [POWERPC] Fix G5 ...
139
  	mb();
feb76c7b2   Olof Johansson   [POWERPC] U4 DART...
140
  	if (dart_dirty) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
141
  		dart_tlb_invalidate_all();
feb76c7b2   Olof Johansson   [POWERPC] U4 DART...
142
143
  		dart_dirty = 0;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
144
  }
6490c4903   Robert Jennings   powerpc/pseries: ...
145
  static int dart_build(struct iommu_table *tbl, long index,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
146
  		       long npages, unsigned long uaddr,
4f3dd8a06   Mark Nelson   powerpc/dma: Use ...
147
148
  		       enum dma_data_direction direction,
  		       struct dma_attrs *attrs)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
149
150
151
  {
  	unsigned int *dp;
  	unsigned int rpn;
feb76c7b2   Olof Johansson   [POWERPC] U4 DART...
152
  	long l;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
153
154
155
156
157
  
  	DBG("dart: build at: %lx, %lx, addr: %x
  ", index, npages, uaddr);
  
  	dp = ((unsigned int*)tbl->it_base) + index;
1beb6a7d6   Benjamin Herrenschmidt   [PATCH] powerpc: ...
158

af901ca18   AndrĂ© Goddard Rosa   tree-wide: fix as...
159
  	/* On U3, all memory is contiguous, so we can move this
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
160
161
  	 * out of the loop.
  	 */
feb76c7b2   Olof Johansson   [POWERPC] U4 DART...
162
163
  	l = npages;
  	while (l--) {
d0035c62d   Olof Johansson   [PATCH] ppc64: Up...
164
  		rpn = virt_to_abs(uaddr) >> DART_PAGE_SHIFT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
165
166
  
  		*(dp++) = DARTMAP_VALID | (rpn & DARTMAP_RPNMASK);
d0035c62d   Olof Johansson   [PATCH] ppc64: Up...
167
  		uaddr += DART_PAGE_SIZE;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
168
  	}
eeac5c142   Benjamin Herrenschmidt   [POWERPC] Fix G5 ...
169
170
171
172
  	/* make sure all updates have reached memory */
  	mb();
  	in_be32((unsigned __iomem *)dp);
  	mb();
feb76c7b2   Olof Johansson   [POWERPC] U4 DART...
173
174
  	if (dart_is_u4) {
  		rpn = index;
feb76c7b2   Olof Johansson   [POWERPC] U4 DART...
175
176
177
178
179
  		while (npages--)
  			dart_tlb_invalidate_one(rpn++);
  	} else {
  		dart_dirty = 1;
  	}
6490c4903   Robert Jennings   powerpc/pseries: ...
180
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
181
182
183
184
185
186
  }
  
  
  static void dart_free(struct iommu_table *tbl, long index, long npages)
  {
  	unsigned int *dp;
1beb6a7d6   Benjamin Herrenschmidt   [PATCH] powerpc: ...
187

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
188
189
190
191
192
193
194
195
196
  	/* We don't worry about flushing the TLB cache. The only drawback of
  	 * not doing it is that we won't catch buggy device drivers doing
  	 * bad DMAs, but then no 32-bit architecture ever does either.
  	 */
  
  	DBG("dart: free at: %lx, %lx
  ", index, npages);
  
  	dp  = ((unsigned int *)tbl->it_base) + index;
1beb6a7d6   Benjamin Herrenschmidt   [PATCH] powerpc: ...
197

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
198
199
200
  	while (npages--)
  		*(dp++) = dart_emptyval;
  }
109b60f0b   Stephen Rothwell   [POWERPC] Fix sec...
201
  static int __init dart_init(struct device_node *dart_node)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
202
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
203
  	unsigned int i;
1beb6a7d6   Benjamin Herrenschmidt   [PATCH] powerpc: ...
204
205
  	unsigned long tmp, base, size;
  	struct resource r;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
206
207
  
  	if (dart_tablebase == 0 || dart_tablesize == 0) {
1beb6a7d6   Benjamin Herrenschmidt   [PATCH] powerpc: ...
208
209
210
  		printk(KERN_INFO "DART: table not allocated, using "
  		       "direct DMA
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
211
212
  		return -ENODEV;
  	}
1beb6a7d6   Benjamin Herrenschmidt   [PATCH] powerpc: ...
213
214
  	if (of_address_to_resource(dart_node, 0, &r))
  		panic("DART: can't get register base ! ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
215
216
217
218
  	/* Make sure nothing from the DART range remains in the CPU cache
  	 * from a previous mapping that existed before the kernel took
  	 * over
  	 */
1beb6a7d6   Benjamin Herrenschmidt   [PATCH] powerpc: ...
219
220
  	flush_dcache_phys_range(dart_tablebase,
  				dart_tablebase + dart_tablesize);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
221
222
223
224
225
  
  	/* Allocate a spare page to map all invalid DART pages. We need to do
  	 * that to work around what looks like a problem with the HT bridge
  	 * prefetching into invalid pages and corrupting data
  	 */
95f72d1ed   Yinghai Lu   lmb: rename to me...
226
  	tmp = memblock_alloc(DART_PAGE_SIZE, DART_PAGE_SIZE);
1beb6a7d6   Benjamin Herrenschmidt   [PATCH] powerpc: ...
227
228
  	dart_emptyval = DARTMAP_VALID | ((tmp >> DART_PAGE_SHIFT) &
  					 DARTMAP_RPNMASK);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
229

1beb6a7d6   Benjamin Herrenschmidt   [PATCH] powerpc: ...
230
  	/* Map in DART registers */
28f65c11f   Joe Perches   treewide: Convert...
231
  	dart = ioremap(r.start, resource_size(&r));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
232
  	if (dart == NULL)
1beb6a7d6   Benjamin Herrenschmidt   [PATCH] powerpc: ...
233
  		panic("DART: Cannot map registers!");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
234

1beb6a7d6   Benjamin Herrenschmidt   [PATCH] powerpc: ...
235
  	/* Map in DART table */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
236
237
238
239
240
241
242
  	dart_vbase = ioremap(virt_to_abs(dart_tablebase), dart_tablesize);
  
  	/* Fill initial table */
  	for (i = 0; i < dart_tablesize/4; i++)
  		dart_vbase[i] = dart_emptyval;
  
  	/* Initialize DART with table base and enable it. */
1beb6a7d6   Benjamin Herrenschmidt   [PATCH] powerpc: ...
243
244
245
  	base = dart_tablebase >> DART_PAGE_SHIFT;
  	size = dart_tablesize >> DART_PAGE_SHIFT;
  	if (dart_is_u4) {
56c8eaee6   Benjamin Herrenschmidt   [PATCH] powerpc: ...
246
  		size &= DART_SIZE_U4_SIZE_MASK;
1beb6a7d6   Benjamin Herrenschmidt   [PATCH] powerpc: ...
247
248
249
250
  		DART_OUT(DART_BASE_U4, base);
  		DART_OUT(DART_SIZE_U4, size);
  		DART_OUT(DART_CNTL, DART_CNTL_U4_ENABLE);
  	} else {
56c8eaee6   Benjamin Herrenschmidt   [PATCH] powerpc: ...
251
  		size &= DART_CNTL_U3_SIZE_MASK;
1beb6a7d6   Benjamin Herrenschmidt   [PATCH] powerpc: ...
252
253
254
255
256
  		DART_OUT(DART_CNTL,
  			 DART_CNTL_U3_ENABLE |
  			 (base << DART_CNTL_U3_BASE_SHIFT) |
  			 (size << DART_CNTL_U3_SIZE_SHIFT));
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
257
258
259
  
  	/* Invalidate DART to get rid of possible stale TLBs */
  	dart_tlb_invalidate_all();
1beb6a7d6   Benjamin Herrenschmidt   [PATCH] powerpc: ...
260
261
262
  	printk(KERN_INFO "DART IOMMU initialized for %s type chipset
  ",
  	       dart_is_u4 ? "U4" : "U3");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
263
264
265
  
  	return 0;
  }
1beb6a7d6   Benjamin Herrenschmidt   [PATCH] powerpc: ...
266
  static void iommu_table_dart_setup(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
267
  {
1beb6a7d6   Benjamin Herrenschmidt   [PATCH] powerpc: ...
268
269
  	iommu_table_dart.it_busno = 0;
  	iommu_table_dart.it_offset = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
270
  	/* it_size is in number of entries */
5d2efba64   Linas Vepstas   [POWERPC] Use 4kB...
271
  	iommu_table_dart.it_size = dart_tablesize / sizeof(u32);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
272
273
  
  	/* Initialize the common IOMMU code */
1beb6a7d6   Benjamin Herrenschmidt   [PATCH] powerpc: ...
274
275
276
  	iommu_table_dart.it_base = (unsigned long)dart_vbase;
  	iommu_table_dart.it_index = 0;
  	iommu_table_dart.it_blocksize = 1;
ca1588e71   Anton Blanchard   [POWERPC] node lo...
277
  	iommu_init_table(&iommu_table_dart, -1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
278
279
280
281
  
  	/* Reserve the last page of the DART to avoid possible prefetch
  	 * past the DART mapped area
  	 */
1beb6a7d6   Benjamin Herrenschmidt   [PATCH] powerpc: ...
282
  	set_bit(iommu_table_dart.it_size - 1, iommu_table_dart.it_map);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
283
  }
8fb07c044   Benjamin Herrenschmidt   powerpc/dart_iomm...
284
  static void dma_dev_setup_dart(struct device *dev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
285
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
286
287
  	/* We only have one iommu table on the mac for now, which makes
  	 * things simple. Setup all PCI devices to point to this table
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
288
  	 */
8fb07c044   Benjamin Herrenschmidt   powerpc/dart_iomm...
289
290
291
292
293
294
295
296
297
  	if (get_dma_ops(dev) == &dma_direct_ops)
  		set_dma_offset(dev, DART_U4_BYPASS_BASE);
  	else
  		set_iommu_table_base(dev, &iommu_table_dart);
  }
  
  static void pci_dma_dev_setup_dart(struct pci_dev *dev)
  {
  	dma_dev_setup_dart(&dev->dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
298
  }
12d04eef9   Benjamin Herrenschmidt   [POWERPC] Refacto...
299
  static void pci_dma_bus_setup_dart(struct pci_bus *bus)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
300
  {
1beb6a7d6   Benjamin Herrenschmidt   [PATCH] powerpc: ...
301
302
303
  	if (!iommu_table_dart_inited) {
  		iommu_table_dart_inited = 1;
  		iommu_table_dart_setup();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
304
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
305
  }
8fb07c044   Benjamin Herrenschmidt   powerpc/dart_iomm...
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
  static bool dart_device_on_pcie(struct device *dev)
  {
  	struct device_node *np = of_node_get(dev->of_node);
  
  	while(np) {
  		if (of_device_is_compatible(np, "U4-pcie") ||
  		    of_device_is_compatible(np, "u4-pcie")) {
  			of_node_put(np);
  			return true;
  		}
  		np = of_get_next_parent(np);
  	}
  	return false;
  }
  
  static int dart_dma_set_mask(struct device *dev, u64 dma_mask)
  {
  	if (!dev->dma_mask || !dma_supported(dev, dma_mask))
  		return -EIO;
  
  	/* U4 supports a DART bypass, we use it for 64-bit capable
  	 * devices to improve performances. However, that only works
  	 * for devices connected to U4 own PCIe interface, not bridged
  	 * through hypertransport. We need the device to support at
  	 * least 40 bits of addresses.
  	 */
  	if (dart_device_on_pcie(dev) && dma_mask >= DMA_BIT_MASK(40)) {
  		dev_info(dev, "Using 64-bit DMA iommu bypass
  ");
  		set_dma_ops(dev, &dma_direct_ops);
  	} else {
  		dev_info(dev, "Using 32-bit DMA via iommu
  ");
  		set_dma_ops(dev, &dma_iommu_ops);
  	}
  	dma_dev_setup_dart(dev);
  
  	*dev->dma_mask = dma_mask;
  	return 0;
  }
109b60f0b   Stephen Rothwell   [POWERPC] Fix sec...
346
  void __init iommu_init_early_dart(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
347
348
349
350
351
  {
  	struct device_node *dn;
  
  	/* Find the DART in the device-tree */
  	dn = of_find_compatible_node(NULL, "dart", "u3-dart");
1beb6a7d6   Benjamin Herrenschmidt   [PATCH] powerpc: ...
352
353
354
  	if (dn == NULL) {
  		dn = of_find_compatible_node(NULL, "dart", "u4-dart");
  		if (dn == NULL)
34c4d0125   Nishanth Aravamudan   powerpc/dart: iom...
355
  			return;	/* use default direct_dma_ops */
1beb6a7d6   Benjamin Herrenschmidt   [PATCH] powerpc: ...
356
357
  		dart_is_u4 = 1;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
358

8fb07c044   Benjamin Herrenschmidt   powerpc/dart_iomm...
359
360
361
  	/* Initialize the DART HW */
  	if (dart_init(dn) != 0)
  		goto bail;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
362
363
364
365
  	/* Setup low level TCE operations for the core IOMMU code */
  	ppc_md.tce_build = dart_build;
  	ppc_md.tce_free  = dart_free;
  	ppc_md.tce_flush = dart_flush;
8fb07c044   Benjamin Herrenschmidt   powerpc/dart_iomm...
366
367
368
  	/* Setup bypass if supported */
  	if (dart_is_u4)
  		ppc_md.dma_set_mask = dart_dma_set_mask;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
369

8fb07c044   Benjamin Herrenschmidt   powerpc/dart_iomm...
370
371
372
373
374
375
  	ppc_md.pci_dma_dev_setup = pci_dma_dev_setup_dart;
  	ppc_md.pci_dma_bus_setup = pci_dma_bus_setup_dart;
  
  	/* Setup pci_dma ops */
  	set_pci_dma_ops(&dma_iommu_ops);
  	return;
1beb6a7d6   Benjamin Herrenschmidt   [PATCH] powerpc: ...
376
377
378
  
   bail:
  	/* If init failed, use direct iommu and null setup functions */
12d04eef9   Benjamin Herrenschmidt   [POWERPC] Refacto...
379
380
  	ppc_md.pci_dma_dev_setup = NULL;
  	ppc_md.pci_dma_bus_setup = NULL;
1beb6a7d6   Benjamin Herrenschmidt   [PATCH] powerpc: ...
381
382
  
  	/* Setup pci_dma ops */
987477701   Stephen Rothwell   [POWERPC] Create ...
383
  	set_pci_dma_ops(&dma_direct_ops);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
384
  }
7e11580b3   Johannes Berg   [POWERPC] DART io...
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
  #ifdef CONFIG_PM
  static void iommu_dart_save(void)
  {
  	memcpy(dart_copy, dart_vbase, 2*1024*1024);
  }
  
  static void iommu_dart_restore(void)
  {
  	memcpy(dart_vbase, dart_copy, 2*1024*1024);
  	dart_tlb_invalidate_all();
  }
  
  static int __init iommu_init_late_dart(void)
  {
  	unsigned long tbasepfn;
  	struct page *p;
  
  	/* if no dart table exists then we won't need to save it
  	 * and the area has also not been reserved */
  	if (!dart_tablebase)
  		return 0;
  
  	tbasepfn = __pa(dart_tablebase) >> PAGE_SHIFT;
  	register_nosave_region_late(tbasepfn,
  				    tbasepfn + ((1<<24) >> PAGE_SHIFT));
  
  	/* For suspend we need to copy the dart contents because
  	 * it is not part of the regular mapping (see above) and
  	 * thus not saved automatically. The memory for this copy
  	 * must be allocated early because we need 2 MB. */
  	p = alloc_pages(GFP_KERNEL, 21 - PAGE_SHIFT);
  	BUG_ON(!p);
  	dart_copy = page_address(p);
  
  	ppc_md.iommu_save = iommu_dart_save;
  	ppc_md.iommu_restore = iommu_dart_restore;
  
  	return 0;
  }
  
  late_initcall(iommu_init_late_dart);
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
427

1beb6a7d6   Benjamin Herrenschmidt   [PATCH] powerpc: ...
428
  void __init alloc_dart_table(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
429
  {
288977313   Olof Johansson   [PATCH] powerpc: ...
430
  	/* Only reserve DART space if machine has more than 1GB of RAM
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
431
  	 * or if requested with iommu=on on cmdline.
288977313   Olof Johansson   [PATCH] powerpc: ...
432
433
434
  	 *
  	 * 1GB of RAM is picked as limit because some default devices
  	 * (i.e. Airport Extreme) have 30 bit address range limits.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
435
  	 */
288977313   Olof Johansson   [PATCH] powerpc: ...
436
437
438
  
  	if (iommu_is_off)
  		return;
95f72d1ed   Yinghai Lu   lmb: rename to me...
439
  	if (!iommu_force_on && memblock_end_of_DRAM() <= 0x40000000ull)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
440
441
442
443
444
445
446
447
  		return;
  
  	/* 512 pages (2MB) is max DART tablesize. */
  	dart_tablesize = 1UL << 21;
  	/* 16MB (1 << 24) alignment. We allocate a full 16Mb chuck since we
  	 * will blow up an entire large page anyway in the kernel mapping
  	 */
  	dart_tablebase = (unsigned long)
95f72d1ed   Yinghai Lu   lmb: rename to me...
448
  		abs_to_virt(memblock_alloc_base(1UL<<24, 1UL<<24, 0x80000000L));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
449

1beb6a7d6   Benjamin Herrenschmidt   [PATCH] powerpc: ...
450
451
  	printk(KERN_INFO "DART table allocated at: %lx
  ", dart_tablebase);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
452
  }