12f9ce69eSAndrew Jones /* 22f9ce69eSAndrew Jones * powerpc RTAS 32f9ce69eSAndrew Jones * 42f9ce69eSAndrew Jones * Copyright (C) 2016, Red Hat Inc, Andrew Jones <drjones@redhat.com> 52f9ce69eSAndrew Jones * 62f9ce69eSAndrew Jones * This work is licensed under the terms of the GNU LGPL, version 2. 72f9ce69eSAndrew Jones */ 82f9ce69eSAndrew Jones #include <libcflat.h> 92f9ce69eSAndrew Jones #include <libfdt/libfdt.h> 102f9ce69eSAndrew Jones #include <devicetree.h> 112f9ce69eSAndrew Jones #include <asm/spinlock.h> 122f9ce69eSAndrew Jones #include <asm/hcall.h> 132f9ce69eSAndrew Jones #include <asm/io.h> 142f9ce69eSAndrew Jones #include <asm/rtas.h> 152f9ce69eSAndrew Jones 162f9ce69eSAndrew Jones extern void enter_rtas(unsigned long); 172f9ce69eSAndrew Jones 182f9ce69eSAndrew Jones unsigned long rtas_entry; 192f9ce69eSAndrew Jones static struct rtas_args rtas_args; 202f9ce69eSAndrew Jones static struct spinlock rtas_lock; 212f9ce69eSAndrew Jones 222f9ce69eSAndrew Jones static int rtas_node(void) 232f9ce69eSAndrew Jones { 242f9ce69eSAndrew Jones int node = fdt_path_offset(dt_fdt(), "/rtas"); 252f9ce69eSAndrew Jones 262f9ce69eSAndrew Jones if (node < 0) { 272f9ce69eSAndrew Jones printf("%s: /rtas: %s\n", __func__, fdt_strerror(node)); 282f9ce69eSAndrew Jones abort(); 292f9ce69eSAndrew Jones } 302f9ce69eSAndrew Jones 312f9ce69eSAndrew Jones return node; 322f9ce69eSAndrew Jones } 332f9ce69eSAndrew Jones 342f9ce69eSAndrew Jones void rtas_init(void) 352f9ce69eSAndrew Jones { 362f9ce69eSAndrew Jones bool broken_sc1 = hcall_have_broken_sc1(); 372f9ce69eSAndrew Jones int node = rtas_node(), len, words, i; 382f9ce69eSAndrew Jones const struct fdt_property *prop; 392f9ce69eSAndrew Jones u32 *data, *insns; 402f9ce69eSAndrew Jones 412f9ce69eSAndrew Jones if (!dt_available()) { 422f9ce69eSAndrew Jones printf("%s: No device tree!\n", __func__); 432f9ce69eSAndrew Jones abort(); 442f9ce69eSAndrew Jones } 452f9ce69eSAndrew Jones 462f9ce69eSAndrew Jones prop = fdt_get_property(dt_fdt(), node, 472f9ce69eSAndrew Jones "linux,rtas-entry", &len); 482f9ce69eSAndrew Jones if (!prop) { 492f9ce69eSAndrew Jones printf("%s: /rtas/linux,rtas-entry: %s\n", 502f9ce69eSAndrew Jones __func__, fdt_strerror(len)); 512f9ce69eSAndrew Jones abort(); 522f9ce69eSAndrew Jones } 532f9ce69eSAndrew Jones data = (u32 *)prop->data; 542f9ce69eSAndrew Jones rtas_entry = (unsigned long)fdt32_to_cpu(*data); 552f9ce69eSAndrew Jones insns = (u32 *)rtas_entry; 562f9ce69eSAndrew Jones 572f9ce69eSAndrew Jones prop = fdt_get_property(dt_fdt(), node, "rtas-size", &len); 582f9ce69eSAndrew Jones if (!prop) { 592f9ce69eSAndrew Jones printf("%s: /rtas/rtas-size: %s\n", 602f9ce69eSAndrew Jones __func__, fdt_strerror(len)); 612f9ce69eSAndrew Jones abort(); 622f9ce69eSAndrew Jones } 632f9ce69eSAndrew Jones data = (u32 *)prop->data; 642f9ce69eSAndrew Jones words = (int)fdt32_to_cpu(*data)/4; 652f9ce69eSAndrew Jones 662f9ce69eSAndrew Jones for (i = 0; i < words; ++i) { 672f9ce69eSAndrew Jones if (broken_sc1 && insns[i] == cpu_to_be32(SC1)) 682f9ce69eSAndrew Jones insns[i] = cpu_to_be32(SC1_REPLACEMENT); 692f9ce69eSAndrew Jones } 702f9ce69eSAndrew Jones } 712f9ce69eSAndrew Jones 72*2565dce1SThomas Huth int rtas_token(const char *service, uint32_t *token) 732f9ce69eSAndrew Jones { 742f9ce69eSAndrew Jones const struct fdt_property *prop; 75*2565dce1SThomas Huth u32 *data; 76*2565dce1SThomas Huth 77*2565dce1SThomas Huth if (!dt_available()) 78*2565dce1SThomas Huth return RTAS_UNKNOWN_SERVICE; 792f9ce69eSAndrew Jones 802f9ce69eSAndrew Jones prop = fdt_get_property(dt_fdt(), rtas_node(), service, NULL); 81*2565dce1SThomas Huth if (!prop) 822f9ce69eSAndrew Jones return RTAS_UNKNOWN_SERVICE; 83*2565dce1SThomas Huth 84*2565dce1SThomas Huth data = (u32 *)prop->data; 85*2565dce1SThomas Huth *token = fdt32_to_cpu(*data); 86*2565dce1SThomas Huth 87*2565dce1SThomas Huth return 0; 882f9ce69eSAndrew Jones } 892f9ce69eSAndrew Jones 902f9ce69eSAndrew Jones int rtas_call(int token, int nargs, int nret, int *outputs, ...) 912f9ce69eSAndrew Jones { 922f9ce69eSAndrew Jones va_list list; 932f9ce69eSAndrew Jones int ret, i; 942f9ce69eSAndrew Jones 952f9ce69eSAndrew Jones spin_lock(&rtas_lock); 962f9ce69eSAndrew Jones 972f9ce69eSAndrew Jones rtas_args.token = cpu_to_be32(token); 982f9ce69eSAndrew Jones rtas_args.nargs = cpu_to_be32(nargs); 992f9ce69eSAndrew Jones rtas_args.nret = cpu_to_be32(nret); 1002f9ce69eSAndrew Jones rtas_args.rets = &rtas_args.args[nargs]; 1012f9ce69eSAndrew Jones 1022f9ce69eSAndrew Jones va_start(list, outputs); 1032f9ce69eSAndrew Jones for (i = 0; i < nargs; ++i) 1042f9ce69eSAndrew Jones rtas_args.args[i] = cpu_to_be32(va_arg(list, u32)); 1052f9ce69eSAndrew Jones va_end(list); 1062f9ce69eSAndrew Jones 1072f9ce69eSAndrew Jones for (i = 0; i < nret; ++i) 1082f9ce69eSAndrew Jones rtas_args.rets[i] = 0; 1092f9ce69eSAndrew Jones 1102f9ce69eSAndrew Jones enter_rtas(__pa(&rtas_args)); 1112f9ce69eSAndrew Jones 1122f9ce69eSAndrew Jones if (nret > 1 && outputs != NULL) 1132f9ce69eSAndrew Jones for (i = 0; i < nret - 1; ++i) 1142f9ce69eSAndrew Jones outputs[i] = be32_to_cpu(rtas_args.rets[i + 1]); 1152f9ce69eSAndrew Jones 1162f9ce69eSAndrew Jones ret = nret > 0 ? be32_to_cpu(rtas_args.rets[0]) : 0; 1172f9ce69eSAndrew Jones 1182f9ce69eSAndrew Jones spin_unlock(&rtas_lock); 1192f9ce69eSAndrew Jones return ret; 1202f9ce69eSAndrew Jones } 1212f9ce69eSAndrew Jones 1222f9ce69eSAndrew Jones void rtas_power_off(void) 1232f9ce69eSAndrew Jones { 124*2565dce1SThomas Huth uint32_t token; 125*2565dce1SThomas Huth int ret; 126*2565dce1SThomas Huth 127*2565dce1SThomas Huth ret = rtas_token("power-off", &token); 128*2565dce1SThomas Huth if (ret) { 129*2565dce1SThomas Huth puts("RTAS power-off not available\n"); 130*2565dce1SThomas Huth return; 131*2565dce1SThomas Huth } 132*2565dce1SThomas Huth 133*2565dce1SThomas Huth ret = rtas_call(token, 2, 1, NULL, -1, -1); 1342f9ce69eSAndrew Jones printf("RTAS power-off returned %d\n", ret); 1352f9ce69eSAndrew Jones } 136