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