Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/microblaze/kernel/cpu/cache.c
26481 views
1
/*
2
* Cache control for MicroBlaze cache memories
3
*
4
* Copyright (C) 2007-2009 Michal Simek <[email protected]>
5
* Copyright (C) 2007-2009 PetaLogix
6
* Copyright (C) 2007-2009 John Williams <[email protected]>
7
*
8
* This file is subject to the terms and conditions of the GNU General
9
* Public License. See the file COPYING in the main directory of this
10
* archive for more details.
11
*/
12
13
#include <asm/cacheflush.h>
14
#include <linux/cache.h>
15
#include <asm/cpuinfo.h>
16
#include <asm/pvr.h>
17
18
static inline void __enable_icache_msr(void)
19
{
20
__asm__ __volatile__ (" msrset r0, %0;" \
21
"nop;" \
22
: : "i" (MSR_ICE) : "memory");
23
}
24
25
static inline void __disable_icache_msr(void)
26
{
27
__asm__ __volatile__ (" msrclr r0, %0;" \
28
"nop;" \
29
: : "i" (MSR_ICE) : "memory");
30
}
31
32
static inline void __enable_dcache_msr(void)
33
{
34
__asm__ __volatile__ (" msrset r0, %0;" \
35
"nop;" \
36
: : "i" (MSR_DCE) : "memory");
37
}
38
39
static inline void __disable_dcache_msr(void)
40
{
41
__asm__ __volatile__ (" msrclr r0, %0;" \
42
"nop; " \
43
: : "i" (MSR_DCE) : "memory");
44
}
45
46
static inline void __enable_icache_nomsr(void)
47
{
48
__asm__ __volatile__ (" mfs r12, rmsr;" \
49
"nop;" \
50
"ori r12, r12, %0;" \
51
"mts rmsr, r12;" \
52
"nop;" \
53
: : "i" (MSR_ICE) : "memory", "r12");
54
}
55
56
static inline void __disable_icache_nomsr(void)
57
{
58
__asm__ __volatile__ (" mfs r12, rmsr;" \
59
"nop;" \
60
"andi r12, r12, ~%0;" \
61
"mts rmsr, r12;" \
62
"nop;" \
63
: : "i" (MSR_ICE) : "memory", "r12");
64
}
65
66
static inline void __enable_dcache_nomsr(void)
67
{
68
__asm__ __volatile__ (" mfs r12, rmsr;" \
69
"nop;" \
70
"ori r12, r12, %0;" \
71
"mts rmsr, r12;" \
72
"nop;" \
73
: : "i" (MSR_DCE) : "memory", "r12");
74
}
75
76
static inline void __disable_dcache_nomsr(void)
77
{
78
__asm__ __volatile__ (" mfs r12, rmsr;" \
79
"nop;" \
80
"andi r12, r12, ~%0;" \
81
"mts rmsr, r12;" \
82
"nop;" \
83
: : "i" (MSR_DCE) : "memory", "r12");
84
}
85
86
87
/* Helper macro for computing the limits of cache range loops
88
*
89
* End address can be unaligned which is OK for C implementation.
90
* ASM implementation align it in ASM macros
91
*/
92
#define CACHE_LOOP_LIMITS(start, end, cache_line_length, cache_size) \
93
do { \
94
int align = ~(cache_line_length - 1); \
95
if (start < UINT_MAX - cache_size) \
96
end = min(start + cache_size, end); \
97
start &= align; \
98
} while (0)
99
100
/*
101
* Helper macro to loop over the specified cache_size/line_length and
102
* execute 'op' on that cacheline
103
*/
104
#define CACHE_ALL_LOOP(cache_size, line_length, op) \
105
do { \
106
unsigned int len = cache_size - line_length; \
107
int step = -line_length; \
108
WARN_ON(step >= 0); \
109
\
110
__asm__ __volatile__ (" 1: " #op " %0, r0;" \
111
"bgtid %0, 1b;" \
112
"addk %0, %0, %1;" \
113
: : "r" (len), "r" (step) \
114
: "memory"); \
115
} while (0)
116
117
/* Used for wdc.flush/clear which can use rB for offset which is not possible
118
* to use for simple wdc or wic.
119
*
120
* start address is cache aligned
121
* end address is not aligned, if end is aligned then I have to subtract
122
* cacheline length because I can't flush/invalidate the next cacheline.
123
* If is not, I align it because I will flush/invalidate whole line.
124
*/
125
#define CACHE_RANGE_LOOP_2(start, end, line_length, op) \
126
do { \
127
int step = -line_length; \
128
int align = ~(line_length - 1); \
129
int count; \
130
end = ((end & align) == end) ? end - line_length : end & align; \
131
count = end - start; \
132
WARN_ON(count < 0); \
133
\
134
__asm__ __volatile__ (" 1: " #op " %0, %1;" \
135
"bgtid %1, 1b;" \
136
"addk %1, %1, %2;" \
137
: : "r" (start), "r" (count), \
138
"r" (step) : "memory"); \
139
} while (0)
140
141
/* It is used only first parameter for OP - for wic, wdc */
142
#define CACHE_RANGE_LOOP_1(start, end, line_length, op) \
143
do { \
144
unsigned int volatile temp = 0; \
145
unsigned int align = ~(line_length - 1); \
146
end = ((end & align) == end) ? end - line_length : end & align; \
147
WARN_ON(end < start); \
148
\
149
__asm__ __volatile__ (" 1: " #op " %1, r0;" \
150
"cmpu %0, %1, %2;" \
151
"bgtid %0, 1b;" \
152
"addk %1, %1, %3;" \
153
: : "r" (temp), "r" (start), "r" (end), \
154
"r" (line_length) : "memory"); \
155
} while (0)
156
157
#define ASM_LOOP
158
159
static void __flush_icache_range_msr_irq(unsigned long start, unsigned long end)
160
{
161
unsigned long flags;
162
#ifndef ASM_LOOP
163
int i;
164
#endif
165
pr_debug("%s: start 0x%x, end 0x%x\n", __func__,
166
(unsigned int)start, (unsigned int) end);
167
168
CACHE_LOOP_LIMITS(start, end,
169
cpuinfo.icache_line_length, cpuinfo.icache_size);
170
171
local_irq_save(flags);
172
__disable_icache_msr();
173
174
#ifdef ASM_LOOP
175
CACHE_RANGE_LOOP_1(start, end, cpuinfo.icache_line_length, wic);
176
#else
177
for (i = start; i < end; i += cpuinfo.icache_line_length)
178
__asm__ __volatile__ ("wic %0, r0;" \
179
: : "r" (i));
180
#endif
181
__enable_icache_msr();
182
local_irq_restore(flags);
183
}
184
185
static void __flush_icache_range_nomsr_irq(unsigned long start,
186
unsigned long end)
187
{
188
unsigned long flags;
189
#ifndef ASM_LOOP
190
int i;
191
#endif
192
pr_debug("%s: start 0x%x, end 0x%x\n", __func__,
193
(unsigned int)start, (unsigned int) end);
194
195
CACHE_LOOP_LIMITS(start, end,
196
cpuinfo.icache_line_length, cpuinfo.icache_size);
197
198
local_irq_save(flags);
199
__disable_icache_nomsr();
200
201
#ifdef ASM_LOOP
202
CACHE_RANGE_LOOP_1(start, end, cpuinfo.icache_line_length, wic);
203
#else
204
for (i = start; i < end; i += cpuinfo.icache_line_length)
205
__asm__ __volatile__ ("wic %0, r0;" \
206
: : "r" (i));
207
#endif
208
209
__enable_icache_nomsr();
210
local_irq_restore(flags);
211
}
212
213
static void __flush_icache_range_noirq(unsigned long start,
214
unsigned long end)
215
{
216
#ifndef ASM_LOOP
217
int i;
218
#endif
219
pr_debug("%s: start 0x%x, end 0x%x\n", __func__,
220
(unsigned int)start, (unsigned int) end);
221
222
CACHE_LOOP_LIMITS(start, end,
223
cpuinfo.icache_line_length, cpuinfo.icache_size);
224
#ifdef ASM_LOOP
225
CACHE_RANGE_LOOP_1(start, end, cpuinfo.icache_line_length, wic);
226
#else
227
for (i = start; i < end; i += cpuinfo.icache_line_length)
228
__asm__ __volatile__ ("wic %0, r0;" \
229
: : "r" (i));
230
#endif
231
}
232
233
static void __flush_icache_all_msr_irq(void)
234
{
235
unsigned long flags;
236
#ifndef ASM_LOOP
237
int i;
238
#endif
239
pr_debug("%s\n", __func__);
240
241
local_irq_save(flags);
242
__disable_icache_msr();
243
#ifdef ASM_LOOP
244
CACHE_ALL_LOOP(cpuinfo.icache_size, cpuinfo.icache_line_length, wic);
245
#else
246
for (i = 0; i < cpuinfo.icache_size;
247
i += cpuinfo.icache_line_length)
248
__asm__ __volatile__ ("wic %0, r0;" \
249
: : "r" (i));
250
#endif
251
__enable_icache_msr();
252
local_irq_restore(flags);
253
}
254
255
static void __flush_icache_all_nomsr_irq(void)
256
{
257
unsigned long flags;
258
#ifndef ASM_LOOP
259
int i;
260
#endif
261
pr_debug("%s\n", __func__);
262
263
local_irq_save(flags);
264
__disable_icache_nomsr();
265
#ifdef ASM_LOOP
266
CACHE_ALL_LOOP(cpuinfo.icache_size, cpuinfo.icache_line_length, wic);
267
#else
268
for (i = 0; i < cpuinfo.icache_size;
269
i += cpuinfo.icache_line_length)
270
__asm__ __volatile__ ("wic %0, r0;" \
271
: : "r" (i));
272
#endif
273
__enable_icache_nomsr();
274
local_irq_restore(flags);
275
}
276
277
static void __flush_icache_all_noirq(void)
278
{
279
#ifndef ASM_LOOP
280
int i;
281
#endif
282
pr_debug("%s\n", __func__);
283
#ifdef ASM_LOOP
284
CACHE_ALL_LOOP(cpuinfo.icache_size, cpuinfo.icache_line_length, wic);
285
#else
286
for (i = 0; i < cpuinfo.icache_size;
287
i += cpuinfo.icache_line_length)
288
__asm__ __volatile__ ("wic %0, r0;" \
289
: : "r" (i));
290
#endif
291
}
292
293
static void __invalidate_dcache_all_msr_irq(void)
294
{
295
unsigned long flags;
296
#ifndef ASM_LOOP
297
int i;
298
#endif
299
pr_debug("%s\n", __func__);
300
301
local_irq_save(flags);
302
__disable_dcache_msr();
303
#ifdef ASM_LOOP
304
CACHE_ALL_LOOP(cpuinfo.dcache_size, cpuinfo.dcache_line_length, wdc);
305
#else
306
for (i = 0; i < cpuinfo.dcache_size;
307
i += cpuinfo.dcache_line_length)
308
__asm__ __volatile__ ("wdc %0, r0;" \
309
: : "r" (i));
310
#endif
311
__enable_dcache_msr();
312
local_irq_restore(flags);
313
}
314
315
static void __invalidate_dcache_all_nomsr_irq(void)
316
{
317
unsigned long flags;
318
#ifndef ASM_LOOP
319
int i;
320
#endif
321
pr_debug("%s\n", __func__);
322
323
local_irq_save(flags);
324
__disable_dcache_nomsr();
325
#ifdef ASM_LOOP
326
CACHE_ALL_LOOP(cpuinfo.dcache_size, cpuinfo.dcache_line_length, wdc);
327
#else
328
for (i = 0; i < cpuinfo.dcache_size;
329
i += cpuinfo.dcache_line_length)
330
__asm__ __volatile__ ("wdc %0, r0;" \
331
: : "r" (i));
332
#endif
333
__enable_dcache_nomsr();
334
local_irq_restore(flags);
335
}
336
337
static void __invalidate_dcache_all_noirq_wt(void)
338
{
339
#ifndef ASM_LOOP
340
int i;
341
#endif
342
pr_debug("%s\n", __func__);
343
#ifdef ASM_LOOP
344
CACHE_ALL_LOOP(cpuinfo.dcache_size, cpuinfo.dcache_line_length, wdc);
345
#else
346
for (i = 0; i < cpuinfo.dcache_size;
347
i += cpuinfo.dcache_line_length)
348
__asm__ __volatile__ ("wdc %0, r0;" \
349
: : "r" (i));
350
#endif
351
}
352
353
/*
354
* FIXME It is blindly invalidation as is expected
355
* but can't be called on noMMU in microblaze_cache_init below
356
*
357
* MS: noMMU kernel won't boot if simple wdc is used
358
* The reason should be that there are discared data which kernel needs
359
*/
360
static void __invalidate_dcache_all_wb(void)
361
{
362
#ifndef ASM_LOOP
363
int i;
364
#endif
365
pr_debug("%s\n", __func__);
366
#ifdef ASM_LOOP
367
CACHE_ALL_LOOP(cpuinfo.dcache_size, cpuinfo.dcache_line_length,
368
wdc);
369
#else
370
for (i = 0; i < cpuinfo.dcache_size;
371
i += cpuinfo.dcache_line_length)
372
__asm__ __volatile__ ("wdc %0, r0;" \
373
: : "r" (i));
374
#endif
375
}
376
377
static void __invalidate_dcache_range_wb(unsigned long start,
378
unsigned long end)
379
{
380
#ifndef ASM_LOOP
381
int i;
382
#endif
383
pr_debug("%s: start 0x%x, end 0x%x\n", __func__,
384
(unsigned int)start, (unsigned int) end);
385
386
CACHE_LOOP_LIMITS(start, end,
387
cpuinfo.dcache_line_length, cpuinfo.dcache_size);
388
#ifdef ASM_LOOP
389
CACHE_RANGE_LOOP_2(start, end, cpuinfo.dcache_line_length, wdc.clear);
390
#else
391
for (i = start; i < end; i += cpuinfo.dcache_line_length)
392
__asm__ __volatile__ ("wdc.clear %0, r0;" \
393
: : "r" (i));
394
#endif
395
}
396
397
static void __invalidate_dcache_range_nomsr_wt(unsigned long start,
398
unsigned long end)
399
{
400
#ifndef ASM_LOOP
401
int i;
402
#endif
403
pr_debug("%s: start 0x%x, end 0x%x\n", __func__,
404
(unsigned int)start, (unsigned int) end);
405
CACHE_LOOP_LIMITS(start, end,
406
cpuinfo.dcache_line_length, cpuinfo.dcache_size);
407
408
#ifdef ASM_LOOP
409
CACHE_RANGE_LOOP_1(start, end, cpuinfo.dcache_line_length, wdc);
410
#else
411
for (i = start; i < end; i += cpuinfo.dcache_line_length)
412
__asm__ __volatile__ ("wdc %0, r0;" \
413
: : "r" (i));
414
#endif
415
}
416
417
static void __invalidate_dcache_range_msr_irq_wt(unsigned long start,
418
unsigned long end)
419
{
420
unsigned long flags;
421
#ifndef ASM_LOOP
422
int i;
423
#endif
424
pr_debug("%s: start 0x%x, end 0x%x\n", __func__,
425
(unsigned int)start, (unsigned int) end);
426
CACHE_LOOP_LIMITS(start, end,
427
cpuinfo.dcache_line_length, cpuinfo.dcache_size);
428
429
local_irq_save(flags);
430
__disable_dcache_msr();
431
432
#ifdef ASM_LOOP
433
CACHE_RANGE_LOOP_1(start, end, cpuinfo.dcache_line_length, wdc);
434
#else
435
for (i = start; i < end; i += cpuinfo.dcache_line_length)
436
__asm__ __volatile__ ("wdc %0, r0;" \
437
: : "r" (i));
438
#endif
439
440
__enable_dcache_msr();
441
local_irq_restore(flags);
442
}
443
444
static void __invalidate_dcache_range_nomsr_irq(unsigned long start,
445
unsigned long end)
446
{
447
unsigned long flags;
448
#ifndef ASM_LOOP
449
int i;
450
#endif
451
pr_debug("%s: start 0x%x, end 0x%x\n", __func__,
452
(unsigned int)start, (unsigned int) end);
453
454
CACHE_LOOP_LIMITS(start, end,
455
cpuinfo.dcache_line_length, cpuinfo.dcache_size);
456
457
local_irq_save(flags);
458
__disable_dcache_nomsr();
459
460
#ifdef ASM_LOOP
461
CACHE_RANGE_LOOP_1(start, end, cpuinfo.dcache_line_length, wdc);
462
#else
463
for (i = start; i < end; i += cpuinfo.dcache_line_length)
464
__asm__ __volatile__ ("wdc %0, r0;" \
465
: : "r" (i));
466
#endif
467
468
__enable_dcache_nomsr();
469
local_irq_restore(flags);
470
}
471
472
static void __flush_dcache_all_wb(void)
473
{
474
#ifndef ASM_LOOP
475
int i;
476
#endif
477
pr_debug("%s\n", __func__);
478
#ifdef ASM_LOOP
479
CACHE_ALL_LOOP(cpuinfo.dcache_size, cpuinfo.dcache_line_length,
480
wdc.flush);
481
#else
482
for (i = 0; i < cpuinfo.dcache_size;
483
i += cpuinfo.dcache_line_length)
484
__asm__ __volatile__ ("wdc.flush %0, r0;" \
485
: : "r" (i));
486
#endif
487
}
488
489
static void __flush_dcache_range_wb(unsigned long start, unsigned long end)
490
{
491
#ifndef ASM_LOOP
492
int i;
493
#endif
494
pr_debug("%s: start 0x%x, end 0x%x\n", __func__,
495
(unsigned int)start, (unsigned int) end);
496
497
CACHE_LOOP_LIMITS(start, end,
498
cpuinfo.dcache_line_length, cpuinfo.dcache_size);
499
#ifdef ASM_LOOP
500
CACHE_RANGE_LOOP_2(start, end, cpuinfo.dcache_line_length, wdc.flush);
501
#else
502
for (i = start; i < end; i += cpuinfo.dcache_line_length)
503
__asm__ __volatile__ ("wdc.flush %0, r0;" \
504
: : "r" (i));
505
#endif
506
}
507
508
/* struct for wb caches and for wt caches */
509
struct scache *mbc;
510
511
/* new wb cache model */
512
static const struct scache wb_msr = {
513
.ie = __enable_icache_msr,
514
.id = __disable_icache_msr,
515
.ifl = __flush_icache_all_noirq,
516
.iflr = __flush_icache_range_noirq,
517
.iin = __flush_icache_all_noirq,
518
.iinr = __flush_icache_range_noirq,
519
.de = __enable_dcache_msr,
520
.dd = __disable_dcache_msr,
521
.dfl = __flush_dcache_all_wb,
522
.dflr = __flush_dcache_range_wb,
523
.din = __invalidate_dcache_all_wb,
524
.dinr = __invalidate_dcache_range_wb,
525
};
526
527
/* There is only difference in ie, id, de, dd functions */
528
static const struct scache wb_nomsr = {
529
.ie = __enable_icache_nomsr,
530
.id = __disable_icache_nomsr,
531
.ifl = __flush_icache_all_noirq,
532
.iflr = __flush_icache_range_noirq,
533
.iin = __flush_icache_all_noirq,
534
.iinr = __flush_icache_range_noirq,
535
.de = __enable_dcache_nomsr,
536
.dd = __disable_dcache_nomsr,
537
.dfl = __flush_dcache_all_wb,
538
.dflr = __flush_dcache_range_wb,
539
.din = __invalidate_dcache_all_wb,
540
.dinr = __invalidate_dcache_range_wb,
541
};
542
543
/* Old wt cache model with disabling irq and turn off cache */
544
static const struct scache wt_msr = {
545
.ie = __enable_icache_msr,
546
.id = __disable_icache_msr,
547
.ifl = __flush_icache_all_msr_irq,
548
.iflr = __flush_icache_range_msr_irq,
549
.iin = __flush_icache_all_msr_irq,
550
.iinr = __flush_icache_range_msr_irq,
551
.de = __enable_dcache_msr,
552
.dd = __disable_dcache_msr,
553
.dfl = __invalidate_dcache_all_msr_irq,
554
.dflr = __invalidate_dcache_range_msr_irq_wt,
555
.din = __invalidate_dcache_all_msr_irq,
556
.dinr = __invalidate_dcache_range_msr_irq_wt,
557
};
558
559
static const struct scache wt_nomsr = {
560
.ie = __enable_icache_nomsr,
561
.id = __disable_icache_nomsr,
562
.ifl = __flush_icache_all_nomsr_irq,
563
.iflr = __flush_icache_range_nomsr_irq,
564
.iin = __flush_icache_all_nomsr_irq,
565
.iinr = __flush_icache_range_nomsr_irq,
566
.de = __enable_dcache_nomsr,
567
.dd = __disable_dcache_nomsr,
568
.dfl = __invalidate_dcache_all_nomsr_irq,
569
.dflr = __invalidate_dcache_range_nomsr_irq,
570
.din = __invalidate_dcache_all_nomsr_irq,
571
.dinr = __invalidate_dcache_range_nomsr_irq,
572
};
573
574
/* New wt cache model for newer Microblaze versions */
575
static const struct scache wt_msr_noirq = {
576
.ie = __enable_icache_msr,
577
.id = __disable_icache_msr,
578
.ifl = __flush_icache_all_noirq,
579
.iflr = __flush_icache_range_noirq,
580
.iin = __flush_icache_all_noirq,
581
.iinr = __flush_icache_range_noirq,
582
.de = __enable_dcache_msr,
583
.dd = __disable_dcache_msr,
584
.dfl = __invalidate_dcache_all_noirq_wt,
585
.dflr = __invalidate_dcache_range_nomsr_wt,
586
.din = __invalidate_dcache_all_noirq_wt,
587
.dinr = __invalidate_dcache_range_nomsr_wt,
588
};
589
590
static const struct scache wt_nomsr_noirq = {
591
.ie = __enable_icache_nomsr,
592
.id = __disable_icache_nomsr,
593
.ifl = __flush_icache_all_noirq,
594
.iflr = __flush_icache_range_noirq,
595
.iin = __flush_icache_all_noirq,
596
.iinr = __flush_icache_range_noirq,
597
.de = __enable_dcache_nomsr,
598
.dd = __disable_dcache_nomsr,
599
.dfl = __invalidate_dcache_all_noirq_wt,
600
.dflr = __invalidate_dcache_range_nomsr_wt,
601
.din = __invalidate_dcache_all_noirq_wt,
602
.dinr = __invalidate_dcache_range_nomsr_wt,
603
};
604
605
/* CPU version code for 7.20.c - see arch/microblaze/kernel/cpu/cpuinfo.c */
606
#define CPUVER_7_20_A 0x0c
607
#define CPUVER_7_20_D 0x0f
608
609
void microblaze_cache_init(void)
610
{
611
if (cpuinfo.use_instr & PVR2_USE_MSR_INSTR) {
612
if (cpuinfo.dcache_wb) {
613
pr_info("wb_msr\n");
614
mbc = (struct scache *)&wb_msr;
615
if (cpuinfo.ver_code <= CPUVER_7_20_D) {
616
/* MS: problem with signal handling - hw bug */
617
pr_info("WB won't work properly\n");
618
}
619
} else {
620
if (cpuinfo.ver_code >= CPUVER_7_20_A) {
621
pr_info("wt_msr_noirq\n");
622
mbc = (struct scache *)&wt_msr_noirq;
623
} else {
624
pr_info("wt_msr\n");
625
mbc = (struct scache *)&wt_msr;
626
}
627
}
628
} else {
629
if (cpuinfo.dcache_wb) {
630
pr_info("wb_nomsr\n");
631
mbc = (struct scache *)&wb_nomsr;
632
if (cpuinfo.ver_code <= CPUVER_7_20_D) {
633
/* MS: problem with signal handling - hw bug */
634
pr_info("WB won't work properly\n");
635
}
636
} else {
637
if (cpuinfo.ver_code >= CPUVER_7_20_A) {
638
pr_info("wt_nomsr_noirq\n");
639
mbc = (struct scache *)&wt_nomsr_noirq;
640
} else {
641
pr_info("wt_nomsr\n");
642
mbc = (struct scache *)&wt_nomsr;
643
}
644
}
645
}
646
/*
647
* FIXME Invalidation is done in U-BOOT
648
* WT cache: Data is already written to main memory
649
* WB cache: Discard data on noMMU which caused that kernel doesn't boot
650
*/
651
/* invalidate_dcache(); */
652
enable_dcache();
653
654
invalidate_icache();
655
enable_icache();
656
}
657
658