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