Merge master.kernel.org:/home/rmk/linux-2.6-arm
[linux-2.6] / 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 extern void prom_cif_interface(void);
31 extern void prom_cif_callback(void);
32
33 /*
34  * This provides SMP safety on the p1275buf. prom_callback() drops this lock
35  * to allow recursuve acquisition.
36  */
37 DEFINE_SPINLOCK(prom_entry_lock);
38
39 long p1275_cmd(const char *service, long fmt, ...)
40 {
41         char *p, *q;
42         unsigned long flags;
43         int nargs, nrets, i;
44         va_list list;
45         long attrs, x;
46         
47         p = p1275buf.prom_buffer;
48
49         spin_lock_irqsave(&prom_entry_lock, flags);
50
51         p1275buf.prom_args[0] = (unsigned long)p;               /* service */
52         strcpy (p, service);
53         p = (char *)(((long)(strchr (p, 0) + 8)) & ~7);
54         p1275buf.prom_args[1] = nargs = (fmt & 0x0f);           /* nargs */
55         p1275buf.prom_args[2] = nrets = ((fmt & 0xf0) >> 4);    /* nrets */
56         attrs = fmt >> 8;
57         va_start(list, fmt);
58         for (i = 0; i < nargs; i++, attrs >>= 3) {
59                 switch (attrs & 0x7) {
60                 case P1275_ARG_NUMBER:
61                         p1275buf.prom_args[i + 3] =
62                                                 (unsigned)va_arg(list, long);
63                         break;
64                 case P1275_ARG_IN_64B:
65                         p1275buf.prom_args[i + 3] =
66                                 va_arg(list, unsigned long);
67                         break;
68                 case P1275_ARG_IN_STRING:
69                         strcpy (p, va_arg(list, char *));
70                         p1275buf.prom_args[i + 3] = (unsigned long)p;
71                         p = (char *)(((long)(strchr (p, 0) + 8)) & ~7);
72                         break;
73                 case P1275_ARG_OUT_BUF:
74                         (void) va_arg(list, char *);
75                         p1275buf.prom_args[i + 3] = (unsigned long)p;
76                         x = va_arg(list, long);
77                         i++; attrs >>= 3;
78                         p = (char *)(((long)(p + (int)x + 7)) & ~7);
79                         p1275buf.prom_args[i + 3] = x;
80                         break;
81                 case P1275_ARG_IN_BUF:
82                         q = va_arg(list, char *);
83                         p1275buf.prom_args[i + 3] = (unsigned long)p;
84                         x = va_arg(list, long);
85                         i++; attrs >>= 3;
86                         memcpy (p, q, (int)x);
87                         p = (char *)(((long)(p + (int)x + 7)) & ~7);
88                         p1275buf.prom_args[i + 3] = x;
89                         break;
90                 case P1275_ARG_OUT_32B:
91                         (void) va_arg(list, char *);
92                         p1275buf.prom_args[i + 3] = (unsigned long)p;
93                         p += 32;
94                         break;
95                 case P1275_ARG_IN_FUNCTION:
96                         p1275buf.prom_args[i + 3] =
97                                         (unsigned long)prom_cif_callback;
98                         p1275buf.prom_callback = va_arg(list, long);
99                         break;
100                 }
101         }
102         va_end(list);
103
104         prom_world(1);
105         prom_cif_interface();
106         prom_world(0);
107
108         attrs = fmt >> 8;
109         va_start(list, fmt);
110         for (i = 0; i < nargs; i++, attrs >>= 3) {
111                 switch (attrs & 0x7) {
112                 case P1275_ARG_NUMBER:
113                         (void) va_arg(list, long);
114                         break;
115                 case P1275_ARG_IN_STRING:
116                         (void) va_arg(list, char *);
117                         break;
118                 case P1275_ARG_IN_FUNCTION:
119                         (void) va_arg(list, long);
120                         break;
121                 case P1275_ARG_IN_BUF:
122                         (void) va_arg(list, char *);
123                         (void) va_arg(list, long);
124                         i++; attrs >>= 3;
125                         break;
126                 case P1275_ARG_OUT_BUF:
127                         p = va_arg(list, char *);
128                         x = va_arg(list, long);
129                         memcpy (p, (char *)(p1275buf.prom_args[i + 3]), (int)x);
130                         i++; attrs >>= 3;
131                         break;
132                 case P1275_ARG_OUT_32B:
133                         p = va_arg(list, char *);
134                         memcpy (p, (char *)(p1275buf.prom_args[i + 3]), 32);
135                         break;
136                 }
137         }
138         va_end(list);
139         x = p1275buf.prom_args [nargs + 3];
140
141         spin_unlock_irqrestore(&prom_entry_lock, flags);
142
143         return x;
144 }
145
146 void prom_cif_init(void *cif_handler, void *cif_stack)
147 {
148         p1275buf.prom_cif_handler = (void (*)(long *))cif_handler;
149         p1275buf.prom_cif_stack = (unsigned long)cif_stack;
150 }