18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/**************************************************************************
38c2ecf20Sopenharmony_ci * Copyright (c) 2007, Intel Corporation.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci **************************************************************************/
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/highmem.h>
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include "mmu.h"
108c2ecf20Sopenharmony_ci#include "psb_drv.h"
118c2ecf20Sopenharmony_ci#include "psb_reg.h"
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci/*
148c2ecf20Sopenharmony_ci * Code for the SGX MMU:
158c2ecf20Sopenharmony_ci */
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci/*
188c2ecf20Sopenharmony_ci * clflush on one processor only:
198c2ecf20Sopenharmony_ci * clflush should apparently flush the cache line on all processors in an
208c2ecf20Sopenharmony_ci * SMP system.
218c2ecf20Sopenharmony_ci */
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci/*
248c2ecf20Sopenharmony_ci * kmap atomic:
258c2ecf20Sopenharmony_ci * The usage of the slots must be completely encapsulated within a spinlock, and
268c2ecf20Sopenharmony_ci * no other functions that may be using the locks for other purposed may be
278c2ecf20Sopenharmony_ci * called from within the locked region.
288c2ecf20Sopenharmony_ci * Since the slots are per processor, this will guarantee that we are the only
298c2ecf20Sopenharmony_ci * user.
308c2ecf20Sopenharmony_ci */
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci/*
338c2ecf20Sopenharmony_ci * TODO: Inserting ptes from an interrupt handler:
348c2ecf20Sopenharmony_ci * This may be desirable for some SGX functionality where the GPU can fault in
358c2ecf20Sopenharmony_ci * needed pages. For that, we need to make an atomic insert_pages function, that
368c2ecf20Sopenharmony_ci * may fail.
378c2ecf20Sopenharmony_ci * If it fails, the caller need to insert the page using a workqueue function,
388c2ecf20Sopenharmony_ci * but on average it should be fast.
398c2ecf20Sopenharmony_ci */
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistatic inline uint32_t psb_mmu_pt_index(uint32_t offset)
428c2ecf20Sopenharmony_ci{
438c2ecf20Sopenharmony_ci	return (offset >> PSB_PTE_SHIFT) & 0x3FF;
448c2ecf20Sopenharmony_ci}
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_cistatic inline uint32_t psb_mmu_pd_index(uint32_t offset)
478c2ecf20Sopenharmony_ci{
488c2ecf20Sopenharmony_ci	return offset >> PSB_PDE_SHIFT;
498c2ecf20Sopenharmony_ci}
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci#if defined(CONFIG_X86)
528c2ecf20Sopenharmony_cistatic inline void psb_clflush(void *addr)
538c2ecf20Sopenharmony_ci{
548c2ecf20Sopenharmony_ci	__asm__ __volatile__("clflush (%0)\n" : : "r"(addr) : "memory");
558c2ecf20Sopenharmony_ci}
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_cistatic inline void psb_mmu_clflush(struct psb_mmu_driver *driver, void *addr)
588c2ecf20Sopenharmony_ci{
598c2ecf20Sopenharmony_ci	if (!driver->has_clflush)
608c2ecf20Sopenharmony_ci		return;
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	mb();
638c2ecf20Sopenharmony_ci	psb_clflush(addr);
648c2ecf20Sopenharmony_ci	mb();
658c2ecf20Sopenharmony_ci}
668c2ecf20Sopenharmony_ci#else
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_cistatic inline void psb_mmu_clflush(struct psb_mmu_driver *driver, void *addr)
698c2ecf20Sopenharmony_ci{;
708c2ecf20Sopenharmony_ci}
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci#endif
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_cistatic void psb_mmu_flush_pd_locked(struct psb_mmu_driver *driver, int force)
758c2ecf20Sopenharmony_ci{
768c2ecf20Sopenharmony_ci	struct drm_device *dev = driver->dev;
778c2ecf20Sopenharmony_ci	struct drm_psb_private *dev_priv = dev->dev_private;
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	if (atomic_read(&driver->needs_tlbflush) || force) {
808c2ecf20Sopenharmony_ci		uint32_t val = PSB_RSGX32(PSB_CR_BIF_CTRL);
818c2ecf20Sopenharmony_ci		PSB_WSGX32(val | _PSB_CB_CTRL_INVALDC, PSB_CR_BIF_CTRL);
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci		/* Make sure data cache is turned off before enabling it */
848c2ecf20Sopenharmony_ci		wmb();
858c2ecf20Sopenharmony_ci		PSB_WSGX32(val & ~_PSB_CB_CTRL_INVALDC, PSB_CR_BIF_CTRL);
868c2ecf20Sopenharmony_ci		(void)PSB_RSGX32(PSB_CR_BIF_CTRL);
878c2ecf20Sopenharmony_ci		if (driver->msvdx_mmu_invaldc)
888c2ecf20Sopenharmony_ci			atomic_set(driver->msvdx_mmu_invaldc, 1);
898c2ecf20Sopenharmony_ci	}
908c2ecf20Sopenharmony_ci	atomic_set(&driver->needs_tlbflush, 0);
918c2ecf20Sopenharmony_ci}
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci#if 0
948c2ecf20Sopenharmony_cistatic void psb_mmu_flush_pd(struct psb_mmu_driver *driver, int force)
958c2ecf20Sopenharmony_ci{
968c2ecf20Sopenharmony_ci	down_write(&driver->sem);
978c2ecf20Sopenharmony_ci	psb_mmu_flush_pd_locked(driver, force);
988c2ecf20Sopenharmony_ci	up_write(&driver->sem);
998c2ecf20Sopenharmony_ci}
1008c2ecf20Sopenharmony_ci#endif
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_civoid psb_mmu_flush(struct psb_mmu_driver *driver)
1038c2ecf20Sopenharmony_ci{
1048c2ecf20Sopenharmony_ci	struct drm_device *dev = driver->dev;
1058c2ecf20Sopenharmony_ci	struct drm_psb_private *dev_priv = dev->dev_private;
1068c2ecf20Sopenharmony_ci	uint32_t val;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	down_write(&driver->sem);
1098c2ecf20Sopenharmony_ci	val = PSB_RSGX32(PSB_CR_BIF_CTRL);
1108c2ecf20Sopenharmony_ci	if (atomic_read(&driver->needs_tlbflush))
1118c2ecf20Sopenharmony_ci		PSB_WSGX32(val | _PSB_CB_CTRL_INVALDC, PSB_CR_BIF_CTRL);
1128c2ecf20Sopenharmony_ci	else
1138c2ecf20Sopenharmony_ci		PSB_WSGX32(val | _PSB_CB_CTRL_FLUSH, PSB_CR_BIF_CTRL);
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	/* Make sure data cache is turned off and MMU is flushed before
1168c2ecf20Sopenharmony_ci	   restoring bank interface control register */
1178c2ecf20Sopenharmony_ci	wmb();
1188c2ecf20Sopenharmony_ci	PSB_WSGX32(val & ~(_PSB_CB_CTRL_FLUSH | _PSB_CB_CTRL_INVALDC),
1198c2ecf20Sopenharmony_ci		   PSB_CR_BIF_CTRL);
1208c2ecf20Sopenharmony_ci	(void)PSB_RSGX32(PSB_CR_BIF_CTRL);
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	atomic_set(&driver->needs_tlbflush, 0);
1238c2ecf20Sopenharmony_ci	if (driver->msvdx_mmu_invaldc)
1248c2ecf20Sopenharmony_ci		atomic_set(driver->msvdx_mmu_invaldc, 1);
1258c2ecf20Sopenharmony_ci	up_write(&driver->sem);
1268c2ecf20Sopenharmony_ci}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_civoid psb_mmu_set_pd_context(struct psb_mmu_pd *pd, int hw_context)
1298c2ecf20Sopenharmony_ci{
1308c2ecf20Sopenharmony_ci	struct drm_device *dev = pd->driver->dev;
1318c2ecf20Sopenharmony_ci	struct drm_psb_private *dev_priv = dev->dev_private;
1328c2ecf20Sopenharmony_ci	uint32_t offset = (hw_context == 0) ? PSB_CR_BIF_DIR_LIST_BASE0 :
1338c2ecf20Sopenharmony_ci			  PSB_CR_BIF_DIR_LIST_BASE1 + hw_context * 4;
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	down_write(&pd->driver->sem);
1368c2ecf20Sopenharmony_ci	PSB_WSGX32(page_to_pfn(pd->p) << PAGE_SHIFT, offset);
1378c2ecf20Sopenharmony_ci	wmb();
1388c2ecf20Sopenharmony_ci	psb_mmu_flush_pd_locked(pd->driver, 1);
1398c2ecf20Sopenharmony_ci	pd->hw_context = hw_context;
1408c2ecf20Sopenharmony_ci	up_write(&pd->driver->sem);
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_cistatic inline unsigned long psb_pd_addr_end(unsigned long addr,
1458c2ecf20Sopenharmony_ci					    unsigned long end)
1468c2ecf20Sopenharmony_ci{
1478c2ecf20Sopenharmony_ci	addr = (addr + PSB_PDE_MASK + 1) & ~PSB_PDE_MASK;
1488c2ecf20Sopenharmony_ci	return (addr < end) ? addr : end;
1498c2ecf20Sopenharmony_ci}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_cistatic inline uint32_t psb_mmu_mask_pte(uint32_t pfn, int type)
1528c2ecf20Sopenharmony_ci{
1538c2ecf20Sopenharmony_ci	uint32_t mask = PSB_PTE_VALID;
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	if (type & PSB_MMU_CACHED_MEMORY)
1568c2ecf20Sopenharmony_ci		mask |= PSB_PTE_CACHED;
1578c2ecf20Sopenharmony_ci	if (type & PSB_MMU_RO_MEMORY)
1588c2ecf20Sopenharmony_ci		mask |= PSB_PTE_RO;
1598c2ecf20Sopenharmony_ci	if (type & PSB_MMU_WO_MEMORY)
1608c2ecf20Sopenharmony_ci		mask |= PSB_PTE_WO;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	return (pfn << PAGE_SHIFT) | mask;
1638c2ecf20Sopenharmony_ci}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_cistruct psb_mmu_pd *psb_mmu_alloc_pd(struct psb_mmu_driver *driver,
1668c2ecf20Sopenharmony_ci				    int trap_pagefaults, int invalid_type)
1678c2ecf20Sopenharmony_ci{
1688c2ecf20Sopenharmony_ci	struct psb_mmu_pd *pd = kmalloc(sizeof(*pd), GFP_KERNEL);
1698c2ecf20Sopenharmony_ci	uint32_t *v;
1708c2ecf20Sopenharmony_ci	int i;
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	if (!pd)
1738c2ecf20Sopenharmony_ci		return NULL;
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	pd->p = alloc_page(GFP_DMA32);
1768c2ecf20Sopenharmony_ci	if (!pd->p)
1778c2ecf20Sopenharmony_ci		goto out_err1;
1788c2ecf20Sopenharmony_ci	pd->dummy_pt = alloc_page(GFP_DMA32);
1798c2ecf20Sopenharmony_ci	if (!pd->dummy_pt)
1808c2ecf20Sopenharmony_ci		goto out_err2;
1818c2ecf20Sopenharmony_ci	pd->dummy_page = alloc_page(GFP_DMA32);
1828c2ecf20Sopenharmony_ci	if (!pd->dummy_page)
1838c2ecf20Sopenharmony_ci		goto out_err3;
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	if (!trap_pagefaults) {
1868c2ecf20Sopenharmony_ci		pd->invalid_pde = psb_mmu_mask_pte(page_to_pfn(pd->dummy_pt),
1878c2ecf20Sopenharmony_ci						   invalid_type);
1888c2ecf20Sopenharmony_ci		pd->invalid_pte = psb_mmu_mask_pte(page_to_pfn(pd->dummy_page),
1898c2ecf20Sopenharmony_ci						   invalid_type);
1908c2ecf20Sopenharmony_ci	} else {
1918c2ecf20Sopenharmony_ci		pd->invalid_pde = 0;
1928c2ecf20Sopenharmony_ci		pd->invalid_pte = 0;
1938c2ecf20Sopenharmony_ci	}
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	v = kmap(pd->dummy_pt);
1968c2ecf20Sopenharmony_ci	for (i = 0; i < (PAGE_SIZE / sizeof(uint32_t)); ++i)
1978c2ecf20Sopenharmony_ci		v[i] = pd->invalid_pte;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	kunmap(pd->dummy_pt);
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	v = kmap(pd->p);
2028c2ecf20Sopenharmony_ci	for (i = 0; i < (PAGE_SIZE / sizeof(uint32_t)); ++i)
2038c2ecf20Sopenharmony_ci		v[i] = pd->invalid_pde;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	kunmap(pd->p);
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	clear_page(kmap(pd->dummy_page));
2088c2ecf20Sopenharmony_ci	kunmap(pd->dummy_page);
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	pd->tables = vmalloc_user(sizeof(struct psb_mmu_pt *) * 1024);
2118c2ecf20Sopenharmony_ci	if (!pd->tables)
2128c2ecf20Sopenharmony_ci		goto out_err4;
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	pd->hw_context = -1;
2158c2ecf20Sopenharmony_ci	pd->pd_mask = PSB_PTE_VALID;
2168c2ecf20Sopenharmony_ci	pd->driver = driver;
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	return pd;
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ciout_err4:
2218c2ecf20Sopenharmony_ci	__free_page(pd->dummy_page);
2228c2ecf20Sopenharmony_ciout_err3:
2238c2ecf20Sopenharmony_ci	__free_page(pd->dummy_pt);
2248c2ecf20Sopenharmony_ciout_err2:
2258c2ecf20Sopenharmony_ci	__free_page(pd->p);
2268c2ecf20Sopenharmony_ciout_err1:
2278c2ecf20Sopenharmony_ci	kfree(pd);
2288c2ecf20Sopenharmony_ci	return NULL;
2298c2ecf20Sopenharmony_ci}
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_cistatic void psb_mmu_free_pt(struct psb_mmu_pt *pt)
2328c2ecf20Sopenharmony_ci{
2338c2ecf20Sopenharmony_ci	__free_page(pt->p);
2348c2ecf20Sopenharmony_ci	kfree(pt);
2358c2ecf20Sopenharmony_ci}
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_civoid psb_mmu_free_pagedir(struct psb_mmu_pd *pd)
2388c2ecf20Sopenharmony_ci{
2398c2ecf20Sopenharmony_ci	struct psb_mmu_driver *driver = pd->driver;
2408c2ecf20Sopenharmony_ci	struct drm_device *dev = driver->dev;
2418c2ecf20Sopenharmony_ci	struct drm_psb_private *dev_priv = dev->dev_private;
2428c2ecf20Sopenharmony_ci	struct psb_mmu_pt *pt;
2438c2ecf20Sopenharmony_ci	int i;
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	down_write(&driver->sem);
2468c2ecf20Sopenharmony_ci	if (pd->hw_context != -1) {
2478c2ecf20Sopenharmony_ci		PSB_WSGX32(0, PSB_CR_BIF_DIR_LIST_BASE0 + pd->hw_context * 4);
2488c2ecf20Sopenharmony_ci		psb_mmu_flush_pd_locked(driver, 1);
2498c2ecf20Sopenharmony_ci	}
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	/* Should take the spinlock here, but we don't need to do that
2528c2ecf20Sopenharmony_ci	   since we have the semaphore in write mode. */
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	for (i = 0; i < 1024; ++i) {
2558c2ecf20Sopenharmony_ci		pt = pd->tables[i];
2568c2ecf20Sopenharmony_ci		if (pt)
2578c2ecf20Sopenharmony_ci			psb_mmu_free_pt(pt);
2588c2ecf20Sopenharmony_ci	}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	vfree(pd->tables);
2618c2ecf20Sopenharmony_ci	__free_page(pd->dummy_page);
2628c2ecf20Sopenharmony_ci	__free_page(pd->dummy_pt);
2638c2ecf20Sopenharmony_ci	__free_page(pd->p);
2648c2ecf20Sopenharmony_ci	kfree(pd);
2658c2ecf20Sopenharmony_ci	up_write(&driver->sem);
2668c2ecf20Sopenharmony_ci}
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_cistatic struct psb_mmu_pt *psb_mmu_alloc_pt(struct psb_mmu_pd *pd)
2698c2ecf20Sopenharmony_ci{
2708c2ecf20Sopenharmony_ci	struct psb_mmu_pt *pt = kmalloc(sizeof(*pt), GFP_KERNEL);
2718c2ecf20Sopenharmony_ci	void *v;
2728c2ecf20Sopenharmony_ci	uint32_t clflush_add = pd->driver->clflush_add >> PAGE_SHIFT;
2738c2ecf20Sopenharmony_ci	uint32_t clflush_count = PAGE_SIZE / clflush_add;
2748c2ecf20Sopenharmony_ci	spinlock_t *lock = &pd->driver->lock;
2758c2ecf20Sopenharmony_ci	uint8_t *clf;
2768c2ecf20Sopenharmony_ci	uint32_t *ptes;
2778c2ecf20Sopenharmony_ci	int i;
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	if (!pt)
2808c2ecf20Sopenharmony_ci		return NULL;
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	pt->p = alloc_page(GFP_DMA32);
2838c2ecf20Sopenharmony_ci	if (!pt->p) {
2848c2ecf20Sopenharmony_ci		kfree(pt);
2858c2ecf20Sopenharmony_ci		return NULL;
2868c2ecf20Sopenharmony_ci	}
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	spin_lock(lock);
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	v = kmap_atomic(pt->p);
2918c2ecf20Sopenharmony_ci	clf = (uint8_t *) v;
2928c2ecf20Sopenharmony_ci	ptes = (uint32_t *) v;
2938c2ecf20Sopenharmony_ci	for (i = 0; i < (PAGE_SIZE / sizeof(uint32_t)); ++i)
2948c2ecf20Sopenharmony_ci		*ptes++ = pd->invalid_pte;
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci#if defined(CONFIG_X86)
2978c2ecf20Sopenharmony_ci	if (pd->driver->has_clflush && pd->hw_context != -1) {
2988c2ecf20Sopenharmony_ci		mb();
2998c2ecf20Sopenharmony_ci		for (i = 0; i < clflush_count; ++i) {
3008c2ecf20Sopenharmony_ci			psb_clflush(clf);
3018c2ecf20Sopenharmony_ci			clf += clflush_add;
3028c2ecf20Sopenharmony_ci		}
3038c2ecf20Sopenharmony_ci		mb();
3048c2ecf20Sopenharmony_ci	}
3058c2ecf20Sopenharmony_ci#endif
3068c2ecf20Sopenharmony_ci	kunmap_atomic(v);
3078c2ecf20Sopenharmony_ci	spin_unlock(lock);
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	pt->count = 0;
3108c2ecf20Sopenharmony_ci	pt->pd = pd;
3118c2ecf20Sopenharmony_ci	pt->index = 0;
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	return pt;
3148c2ecf20Sopenharmony_ci}
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_cistruct psb_mmu_pt *psb_mmu_pt_alloc_map_lock(struct psb_mmu_pd *pd,
3178c2ecf20Sopenharmony_ci					     unsigned long addr)
3188c2ecf20Sopenharmony_ci{
3198c2ecf20Sopenharmony_ci	uint32_t index = psb_mmu_pd_index(addr);
3208c2ecf20Sopenharmony_ci	struct psb_mmu_pt *pt;
3218c2ecf20Sopenharmony_ci	uint32_t *v;
3228c2ecf20Sopenharmony_ci	spinlock_t *lock = &pd->driver->lock;
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	spin_lock(lock);
3258c2ecf20Sopenharmony_ci	pt = pd->tables[index];
3268c2ecf20Sopenharmony_ci	while (!pt) {
3278c2ecf20Sopenharmony_ci		spin_unlock(lock);
3288c2ecf20Sopenharmony_ci		pt = psb_mmu_alloc_pt(pd);
3298c2ecf20Sopenharmony_ci		if (!pt)
3308c2ecf20Sopenharmony_ci			return NULL;
3318c2ecf20Sopenharmony_ci		spin_lock(lock);
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci		if (pd->tables[index]) {
3348c2ecf20Sopenharmony_ci			spin_unlock(lock);
3358c2ecf20Sopenharmony_ci			psb_mmu_free_pt(pt);
3368c2ecf20Sopenharmony_ci			spin_lock(lock);
3378c2ecf20Sopenharmony_ci			pt = pd->tables[index];
3388c2ecf20Sopenharmony_ci			continue;
3398c2ecf20Sopenharmony_ci		}
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci		v = kmap_atomic(pd->p);
3428c2ecf20Sopenharmony_ci		pd->tables[index] = pt;
3438c2ecf20Sopenharmony_ci		v[index] = (page_to_pfn(pt->p) << 12) | pd->pd_mask;
3448c2ecf20Sopenharmony_ci		pt->index = index;
3458c2ecf20Sopenharmony_ci		kunmap_atomic((void *) v);
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci		if (pd->hw_context != -1) {
3488c2ecf20Sopenharmony_ci			psb_mmu_clflush(pd->driver, (void *)&v[index]);
3498c2ecf20Sopenharmony_ci			atomic_set(&pd->driver->needs_tlbflush, 1);
3508c2ecf20Sopenharmony_ci		}
3518c2ecf20Sopenharmony_ci	}
3528c2ecf20Sopenharmony_ci	pt->v = kmap_atomic(pt->p);
3538c2ecf20Sopenharmony_ci	return pt;
3548c2ecf20Sopenharmony_ci}
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_cistatic struct psb_mmu_pt *psb_mmu_pt_map_lock(struct psb_mmu_pd *pd,
3578c2ecf20Sopenharmony_ci					      unsigned long addr)
3588c2ecf20Sopenharmony_ci{
3598c2ecf20Sopenharmony_ci	uint32_t index = psb_mmu_pd_index(addr);
3608c2ecf20Sopenharmony_ci	struct psb_mmu_pt *pt;
3618c2ecf20Sopenharmony_ci	spinlock_t *lock = &pd->driver->lock;
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci	spin_lock(lock);
3648c2ecf20Sopenharmony_ci	pt = pd->tables[index];
3658c2ecf20Sopenharmony_ci	if (!pt) {
3668c2ecf20Sopenharmony_ci		spin_unlock(lock);
3678c2ecf20Sopenharmony_ci		return NULL;
3688c2ecf20Sopenharmony_ci	}
3698c2ecf20Sopenharmony_ci	pt->v = kmap_atomic(pt->p);
3708c2ecf20Sopenharmony_ci	return pt;
3718c2ecf20Sopenharmony_ci}
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_cistatic void psb_mmu_pt_unmap_unlock(struct psb_mmu_pt *pt)
3748c2ecf20Sopenharmony_ci{
3758c2ecf20Sopenharmony_ci	struct psb_mmu_pd *pd = pt->pd;
3768c2ecf20Sopenharmony_ci	uint32_t *v;
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	kunmap_atomic(pt->v);
3798c2ecf20Sopenharmony_ci	if (pt->count == 0) {
3808c2ecf20Sopenharmony_ci		v = kmap_atomic(pd->p);
3818c2ecf20Sopenharmony_ci		v[pt->index] = pd->invalid_pde;
3828c2ecf20Sopenharmony_ci		pd->tables[pt->index] = NULL;
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci		if (pd->hw_context != -1) {
3858c2ecf20Sopenharmony_ci			psb_mmu_clflush(pd->driver, (void *)&v[pt->index]);
3868c2ecf20Sopenharmony_ci			atomic_set(&pd->driver->needs_tlbflush, 1);
3878c2ecf20Sopenharmony_ci		}
3888c2ecf20Sopenharmony_ci		kunmap_atomic(v);
3898c2ecf20Sopenharmony_ci		spin_unlock(&pd->driver->lock);
3908c2ecf20Sopenharmony_ci		psb_mmu_free_pt(pt);
3918c2ecf20Sopenharmony_ci		return;
3928c2ecf20Sopenharmony_ci	}
3938c2ecf20Sopenharmony_ci	spin_unlock(&pd->driver->lock);
3948c2ecf20Sopenharmony_ci}
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_cistatic inline void psb_mmu_set_pte(struct psb_mmu_pt *pt, unsigned long addr,
3978c2ecf20Sopenharmony_ci				   uint32_t pte)
3988c2ecf20Sopenharmony_ci{
3998c2ecf20Sopenharmony_ci	pt->v[psb_mmu_pt_index(addr)] = pte;
4008c2ecf20Sopenharmony_ci}
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_cistatic inline void psb_mmu_invalidate_pte(struct psb_mmu_pt *pt,
4038c2ecf20Sopenharmony_ci					  unsigned long addr)
4048c2ecf20Sopenharmony_ci{
4058c2ecf20Sopenharmony_ci	pt->v[psb_mmu_pt_index(addr)] = pt->pd->invalid_pte;
4068c2ecf20Sopenharmony_ci}
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_cistruct psb_mmu_pd *psb_mmu_get_default_pd(struct psb_mmu_driver *driver)
4098c2ecf20Sopenharmony_ci{
4108c2ecf20Sopenharmony_ci	struct psb_mmu_pd *pd;
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci	down_read(&driver->sem);
4138c2ecf20Sopenharmony_ci	pd = driver->default_pd;
4148c2ecf20Sopenharmony_ci	up_read(&driver->sem);
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	return pd;
4178c2ecf20Sopenharmony_ci}
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci/* Returns the physical address of the PD shared by sgx/msvdx */
4208c2ecf20Sopenharmony_ciuint32_t psb_get_default_pd_addr(struct psb_mmu_driver *driver)
4218c2ecf20Sopenharmony_ci{
4228c2ecf20Sopenharmony_ci	struct psb_mmu_pd *pd;
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	pd = psb_mmu_get_default_pd(driver);
4258c2ecf20Sopenharmony_ci	return page_to_pfn(pd->p) << PAGE_SHIFT;
4268c2ecf20Sopenharmony_ci}
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_civoid psb_mmu_driver_takedown(struct psb_mmu_driver *driver)
4298c2ecf20Sopenharmony_ci{
4308c2ecf20Sopenharmony_ci	struct drm_device *dev = driver->dev;
4318c2ecf20Sopenharmony_ci	struct drm_psb_private *dev_priv = dev->dev_private;
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	PSB_WSGX32(driver->bif_ctrl, PSB_CR_BIF_CTRL);
4348c2ecf20Sopenharmony_ci	psb_mmu_free_pagedir(driver->default_pd);
4358c2ecf20Sopenharmony_ci	kfree(driver);
4368c2ecf20Sopenharmony_ci}
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_cistruct psb_mmu_driver *psb_mmu_driver_init(struct drm_device *dev,
4398c2ecf20Sopenharmony_ci					   int trap_pagefaults,
4408c2ecf20Sopenharmony_ci					   int invalid_type,
4418c2ecf20Sopenharmony_ci					   atomic_t *msvdx_mmu_invaldc)
4428c2ecf20Sopenharmony_ci{
4438c2ecf20Sopenharmony_ci	struct psb_mmu_driver *driver;
4448c2ecf20Sopenharmony_ci	struct drm_psb_private *dev_priv = dev->dev_private;
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	driver = kmalloc(sizeof(*driver), GFP_KERNEL);
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	if (!driver)
4498c2ecf20Sopenharmony_ci		return NULL;
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	driver->dev = dev;
4528c2ecf20Sopenharmony_ci	driver->default_pd = psb_mmu_alloc_pd(driver, trap_pagefaults,
4538c2ecf20Sopenharmony_ci					      invalid_type);
4548c2ecf20Sopenharmony_ci	if (!driver->default_pd)
4558c2ecf20Sopenharmony_ci		goto out_err1;
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	spin_lock_init(&driver->lock);
4588c2ecf20Sopenharmony_ci	init_rwsem(&driver->sem);
4598c2ecf20Sopenharmony_ci	down_write(&driver->sem);
4608c2ecf20Sopenharmony_ci	atomic_set(&driver->needs_tlbflush, 1);
4618c2ecf20Sopenharmony_ci	driver->msvdx_mmu_invaldc = msvdx_mmu_invaldc;
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	driver->bif_ctrl = PSB_RSGX32(PSB_CR_BIF_CTRL);
4648c2ecf20Sopenharmony_ci	PSB_WSGX32(driver->bif_ctrl | _PSB_CB_CTRL_CLEAR_FAULT,
4658c2ecf20Sopenharmony_ci		   PSB_CR_BIF_CTRL);
4668c2ecf20Sopenharmony_ci	PSB_WSGX32(driver->bif_ctrl & ~_PSB_CB_CTRL_CLEAR_FAULT,
4678c2ecf20Sopenharmony_ci		   PSB_CR_BIF_CTRL);
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	driver->has_clflush = 0;
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci#if defined(CONFIG_X86)
4728c2ecf20Sopenharmony_ci	if (boot_cpu_has(X86_FEATURE_CLFLUSH)) {
4738c2ecf20Sopenharmony_ci		uint32_t tfms, misc, cap0, cap4, clflush_size;
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci		/*
4768c2ecf20Sopenharmony_ci		 * clflush size is determined at kernel setup for x86_64 but not
4778c2ecf20Sopenharmony_ci		 * for i386. We have to do it here.
4788c2ecf20Sopenharmony_ci		 */
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci		cpuid(0x00000001, &tfms, &misc, &cap0, &cap4);
4818c2ecf20Sopenharmony_ci		clflush_size = ((misc >> 8) & 0xff) * 8;
4828c2ecf20Sopenharmony_ci		driver->has_clflush = 1;
4838c2ecf20Sopenharmony_ci		driver->clflush_add =
4848c2ecf20Sopenharmony_ci		    PAGE_SIZE * clflush_size / sizeof(uint32_t);
4858c2ecf20Sopenharmony_ci		driver->clflush_mask = driver->clflush_add - 1;
4868c2ecf20Sopenharmony_ci		driver->clflush_mask = ~driver->clflush_mask;
4878c2ecf20Sopenharmony_ci	}
4888c2ecf20Sopenharmony_ci#endif
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci	up_write(&driver->sem);
4918c2ecf20Sopenharmony_ci	return driver;
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ciout_err1:
4948c2ecf20Sopenharmony_ci	kfree(driver);
4958c2ecf20Sopenharmony_ci	return NULL;
4968c2ecf20Sopenharmony_ci}
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci#if defined(CONFIG_X86)
4998c2ecf20Sopenharmony_cistatic void psb_mmu_flush_ptes(struct psb_mmu_pd *pd, unsigned long address,
5008c2ecf20Sopenharmony_ci			       uint32_t num_pages, uint32_t desired_tile_stride,
5018c2ecf20Sopenharmony_ci			       uint32_t hw_tile_stride)
5028c2ecf20Sopenharmony_ci{
5038c2ecf20Sopenharmony_ci	struct psb_mmu_pt *pt;
5048c2ecf20Sopenharmony_ci	uint32_t rows = 1;
5058c2ecf20Sopenharmony_ci	uint32_t i;
5068c2ecf20Sopenharmony_ci	unsigned long addr;
5078c2ecf20Sopenharmony_ci	unsigned long end;
5088c2ecf20Sopenharmony_ci	unsigned long next;
5098c2ecf20Sopenharmony_ci	unsigned long add;
5108c2ecf20Sopenharmony_ci	unsigned long row_add;
5118c2ecf20Sopenharmony_ci	unsigned long clflush_add = pd->driver->clflush_add;
5128c2ecf20Sopenharmony_ci	unsigned long clflush_mask = pd->driver->clflush_mask;
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci	if (!pd->driver->has_clflush)
5158c2ecf20Sopenharmony_ci		return;
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	if (hw_tile_stride)
5188c2ecf20Sopenharmony_ci		rows = num_pages / desired_tile_stride;
5198c2ecf20Sopenharmony_ci	else
5208c2ecf20Sopenharmony_ci		desired_tile_stride = num_pages;
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci	add = desired_tile_stride << PAGE_SHIFT;
5238c2ecf20Sopenharmony_ci	row_add = hw_tile_stride << PAGE_SHIFT;
5248c2ecf20Sopenharmony_ci	mb();
5258c2ecf20Sopenharmony_ci	for (i = 0; i < rows; ++i) {
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci		addr = address;
5288c2ecf20Sopenharmony_ci		end = addr + add;
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci		do {
5318c2ecf20Sopenharmony_ci			next = psb_pd_addr_end(addr, end);
5328c2ecf20Sopenharmony_ci			pt = psb_mmu_pt_map_lock(pd, addr);
5338c2ecf20Sopenharmony_ci			if (!pt)
5348c2ecf20Sopenharmony_ci				continue;
5358c2ecf20Sopenharmony_ci			do {
5368c2ecf20Sopenharmony_ci				psb_clflush(&pt->v[psb_mmu_pt_index(addr)]);
5378c2ecf20Sopenharmony_ci			} while (addr += clflush_add,
5388c2ecf20Sopenharmony_ci				 (addr & clflush_mask) < next);
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci			psb_mmu_pt_unmap_unlock(pt);
5418c2ecf20Sopenharmony_ci		} while (addr = next, next != end);
5428c2ecf20Sopenharmony_ci		address += row_add;
5438c2ecf20Sopenharmony_ci	}
5448c2ecf20Sopenharmony_ci	mb();
5458c2ecf20Sopenharmony_ci}
5468c2ecf20Sopenharmony_ci#else
5478c2ecf20Sopenharmony_cistatic void psb_mmu_flush_ptes(struct psb_mmu_pd *pd, unsigned long address,
5488c2ecf20Sopenharmony_ci			       uint32_t num_pages, uint32_t desired_tile_stride,
5498c2ecf20Sopenharmony_ci			       uint32_t hw_tile_stride)
5508c2ecf20Sopenharmony_ci{
5518c2ecf20Sopenharmony_ci	drm_ttm_cache_flush();
5528c2ecf20Sopenharmony_ci}
5538c2ecf20Sopenharmony_ci#endif
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_civoid psb_mmu_remove_pfn_sequence(struct psb_mmu_pd *pd,
5568c2ecf20Sopenharmony_ci				 unsigned long address, uint32_t num_pages)
5578c2ecf20Sopenharmony_ci{
5588c2ecf20Sopenharmony_ci	struct psb_mmu_pt *pt;
5598c2ecf20Sopenharmony_ci	unsigned long addr;
5608c2ecf20Sopenharmony_ci	unsigned long end;
5618c2ecf20Sopenharmony_ci	unsigned long next;
5628c2ecf20Sopenharmony_ci	unsigned long f_address = address;
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_ci	down_read(&pd->driver->sem);
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	addr = address;
5678c2ecf20Sopenharmony_ci	end = addr + (num_pages << PAGE_SHIFT);
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci	do {
5708c2ecf20Sopenharmony_ci		next = psb_pd_addr_end(addr, end);
5718c2ecf20Sopenharmony_ci		pt = psb_mmu_pt_alloc_map_lock(pd, addr);
5728c2ecf20Sopenharmony_ci		if (!pt)
5738c2ecf20Sopenharmony_ci			goto out;
5748c2ecf20Sopenharmony_ci		do {
5758c2ecf20Sopenharmony_ci			psb_mmu_invalidate_pte(pt, addr);
5768c2ecf20Sopenharmony_ci			--pt->count;
5778c2ecf20Sopenharmony_ci		} while (addr += PAGE_SIZE, addr < next);
5788c2ecf20Sopenharmony_ci		psb_mmu_pt_unmap_unlock(pt);
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci	} while (addr = next, next != end);
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ciout:
5838c2ecf20Sopenharmony_ci	if (pd->hw_context != -1)
5848c2ecf20Sopenharmony_ci		psb_mmu_flush_ptes(pd, f_address, num_pages, 1, 1);
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci	up_read(&pd->driver->sem);
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci	if (pd->hw_context != -1)
5898c2ecf20Sopenharmony_ci		psb_mmu_flush(pd->driver);
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci	return;
5928c2ecf20Sopenharmony_ci}
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_civoid psb_mmu_remove_pages(struct psb_mmu_pd *pd, unsigned long address,
5958c2ecf20Sopenharmony_ci			  uint32_t num_pages, uint32_t desired_tile_stride,
5968c2ecf20Sopenharmony_ci			  uint32_t hw_tile_stride)
5978c2ecf20Sopenharmony_ci{
5988c2ecf20Sopenharmony_ci	struct psb_mmu_pt *pt;
5998c2ecf20Sopenharmony_ci	uint32_t rows = 1;
6008c2ecf20Sopenharmony_ci	uint32_t i;
6018c2ecf20Sopenharmony_ci	unsigned long addr;
6028c2ecf20Sopenharmony_ci	unsigned long end;
6038c2ecf20Sopenharmony_ci	unsigned long next;
6048c2ecf20Sopenharmony_ci	unsigned long add;
6058c2ecf20Sopenharmony_ci	unsigned long row_add;
6068c2ecf20Sopenharmony_ci	unsigned long f_address = address;
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci	if (hw_tile_stride)
6098c2ecf20Sopenharmony_ci		rows = num_pages / desired_tile_stride;
6108c2ecf20Sopenharmony_ci	else
6118c2ecf20Sopenharmony_ci		desired_tile_stride = num_pages;
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci	add = desired_tile_stride << PAGE_SHIFT;
6148c2ecf20Sopenharmony_ci	row_add = hw_tile_stride << PAGE_SHIFT;
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ci	down_read(&pd->driver->sem);
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci	/* Make sure we only need to flush this processor's cache */
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_ci	for (i = 0; i < rows; ++i) {
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci		addr = address;
6238c2ecf20Sopenharmony_ci		end = addr + add;
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_ci		do {
6268c2ecf20Sopenharmony_ci			next = psb_pd_addr_end(addr, end);
6278c2ecf20Sopenharmony_ci			pt = psb_mmu_pt_map_lock(pd, addr);
6288c2ecf20Sopenharmony_ci			if (!pt)
6298c2ecf20Sopenharmony_ci				continue;
6308c2ecf20Sopenharmony_ci			do {
6318c2ecf20Sopenharmony_ci				psb_mmu_invalidate_pte(pt, addr);
6328c2ecf20Sopenharmony_ci				--pt->count;
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_ci			} while (addr += PAGE_SIZE, addr < next);
6358c2ecf20Sopenharmony_ci			psb_mmu_pt_unmap_unlock(pt);
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_ci		} while (addr = next, next != end);
6388c2ecf20Sopenharmony_ci		address += row_add;
6398c2ecf20Sopenharmony_ci	}
6408c2ecf20Sopenharmony_ci	if (pd->hw_context != -1)
6418c2ecf20Sopenharmony_ci		psb_mmu_flush_ptes(pd, f_address, num_pages,
6428c2ecf20Sopenharmony_ci				   desired_tile_stride, hw_tile_stride);
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_ci	up_read(&pd->driver->sem);
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_ci	if (pd->hw_context != -1)
6478c2ecf20Sopenharmony_ci		psb_mmu_flush(pd->driver);
6488c2ecf20Sopenharmony_ci}
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ciint psb_mmu_insert_pfn_sequence(struct psb_mmu_pd *pd, uint32_t start_pfn,
6518c2ecf20Sopenharmony_ci				unsigned long address, uint32_t num_pages,
6528c2ecf20Sopenharmony_ci				int type)
6538c2ecf20Sopenharmony_ci{
6548c2ecf20Sopenharmony_ci	struct psb_mmu_pt *pt;
6558c2ecf20Sopenharmony_ci	uint32_t pte;
6568c2ecf20Sopenharmony_ci	unsigned long addr;
6578c2ecf20Sopenharmony_ci	unsigned long end;
6588c2ecf20Sopenharmony_ci	unsigned long next;
6598c2ecf20Sopenharmony_ci	unsigned long f_address = address;
6608c2ecf20Sopenharmony_ci	int ret = -ENOMEM;
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ci	down_read(&pd->driver->sem);
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_ci	addr = address;
6658c2ecf20Sopenharmony_ci	end = addr + (num_pages << PAGE_SHIFT);
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_ci	do {
6688c2ecf20Sopenharmony_ci		next = psb_pd_addr_end(addr, end);
6698c2ecf20Sopenharmony_ci		pt = psb_mmu_pt_alloc_map_lock(pd, addr);
6708c2ecf20Sopenharmony_ci		if (!pt) {
6718c2ecf20Sopenharmony_ci			ret = -ENOMEM;
6728c2ecf20Sopenharmony_ci			goto out;
6738c2ecf20Sopenharmony_ci		}
6748c2ecf20Sopenharmony_ci		do {
6758c2ecf20Sopenharmony_ci			pte = psb_mmu_mask_pte(start_pfn++, type);
6768c2ecf20Sopenharmony_ci			psb_mmu_set_pte(pt, addr, pte);
6778c2ecf20Sopenharmony_ci			pt->count++;
6788c2ecf20Sopenharmony_ci		} while (addr += PAGE_SIZE, addr < next);
6798c2ecf20Sopenharmony_ci		psb_mmu_pt_unmap_unlock(pt);
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci	} while (addr = next, next != end);
6828c2ecf20Sopenharmony_ci	ret = 0;
6838c2ecf20Sopenharmony_ci
6848c2ecf20Sopenharmony_ciout:
6858c2ecf20Sopenharmony_ci	if (pd->hw_context != -1)
6868c2ecf20Sopenharmony_ci		psb_mmu_flush_ptes(pd, f_address, num_pages, 1, 1);
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ci	up_read(&pd->driver->sem);
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci	if (pd->hw_context != -1)
6918c2ecf20Sopenharmony_ci		psb_mmu_flush(pd->driver);
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_ci	return 0;
6948c2ecf20Sopenharmony_ci}
6958c2ecf20Sopenharmony_ci
6968c2ecf20Sopenharmony_ciint psb_mmu_insert_pages(struct psb_mmu_pd *pd, struct page **pages,
6978c2ecf20Sopenharmony_ci			 unsigned long address, uint32_t num_pages,
6988c2ecf20Sopenharmony_ci			 uint32_t desired_tile_stride, uint32_t hw_tile_stride,
6998c2ecf20Sopenharmony_ci			 int type)
7008c2ecf20Sopenharmony_ci{
7018c2ecf20Sopenharmony_ci	struct psb_mmu_pt *pt;
7028c2ecf20Sopenharmony_ci	uint32_t rows = 1;
7038c2ecf20Sopenharmony_ci	uint32_t i;
7048c2ecf20Sopenharmony_ci	uint32_t pte;
7058c2ecf20Sopenharmony_ci	unsigned long addr;
7068c2ecf20Sopenharmony_ci	unsigned long end;
7078c2ecf20Sopenharmony_ci	unsigned long next;
7088c2ecf20Sopenharmony_ci	unsigned long add;
7098c2ecf20Sopenharmony_ci	unsigned long row_add;
7108c2ecf20Sopenharmony_ci	unsigned long f_address = address;
7118c2ecf20Sopenharmony_ci	int ret = -ENOMEM;
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ci	if (hw_tile_stride) {
7148c2ecf20Sopenharmony_ci		if (num_pages % desired_tile_stride != 0)
7158c2ecf20Sopenharmony_ci			return -EINVAL;
7168c2ecf20Sopenharmony_ci		rows = num_pages / desired_tile_stride;
7178c2ecf20Sopenharmony_ci	} else {
7188c2ecf20Sopenharmony_ci		desired_tile_stride = num_pages;
7198c2ecf20Sopenharmony_ci	}
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_ci	add = desired_tile_stride << PAGE_SHIFT;
7228c2ecf20Sopenharmony_ci	row_add = hw_tile_stride << PAGE_SHIFT;
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_ci	down_read(&pd->driver->sem);
7258c2ecf20Sopenharmony_ci
7268c2ecf20Sopenharmony_ci	for (i = 0; i < rows; ++i) {
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci		addr = address;
7298c2ecf20Sopenharmony_ci		end = addr + add;
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ci		do {
7328c2ecf20Sopenharmony_ci			next = psb_pd_addr_end(addr, end);
7338c2ecf20Sopenharmony_ci			pt = psb_mmu_pt_alloc_map_lock(pd, addr);
7348c2ecf20Sopenharmony_ci			if (!pt)
7358c2ecf20Sopenharmony_ci				goto out;
7368c2ecf20Sopenharmony_ci			do {
7378c2ecf20Sopenharmony_ci				pte = psb_mmu_mask_pte(page_to_pfn(*pages++),
7388c2ecf20Sopenharmony_ci						       type);
7398c2ecf20Sopenharmony_ci				psb_mmu_set_pte(pt, addr, pte);
7408c2ecf20Sopenharmony_ci				pt->count++;
7418c2ecf20Sopenharmony_ci			} while (addr += PAGE_SIZE, addr < next);
7428c2ecf20Sopenharmony_ci			psb_mmu_pt_unmap_unlock(pt);
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci		} while (addr = next, next != end);
7458c2ecf20Sopenharmony_ci
7468c2ecf20Sopenharmony_ci		address += row_add;
7478c2ecf20Sopenharmony_ci	}
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_ci	ret = 0;
7508c2ecf20Sopenharmony_ciout:
7518c2ecf20Sopenharmony_ci	if (pd->hw_context != -1)
7528c2ecf20Sopenharmony_ci		psb_mmu_flush_ptes(pd, f_address, num_pages,
7538c2ecf20Sopenharmony_ci				   desired_tile_stride, hw_tile_stride);
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_ci	up_read(&pd->driver->sem);
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_ci	if (pd->hw_context != -1)
7588c2ecf20Sopenharmony_ci		psb_mmu_flush(pd->driver);
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_ci	return ret;
7618c2ecf20Sopenharmony_ci}
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_ciint psb_mmu_virtual_to_pfn(struct psb_mmu_pd *pd, uint32_t virtual,
7648c2ecf20Sopenharmony_ci			   unsigned long *pfn)
7658c2ecf20Sopenharmony_ci{
7668c2ecf20Sopenharmony_ci	int ret;
7678c2ecf20Sopenharmony_ci	struct psb_mmu_pt *pt;
7688c2ecf20Sopenharmony_ci	uint32_t tmp;
7698c2ecf20Sopenharmony_ci	spinlock_t *lock = &pd->driver->lock;
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_ci	down_read(&pd->driver->sem);
7728c2ecf20Sopenharmony_ci	pt = psb_mmu_pt_map_lock(pd, virtual);
7738c2ecf20Sopenharmony_ci	if (!pt) {
7748c2ecf20Sopenharmony_ci		uint32_t *v;
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_ci		spin_lock(lock);
7778c2ecf20Sopenharmony_ci		v = kmap_atomic(pd->p);
7788c2ecf20Sopenharmony_ci		tmp = v[psb_mmu_pd_index(virtual)];
7798c2ecf20Sopenharmony_ci		kunmap_atomic(v);
7808c2ecf20Sopenharmony_ci		spin_unlock(lock);
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_ci		if (tmp != pd->invalid_pde || !(tmp & PSB_PTE_VALID) ||
7838c2ecf20Sopenharmony_ci		    !(pd->invalid_pte & PSB_PTE_VALID)) {
7848c2ecf20Sopenharmony_ci			ret = -EINVAL;
7858c2ecf20Sopenharmony_ci			goto out;
7868c2ecf20Sopenharmony_ci		}
7878c2ecf20Sopenharmony_ci		ret = 0;
7888c2ecf20Sopenharmony_ci		*pfn = pd->invalid_pte >> PAGE_SHIFT;
7898c2ecf20Sopenharmony_ci		goto out;
7908c2ecf20Sopenharmony_ci	}
7918c2ecf20Sopenharmony_ci	tmp = pt->v[psb_mmu_pt_index(virtual)];
7928c2ecf20Sopenharmony_ci	if (!(tmp & PSB_PTE_VALID)) {
7938c2ecf20Sopenharmony_ci		ret = -EINVAL;
7948c2ecf20Sopenharmony_ci	} else {
7958c2ecf20Sopenharmony_ci		ret = 0;
7968c2ecf20Sopenharmony_ci		*pfn = tmp >> PAGE_SHIFT;
7978c2ecf20Sopenharmony_ci	}
7988c2ecf20Sopenharmony_ci	psb_mmu_pt_unmap_unlock(pt);
7998c2ecf20Sopenharmony_ciout:
8008c2ecf20Sopenharmony_ci	up_read(&pd->driver->sem);
8018c2ecf20Sopenharmony_ci	return ret;
8028c2ecf20Sopenharmony_ci}
803