Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/sparc-2.6
[linux-2.6] / arch / powerpc / platforms / ps3 / htab.c
1 /*
2  *  PS3 pagetable management routines.
3  *
4  *  Copyright (C) 2006 Sony Computer Entertainment Inc.
5  *  Copyright 2006, 2007 Sony Corporation
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; version 2 of the License.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include <linux/kernel.h>
22 #include <linux/lmb.h>
23
24 #include <asm/machdep.h>
25 #include <asm/prom.h>
26 #include <asm/udbg.h>
27 #include <asm/lv1call.h>
28 #include <asm/ps3fb.h>
29
30 #include "platform.h"
31
32 #if defined(DEBUG)
33 #define DBG udbg_printf
34 #else
35 #define DBG pr_debug
36 #endif
37
38 static struct hash_pte *htab;
39 static unsigned long htab_addr;
40 static unsigned char *bolttab;
41 static unsigned char *inusetab;
42
43 static DEFINE_SPINLOCK(ps3_bolttab_lock);
44
45 #define debug_dump_hpte(_a, _b, _c, _d, _e, _f, _g) \
46         _debug_dump_hpte(_a, _b, _c, _d, _e, _f, _g, __func__, __LINE__)
47 static void _debug_dump_hpte(unsigned long pa, unsigned long va,
48         unsigned long group, unsigned long bitmap, struct hash_pte lhpte,
49         int psize, unsigned long slot, const char* func, int line)
50 {
51         DBG("%s:%d: pa     = %lxh\n", func, line, pa);
52         DBG("%s:%d: lpar   = %lxh\n", func, line,
53                 ps3_mm_phys_to_lpar(pa));
54         DBG("%s:%d: va     = %lxh\n", func, line, va);
55         DBG("%s:%d: group  = %lxh\n", func, line, group);
56         DBG("%s:%d: bitmap = %lxh\n", func, line, bitmap);
57         DBG("%s:%d: hpte.v = %lxh\n", func, line, lhpte.v);
58         DBG("%s:%d: hpte.r = %lxh\n", func, line, lhpte.r);
59         DBG("%s:%d: psize  = %xh\n", func, line, psize);
60         DBG("%s:%d: slot   = %lxh\n", func, line, slot);
61 }
62
63 static long ps3_hpte_insert(unsigned long hpte_group, unsigned long va,
64         unsigned long pa, unsigned long rflags, unsigned long vflags,
65         int psize, int ssize)
66 {
67         unsigned long slot;
68         struct hash_pte lhpte;
69         int secondary = 0;
70         unsigned long result;
71         unsigned long bitmap;
72         unsigned long flags;
73         unsigned long p_pteg, s_pteg, b_index, b_mask, cb, ci;
74
75         vflags &= ~HPTE_V_SECONDARY; /* this bit is ignored */
76
77         lhpte.v = hpte_encode_v(va, psize, MMU_SEGSIZE_256M) |
78                 vflags | HPTE_V_VALID;
79         lhpte.r = hpte_encode_r(ps3_mm_phys_to_lpar(pa), psize) | rflags;
80
81         p_pteg = hpte_group / HPTES_PER_GROUP;
82         s_pteg = ~p_pteg & htab_hash_mask;
83
84         spin_lock_irqsave(&ps3_bolttab_lock, flags);
85
86         BUG_ON(bolttab[p_pteg] == 0xff && bolttab[s_pteg] == 0xff);
87
88         bitmap = (inusetab[p_pteg] << 8) | inusetab[s_pteg];
89
90         if (bitmap == 0xffff) {
91                 /*
92                  * PTEG is full. Search for victim.
93                  */
94                 bitmap &= ~((bolttab[p_pteg] << 8) | bolttab[s_pteg]);
95                 do {
96                         ci = mftb() & 15;
97                         cb = 0x8000UL >> ci;
98                 } while ((cb & bitmap) == 0);
99         } else {
100                 /*
101                  * search free slot in hardware order
102                  *      [primary]       0, 2, 4, 6, 1, 3, 5, 7
103                  *      [secondary]     0, 2, 4, 6, 1, 3, 5, 7
104                  */
105                 for (ci = 0; ci < HPTES_PER_GROUP; ci += 2) {
106                         cb = 0x8000UL >> ci;
107                         if ((cb & bitmap) == 0)
108                                 goto found;
109                 }
110                 for (ci = 1; ci < HPTES_PER_GROUP; ci += 2) {
111                         cb = 0x8000UL >> ci;
112                         if ((cb & bitmap) == 0)
113                                 goto found;
114                 }
115                 for (ci = HPTES_PER_GROUP; ci < HPTES_PER_GROUP*2; ci += 2) {
116                         cb = 0x8000UL >> ci;
117                         if ((cb & bitmap) == 0)
118                                 goto found;
119                 }
120                 for (ci = HPTES_PER_GROUP+1; ci < HPTES_PER_GROUP*2; ci += 2) {
121                         cb = 0x8000UL >> ci;
122                         if ((cb & bitmap) == 0)
123                                 goto found;
124                 }
125         }
126
127 found:
128         if (ci < HPTES_PER_GROUP) {
129                 slot = p_pteg * HPTES_PER_GROUP + ci;
130         } else {
131                 slot = s_pteg * HPTES_PER_GROUP + (ci & 7);
132                 /* lhpte.dw0.dw0.h = 1; */
133                 vflags |= HPTE_V_SECONDARY;
134                 lhpte.v |= HPTE_V_SECONDARY;
135         }
136
137         result = lv1_write_htab_entry(0, slot, lhpte.v, lhpte.r);
138
139         if (result) {
140                 debug_dump_hpte(pa, va, hpte_group, bitmap, lhpte, psize, slot);
141                 BUG();
142         }
143
144         /*
145          * If used slot is not in primary HPTE group,
146          * the slot should be in secondary HPTE group.
147          */
148
149         if ((hpte_group ^ slot) & ~(HPTES_PER_GROUP - 1)) {
150                 secondary = 1;
151                 b_index = s_pteg;
152         } else {
153                 secondary = 0;
154                 b_index = p_pteg;
155         }
156
157         b_mask = (lhpte.v & HPTE_V_BOLTED) ? 1 << 7 : 0 << 7;
158         bolttab[b_index] |= b_mask >> (slot & 7);
159         b_mask = 1 << 7;
160         inusetab[b_index] |= b_mask >> (slot & 7);
161         spin_unlock_irqrestore(&ps3_bolttab_lock, flags);
162
163         return (slot & 7) | (secondary << 3);
164 }
165
166 static long ps3_hpte_remove(unsigned long hpte_group)
167 {
168         panic("ps3_hpte_remove() not implemented");
169         return 0;
170 }
171
172 static long ps3_hpte_updatepp(unsigned long slot, unsigned long newpp,
173         unsigned long va, int psize, int ssize, int local)
174 {
175         unsigned long flags;
176         unsigned long result;
177         unsigned long pteg, bit;
178         unsigned long hpte_v, want_v;
179
180         want_v = hpte_encode_v(va, psize, MMU_SEGSIZE_256M);
181
182         spin_lock_irqsave(&ps3_bolttab_lock, flags);
183
184         hpte_v = htab[slot].v;
185         if (!HPTE_V_COMPARE(hpte_v, want_v) || !(hpte_v & HPTE_V_VALID)) {
186                 spin_unlock_irqrestore(&ps3_bolttab_lock, flags);
187
188                 /* ps3_hpte_insert() will be used to update PTE */
189                 return -1;
190         }
191
192         result = lv1_write_htab_entry(0, slot, 0, 0);
193
194         if (result) {
195                 DBG("%s: va=%lx slot=%lx psize=%d result = %ld (0x%lx)\n",
196                        __func__, va, slot, psize, result, result);
197                 BUG();
198         }
199
200         pteg = slot / HPTES_PER_GROUP;
201         bit = slot % HPTES_PER_GROUP;
202         inusetab[pteg] &= ~(0x80 >> bit);
203
204         spin_unlock_irqrestore(&ps3_bolttab_lock, flags);
205
206         /* ps3_hpte_insert() will be used to update PTE */
207         return -1;
208 }
209
210 static void ps3_hpte_updateboltedpp(unsigned long newpp, unsigned long ea,
211         int psize, int ssize)
212 {
213         panic("ps3_hpte_updateboltedpp() not implemented");
214 }
215
216 static void ps3_hpte_invalidate(unsigned long slot, unsigned long va,
217         int psize, int ssize, int local)
218 {
219         unsigned long flags;
220         unsigned long result;
221         unsigned long pteg, bit;
222
223         spin_lock_irqsave(&ps3_bolttab_lock, flags);
224         result = lv1_write_htab_entry(0, slot, 0, 0);
225
226         if (result) {
227                 DBG("%s: va=%lx slot=%lx psize=%d result = %ld (0x%lx)\n",
228                        __func__, va, slot, psize, result, result);
229                 BUG();
230         }
231
232         pteg = slot / HPTES_PER_GROUP;
233         bit = slot % HPTES_PER_GROUP;
234         inusetab[pteg] &= ~(0x80 >> bit);
235         spin_unlock_irqrestore(&ps3_bolttab_lock, flags);
236 }
237
238 static void ps3_hpte_clear(void)
239 {
240         int result;
241
242         DBG(" -> %s:%d\n", __func__, __LINE__);
243
244         result = lv1_unmap_htab(htab_addr);
245         BUG_ON(result);
246
247         ps3_mm_shutdown();
248         ps3_mm_vas_destroy();
249
250         DBG(" <- %s:%d\n", __func__, __LINE__);
251 }
252
253 void __init ps3_hpte_init(unsigned long htab_size)
254 {
255         long bitmap_size;
256
257         DBG(" -> %s:%d\n", __func__, __LINE__);
258
259         ppc_md.hpte_invalidate = ps3_hpte_invalidate;
260         ppc_md.hpte_updatepp = ps3_hpte_updatepp;
261         ppc_md.hpte_updateboltedpp = ps3_hpte_updateboltedpp;
262         ppc_md.hpte_insert = ps3_hpte_insert;
263         ppc_md.hpte_remove = ps3_hpte_remove;
264         ppc_md.hpte_clear_all = ps3_hpte_clear;
265
266         ppc64_pft_size = __ilog2(htab_size);
267
268         bitmap_size = htab_size / sizeof(struct hash_pte) / 8;
269
270         bolttab = __va(lmb_alloc(bitmap_size, 1));
271         inusetab = __va(lmb_alloc(bitmap_size, 1));
272
273         memset(bolttab, 0, bitmap_size);
274         memset(inusetab, 0, bitmap_size);
275
276         DBG(" <- %s:%d\n", __func__, __LINE__);
277 }
278
279 void __init ps3_map_htab(void)
280 {
281         long result;
282         unsigned long htab_size = (1UL << ppc64_pft_size);
283
284         result = lv1_map_htab(0, &htab_addr);
285
286         htab = (__force struct hash_pte *)ioremap_flags(htab_addr, htab_size,
287                                             pgprot_val(PAGE_READONLY_X));
288
289         DBG("%s:%d: lpar %016lxh, virt %016lxh\n", __func__, __LINE__,
290                 htab_addr, (unsigned long)htab);
291 }