xref: /linux/drivers/gpu/drm/v3d/v3d_mmu.c (revision 57692c94dcbe99a1e0444409a3da13fb3443562c)
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