199193Sjmallett// SPDX-License-Identifier: GPL-2.0+
299193Sjmallett/*
399193Sjmallett * Copyright 2008-2011 Freescale Semiconductor, Inc.
499193Sjmallett *
599193Sjmallett * (C) Copyright 2000
699193Sjmallett * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
799193Sjmallett */
899193Sjmallett
999193Sjmallett#include <common.h>
1099193Sjmallett#include <display_options.h>
1199193Sjmallett#include <init.h>
1299193Sjmallett#include <asm/bitops.h>
1399193Sjmallett#include <asm/global_data.h>
1499193Sjmallett#include <asm/processor.h>
1599193Sjmallett#include <asm/mmu.h>
1699193Sjmallett#ifdef CONFIG_ADDR_MAP
1799193Sjmallett#include <addr_map.h>
1899193Sjmallett#endif
1999193Sjmallett
2099193Sjmallett#include <linux/log2.h>
2199193Sjmallett
2299193SjmallettDECLARE_GLOBAL_DATA_PTR;
2399193Sjmallett
2499193Sjmallettvoid invalidate_tlb(u8 tlb)
2599193Sjmallett{
2699193Sjmallett	if (tlb == 0)
2799193Sjmallett		mtspr(MMUCSR0, 0x4);
2899193Sjmallett	if (tlb == 1)
2999193Sjmallett		mtspr(MMUCSR0, 0x2);
3099193Sjmallett}
3199193Sjmallett
3299193Sjmallett__weak void init_tlbs(void)
3399193Sjmallett{
3499193Sjmallett	int i;
3599193Sjmallett
3699193Sjmallett	for (i = 0; i < num_tlb_entries; i++) {
3799193Sjmallett		write_tlb(tlb_table[i].mas0,
3899193Sjmallett			  tlb_table[i].mas1,
3999193Sjmallett			  tlb_table[i].mas2,
4099193Sjmallett			  tlb_table[i].mas3,
4199193Sjmallett			  tlb_table[i].mas7);
42109506Sjmallett	}
43109506Sjmallett
4499193Sjmallett	return;
4599193Sjmallett}
4699193Sjmallett
4799193Sjmallett#if !defined(CONFIG_NAND_SPL) && \
4899193Sjmallett	(!defined(CONFIG_SPL_BUILD) || !CONFIG_IS_ENABLED(INIT_MINIMAL))
4999193Sjmallettvoid read_tlbcam_entry(int idx, u32 *valid, u32 *tsize, unsigned long *epn,
5099193Sjmallett		       phys_addr_t *rpn)
51109506Sjmallett{
52109506Sjmallett	u32 _mas1;
53110066Sjmallett
54110066Sjmallett	mtspr(MAS0, FSL_BOOKE_MAS0(1, idx, 0));
55109506Sjmallett	asm volatile("tlbre;isync");
5699193Sjmallett	_mas1 = mfspr(MAS1);
5799193Sjmallett
5899193Sjmallett	*valid = (_mas1 & MAS1_VALID);
5999193Sjmallett	*tsize = (_mas1 >> 7) & 0x1f;
6099193Sjmallett	*epn = mfspr(MAS2) & MAS2_EPN;
61109462Sjmallett	*rpn = mfspr(MAS3) & MAS3_RPN;
6299193Sjmallett#ifdef CONFIG_ENABLE_36BIT_PHYS
63109462Sjmallett	*rpn |= ((u64)mfspr(MAS7)) << 32;
64109462Sjmallett#endif
6599193Sjmallett}
6699193Sjmallett
67109462Sjmallettvoid print_tlbcam(void)
6899193Sjmallett{
6999193Sjmallett	int i;
7099193Sjmallett	unsigned int num_cam = mfspr(SPRN_TLB1CFG) & 0xfff;
7199193Sjmallett
72109462Sjmallett	/* walk all the entries */
7399193Sjmallett	printf("TLBCAM entries\n");
7499193Sjmallett	for (i = 0; i < num_cam; i++) {
7599193Sjmallett		unsigned long epn;
7699193Sjmallett		u32 tsize, valid;
7799193Sjmallett		phys_addr_t rpn;
7899193Sjmallett
7999193Sjmallett		read_tlbcam_entry(i, &valid, &tsize, &epn, &rpn);
8099193Sjmallett		printf("entry %02d: V: %d EPN 0x%08x RPN 0x%08llx size:",
81109462Sjmallett			i, (valid == 0) ? 0 : 1, (unsigned int)epn,
8299193Sjmallett			(unsigned long long)rpn);
83109462Sjmallett		print_size(TSIZE_TO_BYTES(tsize), "\n");
84109462Sjmallett	}
85109462Sjmallett}
86109462Sjmallett
87109462Sjmallettstatic inline void use_tlb_cam(u8 idx)
88109462Sjmallett{
89109462Sjmallett	int i = idx / 32;
90109462Sjmallett	int bit = idx % 32;
91109462Sjmallett
92109462Sjmallett	gd->arch.used_tlb_cams[i] |= (1 << bit);
93109462Sjmallett}
94109462Sjmallett
9599193Sjmallettstatic inline void free_tlb_cam(u8 idx)
9699193Sjmallett{
9799193Sjmallett	int i = idx / 32;
9899193Sjmallett	int bit = idx % 32;
9999193Sjmallett
100109462Sjmallett	gd->arch.used_tlb_cams[i] &= ~(1 << bit);
10199193Sjmallett}
10299193Sjmallett
10399193Sjmallettvoid init_used_tlb_cams(void)
10499193Sjmallett{
10599193Sjmallett	int i;
106109506Sjmallett	unsigned int num_cam = mfspr(SPRN_TLB1CFG) & 0xfff;
107109506Sjmallett
108109506Sjmallett	for (i = 0; i < ((CONFIG_SYS_NUM_TLBCAMS+31)/32); i++)
109109506Sjmallett		gd->arch.used_tlb_cams[i] = 0;
11099193Sjmallett
11199193Sjmallett	/* walk all the entries */
11299193Sjmallett	for (i = 0; i < num_cam; i++) {
11399193Sjmallett		mtspr(MAS0, FSL_BOOKE_MAS0(1, i, 0));
11499193Sjmallett		asm volatile("tlbre;isync");
11599193Sjmallett		if (mfspr(MAS1) & MAS1_VALID)
116109755Sjmallett			use_tlb_cam(i);
117109755Sjmallett	}
118109755Sjmallett}
119109755Sjmallett
120109755Sjmallettint find_free_tlbcam(void)
121109755Sjmallett{
122109755Sjmallett	int i;
123109755Sjmallett	u32 idx;
124109755Sjmallett
125109755Sjmallett	for (i = 0; i < ((CONFIG_SYS_NUM_TLBCAMS+31)/32); i++) {
126109755Sjmallett		idx = ffz(gd->arch.used_tlb_cams[i]);
127109755Sjmallett
128109506Sjmallett		if (idx != 32)
129109506Sjmallett			break;
130109506Sjmallett	}
131109506Sjmallett
13299193Sjmallett	idx += i * 32;
13399193Sjmallett
134109462Sjmallett	if (idx >= CONFIG_SYS_NUM_TLBCAMS)
13599222Sjmallett		return -1;
136109506Sjmallett
137109506Sjmallett	return idx;
138109506Sjmallett}
139109506Sjmallett
140109506Sjmallettvoid set_tlb(u8 tlb, u32 epn, u64 rpn,
141109506Sjmallett	     u8 perms, u8 wimge,
142109506Sjmallett	     u8 ts, u8 esel, u8 tsize, u8 iprot)
143109506Sjmallett{
144109506Sjmallett	u32 _mas0, _mas1, _mas2, _mas3, _mas7;
145109506Sjmallett
146109506Sjmallett	if (tlb == 1)
147109506Sjmallett		use_tlb_cam(esel);
148109506Sjmallett
149109506Sjmallett	if ((mfspr(SPRN_MMUCFG) & MMUCFG_MAVN) == MMUCFG_MAVN_V1 &&
150109506Sjmallett	    tsize & 1) {
15199193Sjmallett		printf("%s: bad tsize %d on entry %d at 0x%08x\n",
15299193Sjmallett			__func__, tsize, tlb, epn);
153109506Sjmallett		return;
15499193Sjmallett	}
15599193Sjmallett
15699193Sjmallett	_mas0 = FSL_BOOKE_MAS0(tlb, esel, 0);
15799193Sjmallett	_mas1 = FSL_BOOKE_MAS1(1, iprot, 0, ts, tsize);
158109509Sjmallett	_mas2 = FSL_BOOKE_MAS2(epn, wimge);
15999193Sjmallett	_mas3 = FSL_BOOKE_MAS3(rpn, 0, perms);
16099193Sjmallett	_mas7 = FSL_BOOKE_MAS7(rpn);
16199823Sjmallett
16299823Sjmallett	write_tlb(_mas0, _mas1, _mas2, _mas3, _mas7);
163109518Sjmallett
164109506Sjmallett#ifdef CONFIG_ADDR_MAP
165101687Sjmallett	if ((tlb == 1) && (gd->flags & GD_FLG_RELOC))
166105737Sjmallett		addrmap_set_entry(epn, rpn, TSIZE_TO_BYTES(tsize), esel);
16799193Sjmallett#endif
168109506Sjmallett}
169109506Sjmallett
170109506Sjmallettvoid disable_tlb(u8 esel)
171109506Sjmallett{
172109506Sjmallett	u32 _mas0, _mas1, _mas2, _mas3;
173109506Sjmallett
174109506Sjmallett	free_tlb_cam(esel);
175109506Sjmallett
176109506Sjmallett	_mas0 = FSL_BOOKE_MAS0(1, esel, 0);
177109506Sjmallett	_mas1 = 0;
17899193Sjmallett	_mas2 = 0;
17999193Sjmallett	_mas3 = 0;
180110066Sjmallett
181110066Sjmallett	mtspr(MAS0, _mas0);
182110066Sjmallett	mtspr(MAS1, _mas1);
183110066Sjmallett	mtspr(MAS2, _mas2);
184110066Sjmallett	mtspr(MAS3, _mas3);
185110066Sjmallett#ifdef CONFIG_ENABLE_36BIT_PHYS
186110066Sjmallett	mtspr(MAS7, 0);
187110066Sjmallett#endif
188110066Sjmallett	asm volatile("isync;msync;tlbwe;isync");
189110066Sjmallett
190110066Sjmallett#ifdef CONFIG_ADDR_MAP
191110066Sjmallett	if (gd->flags & GD_FLG_RELOC)
192110066Sjmallett		addrmap_set_entry(0, 0, 0, esel);
193110066Sjmallett#endif
194110066Sjmallett}
195110066Sjmallett
196110066Sjmallettstatic void tlbsx (const volatile unsigned *addr)
197110066Sjmallett{
198110066Sjmallett	__asm__ __volatile__ ("tlbsx 0,%0" : : "r" (addr), "m" (*addr));
199110066Sjmallett}
200110066Sjmallett
201110066Sjmallett/* return -1 if we didn't find anything */
202110066Sjmallettint find_tlb_idx(void *addr, u8 tlbsel)
203110066Sjmallett{
204	u32 _mas0, _mas1;
205
206	/* zero out Search PID, AS */
207	mtspr(MAS6, 0);
208
209	tlbsx(addr);
210
211	_mas0 = mfspr(MAS0);
212	_mas1 = mfspr(MAS1);
213
214	/* we found something, and its in the TLB we expect */
215	if ((MAS1_VALID & _mas1) &&
216		(MAS0_TLBSEL(tlbsel) == (_mas0 & MAS0_TLBSEL_MSK))) {
217		return ((_mas0 & MAS0_ESEL_MSK) >> 16);
218	}
219
220	return -1;
221}
222
223#ifdef CONFIG_ADDR_MAP
224int init_addr_map(void)
225{
226	int i;
227	unsigned int num_cam = mfspr(SPRN_TLB1CFG) & 0xfff;
228
229	/* walk all the entries */
230	for (i = 0; i < num_cam; i++) {
231		unsigned long epn;
232		u32 tsize, valid;
233		phys_addr_t rpn;
234
235		read_tlbcam_entry(i, &valid, &tsize, &epn, &rpn);
236		if (valid & MAS1_VALID)
237			addrmap_set_entry(epn, rpn, TSIZE_TO_BYTES(tsize), i);
238	}
239
240	return 0;
241}
242#endif
243
244uint64_t tlb_map_range(ulong v_addr, phys_addr_t p_addr, uint64_t size,
245		       enum tlb_map_type map_type)
246{
247	int i;
248	unsigned int tlb_size;
249	unsigned int wimge;
250	unsigned int perm;
251	unsigned int max_cam, tsize_mask;
252
253	if (map_type == TLB_MAP_RAM) {
254		perm = MAS3_SX|MAS3_SW|MAS3_SR;
255		wimge = MAS2_M;
256#ifdef CONFIG_SYS_PPC_DDR_WIMGE
257		wimge = CONFIG_SYS_PPC_DDR_WIMGE;
258#endif
259	} else {
260		perm = MAS3_SW|MAS3_SR;
261		wimge = MAS2_I|MAS2_G;
262	}
263
264	if ((mfspr(SPRN_MMUCFG) & MMUCFG_MAVN) == MMUCFG_MAVN_V1) {
265		/* Convert (4^max) kB to (2^max) bytes */
266		max_cam = ((mfspr(SPRN_TLB1CFG) >> 16) & 0xf) * 2 + 10;
267		tsize_mask = ~1U;
268	} else {
269		/* Convert (2^max) kB to (2^max) bytes */
270		max_cam = __ilog2(mfspr(SPRN_TLB1PS)) + 10;
271		tsize_mask = ~0U;
272	}
273
274	for (i = 0; size && i < 8; i++) {
275		int tlb_index = find_free_tlbcam();
276		u32 camsize = __ilog2_u64(size) & tsize_mask;
277		u32 align = __ilog2(v_addr) & tsize_mask;
278
279		if (tlb_index == -1)
280			break;
281
282		if (align == -2) align = max_cam;
283		if (camsize > align)
284			camsize = align;
285
286		if (camsize > max_cam)
287			camsize = max_cam;
288
289		tlb_size = camsize - 10;
290
291		set_tlb(1, v_addr, p_addr, perm, wimge,
292			0, tlb_index, tlb_size, 1);
293
294		size -= 1ULL << camsize;
295		v_addr += 1UL << camsize;
296		p_addr += 1UL << camsize;
297	}
298
299	return size;
300}
301
302unsigned int setup_ddr_tlbs_phys(phys_addr_t p_addr,
303				 unsigned int memsize_in_meg)
304{
305	unsigned int ram_tlb_address = (unsigned int)CFG_SYS_DDR_SDRAM_BASE;
306	u64 memsize = (u64)memsize_in_meg << 20;
307	u64 size;
308
309	size = min(memsize, (u64)CFG_MAX_MEM_MAPPED);
310	size = tlb_map_range(ram_tlb_address, p_addr, size, TLB_MAP_RAM);
311
312	if (size || memsize > CFG_MAX_MEM_MAPPED) {
313		print_size(memsize > CFG_MAX_MEM_MAPPED ?
314			   memsize - CFG_MAX_MEM_MAPPED + size : size,
315			   " of DDR memory left unmapped in U-Boot\n");
316#ifndef CONFIG_SPL_BUILD
317		puts("       ");
318#endif
319	}
320
321	return memsize_in_meg;
322}
323
324unsigned int setup_ddr_tlbs(unsigned int memsize_in_meg)
325{
326	return
327		setup_ddr_tlbs_phys(CFG_SYS_DDR_SDRAM_BASE, memsize_in_meg);
328}
329
330/* Invalidate the DDR TLBs for the requested size */
331void clear_ddr_tlbs_phys(phys_addr_t p_addr, unsigned int memsize_in_meg)
332{
333	u32 vstart = CFG_SYS_DDR_SDRAM_BASE;
334	unsigned long epn;
335	u32 tsize, valid, ptr;
336	phys_addr_t rpn = 0;
337	int ddr_esel;
338	u64 memsize = (u64)memsize_in_meg << 20;
339
340	ptr = vstart;
341
342	while (ptr < (vstart + memsize)) {
343		ddr_esel = find_tlb_idx((void *)ptr, 1);
344		if (ddr_esel != -1) {
345			read_tlbcam_entry(ddr_esel, &valid, &tsize, &epn, &rpn);
346			disable_tlb(ddr_esel);
347		}
348		ptr += TSIZE_TO_BYTES(tsize);
349	}
350}
351
352void clear_ddr_tlbs(unsigned int memsize_in_meg)
353{
354	clear_ddr_tlbs_phys(CFG_SYS_DDR_SDRAM_BASE, memsize_in_meg);
355}
356
357
358#endif /* not SPL */
359