1*2f9ce69eSAndrew Jones /* 2*2f9ce69eSAndrew Jones * powerpc RTAS 3*2f9ce69eSAndrew Jones * 4*2f9ce69eSAndrew Jones * Copyright (C) 2016, Red Hat Inc, Andrew Jones <drjones@redhat.com> 5*2f9ce69eSAndrew Jones * 6*2f9ce69eSAndrew Jones * This work is licensed under the terms of the GNU LGPL, version 2. 7*2f9ce69eSAndrew Jones */ 8*2f9ce69eSAndrew Jones #include <libcflat.h> 9*2f9ce69eSAndrew Jones #include <libfdt/libfdt.h> 10*2f9ce69eSAndrew Jones #include <devicetree.h> 11*2f9ce69eSAndrew Jones #include <asm/spinlock.h> 12*2f9ce69eSAndrew Jones #include <asm/hcall.h> 13*2f9ce69eSAndrew Jones #include <asm/io.h> 14*2f9ce69eSAndrew Jones #include <asm/rtas.h> 15*2f9ce69eSAndrew Jones 16*2f9ce69eSAndrew Jones extern void enter_rtas(unsigned long); 17*2f9ce69eSAndrew Jones 18*2f9ce69eSAndrew Jones unsigned long rtas_entry; 19*2f9ce69eSAndrew Jones static struct rtas_args rtas_args; 20*2f9ce69eSAndrew Jones static struct spinlock rtas_lock; 21*2f9ce69eSAndrew Jones 22*2f9ce69eSAndrew Jones static int rtas_node(void) 23*2f9ce69eSAndrew Jones { 24*2f9ce69eSAndrew Jones int node = fdt_path_offset(dt_fdt(), "/rtas"); 25*2f9ce69eSAndrew Jones 26*2f9ce69eSAndrew Jones if (node < 0) { 27*2f9ce69eSAndrew Jones printf("%s: /rtas: %s\n", __func__, fdt_strerror(node)); 28*2f9ce69eSAndrew Jones abort(); 29*2f9ce69eSAndrew Jones } 30*2f9ce69eSAndrew Jones 31*2f9ce69eSAndrew Jones return node; 32*2f9ce69eSAndrew Jones } 33*2f9ce69eSAndrew Jones 34*2f9ce69eSAndrew Jones void rtas_init(void) 35*2f9ce69eSAndrew Jones { 36*2f9ce69eSAndrew Jones bool broken_sc1 = hcall_have_broken_sc1(); 37*2f9ce69eSAndrew Jones int node = rtas_node(), len, words, i; 38*2f9ce69eSAndrew Jones const struct fdt_property *prop; 39*2f9ce69eSAndrew Jones u32 *data, *insns; 40*2f9ce69eSAndrew Jones 41*2f9ce69eSAndrew Jones if (!dt_available()) { 42*2f9ce69eSAndrew Jones printf("%s: No device tree!\n", __func__); 43*2f9ce69eSAndrew Jones abort(); 44*2f9ce69eSAndrew Jones } 45*2f9ce69eSAndrew Jones 46*2f9ce69eSAndrew Jones prop = fdt_get_property(dt_fdt(), node, 47*2f9ce69eSAndrew Jones "linux,rtas-entry", &len); 48*2f9ce69eSAndrew Jones if (!prop) { 49*2f9ce69eSAndrew Jones printf("%s: /rtas/linux,rtas-entry: %s\n", 50*2f9ce69eSAndrew Jones __func__, fdt_strerror(len)); 51*2f9ce69eSAndrew Jones abort(); 52*2f9ce69eSAndrew Jones } 53*2f9ce69eSAndrew Jones data = (u32 *)prop->data; 54*2f9ce69eSAndrew Jones rtas_entry = (unsigned long)fdt32_to_cpu(*data); 55*2f9ce69eSAndrew Jones insns = (u32 *)rtas_entry; 56*2f9ce69eSAndrew Jones 57*2f9ce69eSAndrew Jones prop = fdt_get_property(dt_fdt(), node, "rtas-size", &len); 58*2f9ce69eSAndrew Jones if (!prop) { 59*2f9ce69eSAndrew Jones printf("%s: /rtas/rtas-size: %s\n", 60*2f9ce69eSAndrew Jones __func__, fdt_strerror(len)); 61*2f9ce69eSAndrew Jones abort(); 62*2f9ce69eSAndrew Jones } 63*2f9ce69eSAndrew Jones data = (u32 *)prop->data; 64*2f9ce69eSAndrew Jones words = (int)fdt32_to_cpu(*data)/4; 65*2f9ce69eSAndrew Jones 66*2f9ce69eSAndrew Jones for (i = 0; i < words; ++i) { 67*2f9ce69eSAndrew Jones if (broken_sc1 && insns[i] == cpu_to_be32(SC1)) 68*2f9ce69eSAndrew Jones insns[i] = cpu_to_be32(SC1_REPLACEMENT); 69*2f9ce69eSAndrew Jones } 70*2f9ce69eSAndrew Jones } 71*2f9ce69eSAndrew Jones 72*2f9ce69eSAndrew Jones int rtas_token(const char *service) 73*2f9ce69eSAndrew Jones { 74*2f9ce69eSAndrew Jones const struct fdt_property *prop; 75*2f9ce69eSAndrew Jones u32 *token; 76*2f9ce69eSAndrew Jones 77*2f9ce69eSAndrew Jones prop = fdt_get_property(dt_fdt(), rtas_node(), service, NULL); 78*2f9ce69eSAndrew Jones if (prop) { 79*2f9ce69eSAndrew Jones token = (u32 *)prop->data; 80*2f9ce69eSAndrew Jones return fdt32_to_cpu(*token); 81*2f9ce69eSAndrew Jones } 82*2f9ce69eSAndrew Jones return RTAS_UNKNOWN_SERVICE; 83*2f9ce69eSAndrew Jones } 84*2f9ce69eSAndrew Jones 85*2f9ce69eSAndrew Jones int rtas_call(int token, int nargs, int nret, int *outputs, ...) 86*2f9ce69eSAndrew Jones { 87*2f9ce69eSAndrew Jones va_list list; 88*2f9ce69eSAndrew Jones int ret, i; 89*2f9ce69eSAndrew Jones 90*2f9ce69eSAndrew Jones spin_lock(&rtas_lock); 91*2f9ce69eSAndrew Jones 92*2f9ce69eSAndrew Jones rtas_args.token = cpu_to_be32(token); 93*2f9ce69eSAndrew Jones rtas_args.nargs = cpu_to_be32(nargs); 94*2f9ce69eSAndrew Jones rtas_args.nret = cpu_to_be32(nret); 95*2f9ce69eSAndrew Jones rtas_args.rets = &rtas_args.args[nargs]; 96*2f9ce69eSAndrew Jones 97*2f9ce69eSAndrew Jones va_start(list, outputs); 98*2f9ce69eSAndrew Jones for (i = 0; i < nargs; ++i) 99*2f9ce69eSAndrew Jones rtas_args.args[i] = cpu_to_be32(va_arg(list, u32)); 100*2f9ce69eSAndrew Jones va_end(list); 101*2f9ce69eSAndrew Jones 102*2f9ce69eSAndrew Jones for (i = 0; i < nret; ++i) 103*2f9ce69eSAndrew Jones rtas_args.rets[i] = 0; 104*2f9ce69eSAndrew Jones 105*2f9ce69eSAndrew Jones enter_rtas(__pa(&rtas_args)); 106*2f9ce69eSAndrew Jones 107*2f9ce69eSAndrew Jones if (nret > 1 && outputs != NULL) 108*2f9ce69eSAndrew Jones for (i = 0; i < nret - 1; ++i) 109*2f9ce69eSAndrew Jones outputs[i] = be32_to_cpu(rtas_args.rets[i + 1]); 110*2f9ce69eSAndrew Jones 111*2f9ce69eSAndrew Jones ret = nret > 0 ? be32_to_cpu(rtas_args.rets[0]) : 0; 112*2f9ce69eSAndrew Jones 113*2f9ce69eSAndrew Jones spin_unlock(&rtas_lock); 114*2f9ce69eSAndrew Jones return ret; 115*2f9ce69eSAndrew Jones } 116*2f9ce69eSAndrew Jones 117*2f9ce69eSAndrew Jones void rtas_power_off(void) 118*2f9ce69eSAndrew Jones { 119*2f9ce69eSAndrew Jones int ret = rtas_call(rtas_token("power-off"), 2, 1, NULL, -1, -1); 120*2f9ce69eSAndrew Jones printf("RTAS power-off returned %d\n", ret); 121*2f9ce69eSAndrew Jones } 122