diff options
author | Geoff Levand <geoffrey.levand@am.sony.com> | 2006-11-22 18:46:54 -0500 |
---|---|---|
committer | Paul Mackerras <paulus@samba.org> | 2006-12-04 04:40:47 -0500 |
commit | c6cec72b7ca05822688a952df97b1c24e69a0ef6 (patch) | |
tree | e5cb011d0771426796d6dddc831cc41f71ccd75a /arch/powerpc/platforms | |
parent | 82a527f0bd84977d7e85f15fb1f987c42cc0a68d (diff) |
[POWERPC] ps3: add htab routines
Adds pagetable management routines for the PS3.
Signed-off-by: Geoff Levand <geoffrey.levand@am.sony.com>
Signed-off-by: Arnd Bergmann <arnd.bergmann@de.ibm.com>
Diffstat (limited to 'arch/powerpc/platforms')
-rw-r--r-- | arch/powerpc/platforms/ps3/htab.c | 277 |
1 files changed, 277 insertions, 0 deletions
diff --git a/arch/powerpc/platforms/ps3/htab.c b/arch/powerpc/platforms/ps3/htab.c new file mode 100644 index 000000000000..8fe1769655a3 --- /dev/null +++ b/arch/powerpc/platforms/ps3/htab.c | |||
@@ -0,0 +1,277 @@ | |||
1 | /* | ||
2 | * PS3 pagetable management routines. | ||
3 | * | ||
4 | * Copyright (C) 2006 Sony Computer Entertainment Inc. | ||
5 | * Copyright 2006 Sony Corp. | ||
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 | |||
23 | #include <asm/machdep.h> | ||
24 | #include <asm/lmb.h> | ||
25 | #include <asm/udbg.h> | ||
26 | #include <asm/ps3.h> | ||
27 | #include <asm/lv1call.h> | ||
28 | |||
29 | #include "platform.h" | ||
30 | |||
31 | #if defined(DEBUG) | ||
32 | #define DBG(fmt...) udbg_printf(fmt) | ||
33 | #else | ||
34 | #define DBG(fmt...) do{if(0)printk(fmt);}while(0) | ||
35 | #endif | ||
36 | |||
37 | static hpte_t *htab; | ||
38 | static unsigned long htab_addr; | ||
39 | static unsigned char *bolttab; | ||
40 | static unsigned char *inusetab; | ||
41 | |||
42 | static spinlock_t ps3_bolttab_lock = SPIN_LOCK_UNLOCKED; | ||
43 | |||
44 | #define debug_dump_hpte(_a, _b, _c, _d, _e, _f, _g) \ | ||
45 | _debug_dump_hpte(_a, _b, _c, _d, _e, _f, _g, __func__, __LINE__) | ||
46 | static void _debug_dump_hpte(unsigned long pa, unsigned long va, | ||
47 | unsigned long group, unsigned long bitmap, hpte_t lhpte, int psize, | ||
48 | unsigned long slot, const char* func, int line) | ||
49 | { | ||
50 | DBG("%s:%d: pa = %lxh\n", func, line, pa); | ||
51 | DBG("%s:%d: lpar = %lxh\n", func, line, | ||
52 | ps3_mm_phys_to_lpar(pa)); | ||
53 | DBG("%s:%d: va = %lxh\n", func, line, va); | ||
54 | DBG("%s:%d: group = %lxh\n", func, line, group); | ||
55 | DBG("%s:%d: bitmap = %lxh\n", func, line, bitmap); | ||
56 | DBG("%s:%d: hpte.v = %lxh\n", func, line, lhpte.v); | ||
57 | DBG("%s:%d: hpte.r = %lxh\n", func, line, lhpte.r); | ||
58 | DBG("%s:%d: psize = %xh\n", func, line, psize); | ||
59 | DBG("%s:%d: slot = %lxh\n", func, line, slot); | ||
60 | } | ||
61 | |||
62 | static long ps3_hpte_insert(unsigned long hpte_group, unsigned long va, | ||
63 | unsigned long pa, unsigned long rflags, unsigned long vflags, int psize) | ||
64 | { | ||
65 | unsigned long slot; | ||
66 | hpte_t lhpte; | ||
67 | int secondary = 0; | ||
68 | unsigned long result; | ||
69 | unsigned long bitmap; | ||
70 | unsigned long flags; | ||
71 | unsigned long p_pteg, s_pteg, b_index, b_mask, cb, ci; | ||
72 | |||
73 | vflags &= ~HPTE_V_SECONDARY; /* this bit is ignored */ | ||
74 | |||
75 | lhpte.v = hpte_encode_v(va, psize) | vflags | HPTE_V_VALID; | ||
76 | lhpte.r = hpte_encode_r(ps3_mm_phys_to_lpar(pa), psize) | rflags; | ||
77 | |||
78 | p_pteg = hpte_group / HPTES_PER_GROUP; | ||
79 | s_pteg = ~p_pteg & htab_hash_mask; | ||
80 | |||
81 | spin_lock_irqsave(&ps3_bolttab_lock, flags); | ||
82 | |||
83 | BUG_ON(bolttab[p_pteg] == 0xff && bolttab[s_pteg] == 0xff); | ||
84 | |||
85 | bitmap = (inusetab[p_pteg] << 8) | inusetab[s_pteg]; | ||
86 | |||
87 | if (bitmap == 0xffff) { | ||
88 | /* | ||
89 | * PTEG is full. Search for victim. | ||
90 | */ | ||
91 | bitmap &= ~((bolttab[p_pteg] << 8) | bolttab[s_pteg]); | ||
92 | do { | ||
93 | ci = mftb() & 15; | ||
94 | cb = 0x8000UL >> ci; | ||
95 | } while ((cb & bitmap) == 0); | ||
96 | } else { | ||
97 | /* | ||
98 | * search free slot in hardware order | ||
99 | * [primary] 0, 2, 4, 6, 1, 3, 5, 7 | ||
100 | * [secondary] 0, 2, 4, 6, 1, 3, 5, 7 | ||
101 | */ | ||
102 | for (ci = 0; ci < HPTES_PER_GROUP; ci += 2) { | ||
103 | cb = 0x8000UL >> ci; | ||
104 | if ((cb & bitmap) == 0) | ||
105 | goto found; | ||
106 | } | ||
107 | for (ci = 1; ci < HPTES_PER_GROUP; ci += 2) { | ||
108 | cb = 0x8000UL >> ci; | ||
109 | if ((cb & bitmap) == 0) | ||
110 | goto found; | ||
111 | } | ||
112 | for (ci = HPTES_PER_GROUP; ci < HPTES_PER_GROUP*2; ci += 2) { | ||
113 | cb = 0x8000UL >> ci; | ||
114 | if ((cb & bitmap) == 0) | ||
115 | goto found; | ||
116 | } | ||
117 | for (ci = HPTES_PER_GROUP+1; ci < HPTES_PER_GROUP*2; ci += 2) { | ||
118 | cb = 0x8000UL >> ci; | ||
119 | if ((cb & bitmap) == 0) | ||
120 | goto found; | ||
121 | } | ||
122 | } | ||
123 | |||
124 | found: | ||
125 | if (ci < HPTES_PER_GROUP) { | ||
126 | slot = p_pteg * HPTES_PER_GROUP + ci; | ||
127 | } else { | ||
128 | slot = s_pteg * HPTES_PER_GROUP + (ci & 7); | ||
129 | /* lhpte.dw0.dw0.h = 1; */ | ||
130 | vflags |= HPTE_V_SECONDARY; | ||
131 | lhpte.v |= HPTE_V_SECONDARY; | ||
132 | } | ||
133 | |||
134 | result = lv1_write_htab_entry(0, slot, lhpte.v, lhpte.r); | ||
135 | |||
136 | if (result) { | ||
137 | debug_dump_hpte(pa, va, hpte_group, bitmap, lhpte, psize, slot); | ||
138 | BUG(); | ||
139 | } | ||
140 | |||
141 | /* | ||
142 | * If used slot is not in primary HPTE group, | ||
143 | * the slot should be in secondary HPTE group. | ||
144 | */ | ||
145 | |||
146 | if ((hpte_group ^ slot) & ~(HPTES_PER_GROUP - 1)) { | ||
147 | secondary = 1; | ||
148 | b_index = s_pteg; | ||
149 | } else { | ||
150 | secondary = 0; | ||
151 | b_index = p_pteg; | ||
152 | } | ||
153 | |||
154 | b_mask = (lhpte.v & HPTE_V_BOLTED) ? 1 << 7 : 0 << 7; | ||
155 | bolttab[b_index] |= b_mask >> (slot & 7); | ||
156 | b_mask = 1 << 7; | ||
157 | inusetab[b_index] |= b_mask >> (slot & 7); | ||
158 | spin_unlock_irqrestore(&ps3_bolttab_lock, flags); | ||
159 | |||
160 | return (slot & 7) | (secondary << 3); | ||
161 | } | ||
162 | |||
163 | static long ps3_hpte_remove(unsigned long hpte_group) | ||
164 | { | ||
165 | panic("ps3_hpte_remove() not implemented"); | ||
166 | return 0; | ||
167 | } | ||
168 | |||
169 | static long ps3_hpte_updatepp(unsigned long slot, unsigned long newpp, | ||
170 | unsigned long va, int psize, int local) | ||
171 | { | ||
172 | unsigned long flags; | ||
173 | unsigned long result; | ||
174 | unsigned long pteg, bit; | ||
175 | unsigned long hpte_v, want_v; | ||
176 | |||
177 | want_v = hpte_encode_v(va, psize); | ||
178 | |||
179 | spin_lock_irqsave(&ps3_bolttab_lock, flags); | ||
180 | |||
181 | hpte_v = htab[slot].v; | ||
182 | if (!HPTE_V_COMPARE(hpte_v, want_v) || !(hpte_v & HPTE_V_VALID)) { | ||
183 | spin_unlock_irqrestore(&ps3_bolttab_lock, flags); | ||
184 | |||
185 | /* ps3_hpte_insert() will be used to update PTE */ | ||
186 | return -1; | ||
187 | } | ||
188 | |||
189 | result = lv1_write_htab_entry(0, slot, 0, 0); | ||
190 | |||
191 | if (result) { | ||
192 | DBG("%s: va=%lx slot=%lx psize=%d result = %ld (0x%lx)\n", | ||
193 | __func__, va, slot, psize, result, result); | ||
194 | BUG(); | ||
195 | } | ||
196 | |||
197 | pteg = slot / HPTES_PER_GROUP; | ||
198 | bit = slot % HPTES_PER_GROUP; | ||
199 | inusetab[pteg] &= ~(0x80 >> bit); | ||
200 | |||
201 | spin_unlock_irqrestore(&ps3_bolttab_lock, flags); | ||
202 | |||
203 | /* ps3_hpte_insert() will be used to update PTE */ | ||
204 | return -1; | ||
205 | } | ||
206 | |||
207 | static void ps3_hpte_updateboltedpp(unsigned long newpp, unsigned long ea, | ||
208 | int psize) | ||
209 | { | ||
210 | panic("ps3_hpte_updateboltedpp() not implemented"); | ||
211 | } | ||
212 | |||
213 | static void ps3_hpte_invalidate(unsigned long slot, unsigned long va, | ||
214 | int psize, int local) | ||
215 | { | ||
216 | unsigned long flags; | ||
217 | unsigned long result; | ||
218 | unsigned long pteg, bit; | ||
219 | |||
220 | spin_lock_irqsave(&ps3_bolttab_lock, flags); | ||
221 | result = lv1_write_htab_entry(0, slot, 0, 0); | ||
222 | |||
223 | if (result) { | ||
224 | DBG("%s: va=%lx slot=%lx psize=%d result = %ld (0x%lx)\n", | ||
225 | __func__, va, slot, psize, result, result); | ||
226 | BUG(); | ||
227 | } | ||
228 | |||
229 | pteg = slot / HPTES_PER_GROUP; | ||
230 | bit = slot % HPTES_PER_GROUP; | ||
231 | inusetab[pteg] &= ~(0x80 >> bit); | ||
232 | spin_unlock_irqrestore(&ps3_bolttab_lock, flags); | ||
233 | } | ||
234 | |||
235 | static void ps3_hpte_clear(void) | ||
236 | { | ||
237 | lv1_unmap_htab(htab_addr); | ||
238 | } | ||
239 | |||
240 | void __init ps3_hpte_init(unsigned long htab_size) | ||
241 | { | ||
242 | long bitmap_size; | ||
243 | |||
244 | DBG(" -> %s:%d\n", __func__, __LINE__); | ||
245 | |||
246 | ppc_md.hpte_invalidate = ps3_hpte_invalidate; | ||
247 | ppc_md.hpte_updatepp = ps3_hpte_updatepp; | ||
248 | ppc_md.hpte_updateboltedpp = ps3_hpte_updateboltedpp; | ||
249 | ppc_md.hpte_insert = ps3_hpte_insert; | ||
250 | ppc_md.hpte_remove = ps3_hpte_remove; | ||
251 | ppc_md.hpte_clear_all = ps3_hpte_clear; | ||
252 | |||
253 | ppc64_pft_size = __ilog2(htab_size); | ||
254 | |||
255 | bitmap_size = htab_size / sizeof(hpte_t) / 8; | ||
256 | |||
257 | bolttab = __va(lmb_alloc(bitmap_size, 1)); | ||
258 | inusetab = __va(lmb_alloc(bitmap_size, 1)); | ||
259 | |||
260 | memset(bolttab, 0, bitmap_size); | ||
261 | memset(inusetab, 0, bitmap_size); | ||
262 | |||
263 | DBG(" <- %s:%d\n", __func__, __LINE__); | ||
264 | } | ||
265 | |||
266 | void __init ps3_map_htab(void) | ||
267 | { | ||
268 | long result; | ||
269 | unsigned long htab_size = (1UL << ppc64_pft_size); | ||
270 | |||
271 | result = lv1_map_htab(0, &htab_addr); | ||
272 | |||
273 | htab = (hpte_t *)__ioremap(htab_addr, htab_size, PAGE_READONLY_X); | ||
274 | |||
275 | DBG("%s:%d: lpar %016lxh, virt %016lxh\n", __func__, __LINE__, | ||
276 | htab_addr, (unsigned long)htab); | ||
277 | } | ||