162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2020-2023 Intel Corporation
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/bitfield.h>
762306a36Sopenharmony_ci#include <linux/highmem.h>
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include "ivpu_drv.h"
1062306a36Sopenharmony_ci#include "ivpu_hw.h"
1162306a36Sopenharmony_ci#include "ivpu_mmu.h"
1262306a36Sopenharmony_ci#include "ivpu_mmu_context.h"
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#define IVPU_MMU_VPU_ADDRESS_MASK        GENMASK(47, 12)
1562306a36Sopenharmony_ci#define IVPU_MMU_PGD_INDEX_MASK          GENMASK(47, 39)
1662306a36Sopenharmony_ci#define IVPU_MMU_PUD_INDEX_MASK          GENMASK(38, 30)
1762306a36Sopenharmony_ci#define IVPU_MMU_PMD_INDEX_MASK          GENMASK(29, 21)
1862306a36Sopenharmony_ci#define IVPU_MMU_PTE_INDEX_MASK          GENMASK(20, 12)
1962306a36Sopenharmony_ci#define IVPU_MMU_ENTRY_FLAGS_MASK        (BIT(52) | GENMASK(11, 0))
2062306a36Sopenharmony_ci#define IVPU_MMU_ENTRY_FLAG_CONT         BIT(52)
2162306a36Sopenharmony_ci#define IVPU_MMU_ENTRY_FLAG_NG           BIT(11)
2262306a36Sopenharmony_ci#define IVPU_MMU_ENTRY_FLAG_AF           BIT(10)
2362306a36Sopenharmony_ci#define IVPU_MMU_ENTRY_FLAG_USER         BIT(6)
2462306a36Sopenharmony_ci#define IVPU_MMU_ENTRY_FLAG_LLC_COHERENT BIT(2)
2562306a36Sopenharmony_ci#define IVPU_MMU_ENTRY_FLAG_TYPE_PAGE    BIT(1)
2662306a36Sopenharmony_ci#define IVPU_MMU_ENTRY_FLAG_VALID        BIT(0)
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#define IVPU_MMU_PAGE_SIZE       SZ_4K
2962306a36Sopenharmony_ci#define IVPU_MMU_CONT_PAGES_SIZE (IVPU_MMU_PAGE_SIZE * 16)
3062306a36Sopenharmony_ci#define IVPU_MMU_PTE_MAP_SIZE    (IVPU_MMU_PGTABLE_ENTRIES * IVPU_MMU_PAGE_SIZE)
3162306a36Sopenharmony_ci#define IVPU_MMU_PMD_MAP_SIZE    (IVPU_MMU_PGTABLE_ENTRIES * IVPU_MMU_PTE_MAP_SIZE)
3262306a36Sopenharmony_ci#define IVPU_MMU_PUD_MAP_SIZE    (IVPU_MMU_PGTABLE_ENTRIES * IVPU_MMU_PMD_MAP_SIZE)
3362306a36Sopenharmony_ci#define IVPU_MMU_PGD_MAP_SIZE    (IVPU_MMU_PGTABLE_ENTRIES * IVPU_MMU_PUD_MAP_SIZE)
3462306a36Sopenharmony_ci#define IVPU_MMU_PGTABLE_SIZE    (IVPU_MMU_PGTABLE_ENTRIES * sizeof(u64))
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci#define IVPU_MMU_DUMMY_ADDRESS 0xdeadb000
3762306a36Sopenharmony_ci#define IVPU_MMU_ENTRY_VALID   (IVPU_MMU_ENTRY_FLAG_TYPE_PAGE | IVPU_MMU_ENTRY_FLAG_VALID)
3862306a36Sopenharmony_ci#define IVPU_MMU_ENTRY_INVALID (IVPU_MMU_DUMMY_ADDRESS & ~IVPU_MMU_ENTRY_FLAGS_MASK)
3962306a36Sopenharmony_ci#define IVPU_MMU_ENTRY_MAPPED  (IVPU_MMU_ENTRY_FLAG_AF | IVPU_MMU_ENTRY_FLAG_USER | \
4062306a36Sopenharmony_ci				IVPU_MMU_ENTRY_FLAG_NG | IVPU_MMU_ENTRY_VALID)
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistatic int ivpu_mmu_pgtable_init(struct ivpu_device *vdev, struct ivpu_mmu_pgtable *pgtable)
4362306a36Sopenharmony_ci{
4462306a36Sopenharmony_ci	dma_addr_t pgd_dma;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	pgtable->pgd_dma_ptr = dma_alloc_coherent(vdev->drm.dev, IVPU_MMU_PGTABLE_SIZE, &pgd_dma,
4762306a36Sopenharmony_ci						  GFP_KERNEL);
4862306a36Sopenharmony_ci	if (!pgtable->pgd_dma_ptr)
4962306a36Sopenharmony_ci		return -ENOMEM;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	pgtable->pgd_dma = pgd_dma;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	return 0;
5462306a36Sopenharmony_ci}
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistatic void ivpu_mmu_pgtable_free(struct ivpu_device *vdev, u64 *cpu_addr, dma_addr_t dma_addr)
5762306a36Sopenharmony_ci{
5862306a36Sopenharmony_ci	if (cpu_addr)
5962306a36Sopenharmony_ci		dma_free_coherent(vdev->drm.dev, IVPU_MMU_PGTABLE_SIZE, cpu_addr,
6062306a36Sopenharmony_ci				  dma_addr & ~IVPU_MMU_ENTRY_FLAGS_MASK);
6162306a36Sopenharmony_ci}
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_cistatic void ivpu_mmu_pgtables_free(struct ivpu_device *vdev, struct ivpu_mmu_pgtable *pgtable)
6462306a36Sopenharmony_ci{
6562306a36Sopenharmony_ci	int pgd_idx, pud_idx, pmd_idx;
6662306a36Sopenharmony_ci	dma_addr_t pud_dma, pmd_dma, pte_dma;
6762306a36Sopenharmony_ci	u64 *pud_dma_ptr, *pmd_dma_ptr, *pte_dma_ptr;
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	for (pgd_idx = 0; pgd_idx < IVPU_MMU_PGTABLE_ENTRIES; ++pgd_idx) {
7062306a36Sopenharmony_ci		pud_dma_ptr = pgtable->pud_ptrs[pgd_idx];
7162306a36Sopenharmony_ci		pud_dma = pgtable->pgd_dma_ptr[pgd_idx];
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci		if (!pud_dma_ptr)
7462306a36Sopenharmony_ci			continue;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci		for (pud_idx = 0; pud_idx < IVPU_MMU_PGTABLE_ENTRIES; ++pud_idx) {
7762306a36Sopenharmony_ci			pmd_dma_ptr = pgtable->pmd_ptrs[pgd_idx][pud_idx];
7862306a36Sopenharmony_ci			pmd_dma = pgtable->pud_ptrs[pgd_idx][pud_idx];
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci			if (!pmd_dma_ptr)
8162306a36Sopenharmony_ci				continue;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci			for (pmd_idx = 0; pmd_idx < IVPU_MMU_PGTABLE_ENTRIES; ++pmd_idx) {
8462306a36Sopenharmony_ci				pte_dma_ptr = pgtable->pte_ptrs[pgd_idx][pud_idx][pmd_idx];
8562306a36Sopenharmony_ci				pte_dma = pgtable->pmd_ptrs[pgd_idx][pud_idx][pmd_idx];
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci				ivpu_mmu_pgtable_free(vdev, pte_dma_ptr, pte_dma);
8862306a36Sopenharmony_ci			}
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci			kfree(pgtable->pte_ptrs[pgd_idx][pud_idx]);
9162306a36Sopenharmony_ci			ivpu_mmu_pgtable_free(vdev, pmd_dma_ptr, pmd_dma);
9262306a36Sopenharmony_ci		}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci		kfree(pgtable->pmd_ptrs[pgd_idx]);
9562306a36Sopenharmony_ci		kfree(pgtable->pte_ptrs[pgd_idx]);
9662306a36Sopenharmony_ci		ivpu_mmu_pgtable_free(vdev, pud_dma_ptr, pud_dma);
9762306a36Sopenharmony_ci	}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	ivpu_mmu_pgtable_free(vdev, pgtable->pgd_dma_ptr, pgtable->pgd_dma);
10062306a36Sopenharmony_ci}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_cistatic u64*
10362306a36Sopenharmony_ciivpu_mmu_ensure_pud(struct ivpu_device *vdev, struct ivpu_mmu_pgtable *pgtable, int pgd_idx)
10462306a36Sopenharmony_ci{
10562306a36Sopenharmony_ci	u64 *pud_dma_ptr = pgtable->pud_ptrs[pgd_idx];
10662306a36Sopenharmony_ci	dma_addr_t pud_dma;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	if (pud_dma_ptr)
10962306a36Sopenharmony_ci		return pud_dma_ptr;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	pud_dma_ptr = dma_alloc_wc(vdev->drm.dev, IVPU_MMU_PGTABLE_SIZE, &pud_dma, GFP_KERNEL);
11262306a36Sopenharmony_ci	if (!pud_dma_ptr)
11362306a36Sopenharmony_ci		return NULL;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	drm_WARN_ON(&vdev->drm, pgtable->pmd_ptrs[pgd_idx]);
11662306a36Sopenharmony_ci	pgtable->pmd_ptrs[pgd_idx] = kzalloc(IVPU_MMU_PGTABLE_SIZE, GFP_KERNEL);
11762306a36Sopenharmony_ci	if (!pgtable->pmd_ptrs[pgd_idx])
11862306a36Sopenharmony_ci		goto err_free_pud_dma_ptr;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	drm_WARN_ON(&vdev->drm, pgtable->pte_ptrs[pgd_idx]);
12162306a36Sopenharmony_ci	pgtable->pte_ptrs[pgd_idx] = kzalloc(IVPU_MMU_PGTABLE_SIZE, GFP_KERNEL);
12262306a36Sopenharmony_ci	if (!pgtable->pte_ptrs[pgd_idx])
12362306a36Sopenharmony_ci		goto err_free_pmd_ptrs;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	pgtable->pud_ptrs[pgd_idx] = pud_dma_ptr;
12662306a36Sopenharmony_ci	pgtable->pgd_dma_ptr[pgd_idx] = pud_dma | IVPU_MMU_ENTRY_VALID;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	return pud_dma_ptr;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_cierr_free_pmd_ptrs:
13162306a36Sopenharmony_ci	kfree(pgtable->pmd_ptrs[pgd_idx]);
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_cierr_free_pud_dma_ptr:
13462306a36Sopenharmony_ci	ivpu_mmu_pgtable_free(vdev, pud_dma_ptr, pud_dma);
13562306a36Sopenharmony_ci	return NULL;
13662306a36Sopenharmony_ci}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_cistatic u64*
13962306a36Sopenharmony_ciivpu_mmu_ensure_pmd(struct ivpu_device *vdev, struct ivpu_mmu_pgtable *pgtable, int pgd_idx,
14062306a36Sopenharmony_ci		    int pud_idx)
14162306a36Sopenharmony_ci{
14262306a36Sopenharmony_ci	u64 *pmd_dma_ptr = pgtable->pmd_ptrs[pgd_idx][pud_idx];
14362306a36Sopenharmony_ci	dma_addr_t pmd_dma;
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	if (pmd_dma_ptr)
14662306a36Sopenharmony_ci		return pmd_dma_ptr;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	pmd_dma_ptr = dma_alloc_wc(vdev->drm.dev, IVPU_MMU_PGTABLE_SIZE, &pmd_dma, GFP_KERNEL);
14962306a36Sopenharmony_ci	if (!pmd_dma_ptr)
15062306a36Sopenharmony_ci		return NULL;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	drm_WARN_ON(&vdev->drm, pgtable->pte_ptrs[pgd_idx][pud_idx]);
15362306a36Sopenharmony_ci	pgtable->pte_ptrs[pgd_idx][pud_idx] = kzalloc(IVPU_MMU_PGTABLE_SIZE, GFP_KERNEL);
15462306a36Sopenharmony_ci	if (!pgtable->pte_ptrs[pgd_idx][pud_idx])
15562306a36Sopenharmony_ci		goto err_free_pmd_dma_ptr;
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	pgtable->pmd_ptrs[pgd_idx][pud_idx] = pmd_dma_ptr;
15862306a36Sopenharmony_ci	pgtable->pud_ptrs[pgd_idx][pud_idx] = pmd_dma | IVPU_MMU_ENTRY_VALID;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	return pmd_dma_ptr;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_cierr_free_pmd_dma_ptr:
16362306a36Sopenharmony_ci	ivpu_mmu_pgtable_free(vdev, pmd_dma_ptr, pmd_dma);
16462306a36Sopenharmony_ci	return NULL;
16562306a36Sopenharmony_ci}
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_cistatic u64*
16862306a36Sopenharmony_ciivpu_mmu_ensure_pte(struct ivpu_device *vdev, struct ivpu_mmu_pgtable *pgtable,
16962306a36Sopenharmony_ci		    int pgd_idx, int pud_idx, int pmd_idx)
17062306a36Sopenharmony_ci{
17162306a36Sopenharmony_ci	u64 *pte_dma_ptr = pgtable->pte_ptrs[pgd_idx][pud_idx][pmd_idx];
17262306a36Sopenharmony_ci	dma_addr_t pte_dma;
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	if (pte_dma_ptr)
17562306a36Sopenharmony_ci		return pte_dma_ptr;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	pte_dma_ptr = dma_alloc_wc(vdev->drm.dev, IVPU_MMU_PGTABLE_SIZE, &pte_dma, GFP_KERNEL);
17862306a36Sopenharmony_ci	if (!pte_dma_ptr)
17962306a36Sopenharmony_ci		return NULL;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	pgtable->pte_ptrs[pgd_idx][pud_idx][pmd_idx] = pte_dma_ptr;
18262306a36Sopenharmony_ci	pgtable->pmd_ptrs[pgd_idx][pud_idx][pmd_idx] = pte_dma | IVPU_MMU_ENTRY_VALID;
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	return pte_dma_ptr;
18562306a36Sopenharmony_ci}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_cistatic int
18862306a36Sopenharmony_ciivpu_mmu_context_map_page(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx,
18962306a36Sopenharmony_ci			  u64 vpu_addr, dma_addr_t dma_addr, u64 prot)
19062306a36Sopenharmony_ci{
19162306a36Sopenharmony_ci	u64 *pte;
19262306a36Sopenharmony_ci	int pgd_idx = FIELD_GET(IVPU_MMU_PGD_INDEX_MASK, vpu_addr);
19362306a36Sopenharmony_ci	int pud_idx = FIELD_GET(IVPU_MMU_PUD_INDEX_MASK, vpu_addr);
19462306a36Sopenharmony_ci	int pmd_idx = FIELD_GET(IVPU_MMU_PMD_INDEX_MASK, vpu_addr);
19562306a36Sopenharmony_ci	int pte_idx = FIELD_GET(IVPU_MMU_PTE_INDEX_MASK, vpu_addr);
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	/* Allocate PUD - second level page table if needed */
19862306a36Sopenharmony_ci	if (!ivpu_mmu_ensure_pud(vdev, &ctx->pgtable, pgd_idx))
19962306a36Sopenharmony_ci		return -ENOMEM;
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	/* Allocate PMD - third level page table if needed */
20262306a36Sopenharmony_ci	if (!ivpu_mmu_ensure_pmd(vdev, &ctx->pgtable, pgd_idx, pud_idx))
20362306a36Sopenharmony_ci		return -ENOMEM;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	/* Allocate PTE - fourth level page table if needed */
20662306a36Sopenharmony_ci	pte = ivpu_mmu_ensure_pte(vdev, &ctx->pgtable, pgd_idx, pud_idx, pmd_idx);
20762306a36Sopenharmony_ci	if (!pte)
20862306a36Sopenharmony_ci		return -ENOMEM;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	/* Update PTE */
21162306a36Sopenharmony_ci	pte[pte_idx] = dma_addr | prot;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	return 0;
21462306a36Sopenharmony_ci}
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_cistatic int
21762306a36Sopenharmony_ciivpu_mmu_context_map_cont_64k(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx, u64 vpu_addr,
21862306a36Sopenharmony_ci			      dma_addr_t dma_addr, u64 prot)
21962306a36Sopenharmony_ci{
22062306a36Sopenharmony_ci	size_t size = IVPU_MMU_CONT_PAGES_SIZE;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	drm_WARN_ON(&vdev->drm, !IS_ALIGNED(vpu_addr, size));
22362306a36Sopenharmony_ci	drm_WARN_ON(&vdev->drm, !IS_ALIGNED(dma_addr, size));
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	prot |= IVPU_MMU_ENTRY_FLAG_CONT;
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	while (size) {
22862306a36Sopenharmony_ci		int ret = ivpu_mmu_context_map_page(vdev, ctx, vpu_addr, dma_addr, prot);
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci		if (ret)
23162306a36Sopenharmony_ci			return ret;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci		size -= IVPU_MMU_PAGE_SIZE;
23462306a36Sopenharmony_ci		vpu_addr += IVPU_MMU_PAGE_SIZE;
23562306a36Sopenharmony_ci		dma_addr += IVPU_MMU_PAGE_SIZE;
23662306a36Sopenharmony_ci	}
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	return 0;
23962306a36Sopenharmony_ci}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_cistatic void ivpu_mmu_context_unmap_page(struct ivpu_mmu_context *ctx, u64 vpu_addr)
24262306a36Sopenharmony_ci{
24362306a36Sopenharmony_ci	int pgd_idx = FIELD_GET(IVPU_MMU_PGD_INDEX_MASK, vpu_addr);
24462306a36Sopenharmony_ci	int pud_idx = FIELD_GET(IVPU_MMU_PUD_INDEX_MASK, vpu_addr);
24562306a36Sopenharmony_ci	int pmd_idx = FIELD_GET(IVPU_MMU_PMD_INDEX_MASK, vpu_addr);
24662306a36Sopenharmony_ci	int pte_idx = FIELD_GET(IVPU_MMU_PTE_INDEX_MASK, vpu_addr);
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	/* Update PTE with dummy physical address and clear flags */
24962306a36Sopenharmony_ci	ctx->pgtable.pte_ptrs[pgd_idx][pud_idx][pmd_idx][pte_idx] = IVPU_MMU_ENTRY_INVALID;
25062306a36Sopenharmony_ci}
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_cistatic void
25362306a36Sopenharmony_ciivpu_mmu_context_flush_page_tables(struct ivpu_mmu_context *ctx, u64 vpu_addr, size_t size)
25462306a36Sopenharmony_ci{
25562306a36Sopenharmony_ci	struct ivpu_mmu_pgtable *pgtable = &ctx->pgtable;
25662306a36Sopenharmony_ci	u64 end_addr = vpu_addr + size;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	/* Align to PMD entry (2 MB) */
25962306a36Sopenharmony_ci	vpu_addr &= ~(IVPU_MMU_PTE_MAP_SIZE - 1);
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	while (vpu_addr < end_addr) {
26262306a36Sopenharmony_ci		int pgd_idx = FIELD_GET(IVPU_MMU_PGD_INDEX_MASK, vpu_addr);
26362306a36Sopenharmony_ci		u64 pud_end = (pgd_idx + 1) * (u64)IVPU_MMU_PUD_MAP_SIZE;
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci		while (vpu_addr < end_addr && vpu_addr < pud_end) {
26662306a36Sopenharmony_ci			int pud_idx = FIELD_GET(IVPU_MMU_PUD_INDEX_MASK, vpu_addr);
26762306a36Sopenharmony_ci			u64 pmd_end = (pud_idx + 1) * (u64)IVPU_MMU_PMD_MAP_SIZE;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci			while (vpu_addr < end_addr && vpu_addr < pmd_end) {
27062306a36Sopenharmony_ci				int pmd_idx = FIELD_GET(IVPU_MMU_PMD_INDEX_MASK, vpu_addr);
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci				clflush_cache_range(pgtable->pte_ptrs[pgd_idx][pud_idx][pmd_idx],
27362306a36Sopenharmony_ci						    IVPU_MMU_PGTABLE_SIZE);
27462306a36Sopenharmony_ci				vpu_addr += IVPU_MMU_PTE_MAP_SIZE;
27562306a36Sopenharmony_ci			}
27662306a36Sopenharmony_ci			clflush_cache_range(pgtable->pmd_ptrs[pgd_idx][pud_idx],
27762306a36Sopenharmony_ci					    IVPU_MMU_PGTABLE_SIZE);
27862306a36Sopenharmony_ci		}
27962306a36Sopenharmony_ci		clflush_cache_range(pgtable->pud_ptrs[pgd_idx], IVPU_MMU_PGTABLE_SIZE);
28062306a36Sopenharmony_ci	}
28162306a36Sopenharmony_ci	clflush_cache_range(pgtable->pgd_dma_ptr, IVPU_MMU_PGTABLE_SIZE);
28262306a36Sopenharmony_ci}
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_cistatic int
28562306a36Sopenharmony_ciivpu_mmu_context_map_pages(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx,
28662306a36Sopenharmony_ci			   u64 vpu_addr, dma_addr_t dma_addr, size_t size, u64 prot)
28762306a36Sopenharmony_ci{
28862306a36Sopenharmony_ci	int map_size;
28962306a36Sopenharmony_ci	int ret;
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	while (size) {
29262306a36Sopenharmony_ci		if (!ivpu_disable_mmu_cont_pages && size >= IVPU_MMU_CONT_PAGES_SIZE &&
29362306a36Sopenharmony_ci		    IS_ALIGNED(vpu_addr | dma_addr, IVPU_MMU_CONT_PAGES_SIZE)) {
29462306a36Sopenharmony_ci			ret = ivpu_mmu_context_map_cont_64k(vdev, ctx, vpu_addr, dma_addr, prot);
29562306a36Sopenharmony_ci			map_size = IVPU_MMU_CONT_PAGES_SIZE;
29662306a36Sopenharmony_ci		} else {
29762306a36Sopenharmony_ci			ret = ivpu_mmu_context_map_page(vdev, ctx, vpu_addr, dma_addr, prot);
29862306a36Sopenharmony_ci			map_size = IVPU_MMU_PAGE_SIZE;
29962306a36Sopenharmony_ci		}
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci		if (ret)
30262306a36Sopenharmony_ci			return ret;
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci		vpu_addr += map_size;
30562306a36Sopenharmony_ci		dma_addr += map_size;
30662306a36Sopenharmony_ci		size -= map_size;
30762306a36Sopenharmony_ci	}
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	return 0;
31062306a36Sopenharmony_ci}
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_cistatic void ivpu_mmu_context_unmap_pages(struct ivpu_mmu_context *ctx, u64 vpu_addr, size_t size)
31362306a36Sopenharmony_ci{
31462306a36Sopenharmony_ci	while (size) {
31562306a36Sopenharmony_ci		ivpu_mmu_context_unmap_page(ctx, vpu_addr);
31662306a36Sopenharmony_ci		vpu_addr += IVPU_MMU_PAGE_SIZE;
31762306a36Sopenharmony_ci		size -= IVPU_MMU_PAGE_SIZE;
31862306a36Sopenharmony_ci	}
31962306a36Sopenharmony_ci}
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ciint
32262306a36Sopenharmony_ciivpu_mmu_context_map_sgt(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx,
32362306a36Sopenharmony_ci			 u64 vpu_addr, struct sg_table *sgt,  bool llc_coherent)
32462306a36Sopenharmony_ci{
32562306a36Sopenharmony_ci	struct scatterlist *sg;
32662306a36Sopenharmony_ci	int ret;
32762306a36Sopenharmony_ci	u64 prot;
32862306a36Sopenharmony_ci	u64 i;
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	if (!IS_ALIGNED(vpu_addr, IVPU_MMU_PAGE_SIZE))
33162306a36Sopenharmony_ci		return -EINVAL;
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	if (vpu_addr & ~IVPU_MMU_VPU_ADDRESS_MASK)
33462306a36Sopenharmony_ci		return -EINVAL;
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	prot = IVPU_MMU_ENTRY_MAPPED;
33762306a36Sopenharmony_ci	if (llc_coherent)
33862306a36Sopenharmony_ci		prot |= IVPU_MMU_ENTRY_FLAG_LLC_COHERENT;
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	mutex_lock(&ctx->lock);
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	for_each_sgtable_dma_sg(sgt, sg, i) {
34362306a36Sopenharmony_ci		dma_addr_t dma_addr = sg_dma_address(sg) - sg->offset;
34462306a36Sopenharmony_ci		size_t size = sg_dma_len(sg) + sg->offset;
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci		ret = ivpu_mmu_context_map_pages(vdev, ctx, vpu_addr, dma_addr, size, prot);
34762306a36Sopenharmony_ci		if (ret) {
34862306a36Sopenharmony_ci			ivpu_err(vdev, "Failed to map context pages\n");
34962306a36Sopenharmony_ci			mutex_unlock(&ctx->lock);
35062306a36Sopenharmony_ci			return ret;
35162306a36Sopenharmony_ci		}
35262306a36Sopenharmony_ci		ivpu_mmu_context_flush_page_tables(ctx, vpu_addr, size);
35362306a36Sopenharmony_ci		vpu_addr += size;
35462306a36Sopenharmony_ci	}
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	mutex_unlock(&ctx->lock);
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	ret = ivpu_mmu_invalidate_tlb(vdev, ctx->id);
35962306a36Sopenharmony_ci	if (ret)
36062306a36Sopenharmony_ci		ivpu_err(vdev, "Failed to invalidate TLB for ctx %u: %d\n", ctx->id, ret);
36162306a36Sopenharmony_ci	return ret;
36262306a36Sopenharmony_ci}
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_civoid
36562306a36Sopenharmony_ciivpu_mmu_context_unmap_sgt(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx,
36662306a36Sopenharmony_ci			   u64 vpu_addr, struct sg_table *sgt)
36762306a36Sopenharmony_ci{
36862306a36Sopenharmony_ci	struct scatterlist *sg;
36962306a36Sopenharmony_ci	int ret;
37062306a36Sopenharmony_ci	u64 i;
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	if (!IS_ALIGNED(vpu_addr, IVPU_MMU_PAGE_SIZE))
37362306a36Sopenharmony_ci		ivpu_warn(vdev, "Unaligned vpu_addr: 0x%llx\n", vpu_addr);
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	mutex_lock(&ctx->lock);
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	for_each_sgtable_dma_sg(sgt, sg, i) {
37862306a36Sopenharmony_ci		size_t size = sg_dma_len(sg) + sg->offset;
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci		ivpu_mmu_context_unmap_pages(ctx, vpu_addr, size);
38162306a36Sopenharmony_ci		ivpu_mmu_context_flush_page_tables(ctx, vpu_addr, size);
38262306a36Sopenharmony_ci		vpu_addr += size;
38362306a36Sopenharmony_ci	}
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	mutex_unlock(&ctx->lock);
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	ret = ivpu_mmu_invalidate_tlb(vdev, ctx->id);
38862306a36Sopenharmony_ci	if (ret)
38962306a36Sopenharmony_ci		ivpu_warn(vdev, "Failed to invalidate TLB for ctx %u: %d\n", ctx->id, ret);
39062306a36Sopenharmony_ci}
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ciint
39362306a36Sopenharmony_ciivpu_mmu_context_insert_node_locked(struct ivpu_mmu_context *ctx,
39462306a36Sopenharmony_ci				    const struct ivpu_addr_range *range,
39562306a36Sopenharmony_ci				    u64 size, struct drm_mm_node *node)
39662306a36Sopenharmony_ci{
39762306a36Sopenharmony_ci	lockdep_assert_held(&ctx->lock);
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	if (!ivpu_disable_mmu_cont_pages && size >= IVPU_MMU_CONT_PAGES_SIZE) {
40062306a36Sopenharmony_ci		if (!drm_mm_insert_node_in_range(&ctx->mm, node, size, IVPU_MMU_CONT_PAGES_SIZE, 0,
40162306a36Sopenharmony_ci						 range->start, range->end, DRM_MM_INSERT_BEST))
40262306a36Sopenharmony_ci			return 0;
40362306a36Sopenharmony_ci	}
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	return drm_mm_insert_node_in_range(&ctx->mm, node, size, IVPU_MMU_PAGE_SIZE, 0,
40662306a36Sopenharmony_ci					   range->start, range->end, DRM_MM_INSERT_BEST);
40762306a36Sopenharmony_ci}
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_civoid
41062306a36Sopenharmony_ciivpu_mmu_context_remove_node_locked(struct ivpu_mmu_context *ctx, struct drm_mm_node *node)
41162306a36Sopenharmony_ci{
41262306a36Sopenharmony_ci	lockdep_assert_held(&ctx->lock);
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	drm_mm_remove_node(node);
41562306a36Sopenharmony_ci}
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_cistatic int
41862306a36Sopenharmony_ciivpu_mmu_context_init(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx, u32 context_id)
41962306a36Sopenharmony_ci{
42062306a36Sopenharmony_ci	u64 start, end;
42162306a36Sopenharmony_ci	int ret;
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	mutex_init(&ctx->lock);
42462306a36Sopenharmony_ci	INIT_LIST_HEAD(&ctx->bo_list);
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	ret = ivpu_mmu_pgtable_init(vdev, &ctx->pgtable);
42762306a36Sopenharmony_ci	if (ret)
42862306a36Sopenharmony_ci		return ret;
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	if (!context_id) {
43162306a36Sopenharmony_ci		start = vdev->hw->ranges.global.start;
43262306a36Sopenharmony_ci		end = vdev->hw->ranges.shave.end;
43362306a36Sopenharmony_ci	} else {
43462306a36Sopenharmony_ci		start = vdev->hw->ranges.user.start;
43562306a36Sopenharmony_ci		end = vdev->hw->ranges.dma.end;
43662306a36Sopenharmony_ci	}
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	drm_mm_init(&ctx->mm, start, end - start);
43962306a36Sopenharmony_ci	ctx->id = context_id;
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	return 0;
44262306a36Sopenharmony_ci}
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_cistatic void ivpu_mmu_context_fini(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx)
44562306a36Sopenharmony_ci{
44662306a36Sopenharmony_ci	if (drm_WARN_ON(&vdev->drm, !ctx->pgtable.pgd_dma_ptr))
44762306a36Sopenharmony_ci		return;
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	mutex_destroy(&ctx->lock);
45062306a36Sopenharmony_ci	ivpu_mmu_pgtables_free(vdev, &ctx->pgtable);
45162306a36Sopenharmony_ci	drm_mm_takedown(&ctx->mm);
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	ctx->pgtable.pgd_dma_ptr = NULL;
45462306a36Sopenharmony_ci	ctx->pgtable.pgd_dma = 0;
45562306a36Sopenharmony_ci}
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ciint ivpu_mmu_global_context_init(struct ivpu_device *vdev)
45862306a36Sopenharmony_ci{
45962306a36Sopenharmony_ci	return ivpu_mmu_context_init(vdev, &vdev->gctx, IVPU_GLOBAL_CONTEXT_MMU_SSID);
46062306a36Sopenharmony_ci}
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_civoid ivpu_mmu_global_context_fini(struct ivpu_device *vdev)
46362306a36Sopenharmony_ci{
46462306a36Sopenharmony_ci	return ivpu_mmu_context_fini(vdev, &vdev->gctx);
46562306a36Sopenharmony_ci}
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_civoid ivpu_mmu_user_context_mark_invalid(struct ivpu_device *vdev, u32 ssid)
46862306a36Sopenharmony_ci{
46962306a36Sopenharmony_ci	struct ivpu_file_priv *file_priv;
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	xa_lock(&vdev->context_xa);
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	file_priv = xa_load(&vdev->context_xa, ssid);
47462306a36Sopenharmony_ci	if (file_priv)
47562306a36Sopenharmony_ci		file_priv->has_mmu_faults = true;
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	xa_unlock(&vdev->context_xa);
47862306a36Sopenharmony_ci}
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ciint ivpu_mmu_user_context_init(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx, u32 ctx_id)
48162306a36Sopenharmony_ci{
48262306a36Sopenharmony_ci	int ret;
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	drm_WARN_ON(&vdev->drm, !ctx_id);
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	ret = ivpu_mmu_context_init(vdev, ctx, ctx_id);
48762306a36Sopenharmony_ci	if (ret) {
48862306a36Sopenharmony_ci		ivpu_err(vdev, "Failed to initialize context: %d\n", ret);
48962306a36Sopenharmony_ci		return ret;
49062306a36Sopenharmony_ci	}
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	ret = ivpu_mmu_set_pgtable(vdev, ctx_id, &ctx->pgtable);
49362306a36Sopenharmony_ci	if (ret) {
49462306a36Sopenharmony_ci		ivpu_err(vdev, "Failed to set page table: %d\n", ret);
49562306a36Sopenharmony_ci		goto err_context_fini;
49662306a36Sopenharmony_ci	}
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci	return 0;
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_cierr_context_fini:
50162306a36Sopenharmony_ci	ivpu_mmu_context_fini(vdev, ctx);
50262306a36Sopenharmony_ci	return ret;
50362306a36Sopenharmony_ci}
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_civoid ivpu_mmu_user_context_fini(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx)
50662306a36Sopenharmony_ci{
50762306a36Sopenharmony_ci	drm_WARN_ON(&vdev->drm, !ctx->id);
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	ivpu_mmu_clear_pgtable(vdev, ctx->id);
51062306a36Sopenharmony_ci	ivpu_mmu_context_fini(vdev, ctx);
51162306a36Sopenharmony_ci}
512