xref: /openbmc/linux/drivers/accel/ivpu/ivpu_mmu_context.c (revision 263b2ba5fc93c875129e0d2b4034d7d8a34b3d39)
1*263b2ba5SJacek Lawrynowicz // SPDX-License-Identifier: GPL-2.0-only
2*263b2ba5SJacek Lawrynowicz /*
3*263b2ba5SJacek Lawrynowicz  * Copyright (C) 2020-2023 Intel Corporation
4*263b2ba5SJacek Lawrynowicz  */
5*263b2ba5SJacek Lawrynowicz 
6*263b2ba5SJacek Lawrynowicz #include <linux/bitfield.h>
7*263b2ba5SJacek Lawrynowicz #include <linux/highmem.h>
8*263b2ba5SJacek Lawrynowicz 
9*263b2ba5SJacek Lawrynowicz #include "ivpu_drv.h"
10*263b2ba5SJacek Lawrynowicz #include "ivpu_hw.h"
11*263b2ba5SJacek Lawrynowicz #include "ivpu_mmu.h"
12*263b2ba5SJacek Lawrynowicz #include "ivpu_mmu_context.h"
13*263b2ba5SJacek Lawrynowicz 
14*263b2ba5SJacek Lawrynowicz #define IVPU_MMU_PGD_INDEX_MASK          GENMASK(38, 30)
15*263b2ba5SJacek Lawrynowicz #define IVPU_MMU_PMD_INDEX_MASK          GENMASK(29, 21)
16*263b2ba5SJacek Lawrynowicz #define IVPU_MMU_PTE_INDEX_MASK          GENMASK(20, 12)
17*263b2ba5SJacek Lawrynowicz #define IVPU_MMU_ENTRY_FLAGS_MASK        GENMASK(11, 0)
18*263b2ba5SJacek Lawrynowicz #define IVPU_MMU_ENTRY_FLAG_NG           BIT(11)
19*263b2ba5SJacek Lawrynowicz #define IVPU_MMU_ENTRY_FLAG_AF           BIT(10)
20*263b2ba5SJacek Lawrynowicz #define IVPU_MMU_ENTRY_FLAG_USER         BIT(6)
21*263b2ba5SJacek Lawrynowicz #define IVPU_MMU_ENTRY_FLAG_LLC_COHERENT BIT(2)
22*263b2ba5SJacek Lawrynowicz #define IVPU_MMU_ENTRY_FLAG_TYPE_PAGE    BIT(1)
23*263b2ba5SJacek Lawrynowicz #define IVPU_MMU_ENTRY_FLAG_VALID        BIT(0)
24*263b2ba5SJacek Lawrynowicz 
25*263b2ba5SJacek Lawrynowicz #define IVPU_MMU_PAGE_SIZE    SZ_4K
26*263b2ba5SJacek Lawrynowicz #define IVPU_MMU_PTE_MAP_SIZE (IVPU_MMU_PGTABLE_ENTRIES * IVPU_MMU_PAGE_SIZE)
27*263b2ba5SJacek Lawrynowicz #define IVPU_MMU_PMD_MAP_SIZE (IVPU_MMU_PGTABLE_ENTRIES * IVPU_MMU_PTE_MAP_SIZE)
28*263b2ba5SJacek Lawrynowicz #define IVPU_MMU_PGTABLE_SIZE (IVPU_MMU_PGTABLE_ENTRIES * sizeof(u64))
29*263b2ba5SJacek Lawrynowicz 
30*263b2ba5SJacek Lawrynowicz #define IVPU_MMU_DUMMY_ADDRESS 0xdeadb000
31*263b2ba5SJacek Lawrynowicz #define IVPU_MMU_ENTRY_VALID   (IVPU_MMU_ENTRY_FLAG_TYPE_PAGE | IVPU_MMU_ENTRY_FLAG_VALID)
32*263b2ba5SJacek Lawrynowicz #define IVPU_MMU_ENTRY_INVALID (IVPU_MMU_DUMMY_ADDRESS & ~IVPU_MMU_ENTRY_FLAGS_MASK)
33*263b2ba5SJacek Lawrynowicz #define IVPU_MMU_ENTRY_MAPPED  (IVPU_MMU_ENTRY_FLAG_AF | IVPU_MMU_ENTRY_FLAG_USER | \
34*263b2ba5SJacek Lawrynowicz 				IVPU_MMU_ENTRY_FLAG_NG | IVPU_MMU_ENTRY_VALID)
35*263b2ba5SJacek Lawrynowicz 
36*263b2ba5SJacek Lawrynowicz static int ivpu_mmu_pgtable_init(struct ivpu_device *vdev, struct ivpu_mmu_pgtable *pgtable)
37*263b2ba5SJacek Lawrynowicz {
38*263b2ba5SJacek Lawrynowicz 	dma_addr_t pgd_dma;
39*263b2ba5SJacek Lawrynowicz 	u64 *pgd;
40*263b2ba5SJacek Lawrynowicz 
41*263b2ba5SJacek Lawrynowicz 	pgd = dma_alloc_wc(vdev->drm.dev, IVPU_MMU_PGTABLE_SIZE, &pgd_dma, GFP_KERNEL);
42*263b2ba5SJacek Lawrynowicz 	if (!pgd)
43*263b2ba5SJacek Lawrynowicz 		return -ENOMEM;
44*263b2ba5SJacek Lawrynowicz 
45*263b2ba5SJacek Lawrynowicz 	pgtable->pgd = pgd;
46*263b2ba5SJacek Lawrynowicz 	pgtable->pgd_dma = pgd_dma;
47*263b2ba5SJacek Lawrynowicz 
48*263b2ba5SJacek Lawrynowicz 	return 0;
49*263b2ba5SJacek Lawrynowicz }
50*263b2ba5SJacek Lawrynowicz 
51*263b2ba5SJacek Lawrynowicz static void ivpu_mmu_pgtable_free(struct ivpu_device *vdev, struct ivpu_mmu_pgtable *pgtable)
52*263b2ba5SJacek Lawrynowicz {
53*263b2ba5SJacek Lawrynowicz 	int pgd_index, pmd_index;
54*263b2ba5SJacek Lawrynowicz 
55*263b2ba5SJacek Lawrynowicz 	for (pgd_index = 0; pgd_index < IVPU_MMU_PGTABLE_ENTRIES; ++pgd_index) {
56*263b2ba5SJacek Lawrynowicz 		u64 **pmd_entries = pgtable->pgd_cpu_entries[pgd_index];
57*263b2ba5SJacek Lawrynowicz 		u64 *pmd = pgtable->pgd_entries[pgd_index];
58*263b2ba5SJacek Lawrynowicz 
59*263b2ba5SJacek Lawrynowicz 		if (!pmd_entries)
60*263b2ba5SJacek Lawrynowicz 			continue;
61*263b2ba5SJacek Lawrynowicz 
62*263b2ba5SJacek Lawrynowicz 		for (pmd_index = 0; pmd_index < IVPU_MMU_PGTABLE_ENTRIES; ++pmd_index) {
63*263b2ba5SJacek Lawrynowicz 			if (pmd_entries[pmd_index])
64*263b2ba5SJacek Lawrynowicz 				dma_free_wc(vdev->drm.dev, IVPU_MMU_PGTABLE_SIZE,
65*263b2ba5SJacek Lawrynowicz 					    pmd_entries[pmd_index],
66*263b2ba5SJacek Lawrynowicz 					    pmd[pmd_index] & ~IVPU_MMU_ENTRY_FLAGS_MASK);
67*263b2ba5SJacek Lawrynowicz 		}
68*263b2ba5SJacek Lawrynowicz 
69*263b2ba5SJacek Lawrynowicz 		kfree(pmd_entries);
70*263b2ba5SJacek Lawrynowicz 		dma_free_wc(vdev->drm.dev, IVPU_MMU_PGTABLE_SIZE, pgtable->pgd_entries[pgd_index],
71*263b2ba5SJacek Lawrynowicz 			    pgtable->pgd[pgd_index] & ~IVPU_MMU_ENTRY_FLAGS_MASK);
72*263b2ba5SJacek Lawrynowicz 	}
73*263b2ba5SJacek Lawrynowicz 
74*263b2ba5SJacek Lawrynowicz 	dma_free_wc(vdev->drm.dev, IVPU_MMU_PGTABLE_SIZE, pgtable->pgd,
75*263b2ba5SJacek Lawrynowicz 		    pgtable->pgd_dma & ~IVPU_MMU_ENTRY_FLAGS_MASK);
76*263b2ba5SJacek Lawrynowicz }
77*263b2ba5SJacek Lawrynowicz 
78*263b2ba5SJacek Lawrynowicz static u64*
79*263b2ba5SJacek Lawrynowicz ivpu_mmu_ensure_pmd(struct ivpu_device *vdev, struct ivpu_mmu_pgtable *pgtable, u64 pgd_index)
80*263b2ba5SJacek Lawrynowicz {
81*263b2ba5SJacek Lawrynowicz 	u64 **pmd_entries;
82*263b2ba5SJacek Lawrynowicz 	dma_addr_t pmd_dma;
83*263b2ba5SJacek Lawrynowicz 	u64 *pmd;
84*263b2ba5SJacek Lawrynowicz 
85*263b2ba5SJacek Lawrynowicz 	if (pgtable->pgd_entries[pgd_index])
86*263b2ba5SJacek Lawrynowicz 		return pgtable->pgd_entries[pgd_index];
87*263b2ba5SJacek Lawrynowicz 
88*263b2ba5SJacek Lawrynowicz 	pmd = dma_alloc_wc(vdev->drm.dev, IVPU_MMU_PGTABLE_SIZE, &pmd_dma, GFP_KERNEL);
89*263b2ba5SJacek Lawrynowicz 	if (!pmd)
90*263b2ba5SJacek Lawrynowicz 		return NULL;
91*263b2ba5SJacek Lawrynowicz 
92*263b2ba5SJacek Lawrynowicz 	pmd_entries = kzalloc(IVPU_MMU_PGTABLE_SIZE, GFP_KERNEL);
93*263b2ba5SJacek Lawrynowicz 	if (!pmd_entries)
94*263b2ba5SJacek Lawrynowicz 		goto err_free_pgd;
95*263b2ba5SJacek Lawrynowicz 
96*263b2ba5SJacek Lawrynowicz 	pgtable->pgd_entries[pgd_index] = pmd;
97*263b2ba5SJacek Lawrynowicz 	pgtable->pgd_cpu_entries[pgd_index] = pmd_entries;
98*263b2ba5SJacek Lawrynowicz 	pgtable->pgd[pgd_index] = pmd_dma | IVPU_MMU_ENTRY_VALID;
99*263b2ba5SJacek Lawrynowicz 
100*263b2ba5SJacek Lawrynowicz 	return pmd;
101*263b2ba5SJacek Lawrynowicz 
102*263b2ba5SJacek Lawrynowicz err_free_pgd:
103*263b2ba5SJacek Lawrynowicz 	dma_free_wc(vdev->drm.dev, IVPU_MMU_PGTABLE_SIZE, pmd, pmd_dma);
104*263b2ba5SJacek Lawrynowicz 	return NULL;
105*263b2ba5SJacek Lawrynowicz }
106*263b2ba5SJacek Lawrynowicz 
107*263b2ba5SJacek Lawrynowicz static u64*
108*263b2ba5SJacek Lawrynowicz ivpu_mmu_ensure_pte(struct ivpu_device *vdev, struct ivpu_mmu_pgtable *pgtable,
109*263b2ba5SJacek Lawrynowicz 		    int pgd_index, int pmd_index)
110*263b2ba5SJacek Lawrynowicz {
111*263b2ba5SJacek Lawrynowicz 	dma_addr_t pte_dma;
112*263b2ba5SJacek Lawrynowicz 	u64 *pte;
113*263b2ba5SJacek Lawrynowicz 
114*263b2ba5SJacek Lawrynowicz 	if (pgtable->pgd_cpu_entries[pgd_index][pmd_index])
115*263b2ba5SJacek Lawrynowicz 		return pgtable->pgd_cpu_entries[pgd_index][pmd_index];
116*263b2ba5SJacek Lawrynowicz 
117*263b2ba5SJacek Lawrynowicz 	pte = dma_alloc_wc(vdev->drm.dev, IVPU_MMU_PGTABLE_SIZE, &pte_dma, GFP_KERNEL);
118*263b2ba5SJacek Lawrynowicz 	if (!pte)
119*263b2ba5SJacek Lawrynowicz 		return NULL;
120*263b2ba5SJacek Lawrynowicz 
121*263b2ba5SJacek Lawrynowicz 	pgtable->pgd_cpu_entries[pgd_index][pmd_index] = pte;
122*263b2ba5SJacek Lawrynowicz 	pgtable->pgd_entries[pgd_index][pmd_index] = pte_dma | IVPU_MMU_ENTRY_VALID;
123*263b2ba5SJacek Lawrynowicz 
124*263b2ba5SJacek Lawrynowicz 	return pte;
125*263b2ba5SJacek Lawrynowicz }
126*263b2ba5SJacek Lawrynowicz 
127*263b2ba5SJacek Lawrynowicz static int
128*263b2ba5SJacek Lawrynowicz ivpu_mmu_context_map_page(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx,
129*263b2ba5SJacek Lawrynowicz 			  u64 vpu_addr, dma_addr_t dma_addr, int prot)
130*263b2ba5SJacek Lawrynowicz {
131*263b2ba5SJacek Lawrynowicz 	u64 *pte;
132*263b2ba5SJacek Lawrynowicz 	int pgd_index = FIELD_GET(IVPU_MMU_PGD_INDEX_MASK, vpu_addr);
133*263b2ba5SJacek Lawrynowicz 	int pmd_index = FIELD_GET(IVPU_MMU_PMD_INDEX_MASK, vpu_addr);
134*263b2ba5SJacek Lawrynowicz 	int pte_index = FIELD_GET(IVPU_MMU_PTE_INDEX_MASK, vpu_addr);
135*263b2ba5SJacek Lawrynowicz 
136*263b2ba5SJacek Lawrynowicz 	/* Allocate PMD - second level page table if needed */
137*263b2ba5SJacek Lawrynowicz 	if (!ivpu_mmu_ensure_pmd(vdev, &ctx->pgtable, pgd_index))
138*263b2ba5SJacek Lawrynowicz 		return -ENOMEM;
139*263b2ba5SJacek Lawrynowicz 
140*263b2ba5SJacek Lawrynowicz 	/* Allocate PTE - third level page table if needed */
141*263b2ba5SJacek Lawrynowicz 	pte = ivpu_mmu_ensure_pte(vdev, &ctx->pgtable, pgd_index, pmd_index);
142*263b2ba5SJacek Lawrynowicz 	if (!pte)
143*263b2ba5SJacek Lawrynowicz 		return -ENOMEM;
144*263b2ba5SJacek Lawrynowicz 
145*263b2ba5SJacek Lawrynowicz 	/* Update PTE - third level page table with DMA address */
146*263b2ba5SJacek Lawrynowicz 	pte[pte_index] = dma_addr | prot;
147*263b2ba5SJacek Lawrynowicz 
148*263b2ba5SJacek Lawrynowicz 	return 0;
149*263b2ba5SJacek Lawrynowicz }
150*263b2ba5SJacek Lawrynowicz 
151*263b2ba5SJacek Lawrynowicz static void ivpu_mmu_context_unmap_page(struct ivpu_mmu_context *ctx, u64 vpu_addr)
152*263b2ba5SJacek Lawrynowicz {
153*263b2ba5SJacek Lawrynowicz 	int pgd_index = FIELD_GET(IVPU_MMU_PGD_INDEX_MASK, vpu_addr);
154*263b2ba5SJacek Lawrynowicz 	int pmd_index = FIELD_GET(IVPU_MMU_PMD_INDEX_MASK, vpu_addr);
155*263b2ba5SJacek Lawrynowicz 	int pte_index = FIELD_GET(IVPU_MMU_PTE_INDEX_MASK, vpu_addr);
156*263b2ba5SJacek Lawrynowicz 
157*263b2ba5SJacek Lawrynowicz 	/* Update PTE with dummy physical address and clear flags */
158*263b2ba5SJacek Lawrynowicz 	ctx->pgtable.pgd_cpu_entries[pgd_index][pmd_index][pte_index] = IVPU_MMU_ENTRY_INVALID;
159*263b2ba5SJacek Lawrynowicz }
160*263b2ba5SJacek Lawrynowicz 
161*263b2ba5SJacek Lawrynowicz static void
162*263b2ba5SJacek Lawrynowicz ivpu_mmu_context_flush_page_tables(struct ivpu_mmu_context *ctx, u64 vpu_addr, size_t size)
163*263b2ba5SJacek Lawrynowicz {
164*263b2ba5SJacek Lawrynowicz 	u64 end_addr = vpu_addr + size;
165*263b2ba5SJacek Lawrynowicz 	u64 *pgd = ctx->pgtable.pgd;
166*263b2ba5SJacek Lawrynowicz 
167*263b2ba5SJacek Lawrynowicz 	/* Align to PMD entry (2 MB) */
168*263b2ba5SJacek Lawrynowicz 	vpu_addr &= ~(IVPU_MMU_PTE_MAP_SIZE - 1);
169*263b2ba5SJacek Lawrynowicz 
170*263b2ba5SJacek Lawrynowicz 	while (vpu_addr < end_addr) {
171*263b2ba5SJacek Lawrynowicz 		int pgd_index = FIELD_GET(IVPU_MMU_PGD_INDEX_MASK, vpu_addr);
172*263b2ba5SJacek Lawrynowicz 		u64 pmd_end = (pgd_index + 1) * (u64)IVPU_MMU_PMD_MAP_SIZE;
173*263b2ba5SJacek Lawrynowicz 		u64 *pmd = ctx->pgtable.pgd_entries[pgd_index];
174*263b2ba5SJacek Lawrynowicz 
175*263b2ba5SJacek Lawrynowicz 		while (vpu_addr < end_addr && vpu_addr < pmd_end) {
176*263b2ba5SJacek Lawrynowicz 			int pmd_index = FIELD_GET(IVPU_MMU_PMD_INDEX_MASK, vpu_addr);
177*263b2ba5SJacek Lawrynowicz 			u64 *pte = ctx->pgtable.pgd_cpu_entries[pgd_index][pmd_index];
178*263b2ba5SJacek Lawrynowicz 
179*263b2ba5SJacek Lawrynowicz 			clflush_cache_range(pte, IVPU_MMU_PGTABLE_SIZE);
180*263b2ba5SJacek Lawrynowicz 			vpu_addr += IVPU_MMU_PTE_MAP_SIZE;
181*263b2ba5SJacek Lawrynowicz 		}
182*263b2ba5SJacek Lawrynowicz 		clflush_cache_range(pmd, IVPU_MMU_PGTABLE_SIZE);
183*263b2ba5SJacek Lawrynowicz 	}
184*263b2ba5SJacek Lawrynowicz 	clflush_cache_range(pgd, IVPU_MMU_PGTABLE_SIZE);
185*263b2ba5SJacek Lawrynowicz }
186*263b2ba5SJacek Lawrynowicz 
187*263b2ba5SJacek Lawrynowicz static int
188*263b2ba5SJacek Lawrynowicz ivpu_mmu_context_map_pages(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx,
189*263b2ba5SJacek Lawrynowicz 			   u64 vpu_addr, dma_addr_t dma_addr, size_t size, int prot)
190*263b2ba5SJacek Lawrynowicz {
191*263b2ba5SJacek Lawrynowicz 	while (size) {
192*263b2ba5SJacek Lawrynowicz 		int ret = ivpu_mmu_context_map_page(vdev, ctx, vpu_addr, dma_addr, prot);
193*263b2ba5SJacek Lawrynowicz 
194*263b2ba5SJacek Lawrynowicz 		if (ret)
195*263b2ba5SJacek Lawrynowicz 			return ret;
196*263b2ba5SJacek Lawrynowicz 
197*263b2ba5SJacek Lawrynowicz 		vpu_addr += IVPU_MMU_PAGE_SIZE;
198*263b2ba5SJacek Lawrynowicz 		dma_addr += IVPU_MMU_PAGE_SIZE;
199*263b2ba5SJacek Lawrynowicz 		size -= IVPU_MMU_PAGE_SIZE;
200*263b2ba5SJacek Lawrynowicz 	}
201*263b2ba5SJacek Lawrynowicz 
202*263b2ba5SJacek Lawrynowicz 	return 0;
203*263b2ba5SJacek Lawrynowicz }
204*263b2ba5SJacek Lawrynowicz 
205*263b2ba5SJacek Lawrynowicz static void ivpu_mmu_context_unmap_pages(struct ivpu_mmu_context *ctx, u64 vpu_addr, size_t size)
206*263b2ba5SJacek Lawrynowicz {
207*263b2ba5SJacek Lawrynowicz 	while (size) {
208*263b2ba5SJacek Lawrynowicz 		ivpu_mmu_context_unmap_page(ctx, vpu_addr);
209*263b2ba5SJacek Lawrynowicz 		vpu_addr += IVPU_MMU_PAGE_SIZE;
210*263b2ba5SJacek Lawrynowicz 		size -= IVPU_MMU_PAGE_SIZE;
211*263b2ba5SJacek Lawrynowicz 	}
212*263b2ba5SJacek Lawrynowicz }
213*263b2ba5SJacek Lawrynowicz 
214*263b2ba5SJacek Lawrynowicz int
215*263b2ba5SJacek Lawrynowicz ivpu_mmu_context_map_sgt(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx,
216*263b2ba5SJacek Lawrynowicz 			 u64 vpu_addr, struct sg_table *sgt,  bool llc_coherent)
217*263b2ba5SJacek Lawrynowicz {
218*263b2ba5SJacek Lawrynowicz 	struct scatterlist *sg;
219*263b2ba5SJacek Lawrynowicz 	int prot;
220*263b2ba5SJacek Lawrynowicz 	int ret;
221*263b2ba5SJacek Lawrynowicz 	u64 i;
222*263b2ba5SJacek Lawrynowicz 
223*263b2ba5SJacek Lawrynowicz 	if (!IS_ALIGNED(vpu_addr, IVPU_MMU_PAGE_SIZE))
224*263b2ba5SJacek Lawrynowicz 		return -EINVAL;
225*263b2ba5SJacek Lawrynowicz 	/*
226*263b2ba5SJacek Lawrynowicz 	 * VPU is only 32 bit, but DMA engine is 38 bit
227*263b2ba5SJacek Lawrynowicz 	 * Ranges < 2 GB are reserved for VPU internal registers
228*263b2ba5SJacek Lawrynowicz 	 * Limit range to 8 GB
229*263b2ba5SJacek Lawrynowicz 	 */
230*263b2ba5SJacek Lawrynowicz 	if (vpu_addr < SZ_2G || vpu_addr > SZ_8G)
231*263b2ba5SJacek Lawrynowicz 		return -EINVAL;
232*263b2ba5SJacek Lawrynowicz 
233*263b2ba5SJacek Lawrynowicz 	prot = IVPU_MMU_ENTRY_MAPPED;
234*263b2ba5SJacek Lawrynowicz 	if (llc_coherent)
235*263b2ba5SJacek Lawrynowicz 		prot |= IVPU_MMU_ENTRY_FLAG_LLC_COHERENT;
236*263b2ba5SJacek Lawrynowicz 
237*263b2ba5SJacek Lawrynowicz 	mutex_lock(&ctx->lock);
238*263b2ba5SJacek Lawrynowicz 
239*263b2ba5SJacek Lawrynowicz 	for_each_sgtable_dma_sg(sgt, sg, i) {
240*263b2ba5SJacek Lawrynowicz 		u64 dma_addr = sg_dma_address(sg) - sg->offset;
241*263b2ba5SJacek Lawrynowicz 		size_t size = sg_dma_len(sg) + sg->offset;
242*263b2ba5SJacek Lawrynowicz 
243*263b2ba5SJacek Lawrynowicz 		ret = ivpu_mmu_context_map_pages(vdev, ctx, vpu_addr, dma_addr, size, prot);
244*263b2ba5SJacek Lawrynowicz 		if (ret) {
245*263b2ba5SJacek Lawrynowicz 			ivpu_err(vdev, "Failed to map context pages\n");
246*263b2ba5SJacek Lawrynowicz 			mutex_unlock(&ctx->lock);
247*263b2ba5SJacek Lawrynowicz 			return ret;
248*263b2ba5SJacek Lawrynowicz 		}
249*263b2ba5SJacek Lawrynowicz 		ivpu_mmu_context_flush_page_tables(ctx, vpu_addr, size);
250*263b2ba5SJacek Lawrynowicz 		vpu_addr += size;
251*263b2ba5SJacek Lawrynowicz 	}
252*263b2ba5SJacek Lawrynowicz 
253*263b2ba5SJacek Lawrynowicz 	mutex_unlock(&ctx->lock);
254*263b2ba5SJacek Lawrynowicz 
255*263b2ba5SJacek Lawrynowicz 	ret = ivpu_mmu_invalidate_tlb(vdev, ctx->id);
256*263b2ba5SJacek Lawrynowicz 	if (ret)
257*263b2ba5SJacek Lawrynowicz 		ivpu_err(vdev, "Failed to invalidate TLB for ctx %u: %d\n", ctx->id, ret);
258*263b2ba5SJacek Lawrynowicz 	return ret;
259*263b2ba5SJacek Lawrynowicz }
260*263b2ba5SJacek Lawrynowicz 
261*263b2ba5SJacek Lawrynowicz void
262*263b2ba5SJacek Lawrynowicz ivpu_mmu_context_unmap_sgt(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx,
263*263b2ba5SJacek Lawrynowicz 			   u64 vpu_addr, struct sg_table *sgt)
264*263b2ba5SJacek Lawrynowicz {
265*263b2ba5SJacek Lawrynowicz 	struct scatterlist *sg;
266*263b2ba5SJacek Lawrynowicz 	int ret;
267*263b2ba5SJacek Lawrynowicz 	u64 i;
268*263b2ba5SJacek Lawrynowicz 
269*263b2ba5SJacek Lawrynowicz 	if (!IS_ALIGNED(vpu_addr, IVPU_MMU_PAGE_SIZE))
270*263b2ba5SJacek Lawrynowicz 		ivpu_warn(vdev, "Unaligned vpu_addr: 0x%llx\n", vpu_addr);
271*263b2ba5SJacek Lawrynowicz 
272*263b2ba5SJacek Lawrynowicz 	mutex_lock(&ctx->lock);
273*263b2ba5SJacek Lawrynowicz 
274*263b2ba5SJacek Lawrynowicz 	for_each_sgtable_dma_sg(sgt, sg, i) {
275*263b2ba5SJacek Lawrynowicz 		size_t size = sg_dma_len(sg) + sg->offset;
276*263b2ba5SJacek Lawrynowicz 
277*263b2ba5SJacek Lawrynowicz 		ivpu_mmu_context_unmap_pages(ctx, vpu_addr, size);
278*263b2ba5SJacek Lawrynowicz 		ivpu_mmu_context_flush_page_tables(ctx, vpu_addr, size);
279*263b2ba5SJacek Lawrynowicz 		vpu_addr += size;
280*263b2ba5SJacek Lawrynowicz 	}
281*263b2ba5SJacek Lawrynowicz 
282*263b2ba5SJacek Lawrynowicz 	mutex_unlock(&ctx->lock);
283*263b2ba5SJacek Lawrynowicz 
284*263b2ba5SJacek Lawrynowicz 	ret = ivpu_mmu_invalidate_tlb(vdev, ctx->id);
285*263b2ba5SJacek Lawrynowicz 	if (ret)
286*263b2ba5SJacek Lawrynowicz 		ivpu_warn(vdev, "Failed to invalidate TLB for ctx %u: %d\n", ctx->id, ret);
287*263b2ba5SJacek Lawrynowicz }
288*263b2ba5SJacek Lawrynowicz 
289*263b2ba5SJacek Lawrynowicz int
290*263b2ba5SJacek Lawrynowicz ivpu_mmu_context_insert_node_locked(struct ivpu_mmu_context *ctx,
291*263b2ba5SJacek Lawrynowicz 				    const struct ivpu_addr_range *range,
292*263b2ba5SJacek Lawrynowicz 				    u64 size, struct drm_mm_node *node)
293*263b2ba5SJacek Lawrynowicz {
294*263b2ba5SJacek Lawrynowicz 	lockdep_assert_held(&ctx->lock);
295*263b2ba5SJacek Lawrynowicz 
296*263b2ba5SJacek Lawrynowicz 	return drm_mm_insert_node_in_range(&ctx->mm, node, size, IVPU_MMU_PAGE_SIZE,
297*263b2ba5SJacek Lawrynowicz 					  0, range->start, range->end, DRM_MM_INSERT_BEST);
298*263b2ba5SJacek Lawrynowicz }
299*263b2ba5SJacek Lawrynowicz 
300*263b2ba5SJacek Lawrynowicz void
301*263b2ba5SJacek Lawrynowicz ivpu_mmu_context_remove_node_locked(struct ivpu_mmu_context *ctx, struct drm_mm_node *node)
302*263b2ba5SJacek Lawrynowicz {
303*263b2ba5SJacek Lawrynowicz 	lockdep_assert_held(&ctx->lock);
304*263b2ba5SJacek Lawrynowicz 
305*263b2ba5SJacek Lawrynowicz 	drm_mm_remove_node(node);
306*263b2ba5SJacek Lawrynowicz }
307*263b2ba5SJacek Lawrynowicz 
308*263b2ba5SJacek Lawrynowicz static int
309*263b2ba5SJacek Lawrynowicz ivpu_mmu_context_init(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx, u32 context_id)
310*263b2ba5SJacek Lawrynowicz {
311*263b2ba5SJacek Lawrynowicz 	u64 start, end;
312*263b2ba5SJacek Lawrynowicz 	int ret;
313*263b2ba5SJacek Lawrynowicz 
314*263b2ba5SJacek Lawrynowicz 	mutex_init(&ctx->lock);
315*263b2ba5SJacek Lawrynowicz 	INIT_LIST_HEAD(&ctx->bo_list);
316*263b2ba5SJacek Lawrynowicz 
317*263b2ba5SJacek Lawrynowicz 	ret = ivpu_mmu_pgtable_init(vdev, &ctx->pgtable);
318*263b2ba5SJacek Lawrynowicz 	if (ret)
319*263b2ba5SJacek Lawrynowicz 		return ret;
320*263b2ba5SJacek Lawrynowicz 
321*263b2ba5SJacek Lawrynowicz 	if (!context_id) {
322*263b2ba5SJacek Lawrynowicz 		start = vdev->hw->ranges.global_low.start;
323*263b2ba5SJacek Lawrynowicz 		end = vdev->hw->ranges.global_high.end;
324*263b2ba5SJacek Lawrynowicz 	} else {
325*263b2ba5SJacek Lawrynowicz 		start = vdev->hw->ranges.user_low.start;
326*263b2ba5SJacek Lawrynowicz 		end = vdev->hw->ranges.user_high.end;
327*263b2ba5SJacek Lawrynowicz 	}
328*263b2ba5SJacek Lawrynowicz 
329*263b2ba5SJacek Lawrynowicz 	drm_mm_init(&ctx->mm, start, end - start);
330*263b2ba5SJacek Lawrynowicz 	ctx->id = context_id;
331*263b2ba5SJacek Lawrynowicz 
332*263b2ba5SJacek Lawrynowicz 	return 0;
333*263b2ba5SJacek Lawrynowicz }
334*263b2ba5SJacek Lawrynowicz 
335*263b2ba5SJacek Lawrynowicz static void ivpu_mmu_context_fini(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx)
336*263b2ba5SJacek Lawrynowicz {
337*263b2ba5SJacek Lawrynowicz 	drm_WARN_ON(&vdev->drm, !ctx->pgtable.pgd);
338*263b2ba5SJacek Lawrynowicz 
339*263b2ba5SJacek Lawrynowicz 	mutex_destroy(&ctx->lock);
340*263b2ba5SJacek Lawrynowicz 	ivpu_mmu_pgtable_free(vdev, &ctx->pgtable);
341*263b2ba5SJacek Lawrynowicz 	drm_mm_takedown(&ctx->mm);
342*263b2ba5SJacek Lawrynowicz }
343*263b2ba5SJacek Lawrynowicz 
344*263b2ba5SJacek Lawrynowicz int ivpu_mmu_global_context_init(struct ivpu_device *vdev)
345*263b2ba5SJacek Lawrynowicz {
346*263b2ba5SJacek Lawrynowicz 	return ivpu_mmu_context_init(vdev, &vdev->gctx, IVPU_GLOBAL_CONTEXT_MMU_SSID);
347*263b2ba5SJacek Lawrynowicz }
348*263b2ba5SJacek Lawrynowicz 
349*263b2ba5SJacek Lawrynowicz void ivpu_mmu_global_context_fini(struct ivpu_device *vdev)
350*263b2ba5SJacek Lawrynowicz {
351*263b2ba5SJacek Lawrynowicz 	return ivpu_mmu_context_fini(vdev, &vdev->gctx);
352*263b2ba5SJacek Lawrynowicz }
353*263b2ba5SJacek Lawrynowicz 
354*263b2ba5SJacek Lawrynowicz void ivpu_mmu_user_context_mark_invalid(struct ivpu_device *vdev, u32 ssid)
355*263b2ba5SJacek Lawrynowicz {
356*263b2ba5SJacek Lawrynowicz 	struct ivpu_file_priv *file_priv;
357*263b2ba5SJacek Lawrynowicz 
358*263b2ba5SJacek Lawrynowicz 	xa_lock(&vdev->context_xa);
359*263b2ba5SJacek Lawrynowicz 
360*263b2ba5SJacek Lawrynowicz 	file_priv = xa_load(&vdev->context_xa, ssid);
361*263b2ba5SJacek Lawrynowicz 	if (file_priv)
362*263b2ba5SJacek Lawrynowicz 		file_priv->has_mmu_faults = true;
363*263b2ba5SJacek Lawrynowicz 
364*263b2ba5SJacek Lawrynowicz 	xa_unlock(&vdev->context_xa);
365*263b2ba5SJacek Lawrynowicz }
366*263b2ba5SJacek Lawrynowicz 
367*263b2ba5SJacek Lawrynowicz int ivpu_mmu_user_context_init(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx, u32 ctx_id)
368*263b2ba5SJacek Lawrynowicz {
369*263b2ba5SJacek Lawrynowicz 	int ret;
370*263b2ba5SJacek Lawrynowicz 
371*263b2ba5SJacek Lawrynowicz 	drm_WARN_ON(&vdev->drm, !ctx_id);
372*263b2ba5SJacek Lawrynowicz 
373*263b2ba5SJacek Lawrynowicz 	ret = ivpu_mmu_context_init(vdev, ctx, ctx_id);
374*263b2ba5SJacek Lawrynowicz 	if (ret) {
375*263b2ba5SJacek Lawrynowicz 		ivpu_err(vdev, "Failed to initialize context: %d\n", ret);
376*263b2ba5SJacek Lawrynowicz 		return ret;
377*263b2ba5SJacek Lawrynowicz 	}
378*263b2ba5SJacek Lawrynowicz 
379*263b2ba5SJacek Lawrynowicz 	ret = ivpu_mmu_set_pgtable(vdev, ctx_id, &ctx->pgtable);
380*263b2ba5SJacek Lawrynowicz 	if (ret) {
381*263b2ba5SJacek Lawrynowicz 		ivpu_err(vdev, "Failed to set page table: %d\n", ret);
382*263b2ba5SJacek Lawrynowicz 		goto err_context_fini;
383*263b2ba5SJacek Lawrynowicz 	}
384*263b2ba5SJacek Lawrynowicz 
385*263b2ba5SJacek Lawrynowicz 	return 0;
386*263b2ba5SJacek Lawrynowicz 
387*263b2ba5SJacek Lawrynowicz err_context_fini:
388*263b2ba5SJacek Lawrynowicz 	ivpu_mmu_context_fini(vdev, ctx);
389*263b2ba5SJacek Lawrynowicz 	return ret;
390*263b2ba5SJacek Lawrynowicz }
391*263b2ba5SJacek Lawrynowicz 
392*263b2ba5SJacek Lawrynowicz void ivpu_mmu_user_context_fini(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx)
393*263b2ba5SJacek Lawrynowicz {
394*263b2ba5SJacek Lawrynowicz 	drm_WARN_ON(&vdev->drm, !ctx->id);
395*263b2ba5SJacek Lawrynowicz 
396*263b2ba5SJacek Lawrynowicz 	ivpu_mmu_clear_pgtable(vdev, ctx->id);
397*263b2ba5SJacek Lawrynowicz 	ivpu_mmu_context_fini(vdev, ctx);
398*263b2ba5SJacek Lawrynowicz }
399