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>
12*93c847c1SNicholas Piggin #include <asm/smp.h>
132f9ce69eSAndrew Jones #include <asm/hcall.h>
142f9ce69eSAndrew Jones #include <asm/io.h>
152f9ce69eSAndrew Jones #include <asm/rtas.h>
162f9ce69eSAndrew Jones
172f9ce69eSAndrew Jones extern void enter_rtas(unsigned long);
182f9ce69eSAndrew Jones
192f9ce69eSAndrew Jones unsigned long rtas_entry;
202f9ce69eSAndrew Jones static struct rtas_args rtas_args;
212f9ce69eSAndrew Jones static struct spinlock rtas_lock;
222f9ce69eSAndrew Jones
rtas_node(void)232f9ce69eSAndrew Jones static int rtas_node(void)
242f9ce69eSAndrew Jones {
252f9ce69eSAndrew Jones int node = fdt_path_offset(dt_fdt(), "/rtas");
262f9ce69eSAndrew Jones
272f9ce69eSAndrew Jones if (node < 0) {
282f9ce69eSAndrew Jones printf("%s: /rtas: %s\n", __func__, fdt_strerror(node));
292f9ce69eSAndrew Jones abort();
302f9ce69eSAndrew Jones }
312f9ce69eSAndrew Jones
322f9ce69eSAndrew Jones return node;
332f9ce69eSAndrew Jones }
342f9ce69eSAndrew Jones
rtas_init(void)352f9ce69eSAndrew Jones void rtas_init(void)
362f9ce69eSAndrew Jones {
372f9ce69eSAndrew Jones bool broken_sc1 = hcall_have_broken_sc1();
382f9ce69eSAndrew Jones int node = rtas_node(), len, words, i;
392f9ce69eSAndrew Jones const struct fdt_property *prop;
402f9ce69eSAndrew Jones u32 *data, *insns;
412f9ce69eSAndrew Jones
422f9ce69eSAndrew Jones if (!dt_available()) {
432f9ce69eSAndrew Jones printf("%s: No device tree!\n", __func__);
442f9ce69eSAndrew Jones abort();
452f9ce69eSAndrew Jones }
462f9ce69eSAndrew Jones
472f9ce69eSAndrew Jones prop = fdt_get_property(dt_fdt(), node,
482f9ce69eSAndrew Jones "linux,rtas-entry", &len);
492f9ce69eSAndrew Jones if (!prop) {
50141b4584SDavid Gibson /* We don't have a qemu provided RTAS blob, enter_rtas
51141b4584SDavid Gibson * will use H_RTAS directly */
52141b4584SDavid Gibson return;
532f9ce69eSAndrew Jones }
542f9ce69eSAndrew Jones data = (u32 *)prop->data;
552f9ce69eSAndrew Jones rtas_entry = (unsigned long)fdt32_to_cpu(*data);
562f9ce69eSAndrew Jones insns = (u32 *)rtas_entry;
572f9ce69eSAndrew Jones
582f9ce69eSAndrew Jones prop = fdt_get_property(dt_fdt(), node, "rtas-size", &len);
592f9ce69eSAndrew Jones if (!prop) {
602f9ce69eSAndrew Jones printf("%s: /rtas/rtas-size: %s\n",
612f9ce69eSAndrew Jones __func__, fdt_strerror(len));
622f9ce69eSAndrew Jones abort();
632f9ce69eSAndrew Jones }
642f9ce69eSAndrew Jones data = (u32 *)prop->data;
652f9ce69eSAndrew Jones words = (int)fdt32_to_cpu(*data)/4;
662f9ce69eSAndrew Jones
672f9ce69eSAndrew Jones for (i = 0; i < words; ++i) {
682f9ce69eSAndrew Jones if (broken_sc1 && insns[i] == cpu_to_be32(SC1))
692f9ce69eSAndrew Jones insns[i] = cpu_to_be32(SC1_REPLACEMENT);
702f9ce69eSAndrew Jones }
712f9ce69eSAndrew Jones }
722f9ce69eSAndrew Jones
rtas_token(const char * service,uint32_t * token)732565dce1SThomas Huth int rtas_token(const char *service, uint32_t *token)
742f9ce69eSAndrew Jones {
752f9ce69eSAndrew Jones const struct fdt_property *prop;
762565dce1SThomas Huth u32 *data;
772565dce1SThomas Huth
782565dce1SThomas Huth if (!dt_available())
792565dce1SThomas Huth return RTAS_UNKNOWN_SERVICE;
802f9ce69eSAndrew Jones
812f9ce69eSAndrew Jones prop = fdt_get_property(dt_fdt(), rtas_node(), service, NULL);
822565dce1SThomas Huth if (!prop)
832f9ce69eSAndrew Jones return RTAS_UNKNOWN_SERVICE;
842565dce1SThomas Huth
852565dce1SThomas Huth data = (u32 *)prop->data;
862565dce1SThomas Huth *token = fdt32_to_cpu(*data);
872565dce1SThomas Huth
882565dce1SThomas Huth return 0;
892f9ce69eSAndrew Jones }
902f9ce69eSAndrew Jones
__rtas_call(struct rtas_args * args)91789a8e69SNicholas Piggin static void __rtas_call(struct rtas_args *args)
922f9ce69eSAndrew Jones {
93789a8e69SNicholas Piggin enter_rtas(__pa(args));
94789a8e69SNicholas Piggin }
95789a8e69SNicholas Piggin
rtas_call_unlocked_va(struct rtas_args * args,int token,int nargs,int nret,int * outputs,va_list list)96789a8e69SNicholas Piggin static int rtas_call_unlocked_va(struct rtas_args *args,
97789a8e69SNicholas Piggin int token, int nargs, int nret, int *outputs,
98789a8e69SNicholas Piggin va_list list)
99789a8e69SNicholas Piggin {
1002f9ce69eSAndrew Jones int ret, i;
1012f9ce69eSAndrew Jones
102789a8e69SNicholas Piggin args->token = cpu_to_be32(token);
103789a8e69SNicholas Piggin args->nargs = cpu_to_be32(nargs);
104789a8e69SNicholas Piggin args->nret = cpu_to_be32(nret);
105789a8e69SNicholas Piggin args->rets = &args->args[nargs];
1062f9ce69eSAndrew Jones
1072f9ce69eSAndrew Jones for (i = 0; i < nargs; ++i)
108789a8e69SNicholas Piggin args->args[i] = cpu_to_be32(va_arg(list, u32));
1092f9ce69eSAndrew Jones
1102f9ce69eSAndrew Jones for (i = 0; i < nret; ++i)
111789a8e69SNicholas Piggin args->rets[i] = 0;
1122f9ce69eSAndrew Jones
113789a8e69SNicholas Piggin __rtas_call(args);
1142f9ce69eSAndrew Jones
1152f9ce69eSAndrew Jones if (nret > 1 && outputs != NULL)
1162f9ce69eSAndrew Jones for (i = 0; i < nret - 1; ++i)
117789a8e69SNicholas Piggin outputs[i] = be32_to_cpu(args->rets[i + 1]);
1182f9ce69eSAndrew Jones
119789a8e69SNicholas Piggin ret = nret > 0 ? be32_to_cpu(args->rets[0]) : 0;
120789a8e69SNicholas Piggin
121789a8e69SNicholas Piggin return ret;
122789a8e69SNicholas Piggin }
123789a8e69SNicholas Piggin
rtas_call_unlocked(struct rtas_args * args,int token,int nargs,int nret,int * outputs,...)124789a8e69SNicholas Piggin int rtas_call_unlocked(struct rtas_args *args, int token, int nargs, int nret, int *outputs, ...)
125789a8e69SNicholas Piggin {
126789a8e69SNicholas Piggin va_list list;
127789a8e69SNicholas Piggin int ret;
128789a8e69SNicholas Piggin
129789a8e69SNicholas Piggin va_start(list, outputs);
130789a8e69SNicholas Piggin ret = rtas_call_unlocked_va(args, token, nargs, nret, outputs, list);
131789a8e69SNicholas Piggin va_end(list);
132789a8e69SNicholas Piggin
133789a8e69SNicholas Piggin return ret;
134789a8e69SNicholas Piggin }
135789a8e69SNicholas Piggin
rtas_call(int token,int nargs,int nret,int * outputs,...)136789a8e69SNicholas Piggin int rtas_call(int token, int nargs, int nret, int *outputs, ...)
137789a8e69SNicholas Piggin {
138789a8e69SNicholas Piggin va_list list;
139789a8e69SNicholas Piggin int ret;
140789a8e69SNicholas Piggin
141*93c847c1SNicholas Piggin assert_msg(!in_usermode(), "May not make RTAS call from user mode\n");
142*93c847c1SNicholas Piggin
143789a8e69SNicholas Piggin spin_lock(&rtas_lock);
144789a8e69SNicholas Piggin
145789a8e69SNicholas Piggin va_start(list, outputs);
146789a8e69SNicholas Piggin ret = rtas_call_unlocked_va(&rtas_args, token, nargs, nret, outputs, list);
147789a8e69SNicholas Piggin va_end(list);
1482f9ce69eSAndrew Jones
1492f9ce69eSAndrew Jones spin_unlock(&rtas_lock);
150789a8e69SNicholas Piggin
1512f9ce69eSAndrew Jones return ret;
1522f9ce69eSAndrew Jones }
1532f9ce69eSAndrew Jones
rtas_stop_self(void)154789a8e69SNicholas Piggin void rtas_stop_self(void)
155789a8e69SNicholas Piggin {
156789a8e69SNicholas Piggin struct rtas_args args;
157789a8e69SNicholas Piggin uint32_t token;
158789a8e69SNicholas Piggin int ret;
159789a8e69SNicholas Piggin
160789a8e69SNicholas Piggin ret = rtas_token("stop-self", &token);
161789a8e69SNicholas Piggin if (ret) {
162789a8e69SNicholas Piggin puts("RTAS stop-self not available\n");
163789a8e69SNicholas Piggin return;
164789a8e69SNicholas Piggin }
165789a8e69SNicholas Piggin
166789a8e69SNicholas Piggin ret = rtas_call_unlocked(&args, token, 0, 1, NULL);
167789a8e69SNicholas Piggin printf("RTAS stop-self returned %d\n", ret);
168789a8e69SNicholas Piggin }
169789a8e69SNicholas Piggin
rtas_power_off(void)1702f9ce69eSAndrew Jones void rtas_power_off(void)
1712f9ce69eSAndrew Jones {
172789a8e69SNicholas Piggin struct rtas_args args;
1732565dce1SThomas Huth uint32_t token;
1742565dce1SThomas Huth int ret;
1752565dce1SThomas Huth
1762565dce1SThomas Huth ret = rtas_token("power-off", &token);
1772565dce1SThomas Huth if (ret) {
1782565dce1SThomas Huth puts("RTAS power-off not available\n");
1792565dce1SThomas Huth return;
1802565dce1SThomas Huth }
1812565dce1SThomas Huth
182789a8e69SNicholas Piggin ret = rtas_call_unlocked(&args, token, 2, 1, NULL, -1, -1);
1832f9ce69eSAndrew Jones printf("RTAS power-off returned %d\n", ret);
1842f9ce69eSAndrew Jones }
185