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