1 /*
2  * Copyright (c) 2021 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 /**
8  * @file
9  * @brief Driver to utilize TLB on Intel Audio DSP
10  *
11  * TLB (Translation Lookup Buffer) table is used to map between
12  * physical and virtual memory. This is global to all cores
13  * on the DSP, as changes to the TLB table are visible to
14  * all cores.
15  *
16  * Note that all passed in addresses should be in cached range
17  * (aka cached addresses). Due to the need to calculate TLB
18  * indexes, virtual addresses will be converted internally to
19  * cached one via z_soc_cached_ptr(). However, physical addresses
20  * are untouched.
21  */
22 
23 #define DT_DRV_COMPAT intel_adsp_tlb
24 
25 #include <zephyr/device.h>
26 #include <zephyr/kernel.h>
27 #include <zephyr/spinlock.h>
28 #include <zephyr/sys/__assert.h>
29 #include <zephyr/sys/check.h>
30 #include <zephyr/sys/mem_manage.h>
31 #include <zephyr/sys/util.h>
32 #include <zephyr/debug/sparse.h>
33 #include <zephyr/cache.h>
34 
35 #include <soc.h>
36 #include <adsp_memory.h>
37 
38 #include <zephyr/drivers/mm/system_mm.h>
39 #include "mm_drv_common.h"
40 
41 DEVICE_MMIO_TOPLEVEL_STATIC(tlb_regs, DT_DRV_INST(0));
42 
43 #define TLB_BASE \
44 	((mm_reg_t)DEVICE_MMIO_TOPLEVEL_GET(tlb_regs))
45 
46 /*
47  * Number of significant bits in the page index (defines the size of
48  * the table)
49  */
50 #define TLB_PADDR_SIZE DT_INST_PROP(0, paddr_size)
51 #define TLB_PADDR_MASK ((1 << TLB_PADDR_SIZE) - 1)
52 #define TLB_ENABLE_BIT BIT(TLB_PADDR_SIZE)
53 
54 static struct k_spinlock tlb_lock;
55 
56 /**
57  * Calculate the index to the TLB table.
58  *
59  * @param vaddr Page-aligned virtual address.
60  * @return Index to the TLB table.
61  */
get_tlb_entry_idx(uintptr_t vaddr)62 static uint32_t get_tlb_entry_idx(uintptr_t vaddr)
63 {
64 	return (POINTER_TO_UINT(vaddr) - CONFIG_KERNEL_VM_BASE) /
65 	       CONFIG_MM_DRV_PAGE_SIZE;
66 }
67 
sys_mm_drv_map_page(void * virt,uintptr_t phys,uint32_t flags)68 int sys_mm_drv_map_page(void *virt, uintptr_t phys, uint32_t flags)
69 {
70 	k_spinlock_key_t key;
71 	uint32_t entry_idx;
72 	uint16_t entry;
73 	uint16_t *tlb_entries = UINT_TO_POINTER(TLB_BASE);
74 	int ret = 0;
75 
76 	/*
77 	 * Cached addresses for both physical and virtual.
78 	 *
79 	 * As the main memory is in cached address ranges,
80 	 * the cached physical address is needed to perform
81 	 * bound check.
82 	 */
83 	uintptr_t pa = POINTER_TO_UINT(z_soc_cached_ptr(UINT_TO_POINTER(phys)));
84 	uintptr_t va = POINTER_TO_UINT(z_soc_cached_ptr(virt));
85 
86 	ARG_UNUSED(flags);
87 
88 	/* Make sure inputs are page-aligned */
89 	CHECKIF(!sys_mm_drv_is_addr_aligned(pa) ||
90 		!sys_mm_drv_is_addr_aligned(va)) {
91 		ret = -EINVAL;
92 		goto out;
93 	}
94 
95 	/* Check bounds of physical address space */
96 	CHECKIF((pa < L2_SRAM_BASE) ||
97 		(pa >= (L2_SRAM_BASE + L2_SRAM_SIZE))) {
98 		ret = -EINVAL;
99 		goto out;
100 	}
101 
102 	/* Check bounds of virtual address space */
103 	CHECKIF((va < CONFIG_KERNEL_VM_BASE) ||
104 		(va >= (CONFIG_KERNEL_VM_BASE + CONFIG_KERNEL_VM_SIZE))) {
105 		ret = -EINVAL;
106 		goto out;
107 	}
108 
109 	key = k_spin_lock(&tlb_lock);
110 
111 	entry_idx = get_tlb_entry_idx(va);
112 
113 	/*
114 	 * The address part of the TLB entry takes the lowest
115 	 * TLB_PADDR_SIZE bits of the physical page number,
116 	 * and discards the highest bits.  This is due to the
117 	 * architecture design where the same physical page
118 	 * can be accessed via two addresses. One address goes
119 	 * through the cache, and the other one accesses
120 	 * memory directly (without cache). The difference
121 	 * between these two addresses are in the higher bits,
122 	 * and the lower bits are the same.  And this is why
123 	 * TLB only cares about the lower part of the physical
124 	 * address.
125 	 */
126 	entry = ((pa / CONFIG_MM_DRV_PAGE_SIZE) & TLB_PADDR_MASK);
127 
128 	/* Enable the translation in the TLB entry */
129 	entry |= TLB_ENABLE_BIT;
130 
131 	tlb_entries[entry_idx] = entry;
132 
133 	/*
134 	 * Invalid the cache of the newly mapped virtual page to
135 	 * avoid stale data.
136 	 */
137 	sys_cache_data_invd_range(virt, CONFIG_MM_DRV_PAGE_SIZE);
138 
139 	k_spin_unlock(&tlb_lock, key);
140 
141 out:
142 	return ret;
143 }
144 
sys_mm_drv_map_region(void * virt,uintptr_t phys,size_t size,uint32_t flags)145 int sys_mm_drv_map_region(void *virt, uintptr_t phys,
146 			  size_t size, uint32_t flags)
147 {
148 	void *va = (__sparse_force void *)z_soc_cached_ptr(virt);
149 
150 	return sys_mm_drv_simple_map_region(va, phys, size, flags);
151 }
152 
sys_mm_drv_map_array(void * virt,uintptr_t * phys,size_t cnt,uint32_t flags)153 int sys_mm_drv_map_array(void *virt, uintptr_t *phys,
154 			 size_t cnt, uint32_t flags)
155 {
156 	void *va = (__sparse_force void *)z_soc_cached_ptr(virt);
157 
158 	return sys_mm_drv_simple_map_array(va, phys, cnt, flags);
159 }
160 
sys_mm_drv_unmap_page(void * virt)161 int sys_mm_drv_unmap_page(void *virt)
162 {
163 	k_spinlock_key_t key;
164 	uint32_t entry_idx;
165 	uint16_t *tlb_entries = UINT_TO_POINTER(TLB_BASE);
166 	int ret = 0;
167 
168 	/* Use cached virtual address */
169 	uintptr_t va = POINTER_TO_UINT(z_soc_cached_ptr(virt));
170 
171 	/* Check bounds of virtual address space */
172 	CHECKIF((va < CONFIG_KERNEL_VM_BASE) ||
173 		(va >= (CONFIG_KERNEL_VM_BASE + CONFIG_KERNEL_VM_SIZE))) {
174 		ret = -EINVAL;
175 		goto out;
176 	}
177 
178 	/* Make sure inputs are page-aligned */
179 	CHECKIF(!sys_mm_drv_is_addr_aligned(va)) {
180 		ret = -EINVAL;
181 		goto out;
182 	}
183 
184 	key = k_spin_lock(&tlb_lock);
185 
186 	/*
187 	 * Flush the cache to make sure the backing physical page
188 	 * has the latest data.
189 	 */
190 	sys_cache_data_flush_range(virt, CONFIG_MM_DRV_PAGE_SIZE);
191 
192 	entry_idx = get_tlb_entry_idx(va);
193 
194 	/* Simply clear the enable bit */
195 	tlb_entries[entry_idx] &= ~TLB_ENABLE_BIT;
196 
197 	k_spin_unlock(&tlb_lock, key);
198 
199 out:
200 	return ret;
201 }
202 
sys_mm_drv_unmap_region(void * virt,size_t size)203 int sys_mm_drv_unmap_region(void *virt, size_t size)
204 {
205 	void *va = (__sparse_force void *)z_soc_cached_ptr(virt);
206 
207 	return sys_mm_drv_simple_unmap_region(va, size);
208 }
209 
sys_mm_drv_page_phys_get(void * virt,uintptr_t * phys)210 int sys_mm_drv_page_phys_get(void *virt, uintptr_t *phys)
211 {
212 	uint16_t *tlb_entries = UINT_TO_POINTER(TLB_BASE);
213 	uintptr_t ent;
214 	int ret = 0;
215 
216 	/* Use cached address */
217 	uintptr_t va = POINTER_TO_UINT(z_soc_cached_ptr(virt));
218 
219 	CHECKIF(!sys_mm_drv_is_addr_aligned(va)) {
220 		ret = -EINVAL;
221 		goto out;
222 	}
223 
224 	/* Check bounds of virtual address space */
225 	CHECKIF((va < CONFIG_KERNEL_VM_BASE) ||
226 		(va >= (CONFIG_KERNEL_VM_BASE + CONFIG_KERNEL_VM_SIZE))) {
227 		ret = -EINVAL;
228 		goto out;
229 	}
230 
231 	ent = tlb_entries[get_tlb_entry_idx(va)];
232 
233 	if ((ent & TLB_ENABLE_BIT) != TLB_ENABLE_BIT) {
234 		ret = -EFAULT;
235 	} else {
236 		if (phys != NULL) {
237 			*phys = (ent & TLB_PADDR_MASK) * CONFIG_MM_DRV_PAGE_SIZE + L2_SRAM_BASE;
238 		}
239 
240 		ret = 0;
241 	}
242 
243 out:
244 	return ret;
245 }
246 
sys_mm_drv_page_flag_get(void * virt,uint32_t * flags)247 int sys_mm_drv_page_flag_get(void *virt, uint32_t *flags)
248 {
249 	ARG_UNUSED(virt);
250 
251 	/*
252 	 * There are no caching mode, or R/W, or eXecution (etc.) bits.
253 	 * So just return 0.
254 	 */
255 
256 	*flags = 0U;
257 
258 	return 0;
259 }
260 
sys_mm_drv_update_page_flags(void * virt,uint32_t flags)261 int sys_mm_drv_update_page_flags(void *virt, uint32_t flags)
262 {
263 	ARG_UNUSED(virt);
264 	ARG_UNUSED(flags);
265 
266 	/*
267 	 * There are no caching mode, or R/W, or eXecution (etc.) bits.
268 	 * So just return 0.
269 	 */
270 
271 	return 0;
272 }
273 
sys_mm_drv_update_region_flags(void * virt,size_t size,uint32_t flags)274 int sys_mm_drv_update_region_flags(void *virt, size_t size,
275 				   uint32_t flags)
276 {
277 	void *va = (__sparse_force void *)z_soc_cached_ptr(virt);
278 
279 	return sys_mm_drv_simple_update_region_flags(va, size, flags);
280 }
281 
282 
sys_mm_drv_remap_region(void * virt_old,size_t size,void * virt_new)283 int sys_mm_drv_remap_region(void *virt_old, size_t size,
284 			    void *virt_new)
285 {
286 	void *va_new = (__sparse_force void *)z_soc_cached_ptr(virt_new);
287 	void *va_old = (__sparse_force void *)z_soc_cached_ptr(virt_old);
288 
289 	return sys_mm_drv_simple_remap_region(va_old, size, va_new);
290 }
291 
sys_mm_drv_move_region(void * virt_old,size_t size,void * virt_new,uintptr_t phys_new)292 int sys_mm_drv_move_region(void *virt_old, size_t size, void *virt_new,
293 			   uintptr_t phys_new)
294 {
295 	int ret;
296 
297 	void *va_new = (__sparse_force void *)z_soc_cached_ptr(virt_new);
298 	void *va_old = (__sparse_force void *)z_soc_cached_ptr(virt_old);
299 
300 	ret = sys_mm_drv_simple_move_region(va_old, size, va_new, phys_new);
301 
302 	/*
303 	 * Since memcpy() is done in virtual space, need to
304 	 * flush the cache to make sure the backing physical
305 	 * pages have the new data.
306 	 */
307 	sys_cache_data_flush_range(va_new, size);
308 
309 	return ret;
310 }
311 
sys_mm_drv_move_array(void * virt_old,size_t size,void * virt_new,uintptr_t * phys_new,size_t phys_cnt)312 int sys_mm_drv_move_array(void *virt_old, size_t size, void *virt_new,
313 			  uintptr_t *phys_new, size_t phys_cnt)
314 {
315 	int ret;
316 
317 	void *va_new = (__sparse_force void *)z_soc_cached_ptr(virt_new);
318 	void *va_old = (__sparse_force void *)z_soc_cached_ptr(virt_old);
319 
320 	ret = sys_mm_drv_simple_move_array(va_old, size, va_new,
321 					    phys_new, phys_cnt);
322 
323 	/*
324 	 * Since memcpy() is done in virtual space, need to
325 	 * flush the cache to make sure the backing physical
326 	 * pages have the new data.
327 	 */
328 	sys_cache_data_flush_range(va_new, size);
329 
330 	return ret;
331 }
332