xref: /linux/drivers/iommu/io-pgtable-arm-v7s.c (revision 13c6bba601ac2928e330e14e178c7ebfabb19392)
1caab277bSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2e5fc9753SRobin Murphy /*
3e5fc9753SRobin Murphy  * CPU-agnostic ARM page table allocator.
4e5fc9753SRobin Murphy  *
5e5fc9753SRobin Murphy  * ARMv7 Short-descriptor format, supporting
6e5fc9753SRobin Murphy  * - Basic memory attributes
7e5fc9753SRobin Murphy  * - Simplified access permissions (AP[2:1] model)
8e5fc9753SRobin Murphy  * - Backwards-compatible TEX remap
9e5fc9753SRobin Murphy  * - Large pages/supersections (if indicated by the caller)
10e5fc9753SRobin Murphy  *
11e5fc9753SRobin Murphy  * Not supporting:
12e5fc9753SRobin Murphy  * - Legacy access permissions (AP[2:0] model)
13e5fc9753SRobin Murphy  *
14e5fc9753SRobin Murphy  * Almost certainly never supporting:
15e5fc9753SRobin Murphy  * - PXN
16e5fc9753SRobin Murphy  * - Domains
17e5fc9753SRobin Murphy  *
18e5fc9753SRobin Murphy  * Copyright (C) 2014-2015 ARM Limited
19e5fc9753SRobin Murphy  * Copyright (c) 2014-2015 MediaTek Inc.
20e5fc9753SRobin Murphy  */
21e5fc9753SRobin Murphy 
22e5fc9753SRobin Murphy #define pr_fmt(fmt)	"arm-v7s io-pgtable: " fmt
23e5fc9753SRobin Murphy 
24119ff305SRobin Murphy #include <linux/atomic.h>
25e5fc9753SRobin Murphy #include <linux/dma-mapping.h>
26e5fc9753SRobin Murphy #include <linux/gfp.h>
27b77cf11fSRob Herring #include <linux/io-pgtable.h>
28e5fc9753SRobin Murphy #include <linux/iommu.h>
29e5fc9753SRobin Murphy #include <linux/kernel.h>
30e5fc9753SRobin Murphy #include <linux/kmemleak.h>
31e5fc9753SRobin Murphy #include <linux/sizes.h>
32e5fc9753SRobin Murphy #include <linux/slab.h>
33119ff305SRobin Murphy #include <linux/spinlock.h>
34e5fc9753SRobin Murphy #include <linux/types.h>
35e5fc9753SRobin Murphy 
36e5fc9753SRobin Murphy #include <asm/barrier.h>
37e5fc9753SRobin Murphy 
38e5fc9753SRobin Murphy /* Struct accessors */
39e5fc9753SRobin Murphy #define io_pgtable_to_data(x)						\
40e5fc9753SRobin Murphy 	container_of((x), struct arm_v7s_io_pgtable, iop)
41e5fc9753SRobin Murphy 
42e5fc9753SRobin Murphy #define io_pgtable_ops_to_data(x)					\
43e5fc9753SRobin Murphy 	io_pgtable_to_data(io_pgtable_ops_to_pgtable(x))
44e5fc9753SRobin Murphy 
45e5fc9753SRobin Murphy /*
46e5fc9753SRobin Murphy  * We have 32 bits total; 12 bits resolved at level 1, 8 bits at level 2,
4700ab6f2dSYong Wu  * and 12 bits in a page.
48f3a8a46dSYong Wu  * MediaTek extend 2 bits to reach 34bits, 14 bits at lvl1 and 8 bits at lvl2.
49e5fc9753SRobin Murphy  */
50e5fc9753SRobin Murphy #define ARM_V7S_ADDR_BITS		32
51f3a8a46dSYong Wu #define _ARM_V7S_LVL_BITS(lvl, cfg)	((lvl) == 1 ? ((cfg)->ias - 20) : 8)
5200ab6f2dSYong Wu #define ARM_V7S_LVL_SHIFT(lvl)		((lvl) == 1 ? 20 : 12)
53e5fc9753SRobin Murphy #define ARM_V7S_TABLE_SHIFT		10
54e5fc9753SRobin Murphy 
55468ea0bfSYong Wu #define ARM_V7S_PTES_PER_LVL(lvl, cfg)	(1 << _ARM_V7S_LVL_BITS(lvl, cfg))
56468ea0bfSYong Wu #define ARM_V7S_TABLE_SIZE(lvl, cfg)						\
57468ea0bfSYong Wu 	(ARM_V7S_PTES_PER_LVL(lvl, cfg) * sizeof(arm_v7s_iopte))
58e5fc9753SRobin Murphy 
59e5fc9753SRobin Murphy #define ARM_V7S_BLOCK_SIZE(lvl)		(1UL << ARM_V7S_LVL_SHIFT(lvl))
60e5fc9753SRobin Murphy #define ARM_V7S_LVL_MASK(lvl)		((u32)(~0U << ARM_V7S_LVL_SHIFT(lvl)))
61e5fc9753SRobin Murphy #define ARM_V7S_TABLE_MASK		((u32)(~0U << ARM_V7S_TABLE_SHIFT))
62468ea0bfSYong Wu #define _ARM_V7S_IDX_MASK(lvl, cfg)	(ARM_V7S_PTES_PER_LVL(lvl, cfg) - 1)
63468ea0bfSYong Wu #define ARM_V7S_LVL_IDX(addr, lvl, cfg)	({				\
64e5fc9753SRobin Murphy 	int _l = lvl;							\
65f3a8a46dSYong Wu 	((addr) >> ARM_V7S_LVL_SHIFT(_l)) & _ARM_V7S_IDX_MASK(_l, cfg); \
66e5fc9753SRobin Murphy })
67e5fc9753SRobin Murphy 
68e5fc9753SRobin Murphy /*
69e5fc9753SRobin Murphy  * Large page/supersection entries are effectively a block of 16 page/section
70e5fc9753SRobin Murphy  * entries, along the lines of the LPAE contiguous hint, but all with the
71e5fc9753SRobin Murphy  * same output address. For want of a better common name we'll call them
72e5fc9753SRobin Murphy  * "contiguous" versions of their respective page/section entries here, but
73e5fc9753SRobin Murphy  * noting the distinction (WRT to TLB maintenance) that they represent *one*
74e5fc9753SRobin Murphy  * entry repeated 16 times, not 16 separate entries (as in the LPAE case).
75e5fc9753SRobin Murphy  */
76e5fc9753SRobin Murphy #define ARM_V7S_CONT_PAGES		16
77e5fc9753SRobin Murphy 
78e5fc9753SRobin Murphy /* PTE type bits: these are all mixed up with XN/PXN bits in most cases */
79e5fc9753SRobin Murphy #define ARM_V7S_PTE_TYPE_TABLE		0x1
80e5fc9753SRobin Murphy #define ARM_V7S_PTE_TYPE_PAGE		0x2
81e5fc9753SRobin Murphy #define ARM_V7S_PTE_TYPE_CONT_PAGE	0x1
82e5fc9753SRobin Murphy 
83e5fc9753SRobin Murphy #define ARM_V7S_PTE_IS_VALID(pte)	(((pte) & 0x3) != 0)
849db829d2SRobin Murphy #define ARM_V7S_PTE_IS_TABLE(pte, lvl) \
859db829d2SRobin Murphy 	((lvl) == 1 && (((pte) & 0x3) == ARM_V7S_PTE_TYPE_TABLE))
86e5fc9753SRobin Murphy 
87e5fc9753SRobin Murphy /* Page table bits */
88e5fc9753SRobin Murphy #define ARM_V7S_ATTR_XN(lvl)		BIT(4 * (2 - (lvl)))
89e5fc9753SRobin Murphy #define ARM_V7S_ATTR_B			BIT(2)
90e5fc9753SRobin Murphy #define ARM_V7S_ATTR_C			BIT(3)
91e5fc9753SRobin Murphy #define ARM_V7S_ATTR_NS_TABLE		BIT(3)
92e5fc9753SRobin Murphy #define ARM_V7S_ATTR_NS_SECTION		BIT(19)
93e5fc9753SRobin Murphy 
94e5fc9753SRobin Murphy #define ARM_V7S_CONT_SECTION		BIT(18)
95e5fc9753SRobin Murphy #define ARM_V7S_CONT_PAGE_XN_SHIFT	15
96e5fc9753SRobin Murphy 
97e5fc9753SRobin Murphy /*
98e5fc9753SRobin Murphy  * The attribute bits are consistently ordered*, but occupy bits [17:10] of
99e5fc9753SRobin Murphy  * a level 1 PTE vs. bits [11:4] at level 2. Thus we define the individual
100e5fc9753SRobin Murphy  * fields relative to that 8-bit block, plus a total shift relative to the PTE.
101e5fc9753SRobin Murphy  */
102e5fc9753SRobin Murphy #define ARM_V7S_ATTR_SHIFT(lvl)		(16 - (lvl) * 6)
103e5fc9753SRobin Murphy 
104e5fc9753SRobin Murphy #define ARM_V7S_ATTR_MASK		0xff
105e5fc9753SRobin Murphy #define ARM_V7S_ATTR_AP0		BIT(0)
106e5fc9753SRobin Murphy #define ARM_V7S_ATTR_AP1		BIT(1)
107e5fc9753SRobin Murphy #define ARM_V7S_ATTR_AP2		BIT(5)
108e5fc9753SRobin Murphy #define ARM_V7S_ATTR_S			BIT(6)
109e5fc9753SRobin Murphy #define ARM_V7S_ATTR_NG			BIT(7)
110e5fc9753SRobin Murphy #define ARM_V7S_TEX_SHIFT		2
111e5fc9753SRobin Murphy #define ARM_V7S_TEX_MASK		0x7
112e5fc9753SRobin Murphy #define ARM_V7S_ATTR_TEX(val)		(((val) & ARM_V7S_TEX_MASK) << ARM_V7S_TEX_SHIFT)
113e5fc9753SRobin Murphy 
11440596d2fSYong Wu /* MediaTek extend the bits below for PA 32bit/33bit/34bit */
1154c019de6SYong Wu #define ARM_V7S_ATTR_MTK_PA_BIT32	BIT(9)
1164c019de6SYong Wu #define ARM_V7S_ATTR_MTK_PA_BIT33	BIT(4)
11740596d2fSYong Wu #define ARM_V7S_ATTR_MTK_PA_BIT34	BIT(5)
1181afe2319SYong Wu 
119e5fc9753SRobin Murphy /* *well, except for TEX on level 2 large pages, of course :( */
120e5fc9753SRobin Murphy #define ARM_V7S_CONT_PAGE_TEX_SHIFT	6
121e5fc9753SRobin Murphy #define ARM_V7S_CONT_PAGE_TEX_MASK	(ARM_V7S_TEX_MASK << ARM_V7S_CONT_PAGE_TEX_SHIFT)
122e5fc9753SRobin Murphy 
123e5fc9753SRobin Murphy /* Simplified access permissions */
124e5fc9753SRobin Murphy #define ARM_V7S_PTE_AF			ARM_V7S_ATTR_AP0
125e5fc9753SRobin Murphy #define ARM_V7S_PTE_AP_UNPRIV		ARM_V7S_ATTR_AP1
126e5fc9753SRobin Murphy #define ARM_V7S_PTE_AP_RDONLY		ARM_V7S_ATTR_AP2
127e5fc9753SRobin Murphy 
128e5fc9753SRobin Murphy /* Register bits */
129e5fc9753SRobin Murphy #define ARM_V7S_RGN_NC			0
130e5fc9753SRobin Murphy #define ARM_V7S_RGN_WBWA		1
131e5fc9753SRobin Murphy #define ARM_V7S_RGN_WT			2
132e5fc9753SRobin Murphy #define ARM_V7S_RGN_WB			3
133e5fc9753SRobin Murphy 
134e5fc9753SRobin Murphy #define ARM_V7S_PRRR_TYPE_DEVICE	1
135e5fc9753SRobin Murphy #define ARM_V7S_PRRR_TYPE_NORMAL	2
136e5fc9753SRobin Murphy #define ARM_V7S_PRRR_TR(n, type)	(((type) & 0x3) << ((n) * 2))
137e5fc9753SRobin Murphy #define ARM_V7S_PRRR_DS0		BIT(16)
138e5fc9753SRobin Murphy #define ARM_V7S_PRRR_DS1		BIT(17)
139e5fc9753SRobin Murphy #define ARM_V7S_PRRR_NS0		BIT(18)
140e5fc9753SRobin Murphy #define ARM_V7S_PRRR_NS1		BIT(19)
141e5fc9753SRobin Murphy #define ARM_V7S_PRRR_NOS(n)		BIT((n) + 24)
142e5fc9753SRobin Murphy 
143e5fc9753SRobin Murphy #define ARM_V7S_NMRR_IR(n, attr)	(((attr) & 0x3) << ((n) * 2))
144e5fc9753SRobin Murphy #define ARM_V7S_NMRR_OR(n, attr)	(((attr) & 0x3) << ((n) * 2 + 16))
145e5fc9753SRobin Murphy 
146e5fc9753SRobin Murphy #define ARM_V7S_TTBR_S			BIT(1)
147e5fc9753SRobin Murphy #define ARM_V7S_TTBR_NOS		BIT(5)
148e5fc9753SRobin Murphy #define ARM_V7S_TTBR_ORGN_ATTR(attr)	(((attr) & 0x3) << 3)
149e5fc9753SRobin Murphy #define ARM_V7S_TTBR_IRGN_ATTR(attr)					\
150e5fc9753SRobin Murphy 	((((attr) & 0x1) << 6) | (((attr) & 0x2) >> 1))
151e5fc9753SRobin Murphy 
1520a352554SNicolas Boichat #ifdef CONFIG_ZONE_DMA32
1530a352554SNicolas Boichat #define ARM_V7S_TABLE_GFP_DMA GFP_DMA32
1540a352554SNicolas Boichat #define ARM_V7S_TABLE_SLAB_FLAGS SLAB_CACHE_DMA32
1550a352554SNicolas Boichat #else
1560a352554SNicolas Boichat #define ARM_V7S_TABLE_GFP_DMA GFP_DMA
1570a352554SNicolas Boichat #define ARM_V7S_TABLE_SLAB_FLAGS SLAB_CACHE_DMA
1580a352554SNicolas Boichat #endif
1590a352554SNicolas Boichat 
160e5fc9753SRobin Murphy typedef u32 arm_v7s_iopte;
161e5fc9753SRobin Murphy 
162e5fc9753SRobin Murphy static bool selftest_running;
163e5fc9753SRobin Murphy 
164e5fc9753SRobin Murphy struct arm_v7s_io_pgtable {
165e5fc9753SRobin Murphy 	struct io_pgtable	iop;
166e5fc9753SRobin Murphy 
167e5fc9753SRobin Murphy 	arm_v7s_iopte		*pgd;
168e5fc9753SRobin Murphy 	struct kmem_cache	*l2_tables;
169119ff305SRobin Murphy 	spinlock_t		split_lock;
170e5fc9753SRobin Murphy };
171e5fc9753SRobin Murphy 
1725950b954SYong Wu static bool arm_v7s_pte_is_cont(arm_v7s_iopte pte, int lvl);
1735950b954SYong Wu 
__arm_v7s_dma_addr(void * pages)174e5fc9753SRobin Murphy static dma_addr_t __arm_v7s_dma_addr(void *pages)
175e5fc9753SRobin Murphy {
176e5fc9753SRobin Murphy 	return (dma_addr_t)virt_to_phys(pages);
177e5fc9753SRobin Murphy }
178e5fc9753SRobin Murphy 
arm_v7s_is_mtk_enabled(struct io_pgtable_cfg * cfg)1794c019de6SYong Wu static bool arm_v7s_is_mtk_enabled(struct io_pgtable_cfg *cfg)
1804c019de6SYong Wu {
1814c019de6SYong Wu 	return IS_ENABLED(CONFIG_PHYS_ADDR_T_64BIT) &&
1824c019de6SYong Wu 		(cfg->quirks & IO_PGTABLE_QUIRK_ARM_MTK_EXT);
1834c019de6SYong Wu }
1844c019de6SYong Wu 
to_mtk_iopte(phys_addr_t paddr,arm_v7s_iopte pte)185bfdd2313SYunfei Wang static arm_v7s_iopte to_mtk_iopte(phys_addr_t paddr, arm_v7s_iopte pte)
186e5fc9753SRobin Murphy {
1874c019de6SYong Wu 	if (paddr & BIT_ULL(32))
1884c019de6SYong Wu 		pte |= ARM_V7S_ATTR_MTK_PA_BIT32;
1894c019de6SYong Wu 	if (paddr & BIT_ULL(33))
1904c019de6SYong Wu 		pte |= ARM_V7S_ATTR_MTK_PA_BIT33;
19140596d2fSYong Wu 	if (paddr & BIT_ULL(34))
19240596d2fSYong Wu 		pte |= ARM_V7S_ATTR_MTK_PA_BIT34;
1934c019de6SYong Wu 	return pte;
1945950b954SYong Wu }
1955950b954SYong Wu 
paddr_to_iopte(phys_addr_t paddr,int lvl,struct io_pgtable_cfg * cfg)196bfdd2313SYunfei Wang static arm_v7s_iopte paddr_to_iopte(phys_addr_t paddr, int lvl,
197bfdd2313SYunfei Wang 				    struct io_pgtable_cfg *cfg)
198bfdd2313SYunfei Wang {
199bfdd2313SYunfei Wang 	arm_v7s_iopte pte = paddr & ARM_V7S_LVL_MASK(lvl);
200bfdd2313SYunfei Wang 
201bfdd2313SYunfei Wang 	if (arm_v7s_is_mtk_enabled(cfg))
202bfdd2313SYunfei Wang 		return to_mtk_iopte(paddr, pte);
203bfdd2313SYunfei Wang 
204bfdd2313SYunfei Wang 	return pte;
205bfdd2313SYunfei Wang }
206bfdd2313SYunfei Wang 
iopte_to_paddr(arm_v7s_iopte pte,int lvl,struct io_pgtable_cfg * cfg)2075950b954SYong Wu static phys_addr_t iopte_to_paddr(arm_v7s_iopte pte, int lvl,
2085950b954SYong Wu 				  struct io_pgtable_cfg *cfg)
2095950b954SYong Wu {
2105950b954SYong Wu 	arm_v7s_iopte mask;
2114c019de6SYong Wu 	phys_addr_t paddr;
2125950b954SYong Wu 
213e5fc9753SRobin Murphy 	if (ARM_V7S_PTE_IS_TABLE(pte, lvl))
2145950b954SYong Wu 		mask = ARM_V7S_TABLE_MASK;
2155950b954SYong Wu 	else if (arm_v7s_pte_is_cont(pte, lvl))
2165950b954SYong Wu 		mask = ARM_V7S_LVL_MASK(lvl) * ARM_V7S_CONT_PAGES;
217e5fc9753SRobin Murphy 	else
2185950b954SYong Wu 		mask = ARM_V7S_LVL_MASK(lvl);
2195950b954SYong Wu 
2204c019de6SYong Wu 	paddr = pte & mask;
2214c019de6SYong Wu 	if (!arm_v7s_is_mtk_enabled(cfg))
2224c019de6SYong Wu 		return paddr;
2234c019de6SYong Wu 
2244c019de6SYong Wu 	if (pte & ARM_V7S_ATTR_MTK_PA_BIT32)
2254c019de6SYong Wu 		paddr |= BIT_ULL(32);
2264c019de6SYong Wu 	if (pte & ARM_V7S_ATTR_MTK_PA_BIT33)
2274c019de6SYong Wu 		paddr |= BIT_ULL(33);
22840596d2fSYong Wu 	if (pte & ARM_V7S_ATTR_MTK_PA_BIT34)
22940596d2fSYong Wu 		paddr |= BIT_ULL(34);
2304c019de6SYong Wu 	return paddr;
2315950b954SYong Wu }
2325950b954SYong Wu 
iopte_deref(arm_v7s_iopte pte,int lvl,struct arm_v7s_io_pgtable * data)2335950b954SYong Wu static arm_v7s_iopte *iopte_deref(arm_v7s_iopte pte, int lvl,
2345950b954SYong Wu 				  struct arm_v7s_io_pgtable *data)
2355950b954SYong Wu {
2365950b954SYong Wu 	return phys_to_virt(iopte_to_paddr(pte, lvl, &data->iop.cfg));
237e5fc9753SRobin Murphy }
238e5fc9753SRobin Murphy 
__arm_v7s_alloc_table(int lvl,gfp_t gfp,struct arm_v7s_io_pgtable * data)239e5fc9753SRobin Murphy static void *__arm_v7s_alloc_table(int lvl, gfp_t gfp,
240e5fc9753SRobin Murphy 				   struct arm_v7s_io_pgtable *data)
241e5fc9753SRobin Murphy {
24281b3c252SRobin Murphy 	struct io_pgtable_cfg *cfg = &data->iop.cfg;
24381b3c252SRobin Murphy 	struct device *dev = cfg->iommu_dev;
24429859aebSJean-Philippe Brucker 	phys_addr_t phys;
245e5fc9753SRobin Murphy 	dma_addr_t dma;
246468ea0bfSYong Wu 	size_t size = ARM_V7S_TABLE_SIZE(lvl, cfg);
247e5fc9753SRobin Murphy 	void *table = NULL;
248bfdd2313SYunfei Wang 	gfp_t gfp_l1;
249bfdd2313SYunfei Wang 
250bfdd2313SYunfei Wang 	/*
251bfdd2313SYunfei Wang 	 * ARM_MTK_TTBR_EXT extend the translation table base support larger
252bfdd2313SYunfei Wang 	 * memory address.
253bfdd2313SYunfei Wang 	 */
254bfdd2313SYunfei Wang 	gfp_l1 = cfg->quirks & IO_PGTABLE_QUIRK_ARM_MTK_TTBR_EXT ?
255bfdd2313SYunfei Wang 		 GFP_KERNEL : ARM_V7S_TABLE_GFP_DMA;
256e5fc9753SRobin Murphy 
257e5fc9753SRobin Murphy 	if (lvl == 1)
258bfdd2313SYunfei Wang 		table = (void *)__get_free_pages(gfp_l1 | __GFP_ZERO, get_order(size));
259e5fc9753SRobin Murphy 	else if (lvl == 2)
2600a352554SNicolas Boichat 		table = kmem_cache_zalloc(data->l2_tables, gfp);
261a556cfe4SYunfei Wang 
262a556cfe4SYunfei Wang 	if (!table)
263a556cfe4SYunfei Wang 		return NULL;
264a556cfe4SYunfei Wang 
26529859aebSJean-Philippe Brucker 	phys = virt_to_phys(table);
266bfdd2313SYunfei Wang 	if (cfg->quirks & IO_PGTABLE_QUIRK_ARM_MTK_TTBR_EXT ?
267bfdd2313SYunfei Wang 	    phys >= (1ULL << cfg->oas) : phys != (arm_v7s_iopte)phys) {
26829859aebSJean-Philippe Brucker 		/* Doesn't fit in PTE */
2690a352554SNicolas Boichat 		dev_err(dev, "Page table does not fit in PTE: %pa", &phys);
27029859aebSJean-Philippe Brucker 		goto out_free;
2710a352554SNicolas Boichat 	}
272a556cfe4SYunfei Wang 	if (!cfg->coherent_walk) {
273e5fc9753SRobin Murphy 		dma = dma_map_single(dev, table, size, DMA_TO_DEVICE);
274e5fc9753SRobin Murphy 		if (dma_mapping_error(dev, dma))
275e5fc9753SRobin Murphy 			goto out_free;
276e5fc9753SRobin Murphy 		/*
277e5fc9753SRobin Murphy 		 * We depend on the IOMMU being able to work with any physical
278e5fc9753SRobin Murphy 		 * address directly, so if the DMA layer suggests otherwise by
279e5fc9753SRobin Murphy 		 * translating or truncating them, that bodes very badly...
280e5fc9753SRobin Murphy 		 */
28129859aebSJean-Philippe Brucker 		if (dma != phys)
282e5fc9753SRobin Murphy 			goto out_unmap;
283e5fc9753SRobin Murphy 	}
284032ebd85SNicolas Boichat 	if (lvl == 2)
285e5fc9753SRobin Murphy 		kmemleak_ignore(table);
286e5fc9753SRobin Murphy 	return table;
287e5fc9753SRobin Murphy 
288e5fc9753SRobin Murphy out_unmap:
289e5fc9753SRobin Murphy 	dev_err(dev, "Cannot accommodate DMA translation for IOMMU page tables\n");
290e5fc9753SRobin Murphy 	dma_unmap_single(dev, dma, size, DMA_TO_DEVICE);
291e5fc9753SRobin Murphy out_free:
292e5fc9753SRobin Murphy 	if (lvl == 1)
293e5fc9753SRobin Murphy 		free_pages((unsigned long)table, get_order(size));
294e5fc9753SRobin Murphy 	else
295e5fc9753SRobin Murphy 		kmem_cache_free(data->l2_tables, table);
296e5fc9753SRobin Murphy 	return NULL;
297e5fc9753SRobin Murphy }
298e5fc9753SRobin Murphy 
__arm_v7s_free_table(void * table,int lvl,struct arm_v7s_io_pgtable * data)299e5fc9753SRobin Murphy static void __arm_v7s_free_table(void *table, int lvl,
300e5fc9753SRobin Murphy 				 struct arm_v7s_io_pgtable *data)
301e5fc9753SRobin Murphy {
30281b3c252SRobin Murphy 	struct io_pgtable_cfg *cfg = &data->iop.cfg;
30381b3c252SRobin Murphy 	struct device *dev = cfg->iommu_dev;
304468ea0bfSYong Wu 	size_t size = ARM_V7S_TABLE_SIZE(lvl, cfg);
305e5fc9753SRobin Murphy 
3064f41845bSWill Deacon 	if (!cfg->coherent_walk)
307e5fc9753SRobin Murphy 		dma_unmap_single(dev, __arm_v7s_dma_addr(table), size,
308e5fc9753SRobin Murphy 				 DMA_TO_DEVICE);
309e5fc9753SRobin Murphy 	if (lvl == 1)
310e5fc9753SRobin Murphy 		free_pages((unsigned long)table, get_order(size));
311e5fc9753SRobin Murphy 	else
312e5fc9753SRobin Murphy 		kmem_cache_free(data->l2_tables, table);
313e5fc9753SRobin Murphy }
314e5fc9753SRobin Murphy 
__arm_v7s_pte_sync(arm_v7s_iopte * ptep,int num_entries,struct io_pgtable_cfg * cfg)315e5fc9753SRobin Murphy static void __arm_v7s_pte_sync(arm_v7s_iopte *ptep, int num_entries,
316e5fc9753SRobin Murphy 			       struct io_pgtable_cfg *cfg)
317e5fc9753SRobin Murphy {
3184f41845bSWill Deacon 	if (cfg->coherent_walk)
319e5fc9753SRobin Murphy 		return;
320e5fc9753SRobin Murphy 
321e5fc9753SRobin Murphy 	dma_sync_single_for_device(cfg->iommu_dev, __arm_v7s_dma_addr(ptep),
322e5fc9753SRobin Murphy 				   num_entries * sizeof(*ptep), DMA_TO_DEVICE);
323e5fc9753SRobin Murphy }
__arm_v7s_set_pte(arm_v7s_iopte * ptep,arm_v7s_iopte pte,int num_entries,struct io_pgtable_cfg * cfg)324e5fc9753SRobin Murphy static void __arm_v7s_set_pte(arm_v7s_iopte *ptep, arm_v7s_iopte pte,
325e5fc9753SRobin Murphy 			      int num_entries, struct io_pgtable_cfg *cfg)
326e5fc9753SRobin Murphy {
327e5fc9753SRobin Murphy 	int i;
328e5fc9753SRobin Murphy 
329e5fc9753SRobin Murphy 	for (i = 0; i < num_entries; i++)
330e5fc9753SRobin Murphy 		ptep[i] = pte;
331e5fc9753SRobin Murphy 
332e5fc9753SRobin Murphy 	__arm_v7s_pte_sync(ptep, num_entries, cfg);
333e5fc9753SRobin Murphy }
334e5fc9753SRobin Murphy 
arm_v7s_prot_to_pte(int prot,int lvl,struct io_pgtable_cfg * cfg)335e5fc9753SRobin Murphy static arm_v7s_iopte arm_v7s_prot_to_pte(int prot, int lvl,
336e5fc9753SRobin Murphy 					 struct io_pgtable_cfg *cfg)
337e5fc9753SRobin Murphy {
338e5fc9753SRobin Murphy 	bool ap = !(cfg->quirks & IO_PGTABLE_QUIRK_NO_PERMS);
339e88ccab1SRobin Murphy 	arm_v7s_iopte pte = ARM_V7S_ATTR_NG | ARM_V7S_ATTR_S;
340e5fc9753SRobin Murphy 
341e88ccab1SRobin Murphy 	if (!(prot & IOMMU_MMIO))
342e88ccab1SRobin Murphy 		pte |= ARM_V7S_ATTR_TEX(1);
343e5fc9753SRobin Murphy 	if (ap) {
3445baf1e9dSRobin Murphy 		pte |= ARM_V7S_PTE_AF;
3455baf1e9dSRobin Murphy 		if (!(prot & IOMMU_PRIV))
3465baf1e9dSRobin Murphy 			pte |= ARM_V7S_PTE_AP_UNPRIV;
347e5fc9753SRobin Murphy 		if (!(prot & IOMMU_WRITE))
348e5fc9753SRobin Murphy 			pte |= ARM_V7S_PTE_AP_RDONLY;
349e5fc9753SRobin Murphy 	}
350e5fc9753SRobin Murphy 	pte <<= ARM_V7S_ATTR_SHIFT(lvl);
351e5fc9753SRobin Murphy 
352e5fc9753SRobin Murphy 	if ((prot & IOMMU_NOEXEC) && ap)
353e5fc9753SRobin Murphy 		pte |= ARM_V7S_ATTR_XN(lvl);
354e88ccab1SRobin Murphy 	if (prot & IOMMU_MMIO)
355e88ccab1SRobin Murphy 		pte |= ARM_V7S_ATTR_B;
356e88ccab1SRobin Murphy 	else if (prot & IOMMU_CACHE)
357e5fc9753SRobin Murphy 		pte |= ARM_V7S_ATTR_B | ARM_V7S_ATTR_C;
358e5fc9753SRobin Murphy 
359b9f1ef30SRobin Murphy 	pte |= ARM_V7S_PTE_TYPE_PAGE;
360b9f1ef30SRobin Murphy 	if (lvl == 1 && (cfg->quirks & IO_PGTABLE_QUIRK_ARM_NS))
361b9f1ef30SRobin Murphy 		pte |= ARM_V7S_ATTR_NS_SECTION;
362b9f1ef30SRobin Murphy 
363e5fc9753SRobin Murphy 	return pte;
364e5fc9753SRobin Murphy }
365e5fc9753SRobin Murphy 
arm_v7s_pte_to_prot(arm_v7s_iopte pte,int lvl)366e5fc9753SRobin Murphy static int arm_v7s_pte_to_prot(arm_v7s_iopte pte, int lvl)
367e5fc9753SRobin Murphy {
368e5fc9753SRobin Murphy 	int prot = IOMMU_READ;
369e88ccab1SRobin Murphy 	arm_v7s_iopte attr = pte >> ARM_V7S_ATTR_SHIFT(lvl);
370e5fc9753SRobin Murphy 
371e633fc7aSRobin Murphy 	if (!(attr & ARM_V7S_PTE_AP_RDONLY))
372e5fc9753SRobin Murphy 		prot |= IOMMU_WRITE;
3735baf1e9dSRobin Murphy 	if (!(attr & ARM_V7S_PTE_AP_UNPRIV))
3745baf1e9dSRobin Murphy 		prot |= IOMMU_PRIV;
375e88ccab1SRobin Murphy 	if ((attr & (ARM_V7S_TEX_MASK << ARM_V7S_TEX_SHIFT)) == 0)
376e88ccab1SRobin Murphy 		prot |= IOMMU_MMIO;
377e88ccab1SRobin Murphy 	else if (pte & ARM_V7S_ATTR_C)
378e5fc9753SRobin Murphy 		prot |= IOMMU_CACHE;
379e633fc7aSRobin Murphy 	if (pte & ARM_V7S_ATTR_XN(lvl))
380e633fc7aSRobin Murphy 		prot |= IOMMU_NOEXEC;
381e5fc9753SRobin Murphy 
382e5fc9753SRobin Murphy 	return prot;
383e5fc9753SRobin Murphy }
384e5fc9753SRobin Murphy 
arm_v7s_pte_to_cont(arm_v7s_iopte pte,int lvl)385e5fc9753SRobin Murphy static arm_v7s_iopte arm_v7s_pte_to_cont(arm_v7s_iopte pte, int lvl)
386e5fc9753SRobin Murphy {
387e5fc9753SRobin Murphy 	if (lvl == 1) {
388e5fc9753SRobin Murphy 		pte |= ARM_V7S_CONT_SECTION;
389e5fc9753SRobin Murphy 	} else if (lvl == 2) {
390e5fc9753SRobin Murphy 		arm_v7s_iopte xn = pte & ARM_V7S_ATTR_XN(lvl);
391e5fc9753SRobin Murphy 		arm_v7s_iopte tex = pte & ARM_V7S_CONT_PAGE_TEX_MASK;
392e5fc9753SRobin Murphy 
393e5fc9753SRobin Murphy 		pte ^= xn | tex | ARM_V7S_PTE_TYPE_PAGE;
394e5fc9753SRobin Murphy 		pte |= (xn << ARM_V7S_CONT_PAGE_XN_SHIFT) |
395e5fc9753SRobin Murphy 		       (tex << ARM_V7S_CONT_PAGE_TEX_SHIFT) |
396e5fc9753SRobin Murphy 		       ARM_V7S_PTE_TYPE_CONT_PAGE;
397e5fc9753SRobin Murphy 	}
398e5fc9753SRobin Murphy 	return pte;
399e5fc9753SRobin Murphy }
400e5fc9753SRobin Murphy 
arm_v7s_cont_to_pte(arm_v7s_iopte pte,int lvl)401e5fc9753SRobin Murphy static arm_v7s_iopte arm_v7s_cont_to_pte(arm_v7s_iopte pte, int lvl)
402e5fc9753SRobin Murphy {
403e5fc9753SRobin Murphy 	if (lvl == 1) {
404e5fc9753SRobin Murphy 		pte &= ~ARM_V7S_CONT_SECTION;
405e5fc9753SRobin Murphy 	} else if (lvl == 2) {
406e5fc9753SRobin Murphy 		arm_v7s_iopte xn = pte & BIT(ARM_V7S_CONT_PAGE_XN_SHIFT);
407e5fc9753SRobin Murphy 		arm_v7s_iopte tex = pte & (ARM_V7S_CONT_PAGE_TEX_MASK <<
408e5fc9753SRobin Murphy 					   ARM_V7S_CONT_PAGE_TEX_SHIFT);
409e5fc9753SRobin Murphy 
410e5fc9753SRobin Murphy 		pte ^= xn | tex | ARM_V7S_PTE_TYPE_CONT_PAGE;
411e5fc9753SRobin Murphy 		pte |= (xn >> ARM_V7S_CONT_PAGE_XN_SHIFT) |
412e5fc9753SRobin Murphy 		       (tex >> ARM_V7S_CONT_PAGE_TEX_SHIFT) |
413e5fc9753SRobin Murphy 		       ARM_V7S_PTE_TYPE_PAGE;
414e5fc9753SRobin Murphy 	}
415e5fc9753SRobin Murphy 	return pte;
416e5fc9753SRobin Murphy }
417e5fc9753SRobin Murphy 
arm_v7s_pte_is_cont(arm_v7s_iopte pte,int lvl)418e5fc9753SRobin Murphy static bool arm_v7s_pte_is_cont(arm_v7s_iopte pte, int lvl)
419e5fc9753SRobin Murphy {
420e5fc9753SRobin Murphy 	if (lvl == 1 && !ARM_V7S_PTE_IS_TABLE(pte, lvl))
421e5fc9753SRobin Murphy 		return pte & ARM_V7S_CONT_SECTION;
422e5fc9753SRobin Murphy 	else if (lvl == 2)
423e5fc9753SRobin Murphy 		return !(pte & ARM_V7S_PTE_TYPE_PAGE);
424e5fc9753SRobin Murphy 	return false;
425e5fc9753SRobin Murphy }
426e5fc9753SRobin Murphy 
4273951c41aSWill Deacon static size_t __arm_v7s_unmap(struct arm_v7s_io_pgtable *,
4283951c41aSWill Deacon 			      struct iommu_iotlb_gather *, unsigned long,
429e5fc9753SRobin Murphy 			      size_t, int, arm_v7s_iopte *);
430e5fc9753SRobin Murphy 
arm_v7s_init_pte(struct arm_v7s_io_pgtable * data,unsigned long iova,phys_addr_t paddr,int prot,int lvl,int num_entries,arm_v7s_iopte * ptep)431e5fc9753SRobin Murphy static int arm_v7s_init_pte(struct arm_v7s_io_pgtable *data,
432e5fc9753SRobin Murphy 			    unsigned long iova, phys_addr_t paddr, int prot,
433e5fc9753SRobin Murphy 			    int lvl, int num_entries, arm_v7s_iopte *ptep)
434e5fc9753SRobin Murphy {
435e5fc9753SRobin Murphy 	struct io_pgtable_cfg *cfg = &data->iop.cfg;
436b9f1ef30SRobin Murphy 	arm_v7s_iopte pte;
437e5fc9753SRobin Murphy 	int i;
438e5fc9753SRobin Murphy 
439e5fc9753SRobin Murphy 	for (i = 0; i < num_entries; i++)
440e5fc9753SRobin Murphy 		if (ARM_V7S_PTE_IS_TABLE(ptep[i], lvl)) {
441e5fc9753SRobin Murphy 			/*
442e5fc9753SRobin Murphy 			 * We need to unmap and free the old table before
443e5fc9753SRobin Murphy 			 * overwriting it with a block entry.
444e5fc9753SRobin Murphy 			 */
445e5fc9753SRobin Murphy 			arm_v7s_iopte *tblp;
446e5fc9753SRobin Murphy 			size_t sz = ARM_V7S_BLOCK_SIZE(lvl);
447e5fc9753SRobin Murphy 
448468ea0bfSYong Wu 			tblp = ptep - ARM_V7S_LVL_IDX(iova, lvl, cfg);
4493951c41aSWill Deacon 			if (WARN_ON(__arm_v7s_unmap(data, NULL, iova + i * sz,
450e5fc9753SRobin Murphy 						    sz, lvl, tblp) != sz))
451e5fc9753SRobin Murphy 				return -EINVAL;
452e5fc9753SRobin Murphy 		} else if (ptep[i]) {
453e5fc9753SRobin Murphy 			/* We require an unmap first */
454e5fc9753SRobin Murphy 			WARN_ON(!selftest_running);
455e5fc9753SRobin Murphy 			return -EEXIST;
456e5fc9753SRobin Murphy 		}
457e5fc9753SRobin Murphy 
458b9f1ef30SRobin Murphy 	pte = arm_v7s_prot_to_pte(prot, lvl, cfg);
459e5fc9753SRobin Murphy 	if (num_entries > 1)
460e5fc9753SRobin Murphy 		pte = arm_v7s_pte_to_cont(pte, lvl);
461e5fc9753SRobin Murphy 
4625950b954SYong Wu 	pte |= paddr_to_iopte(paddr, lvl, cfg);
463e5fc9753SRobin Murphy 
464e5fc9753SRobin Murphy 	__arm_v7s_set_pte(ptep, pte, num_entries, cfg);
465e5fc9753SRobin Murphy 	return 0;
466e5fc9753SRobin Murphy }
467e5fc9753SRobin Murphy 
arm_v7s_install_table(arm_v7s_iopte * table,arm_v7s_iopte * ptep,arm_v7s_iopte curr,struct io_pgtable_cfg * cfg)468b9f1ef30SRobin Murphy static arm_v7s_iopte arm_v7s_install_table(arm_v7s_iopte *table,
469b9f1ef30SRobin Murphy 					   arm_v7s_iopte *ptep,
470119ff305SRobin Murphy 					   arm_v7s_iopte curr,
471b9f1ef30SRobin Murphy 					   struct io_pgtable_cfg *cfg)
472b9f1ef30SRobin Murphy {
473bfdd2313SYunfei Wang 	phys_addr_t phys = virt_to_phys(table);
474119ff305SRobin Murphy 	arm_v7s_iopte old, new;
475b9f1ef30SRobin Murphy 
476bfdd2313SYunfei Wang 	new = phys | ARM_V7S_PTE_TYPE_TABLE;
477bfdd2313SYunfei Wang 
478bfdd2313SYunfei Wang 	if (cfg->quirks & IO_PGTABLE_QUIRK_ARM_MTK_TTBR_EXT)
479bfdd2313SYunfei Wang 		new = to_mtk_iopte(phys, new);
480bfdd2313SYunfei Wang 
481b9f1ef30SRobin Murphy 	if (cfg->quirks & IO_PGTABLE_QUIRK_ARM_NS)
482b9f1ef30SRobin Murphy 		new |= ARM_V7S_ATTR_NS_TABLE;
483b9f1ef30SRobin Murphy 
48477f34458SWill Deacon 	/*
48577f34458SWill Deacon 	 * Ensure the table itself is visible before its PTE can be.
48677f34458SWill Deacon 	 * Whilst we could get away with cmpxchg64_release below, this
48777f34458SWill Deacon 	 * doesn't have any ordering semantics when !CONFIG_SMP.
48877f34458SWill Deacon 	 */
48977f34458SWill Deacon 	dma_wmb();
490119ff305SRobin Murphy 
491119ff305SRobin Murphy 	old = cmpxchg_relaxed(ptep, curr, new);
492119ff305SRobin Murphy 	__arm_v7s_pte_sync(ptep, 1, cfg);
493119ff305SRobin Murphy 
494119ff305SRobin Murphy 	return old;
495b9f1ef30SRobin Murphy }
496b9f1ef30SRobin Murphy 
__arm_v7s_map(struct arm_v7s_io_pgtable * data,unsigned long iova,phys_addr_t paddr,size_t size,int prot,int lvl,arm_v7s_iopte * ptep,gfp_t gfp)497e5fc9753SRobin Murphy static int __arm_v7s_map(struct arm_v7s_io_pgtable *data, unsigned long iova,
498e5fc9753SRobin Murphy 			 phys_addr_t paddr, size_t size, int prot,
499f34ce7a7SBaolin Wang 			 int lvl, arm_v7s_iopte *ptep, gfp_t gfp)
500e5fc9753SRobin Murphy {
501e5fc9753SRobin Murphy 	struct io_pgtable_cfg *cfg = &data->iop.cfg;
502e5fc9753SRobin Murphy 	arm_v7s_iopte pte, *cptep;
503e5fc9753SRobin Murphy 	int num_entries = size >> ARM_V7S_LVL_SHIFT(lvl);
504e5fc9753SRobin Murphy 
505e5fc9753SRobin Murphy 	/* Find our entry at the current level */
506468ea0bfSYong Wu 	ptep += ARM_V7S_LVL_IDX(iova, lvl, cfg);
507e5fc9753SRobin Murphy 
508e5fc9753SRobin Murphy 	/* If we can install a leaf entry at this level, then do so */
509e5fc9753SRobin Murphy 	if (num_entries)
510e5fc9753SRobin Murphy 		return arm_v7s_init_pte(data, iova, paddr, prot,
511e5fc9753SRobin Murphy 					lvl, num_entries, ptep);
512e5fc9753SRobin Murphy 
513e5fc9753SRobin Murphy 	/* We can't allocate tables at the final level */
514e5fc9753SRobin Murphy 	if (WARN_ON(lvl == 2))
515e5fc9753SRobin Murphy 		return -EINVAL;
516e5fc9753SRobin Murphy 
517e5fc9753SRobin Murphy 	/* Grab a pointer to the next level */
518119ff305SRobin Murphy 	pte = READ_ONCE(*ptep);
519e5fc9753SRobin Murphy 	if (!pte) {
520f34ce7a7SBaolin Wang 		cptep = __arm_v7s_alloc_table(lvl + 1, gfp, data);
521e5fc9753SRobin Murphy 		if (!cptep)
522e5fc9753SRobin Murphy 			return -ENOMEM;
523e5fc9753SRobin Murphy 
524119ff305SRobin Murphy 		pte = arm_v7s_install_table(cptep, ptep, 0, cfg);
525119ff305SRobin Murphy 		if (pte)
526119ff305SRobin Murphy 			__arm_v7s_free_table(cptep, lvl + 1, data);
527a03849e7SOleksandr Tyshchenko 	} else {
528119ff305SRobin Murphy 		/* We've no easy way of knowing if it's synced yet, so... */
529119ff305SRobin Murphy 		__arm_v7s_pte_sync(ptep, 1, cfg);
530119ff305SRobin Murphy 	}
531119ff305SRobin Murphy 
532119ff305SRobin Murphy 	if (ARM_V7S_PTE_IS_TABLE(pte, lvl)) {
5335950b954SYong Wu 		cptep = iopte_deref(pte, lvl, data);
534119ff305SRobin Murphy 	} else if (pte) {
535a03849e7SOleksandr Tyshchenko 		/* We require an unmap first */
536a03849e7SOleksandr Tyshchenko 		WARN_ON(!selftest_running);
537a03849e7SOleksandr Tyshchenko 		return -EEXIST;
538e5fc9753SRobin Murphy 	}
539e5fc9753SRobin Murphy 
540e5fc9753SRobin Murphy 	/* Rinse, repeat */
541f34ce7a7SBaolin Wang 	return __arm_v7s_map(data, iova, paddr, size, prot, lvl + 1, cptep, gfp);
542e5fc9753SRobin Murphy }
543e5fc9753SRobin Murphy 
arm_v7s_map_pages(struct io_pgtable_ops * ops,unsigned long iova,phys_addr_t paddr,size_t pgsize,size_t pgcount,int prot,gfp_t gfp,size_t * mapped)54423c30bedSIsaac J. Manjarres static int arm_v7s_map_pages(struct io_pgtable_ops *ops, unsigned long iova,
54523c30bedSIsaac J. Manjarres 			     phys_addr_t paddr, size_t pgsize, size_t pgcount,
54623c30bedSIsaac J. Manjarres 			     int prot, gfp_t gfp, size_t *mapped)
547e5fc9753SRobin Murphy {
548e5fc9753SRobin Murphy 	struct arm_v7s_io_pgtable *data = io_pgtable_ops_to_data(ops);
54923c30bedSIsaac J. Manjarres 	int ret = -EINVAL;
550e5fc9753SRobin Murphy 
5517f315c9dSYong Wu 	if (WARN_ON(iova >= (1ULL << data->iop.cfg.ias) ||
5527f315c9dSYong Wu 		    paddr >= (1ULL << data->iop.cfg.oas)))
55376557391SRobin Murphy 		return -ERANGE;
55476557391SRobin Murphy 
555f12e0d22SKeqian Zhu 	if (!(prot & (IOMMU_READ | IOMMU_WRITE)))
556*6093cd58SJason Gunthorpe 		return -EINVAL;
557f12e0d22SKeqian Zhu 
55823c30bedSIsaac J. Manjarres 	while (pgcount--) {
55923c30bedSIsaac J. Manjarres 		ret = __arm_v7s_map(data, iova, paddr, pgsize, prot, 1, data->pgd,
56023c30bedSIsaac J. Manjarres 				    gfp);
56123c30bedSIsaac J. Manjarres 		if (ret)
56223c30bedSIsaac J. Manjarres 			break;
56323c30bedSIsaac J. Manjarres 
56423c30bedSIsaac J. Manjarres 		iova += pgsize;
56523c30bedSIsaac J. Manjarres 		paddr += pgsize;
56623c30bedSIsaac J. Manjarres 		*mapped += pgsize;
56723c30bedSIsaac J. Manjarres 	}
568e5fc9753SRobin Murphy 	/*
569e5fc9753SRobin Murphy 	 * Synchronise all PTE updates for the new mapping before there's
570e5fc9753SRobin Murphy 	 * a chance for anything to kick off a table walk for the new iova.
571e5fc9753SRobin Murphy 	 */
572e5fc9753SRobin Murphy 	wmb();
573e5fc9753SRobin Murphy 
574e5fc9753SRobin Murphy 	return ret;
575e5fc9753SRobin Murphy }
576e5fc9753SRobin Murphy 
arm_v7s_free_pgtable(struct io_pgtable * iop)577e5fc9753SRobin Murphy static void arm_v7s_free_pgtable(struct io_pgtable *iop)
578e5fc9753SRobin Murphy {
579e5fc9753SRobin Murphy 	struct arm_v7s_io_pgtable *data = io_pgtable_to_data(iop);
580e5fc9753SRobin Murphy 	int i;
581e5fc9753SRobin Murphy 
582468ea0bfSYong Wu 	for (i = 0; i < ARM_V7S_PTES_PER_LVL(1, &data->iop.cfg); i++) {
583e5fc9753SRobin Murphy 		arm_v7s_iopte pte = data->pgd[i];
584e5fc9753SRobin Murphy 
585e5fc9753SRobin Murphy 		if (ARM_V7S_PTE_IS_TABLE(pte, 1))
5865950b954SYong Wu 			__arm_v7s_free_table(iopte_deref(pte, 1, data),
5875950b954SYong Wu 					     2, data);
588e5fc9753SRobin Murphy 	}
589e5fc9753SRobin Murphy 	__arm_v7s_free_table(data->pgd, 1, data);
590e5fc9753SRobin Murphy 	kmem_cache_destroy(data->l2_tables);
591e5fc9753SRobin Murphy 	kfree(data);
592e5fc9753SRobin Murphy }
593e5fc9753SRobin Murphy 
arm_v7s_split_cont(struct arm_v7s_io_pgtable * data,unsigned long iova,int idx,int lvl,arm_v7s_iopte * ptep)594119ff305SRobin Murphy static arm_v7s_iopte arm_v7s_split_cont(struct arm_v7s_io_pgtable *data,
595e5fc9753SRobin Murphy 					unsigned long iova, int idx, int lvl,
596e5fc9753SRobin Murphy 					arm_v7s_iopte *ptep)
597e5fc9753SRobin Murphy {
598507e4c9dSRobin Murphy 	struct io_pgtable *iop = &data->iop;
599e5fc9753SRobin Murphy 	arm_v7s_iopte pte;
600e5fc9753SRobin Murphy 	size_t size = ARM_V7S_BLOCK_SIZE(lvl);
601e5fc9753SRobin Murphy 	int i;
602e5fc9753SRobin Murphy 
603119ff305SRobin Murphy 	/* Check that we didn't lose a race to get the lock */
604119ff305SRobin Murphy 	pte = *ptep;
605119ff305SRobin Murphy 	if (!arm_v7s_pte_is_cont(pte, lvl))
606119ff305SRobin Murphy 		return pte;
607119ff305SRobin Murphy 
608e5fc9753SRobin Murphy 	ptep -= idx & (ARM_V7S_CONT_PAGES - 1);
609119ff305SRobin Murphy 	pte = arm_v7s_cont_to_pte(pte, lvl);
610119ff305SRobin Murphy 	for (i = 0; i < ARM_V7S_CONT_PAGES; i++)
611119ff305SRobin Murphy 		ptep[i] = pte + i * size;
612e5fc9753SRobin Murphy 
613507e4c9dSRobin Murphy 	__arm_v7s_pte_sync(ptep, ARM_V7S_CONT_PAGES, &iop->cfg);
614e5fc9753SRobin Murphy 
615e5fc9753SRobin Murphy 	size *= ARM_V7S_CONT_PAGES;
616fefe8527SRobin Murphy 	io_pgtable_tlb_flush_walk(iop, iova, size, size);
617119ff305SRobin Murphy 	return pte;
618e5fc9753SRobin Murphy }
619e5fc9753SRobin Murphy 
arm_v7s_split_blk_unmap(struct arm_v7s_io_pgtable * data,struct iommu_iotlb_gather * gather,unsigned long iova,size_t size,arm_v7s_iopte blk_pte,arm_v7s_iopte * ptep)620193e67c0SVivek Gautam static size_t arm_v7s_split_blk_unmap(struct arm_v7s_io_pgtable *data,
6213951c41aSWill Deacon 				      struct iommu_iotlb_gather *gather,
622e5fc9753SRobin Murphy 				      unsigned long iova, size_t size,
623193e67c0SVivek Gautam 				      arm_v7s_iopte blk_pte,
624193e67c0SVivek Gautam 				      arm_v7s_iopte *ptep)
625e5fc9753SRobin Murphy {
626b9f1ef30SRobin Murphy 	struct io_pgtable_cfg *cfg = &data->iop.cfg;
627b9f1ef30SRobin Murphy 	arm_v7s_iopte pte, *tablep;
628b9f1ef30SRobin Murphy 	int i, unmap_idx, num_entries, num_ptes;
629e5fc9753SRobin Murphy 
630b9f1ef30SRobin Murphy 	tablep = __arm_v7s_alloc_table(2, GFP_ATOMIC, data);
631b9f1ef30SRobin Murphy 	if (!tablep)
632b9f1ef30SRobin Murphy 		return 0; /* Bytes unmapped */
633e5fc9753SRobin Murphy 
634468ea0bfSYong Wu 	num_ptes = ARM_V7S_PTES_PER_LVL(2, cfg);
635b9f1ef30SRobin Murphy 	num_entries = size >> ARM_V7S_LVL_SHIFT(2);
636468ea0bfSYong Wu 	unmap_idx = ARM_V7S_LVL_IDX(iova, 2, cfg);
637e5fc9753SRobin Murphy 
638b9f1ef30SRobin Murphy 	pte = arm_v7s_prot_to_pte(arm_v7s_pte_to_prot(blk_pte, 1), 2, cfg);
639b9f1ef30SRobin Murphy 	if (num_entries > 1)
640b9f1ef30SRobin Murphy 		pte = arm_v7s_pte_to_cont(pte, 2);
641b9f1ef30SRobin Murphy 
642b9f1ef30SRobin Murphy 	for (i = 0; i < num_ptes; i += num_entries, pte += size) {
643e5fc9753SRobin Murphy 		/* Unmap! */
644b9f1ef30SRobin Murphy 		if (i == unmap_idx)
645e5fc9753SRobin Murphy 			continue;
646e5fc9753SRobin Murphy 
647b9f1ef30SRobin Murphy 		__arm_v7s_set_pte(&tablep[i], pte, num_entries, cfg);
648e5fc9753SRobin Murphy 	}
649e5fc9753SRobin Murphy 
650119ff305SRobin Murphy 	pte = arm_v7s_install_table(tablep, ptep, blk_pte, cfg);
651119ff305SRobin Murphy 	if (pte != blk_pte) {
652119ff305SRobin Murphy 		__arm_v7s_free_table(tablep, 2, data);
653119ff305SRobin Murphy 
654119ff305SRobin Murphy 		if (!ARM_V7S_PTE_IS_TABLE(pte, 1))
655119ff305SRobin Murphy 			return 0;
656119ff305SRobin Murphy 
6575950b954SYong Wu 		tablep = iopte_deref(pte, 1, data);
6583951c41aSWill Deacon 		return __arm_v7s_unmap(data, gather, iova, size, 2, tablep);
659119ff305SRobin Murphy 	}
660b9f1ef30SRobin Murphy 
6613951c41aSWill Deacon 	io_pgtable_tlb_add_page(&data->iop, gather, iova, size);
662e5fc9753SRobin Murphy 	return size;
663e5fc9753SRobin Murphy }
664e5fc9753SRobin Murphy 
__arm_v7s_unmap(struct arm_v7s_io_pgtable * data,struct iommu_iotlb_gather * gather,unsigned long iova,size_t size,int lvl,arm_v7s_iopte * ptep)665193e67c0SVivek Gautam static size_t __arm_v7s_unmap(struct arm_v7s_io_pgtable *data,
6663951c41aSWill Deacon 			      struct iommu_iotlb_gather *gather,
667e5fc9753SRobin Murphy 			      unsigned long iova, size_t size, int lvl,
668e5fc9753SRobin Murphy 			      arm_v7s_iopte *ptep)
669e5fc9753SRobin Murphy {
670e5fc9753SRobin Murphy 	arm_v7s_iopte pte[ARM_V7S_CONT_PAGES];
671507e4c9dSRobin Murphy 	struct io_pgtable *iop = &data->iop;
672e5fc9753SRobin Murphy 	int idx, i = 0, num_entries = size >> ARM_V7S_LVL_SHIFT(lvl);
673e5fc9753SRobin Murphy 
674e5fc9753SRobin Murphy 	/* Something went horribly wrong and we ran out of page table */
675e5fc9753SRobin Murphy 	if (WARN_ON(lvl > 2))
676e5fc9753SRobin Murphy 		return 0;
677e5fc9753SRobin Murphy 
678468ea0bfSYong Wu 	idx = ARM_V7S_LVL_IDX(iova, lvl, &iop->cfg);
679e5fc9753SRobin Murphy 	ptep += idx;
680e5fc9753SRobin Murphy 	do {
681119ff305SRobin Murphy 		pte[i] = READ_ONCE(ptep[i]);
682119ff305SRobin Murphy 		if (WARN_ON(!ARM_V7S_PTE_IS_VALID(pte[i])))
683e5fc9753SRobin Murphy 			return 0;
684e5fc9753SRobin Murphy 	} while (++i < num_entries);
685e5fc9753SRobin Murphy 
686e5fc9753SRobin Murphy 	/*
687e5fc9753SRobin Murphy 	 * If we've hit a contiguous 'large page' entry at this level, it
688e5fc9753SRobin Murphy 	 * needs splitting first, unless we're unmapping the whole lot.
689119ff305SRobin Murphy 	 *
690119ff305SRobin Murphy 	 * For splitting, we can't rewrite 16 PTEs atomically, and since we
691119ff305SRobin Murphy 	 * can't necessarily assume TEX remap we don't have a software bit to
692119ff305SRobin Murphy 	 * mark live entries being split. In practice (i.e. DMA API code), we
693119ff305SRobin Murphy 	 * will never be splitting large pages anyway, so just wrap this edge
694119ff305SRobin Murphy 	 * case in a lock for the sake of correctness and be done with it.
695e5fc9753SRobin Murphy 	 */
696119ff305SRobin Murphy 	if (num_entries <= 1 && arm_v7s_pte_is_cont(pte[0], lvl)) {
697119ff305SRobin Murphy 		unsigned long flags;
698119ff305SRobin Murphy 
699119ff305SRobin Murphy 		spin_lock_irqsave(&data->split_lock, flags);
700119ff305SRobin Murphy 		pte[0] = arm_v7s_split_cont(data, iova, idx, lvl, ptep);
701119ff305SRobin Murphy 		spin_unlock_irqrestore(&data->split_lock, flags);
702119ff305SRobin Murphy 	}
703e5fc9753SRobin Murphy 
704e5fc9753SRobin Murphy 	/* If the size matches this level, we're in the right place */
705e5fc9753SRobin Murphy 	if (num_entries) {
706e5fc9753SRobin Murphy 		size_t blk_size = ARM_V7S_BLOCK_SIZE(lvl);
707e5fc9753SRobin Murphy 
708507e4c9dSRobin Murphy 		__arm_v7s_set_pte(ptep, 0, num_entries, &iop->cfg);
709e5fc9753SRobin Murphy 
710e5fc9753SRobin Murphy 		for (i = 0; i < num_entries; i++) {
711e5fc9753SRobin Murphy 			if (ARM_V7S_PTE_IS_TABLE(pte[i], lvl)) {
712e5fc9753SRobin Murphy 				/* Also flush any partial walks */
71310b7a7d9SWill Deacon 				io_pgtable_tlb_flush_walk(iop, iova, blk_size,
71410b7a7d9SWill Deacon 						ARM_V7S_BLOCK_SIZE(lvl + 1));
7155950b954SYong Wu 				ptep = iopte_deref(pte[i], lvl, data);
716e5fc9753SRobin Murphy 				__arm_v7s_free_table(ptep, lvl + 1, data);
717f7403abfSRobin Murphy 			} else if (!iommu_iotlb_gather_queued(gather)) {
7183951c41aSWill Deacon 				io_pgtable_tlb_add_page(iop, gather, iova, blk_size);
719e5fc9753SRobin Murphy 			}
720e5fc9753SRobin Murphy 			iova += blk_size;
721e5fc9753SRobin Murphy 		}
722e5fc9753SRobin Murphy 		return size;
723e5fc9753SRobin Murphy 	} else if (lvl == 1 && !ARM_V7S_PTE_IS_TABLE(pte[0], lvl)) {
724e5fc9753SRobin Murphy 		/*
725e5fc9753SRobin Murphy 		 * Insert a table at the next level to map the old region,
726e5fc9753SRobin Murphy 		 * minus the part we want to unmap
727e5fc9753SRobin Murphy 		 */
7283951c41aSWill Deacon 		return arm_v7s_split_blk_unmap(data, gather, iova, size, pte[0],
7293951c41aSWill Deacon 					       ptep);
730e5fc9753SRobin Murphy 	}
731e5fc9753SRobin Murphy 
732e5fc9753SRobin Murphy 	/* Keep on walkin' */
7335950b954SYong Wu 	ptep = iopte_deref(pte[0], lvl, data);
7343951c41aSWill Deacon 	return __arm_v7s_unmap(data, gather, iova, size, lvl + 1, ptep);
735e5fc9753SRobin Murphy }
736e5fc9753SRobin Murphy 
arm_v7s_unmap_pages(struct io_pgtable_ops * ops,unsigned long iova,size_t pgsize,size_t pgcount,struct iommu_iotlb_gather * gather)737f13eabcfSIsaac J. Manjarres static size_t arm_v7s_unmap_pages(struct io_pgtable_ops *ops, unsigned long iova,
738f13eabcfSIsaac J. Manjarres 				  size_t pgsize, size_t pgcount,
739f13eabcfSIsaac J. Manjarres 				  struct iommu_iotlb_gather *gather)
740e5fc9753SRobin Murphy {
741e5fc9753SRobin Murphy 	struct arm_v7s_io_pgtable *data = io_pgtable_ops_to_data(ops);
742f13eabcfSIsaac J. Manjarres 	size_t unmapped = 0, ret;
743e5fc9753SRobin Murphy 
744859da211SYong Wu 	if (WARN_ON(iova >= (1ULL << data->iop.cfg.ias)))
74576557391SRobin Murphy 		return 0;
74676557391SRobin Murphy 
747f13eabcfSIsaac J. Manjarres 	while (pgcount--) {
748f13eabcfSIsaac J. Manjarres 		ret = __arm_v7s_unmap(data, gather, iova, pgsize, 1, data->pgd);
749f13eabcfSIsaac J. Manjarres 		if (!ret)
750f13eabcfSIsaac J. Manjarres 			break;
751f13eabcfSIsaac J. Manjarres 
752f13eabcfSIsaac J. Manjarres 		unmapped += pgsize;
753f13eabcfSIsaac J. Manjarres 		iova += pgsize;
754f13eabcfSIsaac J. Manjarres 	}
755f13eabcfSIsaac J. Manjarres 
756f13eabcfSIsaac J. Manjarres 	return unmapped;
757f13eabcfSIsaac J. Manjarres }
758f13eabcfSIsaac J. Manjarres 
arm_v7s_iova_to_phys(struct io_pgtable_ops * ops,unsigned long iova)759e5fc9753SRobin Murphy static phys_addr_t arm_v7s_iova_to_phys(struct io_pgtable_ops *ops,
760e5fc9753SRobin Murphy 					unsigned long iova)
761e5fc9753SRobin Murphy {
762e5fc9753SRobin Murphy 	struct arm_v7s_io_pgtable *data = io_pgtable_ops_to_data(ops);
763e5fc9753SRobin Murphy 	arm_v7s_iopte *ptep = data->pgd, pte;
764e5fc9753SRobin Murphy 	int lvl = 0;
765e5fc9753SRobin Murphy 	u32 mask;
766e5fc9753SRobin Murphy 
767e5fc9753SRobin Murphy 	do {
768468ea0bfSYong Wu 		ptep += ARM_V7S_LVL_IDX(iova, ++lvl, &data->iop.cfg);
769119ff305SRobin Murphy 		pte = READ_ONCE(*ptep);
7705950b954SYong Wu 		ptep = iopte_deref(pte, lvl, data);
771e5fc9753SRobin Murphy 	} while (ARM_V7S_PTE_IS_TABLE(pte, lvl));
772e5fc9753SRobin Murphy 
773e5fc9753SRobin Murphy 	if (!ARM_V7S_PTE_IS_VALID(pte))
774e5fc9753SRobin Murphy 		return 0;
775e5fc9753SRobin Murphy 
776e5fc9753SRobin Murphy 	mask = ARM_V7S_LVL_MASK(lvl);
777e5fc9753SRobin Murphy 	if (arm_v7s_pte_is_cont(pte, lvl))
778e5fc9753SRobin Murphy 		mask *= ARM_V7S_CONT_PAGES;
7795950b954SYong Wu 	return iopte_to_paddr(pte, lvl, &data->iop.cfg) | (iova & ~mask);
780e5fc9753SRobin Murphy }
781e5fc9753SRobin Murphy 
arm_v7s_alloc_pgtable(struct io_pgtable_cfg * cfg,void * cookie)782e5fc9753SRobin Murphy static struct io_pgtable *arm_v7s_alloc_pgtable(struct io_pgtable_cfg *cfg,
783e5fc9753SRobin Murphy 						void *cookie)
784e5fc9753SRobin Murphy {
785e5fc9753SRobin Murphy 	struct arm_v7s_io_pgtable *data;
786bfdd2313SYunfei Wang 	slab_flags_t slab_flag;
787bfdd2313SYunfei Wang 	phys_addr_t paddr;
788e5fc9753SRobin Murphy 
789f3a8a46dSYong Wu 	if (cfg->ias > (arm_v7s_is_mtk_enabled(cfg) ? 34 : ARM_V7S_ADDR_BITS))
7904c019de6SYong Wu 		return NULL;
7914c019de6SYong Wu 
79240596d2fSYong Wu 	if (cfg->oas > (arm_v7s_is_mtk_enabled(cfg) ? 35 : ARM_V7S_ADDR_BITS))
793e5fc9753SRobin Murphy 		return NULL;
794e5fc9753SRobin Murphy 
7953850db49SRobin Murphy 	if (cfg->quirks & ~(IO_PGTABLE_QUIRK_ARM_NS |
7963850db49SRobin Murphy 			    IO_PGTABLE_QUIRK_NO_PERMS |
797bfdd2313SYunfei Wang 			    IO_PGTABLE_QUIRK_ARM_MTK_EXT |
798bfdd2313SYunfei Wang 			    IO_PGTABLE_QUIRK_ARM_MTK_TTBR_EXT))
7991afe2319SYong Wu 		return NULL;
8001afe2319SYong Wu 
8011afe2319SYong Wu 	/* If ARM_MTK_4GB is enabled, the NO_PERMS is also expected. */
80273d50811SYong Wu 	if (cfg->quirks & IO_PGTABLE_QUIRK_ARM_MTK_EXT &&
8031afe2319SYong Wu 	    !(cfg->quirks & IO_PGTABLE_QUIRK_NO_PERMS))
8043850db49SRobin Murphy 			return NULL;
8053850db49SRobin Murphy 
806bfdd2313SYunfei Wang 	if ((cfg->quirks & IO_PGTABLE_QUIRK_ARM_MTK_TTBR_EXT) &&
807bfdd2313SYunfei Wang 	    !arm_v7s_is_mtk_enabled(cfg))
808bfdd2313SYunfei Wang 		return NULL;
809bfdd2313SYunfei Wang 
810e5fc9753SRobin Murphy 	data = kmalloc(sizeof(*data), GFP_KERNEL);
811e5fc9753SRobin Murphy 	if (!data)
812e5fc9753SRobin Murphy 		return NULL;
813e5fc9753SRobin Murphy 
814119ff305SRobin Murphy 	spin_lock_init(&data->split_lock);
815bfdd2313SYunfei Wang 
816bfdd2313SYunfei Wang 	/*
817bfdd2313SYunfei Wang 	 * ARM_MTK_TTBR_EXT extend the translation table base support larger
818bfdd2313SYunfei Wang 	 * memory address.
819bfdd2313SYunfei Wang 	 */
820bfdd2313SYunfei Wang 	slab_flag = cfg->quirks & IO_PGTABLE_QUIRK_ARM_MTK_TTBR_EXT ?
821bfdd2313SYunfei Wang 		    0 : ARM_V7S_TABLE_SLAB_FLAGS;
822bfdd2313SYunfei Wang 
823e5fc9753SRobin Murphy 	data->l2_tables = kmem_cache_create("io-pgtable_armv7s_l2",
824468ea0bfSYong Wu 					    ARM_V7S_TABLE_SIZE(2, cfg),
825468ea0bfSYong Wu 					    ARM_V7S_TABLE_SIZE(2, cfg),
826bfdd2313SYunfei Wang 					    slab_flag, NULL);
827e5fc9753SRobin Murphy 	if (!data->l2_tables)
828e5fc9753SRobin Murphy 		goto out_free_data;
829e5fc9753SRobin Murphy 
830e5fc9753SRobin Murphy 	data->iop.ops = (struct io_pgtable_ops) {
83123c30bedSIsaac J. Manjarres 		.map_pages	= arm_v7s_map_pages,
832f13eabcfSIsaac J. Manjarres 		.unmap_pages	= arm_v7s_unmap_pages,
833e5fc9753SRobin Murphy 		.iova_to_phys	= arm_v7s_iova_to_phys,
834e5fc9753SRobin Murphy 	};
835e5fc9753SRobin Murphy 
836e5fc9753SRobin Murphy 	/* We have to do this early for __arm_v7s_alloc_table to work... */
837e5fc9753SRobin Murphy 	data->iop.cfg = *cfg;
838e5fc9753SRobin Murphy 
839e5fc9753SRobin Murphy 	/*
840e5fc9753SRobin Murphy 	 * Unless the IOMMU driver indicates supersection support by
841e5fc9753SRobin Murphy 	 * having SZ_16M set in the initial bitmap, they won't be used.
842e5fc9753SRobin Murphy 	 */
843e5fc9753SRobin Murphy 	cfg->pgsize_bitmap &= SZ_4K | SZ_64K | SZ_1M | SZ_16M;
844e5fc9753SRobin Murphy 
845fb485eb1SRobin Murphy 	/* TCR: T0SZ=0, EAE=0 (if applicable) */
846fb485eb1SRobin Murphy 	cfg->arm_v7s_cfg.tcr = 0;
847e5fc9753SRobin Murphy 
848e5fc9753SRobin Murphy 	/*
849e5fc9753SRobin Murphy 	 * TEX remap: the indices used map to the closest equivalent types
850e5fc9753SRobin Murphy 	 * under the non-TEX-remap interpretation of those attribute bits,
851e5fc9753SRobin Murphy 	 * excepting various implementation-defined aspects of shareability.
852e5fc9753SRobin Murphy 	 */
853e5fc9753SRobin Murphy 	cfg->arm_v7s_cfg.prrr = ARM_V7S_PRRR_TR(1, ARM_V7S_PRRR_TYPE_DEVICE) |
854e5fc9753SRobin Murphy 				ARM_V7S_PRRR_TR(4, ARM_V7S_PRRR_TYPE_NORMAL) |
855e5fc9753SRobin Murphy 				ARM_V7S_PRRR_TR(7, ARM_V7S_PRRR_TYPE_NORMAL) |
856e5fc9753SRobin Murphy 				ARM_V7S_PRRR_DS0 | ARM_V7S_PRRR_DS1 |
857e5fc9753SRobin Murphy 				ARM_V7S_PRRR_NS1 | ARM_V7S_PRRR_NOS(7);
858e5fc9753SRobin Murphy 	cfg->arm_v7s_cfg.nmrr = ARM_V7S_NMRR_IR(7, ARM_V7S_RGN_WBWA) |
859e5fc9753SRobin Murphy 				ARM_V7S_NMRR_OR(7, ARM_V7S_RGN_WBWA);
860e5fc9753SRobin Murphy 
861e5fc9753SRobin Murphy 	/* Looking good; allocate a pgd */
862e5fc9753SRobin Murphy 	data->pgd = __arm_v7s_alloc_table(1, GFP_KERNEL, data);
863e5fc9753SRobin Murphy 	if (!data->pgd)
864e5fc9753SRobin Murphy 		goto out_free_data;
865e5fc9753SRobin Murphy 
866e5fc9753SRobin Murphy 	/* Ensure the empty pgd is visible before any actual TTBR write */
867e5fc9753SRobin Murphy 	wmb();
868e5fc9753SRobin Murphy 
869d1e5f26fSRobin Murphy 	/* TTBR */
870bfdd2313SYunfei Wang 	paddr = virt_to_phys(data->pgd);
871bfdd2313SYunfei Wang 	if (arm_v7s_is_mtk_enabled(cfg))
872bfdd2313SYunfei Wang 		cfg->arm_v7s_cfg.ttbr = paddr | upper_32_bits(paddr);
873bfdd2313SYunfei Wang 	else
874bfdd2313SYunfei Wang 		cfg->arm_v7s_cfg.ttbr = paddr | ARM_V7S_TTBR_S |
8757618e479SRobin Murphy 					(cfg->coherent_walk ? (ARM_V7S_TTBR_NOS |
8767618e479SRobin Murphy 					 ARM_V7S_TTBR_IRGN_ATTR(ARM_V7S_RGN_WBWA) |
8779e6ea59fSBjorn Andersson 					 ARM_V7S_TTBR_ORGN_ATTR(ARM_V7S_RGN_WBWA)) :
8789e6ea59fSBjorn Andersson 					(ARM_V7S_TTBR_IRGN_ATTR(ARM_V7S_RGN_NC) |
8799e6ea59fSBjorn Andersson 					 ARM_V7S_TTBR_ORGN_ATTR(ARM_V7S_RGN_NC)));
880e5fc9753SRobin Murphy 	return &data->iop;
881e5fc9753SRobin Murphy 
882e5fc9753SRobin Murphy out_free_data:
883e5fc9753SRobin Murphy 	kmem_cache_destroy(data->l2_tables);
884e5fc9753SRobin Murphy 	kfree(data);
885e5fc9753SRobin Murphy 	return NULL;
886e5fc9753SRobin Murphy }
887e5fc9753SRobin Murphy 
888e5fc9753SRobin Murphy struct io_pgtable_init_fns io_pgtable_arm_v7s_init_fns = {
889e5fc9753SRobin Murphy 	.alloc	= arm_v7s_alloc_pgtable,
890e5fc9753SRobin Murphy 	.free	= arm_v7s_free_pgtable,
891e5fc9753SRobin Murphy };
892e5fc9753SRobin Murphy 
893e5fc9753SRobin Murphy #ifdef CONFIG_IOMMU_IO_PGTABLE_ARMV7S_SELFTEST
894e5fc9753SRobin Murphy 
895b5813c16SRobin Murphy static struct io_pgtable_cfg *cfg_cookie __initdata;
896e5fc9753SRobin Murphy 
dummy_tlb_flush_all(void * cookie)897b5813c16SRobin Murphy static void __init dummy_tlb_flush_all(void *cookie)
898e5fc9753SRobin Murphy {
899e5fc9753SRobin Murphy 	WARN_ON(cookie != cfg_cookie);
900e5fc9753SRobin Murphy }
901e5fc9753SRobin Murphy 
dummy_tlb_flush(unsigned long iova,size_t size,size_t granule,void * cookie)902b5813c16SRobin Murphy static void __init dummy_tlb_flush(unsigned long iova, size_t size,
903b5813c16SRobin Murphy 				   size_t granule, void *cookie)
904e5fc9753SRobin Murphy {
905e5fc9753SRobin Murphy 	WARN_ON(cookie != cfg_cookie);
906e5fc9753SRobin Murphy 	WARN_ON(!(size & cfg_cookie->pgsize_bitmap));
907e5fc9753SRobin Murphy }
908e5fc9753SRobin Murphy 
dummy_tlb_add_page(struct iommu_iotlb_gather * gather,unsigned long iova,size_t granule,void * cookie)909b5813c16SRobin Murphy static void __init dummy_tlb_add_page(struct iommu_iotlb_gather *gather,
910b5813c16SRobin Murphy 				      unsigned long iova, size_t granule,
911b5813c16SRobin Murphy 				      void *cookie)
912e5fc9753SRobin Murphy {
913abfd6fe0SWill Deacon 	dummy_tlb_flush(iova, granule, granule, cookie);
914e5fc9753SRobin Murphy }
915e5fc9753SRobin Murphy 
916b5813c16SRobin Murphy static const struct iommu_flush_ops dummy_tlb_ops __initconst = {
917e5fc9753SRobin Murphy 	.tlb_flush_all	= dummy_tlb_flush_all,
91810b7a7d9SWill Deacon 	.tlb_flush_walk	= dummy_tlb_flush,
919abfd6fe0SWill Deacon 	.tlb_add_page	= dummy_tlb_add_page,
920e5fc9753SRobin Murphy };
921e5fc9753SRobin Murphy 
922e5fc9753SRobin Murphy #define __FAIL(ops)	({				\
923e5fc9753SRobin Murphy 		WARN(1, "selftest: test failed\n");	\
924e5fc9753SRobin Murphy 		selftest_running = false;		\
925e5fc9753SRobin Murphy 		-EFAULT;				\
926e5fc9753SRobin Murphy })
927e5fc9753SRobin Murphy 
arm_v7s_do_selftests(void)928e5fc9753SRobin Murphy static int __init arm_v7s_do_selftests(void)
929e5fc9753SRobin Murphy {
930e5fc9753SRobin Murphy 	struct io_pgtable_ops *ops;
931e5fc9753SRobin Murphy 	struct io_pgtable_cfg cfg = {
932e5fc9753SRobin Murphy 		.tlb = &dummy_tlb_ops,
933e5fc9753SRobin Murphy 		.oas = 32,
934e5fc9753SRobin Murphy 		.ias = 32,
9354f41845bSWill Deacon 		.coherent_walk = true,
9364f41845bSWill Deacon 		.quirks = IO_PGTABLE_QUIRK_ARM_NS,
937e5fc9753SRobin Murphy 		.pgsize_bitmap = SZ_4K | SZ_64K | SZ_1M | SZ_16M,
938e5fc9753SRobin Murphy 	};
939e5fc9753SRobin Murphy 	unsigned int iova, size, iova_start;
940e5fc9753SRobin Murphy 	unsigned int i, loopnr = 0;
941b9bf41e2SRobin Murphy 	size_t mapped;
942e5fc9753SRobin Murphy 
943e5fc9753SRobin Murphy 	selftest_running = true;
944e5fc9753SRobin Murphy 
945e5fc9753SRobin Murphy 	cfg_cookie = &cfg;
946e5fc9753SRobin Murphy 
947e5fc9753SRobin Murphy 	ops = alloc_io_pgtable_ops(ARM_V7S, &cfg, &cfg);
948e5fc9753SRobin Murphy 	if (!ops) {
949e5fc9753SRobin Murphy 		pr_err("selftest: failed to allocate io pgtable ops\n");
950e5fc9753SRobin Murphy 		return -EINVAL;
951e5fc9753SRobin Murphy 	}
952e5fc9753SRobin Murphy 
953e5fc9753SRobin Murphy 	/*
954e5fc9753SRobin Murphy 	 * Initial sanity checks.
955e5fc9753SRobin Murphy 	 * Empty page tables shouldn't provide any translations.
956e5fc9753SRobin Murphy 	 */
957e5fc9753SRobin Murphy 	if (ops->iova_to_phys(ops, 42))
958e5fc9753SRobin Murphy 		return __FAIL(ops);
959e5fc9753SRobin Murphy 
960e5fc9753SRobin Murphy 	if (ops->iova_to_phys(ops, SZ_1G + 42))
961e5fc9753SRobin Murphy 		return __FAIL(ops);
962e5fc9753SRobin Murphy 
963e5fc9753SRobin Murphy 	if (ops->iova_to_phys(ops, SZ_2G + 42))
964e5fc9753SRobin Murphy 		return __FAIL(ops);
965e5fc9753SRobin Murphy 
966e5fc9753SRobin Murphy 	/*
967e5fc9753SRobin Murphy 	 * Distinct mappings of different granule sizes.
968e5fc9753SRobin Murphy 	 */
969e5fc9753SRobin Murphy 	iova = 0;
9704ae8a5c5SKefeng Wang 	for_each_set_bit(i, &cfg.pgsize_bitmap, BITS_PER_LONG) {
971e5fc9753SRobin Murphy 		size = 1UL << i;
972b9bf41e2SRobin Murphy 		if (ops->map_pages(ops, iova, iova, size, 1,
973b9bf41e2SRobin Murphy 				   IOMMU_READ | IOMMU_WRITE |
974b9bf41e2SRobin Murphy 				   IOMMU_NOEXEC | IOMMU_CACHE,
975b9bf41e2SRobin Murphy 				   GFP_KERNEL, &mapped))
976e5fc9753SRobin Murphy 			return __FAIL(ops);
977e5fc9753SRobin Murphy 
978e5fc9753SRobin Murphy 		/* Overlapping mappings */
979b9bf41e2SRobin Murphy 		if (!ops->map_pages(ops, iova, iova + size, size, 1,
980b9bf41e2SRobin Murphy 				    IOMMU_READ | IOMMU_NOEXEC, GFP_KERNEL,
981b9bf41e2SRobin Murphy 				    &mapped))
982e5fc9753SRobin Murphy 			return __FAIL(ops);
983e5fc9753SRobin Murphy 
984e5fc9753SRobin Murphy 		if (ops->iova_to_phys(ops, iova + 42) != (iova + 42))
985e5fc9753SRobin Murphy 			return __FAIL(ops);
986e5fc9753SRobin Murphy 
987e5fc9753SRobin Murphy 		iova += SZ_16M;
988e5fc9753SRobin Murphy 		loopnr++;
989e5fc9753SRobin Murphy 	}
990e5fc9753SRobin Murphy 
991e5fc9753SRobin Murphy 	/* Partial unmap */
992e5fc9753SRobin Murphy 	i = 1;
993e5fc9753SRobin Murphy 	size = 1UL << __ffs(cfg.pgsize_bitmap);
994e5fc9753SRobin Murphy 	while (i < loopnr) {
995e5fc9753SRobin Murphy 		iova_start = i * SZ_16M;
996b9bf41e2SRobin Murphy 		if (ops->unmap_pages(ops, iova_start + size, size, 1, NULL) != size)
997e5fc9753SRobin Murphy 			return __FAIL(ops);
998e5fc9753SRobin Murphy 
999e5fc9753SRobin Murphy 		/* Remap of partial unmap */
1000b9bf41e2SRobin Murphy 		if (ops->map_pages(ops, iova_start + size, size, size, 1,
1001b9bf41e2SRobin Murphy 				   IOMMU_READ, GFP_KERNEL, &mapped))
1002e5fc9753SRobin Murphy 			return __FAIL(ops);
1003e5fc9753SRobin Murphy 
1004e5fc9753SRobin Murphy 		if (ops->iova_to_phys(ops, iova_start + size + 42)
1005e5fc9753SRobin Murphy 		    != (size + 42))
1006e5fc9753SRobin Murphy 			return __FAIL(ops);
1007e5fc9753SRobin Murphy 		i++;
1008e5fc9753SRobin Murphy 	}
1009e5fc9753SRobin Murphy 
1010e5fc9753SRobin Murphy 	/* Full unmap */
1011e5fc9753SRobin Murphy 	iova = 0;
1012f793b13eSYueHaibing 	for_each_set_bit(i, &cfg.pgsize_bitmap, BITS_PER_LONG) {
1013e5fc9753SRobin Murphy 		size = 1UL << i;
1014e5fc9753SRobin Murphy 
1015b9bf41e2SRobin Murphy 		if (ops->unmap_pages(ops, iova, size, 1, NULL) != size)
1016e5fc9753SRobin Murphy 			return __FAIL(ops);
1017e5fc9753SRobin Murphy 
1018e5fc9753SRobin Murphy 		if (ops->iova_to_phys(ops, iova + 42))
1019e5fc9753SRobin Murphy 			return __FAIL(ops);
1020e5fc9753SRobin Murphy 
1021e5fc9753SRobin Murphy 		/* Remap full block */
1022b9bf41e2SRobin Murphy 		if (ops->map_pages(ops, iova, iova, size, 1, IOMMU_WRITE,
1023b9bf41e2SRobin Murphy 				   GFP_KERNEL, &mapped))
1024e5fc9753SRobin Murphy 			return __FAIL(ops);
1025e5fc9753SRobin Murphy 
1026e5fc9753SRobin Murphy 		if (ops->iova_to_phys(ops, iova + 42) != (iova + 42))
1027e5fc9753SRobin Murphy 			return __FAIL(ops);
1028e5fc9753SRobin Murphy 
1029e5fc9753SRobin Murphy 		iova += SZ_16M;
1030e5fc9753SRobin Murphy 	}
1031e5fc9753SRobin Murphy 
1032e5fc9753SRobin Murphy 	free_io_pgtable_ops(ops);
1033e5fc9753SRobin Murphy 
1034e5fc9753SRobin Murphy 	selftest_running = false;
1035e5fc9753SRobin Murphy 
1036e5fc9753SRobin Murphy 	pr_info("self test ok\n");
1037e5fc9753SRobin Murphy 	return 0;
1038e5fc9753SRobin Murphy }
1039e5fc9753SRobin Murphy subsys_initcall(arm_v7s_do_selftests);
1040e5fc9753SRobin Murphy #endif
1041