/* $Id: init.c,v 1.9 1996/12/18 06:46:55 tridge Exp $
 * init.c:  Initialize internal variables used by the PROM
 *          library functions.
 *
 * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
 * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
 */

#include "promlib.h"

struct linux_romvec *romvec;
enum prom_major_version prom_vers;
unsigned int prom_rev, prom_prev;
void (*prom_cif_handler)(long long *);
void *prom_cif_stack;

/* The root node of the prom device tree. */
int prom_root_node;

int prom_stdin, prom_stdout;
int prom_chosen;

/* Pointer to the device tree operations structure. */
struct linux_nodeops *prom_nodeops;

/* You must call prom_init() before you attempt to use any of the
 * routines in the prom library.  It returns 0 on success, 1 on
 * failure.  It gets passed the pointer to the PROM vector.
 */

extern void prom_meminit(void);
extern void prom_ranges_init(void);
void prom_halt(void);

static long long p1275_args[23];

int p1275_cmd (char *service, int args, ...)
{
	va_list list;
	int i;
	
	p1275_args[0] = (unsigned long long)(unsigned long)service;
	p1275_args[1] = args;
	p1275_args[2] = 1;
	va_start (list, args);
	for (i = 0; i < args; i++)
		p1275_args[i + 3] = (unsigned long long)(unsigned long) va_arg (list, char *);
	va_end (list);
	__asm__ __volatile__ ("
		mov	%1, %%g1
		mov	%2, %%g2
		save	%0, -0xc0, %%sp
		rdpr	%%pstate, %%l1
		andn	%%l1, 8, %%l1
		wrpr	%%l1, 0, %%pstate
		call	%%g1
		 mov	%%g2, %%o0
		wrpr	%%l1, 8, %%pstate
		restore
	" : : "r" (prom_cif_stack), "r" (prom_cif_handler), "r" (p1275_args) :
	"o0", "o1", "o2", "o3", "o4", "o5", "o7", "g1", "g2", "g3", "g4",
	"g5", "g6", "g7");
	return (int) p1275_args [3 + args];
}

void prom_init(struct linux_romvec *rp, void *cifh, void *cifsp)
{
	if (cifh) {
		prom_cif_handler = cifh;
		prom_cif_stack = cifsp;
		prom_vers = PROM_P1275;
		prom_chosen = p1275_cmd ("finddevice", 1, "/chosen");
		if (prom_chosen == -1) prom_halt ();
		prom_stdin = prom_getint (prom_chosen, "stdin");
		prom_stdout = prom_getint (prom_chosen, "stdout");
	} else {
		romvec = rp;

		switch(romvec->pv_romvers) {
		case 0:
			prom_vers = PROM_V0;
			break;
		case 2:
			prom_vers = PROM_V2;
			break;
		case 3:
			prom_vers = PROM_V3;
			break;
		}

		prom_rev = romvec->pv_plugin_revision;
		prom_prev = romvec->pv_printrev;
		prom_nodeops = romvec->pv_nodeops;
		if (prom_vers != PROM_V0) {
			prom_stdin = *romvec->pv_v2bootargs.fd_stdin;
			prom_stdout = *romvec->pv_v2bootargs.fd_stdout;
		}
		if((((unsigned long) prom_nodeops) == 0) || 
		   (((unsigned long) prom_nodeops) == -1))
			prom_halt();
	}

	prom_root_node = prom_getsibling(0);
	if((prom_root_node == 0) || (prom_root_node == -1))
		prom_halt();

	/* Initialization successful. */
	return;
}

/* Drop into the prom, with the chance to continue with the 'go'
 * prom command.
 */
void
prom_cmdline(void)
{
	if (prom_vers != PROM_P1275)
		(*(romvec->pv_abort))();
	else
		p1275_cmd ("enter", 0);
}

/* Drop into the prom, but completely terminate the program.
 * No chance of continuing.
 */
void
prom_halt(void)
{
	if (prom_vers != PROM_P1275)
		(*(romvec->pv_halt))();
	else
		p1275_cmd ("exit", 0);
	/* Not reached */
}

