xref: /linux/drivers/iommu/vsi-iommu.c (revision 0eaed89c18aeedf0898baf2dbf5ff027c6795152)
1917ace84SBenjamin Gaignard // SPDX-License-Identifier: GPL-2.0-only
2917ace84SBenjamin Gaignard /* Copyright (C) 2025 Collabora Ltd.
3917ace84SBenjamin Gaignard  *
4917ace84SBenjamin Gaignard  * IOMMU API for Verisilicon
5917ace84SBenjamin Gaignard  *
6917ace84SBenjamin Gaignard  * Module Authors:	Yandong Lin <yandong.lin@rock-chips.com>
7917ace84SBenjamin Gaignard  *			Simon Xue <xxm@rock-chips.com>
8917ace84SBenjamin Gaignard  *			Benjamin Gaignard <benjamin.gaignard@collabora.com>
9917ace84SBenjamin Gaignard  *
10917ace84SBenjamin Gaignard  * This hardware block is using a 2 pages tables allocation structure.
11917ace84SBenjamin Gaignard  * That make very similar to Rockhip iommu hardware blocks but it has
12917ace84SBenjamin Gaignard  * it own driver because the registers offset and configuration bits
13917ace84SBenjamin Gaignard  * are completely different. An additional reason is that this hardware
14917ace84SBenjamin Gaignard  * has been developed by Verisilicon to be used by their hardware video
15917ace84SBenjamin Gaignard  * decoders and not for a general purpose like Rockchip iommus.
16917ace84SBenjamin Gaignard  */
17917ace84SBenjamin Gaignard 
18917ace84SBenjamin Gaignard #include <linux/clk.h>
19917ace84SBenjamin Gaignard #include <linux/compiler.h>
20917ace84SBenjamin Gaignard #include <linux/delay.h>
21917ace84SBenjamin Gaignard #include <linux/device.h>
22917ace84SBenjamin Gaignard #include <linux/dma-mapping.h>
23917ace84SBenjamin Gaignard #include <linux/errno.h>
24917ace84SBenjamin Gaignard #include <linux/init.h>
25917ace84SBenjamin Gaignard #include <linux/interrupt.h>
26917ace84SBenjamin Gaignard #include <linux/io.h>
27917ace84SBenjamin Gaignard #include <linux/iommu.h>
28917ace84SBenjamin Gaignard #include <linux/list.h>
29917ace84SBenjamin Gaignard #include <linux/mm.h>
30917ace84SBenjamin Gaignard #include <linux/module.h>
31917ace84SBenjamin Gaignard #include <linux/of.h>
32917ace84SBenjamin Gaignard #include <linux/of_iommu.h>
33917ace84SBenjamin Gaignard #include <linux/of_platform.h>
34917ace84SBenjamin Gaignard #include <linux/platform_device.h>
35917ace84SBenjamin Gaignard #include <linux/pm_runtime.h>
36917ace84SBenjamin Gaignard #include <linux/slab.h>
37917ace84SBenjamin Gaignard #include <linux/spinlock.h>
38917ace84SBenjamin Gaignard 
39917ace84SBenjamin Gaignard #include "iommu-pages.h"
40917ace84SBenjamin Gaignard 
41917ace84SBenjamin Gaignard struct vsi_iommu {
42917ace84SBenjamin Gaignard 	struct device *dev;
43917ace84SBenjamin Gaignard 	void __iomem *regs;
44917ace84SBenjamin Gaignard 	struct clk_bulk_data *clocks;
45917ace84SBenjamin Gaignard 	int num_clocks;
46917ace84SBenjamin Gaignard 	struct iommu_device iommu;
47917ace84SBenjamin Gaignard 	struct list_head node; /* entry in vsi_iommu_domain.iommus */
48917ace84SBenjamin Gaignard 	struct iommu_domain *domain; /* domain to which iommu is attached */
49917ace84SBenjamin Gaignard 	spinlock_t lock; /* lock to protect vsi_iommu fields */
50917ace84SBenjamin Gaignard 	int irq;
51917ace84SBenjamin Gaignard 	bool enable;
52917ace84SBenjamin Gaignard };
53917ace84SBenjamin Gaignard 
54917ace84SBenjamin Gaignard struct vsi_iommu_domain {
55917ace84SBenjamin Gaignard 	struct list_head iommus;
56917ace84SBenjamin Gaignard 	struct device *dev;
57917ace84SBenjamin Gaignard 	u32 *dt;
58917ace84SBenjamin Gaignard 	dma_addr_t dt_dma;
59917ace84SBenjamin Gaignard 	struct iommu_domain domain;
60917ace84SBenjamin Gaignard 	u64 *pta;
61917ace84SBenjamin Gaignard 	dma_addr_t pta_dma;
62917ace84SBenjamin Gaignard 	spinlock_t lock; /* lock to protect vsi_iommu_domain fields */
63917ace84SBenjamin Gaignard };
64917ace84SBenjamin Gaignard 
65917ace84SBenjamin Gaignard static struct iommu_domain vsi_identity_domain;
66917ace84SBenjamin Gaignard 
67917ace84SBenjamin Gaignard #define NUM_DT_ENTRIES	1024
68917ace84SBenjamin Gaignard #define NUM_PT_ENTRIES	1024
69917ace84SBenjamin Gaignard 
70917ace84SBenjamin Gaignard #define SPAGE_SIZE	BIT(12)
71917ace84SBenjamin Gaignard 
72917ace84SBenjamin Gaignard /* vsi iommu regs address */
73917ace84SBenjamin Gaignard #define VSI_MMU_CONFIG1_BASE			0x1ac
74917ace84SBenjamin Gaignard #define VSI_MMU_AHB_EXCEPTION_BASE		0x380
75917ace84SBenjamin Gaignard #define VSI_MMU_AHB_CONTROL_BASE		0x388
76917ace84SBenjamin Gaignard #define VSI_MMU_AHB_TLB_ARRAY_BASE_L_BASE	0x38C
77917ace84SBenjamin Gaignard 
78917ace84SBenjamin Gaignard /* MMU register offsets */
79917ace84SBenjamin Gaignard #define VSI_MMU_FLUSH_BASE		0x184
80917ace84SBenjamin Gaignard #define VSI_MMU_BIT_FLUSH		BIT(4)
81917ace84SBenjamin Gaignard 
82917ace84SBenjamin Gaignard #define VSI_MMU_PAGE_FAULT_ADDR		0x380
83917ace84SBenjamin Gaignard #define VSI_MMU_STATUS_BASE		0x384	/* IRQ status */
84917ace84SBenjamin Gaignard 
85917ace84SBenjamin Gaignard #define VSI_MMU_BIT_ENABLE		BIT(0)
86917ace84SBenjamin Gaignard 
87917ace84SBenjamin Gaignard #define VSI_MMU_OUT_OF_BOUND		BIT(28)
88917ace84SBenjamin Gaignard /* Irq mask */
89917ace84SBenjamin Gaignard #define VSI_MMU_IRQ_MASK		0x7
90917ace84SBenjamin Gaignard 
91917ace84SBenjamin Gaignard #define VSI_DTE_PT_ADDRESS_MASK		0xffffffc0
92917ace84SBenjamin Gaignard #define VSI_DTE_PT_VALID		BIT(0)
93917ace84SBenjamin Gaignard 
94917ace84SBenjamin Gaignard #define VSI_PAGE_DESC_LO_MASK		0xfffff000
95917ace84SBenjamin Gaignard #define VSI_PAGE_DESC_HI_MASK		GENMASK_ULL(39, 32)
96917ace84SBenjamin Gaignard #define VSI_PAGE_DESC_HI_SHIFT		(32 - 4)
97917ace84SBenjamin Gaignard 
98917ace84SBenjamin Gaignard static inline phys_addr_t vsi_dte_pt_address(u32 dte)
99917ace84SBenjamin Gaignard {
100917ace84SBenjamin Gaignard 	return (phys_addr_t)dte & VSI_DTE_PT_ADDRESS_MASK;
101917ace84SBenjamin Gaignard }
102917ace84SBenjamin Gaignard 
103917ace84SBenjamin Gaignard static inline u32 vsi_mk_dte(u32 dte)
104917ace84SBenjamin Gaignard {
105917ace84SBenjamin Gaignard 	return (phys_addr_t)dte | VSI_DTE_PT_VALID;
106917ace84SBenjamin Gaignard }
107917ace84SBenjamin Gaignard 
108917ace84SBenjamin Gaignard #define VSI_PTE_PAGE_WRITABLE		BIT(2)
109917ace84SBenjamin Gaignard #define VSI_PTE_PAGE_VALID		BIT(0)
110917ace84SBenjamin Gaignard 
111917ace84SBenjamin Gaignard static inline phys_addr_t vsi_pte_page_address(u64 pte)
112917ace84SBenjamin Gaignard {
113917ace84SBenjamin Gaignard 	return ((pte << VSI_PAGE_DESC_HI_SHIFT) & VSI_PAGE_DESC_HI_MASK) |
114917ace84SBenjamin Gaignard 	       (pte & VSI_PAGE_DESC_LO_MASK);
115917ace84SBenjamin Gaignard }
116917ace84SBenjamin Gaignard 
117917ace84SBenjamin Gaignard static u32 vsi_mk_pte(phys_addr_t page, int prot)
118917ace84SBenjamin Gaignard {
119917ace84SBenjamin Gaignard 	u32 flags = 0;
120917ace84SBenjamin Gaignard 
121917ace84SBenjamin Gaignard 	flags |= (prot & IOMMU_WRITE) ? VSI_PTE_PAGE_WRITABLE : 0;
122917ace84SBenjamin Gaignard 
123917ace84SBenjamin Gaignard 	page = (page & VSI_PAGE_DESC_LO_MASK) |
124917ace84SBenjamin Gaignard 	       ((page & VSI_PAGE_DESC_HI_MASK) >> VSI_PAGE_DESC_HI_SHIFT);
125917ace84SBenjamin Gaignard 
126917ace84SBenjamin Gaignard 	return page | flags | VSI_PTE_PAGE_VALID;
127917ace84SBenjamin Gaignard }
128917ace84SBenjamin Gaignard 
129917ace84SBenjamin Gaignard #define VSI_DTE_PT_VALID	BIT(0)
130917ace84SBenjamin Gaignard 
131917ace84SBenjamin Gaignard static inline bool vsi_dte_is_pt_valid(u32 dte)
132917ace84SBenjamin Gaignard {
133917ace84SBenjamin Gaignard 	return dte & VSI_DTE_PT_VALID;
134917ace84SBenjamin Gaignard }
135917ace84SBenjamin Gaignard 
136917ace84SBenjamin Gaignard static inline bool vsi_pte_is_page_valid(u32 pte)
137917ace84SBenjamin Gaignard {
138917ace84SBenjamin Gaignard 	return pte & VSI_PTE_PAGE_VALID;
139917ace84SBenjamin Gaignard }
140917ace84SBenjamin Gaignard 
141917ace84SBenjamin Gaignard static u32 vsi_mk_pte_invalid(u32 pte)
142917ace84SBenjamin Gaignard {
143917ace84SBenjamin Gaignard 	return pte & ~VSI_PTE_PAGE_VALID;
144917ace84SBenjamin Gaignard }
145917ace84SBenjamin Gaignard 
146917ace84SBenjamin Gaignard #define VSI_MASTER_TLB_MASK	GENMASK_ULL(31, 10)
147917ace84SBenjamin Gaignard /* mode 0 : 4k */
148917ace84SBenjamin Gaignard #define VSI_PTA_4K_MODE	0
149917ace84SBenjamin Gaignard 
150917ace84SBenjamin Gaignard static u64 vsi_mk_pta(dma_addr_t dt_dma)
151917ace84SBenjamin Gaignard {
152917ace84SBenjamin Gaignard 	u64 val = (dt_dma & VSI_MASTER_TLB_MASK) | VSI_PTA_4K_MODE;
153917ace84SBenjamin Gaignard 
154917ace84SBenjamin Gaignard 	return val;
155917ace84SBenjamin Gaignard }
156917ace84SBenjamin Gaignard 
157917ace84SBenjamin Gaignard static struct vsi_iommu_domain *to_vsi_domain(struct iommu_domain *dom)
158917ace84SBenjamin Gaignard {
159917ace84SBenjamin Gaignard 	return container_of(dom, struct vsi_iommu_domain, domain);
160917ace84SBenjamin Gaignard }
161917ace84SBenjamin Gaignard 
162917ace84SBenjamin Gaignard static inline void vsi_table_flush(struct vsi_iommu_domain *vsi_domain, dma_addr_t dma,
163917ace84SBenjamin Gaignard 				   unsigned int count)
164917ace84SBenjamin Gaignard {
165917ace84SBenjamin Gaignard 	size_t size = count * sizeof(u32); /* count of u32 entry */
166917ace84SBenjamin Gaignard 
167917ace84SBenjamin Gaignard 	dma_sync_single_for_device(vsi_domain->dev, dma, size, DMA_TO_DEVICE);
168917ace84SBenjamin Gaignard }
169917ace84SBenjamin Gaignard 
170917ace84SBenjamin Gaignard #define VSI_IOVA_DTE_MASK	0xffc00000
171917ace84SBenjamin Gaignard #define VSI_IOVA_DTE_SHIFT	22
172917ace84SBenjamin Gaignard #define VSI_IOVA_PTE_MASK	0x003ff000
173917ace84SBenjamin Gaignard #define VSI_IOVA_PTE_SHIFT	12
174917ace84SBenjamin Gaignard #define VSI_IOVA_PAGE_MASK	0x00000fff
175917ace84SBenjamin Gaignard #define VSI_IOVA_PAGE_SHIFT	0
176917ace84SBenjamin Gaignard 
177917ace84SBenjamin Gaignard static u32 vsi_iova_dte_index(u32 iova)
178917ace84SBenjamin Gaignard {
179917ace84SBenjamin Gaignard 	return (iova & VSI_IOVA_DTE_MASK) >> VSI_IOVA_DTE_SHIFT;
180917ace84SBenjamin Gaignard }
181917ace84SBenjamin Gaignard 
182917ace84SBenjamin Gaignard static u32 vsi_iova_pte_index(u32 iova)
183917ace84SBenjamin Gaignard {
184917ace84SBenjamin Gaignard 	return (iova & VSI_IOVA_PTE_MASK) >> VSI_IOVA_PTE_SHIFT;
185917ace84SBenjamin Gaignard }
186917ace84SBenjamin Gaignard 
187917ace84SBenjamin Gaignard static u32 vsi_iova_page_offset(u32 iova)
188917ace84SBenjamin Gaignard {
189917ace84SBenjamin Gaignard 	return (iova & VSI_IOVA_PAGE_MASK) >> VSI_IOVA_PAGE_SHIFT;
190917ace84SBenjamin Gaignard }
191917ace84SBenjamin Gaignard 
192917ace84SBenjamin Gaignard static irqreturn_t vsi_iommu_irq(int irq, void *dev_id)
193917ace84SBenjamin Gaignard {
194917ace84SBenjamin Gaignard 	struct vsi_iommu *iommu = dev_id;
195917ace84SBenjamin Gaignard 	unsigned long flags;
196917ace84SBenjamin Gaignard 	dma_addr_t iova;
197917ace84SBenjamin Gaignard 	u32 status;
198917ace84SBenjamin Gaignard 
199917ace84SBenjamin Gaignard 	if (pm_runtime_resume_and_get(iommu->dev) < 0)
200917ace84SBenjamin Gaignard 		return IRQ_NONE;
201917ace84SBenjamin Gaignard 
202917ace84SBenjamin Gaignard 	spin_lock_irqsave(&iommu->lock, flags);
203917ace84SBenjamin Gaignard 
204917ace84SBenjamin Gaignard 	status = readl(iommu->regs + VSI_MMU_STATUS_BASE);
205917ace84SBenjamin Gaignard 	if (status & VSI_MMU_IRQ_MASK) {
206917ace84SBenjamin Gaignard 		dev_err(iommu->dev, "unexpected int_status=%08x\n", status);
207917ace84SBenjamin Gaignard 		iova = readl(iommu->regs + VSI_MMU_PAGE_FAULT_ADDR);
208917ace84SBenjamin Gaignard 		report_iommu_fault(iommu->domain, iommu->dev, iova, status);
209917ace84SBenjamin Gaignard 	}
210917ace84SBenjamin Gaignard 	writel(0, iommu->regs + VSI_MMU_STATUS_BASE);
211917ace84SBenjamin Gaignard 
212917ace84SBenjamin Gaignard 	spin_unlock_irqrestore(&iommu->lock, flags);
213917ace84SBenjamin Gaignard 	pm_runtime_put_autosuspend(iommu->dev);
214917ace84SBenjamin Gaignard 
215917ace84SBenjamin Gaignard 	return IRQ_HANDLED;
216917ace84SBenjamin Gaignard }
217917ace84SBenjamin Gaignard 
218917ace84SBenjamin Gaignard static struct vsi_iommu *vsi_iommu_get_from_dev(struct device *dev)
219917ace84SBenjamin Gaignard {
220917ace84SBenjamin Gaignard 	struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
221917ace84SBenjamin Gaignard 	struct device *iommu_dev = bus_find_device_by_fwnode(&platform_bus_type,
222917ace84SBenjamin Gaignard 							     fwspec->iommu_fwnode);
223917ace84SBenjamin Gaignard 
224917ace84SBenjamin Gaignard 	put_device(iommu_dev);
225917ace84SBenjamin Gaignard 
226917ace84SBenjamin Gaignard 	return iommu_dev ? dev_get_drvdata(iommu_dev) : NULL;
227917ace84SBenjamin Gaignard }
228917ace84SBenjamin Gaignard 
229917ace84SBenjamin Gaignard static struct iommu_domain *vsi_iommu_domain_alloc_paging(struct device *dev)
230917ace84SBenjamin Gaignard {
231917ace84SBenjamin Gaignard 	struct vsi_iommu *iommu = dev_iommu_priv_get(dev);
232917ace84SBenjamin Gaignard 	struct vsi_iommu_domain *vsi_domain;
233917ace84SBenjamin Gaignard 
234917ace84SBenjamin Gaignard 	vsi_domain = kzalloc(sizeof(*vsi_domain), GFP_KERNEL);
235917ace84SBenjamin Gaignard 	if (!vsi_domain)
236917ace84SBenjamin Gaignard 		return NULL;
237917ace84SBenjamin Gaignard 
238917ace84SBenjamin Gaignard 	vsi_domain->dev = iommu->dev;
239917ace84SBenjamin Gaignard 	spin_lock_init(&vsi_domain->lock);
240917ace84SBenjamin Gaignard 
241917ace84SBenjamin Gaignard 	/*
242917ace84SBenjamin Gaignard 	 * iommu use a 2 level pagetable.
243917ace84SBenjamin Gaignard 	 * Each level1 (dt) and level2 (pt) table has 1024 4-byte entries.
244917ace84SBenjamin Gaignard 	 * Allocate one 4 KiB page for each table.
245917ace84SBenjamin Gaignard 	 */
246917ace84SBenjamin Gaignard 	vsi_domain->dt = iommu_alloc_pages_sz(GFP_KERNEL | GFP_DMA32,
247917ace84SBenjamin Gaignard 					      SPAGE_SIZE);
248917ace84SBenjamin Gaignard 	if (!vsi_domain->dt)
249917ace84SBenjamin Gaignard 		goto err_free_domain;
250917ace84SBenjamin Gaignard 
251917ace84SBenjamin Gaignard 	vsi_domain->dt_dma = dma_map_single(vsi_domain->dev, vsi_domain->dt,
252917ace84SBenjamin Gaignard 					    SPAGE_SIZE, DMA_TO_DEVICE);
253917ace84SBenjamin Gaignard 	if (dma_mapping_error(vsi_domain->dev, vsi_domain->dt_dma)) {
254917ace84SBenjamin Gaignard 		dev_err(dev, "DMA map error for DT\n");
255917ace84SBenjamin Gaignard 		goto err_free_dt;
256917ace84SBenjamin Gaignard 	}
257917ace84SBenjamin Gaignard 
258917ace84SBenjamin Gaignard 	vsi_domain->pta = iommu_alloc_pages_sz(GFP_KERNEL | GFP_DMA32,
259917ace84SBenjamin Gaignard 					       SPAGE_SIZE);
260917ace84SBenjamin Gaignard 	if (!vsi_domain->pta)
261917ace84SBenjamin Gaignard 		goto err_unmap_dt;
262917ace84SBenjamin Gaignard 
263917ace84SBenjamin Gaignard 	vsi_domain->pta[0] = vsi_mk_pta(vsi_domain->dt_dma);
264917ace84SBenjamin Gaignard 	vsi_domain->pta_dma = dma_map_single(vsi_domain->dev, vsi_domain->pta,
265917ace84SBenjamin Gaignard 					     SPAGE_SIZE, DMA_TO_DEVICE);
266917ace84SBenjamin Gaignard 	if (dma_mapping_error(vsi_domain->dev, vsi_domain->pta_dma)) {
267917ace84SBenjamin Gaignard 		dev_err(dev, "DMA map error for PTA\n");
268917ace84SBenjamin Gaignard 		goto err_free_pta;
269917ace84SBenjamin Gaignard 	}
270917ace84SBenjamin Gaignard 
271917ace84SBenjamin Gaignard 	INIT_LIST_HEAD(&vsi_domain->iommus);
272917ace84SBenjamin Gaignard 
273917ace84SBenjamin Gaignard 	vsi_domain->domain.geometry.aperture_start = 0;
274917ace84SBenjamin Gaignard 	vsi_domain->domain.geometry.aperture_end   = DMA_BIT_MASK(32);
275917ace84SBenjamin Gaignard 	vsi_domain->domain.geometry.force_aperture = true;
276917ace84SBenjamin Gaignard 	vsi_domain->domain.pgsize_bitmap	   = SZ_4K;
277917ace84SBenjamin Gaignard 
278917ace84SBenjamin Gaignard 	return &vsi_domain->domain;
279917ace84SBenjamin Gaignard 
280917ace84SBenjamin Gaignard err_free_pta:
281917ace84SBenjamin Gaignard 	iommu_free_pages(vsi_domain->pta);
282917ace84SBenjamin Gaignard err_unmap_dt:
283917ace84SBenjamin Gaignard 	dma_unmap_single(vsi_domain->dev, vsi_domain->dt_dma,
284917ace84SBenjamin Gaignard 			 SPAGE_SIZE, DMA_TO_DEVICE);
285917ace84SBenjamin Gaignard err_free_dt:
286917ace84SBenjamin Gaignard 	iommu_free_pages(vsi_domain->dt);
287917ace84SBenjamin Gaignard err_free_domain:
288917ace84SBenjamin Gaignard 	kfree(vsi_domain);
289917ace84SBenjamin Gaignard 
290917ace84SBenjamin Gaignard 	return NULL;
291917ace84SBenjamin Gaignard }
292917ace84SBenjamin Gaignard 
293917ace84SBenjamin Gaignard static phys_addr_t vsi_iommu_iova_to_phys(struct iommu_domain *domain,
294917ace84SBenjamin Gaignard 					  dma_addr_t iova)
295917ace84SBenjamin Gaignard {
296917ace84SBenjamin Gaignard 	struct vsi_iommu_domain *vsi_domain = to_vsi_domain(domain);
297917ace84SBenjamin Gaignard 	phys_addr_t pt_phys, phys = 0;
298917ace84SBenjamin Gaignard 	unsigned long flags;
299917ace84SBenjamin Gaignard 	u32 dte, pte;
300917ace84SBenjamin Gaignard 	u32 *page_table;
301917ace84SBenjamin Gaignard 
302917ace84SBenjamin Gaignard 	spin_lock_irqsave(&vsi_domain->lock, flags);
303917ace84SBenjamin Gaignard 	dte = vsi_domain->dt[vsi_iova_dte_index(iova)];
304917ace84SBenjamin Gaignard 	if (!vsi_dte_is_pt_valid(dte))
305917ace84SBenjamin Gaignard 		goto unlock;
306917ace84SBenjamin Gaignard 
307917ace84SBenjamin Gaignard 	pt_phys = vsi_dte_pt_address(dte);
308917ace84SBenjamin Gaignard 	page_table = (u32 *)phys_to_virt(pt_phys);
309917ace84SBenjamin Gaignard 	pte = page_table[vsi_iova_pte_index(iova)];
310917ace84SBenjamin Gaignard 	if (!vsi_pte_is_page_valid(pte))
311917ace84SBenjamin Gaignard 		goto unlock;
312917ace84SBenjamin Gaignard 
313917ace84SBenjamin Gaignard 	phys = vsi_pte_page_address(pte) + vsi_iova_page_offset(iova);
314917ace84SBenjamin Gaignard 
315917ace84SBenjamin Gaignard unlock:
316917ace84SBenjamin Gaignard 	spin_unlock_irqrestore(&vsi_domain->lock, flags);
317917ace84SBenjamin Gaignard 	return phys;
318917ace84SBenjamin Gaignard }
319917ace84SBenjamin Gaignard 
320917ace84SBenjamin Gaignard static size_t vsi_iommu_unmap_iova(struct vsi_iommu_domain *vsi_domain,
321917ace84SBenjamin Gaignard 				   u32 *pte_addr, dma_addr_t pte_dma,
322917ace84SBenjamin Gaignard 				   size_t size)
323917ace84SBenjamin Gaignard {
324917ace84SBenjamin Gaignard 	unsigned int pte_count;
325917ace84SBenjamin Gaignard 	unsigned int pte_total = size / SPAGE_SIZE;
326917ace84SBenjamin Gaignard 
327917ace84SBenjamin Gaignard 	for (pte_count = 0;
328917ace84SBenjamin Gaignard 	     pte_count < pte_total && pte_count < NUM_PT_ENTRIES; pte_count++) {
329917ace84SBenjamin Gaignard 		u32 pte = pte_addr[pte_count];
330917ace84SBenjamin Gaignard 
331917ace84SBenjamin Gaignard 		if (!vsi_pte_is_page_valid(pte))
332917ace84SBenjamin Gaignard 			break;
333917ace84SBenjamin Gaignard 
334917ace84SBenjamin Gaignard 		pte_addr[pte_count] = vsi_mk_pte_invalid(pte);
335917ace84SBenjamin Gaignard 	}
336917ace84SBenjamin Gaignard 
337917ace84SBenjamin Gaignard 	vsi_table_flush(vsi_domain, pte_dma, pte_total);
338917ace84SBenjamin Gaignard 
339917ace84SBenjamin Gaignard 	return pte_count * SPAGE_SIZE;
340917ace84SBenjamin Gaignard }
341917ace84SBenjamin Gaignard 
342917ace84SBenjamin Gaignard static int vsi_iommu_map_iova(struct vsi_iommu_domain *vsi_domain, u32 *pte_addr,
343917ace84SBenjamin Gaignard 			      dma_addr_t pte_dma, dma_addr_t iova,
344917ace84SBenjamin Gaignard 			      phys_addr_t paddr, size_t size, int prot)
345917ace84SBenjamin Gaignard {
346917ace84SBenjamin Gaignard 	unsigned int pte_count;
347917ace84SBenjamin Gaignard 	unsigned int pte_total = size / SPAGE_SIZE;
348917ace84SBenjamin Gaignard 
349917ace84SBenjamin Gaignard 	for (pte_count = 0;
350917ace84SBenjamin Gaignard 	     pte_count < pte_total && pte_count < NUM_PT_ENTRIES; pte_count++) {
351917ace84SBenjamin Gaignard 		u32 pte = pte_addr[pte_count];
352917ace84SBenjamin Gaignard 
353917ace84SBenjamin Gaignard 		if (vsi_pte_is_page_valid(pte))
354917ace84SBenjamin Gaignard 			return (pte_count - 1) * SPAGE_SIZE;
355917ace84SBenjamin Gaignard 
356917ace84SBenjamin Gaignard 		pte_addr[pte_count] = vsi_mk_pte(paddr, prot);
357917ace84SBenjamin Gaignard 
358917ace84SBenjamin Gaignard 		paddr += SPAGE_SIZE;
359917ace84SBenjamin Gaignard 	}
360917ace84SBenjamin Gaignard 
361917ace84SBenjamin Gaignard 	vsi_table_flush(vsi_domain, pte_dma, pte_total);
362917ace84SBenjamin Gaignard 
363917ace84SBenjamin Gaignard 	return 0;
364917ace84SBenjamin Gaignard }
365917ace84SBenjamin Gaignard 
366917ace84SBenjamin Gaignard static void vsi_iommu_flush_tlb(struct iommu_domain *domain)
367917ace84SBenjamin Gaignard {
368917ace84SBenjamin Gaignard 	struct vsi_iommu_domain *vsi_domain = to_vsi_domain(domain);
369917ace84SBenjamin Gaignard 	struct vsi_iommu *iommu;
370917ace84SBenjamin Gaignard 
371*3040784fSDan Carpenter 	list_for_each_entry(iommu, &vsi_domain->iommus, node) {
372917ace84SBenjamin Gaignard 		if (pm_runtime_get(iommu->dev) < 0)
373917ace84SBenjamin Gaignard 			continue;
374917ace84SBenjamin Gaignard 
375917ace84SBenjamin Gaignard 		spin_lock(&iommu->lock);
376917ace84SBenjamin Gaignard 
377917ace84SBenjamin Gaignard 		if (iommu->enable) {
378917ace84SBenjamin Gaignard 			writel(VSI_MMU_BIT_FLUSH, iommu->regs + VSI_MMU_FLUSH_BASE);
379917ace84SBenjamin Gaignard 			writel(0, iommu->regs + VSI_MMU_FLUSH_BASE);
380917ace84SBenjamin Gaignard 		}
381917ace84SBenjamin Gaignard 
382917ace84SBenjamin Gaignard 		spin_unlock(&iommu->lock);
383917ace84SBenjamin Gaignard 
384917ace84SBenjamin Gaignard 		pm_runtime_put_autosuspend(iommu->dev);
385917ace84SBenjamin Gaignard 	}
386917ace84SBenjamin Gaignard }
387917ace84SBenjamin Gaignard 
388917ace84SBenjamin Gaignard static size_t vsi_iommu_unmap(struct iommu_domain *domain, unsigned long _iova,
389917ace84SBenjamin Gaignard 			      size_t size, size_t count, struct iommu_iotlb_gather *gather)
390917ace84SBenjamin Gaignard {
391917ace84SBenjamin Gaignard 	struct vsi_iommu_domain *vsi_domain = to_vsi_domain(domain);
392917ace84SBenjamin Gaignard 	dma_addr_t pte_dma, iova = (dma_addr_t)_iova;
393917ace84SBenjamin Gaignard 	unsigned long flags;
394917ace84SBenjamin Gaignard 	phys_addr_t pt_phys;
395917ace84SBenjamin Gaignard 	u32 dte;
396917ace84SBenjamin Gaignard 	u32 *pte_addr;
397917ace84SBenjamin Gaignard 	size_t unmap_size = 0;
398917ace84SBenjamin Gaignard 
399917ace84SBenjamin Gaignard 	spin_lock_irqsave(&vsi_domain->lock, flags);
400917ace84SBenjamin Gaignard 
401917ace84SBenjamin Gaignard 	dte = vsi_domain->dt[vsi_iova_dte_index(iova)];
402917ace84SBenjamin Gaignard 	/* Just return 0 if iova is unmapped */
403917ace84SBenjamin Gaignard 	if (!vsi_dte_is_pt_valid(dte))
404917ace84SBenjamin Gaignard 		goto unlock;
405917ace84SBenjamin Gaignard 
406917ace84SBenjamin Gaignard 	pt_phys = vsi_dte_pt_address(dte);
407917ace84SBenjamin Gaignard 	pte_addr = (u32 *)phys_to_virt(pt_phys) + vsi_iova_pte_index(iova);
408917ace84SBenjamin Gaignard 	pte_dma = pt_phys + vsi_iova_pte_index(iova) * sizeof(u32);
409917ace84SBenjamin Gaignard 	unmap_size = vsi_iommu_unmap_iova(vsi_domain, pte_addr, pte_dma, size);
410917ace84SBenjamin Gaignard 	if (!unmap_size)
411917ace84SBenjamin Gaignard 		goto unlock;
412917ace84SBenjamin Gaignard 
413917ace84SBenjamin Gaignard 	vsi_iommu_flush_tlb(domain);
414917ace84SBenjamin Gaignard unlock:
415917ace84SBenjamin Gaignard 	spin_unlock_irqrestore(&vsi_domain->lock, flags);
416917ace84SBenjamin Gaignard 
417917ace84SBenjamin Gaignard 	return unmap_size;
418917ace84SBenjamin Gaignard }
419917ace84SBenjamin Gaignard 
420917ace84SBenjamin Gaignard static u32 *vsi_dte_get_page_table(struct vsi_iommu_domain *vsi_domain,
421917ace84SBenjamin Gaignard 				   dma_addr_t iova, gfp_t gfp)
422917ace84SBenjamin Gaignard {
423917ace84SBenjamin Gaignard 	u32 *page_table, *dte_addr;
424917ace84SBenjamin Gaignard 	u32 dte_index, dte;
425917ace84SBenjamin Gaignard 	phys_addr_t pt_phys;
426917ace84SBenjamin Gaignard 	dma_addr_t pt_dma;
427917ace84SBenjamin Gaignard 	gfp_t flags;
428917ace84SBenjamin Gaignard 
429917ace84SBenjamin Gaignard 	dte_index = vsi_iova_dte_index(iova);
430917ace84SBenjamin Gaignard 	dte_addr = &vsi_domain->dt[dte_index];
431917ace84SBenjamin Gaignard 	dte = *dte_addr;
432917ace84SBenjamin Gaignard 	if (vsi_dte_is_pt_valid(dte))
433917ace84SBenjamin Gaignard 		goto done;
434917ace84SBenjamin Gaignard 
435917ace84SBenjamin Gaignard 	/* Do not allow to sleep while allocating the buffer */
436917ace84SBenjamin Gaignard 	flags = (gfp & ~GFP_KERNEL) | GFP_ATOMIC | GFP_DMA32;
437917ace84SBenjamin Gaignard 	page_table = iommu_alloc_pages_sz(flags, PAGE_SIZE);
438917ace84SBenjamin Gaignard 	if (!page_table)
439917ace84SBenjamin Gaignard 		return ERR_PTR(-ENOMEM);
440917ace84SBenjamin Gaignard 
441917ace84SBenjamin Gaignard 	pt_dma = dma_map_single(vsi_domain->dev, page_table, PAGE_SIZE, DMA_TO_DEVICE);
442917ace84SBenjamin Gaignard 	if (dma_mapping_error(vsi_domain->dev, pt_dma)) {
443917ace84SBenjamin Gaignard 		dev_err(vsi_domain->dev, "DMA mapping error while allocating page table\n");
444917ace84SBenjamin Gaignard 		iommu_free_pages(page_table);
445917ace84SBenjamin Gaignard 		return ERR_PTR(-ENOMEM);
446917ace84SBenjamin Gaignard 	}
447917ace84SBenjamin Gaignard 
448917ace84SBenjamin Gaignard 	dte = vsi_mk_dte(pt_dma);
449917ace84SBenjamin Gaignard 	*dte_addr = dte;
450917ace84SBenjamin Gaignard 
451917ace84SBenjamin Gaignard 	vsi_table_flush(vsi_domain,
452917ace84SBenjamin Gaignard 			vsi_domain->dt_dma + dte_index * sizeof(u32), 1);
453917ace84SBenjamin Gaignard done:
454917ace84SBenjamin Gaignard 	pt_phys = vsi_dte_pt_address(dte);
455917ace84SBenjamin Gaignard 	return (u32 *)phys_to_virt(pt_phys);
456917ace84SBenjamin Gaignard }
457917ace84SBenjamin Gaignard 
458917ace84SBenjamin Gaignard static int vsi_iommu_map(struct iommu_domain *domain, unsigned long _iova,
459917ace84SBenjamin Gaignard 			 phys_addr_t paddr, size_t size, size_t count,
460917ace84SBenjamin Gaignard 			 int prot, gfp_t gfp, size_t *mapped)
461917ace84SBenjamin Gaignard {
462917ace84SBenjamin Gaignard 	struct vsi_iommu_domain *vsi_domain = to_vsi_domain(domain);
463917ace84SBenjamin Gaignard 	dma_addr_t pte_dma, iova = (dma_addr_t)_iova;
464917ace84SBenjamin Gaignard 	u32 *page_table, *pte_addr;
465917ace84SBenjamin Gaignard 	u32 dte, pte_index;
466917ace84SBenjamin Gaignard 	unsigned long flags;
467917ace84SBenjamin Gaignard 	int ret;
468917ace84SBenjamin Gaignard 
469917ace84SBenjamin Gaignard 	spin_lock_irqsave(&vsi_domain->lock, flags);
470917ace84SBenjamin Gaignard 
471917ace84SBenjamin Gaignard 	page_table = vsi_dte_get_page_table(vsi_domain, iova, gfp);
472917ace84SBenjamin Gaignard 	if (IS_ERR(page_table)) {
473917ace84SBenjamin Gaignard 		spin_unlock_irqrestore(&vsi_domain->lock, flags);
474917ace84SBenjamin Gaignard 		return PTR_ERR(page_table);
475917ace84SBenjamin Gaignard 	}
476917ace84SBenjamin Gaignard 
477917ace84SBenjamin Gaignard 	dte = vsi_domain->dt[vsi_iova_dte_index(iova)];
478917ace84SBenjamin Gaignard 	pte_index = vsi_iova_pte_index(iova);
479917ace84SBenjamin Gaignard 	pte_addr = &page_table[pte_index];
480917ace84SBenjamin Gaignard 	pte_dma = vsi_dte_pt_address(dte) + pte_index * sizeof(u32);
481917ace84SBenjamin Gaignard 	ret = vsi_iommu_map_iova(vsi_domain, pte_addr, pte_dma, iova,
482917ace84SBenjamin Gaignard 				 paddr, size, prot);
483917ace84SBenjamin Gaignard 	if (!ret)
484917ace84SBenjamin Gaignard 		*mapped = size;
485917ace84SBenjamin Gaignard 
486917ace84SBenjamin Gaignard 	vsi_iommu_flush_tlb(domain);
487917ace84SBenjamin Gaignard 
488917ace84SBenjamin Gaignard 	spin_unlock_irqrestore(&vsi_domain->lock, flags);
489917ace84SBenjamin Gaignard 
490917ace84SBenjamin Gaignard 	return ret;
491917ace84SBenjamin Gaignard }
492917ace84SBenjamin Gaignard 
493917ace84SBenjamin Gaignard static void vsi_iommu_disable(struct vsi_iommu *iommu)
494917ace84SBenjamin Gaignard {
495917ace84SBenjamin Gaignard 	writel(0, iommu->regs + VSI_MMU_AHB_CONTROL_BASE);
496917ace84SBenjamin Gaignard 	iommu->enable = false;
497917ace84SBenjamin Gaignard }
498917ace84SBenjamin Gaignard 
499917ace84SBenjamin Gaignard static int vsi_iommu_identity_attach(struct iommu_domain *domain,
500917ace84SBenjamin Gaignard 				     struct device *dev, struct iommu_domain *old)
501917ace84SBenjamin Gaignard {
502917ace84SBenjamin Gaignard 	struct vsi_iommu *iommu = dev_iommu_priv_get(dev);
503917ace84SBenjamin Gaignard 	struct vsi_iommu_domain *vsi_domain = to_vsi_domain(domain);
504917ace84SBenjamin Gaignard 	unsigned long flags;
505917ace84SBenjamin Gaignard 	int ret;
506917ace84SBenjamin Gaignard 
507917ace84SBenjamin Gaignard 	ret = pm_runtime_resume_and_get(iommu->dev);
508917ace84SBenjamin Gaignard 	if (ret < 0)
509917ace84SBenjamin Gaignard 		return ret;
510917ace84SBenjamin Gaignard 
511917ace84SBenjamin Gaignard 	spin_lock_irqsave(&vsi_domain->lock, flags);
512917ace84SBenjamin Gaignard 	spin_lock(&iommu->lock);
513917ace84SBenjamin Gaignard 	if (iommu->domain == domain)
514917ace84SBenjamin Gaignard 		goto unlock;
515917ace84SBenjamin Gaignard 
516917ace84SBenjamin Gaignard 	vsi_iommu_disable(iommu);
517917ace84SBenjamin Gaignard 	list_del_init(&iommu->node);
518917ace84SBenjamin Gaignard 
519917ace84SBenjamin Gaignard 	iommu->domain = domain;
520917ace84SBenjamin Gaignard 
521917ace84SBenjamin Gaignard unlock:
522917ace84SBenjamin Gaignard 	spin_unlock(&iommu->lock);
523917ace84SBenjamin Gaignard 	spin_unlock_irqrestore(&vsi_domain->lock, flags);
524917ace84SBenjamin Gaignard 	pm_runtime_put_autosuspend(iommu->dev);
525917ace84SBenjamin Gaignard 	return 0;
526917ace84SBenjamin Gaignard }
527917ace84SBenjamin Gaignard 
528917ace84SBenjamin Gaignard static const struct iommu_domain_ops vsi_identity_ops = {
529917ace84SBenjamin Gaignard 	.attach_dev = vsi_iommu_identity_attach,
530917ace84SBenjamin Gaignard };
531917ace84SBenjamin Gaignard 
532917ace84SBenjamin Gaignard static struct iommu_domain vsi_identity_domain = {
533917ace84SBenjamin Gaignard 	.type = IOMMU_DOMAIN_IDENTITY,
534917ace84SBenjamin Gaignard 	.ops = &vsi_identity_ops,
535917ace84SBenjamin Gaignard };
536917ace84SBenjamin Gaignard 
537917ace84SBenjamin Gaignard static void vsi_iommu_enable(struct vsi_iommu *iommu, struct iommu_domain *domain)
538917ace84SBenjamin Gaignard {
539917ace84SBenjamin Gaignard 	struct vsi_iommu_domain *vsi_domain = to_vsi_domain(domain);
540917ace84SBenjamin Gaignard 
541917ace84SBenjamin Gaignard 	if (domain == &vsi_identity_domain)
542917ace84SBenjamin Gaignard 		return;
543917ace84SBenjamin Gaignard 
544917ace84SBenjamin Gaignard 	writel(vsi_domain->pta_dma, iommu->regs + VSI_MMU_AHB_TLB_ARRAY_BASE_L_BASE);
545917ace84SBenjamin Gaignard 	writel(VSI_MMU_OUT_OF_BOUND, iommu->regs + VSI_MMU_CONFIG1_BASE);
546917ace84SBenjamin Gaignard 	writel(VSI_MMU_BIT_ENABLE, iommu->regs + VSI_MMU_AHB_EXCEPTION_BASE);
547917ace84SBenjamin Gaignard 	writel(VSI_MMU_BIT_ENABLE, iommu->regs + VSI_MMU_AHB_CONTROL_BASE);
548917ace84SBenjamin Gaignard 	iommu->enable = true;
549917ace84SBenjamin Gaignard }
550917ace84SBenjamin Gaignard 
551917ace84SBenjamin Gaignard static int vsi_iommu_attach_device(struct iommu_domain *domain,
552917ace84SBenjamin Gaignard 				   struct device *dev, struct iommu_domain *old)
553917ace84SBenjamin Gaignard {
554917ace84SBenjamin Gaignard 	struct vsi_iommu *iommu = dev_iommu_priv_get(dev);
555917ace84SBenjamin Gaignard 	struct vsi_iommu_domain *vsi_domain = to_vsi_domain(domain);
556917ace84SBenjamin Gaignard 	unsigned long flags;
557917ace84SBenjamin Gaignard 	int ret = 0;
558917ace84SBenjamin Gaignard 
559917ace84SBenjamin Gaignard 	ret = pm_runtime_resume_and_get(iommu->dev);
560917ace84SBenjamin Gaignard 	if (ret < 0)
561917ace84SBenjamin Gaignard 		return ret;
562917ace84SBenjamin Gaignard 
563917ace84SBenjamin Gaignard 	spin_lock_irqsave(&vsi_domain->lock, flags);
564917ace84SBenjamin Gaignard 	spin_lock(&iommu->lock);
565917ace84SBenjamin Gaignard 
566917ace84SBenjamin Gaignard 	vsi_iommu_enable(iommu, domain);
567917ace84SBenjamin Gaignard 	writel(VSI_MMU_BIT_FLUSH, iommu->regs + VSI_MMU_FLUSH_BASE);
568917ace84SBenjamin Gaignard 	writel(0, iommu->regs + VSI_MMU_FLUSH_BASE);
569917ace84SBenjamin Gaignard 
570917ace84SBenjamin Gaignard 	list_del_init(&iommu->node);
571917ace84SBenjamin Gaignard 	list_add_tail(&iommu->node, &vsi_domain->iommus);
572917ace84SBenjamin Gaignard 
573917ace84SBenjamin Gaignard 	iommu->domain = domain;
574917ace84SBenjamin Gaignard 
575917ace84SBenjamin Gaignard 	spin_unlock(&iommu->lock);
576917ace84SBenjamin Gaignard 	spin_unlock_irqrestore(&vsi_domain->lock, flags);
577917ace84SBenjamin Gaignard 	pm_runtime_put_autosuspend(iommu->dev);
578917ace84SBenjamin Gaignard 	return ret;
579917ace84SBenjamin Gaignard }
580917ace84SBenjamin Gaignard 
581917ace84SBenjamin Gaignard static void vsi_iommu_domain_free(struct iommu_domain *domain)
582917ace84SBenjamin Gaignard {
583917ace84SBenjamin Gaignard 	struct vsi_iommu_domain *vsi_domain = to_vsi_domain(domain);
584917ace84SBenjamin Gaignard 	unsigned long flags;
585917ace84SBenjamin Gaignard 	int i;
586917ace84SBenjamin Gaignard 
587917ace84SBenjamin Gaignard 	spin_lock_irqsave(&vsi_domain->lock, flags);
588917ace84SBenjamin Gaignard 
589917ace84SBenjamin Gaignard 	WARN_ON(!list_empty(&vsi_domain->iommus));
590917ace84SBenjamin Gaignard 
591917ace84SBenjamin Gaignard 	for (i = 0; i < NUM_DT_ENTRIES; i++) {
592917ace84SBenjamin Gaignard 		u32 dte = vsi_domain->dt[i];
593917ace84SBenjamin Gaignard 
594917ace84SBenjamin Gaignard 		if (vsi_dte_is_pt_valid(dte)) {
595917ace84SBenjamin Gaignard 			phys_addr_t pt_phys = vsi_dte_pt_address(dte);
596917ace84SBenjamin Gaignard 			u32 *page_table = phys_to_virt(pt_phys);
597917ace84SBenjamin Gaignard 
598917ace84SBenjamin Gaignard 			dma_unmap_single(vsi_domain->dev, pt_phys,
599917ace84SBenjamin Gaignard 					 SPAGE_SIZE, DMA_TO_DEVICE);
600917ace84SBenjamin Gaignard 			iommu_free_pages(page_table);
601917ace84SBenjamin Gaignard 		}
602917ace84SBenjamin Gaignard 	}
603917ace84SBenjamin Gaignard 
604917ace84SBenjamin Gaignard 	dma_unmap_single(vsi_domain->dev, vsi_domain->dt_dma,
605917ace84SBenjamin Gaignard 			 SPAGE_SIZE, DMA_TO_DEVICE);
606917ace84SBenjamin Gaignard 	iommu_free_pages(vsi_domain->dt);
607917ace84SBenjamin Gaignard 
608917ace84SBenjamin Gaignard 	dma_unmap_single(vsi_domain->dev, vsi_domain->pta_dma,
609917ace84SBenjamin Gaignard 			 SPAGE_SIZE, DMA_TO_DEVICE);
610917ace84SBenjamin Gaignard 	iommu_free_pages(vsi_domain->pta);
611917ace84SBenjamin Gaignard 
612917ace84SBenjamin Gaignard 	spin_unlock_irqrestore(&vsi_domain->lock, flags);
613917ace84SBenjamin Gaignard 
614917ace84SBenjamin Gaignard 	kfree(vsi_domain);
615917ace84SBenjamin Gaignard }
616917ace84SBenjamin Gaignard 
617917ace84SBenjamin Gaignard static struct iommu_device *vsi_iommu_probe_device(struct device *dev)
618917ace84SBenjamin Gaignard {
619917ace84SBenjamin Gaignard 	struct vsi_iommu *iommu = vsi_iommu_get_from_dev(dev);
620917ace84SBenjamin Gaignard 	struct device_link *link;
621917ace84SBenjamin Gaignard 
622917ace84SBenjamin Gaignard 	link = device_link_add(dev, iommu->dev,
623917ace84SBenjamin Gaignard 			       DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME);
624917ace84SBenjamin Gaignard 	if (!link)
625917ace84SBenjamin Gaignard 		dev_err(dev, "Unable to link %s\n", dev_name(iommu->dev));
626917ace84SBenjamin Gaignard 
627917ace84SBenjamin Gaignard 	dev_iommu_priv_set(dev, iommu);
628917ace84SBenjamin Gaignard 	return &iommu->iommu;
629917ace84SBenjamin Gaignard }
630917ace84SBenjamin Gaignard 
631917ace84SBenjamin Gaignard static void vsi_iommu_release_device(struct device *dev)
632917ace84SBenjamin Gaignard {
633917ace84SBenjamin Gaignard 	struct vsi_iommu *iommu = dev_iommu_priv_get(dev);
634917ace84SBenjamin Gaignard 
635917ace84SBenjamin Gaignard 	device_link_remove(dev, iommu->dev);
636917ace84SBenjamin Gaignard }
637917ace84SBenjamin Gaignard 
638917ace84SBenjamin Gaignard static int vsi_iommu_of_xlate(struct device *dev, const struct of_phandle_args *args)
639917ace84SBenjamin Gaignard {
640917ace84SBenjamin Gaignard 	return iommu_fwspec_add_ids(dev, args->args, 1);
641917ace84SBenjamin Gaignard }
642917ace84SBenjamin Gaignard 
643917ace84SBenjamin Gaignard static const struct iommu_ops vsi_iommu_ops = {
644917ace84SBenjamin Gaignard 	.identity_domain = &vsi_identity_domain,
645917ace84SBenjamin Gaignard 	.release_domain = &vsi_identity_domain,
646917ace84SBenjamin Gaignard 	.domain_alloc_paging = vsi_iommu_domain_alloc_paging,
647917ace84SBenjamin Gaignard 	.of_xlate = vsi_iommu_of_xlate,
648917ace84SBenjamin Gaignard 	.probe_device = vsi_iommu_probe_device,
649917ace84SBenjamin Gaignard 	.release_device = vsi_iommu_release_device,
650917ace84SBenjamin Gaignard 	.device_group = generic_single_device_group,
651917ace84SBenjamin Gaignard 	.owner = THIS_MODULE,
652917ace84SBenjamin Gaignard 	.default_domain_ops = &(const struct iommu_domain_ops) {
653917ace84SBenjamin Gaignard 		.attach_dev		= vsi_iommu_attach_device,
654917ace84SBenjamin Gaignard 		.map_pages		= vsi_iommu_map,
655917ace84SBenjamin Gaignard 		.unmap_pages		= vsi_iommu_unmap,
656917ace84SBenjamin Gaignard 		.iova_to_phys		= vsi_iommu_iova_to_phys,
657917ace84SBenjamin Gaignard 		.free			= vsi_iommu_domain_free,
658917ace84SBenjamin Gaignard 	}
659917ace84SBenjamin Gaignard };
660917ace84SBenjamin Gaignard 
661917ace84SBenjamin Gaignard static const struct of_device_id vsi_iommu_dt_ids[] = {
662917ace84SBenjamin Gaignard 	{
663917ace84SBenjamin Gaignard 		.compatible = "verisilicon,iommu-1.2",
664917ace84SBenjamin Gaignard 	},
665917ace84SBenjamin Gaignard 	{ /* sentinel */ }
666917ace84SBenjamin Gaignard };
667917ace84SBenjamin Gaignard MODULE_DEVICE_TABLE(of, vsi_iommu_dt_ids);
668917ace84SBenjamin Gaignard 
669917ace84SBenjamin Gaignard static int vsi_iommu_probe(struct platform_device *pdev)
670917ace84SBenjamin Gaignard {
671917ace84SBenjamin Gaignard 	struct device *dev = &pdev->dev;
672917ace84SBenjamin Gaignard 	struct vsi_iommu *iommu;
673917ace84SBenjamin Gaignard 	int err;
674917ace84SBenjamin Gaignard 
675917ace84SBenjamin Gaignard 	iommu = devm_kzalloc(dev, sizeof(*iommu), GFP_KERNEL);
676917ace84SBenjamin Gaignard 	if (!iommu)
677917ace84SBenjamin Gaignard 		return -ENOMEM;
678917ace84SBenjamin Gaignard 
679917ace84SBenjamin Gaignard 	iommu->dev = dev;
680917ace84SBenjamin Gaignard 	spin_lock_init(&iommu->lock);
681917ace84SBenjamin Gaignard 	INIT_LIST_HEAD(&iommu->node);
682917ace84SBenjamin Gaignard 
683917ace84SBenjamin Gaignard 	iommu->regs = devm_platform_ioremap_resource(pdev, 0);
684917ace84SBenjamin Gaignard 	if (IS_ERR(iommu->regs))
685917ace84SBenjamin Gaignard 		return -ENOMEM;
686917ace84SBenjamin Gaignard 
687917ace84SBenjamin Gaignard 	iommu->num_clocks = devm_clk_bulk_get_all(dev, &iommu->clocks);
688917ace84SBenjamin Gaignard 	if  (iommu->num_clocks < 0)
689917ace84SBenjamin Gaignard 		return iommu->num_clocks;
690917ace84SBenjamin Gaignard 
691917ace84SBenjamin Gaignard 	err = clk_bulk_prepare(iommu->num_clocks, iommu->clocks);
692917ace84SBenjamin Gaignard 	if (err)
693917ace84SBenjamin Gaignard 		return err;
694917ace84SBenjamin Gaignard 
695917ace84SBenjamin Gaignard 	iommu->irq = platform_get_irq(pdev, 0);
696917ace84SBenjamin Gaignard 	if (iommu->irq < 0)
697917ace84SBenjamin Gaignard 		return iommu->irq;
698917ace84SBenjamin Gaignard 
699917ace84SBenjamin Gaignard 	err = devm_request_irq(iommu->dev, iommu->irq, vsi_iommu_irq,
700917ace84SBenjamin Gaignard 			       IRQF_SHARED, dev_name(dev), iommu);
701917ace84SBenjamin Gaignard 	if (err)
702917ace84SBenjamin Gaignard 		goto err_unprepare_clocks;
703917ace84SBenjamin Gaignard 
704917ace84SBenjamin Gaignard 	dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
705917ace84SBenjamin Gaignard 	platform_set_drvdata(pdev, iommu);
706917ace84SBenjamin Gaignard 
707917ace84SBenjamin Gaignard 	pm_runtime_set_autosuspend_delay(dev, 100);
708917ace84SBenjamin Gaignard 	pm_runtime_use_autosuspend(dev);
709917ace84SBenjamin Gaignard 	pm_runtime_enable(dev);
710917ace84SBenjamin Gaignard 
71180b0d354SArnd Bergmann 	err = iommu_device_sysfs_add(&iommu->iommu, dev, NULL, "%s",
71280b0d354SArnd Bergmann 				     dev_name(dev));
713917ace84SBenjamin Gaignard 	if (err)
714917ace84SBenjamin Gaignard 		goto err_runtime_disable;
715917ace84SBenjamin Gaignard 
716917ace84SBenjamin Gaignard 	err = iommu_device_register(&iommu->iommu, &vsi_iommu_ops, dev);
717917ace84SBenjamin Gaignard 	if (err)
718917ace84SBenjamin Gaignard 		goto err_remove_sysfs;
719917ace84SBenjamin Gaignard 
720917ace84SBenjamin Gaignard 	return 0;
721917ace84SBenjamin Gaignard 
722917ace84SBenjamin Gaignard err_remove_sysfs:
723917ace84SBenjamin Gaignard 	iommu_device_sysfs_remove(&iommu->iommu);
724917ace84SBenjamin Gaignard err_runtime_disable:
725917ace84SBenjamin Gaignard 	pm_runtime_disable(dev);
726917ace84SBenjamin Gaignard err_unprepare_clocks:
727917ace84SBenjamin Gaignard 	clk_bulk_unprepare(iommu->num_clocks, iommu->clocks);
728917ace84SBenjamin Gaignard 	return err;
729917ace84SBenjamin Gaignard }
730917ace84SBenjamin Gaignard 
731917ace84SBenjamin Gaignard static void vsi_iommu_shutdown(struct platform_device *pdev)
732917ace84SBenjamin Gaignard {
733917ace84SBenjamin Gaignard 	struct vsi_iommu *iommu = platform_get_drvdata(pdev);
734917ace84SBenjamin Gaignard 
735917ace84SBenjamin Gaignard 	disable_irq(iommu->irq);
736917ace84SBenjamin Gaignard 	pm_runtime_force_suspend(&pdev->dev);
737917ace84SBenjamin Gaignard }
738917ace84SBenjamin Gaignard 
739917ace84SBenjamin Gaignard static int __maybe_unused vsi_iommu_suspend(struct device *dev)
740917ace84SBenjamin Gaignard {
741917ace84SBenjamin Gaignard 	struct vsi_iommu *iommu = dev_get_drvdata(dev);
742917ace84SBenjamin Gaignard 
743917ace84SBenjamin Gaignard 	vsi_iommu_disable(iommu);
744917ace84SBenjamin Gaignard 
745917ace84SBenjamin Gaignard 	clk_bulk_disable(iommu->num_clocks, iommu->clocks);
746917ace84SBenjamin Gaignard 
747917ace84SBenjamin Gaignard 	return 0;
748917ace84SBenjamin Gaignard }
749917ace84SBenjamin Gaignard 
750917ace84SBenjamin Gaignard static int __maybe_unused vsi_iommu_resume(struct device *dev)
751917ace84SBenjamin Gaignard {
752917ace84SBenjamin Gaignard 	struct vsi_iommu *iommu = dev_get_drvdata(dev);
753917ace84SBenjamin Gaignard 	unsigned long flags;
754917ace84SBenjamin Gaignard 	int ret;
755917ace84SBenjamin Gaignard 
756917ace84SBenjamin Gaignard 	ret = clk_bulk_enable(iommu->num_clocks, iommu->clocks);
757917ace84SBenjamin Gaignard 	if (ret)
758917ace84SBenjamin Gaignard 		return ret;
759917ace84SBenjamin Gaignard 
760917ace84SBenjamin Gaignard 	if (iommu->domain) {
761917ace84SBenjamin Gaignard 		struct vsi_iommu_domain *vsi_domain = to_vsi_domain(iommu->domain);
762917ace84SBenjamin Gaignard 
763917ace84SBenjamin Gaignard 		spin_lock_irqsave(&vsi_domain->lock, flags);
764917ace84SBenjamin Gaignard 		spin_lock(&iommu->lock);
765917ace84SBenjamin Gaignard 		vsi_iommu_enable(iommu, iommu->domain);
766917ace84SBenjamin Gaignard 		spin_unlock(&iommu->lock);
767917ace84SBenjamin Gaignard 		spin_unlock_irqrestore(&vsi_domain->lock, flags);
768917ace84SBenjamin Gaignard 	}
769917ace84SBenjamin Gaignard 
770917ace84SBenjamin Gaignard 	return 0;
771917ace84SBenjamin Gaignard }
772917ace84SBenjamin Gaignard 
773917ace84SBenjamin Gaignard static DEFINE_RUNTIME_DEV_PM_OPS(vsi_iommu_pm_ops,
774917ace84SBenjamin Gaignard 				 vsi_iommu_suspend, vsi_iommu_resume,
775917ace84SBenjamin Gaignard 				 NULL);
776917ace84SBenjamin Gaignard 
777917ace84SBenjamin Gaignard static struct platform_driver rockchip_vsi_iommu_driver = {
778917ace84SBenjamin Gaignard 	.probe = vsi_iommu_probe,
779917ace84SBenjamin Gaignard 	.shutdown = vsi_iommu_shutdown,
780917ace84SBenjamin Gaignard 	.driver = {
781917ace84SBenjamin Gaignard 		   .name = "vsi_iommu",
782917ace84SBenjamin Gaignard 		   .of_match_table = vsi_iommu_dt_ids,
783917ace84SBenjamin Gaignard 		   .pm = pm_sleep_ptr(&vsi_iommu_pm_ops),
784917ace84SBenjamin Gaignard 		   .suppress_bind_attrs = true,
785917ace84SBenjamin Gaignard 	},
786917ace84SBenjamin Gaignard };
787917ace84SBenjamin Gaignard module_platform_driver(rockchip_vsi_iommu_driver);
788917ace84SBenjamin Gaignard 
789917ace84SBenjamin Gaignard MODULE_LICENSE("GPL");
790917ace84SBenjamin Gaignard MODULE_AUTHOR("Benjamin Gaignard <benjamin.gaignard@collabora.com>");
791917ace84SBenjamin Gaignard MODULE_DESCRIPTION("Verisilicon IOMMU driver");
792