Commit | Line | Data |
---|---|---|
b00dc837 | 1 | /* |
1da177e4 LT |
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> | |
4f0234f4 | 19 | #include <asm/ldc.h> |
1da177e4 LT |
20 | |
21 | struct { | |
22 | long prom_callback; /* 0x00 */ | |
23 | void (*prom_cif_handler)(long *); /* 0x08 */ | |
24 | unsigned long prom_cif_stack; /* 0x10 */ | |
25 | unsigned long prom_args [23]; /* 0x18 */ | |
26 | char prom_buffer [3000]; | |
27 | } p1275buf; | |
28 | ||
29 | extern void prom_world(int); | |
30 | ||
31 | extern void prom_cif_interface(void); | |
32 | extern void prom_cif_callback(void); | |
33 | ||
1da177e4 | 34 | /* |
8a4fd1e4 | 35 | * This provides SMP safety on the p1275buf. |
1da177e4 | 36 | */ |
8a4fd1e4 | 37 | DEFINE_RAW_SPINLOCK(prom_entry_lock); |
1da177e4 | 38 | |
bff06d55 | 39 | long p1275_cmd(const char *service, long fmt, ...) |
1da177e4 LT |
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; | |
1da177e4 | 48 | |
8a4fd1e4 DM |
49 | raw_local_save_flags(flags); |
50 | raw_local_irq_restore(PIL_NMI); | |
51 | raw_spin_lock(&prom_entry_lock); | |
1da177e4 LT |
52 | |
53 | p1275buf.prom_args[0] = (unsigned long)p; /* service */ | |
54 | strcpy (p, service); | |
55 | p = (char *)(((long)(strchr (p, 0) + 8)) & ~7); | |
56 | p1275buf.prom_args[1] = nargs = (fmt & 0x0f); /* nargs */ | |
57 | p1275buf.prom_args[2] = nrets = ((fmt & 0xf0) >> 4); /* nrets */ | |
58 | attrs = fmt >> 8; | |
59 | va_start(list, fmt); | |
60 | for (i = 0; i < nargs; i++, attrs >>= 3) { | |
61 | switch (attrs & 0x7) { | |
62 | case P1275_ARG_NUMBER: | |
63 | p1275buf.prom_args[i + 3] = | |
64 | (unsigned)va_arg(list, long); | |
65 | break; | |
66 | case P1275_ARG_IN_64B: | |
67 | p1275buf.prom_args[i + 3] = | |
68 | va_arg(list, unsigned long); | |
69 | break; | |
70 | case P1275_ARG_IN_STRING: | |
71 | strcpy (p, va_arg(list, char *)); | |
72 | p1275buf.prom_args[i + 3] = (unsigned long)p; | |
73 | p = (char *)(((long)(strchr (p, 0) + 8)) & ~7); | |
74 | break; | |
75 | case P1275_ARG_OUT_BUF: | |
76 | (void) va_arg(list, char *); | |
77 | p1275buf.prom_args[i + 3] = (unsigned long)p; | |
78 | x = va_arg(list, long); | |
79 | i++; attrs >>= 3; | |
80 | p = (char *)(((long)(p + (int)x + 7)) & ~7); | |
81 | p1275buf.prom_args[i + 3] = x; | |
82 | break; | |
83 | case P1275_ARG_IN_BUF: | |
84 | q = va_arg(list, char *); | |
85 | p1275buf.prom_args[i + 3] = (unsigned long)p; | |
86 | x = va_arg(list, long); | |
87 | i++; attrs >>= 3; | |
88 | memcpy (p, q, (int)x); | |
89 | p = (char *)(((long)(p + (int)x + 7)) & ~7); | |
90 | p1275buf.prom_args[i + 3] = x; | |
91 | break; | |
92 | case P1275_ARG_OUT_32B: | |
93 | (void) va_arg(list, char *); | |
94 | p1275buf.prom_args[i + 3] = (unsigned long)p; | |
95 | p += 32; | |
96 | break; | |
97 | case P1275_ARG_IN_FUNCTION: | |
98 | p1275buf.prom_args[i + 3] = | |
99 | (unsigned long)prom_cif_callback; | |
100 | p1275buf.prom_callback = va_arg(list, long); | |
101 | break; | |
102 | } | |
103 | } | |
104 | va_end(list); | |
105 | ||
106 | prom_world(1); | |
107 | prom_cif_interface(); | |
108 | prom_world(0); | |
109 | ||
110 | attrs = fmt >> 8; | |
111 | va_start(list, fmt); | |
112 | for (i = 0; i < nargs; i++, attrs >>= 3) { | |
113 | switch (attrs & 0x7) { | |
114 | case P1275_ARG_NUMBER: | |
115 | (void) va_arg(list, long); | |
116 | break; | |
117 | case P1275_ARG_IN_STRING: | |
118 | (void) va_arg(list, char *); | |
119 | break; | |
120 | case P1275_ARG_IN_FUNCTION: | |
121 | (void) va_arg(list, long); | |
122 | break; | |
123 | case P1275_ARG_IN_BUF: | |
124 | (void) va_arg(list, char *); | |
125 | (void) va_arg(list, long); | |
126 | i++; attrs >>= 3; | |
127 | break; | |
128 | case P1275_ARG_OUT_BUF: | |
129 | p = va_arg(list, char *); | |
130 | x = va_arg(list, long); | |
131 | memcpy (p, (char *)(p1275buf.prom_args[i + 3]), (int)x); | |
132 | i++; attrs >>= 3; | |
133 | break; | |
134 | case P1275_ARG_OUT_32B: | |
135 | p = va_arg(list, char *); | |
136 | memcpy (p, (char *)(p1275buf.prom_args[i + 3]), 32); | |
137 | break; | |
138 | } | |
139 | } | |
140 | va_end(list); | |
141 | x = p1275buf.prom_args [nargs + 3]; | |
142 | ||
8a4fd1e4 DM |
143 | raw_spin_unlock(&prom_entry_lock); |
144 | raw_local_irq_restore(flags); | |
1da177e4 LT |
145 | |
146 | return x; | |
147 | } | |
148 | ||
149 | void prom_cif_init(void *cif_handler, void *cif_stack) | |
150 | { | |
151 | p1275buf.prom_cif_handler = (void (*)(long *))cif_handler; | |
152 | p1275buf.prom_cif_stack = (unsigned long)cif_stack; | |
153 | } |