xref: /openbmc/linux/arch/powerpc/kernel/iommu.c (revision 96d3c5a7d20ec546e44695983fe0508c6f904248)
11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
27568cb4eSPaul Mackerras /*
37568cb4eSPaul Mackerras  * Copyright (C) 2001 Mike Corrigan & Dave Engebretsen, IBM Corporation
47568cb4eSPaul Mackerras  *
57568cb4eSPaul Mackerras  * Rewrite, cleanup, new allocation schemes, virtual merging:
67568cb4eSPaul Mackerras  * Copyright (C) 2004 Olof Johansson, IBM Corporation
77568cb4eSPaul Mackerras  *               and  Ben. Herrenschmidt, IBM Corporation
87568cb4eSPaul Mackerras  *
97568cb4eSPaul Mackerras  * Dynamic DMA mapping support, bus-independent parts.
107568cb4eSPaul Mackerras  */
117568cb4eSPaul Mackerras 
127568cb4eSPaul Mackerras 
137568cb4eSPaul Mackerras #include <linux/init.h>
147568cb4eSPaul Mackerras #include <linux/types.h>
157568cb4eSPaul Mackerras #include <linux/slab.h>
167568cb4eSPaul Mackerras #include <linux/mm.h>
177568cb4eSPaul Mackerras #include <linux/spinlock.h>
187568cb4eSPaul Mackerras #include <linux/string.h>
197568cb4eSPaul Mackerras #include <linux/dma-mapping.h>
20a66022c4SAkinobu Mita #include <linux/bitmap.h>
21fb3475e9SFUJITA Tomonori #include <linux/iommu-helper.h>
2262a8bd6cSMilton Miller #include <linux/crash_dump.h>
23b4c3a872SAnton Blanchard #include <linux/hash.h>
24d6b9a81bSAnton Blanchard #include <linux/fault-inject.h>
25d6b9a81bSAnton Blanchard #include <linux/pci.h>
264e13c1acSAlexey Kardashevskiy #include <linux/iommu.h>
274e13c1acSAlexey Kardashevskiy #include <linux/sched.h>
28691602aaSAlexey Kardashevskiy #include <linux/debugfs.h>
297568cb4eSPaul Mackerras #include <asm/io.h>
307568cb4eSPaul Mackerras #include <asm/iommu.h>
317568cb4eSPaul Mackerras #include <asm/pci-bridge.h>
327568cb4eSPaul Mackerras #include <asm/machdep.h>
335f50867bSHaren Myneni #include <asm/kdump.h>
343ccc00a7SMahesh Salgaonkar #include <asm/fadump.h>
35d6b9a81bSAnton Blanchard #include <asm/vio.h>
364e13c1acSAlexey Kardashevskiy #include <asm/tce.h>
37c10c21efSAlexey Kardashevskiy #include <asm/mmu_context.h>
38a9409044SAlexey Kardashevskiy #include <asm/ppc-pci.h>
397568cb4eSPaul Mackerras 
407568cb4eSPaul Mackerras #define DBG(...)
417568cb4eSPaul Mackerras 
42691602aaSAlexey Kardashevskiy #ifdef CONFIG_IOMMU_DEBUGFS
iommu_debugfs_weight_get(void * data,u64 * val)43691602aaSAlexey Kardashevskiy static int iommu_debugfs_weight_get(void *data, u64 *val)
44691602aaSAlexey Kardashevskiy {
45691602aaSAlexey Kardashevskiy 	struct iommu_table *tbl = data;
46691602aaSAlexey Kardashevskiy 	*val = bitmap_weight(tbl->it_map, tbl->it_size);
47691602aaSAlexey Kardashevskiy 	return 0;
48691602aaSAlexey Kardashevskiy }
49691602aaSAlexey Kardashevskiy DEFINE_DEBUGFS_ATTRIBUTE(iommu_debugfs_fops_weight, iommu_debugfs_weight_get, NULL, "%llu\n");
50691602aaSAlexey Kardashevskiy 
iommu_debugfs_add(struct iommu_table * tbl)51691602aaSAlexey Kardashevskiy static void iommu_debugfs_add(struct iommu_table *tbl)
52691602aaSAlexey Kardashevskiy {
53691602aaSAlexey Kardashevskiy 	char name[10];
54691602aaSAlexey Kardashevskiy 	struct dentry *liobn_entry;
55691602aaSAlexey Kardashevskiy 
56691602aaSAlexey Kardashevskiy 	sprintf(name, "%08lx", tbl->it_index);
57691602aaSAlexey Kardashevskiy 	liobn_entry = debugfs_create_dir(name, iommu_debugfs_dir);
58691602aaSAlexey Kardashevskiy 
59691602aaSAlexey Kardashevskiy 	debugfs_create_file_unsafe("weight", 0400, liobn_entry, tbl, &iommu_debugfs_fops_weight);
60691602aaSAlexey Kardashevskiy 	debugfs_create_ulong("it_size", 0400, liobn_entry, &tbl->it_size);
61691602aaSAlexey Kardashevskiy 	debugfs_create_ulong("it_page_shift", 0400, liobn_entry, &tbl->it_page_shift);
62691602aaSAlexey Kardashevskiy 	debugfs_create_ulong("it_reserved_start", 0400, liobn_entry, &tbl->it_reserved_start);
63691602aaSAlexey Kardashevskiy 	debugfs_create_ulong("it_reserved_end", 0400, liobn_entry, &tbl->it_reserved_end);
64691602aaSAlexey Kardashevskiy 	debugfs_create_ulong("it_indirect_levels", 0400, liobn_entry, &tbl->it_indirect_levels);
65691602aaSAlexey Kardashevskiy 	debugfs_create_ulong("it_level_size", 0400, liobn_entry, &tbl->it_level_size);
66691602aaSAlexey Kardashevskiy }
67691602aaSAlexey Kardashevskiy 
iommu_debugfs_del(struct iommu_table * tbl)68691602aaSAlexey Kardashevskiy static void iommu_debugfs_del(struct iommu_table *tbl)
69691602aaSAlexey Kardashevskiy {
70691602aaSAlexey Kardashevskiy 	char name[10];
71691602aaSAlexey Kardashevskiy 
72691602aaSAlexey Kardashevskiy 	sprintf(name, "%08lx", tbl->it_index);
73b5050639SGreg Kroah-Hartman 	debugfs_lookup_and_remove(name, iommu_debugfs_dir);
74691602aaSAlexey Kardashevskiy }
75691602aaSAlexey Kardashevskiy #else
iommu_debugfs_add(struct iommu_table * tbl)76691602aaSAlexey Kardashevskiy static void iommu_debugfs_add(struct iommu_table *tbl){}
iommu_debugfs_del(struct iommu_table * tbl)77691602aaSAlexey Kardashevskiy static void iommu_debugfs_del(struct iommu_table *tbl){}
78691602aaSAlexey Kardashevskiy #endif
79691602aaSAlexey Kardashevskiy 
80191aee58SFUJITA Tomonori static int novmerge;
8156997559SJake Moilanen 
826490c490SRobert Jennings static void __iommu_free(struct iommu_table *, dma_addr_t, unsigned int);
836490c490SRobert Jennings 
setup_iommu(char * str)847568cb4eSPaul Mackerras static int __init setup_iommu(char *str)
857568cb4eSPaul Mackerras {
867568cb4eSPaul Mackerras 	if (!strcmp(str, "novmerge"))
877568cb4eSPaul Mackerras 		novmerge = 1;
887568cb4eSPaul Mackerras 	else if (!strcmp(str, "vmerge"))
897568cb4eSPaul Mackerras 		novmerge = 0;
907568cb4eSPaul Mackerras 	return 1;
917568cb4eSPaul Mackerras }
927568cb4eSPaul Mackerras 
937568cb4eSPaul Mackerras __setup("iommu=", setup_iommu);
947568cb4eSPaul Mackerras 
95b4c3a872SAnton Blanchard static DEFINE_PER_CPU(unsigned int, iommu_pool_hash);
96b4c3a872SAnton Blanchard 
97b4c3a872SAnton Blanchard /*
98b4c3a872SAnton Blanchard  * We precalculate the hash to avoid doing it on every allocation.
99b4c3a872SAnton Blanchard  *
100b4c3a872SAnton Blanchard  * The hash is important to spread CPUs across all the pools. For example,
101b4c3a872SAnton Blanchard  * on a POWER7 with 4 way SMT we want interrupts on the primary threads and
102b4c3a872SAnton Blanchard  * with 4 pools all primary threads would map to the same pool.
103b4c3a872SAnton Blanchard  */
setup_iommu_pool_hash(void)104b4c3a872SAnton Blanchard static int __init setup_iommu_pool_hash(void)
105b4c3a872SAnton Blanchard {
106b4c3a872SAnton Blanchard 	unsigned int i;
107b4c3a872SAnton Blanchard 
108b4c3a872SAnton Blanchard 	for_each_possible_cpu(i)
109b4c3a872SAnton Blanchard 		per_cpu(iommu_pool_hash, i) = hash_32(i, IOMMU_POOL_HASHBITS);
110b4c3a872SAnton Blanchard 
111b4c3a872SAnton Blanchard 	return 0;
112b4c3a872SAnton Blanchard }
113b4c3a872SAnton Blanchard subsys_initcall(setup_iommu_pool_hash);
114b4c3a872SAnton Blanchard 
115d6b9a81bSAnton Blanchard #ifdef CONFIG_FAIL_IOMMU
116d6b9a81bSAnton Blanchard 
117d6b9a81bSAnton Blanchard static DECLARE_FAULT_ATTR(fail_iommu);
118d6b9a81bSAnton Blanchard 
setup_fail_iommu(char * str)119d6b9a81bSAnton Blanchard static int __init setup_fail_iommu(char *str)
120d6b9a81bSAnton Blanchard {
121d6b9a81bSAnton Blanchard 	return setup_fault_attr(&fail_iommu, str);
122d6b9a81bSAnton Blanchard }
123d6b9a81bSAnton Blanchard __setup("fail_iommu=", setup_fail_iommu);
124d6b9a81bSAnton Blanchard 
should_fail_iommu(struct device * dev)125d6b9a81bSAnton Blanchard static bool should_fail_iommu(struct device *dev)
126d6b9a81bSAnton Blanchard {
127d6b9a81bSAnton Blanchard 	return dev->archdata.fail_iommu && should_fail(&fail_iommu, 1);
128d6b9a81bSAnton Blanchard }
129d6b9a81bSAnton Blanchard 
fail_iommu_debugfs(void)130d6b9a81bSAnton Blanchard static int __init fail_iommu_debugfs(void)
131d6b9a81bSAnton Blanchard {
132d6b9a81bSAnton Blanchard 	struct dentry *dir = fault_create_debugfs_attr("fail_iommu",
133d6b9a81bSAnton Blanchard 						       NULL, &fail_iommu);
134d6b9a81bSAnton Blanchard 
1358c6ffba0SRusty Russell 	return PTR_ERR_OR_ZERO(dir);
136d6b9a81bSAnton Blanchard }
137d6b9a81bSAnton Blanchard late_initcall(fail_iommu_debugfs);
138d6b9a81bSAnton Blanchard 
fail_iommu_show(struct device * dev,struct device_attribute * attr,char * buf)139d6b9a81bSAnton Blanchard static ssize_t fail_iommu_show(struct device *dev,
140d6b9a81bSAnton Blanchard 			       struct device_attribute *attr, char *buf)
141d6b9a81bSAnton Blanchard {
142d6b9a81bSAnton Blanchard 	return sprintf(buf, "%d\n", dev->archdata.fail_iommu);
143d6b9a81bSAnton Blanchard }
144d6b9a81bSAnton Blanchard 
fail_iommu_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)145d6b9a81bSAnton Blanchard static ssize_t fail_iommu_store(struct device *dev,
146d6b9a81bSAnton Blanchard 				struct device_attribute *attr, const char *buf,
147d6b9a81bSAnton Blanchard 				size_t count)
148d6b9a81bSAnton Blanchard {
149d6b9a81bSAnton Blanchard 	int i;
150d6b9a81bSAnton Blanchard 
151d6b9a81bSAnton Blanchard 	if (count > 0 && sscanf(buf, "%d", &i) > 0)
152d6b9a81bSAnton Blanchard 		dev->archdata.fail_iommu = (i == 0) ? 0 : 1;
153d6b9a81bSAnton Blanchard 
154d6b9a81bSAnton Blanchard 	return count;
155d6b9a81bSAnton Blanchard }
156d6b9a81bSAnton Blanchard 
1578a7aef2cSJulia Lawall static DEVICE_ATTR_RW(fail_iommu);
158d6b9a81bSAnton Blanchard 
fail_iommu_bus_notify(struct notifier_block * nb,unsigned long action,void * data)159d6b9a81bSAnton Blanchard static int fail_iommu_bus_notify(struct notifier_block *nb,
160d6b9a81bSAnton Blanchard 				 unsigned long action, void *data)
161d6b9a81bSAnton Blanchard {
162d6b9a81bSAnton Blanchard 	struct device *dev = data;
163d6b9a81bSAnton Blanchard 
164d6b9a81bSAnton Blanchard 	if (action == BUS_NOTIFY_ADD_DEVICE) {
165d6b9a81bSAnton Blanchard 		if (device_create_file(dev, &dev_attr_fail_iommu))
166d6b9a81bSAnton Blanchard 			pr_warn("Unable to create IOMMU fault injection sysfs "
167d6b9a81bSAnton Blanchard 				"entries\n");
168d6b9a81bSAnton Blanchard 	} else if (action == BUS_NOTIFY_DEL_DEVICE) {
169d6b9a81bSAnton Blanchard 		device_remove_file(dev, &dev_attr_fail_iommu);
170d6b9a81bSAnton Blanchard 	}
171d6b9a81bSAnton Blanchard 
172d6b9a81bSAnton Blanchard 	return 0;
173d6b9a81bSAnton Blanchard }
174d6b9a81bSAnton Blanchard 
175c37b6908SRussell Currey /*
176c37b6908SRussell Currey  * PCI and VIO buses need separate notifier_block structs, since they're linked
177c37b6908SRussell Currey  * list nodes.  Sharing a notifier_block would mean that any notifiers later
178c37b6908SRussell Currey  * registered for PCI buses would also get called by VIO buses and vice versa.
179c37b6908SRussell Currey  */
180c37b6908SRussell Currey static struct notifier_block fail_iommu_pci_bus_notifier = {
181d6b9a81bSAnton Blanchard 	.notifier_call = fail_iommu_bus_notify
182d6b9a81bSAnton Blanchard };
183d6b9a81bSAnton Blanchard 
184c37b6908SRussell Currey #ifdef CONFIG_IBMVIO
185c37b6908SRussell Currey static struct notifier_block fail_iommu_vio_bus_notifier = {
186c37b6908SRussell Currey 	.notifier_call = fail_iommu_bus_notify
187c37b6908SRussell Currey };
188c37b6908SRussell Currey #endif
189c37b6908SRussell Currey 
fail_iommu_setup(void)190d6b9a81bSAnton Blanchard static int __init fail_iommu_setup(void)
191d6b9a81bSAnton Blanchard {
192d6b9a81bSAnton Blanchard #ifdef CONFIG_PCI
193c37b6908SRussell Currey 	bus_register_notifier(&pci_bus_type, &fail_iommu_pci_bus_notifier);
194d6b9a81bSAnton Blanchard #endif
195d6b9a81bSAnton Blanchard #ifdef CONFIG_IBMVIO
196c37b6908SRussell Currey 	bus_register_notifier(&vio_bus_type, &fail_iommu_vio_bus_notifier);
197d6b9a81bSAnton Blanchard #endif
198d6b9a81bSAnton Blanchard 
199d6b9a81bSAnton Blanchard 	return 0;
200d6b9a81bSAnton Blanchard }
201d6b9a81bSAnton Blanchard /*
202d6b9a81bSAnton Blanchard  * Must execute after PCI and VIO subsystem have initialised but before
203d6b9a81bSAnton Blanchard  * devices are probed.
204d6b9a81bSAnton Blanchard  */
205d6b9a81bSAnton Blanchard arch_initcall(fail_iommu_setup);
206d6b9a81bSAnton Blanchard #else
should_fail_iommu(struct device * dev)207d6b9a81bSAnton Blanchard static inline bool should_fail_iommu(struct device *dev)
208d6b9a81bSAnton Blanchard {
209d6b9a81bSAnton Blanchard 	return false;
210d6b9a81bSAnton Blanchard }
211d6b9a81bSAnton Blanchard #endif
212d6b9a81bSAnton Blanchard 
iommu_range_alloc(struct device * dev,struct iommu_table * tbl,unsigned long npages,unsigned long * handle,unsigned long mask,unsigned int align_order)213fb3475e9SFUJITA Tomonori static unsigned long iommu_range_alloc(struct device *dev,
214fb3475e9SFUJITA Tomonori 				       struct iommu_table *tbl,
2157568cb4eSPaul Mackerras                                        unsigned long npages,
2167568cb4eSPaul Mackerras                                        unsigned long *handle,
2177daa411bSOlof Johansson                                        unsigned long mask,
2187568cb4eSPaul Mackerras                                        unsigned int align_order)
2197568cb4eSPaul Mackerras {
220fb3475e9SFUJITA Tomonori 	unsigned long n, end, start;
2217568cb4eSPaul Mackerras 	unsigned long limit;
2227568cb4eSPaul Mackerras 	int largealloc = npages > 15;
2237568cb4eSPaul Mackerras 	int pass = 0;
2247568cb4eSPaul Mackerras 	unsigned long align_mask;
225d3622137SAnton Blanchard 	unsigned long flags;
226b4c3a872SAnton Blanchard 	unsigned int pool_nr;
227b4c3a872SAnton Blanchard 	struct iommu_pool *pool;
2287568cb4eSPaul Mackerras 
22963b85621SMichael Ellerman 	align_mask = (1ull << align_order) - 1;
2307568cb4eSPaul Mackerras 
2317568cb4eSPaul Mackerras 	/* This allocator was derived from x86_64's bit string search */
2327568cb4eSPaul Mackerras 
2337568cb4eSPaul Mackerras 	/* Sanity check */
23413a2eea1SNick Piggin 	if (unlikely(npages == 0)) {
2357568cb4eSPaul Mackerras 		if (printk_ratelimit())
2367568cb4eSPaul Mackerras 			WARN_ON(1);
237d11e3d3dSChristoph Hellwig 		return DMA_MAPPING_ERROR;
2387568cb4eSPaul Mackerras 	}
2397568cb4eSPaul Mackerras 
240d6b9a81bSAnton Blanchard 	if (should_fail_iommu(dev))
241d11e3d3dSChristoph Hellwig 		return DMA_MAPPING_ERROR;
242d6b9a81bSAnton Blanchard 
243b4c3a872SAnton Blanchard 	/*
244b4c3a872SAnton Blanchard 	 * We don't need to disable preemption here because any CPU can
245b4c3a872SAnton Blanchard 	 * safely use any IOMMU pool.
246b4c3a872SAnton Blanchard 	 */
24775f327c6SVictor Aoqui 	pool_nr = raw_cpu_read(iommu_pool_hash) & (tbl->nr_pools - 1);
248d3622137SAnton Blanchard 
249b4c3a872SAnton Blanchard 	if (largealloc)
250b4c3a872SAnton Blanchard 		pool = &(tbl->large_pool);
251b4c3a872SAnton Blanchard 	else
252b4c3a872SAnton Blanchard 		pool = &(tbl->pools[pool_nr]);
253b4c3a872SAnton Blanchard 
254b4c3a872SAnton Blanchard 	spin_lock_irqsave(&(pool->lock), flags);
255b4c3a872SAnton Blanchard 
256b4c3a872SAnton Blanchard again:
257d900bd73SAnton Blanchard 	if ((pass == 0) && handle && *handle &&
258d900bd73SAnton Blanchard 	    (*handle >= pool->start) && (*handle < pool->end))
2597568cb4eSPaul Mackerras 		start = *handle;
2607568cb4eSPaul Mackerras 	else
261b4c3a872SAnton Blanchard 		start = pool->hint;
2627568cb4eSPaul Mackerras 
263b4c3a872SAnton Blanchard 	limit = pool->end;
2647568cb4eSPaul Mackerras 
2657568cb4eSPaul Mackerras 	/* The case below can happen if we have a small segment appended
2667568cb4eSPaul Mackerras 	 * to a large, or when the previous alloc was at the very end of
2677568cb4eSPaul Mackerras 	 * the available space. If so, go back to the initial start.
2687568cb4eSPaul Mackerras 	 */
2697568cb4eSPaul Mackerras 	if (start >= limit)
270b4c3a872SAnton Blanchard 		start = pool->start;
2717568cb4eSPaul Mackerras 
2727daa411bSOlof Johansson 	if (limit + tbl->it_offset > mask) {
2737daa411bSOlof Johansson 		limit = mask - tbl->it_offset + 1;
2747daa411bSOlof Johansson 		/* If we're constrained on address range, first try
2757daa411bSOlof Johansson 		 * at the masked hint to avoid O(n) search complexity,
276b4c3a872SAnton Blanchard 		 * but on second pass, start at 0 in pool 0.
2777daa411bSOlof Johansson 		 */
278b4c3a872SAnton Blanchard 		if ((start & mask) >= limit || pass > 0) {
279d900bd73SAnton Blanchard 			spin_unlock(&(pool->lock));
280b4c3a872SAnton Blanchard 			pool = &(tbl->pools[0]);
281d900bd73SAnton Blanchard 			spin_lock(&(pool->lock));
282b4c3a872SAnton Blanchard 			start = pool->start;
283b4c3a872SAnton Blanchard 		} else {
2847daa411bSOlof Johansson 			start &= mask;
2857daa411bSOlof Johansson 		}
286b4c3a872SAnton Blanchard 	}
2877daa411bSOlof Johansson 
288d0847757SAlistair Popple 	n = iommu_area_alloc(tbl->it_map, limit, start, npages, tbl->it_offset,
2891e9d90dbSNicolin Chen 			dma_get_seg_boundary_nr_pages(dev, tbl->it_page_shift),
2901e9d90dbSNicolin Chen 			align_mask);
291fb3475e9SFUJITA Tomonori 	if (n == -1) {
292b4c3a872SAnton Blanchard 		if (likely(pass == 0)) {
293b4c3a872SAnton Blanchard 			/* First try the pool from the start */
294b4c3a872SAnton Blanchard 			pool->hint = pool->start;
2957568cb4eSPaul Mackerras 			pass++;
2967568cb4eSPaul Mackerras 			goto again;
297b4c3a872SAnton Blanchard 
298b4c3a872SAnton Blanchard 		} else if (pass <= tbl->nr_pools) {
299b4c3a872SAnton Blanchard 			/* Now try scanning all the other pools */
300b4c3a872SAnton Blanchard 			spin_unlock(&(pool->lock));
301b4c3a872SAnton Blanchard 			pool_nr = (pool_nr + 1) & (tbl->nr_pools - 1);
302b4c3a872SAnton Blanchard 			pool = &tbl->pools[pool_nr];
303b4c3a872SAnton Blanchard 			spin_lock(&(pool->lock));
304b4c3a872SAnton Blanchard 			pool->hint = pool->start;
305b4c3a872SAnton Blanchard 			pass++;
306b4c3a872SAnton Blanchard 			goto again;
307b4c3a872SAnton Blanchard 
308fc5590fdSLeonardo Bras 		} else if (pass == tbl->nr_pools + 1) {
309fc5590fdSLeonardo Bras 			/* Last resort: try largepool */
310fc5590fdSLeonardo Bras 			spin_unlock(&pool->lock);
311fc5590fdSLeonardo Bras 			pool = &tbl->large_pool;
312fc5590fdSLeonardo Bras 			spin_lock(&pool->lock);
313fc5590fdSLeonardo Bras 			pool->hint = pool->start;
314fc5590fdSLeonardo Bras 			pass++;
315fc5590fdSLeonardo Bras 			goto again;
316fc5590fdSLeonardo Bras 
3177568cb4eSPaul Mackerras 		} else {
318b4c3a872SAnton Blanchard 			/* Give up */
319b4c3a872SAnton Blanchard 			spin_unlock_irqrestore(&(pool->lock), flags);
320d11e3d3dSChristoph Hellwig 			return DMA_MAPPING_ERROR;
3217568cb4eSPaul Mackerras 		}
3227568cb4eSPaul Mackerras 	}
3237568cb4eSPaul Mackerras 
324fb3475e9SFUJITA Tomonori 	end = n + npages;
3257568cb4eSPaul Mackerras 
3267568cb4eSPaul Mackerras 	/* Bump the hint to a new block for small allocs. */
3277568cb4eSPaul Mackerras 	if (largealloc) {
3287568cb4eSPaul Mackerras 		/* Don't bump to new block to avoid fragmentation */
329b4c3a872SAnton Blanchard 		pool->hint = end;
3307568cb4eSPaul Mackerras 	} else {
3317568cb4eSPaul Mackerras 		/* Overflow will be taken care of at the next allocation */
332b4c3a872SAnton Blanchard 		pool->hint = (end + tbl->it_blocksize - 1) &
3337568cb4eSPaul Mackerras 		                ~(tbl->it_blocksize - 1);
3347568cb4eSPaul Mackerras 	}
3357568cb4eSPaul Mackerras 
3367568cb4eSPaul Mackerras 	/* Update handle for SG allocations */
3377568cb4eSPaul Mackerras 	if (handle)
3387568cb4eSPaul Mackerras 		*handle = end;
3397568cb4eSPaul Mackerras 
340b4c3a872SAnton Blanchard 	spin_unlock_irqrestore(&(pool->lock), flags);
341b4c3a872SAnton Blanchard 
3427568cb4eSPaul Mackerras 	return n;
3437568cb4eSPaul Mackerras }
3447568cb4eSPaul Mackerras 
iommu_alloc(struct device * dev,struct iommu_table * tbl,void * page,unsigned int npages,enum dma_data_direction direction,unsigned long mask,unsigned int align_order,unsigned long attrs)345fb3475e9SFUJITA Tomonori static dma_addr_t iommu_alloc(struct device *dev, struct iommu_table *tbl,
346fb3475e9SFUJITA Tomonori 			      void *page, unsigned int npages,
347fb3475e9SFUJITA Tomonori 			      enum dma_data_direction direction,
3484f3dd8a0SMark Nelson 			      unsigned long mask, unsigned int align_order,
34900085f1eSKrzysztof Kozlowski 			      unsigned long attrs)
3507568cb4eSPaul Mackerras {
351d3622137SAnton Blanchard 	unsigned long entry;
352d11e3d3dSChristoph Hellwig 	dma_addr_t ret = DMA_MAPPING_ERROR;
3536490c490SRobert Jennings 	int build_fail;
3547568cb4eSPaul Mackerras 
355fb3475e9SFUJITA Tomonori 	entry = iommu_range_alloc(dev, tbl, npages, NULL, mask, align_order);
3560e4bc95dSAnton Blanchard 
357d11e3d3dSChristoph Hellwig 	if (unlikely(entry == DMA_MAPPING_ERROR))
358d11e3d3dSChristoph Hellwig 		return DMA_MAPPING_ERROR;
3597568cb4eSPaul Mackerras 
3607568cb4eSPaul Mackerras 	entry += tbl->it_offset;	/* Offset into real TCE table */
361d0847757SAlistair Popple 	ret = entry << tbl->it_page_shift;	/* Set the return dma address */
3627568cb4eSPaul Mackerras 
3637568cb4eSPaul Mackerras 	/* Put the TCEs in the HW table */
364da004c36SAlexey Kardashevskiy 	build_fail = tbl->it_ops->set(tbl, entry, npages,
365d0847757SAlistair Popple 				      (unsigned long)page &
366d0847757SAlistair Popple 				      IOMMU_PAGE_MASK(tbl), direction, attrs);
3677568cb4eSPaul Mackerras 
368da004c36SAlexey Kardashevskiy 	/* tbl->it_ops->set() only returns non-zero for transient errors.
3696490c490SRobert Jennings 	 * Clean up the table bitmap in this case and return
370d11e3d3dSChristoph Hellwig 	 * DMA_MAPPING_ERROR. For all other errors the functionality is
3716490c490SRobert Jennings 	 * not altered.
3726490c490SRobert Jennings 	 */
3736490c490SRobert Jennings 	if (unlikely(build_fail)) {
3746490c490SRobert Jennings 		__iommu_free(tbl, ret, npages);
375d11e3d3dSChristoph Hellwig 		return DMA_MAPPING_ERROR;
3766490c490SRobert Jennings 	}
3777568cb4eSPaul Mackerras 
3787568cb4eSPaul Mackerras 	/* Flush/invalidate TLB caches if necessary */
379da004c36SAlexey Kardashevskiy 	if (tbl->it_ops->flush)
380da004c36SAlexey Kardashevskiy 		tbl->it_ops->flush(tbl);
3817568cb4eSPaul Mackerras 
3827568cb4eSPaul Mackerras 	/* Make sure updates are seen by hardware */
3837568cb4eSPaul Mackerras 	mb();
3847568cb4eSPaul Mackerras 
3857568cb4eSPaul Mackerras 	return ret;
3867568cb4eSPaul Mackerras }
3877568cb4eSPaul Mackerras 
iommu_free_check(struct iommu_table * tbl,dma_addr_t dma_addr,unsigned int npages)38867ca1415SAnton Blanchard static bool iommu_free_check(struct iommu_table *tbl, dma_addr_t dma_addr,
3897568cb4eSPaul Mackerras 			     unsigned int npages)
3907568cb4eSPaul Mackerras {
3917568cb4eSPaul Mackerras 	unsigned long entry, free_entry;
3927568cb4eSPaul Mackerras 
393d0847757SAlistair Popple 	entry = dma_addr >> tbl->it_page_shift;
3947568cb4eSPaul Mackerras 	free_entry = entry - tbl->it_offset;
3957568cb4eSPaul Mackerras 
3967568cb4eSPaul Mackerras 	if (((free_entry + npages) > tbl->it_size) ||
3977568cb4eSPaul Mackerras 	    (entry < tbl->it_offset)) {
3987568cb4eSPaul Mackerras 		if (printk_ratelimit()) {
3997568cb4eSPaul Mackerras 			printk(KERN_INFO "iommu_free: invalid entry\n");
4007568cb4eSPaul Mackerras 			printk(KERN_INFO "\tentry     = 0x%lx\n", entry);
401fe333321SIngo Molnar 			printk(KERN_INFO "\tdma_addr  = 0x%llx\n", (u64)dma_addr);
402fe333321SIngo Molnar 			printk(KERN_INFO "\tTable     = 0x%llx\n", (u64)tbl);
403fe333321SIngo Molnar 			printk(KERN_INFO "\tbus#      = 0x%llx\n", (u64)tbl->it_busno);
404fe333321SIngo Molnar 			printk(KERN_INFO "\tsize      = 0x%llx\n", (u64)tbl->it_size);
405fe333321SIngo Molnar 			printk(KERN_INFO "\tstartOff  = 0x%llx\n", (u64)tbl->it_offset);
406fe333321SIngo Molnar 			printk(KERN_INFO "\tindex     = 0x%llx\n", (u64)tbl->it_index);
4077568cb4eSPaul Mackerras 			WARN_ON(1);
4087568cb4eSPaul Mackerras 		}
40967ca1415SAnton Blanchard 
41067ca1415SAnton Blanchard 		return false;
4117568cb4eSPaul Mackerras 	}
4127568cb4eSPaul Mackerras 
41367ca1415SAnton Blanchard 	return true;
41467ca1415SAnton Blanchard }
41567ca1415SAnton Blanchard 
get_pool(struct iommu_table * tbl,unsigned long entry)416b4c3a872SAnton Blanchard static struct iommu_pool *get_pool(struct iommu_table *tbl,
417b4c3a872SAnton Blanchard 				   unsigned long entry)
418b4c3a872SAnton Blanchard {
419b4c3a872SAnton Blanchard 	struct iommu_pool *p;
420b4c3a872SAnton Blanchard 	unsigned long largepool_start = tbl->large_pool.start;
421b4c3a872SAnton Blanchard 
422b4c3a872SAnton Blanchard 	/* The large pool is the last pool at the top of the table */
423b4c3a872SAnton Blanchard 	if (entry >= largepool_start) {
424b4c3a872SAnton Blanchard 		p = &tbl->large_pool;
425b4c3a872SAnton Blanchard 	} else {
426b4c3a872SAnton Blanchard 		unsigned int pool_nr = entry / tbl->poolsize;
427b4c3a872SAnton Blanchard 
428b4c3a872SAnton Blanchard 		BUG_ON(pool_nr > tbl->nr_pools);
429b4c3a872SAnton Blanchard 		p = &tbl->pools[pool_nr];
430b4c3a872SAnton Blanchard 	}
431b4c3a872SAnton Blanchard 
432b4c3a872SAnton Blanchard 	return p;
433b4c3a872SAnton Blanchard }
434b4c3a872SAnton Blanchard 
__iommu_free(struct iommu_table * tbl,dma_addr_t dma_addr,unsigned int npages)43567ca1415SAnton Blanchard static void __iommu_free(struct iommu_table *tbl, dma_addr_t dma_addr,
43667ca1415SAnton Blanchard 			 unsigned int npages)
43767ca1415SAnton Blanchard {
43867ca1415SAnton Blanchard 	unsigned long entry, free_entry;
43967ca1415SAnton Blanchard 	unsigned long flags;
440b4c3a872SAnton Blanchard 	struct iommu_pool *pool;
44167ca1415SAnton Blanchard 
442d0847757SAlistair Popple 	entry = dma_addr >> tbl->it_page_shift;
44367ca1415SAnton Blanchard 	free_entry = entry - tbl->it_offset;
44467ca1415SAnton Blanchard 
445b4c3a872SAnton Blanchard 	pool = get_pool(tbl, free_entry);
446b4c3a872SAnton Blanchard 
44767ca1415SAnton Blanchard 	if (!iommu_free_check(tbl, dma_addr, npages))
44867ca1415SAnton Blanchard 		return;
44967ca1415SAnton Blanchard 
450da004c36SAlexey Kardashevskiy 	tbl->it_ops->clear(tbl, entry, npages);
45167ca1415SAnton Blanchard 
452b4c3a872SAnton Blanchard 	spin_lock_irqsave(&(pool->lock), flags);
45367ca1415SAnton Blanchard 	bitmap_clear(tbl->it_map, free_entry, npages);
454b4c3a872SAnton Blanchard 	spin_unlock_irqrestore(&(pool->lock), flags);
45567ca1415SAnton Blanchard }
45667ca1415SAnton Blanchard 
iommu_free(struct iommu_table * tbl,dma_addr_t dma_addr,unsigned int npages)4577568cb4eSPaul Mackerras static void iommu_free(struct iommu_table *tbl, dma_addr_t dma_addr,
4587568cb4eSPaul Mackerras 		unsigned int npages)
4597568cb4eSPaul Mackerras {
4607568cb4eSPaul Mackerras 	__iommu_free(tbl, dma_addr, npages);
4617568cb4eSPaul Mackerras 
4627568cb4eSPaul Mackerras 	/* Make sure TLB cache is flushed if the HW needs it. We do
4637568cb4eSPaul Mackerras 	 * not do an mb() here on purpose, it is not needed on any of
4647568cb4eSPaul Mackerras 	 * the current platforms.
4657568cb4eSPaul Mackerras 	 */
466da004c36SAlexey Kardashevskiy 	if (tbl->it_ops->flush)
467da004c36SAlexey Kardashevskiy 		tbl->it_ops->flush(tbl);
4687568cb4eSPaul Mackerras }
4697568cb4eSPaul Mackerras 
ppc_iommu_map_sg(struct device * dev,struct iommu_table * tbl,struct scatterlist * sglist,int nelems,unsigned long mask,enum dma_data_direction direction,unsigned long attrs)4700690cbd2SJoerg Roedel int ppc_iommu_map_sg(struct device *dev, struct iommu_table *tbl,
471c8692362SMark Nelson 		     struct scatterlist *sglist, int nelems,
4723affedc4SMark Nelson 		     unsigned long mask, enum dma_data_direction direction,
47300085f1eSKrzysztof Kozlowski 		     unsigned long attrs)
4747568cb4eSPaul Mackerras {
4757568cb4eSPaul Mackerras 	dma_addr_t dma_next = 0, dma_addr;
4767568cb4eSPaul Mackerras 	struct scatterlist *s, *outs, *segstart;
4776490c490SRobert Jennings 	int outcount, incount, i, build_fail = 0;
478d262c32aSBenjamin Herrenschmidt 	unsigned int align;
4797568cb4eSPaul Mackerras 	unsigned long handle;
480740c3ce6SFUJITA Tomonori 	unsigned int max_seg_size;
4817568cb4eSPaul Mackerras 
4827568cb4eSPaul Mackerras 	BUG_ON(direction == DMA_NONE);
4837568cb4eSPaul Mackerras 
4847568cb4eSPaul Mackerras 	if ((nelems == 0) || !tbl)
485c4e0e892SMartin Oliveira 		return -EINVAL;
4867568cb4eSPaul Mackerras 
4877568cb4eSPaul Mackerras 	outs = s = segstart = &sglist[0];
4887568cb4eSPaul Mackerras 	outcount = 1;
4897568cb4eSPaul Mackerras 	incount = nelems;
4907568cb4eSPaul Mackerras 	handle = 0;
4917568cb4eSPaul Mackerras 
4927568cb4eSPaul Mackerras 	/* Init first segment length for backout at failure */
4937568cb4eSPaul Mackerras 	outs->dma_length = 0;
4947568cb4eSPaul Mackerras 
4955d2efba6SLinas Vepstas 	DBG("sg mapping %d elements:\n", nelems);
4967568cb4eSPaul Mackerras 
497740c3ce6SFUJITA Tomonori 	max_seg_size = dma_get_max_seg_size(dev);
49878bdc310SJens Axboe 	for_each_sg(sglist, s, nelems, i) {
4997568cb4eSPaul Mackerras 		unsigned long vaddr, npages, entry, slen;
5007568cb4eSPaul Mackerras 
5017568cb4eSPaul Mackerras 		slen = s->length;
5027568cb4eSPaul Mackerras 		/* Sanity check */
5037568cb4eSPaul Mackerras 		if (slen == 0) {
5047568cb4eSPaul Mackerras 			dma_next = 0;
5057568cb4eSPaul Mackerras 			continue;
5067568cb4eSPaul Mackerras 		}
5077568cb4eSPaul Mackerras 		/* Allocate iommu entries for that segment */
50858b053e4SJens Axboe 		vaddr = (unsigned long) sg_virt(s);
509d0847757SAlistair Popple 		npages = iommu_num_pages(vaddr, slen, IOMMU_PAGE_SIZE(tbl));
510d262c32aSBenjamin Herrenschmidt 		align = 0;
511d0847757SAlistair Popple 		if (tbl->it_page_shift < PAGE_SHIFT && slen >= PAGE_SIZE &&
512d262c32aSBenjamin Herrenschmidt 		    (vaddr & ~PAGE_MASK) == 0)
513d0847757SAlistair Popple 			align = PAGE_SHIFT - tbl->it_page_shift;
514fb3475e9SFUJITA Tomonori 		entry = iommu_range_alloc(dev, tbl, npages, &handle,
515d0847757SAlistair Popple 					  mask >> tbl->it_page_shift, align);
5167568cb4eSPaul Mackerras 
5177568cb4eSPaul Mackerras 		DBG("  - vaddr: %lx, size: %lx\n", vaddr, slen);
5187568cb4eSPaul Mackerras 
5197568cb4eSPaul Mackerras 		/* Handle failure */
520d11e3d3dSChristoph Hellwig 		if (unlikely(entry == DMA_MAPPING_ERROR)) {
521af8a2498SMauricio Faria de Oliveira 			if (!(attrs & DMA_ATTR_NO_WARN) &&
522af8a2498SMauricio Faria de Oliveira 			    printk_ratelimit())
5234dfa9c47SAnton Blanchard 				dev_info(dev, "iommu_alloc failed, tbl %p "
5244dfa9c47SAnton Blanchard 					 "vaddr %lx npages %lu\n", tbl, vaddr,
5254dfa9c47SAnton Blanchard 					 npages);
5267568cb4eSPaul Mackerras 			goto failure;
5277568cb4eSPaul Mackerras 		}
5287568cb4eSPaul Mackerras 
5297568cb4eSPaul Mackerras 		/* Convert entry to a dma_addr_t */
5307568cb4eSPaul Mackerras 		entry += tbl->it_offset;
531d0847757SAlistair Popple 		dma_addr = entry << tbl->it_page_shift;
532096339abSGaurav Batra 		dma_addr |= (vaddr & ~IOMMU_PAGE_MASK(tbl));
5337568cb4eSPaul Mackerras 
5345d2efba6SLinas Vepstas 		DBG("  - %lu pages, entry: %lx, dma_addr: %lx\n",
5357568cb4eSPaul Mackerras 			    npages, entry, dma_addr);
5367568cb4eSPaul Mackerras 
5377568cb4eSPaul Mackerras 		/* Insert into HW table */
538da004c36SAlexey Kardashevskiy 		build_fail = tbl->it_ops->set(tbl, entry, npages,
539d0847757SAlistair Popple 					      vaddr & IOMMU_PAGE_MASK(tbl),
5404f3dd8a0SMark Nelson 					      direction, attrs);
5416490c490SRobert Jennings 		if(unlikely(build_fail))
5426490c490SRobert Jennings 			goto failure;
5437568cb4eSPaul Mackerras 
5447568cb4eSPaul Mackerras 		/* If we are in an open segment, try merging */
5457568cb4eSPaul Mackerras 		if (segstart != s) {
5467568cb4eSPaul Mackerras 			DBG("  - trying merge...\n");
5477568cb4eSPaul Mackerras 			/* We cannot merge if:
5487568cb4eSPaul Mackerras 			 * - allocated dma_addr isn't contiguous to previous allocation
5497568cb4eSPaul Mackerras 			 */
550740c3ce6SFUJITA Tomonori 			if (novmerge || (dma_addr != dma_next) ||
551740c3ce6SFUJITA Tomonori 			    (outs->dma_length + s->length > max_seg_size)) {
5527568cb4eSPaul Mackerras 				/* Can't merge: create a new segment */
5537568cb4eSPaul Mackerras 				segstart = s;
55478bdc310SJens Axboe 				outcount++;
55578bdc310SJens Axboe 				outs = sg_next(outs);
5567568cb4eSPaul Mackerras 				DBG("    can't merge, new segment.\n");
5577568cb4eSPaul Mackerras 			} else {
5587568cb4eSPaul Mackerras 				outs->dma_length += s->length;
5595d2efba6SLinas Vepstas 				DBG("    merged, new len: %ux\n", outs->dma_length);
5607568cb4eSPaul Mackerras 			}
5617568cb4eSPaul Mackerras 		}
5627568cb4eSPaul Mackerras 
5637568cb4eSPaul Mackerras 		if (segstart == s) {
5647568cb4eSPaul Mackerras 			/* This is a new segment, fill entries */
5657568cb4eSPaul Mackerras 			DBG("  - filling new segment.\n");
5667568cb4eSPaul Mackerras 			outs->dma_address = dma_addr;
5677568cb4eSPaul Mackerras 			outs->dma_length = slen;
5687568cb4eSPaul Mackerras 		}
5697568cb4eSPaul Mackerras 
5707568cb4eSPaul Mackerras 		/* Calculate next page pointer for contiguous check */
5717568cb4eSPaul Mackerras 		dma_next = dma_addr + slen;
5727568cb4eSPaul Mackerras 
5737568cb4eSPaul Mackerras 		DBG("  - dma next is: %lx\n", dma_next);
5747568cb4eSPaul Mackerras 	}
5757568cb4eSPaul Mackerras 
5767568cb4eSPaul Mackerras 	/* Flush/invalidate TLB caches if necessary */
577da004c36SAlexey Kardashevskiy 	if (tbl->it_ops->flush)
578da004c36SAlexey Kardashevskiy 		tbl->it_ops->flush(tbl);
5797568cb4eSPaul Mackerras 
5807568cb4eSPaul Mackerras 	DBG("mapped %d elements:\n", outcount);
5817568cb4eSPaul Mackerras 
5820690cbd2SJoerg Roedel 	/* For the sake of ppc_iommu_unmap_sg, we clear out the length in the
5837568cb4eSPaul Mackerras 	 * next entry of the sglist if we didn't fill the list completely
5847568cb4eSPaul Mackerras 	 */
5857568cb4eSPaul Mackerras 	if (outcount < incount) {
58678bdc310SJens Axboe 		outs = sg_next(outs);
5877568cb4eSPaul Mackerras 		outs->dma_length = 0;
5887568cb4eSPaul Mackerras 	}
589a958a264SJake Moilanen 
590a958a264SJake Moilanen 	/* Make sure updates are seen by hardware */
591a958a264SJake Moilanen 	mb();
592a958a264SJake Moilanen 
5937568cb4eSPaul Mackerras 	return outcount;
5947568cb4eSPaul Mackerras 
5957568cb4eSPaul Mackerras  failure:
59678bdc310SJens Axboe 	for_each_sg(sglist, s, nelems, i) {
5977568cb4eSPaul Mackerras 		if (s->dma_length != 0) {
5987568cb4eSPaul Mackerras 			unsigned long vaddr, npages;
5997568cb4eSPaul Mackerras 
600d0847757SAlistair Popple 			vaddr = s->dma_address & IOMMU_PAGE_MASK(tbl);
6012994a3b2SJoerg Roedel 			npages = iommu_num_pages(s->dma_address, s->dma_length,
602d0847757SAlistair Popple 						 IOMMU_PAGE_SIZE(tbl));
603d3622137SAnton Blanchard 			__iommu_free(tbl, vaddr, npages);
604a958a264SJake Moilanen 			s->dma_length = 0;
6057568cb4eSPaul Mackerras 		}
60678bdc310SJens Axboe 		if (s == outs)
60778bdc310SJens Axboe 			break;
6087568cb4eSPaul Mackerras 	}
609c4e0e892SMartin Oliveira 	return -EIO;
6107568cb4eSPaul Mackerras }
6117568cb4eSPaul Mackerras 
6127568cb4eSPaul Mackerras 
ppc_iommu_unmap_sg(struct iommu_table * tbl,struct scatterlist * sglist,int nelems,enum dma_data_direction direction,unsigned long attrs)6130690cbd2SJoerg Roedel void ppc_iommu_unmap_sg(struct iommu_table *tbl, struct scatterlist *sglist,
6143affedc4SMark Nelson 			int nelems, enum dma_data_direction direction,
61500085f1eSKrzysztof Kozlowski 			unsigned long attrs)
6167568cb4eSPaul Mackerras {
61778bdc310SJens Axboe 	struct scatterlist *sg;
6187568cb4eSPaul Mackerras 
6197568cb4eSPaul Mackerras 	BUG_ON(direction == DMA_NONE);
6207568cb4eSPaul Mackerras 
6217568cb4eSPaul Mackerras 	if (!tbl)
6227568cb4eSPaul Mackerras 		return;
6237568cb4eSPaul Mackerras 
62478bdc310SJens Axboe 	sg = sglist;
6257568cb4eSPaul Mackerras 	while (nelems--) {
6267568cb4eSPaul Mackerras 		unsigned int npages;
62778bdc310SJens Axboe 		dma_addr_t dma_handle = sg->dma_address;
6287568cb4eSPaul Mackerras 
62978bdc310SJens Axboe 		if (sg->dma_length == 0)
6307568cb4eSPaul Mackerras 			break;
6312994a3b2SJoerg Roedel 		npages = iommu_num_pages(dma_handle, sg->dma_length,
632d0847757SAlistair Popple 					 IOMMU_PAGE_SIZE(tbl));
633d3622137SAnton Blanchard 		__iommu_free(tbl, dma_handle, npages);
63478bdc310SJens Axboe 		sg = sg_next(sg);
6357568cb4eSPaul Mackerras 	}
6367568cb4eSPaul Mackerras 
6377568cb4eSPaul Mackerras 	/* Flush/invalidate TLBs if necessary. As for iommu_free(), we
6387568cb4eSPaul Mackerras 	 * do not do an mb() here, the affected platforms do not need it
6397568cb4eSPaul Mackerras 	 * when freeing.
6407568cb4eSPaul Mackerras 	 */
641da004c36SAlexey Kardashevskiy 	if (tbl->it_ops->flush)
642da004c36SAlexey Kardashevskiy 		tbl->it_ops->flush(tbl);
6437568cb4eSPaul Mackerras }
6447568cb4eSPaul Mackerras 
iommu_table_clear(struct iommu_table * tbl)64554622f10SMohan Kumar M static void iommu_table_clear(struct iommu_table *tbl)
64654622f10SMohan Kumar M {
6473ccc00a7SMahesh Salgaonkar 	/*
6483ccc00a7SMahesh Salgaonkar 	 * In case of firmware assisted dump system goes through clean
6493ccc00a7SMahesh Salgaonkar 	 * reboot process at the time of system crash. Hence it's safe to
6503ccc00a7SMahesh Salgaonkar 	 * clear the TCE entries if firmware assisted dump is active.
6513ccc00a7SMahesh Salgaonkar 	 */
6523ccc00a7SMahesh Salgaonkar 	if (!is_kdump_kernel() || is_fadump_active()) {
65354622f10SMohan Kumar M 		/* Clear the table in case firmware left allocations in it */
654da004c36SAlexey Kardashevskiy 		tbl->it_ops->clear(tbl, tbl->it_offset, tbl->it_size);
65554622f10SMohan Kumar M 		return;
65654622f10SMohan Kumar M 	}
65754622f10SMohan Kumar M 
65854622f10SMohan Kumar M #ifdef CONFIG_CRASH_DUMP
659da004c36SAlexey Kardashevskiy 	if (tbl->it_ops->get) {
66054622f10SMohan Kumar M 		unsigned long index, tceval, tcecount = 0;
66154622f10SMohan Kumar M 
66254622f10SMohan Kumar M 		/* Reserve the existing mappings left by the first kernel. */
66354622f10SMohan Kumar M 		for (index = 0; index < tbl->it_size; index++) {
664da004c36SAlexey Kardashevskiy 			tceval = tbl->it_ops->get(tbl, index + tbl->it_offset);
66554622f10SMohan Kumar M 			/*
66654622f10SMohan Kumar M 			 * Freed TCE entry contains 0x7fffffffffffffff on JS20
66754622f10SMohan Kumar M 			 */
66854622f10SMohan Kumar M 			if (tceval && (tceval != 0x7fffffffffffffffUL)) {
66954622f10SMohan Kumar M 				__set_bit(index, tbl->it_map);
67054622f10SMohan Kumar M 				tcecount++;
67154622f10SMohan Kumar M 			}
67254622f10SMohan Kumar M 		}
67354622f10SMohan Kumar M 
67454622f10SMohan Kumar M 		if ((tbl->it_size - tcecount) < KDUMP_MIN_TCE_ENTRIES) {
67554622f10SMohan Kumar M 			printk(KERN_WARNING "TCE table is full; freeing ");
67654622f10SMohan Kumar M 			printk(KERN_WARNING "%d entries for the kdump boot\n",
67754622f10SMohan Kumar M 				KDUMP_MIN_TCE_ENTRIES);
67854622f10SMohan Kumar M 			for (index = tbl->it_size - KDUMP_MIN_TCE_ENTRIES;
67954622f10SMohan Kumar M 				index < tbl->it_size; index++)
68054622f10SMohan Kumar M 				__clear_bit(index, tbl->it_map);
68154622f10SMohan Kumar M 		}
68254622f10SMohan Kumar M 	}
68354622f10SMohan Kumar M #endif
68454622f10SMohan Kumar M }
68554622f10SMohan Kumar M 
iommu_table_reserve_pages(struct iommu_table * tbl,unsigned long res_start,unsigned long res_end)686201ed7f3SAlexey Kardashevskiy static void iommu_table_reserve_pages(struct iommu_table *tbl,
687201ed7f3SAlexey Kardashevskiy 		unsigned long res_start, unsigned long res_end)
688201ed7f3SAlexey Kardashevskiy {
689201ed7f3SAlexey Kardashevskiy 	int i;
690201ed7f3SAlexey Kardashevskiy 
691201ed7f3SAlexey Kardashevskiy 	WARN_ON_ONCE(res_end < res_start);
692201ed7f3SAlexey Kardashevskiy 	/*
693201ed7f3SAlexey Kardashevskiy 	 * Reserve page 0 so it will not be used for any mappings.
694201ed7f3SAlexey Kardashevskiy 	 * This avoids buggy drivers that consider page 0 to be invalid
695201ed7f3SAlexey Kardashevskiy 	 * to crash the machine or even lose data.
696201ed7f3SAlexey Kardashevskiy 	 */
697201ed7f3SAlexey Kardashevskiy 	if (tbl->it_offset == 0)
698201ed7f3SAlexey Kardashevskiy 		set_bit(0, tbl->it_map);
699201ed7f3SAlexey Kardashevskiy 
7003c33066aSLeonardo Bras 	if (res_start < tbl->it_offset)
7013c33066aSLeonardo Bras 		res_start = tbl->it_offset;
7023c33066aSLeonardo Bras 
7033c33066aSLeonardo Bras 	if (res_end > (tbl->it_offset + tbl->it_size))
7043c33066aSLeonardo Bras 		res_end = tbl->it_offset + tbl->it_size;
7053c33066aSLeonardo Bras 
7063c33066aSLeonardo Bras 	/* Check if res_start..res_end is a valid range in the table */
7073c33066aSLeonardo Bras 	if (res_start >= res_end) {
7083c33066aSLeonardo Bras 		tbl->it_reserved_start = tbl->it_offset;
7093c33066aSLeonardo Bras 		tbl->it_reserved_end = tbl->it_offset;
7103c33066aSLeonardo Bras 		return;
7113c33066aSLeonardo Bras 	}
7123c33066aSLeonardo Bras 
713201ed7f3SAlexey Kardashevskiy 	tbl->it_reserved_start = res_start;
714201ed7f3SAlexey Kardashevskiy 	tbl->it_reserved_end = res_end;
715201ed7f3SAlexey Kardashevskiy 
716201ed7f3SAlexey Kardashevskiy 	for (i = tbl->it_reserved_start; i < tbl->it_reserved_end; ++i)
717201ed7f3SAlexey Kardashevskiy 		set_bit(i - tbl->it_offset, tbl->it_map);
718201ed7f3SAlexey Kardashevskiy }
719201ed7f3SAlexey Kardashevskiy 
7207568cb4eSPaul Mackerras /*
7217568cb4eSPaul Mackerras  * Build a iommu_table structure.  This contains a bit map which
7227568cb4eSPaul Mackerras  * is used to manage allocation of the tce space.
7237568cb4eSPaul Mackerras  */
iommu_init_table(struct iommu_table * tbl,int nid,unsigned long res_start,unsigned long res_end)724201ed7f3SAlexey Kardashevskiy struct iommu_table *iommu_init_table(struct iommu_table *tbl, int nid,
725201ed7f3SAlexey Kardashevskiy 		unsigned long res_start, unsigned long res_end)
7267568cb4eSPaul Mackerras {
7277568cb4eSPaul Mackerras 	unsigned long sz;
7287568cb4eSPaul Mackerras 	static int welcomed = 0;
729b4c3a872SAnton Blanchard 	unsigned int i;
730b4c3a872SAnton Blanchard 	struct iommu_pool *p;
7317568cb4eSPaul Mackerras 
732da004c36SAlexey Kardashevskiy 	BUG_ON(!tbl->it_ops);
733da004c36SAlexey Kardashevskiy 
7347568cb4eSPaul Mackerras 	/* number of bytes needed for the bitmap */
735c5a0809aSAkinobu Mita 	sz = BITS_TO_LONGS(tbl->it_size) * sizeof(unsigned long);
7367568cb4eSPaul Mackerras 
7377f1fa82dSAlexey Kardashevskiy 	tbl->it_map = vzalloc_node(sz, nid);
7384be518d8SAlexey Kardashevskiy 	if (!tbl->it_map) {
7394be518d8SAlexey Kardashevskiy 		pr_err("%s: Can't allocate %ld bytes\n", __func__, sz);
7404be518d8SAlexey Kardashevskiy 		return NULL;
7414be518d8SAlexey Kardashevskiy 	}
7427568cb4eSPaul Mackerras 
743201ed7f3SAlexey Kardashevskiy 	iommu_table_reserve_pages(tbl, res_start, res_end);
744d12b524fSThadeu Lima de Souza Cascardo 
745b4c3a872SAnton Blanchard 	/* We only split the IOMMU table if we have 1GB or more of space */
746d0847757SAlistair Popple 	if ((tbl->it_size << tbl->it_page_shift) >= (1UL * 1024 * 1024 * 1024))
747b4c3a872SAnton Blanchard 		tbl->nr_pools = IOMMU_NR_POOLS;
748b4c3a872SAnton Blanchard 	else
749b4c3a872SAnton Blanchard 		tbl->nr_pools = 1;
750b4c3a872SAnton Blanchard 
751b4c3a872SAnton Blanchard 	/* We reserve the top 1/4 of the table for large allocations */
752dcd261baSBenjamin Herrenschmidt 	tbl->poolsize = (tbl->it_size * 3 / 4) / tbl->nr_pools;
753b4c3a872SAnton Blanchard 
754dcd261baSBenjamin Herrenschmidt 	for (i = 0; i < tbl->nr_pools; i++) {
755b4c3a872SAnton Blanchard 		p = &tbl->pools[i];
756b4c3a872SAnton Blanchard 		spin_lock_init(&(p->lock));
757b4c3a872SAnton Blanchard 		p->start = tbl->poolsize * i;
758b4c3a872SAnton Blanchard 		p->hint = p->start;
759b4c3a872SAnton Blanchard 		p->end = p->start + tbl->poolsize;
760b4c3a872SAnton Blanchard 	}
761b4c3a872SAnton Blanchard 
762b4c3a872SAnton Blanchard 	p = &tbl->large_pool;
763b4c3a872SAnton Blanchard 	spin_lock_init(&(p->lock));
764b4c3a872SAnton Blanchard 	p->start = tbl->poolsize * i;
765b4c3a872SAnton Blanchard 	p->hint = p->start;
766b4c3a872SAnton Blanchard 	p->end = tbl->it_size;
7677568cb4eSPaul Mackerras 
76854622f10SMohan Kumar M 	iommu_table_clear(tbl);
7697568cb4eSPaul Mackerras 
7707568cb4eSPaul Mackerras 	if (!welcomed) {
7717568cb4eSPaul Mackerras 		printk(KERN_INFO "IOMMU table initialized, virtual merging %s\n",
7727568cb4eSPaul Mackerras 		       novmerge ? "disabled" : "enabled");
7737568cb4eSPaul Mackerras 		welcomed = 1;
7747568cb4eSPaul Mackerras 	}
7757568cb4eSPaul Mackerras 
776691602aaSAlexey Kardashevskiy 	iommu_debugfs_add(tbl);
777691602aaSAlexey Kardashevskiy 
7787568cb4eSPaul Mackerras 	return tbl;
7797568cb4eSPaul Mackerras }
7807568cb4eSPaul Mackerras 
iommu_table_in_use(struct iommu_table * tbl)7813c33066aSLeonardo Bras bool iommu_table_in_use(struct iommu_table *tbl)
7823c33066aSLeonardo Bras {
7833c33066aSLeonardo Bras 	unsigned long start = 0, end;
7843c33066aSLeonardo Bras 
7853c33066aSLeonardo Bras 	/* ignore reserved bit0 */
7863c33066aSLeonardo Bras 	if (tbl->it_offset == 0)
7873c33066aSLeonardo Bras 		start = 1;
788d80f6de9SAlexey Kardashevskiy 
789d80f6de9SAlexey Kardashevskiy 	/* Simple case with no reserved MMIO32 region */
790d80f6de9SAlexey Kardashevskiy 	if (!tbl->it_reserved_start && !tbl->it_reserved_end)
791d80f6de9SAlexey Kardashevskiy 		return find_next_bit(tbl->it_map, tbl->it_size, start) != tbl->it_size;
792d80f6de9SAlexey Kardashevskiy 
7933c33066aSLeonardo Bras 	end = tbl->it_reserved_start - tbl->it_offset;
7943c33066aSLeonardo Bras 	if (find_next_bit(tbl->it_map, end, start) != end)
7953c33066aSLeonardo Bras 		return true;
7963c33066aSLeonardo Bras 
7973c33066aSLeonardo Bras 	start = tbl->it_reserved_end - tbl->it_offset;
7983c33066aSLeonardo Bras 	end = tbl->it_size;
7993c33066aSLeonardo Bras 	return find_next_bit(tbl->it_map, end, start) != end;
8003c33066aSLeonardo Bras }
8013c33066aSLeonardo Bras 
iommu_table_free(struct kref * kref)802e5afdf9dSAlexey Kardashevskiy static void iommu_table_free(struct kref *kref)
8037568cb4eSPaul Mackerras {
804e5afdf9dSAlexey Kardashevskiy 	struct iommu_table *tbl;
8057568cb4eSPaul Mackerras 
806e5afdf9dSAlexey Kardashevskiy 	tbl = container_of(kref, struct iommu_table, it_kref);
8078aca92d8SAlexey Kardashevskiy 
80811edf116SAlexey Kardashevskiy 	if (tbl->it_ops->free)
80911edf116SAlexey Kardashevskiy 		tbl->it_ops->free(tbl);
81011edf116SAlexey Kardashevskiy 
8118aca92d8SAlexey Kardashevskiy 	if (!tbl->it_map) {
8128aca92d8SAlexey Kardashevskiy 		kfree(tbl);
8137568cb4eSPaul Mackerras 		return;
8147568cb4eSPaul Mackerras 	}
8157568cb4eSPaul Mackerras 
816691602aaSAlexey Kardashevskiy 	iommu_debugfs_del(tbl);
817691602aaSAlexey Kardashevskiy 
8187568cb4eSPaul Mackerras 	/* verify that table contains no entries */
8193c33066aSLeonardo Bras 	if (iommu_table_in_use(tbl))
820e5afdf9dSAlexey Kardashevskiy 		pr_warn("%s: Unexpected TCEs\n", __func__);
8217568cb4eSPaul Mackerras 
8227568cb4eSPaul Mackerras 	/* free bitmap */
8237f1fa82dSAlexey Kardashevskiy 	vfree(tbl->it_map);
8247568cb4eSPaul Mackerras 
8257568cb4eSPaul Mackerras 	/* free table */
8267568cb4eSPaul Mackerras 	kfree(tbl);
8277568cb4eSPaul Mackerras }
828e5afdf9dSAlexey Kardashevskiy 
iommu_tce_table_get(struct iommu_table * tbl)829e5afdf9dSAlexey Kardashevskiy struct iommu_table *iommu_tce_table_get(struct iommu_table *tbl)
830e5afdf9dSAlexey Kardashevskiy {
831e5afdf9dSAlexey Kardashevskiy 	if (kref_get_unless_zero(&tbl->it_kref))
832e5afdf9dSAlexey Kardashevskiy 		return tbl;
833e5afdf9dSAlexey Kardashevskiy 
834e5afdf9dSAlexey Kardashevskiy 	return NULL;
835e5afdf9dSAlexey Kardashevskiy }
836e5afdf9dSAlexey Kardashevskiy EXPORT_SYMBOL_GPL(iommu_tce_table_get);
837e5afdf9dSAlexey Kardashevskiy 
iommu_tce_table_put(struct iommu_table * tbl)838e5afdf9dSAlexey Kardashevskiy int iommu_tce_table_put(struct iommu_table *tbl)
839e5afdf9dSAlexey Kardashevskiy {
840e5afdf9dSAlexey Kardashevskiy 	if (WARN_ON(!tbl))
841e5afdf9dSAlexey Kardashevskiy 		return 0;
842e5afdf9dSAlexey Kardashevskiy 
843e5afdf9dSAlexey Kardashevskiy 	return kref_put(&tbl->it_kref, iommu_table_free);
844e5afdf9dSAlexey Kardashevskiy }
845e5afdf9dSAlexey Kardashevskiy EXPORT_SYMBOL_GPL(iommu_tce_table_put);
8467568cb4eSPaul Mackerras 
8477568cb4eSPaul Mackerras /* Creates TCEs for a user provided buffer.  The user buffer must be
848f9226d57SMark Nelson  * contiguous real kernel storage (not vmalloc).  The address passed here
849f9226d57SMark Nelson  * comprises a page address and offset into that page. The dma_addr_t
850f9226d57SMark Nelson  * returned will point to the same byte within the page as was passed in.
8517568cb4eSPaul Mackerras  */
iommu_map_page(struct device * dev,struct iommu_table * tbl,struct page * page,unsigned long offset,size_t size,unsigned long mask,enum dma_data_direction direction,unsigned long attrs)852f9226d57SMark Nelson dma_addr_t iommu_map_page(struct device *dev, struct iommu_table *tbl,
853f9226d57SMark Nelson 			  struct page *page, unsigned long offset, size_t size,
854f9226d57SMark Nelson 			  unsigned long mask, enum dma_data_direction direction,
85500085f1eSKrzysztof Kozlowski 			  unsigned long attrs)
8567568cb4eSPaul Mackerras {
857d11e3d3dSChristoph Hellwig 	dma_addr_t dma_handle = DMA_MAPPING_ERROR;
858f9226d57SMark Nelson 	void *vaddr;
8597568cb4eSPaul Mackerras 	unsigned long uaddr;
860d262c32aSBenjamin Herrenschmidt 	unsigned int npages, align;
8617568cb4eSPaul Mackerras 
8627568cb4eSPaul Mackerras 	BUG_ON(direction == DMA_NONE);
8637568cb4eSPaul Mackerras 
864f9226d57SMark Nelson 	vaddr = page_address(page) + offset;
8657568cb4eSPaul Mackerras 	uaddr = (unsigned long)vaddr;
8667568cb4eSPaul Mackerras 
8677568cb4eSPaul Mackerras 	if (tbl) {
868984ecdd6SBreno Leitao 		npages = iommu_num_pages(uaddr, size, IOMMU_PAGE_SIZE(tbl));
869d262c32aSBenjamin Herrenschmidt 		align = 0;
870d0847757SAlistair Popple 		if (tbl->it_page_shift < PAGE_SHIFT && size >= PAGE_SIZE &&
871d262c32aSBenjamin Herrenschmidt 		    ((unsigned long)vaddr & ~PAGE_MASK) == 0)
872d0847757SAlistair Popple 			align = PAGE_SHIFT - tbl->it_page_shift;
873d262c32aSBenjamin Herrenschmidt 
874fb3475e9SFUJITA Tomonori 		dma_handle = iommu_alloc(dev, tbl, vaddr, npages, direction,
875d0847757SAlistair Popple 					 mask >> tbl->it_page_shift, align,
8764f3dd8a0SMark Nelson 					 attrs);
877d11e3d3dSChristoph Hellwig 		if (dma_handle == DMA_MAPPING_ERROR) {
878af8a2498SMauricio Faria de Oliveira 			if (!(attrs & DMA_ATTR_NO_WARN) &&
879af8a2498SMauricio Faria de Oliveira 			    printk_ratelimit())  {
8804dfa9c47SAnton Blanchard 				dev_info(dev, "iommu_alloc failed, tbl %p "
8814dfa9c47SAnton Blanchard 					 "vaddr %p npages %d\n", tbl, vaddr,
8824dfa9c47SAnton Blanchard 					 npages);
8837568cb4eSPaul Mackerras 			}
8847568cb4eSPaul Mackerras 		} else
885d0847757SAlistair Popple 			dma_handle |= (uaddr & ~IOMMU_PAGE_MASK(tbl));
8867568cb4eSPaul Mackerras 	}
8877568cb4eSPaul Mackerras 
8887568cb4eSPaul Mackerras 	return dma_handle;
8897568cb4eSPaul Mackerras }
8907568cb4eSPaul Mackerras 
iommu_unmap_page(struct iommu_table * tbl,dma_addr_t dma_handle,size_t size,enum dma_data_direction direction,unsigned long attrs)891f9226d57SMark Nelson void iommu_unmap_page(struct iommu_table *tbl, dma_addr_t dma_handle,
8923affedc4SMark Nelson 		      size_t size, enum dma_data_direction direction,
89300085f1eSKrzysztof Kozlowski 		      unsigned long attrs)
8947568cb4eSPaul Mackerras {
8955d2efba6SLinas Vepstas 	unsigned int npages;
8965d2efba6SLinas Vepstas 
8977568cb4eSPaul Mackerras 	BUG_ON(direction == DMA_NONE);
8987568cb4eSPaul Mackerras 
8995d2efba6SLinas Vepstas 	if (tbl) {
900d0847757SAlistair Popple 		npages = iommu_num_pages(dma_handle, size,
901d0847757SAlistair Popple 					 IOMMU_PAGE_SIZE(tbl));
9025d2efba6SLinas Vepstas 		iommu_free(tbl, dma_handle, npages);
9035d2efba6SLinas Vepstas 	}
9047568cb4eSPaul Mackerras }
9057568cb4eSPaul Mackerras 
9067568cb4eSPaul Mackerras /* Allocates a contiguous real buffer and creates mappings over it.
9077568cb4eSPaul Mackerras  * Returns the virtual address of the buffer and sets dma_handle
9087568cb4eSPaul Mackerras  * to the dma address (mapping) of the first page.
9097568cb4eSPaul Mackerras  */
iommu_alloc_coherent(struct device * dev,struct iommu_table * tbl,size_t size,dma_addr_t * dma_handle,unsigned long mask,gfp_t flag,int node)910fb3475e9SFUJITA Tomonori void *iommu_alloc_coherent(struct device *dev, struct iommu_table *tbl,
911fb3475e9SFUJITA Tomonori 			   size_t size,	dma_addr_t *dma_handle,
912fb3475e9SFUJITA Tomonori 			   unsigned long mask, gfp_t flag, int node)
9137568cb4eSPaul Mackerras {
9147568cb4eSPaul Mackerras 	void *ret = NULL;
9157568cb4eSPaul Mackerras 	dma_addr_t mapping;
9165d2efba6SLinas Vepstas 	unsigned int order;
9175d2efba6SLinas Vepstas 	unsigned int nio_pages, io_order;
9188eb6c6e3SChristoph Hellwig 	struct page *page;
919096339abSGaurav Batra 	int tcesize = (1 << tbl->it_page_shift);
9207568cb4eSPaul Mackerras 
9217568cb4eSPaul Mackerras 	size = PAGE_ALIGN(size);
9227568cb4eSPaul Mackerras 	order = get_order(size);
9237568cb4eSPaul Mackerras 
9247568cb4eSPaul Mackerras  	/*
9257568cb4eSPaul Mackerras 	 * Client asked for way too much space.  This is checked later
9267568cb4eSPaul Mackerras 	 * anyway.  It is easier to debug here for the drivers than in
9277568cb4eSPaul Mackerras 	 * the tce tables.
9287568cb4eSPaul Mackerras 	 */
9297568cb4eSPaul Mackerras 	if (order >= IOMAP_MAX_ORDER) {
9304dfa9c47SAnton Blanchard 		dev_info(dev, "iommu_alloc_consistent size too large: 0x%lx\n",
9314dfa9c47SAnton Blanchard 			 size);
9327568cb4eSPaul Mackerras 		return NULL;
9337568cb4eSPaul Mackerras 	}
9347568cb4eSPaul Mackerras 
9357568cb4eSPaul Mackerras 	if (!tbl)
9367568cb4eSPaul Mackerras 		return NULL;
9377568cb4eSPaul Mackerras 
9387568cb4eSPaul Mackerras 	/* Alloc enough pages (and possibly more) */
93905061354SPaul Mackerras 	page = alloc_pages_node(node, flag, order);
9408eb6c6e3SChristoph Hellwig 	if (!page)
9417568cb4eSPaul Mackerras 		return NULL;
9428eb6c6e3SChristoph Hellwig 	ret = page_address(page);
9437568cb4eSPaul Mackerras 	memset(ret, 0, size);
9447568cb4eSPaul Mackerras 
9457568cb4eSPaul Mackerras 	/* Set up tces to cover the allocated range */
946096339abSGaurav Batra 	nio_pages = IOMMU_PAGE_ALIGN(size, tbl) >> tbl->it_page_shift;
947096339abSGaurav Batra 
94859cc84c8SFrederic Barrat 	io_order = get_iommu_order(size, tbl);
949fb3475e9SFUJITA Tomonori 	mapping = iommu_alloc(dev, tbl, ret, nio_pages, DMA_BIDIRECTIONAL,
95000085f1eSKrzysztof Kozlowski 			      mask >> tbl->it_page_shift, io_order, 0);
951d11e3d3dSChristoph Hellwig 	if (mapping == DMA_MAPPING_ERROR) {
9527568cb4eSPaul Mackerras 		free_pages((unsigned long)ret, order);
9538eb6c6e3SChristoph Hellwig 		return NULL;
9548eb6c6e3SChristoph Hellwig 	}
955096339abSGaurav Batra 
956096339abSGaurav Batra 	*dma_handle = mapping | ((u64)ret & (tcesize - 1));
9577568cb4eSPaul Mackerras 	return ret;
9587568cb4eSPaul Mackerras }
9597568cb4eSPaul Mackerras 
iommu_free_coherent(struct iommu_table * tbl,size_t size,void * vaddr,dma_addr_t dma_handle)9607568cb4eSPaul Mackerras void iommu_free_coherent(struct iommu_table *tbl, size_t size,
9617568cb4eSPaul Mackerras 			 void *vaddr, dma_addr_t dma_handle)
9627568cb4eSPaul Mackerras {
9637568cb4eSPaul Mackerras 	if (tbl) {
96459cc84c8SFrederic Barrat 		unsigned int nio_pages;
9655d2efba6SLinas Vepstas 
96659cc84c8SFrederic Barrat 		size = PAGE_ALIGN(size);
967096339abSGaurav Batra 		nio_pages = IOMMU_PAGE_ALIGN(size, tbl) >> tbl->it_page_shift;
9685d2efba6SLinas Vepstas 		iommu_free(tbl, dma_handle, nio_pages);
9695d2efba6SLinas Vepstas 		size = PAGE_ALIGN(size);
9707568cb4eSPaul Mackerras 		free_pages((unsigned long)vaddr, get_order(size));
9717568cb4eSPaul Mackerras 	}
9727568cb4eSPaul Mackerras }
9734e13c1acSAlexey Kardashevskiy 
iommu_direction_to_tce_perm(enum dma_data_direction dir)97410b35b2bSAlexey Kardashevskiy unsigned long iommu_direction_to_tce_perm(enum dma_data_direction dir)
97510b35b2bSAlexey Kardashevskiy {
97610b35b2bSAlexey Kardashevskiy 	switch (dir) {
97710b35b2bSAlexey Kardashevskiy 	case DMA_BIDIRECTIONAL:
97810b35b2bSAlexey Kardashevskiy 		return TCE_PCI_READ | TCE_PCI_WRITE;
97910b35b2bSAlexey Kardashevskiy 	case DMA_FROM_DEVICE:
98010b35b2bSAlexey Kardashevskiy 		return TCE_PCI_WRITE;
98110b35b2bSAlexey Kardashevskiy 	case DMA_TO_DEVICE:
98210b35b2bSAlexey Kardashevskiy 		return TCE_PCI_READ;
98310b35b2bSAlexey Kardashevskiy 	default:
98410b35b2bSAlexey Kardashevskiy 		return 0;
98510b35b2bSAlexey Kardashevskiy 	}
98610b35b2bSAlexey Kardashevskiy }
98710b35b2bSAlexey Kardashevskiy EXPORT_SYMBOL_GPL(iommu_direction_to_tce_perm);
98810b35b2bSAlexey Kardashevskiy 
9894e13c1acSAlexey Kardashevskiy #ifdef CONFIG_IOMMU_API
9904e13c1acSAlexey Kardashevskiy /*
9914e13c1acSAlexey Kardashevskiy  * SPAPR TCE API
9924e13c1acSAlexey Kardashevskiy  */
group_release(void * iommu_data)9934e13c1acSAlexey Kardashevskiy static void group_release(void *iommu_data)
9944e13c1acSAlexey Kardashevskiy {
995b348aa65SAlexey Kardashevskiy 	struct iommu_table_group *table_group = iommu_data;
996b348aa65SAlexey Kardashevskiy 
997b348aa65SAlexey Kardashevskiy 	table_group->group = NULL;
9984e13c1acSAlexey Kardashevskiy }
9994e13c1acSAlexey Kardashevskiy 
iommu_register_group(struct iommu_table_group * table_group,int pci_domain_number,unsigned long pe_num)1000b348aa65SAlexey Kardashevskiy void iommu_register_group(struct iommu_table_group *table_group,
10014e13c1acSAlexey Kardashevskiy 		int pci_domain_number, unsigned long pe_num)
10024e13c1acSAlexey Kardashevskiy {
10034e13c1acSAlexey Kardashevskiy 	struct iommu_group *grp;
10044e13c1acSAlexey Kardashevskiy 	char *name;
10054e13c1acSAlexey Kardashevskiy 
10064e13c1acSAlexey Kardashevskiy 	grp = iommu_group_alloc();
10074e13c1acSAlexey Kardashevskiy 	if (IS_ERR(grp)) {
10084e13c1acSAlexey Kardashevskiy 		pr_warn("powerpc iommu api: cannot create new group, err=%ld\n",
10094e13c1acSAlexey Kardashevskiy 				PTR_ERR(grp));
10104e13c1acSAlexey Kardashevskiy 		return;
10114e13c1acSAlexey Kardashevskiy 	}
1012b348aa65SAlexey Kardashevskiy 	table_group->group = grp;
1013b348aa65SAlexey Kardashevskiy 	iommu_group_set_iommudata(grp, table_group, group_release);
10144e13c1acSAlexey Kardashevskiy 	name = kasprintf(GFP_KERNEL, "domain%d-pe%lx",
10154e13c1acSAlexey Kardashevskiy 			pci_domain_number, pe_num);
10164e13c1acSAlexey Kardashevskiy 	if (!name)
10174e13c1acSAlexey Kardashevskiy 		return;
10184e13c1acSAlexey Kardashevskiy 	iommu_group_set_name(grp, name);
10194e13c1acSAlexey Kardashevskiy 	kfree(name);
10204e13c1acSAlexey Kardashevskiy }
10214e13c1acSAlexey Kardashevskiy 
iommu_tce_direction(unsigned long tce)10224e13c1acSAlexey Kardashevskiy enum dma_data_direction iommu_tce_direction(unsigned long tce)
10234e13c1acSAlexey Kardashevskiy {
10244e13c1acSAlexey Kardashevskiy 	if ((tce & TCE_PCI_READ) && (tce & TCE_PCI_WRITE))
10254e13c1acSAlexey Kardashevskiy 		return DMA_BIDIRECTIONAL;
10264e13c1acSAlexey Kardashevskiy 	else if (tce & TCE_PCI_READ)
10274e13c1acSAlexey Kardashevskiy 		return DMA_TO_DEVICE;
10284e13c1acSAlexey Kardashevskiy 	else if (tce & TCE_PCI_WRITE)
10294e13c1acSAlexey Kardashevskiy 		return DMA_FROM_DEVICE;
10304e13c1acSAlexey Kardashevskiy 	else
10314e13c1acSAlexey Kardashevskiy 		return DMA_NONE;
10324e13c1acSAlexey Kardashevskiy }
10334e13c1acSAlexey Kardashevskiy EXPORT_SYMBOL_GPL(iommu_tce_direction);
10344e13c1acSAlexey Kardashevskiy 
iommu_flush_tce(struct iommu_table * tbl)10354e13c1acSAlexey Kardashevskiy void iommu_flush_tce(struct iommu_table *tbl)
10364e13c1acSAlexey Kardashevskiy {
10374e13c1acSAlexey Kardashevskiy 	/* Flush/invalidate TLB caches if necessary */
1038da004c36SAlexey Kardashevskiy 	if (tbl->it_ops->flush)
1039da004c36SAlexey Kardashevskiy 		tbl->it_ops->flush(tbl);
10404e13c1acSAlexey Kardashevskiy 
10414e13c1acSAlexey Kardashevskiy 	/* Make sure updates are seen by hardware */
10424e13c1acSAlexey Kardashevskiy 	mb();
10434e13c1acSAlexey Kardashevskiy }
10444e13c1acSAlexey Kardashevskiy EXPORT_SYMBOL_GPL(iommu_flush_tce);
10454e13c1acSAlexey Kardashevskiy 
iommu_tce_check_ioba(unsigned long page_shift,unsigned long offset,unsigned long size,unsigned long ioba,unsigned long npages)1046b1af23d8SAlexey Kardashevskiy int iommu_tce_check_ioba(unsigned long page_shift,
1047b1af23d8SAlexey Kardashevskiy 		unsigned long offset, unsigned long size,
1048b1af23d8SAlexey Kardashevskiy 		unsigned long ioba, unsigned long npages)
10494e13c1acSAlexey Kardashevskiy {
1050b1af23d8SAlexey Kardashevskiy 	unsigned long mask = (1UL << page_shift) - 1;
1051b1af23d8SAlexey Kardashevskiy 
1052b1af23d8SAlexey Kardashevskiy 	if (ioba & mask)
10534e13c1acSAlexey Kardashevskiy 		return -EINVAL;
10544e13c1acSAlexey Kardashevskiy 
1055b1af23d8SAlexey Kardashevskiy 	ioba >>= page_shift;
1056b1af23d8SAlexey Kardashevskiy 	if (ioba < offset)
10574e13c1acSAlexey Kardashevskiy 		return -EINVAL;
10584e13c1acSAlexey Kardashevskiy 
1059b1af23d8SAlexey Kardashevskiy 	if ((ioba + 1) > (offset + size))
10604e13c1acSAlexey Kardashevskiy 		return -EINVAL;
10614e13c1acSAlexey Kardashevskiy 
10624e13c1acSAlexey Kardashevskiy 	return 0;
10634e13c1acSAlexey Kardashevskiy }
1064b1af23d8SAlexey Kardashevskiy EXPORT_SYMBOL_GPL(iommu_tce_check_ioba);
10654e13c1acSAlexey Kardashevskiy 
iommu_tce_check_gpa(unsigned long page_shift,unsigned long gpa)1066b1af23d8SAlexey Kardashevskiy int iommu_tce_check_gpa(unsigned long page_shift, unsigned long gpa)
10674e13c1acSAlexey Kardashevskiy {
1068b1af23d8SAlexey Kardashevskiy 	unsigned long mask = (1UL << page_shift) - 1;
10694e13c1acSAlexey Kardashevskiy 
1070b1af23d8SAlexey Kardashevskiy 	if (gpa & mask)
10714e13c1acSAlexey Kardashevskiy 		return -EINVAL;
10724e13c1acSAlexey Kardashevskiy 
10734e13c1acSAlexey Kardashevskiy 	return 0;
10744e13c1acSAlexey Kardashevskiy }
1075b1af23d8SAlexey Kardashevskiy EXPORT_SYMBOL_GPL(iommu_tce_check_gpa);
10764e13c1acSAlexey Kardashevskiy 
iommu_tce_xchg_no_kill(struct mm_struct * mm,struct iommu_table * tbl,unsigned long entry,unsigned long * hpa,enum dma_data_direction * direction)107735872480SAlexey Kardashevskiy extern long iommu_tce_xchg_no_kill(struct mm_struct *mm,
107835872480SAlexey Kardashevskiy 		struct iommu_table *tbl,
1079c10c21efSAlexey Kardashevskiy 		unsigned long entry, unsigned long *hpa,
1080c10c21efSAlexey Kardashevskiy 		enum dma_data_direction *direction)
10814e13c1acSAlexey Kardashevskiy {
108205c6cfb9SAlexey Kardashevskiy 	long ret;
1083c10c21efSAlexey Kardashevskiy 	unsigned long size = 0;
10844e13c1acSAlexey Kardashevskiy 
1085cad32d9dSAlexey Kardashevskiy 	ret = tbl->it_ops->xchg_no_kill(tbl, entry, hpa, direction);
108605c6cfb9SAlexey Kardashevskiy 	if (!ret && ((*direction == DMA_FROM_DEVICE) ||
1087c10c21efSAlexey Kardashevskiy 			(*direction == DMA_BIDIRECTIONAL)) &&
1088c10c21efSAlexey Kardashevskiy 			!mm_iommu_is_devmem(mm, *hpa, tbl->it_page_shift,
1089c10c21efSAlexey Kardashevskiy 					&size))
109005c6cfb9SAlexey Kardashevskiy 		SetPageDirty(pfn_to_page(*hpa >> PAGE_SHIFT));
10914e13c1acSAlexey Kardashevskiy 
10924e13c1acSAlexey Kardashevskiy 	return ret;
10934e13c1acSAlexey Kardashevskiy }
109435872480SAlexey Kardashevskiy EXPORT_SYMBOL_GPL(iommu_tce_xchg_no_kill);
109535872480SAlexey Kardashevskiy 
iommu_tce_kill(struct iommu_table * tbl,unsigned long entry,unsigned long pages)109635872480SAlexey Kardashevskiy void iommu_tce_kill(struct iommu_table *tbl,
109735872480SAlexey Kardashevskiy 		unsigned long entry, unsigned long pages)
109835872480SAlexey Kardashevskiy {
109935872480SAlexey Kardashevskiy 	if (tbl->it_ops->tce_kill)
1100cad32d9dSAlexey Kardashevskiy 		tbl->it_ops->tce_kill(tbl, entry, pages);
110135872480SAlexey Kardashevskiy }
110235872480SAlexey Kardashevskiy EXPORT_SYMBOL_GPL(iommu_tce_kill);
11034e13c1acSAlexey Kardashevskiy 
1104bfd8d989STimothy Pearson #if defined(CONFIG_PPC_PSERIES) || defined(CONFIG_PPC_POWERNV)
iommu_take_ownership(struct iommu_table * tbl)11059d67c943SAlexey Kardashevskiy static int iommu_take_ownership(struct iommu_table *tbl)
11064e13c1acSAlexey Kardashevskiy {
1107b82c75bfSAlexey Kardashevskiy 	unsigned long flags, i, sz = (tbl->it_size + 7) >> 3;
1108b82c75bfSAlexey Kardashevskiy 	int ret = 0;
1109b82c75bfSAlexey Kardashevskiy 
111005c6cfb9SAlexey Kardashevskiy 	/*
111105c6cfb9SAlexey Kardashevskiy 	 * VFIO does not control TCE entries allocation and the guest
111205c6cfb9SAlexey Kardashevskiy 	 * can write new TCEs on top of existing ones so iommu_tce_build()
111305c6cfb9SAlexey Kardashevskiy 	 * must be able to release old pages. This functionality
111405c6cfb9SAlexey Kardashevskiy 	 * requires exchange() callback defined so if it is not
111505c6cfb9SAlexey Kardashevskiy 	 * implemented, we disallow taking ownership over the table.
111605c6cfb9SAlexey Kardashevskiy 	 */
1117a102f139SAlexey Kardashevskiy 	if (!tbl->it_ops->xchg_no_kill)
111805c6cfb9SAlexey Kardashevskiy 		return -EINVAL;
111905c6cfb9SAlexey Kardashevskiy 
1120b82c75bfSAlexey Kardashevskiy 	spin_lock_irqsave(&tbl->large_pool.lock, flags);
1121b82c75bfSAlexey Kardashevskiy 	for (i = 0; i < tbl->nr_pools; i++)
1122cc7130bfSAlexey Kardashevskiy 		spin_lock_nest_lock(&tbl->pools[i].lock, &tbl->large_pool.lock);
11234e13c1acSAlexey Kardashevskiy 
11243c33066aSLeonardo Bras 	if (iommu_table_in_use(tbl)) {
11254e13c1acSAlexey Kardashevskiy 		pr_err("iommu_tce: it_map is not empty");
1126b82c75bfSAlexey Kardashevskiy 		ret = -EBUSY;
1127b82c75bfSAlexey Kardashevskiy 	} else {
1128b82c75bfSAlexey Kardashevskiy 		memset(tbl->it_map, 0xff, sz);
11294e13c1acSAlexey Kardashevskiy 	}
11304e13c1acSAlexey Kardashevskiy 
1131b82c75bfSAlexey Kardashevskiy 	for (i = 0; i < tbl->nr_pools; i++)
1132b82c75bfSAlexey Kardashevskiy 		spin_unlock(&tbl->pools[i].lock);
1133b82c75bfSAlexey Kardashevskiy 	spin_unlock_irqrestore(&tbl->large_pool.lock, flags);
11344e13c1acSAlexey Kardashevskiy 
1135b82c75bfSAlexey Kardashevskiy 	return ret;
11364e13c1acSAlexey Kardashevskiy }
11374e13c1acSAlexey Kardashevskiy 
iommu_release_ownership(struct iommu_table * tbl)11389d67c943SAlexey Kardashevskiy static void iommu_release_ownership(struct iommu_table *tbl)
11394e13c1acSAlexey Kardashevskiy {
1140b82c75bfSAlexey Kardashevskiy 	unsigned long flags, i, sz = (tbl->it_size + 7) >> 3;
1141b82c75bfSAlexey Kardashevskiy 
1142b82c75bfSAlexey Kardashevskiy 	spin_lock_irqsave(&tbl->large_pool.lock, flags);
1143b82c75bfSAlexey Kardashevskiy 	for (i = 0; i < tbl->nr_pools; i++)
1144cc7130bfSAlexey Kardashevskiy 		spin_lock_nest_lock(&tbl->pools[i].lock, &tbl->large_pool.lock);
11454e13c1acSAlexey Kardashevskiy 
11464e13c1acSAlexey Kardashevskiy 	memset(tbl->it_map, 0, sz);
11474e13c1acSAlexey Kardashevskiy 
1148201ed7f3SAlexey Kardashevskiy 	iommu_table_reserve_pages(tbl, tbl->it_reserved_start,
1149201ed7f3SAlexey Kardashevskiy 			tbl->it_reserved_end);
1150b82c75bfSAlexey Kardashevskiy 
1151b82c75bfSAlexey Kardashevskiy 	for (i = 0; i < tbl->nr_pools; i++)
1152b82c75bfSAlexey Kardashevskiy 		spin_unlock(&tbl->pools[i].lock);
1153b82c75bfSAlexey Kardashevskiy 	spin_unlock_irqrestore(&tbl->large_pool.lock, flags);
11544e13c1acSAlexey Kardashevskiy }
1155bfd8d989STimothy Pearson #endif
11564e13c1acSAlexey Kardashevskiy 
iommu_add_device(struct iommu_table_group * table_group,struct device * dev)1157c4e9d3c1SAlexey Kardashevskiy int iommu_add_device(struct iommu_table_group *table_group, struct device *dev)
11584e13c1acSAlexey Kardashevskiy {
1159763fe0adSGavin Shan 	/*
1160763fe0adSGavin Shan 	 * The sysfs entries should be populated before
1161763fe0adSGavin Shan 	 * binding IOMMU group. If sysfs entries isn't
1162763fe0adSGavin Shan 	 * ready, we simply bail.
1163763fe0adSGavin Shan 	 */
1164763fe0adSGavin Shan 	if (!device_is_registered(dev))
1165763fe0adSGavin Shan 		return -ENOENT;
1166763fe0adSGavin Shan 
1167bf8763d8SJoerg Roedel 	if (device_iommu_mapped(dev)) {
1168763fe0adSGavin Shan 		pr_debug("%s: Skipping device %s with iommu group %d\n",
1169763fe0adSGavin Shan 			 __func__, dev_name(dev),
11704e13c1acSAlexey Kardashevskiy 			 iommu_group_id(dev->iommu_group));
11714e13c1acSAlexey Kardashevskiy 		return -EBUSY;
11724e13c1acSAlexey Kardashevskiy 	}
11734e13c1acSAlexey Kardashevskiy 
1174763fe0adSGavin Shan 	pr_debug("%s: Adding %s to iommu group %d\n",
1175c4e9d3c1SAlexey Kardashevskiy 		 __func__, dev_name(dev),  iommu_group_id(table_group->group));
1176a9409044SAlexey Kardashevskiy 	/*
1177a9409044SAlexey Kardashevskiy 	 * This is still not adding devices via the IOMMU bus notifier because
1178a9409044SAlexey Kardashevskiy 	 * of pcibios_init() from arch/powerpc/kernel/pci_64.c which calls
1179a9409044SAlexey Kardashevskiy 	 * pcibios_scan_phb() first (and this guy adds devices and triggers
1180a9409044SAlexey Kardashevskiy 	 * the notifier) and only then it calls pci_bus_add_devices() which
1181a9409044SAlexey Kardashevskiy 	 * configures DMA for buses which also creates PEs and IOMMU groups.
1182a9409044SAlexey Kardashevskiy 	 */
1183a9409044SAlexey Kardashevskiy 	return iommu_probe_device(dev);
11844e13c1acSAlexey Kardashevskiy }
1185d905c5dfSAlexey Kardashevskiy EXPORT_SYMBOL_GPL(iommu_add_device);
11864e13c1acSAlexey Kardashevskiy 
1187bfd8d989STimothy Pearson #if defined(CONFIG_PPC_PSERIES) || defined(CONFIG_PPC_POWERNV)
11889d67c943SAlexey Kardashevskiy /*
11899d67c943SAlexey Kardashevskiy  * A simple iommu_table_group_ops which only allows reusing the existing
11909d67c943SAlexey Kardashevskiy  * iommu_table. This handles VFIO for POWER7 or the nested KVM.
11919d67c943SAlexey Kardashevskiy  * The ops does not allow creating windows and only allows reusing the existing
11929d67c943SAlexey Kardashevskiy  * one if it matches table_group->tce32_start/tce32_size/page_shift.
11939d67c943SAlexey Kardashevskiy  */
spapr_tce_get_table_size(__u32 page_shift,__u64 window_size,__u32 levels)11949d67c943SAlexey Kardashevskiy static unsigned long spapr_tce_get_table_size(__u32 page_shift,
11959d67c943SAlexey Kardashevskiy 					      __u64 window_size, __u32 levels)
11969d67c943SAlexey Kardashevskiy {
11979d67c943SAlexey Kardashevskiy 	unsigned long size;
11989d67c943SAlexey Kardashevskiy 
11999d67c943SAlexey Kardashevskiy 	if (levels > 1)
12009d67c943SAlexey Kardashevskiy 		return ~0U;
12019d67c943SAlexey Kardashevskiy 	size = window_size >> (page_shift - 3);
12029d67c943SAlexey Kardashevskiy 	return size;
12039d67c943SAlexey Kardashevskiy }
12049d67c943SAlexey Kardashevskiy 
spapr_tce_create_table(struct iommu_table_group * table_group,int num,__u32 page_shift,__u64 window_size,__u32 levels,struct iommu_table ** ptbl)12059d67c943SAlexey Kardashevskiy static long spapr_tce_create_table(struct iommu_table_group *table_group, int num,
12069d67c943SAlexey Kardashevskiy 				   __u32 page_shift, __u64 window_size, __u32 levels,
12079d67c943SAlexey Kardashevskiy 				   struct iommu_table **ptbl)
12089d67c943SAlexey Kardashevskiy {
12099d67c943SAlexey Kardashevskiy 	struct iommu_table *tbl = table_group->tables[0];
12109d67c943SAlexey Kardashevskiy 
12119d67c943SAlexey Kardashevskiy 	if (num > 0)
12129d67c943SAlexey Kardashevskiy 		return -EPERM;
12139d67c943SAlexey Kardashevskiy 
12149d67c943SAlexey Kardashevskiy 	if (tbl->it_page_shift != page_shift ||
12159d67c943SAlexey Kardashevskiy 	    tbl->it_size != (window_size >> page_shift) ||
12169d67c943SAlexey Kardashevskiy 	    tbl->it_indirect_levels != levels - 1)
12179d67c943SAlexey Kardashevskiy 		return -EINVAL;
12189d67c943SAlexey Kardashevskiy 
12199d67c943SAlexey Kardashevskiy 	*ptbl = iommu_tce_table_get(tbl);
12209d67c943SAlexey Kardashevskiy 	return 0;
12219d67c943SAlexey Kardashevskiy }
12229d67c943SAlexey Kardashevskiy 
spapr_tce_set_window(struct iommu_table_group * table_group,int num,struct iommu_table * tbl)12239d67c943SAlexey Kardashevskiy static long spapr_tce_set_window(struct iommu_table_group *table_group,
12249d67c943SAlexey Kardashevskiy 				 int num, struct iommu_table *tbl)
12259d67c943SAlexey Kardashevskiy {
12269d67c943SAlexey Kardashevskiy 	return tbl == table_group->tables[num] ? 0 : -EPERM;
12279d67c943SAlexey Kardashevskiy }
12289d67c943SAlexey Kardashevskiy 
spapr_tce_unset_window(struct iommu_table_group * table_group,int num)12299d67c943SAlexey Kardashevskiy static long spapr_tce_unset_window(struct iommu_table_group *table_group, int num)
12309d67c943SAlexey Kardashevskiy {
12319d67c943SAlexey Kardashevskiy 	return 0;
12329d67c943SAlexey Kardashevskiy }
12339d67c943SAlexey Kardashevskiy 
spapr_tce_take_ownership(struct iommu_table_group * table_group)12349d67c943SAlexey Kardashevskiy static long spapr_tce_take_ownership(struct iommu_table_group *table_group)
12359d67c943SAlexey Kardashevskiy {
12369d67c943SAlexey Kardashevskiy 	int i, j, rc = 0;
12379d67c943SAlexey Kardashevskiy 
12389d67c943SAlexey Kardashevskiy 	for (i = 0; i < IOMMU_TABLE_GROUP_MAX_TABLES; ++i) {
12399d67c943SAlexey Kardashevskiy 		struct iommu_table *tbl = table_group->tables[i];
12409d67c943SAlexey Kardashevskiy 
12419d67c943SAlexey Kardashevskiy 		if (!tbl || !tbl->it_map)
12429d67c943SAlexey Kardashevskiy 			continue;
12439d67c943SAlexey Kardashevskiy 
12449d67c943SAlexey Kardashevskiy 		rc = iommu_take_ownership(tbl);
12459d67c943SAlexey Kardashevskiy 		if (!rc)
12469d67c943SAlexey Kardashevskiy 			continue;
1247a9409044SAlexey Kardashevskiy 
12489d67c943SAlexey Kardashevskiy 		for (j = 0; j < i; ++j)
12499d67c943SAlexey Kardashevskiy 			iommu_release_ownership(table_group->tables[j]);
12509d67c943SAlexey Kardashevskiy 		return rc;
12519d67c943SAlexey Kardashevskiy 	}
12529d67c943SAlexey Kardashevskiy 	return 0;
12539d67c943SAlexey Kardashevskiy }
12549d67c943SAlexey Kardashevskiy 
spapr_tce_release_ownership(struct iommu_table_group * table_group)12559d67c943SAlexey Kardashevskiy static void spapr_tce_release_ownership(struct iommu_table_group *table_group)
12569d67c943SAlexey Kardashevskiy {
12579d67c943SAlexey Kardashevskiy 	int i;
12589d67c943SAlexey Kardashevskiy 
12599d67c943SAlexey Kardashevskiy 	for (i = 0; i < IOMMU_TABLE_GROUP_MAX_TABLES; ++i) {
12609d67c943SAlexey Kardashevskiy 		struct iommu_table *tbl = table_group->tables[i];
12619d67c943SAlexey Kardashevskiy 
12629d67c943SAlexey Kardashevskiy 		if (!tbl)
12639d67c943SAlexey Kardashevskiy 			continue;
12649d67c943SAlexey Kardashevskiy 
12659d67c943SAlexey Kardashevskiy 		iommu_table_clear(tbl);
12669d67c943SAlexey Kardashevskiy 		if (tbl->it_map)
12679d67c943SAlexey Kardashevskiy 			iommu_release_ownership(tbl);
12689d67c943SAlexey Kardashevskiy 	}
12699d67c943SAlexey Kardashevskiy }
12709d67c943SAlexey Kardashevskiy 
12719d67c943SAlexey Kardashevskiy struct iommu_table_group_ops spapr_tce_table_group_ops = {
12729d67c943SAlexey Kardashevskiy 	.get_table_size = spapr_tce_get_table_size,
12739d67c943SAlexey Kardashevskiy 	.create_table = spapr_tce_create_table,
12749d67c943SAlexey Kardashevskiy 	.set_window = spapr_tce_set_window,
12759d67c943SAlexey Kardashevskiy 	.unset_window = spapr_tce_unset_window,
12769d67c943SAlexey Kardashevskiy 	.take_ownership = spapr_tce_take_ownership,
12779d67c943SAlexey Kardashevskiy 	.release_ownership = spapr_tce_release_ownership,
12789d67c943SAlexey Kardashevskiy };
12799d67c943SAlexey Kardashevskiy 
1280a9409044SAlexey Kardashevskiy /*
1281a9409044SAlexey Kardashevskiy  * A simple iommu_ops to allow less cruft in generic VFIO code.
1282a9409044SAlexey Kardashevskiy  */
spapr_tce_blocking_iommu_attach_dev(struct iommu_domain * dom,struct device * dev)1283a9409044SAlexey Kardashevskiy static int spapr_tce_blocking_iommu_attach_dev(struct iommu_domain *dom,
1284a9409044SAlexey Kardashevskiy 					       struct device *dev)
1285a9409044SAlexey Kardashevskiy {
1286a9409044SAlexey Kardashevskiy 	struct iommu_group *grp = iommu_group_get(dev);
1287a9409044SAlexey Kardashevskiy 	struct iommu_table_group *table_group;
1288a9409044SAlexey Kardashevskiy 	int ret = -EINVAL;
1289a9409044SAlexey Kardashevskiy 
1290a9409044SAlexey Kardashevskiy 	if (!grp)
1291a9409044SAlexey Kardashevskiy 		return -ENODEV;
1292a9409044SAlexey Kardashevskiy 
1293a9409044SAlexey Kardashevskiy 	table_group = iommu_group_get_iommudata(grp);
1294a9409044SAlexey Kardashevskiy 	ret = table_group->ops->take_ownership(table_group);
1295a9409044SAlexey Kardashevskiy 	iommu_group_put(grp);
1296a9409044SAlexey Kardashevskiy 
1297a9409044SAlexey Kardashevskiy 	return ret;
1298a9409044SAlexey Kardashevskiy }
1299a9409044SAlexey Kardashevskiy 
spapr_tce_blocking_iommu_set_platform_dma(struct device * dev)1300a9409044SAlexey Kardashevskiy static void spapr_tce_blocking_iommu_set_platform_dma(struct device *dev)
1301a9409044SAlexey Kardashevskiy {
1302a9409044SAlexey Kardashevskiy 	struct iommu_group *grp = iommu_group_get(dev);
1303a9409044SAlexey Kardashevskiy 	struct iommu_table_group *table_group;
1304a9409044SAlexey Kardashevskiy 
1305a9409044SAlexey Kardashevskiy 	table_group = iommu_group_get_iommudata(grp);
1306a9409044SAlexey Kardashevskiy 	table_group->ops->release_ownership(table_group);
1307a9409044SAlexey Kardashevskiy }
1308a9409044SAlexey Kardashevskiy 
1309a9409044SAlexey Kardashevskiy static const struct iommu_domain_ops spapr_tce_blocking_domain_ops = {
1310a9409044SAlexey Kardashevskiy 	.attach_dev = spapr_tce_blocking_iommu_attach_dev,
1311a9409044SAlexey Kardashevskiy };
1312a9409044SAlexey Kardashevskiy 
spapr_tce_iommu_capable(struct device * dev,enum iommu_cap cap)1313a9409044SAlexey Kardashevskiy static bool spapr_tce_iommu_capable(struct device *dev, enum iommu_cap cap)
1314a9409044SAlexey Kardashevskiy {
1315a9409044SAlexey Kardashevskiy 	switch (cap) {
1316a9409044SAlexey Kardashevskiy 	case IOMMU_CAP_CACHE_COHERENCY:
1317a9409044SAlexey Kardashevskiy 		return true;
1318a9409044SAlexey Kardashevskiy 	default:
1319a9409044SAlexey Kardashevskiy 		break;
1320a9409044SAlexey Kardashevskiy 	}
1321a9409044SAlexey Kardashevskiy 
1322a9409044SAlexey Kardashevskiy 	return false;
1323a9409044SAlexey Kardashevskiy }
1324a9409044SAlexey Kardashevskiy 
spapr_tce_iommu_domain_alloc(unsigned int type)1325a9409044SAlexey Kardashevskiy static struct iommu_domain *spapr_tce_iommu_domain_alloc(unsigned int type)
1326a9409044SAlexey Kardashevskiy {
1327a9409044SAlexey Kardashevskiy 	struct iommu_domain *dom;
1328a9409044SAlexey Kardashevskiy 
1329a9409044SAlexey Kardashevskiy 	if (type != IOMMU_DOMAIN_BLOCKED)
1330a9409044SAlexey Kardashevskiy 		return NULL;
1331a9409044SAlexey Kardashevskiy 
1332a9409044SAlexey Kardashevskiy 	dom = kzalloc(sizeof(*dom), GFP_KERNEL);
1333a9409044SAlexey Kardashevskiy 	if (!dom)
1334a9409044SAlexey Kardashevskiy 		return NULL;
1335a9409044SAlexey Kardashevskiy 
1336a9409044SAlexey Kardashevskiy 	dom->ops = &spapr_tce_blocking_domain_ops;
1337a9409044SAlexey Kardashevskiy 
1338a9409044SAlexey Kardashevskiy 	return dom;
1339a9409044SAlexey Kardashevskiy }
1340a9409044SAlexey Kardashevskiy 
spapr_tce_iommu_probe_device(struct device * dev)1341a9409044SAlexey Kardashevskiy static struct iommu_device *spapr_tce_iommu_probe_device(struct device *dev)
1342a9409044SAlexey Kardashevskiy {
1343a9409044SAlexey Kardashevskiy 	struct pci_dev *pdev;
1344a9409044SAlexey Kardashevskiy 	struct pci_controller *hose;
1345a9409044SAlexey Kardashevskiy 
1346a9409044SAlexey Kardashevskiy 	if (!dev_is_pci(dev))
1347*b8315b2eSGaurav Batra 		return ERR_PTR(-ENODEV);
1348a9409044SAlexey Kardashevskiy 
1349a9409044SAlexey Kardashevskiy 	pdev = to_pci_dev(dev);
1350a9409044SAlexey Kardashevskiy 	hose = pdev->bus->sysdata;
1351a9409044SAlexey Kardashevskiy 
1352a9409044SAlexey Kardashevskiy 	return &hose->iommu;
1353a9409044SAlexey Kardashevskiy }
1354a9409044SAlexey Kardashevskiy 
spapr_tce_iommu_release_device(struct device * dev)1355a9409044SAlexey Kardashevskiy static void spapr_tce_iommu_release_device(struct device *dev)
1356a9409044SAlexey Kardashevskiy {
1357a9409044SAlexey Kardashevskiy }
1358a9409044SAlexey Kardashevskiy 
spapr_tce_iommu_device_group(struct device * dev)1359a9409044SAlexey Kardashevskiy static struct iommu_group *spapr_tce_iommu_device_group(struct device *dev)
1360a9409044SAlexey Kardashevskiy {
1361a9409044SAlexey Kardashevskiy 	struct pci_controller *hose;
1362a9409044SAlexey Kardashevskiy 	struct pci_dev *pdev;
1363a9409044SAlexey Kardashevskiy 
1364a9409044SAlexey Kardashevskiy 	pdev = to_pci_dev(dev);
1365a9409044SAlexey Kardashevskiy 	hose = pdev->bus->sysdata;
1366a9409044SAlexey Kardashevskiy 
1367a9409044SAlexey Kardashevskiy 	if (!hose->controller_ops.device_group)
1368a9409044SAlexey Kardashevskiy 		return ERR_PTR(-ENOENT);
1369a9409044SAlexey Kardashevskiy 
1370a9409044SAlexey Kardashevskiy 	return hose->controller_ops.device_group(hose, pdev);
1371a9409044SAlexey Kardashevskiy }
1372a9409044SAlexey Kardashevskiy 
1373a9409044SAlexey Kardashevskiy static const struct iommu_ops spapr_tce_iommu_ops = {
1374a9409044SAlexey Kardashevskiy 	.capable = spapr_tce_iommu_capable,
1375a9409044SAlexey Kardashevskiy 	.domain_alloc = spapr_tce_iommu_domain_alloc,
1376a9409044SAlexey Kardashevskiy 	.probe_device = spapr_tce_iommu_probe_device,
1377a9409044SAlexey Kardashevskiy 	.release_device = spapr_tce_iommu_release_device,
1378a9409044SAlexey Kardashevskiy 	.device_group = spapr_tce_iommu_device_group,
1379a9409044SAlexey Kardashevskiy 	.set_platform_dma_ops = spapr_tce_blocking_iommu_set_platform_dma,
1380a9409044SAlexey Kardashevskiy };
1381a9409044SAlexey Kardashevskiy 
1382a9409044SAlexey Kardashevskiy static struct attribute *spapr_tce_iommu_attrs[] = {
1383a9409044SAlexey Kardashevskiy 	NULL,
1384a9409044SAlexey Kardashevskiy };
1385a9409044SAlexey Kardashevskiy 
1386a9409044SAlexey Kardashevskiy static struct attribute_group spapr_tce_iommu_group = {
1387a9409044SAlexey Kardashevskiy 	.name = "spapr-tce-iommu",
1388a9409044SAlexey Kardashevskiy 	.attrs = spapr_tce_iommu_attrs,
1389a9409044SAlexey Kardashevskiy };
1390a9409044SAlexey Kardashevskiy 
1391a9409044SAlexey Kardashevskiy static const struct attribute_group *spapr_tce_iommu_groups[] = {
1392a9409044SAlexey Kardashevskiy 	&spapr_tce_iommu_group,
1393a9409044SAlexey Kardashevskiy 	NULL,
1394a9409044SAlexey Kardashevskiy };
1395a9409044SAlexey Kardashevskiy 
ppc_iommu_register_device(struct pci_controller * phb)1396*b8315b2eSGaurav Batra void ppc_iommu_register_device(struct pci_controller *phb)
1397*b8315b2eSGaurav Batra {
1398*b8315b2eSGaurav Batra 	iommu_device_sysfs_add(&phb->iommu, phb->parent,
1399*b8315b2eSGaurav Batra 				spapr_tce_iommu_groups, "iommu-phb%04x",
1400*b8315b2eSGaurav Batra 				phb->global_number);
1401*b8315b2eSGaurav Batra 	iommu_device_register(&phb->iommu, &spapr_tce_iommu_ops,
1402*b8315b2eSGaurav Batra 				phb->parent);
1403*b8315b2eSGaurav Batra }
1404*b8315b2eSGaurav Batra 
ppc_iommu_unregister_device(struct pci_controller * phb)1405*b8315b2eSGaurav Batra void ppc_iommu_unregister_device(struct pci_controller *phb)
1406*b8315b2eSGaurav Batra {
1407*b8315b2eSGaurav Batra 	iommu_device_unregister(&phb->iommu);
1408*b8315b2eSGaurav Batra 	iommu_device_sysfs_remove(&phb->iommu);
1409*b8315b2eSGaurav Batra }
1410*b8315b2eSGaurav Batra 
1411a9409044SAlexey Kardashevskiy /*
1412a9409044SAlexey Kardashevskiy  * This registers IOMMU devices of PHBs. This needs to happen
1413a9409044SAlexey Kardashevskiy  * after core_initcall(iommu_init) + postcore_initcall(pci_driver_init) and
1414a9409044SAlexey Kardashevskiy  * before subsys_initcall(iommu_subsys_init).
1415a9409044SAlexey Kardashevskiy  */
spapr_tce_setup_phb_iommus_initcall(void)1416a9409044SAlexey Kardashevskiy static int __init spapr_tce_setup_phb_iommus_initcall(void)
1417a9409044SAlexey Kardashevskiy {
1418a9409044SAlexey Kardashevskiy 	struct pci_controller *hose;
1419a9409044SAlexey Kardashevskiy 
1420a9409044SAlexey Kardashevskiy 	list_for_each_entry(hose, &hose_list, list_node) {
1421*b8315b2eSGaurav Batra 		ppc_iommu_register_device(hose);
1422a9409044SAlexey Kardashevskiy 	}
1423a9409044SAlexey Kardashevskiy 	return 0;
1424a9409044SAlexey Kardashevskiy }
1425a9409044SAlexey Kardashevskiy postcore_initcall_sync(spapr_tce_setup_phb_iommus_initcall);
1426bfd8d989STimothy Pearson #endif
1427a9409044SAlexey Kardashevskiy 
14284e13c1acSAlexey Kardashevskiy #endif /* CONFIG_IOMMU_API */
1429