1 /*        $NetBSD: rtas.c,v 1.14 2014/02/28 05:41:21 matt Exp $ */
2 
3 /*
4  * CHRP RTAS support routines
5  * Common Hardware Reference Platform / Run-Time Abstraction Services
6  *
7  * Started by Aymeric Vincent in 2007, public domain.
8  * Modifications by Tim Rightnour 2007.
9  */
10 
11 #include <sys/cdefs.h>
12 __KERNEL_RCSID(0, "$NetBSD: rtas.c,v 1.14 2014/02/28 05:41:21 matt Exp $");
13 
14 #include <sys/param.h>
15 #include <sys/systm.h>
16 #include <sys/device.h>
17 #include <sys/errno.h>
18 #include <uvm/uvm.h>
19 
20 #include <dev/clock_subr.h>
21 #include <dev/ofw/openfirm.h>
22 
23 #include <powerpc/rtas.h>
24 #include <powerpc/psl.h>
25 
26 #include <machine/autoconf.h>
27 
28 bool machine_has_rtas;
29 
30 struct rtas_softc *rtas0_softc;
31 
32 struct rtas_softc {
33           int ra_phandle;
34           int ra_version;
35 
36           void (*ra_entry_pa)(paddr_t, paddr_t);
37           paddr_t ra_base_pa;
38 
39           struct todr_chip_handle ra_todr_handle;
40 };
41 
42 static struct {
43           int token;
44           int exists;
45 } rtas_function_token[RTAS_FUNC_number];
46 
47 static struct {
48         const char *name;
49         int index;
50 } rtas_function_lookup[] = {
51         { "restart-rtas", RTAS_FUNC_RESTART_RTAS },
52         { "nvram-fetch", RTAS_FUNC_NVRAM_FETCH },
53         { "nvram-store", RTAS_FUNC_NVRAM_STORE },
54         { "get-time-of-day", RTAS_FUNC_GET_TIME_OF_DAY },
55         { "set-time-of-day", RTAS_FUNC_SET_TIME_OF_DAY },
56         { "set-time-for-power-on", RTAS_FUNC_SET_TIME_FOR_POWER_ON },
57         { "event-scan", RTAS_FUNC_EVENT_SCAN },
58         { "check-exception", RTAS_FUNC_CHECK_EXCEPTION },
59         /* Typo in my Efika's firmware */
60         { "check-execption", RTAS_FUNC_CHECK_EXCEPTION },
61         { "read-pci-config", RTAS_FUNC_READ_PCI_CONFIG },
62         { "write-pci-config", RTAS_FUNC_WRITE_PCI_CONFIG },
63         { "display-character", RTAS_FUNC_DISPLAY_CHARACTER },
64         { "set-indicator", RTAS_FUNC_SET_INDICATOR },
65         { "power-off", RTAS_FUNC_POWER_OFF },
66         { "suspend", RTAS_FUNC_SUSPEND },
67         { "hibernate", RTAS_FUNC_HIBERNATE },
68         { "system-reboot", RTAS_FUNC_SYSTEM_REBOOT },
69           { "freeze-time-base", RTAS_FUNC_FREEZE_TIME_BASE },
70           { "thaw-time-base", RTAS_FUNC_THAW_TIME_BASE },
71 };
72 
73 static int rtas_match(device_t, cfdata_t, void *);
74 static void rtas_attach(device_t, device_t, void *);
75 static int rtas_detach(device_t, int);
76 static int rtas_activate(device_t, enum devact);
77 static int rtas_todr_gettime_ymdhms(struct todr_chip_handle *,
78     struct clock_ymdhms *);
79 static int rtas_todr_settime_ymdhms(struct todr_chip_handle *,
80     struct clock_ymdhms *);
81 
82 CFATTACH_DECL_NEW(rtas, sizeof (struct rtas_softc),
83     rtas_match, rtas_attach, rtas_detach, rtas_activate);
84 
85 static int
rtas_match(device_t parent,cfdata_t match,void * aux)86 rtas_match(device_t parent, cfdata_t match, void *aux)
87 {
88           struct confargs *ca = aux;
89 
90           if (strcmp(ca->ca_name, "rtas"))
91                     return 0;
92 
93           return 1;
94 }
95 
96 static void
rtas_attach(device_t parent,device_t self,void * aux)97 rtas_attach(device_t parent, device_t self, void *aux)
98 {
99           struct confargs *ca = aux;
100           struct rtas_softc *sc = device_private(self);
101           int ph = ca->ca_node;
102           int ih;
103           int rtas_size;
104           uintptr_t rtas_entry;
105           struct pglist pglist;
106           char buf[4];
107           int i;
108 
109           machine_has_rtas = true;
110 
111           sc->ra_phandle = ph;
112           if (OF_getprop(ph, "rtas-version", buf, sizeof buf) != sizeof buf)
113                     goto fail;
114           sc->ra_version = of_decode_int(buf);
115           if (OF_getprop(ph, "rtas-size", buf, sizeof buf) != sizeof buf)
116                     goto fail;
117           rtas_size = of_decode_int(buf);
118 
119           /*
120            * Instantiate the RTAS.
121            * The physical base address should be in the first 256 MB segment.
122            */
123           if (uvm_pglistalloc(rtas_size, 0x100000, 0x0fffffff, 4096, 256 << 20,
124               &pglist, 1, 0))
125                     goto fail;
126 
127           sc->ra_base_pa = VM_PAGE_TO_PHYS(TAILQ_FIRST(&pglist));
128 
129           ih = OF_open("/rtas");
130           if (ih == -1)
131                     goto fail_and_free;
132 
133           rtas_entry =
134                     OF_call_method_1("instantiate-rtas", ih, 1, sc->ra_base_pa);
135 
136           if (rtas_entry == -1)
137                     goto fail_and_free;
138 
139           sc->ra_entry_pa = (void *) rtas_entry;
140 
141           /*
142            * Get the tokens of the methods the RTAS provides
143            */
144 
145           for (i = 0;
146               i < sizeof rtas_function_lookup / sizeof rtas_function_lookup[0];
147               i++) {
148                     int index = rtas_function_lookup[i].index;
149 
150                     if (OF_getprop(ph, rtas_function_lookup[i].name, buf,
151                                         sizeof buf) != sizeof buf)
152                               continue;
153 
154                     rtas_function_token[index].token = of_decode_int(buf);
155                     rtas_function_token[index].exists = 1;
156           }
157 
158           rtas0_softc = sc;
159 
160           printf(": version %d, entry @pa 0x%"PRIxPTR"\n", sc->ra_version,
161                     rtas_entry);
162 
163           /*
164            * Initialise TODR support
165            */
166           sc->ra_todr_handle.cookie = sc;
167           sc->ra_todr_handle.bus_cookie = NULL;
168           sc->ra_todr_handle.todr_gettime = NULL;
169           sc->ra_todr_handle.todr_settime = NULL;
170           sc->ra_todr_handle.todr_gettime_ymdhms = rtas_todr_gettime_ymdhms;
171           sc->ra_todr_handle.todr_settime_ymdhms = rtas_todr_settime_ymdhms;
172           sc->ra_todr_handle.todr_setwen = NULL;
173           todr_attach(&sc->ra_todr_handle);
174 
175           return;
176 
177 fail_and_free:
178           uvm_pglistfree(&pglist);
179 fail:
180           aprint_error(": attach failed!\n");
181 }
182 
183 static int
rtas_detach(device_t self,int flags)184 rtas_detach(device_t self, int flags)
185 {
186           return EOPNOTSUPP;
187 }
188 
189 static int
rtas_activate(device_t self,enum devact act)190 rtas_activate(device_t self, enum devact act)
191 {
192           return EOPNOTSUPP;
193 }
194 
195 /*
196  * Support for calling to the RTAS
197  */
198 
199 int
rtas_call(int token,int nargs,int nreturns,...)200 rtas_call(int token, int nargs, int nreturns, ...)
201 {
202           va_list ap;
203           static struct {
204                     int token;
205                     int nargs;
206                     int nreturns;
207                     int args_n_results[RTAS_MAXARGS];
208           } args;
209           paddr_t pargs = (paddr_t)&args;
210           paddr_t base;
211           register_t msr;
212           void (*entry)(paddr_t, paddr_t);
213           int n;
214 
215           if (rtas0_softc == NULL)
216                     return -1;
217 
218           if (nargs + nreturns > RTAS_MAXARGS)
219                     return -1;
220 
221           if (!rtas_function_token[token].exists)
222                     return -1;
223 
224           base = rtas0_softc->ra_base_pa;
225           entry = rtas0_softc->ra_entry_pa;
226 
227           memset(args.args_n_results, 0, RTAS_MAXARGS * sizeof(int));
228           args.nargs = nargs;
229           args.nreturns = nreturns;
230           args.token = rtas_function_token[token].token;
231 
232           va_start(ap, nreturns);
233           for (n=0; n < nargs && n < RTAS_MAXARGS; n++)
234                     args.args_n_results[n] = va_arg(ap, int);
235 
236           __insn_barrier();
237           msr = mfmsr();
238           mtmsr(msr & ~(PSL_EE | PSL_FP | PSL_ME | PSL_FE0 | PSL_SE | PSL_BE |
239                     PSL_FE1 | PSL_IR | PSL_DR | PSL_RI));
240           __asm("isync;\n");
241 
242           entry(pargs, base);
243 
244           mtmsr(msr);
245           __asm("isync;\n");
246 
247           for (n = nargs; n < nargs + nreturns && n < RTAS_MAXARGS; n++)
248                     *va_arg(ap, int *) = args.args_n_results[n];
249 
250           va_end(ap);
251 
252           return args.args_n_results[nargs];
253 }
254 
255 int
rtas_has_func(int token)256 rtas_has_func(int token)
257 {
258           return rtas_function_token[token].exists;
259 }
260 
261 /*
262  * Real-Time Clock support
263  */
264 
265 static int
rtas_todr_gettime_ymdhms(struct todr_chip_handle * h,struct clock_ymdhms * t)266 rtas_todr_gettime_ymdhms(struct todr_chip_handle *h, struct clock_ymdhms *t)
267 {
268           int status, year, month, day, hour, minute, second, nanosecond;
269 
270           if (!rtas_function_token[RTAS_FUNC_GET_TIME_OF_DAY].exists)
271                     return ENXIO;
272 
273           if (rtas_call(RTAS_FUNC_GET_TIME_OF_DAY, 0, 8, &status, &year,
274                     &month, &day, &hour, &minute, &second, &nanosecond) < 0)
275                     return ENXIO;
276 
277           t->dt_year = year;
278           t->dt_mon = month;
279           t->dt_day = day;
280           t->dt_hour = hour;
281           t->dt_min = minute;
282           t->dt_sec = second;
283 
284           return 0;
285 }
286 
287 static int
rtas_todr_settime_ymdhms(struct todr_chip_handle * h,struct clock_ymdhms * t)288 rtas_todr_settime_ymdhms(struct todr_chip_handle *h, struct clock_ymdhms *t)
289 {
290           int status, year, month, day, hour, minute, second, nanosecond;
291 
292           if (!rtas_function_token[RTAS_FUNC_SET_TIME_OF_DAY].exists)
293                     return ENXIO;
294 
295           year = t->dt_year;
296           month = t->dt_mon;
297           day = t->dt_day;
298           hour = t->dt_hour;
299           minute = t->dt_min;
300           second = t->dt_sec;
301           nanosecond = 0;
302 
303           if (rtas_call(RTAS_FUNC_SET_TIME_OF_DAY, 7, 1, year, month,
304                     day, hour, minute, second, nanosecond, &status) < 0)
305                     return ENXIO;
306 
307           return 0;
308 }
309