xref: /openbmc/linux/arch/sparc/prom/misc_64.c (revision bd4352cadfacb9084c97c853b025fac010266c26)
15de18cdeSSam Ravnborg /*
25de18cdeSSam Ravnborg  * misc.c:  Miscellaneous prom functions that don't belong
35de18cdeSSam Ravnborg  *          anywhere else.
45de18cdeSSam Ravnborg  *
55de18cdeSSam Ravnborg  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
65de18cdeSSam Ravnborg  * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
75de18cdeSSam Ravnborg  */
85de18cdeSSam Ravnborg 
95de18cdeSSam Ravnborg #include <linux/types.h>
105de18cdeSSam Ravnborg #include <linux/kernel.h>
115de18cdeSSam Ravnborg #include <linux/sched.h>
125de18cdeSSam Ravnborg #include <linux/interrupt.h>
135de18cdeSSam Ravnborg #include <linux/delay.h>
14917c3660SSam Ravnborg #include <linux/module.h>
15917c3660SSam Ravnborg 
165de18cdeSSam Ravnborg #include <asm/openprom.h>
175de18cdeSSam Ravnborg #include <asm/oplib.h>
185de18cdeSSam Ravnborg #include <asm/system.h>
195de18cdeSSam Ravnborg #include <asm/ldc.h>
205de18cdeSSam Ravnborg 
215de18cdeSSam Ravnborg int prom_service_exists(const char *service_name)
225de18cdeSSam Ravnborg {
235de18cdeSSam Ravnborg 	int err = p1275_cmd("test", P1275_ARG(0, P1275_ARG_IN_STRING) |
245de18cdeSSam Ravnborg 			    P1275_INOUT(1, 1), service_name);
255de18cdeSSam Ravnborg 
265de18cdeSSam Ravnborg 	if (err)
275de18cdeSSam Ravnborg 		return 0;
285de18cdeSSam Ravnborg 	return 1;
295de18cdeSSam Ravnborg }
305de18cdeSSam Ravnborg 
315de18cdeSSam Ravnborg void prom_sun4v_guest_soft_state(void)
325de18cdeSSam Ravnborg {
335de18cdeSSam Ravnborg 	const char *svc = "SUNW,soft-state-supported";
345de18cdeSSam Ravnborg 
355de18cdeSSam Ravnborg 	if (!prom_service_exists(svc))
365de18cdeSSam Ravnborg 		return;
375de18cdeSSam Ravnborg 	p1275_cmd(svc, P1275_INOUT(0, 0));
385de18cdeSSam Ravnborg }
395de18cdeSSam Ravnborg 
405de18cdeSSam Ravnborg /* Reset and reboot the machine with the command 'bcommand'. */
415de18cdeSSam Ravnborg void prom_reboot(const char *bcommand)
425de18cdeSSam Ravnborg {
435de18cdeSSam Ravnborg #ifdef CONFIG_SUN_LDOMS
445de18cdeSSam Ravnborg 	if (ldom_domaining_enabled)
455de18cdeSSam Ravnborg 		ldom_reboot(bcommand);
465de18cdeSSam Ravnborg #endif
475de18cdeSSam Ravnborg 	p1275_cmd("boot", P1275_ARG(0, P1275_ARG_IN_STRING) |
485de18cdeSSam Ravnborg 		  P1275_INOUT(1, 0), bcommand);
495de18cdeSSam Ravnborg }
505de18cdeSSam Ravnborg 
515de18cdeSSam Ravnborg /* Forth evaluate the expression contained in 'fstring'. */
525de18cdeSSam Ravnborg void prom_feval(const char *fstring)
535de18cdeSSam Ravnborg {
545de18cdeSSam Ravnborg 	if (!fstring || fstring[0] == 0)
555de18cdeSSam Ravnborg 		return;
565de18cdeSSam Ravnborg 	p1275_cmd("interpret", P1275_ARG(0, P1275_ARG_IN_STRING) |
575de18cdeSSam Ravnborg 		  P1275_INOUT(1, 1), fstring);
585de18cdeSSam Ravnborg }
59917c3660SSam Ravnborg EXPORT_SYMBOL(prom_feval);
605de18cdeSSam Ravnborg 
615de18cdeSSam Ravnborg #ifdef CONFIG_SMP
625de18cdeSSam Ravnborg extern void smp_capture(void);
635de18cdeSSam Ravnborg extern void smp_release(void);
645de18cdeSSam Ravnborg #endif
655de18cdeSSam Ravnborg 
665de18cdeSSam Ravnborg /* Drop into the prom, with the chance to continue with the 'go'
675de18cdeSSam Ravnborg  * prom command.
685de18cdeSSam Ravnborg  */
695de18cdeSSam Ravnborg void prom_cmdline(void)
705de18cdeSSam Ravnborg {
715de18cdeSSam Ravnborg 	unsigned long flags;
725de18cdeSSam Ravnborg 
735de18cdeSSam Ravnborg 	local_irq_save(flags);
745de18cdeSSam Ravnborg 
755de18cdeSSam Ravnborg #ifdef CONFIG_SMP
765de18cdeSSam Ravnborg 	smp_capture();
775de18cdeSSam Ravnborg #endif
785de18cdeSSam Ravnborg 
795de18cdeSSam Ravnborg 	p1275_cmd("enter", P1275_INOUT(0, 0));
805de18cdeSSam Ravnborg 
815de18cdeSSam Ravnborg #ifdef CONFIG_SMP
825de18cdeSSam Ravnborg 	smp_release();
835de18cdeSSam Ravnborg #endif
845de18cdeSSam Ravnborg 
855de18cdeSSam Ravnborg 	local_irq_restore(flags);
865de18cdeSSam Ravnborg }
875de18cdeSSam Ravnborg 
885de18cdeSSam Ravnborg /* Drop into the prom, but completely terminate the program.
895de18cdeSSam Ravnborg  * No chance of continuing.
905de18cdeSSam Ravnborg  */
91*bd4352caSDavid S. Miller void notrace prom_halt(void)
925de18cdeSSam Ravnborg {
935de18cdeSSam Ravnborg #ifdef CONFIG_SUN_LDOMS
945de18cdeSSam Ravnborg 	if (ldom_domaining_enabled)
955de18cdeSSam Ravnborg 		ldom_power_off();
965de18cdeSSam Ravnborg #endif
975de18cdeSSam Ravnborg again:
985de18cdeSSam Ravnborg 	p1275_cmd("exit", P1275_INOUT(0, 0));
995de18cdeSSam Ravnborg 	goto again; /* PROM is out to get me -DaveM */
1005de18cdeSSam Ravnborg }
1015de18cdeSSam Ravnborg 
1025de18cdeSSam Ravnborg void prom_halt_power_off(void)
1035de18cdeSSam Ravnborg {
1045de18cdeSSam Ravnborg #ifdef CONFIG_SUN_LDOMS
1055de18cdeSSam Ravnborg 	if (ldom_domaining_enabled)
1065de18cdeSSam Ravnborg 		ldom_power_off();
1075de18cdeSSam Ravnborg #endif
1085de18cdeSSam Ravnborg 	p1275_cmd("SUNW,power-off", P1275_INOUT(0, 0));
1095de18cdeSSam Ravnborg 
1105de18cdeSSam Ravnborg 	/* if nothing else helps, we just halt */
1115de18cdeSSam Ravnborg 	prom_halt();
1125de18cdeSSam Ravnborg }
1135de18cdeSSam Ravnborg 
1145de18cdeSSam Ravnborg /* Set prom sync handler to call function 'funcp'. */
1155de18cdeSSam Ravnborg void prom_setcallback(callback_func_t funcp)
1165de18cdeSSam Ravnborg {
1175de18cdeSSam Ravnborg 	if (!funcp)
1185de18cdeSSam Ravnborg 		return;
1195de18cdeSSam Ravnborg 	p1275_cmd("set-callback", P1275_ARG(0, P1275_ARG_IN_FUNCTION) |
1205de18cdeSSam Ravnborg 		  P1275_INOUT(1, 1), funcp);
1215de18cdeSSam Ravnborg }
1225de18cdeSSam Ravnborg 
1235de18cdeSSam Ravnborg /* Get the idprom and stuff it into buffer 'idbuf'.  Returns the
1245de18cdeSSam Ravnborg  * format type.  'num_bytes' is the number of bytes that your idbuf
1255de18cdeSSam Ravnborg  * has space for.  Returns 0xff on error.
1265de18cdeSSam Ravnborg  */
1275de18cdeSSam Ravnborg unsigned char prom_get_idprom(char *idbuf, int num_bytes)
1285de18cdeSSam Ravnborg {
1295de18cdeSSam Ravnborg 	int len;
1305de18cdeSSam Ravnborg 
1315de18cdeSSam Ravnborg 	len = prom_getproplen(prom_root_node, "idprom");
1325de18cdeSSam Ravnborg 	if ((len >num_bytes) || (len == -1))
1335de18cdeSSam Ravnborg 		return 0xff;
1345de18cdeSSam Ravnborg 	if (!prom_getproperty(prom_root_node, "idprom", idbuf, num_bytes))
1355de18cdeSSam Ravnborg 		return idbuf[0];
1365de18cdeSSam Ravnborg 
1375de18cdeSSam Ravnborg 	return 0xff;
1385de18cdeSSam Ravnborg }
1395de18cdeSSam Ravnborg 
1405de18cdeSSam Ravnborg int prom_get_mmu_ihandle(void)
1415de18cdeSSam Ravnborg {
1425de18cdeSSam Ravnborg 	int node, ret;
1435de18cdeSSam Ravnborg 
1445de18cdeSSam Ravnborg 	if (prom_mmu_ihandle_cache != 0)
1455de18cdeSSam Ravnborg 		return prom_mmu_ihandle_cache;
1465de18cdeSSam Ravnborg 
1475de18cdeSSam Ravnborg 	node = prom_finddevice(prom_chosen_path);
1485de18cdeSSam Ravnborg 	ret = prom_getint(node, prom_mmu_name);
1495de18cdeSSam Ravnborg 	if (ret == -1 || ret == 0)
1505de18cdeSSam Ravnborg 		prom_mmu_ihandle_cache = -1;
1515de18cdeSSam Ravnborg 	else
1525de18cdeSSam Ravnborg 		prom_mmu_ihandle_cache = ret;
1535de18cdeSSam Ravnborg 
1545de18cdeSSam Ravnborg 	return ret;
1555de18cdeSSam Ravnborg }
1565de18cdeSSam Ravnborg 
1575de18cdeSSam Ravnborg static int prom_get_memory_ihandle(void)
1585de18cdeSSam Ravnborg {
1595de18cdeSSam Ravnborg 	static int memory_ihandle_cache;
1605de18cdeSSam Ravnborg 	int node, ret;
1615de18cdeSSam Ravnborg 
1625de18cdeSSam Ravnborg 	if (memory_ihandle_cache != 0)
1635de18cdeSSam Ravnborg 		return memory_ihandle_cache;
1645de18cdeSSam Ravnborg 
1655de18cdeSSam Ravnborg 	node = prom_finddevice("/chosen");
1665de18cdeSSam Ravnborg 	ret = prom_getint(node, "memory");
1675de18cdeSSam Ravnborg 	if (ret == -1 || ret == 0)
1685de18cdeSSam Ravnborg 		memory_ihandle_cache = -1;
1695de18cdeSSam Ravnborg 	else
1705de18cdeSSam Ravnborg 		memory_ihandle_cache = ret;
1715de18cdeSSam Ravnborg 
1725de18cdeSSam Ravnborg 	return ret;
1735de18cdeSSam Ravnborg }
1745de18cdeSSam Ravnborg 
1755de18cdeSSam Ravnborg /* Load explicit I/D TLB entries. */
1765de18cdeSSam Ravnborg long prom_itlb_load(unsigned long index,
1775de18cdeSSam Ravnborg 		    unsigned long tte_data,
1785de18cdeSSam Ravnborg 		    unsigned long vaddr)
1795de18cdeSSam Ravnborg {
1805de18cdeSSam Ravnborg 	return p1275_cmd(prom_callmethod_name,
1815de18cdeSSam Ravnborg 			 (P1275_ARG(0, P1275_ARG_IN_STRING) |
1825de18cdeSSam Ravnborg 			  P1275_ARG(2, P1275_ARG_IN_64B) |
1835de18cdeSSam Ravnborg 			  P1275_ARG(3, P1275_ARG_IN_64B) |
1845de18cdeSSam Ravnborg 			  P1275_INOUT(5, 1)),
1855de18cdeSSam Ravnborg 			 "SUNW,itlb-load",
1865de18cdeSSam Ravnborg 			 prom_get_mmu_ihandle(),
1875de18cdeSSam Ravnborg 			 /* And then our actual args are pushed backwards. */
1885de18cdeSSam Ravnborg 			 vaddr,
1895de18cdeSSam Ravnborg 			 tte_data,
1905de18cdeSSam Ravnborg 			 index);
1915de18cdeSSam Ravnborg }
1925de18cdeSSam Ravnborg 
1935de18cdeSSam Ravnborg long prom_dtlb_load(unsigned long index,
1945de18cdeSSam Ravnborg 		    unsigned long tte_data,
1955de18cdeSSam Ravnborg 		    unsigned long vaddr)
1965de18cdeSSam Ravnborg {
1975de18cdeSSam Ravnborg 	return p1275_cmd(prom_callmethod_name,
1985de18cdeSSam Ravnborg 			 (P1275_ARG(0, P1275_ARG_IN_STRING) |
1995de18cdeSSam Ravnborg 			  P1275_ARG(2, P1275_ARG_IN_64B) |
2005de18cdeSSam Ravnborg 			  P1275_ARG(3, P1275_ARG_IN_64B) |
2015de18cdeSSam Ravnborg 			  P1275_INOUT(5, 1)),
2025de18cdeSSam Ravnborg 			 "SUNW,dtlb-load",
2035de18cdeSSam Ravnborg 			 prom_get_mmu_ihandle(),
2045de18cdeSSam Ravnborg 			 /* And then our actual args are pushed backwards. */
2055de18cdeSSam Ravnborg 			 vaddr,
2065de18cdeSSam Ravnborg 			 tte_data,
2075de18cdeSSam Ravnborg 			 index);
2085de18cdeSSam Ravnborg }
2095de18cdeSSam Ravnborg 
2105de18cdeSSam Ravnborg int prom_map(int mode, unsigned long size,
2115de18cdeSSam Ravnborg 	     unsigned long vaddr, unsigned long paddr)
2125de18cdeSSam Ravnborg {
2135de18cdeSSam Ravnborg 	int ret = p1275_cmd(prom_callmethod_name,
2145de18cdeSSam Ravnborg 			    (P1275_ARG(0, P1275_ARG_IN_STRING) |
2155de18cdeSSam Ravnborg 			     P1275_ARG(3, P1275_ARG_IN_64B) |
2165de18cdeSSam Ravnborg 			     P1275_ARG(4, P1275_ARG_IN_64B) |
2175de18cdeSSam Ravnborg 			     P1275_ARG(6, P1275_ARG_IN_64B) |
2185de18cdeSSam Ravnborg 			     P1275_INOUT(7, 1)),
2195de18cdeSSam Ravnborg 			    prom_map_name,
2205de18cdeSSam Ravnborg 			    prom_get_mmu_ihandle(),
2215de18cdeSSam Ravnborg 			    mode,
2225de18cdeSSam Ravnborg 			    size,
2235de18cdeSSam Ravnborg 			    vaddr,
2245de18cdeSSam Ravnborg 			    0,
2255de18cdeSSam Ravnborg 			    paddr);
2265de18cdeSSam Ravnborg 
2275de18cdeSSam Ravnborg 	if (ret == 0)
2285de18cdeSSam Ravnborg 		ret = -1;
2295de18cdeSSam Ravnborg 	return ret;
2305de18cdeSSam Ravnborg }
2315de18cdeSSam Ravnborg 
2325de18cdeSSam Ravnborg void prom_unmap(unsigned long size, unsigned long vaddr)
2335de18cdeSSam Ravnborg {
2345de18cdeSSam Ravnborg 	p1275_cmd(prom_callmethod_name,
2355de18cdeSSam Ravnborg 		  (P1275_ARG(0, P1275_ARG_IN_STRING) |
2365de18cdeSSam Ravnborg 		   P1275_ARG(2, P1275_ARG_IN_64B) |
2375de18cdeSSam Ravnborg 		   P1275_ARG(3, P1275_ARG_IN_64B) |
2385de18cdeSSam Ravnborg 		   P1275_INOUT(4, 0)),
2395de18cdeSSam Ravnborg 		  prom_unmap_name,
2405de18cdeSSam Ravnborg 		  prom_get_mmu_ihandle(),
2415de18cdeSSam Ravnborg 		  size,
2425de18cdeSSam Ravnborg 		  vaddr);
2435de18cdeSSam Ravnborg }
2445de18cdeSSam Ravnborg 
2455de18cdeSSam Ravnborg /* Set aside physical memory which is not touched or modified
2465de18cdeSSam Ravnborg  * across soft resets.
2475de18cdeSSam Ravnborg  */
2485de18cdeSSam Ravnborg unsigned long prom_retain(const char *name,
2495de18cdeSSam Ravnborg 			  unsigned long pa_low, unsigned long pa_high,
2505de18cdeSSam Ravnborg 			  long size, long align)
2515de18cdeSSam Ravnborg {
2525de18cdeSSam Ravnborg 	/* XXX I don't think we return multiple values correctly.
2535de18cdeSSam Ravnborg 	 * XXX OBP supposedly returns pa_low/pa_high here, how does
2545de18cdeSSam Ravnborg 	 * XXX it work?
2555de18cdeSSam Ravnborg 	 */
2565de18cdeSSam Ravnborg 
2575de18cdeSSam Ravnborg 	/* If align is zero, the pa_low/pa_high args are passed,
2585de18cdeSSam Ravnborg 	 * else they are not.
2595de18cdeSSam Ravnborg 	 */
2605de18cdeSSam Ravnborg 	if (align == 0)
2615de18cdeSSam Ravnborg 		return p1275_cmd("SUNW,retain",
2625de18cdeSSam Ravnborg 				 (P1275_ARG(0, P1275_ARG_IN_BUF) | P1275_INOUT(5, 2)),
2635de18cdeSSam Ravnborg 				 name, pa_low, pa_high, size, align);
2645de18cdeSSam Ravnborg 	else
2655de18cdeSSam Ravnborg 		return p1275_cmd("SUNW,retain",
2665de18cdeSSam Ravnborg 				 (P1275_ARG(0, P1275_ARG_IN_BUF) | P1275_INOUT(3, 2)),
2675de18cdeSSam Ravnborg 				 name, size, align);
2685de18cdeSSam Ravnborg }
2695de18cdeSSam Ravnborg 
2705de18cdeSSam Ravnborg /* Get "Unumber" string for the SIMM at the given
2715de18cdeSSam Ravnborg  * memory address.  Usually this will be of the form
2725de18cdeSSam Ravnborg  * "Uxxxx" where xxxx is a decimal number which is
2735de18cdeSSam Ravnborg  * etched into the motherboard next to the SIMM slot
2745de18cdeSSam Ravnborg  * in question.
2755de18cdeSSam Ravnborg  */
2765de18cdeSSam Ravnborg int prom_getunumber(int syndrome_code,
2775de18cdeSSam Ravnborg 		    unsigned long phys_addr,
2785de18cdeSSam Ravnborg 		    char *buf, int buflen)
2795de18cdeSSam Ravnborg {
2805de18cdeSSam Ravnborg 	return p1275_cmd(prom_callmethod_name,
2815de18cdeSSam Ravnborg 			 (P1275_ARG(0, P1275_ARG_IN_STRING)	|
2825de18cdeSSam Ravnborg 			  P1275_ARG(3, P1275_ARG_OUT_BUF)	|
2835de18cdeSSam Ravnborg 			  P1275_ARG(6, P1275_ARG_IN_64B)	|
2845de18cdeSSam Ravnborg 			  P1275_INOUT(8, 2)),
2855de18cdeSSam Ravnborg 			 "SUNW,get-unumber", prom_get_memory_ihandle(),
2865de18cdeSSam Ravnborg 			 buflen, buf, P1275_SIZE(buflen),
2875de18cdeSSam Ravnborg 			 0, phys_addr, syndrome_code);
2885de18cdeSSam Ravnborg }
2895de18cdeSSam Ravnborg 
2905de18cdeSSam Ravnborg /* Power management extensions. */
2915de18cdeSSam Ravnborg void prom_sleepself(void)
2925de18cdeSSam Ravnborg {
2935de18cdeSSam Ravnborg 	p1275_cmd("SUNW,sleep-self", P1275_INOUT(0, 0));
2945de18cdeSSam Ravnborg }
2955de18cdeSSam Ravnborg 
2965de18cdeSSam Ravnborg int prom_sleepsystem(void)
2975de18cdeSSam Ravnborg {
2985de18cdeSSam Ravnborg 	return p1275_cmd("SUNW,sleep-system", P1275_INOUT(0, 1));
2995de18cdeSSam Ravnborg }
3005de18cdeSSam Ravnborg 
3015de18cdeSSam Ravnborg int prom_wakeupsystem(void)
3025de18cdeSSam Ravnborg {
3035de18cdeSSam Ravnborg 	return p1275_cmd("SUNW,wakeup-system", P1275_INOUT(0, 1));
3045de18cdeSSam Ravnborg }
3055de18cdeSSam Ravnborg 
3065de18cdeSSam Ravnborg #ifdef CONFIG_SMP
3075de18cdeSSam Ravnborg void prom_startcpu(int cpunode, unsigned long pc, unsigned long arg)
3085de18cdeSSam Ravnborg {
3095de18cdeSSam Ravnborg 	p1275_cmd("SUNW,start-cpu", P1275_INOUT(3, 0), cpunode, pc, arg);
3105de18cdeSSam Ravnborg }
3115de18cdeSSam Ravnborg 
3125de18cdeSSam Ravnborg void prom_startcpu_cpuid(int cpuid, unsigned long pc, unsigned long arg)
3135de18cdeSSam Ravnborg {
3145de18cdeSSam Ravnborg 	p1275_cmd("SUNW,start-cpu-by-cpuid", P1275_INOUT(3, 0),
3155de18cdeSSam Ravnborg 		  cpuid, pc, arg);
3165de18cdeSSam Ravnborg }
3175de18cdeSSam Ravnborg 
3185de18cdeSSam Ravnborg void prom_stopcpu_cpuid(int cpuid)
3195de18cdeSSam Ravnborg {
3205de18cdeSSam Ravnborg 	p1275_cmd("SUNW,stop-cpu-by-cpuid", P1275_INOUT(1, 0),
3215de18cdeSSam Ravnborg 		  cpuid);
3225de18cdeSSam Ravnborg }
3235de18cdeSSam Ravnborg 
3245de18cdeSSam Ravnborg void prom_stopself(void)
3255de18cdeSSam Ravnborg {
3265de18cdeSSam Ravnborg 	p1275_cmd("SUNW,stop-self", P1275_INOUT(0, 0));
3275de18cdeSSam Ravnborg }
3285de18cdeSSam Ravnborg 
3295de18cdeSSam Ravnborg void prom_idleself(void)
3305de18cdeSSam Ravnborg {
3315de18cdeSSam Ravnborg 	p1275_cmd("SUNW,idle-self", P1275_INOUT(0, 0));
3325de18cdeSSam Ravnborg }
3335de18cdeSSam Ravnborg 
3345de18cdeSSam Ravnborg void prom_resumecpu(int cpunode)
3355de18cdeSSam Ravnborg {
3365de18cdeSSam Ravnborg 	p1275_cmd("SUNW,resume-cpu", P1275_INOUT(1, 0), cpunode);
3375de18cdeSSam Ravnborg }
3385de18cdeSSam Ravnborg #endif
339