ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / arch / sparc64 / prom / p1275.c
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
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
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
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
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 }