xref: /netbsd-src/sys/arch/powerpc/powerpc/rtas.c (revision 4ab6fe99ee6026c2920be91d17632b86902add73)
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