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