Commit | Line | Data |
---|---|---|
b00dc837 | 1 | /* |
1da177e4 LT |
2 | * misc.c: Miscellaneous prom functions that don't belong |
3 | * anywhere else. | |
4 | * | |
5 | * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) | |
6 | * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) | |
7 | */ | |
8 | ||
1da177e4 LT |
9 | #include <linux/types.h> |
10 | #include <linux/kernel.h> | |
11 | #include <linux/sched.h> | |
12 | #include <linux/interrupt.h> | |
13 | #include <linux/delay.h> | |
917c3660 SR |
14 | #include <linux/module.h> |
15 | ||
1da177e4 LT |
16 | #include <asm/openprom.h> |
17 | #include <asm/oplib.h> | |
18 | #include <asm/system.h> | |
b3e13fbe | 19 | #include <asm/ldc.h> |
1da177e4 | 20 | |
22d6a1cb DM |
21 | int prom_service_exists(const char *service_name) |
22 | { | |
23 | int err = p1275_cmd("test", P1275_ARG(0, P1275_ARG_IN_STRING) | | |
24 | P1275_INOUT(1, 1), service_name); | |
25 | ||
26 | if (err) | |
27 | return 0; | |
28 | return 1; | |
29 | } | |
30 | ||
31 | void prom_sun4v_guest_soft_state(void) | |
32 | { | |
33 | const char *svc = "SUNW,soft-state-supported"; | |
34 | ||
35 | if (!prom_service_exists(svc)) | |
36 | return; | |
37 | p1275_cmd(svc, P1275_INOUT(0, 0)); | |
38 | } | |
39 | ||
1da177e4 | 40 | /* Reset and reboot the machine with the command 'bcommand'. */ |
bff06d55 | 41 | void prom_reboot(const char *bcommand) |
1da177e4 | 42 | { |
b3e13fbe DM |
43 | #ifdef CONFIG_SUN_LDOMS |
44 | if (ldom_domaining_enabled) | |
45 | ldom_reboot(bcommand); | |
46 | #endif | |
1da177e4 LT |
47 | p1275_cmd("boot", P1275_ARG(0, P1275_ARG_IN_STRING) | |
48 | P1275_INOUT(1, 0), bcommand); | |
49 | } | |
50 | ||
51 | /* Forth evaluate the expression contained in 'fstring'. */ | |
bff06d55 | 52 | void prom_feval(const char *fstring) |
1da177e4 LT |
53 | { |
54 | if (!fstring || fstring[0] == 0) | |
55 | return; | |
56 | p1275_cmd("interpret", P1275_ARG(0, P1275_ARG_IN_STRING) | | |
57 | P1275_INOUT(1, 1), fstring); | |
58 | } | |
917c3660 | 59 | EXPORT_SYMBOL(prom_feval); |
1da177e4 | 60 | |
1da177e4 LT |
61 | #ifdef CONFIG_SMP |
62 | extern void smp_capture(void); | |
63 | extern void smp_release(void); | |
64 | #endif | |
65 | ||
66 | /* Drop into the prom, with the chance to continue with the 'go' | |
67 | * prom command. | |
68 | */ | |
69 | void prom_cmdline(void) | |
70 | { | |
71 | unsigned long flags; | |
72 | ||
73 | local_irq_save(flags); | |
74 | ||
1da177e4 LT |
75 | #ifdef CONFIG_SMP |
76 | smp_capture(); | |
77 | #endif | |
78 | ||
79 | p1275_cmd("enter", P1275_INOUT(0, 0)); | |
80 | ||
81 | #ifdef CONFIG_SMP | |
82 | smp_release(); | |
83 | #endif | |
84 | ||
1da177e4 LT |
85 | local_irq_restore(flags); |
86 | } | |
87 | ||
1da177e4 LT |
88 | /* Drop into the prom, but completely terminate the program. |
89 | * No chance of continuing. | |
90 | */ | |
bd4352ca | 91 | void notrace prom_halt(void) |
1da177e4 | 92 | { |
4f0234f4 DM |
93 | #ifdef CONFIG_SUN_LDOMS |
94 | if (ldom_domaining_enabled) | |
95 | ldom_power_off(); | |
96 | #endif | |
1da177e4 LT |
97 | again: |
98 | p1275_cmd("exit", P1275_INOUT(0, 0)); | |
99 | goto again; /* PROM is out to get me -DaveM */ | |
100 | } | |
101 | ||
102 | void prom_halt_power_off(void) | |
103 | { | |
4f0234f4 DM |
104 | #ifdef CONFIG_SUN_LDOMS |
105 | if (ldom_domaining_enabled) | |
106 | ldom_power_off(); | |
107 | #endif | |
1da177e4 LT |
108 | p1275_cmd("SUNW,power-off", P1275_INOUT(0, 0)); |
109 | ||
110 | /* if nothing else helps, we just halt */ | |
111 | prom_halt(); | |
112 | } | |
113 | ||
114 | /* Set prom sync handler to call function 'funcp'. */ | |
115 | void prom_setcallback(callback_func_t funcp) | |
116 | { | |
117 | if (!funcp) | |
118 | return; | |
119 | p1275_cmd("set-callback", P1275_ARG(0, P1275_ARG_IN_FUNCTION) | | |
120 | P1275_INOUT(1, 1), funcp); | |
121 | } | |
122 | ||
123 | /* Get the idprom and stuff it into buffer 'idbuf'. Returns the | |
124 | * format type. 'num_bytes' is the number of bytes that your idbuf | |
125 | * has space for. Returns 0xff on error. | |
126 | */ | |
127 | unsigned char prom_get_idprom(char *idbuf, int num_bytes) | |
128 | { | |
129 | int len; | |
130 | ||
131 | len = prom_getproplen(prom_root_node, "idprom"); | |
132 | if ((len >num_bytes) || (len == -1)) | |
133 | return 0xff; | |
134 | if (!prom_getproperty(prom_root_node, "idprom", idbuf, num_bytes)) | |
135 | return idbuf[0]; | |
136 | ||
137 | return 0xff; | |
138 | } | |
139 | ||
1da177e4 LT |
140 | int prom_get_mmu_ihandle(void) |
141 | { | |
142 | int node, ret; | |
143 | ||
bff06d55 DM |
144 | if (prom_mmu_ihandle_cache != 0) |
145 | return prom_mmu_ihandle_cache; | |
1da177e4 | 146 | |
bff06d55 DM |
147 | node = prom_finddevice(prom_chosen_path); |
148 | ret = prom_getint(node, prom_mmu_name); | |
1da177e4 | 149 | if (ret == -1 || ret == 0) |
bff06d55 | 150 | prom_mmu_ihandle_cache = -1; |
1da177e4 | 151 | else |
bff06d55 | 152 | prom_mmu_ihandle_cache = ret; |
1da177e4 LT |
153 | |
154 | return ret; | |
155 | } | |
156 | ||
157 | static int prom_get_memory_ihandle(void) | |
158 | { | |
159 | static int memory_ihandle_cache; | |
160 | int node, ret; | |
161 | ||
162 | if (memory_ihandle_cache != 0) | |
163 | return memory_ihandle_cache; | |
164 | ||
165 | node = prom_finddevice("/chosen"); | |
166 | ret = prom_getint(node, "memory"); | |
167 | if (ret == -1 || ret == 0) | |
168 | memory_ihandle_cache = -1; | |
169 | else | |
170 | memory_ihandle_cache = ret; | |
171 | ||
172 | return ret; | |
173 | } | |
174 | ||
175 | /* Load explicit I/D TLB entries. */ | |
176 | long prom_itlb_load(unsigned long index, | |
177 | unsigned long tte_data, | |
178 | unsigned long vaddr) | |
179 | { | |
bff06d55 | 180 | return p1275_cmd(prom_callmethod_name, |
1da177e4 LT |
181 | (P1275_ARG(0, P1275_ARG_IN_STRING) | |
182 | P1275_ARG(2, P1275_ARG_IN_64B) | | |
183 | P1275_ARG(3, P1275_ARG_IN_64B) | | |
184 | P1275_INOUT(5, 1)), | |
185 | "SUNW,itlb-load", | |
186 | prom_get_mmu_ihandle(), | |
187 | /* And then our actual args are pushed backwards. */ | |
188 | vaddr, | |
189 | tte_data, | |
190 | index); | |
191 | } | |
192 | ||
193 | long prom_dtlb_load(unsigned long index, | |
194 | unsigned long tte_data, | |
195 | unsigned long vaddr) | |
196 | { | |
bff06d55 | 197 | return p1275_cmd(prom_callmethod_name, |
1da177e4 LT |
198 | (P1275_ARG(0, P1275_ARG_IN_STRING) | |
199 | P1275_ARG(2, P1275_ARG_IN_64B) | | |
200 | P1275_ARG(3, P1275_ARG_IN_64B) | | |
201 | P1275_INOUT(5, 1)), | |
202 | "SUNW,dtlb-load", | |
203 | prom_get_mmu_ihandle(), | |
204 | /* And then our actual args are pushed backwards. */ | |
205 | vaddr, | |
206 | tte_data, | |
207 | index); | |
208 | } | |
209 | ||
210 | int prom_map(int mode, unsigned long size, | |
211 | unsigned long vaddr, unsigned long paddr) | |
212 | { | |
bff06d55 | 213 | int ret = p1275_cmd(prom_callmethod_name, |
1da177e4 LT |
214 | (P1275_ARG(0, P1275_ARG_IN_STRING) | |
215 | P1275_ARG(3, P1275_ARG_IN_64B) | | |
216 | P1275_ARG(4, P1275_ARG_IN_64B) | | |
217 | P1275_ARG(6, P1275_ARG_IN_64B) | | |
218 | P1275_INOUT(7, 1)), | |
bff06d55 | 219 | prom_map_name, |
1da177e4 LT |
220 | prom_get_mmu_ihandle(), |
221 | mode, | |
222 | size, | |
223 | vaddr, | |
224 | 0, | |
225 | paddr); | |
226 | ||
227 | if (ret == 0) | |
228 | ret = -1; | |
229 | return ret; | |
230 | } | |
231 | ||
232 | void prom_unmap(unsigned long size, unsigned long vaddr) | |
233 | { | |
bff06d55 | 234 | p1275_cmd(prom_callmethod_name, |
1da177e4 LT |
235 | (P1275_ARG(0, P1275_ARG_IN_STRING) | |
236 | P1275_ARG(2, P1275_ARG_IN_64B) | | |
237 | P1275_ARG(3, P1275_ARG_IN_64B) | | |
238 | P1275_INOUT(4, 0)), | |
bff06d55 | 239 | prom_unmap_name, |
1da177e4 LT |
240 | prom_get_mmu_ihandle(), |
241 | size, | |
242 | vaddr); | |
243 | } | |
244 | ||
245 | /* Set aside physical memory which is not touched or modified | |
246 | * across soft resets. | |
247 | */ | |
bff06d55 | 248 | unsigned long prom_retain(const char *name, |
1da177e4 LT |
249 | unsigned long pa_low, unsigned long pa_high, |
250 | long size, long align) | |
251 | { | |
252 | /* XXX I don't think we return multiple values correctly. | |
253 | * XXX OBP supposedly returns pa_low/pa_high here, how does | |
254 | * XXX it work? | |
255 | */ | |
256 | ||
257 | /* If align is zero, the pa_low/pa_high args are passed, | |
258 | * else they are not. | |
259 | */ | |
260 | if (align == 0) | |
261 | return p1275_cmd("SUNW,retain", | |
262 | (P1275_ARG(0, P1275_ARG_IN_BUF) | P1275_INOUT(5, 2)), | |
263 | name, pa_low, pa_high, size, align); | |
264 | else | |
265 | return p1275_cmd("SUNW,retain", | |
266 | (P1275_ARG(0, P1275_ARG_IN_BUF) | P1275_INOUT(3, 2)), | |
267 | name, size, align); | |
268 | } | |
269 | ||
270 | /* Get "Unumber" string for the SIMM at the given | |
271 | * memory address. Usually this will be of the form | |
272 | * "Uxxxx" where xxxx is a decimal number which is | |
273 | * etched into the motherboard next to the SIMM slot | |
274 | * in question. | |
275 | */ | |
276 | int prom_getunumber(int syndrome_code, | |
277 | unsigned long phys_addr, | |
278 | char *buf, int buflen) | |
279 | { | |
bff06d55 | 280 | return p1275_cmd(prom_callmethod_name, |
1da177e4 LT |
281 | (P1275_ARG(0, P1275_ARG_IN_STRING) | |
282 | P1275_ARG(3, P1275_ARG_OUT_BUF) | | |
283 | P1275_ARG(6, P1275_ARG_IN_64B) | | |
284 | P1275_INOUT(8, 2)), | |
285 | "SUNW,get-unumber", prom_get_memory_ihandle(), | |
286 | buflen, buf, P1275_SIZE(buflen), | |
287 | 0, phys_addr, syndrome_code); | |
288 | } | |
289 | ||
290 | /* Power management extensions. */ | |
291 | void prom_sleepself(void) | |
292 | { | |
293 | p1275_cmd("SUNW,sleep-self", P1275_INOUT(0, 0)); | |
294 | } | |
295 | ||
296 | int prom_sleepsystem(void) | |
297 | { | |
298 | return p1275_cmd("SUNW,sleep-system", P1275_INOUT(0, 1)); | |
299 | } | |
300 | ||
301 | int prom_wakeupsystem(void) | |
302 | { | |
303 | return p1275_cmd("SUNW,wakeup-system", P1275_INOUT(0, 1)); | |
304 | } | |
305 | ||
306 | #ifdef CONFIG_SMP | |
7890f794 | 307 | void prom_startcpu(int cpunode, unsigned long pc, unsigned long arg) |
1da177e4 | 308 | { |
7890f794 DM |
309 | p1275_cmd("SUNW,start-cpu", P1275_INOUT(3, 0), cpunode, pc, arg); |
310 | } | |
311 | ||
312 | void prom_startcpu_cpuid(int cpuid, unsigned long pc, unsigned long arg) | |
313 | { | |
314 | p1275_cmd("SUNW,start-cpu-by-cpuid", P1275_INOUT(3, 0), | |
315 | cpuid, pc, arg); | |
316 | } | |
317 | ||
318 | void prom_stopcpu_cpuid(int cpuid) | |
319 | { | |
320 | p1275_cmd("SUNW,stop-cpu-by-cpuid", P1275_INOUT(1, 0), | |
321 | cpuid); | |
1da177e4 LT |
322 | } |
323 | ||
324 | void prom_stopself(void) | |
325 | { | |
326 | p1275_cmd("SUNW,stop-self", P1275_INOUT(0, 0)); | |
327 | } | |
328 | ||
329 | void prom_idleself(void) | |
330 | { | |
331 | p1275_cmd("SUNW,idle-self", P1275_INOUT(0, 0)); | |
332 | } | |
333 | ||
334 | void prom_resumecpu(int cpunode) | |
335 | { | |
336 | p1275_cmd("SUNW,resume-cpu", P1275_INOUT(1, 0), cpunode); | |
337 | } | |
338 | #endif |