blob: a423b9fa102415ac5e54351199600d4bb051ebe2 [file] [log] [blame]
/*
* Creation Date: <2004/08/28 18:38:22 greg>
* Time-stamp: <2004/08/28 18:38:22 greg>
*
* <methods.c>
*
* Misc device node methods
*
* Copyright (C) 2004 Greg Watson
*
* Based on MOL specific code which is
*
* Copyright (C) 2003, 2004 Samuel Rydh (samuel@ibrium.se)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2
*
*/
#include "config.h"
#include "libopenbios/bindings.h"
#include "drivers/drivers.h"
#include "libc/string.h"
#include "qemu/qemu.h"
#include "libopenbios/ofmem.h"
#include "arch/ppc/processor.h"
#include "drivers/usb.h"
/************************************************************************/
/* RTAS (run-time abstraction services) */
/************************************************************************/
#ifdef CONFIG_RTAS
DECLARE_NODE( rtas, INSTALL_OPEN, 0, "+/rtas" );
/* ( physbase -- rtas_callback ) */
static void
rtas_instantiate( void )
{
ucell physbase = POP();
ucell s=0x1000, size = (ucell)of_rtas_end - (ucell)of_rtas_start;
unsigned long virt;
while( s < size )
s += 0x1000;
virt = ofmem_claim_virt( 0, s, 0x1000 );
ofmem_map( physbase, virt, s, -1 );
memcpy( (char*)virt, of_rtas_start, size );
printk("RTAS instantiated at %08x\n", physbase );
flush_icache_range( (char*)virt, (char*)virt + size );
PUSH( physbase );
}
NODE_METHODS( rtas ) = {
{ "instantiate", rtas_instantiate },
{ "instantiate-rtas", rtas_instantiate },
};
#endif
/************************************************************************/
/* tty */
/************************************************************************/
DECLARE_NODE( tty, INSTALL_OPEN, 0, "/packages/terminal-emulator" );
/* ( addr len -- actual ) */
static void
tty_read( void )
{
int ch, len = POP();
char *p = (char*)cell2pointer(POP());
int ret=0;
if( len > 0 ) {
ret = 1;
ch = getchar();
if( ch >= 0 ) {
*p = ch;
} else {
ret = 0;
}
}
PUSH( ret );
}
/* ( addr len -- actual ) */
static void
tty_write( void )
{
int i, len = POP();
char *p = (char*)cell2pointer(POP());
for( i=0; i<len; i++ )
putchar( *p++ );
RET( len );
}
NODE_METHODS( tty ) = {
{ "read", tty_read },
{ "write", tty_write },
};
/************************************************************************/
/* client interface 'quiesce' */
/************************************************************************/
DECLARE_NODE( ciface, 0, 0, "+/openprom/client-services" );
/* ( -- ) */
static void
ciface_quiesce( unsigned long args[], unsigned long ret[] )
{
usb_exit();
ob_ide_quiesce();
#if 0
unsigned long msr;
/* This seems to be the correct thing to do - but I'm not sure */
asm volatile("mfmsr %0" : "=r" (msr) : );
msr &= ~(MSR_IR | MSR_DR);
asm volatile("mtmsr %0" :: "r" (msr) );
#endif
}
/* ( -- ms ) */
/* From drivers/timer.c */
extern unsigned long timer_freq;
static void
ciface_milliseconds( unsigned long args[], unsigned long ret[] )
{
unsigned long tbu, tbl, temp;
unsigned long long ticks, msecs;
asm volatile(
"1:\n"
"mftbu %2\n"
"mftb %0\n"
"mftbu %1\n"
"cmpw %2,%1\n"
"bne 1b\n"
: "=r"(tbl), "=r"(tbu), "=r"(temp)
:
: "cc");
ticks = (((unsigned long long)tbu) << 32) | (unsigned long long)tbl;
msecs = (1000 * ticks) / timer_freq;
PUSH( msecs );
}
NODE_METHODS( ciface ) = {
{ "quiesce", ciface_quiesce },
{ "milliseconds", ciface_milliseconds },
};
/************************************************************************/
/* MMU/memory methods */
/************************************************************************/
DECLARE_NODE( memory, INSTALL_OPEN, 0, "/memory" );
DECLARE_UNNAMED_NODE( mmu, INSTALL_OPEN, 0 );
DECLARE_NODE( mmu_ciface, 0, 0, "+/openprom/client-services" );
/* ( [phys] size align --- base ) */
static void
mem_claim( void )
{
ucell align = POP();
ucell size = POP();
phys_addr_t phys = -1;
if (!align) {
phys = POP();
}
phys = ofmem_claim_phys(phys, size, align);
PUSH(phys);
}
/* ( phys size --- ) */
static void
mem_release( void )
{
POP(); POP();
}
/* ( [virt] size align --- base ) */
static void
mmu_claim( void )
{
ucell align = POP();
ucell size = POP();
ucell virt = -1;
if (!align) {
virt = POP();
}
virt = ofmem_claim_virt(virt, size, align);
PUSH(virt);
}
/* ( virt size --- ) */
static void
mmu_release( void )
{
POP(); POP();
}
/* ( phys virt size mode -- [ret???] ) */
static void
mmu_map( void )
{
ucell mode = POP();
ucell size = POP();
ucell virt = POP();
ucell phys = POP();
ucell ret;
/* printk("mmu_map: %x %x %x %x\n", phys, virt, size, mode ); */
ret = ofmem_map( phys, virt, size, mode );
if( ret ) {
printk("MMU: map failure\n");
throw( -13 );
return;
}
}
/* ( virt size -- ) */
static void
mmu_unmap( void )
{
POP(); POP();
}
/* ( virt -- false | phys mode true ) */
static void
mmu_translate( void )
{
ucell mode;
ucell virt = POP();
ucell phys = ofmem_translate( virt, &mode );
if( phys == -1 ) {
PUSH( 0 );
} else {
PUSH( phys );
PUSH( mode );
PUSH( -1 );
}
}
/* ( virt size align -- baseaddr|-1 ) */
static void
ciface_claim( void )
{
ucell align = POP();
ucell size = POP();
ucell virt = POP();
ucell ret = ofmem_claim( virt, size, align );
/* printk("ciface_claim: %08x %08x %x\n", virt, size, align ); */
PUSH( ret );
}
/* ( virt size -- ) */
static void
ciface_release( void )
{
ucell size = POP();
ucell virt = POP();
ofmem_release(virt, size);
}
NODE_METHODS( memory ) = {
{ "claim", mem_claim },
{ "release", mem_release },
};
NODE_METHODS( mmu ) = {
{ "claim", mmu_claim },
{ "release", mmu_release },
{ "map", mmu_map },
{ "unmap", mmu_unmap },
{ "translate", mmu_translate },
};
NODE_METHODS( mmu_ciface ) = {
{ "cif-claim", ciface_claim },
{ "cif-release", ciface_release },
};
/************************************************************************/
/* init */
/************************************************************************/
void
node_methods_init( const char *cpuname )
{
phandle_t chosen, ph;
#ifdef CONFIG_RTAS
if (is_newworld()) {
REGISTER_NODE( rtas );
}
#endif
REGISTER_NODE( ciface );
REGISTER_NODE( memory );
REGISTER_NODE_METHODS( mmu, cpuname );
REGISTER_NODE( mmu_ciface );
REGISTER_NODE( tty );
chosen = find_dev("/chosen");
if (chosen) {
push_str(cpuname);
fword("open-dev");
ph = POP();
set_int_property(chosen, "mmu", ph);
}
}