1 /* $Id: p1275.c,v 1.22 2001/10/18 09:40:00 davem Exp $
2 * p1275.c: Sun IEEE 1275 PROM low level interface routines
3 *
4 * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
5 */
6
7 #include <linux/kernel.h>
8 #include <linux/init.h>
9 #include <linux/sched.h>
10 #include <linux/smp.h>
11 #include <linux/string.h>
12 #include <linux/spinlock.h>
13
14 #include <asm/openprom.h>
15 #include <asm/oplib.h>
16 #include <asm/system.h>
17 #include <asm/spitfire.h>
18 #include <asm/pstate.h>
19
20 struct {
21 long prom_callback; /* 0x00 */
22 void (*prom_cif_handler)(long *); /* 0x08 */
23 unsigned long prom_cif_stack; /* 0x10 */
24 unsigned long prom_args [23]; /* 0x18 */
25 char prom_buffer [3000];
26 } p1275buf;
27
28 extern void prom_world(int);
29
prom_cif_interface(void)30 void prom_cif_interface (void)
31 {
32 __asm__ __volatile__ (
33 " mov %0, %%o0\n"
34 " ldx [%%o0 + 0x010], %%o1 ! prom_cif_stack\n"
35 " save %%o1, -0x190, %%sp\n"
36 " ldx [%%i0 + 0x008], %%l2 ! prom_cif_handler\n"
37 " rdpr %%pstate, %%l4\n"
38 " wrpr %%g0, 0x15, %%pstate ! save alternate globals\n"
39 " stx %%g1, [%%sp + 2047 + 0x0b0]\n"
40 " stx %%g2, [%%sp + 2047 + 0x0b8]\n"
41 " stx %%g3, [%%sp + 2047 + 0x0c0]\n"
42 " stx %%g4, [%%sp + 2047 + 0x0c8]\n"
43 " stx %%g5, [%%sp + 2047 + 0x0d0]\n"
44 " stx %%g6, [%%sp + 2047 + 0x0d8]\n"
45 " stx %%g7, [%%sp + 2047 + 0x0e0]\n"
46 " wrpr %%g0, 0x814, %%pstate ! save interrupt globals\n"
47 " stx %%g1, [%%sp + 2047 + 0x0e8]\n"
48 " stx %%g2, [%%sp + 2047 + 0x0f0]\n"
49 " stx %%g3, [%%sp + 2047 + 0x0f8]\n"
50 " stx %%g4, [%%sp + 2047 + 0x100]\n"
51 " stx %%g5, [%%sp + 2047 + 0x108]\n"
52 " stx %%g6, [%%sp + 2047 + 0x110]\n"
53 " stx %%g7, [%%sp + 2047 + 0x118]\n"
54 " wrpr %%g0, 0x14, %%pstate ! save normal globals\n"
55 " stx %%g1, [%%sp + 2047 + 0x120]\n"
56 " stx %%g2, [%%sp + 2047 + 0x128]\n"
57 " stx %%g3, [%%sp + 2047 + 0x130]\n"
58 " stx %%g4, [%%sp + 2047 + 0x138]\n"
59 " stx %%g5, [%%sp + 2047 + 0x140]\n"
60 " stx %%g6, [%%sp + 2047 + 0x148]\n"
61 " stx %%g7, [%%sp + 2047 + 0x150]\n"
62 " wrpr %%g0, 0x414, %%pstate ! save mmu globals\n"
63 " stx %%g1, [%%sp + 2047 + 0x158]\n"
64 " stx %%g2, [%%sp + 2047 + 0x160]\n"
65 " stx %%g3, [%%sp + 2047 + 0x168]\n"
66 " stx %%g4, [%%sp + 2047 + 0x170]\n"
67 " stx %%g5, [%%sp + 2047 + 0x178]\n"
68 " stx %%g6, [%%sp + 2047 + 0x180]\n"
69 " stx %%g7, [%%sp + 2047 + 0x188]\n"
70 " mov %%g1, %%l0 ! also save to locals, so we can handle\n"
71 " mov %%g2, %%l1 ! tlb faults later on, when accessing\n"
72 " mov %%g3, %%l3 ! the stack.\n"
73 " mov %%g7, %%l5\n"
74 " wrpr %%l4, %1, %%pstate ! turn off interrupts\n"
75 " call %%l2\n"
76 " add %%i0, 0x018, %%o0 ! prom_args\n"
77 " wrpr %%g0, 0x414, %%pstate ! restore mmu globals\n"
78 " mov %%l0, %%g1\n"
79 " mov %%l1, %%g2\n"
80 " mov %%l3, %%g3\n"
81 " mov %%l5, %%g7\n"
82 " wrpr %%g0, 0x14, %%pstate ! restore normal globals\n"
83 " ldx [%%sp + 2047 + 0x120], %%g1\n"
84 " ldx [%%sp + 2047 + 0x128], %%g2\n"
85 " ldx [%%sp + 2047 + 0x130], %%g3\n"
86 " ldx [%%sp + 2047 + 0x138], %%g4\n"
87 " ldx [%%sp + 2047 + 0x140], %%g5\n"
88 " ldx [%%sp + 2047 + 0x148], %%g6\n"
89 " ldx [%%sp + 2047 + 0x150], %%g7\n"
90 " wrpr %%g0, 0x814, %%pstate ! restore interrupt globals\n"
91 " ldx [%%sp + 2047 + 0x0e8], %%g1\n"
92 " ldx [%%sp + 2047 + 0x0f0], %%g2\n"
93 " ldx [%%sp + 2047 + 0x0f8], %%g3\n"
94 " ldx [%%sp + 2047 + 0x100], %%g4\n"
95 " ldx [%%sp + 2047 + 0x108], %%g5\n"
96 " ldx [%%sp + 2047 + 0x110], %%g6\n"
97 " ldx [%%sp + 2047 + 0x118], %%g7\n"
98 " wrpr %%g0, 0x15, %%pstate ! restore alternate globals\n"
99 " ldx [%%sp + 2047 + 0x0b0], %%g1\n"
100 " ldx [%%sp + 2047 + 0x0b8], %%g2\n"
101 " ldx [%%sp + 2047 + 0x0c0], %%g3\n"
102 " ldx [%%sp + 2047 + 0x0c8], %%g4\n"
103 " ldx [%%sp + 2047 + 0x0d0], %%g5\n"
104 " ldx [%%sp + 2047 + 0x0d8], %%g6\n"
105 " ldx [%%sp + 2047 + 0x0e0], %%g7\n"
106 " wrpr %%l4, 0, %%pstate ! restore original pstate\n"
107 " ret\n"
108 " restore\n"
109 " " : : "r" (&p1275buf), "i" (PSTATE_IE));
110 }
111
prom_cif_callback(void)112 void prom_cif_callback(void)
113 {
114 __asm__ __volatile__ (
115 " mov %0, %%o1\n"
116 " save %%sp, -0x270, %%sp\n"
117 " rdpr %%pstate, %%l4\n"
118 " wrpr %%g0, 0x15, %%pstate ! save PROM alternate globals\n"
119 " stx %%g1, [%%sp + 2047 + 0x0b0]\n"
120 " stx %%g2, [%%sp + 2047 + 0x0b8]\n"
121 " stx %%g3, [%%sp + 2047 + 0x0c0]\n"
122 " stx %%g4, [%%sp + 2047 + 0x0c8]\n"
123 " stx %%g5, [%%sp + 2047 + 0x0d0]\n"
124 " stx %%g6, [%%sp + 2047 + 0x0d8]\n"
125 " stx %%g7, [%%sp + 2047 + 0x0e0]\n"
126 " ! restore Linux alternate globals\n"
127 " ldx [%%sp + 2047 + 0x190], %%g1\n"
128 " ldx [%%sp + 2047 + 0x198], %%g2\n"
129 " ldx [%%sp + 2047 + 0x1a0], %%g3\n"
130 " ldx [%%sp + 2047 + 0x1a8], %%g4\n"
131 " ldx [%%sp + 2047 + 0x1b0], %%g5\n"
132 " ldx [%%sp + 2047 + 0x1b8], %%g6\n"
133 " ldx [%%sp + 2047 + 0x1c0], %%g7\n"
134 " wrpr %%g0, 0x814, %%pstate ! save PROM interrupt globals\n"
135 " stx %%g1, [%%sp + 2047 + 0x0e8]\n"
136 " stx %%g2, [%%sp + 2047 + 0x0f0]\n"
137 " stx %%g3, [%%sp + 2047 + 0x0f8]\n"
138 " stx %%g4, [%%sp + 2047 + 0x100]\n"
139 " stx %%g5, [%%sp + 2047 + 0x108]\n"
140 " stx %%g6, [%%sp + 2047 + 0x110]\n"
141 " stx %%g7, [%%sp + 2047 + 0x118]\n"
142 " ! restore Linux interrupt globals\n"
143 " ldx [%%sp + 2047 + 0x1c8], %%g1\n"
144 " ldx [%%sp + 2047 + 0x1d0], %%g2\n"
145 " ldx [%%sp + 2047 + 0x1d8], %%g3\n"
146 " ldx [%%sp + 2047 + 0x1e0], %%g4\n"
147 " ldx [%%sp + 2047 + 0x1e8], %%g5\n"
148 " ldx [%%sp + 2047 + 0x1f0], %%g6\n"
149 " ldx [%%sp + 2047 + 0x1f8], %%g7\n"
150 " wrpr %%g0, 0x14, %%pstate ! save PROM normal globals\n"
151 " stx %%g1, [%%sp + 2047 + 0x120]\n"
152 " stx %%g2, [%%sp + 2047 + 0x128]\n"
153 " stx %%g3, [%%sp + 2047 + 0x130]\n"
154 " stx %%g4, [%%sp + 2047 + 0x138]\n"
155 " stx %%g5, [%%sp + 2047 + 0x140]\n"
156 " stx %%g6, [%%sp + 2047 + 0x148]\n"
157 " stx %%g7, [%%sp + 2047 + 0x150]\n"
158 " ! restore Linux normal globals\n"
159 " ldx [%%sp + 2047 + 0x200], %%g1\n"
160 " ldx [%%sp + 2047 + 0x208], %%g2\n"
161 " ldx [%%sp + 2047 + 0x210], %%g3\n"
162 " ldx [%%sp + 2047 + 0x218], %%g4\n"
163 " ldx [%%sp + 2047 + 0x220], %%g5\n"
164 " ldx [%%sp + 2047 + 0x228], %%g6\n"
165 " ldx [%%sp + 2047 + 0x230], %%g7\n"
166 " wrpr %%g0, 0x414, %%pstate ! save PROM mmu globals\n"
167 " stx %%g1, [%%sp + 2047 + 0x158]\n"
168 " stx %%g2, [%%sp + 2047 + 0x160]\n"
169 " stx %%g3, [%%sp + 2047 + 0x168]\n"
170 " stx %%g4, [%%sp + 2047 + 0x170]\n"
171 " stx %%g5, [%%sp + 2047 + 0x178]\n"
172 " stx %%g6, [%%sp + 2047 + 0x180]\n"
173 " stx %%g7, [%%sp + 2047 + 0x188]\n"
174 " ! restore Linux mmu globals\n"
175 " ldx [%%sp + 2047 + 0x238], %%o0\n"
176 " ldx [%%sp + 2047 + 0x240], %%o1\n"
177 " ldx [%%sp + 2047 + 0x248], %%l2\n"
178 " ldx [%%sp + 2047 + 0x250], %%l3\n"
179 " ldx [%%sp + 2047 + 0x258], %%l5\n"
180 " ldx [%%sp + 2047 + 0x260], %%l6\n"
181 " ldx [%%sp + 2047 + 0x268], %%l7\n"
182 " ! switch to Linux tba\n"
183 " sethi %%hi(sparc64_ttable_tl0), %%l1\n"
184 " rdpr %%tba, %%l0 ! save PROM tba\n"
185 " mov %%o0, %%g1\n"
186 " mov %%o1, %%g2\n"
187 " mov %%l2, %%g3\n"
188 " mov %%l3, %%g4\n"
189 " mov %%l5, %%g5\n"
190 " mov %%l6, %%g6\n"
191 " mov %%l7, %%g7\n"
192 " wrpr %%l1, %%tba ! install Linux tba\n"
193 " wrpr %%l4, 0, %%pstate ! restore PSTATE\n"
194 " call prom_world\n"
195 " mov %%g0, %%o0\n"
196 " ldx [%%i1 + 0x000], %%l2\n"
197 " call %%l2\n"
198 " mov %%i0, %%o0\n"
199 " mov %%o0, %%l1\n"
200 " call prom_world\n"
201 " or %%g0, 1, %%o0\n"
202 " wrpr %%g0, 0x14, %%pstate ! interrupts off\n"
203 " ! restore PROM mmu globals\n"
204 " ldx [%%sp + 2047 + 0x158], %%o0\n"
205 " ldx [%%sp + 2047 + 0x160], %%o1\n"
206 " ldx [%%sp + 2047 + 0x168], %%l2\n"
207 " ldx [%%sp + 2047 + 0x170], %%l3\n"
208 " ldx [%%sp + 2047 + 0x178], %%l5\n"
209 " ldx [%%sp + 2047 + 0x180], %%l6\n"
210 " ldx [%%sp + 2047 + 0x188], %%l7\n"
211 " wrpr %%g0, 0x414, %%pstate ! restore PROM mmu globals\n"
212 " mov %%o0, %%g1\n"
213 " mov %%o1, %%g2\n"
214 " mov %%l2, %%g3\n"
215 " mov %%l3, %%g4\n"
216 " mov %%l5, %%g5\n"
217 " mov %%l6, %%g6\n"
218 " mov %%l7, %%g7\n"
219 " wrpr %%l0, %%tba ! restore PROM tba\n"
220 " wrpr %%g0, 0x14, %%pstate ! restore PROM normal globals\n"
221 " ldx [%%sp + 2047 + 0x120], %%g1\n"
222 " ldx [%%sp + 2047 + 0x128], %%g2\n"
223 " ldx [%%sp + 2047 + 0x130], %%g3\n"
224 " ldx [%%sp + 2047 + 0x138], %%g4\n"
225 " ldx [%%sp + 2047 + 0x140], %%g5\n"
226 " ldx [%%sp + 2047 + 0x148], %%g6\n"
227 " ldx [%%sp + 2047 + 0x150], %%g7\n"
228 " wrpr %%g0, 0x814, %%pstate ! restore PROM interrupt globals\n"
229 " ldx [%%sp + 2047 + 0x0e8], %%g1\n"
230 " ldx [%%sp + 2047 + 0x0f0], %%g2\n"
231 " ldx [%%sp + 2047 + 0x0f8], %%g3\n"
232 " ldx [%%sp + 2047 + 0x100], %%g4\n"
233 " ldx [%%sp + 2047 + 0x108], %%g5\n"
234 " ldx [%%sp + 2047 + 0x110], %%g6\n"
235 " ldx [%%sp + 2047 + 0x118], %%g7\n"
236 " wrpr %%g0, 0x15, %%pstate ! restore PROM alternate globals\n"
237 " ldx [%%sp + 2047 + 0x0b0], %%g1\n"
238 " ldx [%%sp + 2047 + 0x0b8], %%g2\n"
239 " ldx [%%sp + 2047 + 0x0c0], %%g3\n"
240 " ldx [%%sp + 2047 + 0x0c8], %%g4\n"
241 " ldx [%%sp + 2047 + 0x0d0], %%g5\n"
242 " ldx [%%sp + 2047 + 0x0d8], %%g6\n"
243 " ldx [%%sp + 2047 + 0x0e0], %%g7\n"
244 " wrpr %%l4, 0, %%pstate\n"
245 " ret\n"
246 " restore %%l1, 0, %%o0\n"
247 " " : : "r" (&p1275buf), "i" (PSTATE_PRIV));
248 }
249
250 /*
251 * This provides SMP safety on the p1275buf. prom_callback() drops this lock
252 * to allow recursuve acquisition.
253 */
254 spinlock_t prom_entry_lock = SPIN_LOCK_UNLOCKED;
255
p1275_cmd(char * service,long fmt,...)256 long p1275_cmd (char *service, long fmt, ...)
257 {
258 char *p, *q;
259 unsigned long flags;
260 int nargs, nrets, i;
261 va_list list;
262 long attrs, x;
263 long ctx = 0;
264
265 p = p1275buf.prom_buffer;
266 ctx = spitfire_get_primary_context ();
267 if (ctx) {
268 flushw_user ();
269 spitfire_set_primary_context (0);
270 }
271
272 spin_lock_irqsave(&prom_entry_lock, flags);
273
274 p1275buf.prom_args[0] = (unsigned long)p; /* service */
275 strcpy (p, service);
276 p = (char *)(((long)(strchr (p, 0) + 8)) & ~7);
277 p1275buf.prom_args[1] = nargs = (fmt & 0x0f); /* nargs */
278 p1275buf.prom_args[2] = nrets = ((fmt & 0xf0) >> 4); /* nrets */
279 attrs = fmt >> 8;
280 va_start(list, fmt);
281 for (i = 0; i < nargs; i++, attrs >>= 3) {
282 switch (attrs & 0x7) {
283 case P1275_ARG_NUMBER:
284 p1275buf.prom_args[i + 3] =
285 (unsigned)va_arg(list, long);
286 break;
287 case P1275_ARG_IN_64B:
288 p1275buf.prom_args[i + 3] =
289 va_arg(list, unsigned long);
290 break;
291 case P1275_ARG_IN_STRING:
292 strcpy (p, va_arg(list, char *));
293 p1275buf.prom_args[i + 3] = (unsigned long)p;
294 p = (char *)(((long)(strchr (p, 0) + 8)) & ~7);
295 break;
296 case P1275_ARG_OUT_BUF:
297 (void) va_arg(list, char *);
298 p1275buf.prom_args[i + 3] = (unsigned long)p;
299 x = va_arg(list, long);
300 i++; attrs >>= 3;
301 p = (char *)(((long)(p + (int)x + 7)) & ~7);
302 p1275buf.prom_args[i + 3] = x;
303 break;
304 case P1275_ARG_IN_BUF:
305 q = va_arg(list, char *);
306 p1275buf.prom_args[i + 3] = (unsigned long)p;
307 x = va_arg(list, long);
308 i++; attrs >>= 3;
309 memcpy (p, q, (int)x);
310 p = (char *)(((long)(p + (int)x + 7)) & ~7);
311 p1275buf.prom_args[i + 3] = x;
312 break;
313 case P1275_ARG_OUT_32B:
314 (void) va_arg(list, char *);
315 p1275buf.prom_args[i + 3] = (unsigned long)p;
316 p += 32;
317 break;
318 case P1275_ARG_IN_FUNCTION:
319 p1275buf.prom_args[i + 3] =
320 (unsigned long)prom_cif_callback;
321 p1275buf.prom_callback = va_arg(list, long);
322 break;
323 }
324 }
325 va_end(list);
326
327 prom_world(1);
328 prom_cif_interface();
329 prom_world(0);
330
331 attrs = fmt >> 8;
332 va_start(list, fmt);
333 for (i = 0; i < nargs; i++, attrs >>= 3) {
334 switch (attrs & 0x7) {
335 case P1275_ARG_NUMBER:
336 (void) va_arg(list, long);
337 break;
338 case P1275_ARG_IN_STRING:
339 (void) va_arg(list, char *);
340 break;
341 case P1275_ARG_IN_FUNCTION:
342 (void) va_arg(list, long);
343 break;
344 case P1275_ARG_IN_BUF:
345 (void) va_arg(list, char *);
346 (void) va_arg(list, long);
347 i++; attrs >>= 3;
348 break;
349 case P1275_ARG_OUT_BUF:
350 p = va_arg(list, char *);
351 x = va_arg(list, long);
352 memcpy (p, (char *)(p1275buf.prom_args[i + 3]), (int)x);
353 i++; attrs >>= 3;
354 break;
355 case P1275_ARG_OUT_32B:
356 p = va_arg(list, char *);
357 memcpy (p, (char *)(p1275buf.prom_args[i + 3]), 32);
358 break;
359 }
360 }
361 va_end(list);
362 x = p1275buf.prom_args [nargs + 3];
363
364 spin_unlock_irqrestore(&prom_entry_lock, flags);
365
366 if (ctx)
367 spitfire_set_primary_context (ctx);
368
369 return x;
370 }
371
prom_cif_init(void * cif_handler,void * cif_stack)372 void prom_cif_init(void *cif_handler, void *cif_stack)
373 {
374 p1275buf.prom_cif_handler = (void (*)(long *))cif_handler;
375 p1275buf.prom_cif_stack = (unsigned long)cif_stack;
376 }
377