xref: /OK3568_Linux_fs/kernel/drivers/misc/habanalabs/common/mmu.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun 
3*4882a593Smuzhiyun /*
4*4882a593Smuzhiyun  * Copyright 2016-2020 HabanaLabs, Ltd.
5*4882a593Smuzhiyun  * All Rights Reserved.
6*4882a593Smuzhiyun  */
7*4882a593Smuzhiyun 
8*4882a593Smuzhiyun #include <linux/slab.h>
9*4882a593Smuzhiyun 
10*4882a593Smuzhiyun #include "habanalabs.h"
11*4882a593Smuzhiyun 
is_dram_va(struct hl_device * hdev,u64 virt_addr)12*4882a593Smuzhiyun static bool is_dram_va(struct hl_device *hdev, u64 virt_addr)
13*4882a593Smuzhiyun {
14*4882a593Smuzhiyun 	struct asic_fixed_properties *prop = &hdev->asic_prop;
15*4882a593Smuzhiyun 
16*4882a593Smuzhiyun 	return hl_mem_area_inside_range(virt_addr, prop->dmmu.page_size,
17*4882a593Smuzhiyun 					prop->dmmu.start_addr,
18*4882a593Smuzhiyun 					prop->dmmu.end_addr);
19*4882a593Smuzhiyun }
20*4882a593Smuzhiyun 
21*4882a593Smuzhiyun /**
22*4882a593Smuzhiyun  * hl_mmu_init() - initialize the MMU module.
23*4882a593Smuzhiyun  * @hdev: habanalabs device structure.
24*4882a593Smuzhiyun  *
25*4882a593Smuzhiyun  * This function does the following:
26*4882a593Smuzhiyun  * - Create a pool of pages for pgt_infos.
27*4882a593Smuzhiyun  * - Create a shadow table for pgt
28*4882a593Smuzhiyun  *
29*4882a593Smuzhiyun  * Return: 0 for success, non-zero for failure.
30*4882a593Smuzhiyun  */
hl_mmu_init(struct hl_device * hdev)31*4882a593Smuzhiyun int hl_mmu_init(struct hl_device *hdev)
32*4882a593Smuzhiyun {
33*4882a593Smuzhiyun 	if (hdev->mmu_enable)
34*4882a593Smuzhiyun 		return hdev->mmu_func.init(hdev);
35*4882a593Smuzhiyun 
36*4882a593Smuzhiyun 	return 0;
37*4882a593Smuzhiyun }
38*4882a593Smuzhiyun 
39*4882a593Smuzhiyun /**
40*4882a593Smuzhiyun  * hl_mmu_fini() - release the MMU module.
41*4882a593Smuzhiyun  * @hdev: habanalabs device structure.
42*4882a593Smuzhiyun  *
43*4882a593Smuzhiyun  * This function does the following:
44*4882a593Smuzhiyun  * - Disable MMU in H/W.
45*4882a593Smuzhiyun  * - Free the pgt_infos pool.
46*4882a593Smuzhiyun  *
47*4882a593Smuzhiyun  * All contexts should be freed before calling this function.
48*4882a593Smuzhiyun  */
hl_mmu_fini(struct hl_device * hdev)49*4882a593Smuzhiyun void hl_mmu_fini(struct hl_device *hdev)
50*4882a593Smuzhiyun {
51*4882a593Smuzhiyun 	if (hdev->mmu_enable)
52*4882a593Smuzhiyun 		hdev->mmu_func.fini(hdev);
53*4882a593Smuzhiyun }
54*4882a593Smuzhiyun 
55*4882a593Smuzhiyun /**
56*4882a593Smuzhiyun  * hl_mmu_ctx_init() - initialize a context for using the MMU module.
57*4882a593Smuzhiyun  * @ctx: pointer to the context structure to initialize.
58*4882a593Smuzhiyun  *
59*4882a593Smuzhiyun  * Initialize a mutex to protect the concurrent mapping flow, a hash to hold all
60*4882a593Smuzhiyun  * page tables hops related to this context.
61*4882a593Smuzhiyun  * Return: 0 on success, non-zero otherwise.
62*4882a593Smuzhiyun  */
hl_mmu_ctx_init(struct hl_ctx * ctx)63*4882a593Smuzhiyun int hl_mmu_ctx_init(struct hl_ctx *ctx)
64*4882a593Smuzhiyun {
65*4882a593Smuzhiyun 	struct hl_device *hdev = ctx->hdev;
66*4882a593Smuzhiyun 
67*4882a593Smuzhiyun 	if (hdev->mmu_enable)
68*4882a593Smuzhiyun 		return hdev->mmu_func.ctx_init(ctx);
69*4882a593Smuzhiyun 
70*4882a593Smuzhiyun 	return 0;
71*4882a593Smuzhiyun }
72*4882a593Smuzhiyun 
73*4882a593Smuzhiyun /*
74*4882a593Smuzhiyun  * hl_mmu_ctx_fini - disable a ctx from using the mmu module
75*4882a593Smuzhiyun  *
76*4882a593Smuzhiyun  * @ctx: pointer to the context structure
77*4882a593Smuzhiyun  *
78*4882a593Smuzhiyun  * This function does the following:
79*4882a593Smuzhiyun  * - Free any pgts which were not freed yet
80*4882a593Smuzhiyun  * - Free the mutex
81*4882a593Smuzhiyun  * - Free DRAM default page mapping hops
82*4882a593Smuzhiyun  */
hl_mmu_ctx_fini(struct hl_ctx * ctx)83*4882a593Smuzhiyun void hl_mmu_ctx_fini(struct hl_ctx *ctx)
84*4882a593Smuzhiyun {
85*4882a593Smuzhiyun 	struct hl_device *hdev = ctx->hdev;
86*4882a593Smuzhiyun 
87*4882a593Smuzhiyun 	if (hdev->mmu_enable)
88*4882a593Smuzhiyun 		hdev->mmu_func.ctx_fini(ctx);
89*4882a593Smuzhiyun }
90*4882a593Smuzhiyun 
91*4882a593Smuzhiyun /*
92*4882a593Smuzhiyun  * hl_mmu_unmap - unmaps a virtual addr
93*4882a593Smuzhiyun  *
94*4882a593Smuzhiyun  * @ctx: pointer to the context structure
95*4882a593Smuzhiyun  * @virt_addr: virt addr to map from
96*4882a593Smuzhiyun  * @page_size: size of the page to unmap
97*4882a593Smuzhiyun  * @flush_pte: whether to do a PCI flush
98*4882a593Smuzhiyun  *
99*4882a593Smuzhiyun  * This function does the following:
100*4882a593Smuzhiyun  * - Check that the virt addr is mapped
101*4882a593Smuzhiyun  * - Unmap the virt addr and frees pgts if possible
102*4882a593Smuzhiyun  * - Returns 0 on success, -EINVAL if the given addr is not mapped
103*4882a593Smuzhiyun  *
104*4882a593Smuzhiyun  * Because this function changes the page tables in the device and because it
105*4882a593Smuzhiyun  * changes the MMU hash, it must be protected by a lock.
106*4882a593Smuzhiyun  * However, because it maps only a single page, the lock should be implemented
107*4882a593Smuzhiyun  * in a higher level in order to protect the entire mapping of the memory area
108*4882a593Smuzhiyun  *
109*4882a593Smuzhiyun  * For optimization reasons PCI flush may be requested once after unmapping of
110*4882a593Smuzhiyun  * large area.
111*4882a593Smuzhiyun  */
hl_mmu_unmap(struct hl_ctx * ctx,u64 virt_addr,u32 page_size,bool flush_pte)112*4882a593Smuzhiyun int hl_mmu_unmap(struct hl_ctx *ctx, u64 virt_addr, u32 page_size,
113*4882a593Smuzhiyun 		bool flush_pte)
114*4882a593Smuzhiyun {
115*4882a593Smuzhiyun 	struct hl_device *hdev = ctx->hdev;
116*4882a593Smuzhiyun 	struct asic_fixed_properties *prop = &hdev->asic_prop;
117*4882a593Smuzhiyun 	struct hl_mmu_properties *mmu_prop;
118*4882a593Smuzhiyun 	u64 real_virt_addr;
119*4882a593Smuzhiyun 	u32 real_page_size, npages;
120*4882a593Smuzhiyun 	int i, rc = 0;
121*4882a593Smuzhiyun 	bool is_dram_addr;
122*4882a593Smuzhiyun 
123*4882a593Smuzhiyun 	if (!hdev->mmu_enable)
124*4882a593Smuzhiyun 		return 0;
125*4882a593Smuzhiyun 
126*4882a593Smuzhiyun 	is_dram_addr = is_dram_va(hdev, virt_addr);
127*4882a593Smuzhiyun 
128*4882a593Smuzhiyun 	if (is_dram_addr)
129*4882a593Smuzhiyun 		mmu_prop = &prop->dmmu;
130*4882a593Smuzhiyun 	else if ((page_size % prop->pmmu_huge.page_size) == 0)
131*4882a593Smuzhiyun 		mmu_prop = &prop->pmmu_huge;
132*4882a593Smuzhiyun 	else
133*4882a593Smuzhiyun 		mmu_prop = &prop->pmmu;
134*4882a593Smuzhiyun 
135*4882a593Smuzhiyun 	/*
136*4882a593Smuzhiyun 	 * The H/W handles mapping of specific page sizes. Hence if the page
137*4882a593Smuzhiyun 	 * size is bigger, we break it to sub-pages and unmap them separately.
138*4882a593Smuzhiyun 	 */
139*4882a593Smuzhiyun 	if ((page_size % mmu_prop->page_size) == 0) {
140*4882a593Smuzhiyun 		real_page_size = mmu_prop->page_size;
141*4882a593Smuzhiyun 	} else {
142*4882a593Smuzhiyun 		dev_err(hdev->dev,
143*4882a593Smuzhiyun 			"page size of %u is not %uKB aligned, can't unmap\n",
144*4882a593Smuzhiyun 			page_size, mmu_prop->page_size >> 10);
145*4882a593Smuzhiyun 
146*4882a593Smuzhiyun 		return -EFAULT;
147*4882a593Smuzhiyun 	}
148*4882a593Smuzhiyun 
149*4882a593Smuzhiyun 	npages = page_size / real_page_size;
150*4882a593Smuzhiyun 	real_virt_addr = virt_addr;
151*4882a593Smuzhiyun 
152*4882a593Smuzhiyun 	for (i = 0 ; i < npages ; i++) {
153*4882a593Smuzhiyun 		rc = hdev->mmu_func.unmap(ctx, real_virt_addr, is_dram_addr);
154*4882a593Smuzhiyun 		if (rc)
155*4882a593Smuzhiyun 			break;
156*4882a593Smuzhiyun 
157*4882a593Smuzhiyun 		real_virt_addr += real_page_size;
158*4882a593Smuzhiyun 	}
159*4882a593Smuzhiyun 
160*4882a593Smuzhiyun 	if (flush_pte)
161*4882a593Smuzhiyun 		hdev->mmu_func.flush(ctx);
162*4882a593Smuzhiyun 
163*4882a593Smuzhiyun 	return rc;
164*4882a593Smuzhiyun }
165*4882a593Smuzhiyun 
166*4882a593Smuzhiyun /*
167*4882a593Smuzhiyun  * hl_mmu_map - maps a virtual addr to physical addr
168*4882a593Smuzhiyun  *
169*4882a593Smuzhiyun  * @ctx: pointer to the context structure
170*4882a593Smuzhiyun  * @virt_addr: virt addr to map from
171*4882a593Smuzhiyun  * @phys_addr: phys addr to map to
172*4882a593Smuzhiyun  * @page_size: physical page size
173*4882a593Smuzhiyun  * @flush_pte: whether to do a PCI flush
174*4882a593Smuzhiyun  *
175*4882a593Smuzhiyun  * This function does the following:
176*4882a593Smuzhiyun  * - Check that the virt addr is not mapped
177*4882a593Smuzhiyun  * - Allocate pgts as necessary in order to map the virt addr to the phys
178*4882a593Smuzhiyun  * - Returns 0 on success, -EINVAL if addr is already mapped, or -ENOMEM.
179*4882a593Smuzhiyun  *
180*4882a593Smuzhiyun  * Because this function changes the page tables in the device and because it
181*4882a593Smuzhiyun  * changes the MMU hash, it must be protected by a lock.
182*4882a593Smuzhiyun  * However, because it maps only a single page, the lock should be implemented
183*4882a593Smuzhiyun  * in a higher level in order to protect the entire mapping of the memory area
184*4882a593Smuzhiyun  *
185*4882a593Smuzhiyun  * For optimization reasons PCI flush may be requested once after mapping of
186*4882a593Smuzhiyun  * large area.
187*4882a593Smuzhiyun  */
hl_mmu_map(struct hl_ctx * ctx,u64 virt_addr,u64 phys_addr,u32 page_size,bool flush_pte)188*4882a593Smuzhiyun int hl_mmu_map(struct hl_ctx *ctx, u64 virt_addr, u64 phys_addr, u32 page_size,
189*4882a593Smuzhiyun 		bool flush_pte)
190*4882a593Smuzhiyun {
191*4882a593Smuzhiyun 	struct hl_device *hdev = ctx->hdev;
192*4882a593Smuzhiyun 	struct asic_fixed_properties *prop = &hdev->asic_prop;
193*4882a593Smuzhiyun 	struct hl_mmu_properties *mmu_prop;
194*4882a593Smuzhiyun 	u64 real_virt_addr, real_phys_addr;
195*4882a593Smuzhiyun 	u32 real_page_size, npages;
196*4882a593Smuzhiyun 	int i, rc, mapped_cnt = 0;
197*4882a593Smuzhiyun 	bool is_dram_addr;
198*4882a593Smuzhiyun 
199*4882a593Smuzhiyun 	if (!hdev->mmu_enable)
200*4882a593Smuzhiyun 		return 0;
201*4882a593Smuzhiyun 
202*4882a593Smuzhiyun 	is_dram_addr = is_dram_va(hdev, virt_addr);
203*4882a593Smuzhiyun 
204*4882a593Smuzhiyun 	if (is_dram_addr)
205*4882a593Smuzhiyun 		mmu_prop = &prop->dmmu;
206*4882a593Smuzhiyun 	else if ((page_size % prop->pmmu_huge.page_size) == 0)
207*4882a593Smuzhiyun 		mmu_prop = &prop->pmmu_huge;
208*4882a593Smuzhiyun 	else
209*4882a593Smuzhiyun 		mmu_prop = &prop->pmmu;
210*4882a593Smuzhiyun 
211*4882a593Smuzhiyun 	/*
212*4882a593Smuzhiyun 	 * The H/W handles mapping of specific page sizes. Hence if the page
213*4882a593Smuzhiyun 	 * size is bigger, we break it to sub-pages and map them separately.
214*4882a593Smuzhiyun 	 */
215*4882a593Smuzhiyun 	if ((page_size % mmu_prop->page_size) == 0) {
216*4882a593Smuzhiyun 		real_page_size = mmu_prop->page_size;
217*4882a593Smuzhiyun 	} else {
218*4882a593Smuzhiyun 		dev_err(hdev->dev,
219*4882a593Smuzhiyun 			"page size of %u is not %uKB aligned, can't unmap\n",
220*4882a593Smuzhiyun 			page_size, mmu_prop->page_size >> 10);
221*4882a593Smuzhiyun 
222*4882a593Smuzhiyun 		return -EFAULT;
223*4882a593Smuzhiyun 	}
224*4882a593Smuzhiyun 
225*4882a593Smuzhiyun 	WARN_ONCE((phys_addr & (real_page_size - 1)),
226*4882a593Smuzhiyun 		"Mapping 0x%llx with page size of 0x%x is erroneous! Address must be divisible by page size",
227*4882a593Smuzhiyun 		phys_addr, real_page_size);
228*4882a593Smuzhiyun 
229*4882a593Smuzhiyun 	npages = page_size / real_page_size;
230*4882a593Smuzhiyun 	real_virt_addr = virt_addr;
231*4882a593Smuzhiyun 	real_phys_addr = phys_addr;
232*4882a593Smuzhiyun 
233*4882a593Smuzhiyun 	for (i = 0 ; i < npages ; i++) {
234*4882a593Smuzhiyun 		rc = hdev->mmu_func.map(ctx, real_virt_addr, real_phys_addr,
235*4882a593Smuzhiyun 				real_page_size, is_dram_addr);
236*4882a593Smuzhiyun 		if (rc)
237*4882a593Smuzhiyun 			goto err;
238*4882a593Smuzhiyun 
239*4882a593Smuzhiyun 		real_virt_addr += real_page_size;
240*4882a593Smuzhiyun 		real_phys_addr += real_page_size;
241*4882a593Smuzhiyun 		mapped_cnt++;
242*4882a593Smuzhiyun 	}
243*4882a593Smuzhiyun 
244*4882a593Smuzhiyun 	if (flush_pte)
245*4882a593Smuzhiyun 		hdev->mmu_func.flush(ctx);
246*4882a593Smuzhiyun 
247*4882a593Smuzhiyun 	return 0;
248*4882a593Smuzhiyun 
249*4882a593Smuzhiyun err:
250*4882a593Smuzhiyun 	real_virt_addr = virt_addr;
251*4882a593Smuzhiyun 	for (i = 0 ; i < mapped_cnt ; i++) {
252*4882a593Smuzhiyun 		if (hdev->mmu_func.unmap(ctx, real_virt_addr, is_dram_addr))
253*4882a593Smuzhiyun 			dev_warn_ratelimited(hdev->dev,
254*4882a593Smuzhiyun 				"failed to unmap va: 0x%llx\n", real_virt_addr);
255*4882a593Smuzhiyun 
256*4882a593Smuzhiyun 		real_virt_addr += real_page_size;
257*4882a593Smuzhiyun 	}
258*4882a593Smuzhiyun 
259*4882a593Smuzhiyun 	hdev->mmu_func.flush(ctx);
260*4882a593Smuzhiyun 
261*4882a593Smuzhiyun 	return rc;
262*4882a593Smuzhiyun }
263*4882a593Smuzhiyun 
264*4882a593Smuzhiyun /*
265*4882a593Smuzhiyun  * hl_mmu_swap_out - marks all mapping of the given ctx as swapped out
266*4882a593Smuzhiyun  *
267*4882a593Smuzhiyun  * @ctx: pointer to the context structure
268*4882a593Smuzhiyun  *
269*4882a593Smuzhiyun  */
hl_mmu_swap_out(struct hl_ctx * ctx)270*4882a593Smuzhiyun void hl_mmu_swap_out(struct hl_ctx *ctx)
271*4882a593Smuzhiyun {
272*4882a593Smuzhiyun 	struct hl_device *hdev = ctx->hdev;
273*4882a593Smuzhiyun 
274*4882a593Smuzhiyun 	if (hdev->mmu_enable)
275*4882a593Smuzhiyun 		hdev->mmu_func.swap_out(ctx);
276*4882a593Smuzhiyun }
277*4882a593Smuzhiyun 
278*4882a593Smuzhiyun /*
279*4882a593Smuzhiyun  * hl_mmu_swap_in - marks all mapping of the given ctx as swapped in
280*4882a593Smuzhiyun  *
281*4882a593Smuzhiyun  * @ctx: pointer to the context structure
282*4882a593Smuzhiyun  *
283*4882a593Smuzhiyun  */
hl_mmu_swap_in(struct hl_ctx * ctx)284*4882a593Smuzhiyun void hl_mmu_swap_in(struct hl_ctx *ctx)
285*4882a593Smuzhiyun {
286*4882a593Smuzhiyun 	struct hl_device *hdev = ctx->hdev;
287*4882a593Smuzhiyun 
288*4882a593Smuzhiyun 	if (hdev->mmu_enable)
289*4882a593Smuzhiyun 		hdev->mmu_func.swap_in(ctx);
290*4882a593Smuzhiyun }
291*4882a593Smuzhiyun 
hl_mmu_if_set_funcs(struct hl_device * hdev)292*4882a593Smuzhiyun int hl_mmu_if_set_funcs(struct hl_device *hdev)
293*4882a593Smuzhiyun {
294*4882a593Smuzhiyun 	if (!hdev->mmu_enable)
295*4882a593Smuzhiyun 		return 0;
296*4882a593Smuzhiyun 
297*4882a593Smuzhiyun 	switch (hdev->asic_type) {
298*4882a593Smuzhiyun 	case ASIC_GOYA:
299*4882a593Smuzhiyun 	case ASIC_GAUDI:
300*4882a593Smuzhiyun 		hl_mmu_v1_set_funcs(hdev);
301*4882a593Smuzhiyun 		break;
302*4882a593Smuzhiyun 	default:
303*4882a593Smuzhiyun 		dev_err(hdev->dev, "Unrecognized ASIC type %d\n",
304*4882a593Smuzhiyun 			hdev->asic_type);
305*4882a593Smuzhiyun 		return -EOPNOTSUPP;
306*4882a593Smuzhiyun 	}
307*4882a593Smuzhiyun 
308*4882a593Smuzhiyun 	return 0;
309*4882a593Smuzhiyun }
310