Merge branch 'master' of git://git.denx.de/u-boot
[platform/kernel/u-boot.git] / arch / powerpc / cpu / ppc4xx / tlb.c
1 /*
2  * (C) Copyright 2007
3  * Stefan Roese, DENX Software Engineering, sr@denx.de.
4  *
5  * SPDX-License-Identifier:     GPL-2.0+
6  */
7
8 #include <common.h>
9
10 #if defined(CONFIG_440)
11
12 #include <asm/ppc440.h>
13 #include <asm/cache.h>
14 #include <asm/io.h>
15 #include <asm/mmu.h>
16
17 typedef struct region {
18         u64 base;
19         u32 size;
20         u32 tlb_word2_i_value;
21 } region_t;
22
23 void remove_tlb(u32 vaddr, u32 size)
24 {
25         int i;
26         u32 tlb_word0_value;
27         u32 tlb_vaddr;
28         u32 tlb_size = 0;
29
30         for (i=0; i<PPC4XX_TLB_SIZE; i++) {
31                 tlb_word0_value = mftlb1(i);
32                 tlb_vaddr = TLB_WORD0_EPN_DECODE(tlb_word0_value);
33                 if (((tlb_word0_value & TLB_WORD0_V_MASK) == TLB_WORD0_V_ENABLE) &&
34                     (tlb_vaddr >= vaddr)) {
35                         /*
36                          * TLB is enabled and start address is lower or equal
37                          * than the area we are looking for. Now we only have
38                          * to check the size/end address for a match.
39                          */
40                         switch (tlb_word0_value & TLB_WORD0_SIZE_MASK) {
41                         case TLB_WORD0_SIZE_1KB:
42                                 tlb_size = 1 << 10;
43                                 break;
44                         case TLB_WORD0_SIZE_4KB:
45                                 tlb_size = 4 << 10;
46                                 break;
47                         case TLB_WORD0_SIZE_16KB:
48                                 tlb_size = 16 << 10;
49                                 break;
50                         case TLB_WORD0_SIZE_64KB:
51                                 tlb_size = 64 << 10;
52                                 break;
53                         case TLB_WORD0_SIZE_256KB:
54                                 tlb_size = 256 << 10;
55                                 break;
56                         case TLB_WORD0_SIZE_1MB:
57                                 tlb_size = 1 << 20;
58                                 break;
59                         case TLB_WORD0_SIZE_16MB:
60                                 tlb_size = 16 << 20;
61                                 break;
62                         case TLB_WORD0_SIZE_256MB:
63                                 tlb_size = 256 << 20;
64                                 break;
65                         }
66
67                         /*
68                          * Now check the end-address if it's in the range
69                          */
70                         if ((tlb_vaddr + tlb_size - 1) <= (vaddr + size - 1))
71                                 /*
72                                  * Found a TLB in the range.
73                                  * Disable it by writing 0 to tlb0 word.
74                                  */
75                                 mttlb1(i, 0);
76                 }
77         }
78
79         /* Execute an ISYNC instruction so that the new TLB entry takes effect */
80         asm("isync");
81 }
82
83 /*
84  * Change the I attribute (cache inhibited) of a TLB or multiple TLB's.
85  * This function is used to either turn cache on or off in a specific
86  * memory area.
87  */
88 void change_tlb(u32 vaddr, u32 size, u32 tlb_word2_i_value)
89 {
90         int i;
91         u32 tlb_word0_value;
92         u32 tlb_word2_value;
93         u32 tlb_vaddr;
94         u32 tlb_size = 0;
95
96         for (i=0; i<PPC4XX_TLB_SIZE; i++) {
97                 tlb_word0_value = mftlb1(i);
98                 tlb_vaddr = TLB_WORD0_EPN_DECODE(tlb_word0_value);
99                 if (((tlb_word0_value & TLB_WORD0_V_MASK) == TLB_WORD0_V_ENABLE) &&
100                     (tlb_vaddr >= vaddr)) {
101                         /*
102                          * TLB is enabled and start address is lower or equal
103                          * than the area we are looking for. Now we only have
104                          * to check the size/end address for a match.
105                          */
106                         switch (tlb_word0_value & TLB_WORD0_SIZE_MASK) {
107                         case TLB_WORD0_SIZE_1KB:
108                                 tlb_size = 1 << 10;
109                                 break;
110                         case TLB_WORD0_SIZE_4KB:
111                                 tlb_size = 4 << 10;
112                                 break;
113                         case TLB_WORD0_SIZE_16KB:
114                                 tlb_size = 16 << 10;
115                                 break;
116                         case TLB_WORD0_SIZE_64KB:
117                                 tlb_size = 64 << 10;
118                                 break;
119                         case TLB_WORD0_SIZE_256KB:
120                                 tlb_size = 256 << 10;
121                                 break;
122                         case TLB_WORD0_SIZE_1MB:
123                                 tlb_size = 1 << 20;
124                                 break;
125                         case TLB_WORD0_SIZE_16MB:
126                                 tlb_size = 16 << 20;
127                                 break;
128                         case TLB_WORD0_SIZE_256MB:
129                                 tlb_size = 256 << 20;
130                                 break;
131                         }
132
133                         /*
134                          * Now check the end-address if it's in the range
135                          */
136                         if (((tlb_vaddr + tlb_size - 1) <= (vaddr + size - 1)) ||
137                             ((tlb_vaddr < (vaddr + size - 1)) &&
138                              ((tlb_vaddr + tlb_size - 1) > (vaddr + size - 1)))) {
139                                 /*
140                                  * Found a TLB in the range.
141                                  * Change cache attribute in tlb2 word.
142                                  */
143                                 tlb_word2_value =
144                                         TLB_WORD2_U0_DISABLE | TLB_WORD2_U1_DISABLE |
145                                         TLB_WORD2_U2_DISABLE | TLB_WORD2_U3_DISABLE |
146                                         TLB_WORD2_W_DISABLE | tlb_word2_i_value |
147                                         TLB_WORD2_M_DISABLE | TLB_WORD2_G_DISABLE |
148                                         TLB_WORD2_E_DISABLE | TLB_WORD2_UX_ENABLE |
149                                         TLB_WORD2_UW_ENABLE | TLB_WORD2_UR_ENABLE |
150                                         TLB_WORD2_SX_ENABLE | TLB_WORD2_SW_ENABLE |
151                                         TLB_WORD2_SR_ENABLE;
152
153                                 /*
154                                  * Now either flush or invalidate the dcache
155                                  */
156                                 if (tlb_word2_i_value)
157                                         flush_dcache();
158                                 else
159                                         invalidate_dcache();
160
161                                 mttlb3(i, tlb_word2_value);
162                                 asm("iccci 0,0");
163                         }
164                 }
165         }
166
167         /* Execute an ISYNC instruction so that the new TLB entry takes effect */
168         asm("isync");
169 }
170
171 static int add_tlb_entry(u64 phys_addr,
172                          u32 virt_addr,
173                          u32 tlb_word0_size_value,
174                          u32 tlb_word2_i_value)
175 {
176         int i;
177         unsigned long tlb_word0_value;
178         unsigned long tlb_word1_value;
179         unsigned long tlb_word2_value;
180
181         /* First, find the index of a TLB entry not being used */
182         for (i=0; i<PPC4XX_TLB_SIZE; i++) {
183                 tlb_word0_value = mftlb1(i);
184                 if ((tlb_word0_value & TLB_WORD0_V_MASK) == TLB_WORD0_V_DISABLE)
185                         break;
186         }
187         if (i >= PPC4XX_TLB_SIZE)
188                 return -1;
189
190         /* Second, create the TLB entry */
191         tlb_word0_value = TLB_WORD0_EPN_ENCODE(virt_addr) | TLB_WORD0_V_ENABLE |
192                 TLB_WORD0_TS_0 | tlb_word0_size_value;
193         tlb_word1_value = TLB_WORD1_RPN_ENCODE((u32)phys_addr) |
194                 TLB_WORD1_ERPN_ENCODE(phys_addr >> 32);
195         tlb_word2_value = TLB_WORD2_U0_DISABLE | TLB_WORD2_U1_DISABLE |
196                 TLB_WORD2_U2_DISABLE | TLB_WORD2_U3_DISABLE |
197                 TLB_WORD2_W_DISABLE | tlb_word2_i_value |
198                 TLB_WORD2_M_DISABLE | TLB_WORD2_G_DISABLE |
199                 TLB_WORD2_E_DISABLE | TLB_WORD2_UX_ENABLE |
200                 TLB_WORD2_UW_ENABLE | TLB_WORD2_UR_ENABLE |
201                 TLB_WORD2_SX_ENABLE | TLB_WORD2_SW_ENABLE |
202                 TLB_WORD2_SR_ENABLE;
203
204         /* Wait for all memory accesses to complete */
205         sync();
206
207         /* Third, add the TLB entries */
208         mttlb1(i, tlb_word0_value);
209         mttlb2(i, tlb_word1_value);
210         mttlb3(i, tlb_word2_value);
211
212         /* Execute an ISYNC instruction so that the new TLB entry takes effect */
213         asm("isync");
214
215         return 0;
216 }
217
218 static void program_tlb_addr(u64 phys_addr,
219                              u32 virt_addr,
220                              u32 mem_size,
221                              u32 tlb_word2_i_value)
222 {
223         int rc;
224         int tlb_i;
225
226         tlb_i = tlb_word2_i_value;
227         while (mem_size != 0) {
228                 rc = 0;
229                 /* Add the TLB entries in to map the region. */
230                 if (((phys_addr & TLB_256MB_ALIGN_MASK) == phys_addr) &&
231                     (mem_size >= TLB_256MB_SIZE)) {
232                         /* Add a 256MB TLB entry */
233                         if ((rc = add_tlb_entry(phys_addr, virt_addr,
234                                                 TLB_WORD0_SIZE_256MB, tlb_i)) == 0) {
235                                 mem_size -= TLB_256MB_SIZE;
236                                 phys_addr += TLB_256MB_SIZE;
237                                 virt_addr += TLB_256MB_SIZE;
238                         }
239                 } else if (((phys_addr & TLB_16MB_ALIGN_MASK) == phys_addr) &&
240                            (mem_size >= TLB_16MB_SIZE)) {
241                         /* Add a 16MB TLB entry */
242                         if ((rc = add_tlb_entry(phys_addr, virt_addr,
243                                                 TLB_WORD0_SIZE_16MB, tlb_i)) == 0) {
244                                 mem_size -= TLB_16MB_SIZE;
245                                 phys_addr += TLB_16MB_SIZE;
246                                 virt_addr += TLB_16MB_SIZE;
247                         }
248                 } else if (((phys_addr & TLB_1MB_ALIGN_MASK) == phys_addr) &&
249                            (mem_size >= TLB_1MB_SIZE)) {
250                         /* Add a 1MB TLB entry */
251                         if ((rc = add_tlb_entry(phys_addr, virt_addr,
252                                                 TLB_WORD0_SIZE_1MB, tlb_i)) == 0) {
253                                 mem_size -= TLB_1MB_SIZE;
254                                 phys_addr += TLB_1MB_SIZE;
255                                 virt_addr += TLB_1MB_SIZE;
256                         }
257                 } else if (((phys_addr & TLB_256KB_ALIGN_MASK) == phys_addr) &&
258                            (mem_size >= TLB_256KB_SIZE)) {
259                         /* Add a 256KB TLB entry */
260                         if ((rc = add_tlb_entry(phys_addr, virt_addr,
261                                                 TLB_WORD0_SIZE_256KB, tlb_i)) == 0) {
262                                 mem_size -= TLB_256KB_SIZE;
263                                 phys_addr += TLB_256KB_SIZE;
264                                 virt_addr += TLB_256KB_SIZE;
265                         }
266                 } else if (((phys_addr & TLB_64KB_ALIGN_MASK) == phys_addr) &&
267                            (mem_size >= TLB_64KB_SIZE)) {
268                         /* Add a 64KB TLB entry */
269                         if ((rc = add_tlb_entry(phys_addr, virt_addr,
270                                                 TLB_WORD0_SIZE_64KB, tlb_i)) == 0) {
271                                 mem_size -= TLB_64KB_SIZE;
272                                 phys_addr += TLB_64KB_SIZE;
273                                 virt_addr += TLB_64KB_SIZE;
274                         }
275                 } else if (((phys_addr & TLB_16KB_ALIGN_MASK) == phys_addr) &&
276                            (mem_size >= TLB_16KB_SIZE)) {
277                         /* Add a 16KB TLB entry */
278                         if ((rc = add_tlb_entry(phys_addr, virt_addr,
279                                                 TLB_WORD0_SIZE_16KB, tlb_i)) == 0) {
280                                 mem_size -= TLB_16KB_SIZE;
281                                 phys_addr += TLB_16KB_SIZE;
282                                 virt_addr += TLB_16KB_SIZE;
283                         }
284                 } else if (((phys_addr & TLB_4KB_ALIGN_MASK) == phys_addr) &&
285                            (mem_size >= TLB_4KB_SIZE)) {
286                         /* Add a 4KB TLB entry */
287                         if ((rc = add_tlb_entry(phys_addr, virt_addr,
288                                                 TLB_WORD0_SIZE_4KB, tlb_i)) == 0) {
289                                 mem_size -= TLB_4KB_SIZE;
290                                 phys_addr += TLB_4KB_SIZE;
291                                 virt_addr += TLB_4KB_SIZE;
292                         }
293                 } else if (((phys_addr & TLB_1KB_ALIGN_MASK) == phys_addr) &&
294                            (mem_size >= TLB_1KB_SIZE)) {
295                         /* Add a 1KB TLB entry */
296                         if ((rc = add_tlb_entry(phys_addr, virt_addr,
297                                                 TLB_WORD0_SIZE_1KB, tlb_i)) == 0) {
298                                 mem_size -= TLB_1KB_SIZE;
299                                 phys_addr += TLB_1KB_SIZE;
300                                 virt_addr += TLB_1KB_SIZE;
301                         }
302                 } else {
303                         printf("ERROR: no TLB size exists for the base address 0x%llx.\n",
304                                 phys_addr);
305                 }
306
307                 if (rc != 0)
308                         printf("ERROR: no TLB entries available for the base addr 0x%llx.\n",
309                                 phys_addr);
310         }
311
312         return;
313 }
314
315 /*
316  * Program one (or multiple) TLB entries for one memory region
317  *
318  * Common usage for boards with SDRAM DIMM modules to dynamically
319  * configure the TLB's for the SDRAM
320  */
321 void program_tlb(u64 phys_addr, u32 virt_addr, u32 size, u32 tlb_word2_i_value)
322 {
323         region_t region_array;
324
325         region_array.base = phys_addr;
326         region_array.size = size;
327         region_array.tlb_word2_i_value = tlb_word2_i_value;     /* en-/disable cache */
328
329         /* Call the routine to add in the tlb entries for the memory regions */
330         program_tlb_addr(region_array.base, virt_addr, region_array.size,
331                          region_array.tlb_word2_i_value);
332
333         return;
334 }
335
336 #endif /* CONFIG_440 */