[buildroot] disable sstrip when using musl
[openwrt.git] / target / linux / ubicom32 / files / arch / ubicom32 / mach-common / cachectl.c
1 /*
2  * arch/ubicom32/mach-common/cachectl.c
3  *   Architecture cache control support
4  *
5  * (C) Copyright 2009, Ubicom, Inc.
6  *
7  * This file is part of the Ubicom32 Linux Kernel Port.
8  *
9  * The Ubicom32 Linux Kernel Port is free software: you can redistribute
10  * it and/or modify it under the terms of the GNU General Public License
11  * as published by the Free Software Foundation, either version 2 of the
12  * License, or (at your option) any later version.
13  *
14  * The Ubicom32 Linux Kernel Port is distributed in the hope that it
15  * will be useful, but WITHOUT ANY WARRANTY; without even the implied
16  * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
17  * the GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with the Ubicom32 Linux Kernel Port.  If not,
21  * see <http://www.gnu.org/licenses/>.
22  *
23  * Ubicom32 implementation derived from (with many thanks):
24  *   arch/m68knommu
25  *   arch/blackfin
26  *   arch/parisc
27  */
28
29 #include <linux/types.h>
30 #include <linux/module.h>
31 #include <asm/cachectl.h>
32
33 /*
34  * The write queue flush procedure in mem_cache_control needs to make
35  * DCACHE_WRITE_QUEUE_LENGTH writes to DDR (not OCM). Here we reserve some
36  * memory for this operation.
37  * Allocate array of cache lines of least DCACHE_WRITE_QUEUE_LENGTH + 1 words in
38  * length rounded up to the nearest cache line.
39  */
40 #define CACHE_WRITE_QUEUE_FLUSH_AREA_SIZE \
41         ALIGN(sizeof(int) * (DCACHE_WRITE_QUEUE_LENGTH + 1), CACHE_LINE_SIZE)
42
43 static char cache_write_queue_flush_area[CACHE_WRITE_QUEUE_FLUSH_AREA_SIZE]
44         __attribute__((aligned(CACHE_LINE_SIZE)));
45
46 /*
47  * ONE_CCR_ADDR_OP is a helper macro that executes a single CCR operation.
48  */
49 #define ONE_CCR_ADDR_OP(cc, op_addr, op)                                \
50         do {                                                            \
51                 asm volatile (                                          \
52                 "       btst    "D(CCR_CTRL)"(%0), #"D(CCR_CTRL_VALID)"                         \n\t" \
53                 "       jmpne.f .-4                                                             \n\t" \
54                 "       move.4  "D(CCR_ADDR)"(%0), %1                                           \n\t" \
55                 "       move.1  "D(CCR_CTRL+3)"(%0), %2                                         \n\t" \
56                 "       bset    "D(CCR_CTRL)"(%0), "D(CCR_CTRL)"(%0), #"D(CCR_CTRL_VALID)"      \n\t" \
57                 "       cycles  2                                                               \n\t" \
58                 "       btst    "D(CCR_CTRL)"(%0), #"D(CCR_CTRL_DONE)"                          \n\t" \
59                 "       jmpeq.f .-4                                                             \n\t" \
60                         :                                               \
61                         : "a"(cc), "r"(op_addr), "r"(op & 0xff)         \
62                         : "cc"                                          \
63                 );                                                      \
64         } while (0)
65
66 /*
67  * mem_cache_control()
68  *      Special cache control operation
69  */
70 void mem_cache_control(unsigned long cc, unsigned long begin_addr,
71                        unsigned long end_addr, unsigned long op)
72 {
73         unsigned long op_addr;
74         int dccr = cc == DCCR_BASE;
75         if (dccr && op == CCR_CTRL_FLUSH_ADDR) {
76                 /*
77                  * We ensure all previous writes have left the data cache write
78                  * queue by sending DCACHE_WRITE_QUEUE_LENGTH writes (to
79                  * different words) down the queue.  If this is not done it's
80                  * possible that the data we are trying to flush hasn't even
81                  * entered the data cache.
82                  * The +1 ensure that the final 'flush' is actually a flush.
83                  */
84                 int *flush_area = (int *)cache_write_queue_flush_area;
85                 asm volatile(
86                         "       .rept "D(DCACHE_WRITE_QUEUE_LENGTH + 1)"        \n\t"
87                         "       move.4 (%0)4++, d0                              \n\t"
88                         "       .endr                                           \n\t"
89                         : "+a"(flush_area)
90                         );
91         }
92
93         if (dccr)
94                 UBICOM32_LOCK(DCCR_LOCK_BIT);
95         else
96                 UBICOM32_LOCK(ICCR_LOCK_BIT);
97
98         /*
99          * Calculate the cache lines we need to operate on that include
100          * begin_addr though end_addr.
101          */
102         begin_addr = begin_addr & ~(CACHE_LINE_SIZE - 1);
103         end_addr = (end_addr + CACHE_LINE_SIZE - 1) & ~(CACHE_LINE_SIZE - 1);
104         op_addr = begin_addr;
105
106         do {
107                 ONE_CCR_ADDR_OP(cc, op_addr, op);
108                 op_addr += CACHE_LINE_SIZE;
109         } while (likely(op_addr < end_addr));
110
111         if (dccr && op == CCR_CTRL_FLUSH_ADDR) {
112                 /*
113                  * It turns out that when flushing the data cache the last flush
114                  * isn't actually complete at this point. This is because there
115                  * is another write buffer on the DDR side of the cache that is
116                  * arbitrated with the I-Cache.
117                  *
118                  * The only foolproof method that ensures that the last data
119                  * cache flush *actually* completed is to do another flush on a
120                  * dirty cache line. This flush will block until the DDR write
121                  * buffer is empty.
122                  *
123                  * Rather than creating a another dirty cache line, we use the
124                  * flush_area above as we know that it is dirty from previous
125                  * writes.
126                  */
127                 ONE_CCR_ADDR_OP(cc, cache_write_queue_flush_area, op);
128         }
129
130         if (dccr)
131                 UBICOM32_UNLOCK(DCCR_LOCK_BIT);
132         else
133                 UBICOM32_UNLOCK(ICCR_LOCK_BIT);
134
135 }
136 EXPORT_SYMBOL(mem_cache_control);