1*57692c94SEric Anholt // SPDX-License-Identifier: GPL-2.0+ 2*57692c94SEric Anholt /* Copyright (C) 2017-2018 Broadcom */ 3*57692c94SEric Anholt 4*57692c94SEric Anholt /** 5*57692c94SEric Anholt * DOC: Broadcom V3D MMU 6*57692c94SEric Anholt * 7*57692c94SEric Anholt * The V3D 3.x hardware (compared to VC4) now includes an MMU. It has 8*57692c94SEric Anholt * a single level of page tables for the V3D's 4GB address space to 9*57692c94SEric Anholt * map to AXI bus addresses, thus it could need up to 4MB of 10*57692c94SEric Anholt * physically contiguous memory to store the PTEs. 11*57692c94SEric Anholt * 12*57692c94SEric Anholt * Because the 4MB of contiguous memory for page tables is precious, 13*57692c94SEric Anholt * and switching between them is expensive, we load all BOs into the 14*57692c94SEric Anholt * same 4GB address space. 15*57692c94SEric Anholt * 16*57692c94SEric Anholt * To protect clients from each other, we should use the GMP to 17*57692c94SEric Anholt * quickly mask out (at 128kb granularity) what pages are available to 18*57692c94SEric Anholt * each client. This is not yet implemented. 19*57692c94SEric Anholt */ 20*57692c94SEric Anholt 21*57692c94SEric Anholt #include "v3d_drv.h" 22*57692c94SEric Anholt #include "v3d_regs.h" 23*57692c94SEric Anholt 24*57692c94SEric Anholt #define V3D_MMU_PAGE_SHIFT 12 25*57692c94SEric Anholt 26*57692c94SEric Anholt /* Note: All PTEs for the 1MB superpage must be filled with the 27*57692c94SEric Anholt * superpage bit set. 28*57692c94SEric Anholt */ 29*57692c94SEric Anholt #define V3D_PTE_SUPERPAGE BIT(31) 30*57692c94SEric Anholt #define V3D_PTE_WRITEABLE BIT(29) 31*57692c94SEric Anholt #define V3D_PTE_VALID BIT(28) 32*57692c94SEric Anholt 33*57692c94SEric Anholt static int v3d_mmu_flush_all(struct v3d_dev *v3d) 34*57692c94SEric Anholt { 35*57692c94SEric Anholt int ret; 36*57692c94SEric Anholt 37*57692c94SEric Anholt /* Make sure that another flush isn't already running when we 38*57692c94SEric Anholt * start this one. 39*57692c94SEric Anholt */ 40*57692c94SEric Anholt ret = wait_for(!(V3D_READ(V3D_MMU_CTL) & 41*57692c94SEric Anholt V3D_MMU_CTL_TLB_CLEARING), 100); 42*57692c94SEric Anholt if (ret) 43*57692c94SEric Anholt dev_err(v3d->dev, "TLB clear wait idle pre-wait failed\n"); 44*57692c94SEric Anholt 45*57692c94SEric Anholt V3D_WRITE(V3D_MMU_CTL, V3D_READ(V3D_MMU_CTL) | 46*57692c94SEric Anholt V3D_MMU_CTL_TLB_CLEAR); 47*57692c94SEric Anholt 48*57692c94SEric Anholt V3D_WRITE(V3D_MMUC_CONTROL, 49*57692c94SEric Anholt V3D_MMUC_CONTROL_FLUSH | 50*57692c94SEric Anholt V3D_MMUC_CONTROL_ENABLE); 51*57692c94SEric Anholt 52*57692c94SEric Anholt ret = wait_for(!(V3D_READ(V3D_MMU_CTL) & 53*57692c94SEric Anholt V3D_MMU_CTL_TLB_CLEARING), 100); 54*57692c94SEric Anholt if (ret) { 55*57692c94SEric Anholt dev_err(v3d->dev, "TLB clear wait idle failed\n"); 56*57692c94SEric Anholt return ret; 57*57692c94SEric Anholt } 58*57692c94SEric Anholt 59*57692c94SEric Anholt ret = wait_for(!(V3D_READ(V3D_MMUC_CONTROL) & 60*57692c94SEric Anholt V3D_MMUC_CONTROL_FLUSHING), 100); 61*57692c94SEric Anholt if (ret) 62*57692c94SEric Anholt dev_err(v3d->dev, "MMUC flush wait idle failed\n"); 63*57692c94SEric Anholt 64*57692c94SEric Anholt return ret; 65*57692c94SEric Anholt } 66*57692c94SEric Anholt 67*57692c94SEric Anholt int v3d_mmu_set_page_table(struct v3d_dev *v3d) 68*57692c94SEric Anholt { 69*57692c94SEric Anholt V3D_WRITE(V3D_MMU_PT_PA_BASE, v3d->pt_paddr >> V3D_MMU_PAGE_SHIFT); 70*57692c94SEric Anholt V3D_WRITE(V3D_MMU_CTL, 71*57692c94SEric Anholt V3D_MMU_CTL_ENABLE | 72*57692c94SEric Anholt V3D_MMU_CTL_PT_INVALID | 73*57692c94SEric Anholt V3D_MMU_CTL_PT_INVALID_ABORT | 74*57692c94SEric Anholt V3D_MMU_CTL_WRITE_VIOLATION_ABORT | 75*57692c94SEric Anholt V3D_MMU_CTL_CAP_EXCEEDED_ABORT); 76*57692c94SEric Anholt V3D_WRITE(V3D_MMU_ILLEGAL_ADDR, 77*57692c94SEric Anholt (v3d->mmu_scratch_paddr >> V3D_MMU_PAGE_SHIFT) | 78*57692c94SEric Anholt V3D_MMU_ILLEGAL_ADDR_ENABLE); 79*57692c94SEric Anholt V3D_WRITE(V3D_MMUC_CONTROL, V3D_MMUC_CONTROL_ENABLE); 80*57692c94SEric Anholt 81*57692c94SEric Anholt return v3d_mmu_flush_all(v3d); 82*57692c94SEric Anholt } 83*57692c94SEric Anholt 84*57692c94SEric Anholt void v3d_mmu_insert_ptes(struct v3d_bo *bo) 85*57692c94SEric Anholt { 86*57692c94SEric Anholt struct v3d_dev *v3d = to_v3d_dev(bo->base.dev); 87*57692c94SEric Anholt u32 page = bo->node.start; 88*57692c94SEric Anholt u32 page_prot = V3D_PTE_WRITEABLE | V3D_PTE_VALID; 89*57692c94SEric Anholt unsigned int count; 90*57692c94SEric Anholt struct scatterlist *sgl; 91*57692c94SEric Anholt 92*57692c94SEric Anholt for_each_sg(bo->sgt->sgl, sgl, bo->sgt->nents, count) { 93*57692c94SEric Anholt u32 page_address = sg_dma_address(sgl) >> V3D_MMU_PAGE_SHIFT; 94*57692c94SEric Anholt u32 pte = page_prot | page_address; 95*57692c94SEric Anholt u32 i; 96*57692c94SEric Anholt 97*57692c94SEric Anholt BUG_ON(page_address + (sg_dma_len(sgl) >> V3D_MMU_PAGE_SHIFT) >= 98*57692c94SEric Anholt BIT(24)); 99*57692c94SEric Anholt 100*57692c94SEric Anholt for (i = 0; i < sg_dma_len(sgl) >> V3D_MMU_PAGE_SHIFT; i++) 101*57692c94SEric Anholt v3d->pt[page++] = pte + i; 102*57692c94SEric Anholt } 103*57692c94SEric Anholt 104*57692c94SEric Anholt WARN_ON_ONCE(page - bo->node.start != 105*57692c94SEric Anholt bo->base.size >> V3D_MMU_PAGE_SHIFT); 106*57692c94SEric Anholt 107*57692c94SEric Anholt if (v3d_mmu_flush_all(v3d)) 108*57692c94SEric Anholt dev_err(v3d->dev, "MMU flush timeout\n"); 109*57692c94SEric Anholt } 110*57692c94SEric Anholt 111*57692c94SEric Anholt void v3d_mmu_remove_ptes(struct v3d_bo *bo) 112*57692c94SEric Anholt { 113*57692c94SEric Anholt struct v3d_dev *v3d = to_v3d_dev(bo->base.dev); 114*57692c94SEric Anholt u32 npages = bo->base.size >> V3D_MMU_PAGE_SHIFT; 115*57692c94SEric Anholt u32 page; 116*57692c94SEric Anholt 117*57692c94SEric Anholt for (page = bo->node.start; page < bo->node.start + npages; page++) 118*57692c94SEric Anholt v3d->pt[page] = 0; 119*57692c94SEric Anholt 120*57692c94SEric Anholt if (v3d_mmu_flush_all(v3d)) 121*57692c94SEric Anholt dev_err(v3d->dev, "MMU flush timeout\n"); 122*57692c94SEric Anholt } 123