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> | |
b3e13fbe | 18 | #include <asm/ldc.h> |
1da177e4 | 19 | |
f7b5f55a | 20 | static int prom_service_exists(const char *service_name) |
22d6a1cb | 21 | { |
25edd694 | 22 | unsigned long args[5]; |
22d6a1cb | 23 | |
25edd694 DM |
24 | args[0] = (unsigned long) "test"; |
25 | args[1] = 1; | |
26 | args[2] = 1; | |
27 | args[3] = (unsigned long) service_name; | |
28 | args[4] = (unsigned long) -1; | |
29 | ||
30 | p1275_cmd_direct(args); | |
31 | ||
32 | if (args[4]) | |
22d6a1cb DM |
33 | return 0; |
34 | return 1; | |
35 | } | |
36 | ||
37 | void prom_sun4v_guest_soft_state(void) | |
38 | { | |
39 | const char *svc = "SUNW,soft-state-supported"; | |
25edd694 | 40 | unsigned long args[3]; |
22d6a1cb DM |
41 | |
42 | if (!prom_service_exists(svc)) | |
43 | return; | |
25edd694 DM |
44 | args[0] = (unsigned long) svc; |
45 | args[1] = 0; | |
46 | args[2] = 0; | |
47 | p1275_cmd_direct(args); | |
22d6a1cb DM |
48 | } |
49 | ||
1da177e4 | 50 | /* Reset and reboot the machine with the command 'bcommand'. */ |
bff06d55 | 51 | void prom_reboot(const char *bcommand) |
1da177e4 | 52 | { |
25edd694 DM |
53 | unsigned long args[4]; |
54 | ||
b3e13fbe DM |
55 | #ifdef CONFIG_SUN_LDOMS |
56 | if (ldom_domaining_enabled) | |
57 | ldom_reboot(bcommand); | |
58 | #endif | |
25edd694 DM |
59 | args[0] = (unsigned long) "boot"; |
60 | args[1] = 1; | |
61 | args[2] = 0; | |
62 | args[3] = (unsigned long) bcommand; | |
63 | ||
64 | p1275_cmd_direct(args); | |
1da177e4 LT |
65 | } |
66 | ||
67 | /* Forth evaluate the expression contained in 'fstring'. */ | |
bff06d55 | 68 | void prom_feval(const char *fstring) |
1da177e4 | 69 | { |
25edd694 DM |
70 | unsigned long args[5]; |
71 | ||
1da177e4 LT |
72 | if (!fstring || fstring[0] == 0) |
73 | return; | |
25edd694 DM |
74 | args[0] = (unsigned long) "interpret"; |
75 | args[1] = 1; | |
76 | args[2] = 1; | |
77 | args[3] = (unsigned long) fstring; | |
78 | args[4] = (unsigned long) -1; | |
79 | ||
80 | p1275_cmd_direct(args); | |
1da177e4 | 81 | } |
917c3660 | 82 | EXPORT_SYMBOL(prom_feval); |
1da177e4 | 83 | |
1da177e4 LT |
84 | /* Drop into the prom, with the chance to continue with the 'go' |
85 | * prom command. | |
86 | */ | |
87 | void prom_cmdline(void) | |
88 | { | |
25edd694 | 89 | unsigned long args[3]; |
1da177e4 LT |
90 | unsigned long flags; |
91 | ||
92 | local_irq_save(flags); | |
93 | ||
1da177e4 LT |
94 | #ifdef CONFIG_SMP |
95 | smp_capture(); | |
96 | #endif | |
97 | ||
25edd694 DM |
98 | args[0] = (unsigned long) "enter"; |
99 | args[1] = 0; | |
100 | args[2] = 0; | |
101 | ||
102 | p1275_cmd_direct(args); | |
1da177e4 LT |
103 | |
104 | #ifdef CONFIG_SMP | |
105 | smp_release(); | |
106 | #endif | |
107 | ||
1da177e4 LT |
108 | local_irq_restore(flags); |
109 | } | |
110 | ||
1da177e4 LT |
111 | /* Drop into the prom, but completely terminate the program. |
112 | * No chance of continuing. | |
113 | */ | |
bd4352ca | 114 | void notrace prom_halt(void) |
1da177e4 | 115 | { |
25edd694 DM |
116 | unsigned long args[3]; |
117 | ||
4f0234f4 DM |
118 | #ifdef CONFIG_SUN_LDOMS |
119 | if (ldom_domaining_enabled) | |
120 | ldom_power_off(); | |
121 | #endif | |
1da177e4 | 122 | again: |
25edd694 DM |
123 | args[0] = (unsigned long) "exit"; |
124 | args[1] = 0; | |
125 | args[2] = 0; | |
126 | p1275_cmd_direct(args); | |
1da177e4 LT |
127 | goto again; /* PROM is out to get me -DaveM */ |
128 | } | |
129 | ||
130 | void prom_halt_power_off(void) | |
131 | { | |
25edd694 DM |
132 | unsigned long args[3]; |
133 | ||
4f0234f4 DM |
134 | #ifdef CONFIG_SUN_LDOMS |
135 | if (ldom_domaining_enabled) | |
136 | ldom_power_off(); | |
137 | #endif | |
25edd694 DM |
138 | args[0] = (unsigned long) "SUNW,power-off"; |
139 | args[1] = 0; | |
140 | args[2] = 0; | |
141 | p1275_cmd_direct(args); | |
1da177e4 LT |
142 | |
143 | /* if nothing else helps, we just halt */ | |
144 | prom_halt(); | |
145 | } | |
146 | ||
1da177e4 LT |
147 | /* Get the idprom and stuff it into buffer 'idbuf'. Returns the |
148 | * format type. 'num_bytes' is the number of bytes that your idbuf | |
149 | * has space for. Returns 0xff on error. | |
150 | */ | |
151 | unsigned char prom_get_idprom(char *idbuf, int num_bytes) | |
152 | { | |
153 | int len; | |
154 | ||
155 | len = prom_getproplen(prom_root_node, "idprom"); | |
156 | if ((len >num_bytes) || (len == -1)) | |
157 | return 0xff; | |
158 | if (!prom_getproperty(prom_root_node, "idprom", idbuf, num_bytes)) | |
159 | return idbuf[0]; | |
160 | ||
161 | return 0xff; | |
162 | } | |
163 | ||
1da177e4 LT |
164 | int prom_get_mmu_ihandle(void) |
165 | { | |
8d125562 AS |
166 | phandle node; |
167 | int ret; | |
1da177e4 | 168 | |
bff06d55 DM |
169 | if (prom_mmu_ihandle_cache != 0) |
170 | return prom_mmu_ihandle_cache; | |
1da177e4 | 171 | |
bff06d55 DM |
172 | node = prom_finddevice(prom_chosen_path); |
173 | ret = prom_getint(node, prom_mmu_name); | |
1da177e4 | 174 | if (ret == -1 || ret == 0) |
bff06d55 | 175 | prom_mmu_ihandle_cache = -1; |
1da177e4 | 176 | else |
bff06d55 | 177 | prom_mmu_ihandle_cache = ret; |
1da177e4 LT |
178 | |
179 | return ret; | |
180 | } | |
181 | ||
182 | static int prom_get_memory_ihandle(void) | |
183 | { | |
184 | static int memory_ihandle_cache; | |
8d125562 AS |
185 | phandle node; |
186 | int ret; | |
1da177e4 LT |
187 | |
188 | if (memory_ihandle_cache != 0) | |
189 | return memory_ihandle_cache; | |
190 | ||
191 | node = prom_finddevice("/chosen"); | |
192 | ret = prom_getint(node, "memory"); | |
193 | if (ret == -1 || ret == 0) | |
194 | memory_ihandle_cache = -1; | |
195 | else | |
196 | memory_ihandle_cache = ret; | |
197 | ||
198 | return ret; | |
199 | } | |
200 | ||
201 | /* Load explicit I/D TLB entries. */ | |
25edd694 DM |
202 | static long tlb_load(const char *type, unsigned long index, |
203 | unsigned long tte_data, unsigned long vaddr) | |
204 | { | |
205 | unsigned long args[9]; | |
206 | ||
207 | args[0] = (unsigned long) prom_callmethod_name; | |
208 | args[1] = 5; | |
209 | args[2] = 1; | |
210 | args[3] = (unsigned long) type; | |
211 | args[4] = (unsigned int) prom_get_mmu_ihandle(); | |
212 | args[5] = vaddr; | |
213 | args[6] = tte_data; | |
214 | args[7] = index; | |
215 | args[8] = (unsigned long) -1; | |
216 | ||
217 | p1275_cmd_direct(args); | |
218 | ||
219 | return (long) args[8]; | |
220 | } | |
221 | ||
1da177e4 LT |
222 | long prom_itlb_load(unsigned long index, |
223 | unsigned long tte_data, | |
224 | unsigned long vaddr) | |
225 | { | |
25edd694 | 226 | return tlb_load("SUNW,itlb-load", index, tte_data, vaddr); |
1da177e4 LT |
227 | } |
228 | ||
229 | long prom_dtlb_load(unsigned long index, | |
230 | unsigned long tte_data, | |
231 | unsigned long vaddr) | |
232 | { | |
25edd694 | 233 | return tlb_load("SUNW,dtlb-load", index, tte_data, vaddr); |
1da177e4 LT |
234 | } |
235 | ||
236 | int prom_map(int mode, unsigned long size, | |
237 | unsigned long vaddr, unsigned long paddr) | |
238 | { | |
25edd694 DM |
239 | unsigned long args[11]; |
240 | int ret; | |
241 | ||
242 | args[0] = (unsigned long) prom_callmethod_name; | |
243 | args[1] = 7; | |
244 | args[2] = 1; | |
245 | args[3] = (unsigned long) prom_map_name; | |
246 | args[4] = (unsigned int) prom_get_mmu_ihandle(); | |
247 | args[5] = (unsigned int) mode; | |
248 | args[6] = size; | |
249 | args[7] = vaddr; | |
250 | args[8] = 0; | |
251 | args[9] = paddr; | |
252 | args[10] = (unsigned long) -1; | |
253 | ||
254 | p1275_cmd_direct(args); | |
255 | ||
256 | ret = (int) args[10]; | |
1da177e4 LT |
257 | if (ret == 0) |
258 | ret = -1; | |
259 | return ret; | |
260 | } | |
261 | ||
262 | void prom_unmap(unsigned long size, unsigned long vaddr) | |
263 | { | |
25edd694 DM |
264 | unsigned long args[7]; |
265 | ||
266 | args[0] = (unsigned long) prom_callmethod_name; | |
267 | args[1] = 4; | |
268 | args[2] = 0; | |
269 | args[3] = (unsigned long) prom_unmap_name; | |
270 | args[4] = (unsigned int) prom_get_mmu_ihandle(); | |
271 | args[5] = size; | |
272 | args[6] = vaddr; | |
273 | ||
274 | p1275_cmd_direct(args); | |
1da177e4 LT |
275 | } |
276 | ||
277 | /* Set aside physical memory which is not touched or modified | |
278 | * across soft resets. | |
279 | */ | |
25edd694 DM |
280 | int prom_retain(const char *name, unsigned long size, |
281 | unsigned long align, unsigned long *paddr) | |
1da177e4 | 282 | { |
25edd694 DM |
283 | unsigned long args[11]; |
284 | ||
285 | args[0] = (unsigned long) prom_callmethod_name; | |
286 | args[1] = 5; | |
287 | args[2] = 3; | |
288 | args[3] = (unsigned long) "SUNW,retain"; | |
289 | args[4] = (unsigned int) prom_get_memory_ihandle(); | |
290 | args[5] = align; | |
291 | args[6] = size; | |
292 | args[7] = (unsigned long) name; | |
293 | args[8] = (unsigned long) -1; | |
294 | args[9] = (unsigned long) -1; | |
295 | args[10] = (unsigned long) -1; | |
296 | ||
297 | p1275_cmd_direct(args); | |
298 | ||
299 | if (args[8]) | |
300 | return (int) args[8]; | |
301 | ||
302 | /* Next we get "phys_high" then "phys_low". On 64-bit | |
303 | * the phys_high cell is don't care since the phys_low | |
304 | * cell has the full value. | |
1da177e4 | 305 | */ |
25edd694 | 306 | *paddr = args[10]; |
1da177e4 | 307 | |
25edd694 | 308 | return 0; |
1da177e4 LT |
309 | } |
310 | ||
311 | /* Get "Unumber" string for the SIMM at the given | |
312 | * memory address. Usually this will be of the form | |
313 | * "Uxxxx" where xxxx is a decimal number which is | |
314 | * etched into the motherboard next to the SIMM slot | |
315 | * in question. | |
316 | */ | |
317 | int prom_getunumber(int syndrome_code, | |
318 | unsigned long phys_addr, | |
319 | char *buf, int buflen) | |
320 | { | |
25edd694 DM |
321 | unsigned long args[12]; |
322 | ||
323 | args[0] = (unsigned long) prom_callmethod_name; | |
324 | args[1] = 7; | |
325 | args[2] = 2; | |
326 | args[3] = (unsigned long) "SUNW,get-unumber"; | |
327 | args[4] = (unsigned int) prom_get_memory_ihandle(); | |
328 | args[5] = buflen; | |
329 | args[6] = (unsigned long) buf; | |
330 | args[7] = 0; | |
331 | args[8] = phys_addr; | |
332 | args[9] = (unsigned int) syndrome_code; | |
333 | args[10] = (unsigned long) -1; | |
334 | args[11] = (unsigned long) -1; | |
335 | ||
336 | p1275_cmd_direct(args); | |
337 | ||
338 | return (int) args[10]; | |
1da177e4 LT |
339 | } |
340 | ||
341 | /* Power management extensions. */ | |
342 | void prom_sleepself(void) | |
343 | { | |
25edd694 DM |
344 | unsigned long args[3]; |
345 | ||
346 | args[0] = (unsigned long) "SUNW,sleep-self"; | |
347 | args[1] = 0; | |
348 | args[2] = 0; | |
349 | p1275_cmd_direct(args); | |
1da177e4 LT |
350 | } |
351 | ||
352 | int prom_sleepsystem(void) | |
353 | { | |
25edd694 DM |
354 | unsigned long args[4]; |
355 | ||
356 | args[0] = (unsigned long) "SUNW,sleep-system"; | |
357 | args[1] = 0; | |
358 | args[2] = 1; | |
359 | args[3] = (unsigned long) -1; | |
360 | p1275_cmd_direct(args); | |
361 | ||
362 | return (int) args[3]; | |
1da177e4 LT |
363 | } |
364 | ||
365 | int prom_wakeupsystem(void) | |
366 | { | |
25edd694 DM |
367 | unsigned long args[4]; |
368 | ||
369 | args[0] = (unsigned long) "SUNW,wakeup-system"; | |
370 | args[1] = 0; | |
371 | args[2] = 1; | |
372 | args[3] = (unsigned long) -1; | |
373 | p1275_cmd_direct(args); | |
374 | ||
375 | return (int) args[3]; | |
1da177e4 LT |
376 | } |
377 | ||
378 | #ifdef CONFIG_SMP | |
7890f794 | 379 | void prom_startcpu(int cpunode, unsigned long pc, unsigned long arg) |
1da177e4 | 380 | { |
25edd694 DM |
381 | unsigned long args[6]; |
382 | ||
383 | args[0] = (unsigned long) "SUNW,start-cpu"; | |
384 | args[1] = 3; | |
385 | args[2] = 0; | |
386 | args[3] = (unsigned int) cpunode; | |
387 | args[4] = pc; | |
388 | args[5] = arg; | |
389 | p1275_cmd_direct(args); | |
7890f794 DM |
390 | } |
391 | ||
392 | void prom_startcpu_cpuid(int cpuid, unsigned long pc, unsigned long arg) | |
393 | { | |
25edd694 DM |
394 | unsigned long args[6]; |
395 | ||
396 | args[0] = (unsigned long) "SUNW,start-cpu-by-cpuid"; | |
397 | args[1] = 3; | |
398 | args[2] = 0; | |
399 | args[3] = (unsigned int) cpuid; | |
400 | args[4] = pc; | |
401 | args[5] = arg; | |
402 | p1275_cmd_direct(args); | |
7890f794 DM |
403 | } |
404 | ||
405 | void prom_stopcpu_cpuid(int cpuid) | |
406 | { | |
25edd694 DM |
407 | unsigned long args[4]; |
408 | ||
409 | args[0] = (unsigned long) "SUNW,stop-cpu-by-cpuid"; | |
410 | args[1] = 1; | |
411 | args[2] = 0; | |
412 | args[3] = (unsigned int) cpuid; | |
413 | p1275_cmd_direct(args); | |
1da177e4 LT |
414 | } |
415 | ||
416 | void prom_stopself(void) | |
417 | { | |
25edd694 DM |
418 | unsigned long args[3]; |
419 | ||
420 | args[0] = (unsigned long) "SUNW,stop-self"; | |
421 | args[1] = 0; | |
422 | args[2] = 0; | |
423 | p1275_cmd_direct(args); | |
1da177e4 LT |
424 | } |
425 | ||
426 | void prom_idleself(void) | |
427 | { | |
25edd694 DM |
428 | unsigned long args[3]; |
429 | ||
430 | args[0] = (unsigned long) "SUNW,idle-self"; | |
431 | args[1] = 0; | |
432 | args[2] = 0; | |
433 | p1275_cmd_direct(args); | |
1da177e4 LT |
434 | } |
435 | ||
436 | void prom_resumecpu(int cpunode) | |
437 | { | |
25edd694 DM |
438 | unsigned long args[4]; |
439 | ||
440 | args[0] = (unsigned long) "SUNW,resume-cpu"; | |
441 | args[1] = 1; | |
442 | args[2] = 0; | |
443 | args[3] = (unsigned int) cpunode; | |
444 | p1275_cmd_direct(args); | |
1da177e4 LT |
445 | } |
446 | #endif |