1 /*
2  *   Creation Date: <2004/08/28 18:38:22 greg>
3  *   Time-stamp: <2004/08/28 18:38:22 greg>
4  *
5  *	<methods.c>
6  *
7  *	Misc device node methods
8  *
9  *   Copyright (C) 2004 Greg Watson
10  *
11  *   Based on MOL specific code which is
12  *
13  *   Copyright (C) 2003, 2004 Samuel Rydh (samuel@ibrium.se)
14  *
15  *   This program is free software; you can redistribute it and/or
16  *   modify it under the terms of the GNU General Public License
17  *   version 2
18  *
19  */
20 
21 #include "config.h"
22 #include "libopenbios/bindings.h"
23 #include "drivers/drivers.h"
24 #include "libc/string.h"
25 #include "qemu/qemu.h"
26 #include "libopenbios/ofmem.h"
27 #include "arch/ppc/processor.h"
28 #include "drivers/usb.h"
29 
30 /************************************************************************/
31 /*	RTAS (run-time abstraction services)				*/
32 /************************************************************************/
33 
34 #ifdef CONFIG_RTAS
35 DECLARE_NODE( rtas, INSTALL_OPEN, 0, "+/rtas" );
36 
37 /* ( physbase -- rtas_callback ) */
38 static void
rtas_instantiate(void)39 rtas_instantiate( void )
40 {
41 	ucell physbase = POP();
42 	ucell s=0x1000, size = (ucell)of_rtas_end - (ucell)of_rtas_start;
43 	unsigned long virt;
44 
45 	while( s < size )
46 		s += 0x1000;
47 	virt = ofmem_claim_virt( 0, s, 0x1000 );
48 	ofmem_map( physbase, virt, s, -1 );
49 	memcpy( (char*)virt, of_rtas_start, size );
50 
51 	printk("RTAS instantiated at %08x\n", physbase );
52 	flush_icache_range( (char*)virt, (char*)virt + size );
53 
54 	PUSH( physbase );
55 }
56 
57 NODE_METHODS( rtas ) = {
58 	{ "instantiate",	rtas_instantiate },
59 	{ "instantiate-rtas",	rtas_instantiate },
60 };
61 #endif
62 
63 
64 /************************************************************************/
65 /*	tty								*/
66 /************************************************************************/
67 
68 DECLARE_NODE( tty, INSTALL_OPEN, 0, "/packages/terminal-emulator" );
69 
70 /* ( addr len -- actual ) */
71 static void
tty_read(void)72 tty_read( void )
73 {
74 	int ch, len = POP();
75 	char *p = (char*)cell2pointer(POP());
76 	int ret=0;
77 
78 	if( len > 0 ) {
79 		ret = 1;
80 		ch = getchar();
81 		if( ch >= 0 ) {
82 			*p = ch;
83 		} else {
84 			ret = 0;
85 		}
86 	}
87 	PUSH( ret );
88 }
89 
90 /* ( addr len -- actual ) */
91 static void
tty_write(void)92 tty_write( void )
93 {
94 	int i, len = POP();
95 	char *p = (char*)cell2pointer(POP());
96 	for( i=0; i<len; i++ )
97 		putchar( *p++ );
98 	RET( len );
99 }
100 
101 NODE_METHODS( tty ) = {
102 	{ "read",	tty_read	},
103 	{ "write",	tty_write	},
104 };
105 
106 /************************************************************************/
107 /*	client interface 'quiesce'					*/
108 /************************************************************************/
109 
110 DECLARE_NODE( ciface, 0, 0, "+/openprom/client-services" );
111 
112 /* ( -- ) */
113 static void
ciface_quiesce(unsigned long args[],unsigned long ret[])114 ciface_quiesce( unsigned long args[], unsigned long ret[] )
115 {
116 	usb_exit();
117 
118 	ob_ide_quiesce();
119 #if 0
120 	unsigned long msr;
121 	/* This seems to be the correct thing to do - but I'm not sure */
122 	asm volatile("mfmsr %0" : "=r" (msr) : );
123 	msr &= ~(MSR_IR | MSR_DR);
124 	asm volatile("mtmsr %0" :: "r" (msr) );
125 #endif
126 }
127 
128 /* ( -- ms ) */
129 #define TIMER_FREQUENCY 16600000ULL
130 
131 static void
ciface_milliseconds(unsigned long args[],unsigned long ret[])132 ciface_milliseconds( unsigned long args[], unsigned long ret[] )
133 {
134 	unsigned long tbu, tbl, temp;
135 	unsigned long long ticks, msecs;
136 
137 	asm volatile(
138 		"1:\n"
139 		"mftbu  %2\n"
140 		"mftb   %0\n"
141 		"mftbu  %1\n"
142 		"cmpw   %2,%1\n"
143 		"bne    1b\n"
144 		: "=r"(tbl), "=r"(tbu), "=r"(temp)
145 		:
146 		: "cc");
147 
148 	ticks = (((unsigned long long)tbu) << 32) | (unsigned long long)tbl;
149 	msecs = (1000 * ticks) / TIMER_FREQUENCY;
150 	PUSH( msecs );
151 }
152 
153 
154 NODE_METHODS( ciface ) = {
155 	{ "quiesce",		ciface_quiesce		},
156 	{ "milliseconds",	ciface_milliseconds	},
157 };
158 
159 
160 /************************************************************************/
161 /*	MMU/memory methods						*/
162 /************************************************************************/
163 
164 DECLARE_NODE( memory, INSTALL_OPEN, 0, "/memory" );
165 DECLARE_UNNAMED_NODE( mmu, INSTALL_OPEN, 0 );
166 DECLARE_NODE( mmu_ciface, 0, 0, "+/openprom/client-services" );
167 
168 
169 /* ( [phys] size align --- base ) */
170 static void
mem_claim(void)171 mem_claim( void )
172 {
173 	ucell align = POP();
174 	ucell size = POP();
175 	phys_addr_t phys = -1;
176 
177 	if (!align) {
178 		phys = POP();
179 	}
180 
181 	phys = ofmem_claim_phys(phys, size, align);
182 
183 	PUSH(phys);
184 }
185 
186 /* ( phys size --- ) */
187 static void
mem_release(void)188 mem_release( void )
189 {
190 	POP(); POP();
191 }
192 
193 /* ( [virt] size align --- base ) */
194 static void
mmu_claim(void)195 mmu_claim( void )
196 {
197 	ucell align = POP();
198 	ucell size = POP();
199 	ucell virt = -1;
200 
201 	if (!align) {
202 		virt = POP();
203 	}
204 
205 	virt = ofmem_claim_virt(virt, size, align);
206 
207 	PUSH(virt);
208 }
209 
210 /* ( virt size --- ) */
211 static void
mmu_release(void)212 mmu_release( void )
213 {
214 	POP(); POP();
215 }
216 
217 /* ( phys virt size mode -- [ret???] ) */
218 static void
mmu_map(void)219 mmu_map( void )
220 {
221 	ucell mode = POP();
222 	ucell size = POP();
223 	ucell virt = POP();
224 	ucell phys = POP();
225 	ucell ret;
226 
227 	/* printk("mmu_map: %x %x %x %x\n", phys, virt, size, mode ); */
228 	ret = ofmem_map( phys, virt, size, mode );
229 
230 	if( ret ) {
231 		printk("MMU: map failure\n");
232 		throw( -13 );
233 		return;
234 	}
235 }
236 
237 /* ( virt size -- ) */
238 static void
mmu_unmap(void)239 mmu_unmap( void )
240 {
241 	POP(); POP();
242 }
243 
244 /* ( virt -- false | phys mode true ) */
245 static void
mmu_translate(void)246 mmu_translate( void )
247 {
248 	ucell mode;
249 	ucell virt = POP();
250 	ucell phys = ofmem_translate( virt, &mode );
251 
252 	if( phys == -1 ) {
253 		PUSH( 0 );
254 	} else {
255 		PUSH( phys );
256                 PUSH( mode );
257 		PUSH( -1 );
258 	}
259 }
260 
261 /* ( virt size align -- baseaddr|-1 ) */
262 static void
ciface_claim(void)263 ciface_claim( void )
264 {
265 	ucell align = POP();
266 	ucell size = POP();
267 	ucell virt = POP();
268 	ucell ret = ofmem_claim( virt, size, align );
269 
270 	/* printk("ciface_claim: %08x %08x %x\n", virt, size, align ); */
271 	PUSH( ret );
272 }
273 
274 /* ( virt size -- ) */
275 static void
ciface_release(void)276 ciface_release( void )
277 {
278 	ucell size = POP();
279 	ucell virt = POP();
280 	ofmem_release(virt, size);
281 }
282 
283 
284 NODE_METHODS( memory ) = {
285 	{ "claim",		mem_claim		},
286 	{ "release",		mem_release		},
287 };
288 
289 NODE_METHODS( mmu ) = {
290 	{ "claim",		mmu_claim		},
291 	{ "release",		mmu_release		},
292 	{ "map",		mmu_map			},
293 	{ "unmap",		mmu_unmap		},
294 	{ "translate",		mmu_translate		},
295 };
296 
297 NODE_METHODS( mmu_ciface ) = {
298 	{ "cif-claim",		ciface_claim		},
299 	{ "cif-release",	ciface_release		},
300 };
301 
302 
303 /************************************************************************/
304 /*	init								*/
305 /************************************************************************/
306 
307 void
node_methods_init(const char * cpuname)308 node_methods_init( const char *cpuname )
309 {
310 	phandle_t chosen, ph;
311 #ifdef CONFIG_RTAS
312 	if (is_newworld()) {
313 		REGISTER_NODE( rtas );
314 	}
315 #endif
316 	REGISTER_NODE( ciface );
317 	REGISTER_NODE( memory );
318 	REGISTER_NODE_METHODS( mmu, cpuname );
319 	REGISTER_NODE( mmu_ciface );
320 	REGISTER_NODE( tty );
321 
322 	chosen = find_dev("/chosen");
323 	if (chosen) {
324 		push_str(cpuname);
325 		fword("open-dev");
326 		ph = POP();
327 		set_int_property(chosen, "mmu", ph);
328 	}
329 }
330