18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2014-2018 Etnaviv Project
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/bitops.h>
78c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
88c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
98c2ecf20Sopenharmony_ci#include <linux/sizes.h>
108c2ecf20Sopenharmony_ci#include <linux/slab.h>
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include "etnaviv_gpu.h"
138c2ecf20Sopenharmony_ci#include "etnaviv_mmu.h"
148c2ecf20Sopenharmony_ci#include "state_hi.xml.h"
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#define PT_SIZE		SZ_2M
178c2ecf20Sopenharmony_ci#define PT_ENTRIES	(PT_SIZE / sizeof(u32))
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#define GPU_MEM_START	0x80000000
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_cistruct etnaviv_iommuv1_context {
228c2ecf20Sopenharmony_ci	struct etnaviv_iommu_context base;
238c2ecf20Sopenharmony_ci	u32 *pgtable_cpu;
248c2ecf20Sopenharmony_ci	dma_addr_t pgtable_dma;
258c2ecf20Sopenharmony_ci};
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistatic struct etnaviv_iommuv1_context *
288c2ecf20Sopenharmony_cito_v1_context(struct etnaviv_iommu_context *context)
298c2ecf20Sopenharmony_ci{
308c2ecf20Sopenharmony_ci	return container_of(context, struct etnaviv_iommuv1_context, base);
318c2ecf20Sopenharmony_ci}
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_cistatic void etnaviv_iommuv1_free(struct etnaviv_iommu_context *context)
348c2ecf20Sopenharmony_ci{
358c2ecf20Sopenharmony_ci	struct etnaviv_iommuv1_context *v1_context = to_v1_context(context);
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	drm_mm_takedown(&context->mm);
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	dma_free_wc(context->global->dev, PT_SIZE, v1_context->pgtable_cpu,
408c2ecf20Sopenharmony_ci		    v1_context->pgtable_dma);
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	context->global->v1.shared_context = NULL;
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	kfree(v1_context);
458c2ecf20Sopenharmony_ci}
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_cistatic int etnaviv_iommuv1_map(struct etnaviv_iommu_context *context,
488c2ecf20Sopenharmony_ci			       unsigned long iova, phys_addr_t paddr,
498c2ecf20Sopenharmony_ci			       size_t size, int prot)
508c2ecf20Sopenharmony_ci{
518c2ecf20Sopenharmony_ci	struct etnaviv_iommuv1_context *v1_context = to_v1_context(context);
528c2ecf20Sopenharmony_ci	unsigned int index = (iova - GPU_MEM_START) / SZ_4K;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	if (size != SZ_4K)
558c2ecf20Sopenharmony_ci		return -EINVAL;
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	v1_context->pgtable_cpu[index] = paddr;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	return 0;
608c2ecf20Sopenharmony_ci}
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_cistatic size_t etnaviv_iommuv1_unmap(struct etnaviv_iommu_context *context,
638c2ecf20Sopenharmony_ci	unsigned long iova, size_t size)
648c2ecf20Sopenharmony_ci{
658c2ecf20Sopenharmony_ci	struct etnaviv_iommuv1_context *v1_context = to_v1_context(context);
668c2ecf20Sopenharmony_ci	unsigned int index = (iova - GPU_MEM_START) / SZ_4K;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	if (size != SZ_4K)
698c2ecf20Sopenharmony_ci		return -EINVAL;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	v1_context->pgtable_cpu[index] = context->global->bad_page_dma;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	return SZ_4K;
748c2ecf20Sopenharmony_ci}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_cistatic size_t etnaviv_iommuv1_dump_size(struct etnaviv_iommu_context *context)
778c2ecf20Sopenharmony_ci{
788c2ecf20Sopenharmony_ci	return PT_SIZE;
798c2ecf20Sopenharmony_ci}
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_cistatic void etnaviv_iommuv1_dump(struct etnaviv_iommu_context *context,
828c2ecf20Sopenharmony_ci				 void *buf)
838c2ecf20Sopenharmony_ci{
848c2ecf20Sopenharmony_ci	struct etnaviv_iommuv1_context *v1_context = to_v1_context(context);
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	memcpy(buf, v1_context->pgtable_cpu, PT_SIZE);
878c2ecf20Sopenharmony_ci}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_cistatic void etnaviv_iommuv1_restore(struct etnaviv_gpu *gpu,
908c2ecf20Sopenharmony_ci			     struct etnaviv_iommu_context *context)
918c2ecf20Sopenharmony_ci{
928c2ecf20Sopenharmony_ci	struct etnaviv_iommuv1_context *v1_context = to_v1_context(context);
938c2ecf20Sopenharmony_ci	u32 pgtable;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	if (gpu->mmu_context)
968c2ecf20Sopenharmony_ci		etnaviv_iommu_context_put(gpu->mmu_context);
978c2ecf20Sopenharmony_ci	gpu->mmu_context = etnaviv_iommu_context_get(context);
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	/* set base addresses */
1008c2ecf20Sopenharmony_ci	gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_RA, context->global->memory_base);
1018c2ecf20Sopenharmony_ci	gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_FE, context->global->memory_base);
1028c2ecf20Sopenharmony_ci	gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_TX, context->global->memory_base);
1038c2ecf20Sopenharmony_ci	gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_PEZ, context->global->memory_base);
1048c2ecf20Sopenharmony_ci	gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_PE, context->global->memory_base);
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	/* set page table address in MC */
1078c2ecf20Sopenharmony_ci	pgtable = (u32)v1_context->pgtable_dma;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	gpu_write(gpu, VIVS_MC_MMU_FE_PAGE_TABLE, pgtable);
1108c2ecf20Sopenharmony_ci	gpu_write(gpu, VIVS_MC_MMU_TX_PAGE_TABLE, pgtable);
1118c2ecf20Sopenharmony_ci	gpu_write(gpu, VIVS_MC_MMU_PE_PAGE_TABLE, pgtable);
1128c2ecf20Sopenharmony_ci	gpu_write(gpu, VIVS_MC_MMU_PEZ_PAGE_TABLE, pgtable);
1138c2ecf20Sopenharmony_ci	gpu_write(gpu, VIVS_MC_MMU_RA_PAGE_TABLE, pgtable);
1148c2ecf20Sopenharmony_ci}
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ciconst struct etnaviv_iommu_ops etnaviv_iommuv1_ops = {
1188c2ecf20Sopenharmony_ci	.free = etnaviv_iommuv1_free,
1198c2ecf20Sopenharmony_ci	.map = etnaviv_iommuv1_map,
1208c2ecf20Sopenharmony_ci	.unmap = etnaviv_iommuv1_unmap,
1218c2ecf20Sopenharmony_ci	.dump_size = etnaviv_iommuv1_dump_size,
1228c2ecf20Sopenharmony_ci	.dump = etnaviv_iommuv1_dump,
1238c2ecf20Sopenharmony_ci	.restore = etnaviv_iommuv1_restore,
1248c2ecf20Sopenharmony_ci};
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_cistruct etnaviv_iommu_context *
1278c2ecf20Sopenharmony_cietnaviv_iommuv1_context_alloc(struct etnaviv_iommu_global *global)
1288c2ecf20Sopenharmony_ci{
1298c2ecf20Sopenharmony_ci	struct etnaviv_iommuv1_context *v1_context;
1308c2ecf20Sopenharmony_ci	struct etnaviv_iommu_context *context;
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	mutex_lock(&global->lock);
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	/*
1358c2ecf20Sopenharmony_ci	 * MMUv1 does not support switching between different contexts without
1368c2ecf20Sopenharmony_ci	 * a stop the world operation, so we only support a single shared
1378c2ecf20Sopenharmony_ci	 * context with this version.
1388c2ecf20Sopenharmony_ci	 */
1398c2ecf20Sopenharmony_ci	if (global->v1.shared_context) {
1408c2ecf20Sopenharmony_ci		context = global->v1.shared_context;
1418c2ecf20Sopenharmony_ci		etnaviv_iommu_context_get(context);
1428c2ecf20Sopenharmony_ci		mutex_unlock(&global->lock);
1438c2ecf20Sopenharmony_ci		return context;
1448c2ecf20Sopenharmony_ci	}
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	v1_context = kzalloc(sizeof(*v1_context), GFP_KERNEL);
1478c2ecf20Sopenharmony_ci	if (!v1_context) {
1488c2ecf20Sopenharmony_ci		mutex_unlock(&global->lock);
1498c2ecf20Sopenharmony_ci		return NULL;
1508c2ecf20Sopenharmony_ci	}
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	v1_context->pgtable_cpu = dma_alloc_wc(global->dev, PT_SIZE,
1538c2ecf20Sopenharmony_ci					       &v1_context->pgtable_dma,
1548c2ecf20Sopenharmony_ci					       GFP_KERNEL);
1558c2ecf20Sopenharmony_ci	if (!v1_context->pgtable_cpu)
1568c2ecf20Sopenharmony_ci		goto out_free;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	memset32(v1_context->pgtable_cpu, global->bad_page_dma, PT_ENTRIES);
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	context = &v1_context->base;
1618c2ecf20Sopenharmony_ci	context->global = global;
1628c2ecf20Sopenharmony_ci	kref_init(&context->refcount);
1638c2ecf20Sopenharmony_ci	mutex_init(&context->lock);
1648c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&context->mappings);
1658c2ecf20Sopenharmony_ci	drm_mm_init(&context->mm, GPU_MEM_START, PT_ENTRIES * SZ_4K);
1668c2ecf20Sopenharmony_ci	context->global->v1.shared_context = context;
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	mutex_unlock(&global->lock);
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	return context;
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ciout_free:
1738c2ecf20Sopenharmony_ci	mutex_unlock(&global->lock);
1748c2ecf20Sopenharmony_ci	kfree(v1_context);
1758c2ecf20Sopenharmony_ci	return NULL;
1768c2ecf20Sopenharmony_ci}
177