xref: /linux/drivers/gpu/drm/etnaviv/etnaviv_iommu.c (revision 762f99f4f3cb41a775b5157dd761217beba65873)
1f6ffbd4fSLucas Stach // SPDX-License-Identifier: GPL-2.0
2a8c21a54SThe etnaviv authors /*
3f6ffbd4fSLucas Stach  * Copyright (C) 2014-2018 Etnaviv Project
4a8c21a54SThe etnaviv authors  */
5a8c21a54SThe etnaviv authors 
62e737e52SLucas Stach #include <linux/bitops.h>
72e737e52SLucas Stach #include <linux/dma-mapping.h>
8a8c21a54SThe etnaviv authors #include <linux/platform_device.h>
9a8c21a54SThe etnaviv authors #include <linux/sizes.h>
10a8c21a54SThe etnaviv authors #include <linux/slab.h>
11a8c21a54SThe etnaviv authors 
12a8c21a54SThe etnaviv authors #include "etnaviv_gpu.h"
13a8c21a54SThe etnaviv authors #include "etnaviv_mmu.h"
14a8c21a54SThe etnaviv authors #include "state_hi.xml.h"
15a8c21a54SThe etnaviv authors 
16a8c21a54SThe etnaviv authors #define PT_SIZE		SZ_2M
17a8c21a54SThe etnaviv authors #define PT_ENTRIES	(PT_SIZE / sizeof(u32))
18a8c21a54SThe etnaviv authors 
19a8c21a54SThe etnaviv authors #define GPU_MEM_START	0x80000000
20a8c21a54SThe etnaviv authors 
2127b67278SLucas Stach struct etnaviv_iommuv1_context {
2227b67278SLucas Stach 	struct etnaviv_iommu_context base;
23b6709083SLucas Stach 	u32 *pgtable_cpu;
24b6709083SLucas Stach 	dma_addr_t pgtable_dma;
25a8c21a54SThe etnaviv authors };
26a8c21a54SThe etnaviv authors 
2727b67278SLucas Stach static struct etnaviv_iommuv1_context *
to_v1_context(struct etnaviv_iommu_context * context)2827b67278SLucas Stach to_v1_context(struct etnaviv_iommu_context *context)
29a8c21a54SThe etnaviv authors {
3027b67278SLucas Stach 	return container_of(context, struct etnaviv_iommuv1_context, base);
31a8c21a54SThe etnaviv authors }
32a8c21a54SThe etnaviv authors 
etnaviv_iommuv1_free(struct etnaviv_iommu_context * context)3327b67278SLucas Stach static void etnaviv_iommuv1_free(struct etnaviv_iommu_context *context)
34a8c21a54SThe etnaviv authors {
3527b67278SLucas Stach 	struct etnaviv_iommuv1_context *v1_context = to_v1_context(context);
36a8c21a54SThe etnaviv authors 
3727b67278SLucas Stach 	drm_mm_takedown(&context->mm);
38a8c21a54SThe etnaviv authors 
3927b67278SLucas Stach 	dma_free_wc(context->global->dev, PT_SIZE, v1_context->pgtable_cpu,
4027b67278SLucas Stach 		    v1_context->pgtable_dma);
41a8c21a54SThe etnaviv authors 
4227b67278SLucas Stach 	context->global->v1.shared_context = NULL;
4327b67278SLucas Stach 
4427b67278SLucas Stach 	kfree(v1_context);
45a8c21a54SThe etnaviv authors }
46a8c21a54SThe etnaviv authors 
etnaviv_iommuv1_map(struct etnaviv_iommu_context * context,unsigned long iova,phys_addr_t paddr,size_t size,int prot)4727b67278SLucas Stach static int etnaviv_iommuv1_map(struct etnaviv_iommu_context *context,
48b6709083SLucas Stach 			       unsigned long iova, phys_addr_t paddr,
49b6709083SLucas Stach 			       size_t size, int prot)
50a8c21a54SThe etnaviv authors {
5127b67278SLucas Stach 	struct etnaviv_iommuv1_context *v1_context = to_v1_context(context);
52bd2442bcSLucas Stach 	unsigned int index = (iova - GPU_MEM_START) / SZ_4K;
53a8c21a54SThe etnaviv authors 
54a8c21a54SThe etnaviv authors 	if (size != SZ_4K)
55a8c21a54SThe etnaviv authors 		return -EINVAL;
56a8c21a54SThe etnaviv authors 
5727b67278SLucas Stach 	v1_context->pgtable_cpu[index] = paddr;
58a8c21a54SThe etnaviv authors 
59a8c21a54SThe etnaviv authors 	return 0;
60a8c21a54SThe etnaviv authors }
61a8c21a54SThe etnaviv authors 
etnaviv_iommuv1_unmap(struct etnaviv_iommu_context * context,unsigned long iova,size_t size)6227b67278SLucas Stach static size_t etnaviv_iommuv1_unmap(struct etnaviv_iommu_context *context,
63a8c21a54SThe etnaviv authors 	unsigned long iova, size_t size)
64a8c21a54SThe etnaviv authors {
6527b67278SLucas Stach 	struct etnaviv_iommuv1_context *v1_context = to_v1_context(context);
66bd2442bcSLucas Stach 	unsigned int index = (iova - GPU_MEM_START) / SZ_4K;
67a8c21a54SThe etnaviv authors 
68a8c21a54SThe etnaviv authors 	if (size != SZ_4K)
69a8c21a54SThe etnaviv authors 		return -EINVAL;
70a8c21a54SThe etnaviv authors 
7127b67278SLucas Stach 	v1_context->pgtable_cpu[index] = context->global->bad_page_dma;
72a8c21a54SThe etnaviv authors 
73a8c21a54SThe etnaviv authors 	return SZ_4K;
74a8c21a54SThe etnaviv authors }
75a8c21a54SThe etnaviv authors 
etnaviv_iommuv1_dump_size(struct etnaviv_iommu_context * context)7627b67278SLucas Stach static size_t etnaviv_iommuv1_dump_size(struct etnaviv_iommu_context *context)
77a8c21a54SThe etnaviv authors {
78a8c21a54SThe etnaviv authors 	return PT_SIZE;
79a8c21a54SThe etnaviv authors }
80a8c21a54SThe etnaviv authors 
etnaviv_iommuv1_dump(struct etnaviv_iommu_context * context,void * buf)8127b67278SLucas Stach static void etnaviv_iommuv1_dump(struct etnaviv_iommu_context *context,
8227b67278SLucas Stach 				 void *buf)
83a8c21a54SThe etnaviv authors {
8427b67278SLucas Stach 	struct etnaviv_iommuv1_context *v1_context = to_v1_context(context);
85a8c21a54SThe etnaviv authors 
8627b67278SLucas Stach 	memcpy(buf, v1_context->pgtable_cpu, PT_SIZE);
87a8c21a54SThe etnaviv authors }
88a8c21a54SThe etnaviv authors 
etnaviv_iommuv1_restore(struct etnaviv_gpu * gpu,struct etnaviv_iommu_context * context)8927b67278SLucas Stach static void etnaviv_iommuv1_restore(struct etnaviv_gpu *gpu,
9027b67278SLucas Stach 			     struct etnaviv_iommu_context *context)
91a8c21a54SThe etnaviv authors {
9227b67278SLucas Stach 	struct etnaviv_iommuv1_context *v1_context = to_v1_context(context);
93a8c21a54SThe etnaviv authors 	u32 pgtable;
94a8c21a54SThe etnaviv authors 
95*d6408538SLucas Stach 	if (gpu->mmu_context)
96*d6408538SLucas Stach 		etnaviv_iommu_context_put(gpu->mmu_context);
97*d6408538SLucas Stach 	gpu->mmu_context = etnaviv_iommu_context_get(context);
98*d6408538SLucas Stach 
9999f861bcSLucas Stach 	/* set base addresses */
10017e4660aSLucas Stach 	gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_RA, context->global->memory_base);
10117e4660aSLucas Stach 	gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_FE, context->global->memory_base);
10217e4660aSLucas Stach 	gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_TX, context->global->memory_base);
10317e4660aSLucas Stach 	gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_PEZ, context->global->memory_base);
10417e4660aSLucas Stach 	gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_PE, context->global->memory_base);
10599f861bcSLucas Stach 
106a8c21a54SThe etnaviv authors 	/* set page table address in MC */
10727b67278SLucas Stach 	pgtable = (u32)v1_context->pgtable_dma;
108a8c21a54SThe etnaviv authors 
109a8c21a54SThe etnaviv authors 	gpu_write(gpu, VIVS_MC_MMU_FE_PAGE_TABLE, pgtable);
110a8c21a54SThe etnaviv authors 	gpu_write(gpu, VIVS_MC_MMU_TX_PAGE_TABLE, pgtable);
111a8c21a54SThe etnaviv authors 	gpu_write(gpu, VIVS_MC_MMU_PE_PAGE_TABLE, pgtable);
112a8c21a54SThe etnaviv authors 	gpu_write(gpu, VIVS_MC_MMU_PEZ_PAGE_TABLE, pgtable);
113a8c21a54SThe etnaviv authors 	gpu_write(gpu, VIVS_MC_MMU_RA_PAGE_TABLE, pgtable);
114a8c21a54SThe etnaviv authors }
115a8c21a54SThe etnaviv authors 
11627b67278SLucas Stach 
11727b67278SLucas Stach const struct etnaviv_iommu_ops etnaviv_iommuv1_ops = {
11827b67278SLucas Stach 	.free = etnaviv_iommuv1_free,
119b6709083SLucas Stach 	.map = etnaviv_iommuv1_map,
120b6709083SLucas Stach 	.unmap = etnaviv_iommuv1_unmap,
121b6709083SLucas Stach 	.dump_size = etnaviv_iommuv1_dump_size,
122b6709083SLucas Stach 	.dump = etnaviv_iommuv1_dump,
12327b67278SLucas Stach 	.restore = etnaviv_iommuv1_restore,
124b6709083SLucas Stach };
125b6709083SLucas Stach 
12627b67278SLucas Stach struct etnaviv_iommu_context *
etnaviv_iommuv1_context_alloc(struct etnaviv_iommu_global * global)12727b67278SLucas Stach etnaviv_iommuv1_context_alloc(struct etnaviv_iommu_global *global)
128a8c21a54SThe etnaviv authors {
12927b67278SLucas Stach 	struct etnaviv_iommuv1_context *v1_context;
13027b67278SLucas Stach 	struct etnaviv_iommu_context *context;
131a8c21a54SThe etnaviv authors 
13227b67278SLucas Stach 	mutex_lock(&global->lock);
13327b67278SLucas Stach 
13427b67278SLucas Stach 	/*
13527b67278SLucas Stach 	 * MMUv1 does not support switching between different contexts without
13627b67278SLucas Stach 	 * a stop the world operation, so we only support a single shared
13727b67278SLucas Stach 	 * context with this version.
13827b67278SLucas Stach 	 */
13927b67278SLucas Stach 	if (global->v1.shared_context) {
14027b67278SLucas Stach 		context = global->v1.shared_context;
14127b67278SLucas Stach 		etnaviv_iommu_context_get(context);
14227b67278SLucas Stach 		mutex_unlock(&global->lock);
14327b67278SLucas Stach 		return context;
14427b67278SLucas Stach 	}
14527b67278SLucas Stach 
14627b67278SLucas Stach 	v1_context = kzalloc(sizeof(*v1_context), GFP_KERNEL);
147dbcc574aSWei Yongjun 	if (!v1_context) {
148dbcc574aSWei Yongjun 		mutex_unlock(&global->lock);
149a8c21a54SThe etnaviv authors 		return NULL;
150dbcc574aSWei Yongjun 	}
151a8c21a54SThe etnaviv authors 
15227b67278SLucas Stach 	v1_context->pgtable_cpu = dma_alloc_wc(global->dev, PT_SIZE,
15327b67278SLucas Stach 					       &v1_context->pgtable_dma,
15427b67278SLucas Stach 					       GFP_KERNEL);
15527b67278SLucas Stach 	if (!v1_context->pgtable_cpu)
156a8c21a54SThe etnaviv authors 		goto out_free;
157a8c21a54SThe etnaviv authors 
15827b67278SLucas Stach 	memset32(v1_context->pgtable_cpu, global->bad_page_dma, PT_ENTRIES);
15927b67278SLucas Stach 
16027b67278SLucas Stach 	context = &v1_context->base;
16127b67278SLucas Stach 	context->global = global;
16227b67278SLucas Stach 	kref_init(&context->refcount);
16327b67278SLucas Stach 	mutex_init(&context->lock);
16427b67278SLucas Stach 	INIT_LIST_HEAD(&context->mappings);
16527b67278SLucas Stach 	drm_mm_init(&context->mm, GPU_MEM_START, PT_ENTRIES * SZ_4K);
16627b67278SLucas Stach 	context->global->v1.shared_context = context;
16727b67278SLucas Stach 
16827b67278SLucas Stach 	mutex_unlock(&global->lock);
16927b67278SLucas Stach 
17027b67278SLucas Stach 	return context;
171a8c21a54SThe etnaviv authors 
172a8c21a54SThe etnaviv authors out_free:
17327b67278SLucas Stach 	mutex_unlock(&global->lock);
17427b67278SLucas Stach 	kfree(v1_context);
175a8c21a54SThe etnaviv authors 	return NULL;
176a8c21a54SThe etnaviv authors }
177