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