]> git.karo-electronics.de Git - linux-beck.git/blob - arch/metag/mm/cache.c
b713ec01c204770b9f39c6f4926af5591d950af8
[linux-beck.git] / arch / metag / mm / cache.c
1 /*
2  * arch/metag/mm/cache.c
3  *
4  * Copyright (C) 2001, 2002, 2005, 2007, 2012 Imagination Technologies.
5  *
6  * This program is free software; you can redistribute it and/or modify it under
7  * the terms of the GNU General Public License version 2 as published by the
8  * Free Software Foundation.
9  *
10  * Cache control code
11  */
12
13 #include <linux/export.h>
14 #include <linux/io.h>
15 #include <asm/cacheflush.h>
16 #include <asm/core_reg.h>
17 #include <asm/metag_isa.h>
18 #include <asm/metag_mem.h>
19 #include <asm/metag_regs.h>
20
21 #define DEFAULT_CACHE_WAYS_LOG2 2
22
23 /*
24  * Size of a set in the caches. Initialised for default 16K stride, adjusted
25  * according to values passed through TBI global heap segment via LDLK (on ATP)
26  * or config registers (on HTP/MTP)
27  */
28 static int dcache_set_shift = METAG_TBI_CACHE_SIZE_BASE_LOG2
29                                         - DEFAULT_CACHE_WAYS_LOG2;
30 static int icache_set_shift = METAG_TBI_CACHE_SIZE_BASE_LOG2
31                                         - DEFAULT_CACHE_WAYS_LOG2;
32 /*
33  * The number of sets in the caches. Initialised for HTP/ATP, adjusted
34  * according to NOMMU setting in config registers
35  */
36 static unsigned char dcache_sets_log2 = DEFAULT_CACHE_WAYS_LOG2;
37 static unsigned char icache_sets_log2 = DEFAULT_CACHE_WAYS_LOG2;
38
39 /**
40  * metag_cache_probe() - Probe L1 cache configuration.
41  *
42  * Probe the L1 cache configuration to aid the L1 physical cache flushing
43  * functions.
44  */
45 void metag_cache_probe(void)
46 {
47 #ifndef CONFIG_METAG_META12
48         int coreid = metag_in32(METAC_CORE_ID);
49         int config = metag_in32(METAC_CORE_CONFIG2);
50         int cfgcache = coreid & METAC_COREID_CFGCACHE_BITS;
51
52         if (cfgcache == METAC_COREID_CFGCACHE_TYPE0 ||
53             cfgcache == METAC_COREID_CFGCACHE_PRIVNOMMU) {
54                 icache_sets_log2 = 1;
55                 dcache_sets_log2 = 1;
56         }
57
58         /* For normal size caches, the smallest size is 4Kb.
59            For small caches, the smallest size is 64b */
60         icache_set_shift = (config & METAC_CORECFG2_ICSMALL_BIT)
61                                 ? 6 : 12;
62         icache_set_shift += (config & METAC_CORE_C2ICSZ_BITS)
63                                 >> METAC_CORE_C2ICSZ_S;
64         icache_set_shift -= icache_sets_log2;
65
66         dcache_set_shift = (config & METAC_CORECFG2_DCSMALL_BIT)
67                                 ? 6 : 12;
68         dcache_set_shift += (config & METAC_CORECFG2_DCSZ_BITS)
69                                 >> METAC_CORECFG2_DCSZ_S;
70         dcache_set_shift -= dcache_sets_log2;
71 #else
72         /* Extract cache sizes from global heap segment */
73         unsigned long val, u;
74         int width, shift, addend;
75         PTBISEG seg;
76
77         seg = __TBIFindSeg(NULL, TBID_SEG(TBID_THREAD_GLOBAL,
78                                           TBID_SEGSCOPE_GLOBAL,
79                                           TBID_SEGTYPE_HEAP));
80         if (seg != NULL) {
81                 val = seg->Data[1];
82
83                 /* Work out width of I-cache size bit-field */
84                 u = ((unsigned long) METAG_TBI_ICACHE_SIZE_BITS)
85                        >> METAG_TBI_ICACHE_SIZE_S;
86                 width = 0;
87                 while (u & 1) {
88                         width++;
89                         u >>= 1;
90                 }
91                 /* Extract sign-extended size addend value */
92                 shift = 32 - (METAG_TBI_ICACHE_SIZE_S + width);
93                 addend = (long) ((val & METAG_TBI_ICACHE_SIZE_BITS)
94                                  << shift)
95                         >> (shift + METAG_TBI_ICACHE_SIZE_S);
96                 /* Now calculate I-cache set size */
97                 icache_set_shift = (METAG_TBI_CACHE_SIZE_BASE_LOG2
98                                     - DEFAULT_CACHE_WAYS_LOG2)
99                                         + addend;
100
101                 /* Similarly for D-cache */
102                 u = ((unsigned long) METAG_TBI_DCACHE_SIZE_BITS)
103                        >> METAG_TBI_DCACHE_SIZE_S;
104                 width = 0;
105                 while (u & 1) {
106                         width++;
107                         u >>= 1;
108                 }
109                 shift = 32 - (METAG_TBI_DCACHE_SIZE_S + width);
110                 addend = (long) ((val & METAG_TBI_DCACHE_SIZE_BITS)
111                                  << shift)
112                         >> (shift + METAG_TBI_DCACHE_SIZE_S);
113                 dcache_set_shift = (METAG_TBI_CACHE_SIZE_BASE_LOG2
114                                     - DEFAULT_CACHE_WAYS_LOG2)
115                                         + addend;
116         }
117 #endif
118 }
119
120 static void metag_phys_data_cache_flush(const void *start)
121 {
122         unsigned long flush0, flush1, flush2, flush3;
123         int loops, step;
124         int thread;
125         int part, offset;
126         int set_shift;
127
128         /* Use a sequence of writes to flush the cache region requested */
129         thread = (__core_reg_get(TXENABLE) & TXENABLE_THREAD_BITS)
130                                           >> TXENABLE_THREAD_S;
131
132         /* Cache is broken into sets which lie in contiguous RAMs */
133         set_shift = dcache_set_shift;
134
135         /* Move to the base of the physical cache flush region */
136         flush0 = LINSYSCFLUSH_DCACHE_LINE;
137         step   = 64;
138
139         /* Get partition data for this thread */
140         part = metag_in32(SYSC_DCPART0 +
141                               (SYSC_xCPARTn_STRIDE * thread));
142
143         if ((int)start < 0)
144                 /* Access Global vs Local partition */
145                 part >>= SYSC_xCPARTG_AND_S
146                         - SYSC_xCPARTL_AND_S;
147
148         /* Extract offset and move SetOff */
149         offset = (part & SYSC_xCPARTL_OR_BITS)
150                         >> SYSC_xCPARTL_OR_S;
151         flush0 += (offset << (set_shift - 4));
152
153         /* Shrink size */
154         part = (part & SYSC_xCPARTL_AND_BITS)
155                         >> SYSC_xCPARTL_AND_S;
156         loops = ((part + 1) << (set_shift - 4));
157
158         /* Reduce loops by step of cache line size */
159         loops /= step;
160
161         flush1 = flush0 + (1 << set_shift);
162         flush2 = flush0 + (2 << set_shift);
163         flush3 = flush0 + (3 << set_shift);
164
165         if (dcache_sets_log2 == 1) {
166                 flush2 = flush1;
167                 flush3 = flush1 + step;
168                 flush1 = flush0 + step;
169                 step  <<= 1;
170                 loops >>= 1;
171         }
172
173         /* Clear loops ways in cache */
174         while (loops-- != 0) {
175                 /* Clear the ways. */
176 #if 0
177                 /*
178                  * GCC doesn't generate very good code for this so we
179                  * provide inline assembly instead.
180                  */
181                 metag_out8(0, flush0);
182                 metag_out8(0, flush1);
183                 metag_out8(0, flush2);
184                 metag_out8(0, flush3);
185
186                 flush0 += step;
187                 flush1 += step;
188                 flush2 += step;
189                 flush3 += step;
190 #else
191                 asm volatile (
192                         "SETB\t[%0+%4++],%5\n"
193                         "SETB\t[%1+%4++],%5\n"
194                         "SETB\t[%2+%4++],%5\n"
195                         "SETB\t[%3+%4++],%5\n"
196                         : "+e" (flush0),
197                           "+e" (flush1),
198                           "+e" (flush2),
199                           "+e" (flush3)
200                         : "e" (step), "a" (0));
201 #endif
202         }
203 }
204
205 void metag_data_cache_flush_all(const void *start)
206 {
207         if ((metag_in32(SYSC_CACHE_MMU_CONFIG) & SYSC_CMMUCFG_DC_ON_BIT) == 0)
208                 /* No need to flush the data cache it's not actually enabled */
209                 return;
210
211         metag_phys_data_cache_flush(start);
212 }
213
214 void metag_data_cache_flush(const void *start, int bytes)
215 {
216         unsigned long flush0;
217         int loops, step;
218
219         if ((metag_in32(SYSC_CACHE_MMU_CONFIG) & SYSC_CMMUCFG_DC_ON_BIT) == 0)
220                 /* No need to flush the data cache it's not actually enabled */
221                 return;
222
223         if (bytes >= 4096) {
224                 metag_phys_data_cache_flush(start);
225                 return;
226         }
227
228         /* Use linear cache flush mechanism on META IP */
229         flush0 = (int)start;
230         loops  = ((int)start & (DCACHE_LINE_BYTES - 1)) + bytes +
231                                         (DCACHE_LINE_BYTES - 1);
232         loops  >>= DCACHE_LINE_S;
233
234 #define PRIM_FLUSH(addr, offset) do {                   \
235         int __addr = ((int) (addr)) + ((offset) * 64);  \
236         __builtin_dcache_flush((void *)(__addr));       \
237         } while (0)
238
239 #define LOOP_INC (4*64)
240
241         do {
242                 /* By default stop */
243                 step = 0;
244
245                 switch (loops) {
246                 /* Drop Thru Cases! */
247                 default:
248                         PRIM_FLUSH(flush0, 3);
249                         loops -= 4;
250                         step = 1;
251                 case 3:
252                         PRIM_FLUSH(flush0, 2);
253                 case 2:
254                         PRIM_FLUSH(flush0, 1);
255                 case 1:
256                         PRIM_FLUSH(flush0, 0);
257                         flush0 += LOOP_INC;
258                 case 0:
259                         break;
260                 }
261         } while (step);
262 }
263 EXPORT_SYMBOL(metag_data_cache_flush);
264
265 static void metag_phys_code_cache_flush(const void *start, int bytes)
266 {
267         unsigned long flush0, flush1, flush2, flush3, end_set;
268         int loops, step;
269         int thread;
270         int set_shift, set_size;
271         int part, offset;
272
273         /* Use a sequence of writes to flush the cache region requested */
274         thread = (__core_reg_get(TXENABLE) & TXENABLE_THREAD_BITS)
275                                           >> TXENABLE_THREAD_S;
276         set_shift = icache_set_shift;
277
278         /* Move to the base of the physical cache flush region */
279         flush0 = LINSYSCFLUSH_ICACHE_LINE;
280         step   = 64;
281
282         /* Get partition code for this thread */
283         part = metag_in32(SYSC_ICPART0 +
284                           (SYSC_xCPARTn_STRIDE * thread));
285
286         if ((int)start < 0)
287                 /* Access Global vs Local partition */
288                 part >>= SYSC_xCPARTG_AND_S-SYSC_xCPARTL_AND_S;
289
290         /* Extract offset and move SetOff */
291         offset = (part & SYSC_xCPARTL_OR_BITS)
292                         >> SYSC_xCPARTL_OR_S;
293         flush0 += (offset << (set_shift - 4));
294
295         /* Shrink size */
296         part = (part & SYSC_xCPARTL_AND_BITS)
297                         >> SYSC_xCPARTL_AND_S;
298         loops = ((part + 1) << (set_shift - 4));
299
300         /* Where does the Set end? */
301         end_set = flush0 + loops;
302         set_size = loops;
303
304 #ifdef CONFIG_METAG_META12
305         if ((bytes < 4096) && (bytes < loops)) {
306                 /* Unreachable on HTP/MTP */
307                 /* Only target the sets that could be relavent */
308                 flush0 += (loops - step) & ((int) start);
309                 loops = (((int) start) & (step-1)) + bytes + step - 1;
310         }
311 #endif
312
313         /* Reduce loops by step of cache line size */
314         loops /= step;
315
316         flush1 = flush0 + (1<<set_shift);
317         flush2 = flush0 + (2<<set_shift);
318         flush3 = flush0 + (3<<set_shift);
319
320         if (icache_sets_log2 == 1) {
321                 flush2 = flush1;
322                 flush3 = flush1 + step;
323                 flush1 = flush0 + step;
324 #if 0
325                 /* flush0 will stop one line early in this case
326                  * (flush1 will do the final line).
327                  * However we don't correct end_set here at the moment
328                  * because it will never wrap on HTP/MTP
329                  */
330                 end_set -= step;
331 #endif
332                 step  <<= 1;
333                 loops >>= 1;
334         }
335
336         /* Clear loops ways in cache */
337         while (loops-- != 0) {
338 #if 0
339                 /*
340                  * GCC doesn't generate very good code for this so we
341                  * provide inline assembly instead.
342                  */
343                 /* Clear the ways */
344                 metag_out8(0, flush0);
345                 metag_out8(0, flush1);
346                 metag_out8(0, flush2);
347                 metag_out8(0, flush3);
348
349                 flush0 += step;
350                 flush1 += step;
351                 flush2 += step;
352                 flush3 += step;
353 #else
354                 asm volatile (
355                         "SETB\t[%0+%4++],%5\n"
356                         "SETB\t[%1+%4++],%5\n"
357                         "SETB\t[%2+%4++],%5\n"
358                         "SETB\t[%3+%4++],%5\n"
359                         : "+e" (flush0),
360                           "+e" (flush1),
361                           "+e" (flush2),
362                           "+e" (flush3)
363                         : "e" (step), "a" (0));
364 #endif
365
366                 if (flush0 == end_set) {
367                         /* Wrap within Set 0 */
368                         flush0 -= set_size;
369                         flush1 -= set_size;
370                         flush2 -= set_size;
371                         flush3 -= set_size;
372                 }
373         }
374 }
375
376 void metag_code_cache_flush_all(const void *start)
377 {
378         if ((metag_in32(SYSC_CACHE_MMU_CONFIG) & SYSC_CMMUCFG_IC_ON_BIT) == 0)
379                 /* No need to flush the code cache it's not actually enabled */
380                 return;
381
382         metag_phys_code_cache_flush(start, 4096);
383 }
384
385 void metag_code_cache_flush(const void *start, int bytes)
386 {
387 #ifndef CONFIG_METAG_META12
388         void *flush;
389         int loops, step;
390 #endif /* !CONFIG_METAG_META12 */
391
392         if ((metag_in32(SYSC_CACHE_MMU_CONFIG) & SYSC_CMMUCFG_IC_ON_BIT) == 0)
393                 /* No need to flush the code cache it's not actually enabled */
394                 return;
395
396 #ifdef CONFIG_METAG_META12
397         /* CACHEWD isn't available on Meta1, so always do full cache flush */
398         metag_phys_code_cache_flush(start, bytes);
399
400 #else /* CONFIG_METAG_META12 */
401         /* If large size do full physical cache flush */
402         if (bytes >= 4096) {
403                 metag_phys_code_cache_flush(start, bytes);
404                 return;
405         }
406
407         /* Use linear cache flush mechanism on META IP */
408         flush = (void *)((int)start & ~(ICACHE_LINE_BYTES-1));
409         loops  = ((int)start & (ICACHE_LINE_BYTES-1)) + bytes +
410                 (ICACHE_LINE_BYTES-1);
411         loops  >>= ICACHE_LINE_S;
412
413 #define PRIM_IFLUSH(addr, offset) \
414         __builtin_meta2_cachewd(((addr) + ((offset) * 64)), CACHEW_ICACHE_BIT)
415
416 #define LOOP_INC (4*64)
417
418         do {
419                 /* By default stop */
420                 step = 0;
421
422                 switch (loops) {
423                 /* Drop Thru Cases! */
424                 default:
425                         PRIM_IFLUSH(flush, 3);
426                         loops -= 4;
427                         step = 1;
428                 case 3:
429                         PRIM_IFLUSH(flush, 2);
430                 case 2:
431                         PRIM_IFLUSH(flush, 1);
432                 case 1:
433                         PRIM_IFLUSH(flush, 0);
434                         flush += LOOP_INC;
435                 case 0:
436                         break;
437                 }
438         } while (step);
439 #endif /* !CONFIG_METAG_META12 */
440 }
441 EXPORT_SYMBOL(metag_code_cache_flush);