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