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