Merge branch 'core-fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[pandora-kernel.git] / arch / microblaze / kernel / cpu / cache.c
index 2a56bcc..21c3a92 100644 (file)
 #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;         \
@@ -115,13 +96,16 @@ static inline void __disable_dcache_nomsr(void)
 }
 
 
-/* Helper macro for computing the limits of cache range loops */
+/* Helper macro for computing the limits of cache range loops
+ *
+ * End address can be unaligned which is OK for C implementation.
+ * ASM implementation align it in ASM macros
+ */
 #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);
 
 /*
@@ -130,9 +114,9 @@ do {                                                                        \
  */
 #define CACHE_ALL_LOOP(cache_size, line_length, op)                    \
 do {                                                                   \
-       unsigned int len = cache_size;                                  \
+       unsigned int len = cache_size - line_length;                    \
        int step = -line_length;                                        \
-       BUG_ON(step >= 0);                                              \
+       WARN_ON(step >= 0);                                             \
                                                                        \
        __asm__ __volatile__ (" 1:      " #op " %0, r0;                 \
                                        bgtid   %0, 1b;                 \
@@ -141,30 +125,25 @@ do {                                                                      \
                                        : "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 */
+/* Used for wdc.flush/clear which can use rB for offset which is not possible
+ * to use for simple wdc or wic.
+ *
+ * start address is cache aligned
+ * end address is not aligned, if end is aligned then I have to substract
+ * cacheline length because I can't flush/invalidate the next cacheline.
+ * If is not, I align it because I will flush/invalidate whole line.
+ */
 #define CACHE_RANGE_LOOP_2(start, end, line_length, op)                        \
 do {                                                                   \
        int step = -line_length;                                        \
+       int align = ~(line_length - 1);                                 \
+       end = ((end & align) == end) ? end - line_length : end & align; \
        int count = end - start;                                        \
-       BUG_ON(count <= 0);                                             \
+       WARN_ON(count < 0);                                             \
                                                                        \
-       __asm__ __volatile__ (" 1:      " #op " %0, %1;                 \
-                                       bgtid   %1, 1b;                 \
-                                       addk    %1, %1, %2;             \
+       __asm__ __volatile__ (" 1:      " #op " %0, %1;                 \
+                                       bgtid   %1, 1b;                 \
+                                       addk    %1, %1, %2;             \
                                        " : : "r" (start), "r" (count), \
                                        "r" (step) : "memory");         \
 } while (0);
@@ -173,9 +152,11 @@ do {                                                                       \
 #define CACHE_RANGE_LOOP_1(start, end, line_length, op)                        \
 do {                                                                   \
        int volatile temp;                                              \
-       BUG_ON(end - start <= 0);                                       \
+       int align = ~(line_length - 1);                                 \
+       end = ((end & align) == end) ? end - line_length : end & align; \
+       WARN_ON(end - start < 0);                                       \
                                                                        \
-       __asm__ __volatile__ (" 1:      " #op " %1, r0;                 \
+       __asm__ __volatile__ (" 1:      " #op " %1, r0;                 \
                                        cmpu    %0, %1, %2;             \
                                        bgtid   %0, 1b;                 \
                                        addk    %1, %1, %3;             \
@@ -183,10 +164,14 @@ do {                                                                      \
                                        "r" (line_length) : "memory");  \
 } while (0);
 
+#define ASM_LOOP
+
 static void __flush_icache_range_msr_irq(unsigned long start, unsigned long end)
 {
        unsigned long flags;
-
+#ifndef ASM_LOOP
+       int i;
+#endif
        pr_debug("%s: start 0x%x, end 0x%x\n", __func__,
                                (unsigned int)start, (unsigned int) end);
 
@@ -196,8 +181,13 @@ static void __flush_icache_range_msr_irq(unsigned long start, unsigned long end)
        local_irq_save(flags);
        __disable_icache_msr();
 
+#ifdef ASM_LOOP
        CACHE_RANGE_LOOP_1(start, end, cpuinfo.icache_line_length, wic);
-
+#else
+       for (i = start; i < end; i += cpuinfo.icache_line_length)
+               __asm__ __volatile__ ("wic      %0, r0;"        \
+                               : : "r" (i));
+#endif
        __enable_icache_msr();
        local_irq_restore(flags);
 }
@@ -206,7 +196,9 @@ static void __flush_icache_range_nomsr_irq(unsigned long start,
                                unsigned long end)
 {
        unsigned long flags;
-
+#ifndef ASM_LOOP
+       int i;
+#endif
        pr_debug("%s: start 0x%x, end 0x%x\n", __func__,
                                (unsigned int)start, (unsigned int) end);
 
@@ -216,7 +208,13 @@ static void __flush_icache_range_nomsr_irq(unsigned long start,
        local_irq_save(flags);
        __disable_icache_nomsr();
 
+#ifdef ASM_LOOP
        CACHE_RANGE_LOOP_1(start, end, cpuinfo.icache_line_length, wic);
+#else
+       for (i = start; i < end; i += cpuinfo.icache_line_length)
+               __asm__ __volatile__ ("wic      %0, r0;"        \
+                               : : "r" (i));
+#endif
 
        __enable_icache_nomsr();
        local_irq_restore(flags);
@@ -225,25 +223,41 @@ static void __flush_icache_range_nomsr_irq(unsigned long start,
 static void __flush_icache_range_noirq(unsigned long start,
                                unsigned long end)
 {
+#ifndef ASM_LOOP
+       int i;
+#endif
        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);
+#ifdef ASM_LOOP
        CACHE_RANGE_LOOP_1(start, end, cpuinfo.icache_line_length, wic);
+#else
+       for (i = start; i < end; i += cpuinfo.icache_line_length)
+               __asm__ __volatile__ ("wic      %0, r0;"        \
+                               : : "r" (i));
+#endif
 }
 
 static void __flush_icache_all_msr_irq(void)
 {
        unsigned long flags;
-
+#ifndef ASM_LOOP
+       int i;
+#endif
        pr_debug("%s\n", __func__);
 
        local_irq_save(flags);
        __disable_icache_msr();
-
+#ifdef ASM_LOOP
        CACHE_ALL_LOOP(cpuinfo.icache_size, cpuinfo.icache_line_length, wic);
-
+#else
+       for (i = 0; i < cpuinfo.icache_size;
+                i += cpuinfo.icache_line_length)
+                       __asm__ __volatile__ ("wic      %0, r0;" \
+                                       : : "r" (i));
+#endif
        __enable_icache_msr();
        local_irq_restore(flags);
 }
@@ -251,35 +265,59 @@ static void __flush_icache_all_msr_irq(void)
 static void __flush_icache_all_nomsr_irq(void)
 {
        unsigned long flags;
-
+#ifndef ASM_LOOP
+       int i;
+#endif
        pr_debug("%s\n", __func__);
 
        local_irq_save(flags);
        __disable_icache_nomsr();
-
+#ifdef ASM_LOOP
        CACHE_ALL_LOOP(cpuinfo.icache_size, cpuinfo.icache_line_length, wic);
-
+#else
+       for (i = 0; i < cpuinfo.icache_size;
+                i += cpuinfo.icache_line_length)
+                       __asm__ __volatile__ ("wic      %0, r0;" \
+                                       : : "r" (i));
+#endif
        __enable_icache_nomsr();
        local_irq_restore(flags);
 }
 
 static void __flush_icache_all_noirq(void)
 {
+#ifndef ASM_LOOP
+       int i;
+#endif
        pr_debug("%s\n", __func__);
+#ifdef ASM_LOOP
        CACHE_ALL_LOOP(cpuinfo.icache_size, cpuinfo.icache_line_length, wic);
+#else
+       for (i = 0; i < cpuinfo.icache_size;
+                i += cpuinfo.icache_line_length)
+                       __asm__ __volatile__ ("wic      %0, r0;" \
+                                       : : "r" (i));
+#endif
 }
 
 static void __invalidate_dcache_all_msr_irq(void)
 {
        unsigned long flags;
-
+#ifndef ASM_LOOP
+       int i;
+#endif
        pr_debug("%s\n", __func__);
 
        local_irq_save(flags);
        __disable_dcache_msr();
-
+#ifdef ASM_LOOP
        CACHE_ALL_LOOP(cpuinfo.dcache_size, cpuinfo.dcache_line_length, wdc);
-
+#else
+       for (i = 0; i < cpuinfo.dcache_size;
+                i += cpuinfo.dcache_line_length)
+                       __asm__ __volatile__ ("wdc      %0, r0;" \
+                                       : : "r" (i));
+#endif
        __enable_dcache_msr();
        local_irq_restore(flags);
 }
@@ -287,60 +325,111 @@ static void __invalidate_dcache_all_msr_irq(void)
 static void __invalidate_dcache_all_nomsr_irq(void)
 {
        unsigned long flags;
-
+#ifndef ASM_LOOP
+       int i;
+#endif
        pr_debug("%s\n", __func__);
 
        local_irq_save(flags);
        __disable_dcache_nomsr();
-
+#ifdef ASM_LOOP
        CACHE_ALL_LOOP(cpuinfo.dcache_size, cpuinfo.dcache_line_length, wdc);
-
+#else
+       for (i = 0; i < cpuinfo.dcache_size;
+                i += cpuinfo.dcache_line_length)
+                       __asm__ __volatile__ ("wdc      %0, r0;" \
+                                       : : "r" (i));
+#endif
        __enable_dcache_nomsr();
        local_irq_restore(flags);
 }
 
 static void __invalidate_dcache_all_noirq_wt(void)
 {
+#ifndef ASM_LOOP
+       int i;
+#endif
        pr_debug("%s\n", __func__);
+#ifdef ASM_LOOP
        CACHE_ALL_LOOP(cpuinfo.dcache_size, cpuinfo.dcache_line_length, wdc)
+#else
+       for (i = 0; i < cpuinfo.dcache_size;
+                i += cpuinfo.dcache_line_length)
+                       __asm__ __volatile__ ("wdc      %0, r0;" \
+                                       : : "r" (i));
+#endif
 }
 
-/* FIXME this is weird - should be only wdc but not work
- * MS: I am getting bus errors and other weird things */
+/* FIXME It is blindly invalidation as is expected
+ * but can't be called on noMMU in microblaze_cache_init below
+ *
+ * MS: noMMU kernel won't boot if simple wdc is used
+ * The reason should be that there are discared data which kernel needs
+ */
 static void __invalidate_dcache_all_wb(void)
 {
+#ifndef ASM_LOOP
+       int i;
+#endif
        pr_debug("%s\n", __func__);
-       CACHE_ALL_LOOP2(cpuinfo.dcache_size, cpuinfo.dcache_line_length,
-                                       wdc.clear)
+#ifdef ASM_LOOP
+       CACHE_ALL_LOOP(cpuinfo.dcache_size, cpuinfo.dcache_line_length,
+                                       wdc)
+#else
+       for (i = 0; i < cpuinfo.dcache_size;
+                i += cpuinfo.dcache_line_length)
+                       __asm__ __volatile__ ("wdc      %0, r0;" \
+                                       : : "r" (i));
+#endif
 }
 
 static void __invalidate_dcache_range_wb(unsigned long start,
                                                unsigned long end)
 {
+#ifndef ASM_LOOP
+       int i;
+#endif
        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);
+#ifdef ASM_LOOP
        CACHE_RANGE_LOOP_2(start, end, cpuinfo.dcache_line_length, wdc.clear);
+#else
+       for (i = start; i < end; i += cpuinfo.dcache_line_length)
+               __asm__ __volatile__ ("wdc.clear        %0, r0;"        \
+                               : : "r" (i));
+#endif
 }
 
 static void __invalidate_dcache_range_nomsr_wt(unsigned long start,
                                                        unsigned long end)
 {
+#ifndef ASM_LOOP
+       int i;
+#endif
        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);
 
+#ifdef ASM_LOOP
        CACHE_RANGE_LOOP_1(start, end, cpuinfo.dcache_line_length, wdc);
+#else
+       for (i = start; i < end; i += cpuinfo.dcache_line_length)
+               __asm__ __volatile__ ("wdc      %0, r0;"        \
+                               : : "r" (i));
+#endif
 }
 
 static void __invalidate_dcache_range_msr_irq_wt(unsigned long start,
                                                        unsigned long end)
 {
        unsigned long flags;
-
+#ifndef ASM_LOOP
+       int i;
+#endif
        pr_debug("%s: start 0x%x, end 0x%x\n", __func__,
                                (unsigned int)start, (unsigned int) end);
        CACHE_LOOP_LIMITS(start, end,
@@ -349,7 +438,13 @@ static void __invalidate_dcache_range_msr_irq_wt(unsigned long start,
        local_irq_save(flags);
        __disable_dcache_msr();
 
+#ifdef ASM_LOOP
        CACHE_RANGE_LOOP_1(start, end, cpuinfo.dcache_line_length, wdc);
+#else
+       for (i = start; i < end; i += cpuinfo.dcache_line_length)
+               __asm__ __volatile__ ("wdc      %0, r0;"        \
+                               : : "r" (i));
+#endif
 
        __enable_dcache_msr();
        local_irq_restore(flags);
@@ -359,7 +454,9 @@ static void __invalidate_dcache_range_nomsr_irq(unsigned long start,
                                                        unsigned long end)
 {
        unsigned long flags;
-
+#ifndef ASM_LOOP
+       int i;
+#endif
        pr_debug("%s: start 0x%x, end 0x%x\n", __func__,
                                (unsigned int)start, (unsigned int) end);
 
@@ -369,7 +466,13 @@ static void __invalidate_dcache_range_nomsr_irq(unsigned long start,
        local_irq_save(flags);
        __disable_dcache_nomsr();
 
+#ifdef ASM_LOOP
        CACHE_RANGE_LOOP_1(start, end, cpuinfo.dcache_line_length, wdc);
+#else
+       for (i = start; i < end; i += cpuinfo.dcache_line_length)
+               __asm__ __volatile__ ("wdc      %0, r0;"        \
+                               : : "r" (i));
+#endif
 
        __enable_dcache_nomsr();
        local_irq_restore(flags);
@@ -377,19 +480,38 @@ static void __invalidate_dcache_range_nomsr_irq(unsigned long start,
 
 static void __flush_dcache_all_wb(void)
 {
+#ifndef ASM_LOOP
+       int i;
+#endif
        pr_debug("%s\n", __func__);
+#ifdef ASM_LOOP
        CACHE_ALL_LOOP(cpuinfo.dcache_size, cpuinfo.dcache_line_length,
                                wdc.flush);
+#else
+       for (i = 0; i < cpuinfo.dcache_size;
+                i += cpuinfo.dcache_line_length)
+                       __asm__ __volatile__ ("wdc.flush        %0, r0;" \
+                                       : : "r" (i));
+#endif
 }
 
 static void __flush_dcache_range_wb(unsigned long start, unsigned long end)
 {
+#ifndef ASM_LOOP
+       int i;
+#endif
        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);
+#ifdef ASM_LOOP
        CACHE_RANGE_LOOP_2(start, end, cpuinfo.dcache_line_length, wdc.flush);
+#else
+       for (i = start; i < end; i += cpuinfo.dcache_line_length)
+               __asm__ __volatile__ ("wdc.flush        %0, r0;"        \
+                               : : "r" (i));
+#endif
 }
 
 /* struct for wb caches and for wt caches */
@@ -493,7 +615,7 @@ const struct scache wt_nomsr_noirq = {
 #define CPUVER_7_20_A  0x0c
 #define CPUVER_7_20_D  0x0f
 
-#define INFO(s)        printk(KERN_INFO "cache: " s " \n");
+#define INFO(s)        printk(KERN_INFO "cache: " s "\n");
 
 void microblaze_cache_init(void)
 {
@@ -532,4 +654,13 @@ void microblaze_cache_init(void)
                        }
                }
        }
+/* FIXME Invalidation is done in U-BOOT
+ * WT cache: Data is already written to main memory
+ * WB cache: Discard data on noMMU which caused that kernel doesn't boot
+ */
+       /* invalidate_dcache(); */
+       enable_dcache();
+
+       invalidate_icache();
+       enable_icache();
 }