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