update from upstream
[pandora-kernel.git] / arch / mips / mm / pg-sb1.c
1 /*
2  * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com)
3  * Copyright (C) 1997, 2001 Ralf Baechle (ralf@gnu.org)
4  * Copyright (C) 2000 SiByte, Inc.
5  * Copyright (C) 2005 Thiemo Seufer
6  *
7  * Written by Justin Carlson of SiByte, Inc.
8  *         and Kip Walker of Broadcom Corp.
9  *
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License
13  * as published by the Free Software Foundation; either version 2
14  * of the License, or (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
24  */
25 #include <linux/config.h>
26 #include <linux/module.h>
27 #include <linux/sched.h>
28 #include <linux/smp.h>
29
30 #include <asm/io.h>
31 #include <asm/sibyte/sb1250.h>
32 #include <asm/sibyte/sb1250_regs.h>
33 #include <asm/sibyte/sb1250_dma.h>
34
35 #ifdef CONFIG_SB1_PASS_1_WORKAROUNDS
36 #define SB1_PREF_LOAD_STREAMED_HINT "0"
37 #define SB1_PREF_STORE_STREAMED_HINT "1"
38 #else
39 #define SB1_PREF_LOAD_STREAMED_HINT "4"
40 #define SB1_PREF_STORE_STREAMED_HINT "5"
41 #endif
42
43 static inline void clear_page_cpu(void *page)
44 {
45         unsigned char *addr = (unsigned char *) page;
46         unsigned char *end = addr + PAGE_SIZE;
47
48         /*
49          * JDCXXX - This should be bottlenecked by the write buffer, but these
50          * things tend to be mildly unpredictable...should check this on the
51          * performance model
52          *
53          * We prefetch 4 lines ahead.  We're also "cheating" slightly here...
54          * since we know we're on an SB1, we force the assembler to take
55          * 64-bit operands to speed things up
56          */
57         __asm__ __volatile__(
58         "       .set    push            \n"
59         "       .set    mips4           \n"
60         "       .set    noreorder       \n"
61 #ifdef CONFIG_CPU_HAS_PREFETCH
62         "       daddiu  %0, %0, 128     \n"
63         "       pref    " SB1_PREF_STORE_STREAMED_HINT ", -128(%0)  \n"  /* Prefetch the first 4 lines */
64         "       pref    " SB1_PREF_STORE_STREAMED_HINT ",  -96(%0)  \n"
65         "       pref    " SB1_PREF_STORE_STREAMED_HINT ",  -64(%0)  \n"
66         "       pref    " SB1_PREF_STORE_STREAMED_HINT ",  -32(%0)  \n"
67         "1:     sd      $0, -128(%0)    \n"  /* Throw out a cacheline of 0's */
68         "       sd      $0, -120(%0)    \n"
69         "       sd      $0, -112(%0)    \n"
70         "       sd      $0, -104(%0)    \n"
71         "       daddiu  %0, %0, 32      \n"
72         "       bnel    %0, %1, 1b      \n"
73         "        pref   " SB1_PREF_STORE_STREAMED_HINT ",  -32(%0)  \n"
74         "       daddiu  %0, %0, -128    \n"
75 #endif
76         "       sd      $0, 0(%0)       \n"  /* Throw out a cacheline of 0's */
77         "1:     sd      $0, 8(%0)       \n"
78         "       sd      $0, 16(%0)      \n"
79         "       sd      $0, 24(%0)      \n"
80         "       daddiu  %0, %0, 32      \n"
81         "       bnel    %0, %1, 1b      \n"
82         "        sd     $0, 0(%0)       \n"
83         "       .set    pop             \n"
84         : "+r" (addr)
85         : "r" (end)
86         : "memory");
87 }
88
89 static inline void copy_page_cpu(void *to, void *from)
90 {
91         unsigned char *src = (unsigned char *)from;
92         unsigned char *dst = (unsigned char *)to;
93         unsigned char *end = src + PAGE_SIZE;
94
95         /*
96          * The pref's used here are using "streaming" hints, which cause the
97          * copied data to be kicked out of the cache sooner.  A page copy often
98          * ends up copying a lot more data than is commonly used, so this seems
99          * to make sense in terms of reducing cache pollution, but I've no real
100          * performance data to back this up
101          */
102         __asm__ __volatile__(
103         "       .set    push            \n"
104         "       .set    mips4           \n"
105         "       .set    noreorder       \n"
106 #ifdef CONFIG_CPU_HAS_PREFETCH
107         "       daddiu  %0, %0, 128     \n"
108         "       daddiu  %1, %1, 128     \n"
109         "       pref    " SB1_PREF_LOAD_STREAMED_HINT  ", -128(%0)\n"  /* Prefetch the first 4 lines */
110         "       pref    " SB1_PREF_STORE_STREAMED_HINT ", -128(%1)\n"
111         "       pref    " SB1_PREF_LOAD_STREAMED_HINT  ",  -96(%0)\n"
112         "       pref    " SB1_PREF_STORE_STREAMED_HINT ",  -96(%1)\n"
113         "       pref    " SB1_PREF_LOAD_STREAMED_HINT  ",  -64(%0)\n"
114         "       pref    " SB1_PREF_STORE_STREAMED_HINT ",  -64(%1)\n"
115         "       pref    " SB1_PREF_LOAD_STREAMED_HINT  ",  -32(%0)\n"
116         "1:     pref    " SB1_PREF_STORE_STREAMED_HINT ",  -32(%1)\n"
117 # ifdef CONFIG_64BIT
118         "       ld      $8, -128(%0)    \n"  /* Block copy a cacheline */
119         "       ld      $9, -120(%0)    \n"
120         "       ld      $10, -112(%0)   \n"
121         "       ld      $11, -104(%0)   \n"
122         "       sd      $8, -128(%1)    \n"
123         "       sd      $9, -120(%1)    \n"
124         "       sd      $10, -112(%1)   \n"
125         "       sd      $11, -104(%1)   \n"
126 # else
127         "       lw      $2, -128(%0)    \n"  /* Block copy a cacheline */
128         "       lw      $3, -124(%0)    \n"
129         "       lw      $6, -120(%0)    \n"
130         "       lw      $7, -116(%0)    \n"
131         "       lw      $8, -112(%0)    \n"
132         "       lw      $9, -108(%0)    \n"
133         "       lw      $10, -104(%0)   \n"
134         "       lw      $11, -100(%0)   \n"
135         "       sw      $2, -128(%1)    \n"
136         "       sw      $3, -124(%1)    \n"
137         "       sw      $6, -120(%1)    \n"
138         "       sw      $7, -116(%1)    \n"
139         "       sw      $8, -112(%1)    \n"
140         "       sw      $9, -108(%1)    \n"
141         "       sw      $10, -104(%1)   \n"
142         "       sw      $11, -100(%1)   \n"
143 # endif
144         "       daddiu  %0, %0, 32      \n"
145         "       daddiu  %1, %1, 32      \n"
146         "       bnel    %0, %2, 1b      \n"
147         "        pref   " SB1_PREF_LOAD_STREAMED_HINT  ",  -32(%0)\n"
148         "       daddiu  %0, %0, -128    \n"
149         "       daddiu  %1, %1, -128    \n"
150 #endif
151 #ifdef CONFIG_64BIT
152         "       ld      $8, 0(%0)       \n"  /* Block copy a cacheline */
153         "1:     ld      $9, 8(%0)       \n"
154         "       ld      $10, 16(%0)     \n"
155         "       ld      $11, 24(%0)     \n"
156         "       sd      $8, 0(%1)       \n"
157         "       sd      $9, 8(%1)       \n"
158         "       sd      $10, 16(%1)     \n"
159         "       sd      $11, 24(%1)     \n"
160 #else
161         "       lw      $2, 0(%0)       \n"  /* Block copy a cacheline */
162         "1:     lw      $3, 4(%0)       \n"
163         "       lw      $6, 8(%0)       \n"
164         "       lw      $7, 12(%0)      \n"
165         "       lw      $8, 16(%0)      \n"
166         "       lw      $9, 20(%0)      \n"
167         "       lw      $10, 24(%0)     \n"
168         "       lw      $11, 28(%0)     \n"
169         "       sw      $2, 0(%1)       \n"
170         "       sw      $3, 4(%1)       \n"
171         "       sw      $6, 8(%1)       \n"
172         "       sw      $7, 12(%1)      \n"
173         "       sw      $8, 16(%1)      \n"
174         "       sw      $9, 20(%1)      \n"
175         "       sw      $10, 24(%1)     \n"
176         "       sw      $11, 28(%1)     \n"
177 #endif
178         "       daddiu  %0, %0, 32      \n"
179         "       daddiu  %1, %1, 32      \n"
180         "       bnel    %0, %2, 1b      \n"
181 #ifdef CONFIG_64BIT
182         "        ld     $8, 0(%0)       \n"
183 #else
184         "        lw     $2, 0(%0)       \n"
185 #endif
186         "       .set    pop             \n"
187         : "+r" (src), "+r" (dst)
188         : "r" (end)
189 #ifdef CONFIG_64BIT
190         : "$8","$9","$10","$11","memory");
191 #else
192         : "$2","$3","$6","$7","$8","$9","$10","$11","memory");
193 #endif
194 }
195
196
197 #ifdef CONFIG_SIBYTE_DMA_PAGEOPS
198
199 /*
200  * Pad descriptors to cacheline, since each is exclusively owned by a
201  * particular CPU.
202  */
203 typedef struct dmadscr_s {
204         u64 dscr_a;
205         u64 dscr_b;
206         u64 pad_a;
207         u64 pad_b;
208 } dmadscr_t;
209
210 static dmadscr_t page_descr[NR_CPUS] __attribute__((aligned(SMP_CACHE_BYTES)));
211
212 void sb1_dma_init(void)
213 {
214         int cpu = smp_processor_id();
215         u64 base_val = CPHYSADDR(&page_descr[cpu]) | V_DM_DSCR_BASE_RINGSZ(1);
216
217         bus_writeq(base_val,
218                    (void *)IOADDR(A_DM_REGISTER(cpu, R_DM_DSCR_BASE)));
219         bus_writeq(base_val | M_DM_DSCR_BASE_RESET,
220                    (void *)IOADDR(A_DM_REGISTER(cpu, R_DM_DSCR_BASE)));
221         bus_writeq(base_val | M_DM_DSCR_BASE_ENABL,
222                    (void *)IOADDR(A_DM_REGISTER(cpu, R_DM_DSCR_BASE)));
223 }
224
225 void clear_page(void *page)
226 {
227         int cpu = smp_processor_id();
228
229         /* if the page is above Kseg0, use old way */
230         if ((long)KSEGX(page) != (long)CKSEG0)
231                 return clear_page_cpu(page);
232
233         page_descr[cpu].dscr_a = CPHYSADDR(page) | M_DM_DSCRA_ZERO_MEM | M_DM_DSCRA_L2C_DEST | M_DM_DSCRA_INTERRUPT;
234         page_descr[cpu].dscr_b = V_DM_DSCRB_SRC_LENGTH(PAGE_SIZE);
235         bus_writeq(1, (void *)IOADDR(A_DM_REGISTER(cpu, R_DM_DSCR_COUNT)));
236
237         /*
238          * Don't really want to do it this way, but there's no
239          * reliable way to delay completion detection.
240          */
241         while (!(bus_readq((void *)(IOADDR(A_DM_REGISTER(cpu, R_DM_DSCR_BASE_DEBUG)) &
242                            M_DM_DSCR_BASE_INTERRUPT))))
243                 ;
244         bus_readq((void *)IOADDR(A_DM_REGISTER(cpu, R_DM_DSCR_BASE)));
245 }
246
247 void copy_page(void *to, void *from)
248 {
249         unsigned long from_phys = CPHYSADDR(from);
250         unsigned long to_phys = CPHYSADDR(to);
251         int cpu = smp_processor_id();
252
253         /* if either page is above Kseg0, use old way */
254         if ((long)KSEGX(to) != (long)CKSEG0
255             || (long)KSEGX(from) != (long)CKSEG0)
256                 return copy_page_cpu(to, from);
257
258         page_descr[cpu].dscr_a = CPHYSADDR(to_phys) | M_DM_DSCRA_L2C_DEST | M_DM_DSCRA_INTERRUPT;
259         page_descr[cpu].dscr_b = CPHYSADDR(from_phys) | V_DM_DSCRB_SRC_LENGTH(PAGE_SIZE);
260         bus_writeq(1, (void *)IOADDR(A_DM_REGISTER(cpu, R_DM_DSCR_COUNT)));
261
262         /*
263          * Don't really want to do it this way, but there's no
264          * reliable way to delay completion detection.
265          */
266         while (!(bus_readq((void *)(IOADDR(A_DM_REGISTER(cpu, R_DM_DSCR_BASE_DEBUG)) &
267                                     M_DM_DSCR_BASE_INTERRUPT))))
268                 ;
269         bus_readq((void *)IOADDR(A_DM_REGISTER(cpu, R_DM_DSCR_BASE)));
270 }
271
272 #else /* !CONFIG_SIBYTE_DMA_PAGEOPS */
273
274 void clear_page(void *page)
275 {
276         return clear_page_cpu(page);
277 }
278
279 void copy_page(void *to, void *from)
280 {
281         return copy_page_cpu(to, from);
282 }
283
284 #endif /* !CONFIG_SIBYTE_DMA_PAGEOPS */
285
286 EXPORT_SYMBOL(clear_page);
287 EXPORT_SYMBOL(copy_page);