123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546 |
- /*
- * Cache control for MicroBlaze cache memories
- *
- * Copyright (C) 2007-2009 Michal Simek <monstr@monstr.eu>
- * Copyright (C) 2007-2009 PetaLogix
- * Copyright (C) 2007-2009 John Williams <john.williams@petalogix.com>
- *
- * This file is subject to the terms and conditions of the GNU General
- * Public License. See the file COPYING in the main directory of this
- * archive for more details.
- */
- #include <asm/cacheflush.h>
- #include <linux/cache.h>
- #include <asm/cpuinfo.h>
- #include <asm/pvr.h>
- static inline void __invalidate_flush_icache(unsigned int addr)
- {
- __asm__ __volatile__ ("wic %0, r0;" \
- : : "r" (addr));
- }
- static inline void __flush_dcache(unsigned int addr)
- {
- __asm__ __volatile__ ("wdc.flush %0, r0;" \
- : : "r" (addr));
- }
- static inline void __invalidate_dcache(unsigned int baseaddr,
- unsigned int offset)
- {
- __asm__ __volatile__ ("wdc.clear %0, %1;" \
- : : "r" (baseaddr), "r" (offset));
- }
- static inline void __enable_icache_msr(void)
- {
- __asm__ __volatile__ (" msrset r0, %0; \
- nop; " \
- : : "i" (MSR_ICE) : "memory");
- }
- static inline void __disable_icache_msr(void)
- {
- __asm__ __volatile__ (" msrclr r0, %0; \
- nop; " \
- : : "i" (MSR_ICE) : "memory");
- }
- static inline void __enable_dcache_msr(void)
- {
- __asm__ __volatile__ (" msrset r0, %0; \
- nop; " \
- : \
- : "i" (MSR_DCE) \
- : "memory");
- }
- static inline void __disable_dcache_msr(void)
- {
- __asm__ __volatile__ (" msrclr r0, %0; \
- nop; " \
- : \
- : "i" (MSR_DCE) \
- : "memory");
- }
- static inline void __enable_icache_nomsr(void)
- {
- __asm__ __volatile__ (" mfs r12, rmsr; \
- nop; \
- ori r12, r12, %0; \
- mts rmsr, r12; \
- nop; " \
- : \
- : "i" (MSR_ICE) \
- : "memory", "r12");
- }
- static inline void __disable_icache_nomsr(void)
- {
- __asm__ __volatile__ (" mfs r12, rmsr; \
- nop; \
- andi r12, r12, ~%0; \
- mts rmsr, r12; \
- nop; " \
- : \
- : "i" (MSR_ICE) \
- : "memory", "r12");
- }
- static inline void __enable_dcache_nomsr(void)
- {
- __asm__ __volatile__ (" mfs r12, rmsr; \
- nop; \
- ori r12, r12, %0; \
- mts rmsr, r12; \
- nop; " \
- : \
- : "i" (MSR_DCE) \
- : "memory", "r12");
- }
- static inline void __disable_dcache_nomsr(void)
- {
- __asm__ __volatile__ (" mfs r12, rmsr; \
- nop; \
- andi r12, r12, ~%0; \
- mts rmsr, r12; \
- nop; " \
- : \
- : "i" (MSR_DCE) \
- : "memory", "r12");
- }
- /* Helper macro for computing the limits of cache range loops */
- #define CACHE_LOOP_LIMITS(start, end, cache_line_length, cache_size) \
- do { \
- int align = ~(cache_line_length - 1); \
- end = min(start + cache_size, end); \
- start &= align; \
- end = ((end & align) + cache_line_length); \
- } while (0);
- /*
- * Helper macro to loop over the specified cache_size/line_length and
- * execute 'op' on that cacheline
- */
- #define CACHE_ALL_LOOP(cache_size, line_length, op) \
- do { \
- unsigned int len = cache_size; \
- int step = -line_length; \
- BUG_ON(step >= 0); \
- \
- __asm__ __volatile__ (" 1: " #op " %0, r0; \
- bgtid %0, 1b; \
- addk %0, %0, %1; \
- " : : "r" (len), "r" (step) \
- : "memory"); \
- } while (0);
- #define CACHE_ALL_LOOP2(cache_size, line_length, op) \
- do { \
- unsigned int len = cache_size; \
- int step = -line_length; \
- BUG_ON(step >= 0); \
- \
- __asm__ __volatile__ (" 1: " #op " r0, %0; \
- bgtid %0, 1b; \
- addk %0, %0, %1; \
- " : : "r" (len), "r" (step) \
- : "memory"); \
- } while (0);
- /* for wdc.flush/clear */
- #define CACHE_RANGE_LOOP_2(start, end, line_length, op) \
- do { \
- int step = -line_length; \
- int count = end - start; \
- BUG_ON(count <= 0); \
- \
- __asm__ __volatile__ (" 1: " #op " %0, %1; \
- bgtid %1, 1b; \
- addk %1, %1, %2; \
- " : : "r" (start), "r" (count), \
- "r" (step) : "memory"); \
- } while (0);
- /* It is used only first parameter for OP - for wic, wdc */
- #define CACHE_RANGE_LOOP_1(start, end, line_length, op) \
- do { \
- int step = -line_length; \
- int count = end - start; \
- BUG_ON(count <= 0); \
- \
- __asm__ __volatile__ (" 1: addk %0, %0, %1; \
- " #op " %0, r0; \
- bgtid %1, 1b; \
- addk %1, %1, %2; \
- " : : "r" (start), "r" (count), \
- "r" (step) : "memory"); \
- } while (0);
- static void __flush_icache_range_msr_irq(unsigned long start, unsigned long end)
- {
- unsigned long flags;
- pr_debug("%s: start 0x%x, end 0x%x\n", __func__,
- (unsigned int)start, (unsigned int) end);
- CACHE_LOOP_LIMITS(start, end,
- cpuinfo.icache_line_length, cpuinfo.icache_size);
- local_irq_save(flags);
- __disable_icache_msr();
- CACHE_RANGE_LOOP_1(start, end, cpuinfo.icache_line_length, wic);
- __enable_icache_msr();
- local_irq_restore(flags);
- }
- static void __flush_icache_range_nomsr_irq(unsigned long start,
- unsigned long end)
- {
- unsigned long flags;
- pr_debug("%s: start 0x%x, end 0x%x\n", __func__,
- (unsigned int)start, (unsigned int) end);
- CACHE_LOOP_LIMITS(start, end,
- cpuinfo.icache_line_length, cpuinfo.icache_size);
- local_irq_save(flags);
- __disable_icache_nomsr();
- CACHE_RANGE_LOOP_1(start, end, cpuinfo.icache_line_length, wic);
- __enable_icache_nomsr();
- local_irq_restore(flags);
- }
- static void __flush_icache_range_noirq(unsigned long start,
- unsigned long end)
- {
- pr_debug("%s: start 0x%x, end 0x%x\n", __func__,
- (unsigned int)start, (unsigned int) end);
- CACHE_LOOP_LIMITS(start, end,
- cpuinfo.icache_line_length, cpuinfo.icache_size);
- CACHE_RANGE_LOOP_1(start, end, cpuinfo.icache_line_length, wic);
- }
- static void __flush_icache_all_msr_irq(void)
- {
- unsigned long flags;
- pr_debug("%s\n", __func__);
- local_irq_save(flags);
- __disable_icache_msr();
- CACHE_ALL_LOOP(cpuinfo.icache_size, cpuinfo.icache_line_length, wic);
- __enable_icache_msr();
- local_irq_restore(flags);
- }
- static void __flush_icache_all_nomsr_irq(void)
- {
- unsigned long flags;
- pr_debug("%s\n", __func__);
- local_irq_save(flags);
- __disable_icache_nomsr();
- CACHE_ALL_LOOP(cpuinfo.icache_size, cpuinfo.icache_line_length, wic);
- __enable_icache_nomsr();
- local_irq_restore(flags);
- }
- static void __flush_icache_all_noirq(void)
- {
- pr_debug("%s\n", __func__);
- CACHE_ALL_LOOP(cpuinfo.icache_size, cpuinfo.icache_line_length, wic);
- }
- static void __invalidate_dcache_all_msr_irq(void)
- {
- unsigned long flags;
- pr_debug("%s\n", __func__);
- local_irq_save(flags);
- __disable_dcache_msr();
- CACHE_ALL_LOOP(cpuinfo.dcache_size, cpuinfo.dcache_line_length, wdc);
- __enable_dcache_msr();
- local_irq_restore(flags);
- }
- static void __invalidate_dcache_all_nomsr_irq(void)
- {
- unsigned long flags;
- pr_debug("%s\n", __func__);
- local_irq_save(flags);
- __disable_dcache_nomsr();
- CACHE_ALL_LOOP(cpuinfo.dcache_size, cpuinfo.dcache_line_length, wdc);
- __enable_dcache_nomsr();
- local_irq_restore(flags);
- }
- static void __invalidate_dcache_all_noirq_wt(void)
- {
- pr_debug("%s\n", __func__);
- CACHE_ALL_LOOP(cpuinfo.dcache_size, cpuinfo.dcache_line_length, wdc)
- }
- /* FIXME this is weird - should be only wdc but not work
- * MS: I am getting bus errors and other weird things */
- static void __invalidate_dcache_all_wb(void)
- {
- pr_debug("%s\n", __func__);
- CACHE_ALL_LOOP2(cpuinfo.dcache_size, cpuinfo.dcache_line_length,
- wdc.clear)
- #if 0
- unsigned int i;
- pr_debug("%s\n", __func__);
- /* Just loop through cache size and invalidate it */
- for (i = 0; i < cpuinfo.dcache_size; i += cpuinfo.dcache_line_length)
- __invalidate_dcache(0, i);
- #endif
- }
- static void __invalidate_dcache_range_wb(unsigned long start,
- unsigned long end)
- {
- pr_debug("%s: start 0x%x, end 0x%x\n", __func__,
- (unsigned int)start, (unsigned int) end);
- CACHE_LOOP_LIMITS(start, end,
- cpuinfo.dcache_line_length, cpuinfo.dcache_size);
- CACHE_RANGE_LOOP_2(start, end, cpuinfo.dcache_line_length, wdc.clear);
- }
- static void __invalidate_dcache_range_nomsr_wt(unsigned long start,
- unsigned long end)
- {
- pr_debug("%s: start 0x%x, end 0x%x\n", __func__,
- (unsigned int)start, (unsigned int) end);
- CACHE_LOOP_LIMITS(start, end,
- cpuinfo.dcache_line_length, cpuinfo.dcache_size);
- CACHE_RANGE_LOOP_1(start, end, cpuinfo.dcache_line_length, wdc);
- }
- static void __invalidate_dcache_range_msr_irq_wt(unsigned long start,
- unsigned long end)
- {
- unsigned long flags;
- pr_debug("%s: start 0x%x, end 0x%x\n", __func__,
- (unsigned int)start, (unsigned int) end);
- CACHE_LOOP_LIMITS(start, end,
- cpuinfo.dcache_line_length, cpuinfo.dcache_size);
- local_irq_save(flags);
- __disable_dcache_msr();
- CACHE_RANGE_LOOP_1(start, end, cpuinfo.dcache_line_length, wdc);
- __enable_dcache_msr();
- local_irq_restore(flags);
- }
- static void __invalidate_dcache_range_nomsr_irq(unsigned long start,
- unsigned long end)
- {
- unsigned long flags;
- pr_debug("%s: start 0x%x, end 0x%x\n", __func__,
- (unsigned int)start, (unsigned int) end);
- CACHE_LOOP_LIMITS(start, end,
- cpuinfo.dcache_line_length, cpuinfo.dcache_size);
- local_irq_save(flags);
- __disable_dcache_nomsr();
- CACHE_RANGE_LOOP_1(start, end, cpuinfo.dcache_line_length, wdc);
- __enable_dcache_nomsr();
- local_irq_restore(flags);
- }
- static void __flush_dcache_all_wb(void)
- {
- pr_debug("%s\n", __func__);
- CACHE_ALL_LOOP(cpuinfo.dcache_size, cpuinfo.dcache_line_length,
- wdc.flush);
- }
- static void __flush_dcache_range_wb(unsigned long start, unsigned long end)
- {
- pr_debug("%s: start 0x%x, end 0x%x\n", __func__,
- (unsigned int)start, (unsigned int) end);
- CACHE_LOOP_LIMITS(start, end,
- cpuinfo.dcache_line_length, cpuinfo.dcache_size);
- CACHE_RANGE_LOOP_2(start, end, cpuinfo.dcache_line_length, wdc.flush);
- }
- /* struct for wb caches and for wt caches */
- struct scache *mbc;
- /* new wb cache model */
- const struct scache wb_msr = {
- .ie = __enable_icache_msr,
- .id = __disable_icache_msr,
- .ifl = __flush_icache_all_noirq,
- .iflr = __flush_icache_range_noirq,
- .iin = __flush_icache_all_noirq,
- .iinr = __flush_icache_range_noirq,
- .de = __enable_dcache_msr,
- .dd = __disable_dcache_msr,
- .dfl = __flush_dcache_all_wb,
- .dflr = __flush_dcache_range_wb,
- .din = __invalidate_dcache_all_wb,
- .dinr = __invalidate_dcache_range_wb,
- };
- /* There is only difference in ie, id, de, dd functions */
- const struct scache wb_nomsr = {
- .ie = __enable_icache_nomsr,
- .id = __disable_icache_nomsr,
- .ifl = __flush_icache_all_noirq,
- .iflr = __flush_icache_range_noirq,
- .iin = __flush_icache_all_noirq,
- .iinr = __flush_icache_range_noirq,
- .de = __enable_dcache_nomsr,
- .dd = __disable_dcache_nomsr,
- .dfl = __flush_dcache_all_wb,
- .dflr = __flush_dcache_range_wb,
- .din = __invalidate_dcache_all_wb,
- .dinr = __invalidate_dcache_range_wb,
- };
- /* Old wt cache model with disabling irq and turn off cache */
- const struct scache wt_msr = {
- .ie = __enable_icache_msr,
- .id = __disable_icache_msr,
- .ifl = __flush_icache_all_msr_irq,
- .iflr = __flush_icache_range_msr_irq,
- .iin = __flush_icache_all_msr_irq,
- .iinr = __flush_icache_range_msr_irq,
- .de = __enable_dcache_msr,
- .dd = __disable_dcache_msr,
- .dfl = __invalidate_dcache_all_msr_irq,
- .dflr = __invalidate_dcache_range_msr_irq_wt,
- .din = __invalidate_dcache_all_msr_irq,
- .dinr = __invalidate_dcache_range_msr_irq_wt,
- };
- const struct scache wt_nomsr = {
- .ie = __enable_icache_nomsr,
- .id = __disable_icache_nomsr,
- .ifl = __flush_icache_all_nomsr_irq,
- .iflr = __flush_icache_range_nomsr_irq,
- .iin = __flush_icache_all_nomsr_irq,
- .iinr = __flush_icache_range_nomsr_irq,
- .de = __enable_dcache_nomsr,
- .dd = __disable_dcache_nomsr,
- .dfl = __invalidate_dcache_all_nomsr_irq,
- .dflr = __invalidate_dcache_range_nomsr_irq,
- .din = __invalidate_dcache_all_nomsr_irq,
- .dinr = __invalidate_dcache_range_nomsr_irq,
- };
- /* New wt cache model for newer Microblaze versions */
- const struct scache wt_msr_noirq = {
- .ie = __enable_icache_msr,
- .id = __disable_icache_msr,
- .ifl = __flush_icache_all_noirq,
- .iflr = __flush_icache_range_noirq,
- .iin = __flush_icache_all_noirq,
- .iinr = __flush_icache_range_noirq,
- .de = __enable_dcache_msr,
- .dd = __disable_dcache_msr,
- .dfl = __invalidate_dcache_all_noirq_wt,
- .dflr = __invalidate_dcache_range_nomsr_wt,
- .din = __invalidate_dcache_all_noirq_wt,
- .dinr = __invalidate_dcache_range_nomsr_wt,
- };
- const struct scache wt_nomsr_noirq = {
- .ie = __enable_icache_nomsr,
- .id = __disable_icache_nomsr,
- .ifl = __flush_icache_all_noirq,
- .iflr = __flush_icache_range_noirq,
- .iin = __flush_icache_all_noirq,
- .iinr = __flush_icache_range_noirq,
- .de = __enable_dcache_nomsr,
- .dd = __disable_dcache_nomsr,
- .dfl = __invalidate_dcache_all_noirq_wt,
- .dflr = __invalidate_dcache_range_nomsr_wt,
- .din = __invalidate_dcache_all_noirq_wt,
- .dinr = __invalidate_dcache_range_nomsr_wt,
- };
- /* CPU version code for 7.20.c - see arch/microblaze/kernel/cpu/cpuinfo.c */
- #define CPUVER_7_20_A 0x0c
- #define CPUVER_7_20_D 0x0f
- #define INFO(s) printk(KERN_INFO "cache: " s " \n");
- void microblaze_cache_init(void)
- {
- if (cpuinfo.use_instr & PVR2_USE_MSR_INSTR) {
- if (cpuinfo.dcache_wb) {
- INFO("wb_msr");
- mbc = (struct scache *)&wb_msr;
- if (cpuinfo.ver_code < CPUVER_7_20_D) {
- /* MS: problem with signal handling - hw bug */
- INFO("WB won't work properly");
- }
- } else {
- if (cpuinfo.ver_code >= CPUVER_7_20_A) {
- INFO("wt_msr_noirq");
- mbc = (struct scache *)&wt_msr_noirq;
- } else {
- INFO("wt_msr");
- mbc = (struct scache *)&wt_msr;
- }
- }
- } else {
- if (cpuinfo.dcache_wb) {
- INFO("wb_nomsr");
- mbc = (struct scache *)&wb_nomsr;
- if (cpuinfo.ver_code < CPUVER_7_20_D) {
- /* MS: problem with signal handling - hw bug */
- INFO("WB won't work properly");
- }
- } else {
- if (cpuinfo.ver_code >= CPUVER_7_20_A) {
- INFO("wt_nomsr_noirq");
- mbc = (struct scache *)&wt_nomsr_noirq;
- } else {
- INFO("wt_nomsr");
- mbc = (struct scache *)&wt_nomsr;
- }
- }
- }
- }
|